diff --git a/CHANGELOG.md b/CHANGELOG.md index ea68648..7a82bac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,26 @@ +### 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) diff --git a/Makefile.am b/Makefile.am index 63bf8a3..d89b16a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,6 @@ # # Intel(R) Enclosure LED Utilities -# Copyright (C) 2009-2019 Intel Corporation. +# 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, @@ -22,4 +22,4 @@ endif SUBDIRS = doc src $(OPTIONAL_SUBDIR) EXTRA_DIST = config/config.h systemd/ledmon.service.in -dist_doc_DATA = README +dist_doc_DATA = README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..d2684c0 --- /dev/null +++ b/README.md @@ -0,0 +1,63 @@ +# 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. diff --git a/configure.ac b/configure.ac index 12014f0..8fd5e6b 100644 --- a/configure.ac +++ b/configure.ac @@ -1,11 +1,15 @@ AC_PREREQ([2.69]) -AC_INIT([ledmon], [0.94]) +AC_INIT([ledmon], [0.95]) AC_CONFIG_MACRO_DIR([m4]) -AC_SUBST([PACKAGE_DATE], "February 2020") +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 @@ -26,6 +30,7 @@ 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])]) @@ -61,13 +66,10 @@ AC_CHECK_FUNCS([ftruncate memset munmap realpath regcomp select strcasecmp strch # configure options AC_ARG_ENABLE(systemd, AS_HELP_STRING([--enable-systemd], [install ledmon systemd service])) -AC_ARG_ENABLE(testconfig, AS_HELP_STRING([--enable-testconfig], [build test_config tool])) AS_IF([test "x$enable_systemd" = xyes], [SYSTEMD_STR=yes], [SYSTEMD_STR=no]) -AS_IF([test "x$enable_testconfig" = xyes], [TESTCONFIG_STR=yes], [TESTCONFIG_STR=no]) AM_CONDITIONAL([SYSTEMD_CONDITION], [test "$SYSTEMD_STR" = yes]) -AM_CONDITIONAL([TESTCONFIG_CONDITION], [test "$TESTCONFIG_STR" = yes]) # target directory for ledmon service file AC_SUBST([SYSTEMD_PATH], "$(pkg-config systemd --variable=systemdsystemunitdir)") @@ -85,5 +87,4 @@ $PACKAGE_NAME $VERSION configuration: C compiler flags: ${AM_CFLAGS} ${CFLAGS} Common install location: ${prefix} configure parameters: --enable-systemd=${SYSTEMD_STR} - --enable-testconfig=${TESTCONFIG_STR} ]) diff --git a/doc/Makefile.am b/doc/Makefile.am index 0257062..f894cf5 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -1,6 +1,6 @@ # # Intel(R) Enclosure LED Utilities -# Copyright (C) 2009-2019 Intel Corporation. +# 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, @@ -12,24 +12,24 @@ # 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., +# this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. # -CLEANFILES = ledmon.conf.5.gz ledmon.8.gz ledctl.8.gz +CLEANFILES = ledmon.conf.gz ledmon.gz ledctl.gz EXTRA_DIST = ledmon.conf.pod ledmon.pod ledctl.pod -dist_man5_MANS = ledmon.conf.5.gz -dist_man8_MANS = ledmon.8.gz ledctl.8.gz +dist_man5_MANS = ledmon.conf.gz +dist_man8_MANS = ledmon.gz ledctl.gz -ledmon.conf.5.gz: ledmon.conf.pod +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.8.gz: ledmon.pod +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.8.gz: ledctl.pod +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 > $@ diff --git a/doc/ledctl.pod b/doc/ledctl.pod index 61cfc4b..337280c 100644 --- a/doc/ledctl.pod +++ b/doc/ledctl.pod @@ -1,6 +1,6 @@ # # Intel(R) Enclosure LED Utilities -# Copyright (C) 2009-2020 Intel Corporation. +# 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, @@ -12,7 +12,7 @@ # 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., +# this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. # =head1 NAME @@ -32,14 +32,39 @@ User must have root privileges to use this application. There are two types of systems: 2-LEDs systems (Activity LED, Status LED) and 3-LEDs systems (Activity LED, Locate LED, Fail LED). -The ledctl application uses SGPIO and SES-2 protocol to control LEDs. -The program implements IBPI patterns of SFF-8489 specification for SGPIO. -Please note some enclosures do not stick close to SFF-8489 specification. -It might happen that enclosure's processor will accept an IBPI pattern but it will -blink the LEDs at variance with SFF-8489 specification or it has limited number -of patterns supported. -LED management (AHCI) and S protocols are not supported. +The ledctl application 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). @@ -53,6 +78,47 @@ It means that some patterns set by ledctl may have no effect if ledmon is runnin 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 @@ -187,7 +253,7 @@ translation is being done. =item B -I is translated to I +I is translated to I =item B @@ -314,7 +380,7 @@ example uses the first format of device list. =head1 LICENSE -Copyright (c) 2009-2017 Intel Corporation. +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 diff --git a/doc/ledmon.pod b/doc/ledmon.pod index 17f27b3..3b40492 100644 --- a/doc/ledmon.pod +++ b/doc/ledmon.pod @@ -1,6 +1,6 @@ # # Intel(R) Enclosure LED Utilities -# Copyright (C) 2009-2019 Intel Corporation. +# 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, @@ -12,7 +12,7 @@ # 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., +# this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. # =head1 NAME @@ -33,14 +33,41 @@ Status LED) and 3-LEDs system (Activity LED, Locate LED, Fail LED). This application has the highest priority when accessing the LEDs. -The ledmon application uses SGPIO and SES-2 protocol to control -LEDs. The program implements IBPI patterns of SFF-8489 specification -for SGPIO. Please note some enclosures do not stick close to SFF-8489 -specification. It might happen that enclosure processor will accept -IBPI pattern but it will blink LEDs not according to SFF-8489 -specification or it has limited number of patterns supported. +The ledmon application supports LED management of the SAS/SATA and PCIe +storages. -LED management (AHCI) and SAF-TE protocols are not supported. +=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 @@ -55,6 +82,46 @@ 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 @@ -116,7 +183,7 @@ switch. =head1 LICENSE -Copyright (c) 2009-2017 Intel Corporation. +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 diff --git a/m4/m4_check_if_prog_installed.m4 b/m4/m4_check_if_prog_installed.m4 new file mode 100644 index 0000000..cbc964b --- /dev/null +++ b/m4/m4_check_if_prog_installed.m4 @@ -0,0 +1,23 @@ +# 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.]) + ]) +]) \ No newline at end of file diff --git a/src/Makefile.am b/src/Makefile.am index c6b7ea0..3d78d30 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,6 @@ # # Intel(R) Enclosure LED Utilities -# Copyright (C) 2009-2020 Intel Corporation. +# 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, @@ -19,15 +19,14 @@ 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\ + 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 version.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) -TEST_CONFIG_SRCS = config_file.c list.c utils.c sbin_PROGRAMS = ledmon ledctl @@ -39,11 +38,3 @@ ledctl_LDADD = $(LIBPCI_LIBS) $(LIBUDEV_LIBS) ledmon_CFLAGS = $(AM_CFLAGS) $(LIBPCI_CFLAGS) $(LIBUDEV_CFLAGS) ledctl_CFLAGS = $(AM_CFLAGS) $(LIBPCI_CFLAGS) $(LIBUDEV_CFLAGS) -if TESTCONFIG_CONDITION - -noinst_PROGRAMS = test_config -test_config_SOURCES = $(TEST_CONFIG_SRCS) -test_config_CPPFLAGS = $(AM_CPPFLAGS) -D_TEST_CONFIG - -endif - diff --git a/src/amd.c b/src/amd.c index d66c917..85f3a9a 100644 --- a/src/amd.c +++ b/src/amd.c @@ -1,6 +1,6 @@ /* * AMD LED control - * Copyright (C) 2019, Advanced Micro Devices, Inc. + * 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, @@ -45,7 +45,7 @@ #include "amd_ipmi.h" enum amd_led_interfaces amd_interface = AMD_INTF_UNSET; -enum amd_platforms amd_platform = AMD_PLATFORM_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) @@ -96,37 +96,32 @@ int _find_file_path(const char *start_path, const char *filename, return found; } -static void _get_amd_led_interface(void) +/* 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 *name; + char *platform; + int rc; - name = get_text("/sys/class/dmi/id", "product_name"); - if (!name) - return; + /* Default to SGPIO interface */ + amd_interface = AMD_INTF_SGPIO; - if (!strncmp(name, "ETHANOL_X", 9)) { + 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_platform = AMD_PLATFORM_ETHANOL_X; - } else if (!strncmp(name, "DAYTONA_X", 9)) { + amd_ipmi_platform = AMD_PLATFORM_ETHANOL_X; + } else if (!strncmp(platform, "DAYTONA_X", 9)) { amd_interface = AMD_INTF_IPMI; - amd_platform = AMD_PLATFORM_DAYTONA_X; - } else if (!strncmp(name, "GRANDSTAND", 10)) { - amd_interface = AMD_INTF_SGPIO; - amd_platform = AMD_PLATFORM_GRANDSTAND; - } else if (!strncmp(name, "Speedway", 8)) { - amd_interface = AMD_INTF_SGPIO; - amd_platform = AMD_PLATFORM_SPEEDWAY; + amd_ipmi_platform = AMD_PLATFORM_DAYTONA_X; } - free(name); -} - -int amd_em_enabled(const char *path) -{ - int rc; - - _get_amd_led_interface(); - switch (amd_interface) { case AMD_INTF_SGPIO: rc = _amd_sgpio_em_enabled(path); @@ -135,7 +130,8 @@ int amd_em_enabled(const char *path) rc = _amd_ipmi_em_enabled(path); break; default: - log_error("Unsupported AMD interface\n"); + log_error("Unknown interface for AMD %s platform\n", + platform); rc = -EOPNOTSUPP; break; } diff --git a/src/amd.h b/src/amd.h index 8bdb3a9..8f0ce12 100644 --- a/src/amd.h +++ b/src/amd.h @@ -1,6 +1,6 @@ /* * AMD LED control - * Copyright (C) 2019, Advanced Micro Devices, Inc. + * 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, @@ -39,15 +39,13 @@ enum amd_led_interfaces { extern enum amd_led_interfaces amd_interface; -enum amd_platforms { +enum amd_ipmi_platforms { AMD_PLATFORM_UNSET, AMD_PLATFORM_ETHANOL_X, AMD_PLATFORM_DAYTONA_X, - AMD_PLATFORM_GRANDSTAND, - AMD_PLATFORM_SPEEDWAY, }; -extern enum amd_platforms amd_platform; +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); diff --git a/src/amd_ipmi.c b/src/amd_ipmi.c index 6a5b28c..25bef40 100644 --- a/src/amd_ipmi.c +++ b/src/amd_ipmi.c @@ -1,6 +1,6 @@ /* * AMD IPMI LED control - * Copyright (C) 2019, Advanced Micro Devices, Inc. + * 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, @@ -105,7 +105,7 @@ static int _get_ipmi_nvme_port(char *path) /* Some platfroms require an adjustment to the port value based * on how they are numbered by the BIOS. */ - switch (amd_platform) { + switch (amd_ipmi_platform) { case AMD_PLATFORM_DAYTONA_X: port -= 2; break; @@ -202,7 +202,7 @@ static int _ipmi_platform_channel(struct amd_drive *drive) { int rc = 0; - switch (amd_platform) { + switch (amd_ipmi_platform) { case AMD_PLATFORM_ETHANOL_X: drive->channel = 0xd; break; @@ -222,7 +222,7 @@ static int _ipmi_platform_slave_address(struct amd_drive *drive) { int rc = 0; - switch (amd_platform) { + switch (amd_ipmi_platform) { case AMD_PLATFORM_ETHANOL_X: drive->slave_addr = 0xc0; break; @@ -375,12 +375,12 @@ int _amd_ipmi_em_enabled(const char *path) &data_sz, &status); if (rc) { - log_error("Can't determine MG9098 Status\n"); + log_error("Can't determine MG9098 Status for AMD platform\n"); return 0; } if (status != 98) { - log_error("Not a MG9098\n"); + log_error("Platform %s does not have a MG9098 controller\n"); return 0; } diff --git a/src/block.c b/src/block.c index e4f5b8c..2174acb 100644 --- a/src/block.c +++ b/src/block.c @@ -1,6 +1,6 @@ /* * Intel(R) Enclosure LED Utilities - * Copyright (C) 2009-2020 Intel Corporation. + * 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, @@ -141,7 +141,7 @@ static char *_get_host(char *path, struct cntrl_device *cntrl) char *result = NULL; if (cntrl->cntrl_type == CNTRL_TYPE_SCSI) - result = scsi_get_slot_path(path, cntrl->sysfs_path); + 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) diff --git a/src/cntrl.c b/src/cntrl.c index 409e345..6b73dac 100644 --- a/src/cntrl.c +++ b/src/cntrl.c @@ -1,6 +1,6 @@ /* * Intel(R) Enclosure LED Utilities - * Copyright (C) 2009-2020 Intel Corporation. + * 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, @@ -395,6 +395,29 @@ struct cntrl_device *cntrl_device_init(const char *path) 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: @@ -412,29 +435,6 @@ struct cntrl_device *cntrl_device_init(const char *path) em_enabled = 0; } if (em_enabled) { - if (!list_is_empty(&conf.cntrls_whitelist)) { - char *cntrl = NULL; - - list_for_each(&conf.cntrls_whitelist, cntrl) { - if (match_string(cntrl, path)) - break; - cntrl = NULL; - } - if (!cntrl) { - log_debug("%s not found on whitelist, ignoring", path); - return NULL; - } - } else if (!list_is_empty(&conf.cntrls_blacklist)) { - char *cntrl; - - list_for_each(&conf.cntrls_blacklist, cntrl) { - if (match_string(cntrl, path)) { - log_debug("%s found on blacklist, ignoring", - path); - return NULL; - } - } - } device = malloc(sizeof(struct cntrl_device)); if (device) { if (type == CNTRL_TYPE_SCSI) { diff --git a/src/config_file.c b/src/config_file.c index 7141905..3af0c92 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -1,7 +1,7 @@ /* * Intel(R) Enclosure LED Utilities * - * Copyright (C) 2017-2019 Intel Corporation. + * Copyright (C) 2017-2021 Intel Corporation. * Copyright (C) 2009 Karel Zak * * SPDX-License-Identifier: GPL-2.0 @@ -311,49 +311,3 @@ int ledmon_remove_shared_conf(void) return shm_unlink(LEDMON_SHARE_MEM_FILE); } -#ifdef _TEST_CONFIG -/* - * usage: ledmon_conf_test [] - */ - -int main(int argc, char *argv[]) -{ - char *filename = NULL; - char *s; - - if (argc == 2) - filename = argv[1]; - - if (ledmon_read_config(filename) != STATUS_SUCCESS) - return EXIT_FAILURE; - - printf("INTERVAL: %d\n", conf.scan_interval); - printf("LOG_LEVEL: %d\n", conf.log_level); - printf("LOG_PATH: %s\n", conf.log_path); - printf("BLINK_ON_MIGR: %d\n", conf.blink_on_migration); - printf("BLINK_ON_INIT: %d\n", conf.blink_on_init); - printf("REBUILD_BLINK_ON_ALL: %d\n", conf.rebuild_blink_on_all); - printf("RAID_MEMBERS_ONLY: %d\n", conf.raid_members_only); - - if (list_is_empty(&conf.cntrls_whitelist)) - printf("WHITELIST: NONE\n"); - else { - printf("WHITELIST: "); - list_for_each(&conf.cntrls_whitelist, s) - printf("%s, ", s); - printf("\n"); - } - - if (list_is_empty(&conf.cntrls_blacklist)) - printf("BLACKLIST: NONE\n"); - else { - printf("BLACKLIST: "); - list_for_each(&conf.cntrls_blacklist, s) - printf("%s, ", s); - printf("\n"); - } - - ledmon_free_config(); - return EXIT_SUCCESS; -} -#endif diff --git a/src/enclosure.c b/src/enclosure.c index 25b7ec2..14c892f 100644 --- a/src/enclosure.c +++ b/src/enclosure.c @@ -1,6 +1,6 @@ /* * Intel(R) Enclosure LED Utilities - * Copyright (C) 2009-2019 Intel Corporation. + * 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, @@ -18,10 +18,12 @@ */ #include +#include #include #include #include #include +#include #include #if _HAVE_DMALLOC_H @@ -109,17 +111,42 @@ static char *_get_dev_sg(const char *encl_path) struct enclosure_device *enclosure_device_init(const char *path) { char temp[PATH_MAX]; - struct enclosure_device *result = NULL; - - if (realpath(path, temp)) { - result = calloc(1, sizeof(struct enclosure_device)); - if (result == NULL) - return NULL; - result->sysfs_path = str_dup(temp); - result->sas_address = _get_sas_address(temp); - result->dev_path = _get_dev_sg(temp); + 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 result; + return enclosure; } /* @@ -129,8 +156,19 @@ struct enclosure_device *enclosure_device_init(const char *path) 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; +} diff --git a/src/enclosure.h b/src/enclosure.h index 5dcdb57..6312a97 100644 --- a/src/enclosure.h +++ b/src/enclosure.h @@ -1,6 +1,6 @@ /* * Intel(R) Enclosure LED Utilities - * Copyright (C) 2009-2018 Intel Corporation. + * 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, @@ -47,7 +47,10 @@ struct enclosure_device { */ char *dev_path; - struct ses_pages *ses_pages; + struct ses_pages ses_pages; + + struct ses_slot *slots; + int slots_count; }; /** @@ -78,4 +81,6 @@ struct enclosure_device *enclosure_device_init(const char *path); */ void enclosure_device_fini(struct enclosure_device *enclosure); +int enclosure_open(const struct enclosure_device *enclosure); + #endif /* _ENCLOSURE_H_INCLUDED_ */ diff --git a/src/ledctl.c b/src/ledctl.c index 9d4790e..ce32d86 100644 --- a/src/ledctl.c +++ b/src/ledctl.c @@ -1,6 +1,6 @@ /* * Intel(R) Enclosure LED Utilities - * Copyright (C) 2009-2019 Intel Corporation. + * 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, @@ -17,6 +17,7 @@ * */ +#include #include #include #include @@ -47,7 +48,6 @@ #include "status.h" #include "sysfs.h" #include "utils.h" -#include "version.h" /** * @brief An IBPI state structure. @@ -95,8 +95,8 @@ const char *ibpi_str[] = { * Internal variable of ledctl utility. It is the pattern used to print out * information about the version of ledctl utility. */ -static char *ledctl_version = "Intel(R) Enclosure LED Control Application %d.%d %s\n" - "Copyright (C) 2009-2019 Intel Corporation.\n"; +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 @@ -163,7 +163,7 @@ static void _ledctl_fini(int status __attribute__ ((unused)), */ static void _ledctl_version(void) { - printf(ledctl_version, VERSION_MAJOR, VERSION_MINOR, BUILD_LABEL); + 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"); @@ -181,7 +181,7 @@ static void _ledctl_version(void) */ static void _ledctl_help(void) { - printf(ledctl_version, VERSION_MAJOR, VERSION_MINOR, BUILD_LABEL); + 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"); @@ -261,7 +261,7 @@ static void _determine(struct ibpi_state *state) struct block_device *block; list_for_each(&state->block_list, block) { - if (block->ibpi < state->ibpi) + if (block->ibpi != state->ibpi) block->ibpi = state->ibpi; } } else { @@ -426,7 +426,7 @@ static struct block_device *_block_device_search(const struct list *block_list, * * @param[in] state pointer to IBPI state structure the block * device will be added to. - * @param[in] block pointer to block device structure. + * @param[in] name path to block device. * * @return The function does not return a value. */ @@ -521,6 +521,41 @@ static status_t _cmdline_ibpi_parse(int argc, char *argv[]) } /** + * @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 @@ -537,6 +572,8 @@ 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) @@ -559,12 +596,6 @@ static status_t _cmdline_parse(int argc, char *argv[]) } break; - case 'v': - _ledctl_version(); - exit(EXIT_SUCCESS); - case 'h': - _ledctl_help(); - exit(EXIT_SUCCESS); case 'l': status = set_log_path(optarg); break; @@ -598,18 +629,17 @@ static status_t _cmdline_parse(int argc, char *argv[]) } /** - * @brief Determine and send IBPI pattern. + * @brief Send IBPI pattern. * - * This is internal function of ledctl utility. The function determines a state - * of block device based on ibpi_list list. Then it sends a LED control message - * to controller to visualize the pattern. + * 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] sysfs pointer to sysfs structure holding information - * about the existing controllers, block devices, - * and software RAID devices. - * @param[in] ibpi_local_list TBD + * @param[in] ibpi_local_list pointer to list of ipbi_state. * - * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. + * @return STATUS_SUCCESS if successful, otherwise STATUS_IBPI_DETERMINE_ERROR */ static status_t _ledctl_execute(struct list *ibpi_local_list) { @@ -622,8 +652,15 @@ static status_t _ledctl_execute(struct list *ibpi_local_list) } list_for_each(ibpi_local_list, state) - list_for_each(&state->block_list, device) + 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); @@ -679,9 +716,13 @@ int main(int argc, char *argv[]) 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 (getuid() != 0) { + if (geteuid() != 0) { fprintf(stderr, "Only root can run this application.\n"); return STATUS_NOT_A_PRIVILEGED_USER; } diff --git a/src/ledmon.c b/src/ledmon.c index 866494a..1ab205c 100644 --- a/src/ledmon.c +++ b/src/ledmon.c @@ -1,6 +1,6 @@ /* * Intel(R) Enclosure LED Utilities - * Copyright (C) 2009-2019 Intel Corporation. + * 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, @@ -17,6 +17,7 @@ * */ +#include #include #include #include @@ -55,7 +56,6 @@ #include "sysfs.h" #include "udev.h" #include "utils.h" -#include "version.h" #include "vmdssd.h" /** @@ -118,8 +118,8 @@ const char *ibpi_str[] = { * Internal variable of monitor service. It is the pattern used to print out * information about the version of monitor service. */ -static char *ledmon_version = "Intel(R) Enclosure LED Monitor Service %d.%d %s\n" - "Copyright (C) 2009-2019 Intel Corporation.\n"; +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 @@ -216,7 +216,7 @@ static void _ledmon_status(int status, void *arg) */ static void _ledmon_version(void) { - printf(ledmon_version, VERSION_MAJOR, VERSION_MINOR, BUILD_LABEL); + 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"); @@ -234,7 +234,7 @@ static void _ledmon_version(void) */ static void _ledmon_help(void) { - printf(ledmon_version, VERSION_MAJOR, VERSION_MINOR, BUILD_LABEL); + 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"); @@ -292,7 +292,12 @@ static status_t _set_config_path(char **conf_path, const char *path) */ static status_t _set_sleep_interval(const char *optarg) { - conf.scan_interval = atoi(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; @@ -864,7 +869,7 @@ int main(int argc, char *argv[]) if (_cmdline_parse_non_daemonise(argc, argv) != STATUS_SUCCESS) return STATUS_CMDLINE_ERROR; - if (getuid() != 0) { + if (geteuid() != 0) { fprintf(stderr, "Only root can run this application.\n"); return STATUS_NOT_A_PRIVILEGED_USER; } diff --git a/src/pidfile.c b/src/pidfile.c index 48c8a6e..5d0e230 100644 --- a/src/pidfile.c +++ b/src/pidfile.c @@ -1,6 +1,6 @@ /* * Intel(R) Enclosure LED Utilities - * Copyright (C) 2009-2019 Intel Corporation. + * 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, @@ -99,7 +99,7 @@ status_t pidfile_check(const char *name, pid_t *pid) p = buf_read(path); if (p == NULL) return STATUS_INVALID_PATH; - tp = atoi(p); + tp = strtol(p, NULL, 10); if (pid) *pid = tp; free(p); diff --git a/src/scsi.c b/src/scsi.c index d3658c6..199ae3b 100644 --- a/src/scsi.c +++ b/src/scsi.c @@ -1,6 +1,6 @@ /* * Intel(R) Enclosure LED Utilities - * Copyright (C) 2009-2019 Intel Corporation. + * 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, @@ -19,22 +19,17 @@ #include #include -#include #include #include #include #include #include -#include #include #if _HAVE_DMALLOC_H #include #endif -#include -#include - #include "cntrl.h" #include "config.h" #include "enclosure.h" @@ -45,440 +40,6 @@ #include "sysfs.h" #include "utils.h" -static int debug = 0; - -static int get_ses_page(int fd, struct ses_page *p, int pg_code) -{ - int ret; - int retry_count = 3; - - do { - ret = sg_ll_receive_diag(fd, 1, pg_code, p->buf, sizeof(p->buf), - 0, debug); - } while (ret && retry_count--); - - if (!ret) - p->len = (p->buf[2] << 8) + p->buf[3] + 4; - return ret; -} - -static int process_page1(struct ses_pages *sp) -{ - int num_encl; /* number of subenclosures */ - unsigned char *ed; /* Enclosure Descriptor */ - int len = 0; - int sum_headers = 0; /* Number of Type descriptor headers */ - int i = 0; - - /* How many enclosures is in the main enclosure? */ - num_encl = sp->page1->buf[1] + 1; - /* Go to Enclosure Descriptor */ - ed = sp->page1->buf + 8; - for (i = 0; i < num_encl; i++, ed += len) { - if (ed + 3 > sp->page1->buf + sp->page1->len) { - log_debug - ("SES: Error, response pare 1 truncated at %d\n", - i); - return 1; - } - sum_headers += ed[2]; - len = ed[3] + 4; - if (len < 40) { - log_debug("SES: Response too short for page 1\n"); - continue; - } - } - - sp->page1_types = (struct type_descriptor_header *)ed; - sp->page1_types_len = sum_headers; - - /* ed is on type descr header */ - for (i = 0; i < sum_headers; i++, ed += 4) { - if (ed > sp->page1->buf + sp->page1->len) { - log_debug("SES: Response page 1 truncated at %d\n", i); - return 1; - } - } - return 0; -} - -static struct ses_pages *ses_init(void) -{ - struct ses_pages *sp; - sp = calloc(1, sizeof(*sp)); - if (!sp) - return NULL; - sp->page1 = calloc(1, sizeof(struct ses_page)); - if (!sp->page1) - goto sp1; - sp->page2 = calloc(1, sizeof(struct ses_page)); - if (!sp->page2) - goto sp2; - return sp; - sp2: - free(sp->page1); - sp1: - free(sp); - return NULL; -} - -static void ses_free(struct ses_pages *sp) -{ - if (!sp) - return; - free(sp->page1); - free(sp->page2); - free(sp->page10); - free(sp); -} - -static void dump_p10(unsigned char *p) -{ - int i; - printf("----------------------------------------------\n"); - for (i = 0; i < 8; i++, p += 16) { - printf("%p: %02x %02x %02x %02x %02x %02x %02x " \ - "%02x %02x %02x %02x %02x %02x %02x %02x %02x\n", (void *)p, - p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], - p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); - } -} - -static int enclosure_open(const struct enclosure_device *enclosure) -{ - int fd = -1; - - if (enclosure->dev_path) - fd = open(enclosure->dev_path, O_RDWR); - - return fd; -} - -static int enclosure_load_pages(struct enclosure_device *enclosure) -{ - int ret; - int fd; - struct ses_pages *sp; - - if (enclosure->ses_pages) - return 0; - - fd = enclosure_open(enclosure); - if (fd == -1) - return 1; - - sp = ses_init(); - if (!sp) { - ret = 1; - goto end; - } - - /* Read configuration. */ - ret = get_ses_page(fd, sp->page1, ENCL_CFG_DIAG_STATUS); - if (ret) - goto end; - - ret = process_page1(sp); - if (ret) - goto end; - - /* Get Enclosure Status */ - ret = get_ses_page(fd, sp->page2, ENCL_CTRL_DIAG_STATUS); -end: - close(fd); - if (ret) - ses_free(sp); - else - enclosure->ses_pages = sp; - return ret; -} - -static int enclosure_load_page10(struct enclosure_device *enclosure) -{ - int ret; - int fd; - struct ses_page *p; - - if (enclosure->ses_pages && enclosure->ses_pages->page10) - return 0; - - ret = enclosure_load_pages(enclosure); - if (ret) - return ret; - - fd = enclosure_open(enclosure); - if (fd == -1) - return 1; - - p = calloc(1, sizeof(struct ses_page)); - if (!p) { - ret = 1; - goto end; - } - - /* Additional Element Status */ - ret = get_ses_page(fd, p, ENCL_ADDITIONAL_EL_STATUS); -end: - close(fd); - if (ret) - free(p); - else - enclosure->ses_pages->page10 = p; - - return ret; -} - -static void enclosure_free_pages(struct enclosure_device *enclosure) -{ - ses_free(enclosure->ses_pages); - enclosure->ses_pages = NULL; -} - -static void print_page10(struct ses_pages *sp) -{ - unsigned char *ai = sp->page10->buf + 8; - int i = 0, len = 0, eip = 0; - unsigned char *sas = NULL; - - while (ai < sp->page10->buf + sp->page10->len) { - printf("%s()[%d]: Inv: %d, EIP: %d, Proto: 0x%04x\n", __func__, - i++, ((ai[0] & 0x80) >> 7), ((ai[0] & 0x10) >> 4), - (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; -} - -static enum ibpi_pattern ibpi_to_ses(enum ibpi_pattern ibpi) -{ - switch (ibpi) { - case IBPI_PATTERN_UNKNOWN: - case IBPI_PATTERN_ONESHOT_NORMAL: - case IBPI_PATTERN_NORMAL: - return SES_REQ_OK; - case IBPI_PATTERN_FAILED_ARRAY: - return SES_REQ_IFA; - case IBPI_PATTERN_DEGRADED: - return SES_REQ_ICA; - case IBPI_PATTERN_REBUILD: - return SES_REQ_REBUILD; - case IBPI_PATTERN_FAILED_DRIVE: - return SES_REQ_FAULT; - case IBPI_PATTERN_LOCATE: - return SES_REQ_IDENT; - case IBPI_PATTERN_HOTSPARE: - return SES_REQ_HOSTSPARE; - case IBPI_PATTERN_PFA: - return SES_REQ_PRDFAIL; - default: - return ibpi; - } -} - -static int ses_set_message(enum ibpi_pattern ibpi, struct ses_slot_ctrl_elem *el) -{ - struct ses_slot_ctrl_elem msg; - - 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; -} - -static int ses_write_msg(enum ibpi_pattern ibpi, struct block_device *device) -{ - struct ses_pages *sp = device->enclosure->ses_pages; - int idx = device->encl_index; - /* Move do descriptors */ - struct ses_slot_ctrl_elem *descriptors = (void *)(sp->page2->buf + 8); - int i; - struct ses_slot_ctrl_elem *desc_element = NULL; - element_type local_element_type = SES_UNSPECIFIED; - - for (i = 0; i < sp->page1_types_len; i++) { - struct type_descriptor_header *t = &sp->page1_types[i]; - - descriptors++; /* At first, skip overall header. */ - - if (t->element_type == SES_DEVICE_SLOT || - t->element_type == SES_ARRAY_DEVICE_SLOT) { - if (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; - /* 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; -} - -static int ses_send_diag(struct enclosure_device *enclosure) -{ - int ret; - int fd; - - fd = enclosure_open(enclosure); - if (fd == -1) - return 1; - - ret = sg_ll_send_diag(fd, 0, 1, 0, 0, 0, 0, - enclosure->ses_pages->page2->buf, - enclosure->ses_pages->page2->len, - 0, debug); - close(fd); - return ret; -} - static char *get_drive_end_dev(const char *path) { char *s, *c, *p; @@ -525,140 +86,37 @@ static uint64_t get_drive_sas_addr(const char *path) return ret; } -static int get_encl_slot(struct block_device *device) -{ - struct ses_pages *sp; - unsigned char *add_desc = NULL; - unsigned char *ap = NULL, *addr_p = NULL; - int i, j, len = 0; - uint64_t addr, addr_cmp; - int idx; - - /* try to get slot from sysfs */ - idx = get_int(device->cntrl_path, -1, "slot"); - if (idx != -1) - return idx; - - /* - * Older kernels may not have the "slot" sysfs attribute, - * fallback to Page10 method. - */ - if (enclosure_load_page10(device->enclosure)) - return -1; - sp = device->enclosure->ses_pages; - - addr = get_drive_sas_addr(device->sysfs_path); - if (!addr) - return -1; - - if (debug) - print_page10(sp); - - /* Check Page10 for address. Extract index. */ - ap = add_desc = sp->page10->buf + 8; - for (i = 0; i < sp->page1_types_len; i++) { - struct type_descriptor_header *t = &sp->page1_types[i]; - - if (t->element_type == SES_DEVICE_SLOT || - t->element_type == SES_ARRAY_DEVICE_SLOT) { - for (j = 0; j < t->num_of_elements; j++, ap += len) { - if (debug) - dump_p10(ap); - /* Get Additional Element Status Descriptor */ - /* length (x-1) */ - len = ap[1] + 2; - if ((ap[0] & 0xf) != SCSI_PROTOCOL_SAS) - continue; /* need SAS PROTO */ - /* It is a SAS protocol, go on */ - if ((ap[0] & 0x10)) /* Check EIP */ - addr_p = ap + 8; - else - addr_p = ap + 4; - /* Process only PHY 0 descriptor. */ - - /* Convert be64 to le64 */ - addr_cmp = ((uint64_t)addr_p[12] << 8*7) | - ((uint64_t)addr_p[13] << 8*6) | - ((uint64_t)addr_p[14] << 8*5) | - ((uint64_t)addr_p[15] << 8*4) | - ((uint64_t)addr_p[16] << 8*3) | - ((uint64_t)addr_p[17] << 8*2) | - ((uint64_t)addr_p[18] << 8*1) | - ((uint64_t)addr_p[19]); - - if (addr == addr_cmp) { - idx = ap[0] & 0x10 ? ap[3] : j; - return idx; - } - } - } else { - /* - * Device Slot and Array Device Slot elements are - * always first on the type descriptor header list - */ - break; - } - } - return -1; -} - -/** - */ -static int _slot_match(const char *slot_path, const char *device_path) -{ - char temp[PATH_MAX], link[PATH_MAX]; - - snprintf(temp, sizeof(temp), "%s/device", slot_path); - - if (realpath(temp, link) == NULL) - return 0; - - return strncmp(link, device_path, strlen(link)) == 0; -} - -/** - */ -static char *_slot_find(const char *enclo_path, const char *device_path) -{ - struct list dir; - char *temp, *result = NULL; - - if (scan_dir(enclo_path, &dir) == 0) { - list_for_each(&dir, temp) { - if (_slot_match(temp, device_path)) { - result = str_dup(temp); - break; - } - } - list_erase(&dir); - } - return result; -} - 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) { - if (_slot_match(encl->sysfs_path, device->cntrl_path)) { - device->enclosure = encl; - device->encl_index = get_encl_slot(device); - break; + 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 (device->enclosure != NULL && device->encl_index != -1); + return 0; } /** */ int scsi_ses_write(struct block_device *device, enum ibpi_pattern ibpi) { - int ret; - if (!device || !device->sysfs_path || !device->enclosure || device->encl_index == -1) __set_errno_and_return(EINVAL); @@ -670,45 +128,32 @@ int scsi_ses_write(struct block_device *device, enum ibpi_pattern ibpi) if ((ibpi < IBPI_PATTERN_NORMAL) || (ibpi > SES_REQ_FAULT)) __set_errno_and_return(ERANGE); - ret = enclosure_load_pages(device->enclosure); - if (ret) { - log_warning - ("Unable to send %s message to %s. Device is missing?", - ibpi2str(ibpi), strstr(device->sysfs_path, "host")); - return ret; - } - - return ses_write_msg(ibpi, device); + 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) + if (!device->enclosure->ses_pages.changes) return 0; - ret = ses_send_diag(device->enclosure); + fd = enclosure_open(device->enclosure); + if (fd == -1) + return 1; + + ret = ses_send_diag(fd, &device->enclosure->ses_pages); + + close(fd); - enclosure_free_pages(device->enclosure); return ret; } -/** - * @brief Gets a path to slot of sas controller. - * - * This function returns a sysfs path to component of enclosure the device - * belongs to. - * - * @param[in] path Canonical sysfs path to block device. - * - * @return A sysfs path to controller device associated with the given - * block device if successful, otherwise NULL pointer. - */ -static char *sas_get_slot_path(const char *path, const char *ctrl_path) +char *scsi_get_host_path(const char *path, const char *ctrl_path) { char *host; char host_path[PATH_MAX] = { 0 }; @@ -724,30 +169,3 @@ static char *sas_get_slot_path(const char *path, const char *ctrl_path) } return str_dup(host_path); } - -/** - */ -static char *_get_enc_slot_path(const char *path) -{ - struct enclosure_device *device; - char *result = NULL; - - list_for_each(sysfs_get_enclosure_devices(), device) { - result = _slot_find(device->sysfs_path, path); - if (result != NULL) - break; - } - return result; -} - -/** - */ -char *scsi_get_slot_path(const char *path, const char *ctrl_path) -{ - char *result = NULL; - - result = _get_enc_slot_path(path); - if (!result) - result = sas_get_slot_path(path, ctrl_path); - return result; -} diff --git a/src/scsi.h b/src/scsi.h index 7f1d384..1e0dd72 100644 --- a/src/scsi.h +++ b/src/scsi.h @@ -1,6 +1,6 @@ /* * Intel(R) Enclosure LED Utilities - * Copyright (C) 2009-2018 Intel Corporation. + * 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, @@ -24,17 +24,17 @@ #include "ibpi.h" /** - * @brief Gets a path to slot of an enclosure. + * @brief Gets the sas host path of the device. * - * This function returns a sysfs path to component of enclosure 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 enclosure's component associated with the given + * @return A sysfs path to host device associated with the given * block device if successful, otherwise NULL pointer. */ -char *scsi_get_slot_path(const char *path, const char *cntrl_path); +char *scsi_get_host_path(const char *path, const char *cntrl_path); /** * @brief Prepares SES message based on ibpi pattern. diff --git a/src/ses.c b/src/ses.c new file mode 100644 index 0000000..75e1d5e --- /dev/null +++ b/src/ses.c @@ -0,0 +1,521 @@ +/* + * 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; +} diff --git a/src/ses.h b/src/ses.h index d0c4227..62ca482 100644 --- a/src/ses.h +++ b/src/ses.h @@ -1,6 +1,6 @@ /* * Intel(R) Enclosure LED Utilities - * Copyright (C) 2009-2017 Intel Corporation. + * 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, @@ -22,122 +22,17 @@ #include +#include "ibpi.h" + /* Size of buffer for SES-2 Messages. */ #define SES_ALLOC_BUFF 4096 -#define ENCL_CFG_DIAG_STATUS 0x01 -#define ENCL_CTRL_DIAG_STATUS 0x02 -#define ENCL_CTRL_DIAG_CFG 0x02 -#define ENCL_EL_DESCR_STATUS 0x07 -#define ENCL_ADDITIONAL_EL_STATUS 0x0a -#define SCSI_PROTOCOL_SAS 6 - typedef enum __attribute__((packed)) { SES_UNSPECIFIED = 0x00, SES_DEVICE_SLOT = 0x01, SES_ARRAY_DEVICE_SLOT = 0x17, } element_type; -static inline void _set_prdfail(unsigned char *u) -{ - u[0] |= (1 << 6); -} - -static inline void _set_abrt(unsigned char *u) -{ - u[1] |= (1 << 0); -} - -static inline void _set_rebuild(unsigned char *u) -{ - u[1] |= (1 << 1); -} - -static inline void _set_ifa(unsigned char *u) -{ - u[1] |= (1 << 2); -} - -static inline void _set_ica(unsigned char *u) -{ - u[1] |= (1 << 3); -} - -static inline void _set_cons_check(unsigned char *u) -{ - u[1] |= (1 << 4); -} - -static inline void _set_hspare(unsigned char *u) -{ - u[1] |= (1 << 5); -} - -static inline void _set_rsvd_dev(unsigned char *u) -{ - u[1] |= (1 << 6); -} - -static inline void _set_ok(unsigned char *u) -{ - u[1] |= (1 << 7); -} - -static inline void _set_ident(unsigned char *u) -{ - u[2] |= (1 << 1); -} - -static inline void _clr_ident(unsigned char *u) -{ - u[2] &= ~(1 << 1); -} - -static inline void _set_rm(unsigned char *u) -{ - u[2] |= (1 << 2); -} - -static inline void _set_ins(unsigned char *u) -{ - u[2] |= (1 << 3); -} - -static inline void _set_miss(unsigned char *u) -{ - u[2] |= (1 << 4); -} - -static inline void _set_dnr(unsigned char *u) -{ - u[2] |= (1 << 6); -} - -static inline void _set_actv(unsigned char *u) -{ - u[2] |= (1 << 7); -} - -static inline void _set_enbb(unsigned char *u) -{ - u[3] |= (1 << 2); -} - -static inline void _set_enba(unsigned char *u) -{ - u[3] |= (1 << 3); -} - -static inline void _set_off(unsigned char *u) -{ - u[3] |= (1 << 4); -} - -static inline void _set_fault(unsigned char *u) -{ - u[3] |= (1 << 5); -} - struct ses_page { unsigned char buf[SES_ALLOC_BUFF]; int len; @@ -151,11 +46,12 @@ struct type_descriptor_header { }; struct ses_pages { - struct ses_page *page1; - struct ses_page *page2; - struct ses_page *page10; - struct type_descriptor_header *page1_types; + 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 { @@ -170,4 +66,14 @@ struct ses_slot_ctrl_elem { }; }; +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 diff --git a/src/slave.c b/src/slave.c index 951da06..48f5276 100644 --- a/src/slave.c +++ b/src/slave.c @@ -1,6 +1,6 @@ /* * Intel(R) Enclosure LED Utilities - * Copyright (C) 2009-2019 Intel Corporation. + * 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, @@ -82,7 +82,7 @@ static unsigned int _get_slot(const char *path) char *p = get_text(path, "slot"); if (p) { if (strcmp(p, "none") != 0) - result = atoi(p); + result = strtol(p, NULL, 10); free(p); } return result; diff --git a/src/smp.h b/src/smp.h index bac0386..e2d7485 100644 --- a/src/smp.h +++ b/src/smp.h @@ -1,6 +1,6 @@ /* * Intel(R) Enclosure LED Utilities - * Copyright (C) 2011-2017 Intel Corporation. + * 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, @@ -64,21 +64,6 @@ struct gpio_tx_register_byte { } __attribute__ ((__packed__)); /** - * @brief Sends message to SES processor of an enclosure. - * - * This function send a message to an enclosure in order to control LEDs of - * the given slot/component. It uses interface of ENCLOSURE kernel module to - * control LEDs. - * - * @param[in] device Path to an enclosure device in sysfs. - * @param[in] ibpi IBPI pattern to visualize. - * - * @return Number of characters written if successful or -1 in case of error - * and errno is set to appropriate error code. - */ -int scsi_ses_write(struct block_device *device, enum ibpi_pattern ibpi); - -/** * @brief Write message to outbound raw byte stream buffer. * * @param[in] device Path to a smp device in sysfs. diff --git a/src/utils.c b/src/utils.c index dfe3637..0f0b22f 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,6 +1,6 @@ /* * Intel(R) Enclosure LED Utilities - * Copyright (C) 2009-2019 Intel Corporation. + * 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, @@ -117,7 +117,7 @@ int get_int(const char *path, int defval, const char *name) { char *p = get_text(path, name); if (p) { - defval = atoi(p); + defval = strtol(p, NULL, 10); free(p); } return defval; @@ -235,8 +235,8 @@ void get_id(const char *path, struct device_id *did) t = strchr(p, ':'); if (t) { *(t++) = '\0'; - did->major = atoi(p); - did->minor = atoi(t); + did->major = strtol(p, NULL, 10); + did->minor = strtol(t, NULL, 10); } free(p); } @@ -428,6 +428,7 @@ int match_string(const char *string, const char *pattern) } status = regexec(®ex, string, 0, NULL, 0); + regfree(®ex); if (status != 0) return 0; @@ -622,9 +623,6 @@ status_t set_verbose_level(int log_level) const char *ibpi2str(enum ibpi_pattern ibpi) { -#ifdef _TEST_CONFIG - return NULL; -#else static char buf[20]; const char *ret; @@ -639,5 +637,4 @@ const char *ibpi2str(enum ibpi_pattern ibpi) } return ret; -#endif }