diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..bdb0cabc --- /dev/null +++ b/.gitattributes @@ -0,0 +1,17 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..81d7f578 --- /dev/null +++ b/.gitignore @@ -0,0 +1,46 @@ +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# ========================= +# Operating System Files +# ========================= + +# OSX +# ========================= + +.DS_Store +.AppleDouble +.LSOverride + +# Thumbnails +._* + +# Files that might appear on external disk +.Spotlight-V100 +.Trashes + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Bluefish temporary files +*.*~ diff --git a/plugins/ipmitool.plg b/plugins/ipmitool.plg new file mode 100755 index 00000000..1ea3bd8b --- /dev/null +++ b/plugins/ipmitool.plg @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + +]> + + + + +###2015.12.11 +- change repo +- change install and remove method +###2015.11.18 +- fixed "Waiting for events" triggering notification on system start +###2015.11.17 +- fix permissions for emhttp events +###2015.11.10b +- deleted wrong page file +###2015.11.10 +- stop event monitoring on plugin removal +###2015.11.09 +- minor bug fixes +###2015.11.08 +- add settings page +- add event notifications +- add remote IPMI access +###2015.08.15 +- update for unRAID 6.1-rc+ only +###2015.05.07a +- fix bug +###2015.05.07 +- initial unRAID 6 release. + + + + + +&pkgURL;&pkg; +&md5; + + + + +&gitURL;/archive/&plgname;.txz + + + + +&gitURL;/archive/&plgname;.md5 + + + + + +# Verify and install plugin package +sum1=$(/usr/bin/md5sum &plgpath;/&plgname;.txz) +sum2=$(/usr/bin/cat &plgpath;/&plgname;.md5) +if [ "${sum1:0:32}" != "${sum2:0:32}" ]; then + echo "Wrong 'plugin' package md5 hash." + rm &plgpath;/&plgname;.txz + rm &plgpath;/&plgname;.md5 + exit 1 +else + upgradepkg --install-new &plgpath;/&plgname;.txz +fi + +# Update file permissions of scripts +chmod +0755 &emhttp;/scripts/* + +# Load impi drivers +echo "Loading ipmi drivers..." +modprobe ipmi_si +modprobe ipmi_devintf + +# Cleaning old source files +find &plgpath;/ -type f -iname "&name;*.txz" ! -iname "*&version;*" -delete +find &plgpath;/ -type f -iname "&name;*.md5" ! -iname "*&version;*" -delete + +echo "" +echo "-----------------------------------------------------------" +echo " &name; has been installed." +echo " This plugin requires Dynamix webGui to operate" +echo " Copyright 2015, &author;" +echo " Version: &version;" +echo "-----------------------------------------------------------" +echo "" + + + + + + +&emhttp;/scripts/stop +removepkg &plgpath;/&plgname;.txz +rm -rf &emhttp; +rm -f &plgpath;/&plgname;.txz +rm -f &plgpath;/&plgname;.md5 +removepkg &plugin;/&pkg; + +echo "Unloading ipmi drivers..." +modprobe -r ipmi_si +modprobe -r ipmi_devintf + +echo "" +echo "-----------------------------------------------------------" +echo " &name; has been removed." +echo " Copyright 2015, &author;" +echo " Version: &version;" +echo "-----------------------------------------------------------" +echo "" + + + + + diff --git a/source/ipmitool/install/doinst.sh b/source/ipmitool/install/doinst.sh new file mode 100644 index 00000000..1a248525 --- /dev/null +++ b/source/ipmitool/install/doinst.sh @@ -0,0 +1 @@ +#!/bin/sh diff --git a/source/ipmitool/install/slack-desc b/source/ipmitool/install/slack-desc new file mode 100644 index 00000000..d7d9bf3e --- /dev/null +++ b/source/ipmitool/install/slack-desc @@ -0,0 +1,19 @@ +# HOW TO EDIT THIS FILE: +# The "handy ruler" below makes it easier to edit a package description. +# Line up the first '|' above the ':' following the base package name, and +# the '|' on the right side marks the last column you can put a character in. +# You must make exactly 11 lines for the formatting to be correct. It's also +# customary to leave one space after the ':' except on otherwise blank lines. + + |-----handy-ruler------------------------------------------------------| +speedtest: Speedtest (Command Line Tool) +speedtest: +speedtest: This is a plugin for unRAID 6.1+. It is an interface for testing +speedtest: internet bandwidth using speedtest.net and speedtest-cli. +speedtest: +speedtest: sivel/speedtest-cli +speedtest: https://github.com/sivel/speedtest-cli +speedtest: +speedtest: dmacias72/unRAID +speedtest: https://github.com/dmacias72/unRAID +speedtest: diff --git a/source/ipmitool/pkg_build.sh b/source/ipmitool/pkg_build.sh new file mode 100755 index 00000000..68a24194 --- /dev/null +++ b/source/ipmitool/pkg_build.sh @@ -0,0 +1,37 @@ +#!/bin/bash +DIR="$(dirname "$(readlink -f ${BASH_SOURCE[0]})")" +tmpdir=/tmp/tmp.$(( $RANDOM * 19318203981230 + 40 )) +plugin=$(basename ${DIR}) +archive="$(dirname $(dirname ${DIR}))/archive" +version=$(date +"%Y.%m.%d") +package="${archive}/${plugin}-${version}-x86_64-1.txz" +md5="${archive}/${plugin}-${version}-x86_64-1.md5" + +if [[ -f $package ]]; then + for x in a b c d e d f g h ; do + package="${archive}/${plugin}-${version}${x}-x86_64-1.txz" + md5="${archive}/${plugin}-${version}${x}-x86_64-1.md5" + if [[ ! -f $package ]]; then + break + fi + done +fi + +mkdir -p $tmpdir +cd "$DIR" +cp --parents -f $(find . -type f ! \( -iname "pkg_build.sh" -o -iname "sftp-config.json" -o -iname ".DS_Store" \) ) $tmpdir/ +cd "$tmpdir/" +makepkg -l y -c y "${package}" +cd "$archive/" +md5sum $(basename "$package") > "$md5" +rm -rf "$tmpdir" + +# Verify and install plugin package +sum1=$(md5sum "${package}") +sum2=$(cat "$md5") +if [ "${sum1:0:32}" != "${sum2:0:32}" ]; then + echo "Checksum mismatched."; + rm "$md5" "${package}" +else + echo "Checksum matched." +fi \ No newline at end of file diff --git a/source/ipmitool/usr/local/emhttp/plugins/ipmitool/IPMIEvents.page b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/IPMIEvents.page new file mode 100644 index 00000000..e224ca60 --- /dev/null +++ b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/IPMIEvents.page @@ -0,0 +1,25 @@ +Menu="IPMItool:3" +Title="Event Log" +--- +
+ + + + + + + + + + + + + + + +
All Status Event ID Time Stamp Sensor Name Sensor Type Subject Description
+
+ + +
+
diff --git a/source/ipmitool/usr/local/emhttp/plugins/ipmitool/IPMIFans.page b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/IPMIFans.page new file mode 100644 index 00000000..06b233a4 --- /dev/null +++ b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/IPMIFans.page @@ -0,0 +1,275 @@ +Menu="IPMItool:2" +Title="Fan Control" +--- + + + "", + "name" => "", + "comment" => "", + "allocator" => "highwater", + "floor" => "", + "splitLevel" => "", + "include" => "", + "exclude" => "", + "useCache" => "no"); +} else if (array_key_exists($name, $shares)) { + /* edit existing share */ + $share = $shares[$name]; +} else { + /* handle share deleted case */ + echo "

Share $name has been deleted.

"; + return; +} + +/* check for empty share */ +function shareEmpty($name) { + return (($files = @scandir('/mnt/user/'.$name)) && (count($files) <= 2)); +} + +if ($var['shareUserInclude']) { + $myDisks = explode(',',$var['shareUserInclude']); +} else { + $myDisks = array(); + foreach ($disks as $disk) $myDisks[] = $disk['name']; +} + +if ($var['shareUserExclude']) { + $exclude = explode(',',$var['shareUserExclude']); + foreach ($exclude as $disk) { + $index = array_search($disk,$myDisks); + if ($index !== false) array_splice($myDisks,$index,1); + } +} +?> +> A *Share*, also called a *User Share*, is simply the name of a top-level directory that exists on one or more of your +> storage devices (array and cache). Each share can be exported for network access. When browsing a share, we return the +> composite view of all files and subdirectories for which that top-level directory exists on each storage device. + + +
+ +Name: +: + +> The share name can be up to 40 characters, and is case-sensitive. While almost all characters can be used, +> make your life easier and avoid special characters such as apostrophes and quotes. + +Comments: +: + +> Anything you like, up to 256 characters. + +Allocation method: +: + +> This setting determines how unRAID OS will choose which disk to use when creating a new file or directory: +> +> **High-water** +> Choose the lowest numbered disk with free space still above the current *high water mark*. The +> *high water mark* is initialized with the size of the largest data disk divided by 2. If no disk +> has free space above the current *high water mark*, divide the *high water mark* by 2 and choose again. +> +> The goal of **High-water** is to write as much data as possible to each disk (in order to minimize +> how often disks need to be spun up), while at the same time, try to keep the same amount of free space on +> each disk (in order to distribute data evenly across the array). +> +> **Fill-up** +> Choose the lowest numbered disk that still has free space above the current **Minimum free space** +> setting. +> +> **Most-free** +> Choose the disk that currently has the most free space. + +Minimum free space: +: + +> The *minimum free space* available to allow writing to any disk belonging to the share.
+> +> Choose a value which is equal or greater than the biggest single file size you intend to copy to the share. +> Include units KB, MB, GB and TB as appropriate, e.g. 10MB. + +Split level: +: + +> Determines whether a directory is allowed to expand onto multiple disks. + +> **Automatically split any directory as required** +> When a new file or subdirectory needs to be created in a share, unRAID OS first chooses which disk +> it should be created on, according to the configured *Allocation method*. If the parent directory containing +> the new file or or subdiretory does not exist on this disk, then unRAID OS will first create all necessary +> parent directories, and then create the new file or subdirectory. +> +> **Automatically split only the top level directory as required** +> When a new file or subdirectory is being created in the first level subdirectory of a share, if that first +> level subdirectory does not exist on the disk being written, then the subdirectory will be created first. +> If a new file or subdirectory is being created in the second or lower level subdirectory of a share, the new +> file or subdirectory is created on the same disk as the new file or subdirectorys parent directory. +> +> **Automatically split only the top "N" level directories as required** +> Similar to previous: when a new file or subdirectory is being created, if the parent directory is at level "N", +> and does not exist on the chosen disk, unRAID OS will first create all necessary parent directories. If the +> parent directory of the new file or subdirectory is beyond level "N", then the new file or subdirectory is +> created on the same disk where the parent directory exists. +> +> **Manual: do not automatically split directories** +> When a new file or subdirectory needs to be created in a share, unRAID OS will only condider disks where the +> parent directory already exists. + +Included disk(s): +: + +> Specify the disks which can be used by the share. By default all disks are included; that is, if specific +> disks are not selected here, then the share may expand into *all* array disks. + +Excluded disk(s): +: + +> Specify the disks which can *not* be used by the share. By default no disks are excluded. + + +Use cache disk: +: + + +> Specify whether new files and subdirectories written on the share can be written onto the Cache disk/pool. +> +> **No** prohibits new files and subdirectories from being written onto the Cache disk/pool. +> +> **Yes** indicates that all new files and subdirectories should be written to the Cache disk/pool, provided +> enough free space exists on the Cache disk/pool. If there is insufficant space on the Cache disk/pool, then +> new files and directories are created on the array. When the *mover* is invoked, files and subdirectories are +> transfered off the Cache disk/pool and onto the array. +> +> **Only** indicates that all new files and subdirectories must be writen to the Cache disk/pool. If there +> is insufficient free space on the Cache disk/pool, *create* operations will fail with *out of space* status. + + + +: + +Share empty? +: Yes + +Delete +: + +Share empty? +: No + + +: + +
diff --git a/source/ipmitool/usr/local/emhttp/plugins/ipmitool/IPMISensors.page b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/IPMISensors.page new file mode 100644 index 00000000..3fc4fc82 --- /dev/null +++ b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/IPMISensors.page @@ -0,0 +1,47 @@ +Menu="IPMItool:1" +Title="Sensors" +--- + + + + +
+ + + + + + + + + + + + + + + + + + +
Status Sensor Reading Units
+
+ +
+
+ + + + + diff --git a/source/ipmitool/usr/local/emhttp/plugins/ipmitool/IPMISettings.page b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/IPMISettings.page new file mode 100644 index 00000000..b362f299 --- /dev/null +++ b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/IPMISettings.page @@ -0,0 +1,101 @@ +Icon="ipmitool.png" +Menu="NetworkServices" +Title="IPMI tool" +--- + /dev/null`/exe ] && echo 'yes' || echo 'no' 2> /dev/null" )); +?> + + + +
+ + + +Enable Event Notifications: +: + +Enable Remote Connection: +: + + +: + + +: + + +: + + +: +
+ + \ No newline at end of file diff --git a/source/ipmitool/usr/local/emhttp/plugins/ipmitool/IPMItool.page b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/IPMItool.page new file mode 100644 index 00000000..c4c6f13e --- /dev/null +++ b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/IPMItool.page @@ -0,0 +1,5 @@ +Menu="UNRAID-OS" +Title="IPMI tool" +Type="xmenu" +Tabs="true" +--- diff --git a/source/ipmitool/usr/local/emhttp/plugins/ipmitool/LICENSE b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/LICENSE new file mode 100644 index 00000000..c5f3c450 --- /dev/null +++ b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/LICENSE @@ -0,0 +1,341 @@ +GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {signature of Ty Coon}, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. + + diff --git a/source/ipmitool/usr/local/emhttp/plugins/ipmitool/README.md b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/README.md new file mode 100644 index 00000000..2150fe2f --- /dev/null +++ b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/README.md @@ -0,0 +1,4 @@ +**IPMI support** + +The ipmitool plugin allows you to view your system's sensors and events under the Tools menu using IPMItool and your ipmi hardware. +Uses Local or Remote access and features event notification. diff --git a/source/ipmitool/usr/local/emhttp/plugins/ipmitool/check_ipmi_sensor b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/check_ipmi_sensor new file mode 100755 index 00000000..814e1f13 --- /dev/null +++ b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/check_ipmi_sensor @@ -0,0 +1,392 @@ +#!/bin/bash +# check_ipmi_sensor: Nagios/Icinga plugin to check IPMI sensors +# +# Copyright (C) 2009-2011 Thomas-Krenn.AG (written by Werner Fischer), +# additional contributors see changelog.txt +# +# 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 . +# +################################################################################ +# The following guides provide helpful information if you want to extend this +# script: +# http://tldp.org/LDP/abs/html/ (Advanced Bash-Scripting Guide) +# http://www.gnu.org/software/gawk/manual/ (Gawk: Effective AWK Programming) +# http://de.wikibooks.org/wiki/Awk (awk Wikibook, in German) +# http://nagios.sourceforge.net/docs/3_0/customobjectvars.html (hints on +# custom object variables) +# http://nagiosplug.sourceforge.net/developer-guidelines.html (plug-in +# development guidelines) +# http://nagios.sourceforge.net/docs/3_0/pluginapi.html (plugin API) +################################################################################ + +################################################################################ +# set text variables +version="check_ipmi_sensor version 2.4-dev" +version_text="$version +Copyright (C) 2009-2012 Thomas-Krenn.AG (written by Werner Fischer) +Current updates available at http://git.thomas-krenn.com/check_ipmi_sensor_v2.git" +usage_text="Usage: +check_ipmi_sensor -H + [-f | -U -P -L ] + [-O ] [-b] [-T ] [-x ] [-v 1|2|3] + [-o zenoss] [-h] [-V]" +help_text="Options: + -H + hostname or IP of the IPMI interface. + For \"-H localhost\" the Nagios/Icinga user must be allowed to execute + ipmimonitoring with root privileges via sudo (ipmimonitoring must be + able to access the IPMI devices via the IPMI system interface). + [-f ] + path to the FreeIPMI configuration file. + Only neccessary for communication via network. + Not neccessary for access via IPMI system interface (\"-H localhost\"). + It should contain IPMI username, IPMI password, and IPMI privilege-level, + for example: + username monitoring + password yourpassword + privilege-level user + As alternative you can use -U/-P/-L instead (see below). + [-U -P -L ] + IPMI username, IPMI password and IPMI privilege level, provided as + parameters and not by a FreeIPMI configuration file. Useful for RHEL/ + Centos 5.* with FreeIPMI 0.5.1 (this elder FreeIPMI version does not + support config files). + Warning: with this method the password is visible in the process list. + So whenever possible use a FreeIPMI confiugration file instead. + [-O ] + additional options for FreeIPMI. Useful for RHEL/CentOS 5.* with + FreeIPMI 0.5.1 (this elder FreeIPMI version does not support config + files). + [-b] + backward compatibility mode for FreeIPMI 0.5.* (this omits the FreeIPMI + caching options --quiet-cache and --sdr-cache-recreate) + [-T ] + limit sensors to query based on IPMI sensor type. + Examples for IPMI sensor type are 'Fan', 'Temperature', 'Voltage', ... + See chapter '42.2 Sensor Type Codes and Data' of the IPMI 2.0 spec for a + full list of possible sensor types. The available types depend on your + particular server and the available sensors there. + [-x ] + exclude sensor matching . Useful for cases when unused + sensors cannot be deleted from SDR and are reported in a non-OK state. + Option can be specified multiple times. The is a numeric + value (sensor names are not used as some servers have multiple sensors + with the same name). Use -v 3 option to query the . + [-v 1|2|3] + be verbose + (no -v) .. single line output + -v 1 ..... single line output with additional details for warnings + -v 2 ..... multi line output, also with additional details for warnings + -v 3 ..... debugging output, followed by normal multi line output + [-o] + change output format. Useful for using the plugin with other monitoring + software than Nagios or Icinga. + -o zenoss .. create ZENOSS compatible formatted output (output with + underscores instead of whitespaces and no single quotes) + [-h] + show this help + [-V] + show version information + +When you use the plugin with newer FreeIPMI versions (version 0.8.* and newer) +you need to set the --legacy-ouput option to get a parsable output. Further you +can use --interpret-oem-data to interpret OEM data (available since FreeIPMI +version 0.8.*) +You can set these options in your FreeIPMI configuration file: + ipmimonitoring-legacy-output on + ipmi-sensors-interpret-oem-data on +or you provide + -O '--legacy-output --interpret-oem-data' +to the plugin. + +Further information about this plugin can be found in the Thomas Krenn Wiki +(currently only in German): +http://www.thomas-krenn.com/de/wiki/IPMI_Sensor_Monitoring_Plugin + +Send email to the IPMI-plugin-user mailing list if you have questions regarding +use of this software, to submit patches, or suggest improvements. +The mailing list is available at http://lists.thomas-krenn.com/ +" +abort_text="" +missing_command_text="" + +################################################################################ +# set ipmimonitoring path +if [ -x "/usr/sbin/ipmimonitoring" ]; then IPMICOMMAND="/usr/sbin/ipmimonitoring" +elif [ -x "/usr/bin/ipmimonitoring" ]; then IPMICOMMAND="/usr/bin/ipmimonitoring" +elif [ -x "/usr/local/sbin/ipmimonitoring" ]; then IPMICOMMAND="/usr/local/sbin/ipmimonitoring" +elif [ -x "/usr/local/bin/ipmimonitoring" ]; then IPMICOMMAND="/usr/local/bin/ipmimonitoring" +else missing_command_text=" ipmimonitoring command not found." +fi + +################################################################################ +# read parameters +# * uses getopts, see http://tldp.org/LDP/abs/html/internal.html#GETOPTSX +while getopts "H:f:U:P:L:O:bT:v:x:o:hV?" option +do + case $option in + H) IPMI_HOST=$OPTARG;; + f) IPMI_CONFIG_FILE=$OPTARG;; + U) IPMI_USER=$OPTARG;; + P) IPMI_PASSWORD=$OPTARG;; + L) IPMI_PRIVILEGE_LEVEL=$OPTARG;; + O) FREEIPMI_OPTIONS=$OPTARG;; + b) FREEIPMI_BACKWARD_COMPATIBILITY=1;; + T) IPMI_SENSOR_TYPE=$OPTARG;; + v) VERBOSITY=$OPTARG;; + x) if [ -z "$IPMI_XLIST" ]; then + IPMI_XLIST="$OPTARG" + else + IPMI_XLIST="${IPMI_XLIST};$OPTARG" + fi + ;; + o) IPMI_OUTFORMAT=$OPTARG;; + h) echo "$version_text" + echo + echo "$usage_text" + echo + echo "$help_text" + exit 0;; + V) echo "$version_text" + exit 0;; + \?) echo "$usage_text" + exit 3;; + esac +done + +################################################################################ +# verify if all mandatory parameters are set and initialize various variables +if [ -z "$IPMI_HOST" ]; then abort_text="$abort_text -H " +else + if [ "$IPMI_HOST" == "localhost" ]; then + BASECOMMAND="sudo $IPMICOMMAND" + else + if [ -n "$IPMI_CONFIG_FILE" ]; then + BASECOMMAND="$IPMICOMMAND -h $IPMI_HOST --config-file $IPMI_CONFIG_FILE" + elif [ -n "$IPMI_USER" -a -n "$IPMI_PASSWORD" -a -n "$IPMI_PRIVILEGE_LEVEL" ]; then + BASECOMMAND="$IPMICOMMAND -h $IPMI_HOST -u $IPMI_USER -p $IPMI_PASSWORD -l $IPMI_PRIVILEGE_LEVEL" + else + abort_text="$abort_text -f or -U -P -L "; fi + fi +fi +if [ -n "$missing_command_text" ]; then + echo "Error:$missing_command_text" + exit 3 +fi +if [ -n "$abort_text" ]; then + echo "Error:$abort_text missing." + echo "$usage_text" + exit 3 +fi +if [ -n "$IPMI_SENSOR_TYPE" ]; then BASECOMMAND="$BASECOMMAND -g $IPMI_SENSOR_TYPE"; fi +if [ -n "$FREEIPMI_OPTIONS" ]; then BASECOMMAND="$BASECOMMAND $FREEIPMI_OPTIONS"; fi +if [ -n "$FREEIPMI_BACKWARD_COMPATIBILITY" ]; then + GET_STATUS="$BASECOMMAND" +else + GET_STATUS="$BASECOMMAND --quiet-cache --sdr-cache-recreate" +fi + +################################################################################ +# execute $GET_STATUS +# * uses old-style backquote so the backslash retains its literal meaning except +# when followed by ‘$’, ‘`’, or ‘\’ +# see http://www.gnu.org/software/bash/manual/bashref.html#Command-Substitution +ipmioutput=`eval $GET_STATUS 2>&1` +returncode=$? + +################################################################################ +# print debug output when verbosity is set to 3 (-v 3) +if [ "$VERBOSITY" = "3" ] +then + ipmicommandversion=`eval $IPMICOMMAND -V 2>&1 | head -n 1` + echo "------------- begin of debug output (-v 3 is set): ------------" + echo " script was executed with the following parameters:" + echo " $0 $@" + echo " check_ipmi_sensor version:" + echo " $version" + echo " ipmimonitoring version:" + echo " $ipmicommandversion" + echo " ipmimonitoring was executed with the following parameters:" + echo " $GET_STATUS" + echo " ipmimonitoring return code: $returncode" + echo " output of ipmimonitoring:" + echo "$ipmioutput" + echo "--------------------- end of debug output ---------------------" +fi + +################################################################################ +# generate main output +if [ $returncode != 0 ] +then + echo "$ipmioutput" + echo "-> Execution of ipmimonitoring failed with return code $returncode." + echo "-> ipmimonitoring was executed with the following parameters:" + echo " $GET_STATUS" + exit 3 +else + if [ -n "$IPMI_SENSOR_TYPE" ]; then + echo -n "Sensor Type '$IPMI_SENSOR_TYPE' Status: "; + else + echo -n "IPMI Status: "; + fi + echo "$ipmioutput" | gawk -v verbosity=$VERBOSITY -v xlist="$IPMI_XLIST" -v outformat="$IPMI_OUTFORMAT" -F '|' ' +################################################################################ +# * BEGIN rule is executed once only, before the first input record is read +# see http://www.gnu.org/software/gawk/manual/html_node/Using-BEGIN_002fEND.html +# * we initialize variables here +BEGIN { + EXIT=0 + number_of_numerical_records=0 + w_sensors="" + split(xlist,xl_array,";") +} + +################################################################################ +# * the "$4 !~ /Monitoring Status/" pattern below is used to omit the header +# output of ipmimonitoring +# see http://www.gnu.org/software/gawk/manual/html_node/Regexp-Usage.html +# * we fill the following arrays with data here: +# - arrays containing all sensors: +# - sensor_id[] .......... contains the id of the sensor, e.g. "1" +# - sensor_name[] ........ contains the name of the sensor, e.g. "Fan 1" +# - sensor_status[] ...... contains the status of the sensor, e.g. "Nominal" +# - sensor_units[] ....... contains the units of the sensor, e.g. "RPM" +# - sensor_reading[] ..... contains the sensor reading , e.g. "5719.000" +# - arrays containing only numerical sensors (for performance data) +# - n_record_name[] ...... contains the name of the sensor, e.g. "Fan 1" +# - n_record_value[] ..... contains the numerical reading, e.g. "5719.000" +$4 !~ /Monitoring Status/ { + ######################################################################## + # Remove extra spaces + gsub(/ +$/,"",$1) + gsub(/^ +/,"",$2) + gsub(/ +$/,"",$2) + gsub(/^ +/,"",$4) + gsub(/ +$/,"",$4) + gsub(/^ +/,"",$5) + gsub(/ +$/,"",$5) + gsub(/^ +/,"",$6) + gsub(/ +$/,"",$6) + # Substitute whitespaces with underscores in sensor_name for ZENOSS + if (outformat == "zenoss") gsub(/[[:space:]]/,"_",$2) + + sensor_id[NR]=$1 + sensor_name[NR]=$2 + # sensor_type[NR]=$3 (currently not used) + sensor_status[NR]=$4 + sensor_units[NR]=$5 + sensor_reading[NR]=$6 + + ######################################################################## + # Omit this sensor if the sensor is included in the list of sensors to + # exclude + for (ind in xl_array) + { + if (sensor_id[NR] == xl_array[ind]) next + } + ######################################################################## + # * set EXIT variable to 1 if a sensor is not "ok" or "ns" + # * also build contents of w_sensors variable (sensors with status not + # ok) in this case + if (sensor_status[NR] != "Nominal") + { + if (EXIT < 1) EXIT=1 + if (EXIT < 2 && sensor_status[NR] != "Warning" ) EXIT=2 + if (verbosity>0) + { + if (w_sensors == "") + w_sensors=sensor_name[NR]" = "sensor_status[NR]" ("sensor_reading[NR]")" + else + w_sensors=w_sensors", "sensor_name[NR]" = "sensor_status[NR]" ("sensor_reading[NR]")" + } + else + { + if (w_sensors == "") + w_sensors=sensor_name[NR]" = "sensor_status[NR] + else + w_sensors=w_sensors", "sensor_name[NR]" = "sensor_status[NR] + } + } + + if (sensor_units[NR] != "N/A") + { + number_of_numerical_records++ + n_record_name[number_of_numerical_records]=sensor_name[NR] + n_record_value[number_of_numerical_records]=sensor_reading[NR] + } +} + +################################################################################ +# * END rule is executed once only, after all the input is read +# see http://www.gnu.org/software/gawk/manual/html_node/Using-BEGIN_002fEND.html +# * we print the data which has been collected above in this part below +END { + ######################################################################## + # * build perfdata string (variable pstring) using quotes + # see http://www.gnu.org/software/gawk/manual/html_node/Quoting.html + while(j0) + print "OK | "pstring + else + print "OK" + } + else + { + if (EXIT==1) + { + if (number_of_numerical_records>0) + print "Warning ["w_sensors"] | "pstring + else + print "Warning ["w_sensors"]" + } + else + { + if (number_of_numerical_records>0) + print "Critical ["w_sensors"] | "pstring + else + print "Critical ["w_sensors"]" + } + } + + ######################################################################## + # * print additional text lines (multi-line output) for verbosity > 1 + if (verbosity>1) + { + while(i 1" is necessary to omit the header output line + if (i > 1 && exclude == "false") + print sensor_name[i],"=",sensor_reading[i],"(Status:",sensor_status[i]")" + } + } + exit EXIT +} +' +fi diff --git a/source/ipmitool/usr/local/emhttp/plugins/ipmitool/event/started b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/event/started new file mode 100755 index 00000000..bc02ac55 --- /dev/null +++ b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/event/started @@ -0,0 +1,5 @@ +#!/bin/bash +source /boot/config/plugins/ipmitool/ipmitool.cfg +if [ $SERVICE = enable ]; then + /usr/local/emhttp/plugins/ipmitool/scripts/start +fi diff --git a/source/ipmitool/usr/local/emhttp/plugins/ipmitool/event/stopping_svcs b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/event/stopping_svcs new file mode 100755 index 00000000..7b4bb913 --- /dev/null +++ b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/event/stopping_svcs @@ -0,0 +1,2 @@ +#!/bin/bash +/usr/local/emhttp/plugins/ipmitool/scripts/stop diff --git a/source/ipmitool/usr/local/emhttp/plugins/ipmitool/icons/eventlog.png b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/icons/eventlog.png new file mode 100644 index 00000000..be461ca9 Binary files /dev/null and b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/icons/eventlog.png differ diff --git a/source/ipmitool/usr/local/emhttp/plugins/ipmitool/icons/fancontrol.png b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/icons/fancontrol.png new file mode 100644 index 00000000..82503435 Binary files /dev/null and b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/icons/fancontrol.png differ diff --git a/source/ipmitool/usr/local/emhttp/plugins/ipmitool/icons/ipmitool.png b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/icons/ipmitool.png new file mode 100644 index 00000000..91a061fc Binary files /dev/null and b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/icons/ipmitool.png differ diff --git a/source/ipmitool/usr/local/emhttp/plugins/ipmitool/icons/sensors.png b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/icons/sensors.png new file mode 100644 index 00000000..00054c3e Binary files /dev/null and b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/icons/sensors.png differ diff --git a/source/ipmitool/usr/local/emhttp/plugins/ipmitool/images/green-on.png b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/images/green-on.png new file mode 100644 index 00000000..e326b04d Binary files /dev/null and b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/images/green-on.png differ diff --git a/source/ipmitool/usr/local/emhttp/plugins/ipmitool/images/ipmitool.png b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/images/ipmitool.png new file mode 100644 index 00000000..7dd6d9ff Binary files /dev/null and b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/images/ipmitool.png differ diff --git a/source/ipmitool/usr/local/emhttp/plugins/ipmitool/images/red-on.png b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/images/red-on.png new file mode 100644 index 00000000..1c1ef8e9 Binary files /dev/null and b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/images/red-on.png differ diff --git a/source/ipmitool/usr/local/emhttp/plugins/ipmitool/include/delete_event.php b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/include/delete_event.php new file mode 100644 index 00000000..c5332401 --- /dev/null +++ b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/include/delete_event.php @@ -0,0 +1,4 @@ + diff --git a/source/ipmitool/usr/local/emhttp/plugins/ipmitool/include/fancontrol.php b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/include/fancontrol.php new file mode 100644 index 00000000..0135ef40 --- /dev/null +++ b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/include/fancontrol.php @@ -0,0 +1,248 @@ +#!/usr/bin/php +/dev/null", $crontab); + $crontab = array_unique($crontab); + if (! isset($quit)){ + $entry = sprintf("*/%s * * * * ${service_file}${background_args} 1> /dev/null 2>&1", $cron); + if (! preg_grep("#${service_file}#", $crontab)){ + $crontab[] = $entry; + debug("\nCRONTAB\n".implode("\n", $crontab)."\n"); + file_put_contents("/tmp/${program_name}.cron", implode(PHP_EOL, $crontab)); + shell_exec("crontab /tmp/${program_name}.cron"); + unlink("/tmp/$program_name.cron"); + } + } + unset($crontab); +} else if (isset($quit)){ + exec("crontab -l 2>/dev/null", $crontab); + $crontab = array_unique($crontab); + if (preg_grep("#${service_file}#", $crontab)){ + $crontab = preg_grep("#${service_file}#", $crontab, PREG_GREP_INVERT); + debug("\nCRONTAB\n".implode("\n", $crontab)."\n"); + file_put_contents("/tmp/${program_name}.cron", implode(PHP_EOL, $crontab)); + shell_exec("crontab /tmp/${program_name}.cron"); + unlink("/tmp/$program_name.cron"); + }; + unset($crontab); +} + +if (is_file($lockfile)){ + $lock_pid = file($lockfile, FILE_IGNORE_NEW_LINES)[0]; + $pid_running=preg_replace("/\s+/", "", shell_exec("ps -p ${lock_pid}| grep ${lock_pid}")); + if (! $pid_running){ + if (! isset($quit)){ + file_put_contents($lockfile, getmypid()); + } else { + echo "${lock_pid} is not currently running"; + unlink($lockfile); + exit(0); + } + } else { + if (isset($quit)){ + syslog(LOG_INFO, "killing daemon with PID [${lock_pid}]"); + exec("kill $lock_pid"); + unlink($lockfile); + if (function_exists('at_exit')) at_exit(); + exit(0); + } else { + echo "$program_name is already running [${lock_pid}]".PHP_EOL; + exit(0); + } + } +} else { + if(isset($quit)){ + echo "$program_name not currently running".PHP_EOL; + exit(0); + } else { + file_put_contents($lockfile, getmypid()); + } +} + +if(!isset($background)){ + exec("php $service_file --background $background_args 1>/dev/null ".($DEBUG ? "":"2>&1 ")."&"); + exit(0); +} else { + syslog(LOG_INFO, "process started. To terminate it, type: $program_name -q"); +} + +############################## +##### PROGRAM SECTION ###### +############################## +$plugin = "dynamix.system.autofan"; +$config_file = "/boot/config/plugins/${plugin}/fan.conf"; + +function scan_dir($dir, $type = ""){ + $out = array(); + foreach (array_slice(scandir($dir), 2) as $entry){ + $sep = (preg_match("/\/$/", $dir)) ? "" : "/"; + $out[] = $dir.$sep.$entry ; + } + return $out; +} + +function get_highest_temp($hdds){ + $highest_temp="0"; + foreach ($hdds as $hdd) { + if (shell_exec("hdparm -C ${hdd} 2>/dev/null| grep -c standby") == 0){ + $temp = preg_replace("/\s+/", "", shell_exec("smartctl -A ${hdd} 2>/dev/null| grep -m 1 -i Temperature_Celsius | awk '{print $10}'")); + $highest_temp = ($temp > $highest_temp) ? $temp : $highest_temp; + } + } + debug("Highest temp is ${highest_temp}ºC"); + return $highest_temp; +} + +function get_all_hdds(){ + $hdds = array(); + $flash = preg_replace("/\d$/", "", realpath("/dev/disk/by-label/UNRAID")); + foreach (scan_dir("/dev/") as $dev) { + if(preg_match("/[sh]d[a-z]+$/", $dev) && $dev != $flash) { + $hdds[] = $dev; + } + } + return $hdds; +} + +$hdds = get_all_hdds(); +while(TRUE){ while(TRUE){ +#### DO YOUR STUFF HERE #### + +# Load config file or die +if(is_file($config_file)){ + $params = (is_file($config_file)) ? parse_ini_file($config_file) : array(); + extract($params, EXTR_OVERWRITE); +} else { + unlink($lockfile); + exit(1); +} + +# Wait probes to become ready +if (! is_file($PWM_CONTROLLER) || ! is_file($PWM_FAN)){ + sleep(15); + continue; +} + +# Set PWM_HIGH and PWM_OFF +$PWM_HIGH = 255; +$PWM_OFF = $PWM_LOW-5; + +# Disable fan mininum RPM +$FAN_RPM_MIN = file(split("_", $PWM_FAN)[0]."_min", FILE_IGNORE_NEW_LINES)[0]; +$D_FAN_RPM_MIN = ($FAN_RPM_MIN > 0) ? $FAN_RPM_MIN : FALSE; + +# Enable speed change on fan +$PWM_MODE=file("${PWM_CONTROLLER}_enable", FILE_IGNORE_NEW_LINES)[0]; +if($PWM_MODE != 1){ + $DEFAULT_PWM_MODE = $PWM_MODE; + file_put_contents("${PWM_CONTROLLER}_enable", 1); +} + +# Calculate size of increments. +$TEMP_INCREMENTS = $TEMP_HIGH-$TEMP_LOW; +$PWM_INCREMENTS = round(($PWM_HIGH-$PWM_LOW)/$TEMP_INCREMENTS); +# Get current fan rpm and pwm +$CURRENT_PWM = preg_replace("/\s*/", "", file_get_contents($PWM_CONTROLLER)); +$CURRENT_RPM = preg_replace("/\s*/", "", file_get_contents($PWM_FAN)); +# Get current fan PWM percentage +$CURRENT_PERCENT_PWM = round(($CURRENT_PWM*100)/$PWM_HIGH); +$CURRENT_OUTPUT = "${CURRENT_PWM} (${CURRENT_PERCENT_PWM}% @ ${CURRENT_RPM}rpm)"; + +# Calculate a new scenario +# Get highest drive temperature +$HIGHEST_TEMP = get_highest_temp($hdds); +if ($HIGHEST_TEMP <= $TEMP_LOW){ + $NEW_PWM = $PWM_OFF; + $NEW_PERCENT_PWM = 0; +} else if ($HIGHEST_TEMP >= $TEMP_HIGH){ + $NEW_PWM = $PWM_HIGH; + $NEW_PERCENT_PWM = 100; +} else { + $NEW_PWM = (($HIGHEST_TEMP-$TEMP_LOW)*$PWM_INCREMENTS)+$PWM_LOW; + $NEW_PERCENT_PWM = round(($NEW_PWM*100)/$PWM_HIGH); +} + +# Change the fan speed as needed +if ($CURRENT_PWM != $NEW_PWM){ + file_put_contents($PWM_CONTROLLER, $NEW_PWM); + sleep(5); + $NEW_RPM = preg_replace("/\s*/", "", file_get_contents($PWM_FAN)); + $NEW_PERCENT_PWM = round(($NEW_PWM*100)/$PWM_HIGH); + $NEW_OUTPUT = "${NEW_PWM} (${NEW_PERCENT_PWM}% @ ${NEW_RPM}rpm)"; + syslog(LOG_INFO, "highest disk temp is ${HIGHEST_TEMP}ºC, adjusting fan PWM from: $CURRENT_OUTPUT to: $NEW_OUTPUT"); +} + +# PRINT VARIABLES DEBUG +$defined_vars = get_defined_vars(); +foreach (array("_GET","_POST","_COOKIE","_FILES","argv","argc","_SERVER") as $i) {unset($defined_vars[$i]);} +debug("\nDECLARED VARIABLES:\n".print_r($defined_vars, true)); +unset($defined_vars); + +$time1 = time(); +$MD5 = shell_exec("md5sum $config_file|awk '{print $1}'"); +$MD5 = md5_file($config_file); +for ($i=0; $i < $INTERVAL*6 ; $i++) { + sleep(10); + if (md5_file($config_file) != $MD5){syslog(LOG_INFO, "config file updated, reloading."); $i=10000;} +} +debug("Sleeped ".(time()-$time1)." seconds."); + +###### END OF SECTION ###### +};};?> + diff --git a/source/ipmitool/usr/local/emhttp/plugins/ipmitool/include/ipmitool_array.php b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/include/ipmitool_array.php new file mode 100644 index 00000000..d1381261 --- /dev/null +++ b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/include/ipmitool_array.php @@ -0,0 +1,10 @@ +/dev/null"; +exec($command, $output); +$array = array(); +for ($i = 0; $i < sizeof($output); $i++) { + $value = explode(",", $output[$i]); + $array[] = $value; +} +echo json_encode($array); +?> diff --git a/source/ipmitool/usr/local/emhttp/plugins/ipmitool/ipmifan b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/ipmifan new file mode 100755 index 00000000..d8cb06cf --- /dev/null +++ b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/ipmifan @@ -0,0 +1,285 @@ +#!/bin/bash +#======================================================================================= +# Name: autofan +#======================================================================================= +# Description: +# +# A simple script to check for the highest hard disk temperature and then set the +# fan to an apropriate speed. Fan needs to be connected to a motherboard with pwm +# support. +# +# How to invoke in your "go" script (copy to /boot/scripts): +# chmod +x /boot/scripts/fan_speed.sh -- superseeded by Dynamix plugin +# /boot/fan_speed.sh -- superseeded by Dynamix plugin +#======================================================================================= +# Version 1.0 Authored by Aiden +# Version 1.1 Modified by Dan Stroot to run in a loop. Does not require the user +# to add this to cron - just start it in your go file. +# Version 1.2 Modified by Guzzi - removed -d ata to work on sas controllers +# Version 1.3 Modified by gfjardim - added to dynamix.system.autofan +# Version 1.4 Modified by Bergware - added additional line parameters +# start/stop control by Dynamix plugin +#======================================================================================= +# Dependencies: grep,awk,smartctl,hdparm +#======================================================================================= +version=1.4 +program_name="autofan" +usage() { + echo "Usage: $program_name [-t min_temp] [-T max_temp] [-m loop in minutes] [-c controller] [-f fan] [-l low]" + echo " $program_name -V = print program version " + echo " $program_name -q = quit the program if it is running" + echo + echo " Argument descriptions:" + echo " -t NN = set the low disk temp, below this temp the fan is off (default=35)" + echo " -T NN = set the high disk temp, above this temp the fan is 100% (default=45)" + echo " -c name = specify name of the controller (default=/sys/class/hwmon/hwmon2/pwm1)" + echo " -f name = specify name of the fan (default=/sys/class/hwmon/hwmon2/fan1_input)" + echo " -l NN = set value to slow down the fan speed (default=35)" + echo " -m NN = number of minutes to wait between fan speed changes (default=5)" +} +#======================================================================================= +# USER DEFINED VARIABLES: *MUST* BE SET TO *YOUR* VALUES +#======================================================================================= +# Fan device. Depends on *your* system. pwmconfig can help with finding this out. +# pwm1 is usually the cpu fan. You can "cat /sys/class/hwmon/hwmon1/device/fan4_input" +# to see the current rpm of the fan. If 0 then fan is off or there is no fan connected +# or motherboard can't read rpm of fan. +PWM_CONTROLLER=/sys/class/hwmon/hwmon2/pwm1 # Power (speed) setting +PWM_FAN=/sys/class/hwmon/hwmon2/fan1_input # Used to track actual rpm values + +# Temperature boundaries +TEMP_LOW=35 # Anything this number and below - fan is *off* +TEMP_HIGH=45 # Anything this number and above - fan is *full* + +# Fan speed settings. Run pwmconfig (part of the lm_sensors package) to determine +# what numbers you want to use for your fan pwm settings. Should not need to +# change the OFF variable, only the LOW and maybe also HIGH to what you desire. +# Any real number between 0 and 255. +PWM_OFF=30 # Off Value (note: many PWM fans will not turn off) +PWM_LOW=35 # Value to make your fan go slow +PWM_HIGH=255 # Value to make your fan go fast + +INTERVAL=5 # The default number of minutes to loop +#======================================================================================= +# PROGRAM LOGIC - CHANGE AT YOUR PERIL ;) +#======================================================================================= +# Get User Input +while getopts "t:T:m:c:f:l:qhV" opt; do + case $opt in + t) TEMP_LOW=$OPTARG ;; + T) TEMP_HIGH=$OPTARG ;; + m) INTERVAL=$OPTARG ;; + c) PWM_CONTROLLER=$OPTARG ;; + f) PWM_FAN=$OPTARG ;; + l) PWM_LOW=$OPTARG ;; + V) echo $program_name version: $version ; exit 0 ;; + h) usage >&2 ; exit 0 ;; + q) quit_flag="yes" ;; + \?) usage >&2 ; exit 0 ;; + esac +done + +show_values() { + echo TEMP_LOW=$TEMP_LOW + echo TEMP_HIGH=$TEMP_HIGH + echo PWM_OFF=$PWM_OFF + echo PWM_LOW=$PWM_LOW + echo PWM_HIGH=$PWM_HIGH + echo INTERVAL=$INTERVAL +} +#show_values # uncomment for debugging + +# validate the fan off temp +cc="$(echo $TEMP_LOW | sed 's/[0-9]//g')" +if [[ ! -z $cc ]]; then + echo "Error: min fan temp must be numeric (whole number, not negative)." >&2 + usage >&2 + exit 2 +fi + +# validate the fan high temp +cc="$(echo $TEMP_HIGH | sed 's/[0-9]//g')" +if [[ ! -z $cc ]]; then + echo "Error: max fan temp must be numeric (whole number, not negative)." >&2 + usage >&2 + exit 2 +fi + +# validate the minutes +cc="$(echo $INTERVAL | sed 's/[0-9]//g')" +if [[ ! -z $cc ]]; then + echo "Error: minutes must be numeric (whole number, not negative)." >&2 + usage >&2 + exit 2 +fi + +# Lockfile processing +lockfile="/var/run/${program_name}.pid" +if [[ -f $lockfile ]]; then + # The file exists so read the PID + # to see if it is still running + lock_pid=`head -n 1 "${lockfile}"` + pid_running=`ps -p "${lock_pid}" | grep ${lock_pid}` + + if [[ -z $pid_running ]]; then + if [[ $quit_flag == no ]]; then + # The process is not running + # Echo current PID into lock file + echo $$ > "${lockfile}" + else + echo "$program_name ${lock_pid} is not currently running " + rm "${lockfile}" + fi + else + if [[ $quit_flag == yes ]]; then + echo killing $program_name process "$lock_pid" + echo killing $program_name process "$lock_pid" | logger -t$program_name + kill "$lock_pid" + rm "${lockfile}" + exit 0 + else + echo "$program_name is already running [${lock_pid}]" + exit 2 + fi + fi +else + if [[ $quit_flag == yes ]]; then + echo "$program_name not currently running " + exit 0 + else + echo $$ > "${lockfile}" + fi +fi + +# Obtain the ID of your flash drive (your flash drive is named "UnRaid" right?) +flash=/dev/`ls -l /dev/disk/by-label| grep UNRAID | cut -d/ -f3 | cut -c 1-3` + +# Count the number of drives in your array (ignoring the flash drive we identified) +NUM_OF_DRIVES=$(ls /dev/[hs]d? | grep -v "$flash" | wc -l) + +# Identify the drives in your array so we can test their temperature +COUNT=1 +for d in $(ls /dev/[hs]d? | grep -v "$flash"); do + HD[$COUNT]=$d + #echo HDD=${HD[$COUNT]} # Uncomment for debugging + COUNT=$[$COUNT+1] +done + +function_get_highest_hd_temp() { + HIGHEST_TEMP=0 + for DISK in "${HD[@]}"; do + SLEEPING=`hdparm -C ${DISK} | grep -c standby` + if [[ $SLEEPING -eq 0 ]]; then + CURRENT_TEMP=`smartctl -A ${DISK} | grep -m 1 -i Temperature_Celsius | awk '{print $10}'` + if [[ $HIGHEST_TEMP -le $CURRENT_TEMP ]]; then + HIGHEST_TEMP=$CURRENT_TEMP + fi + fi + done +} + +function_get_current_fan_speed() { + # Function to get current fan values + CURRENT_FAN_SPEED=`cat $PWM_CONTROLLER` + CURRENT_FAN_RPM=`cat $PWM_FAN` + CURRENT_PERCENT_SPEED=$(($(($CURRENT_FAN_SPEED*100))/$PWM_HIGH)) + #echo Current Fan Speed = $CURRENT_FAN_SPEED # Uncomment for debugging + #echo Current Fan RPM = $CURRENT_FAN_RPM # Uncomment for debugging + #echo Current Percent Speed = $CURRENT_PERCENT_SPEED # Uncomment for debugging + if [[ $CURRENT_FAN_SPEED -le $PWM_OFF ]]; then + CURRENT_OUTPUT="OFF (0% @ 0rpm)" + #echo Current output = $CURRENT_OUTPUT # Uncomment for debugging + else + if [[ $CURRENT_FAN_SPEED -ge $PWM_HIGH ]]; then + CURRENT_OUTPUT="FULL (100% @ ${CURRENT_FAN_RPM}rpm)" + #echo Current output = $CURRENT_OUTPUT # Uncomment for debugging + else + CURRENT_OUTPUT="$CURRENT_FAN_SPEED (${CURRENT_PERCENT_SPEED}% @ ${CURRENT_FAN_RPM}rpm)" + #echo Current output = $CURRENT_OUTPUT # Uncomment for debugging + fi + fi +} + +function_calc_new_fan_speed() { + # Calculate new fan values based on highest drive temperature + if [[ $HIGHEST_TEMP -le $TEMP_LOW ]]; then + ADJUSTED_FAN_SPEED=$PWM_OFF + ADJUSTED_PERCENT_SPEED=0 + ADJUSTED_OUTPUT="OFF" + else + if [[ $HIGHEST_TEMP -ge $TEMP_HIGH ]]; then + ADJUSTED_FAN_SPEED=$PWM_HIGH + ADJUSTED_PERCENT_SPEED=100 + ADJUSTED_OUTPUT="FULL" + else + ADJUSTED_FAN_SPEED=$(($(($(($HIGHEST_TEMP-$TEMP_LOW))*$FAN_PWM_INCREMENTS))+$PWM_LOW)) + ADJUSTED_PERCENT_SPEED=$(($(($ADJUSTED_FAN_SPEED*100))/$PWM_HIGH)) + ADJUSTED_OUTPUT=$ADJUSTED_FAN_SPEED + fi + fi + #echo Adjusted output = $ADJUSTED_OUTPUT # Uncomment for debugging +} + +function_change_fan_speed() { + # Implemenent fan speed change if neeeded + if [[ $CURRENT_FAN_SPEED -ne $ADJUSTED_FAN_SPEED ]]; then + # Enable speed change on fan + if [[ `cat ${PWM_CONTROLLER}_enable` -ne 1 ]]; then + echo 1 > "${PWM_CONTROLLER}_enable" + fi + # set fan to new value + echo $ADJUSTED_FAN_SPEED > $PWM_CONTROLLER + sleep 5 + # Get new rpm value + ADJUSTED_FAN_RPM=`cat $PWM_FAN` + ADJUSTED_OUTPUT="${ADJUSTED_OUTPUT} (${ADJUSTED_PERCENT_SPEED}% @ ${ADJUSTED_FAN_RPM}rpm)" + # Output the change + echo "$program_name: Highest disk temp is ${HIGHEST_TEMP}°C, adjusting fan speed from: $CURRENT_OUTPUT to: $ADJUSTED_OUTPUT" + echo "Highest disk temp is ${HIGHEST_TEMP}°C, adjusting fan speed from: $CURRENT_OUTPUT to: $ADJUSTED_OUTPUT" | logger -t$program_name + fi +} + +# Main Loop +while [[ -f $lockfile ]]; do + # Just wait if modules aren't loaded + if [[ ! -f $PWM_FAN || ! -f $PWM_CONTROLLER ]]; then + sleep $(($INTERVAL*60)); + continue; + fi + + # Set PWM_OFF + PWM_OFF=$(($PWM_LOW-5)) # Off Value (note: many PWM fans will not turn off) + + # Disable fan mininum RPM + FAN_RPM_MIN=$(echo ${PWM_FAN} | cut -d"_" -f1)"_min" + if [[ $(cat $FAN_RPM_MIN) -ne 0 ]]; then + echo 0 > $FAN_RPM_MIN + fi + + # Calculate size of increments. + FAN_TEMP_INCREMENTS=$(($TEMP_HIGH-$TEMP_LOW)) + FAN_PWM_INCREMENTS=$(($(($PWM_HIGH-$PWM_LOW))/$FAN_TEMP_INCREMENTS)) + + # Get highest drive temperature + function_get_highest_hd_temp + + # Get current fan speed + function_get_current_fan_speed + + # Calculate new fan speed + function_calc_new_fan_speed + + # Change fan speed if necessary + function_change_fan_speed + + #echo Sleeping for $INTERVAL minutes # uncomment for debugging + sleep $(($INTERVAL*60)) +done & + +# while loop was put into background, now disown it so it will continue to run when you log off. +# to get it to stop, type: rm /var/lock/fan_speed.LCK +background_pid=$! +echo $background_pid > "${lockfile}" +echo "$program_name process ID $background_pid started, To terminate it, type: $program_name -q" >&2 +echo "$program_name process ID $background_pid started, To terminate it, type: $program_name -q" | logger -t$program_name +disown %% diff --git a/source/ipmitool/usr/local/emhttp/plugins/ipmitool/javascript/jquery.ipmitool.js b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/javascript/jquery.ipmitool.js new file mode 100644 index 00000000..dd260c3a --- /dev/null +++ b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/javascript/jquery.ipmitool.js @@ -0,0 +1,172 @@ +$(function(){ + sensorArray(false); + eventArray(); + + //bind Clear button to clearEvents function + $("#btnClearEvents").bind("click", clearEvents); + + //advanced view switch + $('.advancedview').switchButton({ + labels_placement: "left", + on_label: 'Advanced View', + off_label: 'Basic View', + checked: $.cookie('ipmitool_sensor_mode') == 'advanced' + }); + + //set cookie and toggle advanced columns + $('.advancedview').change(function () { + $('.advanced').toggle('slow'); + $.cookie('ipmitool_sensor_mode', $('.advancedview').prop('checked') ? 'advanced' : 'basic', { expires: 3650 }); + }); + + $('#allEvents').click(function(event) { //on click + if(this.checked) { // check select status + $('.checkEvent').each(function() { //loop through each checkbox + this.checked = true; //select all checkboxes with class "checkEvent" + }); + }else{ + $('.checkEvent').each(function() { //loop through each checkbox + this.checked = false; //deselect all checkboxes with class "checkEvent" + }); + } + }); + + $('#tblSensor').tablesorter({headers:{0:{sorter:false}}}); + $('#tblEvent').tablesorter({headers:{0:{sorter:false}}}); + sensorRefresh(); +}); + +function clearEvents() { + //if all events checked clear all + if($('#allEvents').prop('checked')) { + $('#allEvents').attr('checked', false); + $.ajax({ + type : "POST", + url : "/plugins/ipmitool/include/delete_event.php", + data : {options: "clear" + Options + atob(Password)}, + success: function(data) { + $("#tblEvent tbody").empty(); + }, + error : function() { } + }); + } else { + // clear only checked events + $(':checkbox:checked').each(function(){ + var EventId = $(this).val(); //get event id + var par = $(this).parent().parent(); //get table row + $.ajax({ + type : "POST", + url : "/plugins/ipmitool/include/delete_event.php", + data : {options: "delete " + EventId + Options + atob(Password)}, + success: function(data) { + par.remove(); //remove table row + }, + error : function() { } + }); + }); + } +}; + +//sensor refresh +(function sensorRefresh() { + sensorArray(true); + setTimeout(sensorRefresh, 30000); +}()); + +//load ipmi sensor table +function sensorArray(Refresh){ + var Display = 'none'; + + if ($.cookie('ipmitool_sensor_mode') == 'advanced') { + $('.advanced').show(); + Display = 'table-cell'; + } + $.ajax({ + type: "POST", + dataType: "json", + url: "/plugins/ipmitool/include/ipmitool_array.php", + data : {options: "-vc sdr" + Options + atob(Password)}, + success: function(data) { + $.each(data, function (i, val) { + if (data[i][3] != "ns") { + var Reading = data[i][1]; + var LowerNonRec = data[i][13]; + var LowerCritical = data[i][14]; + var LowerNonCrit = data[i][15]; + var UpperNonCrit = data[i][12]; + var UpperCritical = data[i][11]; + var UpperNonRec = data[i][10]; + var Color = "green"; + var Name = data[i][0].replace('+', 'plus_').replace('-', 'minus_').replace(' ', '_').replace('.', '_'); + + if (data[i][6]=="Voltage"){ + if (parseFloat(Reading) > parseFloat(LowerNonRec) && parseFloat(Reading) < parseFloat(UpperNonRec)) + Color = "red"; + if (parseFloat(Reading) > parseFloat(LowerCritical) && parseFloat(Reading) < parseFloat(UpperCritical)) + Color = "yellow"; + if (parseFloat(Reading) > parseFloat(LowerNonCrit) && parseFloat(Reading) < parseFloat(UpperNonCrit)) + Color = "green"; + } else if (data[i][6]=="Fan"){ + if (parseInt(Reading) < parseInt(LowerNonCrit)) + Color = "red"; + } else if (data[i][6]=="Temperature"){ + if (parseInt(Reading) > parseInt(UpperNonCrit)) + Color = "red"; + } + + if (Refresh) { + $("#" + Name + " td.reading").html("" + Reading + ""); + } else { + $("#tblSensor tbody").append( + ""+ + ""+ //status + ""+data[i][0]+""+ //sensor name + "" + LowerNonRec + ""+ + "" + LowerCritical + ""+ + "" + LowerNonCrit + ""+ + "" + "" + Reading + "" + ""+ //sensor reading + ""+data[i][2]+""+ //sensor units + "" + UpperNonCrit + ""+ + "" + UpperCritical + ""+ + "" + UpperNonRec + ""+ + ""); + } + } + }); + $("#tblSensor").trigger("update"); //update sensor table for tablesorter + }, + error : function() {}, + cache: false + }); +}; + +//load ipmi event table +function eventArray(){ + $("#tblEvent tbody").empty(); + + $.ajax({ + type: "POST", + dataType: "json", + url: "/plugins/ipmitool/include/ipmitool_array.php", + data : {options: "-c sel elist" + Options + atob(Password)}, + success: function(data) { + $.each(data, function (i, val) { + var Status = (data[i][5] == 'Asserted') ? 'red' : 'green'; + var Sensor = (data[i][3]).split(" "); // separate sensor name and type + $("#tblEvent tbody").append( + ""+ + " "+ //checkbox + ""+ //status + "" + data[i][0] + ""+ //event id + "" + data[i][1] + " "+data[i][2]+""+ //time stamp + "" + Sensor[1] + ""+ //sensor name + "" + Sensor[0] + ""+ //sensor type + "" + data[i][4] +""+ //subject + "" + data[i][6] + ""+ //description + ""); + $("#tblEvent").trigger("update"); //update table for tablesorter + }); + }, + error : function() {} + }); +}; \ No newline at end of file diff --git a/source/ipmitool/usr/local/emhttp/plugins/ipmitool/javascript/jquery.mask.js b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/javascript/jquery.mask.js new file mode 100755 index 00000000..bc9de52c --- /dev/null +++ b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/javascript/jquery.mask.js @@ -0,0 +1,482 @@ +/** + * jquery.mask.js + * @version: v1.11.3 + * @author: Igor Escobar + * + * Created by Igor Escobar on 2012-03-10. Please report any bug at http://blog.igorescobar.com + * + * Copyright (c) 2012 Igor Escobar http://blog.igorescobar.com + * + * The MIT License (http://www.opensource.org/licenses/mit-license.php) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +/*jshint laxbreak: true */ +/* global define */ + +// UMD (Universal Module Definition) patterns for JavaScript modules that work everywhere. +// https://github.com/umdjs/umd/blob/master/jqueryPluginCommonjs.js +(function (factory) { + if (typeof define === "function" && define.amd) { + // AMD. Register as an anonymous module. + define(["jquery"], factory); + } else if (typeof exports === 'object') { + // Node/CommonJS + factory(require('jquery')); + } else { + // Browser globals + factory(window.jQuery || window.Zepto); + } +}(function ($) { + "use strict"; + var Mask = function (el, mask, options) { + el = $(el); + + var jMask = this, old_value = el.val(), regexMask; + + mask = typeof mask === "function" ? mask(el.val(), undefined, el, options) : mask; + + var p = { + invalid: [], + getCaret: function () { + try { + var sel, + pos = 0, + ctrl = el.get(0), + dSel = document.selection, + cSelStart = ctrl.selectionStart; + + // IE Support + if (dSel && navigator.appVersion.indexOf("MSIE 10") === -1) { + sel = dSel.createRange(); + sel.moveStart('character', el.is("input") ? -el.val().length : -el.text().length); + pos = sel.text.length; + } + // Firefox support + else if (cSelStart || cSelStart === '0') { + pos = cSelStart; + } + + return pos; + } catch (e) {} + }, + setCaret: function(pos) { + try { + if (el.is(":focus")) { + var range, ctrl = el.get(0); + + if (ctrl.setSelectionRange) { + ctrl.setSelectionRange(pos,pos); + } else if (ctrl.createTextRange) { + range = ctrl.createTextRange(); + range.collapse(true); + range.moveEnd('character', pos); + range.moveStart('character', pos); + range.select(); + } + } + } catch (e) {} + }, + events: function() { + el + .on('keyup.mask', p.behaviour) + .on("paste.mask drop.mask", function() { + setTimeout(function() { + el.keydown().keyup(); + }, 100); + }) + .on('change.mask', function(){ + el.data('changed', true); + }) + .on("blur.mask", function(){ + if (old_value !== el.val() && !el.data('changed')) { + el.trigger("change"); + } + el.data('changed', false); + }) + // it's very important that this callback remains in this position + // otherwhise old_value it's going to work buggy + .on('keydown.mask, blur.mask', function() { + old_value = el.val(); + }) + // select all text on focus + .on('focus.mask', function (e) { + if (options.selectOnFocus === true) { + $(e.target).select(); + } + }) + // clear the value if it not complete the mask + .on("focusout.mask", function() { + if (options.clearIfNotMatch && !regexMask.test(p.val())) { + p.val(''); + } + }); + }, + getRegexMask: function() { + var maskChunks = [], translation, pattern, optional, recursive, oRecursive, r; + + for (var i = 0; i < mask.length; i++) { + translation = jMask.translation[mask.charAt(i)]; + + if (translation) { + + pattern = translation.pattern.toString().replace(/.{1}$|^.{1}/g, ""); + optional = translation.optional; + recursive = translation.recursive; + + if (recursive) { + maskChunks.push(mask.charAt(i)); + oRecursive = {digit: mask.charAt(i), pattern: pattern}; + } else { + maskChunks.push(!optional && !recursive ? pattern : (pattern + "?")); + } + + } else { + maskChunks.push(mask.charAt(i).replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')); + } + } + + r = maskChunks.join(""); + + if (oRecursive) { + r = r.replace(new RegExp("(" + oRecursive.digit + "(.*" + oRecursive.digit + ")?)"), "($1)?") + .replace(new RegExp(oRecursive.digit, "g"), oRecursive.pattern); + } + + return new RegExp(r); + }, + destroyEvents: function() { + el.off(['keydown', 'keyup', 'paste', 'drop', 'blur', 'focusout', ''].join('.mask ')); + }, + val: function(v) { + var isInput = el.is('input'), + method = isInput ? 'val' : 'text', + r; + + if (arguments.length > 0) { + if (el[method]() !== v) { + el[method](v); + } + r = el; + } else { + r = el[method](); + } + + return r; + }, + getMCharsBeforeCount: function(index, onCleanVal) { + for (var count = 0, i = 0, maskL = mask.length; i < maskL && i < index; i++) { + if (!jMask.translation[mask.charAt(i)]) { + index = onCleanVal ? index + 1 : index; + count++; + } + } + return count; + }, + caretPos: function (originalCaretPos, oldLength, newLength, maskDif) { + var translation = jMask.translation[mask.charAt(Math.min(originalCaretPos - 1, mask.length - 1))]; + + return !translation ? p.caretPos(originalCaretPos + 1, oldLength, newLength, maskDif) + : Math.min(originalCaretPos + newLength - oldLength - maskDif, newLength); + }, + behaviour: function(e) { + e = e || window.event; + p.invalid = []; + var keyCode = e.keyCode || e.which; + if ($.inArray(keyCode, jMask.byPassKeys) === -1) { + + var caretPos = p.getCaret(), + currVal = p.val(), + currValL = currVal.length, + changeCaret = caretPos < currValL, + newVal = p.getMasked(), + newValL = newVal.length, + maskDif = p.getMCharsBeforeCount(newValL - 1) - p.getMCharsBeforeCount(currValL - 1); + + p.val(newVal); + + // change caret but avoid CTRL+A + if (changeCaret && !(keyCode === 65 && e.ctrlKey)) { + // Avoid adjusting caret on backspace or delete + if (!(keyCode === 8 || keyCode === 46)) { + caretPos = p.caretPos(caretPos, currValL, newValL, maskDif); + } + p.setCaret(caretPos); + } + + return p.callbacks(e); + } + }, + getMasked: function (skipMaskChars) { + var buf = [], + value = p.val(), + m = 0, maskLen = mask.length, + v = 0, valLen = value.length, + offset = 1, addMethod = "push", + resetPos = -1, + lastMaskChar, + check; + + if (options.reverse) { + addMethod = "unshift"; + offset = -1; + lastMaskChar = 0; + m = maskLen - 1; + v = valLen - 1; + check = function () { + return m > -1 && v > -1; + }; + } else { + lastMaskChar = maskLen - 1; + check = function () { + return m < maskLen && v < valLen; + }; + } + + while (check()) { + var maskDigit = mask.charAt(m), + valDigit = value.charAt(v), + translation = jMask.translation[maskDigit]; + + if (translation) { + if (valDigit.match(translation.pattern)) { + buf[addMethod](valDigit); + if (translation.recursive) { + if (resetPos === -1) { + resetPos = m; + } else if (m === lastMaskChar) { + m = resetPos - offset; + } + + if (lastMaskChar === resetPos) { + m -= offset; + } + } + m += offset; + } else if (translation.optional) { + m += offset; + v -= offset; + } else if (translation.fallback) { + buf[addMethod](translation.fallback); + m += offset; + v -= offset; + } else { + p.invalid.push({p: v, v: valDigit, e: translation.pattern}); + } + v += offset; + } else { + if (!skipMaskChars) { + buf[addMethod](maskDigit); + } + + if (valDigit === maskDigit) { + v += offset; + } + + m += offset; + } + } + + var lastMaskCharDigit = mask.charAt(lastMaskChar); + if (maskLen === valLen + 1 && !jMask.translation[lastMaskCharDigit]) { + buf.push(lastMaskCharDigit); + } + + return buf.join(""); + }, + callbacks: function (e) { + var val = p.val(), + changed = val !== old_value, + defaultArgs = [val, e, el, options], + callback = function(name, criteria, args) { + if (typeof options[name] === "function" && criteria) { + options[name].apply(this, args); + } + }; + + callback('onChange', changed === true, defaultArgs); + callback('onKeyPress', changed === true, defaultArgs); + callback('onComplete', val.length === mask.length, defaultArgs); + callback('onInvalid', p.invalid.length > 0, [val, e, el, p.invalid, options]); + } + }; + + + // public methods + jMask.mask = mask; + jMask.options = options; + jMask.remove = function() { + var caret = p.getCaret(); + p.destroyEvents(); + p.val(jMask.getCleanVal()); + p.setCaret(caret - p.getMCharsBeforeCount(caret)); + return el; + }; + + // get value without mask + jMask.getCleanVal = function() { + return p.getMasked(true); + }; + + jMask.init = function(only_mask) { + only_mask = only_mask || false; + options = options || {}; + + jMask.byPassKeys = $.jMaskGlobals.byPassKeys; + jMask.translation = $.jMaskGlobals.translation; + + jMask.translation = $.extend({}, jMask.translation, options.translation); + jMask = $.extend(true, {}, jMask, options); + + regexMask = p.getRegexMask(); + + if (only_mask === false) { + + if (options.placeholder) { + el.attr('placeholder' , options.placeholder); + } + + // autocomplete needs to be off. we can't intercept events + // the browser doesn't fire any kind of event when something is + // selected in a autocomplete list so we can't sanitize it. + el.attr('autocomplete', 'off'); + p.destroyEvents(); + p.events(); + + var caret = p.getCaret(); + p.val(p.getMasked()); + p.setCaret(caret + p.getMCharsBeforeCount(caret, true)); + + } else { + p.events(); + p.val(p.getMasked()); + } + }; + + jMask.init(!el.is("input")); + }; + + $.maskWatchers = {}; + var HTMLAttributes = function () { + var input = $(this), + options = {}, + prefix = "data-mask-", + mask = input.attr('data-mask'); + + if (input.attr(prefix + 'reverse')) { + options.reverse = true; + } + + if (input.attr(prefix + 'clearifnotmatch')) { + options.clearIfNotMatch = true; + } + + if (input.attr(prefix + 'selectonfocus') === 'true') { + options.selectOnFocus = true; + } + + if (notSameMaskObject(input, mask, options)) { + return input.data('mask', new Mask(this, mask, options)); + } + }, + notSameMaskObject = function(field, mask, options) { + options = options || {}; + var maskObject = $(field).data('mask'), + stringify = JSON.stringify, + value = $(field).val() || $(field).text(); + try { + if (typeof mask === "function") { + mask = mask(value); + } + return typeof maskObject !== "object" || stringify(maskObject.options) !== stringify(options) || maskObject.mask !== mask; + } catch (e) {} + }; + + + $.fn.mask = function(mask, options) { + options = options || {}; + var selector = this.selector, + globals = $.jMaskGlobals, + interval = $.jMaskGlobals.watchInterval, + maskFunction = function() { + if (notSameMaskObject(this, mask, options)) { + return $(this).data('mask', new Mask(this, mask, options)); + } + }; + + $(this).each(maskFunction); + + if (selector && selector !== "" && globals.watchInputs) { + clearInterval($.maskWatchers[selector]); + $.maskWatchers[selector] = setInterval(function(){ + $(document).find(selector).each(maskFunction); + }, interval); + } + return this; + }; + + $.fn.unmask = function() { + clearInterval($.maskWatchers[this.selector]); + delete $.maskWatchers[this.selector]; + return this.each(function() { + var dataMask = $(this).data('mask'); + if (dataMask) { + dataMask.remove().removeData('mask'); + } + }); + }; + + $.fn.cleanVal = function() { + return this.data('mask').getCleanVal(); + }; + + $.applyDataMask = function() { + $(document).find($.jMaskGlobals.maskElements).filter(globals.dataMaskAttr).each(HTMLAttributes); + } + + var globals = { + maskElements: 'input,td,span,div', + dataMaskAttr: '*[data-mask]', + dataMask: true, + watchInterval: 300, + watchInputs: true, + watchDataMask: false, + byPassKeys: [9, 16, 17, 18, 36, 37, 38, 39, 40, 91], + translation: { + '0': {pattern: /\d/}, + '9': {pattern: /\d/, optional: true}, + '#': {pattern: /\d/, recursive: true}, + 'A': {pattern: /[a-zA-Z0-9]/}, + 'S': {pattern: /[a-zA-Z]/} + } + }; + + $.jMaskGlobals = $.jMaskGlobals || {}; + globals = $.jMaskGlobals = $.extend(true, {}, globals, $.jMaskGlobals); + + // looking for inputs with data-mask attribute + if (globals.dataMask) { $.applyDataMask(); } + + setInterval(function(){ + if ($.jMaskGlobals.watchDataMask) { $.applyDataMask(); } + }, globals.watchInterval); +})); diff --git a/source/ipmitool/usr/local/emhttp/plugins/ipmitool/scripts/ipmitail b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/scripts/ipmitail new file mode 100755 index 00000000..0aa89d2e --- /dev/null +++ b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/scripts/ipmitail @@ -0,0 +1,11 @@ +#!/bin/bash +prog="ipmievd: " + +exec /usr/bin/tail -n 0 -F /var/log/syslog | \ + +while read LINE; +do +[[ "$LINE" != *$prog* ]] || [["$LINE" == *"Waiting for events"*]] && continue +sleep 1 | +exec /usr/local/emhttp/webGui/scripts/notify -s "Notice [$HOSTNAME]" -d "$(echo "$LINE" | sed -e 's/.*ipmievd: //')" -i "warning" && continue 2 +done diff --git a/source/ipmitool/usr/local/emhttp/plugins/ipmitool/scripts/start b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/scripts/start new file mode 100755 index 00000000..5a80eda1 --- /dev/null +++ b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/scripts/start @@ -0,0 +1,35 @@ +#!/bin/sh +# read our configuration +source /boot/config/plugins/ipmitool/ipmitool.cfg + +prog="ipmievd" +prog2="ipmitail" +IPMIEVD="/usr/sbin/$prog" +IPMITAIL="/usr/local/emhttp/plugins/ipmitool/scripts/$prog2" +LOCKFILE="/var/lock/$prog" +PIDFILE="/var/run/$prog.pid0" +CONFIG="/boot/config/plugins/ipmitool" +OPTIONS="" + +# no-op if already running +if [ ! -r $PIDFILE ]; then + if [ $REMOTE == "enable" ]; then + OPTIONS="-I lanplus -H $IPADDR -U $USER -P $(echo $PASSWORD | base64 --decode)" + fi + sleep 1 + + nohup $IPMIEVD sel $OPTIONS > /dev/null 2>&1 | logger -tipmitool & + touch $LOCKFILE + TIMER=0 + while [ ! -e $PIDFILE ]; do + sleep 1 + let TIMER=$TIMER+1 + if [ $TIMER -gt 5 ]; then + echo -n "$PIDFILE not created" + break + fi + done + if [ -r $PIDFILE ]; then + nohup $IPMITAIL >/dev/null 2>&1 < /dev/null & + fi +fi diff --git a/source/ipmitool/usr/local/emhttp/plugins/ipmitool/scripts/stop b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/scripts/stop new file mode 100755 index 00000000..283afe19 --- /dev/null +++ b/source/ipmitool/usr/local/emhttp/plugins/ipmitool/scripts/stop @@ -0,0 +1,33 @@ +#!/bin/sh +# read our configuration +source /boot/config/plugins/ipmitool/ipmitool.cfg + +prog="ipmievd" +prog2="ipmitail" +LOCKFILE="/var/lock/$prog" +PIDFILE="/var/run/$prog.pid0" + +# no-op if not running +if [ -r $PIDFILE ]; then +TIMER=0 +while `killall $prog 2>/dev/null`; do + sleep 1 + TIMER=$((TIMER+1)) + if [ $TIMER -ge 30 ]; then + killall -9 $prog + sleep 1 + break + fi +done +TIMER=0 +while `killall $prog2 2>/dev/null`; do + sleep 1 + TIMER=$((TIMER+1)) + if [ $TIMER -ge 30 ]; then + killall -9 $prog2 + sleep 1 + break + fi +done +rm -f $LOCKFILE && rm -f $PIDFILE +fi