pax_global_header00006660000000000000000000000064140003350010014474gustar00rootroot0000000000000052 comment=2220b3be777139f4880491cbd89d569dcba2fbd2 ledmon-0.95/000077500000000000000000000000001400033500100127075ustar00rootroot00000000000000ledmon-0.95/.gitignore000066400000000000000000000003311400033500100146740ustar00rootroot00000000000000ledctl.8.gz ledmon.conf.5.gz ledmon.8.gz *.o _build ledmon ledctl *~ *.bak .depend *.swp Makefile Makefile.in aclocal.m4 autom4te.cache/ config.h.in build-aux/ configure config_ac.h config.log config.status stamp-h1 ledmon-0.95/CHANGELOG.md000066400000000000000000000103721400033500100145230ustar00rootroot00000000000000### v0.95 / 2021-01-15 [Commit list](https://github.com/intel/ledmon/compare/v0.94...v0.95) Enhancements * Allow to run ledctl version without root * README update with the compilation steps Bug fixes * Documentation updates * Defaulting to SGPIO for AMD systems * Don't rely on states priority while changing IBPI states * Check the white/blacklist from ledmon.conf earlier in discovery * Change installation directory to /usr/sbin * Use package version from autotools, not version.h * Fix memory leak in utils.c * Bugfixes and refactoring in SES module * Fixed issues reported by static analysis * Build system fixes * Other minor fixes ### v0.94 / 2020-02-04 [Commit list](https://github.com/intel/ledmon/compare/v0.93...v0.94) Enhancements * Support for AMD IPMI enclosure management * Support for NPEM Bug fixes * Documentation updates * Fix activity indicator state for SMP * Fix for GCC 9 compilation * Update ipbi pattern for drives with previous pattern unknown ### v0.93 / 2019-10-17 [Commit list](https://github.com/intel/ledmon/compare/v0.92...v0.93) Enhancements * Support for AMD SGPIO enclosure management * Migration to GNU Autotools build system * Added more strict compilation flags Bug fixes * Fixed segfault when a value is missing from ibpi_str * Use proper format string with syslog() * Fixed issues reported by static analysis * Removed unused SGPIO structures * Added udev_device reference clean-up * Hidden ipmi error messages on non-dell platforms ### v0.92 / 2019-04-12 [Commit list](https://github.com/intel/ledmon/compare/v0.91-fixed...v0.92) Bug fixes * Silence warning and error messages. ### v0.91 / 2019-04-01 [Commit list](https://github.com/intel/ledmon/compare/v0.90...v0.91) Enhancements * Ledmon systemd service file. * Shared configuration between ledmon and ledctl. * Log-level support for ledctl. * Build label support. * 13G/14G Dell Servers support. * Foreground option. Bug fixes * Udev action handling reimplementation. * Unify ping process method. * Recognize volumes under reshape. * Distinguish inactive state for volume and container. * Fix various gcc and clang warnings. * Fix ledctl exit status. * Logging method reimplementation. * Makefile fixes. * Change outdated functions and simplify string modifications. * Ommited errors handling. ### 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.95/COPYING000066400000000000000000000431271400033500100137510ustar00rootroot00000000000000 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.95/Makefile.am000066400000000000000000000016321400033500100147450ustar00rootroot00000000000000# # Intel(R) Enclosure LED Utilities # Copyright (C) 2009-2021 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. # if SYSTEMD_CONDITION OPTIONAL_SUBDIR = systemd endif SUBDIRS = doc src $(OPTIONAL_SUBDIR) EXTRA_DIST = config/config.h systemd/ledmon.service.in dist_doc_DATA = README.md ledmon-0.95/README.md000066400000000000000000000031501400033500100141650ustar00rootroot00000000000000# This package contains the Enclosure LED Utilities, version 0.95 Copyright (C) 2009-2021 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. Dependencies ------------------------- Following packages are required to compile: |RHEL|SLES|Debian/Ubuntu| |:---:|:---:|:---:| | `RHEL8: pkgconf`, `RHEL7: pkgconfig` | `pkg-config` | `pkg-config` | | `automake` | `automake` | `automake` | | `autoconf` | `autoconf` | `autoconf` | | `gcc` | `gcc` | `gcc` | | `make` | `make` | `make` | | `sg3_utils-devel`| `libsgutils-devel` | `libsgutils2-dev` | | `systemd-devel` | `libudev-devel` | `libudev-dev` | | `pciutils-devel` | `pciutils-devel` | `libpci-dev` | ## 2. Configure package ------------------------- Run `autogen.sh` to generate compiling configurations: `./autogen.sh` `./configure` Run ./configure with: `--enable-systemd` to configure with systemd service. ## 3. Compiling the package ------------------------- Run `make` command to compile the package. ## 4. (Un)installing the package ------------------------- Run following commands to install package: `make install` Run following commands to uninstall package: `make uninstall` ## 5. Release notes ------------------------- a. Enclosure LED Utilities is meant as a part of RHEL, SLES and Debian/Ubuntu linux distributions. b. For backplane enclosures attached to ISCI controller support is limited to Intel(R) Intelligent Backplane. ledmon-0.95/autogen.sh000077500000000000000000000000401400033500100147020ustar00rootroot00000000000000#!/bin/sh autoreconf --install ledmon-0.95/config/000077500000000000000000000000001400033500100141545ustar00rootroot00000000000000ledmon-0.95/config/config.h000066400000000000000000000026751400033500100156040ustar00rootroot00000000000000/* * 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.95/configure.ac000066400000000000000000000053161400033500100152020ustar00rootroot00000000000000 AC_PREREQ([2.69]) AC_INIT([ledmon], [0.95]) AC_CONFIG_MACRO_DIR([m4]) AC_SUBST([PACKAGE_DATE], "January 2021") AM_INIT_AUTOMAKE([-Wall -Werror foreign]) # Checks for programs. AX_CHECK_PROG([gcc]) AX_CHECK_PROG([make]) AM_PROG_CC_C_O AC_PROG_CC_C99 AC_PROG_INSTALL AC_CONFIG_HEADERS([config_ac.h]) AM_CFLAGS='-Wall -I../config' AM_CPPFLAGS='-D_DEBUG -D_GNU_SOURCE -D_DEFAULT_SOURCE -DDMALLOC_DISABLE -DBUILD_LABEL=\""$(BUILD_LABEL)"\"' AC_DEFUN([AX_AM_CFLAGS_ADD],[AX_CHECK_COMPILE_FLAG($1, AM_CFLAGS="$AM_CFLAGS $1")]) AX_AM_CFLAGS_ADD([-Wformat -Werror=format-security]) AX_AM_CFLAGS_ADD([-Werror=format-overflow=2]) AX_AM_CFLAGS_ADD([-Werror=format-truncation=1]) AX_AM_CFLAGS_ADD([-Werror=shift-negative-value]) AX_AM_CFLAGS_ADD([-Werror=alloca]) AX_AM_CFLAGS_ADD([-Werror=missing-field-initializers]) AX_AM_CFLAGS_ADD([-Werror=format-signedness]) AC_SUBST([AM_CFLAGS]) AC_SUBST([AM_CPPFLAGS]) AC_PREFIX_DEFAULT(/usr) # Automake 1.11 - silent build rules m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) PKG_PROG_PKG_CONFIG # Checks for libraries. AC_CHECK_LIB([rt], [shm_unlink], [], [AC_MSG_ERROR(librt not found)]) AC_CHECK_LIB([sgutils2], [sg_ll_send_diag], [], [AC_MSG_ERROR(libsgutils not found)]) PKG_CHECK_MODULES([LIBUDEV], [libudev]) PKG_CHECK_MODULES([LIBPCI], [libpci]) # Checks for header files. AC_CHECK_HEADERS([fcntl.h inttypes.h limits.h stdint.h stdlib.h string.h sys/file.h sys/ioctl.h sys/param.h sys/time.h syslog.h unistd.h]) # Checks for typedefs, structures, and compiler characteristics. AC_CHECK_HEADER_STDBOOL AC_C_INLINE AC_TYPE_PID_T AC_TYPE_SIZE_T AC_CHECK_MEMBERS([struct stat.st_blksize]) AC_CHECK_MEMBERS([struct stat.st_rdev]) AC_TYPE_UINT16_T AC_TYPE_UINT32_T AC_TYPE_UINT64_T AC_TYPE_UINT8_T # Checks for library functions. AC_FUNC_FORK AC_FUNC_MALLOC AC_FUNC_MMAP AC_CHECK_FUNCS([ftruncate memset munmap realpath regcomp select strcasecmp strchr strdup strerror strrchr strstr strtol strtoul]) # configure options AC_ARG_ENABLE(systemd, AS_HELP_STRING([--enable-systemd], [install ledmon systemd service])) AS_IF([test "x$enable_systemd" = xyes], [SYSTEMD_STR=yes], [SYSTEMD_STR=no]) AM_CONDITIONAL([SYSTEMD_CONDITION], [test "$SYSTEMD_STR" = yes]) # target directory for ledmon service file AC_SUBST([SYSTEMD_PATH], "$(pkg-config systemd --variable=systemdsystemunitdir)") AC_CONFIG_FILES([Makefile doc/Makefile src/Makefile systemd/Makefile]) AC_OUTPUT AC_MSG_RESULT([ $PACKAGE_NAME $VERSION configuration: Source code location: ${srcdir} Preprocessor flags: ${AM_CPPFLAGS} ${CPPFLAGS} C compiler flags: ${AM_CFLAGS} ${CFLAGS} Common install location: ${prefix} configure parameters: --enable-systemd=${SYSTEMD_STR} ]) ledmon-0.95/doc/000077500000000000000000000000001400033500100134545ustar00rootroot00000000000000ledmon-0.95/doc/Makefile.am000066400000000000000000000027421400033500100155150ustar00rootroot00000000000000# # Intel(R) Enclosure LED Utilities # Copyright (C) 2009-2021 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. # CLEANFILES = ledmon.conf.gz ledmon.gz ledctl.gz EXTRA_DIST = ledmon.conf.pod ledmon.pod ledctl.pod dist_man5_MANS = ledmon.conf.gz dist_man8_MANS = ledmon.gz ledctl.gz ledmon.conf.gz: ledmon.conf.pod pod2man -r "LEDMON.CONF Version $(PACKAGE_VERSION) $(BUILD_LABEL)" -d "@PACKAGE_DATE@" \ -s 5 -n ledmon.conf -c "Intel(R) Enclosure LED Utilities Config" $< | gzip -9f > $@ ledmon.gz: ledmon.pod pod2man -r "LEDMON Version $(PACKAGE_VERSION) $(BUILD_LABEL)" -d "@PACKAGE_DATE@" \ -s 8 -n ledmon -c "Intel(R) Enclosure LED Monitor Service" $< | gzip -9f > $@ ledctl.gz: ledctl.pod pod2man -r "LEDCTL Version $(PACKAGE_VERSION) $(BUILD_LABEL)" -d "@PACKAGE_DATE@" \ -s 8 -n ledctl -c "Intel(R) Enclosure LED Control Application" $< | gzip -9f > $@ ledmon-0.95/doc/ledctl.pod000066400000000000000000000212251400033500100154310ustar00rootroot00000000000000# # Intel(R) Enclosure LED Utilities # Copyright (C) 2009-2021 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 supports LED management of the SAS/SATA and PCIe storages. =head4 Supported protocols/methods for LED management are: =over =item B for SAS devices, =item B for SATA, =item B for PCIe. =back B protocol is not supported. For SAS/SATA storages supporting controllers may transmit LED management information to the backplane controllers via the SGPIO interface. The SGPIO bus carries bit patterns, which translate into LED blink patterns in accordance with the International Blinking Pattern Interpretation (IBPI) of SFF-8489 specification for SGPIO. Please note some enclosures do not stick close to the SFF-8489 specification. It might happen that the enclosure processor will accept the IBPI pattern but it will blink LEDs not according to SFF-8489 specification or it has a limited number of patterns 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. =head4 The ledctl utilizes the following documents as references: =over =item SGPIO (Serial GPIO) - SFF-8485 =item IBPI (International Blinking Pattern Interpretation) - SFF-8489 =item LED Enclosure management messages - AHCI specification rev 1.3, section 12.2.1. =item SAS (Serial Attached SCSI) - T10/1760-D =item SES-2 (SCSI Enclosure Services-2) - T10/1559-D =item SMP (Serial Management Protocol) - T10/1760-D =item NPEM (Native PCIe Enclosure Management) - PCIe base specification rev 4.0 =item VMD (Intel(R) Volume Management Device) - Intel(R) VROC (VMD NVMe RAID) Quick Configuration Guide rev 1.2 =back =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) 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 below 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. =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. =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-2021 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.95/doc/ledmon.conf.pod000066400000000000000000000112321400033500100163610ustar00rootroot00000000000000# # 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.95/doc/ledmon.pod000066400000000000000000000131661400033500100154450ustar00rootroot00000000000000# # Intel(R) Enclosure LED Utilities # Copyright (C) 2009-2021 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 supports LED management of the SAS/SATA and PCIe storages. =head4 Supported protocols/methods for LED management are: =over =item B for SAS devices, =item B for SATA, =item B for PCIe. =back B protocol is not supported. For SAS/SATA storages supporting controllers may transmit LED management information to the backplane controllers via the SGPIO interface. The SGPIO bus carries bit patterns, which translate into LED blink patterns in accordance with the International Blinking Pattern Interpretation (IBPI) of SFF-8489 specification for SGPIO. Please note some enclosures do not stick close to the SFF-8489 specification. It might happen that the enclosure processor will accept the IBPI pattern but it will blink LEDs not according to SFF-8489 specification or it has a limited number of patterns supported. For more information about communication methods please consult the appropriate Specifications. 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. =head4 The ledmon utilizes the following documents as references: =over =item SGPIO (Serial GPIO) - SFF-8485 =item IBPI (International Blinking Pattern Interpretation) - SFF-8489 =item LED Enclosure management messages - AHCI specification rev 1.3, section 12.2.1. =item SAS (Serial Attached SCSI) - T10/1760-D =item SES-2 (SCSI Enclosure Services-2) - T10/1559-D =item SMP (Serial Management Protocol) - T10/1760-D =item NPEM (Native PCIe Enclosure Management) - PCIe base specification rev 4.0 =item VMD (Intel(R) Volume Management Device) - Intel(R) VROC (VMD NVMe RAID) Quick Configuration Guide rev 1.2 =back =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<--foreground> Run process foreground instead of a daemon. This option is useful in systemd service file. Another use case of this option is debugging with elevated 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-2021 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.95/m4/000077500000000000000000000000001400033500100132275ustar00rootroot00000000000000ledmon-0.95/m4/m4_ax_check_compile_flag.m4000066400000000000000000000040701400033500100203400ustar00rootroot00000000000000# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html # =========================================================================== # # SYNOPSIS # # AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) # # DESCRIPTION # # Check whether the given FLAG works with the current language's compiler # or gives an error. (Warnings, however, are ignored) # # ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on # success/failure. # # If EXTRA-FLAGS is defined, it is added to the current language's default # flags (e.g. CFLAGS) when the check is done. The check is thus made with # the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to # force the compiler to issue an error when a bad flag is given. # # INPUT gives an alternative input source to AC_COMPILE_IFELSE. # # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this # macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. # # LICENSE # # Copyright (c) 2008 Guido U. Draheim # Copyright (c) 2011 Maarten Bosmans # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 6 AC_DEFUN([AX_CHECK_COMPILE_FLAG], [AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], [AS_VAR_SET(CACHEVAR,[yes])], [AS_VAR_SET(CACHEVAR,[no])]) _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) AS_VAR_IF(CACHEVAR,yes, [m4_default([$2], :)], [m4_default([$3], :)]) AS_VAR_POPDEF([CACHEVAR])dnl ])dnl AX_CHECK_COMPILE_FLAGS ledmon-0.95/m4/m4_check_if_prog_installed.m4000066400000000000000000000007721400033500100207200ustar00rootroot00000000000000# SYNOPSIS # # AX_CHECK_PROG # # DESCRIPTION # # Check if the given program installed, let script continue if exists, pops up # error message if not. # # Besides checking existence, this macro also set these environment # variables upon completion: # # PROG_"prog_name" = result of checking if program installed (yes/no) AC_DEFUN([AX_CHECK_PROG], [dnl AC_CHECK_PROG([PROG_$1], [$1], [yes], [no]) AS_IF([test "$PROG_$1" = "no"], [dnl AC_MSG_ERROR([Utility "$1" not found.]) ]) ])ledmon-0.95/src/000077500000000000000000000000001400033500100134765ustar00rootroot00000000000000ledmon-0.95/src/Makefile.am000066400000000000000000000033101400033500100155270ustar00rootroot00000000000000# # Intel(R) Enclosure LED Utilities # Copyright (C) 2009-2021 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. # COMMON_SRCS = ahci.c block.c cntrl.c config_file.c enclosure.c list.c \ raid.c scsi.c slave.c status.c sysfs.c smp.c dellssd.c \ utils.c pci_slot.c vmdssd.c udev.c amd.c amd_sgpio.c amd_ipmi.c\ ipmi.c npem.c ses.c \ ahci.h amd_sgpio.h block.h cntrl.h config_file.h dellssd.h \ enclosure.h ibpi.h list.h pci_slot.h pidfile.h raid.h scsi.h \ ses.h slave.h smp.h status.h sysfs.h udev.h utils.h \ vmdssd.h ipmi.h amd.h amd_ipmi.h npem.h LEDMON_SRCS = ledmon.c pidfile.c $(COMMON_SRCS) LEDCTL_SRCS = ledctl.c $(COMMON_SRCS) sbin_PROGRAMS = ledmon ledctl ledmon_SOURCES = $(LEDMON_SRCS) ledctl_SOURCES = $(LEDCTL_SRCS) ledmon_LDADD = $(LIBPCI_LIBS) $(LIBUDEV_LIBS) ledctl_LDADD = $(LIBPCI_LIBS) $(LIBUDEV_LIBS) ledmon_CFLAGS = $(AM_CFLAGS) $(LIBPCI_CFLAGS) $(LIBUDEV_CFLAGS) ledctl_CFLAGS = $(AM_CFLAGS) $(LIBPCI_CFLAGS) $(LIBUDEV_CFLAGS) ledmon-0.95/src/ahci.c000066400000000000000000000067101400033500100145520ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2019 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 nano seconds to wait before enclosure management message * is being sent to AHCI controller. */ #define EM_MSG_WAIT 1500000 /* 0.0015 seconds */ /** * 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; const struct timespec waittime = { .tv_sec = 0, .tv_nsec = EM_MSG_WAIT }; /* 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]); snprintf(path, sizeof(path), "%s/em_message", sysfs_path); nanosleep(&waittime, NULL); return buf_write(path, temp) > 0; } #define SCSI_HOST "/scsi_host" /* * The function return path to SATA port in sysfs tree. See ahci.h for details. */ char *ahci_get_port_path(const char *path) { char *p; char tmp[PATH_MAX]; char *buf; size_t buf_size; p = strstr(path, "/target"); if (p == NULL) return NULL; if (sizeof(tmp) <= (p - path)) return NULL; strncpy(tmp, path, p - path); tmp[p - path] = '\0'; p = strrchr(tmp, PATH_DELIM); if (p == NULL) return NULL; buf_size = strlen(tmp) + strlen(p) + strlen(SCSI_HOST) + 1; buf = malloc(buf_size); if (buf == NULL) return NULL; snprintf(buf, buf_size, "%s%s%s", tmp, SCSI_HOST, p); return buf; } ledmon-0.95/src/ahci.h000066400000000000000000000035241400033500100145570ustar00rootroot00000000000000/* * 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.95/src/amd.c000066400000000000000000000100401400033500100143760ustar00rootroot00000000000000/* * AMD LED control * Copyright (C) 2021, Advanced Micro Devices, 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 #include #include #include #include #if _HAVE_DMALLOC_H #include #endif #include "config.h" #include "ibpi.h" #include "list.h" #include "utils.h" #include "amd.h" #include "amd_sgpio.h" #include "amd_ipmi.h" enum amd_led_interfaces amd_interface = AMD_INTF_UNSET; enum amd_ipmi_platforms amd_ipmi_platform = AMD_PLATFORM_UNSET; int _find_file_path(const char *start_path, const char *filename, char *path, size_t path_len) { int rc, found; struct stat sbuf; struct list dir; char *dir_name; const char *dir_path; rc = scan_dir(start_path, &dir); if (rc) { log_info("Failed to scan %s", start_path); return 0; } found = 0; list_for_each(&dir, dir_path) { dir_name = strrchr(dir_path, '/'); if (!dir_name) continue; /* skip past the leading '/' */ dir_name++; if (strncmp(dir_name, filename, strlen(filename)) == 0) { char tmp[PATH_MAX + 1]; strncpy(tmp, dir_path, path_len); snprintf(path, path_len, "%s", dirname(tmp)); found = 1; break; } if (lstat(dir_path, &sbuf) == -1) continue; if (S_ISDIR(sbuf.st_mode)) { found = _find_file_path(dir_path, filename, path, path_len); if (found) break; } } list_erase(&dir); return found; } /* For AMD platforms to use IPMI for LED control we need to know * the platform we're running on. This enables us to select the * proper channel and slave address when making IPMI requests. * Platforms not checked for IPMI enablement default to using SGPIO. */ int amd_em_enabled(const char *path) { char *platform; int rc; /* Default to SGPIO interface */ amd_interface = AMD_INTF_SGPIO; platform = get_text("/sys/class/dmi/id", "product_name"); if (!platform) return 0; /* Check IPMI platforms */ if (!strncmp(platform, "ETHANOL_X", 9)) { amd_interface = AMD_INTF_IPMI; amd_ipmi_platform = AMD_PLATFORM_ETHANOL_X; } else if (!strncmp(platform, "DAYTONA_X", 9)) { amd_interface = AMD_INTF_IPMI; amd_ipmi_platform = AMD_PLATFORM_DAYTONA_X; } switch (amd_interface) { case AMD_INTF_SGPIO: rc = _amd_sgpio_em_enabled(path); break; case AMD_INTF_IPMI: rc = _amd_ipmi_em_enabled(path); break; default: log_error("Unknown interface for AMD %s platform\n", platform); rc = -EOPNOTSUPP; break; } return rc; } int amd_write(struct block_device *device, enum ibpi_pattern ibpi) { int rc; /* write only if state has changed */ if (ibpi == device->ibpi_prev) return 1; switch (amd_interface) { case AMD_INTF_SGPIO: rc = _amd_sgpio_write(device, ibpi); break; case AMD_INTF_IPMI: rc = _amd_ipmi_write(device, ibpi); break; case AMD_INTF_UNSET: default: log_error("Unsupported AMD interface\n"); rc = -EOPNOTSUPP; break; } return rc; } char *amd_get_path(const char *cntrl_path, const char *sysfs_path) { char *path; switch (amd_interface) { case AMD_INTF_SGPIO: path = _amd_sgpio_get_path(sysfs_path); break; case AMD_INTF_IPMI: path = _amd_ipmi_get_path(cntrl_path, sysfs_path); break; case AMD_INTF_UNSET: default: log_error("Unsupported AMD interface\n"); path = NULL; break; } return path; } ledmon-0.95/src/amd.h000066400000000000000000000032131400033500100144070ustar00rootroot00000000000000/* * AMD LED control * Copyright (C) 2021, Advanced Micro Devices, 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" enum amd_device_type {AMD_NO_DEVICE, AMD_SATA_DEVICE, AMD_NVME_DEVICE}; struct amd_drive { int ata_port; int port; int drive_bay; int initiator; uint8_t channel; uint8_t slave_addr; enum amd_device_type dev; }; enum amd_led_interfaces { AMD_INTF_UNSET, AMD_INTF_SGPIO, AMD_INTF_IPMI, }; extern enum amd_led_interfaces amd_interface; enum amd_ipmi_platforms { AMD_PLATFORM_UNSET, AMD_PLATFORM_ETHANOL_X, AMD_PLATFORM_DAYTONA_X, }; extern enum amd_ipmi_platforms amd_ipmi_platform; int amd_em_enabled(const char *path); int amd_write(struct block_device *device, enum ibpi_pattern ibpi); char *amd_get_path(const char *cntrl_path, const char *sysfs_path); int _find_file_path(const char *start_path, const char *filename, char *path, size_t path_len); /* Register dump formats used for debug output */ #define REG_FMT_2 "%23s: %-4x%23s: %-4x\n" #define REG_FMT_1 "%23s: %-4x\n" ledmon-0.95/src/amd_ipmi.c000066400000000000000000000240271400033500100154260ustar00rootroot00000000000000/* * AMD IPMI LED control * Copyright (C) 2021, Advanced Micro Devices, 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 #include #include #include #include #if _HAVE_DMALLOC_H #include #endif #include "config.h" #include "ibpi.h" #include "list.h" #include "utils.h" #include "amd.h" #include "ipmi.h" static uint8_t amd_ibpi_ipmi_register[] = { [IBPI_PATTERN_PFA] = 0x41, [IBPI_PATTERN_LOCATE] = 0x42, [IBPI_PATTERN_FAILED_DRIVE] = 0x44, [IBPI_PATTERN_FAILED_ARRAY] = 0x45, [IBPI_PATTERN_REBUILD] = 0x46, [IBPI_PATTERN_HOTSPARE] = 0x47, }; /* The path we are given should be similar to * /sys/devices/pci0000:e0/0000:e0:03.3/0000:e3:00.0 * ^^^^^^^^^^ * We need to retrieve the address from the path (indicated above) * then use it to find the corresponding address for a slot in * /sys/bus/pci_slots to determine the icorrect port for the NVMe device. */ static int _get_ipmi_nvme_port(char *path) { int rc; char *p, *f; struct list dir; const char *dir_path; char *port_name; int port = -1; p = strrchr(path, '/'); if (!p) { log_error("Couldn't parse NVMe path to determine port\n"); return -1; } p++; /* p now points to the address, remove the bits after the '.' */ f = strchr(p, '.'); if (!f) { log_error("Couldn't parse NVMe port address\n"); return -1; } *f = '\0'; rc = scan_dir("/sys/bus/pci/slots", &dir); if (rc) return -1; list_for_each(&dir, dir_path) { port_name = get_text(dir_path, "address"); if (port_name && !strcmp(port_name, p)) { char *dname = strrchr(dir_path, '/'); dname++; port = strtol(dname, NULL, 0); break; } } list_erase(&dir); /* Some platfroms require an adjustment to the port value based * on how they are numbered by the BIOS. */ switch (amd_ipmi_platform) { case AMD_PLATFORM_DAYTONA_X: port -= 2; break; case AMD_PLATFORM_ETHANOL_X: port -= 7; break; default: break; } /* Validate port. Some BIOSes provide port values that are * not valid. */ if ((port < 0) || (port > 24)) { log_error("Invalid NVMe physical port %d\n", port); port = -1; } return port; } static int _get_ipmi_sata_port(const char *start_path) { int port; char *p, *t; char path[PATH_MAX]; strncpy(path, start_path, PATH_MAX); path[PATH_MAX - 1] = 0; t = p = strstr(path, "ata"); if (!p) return -1; /* terminate the path after the ataXX/ part */ p = strchr(p, '/'); if (!p) return -1; *p = '\0'; /* skip past 'ata' to get the ata port number */ t += 3; port = strtoul(t, NULL, 10); return port; } static int _get_amd_ipmi_drive(const char *start_path, struct amd_drive *drive) { int found; char path[PATH_MAX]; found = _find_file_path(start_path, "nvme", path, PATH_MAX); if (found) { drive->port = _get_ipmi_nvme_port(path); if (drive->port < 0) { log_error("Could not retrieve port number\n"); return -1; } drive->drive_bay = 1 << (drive->port - 1); drive->dev = AMD_NVME_DEVICE; } else { int shift; drive->port = _get_ipmi_sata_port(start_path); if (drive->port < 0) { log_error("Could not retrieve port number\n"); return -1; } /* IPMI control is handled through the MG9098 chips on * the platform, where each MG9098 chip can control up * to 8 drives. Since we can have multiple MG9098 chips, * we need the drive bay relative to the set of 8 controlled * by the MG9098 chip. */ shift = drive->port - 1; if (shift >= 8) shift %= 8; drive->drive_bay = 1 << shift; drive->dev = AMD_SATA_DEVICE; } log_debug("AMD Drive: port: %d, bay %x\n", drive->port, drive->drive_bay); return 0; } static int _ipmi_platform_channel(struct amd_drive *drive) { int rc = 0; switch (amd_ipmi_platform) { case AMD_PLATFORM_ETHANOL_X: drive->channel = 0xd; break; case AMD_PLATFORM_DAYTONA_X: drive->channel = 0x17; break; default: rc = -1; log_error("AMD Platform does not have a defined IPMI channel\n"); break; } return rc; } static int _ipmi_platform_slave_address(struct amd_drive *drive) { int rc = 0; switch (amd_ipmi_platform) { case AMD_PLATFORM_ETHANOL_X: drive->slave_addr = 0xc0; break; case AMD_PLATFORM_DAYTONA_X: if (drive->dev == AMD_NO_DEVICE) { /* Assume base slave address, we may not be able * to retrieve a valid amd_drive yet. */ drive->slave_addr = 0xc0; } else if (drive->dev == AMD_NVME_DEVICE) { /* On DaytonaX systems only drive bays 19 - 24 * support NVMe devices so use the slave address * for the corresponding MG9098 chip. */ drive->slave_addr = 0xc4; } else { if (drive->port <= 8) drive->slave_addr = 0xc0; else if (drive->port > 8 && drive->port < 17) drive->slave_addr = 0xc2; else drive->slave_addr = 0xc4; } break; default: rc = -1; log_error("AMD Platform does not have a defined IPMI slave address\n"); break; } return rc; } static int _set_ipmi_register(int enable, uint8_t reg, struct amd_drive *drive) { int rc; int status, data_sz; uint8_t drives_status; uint8_t new_drives_status; uint8_t cmd_data[5]; memset(cmd_data, 0, sizeof(cmd_data)); rc = _ipmi_platform_channel(drive); rc |= _ipmi_platform_slave_address(drive); if (rc) return -1; cmd_data[0] = drive->channel; cmd_data[1] = drive->slave_addr; cmd_data[2] = 0x1; cmd_data[3] = reg; /* Find current register setting */ status = 0; log_debug("Retrieving current register status\n"); log_debug(REG_FMT_2, "channel", cmd_data[0], "slave addr", cmd_data[1]); log_debug(REG_FMT_2, "len", cmd_data[2], "register", cmd_data[3]); rc = ipmicmd(BMC_SA, 0x0, 0x6, 0x52, 4, &cmd_data, 1, &data_sz, &status); if (rc) { log_error("Could not determine current register %x setting\n", reg); return rc; } drives_status = status; if (enable) new_drives_status = drives_status | drive->drive_bay; else new_drives_status = drives_status & ~drive->drive_bay; /* Set the appropriate status */ status = 0; cmd_data[4] = new_drives_status; log_debug("Updating register status: %x -> %x\n", drives_status, new_drives_status); log_debug(REG_FMT_2, "channel", cmd_data[0], "slave addr", cmd_data[1]); log_debug(REG_FMT_2, "len", cmd_data[2], "register", cmd_data[3]); log_debug(REG_FMT_1, "status", cmd_data[4]); rc = ipmicmd(BMC_SA, 0x0, 0x6, 0x52, 5, &cmd_data, 1, &data_sz, &status); if (rc) { log_error("Could not enable register %x\n", reg); return rc; } return 0; } static int _enable_smbus_control(struct amd_drive *drive) { log_debug("Enabling SMBUS Control\n"); return _set_ipmi_register(1, 0x3c, drive); } static int _enable_ibpi_state(struct amd_drive *drive, enum ibpi_pattern ibpi) { log_debug("Enabling %s LED\n", ibpi2str(ibpi)); return _set_ipmi_register(1, amd_ibpi_ipmi_register[ibpi], drive); } static int _disable_ibpi_state(struct amd_drive *drive, enum ibpi_pattern ibpi) { log_debug("Disabling %s LED\n", ibpi2str(ibpi)); return _set_ipmi_register(0, amd_ibpi_ipmi_register[ibpi], drive); } static int _disable_all_ibpi_states(struct amd_drive *drive) { int rc; rc = _disable_ibpi_state(drive, IBPI_PATTERN_PFA); rc |= _disable_ibpi_state(drive, IBPI_PATTERN_LOCATE); rc |= _disable_ibpi_state(drive, IBPI_PATTERN_FAILED_DRIVE); rc |= _disable_ibpi_state(drive, IBPI_PATTERN_FAILED_ARRAY); rc |= _disable_ibpi_state(drive, IBPI_PATTERN_REBUILD); return rc; } int _amd_ipmi_em_enabled(const char *path) { int rc; int status, data_sz; uint8_t cmd_data[4]; struct amd_drive drive; memset(&drive, 0, sizeof(struct amd_drive)); rc = _ipmi_platform_channel(&drive); rc |= _ipmi_platform_slave_address(&drive); if (rc) return -1; cmd_data[0] = drive.channel; cmd_data[1] = drive.slave_addr; cmd_data[2] = 0x1; cmd_data[3] = 0x63; status = 0; rc = ipmicmd(BMC_SA, 0x0, 0x6, 0x52, 4, &cmd_data, 1, &data_sz, &status); if (rc) { log_error("Can't determine MG9098 Status for AMD platform\n"); return 0; } if (status != 98) { log_error("Platform %s does not have a MG9098 controller\n"); return 0; } return 1; } int _amd_ipmi_write(struct block_device *device, enum ibpi_pattern ibpi) { int rc; struct amd_drive drive; log_info("\n"); log_info("Setting %s...", ibpi2str(ibpi)); rc = _get_amd_ipmi_drive(device->cntrl_path, &drive); if (rc) return rc; if ((ibpi == IBPI_PATTERN_NORMAL) || (ibpi == IBPI_PATTERN_ONESHOT_NORMAL)) { rc = _disable_all_ibpi_states(&drive); return rc; } if (ibpi == IBPI_PATTERN_LOCATE_OFF) { rc = _disable_ibpi_state(&drive, IBPI_PATTERN_LOCATE); return rc; } rc = _enable_smbus_control(&drive); if (rc) return rc; rc = _enable_ibpi_state(&drive, ibpi); if (rc) return rc; return 0; } char *_amd_ipmi_get_path(const char *cntrl_path, const char *sysfs_path) { char *p, *t; /* For NVMe devices we can just dup the path sysfs path */ p = strstr(cntrl_path, "nvme"); if (p) return strdup(sysfs_path); /* For SATA devices we need everything up to 'ataXX/' in the path */ p = strdup(cntrl_path); if (!p) return NULL; /* Find the beginning of the ataXX piece of the path */ t = strstr(p, "ata"); if (!t) return NULL; /* Move to the '/' after the ataXX piece of the path and terminate the * string there. */ t = strchr(t, '/'); if (!t) return NULL; *++t = '\0'; return p; } ledmon-0.95/src/amd_ipmi.h000066400000000000000000000017031400033500100154270ustar00rootroot00000000000000/* * AMD IPMI LED control * Copyright (C) 2019, Advanced Micro Devices, 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 _amd_ipmi_em_enabled(const char *path); int _amd_ipmi_write(struct block_device *device, enum ibpi_pattern ibpi); char *_amd_ipmi_get_path(const char *cntrl_path, const char *sysfs_path); ledmon-0.95/src/amd_sgpio.c000066400000000000000000000536661400033500100156240ustar00rootroot00000000000000/* * AMD SGPIO LED control * Copyright (C) 2019, Advanced Micro Devices, 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 #include #include #include #include #if _HAVE_DMALLOC_H #include #endif #include "config.h" #include "ibpi.h" #include "list.h" #include "utils.h" #include "amd.h" #include "amd_sgpio.h" #define HOST_CAP_EMS (1 << 6) #define DECLARE_SGPIO(type, name, shift, mask) \ uint32_t _##type##_##name##_shift = shift; \ uint64_t _##type##_##name##_mask = mask << shift; \ \ static void set_sgpio_##type##_##name(sgpio_##type##_t * hdr, \ sgpio_##type##_t val) \ { \ *hdr |= (val << _##type##_##name##_shift); \ } \ \ static uint32_t get_sgpio_##type##_##name(sgpio_##type##_t * hdr)\ { \ return (*hdr & _##type##_##name##_mask) \ >> _##type##_##name##_shift; \ } #define DECLARE_SGPIO_RO(type, name, shift, mask) \ uint32_t _##type##_##name##_shift = shift; \ uint64_t _##type##_##name##_mask = mask; \ \ static uint32_t get_sgpio_##type##_##name(sgpio_##type##_t * hdr)\ { \ return (*hdr & _##type##_##name##_mask) \ >> _##type##_##name##_shift; \ } typedef uint32_t sgpio_hdr_t; #define SGPIO_HDR_MSG_TYPE_SGPIO 0x03 DECLARE_SGPIO(hdr, msg_type, 4, 0xF) DECLARE_SGPIO(hdr, data_size, 8, 0xFF) DECLARE_SGPIO(hdr, msg_size, 16, 0xFF) typedef uint64_t sgpio_req_t; #define SGPIO_REQ_REG_TYPE_CFG 0x00 #define SGPIO_REQ_REG_TYPE_TX 0x03 #define SGPIO_REQ_REG_TYPE_AMD 0xC0 DECLARE_SGPIO(req, frame_type, 0, 0xFFL) DECLARE_SGPIO(req, function, 8, 0xFFL) DECLARE_SGPIO(req, reg_type, 16, 0xFFL) DECLARE_SGPIO(req, reg_index, 24, 0xFFL) DECLARE_SGPIO(req, reg_count, 32, 0xFFL) typedef uint32_t sgpio_amd_t; DECLARE_SGPIO(amd, initiator, 0, 0x1) DECLARE_SGPIO(amd, polarity_flip, 4, 0x1) DECLARE_SGPIO(amd, return_to_normal, 5, 0x1) DECLARE_SGPIO(amd, bypass_enable, 6, 0x1) typedef uint64_t sgpio_cfg_t; DECLARE_SGPIO_RO(cfg, version, 8, 0xF); DECLARE_SGPIO_RO(cfg, gp_reg_count, 16, 0xF); DECLARE_SGPIO_RO(cfg, cfg_reg_count, 20, 0x7); DECLARE_SGPIO(cfg, gpio_enable, 23, 0x1); DECLARE_SGPIO_RO(cfg, drive_count, 24, 0xFF); DECLARE_SGPIO(cfg, blink_gen_a, 40, 0xFL); DECLARE_SGPIO(cfg, blink_gen_b, 44, 0xFL); DECLARE_SGPIO(cfg, max_on, 48, 0xFL); DECLARE_SGPIO(cfg, force_off, 52, 0xFL); DECLARE_SGPIO(cfg, stretch_on, 56, 0xFL); DECLARE_SGPIO(cfg, stretch_off, 60, 0xFL); #define DECLARE_LED(name, shift, mask) \ uint32_t _led_##name##_shift = shift; \ uint64_t _led_##name##_mask = mask; \ \ static void set_##name##_led(drive_led_t *hdr, uint32_t val) \ { \ *hdr |= (val << _led_##name##_shift); \ } \ \ static uint32_t get_##name##_led(drive_led_t *hdr) \ { \ return (*hdr & _led_##name##_mask) >> _led_##name##_shift;\ } typedef uint8_t drive_led_t; DECLARE_LED(error, 0, 0x07); DECLARE_LED(locate, 3, 0x18); DECLARE_LED(activity, 5, 0xE0); typedef struct sgpio_transmit_register { drive_led_t drive[4]; } __attribute__ ((__packed__)) sgpio_tx_t; struct amd_register { sgpio_hdr_t hdr; sgpio_req_t req; sgpio_amd_t amd; } __attribute__ ((__packed__)); struct config_register { sgpio_hdr_t hdr; sgpio_req_t req; sgpio_cfg_t cfg; } __attribute__ ((__packed__)); struct transmit_register { sgpio_hdr_t hdr; sgpio_req_t req; sgpio_tx_t tx; } __attribute__ ((__packed__)); static uint8_t ibpi_pattern[] = { [IBPI_PATTERN_NONE] = 0x00, [IBPI_PATTERN_REBUILD] = 0x07, [IBPI_PATTERN_HOTSPARE] = 0x02, [IBPI_PATTERN_PFA] = 0x03, [IBPI_PATTERN_FAILED_DRIVE] = 0x00, [IBPI_PATTERN_LOCATE] = 0x07, [IBPI_PATTERN_LOCATE_OFF] = 0x00 }; struct drive_leds { drive_led_t error; drive_led_t locate; drive_led_t activity; } __attribute__ ((__packed__)); #define INIT_LED(e, l, a) {.error = e, .locate = l, .activity = a} static struct drive_leds tx_leds_blink_gen_a[] = { [IBPI_PATTERN_NORMAL] = INIT_LED(0, 0, 0b101), [IBPI_PATTERN_ONESHOT_NORMAL] = INIT_LED(0, 0, 0b101), [IBPI_PATTERN_REBUILD] = INIT_LED(0b010, 0, 0), [IBPI_PATTERN_HOTSPARE] = INIT_LED(0b010, 0, 0), [IBPI_PATTERN_PFA] = INIT_LED(0b010, 0, 0), [IBPI_PATTERN_FAILED_DRIVE] = INIT_LED(0b001, 0, 0), [IBPI_PATTERN_LOCATE] = INIT_LED(0b010, 0, 0b010), [IBPI_PATTERN_LOCATE_OFF] = INIT_LED(0, 0, 0b101), [99] = INIT_LED(0, 0, 0b101), }; static struct drive_leds tx_leds_blink_gen_b[] = { [IBPI_PATTERN_NORMAL] = INIT_LED(0, 0, 0b101), [IBPI_PATTERN_ONESHOT_NORMAL] = INIT_LED(0, 0, 0b101), [IBPI_PATTERN_REBUILD] = INIT_LED(0b110, 0, 0), [IBPI_PATTERN_HOTSPARE] = INIT_LED(0b110, 0, 0), [IBPI_PATTERN_PFA] = INIT_LED(0b110, 0, 0), [IBPI_PATTERN_FAILED_DRIVE] = INIT_LED(0b001, 0, 0), [IBPI_PATTERN_LOCATE] = INIT_LED(0b110, 0, 0b110), [IBPI_PATTERN_LOCATE_OFF] = INIT_LED(0, 0, 0b101), [99] = INIT_LED(0, 0, 0b101), }; #define CACHE_SZ 1024 int cache_fd = 0; struct cache_entry { struct drive_leds leds[4]; uint8_t blink_gen_a; uint8_t blink_gen_b; uint16_t reserved; } __attribute__ ((__packed__)); static struct cache_entry *sgpio_cache; static void _put_cache(void) { munmap(sgpio_cache, CACHE_SZ); if (cache_fd) { flock(cache_fd, LOCK_UN); fsync(cache_fd); close(cache_fd); cache_fd = 0; } } static int _open_and_map_cache(void) { struct stat sbuf; if (cache_fd) return 0; cache_fd = shm_open("/ledmon_amd_sgpio_cache", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); if (cache_fd < 1) { log_error("Couldn't open SGPIO cache: %s", strerror(errno)); return -1; } flock(cache_fd, LOCK_EX); fstat(cache_fd, &sbuf); if (sbuf.st_size == 0) { if (ftruncate(cache_fd, CACHE_SZ) != 0) { log_error("Couldn't truncate SGPIO cache: %s", strerror(errno)); return -1; } } sgpio_cache = mmap(NULL, CACHE_SZ, PROT_READ | PROT_WRITE, MAP_SHARED, cache_fd, 0); if (sgpio_cache == MAP_FAILED) { log_error("Couldn't map SGPIO cache: %s", strerror(errno)); _put_cache(); return -1; } return 0; } static struct cache_entry *_get_cache(struct amd_drive *drive) { int rc, index; rc = _open_and_map_cache(); if (rc) return NULL; /* The sgpio_cache is just an array of cache_entry structs, and * each cache_entry describes the drive LED settings for 4 drives. * To find the corresponding cache entry for an ata port we need * to first round down to the nearest multiple of 4, then divide * by four. This gives us the following mapping; * cache_entry[0] => drive 0 to drive 3 * cache_entry[1] => drive 4 to drive 7 * cache_entry[n] => drive (4*n) to drive (4*n + 3) */ index = (drive->ata_port / 4); return &sgpio_cache[index]; } static int _send_sgpio_register(const char *em_buffer_path, void *reg, int reg_len) { int count; int saved_errno; int retries = 3; do { int fd = open(em_buffer_path, O_WRONLY); if (fd < 0) { log_error("Couldn't open EM buffer %s: %s", em_buffer_path, strerror(errno)); return -1; } count = write(fd, reg, reg_len); saved_errno = errno; close(fd); /* Insert small sleep to ensure hardware has enough time to * see the register change and read it. Without the sleep * multiple writes can result in an EBUSY return because * hardware has not cleared the EM_CTL_TM (Transmit Message) * bit. */ usleep(1000); if (count == reg_len || saved_errno != EBUSY) break; } while (--retries != 0); if (count != reg_len) { log_error("Couldn't write SGPIO register: %s", strerror(saved_errno)); return -1; } return 0; } static sgpio_hdr_t _init_sgpio_hdr(int data_size, int msg_size) { sgpio_hdr_t hdr = 0; set_sgpio_hdr_msg_type(&hdr, SGPIO_HDR_MSG_TYPE_SGPIO); set_sgpio_hdr_data_size(&hdr, data_size); set_sgpio_hdr_msg_size(&hdr, msg_size); return hdr; } static void _dump_sgpio_hdr(const char *type, sgpio_hdr_t hdr) { log_debug("%s SGPIO Header: %08x\n", type, hdr); log_debug(REG_FMT_2, "message type", get_sgpio_hdr_msg_type(&hdr), "data size", get_sgpio_hdr_data_size(&hdr)); log_debug(REG_FMT_1, "message size", get_sgpio_hdr_msg_size(&hdr)); } static sgpio_req_t _init_sgpio_req(int frame_type, int function, int reg_type, int reg_index, int reg_count) { sgpio_req_t req = 0; set_sgpio_req_frame_type(&req, frame_type); set_sgpio_req_function(&req, function); set_sgpio_req_reg_type(&req, reg_type); set_sgpio_req_reg_index(&req, reg_index); set_sgpio_req_reg_count(&req, reg_count); return req; } static void _dump_sgpio_req(const char *type, sgpio_req_t req) { uint32_t *r = (uint32_t *)&req; log_debug("%s SGPIO Request Register: %08x %08x\n", type, r[0], r[1]); log_debug(REG_FMT_2, "frame type", get_sgpio_req_frame_type(&req), "function", get_sgpio_req_function(&req)); log_debug(REG_FMT_2, "register type", get_sgpio_req_reg_type(&req), "register index", get_sgpio_req_reg_index(&req)); log_debug(REG_FMT_1, "register count", get_sgpio_req_reg_count(&req)); } static sgpio_cfg_t _init_sgpio_cfg(int gpio_enable, int blink_a, int blink_b, int force_off, int max_on, int stretch_off, int stretch_on) { sgpio_cfg_t cfg = 0; if (gpio_enable) set_sgpio_cfg_gpio_enable(&cfg, 1); set_sgpio_cfg_blink_gen_a(&cfg, blink_a); set_sgpio_cfg_blink_gen_b(&cfg, blink_b); set_sgpio_cfg_max_on(&cfg, max_on); set_sgpio_cfg_force_off(&cfg, force_off); set_sgpio_cfg_stretch_on(&cfg, stretch_on); set_sgpio_cfg_stretch_off(&cfg, stretch_off); return cfg; } static void _dump_sgpio_cfg(const char *type, sgpio_cfg_t cfg) { uint32_t *r = (uint32_t *)&cfg; log_debug("%s SGPIO Configuration Register: %08x %08x\n", type, r[0], r[1]); log_debug(REG_FMT_2, "version", get_sgpio_cfg_version(&cfg), "gp register count", get_sgpio_cfg_gp_reg_count(&cfg)); log_debug(REG_FMT_2, "cfg register count", get_sgpio_cfg_cfg_reg_count(&cfg), "gpio enabled", get_sgpio_cfg_gpio_enable(&cfg)); log_debug(REG_FMT_2, "drive count", get_sgpio_cfg_drive_count(&cfg), "blink gen rate A", get_sgpio_cfg_blink_gen_a(&cfg)); log_debug(REG_FMT_2, "blink gen rate B", get_sgpio_cfg_blink_gen_b(&cfg), "force activity off", get_sgpio_cfg_force_off(&cfg)); log_debug(REG_FMT_2, "max activity on", get_sgpio_cfg_max_on(&cfg), "stretch activity off", get_sgpio_cfg_stretch_off(&cfg)); log_debug(REG_FMT_1, "stretch activity on", get_sgpio_cfg_stretch_on(&cfg)); } static sgpio_amd_t _init_sgpio_amd(int initiator, int polarity, int bypass, int normal) { sgpio_amd_t amd = 0; set_sgpio_amd_initiator(&amd, initiator); set_sgpio_amd_polarity_flip(&amd, polarity); set_sgpio_amd_bypass_enable(&amd, bypass); set_sgpio_amd_return_to_normal(&amd, normal); return amd; } static void _dump_sgpio_amd(const char *type, sgpio_amd_t amd) { log_debug("%s SGPIO AMD Register: %08x\n", type, amd); log_debug(REG_FMT_2, "initiator", get_sgpio_amd_initiator(&amd), "polarity", get_sgpio_amd_polarity_flip(&amd)); log_debug(REG_FMT_2, "bypass enable", get_sgpio_amd_bypass_enable(&amd), "return to normal", get_sgpio_amd_return_to_normal(&amd)); } static int _write_cfg_register(const char *em_buffer_path, struct cache_entry *cache, int ibpi) { struct config_register cfg_reg; cfg_reg.hdr = _init_sgpio_hdr(0, sizeof(cfg_reg)); cfg_reg.req = _init_sgpio_req(0x40, 0x82, SGPIO_REQ_REG_TYPE_CFG, 0, 2); if (cache->blink_gen_a) cache->blink_gen_b = ibpi_pattern[ibpi]; else cache->blink_gen_a = ibpi_pattern[ibpi]; cfg_reg.cfg = _init_sgpio_cfg(1, cache->blink_gen_a, cache->blink_gen_b, 2, 1, 0, 0); _dump_sgpio_hdr("CFG", cfg_reg.hdr); _dump_sgpio_req("CFG", cfg_reg.req); _dump_sgpio_cfg("CFG", cfg_reg.cfg); return _send_sgpio_register(em_buffer_path, &cfg_reg, sizeof(cfg_reg)); } static void _dump_sgpio_tx(const char *type, sgpio_tx_t tx) { int i; log_debug("%s SGPIO TX Register: %08x\n", type, tx); for (i = 0; i < 4; i++) { log_debug("\tdrive %d: error %x, locate %x, activity %x\n", i, get_error_led(&tx.drive[i]), get_locate_led(&tx.drive[i]), get_activity_led(&tx.drive[i])); } } static int _write_tx_register(const char *em_buffer_path, struct transmit_register *tx_reg) { tx_reg->hdr = _init_sgpio_hdr(0, sizeof(*tx_reg)); tx_reg->req = _init_sgpio_req(0x40, 0x82, SGPIO_REQ_REG_TYPE_TX, 0, 1); _dump_sgpio_hdr("TX", tx_reg->hdr); _dump_sgpio_req("TX", tx_reg->req); _dump_sgpio_tx("TX", tx_reg->tx); return _send_sgpio_register(em_buffer_path, tx_reg, sizeof(*tx_reg)); } static void _set_tx_drive_leds(struct transmit_register *tx_reg, struct cache_entry *cache, int drive_bay, int ibpi) { int i; struct drive_leds *leds; memset(&tx_reg->tx, 0, sizeof(tx_reg->tx)); if (cache->blink_gen_a) leds = &tx_leds_blink_gen_b[ibpi]; else leds = &tx_leds_blink_gen_a[ibpi]; cache->leds[drive_bay].error = leds->error; cache->leds[drive_bay].locate = leds->locate; cache->leds[drive_bay].activity = leds->activity; for (i = 0; i < 4; i++) { set_error_led(&tx_reg->tx.drive[i], cache->leds[i].error); set_locate_led(&tx_reg->tx.drive[i], cache->leds[i].locate); set_activity_led(&tx_reg->tx.drive[i], cache->leds[i].activity); } } static int _init_tx_drive_leds(struct transmit_register *tx_reg, struct cache_entry *cache) { int i; int init_done = 0; memset(tx_reg, 0, sizeof(*tx_reg)); for (i = 0; i < 4; i++) { if (cache->leds[i].error || cache->leds[i].locate || cache->leds[i].activity) continue; _set_tx_drive_leds(tx_reg, cache, i, 99); init_done = 1; } return init_done; } static int _write_amd_register(const char *em_buffer_path, struct amd_drive *drive) { struct amd_register amd_reg; amd_reg.hdr = _init_sgpio_hdr(0, sizeof(amd_reg)); amd_reg.req = _init_sgpio_req(0x40, 0x82, SGPIO_REQ_REG_TYPE_AMD, 0, 1); amd_reg.amd = _init_sgpio_amd(drive->initiator, 0, 1, 1); _dump_sgpio_hdr("AMD", amd_reg.hdr); _dump_sgpio_req("AMD", amd_reg.req); _dump_sgpio_amd("AMD", amd_reg.amd); return _send_sgpio_register(em_buffer_path, &amd_reg, sizeof(amd_reg)); } static int _get_amd_sgpio_drive(const char *start_path, struct amd_drive *drive) { char *a, *p; int found; char path[PATH_MAX]; char ata_dir[PATH_MAX]; /* Start the search at the ataXX directory */ strncpy(ata_dir, start_path, PATH_MAX); ata_dir[PATH_MAX - 1] = 0; a = p = strstr(ata_dir, "ata"); if (!p) { log_info("Couldn't find ata path for %s", start_path); return -1; } /* terminate the path after the ataXX/ part */ p = strchr(p, '/'); if (!p) return 1; *p = '\0'; /* skip past 'ata' to get the ata port number */ a += 3; drive->ata_port = strtoul(a, NULL, 10); found = _find_file_path(ata_dir, "port_no", path, PATH_MAX); if (!found) { log_info("Couldn't find 'port_no' for %s\n", ata_dir); return -1; } drive->port = get_int(path, -1, "port_no"); if (drive->port == -1) return -1; drive->drive_bay = 8 - drive->port; if (drive->drive_bay < 4) { drive->initiator = 1; } else { drive->drive_bay -= 4; drive->initiator = 0; } log_debug("AMD Drive: port %d, ata port %d, drive bay %d, initiator %d", drive->port, drive->ata_port, drive->drive_bay, drive->initiator); return 0; } static int _set_ibpi(struct block_device *device, enum ibpi_pattern ibpi) { int rc; struct amd_drive drive; struct transmit_register tx_reg; struct cache_entry *cache; struct cache_entry cache_dup; log_info("\n"); log_info("Setting %s...", ibpi2str(ibpi)); log_debug("\tdevice: ...%s", strstr(device->sysfs_path, "/ata")); log_debug("\tbuffer: ...%s", strstr(device->cntrl_path, "/ata")); /* Retrieve the port number and correlate that to the drive slot. * Port numbers 8..1 correspond to slot numbers 0..7. This is * further broken down (since we can only control 4 drive slots) * such that initiator = 1 for slots 0..3 and initiator = 0 for * slots 4..7, the slot value is reduced by 4 for slots 4..7 so * we can calculate the correct bits to set in the register for * that drive. */ rc = _get_amd_sgpio_drive(device->sysfs_path, &drive); if (rc) return rc; cache = _get_cache(&drive); if (!cache) return -EINVAL; /* Save copy of cache entry */ memcpy(&cache_dup, cache, sizeof(cache_dup)); rc = _write_amd_register(device->cntrl_path, &drive); if (rc) goto _set_ibpi_error; rc = _write_cfg_register(device->cntrl_path, cache, ibpi); if (rc) goto _set_ibpi_error; memset(&tx_reg, 0, sizeof(tx_reg)); _set_tx_drive_leds(&tx_reg, cache, drive.drive_bay, ibpi); rc = _write_tx_register(device->cntrl_path, &tx_reg); _set_ibpi_error: if (rc) { /* Restore saved cache entry */ memcpy(cache, &cache_dup, sizeof(*cache)); } _put_cache(); return rc; } static int _amd_sgpio_init_one(const char *path, struct amd_drive *drive, struct cache_entry *cache) { int rc, do_init; struct transmit_register tx_reg; do_init = _init_tx_drive_leds(&tx_reg, cache); if (!do_init) return 0; log_debug("Initializing host %d..%d:", drive->ata_port, drive->ata_port + 3); log_debug("\tbuffer: %s", strstr(path, "/ata")); rc = _write_amd_register(path, drive); if (rc) return rc; rc = _write_cfg_register(path, cache, IBPI_PATTERN_NONE); if (rc) return rc; return _write_tx_register(path, &tx_reg); } static int _amd_sgpio_init(const char *path) { int rc; char em_path[PATH_MAX+10]; /* 10 == strlen("/em_buffer") */ struct amd_drive drive; struct cache_entry *cache; struct cache_entry cache_dup; snprintf(em_path, PATH_MAX+10, "%s/em_buffer", path); rc = _get_amd_sgpio_drive(em_path, &drive); if (rc) { log_error("Couldn't find drive info for %s\n", em_path); return rc; } cache = _get_cache(&drive); if (!cache) { log_error("Couldn't retrieve cache"); return -1; } /* Save copy of cache entry */ memcpy(&cache_dup, cache, sizeof(cache_dup)); rc = _amd_sgpio_init_one(em_path, &drive, cache); if (rc) { log_error("SGPIO register init failed for bank %d, %s", drive.initiator, em_path); /* Restore saved cache entry */ memcpy(cache, &cache_dup, sizeof(*cache)); goto _init_amd_sgpio_err; } _put_cache(); /* AMD uses SGPIO registers to control drive LEDs in sets of 8 * drives. The initiator bit in the amd register controls which * set of four drives (0-3 or 4-7) the transmit register is * updating. * * When initializing the registers we want to do all 8 drives * so we need to reset the drive ata_port and initiator values. */ if (drive.initiator) { drive.ata_port -= 4; drive.initiator = 0; } else { drive.ata_port += 4; drive.initiator = 1; } cache = _get_cache(&drive); if (!cache) { log_error("Couldn't retrieve cache"); return -1; } /* Save copy of cache entry */ memcpy(&cache_dup, cache, sizeof(cache_dup)); rc = _amd_sgpio_init_one(em_path, &drive, cache); if (rc) { log_error("SGPIO register init failed for bank %d, %s", drive.initiator, em_path); /* Restore saved cache entry */ memcpy(cache, &cache_dup, sizeof(*cache)); } _init_amd_sgpio_err: _put_cache(); return rc; } int _amd_sgpio_em_enabled(const char *path) { char *p; int rc, found; uint32_t caps; char em_path[PATH_MAX]; /* Check that libahci module was loaded with ahci_em_messages=1 */ p = get_text("/sys/module/libahci/parameters", "ahci_em_messages"); if (!p || (p && *p == 'N')) { log_info("Kernel libahci module enclosure management messaging not enabled.\n"); if (p) free(p); return 0; } free(p); /* Find base path for enclosure management */ found = _find_file_path(path, "em_buffer", em_path, PATH_MAX); if (!found) { log_info("Couldn't find base EM path for %s\n", path); return 0; } /* Validate that enclosure management is supported */ p = get_text(em_path, "em_message_supported"); if (!p) { log_info("Couldn't get 'em_messages_supported' for %s", path); return 0; } if (strstr(p, "sgpio") == NULL) { log_info("SGPIO EM not supported for %s\n", path); free(p); return 0; } free(p); /* Verify host enclosure management capabilities */ p = get_text(em_path, "ahci_host_caps"); if (!p) { log_info("Couldn't read host capabilities for %s\n", path); return 0; } rc = sscanf(p, "%" SCNx32, &caps); free(p); if (rc <= 0) { log_info("Couldn't parse host capabilities for %s", path); return 0; } if (!(caps & HOST_CAP_EMS)) { log_info("EM not supported for %s", path); return 0; } rc = _amd_sgpio_init(em_path); return rc ? 0 : 1; } int _amd_sgpio_write(struct block_device *device, enum ibpi_pattern ibpi) { /* 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); if ((ibpi == IBPI_PATTERN_DEGRADED) || (ibpi == IBPI_PATTERN_FAILED_ARRAY)) __set_errno_and_return(ENOTSUP); return _set_ibpi(device, ibpi); } char *_amd_sgpio_get_path(const char *cntrl_path) { int len, found; char *em_buffer_path; char tmp[PATH_MAX]; em_buffer_path = malloc(PATH_MAX); if (!em_buffer_path) { log_error("Couldn't allocate memory to get path for %s\n%s", cntrl_path, strerror(errno)); return NULL; } found = _find_file_path(cntrl_path, "em_buffer", tmp, PATH_MAX); if (!found) { log_error("Couldn't find EM buffer for %s\n", cntrl_path); free(em_buffer_path); return NULL; } len = snprintf(em_buffer_path, PATH_MAX, "%s/em_buffer", tmp); if (len < 0 || len >= PATH_MAX) { free(em_buffer_path); return NULL; } return em_buffer_path; } ledmon-0.95/src/amd_sgpio.h000066400000000000000000000016571400033500100156220ustar00rootroot00000000000000/* * AMD SGPIO LED control * Copyright (C) 2019, Advanced Micro Devices, 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 _amd_sgpio_em_enabled(const char *path); int _amd_sgpio_write(struct block_device *device, enum ibpi_pattern ibpi); char *_amd_sgpio_get_path(const char *cntrl_path); ledmon-0.95/src/block.c000066400000000000000000000267711400033500100147510ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2021 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" #include "npem.h" #include "amd.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; } else if (cntrl->cntrl_type == CNTRL_TYPE_NPEM) { result = npem_write; } else if (cntrl->cntrl_type == CNTRL_TYPE_AMD) { result = amd_write; } return result; } static int do_not_flush(struct block_device *device __attribute__ ((unused))) { 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_host_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(cntrl->sysfs_path); else if (cntrl->cntrl_type == CNTRL_TYPE_VMD) result = vmdssd_get_path(cntrl->sysfs_path); else if (cntrl->cntrl_type == CNTRL_TYPE_NPEM) result = npem_get_path(cntrl->sysfs_path); else if (cntrl->cntrl_type == CNTRL_TYPE_AMD) result = amd_get_path(path, cntrl->sysfs_path); return result; } static int is_host_id_supported(const struct block_device *bd) { if (!bd->cntrl) return 0; switch (bd->cntrl->cntrl_type) { case CNTRL_TYPE_DELLSSD: case CNTRL_TYPE_VMD: case CNTRL_TYPE_NPEM: return 0; default: return 1; } } /** * @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; struct cntrl_device *non_npem_cntrl = NULL; list_for_each(cntrl_list, cntrl) { if (strncmp(cntrl->sysfs_path, path, strlen(cntrl->sysfs_path)) == 0) { if (cntrl->cntrl_type == CNTRL_TYPE_NPEM) return cntrl; non_npem_cntrl = cntrl; } } return non_npem_cntrl; } 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) { if (sscanf(host_name, "host%d", &host_id) != 1) host_id = -1; 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 = str_dup(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_debug("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 = str_dup(block->sysfs_path); result->cntrl_path = str_dup(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_host_id_supported(bd_old) && bd_old->host_id == -1) { log_debug("Device %s : No host_id!", strstr(bd_old->sysfs_path, "host")); return 0; } if (is_host_id_supported(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_NPEM: /* check controller to determine slot. */ i = (strcmp(bd_old->cntrl_path, bd_new->cntrl_path) == 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.95/src/block.h000066400000000000000000000165521400033500100147520ustar00rootroot00000000000000/* * 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.95/src/cntrl.c000066400000000000000000000244631400033500100147750ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2021 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" #include "amd.h" #include "npem.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", [CNTRL_TYPE_NPEM] = "NPEM", [CNTRL_TYPE_AMD] = "AMD", }; /** */ 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_cntrl(const char *path, const char *type) { char temp[PATH_MAX], link[PATH_MAX], *t; snprintf(temp, sizeof(temp), "%s/driver", path); if (realpath(temp, link) == NULL) return 0; t = strrchr(link, '/'); if ((t != NULL) && (strcmp(t + 1, type) != 0)) return 0; return 1; } static int _is_ahci_cntrl(const char *path) { return _is_cntrl(path, "ahci"); } static int _is_nvme_cntrl(const char *path) { return _is_cntrl(path, "nvme"); } static int _is_intel_ahci_cntrl(const char *path) { if (!_is_ahci_cntrl(path)) return 0; return get_uint64(path, 0, "vendor") == 0x8086L; } static int _is_amd_ahci_cntrl(const char *path) { if (!_is_ahci_cntrl(path)) return 0; return get_uint64(path, 0, "vendor") == 0x1022L; } static int _is_amd_nvme_cntrl(const char *path) { char tmp[PATH_MAX]; char *t; if (!_is_nvme_cntrl(path)) return 0; sprintf(tmp, "%s", path); t = strrchr(tmp, '/'); if (!t) return 0; t++; *t = '\0'; return get_uint64(tmp, 0, "vendor") == 0x1022L; } static int _is_amd_cntrl(const char *path) { if (_is_amd_ahci_cntrl(path)) return 1; if (_is_amd_nvme_cntrl(path)) return 1; return 0; } extern int get_dell_server_type(void); static int _is_dellssd_cntrl(const char *path) { uint64_t vdr, dev, svdr, cls; int gen = 0; 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"); if (cls == 0x10802) gen = get_dell_server_type(); return ((vdr == 0x1344L && dev == 0x5150L) || /* micron ssd */ (gen != 0) || /* Dell Server+NVME */ (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"); } static int _is_npem_cntrl(const char *path) { return is_npem_capable(path); } /** * @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_npem_cntrl(path)) { type = CNTRL_TYPE_NPEM; } else 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_intel_ahci_cntrl(path)) type = CNTRL_TYPE_AHCI; else if (_is_amd_cntrl(path)) type = CNTRL_TYPE_AMD; 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) { 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; } } } switch (type) { case CNTRL_TYPE_DELLSSD: case CNTRL_TYPE_SCSI: case CNTRL_TYPE_VMD: case CNTRL_TYPE_NPEM: em_enabled = 1; break; case CNTRL_TYPE_AHCI: em_enabled = _ahci_em_messages(path); break; case CNTRL_TYPE_AMD: em_enabled = amd_em_enabled(path); break; default: em_enabled = 0; } if (em_enabled) { 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 = str_dup(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.95/src/cntrl.h000066400000000000000000000062471400033500100150020ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2020 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, CNTRL_TYPE_NPEM, CNTRL_TYPE_AMD, }; /** * @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.95/src/config_file.c000066400000000000000000000161121400033500100161070ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * * Copyright (C) 2017-2021 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 #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, str_dup(s)); if (sep) s = sep + 1; else break; } } int _map_log_level(char *conf_log_level) { size_t 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) { if (sscanf(s, "%d", &conf.scan_interval) != 1 || 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_members_only = parse_bool(s); if (conf.raid_members_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(void) { 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)) fprintf(stdout, "Both whitelist and blacklist are specified - ignoring blacklist."); return STATUS_SUCCESS; } static char *conf_list_to_str(struct list *list) { char buf[BUFSIZ]; char *elem; memset(buf, 0, sizeof(buf)); list_for_each(list, elem) { if (elem) { int curr = strlen(buf); snprintf(buf + curr, sizeof(buf) - curr, "%s,", elem); } } return str_dup(buf); } int ledmon_write_shared_conf(void) { char buf[BUFSIZ]; char *whitelist = NULL; char *blacklist = NULL; void *shared_mem_ptr; int fd = shm_open(LEDMON_SHARE_MEM_FILE, O_RDWR | O_CREAT, 0644); if (fd == -1) return STATUS_FILE_OPEN_ERROR; if (ftruncate(fd, sizeof(buf)) != 0) { close(fd); return STATUS_FILE_WRITE_ERROR; } shared_mem_ptr = mmap(NULL, sizeof(buf), PROT_WRITE, MAP_SHARED, fd, 0); if (shared_mem_ptr == MAP_FAILED) { close(fd); return STATUS_FILE_WRITE_ERROR; } snprintf(buf, sizeof(buf), "BLINK_ON_INIT=%d\n", conf.blink_on_init); snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "BLINK_ON_MIGR=%d\n", conf.blink_on_migration); snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "LOG_LEVEL=%u\n", conf.log_level); snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "LOG_PATH=%s\n", conf.log_path); snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "RAID_MEMBERS_ONLY=%d\n", conf.raid_members_only); snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "REBUILD_BLINK_ON_ALL=%d\n", conf.rebuild_blink_on_all); snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "INTERVAL=%d\n", conf.scan_interval); whitelist = conf_list_to_str(&conf.cntrls_whitelist); if (whitelist) { snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "WHITELIST=%s\n", whitelist); free(whitelist); } blacklist = conf_list_to_str(&conf.cntrls_blacklist); if (blacklist) { snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "BLACKLIST=%s\n", blacklist); free(blacklist); } memcpy(shared_mem_ptr, buf, strlen(buf)); munmap(shared_mem_ptr, strlen(buf)); close(fd); return STATUS_SUCCESS; } int ledmon_remove_shared_conf(void) { return shm_unlink(LEDMON_SHARE_MEM_FILE); } ledmon-0.95/src/config_file.h000066400000000000000000000024701400033500100161160ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * * Copyright (C) 2017-2019 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_SHARE_MEM_FILE "/ledmon.conf" #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_members_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); int ledmon_write_shared_conf(void); int ledmon_remove_shared_conf(void); #endif /* SRC_CONFIG_FILE_H_ */ ledmon-0.95/src/dellssd.c000066400000000000000000000151131400033500100152750ustar00rootroot00000000000000/* * 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" #include "ipmi.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 DELL_OEM_NETFN 0x30 #define DELL_OEM_STORAGE_CMD 0xD5 #define DELL_OEM_STORAGE_GETDRVMAP_12G 0x07 #define DELL_OEM_STORAGE_SETDRVSTATUS_12G 0x04 #define DELL_OEM_STORAGE_GETDRVMAP_13G 0x17 #define DELL_OEM_STORAGE_SETDRVSTATUS_13G 0x14 #define DELL_OEM_STORAGE_GETDRVMAP_14G 0x37 #define DELL_OEM_STORAGE_SETDRVSTATUS_14G 0x34 #define APP_NETFN 0x06 #define APP_GET_SYSTEM_INFO 0x59 #define DELL_GET_IDRAC_INFO 0xDD enum { DELL_12G_MONOLITHIC = 0x10, DELL_12G_MODULAR = 0x11, DELL_13G_MONOLITHIC = 0x20, DELL_13G_MODULAR = 0x21, DELL_14G_MONOLITHIC = 0x30, DELL_14G_MODULAR = 0x31, }; int get_dell_server_type() { static int gen; uint8_t data[4], rdata[20]; int rc, rlen; /* Don't requery if we already know have ID */ if (gen) return gen; /* Get Dell Generation */ memset(data, 0, sizeof(data)); memset(rdata, 0, sizeof(rdata)); data[0] = 0x00; data[1] = DELL_GET_IDRAC_INFO; data[2] = 0x02; data[3] = 0x00; rc = ipmicmd(BMC_SA, 0, APP_NETFN, APP_GET_SYSTEM_INFO, 4, data, 20, &rlen, rdata); if (rc) { log_debug("Unable to issue IPMI command GetSystemInfo\n"); return 0; } switch (rdata[10]) { case DELL_12G_MONOLITHIC: case DELL_12G_MODULAR: case DELL_13G_MONOLITHIC: case DELL_13G_MODULAR: case DELL_14G_MONOLITHIC: case DELL_14G_MODULAR: gen = rdata[10]; return gen; default: log_debug("Unable to determine Dell Server type\n"); break; } return 0; } static int ipmi_setled(int b, int d, int f, int state) { uint8_t data[20], rdata[20]; int rc, rlen, bay = 0xFF, slot = 0xFF, devfn, gen = 0; /* Check if this is a supported Dell server */ gen = get_dell_server_type(); if (!gen) return 0; 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[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 */ switch (gen) { case DELL_12G_MONOLITHIC: case DELL_12G_MODULAR: data[1] = DELL_OEM_STORAGE_GETDRVMAP_12G; break; case DELL_13G_MONOLITHIC: case DELL_13G_MODULAR: data[1] = DELL_OEM_STORAGE_GETDRVMAP_13G; break; case DELL_14G_MONOLITHIC: case DELL_14G_MODULAR: data[1] = DELL_OEM_STORAGE_GETDRVMAP_14G; break; } 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) { log_error("Unable to determine bay/slot for device %.2x:%.2x.%x\n", b, d, f); return 0; } /* Set Bay:Slot to Mask */ memset(data, 0, sizeof(data)); memset(rdata, 0, sizeof(rdata)); data[0] = 0x00; /* set */ 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 */ switch (gen) { case DELL_12G_MONOLITHIC: case DELL_12G_MODULAR: data[1] = DELL_OEM_STORAGE_SETDRVSTATUS_12G; break; case DELL_13G_MONOLITHIC: case DELL_13G_MODULAR: data[1] = DELL_OEM_STORAGE_SETDRVSTATUS_13G; break; case DELL_14G_MONOLITHIC: case DELL_14G_MODULAR: data[1] = DELL_OEM_STORAGE_SETDRVSTATUS_14G; break; } rc = ipmicmd(BMC_SA, 0, DELL_OEM_NETFN, DELL_OEM_STORAGE_CMD, 20, data, 20, &rlen, rdata); if (rc) { log_error("Unable to issue SetDriveState for %.2x:%.2x.%x\n", b,d,f); } return 0; } char *dellssd_get_path(const char *cntrl_path) { return str_dup(cntrl_path); } int dellssd_write(struct block_device *device, enum ibpi_pattern ibpi) { unsigned 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.95/src/dellssd.h000066400000000000000000000016161400033500100153050ustar00rootroot00000000000000/* * 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 *cntrl_path); int get_dell_server_type(void); ledmon-0.95/src/enclosure.c000066400000000000000000000076161400033500100156530ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2021 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 #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. */ #define SAS_DEVICE "/sas_device" static uint64_t _get_sas_address(const char *path) { char *tmp = str_dup(path); char buf[PATH_MAX]; char *p, *s; p = strstr(tmp, "/expander"); if (p == NULL) goto out; s = strchr(p + 1, PATH_DELIM); if (s == NULL) goto out; *s = '\0'; snprintf(buf, sizeof(buf), "%s%s%s", tmp, SAS_DEVICE, p); free(tmp); return get_uint64(buf, 0, "sas_address"); out: free(tmp); return 0; } #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 *enclosure; int ret; int fd; if (!realpath(path, temp)) return NULL; enclosure = calloc(1, sizeof(struct enclosure_device)); if (enclosure == NULL) { ret = 1; goto out; } enclosure->sysfs_path = str_dup(temp); enclosure->sas_address = _get_sas_address(temp); enclosure->dev_path = _get_dev_sg(temp); fd = enclosure_open(enclosure); if (fd == -1) { ret = 1; goto out; } ret = ses_load_pages(fd, &enclosure->ses_pages); close(fd); if (ret) goto out; ret = ses_get_slots(&enclosure->ses_pages, &enclosure->slots, &enclosure->slots_count); out: if (ret) { log_warning("failed to initialize enclosure_device %s\n", path); enclosure_device_fini(enclosure); enclosure = NULL; } return enclosure; } /* * 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->slots); free(enclosure->sysfs_path); free(enclosure->dev_path); free(enclosure); } } int enclosure_open(const struct enclosure_device *enclosure) { int fd = -1; if (enclosure->dev_path) fd = open(enclosure->dev_path, O_RDWR); return fd; } ledmon-0.95/src/enclosure.h000066400000000000000000000051071400033500100156510ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2021 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; struct ses_slot *slots; int slots_count; }; /** * @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); int enclosure_open(const struct enclosure_device *enclosure); #endif /* _ENCLOSURE_H_INCLUDED_ */ ledmon-0.95/src/ibpi.h000066400000000000000000000075021400033500100145760ustar00rootroot00000000000000/* * 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, ibpi_pattern_count, }; extern const char *ibpi_str[ibpi_pattern_count]; #endif /* _IBPI_H_INCLUDED_ */ ledmon-0.95/src/ipmi.c000066400000000000000000000061051400033500100146020ustar00rootroot00000000000000/* * Generic IPMI Interface * 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 "utils.h" #include "ipmi.h" static int ipmi_open(void) { 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; } 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) log_info("too short..\n"); if (rc != 0 && errno != EMSGSIZE) { log_info("%s\n", strerror(errno)); goto end; } if (rcv.msg.data[0]) log_info("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; } ledmon-0.95/src/ipmi.h000066400000000000000000000015061400033500100146070ustar00rootroot00000000000000/* * Generic IPMI Interface * * 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. * */ #define BMC_SA 0x20 int ipmicmd(int sa, int lun, int netfn, int cmd, int datalen, void *data, int resplen, int *rlen, void *resp); ledmon-0.95/src/ledctl.c000066400000000000000000000546411400033500100151230ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2021 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 #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" /** * @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 %s %s\n" "Copyright (C) 2009-2021 Intel Corporation.\n"; /** * Internal variable of monitor service. It is used to help parse command line * short options. */ static char *shortopt; struct option *longopt; static int possible_params[] = { OPT_HELP, OPT_LOG, OPT_VERSION, OPT_LIST_CTRL, OPT_LISTED_ONLY, OPT_ALL, OPT_DEBUG, OPT_ERROR, OPT_INFO, OPT_QUIET, OPT_WARNING, OPT_LOG_LEVEL, }; static const int possible_params_size = sizeof(possible_params) / sizeof(possible_params[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 status __attribute__ ((unused)), void *ignore __attribute__ ((unused))) { 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, PACKAGE_VERSION, BUILD_LABEL); 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, PACKAGE_VERSION, BUILD_LABEL); 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."); print_opt("--log-level=VALUE", "-l VALUE", "Allows user to set ledctl verbose level in logs."); 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.", ibpi2str(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_local_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_local_list) { if (list_is_empty(ibpi_local_list) == 0) { struct ibpi_state *state; list_for_each(ibpi_local_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_local_list, enum ibpi_pattern ibpi) { struct ibpi_state *state; list_for_each(ibpi_local_list, state) { if (state->ibpi == ibpi) return state; } return NULL; } /** * @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] name path to block device. * * @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/%u:%u", 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.", ibpi2str(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[]) { status_t t_status, ret_status = STATUS_SUCCESS; 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) { log_error("%s - unknown pattern name.", p); return STATUS_INVALID_STATE; } if (*t == '{') { while ((t = argv[optind++]) != NULL) { if (*t == '}') break; t_status = _ibpi_state_add_block(state, t); if (t_status != STATUS_SUCCESS) ret_status = t_status; } } else { while (*(p = t) != '\0') { t = strchrnul(p, ','); if (*t != '\0') *(t++) = '\0'; t_status = _ibpi_state_add_block(state, p); if (t_status != STATUS_SUCCESS) ret_status = t_status; } } } } if (_ibpi_state_determine(&ibpi_list) != STATUS_SUCCESS) ret_status = STATUS_IBPI_DETERMINE_ERROR; return ret_status; } /** * @brief Command line parser - checks if command line input contains * options which don't require to run ledctl as root. * * The function parses options of ledctl application. * It handles option to print version and help. * * @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_non_root(int argc, char *argv[]) { int opt_index, opt = -1; status_t status = STATUS_SUCCESS; do { opt = getopt_long(argc, argv, shortopt, longopt, &opt_index); switch (opt) { case 'v': _ledctl_version(); exit(EXIT_SUCCESS); case 'h': _ledctl_help(); exit(EXIT_SUCCESS); case ':': case '?': return STATUS_CMDLINE_ERROR; } } while (opt >= 0); return status; } /** * @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; optind = 1; do { opt = getopt_long(argc, argv, shortopt, longopt, &opt_index); if (opt == -1) break; switch (opt) { int log_level; case 0: switch (get_option_id(longopt[opt_index].name)) { case OPT_LOG_LEVEL: log_level = get_option_id(optarg); if (log_level != -1) status = set_verbose_level(log_level); else status = STATUS_CMDLINE_ERROR; break; default: status = set_verbose_level( possible_params[opt_index]); } 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 Send IBPI pattern. * * This is internal function of ledctl utility. The function set a requested * ibpi_state for devices linked with this ibpi_state on ibpi_local_list. * For other devices IBPI_PATTERN_LOCATE_OFF might be set - depending on * listed_only parameter. Then it sends a LED control message to controller * to visualize the pattern. * * @param[in] ibpi_local_list pointer to list of ipbi_state. * * @return STATUS_SUCCESS if successful, otherwise STATUS_IBPI_DETERMINE_ERROR */ static status_t _ledctl_execute(struct list *ibpi_local_list) { struct ibpi_state *state; struct block_device *device; if (!listed_only) { list_for_each(sysfs_get_block_devices(), device) device->send_fn(device, IBPI_PATTERN_LOCATE_OFF); } list_for_each(ibpi_local_list, state) list_for_each(&state->block_list, device) { if (state->ibpi != device->ibpi) { log_debug("Mismatch detected for %s, ibpi state: %s, device state %s\n", device->sysfs_path, state->ibpi, device->ibpi); return STATUS_IBPI_DETERMINE_ERROR; } device->send_fn(device, device->ibpi); } list_for_each(sysfs_get_block_devices(), device) device->flush_fn(device); return STATUS_SUCCESS; } static status_t _read_shared_conf(void) { status_t status; char share_conf_path[PATH_MAX]; memset(share_conf_path, 0, sizeof(share_conf_path)); snprintf(share_conf_path, sizeof(share_conf_path), "/dev/shm%s", LEDMON_SHARE_MEM_FILE); status = ledmon_read_config(share_conf_path); return status; } 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; setup_options(&longopt, &shortopt, possible_params, possible_params_size); set_invocation_name(argv[0]); if (_cmdline_parse_non_root(argc, argv) != STATUS_SUCCESS) return STATUS_CMDLINE_ERROR; openlog(progname, LOG_PERROR, LOG_USER); if (geteuid() != 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); free(shortopt); free(longopt); status = _read_shared_conf(); if (status != STATUS_SUCCESS) return status; status = log_open(conf.log_path); if (status != STATUS_SUCCESS) return STATUS_LOG_FILE_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); } return _ledctl_execute(&ibpi_list); } ledmon-0.95/src/ledmon.c000066400000000000000000000650511400033500100151270ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2021 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 #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 "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 Boolean flag whether to run foreground or not. * * This flag is turned on with --foreground option. Primary use of this option * is to use it in systemd service file. */ static int foreground; /** * @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 %s %s\n" "Copyright (C) 2009-2021 Intel Corporation.\n"; /** * Internal variable of monitor service. It is used to help parse command line * short options. */ static char *shortopt; struct option *longopt; static int possible_params[] = { 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, OPT_FOREGROUND, }; static int possible_params_size = sizeof(possible_params) / sizeof(possible_params[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] program_name 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 *program_name) { sysfs_reset(); list_erase(&ledmon_block_list); log_close(); pidfile_remove(program_name); } /** * @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] arg Argument passed to on_exit(). * * @return The function does not return a value. */ static void _ledmon_status(int status, void *arg) { int log_level; char message[4096]; int ignore = *((int *)arg); if (ignore) return; if (status == STATUS_SUCCESS) log_level = LOG_LEVEL_INFO; else log_level = LOG_LEVEL_ERROR; snprintf(message, sizeof(message), "exit status is %s.", strstatus(status)); if (get_log_fd() >= 0) _log(log_level, message); else syslog(log_level_infos[log_level].priority, "%s", message); } /** * @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, PACKAGE_VERSION, BUILD_LABEL); 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, PACKAGE_VERSION, BUILD_LABEL); 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("--foreground", "", "Do not run as daemon."); 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 = str_dup(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) { errno = 0; conf.scan_interval = strtol(optarg, NULL, 10); if (errno != 0) { log_error("Cannot parse sleep interval"); return STATUS_CMDLINE_ERROR; } 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 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 log_level; case 0: switch (get_option_id(longopt[opt_index].name)) { case OPT_LOG_LEVEL: log_level = get_option_id(optarg); if (log_level != -1) status = set_verbose_level(log_level); else status = STATUS_CMDLINE_ERROR; break; case OPT_FOREGROUND: foreground = 1; break; default: status = set_verbose_level( possible_params[opt_index]); } 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 (terminate || !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) { /* * 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, ibpi2str(ibpi), ibpi2str(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 = str_dup(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, ibpi2str(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, ibpi2str(block->ibpi), ibpi2str(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_members_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; int ignore = 0; setup_options(&longopt, &shortopt, possible_params, possible_params_size); set_invocation_name(argv[0]); openlog(progname, LOG_PID | LOG_PERROR, LOG_DAEMON); if (on_exit(_ledmon_status, &ignore)) return STATUS_ONEXIT_ERROR; if (_cmdline_parse_non_daemonise(argc, argv) != STATUS_SUCCESS) return STATUS_CMDLINE_ERROR; if (geteuid() != 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; status = ledmon_read_config(ledmon_conf_path); if (status != STATUS_SUCCESS) return status; if (_cmdline_parse(argc, argv) != STATUS_SUCCESS) return STATUS_CMDLINE_ERROR; ledmon_write_shared_conf(); if (log_open(conf.log_path) != STATUS_SUCCESS) return STATUS_LOG_FILE_ERROR; free(shortopt); free(longopt); if (pidfile_check(progname, NULL) == 0) { log_warning("daemon is running..."); return STATUS_LEDMON_RUNNING; } if (!foreground) { pid_t pid = fork(); if (pid < 0) { log_debug("main(): fork() failed (errno=%d).", errno); exit(EXIT_FAILURE); } if (pid > 0) { ignore = 1; /* parent: don't print exit status */ 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); UNUSED(dup(t)); UNUSED(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(); } ledmon_remove_shared_conf(); stop_udev_monitor(); exit(EXIT_SUCCESS); } ledmon-0.95/src/list.c000066400000000000000000000035101400033500100146140ustar00rootroot00000000000000/* * 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.95/src/list.h000066400000000000000000000152221400033500100146240ustar00rootroot00000000000000/* * 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.95/src/npem.c000066400000000000000000000136371400033500100146130ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2019-2020 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 "config.h" #include "cntrl.h" #include "npem.h" #include "utils.h" #define PCI_EXT_CAP_ID_NPEM 0x29 /* Native PCIe Enclosure Management */ #define PCI_NPEM_CAP_REG 0x04 /* NPEM Capability Register */ #define PCI_NPEM_CTRL_REG 0x08 /* NPEM Control Register */ #define PCI_NPEM_STATUS_REG 0x0C /* NPEM Status Register */ /* NPEM Capable/Enable */ #define PCI_NPEM_CAP 0x001 /* NPEM OK Capable/Control */ #define PCI_NPEM_OK_CAP 0x004 /* NPEM Locate Capable/Control */ #define PCI_NPEM_LOCATE_CAP 0x008 /* NPEM Fail Capable/Control */ #define PCI_NPEM_FAIL_CAP 0x010 /* NPEM Rebuild Capable/Control */ #define PCI_NPEM_REBUILD_CAP 0x020 /* NPEM Predicted Failure Analysis Capable/Control */ #define PCI_NPEM_PFA_CAP 0x040 /* NPEM Hot Spare Capable/Control */ #define PCI_NPEM_HOT_SPARE_CAP 0x080 /* NPEM in a Critical Array Capable/Control */ #define PCI_NPEM_CRA_CAP 0x100 /* NPEM in a Failed Array Capable/Control */ #define PCI_NPEM_FA_CAP 0x200 /* NPEM reserved and enclosure specific */ #define PCI_NPEM_RESERVED ~0xfff #define PCI_NPEM_STATUS_CC 0x01 /* NPEM Command Completed */ const int ibpi_to_npem_capability[] = { [IBPI_PATTERN_NORMAL] = PCI_NPEM_OK_CAP, [IBPI_PATTERN_ONESHOT_NORMAL] = PCI_NPEM_OK_CAP, [IBPI_PATTERN_DEGRADED] = PCI_NPEM_CRA_CAP, [IBPI_PATTERN_HOTSPARE] = PCI_NPEM_HOT_SPARE_CAP, [IBPI_PATTERN_REBUILD] = PCI_NPEM_REBUILD_CAP, [IBPI_PATTERN_FAILED_ARRAY] = PCI_NPEM_FA_CAP, [IBPI_PATTERN_PFA] = PCI_NPEM_PFA_CAP, [IBPI_PATTERN_FAILED_DRIVE] = PCI_NPEM_FAIL_CAP, [IBPI_PATTERN_LOCATE] = PCI_NPEM_LOCATE_CAP, [IBPI_PATTERN_LOCATE_OFF] = PCI_NPEM_OK_CAP, }; static struct pci_access *get_pci_access() { struct pci_access *pacc; pacc = pci_alloc(); pci_init(pacc); return pacc; } static struct pci_dev *get_pci_dev(struct pci_access *pacc, const char *path) { unsigned int domain, bus, dev, fn; char *p = strrchr(path, '/'); if (!p) return NULL; if (sscanf(p + 1, "%x:%x:%x.%x", &domain, &bus, &dev, &fn) != 4) return NULL; return pci_get_dev(pacc, domain, bus, dev, fn); } static struct pci_cap *get_npem_cap(struct pci_dev *pdev) { return pci_find_cap(pdev, PCI_EXT_CAP_ID_NPEM, PCI_CAP_EXTENDED); } static u32 read_npem_register(struct pci_dev *pdev, int reg) { u32 val = 0; struct pci_cap *pcap = get_npem_cap(pdev); if (!pcap) return val; return pci_read_long(pdev, pcap->addr + reg); } static int write_npem_register(struct pci_dev *pdev, int reg, u32 val) { struct pci_cap *pcap = get_npem_cap(pdev); if (!pcap) return val; return pci_write_long(pdev, pcap->addr + reg, val); } int is_npem_capable(const char *path) { u8 val; struct pci_access *pacc = get_pci_access(); struct pci_dev *pdev; if (!pacc) return 0; pdev = get_pci_dev(pacc, path); if (!pdev) { pci_cleanup(pacc); return 0; } val = read_npem_register(pdev, PCI_NPEM_CAP_REG); pci_free_dev(pdev); pci_cleanup(pacc); return (val & PCI_NPEM_CAP); } static int npem_wait_command(struct pci_dev *pdev) { /* * Software must wait for an NPEM command to complete before issuing * the next NPEM command. However, if this bit is not set within * 1 second limit on command execution, software is permitted to repeat * the NPEM command or issue the next NPEM command. * PCIe r4.0 sec 7.9.20.4 * * Poll the status register until the Command Completed bit becomes set * or timeout is reached. */ time_t start, end; u32 reg; time(&start); end = start; while (difftime(start, end) < 1) { reg = read_npem_register(pdev, PCI_NPEM_STATUS_REG); if (reg & PCI_NPEM_STATUS_CC) { /* status register type is RW1C */ write_npem_register(pdev, PCI_NPEM_STATUS_REG, PCI_NPEM_STATUS_CC); return 0; } time(&end); } return 1; } int npem_write(struct block_device *device, enum ibpi_pattern ibpi) { struct cntrl_device *npem_cntrl = device->cntrl; struct pci_access *pacc = NULL; struct pci_dev *pdev = NULL; u32 reg; u32 val; int err = 0; if (ibpi == device->ibpi_prev) return 0; if ((ibpi < IBPI_PATTERN_NORMAL) || (ibpi > IBPI_PATTERN_LOCATE_OFF)) { err = -EINVAL; goto exit; } pacc = get_pci_access(); if (!pacc) { log_error("NPEM: Unable to initialize pci access for %s\n", npem_cntrl->sysfs_path); err = -ENOMEM; goto exit; } pdev = get_pci_dev(pacc, npem_cntrl->sysfs_path); if (!pdev) { log_error("NPEM: Unable to get pci device for %s\n", npem_cntrl->sysfs_path); err = -ENXIO; goto exit; } reg = read_npem_register(pdev, PCI_NPEM_CAP_REG); if ((reg & ibpi_to_npem_capability[ibpi]) == 0) { log_debug("NPEM: Controller %s doesn't support %s pattern\n", npem_cntrl->sysfs_path, ibpi_str[ibpi]); ibpi = IBPI_PATTERN_NORMAL; } reg = read_npem_register(pdev, PCI_NPEM_CTRL_REG); val = (reg & PCI_NPEM_RESERVED); val = (val | PCI_NPEM_CAP | ibpi_to_npem_capability[ibpi]); write_npem_register(pdev, PCI_NPEM_CTRL_REG, val); if (npem_wait_command(pdev)) { log_error("NPEM: Write timeout for %s\n", npem_cntrl->sysfs_path); err = -EAGAIN; } exit: if (pdev) pci_free_dev(pdev); if (pacc) pci_cleanup(pacc); return err; } char *npem_get_path(const char *cntrl_path) { return str_dup(cntrl_path); } ledmon-0.95/src/npem.h000066400000000000000000000020021400033500100146000ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2019-2020 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 NPEM_H_INCLUDED_ #define NPEM_H_INCLUDED_ #include "block.h" #include "ibpi.h" int is_npem_capable(const char *path); int npem_write(struct block_device *device, enum ibpi_pattern ibpi); char *npem_get_path(const char *cntrl_path); #endif // NPEM_H_INCLUDED_ ledmon-0.95/src/pci_slot.c000066400000000000000000000032371400033500100154630ustar00rootroot00000000000000/* * 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.95/src/pci_slot.h000066400000000000000000000042211400033500100154620ustar00rootroot00000000000000/* * 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.95/src/pidfile.c000066400000000000000000000045401400033500100152610ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2021 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 "config.h" #include "pidfile.h" #include "status.h" #include "utils.h" #define RUN_DIR "/var/run/" #define PID ".pid" /** */ status_t pidfile_create(const char *name) { char buf[PATH_MAX]; int fd, count; snprintf(buf, sizeof(buf), "%s/%s%s", RUN_DIR, name, PID); 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]; snprintf(buf, sizeof(buf), "%s/%s%s", RUN_DIR, name, PID); 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) { if (pid <= 0) return STATUS_INVALID_PATH; if (kill(pid, 1) == 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; snprintf(path, sizeof(path), "%s/%s%s", RUN_DIR, name, PID); p = buf_read(path); if (p == NULL) return STATUS_INVALID_PATH; tp = strtol(p, NULL, 10); if (pid) *pid = tp; free(p); return ping_proc(tp); } ledmon-0.95/src/pidfile.h000066400000000000000000000020411400033500100152600ustar00rootroot00000000000000/* * 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.95/src/raid.c000066400000000000000000000117041400033500100145640ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2019 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_INACTIVE || (type == DEVICE_TYPE_CONTAINER && state > RAID_STATE_CLEAR)) { device = malloc(sizeof(struct raid_device)); if (device) { device->sysfs_path = str_dup(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 = str_dup(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.95/src/raid.h000066400000000000000000000041661400033500100145750ustar00rootroot00000000000000/* * 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.95/src/scsi.c000066400000000000000000000070301400033500100146030ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2021 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 #if _HAVE_DMALLOC_H #include #endif #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 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; } int scsi_get_enclosure(struct block_device *device) { struct enclosure_device *encl; uint64_t addr; if (!device || !device->sysfs_path) return 0; addr = get_drive_sas_addr(device->sysfs_path); if (!addr) return 0; list_for_each(sysfs_get_enclosure_devices(), encl) { int i; for (i = 0; i < encl->slots_count; i++) { if (encl->slots[i].sas_addr == addr) { device->enclosure = encl; device->encl_index = device->enclosure->slots[i].index; return 1; } } } return 0; } /** */ int scsi_ses_write(struct block_device *device, enum ibpi_pattern ibpi) { 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); return ses_write_msg(ibpi, &device->enclosure->ses_pages, device->encl_index); } int scsi_ses_flush(struct block_device *device) { int ret; int fd; if (!device || !device->enclosure) return 1; if (!device->enclosure->ses_pages.changes) return 0; fd = enclosure_open(device->enclosure); if (fd == -1) return 1; ret = ses_send_diag(fd, &device->enclosure->ses_pages); close(fd); return ret; } char *scsi_get_host_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); } ledmon-0.95/src/scsi.h000066400000000000000000000042741400033500100146170ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2021 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 the sas host path of the device. * * This function returns a sysfs path to the sas host that the device * belongs to. * * @param[in] path Canonical sysfs path to block device. * * @return A sysfs path to host device associated with the given * block device if successful, otherwise NULL pointer. */ char *scsi_get_host_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.95/src/ses.c000066400000000000000000000267751400033500100144550ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2021 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 #include #include "ses.h" #include "utils.h" static int debug = 0; #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 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 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), (unsigned int) (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", (unsigned int)((sas[0] & 0x70) >> 4)); printf("\tSMP Initiator Port: 0x%01x\n", (unsigned int)((sas[2] & 2) >> 1)); printf("\tSTP Initiator Port: 0x%01x\n", (unsigned int)((sas[2] & 4) >> 2)); printf("\tSSP Initiator Port: 0x%01x\n", (unsigned int)((sas[2] & 8) >> 3)); printf("\tSATA DEVICE: 0x%01x\n", (unsigned int)(sas[3] & 1)); printf("\tSMP Target Port: 0x%01x\n", (unsigned int)((sas[3] & 2) >> 1)); printf("\tSTP Target Port: 0x%01x\n", (unsigned int)((sas[3] & 4) >> 2)); printf("\tSSP Target Port: 0x%01x\n", (unsigned int)((sas[3] & 8) >> 3)); printf("\tSATA Port Selector: 0x%01x\n", (unsigned int)((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", (unsigned int)(ai[0] & 0xf)); /* */ ai += len; } return; } int ses_load_pages(int fd, struct ses_pages *sp) { int ret; /* Read configuration. */ ret = get_ses_page(fd, &sp->page1, ENCL_CFG_DIAG_STATUS); if (ret) return ret; ret = process_page1(sp); if (ret) return ret; /* Get Enclosure Status */ ret = get_ses_page(fd, &sp->page2, ENCL_CTRL_DIAG_STATUS); if (ret) return ret; /* Additional Element Status */ ret = get_ses_page(fd, &sp->page10, ENCL_ADDITIONAL_EL_STATUS); if (ret) return ret; if (debug) print_page10(sp); return ret; } 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 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); } static int ses_set_message(enum ibpi_pattern ibpi, struct ses_slot_ctrl_elem *el) { struct ses_slot_ctrl_elem msg; memset(&msg, 0, sizeof(msg)); 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; } int ses_write_msg(enum ibpi_pattern ibpi, struct ses_pages *sp, int idx) { /* 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 local_element_type = SES_UNSPECIFIED; for (i = 0; i < sp->page1_types_len; i++) { const 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 (local_element_type < t->element_type && t->num_of_elements > idx) { local_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; sp->changes++; /* 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 (local_element_type != SES_ARRAY_DEVICE_SLOT) desc_element->array_slot_control = 0; return 0; } return 1; } int ses_send_diag(int fd, struct ses_pages *sp) { return sg_ll_send_diag(fd, 0, 1, 0, 0, 0, 0, sp->page2.buf, sp->page2.len, 0, debug); } int ses_get_slots(struct ses_pages *sp, struct ses_slot **out_slots, int *out_slots_count) { unsigned char *add_desc = NULL; unsigned char *ap = NULL, *addr_p = NULL; int i, j, len = 0; /* Check Page10 for address. Extract index. */ ap = add_desc = sp->page10.buf + 8; for (i = 0; i < sp->page1_types_len; i++) { const struct type_descriptor_header *t = &sp->page1_types[i]; if (t->element_type == SES_DEVICE_SLOT || t->element_type == SES_ARRAY_DEVICE_SLOT) { struct ses_slot *slots; slots = calloc(t->num_of_elements, sizeof(*slots)); if (!slots) return -1; for (j = 0; j < t->num_of_elements; j++, ap += len) { /* Get Additional Element Status Descriptor */ /* length (x-1) */ len = ap[1] + 2; if ((ap[0] & 0xf) != SCSI_PROTOCOL_SAS) { slots[j].index = -1; 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 */ slots[j].sas_addr = ((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]); slots[j].index = ap[0] & 0x10 ? ap[3] : j; } *out_slots = slots; *out_slots_count = t->num_of_elements; return 0; } } return 1; } ledmon-0.95/src/ses.h000066400000000000000000000036041400033500100144440ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2021 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 #include "ibpi.h" /* Size of buffer for SES-2 Messages. */ #define SES_ALLOC_BUFF 4096 typedef enum __attribute__((packed)) { SES_UNSPECIFIED = 0x00, SES_DEVICE_SLOT = 0x01, SES_ARRAY_DEVICE_SLOT = 0x17, } element_type; 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; const struct type_descriptor_header *page1_types; int page1_types_len; int changes; }; struct ses_slot_ctrl_elem { union { struct { __u8 common_control; __u8 array_slot_control; __u8 b2; __u8 b3; }; __u8 b[4]; }; }; struct ses_slot { int index; uint64_t sas_addr; }; int ses_load_pages(int fd, struct ses_pages *sp); int ses_write_msg(enum ibpi_pattern ibpi, struct ses_pages *sp, int idx); int ses_send_diag(int fd, struct ses_pages *sp); int ses_get_slots(struct ses_pages *sp, struct ses_slot **out_slots, int *out_slots_count); #endif ledmon-0.95/src/slave.c000066400000000000000000000062221400033500100147560ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2021 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 = strtol(p, NULL, 10); 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; snprintf(temp, sizeof(temp), "%s/block", path); 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.95/src/slave.h000066400000000000000000000026261400033500100147670ustar00rootroot00000000000000/* * 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.95/src/smp.c000066400000000000000000000374661400033500100144610ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2011-2019 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_EOF 4 #define LED_SOF 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[]; /* 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[]; /* variable length of data */ /* uint32_t crc; */ } __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) == NULL) { 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)", ibpi2str(ibpi), c); fprintf(stderr, "%s(): pattern %s not supported for device (/dev/%s)\n", __func__, ibpi2str(ibpi), c); } else { log_debug("pattern %s not supported for device %s", ibpi2str(ibpi), device->sysfs_path); fprintf(stderr, "%s(): pattern %s not supported for device\n\t(%s)\n", __func__, ibpi2str(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(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 = str_dup(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 */ if (sscanf(de->d_name, "phy-%d:%d", &host, &port) != 2) continue; break; } } closedir(d); free(path2); } init_smp(cntrl); return port; } ledmon-0.95/src/smp.h000066400000000000000000000072061400033500100144530ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2011-2021 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 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.95/src/status.c000066400000000000000000000044701400033500100151720ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2019 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); _S_CODE(STATUS_LOG_FILE_ERROR); default: return "???"; } } ledmon-0.95/src/status.h000066400000000000000000000036431400033500100152000ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2019 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, STATUS_LOG_FILE_ERROR, }; /** */ char *strstatus(status_t scode); #endif /* _STATUS_H_INCLUDED_ */ ledmon-0.95/src/sysfs.c000066400000000000000000000406431400033500100150200ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2019 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] == '/' || 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]; snprintf(temp, sizeof(temp), "%s/dev", path); 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 = strrchr(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 = strrchr(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; snprintf(temp, sizeof(temp), "%s/md", device->sysfs_path); 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_members_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, ibpi2str(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 *local_slave_list) { struct slave_device *device; list_for_each(local_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; snprintf(buf, sizeof(buf), "%s/driver", path); snprintf(driver_path, sizeof(driver_path), "/%s", driver); link = realpath(buf, NULL); if (link && strstr(link, driver_path)) found = 1; free(link); return found; } ledmon-0.95/src/sysfs.h000066400000000000000000000046701400033500100150250ustar00rootroot00000000000000/* * 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.95/src/udev.c000066400000000000000000000114311400033500100146050ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2017-2020 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(void) { if (udev_monitor) return udev_monitor_get_fd(udev_monitor); return create_udev_monitor(); } static int _check_raid(const char *path) { char *t = strrchr(path, '/'); if (t == NULL) return 0; return strncmp(t + 1, "md", 2) == 0; } static enum udev_action _get_udev_action(const char *action) { enum udev_action ret = UDEV_ACTION_UNKNOWN; if (strncmp(action, "add", 3) == 0) ret = UDEV_ACTION_ADD; else if (strncmp(action, "remove", 6) == 0) ret = UDEV_ACTION_REMOVE; return ret; } static void _clear_raid_dev_info(struct block_device *block, char *raid_dev) { if (block->raid_dev && block->raid_dev->sysfs_path) { char *tmp = strrchr(block->raid_dev->sysfs_path, '/'); if (tmp == NULL) { log_debug("Device: %s have wrong raid_dev path: %s", block->sysfs_path, block->raid_dev->sysfs_path); return; } if (strcmp(raid_dev, tmp + 1) == 0) { log_debug("CLEAR raid_dev %s in %s ", raid_dev, block->sysfs_path); raid_device_fini(block->raid_dev); block->raid_dev = NULL; } } } int handle_udev_event(struct list *ledmon_block_list) { struct udev_device *dev; int status = -1; dev = udev_monitor_receive_device(udev_monitor); if (dev) { const char *action = udev_device_get_action(dev); enum udev_action act = _get_udev_action(action); const char *syspath = udev_device_get_syspath(dev); struct block_device *block = NULL; if (act == UDEV_ACTION_UNKNOWN) { status = 1; goto exit; } list_for_each(ledmon_block_list, block) { if (_compare(block, syspath)) break; block = NULL; } if (!block) { if (act == UDEV_ACTION_REMOVE && _check_raid(syspath)) { /*ledmon is interested about removed arrays*/ char *dev_name; dev_name = strrchr(syspath, '/') + 1; log_debug("REMOVED %s", dev_name); list_for_each(ledmon_block_list, block) _clear_raid_dev_info(block, dev_name); status = 0; goto exit; } status = 1; goto exit; } if (act == UDEV_ACTION_ADD) { log_debug("ADDED %s", block->sysfs_path); if (block->ibpi == IBPI_PATTERN_FAILED_DRIVE || block->ibpi == IBPI_PATTERN_REMOVED || block->ibpi == IBPI_PATTERN_UNKNOWN) block->ibpi = IBPI_PATTERN_ADDED; } else if (act == UDEV_ACTION_REMOVE) { log_debug("REMOVED %s", block->sysfs_path); block->ibpi = IBPI_PATTERN_REMOVED; } else { /* not interesting event */ status = 1; goto exit; } status = 0; } else { return -1; } exit: udev_device_unref(dev); return status; } ledmon-0.95/src/udev.h000066400000000000000000000037271400033500100146230ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2017-2019 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" /** */ enum udev_action { UDEV_ACTION_UNKNOWN = 0, UDEV_ACTION_ADD, UDEV_ACTION_REMOVE }; /** * @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.95/src/utils.c000066400000000000000000000316361400033500100150130ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2021 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 "config.h" #include "list.h" #include "status.h" #include "utils.h" /** */ #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; struct log_level_info log_level_infos[] = { [LOG_LEVEL_DEBUG] = {PREFIX_DEBUG, LOG_DEBUG}, [LOG_LEVEL_WARNING] = {PREFIX_WARNING, LOG_WARNING}, [LOG_LEVEL_INFO] = {PREFIX_INFO, LOG_INFO}, [LOG_LEVEL_ERROR] = {PREFIX_ERROR, LOG_ERR} }; /* * 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]; snprintf(temp, sizeof(temp), "%s/%s", path, name); 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); uint64_t retval = defval; if (p) { if (sscanf(p, "%" SCNx64, &defval) == 1) retval = defval; free(p); } return retval; } /* * 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 = strtol(p, NULL, 10); 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; } /** */ ssize_t buf_write(const char *path, const char *buf) { int fd; ssize_t 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 = strtol(p, NULL, 10); did->minor = strtol(t, NULL, 10); } 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); } } /** */ int log_open(const char *path) { if (s_log) log_close(); s_log = fopen(path, "a"); if (s_log == NULL) return -1; return 0; } /** */ void log_close(void) { if (s_log) { fflush(s_log); fclose(s_log); s_log = NULL; } } /** */ void _log(enum log_level_enum loglevel, const char *buf, ...) { va_list vl; struct log_level_info *lli = &log_level_infos[loglevel]; if (s_log == NULL) log_open(conf.log_path); if (conf.log_level >= loglevel) { char msg[4096]; va_start(vl, buf); vsnprintf(msg, sizeof(msg), buf, vl); va_end(vl); if (s_log) { _log_timestamp(); fprintf(s_log, "%s", lli->prefix); fprintf(s_log, "%s\n", msg); fflush(s_log); } syslog(lli->priority, "%s", msg); } } /** * @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 = strrchr(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) { char *ret; if (!src) return NULL; ret = strdup(src); if (!ret) { log_error("Cannot duplicate string"); exit(EXIT_FAILURE); } return ret; } char *get_path_hostN(const char *path) { char *c = NULL, *s = NULL, *p = str_dup(path); if (!p) return NULL; c = strstr(p, "host"); if (!c) goto end; s = strchr(c, '/'); if (!s) goto end; *s = 0; s = str_dup(c); end: free(p); return s; } char *get_path_component_rev(const char *path, int index) { int i; char *c = NULL, *p = str_dup(path); char *result = NULL; for (i = 0; i <= index; i++) { if (c) *c = '\0'; c = strrchr(p, '/'); } if (c) result = str_dup(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 = str_dup(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", status); return 0; } status = regexec(®ex, string, 0, NULL, 0); regfree(®ex); if (status != 0) return 0; return 1; } int get_log_fd(void) { if (s_log) return fileno(s_log); return -1; } 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]; char log_file[PATH_MAX]; char *resolved, *logdir, *logfile, *cpath; /* * Extract directory from path */ cpath = str_dup(path); logdir = dirname(cpath); /* * Check if directory exists */ resolved = realpath(logdir, temp); if (resolved == NULL) { printf("%s: %s\n", strerror(errno), logdir); free(cpath); return STATUS_INVALID_PATH; } free(cpath); cpath = str_dup(path); logfile = basename(cpath); snprintf(log_file, sizeof(log_file), "%s/%s", resolved, logfile); free(cpath); if (conf.log_path) free(conf.log_path); conf.log_path = str_dup(log_file); return STATUS_SUCCESS; } /** * Internal array with option tokens. It is used to help parse command line * long options. */ struct option longopt_all[] = { [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'}, [OPT_LIST_CTRL] = {"list-controllers", no_argument, NULL, 'L'}, [OPT_LISTED_ONLY] = {"listed-only", no_argument, NULL, 'x'}, [OPT_FOREGROUND] = {"foreground", no_argument, NULL, '\0'}, [OPT_NULL_ELEMENT] = {NULL, no_argument, NULL, '\0'} }; void setup_options(struct option **_longopt, char **_shortopt, int *options, int options_nr) { struct option *longopt; char *shortopt; int i, j = 0; struct option *opt; longopt = malloc(sizeof(struct option) * (options_nr + 1)); shortopt = calloc(options_nr * 2 + 1, sizeof(char)); if (!longopt || !shortopt) { fprintf(stderr, "Out of memory\n"); exit(STATUS_OUT_OF_MEMORY); } for (i = 0; i < options_nr; i++) { opt = &longopt_all[options[i]]; longopt[i] = *opt; if (opt->val != '\0') { shortopt[j++] = (char) opt->val; if (opt->has_arg) shortopt[j++] = ':'; } } longopt[i] = longopt_all[OPT_NULL_ELEMENT]; shortopt[j] = '\0'; *_longopt = longopt; *_shortopt = shortopt; } /** * @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. * * @return integer id if successful, otherwise a -1. */ int get_option_id(const char *optarg) { int i = 0; while (longopt_all[i].name != NULL) { if (strcmp(longopt_all[i].name, optarg) == 0) return i; i++; } return -1; } /** * @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. */ 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; } const char *ibpi2str(enum ibpi_pattern ibpi) { static char buf[20]; const char *ret; if (ibpi >= 0 && ibpi < ibpi_pattern_count) ret = ibpi_str[ibpi]; else ret = NULL; if (!ret) { snprintf(buf, sizeof(buf), "(unknown: %u)", ibpi); ret = buf; } return ret; } ledmon-0.95/src/utils.h000066400000000000000000000270751400033500100150220ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (C) 2009-2019 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 #include "config_file.h" #include "stdlib.h" #include "stdint.h" #include "list.h" #include "status.h" #include "syslog.h" #include "ibpi.h" /** * Value is intentionally unused. * Macro to prevent compiler from raising unused result warning. */ #define UNUSED(x) do { if (x); } while (0) /** * 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; }; struct log_level_info { char prefix[10]; int priority; }; /** */ #define PREFIX_DEBUG " DEBUG: " #define PREFIX_WARNING "WARNING: " #define PREFIX_INFO " INFO: " #define PREFIX_ERROR " ERROR: " /** * This array describes a log levels priorities with message prefix . */ extern struct log_level_info log_level_infos[]; /** * 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. */ ssize_t 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 message with given loglevel. * * The function logs a message at given level of verbosity. * * @param[in] loglevel Level of verbosity for a message. * @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(enum log_level_enum loglevel, const char *buf, ...); #define log_error(buf, ...) _log(LOG_LEVEL_ERROR, buf, ##__VA_ARGS__) #define log_debug(buf, ...) _log(LOG_LEVEL_DEBUG, buf, ##__VA_ARGS__) #define log_info(buf, ...) _log(LOG_LEVEL_INFO, buf, ##__VA_ARGS__) #define log_warning(buf, ...) _log(LOG_LEVEL_WARNING, buf, ##__VA_ARGS__) /** */ 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); /** */ 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); /** * Internal enumeration type. It is used to help parse command line arguments. */ enum opt { 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, OPT_LIST_CTRL, OPT_LISTED_ONLY, OPT_FOREGROUND, OPT_NULL_ELEMENT }; extern struct option longopt_all[]; void setup_options(struct option **longopt, char **shortopt, int *options, int options_nr); int get_option_id(const char *optarg); status_t set_verbose_level(int log_level); const char *ibpi2str(enum ibpi_pattern ibpi); #endif /* _UTILS_H_INCLUDED_ */ ledmon-0.95/src/vmdssd.c000066400000000000000000000100751400033500100151450ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (c) 2016-2019, 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 0xF /* (1111) Attention Off, Power Off */ #define ATTENTION_LOCATE 0x7 /* (0111) Attention Off, Power On */ #define ATTENTION_REBUILD 0x5 /* (0101) Attention On, Power On */ #define ATTENTION_FAILURE 0xD /* (1101) 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 = str_dup(path); 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 = str_dup(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); if (realpath(module_path, real_module_path) == NULL) return -1; 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) != (ssize_t) 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 *cntrl_path) { return str_dup(cntrl_path); } ledmon-0.95/src/vmdssd.h000066400000000000000000000017701400033500100151540ustar00rootroot00000000000000/* * Intel(R) Enclosure LED Utilities * Copyright (c) 2016-2019, 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 *cntrl_path); struct pci_slot *vmdssd_find_pci_slot(char *device_path); #endif ledmon-0.95/systemd/000077500000000000000000000000001400033500100143775ustar00rootroot00000000000000ledmon-0.95/systemd/Makefile.am000066400000000000000000000020021400033500100164250ustar00rootroot00000000000000# # Intel(R) Enclosure LED Utilities # Copyright (C) 2009-2019 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 of ledmon systemd service unit. systemddir = @SYSTEMD_PATH@ SED = sed CLEANFILES = ledmon.service ledmon.service.tmp systemd_DATA = ledmon.service ledmon.service : ledmon.service.in $(SED) -e 's|@sbindir[@]|$(sbindir)|g' < $< > $@.tmp mv $@.tmp $@ ledmon-0.95/systemd/ledmon.service.in000066400000000000000000000002551400033500100176460ustar00rootroot00000000000000[Unit] Description=Enclosure LED Utilities [Install] WantedBy=multi-user.target [Service] Type=simple User=root ExecStart=@sbindir@/ledmon --foreground Restart=on-failure