├── .github ├── .checkpatch.conf ├── ISSUE_TEMPLATE │ ├── bug.yaml │ ├── feature-change-request.yaml │ └── question.yaml ├── build_configure.sh ├── configure_apt.sh ├── dependabot.yml ├── install_debs_compilation.sh ├── nitpicks.yml └── workflows │ ├── nitpicker.yaml │ ├── review.yml │ ├── test.yml │ └── yapf_check.yaml ├── .gitignore ├── .style.yapf ├── CHANGELOG.md ├── CONTRIBUTING.md ├── COPYING ├── COPYING.LIB ├── LICENSES ├── LICENSE.GPL-2.0-only ├── LICENSE.GPL-3.0-or-later └── LICENSE.LGPL-2.1-or-later ├── Makefile.am ├── README.md ├── autogen.sh ├── config └── config.h ├── configure.ac ├── doc ├── Makefile.am ├── ledctl.pod ├── ledmon.conf.pod └── ledmon.pod ├── ledmon.pc.in ├── m4 ├── m4_ax_check_compile_flag.m4 └── m4_check_if_prog_installed.m4 ├── pytest.ini ├── security.md ├── src ├── Makefile.am ├── common │ ├── Makefile.am │ ├── config_file.c │ └── config_file.h ├── ledctl │ ├── Makefile.am │ ├── help.c │ ├── help.h │ └── ledctl.c ├── ledmon │ ├── Makefile.am │ ├── ledmon.c │ ├── pidfile.c │ ├── pidfile.h │ ├── udev.c │ └── udev.h └── lib │ ├── LIBRARY.md │ ├── Makefile.am │ ├── ahci.c │ ├── ahci.h │ ├── amd.c │ ├── amd.h │ ├── amd_ipmi.c │ ├── amd_ipmi.h │ ├── amd_sgpio.c │ ├── amd_sgpio.h │ ├── block.c │ ├── block.h │ ├── cntrl.c │ ├── cntrl.h │ ├── dellssd.c │ ├── dellssd.h │ ├── enclosure.c │ ├── enclosure.h │ ├── include │ ├── Makefile.am │ └── led │ │ ├── Makefile.am │ │ └── libled.h │ ├── ipmi.c │ ├── ipmi.h │ ├── libled.c │ ├── libled_internal.c │ ├── libled_internal.h │ ├── libled_private.h │ ├── list.c │ ├── list.h │ ├── npem.c │ ├── npem.h │ ├── pci_slot.c │ ├── pci_slot.h │ ├── raid.c │ ├── raid.h │ ├── scsi.c │ ├── scsi.h │ ├── ses.c │ ├── ses.h │ ├── slot.c │ ├── slot.h │ ├── smp.c │ ├── smp.h │ ├── status.h │ ├── sysfs.c │ ├── sysfs.h │ ├── tail.c │ ├── tail.h │ ├── utils.c │ ├── utils.h │ ├── vmdssd.c │ └── vmdssd.h ├── systemd ├── Makefile.am └── ledmon.service.in └── tests ├── Makefile.am ├── README.md ├── check_symbol_visibility.sh ├── conftest.py ├── ledctl ├── ledctl_cmd.py ├── parameters_test.py └── slot_test.py ├── lib_unit_test.c ├── licensing.py └── runtests.sh /.github/.checkpatch.conf: -------------------------------------------------------------------------------- 1 | --no-tree 2 | --show-types 3 | --ignore FILE_PATH_CHANGES 4 | --ignore GIT_COMMIT_ID 5 | --ignore SPDX_LICENSE_TAG 6 | --ignore FSF_MAILING_ADDRESS 7 | --ignore PREFER_PACKED 8 | --ignore NEW_TYPEDEFS 9 | --ignore PREFER_PRINTF 10 | --ignore EMAIL_SUBJECT 11 | --ignore PREFER_DEFINED_ATTRIBUTE_MACRO 12 | --ignore COMPLEX_MACRO 13 | --ignore LONG_LINE_STRING 14 | --ignore EMBEDDED_FILENAME 15 | --exclude .github 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | description: Use this form to report problems with application, unwanted behaviors, crashes etc. 4 | title: "[BUG]: Title" 5 | labels: Bug 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Before submitting a bug, please test if your issue reproduces using ledmon upstream [README](https://github.com/intel/ledmon/blob/master/README.md). 11 | It is an open-source project, so we highly encourage you to fix issue yourself 12 | and to become a contributor. 13 | 14 | If the issue is submitted against compilation, please double check that all 15 | dependencies listed [here](https://github.com/intel/ledmon?tab=readme-ov-file#1-dependencies) 16 | are installed. 17 | 18 | Please enable highest log level when collecting logs, it could speed up investigation 19 | e.g. ledctl --log-level=all locate=/dev/nvme0n1 20 | 21 | If the issue is related to AMD hardware, please care to mention [Nathan Fontenot](https://github.com/nfont) in the description. 22 | 23 | - type: textarea 24 | attributes: 25 | label: Description 26 | description: >- 27 | A clear and concise description of what the bug is. 28 | validations: 29 | required: true 30 | 31 | - type: textarea 32 | attributes: 33 | label: Steps to reproduce bug 34 | description: >- 35 | Describe the steps to reproduce this bug. 36 | placeholder: | 37 | 1. 38 | 2. 39 | 3. 40 | validations: 41 | required: true 42 | 43 | - type: textarea 44 | attributes: 45 | label: Expected behavior 46 | description: >- 47 | A clear and concise description of what you expected to happen. 48 | validations: 49 | required: true 50 | 51 | - type: textarea 52 | attributes: 53 | label: Actual behavior 54 | description: >- 55 | A clear and concise description of what happened instead. 56 | validations: 57 | required: true 58 | 59 | - type: textarea 60 | attributes: 61 | label: Environment 62 | description: | 63 | Please describe your environment. 64 | value: | 65 | OS: 66 | Controller Type(supported: https://github.com/intel/ledmon/blob/master/doc/ledmon.pod): 67 | Disks($ ls -l /sys/block): 68 | NVMe multipath enabled(Yes|No|N/A)($ cat /sys/module/nvme_core/parameters/multipath)?: 69 | validations: 70 | required: true 71 | 72 | - type: textarea 73 | attributes: 74 | label: Ledmon version 75 | description: Please add ledmon version, $ ledmon --version, if ledmon is built directly from 76 | the repository, please provide commit_id. 77 | validations: 78 | required: true 79 | 80 | - type: textarea 81 | attributes: 82 | label: Ledmon logs 83 | description: Please add or attach ledmon logs, $ /var/log/ledmon.log. 84 | validations: 85 | required: false 86 | 87 | - type: textarea 88 | attributes: 89 | label: Ledctl logs 90 | description: Please add or attach ledctl logs, $ /var/log/ledctl.log. 91 | validations: 92 | required: false 93 | 94 | - type: textarea 95 | attributes: 96 | label: Ledmon supported controllers 97 | description: List of controllers supported by ledmon, $ ledctl --list-controllers. 98 | validations: 99 | required: true 100 | 101 | 102 | - type: textarea 103 | attributes: 104 | label: Additional information 105 | validations: 106 | required: false 107 | ... 108 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-change-request.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature/Change request 3 | description: If you need enhancement, feature or if you would like to change current behavior use this form. 4 | title: "[ENHANCEMENT] Title" 5 | labels: Enhancement 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Suggest an idea for this project. 11 | This is an open source project, so you are invited to become a contributor. 12 | 13 | If the request is related to AMD hardware, please mention [Nathan Fontenot](https://github.com/nfont). 14 | 15 | - type: textarea 16 | attributes: 17 | label: Description 18 | description: >- 19 | Provide detailed description of wanted functionality or change. 20 | validations: 21 | required: true 22 | 23 | - type: textarea 24 | attributes: 25 | label: Reason 26 | description: >- 27 | Why do you need this functionality/change? What's the gain of introducing it? 28 | validations: 29 | required: false 30 | 31 | - type: textarea 32 | attributes: 33 | label: Environment 34 | description: | 35 | Please describe your environment. 36 | value: | 37 | Include relevant details about the environment you're working in. 38 | OS: 39 | What type of controller and disk the request is directed to: 40 | validations: 41 | required: false 42 | ... 43 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | description: Ask a question. 4 | title: "[QUESTION] Title" 5 | labels: Question 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | If the question is related to AMD hardware, please mention [Nathan Fontenot](https://github.com/nfont). 11 | 12 | - type: textarea 13 | attributes: 14 | label: Question 15 | description: >- 16 | Ask a question. 17 | validations: 18 | required: true 19 | 20 | - type: textarea 21 | attributes: 22 | label: Environment 23 | description: | 24 | Please describe your environment. 25 | value: | 26 | Include relevant details about the environment you're working in. 27 | OS: 28 | What type of controller and disk the question is directed to: 29 | validations: 30 | required: false 31 | ... 32 | -------------------------------------------------------------------------------- /.github/build_configure.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | # Set compiler flags 4 | if ! [[ -z $1 ]]; 5 | then 6 | export CFLAGS=$1 7 | else 8 | echo "There is no CFLAGS set. Please check your configuration." 9 | exit 1 10 | fi 11 | 12 | if [ -z $2 ] 13 | then 14 | set CC=$2 15 | fi 16 | 17 | # Configure 18 | ./autogen.sh && ./configure --enable-test --enable-library 19 | -------------------------------------------------------------------------------- /.github/configure_apt.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | VERSION_CODENAME=$(grep -oP '(?<=^VERSION_CODENAME=).+' /etc/os-release | tr -d '"') 4 | echo "Detected VERSION_CODENAME: $VERSION_CODENAME" 5 | 6 | # Add ubuntu repository 7 | sudo add-apt-repository "deb [arch=amd64] http://archive.ubuntu.com/ubuntu $VERSION_CODENAME \ 8 | main universe" 9 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | -------------------------------------------------------------------------------- /.github/install_debs_compilation.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | # Install gcc 4 | if [ ! -z "$1" ]; then 5 | sudo apt-get -y install gcc-$1 g++-$1 --no-upgrade --no-install-recommends --no-install-suggests 6 | fi 7 | # Install dependencies 8 | sudo apt-get -y install pkg-config automake autoconf autoconf-archive make libsgutils2-dev \ 9 | libudev-dev libpci-dev check devscripts 10 | -------------------------------------------------------------------------------- /.github/nitpicks.yml: -------------------------------------------------------------------------------- 1 | - markdown: | 2 | Hey @tasleson, a file(s) you own were modified in this PR! Could you take a look? 3 | pathFilter: 4 | - 'src/lib/ses*' 5 | - 'src/lib/smp*' 6 | 7 | - markdown: | 8 | Hey @nfont, a file(s) you own were modified in this PR! Could you take a look? 9 | pathFilter: 10 | - 'src/lib/amd*' 11 | 12 | - markdown: | 13 | Hey @prabhakar, a file(s) you own were modified in this PR! Could you take a look? 14 | pathFilter: 15 | - 'src/lib/dell*' 16 | -------------------------------------------------------------------------------- /.github/workflows/nitpicker.yaml: -------------------------------------------------------------------------------- 1 | name: Nitpicker 2 | on: [pull_request_target] 3 | permissions: 4 | pull-requests: write 5 | jobs: 6 | formatting-check: 7 | if: github.repository == 'intel/ledmon' 8 | name: tag codeowners 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | - name: nitpicker 13 | uses: ethanis/nitpicker@v1.7 14 | with: 15 | nitpicks: './.github/nitpicks.yml' 16 | token: '${{ secrets.GITHUB_TOKEN }}' 17 | -------------------------------------------------------------------------------- /.github/workflows/review.yml: -------------------------------------------------------------------------------- 1 | name: review 2 | on: [pull_request] 3 | env: 4 | cflags: -Werror 5 | permissions: read-all 6 | jobs: 7 | commits_review: 8 | name: Commits review 9 | runs-on: ubuntu-24.04 10 | steps: 11 | - uses: actions/checkout@v4 12 | with: 13 | ref: ${{ github.event.pull_request.head.sha }} 14 | fetch-depth: 0 15 | - name: 'Move prepared .checkpatch.conf file to main directory' 16 | run: mv .github/.checkpatch.conf . 17 | - name: Run checkpatch review 18 | uses: webispy/checkpatch-action@v9 19 | - name: Set up Python 3.11 20 | uses: actions/setup-python@v5 21 | with: 22 | python-version: "3.11" 23 | - name: Get modified files 24 | id: changed-files 25 | uses: tj-actions/changed-files@v46 26 | with: 27 | files_ignore: | 28 | .checkpatch.conf 29 | .github/** 30 | LICENSES/** 31 | .gitignore 32 | autogen.sh 33 | pytest.ini 34 | *.yapf 35 | COPYING* 36 | */*.md 37 | CHANGELOG.md 38 | README.md 39 | - name: Run license review 40 | env: 41 | ALL_CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }} 42 | run: | 43 | for FILE in ${ALL_CHANGED_FILES}; do 44 | python3.11 ./tests/licensing.py --file ${FILE} --log-level=INFO 45 | done 46 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | on: [pull_request] 3 | env: 4 | cflags: -Werror 5 | permissions: read-all 6 | jobs: 7 | make: 8 | # when gcc is not found, it may be needed to update runner version 9 | runs-on: ubuntu-24.04 10 | name: Compilation test with gcc 11 | strategy: 12 | matrix: 13 | # gcc-versions are used to test up to 5 years old 14 | gcc-version: [9, 10, 11, 12, 13, 14] 15 | steps: 16 | - uses: actions/checkout@v4 17 | - name: 'Add ubuntu repositories' 18 | run: .github/configure_apt.sh 19 | - name: 'Install building dependencies' 20 | run: .github/install_debs_compilation.sh ${{ matrix.gcc-version }} 21 | - name: 'Check if gcc was installed correctly' 22 | run: gcc-${{ matrix.gcc-version }} --version 23 | - name: 'Check if g++ was installed correctly' 24 | run: g++-${{ matrix.gcc-version }} --version 25 | - name: 'Generate compiling configurations with library disabled' 26 | run: . ./autogen.sh && ./configure CFLAGS=${{ env.cflags }} 27 | - name: 'Make' 28 | run: V=1 make -j$(nproc) CC=gcc-${{ matrix.gcc-version }} CXX=g++-${{ matrix.gcc-version }} 29 | - name: 'Hardening check: ledmon' 30 | run: hardening-check src/ledmon/ledmon 31 | - name: 'Hardening check: ledctl' 32 | run: hardening-check src/ledctl/ledctl 33 | - name: 'Check public symbol visibility' 34 | run: tests/check_symbol_visibility.sh 35 | - name: "Clean before retest with library enabled" 36 | run: make clean 37 | - name: 'Generate compiling configurations with library enabled' 38 | run: . .github/build_configure.sh ${{ env.cflags }} 39 | - name: 'Make' 40 | run: V=1 make -j$(nproc) CC=gcc-${{ matrix.gcc-version }} CXX=g++-${{ matrix.gcc-version }} 41 | - name: 'Hardening check: ledmon' 42 | run: hardening-check src/ledmon/ledmon 43 | - name: 'Hardening check: ledctl' 44 | run: hardening-check src/ledctl/ledctl 45 | - name: 'Check public symbol visibility' 46 | run: tests/check_symbol_visibility.sh 47 | clangcompile: 48 | name: Compilation test with clang 49 | runs-on: ubuntu-24.04 50 | steps: 51 | - uses: actions/checkout@v4 52 | - name: 'Add ubuntu repositories' 53 | run: .github/configure_apt.sh 54 | - name: 'Install building dependencies' 55 | run: .github/install_debs_compilation.sh 56 | - name: 'Install clang' 57 | run: sudo apt-get install clang 58 | - name: 'Generate compiling configurations' 59 | run: .github/build_configure.sh ${{ env.cflags }} clang 60 | - name: 'Make' 61 | run: V=1 make -j$(nproc) 62 | tests: 63 | name: Run tests 64 | strategy: 65 | matrix: 66 | gcc-version: [13] 67 | python-version: ["3.9"] 68 | runs-on: ubuntu-24.04 69 | steps: 70 | - uses: actions/checkout@v4 71 | - name: 'Add ubuntu repositories' 72 | run: .github/configure_apt.sh 73 | - name: 'Install building dependencies' 74 | run: .github/install_debs_compilation.sh ${{ matrix.gcc-version }} 75 | - name: 'Generate compiling configurations' 76 | run: .github/build_configure.sh ${{ env.cflags }} 77 | - name: 'Make' 78 | run: V=1 make -j$(nproc) CC=gcc-${{ matrix.gcc-version }} CXX=g++-${{ matrix.gcc-version }} 79 | - name: Set up Python ${{ matrix.python-version }} 80 | uses: actions/setup-python@v5 81 | with: 82 | python-version: ${{ matrix.python-version }} 83 | - name: Install python dependencies 84 | run: | 85 | python -m pip install --upgrade pip 86 | pip install pytest 87 | - name: Run tests using pytest 88 | run: | 89 | # Run only non-hardware dependent tests 90 | pytest --ledctl-binary=src/ledctl/ledctl 91 | -------------------------------------------------------------------------------- /.github/workflows/yapf_check.yaml: -------------------------------------------------------------------------------- 1 | name: YAPF Formatting Check 2 | on: [pull_request] 3 | permissions: read-all 4 | jobs: 5 | formatting-check: 6 | name: Formatting Check 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - name: run YAPF to test if python code is correctly formatted 11 | uses: AlexanderMelde/yapf-action@master 12 | with: 13 | args: --verbose -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ledctl.8.gz 2 | ledmon.conf.5.gz 3 | ledmon.8.gz 4 | *.o 5 | *.lo 6 | *.la 7 | .deps 8 | .libs 9 | _build 10 | src/ledmon/ledmon 11 | src/ledctl/ledctl 12 | *~ 13 | *.bak 14 | .depend 15 | *.swp 16 | Makefile 17 | Makefile.in 18 | aclocal.m4 19 | m4/libtool.m4 20 | m4/ltoptions.m4 21 | m4/ltsugar.m4 22 | m4/ltversion.m4 23 | m4/lt~obsolete.m4 24 | autom4te.cache/ 25 | config.h.in 26 | build-aux/ 27 | configure 28 | config_ac.h 29 | config.log 30 | config.status 31 | stamp-h1 32 | tests/__pycache__/ 33 | doc/ledctl.gz 34 | doc/ledmon.conf.gz 35 | doc/ledmon.gz 36 | ar-lib 37 | compile 38 | missing 39 | libtool 40 | depcomp 41 | config.guess 42 | config.sub 43 | config_ac.h.in 44 | install-sh 45 | ltmain.sh 46 | .vscode 47 | src/common/.dirstamp 48 | ledmon.pc 49 | -------------------------------------------------------------------------------- /.style.yapf: -------------------------------------------------------------------------------- 1 | [style] 2 | use_tabs = 0 3 | indent_width = 4 -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### v1.1.0 2 | 3 | [Commit list](https://github.com/intel/ledmon/compare/v1.0.0...v1.1.0) 4 | 5 | Enhancements 6 | 7 | * Various enhancements in tests, licensing and deployment 8 | * Add --default-controller command to ledctl 9 | 10 | Bug fixes 11 | 12 | * ledctl: add error message for missing devices 13 | * ledctl: fix musl build failure by replacing on_exit() by atexit() 14 | * Improve error handling for unsupported patters by falling back to normal 15 | * Fix incorrect conversion of large integer values 16 | * Fix compilation warnings 17 | * Fix incorrect array index usage for block device and SES slot lookup 18 | 19 | ### v1.0.0 / 2024-02-28 20 | 21 | [Commit list](https://github.com/intel/ledmon/compare/v0.97...v1.0.0) 22 | 23 | Enhancements 24 | 25 | * lib: introduce library 26 | * tests: migrate tests to pytest 27 | * Introduce "make check" option 28 | * Allow disabling documentation installation 29 | * Add minimal Nvme subsystem support 30 | * Allow choosing output format from get-slot 31 | * Rework handling --help option 32 | * Update manual 33 | 34 | Bug fixes 35 | 36 | * Fix log severity, messages and level detection 37 | * Ledctl: skip slot state check for set locate_off 38 | * ci: Compile with tests and library enabled 39 | * configure.ac: build library when "--enbable-test" 40 | * Allow setting multiple LEDs per slot on SES 41 | * Prevent compiler from optimizing out security checks 42 | * Fix compilation warnings 43 | * Add compiler defenses flags 44 | * Add support for clang compiler 45 | * utils.c: remove duplicated "-c" short parameter 46 | * Remove parsing ALLOWLIST and EXCLUDELIST using regex 47 | * ledctl: Fix exit ledctl with test flag 48 | 49 | ### v0.97 / 2023-05-16 50 | 51 | [Commit list](https://github.com/intel/ledmon/compare/v0.96...v0.97) 52 | 53 | Enhancements 54 | 55 | * ledctl: add support to empty slots blinking 56 | * ledmon license change to LGPLv2 57 | * ledctl: Add SES get/set/list slot support 58 | * Update NPEM wait command 59 | * Remove exclusionary language 60 | * ledmon: Define ONESHOT_NORMAL for VMD 61 | 62 | Bug fixes 63 | 64 | * ipmi: avoid error messages on non-dell platforms 65 | * vmdssd: define normal pattern 66 | * ledctl: clear unsupported params from config 67 | * block.c: get_block_device_from_sysfs_path modification 68 | * fix ibpi_value lists getter 69 | * amd_ipmi: Allow to _enable_smbus_control 70 | * ledmon.c: allocate memory for ignore 71 | * sysfs: add only vmd devices to slots_list 72 | * Rename --controller parameter 73 | * Slots list implementations and fixes 74 | 75 | ### v0.96 / 2022-05-26 76 | 77 | [Commit list](https://github.com/intel/ledmon/compare/v0.95...v0.96) 78 | 79 | Bug fixes 80 | 81 | * Manual updates, clarify --listed-only option 82 | * Fix cache indexing of ATA port 83 | * Fixes in regard to macros 84 | * Fix memory leak in amd_ipmi.c 85 | * Fix NULL pointer dereferences in sysfs.c 86 | * Make messages appear in service log immediately 87 | * Other minor fixes 88 | 89 | ### v0.95 / 2021-01-15 90 | 91 | [Commit list](https://github.com/intel/ledmon/compare/v0.94...v0.95) 92 | 93 | Enhancements 94 | 95 | * Allow to run ledctl version without root 96 | * README update with the compilation steps 97 | 98 | Bug fixes 99 | 100 | * Documentation updates 101 | * Defaulting to SGPIO for AMD systems 102 | * Don't rely on states priority while changing IBPI states 103 | * Check the white/blacklist from ledmon.conf earlier in discovery 104 | * Change installation directory to /usr/sbin 105 | * Use package version from autotools, not version.h 106 | * Fix memory leak in utils.c 107 | * Bugfixes and refactoring in SES module 108 | * Fixed issues reported by static analysis 109 | * Build system fixes 110 | * Other minor fixes 111 | 112 | ### v0.94 / 2020-02-04 113 | 114 | [Commit list](https://github.com/intel/ledmon/compare/v0.93...v0.94) 115 | 116 | Enhancements 117 | 118 | * Support for AMD IPMI enclosure management 119 | * Support for NPEM 120 | 121 | Bug fixes 122 | 123 | * Documentation updates 124 | * Fix activity indicator state for SMP 125 | * Fix for GCC 9 compilation 126 | * Update ipbi pattern for drives with previous pattern unknown 127 | 128 | ### v0.93 / 2019-10-17 129 | 130 | [Commit list](https://github.com/intel/ledmon/compare/v0.92...v0.93) 131 | 132 | Enhancements 133 | 134 | * Support for AMD SGPIO enclosure management 135 | * Migration to GNU Autotools build system 136 | * Added more strict compilation flags 137 | 138 | Bug fixes 139 | 140 | * Fixed segfault when a value is missing from ibpi_str 141 | * Use proper format string with syslog() 142 | * Fixed issues reported by static analysis 143 | * Removed unused SGPIO structures 144 | * Added udev_device reference clean-up 145 | * Hidden ipmi error messages on non-dell platforms 146 | 147 | ### v0.92 / 2019-04-12 148 | 149 | [Commit list](https://github.com/intel/ledmon/compare/v0.91-fixed...v0.92) 150 | 151 | Bug fixes 152 | * Silence warning and error messages. 153 | 154 | 155 | ### v0.91 / 2019-04-01 156 | 157 | [Commit list](https://github.com/intel/ledmon/compare/v0.90...v0.91) 158 | 159 | Enhancements 160 | 161 | * Ledmon systemd service file. 162 | * Shared configuration between ledmon and ledctl. 163 | * Log-level support for ledctl. 164 | * Build label support. 165 | * 13G/14G Dell Servers support. 166 | * Foreground option. 167 | 168 | Bug fixes 169 | 170 | * Udev action handling reimplementation. 171 | * Unify ping process method. 172 | * Recognize volumes under reshape. 173 | * Distinguish inactive state for volume and container. 174 | * Fix various gcc and clang warnings. 175 | * Fix ledctl exit status. 176 | * Logging method reimplementation. 177 | * Makefile fixes. 178 | * Change outdated functions and simplify string modifications. 179 | * Ommited errors handling. 180 | 181 | 182 | ### v0.90 / 2018-02-14 183 | 184 | [Commit list](https://github.com/intel/ledmon/compare/v0.80...v0.90) 185 | 186 | Enhancements 187 | 188 | * Handle udev events in ledmon. 189 | * Possibility to list all controllers detected by LED utilities tool (ledctl --list-controllers). 190 | * Configuration file for ledmon advanced features (check man ledmon.config). 191 | * Added option to ledctl for managing only listed devices (ledctl --listed-only). 192 | * Documentation improvements. 193 | 194 | Bug fixes 195 | 196 | * Detecting nvme disks during scan. 197 | * Keep failure state after VMD reconnecting. 198 | * Blinking failure LED after removing disk from RAID. 199 | * Refactoring of SES-2 protocol implementation. SES minor fixes. 200 | * Logfile and log levels small improvements. 201 | 202 | 203 | ### v0.80 / 2016-10-28 204 | 205 | [Commit list](https://github.com/intel/ledmon/compare/v0.70...v0.80) 206 | 207 | Enhancements 208 | 209 | * Support for NVMe SSD devices. 210 | * Support for NVMe devices under VMD domains. 211 | * Using SMP GPIO_REG_TYPE_TX register for SGPIO. 212 | * Sending LED commands optimization. 213 | * Documentation improvements. 214 | 215 | Bug fixes 216 | 217 | * Fix support for the Dell PCIe SSD devices. 218 | * Handling enclosure device name change. 219 | * Fixes around IBPI_PATTERN_LOCATE_OFF state. 220 | 221 | 222 | ### v0.70 / 2012-12-12 223 | 224 | [Commit list](https://github.com/intel/ledmon/compare/v0.40...v0.70) 225 | 226 | Enhancements 227 | 228 | * Introduce SES-2 protocol support. 229 | 230 | Bug fixes 231 | 232 | * Minor fixes. 233 | * Memory leaks. 234 | 235 | 236 | ### v0.40 / 2012-07-12 237 | 238 | [Commit list](https://github.com/intel/ledmon/compare/v0.3...v0.40) 239 | 240 | Enhancements 241 | 242 | * Support for Dell backplane bays. 243 | * Turn off all unset LEDs in ledctl. 244 | 245 | Bug fixes 246 | 247 | * IPBI pattern interpretation. 248 | 249 | 250 | ### v0.3 / 2012-03-06 251 | 252 | [Commit list](https://github.com/intel/ledmon/compare/v0.2...v0.3) 253 | 254 | Enhancements 255 | 256 | * Support for disk drivers directly attached to SCU HBA. 257 | 258 | Removals 259 | 260 | * Remove dependency of smp_utils. 261 | 262 | 263 | ### v0.2 / 2011-08-24 264 | 265 | [Commit list](https://github.com/intel/ledmon/compare/af8f20626e4e36cdf4bb9955fc65f22fec155580...v0.2) 266 | 267 | Enhancements 268 | 269 | * Ledmon initial version. 270 | * Visualize the state of arrays. 271 | * Introduce daemon app "ledmon" and LED manual control app "ledctl". 272 | 273 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Header and Copyrights 2 | 3 | Verification of License and Copyrights is automated on Github by 4 | [Licensing test](https://github.com/intel/ledmon/blob/main/tests/licensing.py). 5 | 6 | The rules for Licenses and Copyrights are as follows: 7 | - Preferred comment mark should be used for file type. Please prefer to test or other files for 8 | examples. For `.c` and `.h` files, it is `//`. 9 | - [SPDX Licenses](https://spdx.org/licenses/) must be used: 10 | - It must be on the first or the third line (only if interpreter is defined); 11 | - Verification script contains set of allowed licenses for directories to avoid legal issues. 12 | If you need to honor other license types, tests must be extended. 13 | - Immediately after SPDX header Copyright lines may come. There are multiple Copyrights lines 14 | allowed. 15 | - Only Intel copyright must follow strict style check. 16 | - The block must be ended by empty line. 17 | 18 | # Coding 19 | 20 | Commits must pass kernel [Checkpatch](https://docs.kernel.org/dev-tools/checkpatch.html). There are 21 | some excludes and it is automatically tested on Github. 22 | -------------------------------------------------------------------------------- /LICENSES/LICENSE.LGPL-2.1-or-later: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/ledmon/740a352626e2ed5d26389200f38323fde9bd84f1/LICENSES/LICENSE.LGPL-2.1-or-later -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-only 2 | # Copyright (C) 2009 Intel Corporation. 3 | 4 | ACLOCAL_AMFLAGS = -I m4 5 | 6 | if SYSTEMD_CONDITION 7 | OPTIONAL_SUBDIR = systemd 8 | endif 9 | 10 | if WITH_DOC 11 | DOC_SUBDIR = doc 12 | dist_doc_DATA = README.md 13 | endif 14 | 15 | SUBDIRS = src $(DOC_SUBDIR) $(OPTIONAL_SUBDIR) tests 16 | EXTRA_DIST = \ 17 | config/config.h \ 18 | systemd/ledmon.service.in \ 19 | ledmon.pc.in \ 20 | ledmon.pc 21 | 22 | if WITH_LIBRARY 23 | 24 | pkgconfigdir = $(libdir)/pkgconfig 25 | pkgconfig_DATA = ledmon.pc 26 | 27 | endif 28 | 29 | if WITH_TEST 30 | TESTS = tests/runtests.sh 31 | endif 32 | 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This package contains the Enclosure LED Utilities, version 1.1.0 2 | 3 | Copyright (C) 2009-2024 Intel Corporation. 4 | 5 | Files in this package can be freely distributed and used according 6 | to the terms of the GNU General Public License, version 2 or the 7 | GNU Lesser General Public License version 2.1 or later depending on file. 8 | 9 | See http://www.gnu.org/ for details. 10 | 11 | ------------------------- 12 | 13 | ## 1. Dependencies 14 | 15 | ------------------------- 16 | 17 | The following packages are required for compilation: 18 | 19 | |RHEL|SLES|Debian/Ubuntu| 20 | |:---:|:---:|:---:| 21 | | `pkgconf`, `RHEL7: pkgconfig` | `pkg-config` | `pkg-config` | 22 | | `automake` | `automake` | `automake` | 23 | | `autoconf` | `autoconf` | `autoconf` | 24 | | `autoconf-archive` | `autoconf-archive` | `autoconf-archive` | 25 | | `gcc` | `gcc` | `gcc` | 26 | | `libtool` | `libtool` | `libtool` | 27 | | `make` | `make` | `make` | 28 | | `sg3_utils-devel`| `libsgutils-devel` | `libsgutils2-dev` | 29 | | `systemd-devel` | `libudev-devel` | `libudev-dev` | 30 | | `pciutils-devel` | `pciutils-devel` | `libpci-dev` | 31 | | `check-devel` | `check-devel` | `check` | 32 | 33 | ## 2. Configure package 34 | 35 | ------------------------- 36 | 37 | Run `autogen.sh` to generate compilation configurations: 38 | `./autogen.sh` 39 | `./configure` 40 | 41 | Run `./configure` with: 42 | `--enable-systemd` to configure with systemd service. 43 | 44 | Run `./configure` with: 45 | `--enable-library` to enable building and installing the ledmon shared library, 46 | more library [information](src/lib/LIBRARY.md) 47 | 48 | Run `./configure` with: 49 | `--enable-test` to enable building unit tests and adds a target for `make check`, requires `--enable-library` 50 | 51 | Run `./configure` with: 52 | `--disable-doc` to disable building documentation. 53 | 54 | ## 3. Compiling the package 55 | 56 | ------------------------- 57 | 58 | Run `make` command to compile the package. 59 | 60 | ## 4. Installing or uninstalling the package 61 | 62 | ------------------------- 63 | 64 | Run the following command to install the package: 65 | `make install` 66 | 67 | Run the following command to uninstall the package: 68 | `make uninstall` 69 | 70 | ## 5. Release notes 71 | 72 | ------------------------- 73 | 74 | a. Enclosure LED Utilities are included as a part of RHEL, SLES and Debian/Ubuntu linux 75 | distributions. 76 | 77 | b. For backplane enclosures attached to an iSCI controller support is limited to 78 | Intel(R) Intelligent Backplane. 79 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | autoreconf --install 4 | -------------------------------------------------------------------------------- /config/config.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2022 Intel Corporation. 3 | 4 | #ifndef _CONFIG_H_INCLUDED_ 5 | #define _CONFIG_H_INCLUDED_ 6 | 7 | #include 8 | 9 | /** 10 | */ 11 | #define _HAVE_DMALLOC_H 0 12 | 13 | /** 14 | * @brief Sets errno variable and returns. 15 | * 16 | * This macro sets the errno variable and makes function return -1. 17 | * 18 | * @param[in] __val integer value to be set. 19 | * 20 | * @return The macro does not return a value. 21 | */ 22 | #define __set_errno_and_return(__val) { errno = (__val); return -1; } 23 | 24 | /** 25 | */ 26 | #define PATH_DELIM '/' 27 | 28 | /** 29 | */ 30 | #define PATH_DELIM_STR "/" 31 | 32 | /** 33 | */ 34 | #define PATH_SEP ':' 35 | 36 | /** 37 | */ 38 | #define PATH_SEP_STR ":" 39 | 40 | /** 41 | */ 42 | #define END_LINE ('\n') 43 | 44 | /** 45 | */ 46 | #define END_LINE_STR "\n" 47 | 48 | #endif /* _CONFIG_H_INCLUDED_ */ 49 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-3.0-or-later 2 | 3 | AC_PREREQ([2.69]) 4 | 5 | 6 | m4_define([MAJOR_VERSION], 1) 7 | m4_define([MINOR_VERSION], 1) 8 | m4_define([MICRO_VERSION], 0) 9 | 10 | AC_INIT([ledmon],[MAJOR_VERSION.MINOR_VERSION.MICRO_VERSION]) 11 | AC_CONFIG_MACRO_DIR([m4]) 12 | AC_SUBST([PACKAGE_DATE], "2024") 13 | AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects]) 14 | 15 | # If CFLAGS was not set, do not set default compiler flags, leave it empty. 16 | # https://www.gnu.org/savannah-checkouts/gnu/autoconf/manual/autoconf-2.72/autoconf.html#index-AC_005fPROG_005fCC-1 17 | : ${CFLAGS=""} 18 | 19 | LT_PREREQ([2.4.2]) 20 | AM_PROG_AR 21 | LT_INIT 22 | 23 | # Checks for programs. 24 | AC_PROG_CC[gcc clang] 25 | AX_CHECK_PROG([make]) 26 | 27 | 28 | # Using the version parts we will construct the appropriate libtool version. 29 | # Following semantic versioning (X.Y.Z) we will bump X when we have an incompatible 30 | # library version, bump Y when we add a new public function to the library and, bump 31 | # Z when we fix a bug/improve the library internals. We can only add to library, we 32 | # can never remove for backwards compatibility 33 | # eg. 34 | # start 0.97.0 35 | # Add 1 or more library functions, eg. led_foo and do a release -> 0.97.0 36 | # Fix 1 or more bugs in project and do a release -> 0.97.1 37 | # Fix 1 or more bugs and add API additions and do a release -> 0.98.0 38 | # 39 | # Full details for semantic versioning: 40 | # https://semver.org/ 41 | # 42 | 43 | CURRENT=`expr MAJOR_VERSION '*' 1 + MINOR_VERSION` 44 | AGE=MINOR_VERSION 45 | REVISION=MICRO_VERSION 46 | LIBLED_LIBTOOL_VERSION=$CURRENT:$REVISION:$AGE 47 | 48 | AC_SUBST([LIBLED_LIBTOOL_VERSION]) 49 | 50 | AC_PROG_CC_STDC 51 | AC_PROG_INSTALL 52 | 53 | AC_CONFIG_HEADERS([config_ac.h]) 54 | 55 | # Add compiler flags. 56 | AM_CFLAGS='-Wall -I../config -O2' 57 | AM_CPPFLAGS='-D_DEBUG -D_GNU_SOURCE -D_DEFAULT_SOURCE -DDMALLOC_DISABLE -DBUILD_LABEL=\""$(BUILD_LABEL)"\"' 58 | 59 | AC_DEFUN([AX_AM_CFLAGS_ADD],[AX_CHECK_LINK_FLAG($1, AM_CFLAGS="$AM_CFLAGS $1")]) 60 | AX_AM_CFLAGS_ADD([-Wformat -Wformat-security]) 61 | AX_AM_CFLAGS_ADD([-Wformat-overflow=2]) 62 | AX_AM_CFLAGS_ADD([-Wno-strict-overflow]) 63 | AX_AM_CFLAGS_ADD([-fno-delete-null-pointer-checks]) 64 | AX_AM_CFLAGS_ADD([-Wwrapv]) 65 | AX_AM_CFLAGS_ADD([-Wformat-truncation=1]) 66 | AX_AM_CFLAGS_ADD([-Wshift-negative-value]) 67 | AX_AM_CFLAGS_ADD([-Walloca]) 68 | AX_AM_CFLAGS_ADD([-Wmissing-field-initializers]) 69 | AX_AM_CFLAGS_ADD([-Wformat-signedness]) 70 | AX_AM_CFLAGS_ADD([-D_FORTIFY_SOURCE=2]) 71 | AX_AM_CFLAGS_ADD([-fstack-protector-strong]) 72 | AX_AM_CFLAGS_ADD([-fPIE]) 73 | 74 | # Add linker flags. 75 | AC_DEFUN([AX_AM_LDFLAGS_ADD],[AX_CHECK_LINK_FLAG($1, AM_LDFLAGS="$AM_LDFLAGS $1")]) 76 | AX_AM_LDFLAGS_ADD([-pie]) 77 | AX_AM_LDFLAGS_ADD([[-Wl,-z,relro]]) 78 | AX_AM_LDFLAGS_ADD([[-Wl,-z,now]]) 79 | AX_AM_LDFLAGS_ADD([[-Wl,-z,noexecstack]]) 80 | 81 | # Add library compiler flags. 82 | AM_LIB_CFLAGS='' 83 | AC_DEFUN([AX_AM_LIB_CFLAGS_ADD],[AX_CHECK_COMPILE_FLAG($1, AM_LIB_CFLAGS="$AM_LIB_CFLAGS $1")]) 84 | AX_AM_LIB_CFLAGS_ADD([-fPIC -shared]) 85 | AX_AM_LIB_CFLAGS_ADD([-fvisibility=hidden]) 86 | 87 | AC_SUBST([AM_CFLAGS]) 88 | AC_SUBST([AM_CPPFLAGS]) 89 | AC_SUBST([AM_LDFLAGS]) 90 | AC_SUBST([AM_LIB_CFLAGS]) 91 | AC_PREFIX_DEFAULT(/usr) 92 | 93 | # Automake 1.11 - silent build rules 94 | m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) 95 | 96 | PKG_PROG_PKG_CONFIG 97 | 98 | # Checks for libraries. 99 | AC_CHECK_LIB([rt], [shm_unlink], [], [AC_MSG_ERROR(librt not found)]) 100 | AC_CHECK_LIB([sgutils2], [sg_ll_send_diag], [], [AC_MSG_ERROR(libsgutils not found)]) 101 | PKG_CHECK_MODULES([LIBUDEV], [libudev]) 102 | PKG_CHECK_MODULES([LIBPCI], [libpci]) 103 | 104 | # Checks for header files. 105 | 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]) 106 | 107 | # Checks for typedefs, structures, and compiler characteristics. 108 | AC_CHECK_HEADER_STDBOOL 109 | AC_C_INLINE 110 | AC_TYPE_PID_T 111 | AC_TYPE_SIZE_T 112 | AC_CHECK_MEMBERS([struct stat.st_blksize]) 113 | AC_CHECK_MEMBERS([struct stat.st_rdev]) 114 | AC_TYPE_UINT16_T 115 | AC_TYPE_UINT32_T 116 | AC_TYPE_UINT64_T 117 | AC_TYPE_UINT8_T 118 | 119 | # Checks for library functions. 120 | AC_FUNC_FORK 121 | AC_FUNC_MALLOC 122 | AC_FUNC_MMAP 123 | AC_CHECK_FUNCS([ftruncate memset munmap realpath regcomp select strcasecmp strchr strdup strerror strrchr strstr strtol strtoul]) 124 | 125 | # configure options 126 | AC_ARG_ENABLE(systemd, AS_HELP_STRING([--enable-systemd], [install ledmon systemd service])) 127 | 128 | AS_IF([test "x$enable_systemd" = xyes], [SYSTEMD_STR=yes], [SYSTEMD_STR=no]) 129 | 130 | AM_CONDITIONAL([SYSTEMD_CONDITION], [test "$SYSTEMD_STR" = yes]) 131 | 132 | # target directory for ledmon service file 133 | SYSTEMD_PATH="$(pkg-config systemd --variable=systemdsystemunitdir)" 134 | AC_SUBST([SYSTEMD_PATH]) 135 | 136 | # Add configure option to build without library 137 | AC_ARG_ENABLE([library], 138 | [AS_HELP_STRING([--enable-library], 139 | [enable building ledmon library])], 140 | [with_library=${enableval}], 141 | [with_library=no]) 142 | 143 | AC_ARG_ENABLE([test], 144 | [AS_HELP_STRING([--enable-test], 145 | [enable test cases, run using 'make check', requires --enable-library])], 146 | [with_test=${enableval}], 147 | [with_test=no]) 148 | 149 | AM_CONDITIONAL([WITH_TEST], [test "x$with_test" = "xyes"]) 150 | 151 | if test "x${with_test}" = "xyes"; then 152 | PKG_CHECK_MODULES([LIBCHECK], [check >= 0.9.8 ]) 153 | # We need the library enabled if we are going to run the tests 154 | if test "x${with_library}" = "xno"; then 155 | AC_MSG_ERROR([--enable-library is required when specifying --enable-test]) 156 | fi 157 | AC_SUBST([ENABLE_TEST], '-DENABLE_TEST') 158 | fi 159 | 160 | AM_CONDITIONAL([WITH_LIBRARY], [test "x$with_library" = "xyes"]) 161 | 162 | # Add configure option to disable documentation building 163 | AC_ARG_ENABLE([doc], 164 | [AS_HELP_STRING([--disable-doc], 165 | [do not install ledmon documentation])], 166 | [with_doc=${enableval}], 167 | [with_doc=yes]) 168 | 169 | AM_CONDITIONAL([WITH_DOC], [test "x$with_doc" = "xyes"]) 170 | 171 | AC_CONFIG_FILES([Makefile 172 | ledmon.pc 173 | doc/Makefile 174 | src/Makefile 175 | src/common/Makefile 176 | src/lib/Makefile 177 | src/lib/include/Makefile 178 | src/lib/include/led/Makefile 179 | src/ledctl/Makefile 180 | src/ledmon/Makefile 181 | systemd/Makefile 182 | tests/Makefile]) 183 | AC_OUTPUT 184 | AC_MSG_RESULT([ 185 | $PACKAGE_NAME $VERSION configuration: 186 | 187 | Source code location: ${srcdir} 188 | Preprocessor flags: ${AM_CPPFLAGS} ${CPPFLAGS} ${ENABLE_TEST} 189 | C compiler flags: ${AM_CFLAGS} ${CFLAGS} 190 | Linker flags: ${AM_LDFLAGS} ${LDFLAGS} 191 | Common install location: ${prefix} 192 | configure parameters: --enable-systemd=${SYSTEMD_STR} --enable-library=${with_library} --enable-test=${with_test} --enable-doc=${with_doc} 193 | ]) 194 | -------------------------------------------------------------------------------- /doc/Makefile.am: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-only 2 | # Copyright (C) 2009 Intel Corporation. 3 | 4 | # Intel(R) Enclosure LED Utilities 5 | 6 | CLEANFILES = ledmon.conf.gz ledmon.gz ledctl.gz 7 | EXTRA_DIST = ledmon.conf.pod ledmon.pod ledctl.pod 8 | 9 | dist_man5_MANS = ledmon.conf.gz 10 | dist_man8_MANS = ledmon.gz ledctl.gz 11 | 12 | ledmon.conf.gz: ledmon.conf.pod 13 | pod2man -r "LEDMON.CONF Version $(PACKAGE_VERSION) $(BUILD_LABEL)" -d "@PACKAGE_DATE@" \ 14 | -s 5 -n ledmon.conf -c "Intel(R) Enclosure LED Utilities Config" $< | gzip -9f > $@ 15 | 16 | ledmon.gz: ledmon.pod 17 | pod2man -r "LEDMON Version $(PACKAGE_VERSION) $(BUILD_LABEL)" -d "@PACKAGE_DATE@" \ 18 | -s 8 -n ledmon -c "Intel(R) Enclosure LED Monitor Service" $< | gzip -9f > $@ 19 | 20 | ledctl.gz: ledctl.pod 21 | pod2man -r "LEDCTL Version $(PACKAGE_VERSION) $(BUILD_LABEL)" -d "@PACKAGE_DATE@" \ 22 | -s 8 -n ledctl -c "Intel(R) Enclosure LED Control Application" $< | gzip -9f > $@ 23 | -------------------------------------------------------------------------------- /doc/ledmon.conf.pod: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-only 2 | # Copyright (C) 2009 Intel Corporation. 3 | 4 | # Intel(R) Enclosure LED Utilities 5 | 6 | =head1 NAME 7 | 8 | ledmon.conf - Configuration file for Intel(R) Enclosure LED Utilities. 9 | 10 | =head1 DESCRIPTION 11 | 12 | The ledmon configuration file allows you to use advanced settings and functions 13 | of the Intel(R) Enclosure LED Utilities. The global location of the configuration 14 | file is F. Instead of a global configuration file, you can 15 | specify a local config file using the I<-c> option when running ledmon. 16 | 17 | =head2 SYNTAX 18 | 19 | One line should have exactly one option and value in the configuration file in 20 | format: OPTION=VALUE. Any word that begins with a hash sign (B<#>) is considered a 21 | comment and that word together with the remainder of the line is ignored. Empty 22 | lines are allowed. Either single quotes (B<'>) or double quotes (B<">) should 23 | not be used. 24 | 25 | Values are considered as true: enabled, true, yes, 1. 26 | 27 | Values are considered as false: disabled, false, no, 0. 28 | 29 | See also the examples section. 30 | 31 | =head2 List of configurable options: 32 | 33 | B - Ledmon will exclude scanning controllers listed on the excludelist. 34 | When allowlist is also set in config file, the excludelist will be ignored. 35 | The controllers should be separated by comma (B<,>) character. Only exact paths 36 | to the controller (e.g. /sys/devices/pci0000:c9/0000:c9:00.5) will be respected. 37 | The B are supported. The deprecated name of 38 | B is supported for backwards compatibility. It will be 39 | removed in a future release. 40 | 41 | B - Related with RAID Initialization (resync), Verify (check) 42 | and Verify and Fix (repair) processes. If value is set to true - status LEDs of 43 | all member drives will blink with proper pattern if RAID volume is under sync 44 | process. If value is set to false, processes like init or verifications will not 45 | be reported by LEDs. The default value is true. 46 | 47 | B - RAID can be migrated between some levels or strip sizes and 48 | the flag is related with these processes. Also, the RAID Grow operation will be 49 | reported along with this flag. If value is set to true - status LEDs of all 50 | member drives will blink with proper pattern if RAID volume is under reshape. 51 | If value is set to false, listed actions will not be reported by LEDs. The 52 | default value is true. 53 | 54 | B - The value is given in seconds. Defines time interval between 55 | ledmon sysfs scan. The minimum is 5 seconds the maximum is not specified. The 56 | default value is 10 seconds. 57 | 58 | B - Corresponds with I<--log-level> flag from ledmon. Log level QUIET 59 | means no logging at all and ALL means to log everything. The default log level 60 | is WARNING. Acceptable values are: quiet, error, warning, info, debug, all. 61 | Value also can be set by integer number - 0 means 'quiet' and 5 means 'all'. 62 | 63 | B - Sets a path to local log file. If this option is specified the 64 | global log file F is not used. 65 | 66 | B - If flag is set to true ledmon will limit monitoring only 67 | to drives that are RAID members. The default value is false. 68 | 69 | B - Flag is related with RAID rebuild process. When value 70 | is set to false - only the drive that the RAID is rebuilding to will be marked 71 | with appropriate LED pattern. If value is set to true all drives from RAID 72 | that is during rebuild will blink during this operation. 73 | 74 | B - Ledmon will limit changing LED state to controllers listed on 75 | allowlist. If any allowlist is set, only devices from the list will be scanned by 76 | ledmon. The controllers should be separated by a comma (B<,>) character. Only 77 | exact paths to the controller (e.g. /sys/devices/pci0000:c9/0000:c9:00.5) 78 | will be respected. 79 | The B are supported. The deprecated name of 80 | B is supported for backwards compatibility. It will be 81 | removed in a future release. 82 | 83 | =head1 EXAMPLES 84 | 85 | =head2 Excluding one controller from ledmon scans, changing log level and scans 86 | interval: 87 | 88 | LOG_LEVEL=all 89 | 90 | INTERVAL=5 91 | 92 | #Exclude disks from SATA controller 93 | 94 | EXCLUDELIST=/sys/devices/pci0000:00/0000:00:17.0 95 | 96 | =head2 Blink only on RAID members, blink on all disks during rebuild and ignore 97 | init phase: 98 | 99 | RAID_MEMBERS_ONLY=true 100 | 101 | BLINK_ON_INIT=false 102 | 103 | REBUILD_BLINK_ON_ALL=true 104 | 105 | 106 | =head1 LICENSE 107 | 108 | Copyright (c) 2009-2017 Intel Corporation. 109 | 110 | This program is distributed under the terms of the GNU General Public License 111 | as published by the Free Software Foundation. See the built-in help for details 112 | on the License and the lack of warranty. 113 | 114 | =head1 SEE ALSO 115 | 116 | ledmon(8), ledctl(8) 117 | 118 | =head1 AUTHOR 119 | 120 | This manual page was written by Michal Zylowski . It 121 | may be used by others. 122 | -------------------------------------------------------------------------------- /doc/ledmon.pod: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-only 2 | # Copyright (C) 2009 Intel Corporation. 3 | 4 | # Intel(R) Enclosure LED Utilities 5 | 6 | =head1 NAME 7 | 8 | ledmon - Intel(R) LED monitor service for storage enclosures. 9 | 10 | =head1 SYNOPSIS 11 | 12 | B [I] 13 | 14 | =head1 DESCRIPTION 15 | 16 | The ledmon application is a daemon process used to monitor the state(s) of 17 | software RAID devices (md only) or the state(s) of block devices. The state 18 | is visualized on LEDs associated with each slot in an enclosure or a 19 | drive bay. There are two types of systems: 2-LEDs systems (Activity LED, 20 | Status LED) and 3-LEDs systems (Activity LED, Locate LED, Fail 21 | LED). This application has the highest priority when accessing the 22 | LEDs. 23 | 24 | The ledmon application supports LED management of the SAS/SATA and PCIe 25 | storage. 26 | 27 | =head4 Supported protocols/methods for LED management are: 28 | 29 | =over 30 | 31 | =item 32 | 33 | B for SAS devices, 34 | 35 | =item 36 | 37 | B for SATA, 38 | 39 | =item 40 | 41 | B for PCIe. 42 | 43 | =back 44 | 45 | B protocol is not supported. 46 | 47 | For SAS/SATA supported storage, controllers may transmit LED management 48 | information to the backplane controllers via the SGPIO interface. The SGPIO 49 | bus carries bit patterns, which translate into LED blink patterns in 50 | accordance with the International Blinking Pattern Interpretation (IBPI) 51 | of SFF-8489 specification for SGPIO. 52 | Please note some enclosures do not stick closely to the SFF-8489 53 | specification. It might occur that the enclosure processor will accept 54 | the IBPI pattern but it will blink LEDs not according to SFF-8489 55 | specification. Or, it may have a limited number of patterns supported. 56 | 57 | For more information about communication methods please consult the 58 | appropriate Specifications. 59 | 60 | There's no method provided to specify which RAID volume should be monitored 61 | and which not. The ledmon application monitors all RAID devices and visualizes 62 | their state(s). 63 | 64 | The ledmon application has been verified to work with Intel(R) storage 65 | controllers (i.e. Intel(R) AHCI controller and Intel(R) SAS controller). 66 | The application might work with storage controllers of other vendors 67 | (especially SAS/SCSI controllers). However storage controllers of other 68 | vendors have not been tested. 69 | 70 | The ledmon application is part of Intel(R) Enclosure LED Utilities. Only 71 | single instance of the application is allowed. 72 | 73 | =head4 The ledmon utilizes the following documents as references: 74 | 75 | =over 76 | 77 | =item 78 | 79 | SGPIO (Serial GPIO) - SFF-8485 80 | 81 | =item 82 | 83 | IBPI (International Blinking Pattern Interpretation) - SFF-8489 84 | 85 | =item 86 | 87 | LED Enclosure management messages - AHCI specification rev 1.3, 88 | section 12.2.1. 89 | 90 | =item 91 | 92 | SAS (Serial Attached SCSI) - T10/1760-D 93 | 94 | =item 95 | 96 | SES-2 (SCSI Enclosure Services-2) - T10/1559-D 97 | 98 | =item 99 | 100 | SMP (Serial Management Protocol) - T10/1760-D 101 | 102 | =item 103 | 104 | NPEM (Native PCIe Enclosure Management) - PCIe base specification rev 4.0 105 | 106 | =item 107 | 108 | VMD (Intel(R) Volume Management Device) - Intel(R) VROC (VMD NVMe RAID) Quick 109 | Configuration Guide rev 1.2 110 | 111 | =back 112 | 113 | =head1 OPTIONS 114 | 115 | =over 8 116 | 117 | =item B<-c> or B<--config>=I 118 | 119 | Sets a path to local configuration file. If this option is specified the 120 | global configuration file and user configuration file has no effect. 121 | 122 | =item B<-l> or B<--log>=I 123 | 124 | Sets a path to local log file. If this option is specified the global log 125 | file F is not used. 126 | 127 | =item B<-t> or B<--interval>=I 128 | 129 | Sets time interval between scans of sysfs. The value is given in seconds. 130 | The minimum is 5 seconds the maximum is not specified. 131 | 132 | =item B<--quiet> or B<--error> or B<--warning> or B<--info> or B<--debug> or B<--all> 133 | 134 | Verbose level - 'quiet' means no logging at all and 'all' means to log 135 | everything. The levels are given in order. If user specifies more then one 136 | verbose option the last option comes into effect. The default level is 137 | 'warning'. Verbose level also can be set by B<--log-level>=I. 138 | 139 | =item B<--foreground> 140 | 141 | Run process foreground instead of a daemon. This option is useful in 142 | systemd service file. Another use case of this option is debugging with 143 | elevated B<--log-level>=I. 144 | 145 | =item B<-h> or B<--help> 146 | 147 | Prints this text out and exits. 148 | 149 | =item B<-v> or B<--version> 150 | 151 | Displays version of ledmon and information about the license and exits. 152 | 153 | =back 154 | 155 | =head1 FILES 156 | 157 | =over 8 158 | 159 | =item F 160 | 161 | Global log file, used by ledmon application. To force logging to user defined 162 | file use I<-l> option switch. 163 | 164 | =item F 165 | 166 | Global configuration file, shared between ledmon and all ledctl application 167 | instances. Local configuration file can be used by running ledmon with I<-c> 168 | switch. 169 | 170 | =back 171 | 172 | =head1 LICENSE 173 | 174 | Copyright (c) 2009-2024 Intel Corporation. 175 | 176 | This program is distributed under the terms of the GNU General Public License 177 | as published by the Free Software Foundation. See the build-in help for details 178 | on the License and the lack of warranty. 179 | 180 | =head1 BUGS 181 | 182 | The ledmon application does not recognize PFA state (Predicted Failure Analysis), 183 | hence the PFA pattern from SFF-8489 specification is not visualized. 184 | 185 | =head1 SEE ALSO 186 | 187 | ledctl(8), ledmon.conf(5) 188 | 189 | =head1 AUTHOR 190 | 191 | This manual page was written by Artur Wojcik . It may 192 | be used by others. 193 | -------------------------------------------------------------------------------- /ledmon.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@/led 5 | 6 | Name: libled 7 | Version: @VERSION@ 8 | Description: LED library 9 | Requires: 10 | Libs: -L${libdir} -lled @LIBS@ 11 | Cflags: -I${includedir} 12 | -------------------------------------------------------------------------------- /m4/m4_ax_check_compile_flag.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # Check whether the given FLAG works with the current language's compiler 12 | # or gives an error. (Warnings, however, are ignored) 13 | # 14 | # ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on 15 | # success/failure. 16 | # 17 | # If EXTRA-FLAGS is defined, it is added to the current language's default 18 | # flags (e.g. CFLAGS) when the check is done. The check is thus made with 19 | # the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to 20 | # force the compiler to issue an error when a bad flag is given. 21 | # 22 | # INPUT gives an alternative input source to AC_COMPILE_IFELSE. 23 | # 24 | # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this 25 | # macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. 26 | # 27 | # LICENSE 28 | # 29 | # Copyright (c) 2008 Guido U. Draheim 30 | # Copyright (c) 2011 Maarten Bosmans 31 | # 32 | # Copying and distribution of this file, with or without modification, are 33 | # permitted in any medium without royalty provided the copyright notice 34 | # and this notice are preserved. This file is offered as-is, without any 35 | # warranty. 36 | 37 | #serial 6 38 | 39 | AC_DEFUN([AX_CHECK_COMPILE_FLAG], 40 | [AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF 41 | AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl 42 | AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ 43 | ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS 44 | _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" 45 | AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], 46 | [AS_VAR_SET(CACHEVAR,[yes])], 47 | [AS_VAR_SET(CACHEVAR,[no])]) 48 | _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) 49 | AS_VAR_IF(CACHEVAR,yes, 50 | [m4_default([$2], :)], 51 | [m4_default([$3], :)]) 52 | AS_VAR_POPDEF([CACHEVAR])dnl 53 | ])dnl AX_CHECK_COMPILE_FLAGS 54 | -------------------------------------------------------------------------------- /m4/m4_check_if_prog_installed.m4: -------------------------------------------------------------------------------- 1 | # SYNOPSIS 2 | # 3 | # AX_CHECK_PROG 4 | # 5 | # DESCRIPTION 6 | # 7 | # Check if the given program installed, let script continue if exists, pops up 8 | # error message if not. 9 | # 10 | # Besides checking existence, this macro also set these environment 11 | # variables upon completion: 12 | # 13 | # PROG_"prog_name" = result of checking if program installed (yes/no) 14 | 15 | AC_DEFUN([AX_CHECK_PROG], 16 | [dnl 17 | AC_CHECK_PROG([PROG_$1], [$1], [yes], [no]) 18 | 19 | AS_IF([test "$PROG_$1" = "no"], 20 | [dnl 21 | AC_MSG_ERROR([Utility "$1" not found.]) 22 | ]) 23 | ]) -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | log_cli = true 3 | log_level = INFO -------------------------------------------------------------------------------- /security.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | Intel is committed to rapidly addressing security vulnerabilities affecting our customers and providing clear guidance on the solution, impact, severity and mitigation. 3 | 4 | ## Reporting a Vulnerability 5 | Please report any security vulnerabilities in this project [utilizing the guidelines here](https://www.intel.com/content/www/us/en/security-center/vulnerability-handling-guidelines.html). -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-only 2 | # Copyright (C) 2009 Intel Corporation. 3 | 4 | SUBDIRS=common lib ledctl ledmon 5 | -------------------------------------------------------------------------------- /src/common/Makefile.am: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-only 2 | # Copyright (C) 2009 Intel Corporation. 3 | 4 | # Make a convenience library for this code 5 | noinst_LTLIBRARIES = libcommon.la 6 | libcommon_la_SOURCES = config_file.h config_file.c 7 | libcommon_la_CFLAGS = -I$(top_srcdir)/src/lib/include -I$(top_srcdir)/src -I$(top_srcdir)/config \ 8 | $(AM_CFLAGS) $(AM_LIB_CFLAGS) $(LIBPCI_CFLAGS) 9 | -------------------------------------------------------------------------------- /src/common/config_file.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | // Copyright (C) 2017 Intel Corporation. 3 | // Copyright (C) 2009 Karel Zak 4 | 5 | /* 6 | * Contains code from util-linux/libblkid/src/config.c 7 | * originally released under LGPL. 8 | */ 9 | 10 | 11 | #ifndef SRC_CONFIG_FILE_H_ 12 | #define SRC_CONFIG_FILE_H_ 13 | 14 | #include 15 | 16 | #include 17 | #include 18 | 19 | #define LEDMON_SHARE_MEM_FILE "/ledmon.conf" 20 | #define LEDMON_DEF_CONF_FILE "/etc/ledmon.conf" 21 | #define LEDMON_DEF_LOG_FILE "/var/log/ledmon.log" 22 | #define LEDCTL_DEF_LOG_FILE "/var/log/ledctl.log" 23 | #define LEDMON_DEF_SLEEP_INTERVAL 10 24 | #define LEDMON_MIN_SLEEP_INTERVAL 5 25 | 26 | struct ledmon_conf { 27 | /* internal ledmon functions */ 28 | FILE *s_log; 29 | char *log_path; 30 | enum led_log_level_enum log_level; 31 | int scan_interval; 32 | 33 | /* customizable leds behaviour */ 34 | int blink_on_migration; 35 | int blink_on_init; 36 | int rebuild_blink_on_all; 37 | int raid_members_only; 38 | 39 | /* allowlist and excludelist of controllers for blinking */ 40 | struct list cntrls_allowlist; 41 | struct list cntrls_excludelist; 42 | }; 43 | 44 | int ledmon_read_conf(const char *filename, struct ledmon_conf *conf); 45 | int ledmon_write_shared_conf(struct ledmon_conf *conf); 46 | int ledmon_remove_shared_conf(void); 47 | 48 | 49 | int ledmon_init_conf(struct ledmon_conf *conf, enum led_log_level_enum lvl, const char *log_path); 50 | void ledmon_free_conf(struct ledmon_conf *conf); 51 | 52 | #endif /* SRC_CONFIG_FILE_H_ */ 53 | -------------------------------------------------------------------------------- /src/ledctl/Makefile.am: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-only 2 | # Copyright (C) 2009 Intel Corporation. 3 | 4 | sbin_PROGRAMS = ledctl 5 | ledctl_SOURCES = ledctl.c help.c help.h 6 | 7 | ledctl_LDADD = ../lib/libledinternal.la ../common/libcommon.la 8 | ledctl_CFLAGS = -I$(top_srcdir)/src/lib/include -I$(top_srcdir)/src -I$(top_srcdir)/config \ 9 | -I$(top_srcdir)/src/lib $(AM_CFLAGS) $(ENABLE_TEST) 10 | -------------------------------------------------------------------------------- /src/ledctl/help.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2024 Intel Corporation. 3 | 4 | #ifndef HELP_H_ 5 | #define HELP_H_ 6 | 7 | #include "utils.h" 8 | 9 | void print_mode_help(enum opt mode_id); 10 | void _print_incorrect_help_usage(void); 11 | void _print_main_help(void); 12 | void _ledctl_version(void); 13 | 14 | #endif // HELP_H_INCLUDED_ 15 | -------------------------------------------------------------------------------- /src/ledmon/Makefile.am: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-only 2 | # Copyright (C) 2009 Intel Corporation. 3 | 4 | sbin_PROGRAMS = ledmon 5 | 6 | ledmon_SOURCES = ledmon.c pidfile.h pidfile.c udev.c udev.h 7 | ledmon_LDADD = ../lib/libledinternal.la ../common/libcommon.la $(LIBUDEV_LIBS) 8 | ledmon_CFLAGS = -I$(top_srcdir)/src/lib/include -I$(top_srcdir)/src -I$(top_srcdir)/config \ 9 | -I$(top_srcdir)/src/lib $(AM_CFLAGS) $(LIBUDEV_CFLAGS) 10 | -------------------------------------------------------------------------------- /src/ledmon/pidfile.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | // Copyright (C) 2009 Intel Corporation. 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #if _HAVE_DMALLOC_H 16 | #include 17 | #endif 18 | 19 | #include "config.h" 20 | #include "pidfile.h" 21 | #include "status.h" 22 | #include 23 | 24 | #define RUN_DIR "/var/run/" 25 | #define PID ".pid" 26 | 27 | /** 28 | */ 29 | status_t pidfile_create(const char *name) 30 | { 31 | char buf[PATH_MAX]; 32 | int fd, count; 33 | 34 | snprintf(buf, sizeof(buf), "%s/%s%s", RUN_DIR, name, PID); 35 | 36 | fd = open(buf, O_WRONLY | O_CREAT, 0640); 37 | if (fd < 0) 38 | return STATUS_FILE_OPEN_ERROR; 39 | if (lockf(fd, F_TLOCK, 0) < 0) { 40 | close(fd); 41 | return STATUS_FILE_LOCK_ERROR; 42 | } 43 | snprintf(buf, PATH_MAX, "%d\n", getpid()); 44 | count = write(fd, buf, strlen(buf)); 45 | close(fd); 46 | return (count < 0) ? STATUS_FILE_WRITE_ERROR : STATUS_SUCCESS; 47 | } 48 | 49 | /** 50 | */ 51 | int pidfile_remove(const char *name) 52 | { 53 | char buf[PATH_MAX]; 54 | 55 | snprintf(buf, sizeof(buf), "%s/%s%s", RUN_DIR, name, PID); 56 | return unlink(buf); 57 | } 58 | 59 | /** 60 | * @brief Test whether process with given pid is still alive 61 | * 62 | * @return STATUS_SUCCESS if process is alive and other error if not or 63 | * if there is an error 64 | */ 65 | int ping_proc(pid_t pid) 66 | { 67 | if (pid <= 0) 68 | return STATUS_INVALID_PATH; 69 | if (kill(pid, 1) == 0) 70 | return STATUS_SUCCESS; 71 | return STATUS_INVALID_PATH; 72 | } 73 | 74 | /** 75 | */ 76 | status_t pidfile_check(const char *name, pid_t *pid) 77 | { 78 | char path[PATH_MAX], *p; 79 | char buf[BUF_SZ_NUM]; 80 | pid_t tp; 81 | 82 | snprintf(path, sizeof(path), "%s/%s%s", RUN_DIR, name, PID); 83 | 84 | p = buf_read_to_dest(path, buf, sizeof(buf)); 85 | if (p == NULL) 86 | return STATUS_INVALID_PATH; 87 | if (str_toi(&tp, p, NULL, 10) != 0) 88 | return STATUS_DATA_ERROR; 89 | if (pid) 90 | *pid = tp; 91 | 92 | return ping_proc(tp); 93 | } 94 | -------------------------------------------------------------------------------- /src/ledmon/pidfile.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | // Copyright (C) 2009 Intel Corporation. 3 | 4 | #ifndef _PIDFILE_H_INCLUDED_ 5 | #define _PIDFILE_H_INCLUDED_ 6 | 7 | #include 8 | 9 | #include "status.h" 10 | 11 | /** 12 | */ 13 | status_t pidfile_create(const char *name); 14 | 15 | /** 16 | */ 17 | int pidfile_remove(const char *name); 18 | 19 | /** 20 | */ 21 | status_t pidfile_check(const char *name, pid_t *pid); 22 | 23 | #endif /* _PIDFILE_H_INCLUDED_ */ 24 | -------------------------------------------------------------------------------- /src/ledmon/udev.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2022 Intel Corporation. 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "block.h" 10 | #include "led/libled.h" 11 | #include "status.h" 12 | #include "sysfs.h" 13 | #include "udev.h" 14 | #include 15 | 16 | extern struct ledmon_conf conf; 17 | 18 | static struct udev_monitor *udev_monitor; 19 | 20 | static int _compare(const struct block_device *bd, const char *syspath, struct led_ctx *ctx) 21 | { 22 | if (!bd || !syspath) 23 | return 0; 24 | 25 | if (strcmp(bd->sysfs_path, syspath) == 0) { 26 | return 1; 27 | } else { 28 | struct block_device *bd_new; 29 | int ret; 30 | 31 | bd_new = block_device_init(sysfs_get_cntrl_devices(ctx), syspath); 32 | if (!bd_new) 33 | return 0; 34 | 35 | ret = block_compare(bd, bd_new); 36 | block_device_fini(bd_new); 37 | 38 | return ret; 39 | } 40 | } 41 | 42 | static int create_udev_monitor(void) 43 | { 44 | int res; 45 | struct udev *udev = udev_new(); 46 | 47 | if (!udev) { 48 | log_error("Failed to create udev context instance."); 49 | return -1; 50 | } 51 | 52 | udev_monitor = udev_monitor_new_from_netlink(udev, "udev"); 53 | if (!udev_monitor) { 54 | log_error("Failed to create udev monitor object."); 55 | udev_unref(udev); 56 | return -1; 57 | } 58 | 59 | res = udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, 60 | "block", "disk"); 61 | if (res < 0) { 62 | log_error("Failed to modify udev monitor filters."); 63 | stop_udev_monitor(); 64 | return -1; 65 | } 66 | 67 | res = udev_monitor_enable_receiving(udev_monitor); 68 | if (res < 0) { 69 | log_error("Failed to switch udev monitor to listening mode."); 70 | stop_udev_monitor(); 71 | return -1; 72 | } 73 | 74 | return udev_monitor_get_fd(udev_monitor); 75 | } 76 | 77 | void stop_udev_monitor(void) 78 | { 79 | if (udev_monitor) { 80 | struct udev *udev = udev_monitor_get_udev(udev_monitor); 81 | 82 | udev_monitor_unref(udev_monitor); 83 | 84 | if (udev) 85 | udev_unref(udev); 86 | } 87 | } 88 | 89 | int get_udev_monitor(void) 90 | { 91 | if (udev_monitor) 92 | return udev_monitor_get_fd(udev_monitor); 93 | 94 | return create_udev_monitor(); 95 | } 96 | 97 | static int _check_raid(const char *path) 98 | { 99 | char *t = strrchr(path, '/'); 100 | 101 | if (t == NULL) 102 | return 0; 103 | return strncmp(t + 1, "md", 2) == 0; 104 | } 105 | 106 | static enum udev_action _get_udev_action(const char *action) 107 | { 108 | enum udev_action ret = UDEV_ACTION_UNKNOWN; 109 | 110 | if (strncmp(action, "add", 3) == 0) 111 | ret = UDEV_ACTION_ADD; 112 | else if (strncmp(action, "remove", 6) == 0) 113 | ret = UDEV_ACTION_REMOVE; 114 | return ret; 115 | } 116 | 117 | static void _clear_raid_dev_info(struct block_device *block, char *raid_dev) 118 | { 119 | if (block->raid_dev && block->raid_dev->sysfs_path) { 120 | char *tmp = strrchr(block->raid_dev->sysfs_path, '/'); 121 | 122 | if (tmp == NULL) { 123 | log_error( 124 | "Device: %s have wrong raid_dev path: %s", 125 | block->sysfs_path, 126 | block->raid_dev->sysfs_path); 127 | return; 128 | } 129 | if (strcmp(raid_dev, tmp + 1) == 0) { 130 | log_debug( 131 | "CLEAR raid_dev %s in %s ", 132 | raid_dev, block->sysfs_path); 133 | raid_device_fini(block->raid_dev); 134 | block->raid_dev = NULL; 135 | } 136 | } 137 | 138 | } 139 | 140 | int handle_udev_event(struct list *ledmon_block_list, struct led_ctx *ctx) 141 | { 142 | struct udev_device *dev; 143 | int status = -1; 144 | 145 | dev = udev_monitor_receive_device(udev_monitor); 146 | if (dev) { 147 | const char *action = udev_device_get_action(dev); 148 | enum udev_action act = _get_udev_action(action); 149 | const char *syspath = udev_device_get_syspath(dev); 150 | struct block_device *block = NULL; 151 | 152 | if (act == UDEV_ACTION_UNKNOWN) { 153 | status = 1; 154 | goto exit; 155 | } 156 | 157 | list_for_each(ledmon_block_list, block) { 158 | if (_compare(block, syspath, ctx)) 159 | break; 160 | block = NULL; 161 | } 162 | 163 | if (!block) { 164 | if (act == UDEV_ACTION_REMOVE && _check_raid(syspath)) { 165 | /*ledmon is interested about removed arrays*/ 166 | char *dev_name; 167 | 168 | dev_name = strrchr(syspath, '/') + 1; 169 | log_debug("REMOVED %s", dev_name); 170 | list_for_each(ledmon_block_list, block) 171 | _clear_raid_dev_info(block, dev_name); 172 | status = 0; 173 | goto exit; 174 | } 175 | status = 1; 176 | goto exit; 177 | } 178 | 179 | if (act == UDEV_ACTION_ADD) { 180 | log_debug("ADDED %s", block->sysfs_path); 181 | if (block->ibpi == LED_IBPI_PATTERN_FAILED_DRIVE || 182 | block->ibpi == LED_IBPI_PATTERN_REMOVED || 183 | block->ibpi == LED_IBPI_PATTERN_UNKNOWN) 184 | block->ibpi = LED_IBPI_PATTERN_ADDED; 185 | } else if (act == UDEV_ACTION_REMOVE) { 186 | log_debug("REMOVED %s", block->sysfs_path); 187 | block->ibpi = LED_IBPI_PATTERN_REMOVED; 188 | } else { 189 | /* not interesting event */ 190 | status = 1; 191 | goto exit; 192 | } 193 | status = 0; 194 | } else { 195 | return -1; 196 | } 197 | 198 | exit: 199 | udev_device_unref(dev); 200 | return status; 201 | } 202 | -------------------------------------------------------------------------------- /src/ledmon/udev.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2022 Intel Corporation. 3 | 4 | #ifndef _UDEV_H_INCLUDED_ 5 | #define _UDEV_H_INCLUDED_ 6 | 7 | #include 8 | #include "sysfs.h" 9 | 10 | /** 11 | */ 12 | enum udev_action { 13 | UDEV_ACTION_UNKNOWN = 0, 14 | UDEV_ACTION_ADD, 15 | UDEV_ACTION_REMOVE 16 | }; 17 | 18 | /** 19 | * @brief Deletes udev context and udev monitor. 20 | * 21 | * @return The function does not return a value.. 22 | */ 23 | void stop_udev_monitor(void); 24 | 25 | /** 26 | * @brief Returns udev monitor file descriptor or creates udev context and 27 | * udev monitor if monitor does not exist. 28 | * 29 | * @return Udev monitor file descriptor if successful, otherwise the 30 | * function returns -1 on libudev error and libudev sets errno. 31 | */ 32 | int get_udev_monitor(void); 33 | 34 | /** 35 | * @brief Handles udev event. 36 | * 37 | * This function checks event type and if it is 'add' or remove 38 | * function sets custom IBPI pattern to block device which is affected 39 | * by this event. 40 | * 41 | * @param[in] ledmon_block_list list containing block devices, it is 42 | * used to match device from udev event. 43 | * @param[in] ctx Library context. 44 | * 45 | * @return 0 if 'add' or 'remove' event handled successfully; 46 | * 1 if registered event is not 'add' or 'remove'; 47 | * -1 on libudev error. 48 | */ 49 | int handle_udev_event(struct list *ledmon_block_list, struct led_ctx *ctx); 50 | 51 | #endif /* _UDEV_H_INCLUDED_ */ 52 | -------------------------------------------------------------------------------- /src/lib/LIBRARY.md: -------------------------------------------------------------------------------- 1 | ## Using the ledmon LED library 2 | A library is built when enabled using `--enable-library` during `./configure` and can be used to add LED control to your existing applications. Assuming the library 3 | is installed, follow the following steps. 4 | 5 | 1. Make edits to your application, this simple source example shows the key things needed to use the library. 6 | 7 | 8 | ```c 9 | #include 10 | #include 11 | 12 | // Include the library header 13 | #include 14 | 15 | 16 | // Use the slot getter functions to retrieve information about a slot 17 | static inline void print_slot(struct led_slot_list_entry *s) 18 | { 19 | printf("cntrl type: %d: slot: %-15s led state: %-15d device: %-15s\n", 20 | led_slot_cntrl(s), led_slot_id(s), led_slot_state(s), led_slot_device(s)); 21 | } 22 | 23 | int main() { 24 | struct led_slot_list_entry *slot = NULL; 25 | struct led_ctx *ctx = NULL; 26 | struct led_slot_list *slot_list = NULL; 27 | led_status_t status; 28 | 29 | // Create a context 30 | status = led_new(&ctx); 31 | if (LED_STATUS_SUCCESS != status){ 32 | printf("Failed to initialize library\n"); 33 | return 1; 34 | } 35 | 36 | // Run a scan to find all supported hardware 37 | status = led_scan(ctx); 38 | if (LED_STATUS_SUCCESS != status){ 39 | printf("led_scan failed %d\n", status); 40 | return 1; 41 | } 42 | 43 | // Retrieve slots that support LED management 44 | status = led_slots_get(ctx, &slot_list); 45 | if (LED_STATUS_SUCCESS != status) { 46 | printf("led_slots_get failed %d\n", status); 47 | return 1; 48 | } 49 | 50 | printf("Printing slots in order returned\n"); 51 | while ((slot = led_slot_next(slot_list))) { 52 | print_slot(slot); 53 | } 54 | 55 | // Free the slot list 56 | led_slot_list_free(slot_list); 57 | 58 | // Free the library context 59 | led_free(ctx); 60 | return 0; 61 | } 62 | ``` 63 | 64 | 2. Compile and link 65 | 66 | ```bash 67 | $ gcc -Wall `pkg-config --cflags --libs ledmon` list_slots.c -o list_slots 68 | ``` 69 | 70 | 3. Run 71 | 72 | ```bash 73 | $ ./list_slots 74 | Printing slots in order returned 75 | cntrl type: 3: slot: /dev/sg43-0 led state: 2 device: /dev/sdw 76 | cntrl type: 3: slot: /dev/sg43-1 led state: 2 device: /dev/sdx 77 | cntrl type: 3: slot: /dev/sg43-2 led state: 2 device: /dev/sdy 78 | cntrl type: 3: slot: /dev/sg43-3 led state: 2 device: /dev/sdz 79 | cntrl type: 3: slot: /dev/sg43-4 led state: 2 device: /dev/sdaa 80 | cntrl type: 3: slot: /dev/sg43-5 led state: 2 device: /dev/sdab 81 | cntrl type: 3: slot: /dev/sg43-6 led state: 2 device: /dev/sdac 82 | cntrl type: 3: slot: /dev/sg43-7 led state: 2 device: /dev/sdad 83 | cntrl type: 3: slot: /dev/sg43-8 led state: 2 device: /dev/sdae 84 | cntrl type: 3: slot: /dev/sg43-9 led state: 2 device: /dev/sdaf 85 | cntrl type: 3: slot: /dev/sg43-10 led state: 2 device: /dev/sdag 86 | cntrl type: 3: slot: /dev/sg43-11 led state: 2 device: /dev/sdah 87 | cntrl type: 3: slot: /dev/sg43-12 led state: 2 device: /dev/sdai 88 | cntrl type: 3: slot: /dev/sg43-13 led state: 2 device: /dev/sdaj 89 | cntrl type: 3: slot: /dev/sg43-14 led state: 2 device: /dev/sdak 90 | cntrl type: 3: slot: /dev/sg43-15 led state: 2 device: /dev/sdal 91 | $ 92 | ``` 93 | 94 | 4. For more information on API, please review the [led/libled.h](include/led/libled.h) header file. 95 | -------------------------------------------------------------------------------- /src/lib/Makefile.am: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-only 2 | # Copyright (C) 2009 Intel Corporation. 3 | 4 | # Intel(R) Enclosure LED Utilities 5 | 6 | SUBDIRS = include 7 | 8 | LIB_SRCS = ahci.c block.c cntrl.c enclosure.c utils.c list.c \ 9 | raid.c scsi.c tail.c sysfs.c smp.c dellssd.c \ 10 | pci_slot.c vmdssd.c amd.c amd_sgpio.c amd_ipmi.c\ 11 | ipmi.c npem.c ses.c slot.c \ 12 | ahci.h amd_sgpio.h block.h cntrl.h dellssd.h utils.h \ 13 | enclosure.h list.h pci_slot.h raid.h scsi.h \ 14 | ses.h tail.h smp.h status.h sysfs.h \ 15 | vmdssd.h ipmi.h amd.h amd_ipmi.h npem.h libled_internal.c \ 16 | slot.h libled_private.h libled_internal.h 17 | 18 | # Make a convenience library, to be used for led tools and the shared library 19 | noinst_LTLIBRARIES = libledinternal.la 20 | libledinternal_la_SOURCES = libled.c $(LIB_SRCS) 21 | libledinternal_la_LIBADD = $(LIBPCI_LIBS) ../common/libcommon.la 22 | libledinternal_la_CFLAGS = -I$(top_srcdir)/src/lib/include -I$(top_srcdir)/src \ 23 | -I$(top_srcdir)/config $(AM_CFLAGS) $(AM_LIB_CFLAGS) $(LIBPCI_CFLAGS) 24 | 25 | # User may not want the shared library 26 | if WITH_LIBRARY 27 | 28 | lib_LTLIBRARIES = libled.la 29 | libled_la_SOURCES = 30 | libled_la_LIBADD = libledinternal.la 31 | libled_la_LDFLAGS = -version-info $(LIBLED_LIBTOOL_VERSION) 32 | 33 | endif 34 | -------------------------------------------------------------------------------- /src/lib/ahci.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2022 Intel Corporation. 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #if _HAVE_DMALLOC_H 13 | #include 14 | #endif 15 | 16 | #include "ahci.h" 17 | #include "config.h" 18 | #include "utils.h" 19 | #include "libled_private.h" 20 | 21 | /** 22 | * Time interval in nano seconds to wait before enclosure management message 23 | * is being sent to AHCI controller. 24 | */ 25 | #define EM_MSG_WAIT 1500000 /* 0.0015 seconds */ 26 | 27 | /** 28 | * This array maps IBPI pattern to value recognized by AHCI driver. The driver 29 | * uses this control number to issue SGPIO signals appropriately. 30 | */ 31 | static const struct ibpi2value ibpi2sgpio[] = { 32 | {LED_IBPI_PATTERN_NORMAL, 0x00000000}, 33 | {LED_IBPI_PATTERN_ONESHOT_NORMAL, 0x00000000}, 34 | {LED_IBPI_PATTERN_REBUILD, 0x00480000}, 35 | {LED_IBPI_PATTERN_FAILED_DRIVE, 0x00400000}, 36 | {LED_IBPI_PATTERN_LOCATE, 0x00080000}, 37 | {LED_IBPI_PATTERN_LOCATE_OFF, 0x00000000}, 38 | #ifdef DEBUG_IBPI 39 | {LED_IBPI_PATTERN_DEGRADED, 0x00200000}, 40 | {LED_IBPI_PATTERN_FAILED_ARRAY, 0x00280000}, 41 | {LED_IBPI_PATTERN_HOTSPARE, 0x01800000}, 42 | {LED_IBPI_PATTERN_PFA, 0x01400000}, 43 | #endif 44 | {LED_IBPI_PATTERN_UNKNOWN, 0x00000000}, 45 | }; 46 | 47 | /* 48 | * The function sends a LED control message to AHCI controller. It uses 49 | * SGPIO to control the LEDs. See ahci.h for details. 50 | */ 51 | status_t ahci_sgpio_write(struct block_device *device, enum led_ibpi_pattern ibpi) 52 | { 53 | char temp[WRITE_BUFFER_SIZE]; 54 | char path[PATH_MAX]; 55 | char *sysfs_path = device->cntrl_path; 56 | const struct timespec waittime = { 57 | .tv_sec = 0, 58 | .tv_nsec = EM_MSG_WAIT 59 | }; 60 | const struct ibpi2value *ibpi2val; 61 | 62 | /* write only if state has changed */ 63 | if (ibpi == device->ibpi_prev) 64 | return STATUS_SUCCESS; 65 | 66 | if (sysfs_path == NULL) 67 | return STATUS_NULL_POINTER; 68 | if ((ibpi < LED_IBPI_PATTERN_NORMAL) || (ibpi > LED_IBPI_PATTERN_LOCATE_OFF)) 69 | return STATUS_INVALID_STATE; 70 | 71 | ibpi2val = get_by_ibpi(ibpi, ibpi2sgpio, ARRAY_SIZE(ibpi2sgpio)); 72 | 73 | if (ibpi2val->ibpi == LED_IBPI_PATTERN_UNKNOWN) { 74 | lib_log(device->cntrl->ctx, LED_LOG_LEVEL_INFO, 75 | "AHCI: Controller doesn't support %s pattern\n", ibpi2str(ibpi)); 76 | return STATUS_INVALID_STATE; 77 | } 78 | 79 | snprintf(temp, WRITE_BUFFER_SIZE, "%u", ibpi2val->value); 80 | 81 | snprintf(path, sizeof(path), "%s/em_message", sysfs_path); 82 | 83 | nanosleep(&waittime, NULL); 84 | 85 | if (buf_write(path, temp) != (ssize_t) strnlen(temp, WRITE_BUFFER_SIZE)) { 86 | lib_log(device->cntrl->ctx, LED_LOG_LEVEL_ERROR, 87 | "AHCI: %s write error: %d\n", path, errno); 88 | return STATUS_FILE_WRITE_ERROR; 89 | } 90 | return STATUS_SUCCESS; 91 | } 92 | 93 | #define SCSI_HOST "/scsi_host" 94 | /* 95 | * The function return path to SATA port in sysfs tree. See ahci.h for details. 96 | */ 97 | 98 | char *ahci_get_port_path(const char *path) 99 | { 100 | char *target_p, *host_p; 101 | size_t host_length, length_to_target; 102 | char *buf; 103 | 104 | host_p = strstr(path, "/host"); 105 | if (host_p == NULL) 106 | return NULL; 107 | 108 | target_p = strstr(host_p, "/target"); 109 | if (target_p == NULL) 110 | return NULL; 111 | 112 | length_to_target = target_p - path; 113 | host_length = target_p - host_p; 114 | 115 | if (host_length + length_to_target + strlen(SCSI_HOST) > PATH_MAX - 1) 116 | return NULL; 117 | 118 | buf = calloc(PATH_MAX, sizeof(char)); 119 | if (buf == NULL) 120 | return NULL; 121 | 122 | strncpy(buf, path, length_to_target); 123 | strncat(buf, SCSI_HOST, strlen(SCSI_HOST) + 1); 124 | strncat(buf, host_p, host_length); 125 | return buf; 126 | } 127 | -------------------------------------------------------------------------------- /src/lib/ahci.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2022 Intel Corporation. 3 | 4 | #ifndef _AHCI_H_INCLUDED_ 5 | #define _AHCI_H_INCLUDED_ 6 | 7 | #include "block.h" 8 | #include "led/libled.h" 9 | 10 | /** 11 | * @brief Gets sysfs path to SATA port. 12 | * 13 | * The function returns a path to SATA port in sysfs tree the given block device 14 | * is connected to. 15 | * 16 | * @param[in] path Path to block device in sysfs tree. 17 | * 18 | * @return Canonical path if successful, otherwise NULL pointer if an error occurred. 19 | */ 20 | char *ahci_get_port_path(const char *path); 21 | 22 | /** 23 | * @brief Sends LED control message using SGPIO. 24 | * 25 | * This function visualizes IBPI pattern on LEDs associated with a slot in 26 | * drive bay. This function is designed to send messaged to AHCI controller 27 | * only. 28 | * 29 | * @param[in] path Path in sysfs tree to slot in drive bay. 30 | * @param[in] ibpi IBPI pattern to visualize on LEDs associated 31 | * with the given slot. 32 | * 33 | * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. 34 | */ 35 | status_t ahci_sgpio_write(struct block_device *path, enum led_ibpi_pattern ibpi); 36 | 37 | #endif /* _AHCI_H_INCLUDED_ */ 38 | -------------------------------------------------------------------------------- /src/lib/amd.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2023, Advanced Micro Devices, Inc. 3 | 4 | /* AMD LED control */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #if _HAVE_DMALLOC_H 22 | #include 23 | #endif 24 | 25 | #include "config.h" 26 | #include "led/libled.h" 27 | #include "list.h" 28 | #include "utils.h" 29 | #include "amd.h" 30 | #include "amd_sgpio.h" 31 | #include "amd_ipmi.h" 32 | #include "libled_private.h" 33 | 34 | enum amd_led_interfaces amd_interface = AMD_INTF_UNSET; 35 | enum amd_ipmi_platforms amd_ipmi_platform = AMD_PLATFORM_UNSET; 36 | 37 | int _find_file_path(const char *start_path, const char *filename, 38 | char *path, size_t path_len, struct led_ctx *ctx) 39 | { 40 | int rc, found; 41 | struct stat sbuf; 42 | struct list dir; 43 | char *dir_name; 44 | const char *dir_path; 45 | 46 | rc = scan_dir(start_path, &dir); 47 | if (rc) { 48 | lib_log(ctx, LED_LOG_LEVEL_INFO, "Failed to scan %s", start_path); 49 | return 0; 50 | } 51 | 52 | found = 0; 53 | list_for_each(&dir, dir_path) { 54 | dir_name = strrchr(dir_path, '/'); 55 | if (!dir_name) 56 | continue; 57 | 58 | /* skip past the leading '/' */ 59 | dir_name++; 60 | 61 | if (strncmp(dir_name, filename, strlen(filename)) == 0) { 62 | char tmp[PATH_MAX + 1]; 63 | 64 | strncpy(tmp, dir_path, path_len); 65 | snprintf(path, path_len, "%s", dirname(tmp)); 66 | 67 | found = 1; 68 | break; 69 | } 70 | 71 | if (lstat(dir_path, &sbuf) == -1) 72 | continue; 73 | 74 | if (S_ISDIR(sbuf.st_mode)) { 75 | found = _find_file_path(dir_path, filename, 76 | path, path_len, ctx); 77 | if (found) 78 | break; 79 | } 80 | } 81 | 82 | list_erase(&dir); 83 | return found; 84 | } 85 | 86 | /* For AMD platforms to use IPMI for LED control we need to know 87 | * the platform we're running on. This enables us to select the 88 | * proper channel and tail address when making IPMI requests. 89 | * Platforms not checked for IPMI enablement default to using SGPIO. 90 | */ 91 | int amd_em_enabled(const char *path, struct led_ctx *ctx) 92 | { 93 | char *platform; 94 | int rc; 95 | char buf[BUF_SZ_SM]; 96 | 97 | /* Default to SGPIO interface */ 98 | amd_interface = AMD_INTF_SGPIO; 99 | 100 | platform = get_text_to_dest("/sys/class/dmi/id", "product_name", buf, sizeof(buf)); 101 | if (!platform) 102 | return 0; 103 | 104 | /* Check IPMI platforms */ 105 | if (!strncmp(platform, "ETHANOL_X", 9)) { 106 | amd_interface = AMD_INTF_IPMI; 107 | amd_ipmi_platform = AMD_PLATFORM_ETHANOL_X; 108 | } else if (!strncmp(platform, "DAYTONA_X", 9)) { 109 | amd_interface = AMD_INTF_IPMI; 110 | amd_ipmi_platform = AMD_PLATFORM_DAYTONA_X; 111 | } 112 | 113 | switch (amd_interface) { 114 | case AMD_INTF_SGPIO: 115 | rc = _amd_sgpio_em_enabled(path, ctx); 116 | break; 117 | case AMD_INTF_IPMI: 118 | rc = _amd_ipmi_em_enabled(path, ctx); 119 | break; 120 | default: 121 | lib_log(ctx, LED_LOG_LEVEL_ERROR, 122 | "Unknown interface for AMD %s platform\n", platform); 123 | rc = -EOPNOTSUPP; 124 | break; 125 | } 126 | 127 | return rc; 128 | } 129 | 130 | status_t amd_write(struct block_device *device, enum led_ibpi_pattern ibpi) 131 | { 132 | /* write only if state has changed */ 133 | if (ibpi == device->ibpi_prev) 134 | return STATUS_SUCCESS; 135 | 136 | switch (amd_interface) { 137 | case AMD_INTF_SGPIO: 138 | return _amd_sgpio_write(device, ibpi); 139 | case AMD_INTF_IPMI: 140 | return _amd_ipmi_write(device, ibpi); 141 | case AMD_INTF_UNSET: 142 | default: 143 | lib_log(device->cntrl->ctx, LED_LOG_LEVEL_ERROR, 144 | "Unsupported AMD interface %u\n", amd_interface); 145 | return STATUS_FILE_WRITE_ERROR; 146 | } 147 | } 148 | 149 | char *amd_get_path(const char *cntrl_path, const char *sysfs_path, struct led_ctx *ctx) 150 | { 151 | char *path; 152 | 153 | switch (amd_interface) { 154 | case AMD_INTF_SGPIO: 155 | path = _amd_sgpio_get_path(sysfs_path, ctx); 156 | break; 157 | case AMD_INTF_IPMI: 158 | path = _amd_ipmi_get_path(cntrl_path, sysfs_path); 159 | break; 160 | case AMD_INTF_UNSET: 161 | default: 162 | lib_log(ctx, LED_LOG_LEVEL_ERROR, "Unsupported AMD interface\n"); 163 | path = NULL; 164 | break; 165 | } 166 | 167 | return path; 168 | } 169 | -------------------------------------------------------------------------------- /src/lib/amd.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2023, Advanced Micro Devices, Inc. 3 | 4 | /* AMD LED control */ 5 | 6 | #include "block.h" 7 | #include "sysfs.h" 8 | 9 | enum amd_device_type {AMD_NO_DEVICE, AMD_SATA_DEVICE, AMD_NVME_DEVICE}; 10 | 11 | struct amd_drive { 12 | int ata_port; 13 | int port; 14 | int drive_bay; 15 | int initiator; 16 | uint8_t channel; 17 | uint8_t tail_addr; 18 | enum amd_device_type dev; 19 | struct led_ctx *ctx; 20 | }; 21 | 22 | enum amd_led_interfaces { 23 | AMD_INTF_UNSET, 24 | AMD_INTF_SGPIO, 25 | AMD_INTF_IPMI, 26 | }; 27 | 28 | extern enum amd_led_interfaces amd_interface; 29 | 30 | enum amd_ipmi_platforms { 31 | AMD_PLATFORM_UNSET, 32 | AMD_PLATFORM_ETHANOL_X, 33 | AMD_PLATFORM_DAYTONA_X, 34 | }; 35 | 36 | extern enum amd_ipmi_platforms amd_ipmi_platform; 37 | 38 | int amd_em_enabled(const char *path, struct led_ctx *ctx); 39 | status_t amd_write(struct block_device *device, enum led_ibpi_pattern ibpi); 40 | char *amd_get_path(const char *cntrl_path, const char *sysfs_path, struct led_ctx *ctx); 41 | 42 | int _find_file_path(const char *start_path, const char *filename, 43 | char *path, size_t path_len, struct led_ctx *ctx); 44 | 45 | /* Register dump formats used for debug output */ 46 | #define REG_FMT_2 "%23s: %-4x%23s: %-4x\n" 47 | #define REG_FMT_1 "%23s: %-4x\n" 48 | -------------------------------------------------------------------------------- /src/lib/amd_ipmi.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2023, Advanced Micro Devices, Inc. 3 | 4 | /* AMD IPMI LED control */ 5 | 6 | #include "block.h" 7 | 8 | int _amd_ipmi_em_enabled(const char *path, struct led_ctx *ctx); 9 | status_t _amd_ipmi_write(struct block_device *device, enum led_ibpi_pattern ibpi); 10 | char *_amd_ipmi_get_path(const char *cntrl_path, const char *sysfs_path); 11 | -------------------------------------------------------------------------------- /src/lib/amd_sgpio.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2023, Advanced Micro Devices, Inc. 3 | 4 | /* AMD SGPIO LED control */ 5 | 6 | #ifndef _AMD_SGPIO_INCLUDED_ 7 | #define _AMD_SGPIO_INCLUDED_ 8 | 9 | #include "block.h" 10 | #include "led/libled.h" 11 | 12 | #include 13 | 14 | typedef uint8_t drive_led_t; 15 | 16 | struct drive_leds { 17 | drive_led_t error; 18 | drive_led_t locate; 19 | drive_led_t activity; 20 | } __attribute__ ((__packed__)); 21 | 22 | struct cache_entry { 23 | struct drive_leds leds[4]; 24 | uint8_t blink_gen_a; 25 | uint8_t blink_gen_b; 26 | uint16_t reserved; 27 | } __attribute__ ((__packed__)); 28 | 29 | int _amd_sgpio_em_enabled(const char *path, struct led_ctx *ctx); 30 | status_t _amd_sgpio_write(struct block_device *device, enum led_ibpi_pattern ibpi); 31 | char *_amd_sgpio_get_path(const char *cntrl_path, struct led_ctx *ctx); 32 | 33 | void amd_sgpio_cache_free(struct led_ctx *ctx); 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /src/lib/block.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2022 Intel Corporation. 3 | 4 | #ifndef _BLOCK_H_INCLUDED_ 5 | #define _BLOCK_H_INCLUDED_ 6 | 7 | #include 8 | 9 | #include "cntrl.h" 10 | #include "led/libled.h" 11 | #include "time.h" 12 | #include "list.h" 13 | #include "raid.h" 14 | #include "status.h" 15 | #include "sysfs.h" 16 | 17 | struct block_device; 18 | 19 | /** 20 | * @brief Describes a block device. 21 | * 22 | * This structure describes a block device. It does not describe virtual devices 23 | * or partitions on physical block devices. 24 | */ 25 | struct block_device { 26 | /** 27 | * Real path in sysfs tree. This means i.e. if /sys/block/sda is symbolic link 28 | * then the link will be read and path stored in sysfs_path field. This path 29 | * may not exist in sysfs if connection to physical drive is lost. This filed 30 | * cannot have NULL pointer assigned. 31 | */ 32 | char *sysfs_path; 33 | 34 | /** 35 | * Main devnode we can reach this device. It may not match /sys/dev/block/MAJ:MIN 36 | * Could be empty. 37 | */ 38 | char devnode[PATH_MAX]; 39 | 40 | /** 41 | * The pointer to a function which sends a message to driver in order to 42 | * control LEDs in an enclosure or DAS system. 43 | * Params: 44 | * device - pointer to a block device. 45 | * ibpi - an IBPI pattern (state) to visualize. 46 | * Return: STATUS_SUCCESS if successful, otherwise a valid status_t status code. 47 | */ 48 | status_t (*send_message_fn)(struct block_device *device, enum led_ibpi_pattern ibpi); 49 | 50 | /** 51 | * The pointer to a function which flush buffers filled by send_message_fn. 52 | * Params: 53 | * device - pointer to a block device. 54 | * Return 1 if successful, otherwise the function returns 0. 55 | */ 56 | int (*flush_message_fn)(struct block_device *device); 57 | 58 | /** 59 | * Canonical path to block device where enclosure management fields are located. 60 | * This path is always accessible even if the connection to physical device 61 | * is lost. In case of AHCI controller it points to SATA phy. In case of SAS 62 | * this path points to SES entry associated with the slot in an enclosure. 63 | * This field cannot have NULL pointer assign. 64 | */ 65 | char *cntrl_path; 66 | 67 | /** 68 | * The current state of block device. This is an IBPI pattern and it is used 69 | * to visualize the state of block device. 70 | */ 71 | enum led_ibpi_pattern ibpi; 72 | 73 | /** 74 | * The previous state of block device. 75 | */ 76 | enum led_ibpi_pattern ibpi_prev; 77 | 78 | /** 79 | * The time stamp used to determine if the given block device still exist or 80 | * it failed and the device is no longer available. Every time IBPI pattern 81 | * is updated, the time-stamp is updated, too. 82 | */ 83 | time_t timestamp; 84 | 85 | /** 86 | * The pointer to storage controller structure the device is connected to. 87 | */ 88 | struct cntrl_device *cntrl; 89 | 90 | struct _host_type *host; 91 | 92 | int host_id; 93 | 94 | /** 95 | * The index of phy utilized by directly attached to controller block device. 96 | * It is meaningful if device is controlled by isci driver. 97 | */ 98 | int phy_index; 99 | 100 | /** 101 | * The index in Enclosure. This is what should be used when using SES-2. 102 | */ 103 | int encl_index; 104 | 105 | struct enclosure_device *enclosure; 106 | 107 | /** 108 | * If disk is a raid member, this field will be set with a copy of raid device 109 | * struct. 110 | */ 111 | struct raid_device *raid_dev; 112 | }; 113 | 114 | /** 115 | * @brief Creates a block device structure. 116 | * 117 | * This function allocates memory for a new structure of block device. It reads 118 | * the sysfs entries and populates the structure fields. It performs all this 119 | * actions only if the block device is connected to the one of supported storage 120 | * controllers and the controller has enclosure management services enabled. 121 | * 122 | * @param[in] cntrl_list pointer to a list of supported controller 123 | * devices. 124 | * @param[in] sysfs_path a path to block device in sysfs. 125 | * 126 | * @return Pointer to block device structure if successful, otherwise the function 127 | * returns the NULL pointer. 128 | */ 129 | struct block_device *block_device_init(const struct list *cntrl_list, const char *path); 130 | 131 | /** 132 | * @brief Releases a block device structure. 133 | * 134 | * This function releases memory allocated for block device structure. 135 | * 136 | * @param[in] device pointer to block device structure. 137 | * 138 | * @return The function does not return a value. 139 | */ 140 | void block_device_fini(struct block_device *device); 141 | 142 | /** 143 | * @brief Duplicates a block device structure. 144 | * 145 | * The function allocates memory for a block device structure and copies values 146 | * stored in fields of source block structure. The function allocates new memory 147 | * for all string fields in a copy structure. It is safe to release source block 148 | * structure just after it has been duplicated. 149 | * 150 | * @param[in] device pointer to source block device structure. 151 | * 152 | * @return Pointer to block device structure if successful, otherwise the function 153 | * returns the NULL pointer. 154 | */ 155 | struct block_device *block_device_duplicate(struct block_device *device); 156 | 157 | /** 158 | * @brief Determines a storage controller. 159 | * 160 | * This is the internal function of 'block device' module. The function gets 161 | * a pointer to controller structure the device is connected to. 162 | * 163 | * @param[in] cntrl_list pointer to list of supported controllers. 164 | * @param[in] path path to block device in sysfs tree. 165 | * 166 | * @return Pointer to controller structure if successful, otherwise the function 167 | * returns NULL pointer. The NULL pointer means that block devices is 168 | * connected to unsupported storage controller. 169 | */ 170 | struct cntrl_device *block_get_controller(const struct list *cntrl_list, char *path); 171 | 172 | /** 173 | * The global timestamp variable. It is updated every time the sysfs is scanning 174 | * by an application. The value stored in this variable should be used to update 175 | * all timestamp stored in block device structures. 176 | */ 177 | extern time_t timestamp; 178 | 179 | /** 180 | * @brief Determines if block device is attached directly or via expander 181 | */ 182 | int dev_directly_attached(const char *path); 183 | 184 | /** 185 | * @brief Gets the host structure for given control device and host_id 186 | */ 187 | struct _host_type *block_get_host(struct cntrl_device *cntrl, int host_id); 188 | 189 | 190 | /** 191 | * @brief Checks the presence of block device. 192 | * 193 | * This is internal function of monitor service. The function is checking 194 | * whether block device is already on the list or it is missing from the list. 195 | * The function is design to be used as 'test' parameter for list_find_first() 196 | * function. 197 | * 198 | * @param[in] bd_old - an element from a list to compare to. 199 | * @param[in] bd_new - a block device being searched. 200 | * 201 | * @return 0 if the block devices do not match, otherwise function returns 1. 202 | */ 203 | int block_compare(const struct block_device *bd_old, 204 | const struct block_device *bd_new); 205 | 206 | /** 207 | * @brief Finds block device which name contains sub-path. 208 | * 209 | * This function scans block devices and checks their sysfs path 210 | * to find any which contains PCI address specified for device in path. 211 | * The character after the sub_path match is required to be one of the 212 | * following ('\n', '\0', '/'), otherwise it is excluded. 213 | * 214 | * @param[in] ctx Library context. 215 | * @param[in] sub_path Sub path. 216 | * @param[in] sub_path_to_end True if sub_path is complete path. 217 | * 218 | * @return first block device containing sub-path if any, otherwise NULL. 219 | */ 220 | struct block_device *get_block_device_from_sysfs_path(struct led_ctx *ctx, char *sub_path, 221 | bool sub_path_to_end); 222 | 223 | #endif /* _BLOCK_H_INCLUDED_ */ 224 | -------------------------------------------------------------------------------- /src/lib/cntrl.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2022 Intel Corporation. 3 | 4 | #ifndef _CNTRL_H_INCLUDED_ 5 | #define _CNTRL_H_INCLUDED_ 6 | 7 | #include "sysfs.h" 8 | #include "led/libled.h" 9 | 10 | #include 11 | 12 | /** 13 | * @brief Storage controller device structure. 14 | * 15 | * This structure describes a storage controller device existing in the system. 16 | */ 17 | struct cntrl_device { 18 | /** 19 | * Path to the device in sysfs tree. 20 | */ 21 | char sysfs_path[PATH_MAX]; 22 | 23 | /** 24 | * Type of storage controller device. 25 | */ 26 | enum led_cntrl_type cntrl_type; 27 | 28 | /** 29 | * Flag if scsi controller driver is "isci" 30 | */ 31 | int isci_present; 32 | 33 | /** 34 | * Applicable to VMD controllers. Domain address. 35 | */ 36 | char domain[PATH_MAX]; 37 | 38 | struct _host_type { 39 | /** 40 | * ibpi state buffer for directly attached devices 41 | */ 42 | struct gpio_tx_register_byte *ibpi_state_buffer; 43 | /** 44 | * outbound raw byte stream 45 | */ 46 | unsigned char bitstream[4]; 47 | /** 48 | * bitstream's flush flag 49 | */ 50 | int flush; 51 | /** 52 | * host identifier for different hba instances 53 | */ 54 | int host_id; 55 | /** 56 | * number of total phy ports 57 | */ 58 | int ports; 59 | /** 60 | * pointer to next structure 61 | */ 62 | struct _host_type *next; 63 | } *hosts; 64 | 65 | struct led_ctx *ctx; 66 | }; 67 | 68 | /** 69 | * @brief Allocates a new controller device structure. 70 | * 71 | * This function allocates memory for a new structure of storage controller 72 | * device. It reads the sysfs entries and populates structure fields. 73 | * The function registers only supported storage controllers. 74 | * 75 | * @param[in] path path to storage controller in sysfs tree. 76 | * @param[in] ctx library context. 77 | * 78 | * @return Pointer to storage controller structure if successful, otherwise the 79 | * function returns NULL pointer. The NULL pointer means that controller 80 | * device is not supported. 81 | */ 82 | struct cntrl_device *cntrl_device_init(const char *path, struct led_ctx *ctx); 83 | 84 | /** 85 | * @brief Releases a controller device structure. 86 | * 87 | * This function releases memory allocated for controller device structure. 88 | * 89 | * @param[in] device pointer to controller device structure. 90 | * 91 | * @return The function does not return a value. 92 | */ 93 | void cntrl_device_fini(struct cntrl_device *device); 94 | 95 | #endif /* _CNTRL_H_INCLUDED_ */ 96 | -------------------------------------------------------------------------------- /src/lib/dellssd.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2023, Dell Inc. 3 | 4 | /* Dell Backplane LED control */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | #if _HAVE_DMALLOC_H 19 | #include 20 | #endif 21 | 22 | #include "libled_private.h" 23 | #include "ahci.h" 24 | #include "cntrl.h" 25 | #include "config.h" 26 | #include "dellssd.h" 27 | #include "led/libled.h" 28 | #include "list.h" 29 | #include "raid.h" 30 | #include "scsi.h" 31 | #include "tail.h" 32 | #include "smp.h" 33 | #include "status.h" 34 | #include "sysfs.h" 35 | #include "utils.h" 36 | #include "ipmi.h" 37 | 38 | #define BP_PRESENT (1L << 0) 39 | #define BP_ONLINE (1L << 1) 40 | #define BP_HOTSPARE (1L << 2) 41 | #define BP_IDENTIFY (1L << 3) 42 | #define BP_REBUILDING (1L << 4) 43 | #define BP_FAULT (1L << 5) 44 | #define BP_PREDICT (1L << 6) 45 | #define BP_CRITICALARRAY (1L << 9) 46 | #define BP_FAILEDARRAY (1L << 10) 47 | 48 | static const struct ibpi2value ibpi2ssd[] = { 49 | {LED_IBPI_PATTERN_NORMAL, BP_ONLINE}, 50 | {LED_IBPI_PATTERN_ONESHOT_NORMAL, BP_ONLINE}, 51 | {LED_IBPI_PATTERN_DEGRADED, BP_CRITICALARRAY | BP_ONLINE}, 52 | {LED_IBPI_PATTERN_HOTSPARE, BP_HOTSPARE | BP_ONLINE}, 53 | {LED_IBPI_PATTERN_REBUILD, BP_REBUILDING | BP_ONLINE}, 54 | {LED_IBPI_PATTERN_FAILED_ARRAY, BP_FAILEDARRAY | BP_ONLINE}, 55 | {LED_IBPI_PATTERN_PFA, BP_PREDICT | BP_ONLINE}, 56 | {LED_IBPI_PATTERN_FAILED_DRIVE, BP_FAULT | BP_ONLINE}, 57 | {LED_IBPI_PATTERN_LOCATE, BP_IDENTIFY | BP_ONLINE}, 58 | {LED_IBPI_PATTERN_LOCATE_OFF, BP_ONLINE}, 59 | {LED_IBPI_PATTERN_UNKNOWN, 0} 60 | }; 61 | 62 | #define DELL_OEM_NETFN 0x30 63 | 64 | #define DELL_OEM_STORAGE_CMD 0xD5 65 | #define DELL_OEM_STORAGE_GETDRVMAP_12G 0x07 66 | #define DELL_OEM_STORAGE_SETDRVSTATUS_12G 0x04 67 | #define DELL_OEM_STORAGE_GETDRVMAP_13G 0x17 68 | #define DELL_OEM_STORAGE_SETDRVSTATUS_13G 0x14 69 | #define DELL_OEM_STORAGE_GETDRVMAP_14G 0x37 70 | #define DELL_OEM_STORAGE_SETDRVSTATUS_14G 0x34 71 | 72 | #define APP_NETFN 0x06 73 | #define APP_GET_SYSTEM_INFO 0x59 74 | #define DELL_GET_IDRAC_INFO 0xDD 75 | 76 | enum { 77 | DELL_12G_MONOLITHIC = 0x10, 78 | DELL_12G_MODULAR = 0x11, 79 | DELL_13G_MONOLITHIC = 0x20, 80 | DELL_13G_MODULAR = 0x21, 81 | DELL_14G_MONOLITHIC = 0x30, 82 | DELL_14G_MODULAR = 0x31, 83 | DELL_15G_MONOLITHIC = 0x40, 84 | DELL_15G_MODULAR = 0x41, 85 | 86 | }; 87 | 88 | int get_dell_server_type(struct led_ctx *ctx) 89 | { 90 | uint8_t data[4], rdata[20]; 91 | int rc, rlen; 92 | 93 | /* Don't requery if we already know have ID */ 94 | if (ctx->dellssd_hw_gen) 95 | return ctx->dellssd_hw_gen; 96 | 97 | /* Get Dell Generation */ 98 | memset(data, 0, sizeof(data)); 99 | memset(rdata, 0, sizeof(rdata)); 100 | data[0] = 0x00; 101 | data[1] = DELL_GET_IDRAC_INFO; 102 | data[2] = 0x02; 103 | data[3] = 0x00; 104 | rc = ipmicmd(ctx, BMC_TA, 0, APP_NETFN, APP_GET_SYSTEM_INFO, 4, data, 105 | 20, &rlen, rdata); 106 | if (rc) { 107 | lib_log(ctx, LED_LOG_LEVEL_DEBUG, "Unable to issue IPMI command GetSystemInfo\n"); 108 | return 0; 109 | } 110 | switch (rdata[10]) { 111 | case DELL_12G_MONOLITHIC: 112 | case DELL_12G_MODULAR: 113 | case DELL_13G_MONOLITHIC: 114 | case DELL_13G_MODULAR: 115 | case DELL_14G_MONOLITHIC: 116 | case DELL_14G_MODULAR: 117 | case DELL_15G_MONOLITHIC: 118 | case DELL_15G_MODULAR: 119 | 120 | ctx->dellssd_hw_gen = rdata[10]; 121 | return ctx->dellssd_hw_gen; 122 | default: 123 | lib_log(ctx, LED_LOG_LEVEL_DEBUG, "Unable to determine Dell Server type\n"); 124 | break; 125 | } 126 | return 0; 127 | } 128 | 129 | static status_t ipmi_setled(struct led_ctx *ctx, int b, int d, int f, int state) 130 | { 131 | uint8_t data[20], rdata[20]; 132 | int rc, rlen, bay = 0xFF, slot = 0xFF, devfn, gen = 0; 133 | 134 | /* Check if this is a supported Dell server */ 135 | gen = get_dell_server_type(ctx); 136 | if (!gen) 137 | return STATUS_FILE_WRITE_ERROR; 138 | devfn = (((d & 0x1F) << 3) | (f & 0x7)); 139 | 140 | /* Get mapping of BDF to bay:slot */ 141 | memset(data, 0, sizeof(data)); 142 | memset(rdata, 0, sizeof(rdata)); 143 | data[0] = 0x01; /* get */ 144 | data[2] = 0x06; /* length lsb */ 145 | data[3] = 0x00; /* length msb */ 146 | data[4] = 0x00; /* offset lsb */ 147 | data[5] = 0x00; /* offset msb */ 148 | data[6] = b; /* bus */ 149 | data[7] = devfn; /* devfn */ 150 | switch (gen) { 151 | case DELL_12G_MONOLITHIC: 152 | case DELL_12G_MODULAR: 153 | data[1] = DELL_OEM_STORAGE_GETDRVMAP_12G; 154 | break; 155 | case DELL_13G_MONOLITHIC: 156 | case DELL_13G_MODULAR: 157 | data[1] = DELL_OEM_STORAGE_GETDRVMAP_13G; 158 | break; 159 | case DELL_14G_MONOLITHIC: 160 | case DELL_14G_MODULAR: 161 | case DELL_15G_MONOLITHIC: 162 | case DELL_15G_MODULAR: 163 | 164 | data[1] = DELL_OEM_STORAGE_GETDRVMAP_14G; 165 | break; 166 | } 167 | rc = ipmicmd(ctx, BMC_TA, 0, DELL_OEM_NETFN, DELL_OEM_STORAGE_CMD, 8, data, 168 | 20, &rlen, rdata); 169 | if (!rc) { 170 | bay = rdata[7]; 171 | slot = rdata[8]; 172 | } 173 | if (bay == 0xFF || slot == 0xFF) { 174 | lib_log(ctx, LED_LOG_LEVEL_ERROR, 175 | "Unable to determine bay/slot for device %.2x:%.2x.%x\n", 176 | (unsigned int)b, (unsigned int)d, (unsigned int)f); 177 | return STATUS_FILE_WRITE_ERROR; 178 | } 179 | 180 | /* Set Bay:Slot to Mask */ 181 | memset(data, 0, sizeof(data)); 182 | memset(rdata, 0, sizeof(rdata)); 183 | data[0] = 0x00; /* set */ 184 | data[2] = 0x0e; /* length lsb */ 185 | data[3] = 0x00; /* length msb */ 186 | data[4] = 0x00; /* offset lsb */ 187 | data[5] = 0x00; /* offset msb */ 188 | data[6] = 0x0e; /* length lsb */ 189 | data[7] = 0x00; /* length msb */ 190 | data[8] = bay; /* bayid */ 191 | data[9] = slot; /* slotid */ 192 | data[10] = state & 0xff; /* state LSB */ 193 | data[11] = state >> 8; /* state MSB */ 194 | switch (gen) { 195 | case DELL_12G_MONOLITHIC: 196 | case DELL_12G_MODULAR: 197 | data[1] = DELL_OEM_STORAGE_SETDRVSTATUS_12G; 198 | break; 199 | case DELL_13G_MONOLITHIC: 200 | case DELL_13G_MODULAR: 201 | data[1] = DELL_OEM_STORAGE_SETDRVSTATUS_13G; 202 | break; 203 | case DELL_14G_MONOLITHIC: 204 | case DELL_14G_MODULAR: 205 | case DELL_15G_MONOLITHIC: 206 | case DELL_15G_MODULAR: 207 | 208 | data[1] = DELL_OEM_STORAGE_SETDRVSTATUS_14G; 209 | break; 210 | } 211 | rc = ipmicmd(ctx, BMC_TA, 0, DELL_OEM_NETFN, DELL_OEM_STORAGE_CMD, 20, data, 212 | 20, &rlen, rdata); 213 | if (rc) { 214 | lib_log(ctx, LED_LOG_LEVEL_ERROR, 215 | "Unable to issue SetDriveState for %.2x:%.2x.%x\n", 216 | (unsigned int)b, (unsigned int)d, (unsigned int)f); 217 | return STATUS_FILE_WRITE_ERROR; 218 | } 219 | return STATUS_SUCCESS; 220 | } 221 | 222 | char *dellssd_get_path(const char *cntrl_path) 223 | { 224 | return strdup(cntrl_path); 225 | } 226 | 227 | status_t dellssd_write(struct block_device *device, enum led_ibpi_pattern ibpi) 228 | { 229 | unsigned int bus, dev, fun; 230 | char *t; 231 | const struct ibpi2value *ibpi2val; 232 | 233 | /* write only if state has changed */ 234 | if (ibpi == device->ibpi_prev) 235 | return STATUS_SUCCESS; 236 | 237 | if ((ibpi < LED_IBPI_PATTERN_NORMAL) || (ibpi > LED_IBPI_PATTERN_LOCATE_OFF)) 238 | return STATUS_INVALID_STATE; 239 | 240 | ibpi2val = get_by_ibpi(ibpi, ibpi2ssd, ARRAY_SIZE(ibpi2ssd)); 241 | 242 | if (ibpi2val->ibpi == LED_IBPI_PATTERN_UNKNOWN) { 243 | 244 | lib_log(device->cntrl->ctx, LED_LOG_LEVEL_INFO, 245 | "SSD: Controller doesn't support %s pattern\n", ibpi2str(ibpi)); 246 | return STATUS_INVALID_STATE; 247 | } 248 | 249 | t = strrchr(device->cntrl_path, '/'); 250 | 251 | /* Extract PCI bus:device.function */ 252 | if (t == NULL || sscanf(t + 1, "%*x:%x:%x.%x", &bus, &dev, &fun) != 3) 253 | return STATUS_DATA_ERROR; 254 | 255 | return ipmi_setled(device->cntrl->ctx, bus, dev, fun, ibpi2val->value); 256 | } 257 | -------------------------------------------------------------------------------- /src/lib/dellssd.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2023, Dell Inc. 3 | 4 | /* Dell Backplane LED control */ 5 | 6 | #include "block.h" 7 | #include "sysfs.h" 8 | 9 | status_t dellssd_write(struct block_device *device, enum led_ibpi_pattern ibpi); 10 | char *dellssd_get_path(const char *cntrl_path); 11 | int get_dell_server_type(struct led_ctx *ctx); 12 | -------------------------------------------------------------------------------- /src/lib/enclosure.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2022 Intel Corporation. 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #if _HAVE_DMALLOC_H 16 | #include 17 | #endif 18 | 19 | #include "config.h" 20 | #include "enclosure.h" 21 | #include "scsi.h" 22 | #include "sysfs.h" 23 | #include "utils.h" 24 | #include "libled_private.h" 25 | 26 | /** 27 | * @brief Gets SAS address of an enclosure device. 28 | * 29 | * This is internal function of enclosure module. The function reads a 30 | * SAS address of an enclosure from sysfs attribute. 31 | * 32 | * @param[in] path Path to enclosure device in sysfs tree. 33 | * 34 | * @return SAS address of an enclosure if successful, otherwise 0. 35 | */ 36 | #define SAS_DEVICE "/sas_device" 37 | static uint64_t _get_sas_address(const char *path) 38 | { 39 | char buf[PATH_MAX]; 40 | char *p, *s; 41 | char *tmp = strdup(path); 42 | 43 | if (!tmp) 44 | return 0; 45 | 46 | p = strstr(tmp, "/expander"); 47 | if (p == NULL) 48 | goto out; 49 | s = strchr(p + 1, PATH_DELIM); 50 | if (s == NULL) 51 | goto out; 52 | *s = '\0'; 53 | snprintf(buf, sizeof(buf), "%s%s%s", tmp, SAS_DEVICE, p); 54 | free(tmp); 55 | return get_uint64(buf, 0, "sas_address"); 56 | 57 | out: 58 | free(tmp); 59 | return 0; 60 | } 61 | 62 | #define SCSI_GEN "device/scsi_generic" 63 | 64 | static char *_get_dev_sg(const char *encl_path) 65 | { 66 | char *ret = NULL; 67 | DIR *d; 68 | struct dirent *de; 69 | size_t sg_path_size = strlen(encl_path) + strlen(SCSI_GEN) + 2; 70 | char *sg_path = malloc(sg_path_size); 71 | 72 | if (!sg_path) 73 | return NULL; 74 | 75 | snprintf(sg_path, sg_path_size, "%s/%s", encl_path, SCSI_GEN); 76 | 77 | /* /sys/class/enclosure/X/device/scsi_generic path is expected. */ 78 | 79 | d = opendir(sg_path); 80 | free(sg_path); 81 | if (!d) 82 | return NULL; 83 | while ((de = readdir(d))) { 84 | if ((strcmp(de->d_name, ".") == 0) || 85 | (strcmp(de->d_name, "..") == 0)) 86 | continue; 87 | break; 88 | } 89 | if (de) { 90 | size_t size = strlen("/dev/") + strlen(de->d_name) + 1; 91 | ret = malloc(size); 92 | if (ret) 93 | snprintf(ret, size, "/dev/%s", de->d_name); 94 | } 95 | closedir(d); 96 | return ret; 97 | } 98 | 99 | static int slot_array_offset_to_ses_id(struct enclosure_device *encl, int slot_array_idx) 100 | { 101 | if (encl && encl->slots && (slot_array_idx >= 0 && slot_array_idx < encl->slots_count)) 102 | return encl->slots[slot_array_idx].index; 103 | return -1; 104 | } 105 | 106 | static struct ses_slot *find_enclosure_slot_by_index(struct enclosure_device *encl, int ses_index) 107 | { 108 | for (int i = 0; i < encl->slots_count; i++) { 109 | if (encl->slots[i].index == ses_index) 110 | return &encl->slots[i]; 111 | } 112 | return NULL; 113 | } 114 | 115 | static struct block_device *enclosure_get_block_device(struct enclosure_device *encl, int ses_index) 116 | { 117 | struct ses_slot *s_slot = find_enclosure_slot_by_index(encl, ses_index); 118 | 119 | if (!s_slot) 120 | return NULL; 121 | 122 | return locate_block_by_sas_addr(encl->ctx, s_slot->sas_addr); 123 | } 124 | 125 | /* 126 | * Re-loads the ses hardware state for this enclosure, to allow refreshing the 127 | * state after the hardware has be written. 128 | */ 129 | int enclosure_reload(struct enclosure_device * enclosure) 130 | { 131 | int fd, ret; 132 | 133 | fd = enclosure_open(enclosure); 134 | if (fd == -1) { 135 | return 1; 136 | } 137 | 138 | ret = ses_load_pages(fd, &enclosure->ses_pages, enclosure->ctx); 139 | close(fd); 140 | if (ret != 0) 141 | return ret; 142 | 143 | ret = ses_get_slots(&enclosure->ses_pages, &enclosure->slots, &enclosure->slots_count); 144 | if (ret) 145 | return ret; 146 | 147 | /* If there is an associated block device with a slot, we need to update the block ibpi */ 148 | for (int i = 0; i < enclosure->slots_count; i++) { 149 | struct block_device *bd = NULL; 150 | struct ses_slot *s_slot = NULL; 151 | int ses_id = slot_array_offset_to_ses_id(enclosure, i); 152 | 153 | if (ses_id == -1) 154 | continue; 155 | 156 | bd = enclosure_get_block_device(enclosure, ses_id); 157 | if (!bd) 158 | continue; 159 | 160 | s_slot = find_enclosure_slot_by_index(enclosure, ses_id); 161 | if (!s_slot) 162 | continue; 163 | 164 | bd->ibpi_prev = bd->ibpi; 165 | bd->ibpi = s_slot->ibpi_status; 166 | 167 | } 168 | return 0; 169 | } 170 | 171 | /* 172 | * Allocates memory for enclosure device structure and initializes fields of 173 | * the structure. 174 | */ 175 | struct enclosure_device *enclosure_device_init(const char *path, struct led_ctx *ctx) 176 | { 177 | char temp[PATH_MAX] = "\0"; 178 | struct enclosure_device *enclosure; 179 | int ret; 180 | 181 | if (!realpath(path, temp)) 182 | return NULL; 183 | 184 | enclosure = calloc(1, sizeof(struct enclosure_device)); 185 | if (enclosure == NULL) { 186 | ret = 1; 187 | goto out; 188 | } 189 | 190 | memccpy(enclosure->sysfs_path, temp, '\0', PATH_MAX - 1); 191 | enclosure->sas_address = _get_sas_address(temp); 192 | enclosure->dev_path = _get_dev_sg(temp); 193 | enclosure->ctx = ctx; 194 | 195 | ret = enclosure_reload(enclosure); 196 | out: 197 | if (ret) { 198 | lib_log(ctx, LED_LOG_LEVEL_WARNING, 199 | "failed to initialize enclosure_device %s\n", path); 200 | enclosure_device_fini(enclosure); 201 | enclosure = NULL; 202 | } 203 | return enclosure; 204 | } 205 | 206 | /* 207 | * The function returns memory allocated for fields of enclosure structure to 208 | * the system. 209 | */ 210 | void enclosure_device_fini(struct enclosure_device *enclosure) 211 | { 212 | if (enclosure) { 213 | free(enclosure->slots); 214 | free(enclosure->dev_path); 215 | free(enclosure); 216 | } 217 | } 218 | 219 | int enclosure_open(const struct enclosure_device *enclosure) 220 | { 221 | int fd = -1; 222 | 223 | if (enclosure->dev_path) 224 | fd = open(enclosure->dev_path, O_RDWR); 225 | 226 | return fd; 227 | } 228 | 229 | enum led_ibpi_pattern enclosure_get_state(struct slot_property *sp) 230 | { 231 | int index = sp->slot_spec.ses.slot_num; 232 | struct enclosure_device *encl = sp->slot_spec.ses.encl; 233 | struct ses_slot *s_slot; 234 | 235 | s_slot = find_enclosure_slot_by_index(encl, index); 236 | if (!s_slot) { 237 | lib_log(encl->ctx, LED_LOG_LEVEL_ERROR, 238 | "SCSI: Unable to locate slot in enclosure %d\n", index); 239 | return LED_IBPI_PATTERN_UNKNOWN; 240 | } 241 | return s_slot->ibpi_status; 242 | } 243 | 244 | const struct slot_property_common ses_slot_common = { 245 | .cntrl_type = LED_CNTRL_TYPE_SCSI, 246 | .get_state_fn = enclosure_get_state, 247 | .set_slot_fn = enclosure_set_state 248 | }; 249 | 250 | struct slot_property *enclosure_slot_property_init(struct enclosure_device *encl, 251 | int slot_array_offset) 252 | { 253 | struct slot_property *result = NULL; 254 | struct ses_slot *slot_ptr = NULL; 255 | int ses_idx = slot_array_offset_to_ses_id(encl, slot_array_offset); 256 | 257 | // Entry for array offset could be empty 258 | if (ses_idx == -1) 259 | return NULL; 260 | 261 | slot_ptr = find_enclosure_slot_by_index(encl, ses_idx); 262 | if (!slot_ptr) 263 | return NULL; 264 | 265 | result = calloc(1, sizeof(struct slot_property)); 266 | if (result == NULL) 267 | return NULL; 268 | 269 | result->bl_device = enclosure_get_block_device(encl, ses_idx); 270 | result->slot_spec.ses.encl = encl; 271 | result->slot_spec.ses.slot_num = ses_idx; 272 | snprintf(result->slot_id, PATH_MAX, "%s-%d", encl->dev_path, ses_idx); 273 | result->c = &ses_slot_common; 274 | 275 | /* If we have an associated block device, set its ibpi value */ 276 | if (result->bl_device && slot_ptr) 277 | result->bl_device->ibpi = slot_ptr->ibpi_status; 278 | 279 | return result; 280 | } 281 | 282 | status_t enclosure_set_state(struct slot_property *sp, enum led_ibpi_pattern state) 283 | { 284 | struct enclosure_device *enclosure_device = sp->slot_spec.ses.encl; 285 | int index = sp->slot_spec.ses.slot_num; 286 | int rc; 287 | 288 | status_t status = scsi_ses_write_enclosure(enclosure_device, index, state); 289 | 290 | if (status != STATUS_SUCCESS) { 291 | lib_log(enclosure_device->ctx, LED_LOG_LEVEL_ERROR, 292 | "SCSI: ses write failed %d\n", status); 293 | return status; 294 | } 295 | 296 | rc = scsi_ses_flush_enclosure(enclosure_device); 297 | if (rc != 0) { 298 | lib_log(enclosure_device->ctx, LED_LOG_LEVEL_ERROR, 299 | "SCSI: ses flush enclosure failed %d\n", rc); 300 | return STATUS_FILE_WRITE_ERROR; 301 | } 302 | 303 | // Reload from hardware to report actual current state. 304 | rc = enclosure_reload(enclosure_device); 305 | if (rc != 0) { 306 | lib_log(enclosure_device->ctx, LED_LOG_LEVEL_ERROR, 307 | "SCSI: ses enclosure reload error %d\n", rc); 308 | return STATUS_FILE_READ_ERROR; 309 | } 310 | 311 | /* If we have an associated block device, update its ibpi value */ 312 | if (sp->bl_device) { 313 | sp->bl_device->ibpi_prev = sp->bl_device->ibpi; 314 | sp->bl_device->ibpi = enclosure_get_state(sp); 315 | } 316 | return STATUS_SUCCESS; 317 | } 318 | -------------------------------------------------------------------------------- /src/lib/enclosure.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2022 Intel Corporation. 3 | 4 | #ifndef _ENCLOSURE_H_INCLUDED_ 5 | #define _ENCLOSURE_H_INCLUDED_ 6 | 7 | #include 8 | #include 9 | 10 | #include "ses.h" 11 | #include "slot.h" 12 | #include "status.h" 13 | #include "sysfs.h" 14 | 15 | /** 16 | * @brief Enclosure device structure. 17 | * 18 | * This structure describes an enclosure device connected to one of 19 | * SAS controllers existing in the system. 20 | */ 21 | struct enclosure_device { 22 | /** 23 | * Path to an enclosure device in sysfs tree. This is controller base 24 | * canonical path. 25 | */ 26 | char sysfs_path[PATH_MAX]; 27 | 28 | /** 29 | * SAS address as identifier of an enclosure. 30 | */ 31 | uint64_t sas_address; 32 | 33 | /** 34 | * Path to enclosure's sg device. 35 | */ 36 | char *dev_path; 37 | 38 | struct ses_pages ses_pages; 39 | 40 | struct ses_slot *slots; 41 | int slots_count; 42 | 43 | struct led_ctx *ctx; 44 | }; 45 | 46 | /** 47 | * @brief Allocates memory for an enclosure device structure. 48 | * 49 | * This function allocates memory for a new structure describing an enclosure 50 | * device. It reads the sysfs entries and populates structure fields. 51 | * The function uses libsas abstraction layer to extract required information. 52 | * 53 | * @param[in] path Path to an enclosure device in sysfs tree. 54 | * The path begins with "/sys/class/enclosure/". 55 | * @param[in] ctx The library context. 56 | * 57 | * @return Pointer to enclosure device structure if successful, otherwise the 58 | * function returns NULL pointer. The NULL pointer means either the 59 | * specified path is invalid or there's not enough memory in the system 60 | * to allocated new structure. 61 | */ 62 | struct enclosure_device *enclosure_device_init(const char *path, struct led_ctx *ctx); 63 | 64 | /** 65 | * @brief Releases an enclosure device structure. 66 | * 67 | * This function releases memory allocated for enclosure device structure. 68 | * 69 | * @param[in] device Pointer to enclosure device structure. 70 | * 71 | * @return The function does not return a value. 72 | */ 73 | void enclosure_device_fini(struct enclosure_device *enclosure); 74 | 75 | int enclosure_open(const struct enclosure_device *enclosure); 76 | 77 | /** 78 | * @brief Gets slot information. 79 | * 80 | * This function returns the ibpi pattern for the specified slot. 81 | * 82 | * @param[in] slot_property Pointer to the slot property element. 83 | * 84 | * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. 85 | */ 86 | enum led_ibpi_pattern enclosure_get_state(struct slot_property *slot); 87 | 88 | 89 | /** 90 | * @brief Sets led state for slot. 91 | * 92 | * This function sets given led state for slot. 93 | * 94 | * @param[in] slot Requested slot. 95 | * @param[in] state IBPI state based on slot request. 96 | * 97 | * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. 98 | */ 99 | status_t enclosure_set_state(struct slot_property *slot, enum led_ibpi_pattern state); 100 | 101 | /** 102 | * @brief Initializes a slot_property for a specified enclosure and slot number in that enclosure. 103 | * 104 | * @param[in] enclosure Specified enclosure for this slot 105 | * @param[in] slot_num Specified slot number in this enclosure 106 | * @return struct slot_property* if successful, else NULL on allocation failure 107 | */ 108 | struct slot_property *enclosure_slot_property_init(struct enclosure_device *enclosure, 109 | int slot_num); 110 | 111 | /** 112 | * @brief Reloads the state of the hardware for the specified enclosure 113 | * 114 | * @param[in] enclosure Enclosure to reload. 115 | * 116 | * @return int 0 on success, else error 117 | */ 118 | int enclosure_reload(struct enclosure_device *enclosure); 119 | 120 | #endif /* _ENCLOSURE_H_INCLUDED_ */ 121 | -------------------------------------------------------------------------------- /src/lib/include/Makefile.am: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-only 2 | # Copyright (C) 2024 Intel Corporation. 3 | 4 | SUBDIRS = led 5 | -------------------------------------------------------------------------------- /src/lib/include/led/Makefile.am: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-only 2 | # Copyright (C) 2009 Intel Corporation. 3 | 4 | if WITH_LIBRARY 5 | 6 | ledincludedir = $(includedir)/led 7 | 8 | ledinclude_HEADERS = libled.h 9 | 10 | install-exec-hook: 11 | $(mkinstalldirs) $(DESTDIR)$(ledincludedir) 12 | 13 | endif 14 | -------------------------------------------------------------------------------- /src/lib/ipmi.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2023, Dell Inc. 3 | 4 | /* Generic IPMI Interface */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | #include "libled_private.h" 19 | 20 | #if _HAVE_DMALLOC_H 21 | #include 22 | #endif 23 | 24 | #include "utils.h" 25 | #include "ipmi.h" 26 | 27 | static int ipmi_open(void) 28 | { 29 | int fd; 30 | 31 | fd = open("/dev/ipmi0", O_RDWR); 32 | if (fd >= 0) 33 | return fd; 34 | fd = open("/dev/ipmidev/0", O_RDWR); 35 | if (fd >= 0) 36 | return fd; 37 | fd = open("/dev/ipmidev0", O_RDWR); 38 | if (fd >= 0) 39 | return fd; 40 | fd = open("/dev/bmc", O_RDWR); 41 | if (fd >= 0) 42 | return fd; 43 | return -1; 44 | } 45 | 46 | int ipmicmd(struct led_ctx *ctx, int ta, int lun, int netfn, int cmd, int datalen, void *data, 47 | int resplen, int *rlen, void *resp) 48 | { 49 | struct ipmi_system_interface_addr saddr = {0}; 50 | struct ipmi_ipmb_addr iaddr = {0}; 51 | struct ipmi_addr raddr = {0}; 52 | struct ipmi_req req = {0}; 53 | struct ipmi_recv rcv = {0}; 54 | fd_set rfd; 55 | int fd, rc; 56 | uint8_t tresp[resplen + 1]; 57 | 58 | fd = ipmi_open(); 59 | if (fd < 0) 60 | return -1; 61 | 62 | memset(tresp, 0, sizeof(tresp)); 63 | 64 | if (ta == BMC_TA) { 65 | saddr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; 66 | saddr.channel = IPMI_BMC_CHANNEL; 67 | saddr.lun = 0; 68 | req.addr = (void *)&saddr; 69 | req.addr_len = sizeof(saddr); 70 | } else { 71 | iaddr.addr_type = IPMI_IPMB_ADDR_TYPE; 72 | iaddr.channel = 0; 73 | iaddr.slave_addr = ta; 74 | iaddr.lun = lun; 75 | req.addr = (void *)&iaddr; 76 | req.addr_len = sizeof(iaddr); 77 | } 78 | 79 | /* Issue command */ 80 | req.msgid = ++ctx->ipmi_msgid; 81 | req.msg.netfn = netfn; 82 | req.msg.cmd = cmd; 83 | req.msg.data_len = datalen; 84 | req.msg.data = data; 85 | rc = ioctl(fd, IPMICTL_SEND_COMMAND, (void *)&req); 86 | if (rc != 0) { 87 | perror("send"); 88 | goto end; 89 | } 90 | 91 | /* Wait for Response */ 92 | FD_ZERO(&rfd); 93 | FD_SET(fd, &rfd); 94 | rc = select(fd + 1, &rfd, NULL, NULL, NULL); 95 | if (rc < 0) { 96 | perror("select"); 97 | goto end; 98 | } 99 | 100 | /* Get response */ 101 | rcv.msg.data = tresp; 102 | rcv.msg.data_len = resplen + 1; 103 | rcv.addr = (void *)&raddr; 104 | rcv.addr_len = sizeof(raddr); 105 | rc = ioctl(fd, IPMICTL_RECEIVE_MSG_TRUNC, (void *)&rcv); 106 | if (rc != 0 && errno == EMSGSIZE) 107 | lib_log(ctx, LED_LOG_LEVEL_INFO, "too short..\n"); 108 | if (rc != 0 && errno != EMSGSIZE) { 109 | lib_log(ctx, LED_LOG_LEVEL_INFO, "%s\n", strerror(errno)); 110 | goto end; 111 | } 112 | if (rcv.msg.data[0]) 113 | lib_log(ctx, LED_LOG_LEVEL_DEBUG, "IPMI Error: %.2x\n", rcv.msg.data[0]); 114 | rc = 0; 115 | *rlen = rcv.msg.data_len - 1; 116 | memcpy(resp, rcv.msg.data + 1, *rlen); 117 | end: 118 | close(fd); 119 | return rc; 120 | } 121 | -------------------------------------------------------------------------------- /src/lib/ipmi.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | 3 | /* Generic IPMI Interface */ 4 | 5 | #include "led/libled.h" 6 | 7 | #define BMC_TA 0x20 8 | 9 | int ipmicmd(struct led_ctx *ctx, int sa, int lun, int netfn, int cmd, int datalen, void *data, 10 | int resplen, int *rlen, void *resp); 11 | -------------------------------------------------------------------------------- /src/lib/libled_internal.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2022-2023 Red Hat, Inc. 3 | 4 | /* LED library internal */ 5 | 6 | #include 7 | #include 8 | 9 | #include "list.h" 10 | #include "libled_private.h" 11 | #include "utils.h" 12 | 13 | static led_status_t list_add(struct led_ctx *ctx, struct list *l, const char *path) 14 | { 15 | char *t = NULL; 16 | 17 | if (!ctx || !path) 18 | return LED_STATUS_NULL_POINTER; 19 | 20 | t = strdup(path); 21 | if (!t || !list_append(l, t)) { 22 | free(t); 23 | return LED_STATUS_OUT_OF_MEMORY; 24 | } 25 | return LED_STATUS_SUCCESS; 26 | } 27 | 28 | led_status_t device_allow_pattern_add(struct led_ctx *ctx, const char *path) 29 | { 30 | return list_add(ctx, &ctx->config.allowlist, path); 31 | } 32 | 33 | led_status_t device_exclude_pattern_add(struct led_ctx *ctx, const char *path) 34 | { 35 | return list_add(ctx, &ctx->config.excludelist, path); 36 | } 37 | 38 | void lib_log(struct led_ctx *ctx, enum led_log_level_enum loglevel, const char *buf, ...) 39 | { 40 | va_list vl; 41 | 42 | if (!ctx || ctx->log_fd < 0) 43 | return; 44 | 45 | va_start(vl, buf); 46 | _common_log(ctx->log_fd, ctx->log_lvl, loglevel, buf, vl); 47 | va_end(vl); 48 | } 49 | 50 | void off_all(struct led_ctx *ctx) 51 | { 52 | struct block_device *device; 53 | 54 | list_for_each(sysfs_get_block_devices(ctx), device) { 55 | device->send_message_fn(device, LED_IBPI_PATTERN_LOCATE_OFF); 56 | device->flush_message_fn(device); 57 | } 58 | } 59 | 60 | led_status_t device_blink_behavior_set(struct led_ctx *ctx, int migration, 61 | int init, int rebuild_all, int raid_members) 62 | { 63 | if (!ctx) 64 | return LED_STATUS_NULL_POINTER; 65 | 66 | ctx->config.blink_on_init = init; 67 | ctx->config.blink_on_migration = migration; 68 | ctx->config.rebuild_blink_on_all = rebuild_all; 69 | ctx->config.raid_members_only = raid_members; 70 | return LED_STATUS_SUCCESS; 71 | } 72 | -------------------------------------------------------------------------------- /src/lib/libled_internal.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2022-2023 Red Hat, Inc. 3 | 4 | /* LED library internal */ 5 | 6 | /* 7 | * NOTE: This are functions that are used by ledctl and ledmon, 8 | * but are not part of the public API 9 | */ 10 | 11 | #ifndef _LIB_LED_INTERNAL_INCLUDED_ 12 | #define _LIB_LED_INTERNAL_INCLUDED_ 13 | 14 | #include "led/libled.h" 15 | 16 | /** 17 | * @brief Turns off all LEDs for all support hardware for the system. 18 | * 19 | * @param[in] ctx Library context. 20 | * 21 | * Note: Needs to be followed with led_flush(). 22 | */ 23 | void off_all(struct led_ctx *ctx); 24 | 25 | /** 26 | * @brief Sets library blink behavior. 27 | * 28 | * @param[in] ctx Library context. 29 | * @param[in] migration Migration behavior. 30 | * @param[in] init Init behavior. 31 | * @param[in] all All led behavior. 32 | * @param[in] raid_members Raid member behavior. 33 | * 34 | * @return led_status_t LED_STATUS_SUCCESS on success, else error reason. 35 | */ 36 | led_status_t device_blink_behavior_set(struct led_ctx *ctx, int migration, 37 | int init, int all, int raid_members); 38 | 39 | /** 40 | * @brief Add a device path to an allow list. The library will then only consider this device 41 | * for API calls. Function can be repeated, to add to an internal list. 42 | * 43 | * @param[in] ctx Library context. 44 | * @param]in] path Device path in allow. 45 | * 46 | * @return led_status_t LED_STATUS_SUCCESS on success, else error reason. 47 | * 48 | * Note: Mutually exclusive with exclude, allow takes priority. 49 | */ 50 | led_status_t device_allow_pattern_add(struct led_ctx *ctx, const char *path); 51 | 52 | /** 53 | * @brief Add a device path to an exclude list. The library will then exclude this device 54 | * from API calls. Function can be repeated, to add to an internal list. 55 | * 56 | * @param[in] ctx Library context. 57 | * @param[in] path Device path to exclude. 58 | * 59 | * @return led_status_t LED_STATUS_SUCCESS on success, else error reason. 60 | */ 61 | led_status_t device_exclude_pattern_add(struct led_ctx *ctx, const char *path); 62 | 63 | /** 64 | * @brief Log to the library context fd with the specified log level and message 65 | * 66 | * @param[in] ctx Library context 67 | * @param[in] loglevel Enumerated log level 68 | * @param[in] buf printf style format string 69 | * @param[in] ... Variable number of arguments for printf format string 70 | */ 71 | void lib_log(struct led_ctx *ctx, enum led_log_level_enum loglevel, const char *buf, ...) 72 | __attribute__ ((format (printf, 3, 4))); 73 | #endif 74 | -------------------------------------------------------------------------------- /src/lib/libled_private.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2022-2023 Red Hat, Inc. 3 | 4 | /* LED library private */ 5 | 6 | /* Note: This file "libled_private.h" should never be included in a header file! */ 7 | 8 | #ifndef _LIB_LED_PRIVATE_INCLUDED_ 9 | #define _LIB_LED_PRIVATE_INCLUDED_ 10 | 11 | #include "led/libled.h" 12 | #include "sysfs.h" 13 | #include "list.h" 14 | #include "amd_sgpio.h" 15 | #include "slot.h" 16 | #include "libled_internal.h" 17 | 18 | #include 19 | 20 | /** 21 | * @brief Embedded structure within the library context used for amd state and caching. 22 | */ 23 | struct amd_sgpio_state { 24 | int cache_fd; 25 | struct cache_entry *cache; 26 | }; 27 | 28 | /** 29 | * @brief Configuration options for the library. Mainly used by ledmon. 30 | */ 31 | struct configuration { 32 | int blink_on_migration; 33 | int blink_on_init; 34 | int rebuild_blink_on_all; 35 | int raid_members_only; 36 | 37 | struct list allowlist; 38 | struct list excludelist; 39 | }; 40 | 41 | /** 42 | * @brief Implementation of the opaque led_slot_list_entry. 43 | */ 44 | struct led_slot_list_entry { 45 | struct slot_property *slot; 46 | char device_name[PATH_MAX]; 47 | }; 48 | 49 | /** 50 | * @brief Implementation of the opaque led_slot_list. 51 | */ 52 | struct led_slot_list { 53 | struct list slot_list; 54 | struct node *iter; 55 | }; 56 | 57 | /** 58 | * @brief Implementation of the opaque led_cntrl_list_entry. 59 | */ 60 | struct led_cntrl_list_entry { 61 | char path[PATH_MAX]; 62 | enum led_cntrl_type cntrl_type; 63 | }; 64 | 65 | /** 66 | * @brief Implementation of the opaque led_cntrl_list. 67 | */ 68 | struct led_cntrl_list { 69 | struct list cntrl_list; 70 | struct node *iter; 71 | }; 72 | 73 | /** 74 | * @brief Library context 75 | * 76 | * This structure is the common data for the library context. This is an opaque data type and thus 77 | * the internals of this structure cannot be visible to library users. 78 | */ 79 | struct led_ctx { 80 | struct sysfs sys; 81 | int log_fd; 82 | enum led_log_level_enum log_lvl; 83 | led_status_t deferred_error; 84 | int dellssd_hw_gen; /* cached value for dellssd hardware generation */ 85 | long ipmi_msgid; /* incrementing message id */ 86 | struct amd_sgpio_state amd_sgpio; 87 | struct configuration config; 88 | 89 | struct led_cntrl_list cl; 90 | struct led_slot_list sl; 91 | }; 92 | 93 | #endif 94 | -------------------------------------------------------------------------------- /src/lib/list.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2022 Intel Corporation. 3 | 4 | #include 5 | #include 6 | 7 | #include "list.h" 8 | #include "libled_private.h" 9 | #include "utils.h" 10 | 11 | void __list_erase(struct list *list, item_free_t free_fn) 12 | { 13 | struct node *node; 14 | 15 | list_for_each_node(list, node) { 16 | if (free_fn) 17 | free_fn(node->item); 18 | free(node); 19 | } 20 | list->head = list->tail = NULL; 21 | } 22 | 23 | void __list_remove(struct node *node, item_free_t free_fn) 24 | { 25 | struct list *list = node->list; 26 | 27 | if (node->prev) 28 | node->prev->next = node->next; 29 | else 30 | list->head = node->next; 31 | if (node->next) 32 | node->next->prev = node->prev; 33 | else 34 | list->tail = node->prev; 35 | node->list = NULL; 36 | node->next = NULL; 37 | node->prev = NULL; 38 | 39 | if (free_fn) 40 | free_fn(node->item); 41 | } 42 | 43 | bool list_insert(struct list *list, void *item, struct node *after) 44 | { 45 | struct node *new; 46 | struct node **x; 47 | 48 | assert(item != NULL); 49 | new = malloc(sizeof(struct node)); 50 | if (!new) { 51 | return false; 52 | } 53 | 54 | new->list = list; 55 | new->item = item; 56 | 57 | if (after) { 58 | assert(list == after->list); 59 | x = &after->next; 60 | } else { 61 | x = &list->head; 62 | } 63 | 64 | if (*x == NULL) 65 | list->tail = new; 66 | else 67 | (*x)->prev = new; 68 | new->next = *x; 69 | *x = new; 70 | new->prev = after; 71 | return true; 72 | } 73 | 74 | 75 | void list_append_ctx(struct list *list, void *item, struct led_ctx *ctx) 76 | { 77 | if (!list_insert(list, item, list->tail)) 78 | ctx->deferred_error = LED_STATUS_OUT_OF_MEMORY; 79 | } 80 | 81 | bool list_insert_compar(struct list *list, void *item, bool compar_fn(void *item1, void *item2)) 82 | { 83 | struct node *tmp = NULL; 84 | 85 | list_for_each_node(list, tmp) { 86 | if (compar_fn(item, tmp->item) == true) 87 | return list_insert(list, item, list_prev(tmp)); 88 | } 89 | return list_append(list, item); 90 | } 91 | -------------------------------------------------------------------------------- /src/lib/list.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2022 Intel Corporation. 3 | 4 | #ifndef _LIST_H_INCLUDED_ 5 | #define _LIST_H_INCLUDED_ 6 | 7 | #include 8 | #include 9 | 10 | struct led_ctx; 11 | 12 | struct node { 13 | struct node *next, *prev; 14 | struct list *list; 15 | void *item; 16 | }; 17 | 18 | typedef void (*item_free_t)(void *); 19 | 20 | struct list { 21 | struct node *head, *tail; 22 | item_free_t item_free; 23 | }; 24 | 25 | #define __list_for_each_node(__list, __node, __start_fn, __iter_fn) \ 26 | for (struct node *__n = __start_fn(__list), *__next; \ 27 | __n && (__node = __n) && ((__next = __iter_fn(__n)) || (!__next)); \ 28 | __n = __next) 29 | 30 | #define list_for_each_node(__list, __node) \ 31 | __list_for_each_node(__list, __node, list_head, list_next) 32 | 33 | #define list_for_each_node_reverse(__list, __node) \ 34 | __list_for_each_node(__list, __node, list_tail, list_prev) 35 | 36 | #define __list_for_each(__list, __item, __start_fn, __iter_fn) \ 37 | for (struct node *__node = __start_fn(__list); \ 38 | __node && ((__item = __node->item) || (!__item)); \ 39 | __node = __iter_fn(__node)) 40 | 41 | #define list_for_each(__list, __item) \ 42 | __list_for_each(__list, __item, list_head, list_next) 43 | 44 | #define list_for_each_reverse(__list, __item) \ 45 | __list_for_each(__list, __item, list_tail, list_prev) 46 | 47 | /** 48 | * @brief Initializes a list object. 49 | * 50 | * Initializes a list object to reflect an empty state. 51 | * 52 | * @param[in] list pointer to a list object. 53 | * @param[in] item_free_fn custom callback for deallocating list items. 54 | * If NULL, free() will be used. 55 | */ 56 | static inline void list_init(struct list *list, item_free_t item_free_fn) 57 | { 58 | list->head = NULL; 59 | list->tail = NULL; 60 | if (item_free_fn) 61 | list->item_free = item_free_fn; 62 | else 63 | list->item_free = free; 64 | } 65 | 66 | /** 67 | * @brief Clears a list and frees the items it contains. 68 | * 69 | * This function releases the memory allocated for a list object. It also frees 70 | * the data items attached to list nodes. It does not free the list itself. 71 | * 72 | * @param[in] list pointer to a list object. 73 | */ 74 | static inline void list_erase(struct list *list) 75 | { 76 | void __list_erase(struct list *list, item_free_t free_fn); 77 | __list_erase(list, list->item_free); 78 | } 79 | 80 | /** 81 | * @brief Clears a list. 82 | * 83 | * This function removes and deallocates all nodes from the list. It does not 84 | * free the data items, to do that use list_erase(). 85 | * 86 | * @param[in] list pointer to a list object. 87 | */ 88 | static inline void list_clear(struct list *list) 89 | { 90 | void __list_erase(struct list *list, item_free_t free_fn); 91 | __list_erase(list, NULL); 92 | } 93 | 94 | /** 95 | * @brief Removes an element from the list. 96 | * 97 | * This function removes an element from the list. It only detaches the element 98 | * and does not release the memory allocated for the element. To free memory 99 | * allocated for an element use list_delete() instead. 100 | * 101 | * @param[in] node pointer to a node object. 102 | */ 103 | static inline void list_remove(struct node *node) 104 | { 105 | void __list_remove(struct node *node, item_free_t free_fn); 106 | __list_remove(node, NULL); 107 | } 108 | 109 | /** 110 | * @brief Removes an element from the list and releases its memory. 111 | * 112 | * This function removes an element from the list and frees the memory allocated 113 | * for the list node and data item. 114 | * 115 | * @param[in] node pointer to a node object. 116 | */ 117 | static inline void list_delete(struct node *node) 118 | { 119 | void __list_remove(struct node *node, item_free_t free_fn); 120 | __list_remove(node, node->list->item_free); 121 | free(node); 122 | } 123 | 124 | /** 125 | * @brief Inserts an element into the list. 126 | * 127 | * This function puts an element after a given element. 128 | * 129 | * @param[in] list pointer to list object. 130 | * @param[in] item data item to be inserted into the list. 131 | * @param[in] after list node after which to insert the element. 132 | * If NULL, then insert at the head of the list. 133 | * 134 | * @return true if inserted, else false 135 | */ 136 | bool list_insert(struct list *list, void *item, struct node *after); 137 | 138 | /** 139 | * @brief Appends an element to the end of the list. 140 | * 141 | * @param[in] list pointer to list object. 142 | * @param[in] item data item to be inserted into the list. 143 | * 144 | * @return true on success, else false on memory allocation failure. 145 | */ 146 | static inline bool list_append(struct list *list, void *item) 147 | { 148 | return list_insert(list, item, list->tail); 149 | } 150 | 151 | /** 152 | * @brief Appends an element to the end of the list. 153 | * 154 | * @param[in] list pointer to list object. 155 | * @param[in] item data item to be inserted into the list. 156 | * @param[in] ctx library context (sets ctx deferred error). 157 | */ 158 | void list_append_ctx(struct list *list, void *item, struct led_ctx *ctx); 159 | 160 | /** 161 | * @brief Reruns next element. 162 | * 163 | * This function returns next element relatively to the given element. 164 | * 165 | * @param[in] node pointer to a node object. 166 | * 167 | * @return Pointer to an element if successful. The NULL pointer means 168 | * that node is the last element on the list. 169 | */ 170 | static inline struct node *list_next(const struct node *node) 171 | { 172 | return node->next; 173 | } 174 | 175 | /** 176 | * @brief Returns previous element. 177 | * 178 | * This function returns previous element relatively to the given element. 179 | * 180 | * @param[in] node pointer to a node object. 181 | * 182 | * @return Pointer to an element if successful. The NULL pointer means 183 | * that node is the first element on the list. 184 | */ 185 | static inline struct node *list_prev(const struct node *node) 186 | { 187 | return node->prev; 188 | } 189 | 190 | /** 191 | * @brief Returns head of a list. 192 | * 193 | * This function returns a head of a list. 194 | * 195 | * @param[in] list pointer to a list object. 196 | * 197 | * @return Pointer to an element if successful. The NULL pointer means that 198 | * there's no element on a list. 199 | */ 200 | static inline struct node *list_head(const struct list *list) 201 | { 202 | return list->head; 203 | } 204 | 205 | /** 206 | * @brief Returns tail of a list. 207 | * 208 | * This function returns a tail of a list. 209 | * 210 | * @param[in] list pointer to a list object. 211 | * 212 | * @return Pointer to an element if successful. The NULL pointer means that 213 | * there's no element on a list. 214 | */ 215 | static inline struct node *list_tail(const struct list *list) 216 | { 217 | return list->tail; 218 | } 219 | 220 | /** 221 | * @brief Checks if a list is empty. 222 | * 223 | * This function checks if a list object has elements. 224 | * 225 | * @param[in] list pointer to a list object. 226 | * 227 | * @return 1 if list is empty, otherwise the function returns 0. 228 | */ 229 | static inline int list_is_empty(const struct list *list) 230 | { 231 | return (list->head == NULL); 232 | } 233 | 234 | /** 235 | * @brief Appends an item before, if compar_fn returns true. 236 | * 237 | * @param[in] list pointer to a list object. 238 | * @param[in] item data item to be inserted into the list. 239 | * @param[in] compar_fn function to compare item with items from the list. 240 | * 241 | * @return true on success, else false on memory allocation failure. 242 | */ 243 | bool list_insert_compar(struct list *list, void *item, bool compar_fn(void *item1, void *item2)); 244 | 245 | #endif /* _LIST_H_INCLUDED_ */ 246 | -------------------------------------------------------------------------------- /src/lib/npem.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2022 Intel Corporation. 3 | 4 | #ifndef NPEM_H_INCLUDED_ 5 | #define NPEM_H_INCLUDED_ 6 | #include "block.h" 7 | #include "led/libled.h" 8 | #include "slot.h" 9 | #include "status.h" 10 | #include "sysfs.h" 11 | 12 | int is_npem_capable(const char *path, struct led_ctx *ctx); 13 | status_t npem_write(struct block_device *device, enum led_ibpi_pattern ibpi); 14 | char *npem_get_path(const char *cntrl_path); 15 | 16 | /** 17 | * @brief Gets slot information. 18 | * 19 | * This function fills slot information related to the slot. 20 | * 21 | * @param[in] slot Pointer to the slot property element. 22 | * 23 | * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. 24 | */ 25 | enum led_ibpi_pattern npem_get_state(struct slot_property *slot); 26 | 27 | /** 28 | * @brief Sets led state for slot. 29 | * 30 | * This function sets given led state for slot. 31 | * 32 | * @param[in] slot Requested slot. 33 | * @param[in] state IBPI state based on slot request. 34 | * 35 | * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. 36 | */ 37 | status_t npem_set_state(struct slot_property *slot, enum led_ibpi_pattern state); 38 | 39 | /** 40 | * @brief Initializes a slot_property for a specified npem controller. 41 | * 42 | * @param[in] npem_cntrl Specified npem controller for this slot 43 | * @return struct slot_property* if successful, else NULL on allocation failure 44 | */ 45 | struct slot_property *npem_slot_property_init(struct cntrl_device *npem_cntrl); 46 | 47 | #endif // NPEM_H_INCLUDED_ 48 | -------------------------------------------------------------------------------- /src/lib/pci_slot.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2022 Intel Corporation. 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #if _HAVE_DMALLOC_H 11 | #include 12 | #endif 13 | 14 | #include "config.h" 15 | #include "pci_slot.h" 16 | #include "sysfs.h" 17 | #include "utils.h" 18 | #include "vmdssd.h" 19 | #include "libled_private.h" 20 | 21 | /* 22 | * Allocates memory for PCI hotplug slot structure and initializes fields of 23 | * the structure. 24 | */ 25 | struct pci_slot *pci_slot_init(const char *path, struct led_ctx *ctx) 26 | { 27 | struct pci_slot *result = NULL; 28 | 29 | result = calloc(1, sizeof(struct pci_slot)); 30 | if (result == NULL) 31 | return NULL; 32 | result->sysfs_path = strdup(path); 33 | if (!result->sysfs_path) { 34 | goto error; 35 | } 36 | result->address = get_text(path, "address"); 37 | if (!result->address) 38 | goto error; 39 | 40 | result->ctx = ctx; 41 | 42 | return result; 43 | error: 44 | free(result->address); 45 | free(result->sysfs_path); 46 | free(result); 47 | return NULL; 48 | } 49 | 50 | /* 51 | * The function returns memory allocated for fields of hotplug slot structure 52 | * to the system. 53 | */ 54 | void pci_slot_fini(struct pci_slot *slot) 55 | { 56 | if (slot) { 57 | free(slot->sysfs_path); 58 | free(slot->address); 59 | free(slot); 60 | } 61 | } 62 | 63 | const struct slot_property_common pci_slot_common = { 64 | .cntrl_type = LED_CNTRL_TYPE_VMD, 65 | .get_state_fn = pci_get_state, 66 | .set_slot_fn = pci_set_slot 67 | }; 68 | 69 | struct slot_property *pci_slot_property_init(struct pci_slot *pci_slot) 70 | { 71 | struct slot_property *result = calloc(1, sizeof(struct slot_property)); 72 | if (result == NULL) 73 | return NULL; 74 | 75 | result->bl_device = get_block_device_from_sysfs_path(pci_slot->ctx, 76 | pci_slot->address, true); 77 | result->slot_spec.pci = pci_slot; 78 | snprintf(result->slot_id, PATH_MAX, "%s", pci_slot->sysfs_path); 79 | result->c = &pci_slot_common; 80 | 81 | return result; 82 | } 83 | 84 | status_t pci_set_slot(struct slot_property *slot, enum led_ibpi_pattern state) 85 | { 86 | return vmdssd_write_attention_buf(slot->slot_spec.pci, state); 87 | } 88 | 89 | enum led_ibpi_pattern pci_get_state(struct slot_property *slot) 90 | { 91 | return vmdssd_get_attention(slot->slot_spec.pci); 92 | } 93 | -------------------------------------------------------------------------------- /src/lib/pci_slot.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2022 Intel Corporation. 3 | 4 | #ifndef PCI_SLOT_H_INCLUDED_ 5 | #define PCI_SLOT_H_INCLUDED_ 6 | 7 | #include "led/libled.h" 8 | #include "slot.h" 9 | #include "status.h" 10 | #include "sysfs.h" 11 | 12 | /** 13 | * @brief PCI hotplug slot structure. 14 | * 15 | * This structure describes a PCI hotplug slot exposed by the hotplug driver. 16 | */ 17 | struct pci_slot { 18 | /** 19 | * Path to PCI hotplug slot in sysfs tree. 20 | */ 21 | char *sysfs_path; 22 | 23 | /** 24 | * PCI hotplug slot address. 25 | */ 26 | char *address; 27 | 28 | struct led_ctx *ctx; 29 | }; 30 | 31 | /** 32 | * @brief Allocates memory for a PCI hotplug slot structure. 33 | * 34 | * This function allocates memory for a new structure describing hotplug slot. 35 | * It reads the sysfs entries and populates structure fields. 36 | * 37 | * @param[in] path Path to a PCI hotplug slot in sysfs tree. 38 | * The path begins with "/sys/bus/pci/slots/". 39 | * @param[in] ctx The library context. 40 | * 41 | * @return Pointer to PCI hotplug slot structure if successful, otherwise the 42 | * function returns NULL pointer. The NULL pointer means either the 43 | * specified path is invalid or there's not enough memory in the system 44 | * to allocated new structure. 45 | */ 46 | struct pci_slot *pci_slot_init(const char *path, struct led_ctx *ctx); 47 | 48 | /** 49 | * @brief Releases an PCI hotplug slot structure. 50 | * 51 | * This function releases memory allocated for PCI hotplug slot structure. 52 | * 53 | * @param[in] slot Pointer to PCI hotplug slot structure. 54 | * 55 | * @return The function does not return a value. 56 | */ 57 | void pci_slot_fini(struct pci_slot *slot); 58 | 59 | /** 60 | * @brief Gets slot information. 61 | * 62 | * This function fills slot information related to the slot. 63 | * 64 | * @param[in] slot Pointer to the slot property element. 65 | * 66 | * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. 67 | */ 68 | enum led_ibpi_pattern pci_get_state(struct slot_property *slot); 69 | 70 | 71 | /** 72 | * @brief Sets led state for slot. 73 | * 74 | * This function sets given led state for slot. 75 | * 76 | * @param[in] slot Requested slot. 77 | * @param[in] state IBPI state based on slot request. 78 | * 79 | * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. 80 | */ 81 | status_t pci_set_slot(struct slot_property *slot, enum led_ibpi_pattern state); 82 | 83 | /** 84 | * @brief Initializes a slot_property for a specified pci slot. 85 | * 86 | * @param[in] pci_slot The specified pci slot pointer for this slot 87 | * @return struct slot_property* if successful, else NULL on allocation failure 88 | */ 89 | struct slot_property *pci_slot_property_init(struct pci_slot *pci_slot); 90 | 91 | #endif // PCI_SLOT_H_INCLUDED_ 92 | -------------------------------------------------------------------------------- /src/lib/raid.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2022 Intel Corporation. 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #if _HAVE_DMALLOC_H 10 | #include 11 | #endif 12 | 13 | #include "block.h" 14 | #include "config.h" 15 | #include "led/libled.h" 16 | #include "list.h" 17 | #include "raid.h" 18 | #include "tail.h" 19 | #include "status.h" 20 | #include "sysfs.h" 21 | #include "utils.h" 22 | #include "libled_private.h" 23 | 24 | /** 25 | */ 26 | static enum raid_state _get_array_state(const char *path) 27 | { 28 | char buf[BUF_SZ_SM]; 29 | enum raid_state state = RAID_STATE_UNKNOWN; 30 | 31 | char *p = get_text_to_dest(path, "md/array_state", buf, sizeof(buf)); 32 | if (p) { 33 | if (strcmp(p, "clear") == 0) 34 | state = RAID_STATE_CLEAR; 35 | else if (strcmp(p, "inactive") == 0) 36 | state = RAID_STATE_INACTIVE; 37 | else if (strcmp(p, "suspended") == 0) 38 | state = RAID_STATE_SUSPENDED; 39 | else if (strcmp(p, "readonly") == 0) 40 | state = RAID_STATE_READONLY; 41 | else if (strcmp(p, "read-auto") == 0) 42 | state = RAID_STATE_READ_AUTO; 43 | else if (strcmp(p, "clean") == 0) 44 | state = RAID_STATE_CLEAN; 45 | else if (strcmp(p, "active") == 0) 46 | state = RAID_STATE_ACTIVE; 47 | else if (strcmp(p, "write-pending") == 0) 48 | state = RAID_STATE_WRITE_PENDING; 49 | else if (strcmp(p, "active-idle") == 0) 50 | state = RAID_STATE_ACTIVE_IDLE; 51 | } 52 | return state; 53 | } 54 | 55 | /** 56 | */ 57 | static enum raid_action _get_sync_action(const char *path) 58 | { 59 | char buf[BUF_SZ_SM]; 60 | enum raid_action action = RAID_ACTION_UNKNOWN; 61 | 62 | char *p = get_text_to_dest(path, "md/sync_action", buf, sizeof(buf)); 63 | if (p) { 64 | if (strcmp(p, "idle") == 0) 65 | action = RAID_ACTION_IDLE; 66 | else if (strcmp(p, "reshape") == 0) 67 | action = RAID_ACTION_RESHAPE; 68 | else if (strcmp(p, "frozen") == 0) 69 | action = RAID_ACTION_FROZEN; 70 | else if (strcmp(p, "resync") == 0) 71 | action = RAID_ACTION_RESYNC; 72 | else if (strcmp(p, "check") == 0) 73 | action = RAID_ACTION_CHECK; 74 | else if (strcmp(p, "recover") == 0) 75 | action = RAID_ACTION_RECOVER; 76 | else if (strcmp(p, "repair") == 0) 77 | action = RAID_ACTION_REPAIR; 78 | } 79 | return action; 80 | } 81 | 82 | /** 83 | */ 84 | static enum raid_level _get_level(const char *path) 85 | { 86 | char buf[BUF_SZ_SM]; 87 | enum raid_level result = RAID_LEVEL_UNKNOWN; 88 | 89 | char *p = get_text_to_dest(path, "md/level", buf, sizeof(buf)); 90 | if (p) { 91 | if (strcmp(p, "raid0") == 0) 92 | result = RAID_LEVEL_0; 93 | else if (strcmp(p, "raid1") == 0) 94 | result = RAID_LEVEL_1; 95 | else if (strcmp(p, "raid10") == 0) 96 | result = RAID_LEVEL_10; 97 | else if (strcmp(p, "raid4") == 0) 98 | result = RAID_LEVEL_4; 99 | else if (strcmp(p, "raid5") == 0) 100 | result = RAID_LEVEL_5; 101 | else if (strcmp(p, "raid6") == 0) 102 | result = RAID_LEVEL_6; 103 | else if (strcmp(p, "linear") == 0) 104 | result = RAID_LEVEL_LINEAR; 105 | else if (strcmp(p, "faulty") == 0) 106 | result = RAID_LEVEL_FAULTY; 107 | } 108 | return result; 109 | } 110 | 111 | /** 112 | */ 113 | struct raid_device *raid_device_init(const char *path, unsigned int device_num, 114 | enum device_type type, struct led_ctx *ctx) 115 | { 116 | struct raid_device *device = NULL; 117 | enum raid_state state; 118 | const char *debug_dev; 119 | 120 | state = _get_array_state(path); 121 | if (state > RAID_STATE_INACTIVE || 122 | (type == DEVICE_TYPE_CONTAINER && state > RAID_STATE_CLEAR)) { 123 | device = calloc(1, sizeof(struct raid_device)); 124 | if (!device) 125 | return NULL; 126 | 127 | device->sysfs_path = strdup(path); 128 | if (!device->sysfs_path) { 129 | free(device); 130 | return NULL; 131 | } 132 | device->device_num = device_num; 133 | device->sync_action = _get_sync_action(path); 134 | device->array_state = state; 135 | device->level = _get_level(path); 136 | device->degraded = get_int(path, -1, "md/degraded"); 137 | device->raid_disks = get_int(path, 0, "md/raid_disks"); 138 | device->type = type; 139 | debug_dev = strrchr(path, '/'); 140 | debug_dev = debug_dev ? debug_dev + 1 : path; 141 | lib_log(ctx, LED_LOG_LEVEL_DEBUG, 142 | "(%s) path: %s, level=%u, state=%u, degraded=%d, disks=%d, type=%u", 143 | __func__, debug_dev, device->level, state, device->degraded, 144 | device->raid_disks, type); 145 | } 146 | return device; 147 | } 148 | 149 | /** 150 | */ 151 | void raid_device_fini(struct raid_device *device) 152 | { 153 | if (device) { 154 | if (device->sysfs_path) 155 | free(device->sysfs_path); 156 | free(device); 157 | } 158 | } 159 | 160 | /** 161 | */ 162 | struct raid_device *raid_device_duplicate(struct raid_device *device) 163 | { 164 | struct raid_device *new_device = NULL; 165 | 166 | if (device) { 167 | new_device = malloc(sizeof(struct raid_device)); 168 | if (new_device) { 169 | *new_device = *device; 170 | new_device->sysfs_path = strdup(device->sysfs_path); 171 | if (!new_device->sysfs_path) { 172 | free(new_device); 173 | return NULL; 174 | } 175 | } 176 | } 177 | return new_device; 178 | } 179 | 180 | /** 181 | */ 182 | struct raid_device *find_raid_device(const struct list *raid_list, 183 | char *raid_sysfs_path) 184 | { 185 | struct raid_device *raid = NULL; 186 | 187 | list_for_each(raid_list, raid) { 188 | if (strcmp(raid->sysfs_path, raid_sysfs_path) == 0) 189 | return raid; 190 | } 191 | return NULL; 192 | } 193 | -------------------------------------------------------------------------------- /src/lib/raid.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2022 Intel Corporation. 3 | 4 | #ifndef _RAID_H_INCLUDED_ 5 | #define _RAID_H_INCLUDED_ 6 | 7 | /** 8 | */ 9 | enum raid_state { 10 | RAID_STATE_UNKNOWN = 0, 11 | RAID_STATE_CLEAR, 12 | RAID_STATE_INACTIVE, 13 | RAID_STATE_SUSPENDED, 14 | RAID_STATE_READONLY, 15 | RAID_STATE_READ_AUTO, 16 | RAID_STATE_CLEAN, 17 | RAID_STATE_ACTIVE, 18 | RAID_STATE_WRITE_PENDING, 19 | RAID_STATE_ACTIVE_IDLE 20 | }; 21 | 22 | /** 23 | */ 24 | enum raid_level { 25 | RAID_LEVEL_UNKNOWN = 0, 26 | RAID_LEVEL_0, 27 | RAID_LEVEL_1, 28 | RAID_LEVEL_10, 29 | RAID_LEVEL_4, 30 | RAID_LEVEL_5, 31 | RAID_LEVEL_6, 32 | RAID_LEVEL_FAULTY, 33 | RAID_LEVEL_LINEAR 34 | }; 35 | 36 | /** 37 | */ 38 | enum device_type { 39 | DEVICE_TYPE_UNKNOWN = 0, 40 | DEVICE_TYPE_VOLUME, 41 | DEVICE_TYPE_CONTAINER 42 | }; 43 | 44 | /** 45 | */ 46 | enum raid_action { 47 | RAID_ACTION_UNKNOWN = 0, 48 | RAID_ACTION_IDLE, 49 | RAID_ACTION_RESHAPE, 50 | RAID_ACTION_FROZEN, 51 | RAID_ACTION_RESYNC, 52 | RAID_ACTION_CHECK, 53 | RAID_ACTION_RECOVER, 54 | RAID_ACTION_REPAIR 55 | }; 56 | 57 | /** 58 | */ 59 | struct raid_device { 60 | enum device_type type; 61 | int device_num; 62 | char *sysfs_path; 63 | int raid_disks; 64 | int degraded; 65 | enum raid_state array_state; 66 | enum raid_action sync_action; 67 | enum raid_level level; 68 | }; 69 | 70 | /** 71 | */ 72 | struct raid_device *raid_device_init(const char *path, unsigned int device_num, 73 | enum device_type type, struct led_ctx *ctx); 74 | 75 | /** 76 | */ 77 | void raid_device_fini(struct raid_device *device); 78 | 79 | /** 80 | */ 81 | struct raid_device *raid_device_duplicate(struct raid_device *device); 82 | 83 | /** 84 | */ 85 | struct raid_device *find_raid_device(const struct list *raid_list, 86 | char *raid_sysfs_path); 87 | #endif /* _RAID_H_INCLUDED_ */ 88 | -------------------------------------------------------------------------------- /src/lib/scsi.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2022 Intel Corporation. 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #if _HAVE_DMALLOC_H 14 | #include 15 | #endif 16 | 17 | #include "cntrl.h" 18 | #include "config.h" 19 | #include "enclosure.h" 20 | #include "list.h" 21 | #include "scsi.h" 22 | #include "ses.h" 23 | #include "status.h" 24 | #include "sysfs.h" 25 | #include "utils.h" 26 | 27 | static char *get_drive_end_dev(const char *path) 28 | { 29 | char *s, *c, *p; 30 | 31 | c = strstr(path, "end_device"); 32 | if (!c) 33 | return NULL; 34 | s = strchr(c, '/'); 35 | if (!s) 36 | return NULL; 37 | p = calloc(s - c + 1, sizeof(*p)); 38 | if (!p) 39 | return NULL; 40 | 41 | strncpy(p, c, s - c); 42 | return p; 43 | } 44 | 45 | static uint64_t get_drive_sas_addr(const char *path) 46 | { 47 | uint64_t ret = 0; 48 | size_t size = strlen(path) * 2; 49 | char *buff, *end_dev; 50 | 51 | /* Make big buffer. */ 52 | buff = malloc(size + 1); 53 | if (!buff) 54 | return ret; 55 | 56 | end_dev = get_drive_end_dev(path); 57 | if (!end_dev) { 58 | free(buff); 59 | return ret; 60 | } 61 | 62 | snprintf(buff, size, "/sys/class/sas_end_device/%s/device/sas_device/%s", 63 | end_dev, end_dev); 64 | 65 | ret = get_uint64(buff, ret, "sas_address"); 66 | 67 | free(end_dev); 68 | free(buff); 69 | 70 | return ret; 71 | } 72 | 73 | int scsi_get_enclosure(struct led_ctx *ctx, struct block_device *device) 74 | { 75 | struct enclosure_device *encl; 76 | uint64_t addr; 77 | 78 | if (!device || !device->sysfs_path) 79 | return 0; 80 | 81 | addr = get_drive_sas_addr(device->sysfs_path); 82 | if (!addr) 83 | return 0; 84 | 85 | list_for_each(sysfs_get_enclosure_devices(ctx), encl) { 86 | int i; 87 | 88 | for (i = 0; i < encl->slots_count; i++) { 89 | if (encl->slots[i].sas_addr == addr) { 90 | device->enclosure = encl; 91 | device->encl_index = device->enclosure->slots[i].index; 92 | return 1; 93 | } 94 | } 95 | } 96 | 97 | return 0; 98 | } 99 | 100 | /** 101 | */ 102 | status_t scsi_ses_write(struct block_device *device, enum led_ibpi_pattern ibpi) 103 | { 104 | if (!device || !device->sysfs_path || !device->enclosure || 105 | device->encl_index == -1) 106 | return STATUS_DATA_ERROR; 107 | 108 | /* write only if state has changed */ 109 | if (ibpi == device->ibpi_prev) 110 | return STATUS_SUCCESS; 111 | 112 | if ((ibpi < LED_IBPI_PATTERN_NORMAL) || (ibpi > LED_SES_REQ_FAULT)) 113 | return LED_STATUS_INVALID_STATE; 114 | 115 | return ses_write_msg(ibpi, &device->enclosure->ses_pages, device->encl_index); 116 | } 117 | 118 | status_t scsi_ses_write_enclosure(struct enclosure_device *enclosure, int idx, 119 | enum led_ibpi_pattern ibpi) 120 | { 121 | if (!enclosure || idx == -1) 122 | return STATUS_DATA_ERROR; 123 | 124 | if ((ibpi < LED_IBPI_PATTERN_NORMAL) || (ibpi > LED_SES_REQ_FAULT)) 125 | return STATUS_INVALID_STATE; 126 | 127 | return ses_write_msg(ibpi, &enclosure->ses_pages, idx); 128 | } 129 | 130 | int scsi_ses_flush_enclosure(struct enclosure_device *enclosure) 131 | { 132 | int ret = 0; 133 | int fd = enclosure_open(enclosure); 134 | if (fd == -1) 135 | return 1; 136 | 137 | ret = ses_send_diag(fd, &enclosure->ses_pages); 138 | close(fd); 139 | 140 | if (ret) 141 | return ret; 142 | 143 | return enclosure_reload(enclosure); 144 | } 145 | 146 | int scsi_ses_flush(struct block_device *device) 147 | { 148 | int ret; 149 | int fd; 150 | 151 | if (!device || !device->enclosure) 152 | return 1; 153 | 154 | if (!device->enclosure->ses_pages.changes) 155 | return 0; 156 | 157 | fd = enclosure_open(device->enclosure); 158 | if (fd == -1) 159 | return 1; 160 | 161 | ret = ses_send_diag(fd, &device->enclosure->ses_pages); 162 | 163 | close(fd); 164 | 165 | if (ret) 166 | return ret; 167 | 168 | return enclosure_reload(device->enclosure); 169 | } 170 | 171 | char *scsi_get_host_path(const char *path, const char *ctrl_path) 172 | { 173 | char *host; 174 | char host_path[PATH_MAX] = { 0 }; 175 | size_t ctrl_path_len = strnlen(ctrl_path, PATH_MAX); 176 | 177 | if (strncmp(path, ctrl_path, ctrl_path_len) != 0) 178 | return NULL; 179 | host = get_path_hostN(path); 180 | if (host) { 181 | snprintf(host_path, sizeof(host_path), "%s/%s/bsg/sas_%s", 182 | ctrl_path, host, host); 183 | free(host); 184 | } 185 | return strdup(host_path); 186 | } 187 | 188 | 189 | struct block_device *locate_block_by_sas_addr(struct led_ctx *ctx, uint64_t sas_address) 190 | { 191 | struct block_device *block_device; 192 | list_for_each(sysfs_get_block_devices(ctx), block_device) { 193 | uint64_t tmp = get_drive_sas_addr(block_device->sysfs_path); 194 | if (tmp != 0 && tmp == sas_address) { 195 | return block_device; 196 | } 197 | } 198 | return NULL; 199 | } 200 | -------------------------------------------------------------------------------- /src/lib/scsi.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2022 Intel Corporation. 3 | 4 | #ifndef _SCSI_H_INCLUDED 5 | #define _SCSI_H_INCLUDED 6 | 7 | #include "block.h" 8 | #include "led/libled.h" 9 | #include "sysfs.h" 10 | 11 | /** 12 | * @brief Gets the sas host path of the device. 13 | * 14 | * This function returns a sysfs path to the sas host that the device 15 | * belongs to. 16 | * 17 | * @param[in] path Canonical sysfs path to block device. 18 | * 19 | * @return A sysfs path to host device associated with the given 20 | * block device if successful, otherwise NULL pointer. 21 | */ 22 | char *scsi_get_host_path(const char *path, const char *cntrl_path); 23 | 24 | /** 25 | * @brief Prepares SES message based on ibpi pattern. 26 | * 27 | * @param[in] device Sysfs path of a drive in enclosure. 28 | * @param[in] ibpi IBPI pattern to visualize. 29 | * 30 | * @return STATUS_SUCCESS on success, otherwise a valid status_t status code. 31 | */ 32 | status_t scsi_ses_write(struct block_device *device, enum led_ibpi_pattern ibpi); 33 | 34 | /** 35 | * @brief Sends message to SES processor of an enclosure. 36 | * 37 | * This function send a message to an enclosure in order to control LEDs of 38 | * the given slot/component. It uses interface of ENCLOSURE kernel module to 39 | * control LEDs. 40 | * 41 | * @param[in] device Sysfs path of a drive in enclosure. 42 | * 43 | * @return 0 on success, otherwise error. 44 | */ 45 | int scsi_ses_flush(struct block_device *device); 46 | 47 | /** 48 | * @brief Assigns enclosure device to block device. 49 | * 50 | * @param[in] ctx The library context. 51 | * @param[in] device Path to block device. 52 | * 53 | * @return 1 on success, 0 otherwise. 54 | * */ 55 | int scsi_get_enclosure(struct led_ctx *ctx, struct block_device *device); 56 | 57 | /** 58 | * @brief Locates a block device by sas_address 59 | * 60 | * This function walks the list of block devices searching for one that 61 | * matches the sas_address. 62 | * 63 | * @param[in] ctx The library context. 64 | * @param[in] sas_address sas address of block device 65 | * 66 | * @return address of block_device if found, else NULL 67 | */ 68 | struct block_device *locate_block_by_sas_addr(struct led_ctx *ctx, uint64_t sas_address); 69 | 70 | /** 71 | * @brief Given an enclosure_device pointer and slot index, prepare the SES message 72 | * 73 | * @param[in] enclosure Enclosure 74 | * @param[in] idx slot in enclosure 75 | * @param[in] ibpi IBPI pattern to visualize. 76 | * 77 | * @return STATUS_SUCCESS on success, otherwise a valid status_t status code. 78 | */ 79 | status_t scsi_ses_write_enclosure(struct enclosure_device *enclosure, int idx, 80 | enum led_ibpi_pattern ibpi); 81 | 82 | /** 83 | * @brief Sends message to SES processor of an enclosure. 84 | * 85 | * This function sends a message to an enclosure in order to control LEDs of 86 | * the given slot/component. It uses the interface of ENCLOSURE kernel module to 87 | * control LEDs. 88 | * 89 | * @param[in] enclosure Pointer to enclosure to flush 90 | * 91 | * @return 0 on success, otherwise error. 92 | */ 93 | int scsi_ses_flush_enclosure(struct enclosure_device *enclosure); 94 | 95 | #endif /* _SCSI_H_INCLUDED_ */ 96 | -------------------------------------------------------------------------------- /src/lib/ses.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2022 Intel Corporation. 3 | 4 | #ifndef _SES_H_INCLUDED_ 5 | #define _SES_H_INCLUDED_ 6 | 7 | #include 8 | 9 | #include "led/libled.h" 10 | #include 11 | 12 | /* Size of buffer for SES-2 Messages. */ 13 | #define SES_ALLOC_BUFF 4096 14 | 15 | typedef enum __attribute__((packed)) { 16 | SES_UNSPECIFIED = 0x00, 17 | SES_DEVICE_SLOT = 0x01, 18 | SES_ARRAY_DEVICE_SLOT = 0x17, 19 | } element_type; 20 | 21 | struct ses_page { 22 | unsigned char buf[SES_ALLOC_BUFF]; 23 | int len; 24 | }; 25 | 26 | struct type_descriptor_header { 27 | element_type element_type; 28 | __u8 num_of_elements; 29 | __u8 subenclosure_id; 30 | __u8 type_desc_text_len; 31 | }; 32 | 33 | struct ses_pages { 34 | struct ses_page page1; 35 | struct ses_page page2; 36 | struct ses_page page10; 37 | const struct type_descriptor_header *page1_types; 38 | int page1_types_len; 39 | int changes; 40 | }; 41 | 42 | struct ses_slot_ctrl_elem { 43 | union { 44 | struct { 45 | __u8 common_control; 46 | __u8 array_slot_control; 47 | __u8 b2; 48 | __u8 b3; 49 | }; 50 | __u8 b[4]; 51 | }; 52 | }; 53 | 54 | struct ses_slot { 55 | int index; 56 | uint64_t sas_addr; 57 | enum led_ibpi_pattern ibpi_status; 58 | }; 59 | 60 | int ses_load_pages(int fd, struct ses_pages *sp, struct led_ctx *ctx); 61 | status_t ses_write_msg(enum led_ibpi_pattern ibpi, struct ses_pages *sp, int idx); 62 | int ses_send_diag(int fd, struct ses_pages *sp); 63 | int ses_get_slots(struct ses_pages *sp, struct ses_slot **out_slots, int *out_slots_count); 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /src/lib/slot.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2023 Intel Corporation. 3 | 4 | #include 5 | #include 6 | 7 | #if _HAVE_DMALLOC_H 8 | #include 9 | #endif 10 | 11 | #include "enclosure.h" 12 | #include "npem.h" 13 | #include "slot.h" 14 | #include "sysfs.h" 15 | #include "pci_slot.h" 16 | 17 | struct slot_property *find_slot_by_device_name(struct led_ctx *ctx, char *device_name, 18 | enum led_cntrl_type cntrl_type) 19 | { 20 | struct slot_property *slot; 21 | 22 | list_for_each(sysfs_get_slots(ctx), slot) { 23 | if (slot->c->cntrl_type != cntrl_type) 24 | continue; 25 | 26 | if (!slot->bl_device || slot->bl_device->devnode[0] == 0) 27 | continue; 28 | 29 | if (strncmp(basename(slot->bl_device->devnode), 30 | basename(device_name), PATH_MAX) == 0) 31 | return slot; 32 | } 33 | return NULL; 34 | } 35 | 36 | struct slot_property *find_slot_by_slot_path(struct led_ctx *ctx, char *slot_path, 37 | enum led_cntrl_type cntrl_type) 38 | { 39 | struct slot_property *slot; 40 | 41 | list_for_each(sysfs_get_slots(ctx), slot) { 42 | if (slot->c->cntrl_type != cntrl_type) 43 | continue; 44 | if (strncmp(basename(slot->slot_id), basename(slot_path), PATH_MAX) == 0) 45 | return slot; 46 | } 47 | return NULL; 48 | } 49 | 50 | status_t set_slot_pattern(struct slot_property *slot, enum led_ibpi_pattern state) 51 | { 52 | return slot->c->set_slot_fn(slot, state); 53 | } 54 | 55 | enum led_ibpi_pattern get_slot_pattern(struct slot_property *slot) 56 | { 57 | return slot->c->get_state_fn(slot); 58 | } 59 | -------------------------------------------------------------------------------- /src/lib/slot.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2023 Intel Corporation. 3 | 4 | #ifndef SLOT_H_ 5 | #define SLOT_H_ 6 | 7 | #include 8 | 9 | #include "block.h" 10 | #include "cntrl.h" 11 | #include "led/libled.h" 12 | #include "utils.h" 13 | 14 | #include "libled_private.h" 15 | 16 | /* Forward decl. */ 17 | struct slot_property; 18 | 19 | 20 | /** 21 | * @brief slot property attributes which are the same across slots of a specific type 22 | * 23 | */ 24 | struct slot_property_common { 25 | /** 26 | * Controller type which is being represented by slot. 27 | */ 28 | enum led_cntrl_type cntrl_type; 29 | 30 | /** 31 | * Pointer to the set slot function. 32 | */ 33 | status_t (*set_slot_fn)(struct slot_property *slot, enum led_ibpi_pattern state); 34 | 35 | /** 36 | * Pointer to the get led state function. 37 | */ 38 | enum led_ibpi_pattern (*get_state_fn)(struct slot_property *slot); 39 | }; 40 | 41 | /** 42 | * @brief Encapsulates information for enclosure 43 | * 44 | */ 45 | struct ses_slot_info { 46 | struct enclosure_device *encl; 47 | int slot_num; 48 | }; 49 | 50 | /** 51 | * @brief slot property parameters 52 | * 53 | * This structure contains slot parameters. 54 | */ 55 | struct slot_property { 56 | const struct slot_property_common *c; 57 | 58 | /** 59 | * Block device (if exists for slot). 60 | */ 61 | struct block_device *bl_device; 62 | 63 | /** 64 | * Different payload based on slot type 65 | */ 66 | union { 67 | struct pci_slot *pci; 68 | struct cntrl_device *cntrl; 69 | struct ses_slot_info ses; 70 | } slot_spec; 71 | 72 | /** 73 | * Unique slot ID. 74 | */ 75 | char slot_id[PATH_MAX]; 76 | }; 77 | 78 | /** 79 | * @brief Find slot device by path to slot. 80 | * 81 | * @param[in] ctx The library context. 82 | * @param[in] slot_path Path to slot. 83 | * @param[in] cntrl_type Type of controller. 84 | * 85 | * @return This function returns related slot property. 86 | */ 87 | struct slot_property *find_slot_by_slot_path(struct led_ctx *ctx, char *slot_path, 88 | enum led_cntrl_type cntrl_type); 89 | 90 | /** 91 | * @brief Find slot device by device name. 92 | * 93 | * @param[in] ctx The library context. 94 | * @param[in] device_name Device name. 95 | * @param[in] cntrl_type Type of controller. 96 | * 97 | * @return This function returns related slot property. 98 | */ 99 | struct slot_property *find_slot_by_device_name(struct led_ctx *ctx, char *device_name, 100 | enum led_cntrl_type cntrl_type); 101 | 102 | /** 103 | * @brief Set the slot pattern for the given slot 104 | * 105 | * @param[in] slot The slot to change the ibpi_pattern 106 | * @param[in] state The desired ibpi pattern for the slot 107 | * @return status_t 108 | */ 109 | status_t set_slot_pattern(struct slot_property *slot, enum led_ibpi_pattern state); 110 | 111 | /** 112 | * @brief Get the slot pattern 113 | * 114 | * @param[in] slot The slot to retrieve the ibpi pattern 115 | * @return enum ibpi_pattern 116 | */ 117 | enum led_ibpi_pattern get_slot_pattern(struct slot_property *slot); 118 | 119 | #endif // SLOT_H_INCLUDED_ 120 | -------------------------------------------------------------------------------- /src/lib/smp.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2022 Intel Corporation. 3 | 4 | #include "led/libled.h" 5 | #include "block.h" 6 | #include "sysfs.h" 7 | 8 | #ifndef _SMP_H_INCLUDED 9 | #define _SMP_H_INCLUDED 10 | 11 | /* smp constants */ 12 | #define SMP_FRAME_TYPE_REQ 0x40 13 | #define SMP_FRAME_TYPE_RESP 0x41 14 | 15 | #define SMP_FUNC_GPIO_READ 0x02 16 | #define SMP_FUNC_GPIO_WRITE 0x82 17 | 18 | #define SMP_FRAME_CRC_LEN sizeof(uint32_t) 19 | #define SMP_DATA_CHUNK_SIZE sizeof(uint32_t) 20 | 21 | /* gpio constants */ 22 | /* gpio register types */ 23 | #define GPIO_REG_TYPE_CFG 0x00 24 | #define GPIO_REG_TYPE_RX 0x01 25 | #define GPIO_REG_TYPE_RX_GP 0x02 26 | #define GPIO_REG_TYPE_TX 0x03 27 | #define GPIO_REG_TYPE_TX_GP 0x04 28 | 29 | /* gpio register indexes */ 30 | #define GPIO_REG_IND_CFG_0 0x00 31 | #define GPIO_REG_IND_CFG_1 0x01 32 | 33 | #define GPIO_REG_IND_RX_0 0x00 34 | #define GPIO_REG_IND_RX_1 0x01 35 | 36 | #define GPIO_REG_IND_TX_0 0x00 37 | #define GPIO_REG_IND_TX_1 0x01 38 | 39 | #define SG_RESPONSE_TIMEOUT (5 * 1000) /* 1000 as milliseconds multiplier */ 40 | #define SCSI_MAX_CDB_LENGTH 0x10 41 | 42 | #define GPIO_STATUS_OK 0x00 43 | #define GPIO_STATUS_FAILURE 0x80 44 | 45 | struct gpio_tx_register_byte { 46 | unsigned char error:3; 47 | unsigned char locate:2; 48 | unsigned char activity:3; 49 | } __attribute__ ((__packed__)); 50 | 51 | /** 52 | * @brief Write message to outbound raw byte stream buffer. 53 | * 54 | * @param[in] device Path to a smp device in sysfs. 55 | * @param[in] ibpi IBPI pattern to visualize. 56 | * 57 | * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. 58 | */ 59 | status_t scsi_smp_fill_buffer(struct block_device *device, enum led_ibpi_pattern ibpi); 60 | 61 | /** 62 | * @brief Sends message to SMP device. 63 | * 64 | * This function triggers gpio order to control LEDs of 65 | * the given component. 66 | * 67 | * @param[in] device Path to a smp device in sysfs. 68 | * 69 | * @return Number of bytes written to device if successful or -1 in case of error 70 | * and errno is set to appropriate error code. 71 | */ 72 | int scsi_smp_write_buffer(struct block_device *device); 73 | 74 | /** 75 | * @brief Init smp and gets phy index, 76 | * 77 | * @param[in] path Path to the device in sysfs. It can be NULL 78 | * to just initialize cntrl and not to get the 79 | * phy. 80 | * @param[in] cntrl Controller device to be initialized. 81 | * 82 | * @return Phy index on success if path and cntrl weren't NULL 83 | * 0 if error occurred or path was NULL. 84 | */ 85 | int cntrl_init_smp(const char *path, struct cntrl_device *cntrl); 86 | 87 | /** 88 | * @brief Write GPIO data 89 | * 90 | * @param[in] path Path to the device in sysfs. 91 | * phy. 92 | * 93 | * @param[in] smp_reg_type GPIO register type 94 | * 95 | * @param[in] smp_reg_index GPIO register index 96 | * 97 | * @param[in] smp_reg_count GPIO register count 98 | * 99 | * @param[in] data Data to be written 100 | * 101 | * @return written register count 102 | * <0 if error occurred 103 | */ 104 | int smp_write_gpio(const char *path, int smp_reg_type, 105 | int smp_reg_index, int smp_reg_count, void *data, 106 | size_t len); 107 | 108 | #endif /* _SCSI_H_INCLUDED_ */ 109 | -------------------------------------------------------------------------------- /src/lib/status.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2022 Intel Corporation. 3 | 4 | #ifndef _STATUS_H_INCLUDED_ 5 | #define _STATUS_H_INCLUDED_ 6 | 7 | typedef int status_t; 8 | 9 | enum status_code { 10 | STATUS_SUCCESS = 0, 11 | STATUS_BUFFER_OVERFLOW, 12 | STATUS_NULL_POINTER, 13 | STATUS_OUT_OF_MEMORY, 14 | STATUS_OUT_OF_RANGE, 15 | STATUS_INVALID_NODE, 16 | STATUS_DATA_ERROR, 17 | STATUS_INVALID_PATH=8, 18 | STATUS_INVALID_SUBOPTION, 19 | STATUS_INVALID_STATE, 20 | STATUS_SIZE_ERROR, 21 | STATUS_FILE_OPEN_ERROR, 22 | STATUS_FILE_READ_ERROR, 23 | STATUS_FILE_WRITE_ERROR, 24 | STATUS_FILE_LOCK_ERROR, 25 | STATUS_DIR_OPEN_ERROR, 26 | STATUS_SYSFS_PATH_ERROR, 27 | STATUS_SYSFS_INIT_ERROR, 28 | STATUS_SYSFS_SCAN_ERROR, 29 | STATUS_SYSFS_RESET_ERROR, 30 | STATUS_LIST_INIT_ERROR=22, 31 | STATUS_BLOCK_LIST_ERROR, 32 | STATUS_VOLUM_LIST_ERROR, 33 | STATUS_CNTRL_LIST_ERROR, 34 | STATUS_TAIL_LIST_ERROR, 35 | STATUS_CNTNR_LIST_ERROR, 36 | STATUS_INVALID_FORMAT, 37 | STATUS_CMDLINE_ERROR=35, 38 | STATUS_ENCLO_LIST_ERROR=37, 39 | STATUS_SLOTS_LIST_ERROR, 40 | STATUS_CONFIG_FILE_ERROR, 41 | }; 42 | 43 | #endif /* _STATUS_H_INCLUDED_ */ 44 | -------------------------------------------------------------------------------- /src/lib/sysfs.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2022 Intel Corporation. 3 | 4 | #ifndef _SYSFS_H_INCLUDED_ 5 | #define _SYSFS_H_INCLUDED_ 6 | 7 | #include 8 | #include "list.h" 9 | #include "status.h" 10 | 11 | #define SYSFS_PCI_DEVICES "/sys/bus/pci/devices" 12 | 13 | #define SYSTEM_DEV_DIR "/dev" 14 | 15 | struct sysfs { 16 | /** 17 | * This is internal variable global to sysfs module only. It is a list of 18 | * block devices registered in the system. Use sysfs_init() 19 | * function to initialize the variable. Use sysfs_scan() function to populate 20 | * the list. Use sysfs_reset() function to delete the content of the list. 21 | */ 22 | struct list sysfs_block_list; 23 | 24 | /** 25 | * This is internal variable global to sysfs module only. It is a list of 26 | * RAID volumes registered in the system. Use sysfs_init() 27 | * function to initialize the variable. Use sysfs_scan() function to populate 28 | * the list. Use sysfs_reset() function to delete the content of the list. 29 | */ 30 | struct list volum_list; 31 | 32 | /** 33 | * This is internal variable global to sysfs module only. It is a list of 34 | * storage controller devices registered in the system and 35 | * supported by Intel(R) Enclosure LEDs Control Utility. Use sysfs_init() 36 | * function to initialize the variable. Use sysfs_scan() function to populate 37 | * the list. Use sysfs_reset() function to delete the content of the list. 38 | */ 39 | struct list cntrl_list; 40 | 41 | /** 42 | * This is internal variable global to sysfs module only. It is a list of 43 | * tail devices registered in the system. Use sysfs_init() 44 | * function to initialize the variable. Use sysfs_scan() function to populate 45 | * the list. Use sysfs_reset() function to delete the content of the list. 46 | */ 47 | struct list tail_list; 48 | 49 | /** 50 | * This is internal variable global to sysfs module only. It is a list of 51 | * RAID containers registered in the system. Use sysfs_init() 52 | * function to initialize the variable. Use sysfs_scan() function to populate 53 | * the list. Use sysfs_reset() function to delete the content of the list. 54 | */ 55 | struct list cntnr_list; 56 | 57 | /** 58 | * This is internal variable global to sysfs module only. It is a to list of 59 | * enclosures registered in the system. 60 | */ 61 | struct list enclo_list; 62 | 63 | /** 64 | * This is internal variable global to sysfs module only. It is a list of 65 | * PCI slots registered in the system. Use sysfs_init() 66 | * function to initialize the variable. Use sysfs_scan() function to populate 67 | * the list. Use sysfs_reset() function to delete the content of the list. 68 | */ 69 | struct list pci_slots_list; 70 | 71 | /** 72 | * This is internal variable global to sysfs module only. It is a list of 73 | * PCI slots registered in the system. Use sysfs_init() 74 | * function to initialize the variable. Use sysfs_scan() function to populate 75 | * the list. Use sysfs_reset() function to delete the content of the list. 76 | */ 77 | struct list slots_list; 78 | 79 | }; 80 | 81 | 82 | /** 83 | * @brief Initializes sysfs module. 84 | * 85 | * This function initializes sysfs module internal lists. 86 | * Application must call this function before any sysfs module function. 87 | */ 88 | 89 | void sysfs_init(struct led_ctx *ctx); 90 | 91 | /** 92 | * @brief Resets the content of internal lists. 93 | * 94 | * This function releases memory allocated for elements of internal lists. 95 | */ 96 | void sysfs_reset(struct led_ctx *ctx); 97 | 98 | /** 99 | * @brief Scans sysfs tree and populates internal lists. 100 | * 101 | * This function scans sysfs tree for storage controllers, block devices, RAID 102 | * devices, container devices, tail devices and enclosure devices registered 103 | * in the system. Only supported block and controller devices are put on a list. 104 | */ 105 | void sysfs_scan(struct led_ctx *ctx); 106 | 107 | /** 108 | * The function returns list of enclosure devices attached to SAS/SCSI storage 109 | * controller(s). 110 | */ 111 | const struct list *sysfs_get_enclosure_devices(struct led_ctx *ctx); 112 | 113 | /** 114 | * The function returns list of controller devices present in the system. 115 | */ 116 | const struct list *sysfs_get_cntrl_devices(struct led_ctx *ctx); 117 | 118 | /** 119 | * The function returns list of RAID volumes present in the system. 120 | */ 121 | const struct list *sysfs_get_volumes(struct led_ctx *ctx); 122 | 123 | /** 124 | * The function returns list of block devices present in the system. 125 | */ 126 | const struct list *sysfs_get_block_devices(struct led_ctx *ctx); 127 | 128 | /** 129 | * The function returns list of pci slots present in the system. 130 | */ 131 | const struct list *sysfs_get_pci_slots(struct led_ctx *ctx); 132 | 133 | /** 134 | * The function returns list of slots for supported controllers 135 | * present in the system. 136 | */ 137 | const struct list *sysfs_get_slots(struct led_ctx *ctx); 138 | 139 | /** 140 | * The function checks if the given storage controller is attached to enclosure 141 | * device(s). 142 | */ 143 | int sysfs_enclosure_attached_to_cntrl(struct led_ctx *ctx, const char *path); 144 | 145 | /* 146 | * This function checks driver type. 147 | */ 148 | int sysfs_check_driver(const char *path, const char *driver); 149 | 150 | #endif /* _SYSFS_H_INCLUDED_ */ 151 | -------------------------------------------------------------------------------- /src/lib/tail.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2022 Intel Corporation. 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #if _HAVE_DMALLOC_H 12 | #include 13 | #endif 14 | 15 | #include "config.h" 16 | #include "list.h" 17 | #include "tail.h" 18 | #include "status.h" 19 | #include "sysfs.h" 20 | #include "utils.h" 21 | 22 | /** 23 | */ 24 | static unsigned char _get_state(const char *path) 25 | { 26 | char *p, *t, *s; 27 | unsigned char result = TAIL_STATE_UNKNOWN; 28 | 29 | s = p = get_text(path, "state"); 30 | if (p) { 31 | while (s) { 32 | t = strchr(s, ','); 33 | if (t) 34 | *(t++) = '\0'; 35 | if (strcmp(s, "spare") == 0) 36 | result |= TAIL_STATE_SPARE; 37 | else if (strcmp(s, "in_sync") == 0) 38 | result |= TAIL_STATE_IN_SYNC; 39 | else if (strcmp(s, "faulty") == 0) 40 | result |= TAIL_STATE_FAULTY; 41 | else if (strcmp(s, "write_mostly") == 0) 42 | result |= TAIL_STATE_WRITE_MOSTLY; 43 | else if (strcmp(s, "blocked") == 0) 44 | result |= TAIL_STATE_BLOCKED; 45 | s = t; 46 | } 47 | free(p); 48 | } 49 | return result; 50 | } 51 | 52 | /** 53 | */ 54 | static unsigned int _get_errors(const char *path) 55 | { 56 | return get_int(path, 0, "errors"); 57 | } 58 | 59 | /** 60 | */ 61 | static int _get_slot(const char *path, unsigned int *dest) 62 | { 63 | int ret = 1; 64 | unsigned int n; 65 | char *p = get_text(path, "slot"); 66 | 67 | if (p) { 68 | if (strcmp(p, "none") != 0) 69 | if (str_toui(&n, p, NULL, 10) == 0) { 70 | *dest = n; 71 | ret = 0; 72 | } 73 | free(p); 74 | } 75 | return ret; 76 | } 77 | 78 | /** 79 | */ 80 | static struct block_device *_get_block(const char *path, struct list *block_list) 81 | { 82 | char temp[PATH_MAX]; 83 | char link[PATH_MAX]; 84 | struct block_device *device; 85 | 86 | snprintf(temp, sizeof(temp), "%s/block", path); 87 | 88 | if (!realpath(temp, link)) 89 | return NULL; 90 | 91 | /* translate partition to master block dev */ 92 | if (snprintf(temp, PATH_MAX, "%s/partition", link) > 0) { 93 | struct stat sb; 94 | char *ptr; 95 | 96 | if (stat(temp, &sb) == 0 && S_ISREG(sb.st_mode)) { 97 | ptr = strrchr(link, '/'); 98 | if (ptr) 99 | *ptr = '\0'; 100 | } 101 | } 102 | 103 | list_for_each(block_list, device) { 104 | if (strcmp(device->sysfs_path, link) == 0) 105 | return device; 106 | } 107 | return NULL; 108 | } 109 | 110 | /** 111 | */ 112 | struct tail_device *tail_device_init(const char *path, struct list *block_list) 113 | { 114 | struct tail_device *device = NULL; 115 | struct block_device *block; 116 | 117 | block = _get_block(path, block_list); 118 | if (block) { 119 | device = malloc(sizeof(struct tail_device)); 120 | if (device && _get_slot(path, &device->slot) == 0) { 121 | device->raid = NULL; 122 | device->state = _get_state(path); 123 | device->errors = _get_errors(path); 124 | device->block = block; 125 | } else { 126 | free(device); 127 | device = NULL; 128 | } 129 | } 130 | return device; 131 | } 132 | 133 | /** 134 | */ 135 | void tail_device_fini(struct tail_device *device) 136 | { 137 | free(device); 138 | } 139 | -------------------------------------------------------------------------------- /src/lib/tail.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2022 Intel Corporation. 3 | 4 | #ifndef _TAIL_H_INCLUDED_ 5 | #define _TAIL_H_INCLUDED_ 6 | 7 | #include "block.h" 8 | #include "raid.h" 9 | 10 | /** 11 | */ 12 | #define TAIL_STATE_UNKNOWN 0x00 13 | #define TAIL_STATE_IN_SYNC 0x01 14 | #define TAIL_STATE_SPARE 0x02 15 | #define TAIL_STATE_FAULTY 0x04 16 | #define TAIL_STATE_WRITE_MOSTLY 0x08 17 | #define TAIL_STATE_BLOCKED 0x10 18 | 19 | /** 20 | */ 21 | struct tail_device { 22 | struct raid_device *raid; 23 | unsigned int errors; 24 | unsigned int slot; 25 | struct block_device *block; 26 | unsigned char state; 27 | }; 28 | 29 | /** 30 | */ 31 | struct tail_device *tail_device_init(const char *path, struct list *block_list); 32 | 33 | /** 34 | */ 35 | void tail_device_fini(struct tail_device *device); 36 | 37 | #endif /* _TAIL_H_INCLUDED_ */ 38 | -------------------------------------------------------------------------------- /src/lib/vmdssd.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2022 Intel Corporation. 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "config.h" 12 | #include "list.h" 13 | #include "pci_slot.h" 14 | #include "status.h" 15 | #include "sysfs.h" 16 | #include "utils.h" 17 | #include "vmdssd.h" 18 | #include "libled_private.h" 19 | 20 | #define ATTENTION_OFF 0xF /* (1111) Attention Off, Power Off */ 21 | #define ATTENTION_LOCATE 0x7 /* (0111) Attention Off, Power On */ 22 | #define ATTENTION_REBUILD 0x5 /* (0101) Attention On, Power On */ 23 | #define ATTENTION_FAILURE 0xD /* (1101) Attention On, Power Off */ 24 | 25 | struct ibpi2value ibpi_to_attention[] = { 26 | {LED_IBPI_PATTERN_NORMAL, ATTENTION_OFF}, 27 | {LED_IBPI_PATTERN_LOCATE, ATTENTION_LOCATE}, 28 | {LED_IBPI_PATTERN_FAILED_DRIVE, ATTENTION_FAILURE}, 29 | {LED_IBPI_PATTERN_REBUILD, ATTENTION_REBUILD}, 30 | {LED_IBPI_PATTERN_LOCATE_OFF, ATTENTION_OFF}, 31 | {LED_IBPI_PATTERN_ONESHOT_NORMAL, ATTENTION_OFF}, 32 | {LED_IBPI_PATTERN_UNKNOWN, 0} 33 | }; 34 | 35 | #define SYSFS_PCIEHP "/sys/module/pciehp" 36 | #define SYSFS_VMD "/sys/bus/pci/drivers/vmd" 37 | 38 | static char *get_slot_from_syspath(char *path) 39 | { 40 | char *cur, *ret = NULL; 41 | char *temp_path = strdup(path); 42 | 43 | if (!temp_path) 44 | return NULL; 45 | 46 | cur = strtok(temp_path, "/"); 47 | while (cur != NULL) { 48 | char *next = strtok(NULL, "/"); 49 | 50 | if ((next != NULL) && strcmp(next, "nvme") == 0) 51 | break; 52 | cur = next; 53 | } 54 | 55 | cur = strtok(cur, "."); 56 | if (cur) 57 | ret = strdup(cur); 58 | free(temp_path); 59 | 60 | return ret; 61 | } 62 | 63 | char *vmdssd_get_domain(const char *path) 64 | { 65 | char domain_path[PATH_MAX], real_domain_path[PATH_MAX]; 66 | 67 | snprintf(domain_path, PATH_MAX, "%s/%s/domain", 68 | SYSFS_VMD, basename(path)); 69 | if (realpath(domain_path, real_domain_path) == NULL) 70 | return NULL; 71 | 72 | return strtok(basename(real_domain_path), ":"); 73 | } 74 | 75 | bool vmdssd_check_slot_module(struct led_ctx *ctx, const char *slot_path) 76 | { 77 | char *address; 78 | struct cntrl_device *cntrl; 79 | 80 | address = get_text(slot_path, "address"); 81 | if (address == NULL) 82 | return false; 83 | 84 | // check if slot address contains vmd domain 85 | list_for_each(sysfs_get_cntrl_devices(ctx), cntrl) { 86 | if (cntrl->cntrl_type == LED_CNTRL_TYPE_VMD) { 87 | if (cntrl->domain[0] == '\0') 88 | continue; 89 | if (strstr(address, cntrl->domain) == NULL) 90 | continue; 91 | free(address); 92 | return true; 93 | } 94 | } 95 | 96 | free(address); 97 | return false; 98 | } 99 | 100 | struct pci_slot *vmdssd_find_pci_slot(struct led_ctx *ctx, char *device_path) 101 | { 102 | char *pci_addr; 103 | struct pci_slot *slot = NULL; 104 | 105 | pci_addr = get_slot_from_syspath(device_path); 106 | if (!pci_addr) 107 | return NULL; 108 | 109 | list_for_each(sysfs_get_pci_slots(ctx), slot) { 110 | if (strcmp(slot->address, pci_addr) == 0) 111 | break; 112 | slot = NULL; 113 | } 114 | free(pci_addr); 115 | if (slot == NULL || !vmdssd_check_slot_module(ctx, slot->sysfs_path)) 116 | return NULL; 117 | 118 | return slot; 119 | } 120 | 121 | enum led_ibpi_pattern vmdssd_get_attention(struct pci_slot *slot) 122 | { 123 | int attention = get_int(slot->sysfs_path, -1, "attention"); 124 | const struct ibpi2value *ibpi2val; 125 | 126 | if (attention == -1) 127 | return LED_IBPI_PATTERN_UNKNOWN; 128 | 129 | ibpi2val = get_by_value(attention, ibpi_to_attention, ARRAY_SIZE(ibpi_to_attention)); 130 | return ibpi2val->ibpi; 131 | } 132 | 133 | status_t vmdssd_write_attention_buf(struct pci_slot *slot, enum led_ibpi_pattern ibpi) 134 | { 135 | char attention_path[PATH_MAX]; 136 | char buf[WRITE_BUFFER_SIZE]; 137 | const struct ibpi2value *ibpi2val; 138 | 139 | uint16_t val; 140 | 141 | lib_log(slot->ctx, LED_LOG_LEVEL_DEBUG, 142 | "%s before: 0x%x\n", slot->address, 143 | (unsigned int)get_int(slot->sysfs_path, 0, "attention")); 144 | 145 | ibpi2val = get_by_ibpi(ibpi, ibpi_to_attention, ARRAY_SIZE(ibpi_to_attention)); 146 | if (ibpi2val->ibpi == LED_IBPI_PATTERN_UNKNOWN) { 147 | 148 | lib_log(slot->ctx, LED_LOG_LEVEL_INFO, 149 | "VMD: Controller doesn't support %s pattern\n", ibpi2str(ibpi)); 150 | return STATUS_INVALID_STATE; 151 | } 152 | val = (uint16_t)ibpi2val->value; 153 | 154 | snprintf(buf, WRITE_BUFFER_SIZE, "%u", val); 155 | snprintf(attention_path, PATH_MAX, "%s/attention", slot->sysfs_path); 156 | if (buf_write(attention_path, buf) != (ssize_t) strnlen(buf, WRITE_BUFFER_SIZE)) { 157 | lib_log(slot->ctx, LED_LOG_LEVEL_ERROR, 158 | "%s write error: %d\n", slot->sysfs_path, errno); 159 | return STATUS_FILE_WRITE_ERROR; 160 | } 161 | lib_log(slot->ctx, LED_LOG_LEVEL_DEBUG, 162 | "%s after: 0x%x\n", slot->address, 163 | (unsigned int)get_int(slot->sysfs_path, 0, "attention")); 164 | 165 | return STATUS_SUCCESS; 166 | } 167 | 168 | status_t vmdssd_write(struct block_device *device, enum led_ibpi_pattern ibpi) 169 | { 170 | struct pci_slot *slot; 171 | char *short_name = strrchr(device->sysfs_path, '/'); 172 | 173 | if (short_name) 174 | short_name++; 175 | else 176 | short_name = device->sysfs_path; 177 | 178 | if (ibpi == device->ibpi_prev) 179 | return STATUS_SUCCESS; 180 | 181 | if ((ibpi < LED_IBPI_PATTERN_NORMAL) || (ibpi > LED_IBPI_PATTERN_LOCATE_OFF)) 182 | return STATUS_INVALID_STATE; 183 | 184 | slot = vmdssd_find_pci_slot(device->cntrl->ctx, device->sysfs_path); 185 | if (!slot) { 186 | lib_log(device->cntrl->ctx, LED_LOG_LEVEL_DEBUG, 187 | "PCI hotplug slot not found for %s\n", short_name); 188 | return STATUS_NULL_POINTER; 189 | } 190 | 191 | return vmdssd_write_attention_buf(slot, ibpi); 192 | } 193 | 194 | char *vmdssd_get_path(const char *cntrl_path) 195 | { 196 | return strdup(cntrl_path); 197 | } 198 | -------------------------------------------------------------------------------- /src/lib/vmdssd.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1-or-later 2 | // Copyright (C) 2022 Intel Corporation. 3 | 4 | #ifndef _VMDSSD_H 5 | #define _VMDSSD_H 6 | 7 | #include 8 | 9 | #include "block.h" 10 | #include "led/libled.h" 11 | #include "utils.h" 12 | #include "sysfs.h" 13 | 14 | #define ATTENTION_OFF 0xF /* (1111) Attention Off, Power Off */ 15 | #define ATTENTION_LOCATE 0x7 /* (0111) Attention Off, Power On */ 16 | #define ATTENTION_REBUILD 0x5 /* (0101) Attention On, Power On */ 17 | #define ATTENTION_FAILURE 0xD /* (1101) Attention On, Power Off */ 18 | 19 | status_t vmdssd_write(struct block_device *device, enum led_ibpi_pattern ibpi); 20 | char *vmdssd_get_path(const char *cntrl_path); 21 | char *vmdssd_get_domain(const char *path); 22 | struct pci_slot *vmdssd_find_pci_slot(struct led_ctx *ctx, char *device_path); 23 | status_t vmdssd_write_attention_buf(struct pci_slot *slot, enum led_ibpi_pattern ibpi); 24 | enum led_ibpi_pattern vmdssd_get_attention(struct pci_slot *slot); 25 | bool vmdssd_check_slot_module(struct led_ctx *ctx, const char *slot_path); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /systemd/Makefile.am: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-only 2 | # Copyright (C) 2009 Intel Corporation. 3 | 4 | # Installation directory of ledmon systemd service unit. 5 | systemddir = @SYSTEMD_PATH@ 6 | SED = sed 7 | 8 | CLEANFILES = ledmon.service ledmon.service.tmp 9 | 10 | systemd_DATA = ledmon.service 11 | 12 | ledmon.service : ledmon.service.in 13 | $(SED) -e 's|@sbindir[@]|$(sbindir)|g' < $< > $@.tmp 14 | mv $@.tmp $@ 15 | -------------------------------------------------------------------------------- /systemd/ledmon.service.in: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Enclosure LED Utilities 3 | 4 | [Install] 5 | WantedBy=multi-user.target 6 | 7 | [Service] 8 | Type=simple 9 | User=root 10 | ExecStart=@sbindir@/ledmon --foreground 11 | Restart=on-failure 12 | -------------------------------------------------------------------------------- /tests/Makefile.am: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-3.0-or-later 2 | # Copyright (C) 2024 Intel Corporation. 3 | 4 | AM_CPPFLAGS = -I@srcdir@./src/lib/include 5 | 6 | EXTRA_DIST=lib_unit_test.c 7 | 8 | if WITH_TEST 9 | all: lib_unit_test 10 | 11 | check_PROGRAMS = lib_unit_test 12 | lib_unit_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS) 13 | lib_unit_test_LDADD = ../src/lib/libled.la $(LIBCHECK_LIBS) 14 | lib_unit_test_SOURCES = lib_unit_test.c 15 | endif 16 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | ## Running tests with pytest 2 | 3 | To run tests suite, make sure python and pytest with at least 3.11 version is installed. 4 | 5 | ## How to compile with tests enabled 6 | 7 | Run `autogen.sh` to generate compilation configurations. 8 | Run `./configure` with `--enable-test` to enable building unit tests and add a target for `make check`. This option requires `--enable-library` too. 9 | Compile with `make` or `make check` when you want to run unit tests just after compilation. 10 | 11 | ## Parameters to configure 12 | 13 | There is possibility to set: 14 | 15 | - `--ledctl-binary` - path to ledctl, 16 | - `--slot-filters` - comma separated list of slot filters to exclude, 17 | - `--controller-filters` - comma separated list of controller types to exclude. 18 | 19 | 20 | ## How to run tests 21 | 22 | There are few methods to run tests: 23 | 24 | - run `pytest` with or without parameters to set, 25 | 26 | ```shell 27 | $ pytest --ledctl-binary=src/ledctl/ledctl --slot-filters=sg3-,sg2- --controller-filters=VMD 28 | ``` 29 | - run `make check` which will compile the code and run tests. Logs will be placed in `./test-suite.log` file, 30 | - run script `tests/runtests.sh`. -------------------------------------------------------------------------------- /tests/check_symbol_visibility.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | # SPDX-License-Identifier: GPL-3.0-or-later 4 | # Copyright (C) 2023 Red Hat Inc. 5 | 6 | # Ensure we only have shared library symbols which start with "led_*" 7 | 8 | 9 | tmpf=$(mktemp /tmp/ledmon_sym.XXXXXXXX) || exit 1 10 | 11 | function cleanup { 12 | rm -f "${tmpf}" || exit 1 13 | } 14 | 15 | trap cleanup EXIT 16 | 17 | nm -D src/lib/.libs/libled.so | grep " T " | grep -v " led_" > "${tmpf}" 18 | 19 | found=$(wc -l < "${tmpf}") || exit 1 20 | 21 | if [[ ${found} -ne "0" ]]; then 22 | echo "ERROR: Public symbols found which don't have led_ prefix for shared library" 23 | cat "${tmpf}" 24 | exit 1 25 | fi 26 | 27 | exit 0 28 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-3.0-or-later 2 | 3 | import logging 4 | 5 | LOGGER = logging.getLogger(__name__) 6 | 7 | store_params = [ 8 | ["--ledctl-binary", "src/ledctl/ledctl", "Path to the ledctl binary."], 9 | [ 10 | "--slot-filters", "none", 11 | "List comma separated. Filter out slots starting with filter." 12 | ], 13 | [ 14 | "--controller-filters", "none", 15 | "List comma separated. Filter out controllers matching by name e.g. VMD, SCSI, NPEM." 16 | ] 17 | ] 18 | 19 | 20 | def pytest_addoption(parser): 21 | for param, def_val, help_txt in store_params: 22 | parser.addoption(param, action="store", default=def_val, help=help_txt) 23 | 24 | 25 | def pytest_collection_modifyitems(session, config, items): 26 | for param in store_params: 27 | LOGGER.info(f'[CONFIG] {param[0]}: {config.getoption(param[0])}') 28 | 29 | 30 | def pytest_generate_tests(metafunc): 31 | params = { 32 | "ledctl_binary": metafunc.config.option.ledctl_binary, 33 | "slot_filters": metafunc.config.option.slot_filters, 34 | "controller_filters": metafunc.config.option.controller_filters 35 | } 36 | for param, val in params.items(): 37 | if param in metafunc.fixturenames and val is not None: 38 | metafunc.parametrize(param, [val.split(",")]) 39 | -------------------------------------------------------------------------------- /tests/ledctl/ledctl_cmd.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-3.0-or-later 2 | # Copyright (C) 2023 Intel Corporation. 3 | 4 | import subprocess 5 | import logging 6 | from os import listdir 7 | from os.path import join, realpath, exists 8 | import re 9 | 10 | LOGGER = logging.getLogger(__name__) 11 | 12 | 13 | class Slot: 14 | 15 | def __init__(self, cntrl_type, slot_id, state, device_node): 16 | self.cntrl_type = cntrl_type 17 | self.slot = slot_id 18 | self.state = state.lower() 19 | self.device_node = None 20 | if device_node.startswith('/dev/'): 21 | self.device_node = device_node 22 | 23 | def __str__(self): 24 | return "slot: %s device: %s" % (self.slot, self.device_node) 25 | 26 | 27 | class LedctlCmd: 28 | 29 | slot_mgmt_ctrls = ["SCSI", "VMD", "NPEM"] 30 | 31 | # These base states should be supported by all controllers 32 | base_states = ["failure", "locate", "normal", "rebuild"] 33 | 34 | def __init__(self, 35 | ledctl_bin=["none"], 36 | slot_filters="none", 37 | controller_filters="none"): 38 | # We cares about first entry (not full valdation but it is enough for test purposes) 39 | self.bin = ledctl_bin[0] 40 | 41 | # The purpose or slot filters is exclude unsupported but recognized slots, apply it 42 | # globally. 43 | self.slot_filters = slot_filters 44 | 45 | # Give possibility to skip controllers. 46 | self.slot_ctrls = [ 47 | i for i in self.slot_mgmt_ctrls if i not in controller_filters 48 | ] 49 | 50 | def run_ledctl_cmd(self, params: list, output=False, check=True): 51 | params.insert(0, "sudo") 52 | params.insert(1, self.bin) 53 | 54 | LOGGER.debug(f"Command: {params}") 55 | # Without "shell=True" returncode and some output lines are omitted 56 | return subprocess.run(" ".join(params), 57 | shell=True, 58 | universal_newlines=True, 59 | capture_output=output) 60 | 61 | # Run ledctl command and expect it to succeed 62 | def run_ledctl_cmd_valid(self, params: list): 63 | result = self.run_ledctl_cmd(params, output=True) 64 | if result.returncode != 0: 65 | raise Exception( 66 | "Command expected to succeed, but non 0 status was returned!") 67 | 68 | LOGGER.debug(f"Command returned:\n {result.stdout}") 69 | return result 70 | 71 | # Run ledctl command and expect it to fail 72 | def run_ledctl_cmd_not_valid(self, params: list): 73 | result = self.run_ledctl_cmd(params, output=True) 74 | if result.returncode == 0: 75 | raise Exception("Command succeed, but was expected to fail!") 76 | 77 | LOGGER.debug(f"Command returned:\n {result.stderr}") 78 | return result 79 | 80 | # Ledctl Commands 81 | 82 | def set_slot_state(self, slot: Slot, state): 83 | self.run_ledctl_cmd([ 84 | "--set-slot", "--controller-type", slot.cntrl_type, "--slot", 85 | slot.slot, "--state", state 86 | ]) 87 | 88 | def set_device_state(self, slot: Slot, state): 89 | self.run_ledctl_cmd([ 90 | "--set-slot", "--controller-type", slot.cntrl_type, "--device", 91 | slot.device_node, "--state", state 92 | ]) 93 | 94 | def get_slot(self, slot: Slot): 95 | out = self.run_ledctl_cmd_valid([ 96 | "--get-slot", "--controller-type", slot.cntrl_type, "--slot", 97 | slot.slot 98 | ]).stdout 99 | return self.parse_slot_line(slot.cntrl_type, out) 100 | 101 | def get_slot_by_device(self, slot: Slot): 102 | out = self.run_ledctl_cmd_valid([ 103 | "--get-slot", "--controller-type", slot.cntrl_type, "--device", 104 | slot.device_node 105 | ]).stdout 106 | return self.parse_slot_line(slot.cntrl_type, out) 107 | 108 | def get_slot_by_device_cntrl(self, dev_node, cntrl): 109 | # While using this method controllers may be not filtered out. 110 | # Do not return slot for controller removed from test. 111 | if cntrl not in self.slot_ctrls: 112 | return None 113 | 114 | out = self.run_ledctl_cmd_valid( 115 | ["--get-slot", "--controller-type", cntrl, "--device", 116 | dev_node]).stdout 117 | return self.parse_slot_line(cntrl, out) 118 | 119 | def default_controller_by_device(self, dev_node): 120 | result = self.run_ledctl_cmd_valid( 121 | ["--default-controller", "--device", dev_node]).stdout 122 | return result.rstrip() 123 | 124 | def list_slots(self, controller_type): 125 | rc = [] 126 | out = self.run_ledctl_cmd_valid( 127 | ["--list-slots", "--controller-type", controller_type]).stdout 128 | 129 | for line in out.split("\n"): 130 | s = self.parse_slot_line(controller_type, line) 131 | if s is not None: 132 | rc.append(s) 133 | return rc 134 | 135 | def set_ibpi(self, dev_node, state): 136 | option = "%s=%s" % (state, dev_node) 137 | self.run_ledctl_cmd([option]) 138 | 139 | def is_test_flag_enabled(self): 140 | try: 141 | self.run_ledctl_cmd(["-T"]) 142 | except subprocess.CalledProcessError: 143 | raise AssertionError( 144 | "Test flag is disabled. Please add configure option \"--enable-test\"." 145 | ) 146 | 147 | # Helper Functions 148 | 149 | def is_slot_excluded(self, slot: Slot): 150 | if self.slot_filters == "none": 151 | return False 152 | 153 | for slot_filter in self.slot_filters: 154 | if not slot_filter: 155 | return False 156 | if slot.slot.startswith(slot_filter): 157 | # Filter out this slot 158 | return True 159 | return False 160 | 161 | def get_controllers_with_slot_functionality(self): 162 | rc = {} 163 | 164 | out = self.run_ledctl_cmd_valid(["--list-controllers"]).stdout 165 | for raw_line in out.split("\n"): 166 | line = raw_line.strip() 167 | for ctrl in self.slot_ctrls: 168 | if ctrl in line: 169 | rc[ctrl] = True 170 | break 171 | return rc.keys() 172 | 173 | # Respect controller filter 174 | def get_slots(self, cntrl): 175 | if cntrl not in self.slot_ctrls: 176 | raise AssertionError(f"Controller \"{cntrl}\" filtered out") 177 | return self.list_slots(cntrl) 178 | 179 | # Respect controller filter 180 | def get_slots_with_device(self, cntrl): 181 | return [s for s in self.get_slots(cntrl) if s.device_node is not None] 182 | 183 | def get_all_slots(self): 184 | all_slots = [] 185 | for controller in self.get_controllers_with_slot_functionality(): 186 | all_slots.extend(self.list_slots(controller)) 187 | return all_slots 188 | 189 | # Output parsers 190 | 191 | def parse_slot_line(self, controller, rawline): 192 | regex_pat = r"^slot: (.+)led state: (.+)device: (.*)$" 193 | slot_line_re = re.compile(regex_pat) 194 | 195 | line = rawline.strip() 196 | if len(line) == 0: 197 | return None 198 | 199 | match = slot_line_re.match(line) 200 | if match is None: 201 | raise Exception( 202 | f"Text line '{line}' did not match regex '{regex_pat}'") 203 | 204 | slot = Slot(controller, 205 | match.group(1).strip(), 206 | match.group(2).strip(), 207 | match.group(3).strip()) 208 | 209 | if self.is_slot_excluded(slot): 210 | return None 211 | return slot 212 | 213 | # Other 214 | 215 | def get_mp_nodes(self): 216 | sys_block_path = "/sys/block" 217 | nvme_subsys_subpath = "/nvme-subsystem/nvme-subsys" 218 | 219 | mp_drives = [] 220 | for f in listdir(sys_block_path): 221 | rp = realpath(join(sys_block_path, f)) 222 | if nvme_subsys_subpath in rp: 223 | node = join("/dev", f) 224 | if exists(node): 225 | mp_drives.append(node) 226 | return mp_drives 227 | -------------------------------------------------------------------------------- /tests/ledctl/parameters_test.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-3.0-or-later 2 | # Copyright (C) 2023 Intel Corporation. 3 | 4 | from ledctl.ledctl_cmd import LedctlCmd 5 | import pytest 6 | import logging 7 | 8 | LOGGER = logging.getLogger(__name__) 9 | 10 | SUCCESS_EXIT_CODE = 0 11 | CMDLINE_ERROR_EXIT_CODE = 35 12 | 13 | # Line length limit for best user experience. 14 | MAX_LINE_LENGTH = 100 15 | 16 | 17 | def test_parameters_are_valid_long_test_flag(ledctl_binary): 18 | cmd = LedctlCmd(ledctl_binary) 19 | cmd.is_test_flag_enabled() 20 | cmd.run_ledctl_cmd("--list-controllers --test".split()) 21 | 22 | 23 | @pytest.mark.parametrize( 24 | "valid_mode_commands", 25 | [ 26 | "-h", "--help", "-v", "--version", "-L -T", "--list-controllers -T", 27 | "-P -n vmd -T", "--list-slots --controller-type=vmd -T", 28 | "-G -n vmd -d /dev/nvme0n1 -T", 29 | "--get-slot --controller-type=vmd --device=/dev/nvme0n1 -T", 30 | "-G -n vmd -p 1 -T", "--get-slot --controller-type=vmd --slot=1 -T", 31 | "-G -n vmd -d /dev/nvme0n1 -r state -T", 32 | "--get-slot --controller-type=vmd --device=/dev/nvme0n1 --print=state -T", 33 | "-G -n vmd -p 1 -r state -T", 34 | "--get-slot --controller-type=vmd --slot=1 --print=state -T", 35 | "-S -n vmd -p 1 -s normal -T", 36 | "--set-slot --controller-type=vmd --slot=1 --state=normal -T", 37 | "-B -d /dev/nvme0n1 -T", 38 | "--default-controller --device /dev/nvme0n1 -T" 39 | ], 40 | ) 41 | def test_parameters_are_valid_short_test_flag(ledctl_binary, 42 | valid_mode_commands): 43 | cmd = LedctlCmd(ledctl_binary) 44 | cmd.is_test_flag_enabled() 45 | cmd.run_ledctl_cmd(valid_mode_commands.split()) 46 | 47 | 48 | @pytest.mark.parametrize( 49 | "valid_ibpi_commands", 50 | [ 51 | "normal=/dev/nvme0n1 -T", "normal=/dev/nvme0n1 -x -T", 52 | "normal=/dev/nvme0n1 --listed-only -T", 53 | "normal=/dev/nvme0n1 -l /var/log/ledctl.log -T", 54 | "normal=/dev/nvme0n1 --log=/var/log/ledctl.log -T", 55 | "normal=/dev/nvme0n1 --log-level=all -T", 56 | "normal=/dev/nvme0n1 --all -T", "-x normal={ /dev/nvme0n1 } -T", 57 | "locate=/dev/nvme0n1 rebuild={ /sys/block/sd[a-b] } -T", 58 | "off={ /dev/sda /dev/sdb } -T", "locate=/dev/sda,/dev/sdb,/dev/sdc -T" 59 | ], 60 | ) 61 | def test_ibpi_parameters_are_valid_short_test_flag(ledctl_binary, 62 | valid_ibpi_commands): 63 | cmd = LedctlCmd(ledctl_binary) 64 | cmd.is_test_flag_enabled() 65 | cmd.run_ledctl_cmd(valid_ibpi_commands.split()) 66 | 67 | 68 | @pytest.mark.parametrize( 69 | "log_path_commands", 70 | [ 71 | "--list-controllers --log=/root/test.log -T", 72 | "normal=/dev/nvme0n1 --listed-only -l /root/test.log -T", 73 | ], 74 | ) 75 | def test_parameters_log_path(ledctl_binary, log_path_commands): 76 | cmd = LedctlCmd(ledctl_binary) 77 | cmd.is_test_flag_enabled() 78 | output = cmd.run_ledctl_cmd_valid(log_path_commands.split()).stdout 79 | assert "LOG_PATH=/root/test.log" in output 80 | 81 | 82 | def test_parameter_log_level_all_values(ledctl_binary): 83 | cmd = LedctlCmd(ledctl_binary) 84 | cmd.is_test_flag_enabled() 85 | tested_log_levels = ["warning", "debug", "all", "info", "quiet", "error"] 86 | for level in tested_log_levels: 87 | args = "--list-controllers -T --log-level=" + level 88 | output = cmd.run_ledctl_cmd_valid(args.split()).stdout 89 | assert "LOG_LEVEL=" + level.upper() in output 90 | args = "--list-controllers -T --" + level 91 | output = cmd.run_ledctl_cmd_valid(args.split()).stdout 92 | assert "LOG_LEVEL=" + level.upper() in output 93 | 94 | 95 | def parse_version(lines): 96 | help_header = [ 97 | "Intel(R) Enclosure LED Control Application", 98 | "Copyright (C) 2009-2024 Intel Corporation." 99 | ] 100 | 101 | assert (lines[0].startswith(help_header[0])) 102 | assert (lines[1] == help_header[1]) 103 | assert (lines[2] == "") 104 | 105 | 106 | # Check help style and verify that size is no longer than 100. 107 | def parse_help(lines): 108 | parse_version(lines) 109 | 110 | line_inter = iter(lines[3:]) 111 | 112 | line = next(line_inter) 113 | assert (line.startswith("Usage: ledctl --")) 114 | assert (line.endswith(" [option...] ...")) 115 | assert (next(line_inter) == "") 116 | 117 | # Now description comes, ended by empty line. 118 | while next(line_inter) != "": 119 | continue 120 | 121 | # Here options comes. 122 | line = next(line_inter) 123 | assert (line == "Options:" or line == "Modes:") 124 | 125 | # Control place of short option printing to keep columns equals 126 | length = 0 127 | while True: 128 | line = next(line_inter) 129 | if line == "": break 130 | LOGGER.debug(f"line {line}") 131 | if length == 0: 132 | length = line.find(" -") 133 | else: 134 | assert (line.find(" -") == length) 135 | 136 | help_footer = [ 137 | "Refer to ledctl(8) man page for more detailed description (man ledctl).", 138 | "Bugs should be reported at: https://github.com/intel/ledmon/issues" 139 | ] 140 | 141 | assert (next(line_inter) == help_footer[0]) 142 | assert (next(line_inter) == help_footer[1]) 143 | assert (next(line_inter) == "") 144 | assert (next(line_inter, "end") == "end") 145 | 146 | # Check globally that line limit is not exceeded. 147 | for line in lines: 148 | assert (len(line) <= MAX_LINE_LENGTH) 149 | 150 | 151 | @pytest.mark.parametrize( 152 | "help_cmd", 153 | [ 154 | "--help", 155 | "-h", 156 | "--help --badflag", 157 | "-hd", 158 | "-h -s", 159 | "--set-slot --help", 160 | "-S -h --badflag", 161 | "-Sh --badflag", 162 | "--get-slot --help", 163 | "-Ghb", 164 | "--list-controllers --help", 165 | "--ibpi --help", 166 | "--list-slots --help", 167 | "-L -h", 168 | "--default-controller --help", 169 | "-Bh", 170 | ], 171 | ) 172 | # Check formatting, header and footer. 173 | # Test does not check options/mode content and description. 174 | def test_main_help(ledctl_binary, help_cmd): 175 | cmd = LedctlCmd(ledctl_binary) 176 | cmd.is_test_flag_enabled() 177 | res = cmd.run_ledctl_cmd_valid(help_cmd.split()).stdout 178 | lines = res.split('\n') 179 | parse_help(lines) 180 | 181 | 182 | @pytest.mark.parametrize( 183 | "version_cmd", 184 | ["--version", "-v", "-vh", "--version --badflag"], 185 | ) 186 | def test_version(ledctl_binary, version_cmd): 187 | cmd = LedctlCmd(ledctl_binary) 188 | cmd.is_test_flag_enabled() 189 | res = cmd.run_ledctl_cmd_valid(version_cmd.split()).stdout 190 | lines = res.split('\n') 191 | parse_version(lines) 192 | 193 | 194 | @pytest.mark.parametrize("nexist_dev", ["/dev/nvr_gon_giv_u_up"]) 195 | # Check if proper message is returned for not existing device. 196 | def test_nexist_dev_output(ledctl_binary, nexist_dev): 197 | cmd = LedctlCmd(ledctl_binary) 198 | cmd.is_test_flag_enabled() 199 | res = cmd.run_ledctl_cmd_not_valid(["locate={}".format(nexist_dev)]) 200 | assert "Could not find {}".format(nexist_dev) in res.stderr 201 | 202 | 203 | @pytest.mark.parametrize("unsprtd_dev", ["/dev/zero"]) 204 | # Check if proper message is returned for unsupported device. 205 | def test_unsprtd_dev_output(ledctl_binary, unsprtd_dev): 206 | cmd = LedctlCmd(ledctl_binary) 207 | cmd.is_test_flag_enabled() 208 | res = cmd.run_ledctl_cmd_not_valid(["locate={}".format(unsprtd_dev)]) 209 | assert "{}: device not supported".format(unsprtd_dev) in res.stderr 210 | -------------------------------------------------------------------------------- /tests/ledctl/slot_test.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-3.0-or-later 2 | # Copyright (C) 2023-2024 Red Hat Inc. 3 | 4 | # The purpose of this file is testing interactions with hardware. Tests are projected 5 | # to be executed only if possible (required hardware is available). 6 | # Ledctl support many blinking standards, there is no possibility to test them all at once. 7 | # We parametrize each test by controller type to gather logs confirming that it was run 8 | # on appropriative hardware. 9 | # Collecting such results will be beneficial for release stuff, it gives some creditability that it 10 | # was minimally tested. 11 | 12 | import pytest 13 | import logging 14 | from ledctl_cmd import LedctlCmd 15 | 16 | LOGGER = logging.getLogger(__name__) 17 | 18 | 19 | def get_slots_with_device_or_skip(cmd: LedctlCmd, cntrl): 20 | try: 21 | slots_with_device_node = cmd.get_slots_with_device(cntrl) 22 | except AssertionError as e: 23 | pytest.skip(str(e)) 24 | 25 | if len(slots_with_device_node) == 0: 26 | pytest.skip("No slot with device found") 27 | return slots_with_device_node 28 | 29 | 30 | def filter_by_default_controller(cmd: LedctlCmd, slots_to_test): 31 | filtered_slots = [] 32 | for slot in slots_to_test: 33 | default_cntrl = cmd.default_controller_by_device(slot.device_node) 34 | if default_cntrl == slot.cntrl_type: 35 | filtered_slots.append(slot) 36 | return filtered_slots 37 | 38 | 39 | def verify_state(slot, current, expected, msg): 40 | if slot.cntrl_type == "SCSI" and expected == "rebuild": 41 | # No good way to validate this one as read value won't match what we sent down. 42 | pass 43 | else: 44 | assert current == expected, msg 45 | 46 | 47 | @pytest.mark.parametrize("cntrl", LedctlCmd.slot_mgmt_ctrls) 48 | def test_ibpi(ledctl_binary, slot_filters, controller_filters, cntrl): 49 | """ 50 | Test setting the led status by using IBPI syntax for the disk under the chosen controller. 51 | Limited to controllers with slots feature support. 52 | """ 53 | 54 | cmd = LedctlCmd(ledctl_binary, slot_filters, controller_filters) 55 | slots_to_test = get_slots_with_device_or_skip(cmd, cntrl) 56 | slots_to_test = filter_by_default_controller(cmd, slots_to_test) 57 | 58 | if not slots_to_test: 59 | pytest.skip( 60 | "Devices detected but this is not primary controller for any drive, skipping" 61 | ) 62 | 63 | for slot in slots_to_test: 64 | for state in LedctlCmd.base_states: 65 | cmd.set_ibpi(slot.device_node, state) 66 | cur = cmd.get_slot(slot) 67 | verify_state( 68 | cur, cur.state, state, 69 | f"unable to set \"{slot.device_node}\" to \"{state}\", current = \"{cur.state}\" using ibpi syntax" 70 | ) 71 | 72 | 73 | @pytest.mark.parametrize("cntrl", LedctlCmd.slot_mgmt_ctrls) 74 | def test_set_slot_by_slot(ledctl_binary, slot_filters, controller_filters, 75 | cntrl): 76 | """ 77 | Test that we can set slots to different states and verify that they reported a change, using --slot. 78 | """ 79 | 80 | cmd = LedctlCmd(ledctl_binary, slot_filters, controller_filters) 81 | try: 82 | slots = [s for s in cmd.get_slots(cntrl)] 83 | except AssertionError as e: 84 | pytest.skip(str(e)) 85 | 86 | if len(slots) == 0: 87 | pytest.skip("No slot found") 88 | 89 | for slot in slots: 90 | for state in LedctlCmd.base_states: 91 | cmd.set_slot_state(slot, state) 92 | cur = cmd.get_slot(slot) 93 | verify_state( 94 | cur, cur.state, state, 95 | f"unable to set \"{slot}\" to \"{state}\", current = \"{cur.state}\" using slot" 96 | ) 97 | 98 | 99 | def slot_set_and_get_by_device_all(cmd: LedctlCmd, slot): 100 | for state in LedctlCmd.base_states: 101 | cmd.set_device_state(slot, state) 102 | cur = cmd.get_slot_by_device(slot) 103 | verify_state( 104 | cur, cur.state, state, 105 | f"unable to set \"{slot}\" to \"{state}\", current = \"{cur.state}\" using --device" 106 | ) 107 | 108 | 109 | @pytest.mark.parametrize("cntrl", LedctlCmd.slot_mgmt_ctrls) 110 | def test_set_slot_by_device(ledctl_binary, slot_filters, controller_filters, 111 | cntrl): 112 | """ 113 | Test that we can set slots to different states and verify that they reported a change, using device. 114 | """ 115 | 116 | cmd = LedctlCmd(ledctl_binary, slot_filters, controller_filters) 117 | slots_with_device_node = get_slots_with_device_or_skip(cmd, cntrl) 118 | 119 | for slot in slots_with_device_node: 120 | slot_set_and_get_by_device_all(cmd, slot) 121 | 122 | 123 | def test_nvme_multipath_drives(ledctl_binary, slot_filters, 124 | controller_filters): 125 | """ 126 | Special test for multipath drives using both set methods and get via device. We need to check 127 | if ledctl provides nvme multipath minimal support. 128 | """ 129 | cmd = LedctlCmd(ledctl_binary, slot_filters, controller_filters) 130 | 131 | mp_drives = cmd.get_mp_nodes() 132 | if len(mp_drives) == 0: 133 | pytest.skip("No nvme multipath drives found") 134 | 135 | for mp_drive in mp_drives: 136 | mp_cntrl = cmd.default_controller_by_device(mp_drive) 137 | slot = cmd.get_slot_by_device_cntrl(mp_drive, mp_cntrl) 138 | if slot is None: 139 | continue 140 | 141 | for state in cmd.base_states: 142 | cmd.set_ibpi(slot.device_node, state) 143 | cur = cmd.get_slot_by_device(slot) 144 | verify_state( 145 | cur, cur.state, state, 146 | f"unable to set \"{slot}\" to \"{state}\", current = \"{cur.state}\" using ibpi syntax" 147 | ) 148 | 149 | slot_set_and_get_by_device_all(cmd, slot) 150 | -------------------------------------------------------------------------------- /tests/licensing.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3.11 2 | 3 | # SPDX-License-Identifier: GPL-3.0-or-later 4 | # Copyright (C) 2024 Intel Corporation. 5 | 6 | import argparse, logging, os, re, sys 7 | from typing import NamedTuple 8 | 9 | LOGGER = logging.getLogger(__name__) 10 | LOGGER.setLevel(logging.DEBUG) 11 | 12 | comment_marks = { 13 | ".c": '//', 14 | ".h": '//', 15 | ".am": '#', 16 | ".sh": '#', 17 | ".ac": '#', 18 | ".py": '#', 19 | ".pod": '#', 20 | } 21 | 22 | 23 | # For each used SPDX header, there must be full license file in dir. 24 | # It does not check file content. 25 | def check_license_files(path, spdx_licenses): 26 | for license in spdx_licenses: 27 | if not os.path.isfile(f"{path}/LICENSES/LICENSE.{license}"): 28 | raise Exception( 29 | f"License {license} does not have a license file in LICENSES directory" 30 | ) 31 | 32 | 33 | #FIXME: Should it be configurable by Yaml? 34 | class Directory_license(NamedTuple): 35 | licenses: dict[str, set[str]] 36 | 37 | 38 | DIRECTORY_LICENSES: dict[str, Directory_license] = { 39 | "src/ledctl": 40 | Directory_license( 41 | licenses={ 42 | ".c": ["GPL-2.0-only", "LGPL-2.1-or-later"], 43 | ".h": ["GPL-2.0-only", "LGPL-2.1-or-later"], 44 | ".am": ["GPL-2.0-only"], 45 | }), 46 | "src/ledmon": 47 | Directory_license( 48 | licenses={ 49 | ".c": ["GPL-2.0-only", "LGPL-2.1-or-later"], 50 | ".h": ["GPL-2.0-only", "LGPL-2.1-or-later"], 51 | ".am": ["GPL-2.0-only"], 52 | }), 53 | # NOTE: Files in lib directory must be released under LGPL, be careful when extending. 54 | "src/lib": 55 | Directory_license( 56 | licenses={ 57 | ".c": ["LGPL-2.1-or-later"], 58 | ".h": ["LGPL-2.1-or-later"], 59 | ".am": ["GPL-2.0-only"], 60 | }), 61 | "config": 62 | Directory_license(licenses={ 63 | ".h": ["LGPL-2.1-or-later"], 64 | }), 65 | # Default, used if file is not placed in directory explicitly listed 66 | ".": 67 | Directory_license( 68 | licenses={ 69 | ".c": ["GPL-2.0-only", "GPL-3.0-or-later"], 70 | ".h": ["GPL-2.0-only"], 71 | ".am": ["GPL-2.0-only", "GPL-3.0-or-later"], 72 | ".sh": ["GPL-2.0-only", "GPL-3.0-or-later"], 73 | ".ac": ["GPL-3.0-or-later"], 74 | ".py": ["GPL-3.0-or-later"], 75 | ".pod": ["GPL-2.0-only"] 76 | }), 77 | } 78 | 79 | 80 | def get_licenses_set(): 81 | spdx_licenses = set() 82 | 83 | for directory in DIRECTORY_LICENSES.values(): 84 | for licenses in directory.licenses.values(): 85 | spdx_licenses.update(licenses) 86 | 87 | return spdx_licenses 88 | 89 | 90 | # It compares DIRECTORY_LICENSES keys, first match wins. Order matters 91 | def get_allowed_licenses(path): 92 | cwd = os.getcwd() 93 | 94 | for key_dir in DIRECTORY_LICENSES.keys(): 95 | subpath = os.path.realpath(f"{cwd}/{key_dir}") 96 | 97 | if os.path.dirname(path).startswith(subpath): 98 | return DIRECTORY_LICENSES[key_dir].licenses 99 | 100 | raise Exception(f"Cannot find allowed licenses map for {path}") 101 | 102 | 103 | def check_license_header(path, first_line, file_type): 104 | allowed_licenses = get_allowed_licenses(path) 105 | comment_mark = comment_marks[file_type] 106 | 107 | for license in allowed_licenses[file_type]: 108 | license_string = f"{comment_mark} SPDX-License-Identifier: {license}" 109 | LOGGER.debug(F"Checking for \"{license_string}\" license string") 110 | 111 | if license_string == first_line: 112 | return True 113 | 114 | return False 115 | 116 | 117 | # There are no strong requirements for copyrights and how ofter they should be updated. 118 | # Keep them in simple form "Copyright (C) Intel Corporation." to not have to update them 119 | # before each release. 120 | def check_Intel_copyright(line): 121 | copyright = line.partition('Copyright ')[2] 122 | regex_pat = r"\(C\) 20[0-9]{2} Intel Corporation.$" 123 | 124 | copyright_line_re = re.compile(regex_pat) 125 | 126 | match = copyright_line_re.match(copyright) 127 | if match is None: 128 | raise Exception( 129 | f"\'{copyright}\' is Intel Copyright in not expected format \'{regex_pat}\'" 130 | ) 131 | 132 | 133 | def check_copyright_line(line, file_type): 134 | comment_mark = comment_marks[file_type] 135 | 136 | if not line.startswith(f"{comment_mark} Copyright"): 137 | raise Exception(f'\"{line}\" is not Copyright line') 138 | 139 | if "Intel" in line: 140 | check_Intel_copyright(line) 141 | 142 | 143 | def check_licensing(path): 144 | file_type = os.path.splitext(path)[1] 145 | file = open(path) 146 | line = file.readline().strip() 147 | 148 | if line.startswith("#!/"): 149 | line = file.readline().strip() 150 | # Here comes the interpreter. We expect, SPDX in third line but second line must be empty. 151 | if line != "": 152 | raise Exception(f"Second line must be empty, it is \"{line}\"") 153 | line = file.readline().strip() 154 | 155 | check_license_header(path, line, file_type) 156 | 157 | # Loop for Copyrights. Scan them until empty line is reached. 158 | while True: 159 | line = file.readline().strip() 160 | if line == "": 161 | return 162 | check_copyright_line(line, file_type) 163 | 164 | 165 | def main(): 166 | 167 | ch = logging.StreamHandler() 168 | ch.setLevel(logging.INFO) 169 | LOGGER.addHandler(ch) 170 | 171 | if not os.path.exists(f"{os.getcwd()}/tests/licensing.py"): 172 | LOGGER.error( 173 | "The test must be run main repo directory e.g. ./tests/licensing.py" 174 | ) 175 | return 1 176 | 177 | parser = argparse.ArgumentParser( 178 | prog="licensing.py", description='Check licenses used in files') 179 | 180 | parser.add_argument( 181 | '-f', 182 | '--file', 183 | action='append', 184 | required=True, 185 | help='file or dir path to scan, Can be specified multiple times.') 186 | parser.add_argument('-l', 187 | '--log-level', 188 | action='store', 189 | required=False, 190 | help="Log level, use python logging module log levels") 191 | 192 | args = parser.parse_args() 193 | 194 | if args.log_level: 195 | ch.setLevel(logging.getLevelNamesMapping()[args.log_level]) 196 | 197 | path = os.getcwd() 198 | 199 | # Ensure that there is a file with SPDX header in LICENSES directory. 200 | check_license_files(path, get_licenses_set()) 201 | 202 | files = [] 203 | # Normalize them to full paths 204 | for file in args.file: 205 | files.append(f"{path}/{file}") 206 | 207 | ret = 0 208 | for file in files: 209 | LOGGER.info(f"Checking {file}") 210 | try: 211 | check_licensing(file) 212 | except Exception as e: 213 | LOGGER.error(f"\t{e}") 214 | LOGGER.error(f"Check failed for {file}") 215 | LOGGER.error("Please refer to CONTRIBUTING.md") 216 | ret = 1 217 | else: 218 | LOGGER.info(f"Passed {file}") 219 | return ret 220 | 221 | 222 | if __name__ == '__main__': 223 | sys.exit(main()) 224 | -------------------------------------------------------------------------------- /tests/runtests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | # SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) 6 | 7 | PYTEST=pytest 8 | 9 | if ! command -v $PYTEST &> /dev/null 10 | then 11 | PYTEST=pytest-3 12 | fi 13 | 14 | #Put us in a consistent spot 15 | if [ ! -e "./tests" ];then 16 | echo "We are running from test folder directly." 17 | cd .. 18 | fi 19 | 20 | echo "exercising ledctl" 21 | $PYTEST tests --ledctl-binary=src/ledctl/ledctl --slot-filters="$LEDMONTEST_SLOT_FILTER" || exit 1 22 | 23 | echo "running library unit test" 24 | ./tests/lib_unit_test || exit 1 25 | 26 | --------------------------------------------------------------------------------