pax_global_header00006660000000000000000000000064132410163140014505gustar00rootroot0000000000000052 comment=8cb8c5fe04afbb328a61115c7d5bcc639419110d ledmon-0.90/000077500000000000000000000000001324101631400127135ustar00rootroot00000000000000ledmon-0.90/.gitignore000066400000000000000000000001311324101631400146760ustar00rootroot00000000000000ledctl.8.gz ledmon.conf.5.gz ledmon.8.gz *.o _build ledmon ledctl *~ *.bak .depend *.swp ledmon-0.90/CHANGELOG.md000066400000000000000000000037521324101631400145330ustar00rootroot00000000000000### v0.90 / 2018-02-14 [Commit list](https://github.com/intel/ledmon/compare/v0.80...v0.90) Enhancements * Handle udev events in ledmon. * Possibility to list all controllers detected by LED utilities tool (ledctl --list-controllers). * Configuration file for ledmon advanced features (check man ledmon.config). * Added option to ledctl for managing only listed devices (ledctl --listed-only). * Documentation improvements. Bug fixes * Detecting nvme disks during scan. * Keep failure state after VMD reconnecting. * Blinking failure LED after removing disk from RAID. * Refactoring of SES-2 protocol implementation. SES minor fixes. * Logfile and log levels small improvements. ### v0.80 / 2016-10-28 [Commit list](https://github.com/intel/ledmon/compare/v0.70...v0.80) Enhancements * Support for NVMe SSD devices. * Support for NVMe devices under VMD domains. * Using SMP GPIO_REG_TYPE_TX register for SGPIO. * Sending LED commands optimization. * Documentation improvements. Bug fixes * Fix support for the Dell PCIe SSD devices. * Handling enclosure device name change. * Fixes around IBPI_PATTERN_LOCATE_OFF state. ### v0.70 / 2012-12-12 [Commit list](https://github.com/intel/ledmon/compare/v0.40...v0.70) Enhancements * Introduce SES-2 protocol support. Bug fixes * Minor fixes. * Memory leaks. ### v0.40 / 2012-07-12 [Commit list](https://github.com/intel/ledmon/compare/v0.3...v0.40) Enhancements * Support for Dell backplane bays. * Turn off all unset LEDs in ledctl. Bug fixes * IPBI pattern interpretation. ### v0.3 / 2012-03-06 [Commit list](https://github.com/intel/ledmon/compare/v0.2...v0.3) Enhancements * Support for disk drivers directly attached to SCU HBA. Removals * Remove dependency of smp_utils. ### v0.2 / 2011-08-24 [Commit list](https://github.com/intel/ledmon/compare/af8f20626e4e36cdf4bb9955fc65f22fec155580...v0.2) Enhancements * Ledmon initial version. * Visualize the state of arrays. * Introduce daemon app "ledmon" and LED manual control app "ledctl". ledmon-0.90/COPYING000066400000000000000000000431271324101631400137550ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library 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. Copyright (C) 19yy 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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) 19yy 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. , 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 Library General Public License instead of this License. ledmon-0.90/Makefile000066400000000000000000000025121324101631400143530ustar00rootroot00000000000000# # Intel(R) Enclosure LED Utilities # Copyright (C) 2009-2017 Intel Corporation. # # This program is free software; you can redistribute it and/or modify it # under the terms and conditions of the GNU General Public License, # version 2, as published by the Free Software Foundation. # # This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. # # Installation directory DESTDIR?= .PHONY: ledmon ledctl man all default: all ledmon: $(MAKE) -C src ledmon ledctl: $(MAKE) -C src ledctl man: $(MAKE) -C doc all all: ledmon ledctl man install: all $(MAKE) -C src DESTDIR=$(DESTDIR) install $(MAKE) -C doc DESTDIR=$(DESTDIR) install uninstall: $(MAKE) -C src DESTDIR=$(DESTDIR) uninstall $(MAKE) -C doc DESTDIR=$(DESTDIR) uninstall clean: $(MAKE) -C src clean $(MAKE) -C doc clean mrproper: $(MAKE) -C src mrproper $(MAKE) -C doc clean TAGS_FILES=$(shell ls Makefile src/Makefile */*.[ch]) TAGS: $(TAGS_FILES) @etags $(TAGS_FILES) ledmon-0.90/README000066400000000000000000000025141324101631400135750ustar00rootroot00000000000000This package contains the Enclosure LED Utilities, version 0.90 Copyright (C) 2009-2018 Intel Corporation. All files in this package can be freely distributed and used according to the terms of the GNU General Public License, version 2. See http://www.gnu.org/ for details. 1. Compiling the package. ------------------------- Run "make" command to compile the package. Following packages are required for compiling: a. systemd-devel (libudev) b. sg3_utils-devel 2. (Un)installing the package. -------------------------- Run "make install" to install the package. The default location for ledctl and ledmon applications is /usr/sbin directory. The default location for documentation is /usr/share/man directory. Run "make DESTDIR= install" to prepend 'path' to the default install location. The ledctl and ledmon applications will be installed in $DESTDIR/usr/sbin directory and the manual pages will be installed in $DESTDIR/usr/share/man directory. Run "make uninstall" to uninstall the package from default location. Set DESTDIR variable to uninstall the package from non-default location. 3. Release notes. -------------------------- a. Enclosure LED Utilities is meant as a part of RHEL and SLES linux distributions. b. For backplane enclosures attached to ISCI controller support is limited to Intel(R) Intelligent Backplane. ledmon-0.90/config/000077500000000000000000000000001324101631400141605ustar00rootroot00000000000000ledmon-0.90/config/config.h000066400000000000000000000026751324101631400156100ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2017 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #ifndef _CONFIG_H_INCLUDED_ #define _CONFIG_H_INCLUDED_ #include /** */ #define _HAVE_DMALLOC_H 0 /** * @brief Sets errno variable and returns. * * This macro sets the errno variable and makes function return -1. * * @param[in] __val integer value to be set. * * @return The macro does not return a value. */ #define __set_errno_and_return(__val) { errno = (__val); return -1; } /** */ #define PATH_DELIM '/' /** */ #define PATH_DELIM_STR "/" /** */ #define PATH_SEP ':' /** */ #define PATH_SEP_STR ":" /** */ #define END_LINE ('\n') /** */ #define END_LINE_STR "\n" #endif /* _CONFIG_H_INCLUDED_ */ ledmon-0.90/doc/000077500000000000000000000000001324101631400134605ustar00rootroot00000000000000ledmon-0.90/doc/Makefile000066400000000000000000000036361324101631400151300ustar00rootroot00000000000000# # Intel(R) Enclosure LED Utilities # Copyright (C) 2009-2017 Intel Corporation. # # This program is free software; you can redistribute it and/or modify it # under the terms and conditions of the GNU General Public License, # version 2, as published by the Free Software Foundation. # # This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. # # Installation directory DESTDIR?= MAN_INSTDIR=$(DESTDIR)/usr/share/man MAN8_INSTDIR=$(MAN_INSTDIR)/man8 MAN5_INSTDIR=$(MAN_INSTDIR)/man5 RELEASE_DATE?="February 2018" RELEASE_VER?=0.90 default: all ledmon.conf.5.gz: ledmon.conf.pod pod2man -r "LEDMON.CONF Version $(RELEASE_VER)" -d $(RELEASE_DATE) \ -s 5 -n ledmon.conf -c "Intel(R) Enclosure LED Utilities Config" $< | gzip -9f > $@ ledmon.8.gz: ledmon.pod pod2man -r "LEDMON Version $(RELEASE_VER)" -d $(RELEASE_DATE) \ -s 8 -n ledmon -c "Intel(R) Enclosure LED Monitor Service" $< | gzip -9f > $@ ledctl.8.gz: ledctl.pod pod2man -r "LEDCTL Version $(RELEASE_VER)" -d $(RELEASE_DATE) \ -s 8 -n ledctl -c "Intel(R) Enclosure LED Control Application" $< | gzip -9f > $@ install: all install -D -m 644 ./ledmon.8.gz $(MAN8_INSTDIR)/ledmon.8.gz install -D -m 644 ./ledctl.8.gz $(MAN8_INSTDIR)/ledctl.8.gz install -D -m 644 ./ledmon.conf.5.gz $(MAN5_INSTDIR)/ledmon.conf.5.gz uninstall: rm -f $(MAN8_INSTDIR)/ledmon.8.gz $(MAN8_INSTDIR)/ledctl.8.gz rm -f $(MAN5_INSTDIR)/ledmon.conf.5.gz all: ledmon.conf.5.gz ledmon.8.gz ledctl.8.gz clean: rm -f ledmon.8 ledmon.8.gz ledctl.8 ledctl.8.gz rm -f ledmon.conf.5 ledmon.conf.5.gz ledmon-0.90/doc/ledctl.pod000066400000000000000000000164011324101631400154350ustar00rootroot00000000000000# # Intel(R) Enclosure LED Utilities # Copyright (C) 2009-2017 Intel Corporation. # # This program is free software; you can redistribute it and/or modify it # under the terms and conditions of the GNU General Public License, # version 2, as published by the Free Software Foundation. # # This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. # =head1 NAME ledctl - Intel(R) LED control application for a storage enclosures. =head1 SYNOPSIS B [I] I=I ... =head1 DESCRIPTION The ledctl is an user space application designed to control LEDs associated with each slot in an enclosure or a drive bay. The LEDs of devices listed in I are set to the given pattern I and all other LEDs are turned off. User must have root privileges to use this application. There are two types of systems: 2-LEDs systems (Activity LED, Status LED) and 3-LEDs systems (Activity LED, Locate LED, Fail LED). The ledctl application uses SGPIO and SES-2 protocol to control LEDs. The program implements IBPI patterns of SFF-8489 specification for SGPIO. Please note some enclosures do not stick close to SFF-8489 specification. It might happen that enclosure's processor will accept an IBPI pattern but it will blink the LEDs at variance with SFF-8489 specification or it has limited number of patterns supported. LED management (AHCI) and S protocols are not supported. The ledctl application has been verified to work with Intel(R) storage controllers (i.e. Intel(R) AHCI controller and Intel(R) SAS controller). The application might work with storage controllers of other vendors (especially SCSI/SAS controllers). However, storage controllers of other vendors have not been tested. The ledmon application has the highest priority when accessing LEDs. It means that some patterns set by ledctl may have no effect if ledmon is running (except Locate pattern). The ledctl application is a part of Intel(R) Enclosure LED Utilities. =head2 Pattern Names The ledctl application accepts the following names for I argument according to SFF-8489 specification. =over 8 =item B Turns Locate LED associated with the given device(s) or empty slot(s) on. =item B Turns only Locate LED off. =item B Turns Status LED, Failure LED and Locate LED off. =item B Turns only Status LED and Failure LED off. =item B or B Visualizes "In a Critical Array" pattern. =item B Visualizes "Rebuild" pattern. =item B or B Visualizes "In a Failed Array" pattern. =item B Visualizes "Hotspare" pattern. =item B Visualizes "Predicted Failure Analysis" pattern. =item B or B Visualizes "Failure" pattern. =item B SES-2 R/R ABORD =item B SES-2 REBUILD/REMAP =item B SES-2 IN FAILED ARRAY =item B SES-2 IN CRIT ARRAY =item B SES-2 CONS CHECK =item B SES-2 HOT SPARE =item B SES-2 RSVD DEVICE =item B SES-2 OK =item B SES-2 IDENT =item B SES-2 REMOVE =item B SES-2 INSERT =item B SES-2 MISSING =item B SES-2 DO NOT REMOVE =item B SES-2 ACTIVE =item B SES-2 ENABLE BYP B =item B SES-2 ENABLE BYP A =item B SES-2 DEVICE OFF =item B SES-2 FAULT =item B SES-2 PRDFAIL =back =head2 Patterns Translation When non SES-2 pattern is send to device in enclosure automatic translation is being done. =over 8 =item B I is translated to I =item B I is translated to I<~ses_ident> =item B or B I or I is translated to I =item B or B I or I is translated to I =item B I is translated to I =item B or B I or I is translated to I =item B I is translated to I =item B I is translated to I =item B or B I or I is translated to I =back =head2 List of Devices The application accepts a list of devices in two formats. The first format is a list with comma separated elements. The second format is S in curly braces and elements are separated by space. See examples section bellow for details. A device is a path to file in /dev directory or in /sys/block directory. It may identify a block device, a RAID device or a container device. In case of a RAID device or a container device a state will be set for all block devices associated, respectively. The LEDs of devices listed in I are set to the given pattern I and all other LEDs are turned off (unless --listed-only option is given). =head1 OPTIONS =over 8 =item B<-l> or B<--log>=I Sets a path to local log file. If this option is specified the global log file F is not used. =item B<-h> or B<--help> Prints this text out and exits. =item B<-v> or B<--version> Displays version of ledctl and information about the license and exits. =item B<-L> or B<--list-controllers> Prints information (system path and type) of all controllers detected by ledmon and exits. =item B<-x> or B<--listed-only> With this option ledctl will change state only on devices listed in CLI. The rest of devices will not be touched. =back =head1 FILES =over 8 =item F Global log file, used by all instances of ledctl application. To force logging to user defined file use I<-l> option switch. =back =head1 EXAMPLES The following example illustrates how to locate a single block device. ledctl locate=/dev/sda The following example illustrates how to turn Locate LED off for the same block device. ledctl locate_off=/dev/sda The following example illustrates how to locate disks of a RAID device and how to set rebuild pattern for two block devices at the same time. This example uses both formats of device list. ledctl locate=/dev/md127 rebuild={ /sys/block/sd[a-b] } The following example illustrates how to turn Status LED and Failure LED off for the given device(s). ledctl off={ /dev/sda /dev/sdb } The following example illustrates how to locate a three block devices. This example uses the first format of device list. ledctl locate=/dev/sda,/dev/sdb,/dev/sdc =head1 LICENSE Copyright (c) 2009-2017 Intel Corporation. This program is distributed under the terms of the GNU General Public License as published by the Free Software Foundation. See the built-in help for details on the License and the lack of warranty. =head1 SEE ALSO ledmon(8), ledmon.conf(5) =head1 AUTHOR This manual page was written by Artur Wojcik . It may be used by others. ledmon-0.90/doc/ledmon.conf.pod000066400000000000000000000112321324101631400163650ustar00rootroot00000000000000# # Intel(R) Enclosure LED Utilities # Copyright (C) 2009-2017 Intel Corporation. # # This program is free software; you can redistribute it and/or modify it # under the terms and conditions of the GNU General Public License, # version 2, as published by the Free Software Foundation. # # This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. # =head1 NAME ledmon.conf - Configuration file for Intel(R) Enclosure LED Utilities. =head1 DESCRIPTION The ledmon configuration file allows you to use advanced settings and functions of Intel(R) Enclosure LED Utilities. The global location of the configuration file is F. Instead of a global configuration file, you can specify a local config file using the I<-c> option when running ledmon. =head2 SYNTAX One line should keep exactly one option and value in the configuration file in format: OPTION=VALUE. Any word that begins with a hash sign (B<#>) starts a comment and that word together with the remainder of the line is ignored. Empty lines are allowed. Either single quotes (B<'>) or double quotes (B<">) should not be used. Values are considered as truth: enabled, true, yes, 1. Values are considered as false: disabled, false, no, 0. See also the examples section. =head2 List of configurable options: B - Ledmon will exclude scanning controllers listed on blacklist. When whitelist is also set in config file, the blacklist will be ignored. The controllers should be separated by comma (B<,>) character. B - Related with RAID Initialization (resync), Verify (check) and Verify and Fix (repair) processes. If value is set to true - status LEDs of all member drives will blink with proper pattern if RAID volume is under sync process. If value is set to false, processes like init or verifications will not be reported by LEDs. The default value is true. B - RAID can be migrated between some levels or strip sizes and the flag is related with this processes. Also RAID Grow operation will be reported along with this flag. If value is set to true - status LEDs of all member drives will blink with proper pattern if RAID volume is under reshape. If value is set to false, listed actions will not be reported by LEDs. The default value is true. B - The value is given in seconds. Defines time interval between ledmon sysfs scan. The minimum is 5 seconds the maximum is not specified. The default value is 10 seconds. B - Corresponds with I<--log-level> flag from ledmon. Log level QUIET means no logging at all and ALL means to log everything. The default log level is WARNING. Acceptable values are: quiet, error, warning, info, debug, all. Value also can be set by integer number - 0 means 'quiet' and 5 means 'all'. B - Sets a path to local log file. If this option is specified the global log file F is not used. B - If flag is set to true ledmon will limit monitoring only to drives that are RAID members. The default value is false. B - Flag is related with RAID rebuild process. When value is set to false - only the drive that the RAID is rebuilding to will be marked with appropriate LED pattern. If value is set to true all drives from RAID that is during rebuild will blink during this operation. B - Ledmon will limit changing LED state to controllers listed on whitelist. If any whitelist is set, only devices from list will be scanned by ledmon. The controllers should be separated by comma (B<,>) character. =head1 EXAMPLES =head2 Excluding one controller from ledmon scans, changing log level and scans interval: LOG_LEVEL=all INTERVAL=5 #Exclude disks from SATA controller BLACKLIST=/sys/devices/pci0000:00/0000:00:17.0 =head2 Blink only on RAID members, blink on all disks during rebuild and ignore init phase: RAID_MEMBERS_ONLY=true BLINK_ON_INIT=false REBUILD_BLINK_ON_ALL=true =head1 LICENSE Copyright (c) 2009-2017 Intel Corporation. This program is distributed under the terms of the GNU General Public License as published by the Free Software Foundation. See the built-in help for details on the License and the lack of warranty. =head1 SEE ALSO ledmon(8), ledctl(8) =head1 AUTHOR This manual page was written by Michal Zylowski . It may be used by others. ledmon-0.90/doc/ledmon.pod000066400000000000000000000104231324101631400154420ustar00rootroot00000000000000# # Intel(R) Enclosure LED Utilities # Copyright (C) 2009-2017 Intel Corporation. # # This program is free software; you can redistribute it and/or modify it # under the terms and conditions of the GNU General Public License, # version 2, as published by the Free Software Foundation. # # This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. # =head1 NAME ledmon - Intel(R) LED monitor service for storage enclosures. =head1 SYNOPSIS B [I] =head1 DESCRIPTION The ledmon application is a daemon process used to monitor a state of software RAID devices (md only) or a state of block devices. The state is visualizing on LEDs associated to each slot in an enclosure or a drive bay. There are two types of system: 2-LEDs system (Activity LED, Status LED) and 3-LEDs system (Activity LED, Locate LED, Fail LED). This application has the highest priority when accessing the LEDs. The ledmon application uses SGPIO and SES-2 protocol to control LEDs. The program implements IBPI patterns of SFF-8489 specification for SGPIO. Please note some enclosures do not stick close to SFF-8489 specification. It might happen that enclosure processor will accept IBPI pattern but it will blink LEDs not according to SFF-8489 specification or it has limited number of patterns supported. LED management (AHCI) and SAF-TE protocols are not supported. There's no method provided to specify which RAID volume should be monitored and which not. The ledmon application monitors all RAID devices and visualizes their state. The ledmon application has been verified to work with Intel(R) storage controllers (i.e. Intel(R) AHCI controller and Intel(R) SAS controller). The application might work with storage controllers of other vendors (especially SAS/SCSI controllers). However storage controllers of other vendors have not been tested. The ledmon application is part of Intel(R) Enclosure LED Utilities. Only single instance of the application is allowed. =head1 OPTIONS =over 8 =item B<-c> or B<--config>=I Sets a path to local configuration file. If this option is specified the global configuration file and user configuration file has no effect. =item B<-l> or B<--log>=I Sets a path to local log file. If this option is specified the global log file F is not used. =item B<-t> or B<--interval>=I Sets time interval between scans of sysfs. The value is given in seconds. The minimum is 5 seconds the maximum is not specified. =item B<--quiet> or B<--error> or B<--warning> or B<--info> or B<--debug> or B<--all> Verbose level - 'quiet' means no logging at all and 'all' means to log everything. The levels are given in order. If user specifies more then one verbose option the last option comes into effect. The default level is 'warning'. Verbose level also can be set by B<--log-level>=I. =item B<-h> or B<--help> Prints this text out and exits. =item B<-v> or B<--version> Displays version of ledmon and information about the license and exits. =back =head1 FILES =over 8 =item F Global log file, used by ledmon application. To force logging to user defined file use I<-l> option switch. =item F Global configuration file, shared between ledmon and all ledctl application instances. Local configuration file can be used by running ledmon with I<-c> switch. =back =head1 LICENSE Copyright (c) 2009-2017 Intel Corporation. This program is distributed under the terms of the GNU General Public License as published by the Free Software Foundation. See the build-in help for details on the License and the lack of warranty. =head1 BUGS The ledmon application does not recognize PFA state (Predicted Failure Analysis), hence the PFA pattern from SFF-8489 specification is not visualized. =head1 SEE ALSO ledctl(8), ledmon.conf(5) =head1 AUTHOR This manual page was written by Artur Wojcik . It may be used by others. ledmon-0.90/src/000077500000000000000000000000001324101631400135025ustar00rootroot00000000000000ledmon-0.90/src/Makefile000066400000000000000000000060661324101631400151520ustar00rootroot00000000000000# # Intel(R) Enclosure LED Utilities # Copyright (C) 2009-2017 Intel Corporation. # # This program is free software; you can redistribute it and/or modify it # under the terms and conditions of the GNU General Public License, # version 2, as published by the Free Software Foundation. # # This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. # # Installation directory prefix. DESTDIR?= # Installation directory of ledctl application. LEDCTL_INSTDIR=$(DESTDIR)/usr/sbin # Installation directory of ledmon application. LEDMON_INSTDIR=$(DESTDIR)/usr/sbin # Output directory for object files. OUTDIR?=$(shell pwd)/_build # Build components common to both of applications. OBJECTS=\ $(OUTDIR)/ahci.o \ $(OUTDIR)/block.o \ $(OUTDIR)/cntrl.o \ $(OUTDIR)/config_file.o \ $(OUTDIR)/enclosure.o \ $(OUTDIR)/list.o \ $(OUTDIR)/raid.o \ $(OUTDIR)/scsi.o \ $(OUTDIR)/slave.o \ $(OUTDIR)/status.o \ $(OUTDIR)/sysfs.o \ $(OUTDIR)/smp.o \ $(OUTDIR)/dellssd.o \ $(OUTDIR)/utils.o \ $(OUTDIR)/pci_slot.o \ $(OUTDIR)/vmdssd.o \ $(OUTDIR)/udev.o # Build components of ledmon application. LEDMON_OBJECTS=\ $(OUTDIR)/ledmon.o \ $(OUTDIR)/pidfile.o \ $(OBJECTS) # Build components of ledctl application. LEDCTL_OBJECTS=\ $(OUTDIR)/ledctl.o \ $(OBJECTS) TEST_CONFIG_OBJECTS=\ $(OUTDIR)/config_file.o \ $(OUTDIR)/list.o \ $(OUTDIR)/utils.o CXFLAGS=-O0 -g -std=gnu99 CWFLAGS=-Wall CFLAGS=$(CXFLAGS) $(CWFLAGS) DEFFLAGS=-D_DEBUG -D_GNU_SOURCE -D_DEFAULT_SOURCE -DDMALLOC_DISABLE CPPFLAGS=$(DEFFLAGS) ALL_CPPFLAGS=$(CPPFLAGS) -I../config LDFLAGS=$(CXFLAGS) LDLIBS=-lsgutils2 -ludev SOURCES:=$(shell find -name "*.[cC]" -print) default: all ifdef DEBUG_IBPI CPPFLAGS += -DDEBUG_IBPI endif ledmon: $(OUTDIR)/.depend $(LEDMON_OBJECTS) $(CC) $(LDFLAGS) $(LEDMON_OBJECTS) $(LDLIBS) -o $@ ledctl: $(OUTDIR)/.depend $(LEDCTL_OBJECTS) $(CC) $(LDFLAGS) $(LEDCTL_OBJECTS) $(LDLIBS) -o $@ test_config: ALL_CPPFLAGS += -D_TEST_CONFIG test_config: $(OUTDIR)/.depend $(TEST_CONFIG_OBJECTS) $(CC) $(LDFLAGS) $(TEST_CONFIG_OBJECTS) $(LDLIBS) -o $@ $(OUTDIR)/%.o: %.c $(CC) $(CFLAGS) $(ALL_CPPFLAGS) -c $< -o $@ all: $(OUTDIR)/.depend ledmon ledctl uninstall: rm -f $(LEDMON_INSTDIR)/ledmon rm -f $(LEDCTL_INSTDIR)/ledctl install: all install -D ./ledmon $(LEDMON_INSTDIR)/ledmon install -D ./ledctl $(LEDCTL_INSTDIR)/ledctl clean: rm -rf $(OUTDIR) depend dep: $(OUTDIR)/.depend mrproper: clean rm -f ledmon ledctl $(OUTDIR)/.depend: $(SOURCES) mkdir -p $(OUTDIR) $(CC) $(CFLAGS) $(ALL_CPPFLAGS) -M $^ | sed 's/^[a-z,0-9]/$(subst /,\/,$(OUTDIR))\/&/' > $(OUTDIR)/.depend ifeq ($(filter install uninstall depend clean mrproper,$(MAKECMDGOALS)),) -include $(OUTDIR)/.depend endif ledmon-0.90/src/ahci.c000066400000000000000000000063011324101631400145520ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2018 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #include #include #include #if _HAVE_DMALLOC_H #include #endif #include "ahci.h" #include "config.h" #include "utils.h" /** * Time interval in u-seconds to wait before enclosure management message is being * sent to AHCI controller. */ #define EM_MSG_WAIT 1500 /** * This array maps IBPI pattern to value recognized by AHCI driver. The driver * uses this control number to issue SGPIO signals appropriately. */ static const unsigned int ibpi2sgpio[] = { [IBPI_PATTERN_REBUILD] = 0x00480000, [IBPI_PATTERN_FAILED_DRIVE] = 0x00400000, [IBPI_PATTERN_LOCATE] = 0x00080000, [IBPI_PATTERN_UNKNOWN] = 0x00000000, [IBPI_PATTERN_ONESHOT_NORMAL] = 0x00000000, [IBPI_PATTERN_NORMAL] = 0x00000000, [IBPI_PATTERN_LOCATE_OFF] = 0x00000000, #ifdef DEBUG_IBPI [IBPI_PATTERN_DEGRADED] = 0x00200000, [IBPI_PATTERN_FAILED_ARRAY] = 0x00280000, [IBPI_PATTERN_HOTSPARE] = 0x01800000, [IBPI_PATTERN_PFA] = 0x01400000 #else [IBPI_PATTERN_DEGRADED] = 0x00000000, [IBPI_PATTERN_FAILED_ARRAY] = 0x00000000, [IBPI_PATTERN_HOTSPARE] = 0x00000000, [IBPI_PATTERN_PFA] = 0x00000000 #endif }; /* * The function sends a LED control message to AHCI controller. It uses * SGPIO to control the LEDs. See ahci.h for details. */ int ahci_sgpio_write(struct block_device *device, enum ibpi_pattern ibpi) { char temp[WRITE_BUFFER_SIZE]; char path[PATH_MAX]; char *sysfs_path = device->cntrl_path; /* write only if state has changed */ if (ibpi == device->ibpi_prev) return 1; if (sysfs_path == NULL) __set_errno_and_return(EINVAL); if ((ibpi < IBPI_PATTERN_NORMAL) || (ibpi > IBPI_PATTERN_LOCATE_OFF)) __set_errno_and_return(ERANGE); sprintf(temp, "%u", ibpi2sgpio[ibpi]); str_cpy(path, sysfs_path, PATH_MAX); str_cat(path, "/em_message", PATH_MAX); usleep(EM_MSG_WAIT); return buf_write(path, temp) > 0; } /* * The function return path to SATA port in sysfs tree. See ahci.h for details. */ char *ahci_get_port_path(const char *path) { char tmp[PATH_MAX], buf[BUFFER_MAX]; char *p, *s; str_cpy(tmp, path, PATH_MAX); p = strstr(tmp, "/target"); if (p == NULL) return NULL; *p = '\0'; s = rindex(tmp, PATH_DELIM); if (s == NULL) return NULL; str_cpy(buf, s, BUFFER_MAX); str_cat(tmp, "/scsi_host", PATH_MAX); str_cat(tmp, buf, PATH_MAX); return str_dup(tmp); } ledmon-0.90/src/ahci.h000066400000000000000000000035241324101631400145630ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2017 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #ifndef _AHCI_H_INCLUDED_ #define _AHCI_H_INCLUDED_ #include "block.h" #include "ibpi.h" /** * @brief Gets sysfs path to SATA port. * * The function returns a path to SATA port in sysfs tree the given block device * is connected to. * * @param[in] path Path to block device in sysfs tree. * * @return Canonical path if successful, otherwise NULL pointer if an error occurred. */ char *ahci_get_port_path(const char *path); /** * @brief Sends LED control message using SGPIO. * * This function visualizes IBPI pattern on LEDs associated with a slot in * drive bay. This function is designed to send messaged to AHCI controller * only. * * @param[in] path Path in sysfs tree to slot in drive bay. * @param[in] ibpi IBPI pattern to visualize on LEDs associated * with the given slot. * * @return Number of bytes send to controller, -1 means error occurred and * errno has additional error information. */ int ahci_sgpio_write(struct block_device *path, enum ibpi_pattern ibpi); #endif /* _AHCI_H_INCLUDED_ */ ledmon-0.90/src/block.c000066400000000000000000000255421324101631400147500ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2018 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #include #include #include #include #include #if _HAVE_DMALLOC_H #include #endif #include "ahci.h" #include "block.h" #include "config.h" #include "dellssd.h" #include "pci_slot.h" #include "raid.h" #include "scsi.h" #include "slave.h" #include "smp.h" #include "status.h" #include "sysfs.h" #include "utils.h" #include "vmdssd.h" /* Global timestamp value. It shell be used to update a timestamp field of block device structure. See block.h for details. */ time_t timestamp = 0; /** * @brief Determines if disk is attached directly or via expander */ int dev_directly_attached(const char *path) { if (strstr(path, "/expander") == 0) return 1; return 0; } /** * @brief Determines a send function. * * This is the internal function of 'block device' module. The function tries to * determine a LED management protocol based on controller type and the given * path to block device in sysfs tree. First it checks whether to use * the default send function. If not it tries to read the content * of em_message_type field from sysfs tree and determines * the LED control protocol. * * @param[in] cntrl type of a controller a device is connected to. * @param[in] path path to a block device in sysfs tree. * * @return Pointer to send message function if successful, otherwise the function * returns the NULL pointer and it means either the controller does not * support enclosure management or LED control protocol * is not supported. */ static send_message_t _get_send_fn(struct cntrl_device *cntrl, const char *path) { send_message_t result = NULL; if (cntrl->cntrl_type == CNTRL_TYPE_AHCI) { result = ahci_sgpio_write; } else if (cntrl->cntrl_type == CNTRL_TYPE_SCSI && !dev_directly_attached(path)) { result = scsi_ses_write; } else if (cntrl->cntrl_type == CNTRL_TYPE_SCSI && dev_directly_attached(path)) { result = scsi_smp_fill_buffer; } else if (cntrl->cntrl_type == CNTRL_TYPE_DELLSSD) { result = dellssd_write; } else if (cntrl->cntrl_type == CNTRL_TYPE_VMD) { result = vmdssd_write; } return result; } static int do_not_flush(struct block_device *device) { return 1; } static flush_message_t _get_flush_fn(struct cntrl_device *cntrl, const char *path) { flush_message_t result = NULL; if (cntrl->cntrl_type == CNTRL_TYPE_SCSI) { if (dev_directly_attached(path)) result = scsi_smp_write_buffer; else result = scsi_ses_flush; } else { result = do_not_flush; } return result; } /** * @brief Determines a host path to block device. * * This is the internal function of 'block device' module. The function * determines a host path to block device in sysfs. * * @param[in] path path to block device in sysfs. * @param[in] cntrl controller device the block * device is connected to. * * @return Pointer to memory block containing a host path. The memory block * should be freed if one don't need the content. */ static char *_get_host(char *path, struct cntrl_device *cntrl) { char *result = NULL; if (cntrl->cntrl_type == CNTRL_TYPE_SCSI) result = scsi_get_slot_path(path, cntrl->sysfs_path); else if (cntrl->cntrl_type == CNTRL_TYPE_AHCI) result = ahci_get_port_path(path); else if (cntrl->cntrl_type == CNTRL_TYPE_DELLSSD) result = dellssd_get_path(path, cntrl->sysfs_path); else if (cntrl->cntrl_type == CNTRL_TYPE_VMD) result = vmdssd_get_path(path, cntrl->sysfs_path); return result; } static int is_dellssd(const struct block_device *bd) { return (bd->cntrl && bd->cntrl->cntrl_type == CNTRL_TYPE_DELLSSD); } static int is_vmd(const struct block_device *bd) { return ((bd->cntrl && bd->cntrl->cntrl_type == CNTRL_TYPE_VMD)); } /** * @brief Determines a storage controller. * * This is the internal function of 'block device' module. The function gets * a pointer to controller structure the device is connected to. * * @param[in] cntrl_list pointer to list of supported controllers. * @param[in] path path to block device in sysfs tree. * * @return Pointer to controller structure if successful, otherwise the function * returns NULL pointer. The NULL pointer means that block devices is * connected to unsupported storage controller. */ struct cntrl_device *block_get_controller(const struct list *cntrl_list, char *path) { struct cntrl_device *cntrl; list_for_each(cntrl_list, cntrl) { if (strncmp(cntrl->sysfs_path, path, strlen(cntrl->sysfs_path)) == 0) return cntrl; } return NULL; } struct _host_type *block_get_host(struct cntrl_device *cntrl, int host_id) { struct _host_type *hosts = NULL; if (!cntrl) return hosts; hosts = cntrl->hosts; while (hosts) { if (hosts->host_id == host_id) break; hosts = hosts->next; } return hosts; } /* * Allocates a new block device structure. See block.h for details. */ struct block_device *block_device_init(const struct list *cntrl_list, const char *path) { struct cntrl_device *cntrl; char link[PATH_MAX]; char *host = NULL; struct block_device *device = NULL; struct pci_slot *pci_slot = NULL; send_message_t send_fn = NULL; flush_message_t flush_fn = NULL; int host_id = -1; char *host_name; if (realpath(path, link)) { pci_slot = vmdssd_find_pci_slot(link); cntrl = block_get_controller(cntrl_list, link); if (cntrl != NULL) { if (cntrl->cntrl_type == CNTRL_TYPE_VMD && !pci_slot) return NULL; host = _get_host(link, cntrl); if (host == NULL) return NULL; host_name = get_path_hostN(link); if (host_name) { sscanf(host_name, "host%d", &host_id); free(host_name); } flush_fn = _get_flush_fn(cntrl, link); send_fn = _get_send_fn(cntrl, link); if (send_fn == NULL) { free(host); return NULL; } } else { return NULL; } device = calloc(1, sizeof(*device)); if (device) { struct _host_type *hosts = cntrl ? cntrl->hosts : NULL; device->cntrl = cntrl; device->sysfs_path = strdup(link); device->cntrl_path = host; device->ibpi = IBPI_PATTERN_UNKNOWN; device->ibpi_prev = IBPI_PATTERN_NONE; device->send_fn = send_fn; device->flush_fn = flush_fn; device->timestamp = timestamp; device->host = NULL; device->host_id = host_id; device->encl_index = -1; device->raid_dev = NULL; while (hosts) { if (hosts->host_id == host_id) { device->host = hosts; break; } hosts = hosts->next; } if (cntrl && cntrl->cntrl_type == CNTRL_TYPE_SCSI) { device->phy_index = cntrl_init_smp(link, cntrl); if (!dev_directly_attached(link) && !scsi_get_enclosure(device)) { log_warning("Device initialization failed for '%s'", path); free(device->sysfs_path); free(device->cntrl_path); free(device); device = NULL; } } } else if (host) { free(host); } } return device; } /** * Frees memory allocated for block device structure. See block.h for details. */ void block_device_fini(struct block_device *device) { if (device) { if (device->sysfs_path) free(device->sysfs_path); if (device->cntrl_path) free(device->cntrl_path); if (device->raid_dev) raid_device_fini(device->raid_dev); free(device); } } /* * Duplicates a block device structure. See block.h for details. */ struct block_device *block_device_duplicate(struct block_device *block) { struct block_device *result = NULL; if (block) { result = calloc(1, sizeof(*result)); if (result) { result->sysfs_path = strdup(block->sysfs_path); result->cntrl_path = strdup(block->cntrl_path); if (block->ibpi != IBPI_PATTERN_UNKNOWN) result->ibpi = block->ibpi; else result->ibpi = IBPI_PATTERN_ONESHOT_NORMAL; result->ibpi_prev = block->ibpi_prev; result->send_fn = block->send_fn; result->flush_fn = block->flush_fn; result->timestamp = block->timestamp; result->cntrl = block->cntrl; result->host = block->host; result->host_id = block->host_id; result->phy_index = block->phy_index; result->encl_index = block->encl_index; result->enclosure = block->enclosure; result->raid_dev = raid_device_duplicate(block->raid_dev); } } return result; } int block_compare(const struct block_device *bd_old, const struct block_device *bd_new) { int i = 0; if (!is_dellssd(bd_old) && !is_vmd(bd_old) && bd_old->host_id == -1) { log_debug("Device %s : No host_id!", strstr(bd_old->sysfs_path, "host")); return 0; } if (!is_dellssd(bd_new) && !is_vmd(bd_new) && bd_new->host_id == -1) { log_debug("Device %s : No host_id!", strstr(bd_new->sysfs_path, "host")); return 0; } if (bd_old->cntrl->cntrl_type != bd_new->cntrl->cntrl_type) return 0; switch (bd_old->cntrl->cntrl_type) { case CNTRL_TYPE_AHCI: /* Missing support for port multipliers. Compare just hostX. */ i = (bd_old->host_id == bd_new->host_id); break; case CNTRL_TYPE_SCSI: /* Host and phy is not enough. They might be DA or EA. */ if (dev_directly_attached(bd_old->sysfs_path) && dev_directly_attached(bd_new->sysfs_path)) { /* Just compare host & phy */ i = (bd_old->host_id == bd_new->host_id) && (bd_old->phy_index == bd_new->phy_index); break; } if (!dev_directly_attached(bd_old->sysfs_path) && !dev_directly_attached(bd_new->sysfs_path)) { /* Both expander attached */ i = (bd_old->host_id == bd_new->host_id) && (bd_old->phy_index == bd_new->phy_index); i = i && (bd_old->enclosure == bd_new->enclosure); i = i && (bd_old->encl_index == bd_new->encl_index); break; } /* */ break; case CNTRL_TYPE_VMD: /* compare names and address of the drive */ i = (strcmp(bd_old->sysfs_path, bd_new->sysfs_path) == 0); if (!i) { struct pci_slot *old_slot, *new_slot; old_slot = vmdssd_find_pci_slot(bd_old->sysfs_path); new_slot = vmdssd_find_pci_slot(bd_new->sysfs_path); if (old_slot && new_slot) i = (strcmp(old_slot->address, new_slot->address) == 0); } break; case CNTRL_TYPE_DELLSSD: default: /* Just compare names */ i = (strcmp(bd_old->sysfs_path, bd_new->sysfs_path) == 0); break; } return i; } ledmon-0.90/src/block.h000066400000000000000000000165521324101631400147560ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2018 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #ifndef _BLOCK_H_INCLUDED_ #define _BLOCK_H_INCLUDED_ #include "cntrl.h" #include "ibpi.h" #include "time.h" #include "list.h" #include "raid.h" struct block_device; /** * @brief Pointer to a send message function. * * The pointer to a function which knows how to send LED message to a driver of * storage controller using the given protocol. * * @param[in] path path in sysfs to a host device * @see block_device::cntrl_path. * @param[in] ibpi an IBPI pattern (state) to visualize. * * @return 1 if successful, otherwise the function returns 0. */ typedef int (*send_message_t) (struct block_device *device, enum ibpi_pattern ibpi); /** * @brief Pointer to a flush buffer function. * * @param[in] device pointer to a block device * * @return 1 if successful, otherwise the function returns 0. */ typedef int (*flush_message_t) (struct block_device *device); /** * @brief Describes a block device. * * This structure describes a block device. It does not describe virtual devices * or partitions on physical block devices. */ struct block_device { /** * Real path in sysfs tree. This means i.e. if /sys/block/sda is symbolic link * then the link will be read and path stored in sysfs_path field. This path * may not exist in sysfs if connection to physical drive is lost. This filed * cannot have NULL pointer assigned. */ char *sysfs_path; /** * The pointer to a function which sends a message to driver in order to * control LEDs in an enclosure or DAS system - @see send_message_t for details. * This field cannot have NULL pointer assigned. */ send_message_t send_fn; /** * The pointer to a function which flush buffers filled by send_fn. */ flush_message_t flush_fn; /** * Canonical path to block device where enclosure management fields are located. * This path is always accessible even if the connection to physical device * is lost. In case of AHCI controller it points to SATA phy. In case of SAS * this path points to SES entry associated with the slot in an enclosure. * This field cannot have NULL pointer assign. */ char *cntrl_path; /** * The current state of block device. This is an IBPI pattern and it is used * to visualize the state of block device. */ enum ibpi_pattern ibpi; /** * The previous state of block device. */ enum ibpi_pattern ibpi_prev; /** * The time stamp used to determine if the given block device still exist or * it failed and the device is no longer available. Every time IBPI pattern * is updated, the time-stamp is updated, too. */ time_t timestamp; /** * The pointer to storage controller structure the device is connected to. */ struct cntrl_device *cntrl; struct _host_type *host; int host_id; /** * The index of phy utilized by directly attached to controller block device. * It is meaningful if device is controlled by isci driver. */ int phy_index; /** * The index in Enclosure. This is what should be used when using SES-2. */ int encl_index; struct enclosure_device *enclosure; /** * If disk is a raid member, this field will be set with a copy of raid device * struct. */ struct raid_device *raid_dev; }; /** * @brief Creates a block device structure. * * This function allocates memory for a new structure of block device. It reads * the sysfs entries and populates the structure fields. It performs all this * actions only if the block device is connected to the one of supported storage * controllers and the controller has enclosure management services enabled. * * @param[in] cntrl_list pointer to a list of supported controller * devices. * @param[in] sysfs_path a path to block device in sysfs. * * @return Pointer to block device structure if successful, otherwise the function * returns the NULL pointer. */ struct block_device *block_device_init(const struct list *cntrl_list, const char *path); /** * @brief Releases a block device structure. * * This function releases memory allocated for block device structure. * * @param[in] device pointer to block device structure. * * @return The function does not return a value. */ void block_device_fini(struct block_device *device); /** * @brief Duplicates a block device structure. * * The function allocates memory for a block device structure and copies values * stored in fields of source block structure. The function allocates new memory * for all string fields in a copy structure. It is safe to release source block * structure just after it has been duplicated. * * @param[in] device pointer to source block device structure. * * @return Pointer to block device structure if successful, otherwise the function * returns the NULL pointer. */ struct block_device *block_device_duplicate(struct block_device *device); /** * @brief Determines a storage controller. * * This is the internal function of 'block device' module. The function gets * a pointer to controller structure the device is connected to. * * @param[in] cntrl_list pointer to list of supported controllers. * @param[in] path path to block device in sysfs tree. * * @return Pointer to controller structure if successful, otherwise the function * returns NULL pointer. The NULL pointer means that block devices is * connected to unsupported storage controller. */ struct cntrl_device *block_get_controller(const struct list *cntrl_list, char *path); /** * The global timestamp variable. It is updated every time the sysfs is scanning * by an application. The value stored in this variable should be used to update * all timestamp stored in block device structures. */ extern time_t timestamp; /** * @brief Determines if block device is attached directly or via expander */ int dev_directly_attached(const char *path); /** * @brief Gets the host structure for given control device and host_id */ struct _host_type *block_get_host(struct cntrl_device *cntrl, int host_id); /** * @brief Checks the presence of block device. * * This is internal function of monitor service. The function is checking * whether block device is already on the list or it is missing from the list. * The function is design to be used as 'test' parameter for list_find_first() * function. * * @param[in] bd_old - an element from a list to compare to. * @param[in] bd_new - a block device being searched. * * @return 0 if the block devices do not match, otherwise function returns 1. */ int block_compare(const struct block_device *bd_old, const struct block_device *bd_new); #endif /* _BLOCK_H_INCLUDED_ */ ledmon-0.90/src/cntrl.c000066400000000000000000000217521324101631400147770ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2018 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #include #include #if _HAVE_DMALLOC_H #include #endif #include "cntrl.h" #include "config.h" #include "config_file.h" #include "list.h" #include "smp.h" #include "status.h" #include "sysfs.h" #include "utils.h" /** * @brief Name of controllers types. * * This is internal array with names of controller types. Array can be use to * translate enumeration type into the string. */ static const char * const ctrl_type_str[] = { [CNTRL_TYPE_UNKNOWN] = "?", [CNTRL_TYPE_DELLSSD] = "Dell SSD", [CNTRL_TYPE_VMD] = "VMD", [CNTRL_TYPE_SCSI] = "SCSI", [CNTRL_TYPE_AHCI] = "AHCI" }; /** */ static int _is_storage_controller(const char *path) { uint64_t class = get_uint64(path, 0, "class"); return (class & 0xff0000L) == 0x010000L; } /** */ static int _is_isci_cntrl(const char *path) { return sysfs_check_driver(path, "isci"); } /** */ static int _is_ahci_cntrl(const char *path) { char temp[PATH_MAX], link[PATH_MAX], *t; str_cpy(temp, path, PATH_MAX); str_cat(temp, "/driver", PATH_MAX); if (realpath(temp, link) == NULL) return 0; t = strrchr(link, '/'); if ((t != NULL) && (strcmp(t + 1, "ahci") != 0)) return 0; return get_uint64(path, 0, "vendor") == 0x8086L; } static int _is_dellssd_cntrl(const char *path) { uint64_t vdr, dev, svdr, cls; vdr = get_uint64(path, 0, "vendor"); dev = get_uint64(path, 0, "device"); cls = get_uint64(path, 0, "class"); svdr = get_uint64(path, 0, "subsystem_vendor"); return ((vdr == 0x1344L && dev == 0x5150L) || /* micron ssd */ (svdr == 0x1028 && cls == 0x10802)); /* nvmhci ssd */ } /** */ static int _is_smp_cntrl(const char *path) { int result = 0; struct list dir; char *p; char host_path[PATH_MAX] = { 0 }; if (scan_dir(path, &dir) == 0) { const char *dir_path; list_for_each(&dir, dir_path) { p = strrchr(dir_path, '/'); if (!p++) break; if (strncmp(p, "host", strlen("host")) == 0) { snprintf(host_path, sizeof(host_path), "%s/%s/bsg/sas_%s", path, p, p); result = smp_write_gpio(host_path, GPIO_REG_TYPE_TX, 0, 0, "", 0) == 0; } } list_erase(&dir); } return result; } static int _is_vmd_cntrl(const char *path) { return sysfs_check_driver(path, "vmd"); } /** * @brief Determines the type of controller. * * This is internal function of 'controller device' module. The function * determines the type of controller device. It might be AHCI, SCSI or * UNKNOWN device type. * * @param[in] path path to controller device in sysfs tree. * * @return The type of controller device. If the type returned is * CNTRL_TYPE_UNKNOWN this means a controller device is not * supported. */ static enum cntrl_type _get_type(const char *path) { enum cntrl_type type = CNTRL_TYPE_UNKNOWN; if (_is_vmd_cntrl(path)) { type = CNTRL_TYPE_VMD; } else if (_is_dellssd_cntrl(path)) { type = CNTRL_TYPE_DELLSSD; } else if (_is_storage_controller(path)) { if (_is_ahci_cntrl(path)) type = CNTRL_TYPE_AHCI; else if (_is_isci_cntrl(path) || sysfs_enclosure_attached_to_cntrl(path) || _is_smp_cntrl(path)) type = CNTRL_TYPE_SCSI; } return type; } struct _host_type *alloc_host(int id, struct _host_type *next) { struct _host_type *host = NULL; host = malloc(sizeof(struct _host_type)); if (host) { host->host_id = id; host->ibpi_state_buffer = NULL; memset(host->bitstream, 0, sizeof(host->bitstream)); host->flush = 0; host->ports = 0; host->next = next; } return host; } void free_hosts(struct _host_type *h) { struct _host_type *t; while (h) { t = h->next; free(h->ibpi_state_buffer); free(h); h = t; } } void _find_host(const char *path, struct _host_type **hosts) { const int host_len = sizeof("host") - 1; char *p; int index = -1; struct _host_type *th; DIR *d; struct dirent *de; p = strrchr(path, '/'); if (!p++) return; if (strncmp(p, "host", host_len - 1) == 0) { index = atoi(p + host_len); th = alloc_host(index, (*hosts) ? (*hosts) : NULL); if (!th) return; d = opendir(path); if(!d) { free(th); return; } while ((de = readdir(d))) { if (strncmp(de->d_name, "phy-", strlen("phy-")) == 0) { th->ports++; } } closedir(d); *hosts = th; } } /** * @brief Get all instances of separate hosts on isci controller. * */ static struct _host_type *_cntrl_get_hosts(const char *path) { struct _host_type *hosts = NULL; struct list dir; if (scan_dir(path, &dir) == 0) { const char *dir_path; list_for_each(&dir, dir_path) _find_host(dir_path, &hosts); list_erase(&dir); } return hosts; } /** * @brief Check if enclosure management is enabled. * * This is internal function of 'controller device' module. The function checks * whether enclosure management is enabled for AHCI controller. * * @param[in] path path to controller device in sysfs tree. * * @return 1 if enclosure management is enabled, otherwise the function returns 0. */ static unsigned int _ahci_em_messages(const char *path) { /* first, open ...driver/module/parameters/ahci_em_messages * then open /sys/module/libahci/parameters/ahci_em_messages * and check if it is set to enabled. * if so, check if 'holders' of libahci points the same driver name * as device given by path */ char buf[PATH_MAX]; char *link, *name; DIR *dh; struct dirent *de = NULL; /* old kernel (prior to 2.6.36) */ if (get_int(path, 0, "driver/module/parameters/ahci_em_messages") != 0) return 1; /* parameter type changed from int to bool since kernel v3.13 */ if (!get_int("", 0, "sys/module/libahci/parameters/ahci_em_messages")) { if (!get_bool("", 0, "sys/module/libahci/parameters/ahci_em_messages")) return 0; } if (snprintf(buf, sizeof(buf), "%s/%s", path, "driver") < 0) return 0; link = realpath(buf, NULL); if (!link) return 0; name = strrchr(link, '/'); if (!name++) { free(link); return 0; } /* check if the directory /sys/module/libahci/holders exists */ dh = opendir("/sys/module/libahci/holders"); if (dh) { /* name contain controller name (ie. ahci),*/ /* so check if libahci holds this driver */ while ((de = readdir(dh))) { if (!strcmp(de->d_name, name)) break; } closedir(dh); free(link); return de ? 1 : 0; } else { free(link); return 1; } } /* * Allocates memory for a new controller device structure. See cntrl.h for * details. */ struct cntrl_device *cntrl_device_init(const char *path) { unsigned int em_enabled; enum cntrl_type type; struct cntrl_device *device = NULL; type = _get_type(path); if (type != CNTRL_TYPE_UNKNOWN) { switch (type) { case CNTRL_TYPE_DELLSSD: case CNTRL_TYPE_SCSI: case CNTRL_TYPE_VMD: em_enabled = 1; break; case CNTRL_TYPE_AHCI: em_enabled = _ahci_em_messages(path); break; default: em_enabled = 0; } if (em_enabled) { if (!list_is_empty(&conf.cntrls_whitelist)) { char *cntrl = NULL; list_for_each(&conf.cntrls_whitelist, cntrl) { if (match_string(cntrl, path)) break; cntrl = NULL; } if (!cntrl) { log_debug("%s not found on whitelist, ignoring", path); return NULL; } } else if (!list_is_empty(&conf.cntrls_blacklist)) { char *cntrl; list_for_each(&conf.cntrls_blacklist, cntrl) { if (match_string(cntrl, path)) { log_debug("%s found on blacklist, ignoring", path); return NULL; } } } device = malloc(sizeof(struct cntrl_device)); if (device) { if (type == CNTRL_TYPE_SCSI) { device->isci_present = _is_isci_cntrl(path); device->hosts = _cntrl_get_hosts(path); } else { device->isci_present = 0; device->hosts = NULL; } device->cntrl_type = type; device->sysfs_path = strdup(path); } } else { log_error ("controller discovery: %s - enclosure " \ "management not supported.", path); } } return device; } /* * Frees memory allocated for controller device structure. See cntrl.h for * details. */ void cntrl_device_fini(struct cntrl_device *device) { if (device) { free(device->sysfs_path); free_hosts(device->hosts); free(device); } } void print_cntrl(struct cntrl_device *ctrl_dev) { printf("%s (%s)\n", ctrl_dev->sysfs_path, ctrl_type_str[ctrl_dev->cntrl_type]); } ledmon-0.90/src/cntrl.h000066400000000000000000000062031324101631400147760ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2018 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #ifndef _CNTRL_H_INCLUDED_ #define _CNTRL_H_INCLUDED_ /** * This enumeration type lists all supported storage controller types. */ enum cntrl_type { CNTRL_TYPE_UNKNOWN = 0, CNTRL_TYPE_DELLSSD, CNTRL_TYPE_VMD, CNTRL_TYPE_SCSI, CNTRL_TYPE_AHCI }; /** * @brief Storage controller device structure. * * This structure describes a storage controller device existing in the system. */ struct cntrl_device { /** * Path to the device in sysfs tree. */ char *sysfs_path; /** * Type of storage controller device. */ enum cntrl_type cntrl_type; /** * Flag if scsi controller driver is "isci" */ int isci_present; struct _host_type { /** * ibpi state buffer for directly attached devices */ struct gpio_tx_register_byte *ibpi_state_buffer; /** * outbound raw byte stream */ unsigned char bitstream[4]; /** * bitstream's flush flag */ int flush; /** * host identifier for different hba instances */ int host_id; /** * number of total phy ports */ int ports; /** * pointer to next structure */ struct _host_type *next; } *hosts; }; /** * @brief Allocates a new controller device structure. * * This function allocates memory for a new structure of storage controller * device. It reads the sysfs entries and populates structure fields. * The function registers only supported storage controllers. * * @param[in] path path to storage controller in sysfs tree. * * @return Pointer to storage controller structure if successful, otherwise the * function returns NULL pointer. The NULL pointer means that controller * device is not supported. */ struct cntrl_device *cntrl_device_init(const char *path); /** * @brief Releases a controller device structure. * * This function releases memory allocated for controller device structure. * * @param[in] device pointer to controller device structure. * * @return The function does not return a value. */ void cntrl_device_fini(struct cntrl_device *device); /** * @brief Prints given controller to stdout. * * This function prints the path and type of controller device given as * argument. * * @param[in] ctrl_dev address to element from a * controller list. * * @return The function does not return a value. */ void print_cntrl(struct cntrl_device *ctrl_dev); #endif /* _CNTRL_H_INCLUDED_ */ ledmon-0.90/src/config_file.c000066400000000000000000000141141324101631400161130ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * * Copyright (C) 2017-2018 Intel Corporation. * Copyright (C) 2009 Karel Zak * * SPDX-License-Identifier: GPL-2.0 * * Contains code from util-linux/libblkid/src/config.c * originally released under LGPL. */ #include #include #include #include #include #include #include #include #include #include #include "config_file.h" #include "utils.h" #include "status.h" /** * @brief Pointer to global ledmon configuration. * * This is a pointer to structure that contains settings of ledmon behavior * read from configuration file. */ struct ledmon_conf conf; const char *log_level_map[] = { [LOG_LEVEL_QUIET] = "QUIET", [LOG_LEVEL_ERROR] = "ERROR", [LOG_LEVEL_WARNING] = "WARNING", [LOG_LEVEL_INFO] = "INFO", [LOG_LEVEL_DEBUG] = "DEBUG", [LOG_LEVEL_ALL] = "ALL" }; static int parse_bool(char *s) { if (*s && (!strcasecmp(s, "enabled") || !strcasecmp(s, "true") || !strcasecmp(s, "yes") || !strcasecmp(s, "1"))) return 1; else if (*s && (!strcasecmp(s, "disabled") || !strcasecmp(s, "false") || !strcasecmp(s, "no") || !strcasecmp(s, "0"))) return 0; fprintf(stderr, "Unknown bool value: %s\n", s); return -1; } static void parse_list(struct list *list, char *s) { list_erase(list); while (s && *s) { char *sep; sep = strchr(s, ','); if (sep) *sep = '\0'; list_append(list, strdup(s)); if (sep) s = sep + 1; else break; } } int _map_log_level(char *conf_log_level) { int i = 1; while (i < sizeof(log_level_map)/sizeof(char *)) { if (strcasecmp(log_level_map[i], conf_log_level) == 0) return i; i++; } return 0; } void _set_log_level(char *s) { int log_level; log_level = _map_log_level(s); if (log_level) conf.log_level = log_level; else if (sscanf(s, "%d", &log_level) == 1 && log_level >= LOG_LEVEL_QUIET && log_level <= LOG_LEVEL_ALL) conf.log_level = log_level; else log_warning("Log level given in config file (%s) is incorrect! Using default log level: %s", s, log_level_map[conf.log_level]); } static int parse_next(FILE *fd) { char buf[BUFSIZ]; char *s; /* read the next non-blank non-comment line */ do { if (fgets(buf, sizeof(buf), fd) == NULL) return feof(fd) ? 0 : -1; s = strchr(buf, '\n'); if (!s) { /* Missing final newline? Otherwise extremely */ /* long line - assume file was corrupted */ if (feof(fd)) s = strchr(buf, '\0'); else { fprintf(stderr, "config file: missing newline at line '%s'.", buf); return -1; } } *s = '\0'; if (--s >= buf && *s == '\r') *s = '\0'; s = buf; while (*s == ' ' || *s == '\t') /* skip space */ s++; } while (*s == '\0' || *s == '#'); if (!strncmp(s, "INTERVAL=", 9)) { s += 9; if (*s) { sscanf(s, "%d", &conf.scan_interval); if (conf.scan_interval < LEDMON_MIN_SLEEP_INTERVAL) conf.scan_interval = LEDMON_MIN_SLEEP_INTERVAL; } } else if (!strncmp(s, "LOG_LEVEL=", 10)) { s += 10; _set_log_level(s); } else if (!strncmp(s, "LOG_PATH=", 9)) { s += 9; if (*s) set_log_path(s); } else if (!strncmp(s, "BLINK_ON_MIGR=", 14)) { s += 14; conf.blink_on_migration = parse_bool(s); if (conf.blink_on_migration < 0) return -1; } else if (!strncmp(s, "BLINK_ON_INIT=", 14)) { s += 14; conf.blink_on_init = parse_bool(s); if (conf.blink_on_init < 0) return -1; } else if (!strncmp(s, "REBUILD_BLINK_ON_ALL=", 21)) { s += 21; conf.rebuild_blink_on_all = parse_bool(s); if (conf.rebuild_blink_on_all < 0) return -1; } else if (!strncmp(s, "RAID_MEMBERS_ONLY=", 18)) { s += 18; conf.raid_memebers_only = parse_bool(s); if (conf.raid_memebers_only < 0) return -1; } else if (!strncmp(s, "WHITELIST=", 10)) { s += 10; if (*s) parse_list(&conf.cntrls_whitelist, s); } else if (!strncmp(s, "BLACKLIST=", 10)) { s += 10; if (*s) parse_list(&conf.cntrls_blacklist, s); } else { fprintf(stderr, "config file: unknown option '%s'.\n", s); return -1; } return 0; } void ledmon_free_config() { list_erase(&conf.cntrls_blacklist); list_erase(&conf.cntrls_whitelist); if (conf.log_path) free(conf.log_path); } /* return real config data or built-in default */ int ledmon_read_config(const char *filename) { FILE *f; if (!filename || (filename && access(filename, F_OK) < 0)) { if (filename) fprintf(stdout, "%s: does not exist, using global config file\n", filename); filename = LEDMON_DEF_CONF_FILE; } f = fopen(filename, "re"); if (!f) { fprintf(stdout, "%s: does not exist, using built-in defaults\n", filename); } else { while (!feof(f)) { if (parse_next(f)) { fprintf(stderr, "%s: parse error\n", filename); ledmon_free_config(); fclose(f); return STATUS_CONFIG_FILE_ERROR; } } fclose(f); } if (!list_is_empty(&conf.cntrls_whitelist) && !list_is_empty(&conf.cntrls_blacklist)) log_warning("Both whitelist and blacklist are specified - ignoring blacklist."); return STATUS_SUCCESS; } #ifdef _TEST_CONFIG /* * usage: ledmon_conf_test [] */ int main(int argc, char *argv[]) { char *filename = NULL; char *s; if (argc == 2) filename = argv[1]; if (ledmon_read_config(filename) != STATUS_SUCCESS) return EXIT_FAILURE; printf("INTERVAL: %d\n", conf.scan_interval); printf("LOG_LEVEL: %d\n", conf.log_level); printf("LOG_PATH: %s\n", conf.log_path); printf("BLINK_ON_MIGR: %d\n", conf.blink_on_migration); printf("BLINK_ON_INIT: %d\n", conf.blink_on_init); printf("REBUILD_BLINK_ON_ALL: %d\n", conf.rebuild_blink_on_all); printf("RAID_MEMBERS_ONLY: %d\n", conf.raid_memebers_only); if (list_is_empty(&conf.cntrls_whitelist)) printf("WHITELIST: NONE\n"); else { printf("WHITELIST: "); list_for_each(&conf.cntrls_whitelist, s) printf("%s, ", s); printf("\n"); } if (list_is_empty(&conf.cntrls_blacklist)) printf("BLACKLIST: NONE\n"); else { printf("BLACKLIST: "); list_for_each(&conf.cntrls_blacklist, s) printf("%s, ", s); printf("\n"); } ledmon_free_config(); return EXIT_SUCCESS; } #endif ledmon-0.90/src/config_file.h000066400000000000000000000023031324101631400161150ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * * Copyright (C) 2017-2018 Intel Corporation. * Copyright (C) 2009 Karel Zak * * SPDX-License-Identifier: GPL-2.0 * * Contains code from util-linux/libblkid/src/config.c * originally released under LGPL. */ #ifndef SRC_CONFIG_FILE_H_ #define SRC_CONFIG_FILE_H_ #include "list.h" #define LEDMON_DEF_CONF_FILE "/etc/ledmon.conf" #define LEDMON_DEF_LOG_FILE "/var/log/ledmon.log" #define LEDCTL_DEF_LOG_FILE "/var/log/ledctl.log" #define LEDMON_DEF_SLEEP_INTERVAL 10 #define LEDMON_MIN_SLEEP_INTERVAL 5 enum log_level_enum { LOG_LEVEL_UNDEF = 0, LOG_LEVEL_QUIET, LOG_LEVEL_ERROR, LOG_LEVEL_WARNING, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_ALL, }; struct ledmon_conf { /* internal ledmon functions */ char *log_path; enum log_level_enum log_level; int scan_interval; /* customizable leds behaviour */ int blink_on_migration; int blink_on_init; int rebuild_blink_on_all; int raid_memebers_only; /* whitelist and blacklist of controllers for blinking */ struct list cntrls_whitelist; struct list cntrls_blacklist; }; extern struct ledmon_conf conf; int ledmon_read_config(const char *filename); #endif /* SRC_CONFIG_FILE_H_ */ ledmon-0.90/src/dellssd.c000066400000000000000000000151231324101631400153020ustar00rootroot00000000000000/* * Dell Backplane LED control * Copyright (C) 2011, Dell Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #include #include #include #include #include #include #if _HAVE_DMALLOC_H #include #endif #include "ahci.h" #include "cntrl.h" #include "config.h" #include "dellssd.h" #include "ibpi.h" #include "list.h" #include "raid.h" #include "scsi.h" #include "slave.h" #include "smp.h" #include "status.h" #include "sysfs.h" #include "utils.h" #define BP_PRESENT (1L << 0) #define BP_ONLINE (1L << 1) #define BP_HOTSPARE (1L << 2) #define BP_IDENTIFY (1L << 3) #define BP_REBUILDING (1L << 4) #define BP_FAULT (1L << 5) #define BP_PREDICT (1L << 6) #define BP_CRITICALARRAY (1L << 9) #define BP_FAILEDARRAY (1L << 10) static const unsigned int ibpi2ssd[] = { [IBPI_PATTERN_UNKNOWN] = BP_ONLINE, [IBPI_PATTERN_ONESHOT_NORMAL] = BP_ONLINE, [IBPI_PATTERN_NORMAL] = BP_ONLINE, [IBPI_PATTERN_DEGRADED] = BP_CRITICALARRAY|BP_ONLINE, [IBPI_PATTERN_REBUILD] = BP_REBUILDING|BP_ONLINE, [IBPI_PATTERN_FAILED_ARRAY] = BP_FAILEDARRAY|BP_ONLINE, [IBPI_PATTERN_HOTSPARE] = BP_HOTSPARE|BP_ONLINE, [IBPI_PATTERN_PFA] = BP_PREDICT|BP_ONLINE, [IBPI_PATTERN_FAILED_DRIVE] = BP_FAULT|BP_ONLINE, [IBPI_PATTERN_LOCATE] = BP_IDENTIFY|BP_ONLINE, [IBPI_PATTERN_LOCATE_OFF] = BP_ONLINE }; #define BMC_SA 0x20 #define DELL_OEM_NETFN 0x30 #define DELL_OEM_STORAGE_CMD 0xD5 #define DELL_OEM_STORAGE_GETDRVMAP 0x07 #define DELL_OEM_STORAGE_SETDRVSTATUS 0x04 static int ipmi_open() { int fd; fd = open("/dev/ipmi0", O_RDWR); if (fd >= 0) return fd; fd = open("/dev/ipmidev/0", O_RDWR); if (fd >= 0) return fd; fd = open("/dev/ipmidev0", O_RDWR); if (fd >= 0) return fd; fd = open("/dev/bmc", O_RDWR); if (fd >= 0) return fd; return -1; } static int ipmicmd(int sa, int lun, int netfn, int cmd, int datalen, void *data, int resplen, int *rlen, void *resp) { static int msgid; struct ipmi_system_interface_addr saddr; struct ipmi_ipmb_addr iaddr; struct ipmi_addr raddr; struct ipmi_req req; struct ipmi_recv rcv; fd_set rfd; int fd, rc; uint8_t tresp[resplen + 1]; fd = ipmi_open(); if (fd < 0) return -1; memset(&req, 0, sizeof(req)); memset(&rcv, 0, sizeof(rcv)); if (sa == BMC_SA) { memset(&saddr, 0, sizeof(saddr)); saddr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; saddr.channel = IPMI_BMC_CHANNEL; saddr.lun = 0; req.addr = (void *)&saddr; req.addr_len = sizeof(saddr); } else { memset(&iaddr, 0, sizeof(iaddr)); iaddr.addr_type = IPMI_IPMB_ADDR_TYPE; iaddr.channel = 0; iaddr.slave_addr = sa; iaddr.lun = lun; req.addr = (void *)&iaddr; req.addr_len = sizeof(iaddr); } /* Issue command */ req.msgid = ++msgid; req.msg.netfn = netfn; req.msg.cmd = cmd; req.msg.data_len = datalen; req.msg.data = data; rc = ioctl(fd, IPMICTL_SEND_COMMAND, (void *)&req); if (rc != 0) { perror("send"); goto end; } /* Wait for Response */ FD_ZERO(&rfd); FD_SET(fd, &rfd); rc = select(fd + 1, &rfd, NULL, NULL, NULL); if (rc < 0) { perror("select"); goto end; } /* Get response */ rcv.msg.data = tresp; rcv.msg.data_len = resplen + 1; rcv.addr = (void *)&raddr; rcv.addr_len = sizeof(raddr); rc = ioctl(fd, IPMICTL_RECEIVE_MSG_TRUNC, (void *)&rcv); if (rc != 0 && errno == EMSGSIZE) printf("too short..\n"); if (rc != 0 && errno != EMSGSIZE) { fprintf(stderr, "%d ", errno); perror("recv"); goto end; } if (rcv.msg.data[0]) printf("IPMI Error: %.2x\n", rcv.msg.data[0]); rc = 0; *rlen = rcv.msg.data_len - 1; memcpy(resp, rcv.msg.data + 1, *rlen); end: close(fd); return rc; } static int ipmi_setled(int b, int d, int f, int state) { uint8_t data[20], rdata[20]; int rc, rlen, bay, slot, devfn; bay = 0xFF; slot = 0xFF; devfn = (((d & 0x1F) << 3) | (f & 0x7)); /* Get mapping of BDF to bay:slot */ memset(data, 0, sizeof(data)); memset(rdata, 0, sizeof(rdata)); data[0] = 0x01; /* get */ data[1] = DELL_OEM_STORAGE_GETDRVMAP; /* storage map */ data[2] = 0x06; /* length lsb */ data[3] = 0x00; /* length msb */ data[4] = 0x00; /* offset lsb */ data[5] = 0x00; /* offset msb */ data[6] = b; /* bus */ data[7] = devfn; /* devfn */ rc = ipmicmd(BMC_SA, 0, DELL_OEM_NETFN, DELL_OEM_STORAGE_CMD, 8, data, 20, &rlen, rdata); if (!rc) { bay = rdata[7]; slot = rdata[8]; } if (bay == 0xFF || slot == 0xFF) return 0; /* Set Bay:Slot to Mask */ memset(data, 0, sizeof(data)); memset(rdata, 0, sizeof(rdata)); data[0] = 0x00; /* set */ data[1] = DELL_OEM_STORAGE_SETDRVSTATUS; /* set drive status */ data[2] = 0x0e; /* length lsb */ data[3] = 0x00; /* length msb */ data[4] = 0x00; /* offset lsb */ data[5] = 0x00; /* offset msb */ data[6] = 0x0e; /* length lsb */ data[7] = 0x00; /* length msb */ data[8] = bay; /* bayid */ data[9] = slot; /* slotid */ data[10] = state & 0xff; /* state LSB */ data[11] = state >> 8; /* state MSB */ rc = ipmicmd(BMC_SA, 0, DELL_OEM_NETFN, DELL_OEM_STORAGE_CMD, 20, data, 20, &rlen, rdata); return 0; } char *dellssd_get_path(const char *path, const char *cntrl_path) { return strdup(cntrl_path); } int dellssd_write(struct block_device *device, enum ibpi_pattern ibpi) { int mask, bus, dev, fun; char *t; /* write only if state has changed */ if (ibpi == device->ibpi_prev) return 1; if ((ibpi < IBPI_PATTERN_NORMAL) || (ibpi > IBPI_PATTERN_LOCATE_OFF)) __set_errno_and_return(ERANGE); mask = ibpi2ssd[ibpi]; t = strrchr(device->cntrl_path, '/'); if (t != NULL) { /* Extract PCI bus:device.function */ if (sscanf(t + 1, "%*x:%x:%x.%x", &bus, &dev, &fun) == 3) ipmi_setled(bus, dev, fun, mask); } return 0; } ledmon-0.90/src/dellssd.h000066400000000000000000000016001324101631400153020ustar00rootroot00000000000000/* * Dell Backplane LED control * Copyright (C) 2011, Dell Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #include "block.h" int dellssd_write(struct block_device *device, enum ibpi_pattern ibpi); char *dellssd_get_path(const char *path, const char *cntrl_path); ledmon-0.90/src/enclosure.c000066400000000000000000000063351324101631400156540ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2018 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #include #include #if _HAVE_DMALLOC_H #include #endif #include "config.h" #include "enclosure.h" #include "utils.h" /** * @brief Gets SAS address of an enclosure device. * * This is internal function of enclosure module. The function reads a * SAS address of an enclosure from sysfs attribute. * * @param[in] path Path to enclosure device in sysfs tree. * * @return SAS address of an enclosure if successful, otherwise 0. */ static uint64_t _get_sas_address(const char *path) { char tmp[PATH_MAX], buf[BUFFER_MAX]; char *p, *s; str_cpy(tmp, path, PATH_MAX); p = strstr(tmp, "/expander"); if (p == NULL) return 0; s = strchr(p + 1, PATH_DELIM); if (s == NULL) return 0; *s = '\0'; str_cpy(buf, p, s - p + 1); str_cat(tmp, "/sas_device", PATH_MAX); str_cat(tmp, buf, PATH_MAX); return get_uint64(tmp, 0, "sas_address"); } #define SCSI_GEN "device/scsi_generic" static char *_get_dev_sg(const char *encl_path) { char *ret = NULL; DIR *d; struct dirent *de; size_t sg_path_size = strlen(encl_path) + strlen(SCSI_GEN) + 2; char *sg_path = malloc(sg_path_size); if (!sg_path) return NULL; snprintf(sg_path, sg_path_size, "%s/%s", encl_path, SCSI_GEN); /* /sys/class/enclosure/X/device/scsi_generic path is expected. */ d = opendir(sg_path); free(sg_path); if (!d) return NULL; while ((de = readdir(d))) { if ((strcmp(de->d_name, ".") == 0) || (strcmp(de->d_name, "..") == 0)) continue; break; } if (de) { size_t size = strlen("/dev/") + strlen(de->d_name) + 1; ret = malloc(size); if (ret) snprintf(ret, size, "/dev/%s", de->d_name); } closedir(d); return ret; } /* * Allocates memory for enclosure device structure and initializes fields of * the structure. */ struct enclosure_device *enclosure_device_init(const char *path) { char temp[PATH_MAX]; struct enclosure_device *result = NULL; if (realpath(path, temp)) { result = calloc(1, sizeof(struct enclosure_device)); if (result == NULL) return NULL; result->sysfs_path = str_dup(temp); result->sas_address = _get_sas_address(temp); result->dev_path = _get_dev_sg(temp); } return result; } /* * The function returns memory allocated for fields of enclosure structure to * the system. */ void enclosure_device_fini(struct enclosure_device *enclosure) { if (enclosure) { free(enclosure->sysfs_path); free(enclosure->dev_path); free(enclosure); } } ledmon-0.90/src/enclosure.h000066400000000000000000000047351324101631400156630ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2018 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #ifndef _ENCLOSURE_H_INCLUDED_ #define _ENCLOSURE_H_INCLUDED_ #include #include "ses.h" /** * @brief Enclosure device structure. * * This structure describes an enclosure device connected to one of * SAS controllers existing in the system. */ struct enclosure_device { /** * Path to an enclosure device in sysfs tree. This is controller base * canonical path. */ char *sysfs_path; /** * SAS address as identifier of an enclosure. */ uint64_t sas_address; /** * Path to enclosure's sg device. */ char *dev_path; struct ses_pages *ses_pages; }; /** * @brief Allocates memory for an enclosure device structure. * * This function allocates memory for a new structure describing an enclosure * device. It reads the sysfs entries and populates structure fields. * The function uses libsas abstraction layer to extract required information. * * @param[in] path Path to an enclosure device in sysfs tree. * The path begins with "/sys/class/enclosure/". * * @return Pointer to enclosure device structure if successful, otherwise the * function returns NULL pointer. The NULL pointer means either the * specified path is invalid or there's not enough memory in the system * to allocated new structure. */ struct enclosure_device *enclosure_device_init(const char *path); /** * @brief Releases an enclosure device structure. * * This function releases memory allocated for enclosure device structure. * * @param[in] device Pointer to enclosure device structure. * * @return The function does not return a value. */ void enclosure_device_fini(struct enclosure_device *enclosure); #endif /* _ENCLOSURE_H_INCLUDED_ */ ledmon-0.90/src/ibpi.h000066400000000000000000000074331324101631400146050ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2018 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #ifndef _IBPI_H_INCLUDED_ #define _IBPI_H_INCLUDED_ /** * @brief IBPI pattern identifies. * * The IBPI specification lists the following pattern names: * * - NORMAL - either drive is present or missing, activity LED does not * matter. The rest of the LEDs are off. * - FAIL - a block device has failed or is missing. Failure LED is * active and the behavior is depended on implementation * of enclosure management processor. * - REBUILD - this means a RAID device is recovering or rebuilding * its data. Depending on implementation of enclosure * management processor appropriate LED is blinking or solid. * - ICA - In a Critical Array, this means a RAID device is degraded and * there's no spare device available. * - IFA - In a Failed Array, this means a RAID device is damaged and * cannot be recovered or rebuild. * - PFA - Predict Failure Analysis state means that a block device will * fail soon, so it must be replaced with working one. * - LOCATE - turns Locate LED on to identify a block device or slot. * * Additionally the following patterns has been introduced, just for the purpose * of LED control utility. * * - UNKNOWN - unknown IBPI pattern and it means do not control LEDs for * a device it is set (no LED management). * - ONESHOT_NORMAL - this state means that ledmon just started and it does not * know anything about existing patterns set, so it will off all * the LEDs just in case of any problem in the future. The state * is set, when a RAID device disappears, too. Oneshot means * as soon application applies the state it will change * to UNKNOWN. * - ADDED this state means that device previously known to ledmon is * restored. This state will be changed to ONESHOT_NORMAL. * - REMOVED this state means that device was removed from system. It * will be changed to ADDED after restoring device to system. */ enum ibpi_pattern { IBPI_PATTERN_UNKNOWN = 0, IBPI_PATTERN_NONE, /* used only to initialize ibpi_prev */ IBPI_PATTERN_NORMAL, IBPI_PATTERN_ONESHOT_NORMAL, IBPI_PATTERN_DEGRADED, IBPI_PATTERN_HOTSPARE, IBPI_PATTERN_REBUILD, IBPI_PATTERN_FAILED_ARRAY, IBPI_PATTERN_PFA, IBPI_PATTERN_FAILED_DRIVE, IBPI_PATTERN_LOCATE, IBPI_PATTERN_LOCATE_OFF, IBPI_PATTERN_ADDED, IBPI_PATTERN_REMOVED, /* Below are SES-2 codes. Note that by default most IBPI messages are * translated into SES when needed but SES codes can be added also. */ SES_REQ_ABORT, SES_REQ_REBUILD, SES_REQ_IFA, SES_REQ_ICA, SES_REQ_CONS_CHECK, SES_REQ_HOSTSPARE, SES_REQ_RSVD_DEV, SES_REQ_OK, SES_REQ_IDENT, SES_REQ_RM, SES_REQ_INS, SES_REQ_MISSING, SES_REQ_DNR, SES_REQ_ACTIVE, SES_REQ_EN_BB, SES_REQ_EN_BA, SES_REQ_DEV_OFF, SES_REQ_FAULT, SES_REQ_PRDFAIL, }; extern const char *ibpi_str[]; #endif /* _IBPI_H_INCLUDED_ */ ledmon-0.90/src/ledctl.c000066400000000000000000000534171324101631400151270ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2018 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if _HAVE_DMALLOC_H #include #endif #include "ahci.h" #include "block.h" #include "cntrl.h" #include "config.h" #include "config_file.h" #include "ibpi.h" #include "list.h" #include "scsi.h" #include "status.h" #include "sysfs.h" #include "utils.h" #include "version.h" /** * @brief An IBPI state structure. * * This structure connects an IBPI pattern and block devices. It is used by * _determine() function to figure the correct pattern out. */ struct ibpi_state { enum ibpi_pattern ibpi; struct list block_list; }; /** * @brief List of IBPI patterns. * * This is a list of IBPI patterns the user requested to be visualized. * Each element on the list is struct ibpi_state type. There's only one * instance of each IBPI pattern on the list (no duplicates). */ static struct list ibpi_list; /** * @brief IBPI pattern names. * * This is internal array holding names of IBPI pattern. Logging routines use * this entries to translate enumeration type values into the string. */ const char *ibpi_str[] = { [IBPI_PATTERN_UNKNOWN] = "", [IBPI_PATTERN_NORMAL] = "NORMAL", [IBPI_PATTERN_ONESHOT_NORMAL] = "", [IBPI_PATTERN_DEGRADED] = "ICA", [IBPI_PATTERN_REBUILD] = "REBUILD", [IBPI_PATTERN_FAILED_ARRAY] = "IFA", [IBPI_PATTERN_HOTSPARE] = "HOTSPARE", [IBPI_PATTERN_PFA] = "PFA", [IBPI_PATTERN_FAILED_DRIVE] = "FAILURE", [IBPI_PATTERN_LOCATE] = "LOCATE", [IBPI_PATTERN_LOCATE_OFF] = "LOCATE_OFF", [IBPI_PATTERN_ADDED] = "ADDED", [IBPI_PATTERN_REMOVED] = "REMOVED" }; /** * Internal variable of ledctl utility. It is the pattern used to print out * information about the version of ledctl utility. */ static char *ledctl_version = "Intel(R) Enclosure LED Control Application %d.%d\n" "Copyright (C) 2009-2018 Intel Corporation.\n"; /** * Internal variable of monitor service. It is used to help parse command line * short options. */ static char *shortopt = "hLxvl:"; /** * Internal enumeration type. It is used to help parse command line arguments. */ enum longopt { OPT_HELP, OPT_LOG, OPT_VERSION, OPT_LIST_CTRL, OPT_LISTED_ONLY, }; /** * Internal array with option tokens. It is used to help parse command line * long options. */ static struct option longopt[] = { [OPT_HELP] = {"help", no_argument, NULL, 'h'}, [OPT_LOG] = {"log", required_argument, NULL, 'l'}, [OPT_VERSION] = {"version", no_argument, NULL, 'v'}, [OPT_LIST_CTRL] = {"list-controllers", no_argument, NULL, 'L'}, [OPT_LISTED_ONLY] = {"listed-only", no_argument, NULL, 'x'}, {NULL, no_argument, NULL, '\0'} }; static int listed_only; static void ibpi_state_fini(struct ibpi_state *p) { list_clear(&p->block_list); free(p); } /** * @brief Finalizes LED control utility. * * This is internal function of ledctl utility. The function cleans up a memory * allocated for the application and closes all opened handles. This function is * design to be registered as on_exit() handler function. * * @param[in] status exit status of the ledctl application. * @param[in] ignored function ignores this argument. * * @return The function does not return a value. */ static void _ledctl_fini(int __attribute__ ((unused)) status, void *__attribute__ ((unused)) ignore) { sysfs_reset(); list_erase(&ibpi_list); log_close(); } /** * @brief Displays the credits. * * This is internal function of ledctl utility. It prints out the name and * version of the program. It displays the copyright notice and information * about the author and license, too. * * @return The function does not return a value. */ static void _ledctl_version(void) { printf(ledctl_version, VERSION_MAJOR, VERSION_MINOR); printf("\nThis is free software; see the source for copying conditions." \ " There is NO warranty;\nnot even for MERCHANTABILITY or FITNESS" \ " FOR A PARTICULAR PURPOSE.\n\n"); } /** * @brief Displays the help. * * This is internal function of ledctl utility. The function prints the name * and version of the program out. It displays the usage and available options * and its arguments (if any). Each option is described. This is an extract * from user manual page. * * @return The function does not return a value. */ static void _ledctl_help(void) { printf(ledctl_version, VERSION_MAJOR, VERSION_MINOR); printf("\nUsage: %s [OPTIONS] pattern=list_of_devices ...\n\n", progname); printf("Mandatory arguments for long options are mandatory for short options, too.\n\n"); print_opt("--listed-only", "-x", "Ledctl will change state only for given devices."); print_opt("--list-controllers", "-L", "Displays list of controllers detected by ledmon."); print_opt("--log=PATH", "-l PATH", "Use local log file instead /var/log/ledctl.log."); print_opt("--help", "-h", "Displays this help text."); print_opt("--version", "-v", "Displays version and license information."); printf("\nPatterns:\n" "\tCommon patterns are:\n" "\t\tlocate, locate_off, normal, off, degraded, rebuild,\n" "" "\t\tfailed_array, hotspare, pfa, failure, disk_failed\n" "\tSES-2 only patterns:\n" "\t\tses_abort, ses_rebuild, ses_ifa, ses_ica, ses_cons_check,\n" "\t\tses_hotspare, ses_rsvd_dev, ses_ok, ses_ident, ses_rm,\n" "\t\tses_insert, ses_missing, ses_dnr, ses_active, ses_prdfail,\n" "\t\tses_enable_bb, ses_enable_ba, ses_devoff, ses_fault\n" "\tAutomatic translation form IBPI into SES-2:\n" "\t\tlocate=ses_ident, locate_off=~ses_ident,\n" "\t\tnormal=ses_ok, off=ses_ok, degraded=ses_ica,\n" "\t\trebuild=ses_rebuild, failed_array=ses_ifa,\n" "\t\thotspare=ses_hotspare, pfa=ses_prdfail, failure=ses_fault,\n" "\t\tdisk_failed=ses_fault\n"); printf("Refer to ledctl(8) man page for more detailed description.\n"); printf("Bugs should be reported at: " \ "https://github.com/intel/ledmon/issues\n"); } /** * @brief Puts new IBPI state on the list. * * This is internal function of ledctl utility. The function creates a new entry * of the list with IBPI patterns. Each IBPI state has a list of block devices * attached. The function initializes this list and sets empty. * * @param[in] ibpi an IBPI pattern to add. * * @return Pointer to the created element if successful, otherwise function * returns NULL. The NULL pointer means element allocation failed. */ static struct ibpi_state *_ibpi_state_init(enum ibpi_pattern ibpi) { struct ibpi_state *state = malloc(sizeof(struct ibpi_state)); if (!state) return NULL; list_init(&state->block_list, NULL); state->ibpi = ibpi; list_append(&ibpi_list, state); return state; } /** * @brief Sets a state of block device. * * This is internal function of ledctl utility. The function sets * an IBPI pattern for block devices. The function is design to be used * as action parameter of list_for_each() function. * * @param[in] state pointer to structure holding the IBPI pattern * identifier and list of block devices. * * @return The function does not return a value. */ static void _determine(struct ibpi_state *state) { if (list_is_empty(&state->block_list) == 0) { struct block_device *block; list_for_each(&state->block_list, block) { if (block->ibpi < state->ibpi) block->ibpi = state->ibpi; } } else { log_warning ("IBPI %s: missing block device(s)... pattern ignored.", ibpi_str[state->ibpi]); } } /** * @brief Determines a state of block devices. * * This is internal function of ledctl utility. The functions goes through list * of IBPI states and calls _determine() function for each element. If the list * is empty the function logs a warning message and does nothing. * * @param[in] ibpi_list pointer to list of IBPI states. * * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. * The following status codes function returns: * * STATUS_LIST_EMPTY the specified list has no elements. */ static status_t _ibpi_state_determine(struct list *ibpi_list) { if (list_is_empty(ibpi_list) == 0) { struct ibpi_state *state; list_for_each(ibpi_list, state) _determine(state); return STATUS_SUCCESS; } log_error("missing operand(s)... run %s --help for details.", progname); return STATUS_LIST_EMPTY; } static struct ibpi_state *_ibpi_find(const struct list *ibpi_list, enum ibpi_pattern ibpi) { struct ibpi_state *state; list_for_each(ibpi_list, state) { if (state->ibpi == ibpi) return state; } return NULL; } /** * @brief Sets the path to configuration file. * * This is internal function of monitor service. This function sets the path and * name of configuration file. The function is checking whether the given path * is valid or it is invalid and should be ignored. * * @param[in] path the new location and name of config file. * * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. */ static status_t _set_config_path(const char *path) { (void)path; return STATUS_SUCCESS; } /** * @brief Sets the path to local log file. * * This is internal function of ledctl utility. This function sets the path and * file name of log file. The function checks if the specified path is valid. If * the path is invalid then the function does nothing. * * @param[in] path new location and name of log file. * * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. */ static status_t _set_log_path(const char *path) { char temp[PATH_MAX]; if (realpath(path, temp) == NULL) { if ((errno != ENOENT) && (errno != ENOTDIR)) return STATUS_INVALID_PATH; } if (log_open(temp) < 0) return STATUS_FILE_OPEN_ERROR; return STATUS_SUCCESS; } /** * @brief Gets a pointer to IBPI state structure. * * This is internal function of ledctl utility. The function retrieves an entry * to an IBPI state structure from ibpi_list list. If such an entry does not * exist the memory is allocated and entry is added to the list. * * @param[in] name a name of IBPI pattern i.e. taken from command * line interface. It might be 'locate', 'normal', * 'locate_off', 'off', 'ica', 'degraded', 'rebuild', * 'ifa', 'failed_array', 'hotspare', * 'pfa', 'failure' or 'disk_failed' string. * * @return Pointer to IBPI state structure if successful, otherwise the function * returns NULL. The NULL pointer means either the invalid status name * has been given or there's not enough memory available in the system * to allocate the structure. */ static struct ibpi_state *_ibpi_state_get(const char *name) { struct ibpi_state *state = NULL; enum ibpi_pattern ibpi; if (strcmp(name, "locate") == 0) { ibpi = IBPI_PATTERN_LOCATE; } else if (strcmp(name, "locate_off") == 0) { ibpi = IBPI_PATTERN_LOCATE_OFF; } else if (strcmp(name, "normal") == 0) { ibpi = IBPI_PATTERN_NORMAL; } else if (strcmp(name, "off") == 0) { ibpi = IBPI_PATTERN_NORMAL; } else if ((strcmp(name, "ica") == 0) || (strcmp(name, "degraded") == 0)) { ibpi = IBPI_PATTERN_DEGRADED; } else if (strcmp(name, "rebuild") == 0) { ibpi = IBPI_PATTERN_REBUILD; } else if ((strcmp(name, "ifa") == 0) || (strcmp(name, "failed_array") == 0)) { ibpi = IBPI_PATTERN_FAILED_ARRAY; } else if (strcmp(name, "hotspare") == 0) { ibpi = IBPI_PATTERN_HOTSPARE; } else if (strcmp(name, "pfa") == 0) { ibpi = IBPI_PATTERN_PFA; } else if ((strcmp(name, "failure") == 0) || (strcmp(name, "disk_failed") == 0)) { ibpi = IBPI_PATTERN_FAILED_DRIVE; } else if (strcmp(name, "ses_abort") == 0) { ibpi = SES_REQ_ABORT; } else if (strcmp(name, "ses_rebuild") == 0) { ibpi = SES_REQ_REBUILD; } else if (strcmp(name, "ses_ifa") == 0) { ibpi = SES_REQ_IFA; } else if (strcmp(name, "ses_ica") == 0) { ibpi = SES_REQ_ICA; } else if (strcmp(name, "ses_cons_check") == 0) { ibpi = SES_REQ_CONS_CHECK; } else if (strcmp(name, "ses_hotspare") == 0) { ibpi = SES_REQ_HOSTSPARE; } else if (strcmp(name, "ses_rsvd_dev") == 0) { ibpi = SES_REQ_RSVD_DEV; } else if (strcmp(name, "ses_ok") == 0) { ibpi = SES_REQ_OK; } else if (strcmp(name, "ses_ident") == 0) { ibpi = SES_REQ_IDENT; } else if (strcmp(name, "ses_rm") == 0) { ibpi = SES_REQ_RM; } else if (strcmp(name, "ses_insert") == 0) { ibpi = SES_REQ_INS; } else if (strcmp(name, "ses_missing") == 0) { ibpi = SES_REQ_MISSING; } else if (strcmp(name, "ses_dnr") == 0) { ibpi = SES_REQ_DNR; } else if (strcmp(name, "ses_active") == 0) { ibpi = SES_REQ_ACTIVE; } else if (strcmp(name, "ses_enable_bb") == 0) { ibpi = SES_REQ_EN_BB; } else if (strcmp(name, "ses_enable_ba") == 0) { ibpi = SES_REQ_EN_BA; } else if (strcmp(name, "ses_devoff") == 0) { ibpi = SES_REQ_DEV_OFF; } else if (strcmp(name, "ses_fault") == 0) { ibpi = SES_REQ_FAULT; } else if (strcmp(name, "ses_prdfail") == 0) { ibpi = SES_REQ_PRDFAIL; } else { return NULL; } state = _ibpi_find(&ibpi_list, ibpi); if (state == NULL) state = _ibpi_state_init(ibpi); return state; } static struct block_device *_block_device_search(const struct list *block_list, const char *path) { struct block_device *block; list_for_each(block_list, block) { if (strcmp(block->sysfs_path, path) == 0) return block; } return NULL; } /** * @brief Adds a block device to a block list. * * This is internal function of ledctl utility. Each IBPI state has list of * block devices attached to. The function puts a pointer to a block device * on that list. First the function determines the canonical version of the * given path and checks if it is correct. If the path to /dev directory is * given the function finds out the correct entry in sysfs tree. * * @param[in] state pointer to IBPI state structure the block * device will be added to. * @param[in] block pointer to block device structure. * * @return The function does not return a value. */ static status_t _ibpi_state_add_block(struct ibpi_state *state, char *name) { struct stat st; char temp[PATH_MAX], path[PATH_MAX]; struct block_device *blk1, *blk2; if ((realpath(name, temp) == NULL) && (errno != ENOTDIR)) return STATUS_INVALID_PATH; if (strstr(temp, "/dev/") != NULL) { if (stat(temp, &st) < 0) return STATUS_STAT_ERROR; sprintf(temp, "/sys/dev/block/%d:%d", major(st.st_rdev), minor(st.st_rdev)); if ((realpath(temp, path) == NULL) && (errno != ENOTDIR)) return STATUS_INVALID_PATH; } else { str_cpy(path, temp, PATH_MAX); } blk1 = _block_device_search(sysfs_get_block_devices(), path); if (blk1 == NULL) { log_error("%s: device not supported", name); return STATUS_NOT_SUPPORTED; } blk2 = _block_device_search(&state->block_list, path); if (blk2 == NULL) list_append(&state->block_list, blk1); else log_info("%s: %s: device already on the list.", ibpi_str[state->ibpi], path); return STATUS_SUCCESS; } /** * @brief Command line parser - operands. * * This is internal function of ledctl utility. The function parses operands of * ledctl application. The operands section contains the pattern name and a list * of block devices associated with each pattern. There are two different * formats for the operand. First format is pattern={ dev_list }, where elements * are space separated on the dev_list. Second format is pattern=dev1,dev2,... * where elements are comma separated on the list of devices. * * @param[in] argc number of elements in argv array. * @param[in] argv command line arguments. * * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. */ static status_t _cmdline_ibpi_parse(int argc, char *argv[]) { while (optind < argc) { struct ibpi_state *state = NULL; char *p = argv[optind++]; char *t; t = strchrnul(p, '='); if (*t != '\0') { *(t++) = '\0'; state = _ibpi_state_get(p); if (state != NULL) { if (*t == '{') { while ((t = argv[optind++]) != NULL) { if (*t == '}') break; _ibpi_state_add_block(state, t); } } else { while (*(p = t) != '\0') { t = strchrnul(p, ','); if (*t != '\0') *(t++) = '\0'; _ibpi_state_add_block(state, p); } } } } if (state == NULL) { log_error("%s - unknown pattern name.", p); exit(STATUS_INVALID_STATE); } } return STATUS_SUCCESS; } /** * @brief Command line parser - options. * * This is internal function of ledctl utility. The function parses options of * ledctl application. Refer to ledctl help in order to get more information * about ledctl command line options. * * @param[in] argc number of elements in argv array. * @param[in] argv command line arguments. * * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. */ static status_t _cmdline_parse(int argc, char *argv[]) { int opt, opt_index = -1; status_t status = STATUS_SUCCESS; do { opt = getopt_long(argc, argv, shortopt, longopt, &opt_index); if (opt == -1) break; switch (opt) { case 'v': _ledctl_version(); exit(EXIT_SUCCESS); case 'h': _ledctl_help(); exit(EXIT_SUCCESS); case 'c': status = _set_config_path(optarg); break; case 'l': status = _set_log_path(optarg); break; case 'x': status = STATUS_SUCCESS; listed_only = 1; break; case 'L': { struct cntrl_device *ctrl_dev; sysfs_init(); sysfs_scan(); list_for_each(sysfs_get_cntrl_devices(), ctrl_dev) print_cntrl(ctrl_dev); sysfs_reset(); exit(EXIT_SUCCESS); } case ':': case '?': default: log_debug("[opt='%c', opt_index=%d]", opt, opt_index); return STATUS_CMDLINE_ERROR; } opt_index = -1; if (status != STATUS_SUCCESS) return status; } while (1); return STATUS_SUCCESS; } /** * @brief Determine and send IBPI pattern. * * This is internal function of ledctl utility. The function determines a state * of block device based on ibpi_list list. Then it sends a LED control message * to controller to visualize the pattern. * * @param[in] sysfs pointer to sysfs structure holding information * about the existing controllers, block devices, * and software RAID devices. * @param[in] ibpi_list TBD * * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. */ static status_t _ledctl_execute(struct list *ibpi_list) { struct ibpi_state *state; struct block_device *device; if (_ibpi_state_determine(ibpi_list) != STATUS_SUCCESS) return STATUS_IBPI_DETERMINE_ERROR; if (!listed_only) { list_for_each(sysfs_get_block_devices(), device) device->send_fn(device, IBPI_PATTERN_LOCATE_OFF); } list_for_each(ibpi_list, state) list_for_each(&state->block_list, device) device->send_fn(device, device->ibpi); list_for_each(sysfs_get_block_devices(), device) device->flush_fn(device); return STATUS_SUCCESS; } static status_t _init_ledctl_conf(void) { memset(&conf, 0, sizeof(struct ledmon_conf)); /* initialize with default values */ conf.log_level = LOG_LEVEL_WARNING; list_init(&conf.cntrls_whitelist, NULL); list_init(&conf.cntrls_blacklist, NULL); return _set_log_path(LEDCTL_DEF_LOG_FILE); } /** * @brief Application's entry point. * * This is the entry point of ledctl utility. This function does all the work. * It allocates and initializes all used structures. Registers on_exit() * handlers. * Then the function parses command line options and commands given and scans * sysfs tree for controllers, block devices and RAID devices. If no error is * the function send LED control messages according to IBPI pattern set. * * @param[in] argc number of elements in argv array, number of * command line arguments. * @param[in] argv array of command line arguments. The last * element on the list is NULL pointer. * * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. */ int main(int argc, char *argv[]) { status_t status; set_invocation_name(argv[0]); openlog(progname, LOG_PERROR, LOG_USER); if (getuid() != 0) { fprintf(stderr, "Only root can run this application.\n"); return STATUS_NOT_A_PRIVILEGED_USER; } status = _init_ledctl_conf(); if (status != STATUS_SUCCESS) return status; if (on_exit(_ledctl_fini, progname)) exit(STATUS_ONEXIT_ERROR); if (_cmdline_parse(argc, argv)) exit(STATUS_CMDLINE_ERROR); list_init(&ibpi_list, (item_free_t)ibpi_state_fini); sysfs_init(); sysfs_scan(); status = _cmdline_ibpi_parse(argc, argv); if (status != STATUS_SUCCESS) { log_debug("main(): _ibpi_parse() failed (status=%s).", strstatus(status)); exit(STATUS_INVALID_STATE); } return _ledctl_execute(&ibpi_list); } ledmon-0.90/src/ledmon.c000066400000000000000000000707201324101631400151320ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2018 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if _HAVE_DMALLOC_H #include #endif #include "ahci.h" #include "block.h" #include "cntrl.h" #include "config.h" #include "config_file.h" #include "ibpi.h" #include "list.h" #include "pidfile.h" #include "raid.h" #include "scsi.h" #include "slave.h" #include "smp.h" #include "status.h" #include "sysfs.h" #include "udev.h" #include "utils.h" #include "version.h" #include "vmdssd.h" /** * @brief List of active block devices. * * This list holds all block devices attached to supported storage controllers. * Only devices which have enclosure management feature enabled are on the * list, other devices are ignored (except protocol is forced). */ static struct list ledmon_block_list; /** * @brief Daemon process termination flag. * * This flag indicates that daemon process should terminate. User must send * SIGTERM to daemon in order to terminate the process gently. */ static sig_atomic_t terminate; /** * @brief Path to ledmon configuration file. * * This string contains path of the ledmon configuration file. The value is * set to LEDMON_DEF_CONF_FILE by default and it can be changed by command line * option. */ static char *ledmon_conf_path; /** * @brief Name of IBPI patterns. * * This is internal array with names of IBPI patterns. Logging routines use this * entries to translate enumeration type into the string. */ const char *ibpi_str[] = { [IBPI_PATTERN_UNKNOWN] = "None", [IBPI_PATTERN_NORMAL] = "Off", [IBPI_PATTERN_ONESHOT_NORMAL] = "Oneshot Off", [IBPI_PATTERN_DEGRADED] = "In a Critical Array", [IBPI_PATTERN_REBUILD] = "Rebuild", [IBPI_PATTERN_FAILED_ARRAY] = "In a Failed Array", [IBPI_PATTERN_HOTSPARE] = "Hotspare", [IBPI_PATTERN_PFA] = "Predicted Failure Analysis", [IBPI_PATTERN_FAILED_DRIVE] = "Failure", [IBPI_PATTERN_LOCATE] = "Locate", [IBPI_PATTERN_LOCATE_OFF] = "Locate Off", [IBPI_PATTERN_ADDED] = "Added", [IBPI_PATTERN_REMOVED] = "Removed" }; /** * Internal variable of monitor service. It is the pattern used to print out * information about the version of monitor service. */ static char *ledmon_version = "Intel(R) Enclosure LED Monitor Service %d.%d\n" "Copyright (C) 2009-2018 Intel Corporation.\n"; /** * Internal variable of monitor service. It is used to help parse command line * short options. */ static char *shortopt = "t:c:hvl:"; /** * Internal enumeration type. It is used to help parse command line arguments. */ enum longopt { OPT_ALL, OPT_CONFIG, OPT_DEBUG, OPT_ERROR, OPT_HELP, OPT_INFO, OPT_INTERVAL, OPT_LOG, OPT_QUIET, OPT_VERSION, OPT_WARNING, OPT_LOG_LEVEL, }; /** * Internal array with option tokens. It is used to help parse command line * long options. */ static struct option longopt[] = { [OPT_ALL] = {"all", no_argument, NULL, '\0'}, [OPT_CONFIG] = {"config", required_argument, NULL, 'c'}, [OPT_DEBUG] = {"debug", no_argument, NULL, '\0'}, [OPT_ERROR] = {"error", no_argument, NULL, '\0'}, [OPT_HELP] = {"help", no_argument, NULL, 'h'}, [OPT_INFO] = {"info", no_argument, NULL, '\0'}, [OPT_INTERVAL] = {"interval", required_argument, NULL, 't'}, [OPT_LOG] = {"log", required_argument, NULL, 'l'}, [OPT_QUIET] = {"quiet", no_argument, NULL, '\0'}, [OPT_VERSION] = {"version", no_argument, NULL, 'v'}, [OPT_WARNING] = {"warning", no_argument, NULL, '\0'}, [OPT_LOG_LEVEL] = {"log-level", required_argument, NULL, '\0'}, {NULL, no_argument, NULL, '\0'} }; /** * @brief Monitor service finalize function. * * This is internal function of monitor service. It is used to finalize daemon * process i.e. free allocated memory, unlock and remove pidfile and close log * file and syslog. The function is registered as on_exit() handler. * * @param[in] status The function ignores this parameter. * @param[in] progname The name of the binary file. This argument * is passed via on_exit() function. * * @return The function does not return a value. */ static void _ledmon_fini(int __attribute__ ((unused)) status, void *progname) { sysfs_reset(); list_erase(&ledmon_block_list); log_close(); pidfile_remove(progname); } /** * @brief Puts exit status to a log file. * * This is internal function of monitor service. It is used to report an exit * status of the monitor service. The message is logged in to syslog and to log * file. The function is registered as on_exit() hander. * * @param[in] status Status given in the last call to exit() * function. * @param[in] ignore Pointer to placeholder where ignore flag is * stored. If flag is set 0 then parent process * is exiting, otherwise a child is exiting. * This argument must not be NULL pointer. * * @return The function does not return a value. */ static void _ledmon_status(int status, void *ignore) { if (*((int *)ignore) != 0) log_info("exit status is %s.", strstatus(status)); else if (status != STATUS_SUCCESS) log_error("parent exit status is %s.", strstatus(status)); } /** * @brief Displays the credits. * * This is internal function of monitor service. It prints out the name and * version of the program. It displays the copyright notice and information * about the author and license, too. * * @return The function does not return a value. */ static void _ledmon_version(void) { printf(ledmon_version, VERSION_MAJOR, VERSION_MINOR); printf("\nThis is free software; see the source for copying conditions." " There is NO warranty;\nnot even for MERCHANTABILITY or FITNESS" " FOR A PARTICULAR PURPOSE.\n\n"); } /** * @brief Displays the help. * * This is internal function of monitor service. The function prints the name * and version of the program out. It displays the usage and available options * and its arguments (if any). Each option is described. This is an extract * from user manual page. * * @return The function does not return a value. */ static void _ledmon_help(void) { printf(ledmon_version, VERSION_MAJOR, VERSION_MINOR); printf("\nUsage: %s [OPTIONS]\n\n", progname); printf("Mandatory arguments for long options are mandatory for short " "options, too.\n\n"); print_opt("--interval=VALUE", "-t VALUE", "Set time interval to VALUE seconds."); print_opt("", "", "The smallest interval is 5 seconds."); print_opt("--config=PATH", "-c PATH", "Use alternate configuration file."); print_opt("--log=PATH", "-l PATH", "Use local log file instead /var/log/ledmon.log"); print_opt("--log-level=VALUE", "-l VALUE", "Allows user to set ledmon verbose level in logs."); print_opt("--help", "-h", "Displays this help text."); print_opt("--version", "-v", "Displays version and license information."); printf("\nRefer to ledmon(8) man page for more detailed description.\n"); printf("Bugs should be reported at: https://github.com/intel/ledmon/issues\n"); } /** * @brief Sets the path to configuration file. * * This is internal function of monitor service. This function sets the path and * name of configuration file. The function is checking whether the given path * is valid or it is invalid and should be ignored. * * @param[in] path the new location and name of config file. * * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. */ static status_t _set_config_path(char **conf_path, const char *path) { if (!path) path = LEDMON_DEF_CONF_FILE; if (*conf_path) free(*conf_path); *conf_path = strdup(path); return STATUS_SUCCESS; } /** * @brief Sets the value of sleep interval. * * This function is used by command line handler to set new value of time * interval, @see time_interval for details. * * @param[in] optarg String containing the new value of time * interval, given in command line option. * * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. */ static status_t _set_sleep_interval(const char *optarg) { conf.scan_interval = atoi(optarg); if (conf.scan_interval < LEDMON_MIN_SLEEP_INTERVAL) { log_warning("sleep interval too small... using default."); conf.scan_interval = LEDMON_DEF_SLEEP_INTERVAL; } return STATUS_SUCCESS; } /** * @brief Sets verbose variable to given level. * * This is internal function of monitor service. The function maps given level * to the value from verbose_level enum and sets verbose value to ledmon * configuration. * * @param[in] log_level required new log_level. * * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. */ static status_t _set_verbose_level(int log_level) { int new_verbose = -1; switch (log_level) { case OPT_ALL: new_verbose = LOG_LEVEL_ALL; break; case OPT_DEBUG: new_verbose = LOG_LEVEL_DEBUG; break; case OPT_ERROR: new_verbose = LOG_LEVEL_ERROR; break; case OPT_INFO: new_verbose = LOG_LEVEL_INFO; break; case OPT_QUIET: new_verbose = LOG_LEVEL_QUIET; break; case OPT_WARNING: new_verbose = LOG_LEVEL_WARNING; break; } if (new_verbose != -1) { conf.log_level = new_verbose; return STATUS_SUCCESS; } return STATUS_CMDLINE_ERROR; } /** * @brief Gets id for given CLI option which corresponds to value from longopt * table. * * This is internal function of monitor service. The function maps given string * to the value from longopt enum and returns id of matched element. Generic * parameters allow to use this function for any CLI options-table which bases * on option struct. * * @param[in] optarg String containing value given by user in CLI. * @param[in] longopt Table of allowed CLI options. * * @return integer id if successful, otherwise a -1. */ static int _get_option_id(const char *optarg, struct option longopt[]) { struct option *i = longopt; while (i->name != NULL) { if (strcmp(i->name, optarg) == 0) return i-longopt; i++; } return -1; } /** * @brief Reads config file path and checks if command line input contains * options which don't require to run ledmon as daemon. * * This is internal function of monitor service. This function looks for * config file path in command line options given to the program from * command line interface. It also handles options to print help and version. * * @param[in] argc - number of arguments. * @param[in] argv - array of command line arguments. * * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. */ static status_t _cmdline_parse_non_daemonise(int argc, char *argv[]) { int opt_index = -1; int opt = -1; status_t status = STATUS_SUCCESS; do { opt = getopt_long(argc, argv, shortopt, longopt, &opt_index); switch (opt) { case 'c': status = _set_config_path(&ledmon_conf_path, optarg); break; case 'h': _ledmon_help(); exit(EXIT_SUCCESS); case 'v': _ledmon_version(); exit(EXIT_SUCCESS); case ':': case '?': return STATUS_CMDLINE_ERROR; } } while (opt >= 0); return status; } /** * @brief Command line interface handler function. * * This is internal function of monitor service. This function interprets the * options and commands given to the program from command line interface. * * @param[in] argc - number of arguments. * @param[in] argv - array of command line arguments. * * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. */ static status_t _cmdline_parse(int argc, char *argv[]) { int opt, opt_index = -1; status_t status = STATUS_SUCCESS; optind = 1; do { opt = getopt_long(argc, argv, shortopt, longopt, &opt_index); if (opt == -1) break; if (opt == 'c') continue; switch (opt) { int new_log_level; case 0: switch (opt_index) { case OPT_LOG_LEVEL: new_log_level = _get_option_id(optarg, longopt); if (new_log_level != -1) status = _set_verbose_level(new_log_level); else status = STATUS_CMDLINE_ERROR; break; default: status = _set_verbose_level(opt); } break; case 'l': status = set_log_path(optarg); break; case 't': status = _set_sleep_interval(optarg); break; } opt_index = -1; if (status != STATUS_SUCCESS) return status; } while (1); return STATUS_SUCCESS; } /** * @brief SIGTERM handler function. * * This is internal function of monitor service. * * @param[in] signum - the number of signal received. * * @return The function does not return a value. */ static void _ledmon_sig_term(int signum) { if (signum == SIGTERM) { log_info("SIGTERM caught - terminating daemon process."); terminate = 1; } } /** * @brief Configures signal handlers. * * This is internal function of monitor services. It sets to ignore SIGALRM, * SIGHUP and SIGPIPE signals. The function installs a handler for SIGTERM * signal. User must send SIGTERM to daemon process in order to shutdown the * daemon gently. * * @return The function does not return a value. */ static void _ledmon_setup_signals(void) { struct sigaction act; sigset_t sigset; sigemptyset(&sigset); sigaddset(&sigset, SIGALRM); sigaddset(&sigset, SIGHUP); sigaddset(&sigset, SIGTERM); sigaddset(&sigset, SIGPIPE); sigaddset(&sigset, SIGUSR1); sigprocmask(SIG_BLOCK, &sigset, NULL); act.sa_handler = SIG_IGN; act.sa_flags = 0; sigemptyset(&act.sa_mask); sigaction(SIGALRM, &act, NULL); sigaction(SIGHUP, &act, NULL); sigaction(SIGPIPE, &act, NULL); act.sa_handler = _ledmon_sig_term; sigaction(SIGTERM, &act, NULL); sigaction(SIGUSR1, &act, NULL); sigprocmask(SIG_UNBLOCK, &sigset, NULL); } /** * @brief Puts the calling process into sleep. * * This is internal function of monitor service. The function puts the calling * process into a sleep for the given amount of time (expressed in seconds). The * function will give control back to the process as soon as time elapses or * SIGTERM occurs. * * @param[in] seconds - the time interval given in seconds. * * @return The function does not return a value. */ static void _ledmon_wait(int seconds) { int fd, udev_fd, max_fd, res; fd_set rdfds, exfds; struct timespec timeout; sigset_t sigset; sigprocmask(SIG_UNBLOCK, NULL, &sigset); sigdelset(&sigset, SIGTERM); timeout.tv_nsec = 0; timeout.tv_sec = seconds; fd = open("/proc/mdstat", O_RDONLY); udev_fd = get_udev_monitor(); max_fd = MAX(fd, udev_fd) + 1; do { FD_ZERO(&rdfds); FD_ZERO(&exfds); if (fd > 0) FD_SET(fd, &exfds); if (udev_fd > 0) FD_SET(udev_fd, &rdfds); res = pselect(max_fd, &rdfds, NULL, &exfds, &timeout, &sigset); if (!FD_ISSET(udev_fd, &rdfds) || handle_udev_event(&ledmon_block_list) <= 0) break; } while (res > 0); if (fd >= 0) close(fd); } /** * @brief Determine failed state by comparing saved block device with new * scanned. * * This is internal function of monitor service. Due race conditions related * with removing files from /sys/block/md* when raid is stopped or disk is * failed, this function analyse state of every block device between scans. * * @param[in] block Pointer to new (scanned) block device * structure. * @param[in] temp Pointer to previously saved state of block * device structure. * * @return The function does not return a value. */ static void _handle_fail_state(struct block_device *block, struct block_device *temp) { struct raid_device *temp_raid_device = NULL; if (!temp->raid_dev) /* * Device is a RAID member now, so keep information about * related with device RAID. */ temp->raid_dev = raid_device_duplicate(block->raid_dev); if (!temp->raid_dev) return; temp_raid_device = find_raid_device(sysfs_get_volumes(), temp->raid_dev->sysfs_path); if (!block->raid_dev) { if (temp->raid_dev->type == DEVICE_TYPE_VOLUME && temp_raid_device) { /* * Device is outside of the volume, but related raid * still exist, so disk has been removed from volume - * blink fail LED. It is case when drive is removed * by mdadm -If. */ temp->ibpi = IBPI_PATTERN_FAILED_DRIVE; /* * Changing failed state to hotspare will be prevent by * code from _add_block function. If disk come back to * container failed state should be removed. By setting * type to CONTAINER ledmon can react in this case. */ temp->raid_dev->type = DEVICE_TYPE_CONTAINER; } else { /* * Device was RAID member and was failed (was outside * of array and container). Now again is in container. * Release object to perform hotspare state. * Or: * Device is outside of the volume, but related raid is * removed (or stopped) so device is no longer a RAID * member. */ raid_device_fini(temp->raid_dev); temp->raid_dev = NULL; } } else if (block->raid_dev) { if (temp->raid_dev->type == DEVICE_TYPE_VOLUME && block->raid_dev->type == DEVICE_TYPE_CONTAINER) { /* * Drive is removed from volume, but still exist * in container. */ enum raid_level new_level; if (!temp_raid_device) new_level = RAID_LEVEL_UNKNOWN; else new_level = temp_raid_device->level; if ((temp->raid_dev->level == RAID_LEVEL_10 || temp->raid_dev->level == RAID_LEVEL_1) && new_level == RAID_LEVEL_0) { /* * Device is removed from volume due to * migration to raid. State of this disk is * hotspare now. */ temp->ibpi = IBPI_PATTERN_HOTSPARE; } else { /* * Trasitions other than raid 0 migration. * Like reshape, volume stopping etc. */ if (!temp_raid_device) { if (block->raid_dev->sync_action != RAID_ACTION_RESHAPE) /* * Fail state should be * released,because disk is in * reshape process. */ temp->ibpi = IBPI_PATTERN_HOTSPARE; } else { /* * Drive is removed from volume, * but still exist in container. This * situation can be caused by bad * blocks or calling mdadm * --set-faulty. */ temp->ibpi = IBPI_PATTERN_FAILED_DRIVE; } } } else if (temp->raid_dev->type == DEVICE_TYPE_CONTAINER && block->raid_dev->type == DEVICE_TYPE_VOLUME) { /* * Disk was in container and is added to volume. * Release object for recreating. */ raid_device_fini(temp->raid_dev); temp->raid_dev = raid_device_duplicate(block->raid_dev); } } } /** * @brief Adds the block device to list. * * This is internal function of monitor service. The function adds a block * device to the ledmon_block_list list or if the device is already on the list * it updates the IBPI state of the given device. The function updates timestamp * value which indicates the time of last structure modification. The function * is design to be used as 'action' parameter of list_for_each() function. * Each change of state is logged to the file and to the syslog. * * @param[in] block Pointer to block device structure. * * @return The function does not return a value. */ static void _add_block(struct block_device *block) { struct block_device *temp = NULL; list_for_each(&ledmon_block_list, temp) { if (block_compare(temp, block)) break; temp = NULL; } if (temp) { enum ibpi_pattern ibpi = temp->ibpi; temp->timestamp = block->timestamp; if (temp->ibpi == IBPI_PATTERN_ADDED) { temp->ibpi = IBPI_PATTERN_ONESHOT_NORMAL; } else if (temp->ibpi == IBPI_PATTERN_ONESHOT_NORMAL) { temp->ibpi = IBPI_PATTERN_UNKNOWN; } else if (temp->ibpi != IBPI_PATTERN_FAILED_DRIVE) { if (block->ibpi == IBPI_PATTERN_UNKNOWN) { if ((temp->ibpi != IBPI_PATTERN_UNKNOWN) && (temp->ibpi != IBPI_PATTERN_NORMAL)) { temp->ibpi = IBPI_PATTERN_ONESHOT_NORMAL; } else { temp->ibpi = IBPI_PATTERN_UNKNOWN; } } else { temp->ibpi = block->ibpi; } } else if (!(temp->ibpi == IBPI_PATTERN_FAILED_DRIVE && block->ibpi == IBPI_PATTERN_HOTSPARE) || (temp->ibpi == IBPI_PATTERN_FAILED_DRIVE && block->ibpi == IBPI_PATTERN_NONE)) { temp->ibpi = block->ibpi; } _handle_fail_state(block, temp); if (ibpi != temp->ibpi && ibpi <= IBPI_PATTERN_REMOVED) { log_info("CHANGE %s: from '%s' to '%s'.", temp->sysfs_path, ibpi_str[ibpi], ibpi_str[temp->ibpi]); } /* Check if name of the device changed.*/ if (strcmp(temp->sysfs_path, block->sysfs_path)) { log_info("NAME CHANGED %s to %s", temp->sysfs_path, block->sysfs_path); free(temp->sysfs_path); temp->sysfs_path = strdup(block->sysfs_path); } } else { /* Device not found, it's a new one! */ temp = block_device_duplicate(block); if (temp != NULL) { log_info("NEW %s: state '%s'.", temp->sysfs_path, ibpi_str[temp->ibpi]); list_append(&ledmon_block_list, temp); } } } /** * @brief Sends LED control message. * * This is internal function of monitor service. The function sends a LED * command to storage controller or enclosure device. The function checks * the time of last modification of block device structure. If the timestamp * is different then the current global timestamp this means the device is * missing due to hot-remove or hardware failure so it must be reported on * LEDs appropriately. Note that controller device and host attached to this * block device points to invalid pointer so it must be 'refreshed'. * * @param[in] block Pointer to block device structure. * * @return The function does not return a value. */ static void _send_msg(struct block_device *block) { if (!block->cntrl) { log_debug("Missing cntrl for dev: %s. Not sending anything.", strstr(block->sysfs_path, "host")); return; } if (block->timestamp != timestamp || block->ibpi == IBPI_PATTERN_REMOVED) { if (block->ibpi != IBPI_PATTERN_FAILED_DRIVE) { log_info("CHANGE %s: from '%s' to '%s'.", block->sysfs_path, ibpi_str[block->ibpi], ibpi_str[IBPI_PATTERN_FAILED_DRIVE]); block->ibpi = IBPI_PATTERN_FAILED_DRIVE; } else { char *host = strstr(block->sysfs_path, "host"); log_debug("DETACHED DEV '%s' in failed state", host ? host : block->sysfs_path); } } block->send_fn(block, block->ibpi); block->ibpi_prev = block->ibpi; } static void _flush_msg(struct block_device *block) { if (!block->cntrl) return; block->flush_fn(block); } static void _revalidate_dev(struct block_device *block) { /* Bring back controller and host to the device. */ block->cntrl = block_get_controller(sysfs_get_cntrl_devices(), block->cntrl_path); if (!block->cntrl) { /* It could be removed VMD drive */ log_debug("Failed to get controller for dev: %s, ctrl path: %s", block->sysfs_path, block->cntrl_path); return; } if (block->cntrl->cntrl_type == CNTRL_TYPE_SCSI) { block->host = block_get_host(block->cntrl, block->host_id); if (block->host) { if (dev_directly_attached(block->sysfs_path)) cntrl_init_smp(NULL, block->cntrl); else scsi_get_enclosure(block); } else { log_debug("Failed to get host for dev: %s, hostId: %d", block->sysfs_path, block->host_id); /* If failed, invalidate cntrl */ block->cntrl = NULL; } } return; } static void _invalidate_dev(struct block_device *block) { /* Those fields are valid only per 'session' - through single scan. */ block->cntrl = NULL; block->host = NULL; block->enclosure = NULL; block->encl_index = -1; } static void _check_block_dev(struct block_device *block, int *restart) { if (!block->cntrl) { (*restart)++; } } /** * @brief Sets a list of block devices and sends LED control messages. * * This is internal function of monitor service. Based on current layout of * sysfs tree the function extracts block devices and for each block device it * send LED control message to storage controller or enclosure. The message is * determine by appropriate field in block device's structure. See _add_block() * and _send_msg() functions description for more details. * * @return The function does not return a value. */ static void _ledmon_execute(void) { int restart = 0; /* ledmon_block_list needs restart? */ struct block_device *device; /* Revalidate each device in the list. Bring back controller and host */ list_for_each(&ledmon_block_list, device) _revalidate_dev(device); /* Scan all devices and compare them against saved list */ list_for_each(sysfs_get_block_devices(), device) _add_block(device); /* Send message to all devices in the list if needed. */ list_for_each(&ledmon_block_list, device) _send_msg(device); /* Flush unsent messages from internal buffers. */ list_for_each(&ledmon_block_list, device) _flush_msg(device); /* Check if there is any orphaned device. */ list_for_each(&ledmon_block_list, device) _check_block_dev(device, &restart); if (restart) { /* there is at least one detached element in the list. */ list_erase(&ledmon_block_list); } } static status_t _init_ledmon_conf(void) { memset(&conf, 0, sizeof(struct ledmon_conf)); /* initialize with default values */ conf.blink_on_init = 1; conf.blink_on_migration = 1; conf.rebuild_blink_on_all = 0; conf.raid_memebers_only = 0; conf.log_level = LOG_LEVEL_WARNING; conf.scan_interval = LEDMON_DEF_SLEEP_INTERVAL; list_init(&conf.cntrls_whitelist, NULL); list_init(&conf.cntrls_blacklist, NULL); return set_log_path(LEDMON_DEF_LOG_FILE); } static void _close_parent_fds(void) { struct list dir; if (scan_dir("/proc/self/fd", &dir) == 0) { char *elem; list_for_each(&dir, elem) { int fd = (int)strtol(basename(elem), NULL, 10); if (fd != get_log_fd()) close(fd); } list_erase(&dir); } } /** */ int main(int argc, char *argv[]) { status_t status = STATUS_SUCCESS; set_invocation_name(argv[0]); openlog(progname, LOG_PID | LOG_PERROR, LOG_DAEMON); if (_cmdline_parse_non_daemonise(argc, argv) != STATUS_SUCCESS) return STATUS_CMDLINE_ERROR; if (getuid() != 0) { fprintf(stderr, "Only root can run this application.\n"); return STATUS_NOT_A_PRIVILEGED_USER; } status = _init_ledmon_conf(); if (status != STATUS_SUCCESS) return status; if (on_exit(_ledmon_status, &terminate)) return STATUS_ONEXIT_ERROR; status = ledmon_read_config(ledmon_conf_path); if (status != STATUS_SUCCESS) return status; if(conf.log_path) set_log_path(conf.log_path); if (_cmdline_parse(argc, argv) != STATUS_SUCCESS) return STATUS_CMDLINE_ERROR; if (pidfile_check(progname, NULL) == 0) { log_warning("daemon is running..."); return STATUS_LEDMON_RUNNING; } pid_t pid = fork(); if (pid < 0) { log_debug("main(): fork() failed (errno=%d).", errno); exit(EXIT_FAILURE); } if (pid > 0) exit(EXIT_SUCCESS); pid_t sid = setsid(); if (sid < 0) { log_debug("main(): setsid() failed (errno=%d).", errno); exit(EXIT_FAILURE); } _close_parent_fds(); int t = open("/dev/null", O_RDWR); dup(t); dup(t); umask(027); if (chdir("/") < 0) { log_debug("main(): chdir() failed (errno=%d).", errno); exit(EXIT_FAILURE); } if (pidfile_create(progname)) { log_debug("main(): pidfile_creat() failed."); exit(EXIT_FAILURE); } _ledmon_setup_signals(); if (on_exit(_ledmon_fini, progname)) exit(STATUS_ONEXIT_ERROR); list_init(&ledmon_block_list, (item_free_t)block_device_fini); sysfs_init(); log_info("monitor service has been started..."); while (terminate == 0) { struct block_device *device; timestamp = time(NULL); sysfs_scan(); _ledmon_execute(); _ledmon_wait(conf.scan_interval); /* Invalidate each device in the list. Clear controller and host. */ list_for_each(&ledmon_block_list, device) _invalidate_dev(device); sysfs_reset(); } stop_udev_monitor(); exit(EXIT_SUCCESS); } ledmon-0.90/src/list.c000066400000000000000000000035101324101631400146200ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2018 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include "list.h" #include "utils.h" void __list_erase(struct list *list, item_free_t free_fn) { struct node *node; list_for_each_node(list, node) { if (free_fn) free_fn(node->item); free(node); } list->head = list->tail = NULL; } void __list_remove(struct node *node, item_free_t free_fn) { struct list *list = node->list; if (node->prev) node->prev->next = node->next; else list->head = node->next; if (node->next) node->next->prev = node->prev; else list->tail = node->prev; node->list = NULL; node->next = NULL; node->prev = NULL; if (free_fn) free_fn(node->item); } void list_insert(struct list *list, void *item, struct node *after) { struct node *new; struct node **x; new = malloc(sizeof(struct node)); if (!new) { log_error("Failed to allocate memory for list node."); exit(1); } new->list = list; new->item = item; if (after) { assert(list == after->list); x = &after->next; } else { x = &list->head; } if (*x == NULL) list->tail = new; else (*x)->prev = new; new->next = *x; *x = new; new->prev = after; } ledmon-0.90/src/list.h000066400000000000000000000152221324101631400146300ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2018 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #ifndef _LIST_H_INCLUDED_ #define _LIST_H_INCLUDED_ #include struct node { struct node *next, *prev; struct list *list; void *item; }; typedef void (*item_free_t)(void *); struct list { struct node *head, *tail; item_free_t item_free; }; #define __list_for_each_node(__list, __node, __start_fn, __iter_fn) \ for (struct node *__n = __start_fn(__list), *__next; \ __n && (__node = __n) && ((__next = __iter_fn(__n)) || (!__next)); \ __n = __next) #define list_for_each_node(__list, __node) \ __list_for_each_node(__list, __node, list_head, list_next) #define list_for_each_node_reverse(__list, __node) \ __list_for_each_node(__list, __node, list_tail, list_prev) #define __list_for_each(__list, __item, __start_fn, __iter_fn) \ for (struct node *__node = __start_fn(__list); \ __node && ((__item = __node->item) || (!__node->item)); \ __node = __iter_fn(__node)) #define list_for_each(__list, __item) \ __list_for_each(__list, __item, list_head, list_next) #define list_for_each_reverse(__list, __item) \ __list_for_each(__list, __item, list_tail, list_prev) /** * @brief Initializes a list object. * * Initializes a list object to reflect an empty state. * * @param[in] list pointer to a list object. * @param[in] item_free_fn custom callback for deallocating list items. * If NULL, free() will be used. */ static inline void list_init(struct list *list, item_free_t item_free_fn) { list->head = NULL; list->tail = NULL; if (item_free_fn) list->item_free = item_free_fn; else list->item_free = free; } /** * @brief Clears a list and frees the items it contains. * * This function releases the memory allocated for a list object. It also frees * the data items attached to list nodes. It does not free the list itself. * * @param[in] list pointer to a list object. */ static inline void list_erase(struct list *list) { void __list_erase(struct list *list, item_free_t free_fn); __list_erase(list, list->item_free); } /** * @brief Clears a list. * * This function removes and deallocates all nodes from the list. It does not * free the data items, to do that use list_erase(). * * @param[in] list pointer to a list object. */ static inline void list_clear(struct list *list) { void __list_erase(struct list *list, item_free_t free_fn); __list_erase(list, NULL); } /** * @brief Removes an element from the list. * * This function removes an element from the list. It only detaches the element * and does not release the memory allocated for the element. To free memory * allocated for an element use list_delete() instead. * * @param[in] node pointer to a node object. */ static inline void list_remove(struct node *node) { void __list_remove(struct node *node, item_free_t free_fn); __list_remove(node, NULL); } /** * @brief Removes an element from the list and releases its memory. * * This function removes an element from the list and frees the memory allocated * for the list node and data item. * * @param[in] node pointer to a node object. */ static inline void list_delete(struct node *node) { void __list_remove(struct node *node, item_free_t free_fn); __list_remove(node, node->list->item_free); free(node); } /** * @brief Inserts an element into the list. * * This function puts an element after a given element. * * @param[in] list pointer to list object. * @param[in] item data item to be inserted into the list. * @param[in] after list node after which to insert the element. * If NULL, then insert at the head of the list. */ void list_insert(struct list *list, void *item, struct node *after); /** * @brief Appends an element to the end of the list. * * This function puts an element on tail of a list. * * @param[in] list pointer to list object. * @param[in] item data item to be inserted into the list. */ static inline void list_append(struct list *list, void *item) { list_insert(list, item, list->tail); } /** * @brief Reruns next element. * * This function returns next element relatively to the given element. * * @param[in] node pointer to a node object. * * @return Pointer to an element if successful. The NULL pointer means * that node is the last element on the list. */ static inline struct node *list_next(const struct node *node) { return node->next; } /** * @brief Returns previous element. * * This function returns previous element relatively to the given element. * * @param[in] node pointer to a node object. * * @return Pointer to an element if successful. The NULL pointer means * that node is the first element on the list. */ static inline struct node *list_prev(const struct node *node) { return node->prev; } /** * @brief Returns head of a list. * * This function returns a head of a list. * * @param[in] list pointer to a list object. * * @return Pointer to an element if successful. The NULL pointer means that * there's no element on a list. */ static inline struct node *list_head(const struct list *list) { return list->head; } /** * @brief Returns tail of a list. * * This function returns a tail of a list. * * @param[in] list pointer to a list object. * * @return Pointer to an element if successful. The NULL pointer means that * there's no element on a list. */ static inline struct node *list_tail(const struct list *list) { return list->tail; } /** * @brief Checks if a list is empty. * * This function checks if a list object has elements. * * @param[in] list pointer to a list object. * * @return 1 if list is empty, otherwise the function returns 0. */ static inline int list_is_empty(const struct list *list) { return (list->head == NULL); } #endif /* _LIST_H_INCLUDED_ */ ledmon-0.90/src/pci_slot.c000066400000000000000000000032371324101631400154670ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2016-2018 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #include #if _HAVE_DMALLOC_H #include #endif #include "config.h" #include "pci_slot.h" #include "utils.h" /* * Allocates memory for PCI hotplug slot structure and initializes fields of * the structure. */ struct pci_slot *pci_slot_init(const char *path) { struct pci_slot *result = NULL; result = malloc(sizeof(struct pci_slot)); if (result == NULL) return NULL; result->sysfs_path = str_dup(path); result->address = get_text(path, "address"); result->attention = get_int(path, -1, "attention"); if (result->attention == -1) { pci_slot_fini(result); return NULL; } return result; } /* * The function returns memory allocated for fields of hotplug slot structure * to the system. */ void pci_slot_fini(struct pci_slot *slot) { if (slot) { free(slot->sysfs_path); free(slot->address); free(slot); } } ledmon-0.90/src/pci_slot.h000066400000000000000000000042211324101631400154660ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2016-2017 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #ifndef PCI_SLOT_H_INCLUDED_ #define PCI_SLOT_H_INCLUDED_ /** * @brief PCI hotplug slot structure. * * This structure describes a PCI hotplug slot exposed by the hotplug driver. */ struct pci_slot { /** * Path to PCI hotplug slot in sysfs tree. */ char *sysfs_path; /** * PCI hotplug slot address. */ char *address; /** * State of the Amber LED of the PCI slot. */ int attention; }; /** * @brief Allocates memory for a PCI hotplug slot structure. * * This function allocates memory for a new structure describing hotplug slot. * It reads the sysfs entries and populates structure fields. * * @param[in] path Path to a PCI hotplug slot in sysfs tree. * The path begins with "/sys/bus/pci/slots/". * * @return Pointer to PCI hotplug slot structure if successful, otherwise the * function returns NULL pointer. The NULL pointer means either the * specified path is invalid or there's not enough memory in the system * to allocated new structure. */ struct pci_slot *pci_slot_init(const char *path); /** * @brief Releases an PCI hotplug slot structure. * * This function releases memory allocated for PCI hotplug slotstructure. * * @param[in] slot Pointer to PCI hotplug slot structure. * * @return The function does not return a value. */ void pci_slot_fini(struct pci_slot *slot); #endif // PCI_SLOT_H_INCLUDED_ ledmon-0.90/src/pidfile.c000066400000000000000000000046501324101631400152670ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2017 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #include #include #include #include #include #if _HAVE_DMALLOC_H #include #endif #include "config.h" #include "pidfile.h" #include "status.h" #include "utils.h" /** */ status_t pidfile_create(const char *name) { char buf[PATH_MAX]; int fd, count; str_cpy(buf, "/var/run/", PATH_MAX); str_cat(buf, name, PATH_MAX); str_cat(buf, ".pid", PATH_MAX); fd = open(buf, O_WRONLY | O_CREAT, 0640); if (fd < 0) return STATUS_FILE_OPEN_ERROR; if (lockf(fd, F_TLOCK, 0) < 0) { close(fd); return STATUS_FILE_LOCK_ERROR; } sprintf(buf, "%d\n", getpid()); count = write(fd, buf, strlen(buf)); close(fd); return (count < 0) ? STATUS_FILE_WRITE_ERROR : STATUS_SUCCESS; } /** */ int pidfile_remove(const char *name) { char buf[PATH_MAX]; str_cpy(buf, "/var/run/", PATH_MAX); str_cat(buf, name, PATH_MAX); str_cat(buf, ".pid", PATH_MAX); return unlink(buf); } /** * @brief Test whether process with given pid is still alive * * @return STATUS_SUCCESS if proces is alive and other error if not or * if there is an error */ int ping_proc(pid_t pid) { char buf[PATH_MAX]; sprintf(buf, "kill -n 10 %d 2>/dev/null", pid); if (system(buf) == 0) return STATUS_SUCCESS; return STATUS_INVALID_PATH; } /** */ status_t pidfile_check(const char *name, pid_t *pid) { char path[PATH_MAX], *p; pid_t tp; str_cpy(path, "/var/run/", PATH_MAX); str_cat(path, name, PATH_MAX); str_cat(path, ".pid", PATH_MAX); p = buf_read(path); if (p == NULL) return STATUS_INVALID_PATH; tp = atoi(p); if (pid) *pid = tp; free(p); return ping_proc(tp); } ledmon-0.90/src/pidfile.h000066400000000000000000000020411324101631400152640ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2017 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #ifndef _PIDFILE_H_INCLUDED_ #define _PIDFILE_H_INCLUDED_ #include #include "status.h" /** */ status_t pidfile_create(const char *name); /** */ int pidfile_remove(const char *name); /** */ status_t pidfile_check(const char *name, pid_t *pid); #endif /* _PIDFILE_H_INCLUDED_ */ ledmon-0.90/src/raid.c000066400000000000000000000115731324101631400145740ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2018 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #if _HAVE_DMALLOC_H #include #endif #include "block.h" #include "config.h" #include "ibpi.h" #include "list.h" #include "raid.h" #include "slave.h" #include "status.h" #include "sysfs.h" #include "utils.h" /** */ static enum raid_state _get_array_state(const char *path) { enum raid_state state = RAID_STATE_UNKNOWN; char *p = get_text(path, "md/array_state"); if (p) { if (strcmp(p, "clear") == 0) state = RAID_STATE_CLEAR; else if (strcmp(p, "inactive") == 0) state = RAID_STATE_INACTIVE; else if (strcmp(p, "suspended") == 0) state = RAID_STATE_SUSPENDED; else if (strcmp(p, "readonly") == 0) state = RAID_STATE_READONLY; else if (strcmp(p, "read-auto") == 0) state = RAID_STATE_READ_AUTO; else if (strcmp(p, "clean") == 0) state = RAID_STATE_CLEAN; else if (strcmp(p, "active") == 0) state = RAID_STATE_ACTIVE; else if (strcmp(p, "write-pending") == 0) state = RAID_STATE_WRITE_PENDING; else if (strcmp(p, "active-idle") == 0) state = RAID_STATE_ACTIVE_IDLE; free(p); } return state; } /** */ static enum raid_action _get_sync_action(const char *path) { enum raid_action action = RAID_ACTION_UNKNOWN; char *p = get_text(path, "md/sync_action"); if (p) { if (strcmp(p, "idle") == 0) action = RAID_ACTION_IDLE; else if (strcmp(p, "reshape") == 0) action = RAID_ACTION_RESHAPE; else if (strcmp(p, "frozen") == 0) action = RAID_ACTION_FROZEN; else if (strcmp(p, "resync") == 0) action = RAID_ACTION_RESYNC; else if (strcmp(p, "check") == 0) action = RAID_ACTION_CHECK; else if (strcmp(p, "recover") == 0) action = RAID_ACTION_RECOVER; else if (strcmp(p, "repair") == 0) action = RAID_ACTION_REPAIR; free(p); } return action; } /** */ static enum raid_level _get_level(const char *path) { enum raid_level result = RAID_LEVEL_UNKNOWN; char *p = get_text(path, "md/level"); if (p) { if (strcmp(p, "raid0") == 0) result = RAID_LEVEL_0; else if (strcmp(p, "raid1") == 0) result = RAID_LEVEL_1; else if (strcmp(p, "raid10") == 0) result = RAID_LEVEL_10; else if (strcmp(p, "raid4") == 0) result = RAID_LEVEL_4; else if (strcmp(p, "raid5") == 0) result = RAID_LEVEL_5; else if (strcmp(p, "raid6") == 0) result = RAID_LEVEL_6; else if (strcmp(p, "linear") == 0) result = RAID_LEVEL_LINEAR; else if (strcmp(p, "faulty") == 0) result = RAID_LEVEL_FAULTY; free(p); } return result; } /** */ struct raid_device *raid_device_init(const char *path, unsigned int device_num, enum device_type type) { struct raid_device *device = NULL; enum raid_state state; const char *debug_dev; state = _get_array_state(path); if (state > RAID_STATE_CLEAR) { device = malloc(sizeof(struct raid_device)); if (device) { device->sysfs_path = strdup(path); device->device_num = device_num; device->sync_action = _get_sync_action(path); device->array_state = state; device->level = _get_level(path); device->degraded = get_int(path, -1, "md/degraded"); device->raid_disks = get_int(path, 0, "md/raid_disks"); device->type = type; debug_dev = strrchr(path, '/'); debug_dev = debug_dev ? debug_dev + 1 : path; log_debug("(%s) path: %s, level=%d, state=%d, " \ "degraded=%d, disks=%d, type=%d", __func__, debug_dev, device->level, state, device->degraded, device->raid_disks, type); } } return device; } /** */ void raid_device_fini(struct raid_device *device) { if (device) { if (device->sysfs_path) free(device->sysfs_path); free(device); } } /** */ struct raid_device *raid_device_duplicate(struct raid_device *device) { struct raid_device *new_device = NULL; if (device) { new_device = malloc(sizeof(struct raid_device)); if (new_device) { *new_device = *device; new_device->sysfs_path = strdup(device->sysfs_path); } } return new_device; } /** */ struct raid_device *find_raid_device(const struct list *raid_list, char *raid_sysfs_path) { struct raid_device *raid = NULL; list_for_each(raid_list, raid) { if (strcmp(raid->sysfs_path, raid_sysfs_path) == 0) return raid; } return NULL; } ledmon-0.90/src/raid.h000066400000000000000000000041661324101631400146010ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2018 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #ifndef _RAID_H_INCLUDED_ #define _RAID_H_INCLUDED_ /** */ enum raid_state { RAID_STATE_UNKNOWN = 0, RAID_STATE_CLEAR, RAID_STATE_INACTIVE, RAID_STATE_SUSPENDED, RAID_STATE_READONLY, RAID_STATE_READ_AUTO, RAID_STATE_CLEAN, RAID_STATE_ACTIVE, RAID_STATE_WRITE_PENDING, RAID_STATE_ACTIVE_IDLE }; /** */ enum raid_level { RAID_LEVEL_UNKNOWN = 0, RAID_LEVEL_0, RAID_LEVEL_1, RAID_LEVEL_10, RAID_LEVEL_4, RAID_LEVEL_5, RAID_LEVEL_6, RAID_LEVEL_FAULTY, RAID_LEVEL_LINEAR }; /** */ enum device_type { DEVICE_TYPE_UNKNOWN = 0, DEVICE_TYPE_VOLUME, DEVICE_TYPE_CONTAINER }; /** */ enum raid_action { RAID_ACTION_UNKNOWN = 0, RAID_ACTION_IDLE, RAID_ACTION_RESHAPE, RAID_ACTION_FROZEN, RAID_ACTION_RESYNC, RAID_ACTION_CHECK, RAID_ACTION_RECOVER, RAID_ACTION_REPAIR }; /** */ struct raid_device { enum device_type type; int device_num; char *sysfs_path; int raid_disks; int degraded; enum raid_state array_state; enum raid_action sync_action; enum raid_level level; }; /** */ struct raid_device *raid_device_init(const char *path, unsigned int device_num, enum device_type type); /** */ void raid_device_fini(struct raid_device *device); /** */ struct raid_device *raid_device_duplicate(struct raid_device *device); /** */ struct raid_device *find_raid_device(const struct list *raid_list, char *raid_sysfs_path); #endif /* _RAID_H_INCLUDED_ */ ledmon-0.90/src/scsi.c000066400000000000000000000404671324101631400146220ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2018 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #include #include #include #include #include #include #if _HAVE_DMALLOC_H #include #endif #include #include #include "cntrl.h" #include "config.h" #include "enclosure.h" #include "list.h" #include "scsi.h" #include "ses.h" #include "status.h" #include "sysfs.h" #include "utils.h" static int debug = 0; static int get_ses_page(int fd, struct ses_page *p, int pg_code) { int ret; int retry_count = 3; do { ret = sg_ll_receive_diag(fd, 1, pg_code, p->buf, sizeof(p->buf), 0, debug); } while (ret && retry_count--); if (!ret) p->len = (p->buf[2] << 8) + p->buf[3] + 4; return ret; } static int process_page1(struct ses_pages *sp) { int num_encl; /* number of subenclosures */ unsigned char *ed; /* Enclosure Descriptor */ int len = 0; int sum_headers = 0; /* Number of Type descriptor headers */ int i = 0; /* How many enclosures is in the main enclosure? */ num_encl = sp->page1->buf[1] + 1; /* Go to Enclosure Descriptor */ ed = sp->page1->buf + 8; for (i = 0; i < num_encl; i++, ed += len) { if (ed + 3 > sp->page1->buf + sp->page1->len) { log_debug ("SES: Error, response pare 1 truncated at %d\n", i); return 1; } sum_headers += ed[2]; len = ed[3] + 4; if (len < 40) { log_debug("SES: Response too short for page 1\n"); continue; } } sp->page1_types = (struct type_descriptor_header *)ed; sp->page1_types_len = sum_headers; /* ed is on type descr header */ for (i = 0; i < sum_headers; i++, ed += 4) { if (ed > sp->page1->buf + sp->page1->len) { log_debug("SES: Response page 1 truncated at %d\n", i); return 1; } } return 0; } static struct ses_pages *ses_init(void) { struct ses_pages *sp; sp = calloc(1, sizeof(*sp)); if (!sp) return NULL; sp->page1 = calloc(1, sizeof(struct ses_page)); if (!sp->page1) goto sp1; sp->page2 = calloc(1, sizeof(struct ses_page)); if (!sp->page2) goto sp2; return sp; sp2: free(sp->page1); sp1: free(sp); return NULL; } static void ses_free(struct ses_pages *sp) { if (!sp) return; free(sp->page1); free(sp->page2); free(sp->page10); free(sp); } static void dump_p10(unsigned char *p) { int i; printf("----------------------------------------------\n"); for (i = 0; i < 8; i++, p += 16) { printf("%p: %02x %02x %02x %02x %02x %02x %02x " \ "%02x %02x %02x %02x %02x %02x %02x %02x %02x\n", p, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); } } static int enclosure_open(const struct enclosure_device *enclosure) { int fd = -1; if (enclosure->dev_path) fd = open(enclosure->dev_path, O_RDWR); return fd; } static int enclosure_load_pages(struct enclosure_device *enclosure) { int ret; int fd; struct ses_pages *sp; if (enclosure->ses_pages) return 0; fd = enclosure_open(enclosure); if (fd == -1) return 1; sp = ses_init(); if (!sp) { ret = 1; goto end; } /* Read configuration. */ ret = get_ses_page(fd, sp->page1, ENCL_CFG_DIAG_STATUS); if (ret) goto end; ret = process_page1(sp); if (ret) goto end; /* Get Enclosure Status */ ret = get_ses_page(fd, sp->page2, ENCL_CTRL_DIAG_STATUS); end: close(fd); if (ret) ses_free(sp); else enclosure->ses_pages = sp; return ret; } static int enclosure_load_page10(struct enclosure_device *enclosure) { int ret; int fd; struct ses_page *p; if (enclosure->ses_pages && enclosure->ses_pages->page10) return 0; ret = enclosure_load_pages(enclosure); if (ret) return ret; fd = enclosure_open(enclosure); if (fd == -1) return 1; p = calloc(1, sizeof(struct ses_page)); if (!p) { ret = 1; goto end; } /* Additional Element Status */ ret = get_ses_page(fd, p, ENCL_ADDITIONAL_EL_STATUS); end: close(fd); if (ret) free(p); else enclosure->ses_pages->page10 = p; return ret; } static void enclosure_free_pages(struct enclosure_device *enclosure) { ses_free(enclosure->ses_pages); enclosure->ses_pages = NULL; } static void print_page10(struct ses_pages *sp) { unsigned char *ai = sp->page10->buf + 8; int i = 0, len = 0, eip = 0; unsigned char *sas = NULL; while (ai < sp->page10->buf + sp->page10->len) { printf("%s()[%d]: Inv: %d, EIP: %d, Proto: 0x%04x\n", __func__, i++, ((ai[0] & 0x80) >> 7), ((ai[0] & 0x10) >> 4), ai[0] & 0xf); printf("\tDescriptor len (x-1): %d\n", ai[1] + 1); eip = ai[0] && 0x10; if (eip) printf("\tElement Index: %d\n", ai[3]); len = ai[1] + 2; if ((ai[0] & 0xf) == SCSI_PROTOCOL_SAS) { if (eip) sas = ai + 4; else sas = ai + 2; printf("\tProtocol SAS:\n"); printf("\tNumber of phy descriptors: %d\n", sas[0]); printf("\tNot all phys: %d, descriptor type: 0x%1x\n", (sas[1] & 1), ((sas[1] & 0xc0) >> 6)); if (eip) { printf("\tDevice slot number: %d\n", sas[3]); sas += 2; } sas += 2; printf("\tDevice type: 0x%01x\n", ((sas[0] & 0x70) >> 4)); printf("\tSMP Initiator Port: 0x%01x\n", ((sas[2] & 2) >> 1)); printf("\tSTP Initiator Port: 0x%01x\n", ((sas[2] & 4) >> 2)); printf("\tSSP Initiator Port: 0x%01x\n", ((sas[2] & 8) >> 3)); printf("\tSATA DEVICE: 0x%01x\n", (sas[3] & 1)); printf("\tSMP Target Port: 0x%01x\n", ((sas[3] & 2) >> 1)); printf("\tSTP Target Port: 0x%01x\n", ((sas[3] & 4) >> 2)); printf("\tSSP Target Port: 0x%01x\n", ((sas[3] & 8) >> 3)); printf("\tSATA Port Selector: 0x%01x\n", ((sas[3] & 0X80) >> 7)); printf ("\tAttached SAS Address: 0x%02x%02x%02x%02x%02x%02x%02x%02x\n", sas[4], sas[5], sas[6], sas[7], sas[8], sas[9], sas[10], sas[11]); printf ("\tSAS Address: 0x%02x%02x%02x%02x%02x%02x%02x%02x\n", sas[12], sas[13], sas[14], sas[15], sas[16], sas[17], sas[18], sas[19]); printf("\tPHY Identified: 0x%01x\n", sas[20]); } else printf("\tProtocol not SAS: 0x%02x, skipping\n", (ai[0] & 0xf)); /* */ ai += len; } return; } static enum ibpi_pattern ibpi_to_ses(enum ibpi_pattern ibpi) { switch (ibpi) { case IBPI_PATTERN_UNKNOWN: case IBPI_PATTERN_ONESHOT_NORMAL: case IBPI_PATTERN_NORMAL: return SES_REQ_OK; case IBPI_PATTERN_FAILED_ARRAY: return SES_REQ_IFA; case IBPI_PATTERN_DEGRADED: return SES_REQ_ICA; case IBPI_PATTERN_REBUILD: return SES_REQ_REBUILD; case IBPI_PATTERN_FAILED_DRIVE: return SES_REQ_FAULT; case IBPI_PATTERN_LOCATE: return SES_REQ_IDENT; case IBPI_PATTERN_HOTSPARE: return SES_REQ_HOSTSPARE; case IBPI_PATTERN_PFA: return SES_REQ_PRDFAIL; default: return ibpi; } } static int ses_set_message(enum ibpi_pattern ibpi, struct ses_slot_ctrl_elem *el) { struct ses_slot_ctrl_elem msg = { 0 }; if (ibpi == IBPI_PATTERN_LOCATE_OFF) { /* * For locate_off we don't set a new state, just clear the * IDENT bit and the bits that are reserved or have different * meanings in Status and Control pages (RQST ACTIVE and * RQST MISSING). */ _clr_ident(el->b); el->b2 &= 0x4e; el->b3 &= 0x3c; return 0; } switch (ibpi_to_ses(ibpi)) { case SES_REQ_ABORT: _set_abrt(msg.b); break; case SES_REQ_REBUILD: _set_rebuild(msg.b); break; case SES_REQ_IFA: _set_ifa(msg.b); break; case SES_REQ_ICA: _set_ica(msg.b); break; case SES_REQ_CONS_CHECK: _set_cons_check(msg.b); break; case SES_REQ_HOSTSPARE: _set_hspare(msg.b); break; case SES_REQ_RSVD_DEV: _set_rsvd_dev(msg.b); break; case SES_REQ_OK: _set_ok(msg.b); break; case SES_REQ_IDENT: _set_ident(msg.b); break; case SES_REQ_RM: _set_rm(msg.b); break; case SES_REQ_INS: _set_ins(msg.b); break; case SES_REQ_MISSING: _set_miss(msg.b); break; case SES_REQ_DNR: _set_dnr(msg.b); break; case SES_REQ_ACTIVE: _set_actv(msg.b); break; case SES_REQ_EN_BB: _set_enbb(msg.b); break; case SES_REQ_EN_BA: _set_enba(msg.b); break; case SES_REQ_DEV_OFF: _set_off(msg.b); break; case SES_REQ_FAULT: _set_fault(msg.b); break; case SES_REQ_PRDFAIL: _set_prdfail(msg.b); break; default: return 1; } *el = msg; return 0; } static int ses_write_msg(enum ibpi_pattern ibpi, struct block_device *device) { struct ses_pages *sp = device->enclosure->ses_pages; int idx = device->encl_index; /* Move do descriptors */ struct ses_slot_ctrl_elem *descriptors = (void *)(sp->page2->buf + 8); int i; struct ses_slot_ctrl_elem *desc_element = NULL; element_type element_type = SES_UNSPECIFIED; for (i = 0; i < sp->page1_types_len; i++) { struct type_descriptor_header *t = &sp->page1_types[i]; descriptors++; /* At first, skip overall header. */ if (t->element_type == SES_DEVICE_SLOT || t->element_type == SES_ARRAY_DEVICE_SLOT) { if (element_type < t->element_type && t->num_of_elements > idx) { element_type = t->element_type; desc_element = &descriptors[idx]; } } else { /* * Device Slot and Array Device Slot elements are * always first on the type descriptor header list */ break; } descriptors += t->num_of_elements; } if (desc_element) { int ret = ses_set_message(ibpi, desc_element); if (ret) return ret; /* keep PRDFAIL, clear rest */ desc_element->common_control &= 0x40; /* set select */ desc_element->common_control |= 0x80; /* second byte is valid only for Array Device Slot */ if (element_type != SES_ARRAY_DEVICE_SLOT) desc_element->array_slot_control = 0; return 0; } return 1; } static int ses_send_diag(struct enclosure_device *enclosure) { int ret; int fd; fd = enclosure_open(enclosure); if (fd == -1) return 1; ret = sg_ll_send_diag(fd, 0, 1, 0, 0, 0, 0, enclosure->ses_pages->page2->buf, enclosure->ses_pages->page2->len, 0, debug); close(fd); return ret; } static char *get_drive_end_dev(const char *path) { char *s, *c, *p; c = strstr(path, "end_device"); if (!c) return NULL; s = strchr(c, '/'); if (!s) return NULL; p = calloc(s - c + 1, sizeof(*p)); if (!p) return NULL; strncpy(p, c, s - c); return p; } static uint64_t get_drive_sas_addr(const char *path) { uint64_t ret = 0; size_t size = strlen(path) * 2; char *buff, *end_dev; /* Make big buffer. */ buff = malloc(size + 1); if (!buff) return ret; end_dev = get_drive_end_dev(path); if (!end_dev) { free(buff); return ret; } snprintf(buff, size, "/sys/class/sas_end_device/%s/device/sas_device/%s", end_dev, end_dev); ret = get_uint64(buff, ret, "sas_address"); free(end_dev); free(buff); return ret; } static int get_encl_slot(struct block_device *device) { struct ses_pages *sp; unsigned char *add_desc = NULL; unsigned char *ap = NULL, *addr_p = NULL; int i, j, len = 0; uint64_t addr, addr_cmp; int idx; /* try to get slot from sysfs */ idx = get_int(device->cntrl_path, -1, "slot"); if (idx != -1) return idx; /* * Older kernels may not have the "slot" sysfs attribute, * fallback to Page10 method. */ if (enclosure_load_page10(device->enclosure)) return -1; sp = device->enclosure->ses_pages; addr = get_drive_sas_addr(device->sysfs_path); if (!addr) return -1; if (debug) print_page10(sp); /* Check Page10 for address. Extract index. */ ap = add_desc = sp->page10->buf + 8; for (i = 0; i < sp->page1_types_len; i++) { struct type_descriptor_header *t = &sp->page1_types[i]; if (t->element_type == SES_DEVICE_SLOT || t->element_type == SES_ARRAY_DEVICE_SLOT) { for (j = 0; j < t->num_of_elements; j++, ap += len) { if (debug) dump_p10(ap); /* Get Additional Element Status Descriptor */ /* length (x-1) */ len = ap[1] + 2; if ((ap[0] & 0xf) != SCSI_PROTOCOL_SAS) continue; /* need SAS PROTO */ /* It is a SAS protocol, go on */ if ((ap[0] & 0x10)) /* Check EIP */ addr_p = ap + 8; else addr_p = ap + 4; /* Process only PHY 0 descriptor. */ /* Convert be64 to le64 */ addr_cmp = ((uint64_t)addr_p[12] << 8*7) | ((uint64_t)addr_p[13] << 8*6) | ((uint64_t)addr_p[14] << 8*5) | ((uint64_t)addr_p[15] << 8*4) | ((uint64_t)addr_p[16] << 8*3) | ((uint64_t)addr_p[17] << 8*2) | ((uint64_t)addr_p[18] << 8*1) | ((uint64_t)addr_p[19]); if (addr == addr_cmp) { idx = ap[0] & 0x10 ? ap[3] : j; return idx; } } } else { /* * Device Slot and Array Device Slot elements are * always first on the type descriptor header list */ break; } } return -1; } /** */ static int _slot_match(const char *slot_path, const char *device_path) { char temp[PATH_MAX], link[PATH_MAX]; str_cpy(temp, slot_path, PATH_MAX); str_cat(temp, "/device", PATH_MAX); if (realpath(temp, link) == NULL) return 0; return strncmp(link, device_path, strlen(link)) == 0; } /** */ static char *_slot_find(const char *enclo_path, const char *device_path) { struct list dir; char *temp, *result = NULL; if (scan_dir(enclo_path, &dir) == 0) { list_for_each(&dir, temp) { if (_slot_match(temp, device_path)) { result = strdup(temp); break; } } list_erase(&dir); } return result; } int scsi_get_enclosure(struct block_device *device) { struct enclosure_device *encl; if (!device || !device->sysfs_path) return 0; list_for_each(sysfs_get_enclosure_devices(), encl) { if (_slot_match(encl->sysfs_path, device->cntrl_path)) { device->enclosure = encl; device->encl_index = get_encl_slot(device); break; } } return (device->enclosure != NULL && device->encl_index != -1); } /** */ int scsi_ses_write(struct block_device *device, enum ibpi_pattern ibpi) { int ret; if (!device || !device->sysfs_path || !device->enclosure || device->encl_index == -1) __set_errno_and_return(EINVAL); /* write only if state has changed */ if (ibpi == device->ibpi_prev) return 1; if ((ibpi < IBPI_PATTERN_NORMAL) || (ibpi > SES_REQ_FAULT)) __set_errno_and_return(ERANGE); ret = enclosure_load_pages(device->enclosure); if (ret) { log_warning ("Unable to send %s message to %s. Device is missing?", ibpi_str[ibpi], strstr(device->sysfs_path, "host")); return ret; } return ses_write_msg(ibpi, device); } int scsi_ses_flush(struct block_device *device) { int ret; if (!device || !device->enclosure) return 1; if (!device->enclosure->ses_pages) return 0; ret = ses_send_diag(device->enclosure); enclosure_free_pages(device->enclosure); return ret; } /** * @brief Gets a path to slot of sas controller. * * This function returns a sysfs path to component of enclosure the device * belongs to. * * @param[in] path Canonical sysfs path to block device. * * @return A sysfs path to controller device associated with the given * block device if successful, otherwise NULL pointer. */ static char *sas_get_slot_path(const char *path, const char *ctrl_path) { char *host; char host_path[PATH_MAX] = { 0 }; size_t ctrl_path_len = strlen(ctrl_path); if (strncmp(path, ctrl_path, ctrl_path_len) != 0) return NULL; host = get_path_hostN(path); if (host) { snprintf(host_path, sizeof(host_path), "%s/%s/bsg/sas_%s", ctrl_path, host, host); free(host); } return str_dup(host_path); } /** */ static char *_get_enc_slot_path(const char *path) { struct enclosure_device *device; char *result = NULL; list_for_each(sysfs_get_enclosure_devices(), device) { result = _slot_find(device->sysfs_path, path); if (result != NULL) break; } return result; } /** */ char *scsi_get_slot_path(const char *path, const char *ctrl_path) { char *result = NULL; result = _get_enc_slot_path(path); if (!result) result = sas_get_slot_path(path, ctrl_path); return result; } ledmon-0.90/src/scsi.h000066400000000000000000000043121324101631400146140ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2018 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #ifndef _SCSI_H_INCLUDED #define _SCSI_H_INCLUDED #include "block.h" #include "ibpi.h" /** * @brief Gets a path to slot of an enclosure. * * This function returns a sysfs path to component of enclosure the device * belongs to. * * @param[in] path Canonical sysfs path to block device. * * @return A sysfs path to enclosure's component associated with the given * block device if successful, otherwise NULL pointer. */ char *scsi_get_slot_path(const char *path, const char *cntrl_path); /** * @brief Prepares SES message based on ibpi pattern. * * @param[in] device Sysfs path of a drive in enclosure. * @param[in] ibpi IBPI pattern to visualize. * * @return 0 on success, otherwise error. */ int scsi_ses_write(struct block_device *device, enum ibpi_pattern ibpi); /** * @brief Sends message to SES processor of an enclosure. * * This function send a message to an enclosure in order to control LEDs of * the given slot/component. It uses interface of ENCLOSURE kernel module to * control LEDs. * * @param[in] device Sysfs path of a drive in enclosure. * * @return 0 on success, otherwise error. */ int scsi_ses_flush(struct block_device *device); /** * @brief Assigns enclosure device to block device. * * @param[in] device Path to block device. * * @return 1 on success, 0 otherwise. * */ int scsi_get_enclosure(struct block_device *device); #endif /* _SCSI_H_INCLUDED_ */ ledmon-0.90/src/ses.h000066400000000000000000000062111324101631400144450ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2017 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #ifndef _SES_H_INCLUDED_ #define _SES_H_INCLUDED_ #include /* Size of buffer for SES-2 Messages. */ #define SES_ALLOC_BUFF 4096 #define ENCL_CFG_DIAG_STATUS 0x01 #define ENCL_CTRL_DIAG_STATUS 0x02 #define ENCL_CTRL_DIAG_CFG 0x02 #define ENCL_EL_DESCR_STATUS 0x07 #define ENCL_ADDITIONAL_EL_STATUS 0x0a #define SCSI_PROTOCOL_SAS 6 typedef enum __attribute__((packed)) { SES_UNSPECIFIED = 0x00, SES_DEVICE_SLOT = 0x01, SES_ARRAY_DEVICE_SLOT = 0x17, } element_type; static inline void _set_prdfail(unsigned char *u) { u[0] |= (1 << 6); } static inline void _set_abrt(unsigned char *u) { u[1] |= (1 << 0); } static inline void _set_rebuild(unsigned char *u) { u[1] |= (1 << 1); } static inline void _set_ifa(unsigned char *u) { u[1] |= (1 << 2); } static inline void _set_ica(unsigned char *u) { u[1] |= (1 << 3); } static inline void _set_cons_check(unsigned char *u) { u[1] |= (1 << 4); } static inline void _set_hspare(unsigned char *u) { u[1] |= (1 << 5); } static inline void _set_rsvd_dev(unsigned char *u) { u[1] |= (1 << 6); } static inline void _set_ok(unsigned char *u) { u[1] |= (1 << 7); } static inline void _set_ident(unsigned char *u) { u[2] |= (1 << 1); } static inline void _clr_ident(unsigned char *u) { u[2] &= ~(1 << 1); } static inline void _set_rm(unsigned char *u) { u[2] |= (1 << 2); } static inline void _set_ins(unsigned char *u) { u[2] |= (1 << 3); } static inline void _set_miss(unsigned char *u) { u[2] |= (1 << 4); } static inline void _set_dnr(unsigned char *u) { u[2] |= (1 << 6); } static inline void _set_actv(unsigned char *u) { u[2] |= (1 << 7); } static inline void _set_enbb(unsigned char *u) { u[3] |= (1 << 2); } static inline void _set_enba(unsigned char *u) { u[3] |= (1 << 3); } static inline void _set_off(unsigned char *u) { u[3] |= (1 << 4); } static inline void _set_fault(unsigned char *u) { u[3] |= (1 << 5); } struct ses_page { unsigned char buf[SES_ALLOC_BUFF]; int len; }; struct type_descriptor_header { element_type element_type; __u8 num_of_elements; __u8 subenclosure_id; __u8 type_desc_text_len; }; struct ses_pages { struct ses_page *page1; struct ses_page *page2; struct ses_page *page10; struct type_descriptor_header *page1_types; int page1_types_len; }; struct ses_slot_ctrl_elem { union { struct { __u8 common_control; __u8 array_slot_control; __u8 b2; __u8 b3; }; __u8 b[4]; }; }; #endif ledmon-0.90/src/slave.c000066400000000000000000000062311324101631400147620ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2018 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #include #include #if _HAVE_DMALLOC_H #include #endif #include "config.h" #include "ibpi.h" #include "list.h" #include "slave.h" #include "status.h" #include "sysfs.h" #include "utils.h" /** */ static unsigned char _get_state(const char *path) { char *p, *t, *s; unsigned char result = SLAVE_STATE_UNKNOWN; s = p = get_text(path, "state"); if (p) { while (s) { t = strchr(s, ','); if (t) *(t++) = '\0'; if (strcmp(s, "spare") == 0) result |= SLAVE_STATE_SPARE; else if (strcmp(s, "in_sync") == 0) result |= SLAVE_STATE_IN_SYNC; else if (strcmp(s, "faulty") == 0) result |= SLAVE_STATE_FAULTY; else if (strcmp(s, "write_mostly") == 0) result |= SLAVE_STATE_WRITE_MOSTLY; else if (strcmp(s, "blocked") == 0) result |= SLAVE_STATE_BLOCKED; s = t; } free(p); } return result; } /** */ static unsigned int _get_errors(const char *path) { return get_int(path, 0, "errors"); } /** */ static unsigned int _get_slot(const char *path) { unsigned int result = -1; char *p = get_text(path, "slot"); if (p) { if (strcmp(p, "none") != 0) result = atoi(p); free(p); } return result; } /** */ static struct block_device *_get_block(const char *path, struct list *block_list) { char temp[PATH_MAX]; char link[PATH_MAX]; struct block_device *device; str_cpy(temp, path, PATH_MAX); str_cat(temp, "/block", PATH_MAX); if (!realpath(temp, link)) return NULL; /* translate partition to master block dev */ if (snprintf(temp, PATH_MAX, "%s/partition", link) > 0) { struct stat sb; char *ptr; if (stat(temp, &sb) == 0 && S_ISREG(sb.st_mode)) { ptr = strrchr(link, '/'); if (ptr) *ptr = '\0'; } } list_for_each(block_list, device) { if (strcmp(device->sysfs_path, link) == 0) return device; } return NULL; } /** */ struct slave_device *slave_device_init(const char *path, struct list *block_list) { struct slave_device *device = NULL; struct block_device *block; block = _get_block(path, block_list); if (block) { device = malloc(sizeof(struct slave_device)); if (device) { device->raid = NULL; device->state = _get_state(path); device->slot = _get_slot(path); device->errors = _get_errors(path); device->block = block; } } return device; } /** */ void slave_device_fini(struct slave_device *device) { free(device); } ledmon-0.90/src/slave.h000066400000000000000000000026261324101631400147730ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2018 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #ifndef _SLAVE_H_INCLUDED_ #define _SLAVE_H_INCLUDED_ #include "block.h" #include "raid.h" /** */ #define SLAVE_STATE_UNKNOWN 0x00 #define SLAVE_STATE_IN_SYNC 0x01 #define SLAVE_STATE_SPARE 0x02 #define SLAVE_STATE_FAULTY 0x04 #define SLAVE_STATE_WRITE_MOSTLY 0x08 #define SLAVE_STATE_BLOCKED 0x10 /** */ struct slave_device { struct raid_device *raid; unsigned int errors; unsigned int slot; struct block_device *block; unsigned char state; }; /** */ struct slave_device *slave_device_init(const char *path, struct list *block_list); /** */ void slave_device_fini(struct slave_device *device); #endif /* _SLAVE_H_INCLUDED_ */ ledmon-0.90/src/smp.c000066400000000000000000000404011324101631400144440ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2011-2018 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if _HAVE_DMALLOC_H #include #endif #include "block.h" #include "cntrl.h" #include "config.h" #include "enclosure.h" #include "ibpi.h" #include "list.h" #include "scsi.h" #include "smp.h" #include "status.h" #include "sysfs.h" #include "utils.h" #define GPIO_TX_GP1 0x01 #define INIT_IBPI(act, loc, err) \ { .error = err, \ .locate = loc, \ .activity = act \ } #define LED_OFF 0 #define LED_ON 1 #define LED_4HZ 2 #define LED_I4HZ 3 #define LED_SOF 4 #define LED_EOF 5 #define LED_2HZ 6 #define LED_I2HZ 7 static const struct gpio_rx_table { struct gpio_tx_register_byte pattern; int support_mask; } ibpi2sgpio[] = { [IBPI_PATTERN_UNKNOWN] = { INIT_IBPI(LED_SOF,LED_OFF,LED_OFF), 1 }, /* OK */ [IBPI_PATTERN_ONESHOT_NORMAL] = { INIT_IBPI(LED_SOF,LED_OFF,LED_OFF), 1 }, /* OK */ [IBPI_PATTERN_NORMAL] = { INIT_IBPI(LED_SOF,LED_OFF,LED_OFF), 1 }, /* OK */ [IBPI_PATTERN_DEGRADED] = { INIT_IBPI(LED_SOF,LED_OFF,LED_OFF), 0 }, /* NO */ [IBPI_PATTERN_REBUILD] = { INIT_IBPI(LED_SOF,LED_ON,LED_ON), 1 }, /* OK */ [IBPI_PATTERN_FAILED_ARRAY] = { INIT_IBPI(LED_SOF,LED_4HZ,LED_OFF), 0 }, /* NO */ [IBPI_PATTERN_HOTSPARE] = { INIT_IBPI(LED_SOF,LED_OFF,LED_4HZ), 0 }, /* NO */ [IBPI_PATTERN_PFA] = { INIT_IBPI(LED_SOF,LED_OFF,LED_2HZ), 0 }, /* NO */ [IBPI_PATTERN_FAILED_DRIVE] = { INIT_IBPI(LED_SOF,LED_OFF,LED_ON), 1 }, /* OK */ [IBPI_PATTERN_LOCATE] = { INIT_IBPI(LED_SOF,LED_ON,LED_OFF), 1 }, /* OK */ [IBPI_PATTERN_LOCATE_OFF] = { INIT_IBPI(LED_SOF,LED_OFF,LED_OFF), 1 } /* OK */ }; struct smp_read_response_frame_header { uint8_t frame_type; /* =0x41 */ uint8_t function; /* =0x02 for read, 0x82 for write */ uint8_t function_result; uint8_t reserved; uint32_t read_data[0]; /* variable length of data */ /* uint32_t crc; */ } __attribute__ ((__packed__)); struct smp_write_response_frame { uint8_t frame_type; /* =0x41 */ uint8_t function; /* =0x02 for read, 0x82 for write */ uint8_t function_result; uint8_t reserved; uint32_t crc; } __attribute__ ((__packed__)); struct smp_read_request_frame { uint8_t frame_type; /* =0x40 */ uint8_t function; /* =0x02 for read, 0x82 for write */ uint8_t register_type; uint8_t register_index; uint8_t register_count; uint8_t reserved[3]; uint32_t crc; } __attribute__ ((__packed__)); struct smp_write_request_frame_header { uint8_t frame_type; /* =0x40 */ uint8_t function; /* =0x02 for read, 0x82 for write */ uint8_t register_type; uint8_t register_index; uint8_t register_count; uint8_t reserved[3]; uint32_t data[0]; /* variable length of data */ /* uint32_t crc; */ } __attribute__ ((__packed__)); struct sgpio_cfg_0_frame { uint8_t reserved; uint8_t reserved1:4; uint8_t version:4; uint8_t gp_register_count:4; uint8_t cfg_register_count:3; uint8_t enable:1; uint8_t supported_drive_cnt; } __attribute__ ((__packed__)); struct sgpio_cfg_1_frame { uint8_t reserved; uint8_t blink_gen_a:4; uint8_t blink_gen_b:4; uint8_t forced_act_off:4; uint8_t max_act_on:4; uint8_t stretch_act_off:4; uint8_t stretch_act_on:4; } __attribute__ ((__packed__)); /** * to_sas_gpio_gp_bit - given the gpio frame data find the byte/bit position of 'od' * @od: od bit to find * @data: incoming bitstream (from frame) * @index: requested data register index (from frame) * @count: total number of registers in the bitstream (from frame) * @bit: bit position of 'od' in the returned byte * * returns NULL if 'od' is not in 'data' * * From SFF-8485 v0.7: * "In GPIO_TX[1], bit 0 of byte 3 contains the first bit (i.e., OD0.0) * and bit 7 of byte 0 contains the 32nd bit (i.e., OD10.1). * * In GPIO_TX[2], bit 0 of byte 3 contains the 33rd bit (i.e., OD10.2) * and bit 7 of byte 0 contains the 64th bit (i.e., OD21.0)." * * The general-purpose (raw-bitstream) RX registers have the same layout * although 'od' is renamed 'id' for 'input data'. * * SFF-8489 defines the behavior of the LEDs in response to the 'od' values. */ static unsigned char *to_sas_gpio_gp_bit(unsigned int od, unsigned char *data, unsigned char index, unsigned char count, unsigned char *bit) { unsigned int reg; unsigned char byte; /* gp registers start at index 1 */ if (index == 0) return NULL; index--; /* make index 0-based */ if (od < index * 32) return NULL; od -= index * 32; reg = od >> 5; if (reg >= count) return NULL; od &= (1 << 5) - 1; byte = 3 - (od >> 3); *bit = od & ((1 << 3) - 1); return &data[reg * 4 + byte]; } int try_test_sas_gpio_gp_bit(unsigned int od, unsigned char *data, unsigned char index, unsigned char count) { unsigned char *byte; unsigned char bit; byte = to_sas_gpio_gp_bit(od, data, index, count, &bit); if (!byte) return -1; return (*byte >> bit) & 1; } int try_set_sas_gpio_gp_bit(unsigned int od, unsigned char *data, unsigned char index, unsigned char count) { unsigned char *byte; unsigned char bit; byte = to_sas_gpio_gp_bit(od, data, index, count, &bit); if (!byte) return 0; *byte |= 1 << bit; return 1; } int try_clear_sas_gpio_gp_bit(unsigned int od, unsigned char *data, unsigned char index, unsigned char count) { unsigned char *byte; unsigned char bit; byte = to_sas_gpio_gp_bit(od, data, index, count, &bit); if (!byte) return 0; *byte &= ~(1 << bit); return 1; } /** * set_raw_pattern - turn a tx register into a tx_gp bitstream * * takes @dev_idx (phy index) and a @pattern (error, locate, activity) * tuple and modifies the bitstream in @data accordingly */ int set_raw_pattern(unsigned int dev_idx, unsigned char *data, const struct gpio_tx_register_byte *pattern) { int od_offset = dev_idx * 3; int rc = 0; if (pattern->activity == LED_ON) rc += try_set_sas_gpio_gp_bit(od_offset + 0, data, GPIO_TX_GP1, 1); else rc += try_clear_sas_gpio_gp_bit(od_offset + 0, data, GPIO_TX_GP1, 1); if (pattern->locate == LED_ON) rc += try_set_sas_gpio_gp_bit(od_offset + 1, data, GPIO_TX_GP1, 1); else rc += try_clear_sas_gpio_gp_bit(od_offset + 1, data, GPIO_TX_GP1, 1); if (pattern->error == LED_ON) rc += try_set_sas_gpio_gp_bit(od_offset + 2, data, GPIO_TX_GP1, 1); else rc += try_clear_sas_gpio_gp_bit(od_offset + 2, data, GPIO_TX_GP1, 1); return rc == 3; } /** * @brief open device for smp protocol */ static int _open_smp_device(const char *filename) { char buf[PATH_MAX]; FILE *df; int hba_fd; int dmaj, dmin; snprintf(buf, sizeof(buf), "%s/dev", filename); df = fopen(buf, "r"); if (!df) return -1; if (fgets(buf, sizeof(buf), df) < 0) { fclose(df); return -1; } if (sscanf(buf, "%d:%d", &dmaj, &dmin) != 2) { fclose(df); return -1; } fclose(df); snprintf(buf, sizeof(buf), "/var/tmp/led.%d.%d.%d", dmaj, dmin, getpid()); if (mknod(buf, S_IFCHR | S_IRUSR | S_IWUSR, makedev(dmaj, dmin)) < 0) return -1; hba_fd = open(buf, O_RDWR); unlink(buf); if (hba_fd < 0) return -1; return hba_fd; } /** * @brief close smp device */ static int _close_smp_device(int fd) { return close(fd); } /** @brief use sg protocol in order to send data directly to hba driver */ static int _send_smp_frame(int hba, void *data, size_t data_size, void *response, size_t *response_size) { struct sg_io_v4 sg_frame; uint8_t request_buf[SCSI_MAX_CDB_LENGTH]; int response_status = 0; /* wrap the frame into sg structure */ memset(&sg_frame, 0, sizeof(sg_frame)); sg_frame.guard = 'Q'; sg_frame.protocol = BSG_PROTOCOL_SCSI; sg_frame.subprotocol = BSG_SUB_PROTOCOL_SCSI_TRANSPORT; sg_frame.request_len = sizeof(request_buf); sg_frame.request = (uintptr_t) request_buf; sg_frame.dout_xfer_len = data_size; sg_frame.dout_xferp = (uintptr_t) data; sg_frame.din_xfer_len = *response_size; sg_frame.din_xferp = (uintptr_t) response; sg_frame.timeout = SG_RESPONSE_TIMEOUT; /* send ioctl */ if (ioctl(hba, SG_IO, &sg_frame) < 0) return -1; /* return status */ if (sg_frame.driver_status) response_status = sg_frame.driver_status; else if (sg_frame.transport_status) response_status = sg_frame.transport_status; else if (sg_frame.device_status) response_status = sg_frame.device_status; *response_size = sg_frame.din_xfer_len - sg_frame.din_resid; return response_status; } /* 1024 bytes for data, 4 for crc */ #define MAX_SMP_FRAME_DATA 1024 #define MAX_SMP_FRAME_LEN (sizeof(struct smp_write_request_frame_header) + \ MAX_SMP_FRAME_DATA + SMP_FRAME_CRC_LEN) /** @brief prepare full smp frame ready to send to hba @note len is a number of 32bit words */ static int _start_smp_write_gpio(int hba, struct smp_write_request_frame_header *header, void *data, size_t len) { uint8_t buf[MAX_SMP_FRAME_LEN]; struct smp_write_response_frame response; size_t response_size = sizeof(response); int status; memset(&response, 0, sizeof(response)); /* create full frame */ if (len * SMP_DATA_CHUNK_SIZE > MAX_SMP_FRAME_DATA) __set_errno_and_return(EINVAL); memset(buf, 0, sizeof(buf)); memcpy(buf, header, sizeof(*header)); memcpy(buf + sizeof(*header), data, len * SMP_DATA_CHUNK_SIZE); status = _send_smp_frame(hba, buf, sizeof(*header) + len * SMP_DATA_CHUNK_SIZE + SMP_FRAME_CRC_LEN, &response, &response_size); /* if frame is somehow malformed return failure */ if (status != GPIO_STATUS_OK || response.frame_type != SMP_FRAME_TYPE_RESP || response.function != header->function) { return GPIO_STATUS_FAILURE; } return response.function_result; } /** @brief prepare smp frame header */ int smp_write_gpio(const char *path, int smp_reg_type, int smp_reg_index, int smp_reg_count, void *data, size_t len) { struct smp_write_request_frame_header header; int status; header.frame_type = SMP_FRAME_TYPE_REQ; header.function = SMP_FUNC_GPIO_WRITE; header.register_type = smp_reg_type; header.register_index = smp_reg_index; header.register_count = smp_reg_count; memset(header.reserved, 0, sizeof(header.reserved)); int fd = _open_smp_device(path); status = _start_smp_write_gpio(fd, &header, data, len); _close_smp_device(fd); return status; } #define BLINK_GEN_1HZ 8 #define BLINK_GEN_2HZ 4 #define BLINK_GEN_4HZ 2 #define DEFAULT_FORCED_ACTIVITY_OFF 1 #define DEFAULT_MAXIMUM_ACTIVITY_ON 2 #define DEFAULT_STRETCH_ACTIVITY_OFF 0 #define DEFAULT_STRETCH_ACTIVITY_ON 0 /* one data chunk is 32bit long */ #define SMP_DATA_CHUNKS 1 struct gpio_tx_register_byte *get_bdev_ibpi_buffer(struct block_device *bdevice) { if (bdevice && bdevice->host) return bdevice->host->ibpi_state_buffer; return NULL; } /** */ int scsi_smp_fill_buffer(struct block_device *device, enum ibpi_pattern ibpi) { const char *sysfs_path = device->cntrl_path; struct gpio_tx_register_byte *gpio_tx; if (sysfs_path == NULL) __set_errno_and_return(EINVAL); if ((ibpi < IBPI_PATTERN_NORMAL) || (ibpi > IBPI_PATTERN_LOCATE_OFF)) __set_errno_and_return(ERANGE); if (!device->cntrl) { log_debug("No ctrl dev for '%s'", strstr(sysfs_path, "host")); __set_errno_and_return(ENODEV); } if (device->cntrl->cntrl_type != CNTRL_TYPE_SCSI) { log_debug("No SCSI ctrl dev '%s'", strstr(sysfs_path, "host")); __set_errno_and_return(EINVAL); } if (!device->host) { log_debug("No host for '%s'", strstr(sysfs_path, "host")); __set_errno_and_return(ENODEV); } if (device->cntrl->isci_present && !ibpi2sgpio[ibpi].support_mask) { char *c = strrchr(device->sysfs_path, '/'); if (c++) { log_debug ("pattern %s not supported for device (/dev/%s)", ibpi_str[ibpi], c); fprintf(stderr, "%s(): pattern %s not supported for device (/dev/%s)\n", __func__, ibpi_str[ibpi], c); } else { log_debug("pattern %s not supported for device %s", ibpi_str[ibpi], device->sysfs_path); fprintf(stderr, "%s(): pattern %s not supported for device\n\t(%s)\n", __func__, ibpi_str[ibpi], device->sysfs_path); } __set_errno_and_return(ENOTSUP); } gpio_tx = get_bdev_ibpi_buffer(device); if (!gpio_tx) { log_debug("%s(): no IBPI buffer. Skipping.", __func__); __set_errno_and_return(ENODEV); } if (device->cntrl->isci_present) { /* update bit stream for this device */ set_raw_pattern(device->phy_index, &device->host->bitstream[0], &ibpi2sgpio[ibpi].pattern); } else { /* * GPIO_TX[n] register has the highest numbered drive of the * four in the first byte and the lowest numbered drive in the * fourth byte. See SFF-8485 Rev. 0.7 Table 24. */ gpio_tx[device->phy_index + 3 - (device->phy_index % 4) * 2] = ibpi2sgpio[ibpi].pattern; } /* write only if state has changed */ if (ibpi != device->ibpi_prev) device->host->flush = 1; return 1; } int scsi_smp_write_buffer(struct block_device *device) { const char *sysfs_path = device->cntrl_path; if (sysfs_path == NULL) __set_errno_and_return(EINVAL); if (!device->host) __set_errno_and_return(ENODEV); if (device->host->flush) { device->host->flush = 0; /* re-transmit the bitstream */ if (device->cntrl->isci_present) { return smp_write_gpio(sysfs_path, GPIO_REG_TYPE_TX_GP, GPIO_TX_GP1, 1, &device->host->bitstream[0], SMP_DATA_CHUNKS); } else { return smp_write_gpio(sysfs_path, GPIO_REG_TYPE_TX, 0, (device->host->ports+3)/4, device->host->ibpi_state_buffer, (device->host->ports+3)/4); } } else return 1; } /** */ static void init_smp(const char *path, struct cntrl_device *device) { struct _host_type *hosts; int i; if (!device) return; for (hosts = device->hosts; hosts; hosts = hosts->next) { /* already initialized */ if (hosts->ibpi_state_buffer) continue; hosts->ibpi_state_buffer = calloc(hosts->ports, sizeof(struct gpio_tx_register_byte)); if (!hosts->ibpi_state_buffer) continue; for (i = 0; i < hosts->ports; i++) set_raw_pattern(i, &hosts->bitstream[0], &ibpi2sgpio [IBPI_PATTERN_ONESHOT_NORMAL].pattern); hosts->flush = 0; } } /** */ int cntrl_init_smp(const char *path, struct cntrl_device *cntrl) { char *path2 = NULL; char *c; int host, port = 0; struct dirent *de; DIR *d; if (!cntrl) return port; /* Other case - just init controller. */ if (path && strstr(path, "port-")) { path2 = strdup(path); if (!path2) return port; c = strstr(path2, "port-"); if (!c) { /* Should not happen. */ log_debug("%s() missing 'port' in path '%s'", __func__, path2); free(path2); return port; } c = strchr(c, '/'); if (!c) { free(path2); return port; } *c = 0; /* And now path2 has only up to 'port-...' string. */ /* this should open port-XX:X directory * FIXME: for enclosure it may be port-XX:Y:Z but it's a second * occurrence * * We may try this on the missing device. * */ d = opendir(path2); if (!d) { log_debug("%s() Error dir open '%s', path ='%s'", __func__, path2, path); free(path2); return port; } while ((de = readdir(d))) { if ((strcmp(de->d_name, ".") == 0) || (strcmp(de->d_name, "..")) == 0) { continue; } if (strncmp(de->d_name, "phy-", strlen("phy-")) == 0) { /* Need link called "phy-XX:Y * Y is real phy we need. * This can also be found * in phy_identifier file */ sscanf(de->d_name, "phy-%d:%d", &host, &port); break; } } closedir(d); free(path2); } init_smp(path, cntrl); return port; } ledmon-0.90/src/smp.h000066400000000000000000000103161324101631400144530ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2011-2017 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #include "ibpi.h" #include "block.h" #ifndef _SMP_H_INCLUDED #define _SMP_H_INCLUDED /* smp constants */ #define SMP_FRAME_TYPE_REQ 0x40 #define SMP_FRAME_TYPE_RESP 0x41 #define SMP_FUNC_GPIO_READ 0x02 #define SMP_FUNC_GPIO_WRITE 0x82 #define SMP_FRAME_CRC_LEN sizeof(uint32_t) #define SMP_DATA_CHUNK_SIZE sizeof(uint32_t) /* gpio constants */ /* gpio register types */ #define GPIO_REG_TYPE_CFG 0x00 #define GPIO_REG_TYPE_RX 0x01 #define GPIO_REG_TYPE_RX_GP 0x02 #define GPIO_REG_TYPE_TX 0x03 #define GPIO_REG_TYPE_TX_GP 0x04 /* gpio register indexes */ #define GPIO_REG_IND_CFG_0 0x00 #define GPIO_REG_IND_CFG_1 0x01 #define GPIO_REG_IND_RX_0 0x00 #define GPIO_REG_IND_RX_1 0x01 #define GPIO_REG_IND_TX_0 0x00 #define GPIO_REG_IND_TX_1 0x01 #define SG_RESPONSE_TIMEOUT (5 * 1000) /* 1000 as miliseconds multiplier */ #define SCSI_MAX_CDB_LENGTH 0x10 #define GPIO_STATUS_OK 0x00 #define GPIO_STATUS_FAILURE 0x80 struct gpio_tx_register_byte { unsigned char error:3; unsigned char locate:2; unsigned char activity:3; } __attribute__ ((__packed__)); /** * @brief Sends message to SES processor of an enclosure. * * This function send a message to an enclosure in order to control LEDs of * the given slot/component. It uses interface of ENCLOSURE kernel module to * control LEDs. * * @param[in] device Path to an enclosure device in sysfs. * @param[in] ibpi IBPI pattern to visualize. * * @return Number of characters written if successful or -1 in case of error * and errno is set to appropriate error code. */ int scsi_ses_write(struct block_device *device, enum ibpi_pattern ibpi); /** * @brief Write message to outbound raw byte stream buffer. * * @param[in] device Path to a smp device in sysfs. * @param[in] ibpi IBPI pattern to visualize. * * @return 1 if successful or -1 in case of error * and errno is set to appropriate error code. */ int scsi_smp_fill_buffer(struct block_device *device, enum ibpi_pattern ibpi); /** * @brief Sends message to SMP device. * * This function triggers gpio order to control LEDs of * the given component. * * @param[in] device Path to a smp device in sysfs. * * @return Number of bytes written to device if successful or -1 in case of error * and errno is set to appropriate error code. */ int scsi_smp_write_buffer(struct block_device *device); /** * @brief Init smp and gets phy index, * * @param[in] path Path to the device in sysfs. It can be NULL * to just initialize cntrl and not to get the * phy. * @param[in] cntrl Controller device to be initialized. * * @return Phy index on success if path and cntrl weren't NULL * 0 if error occurred or path was NULL. */ int cntrl_init_smp(const char *path, struct cntrl_device *cntrl); /** * @brief Write GPIO data * * @param[in] path Path to the device in sysfs. * phy. * * @param[in] smp_reg_type GPIO register type * * @param[in] smp_reg_index GPIO register index * * @param[in] smp_reg_count GPIO register count * * @param[in] data Data to be written * * @return written register count * <0 if error occurred */ int smp_write_gpio(const char *path, int smp_reg_type, int smp_reg_index, int smp_reg_count, void *data, size_t len); #endif /* _SCSI_H_INCLUDED_ */ ledmon-0.90/src/status.c000066400000000000000000000044261324101631400151770ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2018 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #include "config.h" #include "status.h" /** */ #define _S_CODE(__code) \ case __code: return #__code /** */ char *strstatus(status_t scode) { switch (scode) { _S_CODE(STATUS_SUCCESS); _S_CODE(STATUS_BUFFER_OVERFLOW); _S_CODE(STATUS_INVALID_NODE); _S_CODE(STATUS_OUT_OF_MEMORY); _S_CODE(STATUS_OUT_OF_RANGE); _S_CODE(STATUS_DATA_ERROR); _S_CODE(STATUS_IBPI_DETERMINE_ERROR); _S_CODE(STATUS_INVALID_PATH); _S_CODE(STATUS_INVALID_SUBOPTION); _S_CODE(STATUS_INVALID_STATE); _S_CODE(STATUS_NULL_POINTER); _S_CODE(STATUS_SIZE_ERROR); _S_CODE(STATUS_FILE_OPEN_ERROR); _S_CODE(STATUS_FILE_READ_ERROR); _S_CODE(STATUS_FILE_WRITE_ERROR); _S_CODE(STATUS_FILE_LOCK_ERROR); _S_CODE(STATUS_SYSFS_PATH_ERROR); _S_CODE(STATUS_SYSFS_INIT_ERROR); _S_CODE(STATUS_SYSFS_SCAN_ERROR); _S_CODE(STATUS_SYSFS_RESET_ERROR); _S_CODE(STATUS_DIR_OPEN_ERROR); _S_CODE(STATUS_LIST_EMPTY); _S_CODE(STATUS_LIST_INIT_ERROR); _S_CODE(STATUS_BLOCK_LIST_ERROR); _S_CODE(STATUS_VOLUM_LIST_ERROR); _S_CODE(STATUS_CNTRL_LIST_ERROR); _S_CODE(STATUS_SLAVE_LIST_ERROR); _S_CODE(STATUS_CNTNR_LIST_ERROR); _S_CODE(STATUS_INVALID_FORMAT); _S_CODE(STATUS_LEDMON_INIT); _S_CODE(STATUS_LEDMON_RUNNING); _S_CODE(STATUS_ONEXIT_ERROR); _S_CODE(STATUS_INVALID_CONTROLLER); _S_CODE(STATUS_NOT_SUPPORTED); _S_CODE(STATUS_STAT_ERROR); _S_CODE(STATUS_CMDLINE_ERROR); _S_CODE(STATUS_NOT_A_PRIVILEGED_USER); _S_CODE(STATUS_ENCLO_LIST_ERROR); _S_CODE(STATUS_SLOTS_LIST_ERROR); _S_CODE(STATUS_CONFIG_FILE_ERROR); default: return "???"; } } ledmon-0.90/src/status.h000066400000000000000000000036121324101631400152000ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2017 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #ifndef _STATUS_H_INCLUDED_ #define _STATUS_H_INCLUDED_ /** */ typedef int status_t; /** */ enum status_code { STATUS_SUCCESS = 0, STATUS_BUFFER_OVERFLOW, STATUS_NULL_POINTER, STATUS_OUT_OF_MEMORY, STATUS_OUT_OF_RANGE, STATUS_INVALID_NODE, STATUS_DATA_ERROR, STATUS_IBPI_DETERMINE_ERROR, STATUS_INVALID_PATH, STATUS_INVALID_SUBOPTION, STATUS_INVALID_STATE, STATUS_SIZE_ERROR, STATUS_FILE_OPEN_ERROR, STATUS_FILE_READ_ERROR, STATUS_FILE_WRITE_ERROR, STATUS_FILE_LOCK_ERROR, STATUS_DIR_OPEN_ERROR, STATUS_SYSFS_PATH_ERROR, STATUS_SYSFS_INIT_ERROR, STATUS_SYSFS_SCAN_ERROR, STATUS_SYSFS_RESET_ERROR, STATUS_LIST_EMPTY, STATUS_LIST_INIT_ERROR, STATUS_BLOCK_LIST_ERROR, STATUS_VOLUM_LIST_ERROR, STATUS_CNTRL_LIST_ERROR, STATUS_SLAVE_LIST_ERROR, STATUS_CNTNR_LIST_ERROR, STATUS_INVALID_FORMAT, STATUS_LEDMON_INIT, STATUS_LEDMON_RUNNING, STATUS_ONEXIT_ERROR, STATUS_INVALID_CONTROLLER, STATUS_NOT_SUPPORTED, STATUS_STAT_ERROR, STATUS_CMDLINE_ERROR, STATUS_NOT_A_PRIVILEGED_USER, STATUS_ENCLO_LIST_ERROR, STATUS_SLOTS_LIST_ERROR, STATUS_CONFIG_FILE_ERROR }; /** */ char *strstatus(status_t scode); #endif /* _STATUS_H_INCLUDED_ */ ledmon-0.90/src/sysfs.c000066400000000000000000000407011324101631400150170ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2018 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #include #include #include #if _HAVE_DMALLOC_H #include #endif #include "block.h" #include "cntrl.h" #include "config.h" #include "config_file.h" #include "enclosure.h" #include "ibpi.h" #include "list.h" #include "pci_slot.h" #include "raid.h" #include "slave.h" #include "stdio.h" #include "sysfs.h" #include "utils.h" /** */ #define SYSFS_CLASS_BLOCK "/sys/block" #define SYSFS_CLASS_ENCLOSURE "/sys/class/enclosure" #define SYSFS_PCI_DEVICES "/sys/bus/pci/devices" #define SYSFS_PCI_SLOTS "/sys/bus/pci/slots" /** * This is internal variable global to sysfs module only. It is a list of * block devices registered in the system. Use sysfs_init() * function to initialize the variable. Use sysfs_scan() function to populate * the list. Use sysfs_reset() function to delete the content of the list. */ static struct list sysfs_block_list; /** * This is internal variable global to sysfs module only. It is a list of * RAID volumes registered in the system. Use sysfs_init() * function to initialize the variable. Use sysfs_scan() function to populate * the list. Use sysfs_reset() function to delete the content of the list. */ static struct list volum_list; /** * This is internal variable global to sysfs module only. It is a list of * storage controller devices registered in the system and * supported by Intel(R) Enclosure LEDs Control Utility. Use sysfs_init() * function to initialize the variable. Use sysfs_scan() function to populate * the list. Use sysfs_reset() function to delete the content of the list. */ static struct list cntrl_list; /** * This is internal variable global to sysfs module only. It is a list of * slave devices registered in the system. Use sysfs_init() * function to initialize the variable. Use sysfs_scan() function to populate * the list. Use sysfs_reset() function to delete the content of the list. */ static struct list slave_list; /** * This is internal variable global to sysfs module only. It is a list of * RAID containers registered in the system. Use sysfs_init() * function to initialize the variable. Use sysfs_scan() function to populate * the list. Use sysfs_reset() function to delete the content of the list. */ static struct list cntnr_list; /** * This is internal variable global to sysfs module only. It is a to list of * enclosures registered in the system. */ static struct list enclo_list; /** * This is internal variable global to sysfs module only. It is a list of * PCI slots registered in the system. Use sysfs_init() * function to initialize the variable. Use sysfs_scan() function to populate * the list. Use sysfs_reset() function to delete the content of the list. */ static struct list slots_list; /** * @brief Determine device type. * * This is internal function of sysfs module. The function determines a type of * RAID device either it is VOLUME or CONTAINER device. The information required * if read from 'metadata_version' attribute from sysfs. External and native * RAID devices are reported as volumes with no distinction between both types. * * @param[in] path Path to RAID device in sysfs tree. * * @return Type of RAID device if successful, otherwise DEVICE_TYPE_UNKNOWN. */ static enum device_type _get_device_type(const char *path) { enum device_type result = DEVICE_TYPE_UNKNOWN; char *p = get_text(path, "md/metadata_version"); if (p != NULL) { if (strlen(p) > 0) { if (strncmp(p, "external:", 9) == 0) { if (p[9] == '/') result = DEVICE_TYPE_VOLUME; else result = DEVICE_TYPE_CONTAINER; } else { result = DEVICE_TYPE_VOLUME; } } free(p); } return result; } /** * @brief Gets device major and minor. * * This is internal function of sysfs module. The function retrieves major and * minor of device from sysfs attribute. Each block device has 'dev' attribute * where major and minor separated by colon are stored. * * @param[in] path Path to block device in sysfs tree. * @param[in] d_id Placeholder where major and minor of device * will be stored. If this argument is NULL the * behavior of function is unspecified. * * @return The function does not return a value. */ static void _get_id(const char *path, struct device_id *d_id) { char temp[PATH_MAX]; str_cpy(temp, path, PATH_MAX); str_cat(temp, "/dev", PATH_MAX); get_id(temp, d_id); } /** * @brief Adds slave device to RAID volume. * * This is internal function of sysfs module. The function puts slave device on * list of slave devices of RAID volume. The memory is allocated and structure * fields populated. RAID device is link to slave device. * * @param[in] path Path to 'md' directory of RAID device in sysfs * tree. * @param[in] raid Pointer to RAID device structure corresponding * to 'path' argument. * * @return The function does not return a value. */ static void _slave_vol_add(const char *path, struct raid_device *raid) { struct slave_device *device; char *t = rindex(path, '/'); if (strncmp(t + 1, "dev-", 4) == 0) { device = slave_device_init(path, &sysfs_block_list); if (device) { device->raid = raid; list_append(&slave_list, device); } } } /** * @brief Checks for duplicate entries on list of slave devices. * * This is internal function of sysfs module. The functions checks if the given * slave device is already on list with slave devices. This function is used by * _slave_cnt_add() function to avoid duplicate entries. * * @param[in] slave Pointer to slave device structure to check. * * @return 1 the given device is on the list, otherwise the function returns 0. */ static int _is_duplicate(struct slave_device *slave) { struct slave_device *device; list_for_each(&slave_list, device) { if (device->block == slave->block) return 1; } return 0; } /** * @brief Checks if given disk can be removed from sysfs_block_list if * metatada is not present. * * This is internal function (action) of sysfs module. The slave_list keeps * all devices with metadata (raid devices). If disk is not included in slave * list there is not metadata on it. * * @return 1 if can be removed, otherwise 0. */ static int _is_non_raid_device(struct block_device *block_device) { struct slave_device *slave_device; list_for_each(&slave_list, slave_device) { if (strcmp(slave_device->block->sysfs_path, block_device->sysfs_path) == 0) return 0; } return 1; } /** */ static void _slave_cnt_add(const char *path, struct raid_device *raid) { struct slave_device *device; char *t = rindex(path, '/'); if (strncmp(t + 1, "dev-", 4) == 0) { device = slave_device_init(path, &sysfs_block_list); if (device) { if (!_is_duplicate(device)) { device->raid = raid; list_append(&slave_list, device); } else { slave_device_fini(device); } } } } static void _link_raid_device(struct raid_device *device, enum device_type type) { char temp[PATH_MAX]; struct list dir; str_cpy(temp, device->sysfs_path, PATH_MAX); str_cat(temp, "/md", PATH_MAX - 1); if (scan_dir(temp, &dir) == 0) { const char *dir_path; list_for_each(&dir, dir_path) { if (type == DEVICE_TYPE_VOLUME) _slave_vol_add(dir_path, device); else if (type == DEVICE_TYPE_CONTAINER) _slave_cnt_add(dir_path, device); } list_erase(&dir); } } /** */ static void _block_add(const char *path) { struct block_device *device = block_device_init(&cntrl_list, path); if (device) list_append(&sysfs_block_list, device); } /** */ static void _volum_add(const char *path, unsigned int device_num) { struct raid_device *device = raid_device_init(path, device_num, DEVICE_TYPE_VOLUME); if (device) list_append(&volum_list, device); } /** */ static void _cntnr_add(const char *path, unsigned int device_num) { struct raid_device *device = raid_device_init(path, device_num, DEVICE_TYPE_CONTAINER); if (device) list_append(&cntnr_list, device); } /** */ static void _raid_add(const char *path) { struct device_id device_id; _get_id(path, &device_id); if (device_id.major == 9) { switch (_get_device_type(path)) { case DEVICE_TYPE_VOLUME: _volum_add(path, device_id.minor); break; case DEVICE_TYPE_CONTAINER: _cntnr_add(path, device_id.minor); break; case DEVICE_TYPE_UNKNOWN: break; } } } /** */ static void _cntrl_add(const char *path) { struct cntrl_device *device = cntrl_device_init(path); if (device) list_append(&cntrl_list, device); } /** */ static void _enclo_add(const char *path) { struct enclosure_device *device = enclosure_device_init(path); if (device) list_append(&enclo_list, device); } /** */ static void _slots_add(const char *path) { struct pci_slot *device = pci_slot_init(path); if (device) list_append(&slots_list, device); } /** */ static void _check_raid(const char *path) { char *t = strrchr(path, '/'); if (strncmp(t + 1, "md", 2) == 0) _raid_add(path); } /** */ static void _check_cntrl(const char *path) { char link[PATH_MAX]; if (realpath(path, link) != NULL) _cntrl_add(link); } /** */ static void _check_enclo(const char *path) { char link[PATH_MAX]; if (realpath(path, link) != NULL) _enclo_add(link); } static void _scan_block(void) { struct list dir; if (scan_dir(SYSFS_CLASS_BLOCK, &dir) == 0) { const char *dir_path; list_for_each(&dir, dir_path) _block_add(dir_path); list_erase(&dir); } } static void _scan_raid(void) { struct list dir; if (scan_dir(SYSFS_CLASS_BLOCK, &dir) == 0) { const char *dir_path; list_for_each(&dir, dir_path) _check_raid(dir_path); list_erase(&dir); } } static void _scan_cntrl(void) { struct list dir; if (scan_dir(SYSFS_PCI_DEVICES, &dir) == 0) { const char *dir_path; list_for_each(&dir, dir_path) _check_cntrl(dir_path); list_erase(&dir); } } static void _scan_slave(void) { struct raid_device *device; list_for_each(&volum_list, device) _link_raid_device(device, DEVICE_TYPE_VOLUME); list_for_each(&cntnr_list, device) _link_raid_device(device, DEVICE_TYPE_CONTAINER); if (conf.raid_memebers_only) { struct node *node; list_for_each_node(&sysfs_block_list, node) { if (_is_non_raid_device(node->item)) list_delete(node); } } } static void _scan_enclo(void) { struct list dir; if (scan_dir(SYSFS_CLASS_ENCLOSURE, &dir) == 0) { const char *dir_path; list_for_each(&dir, dir_path) _check_enclo(dir_path); list_erase(&dir); } } static void _scan_slots(void) { struct list dir; if (scan_dir(SYSFS_PCI_SLOTS, &dir) == 0) { const char *dir_path; list_for_each(&dir, dir_path) _slots_add(dir_path); list_erase(&dir); } } /** */ static int _is_failed_array(struct raid_device *raid) { if (raid->degraded > 0) { switch (raid->level) { case RAID_LEVEL_1: case RAID_LEVEL_10: return (raid->degraded == raid->raid_disks); case RAID_LEVEL_4: case RAID_LEVEL_5: return (raid->degraded > 1); case RAID_LEVEL_6: return (raid->degraded > 2); case RAID_LEVEL_LINEAR: case RAID_LEVEL_UNKNOWN: case RAID_LEVEL_0: break; case RAID_LEVEL_FAULTY: return 1; } } return -1; } /** */ static void _set_block_state(struct block_device *block, enum ibpi_pattern ibpi) { char *debug_dev = strrchr(block->sysfs_path, '/'); debug_dev = debug_dev ? debug_dev + 1 : block->sysfs_path; log_debug("(%s): device: %s, state: %s", __func__, debug_dev, ibpi_str[ibpi]); if (block->ibpi < ibpi) block->ibpi = ibpi; } /** */ static void _set_array_state(struct raid_device *raid, struct block_device *block) { switch (raid->sync_action) { case RAID_ACTION_UNKNOWN: case RAID_ACTION_IDLE: case RAID_ACTION_FROZEN: _set_block_state(block, IBPI_PATTERN_NORMAL); break; case RAID_ACTION_RESHAPE: if (conf.blink_on_migration) _set_block_state(block, IBPI_PATTERN_REBUILD); break; case RAID_ACTION_CHECK: case RAID_ACTION_RESYNC: case RAID_ACTION_REPAIR: if (conf.blink_on_init) _set_block_state(block, IBPI_PATTERN_REBUILD); break; case RAID_ACTION_RECOVER: if (conf.rebuild_blink_on_all) _set_block_state(block, IBPI_PATTERN_REBUILD); break; } } /** */ static void _determine(struct slave_device *device) { if (!device->block->raid_dev || (device->block->raid_dev->type == DEVICE_TYPE_CONTAINER && device->raid->type == DEVICE_TYPE_VOLUME)) { raid_device_fini(device->block->raid_dev); device->block->raid_dev = raid_device_duplicate(device->raid); } if ((device->state & SLAVE_STATE_FAULTY) != 0) { _set_block_state(device->block, IBPI_PATTERN_FAILED_DRIVE); } else if ((device-> state & (SLAVE_STATE_BLOCKED | SLAVE_STATE_WRITE_MOSTLY)) != 0) { _set_block_state(device->block, IBPI_PATTERN_NORMAL); } else if ((device->state & SLAVE_STATE_SPARE) != 0) { if (_is_failed_array(device->raid) == 0) { if (device->raid->sync_action != RAID_ACTION_RESHAPE || conf.blink_on_migration == 1) _set_block_state(device->block, IBPI_PATTERN_REBUILD); } else { _set_block_state(device->block, IBPI_PATTERN_HOTSPARE); } } else if ((device->state & SLAVE_STATE_IN_SYNC) != 0) { switch (_is_failed_array(device->raid)) { case 0: _set_block_state(device->block, IBPI_PATTERN_DEGRADED); break; case 1: _set_block_state(device->block, IBPI_PATTERN_FAILED_ARRAY); break; } _set_array_state(device->raid, device->block); } } static void _determine_slaves(struct list *slave_list) { struct slave_device *device; list_for_each(slave_list, device) _determine(device); } void sysfs_init(void) { list_init(&sysfs_block_list, (item_free_t)block_device_fini); list_init(&volum_list, (item_free_t)raid_device_fini); list_init(&cntrl_list, (item_free_t)cntrl_device_fini); list_init(&slave_list, (item_free_t)slave_device_fini); list_init(&cntnr_list, (item_free_t)raid_device_fini); list_init(&enclo_list, (item_free_t)enclosure_device_fini); list_init(&slots_list, (item_free_t)pci_slot_fini); } void sysfs_reset(void) { list_erase(&sysfs_block_list); list_erase(&volum_list); list_erase(&cntrl_list); list_erase(&slave_list); list_erase(&cntnr_list); list_erase(&enclo_list); list_erase(&slots_list); } void sysfs_scan(void) { _scan_enclo(); _scan_cntrl(); _scan_slots(); _scan_block(); _scan_raid(); _scan_slave(); _determine_slaves(&slave_list); } /* * The function reutrns list of enclosure devices attached to SAS/SCSI storage * controller(s). */ const struct list *sysfs_get_enclosure_devices(void) { return &enclo_list; } /* * The function returns list of controller devices present in the system. */ const struct list *sysfs_get_cntrl_devices(void) { return &cntrl_list; } /* * The function returns list of RAID volumes present in the system. */ const struct list *sysfs_get_volumes(void) { return &volum_list; } const struct list *sysfs_get_block_devices(void) { return &sysfs_block_list; } const struct list *sysfs_get_pci_slots(void) { return &slots_list; } /* * The function checks if the given storage controller has enclosure device(s) * attached. */ int sysfs_enclosure_attached_to_cntrl(const char *path) { struct enclosure_device *device; list_for_each(&enclo_list, device) { if ((device->sysfs_path != NULL) && (strncmp(device->sysfs_path, path, strlen(path)) == 0)) return 1; } return 0; } /* * This function checks driver type. */ int sysfs_check_driver(const char *path, const char *driver) { char buf[PATH_MAX]; char driver_path[PATH_MAX]; char *link; int found = 0; str_cpy(buf, path, PATH_MAX); str_cat(buf, "/driver", PATH_MAX - 1); snprintf(driver_path, PATH_MAX - 1, "/%s", driver); link = realpath(buf, NULL); if (link && strstr(link, driver_path)) found = 1; free(link); return found; } ledmon-0.90/src/sysfs.h000066400000000000000000000046701324101631400150310ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2018 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #ifndef _SYSFS_H_INCLUDED_ #define _SYSFS_H_INCLUDED_ #include "status.h" /** * @brief Initializes sysfs module. * * This function initializes sysfs module internal lists. * Application must call this function before any sysfs module function. */ void sysfs_init(void); /** * @brief Resets the content of internal lists. * * This function releases memory allocated for elements of internal lists. */ void sysfs_reset(void); /** * @brief Scans sysfs tree and populates internal lists. * * This function scans sysfs tree for storage controllers, block devices, RAID * devices, container devices, slave devices and enclosure devices registered * in the system. Only supported block and controller devices are put on a list. */ void sysfs_scan(void); /** * The function returns list of enclosure devices attached to SAS/SCSI storage * controller(s). */ const struct list *sysfs_get_enclosure_devices(void); /** * The function returns list of controller devices present in the system. */ const struct list *sysfs_get_cntrl_devices(void); /** * The function returns list of RAID volumes present in the system. */ const struct list *sysfs_get_volumes(void); /** * The function returns list of block devices present in the system. */ const struct list *sysfs_get_block_devices(void); /** * The function returns list of pci slots present in the system. */ const struct list *sysfs_get_pci_slots(void); /** * The function checks if the given storage controller is attached to enclosure * device(s). */ int sysfs_enclosure_attached_to_cntrl(const char *path); /* * This function checks driver type. */ int sysfs_check_driver(const char *path, const char *driver); #endif /* _SYSFS_H_INCLUDED_ */ ledmon-0.90/src/udev.c000066400000000000000000000065471324101631400146250ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2017-2018 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #include "block.h" #include "ibpi.h" #include "status.h" #include "sysfs.h" #include "udev.h" #include "utils.h" static struct udev_monitor *udev_monitor; static int _compare(const struct block_device *bd, const char *syspath) { if (!bd || !syspath) return 0; if (strcmp(bd->sysfs_path, syspath) == 0) { return 1; } else { struct block_device *bd_new; int ret; bd_new = block_device_init(sysfs_get_cntrl_devices(), syspath); if (!bd_new) return 0; ret = block_compare(bd, bd_new); block_device_fini(bd_new); return ret; } } static int create_udev_monitor(void) { int res; struct udev *udev = udev_new(); if (!udev) { log_error("Failed to create udev context instance."); return -1; } udev_monitor = udev_monitor_new_from_netlink(udev, "udev"); if (!udev_monitor) { log_error("Failed to create udev monitor object."); udev_unref(udev); return -1; } res = udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "block", "disk"); if (res < 0) { log_error("Failed to modify udev monitor filters."); stop_udev_monitor(); return -1; } res = udev_monitor_enable_receiving(udev_monitor); if (res < 0) { log_error("Failed to switch udev monitor to listening mode."); stop_udev_monitor(); return -1; } return udev_monitor_get_fd(udev_monitor); } void stop_udev_monitor(void) { if (udev_monitor) { struct udev *udev = udev_monitor_get_udev(udev_monitor); udev_monitor_unref(udev_monitor); if (udev) udev_unref(udev); } } int get_udev_monitor() { if (udev_monitor) return udev_monitor_get_fd(udev_monitor); return create_udev_monitor(); } int handle_udev_event(struct list *ledmon_block_list) { struct udev_device *dev; dev = udev_monitor_receive_device(udev_monitor); if (dev) { const char *action = udev_device_get_action(dev); const char *syspath = udev_device_get_syspath(dev); struct block_device *block = NULL; list_for_each(ledmon_block_list, block) { if (_compare(block, syspath)) break; block = NULL; } if (!block) { /* ignore - device is new or ledmon is not interested about it */ return 0; } if (strncmp(action, "add", 3) == 0) { log_debug("ADDED %s", block->sysfs_path); if (block->ibpi == IBPI_PATTERN_FAILED_DRIVE || block->ibpi == IBPI_PATTERN_REMOVED) block->ibpi = IBPI_PATTERN_ADDED; } else if (strncmp(action, "remove", 6) == 0) { log_debug("REMOVED %s", block->sysfs_path); block->ibpi = IBPI_PATTERN_REMOVED; } else { /* not interesting event */ return 1; } return 0; } else { return -1; } } ledmon-0.90/src/udev.h000066400000000000000000000035701324101631400146230ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2017-2018 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #ifndef _UDEV_H_INCLUDED_ #define _UDEV_H_INCLUDED_ #include "list.h" /** * @brief Deletes udev context and udev monitor. * * @return The function does not return a value.. */ void stop_udev_monitor(void); /** * @brief Returns udev monitor file descriptor or creates udev context and * udev monitor if monitor does not exist. * * @return Udev monitor file descriptor if successful, otherwise the * function returns -1 on libudev error and libudev sets errno. */ int get_udev_monitor(void); /** * @brief Handles udev event. * * This function checks event type and if it is 'add' or remove * function sets custom IBPI pattern to block device which is affected * by this event. * * @param[in] ledmon_block_list list containing block devices, it is * used to match device from udev event. * * @return 0 if 'add' or 'remove' event handled successfully; * 1 if registered event is not 'add' or 'remove'; * -1 on libudev error. */ int handle_udev_event(struct list *ledmon_block_list); #endif /* _UDEV_H_INCLUDED_ */ ledmon-0.90/src/utils.c000066400000000000000000000247521324101631400150200ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2018 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if _HAVE_DMALLOC_H #include #endif #include "config.h" #include "config_file.h" #include "list.h" #include "status.h" #include "utils.h" /** */ #define PREFIX_DEBUG " DEBUG: " #define PREFIX_WARNING "WARNING: " #define PREFIX_INFO " INFO: " #define PREFIX_ERROR " ERROR: " /** */ #define TIMESTAMP_PATTERN "%b %d %T " /** * Name of the executable. It is the last section of invocation path. */ char *progname = NULL; /** */ static FILE *s_log = NULL; /* * Function returns a content of a text file. See utils.h for details. */ char *get_text(const char *path, const char *name) { char temp[PATH_MAX]; str_cpy(temp, path, PATH_MAX); str_cat(temp, PATH_DELIM_STR, PATH_MAX); str_cat(temp, name, PATH_MAX); return buf_read(temp); } /* * Function returns integer value (1 or 0) based on a boolean value ('Y' or 'N') * read from a text file. See utils.h for details. */ int get_bool(const char *path, int defval, const char *name) { char *p = get_text(path, name); if (p) { if (*p == 'Y') defval = 1; else if (*p == 'N') defval = 0; free(p); } return defval; } /* * Function returns 64-bit unsigned integer value read from a text file. See * utils.h for details. */ uint64_t get_uint64(const char *path, uint64_t defval, const char *name) { char *p = get_text(path, name); if (p) { sscanf(p, "0x%Lx", (long long unsigned int *)&defval); free(p); } return defval; } /* * Function returns integer value read from a text file. * See utils.h for details. */ int get_int(const char *path, int defval, const char *name) { char *p = get_text(path, name); if (p) { defval = atoi(p); free(p); } return defval; } /** */ int scan_dir(const char *path, struct list *result) { struct dirent *dirent; int ret = 0; DIR *dir = opendir(path); if (!dir) return -1; list_init(result, NULL); while ((dirent = readdir(dir)) != NULL) { char *str; size_t len; if ((strcmp(dirent->d_name, ".") == 0) || (strcmp(dirent->d_name, "..")) == 0) continue; len = strlen(path) + strlen(dirent->d_name) + 2; str = malloc(len); if (!str) { ret = -1; list_erase(result); break; } snprintf(str, len, "%s/%s", path, dirent->d_name); list_append(result, str); } closedir(dir); return ret; } /** */ static int _is_virtual(int dev_type) { switch (dev_type) { case 0: /* sysfs */ case 3: /* procfs */ return 1; } return 0; } /** */ int buf_write(const char *path, const char *buf) { int fd, size = -1; if (path == NULL) __set_errno_and_return(EINVAL); if ((buf == NULL) || (strlen(buf) == 0)) __set_errno_and_return(ENODATA); fd = open(path, O_WRONLY); if (fd >= 0) { size = write(fd, buf, strlen(buf)); close(fd); } return size; } /** */ char *buf_read(const char *path) { struct stat st; int fd, size; char *buf, *t; if (stat(path, &st) < 0) return NULL; if (st.st_size == 0) { if (!_is_virtual(st.st_dev)) return NULL; st.st_size = st.st_blksize; } if (_is_virtual(st.st_dev)) st.st_size = st.st_blksize; t = buf = malloc(st.st_size); if (buf) { fd = open(path, O_RDONLY); if (fd >= 0) { size = read(fd, buf, st.st_size); close(fd); if (size > 0) t = strchrnul(buf, '\n'); } *t = '\0'; } return buf; } /** */ void get_id(const char *path, struct device_id *did) { char *t, *p; if (did && path) { did->major = did->minor = -1; p = buf_read(path); if (p) { t = strchr(p, ':'); if (t) { *(t++) = '\0'; did->major = atoi(p); did->minor = atoi(t); } free(p); } } } /** */ static void _log_timestamp(void) { time_t timestamp; struct tm *t; char buf[30]; timestamp = time(NULL); t = localtime(×tamp); if (t) { strftime(buf, sizeof(buf), TIMESTAMP_PATTERN, t); fprintf(s_log, "%s", buf); } } /** */ static int _mkdir(const char *path) { char temp[PATH_MAX]; int status = -1; char *t = realpath(path, temp); while (t) { t = strchr(t + 1, PATH_DELIM); if (t) *t = '\0'; status = mkdir(temp, 0640); if (t) *t = PATH_DELIM; if ((status < 0) && (errno != EEXIST)) break; status = 0; } return status; } /** */ int log_open(const char *path) { if (s_log) log_close(); char *t = rindex(path, PATH_DELIM); if (t) *t = '\0'; int status = _mkdir(path); if (t) *t = PATH_DELIM; if (status == 0) { s_log = fopen(path, "a"); if (s_log == NULL) return -1; } return status; } /** */ void log_close(void) { if (s_log) { fflush(s_log); fclose(s_log); s_log = NULL; } closelog(); } /** */ void log_debug(const char *buf, ...) { va_list vl; if (s_log == NULL) log_open(conf.log_path); if (s_log && (conf.log_level >= LOG_LEVEL_DEBUG)) { _log_timestamp(); fprintf(s_log, PREFIX_DEBUG); va_start(vl, buf); vfprintf(s_log, buf, vl); va_end(vl); fprintf(s_log, "\n"); fflush(s_log); va_start(vl, buf); vsyslog(LOG_DEBUG, buf, vl); va_end(vl); } } /** */ void log_error(const char *buf, ...) { va_list vl; if (s_log == NULL) log_open(conf.log_path); if (s_log && (conf.log_level >= LOG_LEVEL_ERROR)) { _log_timestamp(); fprintf(s_log, PREFIX_ERROR); va_start(vl, buf); vfprintf(s_log, buf, vl); va_end(vl); fprintf(s_log, END_LINE_STR); fflush(s_log); va_start(vl, buf); vsyslog(LOG_ERR, buf, vl); va_end(vl); } } /** */ void log_warning(const char *buf, ...) { va_list vl; if (s_log == NULL) log_open(conf.log_path); if (s_log && (conf.log_level >= LOG_LEVEL_WARNING)) { _log_timestamp(); fprintf(s_log, PREFIX_WARNING); va_start(vl, buf); vfprintf(s_log, buf, vl); va_end(vl); fprintf(s_log, END_LINE_STR); fflush(s_log); va_start(vl, buf); vsyslog(LOG_WARNING, buf, vl); va_end(vl); } } /** */ void log_info(const char *buf, ...) { va_list vl; if (s_log == NULL) log_open(conf.log_path); if (s_log && (conf.log_level >= LOG_LEVEL_INFO)) { _log_timestamp(); fprintf(s_log, PREFIX_INFO); va_start(vl, buf); vfprintf(s_log, buf, vl); va_end(vl); fprintf(s_log, END_LINE_STR); fflush(s_log); va_start(vl, buf); vsyslog(LOG_INFO, buf, vl); va_end(vl); } } /** * @brief Sets program's short name. * * This is internal function of monitor service. It is used to extract the name * of executable file from command line argument. * * @param[in] invocation_name - the pointer to command line argument * with the invocation name. * * @return The function does not return a value. */ void set_invocation_name(char *invocation_name) { #ifdef program_invocation_short_name (void)invocation_name; progname = program_invocation_short_name; #else char *t = rindex(invocation_name, PATH_DELIM); if (t) progname = t + 1; else progname = invocation_name; #endif /* program_invocation_short_name */ } /** */ char *str_cpy(char *dest, const char *src, size_t size) { strncpy(dest, src, size - 1); dest[size - 1] = '\0'; return dest; } /** */ char *str_dup(const char *src) { if (src && (strlen(src) > 0)) return strdup(src); return NULL; } /** */ char *str_cat(char *dest, const char *src, size_t size) { int t = strlen(dest); strncat(dest, src, size - t); if (t + strlen(src) >= size) dest[size - 1] = '\0'; return dest; } char *get_path_hostN(const char *path) { char *c = NULL, *s = NULL, *p = strdup(path); if (!p) return NULL; c = strstr(p, "host"); if (!c) goto end; s = strchr(c, '/'); if (!s) goto end; *s = 0; s = strdup(c); end: free(p); return s; } char *get_path_component_rev(const char *path, int index) { int i; char *c = NULL, *p = strdup(path); char *result = NULL; for (i = 0; i <= index; i++) { if (c) *c = '\0'; c = strrchr(p, '/'); } if (c) result = strdup(c + 1); free(p); return result; } char *truncate_path_component_rev(const char *path, int index) { int i; char *c = NULL, *p = str_dup(path); if (!p) return NULL; for (i = 0; i <= index; i++) { if (c) *c = '\0'; c = strrchr(p, '/'); } c = strdup(p); free(p); return c; } int match_string(const char *string, const char *pattern) { int status; regex_t regex; if (!string || !pattern) return 0; if (strcmp(string, pattern) == 0) return 1; status = regcomp(®ex, pattern, REG_EXTENDED); if (status != 0) { log_debug("regecomp failed, ret=%d", __FUNCTION__, status); return 0; } status = regexec(®ex, string, 0, NULL, 0); if (status != 0) return 0; return 1; } int get_log_fd(void) { return fileno(s_log); } void print_opt(const char *long_opt, const char *short_opt, const char *desc) { printf("%-20s%-10s%s\n", long_opt, short_opt, desc); } /** * @brief Sets the path to local log file. * * This function sets the path and * file name of log file. The function checks if the specified path is valid. In * case of incorrect path the function does nothing. * * @param[in] path new location and name of log file. * * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. * The following status code are returned: * * STATUS_INVALID_PATH the given path is invalid. * STATUS_FILE_OPEN_ERROR unable to open a log file i.e. because of * insufficient privileges. */ status_t set_log_path(const char *path) { char temp[PATH_MAX]; if (realpath(path, temp) == NULL) { if ((errno != ENOENT) && (errno != ENOTDIR)) return STATUS_INVALID_PATH; } if (log_open(temp) < 0) return STATUS_FILE_OPEN_ERROR; if (conf.log_path) free(conf.log_path); conf.log_path = strdup(temp); return STATUS_SUCCESS; } ledmon-0.90/src/utils.h000066400000000000000000000300121324101631400150070ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2018 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #ifndef _UTILS_H_INCLUDED_ #define _UTILS_H_INCLUDED_ #include "stdlib.h" #include "stdint.h" #include "list.h" #include "status.h" /** * Maximum number of bytes in temporary buffer. It is used for local variables. */ #define BUFFER_MAX 128 /** * Maximum number of bytes in write buffer. It is used for local variables when * function needs to write a sysfs attribute. */ #define WRITE_BUFFER_SIZE 1024 /** * This structure describes a device identifier. It consists of major and minor * attributes of device. */ struct device_id { int major, minor; }; /** * This global variable holds the name of binary file an application has been * executed from. */ extern char *progname; /** * @brief Reads integer value from a text file. * * This function assumes that the only text in a file is requested number to * read. In case of an error while reading from file the function will return * a value stored in defval argument. * * @param[in] path location of a file to be read. * @param[in] defval default value to return in case the file * does not exist. * @param[in] name name of a file to be read. * * @return Value read from a file if successful, otherwise a value stored in * defval argument. */ int get_int(const char *path, int defval, const char *name); /** * @brief Reads 64-bit unsigned integer from a text file. * * This function assumes that the only text in a file is requested number to * read. In case of an error while reading from file the function will return * a value stored in defval argument. * * @param[in] path Path where a file is located. * @param[in] defval Default value to be returned in case of error. * @param[in] name Name of a file to be read. * * @return Value read from a file if successful, otherwise a value stored in * defval argument. */ uint64_t get_uint64(const char *path, uint64_t defval, const char *name); /** * @brief Reads a content of a text file. * * This function reads a text file and return pointer to memory where the text * has been stored. The memory allocated by the function must be release as soon * as application does not require the content. Use free() function to give * allocated memory back to the system. * * @param[in] path Path where a file is located. * @param[in] name Name of a file to be read. * * @return Pointer to memory buffer if successful, otherwise NULL pointer. */ char *get_text(const char *path, const char *name); /** * @brief Reads boolean value from a text file. * * This function assumes that the only text in a file is the requested value to * read. The recognized boolean values are in the format 'Y' for True and 'N' * for False. In case of an error while reading from file the function will * return a value stored in defval argument. * * @param[in] path Path where a file is located. * @param[in] defval Default value to be returned in case of error. * @param[in] name Name of a file to be read. * * @return Value read from a file if successful, otherwise a value stored in * defval argument. 1 is returned for True, 0 for False. */ int get_bool(const char *path, int defval, const char *name); /** * @brief Writes a text to file. * * This function writes a text to a file and return the number of bytes written. * If the file does not exist or the value is incorrect the function returns -1 * and errno has additional error information. * * @param[in] path Location of file to write. * @param[in] name Name of file to write. * @param[in] value Text to write to a file. * * @return The number of bytes written or -1 if an error occurred. */ int put_text(const char *path, const char *name, const char *value); /** * @brief Writes an integer value to a text file. * * This function writes an integer value to a text file. If the file does not * exist or the value is out of range the function returns -1 and errno variable * has additional error information. * * @param[in] path Location of file to write. * @param[in] name Name of file to write. * @param[in] value Integer value to write to a file. * * @return The number of bytes written or -1 if an error occurred. */ int put_int(const char *path, const char *name, int value); /** * @brief Scans directory for files. * * This function reads a directory specified in path argument and puts found * file on a list. The function puts a canonical paths on the list, however it * does not resolve any symbolic link. * * This function allocates memory for the list elements. The caller should free * it using list_erase(). * * @param[in] path Path to directory to read. * @param[in] result Pointer to list where the directory contents * will be put. * * @return 0 on success, -1 on error. */ int scan_dir(const char *path, struct list *result); /** * @brief Writes a text to file. * * This function writes content of text buffer to file. If the file does not * exist or a content is invalid the function returns -1 and errno variable has * additional error information. * * @param[in] path Location and name of file to write to. * @param[in] buf Pointer to text buffer to write to a file. * * @return Number of bytes written if successful, otherwise -1 for an error. */ int buf_write(const char *path, const char *buf); /** * @brief Reads the content of a text file. * * The function reads the content of a text file and stores it to memory buffer * allocated. The function determines a size of the file and allocates required * amount of memory. User is required to free allocated memory as soon as * application does not require the content. Use free() function to give memory * back to the system pool. The function replaces last end-of-line character * with '\0' character. * * @param[in] path Path and name of file to read. * * @return Pointer to memory block if successful, otherwise NULL pointer. */ char *buf_read(const char *path); /** * @brief Gets major and minor of device. * * The function reads from text buffer the major and minor of block device. * * @param[in] buf Pointer to text buffer to interpret. * @param[out] did Placeholder where major and minor will be * stored. * * @return The function does not return a value. */ void get_id(const char *buf, struct device_id *did); /** * @brief Open a local log file. * * The function opens a file to write log messages. If the given file does not * exist the new one will be created. If the file already exist the file will be * opened in append mode and the pointer will be set at the end of a file. * * @param[in] path Location and name of a log file. * * @return The function returns 0 if successful, otherwise -1 and errno variable * has additional error information. */ int log_open(const char *path); /** * @brief Close a local log file. * * The function closes a local log file. If the file has not been opened the * function does nothing. * * @return The function does not return a value. */ void log_close(void); /** * @brief Logs an error message. * * The function logs a message at error level of verbosity. * * @param[in] buf Buffer containing format of a message. * @param[in] ... Additional arguments according to format of * a message. * * @return The function does not return a value. */ void log_error(const char *buf, ...); /** * @brief Logs a debug message. * * The function logs a message at debug level of verbosity. * * @param[in] buf Buffer containing format of a message. * @param[in] ... Additional arguments according to format of * a message. * * @return The function does not return a value. */ void log_debug(const char *buf, ...); /** * @brief Logs a warning message. * * The function logs a message at warning level of verbosity. * * @param[in] buf Buffer containing format of a message. * @param[in] ... Additional arguments according to format of * a message. * * @return The function does not return a value. */ void log_warning(const char *buf, ...); /** * @brief Logs a information message. * * The function logs a message at info level of verbosity. * * @param[in] buf Buffer containing format of a message. * @param[in] ... Additional arguments according to format of * a message. * * @return The function does not return a value. */ void log_info(const char *buf, ...); /** */ void set_invocation_name(char *invocation_name); /** * @brief Copies a text buffer. * * This function copies source text buffer to destination buffer. The function * always return a null-terminated buffer even if src does not fit in dest. * * @param[out] dest Pointer to destination buffer. * @param[in] src Pointer to source buffer. * @param[in] size Capacity of destination buffer in bytes. * * @return Pointer to destination buffer even if function failed. */ char *str_cpy(char *dest, const char *src, size_t size); /** * @brief Duplicates a text buffer. * * This function duplicates a text buffer. It allocates a new memory block and * copies the content of source buffer to new location. If pointer to source * buffer is NULL the function will return NULL, too. The caller is required to * free allocated memory as soon as content is not needed. * * @param[in] src Source buffer to duplicate the content. * * @return Pointer to allocated memory block if successful, otherwise NULL. */ char *str_dup(const char *src); /** * @brief Concatenates text buffers. * * This function appends source buffer to destination buffer. It is similar to * strncat() standard C function except it always returns null-terminated * buffer. Second difference is that function calculates itself the amount * of free space in destination buffer. If source does not fit in dest * then as many bytes are copied as can be fit in destination buffer minus 1 * for null-character. Otherwise the source is copied to destination * including null-character. * * @param[in,out] dest Pointer to destination buffer. * @param[in] src Pointer to source buffer. * @param[in] size Capacity of destination buffer. * * @return Pointer to destination buffer even if function failed. */ char *str_cat(char *dest, const char *src, size_t size); /** */ char *truncate_path_component_rev(const char *path, int index); /** */ char *get_path_component_rev(const char *path, int index); /** * @brief Extracts the 'hostX' part from path. */ char *get_path_hostN(const char *path); int match_string(const char *string, const char *pattern); /** */ int get_log_fd(void); /** */ void print_opt(const char *long_opt, const char *short_opt, const char *desc); /** */ status_t set_log_path(const char *path); #endif /* _UTILS_H_INCLUDED_ */ ledmon-0.90/src/version.h000066400000000000000000000023301324101631400153360ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2018 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #ifndef _VERSION_H_INCLUDED_ #define _VERSION_H_INCLUDED_ /** * The major number of LEDCTL utilities package. This number may be overwrite * in makefile to reflect current version number. */ #ifndef VERSION_MAJOR #define VERSION_MAJOR 0 #endif /** * The minor number of LEDCTL utilities package. This number may be overwrite * in makefile to reflect current version number. */ #ifndef VERSION_MINOR #define VERSION_MINOR 90 #endif #endif /* _VERSION_H_INCLUDED_ */ ledmon-0.90/src/vmdssd.c000066400000000000000000000100641324101631400151470ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (c) 2016-2018, Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #include #include #include "config.h" #include "list.h" #include "pci_slot.h" #include "status.h" #include "sysfs.h" #include "utils.h" #include "vmdssd.h" #define ATTENTION_OFF 0b1111 /* Attention Off, Power Off */ #define ATTENTION_LOCATE 0b0111 /* Attention Off, Power On */ #define ATTENTION_REBUILD 0b0101 /* Attention On, Power On */ #define ATTENTION_FAILURE 0b1101 /* Attention On, Power Off */ #define SYSFS_PCIEHP "/sys/module/pciehp" static char *get_slot_from_syspath(char *path) { char *cur, *ret = NULL; char *temp_path = strdup(path); if (!temp_path) return NULL; cur = strtok(temp_path, "/"); while (cur != NULL) { char *next = strtok(NULL, "/"); if ((next != NULL) && strcmp(next, "nvme") == 0) break; cur = next; } cur = strtok(cur, "."); if (cur) ret = strdup(cur); free(temp_path); return ret; } static void get_ctrl(enum ibpi_pattern ibpi, uint16_t *new) { switch (ibpi) { case IBPI_PATTERN_LOCATE: *new = ATTENTION_LOCATE; break; case IBPI_PATTERN_FAILED_DRIVE: *new = ATTENTION_FAILURE; break; case IBPI_PATTERN_REBUILD: *new = ATTENTION_REBUILD; break; default: *new = ATTENTION_OFF; break; } } static int check_slot_module(const char *slot_path) { char module_path[PATH_MAX], real_module_path[PATH_MAX]; struct list dir; // check if slot is managed by pciehp driver snprintf(module_path, PATH_MAX, "%s/module", slot_path); if (scan_dir(module_path, &dir) == 0) { list_erase(&dir); realpath(module_path, real_module_path); if (strcmp(real_module_path, SYSFS_PCIEHP) != 0) __set_errno_and_return(EINVAL); } else { __set_errno_and_return(ENOENT); } return 0; } struct pci_slot *vmdssd_find_pci_slot(char *device_path) { char *pci_addr; struct pci_slot *slot = NULL; pci_addr = get_slot_from_syspath(device_path); if (!pci_addr) return NULL; list_for_each(sysfs_get_pci_slots(), slot) { if (strcmp(slot->address, pci_addr) == 0) break; slot = NULL; } free(pci_addr); if (slot == NULL || check_slot_module(slot->sysfs_path) < 0) return NULL; return slot; } int vmdssd_write(struct block_device *device, enum ibpi_pattern ibpi) { char attention_path[PATH_MAX]; char buf[WRITE_BUFFER_SIZE]; uint16_t val; struct pci_slot *slot; char *short_name = strrchr(device->sysfs_path, '/'); if (short_name) short_name++; else short_name = device->sysfs_path; if (ibpi == device->ibpi_prev) return 0; if ((ibpi < IBPI_PATTERN_NORMAL) || (ibpi > IBPI_PATTERN_LOCATE_OFF)) __set_errno_and_return(ERANGE); slot = vmdssd_find_pci_slot(device->sysfs_path); if (!slot) { log_debug("PCI hotplug slot not found for %s\n", short_name); __set_errno_and_return(ENODEV); } log_debug("%s before: 0x%x\n", short_name, get_int(slot->sysfs_path, 0, "attention")); get_ctrl(ibpi, &val); snprintf(buf, WRITE_BUFFER_SIZE, "%u", val); snprintf(attention_path, PATH_MAX, "%s/attention", slot->sysfs_path); if (buf_write(attention_path, buf) != strlen(buf)) { log_error("%s write error: %d\n", slot->sysfs_path, errno); return -1; } log_debug("%s after: 0x%x\n", short_name, get_int(slot->sysfs_path, 0, "attention")); return 0; } char *vmdssd_get_path(const char *path, const char *cntrl_path) { return strdup(cntrl_path); } ledmon-0.90/src/vmdssd.h000066400000000000000000000020121324101631400151460ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (c) 2016-2017, Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #ifndef _VMDSSD_H #define _VMDSSD_H #include "block.h" #include "ibpi.h" int vmdssd_write(struct block_device *device, enum ibpi_pattern ibpi); char *vmdssd_get_path(const char *path, const char *cntrl_path); struct pci_slot *vmdssd_find_pci_slot(char *device_path); #endif