diff options
author | 2018-04-22 15:17:42 -0400 | |
---|---|---|
committer | 2018-04-22 15:17:42 -0400 | |
commit | 2f191c302fed4463b1192d2595184a65411adf25 (patch) | |
tree | bb9c1db12554ac8724f3b5850d0e780ab4309216 | |
parent | e318468b98989bebe33f48b101115f9b7500b10a (diff) | |
download | inxi-upstream/3.0.07-1.tar.bz2 inxi-upstream/3.0.07-1.tar.xz inxi-upstream/3.0.07-1.tar.zst |
New upstream version 3.0.07-1upstream/3.0.07-1
-rw-r--r-- | LICENSE.txt | 674 | ||||
-rw-r--r-- | README.txt | 376 | ||||
-rwxr-xr-x | inxi | 31508 | ||||
-rw-r--r-- | inxi.1 | 1375 | ||||
-rw-r--r-- | inxi.1.gz | bin | 13303 -> 0 bytes | |||
-rw-r--r-- | inxi.changelog | 784 |
6 files changed, 18418 insertions, 16299 deletions
diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..9cecc1d --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +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 +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + 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 +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {one line to give the program's name and a brief idea of what it does.} + Copyright (C) {year} {name of author} + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + {project} Copyright (C) {year} {fullname} + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + 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, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>. diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..5443b0b --- /dev/null +++ b/README.txt @@ -0,0 +1,376 @@ +README for inxi - a command line system information tool + +The new Perl inxi is now here! File all issue reports with the master +branch. All support for versions prior to 3.0 is now ended, sorry. + +Make sure to update to the current inxi from the master branch before +filing any issue reports. The code in pre 2.9 versions literally no +longer exists in inxi 3. Bugs from earlier versions cannot be solved +in the new version since the pre 2.9 and the 2.9 and later versions +are completely different internally. + +inxi strives to support the widest range of operating systems and +hardware, from the most simple consumer desktops, to the most advanced +professional hardware and servers. + +The issues you post help maintain or expand that support, and are +always appreciated since user data and feedback is what keeps inxi +working and supporting the latest or not so latest hardware and +operating systems. + +See the BSD section below for qualifications re BSDs, and OSX in +particular. + +===================================================================== +MASTER BRANCH: + +This is the only supported branch, and the current latest commit is +the only supported 'release'. There are no 'releases' of inxi beyond +the current commit to master. All past commits are not supported. + +git clone https://github.com/smxi/inxi --branch master --single-branch + +OR direct fast and easy install: +wget -Nc https://github.com/smxi/inxi/raw/master/inxi + +OR easy to remember shortcut (which redirects to github): +wget -Nc https://smxi.org/inxi +wget -Nc smxi.org/inxi + +'Tagging' is purely a formality that certain distros can't figure out +how to do without, that's all. A tag is a pointer to a commit, and has +no further meaning. + +NOTE: Just because github calls tagged commits 'Releases' does not +mean they are releases! I can't change the words on the tag page. +They are tagged commmits, period. I did not want to use tags precisely +to avoid the idea that inxi has any release that exists that is other +than it's current master version, but I decided that it was less pain +to add tags than to argue this point any further. + +===================================================================== +DEVELOPMENT BRANCH: +All active development is now done on the inxi-perl branch (pinxi): + +git clone https://github.com/smxi/inxi --branch inxi-perl --single-branch + +OR direct fast and easy install: +wget -Nc https://github.com/smxi/inxi/raw/inxi-perl/pinxi + +OR easy to remember shortcut (which redirects to github): +wget -Nc https://smxi.org/pinxi +wget -Nc smxi.org/pinxi + +Once new features have been debugged, tested, and are stable, they +will move to the master branch. + +===================================================================== +LEGACY BRANCH: +If you'd like to look at or check out the Gawk/Bash version of inxi, +you can find it here, at the inxi-legacy branch (binxi): + +git clone https://github.com/smxi/inxi --branch inxi-legacy --single-branch + +OR direct fast and easy install: +wget -Nc https://github.com/smxi/inxi/raw/inxi-legacy/binxi + +OR easy to remember shortcut (which redirects to github): +wget -Nc https://smxi.org/binxi + +This version will not be maintained, and it's unlikely that any time +will be spent on it in the future, but it is there in case it's of +use or interest to anyone. + +===================================================================== +SUPPORT INFO: + +Do not ask for basic help that reading the inxi -h / --help menus, or +man page would show you, and do not ask for features to be added that +inxi already has. Also do not ask for support if your distro won't +update its inxi version, some are bad about that. + +DOCUMENTATION: https://smxi.org/docs/inxi.htm +(smxi.org/docs/ is easier to remember, and is one click away from +inxi.htm). The one page wiki on github is only a pointer to the real +resources. + +https://github.com/smxi/inxi/tree/inxi-perl/docs +Contains specific Perl inxi documentation, of interest mostly to +developers. Includes internal inxi tools, values, configuration items. +Also has useful information about Perl version support, including the +list of Core modules that _should_ be included in a distribution's +core modules, but which are unfortunately sometimes removed. + +HTML MAN PAGE: https://smxi.org/docs/inxi-man.htm +INXI OPTIONS PAGE: http://smxi.org/docs/inxi-options.htm +NOTE: These may not always be up to date, but generally track the most +recent inxi commits. + +ISSUES: https://github.com/smxi/inxi/issues +No issues accepted for non current inxi releases. See below for more on +that. Unfortunately as of 2.9, no support or issues can be accepted for +older inxi's because inxi 2.9 (Perl) and newer is a full rewrite, and +legacy inxi is not being supported since our time here on earth is +finite (plus of course, one reason for the rewrite was to never have +to work with Gawk->Bash again!). + +SUPPORT FORUMS: https://techpatterns.com/forums/forum-33.html +This is the best place to place support issues that may be complicated. + +If you are developer, use: +DEVELOPER FORUMS: https://techpatterns.com/forums/forum-32.html + +SOURCE VERSION CONTROL: https://github.com/smxi/inxi +MAIN BRANCH: master +DEVELOPMENT BRANCHES: inxi-perl, one, two +inxi-perl is the dev branch, the others are rarely if ever used. inxi +itself has the built in feature to be able to update itself from +anywhere, including these branches, which is very useful for development +and debugging on various user systems. + +PULL REQUESTS: Please talk to me before starting to work on patches of +any reasonable complexity. inxi is hard to work on, and you have to +understand how it works before submitting patches, unless it's a trivial +bug fix. Please: NEVER even think about looking at or using previous +inxi commits, previous to the current master version, as a base for a +patch. If you do, your patch / pull request will probably be rejected. +Developers, get your version from the inxi-perl branch, pinxi, otherwise +you may not be current to actual development versions. inxi-perl pinxi +is always equal to or ahead of master branch inxi. + +Man page updates, doc page updates, etc, of course, are easy and will +probably be accepted, as long as they are properly formatted and +logically coherent. + +inxi releases early, and releases often, when under development. + +PACKAGERS: inxi has one and only one 'release', and that is the current +commit to master branch (plus pinxi inxi-perl branch, of course, but +those should never be packaged). + +===================================================================== +ABOUT INXI - CORE COMMITMENT TO LONG TERM STABILITY + +inxi is a command line system information tool. It was forked from the +ancient and mindbendingly perverse yet ingenius infobash, by locsmif. + +That was a buggy, impossible to update or maintain piece of software, +so the fork fixed those core issues, and made it flexible enough to +expand the utility of the original ideas. Locmsif has given his thumbs +up to inxi, so don't be fooled by legacy infobash stuff you may see +out there. + +inxi is lower case, except when I create a text header here in a file +like this, but it's always lower case. Sometimes to follow convention +I will use upper case inxi to start a sentence, but i find it a bad +idea since invariably, someone will repeat that and type it in as the +command name, then someone will copy that, and complain that the +command: Inxi doesn't exist... + +The primary purpose of inxi is for support, and sys admin use. inxi +is used widely for forum and IRC support, which is I believe it's most +common function. + +If you are piping output to paste or post (or writing to file), inxi +now automatically turns off color codes, so the old suggestion to +use -c 0 to turn off colors is no longer required. + +inxi should always show you your current system state, as far as +possible, and should be more reliable than your own beliefs about +what is in your system, ideally. In other words, the goal in inxi +is to have it be right more than it is wrong about any system that +it runs on. And not to rely on non current system state data if at +all possible. Some things, like memory/ram data, rely on radically +unreliable system self reporting based on OEM filling out data +correctly, which doesn't often happen, so in those cases, you want to +confirm things like ram capacity with a reputable hardware source, +like crucial.com, which has the best ram hardware tool I know of. + +The core mission of inxi is to always work on all systems all the +time. Well, all systems with the core tools inxi requires to operate +installed. Ie, not Android, yet. What this means is this: you can +have a 10 year old box, or probably 15, not sure, and you can install +today's inxi on it, and it will run. It won't run fast, but it will +run. I test inxi on a 200 MHz laptop from about 1998 to keep it +honest. That's also what was used to optimize the code at some +points, since differences appear as seconds, not 10ths or 100ths +of seconds on old systems like that. + +inxi is being written, and tested, on Perl as old as 5.08, and will +work on any system that runs Perl 5.08 or later. Pre 2.9.0 Gawk/Bash +inxi will also run on any system no matter how old, within reason, +so there should be no difference. + +===================================================================== +BSD SUPPORT + +Real BSDs: +BSD support is not as complete as GNU/Linux support due to the fact +some of the data simply is not available, or is structured in a way +that makes it unique to each BSD. This fragmentation makes supporting +BSDs far more difficult than it should be in the 21st century. The +BSD support in inxi is an ongoing process, with more features being +added as new data sources and types are discovered. + +All BSD issue reports unless trivial and obvious will require 1 of +two things: + +1. a full --debug 21 data dump so I don't have to spend days trying +to get the information I need to resolve the issue file by painful +file from the issue poster. This is only the start of the process, +and realistically requires 2. to complete it. + +2. direct SSH access to at least a comparable live BSD version/system, +that is, if the issue is on a laptop, access has to be granted to the +laptop, or a similar one. + +Option 2 is far preferred because in terms of my finite time on this +planet of ours, the fact is, if I don't have direct (or SSH) access, +I can't get much done, and the little I can get done will take 10 to +1000x longer than it should. That's my time spent (and sadly, with +BSDs, largely lost), not yours. + +I decided I have to adopt this much more strict policy with BSDs +after wasting untold hours on trying to get good BSD support, only +to see that support break a few years down the road as the data inxi +relied in changed structure or syntax, or the tools changed, or +whatever else makes the BSDs such a challenge to support. In the end, +I realized, the only BSDs that are well supported are ones that I have +had direct access to for debugging and testing. + +I will always accept patches that are well done, if they do not break +GNU/Linux, and extend BSD support, or add new BSD features, and follow +the internal inxi logic, and aren't too long. inxi sets initial internal +flags to identify that it is a BSD system vs a GNU/Linux system, and +preloads some data structures for BSD use, so make sure you understand +what inxi is doing before you get into it. + +OSX: +Do not insult real BSDs by calling OSX a BSD. OSX is the least +Unix-like operating system I've ever seen that claims to be a Unix, +its tools are mutated, it's data randomly and non-standardly organized, +and it totally fails to respect the 'spirit' of Unix, even though it +might pass some random tests that certify a system as a 'Unix'. + +If you want me to use my time on OSX features or issues, you have to +pay me, because Apple is all about money, not freedom (that's what +the 'free' in 'free software' is referring to, not cost), and I'm not +donating my finite time in support of non-free operating systems. + +===================================================================== +INXI FEATURES AND FUNCTIONALITY + +inxi's functionality continues to grow over time, but it's also +important to understand that each core new feature usually requires +about 30 days work to get it stable. So new features are not trivial +things, nor is it acceptable to submit a patch that works only on your +personal system. One inxi feature (-s, sensors data), took about +2 hours to get working in the alpha test on the local dev system, but +then to handle the massive chaos that is actual user sensors output +and system variations, it took several rewrites and about 30 days to +get somewhat reliable for about 98% or so of inxi users. So if your +patch is rejected, it's likely because you have not thought it through +adequately, have not done adequate testing cross system and +platform, etc. + +===================================================================== +INXI RELEASE/SUPPORT/ISSUES/BUGS INFORMATION: + +Important: the only version of inxi that is supported is the latest +current master branch release. No issue reports or bug reports will be +accepted for anything other than current master branch. No merges, +attempts to patch old code from old releases, will be considered or +accepted. If you are not updated to the latest inxi, do not file a +bug report since it's probably been fixed ages ago. If your distro +isn't packaging a current inxi, then file a bug report with them, not +here. The only valid working code base for inxi is the current +release of inxi. + +Distributions should never feel any advantage comes from using old +inxi releases because inxi has as a core promise to you, the end user, +that it will NEVER require new tools to run. New tools may be required +for a new feature, but that will always be handled internally by inxi, +and will not cause any operational failures. This is a promise, and I +will never as long as I run this project violate that core inxi +requirement. Old inxi is NOT more stable than current inxi, it's just +old, and lacking in bug fixes and features. For pre 2.9 releases, it's +also significantly slower, and with fewer features. + +inxi is a rolling release codebase, just like Debian Sid, Gentoo, or +Arch Linux are rolling release GNU/Linux distributions, with no +'release points'. + +Your distro not updating inxi ever, then failing to show something +that is fixed in current inxi is not a bug, and please do not post it +here. File the issue with your distro, not here. Updating inxi in a +package pool will NEVER make anything break or fail, period. It has no +version based dependencies, just software, like Perl 5.xx, lspci, etc. +There is never a valid reason to not update inxi in a package pool of +any distro in the world (with one single known exception, the Slackware +based Puppy Linux release, which ships without the full Perl language. +The Debian based one works fine). + +Sys Admin type inxi users always get the first level of support. ie, +convince us you run real systems and networks, and your issue shoots +to the top of the line. As do any real bugs. Failure to supply +requested debugger data will lead to a distinct lack of interest on +our part to help you with a bug. ie, saying, oh, x doesn't work, +doesn't cut it, unless it's obvious why. + +===================================================================== + +INXI VERSION NUMBERING: + +inxi uses 'semantic' version numbering, where the version numbers +actually mean something. + +The version number follows these guidelines: +Using example 3.2.28-6 + +The first digit(s), "3", is a major version, and almost never changes. +Only a huge milestone, or if inxi reaches 3.9.xx, when it will simply +move up to 4.0.0 just to keep it clean, would cause a change. + +The second digit(s), "2", means a new real feature has been added. +Not a tweaked existing feature, an actual new feature, which usually +also has a new argument option letter attached. The second number goes +from 0 to 9, and then rolls over the first after 9. It could also be +adding a very complicated expansion of existing features, like Wayland. +It depends. + +The third, "28", is for everything small, can cover bug fixes, tweaks +to existing features to add support for something, pretty much anything +where you want the end user to know that they are not up to date. The +third goes from 0 to 99, then rolls over the second. + +The fourth, "6", is extra information about certain types of inxi +updates. I don't usually use this last one in master branch, but you +will see it in branches one,two, inxi-perl, inxi-legacy since that is +used to confirm remote test system patch version updates. + +The fourth number, when used, will be alpha-numeric, a common version +would be, in say, branch one: 2.2.28-b1-02, in other words, a branch 1 +release, version 2. + +In the past, now and then the 4th, or 'patch', number, was used in +trunk/master branches of inxi, but I've pretty much stopped doing that +because it's confusing. + +inxi does not use the fiction of date based versioning because that +imparts no useful information to the end user, when you look at say, +2.2.28, and you last had 2.2.11, you can know with some certainty that +inxi has no major new features, just fine tunings and bug fixes. And +if you see one with 2.3.2, you will know that there is a new feature, +almost, but not always, linked to one or more new line output items. +Sometimes a fine tuning can be quite significant, sometimes it's a +one line code fix. + +A move to a new full version number, like the rewrite of inxi to Perl, +would reflect in first version say, 2.9.01, then after a period of +testing, where most little glitches are fixed, a move to 3.0.0. These +almost never happen. I do not expect for example version 4.0 to ever +happen after the 3.0 release of early 2018, unless so many new +features are added that it actually hits 3.9, then it would roll +over to 4. + +### EOF ### @@ -1,697 +1,124 @@ -#!/usr/bin/env bash -######################################################################## -SELF_NAME='inxi' -# don't quote the following, parsers grab these too -SELF_VERSION=2.3.56 -SELF_DATE=2018-01-17 -SELF_PATCH=00 -######################################################################## -#### SPECIAL THANKS -######################################################################## -#### Special thanks to all those in #lsc and #smxi for their tireless -#### dedication helping test inxi modules. -######################################################################## -#### ABOUT INXI -######################################################################## -#### inxi is a fork of infobash 3.02, the original bash sys info tool by locsmif -#### As time permits functionality improvements and recoding will occur. -#### -#### inxi, the universal, portable, system information tool for irc. -#### Tested with Irssi, Xchat, Konversation, BitchX, KSirc, ircII, -#### Gaim/Pidgin, Weechat, KVIrc and Kopete. -#### Original infobash author and copyright holder: -#### Copyright (C) 2005-2007 Michiel de Boer a.k.a. locsmif -#### inxi version: Copyright (C) 2008-2018 Harald Hope -#### Additional features (C) Scott Rogers - kde, cpu info -#### Further fixes (listed as known): Horst Tritremmel <hjt at sidux.com> -#### Steven Barrett (aka: damentz) - usb audio patch; swap percent used patch -#### Jarett.Stevens - dmidecde -M patch for older systems with the /sys -#### -#### Current project home page/wiki/git: https://github.com/smxi/inxi -#### Documentation/wiki pages can be found at https://smxi.org/docs/inxi.htm -#### Project forums: http://techpatterns.com/forums/forum-33.html -#### IRC support: irc.oftc.net channel #smxi -#### Version control: -#### * https://github.com/smxi/inxi -#### * git: git pull https://github.com/smxi/inxi master -#### * For basic version, no gz files, much smaller, do: -#### git clone https://github.com/smxi/inxi --branch master-plain --single-branch -#### * source repository url: https://github.com/smxi/inxi -#### -#### This program is free software; you can redistribute it and/or modify -#### it under the terms of the GNU General Public License as published by -#### the Free Software Foundation; either version 3 of the License, or -#### (at your option) any later version. -#### -#### This program is distributed in the hope that it will be useful, -#### but WITHOUT ANY WARRANTY; without even the implied warranty of -#### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#### GNU General Public License for more details. -#### -#### You should have received a copy of the GNU General Public License -#### along with this program. If not, see <http://www.gnu.org/licenses/>. -#### -#### If you don't understand what Free Software is, please read (or reread) -#### this page: http://www.gnu.org/philosophy/free-sw.html -#### -#### But the main thing about Free Software is that its' about the freedom -#### of the individual user, not the corporations that try to coopt it. This -#### grows easy to forget when people confuse freedom with open source. -######################################################################## -#### -#### PACKAGE NAME NOTES -#### * Package names in (...) are the Debian Squeeze package name. Check your -#### distro for proper package name by doing this: which <application> -#### then find what package owns that application file. Or run --recommends -#### which shows package names for Debian/Ubuntu, Arch, and Fedora/Redhat/Suse -#### -#### DEPENDENCIES -#### * bash >=3.0 (bash); df, readlink, stty, tr, uname, wc (coreutils); -#### gawk (gawk); grep (grep); lspci (pciutils); ps; find (findutils); -#### perl (Modules: [HTTP::Tiny IF NO wget/curl/fetch/ftp]; Net::FTP; File::Find); -#### * Also the proc filesystem should be present and mounted for Linux -#### * Some features, like -M and -d will not work, or will work incompletely, -#### if /sys is missing -#### -#### Apparently unpatched bash 3.0 has arrays broken; bug reports: -#### http://ftp.gnu.org/gnu/bash/bash-3.0-patches/bash30-008 -#### http://lists.gnu.org/archive/html/bug-bash/2004-08/msg00144.html -#### Bash 3.1 for proper array use -#### -#### Arrays work in bash 2.05b, but "grep -Em" does not -#### -#### RECOMMENDS (Needed to run certain features, listed by option) -#### -A - for output of usb audio information: lsusb (usbutils) -#### -Ax -Nx - for audio/network module version: modinfo (module-init-tools) -#### -Dx - for hdd temp output (root only default): hddtemp (hddtemp) -#### For user level hdd temp output: sudo (sudo) -#### Note: requires user action for this feature to run as user (edit /etc/sudoers file) -#### -G - full graphics output requires: glxinfo (mesa-utils); xdpyinfo (X11-utils); -#### xrandr (x11-xserver-utils) -#### -i - IP information, local/wan - ip (iproute) legacy, not used if ip present: ifconfig (net-tools) -#### -I - uptime (procps, check Debian if changed) -#### -Ix - view current runlevel while not in X window system (or with -x): runlevel (sysvinit) -#### -m - all systems, dmidecode, unless someone can find a better way. -#### -M - for older systems whose kernel does not have /sys data for machine, dmidecode (dmidecode) -#### -o - for unmounted file system information in unmounted drives (root only default): file (file) -#### Note: requires user action for this feature to run as user (edit /etc/sudoers file) -#### For user level unmounted file system type output: sudo (sudo) -#### -s For any sensors output, fan, temp, etc: sensors (lm-sensors) -#### Note: requires setup of lm-sensors (sensors-detect and adding modules/modprobe/reboot, -#### and ideally, pwmconfig) prior to full output being available. -#### -S For desktop environment, user must be in X and have xprop installed (in X11-utils) -#### -xx@14 - it really helps to have 'tree' installed on Linux systems with /sys for -#### debugger data collection, that creates a very useful map of /sys. -######################################################################## -#### BSD Adjustments -#### * sed -i '' form supported by using SED_I="-i ''". -#### * Note: New BSD sed supports using -r instead of -E for compatibility with gnu sed -#### However, older, like FreeBSD 7.x, does not have -r so using SED_RX='-E' for this. -#### * Gnu grep options can be used if the function component is only run in linux -#### These are the options that bsd grep does not support that inxi uses: -m <number> -o -#### so make sure if you use those to have them in gnu/linux only sections. -#### It appears that freebsd uses gnu grep but openbsd uses bsd grep, however. -#### * BSD ps does not support --without-headers option, and also does not support --sort <option> -#### Tests show that -m fails to sort memory as expected, but -r does sort cpu percentage. -#### * BSD_TYPE is set with values null, debian-bsd (debian gnu/kfreebsd), bsd (all other bsds) -#### * Subshell and array closing ) must not be on their own line unless you use an explicit \ -#### to indicate that logic continues to next line where closing ) or )) are located. -######################################################################## -#### CONVENTIONS: -#### * Character Encoding: UTF-8 - this file contains special characters that must be opened and saved as UTF8 -#### * Indentation: TABS -#### * Do not use `....` (back quotes), those are totally non-reabable, use $(....). -#### * Do not use one liner flow controls. -#### The ONLY time you should use ';' (semi-colon) is in this single case: if [[ condition ]];then. -#### Never use compound 'if': ie, if [[ condition ]] && statement. -#### * Note: [[ -n $something ]] - double brackets does not require quotes for variables: ie, "$something". -#### * Always use quotes, double or single, for all string values. Really. It won't kill you. -#### * All new code/methods must be in a function. - -#### * For all boolean tests, use 'true' / 'false'. -#### !! Do NOT use 0 or 1 unless it's a function return. -#### * Avoid complicated tests in the if condition itself. -#### * To 'return' a value in a function, use 'echo <var>'. -#### * For gawk: use always if ( num_of_cores > 1 ) { hanging { starter for all blocks -#### This lets us use one method for all gawk structures, including BEGIN/END, if, for, etc -#### * Using ${VAR} is about 30% slower than $VAR because bash has to check the stuff for actions -#### SUBSHELLS ARE EXPENSIVE! - run these two if you do not believe me. -#### time for (( i=0; i<1000; i++ )) do ff='/usr/local/bin/foo.pid';ff=${ff##*/};ff=${ff%.*};done;echo $ff -#### time for (( i=0; i<1000; i++ )) do ff='/usr/local/bin/foo.pid';ff=$( basename $ff | cut -d '.' -f 1 );done;echo $ff -#### -#### VARIABLE/FUNCTION NAMING: -#### * All functions should follow standard naming--verb adjective noun. -#### ie, get_cpu_data -#### * All variables MUST be initialized / declared explicitly, either top of file, for Globals, or using local -#### * All variables should clearly explain what they are, except counters like i, j. -#### * Each word of Bash variable must be separated by '_' (underscore) (camel form), like: cpu_data -#### * Each word of Gawk variable must be like this (first word lower, following start with upper): cpuData -#### * Global variables are 'UPPER CASE', at top of this file. -#### ie, SOME_VARIABLE='' -#### * Local variables are 'lower case' and declared at the top of the function using local, always. -#### ie: local some_variable='' -#### * Locals that will be inherited by child functions have first char capitalized (so you know they are inherited). -#### ie, Some_Variable -#### * Booleans should start with b_ (local) or B_ (global) and state clearly what is being tested. -#### * Arrays should start with a_ (local) or A_ (global). -#### -#### SPECIAL NOTES: -#### * The color variable ${C2} must always be followed by a space unless you know what -#### character is going to be next for certain. Otherwise irc color codes can be accidentally -#### activated or altered. -#### * For native script konversation support (check distro for correct konvi scripts path): -#### ln -s <path to inxi> /usr/share/apps/konversation/scripts/inxi -#### DCOP doesn't like \n, so avoid using it for most output unless required, as in error messages. -#### * print_screen_output " " # requires space, not null, to avoid error in for example in irssi -#### * For logging of array data, array must be placed into the a_temp, otherwise only the first key logs -#### * In gawk search patterns, . is a wildcard EXCEPT in [0-9.] type containers, then it's a literal -#### So outside of bracketed items, it must be escaped, \. but inside, no need. Outside of gawk it should -#### be escaped in search patterns if you are using it as a literal. -#### -#### PACKAGE MANAGER DATA (note, while inxi tries to avoid using package managers to get data, sometimes -#### it's the only way to get some data): -#### * dpkg options: http://www.cyberciti.biz/howto/question/linux/dpkg-cheat-sheet.php -#### * pacman options: https://wiki.archlinux.org/index.php/Pacman_Rosetta -#### -#### As with all 'rules' there are acceptions, these are noted where used. -################################################################################### -#### KDE Konversation information. Moving from dcop(qt3/KDE3) to dbus(qt4/KDE4) -################################################################################### -#### * dcop and dbus -- these talk back to Konversation from this program -#### * Scripting info -- http://konversation.berlios.de/docs/scripting.html -#### -- http://www.kde.org.uk/apps/konversation/ -#### * dbus info -- http://dbus.freedesktop.org/doc/dbus-tutorial.html -#### view dbus info -- https://fedorahosted.org/d-feet/ -#### -- or run qdbus -#### * Konvi dbus/usage-- qdbus org.kde.konversation /irc say <server> <target-channel> <output> -#### * Python usage -- http://wiki.python.org/moin/DbusExamples (just in case) -#### -#### Because webpages come and go, the above information needs to be moved to inxi's wiki -######################################################################## -#### Valuable Resources -#### CPU flags: http://unix.stackexchange.com/questions/43539/what-do-the-flags-in-proc-cpuinfo-mean -#### Advanced Bash: http://wiki.bash-hackers.org/syntax/pe -#### gawk arrays: http://www.math.utah.edu/docs/info/gawk_12.html -#### raid mdstat: http://www-01.ibm.com/support/docview.wss?uid=isg3T1011259 -#### http://www.howtoforge.com/replacing_hard_disks_in_a_raid1_array -#### https://raid.wiki.kernel.org/index.php/Mdstat -#### dmi data: http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.0.pdf -######################################################################## -#### TESTING FLAGS -#### inxi supports advanced testing triggers to do various things, using -! <arg> -#### -! 1 - triggers default B_TESTING_1='true' to trigger some test or other -#### -! 2 - triggers default B_TESTING_2='true' to trigger some test or other -#### -! 3 - triggers B_TESTING_1='true' and B_TESTING_2='true' -#### -! 10 - triggers an update from the primary dev download server instead of source -#### -! 11 - triggers an update from source branch one - if present, of course -#### -! 12 - triggers an update from source branch two - if present, of course -#### -! 13 - triggers an update from source branch three - if present, of course -#### -! 14 - triggers an update from source branch four - if present, of course -#### -! <http://......> - Triggers an update from whatever server you list. -#### LOG FLAGS (logs to $HOME/.inxi/inxi.log with rotate 3 total) -#### -@ 8 - Basic data logging of generated data / array values -#### -@ 9 - Full logging of all data used, including cat of files and system data -#### -@ 10 - Basic data logging plus color code logging -######################################################################## -#### VARIABLES -######################################################################## - -## NOTE: we can use hwinfo if it's available in all systems, or most, to get -## a lot more data and verbosity levels going - -### DISTRO MAINTAINER FLAGS ### -# flag to allow distro maintainers to turn off update features. If false, turns off -# -U and -! testing/advanced update options, as well as removing the -U help menu item -# NOTE: Usually you want to create these in /etc/inxi.conf to avoid having to update each time -B_ALLOW_UPDATE='true' -B_ALLOW_WEATHER='true' - -### USER CONFIGS: SET IN inxi.conf file see wiki for directions ### -# http://code.google.com/p/inxi/wiki/script_configuration_files -# override in user config if desired, seems like less than .3 doesn't work as reliably -CPU_SLEEP='0.3' -FILTER_STRING='<filter>' - -# for features like help/version will fit to terminal / console screen width. Console -# widths will be dynamically set in main() based on cols in term/console -COLS_MAX_CONSOLE='115' -COLS_MAX_IRC='105' -# note, this is console out of x/display server, will also be set dynamically -# not used currently, but maybe in future -COLS_MAX_NO_DISPLAY='140' -PS_COUNT=5 -# change to less, or more if you have very slow connection -DL_TIMEOUT=8 -### END USER CONFIGS ### - -### LOCALIZATION - DO NOT CHANGE! ### -# set to default LANG to avoid locales errors with , or . -LANG=C -# Make sure every program speaks English. -LC_ALL="C" -export LC_ALL - -### ARRAYS ### -## Prep -# Clear nullglob, because it creates unpredictable situations with IFS=$'\n' ARR=($VAR) IFS="$ORIGINAL_IFS" -# type constructs. Stuff like [rev a1] is now seen as a glob expansion pattern, and fails, and -# therefore results in nothing. -shopt -u nullglob -## info on bash built in: $IFS - http://tldp.org/LDP/abs/html/internalvariables.html -# Backup the current Internal Field Separator -ORIGINAL_IFS="$IFS" - -## Initialize -A_ALSA_DATA='' -A_AUDIO_DATA='' -A_BATTERY_DATA='' -A_CMDL='' -A_CPU_CORE_DATA='' -A_CPU_DATA='' -A_CPU_TYPE_PCNT_CCNT='' -A_DEBUG_BUFFER='' - -A_GCC_VERSIONS='' -A_GLX_DATA='' -A_GRAPHICS_CARD_DATA='' -A_GRAPHIC_DRIVERS='' -A_HDD_DATA='' -A_INIT_DATA='' -A_INTERFACES_DATA='' -A_MACHINE_DATA='' -A_MEMORY_DATA='' -A_NETWORK_DATA='' -A_OPTICAL_DRIVE_DATA='' -A_PARTITION_DATA='' -A_PCICONF_DATA='' -A_PS_DATA='' -A_RAID_DATA=() -A_SENSORS_DATA='' -A_UNMOUNTED_PARTITION_DATA='' -A_WEATHER_DATA='' -A_DISPLAY_SERVER_DATA='' - -### BOOLEANS ### -## standard boolean flags ## -B_BSD_DISK_SET='false' -B_COLOR_SCHEME_SET='false' -B_CONSOLE_IRC='false' -# triggers full display of cpu flags -B_CPU_FLAGS_FULL='false' -B_CURL='true' -# test for dbus irc client -B_DBUS_CLIENT='false' -# kde dcop -B_DCOP='false' -# Debug flood override: make 'true' to allow long debug output -B_DEBUG_FLOOD='false' -# for special -i debugging cases -B_DEBUG_I='false' -B_DMIDECODE_SET='false' -# show extra output data -B_EXTRA_DATA='false' -# triggered by -xx -B_EXTRA_EXTRA_DATA='false' -B_FETCH='true' -B_FORCE_DMIDECODE='false' -B_ID_SET='false' -# override certain errors due to currupted data -B_HANDLE_CORRUPT_DATA='false' -B_LABEL_SET='false' -B_LSPCI='false' -B_LOG_COLORS='false' -B_LOG_FULL_DATA='false' -B_MAN='true' -B_MAPPER_SET='false' -B_OUTPUT_FILTER='false' -B_OVERRIDE_FILTER='false' -B_PCICONF='false' -B_PCICONF_SET='false' -# kde qdbus -B_QDBUS='false' -B_POSSIBLE_PORTABLE='false' -B_RAID_SET='false' -B_ROOT='false' -B_RUN_COLOR_SELECTOR='false' -B_RUNNING_IN_DISPLAY='false' # in x type display server -if tty >/dev/null;then - B_IRC='false' -else - B_IRC='true' -fi -# this sets the debug buffer -B_SCRIPT_UP='false' -B_SHOW_ADVANCED_NETWORK='false' -# Show sound card data -B_SHOW_AUDIO='false' -B_SHOW_BASIC_RAID='false' -B_SHOW_BASIC_CPU='false' -B_SHOW_BASIC_DISK='false' -B_SHOW_BASIC_OPTICAL='false' -B_SHOW_BATTERY='false' -B_SHOW_BATTERY_FORCED='false' -B_SHOW_CPU='false' -B_SHOW_DISPLAY_DATA='false' -B_SHOW_DISK_TOTAL='false' -B_SHOW_DISK='false' -# Show full hard disk output -B_SHOW_FULL_HDD='false' -B_SHOW_FULL_OPTICAL='false' -B_SHOW_GRAPHICS='false' -# Set this to 'false' to avoid printing the hostname, can be set false now -B_SHOW_HOST='true' -B_SHOW_INFO='false' -B_SHOW_IP='false' -B_SHOW_LABELS='false' -B_SHOW_MACHINE='false' -B_SHOW_MEMORY='false' -B_SHOW_NETWORK='false' -# either -v > 3 or -P will show partitions -B_SHOW_PARTITIONS='false' -B_SHOW_PARTITIONS_FULL='false' -B_SHOW_PS_CPU_DATA='false' -B_SHOW_PS_MEM_DATA='false' -B_SHOW_RAID='false' -# because many systems have no mdstat file, -b/-F should not show error if no raid file found -B_SHOW_RAID_R='false' -B_SHOW_REPOS='false' -B_SHOW_SENSORS='false' -# triggers only short inxi output -B_SHOW_SHORT_OUTPUT='false' -B_SHOW_SYSTEM='false' -B_SHOW_UNMOUNTED_PARTITIONS='false' -B_SHOW_UUIDS='false' -B_SHOW_WEATHER='false' -B_SYSCTL='false' -# triggers various debugging and new option testing -B_TESTING_1='false' -B_TESTING_2='false' -B_UPLOAD_DEBUG_DATA='false' -B_USB_NETWORKING='false' -# set to true here for debug logging from script start -B_USE_LOGGING='false' -B_UUID_SET='false' -B_WGET='true' -B_XORG_LOG='false' - -## Directory/file exist flags; test as [[ $(boolean) ]] not [[ $boolean ]] -B_ASOUND_DEVICE_FILE='false' -B_ASOUND_VERSION_FILE='false' -B_BASH_ARRAY='false' -B_CPUINFO_FILE='false' -B_DMESG_BOOT_FILE='false' # bsd only -B_LSB_FILE='false' -B_MDSTAT_FILE='false' -B_MEMINFO_FILE='false' -B_MODULES_FILE='false' # -B_MOUNTS_FILE='false' -B_OS_RELEASE_FILE='false' # new default distro id file? will this one work where lsb-release didn't? -B_PARTITIONS_FILE='false' # -B_PROC_DIR='false' -B_SCSI_FILE='false' - -## app tested for and present, to avoid repeat tests -B_FILE_TESTED='false' -B_HDDTEMP_TESTED='false' -B_MODINFO_TESTED='false' -B_SUDO_TESTED='false' - -# cpu 64 bit able or not. Does not tell you actual kernel/OS installed -# if ((1<<32)); then -# BITS=64 -# else -# BITS=32 -# fi -# echo $BITS - -### CONSTANTS/INITIALIZE - SOME MAY BE RESET LATER ### -BASH=${BASH_VERSION%%[^0-9]*} # some bash 4 things can be used but only if tested -DCOPOBJ="default" -DEBUG=0 # Set debug levels from 1-10 (8-10 trigger logging levels) -# Debug Buffer Index, index into a debug buffer storing debug messages until inxi is 'all up' -DEBUG_BUFFER_INDEX=0 -DISPLAY_OPT='' # for console switches -## note: the debugger rerouting to /dev/null has been moved to the end of the get_parameters function -## so -@[number] debug levels can be set if there is a failure, otherwise you can't even see the errors -SED_I='-i' # for gnu sed, will be set to -i '' for bsd sed -SED_RX='-r' # for gnu sed, will be set to -E for bsd sed for backward compatibility - -# default to false, no konversation found, 1 is native konvi (qt3/KDE3) script mode, 2 is /cmd inxi start, -## 3 is Konversation > 1.2 (qt4/KDE4) -KONVI=0 -NO_SSL='' -NO_SSL_OPT='' -# NO_CPU_COUNT=0 # Whether or not the string "dual" or similar is found in cpuinfo output. If so, avoid dups. -# This is a variable that controls how many parameters inxi will parse in a /proc/<pid>/cmdline file before stopping. -PARAMETER_LIMIT=30 -SCHEME=0 # set default scheme - do not change this, it's set dynamically -# this is set in user prefs file, to override dynamic temp1/temp2 determination of sensors output in case -# cpu runs colder than mobo -SENSORS_CPU_NO='' -# SHOW_IRC=1 to avoid showing the irc client version number, or SHOW_IRC=0 to disable client information completely. -SHOW_IRC=2 -# Verbosity level defaults to 0, this can also be set with -v0, -v2, -v3, etc as a parameter. -VERBOSITY_LEVEL=0 -# Supported number of verbosity levels, including 0 -VERBOSITY_LEVELS=7 - -### LOGGING ### -## logging eval variables, start and end function: Insert to LOGFS LOGFE when debug level >= 8 -LOGFS_STRING='log_function_data fs $FUNCNAME "$( echo $@ )"' -LOGFE_STRING='log_function_data fe $FUNCNAME' -LOGFS='' -LOGFE='' -# uncomment for debugging from script start -# LOGFS=$LOGFS_STRING -# LOGFE=$LOGFE_STRING - -### FILE NAMES/PATHS/URLS - must be non root writable ### -# File's used when present -FILE_ASOUND_DEVICE='/proc/asound/cards' -FILE_ASOUND_MODULES='/proc/asound/modules' # not used but maybe for -A? -FILE_ASOUND_VERSION='/proc/asound/version' -FILE_CPUINFO='/proc/cpuinfo' -FILE_DMESG_BOOT='/var/run/dmesg.boot' -FILE_LSB_RELEASE='/etc/lsb-release' -FILE_MDSTAT='/proc/mdstat' -FILE_MEMINFO='/proc/meminfo' -FILE_MODULES='/proc/modules' -FILE_MOUNTS='/proc/mounts' -FILE_OS_RELEASE='/etc/os-release' -FILE_PARTITIONS='/proc/partitions' -FILE_SCSI='/proc/scsi/scsi' -FILE_XORG_LOG='/var/log/Xorg.0.log' # if not found, search and replace with actual location - -FILE_PATH='' -HDDTEMP_PATH='' -MODINFO_PATH='' -SUDO_PATH='' - -ALTERNATE_FTP='' # for data uploads -ALTERNATE_WEATHER_LOCATION='' # weather alternate location -SELF_CONFIG_DIR='' -SELF_DATA_DIR='' -LOG_FILE='inxi.log' -LOG_FILE_1='inxi.1.log' -LOG_FILE_2='inxi.2.log' -MAN_FILE_DOWNLOAD='https://github.com/smxi/inxi/raw/master/inxi.1.gz' -SELF_PATH='' # filled-in in Main -SELF_DOWNLOAD='https://github.com/smxi/inxi/raw/master/' -SELF_DOWNLOAD_BRANCH_1='https://github.com/smxi/inxi/raw/one/' -SELF_DOWNLOAD_BRANCH_2='https://github.com/smxi/inxi/raw/two/' -SELF_DOWNLOAD_BRANCH_3='https://github.com/smxi/inxi/raw/three/' -SELF_DOWNLOAD_DEV='https://smxi.org/test/' -# note, you can use any ip url here as long as it's the only line on the output page. -# Also the ip address must be the last thing on that line. If you abuse this ip tool -# you will be banned from further access. Most > 24x daily automated queries to it are abuse. -WAN_IP_URL='https://smxi.org/opt/ip.php' -KONVI_CFG="konversation/scripts/$SELF_NAME.conf" # relative path to $(kde-config --path data) - -### INITIALIZE VARIABLES NULL ### -ARCH='' # cpu microarchitecture -BSD_TYPE='' -BSD_VERSION= -CMDL_MAX='' -CPU_COUNT_ALPHA='' -CURRENT_KERNEL='' -DEV_DISK_ID='' -DEV_DISK_LABEL='' -DEV_DISK_MAPPER='' -DEV_DISK_UUID='' -DMIDECODE_DATA='' -DMESG_BOOT_DATA='' -DNSTOOL='' -DOWNLOADER='wget' -IRC_CLIENT='' -IRC_CLIENT_VERSION='' -LINE_LENGTH=0 -LSPCI_V_DATA='' -LSPCI_N_DATA='' -MEMORY='' -PS_THROTTLED='' -RAID_TYPE='' -REPO_DATA='' -SYSCTL_A_DATA='' -UP_TIME='' - -### LAYOUT ### -# These two determine separators in single line output, to force irc clients not to break off sections -SEP1='~' -SEP2=' ' -# these will assign a separator to non irc states. Important! Using ':' can trigger stupid emoticon +#!/usr/bin/env perl +## infobash: Copyright (C) 2005-2007 Michiel de Boer aka locsmif +## inxi: Copyright (C) 2008-2018 Harald Hope +## Additional features (C) Scott Rogers - kde, cpu info +## Further fixes (listed as known): Horst Tritremmel <hjt at sidux.com> +## Steven Barrett (aka: damentz) - usb audio patch; swap percent used patch +## Jarett.Stevens - dmidecode -M patch for older systems with the /sys +## +## License: GNU GPL v3 or greater +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see <http://www.gnu.org/licenses/>. +## +## If you don't understand what Free Software is, please read (or reread) +## this page: http://www.gnu.org/philosophy/free-sw.html + +use strict; +use warnings; +# use diagnostics; +use 5.008; + +use Cwd qw(abs_path); # qw(abs_path);#abs_path realpath getcwd +use Data::Dumper qw(Dumper); # print_r +use File::Find; +use Getopt::Long qw(GetOptions); +# Note: default auto_abbrev is enabled, that's fine +Getopt::Long::Configure ('bundling', 'no_ignore_case', +'no_getopt_compat', 'no_auto_abbrev','pass_through'); +use POSIX qw(uname strftime ttyname); +# use feature qw(state); + +## INXI INFO ## +my $self_name='inxi'; +my $self_version='3.0.07'; +my $self_date='2018-04-17'; +my $self_patch='00'; +## END INXI INFO ## + +### INITIALIZE VARIABLES ### + +## Self data +my ($self_path, $user_config_dir, $user_config_file,$user_data_dir); + +## Debuggers +my $debug=0; +my (@t0,$end,$start,$fh_l,$log_file); # log file handle, file +my ($b_hires,$t1,$t2,$t3) = (0,0,0,0); +# NOTE: redhat removed HiRes from Perl Core Modules. Why? who knows. +if (eval {require Time::HiRes}){ + Time::HiRes->import('gettimeofday','tv_interval','usleep'); + $b_hires = 1; +} +@t0 = eval 'Time::HiRes::gettimeofday()' if $b_hires; # let's start it right away +## Hashes +my ( %alerts, %client, %colors, %dl, %files, %rows, %system_files, %use ); + +## Arrays +# ps_aux is full output, ps_cmd is only the last 10 to last +my (@app,@dmesg_boot,@dmi,@gpudata,@ifs,@ifs_bsd,@paths,@pci,@ps_aux,@ps_cmd, +@sysctl,@sysctl_battery,@sysctl_sensors,@sysctl_machine,@uname,@usb); +## Disk arrays +my (@dm_boot_disk,@dm_boot_optical,@glabel,@gpart,@labels,@lsblk,@partitions, +@raid,@sysctl_disks,@uuids); +my @test = (0,0,0,0,0); + +## Booleans +my ($b_arm,$b_console_irc,$b_debug_gz,$b_display,$b_dmesg_boot_check,$b_dmi, +$b_dmidecode_force,$b_force_display,$b_gpudata,$b_irc,$b_log,$b_log_colors, +$b_log_full,$b_man,$b_mem,$b_pci,$b_root,$b_running_in_display,$b_sysctl,$b_usb_check); +## Disk checks +my ($b_dm_boot_disk,$b_dm_boot_optical,$b_glabel,$b_lsblk,$b_partitions, +$b_partition_extra,$b_raid); +my ($b_sysctl_disk,$b_update,$b_weather) = (1,1,1); + +## System +my ($bsd_type,$language,$os) = ('','',''); +my ($cpu_sleep,$dl_timeout,$limit,$ps_count,$usb_level) = (0.35,4,10,5,0); +my $sensors_cpu_nu = 0; +my ($bits_sys); + +## Tools +my ($display,$ftp_alt,$tty_session); +my $display_opt = ''; + +## Output +my $extra = 0;# supported values: 0-3 +my $filter_string = '<filter>'; +my $line1 = "----------------------------------------------------------------------\n"; +my $line2 = "======================================================================\n"; +my $line3 = "----------------------------------------\n"; +my ($output_file,$output_type) = ('','screen'); +my $prefix = 0; # for the primiary row hash key prefix + +# these will assign a separator to non irc states. Important! Using ':' can +# trigger stupid emoticon. Note: SEP1/SEP2 from short form not used anymore. # behaviors in output on IRC, so do not use those. -SEP3_IRC='' -SEP3_CONSOLE=':' -SEP3='' # do not set, will be set dynamically -LINE1='---------------------------------------------------------------------------' -LINE2='- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -' - -# Default indentation level. NOTE: actual indent is 1 greater to allow for spacing -INDENT=10 - -### COLUMN WIDTHS ### -COLS_INNER='' ## for width minus INDENT -COLS_MAX='' - -# these will be set dynamically in main() -TERM_COLUMNS=80 -TERM_LINES=100 - -# Only for legacy user config files se we can test and convert the var name -LINE_MAX_CONSOLE='' -LINE_MAX_IRC='' - -### COLORS ### -# Defaults to 2, make this 1 for normal, 0 for no colorcodes at all. Use following variables in config -# files to change defaults for each type, or global -# Same as runtime parameter. -DEFAULT_COLOR_SCHEME=2 -## color variables - set dynamically -COLOR_SCHEME='' -C1='' -C2='' -CN='' -## Always leave these blank, these are only going to be set in inxi.conf files, that makes testing -## for user changes easier after sourcing the files -ESC='\x1b' -GLOBAL_COLOR_SCHEME='' -IRC_COLOR_SCHEME='' -IRC_CONS_COLOR_SCHEME='' -IRC_X_TERM_COLOR_SCHEME='' -CONSOLE_COLOR_SCHEME='' -VIRT_TERM_COLOR_SCHEME='' - -## Output colors -# A more elegant way to have a scheme that doesn't print color codes (neither ANSI nor mIRC) at all. See below. -unset EMPTY -# DGREY BLACK RED DRED GREEN DGREEN YELLOW DYELLOW -ANSI_COLORS="[1;30m [0;30m [1;31m [0;31m [1;32m [0;32m [1;33m [0;33m" -IRC_COLORS=" \x0314 \x0301 \x0304 \x0305 \x0309 \x0303 \x0308 \x0307" -# BLUE DBLUE MAGENTA DMAGENTA CYAN DCYAN WHITE GREY NORMAL -ANSI_COLORS="$ANSI_COLORS [1;34m [0;34m [1;35m [0;35m [1;36m [0;36m [1;37m [0;37m [0;37m" -IRC_COLORS=" $IRC_COLORS \x0312 \x0302 \x0313 \x0306 \x0311 \x0310 \x0300 \x0315 \x03" - -#ANSI_COLORS=($ANSI_COLORS); IRC_COLORS=($IRC_COLORS) -A_COLORS_AVAILABLE=( DGREY BLACK RED DRED GREEN DGREEN YELLOW DYELLOW BLUE DBLUE MAGENTA DMAGENTA CYAN DCYAN WHITE GREY NORMAL ) - -# See above for notes on EMPTY -## note: group 1: 0, 1 are null/normal -## Following: group 2: generic, light/dark or dark/light; group 3: dark on light; group 4 light on dark; -# this is the count of the first two groups, starting at zero -SAFE_COLOR_COUNT=12 -A_COLOR_SCHEMES=( -EMPTY,EMPTY,EMPTY -NORMAL,NORMAL,NORMAL - -BLUE,NORMAL,NORMAL -BLUE,RED,NORMAL -CYAN,BLUE,NORMAL -DCYAN,NORMAL,NORMAL -DCYAN,BLUE,NORMAL -DGREEN,NORMAL,NORMAL -DYELLOW,NORMAL,NORMAL -GREEN,DGREEN,NORMAL -GREEN,NORMAL,NORMAL -MAGENTA,NORMAL,NORMAL -RED,NORMAL,NORMAL - -BLACK,DGREY,NORMAL -DBLUE,DGREY,NORMAL -DBLUE,DMAGENTA,NORMAL -DBLUE,DRED,NORMAL -DBLUE,BLACK,NORMAL -DGREEN,DYELLOW,NORMAL -DYELLOW,BLACK,NORMAL -DMAGENTA,BLACK,NORMAL -DCYAN,DBLUE,NORMAL - -WHITE,GREY,NORMAL -GREY,WHITE,NORMAL -CYAN,GREY,NORMAL -GREEN,WHITE,NORMAL -GREEN,YELLOW,NORMAL -YELLOW,WHITE,NORMAL -MAGENTA,CYAN,NORMAL -MAGENTA,YELLOW,NORMAL -RED,CYAN,NORMAL -RED,WHITE,NORMAL -BLUE,WHITE,NORMAL - -RED,BLUE,NORMAL -RED,DBLUE,NORMAL -BLACK,BLUE,NORMAL -BLACK,DBLUE,NORMAL -NORMAL,BLUE,NORMAL -BLUE,MAGENTA,NORMAL -DBLUE,MAGENTA,NORMAL -BLACK,MAGENTA,NORMAL -MAGENTA,BLUE,NORMAL -MAGENTA,DBLUE,NORMAL -) - -#echo ${#A_COLOR_SCHEMES[@]};exit - -# WARNING: In the main part below (search for 'KONVI') -# there's a check for Konversation-specific config files. -# Any one of these can override the above if inxi is run -# from Konversation! - -## DISTRO DATA/ID ## -# In cases of derived distros where the version file of the base distro can also be found under /etc, -# the derived distro's version file should go first. (Such as with Sabayon / Gentoo) -DISTROS_DERIVED="antix-version aptosid-version kanotix-version knoppix-version mandrake-release mx-version pardus-release porteus-version sabayon-release siduction-version sidux-version slitaz-release solusos-release turbolinux-release zenwalk-version" -# debian_version excluded from DISTROS_PRIMARY so Debian can fall through to /etc/issue detection. Same goes for Ubuntu. -DISTROS_EXCLUDE_LIST="debian_version devuan_version ubuntu_version" -DISTROS_PRIMARY="arch-release gentoo-release redhat-release slackware-version SuSE-release" -DISTROS_LSB_GOOD="mandrake-release mandriva-release mandrakelinux-release manjaro-release" -# this is being used both by core distros and derived distros now, eg, solusos 1 uses it for solusos id, while -# debian, solusos base, uses it as well, so we have to know which it is. -DISTROS_OS_RELEASE_GOOD="arch-release SuSE-release " -## Distros with known problems -# DSL (Bash 2.05b: grep -m doesn't work; arrays won't work) --> unusable output -# Puppy Linux 4.1.2 (Bash 3.0: arrays won't work) --> works partially - -## OUTPUT FILTERS/SEARCH ## -# Note that \<ltd\> bans only words, not parts of strings; in \<corp\> you can't use punctuation characters like . or , -# we're saving about 10+% of the total script exec time by hand building the ban lists here, using hard quotes. - -BAN_LIST_NORMAL='chipset|components|computing|computer|corporation|communications|electronics|electrical|electric|gmbh|group|incorporation|industrial|international|nee|revision|semiconductor|software|technologies|technology|ltd\.|\<ltd\>|inc\.|\<inc\>|intl\.|co\.|\<co\>|corp\.|\<corp\>|\(tm\)|\(r\)|®|\(rev ..\)' -BAN_LIST_CPU='@|cpu |cpu deca|([0-9]+|single|dual|triple|tri|quad|penta|hepta|hexa|octa|multi)[ -]core|ennea|genuine|multi|processor|single|triple|[0-9\.]+ *[MmGg][Hh][Zz]' -# See github issue 75 for more details on value: *, triggers weird behaviors if present in value -# /sys/devices/virtual/dmi/id/product_name:['*'] -# this is for bash arrays AND avoiding * in arrays: ( fred * greg ) expands to the contents of the directory -BAN_LIST_ARRAY=',|\*' - -SENSORS_GPU_SEARCH='amdgpu|intel|radeon|nouveau' - -### USB networking search string data, because some brands can have other products than -### wifi/nic cards, they need further identifiers, with wildcards. -### putting the most common and likely first, then the less common, then some specifics -USB_NETWORK_SEARCH="Wi-Fi.*Adapter|Wireless.*Adapter|Ethernet.*Adapter|WLAN.*Adapter|Network.*Adapter|802\.11|Atheros|Atmel|D-Link.*Adapter|D-Link.*Wireless|Linksys|Netgea|Ralink|Realtek.*Network|Realtek.*Wireless|Realtek.*WLAN|Belkin.*Wireless|Belkin.*WLAN|Belkin.*Network" -USB_NETWORK_SEARCH="$USB_NETWORK_SEARCH|Actiontec.*Wireless|Actiontec.*Network|AirLink.*Wireless|Asus.*Network|Asus.*Wireless|Buffalo.*Wireless|Davicom|DWA-.*RangeBooster|DWA-.*Wireless|ENUWI-.*Wireless|LG.*Wi-Fi|Rosewill.*Wireless|RNX-.*Wireless|Samsung.*LinkStick|Samsung.*Wireless|Sony.*Wireless|TEW-.*Wireless|TP-Link.*Wireless|WG[0-9][0-9][0-9].*Wireless|WNA[0-9][0-9][0-9]|WNDA[0-9][0-9][0-9]|Zonet.*ZEW.*Wireless|54 Mbps" -# then a few known hard to ID ones added -# belkin=050d; d-link=07d1; netgear=0846; ralink=148f; realtek=0bda; -USB_NETWORK_SEARCH="$USB_NETWORK_SEARCH|050d:935b|0bda:8189|0bda:8197" +my %sep = ( +'s1-irc' => ':', +'s1-console' => ':', +'s2-irc' => '', +'s2-console' => ':', +); + +my %show = ('host' => 1); + +my %size = ( +'console' => 115, +# Default indentation level. NOTE: actual indent is 1 greater to allow for +# spacing +'indent' => 11, +'indent-min' => 90, +'irc' => 100, # shorter because IRC clients have nick lists etc +'max' => 0, +'no-display' => 130, +# these will be set dynamically in set_display_width() +'term' => 80, +'term-lines' => 100, +); + +## debug temp tools +$client{'test-konvi'} = 0; ######################################################################## #### STARTUP @@ -701,15637 +128,16338 @@ USB_NETWORK_SEARCH="$USB_NETWORK_SEARCH|050d:935b|0bda:8189|0bda:8197" #### MAIN #### ------------------------------------------------------------------- -main() -{ - # This must be set first so log paths are present when logging starts. - set_user_paths - - eval $LOGFS - - local color_scheme='' kde_config_app='' - # this will be used by all functions following, lower case for bash parameter expansion - local Ps_aux_Data="$( ps aux | tr '[:upper:]' '[:lower:]' )" - - # This function just initializes variables - initialize_data - - # Source global config overrides, needs to be here because some things - # can be reset that were set in initialize, but check_required_apps needs - if [[ -s /etc/$SELF_NAME.conf ]];then - source /etc/$SELF_NAME.conf - fi - # Source user config variables override /etc/inxi.conf variables - if [[ -s $SELF_CONFIG_DIR/$SELF_NAME.conf ]];then - source $SELF_CONFIG_DIR/$SELF_NAME.conf - fi - - set_display_width 'live' # can be reset with -y - - # echo SCHEME $SCHEME - # echo B_IRC $B_IRC - # echo sep3: $SEP3 - # Check for dependencies BEFORE running ANYTHING else except above functions - # Not all distro's have these depends installed by default. Don't want to run - # this if the user is requesting to see this information in the first place - # Only continue if required apps tests ok - if [[ $1 != '--recommends' ]];then - check_required_apps - check_recommended_apps - fi - # previous source location, check for bugs - - ## this needs to run before the KONVI stuff is set below - ## Konversation 1.2 apparently does not like the $PPID test in get_start_client - ## So far there is no known way to detect if qt4_konvi is the parent process - ## this method will infer qt4_konvi as parent - get_start_client - - # note: this only works if it's run from inside konversation as a script builtin or something - # only do this if inxi has been started as a konversation script, otherwise bypass this - # KONVI=3 ## for testing puroses - if [[ $KONVI -eq 1 || $KONVI -eq 3 ]];then - if [[ $KONVI -eq 1 ]]; then ## dcop Konversation (ie 1.x < 1.2(qt3)) - DCPORT="$1" - DCSERVER="$2" - DCTARGET="$3" - shift 3 - elif [[ $KONVI -eq 3 ]]; then ## dbus Konversation (> 1.2 (qt4)) - DCSERVER="$1" ##dbus testing - DCTARGET="$2" ##dbus testing - shift 2 - fi - # always have the current stable kde version tested first, - # then use fallbacks and future proofing - if type -p kde4-config &>/dev/null;then - kde_config_app='kde4-config' - elif type -p kde5-config &>/dev/null;then - kde_config_app='kde5-config' - elif type -p kde-config &>/dev/null;then - kde_config_app='kde-config' - fi - # The section below is on request of Argonel from the Konversation developer team: - # it sources config files like $HOME/.kde/share/apps/konversation/scripts/inxi.conf - if [[ -n $kde_config_app ]];then - IFS=":" - for kde_config in $( $kde_config_app --path data ) - do - if [[ -r $kde_config$KONVI_CFG ]];then - source "$kde_config$KONVI_CFG" - break - fi - done - IFS="$ORIGINAL_IFS" - fi - fi - - ## leave this for debugging dcop stuff if we get that working - # print_screen_output "DCPORT: $DCPORT" - # print_screen_output "DCSERVER: $DCSERVER" - # print_screen_output "DCTARGET: $DCTARGET" - - # first init function must be set first for colors etc. Remember, no debugger - # stuff works on this function unless you set the debugging flag manually. - # Debugging flag -@ [number] will not work until get_parameters runs. - - # "$@" passes every parameter separately quoted, "$*" passes all parameters as one quoted parameter. - # must be here to allow debugger and other flags to be set. - get_parameters "$@" - - # If no colorscheme was set in the parameter handling routine, then set the default scheme - if [[ $B_COLOR_SCHEME_SET != 'true' ]];then - # This let's user pick their color scheme. For IRC, only shows the color schemes, no interactive - # The override value only will be placed in user config files. /etc/inxi.conf can also override - if [[ $B_RUN_COLOR_SELECTOR == 'true' ]];then - select_default_color_scheme - else - # set the default, then override as required - color_scheme=$DEFAULT_COLOR_SCHEME - if [[ -n $GLOBAL_COLOR_SCHEME ]];then - color_scheme=$GLOBAL_COLOR_SCHEME - else - if [[ $B_IRC == 'false' ]];then - if [[ -n $CONSOLE_COLOR_SCHEME && -z $DISPLAY ]];then - color_scheme=$CONSOLE_COLOR_SCHEME - elif [[ -n $VIRT_TERM_COLOR_SCHEME ]];then - color_scheme=$VIRT_TERM_COLOR_SCHEME - fi - else - if [[ -n $IRC_X_TERM_COLOR_SCHEME && $B_CONSOLE_IRC == 'true' && -n $B_RUNNING_IN_DISPLAY ]];then - color_scheme=$IRC_X_TERM_COLOR_SCHEME - elif [[ -n $IRC_CONS_COLOR_SCHEME && -z $DISPLAY ]];then - color_scheme=$IRC_CONS_COLOR_SCHEME - elif [[ -n $IRC_COLOR_SCHEME ]];then - color_scheme=$IRC_COLOR_SCHEME - fi - fi - fi - set_color_scheme $color_scheme - fi - fi - if [[ $B_IRC == 'false' ]];then - SEP3=$SEP3_CONSOLE - else - # too hard to read if no colors, so force that for users on irc - if [[ $SCHEME == 0 ]];then - SEP3=$SEP3_CONSOLE - else - SEP3=$SEP3_IRC - fi - fi - - # all the pre-start stuff is in place now - B_SCRIPT_UP='true' - self_debugger "Debugger: $SELF_NAME is up and running..." - - # then create the output - print_it_out - - eval $LOGFE - # weechat's executor plugin forced me to do this, and rightfully so, because else the exit code - # from the last command is taken.. - exit 0 +sub main { +# print Dumper \@ARGV; + eval $start if $b_log; + initialize(); + ## use for start client debugging + # $debug = 10; + # set_debugger(); # for debugging of konvi issues + #my $ob_start = StartClient->new(); + #$ob_start->get_client_data(); + StartClient::get_client_data(); + # print_line( Dumper \%client); + get_options(); + set_debugger(); # right after so it's set + check_tools(); + set_colors(); + set_sep(); + # print download_file('stdout','https://') . "\n"; + generate_lines(); + eval $end if $b_log; + cleanup(); + # weechat's executor plugin forced me to do this, and rightfully so, + # because else the exit code from the last command is taken.. + exit 0; } #### ------------------------------------------------------------------- #### INITIALIZE #### ------------------------------------------------------------------- -# No args taken. -check_recommended_apps() -{ - eval $LOGFS - local bash_array_test=( "one" "two" ) - - # check for array ability of bash, this is only good for the warning at this time - # the boolean could be used later - # bash version 2.05b is used in DSL - # bash version 3.0 is used in Puppy Linux; it has a known array bug <reference to be placed here> - # versions older than 3.1 don't handle arrays - # distro's using below 2.05b are unknown, released in 2002 - if [[ ${bash_array_test[1]} -eq "two" ]];then - B_BASH_ARRAY='true' - else - self_debugger "Suggestion: update to Bash v3.1 for optimal inxi output" - fi - # test for a few apps that bsds may not have after initial tests - if type -p lspci &>/dev/null;then - B_LSPCI='true' - fi - if [[ -n $BSD_TYPE ]];then - if type -p sysctl &>/dev/null;then - B_SYSCTL='true' - fi - if type -p pciconf &>/dev/null;then - B_PCICONF='true' - fi - fi - # now setting qdbus/dcop for first run, some systems can have both by the way - if type -p qdbus &>/dev/null;then - B_QDBUS='true' - fi - if type -p dcop &>/dev/null;then - B_DCOP='true' - fi - eval $LOGFE -} - -# Determine if any of the absolutely necessary tools are absent -# No args taken. -check_required_apps() -{ - eval $LOGFS - local app_name='' - # bc removed from deps for now - local depends="df gawk grep ps readlink tr uname wc" - - if [[ -z $BSD_TYPE ]];then - depends="$depends lspci" - elif [[ $BSD_TYPE == 'bsd' ]];then - depends="$depends sysctl" - # debian-bsd has lspci but you must be root to run it - elif [[ $BSD_TYPE == 'debian-bsd' ]];then - depends="$depends sysctl lspci" - fi - # no need to add xprop because it will just give N/A if not there, but if we expand use of xprop, - # should add that here as a test, then use the B_SHOW_DISPLAY_DATA flag to trigger the tests in de function - local x_apps="xrandr xdpyinfo glxinfo" - - if [[ $B_RUNNING_IN_DISPLAY == 'true' ]];then - for app_name in $x_apps - do - if ! type -p $app_name &>/dev/null;then - self_debugger "Resuming in non X mode: $app_name not found. For package install advice run: $SELF_NAME --recommends" - B_SHOW_DISPLAY_DATA='false' - break - fi - done - fi - - app_name='' - - for app_name in $depends - do - if ! type -p $app_name &>/dev/null;then - error_handler 5 "$app_name" - fi - done - eval $LOGFE -} - -# Set PATH data so we can access all programs as user. Set BAN lists. -# initialize some boleans, these directories are used throughout the script -# some apps are used for extended functions any directory used, should be -# checked here first. -# No args taken. -initialize_data() -{ - eval $LOGFS - BSD_VERSION=$( uname -s 2>/dev/null | tr '[A-Z]' '[a-z]' ) - # note: archbsd says they are a freebsd distro, so assuming it's the same as freebsd - if [[ -z ${BSD_VERSION/*bsd*/} || -z ${BSD_VERSION/*dragonfly*/} || -z ${BSD_VERSION/*darwin*/} ]];then - if [[ -z ${BSD_VERSION/*openbsd*/} ]];then - BSD_VERSION='openbsd' - elif [[ -z ${BSD_VERSION/*darwin*/} ]];then - BSD_VERSION='darwin' - fi - # GNU/kfreebsd will by definition have GNU tools like sed/grep - if [[ -z ${BSD_VERSION/*kfreebsd*/} ]];then - BSD_TYPE='debian-bsd' # debian gnu bsd - else - BSD_TYPE='bsd' # all other bsds - SED_I="-i ''" - SED_RX='-E' - ESC=$(echo | tr '\n' '\033') - fi - fi - # now set the script BOOLEANS for files required to run features - # note that freebsd has /proc but it's empty - if [[ -d "/proc/" && -z $BSD_TYPE ]];then - B_PROC_DIR='true' - elif [[ -n $BSD_TYPE ]];then - B_PROC_DIR='false' - else - error_handler 6 - fi - initialize_paths - if type -p dig &>/dev/null;then - DNSTOOL='dig' - fi - # set downloaders. - set_downloader - if [[ -n $BSD_TYPE ]];then - if [[ -e $FILE_DMESG_BOOT ]];then - B_DMESG_BOOT_FILE='true' - fi - else - # found a case of battery existing but having nothing in it on desktop mobo - # not all laptops show the first. /proc/acpi/battery is deprecated. - if [[ -n $( ls /proc/acpi/battery 2>/dev/null ) || -n $( ls /sys/class/power_supply/ 2>/dev/null ) ]];then - B_POSSIBLE_PORTABLE='true' - fi - fi - if [[ -e $FILE_CPUINFO ]]; then - B_CPUINFO_FILE='true' - fi - if [[ -e $FILE_MEMINFO ]];then - B_MEMINFO_FILE='true' - fi - if [[ -e $FILE_ASOUND_DEVICE ]];then - B_ASOUND_DEVICE_FILE='true' - fi - if [[ -e $FILE_ASOUND_VERSION ]];then - B_ASOUND_VERSION_FILE='true' - fi - if [[ -f $FILE_LSB_RELEASE ]];then - B_LSB_FILE='true' - fi - if [[ -f $FILE_OS_RELEASE ]];then - B_OS_RELEASE_FILE='true' - fi - if [[ -e $FILE_SCSI ]];then - B_SCSI_FILE='true' - fi - if [[ -n $DISPLAY ]];then - B_SHOW_DISPLAY_DATA='true' - B_RUNNING_IN_DISPLAY='true' - fi - if [[ -e $FILE_MDSTAT ]];then - B_MDSTAT_FILE='true' - fi - if [[ -e $FILE_MODULES ]];then - B_MODULES_FILE='true' - fi - if [[ -e $FILE_MOUNTS ]];then - B_MOUNTS_FILE='true' - fi - if [[ -e $FILE_PARTITIONS ]];then - B_PARTITIONS_FILE='true' - fi - # default to the normal location, then search for it - if [[ -e $FILE_XORG_LOG ]];then - B_XORG_LOG='true' - else - # Detect location of the Xorg log file - if type -p xset &>/dev/null; then - FILE_XORG_LOG=$( xset q 2>/dev/null | grep -i 'Log file' | gawk '{print $3}') - if [[ -e $FILE_XORG_LOG ]];then - B_XORG_LOG='true' - fi - fi - fi - # gfx output will require this flag - if [[ $( whoami ) == 'root' ]];then - B_ROOT='true' - fi - eval $LOGFE -} - -initialize_paths() -{ - local path='' added_path='' b_path_found='' sys_path='' - # Extra path variable to make execute failures less likely, merged below - local extra_paths="/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/opt/local/bin" - - # this needs to be set here because various options call the parent initialize function directly. - SELF_PATH=$( dirname "$0" ) - # Fallback paths put into $extra_paths; This might, among others, help on gentoo. - # Now, create a difference of $PATH and $extra_paths and add that to $PATH: - IFS=":" - for path in $extra_paths - do - b_path_found='false' - for sys_path in $PATH - do - if [[ $path == $sys_path ]];then - b_path_found='true' - fi - done - if [[ $b_path_found == 'false' ]];then - added_path="$added_path:$path" - fi - done - - IFS="$ORIGINAL_IFS" - PATH="$PATH$added_path" - # echo "PATH='$PATH'" - ##/bin/sh -c 'echo "PATH in subshell=\"$PATH\""' -} - -# arg: $1 - version number: main/patch/date -parse_version_data() -{ - # note, this is only now used for self updater function - case $1 in - date) - SELF_DATE=$( gawk -F '=' ' - /^SELF_DATE/ { - print $NF - exit - }' "$SELF_PATH/$SELF_NAME" ) - ;; - main) - SELF_VERSION=$( gawk -F '=' ' - /^SELF_VERSION/ { - print $NF - exit - }' "$SELF_PATH/$SELF_NAME" ) - ;; - patch) - SELF_PATCH=$( gawk -F '=' ' - /^SELF_PATCH/ { - print $NF - exit - }' "$SELF_PATH/$SELF_NAME" ) - ;; - esac -} - -# Set the colorscheme -# args: $1 = <scheme number>|<"none"> -set_color_scheme() -{ - eval $LOGFS - local i='' a_output_colors='' a_color_codes='' - - if [[ $1 -ge ${#A_COLOR_SCHEMES[@]} ]];then - set -- 1 - fi - # Set a global variable to allow checking for chosen scheme later - SCHEME="$1" - if [[ $B_IRC == 'false' ]];then - a_color_codes=( $ANSI_COLORS ) - else - a_color_codes=( $IRC_COLORS ) - fi - for (( i=0; i < ${#A_COLORS_AVAILABLE[@]}; i++ )) - do - eval "${A_COLORS_AVAILABLE[i]}=\"${a_color_codes[i]}\"" - done - IFS="," - a_output_colors=( ${A_COLOR_SCHEMES[$1]} ) - IFS="$ORIGINAL_IFS" - # then assign the colors globally - C1="${!a_output_colors[0]}" - C2="${!a_output_colors[1]}" - CN="${!a_output_colors[2]}" - # ((COLOR_SCHEME++)) ## note: why is this? ## - # handle some explicit colors that are used for no color 0 - if [[ $SCHEME -eq 0 ]];then - NORMAL='' - RED='' - fi - eval $LOGFE -} - -# args: $1 - default OR override default cols max integer count -set_display_width() -{ - local cols_max_override=$1 - - if [[ $cols_max_override == 'live' ]];then +sub initialize { + set_os(); + set_path(); + set_user_paths(); + set_basics(); + system_files('set'); + get_configs(); + # set_downloader(); + set_display_width('live'); +} + +sub check_tools { + my ($action,$program,$message,@data,%commands,%hash); + if ( $b_dmi ){ + $action = 'use'; + if ($program = check_program('dmidecode')) { + my $result = system("$program -t chassis >/dev/null 2>&1"); + if (!$result){ + if ($b_root) { + @data = grabber("$program --type chassis"); + if ( grep { $_ =~ /No SMBIOS/i } @data ){ + $action = 'smbios'; + } + } + } + elsif ($result){ + $action = 'permissions'; + } + } + else { + $action = 'missing'; + } + %hash = ( + 'dmidecode' => { + 'action' => $action, + 'missing' => 'Required program dmidecode not available', + 'permissions' => 'Unable to run dmidecode. Are you root?', + 'smbios' => 'No SMBIOS data for dmidecode to process', + }, + ); + %alerts = (%alerts, %hash); + } + # note: gnu/linux has sysctl so it may be used that for something if present + # there is lspci for bsds so doesn't hurt to check it + if ($b_pci || $b_sysctl){ + if (!$bsd_type){ + if ($b_pci ){ + %hash = ('lspci' => '-n',); + %commands = (%commands,%hash); + } + } + else { + if ($b_pci ){ + %hash = ('pciconf' => '-l',); + %commands = (%commands,%hash); + } + if ($b_sysctl ){ + # note: there is a case of kernel.osrelease but it's a linux distro + %hash = ('sysctl' => 'kern.osrelease',); + %commands = (%commands,%hash); + } + } + foreach ( keys %commands ){ + $action = 'use'; + if ($program = check_program($_)) { + # > 0 means error in shell + #my $cmd = "$program $commands{$_} >/dev/null"; + #print "$cmd\n"; + $action = 'permissions' if system("$program $commands{$_} >/dev/null 2>&1"); + } + else { + $action = 'missing'; + } + %hash = ( + $_ => { + 'action' => $action, + 'missing' => "Missing system tool: $_. Output will be incomplete", + 'permissions' => "Unable to run $_. Root required?", + }, + ); + %alerts = (%alerts, %hash); + } + } + %commands = (); + if ( $show{'sensor'} ){ + %commands = ('sensors' => 'linux',); + } + # note: lsusb ships in FreeBSD ports sysutils/usbutils + if ( $usb_level ){ + %hash = ('lsusb' => 'all',); + %commands = (%commands,%hash); + %hash = ('usbdevs' => 'bsd',); + %commands = (%commands,%hash); + } + if ($show{'ip'} || ($bsd_type && $show{'network-advanced'})){ + %hash = ( + 'ip' => 'linux', + 'ifconfig' => 'all', + ); + %commands = (%commands,%hash); + } + foreach ( keys %commands ){ + $action = 'use'; + $message = 'Present and working'; + if ( ($commands{$_} eq 'linux' && $os ne 'linux' ) || ($commands{$_} eq 'bsd' && $os eq 'linux' ) ){ + $message = "No " . ucfirst($os) . " support. Is a comparable $_ tool available?"; + $action = 'platform'; + } + elsif (!check_program($_)){ + $message = "Required tool $_ not installed. Check --recommends"; + $action = 'missing'; + } + %hash = ( + $_ => { + 'action' => $action, + 'missing' => $message, + 'platform' => $message, + }, + ); + %alerts = (%alerts, %hash); + } + # print Dumper \%alerts; +} +sub set_basics { + ### LOCALIZATION - DO NOT CHANGE! ### + # set to default LANG to avoid locales errors with , or . + # Make sure every program speaks English. + $ENV{'LANG'}='C'; + $ENV{'LC_ALL'}='C'; + # remember, perl uses the opposite t/f return as shell!!! + $b_irc = ( system('tty >/dev/null') ) ? 1 : 0; + # print "birc: $b_irc\n"; + $b_display = ( $ENV{'DISPLAY'} ) ? 1 : 0; + $b_root = ( $ENV{'HOME'} eq '/root' ) ? 1 : 0; + $dl{'dl'} = 'curl'; + $dl{'curl'} = 1; + $dl{'tiny'} = 1; # note: two modules needed, tested for in set_downloader + $dl{'wget'} = 1; + $dl{'fetch'} = 1; + $client{'console-irc'} = 0; + $client{'dcop'} = (check_program('dcop')) ? 1 : 0; + $client{'qdbus'} = (check_program('qdbus')) ? 1 : 0; + $client{'konvi'} = 0; + $client{'name'} = ''; + $client{'name-print'} = ''; + $client{'su-start'} = ''; # shows sudo/su + $client{'version'} = ''; + $colors{'default'} = 2; +} + +# args: $1 - default OR override default cols max integer count. $_[0] +# is the display width override. +sub set_display_width { + my ($width) = @_; + if ( $width eq 'live' ){ ## sometimes tput will trigger an error (mageia) if irc client - if [[ $B_IRC == 'false' ]];then - if type -p tput &>/dev/null;then - TERM_COLUMNS=$(tput cols) - TERM_LINES=$(tput lines) - fi + if ( ! $b_irc ){ + if ( check_program('tput') ) { + # trips error if use qx()... + chomp($size{'term'}=qx{tput cols}); + chomp($size{'term-lines'}=qx{tput lines}); + $size{'term-cols'} = $size{'term'}; + } + # print "tc: $size{'term'} cmc: $size{'console'}\n"; # double check, just in case it's missing functionality or whatever - if [[ -z $TERM_COLUMNS || -n ${TERM_COLUMNS//[0-9]/} ]];then - TERM_COLUMNS=80 - TERM_LINES=100 - fi - fi - # Convert to new variable names if set in config files, legacy test - if [[ -n $LINE_MAX_CONSOLE ]];then - COLS_MAX_CONSOLE=$LINE_MAX_CONSOLE - fi - if [[ -n $LINE_MAX_IRC ]];then - COLS_MAX_IRC=$LINE_MAX_IRC - fi - # this lets you set different widths for in or out of display server - # if [[ $B_RUNNING_IN_DISPLAY == 'false' && -n $COLS_MAX_NO_DISPLAY ]];then - # COLS_MAX_CONSOLE=$COLS_MAX_NO_DISPLAY - # fi - # TERM_COLUMNS is set in top globals, using tput cols - # echo tc: $TERM_COLUMNS cmc: $COLS_MAX_CONSOLE - if [[ $TERM_COLUMNS -lt $COLS_MAX_CONSOLE ]];then - COLS_MAX_CONSOLE=$TERM_COLUMNS - fi + if ( $size{'term'} == 0 || $size{'term'} !~ /\d/ ){ + $size{'term'}=80; + # we'll be using this for terminal dimensions later so don't set default. + # $size{'term-lines'}=100; + } + } + # this lets you set different size for in or out of display server + # if ( ! $b_running_in_display && $configs{'COLS_MAX_NO_DISPLAY'} != 0 ){ + # $size{'console'}=$configs{'COLS_MAX_NO_DISPLAY'}; + # } + # term_cols is set in top globals, using tput cols + # print "tc: $size{'term'} cmc: $size{'console'}\n"; + if ( $size{'term'} < $size{'console'} ){ + $size{'console'}=$size{'term'}; + } # adjust, some terminals will wrap if output cols == term cols - COLS_MAX_CONSOLE=$(( $COLS_MAX_CONSOLE - 2 )) - # echo cmc: $COLS_MAX_CONSOLE + $size{'console'}=( $size{'console'} - 2 ); + # echo cmc: $size{'console'} # comes after source for user set stuff - if [[ $B_IRC == 'false' ]];then - COLS_MAX=$COLS_MAX_CONSOLE - else - COLS_MAX=$COLS_MAX_IRC - fi - else - COLS_MAX=$cols_max_override - fi - COLS_INNER=$(( $COLS_MAX - $INDENT - 1 )) - # echo cm: $COLS_MAX ci: $COLS_INNER -} - -set_downloader() -{ - # curl/wget are faster than HTTP::Tiny - if $B_CURL == 'true' && type -p curl &>/dev/null;then - DOWNLOADER='curl' - NO_SSL=' --insecure' - # wget has had some issues with not testing their code leading to -O failure - elif $B_WGET == 'true' && type -p wget &>/dev/null;then - DOWNLOADER='wget' - NO_SSL=' --no-check-certificate' - # check for bsd stuff - elif $B_FETCH == 'true' && type -p fetch &>/dev/null;then - DOWNLOADER='fetch' - NO_SSL=' --no-verify-peer' - # this is much slower than curl or wget - elif type -p perl &>/dev/null && perl -MHTTP::Tiny -e 1 &>/dev/null;then - DOWNLOADER='perl' # does not use ssl by default - elif [[ $BSD_VERSION == 'openbsd' ]] && type -p ftp &>/dev/null;then - DOWNLOADER='ftp' - else - DOWNLOADER='no-downloader' - fi - # echo $DOWNLOADER -} - -set_user_paths() -{ - local b_conf='false' b_data='false' + if ( ! $b_irc ){ + $size{'max'}=$size{'console'}; + } + else { + $size{'max'}=$size{'irc'}; + } + } + else { + $size{'max'}=$width; + } + # print "tc: $size{'term'} cmc: $size{'console'} cm: $size{'max'}\n"; +} +# NOTE: most tests internally are against !$bsd_type +sub set_os { + @uname = uname(); + $os = lc($uname[0]); + my $type = lc($uname[-1]); + $b_arm = 1 if $type =~ /arm|aarch/; + if ($type =~ /(armv[1-7]|aarch32|sparc_v9)/){ + $bits_sys = 32; + } + elsif ($type =~ /(alpha|64)/){ + $bits_sys = 64; + } + if ( $os =~ /(bsd|dragonfly|darwin)/ ){ + if ( $os =~ /openbsd/ ){ + $os = 'openbsd'; + } + elsif ($os =~ /darwin/){ + $os = 'darwin'; + } + if ($os =~ /kfreebsd/){ + $bsd_type = 'debian-bsd'; + } + else { + $bsd_type = $os; + } + } +} +# This data is hard set top of program but due to a specific project's +# foolish idea that ignoring the FSH totally is somehow a positive step +# forwards for free software, we also have to padd the results with PATH. +sub set_path { + # Extra path variable to make execute failures less likely, merged below + my (@path); + @paths = qw(/sbin /bin /usr/sbin /usr/bin /usr/local/sbin /usr/local/bin /usr/X11R6/bin); + @path = split /:/, $ENV{'PATH'} if $ENV{'PATH'}; + # print "paths: @paths\nPATH: $ENV{'PATH'}\n"; + # Create a difference of $PATH and $extra_paths and add that to $PATH: + foreach my $id (@path) { + if ( !(grep { /^$id$/ } @paths) && $id !~ /(game)/ ){ + push @paths, $id; + } + } + # print "paths: @paths\n"; +} + +sub set_sep { + if ( $b_irc ){ + # too hard to read if no colors, so force that for users on irc + if ($colors{'scheme'} == 0 ){ + $sep{'s1'} = $sep{'s1-console'}; + $sep{'s2'} = $sep{'s2-console'}; + } + else { + $sep{'s1'} = $sep{'s1-irc'}; + $sep{'s2'} = $sep{'s2-irc'}; + } + } + else { + $sep{'s1'} = $sep{'s1-console'}; + $sep{'s2'} = $sep{'s2-console'}; + } +} + +sub set_user_paths { + my ( $b_conf, $b_data ); + # this needs to be set here because various options call the parent + # initialize function directly. + $self_path = $0; + $self_path =~ s/[^\/]+$//; - if [[ -n $XDG_CONFIG_HOME ]];then - SELF_CONFIG_DIR=$XDG_CONFIG_HOME - b_conf=true - elif [[ -d $HOME/.config ]];then - SELF_CONFIG_DIR=$HOME/.config - b_conf=true - else - SELF_CONFIG_DIR="$HOME/.$SELF_NAME" - fi - if [[ -n $XDG_DATA_HOME ]];then - SELF_DATA_DIR=$XDG_DATA_HOME/$SELF_NAME - b_data=true - elif [[ -d $HOME/.local/share ]];then - SELF_DATA_DIR=$HOME/.local/share/$SELF_NAME - b_data=true - else - SELF_DATA_DIR="$HOME/.$SELF_NAME" - fi + if ( defined $ENV{'XDG_CONFIG_HOME'} && $ENV{'XDG_CONFIG_HOME'} ){ + $user_config_dir=$ENV{'XDG_CONFIG_HOME'}; + $b_conf=1; + } + elsif ( -d "$ENV{'HOME'}/.config" ){ + $user_config_dir="$ENV{'HOME'}/.config"; + $b_conf=1; + } + else { + $user_config_dir="$ENV{'HOME'}/.$self_name"; + } + if ( defined $ENV{'XDG_DATA_HOME'} && $ENV{'XDG_DATA_HOME'} ){ + $user_data_dir="$ENV{'XDG_DATA_HOME'}/$self_name"; + $b_data=1; + } + elsif ( -d "$ENV{'HOME'}/.local/share" ){ + $user_data_dir="$ENV{'HOME'}/.local/share/$self_name"; + $b_data=1; + } + else { + $user_data_dir="$ENV{'HOME'}/.$self_name"; + } # note, this used to be created/checked in specific instance, but we'll just do it # universally so it's done at script start. - if [[ ! -d $SELF_DATA_DIR ]];then - mkdir $SELF_DATA_DIR - fi - - if [[ $b_conf == 'true' && -f $HOME/.$SELF_NAME/$SELF_NAME.conf ]];then - mv -f $HOME/.$SELF_NAME/$SELF_NAME.conf $SELF_CONFIG_DIR - echo "Moved $SELF_NAME.conf from $HOME/.$SELF_NAME to $SELF_CONFIG_DIR" - fi - if [[ $b_data == 'true' && -d $HOME/.$SELF_NAME ]];then - mv -f $HOME/.$SELF_NAME/* $SELF_DATA_DIR - rm -Rf $HOME/.$SELF_NAME - echo "Moved data dir $HOME/.$SELF_NAME to $SELF_DATA_DIR" - fi - - LOG_FILE=$SELF_DATA_DIR/$LOG_FILE - LOG_FILE_1=$SELF_DATA_DIR/$LOG_FILE_1 - LOG_FILE_2=$SELF_DATA_DIR/$LOG_FILE_2 + if ( ! -d $user_data_dir ){ + mkdir $user_data_dir; + # system "echo", "Made: $user_data_dir"; + } + if ( $b_conf && -f "$ENV{'HOME'}/.$self_name/$self_name.conf" ){ + #system 'mv', "-f $ENV{'HOME'}/.$self_name/$self_name.conf", $user_config_dir; + # print "WOULD: Moved $self_name.conf from $ENV{'HOME'}/.$self_name to $user_config_dir\n"; + } + if ( $b_data && -d "$ENV{'HOME'}/.$self_name" ){ + #system 'mv', '-f', "$ENV{'HOME'}/.$self_name/*", $user_data_dir; + #system 'rm', '-Rf', "$ENV{'HOME'}/.$self_name"; + # print "WOULD: Moved data dir $ENV{'HOME'}/.$self_name to $user_data_dir\n"; + } + $log_file="$user_data_dir/$self_name.log"; + #system 'echo', "$ENV{'HOME'}/.$self_name/* $user_data_dir"; + # print "scd: $user_config_dir sdd: $user_data_dir \n"; +} +# args: 1: set|hash key to return either null or path +sub system_files { + my ($file) = @_; + if ( $file eq 'set'){ + %files = ( + 'asound-cards' => '/proc/asound/cards', + 'asound-modules' => '/proc/asound/modules', + 'asound-version' => '/proc/asound/version', + 'cpuinfo' => '/proc/cpuinfo', + 'dmesg-boot' => '/var/run/dmesg.boot', + 'lsb-release' => '/etc/lsb-release', + 'mdstat' => '/proc/mdstat', + 'meminfo' => '/proc/meminfo', + 'modules' => '/proc/modules', + 'mounts' => '/proc/mounts', + 'os-release' => '/etc/os-release', + 'partitions' => '/proc/partitions', + 'scsi' => '/proc/scsi/scsi', + 'version' => '/proc/version', + 'xorg-log' => '/var/log/Xorg.0.log' + ); + foreach ( keys %files ){ + $system_files{$_} = ( -e $files{$_} ) ? $files{$_} : ''; + } + if ( ! $system_files{'xorg-log'} && check_program('xset') ){ + my $data = qx(xset q 2>/dev/null); + foreach ( split /\n/, $data){ + if ($_ =~ /Log file/i){ + $system_files{'xorg-log'} = get_piece($_,3); + last; + } + } + } + } + else { + return $system_files{$file}; + } } - ######################################################################## #### UTILITIES ######################################################################## #### ------------------------------------------------------------------- -#### COLOR SELECTOR +#### COLORS #### ------------------------------------------------------------------- -select_default_color_scheme() +## arg: 1 - the type of action, either integer, count, or full +sub get_color_scheme { + my ($type) = @_; + eval $start if $b_log; + my @color_schemes = ( + [qw(EMPTY EMPTY EMPTY )], + [qw(NORMAL NORMAL NORMAL )], + # for dark OR light backgrounds + [qw(BLUE NORMAL NORMAL)], + [qw(BLUE RED NORMAL )], + [qw(CYAN BLUE NORMAL )], + [qw(DCYAN NORMAL NORMAL)], + [qw(DCYAN BLUE NORMAL )], + [qw(DGREEN NORMAL NORMAL )], + [qw(DYELLOW NORMAL NORMAL )], + [qw(GREEN DGREEN NORMAL )], + [qw(GREEN NORMAL NORMAL )], + [qw(MAGENTA NORMAL NORMAL)], + [qw(RED NORMAL NORMAL)], + # for light backgrounds + [qw(BLACK DGREY NORMAL)], + [qw(DBLUE DGREY NORMAL )], + [qw(DBLUE DMAGENTA NORMAL)], + [qw(DBLUE DRED NORMAL )], + [qw(DBLUE BLACK NORMAL)], + [qw(DGREEN DYELLOW NORMAL )], + [qw(DYELLOW BLACK NORMAL)], + [qw(DMAGENTA BLACK NORMAL)], + [qw(DCYAN DBLUE NORMAL)], + # for dark backgrounds + [qw(WHITE GREY NORMAL)], + [qw(GREY WHITE NORMAL)], + [qw(CYAN GREY NORMAL )], + [qw(GREEN WHITE NORMAL )], + [qw(GREEN YELLOW NORMAL )], + [qw(YELLOW WHITE NORMAL )], + [qw(MAGENTA CYAN NORMAL )], + [qw(MAGENTA YELLOW NORMAL)], + [qw(RED CYAN NORMAL)], + [qw(RED WHITE NORMAL )], + [qw(BLUE WHITE NORMAL)], + # miscellaneous + [qw(RED BLUE NORMAL )], + [qw(RED DBLUE NORMAL)], + [qw(BLACK BLUE NORMAL)], + [qw(BLACK DBLUE NORMAL)], + [qw(NORMAL BLUE NORMAL)], + [qw(BLUE MAGENTA NORMAL)], + [qw(DBLUE MAGENTA NORMAL)], + [qw(BLACK MAGENTA NORMAL)], + [qw(MAGENTA BLUE NORMAL)], + [qw(MAGENTA DBLUE NORMAL)], + ); + if ($type eq 'count' ){ + return scalar @color_schemes; + } + if ($type eq 'full' ){ + return @color_schemes; + } + else { + return @{$color_schemes[$type]}; + # print Dumper $color_schemes[$scheme_nu]; + } + eval $end if $b_log; +} + +sub set_color_scheme { + eval $start if $b_log; + my ($scheme) = @_; + $colors{'scheme'} = $scheme; + my $index = ( $b_irc ) ? 1 : 0; # defaults to non irc + + # NOTE: qw(...) kills the escape, it is NOT the same as using + # Literal "..", ".." despite docs saying it is. + my %color_palette = ( + 'EMPTY' => [ '', '' ], + 'DGREY' => [ "\e[1;30m", "\x0314" ], + 'BLACK' => [ "\e[0;30m", "\x0301" ], + 'RED' => [ "\e[1;31m", "\x0304" ], + 'DRED' => [ "\e[0;31m", "\x0305" ], + 'GREEN' => [ "\e[1;32m", "\x0309" ], + 'DGREEN' => [ "\e[0;32m", "\x0303" ], + 'YELLOW' => [ "\e[1;33m", "\x0308" ], + 'DYELLOW' => [ "\e[0;33m", "\x0307" ], + 'BLUE' => [ "\e[1;34m", "\x0312" ], + 'DBLUE' => [ "\e[0;34m", "\x0302" ], + 'MAGENTA' => [ "\e[1;35m", "\x0313" ], + 'DMAGENTA' => [ "\e[0;35m", "\x0306" ], + 'CYAN' => [ "\e[1;36m", "\x0311" ], + 'DCYAN' => [ "\e[0;36m", "\x0310" ], + 'WHITE' => [ "\e[1;37m", "\x0300" ], + 'GREY' => [ "\e[0;37m", "\x0315" ], + 'NORMAL' => [ "\e[0m", "\x03" ], + ); + my @scheme = get_color_scheme($colors{'scheme'}); + $colors{'c1'} = $color_palette{$scheme[0]}[$index]; + $colors{'c2'} = $color_palette{$scheme[1]}[$index]; + $colors{'cn'} = $color_palette{$scheme[2]}[$index]; + # print Dumper \@scheme; + # print "$colors{'c1'}here$colors{'c2'} we are!$colors{'cn'}\n"; + eval $end if $b_log; +} + +sub set_colors { + eval $start if $b_log; + # it's already been set with -c 0-43 + if ( exists $colors{'c1'} ){ + return 1; + } + # This let's user pick their color scheme. For IRC, only shows the color schemes, + # no interactive. The override value only will be placed in user config files. + # /etc/inxi.conf can also override + if (exists $colors{'selector'}){ + my $ob_selector = SelectColors->new($colors{'selector'}); + $ob_selector->select_schema(); + return 1; + } + # set the default, then override as required + my $color_scheme = $colors{'default'}; + # these are set in user configs + if (defined $colors{'global'}){ + $color_scheme = $colors{'global'}; + } + else { + if ( $b_irc ){ + if (defined $colors{'irc-virt-term'} && $b_display && $client{'console-irc'}){ + $color_scheme = $colors{'irc-virt-term'}; + } + elsif (defined $colors{'irc-console'} && !$b_display){ + $color_scheme = $colors{'irc-console'}; + } + elsif ( defined $colors{'irc-gui'}) { + $color_scheme = $colors{'irc-gui'}; + } + } + else { + if (defined $colors{'console'} && !$b_display){ + $color_scheme = $colors{'console'}; + } + elsif (defined $colors{'virt-term'}){ + $color_scheme = $colors{'virt-term'}; + } + } + } + # force 0 for | or > output, all others prints to irc or screen + if (!$b_irc && ! -t STDOUT ){ + $color_scheme = 0; + } + set_color_scheme($color_scheme); + eval $end if $b_log; +} + +## SelectColors { - eval $LOGFS - local spacer=' ' options='' user_selection='' config_variable='' - local config_file="$SELF_CONFIG_DIR/$SELF_NAME.conf" - local irc_clear="[0m" - local irc_gui='Unset' irc_console='Unset' irc_x_term='Unset' - local console='Unset' virt_term='Unset' global='Unset' - - if [[ -n $IRC_COLOR_SCHEME ]];then - irc_gui="Set: $IRC_COLOR_SCHEME" - fi - if [[ -n $IRC_CONS_COLOR_SCHEME ]];then - irc_console="Set: $IRC_CONS_COLOR_SCHEME" - fi - if [[ -n $IRC_X_TERM_COLOR_SCHEME ]];then - irc_x_term="Set: $IRC_X_TERM_COLOR_SCHEME" - fi - if [[ -n $VIRT_TERM_COLOR_SCHEME ]];then - virt_term="Set: $VIRT_TERM_COLOR_SCHEME" - fi - if [[ -n $CONSOLE_COLOR_SCHEME ]];then - console="Set: $CONSOLE_COLOR_SCHEME" - fi - if [[ -n $GLOBAL_COLOR_SCHEME ]];then - global="Set: $GLOBAL_COLOR_SCHEME" - fi - - # don't want these printing in irc since they show literally - if [[ $B_IRC == 'true' ]];then - irc_clear='' - fi - # first make output neutral so it's just plain default for console client - set_color_scheme "0" - # print_lines_basic "0" "" "" - if [[ $B_IRC == 'false' ]];then - print_lines_basic "0" "" "Welcome to $SELF_NAME! Please select the default $COLOR_SELECTION color scheme." - # print_screen_output "You will see this message only one time per user account, unless you set preferences in: /etc/$SELF_NAME.conf" - print_screen_output " " - fi - print_lines_basic "0" "" "Because there is no way to know your $COLOR_SELECTION foreground/background colors, you can set your color preferences from color scheme option list below. 0 is no colors, 1 neutral. After these, there are 4 sets: 1-dark or light backgrounds; 2-light backgrounds; 3-dark backgrounds; 4-miscellaneous." - if [[ $B_IRC == 'false' ]];then - print_lines_basic "0" "" "Please note that this will set the $COLOR_SELECTION preferences only for user: $(whoami)" - fi - print_screen_output "$LINE1" - for (( i=0; i < ${#A_COLOR_SCHEMES[@]}; i++ )) - do - if [[ $i -gt 9 ]];then - spacer=' ' - fi - # only offer the safe universal defaults - case $COLOR_SELECTION in - global|irc|irc-console|irc-virtual-terminal) - if [[ $i -gt $SAFE_COLOR_COUNT ]];then - break - fi - ;; - esac - set_color_scheme $i - print_screen_output "$irc_clear $i)$spacer${C1}Card:${C2} nVidia G86 [GeForce 8400 GS] ${C1}Display Server${C2} x11 (X.Org 1.7.7)" - done - set_color_scheme 0 - - if [[ $B_IRC == 'false' ]];then - echo -n "[0m" - - print_screen_output "$irc_clear $i)${spacer}Remove all color settings. Restore $SELF_NAME default." - print_screen_output "$irc_clear $(($i+1)))${spacer}Continue, no changes or config file setting." - print_screen_output "$irc_clear $(($i+2)))${spacer}Exit, use another terminal, or set manually." - print_screen_output "$LINE1" - print_lines_basic "0" "" "Simply type the number for the color scheme that looks best to your eyes for your $COLOR_SELECTION settings and hit ENTER. NOTE: You can bring this option list up by starting $SELF_NAME with option: -c plus one of these numbers:" - print_lines_basic "0" "" "94^(console,^no X^-^$console); 95^(terminal,^X^-^$virt_term); 96^(irc,^gui,^X^-^$irc_gui); 97^(irc,^X,^in^terminal^-^$irc_x_term); 98^(irc,^no^X^-^$irc_console); 99^(global^-^$global)" - print_lines_basic "0" "" "" - print_screen_output "Your selection(s) will be stored here: $config_file" - print_lines_basic "0" "" "Global overrides all individual color schemes. Individual schemes remove the global setting." - print_screen_output "$LINE1" - read user_selection - if [[ "$user_selection" =~ ^([0-9]+)$ && $user_selection -lt $i ]];then - case $COLOR_SELECTION in - irc) - config_variable='IRC_COLOR_SCHEME' - ;; - irc-console) - config_variable='IRC_CONS_COLOR_SCHEME' - ;; - irc-virtual-terminal) - config_variable='IRC_X_TERM_COLOR_SCHEME' - ;; - console) - config_variable='CONSOLE_COLOR_SCHEME' - ;; - virtual-terminal) - config_variable='VIRT_TERM_COLOR_SCHEME' - ;; - global) - config_variable='GLOBAL_COLOR_SCHEME' - ;; - esac - set_color_scheme $user_selection - # make file/directory first if missing - if [[ ! -f $config_file ]];then - touch $config_file - fi - if [[ -z $( grep -s "$config_variable=" $config_file ) ]];then - print_lines_basic "0" "" "Creating and updating config file for $COLOR_SELECTION color scheme now..." - echo "$config_variable=$user_selection" >> $config_file - else - print_screen_output "Updating config file for $COLOR_SELECTION color scheme now..." - sed $SED_I "s/$config_variable=.*/$config_variable=$user_selection/" $config_file - fi - # file exists now so we can go on to cleanup - case $COLOR_SELECTION in - irc|irc-console|irc-virtual-terminal|console|virtual-terminal) - sed $SED_I '/GLOBAL_COLOR_SCHEME=/d' $config_file - ;; - global) - sed $SED_I -e '/VIRT_TERM_COLOR_SCHEME=/d' -e '/CONSOLE_COLOR_SCHEME=/d' -e '/IRC_COLOR_SCHEME=/d' \ - -e '/IRC_CONS_COLOR_SCHEME=/d' -e '/IRC_X_TERM_COLOR_SCHEME=/d' $config_file - ;; - esac - elif [[ $user_selection == $i ]];then - print_screen_output "Removing all color settings from config file now..." - sed $SED_I -e '/VIRT_TERM_COLOR_SCHEME=/d' -e '/GLOBAL_COLOR_SCHEME=/d' -e '/CONSOLE_COLOR_SCHEME=/d' \ - -e '/IRC_COLOR_SCHEME=/d' -e '/IRC_CONS_COLOR_SCHEME=/d' -e '/IRC_X_TERM_COLOR_SCHEME=/d' $config_file - set_color_scheme $DEFAULT_COLOR_SCHEME - elif [[ $user_selection == $(( $i+1 )) ]];then - print_lines_basic "0" "" "Ok, continuing $SELF_NAME unchanged. You can set the colors anytime by starting with: -c 95 to 99" - if [[ -n $CONSOLE_COLOR_SCHEME && -z $DISPLAY ]];then - set_color_scheme $CONSOLE_COLOR_SCHEME - elif [[ -n $VIRT_TERM_COLOR_SCHEME ]];then - set_color_scheme $VIRT_TERM_COLOR_SCHEME - else - set_color_scheme $DEFAULT_COLOR_SCHEME - fi - elif [[ $user_selection == $(( $i+2 )) ]];then - set_color_scheme $DEFAULT_COLOR_SCHEME - print_screen_output "Ok, exiting $SELF_NAME now. You can set the colors later." - exit 0 - else - print_screen_output "Error - Invalid Selection. You entered this: $user_selection" - print_screen_output " " - select_default_color_scheme - fi - else - print_screen_output "$LINE1" - print_lines_basic "0" "" "After finding the scheme number you like, simply run this again in a terminal to set the configuration data file for your irc client. You can set color schemes for the following: start inxi with -c plus:" - print_screen_output "94 (console, no X - $console); 95 (terminal, X - $virt_term); 96 (irc, gui, X - $irc_gui);" - print_screen_output "97 (irc, X, in terminal - $irc_x_term); 98 (irc, no X - $irc_console); 99 (global - $global)" - exit 0 - fi - eval $LOGFE +package SelectColors; + +# use warnings; +# use strict; +# use diagnostics; +# use 5.008; + +my (@data,@rows,%configs,%status); +my ($type,$w_fh); +my $safe_color_count = 12; # null/normal + default color group +my $count = 0; + +# args: 1 - type +sub new { + my $class = shift; + ($type) = @_; + my $self = {}; + return bless $self, $class; +} +sub select_schema { + eval $start if $b_log; + assign_selectors(); + main::set_color_scheme(0); + set_status(); + start_selector(); + create_color_selections(); + if (! $b_irc ){ + main::check_config_file(); + get_selection(); + } + else { + print_irc_message(); + } + eval $end if $b_log; +} + +sub set_status { + $status{'console'} = (defined $colors{'console'}) ? "Set: $colors{'console'}" : 'Not Set'; + $status{'virt-term'} = (defined $colors{'virt-term'}) ? "Set: $colors{'virt-term'}" : 'Not Set'; + $status{'irc-console'} = (defined $colors{'irc-console'}) ? "Set: $colors{'irc-console'}" : 'Not Set'; + $status{'irc-gui'} = (defined $colors{'irc-gui'}) ? "Set: $colors{'irc-gui'}" : 'Not Set'; + $status{'irc-virt-term'} = (defined $colors{'irc-virt-term'}) ? "Set: $colors{'irc-virt-term'}" : 'Not Set'; + $status{'global'} = (defined $colors{'global'}) ? "Set: $colors{'global'}" : 'Not Set'; +} + +sub assign_selectors { + if ($type == 94){ + $configs{'variable'} = 'CONSOLE_COLOR_SCHEME'; + $configs{'selection'} = 'console'; + } + elsif ($type == 95){ + $configs{'variable'} = 'VIRT_TERM_COLOR_SCHEME'; + $configs{'selection'} = 'virt-term'; + } + elsif ($type == 96){ + $configs{'variable'} = 'IRC_COLOR_SCHEME'; + $configs{'selection'} = 'irc-gui'; + } + elsif ($type == 97){ + $configs{'variable'} = 'IRC_X_TERM_COLOR_SCHEME'; + $configs{'selection'} = 'irc-virt-term'; + } + elsif ($type == 98){ + $configs{'variable'} = 'IRC_CONS_COLOR_SCHEME'; + $configs{'selection'} = 'irc-console'; + } + elsif ($type == 99){ + $configs{'variable'} = 'GLOBAL_COLOR_SCHEME'; + $configs{'selection'} = 'global'; + } +} +sub start_selector { + my $whoami = getpwuid($<) || "unknown???"; + if ( ! $b_irc ){ + @data = ( + [ 0, '', '', "Welcome to $self_name! Please select the default + $configs{'selection'} color scheme."], + ); + } + @rows = ( + [ 0, '', '', "Because there is no way to know your $configs{'selection'} + foreground/background colors, you can set your color preferences from + color scheme option list below:"], + [ 0, '', '', "0 is no colors; 1 is neutral."], + [ 0, '', '', "After these, there are 4 sets:"], + [ 0, '', '', "1-dark^or^light^backgrounds; 2-light^backgrounds; + 3-dark^backgrounds; 4-miscellaneous"], + [ 0, '', '', ""], + ); + push @data, @rows; + if ( ! $b_irc ){ + @rows = ( + [ 0, '', '', "Please note that this will set the $configs{'selection'} + preferences only for user: $whoami"], + ); + push @data, @rows; + } + @rows = ( + [ 0, '', '', "$line1"], + ); + push @data, @rows; + main::print_basic(@data); + @data = (); +} +sub create_color_selections { + my $spacer = '^^'; # printer removes double spaces, but replaces ^ with ' ' + $count = ( main::get_color_scheme('count') - 1 ); + for my $i (0 .. $count){ + if ($i > 9){ + $spacer = '^'; + } + if ($configs{'selection'} =~ /^global|irc-gui|irc-console|irc-virt-term$/ && $i > $safe_color_count ){ + last; + } + main::set_color_scheme($i); + @rows = ( + [0, '', '', "$i)$spacer$colors{'c1'}Card:$colors{'c2'}^nVidia^GT218 + $colors{'c1'}Display^Server$colors{'c2'}^x11^(X.Org^1.7.7)$colors{'cn'}"], + ); + push @data, @rows; + } + main::print_basic(@data); + @data = (); + main::set_color_scheme(0); +} +sub get_selection { + my $number = $count + 1; + @data = ( + [0, '', '', ($number++) . ")^Remove all color settings. Restore $self_name default."], + [0, '', '', ($number++) . ")^Continue, no changes or config file setting."], + [0, '', '', ($number++) . ")^Exit, use another terminal, or set manually."], + [0, '', '', "$line1"], + [0, '', '', "Simply type the number for the color scheme that looks best to your + eyes for your $configs{'selection'} settings and hit <ENTER>. NOTE: You can bring this + option list up by starting $self_name with option: -c plus one of these numbers:"], + [0, '', '', "94^-^console,^not^in^desktop^-^$status{'console'}"], + [0, '', '', "95^-^terminal,^desktop^-^$status{'virt-term'}"], + [0, '', '', "96^-^irc,^gui,^desktop^-^$status{'irc-gui'}"], + [0, '', '', "97^-^irc,^desktop,^in^terminal^-^$status{'irc-virt-term'}"], + [0, '', '', "98^-^irc,^not^in^desktop^-^$status{'irc-console'}"], + [0, '', '', "99^-^global^-^$status{'global'}"], + [0, '', '', ""], + [0, '', '', "Your selection(s) will be stored here: $user_config_file"], + [0, '', '', "Global overrides all individual color schemes. Individual + schemes remove the global setting."], + [0, '', '', "$line1"], + ); + main::print_basic(@data); + @data = (); + my $response = <STDIN>; + chomp $response; + if ($response =~ /[^0-9]/ || $response > ($count + 3)){ + @data = ( + [0, '', '', "Error - Invalid Selection. You entered this: $response. Hit <ENTER> to continue."], + [0, '', '', "$line1"], + ); + main::print_basic(@data); + my $response = <STDIN>; + start_selector(); + create_color_selections(); + get_selection(); + } + else { + process_selection($response); + } +} +sub process_selection { + my $response = shift; + if ($response == ($count + 3) ){ + @data = ([0, '', '', "Ok, exiting $self_name now. You can set the colors later."],); + main::print_basic(@data); + exit 1; + } + elsif ($response == ($count + 2)){ + @data = ( + [0, '', '', "Ok, continuing $self_name unchanged."], + [0, '', '', "$line1"], + ); + main::print_basic(@data); + if ( defined $colors{'console'} && !$b_display ){ + main::set_color_scheme($colors{'console'}); + } + if ( defined $colors{'virt-term'} ){ + main::set_color_scheme($colors{'virt-term'}); + } + else { + main::set_color_scheme($colors{'default'}); + } + } + elsif ($response == ($count + 1)){ + @data = ( + [0, '', '', "Removing all color settings from config file now..."], + [0, '', '', "$line1"], + ); + main::print_basic(@data); + delete_all_config_colors(); + main::set_color_scheme($colors{'default'}); + } + else { + main::set_color_scheme($response); + @data = ( + [0, '', '', "Updating config file for $configs{'selection'} color scheme now..."], + [0, '', '', "$line1"], + ); + main::print_basic(@data); + if ($configs{'selection'} eq 'global'){ + delete_all_config_colors(); + } + set_config_color_scheme($response); + } +} +sub delete_all_config_colors { + my @file_lines = main::reader( $user_config_file ); + open( $w_fh, '>', $user_config_file ) or error_handler('open', $user_config_file, $!); + foreach ( @file_lines ) { + if ( $_ !~ /^(CONSOLE_COLOR_SCHEME|GLOBAL_COLOR_SCHEME|IRC_COLOR_SCHEME|IRC_CONS_COLOR_SCHEME|IRC_X_TERM_COLOR_SCHEME|VIRT_TERM_COLOR_SCHEME)/){ + print {$w_fh} "$_"; + } + } + close $w_fh; +} +sub set_config_color_scheme { + my $value = shift; + my @file_lines = main::reader( $user_config_file ); + my $b_found = 0; + open( $w_fh, '>', $user_config_file ) or error_handler('open', $user_config_file, $!); + foreach ( @file_lines ) { + if ( $_ =~ /^$configs{'variable'}/ ){ + $_ = "$configs{'variable'}=$value"; + $b_found = 1; + } + print $w_fh "$_\n"; + } + if (! $b_found ){ + print $w_fh "$configs{'variable'}=$value\n"; + } + close $w_fh; +} + +sub print_irc_message { + @data = ( + [ 0, '', '', "$line1"], + [ 0, '', '', "After finding the scheme number you like, simply run this again + in a terminal to set the configuration data file for your irc client. You can + set color schemes for the following: start inxi with -c plus:"], + [ 0, '', '', "94 (console,^not^in^desktop^-^$status{'console'})"], + [ 0, '', '', "95 (terminal, desktop^-^$status{'virt-term'})"], + [ 0, '', '', "96 (irc,^gui,^desktop^-^$status{'irc-gui'})"], + [ 0, '', '', "97 (irc,^desktop,^in terminal^-^$status{'irc-virt-term'})"], + [ 0, '', '', "98 (irc,^not^in^desktop^-^$status{'irc-console'})"], + [ 0, '', '', "99 (global^-^$status{'global'})"] + ); + main::print_basic(@data); + exit 1; +} + +} + +#### ------------------------------------------------------------------- +#### CONFIGS +#### ------------------------------------------------------------------- + +sub check_config_file { + $user_config_file = "$user_config_dir/$self_name.conf"; + if ( ! -f $user_config_file ){ + open( my $fh, '>', $user_config_file ) or error_handler('create', $user_config_file, $!); + } +} + +sub get_configs { + my (@configs) = @_; + my ($key, $val,@config_files); + if (!@configs){ + @config_files = ( + qq(/etc/$self_name.conf), + qq($user_config_dir/$self_name.conf) + ); + } + else { + @config_files = (@configs); + } + # Config files should be passed in an array as a param to this function. + # Default intended use: global @CONFIGS; + foreach (@config_files) { + next unless open (my $fh, '<', "$_"); + while (<$fh>) { + chomp; + s/#.*//; + s/^\s+//; + s/\s+$//; + s/'|"//g; + s/true/1/; # switch to 1/0 perl boolean + s/false/0/; # switch to 1/0 perl boolean + next unless length; + ($key, $val) = split(/\s*=\s*/, $_, 2); + get_config_item($key,$val); + # print "f: $file key: $key val: $val\n"; + } + close $fh; + } +} + +# args: 0: key; 1: value +sub get_config_item { + my ($key,$val) = @_; + if ($key eq 'ALLOW_UPDATE' || $key eq 'B_ALLOW_UPDATE') {$b_update = int($val)} + elsif ($key eq 'ALLOW_WEATHER' || $key eq 'B_ALLOW_WEATHER') {$b_weather = int($val)} + elsif ($key eq 'CPU_SLEEP') {$cpu_sleep = $val if $val =~ /^[0-9\.]$/} + elsif ($key eq 'DL_TIMEOUT') {$dl_timeout = int($val)} + elsif ($key eq 'DOWNLOADER') { + if ($val =~ /^(curl|fetch|ftp|perl|wget)$/){ + # this dumps all the other data and resets %dl for only the + # desired downloader. + $val = set_perl_downloader($val); + %dl = ('dl' => $val, $val => 1); + }} + elsif ($key eq 'FILTER_STRING') {$filter_string = $val} + elsif ($key eq 'LANGUAGE') {$language = $val if $val =~ /^(en)$/} + elsif ($key eq 'LIMIT') {$limit = int($val)} + elsif ($key eq 'OUTPUT_TYPE') {$output_type = $val if $val =~ /^json|screen|xml/} + elsif ($key eq 'PS_COUNT') {$ps_count = int($val) } + elsif ($key eq 'SENSORS_CPU_NO') {$sensors_cpu_nu = int($val)} + elsif ($key eq 'SHOW_HOST' || $key eq 'B_SHOW_HOST') { $show{'host'} = int($val)} + # layout + elsif ($key eq 'CONSOLE_COLOR_SCHEME') {$colors{'console'} = int($val)} + elsif ($key eq 'GLOBAL_COLOR_SCHEME') {$colors{'global'} = int($val)} + elsif ($key eq 'IRC_COLOR_SCHEME') {$colors{'irc-gui'} = int($val)} + elsif ($key eq 'IRC_CONS_COLOR_SCHEME') {$colors{'irc-console'} = int($val)} + elsif ($key eq 'IRC_X_TERM_COLOR_SCHEME') {$colors{'irc-virt-term'} = int($val)} + elsif ($key eq 'VIRT_TERM_COLOR_SCHEME') {$colors{'virt-term'} = int($val)} + # note: not using the old short SEP1/SEP2 + elsif ($key eq 'SEP1_IRC') {$sep{'s1-irc'} = $val} + elsif ($key eq 'SEP1_CONSOLE') {$sep{'s1-console'} = $val} + elsif ($key eq 'SEP[23]_IRC') {$sep{'s2-irc'} = $val} + elsif ($key eq 'SEP[23]_CONSOLE') {$sep{'s2-console'} = $val} + # size + elsif ($key eq 'COLS_MAX_CONSOLE') {$size{'console'} = int($val)} + elsif ($key eq 'COLS_MAX_IRC') {$size{'irc'} = int($val)} + elsif ($key eq 'COLS_MAX_NO_DISPLAY') {$size{'no-display'} = int($val)} + elsif ($key eq 'INDENT') {$size{'indent'} = int($val)} + elsif ($key eq 'INDENT_MIN') {$size{'indent-min'} = int($val)} + # print "mc: key: $key val: $val\n"; + # print Dumper (keys %size) . "\n"; } #### ------------------------------------------------------------------- #### DEBUGGERS #### ------------------------------------------------------------------- -# args: $1 - debug data type: sys|xorg|disk -debug_data_collector() +# called in the initial -@ 10 program args setting so we can get logging +# as soon as possible # will have max 3 files, inxi.log, inxi.1.log, +# inxi.2.log +sub begin_logging { + return 1 if $fh_l; # if we want to start logging for testing before options + my $log_file_2="$user_data_dir/$self_name.1.log"; + my $log_file_3="$user_data_dir/$self_name.2.log"; + my $data = ''; + $end='main::log_data("fe", (caller(1))[3], "");'; + $start='main::log_data("fs", (caller(1))[3], \@_);'; + #$t3 = tv_interval ($t0, [gettimeofday]); + $t3 = eval 'Time::HiRes::tv_interval (\@t0, [Time::HiRes::gettimeofday()]);' if $b_hires; + #print Dumper $@; + my $now = strftime "%Y-%m-%d %H:%M:%S", localtime; + # do the rotation if logfile exists + if ( -f $log_file ){ + # copy if present second to third + if ( -f $log_file_2 ){ + rename $log_file_2, $log_file_3 or error_handler('rename', "$log_file_2 -> $log_file_3", "$!"); + } + # then copy initial to second + rename $log_file, $log_file_2 or error_handler('rename', "$log_file -> $log_file_2", "$!"); + } + # now create the logfile + # print "Opening log file for reading: $log_file\n"; + open $fh_l, '>', $log_file or error_handler(4, $log_file, "$!"); + # and echo the start data + $data = $line2; + $data .= "START $self_name LOGGING:\n"; + $data .= "NOTE: HiRes timer not available.\n" if !$b_hires; + $data .= "$now\n"; + $data .= "Elapsed since start: $t3\n"; + $data .= "n: $self_name v: $self_version p: $self_patch d: $self_date\n"; + $data .= '@paths:' . joiner(\@paths, '::', 'unset') . "\n"; + $data .= $line2; + + print $fh_l $data; +} +# NOTE: no logging available until get_parameters is run, since that's what +# sets logging # in order to trigger earlier logging manually set $b_log +# to true in top variables. +# args: $1 - type [fs|fe|cat|dump|raw] OR data to log +# arg: $2 - +# arg: $one type (fs/fe/cat/dump/raw) or logged data; +# [$two is function name; [$three - function args]] +sub log_data { + return if ! $b_log; + my ($one, $two, $three) = @_; + my $args = ''; + my $data = ''; + my $spacer = ' '; + # print "1: $one 2: $two 3: $three\n"; + if ($one eq 'fs') { + if (ref $three eq 'ARRAY'){ + my @temp = @$three; + # print Data::Dumper::Dumper \@$three; + $args = "\n${spacer}Args: " . joiner($three, '; ', 'unset'); + } + else { + $args = "\n${spacer}Args: None"; + } + # $t1 = [gettimeofday]; + #$t3 = tv_interval ($t0, [gettimeofday]); + $t3 = eval 'Time::HiRes::tv_interval(\@t0, [Time::HiRes::gettimeofday()])' if $b_hires; + #print Dumper $@; + $data = "Start: Function: $two$args\n${spacer}Elapsed: $t3\n"; + $spacer=''; + } + elsif ( $one eq 'fe') { + # print 'timer:', Time::HiRes::tv_interval(\@t0, [Time::HiRes::gettimeofday()]),"\n"; + #$t3 = tv_interval ($t0, [gettimeofday]); + eval '$t3 = Time::HiRes::tv_interval(\@t0, [Time::HiRes::gettimeofday()])' if $b_hires; + #print Dumper $t3; + $data = "${spacer}Elapsed: $t3\nEnd: Function: $two\n"; + $spacer=''; + } + elsif ( $one eq 'cat') { + if ( $b_log_full ){ + for my $file ($two){ + my $contents = do { local( @ARGV, $/ ) = $file; <> }; # or: qx(cat $file) + $data = "$data${line3}Full file data: $file\n\n$contents\n$line3\n"; + } + $spacer=''; + } + } + elsif ($one eq 'cmd'){ + $data = "Command: $two\n"; + $data .= qx($two); + } + elsif ($one eq 'data'){ + $data = "$two\n"; + } + elsif ( $one eq 'dump') { + $data = "$two:\n"; + if (ref $three eq 'HASH'){ + $data .= Data::Dumper::Dumper \%$three; + } + elsif (ref $three eq 'ARRAY'){ + # print Data::Dumper::Dumper \@$three; + $data .= Data::Dumper::Dumper \@$three; + } + else { + $data .= Data::Dumper::Dumper $three; + } + $data .= "\n"; + # print $data; + } + elsif ( $one eq 'raw') { + if ( $b_log_full ){ + $data = "\n${line3}Raw System Data:\n\n$two\n$line3"; + $spacer=''; + } + } + else { + $data = "$two\n"; + } + # print "d: $data"; + if ($data){ + print $fh_l "$spacer$data"; + } +} + +sub set_debugger { + if ( $debug < 10 || $debug > 12){ + $end = ''; + $start = ''; + if ( $debug >= 20 ){ + error_handler('not-in-irc', 'debug data generator') if $b_irc; + my $option = ( $debug > 22 ) ? 'main-full' : 'main'; + $b_debug_gz = 1 if ($debug == 22 || $debug == 24); + my $ob_sys = SystemDebugger->new($option); + $ob_sys->run_debugger(); + $ob_sys->upload_file($ftp_alt) if $debug > 20; + exit 0; + } + } + elsif ($debug >= 10 && $debug <= 12){ + $b_log = 1; + if ($debug == 11){ + $b_log_full = 1; + } + elsif ($debug == 12){ + $b_log_colors = 1; + } + begin_logging(); + } +} + +## SystemDebugger { - local sys_data_file='' error='' bsd_string='' sys_traverse_data='' - local xorg_d_files='' xorg_file='' a_distro_ids='' - local completed_gz_file='' Ftp_Upload='ftp.techpatterns.com/incoming' - local Line='-------------------------' - local start_directory=$( pwd ) - local host='' debug_i='' root_string='' b_uploaded='false' - - if [[ -n $ALTERNATE_FTP ]];then - Ftp_Upload=$ALTERNATE_FTP - fi - - if (( "$BASH" >= 4 ));then - host="${HOSTNAME,,}" - else - host=$( tr '[A-Z]' '[a-z]' <<< "$HOSTNAME" ) - fi - if [[ $B_DEBUG_I == 'true' ]];then - debug_i='i' - fi - if [[ -n $host ]];then - host=${host// /-} - else - host="-no-host" - fi - if [[ -n $BSD_TYPE ]];then - bsd_string="-$BSD_TYPE-$BSD_VERSION" - fi - if [[ $( whoami ) == 'root' ]];then - root_string='-root' - fi - - local Debug_Data_Dir="$SELF_NAME$bsd_string-$host-$(date +%Y%m%d-%H%M%S)-$1$root_string" - local debug_gz="$Debug_Data_Dir.tar.gz" +package SystemDebugger; + +# use File::Find q(find); +#no warnings 'File::Find'; +# use File::Spec::Functions; +#use File::Copy; +#use POSIX qw(strftime); + +my $option = 'main'; +my $upload = ''; +my $data_dir = ''; +my $debug_dir = ''; +my $debug_gz = ''; +my @content = (); +my $b_debug = 0; +my $b_delete_dir = 1; +# args: 1 - type +# args: 2 - upload +sub new { + my $class = shift; + ($option) = @_; + my $self = {}; + # print "$f\n"; + # print "$option\n"; + return bless $self, $class; +} + +sub run_debugger { + require File::Copy; + import File::Copy; + require File::Spec::Functions; + import File::Spec::Functions; + + print "Starting $self_name debugging data collector...\n"; + create_debug_directory(); + print "Note: for dmidecode data you must be root.\n" if !$b_root; + print $line3; + if (!$b_debug){ + audio_data(); + disk_data(); + display_data(); + network_data(); + perl_modules(); + system_data(); + } + system_files(); + print $line3; + if (!$b_debug){ + if ( -d '/sys' && main::count_dir_files('/sys') ){ + sys_tree(); + sys_traverse_data(); + } + else { + print "Skipping /sys data collection. /sys not present, or empty.\n"; + } + print $line3; + } + run_self(); + print $line3; + compress_dir(); +} + +sub create_debug_directory { + my $host = main::get_hostname(); + $host =~ s/ /-/g; + $host ||= 'no-host'; + my ($arm_string,$bsd_string,$root_string) = ('','',''); + # note: Time::Piece was introduced in perl 5.9.5 + my ($sec,$min,$hour,$mday,$mon,$year) = localtime; + my $version = substr($self_version,0,3); + $year = $year+1900; + $mon += 1; + if (length($sec) == 1) {$sec = "0$sec";} + if (length($min) == 1) {$min = "0$min";} + if (length($hour) == 1) {$hour = "0$hour";} + if (length($mon) == 1) {$mon = "0$mon";} + if (length($mday) == 1) {$mday = "0$mday";} + + my $today = "$year-$mon-${mday}_$hour$min$sec"; + # my $date = strftime "-%Y-%m-%d_", localtime; + if ($b_root){ + $root_string = '-root'; + } + $bsd_string = "-BSD-$bsd_type" if $bsd_type; + $arm_string = '-ARM' if $b_arm; + $debug_dir = "$self_name$arm_string$bsd_string-$host-$today$root_string-$version"; + $debug_gz = "$debug_dir.tar.gz"; + $data_dir = "$user_data_dir/$debug_dir"; + if ( -d $data_dir ){ + unlink $data_dir or main::error_handler('remove', "$data_dir", "$!"); + } + mkdir $data_dir or main::error_handler('mkdir', "$data_dir", "$!"); + if ( -e "$user_data_dir/$debug_gz" ){ + #rmdir "$user_data_dir$debug_gz" or main::error_handler('remove', "$user_data_dir/$debug_gz", "$!"); + print "Failed removing leftover directory:\n$user_data_dir$debug_gz error: $?" if system('rm','-rf',"$user_data_dir$debug_gz"); + } + print "Data going into:\n$data_dir\n"; +} +sub compress_dir { + print "Creating tar.gz compressed file of this material...\n"; + print "File: $debug_gz\n"; + system("cd $user_data_dir; tar -czf $debug_gz $debug_dir"); + print "Removing $data_dir...\n"; + #rmdir $data_dir or print "failed removing: $data_dir error: $!\n"; + return 1 if !$b_delete_dir; + if (system('rm','-rf',$data_dir) ){ + print "Failed removing: $data_dir\nError: $?\n"; + } + else { + print "Directory removed.\n"; + } +} +# NOTE: incomplete, don't know how to ever find out +# what sound server is actually running, and is in control +sub audio_data { + my (%data,@files,@files2); + print "Collecting audio data...\n"; + my @cmds = ( + ['aplay', '-l'], # alsa + ['pactl', 'list'], # pulseaudio + ); + run_commands(\@cmds,'audio'); + @files = main::globber('/proc/asound/card*/codec*'); + if (@files){ + my $asound = qx(head -n 1 /proc/asound/card*/codec* 2>&1); + $data{'proc-asound-codecs'} = $asound; + } + else { + $data{'proc-asound-codecs'} = undef; + } - if [[ $B_IRC == 'false' ]];then - echo "Starting $SELF_NAME debugging data collection type: $1" - cd $SELF_DATA_DIR - if [[ -d $SELF_DATA_DIR/$Debug_Data_Dir ]];then - echo "Deleting previous $SELF_NAME debugger data directory..." - rm -rf $SELF_DATA_DIR/$Debug_Data_Dir - fi - mkdir $SELF_DATA_DIR/$Debug_Data_Dir - if [[ -f $SELF_DATA_DIR/$debug_gz ]];then - echo 'Deleting previous tar.gz file...' - rm -f $SELF_DATA_DIR/$debug_gz - fi - echo "Data going into: $SELF_DATA_DIR/$Debug_Data_Dir" - echo 'Note: for dmidecode data you must be root.' - echo $Line - echo "Collecting system data..." - - # bsd tools http://cb.vu/unixtoolbox.xhtml - # freebsd - if type -p pciconf &>/dev/null;then - pciconf -l -cv &> $Debug_Data_Dir/bsd-pciconf-cvl.txt - pciconf -vl &> $Debug_Data_Dir/bsd-pciconf-vl.txt - pciconf -l &> $Debug_Data_Dir/bsd-pciconf-l.txt - else - touch $Debug_Data_Dir/bsd-pciconf-absent - fi - # openbsd - if type -p pcidump &>/dev/null;then - pcidump &> $Debug_Data_Dir/bsd-pcidump-openbsd.txt - pcidump -v &> $Debug_Data_Dir/bsd-pcidump-v-openbsd.txt - else - touch $Debug_Data_Dir/bsd-pcidump-openbsd-absent - fi - # netbsd - if type -p pcictl &>/dev/null;then - pcictl list &> $Debug_Data_Dir/bsd-pcictl-list-netbsd.txt - pcictl list -n &> $Debug_Data_Dir/bsd-pcictl-list-n-netbsd.txt - else - touch $Debug_Data_Dir/bsd-pcictl-netbsd-absent - fi - if type -p sysctl &>/dev/null;then - sysctl -a &> $Debug_Data_Dir/bsd-sysctl-a.txt - else - touch $Debug_Data_Dir/bsd-sysctl-absent - fi - if type -p usbdevs &>/dev/null;then - usbdevs -v &> $Debug_Data_Dir/bsd-usbdevs-v.txt - else - touch $Debug_Data_Dir/bsd-usbdevs-absent - fi - if type -p kldstat &>/dev/null;then - kldstat &> $Debug_Data_Dir/bsd-kldstat.txt - else - touch $Debug_Data_Dir/bsd-kldstat-absent - fi - # diskinfo -v <disk> - # fdisk <disk> - dmidecode &> $Debug_Data_Dir/dmidecode.txt - dmesg &> $Debug_Data_Dir/dmesg.txt - lscpu &> $Debug_Data_Dir/lscpu.txt - lspci &> $Debug_Data_Dir/lspci.txt - lspci -k &> $Debug_Data_Dir/lspci-k.txt - lspci -knn &> $Debug_Data_Dir/lspci-knn.txt - lspci -n &> $Debug_Data_Dir/lspci-n.txt - lspci -nn &> $Debug_Data_Dir/lspci-nn.txt - lspci -mm &> $Debug_Data_Dir/lspci-mm.txt - lspci -mmnn &> $Debug_Data_Dir/lspci-mmnn.txt - lspci -mmnnv &> $Debug_Data_Dir/lspci-mmnnv.txt - lspci -v &> $Debug_Data_Dir/lspci-v.txt - if type -p lsusb &>/dev/null;then - lsusb &> $Debug_Data_Dir/lsusb.txt - lsusb -v &> $Debug_Data_Dir/lsusb-v.txt - else - touch $Debug_Data_Dir/lsusb-absent - fi - if type -p hciconfig &>/dev/null;then - hciconfig -a &> $Debug_Data_Dir/hciconfig-a.txt - else - touch $Debug_Data_Dir/hciconfig-absent - fi - ps aux &> $Debug_Data_Dir/ps-aux.txt - ps -e &> $Debug_Data_Dir/ps-e.txt - ps -p 1 &> $Debug_Data_Dir/ps-p-1.txt - runlevel &> $Debug_Data_Dir/runlevel.txt - if type -p rc-status &>/dev/null;then - rc-status -a &> $Debug_Data_Dir/rc-status-a.txt - rc-status -l &> $Debug_Data_Dir/rc-status-l.txt - rc-status -r &> $Debug_Data_Dir/rc-status-r.txt - else - touch $Debug_Data_Dir/rc-status-absent - fi - if type -p systemctl &>/dev/null;then - systemctl list-units &> $Debug_Data_Dir/systemctl-list-units.txt - systemctl list-units --type=target &> $Debug_Data_Dir/systemctl-list-units-target.txt - else - touch $Debug_Data_Dir/systemctl-absent - fi - if type -p initctl &>/dev/null;then - initctl list &> $Debug_Data_Dir/initctl-list.txt - else - touch $Debug_Data_Dir/initctl-absent - fi - sensors &> $Debug_Data_Dir/sensors.txt - if type -p strings &>/dev/null;then - touch $Debug_Data_Dir/strings-present - else - touch $Debug_Data_Dir/strings-absent - fi - # leaving this commented out to remind that some systems do not - # support strings --version, but will just simply hang at that command - # which you can duplicate by simply typing: strings then hitting enter, you will get hang. - # strings --version &> $Debug_Data_Dir/strings.txt - if type -p nvidia-smi &>/dev/null;then - nvidia-smi -q &> $Debug_Data_Dir/nvidia-smi-q.txt - nvidia-smi -q -x &> $Debug_Data_Dir/nvidia-smi-xq.txt - else - touch $Debug_Data_Dir/nvidia-smi-absent - fi - echo $CC &> $Debug_Data_Dir/cc-content.txt - ls /usr/bin/gcc* &> $Debug_Data_Dir/gcc-sys-versions.txt - if type -p gcc &>/dev/null;then - gcc --version &> $Debug_Data_Dir/gcc-version.txt - else - touch $Debug_Data_Dir/gcc-absent - fi - if type -p clang &>/dev/null;then - clang --version &> $Debug_Data_Dir/clang-version.txt - else - touch $Debug_Data_Dir/clang-absent - fi - if type -p systemd-detect-virt &>/dev/null;then - systemd-detect-virt &> $Debug_Data_Dir/systemd-detect-virt-info.txt - else - touch $Debug_Data_Dir/systemd-detect-virt-absent - fi - echo "Collecting Perl module data..." - if type -p perl &>/dev/null;then - perl -MFile::Find=find -MFile::Spec::Functions -Tlwe 'find { wanted => sub { print canonpath $_ if /\.pm\z/ }, no_chdir => 1 }, @INC' 2>/dev/null | sort &> $Debug_Data_Dir/perl-modules.txt - else - touch $Debug_Data_Dir/perl-missing.txt - fi - echo "Collecting system file data..." - cat /proc/1/comm &> $Debug_Data_Dir/proc-1-comm.txt - if type -t get_repo_data &>/dev/null;then - get_repo_data "$SELF_DATA_DIR/$Debug_Data_Dir" - fi - head -n 1 /proc/asound/card*/codec* &> $Debug_Data_Dir/proc-asound-card-codec.txt - if [[ -f /proc/version ]];then - cat /proc/version &> $Debug_Data_Dir/proc-version.txt - else - touch $Debug_Data_Dir/proc-version-absent - fi - local id_dir='/sys/class/power_supply/' - local ids=$( ls $id_dir 2>/dev/null ) - if [[ -n $ids ]];then - for batid in $ids - do - cat $id_dir$batid'/uevent' &> $Debug_Data_Dir/sys-power-supply-$batid.txt - done - else - touch $Debug_Data_Dir/sys-power-supply-none - fi - if type -p shopt &>/dev/null;then - shopt -s nullglob - a_distro_ids=(/etc/*[-_]{release,version}) - shopt -u nullglob - echo ${a_distro_ids[@]} &> $Debug_Data_Dir/etc-distro-files.txt - for distro_file in ${a_distro_ids[@]} /etc/issue - do - if [[ -f $distro_file ]];then - cat $distro_file &> $Debug_Data_Dir/distro-file${distro_file//\//-} - fi - done - fi - cat /etc/src.conf &> $Debug_Data_Dir/bsd-etc-src-conf.txt - cat /etc/make.conf &> $Debug_Data_Dir/bsd-etc-make-conf.txt - cat /etc/issue &> $Debug_Data_Dir/etc-issue.txt - cat $FILE_LSB_RELEASE &> $Debug_Data_Dir/lsb-release.txt - cat $FILE_OS_RELEASE &> $Debug_Data_Dir/os-release.txt - cat $FILE_ASOUND_DEVICE &> $Debug_Data_Dir/proc-asound-device.txt - cat $FILE_ASOUND_VERSION &> $Debug_Data_Dir/proc-asound-version.txt - cat $FILE_CPUINFO &> $Debug_Data_Dir/proc-cpu-info.txt - cat $FILE_MEMINFO &> $Debug_Data_Dir/proc-meminfo.txt - cat $FILE_MODULES &> $Debug_Data_Dir/proc-modules.txt - cat /proc/net/arp &> $Debug_Data_Dir/proc-net-arp.txt - # bsd data - cat /var/run/dmesg.boot &> $Debug_Data_Dir/bsd-var-run-dmesg.boot.txt - echo $COLS_INNER &> $Debug_Data_Dir/cols-inner.txt - echo $XDG_CONFIG_HOME &> $Debug_Data_Dir/xdg_config_home.txt - echo $XDG_CONFIG_DIRS &> $Debug_Data_Dir/xdg_config_dirs.txt - echo $XDG_DATA_HOME &> $Debug_Data_Dir/xdg_data_home.txt - echo $XDG_DATA_DIRS &> $Debug_Data_Dir/xdg_data_dirs.txt - if type -t check_recommends_user_output &>/dev/null;then - check_recommends_user_output &> $Debug_Data_Dir/check-recommends-user-output.txt - fi - if [[ $1 == 'xorg' || $1 == 'all' ]];then - if [[ $B_RUNNING_IN_DISPLAY != 'true' ]];then - echo 'Warning: only some of the data collection can occur if you are not in X' - touch $Debug_Data_Dir/warning-user-not-in-x - fi - if [[ $B_ROOT == 'true' ]];then - echo 'Warning: only some of the data collection can occur if you are running as Root user' - touch $Debug_Data_Dir/warning-root-user - fi - echo 'Collecting Xorg log and xorg.conf files...' - if [[ -e $FILE_XORG_LOG ]];then - cat $FILE_XORG_LOG &> $Debug_Data_Dir/xorg-log-file.txt - else - touch $Debug_Data_Dir/xorg-log-file-absent - fi - if [[ -e /etc/X11/xorg.conf ]];then - cat /etc/X11/xorg.conf &> $Debug_Data_Dir/xorg-conf.txt - else - touch $Debug_Data_Dir/xorg-conf-file-absent - fi - if [[ -n $( ls /etc/X11/xorg.conf.d/ 2>/dev/null ) ]];then - ls /etc/X11/xorg.conf.d &> $Debug_Data_Dir/ls-etc-x11-xorg-conf-d.txt - xorg_d_files=$(ls /etc/X11/xorg.conf.d) - for xorg_file in $xorg_d_files - do - cat /etc/X11/xorg.conf.d/$xorg_file &> $Debug_Data_Dir/xorg-conf-d-$xorg_file.txt - done - else - touch $Debug_Data_Dir/xorg-conf-d-files-absent - fi - echo 'Collecting X, xprop, glxinfo, xrandr, xdpyinfo data, wayland, weston...' - if type -p weston-info &>/dev/null; then - weston-info &> $Debug_Data_Dir/weston-info.txt - else - touch $Debug_Data_Dir/weston-info-absent - fi - if type -p weston &>/dev/null; then - weston --version &> $Debug_Data_Dir/weston-version.txt - else - touch $Debug_Data_Dir/weston-absent - fi - if type -p xprop &>/dev/null; then - xprop -root &> $Debug_Data_Dir/xprop_root.txt - else - touch $Debug_Data_Dir/xprop-absent - fi - if type -p glxinfo &>/dev/null; then - glxinfo &> $Debug_Data_Dir/glxinfo-full.txt - glxinfo -B &> $Debug_Data_Dir/glxinfo-B.txt - else - touch $Debug_Data_Dir/glxinfo-absent - fi - if type -p xdpyinfo &>/dev/null; then - xdpyinfo &> $Debug_Data_Dir/xdpyinfo.txt - else - touch $Debug_Data_Dir/xdpyinfo-absent - fi - if type -p xrandr &>/dev/null; then - xrandr &> $Debug_Data_Dir/xrandr.txt - else - touch $Debug_Data_Dir/xrandr-absent - fi - if type -p X &>/dev/null; then - X -version &> $Debug_Data_Dir/x-version.txt - else - touch $Debug_Data_Dir/x-absent - fi - if type -p Xorg &>/dev/null; then - Xorg -version &> $Debug_Data_Dir/xorg-version.txt - else - touch $Debug_Data_Dir/xorg-absent - fi - echo $GNOME_DESKTOP_SESSION_ID &> $Debug_Data_Dir/gnome-desktop-session-id.txt - # kde 3 id - echo $KDE_FULL_SESSION &> $Debug_Data_Dir/kde3-full-session.txt - echo $KDE_SESSION_VERSION &> $Debug_Data_Dir/kde-gte-4-session-version.txt - if type -p kf5-config &>/dev/null; then - kf5-config --version &> $Debug_Data_Dir/kde-kf5-config-version-data.txt - elif type -p kf6-config &>/dev/null; then - kf6-config --version &> $Debug_Data_Dir/kde-kf6-config-version-data.txt - elif type -p kf$KDE_SESSION_VERSION-config &>/dev/null; then - kf$KDE_SESSION_VERSION-config --version &> $Debug_Data_Dir/kde-kf$KDE_SESSION_VERSION-KSV-config-version-data.txt - else - touch $Debug_Data_Dir/kde-kf-config-absent - fi - if type -p plasmashell &>/dev/null; then - plasmashell --version &> $Debug_Data_Dir/kde-plasmashell-version-data.txt - else - touch $Debug_Data_Dir/kde-plasmashell-absent - fi - if type -p kwin_x11 &>/dev/null; then - kwin_x11 --version &> $Debug_Data_Dir/kde-kwin_x11-version-data.txt - else - touch $Debug_Data_Dir/kde-kwin_x11-absent - fi - if type -p kded4 &>/dev/null; then - kded4 --version &> $Debug_Data_Dir/kded4-version-data.txt - elif type -p kded5 &>/dev/null; then - kded5 --version &> $Debug_Data_Dir/kded5-version-data.txt - elif type -p kded &>/dev/null; then - kded --version &> $Debug_Data_Dir/kded-version-data.txt - else - touch $Debug_Data_Dir/kded-$KDE_SESSION_VERSION-absent - fi - # kde 5/plasma desktop 5, this is maybe an extra package and won't be used - if type -p about-distro &>/dev/null; then - about-distro &> $Debug_Data_Dir/kde-about-distro.txt - else - touch $Debug_Data_Dir/kde-about-distro-absent - fi - echo $XDG_CURRENT_DESKTOP &> $Debug_Data_Dir/xdg-current-desktop.txt - echo $XDG_SESSION_DESKTOP &> $Debug_Data_Dir/xdg-session-desktop.txt - echo $DESKTOP_SESSION &> $Debug_Data_Dir/desktop-session.txt - echo $GDMSESSION &> $Debug_Data_Dir/gdmsession.txt - # wayland data collectors: - echo $XDG_SESSION_TYPE &> $Debug_Data_Dir/xdg-session-type.txt - echo $WAYLAND_DISPLAY &> $Debug_Data_Dir/wayland-display.txt - echo $GDK_BACKEND &> $Debug_Data_Dir/gdk-backend.txt - echo $QT_QPA_PLATFORM &> $Debug_Data_Dir/qt-qpa-platform.txt - echo $CLUTTER_BACKEND &> $Debug_Data_Dir/clutter-backend.txt - echo $SDL_VIDEODRIVER &> $Debug_Data_Dir/sdl-videodriver.txt - if type -p loginctl &>/dev/null;then - loginctl --no-pager list-sessions &> $Debug_Data_Dir/loginctl-list-sessions.txt - else - touch $Debug_Data_Dir/loginctl-absent - fi - fi - if [[ $1 == 'disk' || $1 == 'all' ]];then - echo 'Collecting dev, label, disk, uuid data, df...' - ls -l /dev &> $Debug_Data_Dir/dev-data.txt - ls -l /dev/disk &> $Debug_Data_Dir/dev-disk-data.txt - ls -l /dev/disk/by-id &> $Debug_Data_Dir/dev-disk-id-data.txt - ls -l /dev/disk/by-label &> $Debug_Data_Dir/dev-disk-label-data.txt - ls -l /dev/disk/by-uuid &> $Debug_Data_Dir/dev-disk-uuid-data.txt - # http://comments.gmane.org/gmane.linux.file-systems.zfs.user/2032 - ls -l /dev/disk/by-wwn &> $Debug_Data_Dir/dev-disk-wwn-data.txt - ls -l /dev/disk/by-path &> $Debug_Data_Dir/dev-disk-path-data.txt - ls -l /dev/mapper &> $Debug_Data_Dir/dev-disk-mapper-data.txt - readlink /dev/root &> $Debug_Data_Dir/dev-root.txt - df -h -T -P --exclude-type=aufs --exclude-type=squashfs --exclude-type=unionfs --exclude-type=devtmpfs --exclude-type=tmpfs --exclude-type=iso9660 --exclude-type=devfs --exclude-type=linprocfs --exclude-type=sysfs --exclude-type=fdescfs &> $Debug_Data_Dir/df-h-T-P-excludes.txt - df -T -P --exclude-type=aufs --exclude-type=squashfs --exclude-type=unionfs --exclude-type=devtmpfs --exclude-type=tmpfs --exclude-type=iso9660 --exclude-type=devfs --exclude-type=linprocfs --exclude-type=sysfs --exclude-type=fdescfs &> $Debug_Data_Dir/df-T-P-excludes.txt - df -T -P --exclude-type=aufs --exclude-type=squashfs --exclude-type=unionfs --exclude-type=devtmpfs --exclude-type=tmpfs --exclude-type=iso9660 --exclude-type=devfs --exclude-type=linprocfs --exclude-type=sysfs --exclude-type=fdescfs --total &> $Debug_Data_Dir/df-T-P-excludes-total.txt - df -h -T &> $Debug_Data_Dir/bsd-df-h-T-no-excludes.txt - df -h &> $Debug_Data_Dir/bsd-df-h-no-excludes.txt - df -k -T &> $Debug_Data_Dir/bsd-df-k-T-no-excludes.txt - df -k &> $Debug_Data_Dir/bsd-df-k-no-excludes.txt - atacontrol list &> $Debug_Data_Dir/bsd-atacontrol-list.txt - camcontrol devlist &> $Debug_Data_Dir/bsd-camcontrol-devlist.txt - # bsd tool - mount &> $Debug_Data_Dir/mount.txt - btrfs filesystem show &> $Debug_Data_Dir/btrfs-filesystem-show.txt - btrfs filesystem show --mounted &> $Debug_Data_Dir/btrfs-filesystem-show-mounted.txt - # btrfs filesystem show --all-devices &> $Debug_Data_Dir/btrfs-filesystem-show-all-devices.txt - gpart list &> $Debug_Data_Dir/bsd-gpart-list.txt - gpart show &> $Debug_Data_Dir/bsd-gpart-show.txt - gpart status &> $Debug_Data_Dir/bsd-gpart-status.txt - swapctl -l -k &> $Debug_Data_Dir/bsd-swapctl-l-k.txt - swapon -s &> $Debug_Data_Dir/swapon-s.txt - sysctl -b kern.geom.conftxt &> $Debug_Data_Dir/bsd-sysctl-b-kern.geom.conftxt.txt - sysctl -b kern.geom.confxml &> $Debug_Data_Dir/bsd-sysctl-b-kern.geom.confxml.txt - zfs list &> $Debug_Data_Dir/zfs-list.txt - zpool list &> $Debug_Data_Dir/zpool-list.txt - zpool list -v &> $Debug_Data_Dir/zpool-list-v.txt - df -P --exclude-type=aufs --exclude-type=squashfs --exclude-type=unionfs --exclude-type=devtmpfs --exclude-type=tmpfs --exclude-type=iso9660 &> $Debug_Data_Dir/df-P-excludes.txt - df -P &> $Debug_Data_Dir/bsd-df-P-no-excludes.txt - cat /proc/mdstat &> $Debug_Data_Dir/proc-mdstat.txt - cat $FILE_PARTITIONS &> $Debug_Data_Dir/proc-partitions.txt - cat $FILE_SCSI &> $Debug_Data_Dir/proc-scsi.txt - cat $FILE_MOUNTS &> $Debug_Data_Dir/proc-mounts.txt - cat $FILE_MDSTAT &> $Debug_Data_Dir/proc-mdstat.txt - cat /proc/sys/dev/cdrom/info &> $Debug_Data_Dir/proc-cdrom-info.txt - ls /proc/ide/ &> $Debug_Data_Dir/proc-ide.txt - cat /proc/ide/*/* &> $Debug_Data_Dir/proc-ide-hdx-cat.txt - cat /etc/fstab &> $Debug_Data_Dir/etc-fstab.txt - cat /etc/mtab &> $Debug_Data_Dir/etc-mtab.txt - if type -p nvme &>/dev/null; then - touch $Debug_Data_Dir/nvme-present - else - touch $Debug_Data_Dir/nvme-absent - fi - fi - if [[ $1 == 'disk' || $1 == 'sys' || $1 == 'all' ]];then - echo 'Collecting networking data...' - ifconfig &> $Debug_Data_Dir/ifconfig.txt - ip addr &> $Debug_Data_Dir/ip-addr.txt - fi - # create the error file in case it's needed - if [[ $B_UPLOAD_DEBUG_DATA == 'true' || $1 == 'disk' || $1 == 'sys' || $1 == 'all' ]];then - touch $SELF_DATA_DIR/$Debug_Data_Dir/sys-dir-error.txt - fi - # just on the off chance bsds start having a fake /sys - ls /sys &> $Debug_Data_Dir/sys-ls-1.txt - # note, only bash 4> supports ;;& for case, so using if/then here - if [[ -z $BSD_TYPE ]] && [[ $1 == 'disk' || $1 == 'sys' || $1 == 'all' ]];then - echo $Line - sys_data_file=$SELF_DATA_DIR/$Debug_Data_Dir/sys-dir-traverse.txt - echo "Getting file paths in /sys..." - sys_tree - # note, this generates more lines than the full sys parsing, so only use if required - # ls_sys 5 - touch $sys_data_file - if type -p perl &>/dev/null;then - echo "Parsing /sys files..." - echo -n "Using Perl: " && perl --version | grep -oE 'v[0-9.]+' - sys_traverse_data="$( sys_traverse_data )" - if [[ -z "$sys_traverse_data" ]];then - echo -e "ERROR: failed to generate /sys data - removing data file.\nContinuing with incomplete data collection." - echo "Continuing with incomplete data collection." - rm -f $sys_data_file - echo "/sys data generation failed. No data collected." >> $Debug_Data_Dir/sys-dir-error.txt - else - echo 'Completed /sys data collection.' - echo -n "$sys_traverse_data" > $sys_data_file - fi - fi - fi - # running in inxi - if type -t check_recommends_user_output &>/dev/null;then - echo $Line - echo "Creating $SELF_NAME output file now. This can take a few seconds..." - echo "Starting $SELF_NAME from: $start_directory" - cd $start_directory - $SELF_PATH/$SELF_NAME -F${debug_i}Rfrploudmxxx -c 0 -@ 8 -y 120 > $SELF_DATA_DIR/$Debug_Data_Dir/inxi-F${debug_i}Rfrploudmxxxy120.txt - cp $LOG_FILE $SELF_DATA_DIR/$Debug_Data_Dir - elif type -p inxi &>/dev/null;then - echo $Line - echo "Creating basic inxi output file..." - inxi -Fxxx > $Debug_Data_Dir/inxi-Fxxx.txt - else - touch $Debug_Data_Dir/inxi-absent.txt - fi - cd $SELF_DATA_DIR - echo $Line - echo 'Creating tar.gz compressed file of this material...' - tar -czf $debug_gz $Debug_Data_Dir - echo 'Cleaning up leftovers...' - rm -rf $Debug_Data_Dir - echo 'Testing gzip file integrity...' - gzip -t $debug_gz - if [[ $? -gt 0 ]];then - echo 'Data in gz is corrupted, removing gzip file, try running data collector again.' - rm -f $debug_gz - echo "Data in gz is corrupted, removed gzip file" >> $Debug_Data_Dir/gzip-error.txt - else - echo 'All done, you can find your data gzipped directory here:' - completed_gz_file=$SELF_DATA_DIR/$debug_gz - echo $completed_gz_file - if [[ $B_UPLOAD_DEBUG_DATA == 'true' ]];then - echo $Line - upload_debugger_data "$completed_gz_file" "$Ftp_Upload" - if [[ $? -gt 0 ]];then - echo "Error: looks like the Perl ftp upload failed. Error number: $?" - else - b_uploaded='true' - echo "Hurray! Looks like the Perl ftp upload worked!" - fi - else - echo 'You can upload this here using most file managers: ftp.techpatterns.com/incoming' - echo 'then let a maintainer know it is uploaded.' - fi - fi - else - echo 'This feature only available in console or shell client! Exiting now.' - fi - exit 0 -} - -## args: $1 - depth -ls_sys() -{ - local files='' - case $1 in - 1)files='/sys/';; - 2)files='/sys/*/';; - 3)files='/sys/*/*/';; - 4)files='/sys/*/*/*/';; # this should be enough for most use cases - 5)files='/sys/*/*/*/*/';; # very large file, shows best shortcuts though - 6)files='/sys/*/*/*/*/*/';; # slows down too much, too big, can cause ls error - 7)files='/sys/*/*/*/*/*/*/';; # impossibly big, will fail - esac - ls -l $files 2>/dev/null | awk '{ - if (NF > 7) { - if ($1 ~/^d/){ - f="d - " - } - else if ($1 ~/^l/){ - f="l - " + write_data(\%data,'audio'); + @files = ( + '/proc/asound/cards', + '/proc/asound/version', + ); + @files2 = main::globber('/proc/asound/*/usbid'); + @files = (@files,@files2) if @files2; + copy_files(\@files,'audio'); +} +## NOTE: >/dev/null 2>&1 is sh, and &>/dev/null is bash, fix this +# ls -w 1 /sysrs > tester 2>&1 +sub disk_data { + my (%data,@files,@files2); + print "Collecting dev, label, disk, uuid data, df...\n"; + @files = ( + '/etc/fstab', + '/etc/mtab', + '/proc/mdstat', + '/proc/mounts', + '/proc/partitions', + '/proc/scsi/scsi', + '/proc/sys/dev/cdrom/info', + ); + # very old systems + if (-d '/proc/ide/'){ + my @ides = main::globber('/proc/ide/*/*'); + @files = (@files, @ides) if @ides; + } + else { + push (@files, '/proc-ide-directory'); + } + copy_files(\@files, 'disk'); + my @cmds = ( + ['btrfs', 'filesystem show'], + ['btrfs', 'filesystem show --mounted'], + # ['btrfs', 'filesystem show --all-devices'], + ['df', '-h -T'], + ['df', '-h'], + ['df', '-k'], + ['df', '-k -T'], + ['df', '-k -T -P'], + ['df', '-P'], + ['lsblk', '-fs'], + ['lsblk', '-fsr'], + ['lsblk', '-fsP'], + ['lsblk', '-a'], + ['lsblk', '-aP'], + ['lsblk', '-ar'], + ['lsblk', '-p'], + ['lsblk', '-pr'], + ['lsblk', '-pP'], + ['lsblk', '-r'], + ['lsblk', '-r --output NAME,PKNAME,TYPE,RM,FSTYPE,SIZE,LABEL,UUID,MOUNTPOINT'], + ['lsblk', '-rb --output NAME,PKNAME,TYPE,RM,FSTYPE,SIZE,LABEL,UUID,MOUNTPOINT'], + ['lsblk', '-Pb --output NAME,PKNAME,TYPE,RM,FSTYPE,SIZE,LABEL,UUID,MOUNTPOINT'], + ['gpart', 'list'], + ['gpart', 'show'], + ['gpart', 'status'], + ['ls', '-l /dev'], + ['ls', '-l /dev/disk'], + ['ls', '-l /dev/disk/by-id'], + ['ls', '-l /dev/disk/by-label'], + ['ls', '-l /dev/disk/by-uuid'], + # http://comments.gmane.org/gmane.linux.file-systems.zfs.user/2032 + ['ls', '-l /dev/disk/by-wwn'], + ['ls', '-l /dev/disk/by-path'], + ['ls', '-l /dev/mapper'], + ['mount', ''], + ['nvme', 'present'], + ['readlink', '/dev/root'], + ['swapon', '-s'], + ['zfs', 'list'], + ['zpool', 'list'], + ['zpool', 'list -v'], + ); + run_commands(\@cmds,'disk'); + @cmds = ( + ['atacontrol', 'list'], + ['camcontrol', 'devlist'], + ['glabel', 'status'], + ['swapctl', '-l -k'], + ['swapctl', '-l -k'], + ['vmstat', '-H'], + ); + run_commands(\@cmds,'disk-bsd'); +} +sub display_data { + my (%data,@files,@files2); + my $working = ''; + if ( ! $b_display ){ + print "Warning: only some of the data collection can occur if you are not in X\n"; + main::toucher("$data_dir/display-data-warning-user-not-in-x"); + } + if ( $b_root ){ + print "Warning: only some of the data collection can occur if you are running as Root user\n"; + main::toucher("$data_dir/display-data-warning-root-user"); + } + print "Collecting Xorg log and xorg.conf files...\n"; + if ( -d "/etc/X11/xorg.conf.d/" ){ + @files = main::globber("/etc/X11/xorg.conf.d/*"); + } + else { + @files = ('/xorg-conf-d'); + } + push (@files, $files{'xorg-log'}); + push (@files, '/etc/X11/xorg.conf'); + copy_files(\@files,'display-xorg'); + print "Collecting X, xprop, glxinfo, xrandr, xdpyinfo data, wayland, weston...\n"; + %data = ( + 'desktop-session' => $ENV{'DESKTOP_SESSION'}, + 'gdmsession' => $ENV{'GDMSESSION'}, + 'gnome-desktop-session-id' => $ENV{'GNOME_DESKTOP_SESSION_ID'}, + 'kde3-full-session' => $ENV{'KDE_FULL_SESSION'}, + 'xdg-current-desktop' => $ENV{'XDG_CURRENT_DESKTOP'}, + 'kde-gte-4-session-version' => $ENV{'KDE_SESSION_VERSION'}, + 'xdg-session-desktop' => $ENV{'XDG_SESSION_DESKTOP'}, + # wayland data collectors: + 'xdg-session-type' => $ENV{'XDG_SESSION_TYPE'}, + 'wayland-display' => $ENV{'WAYLAND_DISPLAY'}, + 'gdk-backend' => $ENV{'GDK_BACKEND'}, + 'qt-qpa-platform' => $ENV{'QT_QPA_PLATFORM'}, + 'clutter-backend' => $ENV{'CLUTTER_BACKEND'}, + 'sdl-videodriver' => $ENV{'SDL_VIDEODRIVER'}, + # program display values + 'size-indent' => $size{'indent'}, + 'size-indent-min' => $size{'indent-min'}, + 'size-cols-max' => $size{'max'}, + ); + write_data(\%data,'display'); + my @cmds = ( + # kde 5/plasma desktop 5, this is maybe an extra package and won't be used + ['about-distro',''], + ['aticonfig','--adapter=all --od-gettemperature'], + ['glxinfo',''], + ['glxinfo','-B'], + ['kded','--version'], + ['kded1','--version'], + ['kded2','--version'], + ['kded3','--version'], + ['kded4','--version'], + ['kded5','--version'], + ['kded6','--version'], + ['kf4-config','--version'], + ['kf5-config','--version'], + ['kf6-config','--version'], + ['kwin_x11','--version'], + ['loginctl','--no-pager list-sessions'], + ['nvidia-settings','-q screens'], + ['nvidia-settings','-c :0.0 -q all'], + ['nvidia-smi','-q'], + ['nvidia-smi','-q -x'], + ['plasmashell','--version'], + ['weston-info',''], + ['wmctrl','-m'], + ['weston','--version'], + ['xdpyinfo',''], + ['Xorg','-version'], + ['xprop','-root'], + ['xrandr',''], + ); + run_commands(\@cmds,'display'); +} +sub network_data { + print "Collecting networking data...\n"; +# no warnings 'uninitialized'; + my @cmds = ( + ['ifconfig',''], + ['ip','addr'], + ['ip','-s link'], + ); + run_commands(\@cmds,'network'); +} +sub perl_modules { + print "Collecting Perl module data (this can take a while)...\n"; + my @modules = (); + my ($dirname,$holder,$mods,$value) = ('','','',''); + my $filename = 'perl-modules.txt'; + my @inc; + foreach (sort @INC){ + # some BSD installs have '.' n @INC path + if (-d $_ && $_ ne '.'){ + $_ =~ s/\/$//; # just in case, trim off trailing slash + $value .= "EXISTS: $_\n"; + push @inc, $_; + } + else { + $value .= "ABSENT: $_\n"; + } + } + main::writer("$data_dir/perl-inc-data.txt",$value); + File::Find::find { wanted => sub { + push @modules, File::Spec->canonpath($_) if /\.pm\z/ + }, no_chdir => 1 }, @inc; + @modules = sort(@modules); + foreach (@modules){ + my $dir = $_; + $dir =~ s/[^\/]+$//; + if (!$holder || $holder ne $dir ){ + $holder = $dir; + $value = "DIR: $dir\n"; + $_ =~ s/^$dir//; + $value .= " $_\n"; + } + else { + $value = $_; + $value =~ s/^$dir//; + $value = " $value\n"; + } + $mods .= $value; + } + open (my $fh, '>', "$data_dir/$filename"); + print $fh $mods; + close $fh; +} +sub system_data { + print "Collecting system data...\n"; + my %data = ( + 'cc' => $ENV{'CC'}, + # @(#)MIRBSD KSH R56 2018/03/09: ksh and mksh + 'ksh-version' => $ENV{'KSH_VERSION'}, + 'manpath' => $ENV{'MANPATH'}, + 'path' => $ENV{'PATH'}, + 'xdg-config-home' => $ENV{'XDG_CONFIG_HOME'}, + 'xdg-config-dirs' => $ENV{'XDG_CONFIG_DIRS'}, + 'xdg-data-home' => $ENV{'XDG_DATA_HOME'}, + 'xdg-data-dirs' => $ENV{'XDG_DATA_DIRS'}, + ); + my @files = main::globber('/usr/bin/gcc*'); + if (@files){ + $data{'gcc-versions'} = join "\n",@files; + } + else { + $data{'gcc-versions'} = undef; + } + @files = main::globber('/sys/*'); + if (@files){ + $data{'sys-tree-ls-1-basic'} = join "\n", @files; + } + else { + $data{'sys-tree-ls-1-basic'} = undef; + } + write_data(\%data,'system'); + # bsd tools http://cb.vu/unixtoolbox.xhtml + my @cmds = ( + # general + ['sysctl', '-b kern.geom.conftxt'], + ['sysctl', '-b kern.geom.confxml'], + ['usbdevs','-v'], + # freebsd + ['pciconf','-l -cv'], + ['pciconf','-vl'], + ['pciconf','-l'], + # openbsd + ['pcidump',''], + ['pcidump','-v'], + # netbsd + ['kldstat',''], + ['pcictl','list'], + ['pcictl','list -ns'], + ); + run_commands(\@cmds,'system-bsd'); + # diskinfo -v <disk> + # fdisk <disk> + @cmds = ( + ['clang','--version'], + ['dmidecode',''], + ['dmesg',''], + ['gcc','--version'], + ['hciconfig','-a'], + ['initctl','list'], + ['ipmi-sensors',''], + ['ipmi-sensors','--output-sensor-thresholds'], + ['ipmitool','sensor'], + ['lscpu',''], + ['lspci','-k'], + ['lspci','-knn'], + ['lspci','-knnv'],# returns ports + ['lspci','-n'], + ['lspci','-nn'], + ['lspci','-nnk'], + ['lspci','-mm'], + ['lspci','-mmk'], + ['lspci','-mmkv'], + ['lspci','-mmnn'], + ['lspci','-v'], + ['lspci',''], + ['lsusb',''], + ['lsusb','-v'], + ['ps','aux'], + ['ps','-e'], + ['ps','-p 1'], + ['runlevel',''], + ['rc-status','-a'], + ['rc-status','-l'], + ['rc-status','-r'], + ['sensors',''], + # leaving this commented out to remind that some systems do not + # support strings --version, but will just simply hang at that command + # which you can duplicate by simply typing: strings then hitting enter. + # ['strings','--version'], + ['strings','present'], + ['sysctl','-a'], + ['systemctl','list-units'], + ['systemctl','list-units --type=target'], + ['systemd-detect-virt',''], + ['uptime',''], + ); + run_commands(\@cmds,'system'); + @files = main::globber('/dev/bus/usb/*/*'); + copy_files(\@files, 'system'); +} +sub system_files { + print "Collecting system files data...\n"; + my (%data,@files,@files2); + @files = RepoData::get($data_dir); + copy_files(\@files, 'repo'); + # chdir "/etc"; + @files = main::globber("/etc/*[-_]{[rR]elease,[vV]ersion}"); + push (@files, '/etc/issue'); + push (@files, '/etc/lsb-release'); + push (@files, '/etc/os-release'); + copy_files(\@files,'system-distro'); + @files = ( + '/proc/1/comm', + '/proc/cpuinfo', + '/proc/meminfo', + '/proc/modules', + '/proc/net/arp', + '/proc/version', + ); + @files2=main::globber('/sys/class/power_supply/*/uevent'); + if (@files2){ + @files = (@files,@files2); + } + else { + push (@files, '/sys-class-power-supply-empty'); + } + copy_files(\@files, 'system'); + @files = ( + '/etc/make.conf', + '/etc/src.conf', + '/var/run/dmesg.boot', + ); + copy_files(\@files,'system-bsd'); +} + +sub copy_files { + my ($files_ref, $type) = @_; + my ($absent,$error,$good,$name,$unreadable); + foreach (@$files_ref) { + $name = $_; + $name =~ s/^\///; + $name =~ s/\//-/g; + $name = "$data_dir/$type-file-$name"; + $good = $name . '.txt'; + $absent = $name . '-absent'; + $error = $name . '-error'; + $unreadable = $name . '-unreadable'; + if (-e $_ ) { + if (-r $_){ + copy($_,"$good") or main::toucher($error); } else { - f="f - " + main::toucher($unreadable); } - # includes -> target for symbolic link if present - print "\t" f $9 " " $10 " " $11 - } - else if (!/^total / ) { - print $0 - } - }' &> $Debug_Data_Dir/sys-ls-$1.txt + } + else { + main::toucher($absent); + } + } } -# prior to script up set, pack the data into an array -# then we'll print it out later. -# args: $1 - $@ debugging string text -self_debugger() -{ - eval $LOGFS - if [[ $B_SCRIPT_UP == 'true' ]];then - # only return if debugger is off and no pre start up errors have occurred - if [[ $DEBUG -eq 0 && $DEBUG_BUFFER_INDEX -eq 0 ]];then - return 0 - # print out the stored debugging information if errors occurred - elif [[ $DEBUG_BUFFER_INDEX -gt 0 ]];then - for (( DEBUG_BUFFER_INDEX=0; DEBUG_BUFFER_INDEX < ${#A_DEBUG_BUFFER[@]}; DEBUG_BUFFER_INDEX++ )) - do - print_screen_output "${A_DEBUG_BUFFER[$DEBUG_BUFFER_INDEX]}" - done - DEBUG_BUFFER_INDEX=0 - fi - # or print out normal debugger messages if debugger is on - if [[ $DEBUG -gt 0 ]];then - print_screen_output "$1" - fi - else - if [[ $B_DEBUG_FLOOD == 'true' && $DEBUG_BUFFER_INDEX -gt 10 ]];then - error_handler 2 - # this case stores the data for later printout, will print out only - # at B_SCRIPT_UP == 'true' if array index > 0 - else - A_DEBUG_BUFFER[$DEBUG_BUFFER_INDEX]="$1" - # increment count for next pre script up debugging error - (( DEBUG_BUFFER_INDEX++ )) - fi - fi - eval $LOGFE -} - -sys_traverse_data() -{ - local sys_traverse_data="$( perl -e ' - use File::Find; - use strict; - # use warnings; - use 5.008; - my @content = (); - find( \&wanted, "/sys"); - process_data( @content ); - sub wanted { - return if -d; # not directory - return unless -e; # Must exist - return unless -r; # Must be readable - return unless -f; # Must be file - # note: a new file in 4.11 /sys can hang this, it is /parameter/ then - # a few variables. Since inxi does not need to see that file, we will - # not use it. Also do not need . files or __ starting files - return if $File::Find::name =~ /\/(\.[a-z]|__|kernel\/|parameters\/|debug\/)/; - # comment this one out if you experience hangs or if - # we discover syntax of foreign language characters - # Must be ascii like. This is questionable and might require further - # investigation, it is removing some characters that we might want - return unless -T; - # print $File::Find::name . "\n"; - push @content, $File::Find::name; - return; - } - sub process_data { - my $result = ""; - my $row = ""; - my $fh; - my $data=""; - my $sep=""; - # no sorts, we want the order it comes in - # @content = sort @content; - foreach (@content){ - $data=""; - $sep=""; - open($fh, "<$_"); - while ($row = <$fh>) { - chomp $row; - $data .= $sep . "\"" . $row . "\""; - $sep=", "; - } - $result .= "$_:[$data]\n"; - # print "$_:[$data]\n" - } - # print scalar @content . "\n"; - print "$result"; - } ' )" - echo "$sys_traverse_data" -} - -sys_tree() -{ - if type -p tree &>/dev/null;then - tree -a -L 10 /sys > $Debug_Data_Dir/sys-tree-full-10.txt - for branch in $( tree -i -L 1 -d --noreport /sys | grep -v 'sys$' );do - tree -a -L 10 /sys/$branch > $Debug_Data_Dir/sys-tree-$branch-10.txt - done - else - # ls_sys 1 - ls_sys 2 - ls_sys 3 - ls_sys 4 - fi -} - -## args: $1 - debugger file name; $2 - ftp destination [does not work] -upload_debugger_data() -{ - local result='' debugger_file=$1 - - if ! type -p perl &>/dev/null;then - echo "Perl is not installed!" - return 2 - elif ! perl -MNet::FTP -e 1 &>/dev/null;then - echo "Required Perl module Net::FTP not installed." - return 3 - fi - echo "Starting Perl Uploader..." - - result="$( perl -e ' - use strict; - use warnings; - use 5.008; - use Net::FTP; - my ($ftp, $host, $user, $pass, $dir, $fpath, $error); - $host = "ftp.techpatterns.com"; +sub run_commands { + my ($cmds,$type) = @_; + my $holder = ''; + my ($name,$cmd,$args); + foreach (@$cmds){ + my @rows = @$_; + if (my $program = main::check_program($rows[0])){ + if ($rows[1] eq 'present'){ + $name = "$data_dir/$type-cmd-$rows[0]-present"; + main::toucher($name); + } + else { + $args = $rows[1]; + $args =~ s/\s|--|\/|=/-/g; # for: + $args =~ s/--/-/g;# strip out -- that result from the above + $args =~ s/^-//g; + $args = "-$args" if $args; + $name = "$data_dir/$type-cmd-$rows[0]$args.txt"; + $cmd = "$program $rows[1] >$name 2>&1"; + system($cmd); + } + } + else { + if ($holder ne $rows[0]){ + $name = "$data_dir/$type-cmd-$rows[0]-absent"; + main::toucher($name); + $holder = $rows[0]; + } + } + } +} +sub write_data { + my ($data_ref, $type) = @_; + my ($empty,$error,$fh,$good,$name,$undefined,$value); + foreach (keys %$data_ref) { + $value = $$data_ref{$_}; + $name = "$data_dir/$type-data-$_"; + $good = $name . '.txt'; + $empty = $name . '-empty'; + $error = $name . '-error'; + $undefined = $name . '-undefined'; + if (defined $value) { + if ($value || $value eq '0'){ + open($fh, '>', $good) or main::toucher($error); + print $fh "$value"; + } + else { + main::toucher($empty); + } + } + else { + main::toucher($undefined); + } + } +} + +sub run_self { + print "Creating $self_name output file now. This can take a few seconds...\n"; + print "Starting $self_name from: $self_path\n"; + my $i = ($option eq 'main-full')? ' -i' : ''; + my $cmd = "$self_path/$self_name -FRfrploudmxxx$i -c 0 --usb --slots --debug 10 -y 120 > $data_dir/$self_name-FRfrploudmxxxyusbslots120.txt 2>&1"; + system($cmd); + copy($log_file, "$data_dir") or main::error_handler('copy-failed', "$log_file", "$!"); + system("$self_path/$self_name --recommends -y 120 > $data_dir/$self_name-recommends-120.txt 2>&1"); +} + +sub sys_tree { + print "Constructing /sys tree data...\n"; + if ( main::check_program('tree') ){ + my $dirname = '/sys'; + my $cmd; + system("tree -a -L 10 /sys > $data_dir/sys-data-tree-full-10.txt"); + opendir my($dh), $dirname or main::error_handler('open-dir',"$dirname", "$!"); + my @files = readdir $dh; + closedir $dh; + foreach (@files){ + next if /^\./; + $cmd = "tree -a -L 10 $dirname/$_ > $data_dir/sys-data-tree-$_-10.txt"; + #print "$cmd\n"; + system($cmd); + } + } + # for now, we want all of these as well for better debugging + #else { + sys_ls(1); + sys_ls(2); + sys_ls(3); + sys_ls(4); + #} +} +sub sys_ls { + my ( $depth) = @_; + my $cmd = do { + if ( $depth == 1 ){ 'ls -l /sys/ 2>/dev/null' } + elsif ( $depth == 2 ){ 'ls -l /sys/*/ 2>/dev/null' } + elsif ( $depth == 3 ){ 'ls -l /sys/*/*/ 2>/dev/null' } + elsif ( $depth == 4 ){ 'ls -l /sys/*/*/*/ 2>/dev/null' } + elsif ( $depth == 5 ){ 'ls -l /sys/*/*/*/*/ 2>/dev/null' } + elsif ( $depth == 5 ){ 'ls -l /sys/*/*/*/*/ 2>/dev/null' } + }; + my @working = (); + my $output = ''; + my ($type); + my $result = qx($cmd); + open my $ch, '<', \$result or main::error_handler('open-data',"$cmd", "$!"); + while ( my $line = <$ch> ){ + chomp($line); + $line =~ s/^\s+|\s+$//g; + @working = split /\s+/, $line; + $working[0] ||= ''; + if ( scalar @working > 7 ){ + if ($working[0] =~ /^d/ ){ + $type = "d - "; + } + elsif ($working[0] =~ /^l/){ + $type = "l - "; + } + else { + $type = "f - "; + } + $working[9] ||= ''; + $working[10] ||= ''; + $output = $output . " $type$working[8] $working[9] $working[10]\n"; + } + elsif ( $working[0] !~ /^total/ ){ + $output = $output . $line . "\n"; + } + } + close $ch; + my $file = "$data_dir/sys-data-ls-$depth.txt"; + open my $fh, '>', $file or main::error_handler('create',"$file", "$!"); + print $fh $output; + close $fh; + # print "$output\n"; +} + +sub sys_traverse_data { + print "Parsing /sys files...\n"; + # get rid pointless error:Can't cd to (/sys/kernel/) debug: Permission denied + no warnings 'File::Find'; + File::Find::find( \&wanted, "/sys"); + process_data(); +} +sub wanted { + return if -d; # not directory + return unless -e; # Must exist + return unless -r; # Must be readable + return unless -f; # Must be file + # note: a new file in 4.11 /sys can hang this, it is /parameter/ then + # a few variables. Since inxi does not need to see that file, we will + # not use it. Also do not need . files or __ starting files + # print $File::Find::name . "\n"; + # block maybe: cfgroup\/ + return if $File::Find::name =~ /\/(\.[a-z]|kernel\/|parameters\/|debug\/)/; + # comment this one out if you experience hangs or if + # we discover syntax of foreign language characters + # Must be ascii like. This is questionable and might require further + # investigation, it is removing some characters that we might want + return unless -T; + # print $File::Find::name . "\n"; + push (@content, $File::Find::name); + return; +} +sub process_data { + my ($data,$fh,$result,$row,$sep); + my $filename = "sys-data-parse.txt"; + # no sorts, we want the order it comes in + # @content = sort @content; + foreach (@content){ + $data=''; + $sep=''; + open($fh, '<', $_); + while ($row = <$fh>) { + chomp $row; + $data .= $sep . '"' . $row . '"'; + $sep=', '; + } + $result .= "$_:[$data]\n"; + # print "$_:[$data]\n" + } + # print scalar @content . "\n"; + open ($fh, '>', "$data_dir/$filename"); + print $fh $result; + close $fh; + # print $fh "$result"; +} +# args: 1 - path to file to be uploaded +# args: 2 - optional: alternate ftp upload url +# NOTE: must be in format: ftp.site.com/incoming +sub upload_file { + require Net::FTP; + import Net::FTP; + my ($self, $ftp_url) = @_; + my ($ftp, $domain, $host, $user, $pass, $dir, $error); + $ftp_url ||= main::get_defaults('ftp-upload'); + $ftp_url =~ s/\/$//g; # trim off trailing slash if present + my @url = split(/\//, $ftp_url); + my $file_path = "$user_data_dir/$debug_gz"; + $host = $url[0]; + $dir = $url[1]; + $domain = $host; + $domain =~ s/^ftp\.//; $user = "anonymous"; - $pass = "anonymous\@techpatterns.com"; - $dir = "incoming"; - $fpath = $ARGV[0]; - # NOTE: important: must explicitly set to passive true/1 - $ftp = Net::FTP->new($host, Debug => 0, Passive => 1); - $ftp->login($user, $pass) || die $ftp->message; - $ftp->binary(); - $ftp->cwd($dir); - print "Connected to FTP server.\n"; - $ftp->put($fpath) || die $ftp->message; - $ftp->quit; - print "Uploaded file $fpath.\n"; - print $ftp->message; - ' $debugger_file )" - - if [[ "$result" != '' ]];then - echo "$result" - fi - if [[ "$result" == *Goodbye* ]];then - return 0 - else - return 1 - fi + $pass = "anonymous\@$domain"; + + print $line3; + print "Uploading to: $ftp_url\n"; + # print "$host $domain $dir $user $pass\n"; + print "File to be uploaded:\n$file_path\n"; + + if ($host && ( $file_path && -e $file_path ) ){ + # NOTE: important: must explicitly set to passive true/1 + $ftp = Net::FTP->new($host, Debug => 0, Passive => 1); + $ftp->login($user, $pass) || main::error_handler('ftp-login', $ftp->message); + $ftp->binary(); + $ftp->cwd($dir); + print "Connected to FTP server.\n"; + $ftp->put($file_path) || main::error_handler('ftp-upload', $ftp->message); + $ftp->quit; + print "Uploaded file successfully!\n"; + print $ftp->message; + if ($b_debug_gz){ + print "Removing debugger gz file:\n$file_path\n"; + unlink $file_path or main::error_handler('remove',"$file_path", "$!"); + print "File removed.\n"; + } + print "Debugger data generation and upload completed. Thank you for your help.\n"; + } + else { + main::error_handler('ftp-bad-path', "$file_path"); + } +} } #### ------------------------------------------------------------------- #### DOWNLOADER #### ------------------------------------------------------------------- -download_file() -{ - local retvalue=0 - local data=$( perl -e 'use strict; - use warnings; - use 5.008; - use HTTP::Tiny; - sub get_file { - my ($type, $url, $file) = @_; - my $response = HTTP::Tiny->new->get($url); - my $return = 0; - my $debug = 0; - my $fh; - - if ($response->{success} == 0 ){ - # print "Failed to connect to server/file!\n"; - $return = 1; +sub download_file { + my ($type, $url, $file) = @_; + my ($cmd,$args,$timeout) = ('','',''); + my $debug_data = ''; + my $result = 1; + $dl{'no-ssl-opt'} ||= ''; + $dl{'spider'} ||= ''; + $file ||= 'N/A'; # to avoid debug error + if ( ! $dl{'dl'} ){ + return 0; + } + if ($dl{'timeout'}){ + $timeout = "$dl{'timeout'}$dl_timeout"; + } + # print "$dl{'no-ssl-opt'}\n"; + # print "$dl{'dl'}\n"; + # tiny supports spider sort of + ## NOTE: 1 is success, 0 false for Perl + if ($dl{'dl'} eq 'tiny' ){ + $cmd = "Using tiny: type: $type \nurl: $url \nfile: $file"; + $result = get_file($type, $url, $file); + $debug_data = ($type ne 'stdout') ? $result : 'Success: stdout data not null.'; + } + # But: 0 is success, and 1 is false for these + # when strings are returned, they will be taken as true + else { + if ($type eq 'stdout'){ + $args = $dl{'stdout'}; + $cmd = "$dl{'dl'} $dl{'no-ssl-opt'} $timeout $args $url $dl{'null'}"; + $result = qx($cmd); + $debug_data = ($result) ? 'Success: stdout data not null.' : 'Download resulted in null data!'; } - else { - if ( $debug ){ - print "$response->{success}\n"; - print "$response->{status} $response->{reason}\n"; - while (my ($key, $value) = each %{$response->{headers}}) { - for (ref $value eq "ARRAY" ? @$value : $value) { - print "$key: $_\n"; - } + elsif ($type eq 'file') { + $args = $dl{'file'}; + $cmd = "$dl{'dl'} $dl{'no-ssl-opt'} $timeout $args $file $url $dl{'null'}"; + system($cmd); + $result = ($?) ? 0 : 1; # reverse these into Perl t/f + $debug_data = $result; + } + elsif ( $dl{'dl'} eq 'wget' && $type eq 'spider'){ + $cmd = "$dl{'dl'} $dl{'no-ssl-opt'} $timeout $dl{'spider'} $url"; + system($cmd); + $result = ($?) ? 0 : 1; # reverse these into Perl t/f + $debug_data = $result; + } + } + print "-------\nDownloader Data:\n$cmd\nResult: $debug_data\n" if $test[1]; + log_data('data',"$cmd\nResult: $result") if $b_log; + return $result; +} + +sub get_file { + my ($type, $url, $file) = @_; + my $response = HTTP::Tiny->new->get($url); + my $return = 1; + my $debug = 0; + my $fh; + $file ||= 'N/A'; + log_data('dump','%{$response}',\%{$response}) if $b_log; + # print Dumper \%{$response}; + if ( ! $response->{success} ){ + my $content = $response->{content}; + $content ||= "N/A\n"; + my $msg = "Failed to connect to server/file!\n"; + $msg .= "Response: ${content}Downloader: HTTP::Tiny URL: $url\nFile: $file"; + log_data('data',$msg) if $b_log; + print error_defaults('download-error',$msg) if $test[1]; + $return = 0; + } + else { + if ( $debug ){ + print "$response->{success}\n"; + print "$response->{status} $response->{reason}\n"; + while (my ($key, $value) = each %{$response->{headers}}) { + for (ref $value eq "ARRAY" ? @$value : $value) { + print "$key: $_\n"; } } - if ( $type eq "stdout" || $type eq "ua-stdout" ){ - print "$response->{content}" if length $response->{content}; - } - elsif ($type eq "spider"){ - # do nothing, just use the return value - } - elsif ($type eq "file"){ - open($fh, ">", $file); - print $fh $response->{content}; - close $fh; - } } - return $return; + if ( $type eq "stdout" || $type eq "ua-stdout" ){ + $return = $response->{content}; + } + elsif ($type eq "spider"){ + # do nothing, just use the return value + } + elsif ($type eq "file"){ + open($fh, ">", $file); + print $fh $response->{content}; # or die "can't write to file!\n"; + close $fh; + } } - get_file($ARGV[0],$ARGV[1],$ARGV[2]);' "$1" "$2" "$3" ) - retvalue=$? - if [[ "$data" != '' ]];then - echo "$data" - fi - return $retvalue + return $return; +} + +sub set_downloader { + eval $start if $b_log; + $dl{'no-ssl'} = ''; + $dl{'null'} = ''; + $dl{'spider'} = ''; + # we only want to use HTTP::Tiny if it's present in user system. + # It is NOT part of core modules. IO::Socket::SSL is also required + # For some https connections so only use tiny as option if both present + if ($dl{'tiny'}){ + if (check_module('HTTP::Tiny') && check_module('IO::Socket::SSL')){ + import HTTP::Tiny; + import IO::Socket::SSL; + $dl{'tiny'} = 1; + } + else { + $dl{'tiny'} = 0; + } + } + #print $dl{'tiny'} . "\n"; + if ($dl{'tiny'}){ + $dl{'dl'} = 'tiny'; + $dl{'file'} = ''; + $dl{'stdout'} = ''; + $dl{'timeout'} = ''; + } + elsif ( $dl{'curl'} && check_program('curl') ){ + $dl{'dl'} = 'curl'; + $dl{'file'} = ' -L -s -o '; + $dl{'no-ssl'} = ' --insecure'; + $dl{'stdout'} = ' -L -s '; + $dl{'timeout'} = ' -y '; + } + elsif ($dl{'wget'} && check_program('wget') ){ + $dl{'dl'} = 'wget'; + $dl{'file'} = ' -q -O '; + $dl{'no-ssl'} = ' --no-check-certificate'; + $dl{'spider'} = ' -q --spider'; + $dl{'stdout'} = ' -q -O -'; + $dl{'timeout'} = ' -T '; + } + elsif ($dl{'fetch'} && check_program('fetch')){ + $dl{'dl'} = 'fetch'; + $dl{'file'} = ' -q -o '; + $dl{'no-ssl'} = ' --no-verify-peer'; + $dl{'stdout'} = ' -q -o -'; + $dl{'timeout'} = ' -T '; + } + elsif ( $bsd_type eq 'openbsd' && check_program('ftp') ){ + $dl{'dl'} = 'ftp'; + $dl{'file'} = ' -o '; + $dl{'null'} = ' 2>/dev/null'; + $dl{'stdout'} = ' -o - '; + $dl{'timeout'} = ''; + } + else { + $dl{'dl'} = ''; + } + # no-ssl-opt is set to 1 with --no-ssl, so it is true, then assign + $dl{'no-ssl-opt'} = $dl{'no-ssl'} if $dl{'no-ssl-opt'}; + eval $end if $b_log; +} + +sub set_perl_downloader { + my ($downloader) = @_; + $downloader =~ s/perl/tiny/; + return $downloader; } -# download_file 'stdout' 'https://cnn.com/robots.txt' '';echo $?; exit #### ------------------------------------------------------------------- #### ERROR HANDLER #### ------------------------------------------------------------------- -# Error handling -# args: $1 - error number; $2 - optional, extra information; $3 - optional extra info -error_handler() -{ - eval $LOGFS - local error_message='' - - # assemble the error message - case $1 in - 2) error_message="large flood danger, debug buffer full!" - ;; - 3) error_message="unsupported color scheme number: $2" - ;; - 4) error_message="unsupported verbosity level: $2" - ;; - 5) error_message="dependency not met: $2 not found in path.\nFor distribution installation package names and missing apps information, run: $SELF_NAME --recommends" - ;; - 6) error_message="/proc not found! Quitting..." - ;; - 7) error_message="One of the options you entered in your script parameters: $2\nis not supported.The option may require extra arguments to work.\nFor supported options (and their arguments), check the help menu: $SELF_NAME -h" - ;; - 8) error_message="the self-updater failed, $DOWNLOADER exited with error: $2.\nYou probably need to be root.\nHint, to make for easy updates without being root, do: chown <user name> $SELF_PATH/$SELF_NAME" - ;; - 9) error_message="unsupported debugging level: $2" - ;; - 10) - error_message="the alt download url you provided: $2\nappears to be wrong, download aborted. Please note, the url\nneeds to end in /, without $SELF_NAME, like: http://yoursite.com/downloads/" - ;; - 11) - error_message="unsupported testing option argument: -! $2" - ;; - 12) - error_message="the git branch download url: $2\nappears to be empty currently. Make sure there is an actual source branch version\nactive before you try this again. Check https://github.com/smxi/inxi\nto verify the branch status." - ;; - 13) - error_message="The -t option requires the following extra arguments (no spaces between letters/numbers):\nc m cm [required], for example: -t cm8 OR -t cm OR -t c9\n(numbers: 1-20, > 5 throttled to 5 in irc clients) You entered: $2" - ;; - 14) - error_message="failed to write correctly downloaded $SELF_NAME to location $SELF_PATH.\nThis usually means you don't have permission to write to that location, maybe you need to be root?\nThe operation failed with error: $2" - ;; - 15) - error_message="failed set execute permissions on $SELF_NAME at location $SELF_PATH.\nThis usually means you don't have permission to set permissions on files there, maybe you need to be root?\nThe operation failed with error: $2" - ;; - 16) - error_message="$SELF_NAME downloaded but the file data is corrupted. Purged data and using current version." - ;; - 17) - error_message="All $SELF_NAME self updater features have been disabled by the distribution\npackage maintainer. This includes the option you used: $2" - ;; - 18) - error_message="The argument you provided for $2 does not have supported syntax.\nPlease use the following formatting:\n$3" - ;; - 19) - error_message="The option $2 has been deprecated. Please use $3 instead.\nSee -h for instructions and syntax." - ;; - 20) - error_message="The option you selected has been deprecated. $2\nSee the -h (help) menu for currently supported options." - ;; - 21) - error_message="Width option requires an integer value of 80 or more.\nYou entered: $2" - ;; - *) error_message="error unknown: $@" - set -- 99 - ;; - esac - # then print it and exit - print_screen_output "Error $1: $error_message" - eval $LOGFE - exit $1 +sub error_handler { + my ( $err, $one, $two) = @_; + my ($b_recommends,$b_help,$errno) = (0,0,0); + my $message = do { + if ( $err eq 'empty' ) { 'empty value' } + ## Basic rules + elsif ( $err eq 'not-in-irc' ) { + $errno=1; "You can't run option $one in an IRC client!" } + ## Internal/external options + elsif ( $err eq 'bad-arg' ) { + $errno=10; $b_help=1; "Unsupported value: $two for option: $one" } + elsif ( $err eq 'bad-arg-int' ) { + $errno=11; "Bad internal argument: $one" } + elsif ( $err eq 'distro-block' ) { + $errno=20; "Option: $one has been disabled by the $self_name distribution maintainer." } + elsif ( $err eq 'option-feature-incomplete' ) { + $errno=21; "Option: '$one' feature: '$two' has not been implemented yet." } + elsif ( $err eq 'unknown-option' ) { + $errno=22; $b_help=1; "Unsupported option: $one" } + ## Data + elsif ( $err eq 'open-data' ) { + $errno=32; "Error opening data for reading: $one \nError: $two" } + elsif ( $err eq 'download-error' ) { + $errno=33; "Error downloading file with $dl{'dl'}: $one \nError: $two" } + ## Files: + elsif ( $err eq 'copy-failed' ) { + $errno=40; "Error copying file: $one \nError: $two" } + elsif ( $err eq 'create' ) { + $errno=41; "Error creating file: $one \nError: $two" } + elsif ( $err eq 'downloader-error' ) { + $errno=42; "Error downloading file: $one \nfor download source: $two" } + elsif ( $err eq 'file-corrupt' ) { + $errno=43; "Downloaded file is corrupted: $one" } + elsif ( $err eq 'mkdir' ) { + $errno=44; "Error creating directory: $one \nError: $two" } + elsif ( $err eq 'open' ) { + $errno=45; "Error opening file: $one \nError: $two" } + elsif ( $err eq 'open-dir' ) { + $errno=46; "Error opening directory: $one \nError: $two" } + elsif ( $err eq 'output-file-bad' ) { + $errno=47; "Value for --output-file must be full path, a writable directory, \nand include file name. Path: $two" } + elsif ( $err eq 'not-writable' ) { + $errno=48; "The file: $one is not writable!" } + elsif ( $err eq 'open-dir-failed' ) { + $errno=49; "The directory: $one failed to open with error: $two" } + elsif ( $err eq 'remove' ) { + $errno=50; "Failed to remove file: $one Error: $two" } + elsif ( $err eq 'rename' ) { + $errno=51; "There was an error moving files: $one\nError: $two" } + elsif ( $err eq 'write' ) { + $errno=52; "Failed writing file: $one - Error: $two!" } + ## Downloaders + elsif ( $err eq 'missing-downloader' ) { + $errno=60; "Downloader program $two could not be located on your system." } + elsif ( $err eq 'missing-perl-downloader' ) { + $errno=61; $b_recommends=1; "Perl downloader missing required module." } + ## FTP + elsif ( $err eq 'ftp-bad-path' ) { + $errno=70; "Unable to locate for FTP upload file:\n$one" } + elsif ( $err eq 'ftp-login' ) { + $errno=71; "There was an error with login to ftp server: $one" } + elsif ( $err eq 'ftp-upload' ) { + $errno=72; "There was an error with upload to ftp server: $one" } + ## Modules + elsif ( $err eq 'required-module' ) { + $errno=80; $b_recommends=1; "The required $one Perl module is not installed:\n$two" } + ## DEFAULT + else { + $errno=255; "Error handler ERROR!! Unsupported options: $err!"} + }; + print_line("Error $errno: $message\n"); + if ($b_help){ + print_line("Check -h for correct parameters.\n"); + } + if ($b_recommends){ + print_line("See --recommends for more information.\n"); + } + exit 0; +} +sub error_defaults { + my ($type,$one) = @_; + $one ||= ''; + my %errors = ( + 'download-error' => "Download Failure:\n$one\n", + ); + return $errors{$type}; } #### ------------------------------------------------------------------- -#### LOGGING +#### RECOMMENDS #### ------------------------------------------------------------------- -# called in the initial -@ 10 script args setting so we can get logging as soon as possible -# will have max 3 files, inxi.log, inxi.1.log, inxi.2.log -create_rotate_logfiles() +## CheckRecommends { - # do the rotation if logfile exists - if [[ -f $LOG_FILE ]];then - # copy if present second to third - if [[ -f $LOG_FILE_1 ]];then - mv -f $LOG_FILE_1 $LOG_FILE_2 - fi - # then copy initial to second - mv -f $LOG_FILE $LOG_FILE_1 - fi - # now create the logfile - touch $LOG_FILE - # and echo the start data - echo "=========================================================" >> $LOG_FILE - echo "START $SELF_NAME LOGGING:" >> $LOG_FILE - echo "Script started: $( date +%Y-%m-%d-%H:%M:%S )" >> $LOG_FILE - echo "=========================================================" >> $LOG_FILE +package CheckRecommends; +sub run { + main::error_handler('not-in-irc', 'recommends') if $b_irc; + my (@data,@rows); + my $line = make_line(); + my $pm = get_pm(); + @data = basic_data($line); + push @rows,@data; + if (!$bsd_type){ + @data = check_items('required system directories',$line,$pm); + push @rows,@data; + } + @data = check_items('recommended system programs',$line,$pm); + push @rows,@data; + @data = check_items('recommended display information programs',$line,$pm); + push @rows,@data; + @data = check_items('recommended downloader programs',$line,$pm); + push @rows,@data; + @data = check_items('recommended Perl modules',$line,$pm); + push @rows,@data; + @data = check_items('recommended directories',$line,''); + push @rows,@data; + @data = check_items('recommended files',$line,''); + push @rows,@data; + @data = ( + ['0', '', '', "$line"], + ['0', '', '', "Ok, all done with the checks. Have a nice day."], + ['0', '', '', " "], + ); + push @rows,@data; + #print Data::Dumper::Dumper \@rows; + main::print_basic(@rows); + exit 1; } -# NOTE: no logging available until get_parameters is run, since that's what sets logging -# in order to trigger earlier logging manually set B_USE_LOGGING to true in top variables. -# $1 alone: logs data; $2 with or without $3 logs func start/end. -# $1 type (fs/fe/cat/raw) or logged data; [$2 is $FUNCNAME; [$3 - function args]] -log_function_data() -{ - if [ "$B_USE_LOGGING" == 'true' ];then - local logged_data='' spacer=' ' line='----------------------------------------' - case $1 in - fs) - logged_data="Function: $2 - Primary: Start" - if [ -n "$3" ];then - logged_data="$logged_data\n${spacer}Args: $3" - fi - spacer='' - ;; - fe) - logged_data="Function: $2 - Primary: End" - spacer='' - ;; - cat) - if [[ $B_LOG_FULL_DATA == 'true' ]];then - for cat_file in $2 - do - logged_data="$logged_data\n$line\nFull file data: cat $cat_file\n\n$( cat $cat_file )\n$line\n" - done - spacer='' - fi - ;; - raw) - if [[ $B_LOG_FULL_DATA == 'true' ]];then - logged_data="\n$line\nRaw system data:\n\n$2\n$line\n" - spacer='' - fi - ;; - *) - logged_data="$1" - ;; - esac - # Create any required line breaks and strip out escape color code, either ansi (case 1)or irc (case 2). - # This pattern doesn't work for irc colors, if we need that someone can figure it out - if [[ -n $logged_data ]];then - if [[ $B_LOG_COLORS != 'true' ]];then - echo -e "${spacer}$logged_data" | sed $SED_RX 's/\x1b\[[0-9]{1,2}(;[0-9]{1,2}){0,2}m//g' >> $LOG_FILE - else - echo -e "${spacer}$logged_data" >> $LOG_FILE - fi - fi - fi +sub basic_data { + my ($line) = @_; + my (@data,@rows); + my $client = $client{'name-print'}; + $client .= ' ' . $client{'version'} if $client{'version'}; + my $default_shell = 'N/A'; + if ($ENV{'SHELL'}){ + $default_shell = $ENV{'SHELL'}; + $default_shell =~ s/.*\///; + } + my $sh = main::check_program('sh'); + my $sh_real = Cwd::abs_path($sh); + @rows = ( + ['0', '', '', "$self_name will now begin checking for the programs it needs + to operate."], + ['0', '', '', "" ], + ['0', '', '', "Check $self_name --help or the man page (man $self_name) + to see what options are available." ], + ['0', '', '', "$line" ], + ['0', '', '', "Test: core tools:" ], + ['0', '', '', "" ], + ['0', '', '', "Perl version: ^$]" ], + ['0', '', '', "Current shell: " . $client ], + ['0', '', '', "Default shell: " . $default_shell ], + ['0', '', '', "sh links to: $sh_real" ], + ); + return @rows; +} +sub check_items { + my ($type,$line,$pm) = @_; + my (@data,%info,@missing,$row,@rows,$result,@unreadable); + my ($b_dir,$b_file,$b_module,$b_program,$item); + my ($about,$extra,$extra2,$extra3,$extra4,$info_os,$install) = ('','','','','','info',''); + if ($type eq 'required system directories'){ + @data = qw(/proc /sys); + $b_dir = 1; + $item = 'Directory'; + } + elsif ($type eq 'recommended system programs'){ + if ($bsd_type){ + @data = qw(camcontrol dig dmidecode fdisk file glabel gpart ifconfig ipmi-sensors + ipmitool lsusb sudo smartctl sysctl tree uptime usbdevs); + $info_os = 'info-bsd'; + } + else { + @data = qw(dig dmidecode fdisk file hddtemp ifconfig ip ipmitool ipmi-sensors + lsblk lsusb modinfo runlevel sensors strings sudo tree uptime); + } + $b_program = 1; + $item = 'Program'; + $extra2 = "Note: IPMI sensors are generally only found on servers. To access + that data, you only need one of the ipmi items."; + } + elsif ($type eq 'recommended display information programs'){ + if ($bsd_type){ + @data = qw(glxinfo wmctrl xdpyinfo xprop xrandr); + $info_os = 'info-bsd'; + } + else { + @data = qw(glxinfo wmctrl xdpyinfo xprop xrandr); + } + $b_program = 1; + $item = 'Program'; + } + elsif ($type eq 'recommended downloader programs'){ + if ($bsd_type){ + @data = qw(curl dig fetch ftp wget); + $info_os = 'info-bsd'; + } + else { + @data = qw(curl dig wget); + } + $b_program = 1; + $extra = ' (You only need one of these)'; + $extra2 = "Perl HTTP::Tiny is the default downloader tool if IO::Socket::SSL is present. + See --help --alt 40-44 options for how to override default downloader(s) in case of issues. "; + $extra3 = "If dig is installed, it is the default for WAN IP data. + Strongly recommended. Dig is fast and accurate."; + $extra4 = ". However, you really only need dig in most cases. All systems should have "; + $extra4 .= "at least one of the downloader options present."; + $item = 'Program'; + } + elsif ($type eq 'recommended Perl modules'){ + @data = qw(HTTP::Tiny IO::Socket::SSL Time::HiRes Cpanel::JSON::XS JSON::XS XML::Dumper); + $b_module = 1; + $item = 'Perl Module'; + $extra = ' (Optional)'; + $extra2 = "None of these are strictly required, but if you have them all, you can eliminate + some recommended non Perl programs from the install. "; + $extra3 = "HTTP::Tiny and IO::Socket::SSL must both be present to use as a downloader option. + For json export Cpanel::JSON::XS is preferred over JSON::XS."; + } + elsif ($type eq 'recommended directories'){ + if ($bsd_type){ + @data = qw(/dev); + } + else { + @data = qw(/dev /dev/disk/by-id /dev/disk/by-label /dev/disk/by-path + /dev/disk/by-uuid /sys/class/dmi/id); + } + $b_dir = 1; + $item = 'Directory'; + } + elsif ($type eq 'recommended files'){ + if ($bsd_type){ + @data = qw(/var/run/dmesg.boot /var/log/Xorg.0.log); + } + else { + @data = qw(/etc/lsb-release /etc/os-release /proc/asound/cards + /proc/asound/version /proc/cpuinfo /proc/mdstat /proc/meminfo /proc/modules + /proc/mounts /proc/scsi/scsi /var/log/Xorg.0.log ); + } + $b_file = 1; + $item = 'File'; + $extra2 = "Note that not all of these are used by every system, + so if one is missing it's usually not a big deal."; + } + @rows = ( + ['0', '', '', "$line" ], + ['0', '', '', "Test: $type$extra:" ], + ['0', '', '', " " ], + ); + if ($extra2){ + $rows[scalar @rows] = (['0', '', '', $extra2]); + $rows[scalar @rows] = (['0', '', '', ' ']); + } + if ($extra3){ + $rows[scalar @rows] = (['0', '', '', $extra3]); + $rows[scalar @rows] = (['0', '', '', ' ']); + } + foreach (@data){ + $install = ''; + $about = ''; + %info = item_data($_); + $about = $info{$info_os}; + if ( ( $b_dir && -d $_ ) || ( $b_file && -r $_ ) || + ($b_program && main::check_program($_) ) || ($b_module && main::check_module($_)) ){ + $result = 'Present'; + } + elsif ($b_file && -f $_){ + $result = 'Unreadable'; + push @unreadable, "$_"; + } + else { + $result = 'Missing'; + $install = " ~ Install package: $info{$pm}" if (($b_program || $b_module) && $pm); + push @missing, "$_$install"; + } + $row = make_row($_,$about,$result); + $rows[scalar @rows] = (['0', '', '', $row]); + } + $rows[scalar @rows] = (['0', '', '', " "]); + if (@missing){ + $rows[scalar @rows] = (['0', '', '', "The following $type are missing$extra4:"]); + foreach (@missing) { + $rows[scalar @rows] = (['0', '', '', "$item: $_"]); + } + } + if (@unreadable){ + $rows[scalar @rows] = (['0', '', '', "The following $type are not readable: "]); + foreach (@unreadable) { + $rows[scalar @rows] = (['0', '', '', "$item: $_"]); + } + } + if (!@missing && !@unreadable){ + $rows[scalar @rows] = (['0', '', '', "All $type are present"]); + } + return @rows; +} + +sub item_data { + my ($type) = @_; + my %data = ( + # directory data + '/sys/class/dmi/id' => ({ + 'info' => '-M system, motherboard, bios', + }), + '/dev' => ({ + 'info' => '-l,-u,-o,-p,-P,-D disk partition data', + }), + '/dev/disk/by-id' => ({ + 'info' => '-D serial numbers', + }), + '/dev/disk/by-path' => ({ + 'info' => '-D extra data', + }), + '/dev/disk/by-label' => ({ + 'info' => '-l,-o,-p,-P partition labels', + }), + '/dev/disk/by-uuid' => ({ + 'info' => '-u,-o,-p,-P partition uuid', + }), + '/proc' => ({ + 'info' => '', + }), + '/sys' => ({ + 'info' => '', + }), + # file data + '/etc/lsb-release' => ({ + 'info' => '-S distro version data (older version)', + }), + '/etc/os-release' => ({ + 'info' => '-S distro version data (newer version)', + }), + '/proc/asound/cards' => ({ + 'info' => '-A sound card data', + }), + '/proc/asound/version' => ({ + 'info' => '-A ALSA data', + }), + '/proc/cpuinfo' => ({ + 'info' => '-C cpu data', + }), + '/proc/mdstat' => ({ + 'info' => '-R mdraid data (if you use dm-raid)', + }), + '/proc/meminfo' => ({ + 'info' => '-I,-tm, -m memory data', + }), + '/proc/modules' => ({ + 'info' => '-G module data (sometimes)', + }), + '/proc/mounts' => ({ + 'info' => '-P,-p partition advanced data', + }), + '/proc/scsi/scsi' => ({ + 'info' => '-D Advanced hard disk data (used rarely)', + }), + '/var/log/Xorg.0.log' => ({ + 'info' => '-G graphics driver load status', + }), + '/var/run/dmesg.boot' => ({ + 'info' => '-D,-d disk data', + }), + # system tools + # apt-dpkg,apt-get; pm-arch,pacman; rpm-redhat,suse + 'curl' => ({ + 'info' => '-i (if no dig); -w,-W; -U', + 'info-bsd' => '-i (if no dig); -w,-W; -U', + 'apt' => 'curl', + 'pacman' => 'curl', + 'rpm' => 'curl', + }), + 'camcontrol' => ({ + 'info' => '', + 'info-bsd' => '-R; -D; -P. Get actual gptid /dev path', + 'apt' => '', + 'pacman' => '', + 'rpm' => '', + }), + 'dig' => ({ + 'info' => '-i wlan IP', + 'info-bsd' => '-i wlan IP', + 'apt' => 'dnsutils', + 'pacman' => 'dnsutils', + 'rpm' => 'bind-utils', + }), + 'dmidecode' => ({ + 'info' => '-M if no sys machine data; -m', + 'info-bsd' => '-M if null sysctl; -m; -B if null sysctl', + 'apt' => 'dmidecode', + 'pacman' => 'dmidecode', + 'rpm' => 'dmidecode', + }), + 'fdisk' => ({ + 'info' => '-D partition scheme (fallback)', + 'info-bsd' => '-D partition scheme', + 'apt' => 'fdisk', + 'pacman' => 'util-linux', + 'rpm' => 'util-linux', + }), + 'fetch' => ({ + 'info' => '', + 'info-bsd' => '-i (if no dig); -w,-W; -U', + 'apt' => '', + 'pacman' => '', + 'rpm' => '', + }), + 'file' => ({ + 'info' => '-o unmounted file system (if no lsblk)', + 'info-bsd' => '-o unmounted file system', + 'apt' => 'file', + 'pacman' => 'file', + 'rpm' => 'file', + }), + 'ftp' => ({ + 'info' => '', + 'info-bsd' => '-i (if no dig); -w,-W; -U', + 'apt' => '', + 'pacman' => '', + 'rpm' => '', + }), + 'glabel' => ({ + 'info' => '', + 'info-bsd' => '-R; -D; -P. Get actual gptid /dev path', + 'apt' => '', + 'pacman' => '', + 'rpm' => '', + }), + 'gpart' => ({ + 'info' => '', + 'info-bsd' => '-p,-P file system, size', + 'apt' => '', + 'pacman' => '', + 'rpm' => '', + }), + 'hciconfig' => ({ + 'info' => 'Experimental', + 'info-bsd' => '', + 'apt' => 'bluez', + 'pacman' => 'bluez-utils', + 'rpm' => 'bluez-utils', + }), + 'hddtemp' => ({ + 'info' => '-Dx show hdd temp', + 'info-bsd' => '-Dx show hdd temp', + 'apt' => 'hddtemp', + 'pacman' => 'hddtemp', + 'rpm' => 'hddtemp', + }), + 'ifconfig' => ({ + 'info' => '-i ip LAN (deprecated)', + 'info-bsd' => '-i ip LAN', + 'apt' => 'net-tools', + 'pacman' => 'net-tools', + 'rpm' => 'net-tools', + }), + 'ip' => ({ + 'info' => '-i ip LAN', + 'info-bsd' => '', + 'apt' => 'iproute', + 'pacman' => 'iproute2', + 'rpm' => 'iproute', + }), + 'ipmi-sensors' => ({ + 'info' => '-s IPMI sensors (servers)', + 'info-bsd' => '', + 'apt' => 'freeipmi-tools', + 'pacman' => 'freeipmi', + 'rpm' => 'freeipmi', + }), + 'ipmitool' => ({ + 'info' => '-s IPMI sensors (servers)', + 'info-bsd' => '-s IPMI sensors (servers)', + 'apt' => 'ipmitool', + 'pacman' => 'ipmitool', + 'rpm' => 'ipmitool', + }), + 'lsblk' => ({ + 'info' => '-o unmounted file system (best option)', + 'info-bsd' => '-o unmounted file system', + 'apt' => 'util-linux', + 'pacman' => 'util-linux', + 'rpm' => 'util-linux-ng', + }), + 'lsusb' => ({ + 'info' => '-A usb audio; -N usb networking; --usb', + 'info-bsd' => '-A; -N; --usb. Alternate to usbdevs', + 'apt' => 'usbutils', + 'pacman' => 'usbutils', + 'rpm' => 'usbutils', + }), + 'modinfo' => ({ + 'info' => 'Ax; -Nx module version', + 'info-bsd' => '', + 'apt' => 'module-init-tools', + 'pacman' => 'module-init-tools', + 'rpm' => 'module-init-tools', + }), + 'runlevel' => ({ + 'info' => '-I fallback to Perl', + 'info-bsd' => '', + 'apt' => 'systemd or sysvinit', + 'pacman' => 'systemd', + 'rpm' => 'systemd or sysvinit', + }), + 'sensors' => ({ + 'info' => '-s sensors output', + 'info-bsd' => '', + 'apt' => 'lm-sensors', + 'pacman' => 'lm-sensors', + 'rpm' => 'lm-sensors', + }), + 'smartctl' => ({ + 'info' => '-Dx show hdd temp', + 'info-bsd' => '-Dx show hdd temp', + 'apt' => '', + 'pacman' => '', + 'rpm' => '', + }), + 'strings' => ({ + 'info' => '-I sysvinit version', + 'info-bsd' => '', + 'apt' => 'binutils', + 'pacman' => '?', + 'rpm' => '?', + }), + 'sysctl' => ({ + 'info' => '', + 'info-bsd' => '-C; -I; -m; -tm', + 'apt' => '?', + 'pacman' => '?', + 'rpm' => '?', + }), + 'sudo' => ({ + 'info' => '-Dx hddtemp-user; -o file-user', + 'info-bsd' => '-Dx hddtemp-user; -o file-user', + 'apt' => 'sudo', + 'pacman' => 'sudo', + 'rpm' => 'sudo', + }), + 'tree' => ({ + 'info' => '--debugger 20,21 /sys tree', + 'info-bsd' => '--debugger 20,21 /sys tree', + 'apt' => 'tree', + 'pacman' => 'tree', + 'rpm' => 'tree', + }), + 'uptime' => ({ + 'info' => '-I uptime', + 'info-bsd' => '-I uptime', + 'apt' => 'procps', + 'pacman' => 'procps', + 'rpm' => 'procps', + }), + 'usbdevs' => ({ + 'info' => '', + 'info-bsd' => '-A; -N; --usb;', + 'apt' => 'usbutils', + 'pacman' => 'usbutils', + 'rpm' => 'usbutils', + }), + 'wget' => ({ + 'info' => '-i (if no dig); -w,-W; -U', + 'info-bsd' => '-i (if no dig); -w,-W; -U', + 'apt' => 'wget', + 'pacman' => 'wget', + 'rpm' => 'wget', + }), + # display tools + 'glxinfo' => ({ + 'info' => '-G glx info', + 'info-bsd' => '-G glx info', + 'apt' => 'mesa-utils', + 'pacman' => 'mesa-demos', + 'rpm' => 'glx-utils (openSUSE 12.3 and later Mesa-demo-x)', + }), + 'wmctrl' => ({ + 'info' => '-S active window manager (not all wm)', + 'info-bsd' => '-S active window managerr (not all wm)', + 'apt' => 'wmctrl', + 'pacman' => 'wmctrl', + 'rpm' => 'wmctrl', + }), + 'xdpyinfo' => ({ + 'info' => '-G multi screen resolution', + 'info-bsd' => '-G multi screen resolution', + 'apt' => 'X11-utils', + 'pacman' => 'xorg-xdpyinfo', + 'rpm' => 'xorg-x11-utils', + }), + 'xprop' => ({ + 'info' => '-S desktop data', + 'info-bsd' => '-S desktop data', + 'apt' => 'X11-utils', + 'pacman' => 'xorg-xprop', + 'rpm' => 'x11-utils', + }), + 'xrandr' => ({ + 'info' => '-G single screen resolution', + 'info-bsd' => '-G single screen resolution', + 'apt' => 'x11-xserver-utils', + 'pacman' => 'xrandr', + 'rpm' => 'x11-server-utils', + }), + # Perl modules + 'Cpanel::JSON::XS' => ({ + 'info' => '--output json - required for export.', + 'info-bsd' => '--output json - required for export.', + 'apt' => 'libcpanel-json-xs-perl', + 'pacman' => 'perl-cpanel-json-xs', + 'rpm' => 'perl-Cpanel-JSON-XS', + }), + 'HTTP::Tiny' => ({ + 'info' => '-U; -w,-W; -i (if dig not installed).', + 'info-bsd' => '-U; -w,-W; -i (if dig not installed)', + 'apt' => 'libhttp-tiny-perl', + 'pacman' => 'Core Modules', + 'rpm' => 'Perl-http-tiny', + }), + 'IO::Socket::SSL' => ({ + 'info' => '-U; -w,-W; -i (if dig not installed).', + 'info-bsd' => '-U; -w,-W; -i (if dig not installed)', + 'apt' => 'libio-socket-ssl-perl', + 'pacman' => 'perl-io-socket-ssl', + 'rpm' => 'perl-IO-Socket-SSL', + }), + 'JSON::XS' => ({ + 'info' => '--output json - required for export (legacy).', + 'info-bsd' => '--output json - required for export (legacy).', + 'apt' => 'libjson-xs-perl', + 'pacman' => 'perl-json-xs', + 'rpm' => 'perl-JSON-XS', + }), + 'Time::HiRes' => ({ + 'info' => '-C cpu sleep (not required); --debug timers', + 'info-bsd' => '-C cpu sleep (not required); --debug timers', + 'apt' => 'Core Modules', + 'pacman' => 'Core Modules', + 'rpm' => 'perl-Time-HiRes', + }), + 'XML::Dumper' => ({ + 'info' => '--output xml - Crude and raw.', + 'info-bsd' => '--output xml - Crude and raw.', + 'apt' => 'libxml-dumper-perl', + 'pacman' => 'perl-xml-dumper', + 'rpm' => 'perl-XML-Dumper', + }), + ); + my $ref = $data{$type}; + my %values = %$ref; + return %values; +} +sub get_pm { + my ($pm) = (''); + if (main::check_program('dpkg')){ + $pm = 'apt'; + } + elsif (main::check_program('pacman')){ + $pm = 'pacman'; + } + elsif (main::check_program('rpm')){ + $pm = 'rpm'; + } + return $pm; +} +# note: end will vary, but should always be treated as longest value possible. +# expected values: Present/Missing +sub make_row { + my ($start,$middle,$end) = @_; + my ($dots,$line,$sep) = ('','',': '); + foreach (0 .. ($size{'max'} - 16 - length("$start$middle"))){ + $dots .= '.'; + } + $line = "$start$sep$middle$dots $end"; + return $line; +} +sub make_line { + my $line = ''; + foreach (0 .. $size{'max'} - 2 ){ + $line .= '-'; + } + return $line; +} } #### ------------------------------------------------------------------- -#### RECOMMENDS +#### TOOLS #### ------------------------------------------------------------------- -check_recommends_user_output() -{ - local Line=$LINE1 - local gawk_version='N/A' sed_version='N/A' sudo_version='N/A' python_version='N/A' - local perl_version='N/A' - - if [[ $B_IRC == 'true' ]];then - print_screen_output "Sorry, you can't run this option in an IRC client." - exit 1 - fi - initialize_paths - print_lines_basic "0" "" "$SELF_NAME will now begin checking for the programs it needs to operate. First a check of the main languages and tools $SELF_NAME uses. Python is only for debugging data uploads unless Perl is missing." - echo $Line - echo "Bash version: $( bash --version 2>&1 | awk 'BEGIN {IGNORECASE=1} /^GNU bash/ {print $4}' )" - if type -p gawk &>/dev/null;then - gawk_version=$( gawk --version 2>&1 | gawk 'BEGIN {IGNORECASE=1} /^GNU Awk/ {print $3}' ) - fi - if type -p sed &>/dev/null;then - # sed (GNU sed) 4.4 OR GNU sed version 4.4 - sed_version=$( sed --version 2>&1 | awk 'BEGIN {IGNORECASE=1} /^(GNU sed version|sed)/ {print $4;exit}' ) - if [[ -z $sed_version ]];then - # note: bsd sed shows error with --version flag - sed_version=$( sed --version 2>&1 | awk 'BEGIN {IGNORECASE=1} /^sed: illegal option/ {print "BSD sed";exit}' ) - fi - fi - if type -p sudo &>/dev/null;then - sudo_version=$( sudo -V 2>&1 | awk 'BEGIN {IGNORECASE=1} /^Sudo version/ {print $3}' ) - fi - if type -p python &>/dev/null;then - python_version=$( python --version 2>&1 | awk 'BEGIN {IGNORECASE=1} /^Python/ {print $2}' ) - fi - # NOTE: does not actually handle 5/6 version, but ok for now - if type -p perl &>/dev/null;then - perl_version=$(perl --version | grep -m 1 -oE 'v[0-9.]+') - fi - echo "Gawk version: $gawk_version" - echo "Sed version: $sed_version" - echo "Sudo version: $sudo_version" - echo "Python version: $python_version (legacy, no longer used)" - echo "Perl version: $perl_version" - echo $Line - - echo "Test One: Required System Directories (Linux Only)." - print_lines_basic "0" "" "If one of these system directories is missing, $SELF_NAME cannot operate:" - echo - check_recommends_items 'required-dirs' - - echo "Test Two: Required Core Applications." - print_lines_basic "0" "" "If one of these applications is missing, $SELF_NAME cannot operate:" - echo - check_recommends_items 'required-apps' - - print_lines_basic "0" "" "Test Three: Script Recommends for Graphics Features." - print_lines_basic "0" "" "NOTE: If you do not use X these do not matter (like a headless server). Otherwise, if one of these applications is missing, $SELF_NAME will have incomplete output:" - echo - check_recommends_items 'recommended-x-apps' - - echo 'Test Four: Script Recommends for Remaining Features.' - print_lines_basic "0" "" "If one of these applications is missing, $SELF_NAME will have incomplete output:" - echo - check_recommends_items 'recommended-apps' - - echo 'Test Five: Script Recommends for Remaining Features.' - print_lines_basic "0" "" "One of these downloaders needed for options -i/-w/-W (-U/-! [11-15], if supported):" - echo - check_recommends_items 'downloaders' - - echo 'Test Six: System Directories for Various Information.' - echo '(Unless otherwise noted, these are for GNU/Linux systems)' - print_lines_basic "0" "" "If one of these directories is missing, $SELF_NAME may have incomplete output:" - echo - check_recommends_items 'system-dirs' - - echo 'Test Seven: System Files for Various Information.' - echo '(Unless otherwise noted, these are for GNU/Linux systems)' - print_lines_basic "0" "" "If one of these files is missing, $SELF_NAME may have incomplete output:" - echo - check_recommends_items 'system-files' - - echo 'All tests completed.' +# Duplicates the functionality of awk to allow for one liner +# type data parsing. note: -1 corresponds to awk NF +# args 1: array of data; 2: search term; 3: field result; 4: separator +# correpsonds to: awk -F='separator' '/search/ {print $2}' <<< @data +# array is sent by reference so it must be dereferenced +# NOTE: if you just want the first row, pass it \S as search string +# NOTE: if $num is undefined, it will skip the second step +sub awk { + eval $start if $b_log; + my ($ref,$search,$num,$sep) = @_; + my ($result); + # print "search: $search\n"; + return if ! @$ref || ! $search; + foreach (@$ref){ + if (/$search/i){ + $result = $_; + $result =~ s/^\s+|\s+$//g; + last; + } + } + if ($result && defined $num){ + $sep ||= '\s+'; + $num-- if $num > 0; # retain the negative values as is + $result = (split /$sep/, $result)[$num]; + $result =~ s/^\s+|,|\s+$//g if $result; + } + eval $end if $b_log; + return $result; +} +# $1 - Perl module to check +sub check_module { + my ($module) = @_; + my $b_present = 0; + eval "require $module"; + $b_present = 1 if !$@; + return $b_present; +} +# arg: 1 - string or path to search gneerated @paths data for. +# note: a few nano seconds are saved by using raw $_[0] for program +sub check_program { + (grep { return "$_/$_[0]" if -e "$_/$_[0]"} @paths)[0]; } -# Should come after above for debugging tests -# args: $1 - check item -check_recommends_items() -{ - local item='' item_list='' item_string='' missing_items='' missing_string='' - local package='' application='' feature='' type='' starter='' finisher='' - local package_deb='' package_pacman='' package_rpm='' downloaders_bsd='' - local print_string='' separator='' width=56 - local required_dirs='/proc /sys' - - if [[ -n $BSD_TYPE ]];then - downloaders_bsd='fetch:BSD-only~BSD-only~BSD-only~:-i_wan_ip;-w/-W;-U/-!_[11-15]_[OR] - ftp:ftp-OpenBSD-only~ftp-OpenBSD-only~ftp-OpenBSD-only~:-i_wan_ip;-w/-W;-U/-!_[11-15]_[OR]' - fi - # package-owner: 1 - debian/ubuntu; 2 - arch; 3 - yum/rpm - # pardus: pisi sf -q /usr/bin/package - # https://wiki.archlinux.org/index.php/Perl_Policy - # https://www.debian.org/doc/packaging-manuals/perl-policy/index.html - local required_apps=' - df:coreutils~coreutils~coreutils~:partition_data - gawk:gawk~gawk~gawk~:core_tool - grep:grep~grep~grep~:string_search - perl:perl~perl~perl~:debugger_uploader;_debugger_/sys_traverse - lspci:pciutils~pciutils~pciutils~:hardware_data - ps:procps~procps~procps~:process_data - readlink:coreutils~coreutils~coreutils~: - sed:sed~sed~sed~:string_replace - tr:coreutils~coreutils~coreutils~:character_replace - uname:uname~coreutils~coreutils~:kernel_data - wc:coreutils~coreutils~coreutils~:word_character_count - ' - local x_recommends=' - glxinfo:mesa-utils~mesa-demos~glx-utils_(openSUSE_12.3_and_later_Mesa-demo-x)~:-G_glx_info - xdpyinfo:X11-utils~xorg-xdpyinfo~xorg-x11-utils~:-G_multi_screen_resolution - xprop:X11-utils~xorg-xprop~x11-utils~:-S_desktop_data - xrandr:x11-xserver-utils~xrandr~x11-server-utils~:-G_single_screen_resolution - ' - local recommended_apps=' - dig:dnsutils~dnsutils~bind-utils:-i_wlan_IP_(Default) - dmidecode:dmidecode~dmidecode~dmidecode~:-M_if_no_sys_machine_data;_-m_memory - file:file~file~file~:-o_unmounted_file_system - hciconfig:bluez~bluez-utils~bluez-utils~:-n_-i_bluetooth_data-dev_only-not_used - hddtemp:hddtemp~hddtemp~hddtemp~:-Dx_show_hdd_temp - ifconfig:net-tools~net-tools~net-tools~:-i_ip_lan-deprecated - ip:iproute~iproute2~iproute~:-i_ip_lan - sensors:lm-sensors~lm_sensors~lm-sensors~:-s_sensors_output - strings:binutils~~~:-I_sysvinit_version - lsusb:usbutils~usbutils~usbutils~:-A_usb_audio;-N_usb_networking - modinfo:module-init-tools~module-init-tools~module-init-tools~:-Ax,-Nx_module_version - runlevel:sysvinit~sysvinit~systemd~:-I_runlevel - sudo:sudo~sudo~sudo~:-Dx_hddtemp-user;-o_file-user - tree:tree~tree~tree~:-@1[1-5]_debugger_sys_tree - uptime:procps~procps~procps~:-I_uptime_(check_which_package_owns_Debian) - ' - - local downloaders=" - curl:curl~curl~curl~:-i_wan_ip;-w/-W;-U/-!_[11-15]_[Default|OR] - wget:wget~wget~wget~:-i_wan_ip;-w/-W;-U/-!_[11-15]_[OR] - $downloaders_bsd - perl:perl~perl~perl~:-i_wan_ip;-w/-W;-U/-!_[11-15]_[Module_HTTP::Tiny] - " - local recommended_dirs=' - /sys/class/dmi/id:-M_system,_motherboard,_bios - /dev:-l,-u,-o,-p,-P,-D_disk_partition_data - /dev/disk/by-label:-l,-o,-p,-P_partition_labels - /dev/disk/by-uuid:-u,-o,-p,-P_partition_uuid - ' - local recommended_files=" - $FILE_ASOUND_DEVICE:-A_sound_card_data - $FILE_ASOUND_VERSION:-A_ALSA_data - $FILE_CPUINFO:-C_cpu_data - $FILE_LSB_RELEASE:-S_distro_version_data_[deprecated] - $FILE_MDSTAT:-R_mdraid_data - $FILE_MEMINFO:-I_memory_data - $FILE_OS_RELEASE:-S_distro_version_data - $FILE_PARTITIONS:-p,-P_partitions_data - $FILE_MODULES:-G_module_data - $FILE_MOUNTS:-P,-p_partition_advanced_data - $FILE_DMESG_BOOT:-D,-d_disk_data_[BSD_only] - $FILE_SCSI:-D_Advanced_hard_disk_data_[used_rarely] - $FILE_XORG_LOG:-G_graphics_driver_load_status - " - if [[ -n $COLS_INNER ]];then - if [[ $COLS_INNER -ge 90 ]];then - width=${#LINE1} # match width of $LINE1 - elif [[ $COLS_INNER -ge 78 ]];then - width=$(( $COLS_INNER - 11 )) - fi - fi - case $1 in - downloaders) - item_list=$downloaders - item_string='Downloaders' - item_string='' - missing_string='downloaders, and their corresponding packages,' - type='applications' - ;; - required-dirs) - item_list=$required_dirs - item_string='Required file system' - item_string='' - missing_string='system directories' - type='directories' - ;; - required-apps) - item_list=$required_apps - item_string='Required application' - item_string='' - missing_string='applications, and their corresponding packages,' - type='applications' - ;; - recommended-x-apps) - item_list=$x_recommends - item_string='Recommended X application' - item_string='' - missing_string='applications, and their corresponding packages,' - type='applications' - ;; - recommended-apps) - item_list=$recommended_apps - item_string='Recommended application' - item_string='' - missing_string='applications, and their corresponding packages,' - type='applications' - ;; - system-dirs) - item_list=$recommended_dirs - item_string='System directory' - item_string='' - missing_string='system directories' - type='directories' - ;; - system-files) - item_list=$recommended_files - item_string='System file' - item_string='' - missing_string='system files' - type='files' - ;; - esac - # great trick from: http://ideatrash.net/2011/01/bash-string-padding-with-sed.html - # left pad: sed -e :a -e 's/^.\{1,80\}$/& /;ta' - # right pad: sed -e :a -e 's/^.\{1,80\}$/ &/;ta' - # center pad: sed -e :a -e 's/^.\{1,80\}$/ & /;ta' - - for item in $item_list - do - if [[ $( awk -F ":" '{print NF-1}' <<< $item ) -eq 0 ]];then - application=$item - package='' - feature='' - location='' - elif [[ $( awk -F ":" '{print NF-1}' <<< $item ) -eq 1 ]];then - application=$( cut -d ':' -f 1 <<< $item ) - package='' - feature=$( cut -d ':' -f 2 <<< $item ) - location='' - else - application=$( cut -d ':' -f 1 <<< $item ) - package=$( cut -d ':' -f 2 <<< $item ) - location=$( type -p $application ) - if [[ $( awk -F ":" '{print NF-1}' <<< $item ) -ge 2 ]];then - feature=$( cut -d ':' -f 3-6 <<< $item ) - else - feature='' - fi - fi - if [[ -n $feature ]];then - print_string="$item_string$application (info: $( sed 's/_/ /g' <<< $feature ))" - else - print_string="$item_string$application" - fi - - starter="$( sed -e :a -e 's/^.\{1,'$width'\}$/&./;ta' <<< $print_string )" - if [[ -z $( grep '^/' <<< $application ) && -n $location ]] || [[ -d $application || -f $application ]];then - if [[ -n $location ]];then - finisher=" $location" - else - finisher=" Present" - fi - else - finisher=" Missing" - missing_items="$missing_items$separator$application:$package" - separator=' ' - fi - - echo "$starter$finisher" - done - echo - if [[ -n $missing_items ]];then - echo "The following $type are missing from your system:" - for item in $missing_items - do - application=$( cut -d ':' -f 1 <<< $item ) - if [[ $type == 'applications' ]];then - echo - package=$( cut -d ':' -f 2 <<< $item ) - package_deb=$( cut -d '~' -f 1 <<< $package ) - package_pacman=$( cut -d '~' -f 2 <<< $package ) - package_rpm=$( cut -d '~' -f 3 <<< $package ) - echo "Application: $application" - print_lines_basic "0" "" "To add to your system, install the proper distribution package for your system:" - print_lines_basic "0" "" "Debian/Ubuntu:^$package_deb^:: Arch Linux:^$package_pacman^:: Redhat/Fedora/Suse:^$package_rpm" - elif [[ $type == 'directories' ]];then - echo "Directory: $application" - elif [[ $type == 'files' ]];then - echo "File: $application" - fi - done - if [[ $item_string == 'System directory' ]];then - print_lines_basic "0" "" "These directories are created by the kernel, so don't worry if they are not present." - fi - else - echo "All the $( cut -d ' ' -f 1 <<< $item_string | sed -e 's/Re/re/' -e 's/Sy/sy/' ) $type are present." - fi - echo $Line +sub cleanup { + # maybe add in future: , $fh_c, $fh_j, $fh_x + foreach my $fh ($fh_l){ + if ($fh){ + close $fh; + } + } } -#### ------------------------------------------------------------------- -#### UPDATER -#### ------------------------------------------------------------------- +# returns count of files in directory, if 0, dir is empty +sub count_dir_files { + return unless -d $_[0]; + opendir my $dh, $_[0] or error_handler('open-dir-failed', "$_[0]", $!); + my $count = grep { ! /^\.{1,2}/ } readdir $dh; # strips out . and .. + return $count; +} -# args: $1 - download url, not including file name; $2 - string to print out -# $3 - update type option -# note that $1 must end in / to properly construct the url path -self_updater() -{ - eval $LOGFS - set_downloader - local downloader_error=0 file_contents='' downloader_man_error=0 - local man_file_location=$( set_man_location ) - local man_file_path="$man_file_location/inxi.1.gz" - - if [[ $B_IRC == 'true' ]];then - print_screen_output "Sorry, you can't run the $SELF_NAME self updater option (-$3) in an IRC client." - exit 1 - fi - print_screen_output "Starting $SELF_NAME self updater." - print_screen_output "Currently running $SELF_NAME version number: $SELF_VERSION" - print_screen_output "Current version patch number: $SELF_PATCH" - print_screen_output "Current version release date: $SELF_DATE" - print_screen_output "Updating $SELF_NAME in $SELF_PATH using $2 as download source..." - case $DOWNLOADER in - curl) - file_contents="$( curl $NO_SSL_OPT -L -s $1$SELF_NAME )" || downloader_error=$? - ;; - fetch) - file_contents="$( fetch $NO_SSL_OPT -q -o - $1$SELF_NAME )" || downloader_error=$? - ;; - ftp) - file_contents="$( ftp $NO_SSL_OPT -o - $1$SELF_NAME 2>/dev/null )" || downloader_error=$? - ;; - perl) - file_contents="$( download_file 'stdout' $1$SELF_NAME )" || downloader_error=$? - ;; - wget) - file_contents="$( wget $NO_SSL_OPT -q -O - $1$SELF_NAME )" || downloader_error=$? - ;; - no-downloader) - downloader_error=1 - ;; - esac +# args: 1 - the string to get piece of +# 2 - the position in string, starting at 1 for 0 index. +# 3 - the separator, default is ' ' +sub get_piece { + eval $start if $b_log; + my ($string, $num, $sep) = @_; + $num--; + $sep ||= '\s+'; + $string =~ s/^\s+|\s+$//g; + my @temp = split(/$sep/, $string); + eval $end if $b_log; + if ( exists $temp[$num] ){ + $temp[$num] =~ s/,//g; + return $temp[$num]; + } +} - # then do the actual download - if [[ $downloader_error -eq 0 ]];then - # make sure the whole file got downloaded and is in the variable - if [[ -n $( grep '###\*\*EOF\*\*###' <<< "$file_contents" ) ]];then - echo "$file_contents" > $SELF_PATH/$SELF_NAME || error_handler 14 "$?" - chmod +x $SELF_PATH/$SELF_NAME || error_handler 15 "$?" - parse_version_data 'main' - parse_version_data 'patch' - parse_version_data 'date' - print_screen_output "Successfully updated to $2 version: $SELF_VERSION" - print_screen_output "New $2 version patch number: $SELF_PATCH" - print_screen_output "New $2 version release date: $SELF_DATE" - print_screen_output "To run the new version, just start $SELF_NAME again." - print_screen_output "----------------------------------------" - print_screen_output "Starting download of man page file now." - if [[ $B_MAN == 'false' ]];then - print_screen_output "Skipping man download because branch version is being used." - elif [[ ! -d $man_file_location ]];then - print_screen_output "The required man directory was not detected on your system, unable to continue: $man_file_location" - else - if [[ $B_ROOT == 'true' ]];then - print_screen_output "Checking Man page download URL..." - if [[ -f /usr/share/man/man8/inxi.8.gz ]];then - print_screen_output "Updating man page location to man1." - mv -f /usr/share/man/man8/inxi.8.gz $man_file_location/inxi.1.gz - if type -p mandb &>/dev/null;then - exec $( type -p mandb ) -q - fi - fi - case $DOWNLOADER in - perl) - download_file 'spider' $MAN_FILE_DOWNLOAD || downloader_man_error=$? - ;; - wget) - wget $NO_SSL_OPT -q --spider $MAN_FILE_DOWNLOAD || downloader_man_error=$? - ;; - esac - if [[ $downloader_man_error -eq 0 ]];then - if [[ $DOWNLOADER == 'wget' ]];then - print_screen_output "Man file download URL verified: $MAN_FILE_DOWNLOAD" - fi - print_screen_output "Downloading Man page file now." - case $DOWNLOADER in - curl) - curl $NO_SSL_OPT -L -s -o $man_file_path $MAN_FILE_DOWNLOAD || downloader_man_error=$? - ;; - fetch) - fetch $NO_SSL_OPT -q -o $man_file_path $MAN_FILE_DOWNLOAD || downloader_man_error=$? - ;; - ftp) - ftp $NO_SSL_OPT -o $man_file_path $MAN_FILE_DOWNLOAD 2>/dev/null || downloader_man_error=$? - ;; - perl) - download_file 'file' $MAN_FILE_DOWNLOAD $man_file_path || downloader_man_error=$? - ;; - wget) - wget $NO_SSL_OPT -q -O $man_file_path $MAN_FILE_DOWNLOAD || downloader_man_error=$? - ;; - no-downloader) - downloader_man_error=1 - ;; - esac - if [[ $downloader_man_error -gt 0 ]];then - print_screen_output "Oh no! Something went wrong downloading the Man gz file at: $MAN_FILE_DOWNLOAD" - print_screen_output "Check the error messages for what happened. Error: $downloader_man_error" - else - print_screen_output "Download/install of man page successful. Check to make sure it works: man inxi" - fi - else - print_screen_output "Man file download URL failed, unable to continue: $MAN_FILE_DOWNLOAD" - fi - else - print_screen_output "Updating / Installing the Man page requires root user, writing to: $man_file_location" - print_screen_output "If you want the man page, you'll have to run $SELF_NAME -$3 as root." - fi - fi - exit 0 - else - error_handler 16 - fi - # now run the error handlers on any wget failure - else - if [[ $2 == 'source server' ]];then - error_handler 8 "$downloader_error" - elif [[ $2 == 'alt server' ]];then - error_handler 10 "$1" - else - error_handler 12 "$1" - fi - fi - eval $LOGFS -} - -set_man_location() -{ - local location='' default_location='/usr/share/man/man1' - local man_paths=$(man --path 2>/dev/null) man_local='/usr/local/share/man' - local b_use_local=false - - if [[ -n "$man_paths" && -n $( grep $man_local <<< "$man_paths" ) ]];then - b_use_local=true - fi - - # for distro installs, existing inxi man manual installs, do nothing - if [[ -f $default_location/inxi.1.gz ]];then - location=$default_location - else - if [[ $b_use_local == 'true' ]];then - if [[ ! -d $man_local/man1 ]];then - mkdir $man_local/man1 - fi - location="$man_local/man1" - fi -# print_screen_output "Updating man page location to man1." -# mv -f /usr/share/man/man1/inxi.1.gz /usr/local/share/man/man1/inxi.1.gz -# if type -p mandb &>/dev/null;then -# exec $( type -p mandb ) -q -# fi - fi - - if [[ -z "$location" ]];then - location=$default_location - fi - - echo $location +# arg: 1 - command to turn into an array; 2 - optional: splitter +# 3 - optionsl, strip and clean data +# similar to reader() except this creates an array of data +# by lines from the command arg +sub grabber { + eval $start if $b_log; + my ($cmd,$split,$strip) = @_; + $split ||= "\n"; + my @rows = split /$split/, qx($cmd); + if ($strip && @rows){ + @rows = grep {/^\s*[^#]/} @rows; + @rows = map {s/^\s+|\s+$//g; $_} @rows if @rows; + } + eval $end if $b_log; + return @rows; } -######################################################################## -#### OUTPUT -######################################################################## +# args: 1 - string value to glob +sub globber { + eval $start if $b_log; + my @files = <$_[0]>; + eval $end if $b_log; + return @files; +} +# gets array ref, which may be undefined, plus join string +# this helps avoid debugger print errors when we are printing arrays +# which we don't know are defined or not null. +# args: 1 - array ref; 2 - join string; 3 - default value, optional +sub joiner { + my ($ref,$join,$default) = @_; + my @arr = @$ref; + $default ||= ''; + my $string = ''; + foreach (@arr){ + if (defined $_){ + $string .= $_ . $join; + } + else { + $string .= $default . $join; + } + } + return $string; +} -#### ------------------------------------------------------------------- -#### FILTERS -#### ------------------------------------------------------------------- +# returns array of: +# 0 - match string; 1 - search number; 2 - version string; 3 - Print name +# 4 - console 0/1; 5 - 0/1 exit version loop at first iteration; +# 6 - 0/1 write to stderr +# arg: 1 - program lower case name +sub program_values { + my ($app) = @_; + my (@client_data); + my %data = ( + ## Clients + 'bitchx' => ['bitchx',2,'','BitchX',1,0,0],# special + 'finch' => ['finch',2,'-v','Finch',1,1,0], + 'gaim' => ['[0-9.]+',2,'-v','Gaim',0,1,0], + 'ircii' => ['[0-9.]+',3,'-v','ircII',1,1,0], + 'irssi' => ['irssi',2,'-v','Irssi',1,1,0], + 'irssi-text' => ['irssi',2,'-v','Irssi',1,1,0], + 'konversation' => ['konversation',2,'-v','Konversation',0,0,0], + 'kopete' => ['Kopete',2,'-v','Kopete',0,0,0], + 'kvirc' => ['[0-9.]+',2,'-v','KVIrc',0,0,1], # special + 'pidgin' => ['[0-9.]+',2,'-v','Pidgin',0,1,0], + 'quassel' => ['',1,'-v','Quassel [M]',0,0,0], # special + 'quasselclient' => ['',1,'-v','Quassel',0,0,0],# special + 'quasselcore' => ['',1,'-v','Quassel (core)',0,0,0],# special + 'gribble' => ['^Supybot',2,'--version','Gribble',1,0,0],# special + 'limnoria' => ['^Supybot',2,'--version','Limnoria',1,0,0],# special + 'supybot' => ['^Supybot',2,'--version','Supybot',1,0,0],# special + 'weechat' => ['[0-9.]+',1,'-v','WeeChat',1,0,0], + 'weechat-curses' => ['[0-9.]+',1,'-v','WeeChat',1,0,0], + 'xchat-gnome' => ['[0-9.]+',2,'-v','X-Chat-Gnome',1,1,0], + 'xchat' => ['[0-9.]+',2,'-v','X-Chat',1,1,0], + ## Desktops + 'afterstep' => ['^afterstep',3,'--version','AfterStep',0,1,0], + 'awesome' => ['^awesome',2,'--version','Awesome',0,1,0], + 'blackbox' => ['^Blackbox',2,'--version','Blackbox',0,1,0], + 'budgie' => ['^budgie-desktop',2,'--version','Budgie',0,1,0], + 'cinnamon' => ['^cinnamon',2,'--version','Cinnamon',0,1,0], + 'dwm' => ['^dwm',1,'-v','dwm',0,1,1], + 'fluxbox' => ['^fluxbox',2,'--version','Fluxbox',0,1,0], + 'fvwm' => ['^fvwm',2,'--version','FVWM',0,0,1], + # command: fvwm + 'fvwm-crystal' => ['^fvwm',2,'--version','FVWM-Crystal',0,0,0], + 'gnome-about' => ['gnome',3,'--version','Gnome',0,1,0], + 'gnome-shell' => ['gnome',3,'--version','Gnome',0,1,0], + 'herbstluftwm' => ['^herbstluftwm',-1,'--version','herbstluftwm',0,1,0], + 'jwm' => ['^jwm',2,'--version','JWM',0,1,0], + # i3 version 4.13 (2016-11-08) © 2009 Michael Stapelberg and contributors + 'i3' => ['^i3',3,'--version','i3',0,1,0], + 'icewm' => ['^icewm',2,'--version','IceWM',0,1,0], + 'kded' => ['^KDE Development Platform:',4,'--version','KDE',0,1,0], + 'kded1' => ['^KDE Development Platform:',4,'--version','KDE',0,1,0], + 'kded2' => ['^KDE Development Platform:',4,'--version','KDE',0,1,0], + 'kded3' => ['^KDE Development Platform:',4,'--version','KDE',0,1,0], + 'kded4' => ['^KDE Development Platform:',4,'--version','KDE',0,1,0], + # command: lxqt-about + 'lxqt' => ['^lxqt-about',2,'--version','LXQT',0,1,0], + 'mate-about' => ['^MATE[[:space:]]DESKTOP',-1,'--version','MATE',0,1,0], + # note, mate-session when launched with full path returns full path in version string + 'mate-session' => ['mate-session',-1,'--version','MATE',0,1,0], + 'openbox' => ['^openboxt',2,'--version','Openbox',0,1,0], + 'pekwm' => ['^pekwm',3,'--version','pekwm',0,1,0], + 'plasmashell' => ['^plasmashell',2,'--version','KDE Plasma',0,1,0], + 'qtdiag' => ['^qt',2,'--version','Qt',0,1,0], + 'sawfish' => ['^sawfish',3,'--version','Sawfish',0,1,0], + 'scrotwm' => ['^welcome.*scrotwm',4,'-v','Scrotwm',0,1,1], + 'spectrwm' => ['^spectrwm.*welcome.*spectrwm',5,'-v','Spectrwm',0,1,0], + 'unity' => ['^unity',2,'--version','Unity',0,1,0], + 'wm2' => ['^wm2',-1,'--version','WM2',0,1,0], + 'wmaker' => ['^Window[[:space:]]*Maker',-1,'--version','WindowMaker',0,1,0], + 'wmii' => ['^wmii',1,'--version','wmii',0,1,0], # note: in debian, wmii is wmii3 + 'wmii2' => ['^wmii2',1,'--version','wmii2',0,1,0], + 'xfce4-panel' => ['^xfce4-panel',2,'--version','Xfce',0,1,0], + 'xfce5-panel' => ['^xfce5-panel',2,'--version','Xfce',0,1,0], + 'xfdesktop' => ['xfdesktop[[:space:]]version',5,'--version','Xfce',0,1,0], + # command: xfdesktop + 'xfdesktop-toolkit' => ['Built[[:space:]]with[[:space:]]GTK',4,'--version','Gtk',0,1,0], + ## Shells + 'bash' => ['^GNU[[:space:]]bash,[[:space:]]version',4,'--version','Bash',1,0,0], + 'csh' => ['^tcsh',2,'--version','csh',1,0,0], + 'dash' => ['dash',3,'--version','Dash',1,0,0], # no version, uses dpkg query, sigh + # ksh/lksh/mksh/pdksh can't be handled with version but we'll use the search string to + # trigger version return and tests + 'ksh' => ['ksh',5,'-v','ksh',1,0,0], + 'lksh' => ['ksh',5,'-v','lksh',1,0,0], + 'loksh' => ['ksh',5,'-v','lksh',1,0,0], + 'mksh' => ['ksh',5,'-v','mksh',1,0,0], + 'pdksh' => ['ksh',5,'-v','pdksh',1,0,0], + 'tcsh' => ['^tcsh',2,'--version','tcsh',1,0,0], + 'zsh' => ['^zsh',2,'--version','zsh',1,0,0], + ## Tools + 'clang' => ['clang',3,'--version','Clang',1,0,0], + 'gcc' => ['^gcc',3,'--version','GCC',1,0,0], + 'gcc-apple' => ['Apple[[:space:]]LLVM',2,'--version','LLVM',1,0,0], + 'sudo' => ['^Sudo',3,'--version','Sudo',1,0,0], + ); + if ( defined $data{$app} ){ + my $ref = $data{$app}; + @client_data = @$ref; + } + #my $debug = main::Dumper \@client_data; + main::log_data('dump',"Client Data",\@client_data) if $b_log; + return @client_data; +} +# args: 1 - desktop/app command for --version; 2 - search string; +# 3 - space print number; 4 - [optional] version arg: -v, version, etc +# 5 - [optional] exit first find 0/1; 6 - [optional] 0/1 stderr output +sub program_version { + eval $start if $b_log; + my ($app, $search, $num,$version,$exit,$b_stderr) = @_; + my ($cmd,$line,$output); + my $version_nu = ''; + my $count = 0; + $exit ||= 100; # basically don't exit ever + $version ||= '--version'; + # adjust to array index, not human readable + $num-- if (defined $num && $num > 0); + # ksh: Version JM 93t+ 2010--03-05 + # mksh: @(#)MIRBSD KSH R56 2018/03/09 + # loksh: @(#)PD KSH v5.2.14 99/07/13.2 + # --version opens a new ksh, sigh... This so far does not work + # because the ENV variable is not visible internally + if ($search eq 'ksh'){ + if ( $ENV{'KSH_VERSION'} ){ + my @temp = split /\s+/, $ENV{'KSH_VERSION'}; + if ($temp[2]){ + $temp[2] =~ s/^v//i; # trim off leading v + log_data('data',"Program *ksh array: @temp version: $temp[2]") if $b_log; + return $temp[2]; + } + } + return 0; + } + # konvi in particular doesn't like using $ENV{'PATH'} as set, so we need + # to always assign the full path if it hasn't already been done + if ( $app !~ /^\// ){ + if (my $program = check_program($app) ){ + $app = $program; + } + else { + log_data('data',"$app not found in path."); + return 0; + } + } + # note, some wm/apps send version info to stderr instead of stdout + if ( $b_stderr ) { + $cmd = "$app $version 2>&1"; + } +# elsif ( $app eq 'csh' ){ +# $app = 'tcsh'; +# } + # quick debian/buntu hack until I find a universal way to get version for these + elsif ( $app eq 'dash' ){ + $cmd = "dpkg -l $app 2>/dev/null"; + } + else { + $cmd = "$app $version 2>/dev/null"; + } + log_data('data',"version: $version num: $num search: $search command: $cmd") if $b_log; + $output = qx($cmd); + # print "$cmd : $output\n"; + # sample: dwm-5.8.2, ©.. etc, why no space? who knows. Also get rid of v in number string + # xfce, and other, output has , in it, so dump all commas and parentheses + if ($output){ + open my $ch, '<', \$output or error_handler('open-data',"$cmd", "$!"); + while (<$ch>){ + #chomp; + last if $count > $exit; + if ( $_ =~ /$search/i ) { + $_ = trimmer($_); + # print "$_ ::$num\n"; + my @data = split /\s+/, $_; + $version_nu = $data[$num]; + last if ! defined $version_nu; + # some distros add their distro name before the version data, which + # breaks version detection. A quick fix attempt is to just add 1 to $num + # to get the next value. + $version_nu = $data[$num+1] if $data[$num+1] && $version_nu =~ /version/i; + $version_nu =~ s/(\([^)]+\)|,|dwm-|wmii2-|wmii-|v|V|\||\(|\))//g if $version_nu; + # print "$version_nu\n"; + last; + } + $count++; + } + close $ch if $ch; + } + log_data('data',"Program version: $version_nu") if $b_log; + eval $end if $b_log; + return $version_nu; +} +# print program_version('bash', 'bash', 4) . "\n"; + +# arg: 1 - full file path, returns array of file lines. +# 2 - optionsl, strip and clean data +# note: chomp has to chomp the entire action, not just <$fh> +sub reader { + eval $start if $b_log; + my ($file,$strip) = @_; + return if ! $file; + open( my $fh, '<', $file ) or error_handler('open', $file, $!); + chomp(my @rows = <$fh>); + if ($strip && @rows){ + @rows = grep {/^\s*[^#]/} @rows; + @rows = map {s/^\s+|\s+$//g; $_} @rows if @rows; + } + eval $end if $b_log; + return @rows; +} +# args: 1 - the file to create if not exists +sub toucher { + my $file = shift; + if ( ! -e $file ){ + open( my $fh, '>', $file ) or error_handler('create', $file, $!); + } +} -# this removes newline and pipes. -# args: $1 - string to clean -remove_erroneous_chars() -{ - eval $LOGFS - ## RS is input record separator - ## gsub is substitute; - gawk ' - BEGIN { - RS="" - } - { - gsub(/\n$/,"") ## (newline; end of string) with (nothing) - gsub(/\n/," "); ## (newline) with (space) - gsub(/^ *| *$/, "") ## (pipe char) with (nothing) - gsub(/ +/, " ") ## ( +) with (space) - gsub(/ [ ]+/, " ") ## ([ ]+) with (space) - gsub(/^ +| +$/, "") ## (pipe char) with (nothing) - printf $0 - }' "$1" ## prints (returns) cleaned input - eval $LOGFE -} -## note: this is now running inside each gawk sequence directly to avoid exiting gawk -## looping in bash through arrays, then re-entering gawk to clean up, then writing back to array -## in bash. For now I'll leave this here because there's still some interesting stuff to get re methods -# Enforce boilerplate and buzzword filters -# args: $1 - BAN_LIST_NORMAL/BAN_LIST_CPU; $2 - string to sanitize -sanitize_characters() -{ - eval $LOGFS - # Cannot use strong quotes to unquote a string with pipes in it! - # bash will interpret the |'s as usual and try to run a subshell! - # Using weak quotes instead, or use '"..."' - echo "$2" | gawk " - BEGIN { - IGNORECASE=1 - } - { - gsub(/${!1}/,\"\") - gsub(/ [ ]+/,\" \") ## ([ ]+) with (space) - gsub(/^ +| +$/,\"\") ## (pipe char) with (nothing) - print ## prints (returns) cleaned input - }" - eval $LOGFE +# calling it trimmer to avoid conflicts with existing trim stuff +# arg: 1 - string to be right left trimmed. Also slices off \n so no chomp needed +# this thing is super fast, no need to log its times etc, 0.0001 seconds or less +sub trimmer { + #eval $start if $b_log; + my ($str) = @_; + $str =~ s/^\s+|\s+$|\n$//g; + #eval $end if $b_log; + return $str; +} +# args: 1 - hash +# send array, assign to hash, return array, uniq values only. +sub uniq { + my %seen; + grep !$seen{$_}++, @_; +} +# arg: 1 file full path to write to; 2 - arrayof data to write. +# note: turning off strict refs so we can pass it a scalar or an array reference. +sub writer { + my ($path, $ref_content) = @_; + my ($content); + no strict 'refs'; + # print Dumper $ref_content, "\n"; + if (ref $ref_content eq 'ARRAY'){ + $content = join "\n", @$ref_content or die "failed with error $!"; + } + else { + $content = scalar $ref_content; + } + open(my $fh, '>', $path) or error_handler('open',"$path", "$!"); + print $fh $content; + close $fh; } #### ------------------------------------------------------------------- -#### PRINT -#### ------------------------------------------------------------------- +#### UPDATER +##### ------------------------------------------------------------------- + +# arg 1: type to return +sub get_defaults { + my ($type) = @_; + my %defaults = ( + 'ftp-upload' => 'ftp.techpatterns.com/incoming', + 'inxi-branch-1' => 'https://github.com/smxi/inxi/raw/one/', + 'inxi-branch-2' => 'https://github.com/smxi/inxi/raw/two/', + 'inxi-dev' => 'https://smxi.org/in/', + 'inxi-main' => 'https://github.com/smxi/inxi/raw/master/', + 'inxi-pinxi' => 'https://github.com/smxi/inxi/raw/inxi-perl/', + 'inxi-man' => "https://smxi.org/in/$self_name.1.gz", + 'inxi-man-gh' => "https://github.com/smxi/inxi/raw/master/$self_name.1", + 'pinxi-man' => "https://smxi.org/in/$self_name.1.gz", + 'pinxi-man-gh' => "https://github.com/smxi/inxi/raw/inxi-perl/$self_name.1", + ); + if ( exists $defaults{$type}){ + return $defaults{$type}; + } + else { + error_handler('bad-arg-int', $type); + } +} +# args: 1 - download url, not including file name; 2 - string to print out +# 3 - update type option +# note that 1 must end in / to properly construct the url path +sub update_me { + eval $start if $b_log; + my ( $self_download, $download_id ) = @_; + my $downloader_error=1; + my $file_contents=''; + my $output = ''; + $self_path =~ s/\/$//; # dirname sometimes ends with /, sometimes not + $self_download =~ s/\/$//; # dirname sometimes ends with /, sometimes not + my $full_self_path = "$self_path/$self_name"; + + if ( $b_irc ){ + error_handler('not-in-irc', "-U/--update" ) + } + if ( ! -w $full_self_path ){ + error_handler('not-writable', "$self_name", ''); + } + $output .= "Starting $self_name self updater.\n"; + $output .= "Using $dl{'dl'} as downloader.\n"; + $output .= "Currently running $self_name version number: $self_version\n"; + $output .= "Current version patch number: $self_patch\n"; + $output .= "Current version release date: $self_date\n"; + $output .= "Updating $self_name in $self_path using $download_id as download source...\n"; + print $output; + $output = ''; + $self_download = "$self_download/$self_name"; + $file_contents = download_file('stdout', $self_download); + + # then do the actual download + if ( $file_contents ){ + # make sure the whole file got downloaded and is in the variable + if ( $file_contents =~ /###\*\*EOF\*\*###/ ){ + open(my $fh, '>', $full_self_path); + print $fh $file_contents or error_handler('write', "$full_self_path", "$!" ); + close $fh; + qx( chmod +x '$self_path/$self_name' ); + set_version_data(); + $output .= "Successfully updated to $download_id version: $self_version\n"; + $output .= "New $download_id version patch number: $self_patch\n"; + $output .= "New $download_id version release date: $self_date\n"; + $output .= "To run the new version, just start $self_name again.\n"; + $output .= "$line3\n"; + $output .= "Starting download of man page file now.\n"; + print $output; + $output = ''; + if ($b_man){ + update_man($download_id); + } + else { + print "Skipping man download because branch version is being used.\n"; + } + exit 1; + } + else { + error_handler('file-corrupt', "$self_name"); + } + } + # now run the error handlers on any downloader failure + else { + error_handler('download-error', $self_download, $download_id); + } + eval $end if $b_log; +} -# args: $1 - string to strip color code characters out of -# returns count of string length minus colors -# note; this cleanup may not be working on bsd sed -calculate_line_length() -{ - local string=$1 - # ansi: [1;34m irc: \x0312 - # note: using special trick for bsd sed, tr - NOTE irc sed must use " double quote - string=$( sed -e 's/'$ESC'\[[0-9]\{1,2\}\(;[0-9]\{1,2\}\)\{0,2\}m//g' -e "s/\\\x0[0-9]\{1,3\}//g" <<< $string ) - #echo $string - LINE_LENGTH=${#string} - # echo ${#string} -} - -## this handles all verbose line construction with indentation/line starter -## args: $1 - null (, actually: " ") or line starter; $2 - line content -create_print_line() -{ - eval $LOGFS - # convoluted, yes, but it works to trim spaces off end - local line=${2%${2##*[![:space:]]}} - printf "${C1}%-${INDENT}s${C2} %s" "$1" "$line${CN}" - eval $LOGFE +sub update_man { + my ($download_id) = @_; + my $man_file_location=set_man_location(); + my $man_file_path="$man_file_location/$self_name.1" ; + my ($man_file_url,$output) = ('',''); + + my $b_downloaded = 0; + if ( ! -d $man_file_location ){ + print "The required man directory was not detected on your system.\n"; + print "Unable to continue: $man_file_location\n"; + return 0; + } + if ( ! -w $man_file_location ){ + print "Cannot write to $man_file_location! Are you root?\n"; + print "Unable to continue: $man_file_location\n"; + return 0; + } + if ( -f "/usr/share/man/man8/inxi.8.gz" ){ + print "Updating man page location to man1.\n"; + rename "/usr/share/man/man8/inxi.8.gz", "$man_file_location/inxi.1.gz"; + if ( check_program('mandb') ){ + system( 'mandb' ); + } + } + # first choice is inxi.1/pinxi.1 from gh, second gz from smxi.org + if ( $download_id ne 'dev server' && (my $program = check_program('gzip'))){ + $man_file_url=get_defaults($self_name . '-man-gh'); + print "Downloading Man page file...\n"; + $b_downloaded = download_file('file', $man_file_url, $man_file_path); + if ($b_downloaded){ + print "Download successful. Compressing file...\n"; + system("$program -9 -f $man_file_path > $man_file_path.gz"); + my $err = $?; + if ($err > 0){ + print "Oh no! Something went wrong compressing the manfile:\n"; + print "Local path: $man_file_path Error: $err\n"; + } + else { + print "Download and install of man page successful.\nCheck to make sure it works: man $self_name\n"; + } + } + } + else { + $man_file_url = get_defaults($self_name . '-man'); + # used to use spider tests, but only wget supports that, so no need + print "Downloading Man page file gz...\n"; + $man_file_path .= '.gz'; + # returns perl, 1 for true, 0 for false, even when using shell tool returns + $b_downloaded = download_file('file', $man_file_url, $man_file_path ); + if ($b_downloaded) { + print "Download and install of man page successful.\nCheck to make sure it works: man $self_name\n"; + } + } + if ( !$b_downloaded ){ + print "Oh no! Something went wrong downloading the Man file at:\n$man_file_url\n"; + print "Try -U with --dbg 1 for more information on the failure.\n"; + } } -# inxi speaks through here. When run by Konversation script alias mode, uses DCOP -# for dcop to work, must use 'say' operator, AND colors must be evaluated by echo -e -# note: dcop does not seem able to handle \n so that's being stripped out and replaced with space. -print_screen_output() -{ - eval $LOGFS - # the double quotes are needed to avoid losing whitespace in data when certain output types are used - # trim off whitespace at end - local print_data="$( echo -e "$1" )" - - # just using basic debugger stuff so you can tell which thing is printing out the data. This - # should help debug kde 4 konvi issues when that is released into sid, we'll see. Turning off - # the redundant debugger output which as far as I can tell does exactly nothing to help debugging. - if [[ $DEBUG -gt 5 ]];then - if [[ $KONVI -eq 1 ]];then - # konvi doesn't seem to like \n characters, it just prints them literally - # print_data="$( tr '\n' ' ' <<< "$print_data" )" - # dcop "$DCPORT" "$DCOPOBJ" say "$DCSERVER" "$DCTARGET" "konvi='$KONVI' saying : '$print_data'" - print_data="KP-$KONVI: $print_data" - elif [[ $KONVI -eq 2 ]];then - # echo "konvi='$KONVI' saying : '$print_data'" - print_data="KP-$KONVI: $print_data" - else - # echo "printing out: '$print_data'" - print_data="P: $print_data" - fi - fi - if [[ $KONVI -eq 1 && $B_DCOP == 'true' ]]; then ## dcop Konversation (<= 1.1 (qt3)) - # konvi doesn't seem to like \n characters, it just prints them literally - $print_data="$( tr '\n' ' ' <<< "$print_data" )" - dcop "$DCPORT" "$DCOPOBJ" say "$DCSERVER" "$DCTARGET" "$print_data" - elif [[ $KONVI -eq 3 && $B_QDBUS == 'true' ]]; then ## dbus Konversation (> 1.2 (qt4)) - qdbus org.kde.konversation /irc say "$DCSERVER" "$DCTARGET" "$print_data" -# elif [[ $IRC_CLIENT == 'X-Chat' ]]; then -# qdbus org.xchat.service print "$print_data\n" - else - # the -n is needed to avoid double spacing of output in terminal - echo -ne "$print_data\n" - fi - eval $LOGFE -} - -# uses $TERM_COLUMNS to set width using $COLS_MAX as max width -# IMPORTANT: Must come after print_screen_output for debugging purposes -# IMPORTANT: minimize use of subshells here or the output is too slow -# IMPORTANT: each text chunk must be a continuous line, no line breaks. For anyone who uses a -# code editor that can't do visual (not hard coded) line wrapping, upgrade to one that can. -# args: $1 - 0 1 2 3 4 for indentation level; $2 -line starter, like -m; $3 - content of block. -print_lines_basic() -{ - local line_width=$COLS_MAX - local print_string='' indent_inner='' indent_full='' indent_x='' - local indent_working='' indent_working_full='' - local line_starter='' line_1_starter='' line_x_starter='' - # note: to create a padded string below - local fake_string=' ' temp_count='' line_count='' spacer='' - local indent_main=6 indent_x='' b_indent_x='true' - - case $1 in - # for no options, start at left edge - 0) indent_full=0 - line_1_starter='' - line_x_starter='' - b_indent_x='false' - ;; - 1) indent_full=$indent_main - temp_count=${#2} - if [[ $temp_count -le $indent_full ]];then - indent_working=$indent_full - else - indent_working=$temp_count #$(( $temp_count + 1 )) - fi - line_1_starter="$( sed -e :a -e "s/^.\{1,$indent_working\}$/& /;ta" <<< $2 )" - ;; - # first left pad 2 and 3, then right pad them - 2) indent_full=$(( $indent_main + 6 )) - indent_inner=3 - temp_count=${#2} - if [[ $temp_count -le $indent_inner ]];then - indent_working=$indent_inner - #indent_working_full=$indent_full - else - indent_working=$(( $temp_count + 1 )) - #indent_working_full=$(( $indent_full - $indent_inner - 1 )) - fi - line_1_starter="$( sed -e :a -e "s/^.\{1,$indent_working\}$/& /;ta" <<< $2 )" - line_1_starter="$( sed -e :a -e "s/^.\{1,$indent_full\}$/ &/;ta" <<< "$line_1_starter" )" - ;; - 3) indent_full=$(( $indent_main + 8 )) - indent_inner=3 - temp_count=${#2} - if [[ $temp_count -le $indent_inner ]];then - indent_working=$indent_inner - else - indent_working=$(( $temp_count + 1 )) - fi - line_1_starter="$( sed -e :a -e "s/^.\{1,$indent_working\}$/& /;ta" <<< $2 )" - line_1_starter="$( sed -e :a -e "s/^.\{1,$indent_full\}$/ &/;ta" <<< "$line_1_starter" )" - ;; - # for long options - 4) indent_full=$(( $indent_main + 8 )) - temp_count=${#2} - if [[ $temp_count -lt $indent_full ]];then - indent_working=$indent_full - else - indent_working=$temp_count #$(( $temp_count + 1 )) - fi - line_1_starter="$( sed -e :a -e "s/^.\{1,$indent_working\}$/& /;ta" <<< $2 )" - ;; - esac - - if [[ $b_indent_x == 'true' ]];then - indent_x=$(( $indent_full + 1 )) - line_x_starter="$(printf "%${indent_x}s" '')" - fi - - line_count=$(( $line_width - $indent_full )) - - # bash loop is slow, only run this if required - if [[ ${#3} -gt $line_count ]];then - for word in $3 - do - temp_string="$print_string$spacer$word" - spacer=' ' - if [[ ${#temp_string} -lt $line_count ]];then - print_string=$temp_string # lose any white space start/end - # echo -n $(( $line_width - $indent_full )) - else - if [[ -n $line_1_starter ]];then - line_starter="$line_1_starter" - line_1_starter='' - else - line_starter="$line_x_starter" - fi - # clean up forced connections, ie, stuff we don't want wrapping - print_string=${print_string//\^/ } - print_screen_output "$line_starter$print_string" - print_string="$word$spacer" # needed to handle second word on new line - temp_string='' - spacer='' - fi - done - else - # echo no loop - print_string=$3 - fi - # print anything left over - if [[ -n $print_string ]];then - if [[ -n $line_1_starter ]];then - line_starter="$line_1_starter" - line_1_starter='' - else - line_starter="$line_x_starter" - fi - print_string=${print_string//\^/ } - print_screen_output "$line_starter$print_string" - fi -} -# print_lines_basic '1' '-m' 'let us teest this string and lots more and stuff and more stuff and x is wy and z is x and fred is dead and gus is alive an yes we have to go now' -# print_lines_basic '2' '7' 'and its substring this string and lots more and stuff and more stuff and x is wy and z is x and fred is dead and gus is alive an yes we have to go now' -# print_lines_basic '2' '12' 'and its sss substring' -# print_lines_basic '3' '12' 'and its sss substring this string and lots more and stuff and more stuff and x is wy and z is x and fred is dead and gus is alive an yes we have to go now' -# exit +sub set_man_location { + my $location=''; + my $default_location='/usr/share/man/man1'; + my $man_paths=qx(man --path 2>/dev/null); + my $man_local='/usr/local/share/man'; + my $b_use_local=0; + if ( $man_paths && $man_paths =~ /$man_local/ ){ + $b_use_local=1; + } + # for distro installs + if ( -f "$default_location/inxi.1.gz" ){ + $location=$default_location; + } + else { + if ( $b_use_local ){ + if ( ! -d "$man_local/man1" ){ + mkdir "$man_local/man1"; + } + $location="$man_local/man1"; + } + } + if ( ! $location ){ + $location=$default_location; + } + return $location; +} + +# update for updater output version info +# note, this is only now used for self updater function so it can get +# the values from the UPDATED file, NOT the running program! +sub set_version_data { + open (my $fh, '<', "$self_path/$self_name"); + while( my $row = <$fh>){ + chomp $row; + $row =~ s/'|;//g; + if ($row =~ /^my \$self_name/ ){ + $self_name = (split /=/, $row)[1]; + } + elsif ($row =~ /^my \$self_version/ ){ + $self_version = (split /=/, $row)[1]; + } + elsif ($row =~ /^my \$self_date/ ){ + $self_date = (split /=/, $row)[1]; + } + elsif ($row =~ /^my \$self_patch/ ){ + $self_patch = (split /=/, $row)[1]; + } + elsif ($row =~ /^## END INXI INFO/){ + last; + } + } + close $fh; +} ######################################################################## -#### OPTION AND VERSION HANDLERS +#### OPTIONS HANDLER / VERSION ######################################################################## -# Get the parameters. Note: standard options should be lower case, advanced or testing, upper -# args: $1 - full script startup args: $@ -get_parameters() -{ - eval $LOGFS - local opt='' downloader_test='' debug_data_type='' weather_flag='wW:' - local use_short='true' # this is needed to trigger short output, every v/d/F/line trigger sets this false - +sub get_options{ + eval $start if $b_log; + my (@args) = @_; + $show{'short'} = 1; + my ($b_downloader,$b_help,$b_no_man,$b_no_man_force,$b_recommends,$b_updater,$b_version, + $b_use_man,$self_download, $download_id); + GetOptions ( + 'A|audio' => sub { + $show{'short'} = 0; + $show{'audio'} = 1;}, + 'b|basic' => sub { + $show{'short'} = 0; + $show{'battery'} = 1; + $show{'cpu-basic'} = 1; + $show{'raid-basic'} = 1; + $show{'disk-total'} = 1; + $show{'graphic'} = 1; + $show{'info'} = 1; + $show{'machine'} = 1; + $show{'network'} = 1; + $show{'system'} = 1;}, + 'B|battery' => sub { + $show{'short'} = 0; + $show{'battery'} = 1; + $show{'battery-forced'} = 1; }, + 'c|color:i' => sub { + my ($opt,$arg) = @_; + if ( $arg >= 0 && $arg < get_color_scheme('count') ){ + set_color_scheme($arg); + } + elsif ( $arg >= 94 && $arg <= 99 ){ + $colors{'selector'} = $arg; + } + else { + error_handler('bad-arg', $opt, $arg); + } }, + 'C|cpu' => sub { + $show{'short'} = 0; + $show{'cpu'} = 1; }, + 'd|disk-full|optical' => sub { + $show{'short'} = 0; + $show{'disk'} = 1; + $show{'optical'} = 1; }, + 'D' => sub { + $show{'short'} = 0; + $show{'disk'} = 1; }, + 'f|flags|flag' => sub { + $show{'short'} = 0; + $show{'cpu'} = 1; + $show{'cpu-flag'} = 1; }, + 'F|full' => sub { + $show{'short'} = 0; + $show{'audio'} = 1; + $show{'battery'} = 1; + $show{'cpu'} = 1; + $show{'disk'} = 1; + $show{'graphic'} = 1; + $show{'info'} = 1; + $show{'machine'} = 1; + $show{'network'} = 1; + $show{'network-advanced'} = 1; + $show{'partition'} = 1; + $show{'raid'} = 1; + $show{'sensor'} = 1; + $show{'system'} = 1; }, + 'G|graphics|graphic' => sub { + $show{'short'} = 0; + $show{'graphic'} = 1; }, + 'i|ip' => sub { + $show{'short'} = 0; + $show{'ip'} = 1; + $show{'network'} = 1; + $show{'network-advanced'} = 1; + $b_downloader = 1 if ! check_program('dig');}, + 'I|info' => sub { + $show{'short'} = 0; + $show{'info'} = 1; }, + 'l|labels|label' => sub { + $show{'short'} = 0; + $show{'label'} = 1; + $show{'partition'} = 1; }, + 'limit:i' => sub { + my ($opt,$arg) = @_; + if ($arg != 0){ + $limit = $arg; + } + else { + error_handler('bad-arg',$opt,$arg); + } }, + 'm|memory' => sub { + $show{'short'} = 0; + $show{'ram'} = 1; }, + 'M|machine' => sub { + $show{'short'} = 0; + $show{'machine'} = 1; }, + 'n|network-advanced' => sub { + $show{'short'} = 0; + $show{'network'} = 1; + $show{'network-advanced'} = 1; }, + 'N|network' => sub { + $show{'short'} = 0; + $show{'network'} = 1; }, + 'o|unmounted' => sub { + $show{'short'} = 0; + $show{'unmounted'} = 1; }, + 'p|partition-full' => sub { + $show{'short'} = 0; + $show{'partition'} = 0; + $show{'partition-full'} = 1; }, + 'P|partitions|partition' => sub { + $show{'short'} = 0; + $show{'partition'} = 1; }, + 'r|repos|repo' => sub { + $show{'short'} = 0; + $show{'repo'} = 1; }, + 'R|raid' => sub { + $show{'short'} = 0; + $show{'raid'} = 1; + $show{'raid-forced'} = 1; }, + 's|sensors|sensor' => sub { + $show{'short'} = 0; + $show{'sensor'} = 1; }, + 'sleep:s' => sub { + my ($opt,$arg) = @_; + $arg ||= 0; + if ($arg >= 0){ + $cpu_sleep = $arg; + } + else { + error_handler('bad-arg',$opt,$arg); + } }, + 'slots|slot' => sub { + $show{'short'} = 0; + $show{'slot'} = 1; }, + 'S|system' => sub { + $show{'short'} = 0; + $show{'system'} = 1; }, + 't|processes|process:s' => sub { + my ($opt,$arg) = @_; + $show{'short'} = 0; + $arg ||= 'cm'; + my $num = $arg; + $num =~ s/^[cm]+// if $num; + if ( $arg =~ /^([cm]+)([0-9]+)?$/ && (!$num || $num =~ /^\d+/) ){ + $show{'process'} = 1; + if ($arg =~ /c/){ + $show{'ps-cpu'} = 1; + } + if ($arg =~ /m/){ + $show{'ps-mem'} = 1; + } + $ps_count = $num if $num; + } + else { + error_handler('bad-arg',$opt,$arg); + } }, + 'usb' => sub { + $show{'short'} = 0; + $show{'usb'} = 1; }, + 'u|uuid' => sub { + $show{'short'} = 0; + $show{'partition'} = 1; + $show{'uuid'} = 1; }, + 'v|verbosity:i' => sub { + my ($opt,$arg) = @_; + $show{'short'} = 0; + if ( $arg =~ /^[0-8]$/ ){ + if ($arg == 0 ){ + $show{'short'} = 1; + } + if ($arg >= 1 ){ + $show{'cpu-basic'} = 1; + $show{'disk-total'} = 1; + $show{'graphic'} = 1; + $show{'info'} = 1; + $show{'system'} = 1; + } + if ($arg >= 2 ){ + $show{'battery'} = 1; + $show{'disk-basic'} = 1; + $show{'raid-basic'} = 1; + $show{'machine'} = 1; + $show{'network'} = 1; + } + if ($arg >= 3 ){ + $show{'network-advanced'} = 1; + $show{'cpu'} = 1; + $extra = 1; + } + if ($arg >= 4 ){ + $show{'disk'} = 1; + $show{'partition'} = 1; + } + if ($arg >= 5 ){ + $show{'audio'} = 1; + $show{'ram'} = 1; + $show{'label'} = 1; + $show{'optical-basic'} = 1; + $show{'ram'} = 1; + $show{'raid'} = 1; + $show{'sensor'} = 1; + $show{'uuid'} = 1; + } + if ($arg >= 6 ){ + $show{'optical'} = 1; + $show{'partition-full'} = 1; + $show{'unmounted'} = 1; + $show{'usb'} = 1; + $extra = 2; + } + if ($arg >= 7 ){ + $b_downloader = 1 if ! check_program('dig'); + $show{'cpu-flag'} = 1; + $show{'ip'} = 1; + $show{'raid-forced'} = 1; + $extra = 3; + } + if ($arg >= 8 ){ + $b_downloader = 1; + $show{'slot'} = 1; + $show{'process'} = 1; + $show{'ps-cpu'} = 1; + $show{'ps-mem'} = 1; + $show{'repo'} = 1; + #$show{'weather'} = 1; + } + } + else { + error_handler('bad-arg',$opt,$arg); + } }, + 'w|weather' => sub { + my ($opt) = @_; + $show{'short'} = 0; + $b_downloader = 1; + if ( $b_weather ){ + $show{'weather'} = 1; + } + else { + error_handler('distro-block', $opt); + } }, + 'W|weather-location:s' => sub { + my ($opt,$arg) = @_; + $arg ||= ''; + $arg =~ s/\s//g; + $show{'short'} = 0; + $b_downloader = 1; + if ( $b_weather ){ + if ($arg){ + $show{'weather'} = 1; + $show{'weather-location'} = $arg; + } + else { + error_handler('bad-arg',$opt,$arg); + } + } + else { + error_handler('distro-block', $opt); + } }, + 'x|extra:i' => sub { + my ($opt,$arg) = @_; + if ($arg > 0){ + $extra = $arg; + } + else { + $extra++; + } }, + 'y|width:i' => sub { + my ($opt, $arg) = @_; + $arg = 2000 if defined $arg && $arg == -1; + if ( $arg =~ /\d/ && $arg >= 80 ){ + set_display_width($arg); + } + else { + error_handler('bad-arg', $opt, $arg); + } }, + 'z|filter' => sub { + $show{'filter'} = 1; }, + 'Z|filter-override' => sub { + $show{'filter-override'} = 1; }, + ## Start non data options + 'alt:i' => sub { + my ($opt,$arg) = @_; + if ($arg == 40) { + $dl{'tiny'} = 0; + $b_downloader = 1;} + elsif ($arg == 41) { + $dl{'curl'} = 0; + $b_downloader = 1;} + elsif ($arg == 42) { + $dl{'fetch'} = 0; + $b_downloader = 1;} + elsif ($arg == 43) { + $dl{'wget'} = 0; + $b_downloader = 1;} + elsif ($arg == 44) { + $dl{'curl'} = 0; + $dl{'fetch'} = 0; + $dl{'wget'} = 0; + $b_downloader = 1;} + else { + error_handler('bad-arg', $opt, $arg); + }}, + 'arm' => sub { + $b_arm = 1 }, + 'bsd:s' => sub { + my ($opt,$arg) = @_; + if ($arg =~ /^(darwin|dragonfly|freebsd|openbsd|netbsd)$/i){ + $bsd_type = lc($arg); + } + else { + error_handler('bad-arg', $opt, $arg); + } + }, + 'dbg:i' => sub { + my ($opt,$arg) = @_; + if ($arg > 0) { + $test[$arg] = 1; + } + else { + error_handler('bad-arg', $opt, $arg); + }}, + 'debug:i' => sub { + my ($opt,$arg) = @_; + if ($arg =~ /^[1-3]|1[0-2]|2[0-4]$/){ + $debug=$arg; + } + else { + error_handler('bad-arg', $opt, $arg); + } }, + 'display:s' => sub { + my ($opt,$arg) = @_; + if ($arg =~ /^:?([0-9]+)?$/){ + $display=$arg; + $display ||= ':0'; + $display = ":$display" if $display !~ /^:/; + $b_display = ($b_root) ? 0 : 1; + $b_force_display = 1; + $display_opt = "-display $display"; + } + else { + error_handler('bad-arg', $opt, $arg); + } }, + 'dmidecode' => sub { + $b_dmidecode_force = 1 }, + 'downloader:s' => sub { + my ($opt,$arg) = @_; + $arg = lc($arg); + if ($arg =~ /^(curl|fetch|ftp|perl|wget)$/){ + if ($arg eq 'perl' && (!check_module('HTTP::Tiny') || !check_module('IO::Socket::SSL') )){ + error_handler('missing-perl-downloader', $opt, $arg); + } + elsif ( !check_program($arg)) { + error_handler('missing-downloader', $opt, $arg); + } + else { + # this dumps all the other data and resets %dl for only the + # desired downloader. + $arg = set_perl_downloader($arg); + %dl = ('dl' => $arg, $arg => 1); + $b_downloader = 1; + } + } + else { + error_handler('bad-arg', $opt, $arg); + } }, + 'ftp:s' => sub { + my ($opt,$arg) = @_; + # pattern: ftp.x.x/x + if ($arg =~ /^ftp\..+\..+\/[^\/]+$/ ){ + $ftp_alt = $arg; + } + else { + error_handler('bad-arg', $opt, $arg); + }}, + 'h|help|?' => sub { + $b_help = 1; }, + 'host|hostname' => sub { + $show{'host'} = 1 }, + 'indent-min:i' => sub { + my ($opt,$arg) = @_; + if ($arg =~ /^\d+$/){ + $size{'indent-min'} = 1; + } + else { + error_handler('bad-arg', $opt, $arg); + }}, + 'irc' => sub { + $b_irc = 1; }, + 'man' => sub { + $b_use_man = 1; }, + 'output:s' => sub { + my ($opt,$arg) = @_; + if ($arg =~ /^(json|screen|xml)$/){ + if ($arg =~ /json|screen|xml/){ + $output_type = $arg; + } + else { + error_handler('option-feature-incomplete', $opt, $arg); + } + } + else { + error_handler('bad-arg', $opt, $arg); + }}, + 'no-host|no-hostname' => sub { + $show{'host'} = 0 }, + 'no-man' => sub { + $b_no_man_force = 0; }, + 'no-ssl' => sub { + $dl{'no-ssl-opt'}=1 }, + 'output-file:s' => sub { + my ($opt,$arg) = @_; + if ($arg){ + if ($arg eq 'print' || check_output_path($arg)){ + $output_file = $arg; + } + else { + error_handler('output-file-bad', $opt, $arg); + } + } + else { + error_handler('bad-arg', $opt, $arg); + }}, + 'recommends' => sub { + $b_recommends = 1; }, + 'U|update:s' => sub { # 1,2,3 OR http://myserver/path/inxi + my ($opt,$arg) = @_; + $b_downloader = 1; + if ( $b_update ){ + $b_updater = 1; + if (!$arg && $self_name eq 'pinxi'){ + $b_man = 1; + $download_id = 'inxi-perl branch'; + $self_download = get_defaults('inxi-pinxi'); + } + elsif ($arg && $arg eq '3'){ + $b_man = 1; + $download_id = 'dev server'; + $self_download = get_defaults('inxi-dev'); + } + else { + if (!$arg){ + $download_id = 'main branch'; + $self_download = get_defaults('inxi-main'); + $b_man = 1; + $b_use_man = 1; + } + elsif ( $arg =~ /^[12]$/){ + $download_id = "branch $arg"; + $self_download = get_defaults("inxi-branch-$arg"); + } + elsif ( $arg =~ /^http/){ + $download_id = 'alt server'; + $self_download = $arg; + } + } + if (!$self_download){ + error_handler('bad-arg', $opt, $arg); + } + } + else { + error_handler('distro-block', $opt); + } }, + 'V|version' => sub { + $b_version = 1 }, + '<>' => sub { + my ($opt) = @_; + error_handler('unknown-option', "$opt", "" ); } + ) ; #or error_handler('unknown-option', "@ARGV", ''); + ## run all these after so that we can change widths, downloaders, etc + eval $end if $b_log; + CheckRecommends::run() if $b_recommends; + set_downloader() if $b_downloader; + show_version() if $b_version; + show_options() if $b_help; + $b_man = 0 if (!$b_use_man || $b_no_man_force); + update_me( $self_download, $download_id ) if $b_updater; + if ($output_type){ + if ($output_type ne 'screen' && ! $output_file){ + error_handler('bad-arg', '--output', '--output-file not provided'); + } + } + if ( $show{'ram'} || $show{'slot'} || + ( ( $bsd_type || $b_dmidecode_force ) && ($show{'machine'} || $show{'battery'}) ) ){ + $b_dmi = 1; + } + if ($show{'audio'} || $show{'graphic'} || $show{'network'} ){ + $b_pci = 1; + } + if ($show{'usb'} || $show{'audio'} || $show{'network'} ){ + # to detect wan/lan, we have to use long form to get as much data as possible + $usb_level = ($show{'usb'} || $show{'network'}) ? 2 : 1; + } + if ($bsd_type && ($show{'short'} || $show{'cpu'} || $show{'cpu-basic'} || $show{'machine'} || + $show{'info'} || $show{'process'} || $show{'ram'} ) ){ + $b_sysctl = 1; + } + if ($show{'filter-override'}){ + $show{'filter'} = 0; + } + # override for things like -b or -v2 to -v3 + $show{'cpu-basic'} = 0 if $show{'cpu'}; + $show{'optical-basic'} = 0 if $show{'optical'}; + $show{'partition'} = 0 if $show{'partition-full'}; + if ($show{'disk'} || $show{'optical'} ){ + $show{'disk-basic'} = 0; + $show{'disk-total'} = 0; + } + if ($bsd_type && ($show{'short'} || $show{'disk-basic'} || $show{'disk-total'} || $show{'disk'})){ + $b_dm_boot_disk = 1; + } + if ($bsd_type && ($show{'optical-basic'} || $show{'optical'})){ + $b_dm_boot_optical = 1 + } +} + +sub show_options { + error_handler('not-in-irc', 'help') if $b_irc; + my (@row,@rows,@data); + my $line = ''; + my $color_scheme_count = get_color_scheme('count') - 1; + my $partition_string='partition'; + my $partition_string_u='Partition'; + my $flags = ($b_arm) ? 'features' : 'flags' ; + if ( $bsd_type ){ + $partition_string='slice'; + $partition_string_u='Slice'; + } + # fit the line to the screen! + for my $i ( 0 .. ( ( $size{'max'} / 2 ) - 2 ) ){ + $line = $line . '- '; + } + @rows = ( + ['0', '', '', "$self_name supports the following options. You can combine + these or list them one by one. For more detailed information, see man^$self_name. + Examples:^$self_name^-v4^-c6 OR + $self_name^-bDc^6. If you start $self_name with no arguments, it will display + a short system summary." ], + [0, '', '', '' ], + ['0', '', '', "The following options, if used without -F, -b, or -v, will + show option line(s): A, B, C, D, G, I, M, N, P, R, S, W, d, f, i, l, m, n, + o, p, r, s, t, u, w, --slots, --usb - you can use these alone or together + to show just the line(s) you want to see. If you use them with -v [level], + -b or -F, $self_name will combine the outputs." ], + ['0', '', '', $line ], + ['0', '', '', "Output Control Options:" ], + ['1', '-A', '--audio', "Audio/sound card(s), driver, sound server." ], + ['1', '-b', '--basic', "Basic output, short form. Same as $self_name^-v^2." ], + ['1', '-B', '--battery', "Battery info, including charge and condition, plus + extra info (if battery present)." ], + ['1', '-c', '--color', "Set color scheme (0-42). Example:^$self_name^-c^11" ], + ['1', '', '', "Color selectors let you set the config file value for the + selection (NOTE: IRC and global only show safe color set)" ], + ['2', '94', '', "Console, out of X" ], + ['2', '95', '', "Terminal, running in X - like xTerm" ], + ['2', '96', '', "Gui IRC, running in X - like Xchat, Quassel, Konversation etc." ], + ['2', '97', '', "Console IRC running in X - like irssi in xTerm" ], + ['2', '98', '', "Console IRC not in X" ], + ['2', '99', '', "Global - Overrides/removes all settings. Setting specific + removes global." ], + ['1', '-C', '--cpu', "CPU output, including per CPU clock speed and max + CPU speed (if available)." ], + ['1', '-d', '--disk-full, --optical', "Optical drive data (and floppy disks, + if present). Triggers -D." ], + ['1', '-D', '--disk', "Hard Disk info, including total storage and details + for each disk. Disk total used percentage includes swap partition size(s)." ], + ['1', '-f', '--flags', "All CPU $flags. Triggers -C. Not shown with -F to + avoid spamming." ], + ['1', '-F', '--full', "Full output. Includes all Upper Case line letters + except -W, plus -s and -n. Does not show extra verbose options such + as -d -f -i -l -m -o -p -r -t -u -x, unless specified." ], + ['1', '-G', '--graphics', "Graphics info (card(s), driver, display protocol + (if available), display server, resolution, renderer, OpenGL version)." ], + ['1', '-i', '--ip', "WAN IP address and local interfaces (requires ifconfig + or ip network tool). Triggers -n. Not shown with -F for user security reasons. + You shouldn't paste your local/WAN IP." ], + ['1', '-I', '--info', "General info, including processes, uptime, memory, + IRC client or shell type, $self_name version." ], + ['1', '-l', '--label', "$partition_string_u labels. Triggers -P. + For full -p output, use -pl." ], + ['1', '-m', '--memory', "Memory (RAM) data. Requires root. Numbers of + devices (slots) supported and individual memory devices (sticks of memory etc). + For devices, shows device locator, size, speed, type (e.g. DDR3). + If neither -I nor -tm are selected, also shows RAM used/total." ], + ['1', '-M', '--machine', "Machine data. Device type (desktop, server, laptop, + VM etc.), motherboard, BIOS and, if present, system builder (e.g. Lenovo). + Shows UEFI/BIOS/UEFI [Legacy]. Older systems/kernels without the required /sys + data can use dmidecode instead, run as root. Dmidecode can be forced with --dmidecode" ], + ['1', '-n', '--network-advanced', "Advanced Network card info. Triggers -N. Shows + interface, speed, MAC id, state, etc. " ], + ['1', '-N', '--network', "Network card(s), driver." ], + ['1', '-o', '--unmounted', "Unmounted $partition_string info (includes UUID + and Label if available). Shows file system type if you have lsblk installed + (Linux) or, for BSD/GNU Linux, if 'file' installed and you are root or if + you have added to /etc/sudoers (sudo v. 1.7 or newer)." ], + ['1', '', '', "Example: ^<username>^ALL^=^NOPASSWD:^/usr/bin/file^" ], + ['1', '-p', '--partitions-full', "Full $partition_string information (-P plus all other + detected ${partition_string}s)." ], + ['1', '-P', '--partitions', "Basic $partition_string info. Shows, if detected: + / /boot /home /opt /tmp /usr /var /var/log /var/tmp. Use -p to see all + mounted ${partition_string}s." ], + ['1', '-r', '--repos', "Distro repository data. Supported repo types: APK, + APT, EOPKG, PACMAN, PACMAN-G2, PISI, PORTAGE, PORTS (BSDs), SLACKPKG, + URPMQ, YUM/ZYPP." ], + ['1', '-R', '--raid', "RAID data. Shows RAID devices, states, levels, + and components. md-raid: If device is resyncing, also shows resync progress line." ], + ['1', '-s', '--sensors', "Sensors output (if sensors installed/configured): + mobo/CPU/GPU temp; detected fan speeds. GPU temp only for Fglrx/Nvidia drivers. + Nvidia shows screen number for > 1 screen. IPMI sensors if present." ], + ['1', '', '--slots', "PCI slots: type, speed, status. Requires root." ], + ['1', '-S', '--system', "System info: host name, kernel, desktop environment + (if in X/Wayland), distro." ], + ['1', '-t', '--processes', "Processes. Requires extra options: c (CPU), m + (memory), cm (CPU+memory). If followed by numbers 1-x, shows that number + of processes for each type (default: 5; if in IRC, max: 5). " ], + ['1', '', '', "Make sure that there is no space between letters and + numbers (e.g. write as^-t^cm10)." ], + ['1', '', '--usb', "Show USB data: Hubs and Devices." ], + ['1', '-u', '--uuid', "$partition_string_u UUIDs. Triggers -P. For full -p + output, use -pu." ], + ['1', '-v', '--verbosity', "Set $self_name verbosity level (0-8). + Should not be used with -b or -F. Example: $self_name^-v^4" ], + ['2', '0', '', "Same as: $self_name" ], + ['2', '1', '', "Basic verbose, -S + basic CPU + -G + basic Disk + -I." ], + ['2', '2', '', "Networking card (-N), Machine (-M), Battery (-B; if present), + and, if present, basic RAID (devices only; notes if inactive). + Same as $self_name^-b" ], + ['2', '3', '', "Advanced CPU (-C), battery (-B), network (-n); + triggers -x. " ], + ['2', '4', '', "$partition_string_u size/used data (-P) for + (if present) /, /home, /var/, /boot. Shows full disk data (-D). " ], + ['2', '5', '', "Audio card (-A), sensors (-s), memory/RAM (-m), + $partition_string label^(-l), UUID^(-u), short form of optical drives, + standard RAID data (-R). " ], + ['2', '6', '', "Full $partition_string (-p), unmounted $partition_string (-o), + optical drive (-d), USB (--usb), full RAID; triggers -xx." ], + ['2', '7', '', "Network IP data (-i); triggers -xxx."], + ['2', '8', '', "Everything available, including repos (-r), processes + (-tcm), PCI slots (--slots)."], + ); + push @data, @rows; # if distro maintainers don't want the weather feature disable it - if [[ $B_ALLOW_WEATHER == 'false' ]];then - weather_flag='' - fi - if [[ $1 == '--version' ]];then - show_version_info - exit 0 - elif [[ $1 == '--help' ]];then - show_options - exit 0 - elif [[ $1 == '--recommends' ]];then - check_recommends_user_output - exit 0 - # the short form only runs if no args output args are used - # no need to run through these if there are no args - # reserved for future use: -g for extra Graphics; -m for extra Machine; -d for extra Disk - elif [[ -n $1 ]];then - while getopts AbBc:CdDfFGhHiIlmMnNopPrRsSt:uUv:V${weather_flag}xy:zZ%@:!: opt - do - case $opt in - A) B_SHOW_AUDIO='true' - use_short='false' - ;; - b) use_short='false' - B_SHOW_BASIC_CPU='true' - B_SHOW_BASIC_RAID='true' - B_SHOW_DISK_TOTAL='true' - B_SHOW_GRAPHICS='true' - B_SHOW_INFO='true' - B_SHOW_MACHINE='true' - B_SHOW_BATTERY='true' - B_SHOW_NETWORK='true' - B_SHOW_SYSTEM='true' - ;; - B) B_SHOW_BATTERY_FORCED='true' - B_SHOW_BATTERY='true' - use_short='false' - ;; - c) if [[ $OPTARG =~ ^[0-9][0-9]?$ ]];then - case $OPTARG in - 99) - B_RUN_COLOR_SELECTOR='true' - COLOR_SELECTION='global' - ;; - 98) - B_RUN_COLOR_SELECTOR='true' - COLOR_SELECTION='irc-console' - ;; - 97) - B_RUN_COLOR_SELECTOR='true' - COLOR_SELECTION='irc-virtual-terminal' - ;; - 96) - B_RUN_COLOR_SELECTOR='true' - COLOR_SELECTION='irc' - ;; - 95) - B_RUN_COLOR_SELECTOR='true' - COLOR_SELECTION='virtual-terminal' - ;; - 94) - B_RUN_COLOR_SELECTOR='true' - COLOR_SELECTION='console' - ;; - *) - B_COLOR_SCHEME_SET='true' - ## note: not sure about this, you'd think user values should be overridden, but - ## we'll leave this for now - if [[ -z $COLOR_SCHEME ]];then - set_color_scheme "$OPTARG" - fi - ;; - esac - else - error_handler 3 "$OPTARG" - fi - ;; - C) B_SHOW_CPU='true' - use_short='false' - ;; - d) B_SHOW_DISK='true' - B_SHOW_FULL_OPTICAL='true' - use_short='false' - # error_handler 20 "-d has been replaced by -b" - ;; - D) B_SHOW_DISK='true' - use_short='false' - ;; - f) B_SHOW_CPU='true' - B_CPU_FLAGS_FULL='true' - use_short='false' - ;; - F) # B_EXTRA_DATA='true' - B_SHOW_ADVANCED_NETWORK='true' - B_SHOW_AUDIO='true' - # B_SHOW_BASIC_OPTICAL='true' - B_SHOW_CPU='true' - B_SHOW_DISK='true' - B_SHOW_GRAPHICS='true' - B_SHOW_INFO='true' - B_SHOW_MACHINE='true' - B_SHOW_BATTERY='true' - B_SHOW_NETWORK='true' - B_SHOW_PARTITIONS='true' - B_SHOW_RAID='true' - B_SHOW_SENSORS='true' - B_SHOW_SYSTEM='true' - use_short='false' - ;; - G) B_SHOW_GRAPHICS='true' - use_short='false' - ;; - i) B_SHOW_IP='true' - B_SHOW_NETWORK='true' - B_SHOW_ADVANCED_NETWORK='true' - use_short='false' - ;; - I) B_SHOW_INFO='true' - use_short='false' - ;; - l) B_SHOW_LABELS='true' - B_SHOW_PARTITIONS='true' - use_short='false' - ;; - m) B_SHOW_MEMORY='true' - use_short='false' - ;; - M) B_SHOW_MACHINE='true' - use_short='false' - ;; - n) B_SHOW_ADVANCED_NETWORK='true' - B_SHOW_NETWORK='true' - use_short='false' - ;; - N) B_SHOW_NETWORK='true' - use_short='false' - ;; - o) B_SHOW_UNMOUNTED_PARTITIONS='true' - use_short='false' - ;; - p) B_SHOW_PARTITIONS_FULL='true' - B_SHOW_PARTITIONS='true' - use_short='false' - ;; - P) B_SHOW_PARTITIONS='true' - use_short='false' - ;; - r) B_SHOW_REPOS='true' - use_short='false' - ;; - R) B_SHOW_RAID='true' - # it turns out only users with mdraid software installed will have raid, - # so unless -R is explicitly called, blank -b/-F/-v6 and less output will not show - # error if file is missing. - B_SHOW_RAID_R='true' - use_short='false' - ;; - s) B_SHOW_SENSORS='true' - use_short='false' - ;; - S) B_SHOW_SYSTEM='true' - use_short='false' - ;; - t) if [[ $OPTARG =~ ^(c|m|cm|mc)([1-9]|1[0-9]|20)?$ ]];then - use_short='false' - if [[ -n $( grep -E '[0-9]+' <<< $OPTARG ) ]];then - PS_COUNT=$( sed 's/[^0-9]//g' <<< $OPTARG ) - fi - if [[ -n $( grep 'c' <<< $OPTARG ) ]];then - B_SHOW_PS_CPU_DATA='true' - fi - if [[ -n $( grep 'm' <<< $OPTARG ) ]];then - B_SHOW_PS_MEM_DATA='true' - fi - else - error_handler 13 "$OPTARG" - fi - ;; - u) B_SHOW_UUIDS='true' - B_SHOW_PARTITIONS='true' - use_short='false' - ;; - v) if [[ $OPTARG =~ ^[0-9][0-9]?$ && $OPTARG -le $VERBOSITY_LEVELS ]];then - if [[ $OPTARG -ge 1 ]];then - use_short='false' - B_SHOW_BASIC_CPU='true' - B_SHOW_DISK_TOTAL='true' - B_SHOW_GRAPHICS='true' - B_SHOW_INFO='true' - B_SHOW_SYSTEM='true' - fi - if [[ $OPTARG -ge 2 ]];then - B_SHOW_BASIC_DISK='true' - B_SHOW_BASIC_RAID='true' - B_SHOW_BATTERY='true' - B_SHOW_MACHINE='true' - B_SHOW_NETWORK='true' - fi - if [[ $OPTARG -ge 3 ]];then - B_SHOW_ADVANCED_NETWORK='true' - B_SHOW_CPU='true' - B_EXTRA_DATA='true' - fi - if [[ $OPTARG -ge 4 ]];then - B_SHOW_DISK='true' - B_SHOW_PARTITIONS='true' - fi - if [[ $OPTARG -ge 5 ]];then - B_SHOW_AUDIO='true' - B_SHOW_BASIC_OPTICAL='true' - B_SHOW_MEMORY='true' - B_SHOW_SENSORS='true' - B_SHOW_LABELS='true' - B_SHOW_UUIDS='true' - B_SHOW_RAID='true' - fi - if [[ $OPTARG -ge 6 ]];then - B_SHOW_FULL_OPTICAL='true' - B_SHOW_PARTITIONS_FULL='true' - B_SHOW_UNMOUNTED_PARTITIONS='true' - B_EXTRA_EXTRA_DATA='true' - fi - if [[ $OPTARG -ge 7 ]];then - B_EXTRA_EXTRA_EXTRA_DATA='true' - B_SHOW_IP='true' - B_SHOW_RAID_R='true' - fi - else - error_handler 4 "$OPTARG" - fi - ;; - U) if [[ $B_ALLOW_UPDATE == 'true' ]];then - self_updater "$SELF_DOWNLOAD" 'source server' "$opt" - else - error_handler 17 "-$opt" - fi - ;; - V) show_version_info - exit 0 - ;; - w) B_SHOW_WEATHER=true - use_short='false' - ;; - W) ALTERNATE_WEATHER_LOCATION=$( sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' <<< $OPTARG ) - if [[ -n $( grep -Esi '([^,]+,.+|[0-9-]+)' <<< $ALTERNATE_WEATHER_LOCATION ) ]];then - B_SHOW_WEATHER=true - use_short='false' - else - error_handler 18 "-$opt: '$OPTARG'" "city,state OR latitude,longitude OR postal/zip code." - fi - ;; - # this will trigger either with x, xx, xxx or with Fx but not with xF - x) if [[ $B_EXTRA_EXTRA_DATA == 'true' ]];then - B_EXTRA_EXTRA_EXTRA_DATA='true' - elif [[ $B_EXTRA_DATA == 'true' ]];then - B_EXTRA_EXTRA_DATA='true' - else - B_EXTRA_DATA='true' - fi - ;; - y) if [[ -z ${OPTARG//[0-9]/} && $OPTARG -ge 80 ]];then - set_display_width "$OPTARG" - else - error_handler 21 "$OPTARG" - fi - ;; - z) B_OUTPUT_FILTER='true' - ;; - Z) B_OVERRIDE_FILTER='true' - ;; - h) show_options - exit 0 - ;; - H) show_options 'full' - exit 0 - ;; - ## debuggers and testing tools - %) B_HANDLE_CORRUPT_DATA='true' - ;; - @) if [[ -n $( grep -E "^([1-9]|1[0-5])$" <<< $OPTARG ) ]];then - DEBUG=$OPTARG - if [[ $B_EXTRA_EXTRA_DATA == 'true' ]];then - B_UPLOAD_DEBUG_DATA='true' - fi - exec 2>&1 - # switch on logging only for -@ 8-10 - case $OPTARG in - 8|9|10) - if [[ $OPTARG -eq 10 ]];then - B_LOG_COLORS='true' - elif [[ $OPTARG -eq 9 ]];then - B_LOG_FULL_DATA='true' - fi - B_USE_LOGGING='true' - # pack the logging data for evals function start/end - LOGFS=$LOGFS_STRING - LOGFE=$LOGFE_STRING - create_rotate_logfiles # create/rotate logfiles before we do anything else - ;; - 11|12|13|14|15) - case $OPTARG in - 11) - debug_data_type='sys' - ;; - 12) - debug_data_type='xorg' - ;; - 13) - debug_data_type='disk' - ;; - 14) - debug_data_type='all' - ;; - 15) - debug_data_type='all' - B_DEBUG_I='true' - ;; - esac - initialize_data - debug_data_collector $debug_data_type - ;; - esac - else - error_handler 9 "$OPTARG" - fi - ;; - !) # test for various supported methods - case $OPTARG in - 1) B_TESTING_1='true' - ;; - 2) B_TESTING_2='true' - ;; - 3) B_TESTING_1='true' - B_TESTING_2='true' - ;; - 1[0-3]|http*) - if [[ $B_ALLOW_UPDATE == 'true' ]];then - case $OPTARG in - 10) - self_updater "$SELF_DOWNLOAD_DEV" 'dev server' "$opt $OPTARG" - B_MAN='false' - ;; - 11) - self_updater "$SELF_DOWNLOAD_BRANCH_1" 'branch one server' "$opt $OPTARG" - B_MAN='false' - ;; - 12) - self_updater "$SELF_DOWNLOAD_BRANCH_2" 'branch two server' "$opt $OPTARG" - B_MAN='false' - ;; - 13) - self_updater "$SELF_DOWNLOAD_BRANCH_3" 'branch three server' "$opt $OPTARG" - B_MAN='false' - ;; - http*) - self_updater "$OPTARG" 'alt server' "$opt <http...>" - B_MAN='false' - ;; - esac - else - error_handler 17 "-$opt $OPTARG" - fi - ;; - 30) - B_IRC='false' - ;; - 31) - B_SHOW_HOST='false' - ;; - 32) - B_SHOW_HOST='true' - ;; - 33) - B_FORCE_DMIDECODE='true' - ;; - 34) - NO_SSL_OPT=$NO_SSL - ;; - 40*) - DISPLAY=${OPTARG/40/} - if [[ $DISPLAY == '' ]];then - DISPLAY=':0' - fi - DISPLAY_OPT="-display $DISPLAY" - B_SHOW_DISPLAY_DATA='true' - B_RUNNING_IN_DISPLAY='true' - ;; - 41) - B_CURL='false' - set_downloader - ;; - 42) - B_FETCH='false' - set_downloader - ;; - 43) - B_WGET='false' - set_downloader - ;; - 44) - B_CURL='false' - B_FETCH='false' - B_WGET='false' - set_downloader - ;; - ftp*) - ALTERNATE_FTP="$OPTARG" - ;; - # for weather function, allows user to set an alternate weather location - location=*) - error_handler 19 "-$opt location=" "-W" - ;; - *) error_handler 11 "$OPTARG" - ;; - esac - ;; - *) error_handler 7 "$1" - ;; - esac - done - fi - ## this must occur here so you can use the debugging flag to show errors - ## Reroute all error messages to the bitbucket (if not debugging) - if [[ $DEBUG -eq 0 ]];then - exec 2>/dev/null - fi - #((DEBUG)) && exec 2>&1 # This is for debugging konversation - - # after all the args have been processed, if no long output args used, run short output - if [[ $use_short == 'true' ]];then - B_SHOW_SHORT_OUTPUT='true' - fi - # just in case someone insists on using -zZ - if [[ $B_OVERRIDE_FILTER == 'true' ]];then - B_OUTPUT_FILTER='false' - fi - # change basic to full if user requested it or if arg overrides it - if [[ $B_SHOW_RAID == 'true' && $B_SHOW_BASIC_RAID == 'true' ]];then - B_SHOW_BASIC_RAID='false' - fi - - - eval $LOGFE + if ( $b_weather ){ + @rows = ( + ['1', '-w', '--weather', "Local weather data/time. To check an alternate + location, see -W."], + ['1', '-W', '--weather-location', "<location> Supported options for + <location>: postal code; city, state/country; latitude, longitude. + Only use if you want the weather somewhere other than the machine running + $self_name. Use only ASCII characters, replace spaces in city/state/country names with '+'. + Example:^$self_name^-W^new+york,ny"] + ); + push @data, @rows; + } + @rows = ( + ['1', '-x', '-extra', "Adds the following extra data (only works with + verbose or line output, not short form):" ], + ['2', '-B', '', "Vendor/model, status (if available)." ], + ['2', '-C', '', "CPU $flags, Bogomips on CPU; CPU microarchitecture + + revision (if found)." ], + ['2', '-d', '', "Extra optical drive features data; adds rev version to + optical drive." ], + ['2', '-D', '', "HDD temp with disk data if you have hddtemp installed, if + you are root, or if you have added to /etc/sudoers (sudo v. 1.7 or newer). + Example:^<username>^ALL^=^NOPASSWD:^/usr/sbin/hddtemp" ], + ['2', '-G', '', "Direct rendering status (in X); Screen number GPU is + running on (Nvidia only)." ], + ['2', '-i', '', "For IPv6, show additional scope addresses: Global, Site, + Temporary, Unknown. See --limit for large counts of IP addresses." ], + ['2', '-I', '', "Default system GCC. With -xx, also shows other installed + GCC versions. If running in shell, not in IRC client, shows shell version + number, if detected. Init/RC type and runlevel (if available)." ], + ['2', '-m', '', "Max memory module size (if available), device type." ], + ['2', '-N -A', '', "Version/port(s)/driver version (if available)." ], + ['2', '-N -A -G', '', "PCI Bus ID/USB ID number of card." ], + ['2', '-R', '', "md-raid: second RAID Info line with extra data: + blocks, chunk size, bitmap (if present). Resync line, shows blocks + synced/total blocks." ], + ['2', '-s', '', "Basic voltages (ipmi only): 12v, 5v, 3.3v, vbat." ], + ['2', '-S', '', "Desktop toolkit, if available (GNOME/Xfce/KDE only); + Kernel gcc version" ], + ['2', '-t', '', "Adds memory use output to CPU (-xt c), and CPU use to + memory (-xt m)." ], + ['2', '--usb', '', "For Devices, shows USB version/speed." ], + ); + push @data, @rows; + if ( $b_weather ){ + @rows = (['2', '-w -W', '', "Wind speed and direction, humidity, pressure, + and (-w only) time zone." ]); + push @data, @rows; + } + @rows = ( + ['1', '-xx', '--extra 2', "Show extra, extra data (only works with verbose + or line output, not short form):" ], + ['2', '-A', '', "Chip vendor:product ID for each audio device." ], + ['2', '-B', '', "Serial number, voltage now/minimum (if available)." ], + ['2', '-C', '', "Minimum CPU speed, if available." ], + ['2', '-D', '', "Disk serial number." ], + ['2', '-G', '', "Chip vendor:product ID for each video card; OpenGL + compatibility version, if free drivers and available; compositor (experimental)." ], + ['2', '-I', '', "Other detected installed gcc versions (if present). System + default runlevel. Adds parent program (or tty) for shell info if not in + IRC. Adds Init version number, RC (if found)." ], + ['2', '-m', '', "Manufacturer, part number; single/double bank (if found)." ], + ['2', '-M', '', "Chassis info, BIOS ROM size (dmidecode only), if available." ], + ['2', '-N', '', "Chip vendor:product ID for each NIC." ], + ['2', '-R', '', "md-raid: Superblock (if present), algorithm. If resync, + shows progress bar." ], + ['2', '-s', '', "DIMM/SOC voltages (ipmi only)." ], + ['2', '-S', '', "Display manager (dm) in desktop output if in X (e.g. kdm, + gdm3, lightdm)." ], + ['2', '--slots', '', "Slot length." ], + ['2', '--usb', '', "Vendor:chip ID." ], + ); + push @data, @rows; + if ( $b_weather ){ + @rows = (['2', '-w -W', '', "Wind chill, dew point, heat index, if available." ]); + push @data, @rows; + } + @rows = ( + ['1', '-xxx', '--extra 3', "Show extra, extra, extra data (only works + with verbose or line output, not short form):" ], + ['2', '-B', '', "Chemistry, cycles, location (if available)." ], + ['2', '-D', '', "Firmware rev. if available; partition scheme, in some cases." ], + ['2', '-I', '', "For 'Shell:' adds ([su|sudo|login]) to shell name if present; + for 'running in:' adds (SSH) if SSH session." ], + ['2', '-m', '', "Width of memory bus, data and total (if present and greater + than data); Detail for Type, if present; module voltage, if available; serial number." ], + ['2', '-R', '', "zfs-raid: portion allocated (used) by RAID devices/arrays. + md-raid: system md-raid support types (kernel support, read ahead, RAID events)." ], + ['2', '-S', '', "Panel/shell info in desktop output, if in X (like gnome-shell, + cinnamon, mate-panel); (if available) dm version number, active window manager." ] + ); + push @data, @rows; + if ( $b_weather ){ + @rows = (['2', '-w -W', '', "Location (uses -z/irc filter), weather observation + time, altitude (shows extra lines for data where relevant)." ] ); + push @data, @rows; + } + @rows = ( + ['1', '-y', '--width', "Output line width max (integer >= 80). Overrides IRC/Terminal + settings or actual widths. Example:^inxi^-y^130" ], + ['1', '-z', '--filter', "Adds security filters for IP/MAC addresses, serial numbers, + location (-w), user home directory name. Default on for IRC clients." ], + ['1', '-Z', '--filter-override', "Absolute override for output filters. Useful for + debugging networking issues in IRC, for example." ], + [0, '', '', "$line" ], + [0, '', '', "Additional Options:" ], + ['1', '-h', '--help', "This help menu." ], + ['1', '', '--recommends', "Checks $self_name application dependencies + recommends, + and directories, then shows what package(s) you need to install to add support + for that feature." ] + ); + push @data, @rows; + if ( $b_update ){ + @rows = ( + ['1', '-U', '--update', "Auto-update $self_name. Will also install/update man + page. Note: if you installed as root, you must be root to update, otherwise + user is fine. Man page installs require root. No arguments downloads from + main $self_name git repo." ], + ['1', '', '', "Use alternate sources for updating $self_name" ], + ['2', '1', '', "Get the git branch one version." ], + ['2', '2', '', "Get the git branch two version." ], + ['3', '3', '', "Get the dev server (smxi.org) version." ], + ['2', '<http>', '', "Get a version of $self_name from your own server; + use the full download path, e.g.^$self_name^-U^https://myserver.com/inxi" ] + ); + push @data, @rows; + } + @rows = ( + ['1', '-V', '--version', "Prints $self_name version info then exits." ], + [0, '', '', "$line" ], + [0, '', '', "Debugging Options:" ], + ['1', '', '--debug', "Triggers debugging modes." ], + ['2', '1-3', '', "On screen debugger output." ], + ['2', '10', '', "Basic logging." ], + ['2', '11', '', "Full file/system info logging." ], + ['1', '', ,'', "The following create a tar.gz file of system data, plus $self_name + output. To automatically upload debugger data tar.gz file + to ftp.techpatterns.com: $self_name^--debug^21" ], + ['2', '20', '', "Full system data collection: /sys; xorg conf and log data, xrandr, + xprop, xdpyinfo, glxinfo etc.; data from dev, disks, + ${partition_string}s, etc." ], + ['2', '21', '', "Upload debugger dataset to $self_name debugger server + automatically, removes debugger data directory, leaves tar.gz debugger file." ], + ['2', '22', '', "Upload debugger dataset to $self_name debugger server + automatically, removes debugger data directory and debugger tar.gz file." ], + ['1', '', '--ftp', "Use with --debugger 21 to trigger an alternate FTP server for upload. + Format:^[ftp.xx.xx/yy]. Must include a remote directory to upload to. + Example:^$self_name^--debug^21^--ftp^ftp.myserver.com/incoming" ], + [0, '', '', "$line" ], + [0, '', '', "Advanced Options:" ], + [1, '', '--alt', "Trigger for various advanced options:" ], + ['2', '40', '', "Bypass Perl as a downloader option." ], + ['2', '41', '', "Bypass Curl as a downloader option." ], + ['2', '42', '', "Bypass Fetch as a downloader option." ], + ['2', '43', '', "Bypass Wget as a downloader option." ], + ['2', '44', '', "Bypass Curl, Fetch, and Wget as downloader options. Forces + Perl if HTTP::Tiny present." ], + ['1', '', '--display', "[:[0-9]] Try to get display data out of X (default: display 0)." ], + ['1', '', '--dmidecode', "Force use of dmidecode data instead of /sys where relevant + (e.g. -M, -B)." ], + ['1', '', '--downloader', "Force $self_name to use [curl|fetch|perl|wget] for downloads." ], + ['1', '', '--host', "Turn on hostname for -S." ], + ['1', '', '--indent-min', "Set point where $self_name autowraps line starters." ], + ['1', '', '--limit', "[-1; 1-x] Set max output limit of IP addresses for -i + (default 10; -1 removes limit)." ], + ); + push @data, @rows; + if ( $b_update ){ + @rows = ( + ['1', '', '--man', "Install correct man version for dev branch (-U 3) or pinxi using -U." ], + ); + push @data, @rows; + } + @rows = ( + ['1', '', '--no-host', "Turn off hostname for -S. Useful if showing output from servers etc." ], + ); + push @data, @rows; + if ( $b_update ){ + @rows = ( + ['1', '', '--no-man', "Disable man install for all -U update actions." ], + ); + push @data, @rows; + } + @rows = ( + ['1', '', '--no-ssl', "Skip SSL certificate checks for all downloader actions + (Wget/Fetch/Curl only)." ], + ['1', '', '--output', "[json|screen|xml] Change data output type. Requires --output-file + if not screen." ], + ['1', '', '--output-file', "[Full filepath|print] Output file to be used for --output." ], + ['1', '', '--sleep', "[0-x.x] Change CPU sleep time, in seconds, for -C + (default:^$cpu_sleep). Allows system to catch up and show a more accurate CPU + use. Example:^$self_name^-Cxxx^--sleep^0.15" ], + ['0', '', '', $line ], + ); + push @data, @rows; + print_basic(@data); + exit 1; } -## print out help menu, not including Testing or Debugger stuff because it's not needed -show_options() -{ - local color_scheme_count=$(( ${#A_COLOR_SCHEMES[@]} - 1 )) - local partition_string='partition' partition_string_u='Partition' - - if [[ $B_IRC == 'true' ]];then - print_screen_output "Sorry, you can't run the help option in an IRC client." - exit 1 - fi - if [[ -n $BSD_TYPE ]];then - partition_string='slice' - partition_string_u='Slice' - fi - # print_lines_basic "0" "" "" - # print_lines_basic "1" "" "" - # print_lines_basic "2" "" "" - # print_screen_output " " - print_lines_basic "0" "" "$SELF_NAME supports the following options. You can combine them, or list them one by one. Examples: $SELF_NAME^-v4^-c6 OR $SELF_NAME^-bDc^6. If you start $SELF_NAME with no arguments, it will show the short form." - print_screen_output " " - print_lines_basic "0" "" "The following options if used without -F, -b, or -v will show just option line(s): A, B, C, D, G, I, M, N, P, R, S, f, i, m, n, o, p, l, u, r, s, t - you can use these alone or together to show just the line(s) you want to see. If you use them with -v^[level], -b or -F, it will show the full output for that line along with the output for the chosen verbosity level." - print_screen_output "- - - - - - - - - - - - - - - - - - - - - - - - - - - - -" - print_screen_output "Output Control Options:" - print_lines_basic "1" "-A" "Audio/sound card information." - print_lines_basic "1" "-b" "Basic output, short form. Like $SELF_NAME^-v^2, only minus hard disk names ." - print_lines_basic "1" "-B" "Battery info, shows charge, condition, plus extra information (if battery present)." - print_lines_basic "1" "-c" "Color schemes. Scheme number is required. Color selectors run a color selector option prior to $SELF_NAME starting which lets you set the config file value for the selection." - print_lines_basic "1" "" "Supported color schemes: 0-$color_scheme_count Example:^$SELF_NAME^-c^11" - print_lines_basic "1" "" "Color selectors for each type display (NOTE: irc and global only show safe color set):" -# print_screen_output " Supported color schemes: 0-$color_scheme_count Example: $SELF_NAME -c 11" -# print_screen_output " Color selectors for each type display (NOTE: irc and global only show safe color set):" - print_lines_basic "2" "94" "Console, out of X" - print_lines_basic "2" "95" "Terminal, running in X - like xTerm" - print_lines_basic "2" "96" "Gui IRC, running in X - like Xchat, Quassel, Konversation etc." - print_lines_basic "2" "97" "Console IRC running in X - like irssi in xTerm" - print_lines_basic "2" "98" "Console IRC not in X" - print_lines_basic "2" "99" "Global - Overrides/removes all settings. Setting specific removes global." - print_lines_basic "1" "-C" "CPU output, including per CPU clockspeed and max CPU speed (if available)." - print_lines_basic "1" "-d" "Optical drive data (and floppy disks, if present). Same as -Dd. See also -x and -xx." - print_lines_basic "1" "-D" "Full hard Disk info, not only model, ie: /dev/sda ST380817AS 80.0GB. See also -x and -xx. Disk total used percentage includes swap partition size(s)." - print_lines_basic "1" "-f" "All cpu flags, triggers -C. Not shown with -F to avoid spamming. ARM cpus show 'features'." - print_lines_basic "1" "-F" "Full output for $SELF_NAME. Includes all Upper Case line letters, plus -s and -n. Does not show extra verbose options like -d -f -l -m -o -p -r -t -u -x" - print_lines_basic "1" "-G" "Graphic card information (card, display server type/version, resolution, renderer, OpenGL version)." - print_lines_basic "1" "-i" "Wan IP address, and shows local interfaces (requires ifconfig - network tool). Same as -Nni. Not shown with -F for user security reasons, you shouldn't paste your local/wan IP." - print_lines_basic "1" "-I" "Information: processes, uptime, memory, irc client (or shell type), $SELF_NAME version." - print_lines_basic "1" "-l" "$partition_string_u labels. Default: short $partition_string -P. For full -p output, use: -pl (or -plu)." - print_lines_basic "1" "-m" "Memory (RAM) data. Physical system memory array(s), capacity, how many devices (slots) supported, and individual memory devices (sticks of memory etc). For devices, shows device locator, size, speed, type (like: DDR3). If neither -I nor -tm are selected, also shows ram used/total. Also see -x, -xx, -xxx" - print_lines_basic "1" "-M" "Machine data. Device type (desktop, server, laptop, VM etc.), Motherboard, Bios, and if present, System Builder (Like Lenovo). Shows UEFI/BIOS/UEFI [Legacy]. Older systems/kernels without the required /sys data can use dmidecode instead, run as root. Dmidecode can be forced with -! 33" - print_lines_basic "1" "-n" "Advanced Network card information. Same as -Nn. Shows interface, speed, mac id, state, etc." - print_lines_basic "1" "-N" "Network card information. With -x, shows PCI BusID, Port number." - print_lines_basic "1" "-o" "Unmounted $partition_string information (includes UUID and LABEL if available). Shows file system type if you have file installed, if you are root OR if you have added to /etc/sudoers (sudo v. 1.7 or newer) Example:^<username>^ALL^=^NOPASSWD:^/usr/bin/file^" - print_lines_basic "1" "-p" "Full $partition_string information (-P plus all other detected ${partition_string}s)." - print_lines_basic "1" "-P" "Basic $partition_string information (shows what -v^4 would show, but without extra data). Shows, if detected: / /boot /home /opt /tmp /usr /var /var/log /var/tmp . Use -p to see all mounted ${partition_string}s." - print_lines_basic "1" "-r" "Distro repository data. Supported repo types: APK; APT; PACMAN; PISI; PORTAGE; PORTS (BSDs); SLACKPKG; URPMQ; YUM; ZYPP." - print_lines_basic "1" "-R" "RAID data. Shows RAID devices, states, levels, and components, and extra data with -x/-xx. md-raid: If device is resyncing, shows resync progress line as well." - print_lines_basic "1" "-s" "Sensors output (if sensors installed/configured): mobo/cpu/gpu temp; detected fan speeds. Gpu temp only for Fglrx/Nvidia drivers. Nvidia shows screen number for > 1 screens." - print_lines_basic "1" "-S" "System information: host name, kernel, desktop environment (if in X), distro" - print_lines_basic "1" "-t" "Processes. Requires extra options: c^(cpu) m^(memory) cm^(cpu+memory). If followed by numbers 1-20, shows that number of processes for each type (default:^$PS_COUNT; if in irc, max:^5): -t^cm10" - print_lines_basic "1" "" "Make sure to have no space between letters and numbers (-t^cm10 - right, -t^cm^10 - wrong)." - print_lines_basic "1" "-u" "$partition_string_u UUIDs. Default: short $partition_string -P. For full -p output, use: -pu (or -plu)." - print_lines_basic "1" "-v" "Script verbosity levels. Verbosity level number is required. Should not be used with -b or -F" - print_lines_basic "1" "" "Supported levels: 0-$VERBOSITY_LEVELS Example: $SELF_NAME^-v^4" - print_lines_basic "2" "0" "Short output, same as: $SELF_NAME" - print_lines_basic "2" "1" "Basic verbose, -S + basic CPU + -G + basic Disk + -I." - print_lines_basic "2" "2" "Networking card (-N), Machine (-M) data, if present, Battery (-B), basic hard disk data (names only), and, if present, basic raid (devices only, and if inactive, notes that). similar to: $SELF_NAME^-b" - print_lines_basic "2" "3" "Advanced CPU (-C), battery, network (-n) data, and switches on -x advanced data option." - print_lines_basic "2" "4" "$partition_string_u size/filled data (-P) for (if present): /, /home, /var/, /boot. Shows full disk data (-D)." - print_lines_basic "2" "5" "Audio card (-A); sensors^(-s), memory/ram^(-m), $partition_string label^(-l) and UUID^(-u), short form of optical drives, standard raid data (-R)." - print_lines_basic "2" "6" "Full $partition_string (-p), unmounted $partition_string (-o), optical drive (-d), full raid; triggers -xx." - print_lines_basic "2" "7" "Network IP data (-i); triggers -xxx." - - # if distro maintainers don't want the weather feature disable it - if [[ $B_ALLOW_WEATHER == 'true' ]];then - print_lines_basic "1" "-w" "Local weather data/time. To check an alternate location, see: -W^<location>. For extra weather data options see -x, -xx, and -xxx." - print_lines_basic "1" "-W" "<location> Supported options for <location>: postal code; city, state/country; latitude, longitude. Only use if you want the weather somewhere other than the machine running $SELF_NAME. Use only ascii characters, replace spaces in city/state/country names with '+'. Example:^$SELF_NAME^-W^new+york,ny" - fi - print_lines_basic "1" "-x" "Adds the following extra data (only works with verbose or line output, not short form):" - print_lines_basic "2" "-B" "Vendor/model, status (if available)" - print_lines_basic "2" "-C" "CPU Flags, Bogomips on Cpu;CPU microarchitecture / revision if found, like: (Sandy Bridge rev.2)" - print_lines_basic "2" "-d" "Extra optical drive data; adds rev version to optical drive." - print_lines_basic "2" "-D" "Hdd temp with disk data if you have hddtemp installed, if you are root OR if you have added to /etc/sudoers (sudo v. 1.7 or newer) Example:^<username>^ALL^=^NOPASSWD:^/usr/sbin/hddtemp" - print_lines_basic "2" "-G" "Direct rendering status for Graphics (in X)." - print_lines_basic "2" "-G" "(for single gpu, nvidia driver) screen number gpu is running on." - print_lines_basic "2" "-i" "For IPv6, show additional IP v6 scope addresses: Global, Site, Temporary, Unknown." - print_lines_basic "2" "-I" "System GCC, default. With -xx, also show other installed GCC versions. If running in console, not in IRC client, shows shell version number, if detected. Init/RC Type and runlevel (if available)." - print_lines_basic "2" "-m" "Part number; Max memory module size (if available)." - print_lines_basic "2" "-N -A" "Version/port(s)/driver version (if available) for Network/Audio;" - print_lines_basic "2" "-N -A -G" "Network, audio, graphics, shows PCI Bus ID/Usb ID number of card." - print_lines_basic "2" "-R" "md-raid: Shows component raid id. Adds second RAID Info line: raid level; report on drives (like 5/5); blocks; chunk size; bitmap (if present). Resync line, shows blocks synced/total blocks. zfs-raid: Shows raid array full size; available size; portion allocated to RAID" - print_lines_basic "2" "-S" "Desktop toolkit if available (GNOME/XFCE/KDE only); Kernel gcc version" - print_lines_basic "2" "-t" "Memory use output to cpu (-xt c), and cpu use to memory (-xt m)." - if [[ $B_ALLOW_WEATHER == 'true' ]];then - print_lines_basic "2" "-w -W" "Wind speed and time zone (-w only)." - fi - print_lines_basic "1" "-xx" "Show extra, extra data (only works with verbose or line output, not short form):" - print_lines_basic "2" "-A" "Chip vendor:product ID for each audio device." - print_lines_basic "2" "-B" "serial number, voltage (if available)." - print_lines_basic "2" "-C" "Minimum CPU speed, if available." - print_lines_basic "2" "-D" "Disk serial number; Firmware rev. if available." - print_lines_basic "2" "-G" "Chip vendor:product ID for each video card; (mir/wayland only) compositor (alpha test); OpenGL compatibility version, if free drivers and available." - print_lines_basic "2" "-I" "Other detected installed gcc versions (if present). System default runlevel. Adds parent program (or tty) for shell info if not in IRC (like Konsole or Gterm). Adds Init/RC (if found) version number." - print_lines_basic "2" "-m" "Manufacturer, Serial Number, single/double bank (if found)." - print_lines_basic "2" "-M" "Chassis information, bios rom size (dmidecode only), if data for either is available." - print_lines_basic "2" "-N" "Chip vendor:product ID for each nic." - print_lines_basic "2" "-R" "md-raid: Superblock (if present); algorythm, U data. Adds system info line (kernel support,read ahead, raid events). If present, adds unused device line. Resync line, shows progress bar." - print_lines_basic "2" "-S" "Display manager (dm) in desktop output, if in X (like kdm, gdm3, lightdm)." - if [[ $B_ALLOW_WEATHER == 'true' ]];then - print_lines_basic "2" "-w -W" "Humidity, barometric pressure." - fi - print_lines_basic "2" "-@ 11-14" "Automatically uploads debugger data tar.gz file to ftp.techpatterns.com. EG: $SELF_NAME^-xx@14" - print_lines_basic "1" "-xxx" "Show extra, extra, extra data (only works with verbose or line output, not short form):" - print_lines_basic "2" "-B" "chemistry, cycles, location (if available)." - print_lines_basic "2" "-m" "Width of memory bus, data and total (if present and greater than data); Detail, if present, for Type; module voltage, if available." - print_lines_basic "2" "-S" "Panel/shell information in desktop output, if in X (like gnome-shell, cinnamon, mate-panel)." - if [[ $B_ALLOW_WEATHER == 'true' ]];then - print_lines_basic "2" "-w -W" "Location (uses -z/irc filter), weather observation time, wind chill, heat index, dew point (shows extra lines for data where relevant)." - fi - print_lines_basic "1" "-y" "Required extra option: integer, 80 or greater. Set the output line width max. Overrides IRC/Terminal settings or actual widths. If used with -h, put -y option first. Example:^inxi^-y^130" - print_lines_basic "1" "-z" "Security filters for IP/Mac addresses, location, user home directory name. Default on for irc clients." - print_lines_basic "1" "-Z" "Absolute override for output filters. Useful for debugging networking issues in irc for example." - print_screen_output " " - print_screen_output "Additional Options:" - print_lines_basic "4" "-h --help" "This help menu." - print_lines_basic "4" "-H" "This help menu, plus developer options. Do not use dev options in normal operation!" - print_lines_basic "4" "--recommends" "Checks $SELF_NAME application dependencies + recommends, and directories, then shows what package(s) you need to install to add support for that feature. " - if [[ $B_ALLOW_UPDATE == 'true' ]];then - print_lines_basic "4" "-U" "Auto-update script. Will also install/update man page. Note: if you installed as root, you must be root to update, otherwise user is fine. Man page installs require root user mode." - fi - print_lines_basic "4" "-V --version" "$SELF_NAME version information. Prints information then exits." - print_screen_output " " - print_screen_output "Debugging Options:" - print_lines_basic "1" "-%" "Overrides defective or corrupted data." - print_lines_basic "1" "-@" "Triggers debugger output. Requires debugging level 1-14 (8-10 - logging of data). Less than 8 just triggers $SELF_NAME debugger output on screen." - print_lines_basic "2" "1-7" "On screen debugger output" - print_lines_basic "2" "8" "Basic logging" - print_lines_basic "2" "9" "Full file/sys info logging" - print_lines_basic "2" "10" "Color logging." - print_lines_basic "1" "" "The following create a tar.gz file of system data, plus collecting the inxi output to file. To automatically upload debugger data tar.gz file to ftp.techpatterns.com: inxi^-xx@^<11-14>" - print_lines_basic "1" "" "For alternate ftp upload locations: Example:^inxi^-!^ftp.yourserver.com/incoming^-xx@^14" - print_lines_basic "2" "11" "With data file of tree traverse read of /sys." - print_lines_basic "2" "12" "With xorg conf and log data, xrandr, xprop, xdpyinfo, glxinfo etc." - print_lines_basic "2" "13" "With data from dev, disks, ${partition_string}s, etc., plus /sys tree traverse data file." - print_lines_basic "2" "14" "Everything, full data collection." - print_screen_output " " - print_screen_output "Advanced Options:" - print_lines_basic "1" "-! 31" "Turns off hostname in output. Useful if showing output from servers etc." - print_lines_basic "1" "-! 32" "Turns on hostname in output. Overrides global B_SHOW_HOST='false'" - print_lines_basic "1" "-! 33" "Forces use of dmidecode data instead of /sys where relevant (-M)." - print_lines_basic "1" "-! 34" "Skips SSL certificate checks for all downloader activies (wget/fetch/curl only). Must go before other options." - print_lines_basic "1" "-! 40" "Will try to get display data out of X. Default gets it from display :0. If you use this format: -! 40:1 it would get it from display 1 instead, or any display you specify as long as there is no space between -! 40 and the :[display-number]." - print_lines_basic "1" "-! 41" "Bypass curl as a downloader option." - print_lines_basic "1" "-! 42" "Bypass fetch as a downloader option." - print_lines_basic "1" "-! 43" "Bypass wget as a downloader option." - print_lines_basic "1" "-! 44" "Bypass curl, fetch, and wget as a downloader options. Forces Perl if HTTP::Tiny present." - - if [[ $1 == 'full' ]];then - print_screen_output " " - print_screen_output "Developer and Testing Options (Advanced):" - print_lines_basic "1" "-! 1" "Sets testing flag B_TESTING_1='true' to trigger testing condition 1." - print_lines_basic "1" "-! 2" "Sets testing flag B_TESTING_2='true' to trigger testing condition 2." - print_lines_basic "1" "-! 3" "Sets flags B_TESTING_1='true' and B_TESTING_2='true'." - if [[ $B_ALLOW_UPDATE == 'true' ]];then - print_lines_basic "1" "-! 10" "Triggers an update from the primary dev download server instead of source server." - print_lines_basic "1" "-! 11" "Triggers an update from source branch one - if present, of course." - print_lines_basic "1" "-! 12" "Triggers an update from source branch two - if present, of course." - print_lines_basic "1" "-! 13" "Triggers an update from source branch three - if present, of course." - print_lines_basic "1" "-! " "<http://......> Triggers an update from whatever server you list." - print_lines_basic "1" "" "Example: inxi^-!^http://yourserver.com/testing/inxi" - fi - print_lines_basic "1" "-! " "<ftp.......> Changes debugging data ftp upload location to whatever you enter here. Only used together with -xx@^11-14, and must be used in front of that." - print_lines_basic "1" "" "Example: inxi^-!^ftp.yourserver.com/incoming^-xx@^14" - fi - print_screen_output " " -} - -## print out version information for -V/--version -show_version_info() -{ +sub show_version { + require Cwd; + import Cwd; # if not in PATH could be either . or directory name, no slash starting - local script_path=$SELF_PATH script_symbolic_start='' - if [[ $script_path == '.' ]];then - script_path=$( pwd ) - elif [[ -z $( grep '^/' <<< "$script_path" ) ]];then - script_path="$( pwd )/$script_path" - fi - # handle if it's a symbolic link, rare, but can happen with script directories in irc clients - # which would only matter if user starts inxi with -! 30 override in irc client - if [[ -L $script_path/$SELF_NAME ]];then - script_symbolic_start=$script_path/$SELF_NAME - script_path=$( readlink $script_path/$SELF_NAME ) - script_path=$( dirname $script_path ) - fi - print_screen_output "$SELF_NAME $SELF_VERSION-$SELF_PATCH ($SELF_DATE)" - if [[ $B_IRC == 'false' ]];then - print_screen_output "Program Location: $script_path" - if [[ -n $script_symbolic_start ]];then - print_screen_output "Started via symbolic link: $script_symbolic_start" - fi - print_lines_basic "0" "" "Website:^https://github.com/smxi/inxi^or^http://smxi.org/" - print_lines_basic "0" "" "IRC:^irc.oftc.net channel:^#smxi" - print_lines_basic "0" "" "Forums:^http://techpatterns.com/forums/forum-33.html" - print_screen_output " " - print_lines_basic "0" "" "$SELF_NAME - the universal, portable, system information tool for console and irc." - print_screen_output " " - print_lines_basic "0" "" "This program started life as a fork of Infobash 3.02: Copyright^(C)^2005-2007^Michiel^de^Boer^a.k.a.^locsmif." - print_lines_basic "0" "" "Subsequent changes and modifications (after Infobash 3.02): Copyright^(C)^2008-${SELF_DATE%%-*}^Harald^Hope^aka^h2. CPU/Konversation^fixes:^Scott^Rogers^aka^trash80. USB^audio^fixes:^Steven^Barrett^aka^damentz." - print_screen_output " " - print_lines_basic "0" "" "This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. (http://www.gnu.org/licenses/gpl.html)" - fi + my $working_path=$self_path; + my (@data, @row, @rows, $link, $self_string); + if ( $working_path eq '.' ){ + $working_path = getcwd(); + } + elsif ( $working_path !~ /^\// ){ + $working_path = getcwd() . "/$working_path"; + } + # handle if it's a symbolic link, rare, but can happen with directories + # in irc clients which would only matter if user starts inxi with -! 30 override + # in irc client + if ( -l "$working_path/$self_name" ){ + $link="$working_path/$self_name"; + $working_path = readlink "$working_path/$self_name"; + $working_path =~ s/[^\/]+$//; + } + # strange output /./ ending, but just trim it off, I don't know how it happens + $working_path =~ s%/\./%/%; + @row = ([ 0, '', '', "$self_name $self_version-$self_patch ($self_date)"],); + push @data, @row; + if ( ! $b_irc ){ + @row = ([ 0, '', '', ""],); + push @data, @row; + my $year = (split/-/, $self_date)[0]; + @row = [ 0, '', '', "Program Location: $working_path" ]; + push @data, @row; + if ( $link ){ + @row = [ 0, '', '', "Started via symbolic link: $link" ]; + push @data, @row; + } + @rows = ( + [ 0, '', '', "Website:^https://github.com/smxi/inxi^or^https://smxi.org/" ], + [ 0, '', '', "IRC:^irc.oftc.net channel:^#smxi" ], + [ 0, '', '', "Forums:^https://techpatterns.com/forums/forum-33.html" ], + [ 0, '', '', " " ], + [ 0, '', '', "$self_name - the universal, portable, system information tool + for console and irc." ], + [ 0, '', '', "Using Perl version: $]"], + [ 0, '', '', " " ], + [ 0, '', '', "This program started life as a fork of Infobash 3.02: + Copyright^(C)^2005-2007^Michiel^de^Boer^aka^locsmif." ], + [ 0, '', '', "Subsequent changes and modifications (after Infobash 3.02): + Copyright^(C)^2008-$year^Harald^Hope^aka^h2. + CPU/Konversation^fixes:^Scott^Rogers^aka^trash80. + USB^audio^fixes:^Steven^Barrett^aka^damentz." ], + [ 0, '', '', '' ], + [ 0, '', '', "This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the Free Software + Foundation; either version 3 of the License, or (at your option) any later version. + (https://www.gnu.org/licenses/gpl.html)" ] + ); + push @data, @rows; + } + print_basic(@data); + exit 1; } ######################################################################## #### STARTUP DATA ######################################################################## -# This needs some cleanup and comments, not quite understanding what is -# happening, although generally output is known. Parse the null -# separated commandline under /proc/<pid passed in $1>/cmdline -# args: $1 - $PPID -get_cmdline() -{ - eval $LOGFS - local i=0 ppid=$1 - - if [[ ! -e /proc/$ppid/cmdline ]];then - echo 0 - return - fi - ##print_screen_output "Marker" - ##print_screen_output "\$ppid='$ppid' -=- $(< /proc/$ppid/cmdline)" - unset A_CMDL - # http://transnum.blogspot.com/2008/11/bashs-read-built-in-supports-0-as.html -# Because BASH internally uses C-style strings, in which '\0' is the terminator, -# read -d $'\0' is essentially equivalent to read -d ''. This is why I believed -# read did not accept null-delimited strings. However, it turns out that BASH -# actually handles this correctly. -# I checked BASH’s source code and found the delimiter was simply determined by -# delim = *list_optarg; (bash-3.2/builtins/read.def, line 296) where list_optarg -# points to the argument following -d. Therefore, it makes no difference to the -# value of delim whether $'\0' or '' is used. - ## note: need to figure this one out, and ideally clean it up and make it readable - while read -d $'\0' L && [[ $i -lt 32 ]] - do - A_CMDL[i++]="$L" ## note: make sure this is valid - What does L mean? ## - done < /proc/$ppid/cmdline - ##print_screen_output "\$i='$i'" - if [[ $i -eq 0 ]];then - A_CMDL[0]=$(< /proc/$ppid/cmdline) - if [[ -n ${A_CMDL[0]} ]];then - i=1 - fi - fi - CMDL_MAX=$i - log_function_data "CMDL_MAX: $CMDL_MAX" - eval $LOGFE -} - -# Determine where inxi was run from, set IRC_CLIENT and IRC_CLIENT_VERSION -get_start_client() -{ - eval $LOGFS - local Irc_Client_Path='' irc_client_path_lower='' non_native_konvi='' i='' - local B_Non_Native_App='false' pppid='' App_Working_Name='' - local b_qt4_konvi='false' ps_parent='' - - if [[ $B_IRC == 'false' ]];then - IRC_CLIENT='Shell' - unset IRC_CLIENT_VERSION - # elif [[ -n $PPID ]];then - elif [[ -n $PPID && -f /proc/$PPID/exe ]];then - if [[ $B_OVERRIDE_FILTER != 'true' ]];then - B_OUTPUT_FILTER='true' - fi - Irc_Client_Path=$( readlink /proc/$PPID/exe ) - # Irc_Client_Path=$( ps -p $PPID | gawk '!/[[:space:]]*PID/ {print $5}' ) - # echo $( ps -p $PPID ) - if (( "$BASH" >= 4 ));then - irc_client_path_lower=${Irc_Client_Path,,} - else - irc_client_path_lower=$( tr '[A-Z]' '[a-z]' <<< "$Irc_Client_Path" ) - fi - - App_Working_Name=${irc_client_path_lower##*/} - # handles the xchat/sh/bash/dash cases, and the konversation/perl cases, where clients - # report themselves as perl or unknown shell. IE: when konversation starts inxi - # from inside itself, as a script, the parent is konversation/xchat, not perl/bash etc - # note: perl can report as: perl5.10.0, so it needs wildcard handling - case $App_Working_Name in - # bsd will never use this section - bash|dash|sh|python*|perl*) # We want to know who wrapped it into the shell or perl. - if [[ $BSD_TYPE != 'bsd' ]];then - pppid=$( ps -p $PPID -o ppid --no-headers 2>/dev/null | gawk '{print $NF}' ) - else - # without --no-headers we need the second line - pppid=$( ps -p $PPID -o ppid 2>/dev/null | gawk '$1 ~ /^[0-9]+/ {print $5}' ) - fi - if [[ -n $pppid && -f /proc/$pppid/exe ]];then - Irc_Client_Path="$( readlink /proc/$pppid/exe )" - if (( "$BASH" >= 4 ));then - irc_client_path_lower=${Irc_Client_Path,,} - else - irc_client_path_lower=$( tr '[A-Z]' '[a-z]' <<< "$Irc_Client_Path" ) - fi - App_Working_Name=${irc_client_path_lower##*/} - B_Non_Native_App='true' - fi - ;; - esac - # sets version number if it can find it - get_irc_client_version - else - ## lets look to see if qt4_konvi is the parent. There is no direct way to tell, so lets infer it. - ## because $PPID does not work with qt4_konvi, the above case does not work - if [[ $B_OVERRIDE_FILTER != 'true' ]];then - B_OUTPUT_FILTER='true' - fi - b_qt4_konvi=$( is_this_qt4_konvi ) - if [[ $b_qt4_konvi == 'true' ]];then - KONVI=3 - IRC_CLIENT='Konversation' - IRC_CLIENT_VERSION=" $( konversation -v | gawk ' - /Konversation:/ { - for ( i=2; i<=NF; i++ ) { - if (i == NF) { - print $i - } - else { - printf $i" " - } - } - exit - }' )" - else - # this should handle certain cases where it's ssh or some other startup tool - # that falls through all the other tests. Also bsd irc clients will land here - if [[ $BSD_TYPE != 'bsd' ]];then - App_Working_Name=$(ps -p $PPID --no-headers 2>/dev/null | gawk '{print $NF}' ) - else - # without --no-headers we need the second line - App_Working_Name=$(ps -p $PPID 2>/dev/null | gawk '$1 ~ /^[0-9]+/ {print $5}' ) - fi - - if [[ -n $App_Working_Name ]];then - Irc_Client_Path=$App_Working_Name - if (( "$BASH" >= 4 ));then - irc_client_path_lower=${Irc_Client_Path,,} - else - irc_client_path_lower=$( tr '[A-Z]' '[a-z]' <<< "$Irc_Client_Path" ) - fi - App_Working_Name=${irc_client_path_lower##*/} - B_Non_Native_App='false' - get_irc_client_version - if [[ -z $IRC_CLIENT ]];then - IRC_CLIENT=$App_Working_Name - fi - else - IRC_CLIENT="PPID=\"$PPID\" - empty?" - unset IRC_CLIENT_VERSION - fi - fi - fi - - log_function_data "IRC_CLIENT: $IRC_CLIENT :: IRC_CLIENT_VERSION: $IRC_CLIENT_VERSION :: PPID: $PPID" - eval $LOGFE -} - -# note: all variables set in caller so no need to pass -get_irc_client_version() +# StartClient { - local file_data='' - # replacing loose detection with tight detection, bugs will be handled with app names - # as they appear. - case $App_Working_Name in - # check for shell first - bash|dash|sh) - unset IRC_CLIENT_VERSION - IRC_CLIENT="Shell wrapper" - ;; - # now start on irc clients, alphabetically - bitchx) - IRC_CLIENT_VERSION=" $( $Irc_Client_Path -v | gawk ' - /Version/ { - a=tolower($2) - gsub(/[()]|bitchx-/,"",a) - print a - exit - } - $2 == "version" { - a=tolower($3) - sub(/bitchx-/,"",a) - print a - exit - }' )" - B_CONSOLE_IRC='true' - IRC_CLIENT="BitchX" - ;; - finch) - IRC_CLIENT_VERSION=" $( $Irc_Client_Path -v | gawk 'NR == 1 { - print $2 - }' )" - B_CONSOLE_IRC='true' - IRC_CLIENT="Finch" - ;; - gaim) - IRC_CLIENT_VERSION=" $( $Irc_Client_Path -v | gawk 'NR == 1 { - print $2 - }' )" - IRC_CLIENT="Gaim" - ;; - hexchat) - # the hexchat author decided to make --version/-v return a gtk dialogue box, lol... - # so we need to read the actual config file for hexchat. Note that older hexchats - # used xchat config file, so test first for default, then legacy. Because it's possible - # for this file to be user edited, doing some extra checks here. - if [[ -f ~/.config/hexchat/hexchat.conf ]];then - file_data="$( cat ~/.config/hexchat/hexchat.conf )" - elif [[ -f ~/.config/hexchat/xchat.conf ]];then - file_data="$( cat ~/.config/hexchat/xchat.conf )" - fi - if [[ -n $file_data ]];then - IRC_CLIENT_VERSION=$( gawk ' - BEGIN { - IGNORECASE=1 - FS="=" - } - /^[[:space:]]*version/ { - # get rid of the space if present - gsub(/[[:space:]]*/, "", $2 ) - print $2 - exit # usually this is the first line, no point in continuing - }' <<< "$file_data" ) - - IRC_CLIENT_VERSION=" $IRC_CLIENT_VERSION" - else - IRC_CLIENT_VERSION=' N/A' - fi - IRC_CLIENT="HexChat" - ;; - ircii) - IRC_CLIENT_VERSION=" $( $Irc_Client_Path -v | gawk 'NR == 1 { - print $3 - }' )" - B_CONSOLE_IRC='true' - IRC_CLIENT="ircII" - ;; - irssi|irssi-text) - IRC_CLIENT_VERSION=" $( $Irc_Client_Path -v | gawk 'NR == 1 { - print $2 - }' )" - B_CONSOLE_IRC='true' - IRC_CLIENT="Irssi" - ;; - konversation) ## konvi < 1.2 (qt4) - # this is necessary to avoid the dcop errors from starting inxi as a /cmd started script - if [[ $B_Non_Native_App == 'true' ]];then ## true negative is confusing - KONVI=2 - else # if native app - KONVI=1 - fi - IRC_CLIENT_VERSION=" $( $Irc_Client_Path -v | gawk ' - /Konversation:/ { - for ( i=2; i<=NF; i++ ) { - if (i == NF) { - print $i - } - else { - printf $i" " - } +package StartClient; + +# use warnings; +# use strict; + +my $ppid = ''; +my $pppid = ''; + +# NOTE: there's no reason to crete an object, we can just access +# the features statically. +# args: none +# sub new { +# my $class = shift; +# my $self = {}; +# # print "$f\n"; +# # print "$type\n"; +# return bless $self, $class; +# } + +sub get_client_data { + eval $start if $b_log; + $ppid = getppid(); + main::set_ps_aux() if ! @ps_aux; + if (!$b_irc){ + main::get_shell_data($ppid); + } + else { + $show{'filter'} = 1; + get_client_name(); + if ($client{'konvi'} == 1 || $client{'konvi'} == 3){ + set_konvi_data(); + } + } + eval $end if $b_log; +} + +sub get_client_name { + eval $start if $b_log; + my $client_name = ''; + + # print "$ppid\n"; + if ($ppid && -e "/proc/$ppid/exe" ){ + $client_name = lc(readlink "/proc/$ppid/exe"); + $client_name =~ s/^.*\///; + if ($client_name =~ /^bash|dash|sh|python.*|perl.*$/){ + $pppid = (main::grabber("ps -p $ppid -o ppid"))[1]; + #my @temp = (main::grabber("ps -p $ppid -o ppid 2>/dev/null"))[1]; + $pppid =~ s/^\s+|\s+$//g; + $client_name =~ s/[0-9\.]+$//; # clean things like python2.7 + if ($pppid && -f "/proc/$pppid/exe" ){ + $client_name = lc(readlink "/proc/$pppid/exe"); + $client_name =~ s/^.*\///; + $client{'native'} = 0; + } + } + $client{'name'} = $client_name; + get_client_version(); + # print "c:$client_name p:$pppid\n"; + } + else { + if (! check_modern_konvi() ){ + $ppid = getppid(); + $client_name = (main::grabber("ps -p $ppid"))[1]; + if ($client_name){ + my @data = split /\s+/, $client_name if $client_name; + if ($bsd_type){ + $client_name = lc($data[5]); } - exit - }' )" - T=($IRC_CLIENT_VERSION) - if [[ ${T[0]} == *+* ]];then - # < Sho_> locsmif: The version numbers of SVN versions look like this: - # "<version number of last release>+ #<build number", i.e. "1.0+ #3177" ... - # for releases we remove the + and build number, i.e. "1.0" or soon "1.0.1" - IRC_CLIENT_VERSION=" CVS $IRC_CLIENT_VERSION" - T2="${T[0]/+/}" - else - IRC_CLIENT_VERSION=" ${T[0]}" - T2="${T[0]}" - fi - # Remove any dots except the first, and make sure there are no trailing zeroes, - T2=$( echo "$T2" | gawk '{ - sub(/\./, " ") - gsub(/\./, "") - sub(/ /, ".") - printf("%g\n", $0) - }' ) - # Since Konversation 1.0, the DCOP interface has changed a bit: dcop "$DCPORT" Konversation ..etc - # becomes : dcop "$DCPORT" default ... or dcop "$DCPORT" irc ..etc. So we check for versions smaller - # than 1 and change the DCOP parameter/object accordingly. - if [[ $T2 -lt 1 ]];then - DCOPOBJ="Konversation" - fi - IRC_CLIENT="Konversation" - ;; - kopete) - IRC_CLIENT_VERSION=" $( kopete -v | gawk ' - /Kopete:/ { - print $2 - exit - }' )" - IRC_CLIENT="Kopete" - ;; - kvirc) - IRC_CLIENT_VERSION=" $( $Irc_Client_Path -v 2>&1 | gawk '{ - for ( i=2; i<=NF; i++) { - if ( i == NF ) { - print $i - } - else { - printf $i" " - } + # gnu/linux uses last value + else { + $client_name = lc($data[-1]); } - exit - }' )" - IRC_CLIENT="KVIrc" - ;; - pidgin) - IRC_CLIENT_VERSION=" $( $Irc_Client_Path -v | gawk 'NR == 1 { - print $2 - }' )" - IRC_CLIENT="Pidgin" - ;; - # possible failure of wildcard so make it explicit - quassel*) - # sample: quassel -v - ## Qt: 4.5.0 - in Qt4 the output came from src/common/quassel.cpp - # KDE: 4.2.65 (KDE 4.2.65 (KDE 4.3 >= 20090226)) - # Quassel IRC: v0.4.0 [+60] (git-22effe5) - # note: early < 0.4.1 quassels do not have -v - ## Qt: 5: sample: quassel -v - # quassel v0.13-pre (0.12.0+5 git-8e2f578) - # because in Qt5 the internal CommandLineParser is used - IRC_CLIENT_VERSION=" $( $Irc_Client_Path -v 2>/dev/null | gawk ' - BEGIN { - IGNORECASE=1 - clientVersion="" - } - # qt 4 -v syntax - /^Quassel IRC:/ { - clientVersion = $3 - } - # qt 5 -v syntax - /^quassel\s[v]?[0-9]/ { - clientVersion = $2 - } - END { - # this handles pre 0.4.1 cases with no -v - if ( clientVersion == "" ) { - clientVersion = "(pre v0.4.1)?" - } - print clientVersion - }' )" - # now handle primary, client, and core. quasselcore doesn't actually - # handle scripts with exec, but it's here just to be complete - case $App_Working_Name in - quassel) - IRC_CLIENT="Quassel [M]" - ;; - quasselclient) - IRC_CLIENT="Quassel" - ;; - quasselcore) - IRC_CLIENT="Quassel (core)" - ;; - esac - ;; - gribble|limnoria|supybot) - IRC_CLIENT_VERSION=" $( get_program_version 'supybot' '^Supybot' '2' )" - if [[ -n $IRC_CLIENT_VERSION ]];then - if [[ -n ${IRC_CLIENT_VERSION/*gribble*/} || $App_Working_Name == 'gribble' ]];then - IRC_CLIENT="Gribble" - elif [[ -n ${IRC_CLIENT_VERSION/*limnoria*/} || $App_Working_Name == 'limnoria' ]];then - IRC_CLIENT="Limnoria" - else - IRC_CLIENT="Supybot" - fi - fi - ;; - weechat|weechat-curses) - IRC_CLIENT_VERSION=" $( $Irc_Client_Path -v ) " - B_CONSOLE_IRC='true' - IRC_CLIENT="WeeChat" - ;; - xchat-gnome) - IRC_CLIENT_VERSION=" $( $Irc_Client_Path -v | gawk 'NR == 1 { - print $2 - }' )" - IRC_CLIENT="X-Chat-Gnome" - ;; - xchat) - IRC_CLIENT_VERSION=" $( $Irc_Client_Path -v | gawk 'NR == 1 { - print $2 - }' )" - IRC_CLIENT="X-Chat" - ;; - # then do some perl type searches, do this last since it's a wildcard search - perl*|ksirc|dsirc) - unset IRC_CLIENT_VERSION - # KSirc is one of the possibilities now. KSirc is a wrapper around dsirc, a perl client - get_cmdline $PPID - for (( i=0; i <= $CMDL_MAX; i++ )) - do - case ${A_CMDL[i]} in - *dsirc*) - IRC_CLIENT="KSirc" - # Dynamic runpath detection is too complex with KSirc, because KSirc is started from - # kdeinit. /proc/<pid of the grandparent of this process>/exe is a link to /usr/bin/kdeinit - # with one parameter which contains parameters separated by spaces(??), first param being KSirc. - # Then, KSirc runs dsirc as the perl irc script and wraps around it. When /exec is executed, - # dsirc is the program that runs inxi, therefore that is the parent process that we see. - # You can imagine how hosed I am if I try to make inxi find out dynamically with which path - # KSirc was run by browsing up the process tree in /proc. That alone is straightjacket material. - # (KSirc sucks anyway ;) - IRC_CLIENT_VERSION=" $( ksirc -v | gawk ' - /KSirc:/ { - print $2 - exit - }' )" - break - ;; - esac - done - B_CONSOLE_IRC='true' - set_perl_python_client_data "$App_Working_Name" - ;; - python*) - # B_CONSOLE_IRC='true' # are there even any python type console irc clients? check. - set_perl_python_client_data "$App_Working_Name" - ;; - # then unset, set unknown data - *) - IRC_CLIENT="Unknown : ${Irc_Client_Path##*/}" - unset IRC_CLIENT_VERSION - ;; - esac - if [[ $SHOW_IRC -lt 2 ]];then - unset IRC_CLIENT_VERSION - fi + $client_name =~ s/.*\|-(|)//; + $client_name =~ s/[0-9\.]+$//; # clean things like python2.7 + $client{'name'} = $client_name; + $client{'native'} = 1; + get_client_version(); + } + else { + $client{'name'} = "PPID='$ppid' - Empty?"; + } + } + } + if ($b_log){ + my $string = "Client: $client{'name'} :: version: $client{'version'} :: konvi: $client{'konvi'} :: PPID: $ppid"; + main::log_data('data', $string); + } + eval $end if $b_log; +} +sub get_client_version { + eval $start if $b_log; + @app = main::program_values($client{'name'}); + my (@data,@working,$string); + if (@app){ + $string = ($client{'name'} =~ /^gribble|limnoria|supybot$/) ? 'supybot' : $client{'name'}; + $client{'version'} = main::program_version($string,$app[0],$app[1],$app[2],$app[4],$app[5],$app[6]); + $client{'name-print'} = $app[3]; + $client{'console-irc'} = $app[4]; + } + if ($client{'name'} =~ /^bash|dash|sh$/ ){ + $client{'name-print'} = 'shell wrapper'; + $client{'console-irc'} = 1; + } + elsif ($client{'name'} eq 'bitchx') { + @data = main::grabber("$client{'name'} -v"); + $string = awk(\@data,'Version'); + if ($string){ + $string =~ s/[()]|bitchx-//g; + @data = split /\s+/, $string; + $_=lc for @data; + $client{'version'} = ($data[1] eq 'version') ? $data[2] : $data[1]; + } + } + # 'hexchat' => ['',0,'','HexChat',0,0], # special + # the hexchat author decided to make --version/-v return a gtk dialogue box, lol... + # so we need to read the actual config file for hexchat. Note that older hexchats + # used xchat config file, so test first for default, then legacy. Because it's possible + # for this file to be user edited, doing some extra checks here. + elsif ($client{'name'} eq 'hexchat') { + if ( -f '~/.config/hexchat/hexchat.conf' ){ + @data = main::reader('~/.config/hexchat/hexchat.conf','strip'); + } + elsif ( -f '~/.config/hexchat/xchat.conf' ){ + @data = main::reader('~/.config/hexchat/xchat.conf','strip'); + } + $client{'version'} = main::awk(\@data,'version',2,'\s*=\s*'); + $client{'name-print'} = 'HexChat'; + } + # note: see legacy inxi konvi logic if we need to restore any of the legacy code. + elsif ($client{'name'} eq 'konversation') { + $client{'konvi'} = ( ! $client{'native'} ) ? 2 : 1; + } + elsif ($client{'name'} =~ /quassel/) { + @data = main::grabber("$client{'name'} -v 2>/dev/null"); + foreach (@data){ + if ($_ =~ /^Quassel IRC:/){ + $client{'version'} = (split /\s+/, $_ )[2]; + last; + } + elsif ($_ =~ /quassel\s[v]?[0-9]/){ + $client{'version'} = (split /\s+/, $_ )[1]; + last; + } + } + $client{'version'} ||= '(pre v0.4.1)?'; + } + # then do some perl type searches, do this last since it's a wildcard search + elsif ($client{'name'} =~ /^perl.*|ksirc|dsirc$/ ) { + my @cmdline = main::get_cmdline(); + # Dynamic runpath detection is too complex with KSirc, because KSirc is started from + # kdeinit. /proc/<pid of the grandparent of this process>/exe is a link to /usr/bin/kdeinit + # with one parameter which contains parameters separated by spaces(??), first param being KSirc. + # Then, KSirc runs dsirc as the perl irc script and wraps around it. When /exec is executed, + # dsirc is the program that runs inxi, therefore that is the parent process that we see. + # You can imagine how hosed I am if I try to make inxi find out dynamically with which path + # KSirc was run by browsing up the process tree in /proc. That alone is straightjacket material. + # (KSirc sucks anyway ;) + foreach (@cmdline){ + if ( $_ =~ /dsirc/ ){ + $client{'version'} = main::program_version('ksirc','KSirc:',2,'-v',0,0); + $client{'name'} = 'ksirc'; + $client{'name-print'} = 'KSirc'; + } + } + $client{'console-irc'} = 1; + perl_python_client(); + } + elsif ($client{'name'} =~ /python/) { + perl_python_client(); + } + if (!$client{'name-print'}) { + $client{'name-print'} = 'Unknown Client: ' . $client{'name'}; + } + eval $end if $b_log; +} +sub get_cmdline { + eval $start if $b_log; + my @cmdline; + my $i = 0; + $ppid = getppid(); + if (! -e "/proc/$ppid/cmdline" ){ + return 1; + } + local $\ = ''; + open( my $fh, '<', "/proc/$ppid/cmdline" ) or + print_line("Open /proc/$ppid/cmdline failed: $!"); + my @rows = <$fh>; + close $fh; + + foreach (@rows){ + push @cmdline, $_; + $i++; + last if $i > 31; + } + if ( $i == 0 ){ + $cmdline[0] = $rows[0]; + $i = ($cmdline[0]) ? 1 : 0; + } + main::log_data('string',"cmdline: @cmdline count: $i") if $b_log; + eval $end if $b_log; + return @cmdline; +} +sub perl_python_client { + eval $start if $b_log; + return 1 if $client{'version'}; + # this is a hack to try to show konversation if inxi is running but started via /cmd + # OR via program shortcuts, both cases in fact now + # main::print_line("konvi: " . scalar grep { $_ =~ /konversation/ } @ps_cmd); + if ( $b_display && main::check_program('konversation') && ( grep { $_ =~ /konversation/ } @ps_cmd )){ + @app = main::program_values('konversation'); + $client{'version'} = main::program_version('konversation',$app[0],$app[1],$app[2],$app[5],$app[6]); + $client{'name'} = 'konversation'; + $client{'name-print'} = $app[3]; + $client{'console-irc'} = $app[4]; + } + ## NOTE: supybot only appears in ps aux using 'SHELL' command; the 'CALL' command + ## gives the user system irc priority, and you don't see supybot listed, so use SHELL + elsif ( !$b_display && + (main::check_program('supybot') || main::check_program('gribble') || main::check_program('limnoria')) && + ( grep { $_ =~ /supybot/ } @ps_cmd ) ){ + @app = main::program_values('supybot'); + $client{'version'} = main::program_version('supybot',$app[0],$app[1],$app[2],$app[5],$app[6]); + if ($client{'version'}){ + if ( grep { $_ =~ /gribble/ } @ps_cmd ){ + $client{'name'} = 'gribble'; + $client{'name-print'} = 'Gribble'; + } + if ( grep { $_ =~ /limnoria/ } @ps_cmd){ + $client{'name'} = 'limnoria'; + $client{'name-print'} = 'Limnoria'; + } + else { + $client{'name'} = 'supybot'; + $client{'name-print'} = 'Supybot'; + } + } + else { + $client{'name'} = 'supybot'; + $client{'name-print'} = 'Supybot'; + } + $client{'console-irc'} = 1; + } + else { + $client{'name-print'} = "Unknown $client{'name'} client"; + } + if ($b_log){ + my $string = "namep: $client{'name-print'} name: $client{'name'} version: $client{'version'}"; + main::log_data('data',$string); + } + eval $end if $b_log; } - ## try to infer the use of Konversation >= 1.2, which shows $PPID improperly ## no known method of finding Konvi >= 1.2 as parent process, so we look to see if it is running, ## and all other irc clients are not running. As of 2014-03-25 this isn't used in my cases -is_this_qt4_konvi() -{ - local konvi_qt4_client='' konvi_dbus_exist='' konvi_pid='' konvi_home_dir='' - local konvi='' b_is_qt4='' - - # fringe cases can throw error, always if untested app, use 2>/dev/null after testing if present - if [[ $B_QDBUS == 'true' ]];then - konvi_dbus_exist=$( qdbus 2>/dev/null | grep "org.kde.konversation" ) - fi +sub check_modern_konvi { + eval $start if $b_log; + + return 0 if ! $client{'qdbus'}; + my $b_modern_konvi = 0; + my $konvi_version = ''; + my $konvi = ''; + my $pid = ''; + my (@temp); + # main::log_data('data',"name: $client{'name'} :: qdb: $client{'qdbus'} :: version: $client{'version'} :: konvi: $client{'konvi'} :: PPID: $ppid") if $b_log; # sabayon uses /usr/share/apps/konversation as path - if [[ -n $konvi_dbus_exist ]] && [[ -e /usr/share/kde4/apps/konversation || -e /usr/share/apps/konversation ]]; then - konvi_pid=$( ps -A | gawk 'BEGIN{IGNORECASE=1} /konversation/ { print $1 }' ) - konvi_home_dir=$( readlink /proc/$konvi_pid/exe ) - konvi=$( echo $konvi_home_dir | sed "s/\// /g" ) - konvi=($konvi) - - if [[ ${konvi[2]} == 'konversation' ]];then + if ( -d '/usr/share/kde4/apps/konversation' || -d '/usr/share/apps/konversation' ){ + $pid = main::awk(\@ps_aux,'konversation',2,'\s+'); + main::log_data('data',"pid: $pid") if $b_log; + $konvi = readlink ("/proc/$pid/exe"); + $konvi =~ s/^.*\///; # basename + @app = main::program_values('konversation'); + if ($konvi){ + @app = main::program_values('konversation'); + $konvi_version = main::program_version($konvi,$app[0],$app[1],$app[2],$app[5],$app[6]); + @temp = split /\./, $konvi_version; + $client{'console-irc'} = $app[4]; + $client{'konvi'} = 3; + $client{'name'} = 'konversation'; + $client{'name-print'} = $app[3]; + $client{'version'} = $konvi_version; # note: we need to change this back to a single dot number, like 1.3, not 1.3.2 - konvi_qt4_client=$( konversation -v | grep -i 'konversation' | \ - gawk '{ print $2 }' | cut -d '.' -f 1,2 ) - if [[ $konvi_qt4_client > 1.1 ]]; then - b_is_qt4='true' - fi - fi - else - konvi_qt4="qt3" - b_is_qt4='false' - fi - log_function_data "b_is_qt4: $b_is_qt4" - echo $b_is_qt4 + $konvi_version = $temp[0] . "." . $temp[1]; + if ($konvi_version > 1.1){ + $b_modern_konvi = 1; + } + } + } + main::log_data('data',"name: $client{'name'} name print: $client{'name-print'} + qdb: $client{'qdbus'} version: $konvi_version konvi: $konvi PID: $pid") if $b_log; + main::log_data('data',"b_is_qt4: $b_modern_konvi") if $b_log; ## for testing this module - #qdbus org.kde.konversation /irc say $1 $2 "getpid_dir: $konvi_qt4 qt4_konvi: $konvi_qt4_ver verNum: $konvi_qt4_ver_num pid: $konvi_pid ppid: $PPID konvi_home_dir: ${konvi[2]}" +# my $ppid = getppid(); +# system('qdbus org.kde.konversation', '/irc', 'say', $client{'dserver'}, $client{'dtarget'}, +# "getpid_dir: $konvi_qt4 verNum: $konvi_version pid: $pid ppid: $ppid" ); + eval $end if $b_log; + return $b_modern_konvi; } -# args: $1 - App_Working_Name -set_perl_python_client_data() -{ - if [[ -z $IRC_CLIENT_VERSION ]];then - local version='' - # this is a hack to try to show konversation if inxi is running but started via /cmd - # OR via script shortcuts, both cases in fact now - if [[ $B_RUNNING_IN_DISPLAY == 'true' && -z ${Ps_aux_Data/*konversation*/} ]];then - IRC_CLIENT='Konversation' - version=$( get_program_version 'konversation' '^konversation' '2' ) - B_CONSOLE_IRC='false' - ## NOTE: supybot only appears in ps aux using 'SHELL' command; the 'CALL' command - ## gives the user system irc priority, and you don't see supybot listed, so use SHELL - elif [[ $B_RUNNING_IN_DISPLAY == 'false' && -z ${Ps_aux_Data/*supybot*/} ]];then - version=$( get_program_version 'supybot' '^Supybot' '2' ) - if [[ -n $version ]];then - IRC_CLIENT_VERSION=" $version" - if [[ -z ${version/*gribble*/} ]];then - IRC_CLIENT='Gribble' - elif [[ -z ${version/*limnoria*/} ]];then - IRC_CLIENT='Limnoria' - else - IRC_CLIENT='Supybot' - fi - else - IRC_CLIENT='Supybot' - # currently all use the same actual app name, this will probably change. - fi - B_CONSOLE_IRC='true' - else - IRC_CLIENT="Unknown $1 client" - fi - if [[ -n $version ]];then - IRC_CLIENT_VERSION=" $version" - fi - fi +sub set_konvi_data { + eval $start if $b_log; + my $config_tool = ''; + # https://userbase.kde.org/Konversation/Scripts/Scripting_guide + if ( $client{'konvi'} == 3 ){ + $client{'dserver'} = shift @ARGV; + $client{'dtarget'} = shift @ARGV; + $client{'dobject'} = 'default'; + } + elsif ( $client{'konvi'} == 1 ){ + $client{'dport'} = shift @ARGV; + $client{'dserver'} = shift @ARGV; + $client{'dtarget'} = shift @ARGV; + $client{'dobject'} = 'Konversation'; + } + # for some reason this logic hiccups on multiple spaces between args + @ARGV = grep { $_ ne '' } @ARGV; + # there's no current kde 5 konvi config tool that we're aware of. Correct if changes. + if ( main::check_program('kde4-config') ){ + $config_tool = 'kde4-config'; + } + elsif ( main::check_program('kde5-config') ){ + $config_tool = 'kde5-config'; + } + elsif ( main::check_program('kde-config') ){ + $config_tool = 'kde-config'; + } + # The section below is on request of Argonel from the Konversation developer team: + # it sources config files like $HOME/.kde/share/apps/konversation/scripts/inxi.conf + if ($config_tool){ + my @data = main::grabber("$config_tool --path data 2>/dev/null",':'); + main::get_configs(@data); + } + eval $end if $b_log; +} } ######################################################################## -#### DATA PROCESSORS +#### OUTPUT ######################################################################## #### ------------------------------------------------------------------- -#### GET DATA +#### FILTERS AND TOOLS #### ------------------------------------------------------------------- -## create array of sound cards installed on system, and if found, use asound data as well -get_audio_data() -{ - eval $LOGFS - local i='' alsa_data='' audio_driver='' device_count='' a_temp='' +sub apply_filter { + my ($string) = @_; + if ($string){ + $string = ( $show{'filter'} ) ? $filter_string : $string; + } + else { + $string = 'N/A'; + } + return $string; +} - IFS=$'\n' - # this first step handles the drivers for cases where the second step fails to find one - device_count=$( echo "$LSPCI_V_DATA" | grep -iEc '(multimedia audio controller|audio device)' ) - if [[ $device_count -eq 1 ]] && [[ $B_ASOUND_DEVICE_FILE == 'true' ]];then - audio_driver=$( gawk -F ']: ' ' - BEGIN { - IGNORECASE=1 +sub clean_characters { + my ($data) = @_; + # newline, pipe, brackets, + sign, with space, then clear doubled + # spaces and then strip out trailing/leading spaces. + $data =~ s/\n|\|\+|\[\s\]|\s\s+/ /g if $data; + $data =~ s/^\s+|\s+$//g if $data; + return $data; +} + +sub cleaner { + my ($item) = @_; + return $item if !$item;# handle cases where it was 0 or '' + $item =~ s/chipset|components|computing|computer|corporation|communications|electronics|electrical|electric|gmbh|group|incorporation|industrial|international|nee|revision|semiconductor|software|technologies|technology|ltd\.|<ltd>|\bltd\b|inc\.|<inc>|\binc\b|intl\.|co\.|<co>|corp\.|<corp>|\(tm\)|\(r\)|®|\(rev ..\)|\'|\"|\sinc\s*$|\?//gi; + $item =~ s/,|\*/ /g; + $item =~ s/\s\s+/ /g; + $item =~ s/^\s+|\s+$//g; + return $item; +} + +sub dmi_cleaner { + my ($string) = @_; + my $cleaner = '^Base Board .*|^Chassis .*|empty|Undefined.*|.*O\.E\.M\..*|.*OEM.*|^Not .*'; + $cleaner .= '|^System .*|.*unknow.*|.*N\/A.*|none|^To be filled.*|^0x[0]+$'; + $cleaner .= '|\[Empty\]|<Bad Index>|Default string|^\.\.$|Manufacturer.*'; + $cleaner .= '|AssetTagNum|Manufacturer| Or Motherboard|PartNum.*|SerNum'; + $string =~ s/$cleaner//i; + $string =~ s/^\s+|\bbios\b|\bacpi\b|\s+$//gi; + $string =~ s/http:\/\/www.abit.com.tw\//Abit/i; + $string =~ s/\s\s+/ /g; + $string =~ s/^\s+|\s+$//g; + $string = remove_duplicates($string) if $string; + return $string; +} +sub remove_duplicates { + my ($string) = @_; + return if ! $string; + my $holder = ''; + my (@temp); + my @data = split /\s+/, $string; + foreach (@data){ + if ($holder ne $_){ + push @temp, $_; } - # filtering out modems and usb devices like webcams, this might get a - # usb audio card as well, this will take some trial and error - $0 !~ /modem|usb|webcam/ { - driver=gensub( /^(.+)( - )(.+)$/, "\\1", 1, $2 ) - gsub(/^ +| +$/,"",driver) - if ( driver != "" ){ - print driver - } - }' $FILE_ASOUND_DEVICE ) - log_function_data 'cat' "$FILE_ASOUND_DEVICE" - fi - - # this is to safeguard against line breaks from results > 1, which if inserted into following - # array will create a false array entry. This is a hack, not a permanent solution. - audio_driver=$( echo $audio_driver ) - # now we'll build the main audio data, card name, driver, and port. If no driver is found, - # and if the first method above is not null, and one card is found, it will use that instead. - A_AUDIO_DATA=( $( echo "$LSPCI_V_DATA" | gawk -F ': ' -v audioDriver="$audio_driver" ' - BEGIN { - IGNORECASE=1 - } - /multimedia audio controller|audio device/ { - audioCard=gensub(/^[0-9a-f:\.]+ [^:]+: (.+)$/,"\\1","g",$0) - # The doublequotes are necessary because of the pipes in the variable. - gsub(/'"$BAN_LIST_NORMAL"'/, "", audioCard) - gsub(/'"$BAN_LIST_ARRAY"'/, " ", audioCard) - if ( '$COLS_INNER' < 100 ){ - sub(/Series Family/,"Series", audioCard) - sub(/High Definition/,"High Def.", audioCard) - } - gsub(/^ +| +$/, "", audioCard) - gsub(/ [ \t]+/, " ", audioCard) - aPciBusId[audioCard] = gensub(/(^[0-9a-f:\.]+) [^:]+: .+$/,"\\1","g",$0) - cards[audioCard]++ - - # loop until you get to the end of the data block - while (getline && !/^$/) { - gsub(/'"$BAN_LIST_ARRAY"'/, "", $0 ) - if (/driver in use/) { - drivers[audioCard] = drivers[audioCard] gensub( /(.*): (.*)/ ,"\\2", "g" ,$0 ) "" - } - else if (/kernel modules:/) { - modules[audioCard] = modules[audioCard] gensub( /(.*): (.*)/ ,"\\2" ,"g" ,$0 ) "" - } - else if (/^[[:space:]]*I\/O/) { - portsTemp = gensub(/\t*I\/O ports at ([a-z0-9]+)(| \[.*\])/,"\\1","g",$0) - ports[audioCard] = ports[audioCard] portsTemp " " - } - } - } - - END { - j=0 - for (i in cards) { - useDrivers="" - useModules="" - usePorts="" - usePciBusId="" - - if (cards[i]>1) { - a[j]=cards[i]"x "i - if (drivers[i] != "") { - useDrivers=drivers[i] - } - } - else { - a[j]=i - # little trick here to try to catch the driver if there is - # only one card and it was null, from the first test of asound/cards - if (drivers[i] != "") { - useDrivers=drivers[i] + $holder = $_; + } + $string = join ' ', @temp; + return $string; +} + +# args: $1 - size in KB, return KB, MB, GB, TB, PB, EB +sub get_size { + my ($size,$b_int) = @_; + my (@data); + return ('','') if ! defined $size; + if ($size !~ /^[0-9\.]+$/){ + $data[0] = $size; + $data[1] = ''; + } + elsif ($size > 1024**5){ + $data[0] = sprintf("%.2f",$size/1024**5); + $data[1] = 'EiB'; + } + elsif ($size > 1024**4){ + $data[0] = sprintf("%.2f",$size/1024**4); + $data[1] = 'PiB'; + } + elsif ($size > 1024**3){ + $data[0] = sprintf("%.2f",$size/1024**3); + $data[1] = 'TiB'; + } + elsif ($size > 1024**2){ + $data[0] = sprintf("%.2f",$size/1024**2); + $data[1] = 'GiB'; + } + elsif ($size > 1024){ + $data[0] = sprintf("%.1f",$size/1024); + $data[1] = 'MiB'; + } + else { + $data[0] = sprintf("%.0f",$size); + $data[1] = 'KiB'; + } + $data[0] = int($data[0]) if $b_int && $data[0]; + return @data; +} + +# not used, but keeping logic for now +sub increment_starters { + my ($key,$indexes) = @_; + my $result = $key; + if (defined $$indexes{$key} ){ + $$indexes{$key}++; + $result = "$key-$$indexes{$key}"; + } + return $result; +} +sub memory_data_full { + eval $start if $b_log; + my ($source) = @_; + my $num = 0; + my ($memory,@rows); + my ($percent,$total,$used) = ('','',''); + if (!$show{'info'}){ + $memory = get_memory_data('splits'); + if ($memory){ + my @temp = split /:/, $memory; + my @temp2 = get_size($temp[0]); + $total = ($temp2[1]) ? $temp2[0] . ' ' . $temp2[1] : $temp2[0]; + @temp2 = get_size($temp[1]); + $used = ($temp2[1]) ? $temp2[0] . ' ' . $temp2[1] : $temp2[0]; + $used .= " ($temp[2]%)" if $temp[2]; + } + my $key = ($source eq 'process') ? 'System RAM': 'RAM'; + $rows[0]{main::key($num++,$key)} = ''; + $rows[0]{main::key($num++,'total')} = $total; + $rows[0]{main::key($num++,'used')} = $used; + } + $b_mem = 1; + eval $end if $b_log; + return @rows; +} + +sub pci_cleaner { + my ($string,$type) = @_; + #print "st1 $type:$string\n"; + my $filter = 'compatible\scontroller|\b(device|controller|multimedia)\b|\([^)]+\)'; + # \[[^\]]+\]$| not trimming off ending [...] initial type filters removes end + $filter = '\[[^\]]+\]$|' . $filter if $type eq 'pci'; + $string =~ s/$filter//ig; + $string =~ s/\s\s+/ /g; + $string =~ s/^\s+|\s+$//g; + #print "st2 $type:$string\n"; + $string = remove_duplicates($string) if $string; + return $string; +} + +sub pci_long_filter { + my ($string) = @_; + if ($string =~ /\[AMD(\/ATI)?\]/){ + $string =~ s/Advanced\sMicro\sDevices\s\[AMD(\/ATI)?\]/AMD/; + } + return $string; +} + +sub row_defaults { + my ($type,$id) = @_; + $id ||= ''; + my %unfound = ( + 'arm-cpu-f' => 'Use -f option to see features', + 'arm-pci' => "PCI data type is not supported on ARM systems.", + 'battery-data' => "No battery data found. Is one present?", + 'battery-data-sys' => "No /sys data found. Old system?", + 'cpu-model-null' => "Model N/A", + 'darwin-feature' => "Feature not supported iu Darwin/OSX.", + 'disk-data-bsd' => "No disk data found for this BSD system.", + 'disk-data' => "No Disk data was found.", + 'disk-size-0' => "Total N/A", + 'display-console' => 'No advanced graphics data found on this system in console.', + 'display-null' => 'No advanced graphics data found on this system.', + 'display-root' => 'Advanced graphics data unavailable in console for root.', + 'display-root-x' => 'Advanced graphics data unavailable for root. Old System?', + 'display-server' => "No display server data found. Headless server?", + 'glxinfo-missing' => "Unable to show advanced data. Required tool glxinfo missing.", + 'display-try' => 'Advanced graphics data unavailable in console. Try -G --display', + 'dev' => 'Feature under development', + 'dmesg-boot-permissions' => 'dmesg.boot permissions', + 'dmesg-boot-missing' => 'dmesg.boot not found', + 'IP' => "No $id data found. Connected to the web? SSL issues?", + 'machine-data' => "No machine data: try newer kernel.", + 'machine-data-alt-33' => "No machine data: try newer kernel. Is dmidecode installed? Try -M --dmidecode.", + 'machine-data-dmidecode' => "No machine data: try newer kernel, or install dmidecoce.", + 'optical-data' => "No Optical or Floppy data was found.", + 'optical-data-bsd' => "No floppy or optical data found for this BSD system.", + 'output-limit' => "Output throttled. IPs: $id; Limit: $limit; Override: --limit [1-x;-1 all]", + 'partition-data' => "No Partition data was found.", + 'pci-advanced-data' => 'bus/chip ids unavailable', + 'pci-card-data' => "No PCI card data found.", + 'pci-slot-data' => "No PCI slot data found. SOC?", + 'raid-data' => "No RAID data was found.", + 'ram-data' => "No RAM data was found. SOC?", + 'root-required' => "<root required>", + 'sensors-data-ipmi' => "No ipmi sensors data was found.", + 'sensors-data-linux' => "No sensors data was found. Is sensors configured?", + 'sensors-ipmi-root' => "Unable to run ipmi sensors. Are you root?", + 'unmounted-data' => "No unmounted partitions found.", + 'unmounted-data-bsd' => "No unmounted partition data found for this BSD system.", + 'unmounted-file' => "No /proc/partitions file found.", + 'usb-data' => "No USB data was found. Server?", + 'unknown-desktop-version' => "ERR-101", + 'unknown-dev' => "ERR-102", + 'unknown-shell' => "ERR-100", + 'weather-null' => "No $id found. Internet connection working?", + 'xdpyinfo-missing' => '<xdpyinfo missing>', + ); + return $unfound{$type}; +} +# convert string passed to KB, based on GB/MB/TB id +# NOTE: K 1024 KB 1000 +sub translate_size { + my ($working) = @_; + my $size = 0; + #print ":$working:\n"; + return if ! defined $working; + my $math = ( $working =~ /B$/) ? 1000: 1024; + if ( $working =~ /^([0-9\.]+)M[B]?$/i){ + $size = $1 * $math; + } + elsif ( $working =~ /^([0-9\.]+)G[B]?$/i){ + $size = $1 * $math**2; + } + elsif ( $working =~ /^([0-9\.]+)T[B]?$/i){ + $size = $1 * $math**3; + } + elsif ( $working =~ /^([0-9\.]+)P[B]?$/i){ + $size = $1 * $math**4; + } + elsif ( $working =~ /^([0-9\.]+)E[B]?$/i){ + $size = $1 * $math**5; + } + elsif ( $working =~ /^([0-9\.]+)K[B]?$/i){ + $size = $1; + } + $size = int($size) if $size; + return $size; +} + +#### ------------------------------------------------------------------- +#### GENERATE OUTPUT +#### ------------------------------------------------------------------- + +sub check_output_path { + my ($path) = @_; + my ($b_good,$dir,$file); + $dir = $path; + $dir =~ s/([^\/]+)$//; + $file = $1; + # print "file: $file : dir: $dir\n"; + $b_good = 1 if (-d $dir && -w $dir && $dir =~ /^\// && $file); + return $b_good; +} + +sub output_handler { + my (%data) = @_; + # print Dumper \%data; + if ($output_type eq 'screen'){ + print_data(%data); + } + elsif ($output_type eq 'json'){ + generate_json(%data); + } + elsif ($output_type eq 'xml'){ + generate_xml(%data); + } +} +# NOTE: file has already been set and directory verified +sub generate_json { + eval $start if $b_log; + my (%data) = @_; + my ($json); + my $b_debug = 1; + my ($b_cpanel,$b_valid); + error_handler('not-in-irc', 'help') if $b_irc; + #print Dumper \%data if $b_debug; + if (check_module('Cpanel::JSON::XS')){ + import Cpanel::JSON::XS; + $json = Cpanel::JSON::XS::encode_json(\%data); + } + elsif (check_module('JSON::XS')){ + import JSON::XS; + $json = JSON::XS::encode_json(\%data); + } + else { + error_handler('required-module', 'json', 'Cpanel::JSON::XS OR JSON::XS'); + } + if ($json){ + #$json =~ s/"[0-9]+#/"/g; + if ($output_file eq 'print'){ + #$json =~ s/\}/}\n/g; + print "$json"; + } + else { + print_line("Writing JSON data to: $output_file\n"); + open(my $fh, '>', $output_file) or error_handler('open',$output_file,"$!"); + print $fh "$json"; + close $fh; + print_line("Data written successfully.\n"); + } + } + eval $end if $b_log; +} +# NOTE: So far xml is substantially more difficult than json, so +# using a crude dumper rather than making a nice xml file, but at +# least xml has some output now. +sub generate_xml { + eval $start if $b_log; + my (%data) = @_; + my ($xml); + my $b_debug = 0; + error_handler('not-in-irc', 'help') if $b_irc; + #print Dumper \%data if $b_debug; + if (check_module('XML::Dumper')){ + import XML::Dumper; + $xml = XML::Dumper::pl2xml(\%data); + #$xml =~ s/"[0-9]+#/"/g; + if ($output_file eq 'print'){ + print "$xml"; + } + else { + print_line("Writing XML data to: $output_file\n"); + open(my $fh, '>', $output_file) or error_handler('open',$output_file,"$!"); + print $fh "$xml"; + close $fh; + print_line("Data written successfully.\n"); + } + } + else { + error_handler('required-module', 'xml', 'XML::Dumper'); + } + eval $end if $b_log; +} + +sub key { + return sprintf("%03d#%s", $_[0],$_[1]); +} + +sub print_basic { + my (@data) = @_; + my $indent = 18; + my $indent_static = 18; + my $indent1_static = 5; + my $indent2_static = 8; + my $indent1 = 5; + my $indent2 = 8; + my $length = @data; + my ($start,$aref,$i,$j,$line); + + if ( $size{'max'} > 110 ){ + $indent_static = 22; + } + elsif ($size{'max'} < 90 ){ + $indent_static = 15; + } + # print $length . "\n"; + for my $i (0 .. $#data){ + $aref = $data[$i]; + #print "0: $data[$i][0]\n"; + if ($data[$i][0] == 0 ){ + $indent = 0; + $indent1 = 0; + $indent2 = 0; + } + elsif ($data[$i][0] == 1 ){ + $indent = $indent_static; + $indent1 = $indent1_static; + $indent2= $indent2_static; + } + elsif ($data[$i][0] == 2 ){ + $indent = ( $indent_static + 7 ); + $indent1 = ( $indent_static + 5 ); + $indent2 = 0; + } + $data[$i][3] =~ s/\n/ /g; + $data[$i][3] =~ s/\s+/ /g; + if ($data[$i][1] && $data[$i][2]){ + $data[$i][1] = $data[$i][1] . ', '; + } + $start = sprintf("%${indent1}s%-${indent2}s",$data[$i][1],$data[$i][2]); + if ($indent > 1 && ( length($start) > ( $indent - 1) ) ){ + $line = sprintf("%-${indent}s\n", "$start"); + print_line($line); + $start = ''; + #print "1-print.\n"; + } + if ( ( $indent + length($data[$i][3]) ) < $size{'max'} ){ + $data[$i][3] =~ s/\^/ /g; + $line = sprintf("%-${indent}s%s\n", "$start", $data[$i][3]); + print_line($line); + #print "2-print.\n"; + } + else { + my $holder = ''; + my $sep = ' '; + foreach my $word (split / /, $data[$i][3]){ + #print "$word\n"; + if ( ( $indent + length($holder) + length($word) ) < $size{'max'} ) { + $word =~ s/\^/ /g; + $holder .= $word . $sep; + #print "3-hold.\n"; } - else if ( audioDriver != "" ) { - useDrivers=audioDriver + #elsif ( ( $indent + length($holder) + length($word) ) >= $size{'max'}){ + else { + $line = sprintf("%-${indent}s%s\n", "$start", $holder); + print_line($line); + $start = ''; + $word =~ s/\^/ /g; + $holder = $word . $sep; + #print "4-print-hold.\n"; } } - if (ports[i] != "") { - usePorts = ports[i] + if ($holder !~ /^[ ]*$/){ + $line = sprintf("%-${indent}s%s\n", "$start", $holder); + print_line($line); + #print "5-print-last.\n"; } - if (modules[i] != "" ) { - useModules = modules[i] - } - if ( aPciBusId[i] != "" ) { - usePciBusId = aPciBusId[i] + } + } +} +# this has to get a hash of hashes, at least for now. +# because perl does not retain insertion order, I use a prefix for each +# hash key to force sorts. +sub print_data { + my (%data) = @_; + my $array = 0; + my $array_holder = 1; + my $counter=0; + my $split_count = 0; + my $hash = 0; + my $holder = ''; + my $id_holder = 0; + my $start = ''; + my $start2 = ''; + my $length = 0; + my $indent = $size{'indent'}; + my (@temp,@working,@values,%ids,$holder2,%row); + my ($key,$line,$val2,$val3); + # $size{'max'} = 88; + # NOTE: indent < 11 would break the output badly in some cases + if ($size{'max'} < $size{'indent-min'} || $size{'indent'} < 11 ){ + $indent = 2; + } + #foreach my $key1 (sort { (split/#/, $a)[0] <=> (split/#/, $b)[0] } keys %data) { + foreach my $key1 (sort { substr($a,0,3) <=> substr($b,0,3) } keys %data) { + #foreach my $key1 (sort { $a cmp $b } keys %data) { + $key = (split/#/, $key1)[1]; + if ($key ne 'SHORT' ) { + $start = sprintf("$colors{'c1'}%-${indent}s$colors{'cn'}","$key$sep{'s1'}"); + if ($indent < 10){ + $line = "$start\n"; + print_line($line); + $start = ''; + $line = ''; } - # create array primary item for master array - sub( / $/, "", usePorts ) # clean off trailing whitespace - print a[j] "," useDrivers "," usePorts "," useModules "," usePciBusId - j++ } - }') ) - - # in case of failure of first check do this instead - if [[ ${#A_AUDIO_DATA[@]} -eq 0 ]] && [[ $B_ASOUND_DEVICE_FILE == 'true' ]];then - A_AUDIO_DATA=( $( gawk -F ']: ' ' - BEGIN { - IGNORECASE=1 + else { + $indent = 0; } - $1 !~ /modem/ && $2 !~ /modem/ { - card=gensub( /^(.+)( - )(.+)$/, "\\3", 1, $2 ) - driver=gensub( /^(.+)( - )(.+)$/, "\\1", 1, $2 ) - if ( card != "" ){ - print card","driver + if (ref($data{$key1}) eq 'ARRAY'){ + # @working = @{$data{$key1}}; + %ids = ( + 'Array' => 1, + 'array' => 1, + 'Battery' => 1, + 'Card' => 1, + 'Device' => 1, + 'Floppy' => 1, + 'ID' => 1, + 'IF-ID' => 1, + 'Optical' => 1, + ); + $array_holder = 1; + foreach my $val1 (@{$data{$key1}}){ + $length = $indent; + if (ref($val1) eq 'HASH'){ + #%row = %$val1; + $counter=0; + $split_count = 0; + $hash = scalar %$val1; + #foreach my $key2 (sort { (split/#/, $a)[0] <=> (split/#/, $b)[0] } keys %$val1){ + foreach my $key2 (sort { substr($a,0,3) <=> substr($b,0,3) } keys %$val1){ + #foreach my $key2 (sort { $a cmp $b } keys %$val1){ + $key = (split/#/, $key2)[1]; + # for ram with > 1 system array, we want to reset device count to 1 for each + # new array + if ($key eq 'Array' && $array_holder != $ids{$key} ){ + $array_holder = $ids{$key}; + $ids{'Device'} = 1 if ($ids{'Device'} > 1); + } + if ($key eq 'Device' && $ids{'array'} > 1 && $id_holder != $ids{$key} ){ + $id_holder = $ids{$key}; + $ids{'array'} = 1 if ($ids{'array'} > 1); + } + if ($counter == 0 && defined $ids{$key}){ + $key .= '-' . $ids{$key}++; + } + $val2 = $$val1{$key2}; + # we have to handle cases where $val2 is 0 + if ($val2 || $val2 eq '0'){ + $val2 .= " "; + } + # see: Use of implicit split to @_ is deprecated. Only get this warning + # in Perl 5.08 oddly enough. + @temp = split/\s+/, $val2; + $split_count = scalar @temp; + if ( ( length( "$key$sep{'s2'} $val2" ) + $length ) < $size{'max'} ) { + $length += length("$key$sep{'s2'} $val2"); + $holder .= "$colors{'c1'}$key$sep{'s2'}$colors{'c2'} $val2"; + #print "one\n"; + } + # handle case where the opening key/value pair is > max, and where + # there are a lot of terms, like cpu flags, raid types supported. Raid + # can have the last row have a lot of devices, or many raid types + elsif ( ( length( "$key$sep{'s2'} $val2" ) + $indent ) > $size{'max'} && + !defined $ids{$key} && $split_count > 2 ) { + @values = split/\s+/, $val2; + $val3 = shift @values; + # $length += length("$key$sep{'s2'} $val3 ") + $indent; + $start2 = "$colors{'c1'}$key$sep{'s2'}$colors{'c2'} $val3 "; + $holder2 = ''; + $length += length("$key$sep{'s2'} $val3 "); + # print scalar @values,"\n"; + foreach (@values){ + # my $l = (length("$_ ") + $length); + #print "$l\n"; + if ( (length("$_ ") + $length) < $size{'max'} ){ + #print "a\n"; + if ($start2){ + $holder2 .= "$start2$_ "; + $start2 = ''; + #$length += $length2; + #$length2 = 0; + } + else { + $holder2 .= "$_ "; + } + $length += length("$_ "); + } + else { + #print "three\n"; + if ($start2){ + $holder2 = "$start2$holder2"; + } + else { + $holder2 = "$colors{'c2'}$holder2"; + } + #print "xx:$holder"; + $line = sprintf("%-${indent}s%s$colors{'cn'}\n","$start","$holder$holder2"); + print_line($line); + $holder = ''; + + $holder2 = "$_ "; + #print "h2: $holder2\n"; + $length = length($holder2) + $indent; + $start2 = ''; + $start = ''; + #$length2 = 0; + } + } + if ($holder2 !~ /^\s*$/){ + #print "four\n"; + $holder2 = "$colors{'c2'}$holder2"; + $line = sprintf("%-${indent}s%s$colors{'cn'}\n","$start","$holder$holder2"); + print_line($line); + $holder = ''; + $holder2 = ''; + $length = $indent; + $start2 = ''; + $start = ''; + #$length2 = 0; + } + } + else { + #print "H: $counter $hash\n"; + if ($holder){ + #print "five\n"; + $line = sprintf("%-${indent}s%s$colors{'cn'}\n",$start,"$holder"); + $holder = "$colors{'c1'}$key$sep{'s2'}$colors{'c2'} $val2"; + $length = length("$key$sep{'s2'} $val2") + $indent; + print_line($line); + $start = ''; + } + else { + #print "six\n"; + $holder = "$colors{'c1'}$key$sep{'s2'}$colors{'c2'} $val2"; + #$line = sprintf("%-${indent}s%s$colors{'cn'}\n",$start,"$holder"); + $length = $indent; + #$holder = ''; + } + } + $counter++; + } + if ($holder !~ /^\s*$/){ + #print "seven\n"; + $line = sprintf("%-${indent}s%s$colors{'cn'}\n",$start,"$start2$holder"); + print_line($line); + $holder = ''; + $length = 0; + $start = ''; + } + } + # only for repos? + elsif (ref($val1) eq 'ARRAY'){ + #print "eight\n"; + $array=0; + foreach my $item (@$val1){ + $array++; + $line = "$colors{'c1'}$array$sep{'s2'} $colors{'c2'}$item$colors{'cn'}"; + $line = sprintf("%-${indent}s%s\n","","$line"); + print_line($line); + } + } + else { + + } } - }' $FILE_ASOUND_DEVICE ) ) - fi - IFS="$ORIGINAL_IFS" - get_audio_usb_data - # handle cases where card detection fails, like in PS3, where lspci gives no output, or headless boxes.. - if [[ ${#A_AUDIO_DATA[@]} -eq 0 ]];then - A_AUDIO_DATA[0]='Failed to Detect Sound Card!' - fi - a_temp=${A_AUDIO_DATA[@]} - log_function_data "A_AUDIO_DATA: $a_temp" + } + } +} - eval $LOGFE +sub print_line { + my ($line) = @_; + if ($b_irc && $client{'test-konvi'}){ + $client{'konvi'} = 3; + $client{'dobject'} = 'Konversation'; + } + if ($client{'konvi'} == 1 && $client{'dcop'} ){ + # konvi doesn't seem to like \n characters, it just prints them literally + $line =~ s/\n//g; + #qx('dcop "$client{'dport'}" "$client{'dobject'}" say "$client{'dserver'}" "$client{'dtarget'}" "$line 1"); + system('dcop', $client{'dport'}, $client{'dobject'}, 'say', $client{'dserver'}, $client{'dtarget'}, "$line 1"); + } + elsif ($client{'konvi'} == 3 && $client{'qdbus'} ){ + # print $line; + $line =~ s/\n//g; + #qx(qdbus org.kde.konversation /irc say "$client{'dserver'}" "$client{'dtarget'}" "$line"); + system('qdbus', 'org.kde.konversation', '/irc', 'say', $client{'dserver'}, $client{'dtarget'}, $line); + } + else { + print $line; + } } -# alsa usb detection by damentz -get_audio_usb_data() +######################################################################## +#### DATA PROCESSORS +######################################################################## + +#### ------------------------------------------------------------------- +#### PRIMARY DATA GENERATORS +#### ------------------------------------------------------------------- +# 0 type +# 1 type_id +# 2 bus_id +# 3 sub_id +# 4 device +# 5 vendor_id +# 6 chip_id +# 7 rev +# 8 port +# 9 driver +# 10 modules + +## AudioData { - eval $LOGFS - local usb_proc_file='' array_count='' usb_data='' usb_id='' lsusb_data='' - local a_temp='' - - IFS=$'\n' - if type -p lsusb &>/dev/null;then - lsusb_data=$( lsusb 2>/dev/null ) - fi - log_function_data 'raw' "usb_data:\n$lsusb_data" - if [[ -n $lsusb_data && -d /proc/asound/ ]];then - # for every sound card symlink in /proc/asound - display information about it - for usb_proc_file in /proc/asound/* - do - # If the file is a symlink, and contains an important usb exclusive file: continue - if [[ -L $usb_proc_file && -e $usb_proc_file/usbid ]]; then - # find the contents of usbid in lsusb and print everything after the 7th word on the - # corresponding line. Finally, strip out commas as they will change the driver :) - usb_id=$( cat $usb_proc_file/usbid ) - usb_data=$( grep "$usb_id" <<< "$lsusb_data" ) - if [[ -n $usb_data && -n $usb_id ]];then - usb_data=$( gawk ' - BEGIN { - IGNORECASE=1 - string="" - separator="" +package AudioData; + +sub get { + eval $start if $b_log; + my (@data,@rows); + my $num = 0; + if ($b_arm){ + my $key = 'ARM'; + @data = ({ + main::key($num++,$key) => main::row_defaults('arm-pci',''), + },); + @rows = (@rows,@data); + } + else { + @data = card_data(); + @rows = (@rows,@data); + } + if ( ( $b_arm || !@rows ) && (my $file = main::system_files('asound-cards') ) ){ + @data = asound_data($file); + @rows = (@rows,@data); + } + @data = usb_data(); + @rows = (@rows,@data); + if (!@rows){ + my $key = 'Message'; + @data = ({ + main::key($num++,$key) => main::row_defaults('pci-card-data',''), + },); + @rows = (@rows,@data); + } + @data = sound_server_data(); + @rows = (@rows,@data); + eval $end if $b_log; + return @rows; +} + +sub card_data { + eval $start if $b_log; + my (@rows,@data); + my ($j,$num) = (0,1); + foreach (@pci){ + $num = 1; + my @row = @$_; + if ($row[0] eq 'audio' || $row[0] eq 'multimedia'){ + $j = scalar @rows; + my $driver = $row[9]; + $driver ||= 'N/A'; + my $card = $row[4]; + $card = ($card) ? main::pci_cleaner($card,'output') : 'N/A'; + # have seen absurdly verbose card descriptions, with non related data etc + if (length($card) > 85 || $size{'max'} < 110){ + $card = main::pci_long_filter($card); + } + @data = ({ + main::key($num++,'Card') => $card, + main::key($num++,'driver') => $driver, + },); + @rows = (@rows,@data); + if ($extra > 0 && !$bsd_type){ + if ($row[9] ){ + my $version = main::get_module_version($row[9]); + $rows[$j]{main::key($num++,'v')} = $version if $version; + } + } + if ($extra > 0){ + $rows[$j]{main::key($num++,'bus ID')} = "$row[2].$row[3]"; + } + if ($extra > 1){ + $rows[$j]{main::key($num++,'chip ID')} = "$row[5]:$row[6]"; + } + } + #print "$row[0]\n"; + } + #my $ref = $pci[-1]; + #print $$ref[0],"\n"; + eval $end if $b_log; + return @rows; +} +# this handles fringe cases where there is no card on pcibus, +# but there is a card present. I don't know the exact architecture +# involved but I know this situation exists on at least one old machine. +sub asound_data { + eval $start if $b_log; + my ($file) = @_; + my (@asound,@rows,@data); + my ($card,$driver,$j,$num) = ('','',0,1); + @asound = main::reader($file); + foreach (@asound){ + # filtering out modems and usb devices like webcams, this might get a + # usb audio card as well, this will take some trial and error + if ( !/modem|usb/i && /^\s*[0-9]/ ) { + $num = 1; + my @working = split /:\s*/, $_; + # now let's get 1 2 + $working[1] =~ /(.*)\s+-\s+(.*)/; + $card = $2; + $driver = $1; + if ( $card ){ + $j = scalar @rows; + $driver ||= 'N/A'; + @data = ({ + main::key($num++,'Card') => $card, + main::key($num++,'driver') => $driver, + },); + @rows = (@rows,@data); + if ($extra > 0){ + my $version = main::get_module_version($driver); + $rows[$j]{main::key($num++,'v')} = $version if $version; + $rows[$j]{main::key($num++,'message')} = main::row_defaults('pci-advanced-data',''); + } + } + } + } + # print Data::Dumper:Dumper \s@rows; + eval $end if $b_log; + return @rows; +} +sub usb_data { + eval $start if $b_log; + my (@rows,@data,@ids,$driver,$product,$product2,@temp2,$vendor,$vendor2); + my ($j,$num) = (0,1); + if (-d '/proc/asound') { + # note: this will double the data, but it's easier this way. + # inxi tested for -L in the /proc/asound files, and used only those. + my @files = main::globber('/proc/asound/*/usbid'); + foreach (@files){ + my $id = (main::reader($_))[0]; + push @ids, $id if ($id && ! grep {/$id/} @ids); + } + # lsusb is a very expensive operation + if (@ids){ + if (!$bsd_type && !$b_usb_check){ + main::set_usb_data(); + $b_usb_check = 1; + } + } + main::log_data('dump','@ids',\@ids) if $b_log; + return if !@usb; + foreach my $id (@ids){ + $j = scalar @rows; + foreach my $ref (@usb){ + my @row = @$ref; + # a device will always be the second or > device on the bus + if ($row[1] > 1 && $row[2] eq $id){ + $num = 1; + # makre sure to reset, or second device trips last flag + ($product,$product2,$vendor,$vendor2) = ('','','',''); + if ($usb_level == 1){ + $product = main::cleaner($row[3]); + } + else { + foreach my $line (@row){ + my @working = split /:/, $line; + if ($working[0] eq 'idVendor' && $working[2]){ + $vendor = main::cleaner($working[2]); + } + if ($working[0] eq 'idProduct' && $working[2]){ + $product = main::cleaner($working[2]); + } + if ($working[0] eq 'iManufacturer' && $working[2]){ + $vendor2 = main::cleaner($working[2]); + } + if ($working[0] eq 'iProduct' && $working[2]){ + $product2 = main::cleaner($working[2]); + } + if ($working[0] eq 'Descriptor_Configuration'){ + last; + } + } + } + if ($vendor && $product){ + $product = ($product =~ /$vendor/) ? $product: "$vendor $product" ; } - { - gsub(/'"$BAN_LIST_ARRAY"'/, " ", $0 ) - gsub(/'"$BAN_LIST_NORMAL"'/, "", $0) - gsub(/ [ \t]+/, " ", $0) - for ( i=7; i<= NF; i++ ) { - string = string separator $i - separator = " " + elsif (!$product) { + if ($vendor && $product2){ + $product = ($product2 =~ /$vendor/) ? $product2: "$vendor $product2" ; } - if ( $2 != "" ){ - sub(/:/,"", $4) - print string ",USB Audio,,," $2 "-" $4 "," $6 + elsif ($vendor2 && $product2){ + $product = ($product2 =~ /$vendor2/) ? $product2: "$vendor2 $product2" ; } - }' <<< "$usb_data" ) - fi - # this method is interesting, it shouldn't work but it does - #A_AUDIO_DATA=( "${A_AUDIO_DATA[@]}" "$usb_data,USB Audio,," ) - # but until we learn why the above worked, I'm using this one, which is safer - if [[ -n $usb_data ]];then - array_count=${#A_AUDIO_DATA[@]} - A_AUDIO_DATA[$array_count]="$usb_data" - fi - fi - done - fi - IFS="$ORIGINAL_IFS" - a_temp=${A_AUDIO_DATA[@]} - log_function_data "A_AUDIO_DATA: $a_temp" - - eval $LOGFE + elsif ($vendor){ + $product = $vendor; + } + elsif ($vendor2){ + $product = $vendor2; + } + else { + $product = 'N/A'; + } + } + @temp2 = main::get_usb_drivers($row[0],$row[2]) if !$bsd_type && -d "/sys/devices"; + if (@temp2 && $temp2[0]){ + $driver = $temp2[0]; + } + $driver ||= 'snd-usb-audio'; + @data = ({ + main::key($num++,'Card') => $product, + main::key($num++,'type') => 'USB', + main::key($num++,'driver') => $driver, + },); + @rows = (@rows,@data); + if ($extra > 0){ + $rows[$j]{main::key($num++,'bus ID')} = "$row[0]:$row[1]"; + } + if ($extra > 1){ + $rows[$j]{main::key($num++,'chip ID')} = $row[2]; + } + } + } + } + } + eval $end if $b_log; + return @rows; } -get_audio_alsa_data() -{ - eval $LOGFS - local alsa_data='' a_temp='' - - # now we'll get the alsa data if the file exists - if [[ $B_ASOUND_VERSION_FILE == 'true' ]];then - IFS="," - A_ALSA_DATA=( $( - gawk ' - BEGIN { - IGNORECASE=1 - alsa="" - version="" - } - # some alsa strings have the build date in (...) - # remove trailing . and remove possible second line if compiled by user - $0 !~ /compile/ { - gsub(/Driver | [(].*[)]|\.$/,"",$0 ) - gsub(/'"$BAN_LIST_ARRAY"'/, " ", $0) - gsub(/^ +| +$/, "", $0) - gsub(/ [ \t]+/, " ", $0) - sub(/Advanced Linux Sound Architecture/, "ALSA", $0) - if ( $1 == "ALSA" ){ - alsa=$1 - } - version=$NF - print alsa "," version - }' $FILE_ASOUND_VERSION ) ) - IFS="$ORIGINAL_IFS" - log_function_data 'cat' "$FILE_ASOUND_VERSION" - fi - a_temp=${A_ALSA_DATA[@]} - log_function_data "A_ALSA_DATA: $a_temp" - eval $LOGFE -} - -get_battery_data() +sub sound_server_data { + eval $start if $b_log; + my (@data,$server,$version); + my $num = 0; + if (my $file = main::system_files('asound-version') ){ + my $content = (main::reader($file))[0]; + # some alsa strings have the build date in (...) + # remove trailing . and remove possible second line if compiled by user +# foreach (@content){ +# if (!/compile/i){ + #$_ =~ s/Advanced Linux Sound Architecture/ALSA/; + $version = (split /\s+/, $content)[-1]; + $version =~ s/\.$//; # trim off period + $server = 'ALSA'; +# } +# } + } + elsif (my $program = main::check_program('oss')){ + $server = 'OSS'; + $version = main::program_version('oss','\S',2); + $version ||= 'N/A'; + } + if ($server){ + @data = ({ + main::key($num++,'Sound Server') => $server, + main::key($num++,'v') => $version, + },); + } + eval $end if $b_log; + return @data; +} +} + +## BatteryData { - eval $LOGFS - local a_temp='' id_file='' count=0 - local id_dir='/sys/class/power_supply/' - local ids=$( ls $id_dir 2>/dev/null ) battery_file='' - - # ids='BAT0 BAT1 BAT2' - if [[ -n $ids && $B_FORCE_DMIDECODE == 'false' ]];then - for idx in $ids - do - battery_file=$id_dir$idx'/uevent' - if [[ -r $battery_file ]];then - # echo $battery_file - count=$(( $count + 1 )) - IFS=$'\n' - battery_data=$( - gawk -F '=' ' - BEGIN { - IGNORECASE=1 - name="" - status="" - present="" - chemistry="" - cycles="" - voltage_min_design="" - voltage_now="" - power_now="" - # charge: mAh - charge_full_design="" - charge_full="" - charge_now="" - # energy: Wh - energy_full_design="" - energy_full="" - energy_now="" - capacity="" - capacity_level="" - model="" - company="" - serial="" - of_orig="" - location="" - b_ma="false" # trips ma x voltage - } - { - gsub(/'"$BAN_LIST_NORMAL"'|,|battery|unknown/, "", $2) - gsub(/^ +| +$/, "", $2) +package BatteryData; + +sub get { + eval $start if $b_log; + my (@rows,$key1,%battery,$val1); + my $num = 0; + if ($bsd_type || $b_dmidecode_force){ + my $ref = $alerts{'dmidecode'}; + if ( $$ref{'action'} ne 'use'){ + $key1 = $$ref{'action'}; + $val1 = $$ref{$key1}; + $key1 = ucfirst($key1); + @rows = ({main::key($num++,$key1) => $val1,}); + } + else { + %battery = battery_data_dmi(); + if (!%battery){ + if ($show{'battery-forced'}){ + $key1 = 'Message'; + $val1 = main::row_defaults('battery-data',''); + @rows = ({main::key($num++,$key1) => $val1,}); } - $1 ~ /^POWER_SUPPLY_NAME$/ { - name=$NF + } + else { + @rows = create_output(%battery); + } + } + } + elsif (-d '/sys/class/power_supply/'){ + %battery = battery_data_sys(); + if (!%battery){ + if ($show{'battery-forced'}){ + $key1 = 'Message'; + $val1 = main::row_defaults('battery-data',''); + @rows = ({main::key($num++,$key1) => $val1,}); + } + } + else { + @rows = create_output(%battery); + } + } + else { + if ($show{'battery-forced'}){ + $key1 = 'Message'; + $val1 = main::row_defaults('battery-data-sys',''); + @rows = ({main::key($num++,$key1) => $val1,}); + } + } + eval $end if $b_log; + return @rows; +} +# alarm capacity capacity_level charge_full charge_full_design charge_now +# cycle_count energy_full energy_full_design energy_now location manufacturer model_name +# power_now present serial_number status technology type voltage_min_design voltage_now +# 0 name - battery id, not used +# 1 status +# 2 present +# 3 technology +# 4 cycle_count +# 5 voltage_min_design +# 6 voltage_now +# 7 power_now +# 8 energy_full_design +# 9 energy_full +# 10 energy_now +# 11 capacity +# 12 capacity_level +# 13 of_orig +# 14 model_name +# 15 manufacturer +# 16 serial_number +# 17 location +sub create_output { + eval $start if $b_log; + my (%battery,@data,@rows) = @_; + my ($key); + my $num = 0; + my $j = 0; + foreach $key (sort keys %battery){ + $num = 0; + my ($charge,$condition,$model,$serial,$status,$volts) = ('','','','','',''); + my ($chemistry,$cycles,$location) = ('','',''); + # $battery{$key}{''}; + # we need to handle cases where charge or energy full is 0 + $charge = ($battery{$key}{'energy_now'} || $battery{$key}{'energy_now'} eq 0) ? "$battery{$key}{'energy_now'} Wh" : 'N/A'; + if ($battery{$key}{'energy_full'} || $battery{$key}{'energy_full_design'}){ + $battery{$key}{'energy_full_design'} ||= 'N/A'; + $battery{$key}{'energy_full'}= ($battery{$key}{'energy_full'} || $battery{$key}{'energy_full'} eq 0) ? $battery{$key}{'energy_full'} : 'N/A'; + $condition = "$battery{$key}{'energy_full'}/$battery{$key}{'energy_full_design'} Wh"; + if ($battery{$key}{'of_orig'}){ + $condition .= " ($battery{$key}{'of_orig'}%)"; + } + } + $condition ||= 'N/A'; + $j = scalar @rows; + @data = ({ + main::key($num++,'ID') => $key, + main::key($num++,'charge') => $charge, + main::key($num++,'condition') => $condition, + },); + @rows = (@rows,@data); + if ($extra > 0){ + if ($extra > 1){ + if ($battery{$key}{'voltage_min_design'} || $battery{$key}{'voltage_now'}){ + $battery{$key}{'voltage_min_design'} ||= 'N/A'; + $battery{$key}{'voltage_now'} ||= 'N/A'; + $volts = "$battery{$key}{'voltage_now'}/$battery{$key}{'voltage_min_design'}"; } - $1 ~ /^POWER_SUPPLY_STATUS$/ { - status=$NF + $volts ||= 'N/A'; + $rows[$j]{main::key($num++,'volts')} = $volts; + } + if ($battery{$key}{'manufacturer'} || $battery{$key}{'model_name'}) { + if ($battery{$key}{'manufacturer'} && $battery{$key}{'model_name'}){ + $model = "$battery{$key}{'manufacturer'} $battery{$key}{'model_name'}"; } - $1 ~ /^POWER_SUPPLY$/ { - present=$NF + elsif ($battery{$key}{'manufacturer'}){ + $model = $battery{$key}{'manufacturer'}; } - $1 ~ /^POWER_SUPPLY_TECHNOLOGY$/ { - chemistry=$NF + elsif ($battery{$key}{'model_name'}){ + $model = $battery{$key}{'model_name'}; } - $1 ~ /^POWER_SUPPLY_CYCLE_COUNT$/ { - cycles=$NF + } + else { + $model = 'N/A'; + } + $rows[$j]{main::key($num++,'model')} = $model; + if ($extra > 2){ + $chemistry = ( $battery{$key}{'technology'} ) ? $battery{$key}{'technology'}: 'N/A'; + $rows[$j]{main::key($num++,'type')} = $chemistry; + } + if ($extra > 1){ + $serial = main::apply_filter($battery{$key}{'serial_number'}); + $rows[$j]{main::key($num++,'serial')} = $serial; + } + $status = ($battery{$key}{'status'}) ? $battery{$key}{'status'}: 'N/A'; + $rows[$j]{main::key($num++,'status')} = $status; + if ($extra > 2){ + if ($battery{$key}{'cycle_count'}){ + $rows[$j]{main::key($num++,'cycles')} = $battery{$key}{'cycle_count'}; } - $1 ~ /^POWER_SUPPLY_VOLTAGE_MIN_DESIGN$/ { - voltage_min_design = $NF / 1000000 - voltage_min_design = sprintf( "%.1f", voltage_min_design ) + if ($battery{$key}{'location'}){ + $rows[$j]{main::key($num++,'location')} = $battery{$key}{'location'}; } - $1 ~ /^POWER_SUPPLY_VOLTAGE_NOW$/ { - voltage_now = $NF / 1000000 - voltage_now = sprintf( "%.1f", voltage_now ) + } + } + } + eval $end if $b_log; + return @rows; +} + +# charge: mAh energy: Wh +sub battery_data_sys { + eval $start if $b_log; + my ($b_ma,%battery,$file,$id,$item,$path,$value); + my $num = 0; + # NOTE: known ids: BAT[0-9] CMB[0-9] + my @batteries = main::globber("/sys/class/power_supply/[BC][AM][TB]*/"); + # note: there is no 'location' file, but dmidecode has it + # 'type' is generic, like: Battery + # capacity_level is a string, like: Normal + my @items = qw(alarm capacity capacity_level charge_full charge_full_design charge_now + cycle_count energy_full energy_full_design energy_now location manufacturer model_name + power_now present serial_number status technology type voltage_min_design voltage_now); + foreach $item (@batteries){ + $b_ma = 0; + $id = $item; + $id =~ s%/sys/class/power_supply/|\/$%%g; + $battery{$id} = ({}); + foreach $file (@items){ + $path = "$item$file"; + $value = (-f $path) ? (main::reader($path))[0]: ''; + if ($value){ + if ($file eq 'voltage_min_design'){ + $value = sprintf("%.1f", $value/1000000); } - $1 ~ /^POWER_SUPPLY_POWER_NOW$/ { - power_now=$NF + elsif ($file eq 'voltage_now'){ + $value = sprintf("%.1f", $value/1000000); } - $1 ~ /^POWER_SUPPLY_ENERGY_FULL_DESIGN$/ { - energy_full_design = $NF / 1000000 + elsif ($file eq 'energy_full_design'){ + $value = $value/1000000; } - $1 ~ /^POWER_SUPPLY_ENERGY_FULL$/ { - energy_full = $NF / 1000000 + elsif ($file eq 'energy_full'){ + $value = $value/1000000; } - $1 ~ /^POWER_SUPPLY_ENERGY_NOW$/ { - energy_now = $NF / 1000000 - energy_now = sprintf( "%.1f", energy_now ) + elsif ($file eq 'energy_now'){ + $value = sprintf("%.1f", $value/1000000); } # note: the following 3 were off, 100000 instead of 1000000 # why this is, I do not know. I did not document any reason for that # so going on assumption it is a mistake. CHARGE is mAh, which are converted # to Wh by: mAh x voltage. Note: voltage fluctuates so will make results vary slightly. - $1 ~ /^POWER_SUPPLY_CHARGE_FULL_DESIGN$/ { - charge_full_design = $NF / 1000000 - b_ma="true" - } - $1 ~ /^POWER_SUPPLY_CHARGE_FULL$/ { - charge_full = $NF / 1000000 - b_ma="true" + elsif ($file eq 'charge_full_design'){ + $value = $value/1000000; + $b_ma = 1; } - $1 ~ /^POWER_SUPPLY_CHARGE_NOW$/ { - charge_now = $NF / 1000000 - b_ma="true" + elsif ($file eq 'charge_full'){ + $value = $value/1000000; + $b_ma = 1; } - $1 ~ /^POWER_SUPPLY_CAPACITY$/ { - if ( $NF != "" ){ - capacity=$NF "%" - } + elsif ($file eq 'charge_now'){ + $value = $value/1000000; + $b_ma = 1; } - $1 ~ /^POWER_SUPPLY_CAPACITY_LEVEL$/ { - capacity_level=$NF + elsif ($file eq 'manufacturer'){ + $value = main::dmi_cleaner($value); } - $1 ~ /^POWER_SUPPLY_MODEL_NAME$/ { - model=$NF + elsif ($file eq 'model_name'){ + $value = main::dmi_cleaner($value); } - $1 ~ /^POWER_SUPPLY_MANUFACTURER$/ { - company=$NF - } - $1 ~ /^POWER_SUPPLY_SERIAL_NUMBER$/ { - serial=$NF - } - END { - # note:voltage_now fluctuates, which will make capacity numbers change a bit - # if any of these values failed, the math will be wrong, but no way to fix that - # tests show more systems give right capacity/charge with voltage_min_design - # than with voltage_now - if (b_ma == "true" && voltage_min_design != ""){ - if (charge_now != ""){ - energy_now=charge_now*voltage_min_design - } - if (charge_full != ""){ - energy_full=charge_full*voltage_min_design - } - if (charge_full_design != ""){ - energy_full_design=charge_full_design*voltage_min_design - } - } - if (energy_now != "" && energy_full != "" ){ - capacity = 100*energy_now/energy_full - capacity = sprintf( "%.1f%", capacity ) - } - if (energy_full_design != "" && energy_full != "" ){ - of_orig=100*energy_full/energy_full_design - of_orig = sprintf( "%.0f%", of_orig ) - } - if (energy_now != "" ){ - energy_now = sprintf( "%.1f", energy_now ) - } - if (energy_full_design != "" ){ - energy_full_design = sprintf( "%.1f", energy_full_design ) - } - if ( energy_full != "" ){ - energy_full = sprintf( "%.1f", energy_full ) - } - entry = name "," status "," present "," chemistry "," cycles "," voltage_min_design "," voltage_now "," - entry = entry power_now "," energy_full_design "," energy_full "," energy_now "," capacity "," - entry = entry capacity_level "," of_orig "," model "," company "," serial "," location - print entry - }' < $battery_file ) - # <<< "$data" ) - # < $battery_file ) - A_BATTERY_DATA[$count]=$battery_data - IFS="$ORIGINAL_IFS" - fi - done - elif [[ $B_FORCE_DMIDECODE == 'true' ]] || [[ ! -d $id_dir && -z $ids ]];then - set_dmidecode_data - if [[ -n $DMIDECODE_DATA ]];then - if [[ $DMIDECODE_DATA == 'dmidecode-error-'* ]];then - A_BATTERY_DATA[0]=$DMIDECODE_DATA - # please note: only dmidecode version 2.11 or newer supports consistently the -s flag - else - IFS=$'\n' - # NOTE: this logic has a flaw, which is multiple batteries, which won't work without - # gawk arrays, but sorry, too much of a pain given how little useful data from dmidecode - A_BATTERY_DATA=( $( gawk -F ':' ' - BEGIN { - IGNORECASE=1 - name="" - status="" - present="" - chemistry="" - cycles="" - voltage_min_design="" - voltage_now="" - power_now="" - charge_full_design="" - charge_full="" - charge_now="" - capacity="" - capacity_level="" - model="" - company="" - serial="" - of_orig="" - location="" - bItemFound="false" + } + $battery{$id}{$file} = $value; + # print "$battery{$id}{$file}\n"; + } + # note:voltage_now fluctuates, which will make capacity numbers change a bit + # if any of these values failed, the math will be wrong, but no way to fix that + # tests show more systems give right capacity/charge with voltage_min_design + # than with voltage_now + if ($b_ma && $battery{$id}{'voltage_min_design'}){ + if ($battery{$id}{'charge_now'}){ + $battery{$id}{'energy_now'} = $battery{$id}{'charge_now'} * $battery{$id}{'voltage_min_design'}; + } + if ($battery{$id}{'charge_full'}){ + $battery{$id}{'energy_full'} = $battery{$id}{'charge_full'}*$battery{$id}{'voltage_min_design'}; + } + if ($battery{$id}{'charge_full_design'}){ + $battery{$id}{'energy_full_design'} = $battery{$id}{'charge_full_design'} * $battery{$id}{'voltage_min_design'}; + } + } + if ( $battery{$id}{'energy_now'} && $battery{$id}{'energy_full'} ){ + $battery{$id}{'capacity'} = 100 * $battery{$id}{'energy_now'}/$battery{$id}{'energy_full'}; + $battery{$id}{'capacity'} = sprintf( "%.1f", $battery{$id}{'capacity'} ); + } + if ( $battery{$id}{'energy_full_design'} && $battery{$id}{'energy_full'} ){ + $battery{$id}{'of_orig'} = 100 * $battery{$id}{'energy_full'}/$battery{$id}{'energy_full_design'}; + $battery{$id}{'of_orig'} = sprintf( "%.0f", $battery{$id}{'of_orig'} ); + } + if ( $battery{$id}{'energy_now'} ){ + $battery{$id}{'energy_now'} = sprintf( "%.1f", $battery{$id}{'energy_now'} ); + } + if ( $battery{$id}{'energy_full_design'} ){ + $battery{$id}{'energy_full_design'} = sprintf( "%.1f",$battery{$id}{'energy_full_design'} ); + } + if ( $battery{$id}{'energy_full'} ){ + $battery{$id}{'energy_full'} = sprintf( "%.1f", $battery{$id}{'energy_full'} ); + } + } + eval $end if $b_log; + return %battery; +} +# note, dmidecode does not have charge_now or charge_full +sub battery_data_dmi { + eval $start if $b_log; + my (%battery,$id); + my $i = 0; + foreach (@dmi){ + my @ref = @$_; + # Portable Battery + if ($ref[0] == 22){ + $id = "BAT$i"; + $i++; + $battery{$id} = ({}); + # skip first three row, we don't need that data + splice @ref, 0, 3 if @ref; + foreach my $item (@ref){ + my @value = split /:\s+/, $item; + next if !$value[0]; + if ($value[0] eq 'Location') {$battery{$id}{'location'} = $value[1] } + elsif ($value[0] eq 'Manufacturer') {$battery{$id}{'manufacturer'} = main::dmi_cleaner($value[1]) } + elsif ($value[0] =~ /Chemistry/) {$battery{$id}{'technology'} = $value[1] } + elsif ($value[0] =~ /Serial Number/) {$battery{$id}{'serial_number'} = $value[1] } + elsif ($value[0] =~ /^Name/) {$battery{$id}{'model_name'} = main::dmi_cleaner($value[1]) } + elsif ($value[0] eq 'Design Capacity') { + $value[1] =~ s/\s*mwh$//i; + $battery{$id}{'energy_full_design'} = sprintf( "%.1f", $value[1]/1000); + } - { - gsub(/'"$BAN_LIST_NORMAL"'|,|battery|unknown/, "", $2) - gsub(/^ +| +$/, "", $1) - gsub(/^ +| +$/, "", $2) - } - /^Portable Battery/ { - while ( getline && !/^$/ ) { - if ( $1 ~ /^Location/ ) { location=$2 } - if ( $1 ~ /^Manufacturer/ ) { company=$2 } - if ( $1 ~ /^Serial/ ) { serial=$2 } - if ( $1 ~ /^Name/ ) { model=$2 } - if ( $1 ~ /^Design Capacity/ ) { - sub(/^[[:space:]]*mwh/, "", $2) - charge_full_design = $NF / 1000 - charge_full_design = sprintf( "%.1f", charge_full_design ) - } - if ( $1 ~ /^Design Voltage/ ) { - sub(/^[[:space:]]*mv/, "", $2) - voltage_min_design = $NF / 1000 - voltage_min_design = sprintf( "%.1f", voltage_min_design ) - } - if ( $1 ~ /^SBDS Chemistry/ ) { chemistry=$2 } - } - testString=company serial model charge_full_design voltage_min_design - if ( testString != "" ) { - bItemFound="true" - exit # exit loop, we are not handling > 1 batteries - } + elsif ($value[0] eq 'Design Voltage') { + $value[1] =~ s/\s*mv$//i; + $battery{$id}{'voltage_min_design'} = sprintf( "%.1f", $value[1]/1000); } - END { - if ( bItemFound == "true" ) { - name="BAT-1" - if (charge_now != "" && charge_full != "" ){ - capacity = 100*charge_now/charge_full - capacity = sprintf( "%.1f%", capacity ) - } - if (charge_full_design != "" && charge_full != "" ){ - of_orig=100*charge_full/charge_full_design - of_orig = sprintf( "%.0f%", of_orig ) - } - entry = name "," status "," present "," chemistry "," cycles "," voltage_min_design "," voltage_now "," - entry = entry power_now "," charge_full_design "," charge_full "," charge_now "," capacity "," - entry = entry capacity_level "," of_orig "," model "," company "," serial "," location - print entry - } - }' <<< "$DMIDECODE_DATA" ) ) - IFS="$ORIGINAL_IFS" - fi - fi - fi - # echo $array_string - - #echo ${#A_BATTERY_DATA[@]} - a_temp=${A_BATTERY_DATA[@]} - - # echo $a_temp - log_function_data "A_BATTERY_DATA: $a_temp" - eval $LOGFE + } + if ($battery{$id}{'energy_now'} && $battery{$id}{'energy_full'} ){ + $battery{$id}{'capacity'} = 100 * $battery{$id}{'energy_now'} / $battery{$id}{'energy_full'}; + $battery{$id}{'capacity'} = sprintf( "%.1f%", $battery{$id}{'capacity'} ); + } + if ($battery{$id}{'energy_full_design'} && $battery{$id}{'energy_full'} ){ + $battery{$id}{'of_orig'} = 100 * $battery{$id}{'energy_full'} / $battery{$id}{'energy_full_design'}; + $battery{$id}{'of_orig'} = sprintf( "%.0f%", $battery{$id}{'of_orig'} ); + } + } + elsif ($ref[0] > 22){ + last; + } + } + # print Data::Dumper::Dumper \%battery; + eval $end if $b_log; + return %battery; } - -## args: $1 type [intel|amd|centaur|arm]; $2 family [hex]; $3 model id [hex]; -get_cpu_architecture() -{ - eval $LOGFS - case $1 in - # https://en.wikipedia.org/wiki/List_of_AMD_CPU_microarchitectures - amd) - case $2 in - 4) - case $3 in - 3|7|8|9|A)ARCH='Am486';; - E|F)ARCH='Am5x86';; - esac - ;; - 5) - case $3 in - 0|1|2|3)ARCH='K5';; - 6|7)ARCH='K6';; - 8)ARCH='K6-2';; - 9|D)ARCH='K6-3';; - A)ARCH='Geode';; - esac - ;; - 6) - case $3 in - 1|2)ARCH='K7';; - 3|4)ARCH='K7 Thunderbird';; - 6|7|8|A)ARCH='K7 Palomino+';; - *)ARCH='K7';; - esac - ;; - F) - case $3 in - 4|5|7|8|B|C|E|F|14|15|17|18|1B|1C|1F)ARCH='K8';; - 21|23|24|25|27|28|2C|2F)ARCH='K8 rev.E';; - 41|43|48|4B|4C|4F|5D|5F|68|6B|6C|6F|7C|7F|C1)ARCH='K8 rev.F+';; - *)ARCH='K8';; - esac - ;; - 10) - case $3 in - 2|4|5|6|8|9|A)ARCH='K10';; - *)ARCH='K10';; - esac - ;; - 11) - case $3 in - 3)ARCH='Turion X2 Ultra';; - esac - ;; - 12) # might also need cache handling like 14/16 - case $3 in - 1)ARCH='Fusion';; - *)ARCH='Fusion';; - esac - ;; - 14) # SOC, apu - case $3 in - 1|2)ARCH='Bobcat';; - *)ARCH='Bobcat';; - esac - ;; - 15) - case $3 in - 0|1|2|3|4|5|6|7|8|9|A|B|C|D|E|F)ARCH='Bulldozer';; - 10|11|12|13|14|15|16|17|18|19|1A|1B|1C|1D|1E|1F)ARCH='Piledriver';; - 30|31|32|33|34|35|36|37|38|39|3A|3B|3C|3D|3E|3F)ARCH='Steamroller';; - 60|61|62|63|64|65|66|67|68|69|6A|6B|6C|6D|6E|6F|70|71|72|73|74|75|76|77|78|79|7A|7B|7C|7D|7E|7F)ARCH='Excavator';; - *)ARCH='Bulldozer';; - esac - ;; - 16) # SOC, apu - case $3 in - 0|1|2|3|4|5|6|7|8|9|A|B|C|D|E|F)ARCH='Jaguar';; - 30|31|32|33|34|35|36|37|38|39|3A|3B|3C|3D|3E|3F)ARCH='Puma';; - *)ARCH='Jaguar';; - esac - ;; - 17) - case $3 in - 1)ARCH='Zen';; - *)ARCH='Zen';; - esac - ;; - esac - ;; - arm) - if [[ "$2" != '' ]];then - ARCH="ARMv$2" - else - ARCH='ARM' - fi - ;; - centaur) # aka VIA - case $2 in - 5) - case $3 in - 4)ARCH='WinChip C6';; - 8)ARCH='WinChip 2';; - 9)ARCH='WinChip 3';; - esac - ;; - 6) - case $3 in - 6)ARCH='WinChip-based';; - 7|8)ARCH='C3';; - 9)ARCH='C3-2';; - A|D)ARCH='C7';; - F)ARCH='Isaiah';; - esac - ;; - esac - ;; - intel) - case $2 in - 4) - case $3 in - 0|1|2|3|4|5|6|7|8|9)ARCH='486';; - esac - ;; - 5) - case $3 in - 1|2|3|7)ARCH='P5';; - 4|8)ARCH='P5';; # MMX - 9)ARCH='Quark';; - esac - ;; - 6) - case $3 in - 1)ARCH='P6 Pro';; - 3|5|6)ARCH='P6 II';; - 7|8|A|B)ARCH='P6 III';; - 9)ARCH='Banias';; # pentium M - 15)ARCH='Dothan Tolapai';; # pentium M system on chip - D)ARCH='Dothan';; # Pentium M - E)ARCH='Yonah';; - F|16)ARCH='Conroe';; - 17|1D)ARCH='Penryn';; - 1A|1E|1F|2E|25|2C|2F)ARCH='Nehalem';; - 1C|26)ARCH='Bonnell';; - 27|35|36)ARCH='Saltwell';; - 25|2C|2F)ARCH='Westmere';; - 26|27)ARCH='Bonnell';; - 2A|2D)ARCH='Sandy Bridge';; - 37|4A|4D|5A)ARCH='Silvermont';; - 3A|3E)ARCH='Ivy Bridge';; - 3C|3F|45|46)ARCH='Haswell';; - 3D|47|4F|56)ARCH='Broadwell';; - 4E|55|9E)ARCH='Skylake';; - 5E)ARCH='Skylake-S';; - 4C|5D)ARCH='Airmont';; - 8E|9E)ARCH='Kaby Lake';; - 57)ARCH='Knights Landing';; - 85)ARCH='Knights Mill';; - # product codes: https://en.wikipedia.org/wiki/List_of_Intel_microprocessors - # coming: coffee lake; cannonlake; icelake; tigerlake - esac - ;; - B) - case $3 in - 1)ARCH='Knights Corner';; - esac - ;; - F) - case $3 in - 0|1|2)ARCH='Netburst Willamette';; - 3|4|6)ARCH='Netburst Prescott';; # Nocona - *)ARCH='Netburst';; - esac - ;; - esac - ;; - - esac - log_function_data "ARCH: $ARCH" - eval $LOGFE } -## create A_CPU_CORE_DATA, currently with two values: integer core count; core string text -## return value cpu core count string, this helps resolve the multi redundant lines of old style output -get_cpu_core_count() -{ - eval $LOGFS - local cpu_physical_count='' cpu_core_count='' cpu_type='' cores_per_cpu='' - local array_data='' - - if [[ $B_CPUINFO_FILE == 'true' ]]; then - # load the A_CPU_TYPE_PCNT_CCNT core data array - get_cpu_ht_multicore_smp_data - ## Because of the upcoming release of cpus with core counts over 6, a count of cores is given after Deca (10) - # count the number of processors given - cpu_physical_count=${A_CPU_TYPE_PCNT_CCNT[1]} - cpu_core_count=${A_CPU_TYPE_PCNT_CCNT[2]} - cpu_type=${A_CPU_TYPE_PCNT_CCNT[0]} - - # match the numberic value to an alpha value - get_cpu_core_count_alpha "$cpu_core_count" - - # create array, core count integer; core count string - # A_CPU_CORE_DATA=( "$cpu_core_count" "$CPU_COUNT_ALPHA Core$cpu_type" ) - array_data="$cpu_physical_count,$CPU_COUNT_ALPHA,$cpu_type,$cpu_core_count" - IFS=',' - A_CPU_CORE_DATA=( $array_data ) - IFS="$ORIGINAL_IFS" - elif [[ -n $BSD_TYPE ]];then - local gawk_fs=': ' - - if [[ $BSD_VERSION == 'openbsd' ]];then - gawk_fs='=' - fi - cpu_core_count=$( gawk -F "$gawk_fs" -v bsdVersion="$BSD_VERSION" ' - # note: on openbsd can also be hw.ncpufound so exit after first - BEGIN { - coreCount="" - } - $1 ~ /^hw.ncpu$/ { - coreCount=$NF - } - /^machdep.cpu.core_count/ { - coreCount=$NF - } - END { - print coreCount - }' <<< "$SYSCTL_A_DATA" ) - cores_per_cpu=$( gawk -F "$gawk_fs" ' - /^machdep.cpu.cores_per_package/ { - print $NF - }' <<< "$SYSCTL_A_DATA" ) - - if [[ -n $( grep -E '^[0-9]+$' <<< "$cpu_core_count" ) ]];then - get_cpu_core_count_alpha "$cpu_core_count" - if [[ $cpu_core_count -gt 1 ]];then - cpu_type='-SMP-' - else - cpu_type='-UP-' - fi - fi - if [[ -n $cores_per_cpu ]];then - cpu_physical_count=$(( $cpu_core_count / $cores_per_cpu )) - if [[ $cores_per_cpu -gt 1 ]];then - cpu_type='-MCP-' - fi - # do not guess here, only use phys count if it actually exists, otherwise handle in print_cpu.. - # this 1 value should not be used for output, and is just to avoid math errors - else - cpu_physical_count=1 - fi - array_data="$cpu_physical_count,$CPU_COUNT_ALPHA,$cpu_type,$cpu_core_count" - IFS=',' - A_CPU_CORE_DATA=( $array_data ) - IFS="$ORIGINAL_IFS" - fi - a_temp=${A_CPU_CORE_DATA[@]} - # echo $a_temp :: ${#A_CPU_CORE_DATA[@]} - log_function_data "A_CPU_CORE_DATA: $a_temp" - eval $LOGFE -} - -# args: $1 - integer core count -get_cpu_core_count_alpha() +## CpuData { - eval $LOGFS - - case $1 in - 1) CPU_COUNT_ALPHA='Single';; - 2) CPU_COUNT_ALPHA='Dual';; - 3) CPU_COUNT_ALPHA='Triple';; - 4) CPU_COUNT_ALPHA='Quad';; - *) CPU_COUNT_ALPHA=$1;; - esac - log_function_data "CPU_COUNT_ALPHA: $CPU_COUNT_ALPHA" - - eval $LOGFE +package CpuData; + +sub get { + eval $start if $b_log; + my ($type) = @_; + my (@data,@rows,$single,$key1,$val1); + my $num = 0; + if ($type eq 'short' || $type eq 'basic'){ + @rows = data_short($type); + } + else { + @rows = create_output_full(); + } + eval $end if $b_log; + return @rows; } - -## main cpu data collector -get_cpu_data() -{ - eval $LOGFS - local i='' j='' cpu_array_nu='' a_cpu_working='' multi_cpu='' bits='' a_temp='' - local bsd_cpu_flags='' min_speed='' max_speed='' - - if [[ -f /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq ]];then - max_speed=$(cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq) - if [[ -f /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq ]];then - min_speed=$(cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq) - fi - fi - - if [[ $B_CPUINFO_FILE == 'true' ]];then - # stop script for a bit to let cpu slow down before parsing cpu /proc file - sleep $CPU_SLEEP - IFS=$'\n' - A_CPU_DATA=( $( - gawk -v cpuMin="$min_speed" -v cpuMax="$max_speed" -F': ' ' - BEGIN { - IGNORECASE=1 - # need to prime nr for arm cpus, which do not have processor number output in some cases - nr = 0 - count = 0 - bArm = "false" - bProcInt = "false" # this will avoid certain double counts with processor/Processor lines - # ARM cpus are erratic in /proc/cpuinfo this hack can sometimes resolve it. Linux only. - sysSpeed="'$(get_cpu_speed_hack)'" - speed = 0 - max = 0 - min = 0 - type="" - family="" - model_nu="" - rev="" - } - # TAKE STRONGER NOTE: \t+ does NOT always work, MUST be [ \t]+ - # TAKE NOTE: \t+ will work for $FILE_CPUINFO, but SOME ARBITRARY FILE used for TESTING might contain SPACES! - # Therefore PATCH to use [ \t]+ when TESTING! - /^processor[ \t]+:/ { - gsub(/'"$BAN_LIST_ARRAY"'/, " ", $NF) - gsub(/^ +| +$/, "", $NF) - if ( $NF ~ "^[0-9]+$" ) { - nr = $NF - bProcInt = "true" +sub create_output_full { + eval $start if $b_log; + my $num = 0; + my ($b_flags,$flag_key,@flags,%cpu,@data,@rows); + my $sleep = $cpu_sleep * 1000000; + if ($b_hires){ + eval 'Time::HiRes::usleep( $sleep )'; + } + else { + select(undef, undef, undef, $cpu_sleep); + } + if (my $file = main::system_files('cpuinfo')){ + %cpu = data_cpuinfo($file,'full'); + } + elsif ($bsd_type ){ + my ($key1,$val1) = ('',''); + if ( $alerts{'sysctl'} ){ + if ( $alerts{'sysctl'}{'action'} eq 'use' ){ +# $key1 = 'Status'; +# $val1 = main::row_defaults('dev'); + %cpu = data_sysctl('full'); } else { - # this protects against double processor lines, one int, one string - if ( bProcInt == "false" ){ - count += 1 - nr = count - 1 - } - # note: alternate: - # Processor : AArch64 Processor rev 4 (aarch64) - # but no model name type - if ( $NF ~ "(ARM|AArch)" ) { - bArm = "true" - if ( type=""){ - type="arm" - } - } - gsub(/'"$BAN_LIST_NORMAL"'/, "", $NF ) - gsub(/'"$BAN_LIST_CPU"'/, "", $NF ) - gsub(/^ +| +$/, "", $NF) - gsub(/ [ \t]+/, " ", $NF) - cpu[nr, "model"] = $NF + $key1 = ucfirst($alerts{'sysctl'}{'action'}); + $val1 = $alerts{'sysctl'}{$alerts{'sysctl'}{'action'}}; + @data = ({main::key($num++,$key1) => $val1,}); + return @data; } } - # arm - /^cpu architecture/ && (family = "") { - gsub(/^ +| +$/, "", $NF) - family=$NF + } + my %properties = cpu_properties(%cpu); + my $type = ($properties{'cpu-type'}) ? $properties{'cpu-type'}: ''; + my $ref = $cpu{'processors'}; + my @processors = @$ref; + my @speeds = cpu_speeds(@processors); + my $j = scalar @rows; + $cpu{'model_name'} ||= 'N/A'; + @data = ({ + main::key($num++,'Topology') => $properties{'cpu-layout'}, + main::key($num++,'model') => $cpu{'model_name'}, + },); + @rows = (@rows,@data); + $properties{'bits-sys'} ||= 'N/A'; + $rows[$j]{main::key($num++,'bits')} = $properties{'bits-sys'}; + if ($type){ + $rows[$j]{main::key($num++,'type')} = $type; + } + if ($extra > 0){ + $cpu{'arch'} ||= 'N/A'; + $rows[$j]{main::key($num++,'arch')} = $cpu{'arch'}; + if ( $cpu{'arch'} ne 'N/A' && $cpu{'rev'} ){ + $rows[$j]{main::key($num++,'rev')} = $cpu{'rev'}; } - /^cpu family/ && ( family == ""){ - gsub(/^ +| +$/, "", $NF) - family=toupper( sprintf("%x", $NF) ) + } + $properties{'l2-cache'} ||= 'N/A'; + $rows[$j]{main::key($num++,'L2 cache')} = $properties{'l2-cache'} if (!$b_arm || ($b_arm && $properties{'l2-cache'} ne 'N/A')); + if ($extra > 0 && !$show{'cpu-flag'}){ + $j = scalar @rows; + @flags = split /\s+/, $cpu{'flags'} if $cpu{'flags'}; + $flag_key = ($b_arm || $bsd_type) ? 'features': 'flags'; + my $flag = 'N/A'; + if (@flags){ + # failure to read dmesg.boot: dmesg.boot permissions + @flags = grep {/^(dmesg.boot|lm|nx|pae|permissions|pni|svm|vmx|(sss|ss)e([2-9])?([a-z])?(_[0-9])?)$/} @flags; + @flags = map {s/pni/sse3/; $_} @flags; + @flags = sort(@flags); + $flag = join ' ', @flags if @flags; + } + if ($b_arm && $flag eq 'N/A'){ + $flag = main::row_defaults('arm-cpu-f'); } - /^(stepping|cpu revission)/ && ( rev == "" ){ - gsub(/^ +| +$/, "", $NF) - rev=$NF + @data = ({ + main::key($num++,$flag_key) => $flag, + },); + @rows = (@rows,@data); + $b_flags = 1; + } + if ($extra > 0 && !$bsd_type){ + my $bogomips = ($cpu{'bogomips'}) ? int($cpu{'bogomips'}) : 'N/A'; + $rows[$j]{main::key($num++,'bogomips')} = $bogomips; + } + $j = scalar @rows; + my $core_key = (scalar @speeds > 1) ? 'Core speeds (MHz)' : 'Core speed (MHz)'; + my $speed_key = ($properties{'speed-key'}) ? $properties{'speed-key'}: 'Speed'; + my $min_max = ($properties{'min-max'}) ? $properties{'min-max'}: 'N/A'; + my $min_max_key = ($properties{'min-max-key'}) ? $properties{'min-max-key'}: 'min/max'; + my $speed = (defined $properties{'speed'}) ? $properties{'speed'}: 'N/A'; + # aren't able to get per core speeds in bsds yet + my $core_speeds_value = (@speeds) ? '' : 'N/A'; + $j = scalar @rows; + @data = ({ + main::key($num++,$speed_key) => $speed, + main::key($num++,$min_max_key) => $min_max, + main::key($num++,$core_key) => $core_speeds_value, + }, ); + @rows = (@rows,@data); + my $i = 1; + foreach (@speeds){ + $rows[$j]{main::key($num++,$i++)} = $_; + } + if ($show{'cpu-flag'} && !$b_flags){ + $flag_key = ($b_arm || $bsd_type) ? 'Features': 'Flags'; + @flags = split /\s+/, $cpu{'flags'} if $cpu{'flags'}; + my $flag = 'N/A'; + if (@flags){ + @flags = sort(@flags); + $flag = join ' ', @flags if @flags; } - /^model[ \t]*:/ && ( model_nu == ""){ - gsub(/^ +| +$/, "", $NF) - model_nu=toupper( sprintf("%x", $NF) ) + @data = ({ + main::key($num++,$flag_key) => $flag, + },); + @rows = (@rows,@data); + } + eval $end if $b_log; + return @rows; +} +sub create_output_short { + eval $start if $b_log; + my (@cpu) = @_; + my @data; + my $num = 0; + $cpu[1] ||= main::row_defaults('cpu-model-null'); + $cpu[2] ||= 'N/A'; + @data = ({ + main::key($num++,$cpu[0]) => $cpu[1], + main::key($num++,'type') => $cpu[2], + },); + if ($extra > 0){ + $data[0]{main::key($num++,'arch')} = $cpu[7]; + } + $data[0]{main::key($num++,$cpu[3])} = $cpu[4]; + if ($cpu[6]){ + $data[0]{main::key($num++,$cpu[5])} = $cpu[6]; + } + eval $end if $b_log; + return @data; +} +sub data_short { + eval $start if $b_log; + my ($type) = @_; + my $num = 0; + my (%cpu,@data,%speeds); + my $sys = '/sys/devices/system/cpu/cpufreq/policy0'; + my $sleep = $cpu_sleep * 1000000; + if ($b_hires){ + eval 'Time::HiRes::usleep( $sleep )'; + } + else { + select(undef, undef, undef, $cpu_sleep); + } + # NOTE: : Permission denied, ie, this is not always readable + # /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq + if (my $file = main::system_files('cpuinfo')){ + %cpu = data_cpuinfo($file,$type); + } + elsif ($bsd_type ){ + my ($key1,$val1) = ('',''); + if ( $alerts{'sysctl'} ){ + if ( $alerts{'sysctl'}{'action'} eq 'use' ){ +# $key1 = 'Status'; +# $val1 = main::row_defaults('dev'); + %cpu = data_sysctl($type); + } + else { + $key1 = ucfirst($alerts{'sysctl'}{'action'}); + $val1 = $alerts{'sysctl'}{$alerts{'sysctl'}{'action'}}; + @data = ({main::key($num++,$key1) => $val1,}); + return @data; + } } - /^model name|^cpu\t+:/ { - gsub(/'"$BAN_LIST_NORMAL"'/, "", $NF ) - gsub(/'"$BAN_LIST_CPU"'/, "", $NF ) - gsub(/'"$BAN_LIST_ARRAY"'/, " ", $NF) - gsub(/^ +| +$/, "", $NF) - gsub(/ [ \t]+/, " ", $NF) - cpu[nr, "model"] = $NF - if ( $NF ~ "^(ARM|AArch)" ) { - bArm = "true" + } + # $cpu{'cur-freq'} = $cpu[0]{'core-id'}[0]{'speed'}; + if ($type eq 'short' || $type eq 'basic'){ + @data = prep_short_data(%cpu); + } + if ($type eq 'basic'){ + @data = create_output_short(@data); + } + eval $end if $b_log; + return @data; +} + +sub prep_short_data { + eval $start if $b_log; + my (%cpu) = @_; + my %properties = cpu_properties(%cpu); + my ($cpu,$speed_key,$speed,$type) = ('','speed',0,''); + $cpu = $cpu{'model_name'} if $cpu{'model_name'}; + $type = $properties{'cpu-type'} if $properties{'cpu-type'}; + $speed_key = $properties{'speed-key'} if $properties{'speed-key'}; + $speed = $properties{'speed'} if $properties{'speed'}; + my @result = ( + $properties{'cpu-layout'}, + $cpu, + $type, + $speed_key, + $speed, + $properties{'min-max-key'}, + $properties{'min-max'}, + ); + if ($extra > 0){ + $cpu{'arch'} ||= 'N/A'; + $result[7] = $cpu{'arch'}; + } + eval $end if $b_log; + return @result; +} + +sub data_cpuinfo { + eval $start if $b_log; + my ($file,$type)= @_; + my ($arch,@ids,@line,$b_first,$b_proc_int,$starter); + # use --arm flag when testing arm cpus + # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/arm/arm-4-core-pinebook-1.txt"; + # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/arm/armv6-single-core-1.txt"; + # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/arm/armv7-dual-core-1.txt"; + # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/arm/armv7-new-format-model-name-single-core.txt"; + # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/arm/arm-2-die-96-core-rk01.txt"; + # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/amd/16-core-32-mt-ryzen.txt"; + # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/amd/2-16-core-epyc-abucodonosor.txt"; + # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/amd/2-core-probook-antix.txt"; + # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/amd/4-core-jean-antix.txt"; + # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/amd/4-core-althlon-mjro.txt"; + # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/amd/4-core-apu-vc-box.txt"; + # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/amd/4-core-a10-5800k-1.txt"; + # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/intel/2-core-ht-atom-bruh.txt"; + # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/intel/core-2-i3.txt"; + # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/intel/8-core-i7-damentz64.txt"; + # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/intel/2-10-core-xeon-ht.txt"; + # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/intel/4-core-xeon-fake-dual-die-zyanya.txt"; + # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/intel/2-core-i5-fake-dual-die-hek.txt"; + # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/intel/2-1-core-xeon-vm-vs2017.txt"; + # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/intel/4-1-core-xeon-vps-frodo1.txt"; + # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/intel/4-6-core-xeon-no-mt-lathander.txt"; + my %speeds = set_cpu_speeds_sys(); + my @cpuinfo = main::reader($file); + my @phys_cpus = (0);# start with 1 always + my ($cache,$core_count,$die_holder,$die_id,$phys_id,$proc_count,$speed) = (0,0,0,0,0,0,0); + my ($phys_holder) = (undef); + # need to prime for arm cpus, which do not have physical/core ids usually + # level 0 is phys id, level 1 is die id, level 2 is core id + #$ids[0] = ([(0)]); + $ids[0] = ([]); + $ids[0][0] = ([]); + my %cpu = set_cpu_data(); + # note, there con be a lot of processors, 32 core HT would have 64, for example. + foreach (@cpuinfo){ + next if /^\s*$/; + @line = split /\s*:\s*/, $_; + next if !$line[0]; + $starter = $line[0]; # preserve case for one specific ARM issue + $line[0] = lc($line[0]); + if ($b_arm && !$b_first && $starter eq 'Processor' && $line[1] !~ /^\d+$/){ + #print "l1:$line[1]\n"; + $cpu{'model_name'} = main::cleaner($line[1]); + $cpu{'model_name'} = cpu_cleaner($cpu{'model_name'}); + $cpu{'type'} = 'arm'; + # Processor : AArch64 Processor rev 4 (aarch64) + # Processor : Feroceon 88FR131 rev 1 (v5l) + if ($cpu{'model_name'} && $cpu{'model_name'} =~ /(.*)\srev\s([\S]+)\s(\(([\S]+)\))?/){ + $cpu{'model_name'} = $1; + $cpu{'rev'} = $2; + if ($4){ + $cpu{'arch'} = $4; + $cpu{'model_name'} .= ' ' . $cpu{'arch'} if $cpu{'model_name'} !~ /$cpu{'arch'}/i; + } + $cpu{'processors'}[$proc_count] = 0; + $b_proc_int = 0; + $b_first = 1; + #print "p0:\n"; } } - /^cpu MHz|^clock\t+:/ { - if (speed == 0) { - speed = $NF + elsif ($line[0] eq 'processor'){ + # this protects against double processor lines, one int, one string + if ($line[1] =~ /^\d+$/){ + $b_proc_int = 1; + $b_first = 1; + $cpu{'processors'}[$proc_count] = 0; + $proc_count++; + #print "p1: $proc_count\n"; } else { - if ($NF < speed) { - speed = $NF + if (!$b_proc_int){ + $cpu{'processors'}[$proc_count] = 0; + $proc_count++; + #print "p2a: $proc_count\n"; } + if (!$b_first ){ + # note: alternate: + # Processor : AArch64 Processor rev 4 (aarch64) + # but no model name type + if ( $b_arm || $line[1] =~ /ARM|AArch/i){ + $b_arm = 1; + $cpu{'type'} = 'arm'; + } + $cpu{'model_name'} = main::cleaner($line[1]); + $cpu{'model_name'} = cpu_cleaner($cpu{'model'}); + #print "p2b:\n"; + } + $b_first = 1; + } + } + elsif (!$cpu{'family'} && + ($line[0] eq 'architecture' || $line[0] eq 'cpu family' || $line[0] eq 'cpu architecture' )){ + if ($line[1] =~ /^\d+$/){ + # translate integers to hex + $cpu{'family'} = uc(sprintf("%x", $line[1])); } - if ($NF > max) { - max = $NF + elsif ($b_arm) { + $cpu{'arch'} = $line[1]; } - gsub(/MHZ/,"",$NF) ## clears out for cell cpu - gsub(/.00[0]+$/,".00",$NF) ## clears out excessive zeros - cpu[nr, "speed"] = $NF } - /^cache size/ { - cpu[nr, "cache"] = $NF + elsif (!$cpu{'rev'} && ($line[0] eq 'stepping' || $line[0] eq 'cpu revision' )){ + $cpu{'rev'} = $line[1]; } - /^flags|^features/ { - cpu[nr, "flags"] = $NF - # not all ARM cpus show ARM in model name - if ( $1 ~ /^features/ ) { - bArm = "true" - } + # this is hex so uc for cpu arch id + elsif (!$cpu{'model_id'} && $line[0] eq 'model' ){ + $cpu{'model_id'} = uc(sprintf("%x", $line[1])); } - /^bogomips/ { - cpu[nr, "bogomips"] = $NF - # print nr " " cpu[nr, "bogomips"] > "/dev/tty" + elsif (!$cpu{'model_id'} && $line[0] eq 'cpu variant' ){ + $cpu{'model_id'} = uc($line[1]); + $cpu{'model_id'} =~ s/^0X//; } - /vendor_id/ { - gsub(/genuine|authentic/,"",$NF) - cpu[nr, "vendor"] = tolower( $NF ) - if ( type == ""){ - if ( $NF ~ /.*intel.*/ ) { - type="intel" - } - else if ($NF ~ /.*amd.*/){ - type="amd" - } - # via - else if ($NF ~ /.*centaur.*/){ - type="centaur" + # cpu can show in arm + elsif (!$cpu{'model_name'} &&( $line[0] eq 'model name' || $line[0] eq 'cpu' )){ + $cpu{'model_name'} = main::cleaner($line[1]); + $cpu{'model_name'} = cpu_cleaner($cpu{'model_name'}); + if ( $b_arm || $line[1] =~ /ARM|AArch/i){ + $b_arm = 1; + $cpu{'type'} = 'arm'; + if ($cpu{'model_name'} && $cpu{'model_name'} =~ /(.*)\srev\s([\S]+)\s(\(([\S]+)\))?/){ + $cpu{'model_name'} = $1; + $cpu{'rev'} = $2; + if ($4){ + $cpu{'arch'} = $4; + $cpu{'model_name'} .= ' ' . $cpu{'arch'} if $cpu{'model_name'} !~ /$cpu{'arch'}/i; + } + #$cpu{'processors'}[$proc_count] = 0; } } } - END { - #if (!nr) { print ",,,"; exit } # <- should this be necessary or should bash handle that - for ( i = 0; i <= nr; i++ ) { - # note: assuming bogomips for arm at 1 x clock - # http://en.wikipedia.org/wiki/BogoMips ARM could change so watch this - # maybe add: && bArm == "true" but I think most of the bogomips roughly equal cpu speed if not amd/intel - # 2014-04-08: trying to use sysSpeed hack first, that is more accurate anyway. - if ( ( cpu[i, "speed"] == "" && sysSpeed != "" ) || \ - ( cpu[i, "speed"] == "" && cpu[i, "bogomips"] != "" && cpu[i, "bogomips"] < 50 ) ) { - cpu[i, "speed"] = sysSpeed - } - else if ( cpu[i, "bogomips"] != "" && cpu[i, "speed"] == "" ) { - cpu[i, "speed"] = cpu[i, "bogomips"] - - } - print cpu[i, "model"] "," cpu[i, "speed"] "," cpu[i, "cache"] "," cpu[i, "flags"] "," cpu[i, "bogomips"] "," cpu[nr, "vendor"] "," bArm + elsif ( $line[0] eq 'cpu mhz' ){ + $speed = speed_cleaner($line[1]); + $cpu{'processors'}[$proc_count-1] = $speed; + #$ids[$phys_id][$die_id] = ([($speed)]); + } + elsif (!$cpu{'siblings'} && $line[0] eq 'siblings' ){ + $cpu{'siblings'} = $line[1]; + } + elsif (!$cpu{'cores'} && $line[0] eq 'cpu cores' ){ + $cpu{'cores'} = $line[1]; + } + # increment by 1 for every new physical id we see. These are in almost all cases + # separate cpus, not separate dies within a single cpu body. + elsif ( $line[0] eq 'physical id' ){ + if ( !defined $phys_holder || $phys_holder != $line[1] ){ + # only increment if not in array counter + push @phys_cpus, $line[1] if ! grep {/$line[1]/} @phys_cpus; + $phys_holder = $line[1]; + $ids[$phys_holder] = ([]) if ! exists $ids[$phys_holder]; + $ids[$phys_holder][$die_id] = ([]) if ! exists $ids[$phys_holder][$die_id]; + #print "pid: $line[1] ph: $phys_holder did: $die_id\n"; + $die_id = 0; + #$die_holder = 0; } - if (cpuMin != "") { - min=cpuMin/1000 + } + elsif ( $line[0] eq 'core id' ){ + #print "ph: $phys_holder did: $die_id l1: $line[1] s: $speed\n"; + # https://www.pcworld.com/article/3214635/components-processors/ryzen-threadripper-review-we-test-amds-monster-cpu.html + if ($line[1] > 0 ){ + $die_holder = $line[1]; + $core_count++; } - if (cpuMax != "") { - max=cpuMax/1000 + # NOTE: this logic won't work for die detections, unforutnately. + # ARM uses a different /sys based method, and ryzen relies on math on the cores + # in process_data + elsif ($line[1] == 0 && $die_holder > 0 ){ + $die_holder = $line[1]; + $core_count = 0; + $die_id++ if ($cpu{'type'} ne 'intel' && $cpu{'type'} ne 'amd' ); } - # create last array index, to be used for min/max output - sub(/\.[0-9]+$/,"",max) - sub(/\.[0-9]+$/,"",speed) - sub(/\.[0-9]+$/,"",min) - if ( bArm == "true" ){ - type = "arm" + $phys_holder = 0 if ! defined $phys_holder; + $ids[$phys_holder][$die_id][$line[1]] = $speed; + #print "ph: $phys_holder did: $die_id l1: $line[1] s: $speed\n"; + } + if (!$cpu{'type'} && $line[0] eq 'vendor_id' ){ + $cpu{'type'} = cpu_vendor($line[1]); + } + ## this is only for -C full cpu output + if ( $type eq 'full' ){ + if (!$cpu{'l2-cache'} && $line[0] eq 'cache size'){ + if ($line[1] =~ /(\d+)\sKB$/){ + $cpu{'l2-cache'} = $1; + } + elsif ($line[1] =~ /(\d+)\sMB$/){ + $cpu{'l2-cache'} = ($1*1024); + } } - if (speed == 0) { - print "N/A," min "," max "," type "," family "," model_nu + if (!$cpu{'flags'} && ($line[0] eq 'flags' || $line[0] eq 'features' )){ + $cpu{'flags'} = $line[1]; } - else { - # print speed "," min "," max "," type "," family "," model_nu "," rev > "/dev/tty" - print speed "," min "," max "," type "," family "," model_nu "," rev + } + if ( $extra > 0 && $type eq 'full' ){ + if ($line[0] eq 'bogomips'){ + # new arm shows bad bogomip value, so don't use it + $cpu{'bogomips'} += $line[1] if $line[1] > 50; } - } - ' $FILE_CPUINFO ) ) - - IFS="$ORIGINAL_IFS" - log_function_data 'cat' "$FILE_CPUINFO" - elif [[ -n $BSD_TYPE ]];then - get_cpu_data_bsd - fi - - a_temp=${A_CPU_DATA[@]} - log_function_data "A_CPU_DATA: $a_temp" -# echo ta: ${a_temp[@]} - eval $LOGFE -# echo getMainCpu: ${[@]} -} -# this triggers in one and only one case, ARM cpus that have fake bogomips data -get_cpu_speed_hack() -{ - local speed=$( cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq 2>/dev/null ) - - if [[ -n $speed ]];then - speed=${speed%[0-9][0-9][0-9]} # trim off last 3 digits - fi - echo $speed + } + } + $cpu{'phys'} = scalar @phys_cpus; + $cpu{'dies'} = $die_id++; # count starts at 0, all cpus have 1 die at least + if ($b_arm && $cpu{'dies'} <= 1){ + my $arm_dies = cpu_dies_sys(); + # case were 4 core arm returned 4 sibling lists, obviously wrong + $cpu{'dies'} = $arm_dies if $arm_dies && $proc_count != $arm_dies; + } + $cpu{'type'} = 'arm' if $b_arm && !$cpu{'type'}; + $cpu{'ids'} = (\@ids); + if ( $extra > 0 && !$cpu{'arch'} && $type ne 'short' ){ + $cpu{'arch'} = cpu_arch($cpu{'type'},$cpu{'family'},$cpu{'model_id'}); + #print "$cpu{'type'},$cpu{'family'},$cpu{'model_id'},$cpu{'arch'}\n"; + } + if (!$speeds{'cur-freq'}){ + $cpu{'cur-freq'} = $cpu{'processors'}[0]; + $speeds{'min-freq'} = 0; + $speeds{'max-freq'} = 0; + } + else { + $cpu{'cur-freq'} = $speeds{'cur-freq'}; + $cpu{'min-freq'} = $speeds{'min-freq'}; + $cpu{'max-freq'} = $speeds{'max-freq'}; + } + main::log_data('dump','%cpu',\%cpu) if $b_log; + print Data::Dumper::Dumper \%cpu if $test[8]; + eval $end if $b_log; + return %cpu; } -get_cpu_data_bsd() -{ - eval $LOGFS - - local bsd_cpu_flags=$( get_cpu_flags_bsd ) - local gawk_fs=': ' cpu_max='' - - if [[ $BSD_VERSION == 'openbsd' ]];then - gawk_fs='=' - fi - # avoid setting this for systems where you have no read/execute permissions - # might be cases where the dmesg boot file was readable but sysctl perms failed - if [[ -n $SYSCTL_A_DATA || -n $bsd_cpu_flags ]];then - if [[ -n $DMESG_BOOT_DATA ]];then - cpu_max=$( gawk -F ':' ' - BEGIN { - IGNORECASE=1 - } - # NOTE: freebsd may say: 2300-MHz, so check for dash as well - $1 ~ /^(CPU|cpu0)$/ { - if ( $NF ~ /[^0-9\.][0-9\.]+[\-[:space:]]*[MG]Hz/) { - max=gensub(/.*[^0-9\.]([0-9\.]+[\-[:space:]]*[MG]Hz).*/,"\\1",1,$NF) - if (max ~ /MHz/) { - sub(/[-[:space:]]*MHz/,"",max) - } - if (max ~ /GHz/) { - sub(/[-[:space:]]*GHz/,"",max) - max=max*1000 - } - print max - exit - } - }' <<< "$DMESG_BOOT_DATA" ) - fi - IFS=$'\n' - A_CPU_DATA=( $( - gawk -F "$gawk_fs" -v bsdVersion=$BSD_VERSION -v cpuFlags="$bsd_cpu_flags" -v cpuMax="$cpu_max" ' - BEGIN { - IGNORECASE=1 - cpuModel="" - cpuClock="" - cpuCache="" - cpuBogomips="" - cpuVendor="" - bSwitchFs="false" - min=0 - max=0 - # these can be found in dmesg.boot just like in cpuinfo except all in one row - type="" - family="" - model_nu="" - rev="" - } - /^hw.model/ && ( bsdVersion != "darwin" ) { - gsub(/'"$BAN_LIST_NORMAL"'/, "", $NF ) - gsub(/'"$BAN_LIST_CPU"'/, "", $NF ) - gsub(/'"$BAN_LIST_ARRAY"'/," ",$NF) - sub(/[a-z]+-core/, "", $NF ) - gsub(/^ +| +$|\"/, "", $NF) - gsub(/ [ \t]+/, " ", $NF) +sub data_sysctl { + eval $start if $b_log; + my ($type) = @_; + my %cpu = set_cpu_data(); + my (@ids,@line,%speeds,@working); + my ($sep) = (''); + my ($cache,$die_holder,$die_id,$phys_holder,$phys_id,$proc_count,$speed) = (0,0,0,0,0,0,0); + foreach (@sysctl){ + @line = split /\s*:\s*/, $_; + next if ! $line[0]; + # darwin shows machine, like MacBook7,1, not cpu + # machdep.cpu.brand_string: Intel(R) Core(TM)2 Duo CPU P8600 @ 2.40GHz + if ( ($bsd_type ne 'darwin' && $line[0] eq 'hw.model' ) || $line[0] eq 'machdep.cpu.brand_string' ){ # cut L2 cache/cpu max speed out of model string, if available - if ( $NF ~ /[0-9]+[[:space:]]*[KM]B[[:space:]]+L2 cache/) { - cpuCache=gensub(/.*[^0-9]([0-9]+[[:space:]]*[KM]B)[[:space:]]+L2 cach.*/,"\\1",1,$NF) + # openbsd 5.6: AMD Sempron(tm) Processor 3400+ ("AuthenticAMD" 686-class, 256KB L2 cache) + # freebsd 10: hw.model: AMD Athlon(tm) II X2 245 Processor + $line[1] = main::cleaner($line[1]); + $line[1] = cpu_cleaner($line[1]); + if ( $line[1] =~ /([0-9]+)[\-[:space:]]*([KM]B)\s+L2 cache/) { + my $multiplier = ($2 eq 'KB') ? 1024: 1; + $cpu{'l2-cache'} = $1 * $multiplier; } - if ( $NF ~ /[^0-9\.][0-9\.]+[\-[:space:]]*[MG]Hz/) { - max=gensub(/.*[^0-9\.]([0-9\.]+[\-[:space:]]*[MG]Hz).*/,"\\1",1,$NF) - if (max ~ /MHz/) { - sub(/[\-[:space:]]*MHz/,"",max) + if ( $line[1] =~ /([^0-9\.][0-9\.]+)[\-[:space:]]*[MG]Hz/) { + $cpu{'max-freq'} = $1; + if ($cpu{'max-freq'} =~ /MHz/i) { + $cpu{'max-freq'} =~ s/[\-[:space:]]*MHz//; + $cpu{'max-freq'} = speed_cleaner($cpu{'max-freq'},'mhz'); } - if (max ~ /GHz/) { - sub(/[\-[:space:]]*GHz/,"",max) - max=max*1000 + elsif ($cpu{'max-freq'} =~ /GHz/) { + $cpu{'max-freq'} =~ s/[\-[:space:]]*GHz//i; + $cpu{'max-freq'} = $cpu{'max-freq'} / 1000; + $cpu{'max-freq'} = speed_cleaner($cpu{'max-freq'},'mhz'); } } - if ( $NF ~ /\)$/ ){ - sub(/[[:space:]]*\(.*\)$/,"",$NF) + if ( $line[1] =~ /\)$/ ){ + $line[1] =~ s/\s*\(.*\)$//; } - cpuModel=$NF -# if ( cpuClock != "" ) { -# exit -# } + $cpu{'model_name'} = $line[1]; + $cpu{'type'} = cpu_vendor($line[1]); } - /^hw.clock/ { - cpuClock=$NF -# if ( cpuModel != "" ) { -# exit -# } + # NOTE: hw.l1icachesize: hw.l1dcachesize: + elsif ($line[0] eq 'hw.l1icachesize') { + $cpu{'l1-cache'} = $line[1]/1024; + } + elsif ($line[0] eq 'hw.l2cachesize') { + $cpu{'l2-cache'} = $line[1]/1024; + } + # this is in mghz in samples + elsif ($line[0] eq 'hw.clockrate' || $line[0] eq 'hw.cpuspeed') { + $cpu{'cur-freq'} = $line[1]; } - /^hw.cpufrequency/ { - cpuClock = $NF / 1000000 + # these are in hz: 2400000000 + elsif ($line[0] eq 'hw.cpufrequency') { + $cpu{'cur-freq'} = $line[1]/1000000; } - /^hw.cpuspeed/ { - cpuClock=$NF + elsif ($line[0] eq 'hw.busfrequency_min') { + $cpu{'min-freq'} = $line[1]/1000000; } - /^hw.l2cachesize/ { - cpuCache=$NF/1024 - cpuCache=cpuCache " kB" + elsif ($line[0] eq 'hw.busfrequency_max') { + $cpu{'max-freq'} = $line[1]/1000000; } - /^machdep.cpu.vendor/ { - cpuVendor=$NF + elsif ($line[0] eq 'machdep.cpu.vendor') { + $cpu{'type'} = cpu_vendor($line[1]); + } + # darwin only? + elsif ($line[0] eq 'machdep.cpu.features') { + $cpu{'flags'} = lc($line[1]); + } + elsif ($line[0] eq 'hw.ncpu' ) { + $cpu{'cores'} = $line[1]; } # Freebsd does some voltage hacking to actually run at lowest listed frequencies. # The cpu does not actually support all the speeds output here but works in freebsd. - /^dev.cpu.0.freq_levels/ { - gsub(/^[[:space:]]+|\/[0-9]+|[[:space:]]+$/,"",$NF) - if ( $NF ~ /[0-9]+[[:space:]]+[0-9]+/ ) { - min=gensub(/.*[[:space:]]([0-9]+)$/,"\\1",1,$NF) - max=gensub(/^([0-9]+)[[:space:]].*/,"\\1",1,$NF) - } - } - /^machdep.cpu.brand_string/ { - gsub(/'"$BAN_LIST_NORMAL"'/, "", $NF ) - gsub(/'"$BAN_LIST_CPU"'/, "", $NF ) - gsub(/'"$BAN_LIST_ARRAY"'/," ",$NF) - sub(/[a-z]+-core/, "", $NF ) - gsub(/^ +| +$|\"/, "", $NF) - gsub(/ [ \t]+/, " ", $NF) - sub(/[[:space:]]*@.*/,"",$NF) - cpuModel=$NF - } - END { - if ( max == 0 && cpuMax != "" ) { - max=cpuMax - } - if ( cpuClock == "" ) { - cpuClock="N/A" - } - sub(/\.[0-9]+/,"",cpuClock) - sub(/\.[0-9]+/,"",min) - sub(/\.[0-9]+/,"",max) - print cpuModel "," cpuClock "," cpuCache "," cpuFlags "," cpuBogomips "," cpuVendor - # triggers print case, for architecture, check source for syntax - print cpuClock "," min "," max ",,,," - }' <<< "$SYSCTL_A_DATA" ) ) - IFS="$ORIGINAL_IFS" - fi - - eval $LOGFE + elsif ($line[0] eq 'dev.cpu.0.freq_levels') { + $line[1] =~ s/^\s+|\/[0-9]+|\s+$//g; + if ( $line[1] =~ /[0-9]+\s+[0-9]+/ ) { + my @temp = split /\s+/, $line[1]; + $cpu{'max-freq'} = $temp[0]; + $cpu{'min-freq'} = $temp[-1]; + $cpu{'scalings'} = \@temp; + } + } + elsif (!$cpu{'cur-freq'} && $line[0] eq 'dev.cpu.0.freq' ) { + $cpu{'cur-freq'} = $line[1]; + } + # the following have only been seen in DragonflyBSD data but thumbs up! + elsif ($line[0] eq 'hw.cpu_topology.members' ) { + my @temp = split /\s+/, $line[1]; + my $count = scalar @temp; + $count-- if $count > 0; + $cpu{'processors'}[$count] = 0; + # no way to get per processor speeds yet, so assign 0 to each + foreach (0 .. $count){ + $cpu{'processors'}[$_] = 0; + } + } + elsif ($line[0] eq 'hw.cpu_topology.cpu1.physical_siblings' ) { + # string, like: cpu0 cpu1 + my @temp = split /\s+/, $line[1]; + $cpu{'siblings'} = scalar @temp; + } + # increment by 1 for every new physical id we see. These are in almost all cases + # separate cpus, not separate dies within a single cpu body. + elsif ( $line[0] eq 'hw.cpu_topology.cpu0.physical_id' ){ + if ($phys_holder != $line[1] ){ + $phys_id++; + $phys_holder = $line[1]; + $ids[$phys_id] = ([(0)]); + $ids[$phys_id][$die_id] = ([(0)]); + } + } + elsif ( $line[0] eq 'hw.cpu_topology.cpu0.core_id' ){ + if ($line[1] > 0 ){ + $die_holder = $line[1]; + } + # this handles multi die cpus like 16 core ryzen + elsif ($line[1] == 0 && $die_holder > 0 ){ + $die_id++ ; + $die_holder = $line[1]; + } + $ids[$phys_id][$die_id][$line[1]] = $speed; + $cpu{'dies'} = $die_id; + } + } + if (!$cpu{'flags'}){ + $cpu{'flags'} = cpu_flags_bsd(); + } + main::log_data('dump','%cpu',\%cpu) if $b_log; + print Data::Dumper::Dumper \%cpu if $test[8]; + eval $end if $b_log; + return %cpu; } -get_cpu_flags_bsd() -{ - eval $LOGFS - - local cpu_flags='' - local gawk_fs=':' - - if [[ -n $DMESG_BOOT_DATA ]];then - cpu_flags=$( gawk -v bsdVersion="$BSD_VERSION" -F ":" ' - BEGIN { - IGNORECASE=1 - cpuFlags="" - } - /^(CPU:|cpu0:)/ { - while ( getline && !/memory|real mem/ ) { - if ( $1 ~ /Features/ || ( bsdVersion == "openbsd" && $0 ~ /^cpu0.*[[:space:]][a-z][a-z][a-z][[:space:]][a-z][a-z][a-z][[:space:]]/ ) ) { - # clean up odd stuff like <b23> - gsub(/<[a-z0-9]+>/,"", $2) - # all the flags are contained within < ... > on freebsd at least - gsub(/.*<|>.*/,"", $2) - gsub(/'"$BAN_LIST_ARRAY"'/," ", $2) - cpuFlags = cpuFlags " " $2 - } - } - cpuFlags=tolower(cpuFlags) - print cpuFlags - exit - }' <<< "$DMESG_BOOT_DATA" ) - elif [[ -n $SYSCTL_A_DATA ]];then - if [[ $BSD_VERSION == 'openbsd' ]];then - gawk_fs=':' - fi - cpu_flags=$( gawk -F "$gawk_fs" ' - BEGIN { - cpuFlags="" - } - /^machdep.cpu.features/ { - cpuFlags=tolower($NF) - print cpuFlags - exit - }' <<< "$SYSCTL_A_DATA" ) - fi - echo $cpu_flags - log_function_data "$cpu_flags" - eval $LOGFE -} - -## this is for counting processors and finding HT types -get_cpu_ht_multicore_smp_data() -{ - eval $LOGFS - # in /proc/cpuinfo - local a_temp='' - - # note: known bug with xeon intel, they show a_core_id/physical_id as 0 for ht 4 core - if [[ $B_CPUINFO_FILE == 'true' ]]; then - A_CPU_TYPE_PCNT_CCNT=( $( - gawk ' - BEGIN { - FS=": " - IGNORECASE = 1 - num_of_cores = 0 - num_of_processors = 0 - num_of_physical_cpus = 0 - cpu_core_count = 0 - siblings = 0 - # these 3 arrays cannot be declared because that sets the first element - # but leaving this here so that we avoid doing that in the future - # a_core_id = "" - # a_processor_id = "" - # a_physical_id = "" - cpu_type = "-" - # note: we need separate iterators because some cpuinfo data has only - # processor, no core id or phys id - proc_iter = 0 - core_iter = "" # set from actual NF data - phys_iter = "" # set from actual NF data - # needed to handle arm cpu, no processor number cases - arm_count = 0 - nr = 0 - bArm = "false" - bRyzen = "false" - bProcInt = "false" # this will avoid certain double counts with processor/Processor lines - bXeon = "false" - } - # hack to handle xeons which can have buggy /proc/cpuinfo files - /^model name/ && ( $0 ~ /Xeon/ ) { - bXeon = "true" - } - # amd rizen 16/32 core maybe - /^cpu_family/ && ($2 == 23) { - bRyzen = "true" - } - # only do this once since sibling count does not change. - /^siblings/ && ( bXeon == "true" || bRyzen == "true" ) && ( siblings == 0 ) { - gsub(/[^0-9]/,"",$NF) - if ( $NF != "" ) { - siblings = $NF - } - } - # array of logical processors, both HT and physical - # IMPORTANT: some variants have two lines, one the actual cpu id number, - # the other a misnamed model name line. - # processor : 0 - # Processor : AArch64 Processor rev 4 (aarch64) - /^processor/ { - gsub(/'"$BAN_LIST_ARRAY"'/, " ", $NF) - gsub(/^ +| +$/, "", $NF) - if ( $NF ~ "^[0-9]+$" ) { - a_processor_id[proc_iter] = $NF - proc_iter++ - bProcInt = "true" +sub cpu_properties { + my (%cpu) = @_; + my ($b_amd_zen,$b_epyc,$b_ht,$b_intel,$b_ryzen,$b_xeon); + if ($cpu{'type'} ){ + if ($cpu{'type'} eq 'intel'){ + $b_intel = 1; + $b_xeon = 1 if $cpu{'model_name'} =~ /Xeon/i; + } + elsif ($cpu{'type'} eq 'amd' ){ + if ( $cpu{'family'} && $cpu{'family'} eq '17' ) { + $b_amd_zen = 1; + if ($cpu{'model_name'} ){ + if ($cpu{'model_name'} =~ /Ryzen/i ){ + $b_ryzen = 1; + } + elsif ($cpu{'model_name'} =~ /EPYC/i){ + $b_epyc = 1; + } + } } - else { - # note, for dual core, this can be off by one because the first - # line says: Processor : Arm.. but subsequent say: processor : 0 and so on as usual - # Processor : AArch64 Processor rev 4 (aarch64) - if ( $NF ~ "^(ARM|AArch)" ) { - bArm = "true" - } - # this protects against double processor lines, one int, one string - if ( bProcInt == "false" ){ - arm_count += 1 - nr = arm_count - 1 - # note: do not iterate because new ARM syntax puts cpu in processsor : 0 syntax - a_processor_id[proc_iter] = nr - } - } - } - # array of physical cpu ids, note, this will be unset for vm cpus in many cases - # because they have no physical cpu, so we cannot assume this will be here. - /^physical/ { - phys_iter = $NF - a_physical_id[phys_iter] = $NF - } - # array of core ids, again, here we may have HT, so we need to create an array of the - # actual core ids. As With physical, we cannot assume this will be here in a vm - # also, for xeon/rizen, this can be misleading because there may be two cpus - # inside the single cpu body, which leads to: 0-7 then 0-7 for a rizen 16 core ht - /^core id/ { - core_iter = $NF - a_core_id[core_iter] = $NF - } - # this will be used to fix an intel glitch if needed, cause, intel - # sometimes reports core id as the same number for each core, - # so if cpu cores shows greater value than number of cores, use this. - /^cpu cores/ { - cpu_core_count = $NF - } - END { - ## Look thru the array and filter same numbers. - ## only unique numbers required - ## this is to get an accurate count - ## we are only concerned with array length - i = 0 - ## count unique processors ## - # note, this fails for intel cpus at times - for ( i in a_processor_id ) { - num_of_processors++ - } - i = 0 - ## count unique physical cpus ## - for ( i in a_physical_id ) { - num_of_physical_cpus++ - } - i = 0 - ## count unique cores. Fails for 16 core rizen, which appears to be - ## using 2x8 core internally, core id 0-7 repeats 2 times. - for ( i in a_core_id ) { - num_of_cores++ - } - # xeon may show wrong core / physical id count, if it does, fix it. A xeon - # may show a repeated core id : 0 which gives a fake num_of_cores=1 - if ( bXeon == "true" && num_of_cores == 1 && siblings > 1 ) { - num_of_cores = siblings/2 + } + } + #my @dies = $phys[0][0]; + my $ref = $cpu{'ids'}; + my @phys = @$ref; + my $phyical_count = 0; + #my $phyical_count = scalar @phys; + my @processors; + my ($speed,$speed_key); + # handle case where cpu reports say, phys id 0, 2, 4, 6 [yes, seen it] + foreach (@phys) { + $phyical_count++ if $_; + } + $phyical_count ||= 1; # assume 1 if no id found, as with ARM + # count unique processors ## + # note, this fails for intel cpus at times + $ref = $cpu{'processors'}; + @processors = @$ref; + #print ref $cpu{'processors'}, "\n"; + my $processors_count = scalar @processors; + #print "p count:$processors_count\n"; + #print Data::Dumper::Dumper \@processors; + # $cpu_cores is per physical cpu + my ($cpu_layout,$cpu_type,$min_max,$min_max_key) = ('','','',''); + my ($cache,$core_count,$cpu_cores,$die_count) = (0,0,0,0); + foreach my $die_ref ( @phys ){ + next if ! $die_ref; + my @dies = @$die_ref; + $core_count = 0; + $die_count = scalar @dies; + #$cpu{'dies'} = $die_count; + foreach my $core_ref (@dies){ + next if ref $core_ref ne 'ARRAY'; + my @cores = @$core_ref; + $core_count = 0;# reset for each die!! + # NOTE: the counters can be undefined because the index comes from + # core id: which can be 0 skip 1 then 2, which leaves index 1 undefined + # arm cpus do not actually show core id so ignore that counter + foreach my $id (@cores){ + $core_count++ if defined $id && !$b_arm; + } + #print 'cores: ' . $core_count, "\n"; + } + } + # this covers potentially cases where ARM cpus have > 1 die + $cpu{'dies'} = ($b_arm && $die_count <= 1 && $cpu{'dies'} > 1) ? $cpu{'dies'}: $die_count; + # this is an attempt to fix the amd family 15 bug with reported cores vs actual cores + # NOTE: amd A6-4400M APU 2 core reports: cores: 1 siblings: 2 + # NOTE: AMD A10-5800K APU 4 core reports: cores: 2 siblings: 4 + if ($cpu{'cores'} && ! $core_count || $cpu{'cores'} >= $core_count){ + $cpu_cores = $cpu{'cores'}; + } + elsif ($core_count > $cpu{'cores'}){ + $cpu_cores = $core_count; + } + #print "cpu-c:$cpu_cores\n"; + #$cpu_cores = $cpu{'cores'}; + # like, intel core duo + # NOTE: sadly, not all core intel are HT/MT, oh well... + # xeon may show wrong core / physical id count, if it does, fix it. A xeon + # may show a repeated core id : 0 which gives a fake num_of_cores=1 + if ($b_intel){ + if ($cpu{'siblings'} && $cpu{'siblings'} > 1 && $cpu{'cores'} && $cpu{'cores'} > 1 ){ + if ( $cpu{'siblings'}/$cpu{'cores'} == 1 ){ + $b_intel = 0; + $b_ht = 0; } - # - if ( bRyzen == "true"){ - num_of_cores = cpu_core_count - } - # final check, override the num of cores value if it clearly is wrong - # and use the raw core count and synthesize the total instead of real count - if ( ( num_of_cores == 0 ) && ( cpu_core_count * num_of_physical_cpus > 1 ) ) { - num_of_cores = cpu_core_count * num_of_physical_cpus - } - # last check, seeing some intel cpus and vms with intel cpus that do not show any - # core id data at all, or siblings. - if ( num_of_cores == 0 && num_of_processors > 0 ) { - num_of_cores = num_of_processors - } - # ARM/vm cpu fix, if no physical or core found, use count of 1 instead - if ( num_of_physical_cpus == 0 ) { - num_of_physical_cpus = 1 - } -# print "NoCpu: " num_of_physical_cpus -# print "NoCores: " num_of_cores -# print "NoProc:" num_of_processors -# print "CpuCoreCount:" cpu_core_count - #################################################################### - # algorithm - # if > 1 processor && processor id (physical id) == core id then Multi threaded (MT) - # if siblings > 1 && siblings == 2 * num_of_cores (cpu_core_count) then Multi threaded (MT) - # if > 1 processor && processor id (physical id) != core id then Multi-Core Processors (MCP) - # if > 1 processor && processor ids (physical id) > 1 then Symmetric Multi Processing (SMP) - # if = 1 processor then single core/processor Uni-Processor (UP) - if ( num_of_processors > 1 || ( bXeon == "true" && siblings > 0 ) ) { - # non-multicore MT - if ( num_of_processors == (num_of_cores * 2) ) { - cpu_type = cpu_type "MT-" - } - else if ( bXeon == "true" && siblings > 1 ) { - cpu_type = cpu_type "MT-" - } - else if ( siblings > 1 && siblings == 2 * num_of_cores ){ - cpu_type = cpu_type "MT-" - } - # non-MT multi-core or MT multi-core - if (( num_of_processors == num_of_cores) || ( num_of_physical_cpus < num_of_cores)) { - cpu_type = cpu_type "MCP-" - } - # >1 cpu sockets active: Symetric Multi Processing - if ( num_of_physical_cpus > 1 ) { - cpu_type = cpu_type "SMP-" - } - } else { - cpu_type = cpu_type "UP-" - } - - print cpu_type " " num_of_physical_cpus " " num_of_cores + $cpu_cores = ($cpu{'siblings'}/2); + $b_ht = 1; + } + } + } + # ryzen is made out of blocks of 8 core dies + elsif ($b_ryzen){ + $cpu_cores = $cpu{'cores'}; + # note: posix ceil isn't present in Perl for some reason, deprecated? + my $working = $cpu_cores / 8; + my @temp = split /\./, $working; + $cpu{'dies'} = ($temp[1] && $temp[1] > 0) ? $temp[0]++ : $temp[0]; + } + # these always have 4 dies + elsif ($b_epyc) { + $cpu_cores = $cpu{'cores'}; + $cpu{'dies'} = 4; + } + # final check, override the num of cores value if it clearly is wrong + # and use the raw core count and synthesize the total instead of real count + if ( $cpu_cores == 0 && ($cpu{'cores'} * $phyical_count > 1)){ + $cpu_cores = ($cpu{'cores'} * $phyical_count); + } + # last check, seeing some intel cpus and vms with intel cpus that do not show any + # core id data at all, or siblings. + if ($cpu_cores == 0 && $processors_count > 0){ + $cpu_cores = $processors_count; + } + # this happens with BSDs which have very little cpu data available + if ( $processors_count == 0 && $cpu_cores > 0 ){ + $processors_count = $cpu_cores; + if ($bsd_type && ($b_ht || $b_amd_zen) && $cpu_cores > 2 ){ + $cpu_cores = $cpu_cores/2;; + } + my $count = $processors_count; + $count-- if $count > 0; + $cpu{'processors'}[$count] = 0; + # no way to get per processor speeds yet, so assign 0 to each + # must be a numeric value. Could use raw speed from core 0, but + # that would just be a hack. + foreach (0 .. $count){ + $cpu{'processors'}[$_] = 0; + } + } + # last test to catch some corner cases + # seen a case where a xeon vm in a dual xeon system actually had 2 cores, no MT + # so it reported 4 siblings, 2 cores, but actually only had 1 core per virtual cpu + #print "prc: $processors_count phc: $phyical_count coc: $core_count cpc: $cpu_cores\n"; + if (!$b_arm && $processors_count == $phyical_count*$core_count && $cpu_cores > $core_count){ + $b_ht = 0; + #$b_xeon = 0; + $b_intel = 0; + $cpu_cores = 1; + $core_count = 1; + $cpu{'siblings'} = 1; + } + #print "pc: $processors_count s: $cpu{'siblings'} cpuc: $cpu_cores corec: $core_count\n"; + # Algorithm: + # if > 1 processor && processor id (physical id) == core id then Multi threaded (MT) + # if siblings > 1 && siblings == 2 * num_of_cores ($cpu{'cores'}) then Multi threaded (MT) + # if > 1 processor && processor id (physical id) != core id then Multi-Core Processors (MCP) + # if > 1 processor && processor ids (physical id) > 1 then Symmetric Multi Processing (SMP) + # if = 1 processor then single core/processor Uni-Processor (UP) + if ( $processors_count > 1 || ( $b_intel && $cpu{'siblings'} > 0 ) ) { + # non-multicore MT + if ($processors_count == ($phyical_count * $cpu_cores * 2)){ + #print "mt:1\n"; + $cpu_type .= 'MT'; + } +# elsif ($b_xeon && $cpu{'siblings'} > 1){ +# #print "mt:2\n"; +# $cpu_type .= 'MT'; +# } + elsif ($cpu{'siblings'} > 1 && ($cpu{'siblings'} == 2 * $cpu_cores )){ + #print "mt:3\n"; + $cpu_type .= 'MT'; + } + # non-MT multi-core or MT multi-core + if ( ($processors_count == $cpu_cores ) || ($phyical_count < $cpu_cores)){ + my $sep = ($cpu_type) ? ' ' : '' ; + $cpu_type .= $sep . 'MCP'; } - ' $FILE_CPUINFO ) ) - fi - a_temp=${A_CPU_TYPE_PCNT_CCNT[@]} - log_function_data "A_CPU_TYPE_PCNT_CCNT: $a_temp" - eval $LOGFE + # only solidly known > 1 die cpus will use this, ryzen and arm for now + if ( $cpu{'dies'} > 1 ){ + my $sep = ($cpu_type) ? ' ' : '' ; + $cpu_type .= $sep . 'MCM'; + } + # >1 cpu sockets active: Symetric Multi Processing + if ($phyical_count > 1){ + my $sep = ($cpu_type) ? ' ' : '' ; + $cpu_type .= $sep . 'SMP'; + } + } + else { + $cpu_type = 'UP'; + } + if ($phyical_count > 1){ + $cpu_layout = $phyical_count . 'x '; + } + $cpu_layout .= count_alpha($cpu_cores) . 'Core'; + $cpu_layout .= ' (' . $cpu{'dies'}. '-Die)' if !$bsd_type && $cpu{'dies'} > 1; + # the only possible change for bsds is if we can get phys counts in the future + if ($bsd_type){ + $cache = $cpu{'l2-cache'} * $phyical_count; + } + # AMD SOS chips appear to report full L2 cache per core + elsif ($cpu{'type'} eq 'amd' && ($cpu{'family'} eq '14' || $cpu{'family'} eq '15' || $cpu{'family'} eq '16')){ + $cache = $cpu{'l2-cache'} * $phyical_count; + } + elsif ($cpu{'type'} ne 'intel'){ + $cache = $cpu{'l2-cache'} * $cpu_cores * $phyical_count; + } + ## note: this handles how intel reports L2, total instead of per core like AMD does + # note that we need to multiply by number of actual cpus here to get true cache size + else { + $cache = $cpu{'l2-cache'} * $phyical_count; + } + if ($cache > 10000){ + $cache = sprintf("%.01f MiB",$cache/1024); # trim to no decimals? + } + elsif ($cache > 0){ + $cache = "$cache KiB"; + } + if ($cpu{'cur-freq'} && $cpu{'min-freq'} && $cpu{'max-freq'} ){ + $min_max = "$cpu{'min-freq'}/$cpu{'max-freq'} MHz"; + $min_max_key = "min/max"; + $speed_key = ($show{'short'} || $show{'cpu-basic'}) ? 'speed' : 'Speed'; + $speed = "$cpu{'cur-freq'} MHz"; + } + elsif ($cpu{'cur-freq'} && $cpu{'max-freq'}){ + $min_max = "$cpu{'max-freq'} MHz"; + $min_max_key = "max"; + $speed_key = ($show{'short'} || $show{'cpu-basic'}) ? 'speed' : 'Speed'; + $speed = "$cpu{'cur-freq'} MHz"; + } +# elsif ($cpu{'cur-freq'} && $cpu{'max-freq'} && $cpu{'cur-freq'} == $cpu{'max-freq'}){ +# $speed_key = ($show{'short'} || $show{'cpu-basic'}) ? 'speed' : 'Speed'; +# $speed = "$cpu{'cur-freq'} MHz (max)"; +# } + elsif ($cpu{'cur-freq'} && $cpu{'min-freq'}){ + $min_max = "$cpu{'min-freq'} MHz"; + $min_max_key = "min"; + $speed_key = ($show{'short'} || $show{'cpu-basic'}) ? 'speed' : 'Speed'; + $speed = "$cpu{'cur-freq'} MHz"; + } + elsif ($cpu{'cur-freq'} && !$cpu{'max-freq'}){ + $speed_key = ($show{'short'} || $show{'cpu-basic'}) ? 'speed' : 'Speed'; + $speed = "$cpu{'cur-freq'} MHz"; + } + + if ( !$bits_sys && !$b_arm && $cpu{'flags'} ){ + $bits_sys = ($cpu{'flags'} =~ /\blm\b/) ? 64 : 32; + } + my %cpu_properties = ( + 'bits-sys' => $bits_sys, + 'cpu-layout' => $cpu_layout, + 'cpu-type' => $cpu_type, + 'min-max-key' => $min_max_key, + 'min-max' => $min_max, + 'speed-key' => $speed_key, + 'speed' => $speed, + 'l2-cache' => $cache, + ); + main::log_data('dump','%cpu_properties',\%cpu_properties) if $b_log; + #print Data::Dumper::Dumper \%cpu; + #print Data::Dumper::Dumper \%cpu_properties; + #my $dc = scalar @dies; + #print 'phys: ' . $pc . ' dies: ' . $dc, "\n"; + eval $end if $b_log; + return %cpu_properties; +} +sub cpu_speeds { + eval $start if $b_log; + my (@processors) = @_; + my (@speeds); + my @files = main::globber('/sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq'); + foreach (@files){ + my $speed = (main::reader($_))[0]; + if ($speed || $speed eq '0'){ + $speed = sprintf "%.0f", $speed/1000; + push @speeds, $speed; + } + } + if (!@speeds){ + foreach (@processors){ + if ($_ || $_ eq '0'){ + $_ = sprintf "%.0f", $_; + push @speeds, $_; + } + } + } + #print join '; ', @speeds, "\n"; + eval $end if $b_log; + return @speeds; +} +sub set_cpu_speeds_sys { + eval $start if $b_log; + my (@arm,%speeds); + my $sys = '/sys/devices/system/cpu/cpufreq/policy0'; + my $sys2 = '/sys/devices/system/cpu/cpu0/cpufreq/'; + my ($cur,$min,$max) = ('scaling_cur_freq','scaling_min_freq','scaling_max_freq'); + if (!-d $sys && -d $sys2){ + $sys = $sys2; + ($cur,$min,$max) = ('scaling_cur_freq','cpuinfo_min_freq','cpuinfo_max_freq'); + } + if (-d $sys){ + $speeds{'cur-freq'} = (main::reader("$sys/$cur"))[0]; + $speeds{'cur-freq'} = speed_cleaner($speeds{'cur-freq'},'khz'); + $speeds{'min-freq'} = (main::reader("$sys/$min"))[0]; + $speeds{'min-freq'} = speed_cleaner($speeds{'min-freq'},'khz'); + $speeds{'max-freq'} = (main::reader("$sys/$max"))[0]; + $speeds{'max-freq'} = speed_cleaner($speeds{'max-freq'},'khz'); + if ($b_arm){ + @arm = main::globber('/sys/devices/system/cpu/cpufreq/policy*/'); + # there are arm chips with two dies, that run at different min max speeds!! + # see: https://github.com/smxi/inxi/issues/128. + # it would be slick to show both die min/max/cur speeds, but this is + # ok for now. + if (scalar @arm > 1){ + my ($current,$max,$min) = (0,0,0); + foreach (@arm){ + $_ =~ s/\/$//; # strip off last slash in case globs have them + my $max_temp = main::reader("$_/cpuinfo_max_freq"); + $max_temp = speed_cleaner($max_temp,'khz'); + if ($max_temp > $max){ + $max = $max_temp; + } + my $min_temp = main::reader("$_/cpuinfo_min_freq"); + $min_temp = speed_cleaner($min_temp,'khz'); + if ($min_temp < $min || $min == 0){ + $max = $min_temp; + } + my $cur_temp = main::reader("$_/cpuinfo_max_freq"); + $cur_temp = speed_cleaner($cur_temp,'khz'); + if ($cur_temp > $current){ + $current = $cur_temp; + } + } + $speeds{'cur-freq'} = $current if $current; + $speeds{'max-freq'} = $max if $max; + $speeds{'min-freq'} = $min if $min; + } + } + # policy4/cpuinfo_max_freq:["2000000"] + # policy4/cpuinfo_min_freq:["200000"] + if ($speeds{'min-freq'} > $speeds{'max-freq'} || $speeds{'min-freq'} == $speeds{'max-freq'}){ + $speeds{'min-freq'} = 0; + } + } + main::log_data('dump','%speeds',\%speeds) if $b_log; + eval $end if $b_log; + return %speeds; } -# Detect desktop environment in use, initial rough logic from: compiz-check -# http://forlong.blogage.de/entries/pages/Compiz-Check -# NOTE $XDG_CURRENT_DESKTOP envvar is not reliable, but it shows certain desktops better. -# most desktops are not using it as of 2014-01-13 (KDE, UNITY, LXDE. Not Gnome) -get_desktop_environment() -{ - eval $LOGFS - - # set the default, this function only runs in X, if null, don't print data out - local desktop_environment='' xprop_root='' version2='' - local version='' version_data='' version2_data='' toolkit='' - - # works on 4, assume 5 will id the same, why not, no need to update in future - # KDE_SESSION_VERSION is the integer version of the desktop - # NOTE: as of plasma 5, the tool: about-distro MAY be available, that will show - # actual desktop data, so once that's in debian/ubuntu, if it gets in, add that test - if [[ $XDG_CURRENT_DESKTOP == 'KDE' || -n $KDE_SESSION_VERSION ]]; then - # note the command is actually like, kded4 --version, so we construct it - # this was supposed to extend to 5, but 5 changed it, so it uses the more reliable way - if [[ $KDE_SESSION_VERSION -le 4 ]];then - version_data=$( kded$KDE_SESSION_VERSION --version 2>/dev/null ) - version=$( grep -si '^KDE Development Platform:' <<< "$version_data" | gawk '{print $4}' ) - else - # NOTE: this command string is almost certain to change, and break, with next major plasma desktop, ie, 6 - # version=$( qdbus org.kde.plasmashell /MainApplication org.qtproject.Qt.QCoreApplication.applicationVersion 2>/dev/null ) - #Qt: 5.4.2 - #KDE Frameworks: 5.11.0 - #kf5-config: 1.0 - # for QT, and Frameworks if we use it - if type -p kf$KDE_SESSION_VERSION-config &>/dev/null;then - version_data=$( kf$KDE_SESSION_VERSION-config --version 2>/dev/null ) - # version=$( grep -si '^KDE Frameworks:' <<< "$version_data" | gawk '{print $NF}' ) - fi - # plasmashell 5.3.90 - if type -p plasmashell &>/dev/null;then - version2_data=$( plasmashell --version 2>/dev/null ) - version=$( grep -si '^plasmashell' <<< "$version2_data" | gawk '{print $NF}' ) - fi - fi - if [[ -z $version ]];then - version=$KDE_SESSION_VERSION - fi - if [[ $B_EXTRA_DATA == 'true' && -n $version_data ]];then - toolkit=$( grep -si '^Qt:' <<< "$version_data" | gawk '{print $2}' ) - if [[ -n $toolkit ]];then - version="$version (Qt $toolkit)" - fi - fi - desktop_environment="KDE Plasma" - # KDE_FULL_SESSION property is only available since KDE 3.5.5. - # src: http://humanreadable.nfshost.com/files/startkde - elif [[ $KDE_FULL_SESSION == 'true' ]]; then - version_data=$( kded --version 2>/dev/null ) - version=$( grep -si '^KDE:' <<< "$version_data" | gawk '{print $2}' ) - # version=$( get_program_version 'kded' '^KDE:' '2' ) - if [[ -z $version ]];then - version='3.5' - fi - if [[ $B_EXTRA_DATA == 'true' ]];then - toolkit=$( grep -si '^Qt:' <<< "$version_data" | gawk '{print $2}' ) - if [[ -n $toolkit ]];then - version="$version (Qt $toolkit)" - fi - fi - desktop_environment="KDE" - elif [[ $XDG_CURRENT_DESKTOP == 'Unity' ]];then - version=$( get_program_version 'unity' '^unity' '2' ) - # not certain will always have version, so keep output right if not - if [[ -n $version ]];then - version="$version " - fi - if [[ $B_EXTRA_DATA == 'true' ]];then - toolkit=$( get_de_gtk_data ) - if [[ -n $toolkit ]];then - version="$version(Gtk $toolkit)" - fi - fi - desktop_environment="Unity" - elif [[ $XDG_CURRENT_DESKTOP == *Budgie* ]];then - version=$( get_program_version 'budgie-desktop' '^budgie-desktop' '2' ) - # not certain will always have version, so keep output right if not - if [[ -n $version ]];then - version="$version " - fi - if [[ $B_EXTRA_DATA == 'true' ]];then - toolkit=$( get_de_gtk_data ) - if [[ -n $toolkit ]];then - version="$version(Gtk $toolkit)" - fi - fi - desktop_environment="Budgie" - elif [[ $XDG_CURRENT_DESKTOP == 'LXQt' ]];then -# if type -p lxqt-about &>/dev/null;then -# version=$( get_program_version 'lxqt-about' '^lxqt-about' '2' ) -# fi - if [[ $B_EXTRA_DATA == 'true' ]];then - if kded$KDE_SESSION_VERSION &>/dev/null;then - version_data=$( kded$KDE_SESSION_VERSION --version 2>/dev/null ) - toolkit=$( grep -si '^Qt:' <<< "$version_data" | gawk '{print $2}' ) - elif type -p qtdiag &>/dev/null;then - toolkit=$( get_program_version 'qtdiag' '^qt' '2' ) - fi - if [[ -n $toolkit ]];then - version="$version (Qt $toolkit)" - fi - fi - desktop_environment='LXQt' - # note, X-Cinnamon value strikes me as highly likely to change, so just search for the last part - elif [[ -n $XDG_CURRENT_DESKTOP && -z ${XDG_CURRENT_DESKTOP/*innamon*/} ]];then - version=$( get_program_version 'cinnamon' '^cinnamon' '2' ) - # not certain cinn will always have version, so keep output right if not - if [[ -n $version ]];then - version="$version " - fi - if [[ $B_EXTRA_DATA == 'true' ]];then - toolkit=$( get_de_gtk_data ) - if [[ -n $toolkit ]];then - version="$version(Gtk $toolkit)" - fi - fi - desktop_environment="Cinnamon" - fi - # did we find it? If not, start the xprop tests - if [[ -z $desktop_environment ]];then - if type -p xprop &>/dev/null;then - xprop_root="$( xprop -root $DISPLAY_OPT 2>/dev/null | tr '[:upper:]' '[:lower:]' )" - fi - # note that cinnamon split from gnome, and and can now be id'ed via xprop, - # but it will still trigger the next gnome true case, so this needs to go before gnome test - # eventually this needs to be better organized so all the xprop tests are in the same - # section, but this is good enough for now. - if [[ -n $xprop_root && -z ${xprop_root/*_muffin*/} ]];then - version=$( get_program_version 'cinnamon' '^cinnamon' '2' ) - # not certain cinn will always have version, so keep output right if not - if [[ -n $version ]];then - version="$version " - fi - if [[ $B_EXTRA_DATA == 'true' ]];then - toolkit=$( get_de_gtk_data ) - if [[ -n $toolkit ]];then - version="$version(Gtk $toolkit)" - fi - fi - desktop_environment="Cinnamon" - elif [[ $XDG_CURRENT_DESKTOP == 'MATE' ]] || [[ -n $xprop_root && -z ${xprop_root/*_marco*/} ]];then - version=$( get_program_version 'mate-about' '^MATE[[:space:]]DESKTOP' 'NF' ) - # not certain cinn/mate will always have version, so keep output right if not - if [[ -n $version ]];then - version="$version " - fi - if [[ $B_EXTRA_DATA == 'true' ]];then - toolkit=$( get_de_gtk_data ) - if [[ -n $toolkit ]];then - version="$version(Gtk $toolkit)" - fi - fi - desktop_environment="MATE" - # note, GNOME_DESKTOP_SESSION_ID is deprecated so we'll see how that works out - # https://bugzilla.gnome.org/show_bug.cgi?id=542880 - elif [[ -n $GNOME_DESKTOP_SESSION_ID || $XDG_CURRENT_DESKTOP == 'GNOME' ]]; then - if type -p gnome-shell &>/dev/null;then - version=$( get_program_version 'gnome-shell' 'gnome' '3' ) - elif type -p gnome-about &>/dev/null;then - version=$( get_program_version 'gnome-about' 'gnome' '3' ) - fi - if [[ $B_EXTRA_DATA == 'true' ]];then - toolkit=$( get_de_gtk_data ) - if [[ -n $toolkit ]];then - version="$version (Gtk $toolkit)" - fi - fi - desktop_environment="Gnome" - fi - if [[ -z $desktop_environment ]];then - # now that the primary ones have been handled, next is to find the ones with unique - # xprop detections possible - if [[ -n $xprop_root ]];then - # String: "This is xfdesktop version 4.2.12" - # alternate: xfce4-about --version > xfce4-about 4.10.0 (Xfce 4.10) - if [[ -z ${xprop_root/*\"xfce4\"*/} ]];then - version=$( get_program_version 'xfdesktop' 'xfdesktop[[:space:]]version' '5' ) - # arch linux reports null, so use alternate if null - if [[ -z $version ]];then - version=$( get_program_version 'xfce4-panel' '^xfce4-panel' '2' ) - if [[ -z $version ]];then - version='4' - fi - fi - if [[ $B_EXTRA_DATA == 'true' ]];then - toolkit=$( get_program_version 'xfdesktop' 'Built[[:space:]]with[[:space:]]GTK' '4' ) - if [[ -n $toolkit ]];then - version="$version (Gtk $toolkit)" - fi - fi - desktop_environment="Xfce" - # when 5 is released, the string may need updating - elif [[ -z ${xprop_root/*\"xfce5\"*/} ]];then - version=$( get_program_version 'xfdesktop' 'xfdesktop[[:space:]]version' '5' ) - # arch linux reports null, so use alternate if null - if [[ -z $version ]];then - version=$( get_program_version 'xfce5-panel' '^xfce5-panel' '2' ) - if [[ -z $version ]];then - version='5' - fi - fi - if [[ $B_EXTRA_DATA == 'true' ]];then - toolkit=$( get_program_version 'xfdesktop' 'Built[[:space:]]with[[:space:]]GTK' '4' ) - if [[ -n $toolkit ]];then - version="$version (Gtk $toolkit)" - fi - fi - desktop_environment="Xfce" - # case where no xfce number exists, just xfce - elif [[ -z ${xprop_root/*xfce*/} ]];then - version=$( get_program_version 'xfdesktop' 'xfdesktop[[:space:]]version' '5' ) - # arch linux reports null, so use alternate if null - if [[ -z $version ]];then - version=$( get_program_version 'xfce4-panel' '^xfce5-panel' '2' ) - if [[ -z $version ]];then - # version=$( get_program_version 'xfce5-panel' '^xfce5-panel' '2' ) - #if [[ -z $version ]];then - # version='5' - #fi - version='4' - fi - fi - if [[ $B_EXTRA_DATA == 'true' ]];then - toolkit=$( get_program_version 'xfdesktop' 'Built[[:space:]]with[[:space:]]GTK' '4' ) - if [[ -n $toolkit ]];then - version="$version (Gtk $toolkit)" - fi - fi - desktop_environment="Xfce" - elif [[ -z ${xprop_root/*blackbox_pid*/} ]];then - if [[ -z "${Ps_aux_Data/*fluxbox*/}" ]];then - version=$( get_program_version 'fluxbox' '^fluxbox' '2' ) - desktop_environment='Fluxbox' - else - desktop_environment='Blackbox' - fi - elif [[ -z ${xprop_root/*openbox_pid*/} ]];then - # note: openbox-lxde --version may be present, but returns openbox data - version=$( get_program_version 'openbox' '^openbox' '2' ) - if [[ $XDG_CURRENT_DESKTOP == 'LXDE' || -z "${Ps_aux_Data/*\/lxsession*/}" ]];then - if [[ -n $version ]];then - version="(Openbox $version)" - fi - desktop_environment='LXDE' - elif [[ $XDG_CURRENT_DESKTOP == 'Razor' || $XDG_CURRENT_DESKTOP == 'LXQt' ]] || \ - [[ -n $( grep -Es '(razor-desktop|lxqt-session)' <<< "$Ps_aux_Data" ) ]];then - if [[ -z "${Ps_aux_Data/*lxqt-session*/}" ]];then - desktop_environment='LXQt' - elif [[ -z "${Ps_aux_Data/*razor-desktop*/}" ]];then - desktop_environment='Razor-Qt' - else - desktop_environment='LX-Qt-Variant' - fi - if [[ -n $version ]];then - version="(Openbox $version)" - fi - else - desktop_environment='Openbox' - fi - elif [[ -z ${xprop_root/*icewm*/} ]];then - version=$( get_program_version 'icewm' '^icewm' '2' ) - desktop_environment='IceWM' - elif [[ -z ${xprop_root/*enlightenment*/} ]];then - # no -v or --version but version is in xprop -root - # ENLIGHTENMENT_VERSION(STRING) = "Enlightenment 0.16.999.49898" - version=$( grep -is 'ENLIGHTENMENT_VERSION' <<< "$xprop_root" | cut -d '"' -f 2 | gawk '{print $2}' ) - desktop_environment='Enlightenment' - # need to check starts line because it's so short - elif [[ -n $( grep -s '^i3_' <<< "$xprop_root" ) ]];then - version=$( get_program_version 'i3' '^i3' '3' ) - desktop_environment='i3' - elif [[ -z ${xprop_root/*windowmaker*/} ]];then - version=$( get_program_version 'wmaker' '^Window[[:space:]]*Maker' 'NF' ) - if [[ -n $version ]];then - version="$version " - fi - desktop_environment="WindowMaker" - # need to check starts line because it's so short - elif [[ -n $( grep -s '^_wm2' <<< "$xprop_root" ) ]];then - # note; there isn't actually a wm2 version available but error handling should cover it and return null - # maybe one day they will add it? - version=$( get_program_version 'wm2' '^wm2' 'NF' ) - # not certain will always have version, so keep output right if not - if [[ -n $version ]];then - version="$version " - fi - desktop_environment="WM2" - elif [[ -z "${xprop_root/*herbstluftwm*/}" ]];then - version=$( get_program_version 'herbstluftwm' '^herbstluftwm' 'NF' ) - if [[ -n $version ]];then - version="$version " - fi - desktop_environment="herbstluftwm" - fi - fi - # a few manual hacks for things that don't id with xprop, these are just good guesses - # note that gawk is going to exit after first occurrence of search string, so no need for extra - # http://www.xwinman.org/ for more possible wm - if [[ -z $desktop_environment ]];then - if [[ -z "${Ps_aux_Data/*fvwm-crystal*/}" ]];then - version=$( get_program_version 'fvwm' '^fvwm' '2' ) - desktop_environment='FVWM-Crystal' - elif [[ -z "${Ps_aux_Data/*fvwm*/}" ]];then - version=$( get_program_version 'fvwm' '^fvwm' '2' ) - desktop_environment='FVWM' - elif [[ -z "${Ps_aux_Data/*pekwm*/}" ]];then - version=$( get_program_version 'pekwm' '^pekwm' '3' ) - desktop_environment='pekwm' - elif [[ -z "${Ps_aux_Data/*awesome*/}" ]];then - version=$( get_program_version 'awesome' '^awesome' '2' ) - desktop_environment='Awesome' - elif [[ -z "${Ps_aux_Data/*scrotwm*/}" ]];then - version=$( get_program_version 'scrotwm' '^welcome.*scrotwm' '4' ) - desktop_environment='Scrotwm' # no --version for this one - elif [[ -z "${Ps_aux_Data/*spectrwm*/}" ]];then - version=$( get_program_version 'spectrwm' '^spectrwm.*welcome.*spectrwm' '5' ) - desktop_environment='Spectrwm' # no --version for this one - elif [[ -n $( grep -Es '([[:space:]]|/)twm' <<< "$Ps_aux_Data" ) ]];then - desktop_environment='Twm' # no --version for this one - elif [[ -n $( grep -Es '([[:space:]]|/)dwm' <<< "$Ps_aux_Data" ) ]];then - version=$( get_program_version 'dwm' '^dwm' '1' ) - desktop_environment='dwm' - elif [[ -z "${Ps_aux_Data/*wmii2*/}" ]];then - version=$( get_program_version 'wmii2' '^wmii2' '1' ) - desktop_environment='wmii2' - # note: in debian at least, wmii is actuall wmii3 - elif [[ -z "${Ps_aux_Data/*wmii*/}" ]];then - version=$( get_program_version 'wmii' '^wmii' '1' ) - desktop_environment='wmii' - elif [[ -n $( grep -Es '([[:space:]]|/)jwm' <<< "$Ps_aux_Data" ) ]];then - version=$( get_program_version 'jwm' '^jwm' '2' ) - desktop_environment='JWM' - elif [[ -z "${Ps_aux_Data/*sawfish*/}" ]];then - version=$( get_program_version 'sawfish' '^sawfish' '3' ) - desktop_environment='Sawfish' - elif [[ -z "${Ps_aux_Data/*afterstep*/}" ]];then - version=$( get_program_version 'afterstep' '^afterstep' '3' ) - desktop_environment='AfterStep' - fi - fi - fi - fi - if [[ -n $version ]];then - version=" $version" - fi - log_function_data "desktop_environment version: $desktop_environment$version" - echo "$desktop_environment$version" - eval $LOGFE -} - -get_desktop_extra_data() -{ - eval $LOGFS - local de_data=$( ps -A | gawk ' - BEGIN { - IGNORECASE=1 - desktops="" - separator="" - } - /(gnome-shell|gnome-panel|kicker|lxpanel|mate-panel|plasma-desktop|plasma-netbook|xfce4-panel)$/ { - # only one entry per type, can be multiple - if ( desktops !~ $NF ) { - desktops = desktops separator $NF - separator = "," +# right now only using this for ARM cpus, this is not the same in intel/amd +sub cpu_dies_sys { + eval $start if $b_log; + my @data = main::globber('/sys/devices/system/cpu/cpu*/topology/core_siblings_list'); + my (@dies); + foreach (@data){ + my $siblings = (main::reader($_))[0]; + if (! grep {/$siblings/} @dies){ + push @dies, $siblings; } } - END { - print desktops + my $die_count = scalar @dies; + eval $end if $b_log; + return $die_count; +} +sub cpu_flags_bsd { + eval $start if $b_log; + my ($flags,$sep) = ('',''); + # this will be null if it was not readable + my $file = main::system_files('dmesg-boot'); + if ( $file && ! -r $file ){ + $flags = main::row_defaults('dmesg-boot-permissions'); } - ' ) - log_function_data "de_data: $de_data" - echo $de_data - - eval $LOGFE + else { + foreach (@dmesg_boot){ + if ( /Features/ || ( $bsd_type eq "openbsd" && /^cpu0:\s*[a-z0-9]{2,3}(\s|,)[a-z0-9]{2,3}(\s|,)/i ) ) { + my @line = split /:\s*/, lc($_); + # free bsd has to have weird syntax: <....<b23>,<b34>> + # Features2=0x1e98220b<SSE3,PCLMULQDQ,MON,SSSE3,CX16,SSE4.1,SSE4.2,POPCNT,AESNI,XSAVE,OSXSAVE,AVX> + $line[1] =~ s/^[^<]*<|>[^>]*$//g; + # then get rid of <b23> stuff + $line[1] =~ s/<[^>]+>//g; + # and replace commas with spaces + $line[1] =~ s/,/ /g; + $flags .= $sep . $line[1]; + $sep = ' '; + } + elsif (/real mem/){ + last; + } + } + if ($flags){ + $flags =~ s/\s+/ /g; + $flags =~ s/^\s+|\s+$//g; + } + } + eval $end if $b_log; + return $flags; } -get_de_gtk_data() -{ - eval $LOGFS - - local toolkit='' - - # this is a hack, and has to be changed with every toolkit version change, and only dev systems - # have this installed, but it's a cross distro command so let's test it first - if type -p pkg-config &>/dev/null;then - toolkit=$( pkg-config --modversion gtk+-4.0 2>/dev/null ) - # note: opensuse gets null output here, we need the command to get version and output sample - if [[ -z $toolkit ]];then - toolkit=$( pkg-config --modversion gtk+-3.0 2>/dev/null ) - fi - if [[ -z $toolkit ]];then - toolkit=$( pkg-config --modversion gtk+-2.0 2>/dev/null ) - fi - fi - # now let's go to more specific version tests, this will never cover everything and that's fine. - if [[ -z $toolkit ]];then - # we'll try some known package managers next. dpkg will handle a lot of distros - # this is the most likely order as of: 2014-01-13. Not going to try to support all package managers - # too much work, just the very biggest ones. - if type -p dpkg &>/dev/null;then - toolkit=$( dpkg -s libgtk-3-0 2>/dev/null | gawk -F ':' '/^[[:space:]]*Version/ {print $2}' ) - # just guessing on gkt 4 package name - if [[ -z $toolkit ]];then - toolkit=$( dpkg -s libgtk-4-0 2>/dev/null | gawk -F ':' '/^[[:space:]]*Version/ {print $2}' ) - fi - if [[ -z $toolkit ]];then - toolkit=$( dpkg -s libgtk2.0-0 2>/dev/null | gawk -F ':' '/^[[:space:]]*Version/ {print $2}' ) - fi - elif type -p pacman &>/dev/null;then - toolkit=$( pacman -Qi gtk3 2>/dev/null | gawk -F ':' '/^[[:space:]]*Version/ {print $2}' ) - # just guessing on gkt 4 package name - if [[ -z $toolkit ]];then - toolkit=$( pacman -Qi gtk4 2>/dev/null | gawk -F ':' '/^[[:space:]]*Version/ {print $2}' ) - fi - if [[ -z $toolkit ]];then - toolkit=$( pacman -Qi gtk2 2>/dev/null | gawk -F ':' '/^[[:space:]]*Version/ {print $2}' ) - fi - # Name : libgtk-3-0 - # Version : 3.12.2 - elif type -p rpm &>/dev/null;then - toolkit=$( rpm -qi libgtk-3-0 2>/dev/null | gawk -F ':' ' - /^[[:space:]]*Version/ { - gsub(/^[[:space:]]+|[[:space:]]+$/,"",$2) - print $2 - }' ) - if [[ -z $toolkit ]];then - toolkit=$( rpm -qi libgtk-4-0 2>/dev/null | gawk -F ':' ' - /^[[:space:]]*Version/ { - gsub(/^[[:space:]]+|[[:space:]]+$/,"",$2) - print $2 - }' ) - fi - if [[ -z $toolkit ]];then - toolkit=$( rpm -qi libgtk-2-0 2>/dev/null | gawk -F ':' ' - /^[[:space:]]*Version/ { - gsub(/^[[:space:]]+|[[:space:]]+$/,"",$2) - print $2 - }' ) - fi - fi - fi - log_function_data "toolkit: $toolkit" - echo $toolkit - - eval $LOGFE +sub cpu_vendor { + eval $start if $b_log; + my ($string) = @_; + my ($vendor) = (''); + $string = lc($string); + if ($string =~ /intel/) { + $vendor = "intel" + } + elsif ($string =~ /amd/){ + $vendor = "amd" + } + # via + elsif ($string =~ /centaur/){ + $vendor = "centaur" + } + eval $end if $b_log; + return $vendor; } -get_device_data() -{ - eval $LOGFS - - local device='un-determined' - local chasis_id='' dmi_device='' - - # first: linked version - if [[ -e /sys/class/dmi/id/chassis_type ]];then - chasis_id=$(cat /sys/class/dmi/id/chassis_type) - elif [[ -e /sys/devices/virtual/dmi/id/chassis_type ]];then - chasis_id=$(cat /sys/devices/virtual/dmi/id/chassis_type) - fi - # src: http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.0.pdf - # https://www.404techsupport.com/2012/03/pizza-box-lunch-box-and-other-pc-case-form-factors-identified-by-wmi/ - if [[ $chasis_id != '' ]];then - case $chasis_id in - 1) - device=$(get_device_vm) - ;; - 2) - device='unknown' - ;; - # note: 13 is all-in-one which we take as a mac type system - 3|4|6|7|13|15|24) - device='desktop' - ;; - # 5 - pizza box was a 1 U desktop enclosure, but some old laptops also id this way - 5) - device='pizza-box' - ;; - # note: lenovo T420 shows as 10, notebook, but it's not a notebook - 9|10|16) - device='laptop' - ;; - 14) - device='notebook' - ;; - 8|11) - device='portable' - ;; - 17|23|25) - device='server' - ;; - 27|28|29) - device='blade' - ;; - 12) - device='docking-station' - ;; - 18) - device='expansion-chassis' - ;; - 19) - device='sub-chassis' - ;; - 20) - device='bus-expansion' - ;; - 21) - device='peripheral' - ;; - 22) - device='RAID' - ;; - 26) - device='compact-PCI' - ;; - esac - else - if ! type -p dmidecode &>/dev/null;then - device='dmidecode-missing' - elif [[ $B_ROOT == 'false' ]];then - device='dmidecode-use-root' - else - set_dmidecode_data - if [[ -n $DMIDECODE_DATA ]];then - if [[ $DMIDECODE_DATA == 'dmidecode-error-'* ]];then - device='dmidecode-no-info' - else - dmi_device=$( gawk ' - BEGIN { - IGNORECASE=1 - device="test" - } - /^Chassis Information/ { - device= $1 - while (getline && !/^$/ ) { - if ( $1 ~ /^Type/ ) { - sub(/Type:\s*/,"",$0) - device = $0 - break - } - } - } - END { - print device - }' <<< "$DMIDECODE_DATA" ) - if [[ -n $dmi_device ]];then - device=$dmi_device - fi - if [[ $device == 'Other' ]];then - device=$(get_device_vm) - fi - fi - fi - fi - fi - echo $device - - eval $LOGFE +sub cpu_arch { + eval $start if $b_log; + my ($type,$family,$model) = @_; + my $arch = ''; + # https://en.wikipedia.org/wiki/List_of_AMD_CPU_microarchitectures + # print "$type;$family;$model\n"; + if ( $type eq 'amd'){ + if ($family eq '4'){ + if ( $model =~ /^(3|7|8|9|A)$/ ) {$arch = 'Am486'} + elsif ( $model =~ /^(E|F)$/ ) {$arch = 'Am5x86'} + } + elsif ($family eq '5'){ + if ( $model =~ /^(0|1|2|3)$/ ) {$arch = 'K5'} + elsif ( $model =~ /^(6|7)$/ ) {$arch = 'K6'} + elsif ( $model =~ /^(8)$/ ) {$arch = 'K6-2'} + elsif ( $model =~ /^(9|D)$/ ) {$arch = 'K6-3'} + elsif ( $model =~ /^(A)$/ ) {$arch = 'Geode'} + } + elsif ($family eq '6'){ + if ( $model =~ /^(1|2)$/ ) {$arch = 'K7'} + elsif ( $model =~ /^(3|4)$/ ) {$arch = 'K7 Thunderbird'} + elsif ( $model =~ /^(6|7|8|A)$/ ) {$arch = 'K7 Palomino+'} + else {$arch = 'K7'} + } + elsif ($family eq 'F'){ + if ( $model =~ /^(4|5|7|8|B|C|E|F|14|15|17|18|1B|1C|1F)$/ ) {$arch = 'K8'} + elsif ( $model =~ /^(21|23|24|25|27|28|2C|2F)$/ ) {$arch = 'K8 rev.E'} + elsif ( $model =~ /^(41|43|48|4B|4C|4F|5D|5F|68|6B|6C|6F|7C|7F|C1)$/ ) {$arch = 'K8 rev.F+'} + else {$arch = 'K8'} + } + elsif ($family eq '10'){ + if ( $model =~ /^(2|4|5|6|8|9|A)$/ ) {$arch = 'K10'} + else {$arch = 'K10'} + } + elsif ($family eq '11'){ + if ( $model =~ /^(3)$/ ) {$arch = 'Turion X2 Ultra'} + } + # might also need cache handling like 14/16 + elsif ($family eq '12'){ + if ( $model =~ /^(1)$/ ) {$arch = 'Fusion'} + else {$arch = 'Fusion'} + } + # SOC, apu + elsif ($family eq '14'){ + if ( $model =~ /^(1|2)$/ ) {$arch = 'Bobcat'} + else {$arch = 'Bobcat'} + } + elsif ($family eq '15'){ + if ( $model =~ /^(0|1|2|3|4|5|6|7|8|9|A|B|C|D|E|F)$/ ) {$arch = 'Bulldozer'} + elsif ( $model =~ /^(10|11|12|13|14|15|16|17|18|19|1A|1B|1C|1D|1E|1F)$/ ) {$arch = 'Piledriver'} + elsif ( $model =~ /^(30|31|32|33|34|35|36|37|38|39|3A|3B|3C|3D|3E|3F)$/ ) {$arch = 'Steamroller'} + elsif ( $model =~ /^(60|61|62|63|64|65|66|67|68|69|6A|6B|6C|6D|6E|6F|70|71|72|73|74|75|76|77|78|79|7A|7B|7C|7D|7E|7F)$/ ) {$arch = 'Excavator'} + else {$arch = 'Bulldozer'} + } + # SOC, apu + elsif ($family eq '16'){ + if ( $model =~ /^(0|1|2|3|4|5|6|7|8|9|A|B|C|D|E|F)$/ ) {$arch = 'Jaguar'} + elsif ( $model =~ /^(30|31|32|33|34|35|36|37|38|39|3A|3B|3C|3D|3E|3F)$/ ) {$arch = 'Puma'} + else {$arch = 'Jaguar'} + } + elsif ($family eq '17'){ + if ( $model =~ /^(1)$/ ) {$arch = 'Zen'} + else {$arch = 'Zen'} + } + } + elsif ( $type eq 'arm'){ + if ($family ne ''){$arch="ARMv$family";} + else {$arch='ARM';} + } + # aka VIA + elsif ( $type eq 'centaur'){ + if ($family eq '5'){ + if ( $model =~ /^(4)$/ ) {$arch = 'WinChip C6'} + elsif ( $model =~ /^(8)$/ ) {$arch = 'WinChip 2'} + elsif ( $model =~ /^(9)$/ ) {$arch = 'WinChip 3'} + } + elsif ($family eq '6'){ + if ( $model =~ /^(6)$/ ) {$arch = 'WinChip-based'} + elsif ( $model =~ /^(7|8)$/ ) {$arch = 'C3'} + elsif ( $model =~ /^(9)$/ ) {$arch = 'C3-2'} + elsif ( $model =~ /^(A|D)$/ ) {$arch = 'C7'} + elsif ( $model =~ /^(F)$/ ) {$arch = 'Isaiah'} + } + } + elsif ( $type eq 'intel'){ + if ($family eq '4'){ + if ( $model =~ /^(0|1|2|3|4|5|6|7|8|9)$/ ) {$arch = '486'} + } + elsif ($family eq '5'){ + if ( $model =~ /^(1|2|3|7)$/ ) {$arch = 'P5'} + elsif ( $model =~ /^(4|8)$/ ) {$arch = 'P5'} # MMX + elsif ( $model =~ /^(9)$/ ) {$arch = 'Quark'} + } + elsif ($family eq '6'){ + if ( $model =~ /^(1)$/ ) {$arch = 'P6 Pro'} + elsif ( $model =~ /^(3|5|6)$/ ) {$arch = 'P6 II'} + elsif ( $model =~ /^(7|8)$/ ) {$arch = 'P6 III'} + elsif ( $model =~ /^(9)$/ ) {$arch = 'Banias'} # pentium M + elsif ( $model =~ /^(15)$/ ) {$arch = 'Dothan Tolapai'} # pentium M system on chip + elsif ( $model =~ /^(D)$/ ) {$arch = 'Dothan'} # Pentium M + elsif ( $model =~ /^(E)$/ ) {$arch = 'Yonah'} + elsif ( $model =~ /^(F|16)$/ ) {$arch = 'Conroe'} + elsif ( $model =~ /^(17|1D)$/ ) {$arch = 'Penryn'} + elsif ( $model =~ /^(1A|1E|1F|2E|25|2C|2F)$/ ) {$arch = 'Nehalem'} + elsif ( $model =~ /^(1C|26)$/ ) {$arch = 'Bonnell'} + elsif ( $model =~ /^(27|35|36)$/ ) {$arch = 'Saltwell'} + elsif ( $model =~ /^(25|2C|2F)$/ ) {$arch = 'Westmere'} + elsif ( $model =~ /^(26|27)$/ ) {$arch = 'Bonnell'} + elsif ( $model =~ /^(2A|2D)$/ ) {$arch = 'Sandy Bridge'} + elsif ( $model =~ /^(37|4A|4D|5A)$/ ) {$arch = 'Silvermont'} + elsif ( $model =~ /^(3A|3E)$/ ) {$arch = 'Ivy Bridge'} + elsif ( $model =~ /^(3C|3F|45|46)$/ ) {$arch = 'Haswell'} + elsif ( $model =~ /^(3D|47|4F|56)$/ ) {$arch = 'Broadwell'} + elsif ( $model =~ /^(4E|55|9E)$/ ) {$arch = 'Skylake'} + elsif ( $model =~ /^(5E)$/ ) {$arch = 'Skylake-S'} + elsif ( $model =~ /^(4C|5D)$/ ) {$arch = 'Airmont'} + elsif ( $model =~ /^(8E|9E)$/ ) {$arch = 'Kaby Lake'} + elsif ( $model =~ /^(57)$/ ) {$arch = 'Knights Landing'} + elsif ( $model =~ /^(85)$/ ) {$arch = 'Knights Mill'} + # product codes: https://en.wikipedia.org/wiki/List_of_Intel_microprocessors + # coming: coffee lake; cannonlake; icelake; tigerlake + } + elsif ($family eq 'B'){ + if ( $model =~ /^(1)$/ ) {$arch = 'Knights Corne'} + } + elsif ($family eq 'F'){ + if ( $model =~ /^(0|1|2)$/ ) {$arch = 'Netburst Willamette'} + elsif ( $model =~ /^(3|4|6)$/ ) {$arch = 'Netburst Prescott'} # Nocona + else {$arch = 'Netburst'} + } + } + eval $end if $b_log; + return $arch; } -get_device_vm() -{ - eval $LOGFS - - local vm='other-vm?' vm_data='' vm_test='' - - # https://www.freedesktop.org/software/systemd/man/systemd-detect-virt.html - # note: returns bosh for qemu-kvm so if that's the result, let the other tests - # run - if type -p systemd-detect-virt &>/dev/null;then - vm_test=$(systemd-detect-virt 2>/dev/null | sed 's/none//' ) - if [[ -n $vm_test && $vm_test != 'none' ]];then - vm=$vm_test - fi - fi - # some simple to detect linux vm id's - if [[ $vm == 'other-vm?' || $vm == 'bochs' ]];then - if [[ -e /proc/vz ]];then - vm='openvz' - elif [[ -e /proc/xen ]];then - vm='xen' - elif [[ -e /dev/vzfs ]];then - vm='virtuozzo' - elif type -p lsmod &>/dev/null;then - vm_data="$( lsmod 2>/dev/null )" - if [[ -n $( grep -i 'kqemu' <<< "$vm_data" ) ]];then - vm='kqemu' - elif [[ -n $( grep -i 'kvm' <<< "$vm_data" ) ]];then - vm='kvm' - elif [[ -n $( grep -i 'qemu' <<< "$vm_data" ) ]];then - vm='qemu' - fi - vm_data='' - fi - fi - # this will catch many Linux systems and some BSDs - if [[ $vm == 'other-vm?' || $vm == 'bochs' ]];then - vm_data=$vm_data$LSPCI_V_DATA - vm_data=$vm_data$SYSCTL_A_DATA - vm_data=$vm_data$DMESG_BOOT_DATA - if [[ -e /dev/disk/by-id ]];then - vm_data=$vm_data$(ls -l /dev/disk/by-id 2>/dev/null ) - fi - if [[ -n $( grep -iEs 'innotek|vbox|virtualbox' <<< $vm_data ) ]];then - vm='virtualbox' - elif [[ -n $( grep -is 'vmware' <<< $vm_data ) ]];then - vm='vmware' - elif [[ -n $( grep -is 'qemu' <<< $vm_data ) ]];then - vm='qemu-or-kvm' - elif [[ -n $( grep -s 'Virtual HD' <<< $vm_data ) ]];then - vm='hyper-v' - elif [[ -e /proc/cpuinfo && -n $( grep -is '^flags.*hypervisor' /proc/cpuinfo ) ]];then - vm='virtual-machine' - elif [[ -e /dev/vda || -e /dev/vdb || -e /dev/xvda || -e /dev/xvdb ]];then - vm='virtual-machine' - fi - fi - # this may catch some BSD and fringe Linux cases - if [[ $vm == 'other-vm?' && $B_ROOT == 'true' ]];then - if [[ -n $DMIDECODE_DATA && $DMIDECODE_DATA != 'dmidecode-error-'* ]];then - product_name=$(dmidecode -s system-product-name 2>/dev/null ) - system_manufacturer=$( dmidecode -s system-manufacturer 2>/dev/null ) - if [[ $product_name == 'VMware'* ]];then - vm='vmware' - elif [[ $product_name == 'VirtualBox'* ]];then - vm='virtualbox' - elif [[ $product_name == 'KVM'* ]];then - vm='kvm' - elif [[ $product_name == 'Bochs'* ]];then - vm='qemu' - elif [[ $system_manufacturer == 'Xen' ]];then - vm='xen' - elif [[ -n $( grep -i 'hypervisor' <<< "$DMIDECODE_DATA" ) ]];then - vm='virtual-machine' - fi - fi - fi - - echo $vm - - eval $LOGFE +sub count_alpha { + my ($count) = @_; + #print "$count\n"; + my @alpha = qw(Single Dual Triple Quad); + if ($count > 4){ + $count .= '-'; + } + else { + $count = $alpha[$count-1] . ' ' if $count > 0; + } + return $count; +} +sub set_cpu_data { + my %cpu = ( + 'arch' => '', + 'bogomips' => 0, + 'cores' => 0, + 'cur-freq' => 0, + 'dies' => 0, + 'family' => '', + 'flags' => '', + 'ids' => [], + 'l1-cache' => 0, # store in KB + 'l2-cache' => 0, # store in KB + 'l3-cache' => 0, # store in KB + 'max-freq' => 0, + 'min-freq' => 0, + 'model_id' => '', + 'model_name' => '', + 'processors' => [], + 'rev' => '', + 'scalings' => [], + 'siblings' => 0, + 'type' => '', + ); + return %cpu; +} +# MHZ - cell cpus +sub speed_cleaner { + my ($speed,$opt) = @_; + return if ! $speed || $speed eq '0'; + $speed =~ s/[GMK]HZ$//gi; + $speed = ($speed/1000) if $opt && $opt eq 'khz'; + $speed = sprintf "%.0f", $speed; + return $speed; +} +sub cpu_cleaner { + my ($cpu) = @_; + return if ! $cpu; + my $filters = '@|cpu |cpu deca|([0-9]+|single|dual|two|triple|three|tri|quad|four|'; + $filters .= 'penta|five|hepta|six|hexa|seven|octa|eight|multi)[ -]core|'; + $filters .= 'ennea|genuine|multi|processor|single|triple|[0-9\.]+ *[MmGg][Hh][Zz]'; + $cpu =~ s/$filters//ig; + $cpu =~ s/\s\s+/ /g; + $cpu =~ s/^\s+|\s+$//g; + return $cpu; +} } -# see which dm has started if any -get_display_manager() +## DiskData { - eval $LOGFS - # ldm - LTSP display manager. Note that sddm does not appear to have a .pid extension in Arch - # note: to avoid positives with directories, test for -f explicitly, not -e - local dm_id_list='entranced.pid gdm.pid gdm3.pid kdm.pid ldm.pid lightdm.pid lxdm.pid mdm.pid nodm.pid sddm.pid sddm slim.lock tint2.pid wdm.pid xdm.pid' - local dm_id='' dm='' separator='' - # note we don't need to filter grep if we do it this way - local x_is_running=$( grep '/usr.*/X' <<< "$Ps_aux_Data" | grep -iv '/Xprt' ) - - for dm_id in $dm_id_list - do - # note: ${dm_id%.*}/$dm_id will create a dir name out of the dm id, then test if pid is in that - # note: sddm, in an effort to be unique and special, do not use a pid/lock file, but rather a random - # string inside a directory called /run/sddm/ so assuming the existence of the pid inside a directory named - # from the dm. Hopefully this change will not have negative results. - if [[ -f /run/$dm_id || -d /run/${dm_id%.*}/ || -f /var/run/$dm_id || \ - -d /var/run/${dm_id%.*}/ ]];then - # just on the off chance that two dms are running, good info to have in that case, if possible - dm=$dm$separator${dm_id%.*} - separator=',' - fi - done - # might add this in, but the rate of new dm's makes it more likely it's an unknown dm, so - # we'll keep output to N/A - if [[ -n $x_is_running && -z $dm ]];then - if [[ -z "${Ps_aux_Data/*startx*/}" ]];then - dm='(startx)' - fi - fi - echo $dm - - log_function_data "display manager: $dm" - - eval $LOGFE -} - -# for more on distro id, please reference this python thread: http://bugs.python.org/issue1322 -## return distro name/id if found -get_distro_data() -{ - eval $LOGFS - local i='' j='' distro='' distro_file='' a_distro_glob='' a_temp='' b_osr='false' - - # may need modification if archbsd / debian can be id'ed with /etc files - if [[ -n $BSD_TYPE ]];then - if [[ $BSD_VERSION != 'darwin' ]];then - distro=$( uname -sr ) - else - if [[ -f /System/Library/CoreServices/SystemVersion.plist ]];then - distro=$( grep -A1 -E '(ProductName|ProductVersion)' /System/Library/CoreServices/SystemVersion.plist | grep '<string>' | sed -E 's/<[\/]?string>//g' ) - distro=$( echo $distro ) - fi - if [[ -z $distro ]];then - distro='Mac OS X' - fi - fi - echo "$distro" - log_function_data "distro: $distro" - eval $LOGFE - return 0 - fi - - # get the wild carded array of release/version /etc files if present - shopt -s nullglob - cd /etc - # note: always exceptions, so wild card after release/version: /etc/lsb-release-crunchbang - # wait to handle since crunchbang file is one of the few in the world that uses this method - a_distro_glob=(*[-_]{release,version}) - cd "$OLDPWD" - shopt -u nullglob - - a_temp=${a_distro_glob[@]} - log_function_data "a_distro_glob: $a_temp" - - if [[ ${#a_distro_glob[@]} -eq 1 ]];then - distro_file="$a_distro_glob" - # use the file if it's in the known good lists - elif [[ ${#a_distro_glob[@]} -gt 1 ]];then - for i in $DISTROS_DERIVED $DISTROS_PRIMARY - do - # Only echo works with ${var[@]}, not print_screen_output() or self_debugger() - # This is a known bug, search for the word "strange" inside comments - # echo "i='$i' a_distro_glob[@]='${a_distro_glob[@]}'" - if [[ " ${a_distro_glob[@]} " == *" $i "* ]];then - # Now lets see if the distro file is in the known-good working-lsb-list - # if so, use lsb-release, if not, then just use the found file - # this is for only those distro's with self named release/version files - # because Mint does not use such, it must be done as below - ## this if statement requires the spaces and * as it is, else it won't work - ## - if [[ " $DISTROS_LSB_GOOD " == *" $i "* ]] && [[ $B_LSB_FILE == 'true' ]];then - distro_file='lsb-release' - elif [[ " $DISTROS_OS_RELEASE_GOOD " == *" $i "* ]] && [[ $B_OS_RELEASE_FILE == 'true' ]];then - distro_file='os-release' - else - distro_file="$i" - fi - break - fi - done - fi - log_function_data "distro_file: $distro_file" - # first test for the legacy antiX distro id file - if [[ -e /etc/antiX ]];then - distro="$( grep -Eoi 'antix.*\.iso' <<< $( remove_erroneous_chars '/etc/antiX' ) | sed 's/\.iso//' )" - # this handles case where only one release/version file was found, and it's lsb-release. This would - # never apply for ubuntu or debian, which will filter down to the following conditions. In general - # if there's a specific distro release file available, that's to be preferred, but this is a good backup. - elif [[ -n $distro_file && $B_LSB_FILE == 'true' && " $DISTROS_LSB_GOOD" == *" $distro_file "* ]];then - distro=$( get_distro_lsb_os_release_data 'lsb-file' ) - elif [[ $distro_file == 'lsb-release' ]];then - distro=$( get_distro_lsb_os_release_data 'lsb-file' ) - elif [[ $distro_file == 'os-release' ]];then - distro=$( get_distro_lsb_os_release_data 'os-release-file' ) - b_osr='true' - # then if the distro id file was found and it's not in the exluded primary distro file list, read it - elif [[ -n $distro_file && -s /etc/$distro_file && " $DISTROS_EXCLUDE_LIST " != *" $distro_file "* ]];then - # new opensuse uses os-release, but older ones may have a similar syntax, so just use the first line - if [[ $distro_file == 'SuSE-release' ]];then - # leaving off extra data since all new suse have it, in os-release, this file has line breaks, like os-release - # but in case we want it, it's: CODENAME = Mantis | VERSION = 12.2 - # for now, just take first occurrence, which should be the first line, which does not use a variable type format - distro=$( grep -i -m 1 'suse' /etc/$distro_file ) - else - distro=$( remove_erroneous_chars "/etc/$distro_file" ) - fi - # otherwise try the default debian/ubuntu /etc/issue file - elif [[ -f /etc/issue ]];then - # os-release/lsb gives more manageable and accurate output than issue, but mint should use issue for now - # some bashism, boolean must be in parenthesis to work correctly, ie [[ $(boolean) ]] not [[ $boolean ]] - if [[ $B_OS_RELEASE_FILE == 'true' ]] && [[ -z $( grep -i 'mint' /etc/issue ) ]];then - distro=$( get_distro_lsb_os_release_data 'os-release-file' ) - b_osr='true' - elif [[ $B_LSB_FILE == 'true' ]] && [[ -z $( grep -i 'mint' /etc/issue ) ]];then - distro=$( get_distro_lsb_os_release_data 'lsb-file' ) - else - distro=$( gawk ' - BEGIN { - RS="" +package DiskData; +my ($b_hddtemp,$b_nvme,$b_sudo); +my ($hddtemp,$nvme,$sudo) = ('','',''); +my (@by_id,@by_path); + +sub get { + eval $start if $b_log; + my (@data,@rows,$key1,$val1); + my ($type) = @_; + $type ||= 'standard'; + my $num = 0; + @data = disk_data($type); + # NOTE: + if (@data){ + if ($type eq 'standard'){ + @data = create_output(@data); + @rows = (@rows,@data); + if ( $bsd_type && !@dm_boot_disk && $type eq 'standard' && $show{'disk'} ){ + $key1 = 'Drive Report'; + my $file = main::system_files('dmesg-boot'); + if ( $file && ! -r $file ){ + $val1 = main::row_defaults('dmesg-boot-permissions'); + } + elsif (! -e $file){ + $val1 = main::row_defaults('dmesg-boot-missing'); + } + else { + $val1 = main::row_defaults('disk-data-bsd'); + } + @data = ({main::key($num++,$key1) => $val1,}); + @rows = (@rows,@data); } - { - gsub(/\\[a-z]/, "") - gsub(/'"$BAN_LIST_ARRAY"'/, " ") - gsub(/^ +| +$/, "") - gsub(/ [ \t]+/, " ") - print - }' /etc/issue ) - - # this handles an arch bug where /etc/arch-release is empty and /etc/issue is corrupted - # only older arch installs that have not been updated should have this fallback required, new ones use - # os-release - if [[ -n $( grep -i 'arch linux' <<< $distro ) ]];then - distro='Arch Linux' - fi - fi - fi - # a final check. If a long value, before assigning the debugger output, if os-release - # exists then let's use that if it wasn't tried already. Maybe that will be better. - if [[ ${#distro} -gt 80 ]] && [[ $B_HANDLE_CORRUPT_DATA != 'true' ]];then - if [[ $B_OS_RELEASE_FILE == 'true' && $b_osr == 'false' ]];then - distro=$( get_distro_lsb_os_release_data 'os-release-file' ) - fi - if [[ ${#distro} -gt 80 ]];then - distro="${RED}/etc/$distro_file corrupted, use -% to override${NORMAL}" - fi - fi - ## note: would like to actually understand the method even if it's not used - # : ${distro:=Unknown distro o_O} - ## test for /etc/lsb-release as a backup in case of failure, in cases where > one version/release file - ## were found but the above resulted in null distro value - # Because os-release is now more common, we'll test for it first. - if [[ -z $distro ]] && [[ $B_OS_RELEASE_FILE == 'true' ]];then - distro=$( get_distro_lsb_os_release_data 'os-release-file' ) - fi - if [[ -z $distro ]] && [[ $B_LSB_FILE == 'true' ]];then - distro=$( get_distro_lsb_os_release_data 'lsb-file' ) - fi - - # now some final null tries - if [[ -z $distro ]];then - # if the file was null but present, which can happen in some cases, then use the file name itself to - # set the distro value. Why say unknown if we have a pretty good idea, after all? - if [[ -n $distro_file ]] && [[ " $DISTROS_DERIVED $DISTROS_PRIMARY " == *" $distro_file "* ]];then - distro=$( sed $SED_RX -e 's/[-_]//' -e 's/(release|version)//' <<< $distro_file | sed $SED_RX 's/^([a-z])/\u\1/' ) - fi - ## finally, if all else has failed, give up - if [[ -z $distro ]];then - distro='unknown' - fi - fi - # final step cleanup of unwanted information - # opensuse has the x86 etc type string in names, not needed as redundant since -S already shows that - distro=$( gawk ' - BEGIN { - IGNORECASE=1 - } - { - sub(/ *\(*(x86_64|i486|i586|i686|686|586|486)\)*/, "", $0) - print $0 - }' <<< $distro ) - echo "$distro" - log_function_data "distro: $distro" - eval $LOGFE -} - -# args: $1 - lsb-file/lsb-app/os-release-file -get_distro_lsb_os_release_data() -{ - eval $LOGFS - local distro='' - - case $1 in - lsb-file) - if [[ $B_LSB_FILE == 'true' ]];then - distro=$( gawk -F '=' ' - BEGIN { - IGNORECASE=1 - } - # clean out unwanted characters - { - gsub(/\\|\"|[:\47]/,"", $0 ) - gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2 ) - gsub(/^[[:space:]]+|[[:space:]]+$/, "", $1 ) - } - # note: adding the spacing directly to variable to make sure distro output is null if not found - /^DISTRIB_ID/ { - # this is needed because grep for "arch" is too loose to be safe - if ( $2 == "arch" ) { - distroId = "Arch Linux" - } - else if ( $2 != "n/a" ) { - distroId = $2 " " - } + } + else { + @rows = @data; + # print Data::Dumper::Dumper \@rows; + } + } + else { + $key1 = 'Message'; + $val1 = main::row_defaults('disk-data'); + @rows = ({main::key($num++,$key1) => $val1,}); + } + if (!@rows){ + $key1 = 'Message'; + $val1 = main::row_defaults('disk-data'); + @data = ({main::key($num++,$key1) => $val1,}); + } + #@rows = (@rows,@data); + @data = (); + if ($show{'optical'} || $show{'optical-basic'}){ + @data = OpticalData::get(); + @rows = (@rows,@data); + } + ($b_hddtemp,$b_nvme,$b_sudo,$hddtemp,$nvme,$sudo) = (undef,undef,undef,undef,undef,undef); + (@by_id,@by_path) = (undef,undef); + eval $end if $b_log; + return @rows; +} +sub create_output { + eval $start if $b_log; + my (@disks) = @_; + #print Data::Dumper::Dumper \@disks; + my (@data,@rows); + my ($num,$j) = (0,0); + my ($id,$model,$size,$used,$percent,$size_holder,$used_holder) = ('','','','','','',''); + my @sizing = main::get_size($disks[0]{'size'}) if $disks[0]{'size'}; + #print Data::Dumper::Dumper \@disks; + if (@sizing){ + $size = $sizing[0]; + # note: if a string is returned there will be no Size unit so just use string. + if (defined $sizing[0] && $sizing[1]){ + $size .= ' ' . $sizing[1]; + } + } + $size ||= 'N/A'; + @sizing = main::get_size($disks[0]{'used'}) if $disks[0]{'used'}; + if (@sizing){ + $used = $sizing[0]; + if (defined $sizing[0] && $sizing[1]){ + $used .= ' ' . $sizing[1]; + if (( $disks[0]{'size'} && $disks[0]{'size'} =~ /^[0-9]/ ) && + ( $disks[0]{'used'} =~ /^[0-9]/ ) ){ + $used = $used . ' (' . sprintf("%0.1f", $disks[0]{'used'}/$disks[0]{'size'}*100) . '%)'; + } + } + } + $used ||= 'N/A'; + @data = ({ + main::key($num++,'HDD Total Size') => $size, + main::key($num++,'used') => $used, + }); + @rows = (@rows,@data); + shift @disks; + if ( $show{'disk'} && @disks){ + @disks = sort { $a->{'id'} cmp $b->{'id'} } @disks; + foreach my $ref (@disks){ + ($id,$model,$size) = ('','',''); + my %row = %$ref; + $num = 1; + $model = ($row{'model'}) ? $row{'model'}: 'N/A'; + $id = ($row{'id'}) ? "/dev/$row{'id'}":'N/A'; + my @sizing = main::get_size($row{'size'}); + #print Data::Dumper::Dumper \@disks; + if (@sizing){ + $size = $sizing[0]; + # note: if a string is returned there will be no Size unit so just use string. + if (defined $sizing[0] && $sizing[1]){ + $size .= ' ' . $sizing[1]; + $size_holder = $sizing[0]; } - /^DISTRIB_RELEASE/ { - if ( $2 != "n/a" ) { - distroRelease = $2 " " - } + $size ||= 'N/A'; + } + else { + $size = 'N/A'; + } + $j = scalar @rows; + @data = ({ + main::key($num++,'ID') => $id, + }); + @rows = (@rows,@data); + if ($row{'type'}){ + $rows[$j]{main::key($num++,'type')} = $row{'type'}, + } + $rows[$j]{main::key($num++,'model')} = $model; + $rows[$j]{main::key($num++,'size')} = $size; + if ($extra > 0 && $row{'speed'}){ + $rows[$j]{main::key($num++,'speed')} = $row{'speed'}; + } + if ($extra > 1){ + my $serial = main::apply_filter($row{'serial'}); + $rows[$j]{main::key($num++,'serial')} = $serial; + if ($row{'firmware'}){ + $rows[$j]{main::key($num++,'rev')} = $row{'firmware'}; } - /^DISTRIB_CODENAME/ { - if ( $2 != "n/a" ) { - distroCodename = $2 " " - } + } + if ($extra > 0 && $row{'temp'}){ + $rows[$j]{main::key($num++,'temp')} = $row{'temp'} . ' C'; + } + # extra level tests already done + if (defined $row{'partition-table'}){ + $rows[$j]{main::key($num++,'scheme')} = $row{'partition-table'}; + } + } + } + + eval $end if $b_log; + return @rows; +} +sub disk_data { + eval $start if $b_log; + my ($type) = @_; + my (@rows,@data,@devs); + my $num = 0; + my ($used) = (0); + PartitionData::partition_data() if !$b_partitions; + foreach my $ref (@partitions){ + my %row = %$ref; + # don't count remote used, also, some cases mount + # panfs is parallel NAS volume manager, need more data + next if ($row{'fs'} && $row{'fs'} =~ /nfs|panfs|sshfs|smbfs|unionfs/); + # in some cases, like redhat, mounted cdrom/dvds show up in partition data + next if ($row{'dev-base'} && $row{'dev-base'} =~ /^sr[0-9]+$/); + # this is used for specific cases where bind, or incorrect multiple mounts + # to same partitions, or btrfs sub volume mounts, is present. The value is + # searched for an earlier appearance of that partition and if it is present, + # the data is not added into the partition used size. + if ( $row{'dev-base'} !~ /^\/\/|:\// && ! (grep {/$row{'dev-base'}/} @devs) ){ + $used += $row{'used'} if $row{'used'}; + push @devs, $row{'dev-base'}; + } + } + if (!$bsd_type && (my $file = main::system_files('partitions'))){ + @data = proc_data($used,$file); + } + elsif ($bsd_type) { + @data = dmesg_boot_data($used); + } + #print Data::Dumper::Dumper \@data; + main::log_data('data',"used: $used") if $b_log; + eval $end if $b_log; + return @data; +} +sub proc_data { + eval $start if $b_log; + my ($used,$file) = @_; + my (@data,@drives); + my ($b_hdx,$size,$drive_size) = (0,0,0); + my @proc_partitions = main::reader($file,'strip'); + shift @proc_partitions; + foreach (@proc_partitions){ + next if (/^\s*$/); + my @row = split /\s+/, $_; + if ( $row[-1] =~ /^([hsv]d[a-z]+|(ada|mmcblk|n[b]?d|nvme[0-9]+n)[0-9]+)$/) { + $drive_size = $row[2]; + $b_hdx = 1 if $row[-1] =~ /^hd[a-z]/; + @data = ({ + 'firmware' => '', + 'id' => $row[-1], + 'model' => '', + 'serial' => '', + 'size' => $drive_size, + 'spec' => '', + 'speed' => '', + 'temp' => '', + 'type' => '', + 'vendor' => '', + }); + @drives = (@drives,@data); + } + # See http://lanana.org/docs/device-list/devices-2.6+.txt for major numbers used below + # See https://www.mjmwired.net/kernel/Documentation/devices.txt for kernel 4.x device numbers + # if ( $row[0] =~ /^(3|22|33|8)$/ && $row[1] % 16 == 0 ) { + # $size += $row[2]; + # } + # special case from this data: 8 0 156290904 sda + # 43 0 48828124 nbd0 + # note: known starters: vm: 252/253/254; grsec: 202; nvme: 259 + if ( $row[0] =~ /^(3|8|22|33|43|202|252|253|254|259)$/ && + $row[-1] =~ /(n[b]?d[0-9]+|nvme[0-9]+n[0-9]+|[hsv]d[a-z]+)$/ && + ( $row[1] % 16 == 0 || $row[1] % 16 == 8 ) ) { + $size += $row[2]; + } + } + # print Data::Dumper::Dumper \@drives; + main::log_data('data',"size: $size") if $b_log; + @data = ({ + 'size' => $size, + 'used' => $used, + }); + #print Data::Dumper::Dumper \@data; + if ( $show{'disk'} ){ + @drives = (@data,@drives); + # print 'drives:', Data::Dumper::Dumper \@drives; + @data = proc_data_advanced($b_hdx,@drives); + } + main::log_data('dump','@data',\@data) if $b_log; + # print Data::Dumper::Dumper \@data; + eval $end if $b_log; + return @data; +} +sub proc_data_advanced { + eval $start if $b_log; + my ($b_hdx,@drives) = @_; + my ($i) = (0); + my (@data,@rows,@scsi,@temp,@working); + my ($pt_cmd) = ('unset'); + my ($block_type,$file,$firmware,$model,$path,$partition_scheme, + $serial,$vendor,$working_path); + @by_id = main::globber('/dev/disk/by-id/*'); + @by_path = main::globber('/dev/disk/by-path/*'); + ## check for all ide type drives, non libata, only do it if hdx is in array + ## this is now being updated for new /sys type paths, this may handle that ok too + ## skip the first rows in the loops since that's the basic size/used data + if ($b_hdx){ + for ($i = 1; $i < scalar @drives; $i++){ + $file = "/proc/ide/$drives[$i]{'id'}/model"; + if ( $drives[$i]{'id'} =~ /^hd[a-z]/ && -e $file){ + $model = (main::reader($file,'strip'))[0]; + $drives[$i]{'model'} = $model; + } + } + } + # scsi stuff + if ($file = main::system_files('scsi')){ + @scsi = scsi_data($file); + } + #print 'drives:', Data::Dumper::Dumper \@drives; + for ($i = 1; $i < scalar @drives; $i++){ + #next if $drives[$i]{'id'} =~ /^hd[a-z]/; + ($block_type,$firmware,$model,$partition_scheme, + $serial,$vendor,$working_path) = ('','','','','','',''); + if ($extra > 2){ + @data = partition_scheme($pt_cmd,$drives[$i]{'id'}); + $pt_cmd = $data[0]; + $drives[$i]{'partition-table'} = uc($data[1]) if $data[1]; + } + #print "$drives[$i]{'id'}\n"; + if ($drives[$i]{'id'} =~ /[sv]d[a-z]/){ + $block_type = 'sdx'; + $working_path = "/sys/block/$drives[$i]{'id'}/device/"; + } + elsif ($drives[$i]{'id'} =~ /mmcblk/){ + $block_type = 'mmc'; + $working_path = "/sys/block/$drives[$i]{'id'}/device/"; + } + elsif ($drives[$i]{'id'} =~ /nvme/){ + $block_type = 'nvme'; + # this results in: + # /sys/devices/pci0000:00/0000:00:03.2/0000:06:00.0/nvme/nvme0/nvme0n1 + # but we want to go one level down so slice off trailing nvme0n1 + $working_path = Cwd::abs_path("/sys/block/$drives[$i]{'id'}"); + $working_path =~ s/nvme[^\/]*$//; + } + main::log_data('data',"working path: $working_path") if $b_log; + if ($block_type){ + # NOTE: while path ${working_path}vendor exists, it contains junk value, like: ATA + $path = "${working_path}model"; + if ( -e $path){ + $model = (main::reader($path,'strip'))[0]; + if ($model){ + $model =~ s/\s/_/g; + #@temp = split /-/, $model; + #$drives[$i]{'model'} = $temp[0]; + $drives[$i]{'model'} = $model; } - # sometimes some distros cannot do their lsb-release files correctly, so here is - # one last chance to get it right. - /^DISTRIB_DESCRIPTION/ { - if ( $2 != "n/a" ) { - distroDescription = $2 + } + elsif ($block_type eq 'mmc' && -e "${working_path}name"){ + $path = "${working_path}name"; + if ( -e $path){ + $model = (main::reader($path,'strip'))[0]; + if ($model){ + $model =~ s/\s/_/g; + #@temp = split /-/, $model; + #$drives[$i]{'model'} = $temp[0]; + $drives[$i]{'model'} = $model; } } - END { - fullString="" - if ( distroId == "" && distroRelease == "" && distroCodename == "" && distroDescription != "" ){ - fullString = distroDescription - } - else { - fullString = distroId distroRelease distroCodename - } - print fullString - } - ' $FILE_LSB_RELEASE ) - log_function_data 'cat' "$FILE_LSB_RELEASE" - fi - ;; - lsb-app) - # this is HORRIBLY slow, not using - if type -p lsb_release &>/dev/null;then - distro=$( echo "$( lsb_release -irc )" | gawk -F ':' ' - BEGIN { - IGNORECASE=1 - } - # clean out unwanted characters - { - gsub(/\\|\"|[:\47]/,"", $0 ) - gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2 ) - gsub(/^[[:space:]]+|[[:space:]]+$/, "", $1 ) - } - /^Distributor ID/ { - distroId = $2 - } - /^Release/ { - distroRelease = $2 - } - /^Codename/ { - distroCodename = $2 - } - END { - print distroId " " distroRelease " (" distroCodename ")" - }' ) - fi - ;; - os-release-file) - if [[ $B_OS_RELEASE_FILE == 'true' ]];then - distro=$( gawk -F '=' ' - BEGIN { - IGNORECASE=1 - prettyName="" - regularName="" - versionName="" - versionId="" - distroName="" - } - # clean out unwanted characters - { - gsub(/\\|\"|[:\47]/,"", $0 ) - gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2 ) - gsub(/^[[:space:]]+|[[:space:]]+$/, "", $1 ) - } - # note: adding the spacing directly to variable to make sure distro output is null if not found - /^PRETTY_NAME/ { - if ( $2 != "n/a" ) { - prettyName = $2 + } + elsif (@scsi && @by_id){ + ## ok, ok, it's incomprehensible, search /dev/disk/by-id for a line that contains the + # discovered disk name AND ends with the correct identifier, sdx + # get rid of whitespace for some drive names and ids, and extra data after - in name + foreach my $ref (@scsi){ + my %row = %$ref; + if ($row{'model'}){ + $row{'model'} =~ s/\s/_/g; + $row{'model'} = (split /-/,$row{'model'})[0]; + foreach my $id (@by_id){ + if ($id =~ /$row{'model'}/ && "/dev/$drives[$i]{'id'}" eq Cwd::abs_path($id)){ + $drives[$i]{'firmware'} = $row{'firmware'}; + $drives[$i]{'model'} = $row{'model'}; + $drives[$i]{'vendor'} = $row{'vendor'}; + last; + } + } } } - /^NAME/ { - if ( $2 != "n/a" ) { - regularName = $2 + } + $path = "${working_path}removable"; + if (-e $path){ + my $b_removable = (main::reader($path,'strip'))[0]; # 0/1 value + $drives[$i]{'type'} = 'Removable' if $b_removable; + } + } + my $peripheral = peripheral_data($drives[$i]{'id'}); + # note: we only want to update type if we found a peripheral, otherwise preserve value + $drives[$i]{'type'} = $peripheral if $peripheral; + # print "type:$drives[$i]{'type'}\n"; + if ($extra > 0){ + $drives[$i]{'temp'} = hdd_temp("/dev/$drives[$i]{'id'}"); + if ($extra > 1){ + $path = "${working_path}serial"; + if ( -e $path){ + $serial = (main::reader($path,'strip'))[0]; + if ($serial){ + $drives[$i]{'serial'} = $serial; } } - /^VERSION/ { - if ( $2 != "n/a" && $1 == "VERSION" ) { - versionName = $2 + else { + $drives[$i]{'serial'} = disk_serial_by_id("/dev/$drives[$i]{'id'}"); + } + if ($extra > 2){ + $path = "${working_path}rev"; + if ( -e $path){ + $drives[$i]{'firmware'} = (main::reader($path,'strip'))[0]; } - else if ( $2 != "n/a" && $1 == "VERSION_ID" ) { - versionId = $2 + $path = "${working_path}fwrev"; + if ( !$drives[$i]{'firmware'} && -e $path){ + $drives[$i]{'firmware'} = (main::reader($path,'strip'))[0]; } } - END { - # NOTE: tumbleweed has pretty name but pretty name does not have version id - if ( prettyName != "" && regularName !~ /tumbleweed/ ) { - distroName = prettyName - } - else if ( regularName != "" ) { - distroName = regularName - if ( versionName != "" ) { - distroName = distroName " " versionName - } - else if ( versionId != "" ) { - distroName = distroName " " versionId - } - + } + } + } + # print Data::Dumper::Dumper \@drives; + eval $end if $b_log; + return @drives; +} +# camcontrol identify <device> |grep ^serial (this might be (S)ATA specific) +# smartcl -i <device> |grep ^Serial +# see smartctl; camcontrol devlist; gptid status; +sub dmesg_boot_data { + eval $start if $b_log; + my ($used) = @_; + my (@data,@drives,@temp); + my ($id_holder,$i,$size,$working) = ('',0,0,0); + my $file = main::system_files('dmesg-boot'); + if (@dm_boot_disk){ + foreach (@dm_boot_disk){ + my @row = split /:\s*/, $_; + next if ! defined $row[1]; + if ($id_holder ne $row[0]){ + $i++ if $id_holder; + # print "$i $id_holder $row[0]\n"; + $id_holder = $row[0]; + } + # no dots, note: ada2: 2861588MB BUT: ada2: 600.000MB/s + if (! exists $drives[$i]){ + $drives[$i] = ({}); + $drives[$i]{'id'} = $row[0]; + $drives[$i]{'firmware'} = ''; + $drives[$i]{'temp'} = ''; + $drives[$i]{'type'} = ''; + $drives[$i]{'vendor'} = ''; + } + #print "$i\n"; + if ($bsd_type eq 'openbsd'){ + if ($row[1] =~ /,\s*([0-9\.]+[MGTPE][B]?),.*\ssectors$|^</){ + $working = main::translate_size($1); + $size += $working if $working; + $drives[$i]{'size'} = $working; + } + if ($row[2] && $row[2] =~ /<([^>]+)>/){ + $drives[$i]{'model'} = $1 if $1; + $drives[$i]{'type'} = 'removable' if $_ =~ /removable$/; + # <Generic-, Compact Flash, 1.00> + my $count = ($drives[$i]{'model'} =~ tr/,//); + if ($count && $count > 1){ + @temp = split /,\s*/, $drives[$i]{'model'}; + $drives[$i]{'model'} = $temp[1]; } - print distroName } - ' $FILE_OS_RELEASE ) - log_function_data 'cat' "$FILE_OS_RELEASE" - fi - ;; - esac - echo $distro - log_function_data "distro: $distro" - eval $LOGFE + # print "openbsd\n"; + } + else { + if ($row[1] =~ /^([0-9]+[KMGTPE][B]?)\s/){ + $working = main::translate_size($1); + $size += $working if $working; + $drives[$i]{'size'} = $working; + } + if ($row[1] =~ /device$|^</){ + $row[1] =~ s/\sdevice$//g; + $row[1] =~ /<([^>]*)>\s(.*)/; + $drives[$i]{'model'} = $1 if $1; + $drives[$i]{'spec'} = $2 if $2; + } + if ($row[1] =~ /^Serial\sNumber\s(.*)/){ + $drives[$i]{'serial'} = $1; + } + if ($row[1] =~ /^([0-9\.]+[MG][B]?\/s)/){ + $drives[$i]{'speed'} = $1; + $drives[$i]{'speed'} =~ s/\.[0-9]+// if $drives[$i]{'speed'}; + } + } + } + if (!$size){ + $size = main::row_defaults('data-bsd'); + } + } + elsif ( $file && ! -r $file ){ + $size = main::row_defaults('dmesg-boot-permissions'); + } + elsif (! -f $file ){ + $size = main::row_defaults('dmesg-boot-missing'); + } + @data = ({ + 'size' => $size, + 'used' => $used, + }); + #main::log_data('dump','@data',\@data) if $b_log; + if ( $show{'disk'} ){ + @data = (@data,@drives); + # print 'drives:', Data::Dumper::Dumper \@drives; + } + # print Data::Dumper::Dumper \@data; + eval $end if $b_log; + return @data; } -get_gcc_system_version() -{ - eval $LOGFS - local separator='' gcc_installed='' gcc_list='' gcc_others='' a_temp='' - local gcc_version=$( - gcc --version 2>/dev/null | sed $SED_RX 's/\([^\)]*\)//g' | gawk ' - BEGIN { - IGNORECASE=1 - } - /^gcc/ { - print $2 - exit - }' ) - # can't use xargs -L basename because not all systems support thats - if [[ $B_EXTRA_EXTRA_DATA == 'true' ]];then - gcc_others=$( ls /usr/bin/gcc-* 2>/dev/null ) - if [[ -n $gcc_others ]];then - for item in $gcc_others - do - item=${item##*/} - gcc_installed=$( gawk -F '-' ' - $2 ~ /^[0-9\.]+$/ { - print $2 - }' <<< $item ) - if [[ -n $gcc_installed && -z $( grep "^$gcc_installed" <<< $gcc_version ) ]];then - gcc_list=$gcc_list$separator$gcc_installed - separator=',' - fi - done - fi - fi - if [[ -n $gcc_version ]];then - A_GCC_VERSIONS=( "$gcc_version" $gcc_list ) - fi - a_temp=${A_GCC_VERSIONS[@]} - log_function_data "A_GCC_VERSIONS: $a_temp" - eval $LOGFE -} - -get_gpu_temp_data() -{ - local gpu_temp='' gpu_fan='' screens='' screen_nu='' gpu_temp_looper='' - - # we'll try for nvidia/ati, then add if more are shown - if type -p nvidia-settings &>/dev/null;then - # first get the number of screens. This only work if you are in X - if [[ $B_RUNNING_IN_DISPLAY == 'true' ]];then - screens=$( nvidia-settings -q screens | gawk ' - /:[0-9]\.[0-9]/ { - screens=screens gensub(/(.*)(:[0-9]\.[0-9])(.*)/, "\\2", "1", $0) " " - } - END { - print screens - } - ' ) - else - # do a guess, this will work for most users, it's better than nothing for out of X - screens=':0.0' - fi - # now we'll get the gpu temp for each screen discovered. The print out function - # will handle removing screen data for single gpu systems - for screen_nu in $screens - do - gpu_temp_looper=$( nvidia-settings -c $screen_nu -q GPUCoreTemp 2>/dev/null | gawk -F ': ' ' - BEGIN { - IGNORECASE=1 - gpuTemp="" - gpuTempWorking="" - } - /Attribute (.*)[0-9]+\.$/ { - gsub(/\./, "", $2) - if ( $2 ~ /^[0-9]+$/ ) { - gpuTemp=gpuTemp $2 "C " - } - } - END { - print gpuTemp - }' ) - screen_nu=$( cut -d ':' -f 2 <<< $screen_nu ) - gpu_temp="$gpu_temp$screen_nu:$gpu_temp_looper " - done - elif type -p aticonfig &>/dev/null;then -# gpu_temp=$( aticonfig --adapter=0 --od-gettemperature | gawk -F ': ' ' - gpu_temp=$( aticonfig --adapter=all --od-gettemperature | gawk -F ': ' ' - BEGIN { - IGNORECASE=1 - gpuTemp="" - gpuTempWorking="" - } - /Sensor (.*)[0-9\.]+ / { - gpuTempWorking=gensub(/(.*) ([0-9\.]+) (.*)/, "\\2", "1", $2) - if ( gpuTempWorking ~ /^[0-9\.]+$/ ) { - gpuTemp=gpuTemp gpuTempWorking "C " - } - } - END { - print gpuTemp - }' ) - # this handles some newer cases of free driver temp readouts, will require modifications as - # more user data appears. - elif [[ -n $Sensors_Data ]];then - gpu_temp=$( - gawk ' - BEGIN { - IGNORECASE=1 - gpuTemp="" - separator="" - } - /^('"$SENSORS_GPU_SEARCH"')-pci/ { - while ( getline && !/^$/ ) { - if ( /^temp/ ) { - sub(/^[[:alnum:]]*.*:/, "", $0 ) # clear out everything to the : - gsub(/[\+ \t°]/, "", $1) # ° is a special case, like a space for gawk - gpuTemp=gpuTemp separator $1 - separator="," - } - } - } - END { - print gpuTemp - }' <<< "$Sensors_Data" ) - fi - - if [[ -n $gpu_temp ]];then - echo $gpu_temp - fi +# check for usb/firewire/[and thunderwire when data found] +sub peripheral_data { + eval $start if $b_log; + my ($id) = @_; + my ($type) = (''); + # print "$id here\n"; + if (@by_id){ + foreach (@by_id) { + if ("/dev/$id" eq Cwd::abs_path($_)){ + #print "$id here\n"; + if (/usb-/i){ + $type = 'USB'; + } + elsif (/ieee1394--/i){ + $type = 'FireWire'; + } + last; + } + } + } + # note: sometimes with wwn- numbering usb does not appear in by-id but it does in by-path + if (!$type && @by_path){ + foreach (@by_path) { + if ("/dev/$id" eq Cwd::abs_path($_)){ + if (/usb-/i){ + $type = 'USB'; + } + elsif (/ieee1394--/i){ + $type = 'FireWire'; + } + last; + } + } + } + eval $end if $b_log; + return $type; } - -## for possible future data, not currently used -get_graphics_agp_data() -{ - eval $LOGFS - local agp_module='' - - if [[ $B_MODULES_FILE == 'true' ]];then - ## not used currently - agp_module=$( gawk ' - /agp/ && !/agpgart/ && $3 > 0 { - print(gensub(/(.*)_agp.*/,"\\1","g",$1)) - }' $FILE_MODULES ) - log_function_data 'cat' "$FILE_MODULES" - fi - log_function_data "agp_module: $agp_module" - eval $LOGFE -} - -## create array of gfx cards installed on system -get_graphics_card_data() -{ - eval $LOGFS - local i='' a_temp='' - - IFS=$'\n' - A_GRAPHICS_CARD_DATA=( $( gawk -F': ' ' - BEGIN { - IGNORECASE=1 - busId="" - trueCard="" - card="" - driver="" - } - # not using 3D controller yet, needs research: |3D controller |display controller - # note: this is strange, but all of these can be either a separate or the same - # card. However, by comparing bus id, say: 00:02.0 we can determine that the - # cards are either the same or different. We want only the .0 version as a valid - # card. .1 would be for example: Display Adapter with bus id x:xx.1, not the right one - /vga compatible controller|3D controller|Display controller/ { - driver="" - gsub(/'"$BAN_LIST_NORMAL"'/, "", $NF) - gsub(/'"$BAN_LIST_ARRAY"'/, " ", $NF) - if ( '$COLS_INNER' < 100 ){ - sub(/Core Processor Family/,"Core", $NF) - } - gsub(/^ +| +$/, "", $NF) - gsub(/ [ \t]+/, " ", $NF) - card=$NF - busId=gensub(/^([0-9a-f:\.]+) (.+)$/,"\\1",1,$1) - trueCard=gensub(/(.*)\.([0-9]+)$/,"\\2",1,busId) - while ( getline && !/^$/) { - if ( $1 ~ /Kernel driver in use/ ){ - driver=$2 - } - } - if ( trueCard == 0 ) { - print card "," busId "," driver - # print card "," busId "," driver > "/dev/tty" - } - }' <<< "$LSPCI_V_DATA" ) ) - IFS="$ORIGINAL_IFS" -# for (( i=0; i < ${#A_GRAPHICS_CARD_DATA[@]}; i++ )) -# do -# A_GRAPHICS_CARD_DATA[i]=$( sanitize_characters BAN_LIST_NORMAL "${A_GRAPHICS_CARD_DATA[i]}" ) -# done - - # GFXMEM is UNUSED at the moment, because it shows AGP aperture size, which is not necessarily equal to GFX memory.. - # GFXMEM="size=[$(echo "$LSPCI_V_DATA" | gawk '/VGA/{while (!/^$/) {getline;if (/size=[0-9][0-9]*M/) {size2=gensub(/.*\[size=([0-9]+)M\].*/,"\\1","g",$0);if (size<size2){size=size2}}}}END{print size2}')M]" - a_temp=${A_GRAPHICS_CARD_DATA[@]} - log_function_data "A_GRAPHICS_CARD_DATA: $a_temp" - eval $LOGFE -} - -get_graphics_driver() -{ - eval $LOGFS - - # list is from sgfxi plus non-free drivers - local driver_list='amdgpu|apm|ark|ati|chips|cirrus|cyrix|fbdev|fglrx|glint|i128|i740|i810|iftv|imstt|intel|ivtv|mach64|mesa|mga|modesetting|neomagic|newport|nouveau|nsc|nvidia|nv|openchrome|radeonhd|radeon|rendition|s3virge|s3|savage|siliconmotion|sisimedia|sisusb|sis|tdfx|tga|trident|tseng|unichrome|v4l|vboxvideo|vesa|vga|via|vmware|voodoo' - local driver='' driver_string='' xorg_log_data='' status='' a_temp='' - - if [[ $B_XORG_LOG == 'true' ]];then - A_GRAPHIC_DRIVERS=( $( - gawk ' - BEGIN { - driver="" - bLoaded="false" - IGNORECASE=1 - } - # note that in file names, driver is always lower case - /[[:space:]]Loading.*('"$driver_list"')_drv.so$/ { - driver=gensub(/.*[[:space:]]Loading.*('"$driver_list"')_drv.so/, "\\1", 1, $0 ) - # we get all the actually loaded drivers first, we will use this to compare the - # failed/unloaded, which have not always actually been truly loaded - aDrivers[driver]="loaded" - } - # openbsd uses UnloadModule: - /(Unloading[[:space:]]|UnloadModule).*('"$driver_list"')(\"||_drv.so)$/ { - gsub(/\"/,"",$0) - driver=gensub(/(.*)(Unloading[[:space:]]|UnloadModule).*('"$driver_list"')(\"||_drv.so)$/, "\\3", 1, $0 ) - # we need to make sure that the driver has already been truly loaded, not just discussed - if ( driver in aDrivers ) { - aDrivers[driver]="unloaded" - } - } - /Failed.*('"$driver_list"')_drv.so|Failed.*\"('"$driver_list"')\"/ { - driver=gensub(/(.*)Failed.*('"$driver_list"')_drv.so/, "\\2", 1, $0 ) - if ( driver == $0 ) { - driver=gensub(/(.*)Failed.*\"('"$driver_list"')\".*|fred/, "\\2", 1, $0 ) - } - # we need to make sure that the driver has already been truly loaded, not just discussed - if ( driver != $0 && driver in aDrivers ) { - aDrivers[driver]="failed" - } - } - # verify that the driver actually started the desktop, even with false failed messages which can occur - # this is the driver that is actually driving the display - /.*\([0-9]+\):[[:space:]]Depth.*framebuffer/ { - driver=gensub(/.*('"$driver_list"')\([0-9]+\):[[:space:]]Depth.*framebuffer.*/, "\\1", 1, $0 ) - # we need to make sure that the driver has already been truly loaded, not just discussed, also - # set driver to lower case because sometimes it will show as RADEON or NVIDIA in the actual x start - driver=tolower(driver) - if ( driver != $0 && driver in aDrivers ) { - aDrivers[driver]="loaded" - } - } - END { - for ( driver in aDrivers ) { - print driver "," aDrivers[driver] - } - }' < $FILE_XORG_LOG ) ) - fi - a_temp=${A_GRAPHIC_DRIVERS[@]} - log_function_data "A_GRAPHIC_DRIVERS: $a_temp" - - eval $LOGFE +sub partition_scheme { + eval $start if $b_log; + my ($set_cmd,$id) = @_; + my ($cmd,$pt,$program,@data,@return); + if ($set_cmd ne 'unset'){ + $return[0] = $set_cmd; + } + else { + # runs as user, but is SLOW: udisksctl info -b /dev/sda + # line: org.freedesktop.UDisks2.PartitionTable: + # Type: dos + if (!$b_root){ + if ($program = main::check_program('udevadm')){ + $return[0] = "$program info -q property -n "; + } + } + if (!$return[0] && $b_root && -e "/lib/udev/udisks-part-id") { + $return[0] = "/lib/udev/udisks-part-id /dev/"; + } + elsif (!$return[0] && $b_root && ($program = main::check_program('fdisk'))) { + $return[0] = "$program -l /dev/"; + } + if (!$return[0]) { + $return[0] = 'na' + } + } + if ($return[0] ne 'na'){ + $cmd = "$return[0]$id 2>&1"; + main::log_data('cmd',$cmd) if $b_log; + @data = main::grabber($cmd); + # for pre ~ 2.30 fdisk did not show gpt, but did show gpt scheme error, so + # if no gpt match, it's dos = mbr + if ($cmd =~ /fdisk/){ + foreach (@data){ + if (/^WARNING:\s+GPT/){ + $return[1] = 'gpt'; + last; + } + elsif (/^Disklabel\stype:\s*(.+)/i){ + $return[1] = $1; + last; + } + } + $return[1] = 'dos' if !$return[1]; + } + else { + $return[1] = main::awk(\@data,'^(UDISKS_PARTITION_TABLE_SCHEME|ID_PART_TABLE_TYPE)',2,'='); + } + $return[1] = 'mbr' if $return[1] eq 'dos'; + } + eval $end if $b_log; + return @return; +} +sub scsi_data { + eval $start if $b_log; + my ($file) = @_; + my @temp = main::reader($file); + my (@scsi); + my ($firmware,$model,$vendor) = ('','',''); + foreach (@temp){ + if (/Vendor:\s*(.*)\s+Model:\s*(.*)\s+Rev:\s*(.*)/i){ + $vendor = $1; + $model = $2; + $firmware = $3; + } + if (/Type:/i){ + if (/Type:\s*Direct-Access/i){ + my @working = ({ + 'vendor' => $vendor, + 'model' => $model, + 'firmware' => $firmware, + }); + @scsi = (@scsi,@working); + } + else { + ($firmware,$model,$vendor) = ('','',''); + } + } + } + main::log_data('dump','@scsi', \@scsi) if $b_log; + eval $end if $b_log; + return @scsi; } -## create array of glx data -get_graphics_glx_data() -{ - eval $LOGFS - local a_temp='' - # if [[ $B_SHOW_DISPLAY_DATA == 'true' && $B_ROOT != 'true' ]];then - if [[ $B_SHOW_DISPLAY_DATA == 'true' ]];then - IFS='^' - # NOTE: glxinfo -B is not always available, unforunately - A_GLX_DATA=( $( eval glxinfo $DISPLAY_OPT 2>/dev/null | gawk -F ': ' ' - # handle > 1 cases of detections - function join( arr, sep ) { - s="" - i=flag=0 - for ( i in arr ) { - if ( flag++ ) { - s = s sep - } - s = s i - } - return s - } - - BEGIN { - IGNORECASE=1 - compatVersion="" - # create empty arrays - split("", a) - split("", b) - split("", c) - split("", d) - } - /opengl renderer/ { - gsub(/'"$BAN_LIST_NORMAL"'/, "", $2) - gsub(/ [ \t]+/, " ", $2) # get rid of the created white spaces - gsub(/^ +| +$/, "", $2) - if ( $2 ~ /mesa/ ) { - # Allow all mesas -# if ( $2 ~ / r[3-9][0-9][0-9] / ) { - a[$2] - # this counter failed in one case, a bug, and is not needed now -# f++ -# } - next +sub disk_serial_by_id { + eval $start if $b_log; + my ($device) = @_; + my ($serial) = (''); + foreach (@by_id){ + if ($device eq Cwd::abs_path($_)){ + my @data = split /_/, $_; + $serial = $data[-1]; + $serial =~ s/-[0-9]+:[0-9]+$//; + # print $device, ' ', Cwd::abs_path($_),' ', $serial,"\n"; + last; + } + } + eval $end if $b_log; + return $serial; +} +# Normally hddtemp requires root, but you can set user rights in /etc/sudoers. +# args: $1 - /dev/<disk> to be tested for +sub hdd_temp { + eval $start if $b_log; + my ($device) = @_; + my ($path) = (''); + my (@data,$hdd_temp); + if (!$b_sudo){ + $b_sudo = 1; + if (!$b_root && ($path = main::check_program('sudo') )) { + $sudo = "$path -n "; + } + } + if ($device =~ /nvme/i){ + if (!$b_nvme){ + $b_nvme = 1; + if ($path = main::check_program('nvme')) { + $nvme = $path; } - - $2 && a[$2] - } - # dropping all conditions from this test to just show full mesa information - # there is a user case where not f and mesa apply, atom mobo - # /opengl version/ && ( f || $2 !~ /mesa/ ) { - /opengl version/ { - # fglrx started appearing with this extra string, does not appear to communicate anything of value - sub(/(Compatibility Profile Context|\(Compatibility Profile\))/, "", $2 ) - gsub(/ [ \t]+/, " ", $2) # get rid of the created white spaces - gsub(/^ +| +$/, "", $2) - $2 && b[$2] - # note: this is going to be off if ever multi opengl versions appear, never seen one - compatVersion=gensub(/^([^ \t]+)[ \t].*/,"\\1","g",$2) - } - /opengl core profile version/ { - gsub(/'"$BAN_LIST_NORMAL"'/, "", $2) - # fglrx started appearing with this extra string, does not appear to communicate anything of value - sub(/(Core Profile Context|\(Core Profile\))/, "", $2 ) - gsub(/ [ \t]+/, " ", $2) # get rid of the created white spaces - gsub(/^ +| +$/, "", $2) - $2 && d[$2] - } - /direct rendering/ { - $2 && c[$2] - } - # if -B was always available, we could skip this, but it is not - /GLX Visuals/ { - exit - } - END { - dr = join( c, ", " ) - oglr = join( a, ", " ) - oglv = join( b, ", " ) - oglcpv = join( d, ", " ) - # output processing done in print functions, important! do not use \n IFS - # because Bash treats an empty value surrounded by \n\n as nothing, not an empty array key! - # this came up re oglcpv being empty and compatVersion being used instead - printf( "%s^%s^%s^%s^%s", oglr, oglv, dr, oglcpv, compatVersion ) - }' ) ) - IFS="$ORIGINAL_IFS" - - # GLXR=$(glxinfo | gawk -F ': ' 'BEGIN {IGNORECASE=1} /opengl renderer/ && $2 !~ /mesa/ {seen[$2]++} END {for (i in seen) {printf("%s ",i)}}') - # GLXV=$(glxinfo | gawk -F ': ' 'BEGIN {IGNORECASE=1} /opengl version/ && $2 !~ /mesa/ {seen[$2]++} END {for (i in seen) {printf("%s ",i)}}') - fi - a_temp=${A_GLX_DATA[@]} - log_function_data "A_GLX_DATA: $a_temp" - eval $LOGFE -} - -## return screen resolution / tty resolution -## args: $1 - reg/tty -get_graphics_res_data() -{ - eval $LOGFS - local screen_resolution='' xdpy_data='' screens_count=0 tty_session='' option=$1 - - # if [[ $B_SHOW_DISPLAY_DATA == 'true' && $B_ROOT != 'true' ]];then - if [[ $B_SHOW_DISPLAY_DATA == 'true' && $option != 'tty' ]];then - # Added the two ?'s , because the resolution is now reported without spaces around the 'x', as in - # 1400x1050 instead of 1400 x 1050. Change as of X.org version 1.3.0 - xdpy_data="$( xdpyinfo $DISPLAY_OPT 2>/dev/null )" - xdpy_count=$( grep -c 'dimensions' <<< "$xdpy_data" ) - # we get a bit more info from xrandr than xdpyinfo, but xrandr fails to handle - # multiple screens from different video cards - if [[ $xdpy_count -eq 1 ]];then - screen_resolution=$( xrandr $DISPLAY_OPT 2>/dev/null | gawk ' - /\*/ { - res[++m] = gensub(/^.* ([0-9]+) ?x ?([0-9]+)[_ ].* ([0-9\.]+)\*.*$/,"\\1x\\2@\\3hz","g",$0) - } - END { - for (n in res) { - if (res[n] ~ /^[[:digit:]]+x[[:digit:]]+/) { - line = line ? line ", " res[n] : res[n] - } + } + if ($nvme){ + $device =~ s/n[0-9]//; + @data = main::grabber("$sudo$nvme smart-log $device 2>/dev/null"); + foreach (@data){ + my @row = split /\s*:\s*/, $_; + # other rows may have: Temperature sensor 1 : + if ( $row[0] eq 'temperature') { + $row[1] =~ s/\s*C//; + $hdd_temp = $row[1]; + last; } - if (line) { - print(line) - } - }' ) - fi - if [[ -z $screen_resolution || $xdpy_count -gt 1 ]];then - screen_resolution=$( gawk ' - BEGIN { - IGNORECASE=1 - screens = "" - separator = "" - } - /dimensions/ { - screens = screens separator # first time, this is null, next, has comma last - screens = screens $2 # then tack on the new value for nice comma list - separator = ", " - } - END { - print screens - }' <<< "$xdpy_data" ) - fi - else - if [[ $B_PROC_DIR == 'true' && -z $BSD_TYPE ]];then - screen_resolution=$( stty -F $( readlink /proc/$PPID/fd/0 ) size | gawk '{ - print $2"x"$1 - }' ) - # really old systems may not support the above method - if [[ -z $screen_resolution ]];then - screen_resolution=$( stty -a | gawk -F ';' ' - /^speed/ { - gsub(/[[:space:]]*(rows|columns)[[:space:]]*/,"",$0) - gsub(/[[:space:]]*/,"",$2) - gsub(/[[:space:]]*/,"",$3) - print $3"x"$2 - }' ) - fi - # note: this works fine for all systems but keeping the above for now since - # the above is probably more accurate for linux systems. - else - if [[ $B_CONSOLE_IRC != 'true' ]];then - screen_resolution=$( stty -a | gawk -F ';' ' - /^speed/ { - gsub(/[[:space:]]*(rows|columns)[[:space:]]*/,"",$0) - gsub(/[[:space:]]*/,"",$2) - gsub(/[[:space:]]*/,"",$3) - print $3"x"$2 - }' ) - else - if [[ -n $BSD_TYPE ]];then - tty_session=$( get_tty_console_irc ) - # getting information for tty that owns the irc client - screen_resolution="$( stty -f /dev/pts/$tty_session size | gawk '{print $2"x"$1}' )" - fi - fi - fi - fi - echo "$screen_resolution" - log_function_data "screen_resolution: $screen_resolution" - eval $LOGFE -} - -## create array of display server vendor/version data -get_graphics_display_server_data() -{ - eval $LOGFS - local vendor='' vendor_version='' a_temp='' xdpy_info='' a_display_vendor_working='' - # note: this may not always be set, it won't be out of X, for example - local server="$XDG_SESSION_TYPE" compositor='' compositor_version='' - - if [[ $B_EXTRA_EXTRA_DATA == 'true' ]];then - compositor="$(get_graphics_display_compositor)" compositor_version='' - fi - - if [[ $server == '' ]];then - if [[ -n "$WAYLAND_DISPLAY" ]];then - server='wayland' - fi - fi - # if [[ $B_SHOW_DISPLAY_DATA == 'true' && $B_ROOT != 'true' ]];then - if [[ $B_SHOW_DISPLAY_DATA == 'true' ]];then - # X vendor and version detection. - # new method added since radeon and X.org and the disappearance of <X server name> version : ...etc - # Later on, the normal textual version string returned, e.g. like: X.Org version: 6.8.2 - # A failover mechanism is in place. (if $version is empty, the release number is parsed instead) - # xdpy_info="$( xdpyinfo )" - IFS="," - a_display_vendor_working=( $( xdpyinfo $DISPLAY_OPT 2>/dev/null | gawk -F': +' ' - BEGIN { - IGNORECASE=1 - vendorString="" - version="" - vendorRelease="" - } - /vendor string/ { - gsub(/\ythe\y|\yinc\y|foundation|project|corporation/, "", $2) - gsub(/'"$BAN_LIST_ARRAY"'/, " ", $2) - gsub(/^ +| +$/, "", $2) - gsub(/ [ \t]+/, " ", $2) - vendorString = $2 - } - /version:/ { - version = $NF - } - /vendor release number/ { - gsub(/0+$/, "", $2) - gsub(/0+/, ".", $2) - vendorRelease = $2 - } - /(supported pixmap|keycode range|number of extensions|^screen)/ { - exit # we are done with the info we want, no reason to read the rest - } - END { - print vendorString "," version "," vendorRelease - }' ) ) - vendor=${a_display_vendor_working[0]} - vendor_version=${a_display_vendor_working[1]} - - # this gives better output than the failure last case, which would only show: - # for example: X.org: 1.9 instead of: X.org: 1.9.0 - if [[ -z $vendor_version ]];then - vendor_version=$( get_graphics_display_x_version ) - fi - if [[ -z $vendor_version ]];then - vendor_version=${a_display_vendor_working[2]} - fi - - # some distros, like fedora, report themselves as the xorg vendor, so quick check - # here to make sure the vendor string includes Xorg in string - if [[ -z $( grep -E '(X|xorg|x\.org)' <<< $vendor ) ]];then - vendor="$vendor X.org" - fi - IFS="$ORIGINAL_IFS" - A_DISPLAY_SERVER_DATA[0]="$vendor" - A_DISPLAY_SERVER_DATA[1]="$vendor_version" - A_DISPLAY_SERVER_DATA[2]="$server" - A_DISPLAY_SERVER_DATA[3]="$compositor" - A_DISPLAY_SERVER_DATA[4]="$compositor_version" - else - vendor_version=$( get_graphics_display_x_version ) - if [[ -n $vendor_version ]];then - vendor='X.org' - A_DISPLAY_SERVER_DATA[0]="$vendor" - A_DISPLAY_SERVER_DATA[1]="$vendor_version" - A_DISPLAY_SERVER_DATA[2]="$server" - A_DISPLAY_SERVER_DATA[3]="$compositor" - A_DISPLAY_SERVER_DATA[4]="$compositor_version" - fi - fi - a_temp=${A_DISPLAY_SERVER_DATA[@]} - log_function_data "A_DISPLAY_SERVER_DATA: $a_temp" - eval $LOGFE -} - -get_graphics_display_compositor() -{ - eval $LOGFS - local compositor='' - - if [[ -z "${Ps_aux_Data/*mutter*/}" ]];then - compositor='mutter' - elif [[ -z "${Ps_aux_Data/*gnome-shell*/}" ]];then - compositor='gnome-shell' - elif [[ -z "${Ps_aux_Data/*kwin*/}" ]];then - compositor='kwin' - elif [[ -z "${Ps_aux_Data/*moblin*/}" ]];then - compositor='moblin' - elif [[ -z "${Ps_aux_Data/*kmscon*/}" ]];then - compositor='kmscon' - elif [[ -z "${Ps_aux_Data/*sway*/}" ]];then - compositor='sway' - elif [[ -z "${Ps_aux_Data/*grefson*/}" ]];then - compositor='grefson' - elif [[ -z "${Ps_aux_Data/*westford*/}" ]];then - compositor='westford' - elif [[ -z "${Ps_aux_Data/*rustland*/}" ]];then - compositor='rustland' - elif [[ -z "${Ps_aux_Data/*fireplace*/}" ]];then - compositor='fireplace' - elif [[ -z "${Ps_aux_Data/*wayhouse*/}" ]];then - compositor='wayhouse' - elif [[ -z "${Ps_aux_Data/*weston*/}" ]];then - compositor='weston' - elif [[ -z "${Ps_aux_Data/*compton*/}" ]];then - compositor='compton' - elif [[ -z "${Ps_aux_Data/*compiz*/}" ]];then - compositor='compiz' - elif [[ -z "${Ps_aux_Data/*swc*/}" ]];then - compositor='swc' - elif [[ -z "${Ps_aux_Data/*dwc*/}" ]];then - compositor='dwc' - fi - - log_function_data "compositor: $compositor" - echo $compositor - eval $LOGFE + } + } + } + else { + if (!$b_hddtemp){ + $b_hddtemp = 1; + if ($path = main::check_program('hddtemp')) { + $hddtemp = $path; + } + } + if ($hddtemp){ + $hdd_temp = (main::grabber("$sudo$hddtemp -nq -u C $device 2>/dev/null"))[0]; + } + } + eval $end if $b_log; + return $hdd_temp; +} +# gptid/c5e940f1-5ce2-11e6-9eeb-d05099ac4dc2 N/A ada0p1 +sub match_glabel { + eval $start if $b_log; + my ($gptid) = @_; + return if !@glabel || ! $gptid; + #$gptid =~ s/s[0-9]+$//; + my ($dev_id) = (''); + foreach (@glabel){ + my @temp = split /\s+/, $_; + my $gptid_trimmed = $gptid; + # slice off s[0-9] from end in case they use slice syntax + $gptid_trimmed =~ s/s[0-9]+$//; + if (defined $temp[0] && ($temp[0] eq $gptid || $temp[0] eq $gptid_trimmed ) ){ + $dev_id = $temp[2]; + last; + } + } + $dev_id ||= $gptid; # no match? return full string + eval $end if $b_log; + return $dev_id; +} +sub set_glabel { + eval $start if $b_log; + $b_glabel = 1; + if (my $path = main::check_program('glabel')){ + @glabel = main::grabber("$path status 2>/dev/null"); + } + main::log_data('dump','@glabel:with Headers',\@glabel) if $b_log; + # get rid of first header line + shift @glabel; + eval $end if $b_log; } - -## args: $1 - compositor -get_graphics_display_wayland_version() -{ - eval $LOGFS - - local version='' - - case $1 in - mutter) - : - ;; - esac - log_function_data "version: $version" - echo $version - - eval $LOGFE } -# if other tests fail, try this one, this works for root, out of X also -get_graphics_display_x_version() -{ - eval $LOGFS - local version='' x_data='' - # note that some users can have /usr/bin/Xorg but not /usr/bin/X - if type -p X &>/dev/null;then - # note: MUST be this syntax: X -version 2>&1 - # otherwise X -version overrides everything and this comes out null. - # two knowns id strings: X.Org X Server 1.7.5 AND X Window System Version 1.7.5 - #X -version 2>&1 | gawk '/^X Window System Version/ { print $5 }' - x_data="$( X -version 2>&1 )" - elif type -p Xorg &>/dev/null;then - x_data="$( Xorg -version 2>&1)" - fi - if [[ -n $x_data ]];then - version=$( - gawk ' - BEGIN { - IGNORECASE=1 - } - /^x.org x server/ { - print $4 - exit - } - /^X Window System Version/ { - print $5 - exit - }' <<< "$x_data" ) - fi - echo $version - log_function_data " version: $version" - eval $LOGFE -} - -# this gets just the raw data, total space/percent used and disk/name/per disk capacity -get_hdd_data_basic() +## GraphicData { - eval $LOGFS - local hdd_used='' a_temp='' df_string='' - local hdd_data='' df_test='' swap_size=0 - - if [[ -z $BSD_TYPE ]];then - ## NOTE: older df do not have --total (eg: v: 6.10 2008) - ## keep in mind the only value of use with --total is 'used' in blocks, which - ## we can use later to calculate the real percentags based on disk sizes, not - ## mounted partitions. Not using --total because it's more reliable to exclude non /dev - df_string="df -P -T --exclude-type=aufs --exclude-type=devfs --exclude-type=devtmpfs - --exclude-type=fdescfs --exclude-type=iso9660 --exclude-type=linprocfs --exclude-type=nfs - --exclude-type=nfs3 --exclude-type=nfs4 --exclude-type=nfs5 --exclude-type=procfs --exclude-type=smbfs - --exclude-type=squashfs --exclude-type=sysfs --exclude-type=tmpfs --exclude-type=unionfs" - if swapon -s &>/dev/null;then - swap_size=$( swapon -s 2>/dev/null | gawk ' - BEGIN { - swapSize=0 - total=0 - } - ( $2 == "partition" ) && ( $3 ~ /^[0-9]+$/ ) { - total += ( 1000 / 1024 ) * $3 - } - END { - # result in kB, change to 1024 Byte blocks - total = total * 1000 / 1024 - total = sprintf( "%.1f", total ) - print total - }' ) - fi - else - # default size is 512, , so use -k for 1024 -H only for size in human readable format - # older bsds don't support -T, pain, so we'll use partial output there - if df -k -T &>/dev/null;then - df_string='df -k -T' - else - df_string='df -k' - fi - if swapctl -l -k &>/dev/null;then - swap_size=$( swapctl -l -k 2>/dev/null | gawk ' -BEGIN { -swapSize=0 -total=0 -} -( $1 ~ /^\/dev/ ) && ( $2 ~ /^[0-9]+$/ ) { - total += $2 -} -END { - # result in blocks already - print total -}' ) - fi - fi - # echo ss: $swap_size - hdd_data="$( eval $df_string )" - - # eval $df_string | awk 'BEGIN{tot=0} !/total/ {tot+=$4} END{print tot}' - log_function_data 'raw' "hdd_data:\n$hdd_data" - hdd_used=$( echo "$hdd_data" | gawk -v bsdType="$BSD_TYPE" -v swapSize="$swap_size" ' - BEGIN { - # this is used for specific cases where bind, or incorrect multiple mounts to same partitions, - # is present. The value is searched for an earlier appearance of that partition and if it is - # present, the data is not added into the partition used size. - partitionsSet="" - # this handles a case where the same dev item is mounted twice to different points - devSet="" - devWorking="" - mountWorking="" - used=0 - } - # using $1, not $2, because older bsd df do not have -T, filesystem type - ( bsdType != "" ) && $1 ~ /^(aufs|devfs|devtmpfs|fdescfs|filesystem|iso9660|linprocfs|nfs|nfs3|nfs4|nfs5|procfs|squashfs|smbfs|sysfs|tmpfs|type|unionfs)$/ { - # note use next, not getline or it does not work right - next - } - # also handles odd dm-1 type, from lvm, and mdraid, and some other bsd partition syntax - # note that linux 3.2.45-grsec-9th types kernels have this type of partition name: /dev/xvdc (no number, letter) - # note: btrfs does not seem to use partition integers, just the primary /dev/sdx identifier - # df can also show /dev/disk/(by-label|by-uuid etc) - /^\/dev\/(disk\/|mapper\/|[hsv]d[a-z]+[0-9]*|dm[-]?[0-9]+|(ada|mmcblk|nvme[0-9]+n)[0-9]+p[0-9]+.*|(ad|sd|wd)[0-9]+[a-z]|md[0-9]+|[aw]d[0-9]+s.*|xvd[a-z]+)|^ROOT/ { - # this handles the case where the first item is too long - # and makes df wrap output to next line, so here we advance - # it to the next line for that single case. Using df -P should - # make this unneeded but leave it in just in case - if ( NF < 6 && $0 !~ /.*%/ ) { - devSet = devSet "~" $1 "~" - getline - } - # if the first item caused a wrap, use one less than standard - # testing for the field with % in it, ie: 34%, then go down from there - # this also protects against cases where the mount point has a space in the - # file name, thus breaking going down from $NF directly. - # some bsds will also have only 6 items - if ( $5 ~ /.*%/ ) { - devWorking="~" $1 "~" - mountWorking="~" $6 "~" - if ( partitionsSet !~ mountWorking && devSet !~ devWorking ) { - used += $3 - } - partitionsSet = partitionsSet mountWorking - # make sure to only include bsd real lines here, ie, short df output - if ( $1 ~ /^\/dev\// ) { - devSet = devSet devWorking - } - } - # otherwise use standard - else if ( $6 ~ /.*%/ ) { - devWorking="~" $1 "~" - mountWorking="~" $7 "~" - if ( partitionsSet !~ mountWorking && devSet !~ devWorking ) { - used += $4 - } - partitionsSet = partitionsSet mountWorking - devSet = devSet devWorking - } - # and if this is not detected, give up, we need user data to debug +package GraphicData; +my $driver = ''; # we need this as a fallback in case no xorg.0.log +sub get { + eval $start if $b_log; + my (@data,@rows); + my $num = 0; + if ($b_arm){ + my $key = 'ARM'; + @data = ({ + main::key($num++,$key) => main::row_defaults('arm-pci',''), + },); + @rows = (@rows,@data); + } + else { + @data = card_data(); + @rows = (@rows,@data); + if (!@rows){ + my $key = 'Message'; + @data = ({ + main::key($num++,$key) => main::row_defaults('pci-card-data',''), + },); + @rows = (@rows,@data); + } + } + @data = display_data(); + @rows = (@rows,@data); + @data = gl_data(); + @rows = (@rows,@data); + eval $end if $b_log; + return @rows; +} +# 0 type +# 1 type_id +# 2 bus_id +# 3 sub_id +# 4 device +# 5 vendor_id +# 6 chip_id +# 7 rev +# 8 port +# 9 driver +# 10 modules +# not using 3D controller yet, needs research: |3D controller |display controller +# note: this is strange, but all of these can be either a separate or the same +# card. However, by comparing bus id, say: 00:02.0 we can determine that the +# cards are either the same or different. We want only the .0 version as a valid +# card. .1 would be for example: Display Adapter with bus id x:xx.1, not the right one +sub card_data { + eval $start if $b_log; + my (@rows,@data); + my ($j,$num) = (0,1); + foreach (@pci){ + $num = 1; + my @row = @$_; + #print "$row[0] $row[3]\n"; + if ($row[3] == 0 && ( $row[0] eq 'vga' || $row[0] eq 'display' || $row[0] eq '3d' ) ){ + #print "$row[0] $row[3]\n"; + $j = scalar @rows; + $driver = $row[9]; + $driver ||= 'N/A'; + my $card = main::trimmer($row[4]); + $card = ($card) ? main::pci_cleaner($card,'output') : 'N/A'; + #$card ||= 'N/A'; + # have seen absurdly verbose card descriptions, with non related data etc + if (length($card) > 85 || $size{'max'} < 110){ + $card = main::pci_long_filter($card); + } + @data = ( + { + main::key($num++,'Card') => $card, + main::key($num++,'driver') => $driver, + }, + ); + @rows = (@rows,@data); + if ($row[9] && !$bsd_type){ + my $version = main::get_module_version($row[9]); + $version ||= 'N/A'; + $rows[$j]{main::key($num++,'v')} = $version; + } + if ($extra > 0){ + $rows[$j]{main::key($num++,'bus ID')} = "$row[2].$row[3]"; + } + if ($extra > 1){ + $rows[$j]{main::key($num++,'chip ID')} = "$row[5]:$row[6]"; + } + } + #print "$row[0]\n"; + } + #my $ref = $pci[-1]; + #print $$ref[0],"\n"; + eval $end if $b_log; + return @rows; +} +sub display_data(){ + eval $start if $b_log; + my (%graphics,@row); + my @xdpyinfo; + my $num = 0; + my ($protocol,$server) = ('',''); + # note: these may not always be set, they won't be out of X, for example + $protocol = $ENV{'XDG_SESSION_TYPE'} if $ENV{'XDG_SESSION_TYPE'}; + $protocol = $ENV{'WAYLAND_DISPLAY'} if (!$protocol && $ENV{'WAYLAND_DISPLAY'}); + # need to confirm that there's a point to this test, I believe no, fails out of x + # loginctl also results in the session id + if (!$protocol && $b_display && $b_force_display){ + if (my $program = main::check_program('loginctl')){ + my $id = ''; + # $id = $ENV{'XDG_SESSION_ID'}; # returns tty session in console + my @data = main::grabber("$program --no-pager --no-legend 2>/dev/null",'','strip'); + foreach (@data){ + next if /tty[0-6]$/; + $id = (split /\s+/, $_)[0]; + last; # multiuser? too bad, we'll go for the first one + } + if ($id ){ + my $temp = (main::grabber("$program show-session $id -p Type --no-pager --no-legend 2>/dev/null"))[0]; + $temp =~ s/Type=// if $temp; + # ssh will not show /dev/ttyx so would have passed the first test + $protocol = $temp if $temp && $temp ne 'tty'; + } + } + } + if ($extra > 1){ + # initial tests, if wayland, it is certainly a compositor + $protocol = lc($protocol) if $protocol; + $graphics{'compositor'} = display_compositor($protocol); + } + if ( $b_display){ + # X vendor and version detection. + # new method added since radeon and X.org and the disappearance of + # <X server name> version : ...etc. Later on, the normal textual version string + # returned, e.g. like: X.Org version: 6.8.2 + # A failover mechanism is in place: if $version empty, release number parsed instead + if (my $program = main::check_program('xdpyinfo')){ + my @xdpyinfo = main::grabber("$program $display_opt 2>/dev/null","\n",'strip'); + #@xdpyinfo = map {s/^\s+//;$_} @xdpyinfo if @xdpyinfo; + #print join "\n",@xdpyinfo, "\n"; + foreach (@xdpyinfo){ + my @working = split /:\s+/, $_; + next if ( ($graphics{'dimensions'} && $working[0] ne 'dimensions' ) || !$working[0] ); + #print "$_\n"; + if ($working[0] eq 'vendor string'){ + $working[1] =~ s/The\s|\sFoundation//g; + # some distros, like fedora, report themselves as the xorg vendor, + # so quick check here to make sure the vendor string includes Xorg in string + if ($working[1] !~ /x/i){ + $working[1] .= ' X.org'; + } + $graphics{'vendor'} = $working[1]; + } + elsif ($working[0] eq 'version number'){ + $graphics{'version-id'} = $working[1]; + } + elsif ($working[0] eq 'vendor release number'){ + $graphics{'vendor-release'} = $working[1]; + } + elsif ($working[0] eq 'X.Org version'){ + $graphics{'xorg-version'} = $working[1]; + } + elsif ($working[0] eq 'dimensions'){ + $working[1] =~ s/\spixels//; + $working[1] =~ s/\smillimeters/ mm/; + if ($graphics{'dimensions'}){ + $graphics{'dimensions'} = ([@{$graphics{'dimensions'}},$working[1]]); + } + else { + $graphics{'dimensions'} = ([$working[1]]); + } + } + } + #$graphics{'dimensions'} = (\@dimensions); + # we get a bit more info from xrandr than xdpyinfo, but xrandr fails to handle + # multiple screens from different video cards + my $ref = $graphics{'dimensions'}; + if (defined $ref){ + my @screens = @$ref; + if (scalar @screens == 1){ + if (my $program = main::check_program('xrandr')){ + my @xrandr = main::grabber("$program $display_opt 2>/dev/null",'','strip'); + foreach (@xrandr){ + my @working = split /\s+/,$_; + print join "$_\n"; + if ($working[1] =~ /\*/){ + $working[1] =~ s/\*|\+//g; + $working[1] = sprintf("%0.0f",$working[1]); + my $screen = "$working[0]~$working[1]Hz"; + if ($graphics{'screens'}){ + $graphics{'screens'} = ([@{$graphics{'screens'}},$screen]); + } + else { + $graphics{'screens'} = ([$screen]); + } + } + } + + } + } + } + else { + $graphics{'tty'} = tty_data(); + } + } else { - next - } - } - END { - used=used + swapSize - used = sprintf( "%.1f", used ) - print used - }' ) - # echo hdu:$hdd_used - if [[ -z $hdd_used ]];then - hdd_used='na' - fi - log_function_data "hdd_used: $hdd_used" - # create the initial array strings: - # disk-dev, capacity, name, usb or not - # final item is the total of the disk - IFS=$'\n' - - if [[ $B_PARTITIONS_FILE == 'true' ]];then - A_HDD_DATA=( $( - gawk -v hddUsed=$hdd_used ' - /([hsv]d[a-z]+|(ada|mmcblk|nvme[0-9]+n)[0-9]+)$/ { - driveSize = $(NF - 1)*1024/1000**3 - gsub(/'"$BAN_LIST_ARRAY"'/, " ", driveSize) - gsub(/^ +| +$/, "", driveSize) - printf( $NF",%.1fGB,,\n", driveSize ) + $graphics{'screens'} = ([main::row_defaults('xdpyinfo-missing')]); } - # See http://lanana.org/docs/device-list/devices-2.6+.txt for major numbers used below - # See https://www.mjmwired.net/kernel/Documentation/devices.txt for kernel 4.x device numbers - # $1 ~ /^(3|22|33|8)$/ && $2 % 16 == 0 { - # size += $3 - # } - # special case from this data: 8 0 156290904 sda - # note: known starters: vm: 252/253/254; grsec: 202; nvme: 259 - $1 ~ /^(3|8|22|33|202|252|253|254|259)$/ && $NF ~ /(nvme[0-9]+n[0-9]+|[hsv]d[a-z]+)$/ && ( $2 % 16 == 0 || $2 % 16 == 8 ) { - size += $3 - } - END { - size = size*1024/1000**3 # calculate size in GB size - workingUsed = hddUsed*1024/1000**3 # calculate workingUsed in GB used - # this handles a special case with livecds where no hdd_used is detected - if ( size > 0 && hddUsed == "na" ) { - size = sprintf( "%.1f", size ) - print size "GB,-,,.." - } - else if ( size > 0 && workingUsed > 0 ) { - diskUsed = workingUsed*100/size # calculate used percentage - diskUsed = sprintf( "%.1f", diskUsed ) - if ( int(diskUsed) > 100 ) { - diskUsed = "Used Error!" + } + else { + $graphics{'tty'} = tty_data(); + } + # this gives better output than the failure last case, which would only show: + # for example: X.org: 1.9 instead of: X.org: 1.9.0 + $graphics{'version'} = $graphics{'xorg-version'} if $graphics{'xorg-version'};; + $graphics{'version'} = x_version() if !$graphics{'version'}; + $graphics{'version'} = $graphics{'version-id'} if !$graphics{'version'}; + + undef @xdpyinfo; + #print Data::Dumper::Dumper \%graphics; + if (%graphics){ + my $resolution = ''; + my $server_string = ''; + if ($graphics{'vendor'}){ + my $version = ($graphics{'version'}) ? " $graphics{'version'}" : ''; + $server_string = "$graphics{'vendor'}$version"; + } + elsif ($graphics{'version'}) { + $server_string = "X.org $graphics{'version'}"; + } + if ($graphics{'screens'}){ + my $ref = $graphics{'screens'}; + my @screens = @$ref; + my $sep = ''; + foreach (@screens){ + $resolution .= $sep . $_; + $sep = ', '; + } + } + my @drivers = x_drivers(); + if (!$protocol && !$server_string && !$graphics{'vendor'} && !@drivers){ + $server_string = main::row_defaults('display-server'); + @row = ({ + main::key($num++,'Display') => '', + main::key($num++,'server') => $server_string, + }); + } + else { + $server_string ||= 'N/A'; + # note: if no xorg log, and if wayland, there will be no xorg drivers, + # obviously, so we use the last driver found on the card section in that case. + # those come from lscpi kernel drivers so there should be no xorg/wayland issues. + $driver = ($drivers[0]) ? $drivers[0]: $driver; + @row = ({ + main::key($num++,'Display') => $protocol, + main::key($num++,'server') => $server_string, + main::key($num++,'driver') => $driver, + }); + if ($drivers[2]){ + $row[0]{main::key($num++,'FAILED')} = $drivers[2]; + } + if ($drivers[1]){ + $row[0]{main::key($num++,'unloaded')} = $drivers[1]; + } + if ($graphics{'compositor'}){ + $row[0]{main::key($num++,'compositor')} = $graphics{'compositor'}; + } + } + if ($resolution){ + $row[0]{main::key($num++,'resolution')} = $resolution; + } + else { + $graphics{'tty'} ||= 'N/A'; + $row[0]{main::key($num++,'tty')} = $graphics{'tty'}; + } + } + eval $end if $b_log; + return @row; +} +sub gl_data(){ + eval $start if $b_log; + my $num = 0; + my (@row,$arg); + #print ("$b_display : $b_root\n"); + if ( $b_display){ + if (my $program = main::check_program('glxinfo')){ + # NOTE: glxinfo -B is not always available, unforunately + my @glxinfo = main::grabber("$program $display_opt 2>/dev/null"); + if (!@glxinfo){ + my $type = 'display-console'; + if ($b_root){ + $type = 'display-root-x'; } else { - diskUsed = diskUsed "% used" + $type = 'display-null'; } - size = sprintf( "%.1f", size ) - print size "GB," diskUsed ",,.." + @row = ({ + main::key($num++,'Message') => main::row_defaults($type), + }); + return @row; + } + #print join "\n",@glxinfo,"\n"; + my $compat_version = ''; + my ($b_compat,@core_profile_version,@direct_render,@renderer,@opengl_version,@working); + foreach (@glxinfo){ + next if /^\s/; + if (/^opengl renderer/i){ + @working = split /:\s*/, $_; + $working[1] = main::cleaner($working[1]); + # Allow all mesas + #if ($working[1] =~ /mesa/i){ + # + #} + push @renderer, $working[1]; + } + # dropping all conditions from this test to just show full mesa information + # there is a user case where not f and mesa apply, atom mobo + # /opengl version/ && ( f || $2 !~ /mesa/ ) { + elsif (/^opengl version/i){ + # fglrx started appearing with this extra string, does not appear + # to communicate anything of value + @working = split /:\s*/, $_; + $working[1] =~ s/(Compatibility Profile Context|\(Compatibility Profile\))//; + $working[1] =~ s/\s\s/ /g; + $working[1] =~ s/^\s+|\s+$//; + push @opengl_version, $working[1]; + # note: this is going to be off if ever multi opengl versions appear, never seen one + @working = split /\s+/, $working[1]; + $compat_version = $working[0]; + } + elsif (/^opengl core profile version/i){ + # fglrx started appearing with this extra string, does not appear + # to communicate anything of value + @working = split /:\s*/, $_; + $working[1] =~ s/(Compatibility Profile Context|\((Compatibility|Core) Profile\))//; + $working[1] =~ s/\s\s/ /g; + $working[1] =~ s/^\s+|\s+$//; + push @core_profile_version, $working[1]; + } + elsif (/direct rendering/){ + @working = split /:\s*/, $_; + push @direct_render, $working[1]; + } + # if -B was always available, we could skip this, but it is not + elsif (/GLX Visuals/){ + last; + } + } + my ($direct_render,$renderer,$version) = ('N/A','N/A','N/A'); + $direct_render = join ', ', @direct_render if @direct_render; + # non free drivers once filtered and cleaned show the same for core and compat + # but this stopped for some reason at 4.5/4.6 nvidia + if (@core_profile_version && @opengl_version && + join ('', @core_profile_version) ne join( '', @opengl_version) && + !(grep {/nvidia/i} @opengl_version ) ){ + @opengl_version = @core_profile_version; + $b_compat = 1; + } + $version = join ', ', @opengl_version if @opengl_version; + $renderer = join ', ', @renderer if @renderer; + @row = ({ + main::key($num++,'OpenGL') => '', + main::key($num++,'renderer') => $renderer, + main::key($num++,'v') => $version, + }); + + if ($b_compat && $extra > 1 && $compat_version){ + $row[0]{main::key($num++,'compat-v')} = $compat_version; + } + if ($extra > 0){ + $row[0]{main::key($num++,'direct render')} = $direct_render; + } + } + else { + @row = ({ + main::key($num++,'Message') => main::row_defaults('glxinfo-missing'), + }); + } + } + else { + my $type = 'display-console'; + if (!main::check_program('glxinfo')){ + $type = 'glxinfo-missing'; + } + else { + if ($b_root){ + $type = 'display-root'; } else { - print "NA,-,,.." # print an empty array, this will be further handled in the print out function - } - }' $FILE_PARTITIONS ) ) - log_function_data 'cat' "$FILE_PARTITIONS" - else - if [[ -n $BSD_TYPE ]];then - get_hard_drive_data_bsd "$hdd_used" - fi - fi - IFS="$ORIGINAL_IFS" - a_temp=${A_HDD_DATA[@]} - # echo ${a_temp[@]} - log_function_data "A_HDD_DATA: $a_temp" - eval $LOGFE -} - -## fills out the A_HDD_DATA array with disk names -get_hard_drive_data_advanced() -{ - eval $LOGFS - local a_temp_working='' a_temp_scsi='' temp_holder='' temp_name='' i='' j='' - local sd_ls_by_id='' ls_disk_by_id='' ls_disk_by_path='' usb_exists='' a_temp='' - local firewire_exists='' thunderbolt_exists='' thunderbolt_exists='' hdd_temp hdd_serial='' - local firmware_rev='' working_path='' block_type='' + $type = 'display-try'; + } + } + @row = ({ + main::key($num++,'Message') => main::row_defaults($type), + }); + } + eval $end if $b_log; + return @row; +} +sub tty_data(){ + eval $start if $b_log; + my ($tty); + if ($size{'term-cols'}){ + $tty = "$size{'term-cols'}x$size{'term-lines'}"; + } + elsif ($b_irc && $client{'console-irc'}){ + my $tty_working = main::get_tty_console_irc(); + if (my $program = main::check_program('stty')){ + my $tty_arg = ($bsd_type) ? '-f' : '-F'; + $tty = (main::grabber("$program $tty_arg /dev/pts/$tty_working size 2>/dev/null"))[0]; + if ($tty){ + my @temp = split /\s+/, $tty; + $tty = "$temp[1]x$temp[0]"; + } + } + } + eval $end if $b_log; + return $tty; +} +sub x_drivers { + eval $start if $b_log; + my ($driver,@driver_data,,%drivers); + my ($failed,$loaded,$sep,$unloaded) = ('','','',''); + if (my $log = main::system_files('xorg-log')){ + my @xorg = main::reader($log); + # list is from sgfxi plus non-free drivers + my $list = 'amdgpu|apm|ark|ati|chips|cirrus|cyrix|fbdev|fglrx|glint|'; + $list .= 'i128|i740|i810|iftv|imstt|intel|ivtv|mach64|mesa|mga|modesetting|'; + $list .= 'neomagic|newport|nouveau|nsc|nvidia|nv|openchrome|radeonhd|radeon|'; + $list .= 'rendition|s3virge|s3|savage|siliconmotion|sisimedia|sisusb|sis|tdfx|'; + $list .= 'tga|trident|tseng|unichrome|v4l|vboxvideo|vesa|vga|via|vmware|voodoo'; + # it's much cheaper to grab the simple pattern match then do the expensive one + # in the main loop. + #@xorg = grep {/Failed|Unload|Loading/} @xorg; + foreach (@xorg){ + next if !/Failed|Unload|Loading/; + # note that in file names, driver is always lower case + if (/\sLoading.*($list)_drv.so$/i ) { + $driver=lc($1); + # we get all the actually loaded drivers first, we will use this to compare the + # failed/unloaded, which have not always actually been truly loaded + $drivers{$driver}="loaded"; + } + # openbsd uses UnloadModule: + elsif (/(Unloading\s|UnloadModule).*($list)(_drv.so)?$/i ) { + $driver=lc($2); + # we get all the actually loaded drivers first, we will use this to compare the + # failed/unloaded, which have not always actually been truly loaded + $drivers{$driver}="unloaded" ; + } + # verify that the driver actually started the desktop, even with false failed messages + # which can occur. This is the driver that is actually driving the display + elsif (/Failed.*($list)\"?(_drv.so)?$/i ) { + $driver=lc($1); + # we need to make sure that the driver has already been truly loaded, not just + # discussed, also set driver to lower case because sometimes it will show as + # RADEON or NVIDIA in the actual x start + if (exists $drivers{$driver}){ + $drivers{$driver}="failed"; + } + } + } + my $sep = ''; + foreach (sort keys %drivers){ + if ($drivers{$_} eq 'loaded') { + $sep = ($loaded) ? ',' : ''; + $loaded .= $sep . $_; + } + elsif ($drivers{$_} eq 'unloaded') { + $sep = ($unloaded) ? ',' : ''; + $unloaded .= $sep . $_; + } + elsif ($drivers{$_} eq 'failed') { + $sep = ($failed) ? ',' : ''; + $failed .= $sep . $_; + } + } + $loaded ||= 'none'; + @driver_data = ($loaded,$unloaded,$failed); + } + eval $end if $b_log; + return @driver_data; +} +sub x_version { + eval $start if $b_log; + my ($version,@data,$program); + # IMPORTANT: both commands send version data to stderr! + if ($program = main::check_program('Xorg')){ + @data = main::grabber("$program -version 2>&1"); + } + elsif ($program = main::check_program('X')){ + @data = main::grabber("$program -version 2>&1"); + } + #print Data::Dumper::Dumper \@data; + if (@data){ + foreach (@data){ + if (/^X.org X server/i){ + my @working = split /\s+/, $_; + $version = $working[3]; + last; + } + elsif (/^X Window System Version/i) { + my @working = split /\s+/, $_; + $version = $working[4]; + last; + } + } + } + eval $end if $b_log; + return $version; +} +# $1 - protocol: wayland|x11 +sub display_compositor { + eval $start if $b_log; + my ($protocol) = @_; + my ($compositor) = (''); + # NOTE: chains of greps of ps data are VERY expensive, so check for program presence + if (main::check_program('mutter') && (grep {/mutter/} @ps_cmd ) ) { + $compositor = 'mutter'; + } + elsif (main::check_program('kwin') && (grep {/\bkwin\b/} @ps_cmd ) ) { + $compositor = 'kwin'; + } + elsif (main::check_program('muffin') && (grep {/muffin/} @ps_cmd ) ) { + $compositor = 'muffin'; + } + # Note: other strings have gnome-shell in them, so important to get exact one. Since we + # can't fully trust how this will appear, the pattern should tighten it + elsif (main::check_program('gnome-shell') && (grep {/^(\/[\S]+\/)?gnome-shell(\s|$)/} @ps_cmd ) ) { + $compositor = 'gnome-shell'; + } + elsif (main::check_program('weston') && (grep {/weston/} @ps_cmd ) ) { + $compositor = 'weston'; + } + elsif (main::check_program('compton') && (grep {/compton/} @ps_cmd ) ) { + $compositor = 'compton'; + } + # owned by: compiz-core in debian + elsif (main::check_program('compiz') && (grep {/compiz/} @ps_cmd ) ) { + $compositor = 'compiz'; + } + # did not find in debian apt + elsif (main::check_program('moblin') && (grep {/moblin/} @ps_cmd ) ) { + $compositor = 'moblin'; + } + # did not find in debian apt + elsif (main::check_program('kmscon') && (grep {/kmscon/} @ps_cmd ) ) { + $compositor = 'kmscon'; + } + # did not find in debian apt + elsif (main::check_program('sway') && (grep {/\bsway\b/} @ps_cmd ) ) { + $compositor = 'sway'; + } + # did not find in debian apt + elsif (main::check_program('grefson') && (grep {/grefson/} @ps_cmd ) ) { + $compositor = 'grefson'; + } + # did not find in debian apt + elsif (main::check_program('westford') && (grep {/westford/} @ps_cmd ) ) { + $compositor = 'westford'; + } + # did not find in debian apt + elsif (main::check_program('rustland') && (grep {/rustland/} @ps_cmd ) ) { + $compositor = 'rustland'; + } + # did not find in debian apt + elsif (main::check_program('ireplace') && (grep {/fireplace/} @ps_cmd ) ) { + $compositor = 'fireplace'; + } + # did not find in debian apt + elsif (main::check_program('wayhouse') && (grep {/wayhouse/} @ps_cmd ) ) { + $compositor = 'wayhouse'; + } + # did not find in debian apt + elsif (main::check_program('swc') && (grep {/\bswc\b/} @ps_cmd ) ) { + $compositor = 'swc'; + } + # did not find in debian apt + elsif (main::check_program('dwc') && (grep {/\bdwc\b/} @ps_cmd ) ) { + $compositor = 'dwc'; + } + main::log_data('data',"compositor: $compositor") if $b_log; + eval $end if $b_log; + return $compositor; +} +} - ## check for all ide type drives, non libata, only do it if hdx is in array - ## this is now being updated for new /sys type paths, this may handle that ok too - if [[ -n $( grep -Es 'hd[a-z]' <<< ${A_HDD_DATA[@]} ) ]];then - # remember, we're using the last array item to store the total size of disks - for (( i=0; i < ${#A_HDD_DATA[@]} - 1; i++ )) - do - IFS="," - a_temp_working=( ${A_HDD_DATA[i]} ) - IFS="$ORIGINAL_IFS" - if [[ -z ${a_temp_working[0]/*hd[a-z]*/} ]];then - if [[ -e /proc/ide/${a_temp_working[0]}/model ]];then - a_temp_working[2]="$( remove_erroneous_chars /proc/ide/${a_temp_working[0]}/model )" - else - a_temp_working[2]='' - fi - # these loops are to easily extend the cpu array created in the gawk script above with more fields per cpu. - for (( j=0; j < ${#a_temp_working[@]}; j++ )) - do - if [[ $j -gt 0 ]];then - A_HDD_DATA[i]="${A_HDD_DATA[i]},${a_temp_working[$j]}" - else - A_HDD_DATA[i]="${a_temp_working[$j]}" - fi - done - fi - done - fi - - ## then handle libata names - # first get the ata device names, put them into an array - IFS=$'\n' - if [[ $B_SCSI_FILE == 'true' ]]; then - a_temp_scsi=( $( gawk ' - BEGIN { - IGNORECASE=1 - } - /host/ { - getline a[$0] - getline b[$0] - } - END { - for (i in a) { - if (b[i] ~ / *type: *direct-access.*/) { - #c=gensub(/^ *vendor: (.+) +model: (.+) +rev: (.+)$/,"\\1 \\2 \\3","g",a[i]) - #c=gensub( /^ *vendor: (.+) +model: (.+) +rev:.*$/,"\\1 \\2","g",a[i] ) - # the vendor: string is useless, and is a bug, ATA is not a vendor for example - c=gensub( /^ *vendor: (.+) +model: (.+) +rev:.*$/, "\\2", "g", a[i] ) - gsub(/'"$BAN_LIST_ARRAY"'/, " ", c) - gsub(/^ +| +$/, "", c) - gsub(/ [ \t]+/, " ", c) - #print a[i] - # we actually want this data, so leaving this off for now -# if (c ~ /\<flash\>|\<pendrive\>|memory stick|memory card/) { -# continue -# } - print c - } - } - }' $FILE_SCSI ) ) - log_function_data 'cat' "$FILE_SCSI" - fi - IFS="$ORIGINAL_IFS" - ## then we'll loop through that array looking for matches. - if [[ -n $( grep -Es 'sd[a-z]|nvme' <<< ${A_HDD_DATA[@]} ) ]];then - # first pack the main ls variable so we don't have to keep using ls /dev... - # not all systems have /dev/disk/by-id - ls_disk_by_id="$( ls -l /dev/disk/by-id 2>/dev/null )" - ls_disk_by_path="$( ls -l /dev/disk/by-path 2>/dev/null )" - for (( i=0; i < ${#A_HDD_DATA[@]} - 1; i++ )) - do - firmware_rev='' - hdd_temp='' - hdd_serial='' - temp_name='' - working_path='' - block_type='' - if [[ -z ${A_HDD_DATA[$i]/*nvme*/} ]];then - block_type='nvme' - elif [[ -z ${A_HDD_DATA[$i]/*sd[a-z]*/} ]];then - block_type='sdx' - fi - if [[ -n $block_type ]];then - IFS="," - a_temp_working=( ${A_HDD_DATA[$i]} ) - IFS="$ORIGINAL_IFS" - if [[ $block_type == 'sdx' ]];then - working_path=/sys/block/${a_temp_working[0]}/device/ - elif [[ $block_type == 'nvme' ]];then - # this results in: - # /sys/devices/pci0000:00/0000:00:03.2/0000:06:00.0/nvme/nvme0/nvme0n1 - # but we want to go one level down so slice off trailing nvme0n1 - working_path=$(readlink -f /sys/block/${a_temp_working[0]} 2>/dev/null ) - working_path=${working_path%nvme*} - fi - # /sys/block/[sda,hda]/device/model - # this is handles the new /sys data types first - if [[ -e ${working_path}model ]];then - temp_name="$( remove_erroneous_chars ${working_path}model )" - temp_name=$( cut -d '-' -f 1 <<< ${temp_name// /_} ) - elif [[ ${#a_temp_scsi[@]} -gt 0 ]];then - for (( j=0; j < ${#a_temp_scsi[@]}; j++ )) - do - ## ok, ok, it's incomprehensible, search /dev/disk/by-id for a line that contains the - # discovered disk name AND ends with the correct identifier, sdx - # get rid of whitespace for some drive names and ids, and extra data after - in name - temp_name=$( cut -d '-' -f 1 <<< ${a_temp_scsi[$j]// /_} ) - sd_ls_by_id=$( grep -Em1 ".*$temp_name.*${a_temp_working[0]}$" <<< "$ls_disk_by_id" ) - if [[ -n $sd_ls_by_id ]];then - temp_name=${a_temp_scsi[$j]} - break - else - # test to see if we can get a better name output when null - if [[ -n $temp_name ]];then - temp_name=$temp_name - fi - fi - done - fi - # I don't know identifier for thunderbolt in /dev/disk/by-id / /dev/disk/by-path - if [[ -n $temp_name && -n "$ls_disk_by_id" ]];then - usb_exists=$( grep -Em1 "usb-.*$temp_name.*${a_temp_working[0]}$" <<< "$ls_disk_by_id" ) - firewire_exists=$( grep -Em1 "ieee1394-.*$temp_name.*${a_temp_working[0]}$" <<< "$ls_disk_by_id" ) - # thunderbolt_exists=$( grep -Em1 "ieee1394-.*$temp_name.*${a_temp_working[0]}$" <<< "$ls_disk_by_id" ) - # note: sometimes with wwn- numbering usb does not appear in by-id but it does in by-path - fi - if [[ -n "$ls_disk_by_path" ]];then - if [[ -z $usb_exists ]];then - usb_exists=$( grep -Em1 "usb-.*${a_temp_working[0]}$" <<< "$ls_disk_by_path" ) - fi - if [[ -n $usb_exists ]];then - a_temp_working[3]='USB' - fi - if [[ -z $firewire_exists ]];then - firewire_exists=$( grep -Em1 "ieee1394-.*${a_temp_working[0]}$" <<< "$ls_disk_by_path" ) - fi - if [[ -n $firewire_exists ]];then - a_temp_working[3]='FireWire' - fi - fi - a_temp_working[2]=$temp_name - for (( j=0; j < ${#a_temp_working[@]}; j++ )) - do - if [[ $j -gt 0 ]];then - A_HDD_DATA[i]="${A_HDD_DATA[i]},${a_temp_working[$j]}" - else - A_HDD_DATA[i]="${a_temp_working[$j]}" - fi - done - fi - if [[ $B_EXTRA_DATA == 'true' ]];then - IFS="," - a_temp_working=( ${A_HDD_DATA[i]} ) - # echo "a:" ${a_temp_working[@]} - IFS="$ORIGINAL_IFS" - - if [[ -n ${a_temp_working[1]} ]];then - hdd_temp=$( get_hdd_temp_data "/dev/${a_temp_working[0]}" ) - fi - if [[ $B_EXTRA_EXTRA_DATA == 'true' ]];then - if [[ -e ${working_path}serial ]];then - hdd_serial="$( remove_erroneous_chars ${working_path}serial )" - else - hdd_serial=$( get_hdd_serial_number "${a_temp_working[0]}" ) - fi - if [[ -e ${working_path}firmware_rev ]];then - firmware_rev="$( remove_erroneous_chars ${working_path}firmware_rev )" - fi - fi - A_HDD_DATA[i]="${a_temp_working[0]},${a_temp_working[1]},${a_temp_working[2]},${a_temp_working[3]},$hdd_serial,$hdd_temp,$firmware_rev" - # echo b: ${A_HDD_DATA[i]} - fi - done - fi - a_temp=${A_HDD_DATA[@]} - log_function_data "A_HDD_DATA: $a_temp" - eval $LOGFE -} - -# args: $1 ~ hdd_used -get_hard_drive_data_bsd() +## MachineData { - eval $LOGFS - - local a_temp='' - - if [[ -n $DMESG_BOOT_DATA ]];then - IFS=$'\n' - A_HDD_DATA=( $( gawk -v hddUsed="$1" -F ':' ' - BEGIN { - IGNORECASE=1 - size=0 - bSetSize="false" - } - $1 ~ /^(ad|ada|mmcblk|nvme[0-9]+n|sd|wd)[0-9]+(|[[:space:]]at.*)$/ { - diskId=gensub(/^((ad|ada|mmcblk|nvme[0-9]+n|sd|wd)[0-9]+)[^0-9].*/,"\\1",1,$1) - # note: /var/run/dmesg.boot may repeat items since it is not created - # fresh every boot, this way, only the last items will be used per disk id - if (aIds[diskId] == "" ) { - aIds[diskId]=diskId - if ( $0 !~ /raid/) { - bSetSize="true" - } - } - aDisks[diskId, "id"] = diskId - if ($0 ~ /[^0-9][0-9\.]+[[:space:]]*[MG]B/ && $0 !~ /MB\/s/) { - workingSize=gensub(/.*[^0-9]([0-9\.]+[[:space:]]*[MG]B).*/,"\\1",1,$0) - if (workingSize ~ /GB/ ) { - sub(/[[:space:]]*GB/,"",workingSize) - workingSize=workingSize*1000 - } - else if (workingSize ~ /MB/ ) { - sub(/[[:space:]]*MB/,"",workingSize) - workingSize=workingSize - } - aDisks[diskId, "size"] = workingSize - if ( bSetSize == "true" ) { - if ( workingSize != "" ){ - size=size+workingSize - bSetSize="false" - } +package MachineData; + +sub get { + eval $start if $b_log; + my (%arm_machine,@data,@rows,$key1,$val1,$which); + my $num = 0; + if ($bsd_type || $b_dmidecode_force){ + my $ref = $alerts{'dmidecode'}; + if ( $$ref{'action'} ne 'use'){ + $key1 = $$ref{'action'}; + $val1 = $$ref{$key1}; + $key1 = ucfirst($key1); + } + else { + @data = machine_data_dmi(); + if (!@data && !$key1){ + $key1 = 'Message'; + $val1 = main::row_defaults('machine-data',''); + } + } + } + elsif (-d '/sys/class/dmi/id/') { + @data = machine_data_sys(); + if (!@data){ + $key1 = 'Message'; + $val1 = main::row_defaults('machine-data-dmidecode',''); + } + } + elsif (!$bsd_type) { + # this uses /proc/cpuinfo so only GNU/Linux + if ($b_arm){ + %arm_machine = machine_data_arm(); + if (%arm_machine){ + @data = create_output_arm(%arm_machine); + } + } + if (!@data){ + $key1 = 'Message'; + $val1 = main::row_defaults('machine-data-alt-33',''); + } + } + # if error case, null data, whatever + if ($key1) { + @data = ({main::key($num++,$key1) => $val1,}); + } + eval $end if $b_log; + return @data; +} +## keys for machine data are: +# 0-sys_vendor 1-product_name 2-product_version 3-product_serial 4-product_uuid +# 5-board_vendor 6-board_name 7-board_version 8-board_serial +# 9-bios_vendor 10-bios_version 11-bios_date +## with extra data: +# 12-chassis_vendor 13-chassis_type 14-chassis_version 15-chassis_serial +## unused: 16-bios_rev 17-bios_romsize 18 - firmware type +sub create_output { + eval $start if $b_log; + my ($ref) = @_; + my (%data,@row,@rows); + %data = %$ref; + my $firmware = 'BIOS'; + my $num = 0; + my $j = 0; + my ($b_chassis,$b_skip_chassis,$b_skip_system); + my ($bios_date,$bios_rev,$bios_romsize,$bios_vendor,$bios_version,$chassis_serial, + $chassis_type,$chassis_vendor,$chassis_version, $mobo_model,$mobo_serial,$mobo_vendor, + $mobo_version,$product_name,$product_serial,$product_version,$system_vendor); +# foreach my $key (keys %data){ +# print "$key: $data{$key}\n"; +# } + if (!$data{'sys_vendor'} || ($data{'board_vendor'} && + $data{'sys_vendor'} eq $data{'board_vendor'} && !$data{'product_name'} && + !$data{'product_version'} && !$data{'product_serial'})){ + $b_skip_system = 1; + } + # found a case of battery existing but having nothing in it on desktop mobo + # not all laptops show the first. /proc/acpi/battery is deprecated. + elsif ( !glob('/proc/acpi/battery/*') && !glob('/sys/class/power_supply/*') ){ + # ibm / ibm can be true; dell / quantum is false, so in other words, only do this + # in case where the vendor is the same and the version is the same and not null, + # otherwise the version information is going to be different in all cases I think + if ( ($data{'sys_vendor'} && $data{'sys_vendor'} eq $data{'board_vendor'} ) && + ( ($data{'product_version'} && $data{'product_version'} eq $data{'board_version'} ) || + (!$data{'product_version'} && $data{'product_name'} eq $data{'board_name'} ) ) ){ + $b_skip_system = 1; + } + } + $data{'device'} ||= 'N/A'; + $j = scalar @rows; + @row = ({ + main::key($num++,'Type') => ucfirst($data{'device'}), + },); + @rows = (@rows,@row); + if (!$b_skip_system){ + # this has already been tested for above so we know it's not null + $system_vendor = main::cleaner($data{'sys_vendor'}); + $product_name = ($data{'product_name'}) ? $data{'product_name'}:'N/A'; + $product_version = ($data{'product_version'}) ? $data{'product_version'}:'N/A'; + $product_serial = main::apply_filter($data{'product_serial'}); + $rows[$j]{main::key($num++,'System')} = $system_vendor; + $rows[$j]{main::key($num++,'product')} = $product_name; + $rows[$j]{main::key($num++,'v')} = $product_version; + $rows[$j]{main::key($num++,'serial')} = $product_serial; + # no point in showing chassis if system isn't there, it's very unlikely that + # would be correct + if ($extra > 1){ + if ($data{'board_version'} && $data{'chassis_version'} eq $data{'board_version'}){ + $b_skip_chassis = 1; + } + if (!$b_skip_chassis && $data{'chassis_vendor'} ){ + if ($data{'chassis_vendor'} ne $data{'sys_vendor'} ){ + $chassis_vendor = $data{'chassis_vendor'}; + } + if ($data{'chassis_type'} ){ + $chassis_type = $data{'chassis_type'}; + } + if ($data{'chassis_version'}){ + $chassis_version = $data{'chassis_version'} + } + $chassis_serial = main::apply_filter($data{'chassis_serial'}); + $chassis_vendor ||= ''; + $chassis_type ||= ''; + $rows[$j]{main::key($num++,'Chassis')} = $chassis_vendor; + if ($chassis_type){ + $rows[$j]{main::key($num++,'type')} = $chassis_type; } + if ($chassis_version){ + $rows[$j]{main::key($num++,'v')} = $chassis_version; + } + $rows[$j]{main::key($num++,'serial')} = $chassis_serial; } - if ( $NF ~ /<.*>/ ){ - gsub(/.*<|>.*/,"",$NF) - aDisks[diskId, "model"] = $NF + } + $j++; # start new row + } + if ($data{'firmware'}){ + $firmware = $data{'firmware'}; + } + $mobo_vendor = ($data{'board_vendor'}) ? main::cleaner($data{'board_vendor'}) : 'N/A'; + $mobo_model = ($data{'board_name'}) ? $data{'board_name'}: 'N/A'; + $mobo_version = ($data{'board_version'})? $data{'board_version'} : ''; + $mobo_serial = main::apply_filter($data{'board_serial'}); + $bios_vendor = ($data{'bios_vendor'}) ? main::cleaner($data{'bios_vendor'}) : 'N/A'; + if ($data{'bios_version'}){ + $bios_version = $data{'bios_version'}; + if ($data{'bios_rev'}){ + $bios_rev = $data{'bios_rev'}; + } + $bios_version ||= 'N/A'; + } + if ($data{'bios_date'}){ + $bios_date = $data{'bios_date'}; + } + if ($extra > 1 && $data{'bios_romsize'}){ + $bios_romsize = $data{'bios_romsize'}; + } + $rows[$j]{main::key($num++,'Mobo')} = $mobo_vendor; + $rows[$j]{main::key($num++,'model')} = $mobo_model; + if ($mobo_version){ + $rows[$j]{main::key($num++,'v')} = $mobo_version; + } + $rows[$j]{main::key($num++,'serial')} = $mobo_serial; + $rows[$j]{main::key($num++,$firmware)} = $bios_vendor; + $rows[$j]{main::key($num++,'v')} = $bios_version; + if ($bios_rev){ + $rows[$j]{main::key($num++,'rev')} = $bios_rev; + } + $rows[$j]{main::key($num++,'date')} = $bios_date; + if ($bios_romsize){ + $rows[$j]{main::key($num++,'rom size')} = $bios_romsize; + } + eval $end if $b_log; + return @rows; +} +sub create_output_arm { + my (%data,@row,@rows); + my (%arm_machine) = @_; + my $num = 0; + my $j = 0; + #print Data::Dumper::Dumper \%arm_machine; + if ($arm_machine{'device'}){ + my $device = main::cleaner( $arm_machine{'device'} ); + $device ||= 'N/A'; + $rows[$j]{main::key($num++,'Type')} = 'ARM Device'; + $rows[$j]{main::key($num++,'System')} = $device; + } + # we're going to print N/A for 0000 values sine the item was there. + if ($arm_machine{'firmware'}){ + # most samples I've seen are like: 0000 + $arm_machine{'firmware'} =~ s/^[0]+$//; + $arm_machine{'firmware'} ||= 'N/A'; + $rows[$j]{main::key($num++,'rev')} = $arm_machine{'firmware'}; + } + # sometimes has value like: 0000 + if (defined $arm_machine{'serial'}){ + # most samples I've seen are like: 0000 + $arm_machine{'serial'} =~ s/^[0]+$//; + $rows[$j]{main::key($num++,'serial')} = main::apply_filter($arm_machine{'serial'}); + } + eval $end if $b_log; + return @rows; +} + +sub machine_data_sys { + eval $start if $b_log; + my (%data,$path,$vm); + my $sys_dir = '/sys/class/dmi/id/'; + my $sys_dir_alt = '/sys/devices/virtual/dmi/id/'; + my @sys_files = qw(bios_vendor bios_version bios_date + board_name board_serial board_vendor board_version chassis_type + product_name product_serial product_uuid product_version sys_vendor + ); + if ($extra > 1){ + splice @sys_files, 0, 0, qw( chassis_serial chassis_vendor chassis_version); + } + $data{'firmware'} = 'BIOS'; + # print Data::Dumper::Dumper \@sys_files; + if (!-d $sys_dir ){ + if ( -d $sys_dir_alt){ + $sys_dir = $sys_dir_alt; + } + else { + return 0; + } + } + if ( -d '/sys/firmware/efi'){ + $data{'firmware'} = 'UEFI'; + } + elsif ( glob('/sys/firmware/acpi/tables/UEFI*') ){ + $data{'firmware'} = 'UEFI [Legacy]'; + } + foreach (@sys_files){ + $path = "$sys_dir$_"; + if (-r $path){ + $data{$_} = (main::reader($path))[0]; + $data{$_} = ($data{$_}) ? main::dmi_cleaner($data{$_}) : ''; + } + else { + $data{$_} = ''; + } + } + if ($data{'chassis_type'}){ + if ( $data{'chassis_type'} == 1){ + $data{'device'} = get_device_vm($data{'sys_vendor'},$data{'product_name'}); + $data{'device'} ||= 'other-vm?'; + } + else { + $data{'device'} = get_device_sys($data{'chassis_type'}); + } + } +# print "sys:\n"; +# foreach (keys %data){ +# print "$_: $data{$_}\n"; +# } + main::log_data('dump','%data',\%data) if $b_log; + my @rows = create_output(\%data); + eval $end if $b_log; + return @rows; +} +# this will create an alternate machine data source +# which will be used for alt ARM machine data in cases +# where no dmi data present, or by cpu data to guess at +# certain actions for arm only. +sub machine_data_arm { + eval $end if $b_log; + my (%arm_machine,@temp); + if (my $file = main::system_files('cpuinfo')){ + #$file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/arm/arm-shevaplug-1.2ghz.txt"; + my @data = main::reader($file); + foreach (@data){ + if (/^Hardware/i){ + @temp = split /\s*:\s*/, $_; + $arm_machine{'device'} = $temp[1]; } - if ( $NF ~ /serial number/ ){ - sub(/serial[[:space:]]+number[[:space:]]*/,"",$NF) - aDisks[diskId, "serial"] = $NF + elsif (/^Revision/i){ + @temp = split /\s*:\s*/, $_; + $arm_machine{'firmware'} = $temp[1]; + } + elsif (/^Serial/i){ + @temp = split /\s*:\s*/, $_; + $arm_machine{'serial'} = $temp[1]; } } - END { - # sde,3.9GB,STORE_N_GO,USB,C200431546D3CF49-0:0,0 - # sdd,250.1GB,ST3250824AS,,9ND08GKX,45 - # multi dimensional pseudo arrays are sorted at total random, not in order of - # creation, so force a sort of the aIds, which deletes the array index but preserves - # the sorted keys. - asort(aIds) - - for ( key in aIds ) { - # we are not adding to size above for raid, and we do not print it for raid - # this is re openbsd raid, which uses sd0 for raid array, even though sd is for scsi - if ( aDisks[aIds[key], "model"] !~ /raid/ ) { - workingSize = aDisks[aIds[key], "size"]/1000 - workingSize = sprintf( "%.1fGB", workingSize ) - print aDisks[aIds[key], "id"] "," workingSize "," aDisks[aIds[key], "model"] "," "," aDisks[aIds[key], "serial"] ",," - } - } - size = size/1000 # calculate size in GB size - # in kb - workingUsed = hddUsed*1024/1000**3 # calculate workingUsed in GB used - # this handles a special case with livecds where no hdd_used is detected - if ( size > 0 && hddUsed == "na" ) { - size = sprintf( "%.1f", size ) - print size "GB,-,,.." - } - else if ( size > 0 && workingUsed > 0 ) { - diskUsed = workingUsed*100/size # calculate used percentage - diskUsed = sprintf( "%.1f", diskUsed ) - if ( int(diskUsed) > 100 ) { - diskUsed = "Used Error!" + } + #print Data::Dumper::Dumper \%arm_machine; + eval $end if $b_log; + return %arm_machine; +} + +# bios_date: 09/07/2010 +# bios_romsize: dmi only +# bios_vendor: American Megatrends Inc. +# bios_version: P1.70 +# bios_rev: 8.14: dmi only +# board_name: A770DE+ +# board_serial: +# board_vendor: ASRock +# board_version: +# chassis_serial: +# chassis_type: 3 +# chassis_vendor: +# chassis_version: +# firmware: +# product_name: +# product_serial: +# product_uuid: +# product_version: +# sys_uuid: dmi only +# sys_vendor: +sub machine_data_dmi { + eval $start if $b_log; + my (%data,$vm); + return if ! @dmi; + $data{'firmware'} = 'BIOS'; + # dmi types: + # 0 bios; 1 system info; 2 board|base board info; 3 chassis info; + # 4 processor info, use to check for hypervisor + foreach (@dmi){ + my @ref = @$_; + # bios/firmware + if ($ref[0] == 0){ + # skip first three row, we don't need that data + splice @ref, 0, 3 if @ref; + foreach my $item (@ref){ + if ($item !~ /^~/){ # skip the indented rows + my @value = split /:\s+/, $item; + if ($value[0] eq 'Release Date') {$data{'bios_date'} = main::dmi_cleaner($value[1]) } + elsif ($value[0] eq 'Vendor') {$data{'bios_vendor'} = main::dmi_cleaner($value[1]) } + elsif ($value[0] eq 'Version') {$data{'bios_version'} = main::dmi_cleaner($value[1]) } + elsif ($value[0] eq 'ROM Size') {$data{'bios_romsize'} = main::dmi_cleaner($value[1]) } + elsif ($value[0] eq 'BIOS Revision') {$data{'bios_rev'} = main::dmi_cleaner($value[1]) } + elsif ($value[0] =~ /^UEFI is supported/) {$data{'firmware'} = 'UEFI';} } - else { - diskUsed = diskUsed "% used" + } + next; + } + # system information + elsif ($ref[0] == 1){ + # skip first three row, we don't need that data + splice @ref, 0, 3 if @ref; + foreach my $item (@ref){ + if ($item !~ /^~/){ # skip the indented rows + my @value = split /:\s+/, $item; + if ($value[0] eq 'Product Name') {$data{'product_name'} = main::dmi_cleaner($value[1]) } + elsif ($value[0] eq 'Version') {$data{'product_version'} = main::dmi_cleaner($value[1]) } + elsif ($value[0] eq 'Serial Number') {$data{'product_serial'} = main::dmi_cleaner($value[1]) } + elsif ($value[0] eq 'Manufacturer') {$data{'sys_vendor'} = main::dmi_cleaner($value[1]) } + elsif ($value[0] eq 'UUID') {$data{'sys_uuid'} = main::dmi_cleaner($value[1]) } } - size = sprintf( "%.1f", size ) - print size "GB," diskUsed ",,.." } - else { - print "NA,-,,.." # print an empty array, this will be further handled in the print out function + next; + } + # baseboard information + elsif ($ref[0] == 2){ + # skip first three row, we don't need that data + splice @ref, 0, 3 if @ref; + foreach my $item (@ref){ + if ($item !~ /^~/){ # skip the indented rows + my @value = split /:\s+/, $item; + if ($value[0] eq 'Product Name') {$data{'board_name'} = main::dmi_cleaner($value[1]) } + elsif ($value[0] eq 'Serial Number') {$data{'board_serial'} = main::dmi_cleaner($value[1]) } + elsif ($value[0] eq 'Manufacturer') {$data{'board_vendor'} = main::dmi_cleaner($value[1]) } + } } - }' <<< "$DMESG_BOOT_DATA" ) ) - IFS="$ORIGINAL_IFS" - fi - - a_temp=${A_HDD_DATA[@]} - # echo ${a_temp[@]} - log_function_data "A_HDD_DATA: $a_temp" - - eval $LOGFE + next; + } + # chassis information + elsif ($ref[0] == 3){ + # skip first three row, we don't need that data + splice @ref, 0, 3 if @ref; + foreach my $item (@ref){ + if ($item !~ /^~/){ # skip the indented rows + my @value = split /:\s+/, $item; + if ($value[0] eq 'Serial Number') {$data{'chassis_serial'} = main::dmi_cleaner($value[1]) } + elsif ($value[0] eq 'Type') {$data{'chassis_type'} = main::dmi_cleaner($value[1]) } + elsif ($value[0] eq 'Manufacturer') {$data{'chassis_vendor'} = main::dmi_cleaner($value[1]) } + elsif ($value[0] eq 'Version') {$data{'chassis_version'} = main::dmi_cleaner($value[1]) } + } + } + if ( $data{'chassis_type'} && $data{'chassis_type'} ne 'Other' ){ + $data{'device'} = $data{'chassis_type'}; + } + next; + } + # this may catch some BSD and fringe Linux cases + # processor information: check for hypervisor + elsif ($ref[0] == 4){ + # skip first three row, we don't need that data + splice @ref, 0, 3 if @ref; + if (!$data{'device'}){ + if (grep {/hypervisor/i} @ref){ + $data{'device'} = 'virtual-machine'; + } + } + last; + } + elsif ($ref[0] > 4){ + last; + } + } + if (!$data{'device'}){ + $data{'device'} = get_device_vm($data{'sys_vendor'},$data{'product_name'}); + $data{'device'} ||= 'other-vm?'; + } +# print "dmi:\n"; +# foreach (keys %data){ +# print "$_: $data{$_}\n"; +# } + main::log_data('dump','%data',\%data) if $b_log; + my @rows = create_output(\%data); + eval $end if $b_log; + return @rows; } -# args: $1 - which drive to get serial number of -get_hdd_serial_number() -{ - eval $LOGFS - - local hdd_serial='' - - get_partition_dev_data 'id' - - # lrwxrwxrwx 1 root root 9 Apr 26 09:32 scsi-SATA_ST3160827AS_5MT2HMH6 -> ../../sdc - # exception: ata-InnoDisk_Corp._-_mSATA_3ME3_BCA34401050060191 -> ../../sda - # exit on the first instance - hdd_serial=$( gawk ' - /'$1'$/ { - serial=gensub( /(.+)_([^_]+)$/, "\\2", 1, $9 ) - print serial - exit - }' <<< "$DEV_DISK_ID" ) - - echo $hdd_serial - log_function_data "hdd serial: $hdd_serial" - eval $LOGFE +sub get_device_sys { + eval $start if $b_log; + my ($chasis_id) = @_; + my ($device) = (''); + my @chassis; + # https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.8.0.pdf + $chassis[2] = 'unknown'; + # note: 13 is all-in-one which we take as a mac type system + $chassis[3] = 'desktop'; + $chassis[4] = 'desktop'; + $chassis[6] = 'desktop'; + $chassis[7] = 'desktop'; + $chassis[13] = 'desktop'; + $chassis[15] = 'desktop'; + $chassis[24] = 'desktop'; + # 5 - pizza box was a 1 U desktop enclosure, but some old laptops also id this way + $chassis[5] = 'pizza-box'; + $chassis[9] = 'laptop'; + # note: lenovo T420 shows as 10, notebook, but it's not a notebook + $chassis[10] = 'laptop'; + $chassis[16] = 'laptop'; + $chassis[14] = 'notebook'; + $chassis[8] = 'portable'; + $chassis[11] = 'portable'; + $chassis[17] = 'server'; + $chassis[23] = 'server'; + $chassis[25] = 'server'; + $chassis[27] = 'blade'; + $chassis[25] = 'blade'; + $chassis[29] = 'blade'; + $chassis[12] = 'docking-station'; + $chassis[18] = 'expansion-chassis'; + $chassis[19] = 'sub-chassis'; + $chassis[20] = 'bus-expansion'; + $chassis[21] = 'peripheral'; + $chassis[22] = 'RAID'; + $chassis[26] = 'compact-PCI'; + $device = $chassis[$chasis_id] if $chassis[$chasis_id]; + eval $end if $b_log; + return $device; } -# a few notes, normally hddtemp requires root, but you can set user rights in /etc/sudoers. -# args: $1 - /dev/<disk> to be tested for -get_hdd_temp_data() -{ - eval $LOGFS - local hdd_temp='' sudo_command='' device=$1 - - if [[ $B_SUDO_TESTED != 'true' ]];then - B_SUDO_TESTED='true' - SUDO_PATH=$( type -p sudo ) - fi - # only use sudo if not root, -n option requires sudo -V 1.7 or greater. sudo will just error out - # which is the safest course here for now, otherwise that interactive sudo password thing is too annoying - # important: -n makes it non interactive, no prompt for password - if [[ $B_ROOT != 'true' && -n $SUDO_PATH ]];then - sudo_command='sudo -n ' - fi - # try this to see if hddtemp gives result for the base name - if [[ -z ${device/*nvme*/} ]];then - if type -p nvme &>/dev/null;then - device=${device%n[0-9]} - # this will fail if regular user and no sudo present, but that's fine, it will just return null - hdd_temp=$( eval $sudo_command nvme smart-log $device 2>/dev/null | gawk -F ':' ' - BEGIN { - IGNORECASE=1 - } - # other rows may have: Temperature sensor 1 : - /^temperature\s*:/ { - gsub(/^[[:space:]]+|[[:space:]]*C$/,"",$2) - print $2 - }' ) - fi - else - if [[ $B_HDDTEMP_TESTED != 'true' ]];then - B_HDDTEMP_TESTED='true' - HDDTEMP_PATH=$( type -p hddtemp ) - fi - if [[ -n $HDDTEMP_PATH && -n $device ]];then - # this will fail if regular user and no sudo present, but that's fine, it will just return null - hdd_temp=$( eval $sudo_command $HDDTEMP_PATH -nq -u C $device ) - fi - fi - if [[ -n $hdd_temp && -z ${hdd_temp//[0-9]/} ]];then - echo $hdd_temp - fi - eval $LOGFE -} - -get_init_data() -{ - eval $LOGFS - - local init_type='' init_version='' rc_type='' rc_version='' a_temp='' - local ls_run='' strings_init_version='' - local runlevel=$( get_runlevel_data ) - local default_runlevel=$( get_runlevel_default ) - - # this test is pretty solid, if pid 1 is owned by systemd, it is systemd - # otherwise that is 'init', which covers the rest of the init systems, I think anyway. - # more data may be needed for other init systems. - if [[ -e /proc/1/comm && -n $( grep -s 'systemd' /proc/1/comm ) ]];then - init_type='systemd' - if type -p systemd &>/dev/null;then - init_version=$( get_program_version 'systemd' '^systemd' '2' ) - fi - if [[ -z $init_version ]];then - if type -p systemctl &>/dev/null;then - init_version=$( get_program_version 'systemctl' '^systemd' '2' ) - fi - fi - else - ls_run=$(ls /run) - # note: upstart-file-bridge.pid upstart-socket-bridge.pid upstart-udev-bridge.pid - if [[ -n $( /sbin/init --version 2>/dev/null | grep 'upstart' ) ]];then - init_type='Upstart' - # /sbin/init --version == init (upstart 1.12.1) - init_version=$( get_program_version 'init' 'upstart' '3' ) - elif [[ -e /proc/1/comm && -n $( grep -s 'epoch' /proc/1/comm ) ]];then - init_type='Epoch' - # epoch version == Epoch Init System 1.0.1 "Sage" - init_version=$( get_program_version 'epoch' '^Epoch' '4' ) - # missing data: note, runit can install as a dependency without being the init system - # http://smarden.org/runit/sv.8.html - # NOTE: the proc test won't work on bsds, so if runit is used on bsds we will need more data - elif [[ -e /proc/1/comm && -n $( grep -s 'runit' /proc/1/comm ) ]];then - # elif [[ -e /sbin/runit-init || -e /etc/runit || -n $( type -p sv ) ]];then - init_type='runit' # lower case - # no data on version yet - # freebsd at least - elif type -p launchctl &>/dev/null;then - init_type='launchd' - # / launchd/ version.plist /etc/launchd.conf - # init_version=$( get_program_version 'Launchd' '^Launchd' '4' ) - elif [[ -f /etc/inittab ]];then - init_type='SysVinit' - if type -p strings &>/dev/null;then - strings_init_version="$( strings /sbin/init | grep -E 'version[[:space:]]+[0-9]' )" - fi - if [[ -n $strings_init_version ]];then - init_version=$( gawk '{print $2}' <<< "$strings_init_version" ) - fi - elif [[ -f /etc/ttys ]];then - init_type='init (bsd)' - fi - if [[ -n $( grep 'openrc' <<< "$ls_run" ) ]];then - rc_type='OpenRC' - # /sbin/openrc --version == openrc (OpenRC) 0.13 - if type -p openrc &>/dev/null;then - rc_version=$( get_program_version 'openrc' '^openrc' '3' ) - # /sbin/rc --version == rc (OpenRC) 0.11.8 (Gentoo Linux) - elif type -p rc &>/dev/null;then - rc_version=$( get_program_version 'rc' '^rc' '3' ) - fi - if [[ -e /run/openrc/softlevel ]];then - runlevel=$( cat /run/openrc/softlevel 2>/dev/null ) - elif [[ -e /var/run/openrc/softlevel ]];then - runlevel=$( cat /var/run/openrc/softlevel 2>/dev/null ) - elif type -p rc-status &>/dev/null;then - runlevel=$( rc-status -r 2>/dev/null ) - fi - ## assume sysvrc, but this data is too buggy and weird and inconsistent to have meaning - # leaving this off for now -# elif [[ -f /etc/inittab ]];then -# rc_type='SysVrc' -# # this is a guess that rc and init are same versions, may need updates / fixes -# rc_version=$init_version - fi - fi - IFS=$'\n' - - A_INIT_DATA=( - "$init_type" - "$init_version" - "$rc_type" - "$rc_version" - "$runlevel" - "$default_runlevel" ) - - IFS="$ORIGINAL_IFS" - - a_temp=${A_INIT_DATA[@]} - log_function_data "A_INIT_DATA: $a_temp" - - eval $LOGFE +sub get_device_vm { + eval $start if $b_log; + my ($manufacturer,$product_name) = @_; + my $vm; + if ( my $program = main::check_program('systemd-detect-virt') ){ + my $vm_test = (main::grabber("$program 2>/dev/null"))[0]; + if ($vm_test){ + # kvm vbox reports as oracle, usually, unless they change it + if (lc($vm_test) eq 'oracle'){ + $vm = 'virtualbox'; + } + elsif ( $vm_test ne 'none'){ + $vm = $vm_test; + } + } + } + if (!$vm || lc($vm) eq 'bochs') { + if (-e '/proc/vz'){$vm = 'openvz'} + elsif (-e '/proc/xen'){$vm = 'xen'} + elsif (-e '/dev/vzfs'){$vm = 'virtuozzo'} + elsif (my $program = main::check_program('lsmod')){ + my @vm_data = main::grabber("$program 2>/dev/null"); + if (@vm_data){ + if (grep {/kqemu/i} @vm_data){$vm = 'kqemu'} + elsif (grep {/kvm/i} @vm_data){$vm = 'kvm'} + elsif (grep {/qemu/i} @vm_data){$vm = 'qemu'} + } + } + } + # this will catch many Linux systems and some BSDs + if (!$vm || lc($vm) eq 'bochs' ) { + my @vm_data = (@pci,@sysctl,@dmesg_boot); + if (-e '/dev/disk/by-id'){ + my @dev = glob('/dev/disk/by-id/*'); + @vm_data = (@vm_data,@dev); + } + if ( grep {/innotek|vbox|virtualbox/i} @vm_data){ + $vm = 'virtualbox'; + } + elsif (grep {/vmware/i} @vm_data){ + $vm = 'vmware'; + } + elsif (grep {/Virtual HD/i} @vm_data){ + $vm = 'hyper-v'; + } + if (!$vm && (my $file = main::system_files('cpuinfo'))){ + my @info = main::reader($file); + $vm = 'virtual-machine' if grep {/^flags.*hypervisor/} @info; + } + if (!$vm && -e '/dev/vda' || -e '/dev/vdb' || -e '/dev/xvda' || -e '/dev/xvdb' ){ + $vm = 'virtual-machine'; + } + } + if (!$vm && $product_name){ + if ($product_name eq 'VMware'){ + $vm = 'vmware'; + } + elsif ($product_name eq 'VirtualBox'){ + $vm = 'virtualbox'; + } + elsif ($product_name eq 'KVM'){ + $vm = 'kvm'; + } + elsif ($product_name eq 'Bochs'){ + $vm = 'qemu'; + } + } + if (!$vm && $manufacturer && $manufacturer eq 'Xen'){ + $vm = 'xen'; + } + eval $end if $b_log; + return $vm; } -get_kernel_compiler_version() -{ - # note that we use gawk to get the last part because beta, alpha, git versions can be non-numeric - local compiler_version='' compiler_type='' - - if [[ -e /proc/version ]];then - compiler_version=$( grep -Eio 'gcc[[:space:]]*version[[:space:]]*([^ \t]*)' /proc/version 2>/dev/null | gawk '{print $3}' ) - if [[ -n $compiler_version ]];then - compiler_type='gcc' - fi - else - if [[ $BSD_VERSION == 'darwin' ]];then - if type -p gcc &>/dev/null;then - compiler_version=$( get_program_version 'gcc' 'Apple[[:space:]]LLVM' '4' ) - if [[ -n $compiler_version ]];then - compiler_type='LLVM-GCC' - fi - fi - else - if [[ -f /etc/src.conf ]];then - compiler_type=$( grep '^CC' /etc/src.conf | cut -d '=' -f 2 ) - elif [[ -f /etc/make.conf ]];then - compiler_type=$( grep '^CC' /etc/make.conf | cut -d '=' -f 2 ) - fi - if [[ -n $compiler_type ]];then - if type -p $compiler_type &>/dev/null;then - if [[ $compiler_type == 'gcc' ]];then - compiler_version=$( get_program_version 'gcc' '^gcc' '3' ) - elif [[ $compiler_type == 'clang' ]];then - # FreeBSD clang version 3.0 (tags/RELEASE_30/final 145349) 20111210 - compiler_version=$( get_program_version 'clang' 'clang' '4' ) - fi - fi - fi - fi - fi - if [[ -n $compiler_version ]];then - compiler_version="$compiler_type^$compiler_version" - fi - echo $compiler_version -} - -get_kernel_version() -{ - eval $LOGFS - - local kernel_version='' ksplice_kernel_version='' - - kernel_version=$( uname -rm ) - if [[ $BSD_VERSION == 'darwin' ]];then - kernel_version="Darwin $kernel_version" - fi - if [[ -n $( type -p uptrack-uname ) && -n $kernel_version ]];then - ksplice_kernel_version=$( uptrack-uname -rm ) - if [[ $kernel_version != $ksplice_kernel_version ]];then - kernel_version="$ksplice_kernel_version (ksplice)" - fi - fi - log_function_data "kernel_version: $kernel_version - ksplice_kernel_version: $ksplice_kernel_version" - - CURRENT_KERNEL=$kernel_version - - eval $LOGFE } -get_machine_data() +## NetworkData { - eval $LOGFS - local a_temp='' separator='' id_file='' file_data='' array_string='' - local id_dir='/sys/class/dmi/id/' dmi_data='' firmware_type='BIOS' - local machine_files=" - sys_vendor product_name product_version product_serial product_uuid - board_vendor board_name board_version board_serial - bios_vendor bios_version bios_date - " - if [[ $B_EXTRA_EXTRA_DATA == 'true' ]];then - machine_files="$machine_files - chassis_vendor chassis_type chassis_version chassis_serial - " - fi - if [[ -d $id_dir && $B_FORCE_DMIDECODE == 'false' ]];then - if [[ -d /sys/firmware/efi ]];then - firmware_type='UEFI' - elif [[ -n $(ls /sys/firmware/acpi/tables/UEFI* 2>/dev/null ) ]];then - firmware_type='UEFI [Legacy]' - fi - for id_file in $machine_files - do - file_data='' - if [[ -r $id_dir$id_file ]];then - file_data=$( gawk ' - BEGIN { - IGNORECASE=1 +package NetworkData; +my ($b_ip_run,@ifs_found); +sub get { + eval $start if $b_log; + my (@data,@rows); + my $num = 0; + if ($b_arm){ + my $key = 'ARM'; + @data = ({ + main::key($num++,$key) => main::row_defaults('arm-pci',''), + },); + @rows = (@rows,@data); + } + else { + @data = card_data(); + @rows = (@rows,@data) if @data; + @data = usb_data(); + @rows = (@rows,@data) if @data; + } + if ($show{'network-advanced'}){ + # @ifs_found = (); + # shift @ifs_found; + # pop @ifs_found; + if (!$bsd_type){ + @data = advanced_data_sys('check','',0,'',''); + @rows = (@rows,@data) if @data; + } + else { + @data = advanced_data_bsd('check'); + @rows = (@rows,@data) if @data; + } + } + if ($show{'ip'}){ + @data = wan_ip(); + @rows = (@rows,@data); + } + eval $end if $b_log; + return @rows; +} +# 1 type_id +# 2 bus_id +# 3 sub_id +# 4 device +# 5 vendor_id +# 6 chip_id +# 7 rev +# 8 port +# 9 driver +# 10 modules +# 11 driver nu (bsds) +sub card_data { + eval $start if $b_log; + my ($b_wifi,@rows,@data,%holder); + my ($j,$num) = (0,1); + foreach (@pci){ + $num = 1; + my @row = @$_; + #print "$row[0] $row[3]\n"; + if ($row[0] eq 'network' || $row[0] eq 'ethernet' ){ + #print "$row[0] $row[3]\n"; + $j = scalar @rows; + my $driver = $row[9]; + my $chip_id = "$row[5]:$row[6]"; + # working around a virtuo bug same chip id is used on two nics + if (!defined $holder{$chip_id}){ + $holder{$chip_id} = 0; + } + else { + $holder{$chip_id}++; + } + # first check if it's a known wifi id'ed card, if so, no print of duplex/speed + $b_wifi = check_wifi($row[4]); + my $card = $row[4]; + $card = ($card) ? main::pci_cleaner($card,'output') : 'N/A'; + #$card ||= 'N/A'; + $driver ||= 'N/A'; + @data = ( + { + main::key($num++,'Card') => $card, + main::key($num++,'driver') => $driver, + }, + ); + @rows = (@rows,@data); + if ($extra > 0){ + if ($row[9] && !$bsd_type){ + my $version = main::get_module_version($row[9]); + $version ||= 'N/A'; + $rows[$j]{main::key($num++,'v')} = $version; } - { - gsub(/'"$BAN_LIST_NORMAL"'/, "", $0) - gsub(/'"$BAN_LIST_ARRAY"'/, " ", $0) - # yes, there is a typo in a user data set, unknow - # Base Board Version|Base Board Serial Number - # Chassis Manufacturer|Chassis Version|Chassis Serial Number - # System manufacturer|System Product Name|System Version - # To Be Filled By O.E.M. - sub(/^Base Board .*|^Chassis .*|.*O\.E\.M\..*|.*OEM.*|^Not .*|^System .*|.*unknow.*|.*N\/A.*|Default string|none|^To be filled.*/, "", $0) - gsub(/\ybios\y|\yacpi\y/, "", $0) # note: biostar - sub(/http:\/\/www.abit.com.tw\//, "Abit", $0) - gsub(/^ +| +$/, "", $0) - gsub(/ [ \t]+/, " ", $0) - print $0 - }' < $id_dir$id_file ) - fi - array_string="$array_string$separator$file_data" - separator=',' - done - if [[ $array_string != '' ]];then - # note: dmidecode has two more data types possible, so always add 2 more - if [[ $B_EXTRA_EXTRA_DATA == 'true' ]];then - array_string="$array_string,," - else - array_string="$array_string,,,,,," - fi - array_string="$array_string,$firmware_type" - fi - else - set_dmidecode_data - if [[ -n $DMIDECODE_DATA ]];then - if [[ $DMIDECODE_DATA == 'dmidecode-error-'* ]];then - array_string=$DMIDECODE_DATA - # please note: only dmidecode version 2.11 or newer supports consistently the -s flag - else - array_string=$( gawk -F ':' ' - BEGIN { - IGNORECASE=1 - baseboardManufacturer="" - baseboardProductName="" - baseboardSerialNumber="" - baseboardVersion="" - chassisManufacturer="" - chassisSerialNumber="" - chassisType="" - chassisVersion="" - firmwareReleaseDate="" - firmwareRevision="" # only available from dmidecode - firmwareRomSize="" # only available from dmidecode - firmwareType="BIOS" - firmwareVendor="" - firmwareVersion="" - systemManufacturer="" - systemProductName="" - systemVersion="" - systemSerialNumber="" - systemUuid="" - bItemFound="" # we will only output if at least one item was found - fullString="" - testString="" - bSys="" - bCha="" - bBio="" - bBas="" - } - /^Bios Information/ { - while ( getline && !/^$/ ) { - if ( $1 ~ /^Release Date/ ) { firmwareReleaseDate=$2 } - if ( $1 ~ /^BIOS Revision/ ) { firmwareRevision=$2 } - if ( $1 ~ /^ROM Size/ ) { firmwareRomSize=$2 } - if ( $1 ~ /^Vendor/ ) { firmwareVendor=$2 } - if ( $1 ~ /^Version/ ) { firmwareVersion=$2 } - if ( $1 ~ /^UEFI is supported/ ) { firmwareType="UEFI" } - } - testString=firmwareReleaseDate firmwareRevision firmwareRomSize firmwareVendor firmwareVersion - if ( testString != "" ) { - bItemFound="true" - } - bBio="true" + $row[8] ||= 'N/A'; + # as far as I know, wifi has no port, but in case it does in future, use it + $rows[$j]{main::key($num++,'port')} = $row[8] if (!$b_wifi || ( $b_wifi && $row[8] ne 'N/A') ); + $rows[$j]{main::key($num++,'bus ID')} = $row[2] . '.' . $row[3]; + } + if ($extra > 1){ + $rows[$j]{main::key($num++,'chip ID')} = $chip_id; + } + if ($show{'network-advanced'}){ + if (!$bsd_type){ + @data = advanced_data_sys($row[5],$row[6],$holder{$chip_id},$b_wifi,''); } - /^Base Board Information/ { - while ( getline && !/^$/ ) { - if ( $1 ~ /^Manufacturer/ ) { baseboardManufacturer=$2 } - if ( $1 ~ /^Product Name/ ) { baseboardProductName=$2 } - if ( $1 ~ /^Serial Number/ ) { baseboardSerialNumber=$2 } - } - testString=baseboardManufacturer baseboardProductName baseboardSerialNumber - if ( testString != "" ) { - bItemFound="true" - } - bBas="true" - } - /^Chassis Information/ { - while ( getline && !/^$/ ) { - if ( $1 ~ /^Manufacturer/ ) { chassisManufacturer=$2 } - if ( $1 ~ /^Serial Number/ ) { chassisSerialNumber=$2 } - if ( $1 ~ /^Type/ ) { chassisType=$2 } - if ( $1 ~ /^Version/ ) { chassisVersion=$2 } + else { + @data = advanced_data_bsd("$row[9]$row[11]",$b_wifi); + } + @rows = (@rows,@data); + } + } + #print "$row[0]\n"; + } + # @rows = (); + if (!@rows){ + my $key = 'Message'; + @data = ({ + main::key($num++,$key) => main::row_defaults('pci-card-data',''), + },); + @rows = (@rows,@data); + + } + #my $ref = $pci[-1]; + #print $$ref[0],"\n"; + eval $end if $b_log; + return @rows; +} +sub usb_data { + eval $start if $b_log; + my (@data,@rows,@temp2,$b_wifi,$driver,$path,$product,$product2,$test,$vendor,$vendor2); + my ($j,$num) = (0,1); + return if !@usb; + foreach my $ref (@usb){ + my @row = @$ref; + # a device will always be the second or > device on the bus + if ($row[1] > 1){ + $num = 1; + ($product,$product2,$test,$vendor,$vendor2) = ('','','','',''); + if ($usb_level == 1){ + $product = main::cleaner($row[3]); + } + else { + foreach my $line (@row){ + my @working = split /:/, $line; + if ($working[0] eq 'idVendor' && $working[2]){ + $vendor = main::cleaner($working[2]); } - testString=chassisManufacturer chassisSerialNumber chassisType chassisVersion - if ( testString != "" ) { - bItemFound="true" + if ($working[0] eq 'idProduct' && $working[2]){ + $product = main::cleaner($working[2]); } - bCha="true" - } - /^System Information/ { - while ( getline && !/^$/ ) { - if ( $1 ~ /^Manufacturer/ ) { systemManufacturer=$2 } - if ( $1 ~ /^Product Name/ ) { systemProductName=$2 } - if ( $1 ~ /^Version/ ) { systemVersion=$2 } - if ( $1 ~ /^Serial Number/ ) { systemSerialNumber=$2 } - if ( $1 ~ /^UUID/ ) { systemUuid=$2 } + if ($working[0] eq 'iVendor' && $working[2]){ + $product2 = main::cleaner($working[2]); } - testString=systemManufacturer systemProductName systemVersion systemSerialNumber systemUuid - if ( testString != "" ) { - bItemFound="true" + if ($working[0] eq 'iProduct' && $working[2]){ + $product2 = main::cleaner($working[2]); } - bSys="true" - } - ( bSys == "true" && bCha="true" && bBio == "true" && bBas == "true" ) { - exit # stop the loop - } - END { - # sys_vendor product_name product_version product_serial product_uuid - # board_vendor board_name board_version board_serial - # bios_vendor bios_version bios_date - if ( bItemFound == "true" ) { - fullString = systemManufacturer "," systemProductName "," systemVersion "," systemSerialNumber - fullString = fullString "," systemUuid "," baseboardManufacturer "," baseboardProductName - fullString = fullString "," baseboardVersion "," baseboardSerialNumber "," firmwareVendor - fullString = fullString "," firmwareVersion "," firmwareReleaseDate "," chassisManufacturer - fullString = fullString "," chassisType "," chassisVersion "," chassisSerialNumber - fullString = fullString "," firmwareRevision "," firmwareRomSize "," firmwareType - - print fullString + if ($working[0] eq 'Descriptor_Configuration'){ + last; } - }' <<< "$DMIDECODE_DATA" ) - fi - fi - fi - # echo $array_string - IFS=',' - A_MACHINE_DATA=( $array_string ) - IFS="$ORIGINAL_IFS" - # echo ${A_MACHINE_DATA[5]} - a_temp=${A_MACHINE_DATA[@]} - # echo $a_temp - log_function_data "A_MACHINE_DATA: $a_temp" - eval $LOGFE -} -# B_ROOT='true';get_machine_data;exit -## return memory used/installed -get_memory_data() -{ - eval $LOGFS - local memory='' memory_full='' used_memory='' - if [[ $B_MEMINFO_FILE == 'true' ]];then - memory=$( gawk ' - /^MemTotal:/ { - tot = $2 - } - /^(MemFree|Buffers|Cached):/ { - notused+=$2 - } - END { - used = tot - notused - printf("%.1f/%.1fMB\n", used/1024, tot/1024) - }' $FILE_MEMINFO ) - log_function_data 'cat' "$FILE_MEMINFO" - elif [[ $B_SYSCTL == 'true' && -n $SYSCTL_A_DATA ]];then - local gawk_fs=': ' - # darwin sysctl is broken and uses both : and = and repeats these items - if [[ $BSD_VERSION == 'openbsd' ]];then - gawk_fs='=' - fi - # use this for all bsds, maybe we can get some useful data on other ones - if [[ -n $( type -p vmstat) ]];then - # avail mem:2037186560 (1942MB) - used_memory=$( vmstat 2>/dev/null | tail -n 1 | gawk ' - # openbsd/linux - # procs memory page disks traps cpu - # r b w avm fre flt re pi po fr sr wd0 wd1 int sys cs us sy id - # 0 0 0 55256 1484092 171 0 0 0 0 0 2 0 12 460 39 3 1 96 - # freebsd: - # procs memory page disks faults cpu - # r b w avm fre flt re pi po fr sr ad0 ad1 in sy cs us sy id - # 0 0 0 21880M 6444M 924 32 11 0 822 827 0 0 853 832 463 8 3 88 - # dragonfly - # procs memory page disks faults cpu - # r b w avm fre flt re pi po fr sr ad0 ad1 in sy cs us sy id - # 0 0 0 0 84060 30273993 2845 12742 1164 407498171 320960902 0 0 424453025 1645645889 1254348072 35 38 26 - - BEGIN { - IGNORECASE=1 - memory="" + } + if ($vendor && $product){ + $product = ($product =~ /$vendor/) ? $product: "$vendor $product"; + } + elsif ($vendor && $product2){ + $product = ($product2 =~ /$vendor/) ? $product2: "$vendor $product2"; + } + elsif ($vendor2 && $product){ + $product = ($product =~ /$vendor2/) ? $product: "$vendor2 $product"; + } + elsif ($vendor2 && $product2){ + $product = ($product2 =~ /$vendor2/) ? $product2: "$vendor2 $product2"; + } + elsif ($vendor){ + $product = $vendor; + } + elsif ($vendor2){ + $product = $vendor2; + } + $test = "$vendor $product $vendor2 $vendor2"; } - { - if ($4 ~ /M/ ){ - |