pax_global_header00006660000000000000000000000064150267422630014521gustar00rootroot0000000000000052 comment=660bab6e68fded26b2117e1dcec0844549a22fed s390-tools-2.38.0/000077500000000000000000000000001502674226300134475ustar00rootroot00000000000000s390-tools-2.38.0/.checkpatch.conf000066400000000000000000000003671502674226300164770ustar00rootroot00000000000000--max-line-length=100 --strict # Not Linux therefore don't expect a Linux tree --no-tree # Ignore the following --ignore PREFER_KERNEL_TYPES --ignore EXECUTE_PERMISSIONS --ignore FILE_PATH_CHANGES --ignore NEW_TYPEDEFS --ignore SPDX_LICENSE_TAG s390-tools-2.38.0/.clang-format000066400000000000000000000074421502674226300160310ustar00rootroot00000000000000# Must be compatible with clang-format 14 --- AccessModifierOffset: "-4" AlignAfterOpenBracket: Align AlignArrayOfStructures: None AlignConsecutiveAssignments: false AlignConsecutiveBitFields: AcrossComments AlignConsecutiveMacros: AcrossComments AlignConsecutiveDeclarations: false AlignEscapedNewlines: Right AlignOperands: true AlignTrailingComments: true AllowAllArgumentsOnNextLine: false AllowAllConstructorInitializersOnNextLine: false AllowAllParametersOfDeclarationOnNextLine: false AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: None AllowShortIfStatementsOnASingleLine: Never AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: false BitFieldColonSpacing: Both BinPackArguments: true BinPackParameters: true BraceWrapping: AfterClass: false AfterControlStatement: false AfterEnum: false AfterFunction: true AfterNamespace: true AfterObjCDeclaration: false AfterStruct: false AfterUnion: false AfterExternBlock: false BeforeCatch: false BeforeElse: false IndentBraces: false SplitEmptyFunction: true SplitEmptyRecord: true SplitEmptyNamespace: true BreakBeforeBinaryOperators: None BreakBeforeBraces: Custom BreakBeforeInheritanceComma: false BreakBeforeTernaryOperators: false BreakConstructorInitializers: BeforeComma BreakConstructorInitializersBeforeComma: false BreakStringLiterals: false ColumnLimit: "100" CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: "8" ContinuationIndentWidth: "8" Cpp11BracedListStyle: false DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false FixNamespaceComments: false # git grep -h '^#define [^[:space:]]*\(for_each\|iterate\)[^[:space:]]*(' \ # | sed "s,^#define \([^[:space:]]*\(for_each\|iterate\)[^[:space:]]*\)(.*$, - '\1'," \ # | LC_ALL=C sort -u ForEachMacros: - "dfi_cpu_iterate" - "dfi_mem_chunk_iterate" - "dfo_chunk_iterate" - "for_each_rb_entry" - "ns_range_for_each" - "sd_cpu_item_enable_iterate" - "sd_cpu_item_iterate" - "sd_cpu_iterate" - "sd_cpu_type_iterate" - "sd_sys_item_enable_iterate" - "sd_sys_item_iterate" - "sd_sys_iterate" - "table_col_iterate" - "table_iterate_mark_keys" - "util_list_iterate" - "util_list_iterate_safe" - "util_opt_iterate" - "util_rec_iterate" IncludeBlocks: Preserve # Leave imports as is IncludeCategories: - Regex: .* Priority: 1 IndentCaseLabels: false IndentGotoLabels: false IndentPPDirectives: None IndentWidth: "8" IndentWrappedFunctionNames: false KeepEmptyLinesAtTheStartOfBlocks: false MacroBlockBegin: "" MacroBlockEnd: "" MaxEmptyLinesToKeep: "1" NamespaceIndentation: Inner PackConstructorInitializers: CurrentLine PenaltyBreakAssignment: "10" PenaltyBreakBeforeFirstCallParameter: "30" PenaltyBreakComment: "10" PenaltyBreakFirstLessLess: "0" PenaltyBreakString: "10" PenaltyExcessCharacter: "100" PenaltyReturnTypeOnItsOwnLine: "60" PointerAlignment: Right ReflowComments: true SortIncludes: false SortUsingDeclarations: false SpaceAfterCStyleCast: false SpaceBeforeAssignmentOperators: true SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true SpaceBeforeParens: ControlStatementsExceptForEachMacros SpaceBeforeRangeBasedForLoopColon: true SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: "1" SpacesInAngles: false SpacesInCStyleCastParentheses: false SpacesInContainerLiterals: false SpacesInLineCommentPrefix: Minimum: 1 Maximum: -1 SpacesInParentheses: false SpacesInSquareBrackets: false Standard: Auto StatementMacros: - "STATIC_ASSERT" TabWidth: "8" TypenameMacros: - "STACK_OF" UseTab: Always WhitespaceSensitiveMacros: - "STRINGIFY" s390-tools-2.38.0/.codespell.ignore000066400000000000000000000000211502674226300166750ustar00rootroot00000000000000parm parms crate s390-tools-2.38.0/.codespellrc000066400000000000000000000001101502674226300157370ustar00rootroot00000000000000[codespell] ignore-words = .codespell.ignore count = '' quiet-level = 3 s390-tools-2.38.0/.editorconfig000066400000000000000000000007501502674226300161260ustar00rootroot00000000000000# Check https://editorconfig.org for details root = true [*] end_of_line = lf insert_final_newline = true charset = utf-8 indent_style = tab tab_width = 8 trim_trailing_whitespace = true [*.rs] indent_style = space indent_size = 4 tab_width = 4 [*.sh] shell_variant = bash # used by `shfmt` [*.y{a,}ml] indent_style = space indent_size = 2 [*.py] indent_style = space indent_size = 4 [{Makefile,*.mak}] indent_style = tab [{COMMIT_EDITMSG,EDIT_DESCRIPTION}] max_line_length = 72 s390-tools-2.38.0/.gitignore000066400000000000000000000055231502674226300154440ustar00rootroot00000000000000# # Ignore compiler generated files # *.o *.a *.o.d # ctags files tags TAGS # compile_commands.json # (https://clang.llvm.org/docs/JSONCompilationDatabase.html) compile_commands.json # clangd cache (https://clangd.llvm.org/design/indexing#backgroundindex) .cache/ # Ignore coverage data *.gcda *.gcno # # Ignore generated executables and other generated files # **/.detect-openssl.dep.c *.debug ap_tools/ap-check cmsfs-fuse/cmsfs-fuse cpacfstats/cpacfstats cpacfstats/cpacfstatsd cpumf/chcpumf cpumf/lscpumf cpumf/lshwc cpumf/lspai cpumf/pai cpuplugd/cpuplugd dasdfmt/dasdfmt dasdinfo/dasdinfo dasdview/dasdview dump2tar/src/dump2tar fdasd/fdasd hmcdrvfs/hmcdrvfs hsavmcore/check-dep-fuse hsavmcore/hsavmcore hyptop/hyptop ip_watcher/xcec-bridge ipl_tools/chreipl ipl_tools/chshut ipl_tools/lsreipl ipl_tools/lsshut iucvterm/bin/chiucvallow iucvterm/bin/ts-shell iucvterm/etc/ts-shell.conf iucvterm/po/iucvterm.pot iucvterm/src/iucvconn iucvterm/src/iucvtty iucvterm/src/ttyrun iucvterm/test/test_afiucv libap/check-dep-json libap/check-dep-lock libekmfweb/check-dep-libekmfweb libekmfweb/detect-openssl-version.dep libekmfweb/libekmfweb.so libekmfweb/libekmfweb.so.1 libekmfweb/libekmfweb.so.1.0 libkmipclient/check-dep-libkmipclient libkmipclient/detect-openssl-version.dep libkmipclient/libkmipclient.so libkmipclient/libkmipclient.so.1 libkmipclient/libkmipclient.so.1.0 libseckey/check-dep-libseckey libseckey/detect-openssl-version.dep libutil/*_example libvmcp/vmcp_example libzds/libzds.a lsstp/lsstp mon_tools/mon_fsstatd mon_tools/mon_procd opticsmon/opticsmon osasnmpd/osasnmpd qetharp/qetharp qethqoat/qethqoat systemd/cpacfstatsd.service systemd/iucvtty-login@.service systemd/ttyrun-getty@.service tape390/tape390_crypt tape390/tape390_display tunedasd/src/tunedasd vmcp/vmcp vmur/vmur zconf/chp/chchp zconf/chp/lschp zconf/chp/chpstat/chpstat zconf/css/lscss zconf/qeth/lsqeth zconf/scm/lsscm zconf/zcrypt/chzcrypt zconf/zcrypt/lszcrypt zconf/zcrypt/zcryptctl zconf/zcrypt/zcryptstats zdev/src/chzdev zdev/src/chzdev_usage.c zdev/src/lszdev zdev/src/lszdev_usage.c zdev/src/zdev_id zdsfs/zdsfs zdump/.check_dep_fuse zdump/.check_dep_zgetdump zdump/.detect_openssl.dep.c zdump/zgetdump zfcpdump/10-zfcpdump.install zfcpdump/cpioinit zfcpdump/zfcpdump-initrd zfcpdump/zfcpdump_part ziomon/ziomon_mgr ziomon/ziomon_util ziomon/ziomon_zfcpdd ziomon/ziorep_traffic ziomon/ziorep_utilization zipl/boot/*.bin zipl/boot/*.exec zipl/boot/.loaders zipl/boot/data.h zipl/src/chreipl_helper.device-mapper zipl/src/chreipl_helper.md zipl/src/zipl zipl/src/zipl-editenv zipl/src/zipl_helper.device-mapper zipl/src/zipl_helper.md zkey/check-dep-zkey zkey/check-dep-zkey-cryptsetup zkey/detect-libcryptsetup.dep zkey/ekmfweb/libekmfweb.dep zkey/ekmfweb/zkey-ekmfweb.so zkey/kmip/libkmipclient.dep zkey/kmip/zkey-kmip.so zkey/zkey zkey/zkey-cryptsetup zmemtopo/zmemtopo zpcictl/zpcictl zpwr/zpwr s390-tools-2.38.0/.pre-commit-config.yaml000066400000000000000000000017531502674226300177360ustar00rootroot00000000000000--- exclude: \.(bin|crl|crt|key)$ repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.1.0 hooks: - id: check-merge-conflict - id: end-of-file-fixer - id: mixed-line-ending - id: trailing-whitespace - id: check-executables-have-shebangs - id: check-shebang-scripts-are-executable exclude_types: ['rust'] - repo: local hooks: - id: git-clang-format name: git-clang-format description: Run git-clang-format entry: git args: [clang-format, --staged, --] pass_filenames: true language: system require_serial: true minimum_pre_commit_version: "2.9.0" types_or: [c++, c] - repo: https://github.com/codespell-project/codespell rev: v2.2.1 hooks: - id: codespell exclude_types: ['rust'] - repo: https://github.com/jumanjihouse/pre-commit-hooks rev: 3.0.0 hooks: - id: shellcheck args: ["--external-sources"] s390-tools-2.38.0/.rustfmt.toml000066400000000000000000000006251502674226300161310ustar00rootroot00000000000000edition = "2021" newline_style = "Unix" # Unstable options that help catching some mistakes in formatting and that we may want to enable # when they become stable. # # They are kept here since they are useful to run from time to time. #comment_width = 100 #format_code_in_doc_comments = true #group_imports = "StdExternalCrate" #normalize_comments = true #reorder_impl_items = true #wrap_comments = true s390-tools-2.38.0/.shellcheckrc000066400000000000000000000002301502674226300160750ustar00rootroot00000000000000# Search in the current script's directory by default (since 0.7.0) source-path=SCRIPTDIR # Allow external-sources (since 0.8.0) external-sources=true s390-tools-2.38.0/AUTHORS.md000066400000000000000000000052151502674226300151210ustar00rootroot00000000000000List of all individuals having contributed content to s390-tools ---------------------------------------------------------------- - Alexander Egorenkov - Alexandra Winter - Alexey Ishchuk - Andreas Herrmann - André Wild - Antoinette Kaschner - Arnd Bergmann - Axel Wirbser - Balint Reczey - Benjamin Block - Bjoern Walk - Brian C. Lane - Carsten Otte - Christian Borntraeger - Christian Ehrhardt - Christof Schmitt - Claudio Imbrenda - Clemens von Mann - Colin Walters - Dan Horak - Dan Horák - Daniel S. Haischt - Despina Papadopoulou - Dimitri John Ledkov - Eberhard Pasch - Eduard Shishkin - Einar Lueck - Eric Farman - Eric Sandeen - Erwin Vicari - Eugene Crosser - Eugene Dvurechenski - Fabrice Fontaine - Farhan Ali - Fedor Loshakov - Felix Beck - Finn Callies - Frank Blaschka - Frank Heimes - Frank Munzert - Frank Pavlic - Fritz Elfert - Gautam Gala - Gerald Schaefer - Gerhard Tonn - Graham Inggs - Guevenc Guelce - Hannes Reinecke - Hans-Joachim Picht - Hans Wippel - Harald Freudenberger - Heiko Carstens - Hendrik Brueckner - Holger Dengler - Holger Smolinski - Hongjie Yang - Horst Hummel - Ingo Franzki - Ingo Tuchscherer - Jakob Naucke - Jakub Čajka - Jan Glauber - Jan Höppner - Jan Polensky - Jan Willeke - Jason J. Herne - Javier Martinez Canillas - Jean-Baptiste Joret - Jens Remus - Jochen Roehrig - Joern Siglen - Jörn Siglen - Juergen Christ - Julian Wiedmann - Karsten Graul - Kittipon Meesompop - Klaus-Dieter Wacker - Lakhvich Dmitriy - Marc Hartmayer - Mario Held - Mark Dettinger - Mark Post - Martin Kammerer - Martin Peschke - Martin Petermann - Martin Schwidefsky - Matthew Rosato - Maxim Shchetynin - Melissa Howland - Mete Durlu - Michael Ernst - Michael Holzheu - Michael Mueller - Mijo Safradin - Mikhail Zaslonko - Nihar Panda - Nikita Dubrovskii - Niklas Schnelle - Nikolay Gueorguiev - Peter Jin - Peter Oberparleiter - Peter Tiedemann - Philipp Kern - Philipp Rudo - Prashanth Sundararaman - Rafael Fonseca - Raimund Schroeder - Ralph Wuerthner - Rene Trumpp - Rolf Schaefer - Sa Liu - Sascha Silbe - Sebastian Ott - Sertonix - Seshagiri N. Ippili - Shalini Chellathurai Saroja - Siglen - Simon Sturm - Stefan Bader - Stefan Haberland - Stefan Raspl - Stefan Reimbold - Stefan Weinhuber - Steffen Eiden - Steffen Maier - Steffen Thoss - Sumanth Korikkar - Susanne Wintenberger - Sven Schnelle - Sven Schuetz - Swen Schillig - Taraka R. Bodireddy - Thomas Heidrich - Thomas Huth - Thomas Richter - Thomas Spatzier - Thomas Weber - Thorsten Winkler - Tobias Huschle - Tuan Hoang - Ursula Braun - Utz Bacher - Vance Morris - VasiliyS - Vasily Gorbik - Viktor Mihajlovski - Vineeth Vijayan - Volker Sameske - Wenjia Zhang - Wolfgang Taphorn - Yaakov Selkowitz s390-tools-2.38.0/CHANGELOG.md000066400000000000000000001146771502674226300153000ustar00rootroot00000000000000Release history for s390-tools (MIT version) -------------------------------------------- * __v2.38.0 (2025-06-25)__ For Linux kernel version: 6.14 / 6.15 Add new tools: - udev: New rule to set newly hotplugged CPUs online - zmemtopo: Display memory topology information - zpwr: Display power readings of a partition and CPC Removed tools / features: - check_hostkeydoc: Remove installation target - scsi_logging_level: Delete SCSI logging script (available in sg3_utils) - zdump: Drop build_arch for s390 DASD dumps - zdump: Drop non-extended multi-volume DASD dump support - zdump: Drop support of 32-bit dump architecture - zdump: Drop support of non-extended single volume DASD dumpers - zdump: Drop support of obsolete dumps and dumpers Changes of existing tools / libraries: - Various man-pages fixes - check_hostkeydoc: Add deprecation warning - check_hostkeydoc: Move to scripts directory - cpuplugd: Allow cpu hotplugging on systems without polarization - dbginfo.sh: Add Ubuntu snap tool - dbginfo.sh: Add missing config data and logs - dbginfo.sh: Reworking the container section - dbginfo.sh: Update for network commands - dbginfo.sh: Updating info for disks and lvm - libutil: Add machine type definition for machines 9175 and 9176 - lscpumf: Add support for IBM z17 counter sets - lshwc: Add command line flag for run time - lshwc: Add flags to display counter values in hex - lshwc: Add output '--format' option - lshwc: Add support for delta counter value display - lspai: Add output '--format' option - lsreipl: Add secure boot state to output - lswhc: Add short names to lshwc output - pv_tools: Add Bash and Zsh completions - pvapconfig: Add '--unbind' option - pvimg/boot: Print error messages from stage3a bootloader - pvimg: Add support for CCK update - pvsecret: Add support for CCK update - pvsecret: Allow retrieving secrets by index & warn for duplicated entries - pvsecret: Deny adding secrets with duplicated secret IDs - zdev: Add support for virtio devices - zipl: Enhance mirror support - zipl: Implement '--dry-run' option for all dump jobs - zipl_helper.device-mapper: Support mirrors over NVMe devices - zkey/dracut: Add a dracut config file for zkey - zkey/initramfs: Update initramfs hook to correct drivers and include zkey plugins - zkey: Add support for converting a clear-key LUKS2 volume to use a secure key Bug Fixes: - chpstat: Add missing CMG 5 data fields - chpstat: Fix DPU utilization calculation - libutil/util_file: Handle over-read in util_file_read_fd() - pvattest: Fix successful 'check' evaluation - pvsecret: Fix some edge cases for plaintext keys - zipl_helper.device-mapper: Fix imprecise is_device_mapper() predicate - zkey: Fix EP11 secure key reencipher function - zpcictl: Fix command line parsing for invalid options * __v2.37.0 (2025-02-07)__ For Linux kernel version: 6.13 Changes of existing tools: - dbginfo.sh: Add details on CPU-measurement - dbginfo.sh: Add new crypto command - dbginfo.sh: Add overview commands and crypto update - dbginfo.sh: Adding kdump info - dbginfo.sh: Removing outdated email references - dbginfo.sh: Rework network section - dbginfo.sh: Update copyright 2nd year - pvimg: Add '--(enable|disable)-image-encryption' flags to 'pvimg create' - pvimg: Add '--cck ' command line option and make '--comm-key' an alias - pvimg: Add '--hdr-key' command line option to 'pvimg create' - pvimg: Rename '--key' into '--hdr-key' and use '--key' as an alias (for 'pvimg info') - pvsecret: Add support for retrievable secrets - ziorep_config: Add PCHID field to adapter report - ziorep_traffic: Add DEVBUSID column to traffic report - ziorep_utilization: Add --fcp-device parameter to print virtual adapter report - ziorep_utilization: Add PCHID column to physical adapter report - ziorep_utilization: Now prints only physical adapter report by default - ziorep_utilization: Swap Bus-ID and CHPID columns in virtual adapter report - zipl/boot: Increase section size for eckd_mv dumper - zkey: Add support for listing and importing protected virtualization secrets Bug Fixes: - chpstat: Fix invalid utilization data on older kernels - opticsmon: Fix runaway loop in on_link_change() - zipl: Update inline assembly for GCC 15 - zipl_helper.device-mapper: Add missed step in logical device resolution * __v2.36.0 (2024-12-06)__ For Linux kernel version: 6.12 s390-tools: Define Rust MSRV as 1.75.0 Add new tools / libraries: - cpacfinfo: Tool to provide CPACF information - opticsmon: Tools to monitor optical modules for directly attached PCI based NICs - pvimg: Rust rewrite of genprotimg Changes of existing tools: - chpstat: Add data bandwidth utilization column - chpstat: Add support for full CMCB - chpstat: Add support for new CMG types - dbginfo.sh: add overview commands and crypto update - genprotimg: 1. genprotimg is now a symbolic link to the new tool `pvimg create` 2. Breaking API changes in genprotimg: 1. An existing output file is no longer silently overwritten; to revert to the old behavior use the `--overwrite` option. 2. The Linux kernel component is now checked to determine whether it is a binary s390x kernel. To disable this behavior use the `--no-component-check` option. - hyptop: Support for structured output (json, json-seq, csv) - lszfcp: Add missing fallback marker for non-good fc_host port_state - lszfcp: Improve speed with many SCSI devices - pvattest: Add attestation policy check command - zipl: Add support of partitions of mirror md-devices Bug Fixes: - lszcrypt: Fix wrong state showing up for removed AP queue within SE guest - lszfcp: Show device names line for zfcp_units without SCSI device * __v2.35.0 (2024-10-01)__ For Linux kernel version: 6.11 Add new tools / libraries: Changes of existing tools: - cpacfstats: Add support for FULL XTS (MSA 10) and HMAC (MSA 11) PAI counter - cpuplugd: Make cpuplugd compatible with hiperdispatch - dbginfo.sh: Add network sockstat info - pvapconfig: s390x exclusive build - zdev: Add option to select IPL device - zdump/dfo_s390: Support s390 DFO for vr-kernel dumps - zipl: Add support of mirror devices Bug Fixes: - (genprotimg|zipl)/boot: discard .note.package ELF section to save memory - netboot/mk-s390image: Fix size when argument is a symlink - ziorep_config: Fix warning message when multipath device is not there. - zipl: Fix problems when target parameters are specified by user - zipl: Fix segfault when creating device-based dumps with '--dry-run' * __v2.34.0 (2024-08-01)__ For Linux kernel version: 6.10 Changes of existing tools: - ap_tools/ap-check: Add support for vfio-ap dynamic configuration - dbginfo.sh: Update/Add additional DASD data collection - dumpconf: Add new parameter 'SCP_DATA' for SCSI/NVMe/ECKD dump devices - libutil: Make formatted meta-data configurable - s390-tools: Replace 'which' with built-in 'command -v' - zdump/dfi_elf: Support core dumps of vr-kernels Bug Fixes: - chzdev: Fix warning about failed ATTR writes by udev - rust/pv: Try again if first CRL-URI is invalid - rust/pvattest: Add short option for --arpk - zdump: Fix 'zgetdump -i' ioctl error on s390 formatted dump file * __v2.33.1 (2024-05-28)__ For Linux kernel version: 6.9 Bug Fixes: - s390-tools: Fix formatting and typos in README.md - s390-tools: Fix release string * __v2.33.0 (2024-05-27)__ For Linux kernel version: 6.9 Add new tools / libraries: - chpstat: New tool for displaying channel path statistics - libutil: Add output format helpers(util_fmt: JSON, JSON-SEQ, CSV, text pairs) Changes of existing tools / libraries: - chzdev: Add --is-owner to identify files created by zdev - dasdfmt: Change default mode to always use full-format (Note: affects ESE DASD) - libap: Significantly reduce delay time between file lock retries - pvattest: Rewrite from C to Rust - pvattest: Support additional data & user-data - rust/pv: Support for Attestation Bug Fixes: - chreipl: Improve disk type detection when running under QEMU - dbginfo.sh: Use POSIX option with uname - s390-tools: Fix missing hyphen escapes in the man page for many tools - zipl/src: Fix bugs in disk_get_info() reproducible in corner cases * __v2.32.0 (2024-04-03)__ For Linux kernel version: 6.8 Changes of existing tools: - cpumf/lscpumf: add support for machine type 3932 - genprotimg, pvattest, and pvsecret accept IBM signing key with Armonk as subject locality - zdump/zipl: Support for List-Directed dump from ECKD DASD - zkey: Detect FIPS mode and generate PBKDF for luksFormat according to it Bug Fixes: - dbginfo.sh: dash compatible copy sequence - rust/pv_core: Fix UvDeviceInfo::get() method - zipl/src: Fix leak of files if run with a broken configuration - zkey: Fix convert command to accept only keys of type CCA-AESDATA * __v2.31.0 (2024-02-02)__ For Linux kernel version: 6.7 General: - common.mak: Set default C/C++ standard to gnu11/gnu++11 Add new tools / libraries: - pvapconfig: Tool to automatically configure APQNs in SE KVM guests - s390-tools: Provide pre-commit configuration Changes of existing tools: - cpuplugd: Adjust to CPU 0 being no longer hotpluggable - dbginfo.sh: Check for Dynamic Partition Mode - dbginfo.sh: Update man page and copyright - rust/pv: Add user-data signing and verifying - rust/pvsecret: Add user defined signatures and verifications - zdev/dracut: Consolidate device configuration Bug Fixes: - dbginfo.sh: Fix relative path on script copy - libkmipclient: Fix build with libxml2-2.12.0 - pvsecret: Fix panic if empty file is used as host key document - rust/pv: Fix 'elided_lifetimes_in_associated_constant' warning * __v2.30.0 (2023-12-01)__ For Linux kernel version: 6.6 Add new tools / libraries: - lspai: Tool to display PAI counter sets - s390-tools: Provide a ShellCheck configuration Changes of existing tools / libraries: - cpumf/pai: Add command line option for realtime scheduling - dbginfo.sh: enhance ethtool collection for ROCE - libutil/util_lockfile: add routine to return owning pid of file lock - lszcrypt: Improve lszcrypt output on SE guests - rust: Use a single workspace for all rust tools - zdev: limit the derivation of ZDEV_SITE_ID - zdump/df_s390: Update 'zgetdump -i' output with zlib info - zdump/dfi_s390: Support reading compressed s390_ext dumps - zipl/boot: Integrate zlib compression to single volume DASD dumper - zipl/boot: compile the bootloaders only if HOST_ARCH is s390x - zipl: Add --no-compress option to zipl command - zkey: Also check for deconfigured and check-stopped cards - dbginfo.sh: fix relative path on script copy Bug Fixes: - ap_tools/ap-check: handle get-attributes between pre and post event - libutil: fix util_file_read_*() using wrong format specifiers - rust/pv: fix Invalid write of size 1 * __v2.29.0 (2023-08-04)__ For Linux kernel version: 6.5 General: - s390-tools now supports tools written in Rust. - Add `compdb` Makefile target to create 'compile_commands.json' to LSP backends in IDEs and editors Add new tools / libraries: - rust/pv: Library for pv tools written in rust - rust/pvsecret: Tool to manage UV-secrets Changes of existing tools: - dbginfo.sh: Global IFS variable - genprotimg: Add support for add-secret requests - genprotimg: Build debuginfo files for bootloader - hyptop: Add real SMT utilization field - hyptop: Allow users to set speedup factor - pvattest: Add yaml-output for verify command - zipl: Build debuginfo files for bootloader Bug Fixes: - dump2tar: Fix truncated paths - zdev/dracut: fix kdump build to integrate with site support * __v2.28.0 (2023-07-11)__ For Linux kernel version: 6.4 Changes of existing tools: - chzcrypt: Support for SE AP pass-through support - genprotimg: Add support for non-s390x architectures - lszcrypt: Support for SE AP pass-through support - zdev: Add support for autoquiesce related sysfs attributes Bug Fixes: - ap_tools/ap-check: Handle missing 'matrix' and 'control_domains' attrs - ap_tools/ap-check: Hold ap config file lock over get attributes - s390-tools: Fix build for ppc64le - zdev: Add missing label in the udev-rules - zdev: Add proper value input for the ZDEV_SITE_ID key - zdev: Use rename-file to avoid any symlinks created - zipl/dump: fix ngdump dracut helper script * __v2.27.0 (2023-05-30)__ For Linux kernel version: 6.3 Changes of existing tools: - s390-tools cross-compile and non-s390x support: - `pkg-config` is now mandatory for the build process - Add `PKG_CONFIG` Makefile variable to select pkg-config program; default `pkg-config` or `$(CROSS_COMPILE)pkg-config` if `CROSS_COMPILE` is set - Rename Makefile variable `ARCH` to `HOST_ARCH`. `HOST_ARCH` is the architecture that will run the produced (executable) objects - Add the Makefile variable `BUILD_ARCH`. `BUILD_ARCH` is the architecture of the build system. For each Makefile variable like `CC`, `LINK`, `CPP`, ... there is a suffixed version of it - e.g. `CC_FOR_BUILD`. This is useful for cross compiling, and this naming convention is very similar to the Meson convention (see https://mesonbuild.com/Reference-tables.html#environment-variables-per-machine). - Limit build targets for non-s390x architectures (pvattest) - dasdfmt: Fall back to full format if space release fails - dbginfo.sh: Add nstat for network and SNMP stats - dbginfo.sh: Rework crypto data collection - hyptop: Show thread util by default - zipl: Add support for list-directed IPL dump from ECKD DASD Bug Fixes: - lszcrypt: Fix argument parsing - zdev/dracut: Fix out-of-memory (OOM) situations in the kdump crashkernel environment - ziomon/ziorep_config: Fix for SCSI devices of type disk without block dev - pvextract-hdr: Fix parsing issues on little-endian systems * __v2.26.0 (2023-02-14)__ For Linux kernel version: 6.2 Remove tools / libraries: - Remove vmconvert and libvmdump in favor of vmdump file support in zdump Changes of existing tools: - ipl_tools: Add support for list-directed IPL from ECKD DASD - lszcrypt: Display hardware filtering support capability - vmur: Remove option -c for dump file conversion (See zdump changes) - zdev: Add zfcp ber_stop parameter handling - zdump: Add vmdump dfi for vmdump format to elf format - zkey: Support EP11 host library version Bug Fixes: - zipl: Move dump parmline processing and verification - zipl/genprotimg: Various build improvements * __v2.25.0 (2022-12-08)__ For Linux kernel version: 6.1 Changes of existing tools: - ap_tools: Use new mdevctl installation location - lsdasd/tunedasd/zdev: Add support to handle copy pair relations presented by the DASD driver - zdev: Add --shell command line switch to generate output suitable for shell environments - zipl: Add List-Directed IPL from ECKD DASD to support secure boot Bug Fixes: - ipl_tools: Fix chreipl node for NVMes with CONFIG_NVME_MULTIPATH - libdasd: Fix bug that prevented positive ioctl return codes * __v2.24.0 (2022-11-09)__ For Linux kernel version: 6.0 Add new tools / libraries: - Provide config files for checkpatch, codespell, and clang-format Changes of existing tools: - dbginfo.sh: Collect log from various distro tools (YaST, DNF, Anaconda) - dbginfo.sh: add Kubernetes data collection - libutil: Introduce util_lockfile - zdev: Add site-aware device configuration - zdump: Add support to read Protected Virtualization dumps - zipl/boot: Add secure boot trailer Bug Fixes: - ap_tools/ap-check: Reject start for control domains without usage - cpumf/lshwc: Fix incremented counter output - cpumf/pai: Fix core dump when summary flag set - dbginfo.sh: Ensure compatibility with /bin/dash shell - dbginfo.sh: Save dbginfo.sh version to dbginfo.log - zipl/src/zipl_helper.device-mapper: Fix bug in error path * __v2.23.0 (2022-08-18)__ For Linux kernel version: 5.19 Changes of existing tools: - Makefile: use common Make definition for DRACUTDIR - Makefile: use common Make definition for UDEVDIR and UDEVRULESDIR - cpacfstats: Add PAI and hotplug support - cpumf/pai: Omit file write progress information - dbginfo.sh: Get more details on lspci command - dumpconf: Prevent running the service in containers - libcpumf: Detect PMU named pai_ext - pvattest: Improve error reporting and logging - zdev: Add some --type ap examples to manpages - zkey: Use default benchmarked Argon2i with LUKS2 Bug Fixes: - dbginfo.sh: Fix accidental ftrace buffer shrinkage/free - genprotimg: Fix BIO_reset() returncode handling - libpv: Fix dependency checking - pvattest: Fix dependency checking - zipl: Fix segmentation fault when no parmline is provided * __v2.22.0 (2022-06-20)__ For Linux kernel version: 5.18 Add new tools / libraries: - ap_tools: Introduce ap_tools and the ap-check tool - cpumf/pai: Add Processor Activity Instrumentation tool - libpv: New library for PV tools - pvattest: Add new tool to create, perform, and verify attestation measurements - zipl/zdump: Add Next Gen Dump (NGDump) support Changes of existing tools: - Move man pages to System commands section (lscpumf, lshwc, pai, dbginfo.sh, zfcpdbf, zipl-switch-to-blscfg) - README.md: Add 70-chreipl-fcp-mpath.rules to the list of udev rule descriptions - Remove SysV related daemon scripts (cpacfstatsd, cpuplugd, mon_statd) - genprotimg: Move man page to section 1 for user commands - hyptop: increase initial update interval - libseckey: Adapt keymgmt_match() implementation to OpenSSL - libutil: Add util_exit_code - libutil: Introduce util_udev - zdev: Introduce the ap device type - zipl-editenv: Add zIPL multienvironment support - zipl: Implement sorting BLS entries by versions - zkey: Add initramfs hook Bug Fixes: - cmsfs-fuse: Fix enabling of hard_remove option - s390-tools: Fix typos that were detected by lintian as 'typo-in-manual-page' - zkey-kmip: Fix possible use after free - zkey: Fix EP11 host library version checking - zkey_kmip: Setup ext-lib once the APQNs have been configured * __v2.21.0 (2022-04-20)__ For Linux kernel version: 5.17 Add new tools / libraries: - libcpumf: Create library libcpumf for CPU Measurement functions Changes of existing tools: - chreipl-fcp-mpath: bundle a pre-cooked version of the manpage for build environments without access to `pandoc` - dbginfo.sh: Add multipath info to map paths to FC addressing and prio group - dbginfo.sh: Collect config files of systemd-modules-load.service - dbginfo.sh: Sort list of environment variables for readability - dbginfo.sh: Replace "which" by builtin command "type" - dbginfo.sh: Rework script formatting (indents, order) - dbginfo.sh: Update sysfs collection (excludes, messages) - genprotimg: Add Protected Virtualization (PV) dump support - genprotimg: Remove DigiCert root CA pinning - lszcrypt: Add CEX8S support - zcryptctl: Add control domain handling - zcryptstats: Add CEX8 support - zipl: Allow optional entries that are left out when files are missing - zipl: make IPL sections defined with BLS to inherit a target field - zpcictl: Add option to trigger firmware reset Bug Fixes: - cpictl: Handle excessive kernel version numbers - dbginfo.sh: Collect all places where modprobe.d config files could exist - fdasd: Fix endless menu loop on EOF - zdump/dfi: Fix segfault due to double free - zdump: Fix /dev/mem reading - zpcictl: Fix race of SCLP reset and Linux recovery * __v2.20.0 (2022-02-04)__ For Linux kernel version: 5.16 Add new tools / libraries: - Add EditorConfig configuration Changes of existing tools: - s390-tools switches to Fuse 3 as Fuse 2 is deprecated. Affected tools: cmsfs, hmcdrvfs, hsavmcore, zdsfs, zdump - chreipl-fcp-mpath: don't compress the manpage before installing it - cpictl: Report extended version information - genprotimg: Add extended kernel command line support - zdev: modify the lsblk output parser in lszdev - zipl: Add support for longer kernel command lines (now supports up to 64k length) Bug Fixes: - cpictl: Suppress messages for unwritable sysfs files - dbginfo.sh: Fix missing syslog for step create_package - lshwc: Fix CPU list parameter setup for device driver - zdev: Check for errors when removing a devtype setting - zdev: Fix path resolution for multi-mount point file systems * __v2.19.0 (2021-11-10)__ For Linux kernel version: 5.15 Add new tools / libraries: - chreipl-fcp-mpath: New toolset that uses multipath information to change the configured FCP re-IPL path on detecting issues with the current path Changes of existing tools: - dbginfo.sh: Add retry timeout and remove possible blocking "blockdev --report" - dbginfo.sh: Collect config- and debug-data for chreipl-fcp-mpath - hsci: Add support for multiple MAC addresses Bug Fixes: - lshwc: Fix compile error for gcc <8.1 - zdump: Various clean-ups and fixes - ziomon: Correct throughput calculation in ziorep_printers - zipl: Fix segmentation fault when setting stage3_parms * __v2.18.0 (2021-10-01)__ For Linux kernel version: 5.14 Add new tools: - scripts: Add tool for parsing sclp s390dbf logs - zdev: Add udev rule helper tool - zipl-editenv: Add tool to operate with zIPL environment installed in the boot record Changes of existing tools: - Makefile: Fix order of build of libraries for parallel builds - dbginfo.sh: Add collection in area of timedate, coredump and --check option - dbginfo.sh: Add exception on dump2tar for /sys/kernel/mm/page_idle/bitmap - dbginfo.sh: Cleanup of outdated sections and general code rework - dbginfo.sh: Collect zipl boot menu entries from boot loader specification - lszcrypt: Add support for vfio-ap status field - lszcrypt: Improved output for deconfig cards and queues - lszfcp: Add linkdown case to host marker of extended output - zdev: Add auto-config for PCI and crypto devices - zdump: Introduce multi-level message logging - zipl: Add support for environment block interpretation - zkey-cryptsetup: Support LUKS2 volumes with integrity support enabled Bug Fixes: - hsavmcore: Avoid recompilation of overlay during install step - libkmipclient: Fix parsing of hex values for XML and JSON encoding - vmur/vmur.cpp: Fix error handling on transfer failure - zdump: Lots of smaller fixes across the board * __v2.17.0 (2021-07-07)__ For Linux kernel version: 5.12 / 5.13 Add new tools / libraries: - hsavmcore: New utility to make the dump process with kdump more efficient - libkmipclient: Add KMIP client shared library - libseckey: Add a secure key library - lshwc: New tool to extract and list complete counter sets Changes of existing tools: - genprotimg: Add '--(enable|disable)-pckmo' options - genprotimg: Add OpenSSL 3.0 support - genprotimg: Change plaintext control flags defaults so PCKMO functions are allowed - libutil: Introduce multi-level message logging (util_log) - libutil: Introduce util_arch module - udev/dasd: Change DASD udev-rule to set none scheduler - zdsfs: Add transparent codepage conversion - zkey: Add support for KMIP-based key management systems Bug Fixes: - ttyrun-getty: Avoid conflicts with serial-getty@ - dbginfo: add /proc/kallsyms - refresh zVM, lscpu - fix WORKARCHIVE handling - dbginfo: add KVM data collection for server and guest - fix lszdev - genprotimg: Add missing return values in error paths - zkey: Fix conversion of CCA DATA keys to CCA CIPHER keys - znetconf: avoid conflict with "chzdev -e" * __v2.16.0 (2021-02-19)__ For Linux kernel version: 5.10 / 5.11 Add new tool: - hsci: New tool to manage HSCI (HiperSockets Converged Interfaces) Changes of existing tools: - genprotimg: Add host-key document verification support - genprotimg: boot: Make boot loader -march=z900 compatible - libekmfweb: Make install directory for shared libraries configurable - lsdasd: Add FC Endpoint Security information - make: Add address sanitizer support - netboot: Add version information to scripts - netboot: Bump busybox version in pxelinux.0 build - zdev: Add FC Endpoint Security information for DASD devices - zdev: Add build option to update initial RAM-disk by default - zkey-ekmfweb: Avoid sequence number clash when generating keys - zkey/zkey-ekmfweb: Install KMS plugins into configurable location - zkey: Add support to store LUKS2 dummy passphrase in key repository Bug Fixes: - dasdfmt: Fix segfault when an incorrect option is specified - genprotimg: Fix several build issues - genprotimg: Require argument for 'ramdisk' and 'parmfile' options - zcryptstats: Fix handling of partial results with many domains - zfcpdbf: Deal with crash 7.2.9 change in caller name formatting - zipl/boot: Fix memory use after free in stage2 - zipl/boot: Fix potential heap overflow in stage2 - zipl: Fix reading 4k disk's geometry * __v2.15.1 (2020-10-28)__ For Linux kernel version: 5.9 Changes of existing tools: - lsstp: Improve wording and fix typos in man page - zkey: Ensure zkey and friends are skipped with HAVE_OPENSSL=0 - zkey: Add library versioning for libekmfweb and zkey-ekmfweb - libutil: Add function to determine base device of a partition block device Bug Fixes: - dasdfmt: Fix bad file descriptor error when running on symlinks - libdasd: Fix dasd_get_host_access_count() - zipl: Fix multivolume dump - zgetdump: Fix device node determination via sysfs to work with multivolume again - genprotimg/boot: Fix build by disabling SSP - zipl/boot: Fix build by disabling SSP * __v2.15.0 (2020-10-15)__ For Linux kernel version: 5.9 Add new tool: - lsstp: A small utility to display the Server Time Protocol (STP) information present in sysfs Changes of existing tools: - dumpconf: support NVMe dump/reipl device - ipl_tools: support clear attribute for nvme re-IPL - zcrypt: Support new config state with lszcrypt and chzcrypt - zkey: Add support for key management system plugins including the KMS commands: bind, unbind, info, configure, rencipher, list, import, refresh - zkey: Add EKMFWeb support to remotely generate secure keys - libekmfweb: Add new EKMFWeb client library - libutil: Add util_file_read_va() - libutil: Add util_file_read_i()/util_file_read_ui() Bug Fixes: - cpumf: Fix version and help printout when CPUMF is not installed - ziomon/ziorep_printers: fix virtual adapter CSV output - zipl: Fix Error when title is not the first field in BLS file * __v2.14.0 (2020-08-21)__ For Linux kernel version: 5.7 / 5.8 Changes of existing tools: - cpacfstats: Add ECC counters - dbginfo: Added collection of /proc/softirqs - ipl-tools: Add nvme device support to lsreipl/chreipl - zdsfs: Add coordinated read access - libzds: Add curl interface to access zosmf rest api - util_opt: Change util_opt_init() to honor current command, if set Bug Fixes: - lsluns: Try harder to find udevadm - mon_tools: Update udevadm location - zipl: Fix NVMe partition and base device detection - zipl/stage3: Correctly handle diag308 response code - znetconf: Introduce better ways to locate udevadm * __v2.13.0 (2020-05-06)__ For Linux kernel version: 5.5 / 5.6 Add new tool: - genprotimg: Add genprotimg to create protected virtualization images - genprotimg: Add sample script to verify host keys Changes of existing tools: - dbginfo: Gather bridge related data (using 'bridge') - dbginfo: Removed collection of /var/log/opencryptoki/ - dbginfo: collect softnet_stat - dbginfo: gather ethtool output for per-queue coalescing - ipl_tools: Support clear attribute for FCP and CCW re-IPL - zdev: Report FC Endpoint Security of zfcp devices - zdev/dracut/95zdev/module-setup.sh: Add ctcm kernel module - cpumf/data: Add new deflate counters for IBM z15 - zkey: Add support for EP11 secure keys - zpcictl: Initiate recover after reset - zipl: Add support for NVMe devices - zipl: A multitude of code and stability improvements Bug Fixes: - zipl: Prevent endless loop during IPL - zipl/libc: Fix potential buffer overflow in printf - zkey: Fix listing of keys on file systems reporting DT_UNKNOWN - zkey: Fix display of clear key size for XTS, CCA-AESCIPHER, and EP11-AES XTS keys * __v2.12.0 (2019-12-17)__ For Linux kernel version: 5.4 Changes of existing tools: - dbginfo: Gather qdisc related data (using 'tc') - dbginfo: Gather extended network statistics (using 'ip link') - dbginfo: Collect all files under /usr/lib/systemd/system/ - cpumf/cpumf_helper: Add IBM z15 machine name - zkey: Display MKVP when validating a secure key - zkey: Cross check APQNs when generating, validating, or importing secure keys, and when changing APQN associations - zkey: Check crypto card level during APQN cross checking - zkey: Add support for generating, validating, and re-enciphering AES CIPHER keys - zkey-cryptsetup: Add --to-new and --from-old options - zkey-cryptsetup: Allow setkey to set different key types - lszcrypt/chzcrypt: CEX7S exploitation support - zcryptstats: Add support for CEX7 crypto card - zipl: Ship a minimal zipl.conf - zipl: Add value of target= as search path for BLS case Bug Fixes: - dasdview: Fix exit status in error cases - zipl: Fix various compile warnings - zipl: Fix dependency generation in zipl/boot - zipl: Fix entry point for stand-alone kdump - zipl: Add missing options to help output * __v2.11.0 (2019-09-06)__ For Linux kernel version: 5.3 Changes of existing tools: - dasdfmt: Add support for thin-provisioned volumes - lsdasd: Add support for thin-provisioned volumes - libdasd: Provide function to utilise release space ioctl - libdasd: Provide function to read ese sysfs attribute - dbginfo: Add lspci (PCI devices) and smc_dbg (SMC sockets) - dbginfo: Gather ethtool related data Bug Fixes: - zipl: Fix freeing of uninitialized pointer - zipl: Set correct secure IPL default value * __v2.10.0 (2019-07-31)__ For Linux kernel version: 5.2 Changes of existing tools: - zdev: Add zfcp dix parameter handling - cpumf: Add support for CPU-Measurement Facility counters SVN 6 Bug Fixes: - libutil: Add functions to test path is read/write-only - zdev: Fix reporting of read-only sysfs attributes - zdev: Improve handling of invalid udev rules - zipl: Fix stfle zero padding - zipl: Fix build issues - zipl: Remove trailing spaces from the fields defined in BLS files - zipl: Do not overwrite BOOT_IMAGE entry - zkey: Fix auto-detection of clear key bitsize for XTS keys * __v2.9.0 (2019-05-21)__ For Linux kernel version: 5.0 / 5.1 Add new tool: - zcryptstats: Add zcryptstats to display usage statistics of IBM Crypto Express adapters Changes of existing tools: - lszfcp: New command line option to show module parameters - lszfcp: Sdev attributes for scsi_disk, block, integrity, queue, iosched - lszfcp: Add new output marker for non-good SCSI devices (luns) - lszfcp: Add new output marker for non-good fc_rports - lszfcp: Clean up whitespace (mixed indentation, trailing) - lschp: Add support for specifying a CHPID - zipl: Add secure boot capabilities - zkey: Add common passphrase options for cryptsetup and crypttab - zkey: Add batch-mode option to cryptsetup and zkey-cryptsetup - libu2s: Remove the entire library and provide more robust functionality in libdasd and libutil instead Bug Fixes: - lszfcp: Allow to show zfcp_units without associated SCSI device - lszfcp: Attribute details for: css, zfcp_port, zfcp_unit - lszfcp: Allow to also enumerate FCP device that have never been online - lszfcp: Fix error message if no zfcp-attached SCSI device found - lszfcp: Fix to show defunct FCP devices again - lszfcp: Fix to show non-good target ports again - lszfcp: Fix missing block & sg device output without CONFIG_SYSFS_DEPRECATED - lszfcp: New command line option for extended output format - zfcpdbf: Warn about ambiguous payload records with dup reqid & payarea - zpcictl: Check for regular directory to prevent possible buffer overflow * __v2.8.0 (2019-02-15)__ For Linux kernel version: 4.20 Changes of existing tools: - Switch to using /run directory instead of the legacy /var/run - zkey: Add --pbkdf pbkdf2 to generated cryptsetup luksFormat command - zdsfs: Add online VTOC refresh - pkey: Support autoloading kernel pkey module Bug Fixes: - zkey: Avoid EPERM on key change if user is not owner of key file - cpumf/cpumf_helper: Always return list reference for --sfb-size * __v2.7.1 (2018-12-13)__ For Linux kernel version: 4.19 Changes of existing tools: - zkey: Enhance file read/write error handling - cmsfs-fuse: Write more than a single line in linefeed mode - zpcictl: Add warning for unsupported operations - zipl: Use the BLS "title" field as the IPL section name Bug Fixes: - cmsfs-fuse: Fix iconv buffer aliasing - cmsfs-fuse: Fix memory leak in cmsfs_rename() - fdasd: Fix possible integer overflow - fdasd: Fix resource leak in fdasd_parse_conffile() - zdev: Fix memory leak in misc_readlink() - dasdinfo: Display error messages on stderr - zkey: Include /sbin into PATH when executing commands - Makefile: Fix parallel build - GCC8 warning fixes across the board for: cmsfs-fuse, dasdinfo, dasdview, dump2tar, fdasd, hmcdrvfs, hyptop, ip_watcher, libvmdump, libvtoc, lsqeth, lszcrypt, qethqoat, zdev, zdsfs, zgetdump, zipl, zpcictl * __v2.7.0 (2018-10-31)__ For Linux kernel version: 4.19 Add new tool: - zcryptctl: Add zcryptctl for multiple zcrypt node management - zpcictl: Add zpcictl for reporting defective PCI devices Changes of existing tools: - qethqoat: Add OSA-Express7S support - lszcrypt: Add support for alternative zcrypt device drivers - zfcpdump: Add install script for zfcpdump - zipl: Make zipl work with XFS by using the FIEMAP mapping ioctl Bug Fixes: - lstape: Fix output with SCSI lin_tape and multiple paths to same unit - lstape: Fix output without SCSI generic (sg) - lsluns: Fix to prevent error messages if there are non-zfcp SCSI devices - lstape: Fix to prevent error messages if there are non-zfcp SCSI devices - lstape: Fix description of --type and filter for channel tapes - lstape: Fix SCSI output description in man page - lstape: Fix SCSI HBA CCW device bus-ID e.g. for virtio-scsi-ccw - Direct --help and --version output to stdout for several tools - osasnmpd: Start without real OSA devices * __v2.6.0 (2018-08-10)__ For Linux kernel version: 4.18 Add new tool: - zkey: Add zkey-cryptsetup tool Changes of existing tools: - netboot: add BOOTIF support Bug Fixes: - mon_procd: fix parsing of /proc//stat - netboot: Include compressed kernel modules in initramfs - netboot: Send client architecture and handle path prefix * __v2.5.0 (2018-06-08)__ For Linux kernel version: 4.17 Changes of existing tools: - zdev: Add support for reading firmware configuration files - zipl: Add BootLoaderSpec support - scripts: Add script to switch zipl config to a BootLoaderSpec setup Bug Fixes: - lsluns: Print a message if no adapter or port exists * __v2.4.0 (2018-05-07)__ For Linux kernel version: 4.16 Changes of existing tools: - dbginfo: Gather nvme related data - zipl: Rewrite helper script in C - libutil: Add function util_strstrip - libvmcp: Introduce libvmcp - zipl: Extend DASD stand-alone dumpers to drop zero pages - zgetdump: Add verbose option - zgetdump: Add 'Dump file size' field for zgetdump -i output - cpumf: Add IBM z14 ZR1 to the CPU Measurement Facility model list - zkey: Add build dependency to OpenSSL (libcrypto) - zkey: Add keystore implementation Bug Fixes: - hmcdrvfs: fix parsing of link count >= 1000 - zgetdump: Avoid Segfault on processing dumps with memory limit - chreipl: correct fcp reipl sysfs write sequence - udev: Replace WAIT_FOR with TEST keyword * __v2.3.0 (2018-01-30)__ For Linux kernel version: 4.15 Changes of existing tools: - lscpumf: Add support for IBM z14 hardware counters - libdasd: Introduce libdasd and use for dasd tools - zipl: Always build and link without PIE Bug Fixes: - zgetdump: Fix handling of DASD multi-volume dump for partitions above 4 GB - zdev: Fix zdev dracut module aborting on unknown root device * __v2.2.0 (2017-12-07)__ For Linux kernel version: 4.14 Removed tools: - lsmem/chmem: Moved to util-linux >= 2.30 Changes of existing tools: - lszcrypt: Add CEX6S support - cpuplugd/mon_tools: Improve systemctl start error handling - systemd: Install also the unit configuration files Bug Fixes: - build process: Fix parallel build for libutil - cpi: Add missing Install section to service unit - lsluns: Do not scan (all) if filters match nothing - lsluns: Enhance usage statement and man page - zdev: Use correct path to vmcp binary - ziomon: Re-add missing line in ziomon_fcpconf - ziomon: Fix non-zero return code in ziomon_util - zipl: Remove invalid dasdview command line option * __v2.1.0 (2017-09-25)__ For Linux kernel version: 4.13 Added new tools: - netboot: Scripts for building a PXE-style netboot image for KVM - 90-cpi.rules/cpictl: New udev rule to update CPI when KVM is used Changes of existing tools: - lsqeth/zdev: Add VNIC Characteristics support Bug Fixes: - chzcrypt: Corrected handling of insufficient permissions - cpacfstats: Add size setting to perf event - fdasd: Skip partition check with the force option - ttyrun: Fix deprecated BindTo usage in ttyrun-getty@.service.in - lszcrypt: Fix core dump caused by stack overwrite - lszcrypt: Fix random domain printout when no config available - zdev: Fix segfault with unknown qeth attribute - zdev: Fix IPv6 NDP proxy description - zdev: Fix zdev dracut module temp file location - zkey: Correctly detect abbreviated commands - zkey: Validate XTS key: ignore domain and card - zkey: Use octal values instead of S_IRWX* constants - zkey: Properly set umask to prohibit permissions to group and others - zkey: Add -ldl to LDLIBS (not LDFLAGS) - znetconf: Re-add missing line in lsznet.raw - Fix several gcc 7 warnings * __v2.0.0 (2017-08-21)__ - Publish package under the MIT license with the same contents as the already available s390-tools-1.39.0 Previous releases of s390-tools can be found on the IBM Developer Works web pages: - https://www.ibm.com/developerworks/linux/linux390/s390-tools.html s390-tools-2.38.0/CODINGSTYLE.md000066400000000000000000000072121502674226300155570ustar00rootroot00000000000000Coding guidelines for s390-tools ================================ For s390-tools the preferred language is C. We provide libraries, e.g. [`libutil`](libutil) that should be used by all tools if possible. The coding style is based on the Linux [kernel guidelines]. Therefore, use the [checkpatch] tool for verification before you submit a patch. s390-tools provides a CheckPatch configuration for this - see [`.checkpatch.conf`](.checkpatch.conf). [kernel guidelines]: https://www.kernel.org/doc/html/latest/process/coding-style.html [checkpatch]: https://github.com/torvalds/linux/blob/master/scripts/checkpatch.pl Below we describe some additional things that we want you to consider when writing new code for s390-tools. This package started in 2001 and has a long "tradition" - therefore, older tools might not follow all recommendations. Note that when changing existing code, consistency could have priority over applying rules. Automatic Code Formatting ------------------------- > **NOTE:** clang-format is a helpful tool but please don't use it blindly! s390-tools provides a ClangFormat (https://clang.llvm.org/docs/ClangFormat.html) configuration file - see [`.clang-format`](.clang-format). It can be used to format your C/C++ code automatically. Clang-format can format a single file or multiple files at once. For example, to format `main.c` in place, run the following command in a terminal: ```bash clang-format -i main.c ``` In order to format only your current staged changes use the clang-format git plugin: ```bash git clang-format --staged ``` See also `git clang-format -h`. Automatic Editor Configuration ------------------------------ s390-tools provides a EditorConfig (https://editorconfig.org/) configuration file - see [`.editorconfig`](.editorconfig). EditorConfig defines coding style rules (such as indentation size, use of tabs, etc.) and is supported by most common editors natively or via plugins. Standard abbreviations ---------------------- The abbreviations below are recommended to be used in the source code. | __Short Name__ | __Long Name__ | |:----------------|:--------------------------------------------------| | attr | Attribute | | blk | Block | | buf | Buffer | | col | Column | | count | Count | | desc | Description | | dir | Directory | | fd | File descriptor (open) | | fp | File pointer (fopen) | | len | Length | | lib | Library | | mod | Module | | nr | Number | | parm | Parameter | | path | File path | | ptr | Pointer | | rc | Return code | | size | Size | | src | Source | | str | String | | sym | Symbol | s390-tools-2.38.0/CONTRIBUTING.md000066400000000000000000000204761502674226300157110ustar00rootroot00000000000000Contributing to s390-tools ========================== License ------- All contributions have to be submitted under the MIT license. See also the [LICENSE](LICENSE) file. Developer's Certificate of Origin and Signed-off-by --------------------------------------------------- The sign-off is a simple line at the end of the explanation for the patch, which certifies that you wrote it or otherwise have the right to pass it on as an open-source patch. With the Signed-off-by line you certify the below: ``` Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. ``` If you can certify the above, just add a line stating the following at the bottom of each of your commit messages: ``` Signed-off-by: Random Developer ``` Please use your real name and a valid e-mail address (no pseudonyms or anonymous contributions). Submitting code --------------- The preferred way is to create GitHub pull requests for your code contributions. Please create separate pull requests for each logical enhancement, new feature, or fix. Before you submit your code please consider our recommendations in the [CODINGSTYLE](CODINGSTYLE.md) document. GitHub workflow for contributions --------------------------------- In the examples below we use this fictive identity: - Name: Random Developer - E-mail: random@developer.example.org - GitHub ID: random-developer ### Setup GitHub and local git 1. Create a fork of this repository by clicking the `Fork` button on the top right of the [s390-tools](https://github.com/ibm-s390-linux/s390-tools) main page 2. Clone your forked repository to your local development system ``` $ git clone https://github.com/random-developer/s390-tools.git ``` 3. Configure a remote called "upstream" pointing to the official s390-tools repository on GitHub ``` $ cd s390-tools ~/s390-tools $ git remote add upstream https://github.com/ibm-s390-linux/s390-tools.git ``` 4. Verify your remotes ``` ~/s390-tools $ git remote -v origin https://github.com/random-developer/s390-tools.git (fetch) origin https://github.com/random-developer/s390-tools.git (push) upstream https://github.com/ibm-s390-linux/s390-tools.git (fetch) upstream https://github.com/ibm-s390-linux/s390-tools.git (push) ``` You now have two remotes: The "origin" remote points to your fork and the "upstream" remote to the official s390-tools repository. 5. Configure your git user name and e-mail ``` ~/s390-tools $ git config user.name "Random Developer" ~/s390-tools $ git config user.email "random@developer.example.com" ``` ### Create a pull request 1. Create and checkout a new branch for your contribution ``` ~/s390-tools $ git checkout -b contrib-doc-pr ``` 2. Make your changes to the code ``` ~/s390-tools $ vim CONTRIBUTING.md ``` 3. Build and test your contribution ``` ~/s390-tools $ make clean all ~/s390-tools $ # Whatever you have to do for testing ``` 4. Commit your changes ``` ~/s390-tools $ git add CONTRIBUTING.md ~/s390-tools $ git commit -s ``` Provide a meaningful commit message including your "Signed-off-by" line to each commit: ``` CONTRIBUTING: Outline steps to submit code Explain in more detail how to submit s390-tools contributions as GitHub pull requests. Signed-off-by: Random Developer ``` 5. Use the [checkpatch] tool to validate your commits ``` ~/s390-tools $ checkpatch.pl --no-tree --git master..HEAD ``` Interpret the checkpatch messages wisely - e.g. the 80 character rule can be ignored for printf format strings. [checkpatch]: https://github.com/torvalds/linux/blob/master/scripts/checkpatch.pl 6. Push the changes to your fork of the repository ``` ~/s390-tools $ git push origin contrib-doc-pr ``` 7. Go to the GitHub website of your s390-tools fork and create a pull request for your branch "contrib-doc-pr" ### Update a pull request during review If there are changes requested during the review process, you have to update your code in the pull request. To retain the existing review comments, add commits on top of your pull request branch. Depending on the size and number of changes, a rebase of the pull request might be required. This will be communicated during the review. 1. Update your code with new commits ``` ~/s390-tools $ vi CONTRIBUTING.md ~/s390-tools $ git add CONTRIBUTING.md ~/s390-tools $ git commit -s -m "CONTRIBUTING: Add update PR info" ``` 2. Update your pull request by pushing changes ``` ~/s390-tools $ git push origin contrib-doc-pr ``` ### Finalize a pull request After the review process is finished or if you are explicitly asked for it, you have to create a clean commit series. 1. Save branch to "contrib-doc-pr.v1" ``` $ cd s390-tools ~/s390-tools $ git branch contrib-doc-pr.v1 ``` 2. Use interactive git rebase to merge commits, adjust commit messages, and rebase onto your local master branch ``` ~/s390-tools $ git rebase -i master ``` An editor is started and shows the following: ``` pick 2c73b9fc CONTRIBUTING: Outline steps to submit code pick fcfb0412 CONTRIBUTING: Add update PR info ``` To merge the update into the original commit, replace "pick fcfb0412" with "squash fcfb0412". ``` pick 2c73b9fc CONTRIBUTING: Outline steps to submit code squash fcfb0412 CONTRIBUTING: Add update PR info ``` Save the document and exit the editor to finish the merge. Another editor window is presented to modify the commit message. You now could change the commit message as follows: ``` CONTRIBUTING: Outline steps to submit code Explain in more detail how to submit s390-tools contributions as GitHub pull requests and how to update already submitted pull requests. Signed-off-by: Random Developer ``` With interactive rebasing you can also change the order of commits and modify commit messages with "reword". 3. Use `git push` with the force option to replace the existing pull request with your locally modified commits ``` ~/s390-tools $ git push --force origin contrib-doc-pr ``` ### Rebase a pull request If changes are made to the master branch in the official s390-tools repository you may be asked to rebase your branch with your contribution onto it. This can be required to prevent any merge conflicts that might arise when integrating your contribution. 1. Fetch all upstream changes from the official s390-tools repository, rebase your local master branch and update the master branch on your fork ``` ~/s390-tools $ git fetch upstream ~/s390-tools $ git checkout master ~/s390-tools $ git rebase upstream/master ~/s390-tools $ git push origin master ``` 2. Rebase your branch with your contribution onto the master branch of the official s390-tools repository ``` ~/s390-tools $ git checkout contrib-doc-pr ~/s390-tools $ git rebase master ``` 3. Use `git push` with the force option to replace the existing pull request with your locally modified commits ``` ~/s390-tools $ git push --force origin contrib-doc-pr ``` s390-tools-2.38.0/LICENSE000066400000000000000000000020371502674226300144560ustar00rootroot00000000000000Copyright IBM Corp. 2001, 2017 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. s390-tools-2.38.0/Makefile000066400000000000000000000043341502674226300151130ustar00rootroot00000000000000# Include common definitions include common.mak # # BASELIBS: Libraries that have no dependency to other libraries in s390-tools # LIBS: Libraries that can have a dependency to base libraries # TOOLS: Tools that can have a dependency to base libraries or libraries # ifeq ($(HOST_ARCH),s390x) BASELIB_DIRS = libutil libseckey LIB_DIRS = libvtoc libzds libdasd libccw libvmcp libekmfweb \ libkmipclient libcpumf libap libpv libzpci TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \ tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf \ vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \ ziomon iucvterm hyptop cmsfs-fuse qethqoat zfcpdump zdsfs cpumf \ systemd hmcdrvfs cpacfstats zdev dump2tar zkey netboot etc zpcictl \ lsstp hsci hsavmcore chreipl-fcp-mpath ap_tools rust opticsmon \ zpwr zmemtopo else BASELIB_DIRS = LIB_DIRS = libpv TOOL_DIRS = rust endif SUB_DIRS = $(BASELIB_DIRS) $(LIB_DIRS) $(TOOL_DIRS) all: $(TOOL_DIRS) clean: $(TOOL_DIRS) install: $(TOOL_DIRS) # # For simple "make" we explicitly set the MAKECMDGOALS to "all". # ifeq ($(MAKECMDGOALS),) MAKECMDGOALS = all endif # # We have to build the base libraries before the other libraries are built, # and then build the other libraries before the tools are built. Otherwise the # other libraries and tools would trigger parallel "make -C" builds for the # base libraries and the other libraries in case of "make -j". # # MAKECMDGOALS contains the list of goals, e.g. "clean all". We use # "foreach" to generate a ";" separated list of "make -C ". # For example the the expansion for "make clean all" is: # # $(MAKE) -C $@ [..] clean ; $(MAKE) -C $@ [...] all ; # # This ensures that the commandline targets are serialized and also "make -j" # works as expected, e.g. "make clean all -j 20". # $(TOOL_DIRS): $(LIB_DIRS) $(foreach goal,$(MAKECMDGOALS), \ $(MAKE) -C $@ TOPDIR=$(TOPDIR) HOST_ARCH=$(HOST_ARCH) $(goal) ;) .PHONY: $(TOOL_DIRS) $(LIB_DIRS): $(BASELIB_DIRS) $(foreach goal,$(MAKECMDGOALS), \ $(MAKE) -C $@ TOPDIR=$(TOPDIR) HOST_ARCH=$(HOST_ARCH) $(goal) ;) .PHONY: $(LIB_DIRS) $(BASELIB_DIRS): $(foreach goal,$(MAKECMDGOALS), \ $(MAKE) -C $@ TOPDIR=$(TOPDIR) HOST_ARCH=$(HOST_ARCH) $(goal) ;) .PHONY: $(BASELIB_DIRS) s390-tools-2.38.0/README.md000066400000000000000000000567131502674226300147420ustar00rootroot00000000000000s390-tools ========== The s390-tools package contains the source tree of a set of user space utilities for use with the s390 Linux kernel and device drivers. The package also contains the following files: * [CONTRIBUTING](CONTRIBUTING.md): Contribution guidelines * [LICENSE](LICENSE): The MIT license that applies to this package * [CHANGELOG](CHANGELOG.md): The history of s390-tools versions * [AUTHORS](AUTHORS.md): A list of all authors of the s390-tools package * [CODINGSTYLE](CODINGSTYLE.md): Recommendations for writing s390-tools code Package contents ---------------- * rust: all s390-tools that are written in rust and require external crates. Disable the compilation of all tools in `rust/` using HAVE_CARGO=0 See the `rust/README.md` for Details - cpacfinfo: Command line interface to get information about CP Assist for Cryptographic Functions (CPACF) - pvattest: Create, perform, and verify IBM Secure Execution attestation measurements. - pvapconfig: Automatic configure APQNs within an SE KVM guest - pvsecret: Manage secrets for IBM Secure Execution guests - pvimg: Create and inspect IBM Secure Execution images * dasdfmt: Low-level format ECKD DASDs with the classical Linux disk layout or the new z/OS compatible disk layout. * fdasd: Create or modify partitions on ECKD DASDs formatted with the z/OS compatible disk layout. * dasdview: Display DASD and VTOC information or dump the contents of a DASD to the console. * dasdinfo: Display unique DASD ID, either UID or volser. * genprotimg: Create an IBM Secure Execution (protected virtualization) image. The genprotimg command is a symbolic link to the `pvimg create` command. * udev rules: - 59-dasd.rules: rules for unique DASD device nodes created in /dev/disk/. - 57-osasnmpd.rules: udev rules for osasnmpd. - 60-readahead.rules: udev rules to set increased "default max readahead". - 40-z90crypt.rules: udev rules for z90crypt driver - 90-cpi.rules: udev rule to update Control-Program-Information when KVM is used. - 70-chreipl-fcp-mpath.rules: udev rules to monitor multipath events for re-IPL path failover and to adjust the re-IPL device in case needed. * systemd units: - cpi.service: Unit to apply CPI settings - dumpconf.service: Unit to configure dump on panic for s390 - mon_fsstatd.service: Unit for mon_fsstatd - mon_procd.service: Unit for mon_procd - iucvtty-login@.service: Instance unit to manage iucvtty instances - ttyrun-getty@.service: Instance unit to manage ttyrun * zipl: Make DASDs, SCSIs, NVMes or tapes bootable for system IPL or system dump. * zgetdump: Retrieve system dumps from either tapes, DASDs, SCSIs or NVMes. Decrypt Protected Virtualization (PV) dumps from IBM Secure Execution guests. * qetharp: Read and flush the ARP cache on OSA Express network cards. * tape390_display: Display information on the message display facility of a s390 tape device. * tape390_crypt: Control and query crypto settings for 3592 tape devices. * osasnmpd: NET-SNMP subagent implementing MIBs provided by OSA-Express features Fast Ethernet, Gigabit Ethernet, 10 Gigabit Ethernet. * qethconf: bash shell script simplifying the usage of qeth IPA (IP address takeover), VIPA (Virtual IP address) and Proxy ARP. * dbginfo.sh: Shell script collecting useful information about the current system for debugging purposes. * zfcpdump: Dump tool to create system dumps on fibre channel attached SCSI disk partitions. It is installed using the "zipl -d" command. * ip_watcher: Provides HiperSockets Network Concentrator functionality. It looks for addresses in the HiperSockets and sets them as Proxy ARP on the OSA cards. It also adds routing entries for all IP addresses configured on active HiperSockets devices. Use start_hsnc.sh to start HiperSockets Network Concentrator. * tunedasd: Adjust tunable parameters on DASD devices. * vmcp: Send commands from Linux as a z/VM guest to the z/VM control program (CP). Call vmcp with the CP command as an argument. The response of z/VM is written to the standard output. * vmur: Work with z/VM spool file queues (reader, punch, printer). * zfcpdbf: Display debug data of zfcp. zfcp provides traces via the s390 debug feature. Those traces are filtered with the zfcpdbf script, i.e. merge several traces, make it more readable etc. * sclpdbf: Display debug data for the sclp kernel component. * zconf: Set of scripts to configure and list status information of Linux on s390 devices. - chccwdev: Modify generic attributes of channel attached devices. - lscss: List channel subsystem devices. - lsdasd: List channel attached direct access storage devices (DASD). - lsqeth: List all qeth-based network devices with their corresponding settings. - lstape: List tape devices, both channel and FCP attached. - lszfcp: Show sysfs information about zfcp adapters, ports and units that are online. - lschp: List information about available channel-paths. - lsscm: List information about available Storage Class Memory Increments. - chchp: Modify channel-path state. - lsluns: List LUNs discovered in the FC SAN, or show encryption state of attached LUNs. - lszcrypt: Show Information about zcrypt devices and configuration. - chzcrypt: Modify the zcrypt configuration. - zcryptstats: Display usage statistics of IBM Crypto Express adapters. - znetconf: List and configure network devices for s390 network adapters. - cio_ignore: Query and modify the contents of the CIO device driver blacklist. - dasdstat: Configure and format the debugfs based DASD statistics data. * zkey: Use the zkey tool to generate secure AES keys that are enciphered with a master key of an IBM cryptographic adapter in CCA coprocessor mode. You can also use the zkey tool to validate and re-encipher secure AES keys. * dumpconf: Configure the dump device used for system dump in case a kernel panic occurs. This tool can also be used by the "dumpconf" systemd unit or as System V init script in /etc/init.d. Prerequisite for dumpconf is a Linux kernel with the "dump on panic" feature. * mon_statd: Linux - z/VM monitoring daemons. - mon_fsstatd: Daemon that writes file system utilization data to the z/VM monitor stream. - mon_procd: Daemon that writes process information data to the z/VM monitor stream. * cpuplugd: Manages CPU and memory resources based on a set of rules. Depending on the workload, CPUs can be enabled or disabled. The amount of memory can be increased or decreased exploiting the CMM1 feature. * ipl_tools: Tool set to configure and list re-IPL and shutdown actions. - lsreipl: List information of re-IPL device. - chreipl: Change re-IPL device settings. - lsshut: List the actions that are configured as responses to of halt, poff, reboot or panic. - chshut: Change the actions that are to result from of halt, poff, reboot or panic. * ziomon tools: Tool set to collect data for zfcp performance analysis and report. * iucvterm: z/VM IUCV terminal applications. A set of applications to provide terminal access via the z/VM Inter-User Communication Vehicle (IUCV). The terminal access does not require an active TCP/IP connection between two Linux guest operating systems. - iucvconn: Application to establish a terminal connection via z/VM IUCV. - iucvtty: Application to provide terminal access via z/VM IUCV. - ts-shell: Terminal server shell to authorize and control IUCV terminal connections for individual Linux users. * ttyrun: Depending on your setup, Linux on s390 might or might not provide a particular terminal or console. The ttyrun tool safely starts getty programs and prevents respawns through the init program, if a terminal is not available. * cmsfs-fuse: Read and write files stored on a z/VM CMS disk. The cmsfs-fuse file system translates the record-based EDF file system on the CMS disk to UNIX semantics. It is possible to mount a CMS disk and use common Linux tools to access the files on the disk. * hmcdrvfs: Provide (read-only) access to files stored on a (assigned) DVD inserted in a HMC drive. The command creates a FUSE.HMCDRVFS filesystem at the specified mount point. The feature works with either the LPAR or the z/VM hypervisor. But note especially for z/VM that the DVD must be assigned to the associated system image (LPAR). * hyptop: Provide a dynamic real-time view of a s390 hypervisor environment. The tool works with the z/VM and LPAR hypervisor. Depending on the available data it shows e.g. CPU and memory consumption of active LPARs or z/VM virtual guests. The tool provides a curses based user interface similar to the popular Linux 'top' command. * qethqoat: Query the OSA address table and display physical and logical device information. * zdsfs: Mount a z/OS DASD as Linux file system. * CPU-measurement facilities (CPU-MF) tools: Use the lscpumf tool to display information about the CPU-measurement counter and sampling facilities. Use the chcpumf tool to control the sampling facility support. Use lshwc to extract complete counter sets from the CPU Measurement Facilities. * cpacfstats: The cpacfstats tools provide a client/server application set to monitor and maintain CPACF activity counters. * zdev: Provides two tools to modify (chzdev) and display (lszdev) the persistent configuration of devices and device drivers that are specific to the s390 platform. * dump2tar: dump2tar is a tool for creating a tar archive from the contents of arbitrary files. It works even when the size of the actual file content is not known beforehand (e.g. FIFOs, sysfs files). * netboot: Provides simple tools to create a binary that can be used to implement simple network boot setups following the PXELINUX conventions. * libekmfweb: A shared library that provides functions to communicate with an EKMF Web server via REST calls over HTTPS. EKMF Web stands for IBM Enterprise Key Management Foundation - Web Edition, and is used to manage keys in an enterprise. * libkmipclient: A shared library that provides an KMIP client to communicate with an KMIP server. KMIP stands for Key Management Interoperability Protocol, and is an extensible communication protocol that defines message formats for the manipulation of cryptographic keys on a key management server. * hsci: Manage HiperSockets Converged Interfaces (HSCI). * hsavmcore: hsavmcore is designed to make the dump process with kdump more efficient. With hsavmcore, the HSA memory that contains a part of the production kernel's memory can be released early in the process. Depending on the size of the production kernel's memory, writing the dump to persistent storage can be time consuming and prevent the HSA memory from being reused by other LPARs. * chreipl-fcp-mpath: Use multipath information to change the configured FCP re-IPL path on detecting issues with the current path. * ap-check: A utility called by mdevctl to assist in managing vfio_ap-passthrough devices. For more information refer to the following publications: * "Device Drivers, Features, and Commands" chapter "Useful Linux commands" * "Using the dump tools" Dependencies ------------ The s390-tools package has several build and runtime requirements. If your build system does not have the required support, you can disable parts of the s390-tools build with "`make HAVE_=0`", for example "`make HAVE_FUSE=0`". The following table provides an overview of the used libraries and build options: | __LIBRARY__ | __BUILD OPTION__ | __TOOLS__ | |--------------|:------------------:|:--------------------------------------:| | fuse3 | `HAVE_FUSE` | cmsfs-fuse, zdsfs, hmcdrvfs, zgetdump, | | | | hsavmcore | | zlib | `HAVE_ZLIB` | zgetdump, dump2tar | | ncurses | `HAVE_NCURSES` | hyptop | | net-snmp | `HAVE_SNMP` | osasnmpd | | glibc-static | `HAVE_LIBC_STATIC` | zfcpdump | | openssl | `HAVE_OPENSSL` | zkey, libekmfweb, libkmipclient, | | | | zgetdump, rust/pvattest, rust/pvimg, | | | | zgetdump/pvsecret, opticsmon | | cryptsetup | `HAVE_CRYPTSETUP2` | zkey-cryptsetup | | json-c | `HAVE_JSONC` | zkey-cryptsetup, libekmfweb, | | | | libkmipclient | | glib2 | `HAVE_GLIB2` | zgetdump | | libcurl | `HAVE_LIBCURL` | libekmfweb, libkmipclient, rust/pvimg, | | | | rust/pvattest, rust/pvsecret, | | libxml2 | `HAVE_LIBXML2` | libkmipclient | | systemd | `HAVE_SYSTEMD` | hsavmcore | | libudev | `HAVE_LIBUDEV` | cpacfstatsd | | libnl3 | `HAVE_LIBNL3` | opticsmon | This table lists additional build or install options: | __COMPONENT__ | __OPTION__ | __TOOLS__ | |------------------|:----------------------------:|:------------------------:| | dracut | `HAVE_DRACUT` | zdev, chreipl-fcp-mpath, | | | | zipl | | initramfs-tools | `HAVE_INITRAMFS` | zdev, zipl | | | `ZDEV_ALWAYS_UPDATE_INITRD` | zdev | | rust | `HAVE_CARGO` | rust/* | The s390-tools build process uses "pkg-config" and therefore it must be available. Build and runtime requirements for specific tools ------------------------------------------------- In the following more details on the build an runtime requirements of the different tools are provided: * rust/pvsecret: For building pvsecret you need OpenSSL version 1.1.1 or newer installed (openssl-devel.rpm). Also required is cargo and libcurl. Tip: you may skip the pvsecret build by adding `HAVE_OPENSSL=0`, `HAVE_LIBCURL=0`, or `HAVE_CARGO=0`. The runtime requirements are: openssl-libs (>= 1.1.1). * dbginfo.sh: The tar package is required to archive collected data. * rust/pvimg: For building pvimg you need OpenSSL version 1.1.1 or newer installed (openssl-devel.rpm). Also required is cargo and libcurl. Tip: you may skip the pvimg build by adding `HAVE_OPENSSL=0`, `HAVE_LIBCURL=0`, or `HAVE_CARGO=0`. The runtime requirements are: openssl-libs (>= 1.1.1) and libcurl. * rust/pvattest: For building pvattest you need OpenSSL version 1.1.1 or newer installed (openssl-devel.rpm). Also required is cargo and libcurl. Tip: you may skip the pvattest build by adding `HAVE_OPENSSL=0`, `HAVE_LIBCURL=0`, or `HAVE_CARGO=0`. The runtime requirements are: openssl-libs (>= 1.1.1) and libcurl. * opticsmon: For building opticsmon OpenSSL and the Netlink Library Suite (libnl3) are required. Tip: you may skip the opticsmon build by adding `HAVE_OPENSSL=0` or `HAVE_LIBNL3=0` * osasnmpd: You need at least the NET-SNMP 5.1.x package (net-snmp-devel.rpm) installed, before building the osasnmpd subagent. For more information on NET-SNMP refer to: http://net-snmp.sourceforge.net * lsluns: For executing the lsluns script the sg_luns and sg_inq commands must be available. The sg_luns and sg_inq executables are part of the SCSI generic device driver package (sg3 utils/sg utils). * ziomon tools: For running the ziomon tools the following tools/packages are required: - Packages: blktrace, multipath-tools, sg3-utils - Tools: rsync, tar, lsscsi * zipl For CCW-type DASD dump, zlib compression can be used to compress the dump data before writing it to the DASD partition. It can benefit from s390 on-chip compression accelerator (DFLTCC) and provide a faster dumping process, hence lower system downtime. The zlib version integrated with zipl (zipl/boot/zlib) is based on the Linux kernel zlib (kernel version 6.3) which represents zlib version 1.1.3 with a limited number of functions and a number of updates on top including s390 hardware compression (DFLTCC) support. Also, all memory allocations are performed in advance, which aligns with zipl requirements. The CCW-type standalone dumper is built as a single binary and must be loaded to stage2 during boot. Hence, all required zlib functions must be integrated into it, and its size is restricted. To limit the size, only deflate-related parts are integrated (no decompression is required during dumping). Removing the inflate modules and function prototypes are the only major modifications made to the kernel version of zlib. * zgetdump For building zgetdump you need OpenSSL version 1.1.0 or newer installed (openssl-devel.rpm). Also required is glib2 (glib2-devel.rpm) and zlib (zlib-devel.rpm). Tip: you may skip the zgetdump build by adding `HAVE_OPENSSL=0`, `HAVE_GLIB2=0`, or `HAVE_ZLIB=0`. * cmsfs-fuse/zdsfs/hmcdrvfs/zgetdump: The tools cmsfs-fuse, zdsfs, hmcdrvfs, and zgetdump depend on FUSE. FUSE is provided by installing the fuse3 and libfuse3 packages and by a kernel compiled with `CONFIG_FUSE_FS`. For compiling the s390-tools package the fuse3-devel package is required. The cmsfs-fuse tool requires FUSE version 3.0 or newer for full functionality. For further information about FUSE see: https://github.com/libfuse/libfuse * hyptop: The ncurses-devel package is required to build hyptop. The libncurses package is required to run hyptop. IMPORTANT: When running hyptop on a System z10 LPAR, the required minimum microcode code level is the following: Driver 79 MCL N24404.008 in the SE-LPAR stream * ip_watcher/xcec-bridge: As of s390-tools-1.10.0, the minimum required kernel level is 2.6.26. For running ip_watcher these programs are required: - qetharp (s390-tools) - qethconf (s390-tools) - route (net-tools) * zfcpdbf: As of s390-tools-1.13.0, the minimum required kernel level is 2.6.38. * cpacfstats: For building the cpacfstats tools you need libudev installed (systemd-devel.rpm). Tip: you may skip the cpacfstats build by adding `HAVE_LIBUDEV=0` to the make invocation. To run the cpacfstats daemon the kernel needs to have performance events enabled (check for `CONFIG_PERF_EVENTS=y`). A new group 'cpacfstats' needs to be created and all users intending to use the tool should be added to this group. * zdev: Depending on the boot loader and initial RAM-disk mechanism used by a Linux distribution, specific steps may be required to make changes to the root device configuration persistent. zdev encapsulates this root device logic in a helper script called 'zdev-root-update': This script is invoked whenever the root device configuration is changed and it must ensure that the persistent root device configuration is put into effect during boot. zdev provides a sample implementation for zdev-root-update which can be selected during the 'make install' step using the `HAVE_DRACUT` variable: - `HAVE_DRACUT=1` installs a zdev-root-update helper that works with zipl as boot loader and dracut as initial RAM-disk provider. Distributors with different boot or RAM-disk mechanisms should provide a custom zdev-root-update helper script. - `ZDEV_ALWAYS_UPDATE_INITRD=1` upon modification of any persistent device configuration, chzdev updates the initial RAM-disk by default, without any additional user interaction. For some distributions, all the configuration attributes must be copied to the initial RAM-disk. Because the device configuration directives applied in the initial RAM-disk takes precedence over those stored in the root file- system. This copying is done usually by explicitly invoking a command. This build option makes it user-friendly and does this copying without any manual intervention. Some functions of zdev require that the following programs are available: - modprobe (kmod) - udevadm (systemd) The following programs are not required but when available will improve the functionality of the zdev tools: - lsblk (util-linux) - findmnt (util-linux) - vmcp (s390-tools) - ip (iproute2) * znetconf: For running znetconf these programs are required: - modprobe (kmod) - vmcp (s390-tools) * zkey: For building the zkey tools you need openssl version 0.9.7 or newer installed (openssl-devel.rpm). Also required are cryptsetup version 2.0.3 or newer (cryptsetup-devel.rpm), and json-c version 0.12 or newer (json-c-devel.rpm). Tip: you may skip the zkey build by adding `HAVE_OPENSSL=0`, and you may may skip the zkey-cryptsetup build by adding `HAVE_CRYPTSETUP2=0`, or `HAVE_JSONC=0` to the make invocation. A new group 'zkeyadm' needs to be created and all users intending to use the tool must be added to this group. The owner of the default key repository '/etc/zkey/repository' must be set to group 'zkeyadm' with write permission for this group. * libekmfweb: For building the libekmfweb shared library you need openssl version 1.1.1 or newer installed (openssl-devel.rpm). Also required are json-c version 0.13 or newer (json-c-devel.rpm), and libcurl version 7.59 or newer (libcurl-devel.rpm). Tip: you may skip the libekmfweb build by adding `HAVE_OPENSSL=0`, `HAVE_JSONC=0`, or `HAVE_LIBCURL=0` to the make invocation. * hsavmcore: For building the hsavmcore tool you need fuse version 3.0 and optionally systemd which is enabled by default, to disable systemd support, add `HAVE_SYSTEMD=0` to the make invocation. Tip: you may skip the hsavmcore build by adding `HAVE_FUSE=0` to the make invocation. * libkmipclient: For building the libkmipclient shared library you need openssl version 1.1.1 or newer installed (openssl-devel.rpm). Also required are json-c version 0.13 or newer (json-c-devel.rpm), libxml2 version 2.9.10 or newer (libxml2-devel.rpm), and libcurl version 7.59 or newer (libcurl-devel.rpm). Tip: you may skip the libkmipclient build by adding `HAVE_OPENSSL=0`, `HAVE_JSONC=0`, `HAVE_LIBXML2=0`, or `HAVE_LIBCURL=0` to the make invocation. * chreipl-fcp-mpath: For a complete list and documentation of the requirements, installation and uninstallation, please see [chreipl-fcp-mpath/README.md](chreipl-fcp-mpath/README.md). Summarized: chreipl-fcp-mpath requires GNU Bash, GNU Core Utilities, util-linux, udev, and multipath-tools. When using `HAVE_DRACUT=1` with the make invocation, it also requires dracut. When using `ENABLE_DOC=1` with the make invocation to build a fresh man page (instead of using the pre-cooked version) and render the README.md as HTML, make further requires pandoc and GNU awk for the build process. * ap-check: For building the ap-check mdevctl callout utility you need json-c version 0.13 or newer (json-c-devel.rpm). Tip: you may skip ap-check build by adding `HAVE_JSONC=0` to the make invocation. s390-tools-2.38.0/ap_tools/000077500000000000000000000000001502674226300152675ustar00rootroot00000000000000s390-tools-2.38.0/ap_tools/Makefile000066400000000000000000000031161502674226300167300ustar00rootroot00000000000000include ../common.mak MDEVCTL_DIR = /usr/lib/mdevctl/ MDEVCTL_SCRIPTS = /usr/lib/mdevctl/scripts.d/ MDEVCTL_CALLOUTS = /usr/lib/mdevctl/scripts.d/callouts/ MDEVCTL_DEP_DIR = /etc/mdevctl.d/ MDEVCTL_DEP_SCRIPTS = /etc/mdevctl.d/scripts.d/ MDEVCTL_DEP_CALLOUTS = /etc/mdevctl.d/scripts.d/callouts/ libs = $(rootdir)/libap/libap.a \ $(rootdir)/libutil/libutil.a ifeq (${HAVE_JSONC},0) all: $(SKIP) HAVE_JSONC=0 install: $(SKIP) HAVE_JSONC=0 else LDLIBS += -ljson-c all: ap-check ap-check: ap-check.o $(libs) install: all @if [ ! -d $(DESTDIR)$(MDEVCTL_CALLOUTS) ]; then \ mkdir -p $(DESTDIR)$(MDEVCTL_CALLOUTS); \ chown $(OWNER):$(GROUP) $(DESTDIR)$(MDEVCTL_DIR); \ chown $(OWNER):$(GROUP) $(DESTDIR)$(MDEVCTL_SCRIPTS); \ chown $(OWNER):$(GROUP) $(DESTDIR)$(MDEVCTL_CALLOUTS); \ chmod 755 $(DESTDIR)$(MDEVCTL_DIR); \ chmod 755 $(DESTDIR)$(MDEVCTL_SCRIPTS); \ chmod 755 $(DESTDIR)$(MDEVCTL_CALLOUTS); \ fi; \ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 ap-check \ $(DESTDIR)$(MDEVCTL_CALLOUTS) @if [ ! -d $(DESTDIR)$(MDEVCTL_DEP_CALLOUTS) ]; then \ mkdir -p $(DESTDIR)$(MDEVCTL_DEP_CALLOUTS); \ chown $(OWNER):$(GROUP) $(DESTDIR)$(MDEVCTL_DEP_DIR); \ chown $(OWNER):$(GROUP) $(DESTDIR)$(MDEVCTL_DEP_SCRIPTS); \ chown $(OWNER):$(GROUP) $(DESTDIR)$(MDEVCTL_DEP_CALLOUTS); \ chmod 755 $(DESTDIR)$(MDEVCTL_DEP_DIR); \ chmod 755 $(DESTDIR)$(MDEVCTL_DEP_SCRIPTS); \ chmod 755 $(DESTDIR)$(MDEVCTL_DEP_CALLOUTS); \ fi; \ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 ap-check.sh \ $(DESTDIR)$(MDEVCTL_DEP_CALLOUTS) endif clean: rm -f *.o *~ ap-check core .PHONY: all install clean s390-tools-2.38.0/ap_tools/ap-check.c000066400000000000000000000741331502674226300171160ustar00rootroot00000000000000/* * ap-check - Validate vfio-ap mediated device configuration changes * * This tool in intended to be driven via the callout API of the mdevctl * utility (https://github.com/mdevctl/mdevctl/) * * Copyright IBM Corp. 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include "lib/ap.h" #include "lib/util_base.h" #include "lib/util_libc.h" #include "lib/util_opt.h" #include "lib/util_path.h" #include "ap-check.h" /* The supported mdevctl callout version */ #define MDEVCTL_CAP_VERSION 2 static const struct mdevctl_action mdevctl_action_table[NUM_MDEVCTL_ACTIONS] = { {MDEVCTL_ACTION_DEFINE, "define"}, {MDEVCTL_ACTION_MODIFY, "modify"}, {MDEVCTL_ACTION_START, "start"}, {MDEVCTL_ACTION_STOP, "stop"}, {MDEVCTL_ACTION_UNDEFINE, "undefine"}, {MDEVCTL_ACTION_ATTRIBUTES, "attributes"}, {MDEVCTL_ACTION_CAPABILITIES, "capabilities"} /* * Note: the following actions are known to exist but currently ignored: * {MDEVCTL_ACTION_LIST, "list"}, * {MDEVCTL_ACTION_TYPES, "types"} */ }; static const struct mdevctl_event mdevctl_event_table[NUM_MDEVCTL_EVENTS] = { {MDEVCTL_EVENT_PRE, "pre"}, {MDEVCTL_EVENT_POST, "post"}, {MDEVCTL_EVENT_GET, "get"}, {MDEVCTL_EVENT_LIVE, "live"} /* * Note: the following events are known to exist but currently ignored: * {MDEVCTL_EVENT_NOTIFY, "notify"}, */ }; /* * Convert mdevctl action string to an enumerated value */ static enum mdevctl_action_id validate_action(char *action) { int i; for (i = 0; i < NUM_MDEVCTL_ACTIONS; i++) { if (strcmp(action, mdevctl_action_table[i].action) == 0) return mdevctl_action_table[i].id; } return MDEVCTL_ACTION_UNKNOWN; } /* * Convert mdevctl event string to an enumerated value */ static enum mdevctl_event_id validate_event(char *event) { int i; for (i = 0; i < NUM_MDEVCTL_EVENTS; i++) { if (strcmp(event, mdevctl_event_table[i].event) == 0) return mdevctl_event_table[i].id; } return MDEVCTL_EVENT_UNKNOWN; } static struct util_opt opt_vec[] = { UTIL_OPT_SECTION("DEVICE"), { .option = { "e", required_argument, NULL, 'e' }, .argument = "EVENT", .desc = "The type of callout being issued", }, { .option = { "a", required_argument, NULL, 'a' }, .argument = "ACTION", .desc = "The action being performed on the specified device", }, { .option = { "s", required_argument, NULL, 's' }, .argument = "STATE", .desc = "The state of the associated mdevctl command", }, { .option = { "u", required_argument, NULL, 'u' }, .argument = "UUID", .desc = "Universally Unique ID for the mediated device", }, { .option = { "p", required_argument, NULL, 'p' }, .argument = "PDEV", .desc = "Parent device name, e.g. matrix", }, { .option = { "t", required_argument, NULL, 't' }, .argument = "TYPE", .desc = "Mediated device type, e.g. vfio_ap-passthrough", }, UTIL_OPT_END }; /* * Initialize the ap_check anchor struct. */ static void ap_check_init(struct ap_check_anchor *anc) { anc->uuid = anc->parent = anc->type = NULL; anc->dev = vfio_ap_device_new(); anc->cleanup_lock = false; } /* * Free memory of ap_check anchor struct. */ static void ap_check_cleanup(struct ap_check_anchor *anc) { if (anc->uuid) free(anc->uuid); if (anc->parent) free(anc->parent); if (anc->type) free(anc->type); if (anc->dev) vfio_ap_device_free(anc->dev); if (anc->cleanup_lock) ap_release_lock_callout(); } /* * Exit ap_check */ static void __noreturn ap_check_exit(struct ap_check_anchor *anc, int rc) { ap_check_cleanup(anc); exit(rc); } /* * parses the command line */ static void ap_check_parse(struct ap_check_anchor *anc, int argc, char *argv[]) { bool action = false, event = false, state = false, bad_opts = false; int opt; util_opt_init(opt_vec, NULL); while (1) { opt = util_opt_getopt_long(argc, argv); if (opt == -1) break; switch (opt) { case 'e': if (event) { bad_opts = true; } else { anc->event = validate_event(optarg); event = true; } break; case 'a': if (action) { bad_opts = true; } else { anc->action = validate_action(optarg); action = true; } break; case 's': if (state) { bad_opts = true; } else { /* Ignore the state */ state = true; } break; case 'u': if (anc->uuid) bad_opts = true; else anc->uuid = util_strdup(optarg); break; case 'p': if (anc->parent) bad_opts = true; else anc->parent = util_strdup(optarg); break; case 't': if (anc->type) bad_opts = true; else anc->type = util_strdup(optarg); break; default: fprintf(stderr, "Unknown operand\n"); ap_check_exit(anc, EXIT_FAILURE); } } /* Make sure we got all expected input values */ if (!(action && event && state && anc->uuid && anc->parent && anc->type) || bad_opts) { fprintf(stderr, "Duplicate or missing operand\n"); ap_check_exit(anc, EXIT_FAILURE); } /* Check for invalid UUID */ if (!is_valid_uuid(anc->uuid)) { fprintf(stderr, "Invalid UUID specified\n"); ap_check_exit(anc, EXIT_FAILURE); } anc->dev->uuid = util_strdup(anc->uuid); /* Check for valid type */ if (strcmp(anc->type, VFIO_AP_TYPE) != 0) ap_check_exit(anc, APC_EXIT_UNKNOWN_TYPE); /* Check for invalid parent - currently only 'matrix' supported */ if (strcmp(anc->parent, "matrix") != 0) { fprintf(stderr, "Invalid parent specified\n"); ap_check_exit(anc, EXIT_FAILURE); } } /* * Call a function for each entry in a directory: * int callback(const char *abs_path, const char *rel_path, void *data) * Continues for all entries in the directory regardless of callback return * code. Will return 0 or, if one or more callbacks failed, the first nonzero * rc received. */ static int path_for_each(const char *path, int (*callback)(const char *, const char *, void *), void *data) { struct dirent *de; int rc = 0; int r = 0; DIR *dir; char *p; dir = opendir(path); if (!dir) return -1; while ((de = readdir(dir))) { if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) continue; util_asprintf(&p, "%s/%s", path, de->d_name); r = callback(p, de->d_name, data); /* Save first nonzero return code for caller */ if (rc == 0 && r != 0) rc = r; free(p); } closedir(dir); return rc; } /* * Report an error message when the specified configuration will conflict * with an existing device */ static void conflict_error(const char *uuid, unsigned int a, unsigned int d, bool persistent) { if (uuid) { if (persistent) { fprintf(stderr, "APQN %u.%u is defined for autostart by %s\n", a, d, uuid); } else { fprintf(stderr, "APQN %u.%u already in use by %s\n", a, d, uuid); } } else { if (persistent) { fprintf(stderr, "AQPN %u.%u is not defined for " "vfio_ap-passthrough use by the persistent " "ap bus mask settings\n", a, d); } else { fprintf(stderr, "AQPN %u.%u is not allowed for " "vfio_ap-passthrough use by the active ap " "bus mask settings\n", a, d); } } } /* * Compare the list of adapters and domains for two devices, reporting error * messages for any conflicts that occur. A conflict occurs when both devices * have the same adapter + domain pair. * The function below takes advantage of the fact that the lists are known to * be sorted in numeric order; therefore we can use this information to run * the lists in parallel rather than always starting from the beginning. */ static int find_apqn_conflicts(const char *uuid, struct util_list *adapters, struct util_list *domains, struct util_list *adapters2, struct util_list *domains2, bool persistent) { struct vfio_ap_node *a, *a2, *d, *d2; int rc = 0; /* Checks for conflicts with the device */ a = util_list_start(adapters); a2 = util_list_start(adapters2); while ((a != NULL) && (a2 != NULL)) { if (a->id == a2->id) { d = util_list_start(domains); d2 = util_list_start(domains2); while ((d != NULL) && (d2 != NULL)) { if (d->id == d2->id) { /* Report error, look for more */ conflict_error(uuid, a->id, d->id, persistent); rc = -1; d = util_list_next(domains, d); d2 = util_list_next(domains2, d2); } else if (d->id > d2->id) { d2 = util_list_next(domains2, d2); } else { d = util_list_next(domains, d); } } a = util_list_next(adapters, a); a2 = util_list_next(adapters2, a2); } else if (a->id > a2->id) { a2 = util_list_next(adapters2, a2); } else { a = util_list_next(adapters, a); } } return rc; } /* * If the provided path maps to a valid vfio-ap device configuration, * determine if its current configuration will conflict with the proposed * changes. */ static int check_other_mdev_cfg_cb(const char *path, const char *filename, void *data) { struct other_mdev_cb_data *cbdata = data; struct vfio_ap_device *dev = cbdata->dev; struct vfio_ap_device *dev2 = NULL; int rc = 0; /* Skip anything that isn't an mdev config */ if (!is_valid_uuid(filename)) goto out; /* Skip if this is the input device */ if (strcasecmp(cbdata->uuid, filename) == 0) goto out; /* Read the device config */ dev2 = vfio_ap_device_new(); if (vfio_ap_read_device_config(path, dev2) != 0) goto out; /* If wrong device type, skip */ if (strcmp(dev2->type, VFIO_AP_TYPE) != 0) goto out; /* If not AUTO device, skip */ if (dev2->manual) goto out; /* Perform mdev-to-mdev apqn conflict analysis */ rc = find_apqn_conflicts(filename, dev->adapters, dev->domains, dev2->adapters, dev2->domains, true); out: if (dev2 != NULL) vfio_ap_device_free(dev2); return rc; } /* * Perform conflict analysis against all other vfio-ap persistent * configurations. */ int check_other_mdevs_cfg(struct ap_check_anchor *anc) { struct other_mdev_cb_data cb_data; if (!util_path_is_dir(VFIO_AP_CONFIG_PATH)) return 0; cb_data.uuid = anc->uuid; cb_data.dev = anc->dev; return path_for_each(VFIO_AP_CONFIG_PATH, check_other_mdev_cfg_cb, &cb_data); } /* * If the provided path maps to a valid device, determine if its current * configuration will conflict with the proposed changes. */ static int check_other_mdev_sysfs_cb(const char *path, const char *filename, void *data) { struct other_mdev_cb_data *cbdata = data; struct vfio_ap_device *dev = cbdata->dev; struct vfio_ap_device *dev2; char *matrix_path; char buf[80]; int rc = 0; FILE *f; if (!is_valid_uuid(filename) || path == NULL || strcasecmp(filename, cbdata->uuid) == 0) return 0; /* * Read the 'matrix' attribute to get the list of queues for the active * device. If the sysfs attribute is unreadable, assume the device is * being destroyed and skip it. */ matrix_path = path_get_vfio_ap_attr(filename, "matrix"); f = fopen(matrix_path, "r"); free(matrix_path); if (!f) return 0; dev2 = vfio_ap_device_new(); while (fgets(buf, sizeof(buf), f)) vfio_ap_parse_matrix(dev2, buf); vfio_ap_sort_matrix_results(dev2); fclose(f); /* Look for conflicts between target device and this device */ rc = find_apqn_conflicts(filename, dev->adapters, dev->domains, dev2->adapters, dev2->domains, false); vfio_ap_device_free(dev2); return rc; } /* Run conflict analysis against all other active vfio-ap devices */ static int check_other_mdevs_sysfs(struct ap_check_anchor *anc) { struct other_mdev_cb_data cb_data; char *root; int rc = 0; cb_data.uuid = anc->uuid; cb_data.dev = anc->dev; root = path_get_vfio_ap_mdev(""); if (util_path_is_dir(root)) rc = path_for_each(root, check_other_mdev_sysfs_cb, &cb_data); free(root); return rc; } /* * Determine if there are any conflicts between the specified device and * the active apmask/aqmask settings. This is done by treating the masks * as a temporary vfio_ap_device with all of the associated APQNs owned by * the system. */ static int check_sysfs_mask_conflicts(struct ap_check_anchor *anc) { struct vfio_ap_device *sysdev = vfio_ap_device_new(); char *apmask = util_zalloc(AP_MASK_SIZE); char *aqmask = util_zalloc(AP_MASK_SIZE); int rc = 0; if (ap_read_sysfs_masks(apmask, aqmask, AP_MASK_SIZE) != 0) { fprintf(stderr, "Error reading system AP settings\n"); rc = -1; goto out; } /* Convert the masks to a device with the associated APQNs */ ap_mask_to_list(apmask, sysdev->adapters); ap_mask_to_list(aqmask, sysdev->domains); /* Perform conflict analysis */ rc = find_apqn_conflicts(NULL, anc->dev->adapters, anc->dev->domains, sysdev->adapters, sysdev->domains, false); out: free(apmask); free(aqmask); vfio_ap_device_free(sysdev); return rc; } /* * Determine if there are any conflicts between the specified device and * the apmask/aqmask settings stored in udev. This is done by treating * the masks as a temporary vfio_ap_device with all of the associated * AQPNs owned by the system. */ static int check_cfg_mask_conflicts(struct ap_check_anchor *anc) { struct vfio_ap_device *sysdev = vfio_ap_device_new(); char *apmask = util_zalloc(AP_MASK_SIZE); char *aqmask = util_zalloc(AP_MASK_SIZE); bool read_ap = false, read_aq = false; char *path; int rc = 0; path = path_get_ap_udev(); if (!ap_read_udev_masks(path, apmask, aqmask, &read_ap, &read_aq)) { fprintf(stderr, "Error reading system AP settings\n"); rc = -1; goto out; } /* Convert the masks to a device with the associated APQNs */ ap_mask_to_list(apmask, sysdev->adapters); ap_mask_to_list(aqmask, sysdev->domains); /* Perform conflict analysis */ rc = find_apqn_conflicts(NULL, anc->dev->adapters, anc->dev->domains, sysdev->adapters, sysdev->domains, true); out: free(apmask); free(aqmask); free(path); vfio_ap_device_free(sysdev); return rc; } /* Subroutine to handle checking shared between DEFINE and MODIFY actions. */ static int ap_check_changes(struct ap_check_anchor *anc) { int rc = 0, rc2; rc = ap_get_lock_callout(); if (rc) { fprintf(stderr, "Failed to acquire configuration lock %d\n", rc); rc = -1; goto out; } anc->cleanup_lock = true; if (vfio_ap_read_device_config(NULL, anc->dev) != 0) { fprintf(stderr, "Failed to read device config\n"); rc = -1; goto out; } if (strcmp(anc->dev->type, anc->type) != 0) { fprintf(stderr, "Invalid mdev_type: %s\n", anc->dev->type); rc = -1; goto out; } if (!anc->dev->manual) { /* Check against all other AUTO config files */ rc = check_other_mdevs_cfg(anc); /* Check against the system UDEV rule for apmask/aqmask */ rc2 = check_cfg_mask_conflicts(anc); /* If either hit an error, reflect this */ rc = rc != 0 ? rc : rc2; } /* If successful, lock must remain held until post callout */ if (rc == 0) anc->cleanup_lock = false; out: return rc; } static int ap_check_active(struct ap_check_anchor *anc) { int rc, rc2; /* Ensure device with control domains also has usage domains */ if (util_list_is_empty(anc->dev->domains) && !util_list_is_empty(anc->dev->controls)) { fprintf(stderr, "At least one usage domain must be specified\n"); return -1; } /* Check against all other active vfio-ap devices */ rc = check_other_mdevs_sysfs(anc); /* Check against the system sysfs values for apmask/aqmask */ rc2 = check_sysfs_mask_conflicts(anc); /* If either hit an error, reflect this */ rc = rc != 0 ? rc : rc2; return rc; } static int ap_do_dynamic_config(struct ap_check_anchor *anc) { char *adapters, *domains, *controls, *path, *attr; int asize, dsize, csize, size; int rc = 0; FILE *f; adapters = vfio_ap_device_get_adapter_mask(anc->dev, &asize); domains = vfio_ap_device_get_domain_mask(anc->dev, &dsize); controls = vfio_ap_device_get_control_mask(anc->dev, &csize); if (!adapters || !domains || !controls) { fprintf(stderr, "Failed to read device config\n"); rc = -1; goto out; } /* * The 'ap_config' command takes a comma-delimited list of the 3 masks * combined. Each mask size includes a terminating character, two of * which will be replaced by commas and the final replaced by a * newline, which ap_config seems to require at the end of the input. * Add one to ensure room for a null termination. */ size = asize + dsize + csize + 1; attr = util_zalloc(size); /* Use the 3 masks to generate a 'ap_config' command */ rc = snprintf(attr, size, "%s,%s,%s\n", adapters, domains, controls); if (rc < size - 1) { fprintf(stderr, "Error creating ap_config command\n"); rc = -1; goto out; } /* Apply the new configuration to the active device */ path = path_get_vfio_ap_attr(anc->uuid, "ap_config"); f = fopen(path, "w"); if (!f) { fprintf(stderr, "Error opening ap_config\n"); rc = -1; goto out; } rc = fputs(attr, f); fclose(f); if (rc == EOF) fprintf(stderr, "Error writing to ap_config\n"); else rc = 0; out: if (!adapters) free(adapters); if (!domains) free(domains); if (!controls) free(controls); if (!path) free(path); if (!attr) free(attr); return rc; } /* * Determine if defining the specified device is a valid operation. * mdevctl can reach us for a DEFINE under the following circumstances: * 1) the device does not exist * 2) the device is active but does not have a config file, so this action * would be to generate a config file based upon the active device. * DEFINE has no effect on an active device (if one exists) it only creates * the configuration file. The config file might be empty or may have various * attributes if being fed by --jsonfile or an active device. */ static int ap_check_handle_define(struct ap_check_anchor *anc) { char *path = path_get_vfio_ap_mdev_config(anc->uuid); if (util_path_is_readable(path)) { fprintf(stderr, "Config already exists\n"); free(path); return -1; } free(path); return ap_check_changes(anc); } /* * Determine if modifying the specified device is a valid operation. * mdevctl can reach us for a MODIFY under the following circumstances: * 1) Modifying a MANUAL device * 2) Modifying an AUTO device * In the case of MANUAL, we don't take any action because changes made via * MODIFY don't take affect on the active mdev until a STOP/START cycle. * In the case of AUTO, we must compare the contents of the proposed device * with the contents of stashed AUTO mdev configurations + the system. */ static int ap_check_handle_modify(struct ap_check_anchor *anc) { char *path = path_get_vfio_ap_mdev_config(anc->uuid); FILE *fd = fopen(path, "r"); /* Determine if a base config file already exists for UUID */ free(path); if (fd == NULL) { fprintf(stderr, "Config doesn't exist\n"); return -1; } fclose(fd); return ap_check_changes(anc); } /* * Determine if modifying the active device is a valid operation. * This is similar to STARTing a device, in that the requested modifications * cannot conflict with the active configuration. LIVE MODIFY can only be * handled if the ap_config attribute is available in the vfio-ap driver. */ static int ap_check_handle_live_modify(struct ap_check_anchor *anc) { int rc; rc = ap_get_lock_callout(); if (rc) { fprintf(stderr, "Failed to acquire configuration lock %d\n", rc); return -1; } anc->cleanup_lock = true; if (vfio_ap_read_device_config(NULL, anc->dev) != 0) { fprintf(stderr, "Failed to read device config\n"); return -1; } if (strcmp(anc->dev->type, anc->type) != 0) { fprintf(stderr, "Invalid mdev_type: %s\n", anc->dev->type); return -1; } if (!vfio_ap_need_dynamic_config(anc->dev)) { fprintf(stderr, "vfio-ap module does not support ap_config for live modification"); return -1; } /* Check if the new configuration would cause conflicts */ rc = ap_check_active(anc); if (rc) return rc; /* Attempt to perform the dynamic configuration */ rc = ap_do_dynamic_config(anc); return rc; } /* * Determine if starting the specified device is a valid operation. * mdevctl can reach us for a START under the following circumstances: * 1) STARTing a MANUAL device * 1a) Where the MANUAL device is defined (has a config file) * 1b) Where the MANUAL device is NOT defined (no config file). * For vfio-ap this case provides an mdev with no adapters/domains. * 1c) Where the MANUAL device is NOT defined but a full configuration is * provided via --jsonfile * 2) STARTing an AUTO device * 2a) Where the AUTO device is defined (has a config file) * 2b) Where the AUTO device is NOT defined but a full configuration is * provided via --jsonfile * In each case, we must compare the proposed device with the contents of * active mdevs + the system. */ static int ap_check_handle_start(struct ap_check_anchor *anc) { int rc = 0; /* Can only start a device if vfio_ap is built-in or loaded */ if (!util_path_is_dir(VFIO_AP_PATH)) { fprintf(stderr, "vfio_ap module is not loaded\n"); ap_check_exit(anc, EXIT_FAILURE); } rc = ap_get_lock_callout(); if (rc) { fprintf(stderr, "Failed to acquire configuration lock %d\n", rc); rc = -1; goto out; } anc->cleanup_lock = true; if (vfio_ap_read_device_config(NULL, anc->dev) != 0) { fprintf(stderr, "Failed to read device config\n"); rc = -1; goto out; } if (strcmp(anc->dev->type, anc->type) != 0) { fprintf(stderr, "Invalid mdev_type: %s\n", anc->dev->type); rc = -1; goto out; } rc = ap_check_active(anc); /* If successful, lock must remain held until post callout */ if (rc == 0) anc->cleanup_lock = false; out: return rc; } /* * Acquire the appropriate serialization so that the specified device can be * STOPped. */ static int ap_check_handle_stop(void) { int rc; rc = ap_get_lock_callout(); if (rc) { fprintf(stderr, "Failed to acquire configuration lock %d\n", rc); return -1; } /* The lock must remain held until post callout */ return 0; } /* * Determine if UNDEFINEing the specified device is a valid operation. * mdevctl can reach us for an UNDEFINE under the following circumstances: * 1) UNDEFINEing an active device * 2) UNDEFINEing an inactive device * UNDEFINE has no effect on the active device, it only removes the config * file. */ static int ap_check_handle_undefine(struct ap_check_anchor *anc) { char *path = path_get_vfio_ap_mdev_config(anc->uuid); int rc = 0; rc = ap_get_lock_callout(); if (rc) { fprintf(stderr, "Failed to acquire configuration lock %d\n", rc); rc = -1; goto out; } anc->cleanup_lock = true; if (vfio_ap_read_device_config(path, anc->dev) != 0) { fprintf(stderr, "Failed to read device config\n"); rc = -1; goto out; } if (strcmp(anc->dev->type, anc->type) != 0) { fprintf(stderr, "Invalid mdev_type: %s\n", anc->dev->type); rc = -1; goto out; } /* Success: lock must remain held until post callout */ anc->cleanup_lock = false; out: free(path); return rc; } /* * For callouts where the "pre" callout would have acquired the lock, it is * now safe to remove the lock as all changes have been committed. */ static int ap_check_handle_post(void) { return ap_release_lock_callout(); } /* For the specified device, print the attributes to stdout in JSON format */ static int ap_check_handle_get_attributes(struct ap_check_anchor *anc) { struct vfio_ap_device *dev = anc->dev; struct vfio_ap_node *node; bool has_attr = false; char buf[80]; char *path; FILE *f; int rc; /* * For the get-attributes callout, we are typically called without the * callout lock held. However, there is a particular scenario (define * of an active mdev) where we may or may not be called with the lock * already held on behalf of mdevctl, depending on the mdevctl version. * Let's test for lock ownership first and, if already owned by the * parent (mdevctl) proceed rather than waiting on the file lock. */ rc = ap_try_lock_callout(); switch (rc) { case 0: /* Lock acquired */ anc->cleanup_lock = true; break; case 1: /* Lock held by parent -- trust the lock will remain held */ break; default: /* Lock not acquired or held by parent -- do a normal obtain */ rc = ap_get_lock_callout(); if (rc) { fprintf(stderr, "Failed to acquire configuration lock %d\n", rc); return -1; } anc->cleanup_lock = true; } /* * Read the 'matrix' and 'control_domains' attributes to get the * current attributes of the active device. If either of these sysfs * attributes is unreadable, assume the device is being destroyed * and return nothing. */ path = path_get_vfio_ap_attr(anc->uuid, "matrix"); f = fopen(path, "r"); free(path); if (!f) return 0; while (fgets(buf, sizeof(buf), f)) vfio_ap_parse_matrix(dev, buf); vfio_ap_sort_matrix_results(dev); fclose(f); path = path_get_vfio_ap_attr(anc->uuid, "control_domains"); f = fopen(path, "r"); free(path); if (!f) return 0; while (fgets(buf, sizeof(buf), f)) vfio_ap_parse_control(dev, buf); fclose(f); printf("[{"); if (!util_list_is_empty(dev->adapters)) { util_list_iterate(dev->adapters, node) { if (has_attr) printf("},{"); printf("\"assign_adapter\": \"%u\"", node->id); has_attr = true; } } if (!util_list_is_empty(dev->domains)) { util_list_iterate(dev->domains, node) { if (has_attr) printf("},{"); printf("\"assign_domain\": \"%u\"", node->id); has_attr = true; } } if (!util_list_is_empty(dev->controls)) { util_list_iterate(dev->controls, node) { if (has_attr) printf("},{"); printf("\"assign_control_domain\": \"%u\"", node->id); has_attr = true; } } printf("}]\n"); return 0; } /* * If the target 'attr' is in the 's' array, add it to the 't' array. */ static void json_add_attr(json_object *t, json_object *s, const char *attr) { size_t vlen, alen = strlen(attr); const char *val; json_object *o; int i, num; num = json_object_array_length(s); for (i = 0; i < num; i++) { o = json_object_array_get_idx(s, i); val = json_object_get_string(o); vlen = strlen(val); if (alen == vlen && strncasecmp(attr, val, alen) == 0) { json_object_array_add(t, json_object_new_string(val)); return; } } } /* * Generate a JSON-formatted list of capability information that this script * supports and return it to the caller via stdout. An example of what the * output should look like (without the newlines): * { * "supports": { * "version": 2, * "actions": ["define", * "modify", * "start", * "stop", * "undefine", * "attributes", * "capabilities"], * "events": ["pre", * "post", * "get", * "live"] * } * } */ static int ap_check_handle_get_capabilities(void) { json_object *root, *csup, *cver, *cact, *cev, *cap, *caps, *o; int i, rc = 0; root = json_object_from_fd(STDIN_FILENO); if (!root) { fprintf(stderr, "No capabilities provided\n"); return -1; } if (!json_object_object_get_ex(root, "provides", &csup)) { fprintf(stderr, "No supported capabilities provided\n"); rc = -1; goto out; } if (!json_object_object_get_ex(csup, "version", &cver)) { fprintf(stderr, "No version provided in capabilities\n"); rc = -1; goto out; } if (!json_object_object_get_ex(csup, "actions", &cact)) { fprintf(stderr, "No actions provided in capabilities\n"); rc = -1; goto out; } if (!json_object_object_get_ex(csup, "events", &cev)) { fprintf(stderr, "No events provided in capabilities\n"); rc = -1; goto out; } /* * Advertise the subset of supported capabilities from the list * provided on stdin. */ cap = json_object_new_object(); caps = json_object_new_object(); json_object_object_add(cap, "supports", caps); /* * Currently we always advertise a fixed version, but we may need to * revisit this if we increase MDEVCTL_CAP_VERSION in the future (e.g. * how to handle ap-check having a greater supported version than * what mdevctl reports) */ o = json_object_new_int(MDEVCTL_CAP_VERSION); json_object_object_add(caps, "version", o); o = json_object_new_array(); for (i = 0; i < NUM_MDEVCTL_ACTIONS; i++) json_add_attr(o, cact, mdevctl_action_table[i].action); json_object_object_add(caps, "actions", o); o = json_object_new_array(); for (i = 0; i < NUM_MDEVCTL_EVENTS; i++) json_add_attr(o, cev, mdevctl_event_table[i].event); json_object_object_add(caps, "events", o); /* Return supported capabilities JSON on stdout */ printf("%s\n", json_object_to_json_string(cap)); json_object_put(cap); out: json_object_put(root); return rc; } /* * Determine which mdevctl action is being checked and handle accordingly. */ static int ap_check_handle_action(struct ap_check_anchor *anc) { int rc = 0; switch (anc->event) { case MDEVCTL_EVENT_PRE: switch (anc->action) { case MDEVCTL_ACTION_DEFINE: rc = ap_check_handle_define(anc); break; case MDEVCTL_ACTION_MODIFY: rc = ap_check_handle_modify(anc); break; case MDEVCTL_ACTION_START: rc = ap_check_handle_start(anc); break; case MDEVCTL_ACTION_STOP: rc = ap_check_handle_stop(); break; case MDEVCTL_ACTION_UNDEFINE: rc = ap_check_handle_undefine(anc); break; default: /* Ignore some actions including unknown ones */ break; } break; case MDEVCTL_EVENT_POST: switch (anc->action) { case MDEVCTL_ACTION_DEFINE: case MDEVCTL_ACTION_MODIFY: case MDEVCTL_ACTION_START: case MDEVCTL_ACTION_STOP: case MDEVCTL_ACTION_UNDEFINE: ap_check_handle_post(); break; default: /* Ignore other post events */ break; } break; case MDEVCTL_EVENT_GET: switch (anc->action) { case MDEVCTL_ACTION_ATTRIBUTES: rc = ap_check_handle_get_attributes(anc); break; case MDEVCTL_ACTION_CAPABILITIES: rc = ap_check_handle_get_capabilities(); break; default: /* Ignore some actions including unknown ones */ break; } break; case MDEVCTL_EVENT_LIVE: switch (anc->action) { case MDEVCTL_ACTION_MODIFY: rc = ap_check_handle_live_modify(anc); break; default: /* Ignore some actions including unknown ones */ break; } break; default: /* Ignore any unknown events */ break; } return rc; } /* * */ int main(int argc, char *argv[]) { struct ap_check_anchor anchor; int rc; ap_check_init(&anchor); ap_check_parse(&anchor, argc, argv); rc = ap_check_handle_action(&anchor); ap_check_exit(&anchor, rc); } s390-tools-2.38.0/ap_tools/ap-check.h000066400000000000000000000030321502674226300171110ustar00rootroot00000000000000/* * ap-check - Validate vfio-ap mediated device configuration changes * * Copyright IBM Corp. 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef AP_CHECK_H #define AP_CHECK_H #include /* * List of all of the supported mdevctl actions */ enum mdevctl_action_id { MDEVCTL_ACTION_DEFINE = 0, MDEVCTL_ACTION_LIST, MDEVCTL_ACTION_MODIFY, MDEVCTL_ACTION_START, MDEVCTL_ACTION_STOP, MDEVCTL_ACTION_TYPES, MDEVCTL_ACTION_UNDEFINE, MDEVCTL_ACTION_ATTRIBUTES, MDEVCTL_ACTION_CAPABILITIES, /* UNKNOWN must always be the last in the list */ MDEVCTL_ACTION_UNKNOWN, }; #define NUM_MDEVCTL_ACTIONS MDEVCTL_ACTION_UNKNOWN struct mdevctl_action { enum mdevctl_action_id id; const char action[32]; }; enum mdevctl_event_id { MDEVCTL_EVENT_PRE = 0, MDEVCTL_EVENT_POST, MDEVCTL_EVENT_GET, MDEVCTL_EVENT_LIVE, MDEVCTL_EVENT_UNKNOWN, }; #define NUM_MDEVCTL_EVENTS MDEVCTL_EVENT_UNKNOWN struct mdevctl_event { enum mdevctl_event_id id; const char event[32]; }; /* ap-check special exit codes */ #define APC_EXIT_UNKNOWN_TYPE 2 struct ap_check_anchor { enum mdevctl_event_id event; enum mdevctl_action_id action; char *uuid; char *parent; char *type; struct vfio_ap_device *dev; /* Active Masks */ char apmask[80]; char aqmask[80]; /* Persistent Masks */ char p_apmask[80]; char p_aqmask[80]; bool cleanup_lock; }; struct other_mdev_cb_data { const char *uuid; struct vfio_ap_device *dev; }; #endif /* AP_CHECK_H */ s390-tools-2.38.0/ap_tools/ap-check.sh000066400000000000000000000010621502674226300172750ustar00rootroot00000000000000#!/bin/sh # # ap-check.sh - Wrapper script for 'ap-check' binary # # mdevctl has deprecated the /etc/mdevctl.d/scripts.d/callouts/ location in # newer releases. This wrapper ensures that mdevctl 1.2.0 and older can # still access 'ap-check' for now, and will be removed at a later time. # # Copyright 2022 IBM Corp. # # s390-tools is free software; you can redistribute it and/or modify # it under the terms of the MIT license. See LICENSE for details. # [ -e /usr/lib/mdevctl/scripts.d/callouts/ap-check ] && /usr/lib/mdevctl/scripts.d/callouts/ap-check "$@" s390-tools-2.38.0/bin/000077500000000000000000000000001502674226300142175ustar00rootroot00000000000000s390-tools-2.38.0/bin/mk-authors000077500000000000000000000055621502674226300162470ustar00rootroot00000000000000#!/bin/bash # # mk-authors - Update the s390-tools authors file (AUTHORS.md) # # Invocation examples: # # $ mk-authors # $ mk-authors -t v2.0.0 # # Copyright IBM Corp. 2017 # # s390-tools is free software; you can redistribute it and/or modify # it under the terms of the MIT license. See LICENSE for details. cmd=$(basename $0) authors_file="AUTHORS.md" cleanup() { test -f $authors_file_tmp && rm -f $authors_file_tmp } trap cleanup EXIT # # Print usage information # print_usage() { cat <&2 exit 1 } # # Parse options # opts=$(getopt -o ht: -l help,tag: -n $cmd -- "$@") if [ $? != 0 ]; then print_parse_error_and_exit fi # Use eval to remove getopt quotes eval set -- $opts while [ -n $1 ]; do case $1 in -h | --help) print_usage exit 0 ;; -t | --tag) release_tag_last=$2 shift 2 ;; --) shift break ;; *) break ;; esac done if [ $# != 0 ]; then echo "$cmd: Invalid argument $1" >&2 print_parse_error_and_exit fi # # Get git root and last release tag # git_root=$(git rev-parse --show-toplevel 2> /dev/null) if [ $? -ne 0 ]; then echo "$cmd: Use tool within s390-tools git repository" >&2 print_parse_error_and_exit fi authors_file_path=$git_root/$authors_file # Get last release tag if not specified via -t if [ -z $release_tag_last ]; then tag_commit=$(git rev-list --tags --max-count=1) release_tag_last=$(git describe --tags $tag_commit) fi # Verify release tag git show $release_tag_last &> /dev/null if [ $? -ne 0 ]; then echo "$cmd: Cannot find tag '$release_tag_last'" >&2 print_parse_error_and_exit fi # # Update authors file # # Create temporary file for new author list authors_file_tmp=$(mktemp /tmp/authors.XXXXXX) echo "$cmd: Adding new authors since tag: $release_tag_last" { # Print authors list without header tail -n +4 $authors_file_path # Add the new authors git log --format="- %an" $release_tag_last..master # Then sort everything and remove duplicates } | sort | uniq > $authors_file_tmp # Create new AUTHORS file with header ... cat < $authors_file_path List of all individuals having contributed content to s390-tools ---------------------------------------------------------------- EoHEADER # ... and add new content cat $authors_file_tmp >> $authors_file_path cat < $(@) .chreipl-fcp-mpath.7.cksum: README.md sha256sum $(<) > $(@) .PHONY: chreipl-fcp-mpath-doc-clean chreipl-fcp-mpath-doc-clean: rm -f README.html README.pdf chreipl-fcp-mpath.md chreipl-fcp-mpath: chreipl-fcp-mpath-doc chreipl-fcp-mpath-clean: chreipl-fcp-mpath-doc-clean else # $(ENABLE_DOC) != 1 # We bundle a pre-cooked man page with the source-code so that distributions # don't need `pandoc` in order to be able to ship the man page. As of this # writing multiple distributions don't have a packaged version of it. # # In order to remember to regenerate this pre-cooked version whenever the # README.md is changed, we also generate a checksum of the README.md, bundle # that as well, and compare that whenever `make` is called. This way, the # freshness of the man page can be checked, even if `pandoc` is not available, # or ENABLE_DOC disabled. MANPAGE_FRESH := $(shell sha256sum --check .chreipl-fcp-mpath.7.cksum >/dev/null && echo 1 || echo 0) ifeq ($(MANPAGE_FRESH),0) $(warning chreipl-fcp-mpath.7 is outdated, please regenerate it by calling `make ENABLE_DOC=1`) endif endif # $(ENABLE_DOC) == 1 # ## Install # .PHONY: chreipl-fcp-mpath-install # The content of `CHREIPLZFCPMP_POST_INSTALL` (bash script) is automatically # called *after* installing chreipl-fcp-mpath during `make install`. If not # defined (the default), nothing happens. You may define this on the make # command line, or by creating a `.make.config` and defining the variable in # there. chreipl-fcp-mpath-install: $(CHREIPLZFCPMP_POST_INSTALL) install: chreipl-fcp-mpath-install # install udev rules INSTDIRS += $(UDEVRULESDIR) $(DESTDIR)$(UDEVRULESDIR): install_dirs .PHONY: chreipl-fcp-mpath-install-udev-rules chreipl-fcp-mpath-install-udev-rules: | $(DESTDIR)$(UDEVRULESDIR) chreipl-fcp-mpath-install-udev-rules: udev/rules.d/70-chreipl-fcp-mpath.rules $(INSTALL_DATA) -t $(DESTDIR)$(UDEVRULESDIR) \ udev/rules.d/70-chreipl-fcp-mpath.rules chreipl-fcp-mpath-install: chreipl-fcp-mpath-install-udev-rules # install udev helper programs INSTDIRS += $(UDEVDIR) $(DESTDIR)$(UDEVDIR): install_dirs .PHONY: chreipl-fcp-mpath-install-udev-helper chreipl-fcp-mpath-install-udev-helper: | $(DESTDIR)$(UDEVDIR) chreipl-fcp-mpath-install-udev-helper: $(CHREIPL_FCP_MPATH_UDEV_HELPER) $(INSTALL_EXEC) -t $(DESTDIR)$(UDEVDIR) $(CHREIPL_FCP_MPATH_UDEV_HELPER) chreipl-fcp-mpath-install: chreipl-fcp-mpath-install-udev-helper # install common library files INSTDIRS += $(CHREIPLZFCPMPDIR) $(DESTDIR)$(CHREIPLZFCPMPDIR): install_dirs .PHONY: chreipl-fcp-mpath-install-libfiles chreipl-fcp-mpath-install-libfiles: | $(DESTDIR)$(CHREIPLZFCPMPDIR) chreipl-fcp-mpath-install-libfiles: $(CHREIPL_FCP_MPATH_COMMON) $(INSTALL_DATA) -t $(DESTDIR)$(CHREIPLZFCPMPDIR) \ $(CHREIPL_FCP_MPATH_COMMON) chreipl-fcp-mpath-install: chreipl-fcp-mpath-install-libfiles ifeq ($(HAVE_DRACUT),1) # install dracut config files INSTDIRS += $(DRACUTCONFDIR) $(DESTDIR)$(DRACUTCONFDIR): install_dirs .PHONY: chreipl-fcp-mpath-install-dracut-config chreipl-fcp-mpath-install-dracut-config: | $(DESTDIR)$(DRACUTCONFDIR) chreipl-fcp-mpath-install-dracut-config: dracut/dracut.conf.d/70-chreipl-fcp-mpath.conf $(INSTALL_DATA) -t $(DESTDIR)$(DRACUTCONFDIR) \ dracut/dracut.conf.d/70-chreipl-fcp-mpath.conf chreipl-fcp-mpath-install: chreipl-fcp-mpath-install-dracut-config endif # chreipl-fcp-mpath: install man page INSTDIRS += $(MANDIR) $(DESTDIR)$(MANDIR)/man7: install_dirs .PHONY: chreipl-fcp-mpath-install-man-page chreipl-fcp-mpath-install-man-page: | $(DESTDIR)$(MANDIR)/man7 chreipl-fcp-mpath-install-man-page: chreipl-fcp-mpath.7 $(INSTALL_DATA) -t $(DESTDIR)$(MANDIR)/man7 \ chreipl-fcp-mpath.7 chreipl-fcp-mpath-install: chreipl-fcp-mpath-install-man-page # ## Utility # # Utilities for the debug feature of chreipl-fcp-mpath-common.sh. # # When `chreipl-fcp-mpath` is built with D=1 (default is D=0), each run # of one of the helper scripts will create a debug log in $(DEBUGOUTDIR) # (default: /run/udev) which among other things contains the complete shell # trace of that script run, with some added information that would not be # inspectable otherwise with just the trace. # # There is currently no way of enabling/disabling this at runtime. # # chreipl-fcp-mpath-common.sh defines debug log file name as # "chreiplzfcpmp-${debug_trace_tag}-${SEQNUM:-0}.XXXXXXXXXX" where SEQNUM is a # udev rule environment variable and each X is replaced with some [[:alnum:]] # by mktemp. For a definition of `debug_trace_tag`, please see the comments # in the source. # # The following targets can be used for some simple access and filtering of the # logs during development. DEBUG_LOG_GLOB = $(DEBUGOUTDIR)/chreiplzfcpmp-[[:digit:]][[:digit:]][[:alpha:]][[:alpha:]][[:alpha:]][[:alpha:]]-+([[:digit:]]).[[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]] # display all debug log files on the system .PHONY: chreipl-fcp-mpath-debug-logs chreipl-fcp-mpath-debug-logs: @ls -1d $(DEBUG_LOG_GLOB) # display only debug log files of script runs that exited with status 0 (= good) # # XXX: `sed -n -e '$p'` is used instead of `tail -n1` to prevent an other # dependency just for that (both invocations do the same thing). .PHONY: chreipl-fcp-mpath-debug-logs-filter-good chreipl-fcp-mpath-debug-logs-filter-good: @for lg in $(DEBUG_LOG_GLOB); do \ sed -e '/^+ trap_exit$$/,/^+ trap - EXIT$$/d' "$${lg}" \ | sed -n -e '$$p' \ | grep -q -e '^+ exit 0$$' || continue; \ ls -d "$${lg}"; \ done # display only debug log files of script runs that didn't exit with status 0 # (= bad) .PHONY: chreipl-fcp-mpath-debug-logs-filter-bad chreipl-fcp-mpath-debug-logs-filter-bad: @for lg in $(DEBUG_LOG_GLOB); do \ sed -e '/^+ trap_exit$$/,/^+ trap - EXIT$$/d' "$${lg}" \ | sed -n -e '$$p' \ | grep -q -e '^+ exit 0$$' && continue; \ ls -d "$${lg}"; \ done .PHONY: chreipl-fcp-mpath-debug-logs-clean chreipl-fcp-mpath-debug-logs-clean: rm -f $(DEBUG_LOG_GLOB) s390-tools-2.38.0/chreipl-fcp-mpath/README.md000066400000000000000000000243221502674226300202340ustar00rootroot00000000000000 NAME ==== chreipl-fcp-mpath - use multipath information for re-IPL path failover on a running Linux instance DESCRIPTION =========== The IPL process of Linux on Z or LinuxONE from an FCP-attached SCSI volume uses exactly one path to the volume. If this path is unavailable, the IPL fails. The **chreipl-fcp-mpath** toolset monitors **udev** events about paths to the re-IPL volume. If the currently configured re-IPL path becomes unavailable, the toolset checks for operational paths to the same volume. If available, it reconfigures the re-IPL settings to use an operational path. Thus, re-IPL from an FCP-attached SCSI volume can be successful despite path failures on a running Linux instance if at least one path to the re-IPL volume remains operational. **Chreipl-fcp-mpath** requires **udev**, **multipathd** and **dm-multipath**. Once installed, the toolset runs automatically and autonomously. No user intervention is possible or required. Other than installing the toolset, there is no user interface for **chreipl-fcp-mpath**. Requirements ------------ The **chreipl-fcp-mpath** tool has the following requirements on the Linux instance that is being monitored: - The Linux instance must have started successfully, during IPL. - The running Linux instance must use **dm-multipath** and **multipathd** for the configured re-IPL volume - a volume that contains a zipl boot record and has one of its paths used in the re-IPL configuration. - **udev** must run. - The toolset must observe at least one event about the configured re-IPL path. Examples for such events are: the SCSI disk comes online, or a path of the corresponding multipath device goes down or comes back online. - The WWID of the re-IPL volume must not change while the Linux instance is running. - When the configured re-IPL path becomes unavailable while the Linux instance is running, at least one operational path to the re-IPL volume must be available, or must become available. If no such path is available when the Linux instance is rebooted, the re-IPL path is not changed. - The tool assumes that any manually reconfigured re-IPL device is valid and operational. The tool treats a newly configured re-IPL device like the initially configured re-IPL device. In particular, if the newly configured re-IPL device fulfills the requirements of the tool, re-IPL path failover takes place if the configured re-IPL path becomes unavailable. Caution with Manual Changes to the Configured re-IPL Target ----------------------------------------------------------- **chreipl-fcp-mpath** is designed to accept operator-inititated changes of the re-IPL device. However, concurrent changes by the operator and tool driven changes can result in the operator change being overwritten. To avoid this problem, change the re-IPL device only during steady-state operations, when no path events happen. Alternatively, make sure that no events are processed while you change the device. See [EXAMPLES](#examples) for one way to suspend event processing. MESSAGES ======== During monitoring and event processing, **chreipl-fcp-mpath** writes messages to the syslog. When the configured re-IPL path is changed to a different path to the same volume (priority *daemon.notice*): > Changed re-IPL path to: \:\:\. When a path event indicates that the last available path has become non-operational (priority *daemon.alert*): > The re-IPL device cannot be changed because no operational path to the > re-IPL volume remains. The next re-IPL might fail unless you re-attach or > enable at least one valid path to the re-IPL volume. When changing the configured re-IPL device failed because of an error with the used Linux kernel interface (priority *daemon.crit*): > Changing the re-IPL device failed. The current re-IPL settings might be > inconsistent. Check and correct the settings (see the README.md of > chreipl-fcp-mpath) to make sure that the current re-IPL device is valid. A failure to change the re-IPL device can indicate an inconsistent setting that cannot be corrected automatically by **chreipl-fcp-mpath**. As a result, the next re-IPL might fail or might not use the intended re-IPL device. You can use the following tools to check and correct the current settings: - **lsreipl** to confirm that the intended re-IPL device is configured; - **chreipl** to change the re-IPL device; - **lszfcp** to inspect the state of available paths to the re-IPL device. SOFTWARE REQUIREMENTS ===================== **chreipl-fcp-mpath** integrates into s390-tools's build and install infrastructure. Use **make** to build it. No explicit dependency management is in place, but the toolset has some software dependencies besides the requirements in section [Requirements](#requirements): - GNU Bash; - GNU Core Utilities (mktemp, readlink, sync, truncate, sha256sum); - util-linux (flock, hexdump, logger); - udev / systemd-udev; - multipath-tools. To make use of the optional dracut configuration you need: dracut. To build a fresh version of the documentation (man page) you need: - pandoc; - GNU Core Utilities (date); - GNU awk; otherwise the pre-cooked version shipped with the source will be used. INSTALLATION ============ If your distribution includes a packaged version of **chreipl-fcp-mpath**, either as a separate package or as part of a **s390-tools** package, install that package. Otherwise, you can either install it from source as part of **s390-tools** or separately. To install **chreipl-fcp-mpath** as part of **s390-tools**, use **make** on the top-level directory of your **s390-tools** distribution. Installing the entire distribution might overwrite other already installed tools. To install the tool separately, change into the **chreipl-fcp-mpath** directory, and use **make** there. You need *root* privileges to install the tool into the root file system. Calling **make** runs the build steps. Calling **make install** runs the build steps and copies the resulting components to their final destination. **s390-tools** offers more options and targets to customize the build (see **make help**). **chreipl-fcp-mpath** has the following optional build options: | Option | Values | Default | Effect | :----- | :----: | :-----: | :----- | HAVE_DRACUT | 0, 1 | 0 | Install a dracut configuration file that includes **chreipl-fcp-mpath** in the initial ramdisks built with **dracut**. | ENABLE_DOC | 0, 1 | 0 | Build a fresh version of the man page for **chreipl-fcp-mpath**. Specify any options as arguments for both the **make** and **make install** command as shown in the following example: ~ # cd chreipl-fcp-mpath/ ~ # make HAVE_DRACUT=1 ENABLE_DOC=1 ~ # make HAVE_DRACUT=1 ENABLE_DOC=1 install After the installation, reload the udev rules database: ~ # udevadm control --reload *The toolset is now active on your running Linux instance.* If you use the *HAVE_DRACUT=1* option, also rebuild your initial ramdisk, to immediately include the toolset instead of waiting for the next kernel update. How to rebuild the initial ramdisk and the naming scheme for the resulting file or files depends on your distribution. The following example applies to Fedora and to Red Hat Enterprise Linux: ~ # dracut --force /boot/initramfs-"$(uname -r)".img "$(uname -r)" For SUSE Linux Enterprise Server run for example: ~ # dracut --hostonly --force /boot/initrd-"$(uname -r)" "$(uname -r)" These commands replace the initial ramdisk for the currently running kernel. If your distribution uses **zipl** as its boot loader, run **zipl** to refresh the boot record to find the new initial ramdisk. ~ # zipl With dracut enabled, **make install** deploys the following files to these default locations: /usr/lib/chreipl-fcp-mpath/chreipl-fcp-mpath-common.sh /usr/lib/dracut/dracut.conf.d/70-chreipl-fcp-mpath.conf /usr/lib/udev/chreipl-fcp-mpath-is-ipl-tgt /usr/lib/udev/chreipl-fcp-mpath-is-ipl-vol /usr/lib/udev/chreipl-fcp-mpath-is-reipl-zfcp /usr/lib/udev/chreipl-fcp-mpath-record-volume-identifier /usr/lib/udev/chreipl-fcp-mpath-try-change-ipl-path /usr/lib/udev/rules.d/70-chreipl-fcp-mpath.rules /usr/share/man/man7/chreipl-fcp-mpath.7 UNINSTALL ========= If your distribution includes a separately from **s390-tools** packaged version of **chreipl-fcp-mpath**, uninstall that package. For installations without distribution packaging, you cannot uninstall **chreipl-fcp-mpath** with **make**. Instead, remove the toolset by deleting the installed files as listed in [INSTALLATION](#installation)), reload the udev rules database, and rebuild all modified initial ramdisks as described in [INSTALLATION](#installation)). EXAMPLES ======== Manual Changes to the Configured re-IPL Device ---------------------------------------------- As outlined in [DESCRIPTION](#description), be cautious when manually changing the configured re-IPL device. Assure that your reconfiguration actions do not collide with concurrent automatic event processing by **chreipl-fcp-mpath**. You can avoid such collisions, by stopping event processing, making your changes, and then re-enabling event processing. You need *root* privileges for running the commands in the following example: ~ # udevadm settle ~ # udevadm control --stop-exec-queue ~ # chreipl ... ~ # udevadm control --start-exec-queue Listing messages with journalctl -------------------------------- If your Linux instance includes **journalctl**, use the following command to list all messages that are issued by **chreipl-fcp-mpath**: ~ # journalctl -t chreipl-fcp-mpath To list only messages that were issued since the last IPL, use this command: ~ # journalctl -t chreipl-fcp-mpath -b REPORTING BUGS ============== Use the **Issues** functionality on GitHub to report any bugs in **chreipl-fcp-mpath**: [s390-tools Issues]( "Link to the s390-tools Issues page"). SEE ALSO ======== **chreipl**(8), **dracut**(8), **journalctl**(1), **lsreipl(8)**, **lszfcp**(8), **multipath**(8), **multipathd**(8), **udev**(7), **udevadm**(8), **zipl**(8) s390-tools-2.38.0/chreipl-fcp-mpath/chreipl-fcp-mpath-common.sh.in000066400000000000000000000330421502674226300245060ustar00rootroot00000000000000# SPDX-License-Identifier: MIT # # chreipl-fcp-mpath: use multipath information to change FCP IPL target # (C) Copyright IBM Corp. 2021 # # Uses the following system-utilities (and shell-builtins): # GNU coreutils: # - mktemp # - readlink # - sync # util-linux: # - flock # - logger # Makes use of udev event environment variables: # SEQNUM # (1) expand failed globs to an empty string # (2) extended pattern matching to strip leading/trailing whitespaces shopt -s nullglob extglob # (1) don't overwrite existing files using redirects (e.g.: `>`) set -o noclobber # make sure any state files created are only writeable by the owning user umask 027 # create log if DEBUG is enabled (with Make: D=1) # # Each script importing this library and expecting a debug log to be created # must declare a *trace tag* in a variable `debug_trace_tag`. This is used as # identifier in the log file name. The format is: # # [[:digit:]][[:digit:]][[:alpha:]][[:alpha:]][[:alpha:]][[:alpha:]] # \ /\ / # --------\ /-------- -------------------\ /------------------- # \/ \/ # relative position of some unique abbreviation for the script # execution in the name, excluding any common prefix # udev rules if '@DEBUG@' && [ -v debug_trace_tag ] && tlg="$( mktemp -p '@debugoutdir@' \ "chreiplzfcpmp-${debug_trace_tag}-${SEQNUM:-0}.XXXXXXXXXX" \ 2>/dev/null)" then readonly tlg exec >|"${tlg}" 2>&1 set -x set else unset tlg fi declare -gr ID_FILE='@chreiplzfcpmp-id-file@' declare -gr FW_LOCK_FILE='@chreiplzfcpmp-fwlock-file@' declare -gA TRAP_EXIT_FN=() declare -gf trap_exit 1>/dev/null function trap_exit() { local fn for fn in "${TRAP_EXIT_FN[@]}"; do "${fn}" done trap - EXIT } trap trap_exit EXIT # Output variables: # id_file_unlock_exclusive_create() - call to unlock when finished with # critical section # # XXX: `id_file_lock_*` can't be taken recursively function id_file_lock_exclusive_create() { declare -g ID_FILE_LOCK="" # prevent concurrent file creation # # First, open the file defined in ${ID_FILE} for writing; this will # succeed and create the file only if it doesn't exist already. If the # file already exist, the first open attempt will fail and we fall # back to opening it only for reading; this will always succeed if the # file already exists (the reason why the first attempty failed). In # both cases store the corresponding file descriptor in # ${ID_FILE_LOCK}. # # XXX: This should be race free. # open() with O_EXCL... is atomic (we set `noclobber` as shell # option); at least as long as we talk about a local FS. if ! { exec {ID_FILE_LOCK}>"${ID_FILE}"; } 2>/dev/null; then { exec {ID_FILE_LOCK}<"${ID_FILE}"; } 2>/dev/null \ || return 1 fi declare -gf id_file_unlock_exclusive_create 1>/dev/null function id_file_unlock_exclusive_create() { if [ -v ID_FILE_LOCK ]; then sync "${ID_FILE}" 2>/dev/null # release file and implicitly the lock, if taken exec {ID_FILE_LOCK}>&- unset ID_FILE_LOCK fi unset "TRAP_EXIT_FN[id_file_unlock_exclusive_create]" } TRAP_EXIT_FN+=( [id_file_unlock_exclusive_create]=id_file_unlock_exclusive_create ) flock --exclusive --timeout 5 "${ID_FILE_LOCK}" || return 2 return 0 } # Output variables: # id_file_unlock_exclusive_no_create() - call to unlock when finished with # critical section # # XXX: `id_file_lock_*` can't be taken recursively function id_file_lock_exclusive_no_create() { declare -g ID_FILE_LOCK="" # Open the file defined in ${ID_FILE} for reading, and store the # corresponding file descriptor in ${ID_FILE_LOCK}. # # XXX: return code is used in `chreipl-fcp-mpath-try-change-ipl-path` { exec {ID_FILE_LOCK}<"${ID_FILE}"; } 2>/dev/null || return 1 declare -gf id_file_unlock_exclusive_no_create 1>/dev/null function id_file_unlock_exclusive_no_create() { if [ -v ID_FILE_LOCK ]; then sync "${ID_FILE}" 2>/dev/null # release file and implicitly the lock, if taken exec {ID_FILE_LOCK}<&- unset ID_FILE_LOCK fi unset "TRAP_EXIT_FN[id_file_unlock_exclusive_no_create]" } TRAP_EXIT_FN+=( [id_file_unlock_exclusive_no_create]=id_file_unlock_exclusive_no_create ) flock --exclusive --timeout 5 "${ID_FILE_LOCK}" || return 2 return 0 } # Output variables: # id_file_unlock_shared_no_create() - call to unlock when finished with # critical section # # XXX: `id_file_lock_*` can't be taken recursively function id_file_lock_shared_no_create() { declare -g ID_FILE_LOCK="" # Open the file defined in ${ID_FILE} for reading, and store the # corresponding file descriptor in ${ID_FILE_LOCK}. { exec {ID_FILE_LOCK}<"${ID_FILE}"; } 2>/dev/null || return 1 declare -gf id_file_unlock_shared_no_create 1>/dev/null function id_file_unlock_shared_no_create() { if [ -v ID_FILE_LOCK ]; then # release file and implicitly the lock, if taken exec {ID_FILE_LOCK}<&- unset ID_FILE_LOCK fi unset "TRAP_EXIT_FN[id_file_unlock_shared_no_create]" } TRAP_EXIT_FN+=( [id_file_unlock_shared_no_create]=id_file_unlock_shared_no_create ) flock --shared --timeout 5 "${ID_FILE_LOCK}" || return 2 return 0 } # Output variables: # firmware_unlock_exclusive() - call to unlock when finished with critical section # # XXX: `firmware_lock_*` can't be taken recursively function firmware_lock_exclusive() { declare -g FIRMWARE_LOCK="" # Open the file defined in ${FW_LOCK_FILE} for reading, and store the # corresponding file descriptor in ${FIRMWARE_LOCK} (it doesn't matter # whether this is a normal file or directory). This file descriptor # will only be used for locking - not for actual I/O. { exec {FIRMWARE_LOCK}<"${FW_LOCK_FILE}"; } 2>/dev/null || return 1 declare -gf firmware_unlock_exclusive 1>/dev/null function firmware_unlock_exclusive() { if [ -v FIRMWARE_LOCK ]; then # release file and implicitly the lock, if taken exec {FIRMWARE_LOCK}<&- unset FIRMWARE_LOCK fi unset "TRAP_EXIT_FN[firmware_unlock_exclusive]" } TRAP_EXIT_FN+=([firmware_unlock_exclusive]=firmware_unlock_exclusive) flock --exclusive --timeout 5 "${FIRMWARE_LOCK}" || return 2 return 0 } # Output variables: # firmware_unlock_shared() - call to unlock when finished with critical section # # XXX: `firmware_lock_*` can't be taken recursively function firmware_lock_shared() { declare -g FIRMWARE_LOCK="" # Open the file defined in ${FW_LOCK_FILE} for reading, and store the # corresponding file descriptor in ${FIRMWARE_LOCK} (it doesn't matter # whether this is a normal file or directory). This file descriptor # will only be used for locking - not for actual I/O. { exec {FIRMWARE_LOCK}<"${FW_LOCK_FILE}"; } 2>/dev/null || return 1 declare -gf firmware_unlock_shared 1>/dev/null function firmware_unlock_shared() { if [ -v FIRMWARE_LOCK ]; then # release file and implicitly the lock, if taken exec {FIRMWARE_LOCK}<&- unset FIRMWARE_LOCK fi unset "TRAP_EXIT_FN[firmware_unlock_shared]" } TRAP_EXIT_FN+=([firmware_unlock_shared]=firmware_unlock_shared) flock --shared --timeout 5 "${FIRMWARE_LOCK}" || return 2 return 0 } # Output variables: # IPL_TYPE # IPL_BUSID # IPL_WWPN # IPL_LUN function firmware_get_ipl_information() { declare -g IPL_TYPE="" IPL_BUSID="" IPL_WWPN="" IPL_LUN="" # Take lock so we don't see any intermediate state from other helpers # running in parallel firmware_lock_shared || return 5 { read -r IPL_TYPE _ < /sys/firmware/reipl/reipl_type; } 2>/dev/null \ || return 1 { read -r IPL_BUSID _ < /sys/firmware/reipl/fcp/device; } 2>/dev/null \ || return 2 { read -r IPL_WWPN _ < /sys/firmware/reipl/fcp/wwpn; } 2>/dev/null \ || return 3 { read -r IPL_LUN _ < /sys/firmware/reipl/fcp/lun; } 2>/dev/null \ || return 4 firmware_unlock_shared # show read values in debug log if enabled if '@DEBUG@'; then declare -p IPL_TYPE IPL_BUSID IPL_WWPN IPL_LUN 1>&2 fi return 0 } # Input: # 1: absolute canonical path to the scsi device in sysfs, e.g.: # /sys/devices/css0/0.0.0014/0.0.1700/host1/rport-1:0-0/target1:0:0/1:0:0:1075789848 # Output variables: # SDEV_LUN function sdev_get_lun() { local sdev="${1}" sdev_lun_str # bash uses `intmax_t` as width for integer variables, and glibc # defines this either as `long int` on 64 bit systems, or # `long long int` on other. local -i sdev_lun=0 fcp_lun=0 sdev_lun_str="${sdev##*:}" # e.g.: 1075789848 [[ "${sdev_lun_str}" == +([[:digit:]]) ]] || return 1 # "cast" to integer sdev_lun="${sdev_lun_str}" # convert the Linux integer LUN format to the hexadecimal 64 bit T10 # LUN representation format used by many s390x interfaces (( fcp_lun = (((sdev_lun >> 0) & 0xffff) << 48) | (((sdev_lun >> 16) & 0xffff) << 32) | (((sdev_lun >> 32) & 0xffff) << 16) | (((sdev_lun >> 48) & 0xffff) << 0) )) # the '0x' prefix is part of the length printf -v SDEV_LUN "%#018llx" "${fcp_lun}" # show read values in debug log if enabled if '@DEBUG@'; then declare -p SDEV_LUN 1>&2 fi return 0 } # Input: # 1: path to the scsi device in sysfs, e.g.: # /sys/devices/css0/0.0.0014/0.0.1700/host1/rport-1:0-0/target1:0:0/1:0:0:1075789848 # , or a symlink pointing to the scsi device, e.g.: # /sys/class/block/sds/device # Output variables: # SDEV_BUSID # SDEV_WWPN # SDEV_LUN function sdev_get_fcp_addressing() { local sdev="${1}" fcp_lun rport rport_wwpn zfcp_dev declare -g SDEV_BUSID="" SDEV_WWPN="" SDEV_LUN="" sdev="$(readlink -se "${sdev}")" || return 1 # get the LUN for this SDEV # # sets ${SDEV_LUN} sdev_get_lun "${sdev}" || return 2 # get the WWPN of the remote port this SDEV is attached to printf -v rport "%s" "${sdev}"/../../fc_remote_ports/rport-*:*-* # e.g.: /sys/devices/css0/0.0.0016/0.0.1740/host0/rport-0:0-1/fc_remote_ports/rport-0:0-1 [ "${rport}" != "" ] || return 3 # XXX: This works even if the rport is currently in a bad # state, so e.g. when it has just gone down because of a # cable pull. { read -r rport_wwpn _ < "${rport}"/port_name; } 2>/dev/null \ || return 4 # The Linux kernel doesn't guarantee the same format as in # /sys/firmware/..., so make sure it is the one we expect. [[ "${rport_wwpn}" =~ ^0x[[:xdigit:]]{1,16}$ ]] || return 5 # the '0x' prefix is part of the length printf -v rport_wwpn "%#018llx" "${rport_wwpn}" # get the Device Bus-ID of the device via which this SDEV is attached zfcp_dev="$(readlink -se "${sdev}"/../../../..)" || return 6 # e.g.: /sys/devices/css0/0.0.0016/0.0.1740 zfcp_dev="${zfcp_dev##*/}" # shellcheck disable=2034 SDEV_BUSID="${zfcp_dev}" # shellcheck disable=2034 SDEV_WWPN="${rport_wwpn}" return 0 } # Input: # 1: path to the scsi device in sysfs # Output variables: # SDEV_WWID function sdev_get_wwid() { local sdev="${1}" local -a wwid declare -g SDEV_WWID="" # read the volume identifier without stripping any content # # XXX: we can read the WWID file, even if the SDEV is currently not # operational (e.g.: due to the path has gone away), as long as # the VPD PG 83 is still cached in the kernel; and the page gets # only released on SDEV device release. { readarray -d "" -t wwid < "${sdev}"/wwid; } 2>/dev/null \ || return 1 if '@DEBUG@'; then declare -p wwid 1>&2; fi # test whether we read something # # This strips all leading spaces from the beginning of the read WWID # (until the first non-space or NUL character), and checks whether the # result is empty. # Hence, we return early if the WWID consists of only whitespace. # # XXX: there could be unexpected characters in the returned ID. # `scsi_id` from the udev helpers sanitizes the strings it reads # from the devices, so they can be used in environment variables # without much danger. # But we don't export anything here, so it should be fine. [ "${wwid[0]/#*([[:space:]])}" != "" ] || return 2 # shellcheck disable=2034 SDEV_WWID="${wwid[0]}" return 0 } # Input: # 1: path to the scsi device in sysfs # Return Value: # == 0: SDEV referenced by `1` in good state # != 0: otherwise function sdev_test_path_state() { local sdev="${1}" state zfcp_failed zfcp_in_recovery rport port_state sdev="$(readlink -se "${sdev}")" || return 1 { read -r state _ < "${sdev}"/state; } 2>/dev/null || return 2 { read -r zfcp_failed _ < "${sdev}"/zfcp_failed; } 2>/dev/null \ || return 3 { read -r zfcp_in_recovery _ < "${sdev}"/zfcp_in_recovery; } 2>/dev/null \ || return 4 printf -v rport "%s" "${sdev}"/../../fc_remote_ports/rport-*:*-* # e.g.: /sys/devices/css0/0.0.0016/0.0.1740/host0/rport-0:0-1/fc_remote_ports/rport-0:0-1 [ "${rport}" != "" ] || return 5 { read -r port_state _ < "${rport}"/port_state; } 2>/dev/null \ || return 6 if '@DEBUG@'; then declare -p state zfcp_failed zfcp_in_recovery port_state 1>&2 fi [ "${state}" = "running" ] || return 7 [ "${zfcp_failed}" = "0" ] || return 8 [ "${zfcp_in_recovery}" = "0" ] || return 9 { [ "${port_state}" = "Online" ] \ || [ "${port_state}" = "Marginal" ]; } || return 10 return 0 } # Input: # *: all input parameters are used as quoted message function log_note() { logger -p 'daemon.notice' -t 'chreipl-fcp-mpath' "${*}" &>/dev/null } # Input: # *: all input parameters are used as quoted message function log_crit() { logger -p 'daemon.crit' -t 'chreipl-fcp-mpath' "${*}" &>/dev/null } # Input: # *: all input parameters are used as quoted message function log_alert() { logger -p 'daemon.alert' -t 'chreipl-fcp-mpath' "${*}" &>/dev/null } s390-tools-2.38.0/chreipl-fcp-mpath/chreipl-fcp-mpath-is-ipl-tgt.in000066400000000000000000000032241502674226300245750ustar00rootroot00000000000000#!/bin/bash # SPDX-License-Identifier: MIT # # chreipl-fcp-mpath: use multipath information to change FCP IPL target # (C) Copyright IBM Corp. 2021 # # Uses the following system-utilities (and shell-builtins): # Those necessary for sourced library: # - chreipl-fcp-mpath-common.sh # Find out whether the device in udev event environment variable ${DEVPATH} # represents the device we want to re-IPL from. We do this by comparing # Device-Bus-ID/Target-WWPN/LUN of the individual SDEVs to the parameters set # in `/sys/firmware/reipl/fcp/`. # # Makes use of udev event environment variables: # DM_UUID # SUBSYSTEM # DEVPATH # shellcheck disable=SC2034 declare -gr debug_trace_tag=05iilt # shellcheck disable=SC1091 source '@chreiplzfcpmp-lib@' || exit 127 firmware_get_ipl_information || exit 1 if [[ "${DM_UUID}" == mpath-* ]]; then # Assume Multipath Device Mapper Device; # e.g.: DEVPATH = /devices/virtual/block/dm-0 declare sdev # depends on `nullglob` from `chreipl-fcp-mpath-common.sh` for sdev in /sys/"${DEVPATH}"/slaves/sd*/device; do sdev_get_fcp_addressing "${sdev}" || continue [ "${SDEV_LUN}" = "${IPL_LUN}" ] || continue [ "${SDEV_WWPN}" = "${IPL_WWPN}" ] || continue [ "${SDEV_BUSID}" = "${IPL_BUSID}" ] || continue exit 0 done unset sdev elif [ "${SUBSYSTEM}" = block ]; then # Assume SCSI Disk; # e.g.: DEVPATH = /devices/css0/0.0.0014/0.0.1700/host0/rport-0:0-0/target0:0:0/0:0:0:1074806808/block/sds sdev_get_fcp_addressing /sys/"${DEVPATH}"/device || exit 2 [ "${SDEV_LUN}" = "${IPL_LUN}" ] || exit 3 [ "${SDEV_WWPN}" = "${IPL_WWPN}" ] || exit 4 [ "${SDEV_BUSID}" = "${IPL_BUSID}" ] || exit 5 exit 0 fi exit 6 s390-tools-2.38.0/chreipl-fcp-mpath/chreipl-fcp-mpath-is-ipl-vol.in000066400000000000000000000047351502674226300246070ustar00rootroot00000000000000#!/bin/bash # SPDX-License-Identifier: MIT # # chreipl-fcp-mpath: use multipath information to change FCP IPL target # (C) Copyright IBM Corp. 2021 # # Uses the following system-utilities (and shell-builtins): # Those necessary for sourced library: # - chreipl-fcp-mpath-common.sh # Find out whether the device in environment variable ${DEVPATH} represents the # _volume_ that we IPL'ed from. We do this by comparing its WWID to the one # recorded in `@chreiplzfcpmp-id-file@`. # # Makes use of udev event environment variables: # DM_UUID # SUBSYSTEM # DEVPATH # shellcheck disable=SC2034 declare -gr debug_trace_tag=11iilv # shellcheck disable=SC1091 source '@chreiplzfcpmp-lib@' || exit 127 function id_file_read_ipl_information() { local -a records declare -g REC_WWID="" REC_BUSID="" REC_WWPN="" REC_LUN="" # lock file before reading ID, so we don't see any intermediate state id_file_lock_shared_no_create || return 1 { readarray -d "" -t -u "${ID_FILE_LOCK}" records; } 2>/dev/null \ || return 2 if '@DEBUG@'; then declare -p records 1>&2; fi id_file_unlock_shared_no_create [ "${#records[@]}" = "4" ] || return 3 # check that none of the array fields contains whitespace only [ "${records[0]/#*([[:space:]])}" != "" ] || return 4 [ "${records[1]/#*([[:space:]])}" != "" ] || return 5 [ "${records[2]/#*([[:space:]])}" != "" ] || return 6 [ "${records[3]/#*([[:space:]])}" != "" ] || return 7 REC_WWID="${records[0]}" REC_BUSID="${records[1]}" REC_WWPN="${records[2]}" REC_LUN="${records[3]}" return 0 } id_file_read_ipl_information || exit 1 if [[ "${DM_UUID}" == mpath-* ]]; then # Assume Multipath Device Mapper Device; # e.g.: DEVPATH = /devices/virtual/block/dm-0 declare sdev found=false for sdev in /sys/"${DEVPATH}"/slaves/sd*/device; do if sdev_get_wwid "${sdev}"; then found=true break fi done unset sdev "${found}" || exit 2 elif [ "${SUBSYSTEM}" = block ]; then # Assume SCSI Disk; # e.g.: DEVPATH = /devices/css0/0.0.0014/0.0.1700/host0/rport-0:0-0/target0:0:0/0:0:0:1074806808/block/sds sdev_get_wwid /sys/"${DEVPATH}"/device || exit 3 fi # set by `sdev_get_wwid` and `id_file_read_ipl_information` [ "${SDEV_WWID}" = "${REC_WWID}" ] || exit 4 firmware_get_ipl_information || exit 5 # set by `firmware_get_ipl_information` and `id_file_read_ipl_information` [ "${IPL_BUSID}" = "${REC_BUSID}" ] || exit 6 [ "${IPL_WWPN}" = "${REC_WWPN}" ] || exit 7 [ "${IPL_LUN}" = "${REC_LUN}" ] || exit 8 exit 0 s390-tools-2.38.0/chreipl-fcp-mpath/chreipl-fcp-mpath-is-reipl-zfcp.in000066400000000000000000000012761502674226300252750ustar00rootroot00000000000000#!/bin/bash # SPDX-License-Identifier: MIT # # chreipl-fcp-mpath: use multipath information to change FCP IPL target # (C) Copyright IBM Corp. 2021 # # Uses the following system-utilities (and shell-builtins): # Those necessary for sourced library: # - chreipl-fcp-mpath-common.sh # Find out whether ReIPL is gonna happen from a SCSI volume attached via zFCP # shellcheck disable=SC2034 declare -gr debug_trace_tag=00iriz # shellcheck disable=SC1091 source '@chreiplzfcpmp-lib@' || exit 127 declare reipl_type { read -r reipl_type _ < /sys/firmware/reipl/reipl_type; } 2>/dev/null || exit 1 if '@DEBUG@'; then declare -p reipl_type 1>&2; fi [ "${reipl_type}" = "fcp" ] || exit 2 exit 0 s390-tools-2.38.0/chreipl-fcp-mpath/chreipl-fcp-mpath-record-volume-identifier.in000066400000000000000000000037431502674226300275150ustar00rootroot00000000000000#!/bin/bash # SPDX-License-Identifier: MIT # # chreipl-fcp-mpath: use multipath information to change FCP IPL target # (C) Copyright IBM Corp. 2021 # # Uses the following system-utilities (and shell-builtins): # Those necessary for sourced library: # - chreipl-fcp-mpath-common.sh # GNU coreutils: # - truncate # util-linux: # - hexdump # Record the identification of the volume we want to re-IPL from # # Makes use of udev event environment variables: # DM_UUID # SUBSYSTEM # DEVPATH # shellcheck disable=SC2034 declare -gr debug_trace_tag=10rvid # shellcheck disable=SC1091 source '@chreiplzfcpmp-lib@' || exit 127 function id_file_record_ipl_information() { local sdev_wwid="${1}" ipl_busid="${2}" ipl_wwpn="${3}" ipl_lun="${4}" # lock file before writing ID, so noone sees any intermediate state id_file_lock_exclusive_create || return 1 # reset ID without removing the file (necessary for the locking to work # properly, since the FD we use for locking is on this file/inode) truncate --no-create --size=0 "${ID_FILE}" || return 3 echo -ne "${sdev_wwid}\x00${ipl_busid}\x00${ipl_wwpn}\x00${ipl_lun}\x00" \ >>"${ID_FILE}" || return 4 if '@DEBUG@'; then hexdump -vC "${ID_FILE}" 1>&2; fi id_file_unlock_exclusive_create return 0 } if [[ "${DM_UUID}" == mpath-* ]]; then # Assume Multipath Device Mapper Device; # e.g.: DEVPATH = /devices/virtual/block/dm-0 declare sdev for sdev in /sys/"${DEVPATH}"/slaves/sd*/device; do if sdev_get_wwid "${sdev}"; then break fi done unset sdev elif [ "${SUBSYSTEM}" = block ]; then # Assume SCSI Disk; # e.g.: DEVPATH = /devices/css0/0.0.0014/0.0.1700/host0/rport-0:0-0/target0:0:0/0:0:0:1074806808/block/sds sdev_get_wwid /sys/"${DEVPATH}"/device fi # shellcheck disable=SC2153 [ "${SDEV_WWID}" != "" ] || exit 1 firmware_get_ipl_information || exit 2 # shellcheck disable=SC2153 id_file_record_ipl_information \ "${SDEV_WWID}" "${IPL_BUSID}" "${IPL_WWPN}" "${IPL_LUN}" \ || exit 3 exit 0 s390-tools-2.38.0/chreipl-fcp-mpath/chreipl-fcp-mpath-try-change-ipl-path.in000066400000000000000000000127421502674226300263660ustar00rootroot00000000000000#!/bin/bash # SPDX-License-Identifier: MIT # # chreipl-fcp-mpath: use multipath information to change FCP IPL target # (C) Copyright IBM Corp. 2021 # # Uses the following system-utilities (and shell-builtins): # Those necessary for sourced library: # - chreipl-fcp-mpath-common.sh # GNU coreutils: # - truncate # util-linux: # - hexdump # Try to change the current re-IPL target to a dfferent, operational path to # the same volume. # # Makes use of udev event environment variables: # DM_UUID # SUBSYSTEM # DEVPATH # CHREIPL_FCP_MPATH_IS_TGT # shellcheck disable=SC2034 declare -gr debug_trace_tag=15tcip # shellcheck disable=SC1091 source '@chreiplzfcpmp-lib@' || exit 127 function apply_ipl_information() { local sdev_wwid="${1}" local sdev_busid="${2}" sdev_wwpn="${3}" sdev_lun="${4}" local ipl_type="${5}" ipl_busid="${6}" ipl_wwpn="${7}" ipl_lun="${8}" local -a records local try_update_id_file=true [ "${ipl_type}" = "fcp" ] || return 1 [[ "${sdev_busid}" =~ ^[[:xdigit:]]{1,3}\.[[:xdigit:]]\.[[:xdigit:]]{1,4}$ ]] \ || return 2 [[ "${sdev_wwpn}" =~ ^0x[[:xdigit:]]{16}$ ]] || return 3 [[ "${sdev_lun}" =~ ^0x[[:xdigit:]]{16}$ ]] || return 4 # After updating the firmware re-IPL information below we also try to # update the information stored in the ID file (necessary, so it # contains the correct Device-Bus-ID/WWPN/LUN after the update). For # the update of the ID file we try to grab an exclusive lock, so there # are no overlapping reads/writes. # # In case we can't get the lock because the ID file is missing, but we # have a direct TGT match, we may still try to change the re-IPL # information, but skip the ID file update. # # "direct match" means, the event subject is either the SDEV that is # currently set as re-IPL target, or it is the dm-multipath device that # currently contains the re-IPL target. if ! id_file_lock_exclusive_no_create; then # rc == 1 --> could not read ${ID_FILE} [ "${PIPESTATUS[0]}" -eq 1 ] || return 5 # if true, we are dealing with a direct TGT match [ "${CHREIPL_FCP_MPATH_IS_TGT}" = "true" ] || return 6 try_update_id_file=false fi # If we have a direct match (see in the comment above), we know # that we have a path to the current re-IPL volume - no matter of the # WWID. Otherwise, we got here by comparing the WWID of the event # subject with the one recorded in the ID file; in this case we try to # make sure the information is still up-to-date. if [ "${CHREIPL_FCP_MPATH_IS_TGT}" != "true" ]; then # last bail to make sure we don't overwrite user choices.. # # XXX: this will *NOT* prevent the race completely, but at least # make it less likely { readarray -d "" -t -u "${ID_FILE_LOCK}" records; } 2>/dev/null \ || return 7 if '@DEBUG@'; then declare -p records 1>&2; fi [ "${#records[@]}" = "4" ] || return 8 [ "${records[0]}" = "${sdev_wwid}" ] || return 9 [ "${records[1]}" = "${ipl_busid}" ] || return 10 [ "${records[2]}" = "${ipl_wwpn}" ] || return 11 [ "${records[3]}" = "${ipl_lun}" ] || return 12 fi # Take lock so we don't see any intermediate state from other helpers # running in parallel firmware_lock_exclusive || return 13 if ! { echo "${sdev_busid}" >| /sys/firmware/reipl/fcp/device \ && echo "${sdev_wwpn}" >| /sys/firmware/reipl/fcp/wwpn \ && echo "${sdev_lun}" >| /sys/firmware/reipl/fcp/lun; }; then log_alert "Changing the re-IPL device failed. The current re-IPL settings might be inconsistent. Check and correct the settings (see the README.md of chreipl-fcp-mpath) to make sure that the current re-IPL device is valid." return 14 fi firmware_unlock_exclusive if [ "${sdev_busid}" != "${ipl_busid}" ] \ || [ "${sdev_wwpn}" != "${ipl_wwpn}" ] \ || [ "${sdev_lun}" != "${ipl_lun}" ]; then log_note "Changed re-IPL path to: ${sdev_busid}:${sdev_wwpn}:${sdev_lun}." fi # Try to update the information in the ID file if we have gotten the # lock for it. if ${try_update_id_file}; then # reset ID without removing the file truncate --no-create --size=0 "${ID_FILE}" || return 15 echo -ne "${sdev_wwid}\x00${sdev_busid}\x00${sdev_wwpn}\x00${sdev_lun}\x00" \ >>"${ID_FILE}" || return 16 id_file_unlock_exclusive_no_create if '@DEBUG@'; then hexdump -vC "${ID_FILE}" 1>&2; fi fi return 0 } declare -g SDEV="" if [[ "${DM_UUID}" == mpath-* ]]; then # Assume Multipath Device Mapper Device; # e.g.: DEVPATH = /devices/virtual/block/dm-0 for sdev in /sys/"${DEVPATH}"/slaves/sd*/device; do if sdev_test_path_state "${sdev}"; then SDEV="${sdev}" break fi done # No path of the multipath-device that represents the IPL volume is # online. if [ "${SDEV}" = "" ]; then log_crit "The re-IPL device cannot be changed because no operational path to the re-IPL volume remains. The next re-IPL might fail unless you re-attach or enable at least one valid path to the re-IPL volume." fi elif [ "${SUBSYSTEM}" = block ]; then # Assume SCSI Disk; # e.g.: DEVPATH = /devices/css0/0.0.0014/0.0.1700/host0/rport-0:0-0/target0:0:0/0:0:0:1074806808/block/sds if sdev_test_path_state /sys/"${DEVPATH}"/device; then SDEV=/sys/"${DEVPATH}"/device fi fi [ "${SDEV}" != "" ] || exit 0 sdev_get_wwid "${SDEV}" || exit 0 sdev_get_fcp_addressing "${SDEV}" || exit 0 firmware_get_ipl_information || exit 0 # shellcheck disable=SC2153 apply_ipl_information \ "${SDEV_WWID}" "${SDEV_BUSID}" "${SDEV_WWPN}" "${SDEV_LUN}" \ "${IPL_TYPE}" "${IPL_BUSID}" "${IPL_WWPN}" "${IPL_LUN}" || exit 0 exit 0 s390-tools-2.38.0/chreipl-fcp-mpath/chreipl-fcp-mpath.7000066400000000000000000000143321502674226300223500ustar00rootroot00000000000000.\" Automatically generated by Pandoc 3.1.11.1 .\" .TH "CHREIPL\-FCP\-MPATH" "7" "2025\-03\-12" "s390-tools 2.37.0-build-20250312" "Administrator Manual" .SH NAME chreipl\-fcp\-mpath \- use multipath information for re\-IPL path failover on a running Linux instance .SH DESCRIPTION The IPL process of Linux on Z or LinuxONE from an FCP\-attached SCSI volume uses exactly one path to the volume. If this path is unavailable, the IPL fails. .PP The \f[B]chreipl\-fcp\-mpath\f[R] toolset monitors \f[B]udev\f[R] events about paths to the re\-IPL volume. If the currently configured re\-IPL path becomes unavailable, the toolset checks for operational paths to the same volume. If available, it reconfigures the re\-IPL settings to use an operational path. .PP Thus, re\-IPL from an FCP\-attached SCSI volume can be successful despite path failures on a running Linux instance if at least one path to the re\-IPL volume remains operational. .PP \f[B]Chreipl\-fcp\-mpath\f[R] requires \f[B]udev\f[R], \f[B]multipathd\f[R] and \f[B]dm\-multipath\f[R]. Once installed, the toolset runs automatically and autonomously. No user intervention is possible or required. .PP Other than installing the toolset, there is no user interface for \f[B]chreipl\-fcp\-mpath\f[R]. .SS Requirements The \f[B]chreipl\-fcp\-mpath\f[R] tool has the following requirements on the Linux instance that is being monitored: .IP \[bu] 2 The Linux instance must have started successfully, during IPL. .IP \[bu] 2 The running Linux instance must use \f[B]dm\-multipath\f[R] and \f[B]multipathd\f[R] for the configured re\-IPL volume \- a volume that contains a zipl boot record and has one of its paths used in the re\-IPL configuration. .IP \[bu] 2 \f[B]udev\f[R] must run. .IP \[bu] 2 The toolset must observe at least one event about the configured re\-IPL path. Examples for such events are: the SCSI disk comes online, or a path of the corresponding multipath device goes down or comes back online. .RS 2 .IP \[bu] 2 The WWID of the re\-IPL volume must not change while the Linux instance is running. .RE .IP \[bu] 2 When the configured re\-IPL path becomes unavailable while the Linux instance is running, at least one operational path to the re\-IPL volume must be available, or must become available. If no such path is available when the Linux instance is rebooted, the re\-IPL path is not changed. .IP \[bu] 2 The tool assumes that any manually reconfigured re\-IPL device is valid and operational. .RS 2 .PP The tool treats a newly configured re\-IPL device like the initially configured re\-IPL device. In particular, if the newly configured re\-IPL device fulfills the requirements of the tool, re\-IPL path failover takes place if the configured re\-IPL path becomes unavailable. .RE .SS Caution with Manual Changes to the Configured re\-IPL Target \f[B]chreipl\-fcp\-mpath\f[R] is designed to accept operator\-inititated changes of the re\-IPL device. However, concurrent changes by the operator and tool driven changes can result in the operator change being overwritten. .PP To avoid this problem, change the re\-IPL device only during steady\-state operations, when no path events happen. Alternatively, make sure that no events are processed while you change the device. See EXAMPLES for one way to suspend event processing. .SH MESSAGES During monitoring and event processing, \f[B]chreipl\-fcp\-mpath\f[R] writes messages to the syslog. .PP When the configured re\-IPL path is changed to a different path to the same volume (priority \f[I]daemon.notice\f[R]): .RS .PP Changed re\-IPL path to: ::. .RE .PP When a path event indicates that the last available path has become non\-operational (priority \f[I]daemon.alert\f[R]): .RS .PP The re\-IPL device cannot be changed because no operational path to the re\-IPL volume remains. The next re\-IPL might fail unless you re\-attach or enable at least one valid path to the re\-IPL volume. .RE .PP When changing the configured re\-IPL device failed because of an error with the used Linux kernel interface (priority \f[I]daemon.crit\f[R]): .RS .PP Changing the re\-IPL device failed. The current re\-IPL settings might be inconsistent. Check and correct the settings (see the README.md of chreipl\-fcp\-mpath) to make sure that the current re\-IPL device is valid. .RE .PP A failure to change the re\-IPL device can indicate an inconsistent setting that cannot be corrected automatically by \f[B]chreipl\-fcp\-mpath\f[R]. As a result, the next re\-IPL might fail or might not use the intended re\-IPL device. .PP You can use the following tools to check and correct the current settings: .IP \[bu] 2 \f[B]lsreipl\f[R] to confirm that the intended re\-IPL device is configured; .IP \[bu] 2 \f[B]chreipl\f[R] to change the re\-IPL device; .IP \[bu] 2 \f[B]lszfcp\f[R] to inspect the state of available paths to the re\-IPL device. .SH EXAMPLES .SS Manual Changes to the Configured re\-IPL Device As outlined in DESCRIPTION, be cautious when manually changing the configured re\-IPL device. Assure that your reconfiguration actions do not collide with concurrent automatic event processing by \f[B]chreipl\-fcp\-mpath\f[R]. You can avoid such collisions, by stopping event processing, making your changes, and then re\-enabling event processing. You need \f[I]root\f[R] privileges for running the commands in the following example: .IP .EX \[ti] # udevadm settle \[ti] # udevadm control \-\-stop\-exec\-queue \[ti] # chreipl ... \[ti] # udevadm control \-\-start\-exec\-queue .EE .SS Listing messages with journalctl If your Linux instance includes \f[B]journalctl\f[R], use the following command to list all messages that are issued by \f[B]chreipl\-fcp\-mpath\f[R]: .IP .EX \[ti] # journalctl \-t chreipl\-fcp\-mpath .EE .PP To list only messages that were issued since the last IPL, use this command: .IP .EX \[ti] # journalctl \-t chreipl\-fcp\-mpath \-b .EE .SH REPORTING BUGS Use the \f[B]Issues\f[R] functionality on GitHub to report any bugs in \f[B]chreipl\-fcp\-mpath\f[R]: \c .UR https://github.com/ibm-s390-linux/s390-tools/issues s390\-tools Issues .UE \c \&. .SH SEE ALSO \f[B]chreipl\f[R](8), \f[B]dracut\f[R](8), \f[B]journalctl\f[R](1), \f[B]lsreipl(8)\f[R], \f[B]lszfcp\f[R](8), \f[B]multipath\f[R](8), \f[B]multipathd\f[R](8), \f[B]udev\f[R](7), \f[B]udevadm\f[R](8), \f[B]zipl\f[R](8) s390-tools-2.38.0/chreipl-fcp-mpath/chreipl-fcp-mpath.mak000066400000000000000000000060501502674226300227500ustar00rootroot00000000000000# SPDX-License-Identifier: MIT # # chreipl-fcp-mpath: use multipath information to change FCP IPL target # (C) Copyright IBM Corp. 2021 # # Uses the following system-utilities (and shell-builtins): # Utilities list in GNU Make Conventions: # https://www.gnu.org/software/make/manual/make.html#Utilities-in-Makefiles # GNU coreutils: # - mktemp # If $(ENABLE_DOC) is `1`: # Pandoc: # - pandoc # GNU coreutils: # - date # ## Paths and Build Variables # # Install the configuration file for dracut, to automatically pull in the # toolset into the initial ramdisk, when built with it. HAVE_DRACUT = 0 # Build documentation; requires: Pandoc ENABLE_DOC = 0 # https://www.gnu.org/software/make/manual/make.html#Directory-Variables CHREIPLZFCPMPDIR = $(USRLIBDIR)/chreipl-fcp-mpath UDEVRUNDIR = /run/udev DEBUGOUTDIR = $(UDEVRUNDIR) INSTALL_EXEC = $(INSTALL) -g $(GROUP) -o $(OWNER) --preserve-timestamps INSTALL_DATA = $(INSTALL_EXEC) --mode=0644 # used for data exchange and synchronization across the different helpers chreiplzfcpmp-id-file = $(UDEVRUNDIR)/chreiplzfcpmp-ipl-volume-id # file used to implement mutual exclusion when accessing firmware IPL info: # - this should be something that is (practically) always available, so we # dont have to worry about fallbacks or error-handling; # - at the same time, it should not be used by anything else with flock(2) to # hold a lock for long periods. chreiplzfcpmp-fwlock-file = /sys/firmware/reipl .DELETE_ON_ERROR: # export build-time definitions to the scripts/built-components define chreiplzfcpmp-sed-buildvar-replace = tmpout=$$(mktemp -p ./ .make.tmp.XXXXXXXXXXXXXXXX) && { \ $(SED) -E \ -e 's|@DEBUG@|$(if $(filter 1,$(D)),true,false)|g' \ -e 's|@chreiplzfcpmp-id-file@|$(chreiplzfcpmp-id-file)|g' \ -e 's|@chreiplzfcpmp-fwlock-file@|$(chreiplzfcpmp-fwlock-file)|g' \ -e 's|@chreiplzfcpmp-lib@|$(CHREIPLZFCPMPDIR)/chreipl-fcp-mpath-common.sh|g' \ -e 's|@debugoutdir@|$(DEBUGOUTDIR)|g' \ -e 's|@udevdir@|$(UDEVDIR)|g' \ -e 's|@udevrulesdir@|$(UDEVRULESDIR)|g' \ $(1) > $${tmpout} \ && mv $${tmpout} $(2) \ || { rm $${tmpout}; false; } \ ; } endef .PHONY: clean-mk-temp clean: clean-mk-temp clean-mk-temp: rm -f .make.tmp.[[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]] # Definitions for generating documentation when $(ENABLE_DOC) is set to `1` PANDOCFLAGS = --fail-if-warnings ALL_PANDOCFLAGS = --preserve-tabs --tab-stop=8 --strip-comments \ --standalone --self-contained \ -M date="$(shell date +'%Y-%m-%d')" \ $(PANDOCFLAGS) $(eval $(call cmd_define_and_export, PANDOC," PANDOC ",pandoc)) %.html : ALL_PANDOCFLAGS += -t html %.html : %.md $(PANDOC) $(ALL_PANDOCFLAGS) -f gfm -o $(@) $(<) %.pdf : ALL_PANDOCFLAGS += -t latex --toc %.pdf : %.md $(PANDOC) $(ALL_PANDOCFLAGS) -f gfm -o $(@) $(<) %.7 : ALL_PANDOCFLAGS += -t man %.7 : %.md $(PANDOC) $(ALL_PANDOCFLAGS) -f gfm -o $(@) $(<) s390-tools-2.38.0/chreipl-fcp-mpath/dracut/000077500000000000000000000000001502674226300202345ustar00rootroot00000000000000s390-tools-2.38.0/chreipl-fcp-mpath/dracut/dracut.conf.d/000077500000000000000000000000001502674226300226645ustar00rootroot00000000000000s390-tools-2.38.0/chreipl-fcp-mpath/dracut/dracut.conf.d/70-chreipl-fcp-mpath.conf.in000066400000000000000000000012421502674226300276660ustar00rootroot00000000000000# SPDX-License-Identifier: MIT # # chreipl-fcp-mpath: use multipath information to change FCP IPL target # (C) Copyright IBM Corp. 2021 add_dracutmodules+=" bash multipath udev-rules " install_items+=" @udevrulesdir@/70-chreipl-fcp-mpath.rules " install_items+=" @chreiplzfcpmp-lib@ " install_items+=" @udevdir@/chreipl-fcp-mpath-is-ipl-tgt " install_items+=" @udevdir@/chreipl-fcp-mpath-is-ipl-vol " install_items+=" @udevdir@/chreipl-fcp-mpath-is-reipl-zfcp " install_items+=" @udevdir@/chreipl-fcp-mpath-record-volume-identifier " install_items+=" @udevdir@/chreipl-fcp-mpath-try-change-ipl-path " install_items+=" flock hexdump logger mktemp readlink sync truncate " s390-tools-2.38.0/chreipl-fcp-mpath/udev/000077500000000000000000000000001502674226300177155ustar00rootroot00000000000000s390-tools-2.38.0/chreipl-fcp-mpath/udev/rules.d/000077500000000000000000000000001502674226300212715ustar00rootroot00000000000000s390-tools-2.38.0/chreipl-fcp-mpath/udev/rules.d/70-chreipl-fcp-mpath.rules000066400000000000000000000102151502674226300260730ustar00rootroot00000000000000# SPDX-License-Identifier: MIT # # chreipl-fcp-mpath: use multipath information to change FCP IPL target # (C) Copyright IBM Corp. 2021 # Did the event affect a multipath or scsi disk device? ACTION=="change", KERNEL=="dm-[0-9]*", SUBSYSTEM=="block", \ ENV{DM_UUID}=="mpath-*", ENV{DM_ACTION}=="PATH_FAILED", \ GOTO="chreipl_fcp_mpath_path_change" ACTION=="change", KERNEL=="dm-[0-9]*", SUBSYSTEM=="block", \ ENV{DM_UUID}=="mpath-*", ENV{DM_ACTION}=="PATH_REINSTATED", \ GOTO="chreipl_fcp_mpath_path_change" ACTION=="add", KERNEL=="sd[a-z]*", SUBSYSTEM=="block", \ GOTO="chreipl_fcp_mpath_path_change" GOTO="chreipl_fcp_mpath_end" LABEL="chreipl_fcp_mpath_path_change" # Is this system IPL'ed (IOW, are we on s390x)? And do we ReIPL via zFCP? # # udev(7): If no absolute path is given, the program is expected to live # in /usr/lib/udev; otherwise, the absolute path must be # specified. TEST!="/sys/firmware/ipl", GOTO="chreipl_fcp_mpath_end" PROGRAM!="chreipl-fcp-mpath-is-reipl-zfcp", GOTO="chreipl_fcp_mpath_end" # Consider the following scenarios. # Either: # # (A) We recognized a new SCSI Disk. This might represent: # (a) the path we want to ReIPL from; # (b) an alternative path to the volume we want to ReIPL from; # (c) a path to some unrelated volume. # # Or: # # (B) We recognized a PATH_ event for a multipath device. This might represent: # the path we want to ReIPL from: # (a) went away; # (b) came back online; # an alternative path to the volume we want to ReIPL from: # (c) went away; # (d) came back online; # (e) some unrelated multipath device saw an event. # Test whether the affected device is, or contains, the current IPL target. # # This covers scenarios: # (A) (a), # (B) (a)/(b)/(c)/(d) PROGRAM!="chreipl-fcp-mpath-is-ipl-tgt", \ ENV{CHREIPL_FCP_MPATH_IS_TGT}="false", \ GOTO="chreipl_fcp_mpath_not_direct_match" ENV{CHREIPL_FCP_MPATH_IS_TGT}="true" # Record the WWID, Device-Bus-ID, Remote WWPN, and LUN of the ReIPL target # (see `chreipl-fcp-mpath-is-ipl-vol` for usecases). This information # might change, depending on whether the machine operator changes the ReIPL # target to a different volume. # # XXX: Because the kernel doesn't generate any events upon changing of # the ReIPL target, the chreipl-fcp-mpath toolset can't take any # actions until the next path event for the new target is # generated. Following that, we assume that when the machine # operator changes the ReIPL target, the new target is reachable # and in a good state at this point in time. PROGRAM!="chreipl-fcp-mpath-record-volume-identifier", \ GOTO="chreipl_fcp_mpath_try_change_ipl_path" GOTO="chreipl_fcp_mpath_try_change_ipl_path" # If the even subject is not a direct match (not the sdev that is the current # ReIPL target, and not a mpath device that contains the current ReIPL target) LABEL="chreipl_fcp_mpath_not_direct_match" # While this sdev/mpath device doesn't directly correspond to the path # currently set as ReIPL target, it might still point to the same volume. # # For mpath devices this can happen if the original ReIPL target is completely # gone from the machine, and so there is no way we can successfully, directly # compare the ReIPL parameters to the sdevs of the mpath device. # # For cases like these we recorded the volume identifier, which we now can # compare, and so still decide whether we are addressing the correct volume. # # This covers scenarios: # (A) (b)/(c), # (B) (c)/(d)/(e) # # XXX: we recorded WWID, Device-Bus-ID, Remote WWPN, LUN of the ReIPL target at # the time; if the latter three don't match the current ReIPL setting # anymore, we have to assume that someone changed the ReIPL target # manually, and we cannot use the WWID anymore since we can't possibly # know whether that stayed the same when the change was done. PROGRAM!="chreipl-fcp-mpath-is-ipl-vol", GOTO="chreipl_fcp_mpath_end" # We are here because of scenarios: # (A) (a)/(b), # (B) (a)/(b)/(c)/(d) LABEL="chreipl_fcp_mpath_try_change_ipl_path" RUN{program}+="chreipl-fcp-mpath-try-change-ipl-path" LABEL="chreipl_fcp_mpath_end" s390-tools-2.38.0/cmsfs-fuse/000077500000000000000000000000001502674226300155225ustar00rootroot00000000000000s390-tools-2.38.0/cmsfs-fuse/Makefile000066400000000000000000000021761502674226300171700ustar00rootroot00000000000000#!/usr/bin/make -f include ../common.mak ifeq (${HAVE_FUSE},0) all: $(SKIP) HAVE_FUSE=0 install: $(SKIP) HAVE_FUSE=0 else check_dep: $(call check_dep, \ "cmsfs-fuse", \ "fuse.h", \ "fuse3-devel or libfuse3-dev", \ "HAVE_FUSE=0", \ "-DFUSE_USE_VERSION=30") all: check_dep cmsfs-fuse FUSE_CFLAGS = $(shell $(PKG_CONFIG) --silence-errors --cflags fuse3) FUSE_LDLIBS = $(shell $(PKG_CONFIG) --silence-errors --libs fuse3) ALL_CFLAGS += -DHAVE_SETXATTR $(FUSE_CFLAGS) LDLIBS += $(FUSE_LDLIBS) -lm OBJECTS = cmsfs-fuse.o dasd.o amap.o config.o CMSFS_FUSE_DIR = $(SYSCONFDIR)/cmsfs-fuse CONFIG_FILES = filetypes.conf libs = $(rootdir)/libutil/libutil.a cmsfs-fuse: $(OBJECTS) $(libs) install: all $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 cmsfs-fuse \ $(DESTDIR)$(USRBINDIR) $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 cmsfs-fuse.1 \ $(DESTDIR)$(MANDIR)/man1 $(INSTALL) -g $(GROUP) -o $(OWNER) -d $(DESTDIR)$(CMSFS_FUSE_DIR) for cnf in $(CONFIG_FILES); do \ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 etc/$$cnf $(DESTDIR)$(CMSFS_FUSE_DIR) ; \ done endif clean: rm -f cmsfs-fuse *.o .PHONY: all install clean check_dep s390-tools-2.38.0/cmsfs-fuse/amap.c000066400000000000000000000135601502674226300166110ustar00rootroot00000000000000/* * cmsfs-fuse - CMS EDF filesystem support for Linux * * Allocation map functions * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include "lib/zt_common.h" #include "cmsfs-fuse.h" #include "edf.h" #include "helper.h" /* * Hint where to look for the next free block (level 0 only). * Updated if a free block is found. If the level 0 amap bitmap * block is exhausted we still scan all amap blocks. */ struct amap_alloction_hint { /* addr of amap bitmap block to check */ off_t amap_addr; /* disk addr of the last allocated or freed block */ off_t addr; /* offset to start of the amap data block */ off_t offset; }; static struct amap_alloction_hint amap_hint; static void update_amap_hint(off_t amap_addr, off_t addr) { amap_hint.amap_addr = amap_addr; amap_hint.addr = addr; } /* * Get L1 block number from address. */ static int amap_blocknumber(off_t addr) { return addr / BYTES_PER_BLOCK; } /* * Get the block number for a specific level. */ static int amap_blocknumber_level(int level, off_t addr) { int entry = amap_blocknumber(addr); while (level-- > 1) entry /= PTRS_PER_BLOCK; return entry; } /* * Return address of to the allocation map for a block number > 0. */ static off_t get_amap_addr(int level, off_t addr, off_t ptr) { int block = amap_blocknumber_level(level, addr) % PTRS_PER_BLOCK; if (cmsfs.amap_levels == 0) return cmsfs.amap; if (level--) { ptr = get_fixed_pointer(ptr + (off_t) block * PTR_SIZE); if (!ptr) DIE("amap invalid ptr at addr: %llx\n", (unsigned long long) ptr + (off_t) block * PTR_SIZE); return get_amap_addr(level, addr, ptr); } return ptr; } /* * Mark disk address as allocated in alloc map. */ static void amap_block_set(off_t amap, int bit) { u8 entry; int rc; rc = _read(&entry, sizeof(entry), amap); BUG(rc < 0); /* already used */ BUG(entry & (1 << (7 - bit))); entry |= (1 << (7 - bit)); rc = _write(&entry, sizeof(entry), amap); BUG(rc < 0); } /* * Mark disk address as free in alloc map. Unaligned addr is tolerated. */ static void amap_block_clear(off_t addr) { off_t amap = get_amap_addr(cmsfs.amap_levels, addr, cmsfs.amap); int rc, block = amap_blocknumber(addr); off_t disk_addr = addr; unsigned int byte, bit; u8 entry; if (block > 0) addr -= (off_t) block * BYTES_PER_BLOCK; addr >>= BITS_PER_DATA_BLOCK; byte = addr / 8; bit = addr % 8; rc = _read(&entry, sizeof(entry), amap + byte); BUG(rc < 0); /* already cleared */ BUG(!(entry & (1 << (7 - bit)))); entry &= ~(1 << (7 - bit)); rc = _write(&entry, sizeof(entry), amap + byte); BUG(rc < 0); /* * If the freed addr is lower set the hint to it to ensure * the amap bitmap is packed from the start. That way we do not * need an extra check if the bitmap entry is above disk end, the * check if we overflow the total block limit is sufficient. */ if (disk_addr < amap_hint.addr) update_amap_hint(amap + byte, disk_addr); } /* * Return the first free bit in one byte. */ static inline int find_first_empty_bit(u8 entry) { u8 i; for (i = 0; i < 8; i++) if (!(entry & 1 << (7 - i))) return i; /* unreachable */ return -1; } /* * Return the number of bytes addressed by one pointer entry for the * specified level. */ static off_t bytes_per_level(int level) { off_t mult = BYTES_PER_BLOCK; if (!level) return 0; level--; while (level--) mult *= PTRS_PER_BLOCK; return mult; } static inline int get_amap_entry_bit(off_t amap) { u8 entry; int rc; rc = _read(&entry, sizeof(entry), amap); BUG(rc < 0); if (entry == 0xff) return -1; return find_first_empty_bit(entry); } static off_t __get_free_block_fast(void) { off_t addr, amap = amap_hint.amap_addr & ~DATA_BLOCK_MASK; int bit, i = amap_hint.amap_addr & DATA_BLOCK_MASK; for (; i < cmsfs.blksize; i++) { bit = get_amap_entry_bit(amap + i); if (bit == -1) continue; /* Calculate the addr for the free block we've found. */ addr = (off_t) amap_blocknumber(amap_hint.addr) * BYTES_PER_BLOCK; addr += i * 8 * cmsfs.blksize; addr += bit * cmsfs.blksize; amap_block_set(amap + i, bit); update_amap_hint(amap + i, addr); return addr; } return 0; } /* * Look for the first unallocated block and return addr of allocated block. */ static off_t __get_free_block(int level, off_t amap, off_t addr) { off_t ptr; int bit, i; if (level > 0) { for (i = 0; i < PTRS_PER_BLOCK; i++) { ptr = get_fixed_pointer(amap); if (!ptr) return 0; ptr = __get_free_block(level - 1, ptr, addr + i * bytes_per_level(level)); if (ptr) return ptr; amap += PTR_SIZE; } return 0; } for (i = 0; i < cmsfs.blksize; i++) { bit = get_amap_entry_bit(amap + i); if (bit == -1) continue; amap_block_set(amap + i, bit); /* add byte offset */ addr += i * 8 * cmsfs.blksize; /* add bit offset */ addr += bit * cmsfs.blksize; update_amap_hint(amap + i, addr); return addr; } return 0; } /* * Allocate a free block and increment label block counter. */ off_t get_free_block(void) { off_t addr = 0; if (cmsfs.used_blocks + cmsfs.reserved_blocks >= cmsfs.total_blocks) return -ENOSPC; if (amap_hint.amap_addr) addr = __get_free_block_fast(); if (!addr) addr = __get_free_block(cmsfs.amap_levels, cmsfs.amap, 0); BUG(!addr); cmsfs.used_blocks++; return addr; } /* * Allocate a zero-filled block and increment label block counter. */ off_t get_zero_block(void) { off_t addr = get_free_block(); int rc; if (addr < 0) return -ENOSPC; rc = _zero(addr, cmsfs.blksize); if (rc < 0) return rc; return addr; } /* * Free a block and decrement label block counter. */ void free_block(off_t addr) { if (addr) { amap_block_clear(addr); cmsfs.used_blocks--; } } s390-tools-2.38.0/cmsfs-fuse/cmsfs-fuse.1000066400000000000000000000160131502674226300176600ustar00rootroot00000000000000.\" Copyright IBM Corp. 2010, 2017 .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH CMSFS-FUSE 1 "February 2010" "s390-tools" .SH NAME cmsfs-fuse \- File system for z/VM CMS disks .SH SYNOPSIS .SS mounting: .TP \fBcmsfs-fuse\fP DEVICE MOUNTPOINT [OPTIONS] .SS unmounting: .TP \fBfusermount\fP -u MOUNTPOINT .SH DESCRIPTION Use the \fBcmsfs-fuse\fP command to provide read and write access to files stored on a z/VM CMS disk. The cmsfs-fuse file system translates the record-based EDF file system on the CMS disk to UNIX semantics. After mounting the CMS disk, you can use common Linux tools to access the files on the disk. You can enable automatic conversions of text files from EBCDIC to ASCII. Attention: You can inadvertently damage files and lose data when directly writing to files within the cmsfs-fuse file system. To avoid problems when writing, multiple restrictions must be observed, especially with regard to linefeeds (see section RESTRICTIONS). If you are unsure about how to safely write to a file on the cmsfs-fuse file system, copy the file to a location outside the cmsfs-fuse file system, edit the file, and then copy it back to its original location. .SH OPTIONS .SS "general options:" .TP \fB\-o\fR opt,[opt...] Fuse or mount command options. For fuse options see below, for mount options see \fBmount(8)\fP. .TP \fB\-h\fR or \fB\-\-help\fR Print usage information, then exit. .TP \fB\-v\fR or \fB\-\-version\fR Print version information, then exit. .SS "cmsfs-fuse options:" .TP \fB\-a\fR or \fB\-\-ascii\fR Interpret all files on the CMS disk as text files and convert them from EBCDIC to ASCII. .TP \fB--from\fR The codepage of the files on the CMS disk. If this option is not specified the default codepage CP1047 is used. For a list of all available codepages see iconv --list. .TP \fB--to\fR The codepage to which CMS files should be converted to. If this option is not specified the default codepage ISO-8859-1 is used. For a list of all available codepages see iconv --list. .TP \fB\-t\fR or \fB\-\-filetype\fR Interpret files on the CMS disk as text files based on the file type and convert them from EBCDIC to ASCII. The file types that are treated as text files are taken from a configuration file (see section CONFIGURATION FILES). .SS "Applicable FUSE options (version 2.8):" .TP \fB\-d\fR or \fB\-o\fR debug Enable debug output (implies \fB\-f\fR) .TP \fB\-f\fR Foreground operation .TP \fB\-o\fR allow_other Allow access by other users .TP \fB\-o\fR allow_root Allow access by root .TP \fB\-o\fR default_permissions Enable permission checking by kernel .TP .TP \fB\-o\fR max_read=N Set maximum size of read requests .TP \fB\-o\fR kernel_cache Cache files in kernel .TP \fB\-o\fR [no]auto_cache Enable caching based on modification times .TP \fB\-o\fR umask=M Set file permissions (octal) .TP \fB\-o\fR uid=N Set file owner .TP \fB\-o\fR gid=N Set file group .TP \fB\-o\fR max_write=N Set maximum size of write requests .TP \fB\-o\fR max_readahead=N Set maximum readahead .TP \fB\-o\fR async_read Perform reads asynchronously (default) .TP \fB\-o\fR sync_read Perform reads synchronously .TP \fB\-o big_writes\fR Enable write operations with more than 4 KB .SH EXTENDED ATTRIBUTES Use the following extended attributes to handle the CMS characteristics of a file: \fBuser.record_format\fR: The format of a file. Allowed values are F for fixed record length files and V for variable record length files. This attribute can be set only if the file is empty. \fBuser.record_lrecl\fR: The record length of a file. This attribute can be set only for a fixed record length file and if the file is empty. A valid record length is an integer in the range 1-65535. \fBuser.file_mode\fR: The file mode of a file which is interpreted by CMS. The file mode consists of a mode letter from A-Z and mode number from 0-6. New files are created by default as variable files with file mode A1. .SH RESTRICTIONS \fBrename\fR and \fBcreat\fR: Uppercase file names are enforced. \fBtruncate\fR: Only shrinking of a file is supported. For fixed length record files, the new file size must be a multiple of the record length. \fBunlink\fR: Creating a file with the name of a previously unlinked file which is still in use is not supported and will fail with -ENOENT. \fBwrite\fR: Writes are supported only at the end of the file. A write on a fixed length record file always writes a multiple of the record length. If additional bytes are added, the bytes are filled with zero in binary mode or with spaces in ASCII mode. Sparse files are not supported. If the cp tool is used to write files to a CMS disk the option "--sparse=never" must be specified. If ASCII translation is enabled for a file a linefeed character determines the end of a record. The following restrictions must be observed for writing files in ASCII mode: For fixed record length files a linefeed must occur exactly after a record of the length specified in the fixed record length. For variable record length files a linefeed must occur after the maximum record length is reached or earlier. If a record of a variable record length file consists only of a linefeed character cmsfs-fuse adds a space to this record since empty records are not supported by the CMS file system. .SH CONFIGURATION FILES cmsfs-fuse uses a configuration file for automatic translation based on the file type. Upon startup, cmsfs-fuse evaluates the file .cmsfs-fuse/filetypes.conf in the user's home directory. If the file does not exist cmsfs-fuse evaluates the file /etc/cmsfs-fuse/filetypes.conf. The filetypes.conf file contains the CMS file types that are automatically translated to ASCII if cmsfs-fuse is started with the -t option. The syntax of the configuration file is one file type per line. Lines that start with a # followed by a space are treated as comments and are ignored. The file type is 8 characters long and must consist of valid CMS file name characters only. The default file types in the configuration file were taken from the z/VM TCPIP.DATA file (z/VM version 5.4.0). .SH EXAMPLES To mount the CMS disk with the name dasde enter: .br # cmsfs-fuse /dev/dasde /mnt .br To mount the CMS disk with the name dasde and enable automatic translation of known text files enter: .br # cmsfs-fuse -t /dev/dasde /mnt To mount the CMS disk with the name dasde and enable automatic translation of all files to UTF-8 enter: .br # cmsfs-fuse --to=UTF-8 -a /dev/dasde /mnt To unmount the CMS disk mounted on /mnt enter: .br # fusermount -u /mnt To show the record format of file PROFILE.EXEC assuming the CMS disk was mounted on /mnt: # getfattr -n user.record_format /mnt/PROFILE.EXEC The following example assumes that an empty, fixed record format file, PROFILE.EXEC, can be accessed on a CMS disk that has been mounted on /mnt. To set the record length of PROFILE.EXEC to 80 bytes: # setfattr -n user.record_lrecl -v 80 /mnt/PROFILE.EXEC .SH SEE ALSO attr (5), getfattr (1), setfattr(1), iconv(1) and Linux on System z: Device Drivers, Features and Commands s390-tools-2.38.0/cmsfs-fuse/cmsfs-fuse.c000066400000000000000000003165641502674226300177600ustar00rootroot00000000000000/* * cmsfs-fuse - CMS EDF filesystem support for Linux * * Main function * * Copyright IBM Corp. 2010, 2018 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #define FUSE_USE_VERSION 30 #include #include #include #include #include #include #include #include #include #ifdef HAVE_SETXATTR #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/util_base.h" #include "lib/util_libc.h" #include "lib/util_list.h" #include "lib/zt_common.h" #include "cmsfs-fuse.h" #include "ebcdic.h" #include "edf.h" #include "helper.h" struct cmsfs cmsfs; static struct util_list open_file_list; static struct util_list text_type_list; FILE *logfile; #define FSNAME_MAX_LEN 200 #define MAX_FNAME 18 #define CMSFS_OPT(t, p, v) { t, offsetof(struct cmsfs, p), v } enum { KEY_HELP, KEY_VERSION, }; static const struct fuse_opt cmsfs_opts[] = { CMSFS_OPT("-a", mode, TEXT_MODE), CMSFS_OPT("--ascii", mode, TEXT_MODE), CMSFS_OPT("-t", mode, TYPE_MODE), CMSFS_OPT("--filetype", mode, TYPE_MODE), CMSFS_OPT("--from=%s", codepage_from, 0), CMSFS_OPT("--to=%s", codepage_to, 0), FUSE_OPT_KEY("-h", KEY_HELP), FUSE_OPT_KEY("--help", KEY_HELP), FUSE_OPT_KEY("-v", KEY_VERSION), FUSE_OPT_KEY("--version", KEY_VERSION), FUSE_OPT_END }; static void usage(const char *progname) { fprintf(stdout, "Usage: %s DEVICE MOUNTPOINT [OPTIONS]\n" "\n" "Use the cmsfs-fuse command to read and write files stored on a z/VM CMS disk.\n" "\n" "General options:\n" " -o opt,[opt...] Mount options\n" " -h --help Print help, then exit\n" " -v --version Print version, then exit\n" " -t --filetype ASCII translation based on file type\n" " -a --ascii Force ascii translation\n" " --from= Codepage used on the CMS disk\n" " --to= Codepage used for conversion to Linux\n" "\n", progname); } static char CODEPAGE_EDF[] = "CP1047"; static char CODEPAGE_LINUX[] = "ISO-8859-1"; #define READDIR_FILE_ENTRY -1 #define READDIR_END_OF_DIR -2 #define READDIR_DIR_ENTRY -3 #define READDIR_MAP_ENTRY -4 #define LINEFEED_OFFSET ((struct record *) -1) #define LINEFEED_ASCII 0xa #define LINEFEED_EBCDIC 0x25 #define LINEFEED_NOT_FOUND -1 #define FILLER_EBCDIC 0x40 #define FILLER_ASCII 0x20 #define RSS_HEADER_STARTED 0x1 #define RSS_HEADER_COMPLETE 0x2 #define RSS_DATA_BLOCK_STARTED 0x4 #define RSS_DATA_BLOCK_EXT 0x8 #define RWS_HEADER_STARTED 0x1 #define RWS_HEADER_COMPLETE 0x2 #define RWS_RECORD_INCOMPLETE 0x4 #define RWS_RECORD_COMPLETE 0x8 #define BWS_BLOCK_NOT_INIT 0x0 #define BWS_BLOCK_NEW 0x1 #define BWS_BLOCK_USED 0x2 #define WCACHE_MAX (MAX_RECORD_LEN + 1) struct block { off_t disk_addr; unsigned int disp; int hi_record_nr; }; struct record_ext { /* start addr of the extension */ off_t disk_start; /* length of extension in this disk block */ int len; /* null block start flag */ int null_block_started; /* corresponding disk block number */ int block_nr; struct record_ext *prev; struct record_ext *next; }; struct record { /* length of the complete record */ unsigned int total_len; /* offset of first record block on the disk */ off_t disk_start; /* bytes in first record block */ int first_block_len; /* logical offset, dependent on line feed mode */ off_t file_start; /* null block start flag */ int null_block_started; /* spanned record extension */ struct record_ext *ext; /* corresponding disk block number */ int block_nr; }; struct file; struct file_operations { int (*cache_data) (struct file *f, off_t addr, int level, int *block, unsigned int *disp, int *record, off_t *total); int (*write_data) (struct file *f, const char *buf, int len, size_t size, int rlen); int (*delete_pointers) (struct file *f, int level, off_t addr); int (*write_pointers) (struct file *f, int level, off_t dst, int offset); }; static struct file_operations fops_fixed; static struct file_operations fops_variable; struct io_operations { int (*read) (void *buf, size_t size, off_t addr); int (*write) (const void *buf, size_t size, off_t addr); }; static struct io_operations io_ops; struct write_state { int block_state; /* number of free bytes in the current block */ int block_free; int var_record_state; /* remaining record bytes for a write request */ int var_record_len; /* only used for var records hi_record_nr by now */ int var_records_written; }; /* * File object for operations that follow open */ struct file { /* pointer to the fst entry */ struct fst_entry *fst; /* fst address on disk */ off_t fst_addr; /* translate mode enabled */ int translate; /* linefeed mode enabled */ int linefeed; /* list of records */ struct record *rlist; /* record scan state machine flag */ int record_scan_state; /* next record for sequential reads */ int next_record_hint; /* counter for null bytes to detect block start */ int null_ctr; /* list of disk blocks */ struct block *blist; /* disk address of next byte to write */ off_t write_ptr; /* the filesize while the file is opened */ off_t session_size; /* number of null blocks for fixed files */ int nr_null_blocks; /* number of written padding bytes for a fixed file */ int pad_bytes; /* old levels value, needed to rewrite pointers */ int old_levels; /* state information needed for a write request */ struct write_state *wstate; /* path name for open and unlink */ char path[MAX_FNAME + 1]; /* counter for pseudo null length records */ int null_records; /* write cache for text mode */ char *wcache; /* buffer for iconv */ char *iconv_buf; /* used bytes in write cache */ int wcache_used; /* committed written bytes to FUSE */ int wcache_commited; /* dirty flag for file meta data */ int ptr_dirty; /* fops pointers */ struct file_operations *fops; /* pointers per block constant */ int ptr_per_block; /* open list head */ struct util_list_node list; /* usage counter for all openers */ int use_count; /* usage counter for all writers */ int write_count; /* unlink flag */ int unlinked; }; struct xattr { char name[20]; size_t size; }; /* * Record format: 'F' (fixed) or 'V' (variable), 1 byte * Record lrecl: 0-65535, 5 bytes * Record mode: [A-Z][0-6], 2 bytes */ static struct xattr xattr_format = { .name = "user.record_format", .size = 1 }; static struct xattr xattr_lrecl = { .name = "user.record_lrecl", .size = 5 }; static struct xattr xattr_mode = { .name = "user.file_mode", .size = 2 }; #define SHOW_UNLINKED 0 #define HIDE_UNLINKED 1 #define WALK_FLAG_LOOKUP 0x1 #define WALK_FLAG_READDIR 0x2 #define WALK_FLAG_LOCATE_EMPTY 0x4 #define WALK_FLAG_CACHE_DBLOCKS 0x8 struct walk_file { int flag; char *name; char *type; void *buf; off_t addr; fuse_fill_dir_t filler; off_t *dlist; int dlist_used; }; /* * Prototypes */ static struct file *create_file_object(struct fst_entry *fst, int *rc); static void destroy_file_object(struct file *f); static unsigned long dec_to_hex(unsigned long long num) { unsigned long res; asm volatile("cvb %0,%1" : "=d" (res) : "Q" (num)); return res & 0xffffffff; } static unsigned int hex_to_dec(unsigned int num) { unsigned long long res; asm volatile("cvd %1,%0" : "=Q" (res) : "d" (num)); return res & 0xffffffff; } static void setup_iconv(iconv_t *conv, const char *from, const char *to) { *conv = iconv_open(to, from); if (*conv == ((iconv_t) -1)) DIE("Could not initialize conversion table %s->%s.\n", from, to); } static inline struct file *get_fobj(struct fuse_file_info *fi) { return (struct file *)(unsigned long) fi->fh; } static int access_ok(size_t size, off_t addr) { if (((addr + (off_t) size - 1) & ~DATA_BLOCK_MASK) > (addr & ~DATA_BLOCK_MASK)) DIE("crossing disk blocks, addr: %x size: %d\n", (int) addr, (int) size); if ((unsigned long long) addr < (unsigned long long) cmsfs.label || addr > cmsfs.size) return 0; return 1; } static int read_memory(void *buf, size_t size, off_t addr) { memcpy(buf, cmsfs.map + addr, size); return 0; } static int read_syscall(void *buf, size_t size, off_t addr) { int rc; rc = pread(cmsfs.fd, buf, size, addr); if (rc < 0) perror("pread failed"); return rc; } int _read(void *buf, size_t size, off_t addr) { if (!access_ok(size, addr)) return -EIO; return io_ops.read(buf, size, addr); } static int write_syscall(const void *buf, size_t size, off_t addr) { char *zbuf; int rc; if (buf == NULL) { zbuf = malloc(size); if (zbuf == NULL) return -ENOMEM; memset(zbuf, 0, size); rc = pwrite(cmsfs.fd, zbuf, size, addr); free(zbuf); } else rc = pwrite(cmsfs.fd, buf, size, addr); if (rc < 0) perror("pwrite failed"); return rc; } static int write_memory(const void *buf, size_t size, off_t addr) { if (buf == NULL) memset(cmsfs.map + addr, 0, size); else memcpy(cmsfs.map + addr, buf, size); return 0; } int _write(const void *buf, size_t size, off_t addr) { if (!access_ok(size, addr)) return -EIO; return io_ops.write(buf, size, addr); } int _zero(off_t addr, size_t size) { return _write(NULL, size, addr); } static off_t get_filled_block(void) { off_t addr = get_free_block(); if (addr < 0) return -ENOSPC; memset(cmsfs.map + addr, FILLER_EBCDIC, cmsfs.blksize); return addr; } static int get_fop(off_t addr) { struct fst_entry fst; int rc; rc = _read(&fst, sizeof(fst), addr); BUG(rc < 0); return ABS(fst.fop); } static int get_levels(off_t addr) { struct fst_entry fst; int rc; rc = _read(&fst, sizeof(fst), addr); BUG(rc < 0); return fst.levels; } static int get_files_count(off_t addr) { struct fst_entry fst; int rc; rc = _read(&fst, sizeof(fst), addr); BUG(rc < 0); /* ignore director and allocmap entries */ return fst.nr_records - 2; } static int get_order(int shift) { int count = 0; while (!(shift & 0x1)) { shift >>= 1; count++; } return count; } /* * Read pointer from fixed size pointer block and return * absolute address on disk. */ off_t get_fixed_pointer(off_t addr) { struct fixed_ptr ptr; int rc; if (!addr) return NULL_BLOCK; rc = _read(&ptr, sizeof(ptr), addr); if (rc < 0) return -EIO; if (!ptr.next) return NULL_BLOCK; else return ABS((off_t)ptr.next); } /* * Read variable pointer from block and return absolute address on disk * and highest record number. */ static off_t get_var_pointer(off_t addr, int *max_record, unsigned int *disp) { struct var_ptr vptr; off_t ptr = 0; int rc; BUG(!addr); rc = _read(&vptr, VPTR_SIZE, addr); if (rc < 0) return -EIO; ptr = (off_t) vptr.next; *max_record = vptr.hi_record_nr; *disp = vptr.disp; if (!ptr) { if (vptr.hi_record_nr) return NULL_BLOCK; else return VAR_FILE_END; } else return ABS(ptr); } int is_edf_char(int c) { switch (c) { case 'A' ... 'Z': break; case 'a' ... 'z': break; case '0' ... '9': break; case '#': break; case '@': break; case '+': break; case '$': break; case '-': break; case ':': break; case '_': break; default: return 0; } return 1; } /* * Force conversion to upper case since lower case file names although * valid are not accepted by many CMS tools. */ static void str_toupper(char *str) { int i; for (i = 0; i < (int) strlen(str); i++) str[i] = toupper(str[i]); } /* * Set the FST date to the specified date. */ static void update_fst_date(struct fst_entry *fst, struct tm *tm) { unsigned int num; int i; if (tm->tm_year >= 100) fst->flag |= FST_FLAG_CENTURY; else fst->flag &= ~FST_FLAG_CENTURY; fst->date[0] = tm->tm_year; fst->date[1] = tm->tm_mon + 1; fst->date[2] = tm->tm_mday; fst->date[3] = tm->tm_hour; fst->date[4] = tm->tm_min; fst->date[5] = tm->tm_sec; /* convert hex to decimal */ for (i = 0; i < 6; i++) { num = fst->date[i]; num = hex_to_dec(num); fst->date[i] = num >> 4; } } /* * Set the FST date to the current date. */ static int set_fst_date_current(struct fst_entry *fst) { struct timeval tv; struct tm tm; /* convert timespec to tm */ memset(&tm, 0, sizeof(struct tm)); if (gettimeofday(&tv, NULL) < 0) { perror(COMP "gettimeofday failed"); return -EINVAL; } if (localtime_r(&tv.tv_sec, &tm) == NULL) return -EINVAL; update_fst_date(fst, &tm); return 0; } /* * Check if the file is on the opened list. */ static struct file *file_open(const char *name) { char uc_name[MAX_FNAME]; struct file *f; util_strlcpy(uc_name, name, MAX_FNAME); str_toupper(uc_name); util_list_iterate(&open_file_list, f) if (strncmp(f->path + 1, uc_name, MAX_FNAME) == 0) return f; return NULL; } /* * Check if the file is open and unlinked. */ static int file_unlinked(const char *name) { struct file *f = file_open(name); if (f && f->unlinked) return 1; else return 0; } /* * Convert EDF date to time_t. */ static time_t fst_date_to_time_t(char *date, int century) { unsigned long long num; unsigned int res[6]; struct tm tm; time_t time; int i; /* * date : YY MM DD HH MM SS (decimal!) * century: 0=19, 1=20, dead=21 * convert decimal to hex */ for (i = 0; i < 6; i++) { num = date[i]; num <<= 4; num += 0xc; /* plus */ res[i] = dec_to_hex(num); } memset(&tm, 0, sizeof(tm)); tm.tm_year = res[0]; tm.tm_mon = res[1]; tm.tm_mday = res[2]; tm.tm_hour = res[3]; tm.tm_min = res[4]; tm.tm_sec = res[5]; /* see man 3 tzset */ tm.tm_isdst = -1; /* prepare for mktime */ tm.tm_mon--; if (century == FST_FLAG_CENTURY) tm.tm_year += 100; time = mktime(&tm); if (time == -1) { fprintf(stderr, COMP "mktime failed!\n"); memset(&time, 0, sizeof(time)); } return time; } /* * Read one FST entry into *fst from offset on disk addr and detect type. * * Return values: * ret > 0 : disk address of additional FOP block * ret = -1 : file entry filled * ret = -2 : end of directory * ret = -3 : directory entry * ret = -4 : allocmap entry */ static int readdir_entry(struct fst_entry *fst, off_t addr) { int rc; BUG(addr & (sizeof(struct fst_entry) - 1)); rc = _read(fst, sizeof(*fst), addr); BUG(rc < 0); if (is_directory(fst->name, fst->type)) { /* check for multi-block directory */ if (ABS(fst->fop) != addr) return ABS(fst->fop); return READDIR_DIR_ENTRY; } if (is_allocmap(fst->name, fst->type)) return READDIR_MAP_ENTRY; if (is_file(fst->name, fst->type)) return READDIR_FILE_ENTRY; return READDIR_END_OF_DIR; } /* * Return number of characters excluding trailing spaces. */ static inline int strip_right(const char *str, int size) { while (str[size - 1] == 0x20) size--; return size; } /* * Convert ASCII name to EBCDIC name. */ static int encode_edf_name(const char *name, char *fname, char *ftype) { int dot_pos, tlen; char *tmp; /* * name is ascii string "FILE.EXT" * readdir_entry returns fst.name fst.type as EBCDIC including spaces * pre-fill name and type with ascii spaces, remove dot and convert * to EBCDIC. */ memset(fname, 0x20, 8); memset(ftype, 0x20, 8); tmp = index(name, '.'); /* filenames without a dot are invalid! */ if (tmp == NULL) return -EINVAL; dot_pos = tmp - name; if (dot_pos == 0 || dot_pos > 8) return -EINVAL; memcpy(fname, name, dot_pos); ebcdic_enc(fname, fname, 8); tlen = strlen(name) - (dot_pos + 1); if (tlen == 0 || tlen > 8) return -EINVAL; memcpy(ftype, name + dot_pos + 1, tlen); ebcdic_enc(ftype, ftype, 8); return 0; } /* * Convert EBCDIC name to ASCII name. */ static void decode_edf_name(char *file, char *fname, char *ftype) { int len, pos = 0; ebcdic_dec(fname, fname, 8); ebcdic_dec(ftype, ftype, 8); /* strip spaces but only from the end */ len = strip_right(fname, 8); memcpy(file, fname, len); /* add dot */ pos += len; file[pos] = '.'; pos++; len = strip_right(ftype, 8); memcpy(&file[pos], ftype, len); pos += len; /* terminate string */ file[pos] ='\0'; } static int edf_name_valid(const char *name) { int name_len, i; char *dot; /* name must contain . */ dot = index(name, '.'); if (dot == NULL) return -EINVAL; name_len = dot - name; for (i = 0; i < name_len; i++) if (!is_edf_char(name[i])) return -EINVAL; for (i = name_len + 1; i < (int) strlen(name); i++) if (!is_edf_char(name[i])) return -EINVAL; return 0; } /* * Summarize the number of bytes used in the last data block. */ static int walk_last_var_data_block(off_t addr, off_t *total) { ssize_t left = cmsfs.blksize; u16 len; int rc; /* subtract displacement */ left -= addr & DATA_BLOCK_MASK; while (left >= (int) sizeof(len)) { rc = _read(&len, sizeof(len), addr); if (rc < 0) return rc; /* * Null length means no more records follow. * Assumption: the last block is zero-padded. */ if (!len) return 0; /* add length of record with the header length */ *total += len + sizeof(len); left -= len + sizeof(len); /* point to next record */ addr += len + sizeof(len); } return 0; } /* * Return struct record for record number nr. */ static struct record *get_record(struct file *f, int nr) { BUG(nr > f->fst->nr_records - 1); return &f->rlist[nr]; } static int skip_header_byte(struct file *f) { if (f->fst->record_format == RECORD_LEN_FIXED) return 0; if (f->record_scan_state == RSS_HEADER_STARTED) return 1; else return 0; } static void set_record_len_upper(struct file *f, int record, u8 len) { struct record *r = &f->rlist[record]; if (f->record_scan_state != RSS_DATA_BLOCK_STARTED && f->record_scan_state != RSS_DATA_BLOCK_EXT) DIE("%s: internal error\n", __func__); r->total_len = len << 8; f->record_scan_state = RSS_HEADER_STARTED; } static void set_record_len_lower(struct file *f, int record, u8 len) { struct record *r = &f->rlist[record]; if (f->record_scan_state != RSS_HEADER_STARTED) DIE("%s: internal error\n", __func__); r->total_len += len; f->record_scan_state = RSS_HEADER_COMPLETE; } static void set_record_len(struct file *f, int record, u16 len) { struct record *r = &f->rlist[record]; if (f->fst->nr_records && f->fst->nr_records == record) DIE("%s: record nr: %d out of bounds\n", __func__, record); if (f->record_scan_state != RSS_DATA_BLOCK_STARTED && f->record_scan_state != RSS_DATA_BLOCK_EXT) DIE("%s: internal error\n", __func__); r->total_len = len; f->record_scan_state = RSS_HEADER_COMPLETE; } static void set_record(struct file *f, int *record, off_t addr, int len, off_t *total, int block) { struct record *r = &f->rlist[*record]; if (f->record_scan_state != RSS_HEADER_COMPLETE) DIE("%s: internal error\n", __func__); r->first_block_len = len; r->disk_start = addr; r->block_nr = block; if (addr == NULL_BLOCK) { if (f->null_ctr % cmsfs.blksize == 0) r->null_block_started = 1; f->null_ctr += len; } else f->null_ctr = 0; /* add previous record linefeed but not for the first record */ if (f->linefeed && *record) (*total)++; r->file_start = *total; (*total) += r->total_len; f->record_scan_state = RSS_DATA_BLOCK_STARTED; } static void add_record_ext(struct record *rec, struct record_ext *ext) { struct record_ext *tmp; if (rec->ext == NULL) { rec->ext = ext; ext->prev = NULL; ext->next = NULL; } else { tmp = rec->ext; while (tmp->next != NULL) { tmp = tmp->next; } tmp->next = ext; ext->prev = tmp; ext->next = NULL; } } static void set_record_extension(struct file *f, int *record, off_t addr, int len, int block) { struct record *rec = &f->rlist[*record]; struct record_ext *ext; if (f->record_scan_state != RSS_DATA_BLOCK_STARTED && f->record_scan_state != RSS_DATA_BLOCK_EXT) DIE("%s: internal error\n", __func__); BUG(*record >= f->fst->nr_records); ext = malloc(sizeof(struct record_ext)); if (ext == NULL) DIE_PERROR("malloc failed\n"); memset(ext, 0, sizeof(*ext)); ext->len = len - skip_header_byte(f); ext->disk_start = addr + skip_header_byte(f); ext->block_nr = block; if (ext->disk_start == NULL_BLOCK) { if (f->null_ctr % cmsfs.blksize == 0) ext->null_block_started = 1; f->null_ctr += len; } else f->null_ctr = 0; add_record_ext(rec, ext); f->record_scan_state = RSS_DATA_BLOCK_EXT; } /* check for file end by record number */ static int end_of_file(struct file *f, int record) { if (record == f->fst->nr_records - 1) return 1; return 0; } static int walk_fixed_data_block(struct file *f, off_t addr, int *record, off_t *total, int block, int disp) { off_t offset = (off_t ) block * cmsfs.blksize + disp; int len = (f->fst->record_len > cmsfs.blksize) ? cmsfs.blksize : f->fst->record_len; int left = cmsfs.blksize - disp; int first = 0; if (offset % f->fst->record_len) { if (f->fst->record_len - offset >= cmsfs.blksize) first = cmsfs.blksize; else first = (f->fst->record_len % cmsfs.blksize) - (offset % len); } if (first) { BUG(first > left); set_record_extension(f, record, addr, first, block); left -= first; if (addr != NULL_BLOCK) addr += first; } while (left >= len) { /* * Increment record number only after adding a possible * extension. *record starts with -1 so the first is 0. */ if (end_of_file(f, *record)) return 1; (*record)++; set_record_len(f, *record, f->fst->record_len); set_record(f, record, addr, len, total, block); left -= len; if (addr != NULL_BLOCK) addr += len; } /* partial record left */ if (left > 0) { if (end_of_file(f, *record)) return 1; (*record)++; set_record_len(f, *record, f->fst->record_len); set_record(f, record, addr, left, total, block); return 0; } return 0; } static int get_record_unused_bytes(struct file *f, int nr) { struct record *rec = get_record(f, nr); struct record_ext *rext; int used = 0; /* no data bytes yet */ if (f->record_scan_state == RSS_HEADER_COMPLETE) return rec->total_len; used = rec->first_block_len; /* only first block */ if (f->record_scan_state == RSS_DATA_BLOCK_STARTED) goto out; rext = rec->ext; while (rext != NULL) { used += rext->len; rext = rext->next; } out: return rec->total_len - used; } static int walk_var_data_block(struct file *f, off_t addr, unsigned int disp, int *record, off_t *total, int block, int skip) { ssize_t left = cmsfs.blksize - skip; int last, rc; u8 half_len; u16 len; /* * If records are skipped on this block there is no record extension, * overwrite disp and start with scanning the record. */ if (skip) disp = 0; /* * disp set means 1 or 2 header bytes and possibly data bytes on the * last block or a null block. */ if (disp) { if (addr == NULL_BLOCK) { last = cmsfs.blksize; /* * Special case: last block can be a null block with * not all bytes used on it. */ if (f->fst->nr_blocks - 1 == block) last = get_record_unused_bytes(f, *record); /* * Special case: record header on last block wo. data. * That means no record data yet for this block. */ if (f->record_scan_state == RSS_HEADER_COMPLETE) set_record(f, record, addr, last, total, block); else set_record_extension(f, record, addr, last, block); return 0; } if (disp == VAR_RECORD_SPANNED) len = cmsfs.blksize; else len = disp; /* split header -> read second length byte */ if (f->record_scan_state == RSS_HEADER_STARTED) { rc = _read(&half_len, sizeof(half_len), addr); if (rc < 0) return rc; set_record_len_lower(f, *record, half_len); left--; len--; addr++; } if (f->record_scan_state == RSS_HEADER_COMPLETE) set_record(f, record, addr, len, total, block); else set_record_extension(f, record, addr, len, block); if (disp == VAR_RECORD_SPANNED) return 0; left -= len; addr += len; } /* at least one data byte */ while (left >= (int) sizeof(len) + 1) { rc = _read(&len, sizeof(len), addr); if (rc < 0) return rc; /* * Null length means no more records follow. * Assumption: the last block is zero-padded. */ if (!len) return 0; /* * Increment record number only after adding a possible * extension. *record starts with -1 so the first is 0. */ (*record)++; set_record_len(f, *record, len); /* account consumed header bytes */ left -= sizeof(len); addr += sizeof(len); /* limit to block end */ if (len > left) len = left; /* add length of record including the header length */ set_record(f, record, addr, len, total, block); left -= len; /* point to next record header */ addr += len; } /* 2 header bytes left */ if (left == 2) { rc = _read(&len, sizeof(len), addr); if (rc < 0) return rc; if (!len) return 0; (*record)++; set_record_len(f, *record, len); return 0; } /* split header */ if (left == 1) { if (end_of_file(f, *record)) return 0; rc = _read(&half_len, sizeof(half_len), addr); if (rc < 0) return rc; (*record)++; set_record_len_upper(f, *record, half_len); f->record_scan_state = RSS_HEADER_STARTED; } return 0; } static int cache_fixed_data_block(struct file *f, off_t addr, int *block, int *record, off_t *total, int disp) { /* * Cannot distinguish null block pointers from not existing pointers, * so this fn is called for the whole pointer block and maybe for * non-existing blocks and records too. Check and bail out if EOF. */ if (*block >= f->fst->nr_blocks || *record >= f->fst->nr_records) return 0; f->blist[*block].disk_addr = addr & ~DATA_BLOCK_MASK; walk_fixed_data_block(f, addr, record, total, *block, disp); /* record starts with 0 but on-disk record number with 1 */ f->blist[*block].hi_record_nr = *record + 1; (*block)++; return 0; } /* * Walk all pointer blocks of a fixed file and call function for every * data block respecting the sequence of the data. */ static int cache_file_fixed(struct file *f, off_t addr, int level, int *block, unsigned int *disp, int *record, off_t *total) { int left = f->ptr_per_block; off_t ptr; if (level > 0) { level--; while (left--) { ptr = get_fixed_pointer(addr); if (ptr < 0) return ptr; /* * In difference to variable record format a null pointer * may be valid for a null block so we cannot determine * the file end from a pointer entry. Check for the number * of scanned blocks instead. */ if (*block >= f->fst->nr_blocks || *record >= f->fst->nr_records) return 0; cache_file_fixed(f, ptr, level, block, disp, record, total); /* don't increment for null block pointers */ if (addr) addr += PTR_SIZE; } return 0; } return cache_fixed_data_block(f, addr, block, record, total, 0); } static int cache_variable_data_block(struct file *f, off_t addr, int *block, int *record, int disp, off_t *total, int skip) { int rc; /* * Cannot distinguish null block pointers from not existing pointers, * so this fn is called for the whole pointer block and maybe for * non-existing blocks and records too. Check and bail out if EOF. */ if (*block >= f->fst->nr_blocks || *record >= f->fst->nr_records) return 0; f->blist[*block].disk_addr = addr & ~DATA_BLOCK_MASK; rc = walk_var_data_block(f, addr, disp, record, total, *block, skip); if (rc < 0) return rc; /* record starts with 0 but on-disk record number with 1 */ f->blist[*block].hi_record_nr = *record + 1; f->blist[*block].disp = disp; (*block)++; return 0; } /* * Walk all pointer blocks of a variable file and call function for every * data block respecting the sequence of the data. */ static int cache_file_variable(struct file *f, off_t addr, int level, int *block, unsigned int *disp, int *record, off_t *total) { int nr, left = f->ptr_per_block; off_t ptr; if (level > 0) { level--; /* 4 or 8 bytes are left at the end (offset) which we ignore */ while (left--) { ptr = get_var_pointer(addr, &nr, disp); if (ptr < 0) return ptr; if (ptr == VAR_FILE_END) return 0; cache_file_variable(f, ptr, level, block, disp, record, total); addr += VPTR_SIZE; } return 0; } return cache_variable_data_block(f, addr, block, record, *disp, total, 0); } static int locate_last_data_vptr(off_t addr, int level, struct fst_entry *fst, struct var_ptr *vptr) { int last, rc; if (!level) return 0; level--; /* read offset pointer from the end of the var pointer block */ rc = _read(&last, sizeof(last), addr + cmsfs.blksize - sizeof(last)); if (rc < 0) return rc; if (last % VPTR_SIZE || last > cmsfs.blksize) return -EIO; rc = _read(vptr, VPTR_SIZE, addr + last); if (rc < 0) return rc; if (vptr->hi_record_nr != fst->nr_records) return -EIO; /* vptr should contain the highest data block pointer */ if (!level) return 0; if (vptr->next == NULL_BLOCK) return 0; return locate_last_data_vptr(ABS(vptr->next), level, fst, vptr); } static int is_textfile(struct fst_entry *fst) { char type[MAX_TYPE_LEN]; struct filetype *ft; if (!fst) return 0; memset(type, 0, sizeof(type)); ebcdic_dec(type, fst->type, 8); util_list_iterate(&text_type_list, ft) if (strncmp(ft->name, type, strlen(ft->name)) == 0) return 1; return 0; } /* * Decide if linefeeds are needed for this file type. */ static int linefeed_mode_enabled(struct fst_entry *fst) { if (cmsfs.mode == BINARY_MODE) return 0; if (cmsfs.mode == TEXT_MODE) return 1; return is_textfile(fst); } /* * Workaround glibc 2.9 bug with less than 3 files and give room for some * new files. If cache is full it will be purged and rebuild. */ static int max_cache_entries(void) { return cmsfs.files + 10 + cmsfs.files / 4; } static void resize_htab(void) { int i; for (i = 0; i < cmsfs.fcache_used; i++) free(cmsfs.fcache[i].str); hdestroy_r(&cmsfs.htab); free(cmsfs.fcache); cmsfs.fcache_used = 0; cmsfs.fcache_max = max_cache_entries(); cmsfs.fcache = calloc(cmsfs.fcache_max, sizeof(struct fcache_entry)); if (!hcreate_r(cmsfs.fcache_max, &cmsfs.htab)) DIE("hcreate failed\n"); } static void cache_fst_addr(off_t addr, const char *file) { struct fcache_entry *fce; ENTRY e, *eptr; e.key = strdup(file); again: if (hsearch_r(e, FIND, &eptr, &cmsfs.htab) == 0) { /* cache it */ if (cmsfs.fcache_used == cmsfs.fcache_max - 1) { DEBUG("hsearch: hash table full: %d\n", cmsfs.fcache_used); resize_htab(); goto again; } fce = &cmsfs.fcache[cmsfs.fcache_used]; cmsfs.fcache_used++; fce->fst_addr = addr; fce->str = e.key; e.data = fce; if (hsearch_r(e, ENTER, &eptr, &cmsfs.htab) == 0) DIE("hsearch: hash table full\n"); } else free(e.key); } static void update_htab_entry(off_t addr, const char *file) { struct fcache_entry *fce; ENTRY e, *eptr; e.key = strdup(file); if (hsearch_r(e, FIND, &eptr, &cmsfs.htab) == 0) { /* not yet cached, nothing to do */ free(e.key); return; } else { /* update it */ fce = eptr->data; fce->fst_addr = addr; e.data = fce; if (hsearch_r(e, ENTER, &eptr, &cmsfs.htab) == 0) DIE("%s: hash table full\n", __func__); } } static void invalidate_htab_entry(const char *name) { struct fcache_entry *fce; ENTRY e, *eptr; e.key = strdup(name); if (hsearch_r(e, FIND, &eptr, &cmsfs.htab) == 0) { /* nothing to do if not cached */ free(e.key); return; } fce = eptr->data; fce->fst_addr = 0; e.data = fce; if (hsearch_r(e, ENTER, &eptr, &cmsfs.htab) == 0) DIE("hsearch: hash table full\n"); } /* * For each FST entry in a directory block do action. * * Return: * hit == NULL : lookup file not found * hit != NULL : lookup file found, addr of the fst entry */ static void walk_dir_block(struct fst_entry *fst, struct walk_file *walk, int level, off_t *hit) { off_t ptr, addr = walk->addr; char file[MAX_FNAME]; int ret, left; /* handle higher level directory pointer blocks */ if (level > 0) { level--; left = PTRS_PER_BLOCK; while (left--) { ptr = get_fixed_pointer(addr); BUG(ptr < 0); if (!ptr) break; walk->addr = ptr; walk_dir_block(fst, walk, level, hit); if (hit != NULL && *hit) return; addr += PTR_SIZE; } return; } if (walk->flag == WALK_FLAG_CACHE_DBLOCKS) { walk->dlist[walk->dlist_used++] = walk->addr; return; } left = cmsfs.blksize / sizeof(struct fst_entry); while (left--) { ret = readdir_entry(fst, walk->addr); /* directory and allocmap type are skipped */ if (ret == READDIR_FILE_ENTRY) { if (walk->flag == WALK_FLAG_LOOKUP) { if ((memcmp(fst->name, walk->name, 8) == 0) && (memcmp(fst->type, walk->type, 8) == 0)) { /* got it */ *hit = walk->addr; return; } } if (walk->flag == WALK_FLAG_READDIR) { memset(file, 0, sizeof(file)); decode_edf_name(file, fst->name, fst->type); if (!file_unlinked(file)) { cache_fst_addr(walk->addr, file); walk->filler(walk->buf, file, NULL, 0, 0); } } } if (ret == READDIR_END_OF_DIR) { if (walk->flag == WALK_FLAG_LOCATE_EMPTY) { *hit = walk->addr; return; } break; } walk->addr += sizeof(struct fst_entry); }; return; } static void walk_directory(struct fst_entry *fst, struct walk_file *walk, off_t *hit) { if (cmsfs.dir_levels == 0) walk->addr = cmsfs.fdir; else walk->addr = get_fop(cmsfs.fdir); walk_dir_block(fst, walk, cmsfs.dir_levels, hit); } /* * Check FST record format only when reading FST entry from disk. */ static int check_fst_valid(struct fst_entry *fst) { if (fst->record_format != RECORD_LEN_FIXED && fst->record_format != RECORD_LEN_VARIABLE) return 0; else return 1; } /* * Locate the file's fst_entry in any of the directory blocks. */ static off_t lookup_file(const char *name, struct fst_entry *fst, int flag) { struct fcache_entry *fce; char uc_name[MAX_FNAME]; char fname[8], ftype[8]; struct walk_file walk; ENTRY e, *eptr; off_t faddr = 0; int rc; util_strlcpy(uc_name, name, MAX_FNAME); str_toupper(uc_name); if (flag == HIDE_UNLINKED && file_unlinked(uc_name)) return 0; e.key = strdup(uc_name); /* already cached ? */ if (hsearch_r(e, FIND, &eptr, &cmsfs.htab)) { fce = eptr->data; /* check if fst is valid, may be zero for a stale entry */ if (!fce->fst_addr) goto renew; /* read in the fst entry */ rc = _read(fst, sizeof(*fst), fce->fst_addr); BUG(rc < 0); if (!check_fst_valid(fst)) DIE("Invalid file format in file: %s\n", uc_name); free(e.key); return fce->fst_addr; } renew: free(e.key); if (encode_edf_name(uc_name, fname, ftype)) return 0; memset(&walk, 0, sizeof(walk)); walk.flag = WALK_FLAG_LOOKUP; walk.name = fname; walk.type = ftype; walk_directory(fst, &walk, &faddr); if (!faddr) return 0; if (!check_fst_valid(fst)) DIE("Invalid file format in file: %s\n", uc_name); cache_fst_addr(faddr, uc_name); return faddr; } static int cache_file(struct file *f) { int block = 0, record = -1; unsigned int disp = 0; off_t total = 0; return f->fops->cache_data(f, ABS(f->fst->fop), f->fst->levels, &block, &disp, &record, &total); } /* * Caveat: for fixed files nr_blocks is excluding null blocks, * for variable files nr_blocks is including null blocks. * Add null blocks for fixed files so allocation and file end * checks work identical for both variants. */ static void workaround_nr_blocks(struct file *f) { int nr; if (f->fst->record_format == RECORD_LEN_VARIABLE) return; nr = (off_t) f->fst->nr_records * (off_t) f->fst->record_len / cmsfs.blksize; if ((off_t) f->fst->nr_records * (off_t) f->fst->record_len % cmsfs.blksize) nr++; f->nr_null_blocks = nr - f->fst->nr_blocks; f->fst->nr_blocks = nr; } static off_t get_file_size_fixed(struct fst_entry *fst) { return (off_t) fst->nr_records * (off_t) fst->record_len; } static off_t get_file_size_variable(struct fst_entry *fst) { struct var_ptr vptr; off_t total = 0; off_t ptr; int rc; if (fst->levels > 0) { rc = locate_last_data_vptr(ABS(fst->fop), fst->levels, fst, &vptr); if (rc < 0) return rc; if (vptr.next == 0) { /* * Last block is a null block. No more records can * follow, so the displacement value points to EOF. */ total = vptr.disp; goto skip_scan; } ptr = ABS(vptr.next); if (vptr.disp != VAR_RECORD_SPANNED) { ptr += vptr.disp; /* count displacement as used space */ total += vptr.disp; } else { total += cmsfs.blksize; goto skip_scan; } } else ptr = ABS(fst->fop); /* now count the remaining used space in the last block */ rc = walk_last_var_data_block(ptr, &total); if (rc < 0) return rc; skip_scan: /* * Add the full blocks. For variable record file nr_blocks contains * also null blocks. */ if (fst->nr_blocks) total += ((off_t) fst->nr_blocks - 1) * cmsfs.blksize; return total; } /* * Return the file size as it is on the disk. Includes headers for * variable records. */ static off_t get_file_size(struct fst_entry *fst) { if (fst->record_format == RECORD_LEN_FIXED) return get_file_size_fixed(fst); else if (fst->record_format == RECORD_LEN_VARIABLE) return get_file_size_variable(fst); return 0; } static off_t get_file_size_logical(struct fst_entry *fst) { off_t total; if (fst->nr_records == 0) return 0; if (!fst->fop) return -EIO; total = get_file_size(fst); if (total < 0) return -EIO; /* subtract the record headers */ if (fst->record_format == RECORD_LEN_VARIABLE) total -= (off_t) fst->nr_records * VAR_RECORD_HEADER_SIZE; if (linefeed_mode_enabled(fst)) total += fst->nr_records; return total; } static int cmsfs_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi) { (void) fi; int mask = (cmsfs.allow_other) ? 0444 : 0440; struct fst_entry fst; if (!cmsfs.readonly) mask |= ((cmsfs.allow_other) ? 0222 : 0220); memset(stbuf, 0, sizeof(*stbuf)); stbuf->st_uid = getuid(); stbuf->st_gid = getgid(); stbuf->st_blksize = cmsfs.blksize; if (strcmp(path, "/") == 0) { stbuf->st_mode = S_IFDIR | mask | ((cmsfs.allow_other) ? 0111 : 0110); stbuf->st_nlink = 2; readdir_entry(&fst, cmsfs.fdir); /* date */ stbuf->st_mtime = fst_date_to_time_t(&fst.date[0], fst.flag & FST_FLAG_CENTURY); stbuf->st_atime = stbuf->st_ctime = stbuf->st_mtime; /* size */ stbuf->st_size = (off_t) fst.record_len * (off_t) fst.nr_records; stbuf->st_blocks = MAX(stbuf->st_size, cmsfs.blksize); stbuf->st_blocks = ((stbuf->st_blocks + cmsfs.data_block_mask) & ~cmsfs.data_block_mask) >> 9; } else { if (!lookup_file(path + 1, &fst, HIDE_UNLINKED)) return -ENOENT; stbuf->st_mode = S_IFREG | mask; stbuf->st_nlink = 1; /* date */ stbuf->st_mtime = stbuf->st_atime = stbuf->st_ctime = fst_date_to_time_t(&fst.date[0], fst.flag & FST_FLAG_CENTURY); /* size */ stbuf->st_size = get_file_size_logical(&fst); if (stbuf->st_size < 0) return -EIO; /* * Include potential sparse blocks for variable files which * are included in nr_blocks to avoid scanning the whole file. */ stbuf->st_blocks = (off_t) fst.nr_blocks * cmsfs.nr_blocks_512; } return 0; } static int cmsfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi, enum fuse_readdir_flags flags) { struct walk_file walk; struct fst_entry fst; (void) offset; (void) fi; (void) flags; /* * Offset is ignored and 0 passed to the filler fn so the whole * directory is read at once. */ /* EDF knows only the root directory */ if (strcmp(path, "/") != 0) return -ENOENT; filler(buf, ".", NULL, 0, 0); filler(buf, "..", NULL, 0, 0); memset(&walk, 0, sizeof(walk)); /* readdir is possible without open so fi->fh is not set */ walk.flag = WALK_FLAG_READDIR; walk.buf = buf; walk.filler = filler; walk_directory(&fst, &walk, NULL); return 0; } static int cmsfs_open(const char *path, struct fuse_file_info *fi) { struct fst_entry fst; struct file *f; off_t fst_addr; int rc = 0; /* * open flags: * O_DIRECTORY: FUSE captures open on / so not needed. * O_NOATIME: ignored because there is no atime in EDF. * O_NOFOLLOW: can be ignored since EDF has no links. * O_SYNC: ignored since IO is always sync. * O_TRUNC, O_CREAT, O_EXCL: avoided by FUSE. */ fst_addr = lookup_file(path + 1, &fst, SHOW_UNLINKED); if (!fst_addr) return -ENOENT; f = file_open(path + 1); if (f == NULL) { f = create_file_object(&fst, &rc); if (f == NULL) return rc; f->fst_addr = fst_addr; /* * Store file size in file object. Needed for write of fixed record * length files when the write is not a multiple of the record length. * In this case a second write would fail since the file size would * be calculated by lrecl * nr_records. Use session_size therefore. */ f->session_size = get_file_size_logical(&fst); if (f->session_size < 0) return -EIO; f->wcache = malloc(WCACHE_MAX); if (f->wcache == NULL) return -ENOMEM; /* * For fixed-length records f->fst->record_len contains * the fixed record length, which will not change. For * variable-length records it contains the (current) maximum * record length, which could be increased later by appending * new records, so use MAX_RECORD_LEN for the iconv buffer. * The MAX_RECORD_LEN is not valid for fixed-length records, * only for variable-length records, so use the actual record * length (f->fst->record_len) for fixed-length records. */ if (f->fst->record_format == RECORD_LEN_FIXED) f->iconv_buf = malloc(f->fst->record_len + 1); else f->iconv_buf = malloc(MAX_RECORD_LEN + 1); if (f->iconv_buf == NULL) { destroy_file_object(f); return -ENOMEM; } util_strlcpy(f->path, path, MAX_FNAME + 1); str_toupper(f->path); f->use_count = 1; util_list_add_head(&open_file_list, f); } else f->use_count++; if (fi->flags & O_RDWR || fi->flags & O_WRONLY) f->write_count++; fi->fh = (uint64_t)(unsigned long) f; return 0; } static void set_fdir_date_current(void) { struct fst_entry fst; int rc; rc = _read(&fst, sizeof(fst), cmsfs.fdir); BUG(rc < 0); set_fst_date_current(&fst); rc = _write(&fst, sizeof(fst), cmsfs.fdir); BUG(rc < 0); } static void increase_file_count(void) { struct fst_entry fst; int rc; rc = _read(&fst, sizeof(fst), cmsfs.fdir); BUG(rc < 0); fst.nr_records = ++cmsfs.files + 2; set_fst_date_current(&fst); rc = _write(&fst, sizeof(fst), cmsfs.fdir); BUG(rc < 0); } static void decrease_file_count(void) { struct fst_entry fst; int rc; rc = _read(&fst, sizeof(fst), cmsfs.fdir); BUG(rc < 0); fst.nr_records = --cmsfs.files + 2; set_fst_date_current(&fst); rc = _write(&fst, sizeof(fst), cmsfs.fdir); BUG(rc < 0); } static off_t get_reserved_block(void) { off_t addr; if (cmsfs.reserved_blocks > 0) cmsfs.reserved_blocks--; addr = get_zero_block(); BUG(addr < 0); return addr; } static void cache_dblocks(struct walk_file *walk) { double dblocks; /* calculate number of data blocks used for FST entries */ dblocks = (cmsfs.files + 2) * sizeof(struct fst_entry); dblocks = ceil(dblocks / cmsfs.blksize); /* add a spare one in case of create file */ dblocks++; memset(walk, 0, sizeof(*walk)); walk->flag = WALK_FLAG_CACHE_DBLOCKS; walk->dlist = calloc(dblocks, sizeof(off_t)); if (walk->dlist == NULL) DIE_PERROR("malloc failed"); walk_directory(NULL, walk, NULL); } static void free_dblocks(struct walk_file *walk) { free(walk->dlist); } static void purge_dblock_ptrs(int level, off_t addr) { int left = PTRS_PER_BLOCK; off_t ptr, start = addr; if (!level) return; level--; while (left--) { ptr = get_fixed_pointer(addr); BUG(ptr < 0); if (ptr != NULL_BLOCK) purge_dblock_ptrs(level, ptr); /* don't increment for null block pointers */ if (addr) addr += PTR_SIZE; } free_block(start); } /* * Return total number of pointer entries for level. */ static int pointers_per_level(struct file *f, int level, int nr_blocks) { double entries = nr_blocks; if (!level || nr_blocks < 2) return 0; if (level == 1) return nr_blocks; level--; while (level--) entries = ceil(entries / f->ptr_per_block); return (int) entries; } static int per_level_fixed(int level, int entries) { double val = entries; while (level--) val = ceil(val / PTRS_PER_BLOCK); return (int) val; } static void rewrite_dir_ptr_block(struct walk_file *walk, int level, off_t dst, int start) { struct fixed_ptr ptr; int rc, i, end; off_t addr; if (!level) return; end = MIN(start + PTRS_PER_BLOCK, per_level_fixed(level - 1, walk->dlist_used)); BUG(start > end); for (i = start; i < end; i++) { if (level == 1) { addr = walk->dlist[i]; if (addr) ptr.next = REL(addr); else ptr.next = 0; } else { addr = get_zero_block(); BUG(addr < 0); ptr.next = REL(addr); } rc = _write(&ptr, sizeof(ptr), dst); BUG(rc < 0); dst += sizeof(ptr); rewrite_dir_ptr_block(walk, level - 1, addr, i * PTRS_PER_BLOCK); } } static int update_dir_levels(int blocks) { int levels = 1; if (blocks < 2) return 0; while (blocks / PTRS_PER_BLOCK) { levels++; blocks /= PTRS_PER_BLOCK; } return levels; } static void rewrite_dblock_ptrs(struct walk_file *walk) { int rc, nr_blocks = walk->dlist_used; struct fst_entry fst; off_t dst; BUG(!nr_blocks); /* read in the directory FST */ rc = _read(&fst, sizeof(fst), cmsfs.fdir); BUG(rc < 0); if (nr_blocks == 1) { fst.fop = REL(cmsfs.fdir); fst.levels = 0; cmsfs.dir_levels = fst.levels; goto store; } dst = get_zero_block(); BUG(dst < 0); fst.fop = REL(dst); fst.levels = update_dir_levels(walk->dlist_used); cmsfs.dir_levels = fst.levels; rewrite_dir_ptr_block(walk, fst.levels, dst, 0); store: rc = _write(&fst, sizeof(fst), cmsfs.fdir); BUG(rc < 0); } /* * Update used block count in disk label. */ static void update_block_count(void) { int rc; rc = _write(&cmsfs.used_blocks, sizeof(unsigned int), cmsfs.label + 32); BUG(rc < 0); } static int cmsfs_create(const char *path, mode_t mode, struct fuse_file_info *fi) { char fname[8], ftype[8]; char uc_name[MAX_FNAME]; struct walk_file walk; struct fst_entry fst; off_t fst_addr = 0; int rc; /* no permissions in EDF */ (void) mode; /* * Note: creating a file that was unlinked but not yet deleted from * disk is not supported. That means the unlinked file can still be * opened. */ if (lookup_file(path + 1, &fst, SHOW_UNLINKED)) return cmsfs_open(path, fi); if (cmsfs.readonly) return -EACCES; rc = edf_name_valid(path + 1); if (rc) return rc; /* force uppercase */ util_strlcpy(uc_name, path + 1, MAX_FNAME); str_toupper(uc_name); rc = encode_edf_name(uc_name, fname, ftype); if (rc) return rc; /* find free fst entry */ memset(&walk, 0, sizeof(walk)); walk.flag = WALK_FLAG_LOCATE_EMPTY; walk_directory(&fst, &walk, &fst_addr); /* no free slot found, allocate new directory block */ if (!fst_addr) { /* * Be conservative and check if enough blocks for all * directory levels are available. */ if (cmsfs.total_blocks - cmsfs.used_blocks < cmsfs.dir_levels + 2) return -ENOSPC; fst_addr = get_zero_block(); if (fst_addr < 0) return fst_addr; cache_dblocks(&walk); /* add the newly allocated block to dlist */ walk.dlist[walk.dlist_used++] = fst_addr; purge_dblock_ptrs(cmsfs.dir_levels, get_fop(cmsfs.fdir)); rewrite_dblock_ptrs(&walk); free_dblocks(&walk); update_block_count(); } /* * Fill fst entry. Default template: * format: variable * record_len: 0 * mode: A1 * flag: 0, century bit (0x8) set by following utimens * fop: 0 * nr_records: 0 * nr_blocks: 0 * levels: 0 * ptr_size: 0xc for variable format * date: set to current date */ memset(&fst, 0, sizeof(fst)); memcpy(fst.name, fname, 8); memcpy(fst.type, ftype, 8); ebcdic_enc((char *) &fst.mode, "A1", 2); fst.record_format = RECORD_LEN_VARIABLE; fst.ptr_size = sizeof(struct var_ptr); rc = set_fst_date_current(&fst); if (rc != 0) return rc; rc = _write(&fst, sizeof(fst), fst_addr); BUG(rc < 0); cache_fst_addr(fst_addr, uc_name); increase_file_count(); return cmsfs_open(path, fi); } static int purge_pointer_block_fixed(struct file *f, int level, off_t addr) { int left = f->ptr_per_block; off_t ptr, start = addr; if (!level) return 0; level--; while (left--) { if (!level) break; ptr = get_fixed_pointer(addr); if (ptr < 0) return ptr; if (ptr != NULL_BLOCK) purge_pointer_block_fixed(f, level, ptr); /* don't increment for null block pointers */ if (addr) addr += PTR_SIZE; } free_block(start); return 0; } static int purge_pointer_block_variable(struct file *f, int level, off_t addr) { int nr, left = f->ptr_per_block; off_t ptr, start = addr; unsigned int disp; if (!level) return 0; level--; while (left--) { if (!level) break; ptr = get_var_pointer(addr, &nr, &disp); if (ptr < 0) return ptr; if (ptr == VAR_FILE_END) break; if (ptr != NULL_BLOCK) purge_pointer_block_variable(f, level, ptr); /* don't increment for null block pointers */ if (addr) addr += VPTR_SIZE; } free_block(start); return 0; } /* * Store the back pointer for a variable pointer block. * Pointer is offset of last VPTR to block start. */ static int store_back_pointer(off_t dst, int entries) { unsigned int back; back = (entries - 1) * VPTR_SIZE; return _write(&back, sizeof(back), ((dst | DATA_BLOCK_MASK) + 1) - sizeof(back)); } /* * Rewrite one pointer block starting from the highest level. */ static int rewrite_pointer_block_fixed(struct file *f, int level, off_t dst, int start) { struct fixed_ptr ptr; int i, end, rc = 0; off_t addr; if (!level) return 0; /* * start is always the first entry of a pointer block, * end is the last used entry in this pointer block. */ end = MIN(start + f->ptr_per_block, per_level_fixed(level - 1, f->fst->nr_blocks)); BUG(start > end); for (i = start; i < end; i++) { if (level == 1) { addr = f->blist[i].disk_addr; if (addr) ptr.next = REL(addr); else ptr.next = 0; } else { addr = get_reserved_block(); ptr.next = REL(addr); } rc = _write(&ptr, sizeof(ptr), dst); if (rc < 0) return rc; dst += sizeof(ptr); rc = rewrite_pointer_block_fixed(f, level - 1, addr, i * f->ptr_per_block); if (rc < 0) return rc; } return rc; } static int get_first_block_nr(int level, int entry) { while (level-- > 1) entry *= VPTRS_PER_BLOCK; return entry; } static int get_last_block_nr(int level, int entry, int nr_blocks) { while (level-- > 1) entry *= VPTRS_PER_BLOCK; entry--; if (entry > nr_blocks - 1) entry = nr_blocks - 1; return entry; } static int per_level_var(int level, int entries) { double val = entries; while (level--) val = ceil(val / VPTRS_PER_BLOCK); return (int) val; } /* * Rewrite one pointer block starting from the highest level. */ static int rewrite_pointer_block_variable(struct file *f, int level, off_t dst, int start) { int i, bnr, end, rc = 0; struct var_ptr ptr; off_t addr; if (!level) return 0; /* * start is always the first entry of a pointer block, * end is the last used entry in this pointer block. */ end = MIN(start + f->ptr_per_block, per_level_var(level - 1, f->fst->nr_blocks)); BUG(start > end); for (i = start; i < end; i++) { if (level == 1) { addr = f->blist[i].disk_addr; if (addr) ptr.next = REL(addr); else ptr.next = 0; } else { addr = get_reserved_block(); ptr.next = REL(addr); } bnr = get_first_block_nr(level, i); ptr.disp = f->blist[bnr].disp; bnr = get_last_block_nr(level, i + 1, f->fst->nr_blocks); ptr.hi_record_nr = f->blist[bnr].hi_record_nr; rc = _write(&ptr, sizeof(ptr), dst); if (rc < 0) return rc; dst += sizeof(ptr); rc = rewrite_pointer_block_variable(f, level - 1, addr, i * f->ptr_per_block); if (rc < 0) return rc; } return store_back_pointer(dst, end - start); } /* * Update fop and pointer blocks if needed. */ static int rewrite_pointers(struct file *f) { struct record *rec; off_t dst; if (f->fst->nr_blocks == 0) { f->fst->fop = 0; return 0; } if (f->fst->nr_blocks == 1) { rec = get_record(f, 0); if (rec->disk_start == NULL_BLOCK) f->fst->fop = 0; else f->fst->fop = REL(rec->disk_start); return 0; } /* allocate root block for fst */ dst = get_reserved_block(); f->fst->fop = REL(dst); return f->fops->write_pointers(f, f->fst->levels, dst, 0); } /* * Guess position in record table. */ static int guess_record_number(struct file *f, off_t offset) { int nr; if (f->linefeed) nr = (offset / (f->fst->record_len + 1)); else nr = (offset / f->fst->record_len); if (nr >= f->fst->nr_records) nr = f->fst->nr_records - 1; return nr; } static int offset_is_linefeed(off_t offset, struct record *prev, struct record *next) { if ((offset < next->file_start) && (offset >= prev->file_start + prev->total_len)) return 1; return 0; } static int offset_in_record(off_t offset, struct record *rec) { if (offset >= rec->file_start && offset < rec->file_start + rec->total_len) return 1; return 0; } static void set_hint(struct file *f, int hint) { f->next_record_hint = hint; /* limit hint to last record */ if (f->next_record_hint >= f->fst->nr_records) f->next_record_hint = f->fst->nr_records - 1; } /* * Find record by file offset. * * Returns: record number in *nr * > 0 : ptr to found record * -1 : linefeed offset * NULL : error */ static struct record *find_record(struct file *f, off_t offset, int *nr) { int i, start, step, max = f->fst->nr_records; struct record *rec; /* * next_record_hint is a guess which is optimal for sequential * single-threaded reads. */ i = f->next_record_hint; rec = &f->rlist[i]; if (offset_in_record(offset, rec)) { /* increment hint for sequential read, fails for extensions */ set_hint(f, i + 1); *nr = i; return rec; } /* look out for previous record linefeed from sequential read hint */ if (f->linefeed && i > 0) if (offset_is_linefeed(offset, &f->rlist[i - 1], rec)) return LINEFEED_OFFSET; start = guess_record_number(f, offset); /* because of potential linefeed we need to check the next record */ rec = &f->rlist[start]; if (offset < rec->file_start) step = -1; else step = 1; for (i = start; i >= 0 && i < max; i += step) { rec = &f->rlist[i]; if (offset_in_record(offset, rec)) { set_hint(f, i + 1); *nr = i; return rec; } /* last record reached, only one linefeed can follow */ if (i == max - 1) { if (f->linefeed && (offset == rec->file_start + rec->total_len)) return LINEFEED_OFFSET; else return NULL; } /* check for linefeed byte between two records */ if (step == 1) { if (offset_is_linefeed(offset, rec, &f->rlist[i + 1])) return LINEFEED_OFFSET; } else /* * No need to check if i > 0 since no linefeed before * record 0 possible. */ if (offset_is_linefeed(offset, &f->rlist[i - 1], rec)) return LINEFEED_OFFSET; } DEBUG("find: record not found!\n"); return NULL; } /* * Get disk address and block size from a record. */ static void get_block_data_from_record(struct record *rec, off_t offset, off_t *addr, int *chunk) { int record_off = offset - rec->file_start; struct record_ext *rext; if (record_off >= rec->first_block_len) { record_off -= rec->first_block_len; rext = rec->ext; BUG(rext == NULL); while (record_off >= rext->len) { record_off -= rext->len; rext = rext->next; BUG(rext == NULL); } /* found the right record extension */ if (rext->disk_start == NULL_BLOCK) *addr = NULL_BLOCK; else *addr = rext->disk_start + record_off; *chunk = rext->len - record_off; } else { if (rec->disk_start == NULL_BLOCK) *addr = NULL_BLOCK; else *addr = rec->disk_start + record_off; *chunk = rec->first_block_len - record_off; } } static int convert_text(iconv_t conv, char *in_buf, char *out_buf, int size) { size_t out_count = size; size_t in_count = size; int rc; rc = iconv(conv, &in_buf, &in_count, &out_buf, &out_count); if ((rc == -1) || (in_count != 0)) { DEBUG("Code page translation EBCDIC-ASCII failed\n"); return -EIO; } return 0; } static int cmsfs_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { struct file *f = get_fobj(fi); size_t len, copied = 0; struct record *rec; int chunk, nr, rc; off_t addr; (void) path; len = f->session_size; if ((size_t) offset >= len) return 0; if (offset + size > len) size = len - offset; while (size > 0) { rec = find_record(f, offset, &nr); if (rec == NULL) { copied = -EINVAL; DEBUG("%s: invalid addr, size: %lu copied: %lu len: %lu\n", __func__, size, copied, len); goto out; } /* write linefeed directly to buffer and go to next record */ if (rec == LINEFEED_OFFSET) { BUG(!f->linefeed); if (f->translate) *buf = LINEFEED_ASCII; else *buf = LINEFEED_EBCDIC; buf++; copied++; offset++; size--; continue; } /* get addr and block size from record */ get_block_data_from_record(rec, offset, &addr, &chunk); if (chunk <= 0 || addr < 0) DIE("Invalid record data\n"); /* copy less if there is not enough space in the fuse buffer */ if (size < (size_t) chunk) chunk = size; /* read one record */ if (addr == NULL_BLOCK) memset(buf, 0, chunk); else if (f->translate) { rc = _read(f->iconv_buf, chunk, addr); if (rc < 0) return rc; rc = convert_text(cmsfs.iconv_from, f->iconv_buf, buf, chunk); if (rc < 0) return rc; } else { rc = _read(buf, chunk, addr); if (rc < 0) return rc; } copied += chunk; size -= chunk; buf += chunk; offset += chunk; } out: DEBUG("%s: copied: %lu\n", __func__, copied); return copied; } static int cmsfs_statfs(const char *path, struct statvfs *buf) { unsigned int inode_size = cmsfs.blksize + sizeof(struct fst_entry); unsigned int free_blocks = cmsfs.total_blocks - cmsfs.used_blocks; unsigned long long tmp; (void) path; buf->f_bsize = buf->f_frsize = cmsfs.blksize; buf->f_blocks = cmsfs.total_blocks; buf->f_bfree = buf->f_bavail = free_blocks; /* number of possible inodes */ tmp = (unsigned long long) cmsfs.total_blocks * cmsfs.blksize / inode_size; buf->f_files = (long) tmp; tmp = (unsigned long long) free_blocks * cmsfs.blksize / inode_size; buf->f_ffree = (long) tmp; buf->f_namemax = MAX_FNAME - 1; return 0; } static int cmsfs_utimens(const char *path, const struct timespec ts[2], struct fuse_file_info *fi) { struct fst_entry fst; off_t fst_addr; struct tm tm; int rc; (void) fi; if (cmsfs.readonly) return -EACCES; fst_addr = lookup_file(path + 1, &fst, HIDE_UNLINKED); if (!fst_addr) return -ENOENT; /* convert timespec to tm */ memset(&tm, 0, sizeof(struct tm)); if (localtime_r(&ts[0].tv_sec, &tm) == NULL) return -EINVAL; update_fst_date(&fst, &tm); rc = _write(&fst, sizeof(fst), fst_addr); BUG(rc < 0); return 0; } /* * Get the address of the last directory entry. */ static off_t find_last_fdir_entry(off_t addr, int level) { struct fst_entry fst; int left, rc; off_t ptr; if (level > 0) { level--; left = PTRS_PER_BLOCK; while (left--) { ptr = get_fixed_pointer(addr + left * PTR_SIZE); BUG(ptr < 0); if (ptr) return find_last_fdir_entry(ptr, level); } DIE("Directory entry not found\n"); return 0; } left = cmsfs.blksize / sizeof(struct fst_entry); while (left--) { rc = _read(&fst, sizeof(fst), addr + left * sizeof(struct fst_entry)); BUG(rc < 0); if (is_file(fst.name, fst.type)) return addr + left * sizeof(struct fst_entry); } DIE("Directory entry not found\n"); } static int delete_file(const char *path) { off_t fst_kill, fst_last; struct walk_file walk; struct file *f_moved; char file[MAX_FNAME]; struct fst_entry fst; struct file *f; int rc = 0, i; if (cmsfs.readonly) return -EROFS; fst_kill = lookup_file(path + 1, &fst, SHOW_UNLINKED); if (!fst_kill) return -ENOENT; f = create_file_object(&fst, &rc); if (f == NULL) return rc; /* delete all data blocks */ for (i = 0; i < f->fst->nr_blocks; i++) free_block(f->blist[i].disk_addr); if (f->fst->fop) { rc = f->fops->delete_pointers(f, f->fst->levels, ABS(f->fst->fop)); if (rc < 0) goto error; } if (cmsfs.dir_levels) fst_last = find_last_fdir_entry(get_fop(cmsfs.fdir), cmsfs.dir_levels); else fst_last = find_last_fdir_entry(cmsfs.fdir, cmsfs.dir_levels); /* remove unlinked file from fcache */ util_strlcpy(file, path + 1, MAX_FNAME); str_toupper(file); invalidate_htab_entry(file); if (fst_last == fst_kill) goto skip_copy; /* copy last entry over unlinked entry */ rc = _read(&fst, sizeof(struct fst_entry), fst_last); BUG(rc < 0); rc = _write(&fst, sizeof(struct fst_entry), fst_kill); BUG(rc < 0); /* update moved fcache entry */ memset(file, 0, sizeof(file)); decode_edf_name(file, fst.name, fst.type); update_htab_entry(fst_kill, file); /* update cached address of moved FST */ f_moved = file_open(file); if (f_moved != NULL) f->fst_addr = fst_kill; skip_copy: /* delete last entry */ rc = _zero(fst_last, sizeof(struct fst_entry)); BUG(rc < 0); /* if the deleted entry was the first of a block, free the block */ if (fst_last % cmsfs.blksize == 0) { cache_dblocks(&walk); /* delete the last block from dlist */ walk.dlist_used--; free_block(fst_last); purge_dblock_ptrs(cmsfs.dir_levels, get_fop(cmsfs.fdir)); rewrite_dblock_ptrs(&walk); free_dblocks(&walk); } destroy_file_object(f); decrease_file_count(); update_block_count(); return 0; error: destroy_file_object(f); return rc; } static int cmsfs_rename(const char *path, const char *new_path, unsigned int flags) { struct fst_entry fst, fst_new; off_t fst_addr, fst_addr_new; char uc_old_name[MAX_FNAME]; char fname[8], ftype[8]; char *uc_new_name; struct file *f; int rc; (void) flags; if (cmsfs.readonly) return -EACCES; fst_addr = lookup_file(path + 1, &fst, HIDE_UNLINKED); if (!fst_addr) return -ENOENT; /* if new file already exists it must be overwritten so delete it */ fst_addr_new = lookup_file(new_path + 1, &fst_new, HIDE_UNLINKED); if (fst_addr_new) { delete_file(new_path); /* fst_addr may have changed due to copy-up */ fst_addr = lookup_file(path + 1, &fst, HIDE_UNLINKED); } rc = edf_name_valid(new_path + 1); if (rc) return rc; /* force uppercase */ uc_new_name = strdup(new_path + 1); if (uc_new_name == NULL) return -ENOMEM; str_toupper(uc_new_name); rc = encode_edf_name(uc_new_name, fname, ftype); free(uc_new_name); if (rc) return rc; memcpy(&fst.name[0], fname, 8); memcpy(&fst.type[0], ftype, 8); util_strlcpy(uc_old_name, path + 1, MAX_FNAME); str_toupper(uc_old_name); invalidate_htab_entry(uc_old_name); /* update name in file object if the file is opened */ f = file_open(uc_old_name); if (f != NULL) { util_strlcpy(f->path, new_path, MAX_FNAME + 1); str_toupper(f->path); memcpy(f->fst->name, fname, 8); memcpy(f->fst->type, ftype, 8); } rc = _write(&fst, sizeof(fst), fst_addr); BUG(rc < 0); return 0; } static int cmsfs_fsync(const char *path, int datasync, struct fuse_file_info *fi) { (void) path; (void) datasync; (void) fi; if (cmsfs.readonly) return -EROFS; return msync(cmsfs.map, cmsfs.size, MS_SYNC); } /* * Detect whether the whole block can be freed. */ static int block_started(struct file *f, struct record *rec) { if (rec->disk_start == NULL_BLOCK) { if (rec->null_block_started) return 1; else return 0; } if (f->fst->record_format == RECORD_LEN_FIXED) { if (rec->disk_start % cmsfs.blksize == 0) return 1; else return 0; } if (f->fst->record_format == RECORD_LEN_VARIABLE) { if ((rec->disk_start % cmsfs.blksize == 0) || (rec->disk_start % cmsfs.blksize == 1) || (rec->disk_start % cmsfs.blksize == 2)) return 1; else return 0; } return 0; } /* * Note: only called for the very last record of a file. That means if the * data starts on a block offset the block can be freed. It does not free * header bytes for variable record files if they are on the previous block! */ static void free_record(struct file *f, struct record *rec) { struct record_ext *rext = rec->ext; f->fst->nr_records--; if (block_started(f, rec)) { free_block(rec->disk_start); f->fst->nr_blocks--; if (!rec->disk_start && f->fst->record_format == RECORD_LEN_FIXED) f->nr_null_blocks--; } while (rext != NULL) { /* extensions always start on a new block */ free_block(rext->disk_start); f->fst->nr_blocks--; if (!rext->disk_start && f->fst->record_format == RECORD_LEN_FIXED) f->nr_null_blocks--; rext = rext->next; } } static int update_var_header_len(struct file *f, u16 *header, struct record *rec) { off_t prev_block_end; int rc, split = 0; if (rec->disk_start % cmsfs.blksize == 0) split = 2; if (rec->disk_start % cmsfs.blksize == 1) split = 1; /* header is completely in this block */ if (!split) { rc = _write(header, sizeof(*header), rec->disk_start - sizeof(*header)); if (rc < 0) return rc; return 0; } BUG(!rec->block_nr); prev_block_end = f->blist[rec->block_nr - 1].disk_addr | DATA_BLOCK_MASK; if (split == 1) { rc = _write((char *) header + 1, 1, rec->disk_start - 1); if (rc < 0) return rc; rc = _write((char *) header, 1, prev_block_end); if (rc < 0) return rc; return 0; } if (split == 2) { rc = _write(header, sizeof(*header), prev_block_end - 1); if (rc < 0) return rc; return 0; } return 0; } /* * Update the displacement of the last block if the block was spanned and * the new end is inside the previously spanned block. The displacement * must point after the last record to a null length header. * If the block wasn't spanned the displacement of the trimmed record needs * no update. */ static void adjust_displacement(struct file *f, int bnr, unsigned int disp) { if (f->blist[bnr].disp == VAR_RECORD_SPANNED) f->blist[bnr].disp = disp; } /* * Split the last record if needed and wipe until block end. * offset points to the last byte of the trimmed record that is * not a line feed. */ static int trim_record(struct file *f, off_t offset, struct record *rec) { int rc, wipe_off, wipe_len, free = 0; off_t file_start = rec->file_start; struct record_ext *rext; u16 header; BUG(!offset_in_record(offset, rec)); if (offset >= rec->file_start && offset < rec->file_start + rec->first_block_len) { wipe_off = offset + 1 - rec->file_start; wipe_len = cmsfs.blksize - ((rec->disk_start & DATA_BLOCK_MASK) + wipe_off); if (!wipe_len) goto ext; if (rec->disk_start) { rc = _zero(rec->disk_start + wipe_off, wipe_len); BUG(rc < 0); } if (f->fst->record_format == RECORD_LEN_VARIABLE) adjust_displacement(f, rec->block_nr, (rec->disk_start + wipe_off) & DATA_BLOCK_MASK); free = 1; } ext: if (rec->ext == NULL) goto header; file_start += rec->first_block_len; rext = rec->ext; do { if (free) { free_block(rext->disk_start); f->fst->nr_blocks--; if (!rext->disk_start && f->fst->record_format == RECORD_LEN_FIXED) f->nr_null_blocks--; } else { if (offset >= file_start && offset < file_start + rext->len) { wipe_off = offset + 1 - file_start; wipe_len = cmsfs.blksize - ((rec->disk_start & DATA_BLOCK_MASK) + wipe_off); if (!wipe_len) continue; if (rext->disk_start) { rc = _zero(rext->disk_start + wipe_off, wipe_len); BUG(rc < 0); } if (f->fst->record_format == RECORD_LEN_VARIABLE) adjust_displacement(f, rext->block_nr, (rext->disk_start + wipe_off) & DATA_BLOCK_MASK); free = 1; } } file_start += rext->len; rext = rext->next; } while (rext != NULL); header: /* update variable record header with new record length */ if (f->fst->record_format == RECORD_LEN_VARIABLE) { header = offset + 1 - rec->file_start; rc = update_var_header_len(f, &header, rec); if (rc < 0) return rc; /* update total_len in rlist, needed to recalculate lrecl */ rec->total_len = header; } return 0; } /* * Update levels count. */ static void update_levels(struct file *f) { int per_block = f->ptr_per_block; int levels = 1, blocks = f->fst->nr_blocks; if (blocks < 2) { f->fst->levels = 0; return; } while (blocks / per_block) { levels++; blocks /= per_block; } f->fst->levels = levels; } /* * Called by write only using the cached value. */ static void update_lrecl_fast(struct file *f, int rlen) { if (rlen > (int) f->fst->record_len) f->fst->record_len = rlen; } /* * Update longest record length for variable files. */ static void update_lrecl(struct file *f) { unsigned int lrecl = 0; struct record *rec; int i; if (f->fst->record_format == RECORD_LEN_FIXED) return; if (!f->fst->nr_records) { f->fst->record_len = 0; return; } for (i = 0; i < f->fst->nr_records; i++) { rec = get_record(f, i); if (rec->total_len > lrecl) lrecl = rec->total_len; } f->fst->record_len = lrecl; } static int shrink_file(struct file *f, off_t size) { struct record *new_end_rec, *end_rec; int rlen = f->fst->record_len; off_t offset = size; int new_end_nr, rc; /* truncate MUST be aligned to record length for fixed files */ if (f->fst->record_format == RECORD_LEN_FIXED) { if (f->linefeed) rlen++; if (size % rlen) return -EINVAL; } if (size == 0) { new_end_nr = -1; new_end_rec = NULL; goto free; } /* * offset may point to the linefeed after a record, let it point to the * last byte of the new last record instead. The linefeed is virtual * and will be generated automatically. */ if (f->linefeed) { new_end_rec = find_record(f, offset - 1, &new_end_nr); if (new_end_rec == LINEFEED_OFFSET) offset--; } /* get the new last record of the file */ new_end_rec = find_record(f, offset - 1, &new_end_nr); BUG(new_end_rec == NULL || new_end_rec == LINEFEED_OFFSET); free: /* free from the end until new_end_rec */ while (f->fst->nr_records - 1 > new_end_nr) { /* get the currently last record of the file */ end_rec = get_record(f, f->fst->nr_records - 1); free_record(f, end_rec); } if (new_end_rec != NULL) { rc = trim_record(f, offset - 1, new_end_rec); if (rc < 0) return rc; } f->session_size = size; if (f->fst->fop) f->fops->delete_pointers(f, f->fst->levels, ABS(f->fst->fop)); update_levels(f); update_lrecl(f); rc = rewrite_pointers(f); if (rc < 0) return rc; update_block_count(); return 0; } static void hide_null_blocks(struct file *f) { if (f->fst->record_format == RECORD_LEN_VARIABLE) return; f->fst->nr_blocks -= f->nr_null_blocks; } static void unhide_null_blocks(struct file *f) { if (f->fst->record_format == RECORD_LEN_VARIABLE) return; f->fst->nr_blocks += f->nr_null_blocks; } static void update_fst(struct file *f, off_t addr) { int rc; hide_null_blocks(f); rc = _write(f->fst, sizeof(*f->fst), addr); BUG(rc < 0); unhide_null_blocks(f); } static int cmsfs_truncate(const char *path, off_t size, struct fuse_file_info *fi) { struct fst_entry fst; off_t fst_addr, len; struct file *f; int rc = 0; (void) fi; if (cmsfs.readonly) return -EROFS; /* * If file is opened and modified disk content may be obsolete. * Must use the file object to get the current version of the file. */ f = file_open(path + 1); if (f != NULL) { fst_addr = f->fst_addr; len = f->session_size; } else { fst_addr = lookup_file(path + 1, &fst, HIDE_UNLINKED); if (!fst_addr) return -ENOENT; len = get_file_size_logical(&fst); if (len < 0) return -EIO; f = create_file_object(&fst, &rc); if (f == NULL) return rc; } if (len == size) return 0; if (size < len) { rc = shrink_file(f, size); if (rc != 0) return rc; } else return -EINVAL; rc = set_fst_date_current(f->fst); if (rc != 0) return rc; update_fst(f, fst_addr); if (!f->use_count) destroy_file_object(f); return rc; } #ifdef HAVE_SETXATTR static int cmsfs_setxattr(const char *path, const char *name, const char *value, size_t size, int flags) { struct fst_entry fst; off_t fst_addr; int mode_n, rc; long int rlen; char mode_l; char *in; /* meaningless since our xattrs are virtual and not stored on disk */ (void) flags; if (cmsfs.readonly) return -EROFS; fst_addr = lookup_file(path + 1, &fst, HIDE_UNLINKED); if (!fst_addr) return -ENOENT; if (strcmp(name, xattr_format.name) == 0) { /* only allowed for empty files */ if (fst.nr_records != 0) return -EINVAL; if (size != xattr_format.size) return -ERANGE; if (*value == 'F') { fst.record_format = RECORD_LEN_FIXED; fst.ptr_size = 0x4; } else if (*value == 'V') { fst.record_format = RECORD_LEN_VARIABLE; fst.ptr_size = 0xc; } else return -EINVAL; goto write; } if (strcmp(name, xattr_lrecl.name) == 0) { /* done by fs for variable files */ if (fst.record_format == RECORD_LEN_VARIABLE) return -EINVAL; /* only allowed for empty files */ if (fst.nr_records != 0) return -EINVAL; if (size > xattr_lrecl.size) return -ERANGE; in = calloc(size + 1, 1); if (in == NULL) return -ENOMEM; memcpy(in, value, size); errno = 0; rlen = strtol(in, (char **) NULL, 10); free(in); if (errno != 0 || rlen == 0 || rlen > MAX_RECORD_LEN) return -EINVAL; fst.record_len = rlen; goto write; } if (strcmp(name, xattr_mode.name) == 0) { if (size != xattr_mode.size) return -ERANGE; mode_l = value[0]; if (mode_l < 'A' || mode_l > 'Z') return -EINVAL; mode_n = atoi(&value[1]); if (!isdigit(value[1]) || mode_n < 0 || mode_n > 6) return -EINVAL; ebcdic_enc((char *) &fst.mode, value, sizeof(fst.mode)); goto write; } return -ENODATA; write: rc = _write(&fst, sizeof(fst), fst_addr); BUG(rc < 0); return 0; } static int cmsfs_getxattr(const char *path, const char *name, char *value, size_t size) { char buf[xattr_lrecl.size + 1]; struct fst_entry fst; /* nothing for root directory but clear error code needed */ if (strcmp(path, "/") == 0) return -ENODATA; if (!lookup_file(path + 1, &fst, HIDE_UNLINKED)) return -ENOENT; /* null terminate strings */ memset(value, 0, size); /* format */ if (strcmp(name, xattr_format.name) == 0) { if (size == 0) return xattr_format.size; if (size < xattr_format.size) return -ERANGE; if (fst.record_format == RECORD_LEN_FIXED) *value = 'F'; else if (fst.record_format == RECORD_LEN_VARIABLE) *value = 'V'; return xattr_format.size; } /* lrecl */ if (strcmp(name, xattr_lrecl.name) == 0) { if (size == 0) return xattr_lrecl.size; if (size < xattr_lrecl.size) return -ERANGE; memset(buf, 0, sizeof(buf)); snprintf(buf, sizeof(buf), "%d", fst.record_len); memcpy(value, buf, strlen(buf)); return strlen(buf); } /* mode */ if (strcmp(name, xattr_mode.name) == 0) { if (size == 0) return xattr_mode.size; if (size < xattr_mode.size) return -ERANGE; ebcdic_dec(value, (char *) &fst.mode, 2); return xattr_mode.size; } return -ENODATA; } static int cmsfs_listxattr(const char *path, char *list, size_t size) { struct fst_entry fst; size_t list_len; int pos = 0; if (!lookup_file(path + 1, &fst, HIDE_UNLINKED)) return -ENOENT; list_len = strlen(xattr_format.name) + 1 + strlen(xattr_lrecl.name) + 1 + strlen(xattr_mode.name); if (!size) return list_len; if (size < list_len) return -ERANGE; strcpy(list, xattr_format.name); pos += strlen(xattr_format.name) + 1; strcpy(&list[pos], xattr_lrecl.name); pos += strlen(xattr_lrecl.name) + 1; strcpy(&list[pos], xattr_mode.name); pos += strlen(xattr_mode.name) + 1; return pos; } #endif /* HAVE_SETXATTR */ /* * Return the number of unused bytes in the last block. */ static int examine_last_block(struct file *f) { struct record_ext *rext; struct record *rec; int last_bnr, len; off_t start; if (!f->fst->nr_blocks || !f->fst->nr_records) return 0; /* * For subsequent writes we know how much is left on the current block. * */ if (f->wstate->block_state != BWS_BLOCK_NOT_INIT) return f->wstate->block_free; last_bnr = f->fst->nr_blocks - 1; rec = &f->rlist[f->fst->nr_records - 1]; /* last block may be an extension */ if (rec->block_nr == last_bnr) { start = rec->disk_start; len = rec->first_block_len; goto out; } /* if write is split and exactly the extension not yet started */ if (rec->ext == NULL) return 0; rext = rec->ext; do { if (rext->block_nr == last_bnr) { start = rext->disk_start; len = rext->len; goto out; } rext = rext->next; } while (rext != NULL); return 0; out: if (start == NULL_BLOCK) start = f->null_ctr; return ((start | DATA_BLOCK_MASK) + 1) - (start + len); } static int get_record_len(struct file *f, size_t size) { int chunk = 0; if (f->fst->record_format == RECORD_LEN_FIXED) { if (!f->fst->record_len) { chunk = (size > MAX_RECORD_LEN) ? MAX_RECORD_LEN : size; f->fst->record_len = chunk; } else chunk = f->fst->record_len; if (chunk > MAX_RECORD_LEN) return -EINVAL; if (size < (size_t) chunk) chunk = size; } else if (f->fst->record_format == RECORD_LEN_VARIABLE) { chunk = size; if (chunk > MAX_RECORD_LEN) chunk = MAX_RECORD_LEN; } return chunk; } /* * Get the disk address of the first byte after the last record. */ static off_t disk_end(struct file *f) { struct record_ext *rext; struct record *rec; if (!f->fst->nr_records) return 0; /* * Only the first write on a newly opened file should set the write_ptr * according to what is on the disk. Subsequent writes should use the * actual write_ptr. This avoids the problem for fixed records that * for a record that is not yet completely written the calculated * write_ptr would be wrong. */ if (f->wstate->block_state != BWS_BLOCK_NOT_INIT) return f->write_ptr; rec = &f->rlist[f->fst->nr_records - 1]; if (rec->ext == NULL) { if (rec->disk_start == NULL_BLOCK) return 0; else return rec->disk_start + rec->first_block_len; } rext = rec->ext; while (rext->next != NULL) rext = rext->next; if (rext->disk_start == NULL_BLOCK) return 0; else return rext->disk_start + rext->len; } /* * Store the displacement for the first write to a new block. * nr_blocks already contains the new block. If the block is completely filled * the displacement is spanned, also if one header byte is used * since the header belongs to the record regarding the * displacement but not if both header bytes are on the block. */ void store_displacement(struct file *f, int disp) { f->blist[f->fst->nr_blocks - 1].disp = disp; f->wstate->block_state = BWS_BLOCK_USED; } /* * Allocate a new block if needed, set write pointer and return the number * of bytes available on the block. */ static int write_prepare_block(struct file *f, int null_block, ssize_t size) { int len, new_block = 0; if (null_block) { /* * TODO: need to write header before killing write_ptr for * sparse files. */ BUG(f->fst->record_format == RECORD_LEN_VARIABLE); f->write_ptr = 0; /* new null block started ? */ if (f->null_ctr % cmsfs.blksize == 0) { new_block = 1; len = cmsfs.blksize; /* * Prevent allocating a null block if the block would * not be complete. Use a normal block instead. */ if (size < cmsfs.blksize) { f->write_ptr = get_zero_block(); if (f->write_ptr < 0) return f->write_ptr; } } else len = ((f->null_ctr | DATA_BLOCK_MASK) + 1) - f->null_ctr; } else { if (f->write_ptr % cmsfs.blksize == 0) { /* * For fixed files use a different padding in text * mode to pad records with spaces. */ if (f->fst->record_format == RECORD_LEN_FIXED && f->translate) f->write_ptr = get_filled_block(); else f->write_ptr = get_zero_block(); if (f->write_ptr < 0) { /* reset to catch subsequent writes */ f->write_ptr = 0; return -ENOSPC; } new_block = 1; len = cmsfs.blksize; } else len = ((f->write_ptr | DATA_BLOCK_MASK) + 1) - f->write_ptr; } if (new_block) { f->wstate->block_state = BWS_BLOCK_NEW; f->blist[f->fst->nr_blocks].disk_addr = f->write_ptr; if (!f->write_ptr && f->fst->record_format == RECORD_LEN_FIXED) f->nr_null_blocks++; f->wstate->block_free = cmsfs.blksize; f->fst->nr_blocks++; } return len; } /* * Write variable record length header and return number of written bytes. */ static int write_var_header(struct file *f, int len, u16 vheader) { u8 half_vheader; int rc; if (f->wstate->var_record_state == RWS_HEADER_COMPLETE || f->wstate->var_record_state == RWS_RECORD_INCOMPLETE) return 0; if (f->wstate->var_record_state == RWS_HEADER_STARTED) { /* write secord header byte */ half_vheader = vheader & 0xff; rc = _write(&half_vheader, 1, f->write_ptr); if (rc < 0) return rc; f->write_ptr++; f->wstate->block_free--; f->wstate->var_record_state = RWS_HEADER_COMPLETE; f->wstate->var_records_written++; return 1; } /* block cannot be spanned if a header starts on it */ if (f->wstate->block_state == BWS_BLOCK_NEW) store_displacement(f, f->write_ptr & DATA_BLOCK_MASK); if (len >= 2) { rc = _write(&vheader, 2, f->write_ptr); if (rc < 0) return rc; f->write_ptr += 2; f->wstate->block_free -= 2; f->wstate->var_record_len = vheader; f->wstate->var_record_state = RWS_HEADER_COMPLETE; f->wstate->var_records_written++; return 2; } else { /* len = 1, write first header byte */ half_vheader = vheader >> 8; rc = _write(&half_vheader, 1, f->write_ptr); if (rc < 0) return rc; f->write_ptr++; f->wstate->block_free--; f->wstate->var_record_len = vheader; f->wstate->var_record_state = RWS_HEADER_STARTED; return 1; } } static int extend_block_fixed(struct file *f, const char *buf, int len, size_t size, int rlen) { int rc; (void) rlen; if (size < (size_t) len) len = size; if (f->write_ptr) { rc = _write(buf, len, f->write_ptr); if (rc < 0) return rc; f->write_ptr += len; f->wstate->block_free -= len; } return len; } static int extend_block_variable(struct file *f, const char *buf, int len, size_t size, int rlen) { int rc, copied = 0, vh_len = 0, max = cmsfs.blksize; if (!f->write_ptr) return len; while (len > 0) { /* record may be party written already */ if (size < (size_t) rlen) rlen = size; vh_len = write_var_header(f, len, rlen); if (vh_len < 0) return vh_len; len -= vh_len; if (!len) return copied; /* record does not fit on block */ if (len < rlen) rlen = len; /* remaining record data less than block len */ if (f->wstate->var_record_len < rlen) rlen = f->wstate->var_record_len; rc = _write(buf, rlen, f->write_ptr); if (rc < 0) return rc; f->write_ptr += rlen; f->wstate->block_free -= rlen; if (f->wstate->block_state == BWS_BLOCK_NEW) { /* * If the second byte of a split header was written * (blocksize - 1) is enough to make the block spanned. */ if (vh_len == 1) max--; if (rlen >= max) store_displacement(f, VAR_RECORD_SPANNED); else store_displacement(f, f->write_ptr & DATA_BLOCK_MASK); } copied += rlen; size -= rlen; len -= rlen; f->wstate->var_record_len -= rlen; BUG(f->wstate->var_record_len < 0); if (!f->wstate->var_record_len) { f->wstate->var_record_state = RWS_RECORD_COMPLETE; /* reset rlen for the next record */ rlen = get_record_len(f, size); } DEBUG("%s: wrote %d record bytes\n", __func__, rlen); if (size == 0) return copied; } /* record is not yet finished */ f->wstate->var_record_state = RWS_RECORD_INCOMPLETE; return copied; } /* * Extend an existing block or write data on a new block. * * size: requestes bytes to write to disk * rlen: projected record len * len: bytes left on the block */ static int extend_block(struct file *f, const char *buf, size_t size, int rlen) { int len = write_prepare_block(f, (buf == NULL) ? 1 : 0, size); if (len < 0) return -ENOSPC; BUG(!len); return f->fops->write_data(f, buf, len, size, rlen); } /* * Delete the record data from rlist and free extensions. */ static void delete_record(struct file *f, int nr) { struct record *rec = &f->rlist[nr]; struct record_ext *tmp, *rext = rec->ext; memset(rec, 0, sizeof(struct record)); while (rext != NULL) { tmp = rext->next; free(rext); rext = tmp; } } /* * Update records from a start record to the end. The start record is one less * than the previous last record since the previous last record may be * incomplete. */ static int update_records(struct file *f, int nrecords) { int i, rc, rnr, bnr = 0, skip = 0; off_t total = 0; rnr = f->fst->nr_records - 1; if (rnr >= 0) { total = f->rlist[rnr].file_start; if (f->linefeed && total) total--; bnr = f->rlist[rnr].block_nr; skip = f->rlist[rnr].disk_start & DATA_BLOCK_MASK; /* skip must point before a variable header */ if (f->fst->record_format == RECORD_LEN_VARIABLE) { if (skip >= VAR_RECORD_HEADER_SIZE) skip -= VAR_RECORD_HEADER_SIZE; else if (skip == 1) { bnr--; skip = cmsfs.blksize - 1; } else if (skip == 0 && f->rlist[rnr].disk_start) { bnr--; skip = cmsfs.blksize - 2; } } delete_record(f, rnr); rnr--; } if (rnr < -1) { rnr = -1; skip = 0; total = 0; bnr = 0; } f->fst->nr_records += nrecords; f->record_scan_state = RSS_DATA_BLOCK_STARTED; for (i = bnr; i < f->fst->nr_blocks; i++) { if (f->fst->record_format == RECORD_LEN_FIXED) cache_fixed_data_block(f, f->blist[i].disk_addr + skip, &bnr, &rnr, &total, skip); else { rc = cache_variable_data_block(f, f->blist[i].disk_addr + skip, &bnr, &rnr, f->blist[i].disp, &total, skip); if (rc < 0) return rc; } skip = 0; } return 0; } /* * Calculate the number of new records. */ static int new_records(struct file *f, size_t size, int rlen) { double tmp = size; int len; if (f->fst->record_format == RECORD_LEN_FIXED) { len = f->fst->record_len; if (f->linefeed) len++; /* need to fill a previously started record first */ if (f->session_size && f->session_size % len) tmp = tmp - (len - (f->session_size % len)); } if (tmp <= 0) return 0; tmp = ceil(tmp / rlen); return (int) tmp; } /* * Calculate number of new blocks. */ static int new_blocks(struct file *f, size_t size, int last, int nrecords) { int last_usable = last; double tmp = size; /* subtract header bytes */ if (last_usable && f->fst->record_format == RECORD_LEN_VARIABLE) { if (last_usable == 1) last_usable = 0; else last_usable -= VAR_RECORD_HEADER_SIZE; } if ((int) size <= last_usable) return 0; if (f->fst->record_format == RECORD_LEN_VARIABLE) tmp += (double) nrecords * VAR_RECORD_HEADER_SIZE; tmp -= last; if (tmp <= 0) return 0; tmp = ceil(tmp / cmsfs.blksize); return (int) tmp; } /* * Increase record list count. */ static void resize_rlist(struct file *f, int new) { if (!new) return; f->rlist = realloc(f->rlist, (f->fst->nr_records + new) * sizeof(struct record)); if (f->rlist == NULL) DIE_PERROR("realloc failed"); memset(&f->rlist[f->fst->nr_records], 0, new * sizeof(struct record)); } /* * Increase block list count. */ static void resize_blist(struct file *f, int new) { if (!new) return; f->blist = realloc(f->blist, (f->fst->nr_blocks + new) * sizeof(struct block)); if (f->blist == NULL) DIE_PERROR("realloc failed"); memset(&f->blist[f->fst->nr_blocks], 0, new * sizeof(struct block)); } /* * Reserve blocks for meta data (pointer blocks) of a file so that * blocks for meta-data are available if the disk runs full. */ static void reserve_meta_blocks(struct file *f, int old, int new, int level) { double nentries, oentries; int newp; if (!new) return; newp = pointers_per_level(f, level, old + new); oentries = pointers_per_level(f, level, old); oentries = ceil(oentries / f->ptr_per_block); nentries = newp; nentries = ceil(nentries / f->ptr_per_block); cmsfs.reserved_blocks += nentries - oentries; if (newp > f->ptr_per_block) reserve_meta_blocks(f, oentries, nentries, ++level); } /* * Update the max record number in the last pointer block per level. */ static int update_last_block_vptr(struct file *f, off_t addr, int level, struct var_ptr *vptr) { int last, rc; if (!level) return 0; level--; /* read offset pointer from the end of the var pointer block */ rc = _read(&last, sizeof(last), addr + cmsfs.blksize - sizeof(last)); if (rc < 0) return rc; if (last % sizeof(*vptr) || last > cmsfs.blksize) return -EIO; rc = _read(vptr, sizeof(*vptr), addr + last); if (rc < 0) return rc; /* update max record number */ vptr->hi_record_nr = f->fst->nr_records; rc = _write(vptr, sizeof(*vptr), addr + last); if (rc < 0) return rc; if (!level) return 0; if (vptr->next == NULL_BLOCK) return 0; return update_last_block_vptr(f, ABS(vptr->next), level, vptr); } static void reset_write_state(struct file *f) { f->wstate->var_record_state = RWS_RECORD_COMPLETE; f->wstate->var_record_len = 0; f->wstate->var_records_written = 0; } /* * Append records at current file end. If buf is NULL write zero bytes. */ static int write_append(struct file *f, const char *buf, size_t size) { int rc, i, nrecords, nblocks, last, len, copied = 0; int rlen = get_record_len(f, size); if (rlen < 0) return rlen; nrecords = new_records(f, size, rlen); /* get last block unused bytes for block count */ last = examine_last_block(f); /* initialize write_ptr once */ f->write_ptr = disk_end(f); nblocks = new_blocks(f, size, last, nrecords); if (nblocks > 0) f->ptr_dirty = 1; resize_rlist(f, nrecords); resize_blist(f, nblocks); if (f->fst->nr_blocks + nblocks > 1) reserve_meta_blocks(f, f->fst->nr_blocks, nblocks, 1); reset_write_state(f); /* first use existing last block */ if (last) { len = extend_block(f, buf, size, rlen); if (len < 0) return len; copied += len; size -= len; if (buf != NULL) buf += len; } for (i = 0; i < nblocks; i++) { len = extend_block(f, buf, size, rlen); if (len < 0) { if (copied > 0) { /* * Not all records may be written, need to * update nrecords to store a correct * hi_record_nr in the last vptr. */ if (f->fst->record_format == RECORD_LEN_VARIABLE) nrecords = f->wstate->var_records_written; goto out; } else return len; } copied += len; size -= len; if (buf != NULL) buf += len; DEBUG("%s: wrote: %d bytes\n", __func__, copied); } out: rc = update_records(f, nrecords); if (rc < 0) return rc; if (f->fst->record_format == RECORD_LEN_VARIABLE) update_lrecl_fast(f, rlen); return copied; } static int do_write(struct file *f, const char *buf, size_t size, off_t offset) { off_t len, copied = 0; struct var_ptr vptr; int rc; if (!size) return 0; len = f->session_size; if (f->linefeed) len -= f->fst->nr_records; BUG(len < 0); if (offset < len) return -EINVAL; /* * Writes with null blocks (sparse files) may be prevented by tools * which call lseek instead. Since we don't implement lseek fuse may * call us with an offset after file. We don't support sparse file * writes currently. */ if (offset > len) return -EINVAL; copied = write_append(f, buf, size); if (copied <= 0) return copied; f->session_size += copied; /* add linefeed byte */ if (f->linefeed) f->session_size++; if (f->ptr_dirty) { f->old_levels = f->fst->levels; update_levels(f); if (f->fst->fop) f->fops->delete_pointers(f, f->old_levels, ABS(f->fst->fop)); rc = rewrite_pointers(f); if (rc < 0) return rc; f->ptr_dirty = 0; } else if (f->fst->levels > 0 && f->fst->record_format == RECORD_LEN_VARIABLE) { rc = update_last_block_vptr(f, ABS(f->fst->fop), f->fst->levels, &vptr); if (rc < 0) return rc; } rc = set_fst_date_current(f->fst); if (rc != 0) return rc; update_fst(f, f->fst_addr); set_fdir_date_current(); update_block_count(); return copied; } static void cache_write_data(struct file *f, const char *buf, int len) { if (f->wcache_used + len > WCACHE_MAX) len = WCACHE_MAX - f->wcache_used; if (buf == NULL) memset(&f->wcache[f->wcache_used], FILLER_ASCII, len); else memcpy(&f->wcache[f->wcache_used], buf, len); f->wcache_used += len; } static void purge_wcache(struct file *f) { f->wcache_used = 0; f->wcache_commited = 0; } /* * Scan for the next newline character and return the number of bytes in * this record. */ static ssize_t find_newline(const char *buf, int len) { char *pos; pos = memchr(buf, LINEFEED_ASCII, len); if (pos == NULL) return LINEFEED_NOT_FOUND; else return pos - buf; } static int cmsfs_write_strings(struct file *f, const char *buf, size_t size, off_t offset) { int scan_len = MIN(size, (size_t)MAX_RECORD_LEN + 1); int rc, nl_byte = 1, null_record = 0, pad = 0; ssize_t rsize; /* remove already committed bytes */ offset -= f->wcache_commited; /* write offset must be at the end of the file */ if (offset + f->null_records + f->pad_bytes != f->session_size) return -EINVAL; if (f->fst->record_format == RECORD_LEN_FIXED && f->fst->record_len) scan_len = MIN(scan_len, f->fst->record_len + 1); rsize = find_newline(buf, scan_len); BUG(rsize < LINEFEED_NOT_FOUND); if (rsize == LINEFEED_NOT_FOUND) { if (f->wcache_used + scan_len >= WCACHE_MAX) { purge_wcache(f); return -EINVAL; } else { if (f->fst->record_format == RECORD_LEN_FIXED && f->wcache_commited + scan_len > f->fst->record_len) { purge_wcache(f); return -EINVAL; } cache_write_data(f, buf, scan_len); f->wcache_commited += scan_len; return scan_len; } } cache_write_data(f, buf, rsize); if (f->fst->record_format == RECORD_LEN_FIXED && f->wcache_used < f->fst->record_len) { pad = f->fst->record_len - f->wcache_used; cache_write_data(f, NULL, pad); } /* translate */ rc = convert_text(cmsfs.iconv_to, f->wcache, f->iconv_buf, f->wcache_used); if (rc < 0) return rc; /* * Note: empty records are forbidden by design. CMS converts * an empty record to a single space. Follow that convention. */ if (!f->wcache_used) { *f->wcache = FILLER_EBCDIC; *f->iconv_buf = FILLER_EBCDIC; f->wcache_used = 1; nl_byte = 0; null_record = 1; } /* correct file offset by removing the virtual linefeeds */ offset -= (f->fst->nr_records - f->null_records); offset += f->pad_bytes; BUG(offset < 0); rc = do_write(f, f->iconv_buf, f->wcache_used, offset); if (rc < 0) return rc; rc += nl_byte; if (null_record) f->null_records++; rc -= f->wcache_commited; rc -= pad; BUG(rc < 0); purge_wcache(f); f->pad_bytes += pad; return rc; } static int cmsfs_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { struct file *f = get_fobj(fi); int rc, written, nbytes; (void) path; if (cmsfs.readonly) return -EROFS; if (!f->linefeed) return do_write(f, buf, size, offset); /* Limit the size to what we can report back as written */ nbytes = MIN(size, (size_t) INT_MAX); written = 0; while (nbytes) { rc = cmsfs_write_strings(f, buf, nbytes, offset); if (rc < 0) return written ? written : rc; written += rc; offset += rc; buf += rc; nbytes -= rc; } return written; } static int cmsfs_unlink(const char *path) { struct fst_entry fst; off_t fst_addr; struct file *f; if (cmsfs.readonly) return -EROFS; fst_addr = lookup_file(path + 1, &fst, HIDE_UNLINKED); if (!fst_addr) return -ENOENT; f = file_open(path + 1); if (f != NULL) { f->unlinked = 1; return 0; } return delete_file(path); } static int flush_wcache(struct file *f) { off_t offset = f->session_size; int rc; /* translate */ rc = convert_text(cmsfs.iconv_to, f->wcache, f->iconv_buf, f->wcache_used); if (rc < 0) return rc; /* correct file offset by removing the virtual linefeeds */ offset -= (f->fst->nr_records - f->null_records); BUG(offset < 0); rc = do_write(f, f->iconv_buf, f->wcache_used, offset); purge_wcache(f); f->null_records = 0; if (rc < 0) return rc; return 0; } static int cmsfs_release(const char *path, struct fuse_file_info *fi) { struct file *f = get_fobj(fi); int rc = 0; (void) path; if (f == NULL) { DEBUG("release internal error\n"); return -EINVAL; } if (fi->flags & O_RDWR || fi->flags & O_WRONLY) { f->write_count--; if (f->wcache_used) rc = flush_wcache(f); } if (f->use_count == 1) { if (f->unlinked) delete_file(f->path); util_list_remove(&open_file_list, f); destroy_file_object(f); } else f->use_count--; fi->fh = 0; return rc; } static void init_fops(struct file *f) { if (f->fst->record_format == RECORD_LEN_FIXED) f->fops = &fops_fixed; else f->fops = &fops_variable; } /* * Create a file object to cache all needed file data. * Note: the caller must ensure that the file exists. */ static struct file *create_file_object(struct fst_entry *fst, int *rc) { struct file *f; f = malloc(sizeof(*f)); if (f == NULL) goto oom; memset(f, 0, sizeof(*f)); f->fst = malloc(sizeof(struct fst_entry)); if (f->fst == NULL) goto oom_f; memcpy(f->fst, fst, sizeof(*fst)); workaround_nr_blocks(f); init_fops(f); f->linefeed = linefeed_mode_enabled(f->fst); f->translate = f->linefeed; f->record_scan_state = RSS_DATA_BLOCK_STARTED; if (f->fst->record_format == RECORD_LEN_FIXED) f->ptr_per_block = cmsfs.fixed_ptrs_per_block; else f->ptr_per_block = cmsfs.var_ptrs_per_block; f->wstate = malloc(sizeof(*f->wstate)); if (f->wstate == NULL) goto oom_fst; memset(f->wstate, 0, sizeof(*f->wstate)); /* * Prevent calloc for zero records since it returns a pointer != NULL * which causes trouble at free. Also don't call cache_file. */ if (f->fst->nr_records == 0) return f; f->rlist = calloc(f->fst->nr_records, sizeof(struct record)); if (f->rlist == NULL) goto oom_wstate; f->blist = calloc(f->fst->nr_blocks, sizeof(struct block)); if (f->blist == NULL) goto oom_rlist; *rc = cache_file(f); if (*rc < 0) goto error; return f; error: if (*rc == 0) *rc = -ENOMEM; oom_rlist: free(f->rlist); oom_wstate: free(f->wstate); oom_fst: free(f->fst); oom_f: free(f); oom: return NULL; } static void destroy_file_object(struct file *f) { struct record_ext *rext, *tmp; struct record *rec; int i; free(f->iconv_buf); free(f->wcache); free(f->wstate); for (i = 0; i < f->fst->nr_records; i++) { rec = &f->rlist[i]; rext = rec->ext; while (rext != NULL) { tmp = rext->next; free(rext); rext = tmp; } } free(f->rlist); free(f->blist); free(f->fst); free(f); } static void *cmsfs_oper_init(struct fuse_conn_info *conn, struct fuse_config *cfg) { (void)conn; /* force immediate file removal */ cfg->hard_remove = 1; return NULL; } static struct file_operations fops_fixed = { .cache_data = cache_file_fixed, .write_data = extend_block_fixed, .delete_pointers = purge_pointer_block_fixed, .write_pointers = rewrite_pointer_block_fixed, }; static struct file_operations fops_variable = { .cache_data = cache_file_variable, .write_data = extend_block_variable, .delete_pointers = purge_pointer_block_variable, .write_pointers = rewrite_pointer_block_variable, }; static struct fuse_operations cmsfs_oper = { .init = cmsfs_oper_init, .getattr = cmsfs_getattr, .statfs = cmsfs_statfs, .readdir = cmsfs_readdir, .open = cmsfs_open, .release = cmsfs_release, .read = cmsfs_read, .utimens = cmsfs_utimens, .rename = cmsfs_rename, .fsync = cmsfs_fsync, .truncate = cmsfs_truncate, .create = cmsfs_create, .write = cmsfs_write, .unlink = cmsfs_unlink, #ifdef HAVE_SETXATTR .listxattr = cmsfs_listxattr, .getxattr = cmsfs_getxattr, .setxattr = cmsfs_setxattr, /* no removexattr since our xattrs are virtual */ #endif }; static int cmsfs_fuse_main(struct fuse_args *args, struct fuse_operations *cmsfs_oper) { #if FUSE_VERSION >= 26 return fuse_main(args->argc, args->argv, cmsfs_oper, NULL); #else return fuse_main(args->argc, args->argv, cmsfs_oper); #endif } static int cmsfs_process_args(void *data, const char *arg, int key, struct fuse_args *outargs) { (void) data; switch (key) { case FUSE_OPT_KEY_OPT: if (strcmp(arg, "allow_other") == 0) cmsfs.allow_other = 1; return 1; case FUSE_OPT_KEY_NONOPT: if (cmsfs.device == NULL) { cmsfs.device = strdup(arg); return 0; } return 1; case KEY_HELP: usage(outargs->argv[0]); /* * Usage output needs to go to stdout to be consistent with * coding guidelines. FUSE versions before 3.0.0 print help * output to stderr. Redirect stderr to stdout here to enforce * consistent behavior. */ fflush(stderr); dup2(STDOUT_FILENO, STDERR_FILENO); fuse_opt_add_arg(outargs, "-ho"); cmsfs_fuse_main(outargs, &cmsfs_oper); exit(0); case KEY_VERSION: fprintf(stdout, COMP "FUSE file system for CMS disks " "program version %s\n", RELEASE_STRING); fprintf(stdout, "Copyright IBM Corp. 2010, 2017\n"); exit(0); default: DIE("Process arguments error\n"); } } static void init_io(int fd) { int prot; DEBUG("read-only: %d", cmsfs.readonly); cmsfs.fd = fd; cmsfs.size = (off_t) cmsfs.total_blocks * cmsfs.blksize; DEBUG(" mmap size: %lu", cmsfs.size); /* try to map the whole block device for speeding-up access */ if (cmsfs.readonly) prot = PROT_READ; else prot = PROT_READ | PROT_WRITE; cmsfs.map = mmap(NULL, cmsfs.size, prot, MAP_SHARED, fd, 0); if (cmsfs.map == MAP_FAILED) { DEBUG("\nmmap failed, using pread/write for disk I/O.\n"); io_ops.read = &read_syscall; io_ops.write = &write_syscall; } else { DEBUG(" addr: %p\n", cmsfs.map); io_ops.read = &read_memory; io_ops.write = &write_memory; } } static void cmsfs_init(int fd) { init_io(fd); /* calculate blocksize dependent values */ cmsfs.data_block_mask = cmsfs.blksize - 1; cmsfs.nr_blocks_512 = cmsfs.blksize / 512; cmsfs.fixed_ptrs_per_block = cmsfs.blksize / sizeof(struct fixed_ptr); cmsfs.var_ptrs_per_block = cmsfs.blksize / sizeof(struct var_ptr); cmsfs.bits_per_data_block = get_order(cmsfs.blksize); /* store directory information */ cmsfs.dir_levels = get_levels(cmsfs.fdir); cmsfs.files = get_files_count(cmsfs.fdir); /* alloc cache entries for all files */ cmsfs.fcache_max = max_cache_entries(); cmsfs.fcache = calloc(cmsfs.fcache_max, sizeof(struct fcache_entry)); cmsfs.amap = get_fop(cmsfs.fdir + sizeof(struct fst_entry)); cmsfs.amap_levels = get_levels(cmsfs.fdir + sizeof(struct fst_entry)); cmsfs.amap_bytes_per_block = cmsfs.blksize * 8 * cmsfs.blksize; if (!hcreate_r(cmsfs.fcache_max, &cmsfs.htab)) DIE("hcreate failed\n"); util_list_init(&open_file_list, struct file, list); util_list_init(&text_type_list, struct filetype, list); scan_conf_file(&text_type_list); } int main(int argc, char *argv[]) { struct fuse_args args = FUSE_ARGS_INIT(argc, argv); char *fsname; int rc, fd; #ifdef DEBUG_ENABLED logfile = fopen(DEBUG_LOGFILE, "w"); if (logfile == NULL) DIE_PERROR("Cannot open file " DEBUG_LOGFILE " for writing"); #endif if (fuse_opt_parse(&args, &cmsfs, cmsfs_opts, cmsfs_process_args) == -1) DIE("Failed to parse option\n"); if (!cmsfs.device) DIE("Missing device\n" "Try '%s --help' for more information\n", argv[0]); DEBUG("using device: %s", cmsfs.device); fd = get_device_info(&cmsfs); DEBUG(" blocksize: %d\n", cmsfs.blksize); fsname = malloc(FSNAME_MAX_LEN); if (fsname == NULL) DIE_PERROR("malloc failed"); #if FUSE_VERSION >= 27 snprintf(fsname, FSNAME_MAX_LEN, "-osubtype=cmsfs,fsname=%s", cmsfs.device); #else snprintf(fsname, FSNAME_MAX_LEN, "-ofsname=%s", cmsfs.device); #endif fuse_opt_add_arg(&args, fsname); free(fsname); cmsfs_init(fd); if (cmsfs.readonly) fuse_opt_add_arg(&args, "-oro"); /* force single threaded mode which requires no locking */ fuse_opt_add_arg(&args, "-s"); if (cmsfs.mode == BINARY_MODE && (cmsfs.codepage_from != NULL || cmsfs.codepage_to != NULL)) DIE("Incompatible options, select -a or -t if using --from or --to\n"); if (cmsfs.mode != BINARY_MODE) { if (cmsfs.codepage_from == NULL) cmsfs.codepage_from = CODEPAGE_EDF; if (cmsfs.codepage_to == NULL) cmsfs.codepage_to = CODEPAGE_LINUX; setup_iconv(&cmsfs.iconv_from, cmsfs.codepage_from, cmsfs.codepage_to); setup_iconv(&cmsfs.iconv_to, cmsfs.codepage_to, cmsfs.codepage_from); } rc = cmsfs_fuse_main(&args, &cmsfs_oper); fuse_opt_free_args(&args); #ifdef DEBUG_ENABLED fclose(logfile); #endif hdestroy_r(&cmsfs.htab); return rc; } s390-tools-2.38.0/cmsfs-fuse/cmsfs-fuse.h000066400000000000000000000056371502674226300177610ustar00rootroot00000000000000/* * cmsfs-fuse - CMS EDF filesystem support for Linux * * Data structures * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef _CMSFS_H #define _CMSFS_H #include #include #include "lib/util_list.h" #define COMP "cmsfs-fuse: " extern struct cmsfs cmsfs; /* conversion between absolute and relative addresses */ #define ABS(x) ((off_t) (x - 1) * cmsfs.blksize) #define REL(x) ((x / cmsfs.blksize) + 1) struct fcache_entry { /* filename used as hash key */ char name[18]; /* location of fst entry */ off_t fst_addr; /* filename string address */ char *str; }; enum cmsfs_mode { BINARY_MODE, TEXT_MODE, TYPE_MODE, }; /* the per device global struture */ struct cmsfs { /* name of the block device, e.g. /dev/dasde */ const char *device; /* global file descriptor of the underlying block device */ int fd; /* start of mmap of the whole block device */ char *map; /* size of the disk */ off_t size; /* formatted blocksize */ int blksize; /* number of 512 byte blocks per block */ int nr_blocks_512; /* device is read only */ int readonly; /* access permission for other users */ int allow_other; /* offset to label */ off_t label; /* offset to file directory root FST */ off_t fdir; /* offset to allocation map */ off_t amap; /* depth of directories */ int dir_levels; /* depth of allocation maps */ int amap_levels; /* files count on the device */ int files; /* conversion mode */ enum cmsfs_mode mode; /* iconv codepage options */ const char *codepage_from; const char *codepage_to; iconv_t iconv_from; iconv_t iconv_to; /* disk stats */ int total_blocks; int used_blocks; /* blocks reserved for outstanding meta data */ int reserved_blocks; /* constants */ int fixed_ptrs_per_block; int var_ptrs_per_block; int bits_per_data_block; int bits_per_ptr_block; int data_block_mask; off_t amap_bytes_per_block; /* file cache */ struct fcache_entry *fcache; int fcache_used; int fcache_max; struct hsearch_data htab; }; #define MAX_TYPE_LEN 9 struct filetype { char name[MAX_TYPE_LEN]; struct util_list_node list; }; #define NULL_BLOCK 0 #define VAR_FILE_END 1 #define PTRS_PER_BLOCK (cmsfs.fixed_ptrs_per_block) #define VPTRS_PER_BLOCK (cmsfs.var_ptrs_per_block) #define DATA_BLOCK_MASK (cmsfs.data_block_mask) #define BITS_PER_DATA_BLOCK (cmsfs.bits_per_data_block) #define BYTES_PER_BLOCK (cmsfs.amap_bytes_per_block) extern int get_device_info(struct cmsfs *cmsfs); extern int scan_conf_file(struct util_list *list); extern int is_edf_char(int c); #ifndef _CMSFS_FSCK int _read(void *, size_t, off_t); int _write(const void *, size_t, off_t); int _zero(off_t, size_t); off_t get_fixed_pointer(off_t); off_t get_free_block(void); off_t get_zero_block(void); void free_block(off_t); #endif #endif s390-tools-2.38.0/cmsfs-fuse/config.c000066400000000000000000000047211502674226300171370ustar00rootroot00000000000000/* * cmsfs-fuse - CMS EDF filesystem support for Linux * * Config option parsing * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include "lib/util_libc.h" #include "lib/zt_common.h" #include "cmsfs-fuse.h" #include "helper.h" #define MAX_LINE_LEN 80 static char *conf_file; static int open_conf_file(FILE **fh) { const char *home_env; conf_file = malloc(4096); if (conf_file == NULL) DIE_PERROR("malloc failed"); home_env = getenv("HOME"); if (home_env == NULL) goto no_home; sprintf(conf_file, "%s/.cmsfs-fuse/filetypes.conf", home_env); *fh = fopen(conf_file, "r"); if (*fh != NULL) goto out; no_home: sprintf(conf_file, "%s/%s", TOOLS_SYSCONFDIR, "/cmsfs-fuse/filetypes.conf"); *fh = fopen(conf_file, "r"); if (*fh == NULL) { free(conf_file); return -ENOENT; } out: DEBUG("using config file: %s\n", conf_file); return 0; } static void add_filetype(char *name, struct util_list *list) { struct filetype *entry; entry = malloc(sizeof(*entry)); if (entry == NULL) DIE_PERROR("malloc failed"); util_strlcpy(entry->name, name, MAX_TYPE_LEN); util_list_add_head(list, entry); } static int filetype_valid(const char *type, int line) { unsigned int i; if (strlen(type) > 8) { WARN("entry too long in line: %d in config file: %s\n", line, conf_file); return 0; } for (i = 0; i < strlen(type); i++) if (!is_edf_char(*(type + i))) { WARN("invalid character in line: %d in config file: %s\n", line, conf_file); return 0; } return 1; } int scan_conf_file(struct util_list *list) { char buf[MAX_LINE_LEN], *tmp; int line = 0; FILE *fh; if (open_conf_file(&fh) < 0) return -ENOENT; while (fgets(buf, MAX_LINE_LEN, fh) != NULL) { line++; tmp = buf; while (isblank(*tmp)) tmp++; if (*tmp == '\n') continue; /* * Skip comments, comment must be "# " because # is a valid * EDF character. */ if (strlen(tmp) > 1 && *tmp == '#' && *(tmp + 1) == ' ') continue; /* remove trailing \n */ if (strlen(tmp) && *(tmp + strlen(tmp) - 1) == '\n') *(tmp + strlen(tmp) - 1) = '\0'; if (filetype_valid(tmp, line)) add_filetype(tmp, list); } fclose(fh); free(conf_file); return 0; } s390-tools-2.38.0/cmsfs-fuse/dasd.c000066400000000000000000000100121502674226300165730ustar00rootroot00000000000000/* * cmsfs-fuse - CMS EDF filesystem support for Linux * * DASD specific functions * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include "lib/zt_common.h" #include "cmsfs-fuse.h" #include "edf.h" #include "helper.h" #define BLKSSZGET _IO(0x12, 104) /* CMS disk label starts with ASCII string "CMS1" */ #define VOL_LABEL_EBCDIC 0xc3d4e2f1 static int disk_supported(int fd, struct cmsfs *cmsfs) { unsigned int cms_id = VOL_LABEL_EBCDIC; struct cms_label label; int rc; rc = lseek(fd, cmsfs->label, SEEK_SET); if (rc < 0) { perror(COMP "lseek failed"); return 0; } rc = read(fd, &label, sizeof(label)); if (rc < 0) { perror(COMP "read failed"); return 0; } /* check that the label contains the CMS1 string */ if (memcmp(label.id, &cms_id, sizeof(cms_id)) != 0) return 0; /* label sanity checks */ if (label.blocksize != 4096 && label.blocksize != 2048 && label.blocksize != 1024 && label.blocksize != 512) { fprintf(stderr, COMP "Invalid disk block size!\n"); return 0; } if (label.dop != 4 && label.dop != 5) { fprintf(stderr, COMP "Invalid disk origin pointer!\n"); return 0; } if (label.fst_entry_size != sizeof(struct fst_entry)) { fprintf(stderr, COMP "Invalid FST entry size!\n"); return 0; } if (label.fst_per_block != label.blocksize / label.fst_entry_size) { fprintf(stderr, COMP "Invalid FST per block value!\n"); return 0; } /* set the blocksize to the formatted one */ cmsfs->blksize = label.blocksize; DEBUG(" DOP: %d", label.dop); /* block number 5 means 0x4000... */ cmsfs->fdir = (label.dop - 1) * cmsfs->blksize; DEBUG(" fdir: %lx", cmsfs->fdir); /* get disk usage for statfs */ cmsfs->total_blocks = label.total_blocks; cmsfs->used_blocks = label.used_blocks; DEBUG(" Total blocks: %d Used blocks: %d", cmsfs->total_blocks, cmsfs->used_blocks); return 1; } static void get_device_info_ioctl(int fd, struct cmsfs *cmsfs) { if (ioctl(fd, BLKSSZGET, &cmsfs->blksize) != 0) DIE("ioctl error get blocksize\n"); } static int label_offsets[] = { 4096, 512, 2048, 1024, 8192 }; static void get_device_info_file(int fd, struct cmsfs *cmsfs) { unsigned int cms_id = VOL_LABEL_EBCDIC; unsigned int i; char label[4]; off_t offset; int rc; cmsfs->label = 0; /* * Read the blocksize from label. Unfortunately the blocksize * position depends on the blocksize... time for some heuristics. */ for (i = 0; i < ARRAY_SIZE(label_offsets); i++) { offset = label_offsets[i]; rc = lseek(fd, offset, SEEK_SET); if (rc < 0) DIE_PERROR("lseek failed"); rc = read(fd, &label, 4); if (rc < 0) DIE_PERROR("read failed"); /* check if the label contains the CMS1 string */ if (memcmp(label, &cms_id, sizeof(cms_id)) == 0) { cmsfs->label = offset; break; } } if (!cmsfs->label) DIE("Error CMS1 label not found!\n"); } int get_device_info(struct cmsfs *cmsfs) { struct stat stat; int fd; /* * Open writable, if write access is not granted fall back to * read only. */ fd = open(cmsfs->device, O_RDWR); if (fd < 0) { if (errno == EROFS || errno == EACCES) { cmsfs->readonly = 1; fd = open(cmsfs->device, O_RDONLY); if (fd < 0) DIE_PERROR("open failed"); } else DIE_PERROR("open failed"); } if (fstat(fd, &stat) < 0) DIE_PERROR("fstat failed"); if (S_ISBLK(stat.st_mode)) { get_device_info_ioctl(fd, cmsfs); cmsfs->label = 2 * cmsfs->blksize; /* FBA disks have a different label location */ if (!disk_supported(fd, cmsfs)) { cmsfs->label = cmsfs->blksize; if (!disk_supported(fd, cmsfs)) goto error; } } else if (S_ISREG(stat.st_mode)) get_device_info_file(fd, cmsfs); else goto error; if (!disk_supported(fd, cmsfs)) goto error; return fd; error: DIE("Unsupported disk\n"); } s390-tools-2.38.0/cmsfs-fuse/ebcdic.h000066400000000000000000000110241502674226300171020ustar00rootroot00000000000000/* * cmsfs-fuse - CMS EDF filesystem support for Linux * * EBCDIC to ASCII conversion: * EDF uses an EBCDIC codepage based on 037 with some modifications. * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef _EBCDIC_H #define _EBCDIC_H #include #include /* * EBCDIC 037 -> ISO8859-1 * changes: * 0x5f: 0xaa -> 0x5e ^ * 0xad: 0x07 -> 0x5b [ * 0xbd: 0x07 -> 0x5d ] */ static char ebc2asc[256] = { /* 0x00 */ 0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F, 0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, /* 0x10 */ 0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07, 0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, /* 0x20 */ 0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B, 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07, /* 0x30 */ 0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04, 0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A, /* 0x40 */ 0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86, 0x87, 0xA4, 0x9B, 0x2E, 0x3C, 0x28, 0x2B, 0x7C, /* 0x50 */ 0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07, 0x8D, 0xE1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0x5E, /* 0x60 */ 0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F, 0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, /* 0x70 */ 0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, /* 0x80 */ 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1, /* 0x90 */ 0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07, /* 0xa0 */ 0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x5B, 0x07, 0x07, /* 0xb0 */ 0x5E, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC, 0xAB, 0x07, 0x5B, 0x5D, 0x07, 0x5D, 0x07, 0x07, /* 0xc0 */ 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07, /* 0xd0 */ 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98, /* 0xe0 */ 0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07, /* 0xf0 */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07 }; /* ISO8859-1 -> EBCDIC 037 */ static char asc2ebc[256] = { /* 0x00 */ 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, 0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, /* 0x10 */ 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26, 0x18, 0x19, 0x3F, 0x27, 0x22, 0x1D, 0x1E, 0x1F, /* 0x20 */ 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61, /* 0x30 */ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F, /* 0x40 */ 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, /* 0x50 */ 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xBA, 0xE0, 0xBB, 0xB0, 0x6D, /* 0x60 */ 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, /* 0x70 */ 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07, /* 0x80 */ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, /* 0x90 */ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, /* 0xa0 */ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, /* 0xb0 */ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, /* 0xc0 */ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, /* 0xd0 */ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, /* 0xe0 */ 0x3F, 0x59, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, /* 0xf0 */ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x90, 0x3F, 0x3F, 0x3F, 0x3F, 0xEA, 0x3F, 0xFF }; #define EBCDIC_ENCODE 0x0 #define EBCDIC_DECODE 0x1 static inline void a2e(char *dst, const char *src, int len, int to) { char *conv; int i; if (to == EBCDIC_ENCODE) conv = asc2ebc; else conv = ebc2asc; for (i = 0; i < len; i++) dst[i] = conv[(unsigned int)src[i]]; } static inline void ebcdic_enc(char *dst, const char *src, int len) { a2e(dst, src, len, EBCDIC_ENCODE); } static inline void ebcdic_dec(char *dst, const char *src, int len) { a2e(dst, src, len, EBCDIC_DECODE); } #endif s390-tools-2.38.0/cmsfs-fuse/edf.h000066400000000000000000000047341502674226300164410ustar00rootroot00000000000000/* * cmsfs-fuse - CMS EDF filesystem support for Linux * * EDF and label structures * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef _EDF_H #define _EDF_H #include "helper.h" /* * File status table entry */ struct fst_entry { char name[8]; char type[8]; char res1[8]; short int mode; char res2[4]; char record_format; char flag; int record_len; char res3[4]; unsigned int fop; /* number of data blocks (not incl. pointer blocks) */ int nr_blocks; int nr_records; char levels; char ptr_size; char date[6]; char res4[4]; }; struct cms_label { char id[6]; char user_id[6]; unsigned int blocksize; unsigned int dop; unsigned int f_cylinders; unsigned int max_cylinders; unsigned int total_blocks; unsigned int used_blocks; unsigned int fst_entry_size; unsigned int fst_per_block; char date[6]; unsigned int res1[3]; char res2[8]; }; #define RECORD_LEN_VARIABLE 0xe5 #define RECORD_LEN_FIXED 0xc6 /* TODO: correct for fixed? */ #define MAX_RECORD_LEN 0xffff #define FST_ENTRY_SIZE sizeof(struct fst_entry) #define FST_ENTRY_DIR_NAME 0x0000000100000000ULL #define FST_ENTRY_DIR_TYPE 0xc4c9d9c5c3e3d6d9ULL /* 'DIRECTOR' */ #define FST_ENTRY_ALLOC_NAME 0x0000000200000000ULL #define FST_ENTRY_ALLOC_TYPE 0xc1d3d3d6c3d4c1d7ULL /* 'ALLOCMAP' */ #define FST_FLAG_CENTURY 0x0008 #define FST_FOP_OFFSET 0x28 #define FST_LEVEL_OFFSET 0x34 #define VAR_RECORD_HEADER_SIZE 0x2 #define VAR_RECORD_SPANNED 0xffffffff #define PTR_SIZE (sizeof(struct fixed_ptr)) #define VPTR_SIZE (sizeof(struct var_ptr)) struct fixed_ptr { unsigned int next; }; struct var_ptr { unsigned int next; int hi_record_nr; unsigned int disp; }; static inline int is_directory(const char *name, const char *type) { if ((*(unsigned long long *) name) != FST_ENTRY_DIR_NAME) return 0; if ((*(unsigned long long *) type) != FST_ENTRY_DIR_TYPE) return 0; return 1; } static inline int is_allocmap(const char *name, const char *type) { if ((*(unsigned long long *) name) != FST_ENTRY_ALLOC_NAME) return 0; if ((*(unsigned long long *) type) != FST_ENTRY_ALLOC_TYPE) return 0; return 1; } static inline int is_file(void *name, void *type) { if ((*(unsigned long long *) name) == 0ULL) return 0; /* Assumption: type = 0 is not legal */ if ((*(unsigned long long *) type) == 0ULL) return 0; return 1; } #endif s390-tools-2.38.0/cmsfs-fuse/etc/000077500000000000000000000000001502674226300162755ustar00rootroot00000000000000s390-tools-2.38.0/cmsfs-fuse/etc/filetypes.conf000066400000000000000000000014411502674226300211500ustar00rootroot00000000000000# # Filetypes that are interpreted as text files. If you want an EBCDIC # file translated to ASCII, add the extension here. # # Comments must include a space after the # # Add your extensions here: PRM CONF # The following types were taken from the z/VM TCPIP.DATA file: $EXEC $REXX $XEDIT AMS AMSERV ANN ANNOUNCE APP APPEND ASC ASCII ASM ASM3705 ASSEMBLE AVL AVAIL A37 BASDATA BASIC BKS BKSHELF C C++ CAT CATALOG CNTRL COB COBOL COPY CPP DIRECT DLCS DOCUMENT ESERV EXC EXEC FFT FOR FORM FORTRAN FREEFORT GCS GROUP H HPP HTM HTML H++ JOB LISTING LOG LST MAC MACLIB MACRO MAK MAKE ME MEMBER MEMO MODULE NAM NAMES NETLOG NONE NOT NOTE NOTEBOOK OFS OPT OPTIONS PACKAGE PASCAL PKG PLAS PLI PLIOPT PLS PVT REXX RPG SCR SCRIPT STY STYLE TEXT TEXTXXXX TXT TXTXXXX UPDATE UPDT VMT VSBASIC VSBDATA XED XEDIT s390-tools-2.38.0/cmsfs-fuse/helper.h000066400000000000000000000020261502674226300171520ustar00rootroot00000000000000/* * cmsfs-fuse - CMS EDF filesystem support for Linux * * Common helper functions * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef _HELPER_H #define _HELPER_H extern FILE *logfile; #define DEBUG_LOGFILE "/tmp/cmsfs-fuse.log" #ifdef DEBUG_ENABLED #define DEBUG(...) \ do { \ fprintf(logfile, __VA_ARGS__); \ fflush(logfile); \ } while (0) #else #define DEBUG(...) #endif #define DIE(...) \ do { \ fprintf(stderr, COMP __VA_ARGS__); \ exit(1); \ } while (0) #define DIE_PERROR(...) \ do { \ perror(COMP __VA_ARGS__); \ exit(1); \ } while (0) #define BUG(x) \ if (x) { \ fprintf(stderr, COMP " assert failed at " \ __FILE__ ":%d in %s()\n", __LINE__, __func__); \ exit(1); \ } #define WARN(...) \ do { \ fprintf(stderr, COMP "Warning, " __VA_ARGS__); \ } while (0) #endif s390-tools-2.38.0/common.mak000066400000000000000000000413771502674226300154450ustar00rootroot00000000000000COMMON_INCLUDED ?= false V ?= 0 W ?= 0 G ?= 0 C ?= 0 D ?= 0 ASAN ?= 0 ENABLE_WERROR ?= 0 OPT_FLAGS ?= MAKECMDGOALS ?= CARGO ?= cargo CARGOFLAGS ?= ifeq ($(COMMON_INCLUDED),false) COMMON_INCLUDED := true override SHELL := /bin/bash # 'BUILD_ARCH' is the architecture of the machine where the build takes place BUILD_ARCH := $(shell uname -m | sed -e 's/i.86/i386/' -e 's/sun4u/sparc64/' -e 's/arm.*/arm/' -e 's/sa110/arm/') # 'HOST_ARCH' is the architecture of the machine that will run the compiled output HOST_ARCH ?= $(BUILD_ARCH) # The `*clean` targets are mutually exclusive to all other targets ifneq ($(filter %clean,$(MAKECMDGOALS)),) ifneq ($(MAKECMDGOALS),$(filter %clean,$(MAKECMDGOALS))) $(error The *clean targets are mutually exclusive to all other targets) endif endif # Global definitions # The variable "DISTRELEASE" should be overwritten in rpm spec files with: # "make DISTRELEASE=%{release}" and "make install DISTRELEASE=%{release}" VERSION := 2 RELEASE := 38 PATCHLEVEL := 0 DISTRELEASE := build-$(shell date +%Y%m%d) S390_TOOLS_RELEASE := $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE) export S390_TOOLS_RELEASE reldir = $(subst $(realpath $(dir $(filter %common.mak,$(MAKEFILE_LIST))))/,,$(CURDIR)) rootdir= $(dir $(filter %common.mak,$(MAKEFILE_LIST))) export S390_TEST_LIB_PATH=$(rootdir)/s390-tools-testsuite/lib # # For cross compiles specify HOST_ARCH= and CROSS_COMPILE= on the commandline: # # $ make HOST_ARCH=s390x CROSS_COMPILE="s390x-linux-gnu-" # CROSS_COMPILE ?= # # Commands can be overwritten on the command line with "make =": # # $ make CC=gcc-4.8 # # The "cmd_define_and_export" macro wraps the command definition so that # the commands can be user supplied and are still pretty-printed for the # build process. In addition, the command variable gets exported so it # can be used by sub-makes. # # The macro is called with the following parameters: # # $(1) - Used command variable in the Makefiles # $(2) - Pretty Print output for the command # $(3) - Default command if not user-specified # # The Example below... # # $(eval $(call cmd_define_and_export, CC," CC ",$(CROSS_COMPILE)gcc)) # # ... produces the following code: # # CC = $(CROSS_COMPILE)gcc # CC_SILENT := $(CC) # override CC = $(call echocmd," CC ",/$@)$(CC_SILENT) # export CC # # The "strip" make function is used for the first parameter to allow blanks, # which improves readability. # define cmd_define $(strip $(1)) = $(3) $(strip $(1))_SILENT := $$($(strip $(1))) override $(strip $(1)) = $$(call echocmd,$(2),/$$@)$$($(strip $(1))_SILENT) endef define cmd_define_and_export $(call cmd_define,$(1),$(2),$(3)) export $(strip $(1)) endef define define_toolchain_variables $(call cmd_define_and_export, AS$(1)," AS$(1) ",$(2)as) $(call cmd_define_and_export, CC$(1)," CC$(1) ",$(2)gcc) $(call cmd_define_and_export, LINK$(1)," LINK$(1) ",$$(CC$(1))) $(call cmd_define_and_export, CXX$(1)," CXX$(1) ",$(2)g++) $(call cmd_define_and_export, LINKXX$(1)," LINKXX$(1) ",$$(CXX$(1))) $(call cmd_define_and_export, CPP$(1)," CPP$(1) ",$(2)gcc -E) $(call cmd_define_and_export, AR$(1)," AR$(1) ",$(2)ar) $(call cmd_define_and_export, NM$(1)," NM$(1) ",$(2)nm) $(call cmd_define_and_export, STRIP$(1)," STRIP$(1) ",$(2)strip) $(call cmd_define_and_export,OBJCOPY$(1)," OBJCOPY$(1) ",$(2)objcopy) $(call cmd_define_and_export,OBJDUMP$(1)," OBJDUMP$(1) ",$(2)objdump) PKG_CONFIG$(1) = pkg-config export PKG_CONFIG$(1) endef # If the host architecture is not the same as the build architecture # 'CROSS_COMPILE=...' is always required (except for the '*clean' targets). ifneq ($(HOST_ARCH),$(BUILD_ARCH)) ifeq ($(CROSS_COMPILE),) # `make clean` and similar must always work! ifeq ($(filter %clean,$(MAKECMDGOALS)),) $(error Please specify CROSS_COMPILE=... and try it again!) endif endif endif $(eval $(call define_toolchain_variables,_FOR_BUILD,)) $(eval $(call define_toolchain_variables,,$(CROSS_COMPILE))) $(eval $(call cmd_define, RUNTEST," RUNTEST ",$(S390_TEST_LIB_PATH)/s390_runtest)) $(eval $(call cmd_define, CAT," CAT ",cat)) $(eval $(call cmd_define, SED," SED ",sed)) $(eval $(call cmd_define, GZIP," GZIP ",gzip)) $(eval $(call cmd_define, MV," MV ",mv)) $(eval $(call cmd_define, PERLC," PERLC ",perl -c)) $(eval $(call cmd_define,CARGO_BUILD," CARGO BUILD ",$(CARGO) build)) $(eval $(call cmd_define,CARGO_TEST, " CARGO TEST ",$(CARGO) test)) $(eval $(call cmd_define,CARGO_CLEAN," CARGO CLEAN ",$(CARGO) clean)) CHECK = sparse CHECK_SILENT := $(CHECK) CHECKTOOL = $(call echocmd," CHECK ",/$@)$(CHECK_SILENT) SKIP = echo " SKIP $(call reldir) due to" INSTALL = install CP = cp ALL_CARGOFLAGS := $(CARGOFLAGS) ifneq ("${V}","1") MAKEFLAGS += --quiet ALL_CARGOFLAGS += --quiet echocmd=echo $1$(call reldir)$2; RUNTEST += > /dev/null 2>&1 else echocmd= endif DEFAULT_CFLAGS = -g -fstack-protector-all -W -Wall -Wformat-security ifeq ("${W}","1") DEFAULT_CFLAGS += -Wextra -Wshadow -Wundef -Wuninitialized -Wdouble-promotion -Wconversion endif ifeq ("${D}","1") DEFAULT_CFLAGS += -Og -g3 -ggdb3 else DEFAULT_CFLAGS += -O3 endif ifeq ("${ENABLE_WERROR}", "1") DEFAULT_CFLAGS += -Werror endif DEFAULT_CPPFLAGS = -D_GNU_SOURCE DEFAULT_LDFLAGS = -rdynamic ifeq ("${ASAN}","1") DEFAULT_CFLAGS += -fsanitize=address -fno-omit-frame-pointer DEFAULT_LDFLAGS += -fsanitize=address endif DEFAULT_PERLCFLAGS = ifeq ("${W}","1") DEFAULT_PERLCFLAGS += -w endif # # Check for header prerequisite # # $1: Name of include file to check # $2: Additional compiler & linker options (optional) # # Returns "yes" on success and nothing otherwise # define check_header_prereq $(shell printf "#include <%s>\n int main(void) {return 0;}\n" $1 | \ ( $(CC) $(filter-out --coverage, $(ALL_CFLAGS)) $(ALL_CPPFLAGS) \ $2 -o /dev/null -x c - ) >/dev/null 2>&1 && echo -n yes) endef # # Check for build dependency # # $1: Name of tool or feature that requires dependency # $2: Name of include file to check # $3: Name of required devel package # $4: Option to skip build (e.g. HAVE_FUSE=0) # $5: Additional compiler & linker options (optional) # check_dep=\ printf "\#include <%s>\n int main(void) {return 0;}\n" $2 | ( $(CC) $(filter-out --coverage, $(ALL_CFLAGS)) $(ALL_CPPFLAGS) $5 -o /dev/null -x c - ) > /dev/null 2>&1; \ if [ $$? != 0 ]; \ then \ printf " REQCHK %s (%s)\n" $1 $2; \ printf "********************************************************************************\n" >&2; \ printf "* Missing build requirement for: %-45s *\n" $1 >&2; \ printf "* Install package..............: %-45s *\n" $3 >&2; \ printf "* You can skip build with......: make %-40s *\n" $4 >&2; \ printf "********************************************************************************\n" >&2; \ exit 1; \ fi # # Test for linker option # # $1: Linker option # # Returns the linker option if available and nothing otherwise # define test_linker_flag $(shell printf ".globl _start\n_start:\nnop\n" | \ ( $(LINK) "-Wl,$1" -o /dev/null -nostdlib -x assembler -) >/dev/null 2>&1 && printf -- '-Wl,%s' "$1") endef NO_WARN_RWX_SEGMENTS_LDFLAGS := $(call test_linker_flag,"--no-warn-rwx-segments") # # Support alternate install root # # INSTALLDIR: Finally install s390-tools to INSTALLDIR. This can be used # for testing locally installed tools. # DESTDIR: Temporary install s390-tools to this directory. This can be # used for building s390-tools e.g. with rpmbuild. # # The difference between INSTALLDIR and DESTDIR is that for INSTALLDIR # internally used directories (e.g. for config files) are adjusted. # # Example: # # $ cd cpumf # $ INSTALLDIR=/tmp make install # $ cat /tmp/lib/s390-tools/cpumf_helper | grep DATA_DIR # my $CPUMF_DATA_DIR = '/tmp/usr/share/s390-tools/cpumf'; # # $ make clean # $ DESTDIR=/tmp make install # $ cat /tmp/lib/s390-tools/cpumf_helper | grep DATA_DIR # my $CPUMF_DATA_DIR = '/usr/share/s390-tools/cpumf'; # ifdef INSTROOT $(error INSTROOT is no longer available, use DESTDIR instead) endif INSTALLDIR ?= DESTDIR ?= USRSBINDIR = $(INSTALLDIR)/usr/sbin USRBINDIR = $(INSTALLDIR)/usr/bin BINDIR = $(INSTALLDIR)/sbin LIBDIR = $(INSTALLDIR)/lib USRLIBDIR = $(INSTALLDIR)/usr/lib USRLIB64DIR = $(INSTALLDIR)/usr/lib64 SYSCONFDIR = $(INSTALLDIR)/etc DATADIR = $(INSTALLDIR)/usr/share MANDIR = $(DATADIR)/man BASHCOMPLETIONDIR = $(DATADIR)/bash-completion/completions ZSHCOMPLETIONDIR = $(DATADIR)/zsh/site-functions VARDIR = $(INSTALLDIR)/var TOOLS_DATADIR = $(DATADIR)/s390-tools TOOLS_LIBDIR = $(INSTALLDIR)/lib/s390-tools ZFCPDUMP_DIR = $(TOOLS_LIBDIR)/zfcpdump # Systemd support files are installed only if a directory is specified # for SYSTEMDSYSTEMUNITDIR (e.g. /lib/systemd/system) SYSTEMDSYSTEMUNITDIR = USRINCLUDEDIR = $(INSTALLDIR)/usr/include ZKEYKMSPLUGINDIR = $(USRLIB64DIR)/zkey UDEVDIR = $(USRLIBDIR)/udev UDEVRULESDIR = $(UDEVDIR)/rules.d DRACUTDIR = $(USRLIBDIR)/dracut DRACUTCONFDIR = $(DRACUTDIR)/dracut.conf.d DRACUTMODDIR = $(DRACUTDIR)/modules.d ifeq ($(LIBDIR),$(INSTALLDIR)/lib) SOINSTALLDIR = $(USRLIB64DIR) else SOINSTALLDIR = $(LIBDIR) endif INSTDIRS = $(USRSBINDIR) $(USRBINDIR) $(BINDIR) $(LIBDIR) $(MANDIR) \ $(SYSCONFDIR) $(SYSCONFDIR)/sysconfig \ $(TOOLS_LIBDIR) $(TOOLS_DATADIR) \ $(ZFCPDUMP_DIR) $(SYSTEMDSYSTEMUNITDIR) \ $(USRLIB64DIR) $(USRINCLUDEDIR) $(ZKEYKMSPLUGINDIR) \ $(SOINSTALLDIR) $(USRLIBDIR) OWNER := $(shell id -un) GROUP := $(shell id -gn) export INSTALLDIR BINDIR LIBDIR USRLIBDIR USRLIB64DIR MANDIR OWNER GROUP # Special defines for zfcpdump ZFCPDUMP_IMAGE = zfcpdump-image ZFCPDUMP_INITRD = zfcpdump-initrd ZFCPDUMP_FLAVOR = zfcpdump export ZFCPDUMP_DIR ZFCPDUMP_IMAGE ZFCPDUMP_INITRD ZFCPDUMP_FLAVOR CFLAGS ?= $(DEFAULT_CFLAGS) $(OPT_FLAGS) CFLAGS_FOR_BUILD ?= -std=gnu11 $(DEFAULT_CFLAGS) $(OPT_FLAGS) CPPFLAGS ?= $(DEFAULT_CPPFLAGS) LDFLAGS ?= $(DEFAULT_LDFLAGS) ALL_CFLAGS = -DS390_TOOLS_RELEASE=$(S390_TOOLS_RELEASE) \ -DS390_TOOLS_LIBDIR=$(TOOLS_LIBDIR) \ -DS390_TOOLS_DATADIR=$(TOOLS_DATADIR) \ -DS390_TOOLS_SYSCONFDIR=$(SYSCONFDIR) \ -DS390_TOOLS_BINDIR=$(BINDIR) \ -std=gnu11 $(CFLAGS) CXXFLAGS ?= $(DEFAULT_CFLAGS) $(OPT_FLAGS) ALL_CXXFLAGS = -DS390_TOOLS_RELEASE=$(S390_TOOLS_RELEASE) \ -DS390_TOOLS_LIBDIR=$(TOOLS_LIBDIR) \ -DS390_TOOLS_DATADIR=$(TOOLS_DATADIR) \ -DS390_TOOLS_SYSCONFDIR=$(SYSCONFDIR) \ -DS390_TOOLS_BINDIR=$(BINDIR) \ -std=gnu++11 $(CXXFLAGS) ALL_CPPFLAGS = -I $(rootdir)include $(CPPFLAGS) ALL_LDFLAGS = $(LDFLAGS) ALL_PERLCFLAGS = $(DEFAULT_PERLCFLAGS) # make G=1 # Compile tools so that gcov can be used to collect code coverage data. # See the gcov man page for details. ifeq ("${G}","1") ALL_CFLAGS := $(filter-out -O%,$(ALL_CFLAGS)) --coverage ALL_CXXFLAGS := $(filter-out -O%,$(ALL_CXXFLAGS)) --coverage ALL_LDFLAGS += --coverage endif export INSTALL CFLAGS CXXFLAGS \ LDFLAGS CPPFLAGS ALL_CFLAGS ALL_CXXFLAGS ALL_LDFLAGS ALL_CPPFLAGS ifneq ($(shell $(CC_SILENT) -dumpspecs 2>/dev/null | grep -e '[^f]no-pie'),) NO_PIE_CFLAGS := -fno-pie NO_PIE_LDFLAGS := -no-pie else NO_PIE_CFLAGS := NO_PIE_LDFLAGS := endif # Overwrite implicit makefile rules for having nice compile output %.o: %.c ifeq ("${C}","1") $(CHECKTOOL) $(ALL_CPPFLAGS) $(ALL_CFLAGS) -c $< -o $@ endif $(CC) $(ALL_CPPFLAGS) $(ALL_CFLAGS) -c $< -o $@ %.o: %.cpp $(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $< -o $@ %: %.o $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ %.a: $(AR) rcs $@ $^ all: help: @echo 'Usage: make [TARGETS] [OPTIONS]' @echo '' @echo 'TARGETS' @echo ' all Build all tools (default target)' @echo ' install Install tools' @echo ' clean Delete all generated files' @echo ' compdb Generate compile_commands.json for clangd' @echo '' @echo 'OPTIONS' @echo ' D=1 Build with debugging option "-Og"' @echo ' C=1 Build with check tool defined with "CHECK=" (default=sparse)' @echo ' G=1 Build with gcov to collect code coverage data' @echo ' V=1 Generate verbose build output' @echo ' W=1 Build with higher warning level' @echo ' ASAN=1 Build with address sanitizer' @echo ' ENABLE_WERROR=1 Build with -Werror' @echo '' @echo 'EXAMPLES' @echo ' # make clean all D=1 W=1 -j' @echo ' # make C=1 CHECK=smatch' .PHONY: help # 'compile_commands.json' generation # # Create the compilation database 'compile_commands.json'. See # https://clang.llvm.org/docs/JSONCompilationDatabase.html for details. # .PHONY: compdb compdb: $(MAKE) clean ifneq ($(shell command -v compiledb),) compiledb $(MAKE) else ifneq ($(shell command -v bear),) ifeq ($(shell bear --help|grep -- '-- ...'),) bear $(MAKE) else bear -- $(MAKE) endif else $(error Please install either 'compiledb' or 'bear') endif # Prints the s390-tools release string version: $(info $(S390_TOOLS_RELEASE)) .PHONY: version # Automatic dependency generation # # Create ".o.d" dependency files with the -MM compile option for all ".c" and # ".cpp" files in the directory of the Makefile that includes common.mak: # # $ gcc -MM vmcp.c # vmcp.o: vmcp.c vmcp.h ../include/zt_common.h # # Use -MM instead of -M to *not* mention system header files. We expect # "make clean all" in case of system header updates. # We consider header files in three possible directories sources_h = \ $(wildcard *.h) \ $(wildcard ../include/*.h) \ $(wildcard $(rootdir)/include/lib/*.h) # Rules to create ".o.d" files out of ".c" or ".cpp" files: .%.o.d: %.c $(sources_h) $(CC_SILENT) -MM $(ALL_CPPFLAGS) $(ALL_CFLAGS) $< > $@ .%.o.d: %.cpp $(sources_h) $(CXX_SILENT) -MM $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) $< > $@ # The sources_c/cpp variable contains a list of all ".c" or ".cpp" files in # in the current directory. sources_c = $(wildcard *.c) sources_cpp = $(wildcard *.cpp) # The dependencies_c/cpp variable contains a list of all ".o.d" files, # one for each ".c" or ".cpp" file. dependencies_c = $(sources_c:%.c=.%.o.d) dependencies_cpp = $(sources_cpp:%.cpp=.%.o.d) # Include all ".o.d" dependency files for all make targets except for "clean" ifneq ($(MAKECMDGOALS),clean) -include $(dependencies_c) -include $(dependencies_cpp) endif # Rules for internal libraries needed to ensure that these files are build # with their own build flags even if they are build from external directories. # # Because of the PHONY directory dependency all tools that use libraries # check the library directory via "make -C" when the tools Makefile is # processed. $(rootdir)/libutil/libutil.a: $(rootdir)/libutil $(MAKE) -C $(rootdir)/libutil/ libutil.a .PHONY: $(rootdir)/libutil $(rootdir)/libccw/libccw.a: $(rootdir)/libccw $(MAKE) -C $(rootdir)/libccw/ libccw.a .PHONY: $(rootdir)/libccw $(rootdir)/libvtoc/libvtoc.a: $(rootdir)/libvtoc $(MAKE) -C $(rootdir)/libvtoc/ libvtoc.a .PHONY: $(rootdir)/libvtoc $(rootdir)/libdasd/libdasd.a: $(rootdir)/libdasd $(MAKE) -C $(rootdir)/libdasd/ libdasd.a .PHONY: $(rootdir)/libdasd $(rootdir)/libzds/libzds.a: $(rootdir)/libzds $(MAKE) -C $(rootdir)/libzds/ libzds.a .PHONY: $(rootdir)/libzds $(rootdir)/libvmcp/libvmcp.a: $(rootdir)/libvmcp $(MAKE) -C $(rootdir)/libvmcp/ libvmcp.a .PHONY: $(rootdir)/libvmcp $(rootdir)/libcpumf/libcpumf.a: $(rootdir)/libcpumf $(MAKE) -C $(rootdir)/libcpumf/ libcpumf.a .PHONY: $(rootdir)/libcpumf $(rootdir)/libekmfweb/libekmfweb.so: $(rootdir)/libekmfweb $(MAKE) -C $(rootdir)/libekmfweb/ libekmfweb.so .PHONY: $(rootdir)/libekmfweb $(rootdir)/libseckey/libseckey.a: $(rootdir)/libseckey $(MAKE) -C $(rootdir)/libseckey/ libseckey.a .PHONY: $(rootdir)/libseckey $(rootdir)/libkmipclient/libkmipclient.so: $(rootdir)/libkmipclient $(MAKE) -C $(rootdir)/libkmipclient/ libkmipclient.so .PHONY: $(rootdir)/libkmipclient $(rootdir)/libap/libap.a: $(rootdir)/libap $(MAKE) -C $(rootdir)/libap/ libap.a .PHONY: $(rootdir)/libap $(rootdir)/libpv/libpv.a: $(rootdir)/libpv $(MAKE) -C $(rootdir)/libpv libpv.a .PHONY: $(rootdir)/libpv $(rootdir)/libzpci/libzpci.a: $(rootdir)/libzpci $(MAKE) -C $(rootdir)/libzpci libzpci.a .PHONY: $(rootdir)/libzpci $(rootdir)/zipl/boot/.loaders: $(MAKE) -C $(rootdir)/zipl/boot/ .loaders install_dirs: for dir in $(INSTDIRS); do \ test -d $(DESTDIR)$$dir || $(INSTALL) -g $(GROUP) -o $(OWNER) -d $(DESTDIR)$$dir; \ done for i in 1 2 3 4 5 6 7 8; do \ test -d $(DESTDIR)$(MANDIR)/man$$i || $(INSTALL) -g $(GROUP) -o $(OWNER) \ -d $(DESTDIR)$(MANDIR)/man$$i; \ done install_echo: $(call echocmd," INSTALL ") install: install_echo install_dirs clean_echo: $(call echocmd," CLEAN ") clean_gcov: rm -f -- *.gcda *.gcno *.gcov clean_dep: rm -f -- .*.o.d clean: clean_echo clean_gcov clean_dep endif s390-tools-2.38.0/cpacfstats/000077500000000000000000000000001502674226300156025ustar00rootroot00000000000000s390-tools-2.38.0/cpacfstats/Makefile000066400000000000000000000016261502674226300172470ustar00rootroot00000000000000include ../common.mak ifeq (${HAVE_LIBUDEV},0) all: $(SKIP) HAVE_LIBUDEV=0 install: $(SKIP) HAVE_LIBUDEV=0 else check_dep: $(call check_dep, \ "cpacfstatsd", \ "libudev.h", \ "systemd-devel", \ "HAVE_LIBUDEV=0") ALL_CPPFLAGS += -DVERSION=$(VERSION) all: check_dep cpacfstats cpacfstatsd cpacfstatsd: cpacfstatsd.o stats_sock.o perf_crypto.o cpacfstats_common.o \ $(rootdir)/libutil/libutil.a $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -ludev -lpthread -o $@ cpacfstats: cpacfstats.o stats_sock.o cpacfstats_common.o $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ install: all $(INSTALL) -m 755 cpacfstatsd $(DESTDIR)$(USRSBINDIR) $(INSTALL) -m 755 cpacfstats $(DESTDIR)$(USRBINDIR) $(INSTALL) -m 644 cpacfstatsd.8 $(DESTDIR)$(MANDIR)/man8 $(INSTALL) -m 644 cpacfstats.1 $(DESTDIR)$(MANDIR)/man1 endif clean: rm -f *.o *~ cpacfstatsd cpacfstats .PHONY: all clean install check_dep s390-tools-2.38.0/cpacfstats/cpacfstats.1000066400000000000000000000263041502674226300200240ustar00rootroot00000000000000.\" cpacfstats.1 .\" .\" Copyright IBM Corp. 2015, 2022 .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .\" use .\" groff -man -Tutf8 cpacfstats.1 .\" or .\" nroff -man cpacfstats.1 .\" to process this source .\" .TH cpacfstats "1" "January 2015" "s390-tools" . .ds c \fcpacfstats\fP . .SH NAME cpacfstats \- enable, disable and display CPACF statistical data . .SH SYNOPSIS .B cpacfstats .RB [ \-h | \-\-help ] .RB [ \-v | \-\-version ] .RB [ \-e | \-\-enable .I counter .RB ] .RB [ \-d | \-\-disable .I counter .RB ] .RB [ \-r | \-\-reset .I counter .RB ] .RB [ \-p | \-\-print .I counter .RB [ \-n | \-\-nonzero] .RB ] .RB [ \-j | \-\-json ] . .SH DESCRIPTION The cpacfstats client application interacts with the cpacfstatsd daemon and triggers actions. The application enables, disables, resets, and fetches one or all of the mainframe CPACF performance counters with the help of the daemon process. All counters are initially disabled and must be switched on to measure CPACF activities of the system. There is a slight performance penalty with CPACF counters enabled. CPACF activity counters come in two flavors: CPU-MF and PAI. CPU-MF counters are only available on LPARs and have to be authorized. If they are available, the counters .B des, .B aes, .B sha, .B rng, and .B ecc are made available. These counters can individually be activated, reset, printed, or deactivated. PAI counters are a lot more detailed. The user interface only offers the counters .B pai_user and .B pai_kernel to count CPACF usage in user-space or kernel-space. When printing these counters, detailed counters are shown. A complete list of counters can be found at the end of this manpage. Note that the counters starting with PCKMO and Reserved are only available in the pai_kernel set. Also note that the counters are designed to count successful operations. In the case of KMA this means only complete GCM operations including final hashing are counted. Note: CPU-MF based CPACF performance counters are available on LPARs only. PAI counters are available on all hypervisors. For security reasons only members of the group \fIcpacfstats\fR are allowed to run the cpacfstats client application. Example usage scenario: .P 1. Start the cpacfstatsd daemon with root privileges. .P 2. Check for successful startup by using the ps and syslog commands. .P 3. Enable the CPACF counters of interest. For example, enable all counters by issuing cpacfstats -e. .P 4. Run your applications. .P 5. Display counter values by using the cpacfstats command. Reset the cryptographic counters as required. To reset, use, for example, cpacfstats -r. .P 6. Disable all the CPACF measurements, for example, by using cpacfstats -d. .P 7. Shutdown the cpacfstatsd daemon by using killall cpacfstatsd. .SH OPTIONS .TP \fB\-h\fR or \fB\-\-help\fR Display help information for the command. .TP \fB\-v\fR or \fB\-\-version\fR Display version and copyright information for the command. .TP \fB\-e\fR or \fB\-\-enable\fR [counter] Enable one or all CPACF performance counters. The optional counter argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR, \fBecc\fR, or \fBall\fR. If the counter argument is omitted, all performance counters are enabled. Enabling a counter does not reset it. New events are added to the current counter value. .TP \fB\-d\fR or \fB\-\-disable\fR [counter] Disable one or all CPACF performance counters. The optional counter argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR, \fBecc\fR, or \fBall\fR. If the counter argument is omitted, all performance counters are disabled. Disabling a counter does not reset it. The counter value is preserved when a counter is disabled, and counting will resume using the preserved value when the counter is re-enabled. .TP \fB\-r\fR or \fB\-\-reset\fR [counter] Reset one or all CPACF performance counters. The optional counter argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR, \fBecc\fR, or \fBall\fR. If the counter argument is omitted, all performance counters are reset to 0. .TP \fB\-p\fR or \fB\-\-print\fR [\fB\-n\fR or \fB\-\-nonzero\fR] [counter] Display the value of one or all CPACF performance counters. The optional counter argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR, \fBecc\fR, \fBpai_user\fR, \fBpai_kernel\fR, or \fBall\fR. If the counter argument is omitted or if there is no argument, all performance counters are displayed. If the optional \fB\-n\fR or \fB\-\-nonzero\fR argument is given, then only PAI counters that have a non-zero value are printed. .TP \fB\-j\fR or \fB\-\-json\fR Display all activated counters in JSON format. The JSON contains an array of counter objects. Each object contains the property .B counter specifying either a CPU-MF counter of one of the detailed PAI counter. Additional properties include .B error an error number if the counter could not be read, .B value the counter value if the counter could be read, .B space for PAI counters to specify .B user or .B kernel space counter set, and .B counterid for PAI counters to specify the PAI counter number as specified in the Principles of Operation. .TP The default command is --print all. . .SH FILES .nf /run/cpacfstatsd_socket .fi . .SH RETURN VALUE .IP 0 Successful program execution. .IP 1 An error occurred, reasons include: invalid argument, cpacfstatsd could not be reached (check that the daemon is running), insufficient access rights, version mismatch between client and daemon, or the application is out of memory. The application prints a message with the details of the error and the errno value. . .SH NOTES ECC counters are only available since z15. cpacfstats will show the counters as \fIunsupported\fR if the hardware does not support ECC counters. . .SH APPENDIX The detailed pai counter names are: .RS .IP \(bu KM DES, .IP \(bu KM 2key TDES, .IP \(bu KM TDES, .IP \(bu KM DES protected key, .IP \(bu KM 2key TDES protected key, .IP \(bu KM TDES protected key, .IP \(bu KM AES 128bit, .IP \(bu KM AES 192bit, .IP \(bu KM AES 256bit, .IP \(bu KM AES 128bit protected key, .IP \(bu KM AES 192bit protected key, .IP \(bu KM AES 256bit protected key, .IP \(bu KM AES-XTS 128bit, .IP \(bu KM AES-XTS 256bit, .IP \(bu KM AES-XTS 128bit protected key, .IP \(bu KM AES-XTS 256bit protected key, .IP \(bu KMC DES, .IP \(bu KMC 2key TDES, .IP \(bu KMC TDES, .IP \(bu KMC DES protected key, .IP \(bu KMC 2key TDES protected key, .IP \(bu KMC TDES protected key, .IP \(bu KMC AES 128bit, .IP \(bu KMC AES 192bit, .IP \(bu KMC AES 256bit, .IP \(bu KMC AES 128bit protected key, .IP \(bu KMC AES 192bit protected key, .IP \(bu KMC AES 256bit protected key, .IP \(bu KMC PRNG, .IP \(bu KMA AES 128bit, .IP \(bu KMA AES 192bit, .IP \(bu KMA AES 256bit, .IP \(bu KMA AES 128bit protected key, .IP \(bu KMA AES 192bit protected key, .IP \(bu KMA AES 256bit protected key, .IP \(bu KMF DES, .IP \(bu KMF 2key TDES, .IP \(bu KMF TDES, .IP \(bu KMF DES protected key, .IP \(bu KMF 2key TDES protected key, .IP \(bu KMF TDES protected key, .IP \(bu KMF AES 128bit, .IP \(bu KMF AES 192bit, .IP \(bu KMF AES 256bit, .IP \(bu KMF AES 128bit protected key, .IP \(bu KMF AES 192bit protected key, .IP \(bu KMF AES 256bit protected key, .IP \(bu KMCTR DES, .IP \(bu KMCTR 2key TDES, .IP \(bu KMCTR TDES, .IP \(bu KMCTR DES protected key, .IP \(bu KMCTR 2key TDES protected key, .IP \(bu KMCTR TDES protected key, .IP \(bu KMCTR AES 128bit, .IP \(bu KMCTR AES 192bit, .IP \(bu KMCTR AES 256bit, .IP \(bu KMCTR AES 128bit protected key, .IP \(bu KMCTR AES 192bit protected key, .IP \(bu KMCTR AES 256bit protected key, .IP \(bu KMO DES, .IP \(bu KMO 2key TDES, .IP \(bu KMO TDES, .IP \(bu KMO DES protected key, .IP \(bu KMO 2key TDES protected key, .IP \(bu KMO TDES protected key, .IP \(bu KMO AES 128bit, .IP \(bu KMO AES 192bit, .IP \(bu KMO AES 256bit, .IP \(bu KMO AES 128bit protected key, .IP \(bu KMO AES 192bit protected key, .IP \(bu KMO AES 256bit protected key, .IP \(bu KIMD SHA1, .IP \(bu KIMD SHA256, .IP \(bu KIMD SHA512, .IP \(bu KIMD SHA3-224, .IP \(bu KIMD SHA3-256, .IP \(bu KIMD SHA3-384, .IP \(bu KIMD SHA3-512, .IP \(bu KIMD SHAKE 128, .IP \(bu KIMD SHAKE 256, .IP \(bu KIMD GHASH, .IP \(bu KLMD SHA1, .IP \(bu KLMD SHA256, .IP \(bu KLMD SHA512, .IP \(bu KLMD SHA3-224, .IP \(bu KLMD SHA3-256, .IP \(bu KLMD SHA3-384, .IP \(bu KLMD SHA3-512, .IP \(bu KLMD SHAKE 128, .IP \(bu KLMD SHAKE 256, .IP \(bu KMAC DES, .IP \(bu KMAC 2key TDES, .IP \(bu KMAC TDES, .IP \(bu KMAC DES protected key, .IP \(bu KMAC 2key TDES protected key, .IP \(bu KMAC TDES protected key, .IP \(bu KMAC AES 128bit, .IP \(bu KMAC AES 192bit, .IP \(bu KMAC AES 256bit, .IP \(bu KMAC AES 128bit protected key, .IP \(bu KMAC AES 192bit protected key, .IP \(bu KMAC AES 256bit protected key, .IP \(bu PCC Last Block CMAC DES, .IP \(bu PCC Last Block CMAC 2key TDES, .IP \(bu PCC Last Block CMAC TDES, .IP \(bu PCC Last Block CMAC DES protected key, .IP \(bu PCC Last Block CMAC 2key TDES protected key, .IP \(bu PCC Last Block CMAC TDES protected key, .IP \(bu PCC Last Block CMAC AES 128bit, .IP \(bu PCC Last Block CMAC AES 192bit, .IP \(bu PCC Last Block CMAC AES 256bit, .IP \(bu PCC Last Block CMAC AES 128bit protected key, .IP \(bu PCC Last Block CMAC AES 192bit protected key, .IP \(bu PCC Last Block CMAC AES 256bit protected key, .IP \(bu PCC XTS Parameter AES 128bit, .IP \(bu PCC XTS Parameter AES 256bit, .IP \(bu PCC XTS Parameter AES 128bit protected key, .IP \(bu PCC XTS Parameter AES 256bit protected key, .IP \(bu PCC Scalar Mult P256, .IP \(bu PCC Scalar Mult P384, .IP \(bu PCC Scalar Mult P521, .IP \(bu PCC Scalar Mult Ed25519, .IP \(bu PCC Scalar Mult Ed448, .IP \(bu PCC Scalar Mult X25519, .IP \(bu PCC Scalar Mult X448, .IP \(bu PRNO SHA512 DRNG, .IP \(bu PRNO TRNG Query Ratio, .IP \(bu PRNO TRNG, .IP \(bu KDSA ECDSA Verify P256, .IP \(bu KDSA ECDSA Verify P384, .IP \(bu KDSA ECDSA Verify P521, .IP \(bu KDSA ECDSA Sign P256, .IP \(bu KDSA ECDSA Sign P384, .IP \(bu KDSA ECDSA Sign P521, .IP \(bu KDSA ECDSA Sign P256 protected key, .IP \(bu KDSA ECDSA Sign P384 protected key, .IP \(bu KDSA ECDSA Sign P521 protected key, .IP \(bu KDSA EdDSA Verify Ed25519, .IP \(bu KDSA EdDSA Verify Ed448, .IP \(bu KDSA EdDSA Sign Ed25519, .IP \(bu KDSA EdDSA Sign Ed448, .IP \(bu KDSA EdDSA Sign Ed25519 protected key, .IP \(bu KDSA EdDSA Sign Ed448 protected key, .IP \(bu PCKMO DES, .IP \(bu PCKMO 2key TDES, .IP \(bu PCKMO TDES, .IP \(bu PCKMO AES 128bit, .IP \(bu PCKMO AES 192bit, .IP \(bu PCKMO AES 256bit, .IP \(bu PCKMO ECC P256, .IP \(bu PCKMO ECC P384, .IP \(bu PCKMO ECC P521, .IP \(bu PCKMO ECC Ed25519, .IP \(bu PCKMO ECC Ed448, .IP \(bu Reserved 1, and .IP \(bu Reserved 2. .IP \(bu KM AES-XTS (full) 128bit .IP \(bu KM AES-XTS (full) 256bit .IP \(bu KM AES-XTS (full) 128bit protected key .IP \(bu KM AES-XTS (full) 256bit protected key .IP \(bu KMAC HMAC SHA 224 .IP \(bu KMAC HMAC SHA 256 .IP \(bu KMAC HMAC SHA 384 .IP \(bu KMAC HMAC SHA 512 .IP \(bu KMAC HMAC SHA 224 protected key .IP \(bu KMAC HMAC SHA 256 protected key .IP \(bu KMAC HMAC SHA 384 protected key .IP \(bu KMAC HMAC SHA 512 protected key .IP \(bu PCKMO HMAC 512 protected key .IP \(bu PCKMO HMAC 1024 protected key .IP \(bu PCKMO AES-XTS 128bit double key protected key .IP \(bu PCKMO AES-XTS 256bit double key protected key .RE . .SH SEE ALSO .BR cpacfstatsd (8) s390-tools-2.38.0/cpacfstats/cpacfstats.c000066400000000000000000000226701502674226300201100ustar00rootroot00000000000000/* * cpacfstats - display and maintain CPACF perf counters * * cpacfstats client implementation * * Copyright IBM Corp. 2015, 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #define __STDC_FORMAT_MACROS #include #include #include #include #include #include #include #include "lib/zt_common.h" #include "cpacfstats.h" static const char *const name = "cpacfstats"; static const char *const usage = "Usage: %s [OPTIONS [COUNTER]]\n" "\n" "Enable, disable, reset and read CPACF Crypto Activity Counters\n" "Use OPTIONS described below:\n" "\n" "\t-h, --help Print this help, then exit\n" "\t-v, --version Print version information, then exit\n" "\t-e, --enable [counter] Enable one or all counters\n" "\t-d, --disable [counter] Disable one or all counters\n" "\t-r, --reset [counter] Reset one or all counter values\n" "\t-p, --print [counter] Print one or all counter values\n" "\t-n, --nonzero Print all PAI counters\n" "\t-j, --json Print all counter values in JSON format\n" "\tcounter can be: 'aes' 'des' 'rng' 'sha' 'ecc'\n" "\t 'pai_user' 'pai_kernel' or 'all'\n"; static const char *const counter_str[] = { [DES_FUNCTIONS] = "des", [AES_FUNCTIONS] = "aes", [SHA_FUNCTIONS] = "sha", [PRNG_FUNCTIONS] = "rng", [ECC_FUNCTIONS] = "ecc", [ALL_COUNTER] = "all", [PAI_USER] = "pai_user", [PAI_KERNEL] = "pai_kernel" }; static int paiprintnonzero; static int send_query(int s, enum cmd_e cmd, enum ctr_e ctr) { struct msg m; memset(&m, 0, sizeof(m)); m.head.m_ver = VERSION; m.head.m_type = QUERY; m.query.m_ctr = ctr; m.query.m_cmd = cmd; return send_msg(s, &m, 0); } static int recv_answer(int s, int *ctr, int *state, uint64_t *value) { struct msg m; int rc; rc = recv_msg(s, &m, 0); if (rc == 0) { if (m.head.m_ver != VERSION) { eprint("Received msg with wrong version %d != %d\n", m.head.m_ver, VERSION); return -1; } if (m.head.m_type != ANSWER) { eprint("Received msg with wrong type %d != %d\n", m.head.m_type, ANSWER); return -1; } *ctr = m.answer.m_ctr; *state = m.answer.m_state; *value = m.answer.m_value; } return rc; } static void printjsonsep(void) { static const char *jsonsep = ""; fputs(jsonsep, stdout); jsonsep = ","; } static void json_print_virtual_counter_answer(int s, int ctr, int state, uint64_t value) { int paictr = 0, paistate = 0, ec; uint64_t i, paivalue = 0; unsigned int maxnum; const char *space; switch (ctr) { case HOTPLUG_DETECTED: printjsonsep(); printf("{\"counter\":\"hotplug detected\","); if (state < 0) printf("\"error\":%d}", state); else printf("\"value\":%d}", !!value); return; case PAI_USER: maxnum = get_num_user_space_ctrs(); space = "user"; break; case PAI_KERNEL: maxnum = MAX_NUM_PAI; space = "kernel"; break; default: return; } /* Here, we have validated the PAI counter retrieved, but not * yet printed. */ if (state != ENABLED) return; if (value > maxnum) { eprint("Incompatible versions detected!\n"); eprint("Expected %lu counter space for %s, but got %lu\n", maxnum, space, value); exit(EXIT_FAILURE); } for (i = 0; i < value; ++i) { ec = recv_answer(s, &paictr, &paistate, &paivalue); if (ec < 0 || paistate < 0) { eprint("Error on receiving answer message from daemon\n"); /* No more data for this virtual event after error. */ return; } if (paictr > MAX_NUM_PAI) { eprint("Pai counter number too big: %d\n", paictr); } else { printjsonsep(); printf("{\"counter\":\"%s\",\"space\":\"%s\",\"counterid\":%d,", get_ctr_name(paictr), space, paictr + 1); if (paistate < 0) { printf("\"error\":%d}", paistate); /* Protocol does not send further counters. */ return; } printf("\"value\":%lu}", paivalue); } } } static void print_virtual_counter_answer(int s, int ctr, int state, uint64_t value) { static const char *const states[] = { [DISABLED] = "disabled", [ENABLED] = "enabled", [UNSUPPORTED] = "unsupported" }; int paictr = 0, paistate = 0, ec; uint64_t i, paivalue = 0; unsigned int maxnum; const char *ctrstr; switch (ctr) { case HOTPLUG_DETECTED: if (state >= 0 && value > 0) printf(" hotplug detected\n"); return; case PAI_USER: maxnum = get_num_user_space_ctrs(); ctrstr = "pai_user"; break; case PAI_KERNEL: maxnum = MAX_NUM_PAI; ctrstr = "pai_kernel"; break; default: return; } /* Here, we have validated the PAI counter retrieved, but not * yet printed. */ if (state < 0 || state > UNSUPPORTED) { printf(" %11s: error state %d\n", ctrstr, state); /* No details follow if counter in error state. */ return; } printf(" %-11s: %s\n", ctrstr, states[state]); if (state != ENABLED) return; if (value > maxnum) { eprint("Incompatible versions detected!\n"); eprint("Expected %lu counters for %s, but got %lu\n", maxnum, ctrstr, value); exit(EXIT_FAILURE); } for (i = 0; i < value; ++i) { ec = recv_answer(s, &paictr, &paistate, &paivalue); if (ec < 0 || paistate < 0) { eprint("Error on receiving answer message from daemon\n"); /* No more data for this virtual event after error. */ return; } if (paictr > MAX_NUM_PAI) eprint("Pai counter number too big: %d\n", paictr); else if (!paiprintnonzero || paivalue > 0) printf(" (%3d) %-45s: %lu\n", paictr + 1, get_ctr_name(paictr), paivalue); } } static void print_answer(int s, int ctr, int state, uint64_t value) { if (ctr > ALL_COUNTER) print_virtual_counter_answer(s, ctr, state, value); else if (state < 0) printf(" %s counter: error state %d\n", counter_str[ctr], state); else if (state == DISABLED) printf(" %s counter: disabled\n", counter_str[ctr]); else if (state == UNSUPPORTED) printf(" %s counter: unsupported\n", counter_str[ctr]); else printf(" %s counter: %lu\n", counter_str[ctr], value); } static void json_print_answer(int s, int ctr, int state, uint64_t value) { if (ctr > ALL_COUNTER) { json_print_virtual_counter_answer(s, ctr, state, value); } else if (state < 0) { printjsonsep(); printf("{\"counter\":\"%s\",", counter_str[ctr]); printf("\"error\":%d}\n", state); } else if (state == ENABLED) { printjsonsep(); printf("{\"counter\":\"%s\",", counter_str[ctr]); printf("\"value\":%lu}", value); } } int eprint(const char *format, ...) { char buf[1024]; va_list vargs; int i, n; i = snprintf(buf, sizeof(buf), "%s: ", name); va_start(vargs, format); n = vsnprintf(buf+i, sizeof(buf)-i, format, vargs); va_end(vargs); if (n > 0) fputs(buf, stderr); return n; } int main(int argc, char *argv[]) { enum ctr_e ctr = ALL_COUNTER; enum cmd_e cmd = PRINT; int i, j, s, state, num, json = 0; uint64_t value; if (argc > 1) { int opt, idx = 0; const struct option long_opts[] = { { "help", 0, NULL, 'h' }, { "version", 0, NULL, 'v' }, { "enable", 0, NULL, 'e' }, { "disable", 0, NULL, 'd' }, { "reset", 0, NULL, 'r' }, { "print", 0, NULL, 'p' }, { "nonzero", 0, NULL, 'n' }, { "json", 0, NULL, 'j' }, { NULL, 0, NULL, 0 } }; while (1) { opt = getopt_long(argc, argv, "hvedrpnj", long_opts, &idx); if (opt == -1) break; /* no more arguments */ switch (opt) { case 'h': printf(usage, name); return 0; case 'v': printf("%s: Linux on System z CPACF Crypto Activity Counters Client\n" "Version %s\n%s\n", name, RELEASE_STRING, COPYRIGHT); return 0; case 'e': cmd = ENABLE; json = 0; break; case 'd': cmd = DISABLE; json = 0; break; case 'r': cmd = RESET; break; case 'p': cmd = PRINT; json = 0; break; case 'n': paiprintnonzero = 1; break; case 'j': cmd = PRINT; json = 1; break; default: eprint("Invalid argument, try -h or --help for more information\n"); return EXIT_FAILURE; } } /* there may be an optional counter argument */ if (optind > 0 && optind < argc) { for (i = 0; i < NUM_COUNTER; i++) if (strcmp(argv[optind], counter_str[i]) == 0) break; if (i >= NUM_COUNTER) { eprint("Unknown counter '%s'\n", argv[optind]); return EXIT_FAILURE; } ctr = (enum ctr_e) i; } } if (json) ctr = ALL_COUNTER; /* try to open and connect socket to the cpacfstatsd daemon */ s = open_socket(CLIENT); if (s < 0) { eprint("Can't connect to daemon\n"); return EXIT_FAILURE; } /* send query */ if (send_query(s, cmd, ctr) != 0) { eprint("Error on sending query message to daemon\n"); close(s); return EXIT_FAILURE; } if (ctr == ALL_COUNTER) { /* The -1 is for ALL_COUNTER which is not sent, +1 for * hotplug state. */ num = NUM_COUNTER - 1 + 1; } else { /* +1 for hotplug state */ num = 1 + 1; } if (json) putchar('['); for (i = 0; i < num; i++) { /* receive answer */ if (recv_answer(s, &j, &state, &value) != 0) { eprint("Error on receiving answer message from daemon\n"); return EXIT_FAILURE; } if (state < 0) { eprint("Received bad status code %d from daemon\n", state); close(s); return EXIT_FAILURE; } if (json) json_print_answer(s, j, state, value); else print_answer(s, j, state, value); } if (json) putchar(']'); /* close connection */ close(s); return 0; } s390-tools-2.38.0/cpacfstats/cpacfstats.h000066400000000000000000000065611502674226300201160ustar00rootroot00000000000000/* * cpacfstats - display and maintain CPACF perf counters * * common function prototypes and definitions * * Copyright IBM Corp. 2015, 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef CPACFSTATS_H #define CPACFSTATS_H #include "lib/zt_common.h" #define COPYRIGHT "Copyright IBM Corp. 2015, 2022" #define DEFAULT_SEND_TIMEOUT (30 * 1000) #define DEFAULT_RECV_TIMEOUT (30 * 1000) /* * Number of PAI counters. Contains all counters regardless of kernel or user * space */ #define MAX_NUM_PAI 172 /* * This is the sysfs directory from which cpacfstatsd daemon application loads * the available PAI counters */ #define SYSFS_PAI_COUNTER "/sys/bus/event_source/devices/pai_crypto/events/" /* * Note that this is the first kernel only counter in the 1-based list of the * architecture and NOT from the 0-based list in the cpacfstats code! */ #define FIRST_KERNEL_ONLY_COUNTER 144 int eprint(const char *format, ...); /* * Counter names * ALL_COUNTER specifies the number of physical counters. Virtual * counters might be added afterwards. NUM_COUNTER is the last * managed counter (i.e., a counter that can be activated, reset, * deactivated). */ enum ctr_e { DES_FUNCTIONS = 0, AES_FUNCTIONS, SHA_FUNCTIONS, PRNG_FUNCTIONS, ECC_FUNCTIONS, ALL_COUNTER, PAI_USER, PAI_KERNEL, NUM_COUNTER, HOTPLUG_DETECTED = 0xffff }; enum type_e { QUERY = 0, ANSWER }; enum cmd_e { PRINT = 0, ENABLE, DISABLE, RESET }; enum state_e { DISABLED = 0, ENABLED, UNSUPPORTED }; enum counter_type { SUPPRESS_COUNTER = 0, KERNEL_AND_USER_COUNTER, KERNEL_ONLY_COUNTER, }; /* * query send from client to daemon * Consist of: * enum counter * enum command */ struct msg_query { uint32_t m_ctr; uint32_t m_cmd; } __packed; /* * answer send from daemon to client * Consist of: * enum counter or PAI counter number if following PAI_USER or PAI_KERNEL * status code: < 0 error, 0 disabled, > 0 enabled * counter value */ struct msg_answer { uint32_t m_ctr; int32_t m_state; uint64_t m_value; } __packed; /* stats_sock.c */ #define SERVER 1 #define CLIENT 2 #define BACKLOG 10 #define SOCKET_FILE "/run/cpacfstatsd_socket" #define PID_FILE "/run/cpacfstatsd.pid" #define CPACFSTATS_GROUP "cpacfstats" struct msg_header { uint32_t m_ver; uint32_t m_type; } __packed; struct msg { struct msg_header head; union { struct msg_query query; struct msg_answer answer; }; } __packed; int open_socket(int mode); int send_msg(int sfd, struct msg *m, int timeout); int recv_msg(int sfd, struct msg *m, int timeout); /* perf_crypto.c */ int perf_init(unsigned int *supported_counters); void perf_stop(void); void perf_close(void); int perf_enable_ctr(enum ctr_e ctr, unsigned int *supported_counters); int perf_disable_ctr(enum ctr_e ctr, unsigned int *supported_counters); int perf_reset_ctr(enum ctr_e ctr, uint64_t *value, unsigned int *supported_counters); int perf_read_ctr(enum ctr_e ctr, uint64_t *value, unsigned int *supported_counters); int perf_ecc_supported(void); int perf_ctr_state(enum ctr_e ctr); int perf_read_pai_ctr(unsigned int ctrnum, int user, uint64_t *value); /* cpacfstats_common.c */ enum counter_type is_user_space(unsigned int ctr); const char *get_ctr_name(unsigned int ctr); unsigned int get_num_user_space_ctrs(void); #endif s390-tools-2.38.0/cpacfstats/cpacfstats_common.c000066400000000000000000000270541502674226300214610ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ /* * cpacfstats_common.c - shared code by daemon and client * * Copyright IBM Corp. 2024 */ #include #include #include "cpacfstats.h" struct pai_counter { const char *str; const unsigned int counter_type; }; /* * Strings for the pai counter details. * Integer indicating if kernel space is needed (0 for user, KERNEL_ONLY_COUNTER for kernel) * Note that this is 0-based while PoP is 1-based. * * When adding new items to this list add the counter number in the pai_idx * list in cpacfstatsd.c and increase the number of total counters in * cpacfstats.h. */ const struct pai_counter pai[] = { [ 0] = {"KM DES", KERNEL_AND_USER_COUNTER}, [ 1] = {"KM 2key TDES", KERNEL_AND_USER_COUNTER}, [ 2] = {"KM TDES", KERNEL_AND_USER_COUNTER}, [ 3] = {"KM DES protected key", KERNEL_AND_USER_COUNTER}, [ 4] = {"KM 2key TDES protected key", KERNEL_AND_USER_COUNTER}, [ 5] = {"KM TDES protected key", KERNEL_AND_USER_COUNTER}, [ 6] = {"KM AES 128bit", KERNEL_AND_USER_COUNTER}, [ 7] = {"KM AES 192bit", KERNEL_AND_USER_COUNTER}, [ 8] = {"KM AES 256bit", KERNEL_AND_USER_COUNTER}, [ 9] = {"KM AES 128bit protected key", KERNEL_AND_USER_COUNTER}, [ 10] = {"KM AES 192bit protected key", KERNEL_AND_USER_COUNTER}, [ 11] = {"KM AES 256bit protected key", KERNEL_AND_USER_COUNTER}, [ 12] = {"KM AES-XTS 128bit", KERNEL_AND_USER_COUNTER}, [ 13] = {"KM AES-XTS 256bit", KERNEL_AND_USER_COUNTER}, [ 14] = {"KM AES-XTS 128bit protected key", KERNEL_AND_USER_COUNTER}, [ 15] = {"KM AES-XTS 256bit protected key", KERNEL_AND_USER_COUNTER}, [ 16] = {"KMC DES", KERNEL_AND_USER_COUNTER}, [ 17] = {"KMC 2key TDES", KERNEL_AND_USER_COUNTER}, [ 18] = {"KMC TDES", KERNEL_AND_USER_COUNTER}, [ 19] = {"KMC DES protected key", KERNEL_AND_USER_COUNTER}, [ 20] = {"KMC 2key TDES protected key", KERNEL_AND_USER_COUNTER}, [ 21] = {"KMC TDES protected key", KERNEL_AND_USER_COUNTER}, [ 22] = {"KMC AES 128bit", KERNEL_AND_USER_COUNTER}, [ 23] = {"KMC AES 192bit", KERNEL_AND_USER_COUNTER}, [ 24] = {"KMC AES 256bit", KERNEL_AND_USER_COUNTER}, [ 25] = {"KMC AES 128bit protected key", KERNEL_AND_USER_COUNTER}, [ 26] = {"KMC AES 192bit protected key", KERNEL_AND_USER_COUNTER}, [ 27] = {"KMC AES 256bit protected key", KERNEL_AND_USER_COUNTER}, [ 28] = {"KMC PRNG", KERNEL_AND_USER_COUNTER}, [ 29] = {"KMA AES 128bit", KERNEL_AND_USER_COUNTER}, [ 30] = {"KMA AES 192bit", KERNEL_AND_USER_COUNTER}, [ 31] = {"KMA AES 256bit", KERNEL_AND_USER_COUNTER}, [ 32] = {"KMA AES 128bit protected key", KERNEL_AND_USER_COUNTER}, [ 33] = {"KMA AES 192bit protected key", KERNEL_AND_USER_COUNTER}, [ 34] = {"KMA AES 256bit protected key", KERNEL_AND_USER_COUNTER}, [ 35] = {"KMF DES", KERNEL_AND_USER_COUNTER}, [ 36] = {"KMF 2key TDES", KERNEL_AND_USER_COUNTER}, [ 37] = {"KMF TDES", KERNEL_AND_USER_COUNTER}, [ 38] = {"KMF DES protected key", KERNEL_AND_USER_COUNTER}, [ 39] = {"KMF 2key TDES protected key", KERNEL_AND_USER_COUNTER}, [ 40] = {"KMF TDES protected key", KERNEL_AND_USER_COUNTER}, [ 41] = {"KMF AES 128bit", KERNEL_AND_USER_COUNTER}, [ 42] = {"KMF AES 192bit", KERNEL_AND_USER_COUNTER}, [ 43] = {"KMF AES 256bit", KERNEL_AND_USER_COUNTER}, [ 44] = {"KMF AES 128bit protected key", KERNEL_AND_USER_COUNTER}, [ 45] = {"KMF AES 192bit protected key", KERNEL_AND_USER_COUNTER}, [ 46] = {"KMF AES 256bit protected key", KERNEL_AND_USER_COUNTER}, [ 47] = {"KMCTR DES", KERNEL_AND_USER_COUNTER}, [ 48] = {"KMCTR 2key TDES", KERNEL_AND_USER_COUNTER}, [ 49] = {"KMCTR TDES", KERNEL_AND_USER_COUNTER}, [ 50] = {"KMCTR DES protected key", KERNEL_AND_USER_COUNTER}, [ 51] = {"KMCTR 2key TDES protected key", KERNEL_AND_USER_COUNTER}, [ 52] = {"KMCTR TDES protected key", KERNEL_AND_USER_COUNTER}, [ 53] = {"KMCTR AES 128bit", KERNEL_AND_USER_COUNTER}, [ 54] = {"KMCTR AES 192bit", KERNEL_AND_USER_COUNTER}, [ 55] = {"KMCTR AES 256bit", KERNEL_AND_USER_COUNTER}, [ 56] = {"KMCTR AES 128bit protected key", KERNEL_AND_USER_COUNTER}, [ 57] = {"KMCTR AES 192bit protected key", KERNEL_AND_USER_COUNTER}, [ 58] = {"KMCTR AES 256bit protected key", KERNEL_AND_USER_COUNTER}, [ 59] = {"KMO DES", KERNEL_AND_USER_COUNTER}, [ 60] = {"KMO 2key TDES", KERNEL_AND_USER_COUNTER}, [ 61] = {"KMO TDES", KERNEL_AND_USER_COUNTER}, [ 62] = {"KMO DES protected key", KERNEL_AND_USER_COUNTER}, [ 63] = {"KMO 2key TDES protected key", KERNEL_AND_USER_COUNTER}, [ 64] = {"KMO TDES protected key", KERNEL_AND_USER_COUNTER}, [ 65] = {"KMO AES 128bit", KERNEL_AND_USER_COUNTER}, [ 66] = {"KMO AES 192bit", KERNEL_AND_USER_COUNTER}, [ 67] = {"KMO AES 256bit", KERNEL_AND_USER_COUNTER}, [ 68] = {"KMO AES 128bit protected key", KERNEL_AND_USER_COUNTER}, [ 69] = {"KMO AES 192bit protected key", KERNEL_AND_USER_COUNTER}, [ 70] = {"KMO AES 256bit protected key", KERNEL_AND_USER_COUNTER}, [ 71] = {"KIMD SHA1", KERNEL_AND_USER_COUNTER}, [ 72] = {"KIMD SHA256", KERNEL_AND_USER_COUNTER}, [ 73] = {"KIMD SHA512", KERNEL_AND_USER_COUNTER}, [ 74] = {"KIMD SHA3-224", KERNEL_AND_USER_COUNTER}, [ 75] = {"KIMD SHA3-256", KERNEL_AND_USER_COUNTER}, [ 76] = {"KIMD SHA3-384", KERNEL_AND_USER_COUNTER}, [ 77] = {"KIMD SHA3-512", KERNEL_AND_USER_COUNTER}, [ 78] = {"KIMD SHAKE 128", KERNEL_AND_USER_COUNTER}, [ 79] = {"KIMD SHAKE 256", KERNEL_AND_USER_COUNTER}, [ 80] = {"KIMD GHASH", KERNEL_AND_USER_COUNTER}, [ 81] = {"KLMD SHA1", KERNEL_AND_USER_COUNTER}, [ 82] = {"KLMD SHA256", KERNEL_AND_USER_COUNTER}, [ 83] = {"KLMD SHA512", KERNEL_AND_USER_COUNTER}, [ 84] = {"KLMD SHA3-224", KERNEL_AND_USER_COUNTER}, [ 85] = {"KLMD SHA3-256", KERNEL_AND_USER_COUNTER}, [ 86] = {"KLMD SHA3-384", KERNEL_AND_USER_COUNTER}, [ 87] = {"KLMD SHA3-512", KERNEL_AND_USER_COUNTER}, [ 88] = {"KLMD SHAKE 128", KERNEL_AND_USER_COUNTER}, [ 89] = {"KLMD SHAKE 256", KERNEL_AND_USER_COUNTER}, [ 90] = {"KMAC DES", KERNEL_AND_USER_COUNTER}, [ 91] = {"KMAC 2key TDES", KERNEL_AND_USER_COUNTER}, [ 92] = {"KMAC TDES", KERNEL_AND_USER_COUNTER}, [ 93] = {"KMAC DES protected key", KERNEL_AND_USER_COUNTER}, [ 94] = {"KMAC 2key TDES protected key", KERNEL_AND_USER_COUNTER}, [ 95] = {"KMAC TDES protected key", KERNEL_AND_USER_COUNTER}, [ 96] = {"KMAC AES 128bit", KERNEL_AND_USER_COUNTER}, [ 97] = {"KMAC AES 192bit", KERNEL_AND_USER_COUNTER}, [ 98] = {"KMAC AES 256bit", KERNEL_AND_USER_COUNTER}, [ 99] = {"KMAC AES 128bit protected key", KERNEL_AND_USER_COUNTER}, [100] = {"KMAC AES 192bit protected key", KERNEL_AND_USER_COUNTER}, [101] = {"KMAC AES 256bit protected key", KERNEL_AND_USER_COUNTER}, [102] = {"PCC Last Block CMAC DES", KERNEL_AND_USER_COUNTER}, [103] = {"PCC Last Block CMAC 2key TDES", KERNEL_AND_USER_COUNTER}, [104] = {"PCC Last Block CMAC TDES", KERNEL_AND_USER_COUNTER}, [105] = {"PCC Last Block CMAC DES protected key", KERNEL_AND_USER_COUNTER}, [106] = {"PCC Last Block CMAC 2key TDES protected key", KERNEL_AND_USER_COUNTER}, [107] = {"PCC Last Block CMAC TDES protected key", KERNEL_AND_USER_COUNTER}, [108] = {"PCC Last Block CMAC AES 128bit", KERNEL_AND_USER_COUNTER}, [109] = {"PCC Last Block CMAC AES 192bit", KERNEL_AND_USER_COUNTER}, [110] = {"PCC Last Block CMAC AES 256bit", KERNEL_AND_USER_COUNTER}, [111] = {"PCC Last Block CMAC AES 128bit protected key", KERNEL_AND_USER_COUNTER}, [112] = {"PCC Last Block CMAC AES 192bit protected key", KERNEL_AND_USER_COUNTER}, [113] = {"PCC Last Block CMAC AES 256bit protected key", KERNEL_AND_USER_COUNTER}, [114] = {"PCC XTS Parameter AES 128bit", KERNEL_AND_USER_COUNTER}, [115] = {"PCC XTS Parameter AES 256bit", KERNEL_AND_USER_COUNTER}, [116] = {"PCC XTS Parameter AES 128bit protected key", KERNEL_AND_USER_COUNTER}, [117] = {"PCC XTS Parameter AES 256bit protected key", KERNEL_AND_USER_COUNTER}, [118] = {"PCC Scalar Mult P256", KERNEL_AND_USER_COUNTER}, [119] = {"PCC Scalar Mult P384", KERNEL_AND_USER_COUNTER}, [120] = {"PCC Scalar Mult P521", KERNEL_AND_USER_COUNTER}, [121] = {"PCC Scalar Mult Ed25519", KERNEL_AND_USER_COUNTER}, [122] = {"PCC Scalar Mult Ed448", KERNEL_AND_USER_COUNTER}, [123] = {"PCC Scalar Mult X25519", KERNEL_AND_USER_COUNTER}, [124] = {"PCC Scalar Mult X448", KERNEL_AND_USER_COUNTER}, [125] = {"PRNO SHA512 DRNG", KERNEL_AND_USER_COUNTER}, [126] = {"PRNO TRNG Query Ratio", KERNEL_AND_USER_COUNTER}, [127] = {"PRNO TRNG", KERNEL_AND_USER_COUNTER}, [128] = {"KDSA ECDSA Verify P256", KERNEL_AND_USER_COUNTER}, [129] = {"KDSA ECDSA Verify P384", KERNEL_AND_USER_COUNTER}, [130] = {"KDSA ECDSA Verify P521", KERNEL_AND_USER_COUNTER}, [131] = {"KDSA ECDSA Sign P256", KERNEL_AND_USER_COUNTER}, [132] = {"KDSA ECDSA Sign P384", KERNEL_AND_USER_COUNTER}, [133] = {"KDSA ECDSA Sign P521", KERNEL_AND_USER_COUNTER}, [134] = {"KDSA ECDSA Sign P256 protected key", KERNEL_AND_USER_COUNTER}, [135] = {"KDSA ECDSA Sign P384 protected key", KERNEL_AND_USER_COUNTER}, [136] = {"KDSA ECDSA Sign P521 protected key", KERNEL_AND_USER_COUNTER}, [137] = {"KDSA EdDSA Verify Ed25519", KERNEL_AND_USER_COUNTER}, [138] = {"KDSA EdDSA Verify Ed448", KERNEL_AND_USER_COUNTER}, [139] = {"KDSA EdDSA Sign Ed25519", KERNEL_AND_USER_COUNTER}, [140] = {"KDSA EdDSA Sign Ed448", KERNEL_AND_USER_COUNTER}, [141] = {"KDSA EdDSA Sign Ed25519 protected key", KERNEL_AND_USER_COUNTER}, [142] = {"KDSA EdDSA Sign Ed448 protected key", KERNEL_AND_USER_COUNTER}, [143] = {"PCKMO DES", KERNEL_ONLY_COUNTER}, [144] = {"PCKMO 2key TDES", KERNEL_ONLY_COUNTER}, [145] = {"PCKMO TDES", KERNEL_ONLY_COUNTER}, [146] = {"PCKMO AES 128bit", KERNEL_ONLY_COUNTER}, [147] = {"PCKMO AES 192bit", KERNEL_ONLY_COUNTER}, [148] = {"PCKMO AES 256bit", KERNEL_ONLY_COUNTER}, [149] = {"PCKMO ECC P256", KERNEL_ONLY_COUNTER}, [150] = {"PCKMO ECC P384", KERNEL_ONLY_COUNTER}, [151] = {"PCKMO ECC P521", KERNEL_ONLY_COUNTER}, [152] = {"PCKMO ECC Ed25519", KERNEL_ONLY_COUNTER}, [153] = {"PCKMO ECC Ed448", KERNEL_ONLY_COUNTER}, [154] = {"Reserved 1", KERNEL_ONLY_COUNTER}, [155] = {"Reserved 2", KERNEL_ONLY_COUNTER}, [156] = {"KM AES-XTS (full) 128bit", KERNEL_AND_USER_COUNTER}, [157] = {"KM AES-XTS (full) 256bit", KERNEL_AND_USER_COUNTER}, [158] = {"KM AES-XTS (full) 128bit protected key", KERNEL_AND_USER_COUNTER}, [159] = {"KM AES-XTS (full) 256bit protected key", KERNEL_AND_USER_COUNTER}, [160] = {"KMAC HMAC SHA 224", KERNEL_AND_USER_COUNTER}, [161] = {"KMAC HMAC SHA 256", KERNEL_AND_USER_COUNTER}, [162] = {"KMAC HMAC SHA 384", KERNEL_AND_USER_COUNTER}, [163] = {"KMAC HMAC SHA 512", KERNEL_AND_USER_COUNTER}, [164] = {"KMAC HMAC SHA 224 protected key", KERNEL_AND_USER_COUNTER}, [165] = {"KMAC HMAC SHA 256 protected key", KERNEL_AND_USER_COUNTER}, [166] = {"KMAC HMAC SHA 384 protected key", KERNEL_AND_USER_COUNTER}, [167] = {"KMAC HMAC SHA 512 protected key", KERNEL_AND_USER_COUNTER}, [168] = {"PCKMO HMAC 512 protected key", KERNEL_ONLY_COUNTER}, [169] = {"PCKMO HMAC 1024 protected key", KERNEL_ONLY_COUNTER}, [170] = {"PCKMO AES-XTS 128bit double key protected key", KERNEL_ONLY_COUNTER}, [171] = {"PCKMO AES-XTS 256bit double key protected key", KERNEL_ONLY_COUNTER} }; /* * Returns counter_type of pai_counter struct * * SUPPRESS_COUNTER * KERNEL_AND_USER_COUNTER * KERNEL_ONLY_COUNTER */ enum counter_type is_user_space(unsigned int ctr) { if (ctr >= MAX_NUM_PAI) return SUPPRESS_COUNTER; return pai[ctr].counter_type; } const char *get_ctr_name(unsigned int ctr) { if (ctr >= MAX_NUM_PAI) return NULL; return pai[ctr].str; } /* * Returns number of PAI counters for which no kernel space is needed */ unsigned int get_num_user_space_ctrs(void) { unsigned int counter = 0; unsigned int i; for (i = 0; i < MAX_NUM_PAI; i++) { if (is_user_space(i) == KERNEL_AND_USER_COUNTER) counter++; } return counter; } s390-tools-2.38.0/cpacfstats/cpacfstatsd.8000066400000000000000000000065121502674226300201760ustar00rootroot00000000000000.\" cpacfstatsd.8 .\" .\" Copyright IBM Corp. 2015, 2020 .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .\" use .\" groff -man -Tutf8 cpacfstatsd.8 .\" or .\" nroff -man cpacfstatsd.8 .\" to process this source .\" .TH cpacfstatsd "8" "January 2015" "s390-tools" . .ds c \fcpacfstatsd\fP . .SH NAME cpacfstatsd \- CPACF statistics collection daemon process . .SH SYNOPSIS .B cpacfstatsd .RB [ \-h | \-\-help ] .RB [ \-v | \-\-version ] .RB [ \-f | \-\-foreground ] . .SH DESCRIPTION The cpacfstatsd controlling daemon enables, disables, resets, and fetches the mainframe CPACF performance counter registers. The daemon receives commands from the user application cpacfstats through the UNIX Domain Socket, processes them and returns the requested information. For all available commands, see the cpacfstats man page. Prerequisites .P - The running Linux kernel must have the the CONFIG_PERF_EVENTS config option enabled. .P - Libpfm version 4 or higher is needed to successfully run the daemon. .P - On the HMC or SE, authorize the LPAR for each counter set you want to use. Customize the LPAR activation profile and modify the Counter Facility Security Options. You need to activate the "Crypto activity counter set authorization control" checkbox. .P - The daemon requires root privileges to interact with the performance ioctls of the kernel. CPU hotplug is recognized by the daemon. When adding or removing a CPU, the daemon ensures correct summing of the per-CPU performance counters. The starting daemon first checks for any stale pid file \%/run/cpacfstatsd.pid. If this file exists, and the process ID in the file belongs to an active process, an error message is printed to the console and the program terminates. The daemon and the client cpacfstats communicate through a Unix Domain Socket. This socket is created by the daemon at startup with the associated socket file /run/cpacfstatsd_socket. For security reasons only members of the group \fIcpacfstats\fR are allowed to communicate with the daemon. A system administrator should create this group and add all users which are allowed to run the cpacfstats client to the group. After startup, the daemon runs in the background and detaches from any terminal. Errors and warnings are posted to the syslog subsystem. Check the process list and the system syslog messages for confirmation of successful startup. On regular termination the pid file, the communication socket and the associated file is removed gracefully. .SH OPTIONS .TP \fB\-h\fR or \fB\-\-help\fR Display help information for the command. .TP \fB\-v\fR or \fB\-\-version\fR Display version and copyright information for the command. .TP \fB\-f\fR or \fB\-\-foreground\fR Run the daemon in foreground mode, thus printing errors to stderr instead of posting them through syslog. This option might be useful when debugging daemon startup and initialization failures. .SH FILES .nf /run/cpacfstatsd_socket /run/cpacfstatsd.pid .fi .SH RETURN VALUE .IP 0 The daemon was successfully set to run in the background. This does not imply that the daemon startup was successful, as the main initialization is done in the re-spawned process. Check the syslog for success or failure. .IP 1 The daemon could not be set to run in the background. .SH SEE ALSO .BR cpacfstats (1) s390-tools-2.38.0/cpacfstats/cpacfstatsd.c000066400000000000000000000356471502674226300202640ustar00rootroot00000000000000/* * cpacfstats - display and maintain CPACF perf counters * * cpacfstatsd daemon implementation * * Copyright IBM Corp. 2015, 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #define __STDC_FORMAT_MACROS #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/zt_common.h" #include "lib/util_file.h" #include "cpacfstats.h" static volatile int stopsig; /* * This list contains the counter numbers sorted by instruction */ static const unsigned int pai_idx[] = { // KM 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 156, 157, 158, 159, // KMC 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, // KMA 29, 30, 31, 32, 33, 34, // KMF 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, // KMCTR 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, // KMO 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, // KIMD 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, // KLMD 81, 82, 83, 84, 85, 86, 87, 88, 89, // KMAC 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 160, 161, 162, 163, 164, 165, 166, 167, // PCC 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, // PRNO 125, 126, 127, // KDSA 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, // PCKMO 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 168, 169, 170, 171, // Reserved 154, 155 }; static const char *const name = "cpacfstatsd"; static const char *const usage = "Usage: %s [OPTIONS]\n" "\n" "Daemon to provide access to CPACF perf counters\n" "Use OPTIONS described below:\n" "\n" "\t-h, --help Print this help, then exit\n" "\t-v, --version Print version information, then exit\n" "\t-f, --foreground Run in foreground, do not detach\n"; static int daemonized; static int recv_query(int s, enum ctr_e *ctr, enum cmd_e *cmd) { struct msg m; int rc; rc = recv_msg(s, &m, DEFAULT_RECV_TIMEOUT); if (rc == 0) { if (m.head.m_ver != VERSION) { eprint("Received msg with wrong version %d != %d\n", m.head.m_ver, VERSION); return -1; } if (m.head.m_type != QUERY) { eprint("Received msg with wrong type %d != %d\n", m.head.m_type, QUERY); return -1; } *ctr = m.query.m_ctr; *cmd = m.query.m_cmd; } return rc; } static int send_answer(int s, int ctr, int state, uint64_t value) { struct msg m; memset(&m, 0, sizeof(m)); m.head.m_ver = VERSION; m.head.m_type = ANSWER; m.answer.m_ctr = ctr; m.answer.m_state = state; m.answer.m_value = value; return send_msg(s, &m, DEFAULT_SEND_TIMEOUT); } /* * Print according to protocol for PAI: * - first the state and the number of PAI counters that follow * - if state is ENABLED: * - for each PAI counter the value with state ENABLED * Note that the PAI counters are 0-based, not 1 based as in PoP! * Sending ends with the first error. */ static int do_send_pai(int s, int user, unsigned int *counter) { int ctr, state, i, rc = 0; unsigned int current_ctr; uint64_t value; ctr = user ? PAI_USER : PAI_KERNEL; state = perf_ctr_state(ctr); if (state != ENABLED) return rc; for (i = 0; i < MAX_NUM_PAI; ++i) { current_ctr = pai_idx[i]; if ((user && is_user_space(current_ctr) != KERNEL_AND_USER_COUNTER) || (!user && is_user_space(current_ctr) == SUPPRESS_COUNTER) || counter[current_ctr] != 1) continue; rc = perf_read_pai_ctr(current_ctr, user, &value); if (rc != 0) { send_answer(s, current_ctr, rc, 0); break; } send_answer(s, current_ctr, state, value); } return rc; } static int do_enable(int s, enum ctr_e ctr, unsigned int *supported_counters) { uint64_t value = 0; int i, rc = 0; int state; for (i = 0; i < NUM_COUNTER; i++) { if (i == ALL_COUNTER) continue; if (i == (int) ctr || ctr == ALL_COUNTER) { state = perf_ctr_state(i); if (state == DISABLED) { rc = perf_enable_ctr(i, supported_counters); if (rc != 0) { send_answer(s, i, rc, 0); break; } state = ENABLED; } if (state != UNSUPPORTED) { rc = perf_read_ctr(i, &value, supported_counters); if (rc != 0) { send_answer(s, i, rc, 0); break; } } send_answer(s, i, state, value); if (i == PAI_USER) rc = do_send_pai(s, 1, supported_counters); if (i == PAI_KERNEL) rc = do_send_pai(s, 0, supported_counters); } } if (rc == 0) { rc = perf_read_ctr(HOTPLUG_DETECTED, &value, NULL); send_answer(s, HOTPLUG_DETECTED, rc, value); } return rc; } static int do_disable(int s, enum ctr_e ctr, unsigned int *supported_counters) { int i, rc = 0; uint64_t value; for (i = 0; i < NUM_COUNTER; i++) { if (i == ALL_COUNTER) continue; if (i == (int) ctr || ctr == ALL_COUNTER) { if (perf_ctr_state(i) == ENABLED) { rc = perf_disable_ctr(i, supported_counters); if (rc != 0) { send_answer(s, i, rc, 0); break; } } send_answer(s, i, perf_ctr_state(i), 0); } } if (rc == 0) { rc = perf_read_ctr(HOTPLUG_DETECTED, &value, NULL); send_answer(s, HOTPLUG_DETECTED, rc, value); } return rc; } static int do_reset(int s, enum ctr_e ctr, unsigned int *supported_counters) { int i, rc = 0, state; uint64_t value; for (i = 0; i < NUM_COUNTER; i++) { if (i == ALL_COUNTER) continue; if (i == (int) ctr || ctr == ALL_COUNTER) { state = perf_ctr_state(i); if (state == ENABLED) { rc = perf_reset_ctr(i, &value, supported_counters); if (rc != 0) { send_answer(s, i, rc, 0); break; } } send_answer(s, i, state, value); if (i == PAI_USER) rc = do_send_pai(s, 1, supported_counters); if (i == PAI_KERNEL) rc = do_send_pai(s, 0, supported_counters); } } if (rc == 0) { rc = perf_read_ctr(HOTPLUG_DETECTED, &value, NULL); send_answer(s, HOTPLUG_DETECTED, rc, value); } return rc; } static int do_print(int s, enum ctr_e ctr, unsigned int *supported_counters) { int i, rc = 0, state; uint64_t value = 0; for (i = 0; i < NUM_COUNTER; i++) { if (i == ALL_COUNTER) continue; if (i == (int) ctr || ctr == ALL_COUNTER) { state = perf_ctr_state(i); if (state == ENABLED) { rc = perf_read_ctr(i, &value, supported_counters); if (rc != 0) { send_answer(s, i, rc, 0); break; } } send_answer(s, i, state, value); if (i == PAI_USER) rc = do_send_pai(s, 1, supported_counters); if (i == PAI_KERNEL) rc = do_send_pai(s, 0, supported_counters); } } if (rc == 0) { rc = perf_read_ctr(HOTPLUG_DETECTED, &value, NULL); send_answer(s, HOTPLUG_DETECTED, rc, value); } return rc; } static int become_daemon(int *startup_pipe) { int child_initialized = 0, fd; int pipefds[2]; FILE *f; /* syslog */ openlog("cpacfstatsd", 0, LOG_DAEMON); if (pipe(pipefds) != 0) { eprint("pipe() failed, errno=%d [%s]\n", errno, strerror(errno)); return -1; } /* * fork and terminate parent * Reasons: * - opens new command line prompt * - the child process is guaranteed not to be the process group leader * necessary for setsid. */ switch (fork()) { case -1: /* error */ eprint("Fork() failed, errno=%d [%s]\n", errno, strerror(errno)); return -1; case 0: /* child */ break; default: /* parent */ (void)close(pipefds[1]); if (read(pipefds[0], &child_initialized, sizeof(child_initialized)) != sizeof(child_initialized)) { eprint("Couldn't read from PIPE, errno=%d [%s]\n", errno, strerror(errno)); (void)close(pipefds[0]); _exit(EXIT_FAILURE); } (void)close(pipefds[0]); if (!child_initialized) _exit(EXIT_FAILURE); _exit(0); } /* Executed within the child context only */ (void)close(pipefds[0]); *startup_pipe = pipefds[1]; if (chdir("/") != 0) { eprint("Chdir('/') failed, errno=%d [%s]\n", errno, strerror(errno)); return -1; } /* start new session */ if (setsid() == -1) { eprint("Setsid() failed, errno=%d [%s]\n", errno, strerror(errno)); return -1; } /* clear umask so that socket has right default permission */ umask(0007); /* make stdin, stdout and stderr use /dev/null */ fd = open("/dev/null", O_RDWR); if (fd < 0) { eprint("Could not open /dev/null, errno=%d [%s]\n", errno, strerror(errno)); return -1; } dup2(fd, STDIN_FILENO); dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); close(fd); daemonized = 1; /* make pid file, fails if the file exists */ f = fopen(PID_FILE, "w+x"); if (!f) { eprint("Couldn't create pid file '%s', errno=%d [%s]\n", PID_FILE, errno, strerror(errno)); return -1; } fprintf(f, "%lu", (unsigned long)getpid()); fflush(f); fclose(f); chmod(PID_FILE, 0644); return 0; } static void remove_sock(void) { remove(SOCKET_FILE); } static int check_pidfile(void) { unsigned long pid; FILE *f; f = fopen(PID_FILE, "r"); if (!f) { if (errno == ENOENT) { /* pid file does not exit, pid file check is ok */ return 0; } /* unknown errno, pid file check is not ok */ eprint("Unknown error on pid file check '%s', errno=%d [%s]\n", PID_FILE, errno, strerror(errno)); return -1; } /* pid file could be opened, scan pid in there */ if (fscanf(f, "%lu", &pid) != 1) { /* * invalid, maybe a leftover from a previous run * remove and return pid file check ok */ fclose(f); remove(PID_FILE); return 0; } fclose(f); /* check if this process is still running */ if (kill(pid, 0) != 0) { /* * failure, assume this means there is no such pid running * remove pid file and return pid file check ok */ remove(PID_FILE); return 0; } /* * looks like there is another cpacfstatsd running * return with pid file check failure */ eprint("Looks like there is another cpacfstatsd (pid=%lu) running\n", pid); eprint("Please check and maybe remove stale pid file '%s'\n", PID_FILE); return -1; } static void remove_pidfile(void) { remove(PID_FILE); } void signalhandler(int sig) { perf_stop(); stopsig = sig; } int eprint(const char *format, ...) { char buf[512]; va_list vargs; int i, n; i = snprintf(buf, sizeof(buf), "%s: ", name); va_start(vargs, format); n = vsnprintf(buf+i, sizeof(buf)-i, format, vargs); va_end(vargs); if (n > 0) { if (daemonized) syslog(LOG_WARNING, "%s", buf); else fputs(buf, stderr); } return n; } /* * returns -1 on error * returns X where X is the found counters in dir * * the supplied array supported_counters[] is filled in this function with the * available PAI counters found in SYSFS_PAI_COUNTER */ static void supported_functions(unsigned int supported_counters[]) { const char *dir = SYSFS_PAI_COUNTER; struct dirent *dp = NULL; char filepath[PATH_MAX]; unsigned int num; DIR *dfd = NULL; dfd = opendir(dir); if (dfd == NULL) return; while ((dp = readdir(dfd)) != NULL) { if ((strcmp(dp->d_name, ".") != 0) && (strcmp(dp->d_name, "..") != 0)) { snprintf(filepath, sizeof(filepath), "%s%s", dir, dp->d_name); if (util_file_read_va(filepath, "event=0x10%x", &num) != 1) continue; if (num > 0 && num <= MAX_NUM_PAI) supported_counters[num - 1] = 1; } } closedir(dfd); return; } int main(int argc, char *argv[]) { int rc, sfd, foreground = 0, startup_pipe = -1, initialized = 0; unsigned int supported_counters[MAX_NUM_PAI] = { 0 }; struct sigaction act; if (argc > 1) { int opt, idx = 0; const struct option long_opts[] = { { "help", 0, NULL, 'h' }, { "foreground", 0, NULL, 'f' }, { "version", 0, NULL, 'v' }, { NULL, 0, NULL, 0 } }; while (1) { opt = getopt_long(argc, argv, "hfv", long_opts, &idx); if (opt == -1) break; /* no more arguments */ switch (opt) { case 'h': printf(usage, name); return 0; case 'f': foreground = 1; break; case 'v': printf("%s: Linux on System z CPACF Crypto Activity Counters Daemon\n" "Version %s\n%s\n", name, RELEASE_STRING, COPYRIGHT); return 0; default: printf("%s: Invalid argument, try -h or --help for more information\n", name); return EXIT_FAILURE; } } } if (check_pidfile() != 0) { eprint("Stalled pid file or daemon already running, terminating\n"); return EXIT_FAILURE; } if (!foreground) { if (become_daemon(&startup_pipe) != 0) { eprint("Couldn't daemonize\n"); goto error; } } supported_functions(supported_counters); if (perf_init(supported_counters) != 0) { eprint("Couldn't initialize perf lib\n"); goto error; } atexit(perf_close); sfd = open_socket(SERVER); if (sfd < 0) { eprint("Couldn't initialize server socket\n"); goto error; } atexit(remove_sock); memset(&act, 0, sizeof(act)); act.sa_handler = signalhandler; act.sa_flags = 0; if (sigaction(SIGINT, &act, 0) != 0) { eprint("Couldn't establish signal handler for SIGINT, errno=%d [%s]\n", errno, strerror(errno)); goto error; } if (sigaction(SIGTERM, &act, 0) != 0) { eprint("Couldn't establish signal handler for SIGTERM, errno=%d [%s]\n", errno, strerror(errno)); goto error; } /* Ignore SIGPIPE such that we see EPIPE as return from write. */ signal(SIGPIPE, SIG_IGN); eprint("Running\n"); initialized = 1; /* `startup_pipe` has been initialized, so we know we are * running in daemon mode. Let's write to the pipe so that the * parent knows that the initialization is complete. */ if (startup_pipe != -1 && write(startup_pipe, &initialized, sizeof(initialized)) != sizeof(initialized)) goto error; (void)close(startup_pipe); startup_pipe = -1; while (!stopsig) { enum ctr_e ctr; enum cmd_e cmd; int s; s = accept(sfd, NULL, NULL); if (s < 0) { if (errno == EINTR) continue; eprint("Accept() failure, errno=%d [%s]\n", errno, strerror(errno)); goto error; } rc = recv_query(s, &ctr, &cmd); if (rc != 0) { eprint("Recv_query() failed, ignoring\n"); close(s); continue; } if (cmd == ENABLE) rc = do_enable(s, ctr, supported_counters); else if (cmd == DISABLE) rc = do_disable(s, ctr, supported_counters); else if (cmd == RESET) rc = do_reset(s, ctr, supported_counters); else if (cmd == PRINT) rc = do_print(s, ctr, supported_counters); else { eprint("Received unknown command %d, ignoring\n", (int) cmd); close(s); continue; } } if (stopsig == SIGTERM) eprint("Caught signal SIGTERM, terminating...\n"); else if (stopsig == SIGINT) eprint("Caught signal SIGINT, terminating...\n"); else eprint("Caught signal %d, terminating...\n", stopsig); remove_pidfile(); return 0; error: if (startup_pipe != -1) { /* Notify the parent process that there was an error */ if (write(startup_pipe, &initialized, sizeof(initialized)) != sizeof(initialized)) eprint("Couldn't write to PIPE, errno=%d [%s]\n", errno, strerror(errno)); (void)close(startup_pipe); } return EXIT_FAILURE; } s390-tools-2.38.0/cpacfstats/perf_crypto.c000066400000000000000000000477041502674226300203160ustar00rootroot00000000000000/* * cpacfstats - display and maintain CPACF perf counters * * low level perf functions * * Copyright IBM Corp. 2015, 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #define __STDC_FORMAT_MACROS #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cpacfstats.h" #include "../include/lib/zt_common.h" /* correlation between counter and perf counter string */ static const struct { char pmu[20]; char pfm_name[60]; enum ctr_e ctr; } pmf_counter_name[ALL_COUNTER] = { {"cpum_cf", "DEA_FUNCTIONS", DES_FUNCTIONS}, {"cpum_cf", "AES_FUNCTIONS", AES_FUNCTIONS}, {"cpum_cf", "SHA_FUNCTIONS", SHA_FUNCTIONS}, {"cpum_cf", "PRNG_FUNCTIONS", PRNG_FUNCTIONS}, {"cpum_cf", "ECC_FUNCTION_COUNT", ECC_FUNCTIONS} }; static struct pmf_data { int pmutype; int eventid; } pmf_counter_data[ALL_COUNTER]; struct percpucounter { int ctr_fds[ALL_COUNTER]; int pai_user[MAX_NUM_PAI]; int pai_kernel[MAX_NUM_PAI]; unsigned int cpunum; struct percpucounter *next; }; static struct percpucounter *root; pthread_mutex_t rootmux = PTHREAD_MUTEX_INITIALIZER; static volatile int hotplugdetected; static unsigned int enabledcounter; pthread_t hotplugthread; #define foreachcpu(PCPU) if (pthread_mutex_lock(&rootmux)) return -1; \ for ((PCPU) = root; (PCPU) != NULL; (PCPU) = (PCPU)->next) #define endforeachcpu() pthread_mutex_unlock(&rootmux) static int ctr_state[NUM_COUNTER]; static volatile int stoprequested; static int paipmutype, paipmueventstart; static struct percpucounter *allocpercpucounter(unsigned int cpunum) { struct percpucounter *ppc; ppc = malloc(sizeof(struct percpucounter)); if (ppc) { int i; for (i = 0; i < ALL_COUNTER; ++i) ppc->ctr_fds[i] = -1; for (i = 0; i < MAX_NUM_PAI; ++i) { ppc->pai_user[i] = -1; ppc->pai_kernel[i] = -1; } ppc->cpunum = cpunum; ppc->next = NULL; } return ppc; } static void freepercpucounter(struct percpucounter *pcpu) { int i; for (i = 0; i < ALL_COUNTER; ++i) (void)close(pcpu->ctr_fds[i]); for (i = 0; i < MAX_NUM_PAI; ++i) { (void)close(pcpu->pai_user[i]); (void)close(pcpu->pai_kernel[i]); } free(pcpu); } static struct percpucounter *findcpu(unsigned int cpunum, int unlinkflag) { struct percpucounter **prev = &root, *walk = root; while (walk) { if (walk->cpunum == cpunum) { if (unlinkflag) *prev = walk->next; return walk; } prev = &(walk->next); walk = walk->next; } return NULL; } static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, int group_fd, unsigned long flags) { int ret; ret = syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags); return ret; } static int perf_supported(void) { return !access("/proc/sys/kernel/perf_event_paranoid", R_OK); } static int perf_counter_supported(const char *pmu, const char *counter) { char buf[PATH_MAX]; if (snprintf(buf, PATH_MAX, "/sys/bus/event_source/devices/%s/events/%s", pmu, counter) >= PATH_MAX) { eprint("overflow in path name"); return 0; } return !access(buf, R_OK); } static int cpumf_authorized(void) { unsigned vermin, vermax, auth; int res = 0, found = 0; size_t linesize = 0; char *line = NULL; FILE *f; f = fopen("/proc/service_levels", "r"); if (f == NULL) { eprint("Failed to open /proc/service_levels (%d:%s)\n", errno, strerror(errno)); return 0; } while (getline(&line, &linesize, f) >= 0) { if (sscanf(line, "CPU-MF: Counter facility: version=%d.%d authorization=%x", &vermin, &vermax, &auth) == 3) { if (auth & 0x8) res = 1; else eprint("CPU-MF counters not authorized.\n"); found = 1; break; } } if (!found) eprint("CPU-MF counters not available.\n"); free(line); fclose(f); return res; } static int perf_event_encode(int *pmutype, int *eventid, const char *pmu, const char *event) { FILE *f; char buf[PATH_MAX]; if (snprintf(buf, PATH_MAX, "/sys/bus/event_source/devices/%s/events/%s", pmu, event) >= PATH_MAX) { eprint("overflow in path name"); return -1; } f = fopen(buf, "r"); if (!f) { eprint("Event %s for pmu %s not found (%d:%s)\n", event, pmu, errno, strerror(errno)); return -1; } if (fscanf(f, "event=0x%x\n", eventid) != 1) { fclose(f); eprint("Event file %s has invalid format\n", buf); return -1; } fclose(f); if (snprintf(buf, PATH_MAX, "/sys/bus/event_source/devices/%s/type", pmu) >= PATH_MAX) { eprint("overflow in path name"); return -1; } f = fopen(buf, "r"); if (!f) { eprint("Event %s for pmu %s not found (%d:%s)\n", event, pmu, errno, strerror(errno)); return -1; } if (fscanf(f, "%d\n", pmutype) != 1) { fclose(f); eprint("Type file %s has invalid format\n", buf); return -1; } return 0; } static int activatecpu(unsigned int cpu, unsigned int *supported_counters) { struct perf_event_attr pfm_event; struct percpucounter *ppc; int fd, i, rc = 0; ppc = allocpercpucounter(cpu); if (ppc == NULL) { eprint("Failed to allocate per cpu counter data"); return -1; } if (pthread_mutex_lock(&rootmux)) { freepercpucounter(ppc); return -1; } /* activate CPU-MF */ for (i = 0; i < ALL_COUNTER; ++i) { if (ctr_state[i] == UNSUPPORTED) continue; memset(&pfm_event, 0, sizeof(pfm_event)); pfm_event.size = sizeof(pfm_event); pfm_event.type = pmf_counter_data[i].pmutype; pfm_event.config = pmf_counter_data[i].eventid; /* fetch file descriptor for this perf event * the counter event should start disabled */ pfm_event.disabled = ctr_state[i] == DISABLED; fd = perf_event_open( &pfm_event, -1, /* pid -1 means all processes */ cpu, -1, /* group filedescriptor */ 0); /* flags */ if (fd < 0) { eprint("Perf_event_open() failed with errno=%d [%s]\n", errno, strerror(errno)); rc = -1; ctr_state[i] = UNSUPPORTED; } else { ppc->ctr_fds[i] = fd; } } /* activate pai_user and pai_kernel */ /* invariant: (ctr_state[PAI_USER] == UNSUPPORTED) == (ctr_state[PAI_KERNEL] == UNSUPPORTED) */ if (ctr_state[PAI_USER] != UNSUPPORTED) { for (i = 1; i <= MAX_NUM_PAI; ++i) { if (is_user_space(i - 1) != KERNEL_AND_USER_COUNTER || supported_counters[i - 1] != 1) continue; memset(&pfm_event, 0, sizeof(pfm_event)); pfm_event.size = sizeof(pfm_event); pfm_event.type = paipmutype; pfm_event.config = paipmueventstart + i; pfm_event.exclude_kernel = 1; pfm_event.exclude_user = 0; pfm_event.disabled = ctr_state[PAI_USER] == DISABLED; fd = perf_event_open(&pfm_event, -1, cpu, -1, 0); if (fd < 0) { eprint("Perf_event_open() failed with errno=%d [%s]\n", errno, strerror(errno)); rc = -1; ctr_state[PAI_USER] = UNSUPPORTED; ctr_state[PAI_KERNEL] = UNSUPPORTED; goto outevents; } else { ppc->pai_user[i - 1] = fd; } pfm_event.exclude_kernel = 0; pfm_event.exclude_user = 1; pfm_event.disabled = ctr_state[PAI_KERNEL] == DISABLED; fd = perf_event_open(&pfm_event, -1, cpu, -1, 0); if (fd < 0) { eprint("Perf_event_open() failed with errno=%d [%s]\n", errno, strerror(errno)); rc = -1; ctr_state[PAI_USER] = UNSUPPORTED; ctr_state[PAI_KERNEL] = UNSUPPORTED; goto outevents; } else { ppc->pai_kernel[i - 1] = fd; } } /* * i can start at the index of the first PAI counter * for which kernel space is needed */ for (i = FIRST_KERNEL_ONLY_COUNTER; i <= MAX_NUM_PAI; ++i) { if (is_user_space(i - 1) == SUPPRESS_COUNTER || supported_counters[i - 1] != 1) continue; memset(&pfm_event, 0, sizeof(pfm_event)); pfm_event.size = sizeof(pfm_event); pfm_event.type = paipmutype; pfm_event.config = paipmueventstart + i; pfm_event.exclude_kernel = 0; pfm_event.exclude_user = 1; pfm_event.disabled = ctr_state[PAI_KERNEL] == DISABLED; fd = perf_event_open(&pfm_event, -1, cpu, -1, 0); if (fd < 0) { eprint("Perf_event_open() failed with errno=%d [%s]\n", errno, strerror(errno)); rc = -1; ctr_state[PAI_USER] = UNSUPPORTED; ctr_state[PAI_KERNEL] = UNSUPPORTED; goto outevents; } else { ppc->pai_kernel[i - 1] = fd; } } } outevents: ppc->next = root; root = ppc; if (enabledcounter) hotplugdetected = 1; pthread_mutex_unlock(&rootmux); return rc; } static void deactivatecpu(unsigned int cpunum) { struct percpucounter *pcpu; int i; if (pthread_mutex_lock(&rootmux)) return; pcpu = findcpu(cpunum, 1); if (pcpu != NULL) { for (i = 0; i < ALL_COUNTER; ++i) (void)close(pcpu->ctr_fds[i]); for (i = 0; i < MAX_NUM_PAI; ++i) { (void)close(pcpu->pai_user[i]); (void)close(pcpu->pai_kernel[i]); } free(pcpu); if (enabledcounter) hotplugdetected = 1; } pthread_mutex_unlock(&rootmux); } static int addallcpus(unsigned int *supported_counters) { unsigned int start, end; int scanned, rc = 0; FILE *fp; /* comma separated list of intervals */ if ((fp = fopen("/sys/devices/system/cpu/online", "r")) == NULL) { eprint("Failed to get online cpus (%d:%s)\n", errno, strerror(errno)); return -1; } while (!feof(fp)) { /* scan all intervals of online cpus */ scanned = fscanf(fp, "%u-%u", &start, &end); /* take care of singleton intervals */ if (scanned == 1) end = start; for (; start <= end; ++start) { if (activatecpu(start, supported_counters)) { rc = -1; goto out; } } /* Skip comma separator */ (void)fgetc(fp); } out: fclose(fp); return rc; } static int perf_load_counter_data(void) { int i, res = 0; for (i = 0; i < ALL_COUNTER; ++i) { if (ctr_state[i] != UNSUPPORTED) res |= perf_event_encode(&pmf_counter_data[i].pmutype, &pmf_counter_data[i].eventid, pmf_counter_name[i].pmu, pmf_counter_name[i].pfm_name); } if (ctr_state[PAI_USER] != UNSUPPORTED) res |= perf_event_encode(&paipmutype, &paipmueventstart, "pai_crypto", "CRYPTO_ALL"); return res; } static void *hotplughandler(void *supported_counters) { struct udev *hotplug; struct udev_monitor *monitor; struct pollfd item; hotplug = udev_new(); if (!hotplug) { eprint("Failed to create hotplug device\n"); return NULL; } monitor = udev_monitor_new_from_netlink(hotplug, "udev"); udev_monitor_filter_add_match_subsystem_devtype(monitor, "cpu", NULL); udev_monitor_enable_receiving(monitor); item.fd = udev_monitor_get_fd(monitor); item.events = POLLIN; item.revents = 0; while (!stoprequested) { struct udev_device *dev; const char *path, *action; unsigned int cpunum; int rc, on, off; errno = 0; rc = poll(&item, 1, -1); if (rc == -1) { if (errno == EINTR) continue; break; } dev = udev_monitor_receive_device(monitor); if (dev == NULL) continue; action = udev_device_get_action(dev); if (action == NULL) continue; off = strcmp(action, "offline") == 0; on = strcmp(action, "online") == 0; if (!on && !off) continue; path = udev_device_get_devpath(dev); if (sscanf(path, "/devices/system/cpu/cpu%u", &cpunum) != 1) continue; if (on && activatecpu(cpunum, (unsigned int *) supported_counters)) eprint("Failed to attach to hotplugged CPU %u\n", cpunum); if (off) deactivatecpu(cpunum); } udev_monitor_unref(monitor); udev_unref(hotplug); return NULL; } int perf_init(unsigned int *supported_counters) { static const char *cpum_cf[] = { "DEA_FUNCTIONS", "AES_FUNCTIONS", "SHA_FUNCTIONS", "PRNG_FUNCTIONS", "ECC_FUNCTION_COUNT" }; unsigned long maxfd; struct rlimit rlim; int i, num; FILE *f; /* initialize performance monitoring library */ if (!perf_supported()) { eprint("Performance counter not supported"); return -1; } /* We currently support all cpumf counters plus two virtual * counters for PAI. */ num = ALL_COUNTER + 2; if (!cpumf_authorized()) { for (i = 0; i < ALL_COUNTER; ++i) ctr_state[i] = UNSUPPORTED; num -= ALL_COUNTER; } else { for (i = 0; i < ALL_COUNTER; i++) { if (!perf_counter_supported("cpum_cf", cpum_cf[i])) { ctr_state[i] = UNSUPPORTED; num--; } } } if (!perf_counter_supported("pai_crypto", "CRYPTO_ALL")) { ctr_state[PAI_USER] = UNSUPPORTED; ctr_state[PAI_KERNEL] = UNSUPPORTED; num -= 2; } if (num == 0) eprint("No crypto counters supported!\n"); if (perf_load_counter_data()) return -1; /* We have to adjust the number of FDs possible since we might * need more than 1024 (the typical soft limit) */ f = fopen("/proc/sys/fs/nr_open", "r"); if (f == NULL) { eprint("fopen failed for /proc/sys/fs/nr_open with errno=%d [%s]\n", errno, strerror(errno)); return -1; } if (fscanf(f, "%lu", &maxfd) != 1) { fclose(f); eprint("Failed to parse /proc/sys/fs/nr_open\n"); return -1; } fclose(f); rlim.rlim_cur = maxfd; rlim.rlim_max = maxfd; if (setrlimit(RLIMIT_NOFILE, &rlim) == -1) { eprint("setrlimit failed with errno=%d [%s]\n", errno, strerror(errno)); return -1; } if (pthread_create(&hotplugthread, NULL, hotplughandler, supported_counters)) { eprint("Failed to start hotplug handler thread\n"); return -1; } return addallcpus(supported_counters); } void perf_stop(void) { stoprequested = 1; } void perf_close(void) { struct percpucounter *walk, *next; pthread_kill(hotplugthread, SIGINT); pthread_join(hotplugthread, NULL); walk = root; while (walk) { next = walk->next; freepercpucounter(walk); walk = next; } } static int enable_array(int *arr, int user, unsigned int *supported_counters) { int i, ec, rc = 0; for (i = 0; i < MAX_NUM_PAI; ++i) { if ((user && is_user_space(i) != KERNEL_AND_USER_COUNTER) || supported_counters[i] != 1 || is_user_space(i) == SUPPRESS_COUNTER) continue; ec = ioctl(arr[i], PERF_EVENT_IOC_ENABLE, 0); if (ec < 0) { eprint("Ioctl(PERF_EVENT_IOC_ENABLE) failed with errno=%d [%s]\n", errno, strerror(errno)); rc = -1; } } return rc; } int perf_enable_ctr(enum ctr_e ctr, unsigned int *supported_counters) { struct percpucounter *pcpu; int ec, rc = 0; if (ctr == ALL_COUNTER) { for (ctr = 0; ctr < ALL_COUNTER; ctr++) { rc = perf_enable_ctr(ctr, supported_counters); if (rc != 0) return rc; } } else if (ctr < ALL_COUNTER) { foreachcpu(pcpu) { ec = ioctl(pcpu->ctr_fds[ctr], PERF_EVENT_IOC_ENABLE, 0); if (ec < 0) { eprint("Ioctl(PERF_EVENT_IOC_ENABLE) failed with errno=%d [%s]\n", errno, strerror(errno)); rc = -1; } } ctr_state[ctr] = ENABLED; ++enabledcounter; endforeachcpu(); } else if (ctr == PAI_USER) { foreachcpu(pcpu) { ec = enable_array(pcpu->pai_user, 1, supported_counters); if (ec < 0) rc = -1; } ctr_state[ctr] = ENABLED; ++enabledcounter; endforeachcpu(); } else if (ctr == PAI_KERNEL) { foreachcpu(pcpu) { ec = enable_array(pcpu->pai_kernel, 0, supported_counters); if (ec < 0) rc = -1; } ctr_state[ctr] = ENABLED; ++enabledcounter; endforeachcpu(); } else { rc = -1; } return rc; } static int disable_array(int *arr, int user, unsigned int *supported_counters) { int i, ec, rc = 0; for (i = 0; i < MAX_NUM_PAI; ++i) { if ((user && is_user_space(i) != KERNEL_AND_USER_COUNTER) || supported_counters[i] != 1 || is_user_space(i) == SUPPRESS_COUNTER) continue; ec = ioctl(arr[i], PERF_EVENT_IOC_DISABLE, 0); if (ec < 0) { eprint("Ioctl(PERF_EVENT_IOC_DISABLE) failed with errno=%d [%s]\n", errno, strerror(errno)); rc = -1; } } return rc; } int perf_disable_ctr(enum ctr_e ctr, unsigned int *supported_counters) { struct percpucounter *pcpu; int ec, rc = 0; if (ctr == ALL_COUNTER) { for (ctr = 0; ctr < ALL_COUNTER; ctr++) { rc = perf_disable_ctr(ctr, supported_counters); if (rc != 0) return rc; } } else if (ctr < ALL_COUNTER) { foreachcpu(pcpu) { ec = ioctl(pcpu->ctr_fds[ctr], PERF_EVENT_IOC_DISABLE, 0); if (ec < 0) { eprint("Ioctl(PERF_EVENT_IOC_DISABLE) failed with errno=%d [%s]\n", errno, strerror(errno)); rc = -1; } } ctr_state[ctr] = DISABLED; --enabledcounter; if (enabledcounter == 0) hotplugdetected = 0; endforeachcpu(); } else if (ctr == PAI_USER) { foreachcpu(pcpu) { ec = disable_array(pcpu->pai_user, 1, supported_counters); if (ec < 0) rc = -1; } ctr_state[ctr] = DISABLED; --enabledcounter; if (enabledcounter == 0) hotplugdetected = 0; endforeachcpu(); } else if (ctr == PAI_KERNEL) { foreachcpu(pcpu) { ec = disable_array(pcpu->pai_kernel, 0, supported_counters); if (ec < 0) rc = -1; } ctr_state[ctr] = DISABLED; --enabledcounter; if (enabledcounter == 0) hotplugdetected = 0; endforeachcpu(); } else { rc = -1; } return rc; } static int reset_array(int *arr, int user, unsigned int *supported_counters) { int ec, rc = 0, i; for (i = 0; i < MAX_NUM_PAI; ++i) { if ((user && is_user_space(i) != KERNEL_AND_USER_COUNTER) || supported_counters[i] != 1 || is_user_space(i) == SUPPRESS_COUNTER) continue; ec = ioctl(arr[i], PERF_EVENT_IOC_RESET, 0); if (ec < 0) { eprint("Ioctl(PERF_EVENT_IOC_RESET) failed with errno=%d [%s]\n", errno, strerror(errno)); rc = -1; } } return rc; } int perf_reset_ctr(enum ctr_e ctr, uint64_t *value, unsigned int *supported_counters) { struct percpucounter *pcpu; int ec, rc = 0; if (ctr == ALL_COUNTER) { for (ctr = 0; ctr < ALL_COUNTER; ctr++) { rc = perf_reset_ctr(ctr, value, supported_counters); if (rc != 0) return rc; } } else if (ctr < ALL_COUNTER) { foreachcpu(pcpu) { ec = ioctl(pcpu->ctr_fds[ctr], PERF_EVENT_IOC_RESET, 0); if (ec < 0) { eprint("Ioctl(PERF_EVENT_IOC_RESET) failed with errno=%d [%s]\n", errno, strerror(errno)); rc = -1; } } endforeachcpu(); } else if (ctr == PAI_USER) { foreachcpu(pcpu) { ec = reset_array(pcpu->pai_user, 1, supported_counters); if (ec < 0) rc = -1; } endforeachcpu(); } else if (ctr == PAI_KERNEL) { foreachcpu(pcpu) { ec = reset_array(pcpu->pai_kernel, 0, supported_counters); if (ec < 0) rc = -1; } endforeachcpu(); } else { rc = -1; } if (rc == 0) rc = perf_read_ctr(ctr, value, supported_counters); return rc; } int perf_read_ctr(enum ctr_e ctr, uint64_t *value, unsigned int *supported_counters) { struct percpucounter *pcpu; int ec, rc = 0; uint64_t val; if (!value) return -1; if (ctr == HOTPLUG_DETECTED) { *value = hotplugdetected; return 0; } if (ctr == PAI_USER) { int c = 0; for (int i = 0; i < MAX_NUM_PAI; i++) { if (is_user_space(i) == KERNEL_AND_USER_COUNTER && supported_counters[i] == 1) c++; } *value = c; return 0; } if (ctr == PAI_KERNEL) { int c = 0; for (int i = 0; i < MAX_NUM_PAI; i++) { if (supported_counters[i] == 1) c++; } *value = c; return 0; } if (ctr >= ALL_COUNTER) return -1; *value = 0; foreachcpu(pcpu) { ec = read(pcpu->ctr_fds[ctr], &val, sizeof(val)); if (ec != sizeof(val)) { eprint("Read() on perf file descriptor failed with errno=%d [%s]\n", errno, strerror(errno)); rc = -1; } else { *value += val; } } endforeachcpu(); return rc; } int perf_ecc_supported(void) { return ctr_state[ECC_FUNCTIONS] != UNSUPPORTED; } int perf_ctr_state(enum ctr_e ctr) { if (ctr < NUM_COUNTER) return ctr_state[ctr]; return UNSUPPORTED; } int perf_read_pai_ctr(unsigned int ctrnum, int user, uint64_t *value) { struct percpucounter *pcpu; int *arr, ec, rc = 0; uint64_t val; *value = 0; if (is_user_space(ctrnum) == SUPPRESS_COUNTER || (user && is_user_space(ctrnum) == KERNEL_ONLY_COUNTER)) return -1; foreachcpu(pcpu) { arr = user ? pcpu->pai_user : pcpu->pai_kernel; ec = read(arr[ctrnum], &val, sizeof(val)); if (ec != sizeof(val)) { eprint("Read() on perf file descriptor failed with errno=%d [%s]\n", errno, strerror(errno)); rc = -1; } else { *value += val; } } endforeachcpu(); return rc; } s390-tools-2.38.0/cpacfstats/stats_sock.c000066400000000000000000000123431502674226300201260ustar00rootroot00000000000000/* * cpacfstats - display and maintain CPACF perf counters * * basic socket and receive/send functions * * Copyright IBM Corp. 2015, 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "cpacfstats.h" int open_socket(int mode) { struct sockaddr_un sock_addr; struct group *grp; mode_t m; int s; /* group handling */ grp = getgrnam(CPACFSTATS_GROUP); if (!grp) { eprint("Getgrnam() failed, group '%s' may not exist on this system ?\n", CPACFSTATS_GROUP); return -1; } /* the client checks for the unix domain socket file */ if (mode != SERVER) { if (access(SOCKET_FILE, F_OK) != 0) { eprint("Can't access domain socket file '%s', errno=%d [%s]\n", SOCKET_FILE, errno, strerror(errno)); if (errno == ENOENT) eprint("Maybe cpacfstatsd daemon is not running ???\n"); return -1; } } /* create socket */ s = socket(AF_UNIX, SOCK_STREAM, 0); if (s == -1) { eprint("Socket(AF_UNIX,SOCK_STREAM) failed, errno=%d [%s]\n", errno, strerror(errno)); return -1; } if (mode == SERVER) remove(SOCKET_FILE); memset(&sock_addr, 0, sizeof(sock_addr)); sock_addr.sun_family = AF_UNIX; strncpy(sock_addr.sun_path, SOCKET_FILE, sizeof(sock_addr.sun_path)); sock_addr.sun_path[sizeof(sock_addr.sun_path)-1] = '\0'; if (mode == SERVER) { if (bind(s, (struct sockaddr *) &sock_addr, sizeof(struct sockaddr_un)) < 0) { eprint("Bind('%s') failed, errno=%d [%s]\n", SOCKET_FILE, errno, strerror(errno)); return -1; } /* change group ownership of the socket file */ if (chown(SOCKET_FILE, 0, grp->gr_gid)) { eprint("Chown('%s',...) failed, errno=%d [%s]\n", SOCKET_FILE, errno, strerror(errno)); return -1; } /* adapt permissions */ m = S_IRUSR|S_IWUSR|S_IXUSR | S_IRGRP|S_IWGRP|S_IXGRP; if (chmod(SOCKET_FILE, m)) { eprint("Chmod('%s',...) failed, errno=%d [%s]\n", SOCKET_FILE, errno, strerror(errno)); return -1; } /* now put the socket into listen state */ if (listen(s, BACKLOG) < 0) { eprint("Listen() failed, errno=%d [%s]\n", errno, strerror(errno)); return -1; } } else { if (connect(s, (struct sockaddr *) &sock_addr, sizeof(sock_addr)) < 0) { eprint("Connect() failed, errno=%d [%s]\n", errno, strerror(errno)); return -1; } } return s; } static int __write(int fd, const void *buf, int buflen) { const unsigned char *p = buf; int n, i = 0; while (i < buflen) { n = write(fd, p+i, buflen-i); if (n < 0) { if (errno == EINTR) continue; else return n; } i += n; } return i; } static int __read(int fd, void *buf, int buflen) { unsigned char *p = buf; int n, i = 0; while (i < buflen) { n = read(fd, p+i, buflen-i); if (n < 0) { if (errno == EINTR) continue; else return n; } else if (n == 0) { return i; } else { i += n; } } return i; } static int __timedwrite(int fd, const void *buf, int buflen, int timeout) { struct pollfd pfd = { .fd = fd, .events = POLLOUT }; int i = 0, n; while (poll(&pfd, 1, timeout) == 1) { n = write(fd, buf + i, buflen - i); if (n < 0) { if (errno == EINTR) continue; else return n; } else if (n == 0) { return i; } i += n; if (buflen == i) return i; } return -1; } static int __timedread(int fd, void *buf, int buflen, int timeout) { struct pollfd pfd = { .fd = fd, .events = POLLIN }; int i = 0, n; while (poll(&pfd, 1, timeout) == 1) { n = read(fd, buf + i, buflen - i); if (n < 0) { if (errno == EINTR) continue; else return n; } else if (n == 0) { return i; } i += n; if (buflen == i) return i; } return -1; } int send_msg(int sfd, struct msg *m, int timeout) { int n, len; len = sizeof(m->head); switch (m->head.m_type) { case QUERY: len += sizeof(m->query); break; case ANSWER: len += sizeof(m->answer); break; default: eprint("Unknown type %d\n", m->head.m_type); return -1; } n = timeout ? __timedwrite(sfd, m, len, timeout) : __write(sfd, m, len); if (n != len) { eprint("Write() error: write()=%d expected %d, errno=%d [%s]\n", n, len, errno, strerror(errno)); return -1; } return 0; } int recv_msg(int sfd, struct msg *m, int timeout) { int n, len; len = sizeof(m->head); n = timeout ? __timedread(sfd, m, len, timeout) : __read(sfd, m, len); if (n != len) { eprint("Recv() error: read()=%d expected %d, errno=%d [%s]\n", n, len, errno, strerror(errno)); return -1; } switch (m->head.m_type) { case QUERY: len = sizeof(m->query); break; case ANSWER: len = sizeof(m->answer); break; default: eprint("Unknown type %d\n", m->head.m_type); return -1; } n = timeout ? __timedread(sfd, ((char *)m) + sizeof(m->head), len, timeout) : __read(sfd, ((char *)m) + sizeof(m->head), len); if (n != len) { eprint("Recv() error: recv()=%d expected %d, errno=%d [%s]\n", n, len, errno, strerror(errno)); return -1; } return 0; } s390-tools-2.38.0/cpumf/000077500000000000000000000000001502674226300145615ustar00rootroot00000000000000s390-tools-2.38.0/cpumf/Makefile000066400000000000000000000013421502674226300162210ustar00rootroot00000000000000include ../common.mak BIN_FILES = lscpumf chcpumf lshwc pai lspai MAN_FILES = lscpumf.8 chcpumf.8 lshwc.8 pai.8 lspai.8 all: $(BIN_FILES) libs = $(rootdir)/libcpumf/libcpumf.a $(rootdir)/libutil/libutil.a lscpumf: lscpumf.o $(libs) chcpumf: chcpumf.o $(libs) lshwc: lshwc.o $(libs) pai: pai.o $(libs) lspai: lspai.o $(libs) install: all install-man $(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8 for binf in $(BIN_FILES); do \ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 $$binf $(DESTDIR)$(BINDIR); \ done clean: rm -f *.o *~ $(BIN_FILES) core install-man: for man in $(MAN_FILES); do \ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 man/$$man \ $(DESTDIR)$(MANDIR)/man8 ; \ done .PHONY: all install clean s390-tools-2.38.0/cpumf/chcpumf.c000066400000000000000000000073751502674226300163660ustar00rootroot00000000000000/* * chcpumf - Change CPU Measurement Facility Characteristics * * Copyright IBM Corp. 2020, 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include "lib/libcpumf.h" #include "lib/util_base.h" #include "lib/util_opt.h" #include "lib/util_path.h" #include "lib/util_prg.h" static unsigned int verbose; static unsigned long min_sdb, max_sdb; static struct util_opt opt_vec[] = { UTIL_OPT_SECTION("OPTIONS"), { .option = { "min", required_argument, NULL, 'm' }, .argument = "num_sdb", .desc = "Specifies the initial size of the sampling buffer.\n" "A sample-data-block (SDB) consumes about 4 kilobytes.", }, { .option = { "max", required_argument, NULL, 'x' }, .argument = "num_sdb", .desc = "Specifies the maximum size of the sampling buffer.\n" "A sample-data-block (SDB) consumes about 4 kilobytes.", }, { .option = { "verbose", no_argument, NULL, 'V' }, .desc = "Verbose, display new sample-data-block values.", }, UTIL_OPT_HELP, UTIL_OPT_VERSION, UTIL_OPT_END }; static const struct util_prg prg = { .desc = "Change CPU Measurement facility charactertics", .copyright_vec = { { .owner = "IBM Corp.", .pub_first = 2020, .pub_last = 2020, }, UTIL_PRG_COPYRIGHT_END } }; static long parse_buffersize(char *string) { char *suffix; long bytes; bytes = strtol(string, &suffix, 10); if (strlen(suffix) > 1) return -1; switch (*suffix) { case 'k': case 'K': bytes *= 1024; break; case 'm': case 'M': bytes *= 1048576; break; case '\0': break; default: return 0; } return bytes; } static int write_sfb(unsigned int min, unsigned int max) { int rc = EXIT_SUCCESS; char text[64], *path; size_t len; FILE *fp; path = util_path_sysfs(S390_CPUMSF_BUFFERSZ); fp = fopen(path, "w"); if (!fp) err(EXIT_FAILURE, "%s", path); snprintf(text, sizeof(text), "%u,%u", min, max); len = strlen(text) + 1; if (fwrite(text, 1, len, fp) != len) { warn("%s", path); rc = EXIT_FAILURE; } if (fclose(fp)) { warn("%s", path); rc = EXIT_FAILURE; } if (verbose && rc != EXIT_FAILURE) warnx("Sampling buffer sizes:\n" " Minimum:%7d sample-data-blocks\n" " Maximum:%7d sample-data-blocks\n", min, max); free(path); return rc; } static int parse_args(int argc, char **argv) { int opt, action = 0; long new; while ((opt = util_opt_getopt_long(argc, argv)) != -1) { switch (opt) { case 'h': util_prg_print_help(); util_opt_print_help(); exit(EXIT_SUCCESS); case 'v': util_prg_print_version(); exit(EXIT_SUCCESS); case 'x': new = parse_buffersize(optarg); if (new < 1) errx(EXIT_FAILURE, "The specified number(s) are not valid"); max_sdb = new; action = 1; break; case 'm': new = parse_buffersize(optarg); if (new < 1) errx(EXIT_FAILURE, "The specified number(s) are not valid"); min_sdb = new; action = 1; break; case 'V': verbose = 1; break; default: util_opt_print_parse_error(opt, argv); exit(EXIT_FAILURE); } } if (!action) errx(EXIT_FAILURE, "You must specify a valid option"); return action; } int main(int argc, char **argv) { unsigned long my_min, my_max; util_prg_init(&prg); util_opt_init(opt_vec, NULL); parse_args(argc, argv); if (geteuid()) errx(EXIT_FAILURE, "Must run as root"); if (!libcpumf_have_sfb()) errx(EXIT_FAILURE, "No CPU-measurement sampling facility detected"); libcpumf_sfb_info(&my_min, &my_max); if (!min_sdb) min_sdb = my_min; if (!max_sdb) max_sdb = my_max; return write_sfb(min_sdb, max_sdb); } s390-tools-2.38.0/cpumf/lscpumf.c000066400000000000000000004024641502674226300164100ustar00rootroot00000000000000/* * lscpumf - Show CPU Measurement Facility Characteristics * * Copyright IBM Corp. 2020, 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/util_arch.h" #include "lib/util_base.h" #include "lib/util_opt.h" #include "lib/util_path.h" #include "lib/util_prg.h" #include "lib/libcpumf.h" #define ACTION_NONE 0 #define ACTION_INFO 1 #define ACTION_CNT 2 #define ACTION_CNTALL 3 #define ACTION_SAMPLE 4 static bool actions[ACTION_SAMPLE + 1]; /* Specified command line options */ /* This defines the number of pages a Sample Data Buffer Table (SDBT) can hold * as payload data. Each SDBT is one PAGE (4096 bytes) and continas 512 eight * byte data pointers to Sample Data Buffers (SDB). The last entry of a SDBT * points to another SDBT and can not store payload. */ #define PER_SDBT_SIZE 511 /* File names to read data from */ static struct util_opt opt_vec[] = { UTIL_OPT_SECTION("OPTIONS"), { .option = { "list-counters", no_argument, NULL, 'c' }, .desc = "Lists counters for which the LPAR is authorized.", }, { .option = { "list-all-counters", no_argument, NULL, 'C' }, .desc = "Lists counters regardless of LPAR authorization.", }, { .option = { "name", no_argument, NULL, 'n' }, .desc = "Displays counter names.", }, { .option = { "info", no_argument, NULL, 'i' }, .desc = "Displays detailed information.", }, { .option = { "list-sampling-events", no_argument, NULL, 's' }, .desc = "Lists sampling events for which the LPAR is authorized.", }, UTIL_OPT_HELP, UTIL_OPT_VERSION, UTIL_OPT_END }; static const struct util_prg prg = { .desc = "List CPU Measurement facility charactertics", .copyright_vec = { { .owner = "IBM Corp.", .pub_first = 2020, .pub_last = 2020, }, UTIL_PRG_COPYRIGHT_END } }; static char prefix[32]; /* Counter prefix */ static bool show_names; static struct cpumf_info { int first_vn; /* Counter facility first version nr */ int second_vn; /* Counter facility second version nr */ int authorization; /* Counter facility authorization */ unsigned long min_rate; /* Minimum sampling rate */ unsigned long max_rate; /* Maximum sampling rate */ unsigned long cpu_speed; /* CPU Cycles per micro second */ int basic_sample_sz; /* # of Bytes per basic sample */ int diag_sample_sz; /* # of bytes per diagnostic sample */ bool have_counter; /* CPUM counter facility detected */ bool have_samples; /* CPUM sampling facility detected */ unsigned long min_sfb; /* Minimum sampling buffer size */ unsigned long max_sfb; /* Maximum sampling buffer size */ unsigned short machine_type; /* Machine Type */ } cpumf; /* * Samples definition are identical on all machines. No versioning. */ static struct samples { /* Sample definition for all machines */ unsigned long counter; /* Sample number */ char *name; /* Counter name see SA23-2261 */ char *desc; /* Short description */ char *longdesc; /* Long description */ } def_samples[] = { { .counter = 0xB0000, .name = "SF_CYCLES_BASIC", .desc = "Sample CPU Cycles Using Basic-sampling Mode.", .longdesc = "Sample CPU Cycles Using Basic-sampling Mode." }, { .counter = 0xBD000, .name = "SF_CYCLES_BASIC_DIAG", .desc = "Sample CPU Cycle Using Diagnostic-sampling Mode\n" " (not for ordinary use).", .longdesc = "Sample CPU Cycle Using Diagnostic-sampling Mode\n" " (not for ordinary use).", } }; /* * For exact details and clarifications see document SA23-2260-06 and * SA23-2261-06 (January 2020). * * Counter definitions vary, depending on machine and version numbering. * The CPU Measurement facility has a first and second version number. * * The first version number governs basic counter set and the * problem state counter set. Currently used are first verion numbers 1 and 3. * The counter numbers are identifical for version number 1 and 3, but * have different purpose and description. * * The second version number governs the crypto counter set. Currently used * are numbers 1, 2, 3, 4, 5 and 6. Numbers 1 to 5 use the same counter numbers * and definitions. Number 6 adds 4 more deflate counters but leaves * the other counters in this counter set unchanged. * * The second version number also governs the extended counter set range. * The definition of each extended counter set is machine specific and * determined by machine number. Extended counter set ranges are: * Second version number and range: * Second version number: 1 Range 128 to 159 inclusive (32 counters) * Second version number: 2 Range 128 to 175 inclusive (48 counters) * Second version number: 3,4,5 Range 128 to 255 inclusive (128 counters) * Second version number: 6 Range 128 to 287 inclusive (160 counters) * * The second version number also governs the MT-diagnostic counter set range. * Second version number: 1,2,3 none installed * Second version number: >3 Range 448 to 495 inclusive (48 counters) */ struct counters { int ctrnum; int ctrset; char *name; char *desc; }; static struct counters cpumcf_fvn1_counters[] = { { .ctrnum = 0, .ctrset = CPUMF_CTRSET_BASIC, .name = "CPU_CYCLES", .desc = "Cycle Count", }, { .ctrnum = 1, .ctrset = CPUMF_CTRSET_BASIC, .name = "INSTRUCTIONS", .desc = "Instruction Count", }, { .ctrnum = 2, .ctrset = CPUMF_CTRSET_BASIC, .name = "L1I_DIR_WRITES", .desc = "Level-1 I-Cache Directory Write Count", }, { .ctrnum = 3, .ctrset = CPUMF_CTRSET_BASIC, .name = "L1I_PENALTY_CYCLES", .desc = "Level-1 I-Cache Penalty Cycle Count", }, { .ctrnum = 4, .ctrset = CPUMF_CTRSET_BASIC, .name = "L1D_DIR_WRITES", .desc = "Level-1 D-Cache Directory Write Count", }, { .ctrnum = 5, .ctrset = CPUMF_CTRSET_BASIC, .name = "L1D_PENALTY_CYCLES", .desc = "Level-1 D-Cache Penalty Cycle Count", }, { .ctrnum = 32, .ctrset = CPUMF_CTRSET_PROBLEM_STATE, .name = "PROBLEM_STATE_CPU_CYCLES", .desc = "Problem-State Cycle Count", }, { .ctrnum = 33, .ctrset = CPUMF_CTRSET_PROBLEM_STATE, .name = "PROBLEM_STATE_INSTRUCTIONS", .desc = "Problem-State Instruction Count", }, { .ctrnum = 34, .ctrset = CPUMF_CTRSET_PROBLEM_STATE, .name = "PROBLEM_STATE_L1I_DIR_WRITES", .desc = "Problem-State Level-1 I-Cache Directory Write Count", }, { .ctrnum = 35, .ctrset = CPUMF_CTRSET_PROBLEM_STATE, .name = "PROBLEM_STATE_L1I_PENALTY_CYCLES", .desc = "Problem-State Level-1 I-Cache Penalty Cycle Count", }, { .ctrnum = 36, .ctrset = CPUMF_CTRSET_PROBLEM_STATE, .name = "PROBLEM_STATE_L1D_DIR_WRITES", .desc = "Problem-State Level-1 D-Cache Directory Write Count", }, { .ctrnum = 37, .ctrset = CPUMF_CTRSET_PROBLEM_STATE, .name = "PROBLEM_STATE_L1D_PENALTY_CYCLES", .desc = "Problem-State Level-1 D-Cache Penalty Cycle Count", }, }; static struct counters cpumcf_fvn3_counters[] = { { .ctrnum = 0, .ctrset = CPUMF_CTRSET_BASIC, .name = "CPU_CYCLES", .desc = "Cycle Count", }, { .ctrnum = 1, .ctrset = CPUMF_CTRSET_BASIC, .name = "INSTRUCTIONS", .desc = "Instruction Count", }, { .ctrnum = 2, .ctrset = CPUMF_CTRSET_BASIC, .name = "L1I_DIR_WRITES", .desc = "Level-1 I-Cache Directory Write Count", }, { .ctrnum = 3, .ctrset = CPUMF_CTRSET_BASIC, .name = "L1I_PENALTY_CYCLES", .desc = "Level-1 I-Cache Penalty Cycle Count", }, { .ctrnum = 4, .ctrset = CPUMF_CTRSET_BASIC, .name = "L1D_DIR_WRITES", .desc = "Level-1 D-Cache Directory Write Count", }, { .ctrnum = 5, .ctrset = CPUMF_CTRSET_BASIC, .name = "L1D_PENALTY_CYCLES", .desc = "Level-1 D-Cache Penalty Cycle Count", }, { .ctrnum = 32, .ctrset = CPUMF_CTRSET_PROBLEM_STATE, .name = "PROBLEM_STATE_CPU_CYCLES", .desc = "Problem-State Cycle Count", }, { .ctrnum = 33, .ctrset = CPUMF_CTRSET_PROBLEM_STATE, .name = "PROBLEM_STATE_INSTRUCTIONS", .desc = "Problem-State Instruction Count", }, }; static struct counters cpumcf_svn_12345_counters[] = { { .ctrnum = 64, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "PRNG_FUNCTIONS", .desc = "Total number of the PRNG functions issued by the" "\n\t\tCPU", }, { .ctrnum = 65, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "PRNG_CYCLES", .desc = "Total number of CPU cycles when the DEA/AES" "\n\t\tcoprocessor is busy performing PRNG functions" "\n\t\tissued by the CPU", }, { .ctrnum = 66, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "PRNG_BLOCKED_FUNCTIONS", .desc = "Total number of the PRNG functions that are issued" "\n\t\tby the CPU and are blocked because the DEA/AES" "\n\t\tcoprocessor is busy performing a function issued by" "\n\t\tanother CPU", }, { .ctrnum = 67, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "PRNG_BLOCKED_CYCLES", .desc = "Total number of CPU cycles blocked for the PRNG" "\n\t\tfunctions issued by the CPU because the DEA/AES" "\n\t\tcoprocessor is busy performing a function issued by" "\n\t\tanother CPU", }, { .ctrnum = 68, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "SHA_FUNCTIONS", .desc = "Total number of SHA functions issued by the CPU", }, { .ctrnum = 69, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "SHA_CYCLES", .desc = "Total number of CPU cycles when the SHA coprocessor" "\n\t\tis busy performing the SHA functions issued by the" "\n\t\tCPU", }, { .ctrnum = 70, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "SHA_BLOCKED_FUNCTIONS", .desc = "Total number of the SHA functions that are issued" "\n\t\tby the CPU and are blocked because the SHA" "\n\t\tcoprocessor is busy performing a function issued by" "\n\t\tanother CPU", }, { .ctrnum = 71, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "SHA_BLOCKED_CYCLES", .desc = "Total number of CPU cycles blocked for the SHA" "\n\t\tfunctions issued by the CPU because the SHA" "\n\t\tcoprocessor is busy performing a function issued by" "\n\t\tanother CPU", }, { .ctrnum = 72, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "DEA_FUNCTIONS", .desc = "Total number of the DEA functions issued by the CPU", }, { .ctrnum = 73, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "DEA_CYCLES", .desc = "Total number of CPU cycles when the DEA/AES" "\n\t\tcoprocessor is busy performing the DEA functions" "\n\t\tissued by the CPU", }, { .ctrnum = 74, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "DEA_BLOCKED_FUNCTIONS", .desc = "Total number of the DEA functions that are issued" "\n\t\tby the CPU and are blocked because the DEA/AES" "\n\t\tcoprocessor is busy performing a function issued by" "\n\t\tanother CPU", }, { .ctrnum = 75, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "DEA_BLOCKED_CYCLES", .desc = "Total number of CPU cycles blocked for the DEA" "\n\t\tfunctions issued by the CPU because the DEA/AES" "\n\t\tcoprocessor is busy performing a function issued by" "\n\t\tanother CPU", }, { .ctrnum = 76, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "AES_FUNCTIONS", .desc = "Total number of AES functions issued by the CPU", }, { .ctrnum = 77, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "AES_CYCLES", .desc = "Total number of CPU cycles when the DEA/AES" "\n\t\tcoprocessor is busy performing the AES functions" "\n\t\tissued by the CPU", }, { .ctrnum = 78, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "AES_BLOCKED_FUNCTIONS", .desc = "Total number of AES functions that are issued by" "\n\t\tthe CPU and are blocked because the DEA/AES" "\n\t\tcoprocessor is busy performing a function issued by" "\n\t\tanother CPU", }, { .ctrnum = 79, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "AES_BLOCKED_CYCLES", .desc = "Total number of CPU cycles blocked for the AES" "\n\t\tfunctions issued by the CPU because the DEA/AES" "\n\t\tcoprocessor is busy performing a function issued by" "\n\t\tanother CPU", }, }; static struct counters cpumcf_svn_6_counters[] = { { .ctrnum = 64, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "PRNG_FUNCTIONS", .desc = "Total number of the PRNG functions issued by the" "\n\t\tCPU", }, { .ctrnum = 65, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "PRNG_CYCLES", .desc = "Total number of CPU cycles when the DEA/AES" "\n\t\tcoprocessor is busy performing PRNG functions" "\n\t\tissued by the CPU", }, { .ctrnum = 66, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "PRNG_BLOCKED_FUNCTIONS", .desc = "Total number of the PRNG functions that are issued" "\n\t\tby the CPU and are blocked because the DEA/AES" "\n\t\tcoprocessor is busy performing a function issued by" "\n\t\tanother CPU", }, { .ctrnum = 67, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "PRNG_BLOCKED_CYCLES", .desc = "Total number of CPU cycles blocked for the PRNG" "\n\t\tfunctions issued by the CPU because the DEA/AES" "\n\t\tcoprocessor is busy performing a function issued by" "\n\t\tanother CPU", }, { .ctrnum = 68, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "SHA_FUNCTIONS", .desc = "Total number of SHA functions issued by the CPU", }, { .ctrnum = 69, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "SHA_CYCLES", .desc = "Total number of CPU cycles when the SHA coprocessor" "\n\t\tis busy performing the SHA functions issued by the" "\n\t\tCPU", }, { .ctrnum = 70, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "SHA_BLOCKED_FUNCTIONS", .desc = "Total number of the SHA functions that are issued" "\n\t\tby the CPU and are blocked because the SHA" "\n\t\tcoprocessor is busy performing a function issued by" "\n\t\tanother CPU", }, { .ctrnum = 71, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "SHA_BLOCKED_CYCLES", .desc = "Total number of CPU cycles blocked for the SHA" "\n\t\tfunctions issued by the CPU because the SHA" "\n\t\tcoprocessor is busy performing a function issued by" "\n\t\tanother CPU", }, { .ctrnum = 72, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "DEA_FUNCTIONS", .desc = "Total number of the DEA functions issued by the CPU", }, { .ctrnum = 73, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "DEA_CYCLES", .desc = "Total number of CPU cycles when the DEA/AES" "\n\t\tcoprocessor is busy performing the DEA functions" "\n\t\tissued by the CPU", }, { .ctrnum = 74, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "DEA_BLOCKED_FUNCTIONS", .desc = "Total number of the DEA functions that are issued" "\n\t\tby the CPU and are blocked because the DEA/AES" "\n\t\tcoprocessor is busy performing a function issued by" "\n\t\tanother CPU", }, { .ctrnum = 75, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "DEA_BLOCKED_CYCLES", .desc = "Total number of CPU cycles blocked for the DEA" "\n\t\tfunctions issued by the CPU because the DEA/AES" "\n\t\tcoprocessor is busy performing a function issued by" "\n\t\tanother CPU", }, { .ctrnum = 76, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "AES_FUNCTIONS", .desc = "Total number of AES functions issued by the CPU", }, { .ctrnum = 77, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "AES_CYCLES", .desc = "Total number of CPU cycles when the DEA/AES" "\n\t\tcoprocessor is busy performing the AES functions" "\n\t\tissued by the CPU", }, { .ctrnum = 78, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "AES_BLOCKED_FUNCTIONS", .desc = "Total number of AES functions that are issued by" "\n\t\tthe CPU and are blocked because the DEA/AES" "\n\t\tcoprocessor is busy performing a function issued by" "\n\t\tanother CPU", }, { .ctrnum = 79, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "AES_BLOCKED_CYCLES", .desc = "Total number of CPU cycles blocked for the AES" "\n\t\tfunctions issued by the CPU because the DEA/AES" "\n\t\tcoprocessor is busy performing a function issued by" "\n\t\tanother CPU", }, { .ctrnum = 80, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "ECC_FUNCTION_COUNT", .desc = "This counter counts the total number of the" "\n\t\telliptic-curve cryptography (ECC) functions issued" "\n\t\tby the CPU.", }, { .ctrnum = 81, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "ECC_CYCLES_COUNT", .desc = "This counter counts the total number of CPU cycles" "\n\t\twhen the ECC coprocessor is busy performing the" "\n\t\telliptic-curve cryptography (ECC) functions issued" "\n\t\tby the CPU.", }, { .ctrnum = 82, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "ECC_BLOCKED_FUNCTION_COUNT", .desc = "This counter counts the total number of the" "\n\t\telliptic-curve cryptography (ECC) functions that" "\n\t\tare issued by the CPU and are blocked because the" "\n\t\tECC coprocessor is busy performing a function" "\n\t\tissued by another CPU.", }, { .ctrnum = 83, .ctrset = CPUMF_CTRSET_CRYPTO, .name = "ECC_BLOCKED_CYCLES_COUNT", .desc = "This counter counts the total number of CPU cycles" "\n\t\tblocked for the elliptic-curve cryptography (ECC)" "\n\t\tfunctions issued by the CPU because the ECC" "\n\t\tcoprocessor is busy perform- ing a function issued" "\n\t\tby another CPU.", }, }; static struct counters cpumcf_z10_counters[] = { { .ctrnum = 128, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_L2_SOURCED_WRITES", .desc = "A directory write to the Level-1 I-Cache directory" "\n\t\twhere the returned cache line was sourced from the" "\n\t\tLevel-2 (L1.5) cache", }, { .ctrnum = 129, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_L2_SOURCED_WRITES", .desc = "A directory write to the Level-1 D-Cache directory" "\n\t\twhere the installed cache line was sourced from the" "\n\t\tLevel-2 (L1.5) cache", }, { .ctrnum = 130, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_L3_LOCAL_WRITES", .desc = "A directory write to the Level-1 I-Cache directory" "\n\t\twhere the installed cache line was sourced from the" "\n\t\tLevel-3 cache that is on the same book as the" "\n\t\tInstruction cache (Local L2 cache)", }, { .ctrnum = 131, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_L3_LOCAL_WRITES", .desc = "A directory write to the Level-1 D-Cache directory" "\n\t\twhere the installtion cache line was source from" "\n\t\tthe Level-3 cache that is on the same book as the" "\n\t\tData cache (Local L2 cache)", }, { .ctrnum = 132, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_L3_REMOTE_WRITES", .desc = "A directory write to the Level-1 I-Cache directory" "\n\t\twhere the installed cache line was sourced from a" "\n\t\tLevel-3 cache that is not on the same book as the" "\n\t\tInstruction cache (Remote L2 cache)", }, { .ctrnum = 133, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_L3_REMOTE_WRITES", .desc = "A directory write to the Level-1 D-Cache directory" "\n\t\twhere the installed cache line was sourced from a" "\n\t\tLevel-3 cache that is not on the same book as the" "\n\t\tData cache (Remote L2 cache)", }, { .ctrnum = 134, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_LMEM_SOURCED_WRITES", .desc = "A directory write to the Level-1 D-Cache directory" "\n\t\twhere the installed cache line was sourced from" "\n\t\tmemory that is attached to the same book as the" "\n\t\tData cache (Local Memory)", }, { .ctrnum = 135, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_LMEM_SOURCED_WRITES", .desc = "A directory write to the Level-1 I-Cache where the" "\n\t\tinstalled cache line was sourced from memory that" "\n\t\tis attached to the s ame book as the Instruction" "\n\t\tcache (Local Memory)", }, { .ctrnum = 136, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_RO_EXCL_WRITES", .desc = "A directory write to the Level-1 D-Cache where the" "\n\t\tline was originally in a Read-Only state in the" "\n\t\tcache but has been updated to be in the Exclusive" "\n\t\tstate that allows stores to the cache line", }, { .ctrnum = 137, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_CACHELINE_INVALIDATES", .desc = "A cache line in the Level-1 I-Cache has been" "\n\t\tinvalidated by a store on the same CPU as the Level-" "\n\t\t1 I-Cache", }, { .ctrnum = 138, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ITLB1_WRITES", .desc = "A translation entry has been written into the Level-" "\n\t\t1 Instruction Translation Lookaside Buffer", }, { .ctrnum = 139, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DTLB1_WRITES", .desc = "A translation entry has been written to the Level-1" "\n\t\tData Translation Lookaside Buffer", }, { .ctrnum = 140, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TLB2_PTE_WRITES", .desc = "A translation entry has been written to the Level-2" "\n\t\tTLB Page Table Entry arrays", }, { .ctrnum = 141, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TLB2_CRSTE_WRITES", .desc = "A translation entry has been written to the Level-2" "\n\t\tTLB Common Region Segment Table Entry arrays", }, { .ctrnum = 142, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TLB2_CRSTE_HPAGE_WRITES", .desc = "A translation entry has been written to the Level-2" "\n\t\tTLB Common Region Segment Table Entry arrays for a" "\n\t\tone-megabyte large page translation", }, { .ctrnum = 145, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ITLB1_MISSES", .desc = "Level-1 Instruction TLB miss in progress." "\n\t\tIncremented by one for every cycle an ITLB1 miss is" "\n\t\tin progress", }, { .ctrnum = 146, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DTLB1_MISSES", .desc = "Level-1 Data TLB miss in progress. Incremented by" "\n\t\tone for every cycle an DTLB1 miss is in progress", }, { .ctrnum = 147, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L2C_STORES_SENT", .desc = "Incremented by one for every store sent to Level-2" "\n\t\t(L1.5) cache", }, }; static struct counters cpumcf_z196_counters[] = { { .ctrnum = 128, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_L2_SOURCED_WRITES", .desc = "A directory write to the Level-1 D-Cache directory" "\n\t\twhere the returned cache line was sourced from the" "\n\t\tLevel-2 cache", }, { .ctrnum = 129, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_L2_SOURCED_WRITES", .desc = "A directory write to the Level-1 I-Cache directory" "\n\t\twhere the returned cache line was sourced from the" "\n\t\tLevel-2 cache", }, { .ctrnum = 130, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DTLB1_MISSES", .desc = "Level-1 Data TLB miss in progress. Incremented by" "\n\t\tone for every cycle a DTLB1 miss is in progress.", }, { .ctrnum = 131, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ITLB1_MISSES", .desc = "Level-1 Instruction TLB miss in progress." "\n\t\tIncremented by one for every cycle a ITLB1 miss is" "\n\t\tin progress.", }, { .ctrnum = 133, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L2C_STORES_SENT", .desc = "Incremented by one for every store sent to Level-2" "\n\t\tcache", }, { .ctrnum = 134, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_OFFBOOK_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 D-Cache directory" "\n\t\twhere the returned cache line was sourced from an" "\n\t\tOff Book Level-3 cache", }, { .ctrnum = 135, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_ONBOOK_L4_SOURCED_WRITES", .desc = "A directory write to the Level-1 D-Cache directory" "\n\t\twhere the returned cache line was sourced from an" "\n\t\tOn Book Level-4 cache", }, { .ctrnum = 136, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_ONBOOK_L4_SOURCED_WRITES", .desc = "A directory write to the Level-1 I-Cache directory" "\n\t\twhere the returned cache line was sourced from an" "\n\t\tOn Book Level-4 cache", }, { .ctrnum = 137, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_RO_EXCL_WRITES", .desc = "A directory write to the Level-1 D-Cache where the" "\n\t\tline was originally in a Read-Only state in the" "\n\t\tcache but has been updated to be in the Exclusive" "\n\t\tstate that allows stores to the cache line", }, { .ctrnum = 138, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_OFFBOOK_L4_SOURCED_WRITES", .desc = "A directory write to the Level-1 D-Cache directory" "\n\t\twhere the returned cache line was sourced from an" "\n\t\tOff Book Level-4 cache", }, { .ctrnum = 139, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_OFFBOOK_L4_SOURCED_WRITES", .desc = "A directory write to the Level-1 I-Cache directory" "\n\t\twhere the returned cache line was sourced from an" "\n\t\tOff Book Level-4 cache", }, { .ctrnum = 140, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DTLB1_HPAGE_WRITES", .desc = "A translation entry has been written to the Level-1" "\n\t\tData Translation Lookaside Buffer for a one-" "\n\t\tmegabyte page", }, { .ctrnum = 141, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_LMEM_SOURCED_WRITES", .desc = "A directory write to the Level-1 D-Cache where the" "\n\t\tinstalled cache line was sourced from memory that" "\n\t\tis attached to the same book as the Data cache" "\n\t\t(Local Memory)", }, { .ctrnum = 142, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_LMEM_SOURCED_WRITES", .desc = "A directory write to the Level-1 I-Cache where the" "\n\t\tinstalled cache line was sourced from memory that" "\n\t\tis attached to the same book as the Instruction" "\n\t\tcache (Local Memory)", }, { .ctrnum = 143, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_OFFBOOK_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 I-Cache directory" "\n\t\twhere the returned cache line was sourced from an" "\n\t\tOff Book Level-3 cache", }, { .ctrnum = 144, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DTLB1_WRITES", .desc = "A translation entry has been written to the Level-1" "\n\t\tData Translation Lookaside Buffer", }, { .ctrnum = 145, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ITLB1_WRITES", .desc = "A translation entry has been written to the Level-1" "\n\t\tInstruction Translation Lookaside Buffer", }, { .ctrnum = 146, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TLB2_PTE_WRITES", .desc = "A translation entry has been written to the Level-2" "\n\t\tTLB Page Table Entry arrays", }, { .ctrnum = 147, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TLB2_CRSTE_HPAGE_WRITES", .desc = "A translation entry has been written to the Level-2" "\n\t\tTLB Common Region Segment Table Entry arrays for a" "\n\t\tone-megabyte large page translation", }, { .ctrnum = 148, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TLB2_CRSTE_WRITES", .desc = "A translation entry has been written to the Level-2" "\n\t\tTLB Common Region Segment Table Entry arrays", }, { .ctrnum = 150, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_ONCHIP_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 D-Cache directory" "\n\t\twhere the returned cache line was sourced from an" "\n\t\tOn Chip Level-3 cache", }, { .ctrnum = 152, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_OFFCHIP_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 D-Cache directory" "\n\t\twhere the returned cache line was sourced from an" "\n\t\tOff Chip/On Book Level-3 cache", }, { .ctrnum = 153, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_ONCHIP_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 I-Cache directory" "\n\t\twhere the returned cache line was sourced from an" "\n\t\tOn Chip Level-3 cache", }, { .ctrnum = 155, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_OFFCHIP_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 I-Cache directory" "\n\t\twhere the returned cache line was sourced from an" "\n\t\tOff Chip/On Book Level-3 cache", }, }; static struct counters cpumcf_zec12_counters[] = { { .ctrnum = 128, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DTLB1_MISSES", .desc = "Level-1 Data TLB miss in progress. Incremented by" "\n\t\tone for every cycle a DTLB1 miss is in progress.", }, { .ctrnum = 129, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ITLB1_MISSES", .desc = "Level-1 Instruction TLB miss in progress." "\n\t\tIncremented by one for every cycle a ITLB1 miss is" "\n\t\tin progress.", }, { .ctrnum = 130, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_L2I_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom the Level-2 Instruction cache", }, { .ctrnum = 131, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_L2I_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom the Level-2 Instruction cache", }, { .ctrnum = 132, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_L2D_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom the Level-2 Data cache", }, { .ctrnum = 133, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DTLB1_WRITES", .desc = "A translation entry has been written to the Level-1" "\n\t\tData Translation Lookaside Buffer", }, { .ctrnum = 135, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_LMEM_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache where" "\n\t\tthe installed cache line was sourced from memory" "\n\t\tthat is attached to the same book as the Data cache" "\n\t\t(Local Memory)", }, { .ctrnum = 137, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_LMEM_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\twhere the installed cache line was sourced from" "\n\t\tmemory that is attached to the same book as the" "\n\t\tInstruction cache (Local Memory)", }, { .ctrnum = 138, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_RO_EXCL_WRITES", .desc = "A directory write to the Level-1 D-Cache where the" "\n\t\tline was originally in a Read-Only state in the" "\n\t\tcache but has been updated to be in the Exclusive" "\n\t\tstate that allows stores to the cache line", }, { .ctrnum = 139, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DTLB1_HPAGE_WRITES", .desc = "A translation entry has been written to the Level-1" "\n\t\tData Translation Lookaside Buffer for a one-" "\n\t\tmegabyte page", }, { .ctrnum = 140, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ITLB1_WRITES", .desc = "A translation entry has been written to the Level-1" "\n\t\tInstruction Translation Lookaside Buffer", }, { .ctrnum = 141, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TLB2_PTE_WRITES", .desc = "A translation entry has been written to the Level-2" "\n\t\tTLB Page Table Entry arrays", }, { .ctrnum = 142, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TLB2_CRSTE_HPAGE_WRITES", .desc = "A translation entry has been written to the Level-2" "\n\t\tTLB Common Region Segment Table Entry arrays for a" "\n\t\tone-megabyte large page translation", }, { .ctrnum = 143, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TLB2_CRSTE_WRITES", .desc = "A translation entry has been written to the Level-2" "\n\t\tTLB Common Region Segment Table Entry arrays", }, { .ctrnum = 144, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_ONCHIP_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On Chip Level-3 cache without intervention", }, { .ctrnum = 145, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_OFFCHIP_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off Chip/On Book Level-3 cache without" "\n\t\tintervention", }, { .ctrnum = 146, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_OFFBOOK_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off Book Level-3 cache without intervention", }, { .ctrnum = 147, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_ONBOOK_L4_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On Book Level-4 cache", }, { .ctrnum = 148, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_OFFBOOK_L4_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off Book Level-4 cache", }, { .ctrnum = 149, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TX_NC_TEND", .desc = "A TEND instruction has completed in a" "\n\t\tnonconstrained transactional-execution mode", }, { .ctrnum = 150, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_ONCHIP_L3_SOURCED_WRITES_IV", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom a On Chip Level-3 cache with intervention", }, { .ctrnum = 151, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_OFFCHIP_L3_SOURCED_WRITES_IV", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off Chip/On Book Level-3 cache with" "\n\t\tintervention", }, { .ctrnum = 152, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_OFFBOOK_L3_SOURCED_WRITES_IV", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off Book Level-3 cache with intervention", }, { .ctrnum = 153, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_ONCHIP_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On Chip Level-3 cache without intervention", }, { .ctrnum = 154, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_OFFCHIP_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off Chip/On Book Level-3 cache without" "\n\t\tintervention", }, { .ctrnum = 155, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_OFFBOOK_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off Book Level-3 cache without intervention", }, { .ctrnum = 156, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_ONBOOK_L4_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On Book Level-4 cache", }, { .ctrnum = 157, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_OFFBOOK_L4_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off Book Level-4 cache", }, { .ctrnum = 158, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TX_C_TEND", .desc = "A TEND instruction has completed in a constrained" "\n\t\ttransactional-execution mode", }, { .ctrnum = 159, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_ONCHIP_L3_SOURCED_WRITES_IV", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On Chip Level-3 cache with intervention", }, { .ctrnum = 160, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_OFFCHIP_L3_SOURCED_WRITES_IV", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off Chip/On Book Level-3 cache with" "\n\t\tintervention", }, { .ctrnum = 161, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_OFFBOOK_L3_SOURCED_WRITES_IV", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off Book Level-3 cache with intervention", }, { .ctrnum = 177, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TX_NC_TABORT", .desc = "A transaction abort has occurred in a" "\n\t\tnonconstrained transactional-execution mode", }, { .ctrnum = 178, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TX_C_TABORT_NO_SPECIAL", .desc = "A transaction abort has occurred in a constrained" "\n\t\ttransactional-execution mode and the CPU is not" "\n\t\tusing any special logic to allow the transaction to" "\n\t\tcomplete", }, { .ctrnum = 179, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TX_C_TABORT_SPECIAL", .desc = "A transaction abort has occurred in a constrained" "\n\t\ttransactional-execution mode and the CPU is using" "\n\t\tspecial logic to allow the transaction to complete", }, }; static struct counters cpumcf_z13_counters[] = { { .ctrnum = 128, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_RO_EXCL_WRITES", .desc = "A directory write to the Level-1 Data cache where" "\n\t\tthe line was originally in a Read-Only state in the" "\n\t\tcache but has been updated to be in the Exclusive" "\n\t\tstate that allows stores to the cache line.", }, { .ctrnum = 129, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DTLB1_WRITES", .desc = "A translation entry has been written to the Level-1" "\n\t\tData Translation Lookaside Buffer", }, { .ctrnum = 130, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DTLB1_MISSES", .desc = "Level-1 Data TLB miss in progress. Incremented by" "\n\t\tone for every cycle a DTLB1 miss is in progress.", }, { .ctrnum = 131, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DTLB1_HPAGE_WRITES", .desc = "A translation entry has been written to the Level-1" "\n\t\tData Translation Lookaside Buffer for a one-" "\n\t\tmegabyte page", }, { .ctrnum = 132, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DTLB1_GPAGE_WRITES", .desc = "A translation entry has been written to the Level-1" "\n\t\tData Translation Lookaside Buffer for a two-" "\n\t\tgigabyte page.", }, { .ctrnum = 133, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_L2D_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom the Level-2 Data cache", }, { .ctrnum = 134, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ITLB1_WRITES", .desc = "A translation entry has been written to the Level-1" "\n\t\tInstruction Translation Lookaside Buffer", }, { .ctrnum = 135, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ITLB1_MISSES", .desc = "Level-1 Instruction TLB miss in progress." "\n\t\tIncremented by one for every cycle an ITLB1 miss is" "\n\t\tin progress", }, { .ctrnum = 136, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_L2I_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom the Level-2 Instruction cache", }, { .ctrnum = 137, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TLB2_PTE_WRITES", .desc = "A translation entry has been written to the Level-2" "\n\t\tTLB Page Table Entry arrays", }, { .ctrnum = 138, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TLB2_CRSTE_HPAGE_WRITES", .desc = "A translation entry has been written to the Level-2" "\n\t\tTLB Combined Region Segment Table Entry arrays for" "\n\t\ta one-megabyte large page translation", }, { .ctrnum = 139, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TLB2_CRSTE_WRITES", .desc = "A translation entry has been written to the Level-2" "\n\t\tTLB Combined Region Segment Table Entry arrays", }, { .ctrnum = 140, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TX_C_TEND", .desc = "A TEND instruction has completed in a constrained" "\n\t\ttransactional-execution mode", }, { .ctrnum = 141, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TX_NC_TEND", .desc = "A TEND instruction has completed in a non-" "\n\t\tconstrained transactional-execution mode", }, { .ctrnum = 143, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1C_TLB1_MISSES", .desc = "Increments by one for any cycle where a Level-1" "\n\t\tcache or Level-1 TLB miss is in progress.", }, { .ctrnum = 144, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_ONCHIP_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Chip Level-3 cache without intervention", }, { .ctrnum = 145, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_ONCHIP_L3_SOURCED_WRITES_IV", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Chip Level-3 cache with intervention", }, { .ctrnum = 146, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_ONNODE_L4_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Node Level-4 cache", }, { .ctrnum = 147, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_ONNODE_L3_SOURCED_WRITES_IV", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Node Level-3 cache with intervention", }, { .ctrnum = 148, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_ONNODE_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Node Level-3 cache without intervention", }, { .ctrnum = 149, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_ONDRAWER_L4_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Drawer Level-4 cache", }, { .ctrnum = 150, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_ONDRAWER_L3_SOURCED_WRITES_IV", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Drawer Level-3 cache with intervention", }, { .ctrnum = 151, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_ONDRAWER_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Drawer Level-3 cache without" "\n\t\tintervention", }, { .ctrnum = 152, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_OFFDRAWER_SCOL_L4_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off-Drawer Same-Column Level-4 cache", }, { .ctrnum = 153, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_OFFDRAWER_SCOL_L3_SOURCED_WRITES_IV", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off-Drawer Same-Column Level-3 cache with" "\n\t\tintervention", }, { .ctrnum = 154, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_OFFDRAWER_SCOL_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off-Drawer Same-Column Level-3 cache" "\n\t\twithout intervention", }, { .ctrnum = 155, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_OFFDRAWER_FCOL_L4_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off-Drawer Far-Column Level-4 cache", }, { .ctrnum = 156, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_OFFDRAWER_FCOL_L3_SOURCED_WRITES_IV", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off-Drawer Far-Column Level-3 cache with" "\n\t\tintervention", }, { .ctrnum = 157, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_OFFDRAWER_FCOL_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off-Drawer Far-Column Level-3 cache without" "\n\t\tintervention", }, { .ctrnum = 158, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_ONNODE_MEM_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom On-Node memory", }, { .ctrnum = 159, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_ONDRAWER_MEM_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom On-Drawer memory", }, { .ctrnum = 160, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_OFFDRAWER_MEM_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom On-Drawer memory", }, { .ctrnum = 161, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_ONCHIP_MEM_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom On-Chip memory", }, { .ctrnum = 162, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_ONCHIP_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Chip Level-3 cache without intervention", }, { .ctrnum = 163, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_ONCHIP_L3_SOURCED_WRITES_IV", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On Chip Level-3 cache with intervention", }, { .ctrnum = 164, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_ONNODE_L4_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Node Level-4 cache", }, { .ctrnum = 165, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_ONNODE_L3_SOURCED_WRITES_IV", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Node Level-3 cache with intervention", }, { .ctrnum = 166, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_ONNODE_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Node Level-3 cache without intervention", }, { .ctrnum = 167, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_ONDRAWER_L4_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Drawer Level-4 cache", }, { .ctrnum = 168, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_ONDRAWER_L3_SOURCED_WRITES_IV", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Drawer Level-3 cache with intervention", }, { .ctrnum = 169, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_ONDRAWER_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Drawer Level-3 cache without" "\n\t\tintervention", }, { .ctrnum = 170, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_OFFDRAWER_SCOL_L4_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off-Drawer Same-Column Level-4 cache", }, { .ctrnum = 171, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_OFFDRAWER_SCOL_L3_SOURCED_WRITES_IV", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off-Drawer Same-Column Level-3 cache with" "\n\t\tintervention", }, { .ctrnum = 172, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_OFFDRAWER_SCOL_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off-Drawer Same-Column Level-3 cache" "\n\t\twithout intervention", }, { .ctrnum = 173, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_OFFDRAWER_FCOL_L4_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off-Drawer Far-Column Level-4 cache", }, { .ctrnum = 174, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_OFFDRAWER_FCOL_L3_SOURCED_WRITES_IV", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off-Drawer Far-Column Level-3 cache with" "\n\t\tintervention", }, { .ctrnum = 175, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_OFFDRAWER_FCOL_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off-Drawer Far-Column Level-3 cache without" "\n\t\tintervention", }, { .ctrnum = 176, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_ONNODE_MEM_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom On-Node memory", }, { .ctrnum = 177, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_ONDRAWER_MEM_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom On-Drawer memory", }, { .ctrnum = 178, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_OFFDRAWER_MEM_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom On-Drawer memory", }, { .ctrnum = 179, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_ONCHIP_MEM_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom On-Chip memory", }, { .ctrnum = 218, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TX_NC_TABORT", .desc = "A transaction abort has occurred in a non-" "\n\t\tconstrained transactional-execution mode", }, { .ctrnum = 219, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TX_C_TABORT_NO_SPECIAL", .desc = "A transaction abort has occurred in a constrained" "\n\t\ttransactional-execution mode and the CPU is not" "\n\t\tusing any special logic to allow the transaction to" "\n\t\tcomplete", }, { .ctrnum = 220, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TX_C_TABORT_SPECIAL", .desc = "A transaction abort has occurred in a constrained" "\n\t\ttransactional-execution mode and the CPU is using" "\n\t\tspecial logic to allow the transaction to complete", }, { .ctrnum = 448, .ctrset = CPUMF_CTRSET_MT_DIAG, .name = "MT_DIAG_CYCLES_ONE_THR_ACTIVE", .desc = "Cycle count with one thread active", }, { .ctrnum = 449, .ctrset = CPUMF_CTRSET_MT_DIAG, .name = "MT_DIAG_CYCLES_TWO_THR_ACTIVE", .desc = "Cycle count with two threads active", }, }; static struct counters cpumcf_z14_counters[] = { { .ctrnum = 128, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_RO_EXCL_WRITES", .desc = "A directory write to the Level-1 Data cache where" "\n\t\tthe line was originally in a Read-Only state in the" "\n\t\tcache but has been updated to be in the Exclusive" "\n\t\tstate that allows stores to the cache line", }, { .ctrnum = 129, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DTLB2_WRITES", .desc = "A translation has been written into The Translation" "\n\t\tLookaside Buffer 2 (TLB2) and the request was made" "\n\t\tby the data cache", }, { .ctrnum = 130, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DTLB2_MISSES", .desc = "A TLB2 miss is in progress for a request made by" "\n\t\tthe data cache. Incremented by one for every TLB2" "\n\t\tmiss in progress for the Level-1 Data cache on this" "\n\t\tcycle", }, { .ctrnum = 131, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DTLB2_HPAGE_WRITES", .desc = "A translation entry was written into the Combined" "\n\t\tRegion and Segment Table Entry array in the Level-2" "\n\t\tTLB for a one-megabyte page or a Last Host" "\n\t\tTranslation was done", }, { .ctrnum = 132, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DTLB2_GPAGE_WRITES", .desc = "A translation entry for a two-gigabyte page was" "\n\t\twritten into the Level-2 TLB", }, { .ctrnum = 133, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_L2D_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom the Level-2 Data cache", }, { .ctrnum = 134, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ITLB2_WRITES", .desc = "A translation entry has been written into the" "\n\t\tTranslation Lookaside Buffer 2 (TLB2) and the" "\n\t\trequest was made by the instruction cache", }, { .ctrnum = 135, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ITLB2_MISSES", .desc = "A TLB2 miss is in progress for a request made by" "\n\t\tthe instruction cache. Incremented by one for every" "\n\t\tTLB2 miss in progress for the Level-1 Instruction" "\n\t\tcache in a cycle", }, { .ctrnum = 136, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_L2I_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom the Level-2 Instruction cache", }, { .ctrnum = 137, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TLB2_PTE_WRITES", .desc = "A translation entry was written into the Page Table" "\n\t\tEntry array in the Level-2 TLB", }, { .ctrnum = 138, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TLB2_CRSTE_WRITES", .desc = "Translation entries were written into the Combined" "\n\t\tRegion and Segment Table Entry array and the Page" "\n\t\tTable Entry array in the Level-2 TLB", }, { .ctrnum = 139, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TLB2_ENGINES_BUSY", .desc = "The number of Level-2 TLB translation engines busy" "\n\t\tin a cycle", }, { .ctrnum = 140, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TX_C_TEND", .desc = "A TEND instruction has completed in a constrained" "\n\t\ttransactional-execution mode", }, { .ctrnum = 141, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TX_NC_TEND", .desc = "A TEND instruction has completed in a non-" "\n\t\tconstrained transactional-execution mode", }, { .ctrnum = 143, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1C_TLB2_MISSES", .desc = "Increments by one for any cycle where a level-1" "\n\t\tcache or level-2 TLB miss is in progress", }, { .ctrnum = 144, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_ONCHIP_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Chip Level-3 cache without intervention", }, { .ctrnum = 145, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_ONCHIP_MEMORY_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom On-Chip memory", }, { .ctrnum = 146, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_ONCHIP_L3_SOURCED_WRITES_IV", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Chip Level-3 cache with intervention", }, { .ctrnum = 147, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_ONCLUSTER_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom On-Cluster Level-3 cache withountervention", }, { .ctrnum = 148, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_ONCLUSTER_MEMORY_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Cluster memory", }, { .ctrnum = 149, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_ONCLUSTER_L3_SOURCED_WRITES_IV", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Cluster Level-3 cache with intervention", }, { .ctrnum = 150, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_OFFCLUSTER_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off-Cluster Level-3 cache without" "\n\t\tintervention", }, { .ctrnum = 151, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_OFFCLUSTER_MEMORY_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom Off-Cluster memory", }, { .ctrnum = 152, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_OFFCLUSTER_L3_SOURCED_WRITES_IV", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off-Cluster Level-3 cache with intervention", }, { .ctrnum = 153, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_OFFDRAWER_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off-Drawer Level-3 cache without" "\n\t\tintervention", }, { .ctrnum = 154, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_OFFDRAWER_MEMORY_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom Off-Drawer memory", }, { .ctrnum = 155, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_OFFDRAWER_L3_SOURCED_WRITES_IV", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off-Drawer Level-3 cache with intervention", }, { .ctrnum = 156, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_ONDRAWER_L4_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom On-Drawer Level-4 cache", }, { .ctrnum = 157, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_OFFDRAWER_L4_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom Off-Drawer Level-4 cache", }, { .ctrnum = 158, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_ONCHIP_L3_SOURCED_WRITES_RO", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom On-Chip L3 but a read-only invalidate was done" "\n\t\tto remove other copies of the cache line", }, { .ctrnum = 162, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_ONCHIP_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache ine was sourced" "\n\t\tfrom an On-Chip Level-3 cache without intervention", }, { .ctrnum = 163, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_ONCHIP_MEMORY_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache ine was sourced" "\n\t\tfrom On-Chip memory", }, { .ctrnum = 164, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_ONCHIP_L3_SOURCED_WRITES_IV", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache ine was sourced" "\n\t\tfrom an On-Chip Level-3 cache with intervention", }, { .ctrnum = 165, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_ONCLUSTER_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Cluster Level-3 cache without" "\n\t\tintervention", }, { .ctrnum = 166, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_ONCLUSTER_MEMORY_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Cluster memory", }, { .ctrnum = 167, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_ONCLUSTER_L3_SOURCED_WRITES_IV", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom On-Cluster Level-3 cache with intervention", }, { .ctrnum = 168, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_OFFCLUSTER_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off-Cluster Level-3 cache without" "\n\t\tintervention", }, { .ctrnum = 169, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_OFFCLUSTER_MEMORY_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom Off-Cluster memory", }, { .ctrnum = 170, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_OFFCLUSTER_L3_SOURCED_WRITES_IV", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off-Cluster Level-3 cache with intervention", }, { .ctrnum = 171, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_OFFDRAWER_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off-Drawer Level-3 cache without" "\n\t\tintervention", }, { .ctrnum = 172, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_OFFDRAWER_MEMORY_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom Off-Drawer memory", }, { .ctrnum = 173, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_OFFDRAWER_L3_SOURCED_WRITES_IV", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off-Drawer Level-3 cache with intervention", }, { .ctrnum = 174, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_ONDRAWER_L4_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom On-Drawer Level-4 cache", }, { .ctrnum = 175, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_OFFDRAWER_L4_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom Off-Drawer Level-4 cache", }, { .ctrnum = 224, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "BCD_DFP_EXECUTION_SLOTS", .desc = "Count of floating point execution slots used for" "\n\t\tfinished Binary Coded Decimal to Decimal Floating" "\n\t\tPoint conversions. Instructions: CDZT, CXZT, CZDT," "\n\t\tCZXT", }, { .ctrnum = 225, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "VX_BCD_EXECUTION_SLOTS", .desc = "Count of floating point execution slots used for" "\n\t\tfinished vector arithmetic Binary Coded Decimal" "\n\t\tinstructions. Instructions: VAP, VSP, VMPVMSP, VDP," "\n\t\tVSDP, VRP, VLIP, VSRP, VPSOPVCP, VTP, VPKZ, VUPKZ," "\n\t\tVCVB, VCVBG, VCVDVCVDG", }, { .ctrnum = 226, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DECIMAL_INSTRUCTIONS", .desc = "Decimal instructions dispatched. Instructions: CVB," "\n\t\tCVD, AP, CP, DP, ED, EDMK, MP, SRP, SP, ZAP", }, { .ctrnum = 232, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "LAST_HOST_TRANSLATIONS", .desc = "Last Host Translation done", }, { .ctrnum = 243, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TX_NC_TABORT", .desc = "A transaction abort has occurred in a non-" "\n\t\tconstrained transactional-execution mode", }, { .ctrnum = 244, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TX_C_TABORT_NO_SPECIAL", .desc = "A transaction abort has occurred in a constrained" "\n\t\ttransactional-execution mode and the CPU is not" "\n\t\tusing any special logic to allow the transaction to" "\n\t\tcomplete", }, { .ctrnum = 245, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TX_C_TABORT_SPECIAL", .desc = "A transaction abort has occurred in a constrained" "\n\t\ttransactional-execution mode and the CPU is using" "\n\t\tspecial logic to allow the transaction to complete", }, { .ctrnum = 448, .ctrset = CPUMF_CTRSET_MT_DIAG, .name = "MT_DIAG_CYCLES_ONE_THR_ACTIVE", .desc = "Cycle count with one thread active", }, { .ctrnum = 449, .ctrset = CPUMF_CTRSET_MT_DIAG, .name = "MT_DIAG_CYCLES_TWO_THR_ACTIVE", .desc = "Cycle count with two threads active", }, }; static struct counters cpumcf_z15_counters[] = { { .ctrnum = 128, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_RO_EXCL_WRITES", .desc = "A directory write to the Level-1 Data cache where" "\n\t\tthe line was originally in a Read-Only state in the" "\n\t\tcache but has been updated to be in the Exclusive" "\n\t\tstate that allows stores to the cache line", }, { .ctrnum = 129, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DTLB2_WRITES", .desc = "A translation has been written into The Translation" "\n\t\tLookaside Buffer 2 (TLB2) and the request was made" "\n\t\tby the data cache", }, { .ctrnum = 130, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DTLB2_MISSES", .desc = "A TLB2 miss is in progress for a request made by" "\n\t\tthe data cache. Incremented by one for every TLB2" "\n\t\tmiss in progress for the Level-1 Data cache on this" "\n\t\tcycle", }, { .ctrnum = 131, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DTLB2_HPAGE_WRITES", .desc = "A translation entry was written into the Combined" "\n\t\tRegion and Segment Table Entry array in the Level-2" "\n\t\tTLB for a one-megabyte page", }, { .ctrnum = 132, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DTLB2_GPAGE_WRITES", .desc = "A translation entry for a two-gigabyte page was" "\n\t\twritten into the Level-2 TLB", }, { .ctrnum = 133, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_L2D_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom the Level-2 Data cache", }, { .ctrnum = 134, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ITLB2_WRITES", .desc = "A translation entry has been written into the" "\n\t\tTranslation Lookaside Buffer 2 (TLB2) and the" "\n\t\trequest was made by the instruction cache", }, { .ctrnum = 135, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ITLB2_MISSES", .desc = "A TLB2 miss is in progress for a request made by" "\n\t\tthe instruction cache. Incremented by one for every" "\n\t\tTLB2 miss in progress for the Level-1 Instruction" "\n\t\tcache in a cycle", }, { .ctrnum = 136, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_L2I_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom the Level-2 Instruction cache", }, { .ctrnum = 137, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TLB2_PTE_WRITES", .desc = "A translation entry was written into the Page Table" "\n\t\tEntry array in the Level-2 TLB", }, { .ctrnum = 138, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TLB2_CRSTE_WRITES", .desc = "Translation entries were written into the Combined" "\n\t\tRegion and Segment Table Entry array and the Page" "\n\t\tTable Entry array in the Level-2 TLB", }, { .ctrnum = 139, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TLB2_ENGINES_BUSY", .desc = "The number of Level-2 TLB translation engines busy" "\n\t\tin a cycle", }, { .ctrnum = 140, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TX_C_TEND", .desc = "A TEND instruction has completed in a constrained" "\n\t\ttransactional-execution mode", }, { .ctrnum = 141, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TX_NC_TEND", .desc = "A TEND instruction has completed in a non-" "\n\t\tconstrained transactional-execution mode", }, { .ctrnum = 143, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1C_TLB2_MISSES", .desc = "Increments by one for any cycle where a level-1" "\n\t\tcache or level-2 TLB miss is in progress", }, { .ctrnum = 144, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_ONCHIP_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Chip Level-3 cache without intervention", }, { .ctrnum = 145, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_ONCHIP_MEMORY_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom On-Chip memory", }, { .ctrnum = 146, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_ONCHIP_L3_SOURCED_WRITES_IV", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Chip Level-3 cache with intervention", }, { .ctrnum = 147, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_ONCLUSTER_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom On-Cluster Level-3 cache withountervention", }, { .ctrnum = 148, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_ONCLUSTER_MEMORY_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Cluster memory", }, { .ctrnum = 149, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_ONCLUSTER_L3_SOURCED_WRITES_IV", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Cluster Level-3 cache with intervention", }, { .ctrnum = 150, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_OFFCLUSTER_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off-Cluster Level-3 cache without" "\n\t\tintervention", }, { .ctrnum = 151, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_OFFCLUSTER_MEMORY_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom Off-Cluster memory", }, { .ctrnum = 152, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_OFFCLUSTER_L3_SOURCED_WRITES_IV", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off-Cluster Level-3 cache with intervention", }, { .ctrnum = 153, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_OFFDRAWER_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off-Drawer Level-3 cache without" "\n\t\tintervention", }, { .ctrnum = 154, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_OFFDRAWER_MEMORY_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom Off-Drawer memory", }, { .ctrnum = 155, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_OFFDRAWER_L3_SOURCED_WRITES_IV", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off-Drawer Level-3 cache with intervention", }, { .ctrnum = 156, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_ONDRAWER_L4_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom On-Drawer Level-4 cache", }, { .ctrnum = 157, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_OFFDRAWER_L4_SOURCED_WRITES", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom Off-Drawer Level-4 cache", }, { .ctrnum = 158, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_ONCHIP_L3_SOURCED_WRITES_RO", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom On-Chip L3 but a read-only invalidate was done" "\n\t\tto remove other copies of the cache line", }, { .ctrnum = 162, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_ONCHIP_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache ine was sourced" "\n\t\tfrom an On-Chip Level-3 cache without intervention", }, { .ctrnum = 163, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_ONCHIP_MEMORY_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache ine was sourced" "\n\t\tfrom On-Chip memory", }, { .ctrnum = 164, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_ONCHIP_L3_SOURCED_WRITES_IV", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache ine was sourced" "\n\t\tfrom an On-Chip Level-3 cache with intervention", }, { .ctrnum = 165, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_ONCLUSTER_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Cluster Level-3 cache without" "\n\t\tintervention", }, { .ctrnum = 166, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_ONCLUSTER_MEMORY_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Cluster memory", }, { .ctrnum = 167, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_ONCLUSTER_L3_SOURCED_WRITES_IV", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom On-Cluster Level-3 cache with intervention", }, { .ctrnum = 168, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_OFFCLUSTER_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off-Cluster Level-3 cache without" "\n\t\tintervention", }, { .ctrnum = 169, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_OFFCLUSTER_MEMORY_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom Off-Cluster memory", }, { .ctrnum = 170, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_OFFCLUSTER_L3_SOURCED_WRITES_IV", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off-Cluster Level-3 cache with intervention", }, { .ctrnum = 171, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_OFFDRAWER_L3_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off-Drawer Level-3 cache without" "\n\t\tintervention", }, { .ctrnum = 172, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_OFFDRAWER_MEMORY_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom Off-Drawer memory", }, { .ctrnum = 173, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_OFFDRAWER_L3_SOURCED_WRITES_IV", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off-Drawer Level-3 cache with intervention", }, { .ctrnum = 174, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_ONDRAWER_L4_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom On-Drawer Level-4 cache", }, { .ctrnum = 175, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1I_OFFDRAWER_L4_SOURCED_WRITES", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom Off-Drawer Level-4 cache", }, { .ctrnum = 224, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "BCD_DFP_EXECUTION_SLOTS", .desc = "Count of floating point execution slots used for" "\n\t\tfinished Binary Coded Decimal to Decimal Floating" "\n\t\tPoint conversions. Instructions: CDZT, CXZT, CZDT," "\n\t\tCZXT", }, { .ctrnum = 225, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "VX_BCD_EXECUTION_SLOTS", .desc = "Count of floating point execution slots used for" "\n\t\tfinished vector arithmetic Binary Coded Decimal" "\n\t\tinstructions. Instructions: VAP, VSP, VMPVMSP, VDP," "\n\t\tVSDP, VRP, VLIP, VSRP, VPSOPVCP, VTP, VPKZ, VUPKZ," "\n\t\tVCVB, VCVBG, VCVDVCVDG", }, { .ctrnum = 226, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DECIMAL_INSTRUCTIONS", .desc = "Decimal instructions dispatched. Instructions: CVB," "\n\t\tCVD, AP, CP, DP, ED, EDMK, MP, SRP, SP, ZAP", }, { .ctrnum = 232, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "LAST_HOST_TRANSLATIONS", .desc = "Last Host Translation done", }, { .ctrnum = 243, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TX_NC_TABORT", .desc = "A transaction abort has occurred in a non-" "\n\t\tconstrained transactional-execution mode", }, { .ctrnum = 244, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TX_C_TABORT_NO_SPECIAL", .desc = "A transaction abort has occurred in a constrained" "\n\t\ttransactional-execution mode and the CPU is not" "\n\t\tusing any special logic to allow the transaction to" "\n\t\tcomplete", }, { .ctrnum = 245, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TX_C_TABORT_SPECIAL", .desc = "A transaction abort has occurred in a constrained" "\n\t\ttransactional-execution mode and the CPU is using" "\n\t\tspecial logic to allow the transaction to complete", }, { .ctrnum = 247, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DFLT_ACCESS", .desc = "Cycles CPU spent obtaining access to Deflate unit", }, { .ctrnum = 252, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DFLT_CYCLES", .desc = "Cycles CPU is using Deflate unit", }, { .ctrnum = 264, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DFLT_CC", .desc = "Increments by one for every DEFLATE CONVERSION CALL" "\n\t\tinstruction executed", }, { .ctrnum = 265, .ctrset = CPUMF_CTRSET_EXTENDED, .name = NULL, .desc = "Increments by one for every DEFLATE CONVERSION CALL" "\n\t\tinstruction executed that ended in Condition Codes" "\n\t\t0, 1 or 2", }, { .ctrnum = 448, .ctrset = CPUMF_CTRSET_MT_DIAG, .name = "MT_DIAG_CYCLES_ONE_THR_ACTIVE", .desc = "Cycle count with one thread active", }, { .ctrnum = 449, .ctrset = CPUMF_CTRSET_MT_DIAG, .name = "MT_DIAG_CYCLES_TWO_THR_ACTIVE", .desc = "Cycle count with two threads active", }, }; static struct counters cpumcf_z16_counters[] = { { .ctrnum = 128, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_RO_EXCL_WRITES", .desc = "A directory write to the Level-1 Data cache where" "\n\t\tthe line was originally in a Read-Only state in the" "\n\t\tcache but has been updated to be in the Exclusive" "\n\t\tstate that allows stores to the cache line.", }, { .ctrnum = 129, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DTLB2_WRITES", .desc = "A translation has been written into The Translation" "\n\t\tLookaside Buffer 2 (TLB2) and the request was made" "\n\t\tby the Level-1 Data cache. This is a replacement" "\n\t\tfor what was provided for the DTLB on z13 and prior" "\n\t\tmachines.", }, { .ctrnum = 130, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DTLB2_MISSES", .desc = "A TLB2 miss is in progress for a request made by" "\n\t\tthe Level-1 Data cache. Incremented by one for" "\n\t\tevery TLB2 miss in progress for the Level-1 Data" "\n\t\tcache on this cycle. This is a replacement for what" "\n\t\twas provided for the DTLB on z13 and prior" "\n\t\tmachines.", }, { .ctrnum = 131, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "CRSTE_1MB_WRITES", .desc = "A translation entry was written into the Combined" "\n\t\tRegion and Segment Table Entry array in the Level-2" "\n\t\tTLB for a one-megabyte page.", }, { .ctrnum = 132, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DTLB2_GPAGE_WRITES", .desc = "A translation entry for a two-gigabyte page was" "\n\t\twritten into the Level-2 TLB.", }, { .ctrnum = 134, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ITLB2_WRITES", .desc = "A translation entry has been written into the" "\n\t\tTranslation Lookaside Buffer 2 (TLB2) and the" "\n\t\trequest was made by the instruction cache. This is" "\n\t\ta replacement for what was provided for the ITLB on" "\n\t\tz13 and prior machines.", }, { .ctrnum = 135, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ITLB2_MISSES", .desc = "A TLB2 miss is in progress for a request made by" "\n\t\tthe Level-1 Instruction cache. Incremented by one" "\n\t\tfor every TLB2 miss in progress for the Level-1" "\n\t\tInstruction cache in a cycle. This is a replacement" "\n\t\tfor what was provided for the ITLB on z13 and prior" "\n\t\tmachines.", }, { .ctrnum = 137, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TLB2_PTE_WRITES", .desc = "A translation entry was written into the Page Table" "\n\t\tEntry array in the Level-2 TLB.", }, { .ctrnum = 138, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TLB2_CRSTE_WRITES", .desc = "Translation entries were written into the Combined" "\n\t\tRegion and Segment Table Entry array and the Page" "\n\t\tTable Entry array in the Level-2 TLB.", }, { .ctrnum = 139, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TLB2_ENGINES_BUSY", .desc = "The number of Level-2 TLB translation engines busy" "\n\t\tin a cycle.", }, { .ctrnum = 140, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TX_C_TEND", .desc = "A TEND instruction has completed in a constrained" "\n\t\ttransactional-execution mode.", }, { .ctrnum = 141, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TX_NC_TEND", .desc = "A TEND instruction has completed in a non-" "\n\t\tconstrained transactional-execution mode.", }, { .ctrnum = 143, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1C_TLB2_MISSES", .desc = "Increments by one for any cycle where a level-1" "\n\t\tcache or level-2 TLB miss is in progress.", }, { .ctrnum = 145, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DCW_REQ", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom the requestor’s Level-2 cache.", }, { .ctrnum = 146, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DCW_REQ_IV", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom the requestor’s Level-2 cache with" "\n\t\tintervention.", }, { .ctrnum = 147, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DCW_REQ_CHIP_HIT", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom the requestor’s Level-2 cache after using" "\n\t\tchip level horizontal persistence, Chip-HP hit.", }, { .ctrnum = 148, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DCW_REQ_DRAWER_HIT", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom the requestor’s Level-2 cache after using" "\n\t\tdrawer level horizontal persistence, Drawer-HP hit.", }, { .ctrnum = 149, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DCW_ON_CHIP", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Chip Level-2 cache.", }, { .ctrnum = 150, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DCW_ON_CHIP_IV", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Chip Level-2 cache with intervention.", }, { .ctrnum = 151, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DCW_ON_CHIP_CHIP_HIT", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Chip Level-2 cache after using chip" "\n\t\tlevel horizontal persistence, Chip-HP hit.", }, { .ctrnum = 152, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DCW_ON_CHIP_DRAWER_HIT", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Chip Level-2 cache using drawer level" "\n\t\thorizontal persistence, Drawer-HP hit.", }, { .ctrnum = 153, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DCW_ON_MODULE", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Module Level-2 cache.", }, { .ctrnum = 154, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DCW_ON_DRAWER", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Drawer Level-2 cache.", }, { .ctrnum = 155, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DCW_OFF_DRAWER", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off-Drawer Level-2 cache.", }, { .ctrnum = 156, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DCW_ON_CHIP_MEMORY", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom On-Chip memory.", }, { .ctrnum = 157, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DCW_ON_MODULE_MEMORY", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom On-Module memory.", }, { .ctrnum = 158, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DCW_ON_DRAWER_MEMORY", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom On-Drawer memory.", }, { .ctrnum = 159, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DCW_OFF_DRAWER_MEMORY", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom Off-Drawer memory.", }, { .ctrnum = 160, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "IDCW_ON_MODULE_IV", .desc = "A directory write to the Level-1 Data or Level-1" "\n\t\tInstruction cache directory where the returned" "\n\t\tcache line was sourced from an On-Module Level-2" "\n\t\tcache with intervention.", }, { .ctrnum = 161, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "IDCW_ON_MODULE_CHIP_HIT", .desc = "A directory write to the Level-1 Data or Level-1" "\n\t\tInstruction cache directory where the returned" "\n\t\tcache line was sourced from an On-Module Level-2" "\n\t\tcache using chip horizontal persistence, Chip-HP" "\n\t\thit.", }, { .ctrnum = 162, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "IDCW_ON_MODULE_DRAWER_HIT", .desc = "A directory write to the Level-1 Data or Level-1" "\n\t\tInstruction cache directory where the returned" "\n\t\tcache line was sourced from an On-Module Level-2" "\n\t\tcache using drawer level horizontal persistence," "\n\t\tDrawer-HP hit.", }, { .ctrnum = 163, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "IDCW_ON_DRAWER_IV", .desc = "A directory write to the Level-1 Data or Level-1" "\n\t\tInstruction cache directory where the returned" "\n\t\tcache line was sourced from an On-Drawer Level-2" "\n\t\tcache with intervention.", }, { .ctrnum = 164, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "IDCW_ON_DRAWER_CHIP_HIT", .desc = "A directory write to the Level-1 Data or Level-1" "\n\t\tinstruction cache directory where the returned" "\n\t\tcache line was sourced from an On-Drawer Level-2" "\n\t\tcache using chip level horizontal persistence, Chip-" "\n\t\tHP hit.", }, { .ctrnum = 165, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "IDCW_ON_DRAWER_DRAWER_HIT", .desc = "A directory write to the Level-1 Data or Level-1" "\n\t\tinstruction cache directory where the returned" "\n\t\tcache line was sourced from an On-Drawer Level-2" "\n\t\tcache using drawer level horizontal persistence," "\n\t\tDrawer-HP hit.", }, { .ctrnum = 166, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "IDCW_OFF_DRAWER_IV", .desc = "A directory write to the Level-1 Data or Level-1" "\n\t\tinstruction cache directory where the returned" "\n\t\tcache line was sourced from an Off-Drawer Level-2" "\n\t\tcache with intervention.", }, { .ctrnum = 167, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "IDCW_OFF_DRAWER_CHIP_HIT", .desc = "A directory write to the Level-1 Data or Level-1" "\n\t\tinstruction cache directory where the returned" "\n\t\tcache line was sourced from an Off-Drawer Level-2" "\n\t\tcache using chip level horizontal persistence, Chip-" "\n\t\tHP hit.", }, { .ctrnum = 168, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "IDCW_OFF_DRAWER_DRAWER_HIT", .desc = "A directory write to the Level-1 Data or Level-1" "\n\t\tInstruction cache directory where the returned" "\n\t\tcache line was sourced from an Off-Drawer Level-2" "\n\t\tcache using drawer level horizontal persistence," "\n\t\tDrawer-HP hit.", }, { .ctrnum = 169, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ICW_REQ", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tthe requestors Level-2 cache.", }, { .ctrnum = 170, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ICW_REQ_IV", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom the requestors Level-2 cache with" "\n\t\tintervention.", }, { .ctrnum = 171, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ICW_REQ_CHIP_HIT", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom the requestors Level-2 cache using chip level" "\n\t\thorizontal persistence, Chip-HP hit.", }, { .ctrnum = 172, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ICW_REQ_DRAWER_HIT", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom the requestor’s Level-2 cache using drawer" "\n\t\tlevel horizontal persistence, Drawer-HP hit.", }, { .ctrnum = 173, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ICW_ON_CHIP", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Chip Level-2 cache.", }, { .ctrnum = 174, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ICW_ON_CHIP_IV", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tan On-Chip Level-2 cache with intervention.", }, { .ctrnum = 175, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ICW_ON_CHIP_CHIP_HIT", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Chip Level-2 cache using chip level" "\n\t\thorizontal persistence, Chip-HP hit.", }, { .ctrnum = 176, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ICW_ON_CHIP_DRAWER_HIT", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Chip level 2 cache using drawer level" "\n\t\thorizontal persistence, Drawer-HP hit.", }, { .ctrnum = 177, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ICW_ON_MODULE", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Module Level-2 cache.", }, { .ctrnum = 178, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ICW_ON_DRAWER", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tan On-Drawer Level-2 cache.", }, { .ctrnum = 179, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ICW_OFF_DRAWER", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tan Off-Drawer Level-2 cache.", }, { .ctrnum = 180, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ICW_ON_CHIP_MEMORY", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom On-Chip memory.", }, { .ctrnum = 181, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ICW_ON_MODULE_MEMORY", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom On-Module memory.", }, { .ctrnum = 182, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ICW_ON_DRAWER_MEMORY", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom On-Drawer memory.", }, { .ctrnum = 183, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ICW_OFF_DRAWER_MEMORY", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom Off-Drawer memory.", }, { .ctrnum = 224, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "BCD_DFP_EXECUTION_SLOTS", .desc = "Count of floating point execution slots used for" "\n\t\tfinished Binary Coded Decimal to Decimal Floating" "\n\t\tPoint conversions. Instructions: CDZT, CXZT, CZDT," "\n\t\tCZXT.", }, { .ctrnum = 225, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "VX_BCD_EXECUTION_SLOTS", .desc = "Count of floating point execution slots used for" "\n\t\tfinished vector arithmetic Binary Coded Decimal" "\n\t\tinstructions. Instructions: VAP, VSP, VMP, VMSP," "\n\t\tVDP, VSDP, VRP, VLIP, VSRP, VPSOP, VCP, VTP, VPKZ," "\n\t\tVUPKZ, VCVB, VCVBG, VCVD, VCVDG.", }, { .ctrnum = 226, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DECIMAL_INSTRUCTIONS", .desc = "Decimal instruction dispatched. Instructions: CVB," "\n\t\tCVD, AP, CP, DP, ED, EDMK, MP, SRP, SP, ZAP.", }, { .ctrnum = 232, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "LAST_HOST_TRANSLATIONS", .desc = "Last Host Translation done", }, { .ctrnum = 244, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TX_NC_TABORT", .desc = "A transaction abort has occurred in a non-" "\n\t\tconstrained transactional-execution mode.", }, { .ctrnum = 245, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TX_C_TABORT_NO_SPECIAL", .desc = "A transaction abort has occurred in a constrained" "\n\t\ttransactional-execution mode and the CPU is not" "\n\t\tusing any special logic to allow the transaction to" "\n\t\tcomplete.", }, { .ctrnum = 246, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TX_C_TABORT_SPECIAL", .desc = "A transaction abort has occurred in a constrained" "\n\t\ttransactional-execution mode and the CPU is using" "\n\t\tspecial logic to allow the transaction to complete.", }, { .ctrnum = 248, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DFLT_ACCESS", .desc = "Cycles CPU spent obtaining access to Deflate unit", }, { .ctrnum = 253, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DFLT_CYCLES", .desc = "Cycles CPU is using Deflate unit", }, { .ctrnum = 256, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "SORTL", .desc = "Increments by one for every SORT LISTS instruction" "\n\t\texecuted.", }, { .ctrnum = 265, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DFLT_CC", .desc = "Increments by one for every DEFLATE CONVERSION CALL" "\n\t\tinstruction executed.", }, { .ctrnum = 266, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DFLT_CCFINISH", .desc = "Increments by one for every DEFLATE CONVERSION CALL" "\n\t\tinstruction executed that ended in Condition Codes" "\n\t\t0, 1 or 2.", }, { .ctrnum = 267, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "NNPA_INVOCATIONS", .desc = "Increments by one for every Neural Network" "\n\t\tProcessing Assist instruction executed.", }, { .ctrnum = 268, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "NNPA_COMPLETIONS", .desc = "Increments by one for every Neural Network" "\n\t\tProcessing Assist instruction executed that ended" "\n\t\tin Condition Codes 0, 1 or 2.", }, { .ctrnum = 269, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "NNPA_WAIT_LOCK", .desc = "Cycles CPU spent obtaining access to IBM Z" "\n\t\tIntegrated Accelerator for AI.", }, { .ctrnum = 270, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "NNPA_HOLD_LOCK", .desc = "Cycles CPU is using IBM Z Integrated Accelerator" "\n\t\tfor AI.", }, { .ctrnum = 448, .ctrset = CPUMF_CTRSET_MT_DIAG, .name = "MT_DIAG_CYCLES_ONE_THR_ACTIVE", .desc = "Cycle count with one thread active", }, { .ctrnum = 449, .ctrset = CPUMF_CTRSET_MT_DIAG, .name = "MT_DIAG_CYCLES_TWO_THR_ACTIVE", .desc = "Cycle count with two threads active", }, }; static struct counters cpumcf_z17_counters[] = { { .ctrnum = 128, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1D_RO_EXCL_WRITES", .desc = "A directory write to the Level-1 Data cache where" "\n\t\tthe line was originally in a Read-Only state in the" "\n\t\tcache but has been updated to be in the Exclusive" "\n\t\tstate that allows stores to the cache line.", }, { .ctrnum = 129, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DTLB2_WRITES", .desc = "A translation has been written into The Translation" "\n\t\tLookaside Buffer 2 (TLB2) and the request was made" "\n\t\tby the Level-1 Data cache. This is a replacement" "\n\t\tfor what was provided for the DTLB on z13 and prior" "\n\t\tmachines.", }, { .ctrnum = 130, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DTLB2_MISSES", .desc = "A TLB2 miss is in progress for a request made by" "\n\t\tthe Level-1 Data cache. Incremented by one for" "\n\t\tevery TLB2 miss in progress for the Level-1 Data" "\n\t\tcache on this cycle. This is a replacement for what" "\n\t\twas provided for the DTLB on z13 and prior" "\n\t\tmachines.", }, { .ctrnum = 131, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "CRSTE_1MB_WRITES", .desc = "A translation entry was written into the Combined" "\n\t\tRegion and Segment Table Entry array in the Level-2" "\n\t\tTLB for a one-megabyte page.", }, { .ctrnum = 132, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DTLB2_GPAGE_WRITES", .desc = "A translation entry for a two-gigabyte page was" "\n\t\twritten into the Level-2 TLB.", }, { .ctrnum = 134, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ITLB2_WRITES", .desc = "A translation entry has been written into the" "\n\t\tTranslation Lookaside Buffer 2 (TLB2) and the" "\n\t\trequest was made by the Level-1 Instruction cache." "\n\t\tThis is a replacement for what was provided for the" "\n\t\tITLB on z13 and prior machines.", }, { .ctrnum = 135, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ITLB2_MISSES", .desc = "A TLB2 miss is in progress for a request made by" "\n\t\tthe Level-1 Instruction cache. Incremented by one" "\n\t\tfor every TLB2 miss in progress for the Level-1" "\n\t\tInstruction cache in a cycle. This is a replacement" "\n\t\tfor what was provided for the ITLB on z13 and prior" "\n\t\tmachines.", }, { .ctrnum = 137, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TLB2_PTE_WRITES", .desc = "A translation entry was written into the Page Table" "\n\t\tEntry array in the Level-2 TLB.", }, { .ctrnum = 138, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TLB2_CRSTE_WRITES", .desc = "Translation entries were written into the Combined" "\n\t\tRegion and Segment Table Entry array and the Page" "\n\t\tTable Entry array in the Level-2 TLB.", }, { .ctrnum = 139, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TLB2_ENGINES_BUSY", .desc = "The number of Level-2 TLB translation engines busy" "\n\t\tin a cycle.", }, { .ctrnum = 140, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TX_C_TEND", .desc = "A TEND instruction has completed in a constrained" "\n\t\ttransactional-execution mode.", }, { .ctrnum = 141, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TX_NC_TEND", .desc = "A TEND instruction has completed in a non-" "\n\t\tconstrained transactional-execution mode.", }, { .ctrnum = 143, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "L1C_TLB2_MISSES", .desc = "Increments by one for any cycle where a Level-1" "\n\t\tcache or Level-2 TLB miss is in progress.", }, { .ctrnum = 145, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DCW_REQ", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom the requestors Level-2 cache.", }, { .ctrnum = 146, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DCW_REQ_IV", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom the requestors Level-2 cache with" "\n\t\tintervention.", }, { .ctrnum = 147, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DCW_REQ_CHIP_HIT", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom the requestors Level-2 cache after using" "\n\t\tchip level horizontal persistence, Chip-HP hit.", }, { .ctrnum = 148, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DCW_REQ_DRAWER_HIT", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom the requestors Level-2 cache after using" "\n\t\tdrawer level horizontal persistence, Drawer-HP hit.", }, { .ctrnum = 149, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DCW_ON_CHIP", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Chip Level-2 cache.", }, { .ctrnum = 150, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DCW_ON_CHIP_IV", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Chip Level-2 cache with intervention.", }, { .ctrnum = 151, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DCW_ON_CHIP_CHIP_HIT", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Chip Level-2 cache after using chip" "\n\t\tlevel horizontal persistence, Chip-HP hit.", }, { .ctrnum = 152, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DCW_ON_CHIP_DRAWER_HIT", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Chip Level-2 cache after using drawer" "\n\t\tlevel horizontal persistence, Drawer-HP hit.", }, { .ctrnum = 153, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DCW_ON_MODULE", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Module Level-2 cache.", }, { .ctrnum = 154, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DCW_ON_DRAWER", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Drawer Level-2 cache.", }, { .ctrnum = 155, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DCW_OFF_DRAWER", .desc = "A directory write to the Level-1 Data cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off-Drawer Level-2 cache.", }, { .ctrnum = 156, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DCW_ON_CHIP_MEMORY", .desc = "A directory write to the Level-1 Data or Level-1" "\n\t\tInstruction cache directory where the returned" "\n\t\tcache line was sourced from On-Chip memory.", }, { .ctrnum = 157, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DCW_ON_MODULE_MEMORY", .desc = "A directory write to the Level-1 Data or Level-1" "\n\t\tInstruction cache directory where the returned" "\n\t\tcache line was sourced from On-Module memory.", }, { .ctrnum = 158, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DCW_ON_DRAWER_MEMORY", .desc = "A directory write to the Level-1 Data or Level-1" "\n\t\tInstruction cache directory where the returned" "\n\t\tcache line was sourced from On-Drawer memory.", }, { .ctrnum = 159, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DCW_OFF_DRAWER_MEMORY", .desc = "A directory write to the Level-1 Data or Level-1" "\n\t\tInstruction cache directory where the returned" "\n\t\tcache line was sourced from Off-Drawer memory.", }, { .ctrnum = 160, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "IDCW_ON_MODULE_IV", .desc = "A directory write to the Level-1 Data or Level-1" "\n\t\tInstruction cache directory where the returned" "\n\t\tcache line was sourced from an On-Module Level-2" "\n\t\tcache with intervention.", }, { .ctrnum = 161, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "IDCW_ON_MODULE_CHIP_HIT", .desc = "A directory write to the Level-1 Data or Level-1" "\n\t\tInstruction cache directory where the returned" "\n\t\tcache line was sourced from an On-Module Level-2" "\n\t\tcache after using chip level horizontal" "\n\t\tpersistence, Chip-HP hit.", }, { .ctrnum = 162, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "IDCW_ON_MODULE_DRAWER_HIT", .desc = "A directory write to the Level-1 Data or Level-1" "\n\t\tInstruction cache directory where the returned" "\n\t\tcache line was sourced from an On-Module Level-2" "\n\t\tcache after using drawer level horizontal" "\n\t\tpersistence, Drawer-HP hit.", }, { .ctrnum = 163, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "IDCW_ON_DRAWER_IV", .desc = "A directory write to the Level-1 Data or Level-1" "\n\t\tInstruction cache directory where the returned" "\n\t\tcache line was sourced from an On-Drawer Level-2" "\n\t\tcache with intervention.", }, { .ctrnum = 164, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "IDCW_ON_DRAWER_CHIP_HIT", .desc = "A directory write to the Level-1 Data or Level-1" "\n\t\tinstruction cache directory where the returned" "\n\t\tcache line was sourced from an On-Drawer Level-2" "\n\t\tcache after using chip level horizontal" "\n\t\tpersistence, Chip-HP hit.", }, { .ctrnum = 165, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "IDCW_ON_DRAWER_DRAWER_HIT", .desc = "A directory write to the Level-1 Data or Level-1" "\n\t\tinstruction cache directory where the returned" "\n\t\tcache line was sourced from an On-Drawer Level-2" "\n\t\tcache after using drawer level horizontal" "\n\t\tpersistence, Drawer-HP hit.", }, { .ctrnum = 166, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "IDCW_OFF_DRAWER_IV", .desc = "A directory write to the Level-1 Data or Level-1" "\n\t\tinstruction cache directory where the returned" "\n\t\tcache line was sourced from an Off-Drawer Level-2" "\n\t\tcache with intervention.", }, { .ctrnum = 167, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "IDCW_OFF_DRAWER_CHIP_HIT", .desc = "A directory write to the Level-1 Data or Level-1" "\n\t\tinstruction cache directory where the returned" "\n\t\tcache line was sourced from an Off-Drawer Level-2" "\n\t\tcache after using chip level horizontal" "\n\t\tpersistence, Chip-HP hit.", }, { .ctrnum = 168, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "IDCW_OFF_DRAWER_DRAWER_HIT", .desc = "A directory write to the Level-1 Data or Level-1" "\n\t\tInstruction cache directory where the returned" "\n\t\tcache line was sourced from an Off-Drawer Level-2" "\n\t\tcache after using drawer level horizontal" "\n\t\tpersistence, Drawer-HP hit.", }, { .ctrnum = 169, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ICW_REQ", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tthe requestors Level-2 cache.", }, { .ctrnum = 170, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ICW_REQ_IV", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom the requestors Level-2 cache with" "\n\t\tintervention.", }, { .ctrnum = 171, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ICW_REQ_CHIP_HIT", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom the requestors Level-2 cache after using" "\n\t\tchip level horizontal persistence, Chip-HP hit.", }, { .ctrnum = 172, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ICW_REQ_DRAWER_HIT", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom the requestors Level-2 cache after using" "\n\t\tdrawer level horizontal persistence, Drawer-HP hit.", }, { .ctrnum = 173, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ICW_ON_CHIP", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Chip Level-2 cache.", }, { .ctrnum = 174, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ICW_ON_CHIP_IV", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Chip Level-2 cache with intervention.", }, { .ctrnum = 175, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ICW_ON_CHIP_CHIP_HIT", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Chip Level-2 cache after using chip" "\n\t\tlevel horizontal persistence, Chip-HP hit.", }, { .ctrnum = 176, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ICW_ON_CHIP_DRAWER_HIT", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Chip level 2 cache after using drawer" "\n\t\tlevel horizontal persistence, Drawer-HP hit.", }, { .ctrnum = 177, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ICW_ON_MODULE", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Module Level-2 cache.", }, { .ctrnum = 178, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ICW_ON_DRAWER", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an On-Drawer Level-2 cache.", }, { .ctrnum = 179, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "ICW_OFF_DRAWER", .desc = "A directory write to the Level-1 Instruction cache" "\n\t\tdirectory where the returned cache line was sourced" "\n\t\tfrom an Off-Drawer Level-2 cache.", }, { .ctrnum = 202, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "CYCLES_SAMETHRD", .desc = "The number of cycles the CPU is not in wait state" "\n\t\tand the CPU is running by itself on the Core.", }, { .ctrnum = 203, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "CYCLES_DIFFTHRD", .desc = "The number of cycles the CPU is not in wait state" "\n\t\tand the CPU is running with another thread on the" "\n\t\tCore.", }, { .ctrnum = 204, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "INST_SAMETHRD", .desc = "The number of instructions executed on the CPU and" "\n\t\tthe CPU is running by itself on the Core.", }, { .ctrnum = 205, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "INST_DIFFTHRD", .desc = "The number of instructions executed on the CPU and" "\n\t\tthe CPU is running with another thread on the Core.", }, { .ctrnum = 206, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "WRONG_BRANCH_PREDICTION", .desc = "A count of the number of branches that were" "\n\t\tpredicted incorrectly by the branch prediction" "\n\t\tlogic in the Core. This includes incorrectly" "\n\t\tpredicted branches that are executed in Firmware." "\n\t\tExamples of instructions implemented in Firmware" "\n\t\tare complicated instructions like MVCL (Move" "\n\t\tCharacter Long) and PC (Program Call).", }, { .ctrnum = 225, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "VX_BCD_EXECUTION_SLOTS", .desc = "Count of floating point execution slots used for" "\n\t\tfinished vector arithmetic Binary Coded Decimal" "\n\t\tinstructions. Instructions: VAP, VSP, VMP, VMSP," "\n\t\tVDP, VSDP, VRP, VLIP, VSRP, VPSOP, VCP, VTP, VPKZ," "\n\t\tVUPKZ, VCVB, VCVBG, VCVD, VCVDG, VSCHP, VSCSHP," "\n\t\tVCSPH, VCLZDP, VPKZR, VSRPR, VUPKZH, VUPKZL, VTZ," "\n\t\tVUPH, VUPL, VCVBX, VCVDX.", }, { .ctrnum = 226, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DECIMAL_INSTRUCTIONS", .desc = "Decimal instruction dispatched. Instructions: CVB," "\n\t\tCVD, AP, CP, DP, ED, EDMK, MP, SRP, SP, ZAP, TP.", }, { .ctrnum = 232, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "LAST_HOST_TRANSLATIONS", .desc = "Last Host Translation done.", }, { .ctrnum = 244, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TX_NC_TABORT", .desc = "A transaction abort has occurred in a non-" "\n\t\tconstrained transactional-execution mode.", }, { .ctrnum = 245, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TX_C_TABORT_NO_SPECIAL", .desc = "A transaction abort has occurred in a constrained" "\n\t\ttransactional-execution mode and the CPU is not" "\n\t\tusing any special logic to allow the transaction to" "\n\t\tcomplete.", }, { .ctrnum = 246, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "TX_C_TABORT_SPECIAL", .desc = "A transaction abort has occurred in a constrained" "\n\t\ttransactional-execution mode and the CPU is using" "\n\t\tspecial logic to allow the transaction to complete.", }, { .ctrnum = 248, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DFLT_ACCESS", .desc = "Cycles CPU spent obtaining access to Deflate unit.", }, { .ctrnum = 253, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DFLT_CYCLES", .desc = "Cycles CPU is using Deflate unit.", }, { .ctrnum = 256, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "SORTL", .desc = "Increments by one for every SORT LISTS (SORTL)" "\n\t\tinstruction executed.", }, { .ctrnum = 265, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DFLT_CC", .desc = "Increments by one for every DEFLATE CONVERSION CALL" "\n\t\t(DFLTCC) instruction executed.", }, { .ctrnum = 266, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "DFLT_CCFINISH", .desc = "Increments by one for every DEFLATE CONVERSION CALL" "\n\t\t(DFLTCC) instruction executed that ended in" "\n\t\tCondition Codes 0, 1 or 2.", }, { .ctrnum = 267, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "NNPA_INVOCATIONS", .desc = "Increments by one for every NEURAL NETWORK" "\n\t\tPROCESSING ASSIST (NNPA) instruction executed.", }, { .ctrnum = 268, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "NNPA_COMPLETIONS", .desc = "Increments by one for every NEURAL NETWORK" "\n\t\tPROCESSING ASSIST (NNPA) instruction executed that" "\n\t\tended in Condition Code 0.", }, { .ctrnum = 269, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "NNPA_WAIT_LOCK", .desc = "Cycles CPU spent obtaining access to IBM Z" "\n\t\tIntegrated Accelerator for AI.", }, { .ctrnum = 270, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "NNPA_HOLD_LOCK", .desc = "Cycles CPU is using IBM Z Integrated Accelerator" "\n\t\tfor AI.", }, { .ctrnum = 272, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "NNPA_INST_ONCHIP", .desc = "A NEURAL NETWORK PROCESSING ASSIST (NNPA)" "\n\t\tinstruction has used the Local On-Chip IBM Z" "\n\t\tIntegrated Accelerator for AI during its execution", }, { .ctrnum = 273, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "NNPA_INST_OFFCHIP", .desc = "A NEURAL NETWORK PROCESSING ASSIST (NNPA)" "\n\t\tinstruction has used an Off-Chip IBM Z Integrated" "\n\t\tAccelerator for AI during its execution.", }, { .ctrnum = 274, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "NNPA_INST_DIFF", .desc = "A NEURAL NETWORK PROCESSING ASSIST (NNPA)" "\n\t\tinstruction has used a different IBM Z Integrated" "\n\t\tAccelerator for AI since it was last executed.", }, { .ctrnum = 276, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "NNPA_4K_PREFETCH", .desc = "Number of 4K prefetches done for a remote IBM Z" "\n\t\tIntegated Accelerator for AI.", }, { .ctrnum = 277, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "NNPA_COMPL_LOCK", .desc = "A PERFORM LOCKED OPERATION (PLO) has completed.", }, { .ctrnum = 278, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "NNPA_RETRY_LOCK", .desc = "A PERFORM LOCKED OPERATION (PLO) has been retried and" "\n\t\tthe CPU did not use any special logic to allow the" "\n\t\tPLO to complete.", }, { .ctrnum = 279, .ctrset = CPUMF_CTRSET_EXTENDED, .name = "NNPA_RETRY_LOCK_WITH_PLO", .desc = "A PERFORM LOCKED OPERATION (PLO) has been retried and" "\n\t\tthe CPU is using special logic to allow PLO to" "\n\t\tcomplete.", }, { .ctrnum = 448, .ctrset = CPUMF_CTRSET_MT_DIAG, .name = "MT_DIAG_CYCLES_ONE_THR_ACTIVE", .desc = "Cycle count with one thread active", }, { .ctrnum = 449, .ctrset = CPUMF_CTRSET_MT_DIAG, .name = "MT_DIAG_CYCLES_TWO_THR_ACTIVE", .desc = "Cycle count with two threads active", }, }; /* Return the type number of the CPU Measurement facility from the sysfs file. * If the type number is equal to PERF_TYPE_RAW, then the prefix is 'r' to * specify the raw counter number by the perf tool. * If perf_pmu_register() kernel function assigned any other (higher) type * number, set the prefix to : */ static void set_prefix(int nr) { if (nr == PERF_TYPE_RAW) strcat(prefix, "r"); else snprintf(prefix, sizeof(prefix), "%d:", nr); } /* Parse tool parameters. In case of --help or --version, print * respective text to stdout and exit. * Only handle one option and simulate behavior of previous tool. */ static int parse_args(int argc, char **argv) { int opt; actions[ACTION_NONE] = true; while ((opt = util_opt_getopt_long(argc, argv)) != EOF) { switch (opt) { case 'h': util_prg_print_help(); util_opt_print_help(); exit(EXIT_SUCCESS); case 'v': util_prg_print_version(); exit(EXIT_SUCCESS); case 'i': actions[ACTION_INFO] = true; break; case 'n': show_names = true; break; case 's': actions[ACTION_SAMPLE] = true; break; case 'c': actions[ACTION_CNT] = true; break; case 'C': actions[ACTION_CNTALL] = true; break; default: util_opt_print_parse_error(opt, argv); exit(EXIT_FAILURE); } } if (actions[ACTION_INFO]) return ACTION_INFO; else if (actions[ACTION_CNT]) return ACTION_CNT; else if (actions[ACTION_CNTALL]) return ACTION_CNTALL; else if (actions[ACTION_SAMPLE]) return ACTION_SAMPLE; return ACTION_NONE; } static unsigned long tohz(unsigned long value, unsigned long cpuspeed) { return 1000000 * cpuspeed / value; } static void human(char *buffer, size_t buffer_len, unsigned long bytes) { const char bu[] = " KMGTPEZY"; size_t unit = 0; while (bytes >= 1024 && unit < strlen(bu)) { bytes /= 1024; ++unit; } snprintf(buffer, buffer_len, "%ld%cB", bytes, bu[unit]); } static unsigned long div_ceil(unsigned long a, unsigned long b) { return (a + b - 1) / b; } static void show_info(struct cpumf_info *p, int details) { if (!p->have_counter && !p->have_samples) { warnx("No CPU-measurement facilities detected"); return; } if (p->have_counter) { printf("CPU-measurement Counter Facility\n"); if (details) { printf("----------------------------------------------" "----------------------------\n"); printf("Version: %d.%d\n\n", p->first_vn, p->second_vn); printf("Authorized counter sets:\n"); if (!p->authorization) printf(" None\n"); if (0x2 & p->authorization) printf(" Basic counter Set\n"); if (0x8 & p->authorization) printf(" Crypto-Activity counter Set\n"); if (0x1 & p->authorization) printf(" Extended counter Set\n"); if (0x20 & p->authorization) printf(" MT-diagnostic counter Set\n"); if (0x4 & p->authorization) printf(" Problem-State counter Set\n"); if (0x8000 & p->authorization) printf(" Coprocessor Group counter Set\n"); printf("\nLinux perf event support: %s\n\n", !p->have_counter ? "No" : "Yes (PMU: cpum_cf)"); } } else warnx("No CPU-measurement counter facility detected"); if (p->have_samples) { unsigned long total, fdiag; char text[32]; printf("CPU-measurement Sampling Facility\n"); if (details) { printf("----------------------------------------------" "----------------------------\n"); printf("Sampling Interval:\n"); printf(" Minimum: %10ld cycles" " (approx. %9ld Hz)\n", p->min_rate, tohz(p->min_rate, p->cpu_speed)); printf(" Maximum: %10ld cycles" " (approx. %9ld Hz)\n", p->max_rate, tohz(p->max_rate, p->cpu_speed)); printf("\n"); printf("Authorized sampling modes:\n"); printf(" basic: (sample size: %3d bytes)\n", p->basic_sample_sz); printf(" diagnostic: (sample size: %3d bytes)\n", p->diag_sample_sz); printf("\nLinux perf event support: %s\n\n", !p->have_samples ? "No" : "Yes (PMU: cpum_sf)"); printf("Current sampling buffer settings for" " cpum_sf:\n"); printf(" Basic-sampling mode\n"); total = p->min_sfb + div_ceil(p->min_sfb, PER_SDBT_SIZE); human(text, sizeof(text), PAGE_SIZE * total); printf(" Minimum: %6ld" " sample-data-blocks (%6s)\n", p->min_sfb, text); total = p->max_sfb + div_ceil(p->max_sfb, PER_SDBT_SIZE); human(text, sizeof(text), PAGE_SIZE * total); printf(" Maximum: %6ld" " sample-data-blocks (%6s)\n\n", p->max_sfb, text); printf(" Diagnostic-sampling mode" " (including basic-sampling)\n"); fdiag = div_ceil(p->diag_sample_sz, p->basic_sample_sz); total = fdiag * p->min_sfb + div_ceil(p->min_sfb, PER_SDBT_SIZE); human(text, sizeof(text), PAGE_SIZE * total); printf(" Minimum: %6ld" " sample-data-blocks (%6s)\n", fdiag * p->min_sfb, text); total = fdiag * p->max_sfb + div_ceil(p->max_sfb * fdiag, PER_SDBT_SIZE); human(text, sizeof(text), PAGE_SIZE * total); printf(" Maximum: %6ld" " sample-data-blocks (%6s)\n", fdiag * p->max_sfb, text); printf(" Size factor: %2ld\n", fdiag); } } else warnx("No CPU-measurement sampling facility detected"); } /* Set the counter name for z15 counter numbered 265. It is either named * DFLT_CCERROR or DFLT_CCFINISH, depending on the linux version. The * counter was renamed from CCERROR to CCFINISH in linux version 5.8. * Check for existence of file /sys/devices/cpum_cf/events/DLFT_CCERROR. */ static void read_ccerror(struct counters *cp, size_t cp_cnt) { char *ctrname, *path; struct stat sbuf; size_t i = 0; path = util_path_sysfs("devices/cpum_cf/events/DFLT_CCERROR"); if (!stat(path, &sbuf)) ctrname = "DFLT_CCERROR"; else ctrname = "DFLT_CCFINISH"; free(path); /* Find DFLT_CC{FINISH,ERROR} in table and set name */ for (struct counters *p = cp; i < cp_cnt; ++i, ++p) if (p->ctrnum == 265) { p->name = ctrname; break; } } /* Read allnecessary information from /sysfs file /proc/service_levels */ static int read_info(void) { int rc = EXIT_FAILURE; cpumf.have_counter = libcpumf_cpumcf_info(&cpumf.first_vn, &cpumf.second_vn, &cpumf.authorization); cpumf.have_samples = libcpumf_cpumsf_info(&cpumf.min_rate, &cpumf.max_rate, &cpumf.cpu_speed, &cpumf.basic_sample_sz, &cpumf.diag_sample_sz); if (cpumf.have_samples) libcpumf_sfb_info(&cpumf.min_sfb, &cpumf.max_sfb); cpumf.machine_type = util_arch_machine_type(); if (cpumf.machine_type == UTIL_ARCH_MACHINE_TYPE_UNKNOWN) rc = EXIT_FAILURE; else rc = EXIT_SUCCESS; return rc; } static void show_hdr(void) { printf("================================================" "==============================\n\n" "Raw\n" "event Name Description\n" "---------------------------" "---------------------------------------------------\n"); } #define FORMATS "%s%5lx\t%s\n\n %s\n %s\n\n" #define FORMATC "%s%d%s\n\n %s\n %s %d / %s\n\n" static void show_sample(void) { printf("Perf events for activating the sampling facility\n"); show_hdr(); for (unsigned int i = 0; i < ARRAY_SIZE(def_samples); ++i) { printf(FORMATS, prefix, def_samples[i].counter, def_samples[i].name, def_samples[i].desc, "This event is not associated with a counter set."); } } static const char *name_counterset(int snr) { switch (snr) { case CPUMF_CTRSET_BASIC: return "Basic Counter Set."; case CPUMF_CTRSET_PROBLEM_STATE: return "Problem-State Counter Set."; case CPUMF_CTRSET_CRYPTO: return "Crypto-Activity Counter Set."; case CPUMF_CTRSET_EXTENDED: return "Extended Counter Set."; case CPUMF_CTRSET_MT_DIAG: return "MT-diagnostic Counter Set."; default: return "Unknown Counter Set."; } } /* Check if counter set this counter belongs to has been authorized. */ static bool auth_counterset(struct counters *cp) { if (cp->ctrset == CPUMF_CTRSET_BASIC && (cpumf.authorization & 2)) return true; if (cp->ctrset == CPUMF_CTRSET_PROBLEM_STATE && (cpumf.authorization & 4)) return true; if (cp->ctrset == CPUMF_CTRSET_CRYPTO && (cpumf.authorization & 8)) return true; if (cp->ctrset == CPUMF_CTRSET_EXTENDED && (cpumf.authorization & 1)) return true; if (cp->ctrset == CPUMF_CTRSET_MT_DIAG && (cpumf.authorization & 0x20)) return true; return false; } /* Return pointer to counter set and size. */ static struct counters *get_counter(int ctrset, size_t *len) { struct counters *cp = NULL; *len = 0; switch (ctrset) { case CPUMF_CTRSET_BASIC: if (cpumf.first_vn == 1) { cp = cpumcf_fvn1_counters; *len = ARRAY_SIZE(cpumcf_fvn1_counters); } else { cp = cpumcf_fvn3_counters; *len = ARRAY_SIZE(cpumcf_fvn3_counters); } break; case CPUMF_CTRSET_CRYPTO: if (cpumf.second_vn <= 5) { cp = cpumcf_svn_12345_counters; *len = ARRAY_SIZE(cpumcf_svn_12345_counters); } else { cp = cpumcf_svn_6_counters; *len = ARRAY_SIZE(cpumcf_svn_6_counters); } break; case CPUMF_CTRSET_EXTENDED: switch (cpumf.machine_type) { case UTIL_ARCH_MACHINE_TYPE_Z10_EC: case UTIL_ARCH_MACHINE_TYPE_Z10_BC: cp = cpumcf_z10_counters; *len = ARRAY_SIZE(cpumcf_z10_counters); break; case UTIL_ARCH_MACHINE_TYPE_ZE_196: case UTIL_ARCH_MACHINE_TYPE_ZE_114: cp = cpumcf_z196_counters; *len = ARRAY_SIZE(cpumcf_z196_counters); break; case UTIL_ARCH_MACHINE_TYPE_ZE_EC12: case UTIL_ARCH_MACHINE_TYPE_ZE_BC12: cp = cpumcf_zec12_counters; *len = ARRAY_SIZE(cpumcf_zec12_counters); break; case UTIL_ARCH_MACHINE_TYPE_Z13: case UTIL_ARCH_MACHINE_TYPE_Z13_S: cp = cpumcf_z13_counters; *len = ARRAY_SIZE(cpumcf_z13_counters); break; case UTIL_ARCH_MACHINE_TYPE_Z14: case UTIL_ARCH_MACHINE_TYPE_Z14_ZR1: cp = cpumcf_z14_counters; *len = ARRAY_SIZE(cpumcf_z14_counters); break; case UTIL_ARCH_MACHINE_TYPE_Z15: case UTIL_ARCH_MACHINE_TYPE_Z15_T02: cp = cpumcf_z15_counters; *len = ARRAY_SIZE(cpumcf_z15_counters); read_ccerror(cp, *len); break; case UTIL_ARCH_MACHINE_TYPE_Z16: case UTIL_ARCH_MACHINE_TYPE_Z16_A02: cp = cpumcf_z16_counters; *len = ARRAY_SIZE(cpumcf_z16_counters); break; case UTIL_ARCH_MACHINE_TYPE_Z17: case UTIL_ARCH_MACHINE_TYPE_Z17_2: cp = cpumcf_z17_counters; *len = ARRAY_SIZE(cpumcf_z17_counters); break; } break; } return cp; } static void show_counterset(bool all, struct counters *cp, size_t cp_cnt) { char cnt_name[128]; for (size_t i = 0; i < cp_cnt; ++i, ++cp) { if (!all && !auth_counterset(cp)) continue; if (show_names) snprintf(cnt_name, sizeof cnt_name, "/name=%s/", cp->name); else snprintf(cnt_name, sizeof cnt_name, "\t%s", cp->name); printf(FORMATC, prefix, cp->ctrnum, cnt_name, cp->desc, "Counter", cp->ctrnum, name_counterset(cp->ctrset)); } } static void show_counter(bool all) { struct counters *cp; size_t cp_cnt; printf("perf event counter list for %s\n", util_arch_machine_type_str()); show_hdr(); /* Basic counter set */ cp = get_counter(CPUMF_CTRSET_BASIC, &cp_cnt); show_counterset(all, cp, cp_cnt); /* Crypto counter set */ cp = get_counter(CPUMF_CTRSET_CRYPTO, &cp_cnt); show_counterset(all, cp, cp_cnt); /* Extended counter set */ cp = get_counter(CPUMF_CTRSET_EXTENDED, &cp_cnt); show_counterset(all, cp, cp_cnt); } int main(int argc, char **argv) { bool all = false; int ret; util_prg_init(&prg); util_opt_init(opt_vec, NULL); ret = parse_args(argc, argv); if (read_info() == EXIT_FAILURE) return EXIT_FAILURE; switch (ret) { case ACTION_CNT: case ACTION_CNTALL: all = ret == ACTION_CNTALL; ret = libcpumf_pmutype(S390_CPUMF_CF); if (ret >= EXIT_SUCCESS) { set_prefix(ret); show_counter(all); } break; case ACTION_SAMPLE: ret = libcpumf_pmutype(S390_CPUMF_SF); if (ret >= EXIT_SUCCESS) { set_prefix(ret); show_sample(); } break; case ACTION_NONE: case ACTION_INFO: show_info(&cpumf, ret == ACTION_INFO); ret = EXIT_SUCCESS; break; } return ret; } s390-tools-2.38.0/cpumf/lshwc.c000066400000000000000000000532251502674226300160540ustar00rootroot00000000000000/* Copyright IBM Corp. 2021, 2024 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ /* CPU Measurements counter facility counter sets can be extracted by a * device driver accessible by opening device /dev/hwctr. * This program extracts complete counter set using this device. * Counter sets are per CPU, the interface allows to specify counter sets * for individual CPUs. The supported flags are executed from left to * right, the first error encountered stops the execution of the program. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/util_opt.h" #include "lib/util_prg.h" #include "lib/util_base.h" #include "lib/util_path.h" #include "lib/util_scandir.h" #include "lib/util_libc.h" #include "lib/util_file.h" #include "lib/util_fmt.h" #include "lib/libcpumf.h" #include "lshwc.h" #define CPUS_ONLINE "/sys/devices/system/cpu/online" #define CPUS_POSSIBLE "/sys/devices/system/cpu/possible" #define CPUS_KERNELMAX "/sys/devices/system/cpu/kernel_max" #define MAXCTRS 512 #define IOCTLSLEEP 60U static unsigned int read_interval = IOCTLSLEEP; static int cfvn, csvn, authorization; static unsigned long loop_count = 1, timeout; static unsigned char *ioctlbuffer; static bool allcpu; static char *ctrformat = "%ld"; static bool shortname; static bool hideundef; static bool delta, firstread; static int output_format = FMT_CSV; static bool quote_all; static unsigned int max_possible_cpus; /* No of possible CPUs */ static struct ctrname { /* List of defined counters */ char *name; /* Counter name */ char *label; /* Output name */ bool hitcnt; /* Counter number read from ioctl() */ unsigned long total; /* Total counter value */ unsigned long *ccv; /* Per CPU counter value */ unsigned long *ccvprv; /* Per CPU counter value (previous read) */ } ctrname[MAXCTRS]; struct time_formats { char epoch[32]; char date_time[32]; char date[16]; char time[16]; }; static void mk_labels(void) { char label[64]; size_t i; for (i = 0; i < ARRAY_SIZE(ctrname); ++i) { if (shortname) { if (ctrname[i].name) snprintf(label, sizeof(label), "%s", ctrname[i].name); else snprintf(label, sizeof(label), "U%ld", i); } else { if (output_format == FMT_CSV) snprintf(label, sizeof(label), "%s(%ld)", ctrname[i].name ?: "Counter", i); else if (ctrname[i].name) snprintf(label, sizeof(label), "%s", ctrname[i].name); else label[0] = 0; } if (output_format != FMT_CSV) util_str_tolower(label); ctrname[i].label = util_strdup(label); } } static char *mk_name(int ctr, char *name) { char ctrset[8]; if (!shortname) return util_strdup(name); switch (libcpumf_ctrset(ctr, cfvn, csvn)) { case CPUMF_CTRSET_BASIC: ctrset[0] = 'B'; break; case CPUMF_CTRSET_PROBLEM_STATE: ctrset[0] = 'P'; break; case CPUMF_CTRSET_CRYPTO: ctrset[0] = 'C'; break; case CPUMF_CTRSET_EXTENDED: ctrset[0] = 'E'; break; case CPUMF_CTRSET_MT_DIAG: ctrset[0] = 'M'; break; default: ctrset[0] = 'U'; break; } sprintf(ctrset, "%c%d", ctrset[0], ctr); return util_strdup(ctrset); } static bool read_counternames(void) { struct dirent **namelist = NULL; int i, ctr = 0, count = 0; char *path, *ctrpath; path = util_path_sysfs("/bus/event_source/devices/cpum_cf/events/"); count = util_scandir(&namelist, alphasort, path, "[^.]"); if (count <= 0) { warnx("Cannot open %s", path); free(path); return false; } for (i = 0; i < count && ctr >= 0; i++) { util_asprintf(&ctrpath, "%s/%s", path, namelist[i]->d_name); if (util_file_read_va(ctrpath, "event=%x", &ctr) == 1) ctrname[ctr].name = mk_name(ctr, namelist[i]->d_name); else warnx("Cannot parse %s", ctrpath); free(ctrpath); } util_scandir_free(namelist, count); free(path); return ctr < 0 ? false : true; } static void free_counternames(void) { for (size_t i = 0; i < ARRAY_SIZE(ctrname); ++i) { free(ctrname[i].name); free(ctrname[i].label); free(ctrname[i].ccv); free(ctrname[i].ccvprv); } } static struct check_result { bool cpu_pos; /* CPU Number possible */ bool cpu_req; /* CPU Number requested */ bool cpu_hit; /* CPU Number received */ unsigned char sets_req; /* Counters sets requested */ unsigned char sets_hit; /* Counters sets received */ } *check; static bool check_set(unsigned long a, unsigned long b, unsigned long sets) { if (a > b) return false; for (; a <= b; ++a) { if (a >= max_possible_cpus || !check[a].cpu_pos) return false; check[a].cpu_req = true; check[a].sets_req = sets; } return true; } /* * Functions to parse command line parameters * Convert a number from ascii to int. */ static unsigned long getnumber(char *word, char stopchar) { unsigned long no; char *endp; no = strtoul(word, &endp, 0); if (*endp != stopchar) errx(EXIT_FAILURE, "Invalid parameter %s", word); return no; } /* Read file to get all online CPUs */ static bool get_cpus(char *file, char *buf, size_t bufsz) { char fmt[16]; FILE *slp; int rc; slp = fopen(file, "r"); if (!slp) { warnx("Cannot open %s", file); return false; } snprintf(fmt, sizeof(fmt), "%%%zus", bufsz - 1); rc = fscanf(slp, fmt, buf); fclose(slp); if (rc != 1) warnx("Cannot parse %s", file); return rc == 1 ? true : false; } /* Parse counter set specification */ static unsigned long parse_ctrset(char *cp) { unsigned long x = 0; for (; *cp; ++cp) { switch (tolower(*cp)) { case 'b': x |= S390_HWCTR_BASIC; break; case 'c': x |= S390_HWCTR_CRYPTO; break; case 'e': x |= S390_HWCTR_EXT; break; case 'm': x |= S390_HWCTR_MT_DIAG; break; case 'p': case 'u': x |= S390_HWCTR_USER; break; case 'a': x |= S390_HWCTR_ALL; break; default: errx(EXIT_FAILURE, "Invalid counter set specification '%c'", *cp); } } return x; } static char *show_ctrset(unsigned long set) { static char text[16]; int i = 0; if (set & S390_HWCTR_BASIC) text[i++] = 'B'; if (set & S390_HWCTR_CRYPTO) text[i++] = 'C'; if (set & S390_HWCTR_EXT) text[i++] = 'E'; if (set & S390_HWCTR_MT_DIAG) text[i++] = 'M'; if (set & S390_HWCTR_USER) text[i++] = 'U'; text[i] = '\0'; return text; } /* Parse CPU list and counter sets */ static void parse_cpulist(char *parm, struct s390_hwctr_start *start) { uint64_t *words = start->cpumask; unsigned int i, no_a, no_b; cpu_set_t cpulist; int rc; CPU_ZERO(&cpulist); start->data_bytes = 0; start->counter_sets = S390_HWCTR_ALL; /* Default all counter sets */ if (parm) { /* CPU list with optional counter set */ char *cp = strchr(parm, ':'); if (cp) { /* Handle counter set */ *cp = '\0'; start->counter_sets = parse_ctrset(++cp); } if (strlen(parm) > 0) /* Handle CPU list */ rc = libcpumf_cpuset(parm, &cpulist); else rc = libcpumf_cpuset_fn(S390_CPUS_ONLINE, &cpulist); if (rc) errx(EXIT_FAILURE, "Cannot use CPU list %s", parm); } else { /* No CPU list and no counter sets */ rc = libcpumf_cpuset_fn(S390_CPUS_ONLINE, &cpulist); if (rc) err(EXIT_FAILURE, "Cannot read file " S390_CPUS_ONLINE); } /* Check with authorized counter sets */ if ((start->counter_sets & authorization) != start->counter_sets) { unsigned int noton = ~(start->counter_sets & authorization); start->counter_sets &= authorization; if (!start->counter_sets) errx(EXIT_FAILURE, "No counter sets are authorized"); warnx("One or more counter sets are not authorized: %s", show_ctrset(noton)); } for (rc = 0; rc < CPU_SETSIZE; ++rc) if (CPU_ISSET(rc, &cpulist)) if (!check_set(rc, rc, start->counter_sets)) errx(EXIT_FAILURE, "Invalid CPU %d", rc); /* Convert the CPU list to a bitmask for kernel cpumask_t */ for (i = 0, no_b = 0; i < max_possible_cpus; ++i) { if (check[i].cpu_req) { no_a = i % LONG_BIT; no_b = i / LONG_BIT; words[no_b] |= 1ULL << no_a; } } /* no_b is highest used index */ start->cpumask_len = (no_b + 1) * CHAR_BIT; start->version = S390_HWCTR_START_VERSION; } static bool check_setpossible(void) { char *cp, *parm, *tokens[16]; /* Used to parse command line params */ unsigned long i, no_a, no_b; char cpubuf[1024]; if (!get_cpus(CPUS_KERNELMAX, cpubuf, sizeof(cpubuf))) return false; max_possible_cpus = getnumber(cpubuf, '\0') + 1; check = util_zalloc(max_possible_cpus * sizeof(*check)); if (!get_cpus(CPUS_POSSIBLE, cpubuf, sizeof(cpubuf))) { free(check); return false; } parm = cpubuf; for (i = 0; i < ARRAY_SIZE(tokens) && (tokens[i] = strtok(parm, ",")); ++i, parm = NULL) { cp = strchr(tokens[i], '-'); if (cp) { /* Range */ no_a = getnumber(tokens[i], *cp); no_b = getnumber(++cp, '\0'); } else { no_b = getnumber(tokens[i], '\0'); no_a = no_b; } for (; no_a <= no_b; ++no_a) check[no_a].cpu_pos = true; } return true; } static void safe_strtime(char *dest, size_t size, const char *fmt, const struct tm *tm) { if (!strftime(dest, size, fmt, tm)) dest[0] = 0; } static void generate_timestamp(struct time_formats *date) { time_t now = time(NULL); struct tm *now_tm = localtime(&now); safe_strtime(date->date_time, sizeof(date->date_time), "%F %T%z", now_tm); safe_strtime(date->date, sizeof(date->date), "%F", now_tm); safe_strtime(date->time, sizeof(date->time), "%T", now_tm); safe_strtime(date->epoch, sizeof(date->epoch), "%s", now_tm); } static void output_times(struct time_formats date) { if (output_format == FMT_CSV) { util_fmt_pair(FMT_PERSIST, "Date", "%s", date.date); util_fmt_pair(FMT_PERSIST, "Time", "%s", date.time); } else { util_fmt_pair(FMT_PERSIST | FMT_QUOTE, "date_time", "%s", date.date_time); util_fmt_pair(FMT_PERSIST, "time_epoch", "%s", date.epoch); } } static void prepare_counter(size_t id, unsigned long value) { if (output_format == FMT_CSV) { util_fmt_pair(FMT_PERSIST, ctrname[id].label, ctrformat, value); } else { util_fmt_obj_start(FMT_ROW, NULL); if (strlen(ctrname[id].label)) util_fmt_pair(FMT_PERSIST | FMT_QUOTE, "name", ctrname[id].label); util_fmt_pair(FMT_PERSIST, "id", ctrformat, id); util_fmt_pair(FMT_PERSIST, "value", ctrformat, value); util_fmt_obj_end(); } } static void output_per_cpu(struct time_formats date) { for (unsigned int h = 0; h < max_possible_cpus; ++h) { if (!check[h].cpu_hit) continue; char txt[16]; snprintf(txt, sizeof(txt), "CPU%d", h); util_fmt_obj_start(FMT_ROW, "cpu_%d", h); output_times(date); if (output_format == FMT_CSV) { util_fmt_pair(FMT_PERSIST, "CPU", "CPU%d", h); } else { util_fmt_pair(FMT_PERSIST, "cpu", "%d", h); util_fmt_obj_start(FMT_LIST, "counters"); } for (size_t i = 0; i < ARRAY_SIZE(ctrname); ++i) { if (!ctrname[i].hitcnt) continue; if (hideundef && !ctrname[i].name) continue; prepare_counter(i, ctrname[i].ccv[h]); } if (output_format != FMT_CSV) util_fmt_obj_end(); util_fmt_obj_end(); } } static void output_total(struct time_formats date) { util_fmt_obj_start(FMT_ROW, "total"); output_times(date); if (output_format == FMT_CSV) { util_fmt_pair(FMT_PERSIST, "CPU", "%s", delta && !firstread ? "Delta" : "Total"); } else { util_fmt_pair(FMT_PERSIST | FMT_QUOTE, "cpu", "%s", delta && !firstread ? "delta" : "total"); util_fmt_obj_start(FMT_LIST, "counters"); } for (size_t i = 0; i < ARRAY_SIZE(ctrname); ++i) { if (!ctrname[i].hitcnt) continue; if (hideundef && !ctrname[i].name) continue; prepare_counter(i, ctrname[i].total); ctrname[i].total = 0; ctrname[i].hitcnt = false; } if (output_format != FMT_CSV) util_fmt_obj_end(); util_fmt_obj_end(); } static void show_format(void) { struct time_formats now; generate_timestamp(&now); if (allcpu) output_per_cpu(now); output_total(now); } /* Return Counter set size numbers (in counters) */ static unsigned int ctrset_size(int set) { switch (set) { case S390_HWCTR_BASIC: return 6; case S390_HWCTR_USER: return (cfvn == 1) ? 6 : 2; case S390_HWCTR_CRYPTO: return (csvn <= 5) ? 16 : 20; case S390_HWCTR_EXT: switch (csvn) { case 1: return 32; case 2: return 48; case 3: case 4: case 5: return 128; } return 160; case S390_HWCTR_MT_DIAG: switch (csvn) { case 1: case 2: case 3: return 0; } return 48; } return 0; } /* Return counter set offset numbers */ static int ctrset_offset(int set) { switch (set) { case S390_HWCTR_BASIC: return 0; case S390_HWCTR_USER: return 32; case S390_HWCTR_CRYPTO: return 64; case S390_HWCTR_EXT: return 128; case S390_HWCTR_MT_DIAG: return 448; } return 0; } static bool set_and_size_ok(struct s390_hwctr_setdata *p) { switch (p->set) { case S390_HWCTR_BASIC: case S390_HWCTR_USER: case S390_HWCTR_CRYPTO: case S390_HWCTR_EXT: case S390_HWCTR_MT_DIAG: return p->no_cnts == ctrset_size(p->set); } return false; } static bool add_countervalue(size_t idx, unsigned int cpu, unsigned long value) { if (idx >= ARRAY_SIZE(ctrname)) { warnx("Invalid counter number %zu", idx); return false; } if (cpu >= max_possible_cpus) { warnx("Invalid CPU number %d", cpu); return false; } if (delta) { if (firstread) { ctrname[idx].ccvprv[cpu] = value; ctrname[idx].ccv[cpu] = value; } else { ctrname[idx].ccv[cpu] = value - ctrname[idx].ccvprv[cpu]; ctrname[idx].ccvprv[cpu] = value; value = ctrname[idx].ccv[cpu]; } } else { ctrname[idx].ccv[cpu] = value; } ctrname[idx].total += value; ctrname[idx].hitcnt = true; return true; } static int test_read(struct s390_hwctr_read *read) { void *base = &read->data; size_t offset = 0; /* Clear previous hit counters */ for (unsigned int i = 0; i < max_possible_cpus; ++i) check[i].cpu_hit = false; /* Iterate over all CPUs */ for (unsigned int i = 0; i < read->no_cpus; ++i) { struct s390_hwctr_cpudata *cp = base + offset; check[cp->cpu_nr].cpu_hit = true; check[cp->cpu_nr].sets_hit = 0; offset += sizeof(cp->cpu_nr) + sizeof(cp->no_sets); /* Iterate over all counter sets */ for (unsigned int j = 0; j < cp->no_sets; ++j) { struct s390_hwctr_setdata *sp = base + offset; check[cp->cpu_nr].sets_hit |= sp->set; offset += sizeof(sp->set) + sizeof(sp->no_cnts); if (!set_and_size_ok(sp)) { warnx("CPU %d inconsistent set %d size %d", cp->cpu_nr, sp->set, sp->no_cnts); return -1; } /* Iterate over all counters in each set */ for (unsigned int k = 0; k < sp->no_cnts; ++k) { uint64_t value; void *addr = base + offset; size_t idx = ctrset_offset(sp->set) + k; memcpy(&value, addr, sizeof(value)); offset += sizeof(value); if (!add_countervalue(idx, cp->cpu_nr, value)) return -1; } } } show_format(); firstread = false; return 0; } static int do_open(void) { int fd = open(S390_HWCTR_DEVICE, O_RDWR); if (fd < 0) warn(S390_HWCTR_DEVICE); return fd; } static int do_stop(int ioctlfd) { int rc = ioctl(ioctlfd, S390_HWCTR_STOP, 0); if (rc < 0) warn("ioctl S390_HWCTR_STOP"); return rc; } static int do_start(int ioctlfd, struct s390_hwctr_start *start) { int rc = ioctl(ioctlfd, S390_HWCTR_START, start); if (rc < 0) warn("ioctl S390_HWCTR_START"); return rc; } static int do_read(int ioctlfd) { size_t ioctlbuffer_len = PAGE_SIZE * max_possible_cpus + sizeof(struct s390_hwctr_read); struct s390_hwctr_read *read; int rc; if (!ioctlbuffer) ioctlbuffer = util_malloc(ioctlbuffer_len); read = (struct s390_hwctr_read *)ioctlbuffer; rc = ioctl(ioctlfd, S390_HWCTR_READ, read); if (!rc) rc = test_read(read); else warn("ioctl S390_HWCTR_READ"); return rc; } static void do_sleep(void) { struct timespec req = { .tv_sec = read_interval, .tv_nsec = 0 }; nanosleep(&req, NULL); } /* Execute commands and report first error */ static int do_it(char *s) { struct s390_hwctr_start start; unsigned int flags = FMT_WARN; int ioctlfd; int rc; memset(&start, 0, sizeof(start)); rc = max_possible_cpus / sizeof(uint64_t); start.cpumask = alloca(max_possible_cpus / sizeof(uint64_t)); memset(start.cpumask, 0, rc); parse_cpulist(s, &start); errno = 0; ioctlfd = do_open(); if (ioctlfd < 0) return EXIT_FAILURE; rc = do_start(ioctlfd, &start); if (rc < 0) { close(ioctlfd); return EXIT_FAILURE; } if (output_format == FMT_CSV) flags |= FMT_NOMETA; if (output_format == FMT_JSON || output_format == FMT_JSONSEQ) flags |= FMT_HANDLEINT; if (quote_all) flags |= FMT_QUOTEALL; mk_labels(); util_fmt_init(stdout, output_format, flags, 1); util_fmt_obj_start(FMT_DEFAULT, "lshwc"); if (output_format == FMT_JSON || output_format == FMT_JSONSEQ) { util_fmt_obj_start(FMT_ROW, "cpumcf info"); util_fmt_pair(FMT_PERSIST, "counter first", "%d", cfvn); util_fmt_pair(FMT_PERSIST, "counter second", "%d", csvn); util_fmt_pair(FMT_PERSIST, "authorization", "%d", authorization); util_fmt_obj_end(); } util_fmt_obj_start(FMT_LIST, "measurements"); for (unsigned long i = 0; !rc && i < loop_count; ++i) { rc = do_read(ioctlfd); if (rc) { close(ioctlfd); return EXIT_FAILURE; } if (read_interval && i + 1 < loop_count) do_sleep(); } util_fmt_obj_end(); util_fmt_obj_end(); util_fmt_exit(); rc = do_stop(ioctlfd); close(ioctlfd); return rc ? EXIT_FAILURE : EXIT_SUCCESS; } static struct util_opt opt_vec[] = { UTIL_OPT_SECTION("OPTIONS"), { .option = { "all", no_argument, NULL, 'a' }, .desc = "Displays all CPUs in output" }, { .option = { "loop", required_argument, NULL, 'l' }, .argument = "NUMBER", .desc = "Specifies loop count for next read" }, { .option = { "interval", required_argument, NULL, 'i' }, .argument = "NUMBER", .desc = "Specifies interval between read operations (seconds)" }, { .option = { "short", no_argument, NULL, 's' }, .desc = "Abbreviate counter name with counter set letter and number" }, { .option = { "hex0x", no_argument, NULL, 'X' }, .desc = "Counter values in hexadecimal format with leading 0x" }, { .option = { "hex", no_argument, NULL, 'x' }, .desc = "Counter values in hexadecimal format" }, { .option = { "hide", no_argument, NULL, 'H' }, .desc = "Do not display undefined counters of a counter set" }, { .option = { "delta", no_argument, NULL, 'd' }, .desc = "Display delta counter values" }, { .option = { "timeout", required_argument, NULL, 't' }, .argument = "NUMBER", .desc = "run time in s (seconds) m (minutes) h (hours) and d (days)" }, { .option = { "quote-all", no_argument, NULL, 'q' }, .desc = "Apply quoting to all output elements" }, { .option = { "format", required_argument, NULL, 'f' }, .argument = "FORMAT", .desc = "List counters in specified FORMAT (" FMT_TYPE_NAMES ")" }, UTIL_OPT_HELP, UTIL_OPT_VERSION, UTIL_OPT_END }; static const struct util_prg prg = { .desc = "Read CPU Measurement facility counter sets", .copyright_vec = { { .owner = "IBM Corp.", .pub_first = 2021, .pub_last = 2021, }, UTIL_PRG_COPYRIGHT_END } }; /* Check for hardware support and exit if not available */ static void have_support(void) { struct stat statbuf; if (stat(S390_HWCTR_DEVICE, &statbuf) == -1) errx(EXIT_FAILURE, "No support for CPU Measurement Counter set facility"); } int main(int argc, char **argv) { enum util_fmt_t fmt; unsigned long no; char *slash; int ch; util_prg_init(&prg); util_opt_init(opt_vec, NULL); while ((ch = util_opt_getopt_long(argc, argv)) != -1) { switch (ch) { default: util_opt_print_parse_error(ch, argv); return EXIT_FAILURE; case 'h': util_prg_print_help(); util_opt_print_help(); return EXIT_SUCCESS; case 'v': util_prg_print_version(); return EXIT_SUCCESS; case 'l': errno = 0; loop_count = strtoul(optarg, &slash, 0); if (errno || *slash) errx(EXIT_FAILURE, "Invalid argument for -%c", ch); break; case 'i': errno = 0; read_interval = (unsigned int)strtoul(optarg, &slash, 0); if (errno || *slash) errx(EXIT_FAILURE, "Invalid argument for -%c", ch); break; case 'H': hideundef = true; break; case 's': shortname = true; break; case 'x': ctrformat = "%lx"; break; case 'X': ctrformat = "%#lx"; break; case 'a': allcpu = true; break; case 'd': delta = true; firstread = true; break; case 't': errno = 0; no = strtoul(optarg, &slash, 0); if (errno) errx(EXIT_FAILURE, "Invalid argument for -%c", ch); switch (*slash) { case 's': case '\0': timeout += no; break; case 'm': timeout += no * 60; break; case 'h': timeout += no * 60 * 60; break; case 'd': timeout += no * 60 * 60 * 24; break; default: errx(EXIT_FAILURE, "Invalid argument for -%c", ch); break; } break; case 'q': quote_all = true; break; case 'f': if (!util_fmt_name_to_type(optarg, &fmt)) errx(EXIT_FAILURE, "Supported formats:" FMT_TYPE_NAMES); output_format = fmt; break; } } if (timeout && timeout < read_interval) read_interval = timeout; /* If no timeout specified, simply add zero */ loop_count += timeout / read_interval; have_support(); if (!libcpumf_cpumcf_info(&cfvn, &csvn, &authorization)) return EXIT_FAILURE; if (!check_setpossible()) return EXIT_FAILURE; if (!read_counternames()) { free(check); return EXIT_FAILURE; } for (unsigned int i = 0; i < ARRAY_SIZE(ctrname); ++i) { ctrname[i].ccv = util_zalloc(max_possible_cpus * sizeof(unsigned long)); ctrname[i].ccvprv = util_zalloc(max_possible_cpus * sizeof(unsigned long)); } if (optind >= argc) { ch = do_it(NULL); } else { while (optind < argc) { ch = do_it(argv[optind++]); if (ch) break; } } free_counternames(); free(check); free(ioctlbuffer); return ch; } s390-tools-2.38.0/cpumf/lshwc.h000066400000000000000000000072231502674226300160560ustar00rootroot00000000000000/* Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ /* * CPU Measurement counter facility application for device driver. * * Ioctl system call definitions. */ #ifndef LSHWC_H #define LSHWC_H #include #include enum { S390_HWCTR_BASIC = 0x2, /* BASIC counter set */ S390_HWCTR_USER = 0x4, /* Problem-State Counter Set */ S390_HWCTR_CRYPTO = 0x8, /* Crypto-Activity Counter Set */ S390_HWCTR_EXT = 0x1, /* Extended Counter Set */ S390_HWCTR_MT_DIAG = 0x20, /* MT-diagnostic Counter Set */ S390_HWCTR_ALL = S390_HWCTR_BASIC | S390_HWCTR_USER | S390_HWCTR_CRYPTO | S390_HWCTR_EXT | S390_HWCTR_MT_DIAG }; /* The ioctl(..., S390_HWCTR_READ, ...) is the only subcommand which returns * data. It requires member data_bytes to be positive and indicates the * maximum amount of data available to store counter set data. The other * ioctl() subcommands do not use this member and it should be set to zero. * * The cpuset data is flattened using the following scheme, stored in member * data: * * 0x0 0x8 0xc 0x10 0x14 0x18 0x20 0x28 0xU-1 * +---------+-----+---------+-----+---------+-----+-----+------+------+ * | no_cpus | cpu | no_sets | set | no_cnts | cv1 | cv2 | .... | cv_n | * +---------+-----+---------+-----+---------+-----+-----+------+------+ * * 0xU 0xU+4 0xU+8 0xU+10 0xV-1 * +-----+---------+-----+-----+------+------+ * | set | no_cnts | cv1 | cv2 | .... | cv_n | * +-----+---------+-----+-----+------+------+ * * 0xV 0xV+4 0xV+8 0xV+c * +-----+---------+-----+---------+-----+-----+------+------+ * | cpu | no_sets | set | no_cnts | cv1 | cv2 | .... | cv_n | * +-----+---------+-----+---------+-----+-----+------+------+ * * U and V denote arbitrary hexadezimal addresses. * In fact the first int represents the number of CPUs data was extracted * from. This is followed by CPU number and number of counter sets extracted. * Both are two integer values. This is followed by the set number and number * of counters extracted. Both are two integer values. This is followed by * the counter values, each element is eight bytes in size. */ struct s390_hwctr_start { /* Set CPUs to operate on */ uint64_t version; /* Version of interface */ uint64_t data_bytes; /* # of bytes required */ uint64_t cpumask_len; /* Length of CPU mask in bytes */ uint64_t *cpumask; /* Pointer to CPU mask */ uint64_t counter_sets; /* Bit mask of counter set to get */ }; struct s390_hwctr_setdata { /* Counter set data */ uint32_t set; /* Counter set number */ uint32_t no_cnts; /* # of counters stored in cv[] */ uint64_t cv[0]; /* Counter values (variable length) */ }; struct s390_hwctr_cpudata { /* Counter set data per CPU */ uint32_t cpu_nr; /* Counter set number */ uint32_t no_sets; /* # of counters sets in data[] */ struct s390_hwctr_setdata data[0]; }; struct s390_hwctr_read { /* Structure to get all ctr sets */ uint64_t no_cpus; /* Total # of CPUs data taken from */ struct s390_hwctr_cpudata data[0]; }; #define S390_HWCTR_MAGIC 'C' /* Random magic # for ioctls */ #define S390_HWCTR_START _IOWR(S390_HWCTR_MAGIC, 1, struct s390_hwctr_start) #define S390_HWCTR_STOP _IO(S390_HWCTR_MAGIC, 2) #define S390_HWCTR_READ _IOWR(S390_HWCTR_MAGIC, 3, struct s390_hwctr_read) #define S390_HWCTR_START_VERSION 1 /* Version # s390_hwctr_start */ #define S390_HWCTR_DEVICE "/dev/hwctr" /* Device name */ #endif s390-tools-2.38.0/cpumf/lspai.c000066400000000000000000000237521502674226300160460ustar00rootroot00000000000000/* Copyright IBM Corp. 2023 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ /* List available Processor Assist Instrumentation (PAI) counters. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/util_base.h" #include "lib/util_file.h" #include "lib/util_fmt.h" #include "lib/util_libc.h" #include "lib/util_list.h" #include "lib/util_opt.h" #include "lib/util_path.h" #include "lib/util_prg.h" #include "lib/util_scandir.h" #include "lib/libcpumf.h" #define OPT_FORMAT 256 /* --format XXX option */ static struct util_opt opt_vec[] = { UTIL_OPT_SECTION("OPTIONS"), { .option = { "format", required_argument, NULL, OPT_FORMAT }, .argument = "FORMAT", .flags = UTIL_OPT_FLAG_NOSHORT, .desc = "List counters in specified FORMAT (" FMT_TYPE_NAMES ")" }, { .option = { "numeric", no_argument, NULL, 'n' }, .desc = "Sort PAI counters by counter number" }, { .option = { "type", required_argument, NULL, 't' }, .argument = "TYPE", .desc = "Type of PAI counters to show: crypto, nnpa" }, UTIL_OPT_HELP, UTIL_OPT_VERSION, UTIL_OPT_END }; static const struct util_prg prg = { .desc = "List Processor Assist Information counter sets", .copyright_vec = { { .owner = "IBM Corp.", .pub_first = 2023, .pub_last = 2023, }, UTIL_PRG_COPYRIGHT_END } }; static bool numsort; /* If true sort counter numerically */ static int output_format = -1; /* Generate style if >= 0 */ #define PAI_PATH "/bus/event_source/devices/%s" enum pai_types { /* Bit mask for supported PAI counters */ pai_type_crypto = 0, /* PAI Crypto Counters */ pai_type_nnpa = 1, /* PAI NNPA Counters */ pai_type_max = 2, /* PAI maximum value, must be last */ }; static int pai_types_show; struct pai_ctrname { /* List of defined counters */ char *name; /* Counter name */ unsigned long nr; /* Counter number */ }; struct pai_node { /* Head for PAI counter sets */ struct util_list_node node; /* Successor in PAI counter set list */ enum pai_types type; /* PAI type */ int pmu; /* Assigned PMU type number */ const char *name; /* Counter set name */ char *name_uc; /* Counter set name upper case */ const char *sysfs_name; /* Counter set name in /sysfs tree */ const char *filter_name; /* Counter set name for scandir filter */ struct pai_ctrname *ctrlist; /* List of counter names & numbers */ size_t ctrsize; /* Total size in bytes of ctrlist */ int ctridx; /* Index of last entry used in ctrlist */ unsigned long base; /* Base number for counter set */ }; static struct util_list pai_list; /* Return base of counter set, this is the first counter of this set. */ static unsigned long pai_type_base(enum pai_types t) { switch (t) { case pai_type_crypto: return 0x1000; case pai_type_nnpa: return 0x1800; case pai_type_max: break; } return 0; } /* Test PAI counter name from command line option. */ static const char *pai_type_name(enum pai_types t) { switch (t) { case pai_type_crypto: return "crypto"; case pai_type_nnpa: return "nnpa"; case pai_type_max: break; } return "unknown"; } /* Convert PAI counter type to sysfs directory name. Only validated * input at this time. */ static const char *pai_type_sysfs(enum pai_types t) { if (t == pai_type_crypto) return "pai_crypto"; return "pai_ext"; } /* Convert PAI counter type to sysfs directory name filter for scandir(). */ static const char *pai_type_filter(enum pai_types t) { if (t == pai_type_nnpa) return "^NNPA"; return "[^.]"; /* Matches anything but . and .. in sysfs */ } /* Sort PAI counter names by assigned counter number. */ static int pai_ctrcmp(const void *p1, const void *p2) { struct pai_ctrname *l = (struct pai_ctrname *)p1; struct pai_ctrname *r = (struct pai_ctrname *)p2; return l->nr > r->nr ? 1 : -1; } /* Convert string to upper case. */ static char *str2uc(const char *s) { char *uc = util_strdup(s), *old_uc = uc; for (; *uc; ++uc) *uc = toupper(*uc); return old_uc; } /* Read counter names and assigned event number from sysfs file tree. * Exit when sysfs directory can not be scanned. */ static void read_counternames(struct pai_node *node) { int i, more = 0, ctr = 0, count = 0; struct dirent **namelist = NULL; char *path, *ctrpath; /* Read counter names and assigned event number. */ path = util_path_sysfs(PAI_PATH "/events", node->sysfs_name); count = util_scandir(&namelist, alphasort, path, node->filter_name); if (count <= 0) errx(EXIT_FAILURE, "Cannot open %s", path); node->ctrsize = count * sizeof(*node->ctrlist); node->ctrlist = util_malloc(node->ctrsize); for (i = 0; i < count && ctr >= 0; i++) { util_asprintf(&ctrpath, "%s/%s", path, namelist[i]->d_name); if (util_file_read_va(ctrpath, "event=%x", &ctr) == 1) { node->ctrlist[node->ctridx].name = util_strdup(namelist[i]->d_name); node->ctrlist[node->ctridx++].nr = ctr; more++; } else { warnx("Cannot parse %s", ctrpath); } free(ctrpath); } util_scandir_free(namelist, count); free(path); if (numsort && more > 1) qsort(node->ctrlist, more, sizeof(*node->ctrlist), pai_ctrcmp); } static void show_format(enum util_fmt_t fmt) { struct pai_node *node; util_fmt_init(stdout, fmt, FMT_HANDLEINT, 1); util_fmt_obj_start(FMT_DEFAULT, NULL); util_list_iterate(&pai_list, node) { util_fmt_obj_start(FMT_DEFAULT, "pmu"); util_fmt_pair(FMT_PERSIST, "base", "%d", node->base); util_fmt_pair(FMT_PERSIST, "type", "%d", node->pmu); util_fmt_pair(FMT_QUOTE | FMT_PERSIST, "pmu-name", "%s", node->sysfs_name); util_fmt_obj_start(FMT_LIST, "counters"); for (int i = 0; i < node->ctridx; ++i) { util_fmt_obj_start(FMT_ROW, "counter"); util_fmt_pair(FMT_QUOTE, "name", "%s", node->ctrlist[i].name); util_fmt_pair(FMT_DEFAULT, "config", "%d", node->ctrlist[i].nr); util_fmt_pair(FMT_DEFAULT, "number", "%d", node->ctrlist[i].nr - node->base); util_fmt_obj_end(); } util_fmt_obj_end(); /* Counters */ util_fmt_obj_end(); /* PMU */ } util_fmt_obj_end(); util_fmt_exit(); } static void show_painode(void) { struct pai_node *node; int indent = 0; int offset = 0; if (output_format != -1) { show_format(output_format); return; } util_list_iterate(&pai_list, node) { for (int i = 0; i < node->ctridx; ++i) indent = MAX((size_t)indent, strlen(node->ctrlist[i].name)); } printf("RAW %*s NAME %*s DESCRIPTION\n", 3, "", indent - 5, ""); util_list_iterate(&pai_list, node) { for (int i = 0; i < node->ctridx; ++i) { printf("%d:%ld %s", node->pmu, node->ctrlist[i].nr, node->ctrlist[i].name); offset = indent - strlen(node->ctrlist[i].name) + 1; printf("%*s", offset, ""); printf("Counter %ld / PAI %s counter set\n", node->ctrlist[i].nr - node->base, node->name_uc); } } } /* Release all memory allocated at make_painode(). */ static void free_painode(void) { struct pai_node *next, *node; util_list_iterate_safe(&pai_list, node, next) { free(node->name_uc); for (int i = 0; i < node->ctridx; ++i) free(node->ctrlist[i].name); free(node->ctrlist); free(node); } } static void make_painode(enum pai_types t) { struct pai_node *node = util_zalloc(sizeof(*node)); char *path; node->type = t; node->sysfs_name = pai_type_sysfs(t); node->name = pai_type_name(t); node->name_uc = str2uc(node->name); node->filter_name = pai_type_filter(t); node->base = pai_type_base(t); /* Read PMU type number. */ util_asprintf(&path, PAI_PATH, node->sysfs_name); node->pmu = libcpumf_pmutype(path); if (node->pmu < 0) errx(EXIT_FAILURE, "Cannot open %s", path); free(path); read_counternames(node); util_list_add_tail(&pai_list, node); } static int painode_cmp(void *a, void *b, void *UNUSED(data)) { struct pai_node *n1 = (struct pai_node *)a; struct pai_node *n2 = (struct pai_node *)b; return n1->pmu < n2->pmu ? -1 : 1; } static void sort_painode(void) { util_list_sort(&pai_list, painode_cmp, NULL); } /* Check for hardware support and return false if not available. */ static bool have_support(enum pai_types t) { const char *sysfn = pai_type_sysfs(t); char *path = util_path_sysfs(PAI_PATH, sysfn); bool rc = true; if (!util_path_is_dir(path)) { warnx("No support for PAI %s facility", pai_type_name(t)); rc = false; } free(path); return rc; } /* * Check the argument for option -t. It must be a valid PAI counter set. * Exit when an invalid PAI counter set name has been specified. */ static void check_type_name(const char *type) { bool no_match = true; enum pai_types i; const char *fn; for (i = pai_type_crypto; i < pai_type_max; ++i) { fn = pai_type_name(i); if (!strcasecmp(fn, type)) { pai_types_show |= (1 << i); no_match = false; } } if (no_match) errx(EXIT_FAILURE, "Invalid argument for -t %s", type); } int main(int argc, char **argv) { enum util_fmt_t fmt; int ch; util_list_init(&pai_list, struct pai_node, node); util_prg_init(&prg); util_opt_init(opt_vec, NULL); while ((ch = util_opt_getopt_long(argc, argv)) != -1) { switch (ch) { default: util_opt_print_parse_error(ch, argv); return EXIT_FAILURE; case 'h': util_prg_print_help(); util_opt_print_help(); return EXIT_SUCCESS; case 'v': util_prg_print_version(); return EXIT_SUCCESS; case 'n': numsort = true; break; case 't': check_type_name(optarg); break; case OPT_FORMAT: if (!util_fmt_name_to_type(optarg, &fmt)) errx(EXIT_FAILURE, "Supported formats:" FMT_TYPE_NAMES); output_format = fmt; break; } } /* Nothing specified, show all PAI counters */ if (!pai_types_show) pai_types_show = (1 << pai_type_crypto) | (1 << pai_type_nnpa); /* Check for hardware support */ for (enum pai_types i = pai_type_crypto; i < pai_type_max; ++i) { if ((pai_types_show & (1 << i))) { if (!have_support(i)) pai_types_show &= ~(1 << i); else make_painode(i); } } sort_painode(); show_painode(); free_painode(); return ch; } s390-tools-2.38.0/cpumf/man/000077500000000000000000000000001502674226300153345ustar00rootroot00000000000000s390-tools-2.38.0/cpumf/man/chcpumf.8000066400000000000000000000027471502674226300170640ustar00rootroot00000000000000.\" chcpumf.8 .\" .\" .\" Copyright IBM Corp. 2014, 2017 .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" ---------------------------------------------------------------------- .TH chcpumf "8" "February 2014" "s390-tools" "CPU-MF management programs" . .ds c \fBchcpumf\fP . . .SH NAME chcpumf \- manage the CPU-measurement facilities support . . .SH SYNOPSIS .B chcpumf .RB [ \-m | \-\-min .IR num_sdb ] .RB [ \-x | \-\-max .IR num_sdb ] .RB [ \-V | \-\-verbose ] .br .B chcpumf .BR \-h | \-\-help .br .B chcpumf .BR \-v | \-\-version . . .SH DESCRIPTION The chcpumf program helps you to manage settings for the CPU-measurement facilities for Linux on System z. . . .SH OPTIONS .TP .BR \-m ", " \-\-min " \fInum_sdb\fP" Specifies the minimum sampling facility buffer size in sample-data-blocks (SDB). A sample-data-block consumes about 4 kilobytes. This is the initial buffer size when you start the sampling facility. . .TP .BR \-x ", " \-\-max " \fInum_sdb\fP" Specifies the maximum sampling facility buffer size in sample-data-blocks (SDB). A sample-data-block consumes about 4 kilobytes. This is the maximum size to which the buffer is dynamically adjusted during sampling. . .TP .BR \-V ", " \-\-verbose Displays verbose messages. . .TP .BR \-h ", " \-\-help Displays help information, then exits. . .TP .BR \-v ", " \-\-version Displays version information, then exits. . . .SH "SEE ALSO" .BR lscpumf (1) s390-tools-2.38.0/cpumf/man/lscpumf.8000066400000000000000000000045021502674226300170770ustar00rootroot00000000000000\" lscpumf.8 .\" .\" .\" Copyright IBM Corp. 2014, 2020 .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" ---------------------------------------------------------------------- .TH lscpumf "8" "May 2022" "s390-tools" "CPU-MF management programs" . .ds c \fBlscpumf\fP . . .SH NAME lscpumf \- display information about CPU-measurement facilities . . .SH SYNOPSIS .B lscpumf .RB [ \-i | \-\-info ] .br .B lscpumf .RB \-c | \-\-list\-counters | \-C | \-\-list\-all\-counters .RB [ \-n ] .br .B lscpumf .RB \-s | \-\-list\-sampling\-events .br .B lscpumf .BR \-h | \-\-help .br .B lscpumf .BR \-v | \-\-version . . .SH DESCRIPTION The lscpumf program lists information about CPU-measurement facilities that are supported by Linux on System z. . . .SH OPTIONS .TP .BR \-i ", " \-\-info Displays detailed information about available and supported CPU-measurement facilities. . .TP .BR \-c ", " \-\-list\-counters Lists counters that are provided by the CPU-measurement facility, omitting counters for which the LPAR is not authorized. For counter measurements with the perf program, the raw event identifier is displayed. For Linux version 5.5 and later, the raw event identifier is displayed as :, where type is an integer that the kernel assigns to the CPU Measurement counter facility device driver. For earlier Linux versions the raw event identifier is displayed as r. . .TP .BR \-C ", " \-\-list\-all\-counters Lists all counters that are provided by the CPU-measurement counter facility, regardless of LPAR authorization. To list only those counters for which the LPAR is authorized, use the -c option. For linux version 5.5 and later the raw event identifier is displayed as :, where type is the number the CPU Measurement counter facility device driver was assigned to by the kernel. For earlier linux versions the raw event identifier is displayed as r. . .TP .BR \-n ", " \-\-name . Display the counter name together with the raw event identifier. . .TP .BR \-s ", " \-\-list\-sampling\-events Lists perf raw events that activate the sampling facility. . .TP .BR \-h ", " \-\-help Displays help information, then exits. . .TP .BR \-v ", " \-\-version Displays version information, then exits. . . .SH "SEE ALSO" .BR chcpumf (8) s390-tools-2.38.0/cpumf/man/lshwc.8000066400000000000000000000212551502674226300165520ustar00rootroot00000000000000.\" lshwc.8 .\" .\" .\" Copyright IBM Corp. 2021 .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" ---------------------------------------------------------------------- .ds c \fBlshwc\fP . .TH \*c "8" "Mar 2025" "s390-tools" "CPU-MF management programs" . .SH NAME lshwc \- extract CPU Measurement Facilities counter sets . .SH SYNOPSIS \*c .RB [ \-a ][ \-d ][ \-H ][ \-s ][ \-x ][ \-X ][ \-q ] .RB [ \-l .IR count ] .RB [ \-i .IR interval ] .RB [ \-t .IR time ] .RB [ \-f .IR format ] \fR[\fIcpulist\fR][:\fIsets\fR]\fP .br \*c .BR \-h | \-\-help .br \*c .BR \-v | \-\-version . . .SH DESCRIPTION The \*c command extracts complete counter sets from the CPU Measurement Facilities for Linux on Z. Counter sets can be specified and extracted for individual CPUs. The output is a comma-separated values file. Each line starts with a timestamp and the CPU number, followed by the extracted counter values. . .SH OPTIONS .TP .BR \-h ", " \-\-help Displays help information, then exits. . .TP .BR \-v ", " \-\-version Displays version information, then exits. . .TP .BR \-a ", " \-\-allcpu Displays counter values from each CPU. The default is a total summary line of all counters from all CPUs. . .TP .BR \-d ", " \-\-delta Displays counter values in form of deltas. Each counter value shows the increment to the previous output line. Without this flag the total value of each counter is displayed. See Examples. . .TP .BR \-i ", " \-\-interval \fI\ seconds\fP Specifies a time interval, in seconds, that the command waits between read operations. The default is 60 seconds. . .TP .BR \-l ", " \-\-loop \fI\ count\fP Performs the specified number of read operations. . .TP .BR \-H ", " \-\-hide Do not display values of undefined counters. . .TP .BR \-s ", " \-\-short Displays counter names as short names in the heading. A short name consists of a letter for the counter set followed by a number. For example, this flag displays counter number 0 in the basic counter set as .B B0 instead of .BR Cycles . Valid counter set letters are: .RS .IP B Basic counter set .IP P Problem state counter set .IP C Crypto counter set .IP E Extended counter set .IP M MT-Diagnostic counter set .IP U Undefined counter. .RE . .TP .BR \-t ", " \-\-time Specifies the runtime as an integer in days, hours, minutes or seconds. This option can be specified multiple times: the runtime sums up. Specify time as an integer followed by one of the letters .RS .IP d Specifies the runtime in days. .IP h Specifies the runtime in hours. .IP m Specifies the runtime in minutes. .IP s Specifies the runtime in seconds. This is the default when no letter is given. .RE . .TP .BR \-x ", " \-\-hex Displays counter values as hexadecimal values. . .TP .BR \-X ", " \-\-hex0x Displays counter values as hexadecimal values with a leading 0x prefix. . .TP .BR \-q Apply quoting to every output element, regardless of content or format. . .TP .BR \-f ", " \-\-format \fI\ format\fP Retrieve output in one of the following formats: JSON, CSV, JSON-SEQ or PAIRS. If no format is specified, the output defaults to CSV. . .SS JSON Output Structure The JSON output contains two top-level objects: .B "meta" and .B "lshwc" .TP .B meta General metadata such as API version, host, and timestamp. .TP .B lshwc Measurement data and configuration. .RS .IP \fBcpumcf info\fP CPU Measurement Counter Facility information. .IP \fBmeasurements\fP An array of objects, each representing a CPU or "total".  Each object includes: .RS .IP \fBcpu\fP CPU identifier, such as 0, 1 or "delta" or "total". .IP \fBdate_time\fP Timestamp in ISO 8601 format. .IP \fBtime_epoch\fP The number of seconds since the epoch: 1970-01-01 00:00:00 UTC. .IP \fBcounters\fP An array of counter objects, each containing \fBname\fP (if available), \fBid\fP, and \fBvalue\fP. .RE .RE . .TP \fR[\fIcpulist\fR][:\fIsets\fR]\fP A comma-separated list of CPUs. Each CPU can optionally be followed by characters that specify the counter set. See below for details. . .SS "CPU List and counter-set specification" In the comma-separated list of CPUs, each element is a CPU or a range of CPUs. By default, \*c lists all CPUs. .P The CPU list can be followed by an optional list of characters that specify the counter sets to be extracted, preceded by a colon. The characters can be upper or lower case. By default, all counter sets are used. .IP b Include the basic counter set. .IP c Include the crypto counter set. .IP e Include the extended counter set. .IP m Include the MT_Diagnostic counter set. .IP p|u Include the problem counter set. .IP a Include all known counter sets (default). .SH "EXAMPLES" The first example enables the basic and problem counter sets on CPU 0 and 1. Two read operations are performed and a summary line is printed for each read operation. .sp 1 .nf .ft CW # lshwc -l2 0-1:BP Date,Time,CPU,CPU_CYCLES(0),INSTRUCTIONS(1),L1I_DIR_WRITES(2),L1I_PENALTY_CYCLES(3),L1D_DIR_WRITES(4), L1D_PENALTY_CYCLES(5),PROBLEM_STATE_CPU_CYCLES(32),PROBLEM_STATE_INSTRUCTIONS(33) 2021-04-01,11:50:32,Total,125422,39421,304,13953,454, 97489,0,0 2021-04-01,11:51:32,Total,68074231,16386850,194028,21382384,317227, 104503489,777383,14198 .ft .fi .sp 1 This example shows the counter values of the problem state counter set per CPU. CPU 0 and CPU 1 are selected. .nf .ft CW .sp 1 # lshwc -l3 -a 0-1:P Date,Time,CPU,PROBLEM_STATE_CPU_CYCLES(32),PROBLEM_STATE_INSTRUCTIONS(33) 2021-04-01,11:54:47,CPU0,0,0 2021-04-01,11:54:47,CPU1,0,0 2021-04-01,11:54:47,Total,0,0 2021-04-01,11:55:47,CPU0,818775,14198 2021-04-01,11:55:47,CPU1,125689,1306 2021-04-01,11:55:47,Total,944464,15504 2021-04-01,11:56:47,CPU0,3207071426,1489122591 2021-04-01,11:56:47,CPU1,3225092021,1489278312 2021-04-01,11:56:47,Total,6432163447,2978400903 .ft .fi .sp 1 This example shows the counter values of the basic counter set using delta output format. .nf .ft CW .sp 1 # lshwc -d -l 10 -i 5 -s :b Date,Time,CPU,B0,B1,B2,B3,B4,B5 2025-03-26,10:34:19,Total,208075,117287,1950,50548,1082,49609 2025-03-26,10:34:24,Delta,85800055,70353492,590286,13228290,364034,12945804 2025-03-26,10:34:29,Delta,70654751,60656797,483047,10838672,305703,10570868 2025-03-26,10:34:34,Delta,81043162,69476160,587141,13228161,376662,12868298 2025-03-26,10:34:39,Delta,73434017,62675417,524857,11787256,333966,11543649 2025-03-26,10:34:44,Delta,68367967,58452919,506712,11370740,310785,10589883 2025-03-26,10:34:49,Delta,70351947,57607764,507675,11433377,312433,10676243 2025-03-26,10:34:54,Delta,77154817,65371168,562153,12671030,349750,12311061 2025-03-26,10:34:59,Delta,88871882,75441201,655310,14875963,392530,13773130 2025-03-26,10:35:04,Delta,83763472,71730813,609260,13643680,366992,12672405 .ft .fi .sp 1 This example shows the counter values of the problem state counter set with CPU 3 selected. .nf .ft CW .sp 1 # lshwc -l2 -a 3:P -H --format json { "meta": { "api_level": 1, "version": "2.37.0-build-20250616", "host": "b46lp08.lnxne.boe", "time_epoch": 1750094646, "time": "2025-06-16 19:24:06+0200" }, "lshwc": { "cpumcf info": { "counter first": 3, "counter second": 8, "authorization": 47 }, "measurements": [ { "date_time": "2025-06-16 19:24:06+0200", "time_epoch": 1750094646, "cpu": 3, "counters": [ { "name": "problem_state_cpu_cycles", "id": 32, "value": 0 }, { "name": "problem_state_instructions", "id": 33, "value": 0 } ] }, { "date_time": "2025-06-16 19:24:06+0200", "time_epoch": 1750094646, "cpu": "total", "counters": [ { "name": "problem_state_cpu_cycles", "id": 32, "value": 0 }, { "name": "problem_state_instructions", "id": 33, "value": 0 } ] }, { "date_time": "2025-06-16 19:25:06+0200", "time_epoch": 1750094706, "cpu": 3, "counters": [ { "name": "problem_state_cpu_cycles", "id": 32, "value": 0 }, { "name": "problem_state_instructions", "id": 33, "value": 0 } ] }, { "date_time": "2025-06-16 19:25:06+0200", "time_epoch": 1750094706, "cpu": "total", "counters": [ { "name": "problem_state_cpu_cycles", "id": 32, "value": 0 }, { "name": "problem_state_instructions", "id": 33, "value": 0 } ] } ] } } .ft .fi .SH "SEE ALSO" .BR lscpumf (8) .BR chcpumf (8) s390-tools-2.38.0/cpumf/man/lspai.8000066400000000000000000000043021502674226300165340ustar00rootroot00000000000000.\" lspai.8 .\" .\" .\" Copyright IBM Corp. 2021 .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" ---------------------------------------------------------------------- .ds c \fBlspai\fP . .TH \*c "8" "August 2023" "s390-tools" "CPU-MF management programs" . .SH NAME lspai \- list Processor Activity Instrumentation (PAI) counters . .SH SYNOPSIS \*c .RB [ \-\-format .IR FORMAT ] .RB [ \-n ] .RB [ \-t .IR "\ TYPE" ] .br \*c .BR \-h | \-\-help .br \*c .BR \-v | \-\-version . . .SH DESCRIPTION \*c displays the Processor Activity Instrumentation (PAI) counters for Linux on IBM Z. The output is a human-readable list of available PAI counter names and numbers. .SH OPTIONS .TP .BR \-h ", " \-\-help Displays help information, then exits. . .TP .BR \-v ", " \-\-version Displays version information, then exits. . .TP .BR \-t ", " \-\-type "\ TYPE" Specifies the PAI counter set to list. Valid counter set values are .I crypto and .IR nnpa . By default, the command lists all available PAI counter sets. NNPA refers to the Neural Network Processing Assist facility counter set. Crypto refers to the Cryptografic Processing Assist facility counter set. . .TP .BR \-n ", " \-\-numeric Shows the PAI counter sets sorted by counter number. Default sort order is PAI counter name. . .TP .BI \-\-format "\ FORMAT" Retrieve output in one of the following formats: JSON, csv, json-seq or pairs. If no format is specified, the output defaults to a human-readable format. .SH "EXAMPLE" The \*c invocation lists all PAI Neural Network Processing Assist Facility (NNPA) counters in numeric order: .nf # lspai -t nnpa -n RAW NAME DESCRIPTION 13:6144 NNPA_ALL Counter 0 / PAI NNPA counter set 13:6145 NNPA_ADD Counter 1 / PAI NNPA counter set 13:6146 NNPA_SUB Counter 2 / PAI NNPA counter set 13:6147 NNPA_MUL Counter 3 / PAI NNPA counter set \&... .fi The first column shows the raw event number suitable for .IR perf "(8)" raw event specification. The second column shows the PAI NNPA counter name, suitable for .IR perf "(8)" event specification by name. The third gives a short explanation, if available. .SH "SEE ALSO" .BR pai (8) .BR lscpumf (8) s390-tools-2.38.0/cpumf/man/pai.8000066400000000000000000000167511502674226300162100ustar00rootroot00000000000000.\" pai.8 .\" .\" .\" Copyright IBM Corp. 2022 .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" ---------------------------------------------------------------------- .ds c \fBpai\fP . .TH \*c "8" "May 2022" "s390-tools" "PAI Management Programs" . .SH NAME pai \- record and report Processor Activity Instrumentation (PAI) counters . .SH SYNOPSIS \*c .RB [ \-V ][ \-m | \-\-mapsize .IR size ] .RB [ \-i | \-\-interval .IR ms ] .RB [ \-R | \-\-realtime .IR prio ] .BR \-c | \-\-crypto [ \fIcpulist ][: \fIdata\fR "] [" \fIloops\fP ] .br \*c .RB [ \-V ][ \-m | \-\-mapsize .IR size ] .RB [ \-i | \-\-interval .IR ms ] .RB [ \-R | \-\-realtime .IR prio ] .BR \-n | \-\-nnpa [ \fIcpulist ][: \fIdata\fR "] [" \fIloops\fP ] .br \*c .RB [ \-V ][ \-H | \-\-humantime ][ \-S | \-\-summary "] " \-r | \-\-report " [" \fIfiles\fP ] .br \*c .BR \-h | \-\-help .br \*c .BR \-v | \-\-version . . .SH DESCRIPTION \*c counters tally calls for specific CPU instructions. The \*c command records PAI counters in a ring buffer. \*c can record counter data for all CPUs or for selected CPUs. The main command options are .B \-c for recording cryptographic CPU instructions, .B \-n for recording NNPA CPU instructions and .B \-r for reporting. If all three options are omitted, option .B \-r is assumed and a message is printed. Recording stores data, by CPU, in files .I paicrypto., for option .B \-c or .I painnpa., for option .B \-n where specifies the CPU number with leading zeros. The files are created in the working directory, existing files are overwritten. Reporting evaluates files that are created by recording. .SH OPTIONS .TP .BR \-c ", " \-\-crypto "\fR[\fIcpulist\fR][:\fIdata\fR]" Records data for all (default) or a specified list of CPUs. The CPU list is a comma-separated list of CPU numbers and ranges. In a range, a hyphen separates the first CPU number from the last CPU number. By default \*c lists all CPUs. .RS The optional data specification follows the colon and determines additional collection of data. The specification consists of alphabetic characters that can be upper or lower case: .IP c|C Include task rename system calls .B exec and .BR prctl . .IP f|F Include task creation and deletion system calls .B fork and .BR exit . .IP s|S Include context switch records created by the kernel scheduler. .IP k|K Include only PAI cryptographic counters changed during system call execution. .IP u|U Include only PAI cryptographic counters changed during user space execution. .RE . .TP .BR \-n ", " \-\-nnpa "\fR[\fIcpulist\fR][:\fIdata\fR]" Records data for all (default) or a specified list of CPUs. The CPU list is a comma-separated list of CPU numbers and ranges. In a range, a hyphen separates the first CPU number from the last CPU number. By default \*c lists all CPUs. .RS The optional data specification follows the colon and determines additional collection of data. The specification consists of alphabetic characters that can be upper or lower case: .IP c|C Include task rename system calls .B exec and .BR prctl . .IP f|F Include task creation and deletion system calls .B fork and .BR exit . .IP s|S Include context switch records created by the kernel scheduler. .RE . .TP .BR \-r ", " \-\-report Generates a report from the specified files. Files is a list of blank-separated file names. If no files are specified, uses all files named .I paicrypto., in the working directory, where .I is a CPU number that identifies the CPU for which the data was recorded. . .TP .BR \-h ", " \-\-help Displays help information, then exits. . .TP .BR \-v ", " \-\-version Displays version information, then exits. . .TP .BR \-V ", " \-\-verbose Displays the following information during reporting: the file name and the hexadecimal offset for each sample header. Displays the following information during recording: the read position in the ring buffer, the file name, and the hexadecimal offset for each sample header. . .TP .BR \-H ", " \-\-humantime Changes the time stamp format for sample entries from a hexadecimal number to a human readable . format. The time stamp specifies the elapsed time since Linux was booted. . .TP .BR \-S ", " \-\-summary Prints a summary of counter names and counter numbers with non-zero values. The summary is the last line in the command output. It shows the sum of the counter values of all processed files. . .TP .BR \-i ", " \-\-interval "\ ms" Specifies the waiting time, in milliseconds, between ring buffer read operations during recording. The default is 1000 milliseconds. Argument .B loops is an integer that specifies the number of read operations during recording. The default is 1. . .TP .BR \-m ", " \-\-mapsize "\ size" Specifies the size of the ring buffer that holds the collected data. The value specifies the number of 4 KB pages to be allocated and must be a power of 2. The default size is 512 pages. The ring buffer is created with the .IR mmap (2) system call. . .TP .BR \-R ", " \-\-realtime "\ prio" Collect data using the RT SCHED_FIFO priority specified by .BR prio . Valid values are integers in the range 1 (low) to 99 (high). Use this option when gathering data from multiple CPUs to prevent data loss. . .SH ARGUMENT The command line options determine how command line arguments are interpreted. For option .B \-c to start recording, the argument specifies the number of read operations. If omitted, the default is one. For option .B \-r to start reporting, the argument specifies the file names to be read. If omitted all files in the current directory with file name .I paicryto. are read. .SH "Concurrency with perf tool" The following concurrency restrictions apply for \*c and the .B perf tool. Both tools use the .IR perf_event_open () system call and use the same device driver. The device driver supports limited concurrency: .IP Counting: Counting pai events can run in parallel. .IP Sampling: Only one sampling event pai_crypto/CRYPTO_ALL can be active at any one time. If a sampling event is active, no pai counting event can be active. Both tools stop with an error message if they detect a collision reported by the device driver. .SH "EXAMPLES" This example collects cryptographic counter on CPU 0. The program runs for 10 seconds (10 intervals of 1000 milliseconds). .sp 1 .nf .ft CW # \*c -c0 10 .ft R .fi .sp 1 This example displays the data that is collected in the first example. Each data line shows the time stamp in jiffies, followed by the CPU number, the event number, process and thread identifier (pid/tid) separated by slash (/), and a comma-separated list of counter number, colon (:), and the counter value n hexadecimal notation. .sp 1 .nf .ft CW # \*c -r 0x62a668f2fa 0 event 4096 sample pid 4956/4956 9:0xa7,73:0x8,74:0x18 0x6319c75653 0 event 4096 sample pid 4972/4972 32:0x1 0x6319e2ddee 0 event 4096 sample pid 4972/4972 32:0x1 0x631d3e44f5 0 event 4096 sample pid 4972/4972 32:0x2 0x631d4cfc2e 0 event 4096 sample pid 4972/4972 32:0x3 0x631d529fdd 0 event 4096 sample pid 4972/4972 32:0x2 0x631d84cfa0 0 event 4096 sample pid 4972/4972 73:0x1 0x636e9826bc 0 event 4096 sample pid 4984/4984 9:0xa7,73:0x8,74:0x18 0x636f81a137 0 event 4096 sample pid 4984/4984 9:0x2,74:0x7 0x6378026e54 0 event 4096 sample pid 4984/4984 73:0x2 0x637bcdc8da 0 event 4096 sample pid 4984/4984 73:0x2 0x637bd426cd 0 event 4096 sample pid 4984/4984 73:0x6 0x637c503384 0 event 4096 sample pid 4984/4984 73:0x2 0x64991d83ba 0 event 4096 sample pid 5026/5026 9:0xb0,73:0x8,74:0x18 # .ft R .fi .sp 1 .SH "SEE ALSO" perf(1) s390-tools-2.38.0/cpumf/pai.c000066400000000000000000000664751502674226300155200ustar00rootroot00000000000000/* * pai - Extract CPU Processor Activity Instrumentation (PAI) facility data. * * Copyright IBM Corp. 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/util_base.h" #include "lib/util_file.h" #include "lib/util_libc.h" #include "lib/util_list.h" #include "lib/util_opt.h" #include "lib/util_path.h" #include "lib/util_prg.h" #include "lib/util_scandir.h" #include "lib/libcpumf.h" #include "pai.h" #define S390_EVT_PAI_CRYPTO 0x1000 #define S390_EVT_PAI_NNPA 0x1800 /* Default values for select() timeout: 1 second */ static unsigned long read_interval = 1000; /* Size of mapped perf event ring buffer in 4KB pages. * It must be power of two and >= 4 which is the * absolute minimum required for file descriptors returned by the * perf_event_open system call. Default to 512 pages. */ static unsigned long mapsize = 512; static cpu_set_t cpu_online_mask; static int verbose, humantime; static struct util_list list_pai_event; static struct util_list list_pmu_event; static bool summary; /* System call to perf_event_open(2) */ static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, int group_fd, unsigned long flags) { return syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags); } static void ev_dealloc(void) { struct pai_event *next, *p; util_list_iterate_safe(&list_pai_event, p, next) { util_list_remove(&list_pai_event, p); free(p); } } static void ev_merge(struct pai_event *new) { struct pai_event *p; util_list_iterate(&list_pai_event, p) { if (p->cpu == new->cpu && p->attr.config == new->attr.config) { warnx("dropped duplicate event %#llx for cpu %d", new->attr.config, new->cpu); free(new); return; } } util_list_add_head(&list_pai_event, new); } static void ev_alloc(int enr, int cpu, int flags) { struct pai_event *event = calloc(1, sizeof(*event)); unsigned short as = (S390_EVTATTR_USERSPACE | S390_EVTATTR_KERNELSPACE); if (!event) errx(EXIT_FAILURE, "Not enough memory to allocate event"); if (cpu > CPU_SETSIZE || !CPU_ISSET(cpu, &cpu_online_mask)) errx(EXIT_FAILURE, "Invalid CPU %d specified", cpu); event->file_fd = -1; event->fd = -1; event->flags = flags; event->attr.size = sizeof(event->attr); event->attr.config = enr; switch (enr) { case S390_EVT_PAI_CRYPTO: event->attr.type = libcpumf_pmutype(S390_SYSFS_PAI_CRYPTO); break; case S390_EVT_PAI_NNPA: if ((flags & as)) { warnx("NNPA does not support kernel/user space selector"); flags &= ~as; } event->attr.type = libcpumf_pmutype(S390_SYSFS_PAI_EXT); break; } event->attr.sample_type = PERF_SAMPLE_TID | PERF_SAMPLE_CPU | PERF_SAMPLE_TIME | PERF_SAMPLE_RAW; event->attr.disabled = 1; event->attr.sample_period = 1; event->attr.sample_id_all = 1; event->attr.watermark = 1; /* Wakeup on every event */ event->attr.wakeup_watermark = 1; if (flags & S390_EVTATTR_CTX_SWITCH) event->attr.context_switch = 1; if (flags & S390_EVTATTR_FORK) event->attr.task = 1; if (flags & S390_EVTATTR_COMM) { event->attr.comm = 1; event->attr.comm_exec = 1; } if ((flags & as) != as && enr == S390_EVT_PAI_CRYPTO) { /* User space or kernel space selector */ if (flags & S390_EVTATTR_USERSPACE) event->attr.exclude_kernel = 1; if (flags & S390_EVTATTR_KERNELSPACE) event->attr.exclude_user = 1; } event->cpu = cpu; event->map_size = mapsize; snprintf(event->file_name, sizeof(event->file_name), "pai%s.%03d", enr == S390_EVT_PAI_CRYPTO ? "crypto" : "nnpa", cpu); ev_merge(event); } static void ev_deinstall(void) { struct pai_event *p; util_list_iterate(&list_pai_event, p) { if (p->map_addr) munmap(p->map_addr, PAGE_SIZE + p->map_size * PAGE_SIZE); p->map_addr = NULL; if (p->fd >= 0) close(p->fd); p->fd = -1; if (p->file_fd >= 0) close(p->file_fd); p->file_fd = -1; } } static void ev_enable(void) { struct pai_event *p; int rc; util_list_iterate(&list_pai_event, p) { rc = ioctl(p->fd, PERF_EVENT_IOC_RESET, 0); rc |= ioctl(p->fd, PERF_EVENT_IOC_ENABLE, 0); if (rc) err(EXIT_FAILURE, "ioctl error for enable event %lld CPU %d", p->attr.config, p->cpu); } } static void ev_disable(void) { struct pai_event *p; int rc; util_list_iterate(&list_pai_event, p) { rc = ioctl(p->fd, PERF_EVENT_IOC_DISABLE, 0); if (rc) err(EXIT_FAILURE, "ioctl error for disable event %lld CPU %d", p->attr.config, p->cpu); } } /* Map one event's ring buffer and create an output file for it. */ static void ev_mapevent(struct pai_event *p) { p->map_addr = mmap(NULL, PAGE_SIZE + p->map_size * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, p->fd, 0); if (p->map_addr == MAP_FAILED) err(EXIT_FAILURE, "mmap error for event %lld CPU %d", p->attr.config, p->cpu); p->file_fd = open(p->file_name, O_WRONLY | O_APPEND | O_CREAT | O_TRUNC, 0600); if (p->file_fd < 0) err(EXIT_FAILURE, "file error for event %lld CPU %d", p->attr.config, p->cpu); if (write(p->file_fd, &p->attr, sizeof(p->attr)) == -1) err(EXIT_FAILURE, "write error for event %lld CPU %d", p->attr.config, p->cpu); } /* Install one event using perf_event_open system call. */ static void ev_install(int group) { unsigned long flags = 0; int group_fd = -1, rc; struct pai_event *p; util_list_iterate(&list_pai_event, p) { if (group_fd == -1) { p->attr.watermark = 1; p->attr.wakeup_watermark = PAGE_SIZE * p->map_size / 2; } rc = perf_event_open(&p->attr, -1, p->cpu, group_fd, flags); if (rc == -1) err(EXIT_FAILURE, "perf_event_open error for event %lld CPU %d", p->attr.config, p->cpu); p->fd = rc; if (group && group_fd == -1) { flags = PERF_FLAG_FD_OUTPUT | PERF_FLAG_FD_NO_GROUP; group_fd = rc; ev_mapevent(p); } else if (!group) { ev_mapevent(p); } } } /* Return pointer to event for a given perf event file descriptor returned * by the perf_event_open system call. */ static struct pai_event *perffd_2_event(int fd) { struct pai_event *p; util_list_iterate(&list_pai_event, p) if (p->fd == fd) return p; return NULL; } /* Read the perf event ring buffer and write output to a file. * The file contents is interpreted later after the data collection * phase. */ static int savemap(int fd, void *data, struct data_pos *dp) { unsigned long d_head_old, d_head = dp->data_head; unsigned long d_prev = dp->data_tail; int diff = d_head - d_prev; int wrapped; d_head_old = d_head; if (verbose) { printf("Data head:%#llx tail:%#llx offset:%#llx size:%#llx\n", dp->data_head, dp->data_tail, dp->data_offset, dp->data_size); } if (!diff) return 0; wrapped = d_head / dp->data_size != d_prev / dp->data_size; d_head %= dp->data_size; if (!d_head) { /* Head at buffer end is buffer end */ d_head = dp->data_size; wrapped = 0; } d_prev %= dp->data_size; if (wrapped) { /* Read from d_prev to buffer end */ int part2 = dp->data_size - d_prev; diff -= part2; if (verbose) { printf("Write %d bytes [%ld,%lld)\n", part2, d_prev, dp->data_size); } if (write(fd, data + d_prev, part2) == -1) err(EXIT_FAILURE, "write error for event file"); d_prev = 0; /* Start at position zero */ } if (verbose) printf("Write %d bytes [%ld,%ld)\n", diff, d_prev, d_head); if (write(fd, data + d_prev, diff) == -1) err(EXIT_FAILURE, "write error for event file"); dp->data_tail = d_head_old; /* Write last read position */ return 0; } static void readmap(int fd) { struct pai_event *p = perffd_2_event(fd); struct perf_event_mmap_page *area; if (verbose) { printf("Ring buffer for fd %d %s(%d)\n", fd, p->file_name, p->file_fd); } area = p->map_addr; savemap(p->file_fd, p->map_addr + area->data_offset, (struct data_pos *)&area->data_head); syncfs(p->file_fd); } /* Collect the data in the event ring buffers. Since there might be one * ring buffer per event, sleep some short time and always read all * ring buffer for new contents. */ static int collect(unsigned long cnt) { fd_set r_fds, e_fds, a_fds; struct pai_event *p; struct timeval tv; int rc, max_fd; do { rc = -1; max_fd = -1; tv.tv_sec = read_interval / 1000; tv.tv_usec = (1000 * read_interval) % 1000000; FD_ZERO(&r_fds); FD_ZERO(&e_fds); FD_ZERO(&a_fds); util_list_iterate(&list_pai_event, p) { if (p->attr.watermark) { FD_SET(p->fd, &r_fds); FD_SET(p->fd, &e_fds); FD_SET(p->fd, &a_fds); if (p->fd > max_fd) max_fd = p->fd; } } if (max_fd == -1) break; rc = select(max_fd + 1, &r_fds, NULL, &e_fds, &tv); if (rc >= 0) { if (rc == 0) /* Termination, trigger final read */ r_fds = a_fds; for (int i = 0; i < max_fd + 1; ++i) { if (FD_ISSET(i, &r_fds)) readmap(i); } } } while (rc != -1 && --cnt > 0); return rc; } static void lookup_event(__u64 evtnum, __u16 ctr, __u64 value) { struct pmu_events *p; util_list_iterate(&list_pmu_event, p) { if (p->base == evtnum) { struct event_name *n = p->lst; for (int i = 0; i < p->lstlen; ++n, ++i) { if (p->base + ctr == n->config) { n->total += value; return; } } } } } /* Display the raw data, which is a pair of counter number and values * in the form of counter-nr:value. The first 4 bytes are the length * of the raw-data area. Then follows a key/value pair of 2 bytes key * and 8 bytes value. */ static int evtraw_show(__u64 evtnum, unsigned char *p) { size_t offset = 4, bytes = *(__u32 *)p; __u16 ctr; __u64 value; while (offset < bytes) { ctr = *(__u16 *)(p + offset); offset += sizeof(ctr); value = *(__u64 *)(p + offset); offset += sizeof(value); if (!summary) { printf("%c%hd:%#llx", offset > 14 ? ',' : ' ', ctr, value); } lookup_event(evtnum, ctr, value); if (offset + sizeof(ctr) + sizeof(value) > bytes) break; } return 1; } #define NSEC_PER_SEC 1000000000L static void timestamp(u64 timestamp) { if (humantime) printf("%lld.%09lld ", timestamp / NSEC_PER_SEC, timestamp % NSEC_PER_SEC); else printf("%#llx ", timestamp); } static const char *evt_selector(struct perf_event_attr *pa) { if (pa->exclude_kernel) return ":u"; if (pa->exclude_user) return ":k"; return ""; } static void evt_show(__u64 evtnum, const char *evtsel, struct pai_event_out *ev) { if (summary) { if (ev->type == PERF_RECORD_SAMPLE && ev->raw) evtraw_show(evtnum, ev->raw); return; } timestamp(ev->time); printf("%d ", ev->cpu); switch (ev->type) { case PERF_RECORD_EXIT: case PERF_RECORD_FORK: printf("%s pid %u ppid %u", ev->type == PERF_RECORD_FORK ? "fork" : "exit", ev->u.s_fork.pid, ev->u.s_fork.ppid); break; case PERF_RECORD_COMM: printf("%s %s pid %u/%u", ev->misc ? "exec" : "prctl", ev->u.s_comm.cmd, ev->u.s_comm.pid, ev->u.s_comm.tid); break; case PERF_RECORD_SWITCH: printf("cs-%s", (ev->misc & PERF_RECORD_MISC_SWITCH_OUT) ? "out" : "in"); break; case PERF_RECORD_SWITCH_CPU_WIDE: if (ev->misc & PERF_RECORD_MISC_SWITCH_OUT) { short p = PERF_RECORD_MISC_SWITCH_OUT_PREEMPT; printf("cs-out %c nextpid %u/%u", (ev->misc & p) ? 'P' : '-', ev->u.s_cs.next_prev_pid, ev->u.s_cs.next_prev_tid); } else { printf("cs-in prevpid %u/%u ", ev->u.s_cs.next_prev_pid, ev->u.s_cs.next_prev_tid); if (ev->cs_switch.valid) printf("SWITCH %u/%u->%u/%u", ev->cs_switch.frompid, ev->cs_switch.fromtid, ev->cs_switch.topid, ev->cs_switch.totid); } break; case PERF_RECORD_LOST_SAMPLES: printf("lost %lld\n", ev->u.s_lost.lost); break; case PERF_RECORD_THROTTLE: case PERF_RECORD_UNTHROTTLE: printf("%sthrottle id %lld stream_id %lld\n", ev->type == PERF_RECORD_THROTTLE ? "" : "un", ev->u.s_throttle.id, ev->u.s_throttle.stream_id); break; case PERF_RECORD_SAMPLE: printf("event %lld%s sample pid %u/%u", evtnum, evtsel, ev->u.s_sample.pid, ev->u.s_sample.tid); if (ev->raw) { evtraw_show(evtnum, ev->raw); ev->raw = NULL; } } putchar('\n'); } /* Collect the contents of the event ring buffer data which was saved in * a file during data collection phase. */ static int evt_scan(char *fn, unsigned char *buf, size_t len, struct perf_event_attr *pa) { const char *evtsel = evt_selector(pa); __u64 sample_type = pa->sample_type; int allcnt = 0, cnt = 0, rawok = 0; struct perf_event_header *hdr; size_t offset = sizeof(*pa); __u64 evtnum = pa->config; struct pai_event_out ev; size_t limit; __u32 *ptr32; __u64 *ptr; struct { __u32 pid, tid; unsigned char valid; } last_csout = { 0, 0, 0 }; while (offset < len) { hdr = (struct perf_event_header *)(buf + offset); memset(&ev, 0, sizeof(ev)); if (hdr->size < sizeof(*hdr)) return 1; ++allcnt; if (verbose) printf("[%#08zx] type %d misc %hd size %hx ", offset, hdr->type, hdr->misc, hdr->size); limit = offset + hdr->size; offset += sizeof(*hdr); ev.type = hdr->type; ev.misc = hdr->misc; switch (hdr->type) { case PERF_RECORD_EXIT: case PERF_RECORD_FORK: memcpy(&ev.u, buf + offset, sizeof(ev.u.s_fork)); offset += sizeof(ev.u.s_fork); ev.time = ev.u.s_fork.time; break; case PERF_RECORD_COMM: memcpy(&ev.u, buf + offset, sizeof(ev.u.s_comm)); offset += sizeof(ev.u.s_comm); /* The command name saved by the kernel is either * 8 or 16 bytes in size. If it fits in 8 bytes, the * entry size is eight bytes smaller, and not filled * with terminating null bytes. Adjust offset in this * case. */ if (strlen((const char *)ev.u.s_comm.cmd) < sizeof(__u64)) offset -= sizeof(__u64); break; case PERF_RECORD_SWITCH: break; case PERF_RECORD_SWITCH_CPU_WIDE: memcpy(&ev.u, buf + offset, sizeof(ev.u.s_cs)); offset += sizeof(ev.u.s_cs); if (hdr->misc & PERF_RECORD_MISC_SWITCH_OUT) { last_csout.valid = 1; last_csout.pid = ev.u.s_cs.next_prev_pid; last_csout.tid = ev.u.s_cs.next_prev_tid; } else { ev.cs_switch.valid = last_csout.valid; ev.cs_switch.topid = last_csout.pid; ev.cs_switch.totid = last_csout.tid; ev.cs_switch.frompid = ev.u.s_cs.next_prev_pid; ev.cs_switch.fromtid = ev.u.s_cs.next_prev_tid; last_csout.valid = 0; } break; case PERF_RECORD_THROTTLE: case PERF_RECORD_UNTHROTTLE: memcpy(&ev.u, buf + offset, sizeof(ev.u.s_throttle)); offset += sizeof(ev.u.s_throttle); ev.time = ev.u.s_throttle.time; break; case PERF_RECORD_LOST: memcpy(&ev.u, buf + offset, sizeof(ev.u.s_lost)); offset += sizeof(ev.u.s_lost); break; case PERF_RECORD_SAMPLE: ++cnt; /* Do nothing and collect below */ break; default: printf("unknown header-type %d\n", hdr->type); offset += hdr->size - sizeof(*hdr); goto bypass; } /* Now handle the data returned by samples and the fields * mentioned in sample_id_all members which are appended * to all PERF_RECORDS_xxx * Note: SEQUENCE IS IMPORTANT. */ /* The sample ip is __schedule() no benefit for output */ if (sample_type & PERF_SAMPLE_IP) { ptr = (__u64 *)(buf + offset); offset += sizeof(*ptr); ev.u.s_sample.ip = *ptr; } if (sample_type & PERF_SAMPLE_TID) { ptr = (__u64 *)(buf + offset); offset += sizeof(*ptr); ptr32 = (__u32 *)ptr; ev.u.s_sample.pid = *ptr32; ev.u.s_sample.tid = *(ptr32 + 1); } if (sample_type & PERF_SAMPLE_TIME) { ptr = (__u64 *)(buf + offset); offset += sizeof(*ptr); ev.time = *ptr; } if (sample_type & PERF_SAMPLE_CPU) { ptr = (__u64 *)(buf + offset); offset += sizeof(*ptr); ptr32 = (__u32 *)ptr; ev.cpu = *ptr32; } /* The period is always one, no benefit for output */ if (sample_type & PERF_SAMPLE_PERIOD) { ptr = (__u64 *)(buf + offset); offset += sizeof(*ptr); ev.u.s_sample.period = *ptr; } if (hdr->type == PERF_RECORD_SAMPLE && sample_type & PERF_SAMPLE_RAW) { ptr32 = (__u32 *)(buf + offset); offset += *ptr32 + sizeof(*ptr32); if (*ptr32 > sizeof(*ptr32)) { ev.raw = ptr32; ++rawok; } } evt_show(evtnum, evtsel, &ev); bypass: if (offset != limit) { warnx("%s error at offset:%#zx limit:%#zx", fn, offset, limit); return 1; } } if (verbose) printf("%s records %d samples %d raw-data %d\n", fn, allcnt, cnt, rawok); return 0; } /* Scan event directory and fill event list. */ static int scan_events(struct pmu_events *p) { char *evtname, *evtdir, *path; struct dirent **de_vec; struct event_name *ep; int evtnr, count, rc; path = util_path_sysfs("devices"); rc = util_asprintf(&evtdir, "%s/%s/events", path, p->name); free(path); if (rc == -1) return rc; count = util_scandir(&de_vec, alphasort, evtdir, ".*"); p->lst = calloc(count, sizeof(*p->lst)); if (!p->lst) { rc = -1; goto out; } p->lstlen = 0; ep = p->lst; for (int i = 0; i < count; i++) { if (de_vec[i]->d_type == DT_DIR) continue; ep->name = util_strdup(de_vec[i]->d_name); util_asprintf(&evtname, "%s/%s", evtdir, de_vec[i]->d_name); rc = util_file_read_va(evtname, "event=%x", &evtnr); free(evtname); if (rc != 1) { for (ep = p->lst, rc = 0; rc < p->lstlen; ++rc, ++ep) free(ep->name); free(p->lst); p->lst = NULL; rc = -1; goto out; } ep->config = evtnr; if (p->base > ep->config) p->base = ep->config; ++p->lstlen; ++ep; } rc = 0; out: util_scandir_free(de_vec, count); free(evtdir); return rc; } /* Scan all event names of PMU type. */ static int add_events(int type) { struct pmu_events *p; char *pmuname; int rc; rc = libcpumf_pmuname(type, &pmuname); if (rc) { warnx("PMU type %d not found", type); return rc; } p = malloc(sizeof(*p)); if (p) { p->type = type; p->name = pmuname; p->base = ~0UL; p->lst = NULL; rc = scan_events(p); if (rc) free(p); else util_list_add_head(&list_pmu_event, p); } else { rc = -1; } if (rc) { free(pmuname); warnx("failed building event list for %s", pmuname); } return rc; } /* Check event list for events of PMU type. If it does not exist, build it * and add it to the list all of PMU names. */ static void build_events(int type) { struct pmu_events *p; util_list_iterate(&list_pmu_event, p) if (p->type == type) /* PMU already scanned */ return; /* PMU list not yet scanned read event names */ add_events(type); } /* Show all events with a total number of non-zero. */ static void show_events(void) { struct pmu_events *p; bool header = false; util_list_iterate(&list_pmu_event, p) { int i = 0; for (struct event_name *n = p->lst; i < p->lstlen; ++i, ++n) { if (n->total) { if (!header) { printf("Summary\n"); header = true; } printf("PMU %s event %s nr %lld total %lld\n", p->name, n->name, n->config - p->base, n->total); } } } } /* Free all memory allocated for event summary. */ static void remove_events(void) { struct pmu_events *next, *p; util_list_iterate_safe(&list_pmu_event, p, next) { int i = 0; for (struct event_name *n = p->lst; i < p->lstlen; ++i, ++n) free(n->name); free(p->name); free(p->lst); free(p); } } /* Scan one file which contains event ring buffer output. Print out the * entries to stdout. */ static int map_check(char *fn, int (*fct)(char *, unsigned char *, size_t, struct perf_event_attr *)) { struct perf_event_attr pa; unsigned char *p; struct stat sb; int rc = 1, fd; fd = open(fn, O_RDONLY); if (fd == -1) { warnx("open() failed for %s", fn); return rc; } if (fstat(fd, &sb) == -1) { warnx("stat() failed for %s", fn); close(fd); return rc; } if (verbose) printf("%s size:%zu\n", fn, sb.st_size); if (!S_ISREG(sb.st_mode)) { warn("%s is not a file", fn); close(fd); return rc; } if (sb.st_size < (long)sizeof(pa)) { /* Event grouped --> empty file */ close(fd); unlink(fn); return 0; } p = mmap(0, sb.st_size, PROT_READ, MAP_SHARED, fd, 0); if (p == MAP_FAILED) { warnx("%s cannot map file", fn); close(fd); return rc; } memcpy(&pa, p, sizeof(pa)); if (close(fd) == -1) { warnx("close() failed for %s", fn); return rc; } build_events(pa.type); rc = fct(fn, p, sb.st_size, &pa); munmap(p, sb.st_size); return rc; } /* Parse event attribute specification */ static int parse_event_attr(char *cp) { int x = 0; for (; *cp; ++cp) { switch (tolower(*cp)) { case 's': x |= S390_EVTATTR_CTX_SWITCH; break; case 'c': x |= S390_EVTATTR_COMM; break; case 'f': x |= S390_EVTATTR_FORK; break; case 'u': x |= S390_EVTATTR_USERSPACE; break; case 'k': x |= S390_EVTATTR_KERNELSPACE; break; default: errx(EXIT_FAILURE, "Invalid event specification '%c'", *cp); } } return x; } /* Parse CPU list and event specifications */ static void parse_cpulist(int enr, const char *parm) { unsigned int evt_attr = 0; cpu_set_t cmdlist, result; char *cp; int rc; CPU_ZERO(&cmdlist); if (parm) { /* CPU list with optional event attribute */ cp = strchr(parm, ':'); if (cp) { /* Handle event specification */ *cp = '\0'; evt_attr = parse_event_attr(++cp); } if (strlen(parm) > 0) { CPU_ZERO(&result); rc = libcpumf_cpuset(parm, &cmdlist); if (rc) errx(EXIT_FAILURE, "Cannot use CPU list %s", parm); CPU_AND(&result, &cmdlist, &cpu_online_mask); } else { CPU_OR(&result, &cmdlist, &cpu_online_mask); } } else { CPU_OR(&result, &cmdlist, &cpu_online_mask); evt_attr = S390_EVTATTR_CTX_SWITCH | S390_EVTATTR_COMM | S390_EVTATTR_FORK; } for (rc = 0; rc < CPU_SETSIZE; ++rc) { if (CPU_ISSET(rc, &result)) ev_alloc(enr, rc, evt_attr); } for (rc = 0; rc < CPU_SETSIZE; ++rc) { if (CPU_ISSET(rc, &cmdlist) && !CPU_ISSET(rc, &cpu_online_mask)) warnx("CPU %d not online, event dropped", rc); } } static struct util_opt opt_vec[] = { UTIL_OPT_SECTION("OPTIONS"), { .option = { "crypto", optional_argument, NULL, 'c' }, .argument = "CPULIST[:DATA]", .desc = "Collect PAI crypto counters" }, { .option = { "nnpa", optional_argument, NULL, 'n' }, .argument = "CPULIST[:DATA]", .desc = "Collect PAI nnpa counters" }, { .option = { "mapsize", required_argument, NULL, 'm' }, .argument = "SIZE", .desc = "Specifies number of 4KB pages for event ring buffer" }, { .option = { "report", no_argument, NULL, 'r' }, .desc = "Report file contents" }, { .option = { "realtime", required_argument, NULL, 'R' }, .argument = "PRIO", .desc = "Collect data with this RT SCHED_FIFO priority" }, { .option = { "interval", required_argument, NULL, 'i' }, .argument = "NUMBER", .desc = "Specifies interval between read operations in milliseconds" }, { .option = { "verbose", no_argument, NULL, 'V' }, .desc = "Verbose output" }, { .option = { "humantime", no_argument, NULL, 'H' }, .desc = "Human readable timestamp in seconds.nanoseconds" }, { .option = { "summary", no_argument, NULL, 'S' }, .desc = "Print summary of all non-zero counter values" }, UTIL_OPT_HELP, UTIL_OPT_VERSION, UTIL_OPT_END }; static const struct util_prg prg = { .desc = "Record and report Processor Activity Instrumentation Facility Counters.", .copyright_vec = { { .owner = "IBM Corp.", .pub_first = 2022, .pub_last = 2022, }, UTIL_PRG_COPYRIGHT_END } }; static void record_cpus_crypto(const char *cp) { if (!libcpumf_have_pai_crypto()) errx(EXIT_FAILURE, "No support for PAI crypto counters"); parse_cpulist(S390_EVT_PAI_CRYPTO, cp); } static void record_cpus_nnpa(const char *cp) { if (!libcpumf_have_pai_nnpa()) errx(EXIT_FAILURE, "No support for PAI nnpa counters"); parse_cpulist(S390_EVT_PAI_NNPA, cp); } /* Mapsize must be power of 2 and larger than 4. Count bits in n and * return 0 if input is invalid and has a bit count larger than one. */ static unsigned long check_mapsize(unsigned long n) { int bit, cnt = 0; if (n < 4) return 0; for (bit = 0; bit < __BITS_PER_LONG; ++bit) if (n & (1 << bit)) ++cnt; return cnt == 1 ? n : 0; } static void setprio(const char *prio) { struct sched_param param; char *endstr; memset(¶m, 0, sizeof(param)); param.sched_priority = strtoul(prio, &endstr, 0); if (*endstr) errno = EINVAL; if (*endstr || sched_setscheduler(0, SCHED_FIFO, ¶m)) err(EXIT_FAILURE, "Could not set realtime priority"); } int main(int argc, char **argv) { bool crypto_record = false, report = false; bool nnpa_record = false; unsigned long loop_count = 1; int ch, group = 0; char *slash; util_list_init(&list_pai_event, struct pai_event, node); util_list_init(&list_pmu_event, struct pmu_events, node); util_prg_init(&prg); util_opt_init(opt_vec, NULL); /* Read currently online CPUs and create a bit mask. * This bitmap of online CPUs is used to check command line parameter * for valid CPUs */ ch = libcpumf_cpuset_fn(S390_CPUS_ONLINE, &cpu_online_mask); if (ch) err(EXIT_FAILURE, "Cannot read file " S390_CPUS_ONLINE); while ((ch = util_opt_getopt_long(argc, argv)) != -1) { switch (ch) { default: util_opt_print_parse_error(ch, argv); return EXIT_FAILURE; case 'h': util_prg_print_help(); util_opt_print_help(); return EXIT_SUCCESS; case 'v': util_prg_print_version(); return EXIT_SUCCESS; case 'c': record_cpus_crypto(optarg); crypto_record = true; break; case 'i': errno = 0; read_interval = (unsigned int)strtoul(optarg, &slash, 0); if (errno || !read_interval || *slash) errx(EXIT_FAILURE, "Invalid argument for -%c", ch); break; case 'm': errno = 0; mapsize = strtoul(optarg, &slash, 0); mapsize = check_mapsize(mapsize); if (errno || !mapsize || *slash) errx(EXIT_FAILURE, "Invalid argument for -%c", ch); break; case 'n': record_cpus_nnpa(optarg); nnpa_record = true; break; case 'R': setprio(optarg); break; case 'r': report = true; break; case 'S': summary = true; break; case 'H': humantime = 1; break; case 'V': ++verbose; break; } } /* Without options do report on all files */ if (!crypto_record && !nnpa_record && !report) { warnx("No action specified assume report"); report = true; } if (crypto_record || nnpa_record) { /* In record mode command line parameter is run-time */ if (optind < argc) { errno = 0; loop_count = strtoul(argv[optind], &slash, 0); if (errno || !loop_count || *slash) errx(EXIT_FAILURE, "Invalid argument for runtime"); } ev_install(group); ev_enable(); ch = collect(loop_count); ev_disable(); ev_deinstall(); ev_dealloc(); return ch < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } /* Must be reporting */ ch = 0; if (optind < argc) { /* Report mode command line has files */ for (; optind < argc; ++optind) ch += map_check(argv[optind], evt_scan); } else { /* Scan files in local directory */ struct dirent **de_vec; int count = util_scandir(&de_vec, alphasort, ".", "pai(crypto|nnpa).[0-9]+"); for (int i = 0; i < count; i++) if (de_vec[i]->d_type == DT_REG) ch += map_check(de_vec[i]->d_name, evt_scan); util_scandir_free(de_vec, count); } if (summary && report) { show_events(); remove_events(); } return ch ? EXIT_FAILURE : EXIT_SUCCESS; } s390-tools-2.38.0/cpumf/pai.h000066400000000000000000000056021502674226300155060ustar00rootroot00000000000000/* * pai - Extract CPU Processor Activity Instrumentation (PAI) facility data. * * Copyright IBM Corp. 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef PAI_H #define PAI_H #define S390_EVT_PAI_CRYPTO 0x1000 enum { /* Event attribute specifications */ S390_EVTATTR_CTX_SWITCH = 1, S390_EVTATTR_COMM = 2, S390_EVTATTR_FORK = 4, S390_EVTATTR_USERSPACE = 8, S390_EVTATTR_KERNELSPACE = 16 }; struct pai_event { struct util_list_node node; /* List node */ struct perf_event_attr attr; /* Perf_event_open(2) attr */ int fd; /* Perf event file descriptor */ void *map_addr; /* Sampling data mapping address */ size_t map_size; /* Sampling size mapping size */ char file_name[16]; /* File name of sampled data */ int file_fd; /* Map data output file descriptor */ int cpu; /* Perf_event_open(2) CPU */ unsigned long flags; /* Perf_event_open(2) flags */ }; struct pai_event_out { /* Output for CRYPTO_ALL event */ __u32 type; /* Header type, see PERF_RECORD_xxx */ __u16 misc; /* Header misc, value depends on type */ __u64 time; /* Time stamp valid for all entries */ __u32 cpu; /* CPU number valid for all entries */ union { /* Fields from PERF_RECORD_FORK|EXIT */ struct { __u32 pid, ppid; __u32 tid, ptid; __u64 time; } s_fork; /* Fields from PERF_RECORD_COMM */ struct { __u32 pid, tid; __u8 cmd[16]; } s_comm; /* Fields from PERF_RECORD_SWITCH_CPU_WIDE */ struct { __u32 next_prev_pid, next_prev_tid; } s_cs; /* Fields from PERF_RECORD_LOST_SAMPLES */ struct { __u64 lost; } s_lost; /* Relevant fields from PERF_RECORD_SAMPLE, time and cpu * are stored above */ struct { __u64 ip; __u64 period; __u32 pid, tid; } s_sample; /* Fields from PERF_RECORD_[UN]THROTTLE */ struct { __u64 time; __u64 id; __u64 stream_id; } s_throttle; } u; /* Information on last context switch out */ struct cs_switch { unsigned char valid; __u32 topid, totid; __u32 frompid, fromtid; } cs_switch; void *raw; /* Pointer to key/value array for crypto counters */ }; struct data_pos { /* Perf event mapped ring buffer */ __u64 data_head; /* Head in the data section */ __u64 data_tail; /* User-space written tail */ __u64 data_offset; /* Where the buffer starts */ __u64 data_size; /* Data buffer size */ }; struct event_name { /* Event list for number to name xlate */ char *name; /* Event name */ __u64 config; /* Event config value */ __u64 total; /* Total counter value */ }; struct pmu_events { /* Event list for PMU number to name xlate */ struct util_list_node node; /* List node */ char *name; /* PMU name */ int type; /* PMU type */ int lstlen; /* # of entries in lst */ unsigned long base; /* Base event number */ struct event_name *lst; /* List of event names */ }; #endif /* PAI_H */ s390-tools-2.38.0/cpuplugd/000077500000000000000000000000001502674226300152725ustar00rootroot00000000000000s390-tools-2.38.0/cpuplugd/Makefile000066400000000000000000000010711502674226300167310ustar00rootroot00000000000000include ../common.mak all: cpuplugd LDLIBS += -lm libs = $(rootdir)/libutil/libutil.a OBJECTS = daemon.o cpu.o info.o terms.o config.o main.o getopt.o mem.o cpuplugd: $(OBJECTS) $(libs) $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ clean: rm -f cpuplugd $(OBJECTS) install: all $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 cpuplugd \ $(DESTDIR)$(USRSBINDIR) $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 man/cpuplugd.8 \ $(DESTDIR)$(MANDIR)/man8 $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 man/cpuplugd.conf.5 \ $(DESTDIR)$(MANDIR)/man5 .PHONY: all install clean s390-tools-2.38.0/cpuplugd/config.c000066400000000000000000000231001502674226300166770ustar00rootroot00000000000000/* * cpuplugd - Linux for System z Hotplug Daemon * * Config file parsing * * Copyright IBM Corp. 2007, 2018 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include "cpuplugd.h" /* * Return the value of a variable which parse_config() found within the * configuration file. Use only for values valid >= 0, because -1 is returned * in error case. */ static long parse_positive_value(char *ptr) { long value = 0; unsigned int i; if (ptr == NULL) return -1; for (i = 0; i < strlen(ptr); i++) { if (isdigit(ptr[i]) == 0) return -1; } sscanf(ptr, "%ld", &value); return value; } char *get_var_rvalue(char *var_name) { char tmp_name[MAX_VARNAME + 3]; /* +3 for '\0', '=' and '\n' */ unsigned int tmp_length; char *rvalue; tmp_name[0] = '\n'; strncpy(&tmp_name[1], var_name, MAX_VARNAME + 1); /* +1 for '\0' */ tmp_length = strlen(tmp_name); tmp_name[tmp_length] = '='; tmp_name[tmp_length + 1] = '\0'; rvalue = strstr(varinfo, tmp_name); if (rvalue == NULL) return NULL; rvalue += strlen(tmp_name); return rvalue; } static void add_var(char *name, char *rvalue) { size_t offset, size; unsigned int i; if (get_var_rvalue(name)) cpuplugd_exit("Variable defined twice: %s\n", name); for (i = 0; i < sym_names_count; i++) { if (strncmp(name, sym_names[i].name, MAX(strlen(sym_names[i].name), strlen(name))) != 0) continue; cpuplugd_exit("Cannot use (pre-defined) variable name: %s\n", name); } offset = strlen(varinfo); /* +3 because of extra '=', '\n' and '\0' */ size = offset + strlen(name) + strlen(rvalue) + 3; if (size > varinfo_size) //TODO realloc? cpuplugd_exit("buffer for variables too small: need %ld, " "have %ld (bytes)\n", size, varinfo_size); size -= offset; snprintf(&varinfo[offset], size, "%s=%s\n", name, rvalue); return; } static int check_term(char *symbol, char *name, char *rvalue, struct term **term) { if (!strncasecmp(name, symbol, strlen(symbol))) { cpuplugd_debug("found the following rule: %s = %s\n", name, rvalue); *term = parse_term(&rvalue, OP_PRIO_NONE); if (rvalue[0] == '\0') return 1; cpuplugd_exit("parsing error at %s, position: %s\n", symbol, rvalue); } return 0; } static int check_value(char *symbol, char *name, char *rvalue, long *value) { if (!strncasecmp(name, symbol, strlen(symbol))) { *value = parse_positive_value(rvalue); cpuplugd_debug("found %s value: %ld\n", symbol, *value); if (*value >= 0) return 1; cpuplugd_exit("parsing error at update\n"); } return 0; } /* * Parse a single line of the configuration file */ static void parse_configline(char *line) { char *match, *name, *rvalue, *start, *stop; int i, j; size_t len; char temp[strlen(line) + 1]; if (line[0] == '#') return; for (i = j = 0; line[i] != 0; i++) /* Remove whitespace. */ if (!isblank(line[i]) && !isspace(line[i])) temp[j++] = line[i]; temp[j] = '\0'; match = strchr(temp, '='); if (match == NULL) return; *match = '\0'; /* Separate name and right hand value */ name = temp; /* left side of = */ rvalue = match + 1; /* right side of = */ /* * remove the double quotes * example: CPU_MIN="2" */ start = strchr(rvalue, '"'); /* points to first " */ stop = strrchr(rvalue, '"'); /* points to last " */ len = stop - start; if (start != NULL && stop != NULL && len > 0) { rvalue[len] = '\0'; rvalue = rvalue + 1; } else cpuplugd_exit("the configuration file has syntax " "errors at %s, position: %s\n", name, rvalue); if (check_term("hotplug", name, rvalue, &cfg.hotplug)) return; if (check_term("hotunplug", name, rvalue, &cfg.hotunplug)) return; if (check_term("memplug", name, rvalue, &cfg.memplug)) return; if (check_term("memunplug", name, rvalue, &cfg.memunplug)) return; if (check_term("cmm_inc", name, rvalue, &cfg.cmm_inc)) return; if (check_term("cmm_dec", name, rvalue, &cfg.cmm_dec)) return; if (check_value("update", name, rvalue, &cfg.update)) { if (cfg.update > 0) return; cpuplugd_exit("update must be > 0\n"); } if (check_value("cpu_min", name, rvalue, &cfg.cpu_min)) { if (cfg.cpu_min > 0) return; cpuplugd_exit("cpu_min must be > 0\n"); } if (check_value("cpu_max", name, rvalue, &cfg.cpu_max)) { if (cfg.cpu_max == 0) /* if cpu_max is 0, we use the overall number of cpus */ cfg.cpu_max = get_numcpus(); return; } if (check_value("cmm_min", name, rvalue, &cfg.cmm_min)) return; if (check_value("cmm_max", name, rvalue, &cfg.cmm_max)) return; cpuplugd_debug("found the following variable: %s = %s\n", name, rvalue); if (strlen(name) > MAX_VARNAME) cpuplugd_exit("Variable name too long (max. length is " "%i chars): %s\n", MAX_VARNAME, name); add_var(name, rvalue); } /* * Function used to parse the min and max values at the beginning of the * configuration file as well as the hotplug and hotunplug rules. */ void parse_configfile(char *file) { char linebuffer[MAX_LINESIZE + 2]; /* current line incl. \n and \0 */ char *linep_offset; FILE *filp; filp = fopen(file, "r"); if (!filp) cpuplugd_exit("Opening configuration file failed: %s\n", strerror(errno)); while (fgets(linebuffer, sizeof(linebuffer), filp) != NULL) { if (!(linep_offset = strchr(linebuffer, '\n'))) cpuplugd_exit("Line is too long (max. length is %i " "characters): %s\n", MAX_LINESIZE, linebuffer); parse_configline(linebuffer); } fclose(filp); } void apply_cpu_config(void) { /* * Check that the initial number of cpus is not below the * minimum */ if (num_cpu_start < cfg.cpu_min && get_numcpus() >= cfg.cpu_min) { cpuplugd_debug("The number of online cpus is below "); cpuplugd_debug("the minimum and will be increased.\n"); while (get_num_online_cpus() < cfg.cpu_min) { if (hotplug_one_cpu()) break; } } if (get_num_online_cpus() > cfg.cpu_max) { cpuplugd_debug("The number of online cpus is above the maximum"); cpuplugd_debug(" and will be decreased.\n"); while (get_num_online_cpus() > cfg.cpu_max) { if (hotunplug_one_cpu()) break; } } if (cfg.cpu_min > get_numcpus()) { /* * This check only works if nobody used the * additional_cpus in the boot parameter section */ cpuplugd_debug("The minimum amount of cpus is above the "); cpuplugd_debug("number of available cpus.\n"); cpuplugd_exit("Detected %d available cpus\n", get_numcpus()); } if (get_num_online_cpus() < cfg.cpu_min) { cpuplugd_debug("Failed to set the number of online cpus to "); cpuplugd_debug("the minimum. "); cpuplugd_exit("Aborting.\n"); } } /* * Check if the required settings are found in the configuration file. * "Autodetect" if cpu and/or memory hotplug configuration entries * where specified */ void check_config() { int lpar_status; lpar_status = check_lpar(); if (cfg.update < 0) cpuplugd_exit("No valid update interval specified.\n"); if (cfg.cpu_max < cfg.cpu_min && cfg.cpu_max != 0) cpuplugd_exit("cpu_max below cpu_min, aborting.\n"); if (cfg.cpu_max < 0 || cfg.cpu_min < 0 || cfg.hotplug == NULL || cfg.hotunplug == NULL) { cpuplugd_error("No valid CPU hotplug configuration " "detected.\n"); cpu = 0; } else { cpu = 1; cpuplugd_debug("Valid CPU hotplug configuration detected.\n"); } if (cfg.cmm_max < 0 || cfg.cmm_min < 0 || cfg.memplug == NULL || cfg.memunplug == NULL || cfg.cmm_inc == NULL || cfg.cmm_max < cfg.cmm_min) { cpuplugd_error("No valid memory hotplug configuration " "detected.\n"); memory = 0; } else { memory = 1; /* * check if all the necessary files exit below /proc */ if (check_cmmfiles() != 0 && lpar_status == 0) { cpuplugd_info("Can not open /proc/sys/vm/cmm_pages. " "The memory hotplug function will be " "disabled.\n"); memory = 0; } if (memory == 1 && lpar_status == 0) cpuplugd_debug("Valid memory hotplug configuration " "detected.\n"); if (memory == 1 && lpar_status == 1) { cpuplugd_debug("Valid memory hotplug configuration " "detected inside LPAR. " "The memory hotplug function will be " "disabled. \n"); memory = 0; } } if (memory == 0 && cpu == 0) cpuplugd_exit("Exiting, because neither a valid cpu nor a val" "id memory hotplug configuration was found.\n"); /* * Save the number of online cpus and the cmm_pagesize at startup, * so that we can enable exactly the same amount when the daemon ends * * Don't adjust cpus if system is on vertical polarization */ saved_polarization = get_polarization(); num_cpu_start = get_num_online_cpus(); cpuplugd_debug("Daemon started with %d active cpus.\n", num_cpu_start); if (saved_polarization < 0) { cpuplugd_debug("Daemon couldn't determine system polarization\n"); cpuplugd_debug("Starting without evaluating cpu rules\n"); } else if (saved_polarization == PLR_VERTICAL) { cpuplugd_debug("Daemon started with vertical polarization.\n"); cpuplugd_debug("Cpu adjustments won't be made until system "); cpuplugd_debug("is in horizontal polarization\n"); } else if (cpu == 1) { apply_cpu_config(); } if (memory == 1) { /* * Check that the initial value of cmm_pages is not below * cmm_min or above cmm_max */ cmm_pagesize_start = get_cmmpages_size(); if (cmm_pagesize_start < cfg.cmm_min) { cpuplugd_debug("cmm_pages is below minimum and will " "be increased.\n"); set_cmm_pages(cfg.cmm_min); } if (cmm_pagesize_start > cfg.cmm_max) { cpuplugd_debug("cmm_pages is above the maximum and will" " be decreased.\n"); set_cmm_pages(cfg.cmm_max); } } } s390-tools-2.38.0/cpuplugd/cpu.c000066400000000000000000000142161502674226300162310ustar00rootroot00000000000000/* * cpuplugd - Linux for System z Hotplug Daemon * * CPU hotplug functions * * Copyright IBM Corp. 2007, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include "cpuplugd.h" #define NUM_BASE (10) #define CPU_OFFLINE (0) #define CPU_ONLINE (1) #define CPU_DECONFIGURED (0) #define CPU_CONFIGURED (1) #define CPU_LIST_LEN (4096) static int get_sysfs_attribute_cpu_count(char *path) { char cpu_list[CPU_LIST_LEN]; int number, start, end; char *sub_list; if (util_file_read_line(cpu_list, sizeof(cpu_list), path)) cpuplugd_exit("Cannot open %s file: %s\n", path, strerror(errno)); number = 0; sub_list = strtok(cpu_list, ","); while (sub_list) { if (strchr(sub_list, '-')) { if (sscanf(sub_list, "%d-%d", &start, &end) != 2) cpuplugd_exit("Malformed content of %s: %s\n", path, sub_list); number += (end - start) + 1; } else { number++; } sub_list = strtok(NULL, ","); } return number; } /* * get_numcpus() - return number of present cpus by sysfs' * cpu/present attribute. * This number represents the total number of usable cpus, * this includes offline or deconfigured cpus as well. */ int get_numcpus(void) { int number; char *path; path = util_path_sysfs("devices/system/cpu/present"); number = get_sysfs_attribute_cpu_count(path); free(path); if (number <= 0) cpuplugd_exit("number of present cpus (%d) <= 0\n", number); return number; } /* * get_num_online_cpus() - return number of online cpus * by parsing sysfs cpu/online attribute */ int get_num_online_cpus(void) { int number; char *path; path = util_path_sysfs("devices/system/cpu/online"); number = get_sysfs_attribute_cpu_count(path); free(path); if (number <= 0) cpuplugd_exit("number of online cpus (%d) <= 0\n", number); return number; } /* * get_polarization() - return system polarization */ int get_polarization(void) { int polarization; char *path; path = util_path_sysfs("devices/system/cpu/dispatching"); if (!util_path_exists(path)) return PLR_NONE; if (util_file_read_i(&polarization, NUM_BASE, path) < 0) { polarization = -1; cpuplugd_debug("failed to read system polarization\n"); } free(path); return polarization; } /* * is_cpu_hotpluggable() - check if cpuhotplug operations are supported * for the given cpu. */ static int is_cpu_hotpluggable(int cpuid) { char *path; int rc; path = util_path_sysfs("devices/system/cpu/cpu%d/online", cpuid); rc = util_path_exists(path); free(path); return rc; } /* * hotplug() - perform cpu hotplug on given cpuid */ static int hotplug(int cpuid) { char *path; int rc; path = util_path_sysfs("devices/system/cpu/cpu%d/online", cpuid); rc = util_file_write_l(CPU_ONLINE, NUM_BASE, path); if (rc < 0) cpuplugd_debug("failed to enable cpu with id %d\n", cpuid); free(path); return rc; } /* * hotunplug() - perform cpu hotunplug on given cpuid */ static int hotunplug(int cpuid) { char *path; int rc; path = util_path_sysfs("devices/system/cpu/cpu%d/online", cpuid); rc = util_file_write_l(CPU_OFFLINE, NUM_BASE, path); if (rc < 0) cpuplugd_debug("failed to disable cpu with id %d\n", cpuid); free(path); return rc; } /* * get_cpu_attribute() - get a certain cpu's selected attribute */ static int get_cpu_attribute(int cpuid, char *attribute) { int status; char *path; path = util_path_sysfs("devices/system/cpu/cpu%d/%s", cpuid, attribute); if (util_file_read_i(&status, NUM_BASE, path) < 0) { status = -1; cpuplugd_debug("failed to read %s status of cpu with id %d\n", attribute, cpuid); } free(path); return status; } /* * hotplug_one_cpu() - perform hotplugging on the first available cpu */ int hotplug_one_cpu(void) { struct dirent **cpu_dir; int cpuid, count, i, rc; char *path; rc = -1; path = util_path_sysfs("devices/system/cpu/"); count = util_scandir(&cpu_dir, alphasort, path, "cpu[0-9]*"); for (i = 0; (i < count) && (rc != 0); i++) { if (sscanf(cpu_dir[i]->d_name, "cpu%d", &cpuid) != 1) cpuplugd_exit("Malformed content of %s: %s\n", path, cpu_dir[i]->d_name); if (!is_cpu_hotpluggable(cpuid)) continue; if (get_cpu_attribute(cpuid, "configure") == CPU_CONFIGURED && get_cpu_attribute(cpuid, "online") == CPU_OFFLINE) { cpuplugd_debug("cpu%d will be enabled", cpuid); rc = hotplug(cpuid); } } util_scandir_free(cpu_dir, count); free(path); return rc; } /* * hotunplug_one_cpu() - perform hotunplugging on the first available cpu */ int hotunplug_one_cpu(void) { struct dirent **cpu_dir; int cpuid, count, i, rc; char *path; rc = -1; path = util_path_sysfs("devices/system/cpu/"); count = util_scandir(&cpu_dir, alphasort, path, "cpu[0-9]*"); for (i = 0; (i < count) && (rc != 0); i++) { if (sscanf(cpu_dir[i]->d_name, "cpu%d", &cpuid) != 1) cpuplugd_exit("Malformed content of %s: %s\n", path, cpu_dir[i]->d_name); if (!is_cpu_hotpluggable(cpuid)) continue; if (get_cpu_attribute(cpuid, "online") == CPU_ONLINE) { cpuplugd_debug("cpu%d will be disabled\n", cpuid); rc = hotunplug(cpuid); } } util_scandir_free(cpu_dir, count); free(path); return rc; } /* * Cleanup method. If the daemon is stopped, we (re)activate all cpus */ void reactivate_cpus(void) { struct dirent **cpu_dir; int cpuid, nc, count, i; char *path; /* * Only enable the number of cpus which where available at * daemon startup time by checking num_cpu_start. * We check for num_cpu_start != 0 because we might want to * clean up, before we queried for the number on cpus at * startup */ if (num_cpu_start == 0) return; nc = 0; path = util_path_sysfs("devices/system/cpu/"); count = util_scandir(&cpu_dir, alphasort, path, "cpu[0-9]*"); for (i = 0; (i < count) && (nc != num_cpu_start); i++) { nc = get_num_online_cpus(); if (sscanf(cpu_dir[i]->d_name, "cpu%d", &cpuid) != 1) cpuplugd_exit("Malformed content of %s: %s\n", path, cpu_dir[i]->d_name); if (nc > num_cpu_start && get_cpu_attribute(cpuid, "online") == CPU_ONLINE) hotunplug(cpuid); if (nc < num_cpu_start && get_cpu_attribute(cpuid, "online") == CPU_OFFLINE) hotplug(cpuid); } util_scandir_free(cpu_dir, count); free(path); } s390-tools-2.38.0/cpuplugd/cpuplugd.h000066400000000000000000000121671502674226300172750ustar00rootroot00000000000000/* * cpuplugd - Linux for System z Hotplug Daemon * * Header file * * Copyright IBM Corp. 2007, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef __USE_ISOC99 #define __USE_ISOC99 #endif #include #include #include #include #include #include #include #include #include #include "lib/util_base.h" #include "lib/util_path.h" #include "lib/util_file.h" #include "lib/util_scandir.h" #include "lib/zt_common.h" #define NAME "cpuplugd" #define MAX_HISTORY 100 #define PIDFILE "/run/cpuplugd.pid" #define LOCKFILE "/var/lock/cpuplugd.lock" #define PROCINFO_LINE 512 #define CPUSTAT_SIZE 1024 #define VARINFO_SIZE 4096 #define MAX_VARNAME 128 #define MAX_LINESIZE 2048 #define CPUSTATS 10 #define PLR_HORIZONTAL 0 #define PLR_VERTICAL 1 #define PLR_NONE 2 /* * Precedence of C operators * full list: * http://www.imada.sdu.dk/~svalle/ * courses/dm14-2005/mirror/c/_7193_tabular246.gif * * () * +- * * / * < > * & * | */ enum op_prio { OP_PRIO_NONE, OP_PRIO_OR, OP_PRIO_AND, /* greater and lower */ OP_PRIO_CMP, OP_PRIO_ADD, OP_PRIO_MULT }; enum operation { /* Leaf operators */ OP_SYMBOL_LOADAVG, OP_SYMBOL_RUNABLE, OP_SYMBOL_CPUS, OP_SYMBOL_USER, OP_SYMBOL_NICE, OP_SYMBOL_SYSTEM, OP_SYMBOL_IDLE, OP_SYMBOL_IOWAIT, OP_SYMBOL_IRQ, OP_SYMBOL_SOFTIRQ, OP_SYMBOL_STEAL, OP_SYMBOL_GUEST, OP_SYMBOL_GUEST_NICE, OP_SYMBOL_APCR, OP_SYMBOL_SWAPRATE, OP_SYMBOL_FREEMEM, OP_SYMBOL_MEMINFO, OP_SYMBOL_VMSTAT, OP_SYMBOL_CPUSTAT, OP_SYMBOL_TIME, OP_CONST, /* Unary operators */ OP_NEG, OP_NOT, /* Binary operators */ OP_AND, OP_OR, OP_GREATER, OP_LESSER, OP_PLUS, OP_MINUS, OP_MULT, OP_DIV, /* ... */ /*Variables which are eligible within rules*/ VAR_LOAD, /* loadaverage */ VAR_RUN, /* number of runnable processes */ VAR_ONLINE /* number of online cpus */ }; struct symbols { double loadavg; double runnable_proc; double onumcpus; double idle; double freemem; double apcr; double swaprate; double user; double nice; double system; double iowait; double irq; double softirq; double steal; double guest; double guest_nice; }; struct term { enum operation op; double value; struct term *left, *right; char *proc_name; unsigned int index; }; /* * List of argurments taken fromt the configuration file * */ struct config { long cpu_max; long cpu_min; long update; long cmm_max; long cmm_min; struct term *cmm_inc; struct term *cmm_dec; struct term *hotplug; struct term *hotunplug; struct term *memplug; struct term *memunplug; }; struct symbol_names { char *name; enum operation symop; }; extern int foreground; extern char *configfile; extern int debug; /* is verbose specified? */ extern int memory; extern int cpu; extern int num_cpu_start; /* # of online cpus at the time of the startup */ extern long cmm_pagesize_start; /* cmm_pageize at the time of daemon startup */ extern struct config cfg; extern int reload_pending; extern unsigned long meminfo_size; extern unsigned long vmstat_size; extern unsigned long cpustat_size; extern unsigned long varinfo_size; extern char *meminfo; extern char *vmstat; extern char *cpustat; extern char *varinfo; extern double *timestamps; extern unsigned int history_max; extern unsigned int history_current; extern struct symbol_names sym_names[]; extern unsigned int sym_names_count; extern int saved_polarization; int get_numcpus(); int get_num_online_cpus(); int get_polarization(void); void get_loadavg_runnable(double *loadavg, double *runnable); void clean_up(); void reactivate_cpus(); void parse_configfile(char *file); void print_term(struct term *fn); struct term *parse_term(char **p, enum op_prio prio); int eval_term(struct term *fn, struct symbols *symbols); double eval_double(struct term *fn, struct symbols *symbols); double get_proc_value(char *procinfo, char *name, char separator); void proc_read(char *procinfo, char *path, unsigned long size); void proc_cpu_read(char *procinfo); unsigned long proc_read_size(char *path); char *get_var_rvalue(char *var_name); void cleanup_cmm(void); int hotplug_one_cpu(void); int hotunplug_one_cpu(void); long get_cmmpages_size(); void parse_options(int argc, char **argv); void check_if_started_twice(); void handle_signals(void); void handle_sighup(void); void reload_daemon(void); int daemonize(void); int check_cmmfiles(void); void check_config(); void apply_cpu_config(void); void set_cmm_pages(long size); int check_lpar(); void setup_history(void); #define cpuplugd_info(fmt, ...) ({ \ if (foreground == 1) \ printf(fmt, ##__VA_ARGS__); \ if (foreground == 0) \ syslog(LOG_INFO, fmt, ##__VA_ARGS__); \ }) #define cpuplugd_error(fmt, ...) ({ \ if (foreground == 1) \ fprintf(stderr, fmt, ##__VA_ARGS__); \ if (foreground == 0) \ syslog(LOG_ERR, fmt, ##__VA_ARGS__); \ }) #define cpuplugd_debug(fmt, ...) ({ \ if (debug) \ cpuplugd_info(fmt, ##__VA_ARGS__); \ }) #define cpuplugd_exit(fmt, ...) ({ \ cpuplugd_error(fmt, ##__VA_ARGS__); \ clean_up(); \ }) s390-tools-2.38.0/cpuplugd/daemon.c000066400000000000000000000147261502674226300167130ustar00rootroot00000000000000/* * cpuplugd - Linux for System z Hotplug Daemon * * Daemon functions * * Copyright IBM Corp. 2007, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include "cpuplugd.h" const char *name = NAME; static const char *pid_file = PIDFILE; const char *const usage = "Usage: %s [OPTIONS]\n" "\n" "Daemon to dynamically hotplug cpus and memory based on a set of rules\n" "Use OPTIONS described below.\n" "\n" "\t-c, --config CONFIGFILE Path to the configuration file\n" "\t-f, --foreground Run in foreground, do not detach\n" "\t-h, --help Print this help, then exit\n" "\t-v, --version Print version information, then exit\n" "\t-V, --verbose Provide more verbose output\n"; /* * Print command usage */ void print_usage(int is_error, char program_name[]) { fprintf(is_error ? stderr : stdout, usage, program_name); exit(is_error ? 1 : 0); } /* * Print command version */ void print_version() { printf("%s: Linux on System z CPU hotplug daemon version %s\n", name, RELEASE_STRING); printf("Copyright IBM Corp. 2007, 2017\n"); exit(0); } /* * Store daemon's pid so it can be stopped */ static int store_pid(void) { FILE *filp; filp = fopen(pid_file, "w"); if (!filp) { cpuplugd_error("cannot open pid file %s: %s\n", pid_file, strerror(errno)); return -1; } fprintf(filp, "%d\n", getpid()); fclose(filp); return 0; } /* * Run daemon in background and write pid file */ int daemonize(void) { int fd, pipe_fds[2], startup_rc = 1; pid_t pid; if (pipe(pipe_fds) == -1) { cpuplugd_error("cannot create pipe\n"); return -1; } pid = fork(); if (pid < 0) goto close_pipe; if (pid != 0) { /* Wait for startup return code from daemon */ if (read(pipe_fds[0], &startup_rc, sizeof(startup_rc)) == -1) cpuplugd_error("cannot read from pipe\n"); /* On success daemon has written pid file at this point */ exit(startup_rc); } /* Create new session */ if (setsid() < 0) goto notify_parent; /* Redirect stdin/out/err to /dev/null */ fd = open("/dev/null", O_RDWR, 0); if (fd == -1) goto notify_parent; if (dup2(fd, STDIN_FILENO) < 0) goto notify_parent; if (dup2(fd, STDOUT_FILENO) < 0) goto notify_parent; if (dup2(fd, STDERR_FILENO) < 0) goto notify_parent; /* Create pid file */ if (store_pid() < 0) goto notify_parent; startup_rc = 0; notify_parent: /* Inform waiting parent about startup return code */ if (write(pipe_fds[1], &startup_rc, sizeof(startup_rc)) == -1) { cpuplugd_error("cannot write to pipe\n"); startup_rc = 1; } close_pipe: close(pipe_fds[0]); close(pipe_fds[1]); return startup_rc ? -1 : 0; } /* * Check that we don't try to start this daemon twice */ void check_if_started_twice() { FILE *filp; int pid, rc; filp = fopen(pid_file, "r"); if (filp) { rc = fscanf(filp, "%d", &pid); if (rc != 1) { cpuplugd_error("Reading pid file failed. Aborting!\n"); exit(1); } cpuplugd_error("pid file %s still exists.\nThis might indicate " "that an instance of this daemon is already " "running.\n", pid_file); exit(1); } } /* * Clean up method */ void clean_up() { cpuplugd_info("terminated\n"); remove(pid_file); remove(LOCKFILE); /* suppress verbose messages on exit */ debug = 0; reactivate_cpus(); if (memory) cleanup_cmm(); exit(1); } /* * End the deamon */ void kill_daemon(int UNUSED(a)) { cpuplugd_info("shutting down\n"); remove(pid_file); remove(LOCKFILE); /* suppress verbose messages on exit */ debug = 0; reactivate_cpus(); if (memory) cleanup_cmm(); exit(0); } /* * Reload the daemon (for lsb compliance) */ void reload_handler(int UNUSED(a)) { reload_pending = 1; } void reload_daemon() { unsigned int temp_history; long temp_mem; int temp_cpu; cpuplugd_info("cpuplugd restarted\n"); /* * Before we parse the configuration file again we have to save * the original values prior to startup. If we don't do this cpuplugd * will no longer know how many cpus the system had before the daemon * was started and therefor can't restore theres in case it is stopped */ temp_cpu = num_cpu_start; temp_mem = cmm_pagesize_start; temp_history = history_max; /* clear varinfo before re-reading variables from config file */ memset(varinfo, 0, varinfo_size); history_max = 1; parse_configfile(configfile); if (history_max > MAX_HISTORY) cpuplugd_exit("History depth %i exceeded maximum (%i)\n", history_max, MAX_HISTORY); if (history_max != temp_history) { free(meminfo); free(vmstat); free(cpustat); free(timestamps); setup_history(); } check_config(); num_cpu_start = temp_cpu; cmm_pagesize_start = temp_mem; } /* * Set up for handling SIGTERM or SIGINT */ void handle_signals(void) { struct sigaction act; act.sa_flags = 0; sigemptyset(&act.sa_mask); act.sa_handler = kill_daemon; if (sigaction(SIGTERM, &act, NULL) < 0) { cpuplugd_error("sigaction( SIGTERM, ... ) failed - reason %s\n", strerror(errno)); exit(1); } if (sigaction(SIGINT, &act, NULL) < 0) { cpuplugd_error("sigaction( SIGINT, ... ) failed - reason %s\n", strerror(errno)); exit(1); } } /* * Signal handler for sighup. This is used to force the deamon to reload its * configuration file. * This feature is also required by a lsb compliant init script */ void handle_sighup(void) { struct sigaction act; act.sa_flags = 0; sigemptyset(&act.sa_mask); act.sa_handler = reload_handler; if (sigaction(SIGHUP, &act, NULL) < 0) { cpuplugd_error("sigaction( SIGHUP, ... ) failed - reason %s\n", strerror(errno)); exit(1); } } /* Check if we are running in an LPAR environment. * This functions return 1 if we run inside an lpar and 0 otherwise */ int check_lpar() { int rc; FILE *filp; size_t bytes_read; char buffer[2048]; char *contains_vm; rc = 0; filp = fopen("/proc/cpuinfo", "r"); if (!filp) cpuplugd_exit("cannot open /proc/cpuinfo: %s\n", strerror(errno)); bytes_read = fread(buffer, 1, sizeof(buffer) - 1, filp); if (bytes_read == 0) cpuplugd_exit("Reading /proc/cpuinfo failed: %s\n", strerror(errno)); /* NUL-terminate the text */ buffer[bytes_read] = '\0'; contains_vm = strstr(buffer, "version = FF"); if (contains_vm == NULL) { rc = 1; cpuplugd_debug("Detected System running in LPAR mode\n"); } else cpuplugd_debug("Detected System running in z/VM mode\n"); fclose(filp); return rc; } s390-tools-2.38.0/cpuplugd/getopt.c000066400000000000000000000043551502674226300167470ustar00rootroot00000000000000/* * cpuplugd - Linux for System z Hotplug Daemon * * Command line parsing * * Copyright IBM Corp. 2007, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include "cpuplugd.h" void print_usage(int is_error, char program_name[]); void print_version(); int foreground; int debug; char *configfile; int cpu_idle_limit; void parse_options(int argc, char **argv) { int config_file_specified = -1; const struct option long_options[] = { { "help", no_argument, NULL, 'h'}, { "foreground", no_argument, NULL, 'f' }, { "config", required_argument, NULL, 'c' }, { "version", no_argument, NULL, 'v' }, { "verbose", no_argument, NULL, 'V' }, { NULL, 0, NULL, 0} }; /* dont run without any argument */ if (argc == 0 || argc == 1) print_usage(0, argv[0]); while (optind < argc) { int index = -1; struct option *opt = 0; int result = getopt_long(argc, argv, "hfc:vVm", long_options, &index); if (result == -1) break; /* end of list */ switch (result) { case 'h': print_usage(0, argv[0]); break; case 'f': foreground = 1; break; case 'c': /* * This prevents -cbla and enforces the * user to specify -c bla */ if (strcmp(argv[optind-1], optarg) == 0) { configfile = optarg; config_file_specified = 1; } else { cpuplugd_error("Unrecognized option: %s\n", optarg); exit(1); } break; case 'v': print_version(); break; case 'V': debug = 1; break; case 0: /* all parameter that do not appear in the optstring */ opt = (struct option *)&(long_options[index]); printf("'%s' was specified.", opt->name); if (opt->has_arg == required_argument) printf("Arg: <%s>", optarg); printf("\n"); break; case '?': printf("Try '%s' --help' for more information.\n", argv[0]); exit(1); break; case -1: /* * We also run in this case if no argument was * specified */ break; default: print_usage(0, argv[0]); } } if (config_file_specified == -1) { printf("You have to specify a configuration file!\n"); printf("Try '%s' --help' for more information.\n", argv[0]); exit(1); } } s390-tools-2.38.0/cpuplugd/info.c000066400000000000000000000102621502674226300163720ustar00rootroot00000000000000/* * cpuplugd - Linux for System z Hotplug Daemon * * /proc info functions * * Copyright IBM Corp. 2007, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include "cpuplugd.h" /* * Return current load average and runnable processes based on /proc/loadavg * * Example: 0.20 0.18 0.12 1/80 11206 * * The first three columns measure CPU utilization of the last 1, 5, * and 15 minute periods. * The fourth column shows the number of currently running processes * and the total number of processes. * The last column displays the last process ID used. */ void get_loadavg_runnable(double *loadavg, double *runnable) { FILE *filp; double dummy; int rc; filp = fopen("/proc/loadavg", "r"); if (!filp) cpuplugd_exit("cannot open kernel loadaverage " "statistics: %s\n", strerror(errno)); rc = fscanf(filp, "%lf %lf %lf %lf/", loadavg, &dummy, &dummy, runnable); if (rc != 4) cpuplugd_exit("cannot parse kernel loadaverage " "statistics: %s\n", strerror(errno)); fclose(filp); return; } void proc_cpu_read(char *procinfo) { FILE *filp; unsigned int rc, onumcpus; unsigned long user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice, total_ticks; double loadavg, runnable; guest = guest_nice = 0; /* set to 0 if not present in kernel */ filp = fopen("/proc/stat", "r"); if (!filp) cpuplugd_exit("/proc/stat open failed: %s\n", strerror(errno)); rc = fscanf(filp, "cpu %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld", &user, &nice, &system, &idle, &iowait, &irq, &softirq, &steal, &guest, &guest_nice); get_loadavg_runnable(&loadavg, &runnable); onumcpus = get_num_online_cpus(); total_ticks = user + nice + system + idle + iowait + irq + softirq + steal + guest + guest_nice; rc = snprintf(procinfo, cpustat_size, "onumcpus %d\nloadavg %f\n" "runnable_proc %f\nuser %ld\nnice %ld\nsystem %ld\n" "idle %ld\niowait %ld\nirq %ld\nsoftirq %ld\nsteal %ld\n" "guest %ld\nguest_nice %ld\ntotal_ticks %ld\n", onumcpus, loadavg, runnable, user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice, total_ticks); if (rc >= cpustat_size) cpuplugd_exit("cpustat buffer too small: need %d, have %ld " "(bytes)\n", rc, cpustat_size); fclose(filp); return; } void proc_read(char *procinfo, char *path, unsigned long size) { size_t bytes_read; FILE *filp; filp = fopen(path, "r"); if (!filp) cpuplugd_exit("%s open failed: %s\n", path, strerror(errno)); bytes_read = fread(procinfo, 1, size, filp); if (bytes_read == 0) cpuplugd_exit("%s read failed\n", path); if (bytes_read == size) cpuplugd_exit("procinfo buffer too small for %s\n", path); procinfo[bytes_read] = '\0'; fclose(filp); return; } unsigned long proc_read_size(char *path) { FILE *filp; char buf[PROCINFO_LINE]; char *linep, *linep_offset; unsigned long size; filp = fopen(path, "r"); if (!filp) cpuplugd_exit("%s open failed: %s\n", path, strerror(errno)); size = 0; while ((linep = fgets(buf, sizeof(buf), filp))) { if (!(linep_offset = strchr(linep, '\n'))) cpuplugd_exit("buf too small for line\n"); size = size + linep_offset - linep + 1; } fclose(filp); return size; } double get_proc_value(char *procinfo, char *name, char separator) { char buf[PROCINFO_LINE]; char *proc_offset; unsigned long proc_length, name_length; double value; int found; value = -1; found = 0; name_length = strlen(name); while ((proc_offset = strchr(procinfo, separator))) { proc_length = proc_offset - procinfo; /* * proc_read_size() made sure that proc_length < PROCINFO_LINE */ memcpy(buf, procinfo, proc_length); buf[proc_length] = '\0'; procinfo = proc_offset + 1; if (strncmp(buf, name, MAX(proc_length, name_length)) == 0) { errno = 0; value = strtod(procinfo, NULL); if (errno) cpuplugd_exit("strtod failed\n"); found = 1; break; } proc_offset = strchr(procinfo, '\n'); procinfo = proc_offset + 1; } if (!found) cpuplugd_exit("Symbol %s not found, check your config file\n", name); return value; } s390-tools-2.38.0/cpuplugd/main.c000066400000000000000000000356551502674226300164000ustar00rootroot00000000000000/* * cpuplugd - Linux for System z Hotplug Daemon * * Main functions * * Copyright IBM Corp. 2007, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include "cpuplugd.h" struct symbol_names sym_names[] = { { "loadavg", OP_SYMBOL_LOADAVG }, { "runnable_proc", OP_SYMBOL_RUNABLE }, { "onumcpus", OP_SYMBOL_CPUS }, { "user", OP_SYMBOL_USER }, { "nice", OP_SYMBOL_NICE }, { "system", OP_SYMBOL_SYSTEM }, { "idle", OP_SYMBOL_IDLE }, { "iowait", OP_SYMBOL_IOWAIT }, { "irq", OP_SYMBOL_IRQ }, { "softirq", OP_SYMBOL_SOFTIRQ }, { "steal", OP_SYMBOL_STEAL }, { "guest_nice", OP_SYMBOL_GUEST_NICE }, { "guest", OP_SYMBOL_GUEST }, { "swaprate", OP_SYMBOL_SWAPRATE }, { "apcr", OP_SYMBOL_APCR }, { "freemem", OP_SYMBOL_FREEMEM }, { "meminfo.", OP_SYMBOL_MEMINFO }, { "vmstat.", OP_SYMBOL_VMSTAT }, { "cpustat.", OP_SYMBOL_CPUSTAT }, { "time", OP_SYMBOL_TIME }, }; struct config cfg = { .cpu_max = -1, .cpu_min = -1, .update = -1, .cmm_min = -1, .cmm_max = -1, .cmm_inc = NULL, .cmm_dec = NULL, .memplug = NULL, .memunplug = NULL, .hotplug = NULL, .hotunplug = NULL, }; unsigned int history_max, history_current, history_prev, sym_names_count; unsigned long meminfo_size, vmstat_size, cpustat_size, varinfo_size; int num_cpu_start, memory, cpu, reload_pending, saved_polarization; char *meminfo, *vmstat, *cpustat, *varinfo; long cmm_pagesize_start; double *timestamps; static struct symbols symbols; static jmp_buf jmpenv; static struct sigaction act; /* * Handle the sigfpe signal which we might catch during rule evaluating */ static void sigfpe_handler(int UNUSED(sig)) { longjmp(jmpenv, 1); } static void eval_cpu_rules(void) { double diffs[CPUSTATS], diffs_total, percent_factor; char *procinfo_current, *procinfo_prev; int nr_cpus, on_off, polarization; polarization = get_polarization(); if (polarization < 0) { cpuplugd_debug("couldn't determine system polarization\n"); cpuplugd_debug("skipping cpu rule evaluation\n"); return; } if (saved_polarization != polarization) { saved_polarization = polarization; if (polarization == PLR_VERTICAL) { /* revert cpu hotplug adjustments after switching from horizontal */ reactivate_cpus(); } else if (polarization == PLR_HORIZONTAL) { /* reapply cpu config after switching from vertical */ apply_cpu_config(); } } if (polarization == PLR_VERTICAL) { cpuplugd_debug("system is running vertical polarization\n"); cpuplugd_debug("cpuplugd won't make cpu adjustments\n"); return; } nr_cpus = get_numcpus(); procinfo_current = cpustat + history_current * cpustat_size; procinfo_prev = cpustat + history_prev * cpustat_size; diffs[0] = get_proc_value(procinfo_current, "user", ' ') - get_proc_value(procinfo_prev, "user", ' '); diffs[1] = get_proc_value(procinfo_current, "nice", ' ') - get_proc_value(procinfo_prev, "nice", ' '); diffs[2] = get_proc_value(procinfo_current, "system", ' ') - get_proc_value(procinfo_prev, "system", ' '); diffs[3] = get_proc_value(procinfo_current, "idle", ' ') - get_proc_value(procinfo_prev, "idle", ' '); diffs[4] = get_proc_value(procinfo_current, "iowait", ' ') - get_proc_value(procinfo_prev, "iowait", ' '); diffs[5] = get_proc_value(procinfo_current, "irq", ' ') - get_proc_value(procinfo_prev, "irq", ' '); diffs[6] = get_proc_value(procinfo_current, "softirq", ' ') - get_proc_value(procinfo_prev, "softirq", ' '); diffs[7] = get_proc_value(procinfo_current, "steal", ' ') - get_proc_value(procinfo_prev, "steal", ' '); diffs[8] = get_proc_value(procinfo_current, "guest", ' ') - get_proc_value(procinfo_prev, "guest", ' '); diffs[9] = get_proc_value(procinfo_current, "guest_nice", ' ') - get_proc_value(procinfo_prev, "guest_nice", ' '); diffs_total = get_proc_value(procinfo_current, "total_ticks", ' ') - get_proc_value(procinfo_prev, "total_ticks", ' '); if (diffs_total == 0) diffs_total = 1; symbols.loadavg = get_proc_value(procinfo_current, "loadavg", ' '); symbols.runnable_proc = get_proc_value(procinfo_current, "runnable_proc", ' '); symbols.onumcpus = get_proc_value(procinfo_current, "onumcpus", ' '); percent_factor = 100 * symbols.onumcpus; symbols.user = (diffs[0] / diffs_total) * percent_factor; symbols.nice = (diffs[1] / diffs_total) * percent_factor; symbols.system = (diffs[2] / diffs_total) * percent_factor; symbols.idle = (diffs[3] / diffs_total) * percent_factor; symbols.iowait = (diffs[4] / diffs_total) * percent_factor; symbols.irq = (diffs[5] / diffs_total) * percent_factor; symbols.softirq = (diffs[6] / diffs_total) * percent_factor; symbols.steal = (diffs[7] / diffs_total) * percent_factor; symbols.guest = (diffs[8] / diffs_total) * percent_factor; symbols.guest_nice = (diffs[9] / diffs_total) * percent_factor; /* only use this for development and testing */ cpuplugd_debug("cpustat values:\n%s", cpustat + history_current * cpustat_size); if (debug && foreground == 1) { printf("-------------------- CPU --------------------\n"); printf("cpu_min: %ld\n", cfg.cpu_min); printf("cpu_max: %ld\n", cfg.cpu_max); printf("loadavg: %f \n", symbols.loadavg); printf("user percent = %f\n", symbols.user); printf("nice percent = %f\n", symbols.nice); printf("system percent = %f\n", symbols.system); printf("idle percent = %f\n", symbols.idle); printf("iowait percent = %f\n", symbols.iowait); printf("irq percent = %f\n", symbols.irq); printf("softirq percent = %f\n", symbols.softirq); printf("steal percent = %f\n", symbols.steal); printf("guest percent = %f\n", symbols.guest); printf("guest_nice percent = %f\n", symbols.guest_nice); printf("numcpus %d\n", nr_cpus); printf("runnable_proc: %d\n", (int) symbols.runnable_proc); printf("---------------------------------------------\n"); printf("onumcpus: %d\n", (int) symbols.onumcpus); printf("---------------------------------------------\n"); printf("hotplug: "); print_term(cfg.hotplug); printf("\n"); printf("hotunplug: "); print_term(cfg.hotunplug); printf("\n"); printf("---------------------------------------------\n"); } on_off = 0; /* Evaluate the hotplug rule */ if (eval_term(cfg.hotplug, &symbols)) on_off++; /* Evaluate the hotunplug rule only if hotplug did not match */ else if (eval_term(cfg.hotunplug, &symbols)) on_off--; if (on_off > 0) { /* check the cpu nr limit */ if (symbols.onumcpus + 1 > cfg.cpu_max) { /* cpu limit reached */ cpuplugd_debug("maximum cpu limit is reached\n"); return; } if (hotplug_one_cpu()) cpuplugd_debug("unable to find a cpu which can be enabled\n"); } else if (on_off < 0) { /* check cpu nr limit */ if (symbols.onumcpus <= cfg.cpu_min) { cpuplugd_debug("minimum cpu limit is reached\n"); return; } if (hotunplug_one_cpu()) cpuplugd_debug("unable to find a cpu which can be disabled\n"); } } static void eval_mem_rules(double interval) { long cmmpages_size, cmm_inc, cmm_dec, cmm_new; double free_memory, swaprate, apcr; char *procinfo_current, *procinfo_prev; procinfo_current = meminfo + history_current * meminfo_size; free_memory = get_proc_value(procinfo_current, "MemFree", ':'); procinfo_current = vmstat + history_current * vmstat_size; procinfo_prev = vmstat + history_prev * vmstat_size; swaprate = (get_proc_value(procinfo_current, "pswpin", ' ') + get_proc_value(procinfo_current, "pswpout", ' ') - get_proc_value(procinfo_prev, "pswpin", ' ') - get_proc_value(procinfo_prev, "pswpout", ' ')) / interval; apcr = (get_proc_value(procinfo_current, "pgpgin", ' ') + get_proc_value(procinfo_current, "pgpgout", ' ') - get_proc_value(procinfo_prev, "pgpgin", ' ') - get_proc_value(procinfo_prev, "pgpgout", ' ')) / interval; cmmpages_size = get_cmmpages_size(); symbols.apcr = apcr; // apcr in 512 byte blocks / sec symbols.swaprate = swaprate; // swaprate in 4K pages / sec symbols.freemem = free_memory / 1024; // freemem in MB cmm_inc = eval_double(cfg.cmm_inc, &symbols); /* cmm_dec is optional */ if (cfg.cmm_dec) cmm_dec = eval_double(cfg.cmm_dec, &symbols); else cmm_dec = cmm_inc; /* only use this for development and testing */ if (debug && foreground == 1) { printf("------------------- Memory ------------------\n"); printf("cmm_min: %ld\n", cfg.cmm_min); printf("cmm_max: %ld\n", cfg.cmm_max); printf("swaprate: %f\n", symbols.swaprate); printf("apcr: %f\n", symbols.apcr); printf("cmm_inc: %ld = ", cmm_inc); print_term(cfg.cmm_inc); printf("\n"); printf("cmm_dec: %ld = ", cmm_dec); if (cfg.cmm_dec) print_term(cfg.cmm_dec); else print_term(cfg.cmm_inc); printf("\n"); printf("free memory: %f MB\n", symbols.freemem); printf("---------------------------------------------\n"); printf("cmm_pages: %ld\n", cmmpages_size); printf("---------------------------------------------\n"); printf("memplug: "); print_term(cfg.memplug); printf("\n"); printf("memunplug: "); print_term(cfg.memunplug); printf("\n"); printf("---------------------------------------------\n"); } cmm_new = cmmpages_size; /* Evaluate the memplug rule */ if (eval_term(cfg.memplug, &symbols)) { if (cmm_dec < 0) { cpuplugd_error("cmm_dec went negative (%ld), set it " "to 0.\n", cmm_dec); cmm_dec = 0; } cmm_new -= cmm_dec; /* Evaluate the memunplug rule only if memplug did not match */ } else if (eval_term(cfg.memunplug, &symbols)) { if (cmm_inc < 0) { cpuplugd_error("cmm_inc went negative (%ld), set it " "to 0.\n", cmm_inc); cmm_inc = 0; } cmm_new += cmm_inc; } if (cmm_new < cfg.cmm_min) { cpuplugd_debug("minimum memory limit is reached\n"); cmm_new = cfg.cmm_min; } if (cmm_new > cfg.cmm_max) { cpuplugd_debug("maximum memory limit is reached\n"); cmm_new = cfg.cmm_max; } if (cmm_new != cmmpages_size) set_cmm_pages(cmm_new); } static void time_read(double *timestamps) { struct timeval tv; int rc; cpuplugd_debug("\n==================== New interval " "====================\n"); rc = gettimeofday(&tv, NULL); if (!rc) { *timestamps = tv.tv_sec + (double) tv.tv_usec / 1000000; cpuplugd_debug("Timestamp: %s (%f seconds since " "the Epoch)\n", ctime(&tv.tv_sec), *timestamps); } else cpuplugd_exit("gettimeofday failed: %s\n", strerror(errno)); return; } void setup_history() { /* * The /proc file size will vary during intervals, use double of current * size to have enough buffer for growing values. */ meminfo_size = proc_read_size("/proc/meminfo") * 2; vmstat_size = proc_read_size("/proc/vmstat") * 2; cpustat_size = CPUSTAT_SIZE; meminfo = malloc(meminfo_size * (history_max + 1)); if (!meminfo) cpuplugd_exit("Out of memory: meminfo\n"); vmstat = malloc(vmstat_size * (history_max + 1)); if (!vmstat) cpuplugd_exit("Out of memory: vmstat\n"); cpustat = malloc(cpustat_size * (history_max + 1)); if (!cpustat) cpuplugd_exit("Out of memory: cpustat\n"); timestamps = malloc(sizeof(double) * (history_max + 1)); if (!timestamps) cpuplugd_exit("Out of memory: timestamps\n"); /* * Read history data, at least 1 interval for swaprate, apcr, idle, etc. */ history_current = 0; cpuplugd_info("Waiting %i intervals to accumulate history.\n", history_max); do { time_read(×tamps[history_current]); proc_read(meminfo + history_current * meminfo_size, "/proc/meminfo", meminfo_size); proc_read(vmstat + history_current * vmstat_size, "/proc/vmstat", vmstat_size); proc_cpu_read(cpustat + history_current * cpustat_size); sleep(cfg.update); history_current++; } while (history_current < history_max); history_current--; } int main(int argc, char *argv[]) { double interval; int fd, rc; reload_pending = 0; sym_names_count = sizeof(sym_names) / sizeof(struct symbol_names); varinfo_size = VARINFO_SIZE; varinfo = calloc(varinfo_size, 1); if (!varinfo) { cpuplugd_error("Out of memory: varinfo\n"); exit(1); } /* * varinfo must start with '\n' for correct string matching * in get_var_rvalue(). */ varinfo[0] = '\n'; /* Parse the command line options */ parse_options(argc, argv); /* flock() lock file to prevent multiple instances of cpuplugd */ fd = open(LOCKFILE, O_CREAT | O_RDONLY, S_IRUSR); if (fd == -1) { cpuplugd_error("Cannot open lock file %s: %s\n", LOCKFILE, strerror(errno)); exit(1); } rc = flock(fd, LOCK_EX | LOCK_NB); if (rc) { cpuplugd_error("flock() failed on lock file %s: %s\nThis might " "indicate that an instance of this daemon is " "already running.\n", LOCKFILE, strerror(errno)); exit(1); } /* Make sure that the daemon is not started multiple times */ check_if_started_twice(); /* Store daemon pid also in foreground mode */ handle_signals(); handle_sighup(); /* Need 1 history level minimum for internal symbols */ history_max = 1; /* * Parse arguments from the configuration file, also calculate * history_max */ parse_configfile(configfile); if (history_max > MAX_HISTORY) cpuplugd_exit("History depth %i exceeded maximum (%i)\n", history_max, MAX_HISTORY); /* Check the settings in the configuration file */ check_config(); if (!foreground) { rc = daemonize(); if (rc < 0) cpuplugd_exit("Detach from terminal failed: %s\n", strerror(errno)); } /* Unlock lock file */ flock(fd, LOCK_UN); close(fd); /* Install signal handler for floating point exceptions */ rc = feenableexcept(FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW | FE_INVALID); act.sa_flags = SA_NODEFER; sigemptyset(&act.sa_mask); act.sa_handler = sigfpe_handler; if (sigaction(SIGFPE, &act, NULL) < 0) cpuplugd_exit("sigaction( SIGFPE, ... ) failed - reason %s\n", strerror(errno)); setup_history(); /* Main loop */ while (1) { if (reload_pending) { // check for daemon reload reload_daemon(); reload_pending = 0; } history_prev = history_current; history_current = (history_current + 1) % (history_max + 1); time_read(×tamps[history_current]); proc_read(meminfo + history_current * meminfo_size, "/proc/meminfo", meminfo_size); proc_read(vmstat + history_current * vmstat_size, "/proc/vmstat", vmstat_size); proc_cpu_read(cpustat + history_current * cpustat_size); interval = timestamps[history_current] - timestamps[history_prev]; cpuplugd_debug("config update interval: %ld seconds\n", cfg.update); cpuplugd_debug("real update interval: %f seconds\n", interval); /* Run code that may signal failure via longjmp. */ if (cpu == 1) { if (setjmp(jmpenv) == 0) eval_cpu_rules(); else cpuplugd_error("Floating point exception, " "skipping cpu rule " "evaluation.\n"); } if (memory == 1) { if (setjmp(jmpenv) == 0) eval_mem_rules(interval); else cpuplugd_error("Floating point exception, " "skipping memory rule " "evaluation.\n"); } sleep(cfg.update); } return 0; } s390-tools-2.38.0/cpuplugd/man/000077500000000000000000000000001502674226300160455ustar00rootroot00000000000000s390-tools-2.38.0/cpuplugd/man/cpuplugd.8000066400000000000000000000036361502674226300177710ustar00rootroot00000000000000.\" Copyright 2017 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH CPUPLUGD 8 "May 2011" "s390-tools" . .SH NAME cpuplugd \- Linux on System z CPU and memory hotplug daemon . .SH SYNOPSIS .B cpuplugd .RI [ OPTIONS ] . .SH DESCRIPTION The cpuplugd daemon dynamically enables and disables CPUs and increases or decreases the cooperative memory management (CMM) page pool based on a set of rules. When the daemon is stopped, the size of the CMM page pool and the number of active CPUs are reset to the values they had before the cpuplugd was started. This program can be used to control the number of CPUs for Linux on z/VM and for Linux in LPAR mode. The memory hotplug feature (CMM page pool) applies to Linux on z/VM only. The cpuplugd daemon stops any CPU hot-plug operations when the system switches to vertical polarization, thus avoiding possible performance penalties. . .SH OPTIONS .TP \fB\-c\fP or \fB\-\-config\fP \fI\fP Specify the absolute path to the configuration file. This option is mandatory. The default configuration file can be found in /etc/cpuplugd.conf. . .TP \fB\-f\fP or \fB\-\-foreground\fP Run in the foreground and not as daemon. If this option is omitted, the program runs in the background. . .TP \fB\-h\fP or \fB\-\-help\fP Print usage message and exit. . .TP \fB\-v\fP or \fB\-\-version\fP Print Version information and exit. . .TP \fB\-V\fP or \fB\-\-verbose\fP Print verbose messages to stdout (when running in foreground) or to syslog otherwise. This options is mainly used for debugging purposes. . .SH EXAMPLES To test a setup start cpuplugd in foreground mode using verbose output: .br .RS 4 cpuplugd \-V \-f \-c /etc/cpuplugd.conf .RE For daemon mode, start cpuplugd from an init script as follows: .br .RS 4 cpuplugd \-c /etc/cpuplugd.conf .RE .SH SEE ALSO .BR cpuplugd.conf (5) s390-tools-2.38.0/cpuplugd/man/cpuplugd.conf.5000066400000000000000000000252201502674226300207030ustar00rootroot00000000000000.\" Copyright 2017 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH CPUPLUGD.CONF 5 "May 2011" "s390-tools" . .SH NAME cpuplugd.conf \- Configuration file for the Linux on System z CPU and memory hotplug daemon . .SH DESCRIPTION The cpuplugd.conf configuration file contains the configuration information for the Linux for System z CPU and memory hotplug daemon. Use this file to specify rules for enabling or disabling CPUs and for adding or removing memory. . .SS "CPU hotplug" CPUs can be enabled and disabled through a sysfs interface. The status file for a CPU, here CPU number 16 (counting starts at 0), is /sys/devices/system/cpu/cpu15/online. Writing a 0 to this file disables the CPU. Writing a 1 enables the CPU. . .SS "Memory hotplug" The rules that add or remove memory use the cooperative memory management (CMM) feature. CMM is a mechanism to reduce the memory available to Linux instances that run as guests of z/VM. CMM allocates pages to a dynamic page pool not available to Linux. A diagnose code indicates to z/VM that the pages in the page pool are out of use. z/VM can then immediately reuse these pages for other guests. . .SS "Layout of the configuration file" The configuration file contains variables specifying static numbers or expressions. They are of the format \fB=""\fP and they need to be specified within one line. Expressions can be specified to calculate algebraic values or to define boolean rules, which determine when a hotplug/hotunplug action should be taken. The maximum valid line length is 2048 characters. There are case-insensitive pre-defined and case-sensitive user-defined variables. The configuration file must include specifications for all pre-defined variables. If a variable is not set, the hotplug function it applies to, CPU or memory, is disabled. The only exception to this rule is CMM_DEC, which defaults to the setting for CMM_INC if omitted. If a pre-defined variable is set more than once, only the last occurrence is used. User-defined variables must not be set more than once. . .SS "Hotplug rules" Set these pre-defined variables to an expression that resolves to a boolean value (true or false). These variables trigger hotplug actions. Setting a variable to "0" disables the action. . .RS 2 .IP "-" 2 \fBHOTPLUG\fP - used to enable CPUs .IP "-" 2 \fBHOTUNPLUG\fP - used to disable CPUs .IP "-" 2 \fBMEMPLUG\fP - used to increase the available memory .IP "-" 2 \fBMEMUNPLUG\fP - used to decrease the amount of memory .RE .PP The following operators can be used in a hotplug rule expression: .br .RS 2 .B + * ( ) / - < > .RE .br Furthermore, the boolean operators \fB & \fP (and) \fB|\fP (or) and \fB!\fP (not) can be used. If both HOTPLUG and HOTUNPLUG evaluate to true, only the HOTPLUG action is triggered. If both MEMPLUG and MEMUNPLUG evaluate to true, only the MEMPLUG action is triggered. . .SS "Pre-defined static variables" The following pre-defined variables can be set only to a static, positive, numeric value: . .RS 2 .IP "-" 2 \fBCPU_MIN\fP - the minimum number of CPUs to keep online (> 0) .IP "-" 2 \fBCPU_MAX\fP - the maximum number of CPUs to enable (>= 0) .IP "-" 2 \fBUPDATE\fP - the interval at which cpuplugd evaluates the rules (in seconds, > 0) .IP "-" 2 \fBCMM_MIN\fP - the minimum size of the CMM page pool (>= 0) .IP "-" 2 \fBCMM_MAX\fP - the maximum size of the CMM page pool (>= 0) .RE .PP If the value of CPU_MAX is 0, the overall number of CPUs found in this system is used as the maximum. . .SS "Pre-defined dynamic variables" The following pre-defined variables can either be set to a static value or to an algebraic expression: . .RS 2 .IP "-" 2 \fBCMM_INC\fP - the amount of pages by which the CMM page pool is increased if the MEMUNPLUG rule is matched (available system memory is decreased). .IP "-" 2 \fBCMM_DEC\fP - the amount of pages by which the CMM page pool is decreased if the MEMPLUG rule is matched (available system memory is increased). .RE .PP The following operators can be used in a dynamic variable expression: .br .RS 2 .B + * ( ) / - < > .RE .br . .SS "User-defined variables" You can specify complex calculations as user-defined variables, which can then be used in expressions. User-defined variables are case-sensitive and must not match a pre-defined variable or keyword. In the configuration file, definitions for user-defined variables must precede their use in expressions. Variable names consist of alphanumeric characters (a-z,A-Z,0-9) and the "_" character, see section \fB"EXAMPLES"\fP for an example (pgscanrate). The maximum name length for a variable is 128 characters, and the maximum total size for all user-defined variables (names + values) is 4096 characters. . .SS "Keywords for CPU hotplug rules" The \fBHOTPLUG\fP and \fBHOTUNPLUG\fP rules can contain the following pre-defined keywords: . .RS 2 .IP "-" 2 \fBloadavg\fP - the current load average .IP "-" 2 \fBonumcpus\fP - the current number of CPUs which are online .IP "-" 2 \fBrunnable_proc\fP - the current amount of runnable processes .IP "-" 2 \fBuser\fP - the current CPU user percentage .IP "-" 2 \fBnice\fP - the current CPU nice percentage .IP "-" 2 \fBsystem\fP - the current CPU system percentage .IP "-" 2 \fBidle\fP - the current CPU idle percentage .IP "-" 2 \fBiowait\fP - the current CPU iowait percentage .IP "-" 2 \fBirq\fP - the current CPU irq percentage .IP "-" 2 \fBsoftirq\fP - the current CPU softirq percentage .IP "-" 2 \fBsteal\fP - the current CPU steal percentage .IP "-" 2 \fBguest\fP - the current CPU guest percentage (depends on kernel version: if not reported in /proc/stat, this is set to 0) .IP "-" 2 \fBguest_nice\fP - the current CPU guest_nice percentage (depends on kernel version: if not reported in /proc/stat, this is set to 0) .IP "-" 2 \fBcpustat.\fP - data from /proc/stat and /proc/loadavg .IP "-" 2 \fBtime\fP - floating point timestamp in "seconds.microseconds" since the Unix Epoch (1970-01-01 00:00:00 +0000 (UTC)) .RE .PP The percentage values are accumulated over all online CPUs, so they can vary between 0 and (100 * \fBonumcpus\fP). CPU usage data from /proc/stat and /proc/loadavg is accessible by specifying \fBcpustat.\fP, where \fB\fP can be any of the keywords described above, plus \fBtotal_ticks\fP. In this case, \fBloadavg\fP, \fBonumcpus\fP and \fBrunnable_proc\fP provide the same values as the pre-defined keywords, while the others refer to the raw timer ticks as reported by /proc/stat, not the percentage. For example, \fBcpustat.idle\fP reports the timer ticks spent in idle since system start, and \fBcpustat.total_ticks\fP indicates the sum of all reported timer ticks, which can be useful for user-defined percentage calculations. . .SS "Keywords for memory hotplug rules" The \fBMEMPLUG\fP and \fBMEMUNPLUG\fP rules can contain the following pre-defined keywords: . .RS 2 .IP "-" 2 \fBapcr\fP - the amount of page cache operations, i.e. pgpin + pgpout from /proc/vmstat (in 512 byte blocks / second) .IP "-" 2 \fBfreemem\fP - the amount of free memory (in megabytes) .IP "-" 2 \fBswaprate\fP - the number of swap operations, i.e. pswpin + pswpout from /proc/vmstat (in pages / second) .IP "-" 2 \fBmeminfo.\fP - any value from /proc/meminfo .IP "-" 2 \fBvmstat.\fP - any value from /proc/vmstat .IP "-" 2 \fBtime\fP - floating point timestamp in "seconds.microseconds" since the Unix Epoch (1970-01-01 00:00:00 +0000 (UTC)) .RE .PP All values from /proc/meminfo and /proc/vmstat can be used in an expression by specifying \fBmeminfo.\fP or \fBvmstat.\fP, where \fB\fP matches a symbol name reported by /proc/meminfo or /proc/vmstat (case sensitive), e.g. \fBmeminfo.MemTotal\fP. . .SS "History function" There is a history function for the following keywords: . .RS 2 .IP "-" 2 \fBcpustat.\fP - data from /proc/stat and /proc/loadavg .IP "-" 2 \fBmeminfo.\fP - any value from /proc/meminfo .IP "-" 2 \fBvmstat.\fP - any value from /proc/vmstat .IP "-" 2 \fBtime\fP - floating point timestamp in "seconds.microseconds" since the Unix Epoch (1970-01-01 00:00:00 +0000 (UTC)) .RE .PP The history levels can be accessed by appending \fB[]\fP to the name, where \fB\fP indicates the amount of past intervals where the value was gathered. [0] means the current interval (the [0] can be omitted in this case), [1] means the previous interval, [2] means two intervals ago, and so on. The history limit is 100. For example, \fBcpustat.system[1]\fP would indicate the system value from /proc/stat at the previous interval, while \fBvmstat.pgpgin\fP and \fBvmstat.pgpgin[0]\fP would both mean the current pgpgin value from /proc/vmstat. The \fBtime\fP keyword and its history values can be used to calculate values dependent on time intervals, see section \fB"EXAMPLES"\fP for an example (pgscanrate). . .SH EXAMPLES A complete configuration file could look like this: .nf ------------------------------ config file start ------------------------------ UPDATE="5" CPU_MIN="2" CPU_MAX="5" CMM_MIN="0" CMM_MAX="131072" # 512 MB pgscan_k="vmstat.pgscan_kswapd_dma + vmstat.pgscan_kswapd_normal + vmstat.pgscan_kswapd_movable" pgscan_d="vmstat.pgscan_direct_dma + vmstat.pgscan_direct_normal + vmstat.pgscan_direct_movable" pgscan_k1="vmstat.pgscan_kswapd_dma[1] + vmstat.pgscan_kswapd_normal[1] + vmstat.pgscan_kswapd_movable[1]" pgscan_d1="vmstat.pgscan_direct_dma[1] + vmstat.pgscan_direct_normal[1] + vmstat.pgscan_direct_movable[1]" pgscanrate="(pgscan_k + pgscan_d - pgscan_k1 - pgscan_d1) / (time - time[1])" cache="meminfo.Cached + meminfo.Buffers" # CMM_INC: 10% of free memory + cache, in 4K pages CMM_INC="(meminfo.MemFree + cache) / 40" # CMM_DEC: 10% of total memory in 4K pages CMM_DEC="meminfo.MemTotal / 40" HOTPLUG = "(loadavg > onumcpus + 0.75) & (idle < 10.0)" HOTUNPLUG = "(loadavg < onumcpus - 0.25) | (idle > 50)" # Plug memory if page scan rate is above 20 pages / sec MEMPLUG = "pgscanrate > 20" # Unplug memory while free memory is above 10% of total memory, or cache uses # more than 50% of total memory MEMUNPLUG = "(meminfo.MemFree > meminfo.MemTotal / 10) | (cache > meminfo.MemTotal / 2)" ------------------------------ config file end ------------------------------ .fi The example includes multiple user-defined variables to calculate the page scan rate with values from /proc/vmstat, as well as the cache size. \fBAttention:\fP Do not use these example rules on production systems. The rules have been designed to illustrate the configuration file syntax and are not suitable for actually governing hotplug actions. Useful rules differ considerably depending on the workload, resources, and requirements of the system they are designed for. . .SH SEE ALSO .BR cpuplugd (8) s390-tools-2.38.0/cpuplugd/mem.c000066400000000000000000000032461502674226300162210ustar00rootroot00000000000000/* * cpuplugd - Linux for System z Hotplug Daemon * * cmm functions * * Copyright IBM Corp. 2007, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include "cpuplugd.h" /* * The cmm_pages value defines the size of the balloon of blocked memory. * Increasing the value is removing memory from Linux, which is an memunplug. * Decreasing the value is adding memory back to Linux, which is memplug. */ /* * Set the value of cmm_pages */ void set_cmm_pages(long pages) { FILE *filp; filp = fopen("/proc/sys/vm/cmm_pages", "w"); if (!filp) cpuplugd_exit("Cannot open /proc/sys/vm/cmmpages: %s\n", strerror(errno)); cpuplugd_debug("changing number of pages permanently reserved to %ld\n", pages); fprintf(filp, "%ld\n", pages); fclose(filp); return; } /* * Read number of pages permanently reserved */ long get_cmmpages_size() { FILE *filp; long size; int rc; filp = fopen("/proc/sys/vm/cmm_pages", "r"); if (!filp) cpuplugd_exit("Cannot open /proc/sys/vm/cmm_pages: %s\n", strerror(errno)); rc = fscanf(filp, "%ld", &size); if (rc == 0) cpuplugd_exit("Can not read /proc/sys/vm/cmm_pages: %s\n", strerror(errno)); fclose(filp); return size; } /* * Reset cmm pagesize to value we found prior to daemon startup */ void cleanup_cmm() { set_cmm_pages(cmm_pagesize_start); return; } /* * Function to check if the cmm kernel module is loaded and the required * files below /proc exit */ int check_cmmfiles(void) { FILE *filp; filp = fopen("/proc/sys/vm/cmm_pages", "r"); if (!filp) return -1; fclose(filp); return 0; } s390-tools-2.38.0/cpuplugd/terms.c000066400000000000000000000264551502674226300166040ustar00rootroot00000000000000/* * cpuplugd - Linux for System z Hotplug Daemon * * Term parsing * * Copyright IBM Corp. 2007, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include "cpuplugd.h" static enum op_prio op_prio_table[] = { [OP_NEG] = OP_PRIO_ADD, [OP_GREATER] = OP_PRIO_CMP, [OP_LESSER] = OP_PRIO_CMP, [OP_PLUS] = OP_PRIO_ADD, [OP_MINUS] = OP_PRIO_ADD, [OP_MULT] = OP_PRIO_MULT, [OP_DIV] = OP_PRIO_MULT, [OP_AND] = OP_PRIO_AND, [OP_OR] = OP_PRIO_OR, }; static void free_term(struct term *fn) { if (!fn) return; switch (fn->op) { case OP_SYMBOL_LOADAVG: case OP_SYMBOL_RUNABLE: case OP_SYMBOL_CPUS: case OP_SYMBOL_USER: case OP_SYMBOL_NICE: case OP_SYMBOL_SYSTEM: case OP_SYMBOL_IDLE: case OP_SYMBOL_IOWAIT: case OP_SYMBOL_IRQ: case OP_SYMBOL_SOFTIRQ: case OP_SYMBOL_STEAL: case OP_SYMBOL_GUEST: case OP_SYMBOL_GUEST_NICE: case OP_CONST: free(fn); break; case OP_NEG: case OP_NOT: free_term(fn->left); free(fn); break; case OP_GREATER: case OP_LESSER: case OP_PLUS: case OP_MINUS: case OP_MULT: case OP_DIV: case OP_AND: free_term(fn->left); free_term(fn->right); free(fn); break; case OP_OR: free_term(fn->left); free_term(fn->right); free(fn); break; default: break; } } void print_term(struct term *fn) { switch (fn->op) { case OP_SYMBOL_LOADAVG: printf("loadavg"); break; case OP_SYMBOL_RUNABLE: printf("runnable_proc"); break; case OP_SYMBOL_CPUS: printf("onumcpus"); break; case OP_SYMBOL_USER: printf("user"); break; case OP_SYMBOL_NICE: printf("nice"); break; case OP_SYMBOL_SYSTEM: printf("system"); break; case OP_SYMBOL_IDLE: printf("idle"); break; case OP_SYMBOL_IOWAIT: printf("iowait"); break; case OP_SYMBOL_IRQ: printf("irq"); break; case OP_SYMBOL_SOFTIRQ: printf("softirq"); break; case OP_SYMBOL_STEAL: printf("steal"); break; case OP_SYMBOL_GUEST: printf("guest"); break; case OP_SYMBOL_GUEST_NICE: printf("guest_nice"); break; case OP_SYMBOL_SWAPRATE: printf("swaprate"); break; case OP_SYMBOL_FREEMEM: printf("freemem"); break; case OP_SYMBOL_APCR: printf("apcr"); break; case OP_SYMBOL_MEMINFO: printf("meminfo.%s[%u]", fn->proc_name, fn->index); break; case OP_SYMBOL_VMSTAT: printf("vmstat.%s[%u]", fn->proc_name, fn->index); break; case OP_SYMBOL_CPUSTAT: printf("cpustat.%s[%u]", fn->proc_name, fn->index); break; case OP_SYMBOL_TIME: printf("time[%u]", fn->index); break; case OP_CONST: printf("%f", fn->value); break; case OP_NEG: printf("-("); print_term(fn->left); printf(")"); break; case OP_NOT: printf("!("); print_term(fn->left); printf(")"); break; case OP_PLUS: case OP_MINUS: case OP_MULT: case OP_DIV: case OP_AND: case OP_OR: case OP_GREATER: case OP_LESSER: printf("("); print_term(fn->left); switch (fn->op) { case OP_AND: printf(") & ("); break; case OP_OR: printf(") | ("); break; case OP_GREATER: printf(") > ("); break; case OP_LESSER: printf(") < ("); break; case OP_PLUS: printf(") + ("); break; case OP_MINUS: printf(") - ("); break; case OP_MULT: printf(") * ("); break; case OP_DIV: printf(") / ("); break; // TODO OP_CONST, OP_SYMBOL_LOADAVG, ... possible here??? case OP_CONST: printf("%f", fn->value); break; case OP_SYMBOL_LOADAVG: case OP_SYMBOL_RUNABLE: case OP_SYMBOL_CPUS: case OP_SYMBOL_USER: case OP_SYMBOL_NICE: case OP_SYMBOL_SYSTEM: case OP_SYMBOL_IDLE: case OP_SYMBOL_IOWAIT: case OP_SYMBOL_IRQ: case OP_SYMBOL_SOFTIRQ: case OP_SYMBOL_STEAL: case OP_SYMBOL_GUEST: case OP_SYMBOL_GUEST_NICE: case OP_SYMBOL_APCR: case OP_SYMBOL_SWAPRATE: case OP_SYMBOL_FREEMEM: case OP_SYMBOL_MEMINFO: // TODO use default: ??? case OP_SYMBOL_VMSTAT: // TODO use default: ??? case OP_SYMBOL_CPUSTAT: // TODO use default: ??? case OP_SYMBOL_TIME: // TODO use default: ??? case OP_NEG: case OP_NOT: case VAR_LOAD: case VAR_RUN: case VAR_ONLINE: break; } print_term(fn->right); printf(")"); break; case VAR_LOAD: case VAR_RUN: case VAR_ONLINE: break; } } static struct term *parse_var_term(char **p) { char *s, *var_rvalue; struct term *fn; unsigned int length; char var_name[MAX_VARNAME + 1]; s = *p; length = 0; fn = NULL; while (isalnum(*s) || *s == '_') { var_name[length] = *s; length++; s++; if (length > MAX_VARNAME) cpuplugd_exit("Variable name too long (max. length is " "%i chars): %s\n", MAX_VARNAME, *p); } var_name[length] = '\0'; var_rvalue = get_var_rvalue(var_name); if (var_rvalue) { fn = parse_term(&var_rvalue, OP_PRIO_NONE); if (var_rvalue[0] != '\n') cpuplugd_exit("parsing error at %s, position: %s\n", var_name, var_rvalue); *p = s; } return fn; } struct term *parse_term(char **p, enum op_prio prio) { struct term *fn, *new; enum operation op; char *s, *endptr; double value; unsigned int length, i, index; s = *p; fn = NULL; if (*s == '-') { s++; fn = malloc(sizeof(struct term)); if (fn == NULL) goto out_error; if (isdigit(*s)) { value = 0; length = 0; sscanf(s, "%lf%n", &value, &length); fn->op = OP_CONST; fn->value = -value; s += length; } else { fn->op = OP_NEG; fn->left = parse_term(&s, prio); if (fn->left == NULL) goto out_error; } } else if (*s == '!') { s++; fn = malloc(sizeof(struct term)); if (fn == NULL) goto out_error; fn->op = OP_NOT; fn->left = parse_term(&s, prio); if (fn->left == NULL) goto out_error; } else if (isdigit(*s)) { value = 0; length = 0; sscanf(s, "%lf%n", &value, &length); for (i = 0; i < length; i++) s++; fn = malloc(sizeof(struct term)); if (fn == NULL) goto out_error; fn->op = OP_CONST; fn->value = value; } else if (*s == '(') { s++; fn = parse_term(&s, OP_PRIO_NONE); if (fn == NULL || *s != ')') goto out_error; s++; } else { /* Check for variable name */ fn = parse_var_term(&s); if (fn == NULL) { for (i = 0; i < sym_names_count; i++) if (strncmp(s, sym_names[i].name, strlen(sym_names[i].name)) == 0) break; if (i >= sym_names_count) /* Term doesn't make sense. */ goto out_error; /* * Parse meminfo/vmstat/cpustat with optional history * index [x] */ fn = malloc(sizeof(struct term)); if (fn == NULL) goto out_error; fn->op = sym_names[i].symop; s += strlen(sym_names[i].name); length = 0; if (fn->op == OP_SYMBOL_MEMINFO || fn->op == OP_SYMBOL_VMSTAT || fn->op == OP_SYMBOL_CPUSTAT) { while (isalpha(s[length]) || s[length] == '_') length++; fn->proc_name = malloc(length + 1); if (fn->proc_name == NULL) goto out_error; strncpy(fn->proc_name, s, length); fn->proc_name[length] = '\0'; } if (fn->op == OP_SYMBOL_MEMINFO || fn->op == OP_SYMBOL_VMSTAT || fn->op == OP_SYMBOL_CPUSTAT || fn->op == OP_SYMBOL_TIME) { if (s[length] == '[') { length++; if (!isdigit(s[length])) goto out_error; index = strtol(s + length, &endptr, 10); length = endptr - s; if (s[length] != ']') goto out_error; fn->index = index; if (history_max < index) history_max = index; length++; } s += length; } } } while (1) { switch (*s) { case '>': op = OP_GREATER; break; case '<': op = OP_LESSER; break; case '+': op = OP_PLUS; break; case '-': op = OP_MINUS; break; case '*': op = OP_MULT; break; case '/': op = OP_DIV; break; case '|': op = OP_OR; break; case '&': op = OP_AND; break; default: goto out; } if (prio >= op_prio_table[op]) break; s++; new = malloc(sizeof(struct term)); new->op = op; new->left = fn; if (new == NULL) goto out_error; new->right = parse_term(&s, op_prio_table[op]); if (new->right == NULL) { free(new); goto out_error; } fn = new; } out: *p = s; return fn; out_error: if (fn) free_term(fn); return NULL; } static double get_value(struct term *fn) { double value = 0; char *procinfo; unsigned int history_index; if (fn->index <= history_current) history_index = history_current - fn->index; else history_index = history_max + 1 - (fn->index - history_current); switch (fn->op) { case OP_SYMBOL_MEMINFO: procinfo = meminfo + history_index * meminfo_size; value = get_proc_value(procinfo, fn->proc_name, ':'); break; case OP_SYMBOL_VMSTAT: procinfo = vmstat + history_index * vmstat_size; value = get_proc_value(procinfo, fn->proc_name, ' '); break; case OP_SYMBOL_CPUSTAT: procinfo = cpustat + history_index * cpustat_size; value = get_proc_value(procinfo, fn->proc_name, ' '); break; case OP_SYMBOL_TIME: value = timestamps[history_index]; break; default: cpuplugd_exit("Invalid term specified: %i\n", fn->op); } return value; } double eval_double(struct term *fn, struct symbols *symbols) { double a, b, sum; switch (fn->op) { case OP_SYMBOL_LOADAVG: return symbols->loadavg; case OP_SYMBOL_RUNABLE: return symbols->runnable_proc; case OP_SYMBOL_CPUS: return symbols->onumcpus; case OP_SYMBOL_USER: return symbols->user; case OP_SYMBOL_NICE: return symbols->nice; case OP_SYMBOL_SYSTEM: return symbols->system; case OP_SYMBOL_IDLE: return symbols->idle; case OP_SYMBOL_IOWAIT: return symbols->iowait; case OP_SYMBOL_IRQ: return symbols->irq; case OP_SYMBOL_SOFTIRQ: return symbols->softirq; case OP_SYMBOL_STEAL: return symbols->steal; case OP_SYMBOL_GUEST: return symbols->guest; case OP_SYMBOL_GUEST_NICE: return symbols->guest_nice; case OP_SYMBOL_FREEMEM: return symbols->freemem; case OP_SYMBOL_APCR: return symbols->apcr; case OP_SYMBOL_SWAPRATE: return symbols->swaprate; case OP_SYMBOL_MEMINFO: case OP_SYMBOL_VMSTAT: case OP_SYMBOL_CPUSTAT: case OP_SYMBOL_TIME: return get_value(fn); case OP_CONST: return fn->value; case OP_NEG: return -eval_double(fn->left, symbols); case OP_PLUS: return eval_double(fn->left, symbols) + eval_double(fn->right, symbols); case OP_MINUS: return eval_double(fn->left, symbols) - eval_double(fn->right, symbols); case OP_MULT: a = eval_double(fn->left, symbols); b = eval_double(fn->right, symbols); sum = a*b; return sum; /*return eval_double(fn->left, symbols) * eval_double(fn->right, symbols);*/ case OP_DIV: a = eval_double(fn->left, symbols); b = eval_double(fn->right, symbols); sum = a/b; return sum; /*return eval_double(fn->left, symbols) / eval_double(fn->right, symbols); */ case OP_NOT: case OP_AND: case OP_OR: case OP_GREATER: case OP_LESSER: case VAR_LOAD: case VAR_RUN: case VAR_ONLINE: cpuplugd_exit("Invalid term specified: %i\n", fn->op); } return 0; } int eval_term(struct term *fn, struct symbols *symbols) { if (fn == NULL || symbols == NULL) return 0.0; switch (fn->op) { case OP_NOT: return !eval_term(fn->left, symbols); case OP_OR: return eval_term(fn->left, symbols) == 1 || eval_term(fn->right, symbols) == 1; case OP_AND: return eval_term(fn->left, symbols) == 1 && eval_term(fn->right, symbols) == 1; case OP_GREATER: return eval_double(fn->left, symbols) > eval_double(fn->right, symbols); case OP_LESSER: return eval_double(fn->left, symbols) < eval_double(fn->right, symbols); default: return eval_double(fn, symbols) != 0.0; } } s390-tools-2.38.0/dasdfmt/000077500000000000000000000000001502674226300150715ustar00rootroot00000000000000s390-tools-2.38.0/dasdfmt/Makefile000066400000000000000000000007151502674226300165340ustar00rootroot00000000000000include ../common.mak all: dasdfmt libs = $(rootdir)/libdasd/libdasd.a \ $(rootdir)/libvtoc/libvtoc.a \ $(rootdir)/libutil/libutil.a dasdfmt: dasdfmt.o $(libs) install: all $(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8 $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 dasdfmt $(DESTDIR)$(BINDIR) $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 dasdfmt.8 \ $(DESTDIR)$(MANDIR)/man8 clean: rm -f *.o *~ dasdfmt core .PHONY: all install clean s390-tools-2.38.0/dasdfmt/dasdfmt.8000066400000000000000000000154121502674226300166070ustar00rootroot00000000000000.\" Copyright 2017 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH DASDFMT 8 "Apr 2006" "s390-tools" .SH NAME dasdfmt \- formatting of DASD (ECKD) disk drives. .SH SYNOPSIS \fBdasdfmt\fR [\-h] [\-t] [\-v] [\-y] [\-p] [\-P] [\-m \fIstep\fR] .br [\-r \fIcylinder\fR] [\-b \fIblksize\fR] [\-l \fIvolser\fR] [\-d \fIlayout\fR] .br [\-L] [\-V] [\-F] [\-k] [\-C] [\-M \fImode\fR] \fIdevice\fR .SH DESCRIPTION \fBdasdfmt\fR formats a DASD (ECKD) disk drive to prepare it for usage with Linux for S/390. The \fIdevice\fR is the node of the device (e.g. '/dev/dasda'). Any device node created by udev for kernel 2.6 can be used (e.g. '/dev/dasd/0.0.b100/disc'). .br \fBWARNING\fR: Careless usage of \fBdasdfmt\fR can result in \fBLOSS OF DATA\fR. .SH OPTIONS .TP \fB\-h\fR or \fB\-\-help\fR Print usage and exit. .TP \fB\-t\fR or \fB\-\-test\fR Disables any modification of the disk drive. .br \fBdasdfmt\fR just prints out, what it \fBwould\fR do. .TP \fB\-v\fR Increases verbosity. .TP \fB\-y\fR Start formatting without further user-confirmation. .TP \fB\-\-norecordzero\fR Remove permission for subsystem to format write record zero. .br This is an expert option: Per default in recent dasd drivers, subsystems are granted the permission to format write record zero. This option is used to remove this permission. .br .TP \fB\-L\fR or \fB\-\-no_label\fR Omit the writing of a disk label after formatting. .br This makes only sense for the 'ldl' disk layout. .br The '\-L' option has to be specified after the '\-d ldl' option. .br e.g. dasdfmt \-d ldl \-L /dev/... .TP \fB\-V\fR or \fB\-\-version\fR Print version number and exit. .TP \fB\-F\fR or \fB\-\-force\fR Formats the device without performing sanity checking. .TP \fB\-C\fR or \fB\-\-check_host_count\fR Force dasdfmt to check the host access open count to ensure the device is not online on another operating system instance .TP \fB\-d\fR \fIlayout\fR or \fB\-\-disk_layout\fR=\fIlayout\fR Formats the device with compatible disk layout or linux disk layout. \fIlayout\fR is either \fIcdl\fR for the compatible disk layout (default) or \fIldl\fR for the linux disk layout. .br Compatible disk layout means a special handling of the first two tracks of the volume. This enables other S/390 or zSeries operating systems to access this device (e.g. for backup purposes). .TP \fB-p\fR or \fB--progressbar\fR Print a progress bar while formatting. Do not use this option if you are using a 3270 console, running in background or redirecting the output to a file. .TP \fB\-P\fR or \fB\-\-percentage\fR Print one line for each formatted cylinder showing the number of the cylinder and percentage of formatting process. Intended to be used by higher level interfaces. .TP \fB\-m\fR \fIstep\fR or \fB\-\-hashmarks\fR=\fIstep\fR Print a hashmark every \fIstep\fR cylinders. The value \fIstep\fR has to be within range [1,1000], otherwise it will be set to the default, which is 10. .br You can use this option to see the progress of formatting in case you are not able to use the progress bar option \-p, e.g. with a 3270 terminal. .br The value will be at least as big as the \-r or \-\-requestsize value. .br .TP \fB\-M\fR \fImode\fR or \fB\-\-mode\fR=\fImode\fR Specify the \fImode\fR to be used to format the device. Valid modes are: .RS .IP full Format the entire disk with the specified blocksize. (default) .IP quick Format the first two tracks and write label and partition information. .br Use this option for DASD ESE volumes to take the benefits of thin provisioning. In this case, a full space release precedes the formatting step. If this space release fails, then the formatting also fails. Specify the \fB\-\-no\-discard\fR option to omit the space release. .br For non-ESE volumes use this option only if you are sure that the target DASD already contains a regular format with the specified blocksize. A blocksize can optionally be specified using \fB\-b\fR (\fB\-\-blocksize\fR). .IP expand Format all unformatted tracks at the end of the target DASD. This mode assumes that tracks at the beginning of the DASD volume have already been correctly formatted, while a consecutive set of tracks at the end are unformatted. You can use this mode to make added space available for Linux use after dynamically increasing the size of a DASD volume. A blocksize can optionally be specified using \fB\-b\fR (\fB\-\-blocksize\fR). .RE .TP \fB\-\-check\fR Perform a complete format check on a DASD volume. A blocksize can be specified with \fB\-b\fR (\fB\-\-blocksize\fR). .TP \fB\-\-no\-discard\fR Omit a full space release when formatting a thin-provisioned DASD ESE volume. .TP \fB\-r\fR \fIcylindercount\fR or \fB\-\-requestsize\fR=\fIcylindercount\fR Number of cylinders to be processed in one formatting step. The value must be an integer in the range 1 - 255. .br Use this parameter to exploit any available PAV devices. The number of cylinders optimally matches the number of associated devices, counting the base device and all alias devices. .br .TP \fB\-b\fR \fIblksize\fR or \fB\-\-blocksize\fR=\fIblksize\fR Specify blocksize to be used. \fIblksize\fR must be a positive integer and always be a power of two. The recommended blocksize is 4096 bytes. .TP \fB\-l\fR \fIvolser\fR or \fB\-\-label\fR=\fIvolser\fR Specify the volume serial number or volume identifier to be written to disk after formatting. If no label is specified, a sensible default is used. \fIvolser\fR is interpreted as ASCII string and is automatically converted to uppercase and then to EBCDIC. .br e.g. \-l LNX001 or \-\-label=DASD01 .br The \fIvolser\fR identifies by serial number the volume. A volume serial number is 1 through 6 alphanumeric or one of the following special characters: $, #, @, %. Enclose a serial number that contains special characters in apostrophes. If the number is shorter than six characters, it is padded with trailing blanks. .br Do not code a volume serial number as SCRTCH, PRIVAT, or Lnnnnn (L with five numbers); these are used in OS/390 messages to ask the operator to mount a volume. Do not code a volume serial number as MIGRAT, which is used by the OS/390 Hierarchical Storage Manager DFSMShsm for migrated data sets. .br NOTE: Try to avoid using special characters in the volume serial. This may cause problems accessing a disk by volser. .br In case you really have to use special characters, make sure you are using quotes. In addition there is a special handling for the '$' sign. Please specify it using '\\$' if necessary. .br e.g. \-l 'a@b\\$c#' to get A@B$C# .br .TP \fB\-k\fR or \fB\-\-keep_volser\fR Keeps the Volume Serial Number when writing the Volume Label. This is useful if the volume already has a Serial Number that should not be overwritten. .br .SH SEE ALSO .BR fdasd (8) s390-tools-2.38.0/dasdfmt/dasdfmt.c000066400000000000000000001256561502674226300166760ustar00rootroot00000000000000/* * dasdfmt - Format DASD ECKD devices for use by Linux * * Copyright IBM Corp. 1999, 2017 * Copyright Red Hat Inc. 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include "lib/dasd_base.h" #include "lib/dasd_sys.h" #include "lib/util_libc.h" #include "lib/util_opt.h" #include "lib/util_prg.h" #include "lib/util_proc.h" #include "lib/vtoc.h" #include "lib/zt_common.h" #include "dasdfmt.h" #define BUSIDSIZE 8 #define SEC_PER_DAY (60 * 60 * 24) #define SEC_PER_HOUR (60 * 60) static int filedes; static int disk_disabled; static format_data_t format_params; static format_mode_t mode; static char *prog_name; static volatile sig_atomic_t program_interrupt_in_progress; static int reqsize; static const struct util_prg prg = { .desc = "Use dasdfmt to format a DASD ECKD device for use by Linux.\n" "DEVICE is the node of the device (e.g. '/dev/dasda').", .args = "DEVICE", .copyright_vec = { { .owner = "IBM Corp.", .pub_first = 1999, .pub_last = 2017, }, UTIL_PRG_COPYRIGHT_END } }; /* * Global variables for program options and other relevant information */ static struct dasdfmt_globals { dasd_information2_t dasd_info; char *dev_path; /* device path entered by user */ char *dev_node; /* reliable device node determined by dasdfmt */ int verbosity; int testmode; int withoutprompt; int print_progressbar; int print_hashmarks, hashstep; int print_percentage; int force; int writenolabel; int labelspec; int cdl_format; int blksize_specified; int reqsize_specified; int keep_volser; int force_host; int layout_specified; int check; int mode_specified; int ese; int no_discard; } g = { .dasd_info = { 0 }, }; /* Defines for options with no short command */ #define OPT_CHECK 128 #define OPT_NOZERO 129 #define OPT_NODISCARD 130 static struct util_opt opt_vec[] = { UTIL_OPT_SECTION("FORMAT ACTIONS"), { .option = { "mode", required_argument, NULL, 'M' }, .argument = "MODE", .desc = "Specify scope of operation using MODE:\n" " full: Full device (default)\n" " quick: Only the first two tracks\n" " expand: Unformatted tracks at device end", }, { .option = { "check", no_argument, NULL, OPT_CHECK }, .desc = "Perform complete format check on device", .flags = UTIL_OPT_FLAG_NOSHORT, }, UTIL_OPT_SECTION("FORMAT OPTIONS"), { .option = { "blocksize", required_argument, NULL, 'b' }, .argument = "SIZE", .desc = "Format blocks to SIZE bytes (default 4096)", }, { .option = { "disk_layout", required_argument, NULL, 'd' }, .argument = "LAYOUT", .desc = "Specify the disk layout:\n" " cdl: Compatible Disk Layout (default)\n" " ldl: Linux Disk Layout", }, { .option = { "keep_volser", no_argument, NULL, 'k' }, .desc = "Do not change the current volume serial", }, { .option = { "label", required_argument, NULL, 'l' }, .argument = "VOLSER", .desc = "Specify volume serial number", }, { .option = { "no_label", no_argument, NULL, 'L' }, .desc = "Don't write a disk label", }, { .option = { "requestsize", required_argument, NULL, 'r' }, .argument = "NUM", .desc = "Process NUM cylinders in one formatting step", }, { .option = { "norecordzero", no_argument, NULL, OPT_NOZERO }, .desc = "Prevent storage server from modifying record 0", .flags = UTIL_OPT_FLAG_NOSHORT, }, { .option = { "no-discard", no_argument, NULL, OPT_NODISCARD }, .desc = "Do not discard space before formatting", .flags = UTIL_OPT_FLAG_NOSHORT, }, { .option = { NULL, no_argument, NULL, 'y' }, .desc = "Start formatting without further user-confirmation", .flags = UTIL_OPT_FLAG_NOLONG, }, UTIL_OPT_SECTION("DISPLAY PROGRESS"), { .option = { "hashmarks", required_argument, NULL, 'm' }, .argument = "NUM", .desc = "Show a hashmark every NUM cylinders", }, { .option = { "progressbar", no_argument, NULL, 'p' }, .desc = "Show a progressbar", }, { .option = { "percentage", no_argument, NULL, 'P' }, .desc = "Show progress in percent", }, UTIL_OPT_SECTION("MISC"), { .option = { "check_host_count", no_argument, NULL, 'C' }, .desc = "Check if device is in use by other hosts", }, { .option = { "force", no_argument, NULL, 'F' }, .desc = "Format without performing sanity checking", }, { .option = { "test", no_argument, NULL, 't' }, .desc = "Run in dry-run mode without modifying the DASD", }, { .option = { NULL, no_argument, NULL, 'v' }, .desc = "Print verbose messages when executing", .flags = UTIL_OPT_FLAG_NOLONG, }, UTIL_OPT_HELP, { .option = { "version", no_argument, NULL, 'V' }, .desc = "Print version information, then exit", }, UTIL_OPT_END }; /* Report error, free memory, and exit */ static void error(const char *format, ...) { va_list args; fprintf(stderr, "%s: ", prog_name); va_start(args, format); vfprintf(stderr, format, args); va_end(args); fprintf(stderr, "\n"); free(g.dev_node); free(g.dev_path); exit(EXIT_FAILURE); } /* * Helper function to calculate the days, hours, minutes, and seconds * for a given timestamp in seconds */ static void calc_time(time_t time, int *d, int *h, int *m, int *s) { *d = time / SEC_PER_DAY; time %= SEC_PER_DAY; *h = time / SEC_PER_HOUR; time %= SEC_PER_HOUR; *m = time / 60; *s = time % 60; } /* * This function calculates and prints the estimated time of accomplishment. */ static void print_eta(int p_new, int started) { static struct timeval start; struct timeval now; time_t time_elapsed; time_t time_end; int d, h, m, s; static int p_init; int p; if (!started) { gettimeofday(&start, NULL); p_init = p_new; } gettimeofday(&now, NULL); time_elapsed = now.tv_sec - start.tv_sec; /* * We might start somewhere in the middle with an initial percentage * value of i.e. 60%. Therefore we need to calculate the relative * percentage of p_new within that remaining range (100 - 60) in order * to correctly estimate the remaining time. */ if (p_init == 100) p = p_init; else p = 100 * (p_new - p_init) / (100 - p_init); if (p == 0) time_end = time_elapsed; else time_end = time_elapsed * (100 - p) / p; /* Calculate days, hours, minutes, and seconds */ calc_time(time_end, &d, &h, &m, &s); if (p_new == 100) calc_time(time_elapsed, &d, &h, &m, &s); /* Avoid printing leading zeros */ if (d > 0) printf(" [%dd %dh %dm %ds%-4s", d, h, m, s, "]"); else if (h > 0) printf(" [%dh %dm %ds%-7s", h, m, s, "]"); else if (m > 0) printf(" [%dm %ds%-6s", m, s, "]"); else if (s > 0 || p > 50) printf(" [%ds%-5s", s, "]"); else printf(" [--%-1s", "]"); } /* * Draw the progress indicator depending on what command line argument is set. * This can either be a progressbar, hashmarks, or percentage. */ static void draw_progress(int cyl, unsigned int cylinders, int aborted) { static int hashcount; static int started; static int p_old; int p_new = 0; int barlength; int i; if (g.print_progressbar) { printf("cyl %7d of %7d |", cyl, cylinders); p_new = cyl * 100 / cylinders; if (p_new != p_old || !started || aborted) { /* percent value has changed */ p_old = p_new; barlength = cyl * 33 / cylinders; for (i = 1; i <= barlength; i++) printf("#"); for (i = barlength + 1; i <= 33; i++) printf("-"); printf("|%3d%%", p_new); if (aborted) p_new = 100; print_eta(p_new, started); started = 1; } printf("\r"); fflush(stdout); } if (g.print_hashmarks && (cyl / g.hashstep - hashcount) != 0) { printf("#"); fflush(stdout); hashcount++; } if (g.print_percentage) { printf("cyl %7d of %7d |%3d%%\n", cyl, cylinders, cyl * 100 / cylinders); fflush(stdout); } } /* * Helper function for recs_per_track. */ static inline unsigned int ceil_quot(unsigned int d1, unsigned int d2) { return (d1 + (d2 - 1)) / d2; } /* * Calculate records per track depending on the device characteristics. */ static unsigned int recs_per_track(struct dasd_eckd_characteristics *rdc, unsigned int kl, unsigned int dl) { int dn, kn; switch (rdc->dev_type) { case 0x3380: if (kl) return 1499 / (15 + 7 + ceil_quot(kl + 12, 32) + ceil_quot(dl + 12, 32)); else return 1499 / (15 + ceil_quot(dl + 12, 32)); case 0x3390: dn = ceil_quot(dl + 6, 232) + 1; if (kl) { kn = ceil_quot(kl + 6, 232) + 1; return 1729 / (10 + 9 + ceil_quot(kl + 6 * kn, 34) + 9 + ceil_quot(dl + 6 * dn, 34)); } else { return 1729 / (10 + 9 + ceil_quot(dl + 6 * dn, 34)); } case 0x9345: dn = ceil_quot(dl + 6, 232) + 1; if (kl) { kn = ceil_quot(kl + 6, 232) + 1; return 1420 / (18 + 7 + ceil_quot(kl + 6 * kn, 34) + ceil_quot(dl + 6 * dn, 34)); } else { return 1420 / (18 + 7 + ceil_quot(dl + 6 * dn, 34)); } } return 0; } /* * Evaluate errors recognized by format checks and print appropriate error * messages depending on the content of cdata. */ static void evaluate_format_error(format_check_t *cdata, unsigned int heads) { struct dasd_eckd_characteristics *rdc; /* Special blocksize values of the first 3 records of trk 0 of cyl 0 */ const int blksizes_trk0[] = { 24, 144, 80 }; /* Special blocksize value of trk 1 cyl 0 */ const int blksize_trk1 = 96; unsigned int cyl; unsigned int head; unsigned int rpt; unsigned int kl = 0; int blksize = cdata->expect.blksize; if (g.print_progressbar || g.print_hashmarks) printf("\n"); /* * If mode is not QUICK and a device format couldn't be determined, the * device is considered not formatted. * Also, reading record zero will never happen. If the record in error * is 0 nonetheless, the device is not formatted at all as well! */ if ((g.dasd_info.format == DASD_FORMAT_NONE && mode != QUICK) || cdata->rec == 0) { ERRMSG("WARNING: The specified device is not " "formatted at all.\n"); return; } cyl = cdata->unit / heads; head = cyl >> 16; head <<= 4; head |= cdata->unit % heads; /* * Set special expected values for the first 3 records of trk 0 of cyl 0 * and trk 1 of cyl 0 when checking a CDL formatted device. */ if ((cdata->expect.intensity & DASD_FMT_INT_COMPAT) && cyl == 0 && head == 0 && cdata->rec < 4) { kl = 4; blksize = blksizes_trk0[cdata->rec - 1]; } if ((cdata->expect.intensity & DASD_FMT_INT_COMPAT) && cyl == 0 && head == 1) { kl = 44; blksize = blksize_trk1; } rdc = (struct dasd_eckd_characteristics *) &g.dasd_info.characteristics; rpt = recs_per_track(rdc, kl, cdata->expect.blksize); ERRMSG("WARNING: The specified device is not formatted as expected.\n"); switch (cdata->result) { case DASD_FMT_ERR_TOO_FEW_RECORDS: ERRMSG("Too few records (found %d, expected %d) " "at cyl: %d trk: %d rec: %d.\n", cdata->num_records, rpt, cyl, head, cdata->rec); break; case DASD_FMT_ERR_TOO_MANY_RECORDS: ERRMSG("Too many records (found %d, expected %d) " "at cyl: %d trk: %d rec: %d.\n", cdata->num_records, rpt, cyl, head, cdata->rec); break; case DASD_FMT_ERR_BLKSIZE: ERRMSG("Invalid blocksize (found %d, expected %d) " "at cyl: %d trk: %d rec: %d.\n", cdata->blksize, blksize, cyl, head, cdata->rec); break; case DASD_FMT_ERR_RECORD_ID: ERRMSG("Invalid record ID at cyl: %d trk: %d rec: %d.\n", cyl, head, cdata->rec); break; case DASD_FMT_ERR_KEY_LENGTH: ERRMSG("Invalid key length (found %d, expected %d) " "at cyl: %d trk: %d rec: %d.\n", cdata->key_length, kl, cyl, head, cdata->rec); break; } } static void disk_enable(void) { int err; err = dasd_disk_enable(filedes); if (err != 0) error("(prepare device) IOCTL BIODASDENABLE failed: %s", strerror(err)); disk_disabled = 0; } static void disk_disable(const char *device) { int err; err = dasd_disk_disable(device, &filedes); if (err != 0) error("(prepare device) IOCTL BIODASDDISABLE failed: %s", strerror(err)); disk_disabled = 1; } /* * signal handler: * enables the disk again in case of SIGTERM, SIGINT and SIGQUIT */ static void program_interrupt_signal(int sig) { int rc; if (program_interrupt_in_progress) raise(sig); program_interrupt_in_progress = 1; if (disk_disabled) { printf("Re-accessing the device...\n"); disk_enable(); } printf("Rereading the partition table...\n"); rc = dasd_reread_partition_table(g.dev_node, 5); if (rc) { ERRMSG("%s: (signal handler) Re-reading partition table " "failed. (%s)\n", prog_name, strerror(rc)); } else { printf("Exiting...\n"); } signal(sig, SIG_DFL); raise(sig); } /* * Check given device name for blanks and some special characters. * Retrieve reliable device node and store device information in global * dev_node and dev_path accordingly. */ static void get_device_name(int optind, int argc, char *argv[]) { struct util_proc_dev_entry dev_entry; unsigned int maj, min; struct stat dev_stat; if (optind + 1 < argc) error("More than one device specified!"); if (optind >= argc) error("No device specified!"); if (strlen(argv[optind]) >= PATH_MAX) error("device name too long!"); util_asprintf(&g.dev_path, argv[optind]); if (stat(g.dev_path, &dev_stat) != 0) error("Could not get information for device node %s: %s", g.dev_path, strerror(errno)); maj = major(dev_stat.st_rdev); min = minor(dev_stat.st_rdev); if (min & PARTN_MASK) error("Unable to format partition %s. Please specify a device.", g.dev_path); if (util_proc_dev_get_entry(dev_stat.st_rdev, 1, &dev_entry) == 0) { if (strncmp(dev_entry.name, "dasd", 4) != 0) error("Unsupported device type '%s'.", dev_entry.name); } else { printf("%s WARNING: Unable to get driver name for device node %s", prog_name, g.dev_path); } /* Get reliable device node */ util_asprintf(&g.dev_node, "/dev/block/%d:%d", maj, min); } static void get_blocksize(unsigned int *blksize) { int err; err = dasd_get_blocksize(g.dev_node, blksize); if (err != 0) error("the ioctl to get the blocksize of the device failed: %s", strerror(err)); } /* * Check whether a specified blocksize matches the blocksize of the device */ static void check_blocksize(unsigned int blksize) { unsigned int dev_blksize; if (!g.blksize_specified || g.dasd_info.format == DASD_FORMAT_NONE) return; get_blocksize(&dev_blksize); if (dev_blksize != blksize) { warnx("WARNING: Device is formatted with a different blocksize (%d).", dev_blksize); error("Use --mode=full to perform a clean format."); } } /* * Check whether a specified layout matches the layout * a device is formatted with. */ static void check_layout(unsigned int intensity) { char layout[4]; if (!g.layout_specified || g.dasd_info.format == DASD_FORMAT_NONE) return; if ((intensity & DASD_FMT_INT_COMPAT) && g.dasd_info.format == DASD_FORMAT_CDL) return; if (!(intensity & DASD_FMT_INT_COMPAT) && g.dasd_info.format == DASD_FORMAT_LDL) return; if (g.dasd_info.format == DASD_FORMAT_CDL) sprintf(layout, "CDL"); if (g.dasd_info.format == DASD_FORMAT_LDL) sprintf(layout, "LDL"); error("WARNING: Device is formatted with a different layout (%s).", layout); } /* * check for disk type and set some variables (e.g. usage count) */ static void check_disk(void) { int err; bool ro; err = dasd_is_ro(g.dev_node, &ro); if (err != 0) error("the ioctl call to retrieve read/write status information failed: %s", strerror(err)); if (ro) error("Disk is read only!"); if (!g.force) { if (g.dasd_info.open_count > 1) error("Disk in use!"); } if (strncmp(g.dasd_info.type, "ECKD", 4) != 0) { warnx("Unsupported disk type"); error("%s is not an ECKD disk!", g.dev_path); } if (dasd_sys_raw_track_access(g.dev_node)) error("Device '%s' is in raw-track access mode", g.dev_path); } /* * check the volume serial for special * characters and move blanks to the end */ static int check_volser(char *s, int devno) { int i, j; for (i = 0; i < 6; i++) { if ((s[i] < 0x20) || (s[i] > 0x7a) || ((s[i] >= 0x21) && (s[i] <= 0x22)) || /* !" */ ((s[i] >= 0x26) && (s[i] <= 0x2f)) || /* &'()*+,-./ */ ((s[i] >= 0x3a) && (s[i] <= 0x3f)) || /* :;<=>? */ ((s[i] >= 0x5b) && (s[i] <= 0x60))) /* \]^_` */ s[i] = ' '; s[i] = toupper(s[i]); } s[6] = 0x00; for (i = 0; i < 6; i++) { if (s[i] == ' ') for (j = i; j < 6; j++) if (s[j] != ' ') { s[i] = s[j]; s[j] = ' '; break; } } if (s[0] == ' ') { printf("Usage error, switching to default.\n"); sprintf(s, "0X%04x", devno); for (i = 0; i < 6; i++) s[i] = toupper(s[i]); return -1; } return 0; } /* * do some blocksize checks */ static int check_param(char *s, size_t buffsize, format_data_t *data) { int tmp = data->blksize; if ((tmp < 512) || (tmp > 4096)) { strncpy(s, "Blocksize must be one of the following positive " "integers:\n512, 1024, 2048, 4096.", buffsize); if (buffsize > 0) s[buffsize - 1] = '\0'; return -1; } while (tmp > 0) { if ((tmp % 2) && (tmp != 1)) { strncpy(s, "Blocksize must be a power of 2.", buffsize); if (buffsize > 0) s[buffsize - 1] = '\0'; return -1; } tmp /= 2; } return 0; } /* * Retrieve disk information and set cylinders and heads accordingly. */ static void set_geo(unsigned int *cylinders, unsigned int *heads) { struct dasd_eckd_characteristics *characteristics; if (g.verbosity > 0) printf("Retrieving disk geometry...\n"); characteristics = (struct dasd_eckd_characteristics *) &g.dasd_info.characteristics; if (characteristics->no_cyl == LV_COMPAT_CYL && characteristics->long_no_cyl) *cylinders = characteristics->long_no_cyl; else *cylinders = characteristics->no_cyl; *heads = characteristics->trk_per_cyl; } /* * Set VTOC label information */ static void set_label(volume_label_t *vlabel, format_data_t *p, unsigned int cylinders) { char inp_buffer[5]; if (g.writenolabel) { if (cylinders > LV_COMPAT_CYL && !g.withoutprompt) { printf("\n--->> ATTENTION! <<---\n"); printf("You specified to write no labels to a" " volume with more then %u cylinders.\n" "Cylinders above this limit will not be" " accessible as a linux partition!\n" "Type \"yes\" to continue, no will leave" " the disk untouched: ", LV_COMPAT_CYL); if (fgets(inp_buffer, sizeof(inp_buffer), stdin) == NULL) return; if (strcasecmp(inp_buffer, "yes") && strcasecmp(inp_buffer, "yes\n")) { printf("Omitting ioctl call (disk will " "NOT be formatted).\n"); return; } } } else { if (!g.labelspec && !g.keep_volser) { char buf[7]; sprintf(buf, "0X%04x", g.dasd_info.devno); check_volser(buf, g.dasd_info.devno); vtoc_volume_label_set_volser(vlabel, buf); } if (p->intensity & DASD_FMT_INT_COMPAT) { g.cdl_format = 1; vtoc_volume_label_set_label(vlabel, "VOL1"); vtoc_volume_label_set_key(vlabel, "VOL1"); vtoc_set_cchhb(&vlabel->vtoc, 0x0000, 0x0001, 0x01); } else { vtoc_volume_label_set_label(vlabel, "LNX1"); } } } /* * Check whether hashsteps are within the correct interval. */ static void check_hashmarks(void) { if (g.print_hashmarks) { if (g.hashstep < reqsize) g.hashstep = reqsize; if (g.hashstep < 1 || g.hashstep > 1000) { printf("Hashmark increment is not in range <1,1000>, " "using the default.\n"); g.hashstep = 10; } printf("Printing hashmark every %d cylinders.\n", g.hashstep); } } /* * This function checks whether a range of tracks is in regular format * with the specified block size. */ static format_check_t check_track_format(format_data_t *p) { format_check_t cdata = { .expect = { .blksize = p->blksize, .intensity = p->intensity, .start_unit = p->start_unit, .stop_unit = p->stop_unit }, 0 }; char msg[128] = ""; int err; err = dasd_check_format(g.dev_node, &cdata); if (err != 0) { if (err == ENOTTY) { sprintf(msg, "Missing kernel support for format checking"); if (mode == EXPAND) strcat(msg, ". Mode 'expand' cannot be used"); else if (!g.check) strcat(msg, " (--force to override)"); error("%s.", msg); } error("Could not check format: %s", strerror(err)); } return cdata; } /* * Either do the actual format or check depending on the check-value. */ static int process_tracks(unsigned int cylinders, unsigned int heads, format_data_t *format_params) { format_check_t cdata = { .expect = {0}, 0}; format_data_t step = *format_params; unsigned long step_value; unsigned long cur_trk; int cyl = 0, err; check_hashmarks(); cur_trk = format_params->start_unit; while (cur_trk < format_params->stop_unit) { step_value = reqsize * heads - (cur_trk % heads); step.start_unit = cur_trk; if (cur_trk + heads * reqsize >= format_params->stop_unit) step.stop_unit = format_params->stop_unit; else step.stop_unit = cur_trk + step_value - 1; if (g.check) { cdata = check_track_format(&step); if (cdata.result) { cyl = cur_trk / heads + 1; draw_progress(cyl, cylinders, 1); evaluate_format_error(&cdata, heads); break; } } else { err = dasd_format_disk(filedes, &step); if (err != 0) error("the ioctl call to format tracks failed: %s", strerror(err)); } cyl = cur_trk / heads + 1; draw_progress(cyl, cylinders, 0); cur_trk += step_value; } /* We're done, draw the 100% mark */ if (!cdata.result) { cyl = step.stop_unit / heads + 1; draw_progress(cyl, cylinders, 0); printf("\n"); } return cdata.result; } /* * This function checks the format of the entire disk. */ static void check_disk_format(unsigned int cylinders, unsigned int heads, format_data_t *check_params) { check_params->start_unit = 0; check_params->stop_unit = (cylinders * heads) - 1; printf("Checking format of the entire disk...\n"); if (g.testmode) { printf("Test mode active, omitting ioctl.\n"); return; } check_blocksize(check_params->blksize); check_layout(check_params->intensity); /* * If no layout was specified, set the intensity * according to what the layout seems to be. */ if (!g.layout_specified) { if (g.dasd_info.format == DASD_FORMAT_CDL) check_params->intensity |= DASD_FMT_INT_COMPAT; else if (g.dasd_info.format == DASD_FORMAT_LDL) check_params->intensity &= ~DASD_FMT_INT_COMPAT; } if (process_tracks(cylinders, heads, check_params)) error("Use --mode=full to perform a clean format."); printf("Done. Disk is fine.\n"); } /* * ask the user to specify a blocksize */ static format_data_t ask_user_for_blksize(format_data_t params) { char c, str[ERR_LENGTH], buffer[20]; int i, rc; i = params.blksize; do { params.blksize = i; printf("Please enter the blocksize of the formatting [%d]: ", i); if (fgets(buffer, sizeof(buffer), stdin) == NULL) break; rc = sscanf(buffer, "%d%c", ¶ms.blksize, &c); if ((rc == 2) && (c == '\n')) rc = 1; if (rc == -1) rc = 1; /* this happens, if enter is pressed */ if (rc != 1) printf(" -- wrong input, try again.\n"); if (check_param(str, ERR_LENGTH, ¶ms) < 0) { printf(" -- %s\n", str); rc = 0; } } while (rc != 1); return params; } /* * print all information needed to format the device */ static void dasdfmt_print_info(volume_label_t *vlabel, unsigned int cylinders, unsigned int heads, format_data_t *p) { char volser[6], vollbl[4]; printf("Drive Geometry: %d Cylinders * %d Heads = %d Tracks\n", cylinders, heads, (cylinders * heads)); printf("Device Type: %s Provisioned\n", g.ese ? "Thinly" : "Fully"); printf("\nI am going to format the device "); printf("%s in the following way:\n", g.dev_path); printf(" Device number of device : 0x%x\n", g.dasd_info.devno); printf(" Labelling device : %s\n", (g.writenolabel) ? "no" : "yes"); if (!g.writenolabel) { vtoc_volume_label_get_label(vlabel, vollbl); printf(" Disk label : %.4s\n", vollbl); vtoc_volume_label_get_volser(vlabel, volser); printf(" Disk identifier : %.6s\n", volser); } printf(" Extent start (trk no) : %u\n", p->start_unit); printf(" Extent end (trk no) : %u\n", p->stop_unit); printf(" Compatible Disk Layout : %s\n", (p->intensity & DASD_FMT_INT_COMPAT) ? "yes" : "no"); printf(" Blocksize : %d\n", p->blksize); printf(" Mode : %s\n", mode_str[mode]); if (g.ese) { printf(" Full Space Release : %s\n", (g.no_discard || mode == FULL) ? "no" : "yes"); } if (g.testmode) printf("Test mode active, omitting ioctl.\n"); } /* * get volser */ static int dasdfmt_get_volser(char *volser) { unsigned int blksize; volume_label_t vlabel; get_blocksize(&blksize); if ((strncmp(g.dasd_info.type, "ECKD", 4) == 0) && !g.dasd_info.FBA_layout) { /* OS/390 and zOS compatible disk layout */ vtoc_read_volume_label(g.dev_node, g.dasd_info.label_block * blksize, &vlabel); vtoc_volume_label_get_volser(&vlabel, volser); return 0; } else { return -1; } } /* * do all the labeling (volume label and initial VTOC) */ static void dasdfmt_write_labels(volume_label_t *vlabel, unsigned int cylinders, unsigned int heads) { int label_position; struct hd_geometry geo; format4_label_t f4; format5_label_t f5; format7_label_t f7; unsigned int blksize; int rc, fd; void *ipl1_record, *ipl2_record; int ipl1_record_len, ipl2_record_len; if (g.verbosity > 0) printf("Retrieving dasd information... "); get_blocksize(&blksize); /* * Don't rely on the cylinders returned by HDIO_GETGEO, they might be * to small. geo is only used to get the number of sectors, which may * vary depending on the format. */ rc = dasd_get_geo(g.dev_node, &geo); if (rc != 0) error("(write labels) IOCTL HDIO_GETGEO failed: %s", strerror(rc)); if (g.verbosity > 0) printf("ok\n"); /* write empty bootstrap (initial IPL records) */ if (g.verbosity > 0) printf("Writing empty bootstrap...\n"); /* * Note: ldl labels do not contain the key field */ if (g.cdl_format) { /* Prepare copy with key (CDL) */ ipl1_record = &ipl1; ipl2_record = &ipl2; ipl1_record_len = sizeof(ipl1); ipl2_record_len = sizeof(ipl2); } else { /* Prepare copy without key (LDL) */ ipl1_record = ipl1.data; ipl2_record = ipl2.data; ipl1_record_len = sizeof(ipl1.data); ipl2_record_len = sizeof(ipl2.data); } fd = open(g.dev_node, O_RDWR); if (fd < 0) error("Unable to open device '%s': %s", g.dev_path, strerror(errno)); if (lseek(fd, 0, SEEK_SET) != 0) { close(fd); error("lseek command 0 failed: %s", strerror(errno)); } rc = write(fd, ipl1_record, ipl1_record_len); if (rc != ipl1_record_len) { close(fd); error("Writing the bootstrap IPL1 failed, only wrote %d bytes.", rc); } label_position = blksize; rc = lseek(fd, label_position, SEEK_SET); if (rc != label_position) { close(fd); error("lseek command to %i failed: %s", label_position, strerror(errno)); } rc = write(fd, ipl2_record, ipl2_record_len); if (rc != ipl2_record_len) { close(fd); error("Writing the bootstrap IPL2 failed, only wrote %d bytes.", rc); } /* write VTOC */ vtoc_init_format4_label(&f4, geo.cylinders, cylinders, heads, geo.sectors, blksize, g.dasd_info.dev_type); vtoc_init_format5_label(&f5); vtoc_init_format7_label(&f7); vtoc_set_freespace(&f4, &f5, &f7, '+', 0, FIRST_USABLE_TRK, (cylinders * heads - 1), cylinders, heads); label_position = g.dasd_info.label_block * blksize; if (g.verbosity > 0) printf("Writing label...\n"); rc = lseek(fd, label_position, SEEK_SET); if (rc != label_position) { close(fd); error("lseek command to %i failed: %s", label_position, strerror(errno)); } /* * Note: cdl volume labels do not contain the 'formatted_blocks' part * and ldl labels do not contain the key field */ if (g.cdl_format) { rc = write(fd, vlabel, (sizeof(*vlabel) - sizeof(vlabel->formatted_blocks))); } else { vlabel->ldl_version = 0xf2; /* EBCDIC '2' */ vlabel->formatted_blocks = cylinders * heads * geo.sectors; rc = write(fd, (void *)vlabel + sizeof(vlabel->volkey), (sizeof(*vlabel) - sizeof(vlabel->volkey))); } if (((rc != sizeof(*vlabel) - sizeof(vlabel->formatted_blocks)) && g.cdl_format) || ((rc != (sizeof(*vlabel) - sizeof(vlabel->volkey))) && !g.cdl_format)) { close(fd); error("Error writing volume label (%d).", rc); } if (g.verbosity > 0) printf("Writing VTOC... "); label_position = (VTOC_START_CC * heads + VTOC_START_HH) * geo.sectors * blksize; rc = lseek(fd, label_position, SEEK_SET); if (rc != label_position) { close(fd); error("lseek command to %i failed: %s", label_position, strerror(errno)); } /* write VTOC FMT4 DSCB */ rc = write(fd, &f4, sizeof(format4_label_t)); if (rc != sizeof(format4_label_t)) { close(fd); error("Error writing FMT4 label (%d).", rc); } label_position += blksize; rc = lseek(fd, label_position, SEEK_SET); if (rc != label_position) { close(fd); error("lseek to %i failed: %s", label_position, strerror(errno)); } /* write VTOC FMT5 DSCB */ rc = write(fd, &f5, sizeof(format5_label_t)); if (rc != sizeof(format5_label_t)) { close(fd); error("Error writing FMT5 label (%d).", rc); } if ((cylinders * heads) > BIG_DISK_SIZE) { label_position += blksize; rc = lseek(fd, label_position, SEEK_SET); if (rc != label_position) { close(fd); error("lseek to %i failed: %s", label_position, strerror(errno)); } /* write VTOC FMT 7 DSCB (only on big disks) */ rc = write(fd, &f7, sizeof(format7_label_t)); if (rc != sizeof(format7_label_t)) { close(fd); error("Error writing FMT7 label (rc=%d).", rc); } } fsync(fd); close(fd); if (g.verbosity > 0) printf("ok\n"); } /* * This function will search for the beginning of an unformatted area * on the device. It checks selected tracks beforehand and makes sure * that the device is formatted to a certain extent. Otherwise the * process is terminated. */ static void dasdfmt_find_start(unsigned int cylinders, unsigned int heads, format_data_t *format_params) { format_check_t cdata; unsigned int middle; unsigned int left = 2; unsigned int right = (cylinders * heads) - 1; unsigned int first = left; check_blocksize(format_params->blksize); format_params->start_unit = 0; format_params->stop_unit = 4; cdata = check_track_format(format_params); if (cdata.result) { evaluate_format_error(&cdata, heads); error("Use --mode=full to perform a clean format."); } printf("Expansion mode active. Searching for starting position...\n"); while (left <= right) { /* new track number to look at */ middle = left + ((right - left) / 2); format_params->start_unit = middle; format_params->stop_unit = middle; cdata = check_track_format(format_params); if (cdata.blksize != format_params->blksize) { first = middle; right = middle - 1; } else { left = middle + 1; } } if (first == 2 && cdata.blksize == format_params->blksize) error("No unformatted part found, aborting."); printf("Done. Unformatted part starts at track %d.\n", first); /* return format_params with start_unit set to the correct value */ format_params->start_unit = first; } static void dasdfmt_release_space(void) { format_data_t r = { .start_unit = 0, .stop_unit = 0, .intensity = DASD_FMT_INT_ESE_FULL, }; int err = 0; if (!g.ese || g.no_discard) return; printf("Releasing space for the entire device...\n"); err = dasd_release_space(g.dev_node, &r); if (err) error("Could not release space: %s", strerror(err)); } static void dasdfmt_prepare_and_format(unsigned int cylinders, unsigned int heads, format_data_t *p) { format_data_t temp = { .start_unit = 0, .stop_unit = 0, .blksize = p->blksize, .intensity = ((p->intensity & ~DASD_FMT_INT_FMT_NOR0) | DASD_FMT_INT_INVAL) }; int err; if (!(g.withoutprompt && g.verbosity < 1)) printf("Formatting the device. This may take a while " "(get yourself a coffee).\n"); if (g.verbosity > 0) printf("Detaching the device...\n"); disk_disable(g.dev_node); if (g.verbosity > 0) printf("Invalidate first track...\n"); err = dasd_format_disk(filedes, &temp); if (err != 0) error("(invalidate first track) IOCTL BIODASDFMT failed: %s", strerror(err)); /* except track 0 from standard formatting procss */ p->start_unit = 1; process_tracks(cylinders, heads, p); if (g.verbosity > 0) printf("formatting tracks complete...\n"); temp.intensity = p->intensity; if (g.verbosity > 0) printf("Revalidate first track...\n"); err = dasd_format_disk(filedes, &temp); if (err != 0) error("(re-validate first track) IOCTL BIODASDFMT failed: %s", strerror(err)); if (g.verbosity > 0) printf("Re-accessing the device...\n"); disk_enable(); } /* * This function will start the expand format process. */ static void dasdfmt_expand_format(unsigned int cylinders, unsigned int heads, format_data_t *p) { if (!(g.withoutprompt && g.verbosity < 1)) printf("Formatting the device. This may take a while " "(get yourself a coffee).\n"); if (g.verbosity > 0) printf("Detaching the device...\n"); disk_disable(g.dev_node); process_tracks(cylinders, heads, p); if (g.verbosity > 0) printf("Formatting tracks complete...\n"); if (g.verbosity > 0) printf("Re-accessing the device...\n"); disk_enable(); } /* * This function will only format the first two tracks of a DASD. * The rest of the DASD is untouched and left as is. */ static void dasdfmt_quick_format(unsigned int cylinders, unsigned int heads, format_data_t *p) { format_check_t cdata = { .expect = {0}, 0 }; format_data_t tmp = *p; int err; if (g.force) { printf("Skipping format check due to --force.\n"); } else if (g.ese) { printf("Skipping format check due to thin-provisioned device.\n"); } else { check_blocksize(p->blksize); printf("Checking the format of selected tracks...\n"); /* Check device format on the first and last 3 regular tracks */ tmp.start_unit = 2; tmp.stop_unit = 4; cdata = check_track_format(&tmp); if (!cdata.result) { tmp.start_unit = (cylinders * heads) - 3; tmp.stop_unit = (cylinders * heads) - 1; cdata = check_track_format(&tmp); } if (cdata.result) { evaluate_format_error(&cdata, heads); error("Use --mode=full to perform a clean format."); } else { printf("Done. Disk seems fine.\n"); } } if (!(g.withoutprompt && g.verbosity < 1)) printf("Formatting the first two tracks of the device.\n"); /* Disable the device before we do anything */ disk_disable(g.dev_node); /* Now do the actual formatting of our first two tracks */ err = dasd_format_disk(filedes, p); if (err != 0) error("the ioctl to format the device failed: %s", strerror(err)); /* Re-Enable the device so that we can continue working with it */ disk_enable(); } static void do_format_dasd(volume_label_t *vlabel, format_data_t *p, unsigned int cylinders, unsigned int heads) { char inp_buffer[5]; int count, err; p->start_unit = 0; switch (mode) { case FULL: /* all tracks */ p->stop_unit = (cylinders * heads) - 1; break; case QUICK: /* just the first two */ p->stop_unit = 1; break; case EXPAND: /* only the end of the disk */ dasdfmt_find_start(cylinders, heads, p); p->stop_unit = (cylinders * heads) - 1; break; } if (g.verbosity > 0 || !g.withoutprompt || g.testmode) dasdfmt_print_info(vlabel, cylinders, heads, p); count = dasd_get_host_access_count(g.dev_node); if (g.force_host) { if (count > 1) { printf("\n"); warnx("Disk %s is online on OS instances in %d different LPARs.", g.dev_path, count); warnx("Note: Your installation might include z/VM systems that are configured to"); error("automatically vary on disks, regardless of whether they are subsequently used.\n"); } else if (count < 0) { ERRMSG("\nHosts access information not available for disk %s.\n\n", g.dev_path); return; } } else if (count > 1) ERRMSG("\nWARNING:\n" "Disk %s is online on operating system instances in %d different LPARs.\n" "Ensure that the disk is not being used by a system outside your LPAR.\n" "Note: Your installation might include z/VM systems that are configured to\n" "automatically vary on disks, regardless of whether they are subsequently used.\n", g.dev_path, count); if (!g.testmode) { if (!g.withoutprompt) { printf("\n"); if (mode != EXPAND) printf("--->> ATTENTION! <<---\nAll data of " "that device will be lost.\n"); printf("Type \"yes\" to continue, no will leave the " "disk untouched: "); if (fgets(inp_buffer, sizeof(inp_buffer), stdin) == NULL) return; if (strcasecmp(inp_buffer, "yes") && strcasecmp(inp_buffer, "yes\n")) { printf("Omitting ioctl call (disk will " "NOT be formatted).\n"); return; } } switch (mode) { case FULL: dasdfmt_prepare_and_format(cylinders, heads, p); break; case QUICK: dasdfmt_release_space(); dasdfmt_quick_format(cylinders, heads, p); break; case EXPAND: dasdfmt_expand_format(cylinders, heads, p); break; } printf("Finished formatting the device.\n"); if (!(g.writenolabel || mode == EXPAND)) dasdfmt_write_labels(vlabel, cylinders, heads); printf("Rereading the partition table... "); err = dasd_reread_partition_table(g.dev_node, 5); if (err != 0) { ERRMSG("%s: error during rereading the partition " "table: %s.\n", prog_name, strerror(err)); } else { printf("ok\n"); } } } static void eval_format_mode(void) { if (!g.force && g.mode_specified && g.ese && mode == EXPAND) { warnx("WARNING: The specified device is thin-provisioned"); warnx("Format mode 'expand' is not feasible."); error("Use --mode=full or --mode=quick to perform a clean format"); } if (!g.mode_specified) mode = FULL; } /* * Set prog_name to the last component of the program name to be in line with * err() and warn() function (and its derivatives). */ static void set_prog_name(char *s) { char *p = strrchr(s, '/'); if (p == NULL) prog_name = s; else prog_name = p + 1; } int main(int argc, char *argv[]) { volume_label_t vlabel; char old_volser[7]; char str[ERR_LENGTH]; char buf[7]; char *blksize_param_str = NULL; char *reqsize_param_str = NULL; char *hashstep_str = NULL; int rc; unsigned int cylinders, heads; /* Establish a handler for interrupt signals. */ signal(SIGTERM, program_interrupt_signal); signal(SIGINT, program_interrupt_signal); signal(SIGQUIT, program_interrupt_signal); /******************* initialization ********************/ set_prog_name(argv[0]); util_prg_init(&prg); util_opt_init(opt_vec, NULL); /* set default values */ vtoc_volume_label_init(&vlabel); format_params.blksize = DEFAULT_BLOCKSIZE; format_params.intensity = DASD_FMT_INT_COMPAT; /*************** parse parameters **********************/ while (1) { rc = util_opt_getopt_long(argc, argv); switch (rc) { case 'F': g.force = 1; break; case 'd': if (strcasecmp(optarg, "cdl") == 0) { format_params.intensity |= DASD_FMT_INT_COMPAT; if (g.writenolabel) { error("WARNING: using the cdl " "format without writing a " "label doesn't make much " "sense!"); } } else if (strcasecmp(optarg, "ldl") == 0) { format_params.intensity &= ~DASD_FMT_INT_COMPAT; } else { error("%s is not a valid option!", optarg); } g.layout_specified = 1; break; case 'y': g.withoutprompt = 1; break; case OPT_NOZERO: format_params.intensity |= DASD_FMT_INT_FMT_NOR0; break; case 't': g.testmode = 1; break; case 'p': if (!(g.print_hashmarks || g.print_percentage)) g.print_progressbar = 1; break; case 'm': if (!(g.print_progressbar || g.print_percentage)) { hashstep_str = optarg; g.print_hashmarks = 1; } break; case 'P': if (!(g.print_hashmarks || g.print_progressbar)) g.print_percentage = 1; break; case 'v': g.verbosity = 1; break; case 'h': util_prg_print_help(); util_opt_print_help(); exit(EXIT_SUCCESS); case 'V': util_prg_print_version(); exit(EXIT_SUCCESS); case 'l': strncpy(buf, optarg, 6); if (check_volser(buf, 0) < 0) break; vtoc_volume_label_set_volser(&vlabel, buf); g.labelspec = 1; break; case 'L': if (format_params.intensity & DASD_FMT_INT_COMPAT) { error("WARNING: using the cdl format " "without writing a label doesn't " "make much sense!"); } g.writenolabel = 1; break; case 'b': blksize_param_str = optarg; g.blksize_specified = 1; break; case 'r': reqsize_param_str = optarg; g.reqsize_specified = 1; break; case 'k': g.keep_volser = 1; break; case 'C': g.force_host = 1; break; case 'M': if (strcasecmp(optarg, "full") == 0) mode = FULL; else if (strcasecmp(optarg, "quick") == 0) mode = QUICK; else if (strcasecmp(optarg, "expand") == 0) mode = EXPAND; else error("The specified mode '%s' is invalid. " "Consult the man page for more information.", optarg); g.mode_specified = 1; break; case OPT_NODISCARD: g.no_discard = 1; break; case OPT_CHECK: g.check = 1; break; case -1: /* End of options string - start of devices list */ break; default: error("Try '%s --help' for more information.", prog_name); } if (rc == -1) break; /* exit loop if finished */ } CHECK_SPEC_MAX_ONCE(g.blksize_specified, "blocksize"); CHECK_SPEC_MAX_ONCE(g.labelspec, "label"); CHECK_SPEC_MAX_ONCE(g.writenolabel, "omit-label-writing flag"); if (g.blksize_specified) PARSE_PARAM_INTO(format_params.blksize, blksize_param_str, 10, "blocksize"); if (g.reqsize_specified) { PARSE_PARAM_INTO(reqsize, reqsize_param_str, 10, "requestsize"); if (reqsize < 1 || reqsize > 255) error("invalid requestsize %d specified", reqsize); } else { reqsize = DEFAULT_REQUESTSIZE; } if (g.print_hashmarks) PARSE_PARAM_INTO(g.hashstep, hashstep_str, 10, "hashstep"); get_device_name(optind, argc, argv); rc = dasd_get_info(g.dev_node, &g.dasd_info); if (rc != 0) error("the ioctl call to retrieve device information failed: %s", strerror(rc)); g.ese = dasd_sys_ese(g.dev_node); eval_format_mode(); /* Either let the user specify the blksize or get it from the kernel */ if (!g.blksize_specified) { if (!(mode == FULL || g.dasd_info.format == DASD_FORMAT_NONE) || g.check) get_blocksize(&format_params.blksize); else format_params = ask_user_for_blksize(format_params); } if (g.keep_volser) { if (g.labelspec) error("The -k and -l options are mutually exclusive"); if (!(format_params.intensity & DASD_FMT_INT_COMPAT)) error("WARNING: VOLSER cannot be kept when using the ldl format!"); if (dasdfmt_get_volser(old_volser) == 0) vtoc_volume_label_set_volser(&vlabel, old_volser); else error("VOLSER not found on device %s", g.dev_path); } check_disk(); if (check_param(str, ERR_LENGTH, &format_params) < 0) error("%s", str); set_geo(&cylinders, &heads); set_label(&vlabel, &format_params, cylinders); if (g.check) check_disk_format(cylinders, heads, &format_params); else do_format_dasd(&vlabel, &format_params, cylinders, heads); free(g.dev_path); free(g.dev_node); return 0; } s390-tools-2.38.0/dasdfmt/dasdfmt.h000066400000000000000000000061301502674226300166640ustar00rootroot00000000000000/* * dasdfmt - Format DASD ECKD devices for use by Linux * * Copyright IBM Corp. 2002, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef DASDFMT_H #define DASDFMT_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Represents possible format modes that can be specified when formatting * a DASD. */ typedef enum format_mode_t { FULL, /* default mode */ QUICK, /* format only the first 2 tracks */ EXPAND, /* search for unformatted area and format only that part*/ } format_mode_t; static const char mode_str[3][10] = { "Full", "Quick", "Expand" }; /* Report error, free memory, and exit */ static void error(const char *format, ...) __attribute__((__noreturn__, __format__(__printf__, 1, 2))); #define DASD_PARTN_BITS 2 #define PARTN_MASK ((1 << DASD_PARTN_BITS) - 1) #define EXIT_MISUSE 1 #define EXIT_BUSY 2 #define ERR_LENGTH 90 #define DEFAULT_BLOCKSIZE 4096 /* requestsize - number of cylinders in one format step */ #define DEFAULT_REQUESTSIZE 10 #define ERRMSG(x...) {fflush(stdout);fprintf(stderr,x);} #define ERRMSG_EXIT(ec,x...) {fflush(stdout);fprintf(stderr,x);exit(ec);} #define CHECK_SPEC_MAX_ONCE(i,str) \ {if (i>1) ERRMSG_EXIT(EXIT_MISUSE,"%s: " str " " \ "can only be specified once\n",prog_name);} #define PARSE_PARAM_INTO(x,param,base,str) \ {char *endptr=NULL; x=(int)strtol(param,&endptr,base); \ if (*endptr) ERRMSG_EXIT(EXIT_MISUSE,"%s: " str " " \ "is in invalid format\n",prog_name);} typedef struct bootstrap1 { u_int32_t key; u_int32_t data[6]; } __attribute__ ((packed)) bootstrap1_t; typedef struct bootstrap2 { u_int32_t key; u_int32_t data[36]; } __attribute__ ((packed)) bootstrap2_t; /* C9D7D3F1 000A0000 0000000F 03000000 00000001 00000000 00000000 */ static bootstrap1_t ipl1 = { 0xC9D7D3F1, { 0x000A0000, 0x0000000F, 0x03000000, 0x00000001, 0x00000000, 0x00000000 } }; /* C9D7D3F2 07003AB8 40000006 31003ABE 40000005 08003AA0 00000000 06000000 20000000 00000000 00000000 00000400 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 */ static bootstrap2_t ipl2 = { 0xC9D7D3F2, { 0x07003AB8, 0x40000006, 0x31003ABE, 0x40000005, 0x08003AA0, 0x00000000, 0x06000000, 0x20000000, 0x00000000, 0x00000000, 0x00000400, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }; #endif /* DASDFMT_H */ s390-tools-2.38.0/dasdinfo/000077500000000000000000000000001502674226300152365ustar00rootroot00000000000000s390-tools-2.38.0/dasdinfo/Makefile000066400000000000000000000006751502674226300167060ustar00rootroot00000000000000include ../common.mak libs = $(rootdir)/libutil/libutil.a \ $(rootdir)/libdasd/libdasd.a all: dasdinfo dasdinfo: dasdinfo.o $(libs) install: all $(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) \ $(DESTDIR)$(MANDIR)/man8 $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 dasdinfo $(DESTDIR)$(BINDIR) $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 dasdinfo.8 \ $(DESTDIR)$(MANDIR)/man8 clean: rm -f *.o *~ dasdinfo core .PHONY: all install clean s390-tools-2.38.0/dasdinfo/dasdinfo.8000066400000000000000000000056751502674226300171330ustar00rootroot00000000000000.\" Copyright 2017 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH DASDINFO 8 "Febr 2007" "s390-tools" "Linux Administrator's Manual" .SH NAME .B "dasdinfo " \- tool to read unique id from s390 DASD device .SH SYNOPSIS .BI "dasdinfo [\-a] [\-l] [\-u] [\-x] [\-e] {\-i " .BI "| \-b " .BI " | \-d " .BI "}" .sp .BI "dasdinfo [\-h] [\-v]" .SH DESCRIPTION .B dasdinfo displays specific information about a specified DASD device. It is normally called from a udev rule, to provide udev with a unique id string and additional information (type, serial) for an S390 DASD drive. Udev can use this information to create symlinks in /dev/disk/by\-id and /dev/disk/by\-label to the real device node. .SH OPTIONS .TP .BI "\-a|\-\-all" Same as -u -x -l .TP .BI "\-x|\-\-extended\-uid" Print DASD uid This option prints the full uid of the DASD. When z/VM provides two virtual devices that are actually located on the same real device, the first four tokens of the uid will be identical for both devices. z/VM may provide an additional token that can be used to distinguish between different minidisks. You need both support in the Linux kernel and z/VM to receive such an additional token. For z/VM: VM support for the hypervisor injected Special Node Element Qualifier (SNEQ) (or hypervisor injected self-description data) is available by applying the PTFs for VM APAR VM64273 on z/VM 5.2.0 and higher. .TP .BI "\-u|\-\-uid" Print DASD uid without z/VM minidisk token z/VM may provide an additional token that can be used to distinguish between different minidisks (see \-\-extended\-uid option). To remain compatible with systems that were installed on older Linux or z/VM levels, the \-u option will print the uid excluding any z/VM-provided minidisk token. For example, if the extended uid is IBM.75000000092461.e900.10.00000000000037400000000000000000 then the uid is IBM.75000000092461.e900.10. If the extended uid contains no minidisk token, e.g. in an LPAR environment, then both uids are the same. .TP .BI "\-l|\-\-label" Print DASD volume label (volser). .TP .BI "\-i|\-\-busid " Use the bus ID as input parameter, e.g. 0.0.e910. .TP .BI "\-b|\-\-block " Use the block device name as input parameter, e.g. dasdb. .TP .BI "\-d|\-\-devnode " Use a device node as input parameter, e.g. /dev/dasdb. .TP .BI "\-e|\-\-export" Print all values (ID_BUS, ID_TYPE, ID_SERIAL). .TP .BI "\-h|\-\-help" Print usage text. .TP .BI "\-v|\-\-version" Print version number. .SH EXAMPLES dasdinfo \-u \-i 0.0.e910 dasdinfo \-u \-b dasdb dasdinfo \-u \-d /dev/dasdb All three examples should return the same unique ID for the same DASD device, e.g. IBM.75000000092461.e900.10. In case this uid is not available, dasdinfo will return the volume label instead, e.g. 0XE910. .SH SEE ALSO .BR udev (7) .SH AUTHORS Volker Sameske s390-tools-2.38.0/dasdinfo/dasdinfo.c000066400000000000000000000427261502674226300172040ustar00rootroot00000000000000/* * dasdinfo - Display unique DASD ID, either UID or volser * * Copyright IBM Corp. 2007, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/dasd_base.h" #include "lib/util_base.h" #include "lib/util_file.h" #include "lib/util_libc.h" #include "lib/util_opt.h" #include "lib/util_prg.h" #include "lib/zt_common.h" #define RD_BUFFER_SIZE 80 #define TEMP_DEV_MAX_RETRIES 1000 static const struct util_prg prg = { .desc = "Display DASD volume serial number and ID information", .args = "-i BUSID | -b BLOCKDEV | -d DEVNODE", .copyright_vec = { { .owner = "IBM Corp.", .pub_first = 2007, }, UTIL_PRG_COPYRIGHT_END } }; static struct util_opt opt_vec[] = { UTIL_OPT_SECTION("DEVICE"), { .option = { "block", required_argument, NULL, 'b' }, .argument = "BLOCKDEV", .desc = "Block device name, e.g. dasdb", }, { .option = { "devnode", required_argument, NULL, 'd' }, .argument = "DEVNODE", .desc = "Device node, e.g. /dev/dasda", }, { .option = { "busid", required_argument, NULL, 'i' }, .argument = "BUSID", .desc = "Bus ID, e.g. 0.0.e910", }, UTIL_OPT_SECTION("OPTIONS"), { .option = { "label", no_argument, NULL, 'l' }, .desc = "Print DASD volume label (volser)", }, { .option = { "uid", no_argument, NULL, 'u' }, .desc = "Print DASD uid (without z/VM minidisk token)", }, { .option = { "extended-uid", no_argument, NULL, 'x' }, .desc = "Print DASD uid (including z/VM minidisk token)", }, { .option = { "all", no_argument, NULL, 'a' }, .desc = "Same as -u -x -l", }, { .option = { "export", no_argument, NULL, 'e' }, .desc = "Export ID_BUS, ID_TYPE, ID_SERIAL for use in udev", }, UTIL_OPT_HELP, UTIL_OPT_VERSION, UTIL_OPT_END }; /* needed because ftw can not pass arbitrary arguments */ static char *searchbusid; static char *busiddir; struct volume_label { char volkey[4]; char vollbl[4]; char volid[6]; } __attribute__ ((packed)); static char EBCtoASC[256] = { /* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */ 0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F, /* 0x08 -GE -SPS -RPT VT FF CR SO SI */ 0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, /* 0x10 DLE DC1 DC2 DC3 -RES -NL BS -POC */ 0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07, /* 0x18 CAN EM -UBS -CU1 -IFS -IGS -IRS -ITB */ 0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, /* 0x20 -DS -SOS FS -WUS -BYP LF ETB ESC */ 0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B, /* 0x28 -SA -SFE -SM -CSP -MFA ENQ ACK BEL */ 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07, /* 0x30 ---- ---- SYN -IR -PP -TRN -NBS EOT */ 0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04, /* 0x38 -SBS -IT -RFF -CU3 DC4 NAK ---- SUB */ 0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A, /* 0x40 SP RSP ? ---- */ 0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86, /* 0x48 . < ( + | */ 0x87, 0xA4, 0x9B, 0x2E, 0x3C, 0x28, 0x2B, 0x7C, /* 0x50 & ---- */ 0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07, /* 0x58 ? ! $ * ) ; */ 0x8D, 0xE1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAA, /* 0x60 - / ---- ? ---- ---- ---- */ 0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F, /* 0x68 ---- , % _ > ? */ 0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, /* 0x70 --- ---- ---- ---- ---- ---- ---- */ 0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, /* 0x78 * ` : # @ ' = " */ 0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, /* 0x80 * a b c d e f g */ 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x88 h i ---- ---- ---- */ 0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1, /* 0x90 ? j k l m n o p */ 0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, /* 0x98 q r ---- ---- */ 0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07, /* 0xA0 ~ s t u v w x */ 0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, /* 0xA8 y z ---- ---- ---- ---- */ 0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07, /* 0xB0 ^ ---- ? ---- */ 0x5E, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC, /* 0xB8 ---- [ ] ---- ---- ---- ---- */ 0xAB, 0x07, 0x5B, 0x5D, 0x07, 0x07, 0x07, 0x07, /* 0xC0 { A B C D E F G */ 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0xC8 H I ---- ? ---- */ 0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07, /* 0xD0 } J K L M N O P */ 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, /* 0xD8 Q R ---- ? */ 0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98, /* 0xE0 \ S T U V W X */ 0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, /* 0xE8 Y Z ---- ? ---- ---- ---- */ 0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07, /* 0xF0 0 1 2 3 4 5 6 7 */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0xF8 8 9 ---- ---- ? ---- ---- ---- */ 0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07 }; static char *dinfo_ebcdic_dec(char *source, char *target, int l) { int i; for (i = 0; i < l; i++) target[i] = EBCtoASC[(unsigned char)(source[i])]; return target; } static int dinfo_read_dasd_uid(char *uidfile, char *readbuf) { return util_file_read_line(readbuf, RD_BUFFER_SIZE, uidfile); } static int dinfo_read_dasd_vlabel(char *device, struct volume_label *vlabel, char *readbuf) { struct volume_label tmp; int vlsize = sizeof(struct volume_label); dasd_information2_t dasd_info; unsigned long vlabel_start; unsigned int blksize; char vollbl[5]; int f; char *space; if (dasd_get_blocksize(device, &blksize) != 0) { warnx("Unable to figure out block size"); goto error; } if (dasd_get_info(device, &dasd_info) != 0) { warnx("Unable to figure out DASD information"); goto error; } f = open(device, O_RDONLY); if (f < 0) { warnx("Could not open device node"); goto error; } vlabel_start = dasd_info.label_block * blksize; if (lseek(f, vlabel_start, SEEK_SET) < 0) goto error_close; bzero(vlabel, vlsize); if (read(f, vlabel, vlsize) != vlsize) { warnx("Could not read volume label"); goto error_close; } if (dasd_info.FBA_layout) { bzero(&tmp, vlsize); memcpy(&tmp, vlabel, vlsize); memcpy(vlabel->vollbl, &tmp, vlsize - 4); } close(f); bzero(readbuf, 7); bzero(vollbl, 5); strncpy(vollbl, vlabel->vollbl, 4); dinfo_ebcdic_dec(vollbl, vollbl, 4); if ((strncmp(vollbl, "VOL1", 4) == 0) || (strncmp(vollbl, "LNX1", 4) == 0) || (strncmp(vollbl, "CMS1", 4) == 0)) { strncpy(readbuf, vlabel->volid, 6); dinfo_ebcdic_dec(readbuf, readbuf, 6); space = strchr(readbuf, ' '); if (space) *space = 0; } else { strcpy(readbuf, ""); } return 0; error_close: close(f); error: return -1; } static void *dinfo_malloc(size_t size) { void *result; result = malloc(size); if (result == NULL) warnx("Could not allocate %lu bytes of memory", size); return result; } static char *dinfo_make_path(char *dirname, char *filename) { char *result; size_t len; len = strlen(dirname) + strlen(filename) + 2; result = (char *)dinfo_malloc(len); if (result == NULL) return NULL; sprintf(result, "%s/%s", dirname, filename); return result; } static int dinfo_create_devnode(dev_t dev, char **devno) { char *result; char *pathname[] = { "/dev", getenv("TMPDIR"), "/tmp", getenv("HOME"), ".", "/"}; char filename[] = "dasdinfo0000"; mode_t mode; unsigned int path; int retry; int rc; int fd; mode = S_IFBLK | S_IRWXU; /* Try several locations for the temporary device node. */ for (path = 0; path < ARRAY_SIZE(pathname); path++) { if (pathname[path] == NULL) continue; for (retry = 0; retry < TEMP_DEV_MAX_RETRIES; retry++) { sprintf(filename, "dasdinfo%04d", retry); result = dinfo_make_path(pathname[path], filename); if (result == NULL) return -1; rc = mknod(result, mode, dev); if (rc == 0) { /* Need this test to cover * 'nodev'-mounted * filesystems. */ fd = open(result, O_RDONLY); if (fd != -1) { close(fd); *devno = result; return 0; } remove(result); retry = TEMP_DEV_MAX_RETRIES; } else if (errno != EEXIST) { retry = TEMP_DEV_MAX_RETRIES; } free(result); } } warnx("Error: Unable to create temporary device node"); return -1; } static void dinfo_free_devnode(char *device) { if (remove(device)) warnx("Warning: Could not remove temporary file %s", device); } static int dinfo_extract_dev(dev_t *dev, char *str) { char tmp[RD_BUFFER_SIZE]; char *p = NULL; int ma, mi; bzero(tmp, RD_BUFFER_SIZE); util_strlcpy(tmp, str, RD_BUFFER_SIZE); p = strchr(tmp, ':'); if (p == NULL) { warnx("Error: unable to extract major/minor"); return -1; } *p = '\0'; ma = atoi(tmp); mi = atoi(p + sizeof(char)); *dev = makedev(ma, mi); return 0; } static int dinfo_get_dev_from_blockdev(char *blockdev, dev_t *dev) { char *readbuf = NULL; readbuf = dinfo_malloc(RD_BUFFER_SIZE); if (!readbuf) { warnx("Error: Not enough memory to allocate readbuffer"); return -1; } if (util_file_read_line(readbuf, RD_BUFFER_SIZE, "/sys/block/%s/dev", blockdev) < 0) return -1; if (dinfo_extract_dev(dev, readbuf) != 0) return -1; return 0; } static int dinfo_is_busiddir(const char *fpath, const struct stat *UNUSED(sb), int tflag, struct FTW *ftwbuf) { char *tempdir; char linkdir[128]; ssize_t i; if (tflag != FTW_D || (strncmp((fpath + ftwbuf->base), searchbusid, strlen(searchbusid)) != 0)) return FTW_CONTINUE; /* * ensure that the found entry is a busid and not a * subchannel ID * for large systems subchannel IDs may look like busids */ if (asprintf(&tempdir, "%s/driver", fpath) < 0) return -1; i = readlink(tempdir, linkdir, 128); free(tempdir); if ((i < 0) || (i >= 128)) return -1; /* append '\0' because readlink returns non zero terminated string */ tempdir[i + 1] = '\0'; if (strstr(linkdir, "dasd") == NULL) return FTW_CONTINUE; free(busiddir); busiddir = strdup(fpath); if (busiddir == NULL) return -1; return FTW_STOP; } static int dinfo_find_entry(const char *dir, const char *searchstring, char type, char **result) { DIR *directory = NULL; struct dirent *dir_entry = NULL; directory = opendir(dir); if (directory == NULL) return -1; while ((dir_entry = readdir(directory)) != NULL) { /* compare if the found entry has exactly the same name and type * as searched */ if ((strncmp(dir_entry->d_name, searchstring, strlen(searchstring)) == 0) && (dir_entry->d_type & type)) { *result = strdup(dir_entry->d_name); if (*result == NULL) goto out; closedir(directory); return 0; /* found */ } } out: closedir(directory); return -1; /* nothing found or error */ } static int dinfo_get_blockdev_from_busid(char *busid, char **blkdev) { int flags = FTW_PHYS; /* do not follow links */ int rc = -1; char *tempdir = NULL; char *result = NULL; char *sysfsdir = "/sys/devices/"; /* dinfo_is_devnode needs to know the busid */ searchbusid = busid; if (nftw(sysfsdir, dinfo_is_busiddir, 200, flags) != FTW_STOP) goto out; /* * new sysfs: busid directory contains a directory 'block' * which contains a directory 'dasdXXX' */ rc = dinfo_find_entry(busiddir, "block", DT_DIR, &result); if (rc == 0) { if (asprintf(&tempdir, "%s/%s/", busiddir, result) < 0) { rc = -1; goto out2; } rc = dinfo_find_entry(tempdir, "dasd", DT_DIR, blkdev); } else { /* * old sysfs: entry for busiddir contain a link * 'block:dasdXXX' */ rc = dinfo_find_entry(busiddir, "block:", DT_LNK, &result); if (rc != 0) goto out2; *blkdev = strdup(strchr(result, ':') + 1); if (*blkdev == NULL) rc = -1; } out: free(tempdir); out2: free(busiddir); free(result); return rc; } static int dinfo_get_uid_from_devnode(char **uidfile, char *devnode) { struct stat stat_buffer; char stat_dev[RD_BUFFER_SIZE]; char *readbuf; DIR *directory = NULL; struct dirent *dir_entry = NULL; int rc = 0; if (stat(devnode, &stat_buffer) != 0) { warnx("Error: could not stat %s", devnode); return -1; } sprintf(stat_dev, "%d:%d", major(stat_buffer.st_rdev), minor(stat_buffer.st_rdev)); directory = opendir("/sys/block/"); if (directory == NULL) { warnx("Error: could not open directory /sys/block"); return -1; } readbuf = dinfo_malloc(RD_BUFFER_SIZE); if (!readbuf) { warnx("Error: Not enough memory to allocate readbuffer"); return -1; } while ((dir_entry = readdir(directory)) != NULL) { if (util_file_read_line(readbuf, RD_BUFFER_SIZE, "/sys/block/%s/dev", dir_entry->d_name) < 0) continue; if (strncmp(stat_dev, readbuf, MAX(strlen(stat_dev), strlen(readbuf) - 1)) == 0) { rc = snprintf(*uidfile, RD_BUFFER_SIZE, "/sys/block/%s/device/uid", dir_entry->d_name); if (rc >= RD_BUFFER_SIZE) { fprintf(stderr, "Error: Device name was truncated\n"); return -1; } break; } } closedir(directory); return 0; } int main(int argc, char *argv[]) { struct utsname uname_buf; int version, release; char *uidfile = NULL; char *device = NULL; char *readbuf = NULL; dev_t dev; int export = 0; int c; int print_uid = 0; int print_extended_uid = 0; int print_vlabel = 0; char *blockdev = NULL; char *busid = NULL; char *devnode = NULL; struct volume_label vlabel; char *srchuid; int i, rc = 0; util_prg_init(&prg); util_opt_init(opt_vec, NULL); while (1) { c = util_opt_getopt_long(argc, argv); if (c == -1) break; switch (c) { case 'a': print_uid = 1; print_vlabel = 1; print_extended_uid = 1; break; case 'u': print_uid = 1; break; case 'x': print_extended_uid = 1; break; case 'l': print_vlabel = 1; break; case 'i': busid = strdup(optarg); break; case 'b': blockdev = strdup(optarg); break; case 'd': devnode = strdup(optarg); break; case 'e': export = 1; break; case 'h': util_prg_print_help(); util_opt_print_help(); exit(EXIT_SUCCESS); case 'v': util_prg_print_version(); exit(EXIT_SUCCESS); default: fprintf(stderr, "Try 'dasdinfo --help' for more " "information.\n"); exit(1); } } uname(&uname_buf); sscanf(uname_buf.release, "%d.%d", &version, &release); if (strcmp(uname_buf.sysname, "Linux") || version < 2 || (version == 2 && release < 6)) { warnx("%s %d.%d is not supported", uname_buf.sysname, version, release); exit(1); } if (!busid && !blockdev && !devnode) { warnx("Error: please specify a device using either -b, -i or -d"); exit(1); } if ((busid && blockdev) || (busid && devnode) || (blockdev && devnode)) { warnx("Error: please specify device only once, either -b, -i or -d"); exit(1); } if (!print_uid && !print_extended_uid && !print_vlabel) { warnx("Error: no action specified (e.g. -u)"); exit(1); } readbuf = dinfo_malloc(RD_BUFFER_SIZE); uidfile = dinfo_malloc(RD_BUFFER_SIZE); if (!(readbuf && uidfile)) exit(1); /* try to read the uid attribute */ if (busid) { sprintf(uidfile, "/sys/bus/ccw/devices/%s/uid", busid); } else if (blockdev) { sprintf(uidfile, "/sys/block/%s/device/uid", blockdev); } else if (devnode) { if (dinfo_get_uid_from_devnode(&uidfile, devnode) != 0) goto error; } if (export) { printf("ID_BUS=ccw\n"); printf("ID_TYPE=disk\n"); } if (print_uid) { if (dinfo_read_dasd_uid(uidfile, readbuf) == 0) { /* look for the 4th '.' and cut there */ srchuid = readbuf - 1; for (i = 0; i < 4; ++i) { srchuid = index(srchuid + 1, '.'); if (!srchuid) break; } if (srchuid) srchuid[0] = '\0'; if (export) printf("ID_UID=%s\n", readbuf); else printf("%s\n", readbuf); if (!print_vlabel && !print_extended_uid) goto out; } } if (print_extended_uid) { if (dinfo_read_dasd_uid(uidfile, readbuf) == 0) { if (export) printf("ID_XUID=%s\n", readbuf); else printf("%s\n", readbuf); if (!print_vlabel) goto out; } } /* there is no uid, try to read the volume serial */ if (busid) { char *blockdev_name = NULL; if (dinfo_get_blockdev_from_busid(busid, &blockdev_name) != 0) goto error; if (dinfo_get_dev_from_blockdev(blockdev_name, &dev) != 0) goto error; if (dinfo_create_devnode(dev, &device) != 0) goto error; free(blockdev_name); } else if (blockdev) { if (dinfo_get_dev_from_blockdev(blockdev, &dev) != 0) goto error; if (dinfo_create_devnode(dev, &device) != 0) goto error; } else if (devnode) { device = dinfo_malloc(RD_BUFFER_SIZE); if (!device) exit(1); strcpy(device, devnode); } if (dinfo_read_dasd_vlabel(device, &vlabel, readbuf) == 0) { if (export) printf("ID_SERIAL=%s\n", readbuf); else printf("%s\n", readbuf); goto out; } error: warnx("Error: could not read unique DASD ID"); rc = 1; out: if (device && (busid || blockdev)) dinfo_free_devnode(device); free(uidfile); free(device); free(readbuf); exit(rc); } s390-tools-2.38.0/dasdview/000077500000000000000000000000001502674226300152555ustar00rootroot00000000000000s390-tools-2.38.0/dasdview/Makefile000066400000000000000000000016461502674226300167240ustar00rootroot00000000000000include ../common.mak libs = $(rootdir)/libdasd/libdasd.a \ $(rootdir)/libzds/libzds.a \ $(rootdir)/libvtoc/libvtoc.a \ $(rootdir)/libutil/libutil.a ifneq (${HAVE_CURL},0) check_dep: $(call check_dep, \ "dasdview", \ "curl/curl.h", \ "curl-devel or libcurl-dev", \ "HAVE_CURL=0") BUILDTARGET = check_dep CURL_CFLAGS = $(shell $(PKG_CONFIG) --silence-errors --cflags libcurl) CURL_LDLIBS = $(shell $(PKG_CONFIG) --silence-errors --libs libcurl) endif # HAVE_CURL BUILDTARGET += dasdview ALL_CPPFLAGS += -DSYSFS ALL_CFLAGS += $(CURL_CFLAGS) LDLIBS += $(CURL_LDLIBS) all: $(BUILDTARGET) dasdview: dasdview.o $(libs) install: all $(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8 $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 dasdview $(DESTDIR)$(BINDIR) $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 dasdview.8 \ $(DESTDIR)$(MANDIR)/man8 clean: rm -f *.o *~ dasdview core .PHONY: all install clean s390-tools-2.38.0/dasdview/dasdview.8000066400000000000000000000137221502674226300171610ustar00rootroot00000000000000.\" Copyright 2017 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH DASDVIEW 8 "Apr 2006" "s390-tools" .SH NAME dasdview \- Display DASD and VTOC information and dump the content of a DASD to the console. .SH SYNOPSIS \fBdasdview\fR [\-h] [\-v] .br [\-b \fIbegin\fR] [\-s \fIsize\fR] [\-1|\-2] .br [\-i] [\-x] [\-j] [\-c] .br [\-l] [\-t {\fIinfo\fR|\fIf1\fR|\fIf3\fR|\fIf4\fR|\fIf5\fR|\fIf7\fR|\fIf8\fR|\fIf9\fR}] .br \fIdevice\fR .SH DESCRIPTION \fBdasdview\fR prints you some useful information of your disks to the console. You can display a disk dump by specifying start point and offset and you can print the volume label and VTOC entries. The \fIdevice\fR is the node of the device (e.g. '/dev/dasda'). Any device node created by udev for kernel 2.6 can be used (e.g. '/dev/dasd/0.0.b100/disc'). DASD devices in raw_track_access mode are supported and detected automatically. When in raw_track_access mode, the same basic functions are available as in the regular mode, but the output may have a slightly different layout: .IP \(bu 2 The disk dump functions (\fB\-b\fR and \fB\-s\fR) print the count, key and data information for the whole track, and not just the contents of the data areas. .IP \(bu 2 The VTOC listing (\fB\-t\fR) print all specified DSCBs in the same format as in the regular mode, but in the sequence as they appear in the VTOC. The \fB\-t info\fR overview contains more details for each data set than in the regular mode, to support the larger variety of data set layouts. .SH OPTIONS .TP \fB\-h\fR or \fB\-\-help\fR Print usage and exit. .TP \fB\-v\fR or \fB\-\-version\fR Print version number and exit. .TP \fB\-b\fR \fIbegin\fR or \fB\-\-begin=\fR\fIbegin\fR Print a disk dump to the console, starting with \fIbegin\fR. The content of the disk will be displayed in hexadecimal numbers, ASCII text and EBCDIC text. If no size is specified dasdview will take the default size. The variable \fIbegin\fR can be specified in one of the following ways: .br begin[k|m|b|t|c] .br The default for \fIbegin\fR is \fI0\fR. .br \fBNote 1:\fR dasdview will show you the content of your disk using the DASD driver. If this driver decides to hide or add some parts of the disk, you have to live with it. This happens for example with the first two tracks of a cdl-formatted disk. In this case the DASD driver fills up shorter blocks with zeros to have a constant blocksize. And all applications, including dasdview, believe it. .br \fBNote 2:\fR In raw_track_access mode \fIbegin\fR must be aligned to track boundaries. A simple way to do that is to specify a track or cylinder as starting point. .br examples: .br \-b 32 --> start printing at Byte 32 .br \-b 32k --> start printing at kByte 32 .br \-b 32m --> start printing at MByte 32 .br \-b 32b --> start printing at block 32 .br \-b 32t --> start printing at track 32 .br \-b 32c --> start printing at cylinder 32 .TP \fB\-s\fR \fIsize\fR or \fB\-\-size=\fR\fIsize\fR Print a disk dump to the console, starting with \fIbegin\fR, specified with the \fB\-b\fR option and size \fIsize\fR. The content of the disk will be displayed in hexadecimal numbers, ASCII text and EBCDIC text. If no start value is specified dasdview will take the default start value. The variable \fIsize\fR can be specified in one of the following ways: .br size[k|m|b|t|c] .br \fBNote:\fR In raw_track_access mode \fIsize\fR must be a multiple of one track. A simple way to do that is to specify the size in tracks or cylinders. .br The default for \fIsize\fR is \fI128\fR in regular mode and \fI1t\fR in raw_track_access mode. .br examples: .br \-s 16 --> use a 16 Byte size .br \-s 16k --> use a 16 kByte size .br \-s 16m --> use a 16 MByte size .br \-s 16b --> use a 16 block size .br \-s 16t --> use a 16 track size .br \-s 16c --> use a 16 cylinder size .TP \fB\-1\fR This option tells dasdview to print the disk dump using format 1. This means you will get 16 Bytes per line in hex, ascii and ebcdic. There is no line number. .br The \fB\-1\fR option makes only sense with the \fB\-b\fR and/or the \fB\-s\fR options. .br This is the default. .TP \fB\-2\fR This option tells dasdview to print the disk dump using format 2. This means you will get 8 Bytes per line in hex, ascii and ebcdic. And in addition a line number and a decimal and hexadecimal byte count will be printed. .br The \fB\-2\fR option makes only sense with the \fB\-b\fR and/or the \fB\-s\fR options. In raw_track_access mode this format is not supported and the option will be ignored. .TP \fB\-i\fR or \fB\-\-info\fR Print some useful information (e.g. device node/number/type or geometry data). When running dasdview on a kernel 2.6 based distribution the busid is printed instead of the device number. .TP \fB\-x\fR or \fB\-\-extended\fR Print some more DASD information (e.g. open count, subchannel identifier). .TP \fB\-j\fR or \fB\-\-volser\fR Print volume serial number (volume identifier). .TP \fB\-l\fR or \fB\-\-label\fR Print the volume label. .TP \fB\-c\fR or \fB\-\-characteristic\fR Print some information about the device e.g. if it is encrypted. .TP \fB\-t\fR \fIspec\fR or \fB\-\-vtoc=\fR\fIspec\fR Print the VTOC (table of content) or single VTOC entries to the console. \fIspec\fR can be one of the following strings: .br \fIinfo\fR: .br Gives you a VTOC overview. You will see what other S/390 or zSeries operating systems would see (e.g. data set names and sizes). .br \fIf1\fR: .br Print the content of all format 1 DSCBs. .br \fIf3\fR: .br Print the content of all format 3 DSCBs. .br \fIf4\fR: .br Print the content of the format 4 DSCB. .br \fIf5\fR: .br Print the content of the format 5 DSCB. .br \fIf7\fR: .br Print the content of the format 7 DSCB. .br \fIf8\fR: .br Print the content of all format 8 DSCBs. .br \fIf9\fR: .br Print the content of all format 9 DSCBs. .br \fIall\fR: .br Print the content of all DSCBs. s390-tools-2.38.0/dasdview/dasdview.c000066400000000000000000002104501502674226300172310ustar00rootroot00000000000000/* * dasdview - Display DASD and VTOC information or dump the contents of a DASD * * Copyright IBM Corp. 2001, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #define _LARGEFILE64_SOURCE /* needed for unistd.h */ #define _FILE_OFFSET_BITS 64 /* needed for unistd.h */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/dasd_base.h" #include "lib/dasd_sys.h" #include "lib/libzds.h" #include "lib/util_base.h" #include "lib/util_libc.h" #include "lib/util_opt.h" #include "lib/util_prg.h" #include "lib/util_sys.h" #include "lib/vtoc.h" #include "lib/zt_common.h" #include "dasdview.h" /* Characters per line */ #define DASDVIEW_CPL 16 static const struct util_prg prg = { .desc = "Display DASD and VTOC information and dump the content of " "a DASD to the console.\n" "DEVICE is the node of the device (e.g. '/dev/dasda').", .args = "DEVICE", .copyright_vec = { { .owner = "IBM Corp.", .pub_first = 2001, .pub_last = 2006, }, UTIL_PRG_COPYRIGHT_END } }; static struct util_opt opt_vec[] = { UTIL_OPT_SECTION("DUMP OPTIONS"), { .option = { NULL, no_argument, NULL, '1' }, .desc = "Show DASD content in short Hex/EBCDIC/ASCII format", .flags = UTIL_OPT_FLAG_NOLONG, }, { .option = { NULL, no_argument, NULL, '2' }, .desc = "Show DASD content in detailed Hex/EBCDIC/ASCII format", .flags = UTIL_OPT_FLAG_NOLONG, }, { .option = { "begin", required_argument, NULL, 'b' }, .argument = "BEGIN", .desc = "Specify start of dump in kilobytes (suffix k), " "megabytes (m), blocks (b), tracks (t), or cylinders (c)", }, { .option = { "size", required_argument, NULL, 's' }, .argument = "SIZE", .desc = "Specify size of dump in kilobytes (suffix k), " "megabytes (m), blocks (b), tracks (t), or cylinders (c)", }, UTIL_OPT_SECTION("MISC"), { .option = { "characteristic", no_argument, NULL, 'c' }, .desc = "Print the characteristics of a device", }, { .option = { "info", no_argument, NULL, 'i' }, .desc = "Print general DASD information and geometry", }, { .option = { "volser", no_argument, NULL, 'j' }, .desc = "Print the volume serial number", }, { .option = { "label", no_argument, NULL, 'l' }, .desc = "Print information about the volume label", }, { .option = { "vtoc", required_argument, NULL, 't' }, .argument = "SPEC", .desc = "Print the table of content (VTOC)", }, { .option = { "extended", no_argument, NULL, 'x' }, .desc = "Print extended DASD information", }, UTIL_OPT_HELP, UTIL_OPT_VERSION, UTIL_OPT_END }; /* * Generate and print an error message based on the formatted * text string FMT and a variable amount of extra arguments. */ static void zt_error_print(const char *fmt, ...) { va_list args; va_start(args, fmt); vsnprintf(error_str, ERROR_STRING_SIZE, fmt, args); va_end(args); fprintf(stderr, "Error: %s\n", error_str); } /* * replace special characters with dots and question marks */ static void dot(char label[]) { int i; char c; for (i = 0; i < 16; i++) { c = label[i]; if (c <= 0x20) label[i] = '?'; if (c == 0x00) label[i] = '.'; if (c == 0x60) label[i] = '?'; if (c >= 0x7f) label[i] = '?'; } } static void dasdview_get_info(dasdview_info_t *info) { struct dasd_eckd_characteristics *characteristics; int err; err = dasd_get_geo(info->device, &info->geo); if (err != 0) { /* Test for unknown device in the first call to libdasd to avoid * spitting out two different error messages to the user */ if (err != EBADF) zt_error_print("dasdview: " "Could not retrieve geo information!\n"); exit(EXIT_FAILURE); } if (dasd_get_blocksize(info->device, &info->blksize) != 0) { zt_error_print("dasdview: " "Could not retrieve blocksize information!\n"); exit(EXIT_FAILURE); } if (dasd_get_info(info->device, &info->dasd_info) != 0) { zt_error_print("dasdview: " "Could not retrieve disk information!\n"); exit(EXIT_FAILURE); } characteristics = (struct dasd_eckd_characteristics *) &info->dasd_info.characteristics; if (characteristics->no_cyl == LV_COMPAT_CYL && characteristics->long_no_cyl) info->hw_cylinders = characteristics->long_no_cyl; else info->hw_cylinders = characteristics->no_cyl; if (util_sys_get_dev_addr(info->device, info->busid) != 0) info->busid_valid = 0; else info->busid_valid = 1; info->raw_track_access = dasd_sys_raw_track_access(info->device); } static void dasdview_parse_input(unsigned long long *p, dasdview_info_t *info, char *s) { unsigned long long l; char *endp; char suffix; l = strtoull(s, &endp, 0); if ((endp == s) || ((l + 1) == 0)) goto error; if (*endp) { if (!strchr("kmtbcKMTBC", *endp) || *(endp + 1)) goto error; suffix = tolower(*endp); } else { suffix = 0; } if (info->raw_track_access) { switch (suffix) { case 't': l *= RAWTRACKSIZE; break; case 'c': l *= (unsigned long long)info->geo.heads * RAWTRACKSIZE; break; case 0: if (l % RAWTRACKSIZE) { zt_error_print("dasdview: only full tracks can" " be accessd on devices with " " raw_track_access enabled.\n", s); goto error; } break; default: zt_error_print("dasdview: only types t and c are" " allowed for devices with" " raw_track_access enabled.\n", s); goto error; } } else { switch (suffix) { case 'k': l *= 1024LL; break; case 'm': l *= 1024LL * 1024LL; break; case 't': l *= (unsigned long long)info->blksize * (unsigned long long)info->geo.sectors; break; case 'b': l *= (unsigned long long)info->blksize; break; case 'c': l *= (unsigned long long)info->blksize * (unsigned long long)info->geo.sectors * (unsigned long long)info->geo.heads; break; default: break; } } *p = l; return; error: zt_error_print("dasdview: usage error\n" "%s is not a valid begin/size value!", s); exit(EXIT_FAILURE); } /* * Print general DASD information. */ static void dasdview_print_general_info(dasdview_info_t *info) { printf("\n--- general DASD information -----------------" "---------------------------------\n"); printf("device node : %s\n", info->device); #ifdef SYSFS struct utsname buf; unsigned char a, b, c; char suffix[sizeof(buf.release)]; int rc; rc = uname(&buf); if (!rc) { sscanf(buf.release, "%c.%c.%c-%s", &a, &b, &c, suffix); if (KERNEL_VERSION(2, 5, 0) <= KERNEL_VERSION(a, b, c)) { if (info->busid_valid) printf("busid : %s\n", info->busid); else printf("busid :" " \n"); } else { #endif printf("device number : hex %x \tdec %d\n", info->dasd_info.devno, info->dasd_info.devno); #ifdef SYSFS } } #endif printf("type : %4s\n", info->dasd_info.type); printf("device type : hex %x \tdec %d\n", info->dasd_info.dev_type, info->dasd_info.dev_type); printf("\n--- DASD geometry ----------------------------" "---------------------------------\n"); printf("number of cylinders : hex %x \tdec %d\n", info->hw_cylinders, info->hw_cylinders); printf("tracks per cylinder : hex %x \tdec %d\n", info->geo.heads, info->geo.heads); printf("blocks per track : hex %x \tdec %d\n", info->geo.sectors, info->geo.sectors); printf("blocksize : hex %x \tdec %d\n", info->blksize, info->blksize); } /* * Loop over the given character array and HEXdump the content. */ static inline void dasdview_dump_array(char *name, int size, unsigned char *addr) { int i; for (i = 0; i < size; i++) { if (i % DASDVIEW_CPL == 0) { if (i == 0) printf("%-23.23s: ", name); else printf("\n%25s", ""); } else { if (i % 8 == 0) printf(" "); if (i % 4 == 0) printf(" "); } printf("%02x", addr[i]); } printf("\n"); } /* * Print extended DASD information. */ static void dasdview_print_extended_info(dasdview_info_t *info) { unsigned int i; struct dasd_information2_t *dasd_info; struct { unsigned int mask; char *name; } flist[2] = { {DASD_FEATURE_READONLY, "ro" }, {DASD_FEATURE_USEDIAG, "diag"} }; dasd_info = &info->dasd_info; printf("\n--- extended DASD information ----------------" "---------------------------------\n"); printf("real device number : hex %x \tdec %d\n", dasd_info->real_devno, dasd_info->real_devno); printf("subchannel identifier : hex %x \tdec %d\n", dasd_info->schid, dasd_info->schid); printf("CU type (SenseID) : hex %x \tdec %d\n", dasd_info->cu_type, dasd_info->cu_type); printf("CU model (SenseID) : hex %x \tdec %d\n", dasd_info->cu_model, dasd_info->cu_model); printf("device type (SenseID) : hex %x \tdec %d\n", dasd_info->dev_type, dasd_info->dev_type); printf("device model (SenseID) : hex %x \tdec %d\n", dasd_info->dev_model, dasd_info->dev_model); printf("open count : hex %x \tdec %d\n", dasd_info->open_count, dasd_info->open_count); printf("req_queue_len : hex %x \tdec %d\n", dasd_info->req_queue_len, dasd_info->req_queue_len); printf("chanq_len : hex %x \tdec %d\n", dasd_info->chanq_len, dasd_info->chanq_len); printf("status : hex %x \tdec %d\n", dasd_info->status, dasd_info->status); printf("label_block : hex %x \tdec %d\n", dasd_info->label_block, dasd_info->label_block); printf("FBA_layout : hex %x \tdec %d\n", dasd_info->FBA_layout, dasd_info->FBA_layout); printf("characteristics_size : hex %x \tdec %d\n", dasd_info->characteristics_size, dasd_info->characteristics_size); printf("confdata_size : hex %x \tdec %d\n", dasd_info->confdata_size, dasd_info->confdata_size); printf("format : hex %x \tdec %d \t%s\n", dasd_info->format, dasd_info->format, dasd_info->format == DASD_FORMAT_NONE ? "NOT formatted" : dasd_info->format == DASD_FORMAT_LDL ? "LDL formatted" : dasd_info->format == DASD_FORMAT_CDL ? "CDL formatted" : "unknown format"); printf("features : hex %x \tdec %d \t", dasd_info->features, dasd_info->features); if (dasd_info->features == DASD_FEATURE_DEFAULT) { printf("default\n"); } else { for (i = 0; i < ARRAY_SIZE(flist); i++) if (dasd_info->features & flist[i].mask) printf("%s ", flist[i].name); printf("\n"); } printf("\n"); dasdview_dump_array("characteristics", dasd_info->characteristics_size, dasd_info->characteristics); printf("\n"); dasdview_dump_array("configuration_data", dasd_info->confdata_size, dasd_info->configuration_data); } static void dasdview_read_vlabel(dasdview_info_t *info, volume_label_t *vlabel) { volume_label_t tmp; unsigned long pos; pos = info->dasd_info.label_block * info->blksize; bzero(vlabel, sizeof(volume_label_t)); if ((strncmp(info->dasd_info.type, "ECKD", 4) == 0) && !info->dasd_info.FBA_layout) { /* OS/390 and zOS compatible disk layout */ vtoc_read_volume_label(info->device, pos, vlabel); } else { /* standard LINUX disk layout */ vtoc_read_volume_label(info->device, pos, &tmp); memcpy(vlabel->vollbl, &tmp, sizeof(tmp) - 4); } } static void dasdview_print_vlabel(dasdview_info_t *info) { volume_label_t vlabel; volume_label_t *tmpvlabel; int rc; unsigned char s4[5], t4[5], s5[6], t5[6], s6[7], t6[7]; char s14[15], t14[15], s29[30], t29[30]; int i; if (info->raw_track_access) { rc = lzds_dasd_read_vlabel(info->dasd); if (rc) { zt_error_print("error when reading label from device:" " rc=%d\n", rc); exit(EXIT_FAILURE); } lzds_dasd_get_vlabel(info->dasd, &tmpvlabel); memcpy(&vlabel, tmpvlabel, sizeof(vlabel)); } else { dasdview_read_vlabel(info, &vlabel); } printf("\n--- volume label -----------------------------" "---------------------------------\n"); bzero(s4, 5); bzero(t4, 5); strncpy((char *)s4, vlabel.volkey, 4); printf("volume label key : ascii '%4s'\n", s4); vtoc_ebcdic_dec((char *)s4, (char *)t4, 4); printf(" : ebcdic '%4s'\n", t4); printf(" : hex "); for (i = 0; i < 4; i++) printf("%02x", s4[i]); bzero(s4, 5); bzero(s4, 5); strncpy((char *)s4, vlabel.vollbl, 4); printf("\n\nvolume label identifier : ascii '%4s'\n", s4); vtoc_ebcdic_dec((char *)s4, (char *)t4, 4); printf(" : ebcdic '%4s'\n", t4); printf(" : hex "); for (i = 0; i < 4; i++) printf("%02x", s4[i]); bzero(s6, 7); bzero(t6, 7); strncpy((char *)s6, vlabel.volid, 6); printf("\n\nvolume identifier : ascii '%6s'\n", s6); vtoc_ebcdic_dec((char *)s6, (char *)t6, 6); printf(" : ebcdic '%6s'\n", t6); printf(" : hex "); for (i = 0; i < 6; i++) printf("%02x", s6[i]); printf("\n\nsecurity byte : hex %02x\n", vlabel.security); printf("\n\nVTOC pointer : hex %04x%04x%02x ", vlabel.vtoc.cc, vlabel.vtoc.hh, vlabel.vtoc.b); if ((vlabel.vtoc.cc == 0x4040) && (vlabel.vtoc.hh == 0x4040) && (vlabel.vtoc.b == 0x40)) printf("\n"); else printf("\n " "(cyl %d, trk %d, blk %d)\n\n", vtoc_get_cyl_from_cchhb(&vlabel.vtoc), vtoc_get_head_from_cchhb(&vlabel.vtoc), vlabel.vtoc.b); bzero(s5, 6); bzero(t5, 6); strncpy((char *)s5, vlabel.res1, 5); printf("reserved : ascii '%5s'\n", s5); vtoc_ebcdic_dec((char *)s5, (char *)t5, 5); printf(" : ebcdic '%5s'\n", t5); printf(" : hex "); for (i = 0; i < 5; i++) printf("%02x", s5[i]); bzero(s4, 5); bzero(t4, 5); strncpy((char *)s4, vlabel.cisize, 4); printf("\n\nCI size for FBA : ascii '%4s'\n", s4); vtoc_ebcdic_dec((char *)s4, (char *)t4, 4); printf(" : ebcdic '%4s'\n", t4); printf(" : hex "); for (i = 0; i < 4; i++) printf("%02x", s4[i]); bzero(s4, 5); bzero(t4, 5); strncpy((char *)s4, vlabel.blkperci, 4); printf("\n\nblocks per CI (FBA) : ascii '%4s'\n", s4); vtoc_ebcdic_dec((char *)s4, (char *)t4, 4); printf(" : ebcdic '%4s'\n", t4); printf(" : hex "); for (i = 0; i < 4; i++) printf("%02x", s4[i]); bzero(s4, 5); bzero(t4, 5); strncpy((char *)s4, vlabel.labperci, 4); printf("\n\nlabels per CI (FBA) : ascii '%4s'\n", s4); vtoc_ebcdic_dec((char *)s4, (char *)t4, 4); printf(" : ebcdic '%4s'\n", t4); printf(" : hex "); for (i = 0; i < 4; i++) printf("%02x", s4[i]); bzero(s4, 5); bzero(t4, 5); strncpy((char *)s4, vlabel.res2, 4); printf("\n\nreserved : ascii '%4s'\n", s4); vtoc_ebcdic_dec((char *)s4, (char *)t4, 4); printf(" : ebcdic '%4s'\n", t4); printf(" : hex "); for (i = 0; i < 4; i++) printf("%02x", s4[i]); bzero(s14, 15); bzero(t14, 15); strncpy(s14, vlabel.lvtoc, 14); printf("\n\nowner code for VTOC : ascii '%14s'\n", s14); vtoc_ebcdic_dec(s14, t14, 14); printf(" ebcdic '%14s'\n", t14); printf(" hex "); for (i = 0; i < 14; i++) { printf("%02x", s14[i]); if ((i + 1) % 4 == 0) printf(" "); if ((i + 1) % 8 == 0) printf(" "); } bzero(s29, 30); strncpy(s29, vlabel.res3, 28); printf("\n\nreserved : ascii '%28s'\n", s29); bzero(t29, 30); vtoc_ebcdic_dec(s29, t29, 28); printf(" ebcdic '%28s'\n", t29); printf(" hex "); for (i = 0; i < 28; i++) { printf("%02x", s29[i]); if ((i + 1) % 4 == 0) printf(" "); if ((i + 1) % 8 == 0) printf(" "); if ((i + 1) % 16 == 0) printf("\n " " "); } bzero(s4, 5); bzero(t4, 5); s4[0] = vlabel.ldl_version; printf("\n\nldl_version : ascii '%1s'\n", s4); vtoc_ebcdic_dec((char *)s4, (char *)t4, 1); printf(" : ebcdic '%1s'\n", t4); printf(" : hex %02x", s4[0]); printf("\n\nformatted_blocks : dec %llu", vlabel.formatted_blocks); printf("\n : hex %016llx", vlabel.formatted_blocks); printf("\n"); } static void dasdview_print_volser(dasdview_info_t *info) { volume_label_t vlabel; volume_label_t *tmpvlabel; char volser[7]; char vollbl[5]; int rc; if (info->raw_track_access) { rc = lzds_dasd_read_vlabel(info->dasd); if (rc) { zt_error_print("error when reading label from device:" " rc=%d\n", rc); exit(EXIT_FAILURE); } lzds_dasd_get_vlabel(info->dasd, &tmpvlabel); memcpy(&vlabel, tmpvlabel, sizeof(vlabel)); } else { dasdview_read_vlabel(info, &vlabel); } bzero(vollbl, 5); bzero(volser, 7); strncpy(vollbl, vlabel.vollbl, 4); vtoc_ebcdic_dec(vollbl, vollbl, 4); if ((strncmp(vollbl, "VOL1", 4) == 0) || (strncmp(vollbl, "LNX1", 4) == 0)) { strncpy(volser, vlabel.volid, 6); vtoc_ebcdic_dec(volser, volser, 6); } else { memcpy(volser, " ", 6); } printf("%6.6s\n", volser); } static void dasdview_read_vtoc(dasdview_info_t *info) { volume_label_t vlabel; format1_label_t tmp; unsigned long maxblk, pos; u_int64_t vtocblk; int i; pos = info->dasd_info.label_block * info->blksize; bzero(&vlabel, sizeof(vlabel)); if ((strncmp(info->dasd_info.type, "ECKD", 4) == 0) && !info->dasd_info.FBA_layout) { /* OS/390 and zOS compatible disk layout */ vtoc_read_volume_label(info->device, pos, &vlabel); } else { zt_error_print("dasdview: disk layout error\n" "%s is not formatted with the z/OS " "compatible disk layout!\n", info->device); exit(EXIT_FAILURE); } vtocblk = (u_int64_t)vtoc_get_cyl_from_cchhb(&vlabel.vtoc) * info->geo.heads * info->geo.sectors + vtoc_get_head_from_cchhb(&vlabel.vtoc) * info->geo.sectors + vlabel.vtoc.b; /* * geo.cylinders is the minimum of hw_cylinders and LV_COMPAT_CYL * Actually the vtoc should be located in in the first 65k-1 tracks * so this check could be even more restrictive, but it doesn't * hurt the way it is. Linux cdl format restricts the vtoc to * the first two tracks anyway. */ maxblk = info->geo.cylinders * info->geo.heads * info->geo.sectors; if ((vtocblk <= 0) || (vtocblk > maxblk)) { zt_error_print("dasdview: VTOC error\n" "Volume label VTOC pointer is not valid!\n"); exit(EXIT_FAILURE); } vtoc_read_label(info->device, (vtocblk - 1) * info->blksize, NULL, &info->f4, NULL, NULL); if ((info->f4.DS4KEYCD[0] != 0x04) || (info->f4.DS4KEYCD[43] != 0x04) || (info->f4.DS4IDFMT != 0xf4)) { /* format4 DSCB is invalid */ zt_error_print("dasdview: VTOC error\n" "Format 4 DSCB is invalid!\n"); exit(EXIT_FAILURE); } info->f4c++; pos = (vtocblk - 1) * info->blksize; for (i = 1; i < info->geo.sectors; i++) { pos += info->blksize; vtoc_read_label(info->device, pos, &tmp, NULL, NULL, NULL); switch (tmp.DS1FMTID) { case 0xf1: memcpy(&info->f1[info->f1c], &tmp, sizeof(format1_label_t)); info->f1c++; break; case 0xf4: info->f4c++; break; case 0xf5: memcpy(&info->f5, &tmp, sizeof(format1_label_t)); info->f5c++; break; case 0xf7: memcpy(&info->f7, &tmp, sizeof(format1_label_t)); info->f7c++; break; case 0xf8: memcpy(&info->f8[info->f8c], &tmp, sizeof(format1_label_t)); info->f8c++; break; case 0xf9: memcpy(&info->f9[info->f9c], &tmp, sizeof(format1_label_t)); info->f9c++; break; case 0x00: break; default: printf("Unknown label in VTOC detected (id=%x)\n", tmp.DS1FMTID); } } if (info->f4c > 1) { zt_error_print("dasdview: VTOC error\n" "More than one FMT4 DSCB!\n"); exit(EXIT_FAILURE); } if (info->f5c > 1) { zt_error_print("dasdview: VTOC error\n" "More than one FMT5 DSCB!\n"); exit(EXIT_FAILURE); } if (info->f7c > 1) { zt_error_print("dasdview: VTOC error\n" "More than one FMT7 DSCB!\n"); exit(EXIT_FAILURE); } } static void dasdview_print_format1_8_short_info(format1_label_t *f1, struct hd_geometry *geo) { char s6[7], s13[14], s44[45]; unsigned long track_low, track_up; bzero(s44, 45); strncpy(s44, f1->DS1DSNAM, 44); vtoc_ebcdic_dec(s44, s44, 44); bzero(s6, 7); strncpy(s6, (char *)f1->DS1DSSN, 6); vtoc_ebcdic_dec(s6, s6, 6); bzero(s13, 14); strncpy(s13, (char *)f1->DS1SYSCD, 13); vtoc_ebcdic_dec(s13, s13, 13); track_low = cchh2trk(&f1->DS1EXT1.llimit, geo); track_up = cchh2trk(&f1->DS1EXT1.ulimit, geo); printf(" | %44s | trk | trk |\n", s44); printf(" | data set serial number :" " '%6s' |" " %12ld | %12ld |\n", s6, track_low, track_up); printf(" | system code :" " '%13s' |" " cyl/trk | cyl/trk |\n", s13); printf(" | creation date :" " year %4d, day %3d |" " %8d/%3d | %8d/%3d |\n", f1->DS1CREDT.year + 1900, f1->DS1CREDT.day, vtoc_get_cyl_from_cchh(&f1->DS1EXT1.llimit), vtoc_get_head_from_cchh(&f1->DS1EXT1.llimit), vtoc_get_cyl_from_cchh(&f1->DS1EXT1.ulimit), vtoc_get_head_from_cchh(&f1->DS1EXT1.ulimit)); printf(" +-----------------------------------------" "-----+--------------+--------------+\n"); } static void dasdview_print_vtoc_info(dasdview_info_t *info) { int i; printf("--- VTOC info --------------------------------" "---------------------------------\n"); printf("The VTOC contains:\n"); printf(" %d format 1 label(s)\n", info->f1c); printf(" %d format 4 label(s)\n", info->f4c); printf(" %d format 5 label(s)\n", info->f5c); printf(" %d format 7 label(s)\n", info->f7c); printf(" %d format 8 label(s)\n", info->f8c); printf(" %d format 9 label(s)\n", info->f9c); if ((info->f1c < 1) && (info->f8c < 1)) { printf("There are no partitions defined.\n"); } else { printf("Other mainframe operating systems would see " "the following data sets:\n"); printf(" +----------------------------------------------+" "--------------+--------------+\n"); printf(" | data set |" " start | end |\n"); printf(" +----------------------------------------------+" "--------------+--------------+\n"); for (i = 0; i < info->f1c; i++) dasdview_print_format1_8_short_info(&info->f1[i], &info->geo); for (i = 0; i < info->f8c; i++) dasdview_print_format1_8_short_info(&info->f8[i], &info->geo); } } static void dasdview_print_short_info_extent_raw(extent_t *ext) { if (ext->typeind > 0x00) printf(" %3d (%5d,%5d) (%5d,%5d)\n", ext->seqno, vtoc_get_cyl_from_cchh(&ext->llimit), vtoc_get_head_from_cchh(&ext->llimit), vtoc_get_cyl_from_cchh(&ext->ulimit), vtoc_get_head_from_cchh(&ext->ulimit)); } static void dasdview_print_format1_8_short_info_raw(format1_label_t *f1, dasdview_info_t *info) { char s6[7], s13[14], s44[45]; unsigned long long j; format3_label_t *f3; format9_label_t *f9; struct dscb *dscb; int rc; bzero(s44, 45); strncpy(s44, f1->DS1DSNAM, 44); vtoc_ebcdic_dec(s44, s44, 44); bzero(s6, 7); strncpy(s6, (char *)f1->DS1DSSN, 6); vtoc_ebcdic_dec(s6, s6, 6); bzero(s13, 14); strncpy(s13, (char *)f1->DS1SYSCD, 13); vtoc_ebcdic_dec(s13, s13, 13); printf("data set name : '%44s'\n", s44); printf("data set serial number : '%6s'\n", s6); printf("system code : '%13s'\n", s13); printf("creation date : year %4d, day %3d\n", f1->DS1CREDT.year + 1900, f1->DS1CREDT.day); printf("flags : "); if (f1->DS1FLAG1 & 0x80) printf("DS1COMPR "); if (f1->DS1FLAG1 & 0x40) printf("DS1CPOIT "); if (f1->DS1FLAG1 & 0x20) printf("DS1EXPBY "); if (f1->DS1FLAG1 & 0x10) printf("DS1RECAL "); if (f1->DS1FLAG1 & 0x08) printf("DS1LARGE "); if (f1->DS1FLAG1 & 0x04) printf("unknown "); if ((f1->DS1FLAG1 & 0x01) && (f1->DS1FLAG1 & 0x02)) printf("DS1EATTR=not used "); if (!(f1->DS1FLAG1 & 0x01) && (f1->DS1FLAG1 & 0x02)) printf("DS1EATTR=optional "); if ((f1->DS1FLAG1 & 0x01) && !(f1->DS1FLAG1 & 0x02)) printf("DS1EATTR=no "); if (f1->DS1FLAG1 & 0x00) printf("DS1EATTR=default "); printf("\n"); printf("SMS flags : "); if (f1->DS1SMSFG & 0x80) printf("DS1SMSDS "); if (f1->DS1SMSFG & 0x40) printf("DS1SMSUC "); if (f1->DS1SMSFG & 0x20) printf("DS1REBLK "); if (f1->DS1SMSFG & 0x10) printf("DS1CRSDB "); if (f1->DS1SMSFG & 0x08) printf("DS1PDSE "); if (f1->DS1SMSFG & 0x04) printf("DS1STRP "); if (f1->DS1SMSFG & 0x02) printf("DS1PDSEX "); if (f1->DS1SMSFG & 0x01) printf("DS1DSAE "); printf("\n"); printf("organisation : "); if (f1->DS1DSRG1 & 0x80) printf("DS1DSGIS "); if (f1->DS1DSRG1 & 0x40) printf("DS1DSGPS "); if (f1->DS1DSRG1 & 0x20) printf("DS1DSGDA "); if (f1->DS1DSRG1 & 0x10) printf("DS1DSGCX "); if (f1->DS1DSRG1 & 0x08) printf("reserved "); if (f1->DS1DSRG1 & 0x04) printf("reserved "); if (f1->DS1DSRG1 & 0x02) printf("DS1DSGPO "); if (f1->DS1DSRG1 & 0x01) printf("DS1DSGU "); if (f1->DS1DSRG2 & 0x80) printf("DS1DSGGS "); if (f1->DS1DSRG2 & 0x40) printf("DS1DSGTX "); if (f1->DS1DSRG2 & 0x20) printf("DS1DSGTQ "); if (f1->DS1DSRG2 & 0x10) printf("reserved "); if (f1->DS1DSRG2 & 0x08) printf("DS1ACBM "); if (f1->DS1DSRG2 & 0x04) printf("DS1DSGTR "); if (f1->DS1DSRG2 & 0x02) printf("reserved "); if (f1->DS1DSRG2 & 0x01) printf("reserved"); printf("\n"); printf("record format : "); if ((f1->DS1RECFM & 0x80) && !(f1->DS1RECFM & 0x40)) printf("DS1RECFF "); if (!(f1->DS1RECFM & 0x80) && (f1->DS1RECFM & 0x40)) printf("DS1RECFV "); if ((f1->DS1RECFM & 0x80) && (f1->DS1RECFM & 0x40)) printf("DS1RECFU "); if (f1->DS1RECFM & 0x20) printf("DS1RECFT "); if (f1->DS1RECFM & 0x10) printf("DS1RECFB "); if (f1->DS1RECFM & 0x08) printf("DS1RECFS "); if (f1->DS1RECFM & 0x04) printf("DS1RECFA "); if (f1->DS1RECFM & 0x02) printf("DS1RECMC "); if (f1->DS1RECFM & 0x01) printf("reserved"); printf("\n"); printf("(max) block length : %u\n", f1->DS1BLKL); printf("logical record length : %u\n", f1->DS1LRECL); printf("extents belonging to this dataset:\n"); printf(" seqno llimit (cyl, trk) ulimit (cyl, trk)\n"); /* The format 1 label can point to a chain of f3 labels * The format 8 label points to a (chain of) f9 labels * The format 9 label may contain several format 3 labels, but as * far as I know, it is still OK to follow the 'next dscb' chain. * So for a format 9 label I have to follow this chain until I * find the first format 3 dscb and then I can follow the format 3 * dscbs to the end of the chain. */ rc = lzds_raw_vtoc_get_dscb_from_cchhb(info->rawvtoc, &f1->DS1PTRDS, &dscb); /* The first f9 label contains extra data that we may want to print here */ while (!rc && dscb && dscb->fmtid == 0xf9) { f9 = (format9_label_t *)dscb; rc = lzds_raw_vtoc_get_dscb_from_cchhb(info->rawvtoc, &f9->DS9PTRDS, &dscb); } if (rc) { zt_error_print("dasdview: Broken format 3 DSCB chain \n"); exit(EXIT_FAILURE); } f3 = (dscb && dscb->fmtid == 0xf3) ? (format3_label_t *)dscb : NULL; /* first print the extents that are part of the f1/f8 label itself */ dasdview_print_short_info_extent_raw(&f1->DS1EXT1); dasdview_print_short_info_extent_raw(&f1->DS1EXT2); dasdview_print_short_info_extent_raw(&f1->DS1EXT3); /* now follow the f3 chain into the rabbit hole */ while (f3) { /* sanity check */ if (f3->DS3FMTID != 0xf3) { zt_error_print("dasdview: Broken format 3 DSCB" " chain \n"); exit(EXIT_FAILURE); } for (j = 0; j < 4; ++j) dasdview_print_short_info_extent_raw(&f3->DS3EXTNT[j]); for (j = 0; j < 9; ++j) dasdview_print_short_info_extent_raw(&f3->DS3ADEXT[j]); rc = lzds_raw_vtoc_get_dscb_from_cchhb(info->rawvtoc, &f3->DS3PTRDS, (struct dscb **)&f3); if (rc) { zt_error_print("dasdview: Broken format 3 DSCB" " chain \n"); exit(EXIT_FAILURE); } } printf("\n"); } static void dasdview_print_vtoc_info_raw(dasdview_info_t *info) { struct dscbiterator *it; struct dscb *dscb; int rc; int f1c, f3c, f4c, f5c, f7c, f8c, f9c; f1c = 0; f3c = 0; f4c = 0; f5c = 0; f7c = 0; f8c = 0; f9c = 0; rc = lzds_raw_vtoc_alloc_dscbiterator(info->rawvtoc, &it); if (rc) { zt_error_print("dasdview: could not allocate DSCB iterator \n"); exit(EXIT_FAILURE); } while (!lzds_dscbiterator_get_next_dscb(it, &dscb)) { if (dscb->fmtid == 0xf1) ++f1c; else if (dscb->fmtid == 0xf3) ++f3c; else if (dscb->fmtid == 0xf4) ++f4c; else if (dscb->fmtid == 0xf5) ++f5c; else if (dscb->fmtid == 0xf7) ++f7c; else if (dscb->fmtid == 0xf8) ++f8c; else if (dscb->fmtid == 0xf9) ++f9c; } lzds_dscbiterator_free(it); printf("--- VTOC info --------------------------------" "---------------------------------\n"); printf("The VTOC contains:\n"); printf(" %d format 1 label(s)\n", f1c); printf(" %d format 3 label(s)\n", f3c); printf(" %d format 4 label(s)\n", f4c); printf(" %d format 5 label(s)\n", f5c); printf(" %d format 7 label(s)\n", f7c); printf(" %d format 8 label(s)\n", f8c); printf(" %d format 9 label(s)\n", f9c); rc = lzds_raw_vtoc_alloc_dscbiterator(info->rawvtoc, &it); if (rc) { zt_error_print("dasdview: could not allocate DSCB iterator \n"); exit(EXIT_FAILURE); } while (!lzds_dscbiterator_get_next_dscb(it, &dscb)) { if (dscb->fmtid == 0xf1 || dscb->fmtid == 0xf8) dasdview_print_format1_8_short_info_raw( (format1_label_t *)dscb, info); } lzds_dscbiterator_free(it); } /* * Note: the explicit cylinder/head conversion for large volume * adresses should not be necessary for entries that point to * vtoc labels, as those must be located in the first 65K-1 tracks, * but we do it anyway to be on the safe side. */ static void dasdview_print_format1_8_no_head(format1_label_t *f1) { char s6[7], s13[14], s44[45]; int i; bzero(s6, 7); bzero(s13, 14); bzero(s44, 45); strncpy(s44, f1->DS1DSNAM, 44); printf("DS1DSNAM : ascii '%44s'\n", s44); vtoc_ebcdic_dec(s44, s44, 44); printf(" ebcdic '%44s'\n", s44); printf("DS1FMTID : dec %d, hex %02x\n", f1->DS1FMTID, f1->DS1FMTID); printf("DS1DSSN : hex "); for (i = 0; i < 6; i++) printf("%02x", f1->DS1DSSN[i]); strncpy(s6, (char *)f1->DS1DSSN, 6); printf("\n ascii '%6s'\n", s6); vtoc_ebcdic_dec(s6, s6, 6); printf(" ebcdic '%6s'\n", s6); printf("DS1VOLSQ : dec %d, hex %04x\n", f1->DS1VOLSQ, f1->DS1VOLSQ); printf("DS1CREDT : hex %02x%04x " "(year %d, day %d)\n", f1->DS1CREDT.year, f1->DS1CREDT.day, f1->DS1CREDT.year + 1900, f1->DS1CREDT.day); printf("DS1EXPDT : hex %02x%04x " "(year %d, day %d)\n", f1->DS1EXPDT.year, f1->DS1EXPDT.day, f1->DS1EXPDT.year + 1900, f1->DS1EXPDT.day); printf("DS1NOEPV : dec %d, hex %02x\n", f1->DS1NOEPV, f1->DS1NOEPV); printf("DS1NOBDB : dec %d, hex %02x\n", f1->DS1NOBDB, f1->DS1NOBDB); printf("DS1FLAG1 : dec %d, hex %02x\n", f1->DS1FLAG1, f1->DS1FLAG1); printf("DS1SYSCD : hex "); for (i = 0; i < 13; i++) printf("%02x", f1->DS1SYSCD[i]); strncpy(s13, (char *)f1->DS1SYSCD, 13); printf("\n ascii '%13s'\n", s13); vtoc_ebcdic_dec(s13, s13, 13); printf(" ebcdic '%13s'\n", s13); printf("DS1REFD : hex %02x%04x " "(year %d, day %d)\n", f1->DS1REFD.year, f1->DS1REFD.day, f1->DS1REFD.year + 1900, f1->DS1REFD.day); printf("DS1SMSFG : dec %d, hex %02x\n", f1->DS1SMSFG, f1->DS1SMSFG); printf("DS1SCXTF : dec %d, hex %02x\n", f1->DS1SCXTF, f1->DS1SCXTF); printf("DS1SCXTV : dec %d, hex %04x\n", f1->DS1SCXTV, f1->DS1SCXTV); printf("DS1DSRG1 : dec %d, hex %02x\n", f1->DS1DSRG1, f1->DS1DSRG1); printf("DS1DSRG2 : dec %d, hex %02x\n", f1->DS1DSRG2, f1->DS1DSRG2); printf("DS1RECFM : dec %d, hex %02x\n", f1->DS1RECFM, f1->DS1RECFM); printf("DS1OPTCD : dec %d, hex %02x\n", f1->DS1OPTCD, f1->DS1OPTCD); printf("DS1BLKL : dec %d, hex %04x\n", f1->DS1BLKL, f1->DS1BLKL); printf("DS1LRECL : dec %d, hex %04x\n", f1->DS1LRECL, f1->DS1LRECL); printf("DS1KEYL : dec %d, hex %02x\n", f1->DS1KEYL, f1->DS1KEYL); printf("DS1RKP : dec %d, hex %04x\n", f1->DS1RKP, f1->DS1RKP); printf("DS1DSIND : dec %d, hex %02x\n", f1->DS1DSIND, f1->DS1DSIND); printf("DS1SCAL1 : dec %d, hex %02x\n", f1->DS1SCAL1, f1->DS1SCAL1); printf("DS1SCAL3 : hex "); for (i = 0; i < 3; i++) printf("%02x", f1->DS1SCAL3[i]); printf("\nDS1LSTAR : hex %04x%02x " "(trk %d, blk %d)\n", f1->DS1LSTAR.tt, f1->DS1LSTAR.r, f1->DS1LSTAR.tt, f1->DS1LSTAR.r); printf("DS1TRBAL : dec %d, hex %04x\n", f1->DS1TRBAL, f1->DS1TRBAL); printf("reserved : dec %d, hex %04x\n", f1->res1, f1->res1); printf("DS1EXT1 : hex %02x%02x%04x%04x%04x%04x\n", f1->DS1EXT1.typeind, f1->DS1EXT1.seqno, f1->DS1EXT1.llimit.cc, f1->DS1EXT1.llimit.hh, f1->DS1EXT1.ulimit.cc, f1->DS1EXT1.ulimit.hh); printf(" typeind : dec %d, hex %02x\n", f1->DS1EXT1.typeind, f1->DS1EXT1.typeind); printf(" seqno : dec %d, hex %02x\n", f1->DS1EXT1.seqno, f1->DS1EXT1.seqno); printf(" llimit : hex %04x%04x " "(cyl %d, trk %d)\n", f1->DS1EXT1.llimit.cc, f1->DS1EXT1.llimit.hh, vtoc_get_cyl_from_cchh(&f1->DS1EXT1.llimit), vtoc_get_head_from_cchh(&f1->DS1EXT1.llimit)); printf(" ulimit : hex %04x%04x " "(cyl %d, trk %d)\n", f1->DS1EXT1.ulimit.cc, f1->DS1EXT1.ulimit.hh, vtoc_get_cyl_from_cchh(&f1->DS1EXT1.ulimit), vtoc_get_head_from_cchh(&f1->DS1EXT1.ulimit)); printf("DS1EXT2 : hex %02x%02x%04x%04x%04x%04x\n", f1->DS1EXT2.typeind, f1->DS1EXT2.seqno, f1->DS1EXT2.llimit.cc, f1->DS1EXT2.llimit.hh, f1->DS1EXT2.ulimit.cc, f1->DS1EXT2.ulimit.hh); printf(" typeind : dec %d, hex %02x\n", f1->DS1EXT2.typeind, f1->DS1EXT2.typeind); printf(" seqno : dec %d, hex %02x\n", f1->DS1EXT2.seqno, f1->DS1EXT2.seqno); printf(" llimit : hex %04x%04x " "(cyl %d, trk %d)\n", f1->DS1EXT2.llimit.cc, f1->DS1EXT2.llimit.hh, vtoc_get_cyl_from_cchh(&f1->DS1EXT2.llimit), vtoc_get_head_from_cchh(&f1->DS1EXT2.llimit)); printf(" ulimit : hex %04x%04x " "(cyl %d, trk %d)\n", f1->DS1EXT2.ulimit.cc, f1->DS1EXT2.ulimit.hh, vtoc_get_cyl_from_cchh(&f1->DS1EXT2.ulimit), vtoc_get_head_from_cchh(&f1->DS1EXT2.ulimit)); printf("DS1EXT3 : hex %02x%02x%04x%04x%04x%04x\n", f1->DS1EXT3.typeind, f1->DS1EXT3.seqno, f1->DS1EXT3.llimit.cc, f1->DS1EXT3.llimit.hh, f1->DS1EXT3.ulimit.cc, f1->DS1EXT3.ulimit.hh); printf(" typeind : dec %d, hex %02x\n", f1->DS1EXT3.typeind, f1->DS1EXT3.typeind); printf(" seqno : dec %d, hex %02x\n", f1->DS1EXT3.seqno, f1->DS1EXT3.seqno); printf(" llimit : hex %04x%04x " "(cyl %d, trk %d)\n", f1->DS1EXT3.llimit.cc, f1->DS1EXT3.llimit.hh, vtoc_get_cyl_from_cchh(&f1->DS1EXT3.llimit), vtoc_get_head_from_cchh(&f1->DS1EXT3.llimit)); printf(" ulimit : hex %04x%04x " "(cyl %d, trk %d)\n", f1->DS1EXT3.ulimit.cc, f1->DS1EXT3.ulimit.hh, vtoc_get_cyl_from_cchh(&f1->DS1EXT3.ulimit), vtoc_get_head_from_cchh(&f1->DS1EXT3.ulimit)); printf("DS1PTRDS : %04x%04x%02x " "(cyl %d, trk %d, blk %d)\n", f1->DS1PTRDS.cc, f1->DS1PTRDS.hh, f1->DS1PTRDS.b, vtoc_get_cyl_from_cchhb(&f1->DS1PTRDS), vtoc_get_head_from_cchhb(&f1->DS1PTRDS), f1->DS1PTRDS.b); } static void dasdview_print_vtoc_f1_raw(format1_label_t *f1) { printf("\n--- VTOC format 1 label -----------------------" "---------------------------------\n"); dasdview_print_format1_8_no_head(f1); } /* Note: A format 8 label uses the same type as format 1 */ static void dasdview_print_vtoc_f8_raw(format1_label_t *f8) { printf("\n--- VTOC format 8 label -----------------------" "---------------------------------\n"); dasdview_print_format1_8_no_head(f8); } static void dasdview_print_extent(extent_t *ext, char *name, int index) { printf("%s[%d] : hex %02x%02x%04x%04x%04x%04x\n", name, index, ext->typeind, ext->seqno, ext->llimit.cc, ext->llimit.hh, ext->ulimit.cc, ext->ulimit.hh); printf(" typeind : dec %d, hex %02x\n", ext->typeind, ext->typeind); printf(" seqno : dec %d, hex %02x\n", ext->seqno, ext->seqno); printf(" llimit : hex %04x%04x " "(cyl %d, trk %d)\n", ext->llimit.cc, ext->llimit.hh, vtoc_get_cyl_from_cchh(&ext->llimit), vtoc_get_head_from_cchh(&ext->llimit)); printf(" ulimit : hex %04x%04x " "(cyl %d, trk %d)\n", ext->ulimit.cc, ext->ulimit.hh, vtoc_get_cyl_from_cchh(&ext->ulimit), vtoc_get_head_from_cchh(&ext->ulimit)); } static void dasdview_print_vtoc_f3_raw(format3_label_t *f3) { int i; printf("\n--- VTOC format 3 label ----------------------" "---------------------------------\n"); printf("DS3KEYID : "); for (i = 0; i < 4; i++) printf("%02x", f3->DS3KEYID[i]); printf("\n"); for (i = 0; i < 4; ++i) dasdview_print_extent(&f3->DS3EXTNT[i], "DS3EXTNT", i); printf("DS3FMTID : dec %d, hex %02x\n", f3->DS3FMTID, f3->DS3FMTID); for (i = 0; i < 9; ++i) dasdview_print_extent(&f3->DS3ADEXT[i], "DS3ADEXT", i); printf("DS3PTRDS : %04x%04x%02x " "(cyl %d, trk %d, blk %d)\n", f3->DS3PTRDS.cc, f3->DS3PTRDS.hh, f3->DS3PTRDS.b, vtoc_get_cyl_from_cchhb(&f3->DS3PTRDS), vtoc_get_head_from_cchhb(&f3->DS3PTRDS), f3->DS3PTRDS.b); } static void dasdview_print_vtoc_f4_raw(format4_label_t *f4) { int i; printf("\n--- VTOC format 4 label ----------------------" "---------------------------------\n"); printf("DS4KEYCD : "); for (i = 0; i < 44; i++) printf("%02x", f4->DS4KEYCD[i]); printf("\nDS4IDFMT : dec %d, hex %02x\n", f4->DS4IDFMT, f4->DS4IDFMT); printf("DS4HPCHR : %04x%04x%02x " "(cyl %d, trk %d, blk %d)\n", f4->DS4HPCHR.cc, f4->DS4HPCHR.hh, f4->DS4HPCHR.b, vtoc_get_cyl_from_cchhb(&f4->DS4HPCHR), vtoc_get_head_from_cchhb(&f4->DS4HPCHR), f4->DS4HPCHR.b); printf("DS4DSREC : dec %d, hex %04x\n", f4->DS4DSREC, f4->DS4DSREC); printf("DS4HCCHH : %04x%04x (cyl %d, trk %d)\n", f4->DS4HCCHH.cc, f4->DS4HCCHH.hh, vtoc_get_cyl_from_cchh(&f4->DS4HCCHH), vtoc_get_head_from_cchh(&f4->DS4HCCHH)); printf("DS4NOATK : dec %d, hex %04x\n", f4->DS4NOATK, f4->DS4NOATK); printf("DS4VTOCI : dec %d, hex %02x\n", f4->DS4VTOCI, f4->DS4VTOCI); printf("DS4NOEXT : dec %d, hex %02x\n", f4->DS4NOEXT, f4->DS4NOEXT); printf("DS4SMSFG : dec %d, hex %02x\n", f4->DS4SMSFG, f4->DS4SMSFG); printf("DS4DEVAC : dec %d, hex %02x\n", f4->DS4DEVAC, f4->DS4DEVAC); printf("DS4DSCYL : dec %d, hex %04x\n", f4->DS4DEVCT.DS4DSCYL, f4->DS4DEVCT.DS4DSCYL); printf("DS4DSTRK : dec %d, hex %04x\n", f4->DS4DEVCT.DS4DSTRK, f4->DS4DEVCT.DS4DSTRK); printf("DS4DEVTK : dec %d, hex %04x\n", f4->DS4DEVCT.DS4DEVTK, f4->DS4DEVCT.DS4DEVTK); printf("DS4DEVI : dec %d, hex %02x\n", f4->DS4DEVCT.DS4DEVI, f4->DS4DEVCT.DS4DEVI); printf("DS4DEVL : dec %d, hex %02x\n", f4->DS4DEVCT.DS4DEVL, f4->DS4DEVCT.DS4DEVL); printf("DS4DEVK : dec %d, hex %02x\n", f4->DS4DEVCT.DS4DEVK, f4->DS4DEVCT.DS4DEVK); printf("DS4DEVFG : dec %d, hex %02x\n", f4->DS4DEVCT.DS4DEVFG, f4->DS4DEVCT.DS4DEVFG); printf("DS4DEVTL : dec %d, hex %04x\n", f4->DS4DEVCT.DS4DEVTL, f4->DS4DEVCT.DS4DEVTL); printf("DS4DEVDT : dec %d, hex %02x\n", f4->DS4DEVCT.DS4DEVDT, f4->DS4DEVCT.DS4DEVDT); printf("DS4DEVDB : dec %d, hex %02x\n", f4->DS4DEVCT.DS4DEVDB, f4->DS4DEVCT.DS4DEVDB); printf("DS4AMTIM : hex "); for (i = 0; i < 8; i++) printf("%02x", f4->DS4AMTIM[i]); printf("\nDS4AMCAT : hex "); for (i = 0; i < 3; i++) printf("%02x", f4->DS4AMCAT[i]); printf("\nDS4R2TIM : hex "); for (i = 0; i < 8; i++) printf("%02x", f4->DS4R2TIM[i]); printf("\nres1 : hex "); for (i = 0; i < 5; i++) printf("%02x", f4->res1[i]); printf("\nDS4F6PTR : hex "); for (i = 0; i < 5; i++) printf("%02x", f4->DS4F6PTR[i]); printf("\nDS4VTOCE : hex %02x%02x%04x%04x%04x%04x\n", f4->DS4VTOCE.typeind, f4->DS4VTOCE.seqno, f4->DS4VTOCE.llimit.cc, f4->DS4VTOCE.llimit.hh, f4->DS4VTOCE.ulimit.cc, f4->DS4VTOCE.ulimit.hh); printf(" typeind : dec %d, hex %02x\n", f4->DS4VTOCE.typeind, f4->DS4VTOCE.typeind); printf(" seqno : dec %d, hex %02x\n", f4->DS4VTOCE.seqno, f4->DS4VTOCE.seqno); printf(" llimit : hex %04x%04x (cyl %d, trk %d)\n", f4->DS4VTOCE.llimit.cc, f4->DS4VTOCE.llimit.hh, vtoc_get_cyl_from_cchh(&f4->DS4VTOCE.llimit), vtoc_get_head_from_cchh(&f4->DS4VTOCE.llimit)); printf(" ulimit : hex %04x%04x (cyl %d, trk %d)\n", f4->DS4VTOCE.ulimit.cc, f4->DS4VTOCE.ulimit.hh, vtoc_get_cyl_from_cchh(&f4->DS4VTOCE.ulimit), vtoc_get_head_from_cchh(&f4->DS4VTOCE.ulimit)); printf("res2 : hex "); for (i = 0; i < 10; i++) printf("%02x", f4->res2[i]); printf("\nDS4EFLVL : dec %d, hex %02x\n", f4->DS4EFLVL, f4->DS4EFLVL); printf("DS4EFPTR : hex %04x%04x%02x " "(cyl %d, trk %d, blk %d)\n", f4->DS4EFPTR.cc, f4->DS4EFPTR.hh, f4->DS4EFPTR.b, vtoc_get_cyl_from_cchhb(&f4->DS4EFPTR), vtoc_get_head_from_cchhb(&f4->DS4EFPTR), f4->DS4EFPTR.b); printf("res3 : hex %02x\n", f4->res3); printf("DS4DCYL : dec %d, hex %08x\n", f4->DS4DCYL, f4->DS4DCYL); printf("res4 : hex "); for (i = 0; i < 2; i++) printf("%02x", f4->res4[i]); printf("\nDS4DEVF2 : dec %d, hex %02x\n", f4->DS4DEVF2, f4->DS4DEVF2); printf("res5 : hex %02x\n", f4->res5); } static void dasdview_print_vtoc_f5_raw(format5_label_t *f5) { int i; printf("\n--- VTOC format 5 label ----------------------" "---------------------------------\n"); printf("key identifier\n DS5KEYID : "); for (i = 0; i < 4; i++) printf("%02x", f5->DS5KEYID[i]); printf("\nfirst extent description\n"); printf(" DS5AVEXT : %04x%04x%02x " "(start trk: %d, length: %d cyl, %d trk)\n", f5->DS5AVEXT.t, f5->DS5AVEXT.fc, f5->DS5AVEXT.ft, f5->DS5AVEXT.t, f5->DS5AVEXT.fc, f5->DS5AVEXT.ft); printf("next 7 extent descriptions\n"); for (i = 0; i < 7; i++) { printf(" DS5EXTAV[%d] : %04x%04x%02x " "(start trk: %d, length: %d cyl, %d trk)\n", i + 2, f5->DS5EXTAV[i].t, f5->DS5EXTAV[i].fc, f5->DS5EXTAV[i].ft, f5->DS5EXTAV[i].t, f5->DS5EXTAV[i].fc, f5->DS5EXTAV[i].ft); } printf("format identifier\n" " DS5FMTID : dec %d, hex %02x\n", f5->DS5FMTID, f5->DS5FMTID); printf("next 18 extent descriptions\n"); for (i = 0; i < 18; i++) { printf(" DS5MAVET[%d] : %04x%04x%02x " "(start trk: %d, length: %d cyl, %d trk)\n", i + 9, f5->DS5MAVET[i].t, f5->DS5MAVET[i].fc, f5->DS5MAVET[i].ft, f5->DS5MAVET[i].t, f5->DS5MAVET[i].fc, f5->DS5MAVET[i].ft); } printf("pointer to next format 5 label\n" " DS5PTRDS : %04x%04x%02x " "(cyl %d, trk %d, blk %d)\n", f5->DS5PTRDS.cc, f5->DS5PTRDS.hh, f5->DS5PTRDS.b, vtoc_get_cyl_from_cchhb(&f5->DS5PTRDS), vtoc_get_head_from_cchhb(&f5->DS5PTRDS), f5->DS5PTRDS.b); } static void dasdview_print_vtoc_f7_raw(format7_label_t *f7) { int i; printf("\n--- VTOC format 7 label ----------------------" "---------------------------------\n"); printf("key identifier\n DS7KEYID : "); for (i = 0; i < 4; i++) printf("%02x", f7->DS7KEYID[i]); printf("\nfirst 5 extent descriptions\n"); for (i = 0; i < 5; i++) { printf(" DS7EXTNT[%d] : %08x %08x " "(start trk %d, end trk %d)\n", i + 1, f7->DS7EXTNT[i].a, f7->DS7EXTNT[i].b, f7->DS7EXTNT[i].a, f7->DS7EXTNT[i].b); } printf("format identifier\n" " DS7FMTID : dec %d, hex %02x\n", f7->DS7FMTID, f7->DS7FMTID); printf("next 11 extent descriptions\n"); for (i = 0; i < 11; i++) { printf(" DS7ADEXT[%d] : %08x %08x " "(start trk %d, end trk %d)\n", i + 6, f7->DS7ADEXT[i].a, f7->DS7ADEXT[i].b, f7->DS7ADEXT[i].a, f7->DS7ADEXT[i].b); } printf("reserved field\n res1 : "); for (i = 0; i < 2; i++) printf("%02x", f7->res1[i]); printf("\npointer to next format 7 label\n" " DS7PTRDS : %04x%04x%02x " "(cyl %d, trk %d, blk %d)\n", f7->DS7PTRDS.cc, f7->DS7PTRDS.hh, f7->DS7PTRDS.b, vtoc_get_cyl_from_cchhb(&f7->DS7PTRDS), vtoc_get_head_from_cchhb(&f7->DS7PTRDS), f7->DS7PTRDS.b); } static void dasdview_print_vtoc_f9_nohead(format9_label_t *f9) { unsigned int i; printf("DS9KEYID : dec %d, hex %02x\n", f9->DS9KEYID, f9->DS9KEYID); printf("DS9SUBTY : dec %d, hex %02x\n", f9->DS9SUBTY, f9->DS9SUBTY); printf("DS9NUMF9 : dec %d, hex %02x\n", f9->DS9NUMF9, f9->DS9NUMF9); printf("res1 : hex "); for (i = 0; i < sizeof(f9->res1); i++) { if ((i > 0) && (i % 16 == 0)) printf("\n "); printf("%02x", f9->res1[i]); if ((i + 9) % 16 == 0) printf(" "); } printf("\n"); printf("DS9FMTID : dec %d, hex %02x\n", f9->DS9FMTID, f9->DS9FMTID); printf("res2 : hex "); for (i = 0; i < sizeof(f9->res2); i++) { if ((i > 0) && (i % 16 == 0)) printf("\n "); printf("%02x", f9->res2[i]); if ((i + 9) % 16 == 0) printf(" "); } printf("\n"); printf("pointer to next format 9 label\n" " DS9PTRDS : %04x%04x%02x " "(cyl %d, trk %d, blk %d)\n", f9->DS9PTRDS.cc, f9->DS9PTRDS.hh, f9->DS9PTRDS.b, vtoc_get_cyl_from_cchhb(&f9->DS9PTRDS), vtoc_get_head_from_cchhb(&f9->DS9PTRDS), f9->DS9PTRDS.b); } static void dasdview_print_vtoc_f9_raw(format9_label_t *f9) { printf("\n--- VTOC format 9 label ----------------------" "---------------------------------\n"); dasdview_print_vtoc_f9_nohead(f9); } static void dasdview_print_vtoc_dscb(dasdview_info_t *info, void *dscb) { format1_label_t *tmp = dscb; switch (tmp->DS1FMTID) { case 0x00: break; case 0xf1: if (info->vtoc_f1 || info->vtoc_all) dasdview_print_vtoc_f1_raw(dscb); break; case 0xf3: if (info->vtoc_f3 || info->vtoc_all) dasdview_print_vtoc_f3_raw(dscb); break; case 0xf4: if (info->vtoc_f4 || info->vtoc_all) dasdview_print_vtoc_f4_raw(dscb); break; case 0xf5: if (info->vtoc_f5 || info->vtoc_all) dasdview_print_vtoc_f5_raw(dscb); break; case 0xf7: if (info->vtoc_f7 || info->vtoc_all) dasdview_print_vtoc_f7_raw(dscb); break; case 0xf8: if (info->vtoc_f8 || info->vtoc_all) dasdview_print_vtoc_f8_raw(dscb); break; case 0xf9: if (info->vtoc_f9 || info->vtoc_all) dasdview_print_vtoc_f9_raw(dscb); break; default: printf("unrecognized DSCB of type: %x \n\n", tmp->DS1FMTID); break; } } static void dasdview_print_vtoc_f1(dasdview_info_t *info) { int j; printf("--- VTOC format 1 labels ----------------------" "---------------------------------\n"); if (info->f1c < 1) { printf("This VTOC doesn't contain a format 1 label.\n"); return; } for (j = 0; j < info->f1c; j++) { printf("\n--- format 1 DSCB number %d ---\n", j + 1); dasdview_print_format1_8_no_head(&info->f1[j]); } } static void dasdview_print_vtoc_f8(dasdview_info_t *info) { int j; printf("--- VTOC format 8 labels ----------------------" "---------------------------------\n"); if (info->f8c < 1) { printf("This VTOC doesn't contain a format 8 label.\n"); return; } for (j = 0; j < info->f8c; j++) { printf("\n--- format 8 DSCB number %d ---\n", j + 1); dasdview_print_format1_8_no_head(&info->f8[j]); } } static void dasdview_print_vtoc_f4(dasdview_info_t *info) { if (info->f4c < 1) { printf("\n--- VTOC format 4 label ----------------------" "---------------------------------\n"); printf("This VTOC doesn't contain a format 4 label.\n"); return; } dasdview_print_vtoc_f4_raw(&info->f4); } static void dasdview_print_vtoc_f5(dasdview_info_t *info) { if (info->f5c < 1) { printf("\n--- VTOC format 5 label ----------------------" "---------------------------------\n"); printf("This VTOC doesn't contain a format 5 label.\n"); return; } dasdview_print_vtoc_f5_raw(&info->f5); } static void dasdview_print_vtoc_f7(dasdview_info_t *info) { if (info->f7c < 1) { printf("\n--- VTOC format 7 label ----------------------" "---------------------------------\n"); printf("This VTOC doesn't contain a format 7 label.\n"); return; } dasdview_print_vtoc_f7_raw(&info->f7); } static void dasdview_print_vtoc_f9(dasdview_info_t *info) { int j; printf("\n--- VTOC format 9 label ----------------------" "---------------------------------\n"); if (info->f9c < 1) { printf("This VTOC doesn't contain a format 9 label.\n"); return; } for (j = 0; j < info->f9c; j++) { printf("\n--- format 9 DSCB number %d ---\n", j + 1); dasdview_print_vtoc_f9_nohead(&info->f9[j]); } } static void dasdview_print_vtoc_f3(void) { /* dasdfmt formatted DASD devices have no format3 labels, but since the * option exists for raw DASDs, we need to have some sensible message */ printf("\n--- VTOC format 3 label ----------------------" "---------------------------------\n"); printf("This VTOC doesn't contain a format 3 label.\n"); } static void dasdview_print_vtoc_standard(dasdview_info_t *info) { dasdview_read_vtoc(info); if (info->vtoc_info || info->vtoc_all) dasdview_print_vtoc_info(info); if (info->vtoc_f4 || info->vtoc_all) dasdview_print_vtoc_f4(info); if (info->vtoc_f5 || info->vtoc_all) dasdview_print_vtoc_f5(info); if (info->vtoc_f7 || info->vtoc_all) dasdview_print_vtoc_f7(info); if (info->vtoc_f1 || info->vtoc_all) dasdview_print_vtoc_f1(info); if (info->vtoc_f8 || info->vtoc_all) dasdview_print_vtoc_f8(info); if (info->vtoc_f9 || info->vtoc_all) dasdview_print_vtoc_f9(info); if (info->vtoc_f3 || info->vtoc_all) dasdview_print_vtoc_f3(); } /* a simple routine to print all records in the vtoc */ static void dasdview_print_vtoc_raw(dasdview_info_t *info) { struct dscbiterator *it; struct dscb *record; int rc; rc = lzds_dasd_read_vlabel(info->dasd); if (rc) { zt_error_print("error when reading label from device:" " rc=%d\n", rc); exit(EXIT_FAILURE); } rc = lzds_dasd_alloc_rawvtoc(info->dasd); if (rc == EINVAL) { zt_error_print("dasdview: Cannot read VTOC because disk does" " not contain valid VOL1 label.\n", info->device); exit(EXIT_FAILURE); } else if (rc) { zt_error_print("error when reading vtoc from device:" " rc=%d\n", rc); exit(EXIT_FAILURE); } rc = lzds_dasd_get_rawvtoc(info->dasd, &info->rawvtoc); if (rc || !info->rawvtoc) { zt_error_print("dasdview: libvtoc could not read vtoc\n"); exit(EXIT_FAILURE); } if (info->vtoc_info || info->vtoc_all) dasdview_print_vtoc_info_raw(info); rc = lzds_raw_vtoc_alloc_dscbiterator(info->rawvtoc, &it); if (rc) { zt_error_print("dasdview: could not allocate DSCB iterator\n"); exit(EXIT_FAILURE); } while (!lzds_dscbiterator_get_next_dscb(it, &record)) dasdview_print_vtoc_dscb(info, record); lzds_dscbiterator_free(it); } static void dasdview_print_vtoc(dasdview_info_t *info) { if (info->raw_track_access) dasdview_print_vtoc_raw(info); else dasdview_print_vtoc_standard(info); } static int dasdview_print_format1(unsigned int size, unsigned char *dumpstr) { unsigned int i; char asc[17], ebc[17]; for (i = 0; i < size; i++) { if ((i / 16) * 16 == i) { printf("\n| "); util_strlcpy(asc, (char *)dumpstr + i, 16); util_strlcpy(ebc, (char *)dumpstr + i, 16); } printf("%02X", dumpstr[i]); if (((i + 1) / 4) * 4 == i + 1) printf(" "); if (((i + 1) / 8) * 8 == i + 1) printf(" "); if (((i + 1) / 16) * 16 == i + 1) { vtoc_ebcdic_dec(asc, asc, 16); dot(asc); dot(ebc); printf("| %16.16s | %16.16s |", asc, ebc); } } return 0; } static int dasdview_print_format2(unsigned int size, unsigned char *dumpstr, unsigned long long begin) { unsigned int i; char asc[17], ebc[17]; for (i = 0; i < size; i++) { if ((i / 8) * 8 == i) { printf("\n | %13llu | %13llX | ", begin + (unsigned long long)i, begin + (unsigned long long)i); util_strlcpy(asc, (char *)dumpstr + i, 8); util_strlcpy(ebc, (char *)dumpstr + i, 8); } printf("%02X", dumpstr[i]); if (((i + 1) / 4) * 4 == i + 1) printf(" "); if (((i + 1) / 8) * 8 == i + 1) { vtoc_ebcdic_dec(asc, asc, 8); dot(asc); dot(ebc); printf("| %8.8s | %8.8s |", asc, ebc); } } return 0; } static void dasdview_view_standard(dasdview_info_t *info) { unsigned char dumpstr[DUMP_STRING_SIZE]; unsigned long long i = 0, j = 0, k = 0, count = 0; int fd, rc; unsigned long long a = 0; int b = 0; k = ((info->size) % 16LL); if (k != 0) info->size += (16LL - k); fd = open(info->device, O_RDONLY); if (fd == -1) { zt_error_print("dasdview: open error\n" "Unable to open device %s in read-only mode!\n", info->device); exit(EXIT_FAILURE); } j = (info->begin / SEEK_STEP); k = (info->begin % SEEK_STEP); /* seek in SEEK_STEP steps */ for (i = 1; i <= j; i++) { rc = lseek64(fd, SEEK_STEP, SEEK_CUR); if (rc == -1) { printf("*** rc: %d (%d) ***\n", rc, errno); printf("*** j: %llu ***\n", j); printf("*** k: %llu ***\n", k); printf("*** a: %llu ***\n", a); printf("*** b: %d ***\n", b); close(fd); zt_error_print("dasdview: seek error\n" "Unable to seek in device %s!\n", info->device); exit(EXIT_FAILURE); } b++; a += SEEK_STEP; } if (k > 0) { rc = lseek(fd, k, SEEK_CUR); if (rc == -1) { close(fd); zt_error_print("dasdview: seek error\n" "Unable to seek in device %s!\n", info->device); exit(EXIT_FAILURE); } } j = info->size / DUMP_STRING_SIZE; k = info->size % DUMP_STRING_SIZE; if (info->format1) { printf("+----------------------------------------+" "------------------+------------------+\n"); printf("| HEXADECIMAL |" " EBCDIC | ASCII |\n"); printf("| 01....04 05....08 09....12 13....16 |" " 1.............16 | 1.............16 |\n"); printf("+----------------------------------------+" "------------------+------------------+"); } else if (info->format2) { printf(" +---------------+---------------+----------------" "------+----------+----------+\n"); printf(" | BYTE | BYTE | HEXADECIMAL" " | EBCDIC | ASCII |\n"); printf(" | DECIMAL | HEXADECIMAL | 1 2 3 4 5 6 " "7 8 | 12345678 | 12345678 |\n"); printf(" +---------------+---------------+----------------" "------+----------+----------+"); } count = info->begin; for (i = 1; i <= j; i++) { bzero(dumpstr, DUMP_STRING_SIZE); rc = read(fd, &dumpstr, DUMP_STRING_SIZE); if (rc != DUMP_STRING_SIZE) { close(fd); zt_error_print("dasdview: read error\n" "Unable to read from device %s!\n", info->device); exit(EXIT_FAILURE); } if (info->format1) dasdview_print_format1(DUMP_STRING_SIZE, dumpstr); else if (info->format2) dasdview_print_format2(DUMP_STRING_SIZE, dumpstr, count); count += DUMP_STRING_SIZE; } if (k > 0) { bzero(dumpstr, DUMP_STRING_SIZE); rc = read(fd, &dumpstr, k); if (rc != (int)k) { close(fd); zt_error_print("dasdview: read error\n" "Unable to read from device %s!\n", info->device); exit(EXIT_FAILURE); } if (info->format1) dasdview_print_format1((unsigned int)k, dumpstr); else if (info->format2) dasdview_print_format2((unsigned int)k, dumpstr, count); } close(fd); if (info->format1) printf("\n+----------------------------------------+" "------------------+------------------+\n\n"); else if (info->format2) printf("\n +---------------+---------------+----------------" "------+----------+----------+\n\n"); } static void dasdview_print_format_raw(unsigned int size, char *dumpstr) { unsigned int i; char asc[17], ebc[17]; unsigned int residual, count; char *data; data = dumpstr; residual = size; while (residual) { /* we handle at most 16 bytes per line */ count = MIN(residual, 16u); bzero(asc, 17); bzero(ebc, 17); printf("|"); memcpy(asc, data, count); memcpy(ebc, data, count); for (i = 0; i < 16; ++i) { if ((i % 4) == 0) printf(" "); if ((i % 8) == 0) printf(" "); if (i < count) printf("%02X", data[i]); else printf(" "); } vtoc_ebcdic_dec(asc, asc, count); dot(asc); dot(ebc); printf(" | %16.16s | %16.16s |\n", asc, ebc); data += count; residual -= count; } } /* gets the pointer to an eckd record structure in memory and * prints a hex/ascii/ebcdic dump for it */ static void dasdview_print_raw_record(char *rec) { struct eckd_count *ecount; unsigned int cyl, head; ecount = (struct eckd_count *)rec; /* Note: the first 5 bytes of the count area are the * record ID and by convention these bytes are interpreted * as CCHHR (or ccccCCChR for large volumes) */ cyl = vtoc_get_cyl_from_cchhb(&ecount->recid); head = vtoc_get_head_from_cchhb(&ecount->recid); printf("+-----------------------------------------" "-------------------------------------+\n"); printf("| count area: " " |\n"); printf("| hex: %016llX " " |\n", *((unsigned long long *)ecount)); printf("| cylinder: %9d " " |\n", cyl); printf("| head: %9d " " |\n", head); printf("| record: %9d " " |\n", ecount->recid.b); printf("| key length: %9d " " |\n", ecount->kl); printf("| data length: %9d " " |\n", ecount->dl); printf("+-----------------------------------------" "-------------------------------------+\n"); printf("| key area: " " |\n"); printf("| HEXADECIMAL |" " EBCDIC | ASCII |\n"); printf("| 01....04 05....08 09....12 13....16 |" " 1.............16 | 1.............16 |\n"); printf("+----------------------------------------+" "------------------+------------------+\n"); dasdview_print_format_raw(ecount->kl, rec + sizeof(*ecount)); printf("+----------------------------------------+" "------------------+------------------+\n"); printf("| data area: " " |\n"); printf("| HEXADECIMAL |" " EBCDIC | ASCII |\n"); printf("| 01....04 05....08 09....12 13....16 |" " 1.............16 | 1.............16 |\n"); printf("+----------------------------------------+" "------------------+------------------+\n"); dasdview_print_format_raw(ecount->dl, rec + sizeof(*ecount) + ecount->kl); printf("+----------------------------------------+" "------------------+------------------+\n"); } static void dasdview_print_raw_track(char *trackdata, unsigned int cyl, unsigned int head) { struct eckd_count *ecount; char *data; u_int32_t record; record = 0; data = trackdata; do { printf("cylinder %u, head %u, record %u\n", cyl, head, record); dasdview_print_raw_record(data); printf("\n"); ecount = (struct eckd_count *)data; data += sizeof(*ecount) + ecount->kl + ecount->dl; ++record; if ((*(unsigned long long *)data) == ENDTOKEN) break; if ((unsigned long)data >= (unsigned long)trackdata + RAWTRACKSIZE) break; } while (1); } static void dasdview_view_raw(dasdview_info_t *info) { u_int64_t residual, trckstart, trckend, track, trckbuffsize; u_int64_t tracks_to_read, trckcount, i; char *trackdata; char *data; int rc; struct dasdhandle *dasdh; trckstart = info->begin / RAWTRACKSIZE; tracks_to_read = info->size / RAWTRACKSIZE; /* TODO: how large should we make our buffer? * The DASD device driver cannot read more than 16 tracks at once * but we can read a larger blob and the block layer will split up * the requests for us. */ trckbuffsize = MIN(tracks_to_read, 16u); /* track data must be page aligned for O_DIRECT */ trackdata = memalign(4096, trckbuffsize * RAWTRACKSIZE); if (!trackdata) { zt_error_print("failed to allocate memory\n"); exit(EXIT_FAILURE); } rc = lzds_dasd_alloc_dasdhandle(info->dasd, &dasdh); if (rc) { zt_error_print("failed to allocate memory\n"); exit(EXIT_FAILURE); } rc = lzds_dasdhandle_open(dasdh); if (rc) { lzds_dasdhandle_free(dasdh); zt_error_print("failed to open device\n"); exit(EXIT_FAILURE); } /* residual is the number of tracks we still have to read */ residual = tracks_to_read; track = trckstart; while (residual) { trckcount = MIN(trckbuffsize, residual); trckend = track + trckcount - 1; rc = lzds_dasdhandle_read_tracks_to_buffer(dasdh, track, trckend, trackdata); if (rc) { perror("Error on read"); exit(EXIT_FAILURE); } data = trackdata; for (i = 0; i < trckcount; ++i) { dasdview_print_raw_track(data, track / info->geo.heads, track % info->geo.heads); data += RAWTRACKSIZE; ++track; } residual -= trckcount; } free(trackdata); rc = lzds_dasdhandle_close(dasdh); lzds_dasdhandle_free(dasdh); if (rc < 0) { perror("Error on closing file"); exit(EXIT_FAILURE); } } static void dasdview_view(dasdview_info_t *info) { if (info->raw_track_access) dasdview_view_raw(info); else dasdview_view_standard(info); } static void dasdview_print_characteristic(dasdview_info_t *info) { dasd_information2_t dasd_info = info->dasd_info; printf("encrypted disk : %s\n", (dasd_info.characteristics[46] & 0x80) ? "yes" : "no"); printf("solid state device : %s\n", (dasd_info.characteristics[46] & 0x40) ? "yes" : "no"); } int main(int argc, char *argv[]) { dasdview_info_t info; int oc; unsigned long long max = 0LL; char *begin_param_str = NULL; char *size_param_str = NULL; int rc; util_prg_init(&prg); util_opt_init(opt_vec, NULL); bzero(&info, sizeof(info)); while (1) { oc = util_opt_getopt_long(argc, argv); switch (oc) { case 'h': util_prg_print_help(); util_opt_print_help(); exit(EXIT_SUCCESS); case 'v': util_prg_print_version(); exit(EXIT_SUCCESS); case 'b': begin_param_str = optarg; info.action_specified = 1; info.begin_specified = 1; break; case 's': size_param_str = optarg; info.action_specified = 1; info.size_specified = 1; break; case '1': info.format1 = 1; info.format2 = 0; break; case '2': info.format1 = 0; info.format2 = 1; break; case 'i': /* print general DASD information and geometry */ info.action_specified = 1; info.general_info = 1; break; case 'x': /* print extended DASD information */ info.action_specified = 1; info.extended_info = 1; break; case 'j': info.action_specified = 1; info.volser = 1; break; case 't': if (strcmp(optarg, "info") == 0) { info.vtoc_info = 1; } else if (strcmp(optarg, "f1") == 0) { info.vtoc_f1 = 1; } else if (strcmp(optarg, "f3") == 0) { info.vtoc_f3 = 1; } else if (strcmp(optarg, "f4") == 0) { info.vtoc_f4 = 1; } else if (strcmp(optarg, "f5") == 0) { info.vtoc_f5 = 1; } else if (strcmp(optarg, "f7") == 0) { info.vtoc_f7 = 1; } else if (strcmp(optarg, "f8") == 0) { info.vtoc_f8 = 1; } else if (strcmp(optarg, "f9") == 0) { info.vtoc_f9 = 1; } else if (strcmp(optarg, "all") == 0) { info.vtoc_all = 1; } else { zt_error_print("dasdview: usage error\n" "%s is no valid argument for" " option -t/--vtoc\n", optarg); exit(EXIT_FAILURE); } info.vtoc = 1; info.action_specified = 1; break; case 'l': info.action_specified = 1; info.vlabel_info = 1; break; case 'c': info.action_specified = 1; info.characteristic_specified = 1; break; case -1: /* End of options string - start of devices list */ info.device_id = optind; break; default: fprintf(stderr, "Try 'dasdview --help' for more" " information.\n"); exit(1); } if (oc == -1) break; } /* do some tests */ if (info.device_id >= argc) { zt_error_print("dasdview: usage error\n" "No device specified!"); exit(EXIT_FAILURE); } if (info.device_id + 1 < argc) { zt_error_print("dasdview: usage error\n" "More than one device specified!"); exit(EXIT_FAILURE); } if (info.device_id < argc) strcpy(info.device, argv[info.device_id]); dasdview_get_info(&info); if (info.raw_track_access) { rc = lzds_zdsroot_alloc(&info.zdsroot); if (rc) { zt_error_print("Could not allocate index\n"); exit(EXIT_FAILURE); } rc = lzds_zdsroot_add_device(info.zdsroot, info.device, &info.dasd); if (rc) { zt_error_print("Could not add device to index\n"); exit(EXIT_FAILURE); } } if (info.begin_specified) dasdview_parse_input(&info.begin, &info, begin_param_str); else info.begin = DEFAULT_BEGIN; if (info.raw_track_access) max = (unsigned long long)info.hw_cylinders * (unsigned long long)info.geo.heads * RAWTRACKSIZE; else max = (unsigned long long)info.hw_cylinders * (unsigned long long)info.geo.heads * (unsigned long long)info.geo.sectors * (unsigned long long)info.blksize; if (info.begin > max) { zt_error_print("dasdview: usage error\n" "'begin' value is not within disk range!"); exit(EXIT_FAILURE); } if (info.size_specified) dasdview_parse_input(&info.size, &info, size_param_str); else if (info.raw_track_access) info.size = RAWTRACKSIZE; else info.size = DEFAULT_SIZE; if ((info.begin_specified || info.size_specified) && ((info.begin + info.size) > max)) { zt_error_print("dasdview: usage error\n" "'begin' + 'size' is not within " "disk range!"); exit(EXIT_FAILURE); } if ((info.begin_specified || info.size_specified) && (!info.format1 && !info.format2)) info.format1 = 1; if ((info.format1 || info.format2) && (!info.size_specified && !info.begin_specified)) { zt_error_print("dasdview: usage error\n" "Options -1 or -2 make only sense with " "options -b or -s!"); exit(EXIT_FAILURE); } /* do the output */ if (info.begin_specified || info.size_specified) dasdview_view(&info); if (info.general_info || info.extended_info) dasdview_print_general_info(&info); if (info.extended_info) dasdview_print_extended_info(&info); if (info.volser) dasdview_print_volser(&info); if (info.vlabel_info) dasdview_print_vlabel(&info); if (info.vtoc) dasdview_print_vtoc(&info); if (!info.action_specified) printf("No action specified.\n"); if (info.characteristic_specified) dasdview_print_characteristic(&info); return 0; } s390-tools-2.38.0/dasdview/dasdview.h000066400000000000000000000051471502674226300172430ustar00rootroot00000000000000/* * dasdview - Display DASD and VTOC information or dump the contents of a DASD * * Copyright IBM Corp. 2002, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef DASDVIEW_H #define DASDVIEW_H #include /******************************************************************************** * SECTION: Definitions needed for DASD-API (see dasd.h) *******************************************************************************/ /* * values to be used for dasd_information2_t.format * 0x00: NOT formatted * 0x01: Linux disc layout * 0x02: Common disc layout */ #define DASD_FORMAT_NONE 0 #define DASD_FORMAT_LDL 1 #define DASD_FORMAT_CDL 2 /* * values to be used for dasd_information2_t.features * 0x00: default features * 0x01: readonly (ro) * 0x02: use diag discipline (diag) */ #define DASD_FEATURE_DEFAULT 0 #define DASD_FEATURE_READONLY 1 #define DASD_FEATURE_USEDIAG 2 /******************************************************************************** * SECTION: DASDVIEW internal types *******************************************************************************/ #define LINE_LENGTH 80 #define DASDVIEW_ERROR "dasdview:" #define DEFAULT_BEGIN 0 #define DEFAULT_SIZE 128 #define NO_PART_LABELS 8 /* for partition related labels (f1,f8 and f9) */ #define SEEK_STEP 4194304LL #define DUMP_STRING_SIZE 1024LL #define ERROR_STRING_SIZE 1024 static char error_str[ERROR_STRING_SIZE]; enum dasdview_failure { open_error, seek_error, read_error, ioctl_error, usage_error, disk_layout, vtoc_error }; typedef struct dasdview_info { char device[PATH_MAX]; dasd_information2_t dasd_info; int dasd_info_version; unsigned int blksize; struct hd_geometry geo; u_int32_t hw_cylinders; unsigned long long begin; unsigned long long size; int format1; int format2; int action_specified; int begin_specified; int size_specified; int characteristic_specified; int device_id; int general_info; int extended_info; int volser; int vtoc; int vtoc_info; int vtoc_f1; int vtoc_f3; int vtoc_f4; int vtoc_f5; int vtoc_f7; int vtoc_f8; int vtoc_f9; int vtoc_all; int vlabel_info; format1_label_t f1[NO_PART_LABELS]; format4_label_t f4; format5_label_t f5; format7_label_t f7; format1_label_t f8[NO_PART_LABELS]; format9_label_t f9[NO_PART_LABELS]; int f1c; int f4c; int f5c; int f7c; int f8c; int f9c; char busid[DASD_BUS_ID_SIZE]; int busid_valid; int raw_track_access; struct zdsroot *zdsroot; struct raw_vtoc *rawvtoc; struct dasd *dasd; } dasdview_info_t; #endif /* DASDVIEW_H */ s390-tools-2.38.0/dump2tar/000077500000000000000000000000001502674226300152055ustar00rootroot00000000000000s390-tools-2.38.0/dump2tar/Makefile000066400000000000000000000002351502674226300166450ustar00rootroot00000000000000# Common definitions include ../common.mak all: $(MAKE) -C src install: all $(MAKE) -C src install $(MAKE) -C man install clean: $(MAKE) -C src clean s390-tools-2.38.0/dump2tar/include/000077500000000000000000000000001502674226300166305ustar00rootroot00000000000000s390-tools-2.38.0/dump2tar/include/buffer.h000066400000000000000000000032761502674226300202620ustar00rootroot00000000000000/* * dump2tar - tool to dump files and command output into a tar archive * * Data buffering functions * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef BUFFER_H #define BUFFER_H #include #include #include /* Buffers for building tar file entries */ struct buffer { size_t total; /* Total number of bytes in buffer */ size_t off; /* Current offset to next free byte in memory buffer */ size_t size; /* Memory buffer size */ char *addr; /* Memory buffer address */ bool fd_open; /* Has fd been openend yet? */ FILE *file; /* FILE * of file containing previous buffer data */ int fd; /* Handle of file containing previous buffer data */ }; void buffer_init(struct buffer *buffer, size_t size); struct buffer *buffer_alloc(size_t size); void buffer_reset(struct buffer *buffer); void buffer_close(struct buffer *buffer); void buffer_free(struct buffer *buffer, bool dyn); int buffer_open(struct buffer *buffer); int buffer_flush(struct buffer *buffer); ssize_t buffer_make_room(struct buffer *buffer, size_t size, bool usefile, size_t max_buffer_size); int buffer_truncate(struct buffer *buffer, size_t len); ssize_t buffer_read_fd(struct buffer *buffer, int fd, size_t chunk, bool usefile, size_t max_buffer_size); int buffer_add_data(struct buffer *buffer, char *addr, size_t len, bool usefile, size_t max_buffer_size); typedef int (*buffer_cb_t)(void *data, void *addr, size_t len); int buffer_iterate(struct buffer *buffer, buffer_cb_t cb, void *data); void buffer_print(struct buffer *buffer); #endif /* BUFFER_H */ s390-tools-2.38.0/dump2tar/include/dref.h000066400000000000000000000012161502674226300177210ustar00rootroot00000000000000/* * dump2tar - tool to dump files and command output into a tar archive * * Reference counting for directory handles * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef DREF_H #define DREF_H #include #include /* Multiple jobs may refer to an open DIR * - need reference counting */ struct dref { DIR *dd; int dirfd; unsigned int count; }; struct dref *dref_create(const char *dirname); struct dref *dref_get(struct dref *dref); void dref_put(struct dref *dref); #endif /* DREF_H */ s390-tools-2.38.0/dump2tar/include/dump.h000066400000000000000000000024101502674226300177430ustar00rootroot00000000000000/* * dump2tar - tool to dump files and command output into a tar archive * * Main dump logic * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef DUMP_H #define DUMP_H #include #include #include #include "strarray.h" #define NUM_EXCLUDE_TYPES 7 struct dump_spec { char *inname; char *outname; bool is_cmd; }; struct dump_opts { bool add_cmd_status; bool append; bool dereference; bool exclude_type[NUM_EXCLUDE_TYPES]; bool gzip; bool ignore_failed_read; bool no_eof; bool quiet; bool recursive; bool threaded; bool verbose; const char *output_file; int file_timeout; int timeout; long jobs; long jobs_per_cpu; size_t file_max_size; size_t max_buffer_size; size_t max_size; size_t read_chunk_size; struct strarray exclude; struct dump_spec *specs; unsigned int num_specs; }; struct dump_opts *dump_opts_new(void); int dump_opts_set_type_excluded(struct dump_opts *opts, char c); void dump_opts_add_spec(struct dump_opts *opts, char *inname, char *outname, bool is_cmd); void dump_opts_free(struct dump_opts *opts); int dump_to_tar(struct dump_opts *opts); #endif /* DUMP_H */ s390-tools-2.38.0/dump2tar/include/global.h000066400000000000000000000007631502674226300202470ustar00rootroot00000000000000/* * dump2tar - tool to dump files and command output into a tar archive * * Global variables * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef GLOBAL_H #define GLOBAL_H #include extern bool global_threaded; extern bool global_debug; extern bool global_verbose; extern bool global_quiet; extern bool global_timestamps; #endif /* GLOBAL_H */ s390-tools-2.38.0/dump2tar/include/idcache.h000066400000000000000000000012221502674226300203560ustar00rootroot00000000000000/* * dump2tar - tool to dump files and command output into a tar archive * * Caches for user and group ID lookups * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef IDCACHE_H #define IDCACHE_H #include #include /* Buffer sizes for getpwuid_r and getgid_r calls (bytes) */ #define PWD_BUFFER_SIZE 4096 #define GRP_BUFFER_SIZE 4096 void uid_to_name(uid_t uid, char *name, size_t len); void gid_to_name(gid_t gid, char *name, size_t len); void idcache_cleanup(void); #endif /* IDCACHE_H */ s390-tools-2.38.0/dump2tar/include/misc.h000066400000000000000000000057221502674226300177420ustar00rootroot00000000000000/* * dump2tar - tool to dump files and command output into a tar archive * * Helper functions * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef MISC_H #define MISC_H #include #include #include #include #include "lib/util_libc.h" #include "global.h" #define MSG_LEN 256 #define DBG(...) \ do { \ if (global_debug) \ debug(__FILE__, __LINE__, ##__VA_ARGS__); \ } while (0) #define mwarn(fmt, ...) _mwarn(true, (fmt), ##__VA_ARGS__) #define mwarnx(fmt, ...) _mwarn(false, (fmt), ##__VA_ARGS__) /* Helper macro for constructing messages in variables */ #define HANDLE_RC(rc, max, off, label) \ do { \ if ((rc) > 0) \ (off) += (rc); \ if ((off) > (max)) \ goto label; \ } while (0) /* Program exit codes */ #define EXIT_OK 0 #define EXIT_RUNTIME 1 #define EXIT_USAGE 2 /* Number of nanoseconds in a second */ #define NSEC_PER_SEC 1000000000L #define NSEC_PER_MSEC 1000000L #define NSEC_PER_USEC 1000L extern struct timespec main_start_ts; struct dref; int misc_write_data(int fd, char *addr, size_t len); ssize_t misc_read_data(int fd, char *addr, size_t len); void inc_timespec(struct timespec *ts, time_t sec, long nsec); void set_timespec(struct timespec *ts, time_t sec, long nsec); bool ts_before(struct timespec *a, struct timespec *b); int snprintf_duration(char *buff, size_t len, struct timespec *start, struct timespec *end); char *get_threadname(void); void debug(const char *file, unsigned long line, const char *format, ...); void _mwarn(bool print_errno, const char *format, ...); void verb(const char *format, ...); void info(const char *format, ...); #define mmalloc(len) util_zalloc(len) #define mcalloc(n, len) util_zalloc((n) * (len)) #define mrealloc(ptr, len) util_realloc((ptr), (len)) #define mstrdup(str) util_strdup(str) #define masprintf(fmt, ...) __masprintf(__func__, __FILE__, __LINE__, \ (fmt), ##__VA_ARGS__) char *__masprintf(const char *func, const char *file, int line, const char *fmt, ...); #define set_threadname(fmt, ...) __set_threadname(__func__, __FILE__, \ __LINE__, (fmt), \ ##__VA_ARGS__) void __set_threadname(const char *func, const char *file, int line, const char *fmt, ...); void clear_threadname(void); void chomp(char *str, char *c); void lchomp(char *str, char *c); void remove_double_slashes(char *str); int stat_file(bool dereference, const char *abs, const char *rel, struct dref *dref, struct stat *st); void set_dummy_stat(struct stat *st); bool starts_with(const char *str, const char *prefix); bool ends_with(const char *str, const char *suffix); int cmd_child(int fd, char *cmd); int cmd_open(char *cmd, pid_t *pid_ptr); int cmd_close(int fd, pid_t pid, int *status_ptr); void misc_init(void); void misc_cleanup(void); void set_stdout_data(void); #endif /* MISC_H */ s390-tools-2.38.0/dump2tar/include/strarray.h000066400000000000000000000013111502674226300206440ustar00rootroot00000000000000/* * dump2tar - tool to dump files and command output into a tar archive * * Dynamically growing string arrays * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef STRARRAY_H #define STRARRAY_H /* A string array that can grow in size */ struct strarray { unsigned int num; char **str; }; void free_strarray(struct strarray *array); void add_str_to_strarray(struct strarray *array, const char *str); void add_vstr_to_strarray(struct strarray *array, const char *fmt, ...); int add_file_to_strarray(struct strarray *array, const char *filename); #endif /* STRARRAY_H */ s390-tools-2.38.0/dump2tar/include/tar.h000066400000000000000000000023161502674226300175710ustar00rootroot00000000000000/* * dump2tar - tool to dump files and command output into a tar archive * * TAR file generation * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef TAR_H #define TAR_H #include #include #include #define TYPE_REGULAR '0' #define TYPE_LINK '2' #define TYPE_DIR '5' #define TAR_BLOCKSIZE 512 struct buffer; /* emit_cb_t - Callback used for emitting chunks of a byte stream * @data: Arbitrary pointer passed via the @data parameter of the * tar_emit_file_* functions * @addr: Pointer to data * @len: Size of data * Return %0 on success. Returning non-zero will indicate failure and abort * further data emission. */ typedef int (*emit_cb_t)(void *data, void *addr, size_t len); int tar_emit_file_from_buffer(char *filename, char *link, size_t len, struct stat *stat, char type, struct buffer *content, emit_cb_t emit_cb, void *data); int tar_emit_file_from_data(char *filename, char *link, size_t len, struct stat *stat, char type, void *addr, emit_cb_t emit_cb, void *data); #endif /* TAR_H */ s390-tools-2.38.0/dump2tar/man/000077500000000000000000000000001502674226300157605ustar00rootroot00000000000000s390-tools-2.38.0/dump2tar/man/Makefile000066400000000000000000000003021502674226300174130ustar00rootroot00000000000000# Common definitions include ../../common.mak all: install: $(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man1 $(INSTALL) -m 644 -c dump2tar.1 $(DESTDIR)$(MANDIR)/man1 clean: .PHONY: all clean s390-tools-2.38.0/dump2tar/man/dump2tar.1000066400000000000000000000234101502674226300176000ustar00rootroot00000000000000.\" Copyright 2017 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .\" Macro for inserting an option description prologue. .\" .OD [] [args] .de OD . ds args " . if !'\\$3'' .as args \fI\\$3\fP . if !'\\$4'' .as args \\$4 . if !'\\$5'' .as args \fI\\$5\fP . if !'\\$6'' .as args \\$6 . if !'\\$7'' .as args \fI\\$7\fP . PD 0 . if !'\\$2'' .IP "\fB\-\\$2\fP \\*[args]" 4 . if !'\\$1'' .IP "\fB\-\-\\$1\fP \\*[args]" 4 . PD .. .\" Macro for inserting code line. .\" .CL .de CL . ds pfont \\n[.f] . nh . na . ft CW \\$* . ft \\*[pfont] . ad . hy . br .. .\" Macro for inserting a man page reference. .\" .MP man-page section [suffix] .de MP . nh . na . BR \\$1 (\\$2)\\$3 . ad . hy .. . .TH "dump2tar" "1" "2016\-09\-02" "" "" . .SH "NAME" dump2tar - Gather file contents and command output into a tar archive . . .SH "SYNOPSIS" .B "dump2tar " .RI "[" "OPTIONS" "] " "SPECS" . . .SH "DESCRIPTION" .B dump2tar creates a tar archive from the contents of any files, including files of unknown size. Examples for files of unknown size are: .IP \(bu 3 Named pipes (FIFOs) .PP .IP \(bu 3 Particular Linux kernel debugfs or sysfs files .PP .IP \(bu 3 Character or block devices .PP When adding such a file, .B dump2tar first reads all available data until an end-of-file indication is found. From this data, it then creates a regular file entry in the resulting tar archive. By default, symbolic links and directories are preserved in the archive in their original form. .B dump2tar can also: .IP \(bu 3 Add files under a different name .PP .IP \(bu 3 Run arbitrary commands and add the resulting command output as a regular file .PP . . .SH "FILE SPECIFICATIONS" . This section describes the format of the .I SPECS argument mentioned in the command synopsis. Use the following command line syntax to identify data sources and to specify file names within the archive: .PP .TP .I "PATH" Adds the contents of the file system subtree at file system location .I PATH (with possible exceptions described by options) in the archive under the same file name as on the file system. .PP . . .TP .IR "FILENAME" ":=" "PATH" Adds the contents of the file at file system location .I PATH in the archive under the name specified by .IR FILENAME . .PP . . .TP .IR "FILENAME" "|=" "CMDLINE" Runs the command .IR CMDLINE and captures both the resulting standard output and standard error streams. Adds the collected output as a regular file named .I FILENAME in the resulting archive. You can also include the resulting program exit code by using option \-\-add\-cmd\-status. .PP . You can also specify "\-\-". All specifications that follow are interpreted as simple file names. This is useful for archiving files that contain ":=" or "|=". .PP . . .SH "OUTPUT OPTIONS" . .OD "output\-file" "o" "TARFILE" Writes the resulting tar archive to .IR TARFILE . An existing file at the specified file system location is overwritten. If this option is omitted or if "\-" is specified for .IR TARFILE , the archive is written to the standard output stream. .PP . . .OD "gzip" "z" "" Compresses the resulting tar archive using gzip. .PP . . .OD "max\-size" "m" "VALUE" Sets an upper size limit, in bytes, for the resulting archive. If this limit is exceeded after adding a file, no further files are added. .PP . . .OD "timeout" "t" "VALUE" Sets an upper time limit, in seconds, for the archiving process. If this limit is exceeded while adding a file, that file is truncated and no further files are added. .PP . . .OD "no-eof" "" "" Does not write an end-of-file marker. Use this option if you want to create an archive that can be extended by appending additional tar archive data. Note: Do not use this option for the final data to be added. A valid tar archive requires a trailing end-of-file marker. .PP . . .OD "append" "" "" Appends data to the end of the archive. Use this option to incrementally build a tar file by repeatedly calling .BR dump2tar . You must specify the \-\-no\-eof option for each but the final call of .BR dump2tar . .PP . . .OD "add-cmd-status" "" "" Adds a separate file named .RI \(dq FILENAME .cmdstatus\(dq for each command output added through the .RI \(dq FILENAME |= CMDLINE \(dq notation (see FILE SPECIFICATIONS). This file contains information about the exit status of the process that executed the command: . .RS 8 .TP .RI EXITSTATUS= VALUE Unless .I VALUE is -1, the process ended normally with the specified exit value. .PP . .TP .RI TERMSIG= VALUE Unless .I VALUE is -1, the process was stopped by a signal of the specified number. .PP . .TP .RI WAITPID_ERRNO= VALUE Unless .I VALUE is -1, an attempt to obtain the status of the process failed with the specified error. .PP .RE . . . .SH "INPUT OPTIONS" . .OD "files\-from" "F" "FILENAME" Reads input data specifications (see FILE SPECIFICATIONS) from .IR FILENAME , one specification per line. Each line contains either a file name or a .IR FILENAME := PATH or .IR FILENAME |= CMDLINE specification. Empty lines are ignored. A line can also consist of only "\-\-". All lines following this specification are interpreted as simple file names. This is useful for archiving files that contain ":=" or "|=". .PP . . .OD "ignore\-failed\-read" "i" "" Continues after read errors. By default, .B dump2tar stops processing after encountering errors while reading an input file. With this option, .B dump2tar prints a warning message and adds an empty entry for the erroneous file in the archive. .PP . . .OD "buffer\-size" "b" "VALUE" Reads data from input files in chunks of .I VALUE bytes. Large values can accelerate the archiving process for large files at the cost of increased memory usage. The default value is 1048576. .PP . . .OD "file\-timeout" "T" "VALUE" Sets an upper time limit, in seconds, for reading an input file. .B dump2tar stops processing a file when the time limit is exceeded. Archive entries for such files are truncated to the amount of data that is collected by the time the limit is reached. .PP . . .OD "file\-max\-size" "M" "N" Sets an upper size limit, in bytes, for an input file. .B dump2tar stops processing a file when the size limit is exceeded. Archive entries for such files are truncated to the specified size. .PP . . .OD "jobs" "j" "N" By default, .B dump2tar processes one file at a time. With this option, .B dump2tar processes .I N files in parallel. Parallel processing can accelerate the archiving process, especially if input files are located on slow devices, or when output from multiple commands is added to the archive. Note: Use .B tar option \-\-delay\-directory\-restore when extracting files from an archive created with \-\-jobs to prevent conflicts with directory permissions and modification times. .PP . . .OD "jobs\-per\-cpu" "J" "N" Processes .I N files for each online CPU in parallel. Parallel processing can accelerate the archiving process, especially if input files are located on slow devices, or when output from multiple commands is added to the archive. Note: Use .B tar option \-\-delay\-directory\-restore when extracting files from an archive created with \-\-jobs\-per\-cpu to prevent conflicts with directory permissions and modification times. .PP . . .OD "exclude" "x" "PATTERN" Does not add files to the archive if their file names match .IR PATTERN . .I PATTERN is an expression that uses the shell wildcards. .PP . . .OD "exclude\-from" "X" "FILENAME" Does not add files to the archive if their names match at least one of the patterns listed in the pattern file with name .IR FILENAME . In the pattern file, each line specifies an expression that uses the shell wildcards. .PP . . .OD "exclude\-type" "" "TYPE" Does not add files to the archive if they match at least one of the file types specified with .IR TYPE . .I TYPE uses one or more of the characters "fdcbpls", where: .RS 8 .IP f 3 regular files .PP .IP d 3 directories .PP .IP c 3 character devices .PP .IP b 3 block devices .PP .IP p 3 named pipes (FIFOs) .PP .IP l 3 symbolic links .PP .IP s 3 sockets .PP .RE . .PP . . .OD "dereference" "" "" Adds the content of link targets instead of symbolic links. .PP . . .OD "no\-recursion" "" "" Does not add files from sub\-directories. By default, .B dump2tar adds archive entries for specified directories, and for the files within these directories. With this option, a specified directory results in a single entry for the directory. Any contained files to be included must be specified explicitly. .PP . . .SH "MISC OPTIONS" . .OD "help" "h" "" Prints an overview of available options, then exits. .PP . . .OD "verbose" "V" "" Prints additional informational output. .PP . . .OD "quiet" "q" "" Suppresses printing of informational output. .PP . . . .SH "EXAMPLES" . .\fB .CL # dump2tar a b \-o archive.tar .\fR .RS 4 Creates a tar archive named archive.tar containing files a and b. .RE .PP . .\fB .CL # dump2tar /proc \-o procdump.tar.gz \-z \-i \-T 1 \-M 1048576 .\fR .RS 4 Creates a gzip compressed tar archive named procdump.tar.gz that contains all procfs files. Unreadable files are ignored. Files are truncated when the first of the two limiting conditions is reached, either 1048576 bytes of content or the reading time of 1 second. .RE .PP . .\fB .CL # dump2tar '|=dmesg' '|=lspci' \-o data.tar .\fR .RS 4 Creates a tar archive named data.tar containing the output of the 'dmesg' and 'lspci' commands. .RE .PP . .\fB .CL # dump2tar /sys/kernel/debug/ -x '*/tracing/*' -o debug.tar -i .\fR .RS 4 Creates a tar archive named debug.tar containing the contents of directory /sys/kernel/debug/ while excluding any file that is located in a sub-directory named 'tracing'. .RE .PP . . .SH "EXIT CODES" .TP .B 0 The program finished successfully .TP .B 1 A run-time error occurred .TP .B 2 The specified command was not valid .PP . . .SH "SEE ALSO" .MP dump2tar 1 , .MP tar 1 s390-tools-2.38.0/dump2tar/src/000077500000000000000000000000001502674226300157745ustar00rootroot00000000000000s390-tools-2.38.0/dump2tar/src/Makefile000066400000000000000000000011671502674226300174410ustar00rootroot00000000000000# Common definitions include ../../common.mak ALL_CPPFLAGS += -I../include -Wno-unused-parameter LDLIBS += -lpthread -lrt ifneq ($(HAVE_ZLIB),0) ALL_CPPFLAGS += -DHAVE_ZLIB LDLIBS += -lz endif core_objects = buffer.o dref.o global.o dump.o idcache.o misc.o strarray.o tar.o libs = $(rootdir)/libutil/libutil.a check_dep_zlib: $(call check_dep, \ "dump2tar", \ "zlib.h", \ "zlib-devel or libz-dev", \ "HAVE_ZLIB=0") all: check_dep_zlib dump2tar dump2tar: $(core_objects) dump2tar.o $(libs) install: dump2tar $(INSTALL) -c dump2tar $(DESTDIR)$(USRBINDIR) clean: @rm -f dump2tar *.o .PHONY: all install clean s390-tools-2.38.0/dump2tar/src/buffer.c000066400000000000000000000145701502674226300174200ustar00rootroot00000000000000/* * dump2tar - tool to dump files and command output into a tar archive * * Data buffering functions * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include "buffer.h" #include "misc.h" void buffer_print(struct buffer *buffer) { fprintf(stderr, "DEBUG: buffer at %p\n", (void *) buffer); if (!buffer) return; fprintf(stderr, "DEBUG: total=%zu\n", buffer->total); fprintf(stderr, "DEBUG: off=%zu\n", buffer->off); fprintf(stderr, "DEBUG: size=%zu\n", buffer->size); fprintf(stderr, "DEBUG: addr=%p\n", (void *) buffer->addr); fprintf(stderr, "DEBUG: fd_open=%d\n", buffer->fd_open); fprintf(stderr, "DEBUG: fd=%d\n", buffer->fd); if (buffer->fd_open) { fprintf(stderr, "DEBUG: fd->pos=%zu\n", lseek(buffer->fd, 0, SEEK_CUR)); } } /* Initialize @buffer to hold @size bytes in memory */ void buffer_init(struct buffer *buffer, size_t size) { memset(buffer, 0, sizeof(struct buffer)); buffer->addr = mmalloc(size); buffer->size = size; } /* Allocate a new buffer for holding @size bytes in memory */ struct buffer *buffer_alloc(size_t size) { struct buffer *buffer; buffer = mmalloc(sizeof(struct buffer)); buffer_init(buffer, size); return buffer; } /* Forget about any data stored in @buffer */ void buffer_reset(struct buffer *buffer) { buffer->total = 0; buffer->off = 0; if (buffer->fd_open) { if (ftruncate(buffer->fd, 0)) mwarn("Cannot truncate temporary file"); if (lseek(buffer->fd, 0, SEEK_SET) == (off_t) -1) mwarn("Cannot seek in temporary file"); } } /* Close buffer file associated with @buffer */ void buffer_close(struct buffer *buffer) { if (!buffer->fd_open) return; fclose(buffer->file); buffer->fd = 0; buffer->fd_open = false; } /* Release all resources associated with @buffer. If @dyn is %true, also free * @buffer itself. */ void buffer_free(struct buffer *buffer, bool dyn) { if (!buffer) return; buffer_reset(buffer); buffer_close(buffer); free(buffer->addr); if (dyn) free(buffer); } /* Open a buffer file for @buffer. Return %EXIT_OK on success, %EXIT_RUNTIME * otherwise. */ int buffer_open(struct buffer *buffer) { if (buffer->fd_open) return EXIT_OK; buffer->file = tmpfile(); if (!buffer->file) { mwarn("Could not create temporary file"); return EXIT_RUNTIME; } buffer->fd = fileno(buffer->file); buffer->fd_open = true; return EXIT_OK; } /* Write data in memory of @buffer to buffer file. Return %EXIT_OK on success, * %EXIT_RUNTIME otherwise. */ int buffer_flush(struct buffer *buffer) { if (buffer->off == 0) return EXIT_OK; if (buffer_open(buffer)) return EXIT_RUNTIME; if (misc_write_data(buffer->fd, buffer->addr, buffer->off)) { mwarn("Could not write to temporary file"); return EXIT_RUNTIME; } buffer->off = 0; return EXIT_OK; } /* Try to ensure that at least @size bytes are available at * @buffer->addr[buffer->off]. Return the actual number of bytes available or * @-1 on error. If @usefile is %true, make use of a buffer file if * the total buffer size exceeds @max_buffer_size. */ ssize_t buffer_make_room(struct buffer *buffer, size_t size, bool usefile, size_t max_buffer_size) { size_t needsize; if (size > max_buffer_size && usefile) size = max_buffer_size; needsize = buffer->off + size; if (needsize <= buffer->size) { /* Room available */ return size; } if (needsize > max_buffer_size && usefile) { /* Need to write out memory buffer to buffer file */ if (buffer_flush(buffer)) return -1; if (size <= buffer->size) return size; needsize = size; } /* Need to increase memory buffer size */ buffer->size = needsize; buffer->addr = mrealloc(buffer->addr, buffer->size); return size; } /* Try to read @chunk bytes from @fd to @buffer. Return the number of bytes * read on success, %0 on EOF or %-1 on error. */ ssize_t buffer_read_fd(struct buffer *buffer, int fd, size_t chunk, bool usefile, size_t max_buffer_size) { ssize_t c = buffer_make_room(buffer, chunk, usefile, max_buffer_size); DBG("buffer_read_fd wanted %zd got %zd", chunk, c); if (c < 0) return c; c = read(fd, buffer->addr + buffer->off, c); if (c > 0) { buffer->total += c; buffer->off += c; } return c; } /* Add @len bytes at @addr to @buffer. If @addr is %NULL, add zeroes. Return * %EXIT_OK on success, %EXIT_RUNTIME otherwise. */ int buffer_add_data(struct buffer *buffer, char *addr, size_t len, bool usefile, size_t max_buffer_size) { ssize_t c; while (len > 0) { c = buffer_make_room(buffer, len, usefile, max_buffer_size); if (c < 0) return EXIT_RUNTIME; if (addr) { memcpy(buffer->addr + buffer->off, addr, c); addr += c; } else { memset(buffer->addr + buffer->off, 0, c); } buffer->total += c; buffer->off += c; len -= c; } return EXIT_OK; } /* Call @cb for all chunks of data in @buffer. @data is passed to @cb. */ int buffer_iterate(struct buffer *buffer, buffer_cb_t cb, void *data) { int rc; ssize_t r; if (buffer->total == 0) return EXIT_OK; if (!buffer->fd_open) return cb(data, buffer->addr, buffer->off); /* Free memory buffer to be used as copy buffer */ if (buffer_flush(buffer)) return EXIT_RUNTIME; if (lseek(buffer->fd, 0, SEEK_SET) == (off_t) -1) { mwarn("Cannot seek in temporary file"); return EXIT_RUNTIME; } /* Copy data from temporary file to target file */ while ((r = misc_read_data(buffer->fd, buffer->addr, buffer->size)) != 0) { if (r < 0) { mwarn("Cannot read from temporary file"); return EXIT_RUNTIME; } rc = cb(data, buffer->addr, r); if (rc) return rc; } return EXIT_OK; } /* Truncate @buffer to at most @len bytes */ int buffer_truncate(struct buffer *buffer, size_t len) { size_t delta; if (buffer->total <= len) return EXIT_OK; delta = buffer->total - len; buffer->total = len; if (buffer->fd_open && delta > buffer->off) { /* All of memory and some of file buffer is truncated */ buffer->off = 0; if (ftruncate(buffer->fd, len)) { mwarn("Cannot truncate temporary file"); return EXIT_RUNTIME; } if (lseek(buffer->fd, len, SEEK_SET) == (off_t) -1) { mwarn("Cannot seek in temporary file"); return EXIT_RUNTIME; } } else { /* Only memory buffer is truncated */ buffer->off -= delta; } return EXIT_OK; } s390-tools-2.38.0/dump2tar/src/dref.c000066400000000000000000000035701502674226300170650ustar00rootroot00000000000000/* * dump2tar - tool to dump files and command output into a tar archive * * Reference counting for directory handles * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include "dref.h" #include "global.h" #include "misc.h" /* dref_mutex serializes access to drefs */ static pthread_mutex_t dref_mutex = PTHREAD_MUTEX_INITIALIZER; static unsigned long num_open_dirs; static unsigned long num_open_dirs_max; /* Lock dref mutex */ static void dref_lock(void) { if (!global_threaded) return; pthread_mutex_lock(&dref_mutex); } /* Unlock dref mutex */ static void dref_unlock(void) { if (!global_threaded) return; pthread_mutex_unlock(&dref_mutex); } /* Create a reference count managed directory handle for @dirname */ struct dref *dref_create(const char *dirname) { struct dref *dref; DIR *dd; dd = opendir(dirname); DBG("opendir(%s)=%p (total=%lu)", dirname, dd, ++num_open_dirs); if (!dd) { num_open_dirs--; return NULL; } if (num_open_dirs > num_open_dirs_max) num_open_dirs_max = num_open_dirs; dref = mmalloc(sizeof(struct dref)); dref->dd = dd; dref->dirfd = dirfd(dd); dref->count = 1; return dref; } /* Obtain a reference to @dref */ struct dref *dref_get(struct dref *dref) { if (dref) { dref_lock(); dref->count++; dref_unlock(); } return dref; } /* Release a reference to @dref. If this was the last reference, lose the * associated directory handle and free @dref. */ void dref_put(struct dref *dref) { if (dref) { dref_lock(); dref->count--; if (dref->count == 0) { num_open_dirs--; DBG("closedir(%p) (total=%lu, max=%lu)", dref->dd, num_open_dirs, num_open_dirs_max); closedir(dref->dd); free(dref); } dref_unlock(); } } s390-tools-2.38.0/dump2tar/src/dump.c000066400000000000000000001276231502674226300171200ustar00rootroot00000000000000/* * dump2tar - tool to dump files and command output into a tar archive * * Main dump logic * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_ZLIB #include #endif /* HAVE_ZLIB */ #include "buffer.h" #include "dref.h" #include "dump.h" #include "global.h" #include "idcache.h" #include "misc.h" #include "tar.h" /* Default input file read size (bytes) */ #define DEFAULT_READ_CHUNK_SIZE (512 * 1024) #define DEFAULT_MAX_BUFFER_SIZE (2 * 1024 * 1024) #define _SET_ABORTED(task) _set_aborted((task), __func__, __LINE__) #define SET_ABORTED(task) set_aborted((task), __func__, __LINE__) #define read_error(task, filename, fmt, ...) \ do { \ if (!(task)->opts->ignore_failed_read) \ SET_ABORTED((task)); \ _mwarn(true, "%s: " fmt, (filename), ##__VA_ARGS__); \ } while (0) #define write_error(task, fmt, ...) \ do { \ SET_ABORTED((task)); \ _mwarn(true, "%s: " fmt, (task)->opts->output_file, \ ##__VA_ARGS__); \ } while (0) #define tverb(fmt, ...) \ do { \ if (task->opts->verbose) \ verb((fmt), ##__VA_ARGS__); \ } while (0) /* Jobs representing a file or command output to add */ struct job { struct job *next_job; enum job_type { JOB_INIT, /* Initialization work */ JOB_FILE, /* Add a regular file */ JOB_LINK, /* Add a symbolic link */ JOB_DIR, /* Add a directory */ JOB_CMD, /* Add command output */ } type; enum job_status { JOB_QUEUED, /* Transient: Job processing has not started */ JOB_IN_PROGRESS,/* Transient: Job processing has started */ JOB_EXCLUDED, /* Final: File was excluded */ JOB_FAILED, /* Final: Data could not be obtained */ JOB_DONE, /* Final: All data was obtained */ JOB_PARTIAL, /* Final: Only some data was obtained */ } status; char *outname; char *inname; char *relname; struct stat stat; bool timed; struct timespec deadline; struct dref *dref; int cmd_status; struct buffer *content; }; /* Run-time statistics */ struct stats { unsigned long num_done; unsigned long num_excluded; unsigned long num_failed; unsigned long num_partial; }; /* Information specific to a single dump task */ struct task { /* Input */ struct dump_opts *opts; /* State */ /* mutex serializes access to global data */ pthread_mutex_t mutex; pthread_cond_t worker_cond; pthread_cond_t cond; unsigned long num_jobs_active; struct job *jobs_head; struct job *jobs_tail; bool aborted; /* output_mutex serializes access to output file */ pthread_mutex_t output_mutex; int output_fd; size_t output_written; #ifdef HAVE_ZLIB gzFile output_gzfd; #endif /* HAVE_ZLIB */ unsigned long output_num_files; /* No protection needed (only accessed in single-threaded mode) */ struct stats stats; struct timespec start_ts; }; /* Per thread management data */ struct per_thread { long num; pthread_t thread; bool running; bool timed_out; struct stats stats; struct job *job; struct buffer buffer; struct task *task; }; static const struct { mode_t mode; char c; } exclude_types[NUM_EXCLUDE_TYPES] = { { S_IFREG, 'f' }, { S_IFDIR, 'd' }, { S_IFCHR, 'c' }, { S_IFBLK, 'b' }, { S_IFIFO, 'p' }, { S_IFLNK, 'l' }, { S_IFSOCK, 's' }, }; /* Lock main mutex */ static void main_lock(struct task *task) { if (!global_threaded) return; DBG("main lock"); pthread_mutex_lock(&task->mutex); } /* Unlock main mutex */ static void main_unlock(struct task *task) { if (!global_threaded) return; DBG("main unlock"); pthread_mutex_unlock(&task->mutex); } /* Lock output mutex */ static void output_lock(struct task *task) { if (!global_threaded) return; pthread_mutex_lock(&task->output_mutex); } /* Unlock output mutex */ static void output_unlock(struct task *task) { if (!global_threaded) return; pthread_mutex_unlock(&task->output_mutex); } /* Wake up all waiting workers */ static void _worker_wakeup_all(struct task *task) { if (!global_threaded) return; DBG("waking up all worker threads"); pthread_cond_broadcast(&task->worker_cond); } /* Wake up one waiting worker */ static void _worker_wakeup_one(struct task *task) { if (!global_threaded) return; DBG("waking up one worker thread"); pthread_cond_signal(&task->worker_cond); } /* Wait for a signal to a worker */ static int _worker_wait(struct task *task) { int rc; DBG("waiting for signal to worker"); rc = pthread_cond_wait(&task->worker_cond, &task->mutex); DBG("waiting for signal to worker done (rc=%d)", rc); return rc; } /* Wake up main thread */ static void _main_wakeup(struct task *task) { if (!global_threaded) return; DBG("waking up main thread"); pthread_cond_broadcast(&task->cond); } /* Wait for a signal to the main thread */ static int _main_wait(struct task *task) { int rc; DBG("waiting for status change"); rc = pthread_cond_wait(&task->cond, &task->mutex); DBG("waiting for status change done (rc=%d)", rc); return rc; } /* Wait for a signal to the main thread. Abort waiting after @deadline */ static int _main_wait_timed(struct task *task, struct timespec *deadline) { int rc; DBG("timed waiting for status change"); rc = pthread_cond_timedwait(&task->cond, &task->mutex, deadline); DBG("timed waiting for status change done (rc=%d)", rc); return rc; } /* Allow thread to be canceled */ static void cancel_enable(void) { if (!global_threaded) return; if (pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0) mwarn("pthread_setcancelstate"); } /* Prevent thread from being canceled */ static void cancel_disable(void) { if (!global_threaded) return; if (pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL) != 0) mwarn("pthread_setcancelstate"); } /* Abort processing and inform all threads to shutdown. Must be called with * task->mutex locked */ static void _set_aborted(struct task *task, const char *func, unsigned int line) { DBG("set aborted at %s:%u", func, line); task->aborted = true; _worker_wakeup_all(task); _main_wakeup(task); } /* Abort processing and inform all threads to shutdown */ static void set_aborted(struct task *task, const char *func, unsigned int line) { main_lock(task); _set_aborted(task, func, line); main_unlock(task); } /* Check if abort processing has been initiated */ static bool is_aborted(struct task *task) { bool result; main_lock(task); result = task->aborted; main_unlock(task); return result; } /* Release resources associated with @job */ static void free_job(struct task *task, struct job *job) { DBG("free job %p (%s)", job, job->inname); if (!job) return; free(job->inname); free(job->outname); free(job->relname); dref_put(job->dref); free(job); } /* Check if file type specified by mode @m was marked as excluded */ static bool is_type_excluded(struct dump_opts *opts, mode_t m) { int i; m &= S_IFMT; for (i = 0; i < NUM_EXCLUDE_TYPES; i++) { if (exclude_types[i].mode == m) return opts->exclude_type[i]; } return false; } /* Replace all '/' characters in @filename with '_' */ static void escape_filename(char *filename) { for (; *filename; filename++) { if (*filename == '/') *filename = '_'; } } /* Determine filename in archive from original filename @inname and * requested new filename @outname and depending on @type. */ static void set_outname(char **result_ptr, const char *outname, const char *inname, enum job_type type) { const char *prefix = "", *name, *suffix; char *result, *end; size_t olen = outname ? strlen(outname) : 0, plen, nlen; if (olen == 0) { /* No output name specified: outname = inname */ name = inname; } else if (outname[olen - 1] == '/') { /* Output name is a directory: outname = outname/inname */ prefix = outname; name = inname; } else { /* Output name is a filename: outname = inname */ name = outname; } if (type == JOB_DIR) suffix = "/"; else suffix = ""; plen = strlen(prefix); nlen = strlen(name); result = mmalloc(plen + nlen + strlen(suffix) + /* NUL */ 1); /* Add prefix */ strcpy(result, prefix); /* Add name */ end = result + plen; strcpy(end, name); if (type == JOB_CMD) escape_filename(end); /* Add suffix */ end = end + nlen; strcpy(end, suffix); remove_double_slashes(result); *result_ptr = result; } static void sanitize_dirname(char **name_ptr) { char *name; name = mmalloc(strlen(*name_ptr) + /* Slash */ 1 + /* NUL */ 1); strcpy(name, *name_ptr); remove_double_slashes(name); chomp(name, "/"); strcat(name, "/"); free(*name_ptr); *name_ptr = name; } /* Allocate and initialize a new job representation to add an entry according * to the specified parameters. @relname and @dref are used for opening files * more efficiently using *at() functions if specified. @is_cmd specifies if * the specified inname is a command line. */ static struct job *create_job(struct task *task, const char *inname, const char *outname, bool is_cmd, const char *relname, struct dref *dref, struct stats *stats) { struct job *job = mmalloc(sizeof(struct job)); int rc; DBG("create job inname=%s outname=%s is_cmd=%d relname=%s dref=%p", inname, outname, is_cmd, relname, dref); job->status = JOB_QUEUED; if (!inname) { job->type = JOB_INIT; return job; } job->inname = mstrdup(inname); if (is_cmd) { /* Special case - read from command output */ job->type = JOB_CMD; set_dummy_stat(&job->stat); goto out; } if (!relname && strcmp(job->inname, "-") == 0) { /* Special case - read from standard input */ job->type = JOB_FILE; set_dummy_stat(&job->stat); goto out; } rc = stat_file(task->opts->dereference, job->inname, relname, dref, &job->stat); if (rc < 0) { read_error(task, job->inname, "Cannot stat file"); free_job(task, job); stats->num_failed++; return NULL; } if (is_type_excluded(task->opts, job->stat.st_mode)) { free_job(task, job); stats->num_excluded++; return NULL; } if (S_ISLNK(job->stat.st_mode)) { job->type = JOB_LINK; } else if (S_ISDIR(job->stat.st_mode)) { job->type = JOB_DIR; sanitize_dirname(&job->inname); /* No need to keep parent directory open */ relname = NULL; dref = NULL; } else { job->type = JOB_FILE; } if (relname) job->relname = mstrdup(relname); job->dref = dref_get(dref); out: set_outname(&job->outname, outname, inname, job->type); return job; } void job_print(struct job *job) { printf("DEBUG: job_print at %p\n", job); printf("DEBUG: next_job=%p\n", job->next_job); printf("DEBUG: type=%d\n", job->type); printf("DEBUG: status==%d\n", job->status); printf("DEBUG: outname=%s\n", job->outname); printf("DEBUG: inname=%s\n", job->inname); printf("DEBUG: relname=%s\n", job->relname); printf("DEBUG: timed=%d\n", job->timed); printf("DEBUG: dref=%p\n", job->dref); printf("DEBUG: cmd_status=%d\n", job->cmd_status); printf("DEBUG: content=%p\n", job->content); } /* Return the number of bytes written to the output file */ static size_t get_output_size(struct task *task) { #ifdef HAVE_ZLIB if (task->opts->gzip) { gzflush(task->output_gzfd, Z_SYNC_FLUSH); return gztell(task->output_gzfd); } #endif /* HAVE_ZLIB */ return task->output_written; } /* Write @len bytes at address @ptr to the output file */ static int write_output(struct task *task, const char *ptr, size_t len) { size_t todo = len; ssize_t w; #ifdef HAVE_ZLIB if (task->opts->gzip) { if (gzwrite(task->output_gzfd, ptr, len) == 0) goto err_write; task->output_written += len; return EXIT_OK; } #endif /* HAVE_ZLIB */ while (todo > 0) { w = write(task->output_fd, ptr, todo); if (w < 0) goto err_write; todo -= w; ptr += w; } task->output_written += len; return EXIT_OK; err_write: write_error(task, "Cannot write output"); return EXIT_RUNTIME; } /* Write an end-of-file marker to the output file */ static void write_eof(struct task *task) { char zeroes[TAR_BLOCKSIZE]; memset(zeroes, 0, sizeof(zeroes)); write_output(task, zeroes, TAR_BLOCKSIZE); write_output(task, zeroes, TAR_BLOCKSIZE); } /* Callback for writing out chunks of job data */ static int _write_job_data_cb(void *data, void *addr, size_t len) { struct task *task = data; return write_output(task, addr, len); } /* Write tar entry for a file containing the exit status of the process that * ran command job @job */ static int write_job_status_file(struct task *task, struct job *job) { char *name, *content; size_t len; struct stat st; int rc, status = job->cmd_status, exitstatus = -1, termsig = -1, waitpid_errno = -1; name = masprintf("%s.cmdstatus", job->outname); if (status < 0) waitpid_errno = -status; else if (WIFEXITED(status)) exitstatus = WEXITSTATUS(status); else if (WIFSIGNALED(status)) termsig = WTERMSIG(status); content = masprintf("EXITSTATUS=%d\n" "TERMSIG=%d\n" "WAITPID_ERRNO=%d\n", exitstatus, termsig, waitpid_errno); len = strlen(content); set_dummy_stat(&st); rc = tar_emit_file_from_data(name, NULL, len, &st, TYPE_REGULAR, content, _write_job_data_cb, task); free(name); free(content); return rc; } /* Write tar entry for data in @job to output. Must be called with output_lock * held. */ static void _write_job_data(struct task *task, struct job *job) { struct buffer *buffer = job->content; switch (job->status) { case JOB_DONE: case JOB_PARTIAL: break; case JOB_FAILED: /* Create empty entries for failed reads */ if (task->opts->ignore_failed_read) break; return; default: return; } switch (job->type) { case JOB_CMD: tar_emit_file_from_buffer(job->outname, NULL, buffer->total, &job->stat, TYPE_REGULAR, buffer, _write_job_data_cb, task); task->output_num_files++; if (task->opts->add_cmd_status) { write_job_status_file(task, job); task->output_num_files++; } break; case JOB_FILE: tar_emit_file_from_buffer(job->outname, NULL, buffer->total, &job->stat, TYPE_REGULAR, buffer, _write_job_data_cb, task); task->output_num_files++; break; case JOB_LINK: tar_emit_file_from_buffer(job->outname, buffer->addr, 0, &job->stat, TYPE_LINK, NULL, _write_job_data_cb, task); task->output_num_files++; break; case JOB_DIR: tar_emit_file_from_buffer(job->outname, NULL, 0, &job->stat, TYPE_DIR, NULL, _write_job_data_cb, task); task->output_num_files++; break; default: break; } if (task->opts->max_size > 0 && get_output_size(task) > task->opts->max_size) { mwarnx("Archive size exceeds maximum of %ld bytes - aborting", task->opts->max_size); SET_ABORTED(task); } } /* Read the contents of the symbolic link at @filename. On success, the * contents is returned in @buffer and the return value is %EXIT_OK. * If @relname is non-null it points to the name of the file relative * to its parent directory for which @dirfd is an open file handle. */ static int read_symlink(struct task *task, const char *filename, const char *relname, int dirfd, struct buffer *buffer) { ssize_t actual = 0; size_t currlen = buffer->size ? buffer->size : task->opts->read_chunk_size; int rc = EXIT_OK; while (!is_aborted(task)) { buffer_make_room(buffer, currlen, false, task->opts->max_buffer_size); cancel_enable(); if (relname) actual = readlinkat(dirfd, relname, buffer->addr, buffer->size); else actual = readlink(filename, buffer->addr, buffer->size); cancel_disable(); if (actual == -1) { read_error(task, filename, "Cannot read link"); rc = EXIT_RUNTIME; /* Reset actual counter to get an empty buffer */ actual = 0; break; } /* Ensure that content doesn't exceed --file-max-size limit */ if (task->opts->file_max_size > 0 && (size_t) actual > task->opts->file_max_size) { actual = task->opts->file_max_size;/* Don't count NUL */ mwarnx("%s: Warning: Data exceeds maximum size of %ld " "bytes - truncating", filename, task->opts->file_max_size); break; } if ((size_t) actual < buffer->size) break; currlen += task->opts->read_chunk_size; } if (rc == EXIT_OK && is_aborted(task)) rc = EXIT_RUNTIME; buffer->addr[actual] = 0; buffer->total = actual + 1; return rc; } /* Read data from the file descriptor @fd until an end-of-file condition is * encountered. On success, *@done bytes in @buffer contain the read data * and the return value is %EXIT_OK. */ static int read_fd(struct task *task, const char *name, int fd, struct buffer *buffer) { ssize_t rc = 0; size_t c = buffer->size ? buffer->size : task->opts->read_chunk_size; while (!is_aborted(task)) { cancel_enable(); rc = buffer_read_fd(buffer, fd, c, true, task->opts->max_buffer_size); cancel_disable(); if (rc <= 0) break; /* Ensure that content doesn't exceed --file-max-size limit */ if (task->opts->file_max_size > 0 && buffer->total >= task->opts->file_max_size) { buffer_truncate(buffer, task->opts->file_max_size); rc = 0; mwarnx("%s: Warning: Data exceeds maximum size of %ld " "bytes - truncating", name, task->opts->file_max_size); break; } c = buffer->size - buffer->off; if (c > 0) { /* Read to memory */ } else if (buffer->size + task->opts->read_chunk_size < task->opts->max_buffer_size) { /* Enlarge memory buffer */ c = task->opts->read_chunk_size; } else { /* Use full memory buffer size */ c = task->opts->max_buffer_size; } } if (is_aborted(task) || rc != 0) return EXIT_RUNTIME; return EXIT_OK; } /* Read data from the file at @filename until an end-of-file condition is * encountered. On success, @buffer contains the data read and the return * value is %EXIT_OK. If @relname is non-null it points to the name of the * file relative to its parent directory for which @dirfd is an open file * handle. */ static int read_regular(struct task *task, const char *filename, const char *relname, int dirfd, struct buffer *buffer) { int fd, rc = EXIT_OK; bool need_close = true; /* Opening a named pipe can block when peer is not ready */ cancel_enable(); if (strcmp(filename, "-") == 0) { fd = STDIN_FILENO; need_close = false; filename = "Standard input"; } else if (relname) fd = openat(dirfd, relname, O_RDONLY); else fd = open(filename, O_RDONLY); cancel_disable(); if (fd < 0) { read_error(task, filename, "Cannot open file"); return EXIT_RUNTIME; } rc = read_fd(task, filename, fd, buffer); if (rc) { if (is_aborted(task)) mwarnx("%s: Read aborted", filename); else read_error(task, filename, "Cannot read file"); } if (need_close) close(fd); return rc; } /* Read the output of command @cmd until an end-of-file condition is * encountered. On success, @buffer contain the output and the return value * is %EXIT_OK. When not %NULL, use @status_ptr to store the resulting process * status. */ static int read_cmd_output(struct task *task, char *cmd, struct buffer *buffer, int *status_ptr) { int fd, rc = EXIT_RUNTIME; pid_t pid; fd = cmd_open(cmd, &pid); if (fd < 0) { read_error(task, cmd, "Cannot run command"); return rc; } if (read_fd(task, cmd, fd, buffer)) { if (is_aborted(task)) mwarnx("%s: Command aborted", cmd); else read_error(task, cmd, "Cannot read command output"); } else rc = EXIT_OK; cmd_close(fd, pid, status_ptr); return rc; } /* Check the exclude patterns in @task->opts->exclude for a match of @filename. * If found, return the matching pattern string, otherwise return %NULL. */ static const char *get_exclude_match(struct task *task, const char *filename) { unsigned int i; int mode = FNM_PERIOD | FNM_NOESCAPE; for (i = 0; i < task->opts->exclude.num; i++) { if (fnmatch(task->opts->exclude.str[i], filename, mode) == 0) return task->opts->exclude.str[i]; } return NULL; } /* Add the specified @job to the start of the job queue */ static void _queue_job_head(struct task *task, struct job *job) { DBG("queue job type=%d inname=%s at head", job->type, job->inname); job->next_job = task->jobs_head; task->jobs_head = job; if (!task->jobs_tail) task->jobs_tail = job; } /* Add the specified @job to the end of the job queue */ static void _queue_job_tail(struct task *task, struct job *job) { DBG("queue job type=%d inname=%s at tail", job->type, job->inname); if (task->jobs_tail) task->jobs_tail->next_job = job; else task->jobs_head = job; task->jobs_tail = job; } /* Add the specified @job to the job queue and trigger processing. * If @head is %true, the new job is inserted at the start of the job queue, * otherwise at the end. */ static void queue_job(struct task *task, struct job *job, bool head) { main_lock(task); task->num_jobs_active++; if (head) _queue_job_head(task, job); else _queue_job_tail(task, job); _worker_wakeup_one(task); main_unlock(task); } /* Add the specified list of jobs starting with @first up to @last to the start * of the job queue and trigger processing */ static void queue_jobs(struct task *task, struct job *first, struct job *last, int num) { main_lock(task); last->next_job = task->jobs_head; task->jobs_head = first; task->num_jobs_active += num; _worker_wakeup_all(task); main_unlock(task); } /* Remove the head of the job queue and return it to the caller */ static struct job *_dequeue_job(struct task *task) { struct job *job = NULL; if (task->jobs_head) { job = task->jobs_head; task->jobs_head = job->next_job; job->next_job = NULL; if (job == task->jobs_tail) task->jobs_tail = NULL; DBG("dequeueing job type=%d inname=%s", job->type, job->inname); job->status = JOB_IN_PROGRESS; } else { DBG("no job to dequeue"); } return job; } /* Create and queue job for file at @filename */ static void queue_file(struct task *task, const char *inname, const char *outname, bool is_cmd, const char *relname, struct dref *dref, struct stats *stats, bool head) { struct job *job; job = create_job(task, inname, outname, is_cmd, relname, dref, stats); if (job) queue_job(task, job, head); } /* Queue initial job */ static void init_queue(struct task *task) { queue_file(task, NULL, NULL, false, NULL, NULL, NULL, true); } /* Create and queue jobs for all files found in @dirname */ static void queue_dir(struct task *task, const char *dirname, const char *outname, struct stats *stats) { struct dirent *de; char *inpath, *outpath; struct dref *dref; struct job *job, *first = NULL, *last = NULL; int num = 0; dref = dref_create(dirname); if (!dref) { read_error(task, dirname, "Cannot read directory"); return; } while ((de = readdir(dref->dd))) { if (de->d_name[0] == '.') { if (de->d_name[1] == 0) continue; if (de->d_name[1] == '.' && de->d_name[2] == 0) continue; } DBG("next file %s", de->d_name); inpath = masprintf("%s%s", dirname, de->d_name); outpath = masprintf("%s%s", outname, de->d_name); job = create_job(task, inpath, outpath, false, de->d_name, dref, stats); if (job) { if (last) { last->next_job = job; last = job; } else { first = job; last = job; } num++; } free(inpath); free(outpath); } if (first) queue_jobs(task, first, last, num); dref_put(dref); } /* Create and queue jobs for all files specified on the command line */ static void queue_jobs_from_opts(struct task *task, struct stats *stats) { struct dump_opts *opts = task->opts; unsigned int i; /* Queue directly specified entries */ for (i = 0; i < opts->num_specs && !is_aborted(task); i++) { queue_file(task, opts->specs[i].inname, opts->specs[i].outname, opts->specs[i].is_cmd, NULL, NULL, stats, false); } } /* Prepare output stream */ static int open_output(struct task *task) { bool to_stdout = !task->opts->output_file || strcmp(task->opts->output_file, "-") == 0; int rc = EXIT_OK; struct stat st; if (to_stdout) { set_stdout_data(); task->opts->output_file = "Standard output"; } cancel_enable(); #ifdef HAVE_ZLIB if (task->opts->gzip) { if (to_stdout) { task->output_gzfd = gzdopen(STDOUT_FILENO, task->opts->append ? "ab" : "wb"); } else { task->output_gzfd = gzopen(task->opts->output_file, task->opts->append ? "ab" : "wb"); } if (!task->output_gzfd) rc = EXIT_RUNTIME; goto out; } #endif /* HAVE_ZLIB */ if (to_stdout) { task->output_fd = STDOUT_FILENO; } else { task->output_fd = open(task->opts->output_file, O_WRONLY | O_CREAT | (task->opts->append ? O_APPEND : 0), 0666); } if (task->output_fd < 0) rc = EXIT_RUNTIME; else if (!task->opts->append) { if (fstat(task->output_fd, &st) == -1) rc = EXIT_RUNTIME; else if (S_ISREG(st.st_mode) && ftruncate(task->output_fd, 0) == -1) rc = EXIT_RUNTIME; } #ifdef HAVE_ZLIB out: #endif /* HAVE_ZLIB */ cancel_disable(); if (rc != EXIT_OK) { mwarn("%s: Cannot open output file", task->opts->output_file); return rc; } return EXIT_OK; } /* Determine if the specified @job should be excluded from archiving */ static bool is_job_excluded(struct task *task, struct job *job) { const char *pat; if (job->type == JOB_INIT || job->type == JOB_CMD) return false; pat = get_exclude_match(task, job->inname); if (!pat) return false; tverb("Excluding '%s' due to exclude pattern '%s'\n", job->inname, pat); return true; } /* Perform all actions necessary to process @job and add resulting tar * data buffers to the buffer list of @thread. */ static void process_job(struct per_thread *thread, struct job *job) { struct task *task = thread->task; const char *relname = job->dref ? job->relname : NULL; int dirfd = job->dref ? job->dref->dirfd : -1; struct buffer *buffer = &thread->buffer; enum job_status status = JOB_DONE; DBG("processing job type=%d inname=%s", job->type, job->inname); if (is_job_excluded(task, job)) { status = JOB_EXCLUDED; goto out; } switch (job->type) { case JOB_INIT: /* Perform initial setup steps */ if (open_output(task)) { SET_ABORTED(task); status = JOB_FAILED; goto out; } queue_jobs_from_opts(task, &thread->stats); break; case JOB_CMD: /* Capture command output */ tverb("Dumping command output '%s'\n", job->inname); set_dummy_stat(&job->stat); if (read_cmd_output(task, job->inname, buffer, &job->cmd_status)) status = JOB_FAILED; break; case JOB_LINK: /* Read symbolic link */ tverb("Dumping link '%s'\n", job->inname); if (read_symlink(task, job->inname, relname, dirfd, buffer)) status = JOB_FAILED; break; case JOB_DIR: /* Read directory contents */ tverb("Dumping directory '%s'\n", job->inname); if (task->opts->recursive) { queue_dir(task, job->inname, job->outname, &thread->stats); } break; case JOB_FILE: /* Read file contents */ tverb("Dumping file '%s'\n", job->inname); if (read_regular(task, job->inname, relname, dirfd, buffer)) status = JOB_FAILED; break; default: break; } out: job->status = status; DBG("processing done status=%d", job->status); } /* Add @job results to statistics @stats */ static void account_stats(struct task *task, struct stats *stats, struct job *job) { DBG("accounting job %s", job->inname); if (job->type == JOB_INIT) return; switch (job->status) { case JOB_DONE: stats->num_done++; if (job->type == JOB_CMD && task->opts->add_cmd_status) stats->num_done++; break; case JOB_PARTIAL: stats->num_done++; stats->num_partial++; if (job->type == JOB_CMD && task->opts->add_cmd_status) stats->num_done++; break; case JOB_FAILED: stats->num_failed++; break; case JOB_EXCLUDED: stats->num_excluded++; break; default: break; } } /* Add statistics @from to @to */ static void add_stats(struct stats *to, struct stats *from) { to->num_done += from->num_done; to->num_partial += from->num_partial; to->num_excluded += from->num_excluded; to->num_failed += from->num_failed; } /* Release resources allocated to @thread */ static void cleanup_thread(struct per_thread *thread) { if (thread->job) free_job(thread->task, thread->job); buffer_free(&thread->buffer, false); } /* Register activate @job at @thread */ static void start_thread_job(struct per_thread *thread, struct job *job) { struct task *task = thread->task; thread->job = job; job->content = &thread->buffer; if (task->opts->file_timeout > 0 && job->type != JOB_INIT) { /* Set up per-job timeout */ set_timespec(&job->deadline, task->opts->file_timeout, 0); job->timed = true; /* Signal main thread to update deadline timeout */ _main_wakeup(task); } } /* Unregister active @job at @thread */ static void stop_thread_job(struct per_thread *thread, struct job *job) { thread->job = NULL; job->content = NULL; buffer_reset(&thread->buffer); } /* Wait until a job is available in the job queue. When a job becomes * available, dequeue and return it. Return %NULL if no more jobs are * available, or if processing was aborted. Must be called with task->mutex * locked. */ static struct job *_get_next_job(struct task *task) { struct job *job = NULL; do { DBG("checking for jobs"); if (task->aborted) break; job = _dequeue_job(task); if (job) break; if (task->num_jobs_active == 0) break; DBG("found no jobs (%d active)", task->num_jobs_active); } while (_worker_wait(task) == 0); return job; } /* Unlock the mutex specified by @data */ static void cleanup_unlock(void *data) { pthread_mutex_t *mutex = data; pthread_mutex_unlock(mutex); } /* Write entry for data in @job to output */ static void write_job_data(struct task *task, struct job *job) { DBG("write_job_data"); output_lock(task); pthread_cleanup_push(cleanup_unlock, &task->output_mutex); cancel_enable(); _write_job_data(task, job); cancel_disable(); pthread_cleanup_pop(0); output_unlock(task); } /* Perform second part of job processing for @job at @thread by writing the * resulting tar file entry */ static void postprocess_job(struct per_thread *thread, struct job *job, bool cancelable) { struct task *task = thread->task; account_stats(task, &thread->stats, job); if (cancelable) write_job_data(task, job); else _write_job_data(task, job); } /* Mark @job as complete by releasing all associated resources. If this was * the last active job inform main thread. Must be called with main_lock * mutex held. */ static void _complete_job(struct task *task, struct job *job) { task->num_jobs_active--; if (task->num_jobs_active == 0) _main_wakeup(task); free_job(task, job); } static void init_thread(struct per_thread *thread, struct task *task, long num) { memset(thread, 0, sizeof(struct per_thread)); thread->task = task; thread->num = num; } /* Dequeue and process all jobs on the job queue */ static int process_queue(struct task *task) { struct job *job; struct per_thread thread; init_thread(&thread, task, 0); while ((job = _dequeue_job(task)) && !is_aborted(task)) { start_thread_job(&thread, job); process_job(&thread, job); postprocess_job(&thread, job, false); stop_thread_job(&thread, job); _complete_job(task, job); } task->stats = thread.stats; cleanup_thread(&thread); return EXIT_OK; } /* Return %true if @job is in a final state, %false otherwise */ static bool job_is_final(struct job *job) { switch (job->status) { case JOB_DONE: case JOB_PARTIAL: case JOB_EXCLUDED: case JOB_FAILED: return true; default: break; } return false; } /* Main thread function: process jobs on the job queue until all jobs * are processed or processing was aborted. */ static void *worker_thread_main(void *d) { struct per_thread *thread = d; struct task *task = thread->task; struct job *job; /* Allow cancel only at specific code points */ cancel_disable(); set_threadname("%*sworker %d", (thread->num + 1) * 2, "", thread->num); /* Handle jobs left over from canceled thread */ job = thread->job; if (job) { DBG("handle aborted job %p", job); postprocess_job(thread, job, true); main_lock(task); if (thread->timed_out) goto out; stop_thread_job(thread, job); _complete_job(task, job); main_unlock(task); } DBG("enter worker loop"); main_lock(task); while ((job = _get_next_job(task))) { start_thread_job(thread, job); main_unlock(task); process_job(thread, job); postprocess_job(thread, job, true); main_lock(task); if (thread->timed_out) goto out; stop_thread_job(thread, job); _complete_job(task, job); } out: thread->running = false; _main_wakeup(task); main_unlock(task); cancel_enable(); DBG("leave work loop"); return NULL; } /* Start a worker thread associated with the specified @data. Return %EXIT_OK on * success. */ static int start_worker_thread(struct per_thread *data) { int rc; DBG("start thread"); global_threaded = true; data->timed_out = false; rc = pthread_create(&data->thread, NULL, &worker_thread_main, data); if (rc) { mwarnx("Cannot start thread: %s", strerror(rc)); return EXIT_RUNTIME; } data->running = true; return EXIT_OK; } /* Perform timeout handling for thread associated with @data by canceling and * restarting the corresponding thread. Must be called with task->mutex * held. */ static void _timeout_thread(struct per_thread *data) { struct task *task = data->task; struct job *job = data->job; pthread_t thread = data->thread; const char *op, *action; if (!job) { /* Timeout raced with job completion */ return; } if (job_is_final(job)) { /* Job processing done, timeout does not apply */ return; } data->timed_out = true; /* Allow thread to obtain main lock during cancel handling */ main_unlock(task); DBG("cancel num=%d thread=%p", data->num, thread); pthread_cancel(thread); DBG("join num=%d thread=%p", data->num, thread); pthread_join(thread, NULL); main_lock(task); DBG("join done"); if (job->type == JOB_CMD) op = "Command"; else op = "Read"; if (task->opts->ignore_failed_read) action = "skipping"; else action = "aborting"; if (!job->inname || !*job->inname) job_print(job); mwarnx("%s: %s%s timed out after %d second%s - %s", job->inname, task->opts->ignore_failed_read ? "Warning: " : "", op, task->opts->file_timeout, task->opts->file_timeout > 1 ? "s" : "", action); if (!task->opts->ignore_failed_read) _SET_ABORTED(task); /* Interrupted job will be handled by new thread - adjust status */ if (job->status == JOB_IN_PROGRESS) job->status = JOB_PARTIAL; else if (!job_is_final(job)) job->status = JOB_FAILED; if (start_worker_thread(data)) _SET_ABORTED(task); } /* Return the number of currently running jobs */ static long num_jobs_running(struct task *task, struct per_thread *threads) { long i, num = 0; for (i = 0; i < task->opts->jobs; i++) { if (threads[i].running) num++; } return num; } /* Wait until all jobs are done or timeout occurs */ static int wait_for_completion(struct task *task, struct per_thread *threads) { int rc = 0, earliest_timeout; long i; struct per_thread *earliest_thread; struct timespec tool_deadline_ts, deadline_ts, *earliest_ts; struct job *job; /* Set tool deadline */ tool_deadline_ts = task->start_ts; inc_timespec(&tool_deadline_ts, task->opts->timeout, 0); main_lock(task); while (!task->aborted && task->num_jobs_active > 0) { /* Calculate nearest timeout */ earliest_timeout = 0; earliest_ts = NULL; earliest_thread = NULL; if (task->opts->timeout > 0) { earliest_timeout = task->opts->timeout; earliest_ts = &tool_deadline_ts; } for (i = 0; i < task->opts->jobs; i++) { job = threads[i].job; if (!job || !job->timed) continue; if (task->opts->file_timeout == 0) continue; if (!earliest_ts || ts_before(&job->deadline, earliest_ts)) { earliest_timeout = task->opts->file_timeout; earliest_ts = &job->deadline; earliest_thread = &threads[i]; } } /* Wait for status change or timeout */ if (earliest_ts) { deadline_ts = *earliest_ts; rc = _main_wait_timed(task, &deadline_ts); } else { rc = _main_wait(task); } if (rc == 0) continue; if (rc != ETIMEDOUT) { mwarnx("Cannot wait for status change: %s", strerror(rc)); _SET_ABORTED(task); break; } /* Timeout handling */ if (earliest_thread) { /* Per-file timeout, restart */ _timeout_thread(earliest_thread); rc = 0; } else { /* Global timeout, abort */ mwarnx("Operation timed out after %d second%s - " "aborting", earliest_timeout, earliest_timeout > 1 ? "s" : ""); _SET_ABORTED(task); break; } } if (task->aborted) DBG("aborted"); else DBG("all work done"); _worker_wakeup_all(task); /* Allow jobs to finish */ set_timespec(&deadline_ts, 0, NSEC_PER_SEC / 4); while (!task->aborted && num_jobs_running(task, threads) > 0) { DBG("waiting for %lu processes", num_jobs_running(task, threads)); if (_main_wait_timed(task, &deadline_ts)) break; } main_unlock(task); return rc; } /* Finalize output stream */ static void close_output(struct task *task) { #ifdef HAVE_ZLIB if (task->opts->gzip) { gzclose(task->output_gzfd); return; } #endif /* HAVE_ZLIB */ if (task->output_fd != STDOUT_FILENO) close(task->output_fd); } /* Start multi-threaded processing of job queue */ static int process_queue_threaded(struct task *task) { struct per_thread *threads, *thread; int rc; long i; tverb("Using %ld threads\n", task->opts->jobs); threads = mcalloc(sizeof(struct per_thread), task->opts->jobs); rc = 0; for (i = 0; i < task->opts->jobs; i++) { init_thread(&threads[i], task, i); rc = start_worker_thread(&threads[i]); if (rc) break; } if (!rc) wait_for_completion(task, threads); DBG("thread cleanup"); for (i = 0; i < task->opts->jobs; i++) { thread = &threads[i]; if (thread->running) { DBG("cancel %p", thread->thread); pthread_cancel(thread->thread); } DBG("join %p", thread->thread); pthread_join(thread->thread, NULL); add_stats(&task->stats, &thread->stats); cleanup_thread(thread); } free(threads); return rc; } /* Abort any remaining queued jobs and account to @stats */ static void abort_queued_jobs(struct task *task) { struct job *job; while ((job = _dequeue_job(task))) { DBG("aborting job %s", job->inname); task->stats.num_failed++; job->status = JOB_FAILED; _complete_job(task, job); } } /* Print a summary line */ static void print_summary(struct task *task) { char msg[MSG_LEN]; size_t off = 0; int rc; struct stats *stats = &task->stats; struct timespec end_ts; int num_special; unsigned long num_added; if (task->opts->quiet) return; set_timespec(&end_ts, 0, 0); num_special = 0; num_special += stats->num_partial > 0 ? 1 : 0; num_special += stats->num_excluded > 0 ? 1 : 0; num_special += stats->num_failed > 0 ? 1 : 0; num_added = stats->num_done; if (task->opts->ignore_failed_read) num_added += stats->num_partial + stats->num_failed; rc = snprintf(&msg[off], MSG_LEN - off, "Dumped %lu entries ", num_added); HANDLE_RC(rc, MSG_LEN, off, out); if (num_special > 0) { rc = snprintf(&msg[off], MSG_LEN - off, "("); HANDLE_RC(rc, MSG_LEN, off, out); if (stats->num_partial > 0) { rc = snprintf(&msg[off], MSG_LEN - off, "%lu partial", stats->num_partial); HANDLE_RC(rc, MSG_LEN, off, out); if (--num_special > 0) { rc = snprintf(&msg[off], MSG_LEN - off, ", "); HANDLE_RC(rc, MSG_LEN, off, out); } } if (stats->num_excluded > 0) { rc = snprintf(&msg[off], MSG_LEN - off, "%lu excluded", stats->num_excluded); HANDLE_RC(rc, MSG_LEN, off, out); if (--num_special > 0) { rc = snprintf(&msg[off], MSG_LEN - off, ", "); HANDLE_RC(rc, MSG_LEN, off, out); } } if (stats->num_failed > 0) { rc = snprintf(&msg[off], MSG_LEN - off, "%lu failed", stats->num_failed); HANDLE_RC(rc, MSG_LEN, off, out); } rc = snprintf(&msg[off], MSG_LEN - off, ") "); HANDLE_RC(rc, MSG_LEN, off, out); } rc = snprintf(&msg[off], MSG_LEN - off, "in "); HANDLE_RC(rc, MSG_LEN, off, out); snprintf_duration(&msg[off], MSG_LEN - off, &task->start_ts, &end_ts); out: info("%s\n", msg); } static int init_task(struct task *task, struct dump_opts *opts) { pthread_condattr_t attr; memset(task, 0, sizeof(struct task)); set_timespec(&task->start_ts, 0, 0); task->opts = opts; pthread_mutex_init(&task->mutex, NULL); pthread_mutex_init(&task->output_mutex, NULL); pthread_cond_init(&task->worker_cond, NULL); pthread_condattr_init(&attr); if (pthread_condattr_setclock(&attr, CLOCK_MONOTONIC) || pthread_cond_init(&task->cond, &attr)) { mwarn("Could not adjust pthread clock"); return EXIT_RUNTIME; } return EXIT_OK; } struct dump_opts *dump_opts_new(void) { struct dump_opts *opts = mmalloc(sizeof(struct dump_opts)); opts->recursive = true; opts->read_chunk_size = DEFAULT_READ_CHUNK_SIZE; opts->max_buffer_size = DEFAULT_MAX_BUFFER_SIZE; return opts; } void dump_opts_free(struct dump_opts *opts) { unsigned int i; if (!opts) return; free_strarray(&opts->exclude); for (i = 0; i < opts->num_specs; i++) { free(opts->specs[i].inname); free(opts->specs[i].outname); } free(opts->specs); free(opts); } void dump_opts_print(struct dump_opts *opts) { unsigned int i; printf("DEBUG: dump_opts at %p\n", opts); if (!opts) return; printf("DEBUG: add_cmd_status=%d\n", opts->add_cmd_status); printf("DEBUG: append=%d\n", opts->append); printf("DEBUG: dereference=%d\n", opts->dereference); for (i = 0; i < NUM_EXCLUDE_TYPES; i++) printf("DEBUG: exclude_type[%d]=%d\n", i, opts->exclude_type[i]); printf("DEBUG: gzip=%d\n", opts->gzip); printf("DEBUG: ignore_failed_read=%d\n", opts->ignore_failed_read); printf("DEBUG: no_eof=%d\n", opts->no_eof); printf("DEBUG: quiet=%d\n", opts->quiet); printf("DEBUG: recursive=%d\n", opts->recursive); printf("DEBUG: threaded=%d\n", opts->threaded); printf("DEBUG: verbose=%d\n", opts->verbose); printf("DEBUG: output_file=%s\n", opts->output_file); printf("DEBUG: file_timeout=%d\n", opts->file_timeout); printf("DEBUG: timeout=%d\n", opts->timeout); printf("DEBUG: jobs=%ld\n", opts->jobs); printf("DEBUG: jobs_per_cpu=%ld\n", opts->jobs_per_cpu); printf("DEBUG: file_max_size=%zu\n", opts->file_max_size); printf("DEBUG: max_buffer_size=%zu\n", opts->max_buffer_size); printf("DEBUG: max_size=%zu\n", opts->max_size); printf("DEBUG: read_chunk_size=%zu\n", opts->read_chunk_size); for (i = 0; i < opts->exclude.num; i++) printf("DEBUG: exclude[%d]=%s\n", i, opts->exclude.str[i]); for (i = 0; i < opts->num_specs; i++) { printf("DEBUG: specs[%d]:\n", i); printf("DEBUG: inname=%s\n", opts->specs[i].inname); printf("DEBUG: outname=%s\n", opts->specs[i].outname); printf("DEBUG: is_cmd=%d\n", opts->specs[i].is_cmd); } } /* Mark file type associated with character @c as excluded */ int dump_opts_set_type_excluded(struct dump_opts *opts, char c) { int i; for (i = 0; i < NUM_EXCLUDE_TYPES; i++) { if (exclude_types[i].c == c) { opts->exclude_type[i] = true; return 0; } } return -1; } /* Add entry specification defined by @iname, @outname and @op to @opts. */ void dump_opts_add_spec(struct dump_opts *opts, char *inname, char *outname, bool is_cmd) { unsigned int i = opts->num_specs; opts->specs = mrealloc(opts->specs, (i + 1) * sizeof(struct dump_spec)); opts->specs[i].inname = mstrdup(inname); if (outname) opts->specs[i].outname = mstrdup(outname); else opts->specs[i].outname = NULL; opts->specs[i].is_cmd = is_cmd; opts->num_specs++; } int dump_to_tar(struct dump_opts *opts) { struct task task; int rc; long num_cpus; if (opts->jobs_per_cpu > 0) { num_cpus = sysconf(_SC_NPROCESSORS_ONLN); if (num_cpus < 1) { mwarn("Cannot determine number of CPUs - assuming 1 " "CPU"); num_cpus = 1; } opts->jobs = num_cpus; } if (opts->jobs == 0 && (opts->timeout > 0 || opts->file_timeout > 0)) { /* Separate thread needed to implement timeout via cancel */ opts->jobs = 1; } rc = init_task(&task, opts); if (rc) return rc; /* Queue initial job */ init_queue(&task); /* Process queue */ if (opts->jobs > 0) rc = process_queue_threaded(&task); else rc = process_queue(&task); abort_queued_jobs(&task); if (task.output_num_files > 0 && !opts->no_eof) write_eof(&task); print_summary(&task); close_output(&task); if (rc == 0 && task.aborted) rc = EXIT_RUNTIME; return rc; } s390-tools-2.38.0/dump2tar/src/dump2tar.c000066400000000000000000000262071502674226300177050ustar00rootroot00000000000000/* * dump2tar - tool to dump files and command output into a tar archive * * Command line interface * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include "lib/util_opt.h" #include "lib/util_prg.h" #include "dump.h" #include "global.h" #include "idcache.h" #include "misc.h" #include "strarray.h" #define MIN_BUFFER_SIZE 4096 #define OPT_NOSHORT_BASE 256 #define OPT_DEREFERENCE (OPT_NOSHORT_BASE + 0) #define OPT_NORECURSION (OPT_NOSHORT_BASE + 1) #define OPT_EXCLUDETYPE (OPT_NOSHORT_BASE + 2) /* Program description */ static const struct util_prg dump2tar_prg = { .desc = "Use dump2tar to create a tar archive from the contents " "of arbitrary files.\nIt works even when the size of actual " "file content is not known beforehand,\nsuch as with FIFOs, " "character devices or certain Linux debugfs or sysfs files.\n" "\nYou can also add files under different names and add " "command output using the\nformat described in section SPECS " "below. When no additional options are\nspecified, the " "resulting archive is written to the standard output stream\n" "in uncompressed tar format.", .args = "SPECS", .copyright_vec = { { "IBM Corp.", 2016, 2017 }, UTIL_PRG_COPYRIGHT_END }, }; /* Definition of command line options */ static struct util_opt dump2tar_opts[] = { UTIL_OPT_SECTION("OUTPUT OPTIONS"), { .option = { "output-file", required_argument, NULL, 'o' }, .argument = "FILE", .desc = "Write archive to FILE (default: standard output)", }, #ifdef HAVE_ZLIB { .option = { "gzip", no_argument, NULL, 'z' }, .desc = "Write a gzip compressed archive", }, #endif /* HAVE_ZLIB */ { .option = { "max-size", required_argument, NULL, 'm' }, .argument = "N", .desc = "Stop adding files when archive size exceeds N bytes", }, { .option = { "timeout", required_argument, NULL, 't' }, .argument = "SEC", .desc = "Stop adding files after SEC seconds", }, { .option = { "no-eof", no_argument, NULL, 131 }, .desc = "Do not write an end-of-file marker", .flags = UTIL_OPT_FLAG_NOSHORT, }, { .option = { "add-cmd-status", no_argument, NULL, 132 }, .desc = "Add status of commands as separate file", .flags = UTIL_OPT_FLAG_NOSHORT, }, { .option = { "append", no_argument, NULL, 133 }, .desc = "Append output to end of file", .flags = UTIL_OPT_FLAG_NOSHORT, }, UTIL_OPT_SECTION("INPUT OPTIONS"), { .option = { "files-from", required_argument, NULL, 'F' }, .argument = "FILE", .desc = "Read filenames from FILE (- for standard input)", }, { .option = { "ignore-failed-read", no_argument, NULL, 'i' }, .desc = "Continue after read errors", }, { .option = { "buffer-size", required_argument, NULL, 'b' }, .argument = "N", .desc = "Read data in chunks of N byte (default: 16384)", }, { .option = { "file-timeout", required_argument, NULL, 'T' }, .desc = "Stop reading file after SEC seconds", .argument = "SEC", }, { .option = { "file-max-size", required_argument, NULL, 'M' }, .argument = "N", .desc = "Stop reading file after N bytes", }, { .option = { "jobs", required_argument, NULL, 'j' }, .argument = "N", .desc = "Read N files in parallel (default: 1)", }, { .option = { "jobs-per-cpu", required_argument, NULL, 'J' }, .argument = "N", .desc = "Read N files per CPU in parallel", }, { .option = { "exclude", required_argument, NULL, 'x' }, .argument = "PATTERN", .desc = "Don't add files matching PATTERN", }, { .option = { "exclude-from", required_argument, NULL, 'X' }, .argument = "FILE", .desc = "Don't add files matching patterns in FILE", }, { .option = { "exclude-type", required_argument, NULL, OPT_EXCLUDETYPE }, .argument = "TYPE", .desc = "Don't add files of specified TYPE (one of: fdcbpls)", .flags = UTIL_OPT_FLAG_NOSHORT, }, { .option = { "dereference", no_argument, NULL, OPT_DEREFERENCE }, .desc = "Add link targets instead of links", .flags = UTIL_OPT_FLAG_NOSHORT, }, { .option = { "no-recursion", no_argument, NULL, OPT_NORECURSION }, .desc = "Don't add files from sub-directories", .flags = UTIL_OPT_FLAG_NOSHORT, }, UTIL_OPT_SECTION("MISC OPTIONS"), UTIL_OPT_HELP, UTIL_OPT_VERSION, { .option = { "verbose", no_argument, NULL, 'V' }, .desc = "Print additional informational output", }, { .option = { "quiet", no_argument, NULL, 'q' }, .desc = "Suppress printing of informational output", }, UTIL_OPT_END, }; /* Split buffer size specification in @arg into two numbers to be stored in * @from_ptr and @to_ptr. Return %EXIT_OK on success. */ static int parse_buffer_size(char *arg, size_t *from_ptr, size_t *to_ptr) { char *err; unsigned long from, to; if (!*arg) { mwarnx("Empty buffer size specified"); return EXIT_USAGE; } from = strtoul(arg, &err, 10); if (*err == '-') to = strtoul(err + 1, &err, 10); else to = *to_ptr; if (*err) { mwarnx("Invalid buffer size: %s", arg); return EXIT_USAGE; } if (from < MIN_BUFFER_SIZE || to < MIN_BUFFER_SIZE) { mwarnx("Buffer size too low (minimum %u)", MIN_BUFFER_SIZE); return EXIT_USAGE; } if (to < from) to = from; *from_ptr = from; *to_ptr = to; return EXIT_OK; } static void parse_and_add_spec(struct dump_opts *opts, const char *spec) { char *op, *s, *inname, *outname = NULL; bool is_cmd = false; s = mstrdup(spec); op = strstr(s, "|="); if (op) is_cmd = true; else op = strstr(s, ":="); if (op) { *op = 0; inname = op + 2; outname = s; } else { inname = s; } dump_opts_add_spec(opts, inname, outname, is_cmd); free(s); } static int add_specs_from_file(struct dump_opts *opts, const char *filename) { FILE *fd; char *line = NULL; size_t line_size; int rc = EXIT_RUNTIME; bool need_close = false, parse_spec = true; if (strcmp(filename, "-") == 0) fd = stdin; else { fd = fopen(filename, "r"); if (!fd) { mwarn("%s: Cannot open file", filename); goto out; } need_close = true; } while ((getline(&line, &line_size, fd) != -1)) { chomp(line, "\n"); if (line[0] == 0) continue; if (parse_spec && strcmp(line, "--") == 0) { /* After a line containing --, no more := or |= specs * are expected */ parse_spec = false; continue; } if (parse_spec) parse_and_add_spec(opts, line); else dump_opts_add_spec(opts, line, NULL, false); } if (ferror(fd)) mwarn("%s: Cannot read file", filename); else rc = EXIT_OK; out: if (need_close) fclose(fd); free(line); return rc; } static void print_help(void) { static const struct { const char *name; const char *desc; } specs[] = { { "PATH", "Add file or directory at PATH" }, { "NEWPATH:=PATH", "Add file or directory at PATH as NEWPATH" }, { "NEWPATH|=CMDLINE", "Add output of command line CMDLINE as " "NEWPATH" }, { NULL, NULL }, }; int i; util_prg_print_help(); printf("SPECS\n"); for (i = 0; specs[i].name; i++) util_opt_print_indented(specs[i].name, specs[i].desc); printf("\n"); util_opt_print_help(); } int main(int argc, char *argv[]) { int rc = EXIT_USAGE, opt; long i; struct dump_opts *opts; if (getenv("DUMP2TAR_DEBUG")) global_debug = true; util_prg_init(&dump2tar_prg); util_opt_init(dump2tar_opts, "-"); misc_init(); opts = dump_opts_new(); opterr = 0; while ((opt = util_opt_getopt_long(argc, argv)) != -1) { switch (opt) { case 'h': /* --help */ print_help(); rc = EXIT_OK; goto out; case 'v': /* --version */ util_prg_print_version(); rc = EXIT_OK; goto out; case 'V': /* --verbose */ global_verbose = true; global_quiet = false; opts->verbose = true; opts->quiet = false; break; case 'q': /* --quiet */ global_quiet = true; global_verbose = false; opts->quiet = true; opts->verbose = false; break; case 'i': /* --ignore-failed-read */ opts->ignore_failed_read = true; break; case 'j': /* --jobs N */ opts->jobs = atoi(optarg); if (opts->jobs < 1) { mwarnx("Invalid number of jobs: %s", optarg); goto out; } break; case 'J': /* --jobs-per-cpu N */ opts->jobs_per_cpu = atoi(optarg); if (opts->jobs_per_cpu < 1) { mwarnx("Invalid number of jobs: %s", optarg); goto out; } break; case 'b': /* --buffer-size N */ if (parse_buffer_size(optarg, &opts->read_chunk_size, &opts->max_buffer_size)) goto out; break; case 'x': /* --exclude PATTERN */ add_str_to_strarray(&opts->exclude, optarg); break; case 'X': /* --exclude-from FILE */ if (add_file_to_strarray(&opts->exclude, optarg)) goto out; break; case 'F': /* --files-from FILE */ if (add_specs_from_file(opts, optarg)) goto out; break; case 'o': /* --output-file FILE */ if (opts->output_file) { mwarnx("Output file specified multiple times"); goto out; } opts->output_file = optarg; break; case OPT_DEREFERENCE: /* --dereference */ opts->dereference = true; break; case OPT_NORECURSION: /* --no-recursion */ opts->recursive = false; break; case OPT_EXCLUDETYPE: /* --exclude-type TYPE */ for (i = 0; optarg[i]; i++) { if (dump_opts_set_type_excluded(opts, optarg[i])) break; } if (optarg[i]) { mwarnx("Unrecognized file type: %c", optarg[i]); goto out; } break; case 131: /* --no-eof */ opts->no_eof = true; break; case 132: /* --add-cmd-status */ opts->add_cmd_status = true; break; case 133: /* --append */ opts->append = true; break; case 't': /* --timeout VALUE */ opts->timeout = atoi(optarg); if (opts->timeout < 1) { mwarnx("Invalid timeout value: %s", optarg); goto out; } break; case 'T': /* --file-timeout VALUE */ opts->file_timeout = atoi(optarg); if (opts->file_timeout < 1) { mwarnx("Invalid timeout value: %s", optarg); goto out; } break; case 'm': /* --max-size N */ opts->max_size = atol(optarg); if (opts->max_size < 2) { mwarnx("Invalid maximum size: %s", optarg); goto out; } break; case 'M': /* --file-max-size N */ opts->file_max_size = atol(optarg); if (opts->file_max_size < 2) { mwarnx("Invalid maximum size: %s", optarg); goto out; } break; case 'z': /* --gzip */ opts->gzip = true; break; case 1: /* Filename specification or unrecognized option */ if (optarg[0] == '-') { mwarnx("Invalid option '%s'", optarg); goto out; } parse_and_add_spec(opts, optarg); break; case '?': /* Unrecognized option */ if (optopt) mwarnx("Invalid option '-%c'", optopt); else mwarnx("Invalid option '%s'", argv[optind - 1]); goto out; case ':': /* Missing argument */ mwarnx("Option '%s' requires an argument", argv[optind - 1]); goto out; default: break; } } if (optind >= argc && opts->num_specs == 0) { mwarnx("Please specify files to dump"); goto out; } for (i = optind; i < argc; i++) dump_opts_add_spec(opts, argv[i], NULL, false); rc = dump_to_tar(opts); out: idcache_cleanup(); misc_cleanup(); dump_opts_free(opts); if (rc == EXIT_USAGE) util_prg_print_parse_error(); return rc; } s390-tools-2.38.0/dump2tar/src/global.c000066400000000000000000000006561502674226300174070ustar00rootroot00000000000000/* * dump2tar - tool to dump files and command output into a tar archive * * Global variables * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. * */ #include "global.h" /* Global settings */ bool global_threaded; bool global_debug; bool global_verbose; bool global_quiet; bool global_timestamps; s390-tools-2.38.0/dump2tar/src/idcache.c000066400000000000000000000071471502674226300175310ustar00rootroot00000000000000/* * dump2tar - tool to dump files and command output into a tar archive * * Caches for user and group ID lookups * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include "global.h" #include "idcache.h" #include "misc.h" /* Maximum user and group name lengths as defined in tar header */ #define ID_NAME_MAXLEN 32 /* Types for user and group ID caches */ typedef uid_t generic_id_t; /* Assumes that uid_t == gid_t */ struct id_cache_entry { generic_id_t id; char name[ID_NAME_MAXLEN]; }; struct id_cache { unsigned int num; struct id_cache_entry entries[]; }; /* cache_mutex serializes access to cached uid and gid data */ static pthread_mutex_t id_cache_mutex = PTHREAD_MUTEX_INITIALIZER; static struct id_cache *id_cache_uid; static struct id_cache *id_cache_gid; /* Lock cache mutex */ static void cache_lock(void) { if (!global_threaded) return; pthread_mutex_lock(&id_cache_mutex); } /* Unlock cache mutex */ static void cache_unlock(void) { if (!global_threaded) return; pthread_mutex_unlock(&id_cache_mutex); } /* Copy the name associated with @id in @cache to at most @len bytes at @dest. * Return %true if name was found in cache, %false otherwise. */ static bool strncpy_id_cache_entry(char *dest, struct id_cache *cache, generic_id_t id, size_t len) { unsigned int i; bool hit = false; cache_lock(); if (cache) { for (i = 0; i < cache->num; i++) { if (cache->entries[i].id == id) { strncpy(dest, cache->entries[i].name, len); hit = true; break; } } } cache_unlock(); return hit; } /* Add a new entry consisting of @id and @name to ID cache in @*cache_ptr. * Update @cache_ptr if necessary. */ static void add_id_cache_entry(struct id_cache **cache_ptr, generic_id_t id, char *name) { struct id_cache *cache; unsigned int cache_num; size_t new_size; struct id_cache *new_cache; cache_lock(); cache = *cache_ptr; cache_num = cache ? cache->num : 0; new_size = sizeof(struct id_cache) + sizeof(struct id_cache_entry) * (cache_num + 1); new_cache = mrealloc(cache, new_size); if (cache_num == 0) new_cache->num = 0; new_cache->entries[cache_num].id = id; strncpy(new_cache->entries[cache_num].name, name, ID_NAME_MAXLEN); new_cache->num++; *cache_ptr = new_cache; cache_unlock(); } /* Copy the user name corresponding to user ID @uid to at most @len bytes * at @name */ void uid_to_name(uid_t uid, char *name, size_t len) { struct passwd pwd, *pwd_ptr; char buffer[PWD_BUFFER_SIZE], *result; if (strncpy_id_cache_entry(name, id_cache_uid, uid, len)) return; /* getpwuid() can be slow so cache results */ getpwuid_r(uid, &pwd, buffer, PWD_BUFFER_SIZE, &pwd_ptr); if (!pwd_ptr || !pwd_ptr->pw_name) return; result = pwd_ptr->pw_name; add_id_cache_entry(&id_cache_uid, uid, result); strncpy(name, result, len); } /* Copy the group name corresponding to group ID @gid to at most @len bytes * at @name */ void gid_to_name(gid_t gid, char *name, size_t len) { struct group grp, *grp_ptr; char buffer[GRP_BUFFER_SIZE], *result; if (strncpy_id_cache_entry(name, id_cache_gid, gid, len)) return; /* getgrgid() can be slow so cache results */ getgrgid_r(gid, &grp, buffer, GRP_BUFFER_SIZE, &grp_ptr); if (!grp_ptr || !grp_ptr->gr_name) return; result = grp_ptr->gr_name; add_id_cache_entry(&id_cache_gid, gid, result); strncpy(name, result, len); } void idcache_cleanup(void) { free(id_cache_uid); free(id_cache_gid); } s390-tools-2.38.0/dump2tar/src/misc.c000066400000000000000000000240561502674226300171020ustar00rootroot00000000000000/* * dump2tar - tool to dump files and command output into a tar archive * * Helper functions * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include "dref.h" #include "global.h" #include "misc.h" struct timespec main_start_ts; static pthread_key_t thread_name_key; static bool stdout_data; /* Write @len bytes at @addr to @fd. Return %EXIT_OK on success, %EXIT_RUNTIME * otherwise. */ int misc_write_data(int fd, char *addr, size_t len) { ssize_t w; while (len > 0) { w = write(fd, addr, len); if (w < 0) return EXIT_RUNTIME; len -= w; addr += w; } return EXIT_OK; } /* Read at most @len bytes from @fd to @addr. Return the number of bytes read * or %-1 on error. */ ssize_t misc_read_data(int fd, char *addr, size_t len) { size_t done = 0; ssize_t r; while (len > 0) { r = read(fd, addr, len); if (r < 0) return -1; if (r == 0) break; len -= r; addr += r; done += r; } return done; } /* Advance timespec @ts by @sec seconds and @nsec nanoseconds */ void inc_timespec(struct timespec *ts, time_t sec, long nsec) { ts->tv_nsec += nsec; ts->tv_sec += sec; if (ts->tv_nsec > NSEC_PER_SEC) { ts->tv_nsec -= NSEC_PER_SEC; ts->tv_sec++; } } /* Set timespec @ts to point to @sec seconds and @nsec nanoseconds in the * future */ void set_timespec(struct timespec *ts, time_t sec, long nsec) { clock_gettime(CLOCK_MONOTONIC, ts); inc_timespec(ts, sec, nsec); } /* Return true if timespec @a refers to a point in time before @b */ bool ts_before(struct timespec *a, struct timespec *b) { if (a->tv_sec < b->tv_sec || (a->tv_sec == b->tv_sec && a->tv_nsec < b->tv_nsec)) return true; return false; } /* Store a string representing the time duration between @start and @end in * at most @len bytes of @buff. */ int snprintf_duration(char *buff, size_t len, struct timespec *start, struct timespec *end) { time_t sec; long nsec, msec, s, m, h; sec = end->tv_sec - start->tv_sec; nsec = end->tv_nsec - start->tv_nsec; if (nsec < 0) { nsec += NSEC_PER_SEC; sec--; } msec = nsec / NSEC_PER_MSEC; s = sec % 60; sec /= 60; m = sec % 60; sec /= 60; h = sec; if (h > 0) return snprintf(buff, len, "%luh%lum%lu.%03lus", h, m, s, msec); else if (m > 0) return snprintf(buff, len, "%lum%lu.%03lus", m, s, msec); else return snprintf(buff, len, "%lu.%03lus", s, msec); } /* Return the name of the current thread */ char *get_threadname(void) { return pthread_getspecific(thread_name_key); } static int snprintf_timestamp(char *str, size_t size) { struct timespec now_ts; set_timespec(&now_ts, 0, 0); now_ts.tv_sec -= main_start_ts.tv_sec; now_ts.tv_nsec -= main_start_ts.tv_nsec; if (now_ts.tv_nsec < 0) { now_ts.tv_nsec += NSEC_PER_SEC; now_ts.tv_sec--; } return snprintf(str, size, "[%3lu.%06lu] ", now_ts.tv_sec, now_ts.tv_nsec / NSEC_PER_USEC); } /* When DUMP2TAR_DEBUG is set to non-zero, print debugging information */ void debug(const char *file, unsigned long line, const char *format, ...) { char msg[MSG_LEN]; size_t off = 0; int rc; va_list args; /* Debug marker */ rc = snprintf(&msg[off], MSG_LEN - off, "DEBUG: "); HANDLE_RC(rc, MSG_LEN, off, out); /* Timestamp */ rc = snprintf_timestamp(&msg[off], MSG_LEN - off); HANDLE_RC(rc, MSG_LEN, off, out); /* Thread name */ rc = snprintf(&msg[off], MSG_LEN - off, "%s: ", get_threadname()); HANDLE_RC(rc, MSG_LEN, off, out); /* Message */ va_start(args, format); rc = vsnprintf(&msg[off], MSG_LEN - off, format, args); va_end(args); HANDLE_RC(rc, MSG_LEN, off, out); /* Call site */ rc = snprintf(&msg[off], MSG_LEN - off, " (%s:%lu)", file, line); out: fprintf(stderr, "%s\n", msg); } /* Print a warning message consisting of @format and variable arguments. * If @print_errno is true, also print the text corresponding to errno. * We're not using err.h's warn since we want timestamps and synchronized * output. */ void _mwarn(bool print_errno, const char *format, ...) { char msg[MSG_LEN]; size_t off = 0; int rc; va_list args; if (global_timestamps) { rc = snprintf_timestamp(&msg[off], MSG_LEN - off); HANDLE_RC(rc, MSG_LEN, off, out); } rc = snprintf(&msg[off], MSG_LEN - off, "%s: ", program_invocation_short_name); HANDLE_RC(rc, MSG_LEN, off, out); va_start(args, format); rc = vsnprintf(&msg[off], MSG_LEN - off, format, args); va_end(args); HANDLE_RC(rc, MSG_LEN, off, out); if (print_errno) snprintf(&msg[off], MSG_LEN - off, ": %s", strerror(errno)); out: fprintf(stderr, "%s\n", msg); } /* Provide informational output if --verbose was specified */ void verb(const char *format, ...) { char msg[MSG_LEN]; size_t off = 0; int rc; va_list args; FILE *fd; if (!global_verbose) return; if (stdout_data) fd = stderr; else fd = stdout; if (global_timestamps) { rc = snprintf_timestamp(&msg[off], MSG_LEN - off); HANDLE_RC(rc, MSG_LEN, off, out); } va_start(args, format); rc = vsnprintf(&msg[off], MSG_LEN - off, format, args); va_end(args); out: fprintf(fd, "%s", msg); } /* Provide informational output. */ void info(const char *format, ...) { char msg[MSG_LEN]; size_t off = 0; int rc; va_list args; FILE *fd; if (global_quiet) return; if (stdout_data) fd = stderr; else fd = stdout; if (global_timestamps) { rc = snprintf_timestamp(&msg[off], MSG_LEN - off); HANDLE_RC(rc, MSG_LEN, off, out); } va_start(args, format); rc = vsnprintf(&msg[off], MSG_LEN - off, format, args); va_end(args); out: fprintf(fd, "%s", msg); } /* Return a newly allocated buffer containing the result of the specified * string format arguments */ char *__masprintf(const char *func, const char *file, int line, const char *fmt, ...) { char *str; va_list args; va_start(args, fmt); __util_vasprintf(func, file, line, &str, fmt, args); va_end(args); return str; } /* Set the internal name of the calling thread */ void __set_threadname(const char *func, const char *file, int line, const char *fmt, ...) { char *str; va_list args; va_start(args, fmt); __util_vasprintf(func, file, line, &str, fmt, args); va_end(args); pthread_setspecific(thread_name_key, str); } /* Clear any previously set thread name */ void clear_threadname(void) { void *addr = pthread_getspecific(thread_name_key); if (addr) { pthread_setspecific(thread_name_key, NULL); free(addr); } } /* Remove any number of trailing characters @c in @str */ void chomp(char *str, char *c) { ssize_t i; for (i = strlen(str) - 1; i >= 0 && strchr(c, str[i]); i--) str[i] = 0; } /* Remove any number of leading characters @c in @str */ void lchomp(char *str, char *c) { char *from; for (from = str; *from && strchr(c, *from); from++) ; if (str != from) memmove(str, from, strlen(from) + 1); } /* Perform a stat on file referenced by either @abs or @rel and @dref. Store * results in @stat and return stat()'s return code. */ int stat_file(bool dereference, const char *abs, const char *rel, struct dref *dref, struct stat *st) { int rc; if (dref) { if (dereference) rc = fstatat(dref->dirfd, rel, st, 0); else rc = fstatat(dref->dirfd, rel, st, AT_SYMLINK_NOFOLLOW); } else { if (dereference) rc = stat(abs, st); else rc = lstat(abs, st); } return rc; } /* Fill stat buffer @st with dummy values. */ void set_dummy_stat(struct stat *st) { /* Fake stat */ memset(st, 0, sizeof(struct stat)); st->st_mode = S_IRUSR | S_IWUSR | S_IFREG; st->st_uid = geteuid(); st->st_gid = getegid(); st->st_mtime = time(NULL); } /* Redirect all output streams to @fd and execute command @CMD */ int cmd_child(int fd, char *cmd) { char *argv[] = { "/bin/sh", "-c", NULL, NULL }; char *env[] = { NULL }; argv[2] = cmd; if (dup2(fd, STDOUT_FILENO) == -1 || dup2(fd, STDERR_FILENO) == -1) { mwarn("Could not redirect command output"); return EXIT_RUNTIME; } execve("/bin/sh", argv, env); return EXIT_RUNTIME; } #define PIPE_READ 0 #define PIPE_WRITE 1 /* Run command @cmd as a child process and store its PID in @pid_ptr. On * success, return a file descriptor that is an output pipe to the standard * output and standard error streams of the child process. Return %-1 on * error. */ int cmd_open(char *cmd, pid_t *pid_ptr) { int pfd[2]; pid_t pid; if (pipe(pfd) < 0) return -1; pid = fork(); if (pid < 0) { /* Fork error */ close(pfd[PIPE_READ]); close(pfd[PIPE_WRITE]); return -1; } else if (pid == 0) { /* Child process */ close(pfd[PIPE_READ]); exit(cmd_child(pfd[PIPE_WRITE], cmd)); } /* Parent process */ close(pfd[PIPE_WRITE]); *pid_ptr = pid; return pfd[PIPE_READ]; } /* Close the file descriptor @fd and end the process with PID @pid. When * not %NULL, use @status_ptr to store the resulting process status. */ int cmd_close(int fd, pid_t pid, int *status_ptr) { int status, rc = EXIT_OK; close(fd); kill(pid, SIGQUIT); if (waitpid(pid, &status, 0) == -1) { status = -errno; rc = EXIT_RUNTIME; } if (status_ptr) *status_ptr = status; return rc; } void misc_init(void) { set_timespec(&main_start_ts, 0, 0); pthread_key_create(&thread_name_key, free); set_threadname("main"); } void misc_cleanup(void) { clear_threadname(); pthread_key_delete(thread_name_key); } void set_stdout_data(void) { stdout_data = true; } bool starts_with(const char *str, const char *prefix) { size_t len; len = strlen(prefix); if (strncmp(str, prefix, len) == 0) return true; return false; } bool ends_with(const char *str, const char *suffix) { size_t str_len, s_len; str_len = strlen(str); s_len = strlen(suffix); if (str_len < s_len) return false; if (strcmp(str + str_len - s_len, suffix) != 0) return false; return true; } /* Remove subsequent slashes in @str */ void remove_double_slashes(char *str) { size_t i, to; char last; last = 0; for (i = 0, to = 0; str[i]; i++) { if (last != '/' || str[i] != '/') last = str[to++] = str[i]; } str[to] = 0; } s390-tools-2.38.0/dump2tar/src/strarray.c000066400000000000000000000034751502674226300200200ustar00rootroot00000000000000/* * dump2tar - tool to dump files and command output into a tar archive * * Dynamically growing string arrays * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include "misc.h" #include "strarray.h" /* Release resources associated with string array @array */ void free_strarray(struct strarray *array) { unsigned int i; for (i = 0; i < array->num; i++) free(array->str[i]); free(array->str); array->str = NULL; array->num = 0; } /* Add string @str to string array @array */ void add_str_to_strarray(struct strarray *array, const char *str) { array->str = mrealloc(array->str, sizeof(char *) * (array->num + 2)); array->str[array->num + 1] = NULL; array->str[array->num] = mstrdup(str); array->num++; } /* Add string resulting from @fmt and additional arguments to @array */ void add_vstr_to_strarray(struct strarray *array, const char *fmt, ...) { va_list args; char *str; va_start(args, fmt); util_vasprintf(&str, fmt, args); va_end(args); array->str = mrealloc(array->str, sizeof(char *) * (array->num + 2)); array->str[array->num + 1] = NULL; array->str[array->num] = str; array->num++; } /* Add all lines in file at @filename to @array */ int add_file_to_strarray(struct strarray *array, const char *filename) { FILE *fd; char *line = NULL; size_t line_size; int rc = EXIT_OK; fd = fopen(filename, "r"); if (!fd) { mwarn("%s: Cannot open file", filename); return EXIT_RUNTIME; } while (!feof(fd) && !ferror(fd)) { if (getline(&line, &line_size, fd) == -1) continue; chomp(line, "\n"); add_str_to_strarray(array, line); } if (ferror(fd)) rc = EXIT_RUNTIME; free(line); fclose(fd); return rc; } s390-tools-2.38.0/dump2tar/src/tar.c000066400000000000000000000157051502674226300167360ustar00rootroot00000000000000/* * dump2tar - tool to dump files and command output into a tar archive * * TAR file generation * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include "buffer.h" #include "idcache.h" #include "misc.h" #include "tar.h" #define LONGLINK "././@LongLink" #define TYPE_LONGLINK 'K' #define TYPE_LONGNAME 'L' #define BLOCKSIZE 512 #if __has_attribute(nonstring) # define __nonstring __attribute__ ((nonstring)) #else # define __nonstring #endif /* Basic TAR header */ struct tar_header { char name[100] __nonstring; char mode[8]; char uid[8]; char gid[8]; char size[12]; char mtime[12]; char chksum[8]; char typeflag; char linkname[100] __nonstring; char magic[6]; char version[2]; char uname[32]; char gname[32]; char devmajor[8]; char devminor[8]; char prefix[155]; }; /* Store the octal value of @value to at most @len bytes at @dest */ static void set_octal(char *dest, size_t len, unsigned long value) { int i; dest[len - 1] = 0; for (i = len - 2; i >= 0; i--) { dest[i] = '0' + (value & 7); value >>= 3; } } /* Store time @value to at most @len bytes at @dest */ static void set_time(char *dest, size_t len, time_t value) { time_t max = (1ULL << (3 * (len - 1))) - 1; if (value >= 0 && value <= max) { set_octal(dest, len, value); return; } for (; len > 0; len--) { dest[len - 1] = value & 0xff; value >>= 8; } dest[0] |= 0x80; } #define SET_FIELD(obj, name, value) \ set_octal((obj)->name, sizeof((obj)->name), (unsigned long) (value)) #define SET_TIME_FIELD(obj, name, value) \ set_time((obj)->name, sizeof((obj)->name), (time_t) (value)) #define SET_STR_FIELD(obj, name, value) \ strncpy((obj)->name, (value), sizeof((obj)->name)) /* Initialize the tar file @header with the provided data */ static void init_header(struct tar_header *header, const char *filename, const char *link, size_t len, struct stat *stat, char type) { unsigned int i, checksum; unsigned char *c; memset(header, 0, sizeof(*header)); /* Fill in header fields */ SET_STR_FIELD(header, name, filename); if (link) SET_STR_FIELD(header, linkname, link); SET_FIELD(header, size, len); if (stat) { SET_FIELD(header, mode, stat->st_mode & 07777); SET_FIELD(header, uid, stat->st_uid); SET_FIELD(header, gid, stat->st_gid); SET_TIME_FIELD(header, mtime, stat->st_mtime); uid_to_name(stat->st_uid, header->uname, sizeof(header->uname)); gid_to_name(stat->st_gid, header->gname, sizeof(header->gname)); } else { SET_FIELD(header, mode, 0644); SET_FIELD(header, uid, 0); SET_FIELD(header, gid, 0); SET_TIME_FIELD(header, mtime, 0); uid_to_name(0, header->uname, sizeof(header->uname)); gid_to_name(0, header->gname, sizeof(header->gname)); } header->typeflag = type; memcpy(header->magic, "ustar ", sizeof(header->magic)); memcpy(header->version, " ", sizeof(header->version)); /* Calculate checksum */ memset(header->chksum, ' ', sizeof(header->chksum)); checksum = 0; c = (unsigned char *) header; for (i = 0; i < sizeof(*header); i++) checksum += c[i]; snprintf(header->chksum, 7, "%06o", checksum); } /* Emit zero bytes via @emit_cb to pad @len to a multiple of BLOCKSIZE */ static int emit_padding(emit_cb_t emit_cb, void *data, size_t len) { size_t pad = BLOCKSIZE - len % BLOCKSIZE; char zeroes[BLOCKSIZE]; if (len % BLOCKSIZE > 0) { memset(zeroes, 0, BLOCKSIZE); return emit_cb(data, zeroes, pad); } return 0; } /* Emit @len bytes at @addr via @emit_cb and pad data to BLOCKSIZE with zero * bytes */ static int emit_data(emit_cb_t emit_cb, void *data, void *addr, size_t len) { int rc; if (len == 0) return 0; rc = emit_cb(data, addr, len); if (rc) return rc; return emit_padding(emit_cb, data, len); } /* Emit a tar header via @emit_cb */ static int emit_header(emit_cb_t emit_cb, void *data, char *filename, char *link, size_t len, struct stat *stat, char type) { struct tar_header header; size_t namelen = strlen(filename); size_t linklen; int rc; /* /proc can contain unreadable links which causes tar to complain * during extract - use a dummy value to handle this more gracefully */ if (link && !*link) link = " "; linklen = link ? strlen(link) : 0; if (linklen > sizeof(header.linkname)) { rc = emit_header(emit_cb, data, LONGLINK, NULL, linklen + 1, NULL, TYPE_LONGLINK); if (rc) return rc; rc = emit_data(emit_cb, data, link, linklen + 1); if (rc) return rc; } if (namelen > sizeof(header.name)) { rc = emit_header(emit_cb, data, LONGLINK, NULL, namelen + 1, NULL, TYPE_LONGNAME); if (rc) return rc; rc = emit_data(emit_cb, data, filename, namelen + 1); if (rc) return rc; } init_header(&header, filename, link, len, stat, type); return emit_data(emit_cb, data, &header, sizeof(header)); } struct emit_content_cb_data { emit_cb_t emit_cb; void *data; size_t len; int rc; }; /* Callback for emitting a single chunk of data of a buffer */ static int emit_content_cb(void *data, void *addr, size_t len) { struct emit_content_cb_data *cb_data = data; if (len > cb_data->len) len = cb_data->len; cb_data->len -= len; cb_data->rc = cb_data->emit_cb(cb_data->data, addr, len); if (cb_data->rc || cb_data->len == 0) return 1; return 0; } /* Emit at most @len bytes of contents of @buffer via @emit_cb and pad output * to BLOCKSIZE with zero bytes */ static int emit_content(emit_cb_t emit_cb, void *data, struct buffer *buffer, size_t len) { struct emit_content_cb_data cb_data; cb_data.emit_cb = emit_cb; cb_data.data = data; cb_data.len = len; cb_data.rc = 0; buffer_iterate(buffer, emit_content_cb, &cb_data); if (cb_data.rc) return cb_data.rc; return emit_padding(emit_cb, data, buffer->total); } /* Convert file meta data and content specified as @content into a * stream of bytes that is reported via the @emit_cb callback. @data is * passed through to the callback for arbitrary use. */ int tar_emit_file_from_buffer(char *filename, char *link, size_t len, struct stat *stat, char type, struct buffer *content, emit_cb_t emit_cb, void *data) { int rc; DBG("emit tar file=%s type=%d len=%zu", filename, type, len); rc = emit_header(emit_cb, data, filename, link, len, stat, type); if (rc) return rc; if (content) rc = emit_content(emit_cb, data, content, len); return rc; } /* Convert file meta data and content specified as @addr and @len into a * stream of bytes that is reported via the @emit_cb callback. @data is * passed through to the callback for arbitrary use. */ int tar_emit_file_from_data(char *filename, char *link, size_t len, struct stat *stat, char type, void *addr, emit_cb_t emit_cb, void *data) { int rc; DBG("emit tar file=%s type=%d len=%zu", filename, type, len); rc = emit_header(emit_cb, data, filename, link, len, stat, type); if (rc) return rc; if (addr) rc = emit_data(emit_cb, data, addr, len); return rc; } s390-tools-2.38.0/etc/000077500000000000000000000000001502674226300142225ustar00rootroot00000000000000s390-tools-2.38.0/etc/Makefile000066400000000000000000000005651502674226300156700ustar00rootroot00000000000000include ../common.mak CONFIG_FILES = ifneq (${SYSTEMDSYSTEMUNITDIR},) CONFIG_FILES += cpuplugd.conf sysconfig/cpi sysconfig/dumpconf \ sysconfig/mon_fsstatd sysconfig/mon_procd endif install: for file in $(CONFIG_FILES); do \ $(INSTALL) -g $(GROUP) -o $(OWNER) \ -m 644 $$file $(DESTDIR)$(SYSCONFDIR)/$$file ; \ done .PHONY: all install clean s390-tools-2.38.0/etc/cpuplugd.conf000066400000000000000000000126711502674226300167230ustar00rootroot00000000000000# # Exemplary configuration file for the Linux on System z CPU and memory hotplug # daemon # # The file is evaluated by cpuplugd at startup when called with -c. # It does not contain shell environment variables. ## Type: integer ## Default: 1 # # The minimum number of CPUs must be > 0. # CPU_MIN="1" ## Type: integer ## Default: 0 # # The maximum number of CPUs to be enabled. If 0 is specified here, # the maximum number of CPUs equals the number of CPUs detected. # CPU_MAX="0" ## Type: integer ## Default: 5 # # The update interval described how often the current system state # is checked against the configured set of hotplug and hotunplug rules. # The update interval is defined in seconds. # UPDATE="1" ## Type: integer ## Default: 0 # # The minimum size of the static page pool (in 4K pages) # CMM_MIN="0" ## Type: integer ## Default: 131072 # # The maximum size of the static page pool (in 4K pages) # # Recommended setting is system size minus 256 MB CMM_MAX="131072" # 512 MB # # Variables # # User-defined variables are case-sensitive and must not match a pre-defined # variable or keyword. In the configuration file, definitions # for user-defined variables must precede their use in expressions. # Variable names consist of alphanumeric characters (a-z,A-Z,0-9) and # the "_" character. The maximum name length for a variable is 128 characters, # and the maximum total size for user-defined variables (names + values) is 4096 # characters. # pgscan_d="vmstat.pgscan_direct_dma[0] + vmstat.pgscan_direct_normal[0] + vmstat.pgscan_direct_movable[0]" pgscan_d1="vmstat.pgscan_direct_dma[1] + vmstat.pgscan_direct_normal[1] + vmstat.pgscan_direct_movable[1]" # page scan rate in pages / timer tick pgscanrate="(pgscan_d - pgscan_d1) / (cpustat.total_ticks[0] - cpustat.total_ticks[1])" # cache usage in kilobytes avail_cache="meminfo.Cached - meminfo.Shmem" user_0="(cpustat.user[0] - cpustat.user[1])" nice_0="(cpustat.nice[0] - cpustat.nice[1])" system_0="(cpustat.system[0] - cpustat.system[1])" user_2="(cpustat.user[2] - cpustat.user[3])" nice_2="(cpustat.nice[2] - cpustat.nice[3])" system_2="(cpustat.system[2] - cpustat.system[3])" CP_Active0="(user_0 + nice_0 + system_0) / (cpustat.total_ticks[0] - cpustat.total_ticks[1])" CP_Active2="(user_2 + nice_2 + system_2) / (cpustat.total_ticks[2] - cpustat.total_ticks[3])" CP_ActiveAVG="(CP_Active0+CP_Active2) / 2" idle_0="(cpustat.idle[0] - cpustat.idle[1])" iowait_0="(cpustat.iowait[0] - cpustat.iowait[1])" idle_2="(cpustat.idle[2] - cpustat.idle[3])" iowait_2="(cpustat.iowait[2] - cpustat.iowait[3])" CP_idle0="(idle_0 + iowait_0) / (cpustat.total_ticks[0] - cpustat.total_ticks[1])" CP_idle2="(idle_2 + iowait_2) / (cpustat.total_ticks[2] - cpustat.total_ticks[3])" CP_idleAVG="(CP_idle0 + CP_idle2) / 2" ## Type: string ## Default: "(meminfo.MemFree + cache) / 40" # # The amount of pages the static page pool # is increased if a memunplug rule is matched. # # cmm_inc: 10% of free memory, in 4K pages CMM_INC="meminfo.MemFree / 40" ## Type: string ## Default: "meminfo.MemTotal / 40" # # The amount of pages the static page pool # is decreased if a memplug rule is matched. # # cmm_dec: 10% of total memory, in 4K pages CMM_DEC="meminfo.MemTotal / 40" # # Ruledefinitions # # Four kinds of rules are distinguished # (1) hotplug rules, used to enable CPUs # (2) hotunplug rules, to disable CPUs # (3) memplug rules, used to increase memory (decrease cmm balloon) # (4) memunplug rules, used to decrease memory (increase cmm balloon) # # Within the hotplug/hotunplug rule definitions the following variables # can be used: # - loadavg: the current loadaverage # - onumcpus: the current number of CPUs which are online # - runnable_proc: the current amount of runnable processes # - user: the current user percentage # - nice: the current nice percentage # - system: the current system percentage # - idle: the current idle percentage # - iowait: the current iowait percentage # - irq: the current irq percentage # - softirq: the current softirq percentage # - steal: the current steal percentage # - guest: the current guest percentage # - guest_nice: the current guest_nice percentage # - cpustat.: data from /proc/stat and /proc/loadavg # - time: floating point timestamp in "seconds.microseconds" # since the Unix Epoch (1970-01-01 00:00:00 +0000 (UTC)) # ## Type: string ## Default: "(loadavg > onumcpus + 0.75) & (idle < 10.0)" # HOTPLUG="((1 - CP_ActiveAVG) * onumcpus) < 0.08" ## Type: string ## Default: "(loadavg < onumcpus - 0.25) | (idle > 50)" # HOTUNPLUG="(CP_idleAVG * onumcpus) > 1.15" # # Memplug and memunplug can contain the following keywords: # - apcr: the amount of page cache operations, # i.e. pgpin + pgpout from /proc/vmstat # (in 512 byte blocks / second) # - freemem: the amount of free memory (in megabytes) # - swaprate: the number of swap operations, i.e. pswpin + pswpout # from /proc/vmstat (in pages / second) # - meminfo.: any value from /proc/meminfo # - vmstat.: any value from /proc/vmstat # - time: floating point timestamp in "seconds.microseconds" # since the Unix Epoch (1970-01-01 00:00:00 +0000 (UTC)) # # This function is disabled by default, because this rule has to be # adjusted for each production system, depending on the environment. # ## Type: string ## Default: "0" # #MEMPLUG="pgscanrate > 20" MEMPLUG="0" ## Type: string ## Default: "0" # #MEMUNPLUG="(meminfo.MemFree + avail_cache) > (meminfo.MemTotal / 10)" MEMUNPLUG="0" s390-tools-2.38.0/etc/hsavmcore.conf000066400000000000000000000005611502674226300170620ustar00rootroot00000000000000# Example configuration for hsavmcore # See hsavmcore.conf(8) for documentation # 0 - ERROR # 1 - WARN # 2 - INFO # 3 - DEBUG # 4 - TRACE #verbose = 0 #workdir = /var/crash #mount_debugfs = 0 #use_hsa_mem = 0 #hsa_size = -1 #release_hsa = 1 #bind_mount_vmcore = 1 #swap = /dev/disk/by-uuid/3cf6630b-4c4d-49ac-a0ae-0f5484cb5721 #swap = /swap.img #fuse_debug = 0 s390-tools-2.38.0/etc/modules-load.d/000077500000000000000000000000001502674226300170315ustar00rootroot00000000000000s390-tools-2.38.0/etc/modules-load.d/s390-pkey.conf000066400000000000000000000001471502674226300213460ustar00rootroot00000000000000# Load protected key support module on s390 early at boot pkey pkey_pckmo pkey_ep11 pkey_cca paes_s390 s390-tools-2.38.0/etc/sysconfig/000077500000000000000000000000001502674226300162265ustar00rootroot00000000000000s390-tools-2.38.0/etc/sysconfig/cpi000066400000000000000000000004661502674226300167320ustar00rootroot00000000000000# # Apply control program identification (CPI) settings # # The system and sysplex names consist of up to eight characters of # the following set: A-Z, 0-9, $, @, #, and blank. # # CPI system type # CPI_SYSTEM_TYPE="LINUX" # # CPI system name # CPI_SYSTEM_NAME="" # # CPI sysplex name # CPI_SYSPLEX_NAME="" s390-tools-2.38.0/etc/sysconfig/dumpconf000066400000000000000000000034201502674226300177630ustar00rootroot00000000000000# # s390 dump config # # Configures the actions which should be performed after a kernel panic # and on PSW restart. # # The following actions are supported: # # * stop: Stop Linux (default) # * dump: Dump Linux with stand-alone dump tool # * vmcmd: Issue z/VM CP commands # * reipl: Re-IPL Linux using setting under /sys/firmware/reipl # * dump_reipl: First dump Linux with stand-alone dump tool, then re-IPL Linux # using setting under /sys/firmware/reipl # # For the actions "reipl" and "dump_reipl" the DELAY_MINUTES keyword may # be used to delay the activation of dumpconf. # Thus potential reipl loops caused by kernel panics # which persistently occur early in the boot process can be prevented. # Dump on CCW device (DASD) and re-IPL after dump is complete. # The re-IPL device, as specified under "/sys/firmware/reipl", is used. # The activation of dumpconf is delayed by 5 minutes. # # ON_PANIC=dump_reipl # DUMP_TYPE=ccw # DEVICE=0.0.4e13 # DELAY_MINUTES=5 # # Dump on ECKD device (DASD) # # ON_PANIC=dump # DUMP_TYPE=eckd # DEVICE=0.0.1004 # BOOTPROG=0 # BR_CHR=auto # # Dump on fcp device (SCSI Disk) # # ON_PANIC=dump # DUMP_TYPE=fcp # DEVICE=0.0.4711 # WWPN=0x5005076303004711 # LUN=0x4711000000000000 # BOOTPROG=0 # BR_LBA=0 # # Dump on nvme device (NVMe Disk) # # ON_PANIC=dump # DUMP_TYPE=nvme # FID=0x00000300 # NSID=0x00000001 # BOOTPROG=3 # BR_LBA=0 # # Use VMDUMP # # ON_PANIC=vmcmd # VMCMD_1="MESSAGE * Starting VMDUMP" # VMCMD_2="VMDUMP" # VMCMD_3="IPL 4711" # # Stop Linux (default) # # ON_PANIC=stop # # Re-IPL Linux # The re-IPL device, as specified under "/sys/firmware/reipl", is used. # Since the DELAY_MINUTES keyword is omitted, there is no delay and # dumpconf becomes active immediately during system startup. # # ON_PANIC=reipl s390-tools-2.38.0/etc/sysconfig/mon_fsstatd000066400000000000000000000003641502674226300204750ustar00rootroot00000000000000## Path: System/Monitoring/s390-tools/mon_fsstatd ## Description: Linux - z/VM Monitor Daemon for process and filesystem data. ## Type: integer(1:) ## Default: 60 # # Monitor interval in seconds for filesystem monitoring. # FSSTAT_INTERVAL=60 s390-tools-2.38.0/etc/sysconfig/mon_procd000066400000000000000000000003551502674226300201340ustar00rootroot00000000000000## Path: System/Monitoring/s390-tools/mon_procd ## Description: Linux - z/VM Monitor Daemon for process and filesystem data. ## Type: integer(1:) ## Default: 60 # # Monitor interval in seconds for process monitoring. # PROC_INTERVAL=60 s390-tools-2.38.0/etc/sysconfig/mon_statd000066400000000000000000000010711502674226300201400ustar00rootroot00000000000000## Path: System/Monitoring/s390-tools/mon_statd ## Description: Linux - z/VM Monitor Daemon for process and filesystem data. ## Type: integer(1:) ## Default: 60 # # Monitor interval in seconds for filesystem monitoring. # FSSTAT_INTERVAL=60 ## Type: integer(1:) ## Default: 60 # # Monitor interval in seconds for process monitoring. # PROC_INTERVAL=60 ## Type: yesno ## Default: no # # Set this to "yes" if you want to enable filesystem monitoring. FSSTAT="no" ## Type: yesno ## Default: no # # Set this to "yes" if you want to enable process monitoring. PROC="no" s390-tools-2.38.0/etc/udev/000077500000000000000000000000001502674226300151655ustar00rootroot00000000000000s390-tools-2.38.0/etc/udev/rules.d/000077500000000000000000000000001502674226300165415ustar00rootroot00000000000000s390-tools-2.38.0/etc/udev/rules.d/40-z90crypt.rules000066400000000000000000000002211502674226300215350ustar00rootroot00000000000000# # Rule for z90crypt character device node permissions # This file should be installed in /etc/udev/rules.d # KERNEL=="z90crypt", MODE="0666" s390-tools-2.38.0/etc/udev/rules.d/57-osasnmpd.rules000066400000000000000000000001231502674226300216660ustar00rootroot00000000000000SUBSYSTEM=="ccwgroup", ACTION=="change", RUN+="/usr/bin/killall -SIGUSR1 osasnmpd" s390-tools-2.38.0/etc/udev/rules.d/59-dasd.rules000066400000000000000000000025211502674226300207630ustar00rootroot00000000000000# # Rules for unique DASD device nodes created in /dev/disk/ # This file should be installed in /etc/udev/rules.d # SUBSYSTEM!="block", GOTO="dasd_symlinks_end" KERNEL!="dasd*", GOTO="dasd_symlinks_end" ACTION!="change", GOTO="dasd_block_end" # by-id (hardware serial number) KERNEL=="dasd*[!0-9]", ATTRS{status}=="online", IMPORT{program}="/sbin/dasdinfo -a -e -b $kernel" KERNEL=="dasd*[!0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}" KERNEL=="dasd*[!0-9]", ENV{ID_UID}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_UID}" KERNEL=="dasd*[!0-9]", ENV{ID_XUID}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_XUID}" LABEL="dasd_block_end" ACTION!="change|add", GOTO="dasd_symlinks_end" # for partitions import parent information KERNEL=="dasd*[0-9]", IMPORT{parent}=="ID_*" KERNEL=="dasd*[0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}-part%n" KERNEL=="dasd*[0-9]", ENV{ID_UID}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_UID}-part%n" KERNEL=="dasd*[0-9]", ENV{ID_XUID}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_XUID}-part%n" LABEL="dasd_symlinks_end" # on device add set request queue scheduler to none SUBSYSTEM!="block", GOTO="sched_end" ACTION!="change", GOTO="sched_end" KERNEL=="dasd*[!0-9]", TEST=="queue/scheduler", ATTR{queue/scheduler}="none" LABEL="sched_end" s390-tools-2.38.0/etc/udev/rules.d/60-readahead.rules000066400000000000000000000010611502674226300217340ustar00rootroot00000000000000# # Rules to set an increased default max readahead size # This file should be installed in /etc/udev/rules.d # SUBSYSTEM!="block", GOTO="ra_end" ACTION!="add", GOTO="ra_change" # on device add set initial readahead to 512 (instead of in kernel 128) KERNEL=="sd*[!0-9]", TEST=="queue/read_ahead_kb", ATTR{queue/read_ahead_kb}="512" LABEL="ra_change" ACTION!="change", GOTO="ra_end" # on device change set initial readahead to 512 (instead of in kernel 128) KERNEL=="dasd*[!0-9]", TEST=="queue/read_ahead_kb", ATTR{queue/read_ahead_kb}="512" LABEL="ra_end" s390-tools-2.38.0/etc/udev/rules.d/80-hotplug-cpu.rules000066400000000000000000000002731502674226300223130ustar00rootroot00000000000000# # Rules to set online a newly hotplugged cpu on s390x # SUBSYSTEM=="cpu", ACTION=="add", CONST{arch}=="s390*", ATTR{configure}=="1", TEST=="online", ATTR{online}!="1", ATTR{online}="1" s390-tools-2.38.0/etc/udev/rules.d/90-cpi.rules000066400000000000000000000003251502674226300206160ustar00rootroot00000000000000# # Rules to trigger cpictl when KVM is used # This file should be installed in /etc/udev/rules.d # SUBSYSTEM=="misc", KERNEL=="kvm", ENV{CREATED}=="1", ENV{EVENT}=="create", RUN+="/lib/s390-tools/cpictl -b kvm" s390-tools-2.38.0/fdasd/000077500000000000000000000000001502674226300145305ustar00rootroot00000000000000s390-tools-2.38.0/fdasd/Makefile000066400000000000000000000007371502674226300161770ustar00rootroot00000000000000include ../common.mak libs = $(rootdir)/libvtoc/libvtoc.a \ $(rootdir)/libzds/libzds.a \ $(rootdir)/libdasd/libdasd.a \ $(rootdir)/libutil/libutil.a all: fdasd fdasd: fdasd.o $(libs) install: all $(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8 $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 fdasd $(DESTDIR)$(BINDIR) $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 fdasd.8 \ $(DESTDIR)$(MANDIR)/man8 clean: rm -f *.o *~ fdasd core .PHONY: all install clean s390-tools-2.38.0/fdasd/fdasd.8000066400000000000000000000120621502674226300157030ustar00rootroot00000000000000.\" Copyright 2017 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH FDASD 8 "Apr 2006" "s390-tools" .SH NAME fdasd \- partitioning tool. .SH SYNOPSIS interactive mode: .br \fBfdasd\fR [\-s] [\-r] [\-C] \fIdevice\fR .br command line mode: .br \fBfdasd\fR [\-s] [\-r] [\-C] {\-a[\-k|\-l \fIvolser\fR]|\-i|\-p|\-c \fIconf_file\fR} [-f \fI[type,blocksize]\fR] \fIdevice\fR .br help: .br \fBfdasd\fR {\-h|\-v} .SH DESCRIPTION \fBfdasd\fR writes a partition table to a cdl (compatible disk layout) formatted DASD, in the form of a VTOC (volume table of contents) for usage with Linux for S/390 or zSeries. If fdasd detects a valid \fBVOL1\fR volume label, it will use it, otherwise it asks to write a new one. .br \fBAttention\fR: Careless use of \fBfdasd\fR can result in loss of data. .SH OPTIONS .TP \fB\-h\fR or \fB\-\-help\fR Print usage information, then exit. .TP \fB\-v\fR or \fB\-\-version\fR Print version information, then exit. .TP \fB\-s\fR or \fB\-\-silent\fR Suppress messages in non-interactive mode. .TP \fB\-r\fR or \fB\-\-verbose\fR Provide more verbose output. .TP \fB\-a\fR or \fB\-\-auto\fR Automatically create a partition using the entire disk in non-interactive mode. .TP \fB\-k\fR or \fB\-\-keep_volser\fR Keeps the Volume Serial Number when writing the Volume Label. .br This is useful if the volume already has a Serial Number that should not be overwritten. This option is only applicable in non-interactive mode. .TP \fB\-l\fR \fIvolser\fR or \fB\-\-label\fR \fIvolser\fR Specify the volume serial. .br \fIvolser\fR is interpreted as ASCII string and is automatically converted to uppercase, padded with blanks and finally converted to EBCDIC to be written to disk. This option is only applicable in non-interactive mode. .br Do not use the following reserved volume serial: SCRTCH, PRIVAT, MIGRAT, or Lnnnnn (L with five digit number); These are used as keywords by other operating systems (OS/390). .br A volume serial is 1 through 6 alphanumeric characters or one of the following special characters: $, #, @, %. All other characters are simply ignored. .br Try to avoid using special characters in the volume serial. This may cause problems accessing a disk by volser. In case you really have to use special characters, make sure you are using quotes. In addition there is a special handling for the '$' sign. Please specify it using '\\$' if necessary. .br e.g. \-l 'a@b\\$c#' to get A@B$C# .br Omitting this parameter causes fdasd to ask for it in case it is needed. .br .TP \fB\-c\fR \fIconf_file\fR or \fB\-\-config\fR \fIconf_file\fR Use this option to create multiple partitions according to specifications in a configuration file, \fIconf_file\fR. .br The configuration file contains one line for each partition. The lines have this format: .sp [,,] .br where: .br and are numbers that specify the first and the last track of the partition. Instead of a numerical value, you can specify 'first' for the first possible track on the disk and 'last' for the last possible track on disk. .br is optional and specifies the partition type. can be one of: native, swap, raid, lvm, or gpfs. If omitted, 'native' is used. .br The configuration file of the following example specifies three partitions that use the entire disk: .sp [first,1000] .br [1001,2000,swap] .br [2001,last,lvm] .br .TP \fB\-i\fR or \fB\-\-volser\fR Print the volume serial, then exit. .TP \fB\-p\fR or \fB\-\-table\fR Print partition table, then exit. .br In combination with the \-s option fdasd will display a short version of the partition table. .TP \fB\-C\fR or \fB\-\-check_host_count\fR Force fdasd to check the host access open count to ensure the device is not online on another operating system instance .TP \fB\-f\fR \fI[type,blocksize]\fR or \fB\-\-force\fR \fI[type,blocksize]\fR Force fdasd to work on non DASD devices. .br If fdasd is to be used on a block device that is neither a native DASD nor exposes the proper disk geometry of a DASD of type 3390, then the \-\-force option can be used to assume the geometry of a given device type. The default device type is 3390 and the default block size is 4096. An optional argument of , can be used to specify type and blocksize explicitly. For example: \-f has the same effect as \-f3390,4096 or \-\-force=3390,4096 Valid device types are: 3390, 3380, 9345 .br Valid block sizes are: 4096, 2048, 1024, 512 You can use the verbose option to get information about the disk geometry that fdasd has computed from device type and block size. .TP \fIdevice\fR This parameter specifies the device to be partitioned: .sp \fB/dev/dasd/\fR\fIxxxx\fR\fB/device\fR .br where \fIxxxx\fR is the four-letter devno (device number). .br In case your are not using the device file system, please specify: .sp \fB/dev/dasd\fR\fIx\fR .br where \fIx\fR is one or more lowercase letter(s) or any other device node specification configured by udev for kernel 2.6 or higher. .SH SEE ALSO .BR dasdfmt (8) s390-tools-2.38.0/fdasd/fdasd.c000066400000000000000000002270401502674226300157620ustar00rootroot00000000000000/* * fdasd - Create or modify partitions on ECKD DASDs * * Copyright IBM Corp. 2001, 2018 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include "lib/dasd_base.h" #include "lib/dasd_sys.h" #include "lib/util_base.h" #include "lib/util_opt.h" #include "lib/util_prg.h" #include "lib/vtoc.h" #include "lib/zt_common.h" #include "fdasd.h" /* global variables */ static struct hd_geometry geo; static char line_buffer[LINE_LENGTH]; static char *line_ptr = line_buffer; /* * Array of all supported partition types and its corresponding name and * DSNAME (Data Set Name) */ static partition_type_t partition_types[] = { [PARTITION_NEW] = { .name = "Linux native", .dsname = "NEW", .type = PARTITION_NATIVE, }, [PARTITION_NATIVE] = { .name = "Linux native", .dsname = "NATIVE", .type = PARTITION_NATIVE, }, [PARTITION_SWAP] = { .name = "Linux swap", .dsname = "SWAP", .type = PARTITION_SWAP, }, [PARTITION_RAID] = { .name = "Linux raid", .dsname = "RAID", .type = PARTITION_RAID, }, [PARTITION_LVM] = { .name = "Linux lvm", .dsname = "LVM", .type = PARTITION_LVM, }, [PARTITION_GPFS] = { .name = "GPFS NSD", .dsname = "GPFS", .type = PARTITION_GPFS, }, }; /* * Return the partition name corresponding to DSNAME (Data Set Name) */ static int get_part_name_by_dsname(char *dsname, char **name) { unsigned int i; if (!dsname) { *name = partition_types[PARTITION_NATIVE].name; return 0; } for (i = 0; i < ARRAY_SIZE(partition_types); i++) { if (strstr(dsname, partition_types[i].dsname) != NULL) { *name = partition_types[i].name; return 0; } } return 1; } /* * Return the partition type corresponding to DSNAME (Data Set Name) */ static int get_part_type_by_dsname(char *dsname, int *type) { unsigned int i; if (!dsname) { *type = PARTITION_NATIVE; return 0; } for (i = 0; i < ARRAY_SIZE(partition_types); i++) { if (strstr(dsname, partition_types[i].dsname) != NULL) { *type = partition_types[i].type; return 0; } } return 1; } /* * Return the DSNAME (Data Set Name) of a partition corresponding to its type */ static int get_part_dsname_by_type(unsigned int type, char **dsname) { if (type > ARRAY_SIZE(partition_types)) return 1; if (type == 0) *dsname = partition_types[PARTITION_NATIVE].dsname; else *dsname = partition_types[type].dsname; return 0; } static const struct util_prg prg = { .desc = "Manage partitions on DASD volumes.\n" "DEVICE is the node of the device (e.g. '/dev/dasda').", .args = "DEVICE", .copyright_vec = { { .owner = "IBM Corp.", .pub_first = 2001, .pub_last = 2017, }, UTIL_PRG_COPYRIGHT_END } }; static struct util_opt opt_vec[] = { UTIL_OPT_SECTION("NON-INTERACTIVE MODE"), { .option = { "auto", no_argument, NULL, 'a' }, .desc = "Create a single partition spanning the entire disk", }, { .option = { "config", required_argument, NULL, 'c' }, .argument = "FILE", .desc = "Create partitions(s) based on content of FILE", }, { .option = { "keep_volser", no_argument, NULL, 'k' }, .desc = "Do not change the current volume serial", }, { .option = { "label", required_argument, NULL, 'l' }, .argument = "VOLSER", .desc = "Set the volume serial to VOLSER", }, UTIL_OPT_SECTION("MISC"), { .option = { "check_host_count", no_argument, NULL, 'C' }, .desc = "Check if device is in use by other hosts", }, { .option = { "force", optional_argument, NULL, 'f' }, .argument = "TYPE,SIZE", .desc = "Force fdasd to work on non DASD devices with assumed " "TYPE (3390, 3380, or 9345) and blocksize SIZE", }, { .option = { "volser", no_argument, NULL, 'i' }, .desc = "Print volume serial", }, { .option = { "table", no_argument, NULL, 'p' }, .desc = "Print partition table", }, { .option = { "verbose", no_argument, NULL, 'r' }, .desc = "Provide more verbose output", }, { .option = { "silent", no_argument, NULL, 's' }, .desc = "Suppress messages", }, UTIL_OPT_HELP, UTIL_OPT_VERSION, UTIL_OPT_END }; static int getpos(fdasd_anchor_t *anc, int dsn) { return anc->partno[dsn]; } static int getdsn(fdasd_anchor_t *anc, int pos) { int i; for (i = 0; i < USABLE_PARTITIONS; i++) { if (anc->partno[i] == pos) return i; } return -1; } static void setpos(fdasd_anchor_t *anc, int dsn, int pos) { anc->partno[dsn] = pos; } static u_int32_t get_usable_cylinders(fdasd_anchor_t *anc) { u_int32_t cyl; /* large volume */ if (anc->f4->DS4DEVCT.DS4DSCYL == LV_COMPAT_CYL && anc->f4->DS4DCYL > anc->f4->DS4DEVCT.DS4DSCYL) return anc->f4->DS4DCYL; /* normal volume */ if (anc->f4->DS4DEVCT.DS4DEVFG & ALTERNATE_CYLINDERS_USED) cyl = anc->f4->DS4DEVCT.DS4DSCYL - (u_int16_t)anc->f4->DS4DEVAC; else cyl = anc->f4->DS4DEVCT.DS4DSCYL; return cyl; } static void get_addr_of_highest_f1_f8_label(fdasd_anchor_t *anc, cchhb_t *addr) { u_int8_t record; /* We have to count the following labels: * one format 4 * one format 5 * format 7 only if we have moren then BIG_DISK_SIZE tracks * one for each format 1 or format 8 label == one for each partition * one for each format 9 label before the last format 8 * We assume that all partitions use format 8 labels when * anc->formatted_cylinders > LV_COMPAT_CYL * Note: Record zero is special, so block 0 on our disk is record 1! */ record = anc->used_partitions + 2; if (anc->big_disk) record++; if (anc->formatted_cylinders > LV_COMPAT_CYL) record += anc->used_partitions - 1; vtoc_set_cchhb(addr, VTOC_START_CC, VTOC_START_HH, record); } /* * Check for valid volume serial characters (max. 6) - remove invalid. * If volser is empty, fill with default volser. */ static void fdasd_check_volser(char *volser, int devno) { int from, to; for (from = 0, to = 0; from < VOLSER_LENGTH && volser[from]; from++) if ((volser[from] >= 0x23 && volser[from] <= 0x25) || /* # $ % */ (volser[from] >= 0x30 && volser[from] <= 0x39) || /* 0-9 */ (volser[from] >= 0x40 && volser[from] <= 0x5a) || /* @ A-Z */ (volser[from] >= 0x61 && volser[from] <= 0x7a)) /* a-z */ volser[to++] = toupper(volser[from]); volser[to] = 0x00; if (volser[0] == 0x00) sprintf(volser, "0X%04x", devno); } /* * Free memory of fdasd anchor struct. */ static void fdasd_cleanup(fdasd_anchor_t *anchor) { partition_info_t *part_info, *next; int i; if (anchor == NULL) return; if (anchor->f4 != NULL) free(anchor->f4); if (anchor->f5 != NULL) free(anchor->f5); if (anchor->f7 != NULL) free(anchor->f7); if (anchor->vlabel != NULL) free(anchor->vlabel); part_info = anchor->first; for (i = 1; i <= USABLE_PARTITIONS && part_info != NULL; i++) { next = part_info->next; free(part_info->f1); free(part_info); part_info = next; } } /* * Exit fdasd. */ static void __noreturn fdasd_exit(fdasd_anchor_t *anchor, int rc) { fdasd_cleanup(anchor); exit(rc); } /* * */ static void fdasd_error(fdasd_anchor_t *anc, enum fdasd_failure why, char *str) { char err_str[ERROR_STRING_SIZE]; switch (why) { case parser_failed: snprintf(err_str, ERROR_STRING_SIZE, "%s parser error\n%s\n", FDASD_ERROR, str); break; case unable_to_open_disk: snprintf(err_str, ERROR_STRING_SIZE, "%s open error\n%s\n", FDASD_ERROR, str); break; case unable_to_seek_disk: snprintf(err_str, ERROR_STRING_SIZE, "%s seek error\n%s\n", FDASD_ERROR, str); break; case unable_to_read_disk: snprintf(err_str, ERROR_STRING_SIZE, "%s read error\n%s\n", FDASD_ERROR, str); break; case read_only_disk: snprintf(err_str, ERROR_STRING_SIZE, "%s write error\n%s\n", FDASD_ERROR, str); break; case unable_to_ioctl: snprintf(err_str, ERROR_STRING_SIZE, "%s IOCTL error\n%s\n", FDASD_ERROR, str); break; case wrong_disk_type: snprintf(err_str, ERROR_STRING_SIZE, "%s Unsupported disk type\n%s\n", FDASD_ERROR, str); break; case wrong_disk_format: snprintf(err_str, ERROR_STRING_SIZE, "%s Unsupported disk format\n%s\n", FDASD_ERROR, str); break; case disk_in_use: snprintf(err_str, ERROR_STRING_SIZE, "%s Disk in use\n%s\n", FDASD_ERROR, str); break; case config_syntax_error: snprintf(err_str, ERROR_STRING_SIZE, "%s Config file syntax error\n%s\n", FDASD_ERROR, str); break; case vlabel_corrupted: snprintf(err_str, ERROR_STRING_SIZE, "%s Volume label is corrupted.\n%s\n", FDASD_ERROR, str); break; case dsname_corrupted: snprintf(err_str, ERROR_STRING_SIZE, "%s a data set name is corrupted.\n%s\n", FDASD_ERROR, str); break; case malloc_failed: snprintf(err_str, ERROR_STRING_SIZE, "%s space allocation\n%s\n", FDASD_ERROR, str); break; case device_verification_failed: snprintf(err_str, ERROR_STRING_SIZE, "%s device verification failed\n%s\n", FDASD_ERROR, str); break; case volser_not_found: snprintf(err_str, ERROR_STRING_SIZE, "%s VOLSER not found on device %s\n", FDASD_ERROR, str); break; default: snprintf(err_str, ERROR_STRING_SIZE, "%s Fatal error\n%s\n", FDASD_ERROR, str); } fputc('\n', stderr); fputs(err_str, stderr); fdasd_exit(anc, EXIT_FAILURE); } /* * Read line from stdin into global line_buffer * and set global line_ptr to first printing character except space. */ static int read_line(void) { bzero(line_buffer, LINE_LENGTH); line_ptr = line_buffer; if (!fgets(line_buffer, LINE_LENGTH, stdin)) { clearerr(stdin); return 0; } while (*line_ptr && !isgraph(*line_ptr)) line_ptr++; return *line_ptr; } /* * */ static char read_char(char *mesg) { fputs(mesg, stdout); read_line(); return *line_ptr; } /* * Print question string an enforce y/n answer. */ static int yes_no(char *question_str) { ssize_t bytes_read; char *answer; size_t size; size = 0; answer = NULL; while (1) { printf("%s (y/n): ", question_str); bytes_read = getline(&answer, &size, stdin); if (bytes_read < 0) return -1; if (answer[0] == 'y') return 0; if (answer[0] == 'n') return 1; } free(answer); } static char *fdasd_partition_type(char *dsname) { char *name = NULL; if (get_part_name_by_dsname(dsname, &name)) name = "unknown"; return name; } /* * prints the menu */ static void fdasd_menu(void) { printf("Command action\n" " m print this menu\n" " p print the partition table\n" " n add a new partition\n" " d delete a partition\n" " l list known partition types\n" " v change volume serial\n" " t change partition type\n" " r re-create VTOC and delete all partitions\n" " u re-create VTOC re-using existing partition sizes\n" " s show mapping (partition number - data set name)\n" " q quit without saving changes\n" " w write table to disk and exit\n"); } /* * initializes the anchor structure and allocates some * memory for the labels */ static void fdasd_initialize_anchor(fdasd_anchor_t *anc) { partition_info_t *part_info, *prev_part_info = NULL; volume_label_t *vlabel; int i; bzero(anc, sizeof(fdasd_anchor_t)); for (i = 0; i < USABLE_PARTITIONS; i++) setpos(anc, i, -1); anc->f4 = malloc(sizeof(format4_label_t)); if (anc->f4 == NULL) fdasd_error(anc, malloc_failed, "FMT4 DSCB memory allocation failed."); anc->f5 = malloc(sizeof(format5_label_t)); if (anc->f5 == NULL) fdasd_error(anc, malloc_failed, "FMT5 DSCB memory allocation failed."); anc->f7 = malloc(sizeof(format7_label_t)); if (anc->f7 == NULL) fdasd_error(anc, malloc_failed, "FMT7 DSCB memory allocation failed."); /* template for all format 9 labels */ anc->f9 = malloc(sizeof(format9_label_t)); if (anc->f9 == NULL) fdasd_error(anc, malloc_failed, "FMT9 DSCB memory allocation failed."); bzero(anc->f4, sizeof(format4_label_t)); bzero(anc->f5, sizeof(format5_label_t)); bzero(anc->f7, sizeof(format7_label_t)); bzero(anc->f9, sizeof(format9_label_t)); vtoc_init_format9_label(anc->f9); vlabel = malloc(sizeof(volume_label_t)); if (vlabel == NULL) fdasd_error(anc, malloc_failed, "Volume label memory allocation failed."); bzero(vlabel, sizeof(volume_label_t)); anc->vlabel = vlabel; for (i = 1; i <= USABLE_PARTITIONS; i++) { part_info = malloc(sizeof(partition_info_t)); if (part_info == NULL) fdasd_error(anc, malloc_failed, "Partition info memory allocation failed."); memset(part_info, 0, sizeof(partition_info_t)); /* add part_info to double pointered list */ if (i == 1) { anc->first = part_info; } else if (i == USABLE_PARTITIONS) { anc->last = part_info; part_info->next = NULL; } part_info->f1 = malloc(sizeof(format1_label_t)); if (part_info->f1 == NULL) fdasd_error(anc, malloc_failed, "FMT1 DSCB memory allocation failed."); bzero(part_info->f1, sizeof(format1_label_t)); if (prev_part_info) { prev_part_info->next = part_info; part_info->prev = prev_part_info; } else { part_info->prev = NULL; } prev_part_info = part_info; } anc->hw_cylinders = 0; anc->formatted_cylinders = 0; } static void fdasd_parse_force_options(fdasd_anchor_t *anc, char *optarg) { char err_str[ERROR_STRING_SIZE]; unsigned int devtype, blksize; int rc; if (optarg) { rc = sscanf(optarg, "%x,%d", &devtype, &blksize); if (rc != 2) { snprintf(err_str, ERROR_STRING_SIZE, "Force parameter '%s' could not be parsed.\n", optarg); fdasd_error(anc, parser_failed, err_str); } if (devtype == DASD_3390_TYPE || devtype == DASD_3380_TYPE || devtype == DASD_9345_TYPE) { anc->dev_type = devtype; } else { snprintf(err_str, ERROR_STRING_SIZE, "Force parameter '%x' is not a supported" " device type.\n", devtype); fdasd_error(anc, parser_failed, err_str); } if (blksize == 4096 || blksize == 2048 || blksize == 1024 || blksize == 512) { anc->blksize = blksize; } else { snprintf(err_str, ERROR_STRING_SIZE, "Force parameter '%d' is not a supported" " block size.\n", blksize); fdasd_error(anc, parser_failed, err_str); } } else { /* force option was used without the optional argument */ anc->dev_type = DASD_3390_TYPE; anc->blksize = 4096; } } /* * parses the command line options */ static void fdasd_parse_options(fdasd_anchor_t *anc, struct fdasd_options *options, int argc, char *argv[]) { int opt; util_prg_init(&prg); util_opt_init(opt_vec, NULL); do { opt = util_opt_getopt_long(argc, argv); switch (opt) { case 'v': util_prg_print_version(); fdasd_exit(anc, 0); case 'h': util_prg_print_help(); util_opt_print_help(); fdasd_exit(anc, 0); case 'l': if (options->volser) fdasd_error(anc, parser_failed, "Option 'label' specified more " "than once.\n"); options->volser = optarg; break; case 'a': anc->auto_partition++; break; case 's': anc->silent++; break; case 'r': anc->verbose++; break; case 'p': anc->print_table++; break; case 'i': anc->print_volser++; anc->silent++; break; case 'c': if (options->conffile) fdasd_error(anc, parser_failed, "Option 'config' specified more" " than once.\n"); options->conffile = optarg; break; case 'k': anc->keep_volser++; break; case 'f': anc->force_virtual++; fdasd_parse_force_options(anc, optarg); break; case 'C': anc->force_host++; break; case -1: /* End of options string - start of devices list */ break; default: fprintf(stderr, "Try 'fdasd --help' for more" " information.\n"); fdasd_exit(anc, 1); } } while (opt != -1); /* save device */ if (optind >= argc) fdasd_error(anc, parser_failed, "No device specified.\n"); if (optind + 1 < argc) fdasd_error(anc, parser_failed, "More than one device specified.\n"); options->device = argv[optind]; } static int gettoken(char *str, char *ch, char *token[], int max) { int i; token[0] = strtok(str, ch); if (!token[0]) return 0; for (i = 1; i < max; i++) { token[i] = strtok(NULL, ch); if (!token[i]) break; } return i; } /* * parses config file */ static int fdasd_parse_conffile(fdasd_anchor_t *anc, struct fdasd_options *options) { char err_str[ERROR_STRING_SIZE], *c1, *c2, *token[CONFIG_MAX]; char buffer[CONFIG_FILE_SIZE + 1]; int fd, rc; int i; /* if name of config file was not specified, select the default */ if (options->conffile == NULL) options->conffile = DEFAULT_FDASD_CONF; if (!anc->silent) printf("parsing config file '%s'...\n", options->conffile); fd = open(options->conffile, O_RDONLY); if (fd < 0) { snprintf(err_str, ERROR_STRING_SIZE, "Could not open config file '%s' " "in read-only mode!\n", options->conffile); fdasd_error(anc, unable_to_open_disk, err_str); } memset(buffer, 0, sizeof(buffer)); rc = read(fd, buffer, sizeof(buffer) - 1); close(fd); if (rc < 0) return -1; for (i = 0; i < rc; i++) buffer[i] = toupper(buffer[i]); c1 = buffer; for (i = 0; i < USABLE_PARTITIONS; i++) { c1 = strchr(c1, '['); if (c1 == NULL) { if (!anc->silent) printf("no config file entry for " "partition %d found...\n", i + 1); break; } c1 += 1; c2 = strchr(c1, ']'); if (c2 == NULL) { snprintf(err_str, ERROR_STRING_SIZE, "']' missing in config file " "%s\n", options->conffile); fdasd_error(anc, config_syntax_error, err_str); } strcpy(c2, ""); memset(token, 0, sizeof(token)); if (gettoken(c1, ",", token, CONFIG_MAX) < 2) { snprintf(err_str, ERROR_STRING_SIZE, "Missing parameter in config file " "%s\n", options->conffile); fdasd_error(anc, config_syntax_error, err_str); } if (strstr(token[0], "FIRST") != NULL) { anc->confdata[i].start = FIRST_USABLE_TRK; } else { errno = 0; anc->confdata[i].start = strtol(token[0], (char **)NULL, 10); if (errno != 0 || anc->confdata[i].start == 0) { snprintf(err_str, ERROR_STRING_SIZE, "invalid partition start in config " "file %s\n", options->conffile); fdasd_error(anc, config_syntax_error, err_str); } } if (strstr(token[1], "LAST") != NULL) { anc->confdata[i].stop = anc->formatted_cylinders * geo.heads - 1; } else { errno = 0; anc->confdata[i].stop = strtol(token[1], (char **)NULL, 10); if (errno != 0 || anc->confdata[i].stop == 0) { snprintf(err_str, ERROR_STRING_SIZE, "invalid partition end in config " "file %s\n", options->conffile); fdasd_error(anc, config_syntax_error, err_str); } } if (get_part_type_by_dsname(token[2], &anc->confdata[i].type)) { snprintf(err_str, ERROR_STRING_SIZE, "invalid partition type in config file %s\n", options->conffile); fdasd_error(anc, config_syntax_error, err_str); } c1 = c2 + 1; } return 0; } /* * checks input from config file */ static void fdasd_check_conffile_input(fdasd_anchor_t *anc, struct fdasd_options *options) { unsigned long start, stop, first_trk, last_trk; partition_info_t *part_info = anc->first; char err_str[ERROR_STRING_SIZE]; int i; if (anc->verbose) printf("checking config file data...\n"); for (i = 0; i < USABLE_PARTITIONS; i++) { start = anc->confdata[i].start; stop = anc->confdata[i].stop; if (start == 0 || stop == 0) break; first_trk = FIRST_USABLE_TRK; last_trk = anc->formatted_cylinders * geo.heads - 1; if (start < first_trk || start > last_trk) { snprintf(err_str, ERROR_STRING_SIZE, "One of the lower partition limits " "(%ld) is not within the range of\n" "available tracks on disk (%ld-%ld)!\n", start, first_trk, last_trk); fdasd_error(anc, config_syntax_error, err_str); } if (stop < first_trk || stop > last_trk) { snprintf(err_str, ERROR_STRING_SIZE, "One of the upper partition limits " "(%ld) is not within the range of\n " "available tracks on disk (%ld-%ld)!\n", stop, first_trk, last_trk); fdasd_error(anc, config_syntax_error, err_str); } if (start >= stop) { snprintf(err_str, ERROR_STRING_SIZE, "Lower partition limit (%ld) is not " "less than upper partition\nlimit (%ld) " "in config file %s!\n", start, stop, options->conffile); fdasd_error(anc, config_syntax_error, err_str); } if (i > 0 && start <= anc->confdata[i - 1].stop) { snprintf(err_str, ERROR_STRING_SIZE, "Partitions overlap or are not in " "ascending order!\n"); fdasd_error(anc, config_syntax_error, err_str); } if (i < (USABLE_PARTITIONS - 1) && anc->confdata[i + 1].start > 0 && stop >= anc->confdata[i + 1].start) { snprintf(err_str, ERROR_STRING_SIZE, "Partitions overlap or are not in " "ascending order!\n"); fdasd_error(anc, config_syntax_error, err_str); } part_info->used = 0x01; part_info->start_trk = start; part_info->end_trk = stop; part_info->len_trk = stop - start + 1; part_info->type = anc->confdata[i].type; /* update the current free space counter */ if (i == 0) anc->fspace_trk = start - FIRST_USABLE_TRK; if (i < USABLE_PARTITIONS - 1) { if (anc->confdata[i + 1].start != 0) part_info->fspace_trk = anc->confdata[i + 1].start - stop - 1; else part_info->fspace_trk = last_trk - stop; } else if (i == USABLE_PARTITIONS - 1) { part_info->fspace_trk = last_trk - stop; } part_info = part_info->next; } } /* * Verifies the specified block device. */ static void fdasd_verify_device(fdasd_anchor_t *anc, char *name) { char err_str[ERROR_STRING_SIZE]; struct stat dst; int count; if (stat(name, &dst) < 0) { snprintf(err_str, ERROR_STRING_SIZE, "Unable to get device status for device '%s'\n", name); fdasd_error(anc, device_verification_failed, err_str); } if (!S_ISBLK(dst.st_mode)) { snprintf(err_str, ERROR_STRING_SIZE, "Device '%s' (%d/%d) is not a block device\n", name, (unsigned short)major(dst.st_rdev), (unsigned short)minor(dst.st_rdev)); fdasd_error(anc, device_verification_failed, err_str); } if (!anc->force_virtual && minor(dst.st_rdev) & PARTN_MASK) { snprintf(err_str, ERROR_STRING_SIZE, "Partition '%s' (%d/%d) detected where device is " "required\n", name, (unsigned short)major(dst.st_rdev), (unsigned short)minor(dst.st_rdev)); fdasd_error(anc, device_verification_failed, err_str); } if (dasd_sys_raw_track_access(name)) { snprintf(err_str, ERROR_STRING_SIZE, "Device '%s' is in raw-track access mode\n", name); fdasd_error(anc, device_verification_failed, err_str); } count = dasd_get_host_access_count(name); if (anc->force_host) { if (count > 1) { snprintf(err_str, ERROR_STRING_SIZE, "Disk %s is online on operating system instances in %d different LPARs.\n" "Note: Your installation might include z/VM systems that are configured to\n" "automatically vary on disks, regardless of whether they are subsequently used.\n", name, count); fdasd_error(anc, device_verification_failed, err_str); } else if (count < 0) { snprintf(err_str, ERROR_STRING_SIZE, "Hosts access information not available for disk %s.\n", name); fdasd_error(anc, device_verification_failed, err_str); } } else if (count > 1) { printf("\nWARNING:\n" "Disk %s is online on operating system instances in %d different LPARs.\n" "Ensure that the disk is not being used by a system outside your LPAR.\n" "Note: Your installation might include z/VM systems that are configured to\n" "automatically vary on disks, regardless of whether they are subsequently used.\n\n", name, count); } if (anc->verbose) { printf("Verification successful for '%s' (%d/%d)\n", name, (unsigned short)major(dst.st_rdev), (unsigned short)minor(dst.st_rdev)); } } /* * Verifies the specified fdasd command line option * combinations. * * Note: * - 'version' and 'help' are priority options. * All other parameters are ignored in that case. * - 'silent' and 'verbose' are allways allowed in any * combination. * */ static void fdasd_verify_options(fdasd_anchor_t *anc) { /* Checked option combinations */ /* (inv = invalid / req = required / opt = optional) */ /* */ /* vols labe keep auto conf tabl */ /* er l _vol if e */ /* ser */ /* */ /* volser - inv INV inv inv inv */ /* label - inv REQ REQ inv */ /* keep_volser - REQ REQ inv */ /* auto opt opt - inv inv */ /* config opt opt - inv */ /* table - */ if (anc->print_volser && (options.volser || anc->keep_volser || anc->auto_partition || options.conffile || anc->print_table)) { fdasd_error(anc, parser_failed, "Option 'volser' cannot be used with other" " options.\n"); } if (options.volser) { if (!anc->auto_partition && !options.conffile) { fdasd_error(anc, parser_failed, "Option 'auto' or 'config' required when" " specifying 'label'\n"); } if (anc->keep_volser || anc->print_table) { fdasd_error(anc, parser_failed, "Option 'label' cannot be used with " "'keep_volser' and 'table'.\n"); } } if (anc->keep_volser) { if (!anc->auto_partition && !options.conffile) { fdasd_error(anc, parser_failed, "Option 'auto' or 'config' required when" " specifying 'keep_volser'\n"); } if (anc->print_table) { fdasd_error(anc, parser_failed, "Option 'keep_volser' cannot be used" " with 'table'.\n"); } } if (anc->auto_partition && (options.conffile || anc->print_table)) { fdasd_error(anc, parser_failed, "Option 'auto' cannot be used with " "'config' and 'table'.\n"); } if (options.conffile && anc->print_table) { fdasd_error(anc, parser_failed, "Option 'config' cannot be used with" " 'table'.\n"); } } /* * print mapping: partition number - data set name */ static void fdasd_show_mapping(fdasd_anchor_t *anc) { char str[20], *dev, dsname[45], *strp; partition_info_t *part_info; int i = 0, j = 0, dev_len; printf("\ndevice .........: %s\n", options.device); bzero(str, sizeof(str)); vtoc_volume_label_get_label(anc->vlabel, str); printf("volume label ...: %.4s\n", str); bzero(str, sizeof(str)); vtoc_volume_label_get_volser(anc->vlabel, str); printf("volume serial ..: %s\n\n", str); dev_len = strlen(options.device); dev = malloc(dev_len + 10); if (!dev) fdasd_error(anc, malloc_failed, "Show mapping: memory allocation failed."); strcpy(dev, options.device); strp = strstr(dev, DISC); if (strp == NULL) strp = strstr(dev, DEVICE); if (strp != NULL) strcpy(strp, PART); printf("WARNING: This mapping may be NOT up-to-date,\n" " if you have NOT saved your last changes!\n\n"); for (part_info = anc->first; part_info != NULL; part_info = part_info->next) { i++; if (part_info->used != 0x01) continue; bzero(dsname, sizeof(dsname)); strncpy(dsname, part_info->f1->DS1DSNAM, 44); vtoc_ebcdic_dec(dsname, dsname, 44); if (getdsn(anc, i - 1) < 0) sprintf(dsname, "new data set"); printf("%s%-2d - %-44s\n", dev, i, dsname); j++; } if (j == 0) printf("No partitions defined.\n"); free(dev); } /* * prints only the volume serial */ static void fdasd_print_volser(fdasd_anchor_t *anc) { char volser[VOLSER_LENGTH + 1]; bzero(volser, VOLSER_LENGTH); vtoc_ebcdic_dec(anc->vlabel->volid, volser, VOLSER_LENGTH); printf("%6.6s\n", volser); } /* * print partition table */ static void fdasd_list_partition_table(fdasd_anchor_t *anc) { int i = 0, dev_len = strlen(options.device); char str[20], *dev, *strp, *ch; partition_info_t *part_info; if (!anc->silent) { printf("\nDisk %s:\n" " cylinders ............: %d\n" " tracks per cylinder ..: %d\n" " blocks per track .....: %d\n" " bytes per block ......: %d\n", options.device, anc->formatted_cylinders, geo.heads, geo.sectors, anc->blksize); vtoc_volume_label_get_label(anc->vlabel, str); printf(" volume label .........: %s\n", str); vtoc_volume_label_get_volser(anc->vlabel, str); printf(" volume serial ........: %s\n", str); printf(" max partitions .......: %d\n\n", USABLE_PARTITIONS); } if (dev_len < 20) dev_len = 20; if (!anc->silent) { printf(" ------------------------------- tracks" " -------------------------------\n"); printf("%*s start end length Id System\n", dev_len + 1, "Device"); } dev = malloc(dev_len + 10); if (!dev) fdasd_error(anc, malloc_failed, "Print partition table: memory allocation failed."); strcpy(dev, options.device); strp = strstr(dev, DISC); if (strp == NULL) strp = strstr(dev, DEVICE); if (strp != NULL) strcpy(strp, PART); for (part_info = anc->first; part_info != NULL; part_info = part_info->next) { i++; if (part_info == anc->first && anc->fspace_trk > 0) { printf("%*s %9ld%9ld%9ld unused\n", dev_len, "", (unsigned long)FIRST_USABLE_TRK, (unsigned long)FIRST_USABLE_TRK + anc->fspace_trk - 1, anc->fspace_trk); } if (part_info->used != 0x01) continue; vtoc_ebcdic_dec(part_info->f1->DS1DSNAM, part_info->f1->DS1DSNAM, 44); ch = strstr(part_info->f1->DS1DSNAM, "PART"); if (ch != NULL) { strncpy(str, ch + 9, 6); str[6] = '\0'; } else { strcpy(str, "error"); } vtoc_ebcdic_enc(part_info->f1->DS1DSNAM, part_info->f1->DS1DSNAM, 44); printf("%*s%-2d %9ld%9ld%9ld %2x %6s\n", dev_len, dev, i, part_info->start_trk, part_info->end_trk, part_info->len_trk, i, fdasd_partition_type(str)); if (part_info->fspace_trk > 0) printf("%*s %9ld%9ld%9ld unused\n", dev_len, "", part_info->end_trk + 1, part_info->end_trk + part_info->fspace_trk, part_info->fspace_trk); } free(dev); } /* * List all supported partition types. */ static void fdasd_list_known_partitions(void) { unsigned int i; for (i = VALID_PARTITION_OFFSET; i < ARRAY_SIZE(partition_types); i++) { printf("%3d %s\n", partition_types[i].type, partition_types[i].name); } } /* * get volser from vtoc */ static int fdasd_get_volser(fdasd_anchor_t *anc, char *volser) { volume_label_t vlabel; vtoc_read_volume_label(options.device, anc->label_pos, &vlabel); vtoc_volume_label_get_volser(&vlabel, volser); return 0; } /* * call IOCTL to re-read the partition table */ static void fdasd_reread_partition_table(fdasd_anchor_t *anc) { if (!anc->silent) printf("rereading partition table...\n"); if (dasd_reread_partition_table(options.device, 5) != 0) { fdasd_error(anc, unable_to_ioctl, "Error while rereading " "partition table.\nPlease reboot!"); } } /* * writes all changes to dasd */ static void fdasd_write_vtoc_labels(fdasd_anchor_t *anc) { char dsno[6], volser[VOLSER_LENGTH + 1], s2[45], *c1, *c2, *ch; partition_info_t *part_info; unsigned long blk, maxblk; format1_label_t emptyf1; char *dsname = NULL; cchhb_t f9addr; int i = 0, k = 0; if (!anc->silent) printf("writing VTOC...\n"); if (anc->verbose) printf("DSCBs: "); blk = (cchhb2blk(&anc->vlabel->vtoc, &geo) - 1) * anc->blksize; if (cchhb2blk(&anc->vlabel->vtoc, &geo) == 0 || blk == 0) fdasd_error(anc, vlabel_corrupted, ""); maxblk = blk + anc->blksize * 9; /* f4+f5+f7+3*f8+3*f9 */ /* write FMT4 DSCB */ vtoc_write_label(options.device, blk, NULL, anc->f4, NULL, NULL, NULL); if (anc->verbose) printf("f4 "); blk += anc->blksize; /* write FMT5 DSCB */ vtoc_write_label(options.device, blk, NULL, NULL, anc->f5, NULL, NULL); if (anc->verbose) printf("f5 "); blk += anc->blksize; /* write FMT7 DSCB */ if (anc->big_disk) { vtoc_write_label(options.device, blk, NULL, NULL, NULL, anc->f7, NULL); if (anc->verbose) printf("f7 "); blk += anc->blksize; } /* loop over all partitions (format 1 or format 8 DCB) */ for (part_info = anc->first; part_info != NULL; part_info = part_info->next) { if (part_info->used != 0x01) continue; i++; strncpy((char *)part_info->f1->DS1DSSN, anc->vlabel->volid, VOLSER_LENGTH); ch = part_info->f1->DS1DSNAM; vtoc_ebcdic_dec(ch, ch, 44); c1 = ch + 7; if (getdsn(anc, i - 1) > -1) { /* re-use the existing data set name */ c2 = strchr(c1, '.'); if (c2 != NULL) strncpy(s2, c2, 31); else fdasd_error(anc, dsname_corrupted, ""); strncpy(volser, anc->vlabel->volid, VOLSER_LENGTH); vtoc_ebcdic_dec(volser, volser, VOLSER_LENGTH); volser[VOLSER_LENGTH] = ' '; strncpy(c1, volser, VOLSER_LENGTH + 1); c1 = strchr(ch, ' '); memcpy(c1, s2, 31); } else { if (get_part_type_by_dsname(ch, &part_info->type)) part_info->type = PARTITION_NATIVE; /* create a new data set name */ while (getpos(anc, k) > -1) k++; setpos(anc, k, i - 1); memcpy(ch, "LINUX.V " " ", 44); strncpy(volser, anc->vlabel->volid, VOLSER_LENGTH); vtoc_ebcdic_dec(volser, volser, VOLSER_LENGTH); memcpy(c1, volser, VOLSER_LENGTH); c1 = strchr(ch, ' '); memcpy(c1, ".PART", 5); c1 += 5; sprintf(dsno, "%04d.", k + 1); memcpy(c1, dsno, 5); c1 += 5; get_part_dsname_by_type(part_info->type, &dsname); memcpy(c1, dsname, strlen(dsname)); /* We don't want \0 */ } vtoc_ebcdic_enc(ch, ch, 44); if (anc->verbose) printf("%2x ", part_info->f1->DS1FMTID); if (part_info->f1->DS1FMTID == 0xf8) { /* Now as we know where which label will be written, we * can add the address of the format 9 label to the * format 8 label. The f9 record will be written to the * block after the current blk. Remember: records are of * by one, so we have to add 2 and not just one. */ vtoc_set_cchhb(&f9addr, VTOC_START_CC, VTOC_START_HH, ((blk / anc->blksize) % geo.sectors) + 2); vtoc_update_format8_label(&f9addr, part_info->f1); vtoc_write_label(options.device, blk, part_info->f1, NULL, NULL, NULL, NULL); blk += anc->blksize; vtoc_write_label(options.device, blk, NULL, NULL, NULL, NULL, anc->f9); if (anc->verbose) printf("f9 "); blk += anc->blksize; } else { vtoc_write_label(options.device, blk, part_info->f1, NULL, NULL, NULL, NULL); blk += anc->blksize; } } /* write empty labels to the rest of the blocks */ bzero(&emptyf1, sizeof(emptyf1)); while (blk < maxblk) { vtoc_write_label(options.device, blk, &emptyf1, NULL, NULL, NULL, NULL); if (anc->verbose) printf("empty "); blk += anc->blksize; } if (anc->verbose) printf("\n"); } /* * writes all changes to dasd */ static void fdasd_write_labels(fdasd_anchor_t *anc) { if (anc->vlabel_changed) { if (!anc->silent) printf("writing volume label...\n"); vtoc_write_volume_label(options.device, anc->label_pos, anc->vlabel); } if (anc->vtoc_changed) fdasd_write_vtoc_labels(anc); if (anc->vtoc_changed || anc->vlabel_changed) fdasd_reread_partition_table(anc); } /* * re-creates the VTOC and deletes all partitions */ static void fdasd_recreate_vtoc_unconditional(fdasd_anchor_t *anc) { partition_info_t *part_info = anc->first; int i; vtoc_init_format4_label(anc->f4, geo.cylinders, anc->formatted_cylinders, geo.heads, geo.sectors, anc->blksize, anc->dev_type); vtoc_init_format5_label(anc->f5); vtoc_init_format7_label(anc->f7); vtoc_set_freespace(anc->f4, anc->f5, anc->f7, '+', anc->verbose, FIRST_USABLE_TRK, anc->formatted_cylinders * geo.heads - 1, anc->formatted_cylinders, geo.heads); while (part_info != NULL) { bzero(part_info->f1, sizeof(format1_label_t)); if (part_info->used == 0x01) { part_info->used = 0x00; part_info->start_trk = 0; part_info->end_trk = 0; part_info->len_trk = 0; part_info->fspace_trk = 0; } part_info = part_info->next; } anc->used_partitions = 0; anc->fspace_trk = anc->formatted_cylinders * geo.heads - FIRST_USABLE_TRK; for (i = 0; i < USABLE_PARTITIONS; i++) setpos(anc, i, -1); anc->vtoc_changed++; } /* * asks user for confirmation before recreating the vtoc */ static void fdasd_recreate_vtoc(fdasd_anchor_t *anc) { char str[INPUT_BUF_SIZE]; if (!anc->silent) { snprintf(str, INPUT_BUF_SIZE, "WARNING: All partitions on device '%s' will be " "deleted!\nDo you want to continue?", options.device); if (yes_no(str) != 0) return; printf("creating new VTOC... "); } fdasd_recreate_vtoc_unconditional(anc); if (!anc->silent) printf("ok\n"); } /* * re-create all VTOC labels, but use the partition information * from existing VTOC */ static void fdasd_reuse_vtoc(fdasd_anchor_t *anc) { partition_info_t *part_info = anc->first; char str[INPUT_BUF_SIZE]; format1_label_t f1; format4_label_t f4; format5_label_t f5; format7_label_t f7; if (!anc->silent) { snprintf(str, INPUT_BUF_SIZE, "WARNING: this will re-create your VTOC " "entries using the partition\n " "information of your existing VTOC. Continue?"); if (yes_no(str) != 0) return; } if (!anc->silent) printf("re-creating VTOC... "); vtoc_init_format4_label(&f4, geo.cylinders, anc->formatted_cylinders, geo.heads, geo.sectors, anc->blksize, anc->dev_type); /* reuse some FMT4 values */ f4.DS4HPCHR = anc->f4->DS4HPCHR; f4.DS4DSREC = anc->f4->DS4DSREC; /* re-initialize both free-space labels */ vtoc_init_format5_label(&f5); vtoc_init_format7_label(&f7); if (anc->fspace_trk > 0) { vtoc_set_freespace(&f4, &f5, &f7, '+', anc->verbose, FIRST_USABLE_TRK, FIRST_USABLE_TRK + anc->fspace_trk - 1, anc->formatted_cylinders, geo.heads); } while (part_info != NULL) { if (part_info->used != 0x01) { part_info = part_info->next; continue; } if (anc->formatted_cylinders > LV_COMPAT_CYL) vtoc_init_format8_label(anc->blksize, &part_info->f1->DS1EXT1, &f1); else vtoc_init_format1_label(anc->blksize, &part_info->f1->DS1EXT1, &f1); strncpy(f1.DS1DSNAM, part_info->f1->DS1DSNAM, 44); strncpy((char *)f1.DS1DSSN, (char *)part_info->f1->DS1DSSN, 6); f1.DS1CREDT = part_info->f1->DS1CREDT; memcpy(part_info->f1, &f1, sizeof(format1_label_t)); if (part_info->fspace_trk > 0) { vtoc_set_freespace(&f4, &f5, &f7, '+', anc->verbose, part_info->end_trk + 1, part_info->end_trk + part_info->fspace_trk, anc->formatted_cylinders, geo.heads); } part_info = part_info->next; } /* over-write old labels with new ones */ memcpy(anc->f4, &f4, sizeof(format4_label_t)); memcpy(anc->f5, &f5, sizeof(format5_label_t)); memcpy(anc->f7, &f7, sizeof(format7_label_t)); if (!anc->silent) printf("ok\n"); anc->vtoc_changed++; } /* * Changes the volume serial (menu option) */ static void fdasd_change_volser(fdasd_anchor_t *anc) { char volser[VOLSER_LENGTH + 1]; vtoc_volume_label_get_volser(anc->vlabel, volser); printf("Please specify new volume serial (6 characters).\n"); printf("current : %-6.6s\nnew [0X%04x]: ", volser, anc->devno); read_line(); fdasd_check_volser(line_ptr, anc->devno); printf("\nvolume identifier changed to '%-6s'\n", line_ptr); vtoc_volume_label_set_volser(anc->vlabel, line_ptr); vtoc_set_cchhb(&anc->vlabel->vtoc, VTOC_START_CC, VTOC_START_HH, 0x01); anc->vlabel_changed++; anc->vtoc_changed++; } /* * changes the partition type */ static void fdasd_change_part_type(fdasd_anchor_t *anc) { unsigned int part_id, part_type, i; partition_info_t *part_info; char *dsname = NULL; char str[20], *ch; fdasd_list_partition_table(anc); /* ask for partition number */ printf("\nchange partition type\n"); while (!isdigit(part_id = read_char("partition id (use 0 to exit): "))) printf("Invalid partition id '%c' detected.\n", part_id); part_id -= 48; printf("\n"); if (part_id == 0) return; if (part_id > anc->used_partitions) { printf("'%d' is not a valid partition id!\n", part_id); return; } part_info = anc->first; for (i = 1; i < part_id; i++) part_info = part_info->next; /* ask for partition type */ vtoc_ebcdic_dec(part_info->f1->DS1DSNAM, part_info->f1->DS1DSNAM, 44); ch = strstr(part_info->f1->DS1DSNAM, "PART") + 9; if (ch != NULL) { strncpy(str, ch, 6); str[6] = '\0'; } else { strcpy(str, "error"); } printf("current partition type is: %s\n\n", fdasd_partition_type(str)); fdasd_list_known_partitions(); printf("\n"); part_type = 0; while (part_type < 1 || part_type > ARRAY_SIZE(partition_types) - 1) { do { part_type = read_char("new partition type: "); } while (!isdigit(part_type)); part_type -= 48; } if (get_part_dsname_by_type(part_type, &dsname)) printf("'%d' is not supported!\n", part_type); else snprintf(str, 7, "%-6s", dsname); ch = strstr(part_info->f1->DS1DSNAM, "PART") + 9; if (ch != NULL) memcpy(ch, str, 6); vtoc_ebcdic_enc(part_info->f1->DS1DSNAM, part_info->f1->DS1DSNAM, 44); anc->vtoc_changed++; } /* * initialize the VOL1 volume label */ static void fdasd_init_volume_label(fdasd_anchor_t *anc) { volume_label_t *vlabel = anc->vlabel; char volser[VOLSER_LENGTH + 1]; vtoc_volume_label_init(vlabel); vtoc_volume_label_set_key(vlabel, "VOL1"); vtoc_volume_label_set_label(vlabel, "VOL1"); if (anc->keep_volser) { if (fdasd_get_volser(anc, volser) == 0) vtoc_volume_label_set_volser(vlabel, volser); else fdasd_error(anc, volser_not_found, options.device); } else if (options.volser) { fdasd_check_volser(options.volser, anc->devno); vtoc_volume_label_set_volser(vlabel, options.volser); } else if (anc->auto_partition || options.conffile) { sprintf(volser, "0X%04x", anc->devno); vtoc_volume_label_set_volser(vlabel, volser); } else { printf("\nPlease specify volume serial (6 characters)" "[0X%04x]: ", anc->devno); read_line(); fdasd_check_volser(line_ptr, anc->devno); vtoc_volume_label_set_volser(vlabel, line_ptr); } vtoc_set_cchhb(&vlabel->vtoc, VTOC_START_CC, VTOC_START_HH, 0x01); anc->vlabel_changed++; } /* * sets some important partition data * (like used, start_trk, end_trk, len_trk) * by calculating these values with the * information provided in the labels */ static void fdasd_update_partition_info(fdasd_anchor_t *anc) { partition_info_t *prev_part_info = NULL, *part_info = anc->first; unsigned long max = anc->formatted_cylinders * geo.heads - 1; int i; anc->used_partitions = geo.sectors - 2 - anc->f4->DS4DSREC; for (i = 1; i <= USABLE_PARTITIONS; i++) { if (part_info->f1->DS1FMTID != 0xf1 && part_info->f1->DS1FMTID != 0xf8) { if (i == 1) /* there is no partition at all */ anc->fspace_trk = max - FIRST_USABLE_TRK + 1; else /* previous partition was the last one */ prev_part_info->fspace_trk = max - prev_part_info->end_trk; break; } /* this is a valid format 1 label */ part_info->used = 0x01; part_info->start_trk = cchh2trk(&part_info->f1->DS1EXT1.llimit, &geo); part_info->end_trk = cchh2trk(&part_info->f1->DS1EXT1.ulimit, &geo); part_info->len_trk = part_info->end_trk - part_info->start_trk + 1; if (i == 1) { /* first partition, there is at least one */ anc->fspace_trk = part_info->start_trk - FIRST_USABLE_TRK; } else { if (i == USABLE_PARTITIONS) /* last possible partition */ part_info->fspace_trk = max - part_info->end_trk; /* set free space values of previous partition */ prev_part_info->fspace_trk = part_info->start_trk - prev_part_info->end_trk - 1; } prev_part_info = part_info; part_info = part_info->next; } } /* * reorganizes all FMT1s, move all empty labels to the end */ static void fdasd_reorganize_FMT1s(fdasd_anchor_t *anc) { partition_info_t *part_info; format1_label_t *f1_label; int i, j; for (i = 1; i <= USABLE_PARTITIONS - 1; i++) { part_info = anc->first; for (j = 1; j <= USABLE_PARTITIONS - i; j++) { if (part_info->f1->DS1FMTID < part_info->next->f1->DS1FMTID) { f1_label = part_info->f1; part_info->f1 = part_info->next->f1; part_info->next->f1 = f1_label; } part_info = part_info->next; } } } /* * we have a invalid FMT4 DSCB and therefore we will re-create the VTOC */ static void fdasd_process_invalid_vtoc(fdasd_anchor_t *anc) { printf(" invalid\ncreating new VTOC...\n"); if (anc->hw_cylinders > LV_COMPAT_CYL) { printf("Warning: Device has more then %u cylinders!\n", LV_COMPAT_CYL); if (yes_no("Are you sure it was completely" " formatted with dasdfmt?") == 1) { if (!anc->silent) printf("exiting...\n"); fdasd_exit(anc, 0); } } anc->formatted_cylinders = anc->hw_cylinders; anc->fspace_trk = anc->formatted_cylinders * geo.heads - FIRST_USABLE_TRK; vtoc_init_format4_label(anc->f4, geo.cylinders, anc->formatted_cylinders, geo.heads, geo.sectors, anc->blksize, anc->dev_type); vtoc_init_format5_label(anc->f5); vtoc_init_format7_label(anc->f7); vtoc_set_freespace(anc->f4, anc->f5, anc->f7, '+', anc->verbose, FIRST_USABLE_TRK, anc->formatted_cylinders * geo.heads - 1, anc->formatted_cylinders, geo.heads); anc->vtoc_changed++; } /* * */ static void fdasd_process_valid_vtoc(fdasd_anchor_t *anc, unsigned long blk) { int f1_counter = 0, f7_counter = 0, f5_counter = 0; int i, part_no, f1_size = sizeof(format1_label_t); partition_info_t *part_info = anc->first; char part_no_str[5], *part_pos; format1_label_t f1_label; if (!anc->silent) printf(" ok\n"); if (anc->f4->DS4DEVCT.DS4DSCYL == LV_COMPAT_CYL && anc->f4->DS4DCYL > anc->f4->DS4DEVCT.DS4DSCYL) anc->formatted_cylinders = anc->f4->DS4DCYL; else anc->formatted_cylinders = anc->f4->DS4DEVCT.DS4DSCYL; anc->fspace_trk = anc->formatted_cylinders * geo.heads - FIRST_USABLE_TRK; /* skip f4 label, already read before */ blk += anc->blksize; if (anc->formatted_cylinders < anc->hw_cylinders) printf("WARNING: This device is not fully formatted! " "Only %u of %u cylinders are available.\n", anc->formatted_cylinders, anc->hw_cylinders); if (anc->verbose) printf("VTOC DSCBs : "); /* go through remaining labels, f4 label already done */ for (i = 1; i < geo.sectors; i++) { bzero(&f1_label, f1_size); vtoc_read_label(options.device, blk, &f1_label, NULL, NULL, NULL); switch (f1_label.DS1FMTID) { case 0xf1: case 0xf8: if (anc->verbose) printf("%s ", f1_label.DS1FMTID == 0xf1 ? "f1" : "f8"); if (part_info == NULL) break; memcpy(part_info->f1, &f1_label, f1_size); part_no = -1; vtoc_ebcdic_dec(part_info->f1->DS1DSNAM, part_info->f1->DS1DSNAM, 44); part_pos = strstr(part_info->f1->DS1DSNAM, "PART"); if (part_pos != NULL) { strncpy(part_no_str, part_pos + 4, 4); part_no_str[4] = '\0'; part_no = atoi(part_no_str) - 1; } vtoc_ebcdic_enc(part_info->f1->DS1DSNAM, part_info->f1->DS1DSNAM, 44); if (part_no < 0 || part_no >= USABLE_PARTITIONS) printf("WARNING: partition number (%i) found " "in data set name of an existing " "partition\ndoes not match range of " "possible partition numbers (1-%d)\n\n", part_no + 1, USABLE_PARTITIONS); else setpos(anc, part_no, f1_counter); part_info = part_info->next; f1_counter++; break; case 0xf5: if (anc->verbose) printf("f5 "); memcpy(anc->f5, &f1_label, f1_size); f5_counter++; break; case 0xf7: if (anc->verbose) printf("f7 "); if (f7_counter == 0) memcpy(anc->f7, &f1_label, f1_size); f7_counter++; break; case 0xf9: /* each format 8 lable has an associated format 9 lable, * but they are of no further use to us. */ if (anc->verbose) printf("f9 "); break; default: if (f1_label.DS1FMTID > 0) printf("'%d' is not supported!\n", f1_label.DS1FMTID); } blk += anc->blksize; } if (anc->verbose) printf("\n"); if (f5_counter == 0 || anc->big_disk) vtoc_init_format5_label(anc->f5); if (f7_counter == 0) vtoc_init_format7_label(anc->f7); fdasd_reorganize_FMT1s(anc); fdasd_update_partition_info(anc); } /* * we have a valid VTOC pointer, let's go and read the VTOC labels */ static int fdasd_valid_vtoc_pointer(fdasd_anchor_t *anc, unsigned long blk) { /* VOL1 label contains valid VTOC pointer */ if (!anc->silent) printf("reading vtoc ..........:"); vtoc_read_label(options.device, blk, NULL, anc->f4, NULL, NULL); if (anc->f4->DS4IDFMT != 0xf4) { if (anc->print_table) { printf("Your VTOC is corrupted!\n"); return -1; } fdasd_process_invalid_vtoc(anc); } else { fdasd_process_valid_vtoc(anc, blk); } return 0; } /* * */ static void fdasd_invalid_vtoc_pointer(fdasd_anchor_t *anc) { /* VOL1 label doesn't contain valid VTOC pointer */ if (yes_no("There is no VTOC yet, should I create one?") == 1) { if (!anc->silent) printf("exiting...\n"); fdasd_exit(anc, 0); } if (anc->hw_cylinders > LV_COMPAT_CYL) { printf("Warning: Device has more then %u cylinders!\n", LV_COMPAT_CYL); if (yes_no("Are you sure it was completely" " formatted with dasdfmt?") == 1) { if (!anc->silent) printf("exiting...\n"); fdasd_exit(anc, 0); } } anc->formatted_cylinders = anc->hw_cylinders; anc->fspace_trk = anc->formatted_cylinders * geo.heads - FIRST_USABLE_TRK; vtoc_init_format4_label(anc->f4, geo.cylinders, anc->formatted_cylinders, geo.heads, geo.sectors, anc->blksize, anc->dev_type); vtoc_init_format5_label(anc->f5); vtoc_init_format7_label(anc->f7); vtoc_set_freespace(anc->f4, anc->f5, anc->f7, '+', anc->verbose, FIRST_USABLE_TRK, anc->formatted_cylinders * geo.heads - 1, anc->formatted_cylinders, geo.heads); vtoc_set_cchhb(&anc->vlabel->vtoc, VTOC_START_CC, VTOC_START_HH, 0x01); anc->vtoc_changed++; anc->vlabel_changed++; } /* * check the dasd for a volume label */ static int fdasd_check_volume(fdasd_anchor_t *anc) { volume_label_t *vlabel = anc->vlabel; char inp_buf[INPUT_BUF_SIZE]; char str[LINE_LENGTH]; long long blk = -1; int rc = 1; if (!anc->silent) printf("reading volume label ..:"); vtoc_read_volume_label(options.device, anc->label_pos, vlabel); if (strncmp(vlabel->vollbl, vtoc_ebcdic_enc("VOL1", str, 4), 4) == 0) { /* found VOL1 volume label */ if (!anc->silent) printf(" VOL1\n"); blk = (cchhb2blk(&vlabel->vtoc, &geo) - 1) * anc->blksize; if (cchhb2blk(&vlabel->vtoc, &geo) > 0 && blk > 0) { rc = fdasd_valid_vtoc_pointer(anc, blk); if (anc->print_table && (rc < 0)) return -1; } else { if (anc->print_table) { printf("\nFound invalid VTOC pointer.\n"); return -1; } fdasd_invalid_vtoc_pointer(anc); } } else { /* didn't find VOL1 volume label */ if (anc->print_table || anc->print_volser) { printf("\nCannot show requested information because " "the disk label block is invalid\n"); return -1; } if (strncmp(vlabel->vollbl, vtoc_ebcdic_enc("LNX1", str, 4), 4) == 0) { if (!anc->silent) printf(" LNX1\n"); strcpy(inp_buf, "Overwrite inapplicable label?"); } else { if (!anc->silent) printf(" no known label\n"); if (!anc->auto_partition && !options.conffile) rc = yes_no("Should I create a new one?"); else rc = 0; } if (!anc->print_volser && !anc->print_table && rc == 1) { printf("Disc does not contain a VOL1 label, cannot " "create partitions.\nexiting...\n"); fdasd_exit(anc, -1); } if (anc->hw_cylinders > LV_COMPAT_CYL) { printf("Warning: Device has more than %u cylinders!\n", LV_COMPAT_CYL); if (!anc->auto_partition && !options.conffile && yes_no("Are you sure it was completely" " formatted with dasdfmt?") == 1) { if (!anc->silent) printf("exiting...\n"); fdasd_exit(anc, 0); } } anc->formatted_cylinders = anc->hw_cylinders; anc->fspace_trk = anc->formatted_cylinders * geo.heads - FIRST_USABLE_TRK; fdasd_init_volume_label(anc); vtoc_init_format4_label(anc->f4, geo.cylinders, anc->formatted_cylinders, geo.heads, geo.sectors, anc->blksize, anc->dev_type); vtoc_init_format5_label(anc->f5); vtoc_init_format7_label(anc->f7); vtoc_set_freespace(anc->f4, anc->f5, anc->f7, '+', anc->verbose, FIRST_USABLE_TRK, anc->formatted_cylinders * geo.heads - 1, anc->formatted_cylinders, geo.heads); anc->vtoc_changed++; } if (!anc->silent) printf("\n"); return 0; } /* * check disk access */ static void fdasd_check_disk_access(fdasd_anchor_t *anc) { char err_str[ERROR_STRING_SIZE]; format1_label_t f1; int fd, pos; bool ro; fd = open(options.device, O_RDONLY); if (fd == -1) { snprintf(err_str, ERROR_STRING_SIZE, "Could not open device '%s' " "in read-only mode!\n", options.device); fdasd_error(anc, unable_to_open_disk, err_str); } pos = anc->blksize * (2 * geo.heads - 1); /* last block in the second track */ if (lseek(fd, pos, SEEK_SET) == -1) { close(fd); snprintf(err_str, ERROR_STRING_SIZE, "Could not seek device '%s'.", options.device); fdasd_error(anc, unable_to_seek_disk, err_str); } if (read(fd, &f1, sizeof(format1_label_t)) != sizeof(format1_label_t)) { close(fd); snprintf(err_str, ERROR_STRING_SIZE, "Could not read from device '%s'.", options.device); fdasd_error(anc, unable_to_read_disk, err_str); } if (lseek(fd, pos, SEEK_SET) == -1) { close(fd); snprintf(err_str, ERROR_STRING_SIZE, "Could not seek device '%s'.", options.device); fdasd_error(anc, unable_to_seek_disk, err_str); } close(fd); if (dasd_is_ro(options.device, &ro) != 0) { snprintf(err_str, ERROR_STRING_SIZE, "Could not get read-only status for device '%s'.", options.device); fdasd_error(anc, unable_to_ioctl, err_str); } if (ro && !anc->print_volser && !anc->print_table) { printf("\nWARNING: Device '%s' is a read-only device!\n" "You will not be able to save any changes.\n\n", options.device); } } /* * The following two functions match those in the DASD ECKD device driver. * They are used to compute how many records of a given size can be stored * in one track. */ static unsigned int ceil_quot(unsigned int d1, unsigned int d2) { return (d1 + (d2 - 1)) / d2; } /* kl: key length, dl: data length */ static unsigned int recs_per_track(unsigned short dev_type, unsigned int kl, unsigned int dl) { int dn, kn; switch (dev_type) { case DASD_3380_TYPE: if (kl) return 1499 / (15 + 7 + ceil_quot(kl + 12, 32) + ceil_quot(dl + 12, 32)); else return 1499 / (15 + ceil_quot(dl + 12, 32)); case DASD_3390_TYPE: dn = ceil_quot(dl + 6, 232) + 1; if (kl) { kn = ceil_quot(kl + 6, 232) + 1; return 1729 / (10 + 9 + ceil_quot(kl + 6 * kn, 34) + 9 + ceil_quot(dl + 6 * dn, 34)); } else { return 1729 / (10 + 9 + ceil_quot(dl + 6 * dn, 34)); } case DASD_9345_TYPE: dn = ceil_quot(dl + 6, 232) + 1; if (kl) { kn = ceil_quot(kl + 6, 232) + 1; return 1420 / (18 + 7 + ceil_quot(kl + 6 * kn, 34) + ceil_quot(dl + 6 * dn, 34)); } else { return 1420 / (18 + 7 + ceil_quot(dl + 6 * dn, 34)); } } return 0; } /* * Verify that number of tracks (heads) per cylinder and number of * sectors per track match the expected values for a given device type * and block size. * Returns 1 for a valid match and 0 otherwise. */ static int fdasd_verify_geometry(unsigned short dev_type, int blksize, struct hd_geometry *geometry) { unsigned int expected_sectors; if (geometry->heads != 15) return 0; expected_sectors = recs_per_track(dev_type, 0, blksize); if (geometry->sectors == expected_sectors) return 1; return 0; } /* * reads dasd geometry data */ static void fdasd_get_geometry(fdasd_anchor_t *anc) { struct dasd_eckd_characteristics *characteristics; unsigned long long size_in_bytes; char err_str[ERROR_STRING_SIZE]; dasd_information2_t dasd_info; unsigned int blksize = 0; if (dasd_get_blocksize_in_bytes(options.device, &size_in_bytes) != 0) fdasd_error(anc, unable_to_ioctl, "Could not retrieve disk size."); /* * If anc->force_virtual is set, we do no real geometry detection. * anc->dev_type and anc->blksize have already been set via command * line parameter, and the rest of the geometry is now computed from * these values. */ if (anc->force_virtual) { geo.heads = 15; geo.sectors = recs_per_track(anc->dev_type, 0, anc->blksize); anc->hw_cylinders = size_in_bytes / (anc->blksize * geo.heads * geo.sectors); if (anc->hw_cylinders < LV_COMPAT_CYL) geo.cylinders = anc->hw_cylinders; else geo.cylinders = LV_COMPAT_CYL; geo.start = 0; anc->label_pos = 2 * anc->blksize; anc->devno = 0; if (anc->verbose) { printf("The force option is active. " "The following geometry will be used:\n" "device type %x, block size %d, cylinders %d," " heads %d, sectors %d\n", anc->dev_type, anc->blksize, anc->hw_cylinders, geo.heads, geo.sectors); } return; } if (dasd_get_geo(options.device, &geo) != 0) { fdasd_error(anc, unable_to_ioctl, "Could not retrieve disk geometry information."); } if (dasd_get_blocksize(options.device, &blksize) != 0) { fdasd_error(anc, unable_to_ioctl, "Could not retrieve blocksize information."); } /* get disk type */ if (dasd_get_info(options.device, &dasd_info) != 0) { if (anc->verbose) printf("BIODASDINFO ioctl failed," " use disk geometry only.\n"); /* verify that the geometry matches a 3390 DASD */ if (!fdasd_verify_geometry(DASD_3390_TYPE, blksize, &geo)) { fdasd_error(anc, wrong_disk_type, "Disk geometry does not match a DASD device" " of type 3390."); } anc->dev_type = DASD_3390_TYPE; anc->blksize = blksize; anc->hw_cylinders = size_in_bytes / (blksize * geo.heads * geo.sectors); /* The VOL1 label on a CDL formatted ECKD DASD is in block 2 * It will be verified later, if this position actually holds a * valid label record. */ anc->label_pos = 2 * blksize; /* A devno 0 is actually a valid devno, which could exist * in the system. Since we use this number only to create * a default volume serial, there is no serious conflict. */ anc->devno = 0; if (anc->verbose) { printf("The following device geometry will be used:\n" "device type %x, block size %d, cylinders %d," " heads %d, sectors %d\n", anc->dev_type, anc->blksize, anc->hw_cylinders, geo.heads, geo.sectors); } } else { characteristics = (struct dasd_eckd_characteristics *) &dasd_info.characteristics; if (characteristics->no_cyl == LV_COMPAT_CYL && characteristics->long_no_cyl) anc->hw_cylinders = characteristics->long_no_cyl; else anc->hw_cylinders = characteristics->no_cyl; if (strncmp(dasd_info.type, "ECKD", 4) != 0) { snprintf(err_str, ERROR_STRING_SIZE, "%s is not an ECKD disk! This disk type " "is not supported!", options.device); fdasd_error(anc, wrong_disk_type, err_str); } if (anc->verbose) printf("disk type check : ok\n"); if (dasd_info.FBA_layout != 0) { snprintf(err_str, ERROR_STRING_SIZE, "%s is not formatted with z/OS compatible " "disk layout!", options.device); fdasd_error(anc, wrong_disk_format, err_str); } if (anc->verbose) printf("disk layout check : ok\n"); if (dasd_info.open_count > 1) { if (anc->auto_partition) { snprintf(err_str, ERROR_STRING_SIZE, "DASD '%s' is in use. Unmount it first!", options.device); fdasd_error(anc, disk_in_use, err_str); } else { printf("\nWARNING: Your DASD '%s' is in use.\n" " If you proceed, you can " "heavily damage your system.\n" " If possible exit all" " applications using this disk\n" " and/or unmount it.\n\n", options.device); } } if (anc->verbose) printf("usage count check : ok\n"); anc->dev_type = dasd_info.dev_type; anc->blksize = blksize; anc->label_pos = dasd_info.label_block * blksize; anc->devno = dasd_info.devno; } } /* * asks for partition boundaries */ static unsigned long fdasd_read_int(unsigned long low, unsigned long dflt, unsigned long high, enum offset base, char *mesg, fdasd_anchor_t *anc) { unsigned int use_default = 1; unsigned long long trk = 0; char msg_txt[70]; switch (base) { case lower: sprintf(msg_txt, "%s ([%ld]-%ld): ", mesg, low, high); break; case upper: sprintf(msg_txt, "%s (%ld-[%ld]): ", mesg, low, high); break; default: sprintf(msg_txt, "%s (%ld-%ld): ", mesg, low, high); break; } while (1) { while (!isdigit(read_char(msg_txt)) && (*line_ptr != '-' && *line_ptr != '+' && *line_ptr != '\0')) continue; if ((*line_ptr == '+' || *line_ptr == '-') && base != lower) { if (*line_ptr == '+') ++line_ptr; trk = atoi(line_ptr); while (isdigit(*line_ptr)) { line_ptr++; use_default = 0; } switch (*line_ptr) { case 'c': case 'C': trk *= geo.heads; break; case 'k': case 'K': trk *= 1024; trk /= anc->blksize; trk /= geo.sectors; break; case 'm': case 'M': trk *= (1024 * 1024); trk /= anc->blksize; trk /= geo.sectors; break; case 'g': case 'G': trk *= (1024 * 1024 * 1024); trk /= anc->blksize; trk /= geo.sectors; break; case 0x0a: break; default: printf("WARNING: '%c' is not a " "valid appendix and probably " "not what you want!\n", *line_ptr); break; } trk += (low - 1); } else if (*line_ptr == '\0') { switch (base) { case lower: trk = low; break; case upper: trk = high; break; } } else { if (*line_ptr == '+' || *line_ptr == '-') { printf("\nWARNING: '%c' is not valid in\n" "this case and will be ignored!\n", *line_ptr); ++line_ptr; } trk = atoi(line_ptr); while (isdigit(*line_ptr)) { line_ptr++; use_default = 0; } if (*line_ptr != 0x0a) printf("\nWARNING: '%c' is not a valid " "appendix and probably not what " "you want!\n", *line_ptr); } if (use_default) printf("Using default value %lld\n", trk = dflt); else printf("You have selected track %lld\n", trk); if (trk >= low && trk <= high) break; else printf("Value out of range.\n"); } return trk; } /* * returns unused partition info pointer if there * is a free partition, otherwise NULL */ static partition_info_t *fdasd_get_empty_f1_label(fdasd_anchor_t *anc) { if (anc->used_partitions < USABLE_PARTITIONS) return anc->last; else return NULL; } /* * asks for and sets some important partition data */ static int fdasd_get_partition_data(fdasd_anchor_t *anc, extent_t *part_extent, partition_info_t *part_info) { unsigned long start, stop, limit; partition_info_t *part_tmp; cchh_t llimit, ulimit; u_int16_t hh, head; u_int32_t cc, cyl; u_int8_t b1, b2; char mesg[48]; start = FIRST_USABLE_TRK; cyl = get_usable_cylinders(anc); head = anc->f4->DS4DEVCT.DS4DSTRK; limit = (head * cyl - 1); sprintf(mesg, "First track (1 track = %d KByte)", geo.sectors * anc->blksize / 1024); /* find default start value */ for (part_tmp = anc->first; part_tmp->next != NULL; part_tmp = part_tmp->next) { if ((start >= part_tmp->start_trk) && (start <= part_tmp->end_trk)) start = part_tmp->end_trk + 1; } if (start > limit) { printf("Not that easy, no free tracks available.\n"); return -1; } /* read start value */ start = fdasd_read_int(start, start, limit, lower, mesg, anc); /* check start value from user */ for (part_tmp = anc->first; part_tmp->next != NULL; part_tmp = part_tmp->next) { if (start >= part_tmp->start_trk && start <= part_tmp->end_trk) { /* start is within another partition */ start = part_tmp->end_trk + 1; if (start > limit) { start = FIRST_USABLE_TRK; part_tmp = anc->first; } printf("value within another partition, " "using %ld instead\n", start); } if (start < part_tmp->start_trk) { limit = part_tmp->start_trk - 1; break; } } if (start == limit) { stop = start; } else { sprintf(mesg, "Last track or +size[c|k|m|g]"); stop = fdasd_read_int(start, limit, limit, upper, mesg, anc); } /* update partition info */ part_info->len_trk = stop - start + 1; part_info->start_trk = start; part_info->end_trk = stop; cc = start / geo.heads; hh = start - (cc * geo.heads); vtoc_set_cchh(&llimit, cc, hh); /* check for cylinder boundary */ if (hh == 0) b1 = 0x81; else b1 = 0x01; cc = stop / geo.heads; hh = stop - cc * geo.heads; vtoc_set_cchh(&ulimit, cc, hh); /* it is always the 1st extent */ b2 = 0x00; vtoc_set_extent(part_extent, b1, b2, &llimit, &ulimit); return 0; } /* * */ static void fdasd_enqueue_new_partition(fdasd_anchor_t *anc) { partition_info_t *part_tmp = anc->first, *part_info = anc->last; int i, j, k = 0; for (i = 1; i < USABLE_PARTITIONS; i++) { if ((part_tmp->end_trk == 0) || (part_info->start_trk < part_tmp->start_trk)) { break; } else { part_tmp = part_tmp->next; k++; } } if (anc->first == part_tmp) anc->first = part_info; if (part_info != part_tmp) { anc->last->prev->next = NULL; anc->last = anc->last->prev; part_info->next = part_tmp; part_info->prev = part_tmp->prev; part_tmp->prev = part_info; if (part_info->prev != NULL) part_info->prev->next = part_info; } part_info->used = 0x01; for (i = 0; i < USABLE_PARTITIONS; i++) { j = getpos(anc, i); if (j >= k) setpos(anc, i, j + 1); } /* update free-space counters */ if (anc->first == part_info) { /* partition is the first used partition */ if (part_info->start_trk == FIRST_USABLE_TRK) { /* partition starts right behind VTOC */ part_info->fspace_trk = anc->fspace_trk - part_info->len_trk; anc->fspace_trk = 0; } else { /* there is some space between VTOC and partition */ part_info->fspace_trk = anc->fspace_trk - part_info->len_trk - part_info->start_trk + FIRST_USABLE_TRK; anc->fspace_trk = part_info->start_trk - FIRST_USABLE_TRK; } } else { /* there are partitions in front of the new one */ if (part_info->start_trk == part_info->prev->end_trk + 1) { /* new partition is right behind the previous one */ part_info->fspace_trk = part_info->prev->fspace_trk - part_info->len_trk; part_info->prev->fspace_trk = 0; } else { /* there is some space between new and prev. part. */ part_info->fspace_trk = part_info->prev->fspace_trk - part_info->len_trk - part_info->start_trk + part_info->prev->end_trk + 1; part_info->prev->fspace_trk = part_info->start_trk - part_info->prev->end_trk - 1; } } } /* * */ static void fdasd_dequeue_old_partition(fdasd_anchor_t *anc, partition_info_t *part_info, int k) { int i, j; if (part_info != anc->first && part_info != anc->last) { /* dequeue any non-special element */ part_info->prev->next = part_info->next; part_info->next->prev = part_info->prev; } if (part_info == anc->first) { /* dequeue first element */ anc->first = part_info->next; part_info->next->prev = NULL; anc->fspace_trk += (part_info->len_trk + part_info->fspace_trk); } else { part_info->prev->fspace_trk += (part_info->len_trk + part_info->fspace_trk); } if (part_info != anc->last) { part_info->prev = anc->last; part_info->next = NULL; anc->last->next = part_info; anc->last = part_info; } for (i = 0; i < USABLE_PARTITIONS; i++) { j = getpos(anc, i); if (j >= k) setpos(anc, i, j - 1); } part_info->used = 0x00; part_info->len_trk = 0x0; part_info->start_trk = 0x0; part_info->end_trk = 0x0; part_info->fspace_trk = 0x0; bzero(part_info->f1, sizeof(format1_label_t)); } /* * adds a new partition to the 'partition table' */ static void fdasd_add_partition(fdasd_anchor_t *anc) { partition_info_t *part_info; unsigned long start, stop; extent_t ext; cchhb_t hf1; part_info = fdasd_get_empty_f1_label(anc); if (part_info == NULL) { printf("No more free partitions left,\n" "you have to delete one first!"); return; } if (fdasd_get_partition_data(anc, &ext, part_info) != 0) return; if (anc->formatted_cylinders > LV_COMPAT_CYL) vtoc_init_format8_label(anc->blksize, &ext, part_info->f1); else vtoc_init_format1_label(anc->blksize, &ext, part_info->f1); fdasd_enqueue_new_partition(anc); anc->used_partitions += 1; get_addr_of_highest_f1_f8_label(anc, &hf1); vtoc_update_format4_label(anc->f4, &hf1, anc->f4->DS4DSREC - 1); start = cchh2trk(&ext.llimit, &geo); stop = cchh2trk(&ext.ulimit, &geo); vtoc_set_freespace(anc->f4, anc->f5, anc->f7, '-', anc->verbose, start, stop, anc->formatted_cylinders, geo.heads); anc->vtoc_changed++; } /* * removes a partition from the 'partition table' */ static void fdasd_remove_partition(fdasd_anchor_t *anc) { partition_info_t *part_info = anc->first; unsigned long start, stop; unsigned int part_id, i; cchhb_t hf1; fdasd_list_partition_table(anc); while (!isdigit(part_id = read_char("\ndelete partition with id " "(use 0 to exit): "))) printf("Invalid partition id '%c' detected.\n", part_id); printf("\n"); part_id -= 48; if (part_id == 0) return; if (part_id > anc->used_partitions) { printf("'%d' is not a valid partition id!\n", part_id); return; } printf("deleting partition number '%d'...\n", part_id); setpos(anc, part_id - 1, -1); for (i = 1; i < part_id; i++) part_info = part_info->next; start = cchh2trk(&part_info->f1->DS1EXT1.llimit, &geo); stop = cchh2trk(&part_info->f1->DS1EXT1.ulimit, &geo); fdasd_dequeue_old_partition(anc, part_info, part_id - 1); anc->used_partitions -= 1; if (anc->used_partitions != 0) get_addr_of_highest_f1_f8_label(anc, &hf1); else bzero(&hf1, sizeof(struct cchhb)); vtoc_update_format4_label(anc->f4, &hf1, anc->f4->DS4DSREC + 1); vtoc_set_freespace(anc->f4, anc->f5, anc->f7, '+', anc->verbose, start, stop, anc->formatted_cylinders, geo.heads); anc->vtoc_changed++; } /* * writes a standard volume label and a standard VTOC with * only one partition to disc. With this function is it * possible to create one partition in non-interactive mode, * which can be used within shell scripts */ static void fdasd_auto_partition(fdasd_anchor_t *anc) { partition_info_t *part_info = anc->first; cchh_t llimit, ulimit; u_int16_t head; u_int32_t cyl; extent_t ext; cchhb_t hf1; if (!anc->silent) printf("auto-creating one partition for the whole disk...\n"); fdasd_init_volume_label(anc); if (anc->verbose) printf("initializing labels...\n"); vtoc_init_format4_label(anc->f4, geo.cylinders, anc->formatted_cylinders, geo.heads, geo.sectors, anc->blksize, anc->dev_type); vtoc_init_format5_label(anc->f5); vtoc_init_format7_label(anc->f7); cyl = get_usable_cylinders(anc); head = anc->f4->DS4DEVCT.DS4DSTRK; part_info->used = 0x01; part_info->fspace_trk = 0; part_info->len_trk = head * cyl - FIRST_USABLE_TRK; part_info->start_trk = FIRST_USABLE_TRK; part_info->end_trk = head * cyl - 1; vtoc_set_cchh(&llimit, 0, FIRST_USABLE_TRK); vtoc_set_cchh(&ulimit, cyl - 1, head - 1); vtoc_set_extent(&ext, 0x01, 0x00, &llimit, &ulimit); if (anc->formatted_cylinders > LV_COMPAT_CYL) vtoc_init_format8_label(anc->blksize, &ext, part_info->f1); else vtoc_init_format1_label(anc->blksize, &ext, part_info->f1); anc->fspace_trk = 0; anc->used_partitions = 1; get_addr_of_highest_f1_f8_label(anc, &hf1); vtoc_update_format4_label(anc->f4, &hf1, anc->f4->DS4DSREC - 1); anc->vtoc_changed++; fdasd_write_labels(anc); fdasd_exit(anc, 0); } /* * does the partitioning regarding to the config file */ static void fdasd_auto_partition_conffile(fdasd_anchor_t *anc) { partition_info_t *part_info = anc->first; unsigned long start, stop; cchh_t llimit, ulimit; char *dsname = NULL; extent_t ext; cchhb_t hf1; char *type; fdasd_init_volume_label(anc); if (anc->verbose) printf("initializing labels...\n"); vtoc_init_format4_label(anc->f4, geo.cylinders, anc->formatted_cylinders, geo.heads, geo.sectors, anc->blksize, anc->dev_type); vtoc_init_format5_label(anc->f5); vtoc_init_format7_label(anc->f7); if (anc->fspace_trk != 0) { start = FIRST_USABLE_TRK; stop = start + anc->fspace_trk - 1; vtoc_set_freespace(anc->f4, anc->f5, anc->f7, '+', anc->verbose, start, stop, anc->formatted_cylinders, geo.heads); } do { if (part_info->used != 0x01) continue; vtoc_set_cchh(&llimit, part_info->start_trk / geo.heads, part_info->start_trk % geo.heads); vtoc_set_cchh(&ulimit, part_info->end_trk / geo.heads, part_info->end_trk % geo.heads); vtoc_set_extent(&ext, (vtoc_get_head_from_cchh(&llimit) == 0 ? 0x81 : 0x01), 0x00, &llimit, &ulimit); if (anc->formatted_cylinders > LV_COMPAT_CYL) vtoc_init_format8_label(anc->blksize, &ext, part_info->f1); else vtoc_init_format1_label(anc->blksize, &ext, part_info->f1); anc->used_partitions += 1; get_addr_of_highest_f1_f8_label(anc, &hf1); vtoc_update_format4_label(anc->f4, &hf1, anc->f4->DS4DSREC - 1); /* update free space labels */ if (part_info->fspace_trk != 0) { start = part_info->end_trk + 1; stop = start + part_info->fspace_trk - 1; vtoc_set_freespace(anc->f4, anc->f5, anc->f7, '+', anc->verbose, start, stop, anc->formatted_cylinders, geo.heads); } /* write correct partition type */ vtoc_ebcdic_dec(part_info->f1->DS1DSNAM, part_info->f1->DS1DSNAM, 44); type = strstr(part_info->f1->DS1DSNAM, ".NEW"); get_part_dsname_by_type(part_info->type, &dsname); sprintf(type, ".%s", dsname); vtoc_ebcdic_enc(part_info->f1->DS1DSNAM, part_info->f1->DS1DSNAM, 44); } while ((part_info = part_info->next) != NULL); anc->vtoc_changed++; fdasd_write_labels(anc); fdasd_exit(anc, 0); } /* * quits fdasd without saving */ static void fdasd_quit(fdasd_anchor_t *anc) { char str[INPUT_BUF_SIZE]; if (anc->vtoc_changed || anc->vlabel_changed) { snprintf(str, INPUT_BUF_SIZE, "All changes will be lost! " "Do you really want to quit?"); if (yes_no(str) == 1) return; printf("exiting without saving...\n"); } else { if (!anc->silent) printf("exiting...\n"); } fdasd_exit(anc, 0); } /* * */ int main(int argc, char *argv[]) { fdasd_anchor_t anchor; int rc = 0; fdasd_initialize_anchor(&anchor); fdasd_parse_options(&anchor, &options, argc, argv); fdasd_verify_device(&anchor, options.device); fdasd_verify_options(&anchor); fdasd_get_geometry(&anchor); fdasd_check_disk_access(&anchor); /* check dasd for labels and vtoc */ rc = fdasd_check_volume(&anchor); if (anchor.formatted_cylinders * geo.heads > BIG_DISK_SIZE) anchor.big_disk++; if (anchor.auto_partition) { fdasd_recreate_vtoc_unconditional(&anchor); fdasd_auto_partition(&anchor); } if (options.conffile) { fdasd_recreate_vtoc_unconditional(&anchor); fdasd_parse_conffile(&anchor, &options); fdasd_check_conffile_input(&anchor, &options); fdasd_auto_partition_conffile(&anchor); } if (anchor.print_volser) { fdasd_print_volser(&anchor); fdasd_quit(&anchor); } if (anchor.print_table) { if (rc == 0) fdasd_list_partition_table(&anchor); fdasd_quit(&anchor); } fdasd_menu(); while (1) { putchar('\n'); switch (tolower(read_char("Command (m for help): "))) { case 'd': fdasd_remove_partition(&anchor); break; case 'n': fdasd_add_partition(&anchor); break; case 'v': fdasd_change_volser(&anchor); break; case 't': fdasd_change_part_type(&anchor); break; case 'p': fdasd_list_partition_table(&anchor); break; case 'l': printf("\n"); fdasd_list_known_partitions(); break; case 's': fdasd_show_mapping(&anchor); break; case 'u': anchor.option_reuse++; break; case 'r': anchor.option_recreate++; break; case 'm': fdasd_menu(); break; case 'q': fdasd_quit(&anchor); break; case 'w': fdasd_write_labels(&anchor); fdasd_exit(&anchor, 0); default: printf("please use one of the following commands:\n"); fdasd_menu(); } if (anchor.option_reuse) { fdasd_reuse_vtoc(&anchor); anchor.option_reuse = 0; } if (anchor.option_recreate) { fdasd_recreate_vtoc(&anchor); anchor.option_recreate = 0; } } return EXIT_FAILURE; } s390-tools-2.38.0/fdasd/fdasd.h000066400000000000000000000067421502674226300157730ustar00rootroot00000000000000/* * fdasd - Create or modify partitions on ECKD DASDs * * Copyright IBM Corp. 2001, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef FDASD_H #define FDASD_H /***************************************************************************** * SECTION: Definitions needed for DASD-API (see dasd.h) * *****************************************************************************/ #define DASD_PARTN_BITS 2 /***************************************************************************** * SECTION: FDASD internal types * *****************************************************************************/ #define PARTN_MASK ((1 << DASD_PARTN_BITS) - 1) #define USABLE_PARTITIONS ((1 << DASD_PARTN_BITS) - 1) #define DEFAULT_FDASD_CONF "/etc/fdasd.conf" /* default config file */ #define CONFIG_FILE_SIZE (USABLE_PARTITIONS * LINE_LENGTH) #define CONFIG_MAX 3 /* maximum number of parameters per config file entry */ #define FDASD_ERROR "fdasd error: " #define DEVICE "device" #define DISC "disc" #define PART "part" #define ALTERNATE_CYLINDERS_USED 0x10 /* partition types */ #define PARTITION_NEW 0 #define PARTITION_NATIVE 1 #define PARTITION_SWAP 2 #define PARTITION_RAID 3 #define PARTITION_LVM 4 #define PARTITION_GPFS 5 /* * PARTITION_NEW is the first item in our partition_types array and technically * maps to PARTITION_NATIVE. As PARTITION_NEW isn't a valid partition_type, it * can be ignored. Use this offset when iterate over the array. */ #define VALID_PARTITION_OFFSET 1 typedef struct partition_type { char *name; /* User-friendly Name */ char *dsname; /* Data Set Name */ int type; /* Numerical Representation */ } partition_type_t; struct fdasd_options { char *device; char *volser; char *conffile; }; static struct fdasd_options options = { NULL, /* device */ NULL, /* volser */ NULL, /* conffile */ }; typedef struct partition_info { u_int8_t used; unsigned long start_trk; unsigned long end_trk; unsigned long len_trk; unsigned long fspace_trk; format1_label_t *f1; int type; struct partition_info *next; struct partition_info *prev; } partition_info_t; typedef struct config_data { unsigned long start; unsigned long stop; int type; } config_data_t; typedef struct fdasd_anchor { int vlabel_changed; int vtoc_changed; int auto_partition; int print_table; int print_volser; int keep_volser; int force_virtual; int force_host; int big_disk; int silent; int verbose; int devno; int option_reuse; int option_recreate; int partno[USABLE_PARTITIONS]; u_int16_t dev_type; unsigned int used_partitions; unsigned long label_pos; unsigned int blksize; unsigned long fspace_trk; format4_label_t *f4; format5_label_t *f5; format7_label_t *f7; format9_label_t *f9; /* template for all f9 labels */ partition_info_t *first; partition_info_t *last; volume_label_t *vlabel; config_data_t confdata[USABLE_PARTITIONS]; u_int32_t hw_cylinders; u_int32_t formatted_cylinders; } fdasd_anchor_t; enum offset {lower, upper}; enum fdasd_failure { parser_failed, unable_to_open_disk, unable_to_seek_disk, unable_to_read_disk, read_only_disk, unable_to_ioctl, wrong_disk_type, wrong_disk_format, disk_in_use, config_syntax_error, vlabel_corrupted, dsname_corrupted, malloc_failed, device_verification_failed, volser_not_found }; #define ERROR_STRING_SIZE 1024 #define INPUT_BUF_SIZE 1024 #endif /* FDASD_H */ s390-tools-2.38.0/hmcdrvfs/000077500000000000000000000000001502674226300152635ustar00rootroot00000000000000s390-tools-2.38.0/hmcdrvfs/Makefile000066400000000000000000000023461502674226300167300ustar00rootroot00000000000000#!/usr/bin/make -f include ../common.mak ifeq (${HAVE_FUSE},0) all: $(SKIP) HAVE_FUSE=0 install: $(SKIP) HAVE_FUSE=0 else check_dep: $(call check_dep, \ "hmcdrvfs", \ "fuse.h", \ "fuse3-devel or libfuse3-dev", \ "HAVE_FUSE=0") FUSE_CFLAGS = $(shell $(PKG_CONFIG) --silence-errors --cflags fuse3) FUSE_LDLIBS = $(shell $(PKG_CONFIG) --silence-errors --libs fuse3) ALL_CFLAGS += -DFUSE_USE_VERSION=30 -D_LARGEFILE_SOURCE $(FUSE_CFLAGS) LDLIBS += $(FUSE_LDLIBS) -lpthread -lrt -ldl -lm OBJECTS = hmcdrvfs.o libs = $(rootdir)/libutil/libutil.a all: check_dep hmcdrvfs $(OBJECTS): Makefile hmcdrvfs: $(OBJECTS) $(libs) install: all install-scripts $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 hmcdrvfs \ $(DESTDIR)$(USRBINDIR) $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 hmcdrvfs.1 \ $(DESTDIR)$(MANDIR)/man1 $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 lshmc.8 \ $(DESTDIR)$(MANDIR)/man8 install-scripts: lshmc @for i in $^; do \ cat $$i | \ sed -e 's/%S390_TOOLS_VERSION%/$(S390_TOOLS_RELEASE)/' \ >$(DESTDIR)$(USRSBINDIR)/$$i; \ chown $(OWNER):$(GROUP) $(DESTDIR)$(USRSBINDIR)/$$i; \ chmod 755 $(DESTDIR)$(USRSBINDIR)/$$i; \ done endif clean: rm -f hmcdrvfs *.o .PHONY: all install install-scripts clean check_dep s390-tools-2.38.0/hmcdrvfs/hmcdrvfs.1000066400000000000000000000201121502674226300171550ustar00rootroot00000000000000.\" Copyright IBM Corp. 2015, 2017 .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH HMCDRVFS 1 "Mar 2015" "s390-tools" .\" disable hyphenation for words below .hw hmcdrv hmcdrvfs fuse DIAGNOSTICS .SH NAME hmcdrvfs \- mount a .SM FUSE file system for remote access to a .SM DVD in a .SM HMC DVD drive .SH SYNOPSIS mounting: .nf .RS \fBhmcdrvfs\fP MOUNTPOINT [OPTIONS] .RE .fi .PP unmounting: .nf .RS \fBfusermount\fP -u MOUNTPOINT .RE .fi .SH DESCRIPTION Use the \fBhmcdrvfs\fP command for read-only access to files on a .SM DVD in the .SM DVD drive of an .SM HMC\c \&. On the .SM HMC\c , the .SM DVD must be assigned to the .SM LPAR within which your Linux instance runs. For .SM z/VM guests, the .SM DVD must be assigned to the .SM LPAR where the .SM z/VM hypervisor runs. With the .SM DVD assigned to your Linux instance, this command creates a .SM FUSE.HMCDRVFS file system with the content of the .SM DVD at the specified mountpoint. .SH OPTIONS .SS "General mount options" .TP \fB-o\fP opt[,opt...] .SM FUSE or mount command options; for the .SM FUSE options see below, for mount options see \fBmount(8)\fP .TP \fB-h\fP, \fB--help\fP print usage information, then exit (see also DIAGNOSTICS) .TP \fB-v\fP, \fB--version\fP print version information, then exit .SS "Specific FUSE.HMCDRVFS options" .TP .BI "-o hmclang=" LANG specify the language setting on the .SM HMC\c ; for valid values, see \fBlocale(1)\fP; for more information, see DIAGNOSTICS and EXAMPLES .TP .BI "-o hmctz=" TZ specify the time zone setting on the .SM HMC\c ; for valid values, see \fBtzset(3)\fP; for more information, see DIAGNOSTICS and EXAMPLES .SS "Applicable FUSE options (version 2.6)" .TP \fB-d\fP, \fB-o debug\fP enable debug output (implies \fB-f\fP) .TP .B -f foreground operation .TP .B -s disable multi-threaded operation .TP .B -o allow_other allow access by other users .TP .B -o allow_root allow access by root .TP .B -o default_permissions enable permission checking by kernel .TP .BI "-o fsname=" NAME set file system name .TP .BI "-o subtype=" TYPE set file system type .TP .BI "-o max_read=" N set maximum size of read requests .TP .B -o direct_io use direct I/O .TP .B -o kernel_cache cache files in kernel .TP \fB-o \fP[\fBno\fP]\fBauto_cache\fP enable caching based on modification times .TP .BI "-o umask=" M set file permissions (octal) .TP .BI "-o uid=" N set file owner .TP .BI "-o gid=" N set file group .TP .BI "-o entry_timeout=" T cache timeout for names (default: 1.0 second) .TP .BI "-o attr_timeout=" T cache timeout for attributes (default: 1.0 second) .TP .BI "-o ac_attr_timeout=" T auto cache timeout for attributes (default: \fBattr_timeout\fP) .TP .BI "-o max_readahead=" N set maximum readahead .TP .B -o async_read perform reads asynchronously (default) .TP .B -o sync_read perform reads synchronously .TP .B -o no_remote_lock disable remote file locking .TP .B -o intr allow requests to be interrupted .TP .BI "-o intr_signal=" NUM signal to send on interrupt .SH EXAMPLES To mount the .SM HMC drive .SM DVD at \fI/mnt/hmc\fP without any special options use: .PP .nf .RS .B $ hmcdrvfs /mnt/hmc .RE .fi .PP In case the kernel module \fIhmcdrv\fP was not loaded in advance use: .PP .nf .RS .B # modprobe hmcdrv .B # hmcdrvfs /mnt/hmc .RE .fi .PP To translate the UID and GID of files on the .SM HMC drive .SM DVD to your system users and groups along with overriding the permissions use for example: .PP .nf .RS .B $ hmcdrvfs /mnt/hmc -o uid=500 -o gid=1000 -o umask=0337 .RE .fi .PP To speed up transfer rates to frequently accessed directories use the cache timeout option: .PP .nf .RS .B $ hmcdrvfs /mnt/hmc -o entry_timeout=60 .RE .PP .fi If the .SM HMC is in a different timezone and is configured for a different language use, for example: .PP .nf .RS .B $ hmcdrvfs /mnt/hmc -o hmclang=de_DE -o hmctz=Europe/Berlin .RE .fi .PP or disregarding any daylight saving time, specifying hours west of Prime Meridian (UTC): .PP .nf .RS .B $ hmcdrvfs /mnt/hmc -o hmclang=de_DE -o hmctz="GMT-1" .RE .fi .PP To unmount the .SM HMC drive .SM DVD mounted on \fI/mnt/hmc\fP use: .PP .nf .RS .B $ fusermount -u /mnt/hmc .RE .fi .PP It is also possible to mount by using \fBfstab(5)\fP. Specify the mount point and associated mount options in \fI/etc/fstab\fP, for example: .PP .nf .RS hmcdrvfs /mnt/hmc fuse ro,noatime,allow_other,uid=500,gid=1000 .RE .fi .PP You can then mount the file system with this command: .PP .nf .RS .B # mount /mnt/hmc .RE .fi .SH FILES Some general options about mount policy can be set in the \fI/etc/\:fuse.conf\fP file. These options are: .TP .BI "mount_max=" NNN Set the maximum number of .SM FUSE mounts allowed to non-root users. The default is 1000. .TP .B user_allow_other Allow non-root users to specify the \fBallow_other\fP or \fBallow_root\fP mount options. .SH DIAGNOSTICS .IP 1. 3 The .SM FUSE.HMCDRVFS file system needs access to device node \fI/dev/\:hmcdrv\fP. This node is created automatically when the \fIhmcdrv\fP kernel module is loaded (see Linux kernel configuration option .SM CONFIG_HMC_DRV\c ). The user process that runs the \fBhmcdrvfs\fP command must have sufficient privileges to read from and write to node \fI/dev/\:hmcdrv\fP. Use the commands \fBchown(1)\fP, \fBchgrp(1)\fP and/or \fBchmod(1)\fP on node \fI/dev/\:hmcdrv\fP to ensure this condition. .IP 2. 3 In addition to the required permissions, there are some environmental requirements: .RS .IP - 2 In a .SM z/VM environment, the .SM z/VM guest virtual machine must have at least privilege class B. .IP - 2 For Linux in .SM LPAR mode, the .SM LPAR activation profile must allow issuing .SM SCLP requests. .IP - 2 On the .SM HMC\c , the .SM DVD must be assigned to the associated system image (use menu \fIAccess Removable Media\fP). .RE .IP 3. 3 The .SM FUSE.HMCDRVFS file system maintains a file attributes cache, with an aging timeout. This timeout is related to the \fBentry_timeout\fP and \fBattr_timeout\fP .SM FUSE options. Its value exceeds the greater of the two, \fBentry_timeout\fP and \fBattr_timeout\fP, by 30 - 60 seconds. This timeout affects the performance of the .SM FUSE.HMCDRVFS file system. .RE .IP 4. 3 Different language and time zone settings on the .SM HMC and your Linux instance can result in incorrect file modification information. .RS .IP - 2 Use the \fBhmclang\fP specific .SM FUSE.HMCDRVFS option if the language settings of the .SM HMC and your Linux instance do not match. Correctly setting this option prevents incorrect file modification dates in the file details. Omitting the \fBhmclang\fP option can result in incorrect dates of the form 01-01-YYYY, with misleading values for the day and month. .IP - 2 Use the \fBhmctz\fP specific .SM FUSE.HMCDRVFS option if the time zone settings of the .SM HMC and your Linux instance do not match. Correctly setting this option prevents incorrect file modification times in the file details. Omitting \fBhmctz\fP, an incorrect specification, or a missing time zone description file can result in modification times that differ up to 25 hours from the correct times. Specifications that cannot be interpreted result in GMT/UTC being set. If the .SM HMC is set to GMT/UTC, specify \fBhmctz=""\fP. .RE .IP 5. 3 The following generic mount options from \fBmount(8)\fP are ignored: .RS 3 .TP \fB-w\fP, \fB--rw\fP, \fB-o rw\fP mount the file system read/write .TP .B -o atime update inode access times on this file system .RE .IP 6. 3 The following .SM FUSE mount options from \fBmount.fuse(8)\fP are ignored: .RS 3 .TP .B -o hard_remove immediate removal .TP .B -o negative_timeout cache timeout for deleted names .TP .BI "-o max_write=" N set maximum size of write requests .TP .B -o atomic_o_trunc enable atomic open+truncate support .TP .B -o big_writes enable larger than 4kB writes .TP .BI "-o subdir=" DIR prepend this directory to all paths .TP .B -o use_ino let file system set inode numbers .TP .B -o readdir_ino try to fill in d_ino in readdir .RE .SH SEE ALSO .BR tzset(3), .BR locale(1), .BR fusermount(1), .BR fstab(5), .BR mount(8), .B mount.fuse(8) .BR lshmc(8), and Linux on System z: Device Drivers, Features and Commands s390-tools-2.38.0/hmcdrvfs/hmcdrvfs.c000066400000000000000000001103261502674226300172460ustar00rootroot00000000000000/* * HMC Drive FUSE Filesystem (FUSE.HMCDRVFS) * * Copyright IBM Corp. 2015, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/util_libc.h" #include "lib/zt_common.h" #define HMCDRV_FUSE_LOGNAME "hmcdrvfs" /* log prefix */ #define HMCDRV_FUSE_LOGHEAD HMCDRV_FUSE_LOGNAME ": " /* log header */ #define HMCDRV_FUSE_FTPDEV "/dev/hmcdrv" /* DIAG/SCLP FTP device */ #define HMCDRV_FUSE_MAXPATH 192 /* max. path length (including EOS) */ #define HMCDRV_FUSE_MAXFTPLEN 3 /* max. length of FTP cmd (dir/nls/get) */ #define HMCDRV_FUSE_OFSPATH (HMCDRV_FUSE_MAXFTPLEN + 1) /* path in cmd */ #define HMCDRV_FUSE_MAXCMDLEN (HMCDRV_FUSE_MAXPATH + HMCDRV_FUSE_OFSPATH) /* internal increase (margin/offset) of cache timeout with respect to FUSE * options 'attr_timeout' and/or 'entry_timeout' (having default 1s) */ #define HMCDRV_FUSE_CACHE_TMOFS 30 /* size of file name/attributes cache (should be a prime number) */ #define HMCDRV_FUSE_CACHE_SIZE 2053 /* number of hash table lines inspected in a single garbage loop */ #define HMCDRV_FUSE_GARBAGE_MAX 128 /* max. size of FTP 'dir ' output chunk size */ #define HMCDRV_FUSE_DIRBUF_SIZE (64 * 1024) /* max. string length in FTP 'dir ' output buffer */ #define HMCDRV_FUSE_DIRBUF_LEN (HMCDRV_FUSE_DIRBUF_SIZE - 1) /* pointer to path (token) in FTP command string associated with file 'fp' */ #define HMCDRV_FUSE_PATH(fp) ((fp)->ftpcmd + HMCDRV_FUSE_OFSPATH) /* * DEBUG log macro (requires -DDEBUG, else empty) */ #ifdef DEBUG #define HMCDRV_FUSE_DBGLOG(fmt, ...) syslog(LOG_ERR, (fmt), ##__VA_ARGS__) #else #define HMCDRV_FUSE_DBGLOG(fmt, ...) do {} while (0) #endif /* DEBUG */ /* * log output to 'stderr' and optionally (if from a fork'ed process/thread) * to syslog */ #define HMCDRV_FUSE_LOG(sev, fmt, ...) \ hmcdrv_fuse_log((sev), HMCDRV_FUSE_LOGHEAD "%s: " fmt "\n", \ hmcdrv_fuse_logsevname[sev], ##__VA_ARGS__) #define HMCDRV_FUSE_STR(x) #x #define HMCDRV_FUSE_STRINGER(x) HMCDRV_FUSE_STR(x) #define HMCDRV_FUSE_RELEASE HMCDRV_FUSE_STRINGER(S390_TOOLS_RELEASE) #define HMCDRV_FUSE_OPT(t, p, v) { t, offsetof(struct hmcdrv_fuse_opt, p), v } /* * option key IDs (used on command line parsing) */ enum { HMCDRV_FUSE_OPTKEY_VERSION, /* "-v", "--version" */ HMCDRV_FUSE_OPTKEY_HELP, /* "-h", "--help" */ HMCDRV_FUSE_OPTKEY_RO, /* read-only, e.g. "-o ro" or "-r" */ HMCDRV_FUSE_OPTKEY_RW, /* option associated with -wr (ignored) */ HMCDRV_FUSE_OPTKEY_NATM, /* option "noatime" */ HMCDRV_FUSE_OPTKEY_CTMO, /* cache timeout (entry/attr_timeout) */ HMCDRV_FUSE_OPTKEY_NSUP, /* unsupported options, e.g. "subdir" */ }; /* * internal used FTP command IDs */ enum hmcdrv_fuse_cmdid { HMCDRV_FUSE_CMDID_NLS = 0, HMCDRV_FUSE_CMDID_DIR = 1, HMCDRV_FUSE_CMDID_GET = 2, HMCDRV_FUSE_CMDID_SIZE }; /* * options/parameters to lookup at start */ struct hmcdrv_fuse_opt { unsigned int ro:1; /* option(s) "ro", "-r", etc. */ unsigned int noatime:1; /* option "noatime" */ char *hmctz; /* HMC timezone option "-o hmctz=TZ" */ char *hmclang; /* HMC locale option "-o hmclang=LANG" */ }; /* * HMC drive FUSE context */ struct hmcdrv_fuse_context { struct hmcdrv_fuse_opt opt; /* some saved startup options */ int fd; /* file descriptor of FTP device HMCDRV_FUSE_FTPDEV */ char *path; /* mount point path (root) */ struct stat st; /* mount point stat attributes */ time_t ctmo; /* cache timeout (derived from entry/attr_timeout) */ pthread_t tid; /* cache aging thread ID */ pthread_mutex_t mutex; /* cache access mutex */ pid_t pid; /* PID of main() */ char *abmon[12]; /* abbreviated month name of HMC locale */ int ablen[12]; /* length of each abbreviated month name */ }; /* * SE/HMC drive FUSE file private data */ struct hmcdrv_fuse_file { struct hmcdrv_fuse_file *next; /* collision list (equal hash) */ struct stat st; /* stat structure of this file */ char *symlnk; /* pointer to path name of symlink target (S_IFLNK) */ time_t timeout; /* cache timeout for this file */ size_t cmdlen; /* length of FTP command + path */ char ftpcmd[0]; /* FTP command + path (max HMCDRV_FUSE_MAXCMDLEN) */ }; /* * all file attributes accumulated from interpreting tokens/fields of 'dir' * command listing */ struct hmcdrv_fuse_attr { struct stat *st; /* pointer to stat structure */ struct tm ftm; /* local time */ char *fname; /* (base-) name */ }; /* * prototype (forward) declaration for hmcdrv_cache_dir() */ static struct hmcdrv_fuse_file *hmcdrv_file_get(const char *path); /* * log severity strings */ static const char *const hmcdrv_fuse_logsevname[] = { "Emergency", /* (0) LOG_EMERG */ "Alert", /* (1) LOG_ALERT */ "Critical", /* (2) LOG_CRIT */ "Error", /* (3) LOG_ERR */ "Warning", /* (4) LOG_WARNING */ "Notice", /* (5) LOG_NOTICE */ "Info", /* (6) LOG_INFO */ "Debug" /* (7) DEBUG */ }; /* * file attributes cache */ static struct hmcdrv_fuse_file *hmcdrv_fuse_cache[HMCDRV_FUSE_CACHE_SIZE]; /* * context */ static struct hmcdrv_fuse_context hmcdrv_ctx = { .fd = -1, .ctmo = 1 + HMCDRV_FUSE_CACHE_TMOFS, .mutex = PTHREAD_MUTEX_INITIALIZER, }; /* * log output from hmcdrvfs daemon */ static void hmcdrv_fuse_log(int sev, const char *fmt, ...) { va_list argp; va_start(argp, fmt); vfprintf(stderr, fmt, argp); /* always to stderr */ va_end(argp); if (hmcdrv_ctx.pid != getpid()) { struct fuse_context *fctx = fuse_get_context(); if ((fctx != NULL) && (hmcdrv_ctx.pid != fctx->pid)) { va_start(argp, fmt); vsyslog(sev, fmt + (sizeof(HMCDRV_FUSE_LOGHEAD) - 1), argp); va_end(argp); } } } /* * calculate a hash value from a file path */ static unsigned int hmcdrv_hash_path(const char *path) { unsigned int hash = ~0; int len = strlen(path); while (len > 0) { hash += (unsigned) *path; ++path; --len; } hash %= HMCDRV_FUSE_CACHE_SIZE; return hash; } /* * restart of cache entry aging time */ static void hmcdrv_cache_trestart(struct hmcdrv_fuse_file *fp) { fp->timeout = time(NULL) + hmcdrv_ctx.ctmo; } /* * update or free symlink information */ static void hmcdrv_cache_symlink(struct hmcdrv_fuse_file *fp, const char *symlink) { if (fp->symlnk != NULL) { free(fp->symlnk); fp->symlnk = NULL; } if ((symlink != NULL) && (symlink[0] != '\0')) { fp->symlnk = malloc(HMCDRV_FUSE_MAXPATH); if (fp->symlnk != NULL) { util_strlcpy(fp->symlnk, symlink, HMCDRV_FUSE_MAXPATH); fp->symlnk[HMCDRV_FUSE_MAXPATH - 1] = '\0'; } } } /* * refresh the attributes of file/directory 'path' * note: if there is no cache entry for this file, then create one */ static void hmcdrv_cache_refresh(const char *path, const struct stat *st, const char *symlink) { struct hmcdrv_fuse_file **pbase; /* storage location of fp */ struct hmcdrv_fuse_file *fp; unsigned int index; int pathlen = strlen(path); if ((pathlen == 0) || (pathlen >= HMCDRV_FUSE_MAXPATH)) { HMCDRV_FUSE_LOG(LOG_WARNING, "Invalid path length for '%s'", path); return; } index = hmcdrv_hash_path(path); pbase = &hmcdrv_fuse_cache[index]; fp = *pbase; while (fp != NULL) { if (strcmp(HMCDRV_FUSE_PATH(fp), path) == 0) { fp->st = *st; /* update file info */ hmcdrv_cache_symlink(fp, symlink); hmcdrv_cache_trestart(fp); /* restart aging */ return; } pbase = &fp->next; fp = fp->next; } /* entry does not exist so far - create new one */ fp = malloc((offsetof(struct hmcdrv_fuse_file, ftpcmd) + HMCDRV_FUSE_OFSPATH + 1) + pathlen); if (fp == NULL) { HMCDRV_FUSE_LOG(LOG_ERR, "Out of memory, %ld bytes", (offsetof(struct hmcdrv_fuse_file, ftpcmd) + HMCDRV_FUSE_OFSPATH + 1) + pathlen); } else { *pbase = fp; fp->cmdlen = pathlen + HMCDRV_FUSE_OFSPATH; fp->st = *st; memcpy(HMCDRV_FUSE_PATH(fp), path, pathlen + 1); fp->symlnk = NULL; hmcdrv_cache_symlink(fp, symlink); hmcdrv_cache_trestart(fp); fp->next = NULL; } } /* * search for a file/directory path in cache */ static struct hmcdrv_fuse_file *hmcdrv_cache_find(const char *path) { unsigned index = hmcdrv_hash_path(path); struct hmcdrv_fuse_file *fp = hmcdrv_fuse_cache[index]; while (fp != NULL) { if (strcmp(HMCDRV_FUSE_PATH(fp), path) == 0) { hmcdrv_cache_trestart(fp); return fp; } fp = fp->next; } return NULL; } /* * check for aging timeout of cache entry at index */ static void hmcdrv_cache_expire(unsigned index, time_t now) { struct hmcdrv_fuse_file **pbase; /* storage location of fp */ struct hmcdrv_fuse_file *fp, *next; pbase = &hmcdrv_fuse_cache[index]; fp = *pbase; /* iterate the collision list */ while (fp != NULL) { next = fp->next; if ((fp->timeout <= now) && (strcmp(HMCDRV_FUSE_PATH(fp), "/") != 0)) { hmcdrv_cache_symlink(fp, NULL); *pbase = next; free(fp); } else { pbase = &fp->next; } fp = next; } } /* * cache expiry handler thread */ static void *hmcdrv_cache_aging(void *UNUSED(arg)) { unsigned int cnt; time_t now; unsigned index = 0; while (1) { sleep(1); pthread_mutex_lock(&hmcdrv_ctx.mutex); now = time(NULL); /* each second scan a number of cache entries */ for (cnt = 0; cnt < HMCDRV_FUSE_GARBAGE_MAX; ++cnt) { hmcdrv_cache_expire(index, now); if (++index == HMCDRV_FUSE_CACHE_SIZE) index = 0; } pthread_mutex_unlock(&hmcdrv_ctx.mutex); } return NULL; } /* * convert a FTP command ID into a string * * Note: If a command string is shorter than HMCDRV_FUSE_MAXFTPLEN then * define it right-aligned (prefixed with spaces) in table 'cmdstr'. */ static void hmcdrv_ftp_str(enum hmcdrv_fuse_cmdid cmd, char *ftp) { static const char *cmdstr[HMCDRV_FUSE_CMDID_SIZE] = { #if (HMCDRV_FUSE_MAXFTPLEN != 3) #error The length of strings in cmdstr[] must match HMCDRV_FUSE_MAXFTPLEN #endif "nls", /* HMCDRV_FUSE_CMDID_NLS */ "dir", /* HMCDRV_FUSE_CMDID_DIR */ "get" /* HMCDRV_FUSE_CMDID_GET */ }; strcpy(ftp, cmdstr[cmd]); ftp[HMCDRV_FUSE_MAXFTPLEN] = ' '; /* overwrite '\0' from strcpy() */ } /* * FTP command execution via kernel device */ static ssize_t hmcdrv_ftp_transfer(struct hmcdrv_fuse_file *fp, char *buf, size_t len, off_t offset) { static off_t current_offset; static char last_ftpcmd[HMCDRV_FUSE_MAXCMDLEN]; ssize_t retlen; /* * First check if this is a sequential read from the same file. If * so skip repositioning the files seek pointer and emitting a new * command. */ if ((offset != current_offset) || (strncmp(fp->ftpcmd, last_ftpcmd, HMCDRV_FUSE_MAXCMDLEN) != 0)) { if ((lseek(hmcdrv_ctx.fd, offset, SEEK_END) < 0) || (write(hmcdrv_ctx.fd, fp->ftpcmd, fp->cmdlen) < 0)) { last_ftpcmd[0] = '\0'; return -errno; } current_offset = offset; } retlen = read(hmcdrv_ctx.fd, buf, len); if (retlen < 0) { last_ftpcmd[0] = '\0'; return -errno; } current_offset += retlen; util_strlcpy(last_ftpcmd, fp->ftpcmd, HMCDRV_FUSE_MAXCMDLEN); return retlen; } /* * FTP command assembly and execution */ static ssize_t hmcdrv_ftp_cmd(struct hmcdrv_fuse_file *fp, enum hmcdrv_fuse_cmdid cmd, char *buf, size_t len, off_t offset) { if (fp == NULL) return -EBADF; hmcdrv_ftp_str(cmd, fp->ftpcmd); return hmcdrv_ftp_transfer(fp, buf, len, offset); } /* * returns a file path (from internal file structure) to a buffer, * with appending a slash (in case it is missing) */ static int hmcdrv_path_copy(struct hmcdrv_fuse_file *fp, char *dest) { char *src = HMCDRV_FUSE_PATH(fp); int len = 0; while ((len < (HMCDRV_FUSE_MAXPATH - 1)) && (*src != '\0')) { *dest = *src; ++len; ++dest; ++src; } if ((len > 0) && (*(dest - 1) != '/')) { *dest++ = '/'; ++len; } *dest = '\0'; return len; } /* * convert a string into into a unsigned number, * returning 0 on success or -errno on error */ static int hmcdrv_parse_uint(const char *s, unsigned int *pval) { errno = 0; *pval = strtoul(s, NULL, 10); return -errno; } /* * convert a string into into a signed number (with range check), * returning 0 on success or -errno on error */ static int hmcdrv_parse_int(const char *s, int vmin, int vmax, int *pval) { errno = 0; *pval = strtol(s, NULL, 10); if (errno == 0) { if ((*pval >= vmin) && (*pval <= vmax)) return 0; errno = ERANGE; } return -errno; } /* * convert an abbreviated month name (%b) into a number */ static int hmcdrv_parse_month(const char *s) { int i; for (i = 0; i < 12; ++i) { if (strncasecmp(hmcdrv_ctx.abmon[i], s, hmcdrv_ctx.ablen[i]) == 0) return i; } return -1; } /* * return the file mode from a 'ls -l' like mode string */ static mode_t hmcdrv_parse_mode(const char *s) { int i; mode_t type; mode_t mode = 0; switch (*s) { case 'd': /* directory */ type = S_IFDIR; break; case 'b': /* block device */ type = S_IFBLK; break; case 'c': /* character device */ type = S_IFCHR; break; case 'l': /* symbolic link */ type = S_IFLNK; break; case 'p': /* FIFO */ type = S_IFIFO; break; case 's': /* socket */ type = S_IFSOCK; break; case '-': type = S_IFREG; break; default: return 0; } for (i = 0; i < 9; ++i) { mode <<= 1; switch (*++s) { case 'r': case 'w': case 'x': mode |= 1; break; case '-': break; default: return 0; } } mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); return mode | type; } /* * convert a time string from format "%H:%M" into a 'struct tm' (without any * local time conversion) */ static int hmcdrv_parse_time(const char *s, struct tm *t) { char *endptr; int hour, minute; errno = 0; hour = strtol(s, &endptr, 10); if ((errno == 0) && (endptr != s) && (hour >= 0) && (hour < 24)) { if (*endptr == ':') { s = ++endptr; errno = 0; minute = strtol(s, &endptr, 10); if ((errno == 0) && (endptr != s) && (minute >= 0) && (minute < 60)) { t->tm_hour = hour; t->tm_min = minute; return 0; /* success */ } } } return -1; } /* * interpret token depending on current field number and * fill the attributes structure * * Return: 'field' incremented by 1 or INT_MAX on parse errors */ static int hmcdrv_parse_ntoken(int field, char *token, struct hmcdrv_fuse_attr *attr) { switch (field) { case 0: /* mode */ attr->st->st_mode = hmcdrv_parse_mode(token); if (attr->st->st_mode == 0) /* error ? */ field = INT_MAX - 1; /* stop */ break; case 1: /* number of hard links to this file */ errno = 0; attr->st->st_nlink = strtoul(token, NULL, 10); if (errno != 0) field = INT_MAX - 1; break; case 2: /* user ID */ if (hmcdrv_parse_uint(token, &attr->st->st_uid) != 0) { attr->st->st_uid = hmcdrv_ctx.st.st_uid; HMCDRV_FUSE_LOG(LOG_WARNING, "Could not parse UID (will use %u)", attr->st->st_uid); } break; case 3: /* group ID */ if (hmcdrv_parse_uint(token, &attr->st->st_gid) != 0) { attr->st->st_gid = hmcdrv_ctx.st.st_gid; HMCDRV_FUSE_LOG(LOG_WARNING, "Could not parse GID (will use %u)", attr->st->st_gid); } break; case 4: /* file size */ errno = 0; attr->st->st_size = strtol(token, NULL, 10); if (errno != 0) field = INT_MAX - 1; break; case 5: /* month of year [0,11] */ attr->ftm.tm_mon = hmcdrv_parse_month(token); break; case 6: /* day of month [1,31] */ if (hmcdrv_parse_int(token, 1, 31, &attr->ftm.tm_mday) != 0) { field = INT_MAX - 1; } else { /* * in case month is not in English language we've * got an error in attr->ftm.tm_mon */ if (attr->ftm.tm_mon < 0) { attr->ftm.tm_mday = 1; /* YYYY-01-01 */ attr->ftm.tm_mon = 0; } } break; case 7: /* %H:%M or %Y */ if (hmcdrv_parse_time(token, &attr->ftm) != 0) { if (hmcdrv_parse_int(token, 1900, INT_MAX, &attr->ftm.tm_year) != 0) field = INT_MAX - 1; else attr->ftm.tm_year -= 1900; } break; case 8: /* file name (rest of line) */ attr->fname = token; break; default: break; } return ++field; } /* * parse filename and attributes from 'dir' output (a single line) * * Note: Because HMC command 'dir' always uses the HMC server local time the * current timezone should be set correctly, based on startup option * "-o hmctz=TZ". * * Return: pointer to start of next line (skipping any whitespace) * or pointer to '\0' */ static char *hmcdrv_parse_line(char *line, char *namebuf, int bufsize, struct stat *st, char *symlink) { int field = 0; struct hmcdrv_fuse_attr attr = {.fname = NULL, .st = st}; memset(st, 0, sizeof(*st)); namebuf[0] = '\0'; /* set default values */ st->st_mtime = time(NULL); /* see next line: gmtime_r() */ gmtime_r(&st->st_mtime, &attr.ftm); /* tm_year default */ attr.ftm.tm_isdst = -1; /* mktime() should use "TZ" info */ attr.ftm.tm_hour = attr.ftm.tm_min = attr.ftm.tm_sec = 0; while ((*line != '\0') && isspace(*line)) ++line; /* skip leading whitespace (incl. newlines) */ while ((*line != '\0') && (*line != '\n') && (field < 9)) { while ((*line != '\0') && (*line != '\n') && isspace(*line)) ++line; /* skip spaces, but not newlines */ if ((*line != '\0') && (*line != '\n')) { field = hmcdrv_parse_ntoken(field, line, &attr); while ((*line != '\0') && (*line != '\n')) { if (isspace(*line)) break; if (field == 1 && isdigit(*line)) break; ++line; /* search end of field */ } } } /* while */ /* search end of line now (skips some fields in case of error) */ while ((*line != '\0') && (*line != '\n')) ++line; /* * If start of file name was found by parser, then indicate success * returning the file attributes. But do this only if the end of * line was really found, else this seems to be an incomplete line * in current chunk (fragment). */ if ((attr.fname != NULL) && (*line == '\n')) { char *arrow = NULL; /* " -> " pointer, when symlink */ *line = '\0'; /* temporary set EOS for strncpy() */ if (S_ISLNK(st->st_mode)) { arrow = strstr(attr.fname, " -> "); if (arrow != NULL) *arrow = '\0'; } util_strlcpy(namebuf, attr.fname, bufsize); if (arrow == NULL) { symlink[0] = '\0'; } else { util_strlcpy(symlink, arrow + 4, HMCDRV_FUSE_MAXPATH); *arrow = ' '; /* restore */ } *line = '\n'; /* restore */ st->st_mtime = mktime(&attr.ftm); /* UTC from local time */ if (st->st_mtime == (time_t) -1) st->st_mtime = 0; st->st_atime = st->st_ctime = st->st_mtime; /* In contrast to FUSE documentation the members below must * be set in stat structure. For example 'du' uses them, * because it shows the disk usage from used blocks and not * the file size (unless option '--apparent-size' is given). */ st->st_blksize = 2048U; /* nearly all CD/DVDs have this */ st->st_blocks = (st->st_size + 511U) / 512U; } while ((*line != '\0') && isspace(*line)) ++line; /* skip whitespace after EOL, incl. newlines */ return line; } /* * performs a FTP 'dir', then scans/parses the output, fills the cache and * (optional) calls the FUSE filler function for every file in 'dir' listing */ static int hmcdrv_cache_dir(const char *dir, fuse_fill_dir_t filler, void *buf) { char path[HMCDRV_FUSE_MAXPATH]; /* constructed path of a file */ char symlink[HMCDRV_FUSE_MAXPATH]; /* target of symlink (S_IFLNK) */ struct hmcdrv_fuse_file *fpdir; /* directory file pointer */ struct stat st; /* attributes of file */ char *dirbuf; /* 'dir' listing (chunk) buffer */ char *next; /* 'next line' pointer in 'dir' buffer */ char *line; /* current line start position in 'dir' buffer */ int dirlen; /* number of characters in 'dir' buffer */ int fraglen; /* fragment length in 'dir' buffer */ char *fname; /* buffer pointer for basename of file */ int len; /* maximum length of file basename (buffer space) */ off_t offset; /* device 'dir' position */ char *tzenv = NULL; /* "TZ" environment variable */ fpdir = hmcdrv_file_get(dir); if (fpdir == NULL) { HMCDRV_FUSE_LOG(LOG_ERR, "Could not find directory '%s' in cache", dir); return -ENOENT; } dirbuf = malloc(HMCDRV_FUSE_DIRBUF_SIZE); if (dirbuf == NULL) { HMCDRV_FUSE_LOG(LOG_ERR, "Out of memory, %d bytes", HMCDRV_FUSE_DIRBUF_SIZE); return -ENOMEM; } /* set HMC timezone (optional), else host timezone remains active - * possibly different from HMC timezone and so resulting in wrong * file modification times (cf. mktime() in hmcdrv_parse_line()) */ if (hmcdrv_ctx.opt.hmctz != NULL) { /* "-o hmctz=TZ" present ? */ tzenv = getenv("TZ"); if (tzenv != NULL) tzenv = strdup(tzenv); if (setenv("TZ", hmcdrv_ctx.opt.hmctz, 1) != 0) { HMCDRV_FUSE_LOG(LOG_ERR, "Could not set HMC timezone '%s': %s", hmcdrv_ctx.opt.hmctz, strerror(errno)); if (tzenv != NULL) { setenv("TZ", tzenv, 1); free(tzenv); } free(dirbuf); return -ENOMEM; } tzset(); } len = hmcdrv_path_copy(fpdir, path); /* get directory path */ fname = path + len; /* start of basename in path buffer */ len = HMCDRV_FUSE_MAXPATH - len; /* space for basename */ HMCDRV_FUSE_DBGLOG("scanning '%s'...", path); /* do the 'dir' chunks and parse all */ dirlen = hmcdrv_ftp_cmd(fpdir, HMCDRV_FUSE_CMDID_DIR, dirbuf, HMCDRV_FUSE_DIRBUF_LEN, 0); next = dirbuf; /* first line at start of buffer */ offset = dirlen; while (dirlen > 0) { next[dirlen] = '\0'; /* force end of string */ /* parse all files in this 'dir' listing chunk */ do { line = next; /* parse next line and retrieve the file name */ next = hmcdrv_parse_line(line, fname, len, &st, symlink); /* If parsing of 'dir' line was successful, then * store file attributes and name in cache. Else * skip this line because of incorrect syntax - * possibly it is a fragment (at end of buffer) or * this line does not hold any valid file info. */ if (*fname != '\0') { hmcdrv_cache_refresh(path, &st, symlink); if ((filler != NULL) && (filler(buf, fname, &st, 0, 0) != 0)) filler = NULL; /* stop filling */ #ifdef DEBUG strftime(symlink, sizeof(symlink), "%c", gmtime(&st.st_mtime)); HMCDRV_FUSE_DBGLOG(" * %s (size=%zu, mode=%o, uid=%u, gid=%u, time=%s)", fname, st.st_size, st.st_mode, st.st_uid, st.st_gid, symlink); #endif } } while (*next != '\0'); /* Try to read more bytes from the FTP device. But regard * remaining chars from the last line, in case it could not * completely parsed (in other words the line was a * fragment, and has to be moved to start of buffer). */ if (*fname == '\0') { /* no valid filename? */ fraglen = next - line; if (fraglen > 0) memcpy(dirbuf, line, fraglen); } else { fraglen = 0; } dirlen = hmcdrv_ftp_cmd(fpdir, HMCDRV_FUSE_CMDID_DIR, dirbuf + fraglen, HMCDRV_FUSE_DIRBUF_LEN - fraglen, offset); if (dirlen > 0) { /* on success continue */ next = dirbuf; offset += dirlen; dirlen += fraglen; } } free(dirbuf); /* restore old host timezone */ if (hmcdrv_ctx.opt.hmctz != NULL) { if (tzenv != NULL) { putenv(tzenv); free(tzenv); } else { unsetenv("TZ"); } tzset(); } return dirlen; } /* * cache all files located in parent directory of 'dir', * so also caching the attributes of 'dir' again * * Return: 0 on success, else a negative error code (-ENOENT if 'dir' is "/") */ static int hmcdrv_cache_parent(const char *dir) { char *tmp, *parent; if (strcmp(dir, "/") == 0) /* root has no parent */ return -ENOENT; tmp = strdup(dir); /* need a copy because of dirname() */ if (tmp == NULL) { HMCDRV_FUSE_LOG(LOG_ERR, "Out of memory, %zd bytes", strlen(dir) + 1); return -ENOMEM; } parent = dirname(tmp); HMCDRV_FUSE_DBGLOG("re-scanning parent '%s'...", parent); hmcdrv_cache_dir(parent, NULL, NULL); free(tmp); return 0; } /* * return the file attributes data pointer (or NULL on error) */ static struct hmcdrv_fuse_file *hmcdrv_file_get(const char *path) { struct hmcdrv_fuse_file *fp = hmcdrv_cache_find(path); if (fp != NULL) /* path found in cache ? */ return fp; /* in very, very rare cases we must scan the parent again, * recursive up to the root directory */ if (hmcdrv_cache_parent(path) != 0) return NULL; /* because the mutex is taken by the caller, we MUST find this file * now in cache (if not so then the HMC drive DVD may have changed * without unmounting FUSE.HMCDRVFS) */ return hmcdrv_cache_find(path); } /* * obtain file status information (attributes) on FUSE.HMCDRVFS filesystem * * Note: The most important function which FUSE calls (very often). */ static int hmcdrv_fuse_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *UNUSED(fi)) { struct hmcdrv_fuse_file *fp; int rc = 0; pthread_mutex_lock(&hmcdrv_ctx.mutex); fp = hmcdrv_file_get(path); if (fp == NULL) rc = -ENOENT; else *stbuf = fp->st; pthread_mutex_unlock(&hmcdrv_ctx.mutex); return rc; } /* * get symlink target path */ static int hmcdrv_fuse_readlink(const char *path, char *buf, size_t size) { struct hmcdrv_fuse_file *fp; int rc = 0; pthread_mutex_lock(&hmcdrv_ctx.mutex); fp = hmcdrv_file_get(path); if (fp == NULL) { rc = -ENOENT; } else { if (fp->symlnk == NULL) { rc = -ENOMEM; } else { if (!S_ISLNK(fp->st.st_mode)) { rc = -EINVAL; } else { util_strlcpy(buf, fp->symlnk, size); } } } pthread_mutex_unlock(&hmcdrv_ctx.mutex); return rc; } /* * open a directory on FUSE.HMCDRVFS filesystem */ static int hmcdrv_fuse_opendir(const char *UNUSED(path), struct fuse_file_info *fi) { if ((fi->flags & O_ACCMODE) != O_RDONLY) return -EACCES; /* all files are read-only */ return 0; } /* * read a directory on FUSE.HMCDRVFS filesystem */ static int hmcdrv_fuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t UNUSED(offset), struct fuse_file_info *UNUSED(fi), enum fuse_readdir_flags UNUSED(flags)) { int ret; filler(buf, ".", NULL, 0, 0); filler(buf, "..", NULL, 0, 0); pthread_mutex_lock(&hmcdrv_ctx.mutex); ret = hmcdrv_cache_dir(path, filler, buf); pthread_mutex_unlock(&hmcdrv_ctx.mutex); return ret; } /* * open a file on FUSE.HMCDRVFS filesystem */ static int hmcdrv_fuse_open(const char *UNUSED(path), struct fuse_file_info *fi) { if ((fi->flags & O_ACCMODE) != O_RDONLY) return -EACCES; /* all files are read-only */ return 0; } /* * read a file on FUSE.HMCDRVFS filesystem */ static int hmcdrv_fuse_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *UNUSED(fi)) { struct hmcdrv_fuse_file *fp; int rc; pthread_mutex_lock(&hmcdrv_ctx.mutex); fp = hmcdrv_file_get(path); if (fp == NULL) rc = -ENOENT; else rc = hmcdrv_ftp_cmd(fp, HMCDRV_FUSE_CMDID_GET, buf, size, offset); pthread_mutex_unlock(&hmcdrv_ctx.mutex); return rc; } /* * initialize FUSE.HMCDRVFS filesystem * * Note: calls fuse_exit() on errors * * Return: value to be passed in the private_data field of fuse_context to * all file operations and as a parameter to the destroy() method */ static void *hmcdrv_fuse_init(struct fuse_conn_info *UNUSED(conn), struct fuse_config *UNUSED(cfg)) { pthread_mutexattr_t attr; memset(hmcdrv_fuse_cache, 0, sizeof(hmcdrv_fuse_cache)); openlog(HMCDRV_FUSE_LOGNAME, LOG_PID, LOG_DAEMON); if (pthread_mutexattr_init(&attr) != 0) goto err_out; pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); hmcdrv_ctx.fd = open(HMCDRV_FUSE_FTPDEV, O_RDWR); if (hmcdrv_ctx.fd < 0) goto err_dev; if (pthread_mutex_init(&hmcdrv_ctx.mutex, &attr) != 0) goto err_mutex; hmcdrv_cache_refresh("/", &hmcdrv_ctx.st, NULL); /* never expires */ if (pthread_create(&hmcdrv_ctx.tid, NULL, hmcdrv_cache_aging, NULL) == 0) { pthread_mutexattr_destroy(&attr); return &hmcdrv_ctx.tid; } err_mutex: close(hmcdrv_ctx.fd); err_dev: pthread_mutexattr_destroy(&attr); err_out: HMCDRV_FUSE_LOG(LOG_ERR, "Initialization failed: %s", strerror(errno)); closelog(); fuse_exit(fuse_get_context()->fuse); return NULL; } /* * clean up FUSE.HMCDRVFS filesystem */ static void hmcdrv_fuse_exit(void *arg) { struct hmcdrv_fuse_file *fp, *next; int i; if (arg != NULL) pthread_cancel(*(pthread_t *) arg); pthread_mutex_lock(&hmcdrv_ctx.mutex); for (i = 0; i < HMCDRV_FUSE_CACHE_SIZE; ++i) { fp = hmcdrv_fuse_cache[i]; while (fp != NULL) { next = fp->next; hmcdrv_cache_symlink(fp, NULL); free(fp); fp = next; } } memset(hmcdrv_fuse_cache, 0, sizeof(hmcdrv_fuse_cache)); pthread_mutex_destroy(&hmcdrv_ctx.mutex); closelog(); if (hmcdrv_ctx.fd >= 0) close(hmcdrv_ctx.fd); } /* * FUSE.HMCDRVFS internal main function */ static int hmcdrv_fuse_main(struct fuse_args *args) { static struct fuse_operations hmcdrv_fuse_op = { .init = hmcdrv_fuse_init, .destroy = hmcdrv_fuse_exit, .getattr = hmcdrv_fuse_getattr, .opendir = hmcdrv_fuse_opendir, .readdir = hmcdrv_fuse_readdir, .open = hmcdrv_fuse_open, .read = hmcdrv_fuse_read, .readlink = hmcdrv_fuse_readlink }; #if FUSE_VERSION >= 26 return fuse_main(args->argc, args->argv, &hmcdrv_fuse_op, NULL); #else return fuse_main(args->argc, args->argv, &hmcdrv_fuse_op); #endif } /* * usage output */ static void hmcdrv_fuse_usage(const char *progname) { fprintf(stdout, "Usage: %s MOUNTPOINT [OPTIONS]\n\n" "Use the %s command to read files from a HMC drive DVD.\n" "\n" "General options:\n" " -o opt,[opt...] Mount options\n" " -h --help Print help, then exit\n" " -v --version Print version, then exit\n" "\n" "Specific options:\n" " -o hmclang=LANG HMC speaks language LANG (see locale(1))\n" " -o hmctz=TZ HMC is in timezone TZ (see tzset(3))\n" "\n" "Attention:\n" " The following general and FUSE specific mount options will\n" " be ignored (most do not make sense on a read-only media):\n" " --rw, -w, -o rw, -o atime, -o max_write=N, -o big_writes,\n" " -o atomic_o_trunc, -o hard_remove, -o negative_timeout=T,\n" " -o use_ino, -o readdir_ino, -o subdir=DIR\n" "\n", progname, progname); } /* * process the "-o hmclang=LANG" command line option (if there is one) and * prepare abbreviated month names (%b) in context structure * * Return: 0 on success, -1 on error */ static int hmcdrv_optproc_lang(void) { static const nl_item nl_abmon[12] = { ABMON_1, ABMON_2, ABMON_3, ABMON_4, ABMON_5, ABMON_6, ABMON_7, ABMON_8, ABMON_9, ABMON_10, ABMON_11, ABMON_12}; unsigned int i; char *lc = NULL; /* current LC_TIME locale */ if (hmcdrv_ctx.opt.hmclang != NULL) { HMCDRV_FUSE_DBGLOG("option \"-o hmclang=%s\" detected", hmcdrv_ctx.opt.hmclang); lc = setlocale(LC_TIME, NULL); if (setlocale(LC_TIME, hmcdrv_ctx.opt.hmclang) == NULL) { setlocale(LC_TIME, lc); return -1; } } for (i = 0; i < (sizeof(nl_abmon) / sizeof(nl_abmon[0])); ++i) { hmcdrv_ctx.abmon[i] = nl_langinfo(nl_abmon[i]); hmcdrv_ctx.ablen[i] = strlen(hmcdrv_ctx.abmon[i]); } if (lc) setlocale(LC_TIME, lc); /* restore LC_TIME locale */ return 0; } /* * option parsing function */ static int hmcdrv_fuse_optproc(void *data, const char *arg, int key, struct fuse_args *outargs) { double tmo; /* timeout T in "-o entry/attr_timeout=T" */ mode_t mask; /* umask() of caller */ (void)outargs; switch (key) { case HMCDRV_FUSE_OPTKEY_NSUP: HMCDRV_FUSE_LOG(LOG_WARNING, "FUSE option \"%s\" ignored (unsupported)", arg); return 0; /* remove this option(s) */ case HMCDRV_FUSE_OPTKEY_RW: /* write options (unsupported) */ HMCDRV_FUSE_LOG(LOG_WARNING, "FUSE option \"%s\" ignored (r/o filesystem)", arg); return 0; /* remove this option(s) */ case HMCDRV_FUSE_OPTKEY_RO: /* "-o ro" or "-r" */ ((struct hmcdrv_fuse_opt *)data)->ro = 1; return 1; case HMCDRV_FUSE_OPTKEY_NATM: /* "-o noatime" */ ((struct hmcdrv_fuse_opt *)data)->noatime = 1; return 1; case HMCDRV_FUSE_OPTKEY_CTMO: /* "-o entry/attr_timeout=T" */ if (sscanf(arg, "%*[^=]=%lf", &tmo) == 1) { if (tmo < 1.0) tmo = 1.0; if (hmcdrv_ctx.ctmo < (HMCDRV_FUSE_CACHE_TMOFS + (time_t)tmo)) hmcdrv_ctx.ctmo = HMCDRV_FUSE_CACHE_TMOFS + (time_t)tmo; HMCDRV_FUSE_DBGLOG("option \"%s\" detected (cache timeout now is %" PRIdMAX " sec.)", arg, (intmax_t) hmcdrv_ctx.ctmo); } return 1; case HMCDRV_FUSE_OPTKEY_VERSION: fprintf(stdout, HMCDRV_FUSE_LOGHEAD "HMC drive DVD file system, version %s\n" "Copyright IBM Corp. 2015, 2017\n", HMCDRV_FUSE_RELEASE); exit(EXIT_SUCCESS); case HMCDRV_FUSE_OPTKEY_HELP: hmcdrv_fuse_usage(outargs->argv[0]); /* * Usage output needs to go to stdout to be consistent with * coding guidelines. FUSE versions before 3.0.0 print help * output to stderr. Redirect stderr to stdout here to enforce * consistent behavior. */ fflush(stderr); dup2(STDOUT_FILENO, STDERR_FILENO); fuse_opt_add_arg(outargs, "-ho"); hmcdrv_fuse_main(outargs); exit(EXIT_SUCCESS); case FUSE_OPT_KEY_NONOPT: /* normally the mount point */ mask = umask(0); umask(mask); hmcdrv_ctx.st.st_uid = getgid(); hmcdrv_ctx.st.st_gid = getuid(); hmcdrv_ctx.st.st_ino = 0; hmcdrv_ctx.st.st_size = 0; /* unknown */ hmcdrv_ctx.st.st_blksize = 2048U; /* DVD sector size */ hmcdrv_ctx.st.st_nlink = 2; /* minimum */ hmcdrv_ctx.st.st_mtime = time(NULL); hmcdrv_ctx.st.st_atime = hmcdrv_ctx.st.st_mtime; hmcdrv_ctx.st.st_ctime = hmcdrv_ctx.st.st_mtime; hmcdrv_ctx.st.st_mode = (S_IFDIR | S_IXUSR | S_IRUSR | S_IXGRP | S_IRGRP | S_IXOTH | S_IROTH) & ~(mask | (S_IWUSR | S_IWGRP | S_IWOTH)); HMCDRV_FUSE_DBGLOG("mount point is %s (uid = %u, gid = %u)", arg, hmcdrv_ctx.st.st_uid, hmcdrv_ctx.st.st_gid); return 1; default: HMCDRV_FUSE_DBGLOG("option \"%s\" passed to FUSE", arg); return 1; /* pass all other options to fuse_main() */ } } /* * FUSE.HMCDRVFS entry function */ int main(int argc, char *argv[]) { static struct fuse_opt lookup_opt[] = { HMCDRV_FUSE_OPT("hmctz=%s", hmctz, 0), HMCDRV_FUSE_OPT("hmclang=%s", hmclang, 0), FUSE_OPT_KEY("ro", HMCDRV_FUSE_OPTKEY_RO), FUSE_OPT_KEY("-r", HMCDRV_FUSE_OPTKEY_RO), FUSE_OPT_KEY("--read-only", HMCDRV_FUSE_OPTKEY_RO), FUSE_OPT_KEY("noatime", HMCDRV_FUSE_OPTKEY_NATM), FUSE_OPT_KEY("entry_timeout=%lf", HMCDRV_FUSE_OPTKEY_CTMO), FUSE_OPT_KEY("attr_timeout=%lf", HMCDRV_FUSE_OPTKEY_CTMO), /* unsupported (ignored) */ FUSE_OPT_KEY("subdir=%s", HMCDRV_FUSE_OPTKEY_NSUP), FUSE_OPT_KEY("use_ino", HMCDRV_FUSE_OPTKEY_NSUP), FUSE_OPT_KEY("readdir_ino", HMCDRV_FUSE_OPTKEY_NSUP), /* to be ignored on read-only filesystem */ FUSE_OPT_KEY("rw", HMCDRV_FUSE_OPTKEY_RW), FUSE_OPT_KEY("-w", HMCDRV_FUSE_OPTKEY_RW), FUSE_OPT_KEY("--rw", HMCDRV_FUSE_OPTKEY_RW), FUSE_OPT_KEY("atime", HMCDRV_FUSE_OPTKEY_RW), FUSE_OPT_KEY("max_write=%u", HMCDRV_FUSE_OPTKEY_RW), FUSE_OPT_KEY("big_writes", HMCDRV_FUSE_OPTKEY_RW), FUSE_OPT_KEY("hard_remove", HMCDRV_FUSE_OPTKEY_RW), FUSE_OPT_KEY("atomic_o_trunc", HMCDRV_FUSE_OPTKEY_RW), FUSE_OPT_KEY("negative_timeout=%lf", HMCDRV_FUSE_OPTKEY_RW), /* options, that exit immediately */ FUSE_OPT_KEY("-v", HMCDRV_FUSE_OPTKEY_VERSION), FUSE_OPT_KEY("--version", HMCDRV_FUSE_OPTKEY_VERSION), FUSE_OPT_KEY("-h", HMCDRV_FUSE_OPTKEY_HELP), FUSE_OPT_KEY("--help", HMCDRV_FUSE_OPTKEY_HELP), FUSE_OPT_END }; int ret; struct fuse_args args = FUSE_ARGS_INIT(argc, argv); memset(&hmcdrv_ctx.opt, 0, sizeof(hmcdrv_ctx.opt)); hmcdrv_ctx.pid = getpid(); fuse_opt_parse(&args, &hmcdrv_ctx.opt, lookup_opt, hmcdrv_fuse_optproc); /* the following options are required on a read-only media */ if (!hmcdrv_ctx.opt.ro) fuse_opt_add_arg(&args, "-oro"); if (!hmcdrv_ctx.opt.noatime) fuse_opt_add_arg(&args, "-onoatime"); if (hmcdrv_optproc_lang() != 0) { HMCDRV_FUSE_LOG(LOG_ERR, "Unknown HMC language in '-o hmclang=%s'", hmcdrv_ctx.opt.hmclang); exit(EXIT_FAILURE); } /* notice that there is no way to check the timezone parameter for * correct syntax, not in glibc as well as POSIX 1003.1 */ if (hmcdrv_ctx.opt.hmctz != NULL) HMCDRV_FUSE_DBGLOG("option \"-o hmctz=%s\" detected", hmcdrv_ctx.opt.hmctz); ret = hmcdrv_fuse_main(&args); fuse_opt_free_args(&args); return ret; } s390-tools-2.38.0/hmcdrvfs/lshmc000066400000000000000000000035151502674226300163200ustar00rootroot00000000000000#!/bin/bash # # lshmc - Print files from a HMC drive DVD # # Copyright IBM Corp. 2015, 2017 # # s390-tools is free software; you can redistribute it and/or modify # it under the terms of the MIT license. See LICENSE for details. # TOOL=$(basename $0) FTPDEV="/dev/hmcdrv" FTPCMD="dir" #------------------------------------------------------------------------------ # Print usage #------------------------------------------------------------------------------ function PrintUsage() { cat <<-EOD Usage: $(basename $0) [OPTIONS] [FILE] List information about the FILE(s) residing on a HMC drive DVD. Use OPTIONS described below or present simple wildcards on behalf of FILE. -h, --help Print this help, then exit. -v, --version Print version information, then exit. -s, --short Print only files, in a short listing format. EOD } #------------------------------------------------------------------------------ # Print version #------------------------------------------------------------------------------ function PrintVersion() { cat <<-EOD $TOOL: version %S390_TOOLS_VERSION% Copyright IBM Corp. 2015, 2017 EOD } FILES="" while [ $# -gt 0 ]; do case $1 in --help|-h) PrintUsage exit 0 ;; --version|-v) PrintVersion exit 0 ;; --short|-s) FTPCMD="nls" ;; -*) echo "$TOOL: Invalid option $1" echo "Try '$TOOL --help' for more information." exit 1 ;; *) FILES="$FILES $1" ;; esac shift done if [ ! -c "$FTPDEV" ]; then echo "$TOOL: Device \"$FTPDEV\" does not exist (modprobe hmcdrv ?)" exit 1 fi if [ ${#FILES} -eq 0 ]; then FILES="/" fi # open device $FTPDEV and assign the file descriptor to variable fd exec {fd}<>${FTPDEV} # echo the FTP command into device file echo "$FTPCMD $FILES" >&${fd} # and output the response cat <&${fd} STATUS="$?" # close file descriptor $fd exec {fd}>&- exit $STATUS s390-tools-2.38.0/hmcdrvfs/lshmc.8000066400000000000000000000042251502674226300164650ustar00rootroot00000000000000.\" Copyright IBM Corp. 2015, 2017 .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH LSHMC 8 "Mar 2015" "s390-tools" .\" disable hyphenation for words below .hw hmcdrv lshmc .\" save horizontal spacing to registers .nr default-word-spacing \n[.ss] .nr default-sentence-spacing \n[.sss] .\" define a macro for default horizontal spacing .de ssd .ss \n[default-word-spacing] \n[default-sentence-spacing] .. .SH NAME lshmc \- list .SM HMC drive .SM DVD contents .SH SYNOPSIS .TP \fBlshmc\fP [OPTIONS] [FILE] .SH DESCRIPTION List files on a .SM DVD in the .SM DVD drive of the Hardware Management Console .ss 0 ( .SM HMC ) .ssd \&. By default, the command lists all files in the root directory of the .SM DVD\c \&. Use FILE to list a different set of files. FILE can specify the path, relative to the .SM DVD root directory, for a directory and file and can contain the * and ? wildcard characters. .SH OPTIONS .TP .B "-h, --help" Print help text, then exit. .TP .B "-v, --version" Print version information, then exit. .TP .B "-s, --short" Print only regular files (no directories, symbolic links and special files), in a short listing format. .SH EXAMPLES To list the files in a .SM HMC drive .SM DVD root directory use: .PP .nf .RS .B # lshmc .RE .fi .PP In case the kernel module \fIhmcdrv\fP was not loaded in advance use: .PP .nf .RS .B # modprobe hmcdrv .B # lshmc .RE .fi .PP To list all HTML files in subdirectory \fIwww\fP use: .PP .nf .RS .B # lshmc /www/*.html .RE .fi .PP .SH DIAGNOSTICS The \fBlshmc\fP command needs access to device node \fI/dev/\:hmcdrv\fP. This node is created automatically when the \fIhmcdrv\fP kernel module is loaded (see Linux kernel configuration option .SM CONFIG_HMC_DRV\c ). The user process that runs the \fBlshmc\fP command must have sufficient privileges to read from and write to node \fI/dev/\:hmcdrv\fP. Use the commands \fBchown(1)\fP, \fBchgrp(1)\fP and/or \fBchmod(1)\fP on node \fI/dev/\:hmcdrv\fP to ensure this condition. .SH SEE ALSO .B ls(1), chown(1), chgrp(1), chmod(1) and Linux on System z: Device Drivers, Features and Commands s390-tools-2.38.0/hsavmcore/000077500000000000000000000000001502674226300154365ustar00rootroot00000000000000s390-tools-2.38.0/hsavmcore/Makefile000066400000000000000000000033021502674226300170740ustar00rootroot00000000000000# # Copyright IBM Corp. 2021 # # s390-tools is free software; you can redistribute it and/or modify # it under the terms of the MIT license. See LICENSE for details. # include ../common.mak ALL_CPPFLAGS += -D_FILE_OFFSET_BITS=64 ifeq (${HAVE_FUSE},0) all: $(SKIP) HAVE_FUSE=0 install: $(SKIP) HAVE_FUSE=0 else # HAVE_FUSE # # FUSE # FUSE_CFLAGS = $(shell $(PKG_CONFIG) --silence-errors --cflags fuse3) FUSE_LDLIBS = $(shell $(PKG_CONFIG) --silence-errors --libs fuse3) # # systemd # ifneq (${HAVE_SYSTEMD},0) ifeq ($(call check_header_prereq,"systemd/sd-daemon.h"),yes) SYSTEMD_CFLAGS = $(shell $(PKG_CONFIG) --silence-errors --cflags libsystemd) SYSTEMD_LDLIBS = $(shell $(PKG_CONFIG) --silence-errors --libs libsystemd) ALL_CPPFLAGS += -DHAVE_SYSTEMD else $(warning "systemd support disabled") endif endif ALL_CFLAGS += $(FUSE_CFLAGS) $(SYSTEMD_CFLAGS) LDLIBS += $(FUSE_LDLIBS) $(SYSTEMD_LDLIBS) -lpthread sources := $(wildcard *.c) objects := $(patsubst %.c,%.o,$(sources)) libs = $(rootdir)/libutil/libutil.a all: hsavmcore hsavmcore: $(objects) $(libs) $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ overlay.o: check-dep-fuse overlay.c overlay.h check-dep-fuse: $(call check_dep, \ "hsavmcore", \ "fuse.h", \ "fuse3-devel or libfuse3-dev", \ "HAVE_FUSE=0", \ "-DFUSE_USE_VERSION=30") touch check-dep-fuse install: all $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 hsavmcore \ $(DESTDIR)$(USRSBINDIR) $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 man/hsavmcore.8 \ $(DESTDIR)$(MANDIR)/man8 $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 man/hsavmcore.conf.5 \ $(DESTDIR)$(MANDIR)/man5 endif # HAVE_FUSE clean: rm -f hsavmcore $(objects) check-dep-fuse .PHONY: all install clean s390-tools-2.38.0/hsavmcore/cmdline_options.c000066400000000000000000000132311502674226300207700ustar00rootroot00000000000000/* * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include "lib/zt_common.h" #include "lib/util_opt.h" #include "lib/util_prg.h" #include "lib/util_log.h" #include "cmdline_options.h" static const struct util_prg prg = { .desc = "hsavmcore is designed to make the dump process with kdump more " "efficient. The HSA memory contains a part of the production " "kernel's memory. Use hsavmcore to cache this information and " "release HSA memory early in the process.", .copyright_vec = { { .owner = "IBM Corp.", .pub_first = 2021, .pub_last = 2021, }, UTIL_PRG_COPYRIGHT_END } }; static struct util_opt opt_vec[] = { UTIL_OPT_SECTION("CONFIGURATION"), { .option = { "config", required_argument, NULL, 'c' }, .argument = "CONFIGFILE", .desc = "Path to the configuration file.\n" "Default: no configuration file is used", }, { .option = { "vmcore", required_argument, NULL, 'C' }, .argument = "VMCOREFILE", .desc = "Path to the vmcore file.\n" "Default: " PROC_VMCORE, }, { .option = { "hsa", required_argument, NULL, 'H' }, .argument = "ZCOREHSAFILE", .desc = "Path to the zcore HSA file.\n" "Default: " ZCORE_HSA, }, { .option = { "workdir", required_argument, NULL, 'W' }, .argument = "WORKDIR", .desc = "Path to the work directory where temporary files can be " "stored.\nDefault: " WORKDIR, }, { .option = { "bmvmcore", required_argument, NULL, 'B' }, .argument = "VMCOREFILE", .desc = "Path to the target of the bind mount for the vmcore " "replacement.\nDefault: " PROC_VMCORE, }, { .option = { "swap", required_argument, NULL, 'S' }, .argument = "PATH", .desc = "Path to a swap device or file. The specified swap " "device or file must exist and have the proper swap " "format.\nDefault: no swap device or file is activated", }, { .option = { "hsasize", required_argument, NULL, 'T' }, .argument = "HSASIZE", .desc = "HSA size in bytes.\n" "Default: -1 (read from the zcore HSA file)", }, { .option = { "dbgfsmnt", no_argument, NULL, 'D' }, .desc = "Mount the debug file system.\n" "Default: the debug file system is not mounted", }, { .option = { "hsamem", no_argument, NULL, 'F' }, .desc = "Cache the HSA memory in regular memory.\n" "Default: the HSA memory is cached as a file within " "WORKDIR", }, { .option = { "norelhsa", no_argument, NULL, 'R' }, .desc = "Do NOT release the HSA memory after caching.\n" "Default: the HSA memory is released", }, { .option = { "nobindmnt", no_argument, NULL, 'N' }, .desc = "Do NOT replace the system's vmcore.\n" "Default: the system's vmcore is replaced", }, UTIL_OPT_SECTION("LOGGING"), { .option = { "verbose", no_argument, NULL, 'V' }, .desc = "Print verbose messages to stdout. Repeat this option " "for increased verbosity from just error messages to " "also include warning, information, debug, and trace " "messages. This option is intended for debugging", }, { .option = { "fusedbg", no_argument, NULL, 'G' }, .desc = "Enable FUSE debugging.\n" "Default: FUSE debugging is disabled", }, UTIL_OPT_SECTION("GENERAL OPTIONS"), UTIL_OPT_HELP, UTIL_OPT_VERSION, UTIL_OPT_END }; void parse_cmdline_options(int argc, char *argv[], struct config *config) { int opt, ret; util_prg_init(&prg); util_opt_init(opt_vec, NULL); /* Parse given command-line config */ while (1) { opt = util_opt_getopt_long(argc, argv); if (opt == -1) break; switch (opt) { case 'h': util_prg_print_help(); util_opt_print_help(); exit(EXIT_SUCCESS); case 'v': util_prg_print_version(); exit(EXIT_SUCCESS); case 'V': config->verbose++; util_log_set_level(config->verbose); break; case 'c': ret = update_config_from_file(optarg, config); if (ret < 0) exit(EXIT_FAILURE); util_log_set_level(config->verbose); break; case 'C': strncpy(config->vmcore_path, optarg, sizeof(config->vmcore_path) - 1); /* Ensure null termination */ config->vmcore_path[sizeof(config->vmcore_path) - 1] = '\0'; break; case 'H': strncpy(config->zcore_hsa_path, optarg, sizeof(config->zcore_hsa_path) - 1); /* Ensure null termination */ config->zcore_hsa_path[sizeof(config->zcore_hsa_path) - 1] = '\0'; break; case 'W': strncpy(config->workdir_path, optarg, sizeof(config->workdir_path) - 1); /* Ensure null termination */ config->workdir_path[sizeof(config->workdir_path) - 1] = '\0'; break; case 'B': strncpy(config->bind_mount_vmcore_path, optarg, sizeof(config->bind_mount_vmcore_path) - 1); /* Ensure null termination */ config->bind_mount_vmcore_path [sizeof(config->bind_mount_vmcore_path) - 1] = '\0'; break; case 'S': strncpy(config->swap, optarg, sizeof(config->swap) - 1); /* Ensure null termination */ config->swap[sizeof(config->swap) - 1] = '\0'; break; case 'T': { char *endptr; long hsa_size = strtol(optarg, &endptr, 0); if (*endptr != '\0' || hsa_size < -1 || hsa_size > INT_MAX) { fprintf(stderr, "The given HSA size is invalid.\n"); exit(EXIT_FAILURE); } config->hsa_size = hsa_size; break; } case 'D': config->mount_debugfs = true; break; case 'F': config->use_hsa_mem = true; break; case 'R': config->release_hsa = false; break; case 'N': config->bind_mount_vmcore = false; break; case 'G': config->fuse_debug = true; break; case '?': default: util_opt_print_parse_error(opt, argv); exit(EXIT_FAILURE); } } } s390-tools-2.38.0/hsavmcore/cmdline_options.h000066400000000000000000000007171502674226300210020ustar00rootroot00000000000000/* * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef _HSAVMCORE_CMDLINE_OPTIONS_H #define _HSAVMCORE_CMDLINE_OPTIONS_H #include "config.h" /* * Parses the given command-line options and adjusts the application's * configuration accordingly. */ void parse_cmdline_options(int argc, char *argv[], struct config *config); #endif s390-tools-2.38.0/hsavmcore/common.h000066400000000000000000000010711502674226300170760ustar00rootroot00000000000000/* * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef _HSAVMCORE_COMMON_H #define _HSAVMCORE_COMMON_H #define NAME "hsavmcore" #define DEBUGFS_MOUNT_POINT "/sys/kernel/debug" #define ZCORE_HSA DEBUGFS_MOUNT_POINT "/zcore/hsa" #define VMCORE_FILE "vmcore" #define PROC_VMCORE "/proc/" VMCORE_FILE #define WORKDIR "/var/crash" #define HSA_CACHE_FILE NAME "-hsa-cache.bin" #define OVERLAY_MOUNT_POINT "/tmp/" NAME "-overlay/" #endif s390-tools-2.38.0/hsavmcore/config.c000066400000000000000000000142761502674226300170610ustar00rootroot00000000000000/* * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include "lib/util_libc.h" #include "lib/util_log.h" #include "config.h" #define CONFIG_LINE_MAX_SIZE 1024 /* * Supported configuration parameters */ #define CONFIG_VERBOSE "verbose" #define CONFIG_WORKDIR "workdir" #define CONFIG_HSA_SIZE "hsa_size" #define CONFIG_MOUNT_DEBUGFS "mount_debugfs" #define CONFIG_USE_HSA_MEM "use_hsa_mem" #define CONFIG_RELEASE_HSA "release_hsa" #define CONFIG_BIND_MOUNT_VMCORE "bind_mount_vmcore" #define CONFIG_FUSE_DEBUG "fuse_debug" #define CONFIG_SWAP "swap" static char *get_value_str(char *line) { char *ptr = strchr(line, '='); if (!ptr) return NULL; return util_strstrip(ptr + 1); } static int parse_bool(char *line, int linenum, const char *name, bool *value) { const char *value_str; unsigned long value_num; char *endptr; value_str = get_value_str(line); if (!value_str) { util_log_print(UTIL_LOG_ERROR, "config line %d: value expected for %s\n", linenum, name); return -1; } util_log_print(UTIL_LOG_DEBUG, "config parse bool: %s\n", value_str); value_num = strtoul(value_str, &endptr, 0); if (*endptr != '\0' || (value_num != 0 && value_num != 1)) { util_log_print(UTIL_LOG_ERROR, "config line %d: invalid value for %s\n", linenum, name); return -1; } *value = value_num; return 0; } static int parse_int(char *line, int linenum, const char *name, int min_value, int max_value, int *value) { const char *value_str; long value_num; char *endptr; value_str = get_value_str(line); if (!value_str) { util_log_print(UTIL_LOG_ERROR, "config line %d: value expected for %s\n", linenum, name); return -1; } util_log_print(UTIL_LOG_DEBUG, "config parse int: %s\n", value_str); value_num = strtol(value_str, &endptr, 0); if (*endptr != '\0' || value_num < min_value || value_num > max_value) { util_log_print(UTIL_LOG_ERROR, "config line %d: invalid value for %s\n", linenum, name); return -1; } *value = value_num; return 0; } static int parse_str(char *line, int linenum, const char *name, int min_size, int max_size, char *value) { const char *value_str; int size; value_str = get_value_str(line); if (!value_str) { util_log_print(UTIL_LOG_ERROR, "config line %d: value expected for %s\n", linenum, name); return -1; } util_log_print(UTIL_LOG_DEBUG, "config parse string: %s\n", value_str); size = strlen(value_str); if ((min_size >= 0 && size < min_size) || size > max_size) { util_log_print(UTIL_LOG_ERROR, "config line %d: invalid value for %s\n", linenum, name); return -1; } strncpy(value, value_str, max_size); /* Ensure null termination */ value[max_size] = '\0'; return 0; } static int parse_line(char *line, int linenum, struct config *config) { if (strncmp(line, CONFIG_VERBOSE, strlen(CONFIG_VERBOSE)) == 0) { if (parse_int(line, linenum, CONFIG_VERBOSE, UTIL_LOG_ERROR, UTIL_LOG_TRACE, &config->verbose)) return -1; } else if (strncmp(line, CONFIG_WORKDIR, strlen(CONFIG_WORKDIR)) == 0) { if (parse_str(line, linenum, CONFIG_WORKDIR, 0, sizeof(config->workdir_path) - 1, config->workdir_path)) return -1; } else if (strncmp(line, CONFIG_HSA_SIZE, strlen(CONFIG_HSA_SIZE)) == 0) { if (parse_int(line, linenum, CONFIG_HSA_SIZE, -1, INT_MAX, &config->hsa_size)) return -1; } else if (strncmp(line, CONFIG_MOUNT_DEBUGFS, strlen(CONFIG_MOUNT_DEBUGFS)) == 0) { if (parse_bool(line, linenum, CONFIG_MOUNT_DEBUGFS, &config->mount_debugfs)) return -1; } else if (strncmp(line, CONFIG_USE_HSA_MEM, strlen(CONFIG_USE_HSA_MEM)) == 0) { if (parse_bool(line, linenum, CONFIG_USE_HSA_MEM, &config->use_hsa_mem)) return -1; } else if (strncmp(line, CONFIG_RELEASE_HSA, strlen(CONFIG_RELEASE_HSA)) == 0) { if (parse_bool(line, linenum, CONFIG_RELEASE_HSA, &config->release_hsa)) return -1; } else if (strncmp(line, CONFIG_BIND_MOUNT_VMCORE, strlen(CONFIG_BIND_MOUNT_VMCORE)) == 0) { if (parse_bool(line, linenum, CONFIG_BIND_MOUNT_VMCORE, &config->bind_mount_vmcore)) return -1; } else if (strncmp(line, CONFIG_FUSE_DEBUG, strlen(CONFIG_FUSE_DEBUG)) == 0) { if (parse_bool(line, linenum, CONFIG_FUSE_DEBUG, &config->fuse_debug)) return -1; } else if (strncmp(line, CONFIG_SWAP, strlen(CONFIG_SWAP)) == 0) { if (parse_str(line, linenum, CONFIG_SWAP, 0, sizeof(config->swap) - 1, config->swap)) return -1; } else { util_log_print(UTIL_LOG_ERROR, "config line %d: unknown configuration '%s'\n", linenum, line); return -1; } return 0; } void init_config(struct config *config) { memset(config, 0, sizeof(struct config)); strncpy(config->vmcore_path, PROC_VMCORE, sizeof(config->vmcore_path) - 1); strncpy(config->zcore_hsa_path, ZCORE_HSA, sizeof(config->zcore_hsa_path) - 1); strncpy(config->workdir_path, WORKDIR, sizeof(config->workdir_path) - 1); strncpy(config->bind_mount_vmcore_path, PROC_VMCORE, sizeof(config->bind_mount_vmcore_path) - 1); config->hsa_size = -1; config->mount_debugfs = false; config->use_hsa_mem = false; config->release_hsa = true; config->bind_mount_vmcore = true; config->fuse_debug = false; } int update_config_from_file(const char *config_path, struct config *config) { char line[CONFIG_LINE_MAX_SIZE], *ptr; int ret = 0, linenum; FILE *fp; fp = fopen(config_path, "r"); if (!fp) { util_log_print(UTIL_LOG_ERROR, "Couldn't open config file %s\n", config_path); return -1; } /* Read the given configuration file linewise and parse parameters */ linenum = 0; while (fgets(line, sizeof(line), fp)) { linenum++; ptr = util_strstrip(line); /* Skip empty or comment lines */ if (ptr[0] == '\0' || ptr[0] == '#') continue; util_log_print(UTIL_LOG_DEBUG, "config line %d: %s\n", linenum, ptr); ret = parse_line(ptr, linenum, config); if (ret < 0) break; } fclose(fp); return ret; } s390-tools-2.38.0/hsavmcore/config.h000066400000000000000000000030611502674226300170540ustar00rootroot00000000000000/* * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef _HSAVMCORE_CONFIG_H #define _HSAVMCORE_CONFIG_H #include #include #include "common.h" /* * This represents the application's configuration. */ struct config { /* Log message level */ int verbose; /* Path to the system's vmcore file */ char vmcore_path[PATH_MAX]; /* Path to the system's zcore hsa file */ char zcore_hsa_path[PATH_MAX]; /* * Path to a directory where the application could create temporary * files. */ char workdir_path[PATH_MAX]; /* Path to a bind-mount target for vmcore Overlay */ char bind_mount_vmcore_path[PATH_MAX]; /* Path to a swap device/file */ char swap[PATH_MAX]; /* HSA memory size */ int hsa_size; /* Indicates whether the debugfs shall be mounted */ bool mount_debugfs; /* Indicates whether the HSA memory file reader shall be used */ bool use_hsa_mem; /* Indicates whether the HSA memory shall be released after caching */ bool release_hsa; /* Indicates whether a bind-mount of vmcore Proxy shall be enabled */ bool bind_mount_vmcore; /* Indicates whether the FUSE debug messages shall be enabled */ bool fuse_debug; }; /* * Initializes the application's configuration to its default values. */ void init_config(struct config *config); /* * Updates the application's configuration from the given configuration file. */ int update_config_from_file(const char *config_path, struct config *config); #endif s390-tools-2.38.0/hsavmcore/hsa.c000066400000000000000000000100521502674226300163530ustar00rootroot00000000000000/* * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include "lib/util_file.h" #include "lib/util_log.h" #include "hsa.h" long get_hsa_size(const char *zcore_hsa_path) { long size; int ret; util_log_print(UTIL_LOG_DEBUG, "Reading HSA memory size from %s\n", zcore_hsa_path); /* Read HSA size */ ret = util_file_read_l(&size, 16, zcore_hsa_path); if (ret < 0) { util_log_print(UTIL_LOG_ERROR, "File read failed (%s)\n", strerror(errno)); return -1; } return size; } long get_hsa_vmcore_offset(const char *vmcore_path) { Elf64_Ehdr elf_hdr; int fd = -1, n, i; long offset = -1; util_log_print(UTIL_LOG_DEBUG, "Reading HSA memory offset from vmcore %s\n", vmcore_path); /* Open vmcore file */ fd = open(vmcore_path, O_RDONLY); if (fd < 0) { util_log_print(UTIL_LOG_ERROR, "open syscall failed (%s)\n", strerror(errno)); goto fail; } util_log_print(UTIL_LOG_DEBUG, "Reading vmcore ELF header\n"); /* Read ELF header */ n = read(fd, &elf_hdr, sizeof(Elf64_Ehdr)); if (n < 0) { util_log_print(UTIL_LOG_ERROR, "read syscall failed (%s)\n", strerror(errno)); goto fail; } else if (n != sizeof(Elf64_Ehdr)) { util_log_print(UTIL_LOG_ERROR, "read syscall read less data than expected (%s)\n", strerror(errno)); goto fail; } /* Verify ELF header */ if ((memcmp(elf_hdr.e_ident, ELFMAG, SELFMAG) != 0) || elf_hdr.e_type != ET_CORE || elf_hdr.e_machine != EM_S390 || elf_hdr.e_ident[EI_CLASS] != ELFCLASS64) { util_log_print(UTIL_LOG_ERROR, "Invalid vmcore ELF header\n"); goto fail; } util_log_print(UTIL_LOG_DEBUG, "Reading vmcore ELF program header(s)\n"); /* Read ELF program header(s) */ n = lseek(fd, elf_hdr.e_phoff, SEEK_SET); if (n < 0) { util_log_print(UTIL_LOG_ERROR, "lseek syscall failed (%s)\n", strerror(errno)); goto fail; } /* * Go through all ELF program headers and find one * that starts at physical/virtual address 0x0. */ for (i = 0; i < elf_hdr.e_phnum; i++) { Elf64_Phdr elf_phdr; util_log_print(UTIL_LOG_DEBUG, "Reading vmcore ELF program header #%d\n", i); n = read(fd, &elf_phdr, sizeof(Elf64_Phdr)); if (n < 0) { util_log_print(UTIL_LOG_ERROR, "read syscall failed (%s)\n", strerror(errno)); goto fail; } else if (n != sizeof(Elf64_Phdr)) { util_log_print(UTIL_LOG_ERROR, "read syscall read less data than expected (%s)\n", strerror(errno)); goto fail; } util_log_print(UTIL_LOG_DEBUG, "vmcore ELF program segment #%d: type=%lx vaddr=%lx paddr=%lx offset=%lx\n", i, elf_phdr.p_type, elf_phdr.p_vaddr, elf_phdr.p_paddr, elf_phdr.p_offset); /* HSA memory starts at physical/virtual address 0x0 */ if (elf_phdr.p_type == PT_LOAD && elf_phdr.p_vaddr == 0 && elf_phdr.p_paddr == 0) { offset = elf_phdr.p_offset; break; } } if (offset < 0) { util_log_print(UTIL_LOG_ERROR, "Couldn't find HSA memory offset in vmcore\n"); goto fail; } util_log_print(UTIL_LOG_DEBUG, "HSA memory vmcore offset %lx\n", offset); close(fd); return offset; fail: if (fd >= 0) close(fd); return -1; } int release_hsa(const char *zcore_hsa_path) { int ret; util_log_print(UTIL_LOG_INFO, "Release HSA memory via %s\n", zcore_hsa_path); /* Release HSA memory */ ret = util_file_write_s("0", zcore_hsa_path); if (ret < 0) { util_log_print(UTIL_LOG_ERROR, "File write failed (%s)\n", strerror(errno)); return -1; } /* Verify that HSA memory has been released */ if (get_hsa_size(zcore_hsa_path) > 0) { util_log_print(UTIL_LOG_ERROR, "HSA memory release failed\n"); return -1; } util_log_print(UTIL_LOG_INFO, "HSA memory successfully released\n"); return 0; } s390-tools-2.38.0/hsavmcore/hsa.h000066400000000000000000000033711502674226300163660ustar00rootroot00000000000000/* * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef _HSAVMCORE_HSA_H #define _HSAVMCORE_HSA_H #include /* * The interface to a HSA memory reader. * This interface must be implemented by a concrete HSA memory reader. */ struct hsa_reader { /* Total HSA memory size */ long hsa_size; /* Offset of HSA memory in /proc/vmcore */ long hsa_vmcore_offset; /* Destroys a concrete HSA memory reader */ void (*destroy)(struct hsa_reader *self); /* Reads a HSA memory block given by offset and size */ int (*read_at)(struct hsa_reader *self, long offset, void *buf, int size); }; static inline long hsa_get_size(struct hsa_reader *self) { return self->hsa_size; } static inline long hsa_get_vmcore_offset(struct hsa_reader *self) { return self->hsa_vmcore_offset; } static inline void destroy_hsa_reader(struct hsa_reader *self) { self->destroy(self); } static inline int read_hsa_at(struct hsa_reader *self, long offset, void *buf, int size) { return self->read_at(self, offset, buf, size); } /* * Reads total HSA memory size from /sys/kernel/debug/zcore/hsa. */ long get_hsa_size(const char *zcore_hsa_path); /* * Returns the offset of HSA memory in /proc/vmcore. */ long get_hsa_vmcore_offset(const char *vmcore_path); /* * Releases HSA memory. */ int release_hsa(const char *zcore_hsa_path); /* * Returns a pointer to the enclosing struct which contains * the variable pointed to by the given pointer as a member. */ #define container_of(ptr, type, member) \ ({ \ const typeof(((type *)NULL)->member) *mptr = (ptr); \ (type *)((char *)mptr - offsetof(type, member)); \ }) #endif s390-tools-2.38.0/hsavmcore/hsa_file.c000066400000000000000000000122541502674226300173600ustar00rootroot00000000000000/* * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include "lib/zt_common.h" #include "lib/util_log.h" #include "common.h" #include "hsa.h" #include "hsa_file.h" struct hsa_file_reader { struct hsa_reader super; /* Temporary file containing a copy of the HSA memory */ int fd; }; static void destroy(struct hsa_reader *super) { struct hsa_file_reader *self = container_of(super, struct hsa_file_reader, super); close(self->fd); free(self); } static int read_at(struct hsa_reader *super, long offset, void *buf, int size) { struct hsa_file_reader *self = container_of(super, struct hsa_file_reader, super); long n, nread = 0; util_log_print(UTIL_LOG_DEBUG, "HSA file read: offset=%lx size=%x\n", offset, size); /* Validate given offset */ if (offset >= super->hsa_size) return 0; /* Validate given size */ size = MIN(super->hsa_size - offset, size); n = lseek(self->fd, offset, SEEK_SET); if (n < 0) { util_log_print(UTIL_LOG_ERROR, "lseek syscall failed (%s)\n", strerror(errno)); return -1; } while (size) { n = read(self->fd, buf + nread, size); if (n < 0) { util_log_print(UTIL_LOG_ERROR, "read syscall failed (%s)\n", strerror(errno)); return -1; } else if (n == 0) { break; } nread += n; size -= n; } return nread; } static int copy_hsa_to_file(const char *vmcore_path, const char *workdir_path, long size, long offset) { int fd_in = -1, fd_out = -1; char cache_file_path[PATH_MAX]; long n; snprintf(cache_file_path, sizeof(cache_file_path), "%s/%s", workdir_path, HSA_CACHE_FILE); util_log_print(UTIL_LOG_DEBUG, "Copy HSA memory from vmcore %s to cache file %s\n", vmcore_path, cache_file_path); /* Open vmcore file */ fd_in = open(vmcore_path, O_RDONLY); if (fd_in < 0) { util_log_print(UTIL_LOG_ERROR, "open syscall failed (%s)\n", strerror(errno)); goto fail; } /* Open cache file */ fd_out = open(cache_file_path, O_RDWR | O_CREAT | O_TRUNC, 0644); if (fd_out < 0) { util_log_print(UTIL_LOG_ERROR, "open syscall failed (%s)\n", strerror(errno)); goto fail; } /* Unlink cache file to auto-delete it on close */ n = unlink(cache_file_path); if (n < 0) { util_log_print(UTIL_LOG_ERROR, "unlink syscall failed (%s)\n", strerror(errno)); goto fail; } /* Copy HSA memory to cache file */ n = lseek(fd_in, offset, SEEK_SET); if (n < 0) { util_log_print(UTIL_LOG_ERROR, "lseek syscall failed (%s)\n", strerror(errno)); goto fail; } /* Copy HSA memory chunkwise to the temporary file */ while (size) { char buf[1024]; long nread, nwrite; /* Read a chunk from vmcore */ nread = MIN((long)sizeof(buf), size); n = read(fd_in, buf, nread); if (n < 0) { util_log_print(UTIL_LOG_ERROR, "read syscall failed (%s)\n", strerror(errno)); goto fail; } else if (n == 0) { util_log_print(UTIL_LOG_ERROR, "read syscall read less data than expected\n"); goto fail; } /* Write a chunk to cache file */ nwrite = n; n = write(fd_out, buf, nwrite); if (n < 0) { util_log_print(UTIL_LOG_ERROR, "write syscall failed (%s)\n", strerror(errno)); goto fail; } else if (n != nwrite) { util_log_print(UTIL_LOG_ERROR, "write syscall wrote less data than expected\n"); goto fail; } size -= n; } /* Reset cache file position */ n = lseek(fd_out, 0, SEEK_SET); if (n < 0) { util_log_print(UTIL_LOG_ERROR, "lseek syscall failed (%s)\n", strerror(errno)); goto fail; } close(fd_in); return fd_out; fail: if (fd_in >= 0) close(fd_in); if (fd_out >= 0) close(fd_out); return -1; } struct hsa_reader *make_hsa_file_reader(const char *zcore_hsa_path, const char *vmcore_path, const char *workdir_path, long hsa_size, bool release_hsa_flag) { struct hsa_file_reader *self; long hsa_vmcore_offset; int fd; /* Calculate HSA size if not given by user */ if (hsa_size < 0) { hsa_size = get_hsa_size(zcore_hsa_path); if (hsa_size <= 0) return NULL; } hsa_vmcore_offset = get_hsa_vmcore_offset(vmcore_path); if (hsa_vmcore_offset < 0) return NULL; util_log_print(UTIL_LOG_INFO, "HSA: size=%lx vmcore offset=%lx\n", hsa_size, hsa_vmcore_offset); /* * Store the whole HSA memory from /proc/vmcore to a temporary file * before releasing HSA. */ fd = copy_hsa_to_file(vmcore_path, workdir_path, hsa_size, hsa_vmcore_offset); if (fd < 0) return NULL; if (release_hsa_flag) { if (release_hsa(zcore_hsa_path)) { close(fd); return NULL; } } self = malloc(sizeof(struct hsa_file_reader)); if (!self) { util_log_print(UTIL_LOG_ERROR, "malloc failed\n"); close(fd); return NULL; } self->super.hsa_size = hsa_size; self->super.hsa_vmcore_offset = hsa_vmcore_offset; self->super.destroy = destroy; self->super.read_at = read_at; self->fd = fd; return &self->super; } s390-tools-2.38.0/hsavmcore/hsa_file.h000066400000000000000000000013331502674226300173610ustar00rootroot00000000000000/* * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef _HSAVMCORE_HSA_FILE_H #define _HSAVMCORE_HSA_FILE_H #include #include "hsa.h" /* * This concrete HSA memory reader copies the whole HSA memory from /proc/vmcore * to a temporary file. * In order for it to work, the system must provide enough file storage. * The advantage of this reader is that it doesn't require extra memory for * caching. */ struct hsa_reader *make_hsa_file_reader(const char *zcore_hsa_path, const char *vmcore_path, const char *workdir_path, long hsa_size, bool release_hsa_flag); #endif s390-tools-2.38.0/hsavmcore/hsa_mem.c000066400000000000000000000063061502674226300172200ustar00rootroot00000000000000/* * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include "lib/zt_common.h" #include "lib/util_log.h" #include "hsa.h" #include "hsa_mem.h" struct hsa_mem_reader { struct hsa_reader super; unsigned char cache[]; }; static void destroy(struct hsa_reader *super) { struct hsa_mem_reader *self = container_of(super, struct hsa_mem_reader, super); free(self); } static int read_at(struct hsa_reader *super, long offset, void *buf, int size) { struct hsa_mem_reader *self = container_of(super, struct hsa_mem_reader, super); util_log_print(UTIL_LOG_DEBUG, "HSA file read: offset=%lx size=%x\n", offset, size); /* Validate given offset */ if (offset >= super->hsa_size) return 0; /* Validate given size */ size = MIN(super->hsa_size - offset, size); memcpy(buf, self->cache + offset, size); return size; } static int read_hsa(const char *vmcore_path, long offset, void *buf, int size) { long n, nread = 0; int fd = -1; util_log_print(UTIL_LOG_DEBUG, "Read HSA memory from vmcore %s\n", vmcore_path); /* Open vmcore file */ fd = open(vmcore_path, O_RDONLY); if (fd < 0) { util_log_print(UTIL_LOG_ERROR, "open syscall failed (%s)\n", strerror(errno)); goto fail; } n = lseek(fd, offset, SEEK_SET); if (n < 0) { util_log_print(UTIL_LOG_ERROR, "lseek syscall failed (%s)\n", strerror(errno)); goto fail; } /* Read HSA memory */ while (size) { n = read(fd, buf + nread, size); if (n < 0) { util_log_print(UTIL_LOG_ERROR, "read syscall failed (%s)\n", strerror(errno)); goto fail; } else if (n == 0) { util_log_print(UTIL_LOG_ERROR, "read syscall read less data than expected\n"); goto fail; } nread += n; size -= n; } close(fd); return 0; fail: if (fd >= 0) close(fd); return -1; } struct hsa_reader *make_hsa_mem_reader(const char *zcore_hsa_path, const char *vmcore_path, long hsa_size, bool release_hsa_flag) { struct hsa_mem_reader *self; long hsa_vmcore_offset; /* Calculate HSA size if not given by user */ if (hsa_size < 0) { hsa_size = get_hsa_size(zcore_hsa_path); if (hsa_size <= 0) return NULL; } hsa_vmcore_offset = get_hsa_vmcore_offset(vmcore_path); if (hsa_vmcore_offset < 0) return NULL; util_log_print(UTIL_LOG_INFO, "HSA: size=%lx vmcore offset=%lx\n", hsa_size, hsa_vmcore_offset); self = malloc(sizeof(struct hsa_mem_reader) + hsa_size); if (!self) { util_log_print(UTIL_LOG_ERROR, "malloc failed\n"); return NULL; } /* Cache the whole HSA memory from /proc/vmcore before releasing HSA */ if (read_hsa(vmcore_path, hsa_vmcore_offset, self->cache, hsa_size)) { free(self); return NULL; } if (release_hsa_flag) { if (release_hsa(zcore_hsa_path)) { free(self); return NULL; } } self->super.hsa_size = hsa_size; self->super.hsa_vmcore_offset = hsa_vmcore_offset; self->super.destroy = destroy; self->super.read_at = read_at; return &self->super; } s390-tools-2.38.0/hsavmcore/hsa_mem.h000066400000000000000000000012171502674226300172210ustar00rootroot00000000000000/* * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef _HSAVMCORE_HSA_MEM_H #define _HSAVMCORE_HSA_MEM_H #include #include "hsa.h" /* * This concrete HSA memory reader reads the whole HSA memory from /proc/vmcore * and caches it all in an internal memory buffer. * In order for it to work, the system must provide enough memory or swap space. */ struct hsa_reader *make_hsa_mem_reader(const char *zcore_hsa_path, const char *vmcore_path, long hsa_size, bool release_hsa_flag); #endif s390-tools-2.38.0/hsavmcore/initramfs/000077500000000000000000000000001502674226300174325ustar00rootroot00000000000000s390-tools-2.38.0/hsavmcore/initramfs/fedora-rhel/000077500000000000000000000000001502674226300216225ustar00rootroot00000000000000s390-tools-2.38.0/hsavmcore/initramfs/fedora-rhel/README.md000066400000000000000000000047501502674226300231070ustar00rootroot00000000000000 # Setup ## Configure crashkernel ```shell sudo grubby --args "crashkernel=512M" --update-kernel=ALL sudo reboot ``` ## Production kernel's root file system - kdump mounts the production kernel's root file system under **/sysroot**. ## Dependencies ```shell sudo dnf install -y fuse3 fuse3-devel systemd-devel ``` ## Build hsavmcore ```shell make -C s390-tools/hsavmcore ``` ## Install hsavmcore ```shell sudo cp s390-tools/hsavmcore/hsavmcore /usr/sbin/ ``` ## Create swap file ```shell sudo dd if=/dev/zero of=/var/crash/swap.img bs=1M count=1024 sudo mkswap /var/crash/swap.img ``` ## Install hsavmcore.conf ### Test configuration - Doesn't require HSA support #### HSA cache in file ```shell cat < #include #include #include #include #include #include #ifdef HAVE_SYSTEMD #include #endif #include "lib/util_log.h" #include "common.h" #include "config.h" #include "cmdline_options.h" #include "mount.h" #include "swap.h" #include "hsa.h" #include "hsa_mem.h" #include "hsa_file.h" #include "proxy.h" #include "overlay.h" #define MAX_WAIT_VMCORE_OVERLAY_SECS 5 static int bind_mount_vmcore(const char *src, const char *target, int max_wait_secs) { struct stat st; int ret; util_log_print(UTIL_LOG_INFO, "Wait %d secs for %s to appear\n", max_wait_secs, src); while (max_wait_secs--) { if (!stat(src, &st)) break; sleep(1); } if (stat(src, &st)) { util_log_print(UTIL_LOG_ERROR, "Timeout for appearance of %s\n", src); return -1; } ret = bind_mount(src, target); if (ret < 0) return -1; return 0; } static void block_all_signals(void) { sigset_t signal_set; sigfillset(&signal_set); pthread_sigmask(SIG_BLOCK, &signal_set, NULL); } static void unblock_all_signals(void) { sigset_t signal_set; sigfillset(&signal_set); pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL); } static void *vmcore_overlay_server(void *arg) { struct vmcore_overlay *vmcore_overlay = (struct vmcore_overlay *)arg; int ret; util_log_print(UTIL_LOG_DEBUG, "vmcore overlay thread: start\n"); /* Unblock all signals because vmcore overlay handles them */ unblock_all_signals(); /* Blocks until a signal has been received or an error occurred */ ret = serve_vmcore_overlay(vmcore_overlay); util_log_print(UTIL_LOG_DEBUG, "vmcore overlay thread: end (%d)\n", ret); return (void *)(long)ret; } static void terminate_vmcore_overlay(pthread_t tid) { pthread_kill(tid, SIGINT); pthread_join(tid, NULL); } static int wait_for_vmcore_overlay(pthread_t tid) { int ret; pthread_join(tid, (void **)&ret); return ret; } int main(int argc, char *argv[]) { struct vmcore_overlay *vmcore_overlay; struct vmcore_proxy *vmcore_proxy; int exit_code = EXIT_SUCCESS, ret; struct hsa_reader *hsa_reader; pthread_t vmcore_overlay_tid; struct config config; init_config(&config); parse_cmdline_options(argc, argv, &config); if (strlen(config.swap)) { ret = swap_on(config.swap); if (ret < 0) { exit_code = EXIT_FAILURE; goto done; } } if (config.mount_debugfs) { ret = mount_debugfs(DEBUGFS_MOUNT_POINT); if (ret < 0) { exit_code = EXIT_FAILURE; goto swap_off; } } if (config.use_hsa_mem) hsa_reader = make_hsa_mem_reader(config.zcore_hsa_path, config.vmcore_path, config.hsa_size, config.release_hsa); else hsa_reader = make_hsa_file_reader(config.zcore_hsa_path, config.vmcore_path, config.workdir_path, config.hsa_size, config.release_hsa); if (!hsa_reader) { exit_code = EXIT_FAILURE; goto unmount_debugfs; } vmcore_proxy = make_vmcore_proxy(config.vmcore_path, hsa_reader); if (!vmcore_proxy) { exit_code = EXIT_FAILURE; goto destroy_hsa_reader; } vmcore_overlay = make_vmcore_overlay(vmcore_proxy, OVERLAY_MOUNT_POINT, config.fuse_debug); if (!vmcore_overlay) { exit_code = EXIT_FAILURE; goto destroy_vmcore_proxy; } /* vmcore overlay thread handles all signals */ block_all_signals(); /* Start vmcore overlay thread which handles file system calls */ ret = pthread_create(&vmcore_overlay_tid, NULL, vmcore_overlay_server, vmcore_overlay); if (ret < 0) { exit_code = EXIT_FAILURE; goto destroy_vmcore_overlay; } /* Bind mount /proc/vmcore */ if (config.bind_mount_vmcore) { ret = bind_mount_vmcore(OVERLAY_MOUNT_POINT "/" VMCORE_FILE, config.bind_mount_vmcore_path, MAX_WAIT_VMCORE_OVERLAY_SECS); if (ret < 0) { terminate_vmcore_overlay(vmcore_overlay_tid); exit_code = EXIT_FAILURE; goto destroy_vmcore_overlay; } } #ifdef HAVE_SYSTEMD /* Tell systemd that service is ready now */ ret = sd_notify(0, "READY=1"); if (ret <= 0) util_log_print(UTIL_LOG_WARN, "Failed to notify systemd (%d)\n", ret); #endif ret = wait_for_vmcore_overlay(vmcore_overlay_tid); if (ret < 0) exit_code = EXIT_FAILURE; #ifdef HAVE_SYSTEMD /* Tell systemd that service is stopping now */ ret = sd_notify(0, "STOPPING=1"); if (ret <= 0) util_log_print(UTIL_LOG_WARN, "Failed to notify systemd (%d)\n", ret); #endif unblock_all_signals(); if (config.bind_mount_vmcore) unmount_detach(config.bind_mount_vmcore_path); destroy_vmcore_overlay: destroy_vmcore_overlay(vmcore_overlay); destroy_vmcore_proxy: destroy_vmcore_proxy(vmcore_proxy); destroy_hsa_reader: destroy_hsa_reader(hsa_reader); unmount_debugfs: if (config.mount_debugfs) unmount_detach(DEBUGFS_MOUNT_POINT); swap_off: if (strlen(config.swap)) swap_off(config.swap); done: return exit_code; } s390-tools-2.38.0/hsavmcore/man/000077500000000000000000000000001502674226300162115ustar00rootroot00000000000000s390-tools-2.38.0/hsavmcore/man/hsavmcore.8000066400000000000000000000074171502674226300203020ustar00rootroot00000000000000.\" Copyright 2021 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH HSAVMCORE 8 "May 2021" "s390-tools" . .SH NAME hsavmcore - Enable kdump to release the HSA memory early in the dump process . .SH SYNOPSIS .B hsavmcore .RI [ OPTIONS ] . .SH DESCRIPTION .B hsavmcore is designed to make the dump process with kdump more efficient. The HSA memory contains a part of the production kernel's memory. Use hsavmcore to cache this information and release HSA memory early in the process. .PP Depending on the size of the production kernel's memory, writing the dump to persistent storage can be time consuming and prevent the HSA memory from being reused by other LPARs. . The .B hsavmcore tool performs these steps: .IP " 1)" Read the size of the HSA memory from .B /sys/kernel/debug/zcore/hsa. .IP " 2)" Cache the HSA memory content contained in .B /proc/vmcore either in regular memory or within the file system. .IP " 3)" Releases the HSA memory by writing to .B /sys/kernel/debug/zcore/hsa. .PP At this stage, the HSA memory region is unavailable to .B /proc/vmcore and cannot be used by kdump. . The .B hsavmcore tool now combines the cached HSA memory and the non-HSA memory from the original .B /proc/vmcore to create a replacement for .B /proc/vmcore. . The replacement .B /proc/vmcore can be processed as usual. . .SH OPTIONS .TP \fB\-h\fP or \fB\-\-help\fP Print usage information and exit. . .TP \fB\-v\fP or \fB\-\-version\fP Print version information and exit. . .TP \fB\-V\fP or \fB\-\-verbose\fP Print verbose messages to stdout. Repeat this option for increased verbosity from just error messages to also include warning, information, debug, and trace messages. This option is intended for debugging. . .TP \fB\-c\fP or \fB\-\-config\fP \fICONFIGFILE\fP Path to the configuration file. By default, no configuration file is used. . .TP \fB\-C\fP or \fB\-\-vmcore\fP \fIVMCOREFILE\fP Path to the vmcore file. Default: .B /proc/vmcore. . .TP \fB\-H\fP or \fB\-\-hsa\fP \fIZCOREHSAFILE\fP Path to the zcore HSA file. Default: .B /sys/kernel/debug/zcore/hsa. . .TP \fB\-W\fP or \fB\-\-workdir\fP \fIWORKDIR\fP Path to the work directory where temporary files can be stored. Default: .B /var/crash. . .TP \fB\-B\fP or \fB\-\-bmvmcore\fP \fIVMCOREFILE\fP Path to the target of the bind mount for the replacement vmcore file. Default: .B /proc/vmcore. . .TP \fB\-S\fP or \fB\-\-swap\fP \fIPATH\fP Path to a swap device or file. The specified swap device or file must exist and have the proper swap format. Default: no swap device or file is activated. . .TP \fB\-T\fP or \fB\-\-hsasize\fP \fIHSASIZE\fP HSA size in bytes. Used for testing purposes. Default: -1 (read from the zcore HSA file). . .TP \fB\-D\fP or \fB\-\-dbgfsmnt\fP Mount the debug file system. Default: the debug file system is not mounted. . .TP \fB\-F\fP or \fB\-\-hsamem\fP Cache the HSA memory in regular memory. Default: the HSA memory is cached as a file within WORKDIR. . .TP \fB\-R\fP or \fB\-\-norelhsa\fP Do NOT release the HSA memory after caching. Default: the HSA memory is released. . .TP \fB\-N\fP or \fB\-\-nobindmnt\fP Do NOT replace the system's vmcore file. Default: the system's vmcore file is replaced. . .TP \fB\-G\fP or \fB\-\-fusedbg\fP Enable FUSE debugging. Default: FUSE debugging is disabled. .RE . .SH EXAMPLES .TP .B To run hsavmcore on a kdump system during a stand-alone dump with default parameters: .RS 4 hsavmcore .br makedumpfile \-d 31 /proc/vmcore test-dump.elf .RE .TP .B To test hsavmcore with a vmcore copy and without being in a kdump system (for debugging): .RS 4 hsavmcore \-VVV \-T 0x1ffff000 \-C vmcore-dump.elf \-N \-R .br makedumpfile \-d 31 /tmp/hsavmcore-overlay/vmcore test-dump.elf .RE .SH SEE ALSO .BR hsavmcore.conf (5) s390-tools-2.38.0/hsavmcore/man/hsavmcore.conf.5000066400000000000000000000060321502674226300212130ustar00rootroot00000000000000.\" Copyright 2021 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH HSAVMCORE.CONF 5 "May 2021" "s390-tools" .SH NAME hsavmcore.conf \- Configuration file for the hsavmcore tool . .SH DESCRIPTION The .B hsavmcore.conf configuration file contains the configuration information for the .B hsavmcore tool. All specifications in the configuration file are optional. The command defaults apply for omitted parameters. . .SS "verbose" This parameter sets the verbosity level of the output messages. The following pre-defined numeric values can be used: . .RS 2 .IP "-" 2 \fB0\fP - show only error messages (default) .IP "-" 2 \fB1\fP - show error and warning messages .IP "-" 2 \fB2\fP - show error, warning and information messages .IP "-" 2 \fB3\fP - show error, warning, information and debug messages .IP "-" 2 \fB4\fP - show error, warning, information, debug and trace messages .RE .PP . .SS "mount_debugfs" Mount (1) or do not mount (0) debugfs. Use this configuration if the kdump kernel does not mount the debugfs during the boot process. . .SS "workdir" Specifies a work directory on the kdump system where the hsavmcore tool can create temporary files. This specification is required if .B use_hsa_mem is set to 0. . .SS "use_hsa_mem" Cache the HSA memory in regular memory (1) or in a file on a file system (0). . .SS "hsa_size" Specify a value, in bytes, for the HSA memory size instead of reading the size from .B /sys/kernel/debug/zcore/hsa. This parameter is intended only to test the .B hsavmcore tool without being in a kdump kernel. Specifying -1 falls back to reading the size from .B /sys/kernel/debug/zcore/hsa. . .SS "release_hsa" Release (1) or do not release (0) the HSA memory after it is cached by the .B hsavmcore tool. . .SS "bind_mount_vmcore" Replace (1) the original vmcore file with the new file created by the .B hsavmcore tool or keep the original file (0), which no longer contains the information from the HSA memory. Set this parameter to 1 if you intend to use kdump tools to create a core dump. . .SS "swap" Specify a swap device or file through its path in a kdump system. The specified swap device or file must exist and have the proper swap format. You might need a swap device because the amount of memory available in the kdump kernel during a stand-alone dump is limited to the size of the HSA memory. . .SS "fuse_debug" Enable (1) or disable (0) fuse debugging. . .SH EXAMPLES A complete configuration file could look like this: .nf ------------------------------ config file start ------------------------------ # Example configuration for hsavmcore # 0 - ERROR # 1 - WARN # 2 - INFO # 3 - DEBUG # 4 - TRACE verbose = 3 workdir = /var/crash mount_debugfs = 1 use_hsa_mem = 1 hsa_size = -1 release_hsa = 1 bind_mount_vmcore = 1 swap = /dev/disk/by-uuid/3cf6630b-4c4d-49ac-a0ae-0f5484cb5721 #swap = /swap.img fuse_debug = 0 ------------------------------ config file end ------------------------------ .fi . .SH SEE ALSO .BR hsavmcore (8) s390-tools-2.38.0/hsavmcore/mount.c000066400000000000000000000022751502674226300167520ustar00rootroot00000000000000/* * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include "lib/util_log.h" #include "mount.h" int mount_debugfs(const char *target) { int ret; util_log_print(UTIL_LOG_INFO, "Mount debugfs on %s\n", target); ret = mount("none", target, "debugfs", 0, NULL); if (ret) { util_log_print(UTIL_LOG_ERROR, "mount syscall failed (%s)\n", strerror(errno)); return -1; } return 0; } int bind_mount(const char *src, const char *target) { int ret; util_log_print(UTIL_LOG_INFO, "Bind mount %s on %s\n", src, target); ret = mount(src, target, "", MS_BIND, NULL); if (ret) { util_log_print(UTIL_LOG_ERROR, "mount syscall failed (%s)\n", strerror(errno)); return -1; } return 0; } int unmount_detach(const char *target) { int ret; util_log_print(UTIL_LOG_INFO, "Unmount detach %s\n", target); ret = umount2(target, MNT_DETACH); if (ret) { util_log_print(UTIL_LOG_ERROR, "umount2 syscall failed (%s)\n", strerror(errno)); return -1; } return 0; } s390-tools-2.38.0/hsavmcore/mount.h000066400000000000000000000005651502674226300167570ustar00rootroot00000000000000/* * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef _HSAVMCORE_MOUNT_H #define _HSAVMCORE_MOUNT_H int mount_debugfs(const char *target); int bind_mount(const char *src, const char *target); int unmount_detach(const char *target); #endif s390-tools-2.38.0/hsavmcore/overlay.c000066400000000000000000000105041502674226300172630ustar00rootroot00000000000000/* * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #define FUSE_USE_VERSION 30 #include #include "lib/util_log.h" #include "common.h" #include "overlay.h" #define ROOT_DIR "/" struct vmcore_overlay { struct vmcore_proxy *vmcore_proxy; char mount_point[PATH_MAX]; bool fuse_debug; }; static int vmcore_fuse_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi) { (void)fi; struct vmcore_overlay *overlay = fuse_get_context()->private_data; int ret = 0; memset(stbuf, 0, sizeof(struct stat)); if (strcmp(path, ROOT_DIR) == 0) { stbuf->st_mode = S_IFDIR | 0755; stbuf->st_nlink = 2; } else if (strcmp(path + 1, VMCORE_FILE) == 0) { stbuf->st_mode = S_IFREG | 0444; stbuf->st_nlink = 1; stbuf->st_size = vmcore_proxy_size(overlay->vmcore_proxy); } else { ret = -ENOENT; } return ret; } static int vmcore_fuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi, enum fuse_readdir_flags flags) { (void)offset; (void)fi; (void)flags; if (strcmp(path, ROOT_DIR) != 0) return -ENOENT; /* We have only one file */ filler(buf, ".", NULL, 0, 0); filler(buf, "..", NULL, 0, 0); filler(buf, VMCORE_FILE, NULL, 0, 0); return 0; } static int vmcore_fuse_open(const char *path, struct fuse_file_info *fi) { if (strcmp(path + 1, VMCORE_FILE) != 0) return -ENOENT; if ((fi->flags & O_ACCMODE) != O_RDONLY) return -EACCES; return 0; } static int vmcore_fuse_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { (void)fi; if (strcmp(path + 1, VMCORE_FILE) != 0) return -ENOENT; struct vmcore_overlay *overlay = fuse_get_context()->private_data; return read_vmcore_proxy_at(overlay->vmcore_proxy, offset, buf, size); } static int setup_fuse_args(struct fuse_args *args, const char *mount_point, bool debug) { int ret; ret = fuse_opt_add_arg(args, NAME); if (ret) goto done; /* Single-threaded */ ret = fuse_opt_add_arg(args, "-s"); if (ret) goto done; /* Foreground */ ret = fuse_opt_add_arg(args, "-f"); if (ret) goto done; /* Debugging */ if (debug) { ret = fuse_opt_add_arg(args, "-d"); if (ret) goto done; } ret = fuse_opt_add_arg(args, mount_point); if (ret) goto done; done: if (ret) return -1; else return 0; } struct vmcore_overlay *make_vmcore_overlay(struct vmcore_proxy *vmcore_proxy, const char *mount_point, bool fuse_debug) { struct vmcore_overlay *overlay; util_log_print(UTIL_LOG_INFO, "vmcore overlay: mountpoint=%s\n", mount_point); overlay = malloc(sizeof(struct vmcore_overlay)); if (!overlay) { util_log_print(UTIL_LOG_ERROR, "malloc failed\n"); return NULL; } overlay->vmcore_proxy = vmcore_proxy; strncpy(overlay->mount_point, mount_point, sizeof(overlay->mount_point) - 1); /* Ensure null termination */ overlay->mount_point[sizeof(overlay->mount_point) - 1] = '\0'; overlay->fuse_debug = fuse_debug; return overlay; } void destroy_vmcore_overlay(struct vmcore_overlay *overlay) { free(overlay); } /* * FUSE file system operations */ static struct fuse_operations vmcore_fuse_ops = { .getattr = vmcore_fuse_getattr, .readdir = vmcore_fuse_readdir, .open = vmcore_fuse_open, .read = vmcore_fuse_read, }; int serve_vmcore_overlay(struct vmcore_overlay *overlay) { struct fuse_args args = FUSE_ARGS_INIT(0, NULL); int ret; util_log_print(UTIL_LOG_DEBUG, "vmcore overlay: FUSE main\n"); ret = setup_fuse_args(&args, overlay->mount_point, overlay->fuse_debug); if (ret < 0) goto free_args; /* Create mount point */ ret = mkdir(overlay->mount_point, 0755); if (ret < 0) { util_log_print(UTIL_LOG_ERROR, "mkdir syscall failed (%s)\n", strerror(errno)); goto free_args; } /* * Run file system, blocks until a signal has been received or an error * occurred. */ fuse_main(args.argc, args.argv, &vmcore_fuse_ops, overlay); /* Remove mount point */ rmdir(overlay->mount_point); ret = 0; free_args: fuse_opt_free_args(&args); return ret; } s390-tools-2.38.0/hsavmcore/overlay.h000066400000000000000000000014241502674226300172710ustar00rootroot00000000000000/* * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef _HSAVMCORE_OVERLAY_H #define _HSAVMCORE_OVERLAY_H #include #include "proxy.h" /* * A vmcore Overlay exports a vmcore Proxy as a normal read-only file * that could be used, for instance, by *makedumpfile*. */ struct vmcore_overlay; struct vmcore_overlay *make_vmcore_overlay(struct vmcore_proxy *vmcore_proxy, const char *mount_point, bool fuse_debug); void destroy_vmcore_overlay(struct vmcore_overlay *overlay); /* * This method handles all file system calls and blocks until a signal arrives. */ int serve_vmcore_overlay(struct vmcore_overlay *overlay); #endif s390-tools-2.38.0/hsavmcore/proxy.c000066400000000000000000000106541502674226300167710ustar00rootroot00000000000000/* * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include "lib/zt_common.h" #include "lib/util_log.h" #include "proxy.h" struct vmcore_proxy { int vmcore_fd; long vmcore_size; struct hsa_reader *hsa_reader; }; static int read_file_at(int fd, long offset, void *buf, int size) { long n, nread = 0; util_log_print(UTIL_LOG_DEBUG, "vmcore proxy vmcore read: offset=%lx size=%x\n", offset, size); n = lseek(fd, offset, SEEK_SET); if (n < 0) { util_log_print(UTIL_LOG_ERROR, "lseek syscall failed (%s)\n", strerror(errno)); return -1; } while (size) { n = read(fd, buf + nread, size); if (n < 0) { util_log_print(UTIL_LOG_ERROR, "read syscall failed (%s)\n", strerror(errno)); return -1; } else if (n == 0) { break; } nread += n; size -= n; } return nread; } static long get_vmcore_size(int fd) { long n, size; /* Get vmcore file size */ n = lseek(fd, 0, SEEK_END); if (n < 0) { util_log_print(UTIL_LOG_ERROR, "lseek syscall failed (%s)\n", strerror(errno)); return 0; } size = n; /* Reset vmcore file position */ n = lseek(fd, 0, SEEK_SET); if (n < 0) { util_log_print(UTIL_LOG_ERROR, "lseek syscall failed (%s)\n", strerror(errno)); return 0; } return size; } struct vmcore_proxy *make_vmcore_proxy(const char *vmcore_path, struct hsa_reader *hsa_reader) { struct vmcore_proxy *proxy; int vmcore_fd; long vmcore_size; util_log_print(UTIL_LOG_INFO, "vmcore proxy: vmcore path=%s\n", vmcore_path); /* Open vmcore file */ vmcore_fd = open(vmcore_path, O_RDONLY); if (vmcore_fd < 0) { util_log_print(UTIL_LOG_ERROR, "open syscall failed (%s)\n", strerror(errno)); return NULL; } vmcore_size = get_vmcore_size(vmcore_fd); if (!vmcore_size) { close(vmcore_fd); return NULL; } util_log_print(UTIL_LOG_INFO, "vmcore proxy: vmcore size=%lx\n", vmcore_size); proxy = malloc(sizeof(struct vmcore_proxy)); if (!proxy) { util_log_print(UTIL_LOG_ERROR, "malloc failed\n"); close(vmcore_fd); return NULL; } proxy->vmcore_fd = vmcore_fd; proxy->vmcore_size = vmcore_size; proxy->hsa_reader = hsa_reader; return proxy; } void destroy_vmcore_proxy(struct vmcore_proxy *proxy) { close(proxy->vmcore_fd); free(proxy); } long vmcore_proxy_size(struct vmcore_proxy *proxy) { return proxy->vmcore_size; } int read_vmcore_proxy_at(struct vmcore_proxy *proxy, long offset, void *buf, int size) { const long hsa_size = hsa_get_size(proxy->hsa_reader); const long hsa_vmcore_offset = hsa_get_vmcore_offset(proxy->hsa_reader); long nread = 0; util_log_print(UTIL_LOG_DEBUG, "vmcore proxy read: offset=%lx size=%x\n", offset, size); /* * The caller might try to read beyond the maximum length of vmcore. * This guarantees the termination of the loop below in that case. */ size = MIN(proxy->vmcore_size - offset, size); /* * 0 HSA offset HSA offset + vmcore size * HSA size * * +---------------------+---------------------+-----------------------+ * | | | | * | vmcore 1st part | HSA memory region | vmcore 2nd part | * | | | | * +---------------------+---------------------+-----------------------+ */ while (size) { long n, nbyte; if (offset < hsa_vmcore_offset) { /* vmcore 1st part */ nbyte = MIN(hsa_vmcore_offset - offset, size); n = read_file_at(proxy->vmcore_fd, offset, buf + nread, nbyte); } else if (offset >= hsa_vmcore_offset && offset < (hsa_vmcore_offset + hsa_size)) { /* HSA memory region */ nbyte = MIN(hsa_vmcore_offset + hsa_size - offset, size); n = read_hsa_at(proxy->hsa_reader, offset - hsa_vmcore_offset, buf + nread, nbyte); } else { /* vmcore 2nd part */ nbyte = MIN(proxy->vmcore_size - offset, size); n = read_file_at(proxy->vmcore_fd, offset, buf + nread, nbyte); } if (n != nbyte) return -1; nread += n; size -= n; offset += n; } return nread; } s390-tools-2.38.0/hsavmcore/proxy.h000066400000000000000000000020071502674226300167670ustar00rootroot00000000000000/* * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef _HSAVMCORE_PROXY_H #define _HSAVMCORE_PROXY_H #include "hsa.h" /* * A vmcore Proxy combines the original /proc/vmcore file with a HSA memory * reader into a new interface which can be used to read vmcore data w/o being * aware that the HSA memory region is NOT contained in the file /proc/vmcore. * * After releasing the HSA memory, the original /proc/vmcore will contain * a *hole* where the HSA memory was located. The vmcore proxy hides this * inconvenience from the user of this interface. */ struct vmcore_proxy; struct vmcore_proxy *make_vmcore_proxy(const char *vmcore_path, struct hsa_reader *hsa_reader); void destroy_vmcore_proxy(struct vmcore_proxy *proxy); long vmcore_proxy_size(struct vmcore_proxy *proxy); int read_vmcore_proxy_at(struct vmcore_proxy *proxy, long offset, void *buf, int size); #endif s390-tools-2.38.0/hsavmcore/swap.c000066400000000000000000000016051502674226300165560ustar00rootroot00000000000000/* * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include "lib/util_log.h" #include "common.h" #include "swap.h" int swap_on(const char *path) { int ret; util_log_print(UTIL_LOG_INFO, "Swap on %s\n", path); ret = swapon(path, 0); if (ret) { util_log_print(UTIL_LOG_ERROR, "swapon syscall failed (%s)\n", strerror(errno)); return ret; } return 0; } int swap_off(const char *path) { int ret; util_log_print(UTIL_LOG_INFO, "Swap off %s\n", path); ret = swapoff(path); if (ret) { util_log_print(UTIL_LOG_ERROR, "swapoff syscall failed (%s)\n", strerror(errno)); return ret; } return 0; } s390-tools-2.38.0/hsavmcore/swap.h000066400000000000000000000004551502674226300165650ustar00rootroot00000000000000/* * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef _HSAVMCORE_SWAP_H #define _HSAVMCORE_SWAP_H int swap_on(const char *path); int swap_off(const char *path); #endif s390-tools-2.38.0/hsci/000077500000000000000000000000001502674226300143755ustar00rootroot00000000000000s390-tools-2.38.0/hsci/Makefile000066400000000000000000000006361502674226300160420ustar00rootroot00000000000000include ../common.mak all: install: hsci $(SED) -e 's/%S390_TOOLS_VERSION%/$(S390_TOOLS_RELEASE)/' \ < hsci >$(DESTDIR)$(BINDIR)/hsci; \ chown $(OWNER):$(GROUP) $(DESTDIR)$(BINDIR)/hsci; \ chmod 755 $(DESTDIR)$(BINDIR)/hsci; \ $(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8 $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 hsci.8 \ $(DESTDIR)$(MANDIR)/man8 clean: .PHONY: all install clean s390-tools-2.38.0/hsci/hsci000077500000000000000000000421061502674226300152540ustar00rootroot00000000000000#!/bin/bash # # hsci - Tool to manage HiperSockets Converged Interfaces (HSCI) # # Copyright IBM Corp. 2020 # # s390-tools is free software; you can redistribute it and/or modify # it under the terms of the MIT license. See LICENSE for details. # hsdev="" ndev="" hsci="" hscibr="" hscibp="" hsci_mac="" hsif_pnetid="" netif_pnetid="" hsci_pnetid="" ############################################################################## # Concept: # -------- # | hsci | # -------- # / \ # -------- -------- # | ndev | | hsdev | # -------- -------- # # Detail (bridge w/ bridgeports): # -------- # | hsci | # -------- # | # ---------------------- # | |hsci-bp| | # | hsci-br --------- | # | | # -------- -------- | # | ndev | | hsdev | | # ---------------------- function usage { cat <<-EOD Usage: hsci COMMAND [OPTION] This tool is designed to control and show HSCI (HiperSockets Converged Interfaces) settings. A HiperSockets interface and an external network interface are converged into an HSCI interface. COMMANDS add HIPERSOCKETS_DEV NET_DEV Adds an HSCI interface del HSCI_NAME Deletes an HSCI interface show Lists the configured HSCI interfaces OPTIONS: -v, --version Prints the version number of the hsci tool and exits -h, --help Displays the help information for the command EOD } function prereqs_check { if ! [ -x "$(command -v ip)" ]; then echo "Error: No iproute2 installed on this system" >&2 return 1 fi } function get_pnetid { local netdev=$1 local pnetid="" #### ROCE: if [ -e /sys/class/net/$netdev/device/util_string ]; then pnetids="$(cat /sys/class/net/$netdev/device/util_string | tr -d '\000' | iconv -f IBM-1047 -t ASCII)" else #### OSA /HiperSockets: if [ -e /sys/class/net/$netdev/device/chpid ]; then chpid="$(cat /sys/class/net/$netdev/device/chpid | tr [:upper:] [:lower:])" pnetids="$(cat /sys/devices/css0/chp0.$chpid/util_string | tr -d '\000' | iconv -f IBM-1047 -t ASCII)" fi fi if [ "$pnetids" != "" ]; then port_if="$(cat /sys/class/net/$netdev/dev_port)" (( idx=16*$port_if+1 )) (( end=$idx+15 )) pnetid="$(echo "$pnetids" | cut -c $idx-$end | tr -d ' ')" fi echo $pnetid } function check_pnetids { if [ "$hsdev" != "" ]; then hsif_pnetid="$(get_pnetid $hsdev)" else hsif_pnetid="" fi if [ "$ndev" != "" ]; then netif_pnetid="$(get_pnetid $ndev)" else netif_pnetid="" fi #Check PNETIDs if [ "$hsif_pnetid" != "" ] && [ "$netif_pnetid" != "" ] && [ "$netif_pnetid" != "$hsif_pnetid" ]; then echo "Error: $hsdev and $ndev have different PNETIDs! They are $hsif_pnetid and $netif_pnetid respectively" >&2 return 1 else if [ "$hsif_pnetid" != "" ]; then hsci_pnetid=$hsif_pnetid else hsci_pnetid=$netif_pnetid fi return 0 fi } function verify_precon { echo "Verifying net dev $ndev and HiperSockets dev $hsdev" if [ ! -e /sys/class/net/$hsdev ]; then echo "Error: $hsdev does not exist" >&2 return 1 fi if [ "$(cat /sys/class/net/$hsdev/device/card_type 2>/dev/null)" != "HiperSockets" ]; then echo "Error: $hsdev is not a HiperSockets device" >&2 return 1 fi if [ "$(cat /sys/class/net/$hsdev/device/layer2 2>/dev/null)" != "1" ]; then echo "Error: $hsdev is not in layer 2 mode" >&2 return 1 fi if [ ! -e /sys/class/net/$hsdev/device/vnicc/bridge_invisible ]; then echo "Error: Missing vnic-characteristics support" >&2 return 1 fi if [ "$(cat /sys/class/net/$hsdev/device/vnicc/bridge_invisible 2>/dev/null)" == "n/a" ]; then echo "Error: $hsdev does not support vnicc" >&2 return 1 fi if [ $(ip link show $hsdev | grep UP | wc -l) -eq 0 ]; then echo "Error: $hsdev is not in state UP" >&2 return 1 fi if [ $(bridge -d link show dev $hsdev self | grep learning_sync | wc -l) -eq 0 ]; then echo "Error: $hsdev does not support attribute learning_sync" >&2 return 1 fi if [ $(ip link show $hsdev | grep master | wc -l) -ne 0 ]; then if [ $(ip link show $hsdev | grep "master $hsci" | wc -l) -eq 0 ]; then echo "Error: $hsdev is already a subordinate to another master" >&2 return 1 fi fi #Pre-verify net_dev if [ ! -e /sys/class/net/$ndev ]; then echo "Error: $ndev does not exist" >&2 return 1 fi if [ $(ip link show $ndev | grep UP | wc -l) -eq 0 ]; then echo "Error: $ndev is not in state UP" >&2 return 1 fi if [ $(ip link show $ndev | grep master | wc -l) -ne 0 ]; then if [ $(ip link show $ndev | grep "master $hsci" | wc -l) -eq 0 ]; then echo "Error: $ndev is already a subordinate to another master" >&2 return 1 fi fi #Check PNETIDs check_pnetids if [ $? -ne 0 ]; then return 1 fi return 0 } function clean_up { ip link del $hsci >/dev/null 2>&1 bridge link set dev $hsdev learning_sync off self >/dev/null 2>&1 echo 0 > /sys/class/net/$hsdev/device/vnicc/bridge_invisible >/dev/null 2>&1 bridge fdb del $hsci_mac dev $hsdev self local >/dev/null 2>&1 bridge fdb del $hsci_mac dev $ndev self local >/dev/null 2>&1 ip link del $hscibr >/dev/null 2>&1 } ############################################################################## ## add a new HSCI interface ############################################################################## function add_hsci { if [ $# != 2 ]; then echo "hsci: Invalid parameters" >&2 echo "Use 'hsci --help' for more information" >&2 return 1 fi hsdev=$1 ndev=$2 hsci_postfix="$(readlink /sys/class/net/$hsdev/device/cdev0 | tail -c5)" hscibr=hsci$hsci_postfix-br hscibp=hsci$hsci_postfix-bp hsci=hsci$hsci_postfix #### Verify preconditions verify_precon if [ $? -ne 0 ]; then return 1 fi echo "Adding $hsci with a HiperSockets dev $hsdev and an external dev $ndev" #### Create bridge (idempotent) if [ ! -e /sys/class/net/$hscibr ]; then # ageing_time of $hscibr defaults to 30000 (300 secs) ip link add name $hscibr type bridge stp_state 0 >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Error: Could not create a bridge" >&2 return 1 fi else ip link set dev $hscibr type bridge stp_state 0 >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Error: $hscibr is not a bridge" >&2 return 1 fi fi #### Prepare hsdev # Set VNICC of hsdev to invisible #(mandatory for co-existence with HS-OSA bridges!) echo 1 > /sys/class/net/$hsdev/device/vnicc/bridge_invisible #### Create bridge ports ip link set dev $ndev master $hscibr >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Error: Could not set master for $ndev" >&2 clean_up return 1 fi ip link set dev $hsdev master $hscibr >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Error: Could not set master for $hsdev" >&2 clean_up return 1 fi # Do not learn from ndev, but do learn from hsci-bp: # - First define hsdev and ndev as _isolated_ bridgeports # - Then turn on learning_sync on self on hsdev # - Then define hsci-bp as non-isolated veth bridgeport # no forwarding between ndev and hsdev -> isolated on # ndev is default for outgoing unknown targets -> flood on # no need to learn external LAN targets into fdb -> learning off bridge link set dev $ndev isolated on learning off flood on mcast_flood on >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Error: Failed to set bridge attributes on $ndev" >&2 clean_up return 1 fi # no forwarding between ndev and hsdev -> isolated on # fdb will be populated by dev-to-bridge-notification, no need to learn # -> learning off # only send to hsdev, if listed in fdb -> flood off # don't send MC/BC on hsdev -> mcast_flood off bridge link set dev $hsdev isolated on learning off flood off mcast_flood off >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Error: Failed to set bridge attributes on $hsdev" >&2 clean_up return 1 fi # NOTE: Although not required, BCs will be sent out on hsdev. # NOTE: We need to receive BCs on hsdev, as z/OS HSCI does ARP requests on HS. ip link set dev $hscibr up >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Error: Failed to set $hscibr up" >&2 clean_up return 1 fi #### Turn on device for bridge notification #### Toggle is required to learn full list of HS targets, #### not only future changes. bridge link set dev $hsdev learning_sync off self >/dev/null 2>&1 bridge link set dev $hsdev learning_sync on self >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Error: Failed to turn on device for bridge notification" >&2 clean_up return 1 fi # define veth pair for hsci (idempotent) if [ ! -e /sys/class/net/$hsci ]; then ip link add dev $hsci type veth peer name $hscibp >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Error: Could not create veth pair $hsci - $hscibp " >&2 clean_up return 1 fi else if [ $hsci@$hscibp: != "$(ip -o -d link show dev $hsci | awk '/veth/ {print $2}' )" ]; then echo "Error: $hsci@$hscibp is not a veth" >&2 clean_up return 1 fi fi ip link set dev $hscibp master $hscibr >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Error: Failed to add $hscibp to $hscibr" >&2 clean_up return 1 fi bridge link set dev $hscibp isolated off learning on flood on mcast_flood on >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Error: Failed to set bridge parameters for $hscibp" >&2 clean_up return 1 fi #### Set a static forwarding rule for hsci MAC, so hsci can be used as a #### single-MAC network interface without being subject to #### ageing and re-learning #### Wait for systemd to change the MAC of hsci, if it wants to: sleep 1 hsci_mac="$(cat /sys/class/net/$hsci/address)" #### (idempotent) if [ $(bridge fdb show dev $hscibp | grep "$hsci_mac master $hscibr static" | wc -l) -eq 0 ]; then bridge fdb add $hsci_mac dev $hscibp master static if [ $? -ne 0 ]; then echo "Error: Failed to set $hsci_mac to $hscibr fdb" >&2 clean_up return 1 fi fi # Bridge-to-device learning will set this MAC on hsdev and ndev. # Old kernel code doesn't do hsci bridge-to-device learning. # In this case: Set hsci_mac as local MAC of hsdev and ndev, # so at least the single-MAC scenario works. if [ $(bridge fdb show dev $hsdev | grep "$hsci_mac self permanent" | wc -l) -eq 0 ]; then echo "Warning: $hsci will support only its current static MAC address. Please upgrade your kernel to the latest level." >&2 bridge fdb add $hsci_mac dev $hsdev self local if [ $? -ne 0 ]; then echo "Error: Failed to add $hsci_mac to $hsdev" >&2 clean_up return 1 fi fi if [ $(bridge fdb show dev $ndev | grep "$hsci_mac self permanent" | wc -l) -eq 0 ]; then bridge fdb add $hsci_mac dev $ndev self local if [ $? -ne 0 ]; then echo "Error: Failed to add $hsci_mac to $ndev" >&2 clean_up return 1 fi fi #### Set veth pair to UP ip link set dev $hscibp up >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Error: Failed to set $hscibp up" >&2 clean_up return 1 fi ip link set dev $hsci up >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Error: Failed to set $hsci up" >&2 clean_up return 1 fi echo "Added HSCI interface $hsci" return 0 } ############################################################################## ## Delete HSCI ############################################################################## function del_hsci { if [ $# != 1 ]; then echo "hsci: invalid parameters" >&2 echo "Use 'hsci --help' for more information" >&2 return 1 fi hsci=$1 if [ $(ip link show dev $hsci | wc -l) -eq 0 ]; then echo "Error: $hsci does not exit" >&2 return 1 fi hsci_mac="$(cat /sys/class/net/$hsci/address)" #### Find hscibp and hscibr hscibp="$(ip -o link show dev $hsci | awk '{print $2}')" if [[ $hscibp != *@* ]]; then # $hsci has no HSCI veth peer echo "Warning: $hsci may have been created by an older version of hsci" >&2 mvp=1 hscibp="" hscibr=$hsci else mvp=0 hscibp=${hscibp##*@} hscibp=${hscibp%:} echo "$hsci is paired with $hscibp" >&2 hscibr="$(ip link show dev $hscibp | awk '{for(x=1;x&2 fi if [ "$ndev" == "" ]; then echo "Error: $hsci has no active external port" >&2 fi echo "Deleting HSCI interface $hsci with HiperSockets interface $hsdev and external interface $ndev" #### Delete veth before resetting learning_sync and deleting bridge, #### so fdb entries are cleaned up (synced)! ip link del $hsci >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Error: Failed to delete $hsci" >&2 fi # Bridge-to-device learning will remove learned MACs from hsdev and ndev. # Old kernel code doesn't do hsci bridge-to-device learning. # In this case: Remove the hsci_mac that was added during 'add' from # hsdev and ndev. if [ "$hsdev" != "" ] && [ $mvp -eq 0 ]; then if [ $(bridge fdb show dev $hsdev | grep "$hsci_mac self permanent" | wc -l) -ne 0 ]; then echo "Warning: It seems your kernel does not support all hsci features, please upgrade." >&2 bridge fdb del $hsci_mac dev $hsdev self local if [ $? -ne 0 ]; then echo "Error: Failed to delete $hsci_mac from $hsdev" >&2 fi fi fi # In the mvp case hsci_mac == hs_mac and was only set on ndev. if [ "$ndev" != "" ]; then if [ $(bridge fdb show dev $ndev | grep "$hsci_mac self permanent" | wc -l) -ne 0 ]; then bridge fdb del $hsci_mac dev $ndev self local if [ $? -ne 0 ]; then echo "Error: Failed to delete $hsci_mac from $ndev" >&2 fi fi fi #### Reset learning_sync if [ "$hsdev" != "" ]; then bridge link set dev $hsdev learning_sync off self >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Error: Failed to turn off learning_sync on $hsdev" >&2 fi fi #### Delete bridge if [ "$hscibr" != "" ]; then ip link del $hscibr >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "Error: Failed to delete $hscibr" >&2 fi fi if [ "$hsdev" != "" ]; then echo 0 > /sys/class/net/$hsdev/device/vnicc/bridge_invisible fi echo "Deleted $hsci" return 0 } ############################################################################## ## Show HSCI ############################################################################## function print_row { if [ $mvp -eq 0 ]; then printf '%-8s %-16s %-15s %-15s\n' "$hsci" "$hsci_pnetid" "$hsdev" "$ndev" else printf '%-8s %-16s %-15s %-15s (v1)\n' "$hsci" "$hsci_pnetid" "$hsdev" "$ndev" fi } function list_active { hsci="" hsdev=$1 ndev="" hscibp="" mvp=1 hsci_pnetid="$(get_pnetid $hsdev)" hscibr="$(ip link show dev $hsdev | awk '{for(x=1;x&2 print_row return 1 fi hsci=${hsci##*@} hsci=${hsci%:} print_row return 0 } function print_header { if [ $header -eq 0 ]; then echo "HSCI PNET_ID HiperSockets External " echo "------------------------------------------------------------" header=1 fi } function list_one { local hsnetdev=$1 if [ $(bridge -d link show dev $hsnetdev 2>/dev/null | grep "learning_sync on" | wc -l) -ne 0 ]; then print_header list_active $hsnetdev fi return 0 } function show_hsci { if [ $# != 0 ]; then echo "hsci: invalid parameters" >&2 echo "Use 'hsci --help' for more information" >&2 return 1 fi header=0 for hs_net_dev in $(ls -1 /sys/class/net/); do list_one $hs_net_dev done return 0 } #============================================================================== function print_version() { echo "hsci utility: version %S390_TOOLS_VERSION%" echo "Copyright IBM Corp. 2020" } ############################################################################## ##### Main ############################################################################## prereqs_check args="$(getopt -u -o hv -l help,version -- $*)" [ $? -ne 0 ] && exit 2 set -- $args while true; do case $1 in -v | --version) print_version exit 0 ;; -h | --help) usage exit 0 ;; --) ;; add) shift add_hsci "$@" exit $? ;; del) shift del_hsci "$@" exit $? ;; show) shift show_hsci "$@" exit $? ;; *) echo "hsci: Please specify a valid command or option" >&2 echo "Use 'hsci --help' for more information" >&2 exit 1 esac shift done s390-tools-2.38.0/hsci/hsci.8000066400000000000000000000031601502674226300154140ustar00rootroot00000000000000.\" Copyright IBM Corp. 2020 .TH HSCI 8 "November 2020" "s390-tools" "Linux Programmer's Manual" .SH NAME .B hsci \- control and show HSCI settings. .SH SYNOPSIS .B hsci add .I HSDEV .I NETDEV .br .B hsci del .I HSCINAME .br .B hsci show .br .B hsci [\-hv] .SH DESCRIPTION .BI hsci is used to control and show HSCI (HiperSockets Converged Interfaces) settings. A HiperSockets interface and an external network interface are converged into an HSCI interface. .SH COMMANDS .TP .B add \fIHSDEV\fR \fINETDEV\fR .RS .4i .PP Adds an HSCI interface .PP .I HSDEV is the interface name of the HiperSockets device to be converged into the HSCI interface. .PP .I NETDEV is the interface name of the external network device to be converged into the HSCI interface. .RE .TP .B del \fIHSCINAME\fR .RS .4i .PP Deletes an HSCI interface .PP .I HSCINAME is the name of the HSCI interface for the HiperSockets device and the external network device. .RE .TP .B show .RS .4i .PP Lists the configured HSCI interfaces. .RE .SH OPTIONS .TP .BR \-v ", " \-\-version Prints the version number of hsci and exits. .TP .BR \-h ", " \-\-help Displays the help information for the command. .SH EXIT CODES .TP .BR "0" The hsci command ran successfully. .TP .BR "1" An error occurred. .SH EXAMPLE .BR "hsci show" .TP .RB Lists the configured HSCI interfaces: .RS 1.2i HSCI PNET_ID HiperSockets External .br ----------------------------------------- .br hsci8410 NET1 enc8410 encb040 .RE .SH SEE ALSO .nf ip(8), bridge(8) .fi .SH AUTHOR .nf Written by Alexandra Winter Wenjia Zhang .fi s390-tools-2.38.0/hyptop/000077500000000000000000000000001502674226300147725ustar00rootroot00000000000000s390-tools-2.38.0/hyptop/Makefile000066400000000000000000000015341502674226300164350ustar00rootroot00000000000000include ../common.mak ifeq (${HAVE_NCURSES},0) all: $(SKIP) HAVE_NCURSES=0 install: $(SKIP) HAVE_NCURSES=0 else check_dep: $(call check_dep, \ "hyptop", \ "ncurses.h", \ "ncurses-devel or libncurses-dev", \ "HAVE_NCURSES=0") LDLIBS += -lncurses all: check_dep hyptop OBJECTS = hyptop.o opts.o helper.o \ sd_core.o sd_sys_items.o sd_cpu_items.o \ tbox.o table.o table_col_unit.o \ dg_debugfs.o dg_debugfs_lpar.o dg_debugfs_vm.o dg_debugfs_vmd0c.o \ win_sys_list.o win_sys.o win_fields.o \ win_cpu_types.o win_help.o nav_desc.o hyptop: $(OBJECTS) $(rootdir)/libutil/libutil.a install: all $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 hyptop \ $(DESTDIR)$(USRSBINDIR) $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 hyptop.8 \ $(DESTDIR)$(MANDIR)/man8 endif clean: rm -f *.o *~ hyptop core .PHONY: all install clean check_dep s390-tools-2.38.0/hyptop/dg_debugfs.c000066400000000000000000000035261502674226300172350ustar00rootroot00000000000000/* * hyptop - Show hypervisor performance data on System z * * Common functions for debugfs data gatherer * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include "dg_debugfs.h" #include "helper.h" #include "hyptop.h" #define HYPFS_SUBDIR "/s390_hypfs/" static char *l_debugfs_dir; static void l_check_rc(int rc, int exit_on_err) { if (!exit_on_err) return; if (rc == -EACCES) ERR_EXIT("Permission denied, check \"%s/s390_hypfs/\"\n", l_debugfs_dir); if (rc != -ENOENT) ERR_EXIT("Could not initialize data gatherer (%s)\n", strerror(-rc)); } static void l_check_rc_final(int rc, int exit_on_err) { if (!exit_on_err) return; l_check_rc(rc, exit_on_err); ERR_EXIT("Could not initialize data gatherer (%s)\n", strerror(-rc)); } /* * Initialize debugfs data gatherer backend */ int dg_debugfs_init(int exit_on_err) { int rc; l_debugfs_dir = ht_mount_point_get("debugfs"); if (!l_debugfs_dir) { if (!exit_on_err) return -ENODEV; ERR_EXIT("Debugfs is not mounted, try \"mount none -t debugfs " "/sys/kernel/debug\"\n"); } rc = dg_debugfs_vm_init(); if (rc == 0) return 0; else l_check_rc(rc, exit_on_err); rc = dg_debugfs_lpar_init(); if (rc == 0) return 0; else l_check_rc_final(rc, exit_on_err); return rc; } /* * Open a debugfs file */ int dg_debugfs_open(const char *file) { char *path; int fh; path = ht_alloc(strlen(l_debugfs_dir) + strlen(HYPFS_SUBDIR) + strlen(file) + 1); path[0] = 0; strcat(path, l_debugfs_dir); strcat(path, HYPFS_SUBDIR); strcat(path, file); fh = open(path, O_RDONLY); ht_free(path); if (fh == -1) return -errno; else return fh; } s390-tools-2.38.0/hyptop/dg_debugfs.h000066400000000000000000000013541502674226300172370ustar00rootroot00000000000000/* * hyptop - Show hypervisor performance data on System z * * Common functions for debugfs data gatherer * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef DG_DEBUGFS_H #define DG_DEBUGFS_H #include "sd.h" #define DBFS_WAIT_TIME_US 10000 extern int dg_debugfs_init(int exit_on_err); extern int dg_debugfs_vm_init(void); extern int dg_debugfs_lpar_init(void); extern int dg_debugfs_open(const char *file); /* * z/VM diag 0C prototypes */ int dg_debugfs_vmd0c_init(void); void dg_debugfs_vmd0c_sys_cpu_fill(struct sd_sys *sys, u64 online_time, unsigned int cpu_cnt); #endif /* DG_DEBUGFS_H */ s390-tools-2.38.0/hyptop/dg_debugfs_lpar.c000066400000000000000000000174421502674226300202550ustar00rootroot00000000000000/* * hyptop - Show hypervisor performance data on System z * * Hyptop LPAR data gatherer that operates on debugfs * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include "dg_debugfs.h" #include "helper.h" #include "hyptop.h" #include "sd.h" #define MTID_MASK 0x1f #define LPAR_NAME_LEN 8 #define TMP_SIZE 64 #define LPAR_PHYS_FLG 0x80 #define CPU_TYPE_LEN 16 #define DEBUGFS_FILE "diag_204" static u64 l_update_time_us; static long l_204_buf_size; /* * Diag data structure definition */ struct l_x_info_blk_hdr { u8 npar; u8 flags; u8 reserved1[6]; u64 curtod1; u64 curtod2; u8 reserved[40]; } __attribute__ ((packed)); struct l_x_sys_hdr { u8 reserved1; u8 cpus; u8 rcpus; u8 reserved2[5]; char sys_name[LPAR_NAME_LEN]; u8 reserved3[33]; u8 mtid; u8 reserved4[46]; } __attribute__ ((packed)); static inline void l_sys_hdr__sys_name(struct l_x_sys_hdr *hdr, char *name) { ht_ebcdic_to_ascii(hdr->sys_name, name, LPAR_NAME_LEN); name[LPAR_NAME_LEN] = 0; ht_strstrip(name); } struct l_x_cpu_info { u16 cpu_addr; u8 reserved1[2]; u8 ctidx; u8 reserved2[3]; u64 acc_time; u64 lp_time; u8 reserved3[6]; u8 reserved4[2]; u64 online_time; u8 reserved5[24]; u64 mt_idle_time; u8 reserved6[24]; } __attribute__ ((packed)); static int l_thread_cnt(struct l_x_sys_hdr *hdr) { return (hdr->mtid & MTID_MASK) + 1; } static void l_idx2name(int index, char *name) { switch (index) { case 0: strcpy(name, SD_CPU_TYPE_STR_CP); break; case 3: strcpy(name, SD_CPU_TYPE_STR_IFL); break; default: strcpy(name, SD_CPU_TYPE_STR_UN); } } struct l_x_phys_hdr { u8 reserved1[1]; u8 cpus; u8 reserved2[94]; } __attribute__ ((packed)); struct l_x_phys_cpu { u16 cpu_addr; u8 reserved1[2]; u8 ctidx; u8 reserved2[3]; u64 mgm_time; u8 reserved3[80]; } __attribute__ ((packed)); /* * Fill CPU with data */ static void l_sd_cpu_fill(struct sd_cpu *cpu, struct l_x_cpu_info *cpu_info, int threads) { sd_cpu_cpu_time_us_set(cpu, cpu_info->lp_time); sd_cpu_threads_per_core_set(cpu, threads); if (threads > 1) sd_cpu_thread_time_us_set(cpu, cpu_info->lp_time * threads - cpu_info->mt_idle_time); else sd_cpu_thread_time_us_set(cpu, cpu_info->lp_time); sd_cpu_mgm_time_us_set(cpu, G0(cpu_info->acc_time - cpu_info->lp_time)); sd_cpu_online_time_us_set(cpu, cpu_info->online_time); sd_cpu_state_set(cpu, SD_CPU_STATE_UNKNOWN); } /* * Fill system with data */ static void *l_sd_sys_fill(struct sd_sys *lpar, struct l_x_sys_hdr *sys_hdr) { struct l_x_cpu_info *cpu_info; int i; cpu_info = (struct l_x_cpu_info *) (sys_hdr + 1); for (i = 0; i < sys_hdr->rcpus; i++) { char cpu_type[CPU_TYPE_LEN + 1]; struct sd_cpu *cpu; char cpu_id[10]; sprintf(cpu_id, "%i", cpu_info->cpu_addr); cpu = sd_cpu_get(lpar, cpu_id); if (!cpu) { l_idx2name(cpu_info->ctidx, cpu_type); cpu = sd_cpu_new(lpar, cpu_id, cpu_type, 1); } l_sd_cpu_fill(cpu, cpu_info, lpar->threads_per_core); sd_cpu_commit(cpu); cpu_info++; } return cpu_info; } /* * Fill one physical CPU with data */ static void l_sd_cpu_phys_fill(struct sd_sys *sys, struct l_x_phys_cpu *cpu_info) { char cpu_type[CPU_TYPE_LEN + 1]; char cpu_id[TMP_SIZE]; struct sd_cpu *cpu; snprintf(cpu_id, TMP_SIZE, "%i", cpu_info->cpu_addr); cpu = sd_cpu_get(sys, cpu_id); if (!cpu) { l_idx2name(cpu_info->ctidx, cpu_type); cpu = sd_cpu_new(sys, cpu_id, cpu_type, 1); sd_cpu_state_set(cpu, SD_CPU_STATE_UNKNOWN); } sd_cpu_mgm_time_us_set(cpu, cpu_info->mgm_time); sd_cpu_commit(cpu); } /* * Fill all physical CPUs with data */ static void l_sd_sys_root_cpu_phys_fill(struct sd_sys *sys, struct l_x_phys_hdr *phys_hdr) { struct l_x_phys_cpu *cpu_info; int i; cpu_info = (struct l_x_phys_cpu *) (phys_hdr + 1); for (i = 0; i < phys_hdr->cpus; i++) { l_sd_cpu_phys_fill(sys, cpu_info); cpu_info++; } } /* * Header for debugfs file "diag_204" */ struct l_debugfs_d204_hdr { u64 len; u16 version; u8 reserved[54]; } __attribute__ ((packed)); struct l_debugfs_d204 { struct l_debugfs_d204_hdr h; char buf[]; } __attribute__ ((packed)); /* * Read debugfs file */ static void l_read_debugfs(struct l_debugfs_d204_hdr **hdr, struct l_x_info_blk_hdr **data) { long real_buf_size; ssize_t rc; void *buf; int fh; do { fh = dg_debugfs_open(DEBUGFS_FILE); *hdr = buf = ht_alloc(l_204_buf_size); rc = read(fh, buf, l_204_buf_size); if (rc == -1) ERR_EXIT_ERRNO("Reading hypervisor data failed"); close(fh); real_buf_size = (*hdr)->len + sizeof(struct l_debugfs_d204_hdr); if (rc == real_buf_size) break; l_204_buf_size = real_buf_size; ht_free(buf); } while (1); *data = buf + sizeof(struct l_debugfs_d204_hdr); } /* * Fill System Data */ static void l_sd_sys_root_fill(struct sd_sys *sys) { struct l_x_info_blk_hdr *time_hdr; struct l_debugfs_d204_hdr *hdr; struct l_x_sys_hdr *sys_hdr; struct sd_sys *lpar; char lpar_id[10]; int i; do { l_read_debugfs(&hdr, &time_hdr); if (l_update_time_us != ht_ext_tod_2_us(&time_hdr->curtod1)) { l_update_time_us = ht_ext_tod_2_us(&time_hdr->curtod1); break; } /* * Got old snapshot from kernel. Wait some time until * new snapshot is available. */ ht_free(hdr); usleep(DBFS_WAIT_TIME_US); } while (1); sys_hdr = ((void *) time_hdr) + sizeof(struct l_x_info_blk_hdr); for (i = 0; i < time_hdr->npar; i++) { l_sys_hdr__sys_name(sys_hdr, lpar_id); lpar = sd_sys_get(sys, lpar_id); if (!lpar) lpar = sd_sys_new(sys, lpar_id); lpar->threads_per_core = l_thread_cnt(sys_hdr); sys_hdr = l_sd_sys_fill(lpar, sys_hdr); sd_sys_commit(lpar); } if (time_hdr->flags & LPAR_PHYS_FLG) l_sd_sys_root_cpu_phys_fill(sys, (void *) sys_hdr); ht_free(hdr); sd_sys_commit(sys); } /* * Update system data */ static void l_sd_update(void) { struct sd_sys *root = sd_sys_root_get(); sd_sys_update_start(root); l_sd_sys_root_fill(root); sd_sys_update_end(root, l_update_time_us); } /* * Supported system items */ static struct sd_sys_item *l_sys_item_vec[] = { &sd_sys_item_core_cnt, &sd_sys_item_thread_cnt, &sd_sys_item_core_diff, &sd_sys_item_thread_diff, &sd_sys_item_smt_diff, &sd_sys_item_mgm_diff, &sd_sys_item_core, &sd_sys_item_thread, &sd_sys_item_mgm, &sd_sys_item_online, NULL, }; /* * Default system items */ static struct sd_sys_item *l_sys_item_enable_vec[] = { &sd_sys_item_core_cnt, &sd_sys_item_core_diff, &sd_sys_item_thread_diff, &sd_sys_item_mgm_diff, &sd_sys_item_core, &sd_sys_item_mgm, &sd_sys_item_online, NULL, }; /* * Supported CPU items */ static struct sd_cpu_item *l_cpu_item_vec[] = { &sd_cpu_item_type, &sd_cpu_item_core_diff, &sd_cpu_item_thread_diff, &sd_cpu_item_smt_diff, &sd_cpu_item_mgm_diff, &sd_cpu_item_core, &sd_cpu_item_thread, &sd_cpu_item_mgm, &sd_cpu_item_online, NULL, }; /* * Default CPU items */ static struct sd_cpu_item *l_cpu_item_enable_vec[] = { &sd_cpu_item_type, &sd_cpu_item_core_diff, &sd_cpu_item_thread_diff, &sd_cpu_item_mgm_diff, NULL, }; /* * Supported CPU types */ static struct sd_cpu_type *l_cpu_type_vec[] = { &sd_cpu_type_ifl, &sd_cpu_type_cp, &sd_cpu_type_un, NULL, }; /* * Define data gatherer structure */ static struct sd_dg l_sd_dg = { .update_sys = l_sd_update, .cpu_type_vec = l_cpu_type_vec, .sys_item_vec = l_sys_item_vec, .sys_item_enable_vec = l_sys_item_enable_vec, .cpu_item_vec = l_cpu_item_vec, .cpu_item_enable_vec = l_cpu_item_enable_vec, }; /* * Initialize LPAR debugfs data gatherer */ int dg_debugfs_lpar_init(void) { int fh; l_204_buf_size = sizeof(struct l_debugfs_d204_hdr); fh = dg_debugfs_open(DEBUGFS_FILE); if (fh < 0) return fh; else close(fh); sd_dg_register(&l_sd_dg, 1); return 0; } s390-tools-2.38.0/hyptop/dg_debugfs_vm.c000066400000000000000000000170371502674226300177410ustar00rootroot00000000000000/* * hyptop - Show hypervisor performance data on System z * * Hyptop z/VM data gatherer that operates on debugfs * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include "dg_debugfs.h" #include "helper.h" #include "hyptop.h" #include "sd.h" #define VM_CPU_TYPE "UN" #define VM_CPU_ID "ALL" #define NAME_LEN 8 #define DEBUGFS_FILE "diag_2fc" #define VM_CPU_ID_OPERATING "0" #define VM_CPU_ID_STOPPED "1" static u64 l_update_time_us; static long l_2fc_buf_size; static int l_use_debugfs_vmd0c; static char l_guest_name[64]; /* * Diag 2fc data structure definition */ struct l_diag2fc_data { u32 version; u32 flags; u64 used_cpu; u64 el_time; u64 mem_min_kb; u64 mem_max_kb; u64 mem_share_kb; u64 mem_used_kb; u32 pcpus; u32 lcpus; u32 vcpus; u32 ocpus; u32 cpu_max; u32 cpu_shares; u32 cpu_use_samp; u32 cpu_delay_samp; u32 page_wait_samp; u32 idle_samp; u32 other_samp; u32 total_samp; char guest_name[NAME_LEN]; }; /* * Header for debugfs file "diag_2fc" */ struct l_debugfs_d2fc_hdr { u64 len; u16 version; char tod_ext[16]; u64 count; char reserved[30]; } __attribute__ ((packed)); struct l_debugfs_d2fc { struct l_debugfs_d2fc_hdr h; char diag2fc_buf[]; } __attribute__ ((packed)); /* * Get local guest name */ static void l_guest_name_init(void) { int level, found = 0; char line[1024]; FILE *fh; fh = fopen("/proc/sysinfo", "r"); if (!fh) ERR_EXIT_ERRNO("Could not open '/proc/sysinfo'"); while (fgets(line, sizeof(line), fh)) { if (sscanf(line, "VM%02d Name: %s", &level, l_guest_name) == 2) found = 1; } if (!found) ERR_EXIT("Could find guest name in '/proc/sysinfo'"); fclose(fh); } /* * Get existing or create new CPU */ static struct sd_cpu *l_cpu_alloc(struct sd_sys *guest, const char *id, int cnt) { struct sd_cpu *cpu = sd_cpu_get(guest, id); return cpu ? cpu : sd_cpu_new(guest, id, SD_CPU_TYPE_STR_UN, cnt); } /* * Get number of operating CPUs */ static int l_ocpus(struct l_diag2fc_data *data) { /* * For guests with ABSOLUTE or limit SHARE, ocpus and cpu_max is zero. * In this case we return vcpus. */ return (data->cpu_max == 0) ? data->vcpus : data->ocpus; } /* * Fill operating CPUs with data */ static void l_cpu_oper_fill(struct sd_sys *guest, struct l_diag2fc_data *data) { struct sd_cpu *cpu; cpu = l_cpu_alloc(guest, VM_CPU_ID_OPERATING, l_ocpus(data)); sd_cpu_state_set(cpu, SD_CPU_STATE_OPERATING); sd_cpu_cpu_time_us_set(cpu, data->used_cpu); sd_cpu_online_time_us_set(cpu, data->el_time); sd_cpu_cnt(cpu) = l_ocpus(data); sd_cpu_commit(cpu); } /* * Fill stopped CPUs with data */ static void l_cpu_stop_fill(struct sd_sys *guest, struct l_diag2fc_data *data) { int cnt = data->vcpus - l_ocpus(data); struct sd_cpu *cpu; cpu = l_cpu_alloc(guest, VM_CPU_ID_STOPPED, cnt); sd_cpu_state_set(cpu, SD_CPU_STATE_STOPPED); sd_cpu_cpu_time_us_set(cpu, 0); sd_cpu_online_time_us_set(cpu, 0); sd_cpu_cnt(cpu) = cnt; sd_cpu_commit(cpu); } /* * Fill CPUs will data */ static void l_cpu_fill(struct sd_sys *guest, struct l_diag2fc_data *data) { if (l_ocpus(data) > 0) l_cpu_oper_fill(guest, data); if (data->vcpus - l_ocpus(data) > 0) l_cpu_stop_fill(guest, data); } /* * Fill "guest" with data */ static void l_sd_sys_fill(struct sd_sys *guest, struct l_diag2fc_data *data) { if (l_use_debugfs_vmd0c && (strcmp(guest->id, l_guest_name) == 0)) dg_debugfs_vmd0c_sys_cpu_fill(guest, data->el_time, data->vcpus); else l_cpu_fill(guest, data); sd_sys_weight_cur_set(guest, data->cpu_shares); sd_sys_weight_max_set(guest, data->cpu_max); sd_sys_mem_min_kib_set(guest, data->mem_min_kb); sd_sys_mem_max_kib_set(guest, data->mem_max_kb); sd_sys_mem_use_kib_set(guest, data->mem_used_kb); sd_sys_update_time_us_set(guest, l_update_time_us); sd_sys_commit(guest); } /* * Read debugfs file */ static void l_read_debugfs(struct l_debugfs_d2fc_hdr **hdr, struct l_diag2fc_data **data) { long real_buf_size; ssize_t rc; void *buf; int fh; do { fh = dg_debugfs_open(DEBUGFS_FILE); *hdr = buf = ht_alloc(l_2fc_buf_size); rc = read(fh, buf, l_2fc_buf_size); if (rc == -1) ERR_EXIT_ERRNO("Reading hypervisor data failed"); close(fh); real_buf_size = (*hdr)->len + sizeof(struct l_debugfs_d2fc_hdr); if (rc == real_buf_size) break; l_2fc_buf_size = real_buf_size; ht_free(buf); } while (1); *data = buf + sizeof(struct l_debugfs_d2fc_hdr); } /* * Fill System Data */ static void l_sd_sys_root_fill(struct sd_sys *sys) { struct l_diag2fc_data *d2fc_data; struct l_debugfs_d2fc_hdr *hdr; struct sd_cpu *cpu; unsigned int i; do { l_read_debugfs(&hdr, &d2fc_data); if (l_update_time_us != ht_ext_tod_2_us(&hdr->tod_ext)) { l_update_time_us = ht_ext_tod_2_us(&hdr->tod_ext); break; } /* * Got old snapshot from kernel. Wait some time until * new snapshot is available. */ ht_free(hdr); usleep(DBFS_WAIT_TIME_US); } while (1); cpu = sd_cpu_get(sys, VM_CPU_ID); if (!cpu) cpu = sd_cpu_new(sys, VM_CPU_ID, SD_CPU_TYPE_STR_UN, d2fc_data[0].lcpus); sd_cpu_state_set(cpu, SD_CPU_STATE_UNKNOWN); sd_cpu_cnt(cpu) = d2fc_data[0].lcpus; sd_cpu_commit(cpu); for (i = 0; i < hdr->count; i++) { struct l_diag2fc_data *data = &d2fc_data[i]; char guest_name[NAME_LEN + 1]; struct sd_sys *guest; guest_name[NAME_LEN] = 0; ht_ebcdic_to_ascii(data->guest_name, guest_name, NAME_LEN); ht_strstrip(guest_name); guest = sd_sys_get(sys, guest_name); if (!guest) guest = sd_sys_new(sys, guest_name); l_sd_sys_fill(guest, data); } ht_free(hdr); sd_sys_commit(sys); } /* * Update system data */ static void l_sd_update(void) { struct sd_sys *root = sd_sys_root_get(); sd_sys_update_start(root); l_sd_sys_root_fill(root); sd_sys_update_end(root, l_update_time_us); } /* * Supported system items */ static struct sd_sys_item *l_sys_item_vec[] = { &sd_sys_item_cpu_cnt, &sd_sys_item_cpu_oper_cnt, &sd_sys_item_cpu_diff, &sd_sys_item_mgm_diff, &sd_sys_item_cpu, &sd_sys_item_mgm, &sd_sys_item_online, &sd_sys_item_mem_use, &sd_sys_item_mem_max, &sd_sys_item_weight_cur, &sd_sys_item_weight_max, NULL, }; /* * Default system items */ static struct sd_sys_item *l_sys_item_enable_vec[] = { &sd_sys_item_cpu_cnt, &sd_sys_item_cpu_diff, &sd_sys_item_cpu, &sd_sys_item_online, &sd_sys_item_mem_max, &sd_sys_item_mem_use, &sd_sys_item_weight_cur, NULL, }; /* * Supported CPU items */ static struct sd_cpu_item *l_cpu_item_vec[] = { &sd_cpu_item_cpu_diff, &sd_cpu_item_mgm_diff, &sd_cpu_item_cpu, &sd_cpu_item_mgm, &sd_cpu_item_online, NULL, }; /* * Default CPU items */ static struct sd_cpu_item *l_cpu_item_enable_vec[] = { &sd_cpu_item_cpu_diff, NULL, }; /* * Supported CPU types */ static struct sd_cpu_type *l_cpu_type_vec[] = { &sd_cpu_type_un, NULL, }; /* * Define data gatherer structure */ static struct sd_dg dg_debugfs_vm_dg = { .update_sys = l_sd_update, .cpu_type_vec = l_cpu_type_vec, .sys_item_vec = l_sys_item_vec, .sys_item_enable_vec = l_sys_item_enable_vec, .cpu_item_vec = l_cpu_item_vec, .cpu_item_enable_vec = l_cpu_item_enable_vec, }; /* * Initialize z/VM debugfs data gatherer */ int dg_debugfs_vm_init(void) { int fh; fh = dg_debugfs_vmd0c_init(); if (fh == 0) l_use_debugfs_vmd0c = 1; fh = dg_debugfs_open(DEBUGFS_FILE); if (fh < 0) return fh; else close(fh); l_2fc_buf_size = sizeof(struct l_debugfs_d2fc_hdr); l_guest_name_init(); sd_dg_register(&dg_debugfs_vm_dg, 0); return 0; } s390-tools-2.38.0/hyptop/dg_debugfs_vmd0c.c000066400000000000000000000071451502674226300203270ustar00rootroot00000000000000/* * hyptop - Show hypervisor performance data on System z * * Hyptop z/VM data gatherer for diag 0c that operates on debugfs * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include "dg_debugfs.h" #include "helper.h" #include "hyptop.h" #include "sd.h" #define DEBUGFS_FILE "diag_0c" static long l_0c_buf_size; /* * Diag 0c entry structure definition */ struct hypfs_diag0c_entry { char date[8]; /* MM/DD/YY in EBCDIC */ char time[8]; /* HH:MM:SS in EBCDIC */ __u64 virtcpu; /* Virtual time consumed by the virt CPU (us) */ __u64 totalproc; /* Total of virtual and simulation time (us) */ __u32 cpu; /* Linux logical CPU number */ __u32 reserved; /* Align to 8 byte */ }; /* * Header for debugfs file "diag_0c" */ struct hypfs_diag0c_hdr { __u64 len; /* Length of diag0c buffer without header */ __u16 version; /* Version of header */ char reserved1[6]; /* Reserved */ char tod_ext[16]; /* TOD clock for diag0c */ __u64 count; /* Number of entries (CPUs) in diag0c array */ char reserved2[24]; /* Reserved */ }; struct hypfs_diag0c_data { struct hypfs_diag0c_hdr hdr; /* 64 byte header */ struct hypfs_diag0c_entry entry[]; /* diag0c entry array */ }; /* * Fill one CPU with data */ static void l_sd_cpu_fill(struct sd_sys *sys, unsigned int cpu_nr, u64 online_time, u64 cpu_time, u64 mgm_time, enum sd_cpu_state state) { struct sd_cpu *cpu; char cpu_id[16]; sprintf(cpu_id, "%d", cpu_nr); cpu = sd_cpu_get(sys, cpu_id); if (!cpu) cpu = sd_cpu_new(sys, cpu_id, SD_CPU_TYPE_STR_UN, 1); sd_cpu_cpu_time_us_set(cpu, cpu_time); sd_cpu_mgm_time_us_set(cpu, mgm_time); sd_cpu_online_time_us_set(cpu, online_time); sd_cpu_state_set(cpu, state); sd_cpu_cnt(cpu) = 1; sd_cpu_commit(cpu); } /* * Read debugfs file */ static void l_read_debugfs(struct hypfs_diag0c_hdr **hdr, struct hypfs_diag0c_entry **entry) { long real_buf_size; ssize_t rc; void *buf; int fh; do { fh = dg_debugfs_open(DEBUGFS_FILE); if (fh < 0) ERR_EXIT_ERRNO("Could not open file: %s", DEBUGFS_FILE); *hdr = buf = ht_alloc(l_0c_buf_size); rc = read(fh, buf, l_0c_buf_size); if (rc == -1) ERR_EXIT_ERRNO("Reading hypervisor data failed"); close(fh); real_buf_size = (*hdr)->len + sizeof(struct hypfs_diag0c_hdr); if (rc == real_buf_size) break; l_0c_buf_size = real_buf_size; ht_free(buf); } while (1); *entry = buf + sizeof(struct hypfs_diag0c_hdr); } /* * Fill System Data */ void dg_debugfs_vmd0c_sys_cpu_fill(struct sd_sys *sys, u64 online_time, unsigned int cpu_cnt) { unsigned int i, cpu_online_vec[cpu_cnt]; struct hypfs_diag0c_entry *d0c_entry; struct hypfs_diag0c_hdr *hdr; u64 mgm_time; memset(cpu_online_vec, 0, sizeof(cpu_online_vec)); l_read_debugfs(&hdr, &d0c_entry); /* First fill online CPUs */ for (i = 0; i < hdr->count; i++) { mgm_time = G0(d0c_entry[i].totalproc - d0c_entry[i].virtcpu); l_sd_cpu_fill(sys, d0c_entry[i].cpu, online_time, d0c_entry[i].virtcpu, mgm_time, SD_CPU_STATE_OPERATING); cpu_online_vec[d0c_entry[i].cpu] = 1; } /* Then fill offline CPUs */ for (i = 0; i < cpu_cnt; i++) { if (cpu_online_vec[i]) continue; l_sd_cpu_fill(sys, i, 0, 0, 0, SD_CPU_STATE_STOPPED); } } /* * Initialize z/VM debugfs data gatherer */ int dg_debugfs_vmd0c_init(void) { int fh; fh = dg_debugfs_open(DEBUGFS_FILE); if (fh < 0) return -1; else close(fh); l_0c_buf_size = sizeof(struct hypfs_diag0c_hdr); return 0; } s390-tools-2.38.0/hyptop/helper.c000066400000000000000000000210271502674226300164170ustar00rootroot00000000000000/* * hyptop - Show hypervisor performance data on System z * * Helper functions * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/util_fmt.h" #include "lib/util_libc.h" #include "helper.h" #include "hyptop.h" #include "sd.h" /* * Globals */ static iconv_t l_iconv_ebcdic_ascii; static int l_underline_cnt; static int l_reverse_cnt; static int l_bold_cnt; /* * Print time of day */ void ht_print_time(void) { char time_str[40]; struct timeval tv; struct tm *tm; gettimeofday(&tv, NULL); tm = localtime(&tv.tv_sec); strftime(time_str, sizeof(time_str), "%H:%M:%S", tm); hyptop_printf("%s", time_str); } /* * Alloc uninitialized memory and exit on failure */ void *ht_alloc(size_t size) { void *ptr; ptr = malloc(size); if (!ptr) ERR_EXIT("Out of memory (%zu Kb)", size / 1024); return ptr; } /* * Alloc memory initialized with "0" and exit on failure */ void *ht_zalloc(size_t size) { void *ptr; ptr = calloc(1, size); if (!ptr) ERR_EXIT("Out of memory (%zu Kb)", size / 1024); return ptr; } /* * Realloc memory and exit on failure */ void *ht_realloc(void *old_ptr, size_t size) { void *ptr; assert(size != 0); if (old_ptr) ptr = realloc(old_ptr, size); else ptr = calloc(1, size); if (!ptr) ERR_EXIT("Out of memory (%lu Kb)", (unsigned long) size / 1024); return ptr; } /* * Convert EBCDIC string to ASCII */ void ht_ebcdic_to_ascii(char *in, char *out, size_t size) { size_t size_out = size; size_t size_in = size; size_t rc; rc = iconv(l_iconv_ebcdic_ascii, &in, &size_in, &out, &size_out); if (rc == (size_t) -1) ERR_EXIT_ERRNO("Code page translation EBCDIC-ASCII failed"); } /* * Get mount point for file system tye "fs_type" */ char *ht_mount_point_get(const char *fs_type) { struct mntent *mntbuf; FILE *mounts; mounts = setmntent(_PATH_MOUNTED, "r"); if (!mounts) ERR_EXIT_ERRNO("Could not find \"%s\" mount point", fs_type); while ((mntbuf = getmntent(mounts)) != NULL) { if (strcmp(mntbuf->mnt_type, fs_type) == 0) { endmntent(mounts); return ht_strdup(mntbuf->mnt_dir); } } endmntent(mounts); return NULL; } /* * Remove all trailing blanks and reture pointer to first non blank character */ char *ht_strstrip(char *s) { size_t size; char *end; size = strlen(s); if (!size) return s; end = s + size - 1; while (end >= s && isspace(*end)) end--; *(end + 1) = '\0'; while (*s && isspace(*s)) s++; return s; } /* * Return copy of string */ char *ht_strdup(const char *str) { char *rc; rc = ht_alloc(strlen(str) + 1); strcpy(rc, str); return rc; } /* * Print help icon in current line */ void ht_print_help_icon(void) { hyptop_print_seek_back(6); ht_underline_on(); hyptop_printf("?"); ht_underline_off(); hyptop_printf("=help"); } /* * Print headline */ void ht_print_head(const char *sys) { struct sd_cpu_type *cpu_type; int i; ht_print_time(); hyptop_printf(" "); if (sys) { ht_bold_on(); hyptop_printf("%s", sys); ht_bold_off(); hyptop_printf(" "); } hyptop_printf("cpu-"); ht_underline_on(); hyptop_printf("t"); ht_underline_off(); hyptop_printf(": "); sd_cpu_type_iterate(cpu_type, i) { if (!sd_cpu_type_selected(cpu_type)) continue; hyptop_printf("%s(%i) ", sd_cpu_type_id(cpu_type), sd_cpu_type_cpu_cnt(cpu_type)); } ht_print_help_icon(); hyptop_print_nl(); } /* * Curses attribute functions */ static void ht_attr_on(int attr) { if (g.o.batch_mode_specified) return; attron(attr); } static void ht_attr_off(int attr) { if (g.o.batch_mode_specified) return; attroff(attr); } void ht_bold_on(void) { if (l_bold_cnt == 0) ht_attr_on(A_BOLD); l_bold_cnt++; } void ht_bold_off(void) { l_bold_cnt--; if (l_bold_cnt == 0) ht_attr_off(A_BOLD); } void ht_underline_on(void) { if (l_underline_cnt == 0) ht_attr_on(A_UNDERLINE); l_underline_cnt++; } void ht_underline_off(void) { l_underline_cnt--; if (l_underline_cnt == 0) ht_attr_off(A_UNDERLINE); } void ht_reverse_on(void) { if (l_reverse_cnt == 0) ht_attr_on(A_REVERSE); l_reverse_cnt++; } void ht_reverse_off(void) { l_reverse_cnt--; if (l_reverse_cnt == 0) ht_attr_off(A_REVERSE); } /* * Print scroll bar */ void ht_print_scroll_bar(int row_cnt, int row_start, int rows_add_top, int rows_add_bottom, int can_scroll_up, int can_scroll_down, int with_border) { int row_cnt_displ, bar_len, start, i; double scale1, scale2; row_cnt_displ = MIN(row_cnt, g.c.row_cnt - rows_add_top - rows_add_bottom); if (row_cnt_displ <= 0) return; /* scale1: Scaling factor virtual screen to physical screen */ scale1 = ((double) row_cnt_displ) / ((double) row_cnt); /* scale2: Scaling factor physical screen to scroll bar size */ scale2 = ((double) row_cnt_displ - 2) / row_cnt_displ; bar_len = MAX(((double) row_cnt_displ * scale1 * scale2 + 0.5), 1); /* start: Start row in scroll bar */ start = ((double) row_start) * scale1 * scale2 + 0.5; if (row_cnt_displ - 2 - start < bar_len) start = row_cnt_displ - 2 - bar_len; ht_reverse_on(); if (with_border) { ht_underline_on(); hyptop_printf_pos(rows_add_top - 1, g.c.col_cnt - 1, " "); ht_underline_off(); hyptop_printf_pos(row_cnt_displ + rows_add_top, g.c.col_cnt - 1, " "); } ht_underline_on(); if (can_scroll_up) { ht_bold_on(); hyptop_printf_pos(rows_add_top, g.c.col_cnt - 1, "^"); ht_bold_off(); } else { hyptop_printf_pos(rows_add_top, g.c.col_cnt - 1, "^"); } ht_underline_off(); if (row_cnt_displ == 1) goto out; ht_underline_on(); if (can_scroll_down) { ht_bold_on(); hyptop_printf_pos(row_cnt_displ - 1 + rows_add_top, g.c.col_cnt - 1, "v"); ht_bold_off(); } else { hyptop_printf_pos(row_cnt_displ - 1 + rows_add_top, g.c.col_cnt - 1, "v"); } ht_underline_off(); if (row_cnt_displ == 2) goto out; for (i = 0; i < row_cnt_displ - 2; i++) hyptop_printf_pos(i + rows_add_top + 1, g.c.col_cnt - 1, " "); ht_underline_on(); hyptop_printf_pos(i + rows_add_top, g.c.col_cnt - 1, " "); ht_underline_off(); ht_bold_on(); for (i = 0; i < bar_len; i++) { if (i + start == row_cnt_displ - 3) ht_underline_on(); hyptop_printf_pos(i + start + 1 + rows_add_top, g.c.col_cnt - 1, "#"); if (i + start == row_cnt_displ - 3) ht_underline_off(); } ht_bold_off(); out: ht_reverse_off(); } /* * Convert ext TOD to microseconds */ u64 ht_ext_tod_2_us(void *tod_ext) { char *tod_ptr = tod_ext; u64 us, *tod1, *tod2; tod1 = (u64 *) tod_ptr; tod2 = (u64 *) &tod_ptr[8]; us = *tod1 << 8; us |= *tod2 >> 58; us = us >> 12; return us; } /* * Initialize helper module */ void hyptop_helper_init(void) { l_iconv_ebcdic_ascii = iconv_open("ISO-8859-1", "EBCDIC-US"); if (l_iconv_ebcdic_ascii == (iconv_t) -1) ERR_EXIT("Could not initialize iconv\n"); } /* * Calculate real SMT utilization * @core_us: core utilization in us * @thr_us: thread utilization in us * @mgm_us: management utilization in us * @thread_per_core: SMT thread count per core */ s64 ht_calculate_smt_util(u64 core_us, u64 thr_us, u64 mgm_us, int thread_per_core) { s64 component1, component2, smt_us; double smt_factor = g.o.smt_factor; component1 = thread_per_core * core_us - thr_us; if (thread_per_core > 1) component1 /= smt_factor; component2 = thr_us - core_us; smt_us = G0(component1 + component2 + mgm_us); return smt_us; } /* * Add two new key value pairs containing the current time as UNIX epoch and formatted string to a * structured output object. */ void ht_fmt_time(void) { struct timeval tv; struct tm *tm; char str[30]; gettimeofday(&tv, NULL); tm = localtime(&tv.tv_sec); if (!tm) return; util_fmt_pair(FMT_PERSIST, "time_epoch", "%lld", mktime(tm)); strftime(str, sizeof(str), "%F %T%z", tm); util_fmt_pair(FMT_PERSIST | FMT_QUOTE, "time", "%s", str); } /* * Add a new object for available CPU types to a structured output object. */ void ht_fmt_cpu_types(void) { struct sd_cpu_type *cpu_type; int i; util_fmt_obj_start(FMT_DEFAULT, "cputypes"); sd_cpu_type_iterate(cpu_type, i) { char *cpu_type_str = sd_cpu_type_id(cpu_type); util_str_tolower(cpu_type_str); util_fmt_pair(FMT_PERSIST, cpu_type_str, "%i", sd_cpu_type_cpu_cnt(cpu_type)); } util_fmt_obj_end(); /* cpus{} */ } s390-tools-2.38.0/hyptop/helper.h000066400000000000000000000044211502674226300164230ustar00rootroot00000000000000/* * hyptop - Show hypervisor performance data on System z * * Helper functions * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef HELPER_H #define HELPER_H #include #include #include #include "lib/util_base.h" #include "lib/util_libc.h" #include "lib/zt_common.h" #define G0(x) MAX(0, (s64) (x)) /* * Helper Prototypes */ extern void hyptop_helper_init(void); extern char *ht_strstrip(char *str); extern char *ht_strdup(const char *str); extern void ht_print_head(const char *sys); extern void ht_print_help_icon(void); extern void ht_ebcdic_to_ascii(char *in, char *out, size_t len); extern char *ht_mount_point_get(const char *fs_type); extern u64 ht_ext_tod_2_us(void *tod_ext); extern void ht_print_time(void); extern s64 ht_calculate_smt_util(u64 core_us, u64 thr_us, u64 mgm_us, int thread_per_core); /* * Memory alloc functions */ extern void *ht_zalloc(size_t size); extern void *ht_alloc(size_t size); extern void *ht_realloc(void *ptr, size_t size); static inline void ht_free(void *ptr) { free(ptr); } /* * Curses extensions */ #define KEY_RETURN 0012 #define KEY_ESCAPE 0033 void ht_bold_on(void); void ht_bold_off(void); void ht_reverse_on(void); void ht_reverse_off(void); void ht_underline_on(void); void ht_underline_off(void); void ht_str_to_upper(char *str); void ht_print_scroll_bar(int row_cnt, int row_start, int row_bar_start, int row_bar_bottom, int can_scroll_up, int can_scroll_down, int with_boder); /* * util_fmt helper functions */ void ht_fmt_time(void); void ht_fmt_cpu_types(void); /* * Error Macros */ #define ERR_MSG(x...) \ do { \ hyptop_text_mode(); \ fflush(stdout); \ fprintf(stderr, "%s: ", g.prog_name);\ fprintf(stderr, x); \ } while (0) #define ERR_EXIT(x...) \ do { \ hyptop_text_mode(); \ fflush(stdout); \ fprintf(stderr, "%s: ", g.prog_name); \ fprintf(stderr, x); \ hyptop_exit(1); \ exit(1); \ } while (0) #define ERR_EXIT_ERRNO(x...) \ do { \ fflush(stdout); \ fprintf(stderr, "%s: ", g.prog_name); \ fprintf(stderr, x); \ fprintf(stderr, " (%s)", strerror(errno)); \ fprintf(stderr, "\n"); \ hyptop_exit(1); \ } while (0) #endif /* HELPER_H */ s390-tools-2.38.0/hyptop/hyptop.8000066400000000000000000000326151502674226300164150ustar00rootroot00000000000000.\" Copyright 2017 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH HYPTOP 8 "Nov 2009" "s390-tools" .SH NAME hyptop \- Show hypervisor performance data on System z .SH SYNOPSIS .B hyptop [OPTIONS] .SH DESCRIPTION .B hyptop provides a dynamic real-time view of a hypervisor environment on System z. It works with either the z/VM or the LPAR hypervisor. Depending on the available data it shows for example CPU and memory information about running LPARs or z/VM guests. hyptop provides two windows: .IP " -" sys_list: Shows a list of systems that the hypervisor is currently running .IP " -" sys: Shows one system in more detail. .PP System names in hyptop are either LPAR names as shown on the SE or HMC, or z/VM guest IDs that identify z/VM guest virtual machines. .PP You can run hyptop in interactive mode (default) or in batch mode with the "\-b" option. For how to use the interactive mode, see the online help (enter "?" after hyptop is started). .SH OPTIONS .TP .BR "\-h" " or " "\-\-help" Print usage information, then exit. .TP .BR "\-v" " or " "\-\-version" Print version information, then exit. .TP .BR "\-w " " or " "\-\-window=" Select current window. Use the options "--sys", "--fields", and "--sort" to modify the current window. The last window specified with the "--window" option will be used as start window. The default window is "sys_list". .TP .BR "\-s ,..." " or " "\-\-sys=,..." Select systems for current window. If this option is specified, only the selected systems are shown for the window. For window "sys" only one system can be specified. .TP .BR "\-f [:],..." " or " "\-\-fields=[:],..." Select fields and units in the current window. "F_LETTER" is the field letter that identifies uniquely a field (for example "c" for CPU time). "UNIT" is the used entity for displaying data for the field (for example "us" for microseconds). See FIELDS and UNITS below for definitions. If the "--fields" option is specified, only the selected fields are shown. .TP .BR "\-S " " or " "\-\-sort=" Select sort field for current window. To reverse the sort order, specify the option twice. See FIELDS below for definitions. .TP .BR "\-t ,..." " or " "\-\-cpu_types=,..." Select CPU types that are used for CPU time calculations. See CPU TYPES below for definitions. .TP .BR "\-b" " or " "\-\-batch_mode" Use batch mode (no curses). This can be useful for sending output from hyptop to another program, a file, or a line mode terminal. In this mode no user input is accepted. .TP .BR "\-\-format=" .RS Use this option to show output in a machine-readable format. FORMAT can be one of: .IP \(bu 3 .B json: Single JavaScript Object Notation (JSON) data structure Data for all iterations is formatted as one JSON data structure in multiple lines to make them more readable by humans. .BR See section "OUTPUT FORMAT" for more details. .BR .PP .IP \(bu 3 .B json\-seq: Sequence of JSON data structures Data for each iteration is formatted as a separate JSON data structure prefixed with an ASCII Record Separator character (0x1e) and suffixed with an ASCII Line Feed character (0x0a) in accordance with RFC7464. .BR See section "OUTPUT FORMAT" for more details. .BR .PP .IP \(bu 3 .B pairs: Textual key=value pairs .PP .IP \(bu 3 .B csv: Comma-separated-value (CSV) list .BR All values are enclosed in double quotation marks and separated by commas. The first line of output contains a list of headings. Subsequent lines each represent data for one system in one iteration. .PP This option implies the "\-\-batch_mode" option. .RE .TP .BR "\-d " " or " "\-\-delay=" Specifies the delay between screen updates. .TP .BR "\-m " " or " "\-\-smt_factor=" Specifies a workload dependent SMT speedup factor. For IBM z15 servers, the default value is 1.3. If the workload benefits from SMT, you can specify a higher value. If the workload does not benefit from SMT, specifying lower values results in more accurate reports of real CPU SMT utilization field for LPARs. There is no hard boundary except that it must be a positive value. Example ranges to select a sensible value from: For IBM z13: [0.8, 1.3] For IBM z15: [1.1, 1.5] .TP .BR "\-n " " or " "\-\-iterations=" Specifies the maximum number of iterations before ending. .SH PREREQUISITES The following things are required to run hyptop: .IP " -" The Linux kernel must have the required support to provide the performance data. .IP " -" debugfs has to be mounted. .IP " -" The hyptop user must have read permission for the required debugfs files. .IP " -" You can always monitor the guest operating system where hyptop is running. To monitor any other operating system instances running on the same hypervisor as hyptop, you will need additional permissions. For z/VM, the guest virtual machine must have privilege class B. For LPAR, on the HMC or SE security menu of the LPAR activation profile, select the Global performance data control checkbox. .PP To mount debugfs, you can use this command: # mount none -t debugfs /sys/kernel/debug To make this persistent, add the following to "/etc/fstab": none /sys/kernel/debug debugfs defaults 0 0 .SH FIELDS The supported fields depend on the available data on the hypervisor. This is different between LPAR and z/VM. It might also depend on machine type, z/VM version and kernel version. Each field has a unique field letter that can be used to select the field in interactive mode or through the "--fields" command line option. The following fields are available under LPAR: In "sys_list" and "sys" window: 'c' - Core dispatch time per second 'e' - Thread time per second 'S' - Real CPU SMT utilization 'm' - Management time per second 'C' - Total core dispatch time 'E' - Total thread time 'M' - Total management time 'o' - Online time In "sys_list" window: '#' - Number of cores (sum of initial and reserved) 'T' - Number of threads (sum of initial and reserved) In "sys" window: 'p' - CPU type 'v' - Visualization of core dispatch time per second The following fields are available under z/VM: In "sys_list" and "sys" window: 'c' - CPU time per second 'm' - Management time per second (*) 'C' - Total CPU time 'M' - Total management time (*) 'o' - Online time In "sys_list" window: '#' - Number of CPUs 'O' - Number of operating CPUs 'u' - Used memory 'a' - Maximum memory 'r' - Current weight 'x' - Maximum weight In "sys" window: 'v' - Visualization of CPU time per second (*) Only available for the local guest virtual machine Only available if the system has the required support .SH UNITS Depending on the field type the values can be displayed in different units. The following units are supported: Time: 'us' - Microseconds (10^-6 seconds) 'ms' - Millisconds (10^-3 seconds) '%' - Hundreds of a second (10^-2 seconds) or percent 's' - Seconds 'm' - Minutes 'hm' - Hours & Minutes 'dhm' - Days & Hours & Minutes Memory: 'kib' - Kibibytes (1.024 bytes) 'mib' - Mebibytes (1.048.576 bytes) 'gib' - Gibibytes (1.073.741.824 bytes) Miscellaneous: 'str' - String '#' - Count/Number 'vis' - Visualization .SH CPU TYPES Depending on the hypervisor different CPU types are supported. These CPU types can be selected either interactively or with the "--cpu_types" command line option. The calculation of the CPU data only uses CPUs of the specified types. On LPAR the following CPU types are supported: 'IFL' - Integrated Facility for Linux 'CP' - CP processor type 'UN' - Unspecified processor type (other than CP or IFL) NOTE: It is possible that on older machines also IFLs are shown as CPs. On z/VM currently only the processor type 'UN' is available. .SH CPU DATA For Linux on z/VM, no performance data is available for individual CPUs on remote guests. Therefore, the hyptop "sys" window shows identical values for each of the operating CPUs. For the CPU time fields, these values represent the total across all operating CPUs divided by the number of operating CPUs. For the online time field, the value is the time during which at least one CPU has been operational. Operating CPUs are shown with CPU identifier "0" and stopped CPUs with "1". .SH OUTPUT FORMATS This section contains additional information for some of the supported output formats. .SS json JSON output consists of a top-level object with the following properties (key-value pairs): .IP \(bu 3 .BR meta : Tool meta-data including API level, version, host name, and time of invocation .PP .IP \(bu 3 .BR hyptop : Hypervisor performance data .PP Performance data is stored as an array of iteration objects under the "hyptop" property in the top-level object. Iteration objects contain the following properties: .IP \(bu 3 .BR iteration : sequential count of the current iteration .PP .IP \(bu 3 .BR "time" " and " "time_epoch" : time when the iteration was recorded, in human-readable format and as Unix epoch. .PP .IP \(bu 3 .BR cputypes : contains the number of CPUs hyptop detected, per CPU type. Possible properties are .BR "cp" ", " "ifl" " and " "un". .PP .IP \(bu 3 .BR systems : when the "sys_list" window is selected, contains an array of system objects that contain the performance data per system. Each system object contains a "system" property and properties for its performance data (see below). .PP .IP \(bu 3 .BR cpus : when the "sys" window is selected, contains an array of CPU objects that contain the performance data per CPU of the selected system. Each CPU object contains a "coreid" property and properties for its performance data (see below). .PP .IP \(bu 3 .BR summary : contains aggregated performance data over all systems in the "systems" or "cpus" array. .PP All performance data available for the platform (LPAR, z/VM) hyptop is executed on is shown. Selecting specific fields with \-\-fields is not supported but it is possible to change the units of fields. Properties for performance data are labeled after their respective columns. Example JSON output for a single iteration: .br .RS { .br "meta": { .br "api_level": 1, .br "version": "2.35.0", .br "host": "mylpar.local", .br "time_epoch": 1730787834, .br "time": "2024-11-05 07:23:54+0100" .br }, .br "hyptop": [ .br { .br "iteration": 0, .br "time_epoch": 1730787834, .br "time": "2024-11-05 07:23:54+0100", .br "cputypes": { .br "ifl": 124, .br "cp": 1, .br "un": 0 .br }, .br "systems": [ .br { .br "system": "MYLPAR", .br "#core": 30, .br "#the": 60, .br "core": 379.57, .br "the": 382.43, .br "smt": 294.73, .br "mgm": 2.09, .br "core+": "1503:13", .br "the+": "2060:45", .br "mgm+": "22:19", .br "online": "18:18:09" .br } .br ], .br "summary": { .br "#core": 30, .br "#the": 60, .br "core": 379.57, .br "the": 382.43, .br "smt": 294.73, .br "mgm": 2.09, .br "core+": "1503:13", .br "the+": "2060:45", .br "mgm+": "22:19", .br "online": "18:18:09" .br } .br } .br ] .br } .br .RE .SS json\-seq The json\-seq output format is a variation of the JSON output format described above with the following differences: .IP \(bu 3 Output consists of a sequence of top-level JSON objects, each contained in single line with no indentation .br .IP \(bu 3 Each top-level object is prefixed by an ASCII Record Separator character (0x1e) and suffixed with an ASCII Line Feed character (0x0a) in accordance with RFC7464 .br .PP .IP \(bu 3 The first object contains tool meta-data properties defined in the previous section .br .PP .IP \(bu 3 Subsequent objects each represent performance data for one iteration .br .PP .SH EXAMPLES To start hyptop with the "sys_list" window in interactive mode, enter: .br # hyptop .br To start hyptop with the "sys_list" window in batch mode, enter: .br # hyptop -b .br To start hyptop with the "sys_list" window in interactive mode with the fields CPU time (in milliseconds) and online time (unit default) and sort the output according to online time, enter: .br # hyptop -f c:ms,o -S o .br To start hyptop with the "sys" window with system "MYLPAR" with the fields CPU time (unit milliseconds) and online time (unit default) and sort the output reverse according the online time, enter: .br # hyptop -w sys -s MYLPAR -f c:ms,o -S o -S o .br To start hyptop with the "sys_list" window in batch mode with update delay 5 seconds and 10 iterations, enter: .br # hyptop -b -d 5 -n 10 .br To start hyptop with the "sys_list" window and use only CPU types IFL and CP for CPU time calculation, enter: .br # hyptop -t ifl,cp .br To show a single iteration of performance data as a formatted JSON object for system "MYLPAR" and with fields CPU time, thread time, management time and online time in microseconds, enter: .br # hyptop -f C:us,E:us,M:us,o:us --format json -n 1 -s MYLPAR .SH ENVIRONMENT .TP .B TERM The TERM environment variable specifies your terminal type. To run \fBhyptop\fP in interactive mode the TERM environment variable has to be set. The interactive mode is not available for terminals that have TERM=dumb (e.g. line mode terminals). s390-tools-2.38.0/hyptop/hyptop.c000066400000000000000000000156421502674226300164710ustar00rootroot00000000000000/* * hyptop - Show hypervisor performance data on System z * * Main & init functions * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include "lib/util_fmt.h" #include "dg_debugfs.h" #include "helper.h" #include "hyptop.h" #include "opts.h" #include "sd.h" #include "win_cpu_types.h" #ifdef WITH_HYPFS #include "dg_hypfs.h" #endif /* * Globals for the whole program */ struct hyptop_globals g; /* * Get current terminal size and tell curses about it */ static void l_term_size_get(void) { struct winsize ws; g.c.col_cnt = 80; g.c.row_cnt = 24; if (ioctl(1, TIOCGWINSZ, &ws) != -1) { if ((ws.ws_col != 0) && (ws.ws_row != 0)) { g.c.col_cnt = ws.ws_col; g.c.row_cnt = ws.ws_row; } } resizeterm(g.c.row_cnt, g.c.col_cnt); } /* * Process input */ static enum hyptop_win_action l_process_input(struct hyptop_win *win) { int c; /* Skip all resize events */ while ((c = wgetch(stdscr)) == KEY_RESIZE) {} return win->process_input(win, c); } /* * Process input with timeout */ static enum hyptop_win_action l_process_input_timeout(time_t time_s, long time_us) { struct timeval tv; fd_set fds; int rc; while (1) { FD_ZERO(&fds); FD_SET(0, &fds); tv.tv_sec = time_s; tv.tv_usec = time_us; rc = select(1, &fds, NULL, NULL, &tv); switch (rc) { case 0: /* Timeout */ return WIN_KEEP; case 1: /* Input */ if (l_process_input(g.w.cur) == WIN_SWITCH) return WIN_SWITCH; continue; case -1: if (errno != EINTR) ERR_EXIT_ERRNO("Select call failed"); /* Signal: Resize */ hyptop_update_term(); continue; default: assert(0); } } } /* * Sleep */ static enum hyptop_win_action l_sleep(time_t time_s, long time_us) { struct timespec ts; ts.tv_sec = time_s; ts.tv_nsec = time_us * 1000; nanosleep(&ts, NULL); return WIN_KEEP; } /* * External process input with timeout funciton */ enum hyptop_win_action hyptop_process_input_timeout(void) { enum hyptop_win_action rc; if (g.o.batch_mode_specified) { opts_iterations_next(); rc = l_sleep(g.o.delay_s, g.o.delay_us); } else { rc = l_process_input_timeout(g.o.delay_s, g.o.delay_us); opts_iterations_next(); } return rc; } /* * External process input funciton */ enum hyptop_win_action hyptop_process_input(void) { return l_process_input_timeout(-1U, 0); } /* * Signal handler for exiting hyptop */ static void l_sig_exit(int sig) { (void) sig; hyptop_exit(0); } /* * Install signal handler */ static void l_sig_handler_init(void) { struct sigaction sigact; /* Ignore signals SIGUSR1 and SIGUSR2 */ if (sigemptyset(&sigact.sa_mask) < 0) goto fail; sigact.sa_flags = 0; sigact.sa_handler = SIG_IGN; if (sigaction(SIGUSR1, &sigact, NULL) < 0) goto fail; if (sigaction(SIGUSR2, &sigact, NULL) < 0) goto fail; /* Exit on SIGINT, SIGTERM, SIGHUP, ... */ if (sigemptyset(&sigact.sa_mask) < 0) goto fail; sigact.sa_handler = l_sig_exit; if (sigaction(SIGINT, &sigact, NULL) < 0) goto fail; if (sigaction(SIGTERM, &sigact, NULL) < 0) goto fail; if (sigaction(SIGHUP, &sigact, NULL) < 0) goto fail; if (sigaction(SIGQUIT, &sigact, NULL) < 0) goto fail; if (sigaction(SIGALRM, &sigact, NULL) < 0) goto fail; if (sigaction(SIGPIPE, &sigact, NULL) < 0) goto fail; return; fail: ERR_EXIT_ERRNO("Could not initialize signal handler"); } /* * Start curses */ static int l_initscr(void) { if (!initscr()) return ERR; g.c.initialized = 1; atexit(hyptop_text_mode); return 0; } /* * Check if terminal is able to run hyptop in curses mode */ static void l_term_check(void) { char *term_str = getenv("TERM"); if (!term_str) ERR_EXIT("Please set TERM environment variable or " "try \"--batch_mode\"\n"); /* S390 line mode terminals normally have TERM=dumb */ if (strcmp(term_str, "dumb") == 0) ERR_EXIT("Terminal of type \"dumb\" is not supported," " try \"--batch_mode\"\n"); } /* * Init util_fmt if --format is specified on the command line. */ static void l_fmt_init(void) { unsigned int flags = FMT_WARN; if (!g.o.format_specified) return; if (g.o.format == FMT_CSV) flags |= FMT_QUOTEALL; if (g.o.format == FMT_JSON || g.o.format == FMT_JSONSEQ) flags |= FMT_HANDLEINT; util_fmt_init(stdout, g.o.format, flags, 1); } /* * Init curses */ static void l_term_init(void) { if (g.o.batch_mode_specified) return; l_term_check(); if (l_initscr() == ERR) goto fail; if (noecho() == ERR) goto fail; if (nodelay(stdscr, TRUE) == ERR) goto fail; if (cbreak() == ERR) /* Line buffering disabled. pass on everything */ goto fail; if (keypad(stdscr, TRUE) == ERR) goto fail; curs_set(0); /* prevent cursor from blinking */ l_term_size_get(); l_sig_handler_init(); return; fail: ERR_EXIT("Could not initialize curses, try \"--batch_mode\"\n"); } /* * Initialize data gatherer */ #ifdef WITH_HYPFS static void l_dg_init(void) { if (dg_debugfs_init(0) == 0) return; if (dg_hypfs_init() == 0) return; ERR_EXIT("Could not initialize data gatherer\n"); } #else static void l_dg_init(void) { dg_debugfs_init(1); } #endif /* * Windows event loop */ static void l_event_loop(void) { while (1) g.w.cur->run(g.w.cur); } /* * Clear terminal and write new window content to it */ static void l_update_term_curses(void) { /* Init screen */ l_term_size_get(); curs_set(0); /* pervent cursor from blinking */ move(0, 0); erase(); hyptop_printf_init(); /* Write window to screen */ g.w.cur->update_term(g.w.cur); refresh(); } /* * Write window content in line mode */ static void l_update_term_batch(void) { g.w.cur->update_term(g.w.cur); } /* * Update terminal with new window content */ void hyptop_update_term(void) { if (g.o.batch_mode_specified) l_update_term_batch(); else l_update_term_curses(); } /* * Switch to new window "win" */ enum hyptop_win_action win_switch(struct hyptop_win *win) { assert(g.w.prev_cnt < sizeof(g.w.prev) / sizeof(void *)); g.w.prev[g.w.prev_cnt] = g.w.cur; g.w.prev_cnt++; g.w.cur = win; return WIN_SWITCH; } /* * Switch back to previous window */ enum hyptop_win_action win_back(void) { g.w.prev_cnt--; g.w.cur = g.w.prev[g.w.prev_cnt]; return WIN_SWITCH; } /* * Switch to text mode */ void hyptop_text_mode(void) { if (!g.c.initialized) return; g.c.initialized = 0; clear(); refresh(); endwin(); } /* * Exit hyptop */ void __noreturn hyptop_exit(int rc) { hyptop_text_mode(); exit(rc); } /* * Initialize all modules and start first window */ int main(int argc, char *argv[]) { opts_parse(argc, argv); l_fmt_init(); hyptop_helper_init(); sd_init(); l_dg_init(); opt_verify_systems(); l_term_init(); win_sys_list_init(); win_sys_init(); g.win_cpu_types = win_cpu_types_new(); l_event_loop(); return 0; } s390-tools-2.38.0/hyptop/hyptop.h000066400000000000000000000105761502674226300164770ustar00rootroot00000000000000/* * hyptop - Show hypervisor performance data on System z * * Command line options, window definition, print functions, etc. * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef HYPTOP_H #define HYPTOP_H #include #include #include #include #include "lib/util_fmt.h" #include "helper.h" #include "nav_desc.h" #include "table.h" #define HYPTOP_OPT_DEFAULT_DELAY 2 #define HYPTOP_OPT_DEFAULT_SMT_SCALE 1.3 #define HYPTOP_MAX_WIN_DEPTH 4 #define HYPTOP_MAX_LINE 512 #define PROG_NAME "hyptop" /* * Options info */ struct hyptop_str_vec_opt { unsigned int specified; char **vec; unsigned int cnt; }; struct hyptop_col_vec_opt { unsigned int specified; struct table_col_spec **vec; unsigned int cnt; }; struct hyptop_win_opts { struct hyptop_str_vec_opt sys; struct hyptop_col_vec_opt fields; unsigned int sort_field_specified; char sort_field; }; struct hyptop_opts { unsigned int win_specified; unsigned int batch_mode_specified; unsigned int format_specified; enum util_fmt_t format; unsigned int iterations_specified; unsigned int iterations; unsigned int iterations_act; struct hyptop_win *cur_win; struct hyptop_str_vec_opt cpu_types; int delay_s; int delay_us; double smt_factor; }; /* * Curses info */ struct hyptop_curses { int row_cnt; int col_cnt; char line[HYPTOP_MAX_LINE]; int x; int y; int initialized; }; /* * Window info */ struct hyptop_win_info { struct hyptop_win *cur; struct hyptop_win *prev[HYPTOP_MAX_WIN_DEPTH]; unsigned int prev_cnt; }; /* * Globals definition */ struct hyptop_globals { struct hyptop_opts o; struct hyptop_curses c; struct hyptop_win_info w; const char *prog_name; struct hyptop_win *win_cpu_types; }; extern struct hyptop_globals g; /* * Print functions */ #define hyptop_printf_pos(y, x, p...) \ do { \ if (g.o.batch_mode_specified) \ printf(p); \ else { \ int len; \ len = snprintf(g.c.line, sizeof(g.c.line) - 1, p); \ len = MIN(len, (g.c.col_cnt - (x))); \ if (len > 0) { \ mvaddnstr((y), (x), g.c.line, len); \ } \ } \ } while (0) #define hyptop_printf(p...) \ do { \ if (g.o.batch_mode_specified) \ printf(p); \ else { \ int len; \ len = snprintf(g.c.line, sizeof(g.c.line) - 1, p); \ len = MIN(len, (g.c.col_cnt - g.c.x)); \ if (len > 0) { \ mvaddnstr(g.c.y, g.c.x, g.c.line, len); \ g.c.x += len; \ } \ } \ } while (0) static inline void hyptop_printf_init(void) { g.c.x = 0; g.c.y = 0; } static inline void hyptop_print_seek_back(int i) { unsigned int cnt = MAX(g.c.col_cnt - g.c.x - i, 0); if (g.o.batch_mode_specified) return; if (cnt) { memset(g.c.line, ' ', cnt); assert(cnt < sizeof(g.c.line)); g.c.line[cnt] = 0; addstr(g.c.line); } g.c.x = g.c.col_cnt - i; } static inline void hyptop_print_nl(void) { unsigned int cnt = MAX(g.c.col_cnt - g.c.x, 0); if (g.o.batch_mode_specified) { printf("\n"); return; } if (cnt) { memset(g.c.line, ' ', g.c.col_cnt - g.c.x); assert(cnt < sizeof(g.c.line)); g.c.line[cnt] = 0; addstr(g.c.line); } g.c.x = 0; g.c.y++; } /* * hyptop windows */ enum hyptop_win_action { WIN_SWITCH, WIN_KEEP, }; extern enum hyptop_win_action hyptop_process_input_timeout(void); extern enum hyptop_win_action hyptop_process_input(void); extern enum hyptop_win_action win_switch(struct hyptop_win *w); extern enum hyptop_win_action win_back(void); struct hyptop_win; struct hyptop_win { enum hyptop_win_action (*process_input)(struct hyptop_win *w, int c); void (*update_term)(struct hyptop_win *w); void (*run)(struct hyptop_win *w); const char *id; const char *desc; struct nav_desc **desc_normal_vec; struct nav_desc **desc_select_vec; struct nav_desc **desc_general_vec; struct hyptop_win_opts opts; }; /* * Window sys_list */ extern struct hyptop_win win_sys_list; extern void win_sys_list_init(void); /* * Window sys */ extern struct hyptop_win win_sys; extern void win_sys_set(const char *sys_id); extern void win_sys_init(void); /* * Window cpu_types */ extern void win_cpu_types_init(void); /* * Misc functions */ extern void hyptop_update_term(void); extern void __noreturn hyptop_exit(int rc); extern void hyptop_text_mode(void); #endif /* HYPTOP_H */ s390-tools-2.38.0/hyptop/nav_desc.c000066400000000000000000000125441502674226300167260ustar00rootroot00000000000000/* * hyptop - Show hypervisor performance data on System z * * Description of navigation keys * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include "nav_desc.h" #include "tbox.h" #define L_KEY_LEN 14 #define L_KEY_FMT "%-14s" /* Select mode */ struct nav_desc nav_desc_select_mode_enter = { .desc = "Enter select mode", .keys = {"RIGHT", "l", NULL}, }; struct nav_desc nav_desc_select_mode_leave = { .desc = "Leave select mode", .keys = {"LEFT", "h", NULL}, }; /* "sys" Window */ struct nav_desc nav_desc_win_enter_sys = { .desc = "Go to the \"sys\" window for selected system", .keys = {"RIGHT", "l", NULL}, }; struct nav_desc nav_desc_win_leave_sys = { .desc = "Go to the previous window", .keys = {"LEFT", "h", "q", NULL}, }; struct nav_desc nav_desc_win_leave_sys_fast = { .desc = "Go to the previous window", .keys = {"q", NULL}, }; /* "fields" window */ struct nav_desc nav_desc_win_enter_fields = { .desc = "Go to the \"fields\" window", .keys = {"f", NULL}, } ; struct nav_desc nav_desc_win_leave_fields = { .desc = "Go to the previous window", .keys = {"LEFT", "ENTER", "h", "f", "q", NULL}, }; struct nav_desc nav_desc_win_leave_fields_fast = { .desc = "Go to the previous window", .keys = {"f", "q", NULL}, }; /* "cpu_types" window */ struct nav_desc nav_desc_win_enter_cpu_types = { .desc = "Go to the \"cpu_types\" window", .keys = {"t", NULL}, }; struct nav_desc nav_desc_win_leave_cpu_types = { .desc = "Go to the previous window", .keys = {"LEFT", "ENTER", "h", "t", "q", NULL}, }; struct nav_desc nav_desc_win_leave_cpu_types_fast = { .desc = "Go to the previous window", .keys = {"t", "q", NULL}, }; /* Marks */ struct nav_desc nav_desc_marks_clear = { .desc = "Clear all marked rows", .keys = {"SPACE", NULL}, }; struct nav_desc nav_desc_mark_toggle = { .desc = "Toggle mark for selected row", .keys = {"SPACE", NULL}, }; struct nav_desc nav_desc_mark_toggle_view = { .desc = "Toggle view for marked rows", .keys = {".", NULL}, }; /* Units */ struct nav_desc nav_desc_col_unit_increase = { .desc = "Increase unit type of selected column", .keys = {"+", NULL}, }; struct nav_desc nav_desc_col_unit_decrease = { .desc = "Decrease unit type of selected column", .keys = {"-", NULL}, }; struct nav_desc nav_desc_row_unit_increase = { .desc = "Increase unit type of selected row", .keys = {"+", NULL}, }; struct nav_desc nav_desc_row_unit_decrease = { .desc = "Decrease unit type of selected row", .keys = {"-", NULL}, }; /* Select columns */ struct nav_desc nav_desc_select_col_next = { .desc = "Select next column", .keys = {">", NULL}, }; struct nav_desc nav_desc_select_col_prev = { .desc = "Select previous column", .keys = {"<", NULL}, }; struct nav_desc nav_desc_select_col_hotkey = { .desc = "Select column with hotkey", .keys = {"", NULL}, }; /* Quit */ struct nav_desc nav_desc_quit = { .desc = "Quit program", .keys = {"q", NULL}, }; /* Select rows */ struct nav_desc nav_desc_toggle_mark_hotkey = { .desc = "Toggle mark for row with hotkey", .keys = {"", NULL}, }; /* Navigation */ struct nav_desc nav_desc_scroll_up_line = { .desc = "Scroll up one line", .keys = {"UP", "k", NULL}, }; struct nav_desc nav_desc_scroll_down_line = { .desc = "Scroll down one line", .keys = {"DOWN", "j", NULL}, }; struct nav_desc nav_desc_scroll_up_page = { .desc = "Scroll up one page", .keys = {"PGUP", NULL}, }; struct nav_desc nav_desc_scroll_down_page = { .desc = "Scroll down one page", .keys = {"PGDOWN", NULL}, }; struct nav_desc nav_desc_scroll_up_head = { .desc = "Scroll up to head of window", .keys = {"g", NULL}, }; struct nav_desc nav_desc_scroll_down_tail = { .desc = "Scroll down to tail of window", .keys = {"G", NULL}, }; /* * Add navigation descriptons to text box */ static void l_nav_desc_add(struct tbox *tb, struct nav_desc *desc) { char keys_str[L_KEY_LEN + 1]; unsigned int i, first; char *key; first = 1; keys_str[0] = 0; for (i = 0; (key = desc->keys[i]); i++) { /* * If we have used the whole space for the keys, * we write the line and begin a new one */ if (strlen(desc->keys[i]) + strlen(keys_str) + 1 > L_KEY_LEN) { tbox_printf(tb, " " L_KEY_FMT ": %s", keys_str, desc->desc); keys_str[0] = 0; first = 1; } if (!first) strcat(keys_str, ","); else first = 0; strcat(keys_str, "'"); strcat(keys_str, desc->keys[i]); strcat(keys_str, "'"); assert(strlen(keys_str) <= L_KEY_LEN); } tbox_printf(tb, " " L_KEY_FMT ": %s", keys_str, desc->desc); } /* * Add navigation descriptions for "normal", "select" and "general" to text box */ void nav_desc_add(struct tbox *tb, struct nav_desc **desc_normal, struct nav_desc **desc_select, struct nav_desc **desc_general) { unsigned int i; tbox_printf(tb, "\\BSupported keys in this window\\B"); tbox_printf(tb, " "); tbox_printf(tb, "NORMAL MODE:"); for (i = 0; (desc_normal[i]); i++) l_nav_desc_add(tb, desc_normal[i]); tbox_printf(tb, " "); tbox_printf(tb, "SELECT MODE:"); for (i = 0; (desc_select[i]); i++) l_nav_desc_add(tb, desc_select[i]); tbox_printf(tb, " "); tbox_printf(tb, "GENERAL:"); for (i = 0; (desc_general[i]); i++) l_nav_desc_add(tb, desc_general[i]); tbox_printf(tb, " "); } s390-tools-2.38.0/hyptop/nav_desc.h000066400000000000000000000037171502674226300167350ustar00rootroot00000000000000/* * hyptop - Show hypervisor performance data on System z * * Description of navigation keys * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef NAV_DESC_H #define NAV_DESC_H #include "tbox.h" struct nav_desc { char *desc; char *keys[]; }; void nav_desc_add(struct tbox *tb, struct nav_desc **desc_normal, struct nav_desc **desc_select, struct nav_desc **desc_general); extern struct nav_desc nav_desc_quit; extern struct nav_desc nav_desc_select_mode_enter; extern struct nav_desc nav_desc_select_mode_leave; extern struct nav_desc nav_desc_win_enter_sys; extern struct nav_desc nav_desc_win_leave_sys; extern struct nav_desc nav_desc_win_leave_sys_fast; extern struct nav_desc nav_desc_win_enter_fields; extern struct nav_desc nav_desc_win_leave_fields; extern struct nav_desc nav_desc_win_leave_fields_fast; extern struct nav_desc nav_desc_win_enter_cpu_types; extern struct nav_desc nav_desc_win_leave_cpu_types; extern struct nav_desc nav_desc_win_leave_cpu_types_fast; extern struct nav_desc nav_desc_marks_clear; extern struct nav_desc nav_desc_mark_toggle; extern struct nav_desc nav_desc_mark_toggle_view; extern struct nav_desc nav_desc_col_unit_increase; extern struct nav_desc nav_desc_col_unit_decrease; extern struct nav_desc nav_desc_row_unit_increase; extern struct nav_desc nav_desc_row_unit_decrease; extern struct nav_desc nav_desc_select_col_next; extern struct nav_desc nav_desc_select_col_prev; extern struct nav_desc nav_desc_select_col_hotkey; extern struct nav_desc nav_desc_toggle_mark_hotkey; extern struct nav_desc nav_desc_scroll_up_line; extern struct nav_desc nav_desc_scroll_down_line; extern struct nav_desc nav_desc_scroll_up_page; extern struct nav_desc nav_desc_scroll_down_page; extern struct nav_desc nav_desc_scroll_up_head; extern struct nav_desc nav_desc_scroll_down_tail; #endif /* NAV_DESC_H */ s390-tools-2.38.0/hyptop/opts.c000066400000000000000000000237661502674226300161410ustar00rootroot00000000000000/* * hyptop - Show hypervisor performance data on System z * * Command line parsing * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include "lib/util_fmt.h" #include "lib/util_libc.h" #include "lib/zt_common.h" #include "getopt.h" #include "helper.h" #include "hyptop.h" #include "opts.h" #include "sd.h" #include "table.h" static const char l_copyright_str[] = "Copyright IBM Corp. 2010, 2017"; /* * Help text for tool */ static char HELP_TEXT[] = "Usage: hyptop [OPTIONS]\n" "\n" "Show hypervisor performance data on System z.\n" "\n" "-h, --help Print this help, then exit\n" "-v, --version Print version information, then exit\n" "-w, --window WIN_NAME Current window (\"sys\" or \"sys_list\")\n" "-s, --sys SYSTEM[,..] Systems for current window\n" "-f, --fields LETTER[:UNIT][,..] Fields and units for current window\n" "-S, --sort LETTER Sort field for current window\n" "-t, --cpu_types TYPE[,..] CPU types used for time calculations\n" "-b, --batch_mode Use batch mode (no curses)\n" " --format FORMAT Output format (" FMT_TYPE_NAMES "), implies -b\n" "-d, --delay SECONDS Delay time between screen updates\n" "-m, --smt_factor FACTOR Machine generation dependent SMT speedup factor.\n" "-n, --iterations NUMBER Number of iterations before ending\n"; /* * Options with long-name only */ #define OPT_FORMAT 256 /* --format */ /* * Initialize default settings */ static void l_init_defaults(void) { g.prog_name = PROG_NAME; g.o.delay_s = HYPTOP_OPT_DEFAULT_DELAY; g.o.smt_factor = HYPTOP_OPT_DEFAULT_SMT_SCALE; g.w.cur = &win_sys_list; g.o.cur_win = &win_sys_list; } /* * Print "help" hint */ static void l_std_usage_exit(void) { fprintf(stderr, "Try '%s --help' for more information.\n", g.prog_name); hyptop_exit(1); } /* * Print help text */ static void l_usage(void) { printf("%s", HELP_TEXT); } /* * Print version information */ static void l_print_version(void) { printf("%s: Hypervisor Top version %s\n", g.prog_name, RELEASE_STRING); printf("%s\n", l_copyright_str); } /* * Check if string is a number */ static int l_number_check(const char *str) { const char *ptr = str; while (*ptr) { if (!isdigit(*ptr)) ERR_EXIT("The argument \"%s\" is not an integer\n", str); ptr++; } return 1; } /* * Set delay option */ static void l_delay_set(char *delay_string) { int secs; l_number_check(delay_string); if (sscanf(delay_string, "%i", &secs) != 1) ERR_EXIT("The delay value \"%s\" is invalid\n", delay_string); g.o.delay_s = secs; g.o.delay_us = 0; } /* * Set SMT factor option */ static void l_factor_set(char *value_string) { double factor; if (sscanf(value_string, "%lf", &factor) != 1) ERR_EXIT("The SMT factor \"%s\" is invalid\n", value_string); if (factor <= 0) ERR_EXIT("The SMT factor \"%s\" is <= 0\n", value_string); g.o.smt_factor = factor; } /* * Get number of occurrences of character 'c' in "str" */ static int l_get_char_cnt(char *str, char c) { unsigned int i; int cnt = 0; for (i = 0; str[i] != 0; i++) { if (str[i] == c) cnt++; } return cnt; } /* * Return copy of string with removed trailing and leading blanks */ static char *l_trim_str_new(char *str) { char *rc; int i; for (i = 0; *(str + i) == ' '; i++) {} rc = ht_strdup(str + i); ht_strstrip(rc); if (strlen(rc) == 0) ERR_EXIT("The argument \"%s\" is invalid\n", str); return rc; } /* * Get column specification for string */ static struct table_col_spec *l_get_col_spec(char *str) { struct table_col_spec *col_spec; unsigned int i; char *key_str; col_spec = ht_zalloc(sizeof(*col_spec)); for (i = strlen(str); i > 0; i--) { if (str[i] == ':') { col_spec->unit_str = l_trim_str_new(&str[i + 1]); str[i] = 0; } } key_str = l_trim_str_new(str); if (strlen(key_str) > 1) ERR_EXIT("The field key \"%s\" is invalid\n", key_str); col_spec->hotkey = key_str[0]; ht_free(key_str); return col_spec; } /* * Set the "--fields" option */ static void l_fields_set(char *str) { struct hyptop_col_vec_opt *opt = &g.o.cur_win->opts.fields; unsigned int i, j; opt->cnt = l_get_char_cnt(str, ',') + 1; opt->vec = ht_zalloc(sizeof(void *) * (opt->cnt + 1)); j = 0; for (i = strlen(str); i > 0; i--) { if (str[i] != ',') continue; opt->vec[j] = l_get_col_spec(&str[i + 1]); str[i] = 0; j++; } opt->vec[j] = l_get_col_spec(str); opt->specified = 1; } /* * Set the "--sort_field" option */ static void l_sort_field_set(char *str) { if (strlen(str) > 1) ERR_EXIT("The sort field \"%s\" is invalid\n", str); if (g.o.cur_win->opts.sort_field_specified && g.o.cur_win->opts.sort_field != str[0]) g.o.cur_win->opts.sort_field_specified = 0; g.o.cur_win->opts.sort_field_specified++; g.o.cur_win->opts.sort_field = str[0]; } /* * Setup a string vector out of a comma separated list in "str" */ static void l_str_vec_set(char *str, struct hyptop_str_vec_opt *opt) { unsigned int i, j; opt->cnt = l_get_char_cnt(str, ',') + 1; opt->vec = ht_zalloc(sizeof(void *) * (opt->cnt + 1)); j = 0; for (i = strlen(str); i > 0; i--) { if (str[i] != ',') continue; opt->vec[j] = l_trim_str_new(&str[i + 1]); str[i] = 0; j++; } opt->vec[j] = l_trim_str_new(str); opt->specified = 1; } /* * Set the "--sys" option */ static void l_sys_set(char *str) { l_str_vec_set(str, &g.o.cur_win->opts.sys); } /* * Set the "--cpu_types" option */ static void l_cpu_types_set(char *str) { l_str_vec_set(str, &g.o.cpu_types); } /* * Set the "--window" option */ static void l_window_set(const char *str) { g.o.win_specified = 1; if (strcmp(str, win_sys_list.id) == 0) g.o.cur_win = &win_sys_list; else if (strcmp(str, win_sys.id) == 0) g.o.cur_win = &win_sys; else ERR_EXIT("The window \"%s\" is unknown\n", str); } /* * Set the "--iterations" option */ static void l_iterations_set(const char *str) { l_number_check(str); g.o.iterations_specified = 1; g.o.iterations = atoi(str); } /* * Set the "--batch_mode" option */ static void l_batch_mode_set(void) { g.o.batch_mode_specified = 1; } /* * Set the "--format" option */ static void l_format_set(const char *str) { enum util_fmt_t fmt; if (!util_fmt_name_to_type(str, &fmt)) { ERR_EXIT("Unknown format '%s', supported formats: " FMT_TYPE_NAMES "\n", str); } l_batch_mode_set(); g.o.format_specified = 1; g.o.format = fmt; } /* * Make option consisteny checks at end of command line parsing */ static void l_parse_finish(void) { if (g.o.iterations_specified && g.o.iterations == 0) hyptop_exit(0); if (g.o.cur_win != &win_sys) return; if (!win_sys.opts.sys.specified) ERR_EXIT("Specify a system for window \"sys\"\n"); if (win_sys.opts.sys.cnt != 1) ERR_EXIT("More than one system for window \"sys\" has been " "specified\n"); win_switch(&win_sys); } /* * Main command line parsing function */ void opts_parse(int argc, char *argv[]) { int opt, index; static struct option long_options[] = { { "version", no_argument, NULL, 'v'}, { "help", no_argument, NULL, 'h'}, { "batch_mode", no_argument, NULL, 'b'}, { "delay", required_argument, NULL, 'd'}, { "smt_factor", required_argument, NULL, 'm'}, { "window", required_argument, NULL, 'w'}, { "sys", required_argument, NULL, 's'}, { "iterations", required_argument, NULL, 'n'}, { "fields", required_argument, NULL, 'f'}, { "sort_field", required_argument, NULL, 'S'}, { "cpu_types", required_argument, NULL, 't'}, { "format", required_argument, NULL, OPT_FORMAT }, { NULL, 0, NULL, 0 } }; static const char option_string[] = "vhbd:m:w:s:n:f:t:S:"; l_init_defaults(); while (1) { opt = getopt_long(argc, argv, option_string, long_options, &index); if (opt == -1) break; switch (opt) { case 'v': l_print_version(); hyptop_exit(0); case 'h': l_usage(); hyptop_exit(0); case 'b': l_batch_mode_set(); break; case 'd': l_delay_set(optarg); break; case 'm': l_factor_set(optarg); break; case 'w': l_window_set(optarg); break; case 's': l_sys_set(optarg); break; case 'n': l_iterations_set(optarg); break; case 't': l_cpu_types_set(optarg); break; case 'f': l_fields_set(optarg); break; case 'S': l_sort_field_set(optarg); break; case OPT_FORMAT: l_format_set(optarg); break; default: l_std_usage_exit(); } } if (optind != argc) ERR_EXIT("Invalid positional parameter \"%s\" specified\n", argv[optind]); l_parse_finish(); } /* * Has "sys_name" been specified on command line? */ int opts_sys_specified(struct hyptop_win *win, const char* sys_name) { unsigned int i; if (!win->opts.sys.specified) return 1; for (i = 0; i < win->opts.sys.cnt; i++) { if (strcmp(win->opts.sys.vec[i], sys_name) == 0) return 1; } return 0; } /* * Verify that all specified systems are available for window */ static void l_verify_systems(struct hyptop_win *win) { char *sys_name; unsigned int i; for (i = 0; i < win->opts.sys.cnt; i++) { if (sd_sys_get(sd_sys_root_get(), win->opts.sys.vec[i])) continue; sys_name = ht_strdup(win->opts.sys.vec[i]); util_str_toupper(win->opts.sys.vec[i]); if (sd_sys_get(sd_sys_root_get(), win->opts.sys.vec[i])) { ht_free(sys_name); continue; } ERR_EXIT("System \"%s\" is not available\n", sys_name); } } /* * Verify that all specified systems are available for all windows */ void opt_verify_systems(void) { l_verify_systems(&win_sys_list); l_verify_systems(&win_sys); if (g.o.cur_win == &win_sys) win_sys_set(win_sys.opts.sys.vec[0]); } /* * Increase iterations count and exit if necessary */ void opts_iterations_next(void) { if (g.o.iterations_specified) { g.o.iterations_act++; if (g.o.iterations_act >= g.o.iterations) { if (g.o.format_specified) table_fmt_end(); hyptop_exit(0); } } } s390-tools-2.38.0/hyptop/opts.h000066400000000000000000000010411502674226300161240ustar00rootroot00000000000000/* * hyptop - Show hypervisor performance data on System z * * Command line parsing * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef OPTS_H #define OPTS_H #include "hyptop.h" extern void opts_parse(int argc, char *argv[]); extern void opts_iterations_next(void); extern int opts_sys_specified(struct hyptop_win *win, const char* sys_name); extern void opt_verify_systems(void); #endif /* OPTS_H */ s390-tools-2.38.0/hyptop/sd.h000066400000000000000000000275231502674226300155620ustar00rootroot00000000000000/* * hyptop - Show hypervisor performance data on System z * * System data module: Provide database for system data (e.g. CPU and memory) * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef SD_H #define SD_H #include "lib/util_list.h" #include "helper.h" #include "table.h" #define SD_DG_INIT_INTERVAL_SEC 1 #define SD_SYS_ID_SIZE 9 /* * CPU info */ struct sd_cpu_info { u64 cpu_time_us; u64 thread_time_us; u64 mgm_time_us_set; u64 mgm_time_us; u64 wait_time_us; s64 steal_time_us; u64 online_time_us; }; /* * Memory Info */ struct sd_mem { u64 min_kib; u64 max_kib; u64 use_kib; }; /* * Weight */ struct sd_weight { u16 cur; u16 min; u16 max; }; /* * System Name */ struct sd_sys_name { char os[9]; }; struct sd_sys; /* * SD info */ struct sd_info { u8 active; struct sd_sys *parent; }; struct sd_cpu; /* * SD System (can be e.g. CEC, VM or guest/LPAR) */ struct sd_sys { struct util_list_node list; struct sd_info i; u64 update_time_us; u32 child_cnt; u32 child_cnt_active; struct util_list child_list; u32 cpu_cnt; u32 cpu_cnt_active; struct util_list cpu_list; u32 threads_per_core; char id[SD_SYS_ID_SIZE]; struct sd_sys_name name; struct sd_mem mem; struct sd_weight weight; }; #define sd_sys_id(sys) ((sys)->id) #define sd_sys_name_os(sys) ((sys)->name.os) void sd_sys_update_start(struct sd_sys *sys); void sd_sys_update_end(struct sd_sys *sys, u64 update_time_us); struct sd_sys *sd_sys_root_get(void); struct sd_sys *sd_sys_get(struct sd_sys *parent, const char *id); struct sd_sys *sd_sys_new(struct sd_sys *parent, const char *id); static inline void sd_sys_weight_cur_set(struct sd_sys *sys, u64 value) { sys->weight.cur = value; } static inline void sd_sys_weight_min_set(struct sd_sys *sys, u64 value) { sys->weight.min = value; } static inline void sd_sys_weight_max_set(struct sd_sys *sys, u64 value) { sys->weight.max = value; } static inline void sd_sys_mem_use_kib_set(struct sd_sys *sys, u64 value) { sys->mem.use_kib = value; } static inline void sd_sys_mem_min_kib_set(struct sd_sys *sys, u64 value) { sys->mem.min_kib = value; } static inline void sd_sys_mem_max_kib_set(struct sd_sys *sys, u64 value) { sys->mem.max_kib = value; } static inline void sd_sys_update_time_us_set(struct sd_sys *sys, u64 value) { sys->update_time_us = value; } /* * CPU type */ #define CPU_TYPE_ID_LEN 16 #define CPU_TYPE_DESC_LEN 64 #define SD_CPU_TYPE_STR_IFL "IFL" #define SD_CPU_TYPE_STR_CP "CP" #define SD_CPU_TYPE_STR_UN "UN" struct sd_cpu_type { char id[CPU_TYPE_ID_LEN]; char desc[CPU_TYPE_DESC_LEN]; u32 idx; int cpu_cnt; char hotkey; }; #define sd_cpu_type_id(type) (type->id) #define sd_cpu_type_desc(type) (type->desc) int sd_cpu_type_selected(struct sd_cpu_type *cpu_type); void sd_cpu_type_select_toggle(struct sd_cpu_type *cpu_type); void sd_cpu_type_select(struct sd_cpu_type *cpu_type); void sd_cpu_type_select_all(void); void sd_cpu_type_select_none(void); struct sd_cpu_type *sd_cpu_type_by_id(const char *id); static inline int sd_cpu_type_cpu_cnt(struct sd_cpu_type *type) { return type->cpu_cnt; } static inline void sd_sys_commit(struct sd_sys *sys) { struct sd_sys *parent = sys->i.parent; sys->i.active = 1; if (parent) parent->child_cnt_active++; } extern struct sd_cpu_type sd_cpu_type_ifl; extern struct sd_cpu_type sd_cpu_type_cp; extern struct sd_cpu_type sd_cpu_type_un; /* * SD CPU */ enum sd_cpu_state { SD_CPU_STATE_UNKNOWN = 0, SD_CPU_STATE_OPERATING = 1, SD_CPU_STATE_STOPPED = 2, SD_CPU_STATE_DECONFIG = 3, }; struct sd_cpu { struct util_list_node list; struct sd_info i; char id[9]; struct sd_cpu_type *type; struct sd_cpu_info d1; struct sd_cpu_info d2; struct sd_cpu_info *d_cur; struct sd_cpu_info *d_prev; u16 cnt; int threads_per_core; enum sd_cpu_state state; }; static inline char *sd_cpu_state_str(enum sd_cpu_state state) { static char *state_str[] = {"UK", "OP", "ST", "DC"}; return state_str[(int) state]; } #define sd_cpu_has_diff(cpu) (cpu->d_prev != NULL) #define sd_cpu_diff(cpu, member) (cpu->d_cur->member - cpu->d_prev->member) #define sd_cpu_id(cpu) (cpu->id) #define sd_cpu_cnt(cpu) (cpu->cnt) #define sd_cpu_type_str(cpu) (cpu->type->id) #define sd_cpu_state(cpu) (cpu->state) struct sd_cpu *sd_cpu_get(struct sd_sys *sys, const char *cpu_id); struct sd_cpu *sd_cpu_new(struct sd_sys *parent, const char *id, const char *type, int cnt); static inline void sd_cpu_state_set(struct sd_cpu *cpu, enum sd_cpu_state state) { cpu->state = state; } static inline void sd_cpu_cpu_time_us_set(struct sd_cpu *cpu, u64 value) { cpu->d_cur->cpu_time_us = value; } static inline void sd_cpu_threads_per_core_set(struct sd_cpu *cpu, int value) { cpu->threads_per_core = value; } static inline void sd_cpu_thread_time_us_set(struct sd_cpu *cpu, u64 value) { cpu->d_cur->thread_time_us = value; } static inline void sd_cpu_mgm_time_us_set(struct sd_cpu *cpu, u64 value) { cpu->d_cur->mgm_time_us_set = 1; cpu->d_cur->mgm_time_us = value; } static inline void sd_cpu_wait_time_us_set(struct sd_cpu *cpu, u64 value) { cpu->d_cur->wait_time_us = value; } static inline void sd_cpu_steal_time_us_set(struct sd_cpu *cpu, s64 value) { cpu->d_cur->steal_time_us = value; } static inline void sd_cpu_online_time_us_set(struct sd_cpu *cpu, u64 value) { cpu->d_cur->online_time_us = value; } static inline void sd_cpu_commit(struct sd_cpu *cpu) { struct sd_sys *parent = cpu->i.parent; cpu->i.active = 1; if (parent) parent->cpu_cnt_active++; } /* * Item types */ enum sd_item_type { SD_TYPE_U16, SD_TYPE_U32, SD_TYPE_U64, SD_TYPE_S64, SD_TYPE_STR, }; /* * CPU item */ struct sd_cpu_item { struct table_col table_col; enum sd_item_type type; int offset; char *desc; int (*fn_set)(struct sd_cpu_item *, struct sd_cpu *); u64 (*fn_u64)(struct sd_cpu_item *, struct sd_cpu *); s64 (*fn_s64)(struct sd_cpu_item *, struct sd_cpu *); char *(*fn_str)(struct sd_cpu_item *, struct sd_cpu *); }; #define sd_cpu_item_type(x) ((x)->type) #define sd_cpu_item_table_col(item) (&(item)->table_col) extern int sd_cpu_item_available(struct sd_cpu_item *item); extern int sd_cpu_item_cnt(void); /* * Item access functions */ static inline u64 sd_cpu_item_set(struct sd_cpu_item *item, struct sd_cpu *cpu) { return (item->fn_set == NULL) ? 1 : item->fn_set(item, cpu); } static inline u64 sd_cpu_item_u64(struct sd_cpu_item *item, struct sd_cpu *cpu) { return item->fn_u64(item, cpu); } static inline u64 sd_cpu_item_s64(struct sd_cpu_item *item, struct sd_cpu *cpu) { return item->fn_s64(item, cpu); } static inline char *sd_cpu_item_str(struct sd_cpu_item *item, struct sd_cpu *cpu) { if (item->fn_str) return item->fn_str(item, cpu); else return ((char *) cpu) + item->offset; } /* * Predefined CPU items */ extern struct sd_cpu_item sd_cpu_item_type; extern struct sd_cpu_item sd_cpu_item_state; extern struct sd_cpu_item sd_cpu_item_cpu_diff; extern struct sd_cpu_item sd_cpu_item_core_diff; extern struct sd_cpu_item sd_cpu_item_thread_diff; extern struct sd_cpu_item sd_cpu_item_smt_diff; extern struct sd_cpu_item sd_cpu_item_mgm_diff; extern struct sd_cpu_item sd_cpu_item_wait_diff; extern struct sd_cpu_item sd_cpu_item_steal_diff; extern struct sd_cpu_item sd_cpu_item_cpu; extern struct sd_cpu_item sd_cpu_item_core; extern struct sd_cpu_item sd_cpu_item_thread; extern struct sd_cpu_item sd_cpu_item_mgm; extern struct sd_cpu_item sd_cpu_item_wait; extern struct sd_cpu_item sd_cpu_item_steal; extern struct sd_cpu_item sd_cpu_item_online; /* * System item */ struct sd_sys_item { struct table_col table_col; enum sd_item_type type; int offset; char *desc; int info; int (*fn_set)(struct sd_sys_item *, struct sd_sys *); u64 (*fn_u64)(struct sd_sys_item *, struct sd_sys *); s64 (*fn_s64)(struct sd_sys_item *, struct sd_sys *); }; #define sd_sys_item_table_col(item) (&item->table_col) #define sd_sys_item_type(item) (item->type) extern int sd_sys_item_available(struct sd_sys_item *item); extern int sd_sys_item_cnt(void); /* * Item access functions */ static inline int sd_sys_item_set(struct sd_sys *sys, struct sd_sys_item *item) { return (item->fn_set == NULL) ? 1 : item->fn_set(item, sys); } static inline u64 sd_sys_item_u64(struct sd_sys *sys, struct sd_sys_item *item) { return item->fn_u64(item, sys); } static inline s64 sd_sys_item_s64(struct sd_sys *sys, struct sd_sys_item *item) { return item->fn_s64(item, sys); } static inline char *sd_sys_item_str(struct sd_sys *sys, struct sd_sys_item *item) { return ((char *) sys) + item->offset; } /* * Predefined System items */ extern struct sd_sys_item sd_sys_item_cpu_cnt; extern struct sd_sys_item sd_sys_item_core_cnt; extern struct sd_sys_item sd_sys_item_thread_cnt; extern struct sd_sys_item sd_sys_item_smt_diff; extern struct sd_sys_item sd_sys_item_cpu_oper_cnt; extern struct sd_sys_item sd_sys_item_cpu_deconf_cnt; extern struct sd_sys_item sd_sys_item_cpu_stop_cnt; extern struct sd_sys_item sd_sys_item_cpu_diff; extern struct sd_sys_item sd_sys_item_core_diff; extern struct sd_sys_item sd_sys_item_thread_diff; extern struct sd_sys_item sd_sys_item_mgm_diff; extern struct sd_sys_item sd_sys_item_wait_diff; extern struct sd_sys_item sd_sys_item_steal_diff; extern struct sd_sys_item sd_sys_item_cpu; extern struct sd_sys_item sd_sys_item_core; extern struct sd_sys_item sd_sys_item_thread; extern struct sd_sys_item sd_sys_item_mgm; extern struct sd_sys_item sd_sys_item_wait; extern struct sd_sys_item sd_sys_item_steal; extern struct sd_sys_item sd_sys_item_online; extern struct sd_sys_item sd_sys_item_mem_max; extern struct sd_sys_item sd_sys_item_mem_min; extern struct sd_sys_item sd_sys_item_mem_use; extern struct sd_sys_item sd_sys_item_weight_cur; extern struct sd_sys_item sd_sys_item_weight_min; extern struct sd_sys_item sd_sys_item_weight_max; extern struct sd_sys_item sd_sys_item_os_name; extern struct sd_sys_item sd_sys_item_samples_total; extern struct sd_sys_item sd_sys_item_samples_cpu_using; /* * Data gatherer backend */ struct sd_dg { void (*update_sys)(void); struct sd_cpu_type **cpu_type_vec; struct sd_sys_item **sys_item_vec; struct sd_sys_item **sys_item_enable_vec; struct sd_cpu_item **cpu_item_vec; struct sd_cpu_item **cpu_item_enable_vec; }; void sd_dg_register(struct sd_dg *, int); int sd_dg_has_core_data(void); /* * Iterators */ #define sd_sys_iterate(parent, sys) \ util_list_iterate(&parent->child_list, sys) #define sd_cpu_iterate(parent, cpu) \ util_list_iterate(&parent->cpu_list, cpu) #define sd_sys_item_iterate(ptr, i) \ for (i = 0; (ptr = sd.dg->sys_item_vec[i]); i++) #define sd_sys_item_enable_iterate(ptr, i) \ for (i = 0; (ptr = sd.dg->sys_item_enable_vec[i]); i++) #define sd_cpu_item_iterate(ptr, i) \ for (i = 0; (ptr = sd.dg->cpu_item_vec[i]); i++) #define sd_cpu_item_enable_iterate(ptr, i) \ for (i = 0; (ptr = sd.dg->cpu_item_enable_vec[i]); i++) #define sd_cpu_type_iterate(ptr, i) \ for (i = 0; (ptr = sd.dg->cpu_type_vec[i]); i++) /* * Offset macros */ #define SD_SYSTEM_OFFSET(x) \ ((unsigned long)(void *)&(((struct sd_sys *) NULL)->x)) #define SD_CPU_INFO_OFFSET(x) \ ((unsigned long)(void *)&(((struct sd_cpu_info *) NULL)->x)) static inline int l_cpu_info_set(struct sd_cpu_info *info, unsigned long offset) { return (int)*(u64 *)(((char *) info) + offset - 8); } static inline u64 l_cpu_info_u64(struct sd_cpu_info *info, unsigned long offset) { return *(u64 *)(((char *) info) + offset); } static inline s64 l_cpu_info_s64(struct sd_cpu_info *info, unsigned long offset) { return *(s64 *)(((char *) info) + offset); } /* * Misc */ void sd_update(void); extern void sd_init(void); static inline u64 l_sub_64(u64 x, u64 y) { return x < y ? 0 : x - y; } struct sd_globals { struct sd_dg *dg; }; extern struct sd_globals sd; #endif /* SD_H */ s390-tools-2.38.0/hyptop/sd_core.c000066400000000000000000000176241502674226300165660ustar00rootroot00000000000000/* * hyptop - Show hypervisor performance data on System z * * System data module: Provide backend independent database for system data * (e.g. for CPU and memory data) * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include "helper.h" #include "hyptop.h" #include "opts.h" #include "sd.h" /* * Internal globals for system data */ static u32 l_cpu_type_selected_mask; static int l_cpu_type_cnt; static int l_sys_item_cnt; static int l_cpu_item_cnt; static int l_has_core_data; static struct sd_sys *l_root_sys; /* * External globals for system data */ struct sd_globals sd; /* * Get root system */ struct sd_sys *sd_sys_root_get(void) { return l_root_sys; } /* * Get CPU type by it's ID */ struct sd_cpu_type *sd_cpu_type_by_id(const char *id) { struct sd_cpu_type *type; unsigned int i; sd_cpu_type_iterate(type, i) { if (strcasecmp(id, type->id) == 0) return type; } return NULL; } /* * Is CPU type selected? */ int sd_cpu_type_selected(struct sd_cpu_type *cpu_type) { return l_cpu_type_selected_mask & cpu_type->idx; } /* * Toggle selection of CPU type */ void sd_cpu_type_select_toggle(struct sd_cpu_type *cpu_type) { if (l_cpu_type_selected_mask & cpu_type->idx) l_cpu_type_selected_mask &= ~cpu_type->idx; else l_cpu_type_selected_mask |= cpu_type->idx; } /* * Select exactly specified CPU type */ void sd_cpu_type_select(struct sd_cpu_type *cpu_type) { l_cpu_type_selected_mask = cpu_type->idx; } /* * Select all available CPU types */ void sd_cpu_type_select_all(void) { l_cpu_type_selected_mask = (u32)-1; } /* * Deselect all CPU types */ void sd_cpu_type_select_none(void) { l_cpu_type_selected_mask = 0; } /* * Setup CPU types specified on command line */ static void l_opts_cpu_types_init(void) { struct sd_cpu_type *type; unsigned int i; if (!g.o.cpu_types.specified) return; sd_cpu_type_select_none(); for (i = 0; i < g.o.cpu_types.cnt; i++) { type = sd_cpu_type_by_id(g.o.cpu_types.vec[i]); if (!type) ERR_EXIT("Invalid CPU type \"%s\"\n", g.o.cpu_types.vec[i]); sd_cpu_type_select_toggle(type); } } /* * Init CPU count for all CPU types */ static void l_cpu_types_init(void) { struct sd_sys *sys = sd_sys_root_get(); struct sd_cpu_type *cpu_type; unsigned int i; sd_cpu_type_iterate(cpu_type, i) { sd_cpu_type_select(cpu_type); cpu_type->cpu_cnt = sd_sys_item_u64(sys, &sd_sys_item_cpu_cnt); } sd_cpu_type_select_all(); l_opts_cpu_types_init(); } /* * Update system data using the data gatherer */ void sd_update(void) { sd.dg->update_sys(); } /* * Register a data gatherer */ void sd_dg_register(struct sd_dg *dg, int has_core_data) { struct timespec ts = {SD_DG_INIT_INTERVAL_SEC, 0}; struct sd_sys_item *sys_item; struct sd_cpu_item *cpu_item; unsigned int i; l_has_core_data = has_core_data; sd.dg = dg; for (i = 0; dg->cpu_type_vec[i]; i++) dg->cpu_type_vec[i]->idx = (1UL << i); l_cpu_type_cnt = i; sd_sys_item_iterate(sys_item, i) l_sys_item_cnt++; sd_cpu_item_iterate(cpu_item, i) l_cpu_item_cnt++; sd_update(); nanosleep(&ts, NULL); sd_update(); l_cpu_types_init(); } /* * Does backend has core data? */ int sd_dg_has_core_data(void) { return l_has_core_data; } /* * Get CPU from sys by ID */ struct sd_cpu *sd_cpu_get(struct sd_sys *sys, const char* id) { struct sd_cpu *cpu; util_list_iterate(&sys->cpu_list, cpu) { if (strcmp(cpu->id, id) == 0) return cpu; } return NULL; } /* * Get CPU type by ID */ static struct sd_cpu_type *l_cpu_type_by_id(const char *id) { struct sd_cpu_type **cpu_type_vec = sd.dg->cpu_type_vec; int i; for (i = 0; i < l_cpu_type_cnt; i++) { if (strcmp(cpu_type_vec[i]->id, id) == 0) return cpu_type_vec[i]; } return NULL; } /* * Allocate and initialize new CPU */ struct sd_cpu *sd_cpu_new(struct sd_sys *parent, const char *id, const char *type, int cnt) { struct sd_cpu *cpu; cpu = ht_zalloc(sizeof(*cpu)); cpu->i.parent = parent; util_strlcpy(cpu->id, id, sizeof(cpu->id)); cpu->type = l_cpu_type_by_id(type); cpu->d_cur = &cpu->d1; cpu->cnt = cnt; util_list_add_tail(&parent->cpu_list, cpu); return cpu; } /* * Get system by ID */ struct sd_sys *sd_sys_get(struct sd_sys *parent, const char* id) { struct sd_sys *sys; util_list_iterate(&parent->child_list, sys) { if (strcmp(sys->id, id) == 0) return sys; } return NULL; } /* * Allocate and initialize new system */ struct sd_sys *sd_sys_new(struct sd_sys *parent, const char *id) { struct sd_sys *sys_new; sys_new = ht_zalloc(sizeof(*sys_new)); util_strlcpy(sys_new->id, id, sizeof(sys_new->id)); util_list_init(&sys_new->child_list, struct sd_sys, list); util_list_init(&sys_new->cpu_list, struct sd_cpu, list); if (parent) { sys_new->i.parent = parent; parent->child_cnt++; util_list_add_tail(&parent->child_list, sys_new); } sys_new->threads_per_core = 1; return sys_new; } /* * Free system */ static void sd_sys_free(struct sd_sys *sys) { ht_free(sys); } /* * Free CPU */ static void sd_cpu_free(struct sd_cpu *cpu) { ht_free(cpu); } /* * Start update cycle for CPU */ static void l_cpu_update_start(struct sd_cpu *cpu) { struct sd_cpu_info *tmp; cpu->i.active = 0; if (!cpu->d_prev) { cpu->d_prev = &cpu->d1; cpu->d_cur = &cpu->d2; } else { tmp = cpu->d_prev; cpu->d_prev = cpu->d_cur; cpu->d_cur = tmp; } } /* * Start update cycle for system */ void sd_sys_update_start(struct sd_sys *sys) { struct sd_sys *child; struct sd_cpu *cpu; sys->i.active = 0; sys->child_cnt_active = 0; sys->cpu_cnt_active = 0; util_list_iterate(&sys->cpu_list, cpu) l_cpu_update_start(cpu); util_list_iterate(&sys->child_list, child) sd_sys_update_start(child); } /* * End update cycle for CPUs of a system */ static void l_cpu_update_end(struct sd_sys *sys) { struct sd_cpu *cpu, *tmp; /* Has system not lost any CPU? */ if (sys->cpu_cnt_active == sys->cpu_cnt) return; util_list_iterate_safe(&sys->cpu_list, cpu, tmp) { if (!cpu->i.active) { /* CPU has not been updated, remove it */ util_list_remove(&sys->cpu_list, cpu); sd_cpu_free(cpu); continue; } } sys->cpu_cnt = sys->cpu_cnt_active; } /* * End update cycle for system */ static void l_sys_update_end(struct sd_sys *sys) { struct sd_sys *child, *tmp; l_cpu_update_end(sys); util_list_iterate_safe(&sys->child_list, child, tmp) { if (!child->i.active) { /* child has not been updated, remove it */ util_list_remove(&sys->child_list, child); sd_sys_free(child); continue; } /* Recursively update child */ l_sys_update_end(child); } sys->child_cnt = sys->child_cnt_active; } /* * End update cycle for system */ void sd_sys_update_end(struct sd_sys *sys, u64 update_time_us) { sys->update_time_us = update_time_us; l_sys_update_end(sys); } /* * Is system item available? */ int sd_sys_item_available(struct sd_sys_item *item) { struct sd_sys_item *ptr; unsigned int i; sd_sys_item_iterate(ptr, i) { if (item == ptr) return 1; } return 0; } /* * Number of system items */ int sd_sys_item_cnt(void) { return l_sys_item_cnt; } /* * Is CPU item avaiable? */ int sd_cpu_item_available(struct sd_cpu_item *item) { struct sd_cpu_item *ptr; unsigned int i; sd_cpu_item_iterate(ptr, i) { if (item == ptr) return 1; } return 0; } /* * Number of CPU items */ int sd_cpu_item_cnt(void) { return l_cpu_item_cnt; } /* * Init system data module */ void sd_init(void) { l_root_sys = sd_sys_new(NULL, "root"); } /* * CPU Types */ struct sd_cpu_type sd_cpu_type_ifl = { .id = SD_CPU_TYPE_STR_IFL, .desc = "Integrated Facility for Linux", .hotkey = 'i', }; struct sd_cpu_type sd_cpu_type_cp = { .id = SD_CPU_TYPE_STR_CP, .desc = "Central processor", .hotkey = 'p', }; struct sd_cpu_type sd_cpu_type_un = { .id = SD_CPU_TYPE_STR_UN, .desc = "Unspecified processor type", .hotkey = 'u', }; s390-tools-2.38.0/hyptop/sd_cpu_items.c000066400000000000000000000145341502674226300176230ustar00rootroot00000000000000/* * hyptop - Show hypervisor performance data on System z * * Provide CPU Items * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include "sd.h" /* * Return CPU type of "cpu" */ static char *l_cpu_type(struct sd_cpu_item *item, struct sd_cpu *cpu) { (void) item; return sd_cpu_type_str(cpu); } /* * Return CPU state of "cpu" */ static char *l_cpu_state(struct sd_cpu_item *item, struct sd_cpu *cpu) { (void) item; return sd_cpu_state_str(sd_cpu_state(cpu)); } /* * value = (value_current - value_prev) / online_time_diff */ static double l_cpu_diff(struct sd_cpu_item *item, struct sd_cpu *cpu, int sign) { u64 online_time_diff_us; double factor, diff_us; if (sd_cpu_state(cpu) == SD_CPU_STATE_STOPPED) return 0; if (!cpu->d_prev || !cpu->d_cur) return 0; if (!sd_cpu_type_selected(cpu->type)) return 0; online_time_diff_us = l_sub_64(cpu->d_cur->online_time_us, cpu->d_prev->online_time_us); if (online_time_diff_us == 0) return 0; factor = ((double) online_time_diff_us) / 1000000 * cpu->cnt; if (sign) diff_us = l_cpu_info_s64(cpu->d_cur, item->offset) - l_cpu_info_s64(cpu->d_prev, item->offset); else diff_us = l_sub_64(l_cpu_info_u64(cpu->d_cur, item->offset), l_cpu_info_u64(cpu->d_prev, item->offset)); diff_us /= factor; return diff_us; } /* * unsigned value = (value_current - value_prev) / online_time_diff */ static u64 l_cpu_diff_u64(struct sd_cpu_item *item, struct sd_cpu *cpu) { return l_cpu_diff(item, cpu, 0); } /* * signed value = (value_current - value_prev) / online_time_diff */ static s64 l_cpu_diff_s64(struct sd_cpu_item *item, struct sd_cpu *cpu) { return l_cpu_diff(item, cpu, 1); } /* * Return true, if CPU item has been set */ static inline int l_cpu_item_set(struct sd_cpu_item *item, struct sd_cpu *cpu) { return l_cpu_info_set(cpu->d_cur, item->offset); } /* * Return cpu item value */ static u64 l_cpu_item_64(struct sd_cpu_item *item, struct sd_cpu *cpu) { if (!cpu->d_cur) return 0; if (!sd_cpu_type_selected(cpu->type)) return 0; if (item->table_col.agg == TABLE_COL_AGG_MAX) return l_cpu_info_u64(cpu->d_cur, item->offset); else return l_cpu_info_u64(cpu->d_cur, item->offset) / cpu->cnt; } static u64 l_cpu_smt_util(struct sd_cpu_item *item, struct sd_cpu *cpu) { u64 core_us, thr_us, mgm_us; (void)item; core_us = sd_cpu_item_u64(&sd_cpu_item_core_diff, cpu); thr_us = sd_cpu_item_u64(&sd_cpu_item_thread_diff, cpu); mgm_us = sd_cpu_item_u64(&sd_cpu_item_mgm_diff, cpu); return ht_calculate_smt_util(core_us, thr_us, mgm_us, cpu->threads_per_core); } /* * CPU item definitions */ struct sd_cpu_item sd_cpu_item_type = { .table_col = TABLE_COL_STR('p', "type"), .type = SD_TYPE_STR, .desc = "CPU type", .fn_str = l_cpu_type, }; struct sd_cpu_item sd_cpu_item_state = { .table_col = TABLE_COL_STR('a', "stat"), .type = SD_TYPE_STR, .desc = "CPU state", .fn_str = l_cpu_state, }; struct sd_cpu_item sd_cpu_item_core_diff = { .table_col = TABLE_COL_TIME_DIFF_SUM(table_col_unit_perc, 'c', "core"), .type = SD_TYPE_U64, .offset = SD_CPU_INFO_OFFSET(cpu_time_us), .desc = "Core dispatch time per second", .fn_u64 = l_cpu_diff_u64, }; struct sd_cpu_item sd_cpu_item_cpu_diff = { .table_col = TABLE_COL_TIME_DIFF_SUM(table_col_unit_perc, 'c', "cpu"), .type = SD_TYPE_U64, .offset = SD_CPU_INFO_OFFSET(cpu_time_us), .desc = "CPU time per second", .fn_u64 = l_cpu_diff_u64, }; struct sd_cpu_item sd_cpu_item_thread_diff = { .table_col = TABLE_COL_TIME_DIFF_SUM(table_col_unit_perc, 'e', "the"), .type = SD_TYPE_U64, .offset = SD_CPU_INFO_OFFSET(thread_time_us), .desc = "Thread time per second", .fn_u64 = l_cpu_diff_u64, }; struct sd_cpu_item sd_cpu_item_smt_diff = { .table_col = TABLE_COL_TIME_DIFF_SUM(table_col_unit_perc, 'S', "smt"), .type = SD_TYPE_U64, .desc = "Real CPU SMT utilization", .fn_u64 = l_cpu_smt_util, }; struct sd_cpu_item sd_cpu_item_mgm_diff = { .table_col = TABLE_COL_TIME_DIFF_SUM(table_col_unit_perc, 'm', "mgm"), .type = SD_TYPE_U64, .offset = SD_CPU_INFO_OFFSET(mgm_time_us), .desc = "Management time per second", .fn_set = l_cpu_item_set, .fn_u64 = l_cpu_diff_u64, }; struct sd_cpu_item sd_cpu_item_wait_diff = { .table_col = TABLE_COL_TIME_DIFF_SUM(table_col_unit_perc, 'w', "wait"), .type = SD_TYPE_U64, .offset = SD_CPU_INFO_OFFSET(wait_time_us), .desc = "Wait time per second", .fn_u64 = l_cpu_diff_u64, }; struct sd_cpu_item sd_cpu_item_steal_diff = { .table_col = TABLE_COL_STIME_DIFF_SUM(table_col_unit_perc, 's', "steal"), .type = SD_TYPE_S64, .offset = SD_CPU_INFO_OFFSET(steal_time_us), .desc = "Steal time per second", .fn_s64 = l_cpu_diff_s64, }; struct sd_cpu_item sd_cpu_item_core = { .table_col = TABLE_COL_TIME_SUM(table_col_unit_hm, 'C', "core+"), .type = SD_TYPE_U64, .offset = SD_CPU_INFO_OFFSET(cpu_time_us), .desc = "Total core dispatch time", .fn_u64 = l_cpu_item_64, }; struct sd_cpu_item sd_cpu_item_cpu = { .table_col = TABLE_COL_TIME_SUM(table_col_unit_hm, 'C', "cpu+"), .type = SD_TYPE_U64, .offset = SD_CPU_INFO_OFFSET(cpu_time_us), .desc = "Total CPU time", .fn_u64 = l_cpu_item_64, }; struct sd_cpu_item sd_cpu_item_thread = { .table_col = TABLE_COL_TIME_SUM(table_col_unit_hm, 'E', "the+"), .type = SD_TYPE_U64, .offset = SD_CPU_INFO_OFFSET(thread_time_us), .desc = "Total thread time", .fn_u64 = l_cpu_item_64, }; struct sd_cpu_item sd_cpu_item_mgm = { .table_col = TABLE_COL_TIME_SUM(table_col_unit_hm, 'M', "mgm+"), .type = SD_TYPE_U64, .offset = SD_CPU_INFO_OFFSET(mgm_time_us), .desc = "Total management time", .fn_set = l_cpu_item_set, .fn_u64 = l_cpu_item_64, }; struct sd_cpu_item sd_cpu_item_wait = { .table_col = TABLE_COL_TIME_SUM(table_col_unit_hm, 'W', "wait+"), .type = SD_TYPE_U64, .offset = SD_CPU_INFO_OFFSET(wait_time_us), .desc = "Total wait time", .fn_u64 = l_cpu_item_64, }; struct sd_cpu_item sd_cpu_item_steal = { .table_col = TABLE_COL_STIME_SUM(table_col_unit_hm, 'T', "steal+"), .type = SD_TYPE_U64, .offset = SD_CPU_INFO_OFFSET(steal_time_us), .desc = "Total steal time", .fn_u64 = l_cpu_item_64, }; struct sd_cpu_item sd_cpu_item_online = { .table_col = TABLE_COL_TIME_MAX(table_col_unit_dhm, 'o', "online"), .type = SD_TYPE_U64, .offset = SD_CPU_INFO_OFFSET(online_time_us), .desc = "Online time", .fn_u64 = l_cpu_item_64, }; s390-tools-2.38.0/hyptop/sd_sys_items.c000066400000000000000000000246661502674226300176610ustar00rootroot00000000000000/* * hyptop - Show hypervisor performance data on System z * * Provide System Items * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include "sd.h" /* * Count CPUs of system according to active CPU types and requested CPU state */ static u64 l_sys_cpu_cnt_gen(struct sd_sys *sys, enum sd_cpu_state state, int all) { struct sd_cpu *cpu; u32 cnt = 0; sd_cpu_iterate(sys, cpu) { if (!sd_cpu_type_selected(cpu->type)) continue; if (all || sd_cpu_state(cpu) == state) cnt += cpu->cnt; } return cnt; } /* * Count all CPUs of system */ static u64 l_sys_cpu_cnt(struct sd_sys_item *item, struct sd_sys *sys) { (void) item; return l_sys_cpu_cnt_gen(sys, SD_CPU_STATE_UNKNOWN, 1); } /* * Count all threads of system */ static u64 l_sys_thread_cnt(struct sd_sys_item *item, struct sd_sys *sys) { (void) item; return l_sys_cpu_cnt_gen(sys, SD_CPU_STATE_UNKNOWN, 1) * sys->threads_per_core; } /* * Count CPUs of system with state stopped */ static u64 l_sys_cpu_st_cnt(struct sd_sys_item *item, struct sd_sys *sys) { (void) item; return l_sys_cpu_cnt_gen(sys, SD_CPU_STATE_STOPPED, 0); } /* * Count CPUs of system with state operating */ static u64 l_sys_cpu_op_cnt(struct sd_sys_item *item, struct sd_sys *sys) { (void) item; return l_sys_cpu_cnt_gen(sys, SD_CPU_STATE_OPERATING, 0); } /* * Count CPUs of system with state deconfigured */ static u64 l_sys_cpu_dc_cnt(struct sd_sys_item *item, struct sd_sys *sys) { (void) item; return l_sys_cpu_cnt_gen(sys, SD_CPU_STATE_DECONFIG, 0); } /* * Check if CPU info is set */ static int l_sys_cpu_info_set(struct sd_sys_item *item, struct sd_sys *sys) { struct sd_cpu *cpu; sd_cpu_iterate(sys, cpu) { if (!sd_cpu_type_selected(cpu->type)) continue; if (!l_cpu_info_set(cpu->d_cur, item->offset)) return 0; } return 1; } /* * Get u64 system item value from "sys" */ static u64 l_sys_item_u64(struct sd_sys_item *item, struct sd_sys *sys) { switch (item->type) { case SD_TYPE_U16: return *(u16 *)(((char *) sys) + item->offset); case SD_TYPE_U32: return *(u32 *)(((char *) sys) + item->offset); case SD_TYPE_U64: return *(u64 *)(((char *) sys) + item->offset); case SD_TYPE_S64: case SD_TYPE_STR: break; } assert(0); return 0; } /* * Calculate system item out of sum of CPU info */ static u64 l_sys_cpu_info_sum_u64(struct sd_sys_item *item, struct sd_sys *sys) { struct sd_cpu *cpu; u64 rc = 0; sd_cpu_iterate(sys, cpu) { if (!sd_cpu_type_selected(cpu->type)) continue; rc += l_cpu_info_u64(cpu->d_cur, item->offset); } return rc; } /* * Calculate system item out of MAX of CPU info */ static u64 l_sys_cpu_info_max_u64(struct sd_sys_item *item, struct sd_sys *sys) { struct sd_cpu *cpu; u64 rc = 0; sd_cpu_iterate(sys, cpu) { if (!sd_cpu_type_selected(cpu->type)) continue; rc = MAX(rc, l_cpu_info_u64(cpu->d_cur, item->offset)); } return rc; } /* * value = (value_current - value_prev) / online_time_diff */ static double l_cpu_info_diff_u64(struct sd_sys_item *item, struct sd_cpu *cpu, int sign) { u64 online_time_diff_us; double factor, diff_us; if (!sd_cpu_type_selected(cpu->type)) return 0; if (sd_cpu_state(cpu) == SD_CPU_STATE_STOPPED) return 0; online_time_diff_us = l_sub_64(cpu->d_cur->online_time_us, cpu->d_prev->online_time_us); if (online_time_diff_us == 0) return 0; if (sign) { diff_us = l_cpu_info_s64(cpu->d_cur, item->offset) - l_cpu_info_s64(cpu->d_prev, item->offset); } else { diff_us = l_sub_64(l_cpu_info_u64(cpu->d_cur, item->offset), l_cpu_info_u64(cpu->d_prev, item->offset)); } factor = ((double) online_time_diff_us) / 1000000; diff_us /= factor; return diff_us; } /* * SUM over all CPUs: value = (value_current - value_prev) / online_time_diff */ static u64 l_sys_cpu_info_diff_u64(struct sd_sys_item *item, struct sd_sys *sys) { struct sd_cpu *cpu; u64 rc = 0; sd_cpu_iterate(sys, cpu) { if (!cpu->d_prev || !cpu->d_cur) return 0; rc += l_cpu_info_diff_u64(item, cpu, 0); } return rc; } /* * SUM over all CPUs: value = (value_current - value_prev) / online_time_diff */ static s64 l_sys_cpu_info_diff_s64(struct sd_sys_item *item, struct sd_sys *sys) { struct sd_cpu *cpu; s64 rc = 0; sd_cpu_iterate(sys, cpu) { if (!cpu->d_prev || !cpu->d_cur) return 0; rc += l_cpu_info_diff_u64(item, cpu, 1); } return rc; } static u64 l_sys_smt_util(struct sd_sys_item *item, struct sd_sys *sys) { u64 core_us, thr_us, mgm_us; (void)item; core_us = sd_sys_item_u64(sys, &sd_sys_item_core_diff); thr_us = sd_sys_item_u64(sys, &sd_sys_item_thread_diff); mgm_us = sd_sys_item_u64(sys, &sd_sys_item_mgm_diff); return ht_calculate_smt_util(core_us, thr_us, mgm_us, sys->threads_per_core); } /* * System item definitions */ struct sd_sys_item sd_sys_item_core_cnt = { .table_col = TABLE_COL_CNT_SUM('#', "#core"), .type = SD_TYPE_U32, .desc = "Number of cores", .fn_u64 = l_sys_cpu_cnt, }; struct sd_sys_item sd_sys_item_cpu_cnt = { .table_col = TABLE_COL_CNT_SUM('#', "#cpu"), .type = SD_TYPE_U32, .desc = "Number of CPUs", .fn_u64 = l_sys_cpu_cnt, }; struct sd_sys_item sd_sys_item_thread_cnt = { .table_col = TABLE_COL_CNT_SUM('T', "#the"), .type = SD_TYPE_U32, .desc = "Number of threads", .fn_u64 = l_sys_thread_cnt, }; struct sd_sys_item sd_sys_item_cpu_oper_cnt = { .table_col = TABLE_COL_CNT_SUM('O', "#cpuop"), .type = SD_TYPE_U32, .desc = "Number of operating CPUs", .fn_u64 = l_sys_cpu_op_cnt, }; struct sd_sys_item sd_sys_item_cpu_stop_cnt = { .table_col = TABLE_COL_CNT_SUM('S', "#cpust"), .type = SD_TYPE_U32, .desc = "Number of stopped CPUs", .fn_u64 = l_sys_cpu_st_cnt, }; struct sd_sys_item sd_sys_item_cpu_deconf_cnt = { .table_col = TABLE_COL_CNT_SUM('D', "#cpudc"), .type = SD_TYPE_U32, .desc = "Number of deconfigured CPUs", .fn_u64 = l_sys_cpu_dc_cnt, }; struct sd_sys_item sd_sys_item_core_diff = { .table_col = TABLE_COL_TIME_DIFF_SUM(table_col_unit_perc, 'c', "core"), .offset = SD_CPU_INFO_OFFSET(cpu_time_us), .type = SD_TYPE_U64, .desc = "Core dispatch time per second", .fn_u64 = l_sys_cpu_info_diff_u64, }; struct sd_sys_item sd_sys_item_cpu_diff = { .table_col = TABLE_COL_TIME_DIFF_SUM(table_col_unit_perc, 'c', "cpu"), .offset = SD_CPU_INFO_OFFSET(cpu_time_us), .type = SD_TYPE_U64, .desc = "CPU time per second", .fn_u64 = l_sys_cpu_info_diff_u64, }; struct sd_sys_item sd_sys_item_thread_diff = { .table_col = TABLE_COL_TIME_DIFF_SUM(table_col_unit_perc, 'e', "the"), .offset = SD_CPU_INFO_OFFSET(thread_time_us), .type = SD_TYPE_U64, .desc = "Thread time per second", .fn_u64 = l_sys_cpu_info_diff_u64, }; struct sd_sys_item sd_sys_item_smt_diff = { .table_col = TABLE_COL_TIME_DIFF_SUM(table_col_unit_perc, 'S', "smt"), .type = SD_TYPE_U64, .desc = "Real CPU SMT utilization", .fn_u64 = l_sys_smt_util, }; struct sd_sys_item sd_sys_item_mgm_diff = { .table_col = TABLE_COL_TIME_DIFF_SUM(table_col_unit_perc, 'm', "mgm"), .offset = SD_CPU_INFO_OFFSET(mgm_time_us), .type = SD_TYPE_U64, .desc = "Management time per second", .fn_set = l_sys_cpu_info_set, .fn_u64 = l_sys_cpu_info_diff_u64, }; struct sd_sys_item sd_sys_item_wait_diff = { .table_col = TABLE_COL_TIME_DIFF_SUM(table_col_unit_perc, 'w', "wait"), .offset = SD_CPU_INFO_OFFSET(wait_time_us), .type = SD_TYPE_U64, .desc = "Wait time per second", .fn_u64 = l_sys_cpu_info_diff_u64, }; struct sd_sys_item sd_sys_item_steal_diff = { .table_col = TABLE_COL_STIME_DIFF_SUM(table_col_unit_perc, 's', "steal"), .offset = SD_CPU_INFO_OFFSET(steal_time_us), .type = SD_TYPE_S64, .desc = "Steal time per second", .fn_s64 = l_sys_cpu_info_diff_s64, }; struct sd_sys_item sd_sys_item_core = { .table_col = TABLE_COL_TIME_SUM(table_col_unit_hm, 'C', "core+"), .offset = SD_CPU_INFO_OFFSET(cpu_time_us), .type = SD_TYPE_U64, .desc = "Total core dispatch time", .fn_u64 = l_sys_cpu_info_sum_u64, }; struct sd_sys_item sd_sys_item_cpu = { .table_col = TABLE_COL_TIME_SUM(table_col_unit_hm, 'C', "cpu+"), .offset = SD_CPU_INFO_OFFSET(cpu_time_us), .type = SD_TYPE_U64, .desc = "Total CPU time", .fn_u64 = l_sys_cpu_info_sum_u64, }; struct sd_sys_item sd_sys_item_thread = { .table_col = TABLE_COL_TIME_SUM(table_col_unit_hm, 'E', "the+"), .offset = SD_CPU_INFO_OFFSET(thread_time_us), .type = SD_TYPE_U64, .desc = "Total thread time", .fn_u64 = l_sys_cpu_info_sum_u64, }; struct sd_sys_item sd_sys_item_wait = { .table_col = TABLE_COL_TIME_SUM(table_col_unit_hm, 'W', "wait+"), .offset = SD_CPU_INFO_OFFSET(wait_time_us), .type = SD_TYPE_U64, .desc = "Total wait time", .fn_u64 = l_sys_cpu_info_sum_u64, }; struct sd_sys_item sd_sys_item_mgm = { .table_col = TABLE_COL_TIME_SUM(table_col_unit_hm, 'M', "mgm+"), .offset = SD_CPU_INFO_OFFSET(mgm_time_us), .type = SD_TYPE_U64, .desc = "Total management time", .fn_set = l_sys_cpu_info_set, .fn_u64 = l_sys_cpu_info_sum_u64, }; struct sd_sys_item sd_sys_item_steal = { .table_col = TABLE_COL_STIME_SUM(table_col_unit_hm, 'T', "steal+"), .offset = SD_CPU_INFO_OFFSET(steal_time_us), .type = SD_TYPE_U64, .desc = "Total steal time", .fn_u64 = l_sys_cpu_info_sum_u64, }; struct sd_sys_item sd_sys_item_online = { .table_col = TABLE_COL_TIME_MAX(table_col_unit_dhm, 'o', "online"), .offset = SD_CPU_INFO_OFFSET(online_time_us), .type = SD_TYPE_U64, .desc = "Online time", .fn_u64 = l_sys_cpu_info_max_u64, }; struct sd_sys_item sd_sys_item_mem_max = { .table_col = TABLE_COL_MEM_SUM(table_col_unit_gib, 'a', "memmax"), .offset = SD_SYSTEM_OFFSET(mem.max_kib), .type = SD_TYPE_U64, .desc = "Maximum memory", .fn_u64 = l_sys_item_u64, }; struct sd_sys_item sd_sys_item_mem_use = { .table_col = TABLE_COL_MEM_SUM(table_col_unit_gib, 'u', "memuse"), .offset = SD_SYSTEM_OFFSET(mem.use_kib), .type = SD_TYPE_U64, .desc = "Used memory", .fn_u64 = l_sys_item_u64, }; struct sd_sys_item sd_sys_item_weight_cur = { .table_col = TABLE_COL_CNT_MAX('r', "wcur"), .offset = SD_SYSTEM_OFFSET(weight.cur), .type = SD_TYPE_U16, .desc = "Current weight", .fn_u64 = l_sys_item_u64, }; struct sd_sys_item sd_sys_item_weight_min = { .table_col = TABLE_COL_CNT_MAX('n', "wmin"), .offset = SD_SYSTEM_OFFSET(weight.min), .type = SD_TYPE_U16, .desc = "Minimum weight", .fn_u64 = l_sys_item_u64, }; struct sd_sys_item sd_sys_item_weight_max = { .table_col = TABLE_COL_CNT_MAX('x', "wmax"), .offset = SD_SYSTEM_OFFSET(weight.max), .type = SD_TYPE_U16, .desc = "Maximum weight", .fn_u64 = l_sys_item_u64, }; s390-tools-2.38.0/hyptop/table.c000066400000000000000000000643251502674226300162370ustar00rootroot00000000000000/* * hyptop - Show hypervisor performance data on System z * * Table module: Provide line mode and curses base table * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include "lib/util_fmt.h" #include "lib/util_libc.h" #include "helper.h" #include "hyptop.h" #include "table.h" #define L_ROWS_EXTRA 2 /* head + last */ #define table_col_iterate(t, col, i) \ for (i = 0, col = t->col_vec[0]; col != NULL; col = t->col_vec[++i]) /* * Is row marked? */ static int l_row_is_marked(struct table *t, struct table_row *row) { struct table_mark_key *key; util_list_iterate(&t->mark_key_list, key) { if (strcmp(row->entries[0].str, key->str) == 0) return 1; } return 0; } /* * Add mark key to table */ static void l_mark_key_add(struct table *t, char *str) { struct table_mark_key *key; key = ht_zalloc(sizeof(*key)); util_strlcpy(key->str, str, sizeof(key->str)); util_list_add_tail(&t->mark_key_list, key); } /* * Remove mark key from table */ static void l_mark_key_remove(struct table *t, char *str) { struct table_mark_key *key, *tmp; util_list_iterate_safe(&t->mark_key_list, key, tmp) { if (strcmp(str, key->str) == 0) { util_list_remove(&t->mark_key_list, key); ht_free(key); return; } } } /* * Delete all mark keys from table */ void table_row_mark_del_all(struct table *t) { struct table_mark_key *key, *tmp; struct table_row *row; util_list_iterate(&t->row_list, row) row->marked = 0; util_list_iterate_safe(&t->mark_key_list, key, tmp) { util_list_remove(&t->mark_key_list, key); ht_free(key); } } /* * Toggle mark for "row" */ void table_row_mark_toggle(struct table *t, struct table_row *row) { if (row->marked) { l_mark_key_remove(t, row->entries[0].str); row->marked = 0; t->row_cnt_marked--; if (t->row_cnt_marked == 0) t->mode_hide_unmarked = 0; } else { l_mark_key_add(t, row->entries[0].str); row->marked = 1; t->row_cnt_marked++; } } /* * Toggle mark by key */ void table_row_mark_toggle_by_key(struct table *t, const char *str) { struct table_row *row; util_list_iterate(&t->row_list, row) { if (strcmp(str, row->entries[0].str) == 0) table_row_mark_toggle(t, row); } } /* * Is column selected? */ static int l_col_selected(struct table *t, struct table_col *col) { return t->col_selected == col; } /* * Get number of rows for table */ static int l_row_cnt(struct table *t) { return t->mode_hide_unmarked ? t->row_cnt_marked : t->row_cnt; } /* * Get number of data rows that we can display on screen */ static int l_row_cnt_displ(struct table *t) { return g.c.row_cnt - t->row_cnt_extra; } /* * Alloc a new row for table */ struct table_row *table_row_alloc(struct table *t) { struct table_row *table_row; table_row = ht_zalloc(sizeof(*table_row)); table_row->entries = ht_zalloc(sizeof(*table_row->entries) * t->col_cnt); return table_row; } /* * Free table row */ static void table_row_free(struct table_row *table_row) { ht_free(table_row->entries); ht_free(table_row); } /* * Allocate and initialize a new table */ struct table *table_new(int extra_rows, int sorted, int first_bold, int with_units) { struct table *t = ht_zalloc(sizeof(*t)); util_list_init(&t->row_list, struct table_row, list); util_list_init(&t->mark_key_list, struct table_mark_key, list); t->row_cnt_marked = 0; if (with_units) t->row_cnt_extra = extra_rows + L_ROWS_EXTRA + 1; else t->row_cnt_extra = extra_rows + L_ROWS_EXTRA; t->attr_with_units = with_units; t->attr_sorted_table = sorted; t->attr_first_bold = first_bold; return t; } /* * Initialize headline for one column */ static void l_col_headline_init(struct table *t, struct table_col *col) { char *ptr; strcpy(col->p->head_first, col->head); ptr = strchr(col->p->head_first, tolower(col->hotkey)); assert(ptr != NULL); *ptr = 0; col->p->head_char[0] = col->hotkey; strcpy(col->p->head_last, ++ptr); if (!t->attr_sorted_table) { util_str_toupper(col->p->head_first); util_str_toupper(col->p->head_last); col->p->head_char[0] = toupper(col->p->head_char[0]); } } /* * Initialize the max width values for a column */ static void l_col_max_width_init(struct table *t, struct table_col *col) { /* Units are displayed with brackets, therefore (+2) */ if (t->attr_with_units) col->p->max_width = MAX(strlen(col->head), strlen(col->unit->str) + 2); else col->p->max_width = strlen(col->head); } /* * Add a new column to table */ void table_col_add(struct table *t, struct table_col *col) { col->p = ht_zalloc(sizeof(*col->p)); col->p->col_nr = t->col_cnt; col->p->enabled = 1; t->col_cnt++; t->col_vec = ht_realloc(t->col_vec, sizeof(void *) * (t->col_cnt + 1)); t->col_vec[t->col_cnt - 1] = col; t->col_vec[t->col_cnt] = NULL; if (!t->col_selected && t->attr_sorted_table) t->col_selected = col; if (t->row_last) table_row_free(t->row_last); t->row_last = table_row_alloc(t); l_col_headline_init(t, col); l_col_max_width_init(t, col); } /* * Initialize last row */ static void l_row_last_init(struct table *t) { memset(t->row_last->entries, 0, t->col_cnt * sizeof(struct table_entry)); } /* * Delete all rows of a table */ void table_row_del_all(struct table *t) { struct table_row *row, *tmp; util_list_iterate_safe(&t->row_list, row, tmp) { util_list_remove(&t->row_list, row); table_row_free(row); } l_row_last_init(t); t->row_cnt_marked = 0; t->ready = 0; t->row_cnt = 0; } /* * Reset table */ void table_reset(struct table *t) { table_row_mark_del_all(t); table_row_del_all(t); t->mode_sort_inverse = 0; t->mode_select = 0; } /* * Return true, if "e1" is less than "e2" */ static int l_entry_less_than(enum table_col_type type, struct table_entry *e1, struct table_entry *e2) { switch (type) { case TABLE_COL_TYPE_U64: return (e1->d.u64.v1 < e2->d.u64.v1); case TABLE_COL_TYPE_S64: return (e1->d.s64.v1 < e2->d.s64.v1); case TABLE_COL_TYPE_STR: return (strcmp(e1->str, e2->str) > 0); } return 0; /* Keep gcc quite */ } /* * Return true, if "row1" is less than "row2" */ static int l_row_less_than(struct table *t, struct table_row *row1, struct table_row *row2) { struct table_col *col = t->col_selected; struct table_entry *e1 = &row1->entries[col->p->col_nr]; struct table_entry *e2 = &row2->entries[col->p->col_nr]; if ((t->mode_sort_inverse && !col->p->rsort) || (!t->mode_sort_inverse && col->p->rsort)) return !l_entry_less_than(col->type, e1, e2); else return l_entry_less_than(col->type, e1, e2); } /* * Calculate: e1 = e1 + e2 */ static void l_entry_sum(enum table_col_type type, struct table_entry *e1, struct table_entry *e2) { switch (type) { case TABLE_COL_TYPE_U64: e1->d.u64.v1 += e2->d.u64.v1; return; case TABLE_COL_TYPE_S64: e1->d.s64.v1 += e2->d.s64.v1; return; default: assert(0); return; } } /* * Calculate: e1 = MAX(e1, e2) */ static void l_entry_max(enum table_col_type type, struct table_entry *e1, struct table_entry *e2) { switch (type) { case TABLE_COL_TYPE_U64: e1->d.u64.v1 = MAX(e1->d.u64.v1, e2->d.u64.v1); return; case TABLE_COL_TYPE_S64: e1->d.s64.v1 = MAX(e1->d.s64.v1, e2->d.s64.v1); return; default: assert(0); return; } } /* * Aggregate "row" to "last row" */ static void l_row_last_agg(struct table *t, struct table_row *table_row) { struct table_col *col; int col_nr; table_col_iterate(t, col, col_nr) { struct table_entry *e_last = &t->row_last->entries[col_nr]; struct table_entry *e_new = &table_row->entries[col_nr]; if (!e_new->set) continue; switch (col->agg) { case TABLE_COL_AGG_SUM: l_entry_sum(col->type, e_last, e_new); break; case TABLE_COL_AGG_MAX: l_entry_max(col->type, e_last, e_new); break; case TABLE_COL_AGG_NONE: break; } e_last->set = 1; } } /* * Format row: Invoke unit callback and adjust max width of column */ static void l_row_format(struct table *t, struct table_row *row) { unsigned int len, col_nr; struct table_col *col; table_col_iterate(t, col, col_nr) { struct table_entry *e = &row->entries[col_nr]; if (col->agg == TABLE_COL_AGG_NONE && row == t->row_last) len = 0; else len = col->unit->fn(col, e); assert(len < TABLE_STR_MAX); if (len > col->p->max_width) col->p->max_width = len; } } /* * Calculate last row */ static void l_row_last_calc(struct table *t) { struct table_row *row; l_row_last_init(t); util_list_iterate(&t->row_list, row) { if (t->mode_hide_unmarked && !row->marked) continue; l_row_last_agg(t, row); } l_row_format(t, t->row_last); } /* * Finish table after all rows have been added */ void table_finish(struct table *t) { l_row_last_calc(t); t->ready = 1; } /* * Add new row to table */ void table_row_add(struct table *t, struct table_row *row) { struct table_row *tmp; l_row_format(t, row); if (util_list_is_empty(&t->row_list) || !t->attr_sorted_table) { util_list_add_tail(&t->row_list, row); } else { util_list_iterate(&t->row_list, tmp) { if (l_row_less_than(t, tmp, row)) break; } if (tmp) util_list_add_prev(&t->row_list, row, tmp); else util_list_add_tail(&t->row_list, row); } if (l_row_is_marked(t, row)) { row->marked = 1; t->row_cnt_marked++; } t->row_cnt++; } /* * Rebuild table: Reformat all rows and adjust max width values */ void table_rebuild(struct table *t) { struct table_col *col; struct table_row *row; unsigned int i; table_col_iterate(t, col, i) l_col_max_width_init(t, col); util_list_iterate(&t->row_list, row) l_row_format(t, row); l_row_format(t, t->row_last); } /* * Compare callback for linked list sorting (ordering: large to small) */ static int l_row_cmp_fn(void *a, void *b, void *data) { return l_row_less_than(data, a, b) ? 1 : -1; } /* * Sort table (ordering: large to small) */ static void l_table_sort(struct table *t) { util_list_sort(&t->row_list, l_row_cmp_fn, t); } /* * Adjust table values for select mode (e.g. for window resize or scrolling) */ static void l_adjust_values_select_mode(struct table *t) { int row_cnt_displ = l_row_cnt_displ(t); int row_cnt = l_row_cnt(t); /* We went out of range with row selection */ if (t->row_nr_select >= row_cnt) t->row_nr_select = row_cnt - 1; /* Is selected row within visible area? */ if (t->row_nr_select < t->row_nr_begin) { /* Selected row is above area: Scroll up */ t->row_nr_begin = t->row_nr_select; } else if (t->row_nr_select - t->row_nr_begin >= row_cnt_displ) { /* Selected row is below area: Scroll down */ t->row_nr_begin = MAX(t->row_nr_select - row_cnt_displ + 1, 0); } } /* * Adjust table values (e.g. for window resize or scrolling) */ static void l_adjust_values(struct table *t) { int row_cnt_displ = l_row_cnt_displ(t); int row_cnt = l_row_cnt(t); if (t->mode_select) l_adjust_values_select_mode(t); /* If we do not use the whole screen, scroll up */ if (row_cnt - t->row_nr_begin < row_cnt_displ) t->row_nr_begin = MAX(row_cnt - row_cnt_displ, 0); } /* * Number of rows to be scrolled for page scroll */ static int l_scroll_page_row_cnt(struct table *t) { /* We have two rows overlap for scrolling pages */ return l_row_cnt_displ(t) - 2; } /* * Scroll table down */ void table_scroll_down(struct table *t, enum table_scroll_unit scroll_unit) { switch (scroll_unit) { case TABLE_SCROLL_LINE: t->row_nr_begin++; break; case TABLE_SCROLL_PAGE: t->row_nr_begin += l_scroll_page_row_cnt(t); break; case TABLE_SCROLL_LAST: t->row_nr_begin = t->row_cnt; break; } } /* * Scroll table up */ void table_scroll_up(struct table *t, enum table_scroll_unit scroll_unit) { switch (scroll_unit) { case TABLE_SCROLL_LINE: t->row_nr_begin = MAX(t->row_nr_begin - 1, 0); break; case TABLE_SCROLL_PAGE: t->row_nr_begin = MAX(t->row_nr_begin - l_scroll_page_row_cnt(t), 0); break; case TABLE_SCROLL_LAST: t->row_nr_begin = 0; break; } } /* * Return selected row */ static struct table_row *l_selected_row(struct table *t) { struct table_row *row; int row_nr = 0; util_list_iterate(&t->row_list, row) { if (t->mode_hide_unmarked && !row->marked) continue; if (row_nr == t->row_nr_select) return row; row_nr++; } return NULL; } /* * Toggle mark for selected row */ static void l_row_select_mark_toggle(struct table *t) { struct table_row *row; row = l_selected_row(t); table_row_mark_toggle(t, row); l_row_last_calc(t); } /* * Switch select mode off */ static void l_select_mode_off(struct table *t) { t->mode_select = 0; } /* * Switch select mode on */ static void l_select_mode_on(struct table *t) { t->mode_select = 1; t->row_nr_select = t->row_nr_begin; } /* * Get key for selected row */ void table_row_select_key_get(struct table *t, char str[TABLE_STR_MAX]) { struct table_row *row; row = l_selected_row(t); util_strlcpy(str, row->entries[0].str, TABLE_STR_MAX); } /* * Select row one page down */ void table_row_select_down(struct table *t, enum table_scroll_unit scroll_unit) { switch (scroll_unit) { case TABLE_SCROLL_LINE: t->row_nr_select++; break; case TABLE_SCROLL_PAGE: t->row_nr_select += g.c.row_cnt - t->row_cnt_extra; break; case TABLE_SCROLL_LAST: t->row_nr_select = t->row_cnt; break; } } /* * Select row one page up */ void table_row_select_up(struct table *t, enum table_scroll_unit scroll_unit) { switch (scroll_unit) { case TABLE_SCROLL_LINE: t->row_nr_select = MAX(t->row_nr_select - 1, 0); break; case TABLE_SCROLL_PAGE: t->row_nr_select = MAX(t->row_nr_begin - (g.c.row_cnt - t->row_cnt_extra), 0); break; case TABLE_SCROLL_LAST: t->row_nr_select = 0; break; } } /* * Toggle "hide unmarked" mode */ static int l_mode_hide_unmarked_toggle(struct table *t) { if (t->row_cnt_marked == 0) return -ENODEV; t->mode_hide_unmarked = t->mode_hide_unmarked ? 0 : 1; t->row_nr_select = 0; l_row_last_calc(t); return 0; } /* * Is it possible to scroll down the table? */ static int l_can_scroll_down(struct table *t) { int row_cnt = t->mode_hide_unmarked ? t->row_cnt_marked : t->row_cnt; int row_cnt_real = g.c.row_cnt - t->row_cnt_extra; return (row_cnt - t->row_nr_begin > row_cnt_real); } /* * Is it possible to scroll up the table? */ static int l_can_scroll_up(struct table *t) { return (t->row_nr_begin > 0); } /* * Update the status field */ static void l_status_update(struct table *t) { struct table_entry *e_status = &t->row_last->entries[0]; if (g.o.batch_mode_specified) return; if (l_can_scroll_down(t) && l_can_scroll_up(t)) strcpy(e_status->str, "|"); else if (l_can_scroll_up(t)) strcpy(e_status->str, "^"); else if (l_can_scroll_down(t)) strcpy(e_status->str, "V"); else strcpy(e_status->str, "="); if (t->attr_sorted_table) { strcat(e_status->str, ":"); if (t->mode_sort_inverse) strcat(e_status->str, "^"); else strcat(e_status->str, "V"); } strcat(e_status->str, ":"); if (t->mode_select) strcat(e_status->str, "S"); else strcat(e_status->str, "N"); } /* * Print string with alignment */ static void l_str_print(struct table_col *col, const char *str) { char unit[10]; if (col->align == TABLE_COL_ALIGN_LEFT) sprintf(unit, "%%-%ds", col->p->max_width); else sprintf(unit, "%%%ds", col->p->max_width); hyptop_printf(unit, str); } /* * Print string for "col" */ static void l_col_print(struct table *t, struct table_col *col, const char *str) { if (l_col_selected(t, col)) ht_underline_on(); if (col->p->col_nr == 0 && t->attr_first_bold) ht_bold_on(); l_str_print(col, str); if (l_col_selected(t, col)) ht_underline_off(); if (col->p->col_nr == 0 && t->attr_first_bold) ht_bold_off(); } /* * Print status field */ static void l_status_print(struct table *t, const char *str) { ht_bold_on(); l_str_print(t->col_vec[0], str); ht_bold_off(); } /* * Print headline of column */ static void l_col_headline_print(struct table *t, struct table_col *col) { unsigned int len = strlen(col->head); char blank_str[TABLE_STR_MAX]; (void) t; memset(blank_str, ' ', col->p->max_width - len); blank_str[col->p->max_width - len] = 0; if (l_col_selected(t, col)) ht_bold_on(); if (col->align == TABLE_COL_ALIGN_RIGHT) hyptop_printf("%s", blank_str); hyptop_printf("%s", col->p->head_first); if (t->attr_sorted_table) ht_underline_on(); hyptop_printf("%s", col->p->head_char); if (t->attr_sorted_table) ht_underline_off(); hyptop_printf("%s", col->p->head_last); if (col->align == TABLE_COL_ALIGN_LEFT) hyptop_printf("%s", blank_str); if (l_col_selected(t, col)) ht_bold_off(); } /* * Print headline for table */ static void l_headline_print(struct table *t) { struct table_col *col; int col_nr, first = 1; ht_reverse_on(); /* Print all column headlines */ table_col_iterate(t, col, col_nr) { if (!col->p->enabled) continue; if (first) first = 0; else hyptop_printf(" "); l_col_headline_print(t, col); } /* This creates a black bar to the end of the line */ hyptop_print_seek_back(0); ht_reverse_off(); hyptop_print_nl(); } /* * Print unit line for table */ static void l_unitline_print(struct table *t) { struct table_col *col; int col_nr, first = 1; char unit_str[20]; if (!t->attr_with_units) return; ht_reverse_on(); /* Print all column units */ table_col_iterate(t, col, col_nr) { if (!col->p->enabled) continue; if (first) first = 0; else hyptop_printf(" "); if (l_col_selected(t, col)) ht_bold_on(); snprintf(unit_str, sizeof(unit_str), "(%s)", col->unit->str); l_str_print(col, unit_str); if (l_col_selected(t, col)) ht_bold_off(); } /* This creates a black bar to the end of the line */ hyptop_print_seek_back(0); ht_reverse_off(); hyptop_print_nl(); } /* * Print one table row */ static void l_row_print(struct table *t, struct table_row *row) { struct table_col *col; int first = 1, col_nr; table_col_iterate(t, col, col_nr) { struct table_entry *e = &row->entries[col_nr]; if (!col->p->enabled) continue; if (!first) hyptop_printf(" "); else first = 0; if (row == t->row_last && col_nr == 0) l_status_print(t, e->str); else l_col_print(t, col, e->str); } } /* * Print table under curses */ static void l_table_print_curses(struct table *t) { struct table_row *row; int row_nr = 0; if (!t->ready) return; l_adjust_values(t); l_status_update(t); l_headline_print(t); l_unitline_print(t); util_list_iterate(&t->row_list, row) { if (t->mode_hide_unmarked && !row->marked) continue; if (row_nr < t->row_nr_begin) { row_nr++; continue; } if (row_nr - t->row_nr_begin >= g.c.row_cnt - t->row_cnt_extra) break; if (t->mode_select && row_nr == t->row_nr_select) ht_reverse_on(); if (row->marked) ht_bold_on(); l_row_print(t, row); if (t->mode_select && row_nr == t->row_nr_select) { #ifdef WITH_SCROLL_BAR hyptop_print_seek_back(1); #else hyptop_print_seek_back(0); #endif ht_reverse_off(); } if (row->marked) ht_bold_off(); hyptop_print_nl(); row_nr++; } ht_reverse_on(); l_row_print(t, t->row_last); hyptop_print_seek_back(0); ht_reverse_off(); #ifdef WITH_SCROLL_BAR if (t->mode_hide_unmarked) ht_print_scroll_bar(t->row_cnt_marked, t->row_nr_begin, t->row_cnt_extra - 1, 1, l_can_scroll_up(t), l_can_scroll_down(t), 1); else ht_print_scroll_bar(t->row_cnt, t->row_nr_begin, t->row_cnt_extra - 1, 1, l_can_scroll_up(t), l_can_scroll_down(t), 1); #endif } /* * Print table under batch mode */ static void l_table_print_all(struct table *t) { struct table_row *row; l_headline_print(t); l_unitline_print(t); util_list_iterate(&t->row_list, row) { l_row_print(t, row); hyptop_print_nl(); } l_row_print(t, t->row_last); hyptop_print_nl(); hyptop_printf("------------------------------------------------------" "-------------------------\n"); } /* * Print one table row as structured output * * Note: column filtering and sorting is explicitly ignored because the * assumption is that these operations can be trivially performed by the * consumer. */ static void l_row_print_formatted(struct table *t, struct table_row *row) { struct table_col *col; int col_nr; table_col_iterate(t, col, col_nr) { unsigned int flags = 0; struct table_entry *e = &row->entries[col_nr]; if (row == t->row_last && col_nr == 0) continue; if (table_col_needs_quotes(col)) flags = FMT_QUOTE; util_fmt_pair(flags, col->head, "%s", e->str); } } /* * Print table as structured output */ static void l_table_print_all_formatted(struct table *t) { struct table_row *row; util_fmt_obj_start(FMT_ROW, "iteration"); util_fmt_pair(FMT_PERSIST, "iteration", "%u", g.o.iterations_act); ht_fmt_time(); ht_fmt_cpu_types(); if (strcmp(g.o.cur_win->id, "sys_list") == 0) util_fmt_obj_start(FMT_LIST, "systems"); else util_fmt_obj_start(FMT_LIST, "cpus"); util_list_iterate(&t->row_list, row) { util_fmt_obj_start(FMT_ROW, "entry"); l_row_print_formatted(t, row); util_fmt_obj_end(); /* entry */ } util_fmt_obj_end(); /* systems[] */ util_fmt_obj_start(FMT_DEFAULT, "summary"); l_row_print_formatted(t, t->row_last); util_fmt_obj_end(); /* summary{} */ util_fmt_obj_end(); /* iteration */ } void table_fmt_start(void) { if (!g.o.format_specified) return; if (g.o.format != FMT_JSONSEQ) util_fmt_obj_start(FMT_LIST, "hyptop"); } void table_fmt_end(void) { if (!g.o.format_specified) return; if (g.o.format != FMT_JSONSEQ) util_fmt_obj_end(); /* hyptop[] */ } /* * Print table to screen */ void table_print(struct table *t) { if (g.o.batch_mode_specified) { if (!g.o.format_specified) l_table_print_all(t); else l_table_print_all_formatted(t); } else { l_table_print_curses(t); } } /* * Return column by hotkey */ static struct table_col *l_col_by_hotkey(struct table *t, char hotkey) { struct table_col *col; int col_nr; table_col_iterate(t, col, col_nr) { if (col->hotkey == hotkey) return col; } return NULL; } /* * Select next unit for column with "hotkey" */ void table_col_unit_next(struct table *t, char hotkey) { struct table_col *col; int i; col = l_col_by_hotkey(t, hotkey); if (!col || !col->unit_fam) assert(0); for (i = 0; col->unit_fam[i] != NULL; i++) { if (col->unit != col->unit_fam[i]) continue; if (col->unit_fam[i + 1] == NULL) col->unit = col->unit_fam[0]; else col->unit = col->unit_fam[i + 1]; return; } assert(0); } /* * Select previous unit for column with "hotkey" */ void table_col_unit_prev(struct table *t, char hotkey) { struct table_col *col; int i; col = l_col_by_hotkey(t, hotkey); if (!col || !col->unit_fam) assert(0); for (i = 0; col->unit_fam[i] != NULL; i++) { if (col->unit != col->unit_fam[i]) continue; if (i == 0) { int j; for (j = 0; col->unit_fam[j] != NULL; j++) {} col->unit = col->unit_fam[j - 1]; } else { col->unit = col->unit_fam[i - 1]; } return; } assert(0); } /* * Set unit for column */ int table_col_unit_set(struct table *t, char hotkey, const char *str) { struct table_col *col; int i; col = l_col_by_hotkey(t, hotkey); if (!col) return -ENODEV; for (i = 0; col->unit_fam[i] != NULL; i++) { if (strcasecmp(col->unit_fam[i]->str, str) == 0) { col->unit = col->unit_fam[i]; return 0; } } return -EINVAL; } /* * Select column by hotkey */ int table_col_select(struct table *t, char hotkey) { struct table_col *col; if (!t->attr_sorted_table) assert(0); col = l_col_by_hotkey(t, hotkey); if (!col || !col->p->enabled) return -ENODEV; if (t->col_selected == col) { t->mode_sort_inverse = t->mode_sort_inverse ? 0 : 1; } else { t->mode_sort_inverse = 0; t->col_selected = col; } table_rebuild(t); l_table_sort(t); return 0; } /* * Select next column */ void table_col_select_next(struct table *t) { int i; for (i = t->col_selected->p->col_nr + 1; i < t->col_cnt; i++) { if (t->col_vec[i]->p->enabled) goto found; } return; found: t->col_selected = t->col_vec[i]; l_table_sort(t); } /* * Select previous column */ void table_col_select_prev(struct table *t) { int i; for (i = t->col_selected->p->col_nr - 1; i >= 0; i--) { if (t->col_vec[i]->p->enabled) goto found; } return; found: t->col_selected = t->col_vec[i]; l_table_sort(t); } /* * Toggle enabled status for column */ void table_col_enable_toggle(struct table *t, char hotkey) { struct table_col *col; col = l_col_by_hotkey(t, hotkey); if (!col || col->p->col_nr == 0) return; col->p->enabled = col->p->enabled ? 0 : 1; if (col == t->col_selected) t->col_selected = t->col_vec[0]; } /* * Process input for table */ void table_process_input(struct table *t, int c) { switch (c) { case '<': if (t->attr_sorted_table) table_col_select_prev(t); break; case '>': if (t->attr_sorted_table) table_col_select_next(t); break; case '.': if (l_mode_hide_unmarked_toggle(t) == 0) l_select_mode_off(t); break; case '+': if (!t->attr_with_units) break; table_col_unit_next(t, t->col_selected->hotkey); table_rebuild(t); break; case '-': if (!t->attr_with_units) break; table_col_unit_prev(t, t->col_selected->hotkey); table_rebuild(t); break; case 'G': if (t->mode_select) table_row_select_down(t, TABLE_SCROLL_LAST); else table_scroll_down(t, TABLE_SCROLL_LAST); break; case 'g': if (t->mode_select) table_row_select_up(t, TABLE_SCROLL_LAST); else table_scroll_up(t, TABLE_SCROLL_LAST); break; case KEY_NPAGE: if (t->mode_select) table_row_select_down(t, TABLE_SCROLL_PAGE); else table_scroll_down(t, TABLE_SCROLL_PAGE); break; case KEY_PPAGE: if (t->mode_select) table_row_select_up(t, TABLE_SCROLL_PAGE); else table_scroll_up(t, TABLE_SCROLL_PAGE); break; case 'j': case KEY_DOWN: if (t->mode_select) table_row_select_down(t, TABLE_SCROLL_LINE); else table_scroll_down(t, TABLE_SCROLL_LINE); break; case 'k': case KEY_UP: if (t->mode_select) table_row_select_up(t, TABLE_SCROLL_LINE); else table_scroll_up(t, TABLE_SCROLL_LINE); break; case ' ': if (t->mode_select) { l_row_select_mark_toggle(t); } else { table_row_mark_del_all(t); t->mode_hide_unmarked = 0; } break; case 'l': case KEY_RIGHT: if (!t->mode_select) l_select_mode_on(t); break; case 'h': case KEY_LEFT: if (t->mode_select) l_select_mode_off(t); break; default: if (t->attr_sorted_table) table_col_select(t, c); } } s390-tools-2.38.0/hyptop/table.h000066400000000000000000000236621502674226300162430ustar00rootroot00000000000000/* * hyptop - Show hypervisor performance data on System z * * Table module: Provide line mode and curses base table * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef TABLE_H #define TABLE_H #include #include #include "lib/util_list.h" #include "helper.h" #define TABLE_STR_MAX 64 #define TABLE_HEADING_SIZE 20 struct table_col; struct table_entry; /* * Table Column Unit */ struct table_col_unit { int (*fn)(struct table_col*, struct table_entry *); const char *str; char *desc; char hotkey; }; /* Predefined units */ extern struct table_col_unit table_col_unit_str; extern struct table_col_unit table_col_unit_cnt; extern struct table_col_unit table_col_unit_kib; extern struct table_col_unit table_col_unit_mib; extern struct table_col_unit table_col_unit_gib; extern struct table_col_unit table_col_unit_us; extern struct table_col_unit table_col_unit_ms; extern struct table_col_unit table_col_unit_s; extern struct table_col_unit table_col_unit_hm; extern struct table_col_unit table_col_unit_dhm; extern struct table_col_unit table_col_unit_perc; extern struct table_col_unit table_col_unit_vis; /* Predefined families */ extern struct table_col_unit *table_col_unit_fam_str[]; extern struct table_col_unit *table_col_unit_fam_cnt[]; extern struct table_col_unit *table_col_unit_fam_mem[]; extern struct table_col_unit *table_col_unit_fam_time[]; extern struct table_col_unit *table_col_unit_fam_time_diff[]; extern struct table_col_unit *table_col_unit_fam_vis[]; /* * Table Column Type */ enum table_col_type { TABLE_COL_TYPE_U64, TABLE_COL_TYPE_S64, TABLE_COL_TYPE_STR, }; /* * Table Column Alignment */ enum table_col_align { TABLE_COL_ALIGN_LEFT, TABLE_COL_ALIGN_RIGHT, }; /* * Table Column Aggregation */ enum table_col_agg { TABLE_COL_AGG_SUM, TABLE_COL_AGG_MAX, TABLE_COL_AGG_NONE, }; static inline const char *table_col_agg_str(enum table_col_agg agg) { switch (agg) { case TABLE_COL_AGG_SUM: return "sum"; case TABLE_COL_AGG_MAX: return "max"; case TABLE_COL_AGG_NONE: return "none"; } return NULL; } /* * Table Column */ struct table_col_priv { unsigned int max_width; int col_nr; int enabled; char head_first[TABLE_HEADING_SIZE]; char head_char[2]; char head_last[TABLE_HEADING_SIZE]; int rsort; int needs_quotes; }; /* * Table Column Specification */ struct table_col_spec { char hotkey; char *unit_str; }; /* * Table Column */ struct table_col { enum table_col_type type; struct table_col_unit *unit; struct table_col_unit **unit_fam; enum table_col_align align; enum table_col_agg agg; char hotkey; char head[TABLE_HEADING_SIZE]; struct table_col_priv *p; }; static inline int table_col_enabled(struct table_col *col) { return col->p->enabled; } static inline int table_col_needs_quotes(struct table_col *col) { return col->p->needs_quotes; } /* * Table Column Constructor Macros */ #define TABLE_COL_STR(l, h) \ { \ .type = TABLE_COL_TYPE_STR, \ .unit = &table_col_unit_str, \ .unit_fam = table_col_unit_fam_str, \ .align = TABLE_COL_ALIGN_RIGHT, \ .agg = TABLE_COL_AGG_NONE, \ .hotkey = l, \ .head = h, \ } #define TABLE_COL_STR_LEFT(l, h) \ { \ .type = TABLE_COL_TYPE_STR, \ .unit = &table_col_unit_str, \ .unit_fam = table_col_unit_fam_str, \ .align = TABLE_COL_ALIGN_LEFT, \ .agg = TABLE_COL_AGG_NONE, \ .hotkey = l, \ .head = h, \ } #define TABLE_COL_CNT_SUM(l, h) \ { \ .type = TABLE_COL_TYPE_U64, \ .unit = &table_col_unit_cnt, \ .unit_fam = table_col_unit_fam_cnt, \ .align = TABLE_COL_ALIGN_RIGHT, \ .agg = TABLE_COL_AGG_SUM, \ .hotkey = l, \ .head = h, \ } #define TABLE_COL_CNT_NONE(l, h) \ { \ .type = TABLE_COL_TYPE_U64, \ .unit = &table_col_unit_cnt, \ .unit_fam = table_col_unit_fam_cnt, \ .align = TABLE_COL_ALIGN_RIGHT, \ .agg = TABLE_COL_AGG_NONE, \ .hotkey = l, \ .head = h, \ } #define TABLE_COL_CNT_MAX(l, h) \ { \ .type = TABLE_COL_TYPE_U64, \ .unit = &table_col_unit_cnt, \ .unit_fam = table_col_unit_fam_cnt, \ .align = TABLE_COL_ALIGN_RIGHT, \ .agg = TABLE_COL_AGG_MAX, \ .hotkey = l, \ .head = h, \ } #define TABLE_COL_MEM_SUM(f, l, h) \ { \ .type = TABLE_COL_TYPE_U64, \ .unit = &f, \ .unit_fam = table_col_unit_fam_mem, \ .align = TABLE_COL_ALIGN_RIGHT, \ .agg = TABLE_COL_AGG_SUM, \ .hotkey = l, \ .head = h, \ } #define TABLE_COL_TIME_SUM(f, l, h) \ { \ .type = TABLE_COL_TYPE_U64, \ .unit = &f, \ .unit_fam = table_col_unit_fam_time, \ .align = TABLE_COL_ALIGN_RIGHT, \ .agg = TABLE_COL_AGG_SUM, \ .hotkey = l, \ .head = h, \ } #define TABLE_COL_TIME_DIFF_SUM(f, l, h) \ { \ .type = TABLE_COL_TYPE_U64, \ .unit = &f, \ .unit_fam = table_col_unit_fam_time_diff, \ .align = TABLE_COL_ALIGN_RIGHT, \ .agg = TABLE_COL_AGG_SUM, \ .hotkey = l, \ .head = h, \ } #define TABLE_COL_STIME_SUM(f, l, h) \ { \ .type = TABLE_COL_TYPE_S64, \ .unit = &f, \ .unit_fam = table_col_unit_fam_time, \ .align = TABLE_COL_ALIGN_RIGHT, \ .agg = TABLE_COL_AGG_SUM, \ .hotkey = l, \ .head = h, \ } #define TABLE_COL_STIME_DIFF_SUM(f, l, h) \ { \ .type = TABLE_COL_TYPE_S64, \ .unit = &f, \ .unit_fam = table_col_unit_fam_time_diff, \ .align = TABLE_COL_ALIGN_RIGHT, \ .agg = TABLE_COL_AGG_SUM, \ .hotkey = l, \ .head = h, \ } #define TABLE_COL_TIME_MAX(f, l, h) \ { \ .type = TABLE_COL_TYPE_U64, \ .unit = &f, \ .unit_fam = table_col_unit_fam_time, \ .align = TABLE_COL_ALIGN_RIGHT, \ .agg = TABLE_COL_AGG_MAX, \ .hotkey = l, \ .head = h, \ } /* * Set reverse sort property for column */ static inline void table_col_rsort(struct table_col *col) { col->p->rsort = 1; } /* * Column member access macros */ #define table_col_hotkey(col) ((col)->hotkey) #define table_col_head(col) ((col)->head) #define table_col_unit_str(col) ((col)->unit->str) /* * Table Entry */ struct table_entry { union { struct { u64 v1; u64 v2; } u64; struct { s64 v1; s64 v2; } s64; } d; int set; char str[TABLE_STR_MAX]; }; /* * Table Row */ struct table_row { struct util_list_node list; struct table_entry *entries; int marked; }; /* * Table Mark Key */ struct table_mark_key { struct util_list_node list; char str[TABLE_STR_MAX]; }; /* * Table */ struct table { struct util_list row_list; int col_cnt; struct table_col **col_vec; struct table_col *col_selected; struct table_row *row_last; int row_cnt; int row_cnt_marked; int row_cnt_extra; int row_nr_begin; int row_nr_select; int ready; struct util_list mark_key_list; int attr_sorted_table; int attr_first_bold; int attr_with_units; int mode_sort_inverse; int mode_select; int mode_hide_unmarked; }; /* * Return if we are in select mode */ static inline int table_mode_select(struct table *t) { return t->mode_select; } /* * Table croll units */ enum table_scroll_unit { TABLE_SCROLL_LINE, TABLE_SCROLL_PAGE, TABLE_SCROLL_LAST, }; /* * Prototypes */ extern struct table *table_new(int extra_rows, int sorted, int first_bold, int with_units); extern void table_reset(struct table *t); extern void table_rebuild(struct table *t); extern void table_finish(struct table *t); extern void table_print(struct table *t); extern void table_process_input(struct table *t, int c); extern void table_col_unit_next(struct table *t, char hotkey); extern void table_col_unit_prev(struct table *t, char hotkey); extern int table_col_unit_set(struct table *t, char hotkey, const char *unit); extern void table_col_add(struct table *t, struct table_col *col); extern int table_col_select(struct table *t, char hotkey); extern void table_col_select_next(struct table *t); extern void table_col_select_prev(struct table *t); extern void table_col_enable_toggle(struct table *t, char hotkey); extern void table_row_del_all(struct table *t); extern void table_row_add(struct table *t, struct table_row *row); extern void table_row_mark(struct table *t, struct table_row *row); extern void table_row_mark_del_all(struct table *t); extern void table_row_mark_toggle(struct table *t, struct table_row *row); extern void table_row_mark_toggle_by_key(struct table *t, const char *mark_key); extern void table_row_select_down(struct table *t, enum table_scroll_unit unit); extern void table_row_select_up(struct table *t, enum table_scroll_unit unit); extern void table_row_select_key_get(struct table *t, char str[TABLE_STR_MAX]); extern struct table_row *table_row_alloc(struct table *t); extern void table_scroll_down(struct table *t, enum table_scroll_unit unit); extern void table_scroll_up(struct table *t, enum table_scroll_unit unit); extern void table_fmt_start(void); extern void table_fmt_end(void); /* * Entry add functions */ static inline void table_row_entry_u64_add(struct table_row *table_row, struct table_col *table_col, u64 value) { table_row->entries[table_col->p->col_nr].d.u64.v1 = value; table_row->entries[table_col->p->col_nr].set = 1; } static inline void table_row_entry_s64_add(struct table_row *table_row, struct table_col *table_col, s64 value) { table_row->entries[table_col->p->col_nr].d.s64.v1 = value; table_row->entries[table_col->p->col_nr].set = 1; } static inline void table_row_entry_u64_add_pair(struct table_row *table_row, struct table_col *table_col, u64 value1, u64 value2) { table_row->entries[table_col->p->col_nr].d.u64.v1 = value1; table_row->entries[table_col->p->col_nr].d.u64.v2 = value2; table_row->entries[table_col->p->col_nr].set = 1; } static inline void table_row_entry_str_add(struct table_row *table_row, struct table_col *table_col, const char *str) { assert(strlen(str) < TABLE_STR_MAX); strcpy(table_row->entries[table_col->p->col_nr].str, str); table_row->entries[table_col->p->col_nr].set = 1; } /* * Interate over all mark keys */ #define table_iterate_mark_keys(t, key) \ util_list_iterate(&t->mark_key_list, key) #endif /* TABLE_H */ s390-tools-2.38.0/hyptop/table_col_unit.c000066400000000000000000000172021502674226300201230ustar00rootroot00000000000000/* * hyptop - Show hypervisor performance data on System z * * Table unit module: Provide different units for data * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include "table.h" #define L_VISUAL_ROW_CNT 45 #define L_COL_FMT_STR_0 "%.0lf" #define L_COL_FMT_STR_2 "%.2lf" #define L_COL_NOT_SET_STR "-" /* * Helper: Divide value and format it */ static int l_unit_raw_div(struct table_col *col, struct table_entry *e, unsigned int divisor, const char *fmt_str) { double v1; if (!e->set) return snprintf(e->str, sizeof(e->str), L_COL_NOT_SET_STR); switch (col->type) { case TABLE_COL_TYPE_U64: v1 = ((double) e->d.u64.v1) / divisor; break; case TABLE_COL_TYPE_S64: v1 = ((double) e->d.s64.v1) / divisor; break; default: assert(0); } return snprintf(e->str, sizeof(e->str), fmt_str, v1); } /* * Helper: Format value as is */ static int l_unit_raw(struct table_col *col, struct table_entry *e) { if (!e->set) return snprintf(e->str, sizeof(e->str), L_COL_NOT_SET_STR); switch (col->type) { case TABLE_COL_TYPE_U64: return snprintf(e->str, sizeof(e->str), "%llu", e->d.u64.v1); case TABLE_COL_TYPE_S64: return snprintf(e->str, sizeof(e->str), "%lld", e->d.s64.v1); default: assert(0); return 0; } } /* * Format: String */ static int l_str(struct table_col *col, struct table_entry *e) { col->p->needs_quotes = 1; return strlen(e->str); } struct table_col_unit table_col_unit_str = { .fn = l_str, .hotkey = 'S', .str = "str", .desc = "String", }; /* * Format: Count */ static int l_unit_cnt(struct table_col *col, struct table_entry *e) { return l_unit_raw(col, e); } struct table_col_unit table_col_unit_cnt = { .fn = l_unit_cnt, .hotkey = '#', .str = "#", .desc = "Count", }; /* * Format: Kibibytes */ static int l_unit_kib(struct table_col *col, struct table_entry *e) { return l_unit_raw(col, e); } struct table_col_unit table_col_unit_kib = { .fn = l_unit_kib, .hotkey = 'k', .str = "KiB", .desc = "Kibibyte (1.024 bytes)", }; /* * Format: Mebibytes */ static int l_unit_mib(struct table_col *col, struct table_entry *e) { return l_unit_raw_div(col, e, 1024, L_COL_FMT_STR_2); } struct table_col_unit table_col_unit_mib = { .fn = l_unit_mib, .hotkey = 'M', .str = "MiB", .desc = "Mebibyte (1.048.576 bytes)", }; /* * Format: Gibibytes */ static int l_unit_gib(struct table_col *col, struct table_entry *e) { return l_unit_raw_div(col, e, 1024 * 1024, L_COL_FMT_STR_2); } struct table_col_unit table_col_unit_gib = { .fn = l_unit_gib, .hotkey = 'g', .str = "GiB", .desc = "Gibibyte (1.073.741.824 bytes)", }; /* * Format: Microseconds */ static int l_unit_us(struct table_col *col, struct table_entry *e) { return l_unit_raw(col, e); } struct table_col_unit table_col_unit_us = { .fn = l_unit_us, .hotkey = 'u', .str = "us", }; /* * Format: Milliseconds */ static int l_unit_ms(struct table_col *col, struct table_entry *e) { return l_unit_raw_div(col, e, 1000, L_COL_FMT_STR_2); } struct table_col_unit table_col_unit_ms = { .fn = l_unit_ms, .hotkey = 'm', .str = "ms", }; /* * Format: Percent (Hundreds) */ static int l_unit_perc(struct table_col *col, struct table_entry *e) { return l_unit_raw_div(col, e, 10000, L_COL_FMT_STR_2); } struct table_col_unit table_col_unit_perc = { .fn = l_unit_perc, .hotkey = '%', .str = "%", .desc = "Percent", }; /* * Format: Seconds */ static int l_unit_s(struct table_col *col, struct table_entry *e) { return l_unit_raw_div(col, e, 1000000, L_COL_FMT_STR_2); } struct table_col_unit table_col_unit_s = { .fn = l_unit_s, .hotkey = 's', .str = "s", .desc = "Seconds", }; /* * Format: Minutes */ static int l_unit_m(struct table_col *col, struct table_entry *e) { return l_unit_raw_div(col, e, 1000000 * 60, L_COL_FMT_STR_0); } static struct table_col_unit table_col_unit_m = { .fn = l_unit_m, .hotkey = 'm', .str = "m", .desc = "Minutes", }; /* * Format: Hours:Minutes */ static int l_unit_hm_u64(char *str, u64 v1, int negative) { u64 time_tmp, time_h, time_m; time_tmp = v1 / (1000000 * 60); time_h = time_tmp / 60; time_m = time_tmp - time_h * 60; if (negative) return sprintf(str, "-%llu:%02llu", time_h, time_m); else return sprintf(str, "%llu:%02llu", time_h, time_m); } static int l_unit_hm(struct table_col *col, struct table_entry *e) { col->p->needs_quotes = 1; if (!e->set) return snprintf(e->str, sizeof(e->str), L_COL_NOT_SET_STR); switch (col->type) { case TABLE_COL_TYPE_U64: return l_unit_hm_u64(e->str, e->d.u64.v1, 0); case TABLE_COL_TYPE_S64: if (e->d.s64.v1 < 0) return l_unit_hm_u64(e->str, -e->d.s64.v1, 1); else return l_unit_hm_u64(e->str, e->d.s64.v1, 0); default: assert(0); return 0; } } struct table_col_unit table_col_unit_hm = { .fn = l_unit_hm, .hotkey = 'H', .str = "hm", .desc = "Hours:Minutes", }; /* * Format: Days:Hours:Minutes */ static int l_unit_dhm_u64(char *str, u64 v1, int negative) { u64 time_tmp, time_d, time_h, time_m; time_tmp = v1 / (1000000 * 60); time_d = time_tmp / (60 * 24); time_h = time_tmp / 60 - time_d * 24; time_m = time_tmp - time_h * 60 - time_d * 60 * 24; if (negative) return sprintf(str, "-%llu:%02llu:%02llu", time_d, time_h, time_m); else return sprintf(str, "%llu:%02llu:%02llu", time_d, time_h, time_m); } static int l_unit_dhm(struct table_col *col, struct table_entry *e) { col->p->needs_quotes = 1; if (!e->set) return snprintf(e->str, sizeof(e->str), L_COL_NOT_SET_STR); switch (col->type) { case TABLE_COL_TYPE_U64: return l_unit_dhm_u64(e->str, e->d.u64.v1, 0); case TABLE_COL_TYPE_S64: if (e->d.s64.v1 < 0) return l_unit_dhm_u64(e->str, -e->d.s64.v1, 1); else return l_unit_dhm_u64(e->str, e->d.s64.v1, 0); default: assert(0); return 0; } } struct table_col_unit table_col_unit_dhm = { .fn = l_unit_dhm, .hotkey = 'D', .str = "dhm", .desc = "Days:Hours:Minutes", }; /* * Format: Visualization with bar chart */ static int l_unit_vis(struct table_col *col, struct table_entry *e) { double val1_perc, val2_perc; int val1_nr, val2_nr; int i; assert(col->type == TABLE_COL_TYPE_U64); sprintf(e->str, "|"); val1_perc = e->d.u64.v1; val1_perc /= 1000000; val2_perc = e->d.u64.v2; val2_perc /= 1000000; val1_nr = (val1_perc * L_VISUAL_ROW_CNT) + 0.5; val2_nr = (val2_perc * L_VISUAL_ROW_CNT) + 0.5; if (val1_nr > L_VISUAL_ROW_CNT) val1_nr = L_VISUAL_ROW_CNT; if (val1_nr + val2_nr > L_VISUAL_ROW_CNT) val2_nr = L_VISUAL_ROW_CNT - val1_nr; for (i = 0; i < val1_nr; i++) strcat(e->str, "#"); for (i = 0; i < val2_nr; i++) strcat(e->str, "-"); for (i = 0; i < L_VISUAL_ROW_CNT - val1_nr - val2_nr; i++) strcat(e->str, " "); strcat(e->str, "|"); return strlen(e->str); } struct table_col_unit table_col_unit_vis = { .fn = l_unit_vis, .hotkey = 'v', .str = "vis", .desc = "Visualization with bar chart", }; /* * Families */ struct table_col_unit *table_col_unit_fam_str[] = { &table_col_unit_str, NULL, }; struct table_col_unit *table_col_unit_fam_cnt[] = { &table_col_unit_cnt, NULL, }; struct table_col_unit *table_col_unit_fam_mem[] = { &table_col_unit_kib, &table_col_unit_mib, &table_col_unit_gib, NULL, }; struct table_col_unit *table_col_unit_fam_time_diff[] = { &table_col_unit_us, &table_col_unit_ms, &table_col_unit_perc, &table_col_unit_s, NULL, }; struct table_col_unit *table_col_unit_fam_time[] = { &table_col_unit_us, &table_col_unit_ms, &table_col_unit_s, &table_col_unit_m, &table_col_unit_hm, &table_col_unit_dhm, NULL, }; struct table_col_unit *table_col_unit_fam_vis[] = { &table_col_unit_vis, NULL, }; s390-tools-2.38.0/hyptop/tbox.c000066400000000000000000000101611502674226300161110ustar00rootroot00000000000000/* * hyptop - Show hypervisor performance data on System z * * Text box: Provide scrollable text window under curses. * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include "helper.h" #include "hyptop.h" #include "tbox.h" /* * Delete one line */ static void l_line_free(struct tbox_line *line) { ht_free(line->str); ht_free(line); } /* * Delete all lines */ void tbox_line_del_all(struct tbox *tb) { struct tbox_line *line, *tmp; util_list_iterate_safe(&tb->line_list, line, tmp) { util_list_remove(&tb->line_list, line); l_line_free(line); } tb->tbox_ready = 0; tb->line_cnt = 0; } /* * Finish text box after all lines have been added */ void tbox_finish(struct tbox *tb) { tb->tbox_ready = 1; } /* * Add one line to text box */ void tbox_line_add(struct tbox *tb, const char *str) { struct tbox_line *line; if (strlen(str) > TBOX_MAX_STR) assert(0); line = ht_zalloc(sizeof(*line)); line->str = ht_strdup(str); util_list_add_tail(&tb->line_list, line); tb->last_line = line; tb->line_cnt++; } /* * Adjust values, if we scrolled out of range */ static void l_adjust_values(struct tbox *tb) { if (tb->line_cnt - tb->line_start < g.c.row_cnt) tb->line_start = MAX(tb->line_cnt - g.c.row_cnt, 0); } /* * Scroll text box down */ void tbox_scroll_down(struct tbox *tb, enum tbox_scroll_unit unit) { switch (unit) { case TBOX_SCROLL_LINE: tb->line_start++; break; case TBOX_SCROLL_PAGE: tb->line_start += (g.c.row_cnt - 2); break; case TBOX_SCROLL_LAST: tb->line_start = tb->line_cnt; break; } } /* * Scroll text box up */ void tbox_scroll_up(struct tbox *tb, enum tbox_scroll_unit unit) { switch (unit) { case TBOX_SCROLL_LINE: tb->line_start = MAX(tb->line_start - 1, 0); break; case TBOX_SCROLL_PAGE: tb->line_start = MAX(tb->line_start - (g.c.row_cnt - 2), 0); break; case TBOX_SCROLL_LAST: tb->line_start = 0; break; } } /* * Resize text box */ void tbox_term_resize(struct tbox *tb) { l_adjust_values(tb); } /* * Toggle bold curses format attribute */ static void l_bold_toggle(void) { static int bold_on; if (bold_on) { ht_bold_off(); bold_on = 0; } else { ht_bold_on(); bold_on = 1; } } /* * Toggle underline curses format attribute */ static void l_underline_toggle(void) { static int underline_on; if (underline_on) { ht_underline_off(); underline_on = 0; } else { ht_underline_on(); underline_on = 1; } } /* * Print one line with attributes (bold and underline) */ static void l_print_line(const char *line) { char line_cpy[TBOX_MAX_STR + 1]; char *ptr_old, *ptr; util_strlcpy(line_cpy, line, sizeof(line_cpy)); ptr_old = ptr = line_cpy; do { ptr = strchr(ptr, '\\'); if (ptr) { *ptr = 0; hyptop_printf("%s", ptr_old); switch (ptr[1]) { case 'B': l_bold_toggle(); break; case 'U': l_underline_toggle(); break; } ptr += 2; ptr_old = ptr; } else { hyptop_printf("%s", ptr_old); return; } } while (*ptr); } #ifdef WITH_SCROLL_BAR static int l_can_scroll_down(struct tbox *tb) { return (tb->line_cnt - tb->line_start > g.c.row_cnt); } static int l_can_scroll_up(struct tbox *tb) { return (tb->line_start > 0); } #endif /* * Print text box to screen */ void tbox_print(struct tbox *tb) { int line_nr = 0, first = 1; struct tbox_line *line; if (!tb->tbox_ready) return; l_adjust_values(tb); util_list_iterate(&tb->line_list, line) { if (line_nr < tb->line_start) { line_nr++; continue; } /* Have we printed the whole visible screen ? */ if (line_nr - tb->line_start >= g.c.row_cnt) break; if (first) first = 0; else hyptop_print_nl(); l_print_line(line->str); line_nr++; } #ifdef WITH_SCROLL_BAR ht_print_scroll_bar(tb->line_cnt, tb->line_start, 0, 0, l_can_scroll_up(tb), l_can_scroll_down(tb), 0); #endif } /* * Create new text box */ struct tbox *tbox_new(void) { struct tbox *tb; tb = ht_zalloc(sizeof(*tb)); util_list_init(&tb->line_list, struct tbox_line, list); return tb; } s390-tools-2.38.0/hyptop/tbox.h000066400000000000000000000022211502674226300161140ustar00rootroot00000000000000/* * hyptop - Show hypervisor performance data on System z * * Text box: Provide scrollable text window under curses. * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef TBOX_H #define TBOX_H #include "lib/util_list.h" #define TBOX_MAX_STR 120 struct tbox_line { struct util_list_node list; char *str; }; struct tbox { struct util_list line_list; int line_cnt; int line_start; int tbox_ready; struct tbox_line *last_line; }; enum tbox_scroll_unit { TBOX_SCROLL_LINE, TBOX_SCROLL_PAGE, TBOX_SCROLL_LAST, }; struct tbox *tbox_new(void); void tbox_line_del_all(struct tbox *tb); void tbox_line_add(struct tbox *tb, const char *str); void tbox_finish(struct tbox *tb); void tbox_scroll_down(struct tbox *tb, enum tbox_scroll_unit); void tbox_scroll_up(struct tbox *tb, enum tbox_scroll_unit); void tbox_term_resize(struct tbox *tb); void tbox_print(struct tbox *tb); #define tbox_printf(tb, x...) \ { \ char line[TBOX_MAX_STR + 1]; \ sprintf(line, x); \ tbox_line_add(tb, line); \ } #endif /* TBOX_H */ s390-tools-2.38.0/hyptop/win_cpu_types.c000066400000000000000000000145261502674226300200360ustar00rootroot00000000000000/* * hyptop - Show hypervisor performance data on System z * * Window "cpu_types": Select CPU types used for CPU data calculation. * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include "helper.h" #include "hyptop.h" #include "nav_desc.h" #include "sd.h" #include "table.h" #include "win_cpu_types.h" /* * Globals for cpu_types window */ static struct table_col l_col_key = TABLE_COL_STR_LEFT('k', "k"); static struct table_col l_col_select = TABLE_COL_STR_LEFT('s', "s"); static struct table_col l_col_id = TABLE_COL_STR_LEFT('i', "id"); static struct table_col l_col_desc = TABLE_COL_STR_LEFT('d', "description"); /* * Online help text for cpu_types window */ static const char l_help_str[] = "In the \"cpu_types\" window you can select the CPU types that are used for\n" "calculating CPU data. Toggle the selection of types either by pressing the\n" "corresponding hotkey or by selecting them in select mode using the SPACE bar.\n" "\n" "The table of the \"cpu_types\" window has the following columns:\n" " - K : Hotkey of CPU type\n" " - S : Shows if CPU type is selected\n" " - ID : Name of CPU type\n" " - DESC: Description of CPU type\n"; /* * Description of Navigation Keys (used for help window) */ static struct nav_desc *l_nav_desc_normal_vec[] = { &nav_desc_select_mode_enter, &nav_desc_marks_clear, &nav_desc_win_leave_cpu_types, NULL, }; static struct nav_desc *l_nav_desc_select_vec[] = { &nav_desc_select_mode_leave, &nav_desc_mark_toggle, &nav_desc_win_leave_cpu_types_fast, NULL, }; static struct nav_desc *l_nav_desc_general_vec[] = { &nav_desc_toggle_mark_hotkey, &nav_desc_scroll_up_line, &nav_desc_scroll_down_line, &nav_desc_scroll_up_page, &nav_desc_scroll_down_page, &nav_desc_scroll_up_head, &nav_desc_scroll_down_tail, &nav_desc_mark_toggle_view, NULL, }; /* * Add a CPU type to the table */ static void l_add_cpu_type(struct win_cpu_types *win_cpu_types, struct sd_cpu_type *cpu_type) { char char_str[2], select_str[2]; struct table_row *table_row; if (sd_cpu_type_selected(cpu_type)) sprintf(select_str, "*"); else sprintf(select_str, " "); sprintf(char_str, "%c", cpu_type->hotkey); table_row = table_row_alloc(win_cpu_types->t); table_row_entry_str_add(table_row, &l_col_select, select_str); table_row_entry_str_add(table_row, &l_col_key, char_str); table_row_entry_str_add(table_row, &l_col_id, cpu_type->id); table_row_entry_str_add(table_row, &l_col_desc, cpu_type->desc); table_row_add(win_cpu_types->t, table_row); if (sd_cpu_type_selected(cpu_type)) table_row_mark_toggle(win_cpu_types->t, table_row); } /* * Fill all available CPU types into table */ static void l_table_create(struct win_cpu_types *win_cpu_types) { struct sd_cpu_type *cpu_type; unsigned int i; table_row_del_all(win_cpu_types->t); table_row_mark_del_all(win_cpu_types->t); sd_cpu_type_iterate(cpu_type, i) l_add_cpu_type(win_cpu_types, cpu_type); table_finish(win_cpu_types->t); } /* * Toggle the cpu type specified by "key" in the system data module */ static void l_toggle_cpu_type(char key) { struct sd_cpu_type *cpu_type; unsigned int i; sd_cpu_type_iterate(cpu_type, i) { if (key == cpu_type->hotkey) { sd_cpu_type_select_toggle(cpu_type); return; } } } /* * Process input for selection with SPACE key */ static void l_process_input_select_space(struct win_cpu_types *win_cpu_types) { char cpu_type_key[TABLE_STR_MAX]; if (table_mode_select(win_cpu_types->t)) { table_row_select_key_get(win_cpu_types->t, cpu_type_key); l_toggle_cpu_type(cpu_type_key[0]); } else { struct table_mark_key *key; table_iterate_mark_keys(win_cpu_types->t, key) l_toggle_cpu_type(key->str[0]); } } /* * Process input for selection with hotkey */ static void l_process_input_select_key(struct win_cpu_types *win_cpu_types, int c) { char cpu_type_key[TABLE_STR_MAX]; sprintf(cpu_type_key, "%c", c); table_row_mark_toggle_by_key(win_cpu_types->t, cpu_type_key); l_toggle_cpu_type(cpu_type_key[0]); } /* * Process input and switch window if necessary */ static enum hyptop_win_action l_process_input(struct hyptop_win *win, int c) { struct win_cpu_types *win_cpu_types = (struct win_cpu_types *) win; switch (c) { case 't': case 'q': return win_back(); case KEY_RETURN: case KEY_ENTER: case 'h': case KEY_LEFT: if (!table_mode_select(win_cpu_types->t)) return win_back(); break; case '?': return win_switch(win_cpu_types->win_help); case ' ': l_process_input_select_space(win_cpu_types); break; case ERR: return WIN_KEEP; default: l_process_input_select_key(win_cpu_types, c); break; } table_process_input(win_cpu_types->t, c); hyptop_update_term(); return WIN_KEEP; } /* * Event loop: We stay in hyptop_process_input() until fields menu * is left. */ static void l_run(struct hyptop_win *win) { struct win_cpu_types *win_cpu_types = (struct win_cpu_types *) win; table_reset(win_cpu_types->t); while (1) { hyptop_update_term(); if (hyptop_process_input() == WIN_SWITCH) return; } } /* * Create table and print it to screen */ static void l_update_term(struct hyptop_win *win) { struct win_cpu_types *win_cpu_types = (struct win_cpu_types *) win; l_table_create(win_cpu_types); hyptop_printf("Select Processor Types"); ht_print_help_icon(); hyptop_print_nl(); table_print(win_cpu_types->t); } /* * Create new cpu_types window */ struct hyptop_win *win_cpu_types_new(void) { struct win_cpu_types *win_cpu_types; win_cpu_types = ht_zalloc(sizeof(*win_cpu_types)); win_cpu_types->win.process_input = l_process_input; win_cpu_types->win.update_term = l_update_term; win_cpu_types->win.run = l_run; win_cpu_types->win.desc = l_help_str; win_cpu_types->win.desc_normal_vec = l_nav_desc_normal_vec; win_cpu_types->win.desc_select_vec = l_nav_desc_select_vec; win_cpu_types->win.desc_general_vec = l_nav_desc_general_vec; win_cpu_types->win.id = "cpu_types"; win_cpu_types->t = table_new(1, 0, 0, 0); table_col_add(win_cpu_types->t, &l_col_key); table_col_add(win_cpu_types->t, &l_col_select); table_col_add(win_cpu_types->t, &l_col_id); table_col_add(win_cpu_types->t, &l_col_desc); win_cpu_types->win_help = win_help_new((struct hyptop_win *) win_cpu_types); l_table_create(win_cpu_types); return (struct hyptop_win *) win_cpu_types; } s390-tools-2.38.0/hyptop/win_cpu_types.h000066400000000000000000000011621502674226300200330ustar00rootroot00000000000000/* * hyptop - Show hypervisor performance data on System z * * Window "cpu_types": Select CPU types used for CPU data calculation. * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef WIN_CPU_TYPES_H #define WIN_CPU_TYPES_H #include "hyptop.h" #include "table.h" #include "win_help.h" struct win_cpu_types { struct hyptop_win win; struct table *t; int in_select; struct hyptop_win *win_help; }; extern struct hyptop_win *win_cpu_types_new(void); #endif /* WIN_CPU_TYPES_H */ s390-tools-2.38.0/hyptop/win_fields.c000066400000000000000000000164071502674226300172710ustar00rootroot00000000000000/* * hyptop - Show hypervisor performance data on System z * * Window "fields": Select fields dialog. * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include "helper.h" #include "hyptop.h" #include "table.h" #include "win_fields.h" /* * Globals for fields window */ static struct table_col l_col_select = TABLE_COL_STR_LEFT('s', "s"); static struct table_col l_col_id = TABLE_COL_STR_LEFT('i', "id"); static struct table_col l_col_key = TABLE_COL_STR_LEFT('k', "k"); static struct table_col l_col_unit = TABLE_COL_STR_LEFT('u', "unit"); static struct table_col l_col_agg = TABLE_COL_STR_LEFT('a', "agg"); static struct table_col l_col_desc = TABLE_COL_STR_LEFT('d', "description"); /* * Online help text for fields window */ static const char l_help_str[] = "In the \"fields\" window you can select fields and units. Toggle the selection\n" "of fields either by pressing the corresponding hotkey or by selecting them\n" "in select mode using the SPACE bar. The units can be changed by selecting a\n" "field in select mode and by pressing '+' or '-'.\n" "\n" "The table of the \"fields\" window has the following columns:\n" " - K : Hotkey of field\n" " - S : Shows if field is selected\n" " - ID : Name of field\n" " - UNIT: Current unit used for field\n" " - AGG : Aggregation used for last line of table\n" " - DESC: Description of field\n"; /* * Description of Navigation Keys (used for help window) */ static struct nav_desc *l_nav_desc_normal_vec[] = { &nav_desc_select_mode_enter, &nav_desc_marks_clear, &nav_desc_win_leave_fields, NULL, }; static struct nav_desc *l_nav_desc_select_vec[] = { &nav_desc_select_mode_leave, &nav_desc_mark_toggle, &nav_desc_row_unit_increase, &nav_desc_row_unit_decrease, &nav_desc_win_leave_fields_fast, NULL, }; static struct nav_desc *l_nav_desc_general_vec[] = { &nav_desc_toggle_mark_hotkey, &nav_desc_scroll_up_line, &nav_desc_scroll_down_line, &nav_desc_scroll_up_page, &nav_desc_scroll_down_page, &nav_desc_scroll_up_head, &nav_desc_scroll_down_tail, &nav_desc_mark_toggle_view, NULL, }; /* * Add a field that is the column of the reference table to the table */ static void l_add_field(struct win_fields *win_fields, struct table_col *col, const char *desc) { char char_str[2], select_str[2]; struct table_row *table_row; if (table_col_enabled(col)) sprintf(select_str, "*"); else sprintf(select_str, " "); sprintf(char_str, "%c", table_col_hotkey(col)); table_row = table_row_alloc(win_fields->t); table_row_entry_str_add(table_row, &l_col_select, select_str); table_row_entry_str_add(table_row, &l_col_key, char_str); table_row_entry_str_add(table_row, &l_col_id, table_col_head(col)); table_row_entry_str_add(table_row, &l_col_unit, table_col_unit_str(col)); table_row_entry_str_add(table_row, &l_col_agg, table_col_agg_str(col->agg)); table_row_entry_str_add(table_row, &l_col_desc, desc); table_row_add(win_fields->t, table_row); if (table_col_enabled(col)) table_row_mark_toggle(win_fields->t, table_row); } /* * Fill all field information into table */ static void l_table_create(struct win_fields *win_fields) { unsigned int i; table_row_del_all(win_fields->t); table_row_mark_del_all(win_fields->t); for (i = 0; win_fields->col_vec[i]; i++) { l_add_field(win_fields, win_fields->col_vec[i], win_fields->col_desc_vec[i]); } table_finish(win_fields->t); } /* * Process input for selection with SPACE key */ static void l_process_input_select_space(struct win_fields *win_fields) { char field_key[TABLE_STR_MAX]; if (table_mode_select(win_fields->t)) { table_row_select_key_get(win_fields->t, field_key); table_col_enable_toggle(win_fields->table, field_key[0]); } else { struct table_mark_key *key; /* switch off all fields in reference table */ table_iterate_mark_keys(win_fields->t, key) table_col_enable_toggle(win_fields->table, key->str[0]); } } /* * Process input for selection with hotkey */ static void l_process_input_select_key(struct win_fields *win_fields, int c) { char field_key[TABLE_STR_MAX]; sprintf(field_key, "%c", c); table_row_mark_toggle_by_key(win_fields->t, field_key); table_col_enable_toggle(win_fields->table, field_key[0]); } /* * Process input for unit selection */ static void l_process_input_units(struct win_fields *win_fields, int c) { char field_key[TABLE_STR_MAX]; if (!table_mode_select(win_fields->t)) return; table_row_select_key_get(win_fields->t, field_key); if (c == '+') table_col_unit_next(win_fields->table, field_key[0]); else table_col_unit_prev(win_fields->table, field_key[0]); } /* * Process input and switch window if necessary */ static enum hyptop_win_action l_process_input(struct hyptop_win *win, int c) { struct win_fields *win_fields = (struct win_fields *) win; switch (c) { case 'f': case 'q': return win_back(); case KEY_RETURN: case KEY_ENTER: case 'h': case KEY_LEFT: if (!table_mode_select(win_fields->t)) return win_back(); break; case '?': return win_switch(win_fields->win_help); case ' ': l_process_input_select_space(win_fields); break; case '+': case '-': l_process_input_units(win_fields, c); break; case ERR: return WIN_KEEP; default: l_process_input_select_key(win_fields, c); break; } table_process_input(win_fields->t, c); hyptop_update_term(); return WIN_KEEP; } /* * Event loop: We stay in hyptop_process_input() until fields menu * is left. */ static void l_run(struct hyptop_win *win) { struct win_fields *win_fields = (struct win_fields *) win; table_reset(win_fields->t); while (1) { hyptop_update_term(); if (hyptop_process_input() == WIN_SWITCH) return; } } /* * Create table and print it to screen */ static void l_update_term(struct hyptop_win *win) { struct win_fields *win_fields = (struct win_fields *) win; l_table_create(win_fields); hyptop_printf("Select Fields and Units"); ht_print_help_icon(); hyptop_print_nl(); table_print(win_fields->t); } /* * Create new fields window * * - t...........: Reference table * - col_vec.....: Table column vector for fields * - col_desc_vec: Vector with descriptions for fields */ struct hyptop_win *win_fields_new(struct table *t, struct table_col **col_vec, char **col_desc_vec) { struct win_fields *win_fields; win_fields = ht_zalloc(sizeof(*win_fields)); win_fields->win.process_input = l_process_input; win_fields->win.update_term = l_update_term; win_fields->win.run = l_run; win_fields->win.desc = l_help_str; win_fields->win.desc_normal_vec = l_nav_desc_normal_vec; win_fields->win.desc_select_vec = l_nav_desc_select_vec; win_fields->win.desc_general_vec = l_nav_desc_general_vec; win_fields->win.id = "fields"; win_fields->t = table_new(1, 0, 0, 0); table_col_add(win_fields->t, &l_col_key); table_col_add(win_fields->t, &l_col_select); table_col_add(win_fields->t, &l_col_id); table_col_add(win_fields->t, &l_col_unit); table_col_add(win_fields->t, &l_col_agg); table_col_add(win_fields->t, &l_col_desc); win_fields->col_desc_vec = col_desc_vec; win_fields->col_vec = col_vec; win_fields->table = t; win_fields->win_help = win_help_new((struct hyptop_win *) win_fields); l_table_create(win_fields); return (struct hyptop_win *) win_fields; } s390-tools-2.38.0/hyptop/win_fields.h000066400000000000000000000013461502674226300172720ustar00rootroot00000000000000/* * hyptop - Show hypervisor performance data on System z * * Window "fields": Select fields dialog. * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef WIN_FIELDS_H #define WIN_FIELDS_H #include "hyptop.h" #include "table.h" #include "win_help.h" struct win_fields { struct hyptop_win win; struct table *t; struct table *table; struct table_col **col_vec; char **col_desc_vec; int mode_unit_change; int in_select; struct hyptop_win *win_help; }; struct hyptop_win *win_fields_new(struct table *t, struct table_col **col_vec, char **col_desc_vec); #endif /* WIN_FIELDS_H */ s390-tools-2.38.0/hyptop/win_help.c000066400000000000000000000051161502674226300167460ustar00rootroot00000000000000/* * hyptop - Show hypervisor performance data on System z * * Window "help": Show online help text. * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include "helper.h" #include "hyptop.h" #include "sd.h" #include "table.h" #include "win_help.h" /* * Print help text to screen */ static void l_update_term(struct hyptop_win *win) { struct win_help *win_help = (struct win_help *) win; tbox_print(win_help->tb); } /* * Process input and switch window if necessary */ static enum hyptop_win_action l_process_input(struct hyptop_win *win, int c) { struct win_help *win_help = (struct win_help *) win; switch (c) { case 'h': case KEY_RETURN: case KEY_ENTER: case KEY_LEFT: case '?': case 'q': return win_back(); case 'G': tbox_scroll_down(win_help->tb, TBOX_SCROLL_LAST); break; case 'g': tbox_scroll_up(win_help->tb, TBOX_SCROLL_LAST); break; case KEY_NPAGE: tbox_scroll_down(win_help->tb, TBOX_SCROLL_PAGE); break; case KEY_PPAGE: tbox_scroll_up(win_help->tb, TBOX_SCROLL_PAGE); break; case 'k': case KEY_UP: tbox_scroll_up(win_help->tb, TBOX_SCROLL_LINE); break; case 'j': case KEY_DOWN: tbox_scroll_down(win_help->tb, TBOX_SCROLL_LINE); break; case ERR: return WIN_KEEP; default: break; } hyptop_update_term(); return WIN_KEEP; } /* * Event loop: wait for input and print help text */ static void l_run(struct hyptop_win *win) { (void) win; while (1) { hyptop_update_term(); if (hyptop_process_input() == WIN_SWITCH) return; } } /* * Add text to text box */ static void l_add_text(struct tbox *tb, const char *str) { char *line, *line_end, *str_cpy; str_cpy = line_end = ht_strdup(str); for (line = str_cpy; line_end != NULL; line = line_end + 1) { line_end = strchr(line, '\n'); if (line_end) *line_end = 0; tbox_line_add(tb, line); } ht_free(str_cpy); } /* * Create new help window for "win" and init window description */ struct hyptop_win *win_help_new(struct hyptop_win *win) { struct win_help *win_help; win_help = ht_zalloc(sizeof(*win_help)); win_help->tb = tbox_new(); tbox_printf(win_help->tb, "\\BWindow: %s\\B", win->id); tbox_printf(win_help->tb, " "); l_add_text(win_help->tb, win->desc); nav_desc_add(win_help->tb, win->desc_normal_vec, win->desc_select_vec, win->desc_general_vec); tbox_finish(win_help->tb); win_help->win.process_input = l_process_input; win_help->win.update_term = l_update_term; win_help->win.run = l_run; return (struct hyptop_win *) win_help; } s390-tools-2.38.0/hyptop/win_help.h000066400000000000000000000007771502674226300167630ustar00rootroot00000000000000/* * hyptop - Show hypervisor performance data on System z * * Window "help": Show online help text. * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef WIN_HELP_H #define WIN_HELP_H #include "hyptop.h" #include "tbox.h" struct win_help { struct hyptop_win win; struct tbox *tb; }; struct hyptop_win *win_help_new(struct hyptop_win *win); #endif /* WIN_HELP_H */ s390-tools-2.38.0/hyptop/win_sys.c000066400000000000000000000223241502674226300166340ustar00rootroot00000000000000/* * hyptop - Show hypervisor performance data on System z * * Window "sys": Shows one system in more detail. * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include "helper.h" #include "hyptop.h" #include "opts.h" #include "sd.h" #include "table.h" #include "win_fields.h" #include "win_help.h" /* * Globals for sys_list window */ static char l_sys_id[SD_SYS_ID_SIZE]; /* System to show */ static struct table_col l_cpu_col; /* CPU column */ static struct table_col l_vis_col; /* Visual column */ static struct table *l_t; /* Table */ static int l_initialized; /* Win initialized ? */ static struct hyptop_win *l_win_fields; /* Fields window */ static struct hyptop_win *l_win_help; /* Help window */ /* CPU column */ static struct table_col l_cpu_col = { .type = TABLE_COL_TYPE_U64, .unit = &table_col_unit_cnt, .unit_fam = table_col_unit_fam_cnt, .align = TABLE_COL_ALIGN_LEFT, .agg = TABLE_COL_AGG_NONE, .hotkey = 'i', .head = "cpuid", }; /* Visual column */ static struct table_col l_vis_col = { .type = TABLE_COL_TYPE_U64, .unit = &table_col_unit_vis, .unit_fam = table_col_unit_fam_vis, .align = TABLE_COL_ALIGN_LEFT, .agg = TABLE_COL_AGG_NONE, .hotkey = 'v', .head = "visual", }; /* * Online help text for sys window */ static const char l_help_str[] = "The \"sys\" window displays CPU information about one selected system.\n" "Under z/VM you can only see aggregated CPU information and not information\n" "about single CPUs.\n" "\n" "Select a column by pressing the hotkey of the column. This key is underlined\n" "in the heading. The table is sorted according to the values in the selected\n" "column. If you press the hotkey again, the sort order is reversed.\n" "Alternatively you can select columns with the '<' and '>' keys.\n"; /* * Description of Navigation Keys (used for help window) */ static struct nav_desc *l_nav_desc_normal_vec[] = { &nav_desc_select_mode_enter, &nav_desc_marks_clear, &nav_desc_win_leave_sys, NULL, }; static struct nav_desc *l_nav_desc_select_vec[] = { &nav_desc_select_mode_leave, &nav_desc_mark_toggle, &nav_desc_win_leave_sys_fast, NULL, }; static struct nav_desc *l_nav_desc_general_vec[] = { &nav_desc_win_enter_fields, &nav_desc_win_enter_cpu_types, &nav_desc_col_unit_increase, &nav_desc_col_unit_decrease, &nav_desc_select_col_next, &nav_desc_select_col_prev, &nav_desc_select_col_hotkey, &nav_desc_scroll_up_line, &nav_desc_scroll_down_line, &nav_desc_scroll_up_page, &nav_desc_scroll_down_page, &nav_desc_scroll_up_head, &nav_desc_scroll_down_tail, &nav_desc_mark_toggle_view, NULL, }; /* * Add CPU item to table row */ static void l_cpu_item_add(struct table_row *table_row, struct sd_cpu *cpu, struct sd_cpu_item *item) { switch (sd_cpu_item_type(item)) { case SD_TYPE_U16: case SD_TYPE_U32: assert(0); break; case SD_TYPE_U64: table_row_entry_u64_add(table_row, sd_cpu_item_table_col(item), sd_cpu_item_u64(item, cpu)); break; case SD_TYPE_S64: table_row_entry_s64_add(table_row, sd_cpu_item_table_col(item), sd_cpu_item_s64(item, cpu)); break; case SD_TYPE_STR: table_row_entry_str_add(table_row, sd_cpu_item_table_col(item), sd_cpu_item_str(item, cpu)); break; } } /* * Add visualization of CPU time to table row */ static void l_cpu_add_visual(struct table_row *table_row, struct sd_cpu *cpu) { s64 steal_us; u64 cpu_us; cpu_us = sd_cpu_item_u64(&sd_cpu_item_cpu_diff, cpu); steal_us = sd_cpu_item_s64(&sd_cpu_item_steal_diff, cpu); steal_us = MAX(steal_us, 0); table_row_entry_u64_add_pair(table_row, &l_vis_col, cpu_us, steal_us); } /* * Add CPU to table */ static void l_cpu_add(struct sd_cpu *cpu) { struct table_row *table_row; struct sd_cpu_item *item; unsigned int cpu_id; unsigned int i; table_row = table_row_alloc(l_t); cpu_id = atoi(sd_cpu_id(cpu)); table_row_entry_u64_add(table_row, &l_cpu_col, cpu_id); sd_cpu_item_iterate(item, i) { if (!sd_cpu_item_set(item, cpu)) continue; l_cpu_item_add(table_row, cpu, item); } if (!g.o.format_specified) l_cpu_add_visual(table_row, cpu); table_row_add(l_t, table_row); } /* * Fill system CPU data into table */ static int l_table_create(void) { struct sd_sys *parent; struct sd_cpu *cpu; int i; parent = sd_sys_get(sd_sys_root_get(), l_sys_id); if (!parent) return -ENODEV; table_row_del_all(l_t); sd_cpu_iterate(parent, cpu) { for (i = 0; i < cpu->cnt; i++) l_cpu_add(cpu); } table_finish(l_t); return 0; } /* * Print table to screen */ static void l_table_update_term(struct hyptop_win *win) { (void) win; if (!g.o.format_specified) ht_print_head(l_sys_id); table_print(l_t); } /* * Process input and switch window if necessary */ static enum hyptop_win_action l_process_input(struct hyptop_win *win, int c) { (void) win; switch (c) { case 't': return win_switch(g.win_cpu_types); case '?': return win_switch(l_win_help); case 'f': return win_switch(l_win_fields); case 'q': return win_back(); case 'h': case KEY_LEFT: if (!(table_mode_select(l_t))) return win_back(); break; case ERR: return WIN_KEEP; } table_process_input(l_t, c); hyptop_update_term(); return WIN_KEEP; } /* * Enable field and set unit */ static void l_field_set(struct table_col_spec *col_spec) { table_col_enable_toggle(l_t, col_spec->hotkey); if (!col_spec->unit_str) return; if (table_col_unit_set(l_t, col_spec->hotkey, col_spec->unit_str)) ERR_EXIT("Invalid unit \"%s\" for field \"%c\"\n", col_spec->unit_str, col_spec->hotkey); } /* * Enable field defined in "col_spec" */ static void l_field_enable(struct table_col_spec *col_spec) { struct sd_cpu_item *item; struct table_col *col; unsigned int i; if (table_col_hotkey(&l_vis_col) == col_spec->hotkey) { l_field_set(col_spec); return; } sd_cpu_item_iterate(item, i) { col = sd_cpu_item_table_col(item); if (table_col_hotkey(col) != col_spec->hotkey) continue; l_field_set(col_spec); return; } ERR_EXIT("Unknown field \"%c\"\n", col_spec->hotkey); } /* * Enable fields defined on command line */ static void l_fields_enable_cmdline(void) { unsigned int i; table_col_enable_toggle(l_t, table_col_hotkey(&l_vis_col)); for (i = 0; i < win_sys.opts.fields.cnt; i++) l_field_enable(win_sys.opts.fields.vec[i]); } /* * Enable fields like defined in data gatherer */ static void l_fields_enable_default(void) { struct sd_cpu_item *item; struct table_col *col; unsigned int i; sd_cpu_item_enable_iterate(item, i) { col = sd_cpu_item_table_col(item); table_col_enable_toggle(l_t, table_col_hotkey(col)); } } /* * Event loop: Make regular updates of table */ static void l_run(struct hyptop_win *win) { enum hyptop_win_action action; (void) win; /* Reformat table when entering window */ table_rebuild(l_t); table_fmt_start(); while (1) { if (l_table_create()) { if (g.o.batch_mode_specified) ERR_EXIT("System \"%s\" not available.\n", l_sys_id); win_back(); return; } hyptop_update_term(); action = hyptop_process_input_timeout(); if (action == WIN_SWITCH) return; /* No updates in select mode */ if (!table_mode_select(l_t)) sd_update(); } } /* * Define system for window */ void win_sys_set(const char *sys_id) { if (l_initialized) table_reset(l_t); util_strlcpy(l_sys_id, sys_id, sizeof(l_sys_id)); } /* * Initialize window */ void win_sys_init(void) { struct table_col **col_vec; struct sd_cpu_item *item; struct table_col *col; char **col_desc_vec, *vis_str; unsigned int i, item_cnt; if (sd_dg_has_core_data()) { strcpy(l_cpu_col.head, "coreid"); vis_str = "Visualization of core dispatch time per second"; } else { vis_str = "Visualization of CPU time per second"; } /* Alloc table and add columns */ l_t = table_new(1, 1, 1, 1); table_col_add(l_t, &l_cpu_col); table_col_rsort(&l_cpu_col); item_cnt = sd_cpu_item_cnt() + 2; col_vec = ht_zalloc(sizeof(void *) * item_cnt); col_desc_vec = ht_zalloc(sizeof(void *) * item_cnt); sd_cpu_item_iterate(item, i) { col = sd_cpu_item_table_col(item); table_col_add(l_t, col); table_col_enable_toggle(l_t, table_col_hotkey(col)); col_vec[i] = col; col_desc_vec[i] = item->desc; } if (!g.o.format_specified) { col_vec[i] = &l_vis_col; col_desc_vec[i] = vis_str; table_col_add(l_t, &l_vis_col); } /* Enable fields */ if (win_sys.opts.fields.specified) l_fields_enable_cmdline(); else l_fields_enable_default(); /* Select sort field */ if (win_sys.opts.sort_field_specified) { for (i = 0; i < win_sys.opts.sort_field_specified; i++) { if (table_col_select(l_t, win_sys.opts.sort_field)) ERR_EXIT("Sort field \"%c\" is not available\n", win_sys.opts.sort_field); } } /* Initialize help and fields window */ l_win_fields = win_fields_new(l_t, col_vec, col_desc_vec); l_win_help = win_help_new(&win_sys); l_initialized = 1; } /* * hyptop window structure definition */ struct hyptop_win win_sys = { .process_input = l_process_input, .update_term = l_table_update_term, .run = l_run, .id = "sys", .desc = l_help_str, .desc_normal_vec = l_nav_desc_normal_vec, .desc_select_vec = l_nav_desc_select_vec, .desc_general_vec = l_nav_desc_general_vec, }; s390-tools-2.38.0/hyptop/win_sys_list.c000066400000000000000000000246031502674226300176710ustar00rootroot00000000000000/* * hyptop - Show hypervisor performance data on System z * * Window "sys_list": * Shows a list of systems that the hypervisor is currently running. * * Copyright IBM Corp. 2010, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include "helper.h" #include "hyptop.h" #include "nav_desc.h" #include "opts.h" #include "sd.h" #include "table.h" #include "win_fields.h" #include "win_help.h" /* * Globals for sys_list window */ static struct table *l_t; /* Table */ static struct hyptop_win *l_win_fields; /* Fields Window */ static struct hyptop_win *l_win_help; /* Herp Window */ /* System column */ static struct table_col l_col_sys = TABLE_COL_STR_LEFT('y', "system"); /* * Online help text for sys_list window */ static const char l_help_str[] = "The following windows can be accessed:\n" "\n" " +-----------+ RIGHT +----------+\n" " | | <----------------------> | |\n" " | | LEFT | |\n" " | | | |\n" " | sys_list | 't' +-----------+ | | 't' +-----------+\n" " | | <-------> | cpu_types | | sys | <-------> | cpu_types |\n" " | (start) | 't',LEFT +-----------+ | | 't',LEFT +-----------+\n" " | | | |\n" " | | 'f' +--------+ | | 'f' +--------+\n" " | | <-------> | fields | | | <-------> | fields |\n" " | | 'f',LEFT +--------+ | | 'f',LEFT +--------+\n" " +-----------+ +----------+\n" "\n" " * sys_list: Start window that shows a list of systems that the hypervisor\n" " is currently running.\n" " * sys: Shows one system in more detail.\n" " * cpu_types: Select CPU types that are used for calculating CPU data.\n" " * fields: Select fields and units for windows sys_list or sys.\n" "\n" "\\BNavigation\\B\n" "\n" "To navigate between the windows, use the arrow keys or 'hjkl'. The windows\n" "have two modes, \"normal mode\" and \"select mode\". When you start the " "program,\n" "the window is in normal mode where data is updated at regular intervals. Use\n" "the RIGHT arrow key to enter the select mode. In select mode you can select\n" "rows with with UP and DOWN arrow keys and mark them with the SPACE bar. From\n" "the \"sys_list\" window you can access the \"sys\" window in select mode\n" "with the arrow key RIGHT. Leave the select mode with the arrow key LEFT.\n" "If you are in normal mode, the arrow key LEFT goes to the previous window.\n" "You can scroll all windows using the arrow keys UP, DOWN, PAGEUP and\n" "PAGEDOWN. You can jump to the end of a window with 'G' and to the beginning\n" "with 'g'.\n" "\n" "Select a column by pressing the hotkey of the column. This key is underlined\n" "in the heading. The table is sorted according to the values in the selected\n" "column. If you press the hotkey again, the sort order is reversed.\n" "Alternatively you can select columns with the '<' and '>' keys.\n" "\n" "\\BTable layout\\B\n" "\n" "At the top left of the table the current time is shown. Then the CPU types\n" "with the physical CPU numbers that are used for CPU time calculation are\n" "displayed. The second row shows the units that are currently used for\n" "formatting the data. The last row shows the status display (see description\n" "below) and the aggregation of the the data columns. The last row aggregates\n" "all rows, not only the visible ones. If only the marked rows are shown\n" "(with '.') then only these rows are aggregated.\n" "\n" "\\BStatus display\\B\n" "\n" "At the left bottom of the screen a status display is shown.\n" "Example: \"V:V:N\"\n\n" "The first character shows, if the window can be scrolled:\n" " 'V': Window can be scrolled down\n" " '|': Window can be scrolled up/down\n" " '^': Window can be scrolled up\n" " '=': Window cannot be scrolled\n" "The second character shows the sort order for sorted tables:\n" " 'V': Higher values first\n" " '^': Lower values first\n" "The third character shows the current mode:\n" " 'N': Normal mode\n" " 'S': Select mode\n"; /* * Description of Navigation Keys (used for help window) */ static struct nav_desc *l_nav_desc_normal_vec[] = { &nav_desc_select_mode_enter, &nav_desc_marks_clear, NULL, }; static struct nav_desc *l_nav_desc_select_vec[] = { &nav_desc_select_mode_leave, &nav_desc_win_enter_sys, &nav_desc_mark_toggle, NULL, }; static struct nav_desc *l_nav_desc_general_vec[] = { &nav_desc_win_enter_fields, &nav_desc_win_enter_cpu_types, &nav_desc_col_unit_increase, &nav_desc_col_unit_decrease, &nav_desc_select_col_next, &nav_desc_select_col_prev, &nav_desc_select_col_hotkey, &nav_desc_scroll_up_line, &nav_desc_scroll_down_line, &nav_desc_scroll_up_page, &nav_desc_scroll_down_page, &nav_desc_scroll_up_head, &nav_desc_scroll_down_tail, &nav_desc_mark_toggle_view, &nav_desc_quit, NULL, }; /* * Add system item to table row */ static void l_sys_item_add(struct table_row *table_row, struct sd_sys *sys, struct sd_sys_item *item) { switch (sd_sys_item_type(item)) { case SD_TYPE_U64: case SD_TYPE_U32: case SD_TYPE_U16: table_row_entry_u64_add(table_row, sd_sys_item_table_col(item), sd_sys_item_u64(sys, item)); break; case SD_TYPE_S64: table_row_entry_s64_add(table_row, sd_sys_item_table_col(item), sd_sys_item_s64(sys, item)); break; case SD_TYPE_STR: table_row_entry_str_add(table_row, sd_sys_item_table_col(item), sd_sys_item_str(sys, item)); break; } } /* * Add system to table */ static void l_sys_add(struct sd_sys *sys) { struct table_row *table_row; struct sd_sys_item *item; unsigned int i; table_row = table_row_alloc(l_t); table_row_entry_str_add(table_row, &l_col_sys, sd_sys_id(sys)); sd_sys_item_iterate(item, i) { if (!sd_sys_item_set(sys, item)) continue; l_sys_item_add(table_row, sys, item); } table_row_add(l_t, table_row); } /* * Fill system data into table */ static void l_table_create(void) { struct sd_sys *parent, *guest; table_row_del_all(l_t); parent = sd_sys_root_get(); sd_sys_iterate(parent, guest) { if (!opts_sys_specified(&win_sys_list, sd_sys_id(guest))) continue; l_sys_add(guest); } table_finish(l_t); } /* * Print table to screen */ static void l_table_update_term(struct hyptop_win *win) { (void) win; if (!g.o.format_specified) ht_print_head(NULL); table_print(l_t); } /* * Process input and switch window if necessary */ static enum hyptop_win_action l_process_input(struct hyptop_win *win, int c) { char selected_sys[TABLE_STR_MAX]; (void) win; switch (c) { case 'f': return win_switch(l_win_fields); case 't': return win_switch(g.win_cpu_types); case '?': return win_switch(l_win_help); case 'q': hyptop_exit(0); case 'l': case KEY_RIGHT: if (!table_mode_select(l_t)) break; table_row_select_key_get(l_t, selected_sys); win_sys_set(selected_sys); return win_switch(&win_sys); case ERR: break; } table_process_input(l_t, c); hyptop_update_term(); return WIN_KEEP; } /* * Enable field and set unit */ static void l_field_set(struct table_col_spec *col_spec) { table_col_enable_toggle(l_t, col_spec->hotkey); if (!col_spec->unit_str) return; if (table_col_unit_set(l_t, col_spec->hotkey, col_spec->unit_str)) ERR_EXIT("Invalid unit \"%s\" for field \"%c\"\n", col_spec->unit_str, col_spec->hotkey); } /* * Enable field defined in "col_spec" */ static void l_field_enable(struct table_col_spec *col_spec) { struct sd_sys_item *item; struct table_col *col; unsigned int i; sd_sys_item_iterate(item, i) { col = sd_sys_item_table_col(item); if (table_col_hotkey(col) != col_spec->hotkey) continue; l_field_set(col_spec); return; } ERR_EXIT("Unknown field \"%c\"\n", col_spec->hotkey); } /* * Enable fields defined on command line */ static void l_fields_enable_cmdline(void) { unsigned int i; for (i = 0; i < win_sys_list.opts.fields.cnt; i++) l_field_enable(win_sys_list.opts.fields.vec[i]); } /* * Enable fields like defined in data gatherer */ static void l_fields_enable_default(void) { struct sd_sys_item *item; struct table_col *col; unsigned int i; sd_sys_item_enable_iterate(item, i) { col = sd_sys_item_table_col(item); table_col_enable_toggle(l_t, table_col_hotkey(col)); } } /* * Event loop: Make regular updates of table */ static void l_run(struct hyptop_win *win) { enum hyptop_win_action action; (void) win; /* Reformat table when entering window */ table_rebuild(l_t); table_fmt_start(); while (1) { l_table_create(); hyptop_update_term(); action = hyptop_process_input_timeout(); if (action == WIN_SWITCH) return; /* No updates in select mode */ if (!table_mode_select(l_t)) sd_update(); } } /* * Initialize window */ void win_sys_list_init(void) { struct table_col **col_vec; struct sd_sys_item *item; struct table_col *col; char **col_desc_vec; unsigned int i; int item_cnt; /* Alloc table and add columns */ l_t = table_new(1, 1, 1, 1); table_col_add(l_t, &l_col_sys); item_cnt = sd_sys_item_cnt() + 1; col_vec = ht_zalloc(sizeof(void *) * item_cnt); col_desc_vec = ht_zalloc(sizeof(void *) * item_cnt); sd_sys_item_iterate(item, i) { col = sd_sys_item_table_col(item); table_col_add(l_t, col); table_col_enable_toggle(l_t, table_col_hotkey(col)); col_vec[i] = col; col_desc_vec[i] = item->desc; } /* Enable fields */ if (win_sys_list.opts.fields.specified) l_fields_enable_cmdline(); else l_fields_enable_default(); /* Select sort field */ if (win_sys_list.opts.sort_field_specified) { for (i = 0; i < win_sys_list.opts.sort_field_specified; i++) { if (table_col_select(l_t, win_sys_list.opts.sort_field)) ERR_EXIT("Sort field \"%c\" is not available\n", win_sys_list.opts.sort_field); } } else { table_col_select(l_t, sd_sys_item_cpu_diff.table_col.hotkey); } /* Initialize help and fields window */ l_win_help = win_help_new(&win_sys_list); l_win_fields = win_fields_new(l_t, col_vec, col_desc_vec); } /* * hyptop window structure definition */ struct hyptop_win win_sys_list = { .process_input = l_process_input, .update_term = l_table_update_term, .run = l_run, .id = "sys_list", .desc = l_help_str, .desc_normal_vec = l_nav_desc_normal_vec, .desc_select_vec = l_nav_desc_select_vec, .desc_general_vec = l_nav_desc_general_vec, }; s390-tools-2.38.0/include/000077500000000000000000000000001502674226300150725ustar00rootroot00000000000000s390-tools-2.38.0/include/boot/000077500000000000000000000000001502674226300160355ustar00rootroot00000000000000s390-tools-2.38.0/include/boot/boot_defs.h000066400000000000000000000111071502674226300201520ustar00rootroot00000000000000/* * Boot and dump related definitions * * Copyright IBM Corp. 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef BOOT_DEFS_H #define BOOT_DEFS_H #include #include "lib/zt_common.h" #define ZIPL_MAGIC "zIPL" #define ZIPL_MAGIC_SIZE 4 /* * ECKD dump parameter */ struct eckd_dump_param { uint32_t blk_start; uint32_t blk_end; uint16_t blk_size; uint8_t num_heads; uint8_t bpt; char reserved[4]; } __packed; /* * FBA dump parameter */ struct fba_dump_param { uint32_t res1; uint32_t blk_start; uint32_t res2; uint32_t blk_end; } __packed; /* * SCSI dump parameter */ struct scsi_dump_param { uint64_t block; uint64_t reserved; } __packed; /* * Layout of block pointer for linear devices * e.g. SCSI */ struct linear_blockptr { uint64_t blockno; uint16_t size; uint16_t blockct; uint8_t reserved[4]; } __packed; /* * Format of a boot record on ECKD DASD for List-Directed IPL */ struct eckd_boot_record { uint8_t magic[4]; uint32_t version_id; uint8_t unused[8]; uint8_t program_table_pointer[16]; uint8_t reserved[478]; uint16_t os_id; } __packed; /* * Layout of block pointer for cylinder/head/sector devices * e.g. ECKD */ enum blkptr_format_id { /* * this is the old format which serves only CCW-type IPL, * and doesn't fit List-Directed IPL. Still supported for * compatibility reasons. */ LEGACY_BLKPTR_FORMAT_ID, /* * this is the "new" format which serves only List-Directed IPL, * but is also suitable for CCW-type IPL. */ BLKPTR_FORMAT_ID, NR_BLKPTR_FORMATS }; /* * Block pointers format identified as LEGACY_BLKPTR_FORMAT_ID. */ struct eckd_blockptr_legacy { uint16_t cyl; uint16_t head; uint8_t sec; uint16_t size; uint8_t blockct; uint8_t reserved[8]; } __packed; /* * Block pointers format identified as BLKPTR_FORMAT_ID. */ struct eckd_blockptr { uint32_t cyl; uint8_t head; uint8_t sec; uint8_t reserved1[4]; uint16_t blockct; uint8_t reserved2[4]; } __packed; typedef enum { COMPONENT_TYPE_EXECUTE = 0x01, COMPONENT_TYPE_LOAD = 0x02, COMPONENT_TYPE_SIGNATURE = 0x03 } component_type; typedef enum { COMPONENT_HEADER_IPL = 0x00, COMPONENT_HEADER_DUMP = 0x01 } component_header_type; struct component_header { uint8_t magic[4]; uint8_t type; uint8_t reserved[27]; } __packed; struct signature_header { uint8_t format; uint8_t reserved[3]; uint32_t length; } __packed; typedef union { uint64_t load_address; uint64_t load_psw; struct signature_header sig_head; } component_data; struct component_entry { uint8_t data[23]; uint8_t type; component_data compdat; } __packed; /* SCSI dump super block */ struct scsi_dump_sb { uint64_t magic; uint64_t version; uint64_t part_start; uint64_t part_size; uint64_t dump_offset; uint64_t dump_size; uint64_t csum_offset; uint64_t csum_size; uint64_t csum; }; STATIC_ASSERT(sizeof(struct scsi_dump_sb) == 72); #define SCSI_DUMP_SB_MAGIC 0x5a46435044554d50ULL /* ZFCPDUMP */ /* To avoid a csum entry of 0 a seed is used */ #define SCSI_DUMP_SB_SEED 0x12345678 #define SCSI_DUMP_SB_CSUM_SIZE 4096 /* Boot info */ #define BOOT_INFO_VERSION 1 #define BOOT_INFO_MAGIC "zIPL" #define BOOT_INFO_DEV_TYPE_ECKD 0x00 #define BOOT_INFO_DEV_TYPE_FBA 0x01 #define BOOT_INFO_DEV_TYPE_SCSI 0x02 #define BOOT_INFO_BP_TYPE_IPL 0x00 #define BOOT_INFO_BP_TYPE_DUMP 0x01 #ifdef __s390x__ #define BOOT_INFO_FLAGS_ARCH 0x01 #else #define BOOT_INFO_FLAGS_ARCH 0x00 #endif struct boot_info_bp_dump { union { struct eckd_dump_param eckd; struct fba_dump_param fba; struct scsi_dump_param scsi; } param; uint8_t unused[16]; } __packed; /* This represents on-disk pointer to a block on disk */ union disk_blockptr { struct eckd_blockptr_legacy eckd_legacy; struct eckd_blockptr eckd; struct linear_blockptr linear; }; struct boot_info_bp_ipl { union disk_blockptr bm_ptr; uint8_t unused[16]; } __packed; struct disk_program_table { uint32_t magic; uint32_t version; uint64_t unused; union disk_blockptr component_table[0]; } __packed; struct boot_info { char magic[4]; uint8_t version; uint8_t bp_type; uint8_t dev_type; uint8_t flags; union { struct boot_info_bp_dump dump; struct boot_info_bp_ipl ipl; } bp; } __packed; #define DISK_LAYOUT_ID 0x00000001 struct scsi_mbr { uint8_t magic[4]; uint32_t version_id; uint8_t reserved[8]; struct linear_blockptr program_table_pointer; uint8_t reserved2[0x50]; struct boot_info boot_info; } __packed; #endif /* BOOT_DEFS_H */ s390-tools-2.38.0/include/boot/error.h000066400000000000000000000044501502674226300173420ustar00rootroot00000000000000/* * zipl boot loader error codes * * Copyright IBM Corp. 2012, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef ERROR_H #define ERROR_H /************************************************************************ * zipl boot loader error codes (use decimal range 4500-4549) * * The error numbers are ABI. Do not change them! ************************************************************************/ /* I/O error: Enabling the IPL device failed */ #define EENABLE_DEV 0x00004500 /* I/O error: Disabling the IPL device failed */ #define EDISABLE_DEV 0x00004501 /* I/O error: The start subchannel command failed */ #define ESSCH 0x00004502 /* Internal error: The IPL type is incorrect */ #define EWRONGTYPE 0x00004510 /* Internal error */ #define EINTERNAL 0x00004511 /* Secure IPL error */ #define ESECUREBOOT 0x00004512 /* os_info error: No operating system information was found */ #define EOS_INFO_MISSING 0x00004520 /* os_info error: The checksum of the operating system information is incorrect */ #define EOS_INFO_CSUM_FAILED 0x00004521 /* kdump: The major version of the operating system information is too high */ #define EOS_INFO_VERSION 0x00004522 /* kdump: No crashkernel memory was defined */ #define EOS_INFO_NOCRASHKERNEL 0x00004523 /* kdump: HSA copy failed */ #define EHSA_COPY_FAILED 0x00004524 /************************************************************************ * The following codes are not ABI ************************************************************************/ #define ETIMEOUT 2 /* * Stand-alone dump */ #define OK 0x00000000 /* Dump completed successfully */ #define EMEM 0x00004600 /* Device too small for dump */ #define EDEVNOTSUP 0x00004601 /* Device not supported */ #define EMEMCOUNT 0x00004602 /* Could not evaluate memory layout */ /* * Multi-volume dump */ #define ENODEVNO 0x00004603 /* The devno does not exist */ #define ENOSIGN 0x00004604 /* No valid dump signature on device */ #define ENOTIME 0x00004605 /* The zipl time stamps do not match */ #define ENOMSS 0x00004606 /* Could not enable MSS */ /* * PV error codes */ #define ENOPV 0x00004607 /* No support for PV */ #define EPV 0x00004608 /* PV error */ #endif /* ERROR_H */ s390-tools-2.38.0/include/boot/ipl.h000066400000000000000000000076101502674226300167760ustar00rootroot00000000000000/* * IPL related definitions * * Copyright IBM Corp. 2020 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef IPL_H #define IPL_H #include "lib/zt_common.h" #include "page.h" #define IPL_FLAG_SECURE 0x40 #define IPL_RB_COMPONENT_FLAG_SIGNED 0x80 #define IPL_RB_COMPONENT_FLAG_VERIFIED 0x40 #define IPL_MAX_SUPPORTED_VERSION 0 #define IPL_PARM_BLOCK_VERSION 0x1 #ifndef __ASSEMBLER__ #include /* IPL Parameter List header */ struct ipl_pl_hdr { uint32_t len; uint8_t flags; uint8_t reserved1[2]; uint8_t version; } __packed; /* IPL Parameter Block header */ struct ipl_pb_hdr { uint32_t len; uint8_t pbt; } __packed; /* IPL Parameter Block types */ enum ipl_pbt { IPL_PBT_FCP = 0, IPL_PBT_SCP_DATA = 1, IPL_PBT_CCW = 2, IPL_PBT_ECKD = 3, IPL_PBT_NVME = 4, IPL_PBT_PV = 5, }; /* IPL Parameter Block 0 with common fields */ struct ipl_pb0_common { uint32_t len; uint8_t pbt; uint8_t flags; uint8_t reserved1[2]; uint8_t loadparm[8]; uint8_t reserved2[84]; } __packed; /* IPL Parameter Block 0 for FCP */ struct ipl_pb0_fcp { uint32_t len; uint8_t pbt; uint8_t reserved1[3]; uint8_t loadparm[8]; uint8_t reserved2[304]; uint8_t opt; uint8_t reserved3[3]; uint8_t cssid; uint8_t reserved4[1]; uint8_t devno; uint8_t reserved5[4]; uint64_t wwpn; uint64_t lun; uint32_t bootprog; uint8_t reserved6[12]; uint64_t br_lba; uint32_t scp_data_len; uint8_t reserved7[260]; uint8_t scp_data[]; } __packed; /* IPL Parameter Block 0 for CCW */ struct ipl_pb0_ccw { uint32_t len; uint8_t pbt; uint8_t flags; uint8_t reserved1[2]; uint8_t loadparm[8]; uint8_t reserved2[84]; uint16_t reserved3 : 13; uint8_t ssid : 3; uint16_t devno; uint8_t vm_flags; uint8_t reserved4[3]; uint32_t vm_parm_len; uint8_t nss_name[8]; uint8_t vm_parm[64]; uint8_t reserved5[8]; } __packed; /* Structure must not have any padding */ struct ipl_pb0_pv_comp { uint64_t tweak_pref; uint64_t addr; uint64_t len; }; STATIC_ASSERT(sizeof(struct ipl_pb0_pv_comp) == 3 * 8) /* IPL Parameter Block 0 for PV */ struct ipl_pb0_pv { uint32_t len; uint8_t pbt; uint8_t reserved1[3]; uint8_t loadparm[8]; uint8_t reserved2[84]; uint8_t reserved3[3]; uint8_t version; uint8_t reserved4[4]; uint32_t num_comp; uint64_t pv_hdr_addr; uint64_t pv_hdr_size; struct ipl_pb0_pv_comp components[]; } __packed; struct ipl_parameter_block { struct ipl_pl_hdr hdr; union { struct ipl_pb_hdr pb0_hdr; struct ipl_pb0_common common; struct ipl_pb0_fcp fcp; struct ipl_pb0_ccw ccw; struct ipl_pb0_pv pv; char raw[PAGE_SIZE - sizeof(struct ipl_pl_hdr)]; }; } __packed; /* IPL Report List header */ struct ipl_rl_hdr { uint32_t len; uint8_t flags; uint8_t reserved1[2]; uint8_t version; uint8_t reserved2[8]; } __packed; /* IPL Report Block header */ /* Structure must not have any padding */ struct ipl_rb_hdr { uint32_t len; uint8_t rbt; uint8_t reserved1[11]; }; STATIC_ASSERT(sizeof(struct ipl_rb_hdr) == 4 + 1 + 11) /* IPL Report Block types */ enum ipl_rbt { IPL_RBT_CERTIFICATES = 1, IPL_RBT_COMPONENTS = 2, }; /* IPL Report Block for the certificate list */ struct ipl_rb_certificate_entry { uint64_t addr; uint64_t len; } __packed; struct ipl_rb_certificates { uint32_t len; uint8_t rbt; uint8_t reserved1[11]; struct ipl_rb_certificate_entry entries[]; } __packed; /* IPL Report Block for the component list */ struct ipl_rb_component_entry { uint64_t addr; uint64_t len; uint8_t flags; uint8_t reserved1[5]; uint16_t certificate_index; uint8_t reserved2[8]; }; /* Structure must not have any padding */ struct ipl_rb_components { uint32_t len; uint8_t rbt; uint8_t reserved1[11]; struct ipl_rb_component_entry entries[]; }; STATIC_ASSERT(sizeof(struct ipl_rb_components) == 4 + 1 + 11) #endif /* __ASSEMBLER__ */ #endif /* IPL_H */ s390-tools-2.38.0/include/boot/linux_layout.h000066400000000000000000000015571502674226300207520ustar00rootroot00000000000000/* * s390 Linux layout definitions * * Copyright IBM Corp. 2020 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef LINUX_LAYOUT_H #define LINUX_LAYOUT_H #include "lib/zt_common.h" /* Entry address offsets */ #define IMAGE_ENTRY _AC(0x10000, UL) #define IMAGE_ENTRY_KDUMP _AC(0x10010, UL) /* Parameter address offsets */ #define PARMAREA _AC(0x10400, UL) #define IPL_DEVICE _AC(0x10400, UL) #define INITRD_START _AC(0x10408, UL) #define INITRD_SIZE _AC(0x10410, UL) #define OLDMEM_BASE _AC(0x10418, UL) #define OLDMEM_SIZE _AC(0x10420, UL) #define MAX_COMMAND_LINE_SIZE _AC(0x10430, UL) #define COMMAND_LINE _AC(0x10480, UL) /* Parameter sizes */ #define LEGACY_COMMAND_LINE_SIZE 896 #ifndef __ASSEMBLER__ #endif /* __ASSEMBLER__ */ #endif /* LINUX_LAYOUT_H */ s390-tools-2.38.0/include/boot/loaders_layout.h000066400000000000000000000033331502674226300212360ustar00rootroot00000000000000/* * zipl stage2/stage3 layout definitions * * Copyright IBM Corp. 2020 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. * */ #ifndef LOADERS_LAYOUT_H #define LOADERS_LAYOUT_H #include "lib/zt_common.h" #include "linux_layout.h" #define STAGE0_LOAD_ADDRESS _AC(0x0, UL) #define STAGE1_LOAD_ADDRESS _AC(0x18, UL) #define STAGE1B_LOAD_ADDRESS _AC(0xe000, UL) #define STAGE2_DESC _AC(0x78, UL) #define STAGE2_ENTRY _AC(0x2018, UL) #define ECKD2DUMP_MV_TAIL_ADDRESS _AC(0x6000, UL) #define STAGE2_HEAP_ADDRESS _AC(0x6000, UL) #define ECKD2DUMP_HEAP_ADDRESS _AC(0xb000, UL) #define STAGE2_HEAP_SIZE _AC(0x3000, UL) #define STAGE2_STACK_ADDRESS _AC(0xe400, UL) #define STAGE2_STACK_SIZE _AC(0x1c00, UL) #define ECKD2DUMP_STACK_ADDRESS _AC(0xe000, UL) #define ECKD2DUMP_STACK_SIZE _AC(0x2000, UL) #define STAGE2_MAX_SIZE _AC(0x3000, UL) #define STAGE2_DUMPER_SIZE_SV _AC(0x3000, UL) #define STAGE2_DUMPER_SIZE_MV _AC(0x4000, UL) #define STAGE2_DUMPER_SIZE_SV_ZLIB _AC(0x8000, UL) #define STAGE3_ENTRY _AC(0xa000, UL) #define STAGE2_LOAD_ADDRESS _AC(0x2000, UL) #define STAGE3_LOAD_ADDRESS STAGE3_ENTRY #define IMAGE_LOAD_ADDRESS IMAGE_ENTRY #define STAGE3_MAXIMUM_SIZE _AC(0x3000, UL) #define STAGE3_HEAP_SIZE _AC(0x4000, UL) #define STAGE3_HEAP_ADDRESS _AC(0x2000, UL) #define STAGE3_STACK_SIZE _AC(0x1000, UL) #define STAGE3_STACK_ADDRESS _AC(0xF000, UL) #define STAGE3_PARAMS_ADDRESS _AC(0x9000, UL) #define STAGE3_PARAMS_MAXIMUM_SIZE _AC(0x1000, UL) #define COMMAND_LINE_EXTRA _AC(0xE000, UL) #define COMMAND_LINE_EXTRA_SIZE _AC(0x0400, UL) #ifndef __ASSEMBLER__ #endif /* __ASSEMBLER__ */ #endif /* LOADERS_LAYOUT_H */ s390-tools-2.38.0/include/boot/os_info.h000066400000000000000000000046671502674226300176570ustar00rootroot00000000000000/* * zipl - zSeries Initial Program Loader tool * * os-info definitions * * Copyright IBM Corp. 2013, 2023 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef OS_INFO_H #define OS_INFO_H #include "lib/zt_common.h" #include "boot/error.h" #include "boot/s390.h" #include #define OS_INFO_MAGIC 0x4f53494e464f535aULL /* OSINFOSZ */ #define OS_INFO_CSUM_SIZE (sizeof(struct os_info) - offsetof(struct os_info, version_major)) #define OS_INFO_FLAGS_ENTRY_SIZE (sizeof(unsigned long)) #define OS_INFO_VMCOREINFO 0 #define OS_INFO_REIPL_BLOCK 1 #define OS_INFO_FLAGS_ENTRY 2 #define OS_INFO_RESERVED 3 #define OS_INFO_IDENTITY_BASE 4 #define OS_INFO_KASLR_OFFSET 5 #define OS_INFO_KASLR_OFF_PHYS 6 #define OS_INFO_VMEMMAP 7 #define OS_INFO_AMODE31_START 8 #define OS_INFO_AMODE31_END 9 #define OS_INFO_IMAGE_START 10 #define OS_INFO_IMAGE_END 11 #define OS_INFO_IMAGE_PHYS 12 #define OS_INFO_MAX 13 #define OS_INFO_FLAG_REIPL_CLEAR (1UL << 0) struct os_info_entry { union { uint64_t addr; uint64_t val; }; uint64_t size; uint32_t csum; } __packed; struct os_info { uint64_t magic; uint32_t csum; uint16_t version_major; uint16_t version_minor; uint64_t crashkernel_addr; uint64_t crashkernel_size; struct os_info_entry entry[OS_INFO_MAX]; uint8_t reserved[3804]; } __packed; STATIC_ASSERT(sizeof(struct os_info) == 4096) /* * Return 0 in case of valid os_info * Return -EOS_INFO_MISSING if os_info address is not page aligned or page is * not accessible or os_info magic value is missing. * Return -EOS_INFO_CSUM_FAILED if os_info checksum is invalid. */ static inline int os_info_check(const struct os_info *os_info) { if (!os_info || (unsigned long)os_info % PAGE_SIZE || !page_is_valid((unsigned long)os_info) || os_info->magic != OS_INFO_MAGIC) return -EOS_INFO_MISSING; if (os_info->csum != csum_partial(&os_info->version_major, OS_INFO_CSUM_SIZE, 0)) return -EOS_INFO_CSUM_FAILED; return 0; } /* * Return 1 in case of valid os_info_entry, otherwise 0 * Make sure that the entire os_info structure is checked first with os_info_check(). */ static inline int os_info_entry_is_valid(const struct os_info_entry *entry) { return (entry && entry->addr && entry->size && page_is_valid(entry->addr) && entry->csum == csum_partial((void *)entry->addr, entry->size, 0)); } #endif /* OS_INFO_H */ s390-tools-2.38.0/include/boot/page.h000066400000000000000000000005121502674226300171200ustar00rootroot00000000000000/* * Page related definitions and functions. * * Copyright IBM Corp. 2023 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef BOOT_PAGE_H #define BOOT_PAGE_H #include "lib/zt_common.h" #define PAGE_SIZE _AC(4096, UL) #endif s390-tools-2.38.0/include/boot/psw.h000066400000000000000000000013371502674226300170230ustar00rootroot00000000000000/* * Program Status Word related definitions and functions. * * Copyright IBM Corp. 2023 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef BOOT_PSW_H #define BOOT_PSW_H #include "lib/zt_common.h" #define PSW32_ADDR_MASK _AC(0x000000007fffffff, UL) #define PSW_MASK_BA _AC(0x0000000080000000, UL) #define PSW_MASK_EA _AC(0x0000000100000000, UL) #define PSW_MASK_BIT_12 _AC(0x0008000000000000, UL) #define PSW_LOAD _AC(0x0008000080000000, UL) #define PSW_DISABLED_WAIT _AC(0x000a000000000000, UL) #ifndef __ASSEMBLER__ #include struct psw_t { uint64_t mask; uint64_t addr; } __aligned(8); #endif #endif s390-tools-2.38.0/include/boot/s390.h000066400000000000000000000312031502674226300167030ustar00rootroot00000000000000/* * s390 related definitions and functions. * Should only be used for code targeting s390 (bootloader code) * * Copyright IBM Corp. 2013, 2023 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef S390_H #define S390_H #include "lib/zt_common.h" #include "boot/sigp.h" #include "boot/psw.h" #include "boot/page.h" #define __LC_IPLDEV 0x0c6c #define __LC_OS_INFO 0x0e18 #define LOWCORE_SIZE _AC(0x2000, UL) /* Minimum size of a stack frame in bytes */ #define STACK_FRAME_OVERHEAD _AC(160, U) /* Facilities */ #define DFLTCC_FACILITY _AC(151, U) #define UNPACK_FACILITY _AC(161, U) #ifndef __ASSEMBLER__ #include /* * Helper macro for exception table entries */ #define EX_TABLE(_fault, _target) \ ".section .ex_table,\"a\"\n" \ ".align 4\n" \ ".long (" #_fault ")\n" \ ".long (" #_target ")\n" \ ".previous\n" struct subchannel_id { uint32_t cssid:8; uint32_t:4; uint32_t m:1; uint32_t ssid:2; uint32_t one:1; uint32_t sch_no:16; } __packed __aligned(4); struct tpi_info { struct subchannel_id schid; uint32_t intparm; uint32_t adapter_IO:1; uint32_t directed_irq:1; uint32_t isc:3; uint32_t:12; uint32_t type:3; uint32_t:12; } __packed __aligned(4); struct _lowcore { uint8_t pad_0x0000[0x0014-0x0000]; /* 0x0000 */ uint32_t ipl_parmblock_ptr; /* 0x0014 */ uint8_t pad_0x0018[0x0080-0x0018]; /* 0x0018 */ uint32_t ext_params; /* 0x0080 */ uint16_t ext_cpu_addr; /* 0x0084 */ uint16_t ext_int_code; /* 0x0086 */ uint16_t svc_ilc; /* 0x0088 */ uint16_t svc_code; /* 0x008a */ uint16_t pgm_ilc; /* 0x008c */ uint16_t pgm_code; /* 0x008e */ uint32_t data_exc_code; /* 0x0090 */ uint16_t mon_class_num; /* 0x0094 */ uint16_t per_perc_atmid; /* 0x0096 */ uint64_t per_address; /* 0x0098 */ uint8_t exc_access_id; /* 0x00a0 */ uint8_t per_access_id; /* 0x00a1 */ uint8_t op_access_id; /* 0x00a2 */ uint8_t ar_access_id; /* 0x00a3 */ uint8_t pad_0x00a4[0x00a8-0x00a4]; /* 0x00a4 */ uint64_t trans_exc_code; /* 0x00a8 */ uint64_t monitor_code; /* 0x00b0 */ union { struct { uint16_t subchannel_id; /* 0x00b8 */ uint16_t subchannel_nr; /* 0x00ba */ uint32_t io_int_parm; /* 0x00bc */ uint32_t io_int_word; /* 0x00c0 */ }; struct tpi_info tpi_info; /* 0x00b8 */ }; uint8_t pad_0x00c4[0x00c8-0x00c4]; /* 0x00c4 */ uint32_t stfl_fac_list; /* 0x00c8 */ uint8_t pad_0x00cc[0x00e8-0x00cc]; /* 0x00cc */ uint32_t mcck_interruption_code[2]; /* 0x00e8 */ uint8_t pad_0x00f0[0x00f4-0x00f0]; /* 0x00f0 */ uint32_t external_damage_code; /* 0x00f4 */ uint64_t failing_storage_address; /* 0x00f8 */ uint8_t pad_0x0100[0x0110-0x0100]; /* 0x0100 */ uint64_t breaking_event_addr; /* 0x0110 */ uint8_t pad_0x0118[0x0120-0x0118]; /* 0x0118 */ struct psw_t restart_old_psw; /* 0x0120 */ struct psw_t external_old_psw; /* 0x0130 */ struct psw_t svc_old_psw; /* 0x0140 */ struct psw_t program_old_psw; /* 0x0150 */ struct psw_t mcck_old_psw; /* 0x0160 */ struct psw_t io_old_psw; /* 0x0170 */ uint8_t pad_0x0180[0x01a0-0x0180]; /* 0x0180 */ struct psw_t restart_psw; /* 0x01a0 */ struct psw_t external_new_psw; /* 0x01b0 */ struct psw_t svc_new_psw; /* 0x01c0 */ struct psw_t program_new_psw; /* 0x01d0 */ struct psw_t mcck_new_psw; /* 0x01e0 */ struct psw_t io_new_psw; /* 0x01f0 */ /* Save areas. */ uint64_t save_area_sync[8]; /* 0x0200 */ uint64_t save_area_async[8]; /* 0x0240 */ uint64_t save_area_restart[1]; /* 0x0280 */ uint8_t pad_0x0288[0x0290-0x0288]; /* 0x0288 */ /* Return psws. */ struct psw_t return_psw; /* 0x0290 */ struct psw_t return_mcck_psw; /* 0x02a0 */ /* CPU accounting and timing values. */ uint64_t sync_enter_timer; /* 0x02b0 */ uint64_t async_enter_timer; /* 0x02b8 */ uint64_t mcck_enter_timer; /* 0x02c0 */ uint64_t exit_timer; /* 0x02c8 */ uint64_t user_timer; /* 0x02d0 */ uint64_t system_timer; /* 0x02d8 */ uint64_t steal_timer; /* 0x02e0 */ uint64_t last_update_timer; /* 0x02e8 */ uint64_t last_update_clock; /* 0x02f0 */ uint64_t int_clock; /* 0x02f8 */ uint64_t mcck_clock; /* 0x0300 */ uint64_t clock_comparator; /* 0x0308 */ /* Current process. */ uint64_t current_task; /* 0x0310 */ uint64_t thread_info; /* 0x0318 */ uint64_t kernel_stack; /* 0x0320 */ /* Interrupt, panic and restart stack. */ uint64_t async_stack; /* 0x0328 */ uint64_t panic_stack; /* 0x0330 */ uint64_t restart_stack; /* 0x0338 */ /* Restart function and parameter. */ uint64_t restart_fn; /* 0x0340 */ uint64_t restart_data; /* 0x0348 */ uint64_t restart_source; /* 0x0350 */ /* Address space pointer. */ uint64_t kernel_asce; /* 0x0358 */ uint64_t user_asce; /* 0x0360 */ uint64_t current_pid; /* 0x0368 */ /* SMP info area */ uint32_t cpu_nr; /* 0x0370 */ uint32_t softirq_pending; /* 0x0374 */ uint64_t percpu_offset; /* 0x0378 */ uint64_t vdso_per_cpu_data; /* 0x0380 */ uint64_t machine_flags; /* 0x0388 */ uint64_t ftrace_func; /* 0x0390 */ uint64_t gmap; /* 0x0398 */ uint8_t pad_0x03a0[0x0400-0x03a0]; /* 0x03a0 */ /* Interrupt response block. */ uint8_t irb[64]; /* 0x0400 */ /* Per cpu primary space access list */ uint32_t paste[16]; /* 0x0440 */ uint8_t pad_0x0480[0x0e00-0x0480]; /* 0x0480 */ /* * 0xe00 contains the address of the IPL Parameter Information * block. Dump tools need IPIB for IPL after dump. * Note: do not change the position of any fields in 0x0e00-0x0f00 */ uint64_t ipib; /* 0x0e00 */ uint32_t ipib_checksum; /* 0x0e08 */ uint64_t vmcore_info; /* 0x0e0c */ uint8_t pad_0x0e14[0x0e18-0x0e14]; /* 0x0e14 */ uint64_t os_info; /* 0x0e18 */ uint8_t pad_0x0e20[0x0f00-0x0e20]; /* 0x0e20 */ /* Extended facility list */ uint64_t stfle_fac_list[32]; /* 0x0f00 */ uint8_t pad_0x1000[0x11b0-0x1000]; /* 0x1000 */ uint64_t vector_save_area_addr; /* 0x11b0 */ /* 64 bit extparam used for pfault/diag 250: defined by architecture */ uint64_t ext_params2; /* 0x11B8 */ uint8_t pad_0x11c0[0x1200-0x11C0]; /* 0x11C0 */ /* CPU register save area: defined by architecture */ uint64_t floating_pt_save_area[16]; /* 0x1200 */ uint64_t gpregs_save_area[16]; /* 0x1280 */ struct psw_t psw_save_area; /* 0x1300 */ uint8_t pad_0x1310[0x1318-0x1310]; /* 0x1310 */ uint32_t prefixreg_save_area; /* 0x1318 */ uint32_t fpt_creg_save_area; /* 0x131c */ uint8_t pad_0x1320[0x1324-0x1320]; /* 0x1320 */ uint32_t tod_progreg_save_area; /* 0x1324 */ uint32_t cpu_timer_save_area[2]; /* 0x1328 */ uint32_t clock_comp_save_area[2]; /* 0x1330 */ uint8_t pad_0x1338[0x1340-0x1338]; /* 0x1338 */ uint32_t access_regs_save_area[16]; /* 0x1340 */ uint64_t cregs_save_area[16]; /* 0x1380 */ uint8_t pad_0x1400[0x1800-0x1400]; /* 0x1400 */ /* Transaction abort diagnostic block */ uint8_t pgm_tdb[256]; /* 0x1800 */ /* align to the top of the prefix area */ uint8_t pad_0x1900[0x2000-0x1900]; /* 0x1900 */ } __packed __aligned(8192); STATIC_ASSERT(sizeof(struct _lowcore) == LOWCORE_SIZE) #define S390_lowcore (*((struct _lowcore *) 0)) static __always_inline int page_is_valid(unsigned long addr) { unsigned long tmp; int rc; asm volatile( "0: ic %1,%2\n" "1: lhi %0,1\n" "2:\n" ".pushsection .fixup, \"ax\"\n" "3: xr %0,%0\n" " jg 2b\n" ".popsection\n" EX_TABLE(0b, 3b) EX_TABLE(1b, 3b) : "=d" (rc), "=d" (tmp) : "Q" (*(unsigned long *) addr) : "cc"); return rc; } static __always_inline uint32_t csum_partial(const void *buf, int len, uint32_t sum) { register unsigned long reg2 asm("2") = (unsigned long) buf; register unsigned long reg3 asm("3") = (unsigned long) len; asm volatile( "0: cksm %0,%1\n" /* do checksum on longs */ " jo 0b\n" : "+d" (sum), "+d" (reg2), "+d" (reg3) : : "cc", "memory"); return sum; } #define __ctl_store(array, low, high) ({ \ typedef struct { char _[sizeof(array)]; } __may_alias addrtype;\ asm volatile( \ " stctg %1,%2,%0\n" \ : "=Q" (*(addrtype *)(&array)) \ : "i" (low), "i" (high)); \ }) #define __ctl_load(array, low, high) ({ \ typedef struct { char _[sizeof(array)]; } __may_alias addrtype; \ asm volatile( \ " lctlg %1,%2,%0\n" \ : : "Q" (*(addrtype *)(&array)), \ "i" (low), "i" (high)); \ }) static __always_inline void __ctl_set_bit(unsigned int cr, unsigned int bit) { unsigned long reg; __ctl_store(reg, cr, cr); reg |= 1UL << bit; __ctl_load(reg, cr, cr); } /* * DIAG 308 support */ enum diag308_subcode { DIAG308_CLEAR_RESET = 0, DIAG308_LOAD_NORMAL_RESET = 1, DIAG308_REL_HSA = 2, DIAG308_LOAD_CLEAR = 3, DIAG308_LOAD_NORMAL_DUMP = 4, DIAG308_SET = 5, DIAG308_STORE = 6, DIAG308_LOAD_NORMAL = 7, DIAG308_SET_PV = 8, DIAG308_UNPACK_PV = 10, }; enum diag308_rc { DIAG308_RC_OK = 0x0001, DIAG308_RC_NOCONFIG = 0x0102, }; static __always_inline unsigned long diag308(unsigned long subcode, void *addr) { register unsigned long _addr asm("0") = (unsigned long) addr; register unsigned long _rc asm("1") = 0; asm volatile( " diag %0,%2,0x308\n" "0:\n" : "+d" (_addr), "+d" (_rc) : "d" (subcode) : "cc", "memory"); return _rc; } /* * Store CPU address */ static __always_inline unsigned short stap(void) { unsigned short cpu_address; asm volatile("stap %0" : "=m" (cpu_address)); return cpu_address; } /* * Program the clock comparator */ static __always_inline void set_clock_comparator(uint64_t time) { asm volatile("sckc %0" : : "Q" (time)); } /* * Program the CPU timer */ static __always_inline void set_cpu_timer(uint64_t timer) { asm volatile("spt %0" : : "Q" (timer)); } /* * Get current time (store clock) */ static __always_inline unsigned long long get_tod_clock(void) { unsigned long long clk; asm volatile("stck %0" : "=Q" (clk) : : "cc"); return clk; } /* * Get ID of current CPU */ struct cpuid { unsigned int version:8; unsigned int ident:24; unsigned int machine:16; unsigned int unused:16; } __packed __aligned(8); static __always_inline void get_cpu_id(struct cpuid *ptr) { asm volatile("stidp %0" : "=Q" (*ptr)); } /* * Check if we run under z/VM */ static __always_inline int is_zvm(void) { struct cpuid cpuid; get_cpu_id(&cpuid); return cpuid.version == 0xff; } /* To avoid conflicts add a macro guard since __vector128 is also * defined in 'linux/asm/types.h'. */ #ifndef _S390_TYPES_H /* * Vector register definition */ typedef struct { uint32_t u[4]; } __vector128; #endif /* * Save vector registers */ static __always_inline void save_vx_regs(__vector128 *vxrs) { typedef struct { __vector128 _[32]; } addrtype; asm volatile( " la 1,%0\n" " .word 0xe70f,0x1000,0x003e\n" /* vstm 0,15,0(1) */ " .word 0xe70f,0x1100,0x0c3e\n" /* vstm 16,31,256(1) */ : "=Q" (*(addrtype *) vxrs) : : "1"); } /* * Save vector registers safe */ static __always_inline void save_vx_regs_safe(__vector128 *vxrs) { unsigned long cr0; __ctl_store(cr0, 0, 0); __ctl_set_bit(0, 17); __ctl_set_bit(0, 18); /* AFP-register-control */ save_vx_regs(vxrs); __ctl_load(cr0, 0, 0); } #define MAX_FACILITY_BIT (256*8) /* stfle_fac_list has 256 bytes */ static __always_inline int __test_facility(unsigned long nr, void *facilities) { unsigned char *ptr; if (nr >= MAX_FACILITY_BIT) return 0; ptr = (unsigned char *) facilities + (nr >> 3); return (*ptr & (0x80 >> (nr & 7))) != 0; } /* * The test_facility function uses the bit odering where the MSB is bit 0. * That makes it easier to query facility bits with the bit number as * documented in the Principles of Operation. */ static __always_inline int test_facility(unsigned long nr) { return __test_facility(nr, &S390_lowcore.stfle_fac_list); } static __always_inline unsigned long __stfle_asm(uint64_t *stfle_fac_list, unsigned int size) { register unsigned long reg0 asm("0") = size - 1; asm volatile( ".insn s,0xb2b00000,0(%1)" /* stfle */ : "+d" (reg0) : "a" (stfle_fac_list) : "memory", "cc"); return reg0; } /** * stfle - Store facility list extended * @stfle_fac_list: array where facility list can be stored * @size: size of passed in array in double words */ static __always_inline void stfle(uint64_t *stfle_fac_list, unsigned int size) { unsigned long nr; asm volatile( " .insn s,0xb2b10000,0(0)\n" /* stfl */ "0:\n" EX_TABLE(0b, 0b) : "+m" (S390_lowcore.stfl_fac_list)); nr = 4; /* bytes stored by stfl */ memcpy(stfle_fac_list, &S390_lowcore.stfl_fac_list, 4); if (S390_lowcore.stfl_fac_list & 0x01000000) { /* More facility bits available with stfle */ nr = __stfle_asm(stfle_fac_list, size); nr = MIN((nr + 1) * 8, size * 8UL); } memset((char *) stfle_fac_list + nr, 0, size * 8 - nr); } #endif /* __ASSEMBLER__ */ #endif /* S390_H */ s390-tools-2.38.0/include/boot/sigp.h000066400000000000000000000022471502674226300171550ustar00rootroot00000000000000/* * SIGP related definitions and functions. * * Copyright IBM Corp. 2020 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef S390_SIGP_H #define S390_SIGP_H /* Signal Processor Order Codes */ #define SIGP_STOP_AND_STORE_STATUS 9 #define SIGP_SET_ARCHITECTURE 18 #define SIGP_SET_MULTI_THREADING 22 #define SIGP_STORE_ASTATUS_AT_ADDRESS 23 /* Signal Processor Condition Codes */ #define SIGP_CC_ORDER_CODE_ACCEPTED 0 #define SIGP_CC_BUSY 2 #ifndef __ASSEMBLER__ #include static inline int sigp(uint16_t addr, uint8_t order, uint32_t parm, uint32_t *status) { register unsigned int reg1 asm ("1") = parm; int cc; asm volatile( " sigp %1,%2,0(%3)\n" " ipm %0\n" " srl %0,28\n" : "=d" (cc), "+d" (reg1) : "d" (addr), "a" (order) : "cc"); if (status && cc == 1) *status = reg1; return cc; } static inline int sigp_busy(uint16_t addr, uint8_t order, uint32_t parm, uint32_t *status) { int cc; do { cc = sigp(addr, order, parm, status); } while (cc == SIGP_CC_BUSY); return cc; } #endif /* __ASSEMBLER__ */ #endif /* S390_SIGP_H */ s390-tools-2.38.0/include/dump/000077500000000000000000000000001502674226300160375ustar00rootroot00000000000000s390-tools-2.38.0/include/dump/s390_dump.h000066400000000000000000000072761502674226300177470ustar00rootroot00000000000000/* * s390 related definitions and functions. * * Copyright IBM Corp. 2013, 2023 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef S390_DUMP_H #define S390_DUMP_H #include #include "boot/page.h" #include "lib/zt_common.h" /* * S390 dump format defines */ #define DF_S390_MAGIC 0xa8190173618f23fdULL #define DF_S390_MAGIC_EXT 0xa8190173618f23feULL #define DF_S390_HDR_SIZE 0x1000 #define DF_S390_EM_SIZE 16 #define DF_S390_EM_MAGIC 0x44554d505f454e44ULL #define DF_S390_EM_STR "DUMP_END" #define DF_S390_CPU_MAX 512 #define DF_S390_MAGIC_BLK_ECKD 3 #define DF_S390_DUMPER_MAGIC_SIZE 7 #define DF_S390_DUMPER_MAGIC_EXT "XECKD64" #define DF_S390_DUMPER_MAGIC_FBA_EXT "XDFBA64" #define DF_S390_DUMPER_MAGIC_MV_EXT "XMULT64" /* * Architecture of dumped system */ enum df_s390_arch { DF_S390_ARCH_32 = 1, DF_S390_ARCH_64 = 2, }; /* * zipl parameters passed at tail of dump tools */ struct stage2dump_parm_tail { char reserved[6]; uint8_t no_compress; uint8_t mvdump_force; uint64_t mem_upper_limit; } __packed; /* * s390 dump header format */ struct df_s390_hdr { uint64_t magic; /* 0x000 */ uint32_t version; /* 0x008 */ uint32_t hdr_size; /* 0x00c */ uint32_t dump_level; /* 0x010 */ uint32_t page_size; /* 0x014 */ uint64_t mem_size; /* 0x018 */ uint64_t mem_start; /* 0x020 */ uint64_t mem_end; /* 0x028 */ uint32_t num_pages; /* 0x030 */ uint32_t pad; /* 0x034 */ uint64_t tod; /* 0x038 */ uint64_t cpu_id; /* 0x040 */ uint32_t arch; /* 0x048 */ uint32_t volnr; /* 0x04c */ uint32_t build_arch; /* 0x050 */ uint64_t mem_size_real; /* 0x054 */ uint8_t mvdump; /* 0x05c */ uint16_t cpu_cnt; /* 0x05d */ uint16_t real_cpu_cnt; /* 0x05f */ uint8_t zlib_version_s390; /* 0x061 */ uint32_t zlib_entry_size; /* 0x062 */ uint8_t end_pad1[0x200 - 0x066]; /* 0x066 */ uint64_t mvdump_sign; /* 0x200 */ uint64_t mvdump_zipl_time; /* 0x208 */ uint8_t end_pad2[0x800 - 0x210]; /* 0x210 */ uint32_t lc_vec[DF_S390_CPU_MAX]; /* 0x800 */ } __packed __aligned(16); /* * End marker: Should be at the end of every valid s390 crash dump */ struct df_s390_em { union { uint64_t magic; char str[8]; }; uint64_t tod; } __packed __aligned(16); /* * Dump segment header */ struct df_s390_dump_segm_hdr { union { struct { uint64_t start; /* 0x000 */ uint64_t len; /* 0x008 */ uint64_t stop_marker; /* 0x010 */ /* Size in blocks of compressed dump segment written to disk */ uint32_t size_on_disk; /* 0x018 */ uint8_t reserved_pad[0x30 - 0x1c]; /* 0x01c */ /* * Number of compressed entries in this dump segment (up to * 1011 entries) */ uint32_t entry_count; /* 0x030 */ /* * Offsets in blocks to compressed entries written to disk * from the start of the dump segment. * High-order bit is set if the entry has been written * uncompressed. */ uint32_t entry_offset[]; /* 0x034 */ } __packed; uint8_t padding[PAGE_SIZE]; }; }; /* Data compression granularity (size of input data chunk for zlib deflate) */ #define DUMP_SEGM_ZLIB_ENTSIZE (1 * MIB) /* Maximum number of compressed entries in one dump segment */ #define DUMP_SEGM_ZLIB_MAXENTS ((sizeof(struct df_s390_dump_segm_hdr) \ - offsetof(struct df_s390_dump_segm_hdr, entry_offset)) \ / sizeof(uint32_t)) /* * Maximum length of compressed dump segment considering the size of * a single input chunk */ #define DUMP_SEGM_ZLIB_MAXLEN (DUMP_SEGM_ZLIB_MAXENTS * DUMP_SEGM_ZLIB_ENTSIZE) /* Bitmask to mark uncompressed chunks */ #define DUMP_SEGM_ENTRY_UNCOMPRESSED 0x80000000 #endif /* S390_DUMP_H */ s390-tools-2.38.0/include/ekmfweb/000077500000000000000000000000001502674226300165125ustar00rootroot00000000000000s390-tools-2.38.0/include/ekmfweb/ekmfweb.h000066400000000000000000001500311502674226300203030ustar00rootroot00000000000000/* * libekmfweb - EKMFWeb client library * * Copyright IBM Corp. 2020 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef LIB_EKMFWEB_H #define LIB_EKMFWEB_H #include #include typedef void CURL; struct ekmf_config { /** The base URL of the server. Should use https:// ! */ const char *base_url; /** Optional: File name of the CA bundle PEM file, or a name of a * directory the multiple CA certificates. If this is NULL, then the * default system path for CA certificates is used */ const char *tls_ca; /** Optional: File name of the client certificate PEM file */ const char *tls_client_cert; /** Optional: File name of the clients key PEM file */ const char *tls_client_key; /** Optional: Passphrase to read the clients key PEM file */ const char *tls_client_key_passphrase; /** Optional: File name of a PEM file holding a CA certificate of the * issuer */ const char *tls_issuer_cert; /** Optional: File name of a PEM file containing the servers pinned * public key. Public key pinning requires that verify_peer or * verify_host (or both) is true. */ const char *tls_pinned_pubkey; /** Optional: File name of a PEM file containing the server's * certificate. This can be used to allow peer verification with * self-signed server certificates */ const char *tls_server_cert; /** If true, the peer certificate is verified */ bool tls_verify_peer; /** If true, that the server certificate is for the server it is known * as (i.e. the hostname in the url) */ bool tls_verify_host; /** Maximum number of redirects to follow. Zero means that redirects are * not followed. -1 means to infinitely follow redirects. */ long max_redirs; /** File name of the login token (JSON Web Token) used for the last * login. */ const char *login_token; /** File name of a file containing the client identity secure key blob. * This key represents the client identity against EKMFWeb. Some * requests sent to EKMFWeb are signed with this (secure) key */ const char *identity_secure_key; /** File name of a PEM file containing the EKMFWeb servers public key * used to sign key export responses. */ const char *ekmf_server_pubkey; }; struct ekmf_cca_lib { void *cca_lib; /* Handle of CCA host library loaded via dlopen */ }; enum ekmf_ext_lib_type { EKMF_EXT_LIB_CCA = 1, }; struct ekmf_ext_lib { enum ekmf_ext_lib_type type; union { struct ekmf_cca_lib *cca; /* Used if type = EKMF_EXT_LIB_CCA */ }; }; /** * Connects to the specified server url and obtains the servers certificate * and its chain of signing certificates and stores them in the specified * PEM files. * * @param config the configuration structure. Only the base_url must * be specified, all others are optional. * @param server_cert_pem Optional: name of a PEM file to store the servers * certificate * @param server_pubkey_pem Optional: name of a PEM file to store the servers * public key (can be used for public key pinning) * @param ca_bundle_pem Optional: name of a PEM file to store the CA * certificate chain as a bundle * @param verified On return: If the server 's certificate has been * verified using the CA specification from the config * (if ca = NULL: default system CAs, otherwise path * or file to CAs). * @param error_msg on return: If not NULL, then a textual error message * is returned in case of a failing request. The caller * must free the error string when it is not NULL. * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. */ int ekmf_get_server_cert_chain(const struct ekmf_config *config, const char *server_cert_pem, const char *server_pubkey_pem, const char *ca_bundle_pem, bool *verified, char **error_msg, bool verbose); /** * Print the certificate(s) contained in the specified PEM file. * * @param cert_pem the file name of the PEM file to print * @param verbose if true, verbose messages are printed * * @returns -EIO if the file could not be opened. -ENOENT if the PEM file * does not contain any certificates. 0 if success. */ int ekmf_print_certificates(const char *cert_pem, bool verbose); /** * Checks if the login token stored in the file denoted by field login_token * of the config structure is valid or not. The file (if existent) contains a * JSON Web Token (JWT, see RFC7519). It is valid if the current date and time * is before its expiration time ("exp" claim), and after or equal its * not-before time ("nbf" claim). * Note: The signature (if any) of the JWT is not checked, nor any other JWT * fields. * * @param config the configuration structure * @param valid On return: true if the token is valid, false if not * @param login_token On return: If not NULL: the login token, if the * token is still valid. The returned string must * be freed by the caller when no longer needed. * @param verbose if true, verbose messages are printed * * @returns a negative errno in case of an error, 0 if success. */ int ekmf_check_login_token(const struct ekmf_config *config, bool *valid, char **login_token, bool verbose); /** * Performs a login of the specified user with a passcode. On success the * returned login token is stored in the file denoted by field login_token * of the config structure, so that it can be used by subsequent requests. * * To perform a single request, set curl_handle to NULL. This will cause the * function to initialize a new CURL handle, use it, and destroy it. * If you plan to perform multiple requests to the same host, supply the address * of a CURL pointer that is initially NULL. This function will then initialize * a new CURL handle on the first call. On subsequent calls, pass in the address * of the same CURL pointer so that the CURL handle is reused. After the last * request, the CURL handle must be destroyed by calling ekmf_curl_destroy). * * @param config the configuration structure * @param curl_handle address of a CURL handle used for reusing the same * CURL handle with multiple requests. * @param user_id the user-ID to log-in. * @param passcode the passcode to log-in the user. * @param error_msg on return: If not NULL, then a textual error message * is returned in case of a failing request. The caller * must free the error string when it is not NULL. * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. * -EACCES is returned, if the passcode is no longer valid. */ int ekmf_login(const struct ekmf_config *config, CURL **curl_handle, const char *user_id, const char *passcode, char **error_msg, bool verbose); enum ekmf_key_type { EKMF_KEY_TYPE_ECC = 1, EKMF_KEY_TYPE_RSA = 2, }; struct ekmf_key_gen_info { enum ekmf_key_type type; union { struct { int curve_nid; } ecc; struct { size_t modulus_bits; unsigned int pub_exp; } rsa; } params; }; /** * Generate a secure identity key used to identify the client to EKMFWeb. * The secure key blob is stored in a file specified in field * identity_secure_key of the config structure. If an secure key already exists * at that location, it is overwritten. * * @param config the configuration structure. Only field * identity_secure_key must be specified, all others * are optional. * @param info key generation info, such as key type (ECC or RSA) * and key parameters. * @param ext_lib External secure key crypto library to use * @param verbose if true, verbose messages are printed * * @returns a negative errno in case of an error, 0 if success. */ int ekmf_generate_identity_key(const struct ekmf_config *config, const struct ekmf_key_gen_info *info, const struct ekmf_ext_lib *ext_lib, bool verbose); /** * Re-encipher the secure identity key (form field identity_secure_key in * config) used to identify the client to EKMFWeb. * The secure key blob is encrypted using the HSM master key. Whenever the HSM * master key is being changed, the secure identity key must be re-enciphered. * You can either pro-actively re-encipher a secure key once the new master key * has been prepared (but not yet made active): to_new = true; or you can * re-encipher a secure key when the HSM master key has already been changed: * to_new = false. This requires that the HSM still has the old master key. * Not all HSMs support this. * * For pro-active re-encipherment it is suggested to store the re-enciphered * secure key on a separate place, until the new HSM master key has been made * active. Specify a file name in reenc_secure_key to do so. For an in-place * re-encipherment, set reenc_secure_key = NULL. * * @param config the configuration structure. Only field * identity_secure_key must be specified, all others * are optional. * @param to_new If true: the identity key is re-enciphered from the * current to the new master key. * If false: the identity key is re-enciphered from the * old to the current master key. * @param reenc_secure_key if not NULL, then the re-enciphered secure key is * stored into the filename specified here. Otherwise * the re-enciphered secure key replaces the original * secure identity key. * @param ext_lib External secure key crypto library to use * @param verbose if true, verbose messages are printed * * @returns a negative errno in case of an error, 0 if success. * A -ENODEV indicates that the master keys are not loaded. */ int ekmf_reencipher_identity_key(const struct ekmf_config *config, bool to_new, const char *reenc_secure_key, const struct ekmf_ext_lib *ext_lib, bool verbose); struct ekmf_rsa_pss_params { int salt_len; /* salt length in bytes, or OpenSSL constants RSA_PSS_SALTLEN_DIGEST (-1), RSA_PSS_SALTLEN_AUTO (-2), or RSA_PSS_SALTLEN_MAX(-3) */ int mgf_digest_nid; /* OpenSSl digest nid, or zero to use the same digest algorithm as the signature algorithm */ }; /** * Generate a certificate signing request using the secure identity key (field * identity_secure_key in config structure) with the specified subject name, * certificate extensions (if any), and writes the CSR to the specified file * in PEM format. * * To renew an existing certificate, specify renew_cert = true. In this case * the existing certificate (field sign_certificate in config struct) is read, * and the subject name is extracted from it. Any specified subject name RDNs * are added to the CSR. Also, the extensions are taken from the existing * certificate, and any specified extensions are added to the CSR. * * The CSR is signed using the secure identity key (field identity_secure_key in * config structure) with an signing algorithm matching the identity key (ECDSA, * RSA-PKCS, or RSA-PSS if rsa_pss is true), and the specified digest. If the * digest nid is zero, then a default digest is used. * * @param config the configuration structure. Only field * identity_secure_key must be specified, all others * are optional. * @param subject_rdns an array of strings, each string representing an * RDN in the form '[+]type=value'. If the type is * prepended with a '+', then this RDN is added to the * previous one. * @param num_subject_rdns number of RDN elements in the array. * @param subject_utf8 if true, RDNs of type MBSTRING_UTF8 are created, * otherwise type is MBSTRING_ASC is used. * @param renew_cert_filename if not NULL, specifies the file name of a PEM file * containing an existing certificate that is renewed * @param extensions an array of strings, each string representing an * certificate extension in the form 'type=value'. * @param num_extensions number of extension elements in the array. * @param digest_nid the OpenSSL digest nid to use with the signature * algorithm, or 0 to use the default * @param rsa_pss_params if not NULL and the identity key is an RSA key, then * the CSR is signed with RSA-PSS using the specified * PSS parameters. Ignored if the identity key is an EC * key * @param csr_pem_filename the name of the PEM file to which the CSR is written * @param new_hdr if true, output "NEW" in the PEM header lines * @param ext_lib External secure key crypto library to use * @param verbose if true, verbose messages are printed * * @returns a negative errno in case of an error, 0 if success: * -EINVAL: invalid parameter * -ENOMEM: Failed to allocate memory * -EBADMSG: an RDN or extension is not formatted correctly * -EIO: OpenSSL failed to create the CSR * -EEXIST: if one of the RDN name entries or extensions to add is a * duplicate * -ENOTSUP: the specified digest is not supported * any other errno from file I/O routines */ int ekmf_generate_csr(const struct ekmf_config *config, const char *subject_rdns[], size_t num_subject_rdns, bool subject_utf8, const char *renew_cert_filename, const char *extensions[], size_t num_extensions, int digest_nid, struct ekmf_rsa_pss_params *rsa_pss_params, const char *csr_pem_filename, bool new_hdr, const struct ekmf_ext_lib *ext_lib, bool verbose); /** * Generate a self signed certificate using the secure identity key (field * identity_secure_key in config structure) with the specified subject name, * certificate extensions (if any), and writes the certificate the specified * file in PEM format. * * To renew an existing certificate, specify renew_cert = true. In this case * the existing certificate (field sign_certificate in config struct) is read, * and the subject name is extracted from it. Any specified subject name RDNs * are added to the certificate. Also, the extensions are taken from the * existing certificate, and any specified extensions are added to the new * certificate. * * The certificate is signed using the secure identity key (field * identity_secure_key in config structure) with an signing algorithm matching * the identity key (ECDSA, RSA-PKCS, or RSA-PSS if rsa_pss is true), and the * specified digest. If the digest nid is zero, then a default digest is used. * * @param config the configuration structure. Only field * identity_secure_key must be specified, all others * are optional. * @param subject_rdns an array of strings, each string representing an * RDN in the form '[+]type=value'. If the type is * prepended with a '+', then this RDN is added to the * previous one. * @param num_subject_rdns number of RDN elements in the array. * @param subject_utf8 if true, RDNs of type MBSTRING_UTF8 are created, * otherwise type is MBSTRING_ASC is used. * @param renew_cert_filename if not NULL, specifies the file name of a PEM file * containing an existing certificate that is renewed * @param extensions an array of strings, each string representing an * certificate extension in the form 'type=value'. * @param num_extensions number of extension elements in the array. * @param validity_days number if day from the current date how long the * certificate is valid. * @param digest_nid the OpenSSL digest nid to use with the signature * algorithm, or 0 to use the default * @param rsa_pss_params if not NULL and the identity key is an RSA key, then * the certificate is signed with RSA-PSS using the * specified PSS parameters. Ignored if the identity * key is an EC key * @param cert_pem_filename the name of the PEM file to which the Certificate * is written * @param ext_lib External secure key crypto library to use * @param verbose if true, verbose messages are printed * * @returns a negative errno in case of an error, 0 if success. * -EINVAL: invalid parameter * -ENOMEM: Failed to allocate memory * -EBADMSG: an RDN or extension is not formatted correctly * -EIO: OpenSSL failed to create the certificate * -EEXIST: if one of the RDN name entries or extensions to add is a * duplicate * -ENOTSUP: the specified digest is not supported * any other errno from file I/O routines */ int ekmf_generate_ss_cert(const struct ekmf_config *config, const char *subject_rdns[], size_t num_subject_rdns, bool subject_utf8, const char *renew_cert_filename, const char *extensions[], size_t num_extensions, int validity_days, int digest_nid, struct ekmf_rsa_pss_params *rsa_pss_params, const char *cert_pem_filename, const struct ekmf_ext_lib *ext_lib, bool verbose); /** * Retrieves settings from the EKMFWeb server, such as the template names for * generating keys in EKMFWeb. * * To perform a single request, set curl_handle to NULL. This will cause the * function to initialize a new CURL handle, use it, and destroy it. * If you plan to perform multiple requests to the same host, supply the address * of a CURL pointer that is initially NULL. This function will then initialize * a new CURL handle on the first call. On subsequent calls, pass in the address * of the same CURL pointer so that the CURL handle is reused. After the last * request, the CURL handle must be destroyed by calling ekmf_curl_destroy). * * @param config the configuration structure * @param curl_handle address of a CURL handle used for reusing the same * CURL handle with multiple requests. * @param identity_template on return: If not NULL, the name of the template * used to generate identity keys with. The caller * must free the error string when it is not NULL. * @param xts_key1_template on return: If not NULL, the name of the template * used to generate the first XTS key with. The caller * must free the error string when it is not NULL. * @param xts_key1_template on return: If not NULL, the name of the template * used to generate the second XTS key with. The caller * must free the error string when it is not NULL. * @param xts_key1_template on return: If not NULL, the name of the template * used to generate a non-XTS key with. The caller * must free the error string when it is not NULL. * @param error_msg on return: If not NULL, then a textual error message * is returned in case of a failing request. The caller * must free the error string when it is not NULL. * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. * -EACCES is returned, if no or no valid login token is available. */ int ekmf_get_settings(const struct ekmf_config *config, CURL **curl_handle, char **identity_template, char **xts_key1_template, char **xts_key2_template, char **non_xts_template, char **error_msg, bool verbose); /** * Checks if the EKMFWeb server has the required Pervasive Encryption feature * installed * * To perform a single request, set curl_handle to NULL. This will cause the * function to initialize a new CURL handle, use it, and destroy it. * If you plan to perform multiple requests to the same host, supply the address * of a CURL pointer that is initially NULL. This function will then initialize * a new CURL handle on the first call. On subsequent calls, pass in the address * of the same CURL pointer so that the CURL handle is reused. After the last * request, the CURL handle must be destroyed by calling ekmf_curl_destroy). * * @param config the configuration structure * @param curl_handle address of a CURL handle used for reusing the same * CURL handle with multiple requests. * @param error_msg on return: If not NULL, then a textual error message * is returned in case of a failing request. The caller * must free the error string when it is not NULL. * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. * -ENOTSUP is returned, if the feature is not installed. */ int ekmf_check_feature(const struct ekmf_config *config, CURL **curl_handle, char **error_msg, bool verbose); /** * Request the EKMFWeb server's public signing key and store it into PEM file * specified in field server_pubkey of the config structure. * * To perform a single request, set curl_handle to NULL. This will cause the * function to initialize a new CURL handle, use it, and destroy it. * If you plan to perform multiple requests to the same host, supply the address * of a CURL pointer that is initially NULL. This function will then initialize * a new CURL handle on the first call. On subsequent calls, pass in the address * of the same CURL pointer so that the CURL handle is reused. After the last * request, the CURL handle must be destroyed by calling ekmf_curl_destroy). * * @param config the configuration structure * @param curl_handle address of a CURL handle used for reusing the same * CURL handle with multiple requests. * @param error_msg on return: If not NULL, then a textual error message * is returned in case of a failing request. The caller * must free the error string when it is not NULL. * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. * -EACCES is returned, if no or no valid login token is available. */ int ekmf_get_public_key(const struct ekmf_config *config, CURL **curl_handle, char **error_msg, bool verbose); /** * Requests a key to be retrieved from EKMFweb and imported under the current * HSM's master key. * * To perform a single request, set curl_handle to NULL. This will cause the * function to initialize a new CURL handle, use it, and destroy it. * If you plan to perform multiple requests to the same host, supply the address * of a CURL pointer that is initially NULL. This function will then initialize * a new CURL handle on the first call. On subsequent calls, pass in the address * of the same CURL pointer so that the CURL handle is reused. After the last * request, the CURL handle must be destroyed by calling ekmf_curl_destroy). * * @param config the configuration structure * @param curl_handle address of a CURL handle used for reusing the same * CURL handle with multiple requests. * @param key_uuid the UUID of the key to retrieve * @param sess_ec_curve_nid The OpenSSL nid of the EC curve used for the session * ECC key. If 0, then the default curve is used. * @param sign_rsa_digest_nid The OpenSSL nid of a digest used to sign the * request with if the identity key is an RSA-type key. * If 0, then the default digest is used. * Ignored for ECC-type identity keys. * @param use_rsa_pss If true, and the identity key is an RSA-type key, * use RSA-PSS to sign the request. * @param signature_kid the Key ID for the signature of the request * @param key_blob a buffer to store the retrieved key blob to * @param key_blob_length On entry: the size ofthe buffer * On return: the size of the key blob retrieved * @param error_msg on return: If not NULL, then a textual error message * is returned in case of a failing request. The caller * must free the error string when it is not NULL. * @param ext_lib External secure key crypto library to use * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. * -EACCES is returned, if no or no valid login token is available. * -EPERM is returned if the login token does not have permission to * retrieve the key */ int ekmf_retrieve_key(const struct ekmf_config *config, CURL **curl_handle, const char *key_uuid, int sess_ec_curve_nid, int sign_rsa_digest_nid, bool use_rsa_pss, const char *signature_kid, unsigned char *key_blob, size_t *key_blob_length, char **error_msg, const struct ekmf_ext_lib *ext_lib, bool verbose); struct ekmf_tag_definition { /** name of the tag */ const char *name; /** Optional: description of the tag (can be NULL) */ const char *description; }; struct ekmf_tag_def_list { /** array of tag definitions */ struct ekmf_tag_definition *tag_defs; /** number of tag definitions in array above */ size_t num_tag_defs; }; struct ekmf_template_info { /** name of the template */ const char *name; /** UUID of the template */ const char *uuid; /** type of the keys generated with this template , e.g. CIPHER */ const char *key_type; /** algorithm of the keys generated with this template, e.g. AES */ const char *algorithm; /** bit size of the keys generated with this template */ size_t key_size; /** state of the template , e.g. ACTIVE */ const char *state; /** state of the keys generated with this template , e.g. ACTIVE */ const char *key_state; /** label template when generating keys with this template */ const char *label_template; /** label tag definition list */ struct ekmf_tag_def_list label_tags; /** true if keys generated with this template can be exported */ bool export_allowed; /** the keystore type of the keys generated with this template */ const char *keystore_type; /** Type of ECC curve, e.g. PRIME_CURVE (for algorithm = ECC) */ const char *curve; /** timestamp when the template was created */ const char *created_on; /** timestamp when the template was updated */ const char *updated_on; }; struct ekmf_tag { /** name of the tag */ const char *name; /** value of the tag */ const char *value; }; struct ekmf_tag_list { /** array of tags */ struct ekmf_tag *tags; /** number of tags in array above */ size_t num_tags; }; struct ekmf_exporting_key { /** name of the exporting key */ const char *name; /** uuid of the exporting key */ const char *uuid; }; struct ekmf_export_control { /** If true, export is allowed using the exporting keys below */ bool export_allowed; /** array of erporting keys */ struct ekmf_exporting_key *exporting_keys; /** number of keys in array above */ size_t num_exporting_keys; }; struct ekmf_key_info { /** label (name) of the key */ const char *label; /** Optional: description of the key (can be NULL) */ const char *description; /** UUID of the key */ const char *uuid; /** type of the key, e.g. CIPHER */ const char *key_type; /** algorithm of the key, e.g. AES */ const char *algorithm; /** bit size of the key */ size_t key_size; /** state of the key, e.g. ACTIVE */ const char *state; /** type of the keystore, e.g. PERVASIVE_ENCRYPTION */ const char *keystore_type; /** name of the template used to generate the key */ const char *template; /** UUID of the template used to generate the key */ const char *template_uuid; /** label tag list */ struct ekmf_tag_list label_tags; /** custom tag list */ struct ekmf_tag_list custom_tags; /** export control information */ struct ekmf_export_control export_control; /** timestamp when the key was activated */ const char *activate_on; /** timestamp when the key expires */ const char *expires_on; /** timestamp when the key was created */ const char *created_on; /** timestamp when the key was updated */ const char *updated_on; }; #define EKMF_KEY_STATE_PRE_ACTIVATION "PRE-ACTIVATION" #define EKMF_KEY_STATE_ACTIVE "ACTIVE" #define EKMF_KEY_STATE_DEACTIVATED "DEACTIVATED" #define EKMF_KEY_STATE_COMPROMISED "COMPROMISED" #define EKMF_KEY_STATE_DESTROYED "DESTROYED" #define EKMF_KEY_STATE_DESTROYED_COMPROMISED "DESTROYED-COMPROMISED" /** * Callback function used with the ekmf_list_templates function. This * callback is called for each template found. * * @param curl_handle a CURL handle that can be used to perform further * EKMFWeb functions within the callback. * @param template_info a struct containing information about the template. * If any of the information needs to be kept, then the * callback function must make a copy of the * information. The memory holding the information * passed to the callback is no longer valid after the * callback has returned. * @param private the private pointer that was specified with the * ekmf_list_templates invocation. * * @returns zero for success, a negative errno in case of an error. * When a nonzero return code is returned, the template listing process stops, * and ekmf_list_templates returns the return code from the callback. */ typedef int (*ekmf_template_cb_t)(CURL *curl_handle, struct ekmf_template_info *template_info, void *private); /** * List available key templates. Only templates in state ACTIVE, with key * algorithm AES and keystore type PERVASIVE_ENCRYPTION are listed. The * templates are ordered by name in ascending order. * * To perform a single request, set curl_handle to NULL. This will cause the * function to initialize a new CURL handle, use it, and destroy it. * If you plan to perform multiple requests to the same host, supply the address * of a CURL pointer that is initially NULL. This function will then initialize * a new CURL handle on the first call. On subsequent calls, pass in the address * of the same CURL pointer so that the CURL handle is reused. After the last * request, the CURL handle must be destroyed by calling ekmf_curl_destroy). * * @param config the configuration structure * @param curl_handle address of a CURL handle used for reusing the same * CURL handle with multiple requests. * @param template_cb a callback function that is called for each template * found * @param private a pointer that is passed as-is to the callback * @param name_pattern a pattern to filter by name, or NULL to list all. * @param state the state of the templates to list. If NULL then * templates in state 'ACTIVE' are listed * @param error_msg on return: If not NULL, then a textual error message * is returned in case of a failing request. The caller * must free the error string when it is not NULL. * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. * -EACCES is returned, if no or no valid login token is available. * -EPERM is returned if the login token does not have permission to * list the templates */ int ekmf_list_templates(const struct ekmf_config *config, CURL **curl_handle, ekmf_template_cb_t template_cb, void *private, const char *name_pattern, const char *state, char **error_msg, bool verbose); /** * Get a template by its UUID. * * To perform a single request, set curl_handle to NULL. This will cause the * function to initialize a new CURL handle, use it, and destroy it. * If you plan to perform multiple requests to the same host, supply the address * of a CURL pointer that is initially NULL. This function will then initialize * a new CURL handle on the first call. On subsequent calls, pass in the address * of the same CURL pointer so that the CURL handle is reused. After the last * request, the CURL handle must be destroyed by calling ekmf_curl_destroy). * * @param config the configuration structure * @param curl_handle address of a CURL handle used for reusing the same * CURL handle with multiple requests. * @param template_uuid the UUID of the template to get * @param template an address of a template info pointer. On return * the pointer is updated to point to a newly allocated * template info struct. It must be freed by the caller * using ekmf_free_template_info when no longer needed. * @param error_msg on return: If not NULL, then a textual error message * is returned in case of a failing request. The caller * must free the error string when it is not NULL. * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. * -EACCES is returned, if no or no valid login token is available. * -EPERM is returned if the login token does not have permission to * get the template */ int ekmf_get_template(const struct ekmf_config *config, CURL **curl_handle, const char *template_uuid, struct ekmf_template_info **template, char **error_msg, bool verbose); /** * Get the last used sequence number of a template by its UUID. * * To perform a single request, set curl_handle to NULL. This will cause the * function to initialize a new CURL handle, use it, and destroy it. * If you plan to perform multiple requests to the same host, supply the address * of a CURL pointer that is initially NULL. This function will then initialize * a new CURL handle on the first call. On subsequent calls, pass in the address * of the same CURL pointer so that the CURL handle is reused. After the last * request, the CURL handle must be destroyed by calling ekmf_curl_destroy). * * @param config the configuration structure * @param curl_handle address of a CURL handle used for reusing the same * CURL handle with multiple requests. * @param template_uuid the UUID of the template to get * @param seqNumber On return: the last used sequence number of this * template. * @param error_msg on return: If not NULL, then a textual error message * is returned in case of a failing request. The caller * must free the error string when it is not NULL. * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. * -EACCES is returned, if no or no valid login token is available. * -EPERM is returned if the login token does not have permission to * get the template */ int ekmf_get_last_seq_no(const struct ekmf_config *config, CURL **curl_handle, const char *template_uuid, unsigned int *seqNumber, char **error_msg, bool verbose); /** * Clones a template info structure by making a deep copy of all strings and * arrays. * The copied template info must be freed using ekmf_free_template_info() by * the caller. * * @param src the source template info structure * @param dest the destination template info structure * * @returns zero for success, a negative errno in case of an error */ int ekmf_clone_template_info(const struct ekmf_template_info *src, struct ekmf_template_info **dest); /** * Free a template info structure. * * @param template the template to free */ void ekmf_free_template_info(struct ekmf_template_info *template); /** * Callback function used with the ekmf_list_keys function. This * callback is called for each ky found. * * @param curl_handle a CURL handle that can be used to perform further * EKMFWeb functions within the callback. * @param template_info a struct containing information about the key. * If any of the information needs to be kept, then the * callback function must make a copy of the * information. The memory holding the information * passed to the callback is no longer valid after the * callback has returned. * @param private the private pointer that was specified with the * ekmf_list_keys invocation. * * @returns zero for success, a negative errno in case of an error. * When a nonzero return code is returned, the key listing process stops, * and ekmf_list_keys returns the return code from the callback. */ typedef int (*ekmf_key_cb_t)(CURL *curl_handle, struct ekmf_key_info *key_info, void *private); /** * List available keys. The keys are ordered by name in ascending order. * * To perform a single request, set curl_handle to NULL. This will cause the * function to initialize a new CURL handle, use it, and destroy it. * If you plan to perform multiple requests to the same host, supply the address * of a CURL pointer that is initially NULL. This function will then initialize * a new CURL handle on the first call. On subsequent calls, pass in the address * of the same CURL pointer so that the CURL handle is reused. After the last * request, the CURL handle must be destroyed by calling ekmf_curl_destroy). * * @param config the configuration structure * @param curl_handle address of a CURL handle used for reusing the same * CURL handle with multiple requests. * @param key_cb a callback function that is called for each key * found * @param private a pointer that is passed as-is to the callback * @param name_pattern a pattern to filter by name, or NULL to list all. * @param states the states of the keys to list, or NULL to list keys * in ACTIVE state only. Multiple states can be * specified separated by comma.* * @param tags a list of custom tags to use as filter, or NULL * @param error_msg on return: If not NULL, then a textual error message * is returned in case of a failing request. The caller * must free the error string when it is not NULL. * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. * -EACCES is returned, if no or no valid login token is available. * -EPERM is returned if the login token does not have permission to * list the keys */ int ekmf_list_keys(const struct ekmf_config *config, CURL **curl_handle, ekmf_key_cb_t key_cb, void *private, const char *name_pattern, const char *states, const struct ekmf_tag_list *tags, char **error_msg, bool verbose); /** * Get information about a key by its UUID. * * To perform a single request, set curl_handle to NULL. This will cause the * function to initialize a new CURL handle, use it, and destroy it. * If you plan to perform multiple requests to the same host, supply the address * of a CURL pointer that is initially NULL. This function will then initialize * a new CURL handle on the first call. On subsequent calls, pass in the address * of the same CURL pointer so that the CURL handle is reused. After the last * request, the CURL handle must be destroyed by calling ekmf_curl_destroy). * * @param config the configuration structure * @param curl_handle address of a CURL handle used for reusing the same * CURL handle with multiple requests. * @param key_uuid the UUID of the key to get info for * @param key an address of a key info pointer. On return * the pointer is updated to point to a newly allocated * key info struct. It must be freed by the caller * using ekmf_free_key_info when no longer needed. * @param error_msg on return: If not NULL, then a textual error message * is returned in case of a failing request. The caller * must free the error string when it is not NULL. * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. * -EACCES is returned, if no or no valid login token is available. * -EPERM is returned if the login token does not have permission to * get the key info */ int ekmf_get_key_info(const struct ekmf_config *config, CURL **curl_handle, const char *key_uuid, struct ekmf_key_info **key, char **error_msg, bool verbose); /** * Changes the state of a key identified by its UUID. To update a key, * the timestamp from the last update is required. This can be found in * the key info struct in field update_on. * * To perform a single request, set curl_handle to NULL. This will cause the * function to initialize a new CURL handle, use it, and destroy it. * If you plan to perform multiple requests to the same host, supply the address * of a CURL pointer that is initially NULL. This function will then initialize * a new CURL handle on the first call. On subsequent calls, pass in the address * of the same CURL pointer so that the CURL handle is reused. After the last * request, the CURL handle must be destroyed by calling ekmf_curl_destroy). * * @param config the configuration structure * @param curl_handle address of a CURL handle used for reusing the same * CURL handle with multiple requests. * @param key_uuid the UUID of the key to get info for * @param new_state the new state of the key * @param updated_on the timestamp of the last update (must match) * @param error_msg on return: If not NULL, then a textual error message * is returned in case of a failing request. The caller * must free the error string when it is not NULL. * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. * -EACCES is returned, if no or no valid login token is available. * -EPERM is returned if the login token does not have permission to * update the key. * -EAGAIN is returned if the timestamp does not match, indicating that * the key has been updated in the meantime. */ int ekmf_set_key_state(const struct ekmf_config *config, CURL **curl_handle, const char *key_uuid, const char *new_state, const char *updated_on, char **error_msg, bool verbose); /** * Sets (changed/adds) custom tags of a key identified by its UUID. To update a * key, the timestamp from the last update is required. This can be found in * the key info struct in field update_on. * * To perform a single request, set curl_handle to NULL. This will cause the * function to initialize a new CURL handle, use it, and destroy it. * If you plan to perform multiple requests to the same host, supply the address * of a CURL pointer that is initially NULL. This function will then initialize * a new CURL handle on the first call. On subsequent calls, pass in the address * of the same CURL pointer so that the CURL handle is reused. After the last * request, the CURL handle must be destroyed by calling ekmf_curl_destroy). * * @param config the configuration structure * @param curl_handle address of a CURL handle used for reusing the same * CURL handle with multiple requests. * @param key_uuid the UUID of the key to get info for * @param tags a list of tags to set * @param updated_on the timestamp of the last update (must match) * @param new_updated_on on return: if not NULL, the new timestamp of the * current update. Can be used for subsequent updates * on the key. * @param error_msg on return: If not NULL, then a textual error message * is returned in case of a failing request. The caller * must free the error string when it is not NULL. * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. * -EACCES is returned, if no or no valid login token is available. * -EPERM is returned if the login token does not have permission to * update the key. * -EAGAIN is returned if the timestamp does not match, indicating that * the key has been updated in the meantime. */ int ekmf_set_key_tags(const struct ekmf_config *config, CURL **curl_handle, const char *key_uuid, const struct ekmf_tag_list *tags, const char *updated_on, char **new_updated_on, char **error_msg, bool verbose); /** * Deletes custom tags of a key identified by its UUID. To update a * key, the timestamp from the last update is required. This can be found in * the key info struct in field update_on. * * To perform a single request, set curl_handle to NULL. This will cause the * function to initialize a new CURL handle, use it, and destroy it. * If you plan to perform multiple requests to the same host, supply the address * of a CURL pointer that is initially NULL. This function will then initialize * a new CURL handle on the first call. On subsequent calls, pass in the address * of the same CURL pointer so that the CURL handle is reused. After the last * request, the CURL handle must be destroyed by calling ekmf_curl_destroy). * * @param config the configuration structure * @param curl_handle address of a CURL handle used for reusing the same * CURL handle with multiple requests. * @param key_uuid the UUID of the key to get info for * @param tags a list of tags to delete. Only the name of the tags * must be present in the tag structs of the list, the * values are ignored. * @param updated_on the timestamp of the last update (must match) * @param new_updated_on on return: if not NULL, the new timestamp of the * current update. Can be used for subsequent updates * on the key. * @param error_msg on return: If not NULL, then a textual error message * is returned in case of a failing request. The caller * must free the error string when it is not NULL. * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. * -EACCES is returned, if no or no valid login token is available. * -EPERM is returned if the login token does not have permission to * update the key. * -EAGAIN is returned if the timestamp does not match, indicating that * the key has been updated in the meantime. */ int ekmf_delete_key_tags(const struct ekmf_config *config, CURL **curl_handle, const char *key_uuid, const struct ekmf_tag_list *tags, const char *updated_on, char **new_updated_on, char **error_msg, bool verbose); /** * Clones a key info structure by making a deep copy of all strings and * arrays. * The copied key info must be freed using ekmf_free_key_info() by * the caller. * * @param src the source key info structure * @param dest the destination key info structure * * @returns zero for success, a negative errno in case of an error */ int ekmf_clone_key_info(const struct ekmf_key_info *src, struct ekmf_key_info **dest); /** * Free a key info structure. * * @param key the key info to free */ void ekmf_free_key_info(struct ekmf_key_info *key); /** * Generates a new key in EKMFWeb * * To perform a single request, set curl_handle to NULL. This will cause the * function to initialize a new CURL handle, use it, and destroy it. * If you plan to perform multiple requests to the same host, supply the address * of a CURL pointer that is initially NULL. This function will then initialize * a new CURL handle on the first call. On subsequent calls, pass in the address * of the same CURL pointer so that the CURL handle is reused. After the last * request, the CURL handle must be destroyed by calling ekmf_curl_destroy). * * @param config the configuration structure * @param curl_handle address of a CURL handle used for reusing the same * CURL handle with multiple requests. * @param template the name of the template to generate the key with * @param description Optional: a textual description of the key (can be * NULL) * @param label_tags list of label tags. The label tags are required as * defined in the template * @param custom_tags Optional: list of custom tags (can be NULL) * @param exporting_key Optional: The uuid of the key that is allowed to * export the newly generated key (can be NULL). * @param certificate Optional: The certificate to generate an identity * key from. Should be NULL for generating AES keys. * @param certificate_size Optional: the size of the certificate. Required if * certificate is not NULL. * @param key_info Optional: On return: If not NULL, a key info struct * is returned here containing key information. This * must be freed by the caller with ekmf_free_key_info * when no longer needed. * @param error_msg on return: If not NULL, then a textual error message * is returned in case of a failing request. The caller * must free the error string when it is not NULL. * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. * -EACCES is returned, if no or no valid login token is available. * -EPERM is returned if the login token does not have permission to * generate keys */ int ekmf_generate_key(const struct ekmf_config *config, CURL **curl_handle, const char *template, const char *description, const struct ekmf_tag_list *label_tags, const struct ekmf_tag_list *custom_tags, const char *exporting_key, const unsigned char *certificate, size_t certificate_size, struct ekmf_key_info **key_info, char **error_msg, bool verbose); /** * Close the connection to the EKMFWeb server by destroying the CURL handle. * * @param curl_handle the CURL handle to destroy */ void ekmf_curl_destroy(CURL *curl_handle); #endif s390-tools-2.38.0/include/kmipclient/000077500000000000000000000000001502674226300172315ustar00rootroot00000000000000s390-tools-2.38.0/include/kmipclient/kmipclient.h000066400000000000000000002657721502674226300215640ustar00rootroot00000000000000/* * libkmipclient - KMIP client library * * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef LIB_KMIPCLIENT_H #define LIB_KMIPCLIENT_H #include #include #include enum kmip_tag { KMIP_TAG_ACTIVATION_DATE = 0x420001, KMIP_TAG_APPLICATION_DATA = 0x420002, KMIP_TAG_APPLICATION_NAMESPACE = 0x420003, KMIP_TAG_APPLICATION_SPECIFIC_INFORMATION = 0x420004, KMIP_TAG_ARCHIVE_DATE = 0x420005, /* deprecated since v1.1 */ KMIP_TAG_ASYNCHRONOUS_CORRELATION_VALUE = 0x420006, KMIP_TAG_ASYNCHRONOUS_INDICATOR = 0x420007, KMIP_TAG_ATTRIBUTE = 0x420008, KMIP_TAG_ATTRIBUTE_INDEX = 0x420009, /* v1.x only */ KMIP_TAG_ATTRIBUTE_NAME = 0x42000A, KMIP_TAG_ATTRIBUTE_VALUE = 0x42000B, KMIP_TAG_AUTHENTICATION = 0x42000C, KMIP_TAG_BATCH_COUNT = 0x42000D, KMIP_TAG_BATCH_ERROR_CONTINUATION_OPTION = 0x42000E, KMIP_TAG_BATCH_ITEM = 0x42000F, KMIP_TAG_BATCH_ORDER_OPTION = 0x420010, KMIP_TAG_BLOCK_CIPHER_MODE = 0x420011, KMIP_TAG_CANCELATION_RESULT = 0x420012, KMIP_TAG_CERTIFICATE = 0x420013, KMIP_TAG_CERTIFICATE_IDENTIFIER = 0x420014, /* deprecated since v1.1 */ KMIP_TAG_CERTIFICATE_ISSUER = 0x420015, /* deprecated since v1.1 */ KMIP_TAG_CERTIFICATE_ISSUER_ALTERNATIVE_NAME = 0x420016, /* deprecated since v1.1 */ KMIP_TAG_CERTIFICATE_ISSUER_DISTINGUISHED_NAME = 0x420017, /* deprecated since v1.1 */ KMIP_TAG_CERTIFICATE_REQUEST = 0x420018, KMIP_TAG_CERTIFICATE_REQUEST_TYPE = 0x420019, KMIP_TAG_CERTIFICATE_SUBJECT = 0x42001A, /* deprecated since v1.1 */ KMIP_TAG_CERTIFICATE_SUBJECT_ALTERNATIVE_NAME = 0x42001B, /* deprecated since v1.1 */ KMIP_TAG_CERTIFICATE_SUBJECT_DISTINGUISHED_NAME = 0x42001C, /* deprecated since v1.1 */ KMIP_TAG_CERTIFICATE_TYPE = 0x42001D, KMIP_TAG_CERTIFICATE_VALUE = 0x42001E, KMIP_TAG_COMMON_TEMPLATE_ATTRIBUTE = 0x42001F, /* v1.x only */ KMIP_TAG_COMPROMIZE_DATE = 0x420020, KMIP_TAG_COMPROMISE_OCCURRENCE_DATE = 0x420021, KMIP_TAG_CONTACT_INFORMATION = 0x420022, KMIP_TAG_CREDENTIAL = 0x420023, KMIP_TAG_CREDENTIAL_TYPE = 0x420024, KMIP_TAG_CREDENTIAL_VALUE = 0x420025, KMIP_TAG_CRITICALITY_INDICATOR = 0x420026, KMIP_TAG_CRT_Coefficient = 0x420027, KMIP_TAG_CRYPTOGRAPHIC_ALGORITHM = 0x420028, KMIP_TAG_CRYPTOGRAPHIC_DOMAIN_PARAMETERS = 0x420029, KMIP_TAG_CRYPTOGRAPHIC_LENGTH = 0x42002A, KMIP_TAG_CRYPTOGRAPHIC_PARAMETERS = 0x42002B, KMIP_TAG_CRYPTOGRAPHIC_USAGE_MASK = 0x42002C, KMIP_TAG_CUSTOM_ATTRIBUTE = 0x42002D, /* v1.x only */ KMIP_TAG_D = 0x42002E, KMIP_TAG_DEACTIVATION_DATE = 0x42002F, KMIP_TAG_DERIVATION_DATE = 0x420030, KMIP_TAG_DERIVATION_DATA = 0x420031, KMIP_TAG_DERIVATION_PARAMETERS = 0x420032, KMIP_TAG_DESTROY_DATE = 0x420033, KMIP_TAG_DIGEST = 0x420034, KMIP_TAG_DIGEST_VALUE = 0x420035, KMIP_TAG_ENCRYPTION_KEY_INFORMATION = 0x420036, KMIP_TAG_G = 0x420037, KMIP_TAG_HASHING_ALGORITHM = 0x420038, KMIP_TAG_INITIAL_DATE = 0x420039, KMIP_TAG_INITIALIZATION_VECTOR = 0x42003A, KMIP_TAG_ISSUER = 0x42003B, /* deprecated since v1.1 */ KMIP_TAG_ITERATION_COUNT = 0x42003C, KMIP_TAG_IV_COUNTER_NONCE = 0x42003D, KMIP_TAG_J = 0x42003E, KMIP_TAG_KEY = 0x42003F, KMIP_TAG_KEY_BLOCK = 0x420040, KMIP_TAG_KEY_COMPRESSION_TYPE = 0x420041, KMIP_TAG_KEY_FORMAT_TYPE = 0x420042, KMIP_TAG_KEY_MATERIAL = 0x420043, KMIP_TAG_KEY_PART_IDENTIFIER = 0x420044, KMIP_TAG_KEY_VALUE = 0x420045, KMIP_TAG_KEY_WRAPPING_DATA = 0x420046, KMIP_TAG_KEY_WRAPPING_SPECIFICATION = 0x420047, KMIP_TAG_LAST_CHANGE_DATE = 0x420048, KMIP_TAG_LEASE_TIME = 0x420049, KMIP_TAG_LINK = 0x42004A, KMIP_TAG_LINK_TYPE = 0x42004B, KMIP_TAG_LINKED_OBJECT_IDENTIFIER = 0x42004C, KMIP_TAG_MAC_SIGNATURE = 0x42004D, KMIP_TAG_MAC_SIGNATURE_KEY_INFORMATION = 0x42004E, KMIP_TAG_MAXIMUM_ITEMS = 0x42004F, KMIP_TAG_MAXIMUM_RESPONSE_SIZE = 0x420050, KMIP_TAG_MESSAGE_EXTENSION = 0x420051, KMIP_TAG_MODULUS = 0x420052, KMIP_TAG_NAME = 0x420053, KMIP_TAG_NAME_TYPE = 0x420054, KMIP_TAG_NAME_VALUE = 0x420055, KMIP_TAG_OBJECT_GROUP = 0x420056, KMIP_TAG_OBJECT_TYPE = 0x420057, KMIP_TAG_OFFSET = 0x420058, KMIP_TAG_OPAQUE_DATA_TYPE = 0x420059, KMIP_TAG_OPAQUE_DATA_VALUE = 0x42005A, KMIP_TAG_OPAQUE_OBJECT = 0x42005B, KMIP_TAG_OPERATION = 0x42005C, KMIP_TAG_OPERATION_POLICY_NAME = 0x42005D, /* deprecated since v1.3 */ KMIP_TAG_P = 0x42005E, KMIP_TAG_PADDING_METHOD = 0x42005F, KMIP_TAG_PRIME_EXPONENT_P = 0x420060, KMIP_TAG_PRIME_EXPONENT_Q = 0x420061, KMIP_TAG_PRIME_FIELD_SIZE = 0x420062, KMIP_TAG_PRIVATE_EXPONENT = 0x420063, KMIP_TAG_PRIVATE_KEY = 0x420064, KMIP_TAG_PRIVATE_KEY_TEMPLATE_ATTRIBUTE = 0x420065, /* v1.x only */ KMIP_TAG_PRIVATE_KEY_UNIQUE_IDENTIFIER = 0x420066, KMIP_TAG_PROCESS_START_DATE = 0x420067, KMIP_TAG_PROTECT_STOP_DATE = 0x420068, KMIP_TAG_PROTOCOL_VERSION = 0x420069, KMIP_TAG_PROTOCOL_VERSION_MAJOR = 0x42006A, KMIP_TAG_PROTOCOL_VERSION_MINOR = 0x42006B, KMIP_TAG_PUBLIC_EXPONENT = 0x42006C, KMIP_TAG_PUBLIC_KEY = 0x42006D, KMIP_TAG_PUBLIC_KEY_TEMPLATE_ATTRIBUTE = 0x42006E, /* v1.x only */ KMIP_TAG_PUBLIC_KEY_UNIQUE_IDENTIFIER = 0x42006F, KMIP_TAG_PUT_FUNCTION = 0x420070, KMIP_TAG_Q = 0x420071, KMIP_TAG_Q_STRING = 0x420072, KMIP_TAG_Q_LENGTH = 0x420073, KMIP_TAG_QUERY_FUNCTION = 0x420074, KMIP_TAG_RECOMMENDED_CURVE = 0x420075, KMIP_TAG_REPLACED_UNIQUE_IDENTIFIER = 0x420076, KMIP_TAG_REQUEST_HEADER = 0x420077, KMIP_TAG_REQUEST_MESSAGE = 0x420078, KMIP_TAG_REQUEST_PAYLOAD = 0x420079, KMIP_TAG_RESPONSE_HEADER = 0x42007A, KMIP_TAG_RESPONSE_MESSAGE = 0x42007B, KMIP_TAG_RESPONSE_PAYLOAD = 0x42007C, KMIP_TAG_RESULT_MESSAGE = 0x42007D, KMIP_TAG_RESULT_REASON = 0x42007E, KMIP_TAG_RESULT_STATUS = 0x42007F, KMIP_TAG_REVOCATION_MESSAGE = 0x420080, KMIP_TAG_REVOCATION_REASON = 0x420081, KMIP_TAG_REVOCATION_REASON_CODE = 0x420082, KMIP_TAG_KEY_ROLE_TYPE = 0x420083, KMIP_TAG_SALT = 0x420084, KMIP_TAG_SECRET_DATA = 0x420085, KMIP_TAG_SECRET_DATA_TYPE = 0x420086, KMIP_TAG_SERIAL_NUMBER = 0x420087, /* deprecated since v1.1 */ KMIP_TAG_SERVER_INFORMATION = 0x420088, KMIP_TAG_SPLIT_KEY = 0x420089, KMIP_TAG_SPLIT_KEY_METHOD = 0x42008A, KMIP_TAG_SPLIT_KEY_PARTS = 0x42008B, KMIP_TAG_SPLIT_KEY_THRESHOLD = 0x42008C, KMIP_TAG_STATE = 0x42008D, KMIP_TAG_STORAGE_STATUS_MASK = 0x42008E, KMIP_TAG_SYMMETRIC_KEY = 0x42008F, KMIP_TAG_TEMPLATE = 0x420090, /* v1.x only */ KMIP_TAG_TEMPLATE_ATTRIBUTE = 0x420091, /* v1.x only */ KMIP_TAG_TIME_STAMP = 0x420092, KMIP_TAG_UNIQUE_BATCH_ITEM_ID = 0x420093, KMIP_TAG_UNIQUE_IDENTIFIER = 0x420094, KMIP_TAG_USAGE_LIMITS = 0x420095, KMIP_TAG_USAGE_LIMITS_COUNT = 0x420096, KMIP_TAG_USAGE_LIMITS_TOTAL = 0x420097, KMIP_TAG_USAGE_LIMITS_UNIT = 0x420098, KMIP_TAG_USERNAME = 0x420099, KMIP_TAG_VALIDITY_DATE = 0x42009A, KMIP_TAG_VALIDITY_INDICATOR = 0x42009B, KMIP_TAG_VENDOR_EXTENSION = 0x42009C, KMIP_TAG_VENDOR_IDENTIFICATION = 0x42009D, KMIP_TAG_WRAPPING_METHOD = 0x42009E, KMIP_TAG_X = 0x42009F, KMIP_TAG_Y = 0x4200A0, KMIP_TAG_PASSWORD = 0x4200A1, KMIP_TAG_DEVICE_IDENTIFIER = 0x4200A2, /* since v1.2 */ KMIP_TAG_ENCODING_OPTION = 0x4200A3, /* since v1.2 */ KMIP_TAG_EXTENSION_INFORMATION = 0x4200A4, /* since v1.2 */ KMIP_TAG_EXTENSION_NAME = 0x4200A5, /* since v1.2 */ KMIP_TAG_EXTENSION_TAG = 0x4200A6, /* since v1.2 */ KMIP_TAG_EXTENSION_TYPE = 0x4200A7, /* since v1.2 */ KMIP_TAG_FRESH = 0x4200A8, /* since v1.2 */ KMIP_TAG_MACHINE_IDENTIFIER = 0x4200A9, /* since v1.2 */ KMIP_TAG_MEDIA_IDENTIFIER = 0x4200AA, /* since v1.2 */ KMIP_TAG_NETWORK_IDENTIFIER = 0x4200AB, /* since v1.2 */ KMIP_TAG_OBJECT_GROUP_MEMBER = 0x4200AC, /* since v1.2 */ KMIP_TAG_CERTIFICATE_LENGTH = 0x4200AD, /* since v1.2 */ KMIP_TAG_DIGITAL_SIGNATURE_ALGORITHM = 0x4200AE, /* since v1.2 */ KMIP_TAG_CERTIFICATE_SERIAL_NUMBER = 0x4200AF, /* since v1.2 */ KMIP_TAG_DEVICE_SERIAL_NUMBER = 0x4200B0, /* since v1.2 */ KMIP_TAG_ISSUER_ALTERNATE_NAME = 0x4200B1, /* since v1.2 */ KMIP_TAG_ISSUER_DISTINGUISHED_NAME = 0x4200B2, /* since v1.2 */ KMIP_TAG_SUBJECT_ALTERNATE_NAME = 0x4200B3, /* since v1.2 */ KMIP_TAG_SUBJECT_DISTINGUISHED_NAME = 0x4200B4, /* since v1.2 */ KMIP_TAG_X_509_CERTIFICATE_IDENTIFIER = 0x4200B5, /* since v1.2 */ KMIP_TAG_X_509_CERTIFICATE_ISSUER = 0x4200B6, /* since v1.2 */ KMIP_TAG_X_509_CERTIFICATE_SUBJECT = 0x4200B7, /* since v1.2 */ KMIP_TAG_KEY_VALUE_LOCATION = 0x4200B8, /* since v1.2 */ KMIP_TAG_KEY_VALUE_LOCATION_VALUE = 0x4200B9, /* since v1.2 */ KMIP_TAG_KEY_VALUE_LOCATION_TYPE = 0x4200BA, /* since v1.2 */ KMIP_TAG_KEY_VALUE_PRESENT = 0x4200BB, /* since v1.2 */ KMIP_TAG_ORIGINAL_CREATION_DATE = 0x4200BC, /* since v1.2 */ KMIP_TAG_PGP_KEY = 0x4200BD, /* since v1.2 */ KMIP_TAG_PGP_KEY_VERSION = 0x4200BE, /* since v1.2 */ KMIP_TAG_ALTERNATE_NAME = 0x4200BF, /* since v1.2 */ KMIP_TAG_ALTERNATE_NAME_VALUE = 0x4200C0, /* since v1.2 */ KMIP_TAG_ALTERNATE_NAME_TYPE = 0x4200C1, /* since v1.2 */ KMIP_TAG_DATA = 0x4200C2, /* since v1.2 */ KMIP_TAG_SIGNATURE_DATA = 0x4200C3, /* since v1.2 */ KMIP_TAG_DATA_LENGTH = 0x4200C4, /* since v1.2 */ KMIP_TAG_RANDOM_IV = 0x4200C5, /* since v1.2 */ KMIP_TAG_MAC_DATA = 0x4200C6, /* since v1.2 */ KMIP_TAG_ATTESTATION_TYPE = 0x4200C7, /* since v1.2 */ KMIP_TAG_NONCE = 0x4200C8, /* since v1.2 */ KMIP_TAG_NONCE_ID = 0x4200C9, /* since v1.2 */ KMIP_TAG_NONCE_VALUE = 0x4200CA, /* since v1.2 */ KMIP_TAG_ATTESTATION_MEASUREMENT = 0x4200CB, /* since v1.2 */ KMIP_TAG_ATTESTATION_ASSERTION = 0x4200CC, /* since v1.2 */ KMIP_TAG_IV_LENGTH = 0x4200CD, /* since v1.2 */ KMIP_TAG_TAG_LENGTH = 0x4200CE, /* since v1.2 */ KMIP_TAG_FIXED_FIELD_LENGTH = 0x4200CF, /* since v1.2 */ KMIP_TAG_COUNTER_LENGTH = 0x4200D0, /* since v1.2 */ KMIP_TAG_INITIAL_COUNTER_VALUE = 0x4200D1, /* since v1.2 */ KMIP_TAG_INVOCATION_FIELD_LENGTH = 0x4200D2, /* since v1.2 */ KMIP_TAG_ATTESTATION_CAPABLE_INDICATOR = 0x4200D3, /* since v1.2 */ KMIP_TAG_OFFSET_ITEMS = 0x4200D4, /* since v1.3 */ KMIP_TAG_LOCATED_ITEMS = 0x4200D5, /* since v1.3 */ KMIP_TAG_CORRELATION_VALUE = 0x4200D6, /* since v1.3 */ KMIP_TAG_INIT_INDICATOR = 0x4200D7, /* since v1.3 */ KMIP_TAG_FINAL_INDICATOR = 0x4200D8, /* since v1.3 */ KMIP_TAG_RNG_PARAMETERS = 0x4200D9, /* since v1.3 */ KMIP_TAG_RNG_ALGORITHM = 0x4200DA, /* since v1.3 */ KMIP_TAG_DRBG_ALGORITHM = 0x4200DB, /* since v1.3 */ KMIP_TAG_FIPS186_VARIANT = 0x4200DC, /* since v1.3 */ KMIP_TAG_PREDICTION_RESISTANCE = 0x4200DD, /* since v1.3 */ KMIP_TAG_RANDOM_NUMBER_GENERATOR = 0x4200DE, /* since v1.3 */ KMIP_TAG_VALIDATION_INFORMATION = 0x4200DF, /* since v1.3 */ KMIP_TAG_VALIDATION_AUTHORITY_TYPE = 0x4200E0, /* since v1.3 */ KMIP_TAG_VALIDATION_AUTHORITY_COUNTRY = 0x4200E1, /* since v1.3 */ KMIP_TAG_VALIDATION_AUTHORITY_URI = 0x4200E2, /* since v1.3 */ KMIP_TAG_VALIDATION_VERSION_MAJOR = 0x4200E3, /* since v1.3 */ KMIP_TAG_VALIDATION_VERSION_MINOR = 0x4200E4, /* since v1.3 */ KMIP_TAG_VALIDATION_TYPE = 0x4200E5, /* since v1.3 */ KMIP_TAG_VALIDATION_LEVEL = 0x4200E6, /* since v1.3 */ KMIP_TAG_VALIDATION_CERTIFICATE_IDENTIFIER = 0x4200E7, /* since v1.3 */ KMIP_TAG_VALIDATION_CERTIFICATE_URI = 0x4200E8, /* since v1.3 */ KMIP_TAG_VALIDATION_VENDOR_URI = 0x4200E9, /* since v1.3 */ KMIP_TAG_VALIDATION_PROFILE = 0x4200EA, /* since v1.3 */ KMIP_TAG_PROFILE_INFORMATION = 0x4200EB, /* since v1.3 */ KMIP_TAG_PROFILE_NAME = 0x4200EC, /* since v1.3 */ KMIP_TAG_SERVER_URI = 0x4200ED, /* since v1.3 */ KMIP_TAG_SERVER_PORT = 0x4200EE, /* since v1.3 */ KMIP_TAG_STREAMING_CAPABILITY = 0x4200EF, /* since v1.3 */ KMIP_TAG_ASYNCHRONOUS_CAPABILITY = 0x4200F0, /* since v1.3 */ KMIP_TAG_ATTESTATION_CAPABILITY = 0x4200F1, /* since v1.3 */ KMIP_TAG_UNWRAP_MODE = 0x4200F2, /* since v1.3 */ KMIP_TAG_DESTROY_ACTION = 0x4200F3, /* since v1.3 */ KMIP_TAG_SHREDDING_ALGORITHM = 0x4200F4, /* since v1.3 */ KMIP_TAG_RNG_MODE = 0x4200F5, /* since v1.3 */ KMIP_TAG_CLIENT_REGISTRATION_METHOD = 0x4200F6, /* since v1.3 */ KMIP_TAG_CAPABILITY_INFORMATION = 0x4200F7, /* since v1.3 */ KMIP_TAG_KEY_WRAP_TYPE = 0x4200F8, /* since v1.4 */ KMIP_TAG_BATCH_UNDO_CAPABILITY = 0x4200F9, /* since v1.4 */ KMIP_TAG_BATCH_CONTINUE_CAPABILITY = 0x4200FA, /* since v1.4 */ KMIP_TAG_PKCS_12_FRIENDLY_NAME = 0x4200FB, /* since v1.4 */ KMIP_TAG_DESCRIPTION = 0x4200FC, /* since v1.4 */ KMIP_TAG_COMMENT = 0x4200FD, /* since v1.4 */ KMIP_TAG_AUTHENTICATED_ENCRYPTION_ADDITIONAL_DATA = 0x4200FE, /* since v1.4 */ KMIP_TAG_AUTHENTICTAED_ENCRYPTION_TAG = 0x4200FF, /* since v1.4 */ KMIP_TAG_SALT_LENGTH = 0x420100, /* since v1.4 */ KMIP_TAG_MASK_GENERATOR = 0x420101, /* since v1.4 */ KMIP_TAG_MASK_GENERATOR_HASHING_ALGORITHM = 0x420102, /* since v1.4 */ KMIP_TAG_P_SOURCE = 0x420103, /* since v1.4 */ KMIP_TAG_TRAILER_FIELD = 0x420104, /* since v1.4 */ KMIP_TAG_CLIENT_CORRELATION_VALUE = 0x420105, /* since v1.4 */ KMIP_TAG_SERVER_CORRELATION_VALUE = 0x420106, /* since v1.4 */ KMIP_TAG_DIGESTED_DATA = 0x420107, /* since v1.4 */ KMIP_TAG_CERTIFICATE_SUBJECT_CN = 0x420108, /* since v1.4 */ KMIP_TAG_CERTIFICATE_SUBJECT_O = 0x420109, /* since v1.4 */ KMIP_TAG_CERTIFICATE_SUBJECT_OU = 0x42010A, /* since v1.4 */ KMIP_TAG_CERTIFICATE_SUBJECT_EMAIL = 0x42010B, /* since v1.4 */ KMIP_TAG_CERTIFICATE_SUBJECT_C = 0x42010C, /* since v1.4 */ KMIP_TAG_CERTIFICATE_SUBJECT_ST = 0x42010D, /* since v1.4 */ KMIP_TAG_CERTIFICATE_SUBJECT_L = 0x42010E, /* since v1.4 */ KMIP_TAG_CERTIFICATE_SUBJECT_UID = 0x42010F, /* since v1.4 */ KMIP_TAG_CERTIFICATE_SUBJECT_SERIAL_NUMBER = 0x420110, /* since v1.4 */ KMIP_TAG_CERTIFICATE_SUBJECT_TITLE = 0x420111, /* since v1.4 */ KMIP_TAG_CERTIFICATE_SUBJECT_DC = 0x420112, /* since v1.4 */ KMIP_TAG_CERTIFICATE_SUBJECT_DN_QUALIFIER = 0x420113, /* since v1.4 */ KMIP_TAG_CERTIFICATE_ISSUER_CN = 0x420114, /* since v1.4 */ KMIP_TAG_CERTIFICATE_ISSUER_O = 0x420115, /* since v1.4 */ KMIP_TAG_CERTIFICATE_ISSUER_OU = 0x420116, /* since v1.4 */ KMIP_TAG_CERTIFICATE_ISSUER_EMAIL = 0x420117, /* since v1.4 */ KMIP_TAG_CERTIFICATE_ISSUER_C = 0x420118, /* since v1.4 */ KMIP_TAG_CERTIFICATE_ISSUER_ST = 0x420119, /* since v1.4 */ KMIP_TAG_CERTIFICATE_ISSUER_L = 0x42011A, /* since v1.4 */ KMIP_TAG_CERTIFICATE_ISSUER_UID = 0x42011B, /* since v1.4 */ KMIP_TAG_CERTIFICATE_ISSUER_SERIAL_NUMBER = 0x42011C, /* since v1.4 */ KMIP_TAG_CERTIFICATE_ISSUER_TITLE = 0x42011D, /* since v1.4 */ KMIP_TAG_CERTIFICATE_ISSUER_DC = 0x42011E, /* since v1.4 */ KMIP_TAG_CERTIFICATE_ISSUER_DN_QUALIFIER = 0x42011F, /* since v1.4 */ KMIP_TAG_SENSITIVE = 0x420120, /* since v1.4 */ KMIP_TAG_ALWAYS_SENSITIVE = 0x420121, /* since v1.4 */ KMIP_TAG_EXTRACTABLE = 0x420122, /* since v1.4 */ KMIP_TAG_NEVER_EXTRACTABLE = 0x420123, /* since v1.4 */ KMIP_TAG_REPLACE_EXISTING = 0x420124, /* since v1.4 */ KMIP_TAG_ATTRIBUTES = 0x420125, /* since v2.0 */ KMIP_TAG_COMMON_ATTRIBUTES = 0x420126, /* since v2.0 */ KMIP_TAG_PRIVATE_KEY_ATTRIBUTES = 0x420127, /* since v2.0 */ KMIP_TAG_PUBLIC_KEY_ATTRIBUTES = 0x420128, /* since v2.0 */ KMIP_TAG_EXTENSION_ENUMERATION = 0x420129, /* since v2.0 */ KMIP_TAG_EXTENSION_ATTRIBUTE = 0x42012A, /* since v2.0 */ KMIP_TAG_EXTENSION_PARENT_STRUCTURE_TAG = 0x42012B, /* since v2.0 */ KMIP_TAG_EXTENSION_DESCRIPTION = 0x42012C, /* since v2.0 */ KMIP_TAG_SERVER_NAME = 0x42012D, /* since v2.0 */ KMIP_TAG_SERVER_SERIAL_NUMBER = 0x42012E, /* since v2.0 */ KMIP_TAG_SERVER_VERSION = 0x42012F, /* since v2.0 */ KMIP_TAG_SERVER_LOAD = 0x420130, /* since v2.0 */ KMIP_TAG_PRODUCT_NAME = 0x420131, /* since v2.0 */ KMIP_TAG_BUILD_LEVEL = 0x420132, /* since v2.0 */ KMIP_TAG_BUILD_DATE = 0x420133, /* since v2.0 */ KMIP_TAG_CLUSTER_INFO = 0x420134, /* since v2.0 */ KMIP_TAG_ALTERNATE_FAILOVER_ENDPOINTS = 0x420135, /* since v2.0 */ KMIP_TAG_SHORT_UNIQUE_IDENTIFIER = 0x420136, /* since v2.0 */ KMIP_TAG_TAG = 0x420138, /* since v2.0 */ KMIP_TAG_CERTIFICATE_REQUEST_UNIQUE_IDENTIFIER = 0x420139, /* since v2.0 */ KMIP_TAG_NIST_KEY_TYPE = 0x42013A, /* since v2.0 */ KMIP_TAG_ATTRIBUTE_REFERENCE = 0x42013B, /* since v2.0 */ KMIP_TAG_CURRENT_ATTRIBUTE = 0x42013C, /* since v2.0 */ KMIP_TAG_NEW_ATTRIBUTE = 0x42013D, /* since v2.0 */ KMIP_TAG_CERTIFICATE_REQUEST_VALUE = 0x420140, /* since v2.0 */ KMIP_TAG_LOG_MESSAGE = 0x420141, /* since v2.0 */ KMIP_TAG_PROFILE_VERSION = 0x420142, /* since v2.0 */ KMIP_TAG_PROFILE_VERSION_MAJOR = 0x420143, /* since v2.0 */ KMIP_TAG_PROFILE_VERSION_MINOR = 0x420144, /* since v2.0 */ KMIP_TAG_PROTECTION_LEVEL = 0x420145, /* since v2.0 */ KMIP_TAG_PROTECTION_PERIOD = 0x420146, /* since v2.0 */ KMIP_TAG_QUANTUM_SAFE = 0x420147, /* since v2.0 */ KMIP_TAG_QUANTUM_SAFE_CAPABILITY = 0x420148, /* since v2.0 */ KMIP_TAG_TICKET = 0x420149, /* since v2.0 */ KMIP_TAG_TICKET_TYPE = 0x42014A, /* since v2.0 */ KMIP_TAG_TICKET_VALUE = 0x42014B, /* since v2.0 */ KMIP_TAG_REQUEST_COUNT = 0x42014C, /* since v2.0 */ KMIP_TAG_RIGHTS = 0x42014D, /* since v2.0 */ KMIP_TAG_OBJECTS = 0x42014E, /* since v2.0 */ KMIP_TAG_OPERATIONS = 0x42014F, /* since v2.0 */ KMIP_TAG_RIGHT = 0x420150, /* since v2.0 */ KMIP_TAG_ENDPOINT_ROLE = 0x420151, /* since v2.0 */ KMIP_TAG_DEFAULTS_INFORMATION = 0x420152, /* since v2.0 */ KMIP_TAG_OBJECT_DEFAULTS = 0x420153, /* since v2.0 */ KMIP_TAG_EPHEMERAL = 0x420154, /* since v2.0 */ KMIP_TAG_SERVER_HASHED_PASSWORD = 0x420155, /* since v2.0 */ KMIP_TAG_ONE_TIME_PASSWORD = 0x420156, /* since v2.0 */ KMIP_TAG_HASHED_PASSWORD = 0x420157, /* since v2.0 */ KMIP_TAG_ADJUSTMENT_TYPE = 0x420158, /* since v2.0 */ KMIP_TAG_PKCS_11_INTERFACE = 0x420159, /* since v2.0 */ KMIP_TAG_PKCS_11_FUNCTION = 0x42015A, /* since v2.0 */ KMIP_TAG_PKCS_11_INPUT_PARAMETERS = 0x42015B, /* since v2.0 */ KMIP_TAG_PKCS_11_OUTPUT_PARAMETERS = 0x42015C, /* since v2.0 */ KMIP_TAG_PKCS_11_RETURN_CODE = 0x42015D, /* since v2.0 */ KMIP_TAG_PROTECTION_STORAGE_MASK = 0x42015E, /* since v2.0 */ KMIP_TAG_PROTECTION_STORAGE_MASKS = 0x42015F, /* since v2.0 */ KMIP_TAG_INTEROP_FUNCTION = 0x420160, /* since v2.0 */ KMIP_TAG_INTEROP_IDENTIFIER = 0x420161, /* since v2.0 */ KMIP_TAG_ADJUSTMENT_VALUE = 0x420162, /* since v2.0 */ KMIP_TAG_COMMON_PROTECTION_STORAGE_MASKS = 0x420163, /* since v2.0 */ KMIP_TAG_PRIVATE_PROTECTION_STORAGE_MASKS = 0x420164, /* since v2.0 */ KMIP_TAG_PUBLIC_PROTECTION_STORAGE_MASKS = 0x420165, /* since v2.0 */ KMIP_TAG_OBJECT_GROUPS = 0x420166, /* since v2.1 */ KMIP_TAG_OBJECT_TYPES = 0x420167, /* since v2.1 */ KMIP_TAG_CONSTRAINTS = 0x420168, /* since v2.1 */ KMIP_TAG_CONSTRAINT = 0x420169, /* since v2.1 */ KMIP_TAG_ROTATE_INTERVAL = 0x42016A, /* since v2.1 */ KMIP_TAG_ROTATE_AUTOMATIC = 0x42016B, /* since v2.1 */ KMIP_TAG_ROTATE_OFFSET = 0x42016C, /* since v2.1 */ KMIP_TAG_ROTATE_DATE = 0x42016D, /* since v2.1 */ KMIP_TAG_ROTATE_GENERATION = 0x42016E, /* since v2.1 */ KMIP_TAG_ROTATE_NAME = 0x42016F, /* since v2.1 */ KMIP_TAG_ROTATE_NAME_VALUE = 0x420170, /* since v2.1 */ KMIP_TAG_ROTATE_NAME_TYPE = 0x420171, /* since v2.1 */ KMIP_TAG_ROTATE_LATEST = 0x420172, /* since v2.1 */ KMIP_TAG_ASYNCHRONOUS_REQUEST = 0x420173, /* since v2.1 */ KMIP_TAG_SUBMISSION_DATE = 0x420174, /* since v2.1 */ KMIP_TAG_PROCESSING_STAGE = 0x420175, /* since v2.1 */ KMIP_TAG_ASYNCHRONOUS_CORRELATION_VALUES = 0x420176, /* since v2.1 */ }; enum kmip_type { KMIP_TYPE_STRUCTURE = 0x01, KMIP_TYPE_INTEGER = 0x02, KMIP_TYPE_LONG_INTEGER = 0x03, KMIP_TYPE_BIG_INTEGER = 0x04, KMIP_TYPE_ENUMERATION = 0x05, KMIP_TYPE_BOOLEAN = 0x06, KMIP_TYPE_TEXT_STRING = 0x07, KMIP_TYPE_BYTE_STRING = 0x08, KMIP_TYPE_DATE_TIME = 0x09, KMIP_TYPE_INTERVAL = 0x0A, KMIP_TYPE_DATE_TIME_EXTENDED = 0x0B, /* Since v2.0 */ }; enum kmip_operation { KMIP_OPERATION_CREATE = 0x01, KMIP_OPERATION_CREATE_KEY_PAIR = 0x02, KMIP_OPERATION_REGISTER = 0x03, KMIP_OPERATION_RE_KEY = 0x04, KMIP_OPERATION_DERIVE_KEY = 0x05, KMIP_OPERATION_CERTIFY = 0x06, KMIP_OPERATION_RE_CERTIFY = 0x07, KMIP_OPERATION_LOCATE = 0x08, KMIP_OPERATION_CHECK = 0x09, KMIP_OPERATION_GET = 0x0A, KMIP_OPERATION_GET_ATTRIBUTES = 0x0B, KMIP_OPERATION_GET_ATTRIBUTE_LIST = 0x0C, KMIP_OPERATION_ADD_ATTRIBUTE = 0x0D, KMIP_OPERATION_MODIFY_ATTRIBUTE = 0x0E, KMIP_OPERATION_DELETE_ATTRIBUTE = 0x0F, KMIP_OPERATION_OBTAIN_LEASE = 0x10, KMIP_OPERATION_GET_USAGE_ALLOCATION = 0x11, KMIP_OPERATION_ACTIVATE = 0x12, KMIP_OPERATION_REVOKE = 0x13, KMIP_OPERATION_DESTROY = 0x14, KMIP_OPERATION_ARCHIVE = 0x15, KMIP_OPERATION_RECOVER = 0x16, KMIP_OPERATION_VALIDATE = 0x17, KMIP_OPERATION_QUERY = 0x18, KMIP_OPERATION_CANCEL = 0x19, KMIP_OPERATION_POLL = 0x1A, KMIP_OPERATION_NOTIFY = 0x1B, KMIP_OPERATION_PUT = 0x1C, KMIP_OPERATION_RE_KEY_KEY_PAIR = 0x1D, /* since v1.2 */ KMIP_OPERATION_DISCOVER_VERSIONS = 0x1E, /* since v1.2 */ KMIP_OPERATION_ENCRYPT = 0x1F, /* since v1.2 */ KMIP_OPERATION_DECRYPT = 0x20, /* since v1.2 */ KMIP_OPERATION_SIGN = 0x21, /* since v1.2 */ KMIP_OPERATION_SIGNATURE_VERIFY = 0x22, /* since v1.2 */ KMIP_OPERATION_MAC = 0x23, /* since v1.2 */ KMIP_OPERATION_MAC_VERIFY = 0x24, /* since v1.2 */ KMIP_OPERATION_RNG_RETRIEVE = 0x25, /* since v1.2 */ KMIP_OPERATION_RNG_SEED = 0x26, /* since v1.2 */ KMIP_OPERATION_HASH = 0x27, /* since v1.2 */ KMIP_OPERATION_CREATE_SPLIT_KEY = 0x28, /* since v1.2 */ KMIP_OPERATION_JOIN_SPLIT_KEY = 0x29, /* since v1.2 */ KMIP_OPERATION_IMPORT = 0x2A, /* since v1.4 */ KMIP_OPERATION_EXPORT = 0x2B, /* since v1.4 */ KMIP_OPERATION_LOG = 0x2C, /* since v2.0 */ KMIP_OPERATION_LOGIN = 0x2D, /* since v2.0 */ KMIP_OPERATION_LOGOUT = 0x2E, /* since v2.0 */ KMIP_OPERATION_DELEGATE_LOGIN = 0x2F, /* since v2.0 */ KMIP_OPERATION_ADJUST_ATTRIBUTE = 0x30, /* since v2.0 */ KMIP_OPERATION_SET_ATTRIBUTE = 0x31, /* since v2.0 */ KMIP_OPERATION_SET_ENDPOINT_ROLE = 0x32, /* since v2.0 */ KMIP_OPERATION_PKS_11 = 0x33, /* since v2.0 */ KMIP_OPERATION_INTEROP = 0x34, /* since v2.0 */ KMIP_OPERATION_RE_PROVISION = 0x35, /* since v2.0 */ KMIP_OPERATION_SET_DEFAULTS = 0x36, /* since v2.1 */ KMIP_OPERATION_SET_CONSTRAINTS = 0x37, /* since v2.1 */ KMIP_OPERATION_GET_CONSTRAINTS = 0x38, /* since v2.1 */ KMIP_OPERATION_QUERY_ASYNCHRONOUS_REQUESTS = 0x39, /* since v2.1 */ KMIP_OPERATION_PROCESS = 0x3A, /* since v2.1 */ KMIP_OPERATION_PING = 0x3B, /* since v2.1 */ }; enum kmip_batch_error_cont_option { KMIP_BATCH_ERR_CONT_CONTINUE = 0x01, KMIP_BATCH_ERR_CONT_STOP = 0x02, KMIP_BATCH_ERR_CONT_UNDO = 0x03, }; enum kmip_crypto_usage_mask { KMIP_CRY_USAGE_MASK_SIGN = 0x00000001, KMIP_CRY_USAGE_MASK_VERIFY = 0x00000002, KMIP_CRY_USAGE_MASK_ENCRYPT = 0x00000004, KMIP_CRY_USAGE_MASK_DECRYPT = 0x00000008, KMIP_CRY_USAGE_MASK_WRAP_KEY = 0x00000010, KMIP_CRY_USAGE_MASK_UNWRAP_KEY = 0x00000020, KMIP_CRY_USAGE_MASK_EXPORT = 0x00000040, /* v1.x only */ KMIP_CRY_USAGE_MASK_MAC_GENERATE = 0x00000080, KMIP_CRY_USAGE_MASK_MAC_VERIFY = 0x00000100, KMIP_CRY_USAGE_MASK_DERIVE_KEY = 0x00000200, KMIP_CRY_USAGE_MASK_CONTENT_COMMITMENT = 0x00000400, /* v1.x only */ KMIP_CRY_USAGE_MASK_KEY_AGREEMENT = 0x00000800, KMIP_CRY_USAGE_MASK_CERTIFICATE_SIGN = 0x00001000, KMIP_CRY_USAGE_MASK_CLR_SIGN = 0x00002000, KMIP_CRY_USAGE_MASK_GENERATE_CRYPTOGRAM = 0x00004000, /* v1.x only */ KMIP_CRY_USAGE_MASK_VALIDATE_CRYPTOGRAM = 0x00008000, /* v1.x only */ KMIP_CRY_USAGE_MASK_TRANSLATE_ENCRYPT = 0x00010000, /* v1.x only */ KMIP_CRY_USAGE_MASK_TRANSLATE_DECRYPT = 0x00020000, /* v1.x only */ KMIP_CRY_USAGE_MASK_TRANSLATE_WRAP = 0x00040000, /* v1.x only */ KMIP_CRY_USAGE_MASK_TRANSLATE_UNWRAP = 0x00080000, /* v1.x only */ KMIP_CRY_USAGE_MASK_AUTHENTICATE = 0x00100000, /* since v2.0 */ KMIP_CRY_USAGE_MASK_UNRESTRICTED = 0x00200000, /* since v2.0 */ KMIP_CRY_USAGE_MASK_FPE_ENCRYPT = 0x00400000, /* since v2.0 */ KMIP_CRY_USAGE_MASK_FPE_DECRYPT = 0x00800000, /* since v2.0 */ }; enum kmip_result_status { KMIP_RESULT_STATUS_SUCCESS = 0x00, KMIP_RESULT_STATUS_OPERATION_FAILED = 0x01, KMIP_RESULT_STATUS_OPERATION_PENDING = 0x02, KMIP_RESULT_STATUS_OPERATION_UNDONE = 0x03, }; enum kmip_result_reason { KMIP_RESULT_REASON_ITEM_NOT_FOUND = 0x01, KMIP_RESULT_REASON_RESPONSE_TOO_LARGE = 0x02, KMIP_RESULT_REASON_AUTH_NOT_SUCCESSFUL = 0x03, KMIP_RESULT_REASON_INVALID_MESSAGE = 0x04, KMIP_RESULT_REASON_OPERATION_NOT_SUCCESSFUL = 0x05, KMIP_RESULT_REASON_MISSING_DATA = 0x06, KMIP_RESULT_REASON_INVALIUD_FIELD = 0x07, KMIP_RESULT_REASON_FEATURE_NOT_SUPPORTED = 0x08, KMIP_RESULT_REASON_OP_CANCELED_BY_REQUESTOR = 0x09, KMIP_RESULT_REASON_CRYPTOGRAPHIC_FAILURE = 0x0A, KMIP_RESULT_REASON_ILLEGAL_OPERATION = 0x0B, /* v 1.x only */ KMIP_RESULT_REASON_PERMISSION_DENIED = 0x0C, KMIP_RESULT_REASON_OBJECT_ARCHIVED = 0x0D, KMIP_RESULT_REASON_INDEX_OUT_OF_BOUNDS = 0x0E, /* v 1.x only */ KMIP_RESULT_REASON_APP_NAMESPACE_NOT_SUPPORTED = 0x0F, KMIP_RESULT_REASON_KEY_FORMAT_TYPE_NOT_SUPPORTED = 0x10, KMIP_RESULT_REASON_KEY_COMPRESSION_TYPE_NOT_SUPPORTED = 0x11, KMIP_RESULT_REASON_ENCODING_OPTION_ERROR = 0x12, /* since v1.2 */ KMIP_RESULT_REASON_KEY_VALUE_NOT_PRESENT = 0x13, /* since v1.2 */ KMIP_RESULT_REASON_ATTESTATION_REQUIRED = 0x14, /* since v1.2 */ KMIP_RESULT_REASON_ATTESTATION_FAILED = 0x15, /* since v1.2 */ KMIP_RESULT_REASON_SENSITIVE = 0x16, /* since v1.4 */ KMIP_RESULT_REASON_NOT_EXTRACTABLE = 0x17, /* since v1.4 */ KMIP_RESULT_REASON_OBJECT_ALREADY_EXISTS = 0x18, /* since v1.4 */ KMIP_RESULT_REASON_INVALID_TICKET = 0x19, /* since v2.0 */ KMIP_RESULT_REASON_USAGE_LIMIT_EXCEEDED = 0x1A, /* since v2.0 */ KMIP_RESULT_REASON_NUMERIC_RANGE = 0x1B, /* since v2.0 */ KMIP_RESULT_REASON_INVALID_DATA_TYPE = 0x1C, /* since v2.0 */ KMIP_RESULT_REASON_READ_ONLY_ATTRIBUTE = 0x1D, /* since v2.0 */ KMIP_RESULT_REASON_MULTI_VALUED_ATTRIBUTE = 0x1E, /* since v2.0 */ KMIP_RESULT_REASON_UNSUPPORTED_ATTRIBUTE = 0x1F, /* since v2.0 */ KMIP_RESULT_REASON_ATTRIBUTE_INSTANCE_NOT_FOUND = 0x20, /* since v2.0 */ KMIP_RESULT_REASON_ATTRIBUTE_NOT_FOUND = 0x21, /* since v2.0 */ KMIP_RESULT_REASON_ATTRIBUTE_READ_ONLY = 0x22, /* since v2.0 */ KMIP_RESULT_REASON_ATTRIBUTE_SINGLE_VALUED = 0x23, /* since v2.0 */ KMIP_RESULT_REASON_BAD_CRYPTOGRAPHIC_PARAMETERS = 0x24, /* since v2.0 */ KMIP_RESULT_REASON_BAD_PASSWORD = 0x25, /* since v2.0 */ KMIP_RESULT_REASON_CODEC_ERROR = 0x26, /* since v2.0 */ KMIP_RESULT_REASON_ILLEGAL_OBJECT_TYPE = 0x28, /* since v2.0 */ KMIP_RESULT_REASON_INCOMPATIBLE_CRYPTO_USAGE_MASK = 0x29, /* since v2.0 */ KMIP_RESULT_REASON_INTERNAL_SERVER_ERROR = 0x2A, /* since v2.0 */ KMIP_RESULT_REASON_INVALID_ASYNC_CORRELATION_VALUE = 0x2B, /* since v2.0 */ KMIP_RESULT_REASON_INVALID_ATTRIBUTE = 0x2C, /* since v2.0 */ KMIP_RESULT_REASON_INVALID_ATTRIBUTE_VALUE = 0x2D, /* since v2.0 */ KMIP_RESULT_REASON_INVALID_CORRELATION_VALUE = 0x2E, /* since v2.0 */ KMIP_RESULT_REASON_INVALID_CSR = 0x2F, /* since v2.0 */ KMIP_RESULT_REASON_INVALID_OBJECT_TYPE = 0x30, /* since v2.0 */ KMIP_RESULT_REASON_KEY_WRAP_TYPE_NOT_SUPPORTED = 0x32, /* since v2.0 */ KMIP_RESULT_REASON_MISSING_INITIALIZATION_VECTOR = 0x34, /* since v2.0 */ KMIP_RESULT_REASON_NOT_UNIQUE_NAME_ATTRIBUTE = 0x35, /* since v2.0 */ KMIP_RESULT_REASON_OBJECT_DESTROYED = 0x36, /* since v2.0 */ KMIP_RESULT_REASON_OBJECT_NOT_FOUND = 0x37, /* since v2.0 */ KMIP_RESULT_REASON_NOT_AUTHORISED = 0x39, /* since v2.0 */ KMIP_RESULT_REASON_SERVER_LIMIT_EXCEEDED = 0x3A, /* since v2.0 */ KMIP_RESULT_REASON_UNKNOWN_ENUMERATION = 0x3B, /* since v2.0 */ KMIP_RESULT_REASON_UNKNOWN_MESSAGE_EXTENSION = 0x3C, /* since v2.0 */ KMIP_RESULT_REASON_UNKNOWN_TAG = 0x3D, /* since v2.0 */ KMIP_RESULT_REASON_UNSUPPORTED_CRYPTO_PARAMETERS = 0x3E, /* since v2.0 */ KMIP_RESULT_REASON_UNSUPPORTED_PROTOCOL_VERSION = 0x3F, /* since v2.0 */ KMIP_RESULT_REASON_WRAPPING_OBJECT_ARCHIVED = 0x40, /* since v2.0 */ KMIP_RESULT_REASON_WRAPPING_OBJECT_DESTROYED = 0x41, /* since v2.0 */ KMIP_RESULT_REASON_WRAPPING_OBJECT_NOT_FOUND = 0x42, /* since v2.0 */ KMIP_RESULT_REASON_WRONG_KEY_LIFECYCLE_STATE = 0x43, /* since v2.0 */ KMIP_RESULT_REASON_PROTECTION_STORAGE_UNAVAILABLE = 0x44, /* since v2.0 */ KMIP_RESULT_REASON_PKCS_11_CODE_ERROR = 0x45, /* since v2.0 */ KMIP_RESULT_REASON_PKCS_11_INVALID_FUNCTION = 0x46, /* since v2.0 */ KMIP_RESULT_REASON_PKCS_11_INVALID_INTERFACE = 0x47, /* since v2.0 */ KMIP_RESULT_REASON_PRIVATE_PROT_STORAGE_UNAVAILABLE = 0x48, /* since v2.0 */ KMIP_RESULT_REASON_PUBLIC_PROT_STORAGE_UNAVAILABLE = 0x49, /* since v2.0 */ KMIP_RESULT_REASON_UNKNOWN_OBJECT_GROUP = 0x4A, /* since v2.1 */ KMIP_RESULT_REASON_CONSTRAINT_VIOLATION = 0x4B, /* since v2.1 */ KMIP_RESULT_REASON_DUPLICATE_PROCESS_REQUEST = 0x4C, /* since v2.1 */ KMIP_RESULT_REASON_GENERAL_FAILURE = 0x100, }; enum kmip_query_function { KMIP_QUERY_OPERATIONS = 0x01, KMIP_QUERY_OBJECTS = 0x02, KMIP_QUERY_SERVER_INFORMATION = 0x03, KMIP_QUERY_APPLICATION_NAMESPACES = 0x04, /* since v1.2 */ KMIP_QUERY_EXTENSION_LIST = 0x05, /* since v1.2 */ KMIP_QUERY_EXTENSION_MAP = 0x06, /* since v1.2 */ KMIP_QUERY_ATTESTATION_TYPES = 0x07, /* since v1.2 */ KMIP_QUERY_QUERY_RNGS = 0x08, /* since v1.3 */ KMIP_QUERY_VALIDATIONS = 0x09, /* since v1.3 */ KMIP_QUERY_PROFILES = 0x0A, /* since v1.3 */ KMIP_QUERY_CAPABILITIES = 0x0B, /* since v1.3 */ KMIP_QUERY_CLIENT_REGISTRATION_METHODS = 0x0C, /* since v1.3 */ KMIP_QUERY_DEFAULTS_INFORMATION = 0x0D, /* since v2.0 */ KMIP_QUERY_STORAGE_PROTECTION_MASKS = 0x0E, /* since v2.0 */ }; enum kmip_name_type { KMIP_NAME_TYPE_UNINTERPRETED_TEXT_STRING = 0x01, KMIP_NAME_TYPE_URI = 0x02, }; enum kmip_alternative_name_type { KMIP_ALT_NAME_TYPE_UNINTERPRETED_TEXT_STRING = 0x01, KMIP_ALT_NAME_TYPE_URI = 0x02, KMIP_ALT_NAME_TYPE_OBJECT_SERIAL_NUMBER = 0x03, KMIP_ALT_NAME_TYPE_EMAIL_ADDRESS = 0x04, KMIP_ALT_NAME_TYPE_DNS_NAME = 0x05, KMIP_ALT_NAME_TYPE_X_500_DISTINGUISHED_NAME = 0x06, KMIP_ALT_NAME_TYPE_IP_ADDRESS = 0x07, }; enum kmip_unique_identifier { KMIP_UNIQUE_ID_ID_PLACEHOLDER = 0x01, /* since v2.0 */ KMIP_UNIQUE_ID_CERTIFY = 0x02, /* since v2.0 */ KMIP_UNIQUE_ID_CREATE = 0x03, /* since v2.0 */ KMIP_UNIQUE_ID_CREATE_KEY_PAIR = 0x04, /* since v2.0 */ KMIP_UNIQUE_ID_CREATE_KEY_PAIR_PRIVATE = 0x05, /* since v2.0 */ KMIP_UNIQUE_ID_CREATE_KEY_PAIR_PUBLIC = 0x06, /* since v2.0 */ KMIP_UNIQUE_ID_CREATE_SPLIT_KEY = 0x07, /* since v2.0 */ KMIP_UNIQUE_ID_DERIVE_KEY = 0x08, /* since v2.0 */ KMIP_UNIQUE_ID_IMPORT = 0x09, /* since v2.0 */ KMIP_UNIQUE_ID_JOIN_SPLIT_KEY = 0x0A, /* since v2.0 */ KMIP_UNIQUE_ID_LOCATE = 0x0B, /* since v2.0 */ KMIP_UNIQUE_ID_REGISTER = 0x0C, /* since v2.0 */ KMIP_UNIQUE_ID_RE_KEY = 0x0D, /* since v2.0 */ KMIP_UNIQUE_ID_RE_CERTIFY = 0x0E, /* since v2.0 */ KMIP_UNIQUE_ID_RE_KEY_KEY_PAIR = 0x0F, /* since v2.0 */ KMIP_UNIQUE_ID_RE_KEY_KEY_PAIR_PRIVATE = 0x10, /* since v2.0 */ KMIP_UNIQUE_ID_RE_KEY_KEY_PAIR_PUBLIC = 0x11, /* since v2.0 */ }; enum kmip_object_type { KMIP_OBJECT_TYPE_CERTIFICATE = 0x01, KMIP_OBJECT_TYPE_SYMMETRIC_KEY = 0x02, KMIP_OBJECT_TYPE_PUBLIC_KEY = 0x03, KMIP_OBJECT_TYPE_PRIVATE_KEY = 0x04, KMIP_OBJECT_TYPE_SPLIT_KEY = 0x05, KMIP_OBJECT_TYPE_TEMPLATE = 0x06, /* v1.x only */ KMIP_OBJECT_TYPE_SECRET_DATA = 0x07, KMIP_OBJECT_TYPE_OPAQUE_OBJECT = 0x08, KMIP_OBJECT_TYPE_PGP_KEY = 0x09, /* since v1.2 */ KMIP_OBJECT_TYPE_CERTIFICATE_REQUEST = 0x0A, /* since v2.0 */ }; enum kmip_crypto_algo { KMIP_CRYPTO_ALGO_DES = 0x01, KMIP_CRYPTO_ALGO_3DES = 0x02, KMIP_CRYPTO_ALGO_AES = 0x03, KMIP_CRYPTO_ALGO_RSA = 0x04, KMIP_CRYPTO_ALGO_DSA = 0x05, KMIP_CRYPTO_ALGO_ECDSA = 0x06, KMIP_CRYPTO_ALGO_HMAC_SHA1 = 0x07, KMIP_CRYPTO_ALGO_HMAC_SHA224 = 0x08, KMIP_CRYPTO_ALGO_HMAC_SHA256 = 0x09, KMIP_CRYPTO_ALGO_HMAC_SHA384 = 0x0A, KMIP_CRYPTO_ALGO_HMAC_SHA512 = 0x0B, KMIP_CRYPTO_ALGO_HMAC_MD5 = 0x0C, KMIP_CRYPTO_ALGO_DH = 0x0D, KMIP_CRYPTO_ALGO_ECDH = 0x0E, KMIP_CRYPTO_ALGO_ECMQV = 0x0F, KMIP_CRYPTO_ALGO_BLOWFISH = 0x10, KMIP_CRYPTO_ALGO_CAMELLIA = 0x11, KMIP_CRYPTO_ALGO_CAST5 = 0x12, KMIP_CRYPTO_ALGO_IDEA = 0x13, KMIP_CRYPTO_ALGO_MARS = 0x14, KMIP_CRYPTO_ALGO_RC2 = 0x15, KMIP_CRYPTO_ALGO_RC4 = 0x16, KMIP_CRYPTO_ALGO_RC5 = 0x17, KMIP_CRYPTO_ALGO_SKIPJACK = 0x18, KMIP_CRYPTO_ALGO_TWOFISH = 0x19, KMIP_CRYPTO_ALGO_EC = 0x1A, /* since v1.2 */ KMIP_CRYPTO_ALGO_ONE_TIME_PAD = 0x1B, /* since v1.3 */ KMIP_CRYPTO_ALGO_CHACHA20 = 0x1C, /* since v1.4 */ KMIP_CRYPTO_ALGO_POLY1305 = 0x1D, /* since v1.4 */ KMIP_CRYPTO_ALGO_CHACHA20_POLY1305 = 0x1E, /* since v1.4 */ KMIP_CRYPTO_ALGO_SHA3_224 = 0x1F, /* since v1.4 */ KMIP_CRYPTO_ALGO_SHA3_256 = 0x20, /* since v1.4 */ KMIP_CRYPTO_ALGO_SHA3_384 = 0x21, /* since v1.4 */ KMIP_CRYPTO_ALGO_SHA3_512 = 0x22, /* since v1.4 */ KMIP_CRYPTO_ALGO_HMAC_SHA3_224 = 0x23, /* since v1.4 */ KMIP_CRYPTO_ALGO_HMAC_SHA3_256 = 0x24, /* since v1.4 */ KMIP_CRYPTO_ALGO_HMAC_SHA3_384 = 0x25, /* since v1.4 */ KMIP_CRYPTO_ALGO_HMAC_SHA3_512 = 0x26, /* since v1.4 */ KMIP_CRYPTO_ALGO_SHAKE_128 = 0x27, /* since v1.4 */ KMIP_CRYPTO_ALGO_SHAKE_256 = 0x28, /* since v1.4 */ KMIP_CRYPTO_ALGO_ARIA = 0x29, /* since v2.0 */ KMIP_CRYPTO_ALGO_SEED = 0x2A, /* since v2.0 */ KMIP_CRYPTO_ALGO_SM2 = 0x2B, /* since v2.0 */ KMIP_CRYPTO_ALGO_SM3 = 0x2C, /* since v2.0 */ KMIP_CRYPTO_ALGO_SM4 = 0x2D, /* since v2.0 */ KMIP_CRYPTO_ALGO_GOST_R34_10_2012 = 0x2E, /* since v2.0 */ KMIP_CRYPTO_ALGO_GOST_R34_11_2012 = 0x2F, /* since v2.0 */ KMIP_CRYPTO_ALGO_GOST_R34_13_2015 = 0x30, /* since v2.0 */ KMIP_CRYPTO_ALGO_GOST_28147_89 = 0x31, /* since v2.0 */ KMIP_CRYPTO_ALGO_XMSS = 0x32, /* since v2.0 */ KMIP_CRYPTO_ALGO_SPHINCS_256 = 0x33, /* since v2.0 */ KMIP_CRYPTO_ALGO_MCELIECE = 0x34, /* since v2.0 */ KMIP_CRYPTO_ALGO_MCELIECE_6960119 = 0x35, /* since v2.0 */ KMIP_CRYPTO_ALGO_MCELIECE_8192128 = 0x36, /* since v2.0 */ KMIP_CRYPTO_ALGO_ED25519 = 0x37, /* since v2.0 */ KMIP_CRYPTO_ALGO_ED448 = 0x38, /* since v2.0 */ }; enum kmip_certificate_type { KMIP_CERTIFICATE_TYPE_X_509 = 0x01, KMIP_CERTIFICATE_TYPE_PGP = 0x02, }; enum kmip_state { KMIP_STATE_PRE_ACTIVE = 0x01, KMIP_STATE_ACTIVE = 0x02, KMIP_STATE_DEACTIVATED = 0x03, KMIP_STATE_COMPROMISED = 0x04, KMIP_STATE_DESTROYED = 0x05, KMIP_STATE_DESTROYED_COMPROMISED = 0x06, }; enum kmip_protection_storage_mask { KMIP_PROT_STORAGE_MASK_SOFTWARE = 0x00000001, KMIP_PROT_STORAGE_MASK_HARDWARE = 0x00000002, KMIP_PROT_STORAGE_MASK_ON_PROCESSOR = 0x00000004, KMIP_PROT_STORAGE_MASK_ON_SYSTEM = 0x00000008, KMIP_PROT_STORAGE_MASK_OFF_SYSTEM = 0x00000010, KMIP_PROT_STORAGE_MASK_HYPERVISOR = 0x00000020, KMIP_PROT_STORAGE_MASK_OPERATING_SYSTEM = 0x00000040, KMIP_PROT_STORAGE_MASK_CONTAINER = 0x00000080, KMIP_PROT_STORAGE_MASK_ON_PREMISES = 0x00000100, KMIP_PROT_STORAGE_MASK_OFF_PREMISES = 0x00000200, KMIP_PROT_STORAGE_MASK_SELF_MANAGED = 0x00000400, KMIP_PROT_STORAGE_MASK_OUTSOURCED = 0x00000800, KMIP_PROT_STORAGE_MASK_VALIDATED = 0x00001000, KMIP_PROT_STORAGE_MASK_SAME_JURISDICATION = 0x00002000, }; enum kmip_revoke_reason { KMIP_REVOK_RSN_UNSPECIFIED = 0x01, KMIP_REVOK_RSN_KEY_COMPROMISE = 0x02, KMIP_REVOK_RSN_CA_COMPROMISE = 0x03, KMIP_REVOK_RSN_AFFILIATION_CHANGED = 0x04, KMIP_REVOK_RSN_SUPERSEDED = 0x05, KMIP_REVOK_RSN_CESSATION_OF_OPERATION = 0x06, KMIP_REVOK_RSN_PRIVILEGE_WITHDRAWN = 0x07, }; enum kmip_object_group_member { KMIP_OBJ_GROUP_MEMBER_FRESH = 0x01, KMIP_OBJ_GROUP_MEMBER_DEFAULT = 0x02, }; enum kmip_storage_status_mask { KMIP_STORAGE_STATUS_MASK_ONLINE = 0x01, KMIP_STORAGE_STATUS_MASK_ARCHIVAL = 0x02, KMIP_STORAGE_STATUS_MASK_DESTTROYED = 0x04, }; enum kmip_key_format_type { KMIP_KEY_FORMAT_TYPE_RAW = 0x01, KMIP_KEY_FORMAT_TYPE_OPAQUE = 0x02, KMIP_KEY_FORMAT_TYPE_PKCS_1 = 0x03, KMIP_KEY_FORMAT_TYPE_PKCS_8 = 0x04, KMIP_KEY_FORMAT_TYPE_X_509 = 0x05, KMIP_KEY_FORMAT_TYPE_EC_PRIVATE_KEY = 0x06, KMIP_KEY_FORMAT_TYPE_TRANSPARENT_SYMMETRIC_KEY = 0x07, KMIP_KEY_FORMAT_TYPE_TRANSPARENT_DSA_PRIVATE_KEY = 0x08, KMIP_KEY_FORMAT_TYPE_TRANSPARENT_DSA_PUBLIC_KEY = 0x09, KMIP_KEY_FORMAT_TYPE_TRANSPARENT_RSA_PRIVATE_KEY = 0x0A, KMIP_KEY_FORMAT_TYPE_TRANSPARENT_RSA_PUBLIC_KEY = 0x0B, KMIP_KEY_FORMAT_TYPE_TRANSPARENT_DH_PRIVATE_KEY = 0x0C, KMIP_KEY_FORMAT_TYPE_TRANSPARENT_DH_PUBLIC_KEY = 0x0D, KMIP_KEY_FORMAT_TYPE_TRANSPARENT_ECDSA_PRIVATE_KEY = 0x0E, /* deprecated since v1.3 */ KMIP_KEY_FORMAT_TYPE_TRANSPARENT_ECDSA_PUBLIC_KEY = 0x0F, /* deprecated since v1.3 */ KMIP_KEY_FORMAT_TYPE_TRANSPARENT_ECDH_PRIVATE_KEY = 0x10, /* deprecated since v1.3 */ KMIP_KEY_FORMAT_TYPE_TRANSPARENT_ECDH_PUBLIC_KEY = 0x11, /* deprecated since v1.3 */ KMIP_KEY_FORMAT_TYPE_TRANSPARENT_ECMQV_PRIVATE_KEY = 0x12, /* deprecated since v1.3 */ KMIP_KEY_FORMAT_TYPE_TRANSPARENT_ECMQV_PUBLIC_KEY = 0x13, /* deprecated since v1.3 */ KMIP_KEY_FORMAT_TYPE_TRANSPARENT_EC_PRIVATE_KEY = 0x14, /* since v1.3 */ KMIP_KEY_FORMAT_TYPE_TRANSPARENT_EC_PUBLIC_KEY = 0x15, /* since v1.3 */ KMIP_KEY_FORMAT_TYPE_PKCS_12 = 0x16, /* since v1.4 */ KMIP_KEY_FORMAT_TYPE_PKCS_10 = 0x17, /* since v2.0 */ }; enum kmip_key_compression_type { KMIP_KEY_COMPRESSION_TYPE_EC_PUBKEY_UNCOMPRESSED = 0x01, KMIP_KEY_COMPRESSION_TYPE_EC_PUBKEY_COMPRESSED_PRIME = 0x02, KMIP_KEY_COMPRESSION_TYPE_EC_PUBKEY_COMPRESSED_CHAR2 = 0x03, KMIP_KEY_COMPRESSION_TYPE_EC_PUBKEY_HYBID = 0x04, }; enum kmip_wrapping_method { KMIP_WRAPPING_METHOD_ENCRYPT = 0x01, KMIP_WRAPPING_METHOD_MAC_SIGN = 0x02, KMIP_WRAPPING_METHOD_ENCRYPT_THEN_MAC_SIGN = 0x03, KMIP_WRAPPING_METHOD_MAC_SIGN_THEN_ENCRYPT = 0x04, KMIP_WRAPPING_METHOD_TR_31 = 0x05, }; enum kmip_key_wrap_type { KMIP_KEY_WRAP_TYPE_NOT_WRAPPED = 0x01, KMIP_KEY_WRAP_TYPE_AS_REGISTERED = 0x02, }; enum kmip_block_cipher_mode { KMIP_BLOCK_CIPHER_MODE_CBC = 0x01, KMIP_BLOCK_CIPHER_MODE_ECB = 0x02, KMIP_BLOCK_CIPHER_MODE_PCBC = 0x03, KMIP_BLOCK_CIPHER_MODE_CFB = 0x04, KMIP_BLOCK_CIPHER_MODE_OFB = 0x05, KMIP_BLOCK_CIPHER_MODE_CTR = 0x06, KMIP_BLOCK_CIPHER_MODE_CMAC = 0x07, KMIP_BLOCK_CIPHER_MODE_CCM = 0x08, KMIP_BLOCK_CIPHER_MODE_GCM = 0x09, KMIP_BLOCK_CIPHER_MODE_CBC_MAC = 0x0A, KMIP_BLOCK_CIPHER_MODE_XTS = 0x0B, KMIP_BLOCK_CIPHER_MODE_AES_KEY_WRAP_PADDING = 0x0C, KMIP_BLOCK_CIPHER_MODE_NIST_KEY_WRAP = 0x0D, KMIP_BLOCK_CIPHER_MODE_X9_102_AESKW = 0x0E, KMIP_BLOCK_CIPHER_MODE_X9_102_TDKW = 0x0F, KMIP_BLOCK_CIPHER_MODE_X9_102_AKW1 = 0x10, KMIP_BLOCK_CIPHER_MODE_X9_102_AKW2 = 0x11, KMIP_BLOCK_CIPHER_MODE_AEAD = 0x12, /* since v1.4 */ }; enum kmip_padding_method { KMIP_PADDING_METHOD_NONE = 0x01, KMIP_PADDING_METHOD_OAEP = 0x02, KMIP_PADDING_METHOD_PKCS5 = 0x03, KMIP_PADDING_METHOD_SSL3 = 0x04, KMIP_PADDING_METHOD_ZEROS = 0x05, KMIP_PADDING_METHOD_ANSI_X9_23 = 0x06, KMIP_PADDING_METHOD_ISO_10126 = 0x07, KMIP_PADDING_METHOD_PKCS_1_5 = 0x08, KMIP_PADDING_METHOD_X9_31 = 0x09, KMIP_PADDING_METHOD_PSS = 0x0A, }; enum kmip_hashing_algo { KMIP_HASHING_ALGO_MD2 = 0x01, KMIP_HASHING_ALGO_MD4 = 0x02, KMIP_HASHING_ALGO_MD5 = 0x03, KMIP_HASHING_ALGO_SHA_1 = 0x04, KMIP_HASHING_ALGO_SHA_224 = 0x05, KMIP_HASHING_ALGO_SHA_256 = 0x06, KMIP_HASHING_ALGO_SHA_384 = 0x07, KMIP_HASHING_ALGO_SHA_512 = 0x08, KMIP_HASHING_ALGO_RIPEMD_160 = 0x09, KMIP_HASHING_ALGO_TIGER = 0x0A, KMIP_HASHING_ALGO_WIRLPOOL = 0x0B, KMIP_HASHING_ALGO_SHA_512_224 = 0x0C, /* since v1.2 */ KMIP_HASHING_ALGO_SHA_512_256 = 0x0D, /* since v1.2 */ KMIP_HASHING_ALGO_SHA_3_224 = 0x0E, /* since v1.4 */ KMIP_HASHING_ALGO_SHA_3_256 = 0x0F, /* since v1.4 */ KMIP_HASHING_ALGO_SHA_3_384 = 0x10, /* since v1.4 */ KMIP_HASHING_ALGO_SHA_3_512 = 0x11, /* since v1.4 */ }; enum kmip_key_role_type { KMIP_KEY_ROLE_TYPE_BDK = 0x01, KMIP_KEY_ROLE_TYPE_CVK = 0x02, KMIP_KEY_ROLE_TYPE_DEK = 0x03, KMIP_KEY_ROLE_TYPE_KMAC = 0x04, KMIP_KEY_ROLE_TYPE_MKSMC = 0x05, KMIP_KEY_ROLE_TYPE_MKSMI = 0x06, KMIP_KEY_ROLE_TYPE_MKDAC = 0x07, KMIP_KEY_ROLE_TYPE_MKDN = 0x08, KMIP_KEY_ROLE_TYPE_MKCP = 0x09, KMIP_KEY_ROLE_TYPE_MKOTH = 0x0A, KMIP_KEY_ROLE_TYPE_KEK = 0x0B, KMIP_KEY_ROLE_TYPE_MAC16609 = 0x0C, KMIP_KEY_ROLE_TYPE_MAC97971 = 0x0D, KMIP_KEY_ROLE_TYPE_MAC97972 = 0x0E, KMIP_KEY_ROLE_TYPE_MAC97973 = 0x0F, KMIP_KEY_ROLE_TYPE_MAC97974 = 0x10, KMIP_KEY_ROLE_TYPE_MAC97975 = 0x11, KMIP_KEY_ROLE_TYPE_ZPK = 0x12, KMIP_KEY_ROLE_TYPE_PVKIBM = 0x13, KMIP_KEY_ROLE_TYPE_PVKPVV = 0x14, KMIP_KEY_ROLE_TYPE_PVKOTH = 0x15, KMIP_KEY_ROLE_TYPE_DUKPT = 0x16, /* since v1.4 */ KMIP_KEY_ROLE_TYPE_IV = 0x17, /* since v1.4 */ KMIP_KEY_ROLE_TYPE_TRKBK = 0x18, /* since v1.4 */ }; enum kmip_signature_algo { KMIP_SIGNATURE_ALGO_MD2_WITH_RSA_ENCRYPTION = 0x01, /* since v1.2 */ KMIP_SIGNATURE_ALGO_MD5_WITH_RSA_ENCRYPTION = 0x02, /* since v1.2 */ KMIP_SIGNATURE_ALGO_SHA_1_WITH_RSA_ENCRYPTION = 0x03, /* since v1.2 */ KMIP_SIGNATURE_ALGO_SHA_224_WITH_RSA_ENCRYPTION = 0x04, /* since v1.2 */ KMIP_SIGNATURE_ALGO_SHA_256_WITH_RSA_ENCRYPTION = 0x05, /* since v1.2 */ KMIP_SIGNATURE_ALGO_SHA_384_WITH_RSA_ENCRYPTION = 0x06, /* since v1.2 */ KMIP_SIGNATURE_ALGO_SHA_512_WITH_RSA_ENCRYPTION = 0x07, /* since v1.2 */ KMIP_SIGNATURE_ALGO_RSASSA_PSS = 0x08, /* since v1.2 */ KMIP_SIGNATURE_ALGO_DSA_WITH_SHA_1 = 0x09, /* since v1.2 */ KMIP_SIGNATURE_ALGO_DSA_WITH_SHA_244 = 0x0A, /* since v1.2 */ KMIP_SIGNATURE_ALGO_DSA_WITH_SHA_256 = 0x0B, /* since v1.2 */ KMIP_SIGNATURE_ALGO_ECDSA_WITH_SHA_1 = 0x0C, /* since v1.2 */ KMIP_SIGNATURE_ALGO_ECDSA_WITH_SHA_224 = 0x0D, /* since v1.2 */ KMIP_SIGNATURE_ALGO_ECDSA_WITH_SHA_256 = 0x0E, /* since v1.2 */ KMIP_SIGNATURE_ALGO_ECDSA_WITH_SHA_384 = 0x0F, /* since v1.2 */ KMIP_SIGNATURE_ALGO_ECDSA_WITH_SHA_512 = 0x10, /* since v1.2 */ KMIP_SIGNATURE_ALGO_SHA3_256_WITH_RSA_ENCRYPTION = 0x11, /* since v1.4 */ KMIP_SIGNATURE_ALGO_SHA3_385_WITH_RSA_ENCRYPTION = 0x12, /* since v1.4 */ KMIP_SIGNATURE_ALGO_SHA3_512_WITH_RSA_ENCRYPTION = 0x13, /* since v1.4 */ }; enum kmip_mask_generator { KMIP_MASK_GENERATOR_MGF1 = 0x01, /* since v1.4 */ }; enum kmip_encoding_option { KMIP_ENCODING_OPTION_NO = 0x01, /* since v1.2 */ KMIP_ENCODING_OPTION_TTLV = 0x02, /* since v1.2 */ }; enum kmip_recommended_curve { KMIP_REC_CURVE_P_192 = 0x01, KMIP_REC_CURVE_K_163 = 0x02, KMIP_REC_CURVE_B_163 = 0x03, KMIP_REC_CURVE_P_224 = 0x04, KMIP_REC_CURVE_K_223 = 0x05, KMIP_REC_CURVE_B_223 = 0x06, KMIP_REC_CURVE_P_256 = 0x07, KMIP_REC_CURVE_K_283 = 0x08, KMIP_REC_CURVE_B_283 = 0x09, KMIP_REC_CURVE_P_384 = 0x0A, KMIP_REC_CURVE_K_409 = 0x0B, KMIP_REC_CURVE_B_409 = 0x0C, KMIP_REC_CURVE_P_521 = 0x0D, KMIP_REC_CURVE_K_571 = 0x0E, KMIP_REC_CURVE_B_571 = 0x0F, KMIP_REC_CURVE_SECP112R1 = 0x10, /* since v1.2 */ KMIP_REC_CURVE_SECP112R2 = 0x11, /* since v1.2 */ KMIP_REC_CURVE_SECP128R1 = 0x12, /* since v1.2 */ KMIP_REC_CURVE_SECP128R2 = 0x13, /* since v1.2 */ KMIP_REC_CURVE_SECP160K1 = 0x14, /* since v1.2 */ KMIP_REC_CURVE_SECP160R1 = 0x15, /* since v1.2 */ KMIP_REC_CURVE_SECP160R2 = 0x16, /* since v1.2 */ KMIP_REC_CURVE_SECP192K1 = 0x17, /* since v1.2 */ KMIP_REC_CURVE_SECP224K1 = 0x18, /* since v1.2 */ KMIP_REC_CURVE_SECP256K1 = 0x19, /* since v1.2 */ KMIP_REC_CURVE_SECT113R1 = 0x1A, /* since v1.2 */ KMIP_REC_CURVE_SECT113R2 = 0x1B, /* since v1.2 */ KMIP_REC_CURVE_SECT131R1 = 0x1C, /* since v1.2 */ KMIP_REC_CURVE_SECT131R2 = 0x1D, /* since v1.2 */ KMIP_REC_CURVE_SECT163R1 = 0x1E, /* since v1.2 */ KMIP_REC_CURVE_SECT193R1 = 0x1F, /* since v1.2 */ KMIP_REC_CURVE_SECT193R2 = 0x20, /* since v1.2 */ KMIP_REC_CURVE_SECT239K1 = 0x21, /* since v1.2 */ KMIP_REC_CURVE_ANSIX9P192V2 = 0x22, /* since v1.2 */ KMIP_REC_CURVE_ANSIX9P192V3 = 0x23, /* since v1.2 */ KMIP_REC_CURVE_ANSIX9P239V1 = 0x24, /* since v1.2 */ KMIP_REC_CURVE_ANSIX9P239V2 = 0x25, /* since v1.2 */ KMIP_REC_CURVE_ANSIX9P239V3 = 0x26, /* since v1.2 */ KMIP_REC_CURVE_ANSIX9C2PNB163V1 = 0x27, /* since v1.2 */ KMIP_REC_CURVE_ANSIX9C2PNB163V2 = 0x28, /* since v1.2 */ KMIP_REC_CURVE_ANSIX9C2PNB163V3 = 0x29, /* since v1.2 */ KMIP_REC_CURVE_ANSIX9C2PNB176V1 = 0x2A, /* since v1.2 */ KMIP_REC_CURVE_ANSIX9C2TNB191V1 = 0x2B, /* since v1.2 */ KMIP_REC_CURVE_ANSIX9C2TNB191V2 = 0x2C, /* since v1.2 */ KMIP_REC_CURVE_ANSIX9C2TNB191V3 = 0x2D, /* since v1.2 */ KMIP_REC_CURVE_ANSIX9C2PNB208W1 = 0x2E, /* since v1.2 */ KMIP_REC_CURVE_ANSIX9C2TNB239V1 = 0x2F, /* since v1.2 */ KMIP_REC_CURVE_ANSIX9C2TNB239V2 = 0x30, /* since v1.2 */ KMIP_REC_CURVE_ANSIX9C2TNB239V3 = 0x31, /* since v1.2 */ KMIP_REC_CURVE_ANSIX9C2PNB272W1 = 0x32, /* since v1.2 */ KMIP_REC_CURVE_ANSIX9C2PNB304W1 = 0x33, /* since v1.2 */ KMIP_REC_CURVE_ANSIX9C2TNB359V1 = 0x34, /* since v1.2 */ KMIP_REC_CURVE_ANSIX9C2PNB368W1 = 0x35, /* since v1.2 */ KMIP_REC_CURVE_ANSIX9C2TNB431R1 = 0x36, /* since v1.2 */ KMIP_REC_CURVE_BRAINPOOLP160R1 = 0x37, /* since v1.2 */ KMIP_REC_CURVE_BRAINPOOLP160T1 = 0x38, /* since v1.2 */ KMIP_REC_CURVE_BRAINPOOLP192R1 = 0x39, /* since v1.2 */ KMIP_REC_CURVE_BRAINPOOLP192T1 = 0x3A, /* since v1.2 */ KMIP_REC_CURVE_BRAINPOOLP224R1 = 0x3B, /* since v1.2 */ KMIP_REC_CURVE_BRAINPOOLP224T1 = 0x3C, /* since v1.2 */ KMIP_REC_CURVE_BRAINPOOLP256R1 = 0x3D, /* since v1.2 */ KMIP_REC_CURVE_BRAINPOOLP256T1 = 0x3E, /* since v1.2 */ KMIP_REC_CURVE_BRAINPOOLP320R1 = 0x3F, /* since v1.2 */ KMIP_REC_CURVE_BRAINPOOLP320T1 = 0x40, /* since v1.2 */ KMIP_REC_CURVE_BRAINPOOLP384R1 = 0x41, /* since v1.2 */ KMIP_REC_CURVE_BRAINPOOLP384T1 = 0x42, /* since v1.2 */ KMIP_REC_CURVE_BRAINPOOLP512R1 = 0x43, /* since v1.2 */ KMIP_REC_CURVE_BRAINPOOLP512T1 = 0x44, /* since v1.2 */ KMIP_REC_CURVE_CURVE25519 = 0x45, /* since v2.0 */ KMIP_REC_CURVE_CURVE448 = 0x46, /* since v2.0 */ }; enum kmip_protection_level { KMIP_PROTECTION_LEVEL_HIGH = 0x01, /* since v2.0 */ KMIP_PROTECTION_LEVEL_LOW = 0x02, /* since v2.0 */ }; enum kmip_key_value_location_type { KMIP_KEY_VAL_LOC_TYPE_UNINTERPRETED_TEXT_STRING = 0x01, KMIP_KEY_VAL_LOC_TYPE_URI = 0x02, }; enum kmip_link_type { KMIP_LINK_TYPE_CERTIFICATE = 0x0101, KMIP_LINK_TYPE_PUBLIC_KEY = 0x0102, KMIP_LINK_TYPE_PRIVATE_KEY = 0x0103, KMIP_LINK_TYPE_DERIVATION_BASE_OBJECT = 0x0104, KMIP_LINK_TYPE_DERIVED_KEY = 0x0105, KMIP_LINK_TYPE_REPLACEMENT_OBJECT = 0x0106, KMIP_LINK_TYPE_REPLACED_OBJECT = 0x0107, KMIP_LINK_TYPE_PARENT = 0x0108, /* since v1.2 */ KMIP_LINK_TYPE_CHILD = 0x0109, /* since v1.2 */ KMIP_LINK_TYPE_PREVIOUS = 0x010A, /* since v1.2 */ KMIP_LINK_TYPE_NEXT = 0x010B, /* since v1.2 */ KMIP_LINK_TYPE_PKCS_12_CERTIFICATE = 0x010C, /* since v1.4 */ KMIP_LINK_TYPE_PKCS_12_PASSWORD = 0x010D, /* since v1.4 */ KMIP_LINK_TYPE_WRAPPING_KEY = 0x010E, /* since v2.0 */ }; enum kmip_client_registration_method { KMIP_CLIENT_REG_METH_UNSPECIFIED = 0x01, /* since v1.3 */ KMIP_CLIENT_REG_METH_SERVER_PRE_GENERATED = 0x02, /* since v1.3 */ KMIP_CLIENT_REG_METH_SERVER_ON_DEMAND = 0x03, /* since v1.3 */ KMIP_CLIENT_REG_METH_CLIENT_GENERATED = 0x04, /* since v1.3 */ KMIP_CLIENT_REG_METH_CLIENT_REGISTERED = 0x05, /* since v1.3 */ }; enum kmip_rng_algorithm { KMIP_RNG_ALGO_UNSPECIFIED = 0x01, /* since v1.3 */ KMIP_RNG_ALGO_FIPS_186_2 = 0x02, /* since v1.3 */ KMIP_RNG_ALGO_DRBG = 0x03, /* since v1.3 */ KMIP_RNG_ALGO_NRBG = 0x04, /* since v1.3 */ KMIP_RNG_ALGO_ANSI_X9_31 = 0x05, /* since v1.3 */ KMIP_RNG_ALGO_ANSI_X9_62 = 0x06, /* since v1.3 */ }; enum kmip_drbg_algorithm { KMIP_DRBG_ALGO_UNSPECIFIED = 0x01, /* since v1.3 */ KMIP_DRBG_ALGO_DUAL_EC = 0x02, /* since v1.3 */ KMIP_DRBG_ALGO_HASH = 0x03, /* since v1.3 */ KMIP_DRBG_ALGO_HMAC = 0x04, /* since v1.3 */ KMIP_DRBG_ALGO_CTR = 0x05, /* since v1.3 */ }; enum kmip_fips186_variation { KMIP_FIPS186_VARI_UNSPECIFIED = 0x01, /* since v1.3 */ KMIP_FIPS186_VARI_GP_X_ORIGINAL = 0x02, /* since v1.3 */ KMIP_FIPS186_VARI_GP_X_CHANGE_NOTICE = 0x03, /* since v1.3 */ KMIP_FIPS186_VARI_X_ORIGINAL = 0x04, /* since v1.3 */ KMIP_FIPS186_VARI_X_CHANGE_NOTICE = 0x05, /* since v1.3 */ KMIP_FIPS186_VARI_K_ORIGINAL = 0x06, /* since v1.3 */ KMIP_FIPS186_VARI_K_CHANGE_NOTICE = 0x07, /* since v1.3 */ }; enum kmip_validation_authority_type { KMIP_VALIDATION_AUTH_TYPE_UNSPECIFIED = 0x01, /* since v1.3 */ KMIP_VALIDATION_AUTH_TYPE_NIST_CMVP = 0x02, /* since v1.3 */ KMIP_VALIDATION_AUTH_TYPE_COMMON_CRITERIA = 0x03, /* since v1.3 */ }; enum kmip_validation_type { KMIP_VALIDATION_TYPE_UNSPECIFIED = 0x01, /* since v1.3 */ KMIP_VALIDATION_TYPE_HARDWARE = 0x02, /* since v1.3 */ KMIP_VALIDATION_TYPE_SOFTWARE = 0x03, /* since v1.3 */ KMIP_VALIDATION_TYPE_FIRMWARE = 0x04, /* since v1.3 */ KMIP_VALIDATION_TYPE_HYBRID = 0x05, /* since v1.3 */ }; enum kmip_unwrap_mode { KMIP_UNWRAP_MODE_UNSPECIFIED = 0x01, /* since v1.3 */ KMIP_UNWRAP_MODE_PROCESSED = 0x02, /* since v1.3 */ KMIP_UNWRAP_MODE_NOT_PROCESSED = 0x03, /* since v1.3 */ }; enum kmip_destroy_action { KMIP_DESTROY_ACTION_UNSPECIFIED = 0x01, /* since v1.3 */ KMIP_DESTROY_ACTION_KEY_MATERIAL_DELETED = 0x02, /* since v1.3 */ KMIP_DESTROY_ACTION_KEY_MATERIAL_SHREDDED = 0x03, /* since v1.3 */ KMIP_DESTROY_ACTION_META_DATA_DELETED = 0x04, /* since v1.3 */ KMIP_DESTROY_ACTION_META_DATA_SHREDDED = 0x05, /* since v1.3 */ KMIP_DESTROY_ACTION_DELETED = 0x06, /* since v1.3 */ KMIP_DESTROY_ACTION_SHREDDED = 0x07, /* since v1.3 */ }; enum kmip_shredding_algorithm { KMIP_SHREDDING_ALGO_UNSPECIFIED = 0x01, /* since v1.3 */ KMIP_SHREDDING_ALGO_CRYPTOGRAPHIC = 0x02, /* since v1.3 */ KMIP_SHREDDING_ALGO_UNSUPPORTED = 0x03, /* since v1.3 */ }; enum kmip_rng_mode { KMIP_RNG_MODE_UNSPECIFIED = 0x01, /* since v1.3 */ KMIP_RNG_MODE_SHARED_INSTANTIATION = 0x02, /* since v1.3 */ KMIP_RNG_MODE_NON_SHARED_INSTANCIATION = 0x03, /* since v1.3 */ }; enum kmip_KMIP_PROFILE_s { KMIP_PROFILE_BASELINE_SERVER_BASIC_KMIP_V1_2 = 0x0001, KMIP_PROFILE_BASELINE_SERVER_TLS_V1_2_KMIP_V1_2 = 0x0002, KMIP_PROFILE_BASELINE_CLIENT_BASIC_KMIP_V1_2 = 0x0003, KMIP_PROFILE_BASELINE_CLIENT_TLS_V1_2_KMIP_V1_2 = 0x0004, KMIP_PROFILE_COMPLETE_SERVER_BASIC_KMIP_V1_2 = 0x0005, KMIP_PROFILE_COMPLETE_SERVER_TLS_V1_2_KMIP_V1_2 = 0x0006, KMIP_PROFILE_TAPE_LIBRARY_CLIENT_KMIP_V1_0 = 0x0007, KMIP_PROFILE_TAPE_LIBRARY_CLIENT_KMIP_V1_1 = 0x0008, KMIP_PROFILE_TAPE_LIBRARY_CLIENT_KMIP_V1_2 = 0x0009, KMIP_PROFILE_TAPE_LIBRARY_SERVER_KMIP_V1_0 = 0x000A, KMIP_PROFILE_TAPE_LIBRARY_SERVER_KMIP_V1_1 = 0x000B, KMIP_PROFILE_TAPE_LIBRARY_SERVER_KMIP_V1_2 = 0x000C, KMIP_PROFILE_SYMMETRIC_KEY_LIFECYCLE_CLIENT_KMIP_V1_0 = 0x000D, KMIP_PROFILE_SYMMETRIC_KEY_LIFECYCLE_CLIENT_KMIP_V1_1 = 0x000E, KMIP_PROFILE_SYMMETRIC_KEY_LIFECYCLE_CLIENT_KMIP_V1_2 = 0x000F, KMIP_PROFILE_SYMMETRIC_KEY_LIFECYCLE_SERVER_KMIP_V1_0 = 0x0010, KMIP_PROFILE_SYMMETRIC_KEY_LIFECYCLE_SERVER_KMIP_V1_1 = 0x0011, KMIP_PROFILE_SYMMETRIC_KEY_LIFECYCLE_SERVER_KMIP_V1_2 = 0x0012, KMIP_PROFILE_ASYMMETRIC_KEY_LIFECYCLE_CLIENT_KMIP_V1_0 = 0x0013, KMIP_PROFILE_ASYMMETRIC_KEY_LIFECYCLE_CLIENT_KMIP_V1_1 = 0x0014, KMIP_PROFILE_ASYMMETRIC_KEY_LIFECYCLE_CLIENT_KMIP_V1_2 = 0x0015, KMIP_PROFILE_ASYMMETRIC_KEY_LIFECYCLE_SERVER_KMIP_V1_0 = 0x0016, KMIP_PROFILE_ASYMMETRIC_KEY_LIFECYCLE_SERVER_KMIP_V1_1 = 0x0017, KMIP_PROFILE_ASYMMETRIC_KEY_LIFECYCLE_SERVER_KMIP_V1_2 = 0x0018, KMIP_PROFILE_BASIC_CRYPTOGRAPHIC_CLIENT_KMIP_V1_2 = 0x0019, KMIP_PROFILE_BASIC_CRYPTOGRAPHIC_SERVER_KMIP_V1_2 = 0x001A, KMIP_PROFILE_ADVANCED_CRYPTOGRAPHIC_CLIENT_KMIP_V1_2 = 0x001B, KMIP_PROFILE_ADVANCED_CRYPTOGRAPHIC_SERVER_KMIP_V1_2 = 0x001C, KMIP_PROFILE_RNG_CRYPTOGRAPHIC_CLIENT_KMIP_V1_2 = 0x001D, KMIP_PROFILE_RNG_CRYPTOGRAPHIC_SERVER_KMIP_V1_2 = 0x001E, KMIP_PROFILE_BASIC_SYMMETRIC_KEY_FOUNDRY_CLIENT_KMIP_V1_0 = 0x001F, KMIP_PROFILE_INTERMEDIATE_SYMMETRIC_KEY_FOUNDRY_CLIENT_KMIP_V1_0 = 0x0020, KMIP_PROFILE_ADVANCED_SYMMETRIC_KEY_FOUNDRY_CLIENT_KMIP_V1_0 = 0x0021, KMIP_PROFILE_BASIC_SYMMETRIC_KEY_FOUNDRY_CLIENT_KMIP_V1_1 = 0x0022, KMIP_PROFILE_INTERMEDIATE_SYMMETRIC_KEY_FOUNDRY_CLIENT_KMIP_V1_1 = 0x0023, KMIP_PROFILE_ADVANCED_SYMMETRIC_KEY_FOUNDRY_CLIENT_KMIP_V1_1 = 0x0024, KMIP_PROFILE_BASIC_SYMMETRIC_KEY_FOUNDRY_CLIENT_KMIP_V1_2 = 0x0025, KMIP_PROFILE_INTERMEDIATE_SYMMETRIC_KEY_FOUNDRY_CLIENT_KMIP_V1_2 = 0x0026, KMIP_PROFILE_ADVANCED_SYMMETRIC_KEY_FOUNDRY_CLIENT_KMIP_V1_2 = 0x0027, KMIP_PROFILE_SYMMETRIC_KEY_FOUNDRY_SERVER_KMIP_V1_0 = 0x0028, KMIP_PROFILE_SYMMETRIC_KEY_FOUNDRY_SERVER_KMIP_V1_1 = 0x0029, KMIP_PROFILE_SYMMETRIC_KEY_FOUNDRY_SERVER_KMIP_V1_2 = 0x002A, KMIP_PROFILE_OPAQUE_MANAGED_OBJECT_STORE_CLIENT_KMIP_V1_0 = 0x002B, KMIP_PROFILE_OPAQUE_MANAGED_OBJECT_STORE_CLIENT_KMIP_V1_1 = 0x002C, KMIP_PROFILE_OPAQUE_MANAGED_OBJECT_STORE_CLIENT_KMIP_V1_2 = 0x002D, KMIP_PROFILE_OPAQUE_MANAGED_OBJECT_STORE_SERVER_KMIP_V1_0 = 0x002E, KMIP_PROFILE_OPAQUE_MANAGED_OBJECT_STORE_SERVER_KMIP_V1_1 = 0x002F, KMIP_PROFILE_OPAQUE_MANAGED_OBJECT_STORE_SERVER_KMIP_V1_2 = 0x0030, KMIP_PROFILE_SUITE_B_MINLOS_128_CLIENT_KMIP_V1_0 = 0x0031, KMIP_PROFILE_SUITE_B_MINLOS_128_CLIENT_KMIP_V1_1 = 0x0032, KMIP_PROFILE_SUITE_B_MINLOS_128_CLIENT_KMIP_V1_2 = 0x0033, KMIP_PROFILE_SUITE_B_MINLOS_128_SERVER_KMIP_V1_0 = 0x0034, KMIP_PROFILE_SUITE_B_MINLOS_128_SERVER_KMIP_V1_1 = 0x0035, KMIP_PROFILE_SUITE_B_MINLOS_128_SERVER_KMIP_V1_2 = 0x0036, KMIP_PROFILE_SUITE_B_MINLOS_192_CLIENT_KMIP_V1_0 = 0x0037, KMIP_PROFILE_SUITE_B_MINLOS_192_CLIENT_KMIP_V1_1 = 0x0038, KMIP_PROFILE_SUITE_B_MINLOS_192_CLIENT_KMIP_V1_2 = 0x0039, KMIP_PROFILE_SUITE_B_MINLOS_192_SERVER_KMIP_V1_0 = 0x003A, KMIP_PROFILE_SUITE_B_MINLOS_192_SERVER_KMIP_V1_1 = 0x003B, KMIP_PROFILE_SUITE_B_MINLOS_192_SERVER_KMIP_V1_2 = 0x003C, KMIP_PROFILE_STORAGE_ARRAY_WITH_SELF_ENCRYPTING_DRIVE_CLIENT_KMIP_V1_0 = 0x003D, KMIP_PROFILE_STORAGE_ARRAY_WITH_SELF_ENCRYPTING_DRIVE_CLIENT_KMIP_V1_1 = 0x003E, KMIP_PROFILE_STORAGE_ARRAY_WITH_SELF_ENCRYPTING_DRIVE_CLIENT_KMIP_V1_2 = 0x003F, KMIP_PROFILE_STORAGE_ARRAY_WITH_SELF_ENCRYPTING_DRIVE_SERVER_KMIP_V1_0 = 0x0040, KMIP_PROFILE_STORAGE_ARRAY_WITH_SELF_ENCRYPTING_DRIVE_SERVER_KMIP_V1_1 = 0x0041, KMIP_PROFILE_STORAGE_ARRAY_WITH_SELF_ENCRYPTING_DRIVE_SERVER_KMIP_V1_2 = 0x0042, KMIP_PROFILE_HTTPS_CLIENT_KMIP_V1_0 = 0x0043, KMIP_PROFILE_HTTPS_CLIENT_KMIP_V1_1 = 0x0044, KMIP_PROFILE_HTTPS_CLIENT_KMIP_V1_2 = 0x0045, KMIP_PROFILE_HTTPS_SERVER_KMIP_V1_0 = 0x0046, KMIP_PROFILE_HTTPS_SERVER_KMIP_V1_1 = 0x0047, KMIP_PROFILE_HTTPS_SERVER_KMIP_V1_2 = 0x0048, KMIP_PROFILE_JSON_CLIENT_KMIP_V1_0 = 0x0049, KMIP_PROFILE_JSON_CLIENT_KMIP_V1_1 = 0x004A, KMIP_PROFILE_JSON_CLIENT_KMIP_V1_2 = 0x004B, KMIP_PROFILE_JSON_SERVER_KMIP_V1_0 = 0x004C, KMIP_PROFILE_JSON_SERVER_KMIP_V1_1 = 0x004D, KMIP_PROFILE_JSON_SERVER_KMIP_V1_2 = 0x004E, KMIP_PROFILE_XML_CLIENT_KMIP_V1_0 = 0x004F, KMIP_PROFILE_XML_CLIENT_KMIP_V1_1 = 0x0050, KMIP_PROFILE_XML_CLIENT_KMIP_V1_2 = 0x0051, KMIP_PROFILE_XML_SERVER_KMIP_V1_0 = 0x0052, KMIP_PROFILE_XML_SERVER_KMIP_V1_1 = 0x0053, KMIP_PROFILE_XML_SERVER_KMIP_V1_2 = 0x0054, KMIP_PROFILE_BASELINE_SERVER_BASIC_KMIP_V1_3 = 0x0055, KMIP_PROFILE_BASELINE_SERVER_TLS_V1_2_KMIP_V1_3 = 0x0056, KMIP_PROFILE_BASELINE_CLIENT_BASIC_KMIP_V1_3 = 0x0057, KMIP_PROFILE_BASELINE_CLIENT_TLS_V1_2_KMIP_V1_3 = 0x0058, KMIP_PROFILE_COMPLETE_SERVER_BASIC_KMIP_V1_3 = 0x0059, KMIP_PROFILE_COMPLETE_SERVER_TLS_V1_2_KMIP_V1_3 = 0x005A, KMIP_PROFILE_TAPE_LIBRARY_CLIENT_KMIP_V1_3 = 0x005B, KMIP_PROFILE_TAPE_LIBRARY_SERVER_KMIP_V1_3 = 0x005C, KMIP_PROFILE_SYMMETRIC_KEY_LIFECYCLE_CLIENT_KMIP_V1_3 = 0x005D, KMIP_PROFILE_SYMMETRIC_KEY_LIFECYCLE_SERVER_KMIP_V1_3 = 0x005E, KMIP_PROFILE_ASYMMETRIC_KEY_LIFECYCLE_CLIENT_KMIP_V1_3 = 0x005F, KMIP_PROFILE_ASYMMETRIC_KEY_LIFECYCLE_SERVER_KMIP_V1_3 = 0x0060, KMIP_PROFILE_BASIC_CRYPTOGRAPHIC_CLIENT_KMIP_V1_3 = 0x0061, KMIP_PROFILE_BASIC_CRYPTOGRAPHIC_SERVER_KMIP_V1_3 = 0x0062, KMIP_PROFILE_ADVANCED_CRYPTOGRAPHIC_CLIENT_KMIP_V1_3 = 0x0063, KMIP_PROFILE_ADVANCED_CRYPTOGRAPHIC_SERVER_KMIP_V1_3 = 0x0064, KMIP_PROFILE_RNG_CRYPTOGRAPHIC_CLIENT_KMIP_V1_3 = 0x0065, KMIP_PROFILE_RNG_CRYPTOGRAPHIC_SERVER_KMIP_V1_3 = 0x0066, KMIP_PROFILE_BASIC_SYMMETRIC_KEY_FOUNDRY_CLIENT_KMIP_V1_3 = 0x0067, KMIP_PROFILE_INTERMEDIATE_SYMMETRIC_KEY_FOUNDRY_CLIENT_KMIP_V1_3 = 0x0068, KMIP_PROFILE_ADVANCED_SYMMETRIC_KEY_FOUNDRY_CLIENT_KMIP_V1_3 = 0x0069, KMIP_PROFILE_SYMMETRIC_KEY_FOUNDRY_SERVER_KMIP_V1_3 = 0x006A, KMIP_PROFILE_OPAQUE_MANAGED_OBJECT_STORE_CLIENT_KMIP_V1_3 = 0x006B, KMIP_PROFILE_OPAQUE_MANAGED_OBJECT_STORE_SERVER_KMIP_V1_3 = 0x006C, KMIP_PROFILE_SUITE_B_MINLOS_128_CLIENT_KMIP_V1_3 = 0x006D, KMIP_PROFILE_SUITE_B_MINLOS_128_SERVER_KMIP_V1_3 = 0x006E, KMIP_PROFILE_SUITE_B_MINLOS_192_CLIENT_KMIP_V1_3 = 0x006F, KMIP_PROFILE_SUITE_B_MINLOS_192_SERVER_KMIP_V1_3 = 0x0070, KMIP_PROFILE_STORAGE_ARRAY_WITH_SELF_ENCRYPTING_DRIVE_CLIENT_KMIP_V1_3 = 0x0071, KMIP_PROFILE_STORAGE_ARRAY_WITH_SELF_ENCRYPTING_DRIVE_SERVER_KMIP_V1_3 = 0x0072, KMIP_PROFILE_HTTPS_CLIENT_KMIP_V1_3 = 0x0073, KMIP_PROFILE_HTTPS_SERVER_KMIP_V1_3 = 0x0074, KMIP_PROFILE_JSON_CLIENT_KMIP_V1_3 = 0x0075, KMIP_PROFILE_JSON_SERVER_KMIP_V1_3 = 0x0076, KMIP_PROFILE_XML_CLIENT_KMIP_V1_3 = 0x0077, KMIP_PROFILE_XML_SERVER_KMIP_V1_3 = 0x0078, KMIP_PROFILE_BASELINE_SERVER_BASIC_KMIP_V1_4 = 0x0079, KMIP_PROFILE_BASELINE_SERVER_TLS_V1_2_KMIP_V1_4 = 0x007A, KMIP_PROFILE_BASELINE_CLIENT_BASIC_KMIP_V1_4 = 0x007B, KMIP_PROFILE_BASELINE_CLIENT_TLS_V1_2_KMIP_V1_4 = 0x007C, KMIP_PROFILE_COMPLETE_SERVER_BASIC_KMIP_V1_4 = 0x007D, KMIP_PROFILE_COMPLETE_SERVER_TLS_V1_2_KMIP_V1_4 = 0x007E, KMIP_PROFILE_TAPE_LIBRARY_CLIENT_KMIP_V1_4 = 0x007F, KMIP_PROFILE_TAPE_LIBRARY_SERVER_KMIP_V1_4 = 0x0080, KMIP_PROFILE_SYMMETRIC_KEY_LIFECYCLE_CLIENT_KMIP_V1_4 = 0x0081, KMIP_PROFILE_SYMMETRIC_KEY_LIFECYCLE_SERVER_KMIP_V1_4 = 0x0082, KMIP_PROFILE_ASYMMETRIC_KEY_LIFECYCLE_CLIENT_KMIP_V1_4 = 0x0083, KMIP_PROFILE_ASYMMETRIC_KEY_LIFECYCLE_SERVER_KMIP_V1_4 = 0x0084, KMIP_PROFILE_BASIC_CRYPTOGRAPHIC_CLIENT_KMIP_V1_4 = 0x0085, KMIP_PROFILE_BASIC_CRYPTOGRAPHIC_SERVER_KMIP_V1_4 = 0x0086, KMIP_PROFILE_ADVANCED_CRYPTOGRAPHIC_CLIENT_KMIP_V1_4 = 0x0087, KMIP_PROFILE_ADVANCED_CRYPTOGRAPHIC_SERVER_KMIP_V1_4 = 0x0088, KMIP_PROFILE_RNG_CRYPTOGRAPHIC_CLIENT_KMIP_V1_4 = 0x0089, KMIP_PROFILE_RNG_CRYPTOGRAPHIC_SERVER_KMIP_V1_4 = 0x008A, KMIP_PROFILE_BASIC_SYMMETRIC_KEY_FOUNDRY_CLIENT_KMIP_V1_4 = 0x008B, KMIP_PROFILE_INTERMEDIATE_SYMMETRIC_KEY_FOUNDRY_CLIENT_KMIP_V1_4 = 0x008C, KMIP_PROFILE_ADVANCED_SYMMETRIC_KEY_FOUNDRY_CLIENT_KMIP_V1_4 = 0x008D, KMIP_PROFILE_SYMMETRIC_KEY_FOUNDRY_SERVER_KMIP_V1_4 = 0x008E, KMIP_PROFILE_OPAQUE_MANAGED_OBJECT_STORE_CLIENT_KMIP_V1_4 = 0x008F, KMIP_PROFILE_OPAQUE_MANAGED_OBJECT_STORE_SERVER_KMIP_V1_4 = 0x0090, KMIP_PROFILE_SUITE_B_MINLOS_128_CLIENT_KMIP_V1_4 = 0x0091, KMIP_PROFILE_SUITE_B_MINLOS_128_SERVER_KMIP_V1_4 = 0x0092, KMIP_PROFILE_SUITE_B_MINLOS_192_CLIENT_KMIP_V1_4 = 0x0093, KMIP_PROFILE_SUITE_B_MINLOS_192_SERVER_KMIP_V1_4 = 0x0094, KMIP_PROFILE_STORAGE_ARRAY_WITH_SELF_ENCRYPTING_DRIVE_CLIENT_KMIP_V1_4 = 0x0095, KMIP_PROFILE_STORAGE_ARRAY_WITH_SELF_ENCRYPTING_DRIVE_SERVER_KMIP_V1_4 = 0x0096, KMIP_PROFILE_HTTPS_CLIENT_KMIP_V1_4 = 0x0097, KMIP_PROFILE_HTTPS_SERVER_KMIP_V1_4 = 0x0098, KMIP_PROFILE_JSON_CLIENT_KMIP_V1_4 = 0x0099, KMIP_PROFILE_JSON_SERVER_KMIP_V1_4 = 0x009A, KMIP_PROFILE_XML_CLIENT_KMIP_V1_4 = 0x009B, KMIP_PROFILE_XML_SERVER_KMIP_V1_4 = 0x009C, KMIP_PROFILE_COMPLETE_SERVER_BASIC = 0x0104, KMIP_PROFILE_COMPLETE_SERVER_TLS_V1_2 = 0x0105, KMIP_PROFILE_TAPE_LIBRARY_CLIENT = 0x0106, KMIP_PROFILE_TAPE_LIBRARY_SERVER = 0x0107, KMIP_PROFILE_SYMMETRIC_KEY_LIFECYCLE_CLIENT = 0x0108, KMIP_PROFILE_SYMMETRIC_KEY_LIFECYCLE_SERVER = 0x0109, KMIP_PROFILE_ASYMMETRIC_KEY_LIFECYCLE_CLIENT = 0x010A, KMIP_PROFILE_ASYMMETRIC_KEY_LIFECYCLE_SERVER = 0x010B, KMIP_PROFILE_BASIC_CRYPTOGRAPHIC_CLIENT = 0x010C, KMIP_PROFILE_BASIC_CRYPTOGRAPHIC_SERVER = 0x010D, KMIP_PROFILE_ADVANCED_CRYPTOGRAPHIC_CLIENT = 0x010E, KMIP_PROFILE_ADVANCED_CRYPTOGRAPHIC_SERVER = 0x010F, KMIP_PROFILE_RNG_CRYPTOGRAPHIC_CLIENT = 0x0110, KMIP_PROFILE_RNG_CRYPTOGRAPHIC_SERVER = 0x0111, KMIP_PROFILE_BASIC_SYMMETRIC_KEY_FOUNDRY_CLIENT = 0x0112, KMIP_PROFILE_INTERMEDIATE_SYMMETRIC_KEY_FOUNDRY_CLIENT = 0x0113, KMIP_PROFILE_ADVANCED_SYMMETRIC_KEY_FOUNDRY_CLIENT = 0x0114, KMIP_PROFILE_SYMMETRIC_KEY_FOUNDRY_SERVER = 0x0115, KMIP_PROFILE_OPAQUE_MANAGED_OBJECT_STORE_CLIENT = 0x0116, KMIP_PROFILE_OPAQUE_MANAGED_OBJECT_STORE_SERVER = 0x0117, KMIP_PROFILE_STORAGE_ARRAY_WITH_SELF_ENCRYPTING_DRIVE_CLIENT = 0x011C, KMIP_PROFILE_STORAGE_ARRAY_WITH_SELF_ENCRYPTING_DRIVE_SERVER = 0x011D, KMIP_PROFILE_HTTPS_CLIENT = 0x011E, KMIP_PROFILE_HTTPS_SERVER = 0x011F, KMIP_PROFILE_JSON_CLIENT = 0x0120, KMIP_PROFILE_JSON_SERVER = 0x0121, KMIP_PROFILE_XML_CLIENT = 0x0122, KMIP_PROFILE_XML_SERVER = 0x0123, KMIP_PROFILE_AES_XTS_CLIENT = 0x0124, KMIP_PROFILE_AES_XTS_SERVER = 0x0125, KMIP_PROFILE_QUANTUM_SAFE_CLIENT = 0x0126, KMIP_PROFILE_QUANTUM_SAFE_SERVER = 0x0127, KMIP_PROFILE_PKCS_11_CLIENT = 0x0128, KMIP_PROFILE_PKCS_11_SERVER = 0x0129, KMIP_PROFILE_BASELINE_CLIENT = 0x012A, KMIP_PROFILE_BASELINE_SERVER = 0x012B, KMIP_PROFILE_COMPLETE_SERVER = 0x012C, }; struct kmip_version { int32_t major; int32_t minor; }; enum kmip_encoding { KMIP_ENCODING_TTLV = 1, KMIP_ENCODING_JSON = 2, /* Only via HTTPS transport */ KMIP_ENCODING_XML = 3, /* Only via HTTPS transport */ }; enum kmip_transport { KMIP_TRANSPORT_PLAIN_TLS = 1, KMIP_TRANSPORT_HTTPS = 2, }; #define KMIP_DEFAULT_PLAIN_TLS_PORT "5696" #define KMIP_DEFAULT_PLAIN_TLS_PORT_NUM 5696 #define KMIP_DEFAULT_HTTPS_PORT "5696" #define KMIP_DEFAULT_HTTPS_PORT_NUM 5696 struct kmip_conn_config { /** Encoding used for the KMIP messages */ enum kmip_encoding encoding; /** Transport method used to deliver KMIP messages */ enum kmip_transport transport; /** * The KMIP server. * For Plain-TLS transport, only the hostname and optional port number. * For HTTPS transport, an URL in the form * 'https://hostname[:port]/uri' */ const char *server; /** The client key as an OpenSSL PKEY object. */ EVP_PKEY *tls_client_key; /** File name of the client certificate PEM file */ const char *tls_client_cert; /** * Optional: File name of the CA bundle PEM file, or a name of a * directory the multiple CA certificates. If this is NULL, then the * default system path for CA certificates is used */ const char *tls_ca; /** * Optional: File name of a PEM file holding a CA certificate of the * issuer */ const char *tls_issuer_cert; /** * Optional: File name of a PEM file containing the servers pinned * public key. Public key pinning requires that verify_peer or * verify_host (or both) is true. */ const char *tls_pinned_pubkey; /** * Optional: File name of a PEM file containing the server's * certificate. This can be used to allow peer verification with * self-signed server certificates */ const char *tls_server_cert; /** If true, the peer certificate is verified */ bool tls_verify_peer; /** * If true, that the server certificate is for the server it is known * as (i.e. the hostname in the url) */ bool tls_verify_host; /** * Optional: A list of ciphers for TLSv1.2 and below. This is a colon * separated list of cipher strings. The format of the string is * described in * https://www.openssl.org/docs/man1.1.1/man1/ciphers.html */ const char *tls_cipher_list; /** * Optional: A list of ciphers for TLSv1.3. This is a colon separated * list of TLSv1.3 ciphersuite names in order of preference. Valid * TLSv1.3 ciphersuite names are: * - TLS_AES_128_GCM_SHA256 * - TLS_AES_256_GCM_SHA384 * - TLS_CHACHA20_POLY1305_SHA256 * - TLS_AES_128_CCM_SHA256 * - TLS_AES_128_CCM_8_SHA256 */ const char *tls13_cipher_list; }; /* Opaque KMIP node and connection structures */ struct kmip_connection; struct kmip_node; /* Generic KMIP node constructors/destructors and getters */ struct kmip_node *kmip_node_clone(const struct kmip_node *node); void kmip_node_upref(struct kmip_node *node); void kmip_node_free(struct kmip_node *node); enum kmip_tag kmip_node_get_tag(const struct kmip_node *node); enum kmip_type kmip_node_get_type(const struct kmip_node *node); char *kmip_node_get_name(const struct kmip_node *node); void kmip_node_dump(struct kmip_node *node, bool debug); struct kmip_node *kmip_node_new_structure(enum kmip_tag tag, const char *name, unsigned int num_elements, struct kmip_node **elements); struct kmip_node *kmip_node_new_structure_va(enum kmip_tag tag, const char *name, unsigned int num_elements, ...); unsigned int kmip_node_get_structure_element_count( const struct kmip_node *node); struct kmip_node *kmip_node_get_structure_element_by_index( const struct kmip_node *node, unsigned int index); unsigned int kmip_node_get_structure_element_by_tag_count( const struct kmip_node *node, enum kmip_tag tag); struct kmip_node *kmip_node_get_structure_element_by_tag( const struct kmip_node *node, enum kmip_tag tag, unsigned int index); int kmip_node_add_structure_element(struct kmip_node *node, struct kmip_node *element); int kmip_node_add_structure_elements(struct kmip_node *node, unsigned int num_elements, struct kmip_node **elements); struct kmip_node *kmip_node_new_integer(enum kmip_tag tag, const char *name, int32_t value); int32_t kmip_node_get_integer(const struct kmip_node *node); struct kmip_node *kmip_node_new_long(enum kmip_tag tag, const char *name, int64_t value); int64_t kmip_node_get_long(const struct kmip_node *node); struct kmip_node *kmip_node_new_bigint(enum kmip_tag tag, const char *name, const BIGNUM *value); const BIGNUM *kmip_node_get_bigint(const struct kmip_node *node); struct kmip_node *kmip_node_new_enumeration(enum kmip_tag tag, const char *name, uint32_t enumeration); uint32_t kmip_node_get_enumeration(const struct kmip_node *node); struct kmip_node *kmip_node_new_boolean(enum kmip_tag tag, const char *name, bool value); bool kmip_node_get_boolean(const struct kmip_node *node); struct kmip_node *kmip_node_new_text_string(enum kmip_tag tag, const char *name, const char *value); const char *kmip_node_get_text_string(const struct kmip_node *node); struct kmip_node *kmip_node_new_byte_string(enum kmip_tag tag, const char *name, const unsigned char *value, uint32_t length); const unsigned char *kmip_node_get_byte_string(const struct kmip_node *node, uint32_t *length); struct kmip_node *kmip_node_new_date_time(enum kmip_tag tag, const char *name, int64_t value); int64_t kmip_node_get_date_time(const struct kmip_node *node); struct kmip_node *kmip_node_new_interval(enum kmip_tag tag, const char *name, uint32_t value); uint32_t kmip_node_get_interval(const struct kmip_node *node); struct kmip_node *kmip_node_new_date_time_ext(enum kmip_tag tag, const char *name, int64_t value); int64_t kmip_node_get_date_time_ext(const struct kmip_node *node); /* Generic functions */ void kmip_set_default_protocol_version(const struct kmip_version *version); const struct kmip_version *kmip_get_default_protocol_version(void); /* Request related functions */ struct kmip_node *kmip_new_protocol_version(const struct kmip_version *version); struct kmip_node *kmip_new_profile_version(const struct kmip_version *version); struct kmip_node *kmip_new_request_header(const struct kmip_version *version, int32_t max_response_size, const char *client_corr_value, const char *server_corr_value, bool asynchronous, struct kmip_node *authentication, enum kmip_batch_error_cont_option batch_err_opt, bool batch_order_option, int32_t batch_count); struct kmip_node *kmip_new_request_batch_item(enum kmip_operation operation, unsigned char *batch_id, uint32_t batch_id_length, struct kmip_node *payload); struct kmip_node *kmip_new_request(struct kmip_node *request_header, int32_t batch_count, struct kmip_node **batch_items); struct kmip_node *kmip_new_request_va(struct kmip_node *request_header, int32_t batch_count, ...); struct kmip_node *kmip_new_query_request_payload(unsigned int query_count, const enum kmip_query_function *functions); struct kmip_node *kmip_new_query_request_payload_va(unsigned int query_count, ...); struct kmip_node *kmip_new_discover_versions_payload(int version_count, const struct kmip_version *versions); struct kmip_node *kmip_new_discover_versions_payload_va(int version_count, ...); struct kmip_node *kmip_new_protection_storage_masks(unsigned int masks_count, int32_t *masks); struct kmip_node *kmip_new_protection_storage_masks_va(unsigned int masks_count, ...); struct kmip_node *kmip_new_create_request_payload( const struct kmip_version *version, enum kmip_object_type obj_type, struct kmip_node *prot_storage_masks, unsigned int attrs_count, struct kmip_node **attrs); struct kmip_node *kmip_new_create_request_payload_va( const struct kmip_version *version, enum kmip_object_type obj_type, struct kmip_node *prot_storage_masks, unsigned int attrs_count, ...); struct kmip_node *kmip_new_get_attribute_list_request_payload( struct kmip_node *unique_id); struct kmip_node *kmip_new_get_attributes_request_payload( const struct kmip_version *version, struct kmip_node *unique_id, unsigned int num_attrs, struct kmip_node **attr_refs); struct kmip_node *kmip_new_get_attributes_request_payload_va( const struct kmip_version *version, struct kmip_node *unique_id, unsigned int num_attrs, ...); struct kmip_node *kmip_new_add_attribute_request_payload( const struct kmip_version *version, struct kmip_node *unique_id, struct kmip_node *v2_attr); struct kmip_node *kmip_new_modify_attribute_request_payload( const struct kmip_version *version, struct kmip_node *unique_id, struct kmip_node *v2_current, struct kmip_node *v2_attr); struct kmip_node *kmip_new_set_attribute_v2_request_payload( struct kmip_node *unique_id, struct kmip_node *v2_attr); struct kmip_node *kmip_new_delete_attribute_request_payload( const struct kmip_version *version, struct kmip_node *unique_id, struct kmip_node *v2_current, struct kmip_node *attr_ref); struct kmip_node *kmip_new_activate_request_payload( struct kmip_node *unique_id); struct kmip_node *kmip_new_destroy_request_payload(struct kmip_node *unique_id); struct kmip_node *kmip_new_archive_request_payload(struct kmip_node *unique_id); struct kmip_node *kmip_new_recover_request_payload(struct kmip_node *unique_id); struct kmip_node *kmip_new_revoke_request_payload(struct kmip_node *unique_id, enum kmip_revoke_reason rsn, const char *message, uint64_t compromise_date); struct kmip_node *kmip_new_locate_request_payload( const struct kmip_version *version, int32_t max_items, int32_t offset_items, enum kmip_storage_status_mask storage_status, enum kmip_object_group_member obj_group, unsigned int attrs_count, struct kmip_node **attrs); struct kmip_node *kmip_new_locate_request_payload_va( const struct kmip_version *version, int32_t max_items, int32_t offset_items, enum kmip_storage_status_mask storage_status, enum kmip_object_group_member obj_group, unsigned int attrs_count, ...); struct kmip_node *kmip_new_register_request_payload( const struct kmip_version *version, enum kmip_object_type obj_type, struct kmip_node *object, struct kmip_node *prot_storage_masks, unsigned int attrs_count, struct kmip_node **attrs); struct kmip_node *kmip_new_register_request_payload_va( const struct kmip_version *version, enum kmip_object_type obj_type, struct kmip_node *object, struct kmip_node *prot_storage_masks, unsigned int attrs_count, ...); struct kmip_node *kmip_new_get_request_payload( const struct kmip_version *version, struct kmip_node *unique_id, enum kmip_key_format_type format_type, enum kmip_key_wrap_type wrap_type, enum kmip_key_compression_type compr_type, struct kmip_node *wrap_specification); /* Response related functions */ int kmip_get_protocol_version(const struct kmip_node *node, struct kmip_version *version); int kmip_get_profile_version(const struct kmip_node *node, struct kmip_version *version); int kmip_get_response_header(const struct kmip_node *node, struct kmip_version *version, int64_t *time_stamp, const char **client_corr_value, const char **server_corr_value, int32_t *batch_count); int kmip_get_response_batch_item(const struct kmip_node *node, enum kmip_operation *operation, const unsigned char **batch_id, uint32_t *batch_id_length, enum kmip_result_status *status, enum kmip_result_reason *reason, const char **message, const unsigned char **async_corr_value, uint32_t *async_corr_value_len, struct kmip_node **payload); int kmip_get_response(const struct kmip_node *node, struct kmip_node **response_header, unsigned int batch_index, struct kmip_node **batch_item); int kmip_get_query_response_payload(const struct kmip_node *node, enum kmip_query_function query_function, unsigned int *num_results, unsigned int result_index, struct kmip_node **result); int kmip_get_discover_versions_response_payload(const struct kmip_node *node, unsigned int *num_versions, unsigned int index, struct kmip_version *version); int kmip_get_create_response_payload(const struct kmip_node *node, enum kmip_object_type *obj_type, struct kmip_node **unique_id, unsigned int *num_attrs, unsigned int attr_index, struct kmip_node **attribute); int kmip_get_get_attribute_list_response_payload(const struct kmip_node *node, struct kmip_node **unique_id, unsigned int *num_attr_refs, unsigned int index, struct kmip_node **attr_ref); int kmip_get_get_attributes_response_payload(const struct kmip_node *node, struct kmip_node **unique_id, unsigned int *num_attrs, unsigned int index, struct kmip_node **v2_attr); int kmip_get_add_attribute_response_payload(const struct kmip_node *node, struct kmip_node **unique_id, struct kmip_node **v2_attr); int kmip_get_modify_attribute_response_payload(const struct kmip_node *node, struct kmip_node **unique_id, struct kmip_node **v2_attr); int kmip_get_set_attribute_v2_response_payload(const struct kmip_node *node, struct kmip_node **unique_id); int kmip_get_delete_attribute_response_payload(const struct kmip_node *node, struct kmip_node **unique_id, struct kmip_node **v2_attr); int kmip_get_activate_response_payload(const struct kmip_node *node, struct kmip_node **unique_id); int kmip_get_destroy_response_payload(const struct kmip_node *node, struct kmip_node **unique_id); int kmip_get_archive_response_payload(const struct kmip_node *node, struct kmip_node **unique_id); int kmip_get_recover_response_payload(const struct kmip_node *node, struct kmip_node **unique_id); int kmip_get_revoke_response_payload(const struct kmip_node *node, struct kmip_node **unique_id); int kmip_get_activate_response_payload(const struct kmip_node *node, struct kmip_node **unique_id); int kmip_get_locate_response_payload(const struct kmip_node *node, int32_t *located_items, unsigned int *num_items, unsigned int index, struct kmip_node **unique_id); int kmip_get_register_response_payload(const struct kmip_node *node, struct kmip_node **unique_id, unsigned int *num_attrs, unsigned int attr_index, struct kmip_node **attribute); int kmip_get_get_response_payload(const struct kmip_node *node, enum kmip_object_type *obj_type, struct kmip_node **unique_id, struct kmip_node **object); /* Attribute related functions */ struct kmip_node *kmip_new_attributes(const struct kmip_version *version, enum kmip_tag v2_tag, unsigned int attrs_count, struct kmip_node **v2_attrs); struct kmip_node *kmip_new_attributes_va(const struct kmip_version *version, enum kmip_tag v2_tag, unsigned int attrs_count, ...); int kmip_get_attributes(const struct kmip_node *node, unsigned int *num_attrs, unsigned int attr_index, struct kmip_node **attr); struct kmip_node *kmip_new_vendor_attribute(const char *vendor_id, const char *name, struct kmip_node *value); int kmip_get_vendor_attribute(const struct kmip_node *node, const char **vendor_id, const char **name, struct kmip_node **value); struct kmip_node *kmip_new_attribute_reference(enum kmip_tag attr_tag, const char *vendor_id, const char *name); int kmip_get_attribute_reference(const struct kmip_node *node, enum kmip_tag *attr_tag, const char **vendor_id, const char **name); struct kmip_node *kmip_new_current_new_attribute(bool new_attr, struct kmip_node *attr); struct kmip_node *kmip_new_unique_identifier(const char *text_id, enum kmip_unique_identifier enum_id, int32_t int_id); int kmip_get_unique_identifier(const struct kmip_node *node, const char **text_id, enum kmip_unique_identifier *enum_id, int32_t *int_id); struct kmip_node *kmip_new_name(const char *value, enum kmip_name_type type); int kmip_get_name(const struct kmip_node *node, const char **value, enum kmip_name_type *type); struct kmip_node *kmip_new_alternative_name(const char *value, enum kmip_alternative_name_type type); int kmip_get_alternative_name(const struct kmip_node *node, const char **value, enum kmip_alternative_name_type *type); struct kmip_node *kmip_new_object_type(enum kmip_object_type obj_type); int kmip_get_object_type(const struct kmip_node *node, enum kmip_object_type *obj_type); struct kmip_node *kmip_new_cryptographic_algorithm(enum kmip_crypto_algo algo); int kmip_get_cryptographic_algorithm(const struct kmip_node *node, enum kmip_crypto_algo *algo); struct kmip_node *kmip_new_cryptographic_length(int32_t length); int kmip_get_cryptographic_length(const struct kmip_node *node, int32_t *length); struct kmip_node *kmip_new_certificate_type(enum kmip_certificate_type type); int kmip_get_certificate_type(const struct kmip_node *node, enum kmip_certificate_type *type); struct kmip_node *kmip_new_cryptographic_usage_mask(int32_t usage_mask); int kmip_get_cryptographic_usage_mask(const struct kmip_node *node, int32_t *usage_mask); struct kmip_node *kmip_new_state(enum kmip_state state); int kmip_get_state(const struct kmip_node *node, enum kmip_state *state); struct kmip_node *kmip_new_initial_date(int64_t date); int kmip_get_initial_date(const struct kmip_node *node, int64_t *date); struct kmip_node *kmip_new_activation_date(int64_t date); int kmip_get_activation_date(const struct kmip_node *node, int64_t *date); struct kmip_node *kmip_new_deactivation_date(int64_t date); int kmip_get_deactivation_date(const struct kmip_node *node, int64_t *date); struct kmip_node *kmip_new_destroy_date(int64_t date); int kmip_get_destroy_date(const struct kmip_node *node, int64_t *date); struct kmip_node *kmip_new_compromise_date(int64_t date); int kmip_get_compromise_date(const struct kmip_node *node, int64_t *date); struct kmip_node *kmip_new_compromise_occurrence_date(int64_t date); int kmip_get_compromise_occurrence_date(const struct kmip_node *node, int64_t *date); struct kmip_node *kmip_new_last_change_date(int64_t date); int kmip_get_last_change_date(const struct kmip_node *node, int64_t *date); struct kmip_node *kmip_new_original_creation_date(int64_t date); int kmip_get_original_creation_date(const struct kmip_node *node, int64_t *date); struct kmip_node *kmip_new_archive_date(int64_t date); int kmip_get_archive_date(const struct kmip_node *node, int64_t *date); struct kmip_node *kmip_new_process_start_date(int64_t date); int kmip_get_process_start_date(const struct kmip_node *node, int64_t *date); struct kmip_node *kmip_new_protect_stop_date(int64_t date); int kmip_get_protect_stop_date(const struct kmip_node *node, int64_t *date); struct kmip_node *kmip_new_cryptographic_parameters( const struct kmip_version *version, enum kmip_block_cipher_mode mode, enum kmip_padding_method padding, enum kmip_hashing_algo hash_algo, enum kmip_key_role_type key_role, enum kmip_signature_algo signature_algo, enum kmip_crypto_algo crypto_algo, bool *random_iv, int32_t *iv_length, int32_t *tag_length, int32_t *fixed_field_length, int32_t *invoc_field_length, int32_t *counter_length, int32_t *init_counter_value, int32_t *salt_length, enum kmip_mask_generator mgf, enum kmip_hashing_algo mgf_hash_algo, int32_t *trailer_field); int kmip_get_cryptographic_parameter(const struct kmip_node *node, enum kmip_block_cipher_mode *mode, enum kmip_padding_method *padding, enum kmip_hashing_algo *hash_algo, enum kmip_key_role_type *key_role, enum kmip_signature_algo *signature_algo, enum kmip_crypto_algo *crypto_algo, bool *random_iv, int32_t *iv_length, int32_t *tag_length, int32_t *fixed_field_length, int32_t *invoc_field_length, int32_t *counter_length, int32_t *init_counter_value, int32_t *salt_length, enum kmip_mask_generator *mgf, enum kmip_hashing_algo *mgf_hash_algo, int32_t *trailer_field); struct kmip_node *kmip_new_cryptographic_domain_parameters( int32_t qlength, enum kmip_recommended_curve curve); int kmip_get_cryptographic_domain_parameters(const struct kmip_node *node, int32_t *qlength, enum kmip_recommended_curve *curve); struct kmip_node *kmip_new_digital_signature_algorithm( enum kmip_signature_algo signature_algo); int kmip_get_digital_signature_algorithm(const struct kmip_node *node, enum kmip_signature_algo *signature_algo); struct kmip_node *kmip_new_object_group(const char *group); int kmip_get_object_group(const struct kmip_node *node, const char **group); struct kmip_node *kmip_new_revocation_reason(enum kmip_revoke_reason reason, const char *message); int kmip_get_revocation_reason(const struct kmip_node *node, enum kmip_revoke_reason *reason, const char **message); struct kmip_node *kmip_new_contact_information(const char *contact); int kmip_get_contact_information(const struct kmip_node *node, const char **contact); struct kmip_node *kmip_new_description(const char *description); int kmip_get_description(const struct kmip_node *node, const char **description); struct kmip_node *kmip_new_comment(const char *comment); int kmip_get_comment(const struct kmip_node *node, const char **comment); struct kmip_node *kmip_new_key_format_type(enum kmip_key_format_type type); int kmip_get_key_format_type(const struct kmip_node *node, enum kmip_key_format_type *type); struct kmip_node *kmip_new_protection_level(enum kmip_protection_level level); int kmip_get_protection_level(const struct kmip_node *node, enum kmip_protection_level *level); struct kmip_node *kmip_new_protection_period(uint32_t period); int kmip_get_protection_period(const struct kmip_node *node, uint32_t *period); struct kmip_node *kmip_new_protection_storage_mask(int32_t protection_mask); int kmip_get_protection_storage_mask(const struct kmip_node *node, int32_t *protection_mask); struct kmip_node *kmip_new_fresh(bool fresh); int kmip_get_fresh(const struct kmip_node *node, bool *fresh); struct kmip_node *kmip_new_key_value_present(bool present); int kmip_get_key_value_present(const struct kmip_node *node, bool *present); struct kmip_node *kmip_new_short_unique_identifier( const unsigned char *short_uid, uint32_t short_uid_len); int kmip_get_short_unique_identifier(const struct kmip_node *node, const unsigned char **short_uid, uint32_t *short_uid_len); struct kmip_node *kmip_new_application_specific_information( const char *name_space, const char *data); int kmip_get_application_specific_information(const struct kmip_node *node, const char **name_space, const char **data); struct kmip_node *kmip_new_key_value_location(const char *value, enum kmip_key_value_location_type type); int kmip_get_key_value_location(const struct kmip_node *node, const char **value, enum kmip_key_value_location_type *type); struct kmip_node *kmip_new_digest(enum kmip_hashing_algo hash_algo, const unsigned char *digest, uint32_t digest_len); int kmip_get_digest(const struct kmip_node *node, enum kmip_hashing_algo *hash_algo, const unsigned char **digest, uint32_t *digest_len); struct kmip_node *kmip_new_sensitive(bool sensitive); int kmip_get_sensitive(const struct kmip_node *node, bool *sensitive); struct kmip_node *kmip_new_always_sensitive(bool sensitive); int kmip_get_always_sensitive(const struct kmip_node *node, bool *sensitive); struct kmip_node *kmip_new_extractable(bool extractable); int kmip_get_extractable(const struct kmip_node *node, bool *extractable); struct kmip_node *kmip_new_never_extractable(bool extractable); int kmip_get_never_extractable(const struct kmip_node *node, bool *extractable); struct kmip_node *kmip_new_link(enum kmip_link_type type, struct kmip_node *obj_id); int kmip_get_link(const struct kmip_node *node, enum kmip_link_type *type, struct kmip_node **obj_id); struct kmip_node *kmip_new_linked_object_identifier(const char *text_id, enum kmip_unique_identifier enum_id, int32_t int_id); int kmip_get_linked_object_identifier(const struct kmip_node *node, const char **text_id, enum kmip_unique_identifier *enum_id, int32_t *int_id); struct kmip_node *kmip_new_operation_policy_name(const char *policy); int kmip_get_operation_policy_name(const struct kmip_node *node, const char **policy); struct kmip_node *kmip_new_lease_time(uint32_t lease_time); int kmip_get_lease_time(const struct kmip_node *node, uint32_t *lease_time); /* Key related functions */ struct kmip_node *kmip_new_key_block(enum kmip_key_format_type format_type, enum kmip_key_compression_type compr_type, struct kmip_node *key_value, enum kmip_crypto_algo algorithm, int32_t length, struct kmip_node *wrappig_data); int kmip_get_key_block(const struct kmip_node *node, enum kmip_key_format_type *format_type, enum kmip_key_compression_type *compr_type, struct kmip_node **key_value, enum kmip_crypto_algo *algorithm, int32_t *length, struct kmip_node **wrappig_data); struct kmip_node *kmip_new_key_value(const struct kmip_version *version, struct kmip_node *key_material, unsigned int attrs_count, struct kmip_node **v2_attrs); struct kmip_node *kmip_new_key_value_va(const struct kmip_version *version, struct kmip_node *key_material, unsigned int attrs_count, ...); int kmip_get_key_value(const struct kmip_node *node, struct kmip_node **key_material, unsigned int *num_attrs, unsigned int index, struct kmip_node **v2_attr); struct kmip_node *kmip_new_key_wrapping_data( const struct kmip_version *version, enum kmip_wrapping_method wrap_method, struct kmip_node *encr_key_info, struct kmip_node *mac_sign_key_info, const unsigned char *mac_signature, uint32_t mac_signature_len, const unsigned char *iv_counter_nonce, uint32_t iv_counter_nonce_len, enum kmip_encoding_option encoding); int kmip_get_key_wrapping_data(const struct kmip_node *node, enum kmip_wrapping_method *wrap_method, struct kmip_node **encr_key_info, struct kmip_node **mac_sign_key_info, const unsigned char **mac_signature, uint32_t *mac_signature_len, const unsigned char **iv_counter_nonce, uint32_t *iv_counter_nonce_len, enum kmip_encoding_option *encoding); struct kmip_node *kmip_new_key_wrapping_specification( const struct kmip_version *version, enum kmip_wrapping_method wrap_method, struct kmip_node *encr_key_info, struct kmip_node *mac_sign_key_info, enum kmip_encoding_option encoding, unsigned int attr_name_count, const char **attr_names); struct kmip_node *kmip_new_key_wrapping_specification_va( const struct kmip_version *version, enum kmip_wrapping_method wrap_method, struct kmip_node *encr_key_info, struct kmip_node *mac_sign_key_info, enum kmip_encoding_option encoding, unsigned int attr_name_count, ...); int kmip_get_key_wrapping_specification(const struct kmip_node *node, enum kmip_wrapping_method *wrap_method, struct kmip_node **encr_key_info, struct kmip_node **mac_sign_key_info, enum kmip_encoding_option *encoding, unsigned int *num_attr_names, unsigned int attr_name_index, const char **attr_name); struct kmip_node *kmip_new_key_info(bool mac_sign, struct kmip_node *unique_id, struct kmip_node *crypto_params); int kmip_get_key_info(const struct kmip_node *node, struct kmip_node **unique_id, struct kmip_node **crypto_params); struct kmip_node *kmip_new_transparent_symmetric_key(const unsigned char *key, uint32_t key_length); int kmip_get_transparent_symmetric_key(const struct kmip_node *node, const unsigned char **key, uint32_t *key_length); struct kmip_node *kmip_new_transparent_rsa_public_key(const BIGNUM *modulus, const BIGNUM *pub_exp); int kmip_get_transparent_rsa_public_key(const struct kmip_node *node, const BIGNUM **modulus, const BIGNUM **pub_exp); struct kmip_node *kmip_new_pkcs1_public_key(EVP_PKEY *pub_key); int kmip_get_pkcs1_public_key(const struct kmip_node *node, enum kmip_crypto_algo algo, EVP_PKEY **pub_key); struct kmip_node *kmip_new_pkcs8_public_key(EVP_PKEY *pub_key); int kmip_get_pkcs8_public_key(const struct kmip_node *node, EVP_PKEY **pub_key); struct kmip_node *kmip_new_raw_key(const unsigned char *key, uint32_t key_len); int kmip_get_raw_key(const struct kmip_node *node, const unsigned char **key, uint32_t *key_len); struct kmip_node *kmip_new_symmetric_key(struct kmip_node *keyblock); int kmip_get_symmetric_key(const struct kmip_node *node, struct kmip_node **keyblock); struct kmip_node *kmip_new_public_key(struct kmip_node *keyblock); int kmip_get_public_key(const struct kmip_node *node, struct kmip_node **keyblock); /* Connection related functions */ int kmip_connection_new(const struct kmip_conn_config *config, struct kmip_connection **connection, bool debug); int kmip_connection_perform(struct kmip_connection *connection, struct kmip_node *request, struct kmip_node **response, bool debug); void kmip_connection_free(struct kmip_connection *connection); int kmip_connection_get_server_cert(const char *server, enum kmip_transport transport, const char *ca, EVP_PKEY *client_key, const char *client_cert, const char *server_cert_pem, const char *server_pubkey_pem, const char *cert_chain_pem, bool *verified, bool debug); #endif s390-tools-2.38.0/include/lib/000077500000000000000000000000001502674226300156405ustar00rootroot00000000000000s390-tools-2.38.0/include/lib/ap.h000066400000000000000000000074401502674226300164160ustar00rootroot00000000000000/* * libap - A collection of tools for ap/vfio-ap management * * Copyright IBM Corp. 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef LIB_AP_H #define LIB_AP_H #include #include "lib/util_list.h" #define VFIO_AP_PATH "/sys/devices/vfio_ap" #define VFIO_AP_PARENT_PATH "devices/vfio_ap/matrix" #define VFIO_AP_CONFIG_PATH "/etc/mdevctl.d/matrix" #define VFIO_AP_TYPE "vfio_ap-passthrough" #define AP_UDEV_FILE "/etc/udev/rules.d/41-ap.rules" #define AP_LOCKFILE "/run/lock/s390apconfig.lock" #define AP_LOCK_RETRIES 3000 #define AP_LOCK_DELAY_US 30000 /* wait at least 30ms between lock retries */ #define AP_LOCK_VARIANCE_US 3000 /* or as much as 33ms */ /* apmask and aqmask are each represented as 67 character strings with: * '0x' leading characters * 64 hex digits (to represent 256 bits) * terminating character */ #define AP_MASK_SIZE 67 #define AP_MAX_MASK_VALUE 255 /* List structure used for keeping track of lists of adapter/domain IDs */ struct vfio_ap_node { struct util_list_node node; /* prev/next list info */ unsigned int id; /* list entry (adapter, domain, etc) */ }; /* * Structure used to represent a vfio_ap-passthrough device configuration. * The list of adapters and domains can be used to derive the APQNs for the * device. The type value is used to verify that the device (when read from * a mdevctl config file) is of type vfio_ap-passthrough. The manual value * represents whether the device is started on-demand after boot (true) or * automatically during boot (false). */ struct vfio_ap_device { char *uuid; /* Unique ID for this mdev */ struct util_list *adapters; /* List of adapters for device */ struct util_list *domains; /* List of usage domains for device */ struct util_list *controls; /* List of control domains for device */ char *type; /* mdev type string */ bool manual; /* manual/auto start setting for mdev */ }; /* General Utility Functions */ void print_ap_device(struct vfio_ap_device *dev); bool is_valid_uuid(const char *uuid); int ap_test_bit(int n, const char *hexbytestr); void ap_set_bit(int n, char *hexbytestr, bool val); /* Path-related functions */ char *path_get_vfio_ap_mdev(const char *uuid); char *path_get_vfio_ap_mdev_config(const char *uuid); char *path_get_vfio_ap_attr(const char *uuid, const char *attr); char *path_get_ap_udev(void); /* Functions for manipulating sysfs to read active device info */ void vfio_ap_parse_matrix(struct vfio_ap_device *dev, char *matrix); void vfio_ap_sort_matrix_results(struct vfio_ap_device *dev); void vfio_ap_parse_control(struct vfio_ap_device *dev, char *control); bool vfio_ap_need_dynamic_config(struct vfio_ap_device *dev); /* Functions for reading JSON device config */ int vfio_ap_read_device_config(const char *path, struct vfio_ap_device *dev); /* Functions for managing vfio_ap device structures */ struct vfio_ap_device *vfio_ap_device_new(void); void vfio_ap_device_clear(struct vfio_ap_device *dev); void vfio_ap_device_free(struct vfio_ap_device *dev); /* Functions for acquiring current vfio_ap device info */ int ap_read_sysfs_masks(char *ap, char *aq, int size); bool ap_read_udev_masks(char *path, char *ap, char *aq, bool *read_ap, bool *read_aq); void ap_mask_to_list(char *mask, struct util_list *list); void ap_list_remove_all(struct util_list *list); char *vfio_ap_device_get_adapter_mask(struct vfio_ap_device *dev, int *size); char *vfio_ap_device_get_domain_mask(struct vfio_ap_device *dev, int *size); char *vfio_ap_device_get_control_mask(struct vfio_ap_device *dev, int *size); /* Lock Functions */ int ap_get_lock(void); int ap_get_lock_callout(void); int ap_try_lock_callout(void); int ap_release_lock(void); int ap_release_lock_callout(void); #endif /* LIB_AP_H */ s390-tools-2.38.0/include/lib/ccw.h000066400000000000000000000020421502674226300165630ustar00rootroot00000000000000/* * ccw - Channel Command Word library (traditional I/O) * * Copyright 2017 IBM Corp. * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef LIB_CCW_H #define LIB_CCW_H #include #include "lib/zt_common.h" /** * ccw_devid - CCW device ID */ struct ccw_devid { /** Channel Subsystem ID */ unsigned int cssid:8; /** Subchannel set ID */ unsigned int ssid:8; /** Device number */ unsigned int devno:16; } __packed; /** * Initialize ccw_devid structure * * @param[in,out] devid Pointer to ccw_devid structure to be initialized * @param[in] cssid Channel Subsystem ID * @param[in] ssid Subchannel set ID * @param[in] devno Device number */ static inline void ccw_devid_init(struct ccw_devid *devid, unsigned int cssid, unsigned int ssid, unsigned int devno) { devid->cssid = cssid; devid->ssid = ssid; devid->devno = devno; } bool ccw_parse_str(struct ccw_devid *devid, const char *id); #endif s390-tools-2.38.0/include/lib/dasd_base.h000066400000000000000000000256401502674226300177250ustar00rootroot00000000000000/* * dasd_base - Library for DASD related functions * * DASD related helper functions for accessing device information * * Copyright IBM Corp. 2013, 2017 * Copyright Red Hat Inc. 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef LIB_DASD_BASE_H #define LIB_DASD_BASE_H #ifdef __linux__ #include #endif #include #include #include /* the definition of a BUSID in the DASD driver is 20 */ #define DASD_BUS_ID_SIZE 20 typedef struct dasd_information2_t { unsigned int devno; /* S/390 devno */ unsigned int real_devno; /* for aliases */ unsigned int schid; /* S/390 subchannel identifier */ unsigned int cu_type : 16; /* from SenseID */ unsigned int cu_model : 8; /* from SenseID */ unsigned int dev_type : 16; /* from SenseID */ unsigned int dev_model : 8; /* from SenseID */ unsigned int open_count; unsigned int req_queue_len; unsigned int chanq_len; /* length of chanq */ char type[4]; /* from discipline.name, 'none' for unknown */ unsigned int status; /* current device level */ unsigned int label_block; /* where to find the VOLSER */ unsigned int FBA_layout; /* fixed block size (like AIXVOL) */ unsigned int characteristics_size; unsigned int confdata_size; unsigned char characteristics[64];/*from read_device_characteristics */ unsigned char configuration_data[256];/*from read_configuration_data */ unsigned int format; /* format info like formatted/cdl/ldl/... */ unsigned int features; /* dasd features like 'ro',... */ unsigned int reserved0; /* reserved for further use ,... */ unsigned int reserved1; /* reserved for further use ,... */ unsigned int reserved2; /* reserved for further use ,... */ unsigned int reserved3; /* reserved for further use ,... */ unsigned int reserved4; /* reserved for further use ,... */ unsigned int reserved5; /* reserved for further use ,... */ unsigned int reserved6; /* reserved for further use ,... */ unsigned int reserved7; /* reserved for further use ,... */ } dasd_information2_t; /* * values to be used for dasd_information2_t.format * 0x00: NOT formatted * 0x01: Linux disc layout * 0x02: Common disc layout */ #define DASD_FORMAT_NONE 0 #define DASD_FORMAT_LDL 1 #define DASD_FORMAT_CDL 2 struct dasd_eckd_characteristics { unsigned short cu_type; struct { unsigned char support:2; unsigned char async:1; unsigned char reserved:1; unsigned char cache_info:1; unsigned char model:3; } __attribute__ ((packed)) cu_model; unsigned short dev_type; unsigned char dev_model; struct { unsigned char mult_burst:1; unsigned char RT_in_LR:1; unsigned char reserved1:1; unsigned char RD_IN_LR:1; unsigned char reserved2:4; unsigned char reserved3:8; unsigned char defect_wr:1; unsigned char XRC_supported:1; unsigned char reserved4:1; unsigned char striping:1; unsigned char reserved5:4; unsigned char cfw:1; unsigned char reserved6:2; unsigned char cache:1; unsigned char dual_copy:1; unsigned char dfw:1; unsigned char reset_alleg:1; unsigned char sense_down:1; } __attribute__ ((packed)) facilities; unsigned char dev_class; unsigned char unit_type; unsigned short no_cyl; unsigned short trk_per_cyl; unsigned char sec_per_trk; unsigned char byte_per_track[3]; unsigned short home_bytes; unsigned char formula; union { struct { unsigned char f1; unsigned short f2; unsigned short f3; } __attribute__ ((packed)) f_0x01; struct { unsigned char f1; unsigned char f2; unsigned char f3; unsigned char f4; unsigned char f5; } __attribute__ ((packed)) f_0x02; } __attribute__ ((packed)) factors; unsigned short first_alt_trk; unsigned short no_alt_trk; unsigned short first_dia_trk; unsigned short no_dia_trk; unsigned short first_sup_trk; unsigned short no_sup_trk; unsigned char MDR_ID; unsigned char OBR_ID; unsigned char director; unsigned char rd_trk_set; unsigned short max_rec_zero; unsigned char reserved1; unsigned char RWANY_in_LR; unsigned char factor6; unsigned char factor7; unsigned char factor8; unsigned char reserved2[3]; unsigned char reserved3[6]; unsigned int long_no_cyl; } __attribute__ ((packed)); /* * struct format_data_t * represents all data necessary to format a dasd */ typedef struct format_data_t { unsigned int start_unit; /* from track */ unsigned int stop_unit; /* to track */ unsigned int blksize; /* sectorsize */ unsigned int intensity; } format_data_t; /* * values to be used for format_data_t.intensity */ #define DASD_FMT_INT_FMT_R0 1 /* write record zero */ #define DASD_FMT_INT_FMT_HA 2 /* write home address, also set FMT_R0 ! */ #define DASD_FMT_INT_INVAL 4 /* invalidate tracks */ #define DASD_FMT_INT_COMPAT 8 /* use OS/390 compatible disk layout */ #define DASD_FMT_INT_FMT_NOR0 16 /* remove permission to write record zero */ #define DASD_FMT_INT_ESE_FULL 32 /* release space for entire volume */ /* * struct format_check_t * represents all data necessary to evaluate the format of * different tracks of a dasd */ typedef struct format_check_t { /* Input */ struct format_data_t expect; /* Output */ unsigned int result; /* Error indication (DASD_FMT_ERR_*) */ unsigned int unit; /* Track that is in error */ unsigned int rec; /* Record that is in error */ unsigned int num_records; /* Records in the track in error */ unsigned int blksize; /* Block-size of first record in error */ unsigned int key_length; /* Key length of first record in error */ } format_check_t; /* * values to be used in format_check_t for indicating * possible format errors */ #define DASD_FMT_ERR_TOO_FEW_RECORDS 1 #define DASD_FMT_ERR_TOO_MANY_RECORDS 2 #define DASD_FMT_ERR_BLKSIZE 3 #define DASD_FMT_ERR_RECORD_ID 4 #define DASD_FMT_ERR_KEY_LENGTH 5 /* * struct profile_info_t * holds the profiling information */ typedef struct dasd_profile_info_t { unsigned int dasd_io_reqs; /* # of requests processed at all */ unsigned int dasd_io_sects; /* # of sectors processed at all */ unsigned int dasd_io_secs[32]; /* request's sizes */ unsigned int dasd_io_times[32]; /* requests's times */ unsigned int dasd_io_timps[32]; /* requests's times per sector */ unsigned int dasd_io_time1[32]; /* time from build to start */ unsigned int dasd_io_time2[32]; /* time from start to irq */ unsigned int dasd_io_time2ps[32]; /* time from start to irq */ unsigned int dasd_io_time3[32]; /* time from irq to end */ unsigned int dasd_io_nr_req[32]; /* # of requests in chanq */ } dasd_profile_info_t; /* * struct attrib_data_t * represents the operation (cache) bits for the device. * Used in DE to influence caching of the DASD. */ typedef struct attrib_data_t { unsigned char operation : 3; /* cache operation mode */ unsigned char reserved : 5; unsigned short nr_cyl; /* no of cyliners for read ahaed */ unsigned char reserved2[29]; /* for future use */ } __attribute__((packed)) attrib_data_t; /* definition of operation (cache) bits within attributes of DE */ #define DASD_NORMAL_CACHE 0x0 #define DASD_BYPASS_CACHE 0x1 #define DASD_INHIBIT_LOAD 0x2 #define DASD_SEQ_ACCESS 0x3 #define DASD_SEQ_PRESTAGE 0x4 #define DASD_REC_ACCESS 0x5 /* * Data returned by Sense Path Group ID (SNID) */ struct dasd_snid_data { struct { __u8 group : 2; __u8 reserve : 2; __u8 mode : 1; __u8 res : 3; } __attribute__((packed)) path_state; __u8 pgid[11]; } __attribute__((packed)); struct dasd_snid_ioctl_data { struct dasd_snid_data data; __u8 path_mask; } __attribute__((packed)); struct dasd_copypair_swap_data { char primary[DASD_BUS_ID_SIZE]; /* BUSID of primary */ char secondary[DASD_BUS_ID_SIZE]; /* BUSID of secondary */ /* Reserved for future updates. */ char reserved[64]; }; #ifndef __linux__ /* definition from hdreg.h */ struct hd_geometry { unsigned char heads; unsigned char sectors; unsigned short cylinders; unsigned long start; }; #endif #define DASD_IOCTL_LETTER 'D' /* Disable the volume (for Linux) */ #define BIODASDDISABLE _IO(DASD_IOCTL_LETTER, 0) /* Enable the volume (for Linux) */ #define BIODASDENABLE _IO(DASD_IOCTL_LETTER, 1) /* Reserve the device for the current LPAR */ #define BIODASDRSRV _IO(DASD_IOCTL_LETTER, 2) /* Release the device for the current LPAR */ #define BIODASDRLSE _IO(DASD_IOCTL_LETTER, 3) /* Unconditional reserve the device for the current LPAR */ #define BIODASDSLCK _IO(DASD_IOCTL_LETTER, 4) /* reset profiling information of a device */ #define BIODASDPRRST _IO(DASD_IOCTL_LETTER, 5) /* retrieve profiling information of a device */ #define BIODASDPRRD _IOR(DASD_IOCTL_LETTER, 2, dasd_profile_info_t) /* Get information on a dasd device (enhanced) */ #define BIODASDINFO2 _IOR(DASD_IOCTL_LETTER, 3, dasd_information2_t) /* Get Attributes (cache operations) */ #define BIODASDGATTR _IOR(DASD_IOCTL_LETTER, 5, attrib_data_t) /* #define BIODASDFORMAT _IOW(IOCTL_LETTER,0,format_data_t) , deprecated */ #define BIODASDFMT _IOW(DASD_IOCTL_LETTER, 1, format_data_t) /* Set Attributes (cache operations) */ #define BIODASDSATTR _IOW(DASD_IOCTL_LETTER, 2, attrib_data_t) /* Release Allocated Space */ #define BIODASDRAS _IOW(DASD_IOCTL_LETTER, 3, format_data_t) /* Swap copy pair */ #define BIODASDPPRCSWAP _IOW(DASD_IOCTL_LETTER, 4, struct dasd_copypair_swap_data) /* Get Sense Path Group ID (SNID) data */ #define BIODASDSNID _IOWR(DASD_IOCTL_LETTER, 1, struct dasd_snid_ioctl_data) /* Check device format according to format_data_t */ #define BIODASDCHECKFMT _IOWR(DASD_IOCTL_LETTER, 2, format_check_t) #ifndef __linux__ /* from */ #define HDIO_GETGEO 0x0301 #endif int dasd_check_format(const char *device, format_check_t *p); int dasd_format_disk(int fd, format_data_t *p); int dasd_disk_disable(const char *device, int *fd); int dasd_disk_enable(int fd); int dasd_release_space(const char *device, format_data_t *r); int dasd_get_blocksize(const char *device, unsigned int *blksize); int dasd_get_blocksize_in_bytes(const char *device, unsigned long long *blksize); int dasd_get_geo(const char *device, struct hd_geometry *geo); int dasd_get_info(const char *device, dasd_information2_t *info); int dasd_is_ro(const char *device, bool *ro); int dasd_reread_partition_table(const char *device, int ntries); int dasd_disk_reserve(const char *device); int dasd_disk_release(const char *device); int dasd_slock(const char *device); int dasd_get_cache(const char *device, attrib_data_t *attrib_data); int dasd_set_cache(const char *device, attrib_data_t *attrib_data); int dasd_query_reserve(const char *device); int dasd_profile(const char *device, dasd_profile_info_t *dasd_profile_info); int dasd_reset_profile(const char *device); int dasd_copy_swap(const char *device, struct dasd_copypair_swap_data *data); #endif /* LIB_DASD_BASE_H */ s390-tools-2.38.0/include/lib/dasd_sys.h000066400000000000000000000010521502674226300176200ustar00rootroot00000000000000/* * dasd - Library for DASD related functions * * DASD related helper functions for accessing device information via sysfs * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef LIB_DASD_SYS_H #define LIB_DASD_SYS_H #include int dasd_sys_raw_track_access(char *); int dasd_sys_ese(char *); int dasd_reset_chpid(char *, char *); int dasd_get_host_access_count(char *device); #endif /* LIB_DASD_SYS_H */ s390-tools-2.38.0/include/lib/libcpumf.h000066400000000000000000000152021502674226300176120ustar00rootroot00000000000000/* Copyright IBM Corp. 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef LIBCPUMF_H #define LIBCPUMF_H #include #include #define S390_CPUMF_CF "devices/cpum_cf/" #define S390_CPUMF_CFDIAG "devices/cpum_cf_diag/" #define S390_CPUMF_SF "devices/cpum_sf/" #define S390_CPUS_ONLINE "devices/system/cpu/online" #define S390_CPUMSF_BUFFERSZ "module/kernel/parameters/cpum_sfb_size" #define S390_SYSFS_PAI_CRYPTO "devices/pai_crypto/" #define S390_SYSFS_PAI_EXT "devices/pai_ext/" #define S390_SYSFS_PAI_NNPA S390_SYSFS_PAI_EXT "events/NNPA_ALL" #define CPUMF_CTRSET_NONE 0 #define CPUMF_CTRSET_EXTENDED 1 #define CPUMF_CTRSET_BASIC 2 #define CPUMF_CTRSET_PROBLEM_STATE 4 #define CPUMF_CTRSET_CRYPTO 8 #define CPUMF_CTRSET_MT_DIAG 32 /** * Return counter set a counter belongs to. * * Return the counter set a given counter belongs to, given the * CPU Measurement facility counter version first and second number. * * @param[in] ctr Counter number * @param[in] cfvn CPUM Counter facility first version number * @param[in] csvn CPUM Counter facility second version number * @retval >=0 Counter set number to counter belongs to */ int libcpumf_ctrset(int ctr, int cfvn, int csvn); /** * Read out the PMU type from a given file. * * Return the PMU type number assigned to this PMU by the kernel. This is * a non zero number. * * @param[in] dirname Name of the event directory in sysfs * @retval >=0 Number of PMU assigned by the kernel * @retval -1 PMU unknown to kernel */ int libcpumf_pmutype(const char *dirname); /** * Read out the PMU name from a given type. * * Return the PMU name this PMU was registered in kernel. If the PMU was * registered without a name, it is not listed in the directory. * The caller must free the memory returned by name. * * @param[in] wanted_type Type number of the PMU * @param[out] name Name of the PMU (when retval is 0) * @retval 0 PMU wanted_type detected and PMU name valid * @retval -1 No PMU with wanted_type */ int libcpumf_pmuname(unsigned int wanted_type, char **name); /** * Read out the CPU list from a given file name, for example from files * /sys/devices/system/cpu/online or /sys/devices/system/cpu/possible. * * Return the cpu_set_t created from parsing the CPU list in the second * parameter. * * @param[in] buffer Comma separated string of a CPU list * @param[in] filename Name of a sysfs CPU list file name * @param[out] mask Converted buffer into cpu_set_t mask structure * @retval 0 Successful conversion of cpulist * @retval -1 Unsuccessful conversion of cpulist */ int libcpumf_cpuset(const char *buffer, cpu_set_t *mask); int libcpumf_cpuset_fn(const char *filename, cpu_set_t *mask); /** * Read CPU Measurement Counting Facility hardware information * * Return true if CPU Measurement Counter facility information has been * retrieved and is valid. * * Return false if the information could not be extracted from the file. * * @param[out] cfvn Contains CPUMF counter first version number * @param[out] csvn Contains CPUMF counter second version number * @param[out] auth Contains CPUMF counter set authorization level * @retval true Information returned in parameters is valid * @retval false Information could not be retrieved */ bool libcpumf_cpumcf_info(int *cfvn, int *csvn, int *auth); /** * Return true if CPU Measurement Counter Facility is available. * * @retval true CPU Measurement Counter Facility is available * @retval false CPU Measurement Counter Facility is not available */ bool libcpumf_have_cpumcf(void); /** * Read CPU Measurement Sampling Facility hardware information * * Read all necessary information from /sysfs file /proc/service_levels * to return CPU Measurement Counter Sampling facility information * characteristics. * Return true on success and false when the data can not be retrieved. * * @param[out] min Minimum supported sampling interval * @param[out] max Maximum supported sampling interval * @param[out] speed Current CPU speed, number of CPU cylces per * microsecond * @param[out] basic_sz Basic sample size in bytes * @param[out] diag_sz Diagnostic sample size in bytes * @retval true Information returned in parameters is valid * @retval false Information could not be retrieved */ bool libcpumf_cpumsf_info(unsigned long *min, unsigned long *max, unsigned long *speed, int *basic_sz, int *diag_sz); /** * Return true if CPU Measurement Sampling Facility is available. * * @retval true CPU Measurement Sampling Facility is available * @retval false CPU Measurement Sampling Facility is not available */ bool libcpumf_have_cpumsf(void); /** * Return true if CPU Measurement Sampling Facility buffer sizes are * available. * * @retval true CPU Measurement Sampling Facility buffer sizes are * available * @retval false CPU Measurement Sampling Facility buffer sizes are * not available */ bool libcpumf_have_sfb(void); /** * Read CPU Measurement Sampling Facility supported sampling buffer sizes. * * Return the minimum and maximum CPU Measurement sampling facitity buffer * sizes supported. * Return true on success and false otherwise. * * @param[out] min Minimum supported sampling buffer size * @param[out] max Maximum supported sampling buffer size * @retval true Information returned in parameters is valid * @retval false Information could not be retrieved */ bool libcpumf_sfb_info(unsigned long *min, unsigned long *max); /** * Return true if PAI_CRYPTO counter Facility is supported. * * @retval true PAI_CRYPTO counter Facility is available * @retval false PAI_CRYPTO counter Facility is not available */ bool libcpumf_have_pai_crypto(void); /** * Return true if PAI_EXTENSION Facility is supported. * * @retval true PAI_EXTENSION counter Facility is available * @retval false PAI_EXTENSION counter Facility is not available */ bool libcpumf_have_pai_ext(void); /** * Return true if PAI_NNPA counter Facility is supported. This PMU facility * supports the Neural Network Processing Assist (NNPA) counter set. * * @retval true PAI_NNPA counter Facility is available * @retval false PAI_NNPA counter Facility is not available */ bool libcpumf_have_pai_nnpa(void); #endif s390-tools-2.38.0/include/lib/libzds.h000066400000000000000000000627731502674226300173170ustar00rootroot00000000000000/** * \file libzds.h * This is the main header file for the internal library libzds. * Please note that this library should currently only be used * by programs in the s390-tools package. It is not yet meant * for external use as interfaces and definitions may change * without further notice. * * Copyright IBM Corp. 2013, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ /** * @mainpage * The libzds is a s390-tools internal library for use with DASD * devices in raw_track_access mode. * * The regular operation mode of the DASD device driver allows only to * access ECKD DASDs that were formatted with a specific record * layout. The raw access mode of the DASD device driver allows to * access any kind of ECKD DASD, but requires the correct use of * the DIRECT_IO interface and leaves the interpretation of the data * format on these devices to the user. * * This library supports the use of raw DASD devices by providing * functions that * @li access the device with DIRECT_IO and the correct buffer alignment * @li provide access to label and VTOC data on the device * @li provide access to simple z/OS data set formats * (physical sequential (PS) and partitioned data sets (PDS)) * * * @section interface_groups Library Interface * * @subsection interface_structures Data Structures * * The data structures provided by this library can be divided into * two types: Structures that represent external hardware and software * interfaces, and structures that are defined by libzds itself: * * @ref external_interfaces * * @ref libzds_data * * * @subsection interface_functions Functions * * The functions provided by this library are divided into 5 categories: * base, low, mid, and highlevel functions and helper functions. * * The lower the level, the less dependent are the functions on the data * that is stored on the DASDs. The higher the level the more abstract * are the implemented concepts. * * The base level functions are needed to setup the internal data * structures which the other functions work on. Otherwise, he use of higher * level functions does not require the use of low level functions. * For example: To simply read data from a data set, you just need the * base and high level functions and can ignore the low and mid level * functions. * * \ref libzds_functions_base * * \ref libzds_functions_low * * \ref libzds_functions_mid * * \ref libzds_functions_high * * \ref libzds_functions_helper * * * @section naming_scheme Naming Scheme * * All interface functions start with lzds_ for libzds (could be changed later * but libzds_ is quite long). Next is the entity the function works on, * for example * @li @c lzds_zdsroot_... * @li @c lzds_dasd_... * * So if you know what entity you want to work on, you know where to look. * * Then follows the operation (add, get, read, alloc, ...). * There are several verbs that can mean 'access data'. * As a guideline we define the following meaning for use in this library: * @li read: Will result in data being read from a device * @li get: Get a value from one of the internal structures * @li extract: Use the internal data to create higher level data, * e.g. use the VTOC information of a DASD to create data * set structures * @li alloc: Create and return a libzds data structure * * @note For every alloc function there shall be a matching free function to * release the memory. However, often the structure is created in the * context of a another structure, but when it is freed, that is done in * its own context. For example: * lzds_zdsroot_alloc_dasditerator is matched by lzds_dasditerator_free * * Finally the object of the operation, what you want to get or achieve, * e.g lzds_ds_get_is_PDS * * For the parmeter list, the general rule is: * The subject comes first, the object last, further parameters in between. */ #ifndef LIB_LIBZDS_H /** * @brief Watchdog for libzds.h inclusion. */ #define LIB_LIBZDS_H #include "lib/util_base.h" #include "lib/util_list.h" #include "vtoc.h" #include /** * \defgroup external_interfaces External constants and structures. * @{ * @brief These constants and structures are related to * hardware and software interfaces that are specified outside of * libzds. * * @li For a description of ECKD data formats see * 'IBM 3990/9390 Storage Control Reference', Document Number GA32-0274-05 * @li For a description of VTOC entries see * 'z/OS DFSMSdfp Advanced Services', Document Number SC26-7400-11 * @li For a description of physical sequential and partitioned data sets see * 'z/OS DFSMS Using Data Sets', Document Number SC26-7410-11 * @li For a description of the Linux on System z DASD device driver see * 'Device Drivers, Features, and Commands (kernel 3.7)', * Document Number SC33-8411-18 */ /** * @brief The size of one raw track when read via the DASD device driver * with raw_track_access. * * When reading from a DASD in raw_track_access mode, you need to * align your I/O to multiples of this size. */ #define RAWTRACKSIZE 65536 /** * @brief Maximum size of one record on a track * * This is the maximum size of a single record on a track. If a track contains * multiple records, the additional overhead will cause the sum of these * multiple records to be smaller than the biggest single record, so MAXRECSIZE * is also the upper limit for user data that a single track can hold. */ #define MAXRECSIZE 56664 /** * @brief Maximum number of extents a data set can hold. * * We do not handle extended format data sets so we can have a total of 16 * extents per dataset (3 in the f1 and 13 in the f3 label). */ #define MAXEXTENTS 16 /** * @brief Maximum size of a data set name string (including one byte for * 0-termination) */ #define MAXDSNAMELENGTH 45 /** * @brief Maximum size of a partitioned data set member name string * (including one byte for 0-termination) */ #define MEMBERNAMELENGTH 9 /** * @brief The maximum number of volumes (devices) that a multi volume data * set can span. */ #define MAXVOLUMESPERDS 59 /** * @brief Eight bytes of 0xFF are used in several cases to designate the end of data. * */ #define ENDTOKEN 0xFFFFFFFFFFFFFFFFULL #define MAX_LINE_LENGTH 512 #define MAX_SERVER 3 /** * @brief This structure represents the count field in an ECKD record. */ struct eckd_count { /** @brief record ID * * In general the record ID is defined as just a 5 byte field. * The interpretation of these 5 bytes as a struct cchhb_t is a common * convention, which we assume here as well. */ cchhb_t recid; /** @brief key length */ unsigned char kl; /** @brief data length */ unsigned short dl; } __attribute__ ((packed)); /** * @brief A generic structure to describe a data set control block (DSCB) * * The elements of the VTOC are called DSCBs. All DSCBs have in common that * they have a size of 140 bytes, and byte 44 is the identifier that * determines the type of DSCB. */ struct dscb { /** @brief key area * * This part of the DSCB is usually stored in the key part of an * ECKD record in the VTOC. The contents depends on the format. */ char key[44]; /** @brief Format identifier * * This identifier determines the data layout of the rest of the * DSCB. In a format-x DSCB this field is called DSxFMTID. * The identifiers for format-1 to format-9 are the respective * EBCDIC characters '1' to '9' (0xf1 to 0xf9). * An empty DSCB record (format-0 DSCB) contains 140 zeros, so * here the format id is 0x00. */ char fmtid; /** @brief The residual data part of the DSCB * * The contents depends on the format. */ char data[95]; } __attribute__ ((packed)); /** * @brief This structure represents a segment descriptor word (SDW), * record descriptor word (RDW) or block descriptor word (BDW). * * These are used to describe data set blocks and records. * (see z/OS DFSMS Using Data Sets) */ struct segment_header { /** @brief Is this an empty segment (valid for SDW) */ unsigned short nullsegment:1; /** @brief Length of the segment */ unsigned short length:15; /** @brief reserved */ unsigned char reserved1:6; /** @brief Segment control code (valid for SDW) * * 0: logical record consists of just this segment, * 1: first segment in the logical record, * 2: last segment in the logical record, * 3: intermediate in the logical record */ unsigned char position:2; /** @brief reserved */ unsigned char reserved3:8; } __attribute__ ((packed)); /** * @brief The key length of a PDS directory member record. */ #define PDS_DIR_KL 8 /** * @brief The data length of a PDS directory member record. */ #define PDS_DIR_DL 256 /** * @brief This structure represents and entry in the PDS directory and * describes a member of the data set. * * This structure represents only the fixed part of the member * entry. The variable size of the user data part is determined * by the entry user_data_count. * (see z/OS DFSMS Using Data Sets) */ struct pds_member_entry { /** @brief Member name */ char name[8]; /** @brief Start track of the member (relative to start of the PDS) */ unsigned short track; /** @brief Start record of the member */ unsigned char record; /** @brief Is this entry an alias for another entry? */ unsigned char is_alias:1; /** @brief How many TTRN note lists are contained in the user data. */ unsigned char ttrn_count:2; /** @brief The user_data_count value counts 'half words' i.e. shorts! */ unsigned char user_data_count:5; } __attribute__ ((packed)); /** @} */ /* end of group hardware */ /** * @defgroup libzds_data libzds data structures * @{ * @brief These are the data structures used by the libzds API. * * For users of libzds these are opaque data structures and they have * no dependency on the implementation details of these structures. * All libzds interface functions work on pointers to these structures, * so programs that use the library do not need to know them either. * This prevents users from accessing the data in unsupported ways * allows us to change the implementation without changing the * interface. */ /** * @struct zdsroot * @brief The root of all device and data set information. * * Note that data sets do not belong to DASDs, as they * may span over more than one DASD. */ struct zdsroot; /** * @struct raw_vtoc * @brief The VTOC is a directory of data sets on one DASD * * As the VTOC is the data area on the DASD that describes all data sets, * this library will often have to refer to the various records in the VTOC. * To make this more efficient, we will read the whole VTOC once and identify * all elements (DSCBs). The raw data of the VTOC tracks and the index to the * DSCBs is stored. */ struct raw_vtoc { /** @brief The raw track data */ char *rawdata; /** @brief This size of the raw track data in bytes */ unsigned long long rawdatasize; /** @brief An array with pointers to the various DSCBs in the rawdata */ char **vtocindex; /** @brief Number of entries in the index */ unsigned int vtocindexcount; /** @brief Number of records per VTOC track * * @note While the DS4DEVDT field in the format 4 DSCB names the number * if DSCBs per VTOC track, we count the records, which is DS4DEVDT + 1 * for record 0. */ unsigned int vtoc_rec_per_track; /** @brief The track number at which the vtoc begins on the DASD */ unsigned int vtoctrackoffset; /** @brief Start record of VTOC. * * The rawdata contains full tracks. This is the number of the first * record that actually belongs to the VTOC */ unsigned int vtocrecno; /** @brief The DASD this vtoc was read from */ struct dasd *dasd; /** @brief Detailed error messages in case of a problem */ struct errorlog *log; }; /** * @struct dasd * @brief Represents one physical device, may have a vtoc */ struct dasd { /** @brief List head used to store a list of DASDs in struct zdsroot */ struct util_list_node list; /** @brief Name of the block device, e.g. /dev/dasde */ char *device; /** @brief File descriptor for the block device. * * The device is kept open for as along as the library uses it. * This lets the system know that the device is still in use. */ int inusefd; /* @brief where to find the volume label */ unsigned int label_block; /** @brief Device geometry. How many cylinders does the DASD have. */ unsigned int cylinders; /** @brief Device geometry. How many heads does the DASD have. */ unsigned int heads; /** @brief The VTOC data that has been read from this device */ struct raw_vtoc *rawvtoc; /** @brief The volume label that has been read from this device */ volume_label_t *vlabel; /** @brief Detailed error messages in case of a problem */ struct errorlog *log; }; /** * @struct dasditerator * @brief Allows to iterate over all dasds in the zdsroot */ struct dasditerator; /** * @struct dasdhandle * @brief Represents the state of a DASD device while it is in use. * * For applications that need to read data directly from a DASD device. * The idea is to have an abstract handle for a DASD that is in * use, similar to a FILE pointer */ struct dasdhandle; /** * @struct dscbiterator * @brief allows to iterate over all DSCBs in a vtoc */ struct dscbiterator; /** * @struct dataset * @brief The whole of one data set * * May refer to one or more dataset parts * and may have a list of partitioned dataset members. */ struct dataset; /** * @struct dsiterator * @brief Allows to iterate over all data sets in the zdsroot */ struct dsiterator; /** * @struct pdsmember * @brief If a data set is a partitioned data set (PDS) it * may have zero or more PDS members */ struct pdsmember; /** * @struct memberiterator * @brief Allows to iterate over all members in the dataset */ struct memberiterator; /** * @struct dshandle * @brief Represents the state of a data set while it is in use * * This state includes the I/O buffers used for reading, * the position within the data set, options used for processing the data, * etc. The idea is to have an abstract handle for a data set that is in * use, similar to a FILE pointer. */ struct dshandle; /** * @struct error_log * @brief A stack of error messages that are related to the last error */ struct errorlog; /** @} */ /* end of group libzds_data */ /** * @defgroup libzds_functions_base Base functions * @{ * @brief These functions are basic setup functions which need to * be used before any of the low, mid or high level functions * can be used. These functions concern the allocation and * initialization of the zdsroot and the basic handling of devices. */ /** * @brief Allocate a new zdsroot structure. */ int lzds_zdsroot_alloc(struct zdsroot **root); /** * @brief Free the memory of the given zdsroot structure. */ void lzds_zdsroot_free(struct zdsroot *root); /** * @brief Add a DASD device to the zdsroot. */ int lzds_zdsroot_add_device(struct zdsroot *root, const char *devnode, struct dasd **dasd); /** * @brief Get the errorlog. */ void lzds_dasd_get_errorlog(struct dasd *dasd, struct errorlog **log); /** * @brief Allocate index that allows to iterate through all DASDs * stored in the root. */ int lzds_zdsroot_alloc_dasditerator(struct zdsroot *root, struct dasditerator **it); /** * @brief Free the dasditerator structure. */ void lzds_dasditerator_free(struct dasditerator *it); /** * @brief Get the next dasd structure. */ int lzds_dasditerator_get_next_dasd(struct dasditerator *it, struct dasd **dasd); /** * @brief Return the device node name that was used for this dasd. */ void lzds_dasd_get_device(struct dasd *dasd, char **device); /** * @brief Get the dasd structure that belongs to the given device. */ int lzds_zdsroot_get_dasd_by_node_name(struct zdsroot *root, const char *device, struct dasd **dasd); /** * @brief Get the errorlog. */ void lzds_zdsroot_get_errorlog(struct zdsroot *root, struct errorlog **log); int lzds_errorlog_fprint(struct errorlog *log, FILE *stream); /** @} */ /* end of group libzds_functions_base */ /** * @defgroup libzds_functions_low Low level interface functions. * @{ * @brief Very basic functions, should all work on every kind DASD. * * These functions give access to the data on a DASD on a very * low abstraction level. They do not dependent on the data on the * device itself. * */ /** * @brief Based on the dasd device geometry, compute a track number from a * given cchh_t (cylinder, head) address value. */ void lzds_dasd_cchh2trk(struct dasd *dasd, cchh_t *p, unsigned int *track); /** * @brief Get the number of cylinders that this DASD has. */ void lzds_dasd_get_cylinders(struct dasd *dasd, unsigned int *cylinders); /** * @brief Get the number of heads, that a cylinder of this DASD has. */ void lzds_dasd_get_heads(struct dasd *dasd, unsigned int *heads); /** * @brief Allocate a new dasd context structure for given data set. */ int lzds_dasd_alloc_dasdhandle(struct dasd *dasd, struct dasdhandle **dasdh); /** * @brief Free memory that was allocated for a dasdhandle. */ void lzds_dasdhandle_free(struct dasdhandle *dasdh); /** * @brief This makes the dasd context ready for read operations. */ int lzds_dasdhandle_open(struct dasdhandle *dasdh); /** * @brief This closes the file descriptor connected with the dasdhandle. */ int lzds_dasdhandle_close(struct dasdhandle *dasdh); /** * @brief Read raw tracks from the dasdhandle. */ int lzds_dasdhandle_read_tracks_to_buffer(struct dasdhandle *dasdh, unsigned int starttrck, unsigned int endtrck, char *trackdata); /** @} */ /* end of group libzds_functions_low */ /** * @defgroup libzds_functions_mid Mid level interface functions * @{ * @brief Functions that give access to low level structures like VTOC * records. * * These functions give access to and rely on the meta data stored on * the DASD, in particular the VTOC. * These functions take the structure of the data on the * device into account, so they may fail if this data is not * correct (e.g. if the VTOC is broken). * * @todo The interface of the mid level functions is not properly structured yet. * */ /** * @brief Read the volume label from device. The data as stored as * part of the struct dasd. */ int lzds_dasd_read_vlabel(struct dasd *dasd); /** * @brief Get the previously read volume label data.. */ int lzds_dasd_get_vlabel(struct dasd *dasd, struct volume_label **vlabel); /** * @brief Read the vtoc data from device. The data as stored as part * of the struct dasd. */ int lzds_dasd_read_rawvtoc(struct dasd *dasd, struct raw_vtoc *vtoc); /** * @brief Read the vtoc data from device. The data as stored as part * of the struct dasd. */ int lzds_dasd_alloc_rawvtoc(struct dasd *dasd); /** * @brief Get the previously read raw_vtoc data. */ int lzds_dasd_get_rawvtoc(struct dasd *dasd, struct raw_vtoc **vtoc); /** * @brief Allocate index that allows to iterate through all DSCB * records stored in the raw_vtoc. */ int lzds_raw_vtoc_alloc_dscbiterator(struct raw_vtoc *rawvtoc, struct dscbiterator **it); /** * @brief Free the iterators memory */ void lzds_dscbiterator_free(struct dscbiterator *it); /** * @brief Get the next DSCB in the VTOC. */ int lzds_dscbiterator_get_next_dscb(struct dscbiterator *it, struct dscb **dscb); /** * @brief Find and get a specific DSCB record by its cylinder, head * and record address (cchhb_t) */ int lzds_raw_vtoc_get_dscb_from_cchhb(struct raw_vtoc *rv, cchhb_t *p, struct dscb **dscb); /** @} */ /* end of group libzds_functions_mid */ /** * @defgroup libzds_functions_high High level interface functions * @{ * @brief These functions give access to the data on a DASD on a * high abstraction level. * * These functions abstract away most of the low level details. * They give access to the user data stored on the DASD using abstract * concepts like 'data set' without requiring the user * to do any low level analysis. */ /** * @brief Search zdsroot for a specific data set. */ int lzds_zdsroot_find_dataset(struct zdsroot *root, const char *name, struct dataset **ds); /** * @brief Allocate an iterator that will allow to iterate through all * datasets on the index. */ int lzds_zdsroot_alloc_dsiterator(struct zdsroot *zdsroot, struct dsiterator **it); /** * @brief Free memory of the given data set iterator. */ void lzds_dsiterator_free(struct dsiterator *it); /** * @brief Return the next data set the iterator points to. */ int lzds_dsiterator_get_next_dataset(struct dsiterator *it, struct dataset **ds); /** * @brief Get the 'partitioned data set' status of a data set? */ void lzds_dataset_get_is_PDS(struct dataset *ds, int *ispds); /** * @brief Are all parts of a multi volume data set available? */ void lzds_dataset_get_is_complete(struct dataset *ds, int *iscomplete); /** * @brief Can the data set be opened and read with this library? */ void lzds_dataset_get_is_supported(struct dataset *ds, int *issupported); /** * @brief Get the name of a dataset as ASCII string. */ void lzds_dataset_get_name(struct dataset *ds, char **name); /** * @brief Get the format 1 DSCB for a data set. In case of a multi volume * data set it returns the DSCB of the first volume. */ void lzds_dataset_get_format1_dscb(struct dataset *ds, format1_label_t **f1); /** * @brief Search the data set for a given member name and if a matching * member is found return a struct pdsmember. */ int lzds_dataset_get_member_by_name(struct dataset *ds, char *membername, struct pdsmember **member); /** * @brief Allocate an iterator that will allow to iterate through all members * on a datasets. */ int lzds_dataset_alloc_memberiterator(struct dataset *ds, struct memberiterator **it); /** * @brief Free memory of the given member iterator. */ void lzds_memberiterator_free(struct memberiterator *it); /** * @brief Return the next data set member the iterator points to. */ int lzds_memberiterator_get_next_member(struct memberiterator *it, struct pdsmember **member); /** * @brief Allocate a new data set context structure for given data set. */ int lzds_dataset_alloc_dshandle(struct dataset *ds, unsigned int tracks_per_frame, struct dshandle **dsh); /** * @brief Free the memory of the given dshandle structure. */ void lzds_dshandle_free(struct dshandle *dsh); /** * @brief If the dsh points to a partitioned data set, this function will * set which member of that PDS is read via the dsh. */ int lzds_dshandle_set_member(struct dshandle *dsh, char *membername); /** * @brief Read out the member pointer that has been set on this dshandle. */ void lzds_dshandle_get_member(struct dshandle *dsh, struct pdsmember **member); /** * @brief Set the flag that causes the library to keep the record descriptor * word (RDW) of variable records in the data stream. */ int lzds_dshandle_set_keepRDW(struct dshandle *dsh, int keepRDW); /** * @brief Read out the current setting of the RDW flag. */ void lzds_dshandle_get_keepRDW(struct dshandle *dsh, int *keepRDW); /** * @brief Prepares the dsh and the related devices for read operations. */ int lzds_dshandle_open(struct dshandle *dsh); /** * @brief Matching close operation for the data set context. */ void lzds_dshandle_close(struct dshandle *dsh); /** * @brief Read data from the data set to which the dsh points. */ int lzds_dshandle_read(struct dshandle *dsh, char *buf, size_t size, ssize_t *rcsize); /** * @brief Move buffer position of dsh to offset. */ int lzds_dshandle_lseek(struct dshandle *dsh, long long offset, long long *rcoffset); /** * @brief Get the current buffer position. */ void lzds_dshandle_get_offset(struct dshandle *dsh, long long *offset); /** * @brief Get the errorlog. */ void lzds_dshandle_get_errorlog(struct dshandle *dsh, struct errorlog **log); /** * @brief Set an upper limit for the seek buffer. */ int lzds_dshandle_set_seekbuffer(struct dshandle *dsh, unsigned long long seek_buffer_size); /** * @brief Set iconv handle for codepage conversion. */ int lzds_dshandle_set_iconv(struct dshandle *dsh, iconv_t *iconv); /** * @brief Get the size of the data set in number of tracks (sum of all extents). */ void lzds_dataset_get_size_in_tracks(struct dataset *ds, unsigned long long *tracks); /** * @brief Get the name of a partitioned dataset member. */ void lzds_pdsmember_get_name(struct pdsmember *member, char **name); /** * @brief Extract the data set information from the rawvtoc stored in the * dasd and add it to the list of data sets stored in the zdsroot. */ int lzds_zdsroot_extract_datasets_from_dasd(struct zdsroot *root, struct dasd *dasd); void lzds_dslist_free(struct zdsroot *root); int lzds_ping_rest(struct dshandle *dsh, char *server); /** @} */ /* end of group libzds_functions_high */ /** * @defgroup libzds_functions_helper Helper functions * @{ * * @brief These functions do not fit in the hierarchy of the other * libzds functions, but are useful helpers. */ /** * @brief Translates a DS1RECFM byte to a recfm format string. */ void lzds_DS1RECFM_to_recfm(char DS1RECFM, char *buffer); int lzds_analyse_open_count(struct zdsroot *root, int warn); /** @} */ /* end of group libzds_functions_helper */ int lzds_rest_get_enq(struct dshandle *dsh, char *server); int lzds_rest_release_enq(struct dshandle *dsh, char *server); int lzds_rest_ping(struct dshandle *dsh, char *server); #endif /* LIB_LIBZDS_H */ s390-tools-2.38.0/include/lib/pci_list.h000066400000000000000000000041541502674226300176230ustar00rootroot00000000000000/** * @defgroup pci_list_h libzpci: zPCI device handling * @{ * @brief Work with zPCI devices * * Copyright IBM Corp. 2024 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef LIB_ZPCI_PCI_LIST_H #define LIB_ZPCI_PCI_LIST_H #include #include #include "util_list.h" enum zpci_pft { ZPCI_PFT_UNCLASSIFIED = 0x00, ZPCI_PFT_ROCE_EXPRESS = 0x02, ZPCI_PFT_ROCE_EXPRESS2 = 0x0a, ZPCI_PFT_CNW = 0x0d, ZPCI_PFT_NETH = 0x0c, ZPCI_PFT_NETD = 0x0f, ZPCI_PFT_NVME = 0x0b, ZPCI_PFT_ISM = 0x05 }; /* * Follows RFC 2863 operational states with the * numeric values from IF_OPER_* in linux/if.h: */ typedef uint8_t operstate_t; struct zpci_netdev { char *name; operstate_t operstate; }; struct zpci_dev { struct util_list_node entry; /* PCI Domain */ uint32_t domain_nr; /* PCI Bus (8 bits), Device (5 bits), Function (3 bits) */ union { uint16_t val; struct { uint16_t bus : 8; uint16_t dev : 5; uint16_t fn : 3; }; } bdf; /* Function attributes (see linux/Documentation/arch/s390/pci.rst) */ uint32_t fid; uint32_t uid; uint16_t pchid; uint16_t vfn; uint8_t port; enum zpci_pft pft; bool uid_is_unique; /* Configuration state 0 - Standby, 1 Configured */ bool conf; /* Associated netdevs if any */ int num_netdevs; struct zpci_netdev *netdevs; }; /** * Get if a PCI device is a PCI Virtual Function * * @param[in] zdev The device in question * * @return true if the device is a VF false otherwise */ static inline bool zpci_is_vf(struct zpci_dev *zdev) { return !!zdev->vfn; } struct util_list *zpci_dev_list(void); void zpci_free_dev_list(struct util_list *zpci_list); void zpci_free_dev(struct zpci_dev *zdev); char *zpci_pci_addr(struct zpci_dev *zdev); const char *zpci_pft_str(struct zpci_dev *zdev); const char *zpci_operstate_str(operstate_t state); operstate_t zpci_operstate_from_str(const char *oper_str); struct zpci_dev *zpci_find_by_netdev(struct util_list *zpci_list, char *netdev_name, struct zpci_netdev **netdev); #endif /* LIB_ZPCI_PCI_LIST_H */ s390-tools-2.38.0/include/lib/pci_sclp.h000066400000000000000000000027041502674226300176100ustar00rootroot00000000000000/** * @defgroup pci_sclp_h libzpci: zPCI device handling * @{ * @brief Issue SCLPs for zPCI devices * * Copyright IBM Corp. 2024 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef LIB_ZPCI_PCI_SCLP_H #define LIB_ZPCI_PCI_SCLP_H #include #include #include "lib/zt_common.h" #define SCLP_ERRNOTIFY_AQ_RESET 0 #define SCLP_ERRNOTIFY_AQ_DECONF 1 #define SCLP_ERRNOTIFY_AQ_REPORT_ERR 2 #define SCLP_ERRNOTIFY_AQ_OPTICS_DATA 3 #define SCLP_ERRNOTIFY_ID_ZPCICTL 0x4713 #define SCLP_ERRNOTIFY_ID_OPTICSMON 0x4714 #define SCLP_ERRNOTIFY_DATA_SIZE 4054 struct zpci_report_error_header { __u8 version; /* Interface version byte */ __u8 action; /* Action qualifier byte * 0: Adapter Reset Request * 1: Deconfigure and repair action requested * 2: Informational Report * 3: Optics Data */ __u16 length; /* Length of Subsequent Data (up to 4K – SCLP header) */ } __packed; struct zpci_report_error_data { __u64 timestamp; __u64 err_log_id; /* We cannot exceed a total of 4074 bytes (header + data) */ char log_data[SCLP_ERRNOTIFY_DATA_SIZE]; } __packed; struct zpci_report_error { struct zpci_report_error_header header; struct zpci_report_error_data data; } __packed; int zpci_sclp_issue_action(char *pci_addr, int action, char *data, size_t length, u64 err_log_id); #endif /* LIB_ZPCI_PCI_SCLP_H */ s390-tools-2.38.0/include/lib/util.h000066400000000000000000000005641502674226300167730ustar00rootroot00000000000000/* * util - Utility function library * * Library helper functions * * Copyright IBM Corp. 2013, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef UTIL_H #define UTIL_H #include "util_base.h" #include "util_list.h" #include "util_part.h" #endif /* UTIL_H */ s390-tools-2.38.0/include/lib/util_arch.h000066400000000000000000000023231502674226300177630ustar00rootroot00000000000000/** * @defgroup util_arch_h util_arch: General architecture helpers * @{ * @brief General architecture helpers * * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef LIB_UTIL_ARCH_H #define LIB_UTIL_ARCH_H enum util_arch_machine_type { UTIL_ARCH_MACHINE_TYPE_UNKNOWN = 0, UTIL_ARCH_MACHINE_TYPE_Z10_EC = 2097, UTIL_ARCH_MACHINE_TYPE_Z10_BC = 2098, UTIL_ARCH_MACHINE_TYPE_ZE_196 = 2817, UTIL_ARCH_MACHINE_TYPE_ZE_114 = 2818, UTIL_ARCH_MACHINE_TYPE_ZE_EC12 = 2827, UTIL_ARCH_MACHINE_TYPE_ZE_BC12 = 2828, UTIL_ARCH_MACHINE_TYPE_Z13 = 2964, UTIL_ARCH_MACHINE_TYPE_Z13_S = 2965, UTIL_ARCH_MACHINE_TYPE_Z14 = 3906, UTIL_ARCH_MACHINE_TYPE_Z14_ZR1 = 3907, UTIL_ARCH_MACHINE_TYPE_Z15 = 8561, UTIL_ARCH_MACHINE_TYPE_Z15_T02 = 8562, UTIL_ARCH_MACHINE_TYPE_Z16 = 3931, UTIL_ARCH_MACHINE_TYPE_Z16_A02 = 3932, UTIL_ARCH_MACHINE_TYPE_Z17 = 9175, UTIL_ARCH_MACHINE_TYPE_Z17_2 = 9176, }; int util_arch_machine_type(void); const char *util_arch_machine_type_str(void); const char *util_arch_machine_type_to_str(int type); unsigned long util_arch_hsa_maxsize(void); #endif /** LIB_UTIL_ARCH_H @} */ s390-tools-2.38.0/include/lib/util_base.h000066400000000000000000000034711502674226300177650ustar00rootroot00000000000000/* * util - Utility function library * * General helper functions * * Copyright IBM Corp. 2013, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef LIB_UTIL_BASE_H #define LIB_UTIL_BASE_H #include #include #include #include "zt_common.h" #include "lib/util_libc.h" void util_hexdump(FILE *fh, const char *tag, const void *data, int cnt); void util_hexdump_grp(FILE *fh, const char *tag, const void *data, int group, int cnt, int indent); void util_print_indented(const char *str, int indent); const char *util_libdir(void); const char *util_libdir_path(const char *filename); const char *util_datadir(void); const char *util_datadir_path(const char *filename); static inline void util_ptr_vec_free(void **ptr_vec, int count) { int i; if (!ptr_vec || count < 0) return; for (i = 0; i < count; i++) free(ptr_vec[i]); free(ptr_vec); } /* * Expand size of dynamic array (element_t *) by one element * * @param[in,out] array Pointer to array (element_t **) * @param[in,out] num Pointer to integer containing number of elements */ #define util_expand_array(array, num) \ do { \ unsigned int __size = sizeof(*(*(array))); \ *(array) = util_realloc(*(array), ++(*(num)) * __size); \ memset(&((*(array))[*(num) - 1]), 0, __size); \ } while (0) /* * Append one element to dynamic array (element_t *) * * @param[in,out] array Pointer to array (element_t **) * @param[in,out] num Pointer to integer containing number of elements * @param[in] element Element to add (element_t) */ #define util_add_array(array, num, element) \ do { \ util_expand_array(array, num); \ (*(array))[*(num) - 1] = (element) ; \ } while (0) #endif /* LIB_UTIL_BASE_H */ s390-tools-2.38.0/include/lib/util_exit_code.h000066400000000000000000000011461502674226300210130ustar00rootroot00000000000000/** * @defgroup util_exit_code_h util_exit_code: General purpose exit codes * @{ * @brief General purpose exit codes * * Copyright IBM Corp. 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef LIB_UTIL_EXIT_CODE_H #define LIB_UTIL_EXIT_CODE_H typedef enum { UTIL_EXIT_OK = 0, /* Program finished successfully */ UTIL_EXIT_RUNTIME_ERROR = 15, /* A run-time error occurred */ UTIL_EXIT_OUT_OF_MEMORY = 22, /* Not enough available memory */ } util_exit_code_t; #endif /** LIB_UTIL_EXIT_CODE_H @} */ s390-tools-2.38.0/include/lib/util_file.h000066400000000000000000000030121502674226300177610ustar00rootroot00000000000000/** * @defgroup util_file_h util_file: File read/write interface * @{ * @brief Read and write files * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef LIB_UTIL_FILE_H #define LIB_UTIL_FILE_H #include #include "lib/util_exit_code.h" int util_file_read_line(char *str, size_t size, const char *fmt, ...); int util_file_read_i(int *val, int base, const char *fmt, ...); int util_file_read_l(long *val, int base, const char *fmt, ...); int util_file_read_ll(long long *val, int base, const char *fmt, ...); int util_file_read_ui(unsigned int *val, int base, const char *fmt, ...); int util_file_read_ul(unsigned long *val, int base, const char *fmt, ...); int util_file_read_ull(unsigned long long *val, int base, const char *fmt, ...); int util_file_write_s(const char *str, const char *fmt, ...); int util_file_write_l(long val, int base, const char *fmt, ...); int util_file_write_ll(long long val, int base, const char *fmt, ...); int util_file_write_ul(unsigned long val, int base, const char *fmt, ...); int util_file_write_ull(unsigned long long val, int base, const char *fmt, ...); int util_file_read_va(const char *path, const char *fmt, ...); util_exit_code_t util_file_read_fd_buf(FILE *fd, void **buffer_ptr, size_t *size_ptr); char *util_file_read_fd(FILE *fd, int chomp); char *util_file_read_text_file(const char *path, int chomp); #endif /** LIB_UTIL_FILE_H @} */ s390-tools-2.38.0/include/lib/util_fmt.h000066400000000000000000000202071502674226300176350ustar00rootroot00000000000000/* * util_fmt - Format structured key-value data as JSON, text pairs, or CSV * * Copyright IBM Corp. 2024 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. * * This module provides helper functions for converting structured key-value * data into different output formats. * * Benefits: * - Output format can be dynamically configured at run-time * - Callers do not need to add extra code for each output format * - Some format-specific requirements such as quoting, indentation, and * comma-placement are automated * * Basic API calling sequence: * * util_fmt_init() => Select output format * util_fmt_obj_start() => Start a new object or list * util_fmt_pair() => Emit a key-value pair * util_fmt_obj_end() => End the latest object or list * util_fmt_exit() => Cleanup * * Note: * - Supported data elements are objects, lists and key-value pairs (mappings) * - Scalars are only supported as part of a mapping * - For CSV output and key filtering, mapping keys must be unique - this can * be achieved either by choosing unique key names or by including object * names via the FMT_PREFIX flag * - For CSV output, at least one object or list with the FMT_ROW flag must be * emitted * - Common tool-specific meta-information such as API-level, tool version, * etc. is automatically added to the output */ #ifndef LIB_UTIL_FMT_H #define LIB_UTIL_FMT_H #include #include /* Flag value for default behavior (all flag types). */ #define FMT_DEFAULT 0 /* Names of supported output format types. */ #define FMT_TYPE_NAMES "json json-seq pairs csv" /** * enum util_fmt_t - Output format types. * @FMT_JSON: JavaScript Object Notation output data structure * @FMT_JSONSEQ: Sequence of JSON data structures according to RFC7464 * @FMT_PAIRS: Textual key=value pairs * @FMT_CSV: Comma-separated-values output * * Use these types with util_fmt_init() to control the output format. */ enum util_fmt_t { FMT_JSON, FMT_JSONSEQ, FMT_PAIRS, FMT_CSV, }; /** * enum util_fmt_flags_t - Format control flags. * @FMT_NOPREFIX: (pairs) Remove object hierarchy prefix from keys * @FMT_KEEPINVAL: (all) Print mappings even if value is marked as invalid * Values will be replaced with null (JSON) or an empty * string * @FMT_QUOTEALL: (all) Add quotes to all mapping values * @FMT_FILTER: (all) Ignore keys not announced via util_fmt_add_key() * @FMT_HANDLEINT: (json) Ensure correct JSON closure when interrupted * @FMT_NOMETA: (all) Do not emit tool meta-data * @FMT_WARN: (all) Warn about incorrect API usage * * Use these flags with util_fmt_init() to control generic aspects. */ enum util_fmt_flags_t { FMT_NOPREFIX = (1 << 0), FMT_KEEPINVAL = (1 << 1), FMT_QUOTEALL = (1 << 2), FMT_FILTER = (1 << 3), FMT_HANDLEINT = (1 << 4), FMT_NOMETA = (1 << 5), FMT_WARN = (1 << 6), }; /** * enum util_fmt_oflags_t - Object flags. * @FMT_LIST: (all) Object is a list * @FMT_ROW: (csv) Start a new CSV row with this object * @FMT_PREFIX: (all) Include object name in key prefix for CSV headings * and filter keys * * Use these flags with util_fmt_obj_start() to control object related * aspects. */ enum util_fmt_oflags_t { FMT_LIST = (1 << 0), FMT_ROW = (1 << 1), FMT_PREFIX = (1 << 2), }; /** * enum util_fmt_mflags_t - Mapping flags. * @FMT_QUOTE: (all) Quote value * @FMT_INVAL: (all) Mark value as invalid * @FMT_PERSIST: (csv) Keep value across CSV rows until overwritten * * Use these flags with util_fmt_pair() to control mapping related aspects. */ enum util_fmt_mflags_t { FMT_QUOTE = (1 << 0), FMT_INVAL = (1 << 1), FMT_PERSIST = (1 << 2), }; /** * util_fmt_init() - Initialize output formatter. * @fd : Output file descriptor * @type : Output format type * @flags: Formatting parameters * @api_level: Output format level indicator * * Prepare for writing formatted output with the given @type to @fd. Additional * @flags can be specified to control certain output aspects (see &enum * util_fmt_flags_t). * * @api_level represents an application-specific output format version number: * this number starts at 1 and must be increased whenever an incompatible format * change is introduced, e.g. when a non-optional object or mapping is removed * or used for different data. */ void util_fmt_init(FILE *fd, enum util_fmt_t type, unsigned int flags, int api_level); /** * util_fmt_exit() - Release resources used by output formatter. * * Release all resources currently in use by the output formatter. */ void util_fmt_exit(void); /** * util_fmt_name_to_type() - Convert format name to type identifier. * @name: Format name * @type: Pointer to resulting format type identifier * * Search supported output format types for a type with associated @name. If * found, store resulting type identifier in @type. * * Return: %true if type is found, %false otherwise. */ bool util_fmt_name_to_type(const char *name, enum util_fmt_t *type); /** * util_fmt_set_indent() - Set indentation parameters. * @base : Base indentation level to apply to all output lines (default 0) * @width : Number of indentation characters per intendation level (default 2) * @ind_char: Indentation characters to use (default space). */ void util_fmt_set_indent(unsigned int base, unsigned int width, char ind_char); /** * util_fmt_add_key() - Register expected mapping keys. * @fmt: Format string to generate key * * Register a mapping key before the associated key-value pair is emitted. * * Use this function together with format control flag @FMT_FILTER to ignore all * key-value pairs for which the key has not been registered. This can be * useful to allow for dynamically configured filtering of output based on * a static list of emitted mappings. * * When creating CSV output, use this function to register all column keys * in advance to enable a stable column list in case of rows that do not * provide data for all columns. */ void util_fmt_add_key(const char *fmt, ...); /** * util_fmt_obj_start() - Start a new data object. * @oflags: Flags controlling aspects of this object. * @fmt : Format string for generating an object name or %NULL. * * Use this function to start a new object in output data. Depending on * @oflags, the new object represents either a normal object or a list. @oflags * can also be used to indicated that an object corresponds to a new row of * CSV data. If @fmt is non-%NULL, the resulting name is used in a format * type specified way: * * Pairs: * - Object names are reflected as dot-separated component in the mapping * prefix, e.g. 'a.b.key=value' * - An index is generated for mappings and objects that are part of list, * e.g. 'a.b[1].key=value' * JSON: * - Object names are reflected as key-object mappings, e.g. * : { } * - Required commas between objects and mappings are automatically generated * CSV: * - Object names and the list type flag have no effect * - When flag @FMT_ROW is specified, a CSV row will be emitted when * util_fmt_obj_end() is called for the associated object */ void util_fmt_obj_start(unsigned int oflags, const char *fmt, ...); /** * util_fmt_obj_end() - Announce the end of the latest data object started. * * Each object started with util_fmt_obj_start() must be ended with an * associated util_fmt_obj_end() call. */ void util_fmt_obj_end(void); /** * util_fmt_pair() - Emit a key-value pair. * @mflags: Flags controlling this pair. * @key : Key for this pair, excluding prefix. * @fmt : Format string used to generated the pair value. * * Emit a key-value pair with the specified @key and the value that results * from format string @fmt. * * Notes: * - For JSON, a mapping can only occur after util_fmt_obj_start() * - For CSV, each @key must be unique, either by choosing unique key names * or by including object names as prefix via the use of FMT_PREFIX in * parent objects */ void util_fmt_pair(unsigned int mflags, const char *key, const char *fmt, ...); #endif /* LIB_UTIL_FMT_H */ s390-tools-2.38.0/include/lib/util_libc.h000066400000000000000000000076321502674226300177670ustar00rootroot00000000000000/** * @defgroup util_libc_h util_libc: Libc wrapper interface * @{ * @brief Handle standard errors for libc functions * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef LIB_UTIL_LIBC_H #define LIB_UTIL_LIBC_H #include #ifdef __cplusplus extern "C" { #endif /** * Allocate memory or panic in case of failure * * @param[in] size Number of bytes to be allocated * * @returns Pointer to memory buffer created with malloc() */ #define util_malloc(size) \ __util_malloc(__func__, __FILE__, __LINE__, size) void *__util_malloc(const char *func, const char *file, int line, size_t size); /** * Allocate zero-initialized memory or panic in case of failure * * @param[in] size Number of bytes to be allocated * * @returns Pointer to memory buffer created with calloc() */ #define util_zalloc(size) \ __util_zalloc(__func__, __FILE__, __LINE__, size) void *__util_zalloc(const char *func, const char *file, int line, size_t size); /** * Re-allocate memory or exit in case of failure * * @param[in] ptr Pointer ot old memory buffer * @param[in] size Number of bytes to be allocated * * @returns Pointer to memory buffer created with realloc() */ #define util_realloc(ptr, size) \ __util_realloc(__func__, __FILE__, __LINE__, ptr, size) void *__util_realloc(const char *func, const char *file, int line, void *ptr, size_t size); /** * Duplicate a string buffer or exit in case of failure * * @param[in] str String to be duplicated * * @returns Pointer to copied string allocated with malloc() */ #define util_strdup(str) \ __util_strdup(__func__, __FILE__, __LINE__, str) void *__util_strdup(const char *func, const char *file, int line, const char *str); /** * Print to allocated string or exit in case of failure * * @param[in,out] strp Pointer for returned string allocated with malloc() * @param[in] fmt Format string for generation of string * @param[in] ap Parameters for format string * * @returns num Number of formatted characters */ #define util_vasprintf(strp, fmt, ap) \ __util_vasprintf(__func__, __FILE__, __LINE__, strp, fmt, ap) #define UTIL_VASPRINTF(strp, fmt, ap) \ do { \ va_start(ap, fmt); \ util_vasprintf(strp, fmt, ap); \ va_end(ap); \ } while (0) int __util_vasprintf(const char *func, const char *file, int line, char **strp, const char *fmt, va_list ap); /** * Print to newly allocated string or exit in case of failure * * @param[in,out] strp Pointer for returned string allocated with malloc() * @param[in] ... Format string and parameters for format string * * @returns num Number of formatted characters */ #define util_asprintf(strp, ...) \ __util_asprintf(__func__, __FILE__, __LINE__, strp, ##__VA_ARGS__) int __util_asprintf(const char *func, const char *file, int line, char **strp, const char *fmt, ...); /** * Print to string buffer or exit in case of failure * * @param[in] str String buffer * @param[in] fmt Format string for generation of string * @param[in] ap Parameters for format string * * @returns num Number of formatted characters */ #define util_vsprintf(str, fmt, ap) \ __util_vsprintf(__func__, __FILE__, __LINE__, str, fmt, ap) #define UTIL_VSPRINTF(str, fmt, ap) \ do { \ va_start(ap, fmt); \ util_vsprintf(str, fmt, ap); \ va_end(ap); \ } while (0) int __util_vsprintf(const char *func, const char *file, int line, char *str, const char *fmt, va_list ap); char *util_strcat_realloc(char *str1, const char *str2); void util_concatf(char **str1, const char *fmt, ...); void util_str_toupper(char *str); void util_str_tolower(char *str); char *util_strstrip(char *s); size_t util_strlcpy(char *dest, const char *src, size_t size); #ifdef __cplusplus } #endif #endif /** LIB_UTIL_LIBC_H @} */ s390-tools-2.38.0/include/lib/util_list.h000066400000000000000000000042301502674226300200200ustar00rootroot00000000000000/* * util - Utility function library * * Linked lists * * Copyright IBM Corp. 2013, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef LIB_UTIL_LIST_H #define LIB_UTIL_LIST_H #include struct util_list { unsigned long offset; /* Offset of struct util_list_node */ struct util_list_node *start; /* First element */ struct util_list_node *end; /* Last element */ }; struct util_list_node { struct util_list_node *next; struct util_list_node *prev; }; #define util_list_new(type, member) util_list_new_offset(offsetof(type, member)) #define util_list_init(list, type, member) \ util_list_init_offset(list, offsetof(type, member)) void util_list_free(struct util_list *list); struct util_list *util_list_new_offset(unsigned long offset); void util_list_init_offset(struct util_list *list, unsigned long offset); void util_list_add_tail(struct util_list *list, void *entry); void util_list_add_head(struct util_list *list, void *entry); void util_list_add_next(struct util_list *list, void *entry, void *list_entry); void util_list_add_prev(struct util_list *list, void *entry, void *list_entry); void util_list_remove(struct util_list *list, void *entry); void *util_list_next(struct util_list *list, void *entry); void *util_list_prev(struct util_list *list, void *entry); void *util_list_start(struct util_list *list); void *util_list_end(struct util_list *list); int util_list_is_empty(struct util_list *list); unsigned long util_list_len(struct util_list *list); /* * The compare function should return the following: * a < b --> < 0 * a > b --> > 0 * a = b --> = 0 */ typedef int (*util_list_cmp_fn)(void *a, void *b, void *data); void util_list_sort(struct util_list *list, util_list_cmp_fn fn, void *data); #define util_list_iterate(list, i) \ for (i = util_list_start(list); \ i != NULL; \ i = util_list_next(list, i)) \ #define util_list_iterate_safe(list, i, n) \ for (i = util_list_start(list), n = util_list_next(list, i); \ i != NULL; \ i = n, n = util_list_next(list, i)) \ #endif /* LIB_UTIL_LIST_H */ s390-tools-2.38.0/include/lib/util_lockfile.h000066400000000000000000000023011502674226300206320ustar00rootroot00000000000000/** * @defgroup util_lockfile_h util_lockfile: File locking utility * @{ * @brief Create file-based locks * * Copyright IBM Corp. 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef LIB_UTIL_LOCKFILE_H #define LIB_UTIL_LOCKFILE_H #define UTIL_LOCKFILE_OK 0 /* Lock acquired/released successfully */ #define UTIL_LOCKFILE_LOCK_FAIL 1 /* Lock already held, ran out of retries */ #define UTIL_LOCKFILE_RELEASE_NONE 2 /* Lock not held */ #define UTIL_LOCKFILE_RELEASE_FAIL 3 /* Lock not held by specified pid */ #define UTIL_LOCKFILE_ERR 4 /* Other, unexpected error conditions */ int util_lockfile_lock(char *lockfile, int retries); int util_lockfile_lock_cw(char *lockfile, int retries, unsigned int waitinc, unsigned int maxwait); int util_lockfile_parent_lock(char *lockfile, int retries); int util_lockfile_parent_lock_cw(char *lockfile, int retries, unsigned int waitinc, unsigned int maxwait); int util_lockfile_release(char *lockfile); int util_lockfile_parent_release(char *lockfile); int util_lockfile_peek_owner(char *lockfile, int *pid); #endif /** LIB_UTIL_LOCKFILE_H @} */ s390-tools-2.38.0/include/lib/util_log.h000066400000000000000000000011711502674226300176270ustar00rootroot00000000000000/** * @defgroup util_log_h util_log: Multi-level message logging interface * @{ * @brief Multi-level message logging * * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef LIB_UTIL_LOG_H #define LIB_UTIL_LOG_H enum util_log_level { UTIL_LOG_ERROR, UTIL_LOG_WARN, UTIL_LOG_INFO, UTIL_LOG_DEBUG, UTIL_LOG_TRACE, UTIL_LOG_NUM_LEVELS /* Must be the last one. */ }; void util_log_set_level(int log_level); void util_log_print(int log_level, const char *fmt, ...); #endif /** LIB_UTIL_LOG_H @} */ s390-tools-2.38.0/include/lib/util_opt.h000066400000000000000000000037441502674226300176600ustar00rootroot00000000000000/** * @defgroup util_opt_h util_opt: Command line options interface * @{ * @brief Parse the command line options * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef LIB_UTIL_OPT_H #define LIB_UTIL_OPT_H #include #include /* Flag indicating that an option does not have a short form */ #define UTIL_OPT_FLAG_NOSHORT 1 /* Flag indicating that an option does not have a long form */ #define UTIL_OPT_FLAG_NOLONG 2 /* Flag indicating that this is a section heading */ #define UTIL_OPT_FLAG_SECTION 4 /** * Command line option */ struct util_opt { /** Defined by getopt.h, see "man getopt_long" */ struct option option; /** For options with arguments: Argument name */ char *argument; /** Description displayed for --help */ char *desc; /** Flags for this option */ int flags; /** Command to which this option belongs. NULL means all commands */ char *command; }; /** * Standard option: --help */ #define UTIL_OPT_HELP \ { \ .option = { "help", 0, NULL, 'h' }, \ .desc = "Print this help, then exit", \ } /** * Standard option: --version */ #define UTIL_OPT_VERSION \ { \ .option = { "version", 0, NULL, 'v' }, \ .desc = "Print version information, then exit", \ } /** * End-marker for the option pointer vector */ #define UTIL_OPT_END \ { \ .option = { NULL, 0, NULL, 0 }, \ } /** * Section header */ #define UTIL_OPT_SECTION(title) \ { \ .desc = (title), \ .flags = UTIL_OPT_FLAG_SECTION, \ } /* * Option functions */ void util_opt_init(struct util_opt *opt_vec, const char *opt_prefix); void util_opt_set_command(const char *command); int util_opt_getopt_long(int argc, char *argv[]); void util_opt_print_help(void); void util_opt_print_indented(const char *opt, const char *desc); void util_opt_print_parse_error(char opt, char *argv[]); #endif /** LIB_UTIL_OPT_H @} */ s390-tools-2.38.0/include/lib/util_panic.h000066400000000000000000000024451502674226300201450ustar00rootroot00000000000000/** * @defgroup util_panic_h util_panic: Panic interface * @{ * @brief Collect FFDC data for unexpected errors * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef LIB_UTIL_PANIC_H #define LIB_UTIL_PANIC_H #include "zt_common.h" /** * Write message, print backtrace and then call the abort() function * * @param[in] ... Format string and parameters describing the panic reason */ #define util_panic(...) \ __util_panic(__func__, __FILE__, __LINE__, ##__VA_ARGS__) void __noreturn __util_panic(const char *func, const char *file, int line, const char *fmt, ...); /** * Ensure that assumption is not true, otherwise panic * * Example: util_assert(ptr == NULL, "The ptr must be NULL, but is %p", ptr) * * @param[in] assumption This assumption has to be true * @param[in] ... Format string and parameters describing the assumption */ #define util_assert(assumption, ...) \ __util_assert(#assumption, __func__, __FILE__, __LINE__, \ assumption, ##__VA_ARGS__) void __util_assert(const char *assertion_string, const char *func, const char *file, int line, int assumption, const char *fmt, ...); #endif /** LIB_UTIL_PANIC_H @} */ s390-tools-2.38.0/include/lib/util_part.h000066400000000000000000000010451502674226300200140ustar00rootroot00000000000000/* * util - Utility function library * * Partition detection functions * * Copyright IBM Corp. 2013, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef LIB_UTIL_PART_H #define LIB_UTIL_PART_H int util_part_search(const char *dev, size_t blk_start, size_t blk_cnt, size_t blk_size, int *ext_part); int util_part_search_fh(int fh, size_t blk_start, size_t blk_cnt, size_t blk_size, int *ext_part); #endif /* LIB_UTIL_PART_H */ s390-tools-2.38.0/include/lib/util_path.h000066400000000000000000000014031502674226300200000ustar00rootroot00000000000000/** * @defgroup util_path_h util_path: Path interface * @{ * @brief Work with paths * * Copyright IBM Corp. 2016, 2019 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef LIB_UTIL_PATH_H #define LIB_UTIL_PATH_H #include char *util_path_sysfs(const char *fmt, ...); bool util_path_is_readable(const char *fmt, ...); bool util_path_is_writable(const char *fmt, ...); bool util_path_is_dir(const char *fmt, ...); bool util_path_is_reg_file(const char *fmt, ...); bool util_path_exists(const char *fmt, ...); bool util_path_is_readonly_file(const char *fmt, ...); bool util_path_is_writeonly_file(const char *fmt, ...); #endif /** LIB_UTIL_PATH_H @} */ s390-tools-2.38.0/include/lib/util_prg.h000066400000000000000000000025761502674226300176500ustar00rootroot00000000000000/** * @defgroup util_prg_h util_prg: Program interface * @{ * @brief Print standard program messages * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef LIB_UTIL_PRG_H #define LIB_UTIL_PRG_H #include #include #include #include #include struct util_prg_copyright { /** Name of the copyright owner, e.g. IBM */ const char *owner; /** Year of first publishing */ int pub_first; /** Year of last major changes */ int pub_last; }; /** * @brief Coypright end marker */ #define UTIL_PRG_COPYRIGHT_END {NULL, 0, 0} /** * Program description */ struct util_prg { /** Description for help */ const char *desc; /** Command arguments in front of other options */ const char *command_args; /** Positional arguments */ const char *args; /** Copyright list */ struct util_prg_copyright copyright_vec[]; }; void util_prg_init(const struct util_prg *prg); void util_prg_set_command(const char *command); void util_prg_print_parse_error(void); void util_prg_print_required_arg(const char *option); void util_prg_print_invalid_option(const char *option); void util_prg_print_arg_error(const char *arg_name); void util_prg_print_version(void); void util_prg_print_help(void); #endif /** LIB_UTIL_PRG_H @} */ s390-tools-2.38.0/include/lib/util_proc.h000066400000000000000000000020761502674226300200160ustar00rootroot00000000000000/* * Scanner for the /proc files * * Copyright IBM Corp. 2001, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. * */ #ifndef LIB_UTIL_PROC_H #define LIB_UTIL_PROC_H #include #include struct util_proc_part_entry { dev_t device; size_t blockcount; char *name; }; struct util_proc_dev_entry { int blockdev; dev_t device; char *name; }; #define UTIL_PROC_DEV_ENTRY_DASD "dasd" #define UTIL_PROC_DEV_ENTRY_VIRTBLK "virtblk" #define UTIL_PROC_DEV_ENTRY_SD "sd" #define UTIL_PROC_DEV_ENTRY_BLKEXT "blkext" #define UTIL_PROC_DEV_ENTRY_MD "md" #define UTIL_PROC_DEV_ENTRY_DM "device-mapper" int util_proc_part_get_entry(dev_t device, struct util_proc_part_entry *entry); void util_proc_part_free_entry(struct util_proc_part_entry *entry); int util_proc_dev_get_entry(dev_t dev, int blockdev, struct util_proc_dev_entry *entry); void util_proc_dev_free_entry(struct util_proc_dev_entry *entry); #endif /* LIB_UTIL_PROC_H */ s390-tools-2.38.0/include/lib/util_rec.h000066400000000000000000000040761502674226300176260ustar00rootroot00000000000000/** * @defgroup util_rec_h util_rec: Record interface * @{ * @brief Print records in different output formats * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef LIB_UTIL_REC_H #define LIB_UTIL_REC_H #include "lib/util_list.h" #define PAGE_SIZE 4096 /** * Opaque handle for a record * * The util_rec structure describes: * * - A set of fields * - An output format with the required formatting attributes (e.g. character * for header separator) */ struct util_rec; /** * Opaque handle for a record field (used for util_rec_iterate) * * The util_rec_fld structure describes: * * - Field value * - Field key name * - A set of attributes (e.g. width, alignment etc.) */ struct util_rec_fld; /** * Alignment in util_rec tables */ enum util_rec_align { /** Align field left */ UTIL_REC_ALIGN_LEFT, /** Align field right */ UTIL_REC_ALIGN_RIGHT, }; struct util_list *__util_rec_get_list(struct util_rec *rec); const char *util_rec_fld_get_key(struct util_rec_fld *fld); #define util_rec_iterate(rec, fld) \ util_list_iterate(__util_rec_get_list(rec), fld) struct util_rec *util_rec_new_wide(const char *hdr_sep); struct util_rec *util_rec_new_csv(const char *col_sep); struct util_rec *util_rec_new_long(const char *hdr_sep, const char *col_sep, const char *key, int key_size, int val_size); void util_rec_free(struct util_rec *rec); void util_rec_def(struct util_rec *rec, const char *key, enum util_rec_align align, int width, const char *hdr); void util_rec_set(struct util_rec *rec, const char *key, const char *fmt, ...); void util_rec_set_argz(struct util_rec *rec, const char *key, const char *argz, size_t len); const char *util_rec_get(struct util_rec *rec, const char *key); void util_rec_print_hdr(struct util_rec *rec); void util_rec_print(struct util_rec *rec); void util_rec_print_separator(struct util_rec *rec); void util_rec_set_indent(struct util_rec *rec, int indent); #endif /** LIB_UTIL_REC_H @} */ s390-tools-2.38.0/include/lib/util_scandir.h000066400000000000000000000013231502674226300204700ustar00rootroot00000000000000/** * @defgroup util_scandir_h util_scandir: Scandir interface * @{ * @brief Scan a directory for matching entries * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef LIB_UTIL_SCANDIR_H #define LIB_UTIL_SCANDIR_H #include int util_scandir_hexsort(const struct dirent **de1, const struct dirent **de2); int util_scandir(struct dirent ***namelist, int compar_fn(const struct dirent **, const struct dirent **), const char *path, const char *pattern, ...); void util_scandir_free(struct dirent **de_vec, int count); #endif /** LIB_UTIL_SCANDIR_H @} */ s390-tools-2.38.0/include/lib/util_sys.h000066400000000000000000000010531502674226300176630ustar00rootroot00000000000000/* * @defgroup util_sys_h util_sys: SysFS interface * @{ * @brief Work with SysFS * * Copyright IBM Corp. 2019 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef LIB_UTIL_SYS_H #define LIB_UTIL_SYS_H #include int util_sys_get_dev_addr(const char *dev, char *addr); bool util_sys_dev_is_partition(dev_t dev); int util_sys_get_partnum(dev_t dev); int util_sys_get_base_dev(dev_t dev, dev_t *base_dev); #endif /** LIB_UTIL_SYS_H @} */ s390-tools-2.38.0/include/lib/util_udev.h000066400000000000000000000017751502674226300200230ustar00rootroot00000000000000/* * @defgroup util_udev_h util_udev: UDEV interface * @{ * @brief Work with UDEV files * * Copyright IBM Corp. 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef LIB_UTIL_UDEV_H #define LIB_UTIL_UDEV_H #include #include "lib/util_exit_code.h" #include "lib/util_list.h" /* Single key-operator-value entry in a udev rule line.*/ struct util_udev_entry_node { struct util_list_node node; char *key; char *op; char *value; }; /* Single udev line in a udev rule file. */ struct util_udev_line_node { struct util_list_node node; struct util_list entries; char *line; }; /* Udev rule file. */ struct util_udev_file { struct util_list lines; }; util_exit_code_t util_udev_read_file(const char *path, struct util_udev_file **file_ptr); void util_udev_free_file(struct util_udev_file *file); void util_udev_file_print(struct util_udev_file *file); #endif /** LIB_UTIL_UDEV_H @} */ s390-tools-2.38.0/include/lib/vmcp.h000066400000000000000000000024521502674226300167610ustar00rootroot00000000000000/* * vmcp - z/VM CP command library * * Copyright IBM Corp. 2005, 2018 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef LIB_VMCP_H #define LIB_VMCP_H #include #ifdef __cplusplus extern "C" { #endif #define VMCP_DEVICE_NODE "/dev/vmcp" /* VMCP device name */ #define VMCP_DEFAULT_BUFSZ 0x4000 /* VMCP default buffer size */ /* * Error codes returned by vmcp() function. This enables the caller * to emit a detailed error message on the type of failure. */ #define VMCP_SUCCESS 0 /* VMCP operation completed successfully */ #define VMCP_ERR_OPEN (-1) /* Error Open of VMCP device */ #define VMCP_ERR_SETBUF (-2) /* Error VMCP_SETBUF ioctl */ #define VMCP_ERR_READ (-3) /* Error VMCP read system call */ #define VMCP_ERR_GETCODE (-4) /* Error VMCP_GETCODE ioctl */ #define VMCP_ERR_GETSIZE (-5) /* Error VMCP_GETSIZE ioctl */ #define VMCP_ERR_TOOSMALL (-6) /* Error VMCP buffer too small for * response */ #define VMCP_ERR_WRITE (-7) /* Error VMCP write system call */ struct vmcp_parm { const char *cpcmd; unsigned int buffer_size; bool do_upper; int cprc; char *response; unsigned int response_size; }; int vmcp(struct vmcp_parm *cp); #ifdef __cplusplus } #endif #endif s390-tools-2.38.0/include/lib/vmdump.h000066400000000000000000000006131502674226300173210ustar00rootroot00000000000000/* * vmdump - VMDUMP conversion library * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef LIB_VMDUMP_H #define LIB_VMDUMP_H int vmdump_convert(const char* inputFileName, const char* outputFileName, const char* progName); #endif /* LIB_VMDUMP_H */ s390-tools-2.38.0/include/lib/vtoc.h000066400000000000000000000363511502674226300167740ustar00rootroot00000000000000/* * Copyright IBM Corp. 2002, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef LIB_VTOC_H #define LIB_VTOC_H #include #include #include #include #include #include #include #include #include #include #include "lib/dasd_base.h" #define LINE_LENGTH 80 #define VTOC_START_CC 0x0 #define VTOC_START_HH 0x1 #define FIRST_USABLE_CYL 1 #define FIRST_USABLE_TRK 2 #define DASD_3380_TYPE 13184 #define DASD_3390_TYPE 13200 #define DASD_9345_TYPE 37701 #define DASD_3380_VALUE 0xbb60 #define DASD_3390_VALUE 0xe5a2 #define DASD_9345_VALUE 0xbc98 #define VOLSER_LENGTH 6 #define BIG_DISK_SIZE 0x10000 #define LV_COMPAT_CYL 0xFFFE #define VTOC_ERROR "VTOC error:" #define MAX_VTOC_ENTRIES 9 /* max number of VTOC labels for cdl formatted DASD */ typedef struct ttr { u_int16_t tt; u_int8_t r; } __attribute__ ((packed)) ttr_t; typedef struct cchhb { u_int16_t cc; u_int16_t hh; u_int8_t b; } __attribute__ ((packed)) cchhb_t; typedef struct cchh { u_int16_t cc; u_int16_t hh; } __attribute__ ((packed)) cchh_t; typedef struct labeldate { u_int8_t year; u_int16_t day; } __attribute__ ((packed)) labeldate_t; /* * The following structure is a merger of the cdl and ldl volume label. * On an ldl disk there is no key information, so when reading an * ldl label from disk, the data should be copied at the address of vollbl. * On the other side, the field ldl_version is reserved in a cdl record * and the field formatted_cyl exists only for ldl labels. So when * reading a cdl label from disk, the formatted_cyl field will contain * arbitrary data. * This layout may be a bit awkward, but the advantage of having the * same label type for both disk layout types is bigger than the effort * for taking a bit of extra care at the fringes. */ typedef struct volume_label { char volkey[4]; /* volume key = volume label */ char vollbl[4]; /* volume label */ char volid[6]; /* volume identifier */ u_int8_t security; /* security byte */ cchhb_t vtoc; /* VTOC address */ char res1[5]; /* reserved */ char cisize[4]; /* CI-size for FBA,... */ /* ...blanks for CKD */ char blkperci[4]; /* no of blocks per CI (FBA), blanks for CKD */ char labperci[4]; /* no of labels per CI (FBA), blanks for CKD */ char res2[4]; /* reserved */ char lvtoc[14]; /* owner code for LVTOC */ char res3[28]; /* reserved */ char ldl_version; /* version number, valid for ldl format */ unsigned long long formatted_blocks; /* valid when ldl_version >= f2 */ } __attribute__ ((packed)) volume_label_t; /** * This represents a volume label specific for OS/390 compatible disk layout */ struct vol_label_cdl { char volkey[4]; char vollbl[4]; char opaq[70]; cchhb_t br; /* boot record address */ char stdv[1]; /* standard version ID */ } __attribute__ ((packed)); static inline int is_vol1(char *this) { char vol1[] = {0xe5, 0xd6, 0xd3, 0xf1, 0x00} /* "VOL1" in EBCDIC */; return !strncmp(this, vol1, 4); } typedef struct extent { u_int8_t typeind; /* extent type indicator */ u_int8_t seqno; /* extent sequence number */ cchh_t llimit; /* starting point of this extent */ cchh_t ulimit; /* ending point of this extent */ } __attribute__ ((packed)) extent_t; typedef struct dev_const { u_int16_t DS4DSCYL; /* number of logical cyls */ u_int16_t DS4DSTRK; /* number of tracks in a logical cylinder */ u_int16_t DS4DEVTK; /* device track length */ u_int8_t DS4DEVI; /* non-last keyed record overhead */ u_int8_t DS4DEVL; /* last keyed record overhead */ u_int8_t DS4DEVK; /* non-keyed record overhead differential */ u_int8_t DS4DEVFG; /* flag byte */ u_int16_t DS4DEVTL; /* device tolerance */ u_int8_t DS4DEVDT; /* number of DSCB's per track */ u_int8_t DS4DEVDB; /* number of directory blocks per track */ } __attribute__ ((packed)) dev_const_t; /* * format 1 and format 8 label have the same layout so we use the following * structure for both. */ typedef struct format1_label { char DS1DSNAM[44]; /* data set name */ u_int8_t DS1FMTID; /* format identifier */ unsigned char DS1DSSN[6];/* data set serial number */ u_int16_t DS1VOLSQ; /* volume sequence number */ labeldate_t DS1CREDT; /* creation date: ydd */ labeldate_t DS1EXPDT; /* expiration date */ u_int8_t DS1NOEPV; /* number of extents on volume */ u_int8_t DS1NOBDB; /* no. of bytes used in last direction blk */ u_int8_t DS1FLAG1; /* flag 1 */ unsigned char DS1SYSCD[13]; /* system code */ labeldate_t DS1REFD; /* date last referenced */ u_int8_t DS1SMSFG; /* system managed storage indicators */ u_int8_t DS1SCXTF; /* sec. space extension flag byte */ u_int16_t DS1SCXTV; /* secondary space extension value */ u_int8_t DS1DSRG1; /* data set organisation byte 1 */ u_int8_t DS1DSRG2; /* data set organisation byte 2 */ u_int8_t DS1RECFM; /* record format */ u_int8_t DS1OPTCD; /* option code */ u_int16_t DS1BLKL; /* block length */ u_int16_t DS1LRECL; /* record length */ u_int8_t DS1KEYL; /* key length */ u_int16_t DS1RKP; /* relative key position */ u_int8_t DS1DSIND; /* data set indicators */ u_int8_t DS1SCAL1; /* secondary allocation flag byte */ char DS1SCAL3[3]; /* secondary allocation quantity */ ttr_t DS1LSTAR; /* last used track and block on track */ u_int16_t DS1TRBAL; /* space remaining on last used track */ u_int16_t res1; /* reserved */ extent_t DS1EXT1; /* first extent description */ extent_t DS1EXT2; /* second extent description */ extent_t DS1EXT3; /* third extent description */ cchhb_t DS1PTRDS; /* possible pointer to f2 or f3 DSCB */ } __attribute__ ((packed)) format1_label_t; typedef struct format3_label { char DS3KEYID[4]; /* key identifier */ extent_t DS3EXTNT[4]; /* first 4 extent descriptions */ u_int8_t DS3FMTID; /* format identifier */ extent_t DS3ADEXT[9]; /* last 9 extent description */ cchhb_t DS3PTRDS; /* pointer to next format3 DSCB */ } __attribute__ ((packed)) format3_label_t; typedef struct format4_label { char DS4KEYCD[44]; /* key code for VTOC labels: 44 times 0x04 */ u_int8_t DS4IDFMT; /* format identifier */ cchhb_t DS4HPCHR; /* highest address of a format 1 DSCB */ u_int16_t DS4DSREC; /* number of available DSCB's */ cchh_t DS4HCCHH; /* CCHH of next available alternate track */ u_int16_t DS4NOATK; /* number of remaining alternate tracks */ u_int8_t DS4VTOCI; /* VTOC indicators */ u_int8_t DS4NOEXT; /* number of extents in VTOC */ u_int8_t DS4SMSFG; /* system managed storage indicators */ u_int8_t DS4DEVAC; /* number of alternate cylinders. Subtract from first two bytes of DS4DEVSZ to get number of usable cylinders. can be zero. valid only if DS4DEVAV on. */ dev_const_t DS4DEVCT; /* device constants */ char DS4AMTIM[8]; /* VSAM time stamp */ char DS4AMCAT[3]; /* VSAM catalog indicator */ char DS4R2TIM[8]; /* VSAM volume/catalog match time stamp */ char res1[5]; /* reserved */ char DS4F6PTR[5]; /* pointer to first format 6 DSCB */ extent_t DS4VTOCE; /* VTOC extent description */ char res2[10]; /* reserved */ u_int8_t DS4EFLVL; /* extended free-space management level */ cchhb_t DS4EFPTR; /* pointer to extended free-space info */ char res3; /* reserved */ u_int32_t DS4DCYL; /* number of logical cyls */ char res4[2]; /* reserved */ u_int8_t DS4DEVF2; /* device flags */ char res5; /* reserved */ } __attribute__ ((packed)) format4_label_t; typedef struct ds5ext { u_int16_t t; /* RTA of the first track of free extent */ u_int16_t fc; /* number of whole cylinders in free ext. */ u_int8_t ft; /* number of remaining free tracks */ } __attribute__ ((packed)) ds5ext_t; typedef struct format5_label { char DS5KEYID[4]; /* key identifier */ ds5ext_t DS5AVEXT; /* first available (free-space) extent. */ ds5ext_t DS5EXTAV[7]; /* seven available extents */ u_int8_t DS5FMTID; /* format identifier */ ds5ext_t DS5MAVET[18]; /* eighteen available extents */ cchhb_t DS5PTRDS; /* pointer to next format5 DSCB */ } __attribute__ ((packed)) format5_label_t; typedef struct ds7ext { u_int32_t a; /* starting RTA value */ u_int32_t b; /* ending RTA value + 1 */ } __attribute__ ((packed)) ds7ext_t; typedef struct format7_label { char DS7KEYID[4]; /* key identifier */ ds7ext_t DS7EXTNT[5]; /* space for 5 extent descriptions */ u_int8_t DS7FMTID; /* format identifier */ ds7ext_t DS7ADEXT[11]; /* space for 11 extent descriptions */ char res1[2]; /* reserved */ cchhb_t DS7PTRDS; /* pointer to next FMT7 DSCB */ } __attribute__ ((packed)) format7_label_t; typedef struct format9_label { u_int8_t DS9KEYID; /* key code for format 9 labels (0x09) */ u_int8_t DS9SUBTY; /* subtype (0x01) */ u_int8_t DS9NUMF9; /* number of F9 datasets */ u_int8_t res1[41]; /* reserved */ u_int8_t DS9FMTID; /* format identifier */ u_int8_t res2[90]; /* reserved */ cchhb_t DS9PTRDS; /* pointer to next DSCB */ } __attribute__ ((packed)) format9_label_t; char * vtoc_ebcdic_enc (char *source, char *target, int l); char * vtoc_ebcdic_dec (char *source, char *target, int l); void vtoc_set_extent ( extent_t * ext, u_int8_t typeind, u_int8_t seqno, cchh_t * lower, cchh_t * upper); void vtoc_set_cchh ( cchh_t * addr, u_int32_t cc, u_int16_t hh); u_int32_t vtoc_get_cyl_from_cchh(cchh_t *addr); u_int16_t vtoc_get_head_from_cchh(cchh_t *addr); void vtoc_set_cchhb ( cchhb_t * addr, u_int32_t cc, u_int16_t hh, u_int8_t b); u_int32_t vtoc_get_cyl_from_cchhb(cchhb_t *addr); u_int16_t vtoc_get_head_from_cchhb(cchhb_t *addr); u_int64_t cchhb2blk(cchhb_t *p, struct hd_geometry *geo); u_int64_t cchh2blk (cchh_t *p, struct hd_geometry *geo); u_int32_t cchh2trk (cchh_t *p, struct hd_geometry *geo); void vtoc_set_date ( labeldate_t * d, u_int8_t year, u_int16_t day); void vtoc_volume_label_init ( volume_label_t *vlabel); int vtoc_read_volume_label ( char * device, unsigned long vlabel_start, volume_label_t * vlabel); int vtoc_write_volume_label ( char *device, unsigned long vlabel_start, volume_label_t *vlabel); void vtoc_volume_label_set_volser ( volume_label_t *vlabel, char *volser); char *vtoc_volume_label_get_volser ( volume_label_t *vlabel, char *volser); void vtoc_volume_label_set_key ( volume_label_t *vlabel, char *key); void vtoc_volume_label_set_label ( volume_label_t *vlabel, char *lbl); char *vtoc_volume_label_get_label ( volume_label_t *vlabel, char *lbl); void vtoc_read_label ( char *device, unsigned long position, format1_label_t *f1, format4_label_t *f4, format5_label_t *f5, format7_label_t *f7); void vtoc_write_label ( char *device, unsigned long position, format1_label_t *f1, format4_label_t *f4, format5_label_t *f5, format7_label_t *f7, format9_label_t *f9); void vtoc_init_format1_label ( unsigned int blksize, extent_t *part_extent, format1_label_t *f1); void vtoc_init_format4_label ( format4_label_t *f4lbl, unsigned int compat_cylinders, unsigned int real_cylinders, unsigned int tracks, unsigned int blocks, unsigned int blksize, u_int16_t dev_type); void vtoc_update_format4_label ( format4_label_t *f4, cchhb_t *highest_f1, u_int16_t unused_update); void vtoc_init_format5_label ( format5_label_t *f5); void vtoc_update_format5_label_add ( format5_label_t *f5, int verbose, int trk, u_int16_t a, u_int16_t b, u_int8_t c); void vtoc_update_format5_label_del ( format5_label_t *f5, int verbose, int trk, u_int16_t a, u_int16_t b, u_int8_t c); void vtoc_init_format7_label ( format7_label_t *f7); void vtoc_update_format7_label_add ( format7_label_t *f7, int verbose, u_int32_t a, u_int32_t b); void vtoc_update_format7_label_del ( format7_label_t *f7, int verbose, u_int32_t a, u_int32_t b); void vtoc_init_format8_label ( unsigned int blksize, extent_t *part_extent, format1_label_t *f1); void vtoc_update_format8_label ( cchhb_t *associated_f9, format1_label_t *f8); void vtoc_init_format9_label ( format9_label_t *f9); void vtoc_set_freespace( format4_label_t *f4, format5_label_t *f5, format7_label_t *f7, char ch, int verbose, u_int32_t start, u_int32_t stop, u_int32_t cyl, u_int32_t trk); #endif /* LIB_VTOC_H */ s390-tools-2.38.0/include/lib/zt_common.h000066400000000000000000000054721502674226300200260ustar00rootroot00000000000000/* * s390-tools/include/zt_common.h * common s390-tools definitions. * * Copyright IBM Corp. 2004, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. * */ #ifndef LIB_ZT_COMMON_H #define LIB_ZT_COMMON_H #define STRINGIFY_1(x) #x #define STRINGIFY(x) STRINGIFY_1(x) /* Use this macro to make constant macros usable in both assembler and * C code. * * Usage example: * #define IMAGE_ENTRY _AC(0x10000, UL) */ #ifndef _AC #ifdef __ASSEMBLER__ #define _AC(X, TYPE) X #else #define _AC(X, TYPE) X##TYPE #endif #endif #ifndef __ASSEMBLER__ #ifndef sizeof_field #define sizeof_field(type, field) (sizeof((type *)0)->field) #endif #ifdef UNUSED #elif defined(__GNUC__) # define UNUSED(x) UNUSED_ ## x __attribute__((unused)) #else # define UNUSED(x) x #endif #ifdef STATIC_ASSERT #elif defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ >= 5) # define STATIC_ASSERT(test) _Static_assert((test), "(" #test ") failed"); #else # define STATIC_ASSERT(test) #endif #define ALIGN(x, a) __ALIGN_MASK(x, (typeof(x))(a) - 1) #define __ALIGN_MASK(x, mask) (((x) + (mask)) & ~(mask)) #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #ifndef ROUNDUP # define ROUNDUP(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) #endif #define RELEASE_STRING STRINGIFY (S390_TOOLS_RELEASE) #define TOOLS_LIBDIR STRINGIFY (S390_TOOLS_LIBDIR) #define TOOLS_SYSCONFDIR STRINGIFY (S390_TOOLS_SYSCONFDIR) #define TOOLS_BINDIR STRINGIFY (S390_TOOLS_BINDIR) #define TOOLS_DATADIR STRINGIFY (S390_TOOLS_DATADIR) #define __noreturn __attribute__((noreturn)) #define __packed __attribute__((packed)) #define __aligned(x) __attribute__((aligned(x))) #define __may_alias __attribute__((may_alias)) #define __section(x) __attribute__((__section__(#x))) #define __noinline __attribute__((__noinline__)) #define __big_endian /* The Linux kernel (in stddef.h) and glibc (sys/cdefs.h) define * __always_inline. Therefore undefine it first to allow the headers * to be included first. */ #undef __always_inline #define __always_inline inline __attribute__((always_inline)) #define __pa32(x) ((uint32_t)(unsigned long)(x)) #define __pa(x) ((unsigned long)(x)) #define barrier() __asm__ __volatile__("": : :"memory") #undef MIN #define MIN(x, y) \ ({ \ __typeof__(x) _x = (x); \ __typeof__(y) _y = (y); \ \ _x < _y ? _x : _y; \ }) #undef MAX #define MAX(x, y) \ ({ \ __typeof__(x) _x = (x); \ __typeof__(y) _y = (y); \ \ _x > _y ? _x : _y; \ }) typedef unsigned long long u64; typedef signed long long s64; typedef unsigned int u32; typedef signed int s32; typedef unsigned short int u16; typedef signed short int s16; typedef unsigned char u8; typedef signed char s8; #endif /* __ASSEMBLER__ */ #endif /* LIB_ZT_COMMON_H */ s390-tools-2.38.0/include/libpv/000077500000000000000000000000001502674226300162065ustar00rootroot00000000000000s390-tools-2.38.0/include/libpv/common.h000066400000000000000000000006741502674226300176560ustar00rootroot00000000000000/* * Libpv common definitions. * * Copyright IBM Corp. 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. * */ #ifndef LIBPV_COMMON_H #define LIBPV_COMMON_H /* must be included before any (other) glib header to verify that * the glib version is supported */ #include "libpv/glib-helper.h" #include #endif /* LIBPV_COMMON_H */ s390-tools-2.38.0/include/libpv/crypto.h000066400000000000000000000061211502674226300176770ustar00rootroot00000000000000/* * General cryptography helper functions and definitions * * Copyright IBM Corp. 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef LIBPV_CRYPTO_H #define LIBPV_CRYPTO_H #include #include #include #include #include "libpv/common.h" #define PV_NONNULL(...) typedef struct pv_cipher_parms { const EVP_CIPHER *cipher; size_t tag_size; GBytes *key; union { GBytes *iv; GBytes *tweak; }; } PvCipherParms; enum PvCryptoMode { PV_ENCRYPT, PV_DECRYPT, }; /** pv_get_openssl_errors: * * Returns the last OpenSSL error messages. * Caller is responsible to free the returned value. * * Returns: String representing the error. */ char *pv_get_openssl_errors(void); /** * pv_BIO_reset: * @b: BIO to reset * * Resets a BIO to its initial state. * * Returns: 0 in case of success, -1 otherwise. */ int pv_BIO_reset(BIO *b); /* Symmetric en/decryption functions */ /** * pv_gcm_encrypt: * @plain: data to encrypt * @aad: (optional): additional data that should be authenticated with the key * @parms: * @cipher: (out): location to store the ciphertext * @tag: (out): location to store the generated GCM tag * @error: return location for a #GError * * Encrypts the @plain data and authenticates @aad data. * * Returns: number of bytes, or -1 in case of an error */ int64_t pv_gcm_encrypt(GBytes *plain, GBytes *aad, const PvCipherParms *parms, GBytes **cipher, GBytes **tag, GError **error) PV_NONNULL(1, 3, 4, 5); /** * pv_gcm_decrypt: * @cipher: ciphertext to decrypt * @aad: (optional): additional date to authenticate * @tag: the GCM tag * @parms: * @plain: (out): location to store the decrypted data * @error: return location for a #GError * * Decrypts the @cipher data and authenticates the @aad data. * * Returns: number of bytes, or -1 in case of an error */ int64_t pv_gcm_decrypt(GBytes *cipher, GBytes *aad, GBytes *tag, const PvCipherParms *parms, GBytes **plain, GError **error) PV_NONNULL(1, 3, 4, 5); /** pv_hkdf_extract_and_expand: * @derived_key_len: size of the output key * @key: input key * @salt: salt for the extraction * @info: info for the expansion * @md: EVP mode of operation * @error: return location for a #GError * * Performs a RFC 5869 HKDF. * * Returns: (nullable) (transfer full): Result of RFC 5869 HKDF * */ GBytes *pv_hkdf_extract_and_expand(size_t derived_key_len, GBytes *key, GBytes *salt, GBytes *info, const EVP_MD *md, GError **error) PV_NONNULL(2, 3, 4, 5); GQuark pv_crypto_error_quark(void); #define PV_CRYPTO_ERROR pv_crypto_error_quark() typedef enum { PV_CRYPTO_ERROR_HKDF_FAIL, PV_CRYPTO_ERROR_INTERNAL, PV_CRYPTO_ERROR_NO_MATCH_TAG, } PvCryptoErrors; WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BIO, BIO_free_all) WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_CIPHER_CTX, EVP_CIPHER_CTX_free) WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_PKEY, EVP_PKEY_free) WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_PKEY_CTX, EVP_PKEY_CTX_free) #endif /* LIBPV_CRYPTO_H */ s390-tools-2.38.0/include/libpv/glib-helper.h000066400000000000000000000064241502674226300205570ustar00rootroot00000000000000/* * Glib convenience functions * Shall be used instead of manually including glib. * * Copyright IBM Corp. 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef LIBPV_GLIB_HELPER_H #define LIBPV_GLIB_HELPER_H #if defined(GLIB_VERSION_MIN_REQUIRED) #if GLIB_VERSION_MIN_REQUIRED < GLIB_VERSION_2_56 #error "GLIB_VERSION must be at least 2.56" #endif #else #define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_56 #endif #define GLIB_VERSION_MAX_ALLOWED GLIB_VERSION_2_56 #ifdef __G_LIB_H__ #error "glib.h must be included via libpv/glib-helper.h" #endif #include #include #include #ifdef __clang__ #define WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(...) \ DO_PRAGMA(clang diagnostic push) \ DO_PRAGMA(clang diagnostic ignored "-Wunused-function") \ G_DEFINE_AUTOPTR_CLEANUP_FUNC(__VA_ARGS__) \ DO_PRAGMA(clang diagnostic pop) #else #define WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(...) G_DEFINE_AUTOPTR_CLEANUP_FUNC(__VA_ARGS__) #endif #define DO_PRAGMA(x) _Pragma(#x) #define pv_wrapped_g_assert(__expr) g_assert(__expr) /** pv_sec_gbytes_new_take: * * #g_bytes_new_take() with secure cleanup */ GBytes *pv_sec_gbytes_new_take(void *data, size_t size); /** pv_sec_gbytes_new: * * #g_bytes_new() with secure cleanup */ GBytes *pv_sec_gbytes_new(const void *data, size_t size); /** pv_file_get_content_as_secure_bytes: * * @filename: path to file for reading in * * read file and save as secure gbytes * * Return: * Content of file as #GBytes with secure cleanup */ GBytes *pv_file_get_content_as_secure_bytes(const char *filename); /** pv_file_get_content_as_g_bytes: * * @filename: path to file for reading in * * read file and save as gbytes * * Return: * Content of file as #GBytes */ GBytes *pv_file_get_content_as_g_bytes(const char *filename, GError **error); /** pv_file_seek: * * fseek with error reporting */ int pv_file_seek(FILE *file, long offset, int whence, GError **error); /** pv_file_write: * * fwrite with error reporting */ size_t pv_file_write(FILE *file, const void *ptr, size_t size, GError **error); /** pv_file_close: * * fclose with error reporting */ long pv_file_close(FILE *file, GError **error); /** pv_file_tell: * * ftell with error reporting */ long pv_file_tell(FILE *file, GError **error); /** pv_file_open: * * fopen with error reporting */ FILE *pv_file_open(const char *filename, const char *mode, GError **error); /** pv_gbytes_memcpy: * * memcpy with size check. In case @dst_size is smaller than the size of @src * the error value %NULL is returned and @copied and @dst are left unchanged. * * If @dst and @src data overlap, the behavior is undefined. */ void *pv_gbytes_memcpy(void *dst, size_t dst_size, GBytes *src, size_t *copied); #define PV_GLIB_HELPER_ERROR g_quark_from_static_string("pv-glib-helper_error-quark") typedef enum { PV_GLIB_HELPER_FILE_ERROR, } pv_glib_helper_error_e; void pv_auto_close_file(FILE *file); WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(FILE, pv_auto_close_file) #endif /* LIBPV_GLIB_HELPER_H */ s390-tools-2.38.0/include/libseckey/000077500000000000000000000000001502674226300170445ustar00rootroot00000000000000s390-tools-2.38.0/include/libseckey/sk_cca.h000066400000000000000000000025601502674226300204430ustar00rootroot00000000000000/* * libseckey - Secure key library * * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef SK_CCA_H #define SK_CCA_H #include #include #include #include "libseckey/sk_openssl.h" #define CCA_MAX_PKA_KEY_TOKEN_SIZE 3500 int SK_CCA_generate_ec_key_pair(const struct sk_ext_cca_lib *cca_lib, int curve_nid, unsigned char *key_token, size_t *key_token_length, bool debug); int SK_CCA_generate_rsa_key_pair(const struct sk_ext_cca_lib *cca_lib, size_t modulus_bits, unsigned int pub_exp, unsigned char *key_token, size_t *key_token_length, bool debug); int SK_CCA_get_key_type(const unsigned char *key_token, size_t key_token_length, int *pkey_type); int SK_CCA_get_secure_key_as_pkey(const struct sk_ext_cca_lib *cca_lib, const unsigned char *key_token, size_t key_token_length, bool rsa_pss, EVP_PKEY **pkey, bool debug); int SK_CCA_get_public_from_secure_key(const unsigned char *key_token, size_t key_token_length, sk_pub_key_func_t pub_key_cb, void *private, bool debug); int SK_CCA_reencipher_key(const struct sk_ext_cca_lib *cca_lib, unsigned char *key_token, size_t key_token_length, bool to_new, bool debug); #endif s390-tools-2.38.0/include/libseckey/sk_ep11.h000066400000000000000000000122211502674226300204560ustar00rootroot00000000000000/* * libseckey - Secure key library * * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef SK_EP11_H #define SK_EP11_H #include #include #include #include "libseckey/sk_openssl.h" #define EP11_MAX_KEY_TOKEN_SIZE 8192 int SK_EP11_generate_ec_key_pair(const struct sk_ext_ep11_lib *ep11_lib, int curve_nid, unsigned char *key_token, size_t *key_token_length, bool debug); int SK_EP11_generate_rsa_key_pair(const struct sk_ext_ep11_lib *ep11_lib, size_t modulus_bits, unsigned int pub_exp, bool x9_31, unsigned char *key_token, size_t *key_token_length, bool debug); int SK_EP11_get_key_type(const unsigned char *key_token, size_t key_token_length, int *pkey_type); const unsigned char *SK_EP11_get_key_blob(const unsigned char *key_token, size_t key_token_length); size_t SK_EP11_get_key_blob_size(const unsigned char *key_token, size_t key_token_length); int SK_EP11_get_secure_key_as_pkey(const struct sk_ext_ep11_lib *ep11_lib, const unsigned char *key_token, size_t key_token_length, bool rsa_pss, EVP_PKEY **pkey, bool debug); int SK_EP11_get_public_from_secure_key(const unsigned char *key_token, size_t key_token_length, sk_pub_key_func_t pub_key_cb, void *private, bool debug); int SK_EP11_reencipher_key(const struct sk_ext_ep11_lib *ep11_lib, unsigned char *key_token, size_t key_token_length, bool debug); /* PKCS#11 definitions */ #define CK_PTR * typedef unsigned char CK_BYTE; typedef CK_BYTE CK_CHAR; typedef CK_BYTE CK_UTF8CHAR; typedef CK_BYTE CK_BBOOL; typedef unsigned long CK_ULONG; typedef long CK_LONG; typedef CK_ULONG CK_FLAGS; typedef CK_ULONG CK_RV; typedef CK_ULONG CK_SLOT_ID; typedef CK_ULONG CK_MECHANISM_TYPE; typedef CK_ULONG CK_ATTRIBUTE_TYPE; typedef CK_ULONG CK_OBJECT_CLASS; typedef CK_ULONG CK_KEY_TYPE; typedef CK_ULONG CK_RSA_PKCS_OAEP_SOURCE_TYPE; typedef CK_ULONG CK_RSA_PKCS_MGF_TYPE; typedef CK_BYTE CK_PTR CK_BYTE_PTR; typedef CK_CHAR CK_PTR CK_CHAR_PTR; typedef CK_UTF8CHAR CK_PTR CK_UTF8CHAR_PTR; typedef CK_ULONG CK_PTR CK_ULONG_PTR; typedef void CK_PTR CK_VOID_PTR; typedef CK_SLOT_ID CK_PTR CK_SLOT_ID_PTR; typedef CK_MECHANISM_TYPE CK_PTR CK_MECHANISM_TYPE_PTR; typedef CK_RSA_PKCS_MGF_TYPE CK_PTR CK_RSA_PKCS_MGF_TYPE_PTR; typedef struct CK_MECHANISM { CK_MECHANISM_TYPE mechanism; CK_VOID_PTR pParameter; CK_ULONG ulParameterLen; } CK_MECHANISM; typedef CK_MECHANISM CK_PTR CK_MECHANISM_PTR; typedef struct CK_ATTRIBUTE { CK_ATTRIBUTE_TYPE type; CK_VOID_PTR pValue; CK_ULONG ulValueLen; } CK_ATTRIBUTE; typedef CK_ATTRIBUTE CK_PTR CK_ATTRIBUTE_PTR; typedef struct CK_RSA_PKCS_PSS_PARAMS { CK_MECHANISM_TYPE hashAlg; CK_RSA_PKCS_MGF_TYPE mgf; CK_ULONG sLen; } CK_RSA_PKCS_PSS_PARAMS; typedef CK_RSA_PKCS_PSS_PARAMS CK_PTR CK_RSA_PKCS_PSS_PARAMS_PTR; typedef struct CK_RSA_PKCS_OAEP_PARAMS { CK_MECHANISM_TYPE hashAlg; CK_RSA_PKCS_MGF_TYPE mgf; CK_RSA_PKCS_OAEP_SOURCE_TYPE source; CK_VOID_PTR pSourceData; CK_ULONG ulSourceDataLen; } CK_RSA_PKCS_OAEP_PARAMS; typedef CK_RSA_PKCS_OAEP_PARAMS CK_PTR CK_RSA_PKCS_OAEP_PARAMS_PTR; #define CKZ_DATA_SPECIFIED 0x00000001 #define CKG_MGF1_SHA1 0x00000001 #define CKG_MGF1_SHA224 0x00000005 #define CKG_MGF1_SHA256 0x00000002 #define CKG_MGF1_SHA384 0x00000003 #define CKG_MGF1_SHA512 0x00000004 #define CKG_VENDOR_DEFINED 0x80000000UL #define CKG_IBM_MGF1_SHA3_224 (CKG_VENDOR_DEFINED + 1) #define CKG_IBM_MGF1_SHA3_256 (CKG_VENDOR_DEFINED + 2) #define CKG_IBM_MGF1_SHA3_384 (CKG_VENDOR_DEFINED + 3) #define CKG_IBM_MGF1_SHA3_512 (CKG_VENDOR_DEFINED + 4) #define CKR_OK 0x00000000 #define CKR_VENDOR_DEFINED 0x80000000 #define CKO_PUBLIC_KEY 0x00000002 #define CKO_PRIVATE_KEY 0x00000003 #define CKK_EC 0x00000003 #define CKM_RSA_PKCS_KEY_PAIR_GEN 0x00000000 #define CKM_RSA_PKCS 0x00000001 #define CKM_RSA_PKCS_OAEP 0x00000009 #define CKM_RSA_X9_31_KEY_PAIR_GEN 0x0000000A #define CKM_RSA_X9_31 0x0000000B #define CKM_RSA_PKCS_PSS 0x0000000D #define CKM_SHA_1 0x00000220 #define CKM_SHA256 0x00000250 #define CKM_SHA224 0x00000255 #define CKM_SHA384 0x00000260 #define CKM_SHA512 0x00000270 #define CKM_SHA512_224 0x00000048 #define CKM_SHA512_256 0x0000004C #define CKM_EC_KEY_PAIR_GEN 0x00001040 #define CKM_ECDSA 0x00001041 #define CKM_VENDOR_DEFINED 0x80000000 #define CKM_IBM_SHA3_224 (CKM_VENDOR_DEFINED + 0x00010001) #define CKM_IBM_SHA3_256 (CKM_VENDOR_DEFINED + 0x00010002) #define CKM_IBM_SHA3_384 (CKM_VENDOR_DEFINED + 0x00010003) #define CKM_IBM_SHA3_512 (CKM_VENDOR_DEFINED + 0x00010004) #define CKA_CLASS 0x00000000 #define CKA_KEY_TYPE 0x00000100 #define CKA_SENSITIVE 0x00000103 #define CKA_ENCRYPT 0x00000104 #define CKA_DECRYPT 0x00000105 #define CKA_SIGN 0x00000108 #define CKA_VERIFY 0x0000010A #define CKA_DERIVE 0x0000010C #define CKA_DECRYPT 0x00000105 #define CKA_WRAP 0x00000106 #define CKA_UNWRAP 0x00000107 #define CKA_MODULUS_BITS 0x00000121 #define CKA_PUBLIC_EXPONENT 0x00000122 #define CKA_EC_PARAMS 0x00000180 #endif s390-tools-2.38.0/include/libseckey/sk_openssl.h000066400000000000000000000146021502674226300214000ustar00rootroot00000000000000/* * libseckey - Secure key library * * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef SK_OPENSSL_H #define SK_OPENSSL_H #include #include #include #include #include #include #ifndef OPENSSL_VERSION_PREREQ #if defined(OPENSSL_VERSION_MAJOR) && defined(OPENSSL_VERSION_MINOR) #define OPENSSL_VERSION_PREREQ(maj, min) \ ((OPENSSL_VERSION_MAJOR << 16) + \ OPENSSL_VERSION_MINOR >= ((maj) << 16) + (min)) #else #define OPENSSL_VERSION_PREREQ(maj, min) \ (OPENSSL_VERSION_NUMBER >= (((maj) << 28) | \ ((min) << 20))) #endif #endif /** * External crypto library definitions */ struct sk_ext_cca_lib { void *cca_lib; /* Handle of CCA host library loaded via dlopen */ }; typedef uint64_t target_t; struct sk_ext_ep11_lib { void *ep11_lib; /* Handle of EP11 host library loaded via dlopen */ target_t target; /* single or group target handle */ }; enum sk_ext_lib_type { SK_EXT_LIB_CCA = 1, SK_EXT_LIB_EP11 = 2, }; struct sk_ext_lib { enum sk_ext_lib_type type; union { struct sk_ext_cca_lib *cca; /* Used if type = EXT_LIB_CCA */ struct sk_ext_ep11_lib *ep11; /* Used if type = EXT_LIB_EP11 */ }; }; /* * Secure key library initialization and termination functions */ int SK_OPENSSL_init(bool debug); void SK_OPENSSL_term(void); /* * Secure key generation and reenciphering definitions and functions */ enum sk_key_type { SK_KEY_TYPE_EC = 1, SK_KEY_TYPE_RSA = 2, }; struct sk_key_gen_info { enum sk_key_type type; union { struct { int curve_nid; } ec; struct { size_t modulus_bits; unsigned int pub_exp; bool x9_31; } rsa; }; }; int SK_OPENSSL_generate_secure_key(unsigned char *secure_key, size_t *secure_key_size, const struct sk_key_gen_info *info, const struct sk_ext_lib *ext_lib, bool debug); int SK_OPENSSL_reencipher_secure_key(unsigned char *secure_key, size_t secure_key_size, bool to_new, const struct sk_ext_lib *ext_lib, bool debug); /* * Get an OpenSSL PKEY from a secure key to be used with OpenSSL. */ int SK_OPENSSL_get_secure_key_as_pkey(const unsigned char *secure_key, size_t secure_key_size, bool rsa_pss, EVP_PKEY **pkey, const struct sk_ext_lib *ext_lib, bool debug); /* * Get the public key parts from a secure key. */ struct sk_pub_key_info { enum sk_key_type type; union { struct { int curve_nid; size_t prime_len; const unsigned char *x; const unsigned char *y; } ec; struct { size_t modulus_len; const unsigned char *modulus; size_t pub_exp_len; const unsigned char *pub_exp; } rsa; }; }; typedef int (*sk_pub_key_func_t)(const struct sk_pub_key_info *pub_key, void *private); int SK_OPENSSL_get_public_from_secure_key(const unsigned char *secure_key, size_t secure_key_size, sk_pub_key_func_t pub_key_cb, void *private, const struct sk_ext_lib *ext_lib, bool debug); /* * Helper functions to setup a secure key sign context and to generate * certificate signing requests or self signed certificates with the secure key */ struct sk_rsa_pss_params { /* * salt length in bytes, or OpenSSL constants * RSA_PSS_SALTLEN_DIGEST (-1), RSA_PSS_SALTLEN_AUTO (-2), or * RSA_PSS_SALTLEN_MAX(-3) */ int salt_len; /* * OpenSSl digest nid, or NID_undef to use the same digest algorithm * as the signature algorithm */ int mgf_digest_nid; }; int SK_OPENSSL_setup_sign_context(EVP_PKEY *pkey, bool verify, int digest_nid, struct sk_rsa_pss_params *rsa_pss_params, EVP_MD_CTX **md_ctx, EVP_PKEY_CTX **pkey_ctx, bool debug); int SK_OPENSSL_generate_csr(const unsigned char *secure_key, size_t secure_key_size, const char *subject_rdns[], size_t num_subject_rdns, bool subject_utf8, const X509 *renew_cert, const char *extensions[], size_t num_extensions, int digest_nid, struct sk_rsa_pss_params *rsa_pss_params, X509_REQ **csr, const struct sk_ext_lib *ext_lib, bool debug); int SK_OPENSSL_generate_ss_cert(const unsigned char *secure_key, size_t secure_key_size, const char *subject_rdns[], size_t num_subject_rdns, bool subject_utf8, const X509 *renew_cert, const char *extensions[], size_t num_extensions, int validity_days, int digest_nid, struct sk_rsa_pss_params *rsa_pss_params, X509 **ss_cert, const struct sk_ext_lib *ext_lib, bool debug); /* * Import secure keys as PKEY, or import clear public keys as PKEY */ typedef int (*sk_rsa_sign_t)(const unsigned char *key_blob, size_t key_blob_length, unsigned char *sig, size_t *siglen, const unsigned char *tbs, size_t tbslen, int padding_type, int md_nid, void *private, bool debug); typedef int (*sk_rsa_pss_sign_t)(const unsigned char *key_blob, size_t key_blob_length, unsigned char *sig, size_t *siglen, const unsigned char *tbs, size_t tbslen, int md_nid, int mfgmd_nid, int saltlen, void *private, bool debug); typedef int (*sk_ecdsa_sign_t)(const unsigned char *key_blob, size_t key_blob_length, unsigned char *sig, size_t *siglen, const unsigned char *tbs, size_t tbslen, int md_nid, void *private, bool debug); typedef int (*sk_rsa_decrypt_t)(const unsigned char *key_blob, size_t key_blob_length, unsigned char *to, size_t *tolen, const unsigned char *from, size_t fromlen, int padding_type, void *private, bool debug); typedef int (*sk_rsa_decrypt_oaep_t)(const unsigned char *key_blob, size_t key_blob_length, unsigned char *to, size_t *tolen, const unsigned char *from, size_t fromlen, int oaep_md_nid, int mgfmd_nid, unsigned char *label, int label_len, void *private, bool debug); struct sk_funcs { sk_rsa_sign_t rsa_sign; sk_rsa_pss_sign_t rsa_pss_sign; sk_ecdsa_sign_t ecdsa_sign; sk_rsa_decrypt_t rsa_decrypt; sk_rsa_decrypt_oaep_t rsa_decrypt_oaep; }; int SK_OPENSSL_get_pkey(const unsigned char *secure_key, size_t secure_key_size, const struct sk_pub_key_info *pub_key, bool rsa_pss, const struct sk_funcs *sk_funcs, const void *private, EVP_PKEY **pkey, bool debug); int SK_OPENSSL_get_curve_from_ec_pkey(EVP_PKEY *pkey); #endif s390-tools-2.38.0/include/libseckey/sk_utilities.h000066400000000000000000000054341502674226300217330ustar00rootroot00000000000000/* * libseckey - Secure key library * * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef SK_UTILITIES_H #define SK_UTILITIES_H #include #include #include #include #include #include "libseckey/sk_openssl.h" void SK_UTIL_warnx(const char *func, const char *fmt, ...); #define sk_debug(debug, fmt...) \ do { \ if (debug) \ SK_UTIL_warnx(__func__, fmt); \ } while (0) /* EC curve information definitions and functions */ struct sk_ec_curve_info { int curve_nid; enum { SK_EC_TYPE_PRIME = 0, SK_EC_TYPE_BRAINPOOL = 1, } type; size_t prime_bits; size_t prime_len; const unsigned char *der; /* DER encoded OID */ size_t der_size; }; const struct sk_ec_curve_info *SK_UTIL_ec_get_curve_info(int curve_nid); int SK_UTIL_ec_get_prime_curve_by_prime_bits(size_t prime_bits); int SK_UTIL_ec_get_brainpool_curve_by_prime_bits(size_t prime_bits); int SK_UTIL_ec_calculate_y_coordinate(int nid, size_t prime_len, const unsigned char *x, int y_bit, unsigned char *y); /* Digest information definitions and functions */ struct sk_digest_info { int digest_nid; size_t digest_size; const char *cca_keyword; const unsigned char *der; /* DER encoded SEQ of OID and OCT-STRING */ size_t der_size; unsigned long pkcs11_mech; unsigned long pkcs11_mgf; unsigned char x9_31_md; /* X9.31 digest identifier */ }; const struct sk_digest_info *SK_UTIL_get_digest_info(int digest_nid); /* Helper functions for certificate and CSR handling */ int SK_UTIL_build_subject_name(X509_NAME **name, const char *rdns[], size_t num_rdns, bool utf8); int SK_UTIL_build_certificate_extensions(X509 *cert, X509_REQ *req, const char *exts[], size_t num_exts, const STACK_OF(X509_EXTENSION) *addl_exts); int SK_UTIL_generate_x509_serial_number(X509 *cert, size_t sn_bit_size); int SK_UTIL_build_ecdsa_signature(const unsigned char *raw_sig, size_t raw_sig_len, unsigned char *sig, size_t *sig_len); /* Functions to read and write keys, certificates, requests, etc. */ int SK_UTIL_read_x509_certificate(const char *pem_filename, X509 **cert); int SK_UTIL_write_x509_certificate(const char *pem_filename, X509 *cert); int SK_UTIL_write_x509_request(const char *pem_filename, X509_REQ *req, bool new_hdr); int SK_UTIL_read_key_blob(const char *filename, unsigned char *key_blob, size_t *key_blob_len); int SK_UTIL_write_key_blob(const char *filename, unsigned char *key_blob, size_t key_blob_len); int SK_UTIL_read_public_key(const char *pem_filename, EVP_PKEY **pkey); int SK_UTIL_write_public_key(const char *pem_filename, EVP_PKEY *pkey); #endif s390-tools-2.38.0/ip_watcher/000077500000000000000000000000001502674226300155745ustar00rootroot00000000000000s390-tools-2.38.0/ip_watcher/Makefile000066400000000000000000000011701502674226300172330ustar00rootroot00000000000000include ../common.mak all: xcec-bridge libs = $(rootdir)/libutil/libutil.a xcec-bridge: xcec-bridge.o $(libs) clean: rm -f *.o core xcec-bridge install: ip_watcher.pl xcec-bridge start_hsnc.sh $(SED) -e 's/%S390_TOOLS_VERSION%/$(S390_TOOLS_RELEASE)/' \ < start_hsnc.sh >$(DESTDIR)$(USRSBINDIR)/start_hsnc.sh; \ chown $(OWNER):$(GROUP) $(DESTDIR)$(USRSBINDIR)/start_hsnc.sh; \ chmod 755 $(DESTDIR)$(USRSBINDIR)/start_hsnc.sh; \ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 ip_watcher.pl \ $(DESTDIR)$(USRSBINDIR) $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 xcec-bridge \ $(DESTDIR)$(USRSBINDIR) .PHONY: all install clean s390-tools-2.38.0/ip_watcher/ip_watcher.pl000077500000000000000000000236771502674226300203000ustar00rootroot00000000000000#!/usr/bin/perl -w # # ip_watcher.pl - HiperSockets Network Concentrator # # looks for addresses in the HiperSockets and sets them as Proxy ARP on the # OSAs. Also adds routing entries towards the HiperSockets interfaces for # all IP addresses in it # $OPERATING_MODE="routing_only"; # ip_watcher just takes care of adapting the routing entries. ipv4 # forwarding needs to be switched on, if desired mrouted or some multicast # routing daemon should run. ip_watcher also sets the proxy arp entries # of all hsi addresses on the osa device. # # $OPERATING_MODE="full_bridging"; # this is like routing_only mode, plus xcec-bridge will bridge all # kinds of traffic (uni-, multi-, broadcast) between the interfaces, # so the stack will not do forwarding. # if interfaces come and go, xcec-bridge will be sent a SIGUSR1. # # $OPERATING_MODE="mc_bridging"; # this is a mixture of the above -- ipv4 forwarding of unicast packets # is done by the kernel, multi- and broadcast traffic is bridged by # xcec-bridge. # # $OPERATING_MODE="bc_bridging"; # this is another mixture of the above -- ipv4 forwarding of unicast # packets is done by the kernel, multicast is handled by mrouted or some # multicast router, and broadcast traffic is bridged by xcec-bridge. # # Copyright 2017 IBM Corp. # # s390-tools is free software; you can redistribute it and/or modify # it under the terms of the MIT license. See LICENSE for details. # $OPERATING_MODE="mc_bridging"; $XCEC_BRIDGE="xcec-bridge"; $XCEC_BRIDGE_FULL_PARAM="also_unicast"; $XCEC_BRIDGE_MC_PARAM=""; $XCEC_BRIDGE_BC_PARAM="only_broadcast"; $KILLALL="killall"; $SIGNAL="-USR1"; $MASK_PARAM="netmask"; $DEV_PARAM="dev"; $QETHARP="qetharp -c -q"; $ROUTE_ADD_CMD='route add -net '; $ROUTE_DEL_CMD='route del -net '; $PA_ADD_CMD='qethconf parp add -x'; $PA_DEL_CMD='qethconf parp del -x'; # $PA_ADD_CMD='echo add_rxip4'; # $PA_DEL_CMD='echo del_rxip4'; $CHECK_ONLY="no"; $nextarg=0; if ($#ARGV>=$nextarg) { if ($ARGV[$nextarg] eq "--check") { $CHECK_ONLY="yes"; $nextarg++; } } # if there is a parameter to ip_watcher.pl, the parameter will be the # Proxy ARP interface (i.e. the outgoing OSA interface). In this case, # xcec-bridge will not be started, so that only unicast is forwarded. # eth0 is default OSA interface if ($#ARGV>=$nextarg) { $PA_INTERFACE=$ARGV[$nextarg]; $START_XCEC_BRIDGE="no"; } else { $PA_INTERFACE=""; $START_XCEC_BRIDGE="yes"; } $SLEEP_TIME=2; #$TIME_LIMIT=4; #@time_array=(time,time-1,time-2); sub print_list($@) { my($h)=shift; my(@a)=@_; my($i); foreach $i (@a) { print "DEBUG ". $h .": ". $i ."\n"; } } # get outgoing OSA interface (connecting the CECs) sub get_proxy_arp_interface { my($devnos); my($chpid); my($if_name); my($type); my($port); my($chksum); my($prio); my($rtr); my($rest); if (opendir(SYSQETH, "/sys/devices/qeth")) { @ALLDEV = grep { /^.+\..+\..+$/ } readdir SYSQETH; closedir SYSQETH; foreach $DEV (@ALLDEV) { open(IFNAME, "0) { # sleep($sleep_time); # } sleep($SLEEP_TIME); } # creates a 0x01020304 out of a 1.2.3.4 sub convert_ip_string_to_number($) { my($ip_str)=shift; my(@ip); my($ip_oct1); my($ip_oct2); my($ip_oct3); my($ip_oct4); @ip=split(/\./,$ip_str); # check for parsing error if ($#ip<3) { return 0; } ($ip_oct1,$ip_oct2,$ip_oct3,$ip_oct4)=@ip; if ( ($ip_oct1<0) || ($ip_oct1>255) || ($ip_oct2<0) || ($ip_oct2>255) || ($ip_oct3<0) || ($ip_oct3>255) || ($ip_oct4<0) || ($ip_oct4>255) ) { return 0; } return ($ip_oct1<<24)+($ip_oct2<<16)+($ip_oct3<<8)+($ip_oct4); } # returns sorted list of ips (in integer format like __u32) of the interface sub get_ips_on_interface($) { my($interface)=shift; my($cmdline)="$QETHARP $interface |"; my(@ip_list)=(); my($OUTPUT); my($ip); unless (open(OUTPUT,$cmdline)) { print STDERR "can't open $cmdline"; return @ip_list; } while () { chop; $ip=convert_ip_string_to_number($_); if ($ip>0) { push(@ip_list,$ip); } } close(OUTPUT) || print STDERR "can't close $cmdline"; return sort @ip_list; } # creates a 1.2.3.4 out of a 0x1020304 sub convert_string_to_ip($) { my($ip)=shift; my($ip_oct1); my($ip_oct2); my($ip_oct3); my($ip_oct4); $ip_oct4=$ip&0xff; $ip>>=8; $ip_oct3=$ip&0xff; $ip>>=8; $ip_oct2=$ip&0xff; $ip>>=8; $ip_oct1=$ip&0xff; return "$ip_oct1.$ip_oct2.$ip_oct3.$ip_oct4"; } sub __min($$) { my($a)=shift; my($b)=shift; if ($a<$b) { return $a; } else { return $b; } } # will create an array of routes in string format sub get_routes_of_ip_list(@) { my(@ip_list)=@_; my(@route_list)=(); my($ip); my($ips_left); my($ips_to_combine); my($ip_shifted); my($ips_found); my($end); my($order); my($mask); my($ip_str); my($mask_str); my($ips_fetched); while ($#ip_list>=0) { # ips_left is the number of ips left in the list $ips_left=$#ip_list; $ip=shift(@ip_list); $ips_to_combine=1; $ip_shifted=$ip; while ($ip_shifted%2==0) { $ips_to_combine<<=1; $ip_shifted>>=1; # 0 should never be in the list, anyway... if (!$ip_shifted) { last; } } # ips_to_combine is a power of 2 and contains the max number # of entries that could compressed into one route due to its # alignment $end=__min($ips_to_combine-1,$ips_left); $order=1; $ips_found=1; while ($ips_found<=$end) { # ips_found-1, as we have shifted the first ip # already if ($ip_list[$ips_found-1]!=$ip+$ips_found) { last; } $ips_found++; if ($ips_found==2*$order) { $order<<=1; } } # ips_found is now the number of subsequent ips that we can # subsum (one of which is shifted already) $mask=(-$order)&0xffffffff; $ips_fetched=1; while ($ips_fetched<$order) { $ips_fetched++; shift(@ip_list); } $mask_str=convert_string_to_ip($mask); $ip_str=convert_string_to_ip($ip); unshift(@route_list,"$ip_str $MASK_PARAM $mask_str"); } return @route_list; } # will create an array of rxips in string format sub get_pas_of_ip_list(@) { my(@ip_list)=@_; my(@pa_list)=(); foreach $ip (@ip_list) { unshift(@pa_list,"" . sprintf("%08x",$ip)); } return @pa_list; } sub is_in_list($@) { my($item)=shift; my(@list)=@_; my($i); foreach $i (@list) { if ($i eq $item) { return 1; } } return 0; } sub exec_for_diff(@) { my($cmd)=shift; my($new_list,$old_list)=@_; foreach $line (@$new_list) { unless (is_in_list($line,@$old_list)) { system($cmd . $line . "> /dev/null 2>&1"); } } } sub wait_for_changes() { # blocking ioctl to be informed on SETIP/DELIPs (once it's implemented in # hardware) or sleep for X timeunits } sub main() { my(@routes)=(); my(@pas)=(); my(@new_routes); my(@new_pas); my(@interface_list)=(); my(@old_if_list); my($interface); my(@ip_list); my($route); my(@tmp_routes); get_proxy_arp_interface(); if ($CHECK_ONLY eq "yes") { exit 0; } if ($START_XCEC_BRIDGE eq "yes") { if ($OPERATING_MODE eq "full_bridging") { system("$XCEC_BRIDGE $XCEC_BRIDGE_FULL_PARAM &")==0 || die "can't fork $XCEC_BRIDGE: $?"; } if ($OPERATING_MODE eq "mc_bridging") { system("$XCEC_BRIDGE $XCEC_BRIDGE_MC_PARAM &")==0 || die "can't fork $XCEC_BRIDGE: $?"; } if ($OPERATING_MODE eq "bc_bridging") { system("$XCEC_BRIDGE $XCEC_BRIDGE_BC_PARAM &")==0 || die "can't fork $XCEC_BRIDGE: $?"; } } for (;;) { if ( ($OPERATING_MODE eq "mc_bridging") || ($OPERATING_MODE eq "bc_bridging") || ($OPERATING_MODE eq "full_bridging") ) { @old_if_list=@interface_list; } @interface_list=update_interface_list(); if ( ($OPERATING_MODE eq "mc_bridging") || ($OPERATING_MODE eq "bc_bridging") || ($OPERATING_MODE eq "full_bridging") ) { if ( join(':',@old_if_list) ne join(':',@interface_list) ) { if ($START_XCEC_BRIDGE eq "yes") { system("$KILLALL $SIGNAL $XCEC_BRIDGE")==0 || print STDERR "can't send signal " . "to $XCEC_BRIDGE to update " . "interfaces.\n"; } } } @new_routes=(); @new_pas=(); foreach $interface (@interface_list) { @ip_list=get_ips_on_interface($interface); @tmp_routes=get_routes_of_ip_list(@ip_list); foreach $route (@tmp_routes) { unshift(@new_routes, "$route $DEV_PARAM $interface"); } @tmp_pas=get_pas_of_ip_list(@ip_list); foreach $pa (@tmp_pas) { unshift(@new_pas, "$pa $PA_INTERFACE"); } } exec_for_diff($ROUTE_ADD_CMD,\@new_routes,\@routes); exec_for_diff($ROUTE_DEL_CMD,\@routes,\@new_routes); @routes=@new_routes; exec_for_diff($PA_ADD_CMD,\@new_pas,\@pas); exec_for_diff($PA_DEL_CMD,\@pas,\@new_pas); @pas=@new_pas; wait_for_changes(); limit_frequency(); } } main(); s390-tools-2.38.0/ip_watcher/start_hsnc.sh000066400000000000000000000055711502674226300203100ustar00rootroot00000000000000#!/bin/bash # # start_hsnc.sh - HiperSockets Network Concentrator # # Wrapper start script for ip_watcher.pl, also cleanup, when ip_watcher.pl # gets killed. # # Copyright IBM Corp. 2003, 2017 # # s390-tools is free software; you can redistribute it and/or modify # it under the terms of the MIT license. See LICENSE for details. # # # functions # function __usage { echo "" echo "For more information about HiperSocket Network Concentrator" echo "please refer to the 'Device Drivers, Features, and Commands'" echo "manual." exit 0 } function PrintVersion { echo "$script_name version %S390_TOOLS_VERSION%" echo "Copyright IBM Corp. 2003, 2017" } # # main # script_name="HiperSocket Network Concentrator" # name of this script # # what is the kernel version we are on ? # kernel_version=`uname -r` xcec_bridge="yes" if [ "${kernel_version:0:1}" \> 2 ]; then kernel_version="ok" else if [ "${kernel_version:4:2}" \< 26 ]; then xcec_bridge="no" fi if [ "${kernel_version:2:1}" \> 4 ]; then kernel_version="ok" else echo kernel version too old for this hsnc version. exit 1 fi fi # # parse options (currently none avail) # case "$1" in -v | --version ) PrintVersion exit 0 ;; -h | --help ) __usage ;; esac if [ X${1}X != XX ] && [ $kernel_version = "ok" ] ; then if ! ls /sys/class/net | grep "^$1$" > /dev/null; then echo interface $1 does not exist. exit 1 fi else if [ $xcec_bridge = "no" ] ; then echo kernel version too old for this hsnc version. exit 1 fi fi ip_watcher.pl $* echo ip_watcher.pl was terminated, cleaning up. if [ X${1}X == XX ] ; then echo killing xcec-bridge killall xcec-bridge fi echo removing all parp entries from mc interfaces if [ X${1}X == XX ] ; then for DEV in $(ls /sys/devices/qeth/ | egrep '^.+\..+\..+') do if_name=`cat /sys/devices/qeth/$DEV/if_name | sed 's/$/\$/'` rtr=`cat /sys/devices/qeth/$DEV/route4 2> /dev/null | egrep 'multicast'` if [ -n "$rtr" ] ; then echo $if_name >> /tmp/ip_watcher.cleanup1 fi done else echo ${1}$ > /tmp/ip_watcher.cleanup1 fi qethconf rxip list | sed 's/add/del/' | egrep -f /tmp/ip_watcher.cleanup1 > /tmp/ip_watcher.cleanup2 while read line; do qethconf $line > /dev/null 2>&1 done < /tmp/ip_watcher.cleanup2 rm /tmp/ip_watcher.cleanup1 rm /tmp/ip_watcher.cleanup2 echo removing all routes from connector interfaces for DEV in $(ls /sys/devices/qeth/ | egrep '^.+\..+\..+') do if_name=`cat /sys/devices/qeth/$DEV/if_name | sed 's/$/\$/'` rtr=`cat /sys/devices/qeth/$DEV/route4 2> /dev/null | egrep 'connector'` if [ -n "$rtr" ] ; then echo $if_name >> /tmp/ip_watcher.cleanup1 fi done route -n | egrep -f /tmp/ip_watcher.cleanup1 > /tmp/ip_watcher.cleanup2 while read line; do route del -net `echo $line | awk '{print $1 " netmask " $3 " dev " $8}'` done < /tmp/ip_watcher.cleanup2 rm /tmp/ip_watcher.cleanup1 rm /tmp/ip_watcher.cleanup2 s390-tools-2.38.0/ip_watcher/xcec-bridge.c000066400000000000000000000330331502674226300201160ustar00rootroot00000000000000/* * xcec-bridge - HiperSockets Network Concentrator * * Parameters: * also_unicast - uni-, multi-, broadcast is bridged * - multi-, broadcast is bridged * only broadcast - only broadcast is bridged * * Copyright IBM Corp. 2003, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/util_libc.h" #include "lib/zt_common.h" /* a signal causes the interfaces to be re-checked */ #define LOGGING_FACILITY LOG_LOCAL0 #define UPDATE_SIGNAL SIGUSR1 #define DEV_NAME_SIZE IFNAMSIZ #define BUFFER_LEN 65536 int so_sndbuf=(8*1024*1024); int do_unicast_bridging=0; int do_multicast_bridging=1; int do_broadcast_bridging=0; struct int_sock { #define I_S_FEATURE_PASSTHROUGH 0x01 int features; int i_fd; int o_fd; char dev_name[DEV_NAME_SIZE]; int mtu_warning; struct int_sock *next; }; fd_set work_fd_set; /* used in and changed by select */ struct set { fd_set fds; int highest_fd; struct int_sock *i_s_list; }; struct set select_set; volatile int update_interface_trigger=0; int open_incoming_socket(char *dev_name) { int fd,retval; struct sockaddr_ll sock_addr; struct ifreq if_req; struct packet_mreq mc_req; /* we want to receive everything. we filter out stuff that we * don't forward by ourselves */ fd=socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL)); if (fd==-1) { syslog(LOG_ERR,"can't open raw packet socket, " \ "interface %s will not be used: %s", dev_name,strerror(errno)); return -1; } util_strlcpy(if_req.ifr_name, dev_name, DEV_NAME_SIZE); retval=ioctl(fd,SIOCGIFINDEX,&if_req); if (retval==-1) { syslog(LOG_ERR,"can't ioctl on raw packet socket, " \ "interface %s will not be used: %s", dev_name,strerror(errno)); close(fd); return -1; } sock_addr.sll_protocol=htons(ETH_P_ALL); sock_addr.sll_ifindex=if_req.ifr_ifindex; sock_addr.sll_family=AF_PACKET; retval=bind(fd,(struct sockaddr *)&sock_addr, sizeof(struct sockaddr_ll)); if (retval==-1) { syslog(LOG_ERR,"can't bind packet raw packet socket to " \ "interface %s -- it will not be used: %s", dev_name,strerror(errno)); close(fd); return -1; } mc_req.mr_ifindex=if_req.ifr_ifindex; mc_req.mr_type=PACKET_MR_ALLMULTI; mc_req.mr_alen=0; retval=setsockopt(fd,SOL_SOCKET,PACKET_ADD_MEMBERSHIP, &mc_req,sizeof(struct packet_mreq)); if (retval==-1) { syslog(LOG_ERR,"can't set socket options to join all " \ "multicast groups -- multicast may not be " \ "forwarded from %s: %s",dev_name,strerror(errno)); } return fd; } int open_outgoing_socket(char *dev_name) { int fd,retval; int val; fd=socket(PF_INET,SOCK_RAW,IPPROTO_RAW); if (fd==-1) { syslog(LOG_ERR,"can't open raw inet socket, " \ "interface %s will not be used: %s", dev_name,strerror(errno)); return -1; } /* IP_HDRINCL should be set by the stack already, we'll set * it nevertheless */ val=1; retval=setsockopt(fd,SOL_IP,IP_HDRINCL,&val,sizeof(int)); if (retval==-1) { syslog(LOG_ERR,"can't set IP_HDRINCL on raw inet socket, " \ "interface %s will not be used: %s", dev_name,strerror(errno)); close(fd); return -1; } /* we bind the socket to the device */ retval=setsockopt(fd,SOL_SOCKET,SO_BINDTODEVICE, dev_name,strlen(dev_name)+1); if (retval==-1) { syslog(LOG_ERR,"can't bind raw inet socket to device, " \ "interface %s will not be used: %s", dev_name,strerror(errno)); close(fd); return -1; } /* get max socket buffer */ retval=setsockopt(fd,SOL_SOCKET,SO_SNDBUF,&so_sndbuf,sizeof(int)); if (retval==-1) { syslog(LOG_ERR,"can't set socket buffer size, " \ "interface %s will not be used: %s", dev_name,strerror(errno)); close(fd); return -1; } /* and enable broadcast on the socket */ val=1; retval=setsockopt(fd,SOL_SOCKET,SO_BROADCAST,&val,sizeof(int)); if (retval==-1) { syslog(LOG_ERR,"can't enable broadcast on raw inet socket, " \ "interface %s will not be used: %s", dev_name,strerror(errno)); close(fd); return -1; } return fd; } int interface_in_list(struct int_sock *item,struct int_sock *list) { for (;list;list=list->next) { if (!strncmp(item->dev_name,list->dev_name,DEV_NAME_SIZE)) { return 1; } } return 0; } int read_sys(struct int_sock **nlist) { DIR *qdir; FILE *qfile; struct dirent *qde; char fname[256]; char *tmp; char if_name[256]; char rtr[256]; struct int_sock *is = NULL; int i; qdir = opendir("/sys/devices/qeth"); if (!qdir) { syslog(LOG_ERR,"failed to open directory /sys/devices/qeth"); return errno; } while ((qde = readdir(qdir))) { if ((qde->d_type == DT_DIR) && (qde->d_name[0] != '.')) { strcpy(fname, "/sys/devices/qeth/"); strcat(fname, qde->d_name); strcat(fname, "/if_name"); qfile = fopen(fname, "r"); if (!qfile) { continue; } tmp = if_name; while ((i = fgetc(qfile)) != EOF) { if ((char)i == '\n') { *tmp = 0; break; } else { *tmp = (char)i; } tmp++; } *tmp = 0; fclose(qfile); strcpy(fname, "/sys/devices/qeth/"); strcat(fname, qde->d_name); strcat(fname, "/route4"); qfile = fopen(fname, "r"); if (!qfile) { continue; } tmp = rtr; while ((i = fgetc(qfile)) != EOF) { if ((char)i == '\n') { *tmp = 0; break; } else { *tmp = (char)i; } tmp++; } *tmp = 0; fclose(qfile); if (!strstr(rtr, "multicast") && !strstr(rtr, "connector")) continue; is = malloc(sizeof(struct int_sock)); if (!is) { syslog(LOG_ERR,"no memory while reading from "\ "/sys/devices/qeth some interface"\ "might not be used"); continue; } /* as soon as we have one interface echoeing back * broadcasts to us, we don't bridge broadcast * traffic */ if (!(strstr(rtr, "connector+") || strstr(rtr, "multicast router+"))) do_broadcast_bridging=0; is->mtu_warning=0; util_strlcpy(is->dev_name, if_name, DEV_NAME_SIZE); if (!strncmp(if_name,"hsi",3)) { is->features=I_S_FEATURE_PASSTHROUGH; } is->next = *nlist; *nlist = is; } } closedir(qdir); return 0; } void update_interfaces() { struct int_sock *new_list=NULL; struct int_sock *i=NULL,*j,*prev; struct int_sock *new_int=NULL; int i_fd,o_fd; /* if all interfaces are '+'-interfaces, we bridge broadcast */ do_broadcast_bridging=1; update_interface_trigger=0; syslog(LOG_DEBUG,"rechecking interfaces"); if (read_sys(&new_list)) return; for (i=select_set.i_s_list;i;i=i->next) { if (!interface_in_list(i,new_list)) { /* remove interface i */ j=select_set.i_s_list; prev=NULL; while (j) { if (!strncmp(j->dev_name,i->dev_name, DEV_NAME_SIZE)) { if (!prev) { select_set.i_s_list=j->next; } else { prev->next=j->next; } prev=j; j=j->next; free(j); } else { j=j->next; } } /* and close the socket */ close(i->i_fd); close(i->o_fd); syslog(LOG_INFO,"removed interface %s",i->dev_name); } } for (i=new_list;i;i=i->next) { if (!interface_in_list(i,select_set.i_s_list)) { /* add interface i */ new_int=malloc(sizeof(struct int_sock)); if (!new_int) { syslog(LOG_ERR,"can't add interface %s -- " \ "no memory",i->dev_name); continue; } i_fd=open_incoming_socket(i->dev_name); if (i_fd==-1) { free(new_int); continue; } o_fd=open_outgoing_socket(i->dev_name); if (o_fd==-1) { close(i_fd); free(new_int); continue; } util_strlcpy(new_int->dev_name, i->dev_name, DEV_NAME_SIZE); new_int->i_fd=i_fd; new_int->o_fd=o_fd; new_int->features=i->features; new_int->next=select_set.i_s_list; select_set.i_s_list=new_int; syslog(LOG_INFO,"added interface %s",i->dev_name); } } /* kill temporary new_list */ while (new_list) { i=new_list->next; free(new_list); new_list=i; } /* prepare the fd_set for select */ FD_ZERO(&select_set.fds); for (i=select_set.i_s_list;i;i=i->next) { FD_SET(i->i_fd,&select_set.fds); select_set.highest_fd=(i->i_fd>select_set.highest_fd)? i->i_fd:select_set.highest_fd; } } void process_packet(struct int_sock *i_s) { int retval; char buffer[BUFFER_LEN]; int buffer_len; struct int_sock *i_s_item; struct sockaddr_ll s_ll; struct sockaddr_in s_in; socklen_t sll_len; sll_len=(socklen_t)sizeof(struct sockaddr_ll); buffer_len=recvfrom(i_s->i_fd,buffer,BUFFER_LEN,0, (struct sockaddr *)&s_ll,&sll_len); if (buffer_len==-1) { syslog(LOG_WARNING,"recvfrom failed on %s: %s\n", i_s->dev_name,strerror(errno)); return; } /* nothing read */ if (buffer_len==0) return; /* no packets that came from our own stack... that could lead to * traffic loops */ if (s_ll.sll_pkttype==PACKET_OUTGOING) return; /* only do unicast bridging when required */ if ( (s_ll.sll_pkttype==PACKET_HOST) && (!do_unicast_bridging) ) return; /* only do multicast bridging when required */ if ( (s_ll.sll_pkttype==PACKET_MULTICAST) && (!do_multicast_bridging) ) return; /* broadcast is critical, see comment above */ if (!do_broadcast_bridging) { if (s_ll.sll_pkttype==PACKET_BROADCAST) return; } /* only do v4 at this time */ if (s_ll.sll_protocol!=ETH_P_IP) return; /* forward buffer to each interface ... */ for (i_s_item=select_set.i_s_list;i_s_item;i_s_item=i_s_item->next) { /* ... but i_s */ if (i_s_item==i_s) continue; s_ll.sll_ifindex=0; s_in.sin_family=AF_INET; s_in.sin_port=0; if (s_ll.sll_pkttype==PACKET_BROADCAST) { s_in.sin_addr.s_addr=INADDR_BROADCAST; } else { memcpy(&s_in.sin_addr, &buffer[16 + ETH_HLEN], 4); } retval=sendto(i_s_item->o_fd, buffer + ETH_HLEN, buffer_len - ETH_HLEN, 0, (struct sockaddr *)&s_in, sizeof(struct sockaddr_in)); if (retval==-1) { if ( (errno==EMSGSIZE) && (!i_s_item->mtu_warning) ) { syslog(LOG_WARNING,"MTU of %s too small " \ "to forward packet with size of %i" \ " -- won't show warning again.", i_s_item->dev_name,buffer_len); i_s_item->mtu_warning=1; } else { syslog(LOG_WARNING,"sendto failed on %s: " \ "%s\n",i_s_item->dev_name, strerror(errno)); } } else if (retval != (buffer_len - ETH_HLEN)) { syslog(LOG_WARNING,"sendto sent only %i instead " \ "of %i bytes on %s\n", retval,buffer_len,i_s->dev_name); } } } void action_handler(int UNUSED(s)) { update_interface_trigger=1; syslog(LOG_DEBUG,"signal caught"); /* select will return, interfaces will be re-checked */ } int main(int argc,char *argv[]) { struct int_sock *i_s; int retval,r; struct sigaction s_a; if ( (argc>1) && (!strncmp(argv[1],"also_unicast",12)) ) { do_unicast_bridging=1; } else if ( (argc>1) && (!strncmp(argv[1],"only_broadcast",14)) ) { do_multicast_bridging=0; } openlog("xcec-bridge",LOG_NDELAY,LOGGING_FACILITY); FD_ZERO(&select_set.fds); select_set.i_s_list=NULL; select_set.highest_fd=0; s_a.sa_handler=action_handler; if (sigemptyset(&s_a.sa_mask)) { syslog(LOG_ERR,"problem in sigemptyset: %s -- exiting", strerror(errno)); return 1; } s_a.sa_flags=0; retval=sigaction(UPDATE_SIGNAL,&s_a,NULL); if (sigemptyset(&s_a.sa_mask)) { syslog(LOG_ERR,"problem in sigemptyset: %s -- exiting", strerror(errno)); return 1; } if (sigaddset(&s_a.sa_mask,UPDATE_SIGNAL)) { syslog(LOG_ERR,"problem in sigaddset: %s -- exiting %s", argv[0],strerror(errno)); return 1; } r=sigprocmask(SIG_BLOCK,&s_a.sa_mask,NULL); if (r) { syslog(LOG_ERR,"sigprocmask: %s",strerror(errno)); } syslog(LOG_INFO,"*** started ***"); update_interfaces(); while (1) { r=sigprocmask(SIG_UNBLOCK,&s_a.sa_mask,NULL); if (r) { /* while blocked: */ update_interfaces(); syslog(LOG_INFO,"sigprocmask (unblock): %s", strerror(errno)); /* try until sigprocmask is not interrupted by a * signal */ while (sigprocmask(SIG_UNBLOCK,&s_a.sa_mask,NULL)) ; } memcpy(&work_fd_set,&select_set.fds,sizeof(fd_set)); retval=select(select_set.highest_fd+1,&work_fd_set, NULL,NULL,NULL); r=sigprocmask(SIG_BLOCK,&s_a.sa_mask,NULL); if (r) { syslog(LOG_INFO,"sigprocmask (block): %s", strerror(errno)); /* try until sigprocmask is not interrupted by a * signal */ while (sigprocmask(SIG_BLOCK,&s_a.sa_mask,NULL)) ; /* when blocked: */ update_interfaces(); } /* a signal came in after we unblocked * or before we blocked? we may process one packet before * the list gets updated, but we do this check here never- * theless in order to not catch a signal during some * system call in update_interfaces */ if (update_interface_trigger) { update_interfaces(); } if (retval==-1) { if (errno==EINTR) { update_interfaces(); } else if (errno) { syslog(LOG_WARNING,"select returned with %s", strerror(errno)); } continue; /* fds are undefined -> no packets came in at this time */ } /* check all fds regardless of retval */ for (i_s=select_set.i_s_list;i_s;i_s=i_s->next) { if (FD_ISSET(i_s->i_fd,&work_fd_set)) { process_packet(i_s); } } } /* cleanup... no. */ } s390-tools-2.38.0/ipl_tools/000077500000000000000000000000001502674226300154535ustar00rootroot00000000000000s390-tools-2.38.0/ipl_tools/Makefile000066400000000000000000000020301502674226300171060ustar00rootroot00000000000000include ../common.mak libs = $(rootdir)/libutil/libutil.a all: chreipl lsreipl chshut lsshut objects = main.o ccw.o fcp.o nvme.o system.o shutdown.o \ cmd_lsshut.o cmd_chshut.o cmd_lsreipl.o cmd_chreipl.o proc.o chreipl: $(objects) $(libs) $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ lsreipl: ln -sf chreipl lsreipl chshut: ln -sf chreipl chshut lsshut: ln -sf chreipl lsshut clean: rm -f *.o lsreipl chreipl chshut lsshut install: all $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 chreipl \ $(DESTDIR)$(USRSBINDIR) ln -f -s chreipl $(DESTDIR)$(USRSBINDIR)/lsreipl ln -f -s chreipl $(DESTDIR)$(USRSBINDIR)/chshut ln -f -s chreipl $(DESTDIR)$(USRSBINDIR)/lsshut $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 man/chreipl.8 \ $(DESTDIR)$(MANDIR)/man8 $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 man/lsreipl.8 \ $(DESTDIR)$(MANDIR)/man8 $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 man/lsshut.8 \ $(DESTDIR)$(MANDIR)/man8 $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 man/chshut.8 \ $(DESTDIR)$(MANDIR)/man8 .PHONY: all install clean s390-tools-2.38.0/ipl_tools/ccw.c000066400000000000000000000054511502674226300164000ustar00rootroot00000000000000/* * ipl_tools - Linux for System z reipl and shutdown tools * * CCW device functions * * Copyright IBM Corp. 2008, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include "lib/util_path.h" #include "lib/util_panic.h" #include "ipl_tools.h" /* * Look up for the device in /sys/devices/ hierarchy. * * path must be PATH_MAX large and the value will be replaced in place */ static int device_sysfs_path(const char *device, char *path, const size_t path_size) { util_assert(device != NULL, "Internal error: device is NULL"); util_assert(path != NULL, "Internal error: path is NULL"); util_assert(path_size == PATH_MAX, "Internal error: path_size is '%zu', but must be '%zu'", path_size, PATH_MAX); char *buf = util_path_sysfs("block/%s/device", device); if (!realpath(buf, path)) { free(buf); return -1; } free(buf); return 0; } /* * Check if the specified device number is a valid device number * which can be found in the /sys/bus/ccw/drivers/dasd-eckd/ * structure. * * This does not work when booting from tape. */ int ccw_is_device(const char *busid) { static const char *const driver_paths[] = { "dasd-eckd", "virtio_ccw", "dasd-fba" }; char *path; size_t i; for (i = 0; i < ARRAY_SIZE(driver_paths); i++) { path = util_path_sysfs("bus/ccw/drivers/%s/%s", driver_paths[i], busid); if (access(path, R_OK) == 0) { free(path); return 1; } free(path); } return 0; } /* * Check if the specified device is a valid virtio subchannel device */ int ccw_is_virtio_device(const char *device) { char path[PATH_MAX] = { '\0' }; unsigned virtio = 0; char *path_pattern; if (device_sysfs_path(device, path, sizeof(path)) != 0) return -1; /* * The output has the following format: * /sys/devices/css0/0.0.0000/0.0.0000/virtio0/block/vda */ path_pattern = util_path_sysfs("devices/css0/%%*[0-9a-f.]/%%*[0-9a-f.]/virtio%%u"); if (sscanf(path, path_pattern, &virtio) != 1) { free(path_pattern); return -1; } free(path_pattern); return 0; } /* * Return CCW Bus ID */ void ccw_busid_get(const char *device, char *busid) { char path[PATH_MAX] = { '\0' }; char *path_pattern; if (device_sysfs_path(device, path, sizeof(path)) != 0) ERR_EXIT("Could not lookup device number for \"%s\"", device); /* * The output has the following format: * /sys/devices/css0/0.0.0119/0.0.3f19/block/dasda * /sys/devices/css0/0.0.0000/0.0.0000/virtio0/block/vda */ path_pattern = util_path_sysfs("devices/css0/%%*[0-9a-f.]/%%[0-9a-f.]"); if (sscanf(path, path_pattern, busid) != 1) { free(path_pattern); ERR_EXIT("Could not lookup device number for \"%s\"", device); } free(path_pattern); return; } s390-tools-2.38.0/ipl_tools/cmd_chreipl.c000066400000000000000000000626671502674226300201110ustar00rootroot00000000000000/* * ipl_tools - Linux for System z reipl and shutdown tools * * Command: chreipl * * Copyright IBM Corp. 2008, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include "lib/util_libc.h" #include "lib/util_proc.h" #include "lib/util_base.h" #include "lib/zt_common.h" #include "lib/util_path.h" #include "ipl_tools.h" #include "proc.h" #include #define BOOTPARMS_NSS_MAX 56 #define BOOTPARMS_CCW_MAX 64 #define BOOTPARMS_FCP_MAX 3452 #define OPT_BRCHR 0x80 #define MD_MAJOR 9 enum target_type { TT_CCW, TT_FCP, TT_NSS, TT_NODE, TT_NVME, TT_ECKD, }; enum reipl_type { REIPL_FCP, REIPL_CCW, REIPL_NSS, REIPL_NVME, REIPL_ECKD, }; static const char *const usage_chreipl = "Usage: %s [TARGET] [ARGS] [OPTIONS]\n" "\n" " chreipl [ccw] [-d] [OPTIONS]\n" " chreipl [eckd] [-d] [OPTIONS]\n" " chreipl [fcp] [-d] [-w] [-l] [OPTIONS]\n" " chreipl nvme [-i] [-s] [OPTIONS]\n" " chreipl [node] [OPTIONS]\n" " chreipl nss [-n] [OPTIONS]\n" " chreipl [-h] [-v]\n" "\n" "The following re-IPL targets are supported:\n" " ccw IPL from CCW device\n" " eckd IPL from ECKD device\n" " fcp IPL from FCP device\n" " nvme IPL from NVME device\n" " nss IPL from NSS\n" " node IPL from device specified by device node or directory\n" "\n" "General options:\n" " -f, --force Allow targets that cannot be verified by the system\n" " -p, --bootparms Boot parameter specification\n" " -h, --help Print this help, then exit\n" " -v, --version Print version information, then exit\n" "\n" "Options for ccw target:\n" " -d, --device Device number of the CCW IPL device\n" " -L, --loadparm Loadparm specification\n" " -c, --clear 0|1 Control if memory is cleared on re-IPL\n" "\n" "Options for eckd target:\n" " -d, --device Device number of the ECKD IPL device\n" " -b, --bootprog Bootprog specification\n" " --brchr Boot record location in Cylinder,Head,Record format\n" " -L, --loadparm Loadparm specification\n" " -c, --clear 0|1 Control if memory is cleared on re-IPL\n" "\n" "Options for fcp target:\n" " -d, --device Device number of the adapter of the FCP IPL device\n" " -l --lun Logical unit number of the FCP IPL device\n" " -w --wwpn World Wide Port Name of the FCP IPL device\n" " -b, --bootprog Bootprog specification\n" " -L, --loadparm Loadparm specification\n" " -c, --clear 0|1 Control if memory is cleared on re-IPL\n" "\n" "Options for nvme target:\n" " -i, --fid PCI Function ID of NVME IPL device (hex)\n" " -s --nsid Namespace ID of NVME IPL device (decimal, default 1)\n" " -b, --bootprog Bootprog specification\n" " -L, --loadparm Loadparm specification\n" " -c, --clear 0|1 Control if memory is cleared on re-IPL\n" "\n" "Options for nss target:\n" " -n, --name Identifier of the NSS\n" "\n" "Options for node target:\n" " Depending on underlying target type (ccw or fcp)\n"; static struct locals { char loadparm[9]; /* Entry in the boot menu */ int loadparm_set; char bootprog[11]; /* bootprog number (32 bit)*/ int bootprog_set; char wwpn[20]; /* 18 character +0x" */ int wwpn_set; char lun[20]; /* 18 character +0x" */ int lun_set; char busid[10]; /* Bus ID e.g. 0.0.4711 */ int fid_set; char fid[FID_MAX_LEN]; int nsid_set; char nsid[11]; /* 10 decimal chars + null */ int busid_set; char dev[15]; /* Device (e.g. dasda) */ int dev_set; char nss_name[9]; /* NSS name */ int nss_name_set; char bootparms[4096]; int bootparms_set; int force_set; enum target_type target_type; /* CCW,FCP,NVME,NSS or NODE */ int target_type_set; int target_type_auto_mode; enum reipl_type reipl_type; /* CCW, FCP, NVME, NSS */ int reipl_clear; char *brchr; int brchr_set; } l; static void __noreturn print_usage_chreipl_exit(void) { printf(usage_chreipl, g.prog_name); exit(0); } static int busid_strtok(char *str, unsigned long max_len, unsigned long *val) { char *token, *end; token = strtok(str, "."); if (!token) return -1; if (strlen(token) > max_len) return -1; *val = strtoul(token, &end, 16); if (*end) return -1; return 0; } static int mk_busid(char busid_out[9], const char *busid_in) { unsigned long devno, cssid = 0, ssid = 0; char busid_tmp[10]; if (strlen(busid_in) > 9) return -1; strcpy(busid_tmp, busid_in); if (strstr(busid_in, ".")) { /* Check xx.x.xxxx full bus-ID format */ if (busid_strtok(busid_tmp, 2, &cssid)) return -1; if (busid_strtok(NULL, 1, &ssid)) return -1; if (busid_strtok(NULL, 4, &devno)) return -1; /* Ensure that there are no more additional fields */ if (strtok(NULL, ".")) return -1; } else { /* Check xxxx short bus-ID format */ if (busid_strtok(busid_tmp, 4, &devno)) return -1; } sprintf(busid_out, "%lx.%lx.%04lx", cssid, ssid, devno); return 0; } static void set_device(const char *busid) { if (mk_busid(l.busid, busid)) ERR_EXIT("Invalid device number \"%s\" specified", busid); l.busid_set = 1; } static void set_nss_name(const char *nss_name) { if (strlen(nss_name) > 8) ERR_EXIT("NSS name \"%s\" exceeds maximum of 8 characters", nss_name); strcpy(l.nss_name, nss_name); l.nss_name_set = 1; } static void set_loadparm(const char *loadparm) { if (strlen(loadparm) > 8) ERR_EXIT("Loadparm \"%s\" exceeds 8 characters", loadparm); strcpy(l.loadparm, loadparm); if (strcmp(l.loadparm, " ") == 0) l.loadparm[0] = '\0'; l.loadparm_set = 1; } static void set_bootprog(const char *bootprog) { long long bootprog_int; char *endptr; bootprog_int = strtoll(bootprog, &endptr, 10); if (*endptr) ERR_EXIT("Bootprog \"%s\" is not a decimal number", bootprog); if (bootprog_int > UINT_MAX) ERR_EXIT("Invalid bootprog specified"); util_strlcpy(l.bootprog, bootprog, sizeof(l.bootprog)); l.bootprog_set = 1; } static void set_bootparms(const char *bootparms) { unsigned int i; for (i = 0; i < strlen(bootparms); i++) { if (isascii(bootparms[i])) continue; ERR_EXIT("Non ASCII characters found in boot parameters"); } if (strlen(bootparms) + 1 > sizeof(l.bootparms)) ERR_EXIT("Boot parameter line is too long"); strcpy(l.bootparms, bootparms); l.bootparms_set = 1; } static void set_lun(const char *lun) { unsigned long long lun_tmp; char *endptr; lun_tmp = strtoull(lun, &endptr, 16); if (*endptr) ERR_EXIT("LUN \"%s\" is not a hexadecimal number", lun); snprintf(l.lun, sizeof(l.lun), "0x%016llx", lun_tmp); l.lun_set = 1; } static void set_wwpn(const char *wwpn) { unsigned long long wwpn_tmp; char *endptr; wwpn_tmp = strtoull(wwpn, &endptr, 16); if (*endptr) ERR_EXIT("WWPN \"%s\" is not a hexadecimal number", wwpn); snprintf(l.wwpn, sizeof(l.wwpn), "0x%016llx", wwpn_tmp); l.wwpn_set = 1; } static void set_nvme_nsid(const char *nsid) { unsigned long long nsid_tmp; char *endptr; nsid_tmp = strtoull(nsid, &endptr, 10); if (*endptr) ERR_EXIT("NSID \"%s\" is not a decimal number", nsid); snprintf(l.nsid, sizeof(l.nsid), "%08llu", nsid_tmp); l.nsid_set = 1; } static void set_nvme_fid(const char *fid) { unsigned long long fid_tmp; char *endptr; fid_tmp = strtoull(fid, &endptr, 16); if (*endptr) ERR_EXIT("FID \"%s\" is not a hexadecimal number", fid); snprintf(l.fid, sizeof(l.fid), "0x%08llx", fid_tmp); l.fid_set = 1; /* nsid defaults to 1, if not already set */ if (!l.nsid_set) set_nvme_nsid("1"); } static void parse_fcp_args(char *nargv[], int nargc) { /* * we might be called like this: * chreipl fcp 4711 0x12345... 0x12345... */ if (l.busid_set || l.wwpn_set || l.lun_set) ERR_EXIT("Use either options or positional parameters"); if (nargc > 3) ERR_EXIT("Too many arguments specified for \"fcp\" re-IPL " "type"); else if (nargc != 3) ERR_EXIT("The \"fcp\" re-IPL type requires device, WWPN, " "and LUN"); set_device(nargv[0]); set_wwpn(nargv[1]); set_lun(nargv[2]); } static void parse_nvme_args(char *nargv[], int nargc) { /* * we might be called like this: * chreipl nvme 0x13 1 */ if (l.busid_set || l.fid_set || l.nsid_set || l.dev_set) ERR_EXIT("Use either options or positional parameters"); if (nargc > 2) ERR_EXIT("Too many arguments specified for \"nvme\" re-IPL " "type"); else if (nargc < 1) ERR_EXIT("The \"nvme\" re-IPL type requires function id, and " "optional namespace id"); set_nvme_fid(nargv[0]); if (nargc == 2) set_nvme_nsid(nargv[1]); else set_nvme_nsid("1"); } static void parse_ccw_args(char *nargv[], int nargc) { /* * we might be called like this: * chreipl ccw 4711 */ if (l.busid_set) ERR_EXIT("Use either options or positional parameters"); if (nargc == 0) ERR_EXIT("The \"ccw\" re-IPL type requires device"); else if (nargc > 1) ERR_EXIT("Too many arguments specified for \"ccw\" re-IPL " "type"); set_device(nargv[0]); } static void parse_eckd_args(char *nargv[], int nargc) { /* * we might be called like this: * chreipl eckd 4711 */ if (l.busid_set) ERR_EXIT("Use either options or positional parameters"); if (nargc > 1) ERR_EXIT("Too many arguments specified for \"eckd\" re-IPL type"); set_device(nargv[0]); } static void parse_nss_args(char *nargv[], int nargc) { /* * we might be called like this: * chreipl nss lnxnss */ if (l.nss_name_set) ERR_EXIT("Use either options or positional parameters"); if (nargc == 0) ERR_EXIT("A NSS name must be specified"); if (nargc > 1) ERR_EXIT("Too many arguments specified for \"nss\" re-IPL " "type"); set_nss_name(nargv[0]); } static void dev_from_part(char *dev_name) { int i; for (i = strlen(dev_name) - 1; isdigit(dev_name[i]); i--) dev_name[i] = 0; } static void dev_from_part_nvme(char *dev_name) { char *delim = strrchr(dev_name, 'p'); if (delim) *delim = 0; } static int set_reipl_type(const char *dev_name) { if (strncmp(dev_name, "dasd", strlen("dasd")) == 0 || strncmp(dev_name, "vd", strlen("vd")) == 0 || ccw_is_virtio_device(dev_name) == 0) l.reipl_type = REIPL_CCW; else if (strncmp(dev_name, "sd", strlen("sd")) == 0) l.reipl_type = REIPL_FCP; else if (strncmp(dev_name, "nvme", strlen("nvme")) == 0) l.reipl_type = REIPL_NVME; else if (strncmp(dev_name, "eckd", strlen("eckd")) == 0) l.reipl_type = REIPL_ECKD; else return -1; util_strlcpy(l.dev, dev_name, sizeof(l.dev)); if (l.reipl_type == REIPL_NVME) dev_from_part_nvme(l.dev); else dev_from_part(l.dev); l.dev_set = 1; return 0; } static int is_md_device(const char *dev_name) { char abs_dev_name[PATH_MAX]; mdu_array_info_t array; int is_md_device = 0; int fd; if (snprintf(abs_dev_name, PATH_MAX, "/dev/%s", dev_name) >= PATH_MAX) return 0; fd = open(abs_dev_name, O_RDONLY); if (fd == -1) return 0; if (ioctl(fd, GET_ARRAY_INFO, &array) >= 0) is_md_device = 1; close(fd); return is_md_device; } static int get_chreipl_helper_cmd(dev_t dev, char *dev_name, char cmd[PATH_MAX]) { struct proc_dev_entry pde; char *chreipl_helper; char *driver_name; if (proc_dev_get_entry(dev, 1, &pde) != 0) return -1; driver_name = pde.name; if (strcmp(driver_name, UTIL_PROC_DEV_ENTRY_BLKEXT) == 0 && is_md_device(dev_name)) driver_name = UTIL_PROC_DEV_ENTRY_MD; util_asprintf(&chreipl_helper, "%s.%s", util_libdir_path("chreipl_helper"), driver_name); if (access(chreipl_helper, X_OK) != 0) { proc_dev_free_entry(&pde); free(chreipl_helper); return -1; } sprintf(cmd, "%s %d:%d", chreipl_helper, major(dev), minor(dev)); proc_dev_free_entry(&pde); free(chreipl_helper); return 0; } /* * Use chreipl_helper (E.g. for device mapper devices) */ static int set_reipl_type_helper(int maj, int min, char *dev_name) { char helper_cmd[PATH_MAX], buf[4096]; struct proc_part_entry ppe; int rc = -1; dev_t dev; FILE *fh; if (get_chreipl_helper_cmd(makedev(maj, min), dev_name, helper_cmd) != 0) return -1; fh = popen(helper_cmd, "r"); if (fh == NULL) ERR_EXIT_ERRNO("Could not start chreipl_helper"); if (fread(buf, 1, sizeof(buf), fh) == 0) ERR_EXIT_ERRNO("Could not read from chreipl_helper"); if (sscanf(buf, "%d:%d", &maj, &min) != 2) goto fail_pclose; dev = makedev(maj, min); if (proc_part_get_entry(dev, &ppe) != 0) goto fail_pclose; if (set_reipl_type(ppe.name)) goto fail_part_free; rc = 0; fail_part_free: proc_part_free_entry(&ppe); fail_pclose: pclose(fh); return rc; } static void get_dev_by_path(const char *path, dev_t *dev) { struct stat sb; if (stat(path, &sb) != 0) ERR_EXIT_ERRNO("Could not access device node \"%s\"", path); if (S_ISDIR(sb.st_mode)) *dev = sb.st_dev; else if (S_ISBLK(sb.st_mode)) *dev = sb.st_rdev; else ERR_EXIT("Only block device nodes or directories are valid for" " \"node\" target"); } static void parse_node_args(char *nargv[], int nargc) { struct proc_part_entry ppe; char *path = nargv[0]; dev_t dev; if (nargc == 0) ERR_EXIT("No device node specified"); if (l.busid_set || l.wwpn_set || l.lun_set) ERR_EXIT("Do not use device, WWPN, or LUN for \"node\" target"); get_dev_by_path(path, &dev); if (proc_part_get_entry(dev, &ppe) != 0) ERR_EXIT("Invalid device node \"%s\" specified", path); if (set_reipl_type(ppe.name) == 0) goto out; if (set_reipl_type_helper(major(dev), minor(dev), ppe.name) == 0) goto out; ERR_EXIT("Unsupported device node \"%s\" specified", path); out: proc_part_free_entry(&ppe); } static void parse_pos_args(char *nargv[], int nargc) { switch (l.target_type) { case TT_FCP: parse_fcp_args(nargv, nargc); break; case TT_NVME: parse_nvme_args(nargv, nargc); break; case TT_CCW: parse_ccw_args(nargv, nargc); break; case TT_NSS: parse_nss_args(nargv, nargc); break; case TT_NODE: parse_node_args(nargv, nargc); break; case TT_ECKD: parse_eckd_args(nargv, nargc); break; } } static void check_fcp_opts(void) { if (l.nss_name_set || l.brchr_set) ERR_EXIT("Invalid option for \"fcp\" target specified"); if (!(l.busid_set && l.wwpn_set && l.lun_set)) ERR_EXIT("The \"fcp\" target requires device, WWPN, " "and LUN"); } static void check_nvme_opts(void) { if (l.nss_name_set || l.wwpn_set || l.lun_set || l.busid_set || l.brchr_set) ERR_EXIT("Invalid option for \"nvme\" target specified"); if (!(l.fid_set && l.nsid_set)) ERR_EXIT("The \"nvme\" target requires FID, and optional NSID"); } static void check_ccw_opts(void) { if (l.bootprog_set || l.lun_set || l.wwpn_set || l.nss_name_set || l.brchr_set) ERR_EXIT("Invalid option for \"ccw\" target specified"); if (!l.busid_set) ERR_EXIT("The \"ccw\" target requires device"); } static void check_eckd_opts(void) { if (l.nss_name_set || l.wwpn_set || l.lun_set || l.fid_set) ERR_EXIT("Invalid option for \"eckd\" target specified"); if (!(l.busid_set)) ERR_EXIT("The \"eckd\" target requires device"); } static void check_nss_opts(void) { if (l.bootprog_set || l.loadparm_set || l.busid_set || l.wwpn_set || l.lun_set || l.brchr_set) ERR_EXIT("Invalid option for \"nss\" target specified"); if (!l.nss_name_set) ERR_EXIT("The \"nss\" target requires NSS name"); } static void set_target_type(enum target_type tt, int mode_auto) { l.target_type = tt; l.target_type_set = 1; l.target_type_auto_mode = mode_auto; } static void set_target_type_auto(const char *arg) { char busid[10]; if (access(arg, F_OK) == 0) { set_target_type(TT_NODE, 1); return; } if (mk_busid(busid, arg) == 0) { if (ccw_is_device(busid)) set_target_type(TT_CCW, 1); else if (fcp_is_device(busid)) set_target_type(TT_FCP, 1); } } static void set_reipl_clear(const char *arg) { if (arg[0] == '1') l.reipl_clear = 1; else if (arg[0] == '0') l.reipl_clear = 0; else ERR_EXIT("re-IPL clear argument must be either 1 or 0"); } static void set_brchr(const char *arg) { l.brchr = strdup(arg); l.brchr_set = 1; } static void parse_chreipl_options(int argc, char *argv[]) { int opt, idx; const struct option long_opts[] = { { "help", no_argument, NULL, 'h'}, { "bootprog", required_argument, NULL, 'b' }, { "device", required_argument, NULL, 'd' }, { "lun", required_argument, NULL, 'l' }, { "wwpn", required_argument, NULL, 'w' }, { "fid", required_argument, NULL, 'i' }, { "nsid", required_argument, NULL, 's' }, { "loadparm", required_argument, NULL, 'L' }, { "name", required_argument, NULL, 'n' }, { "bootparms", required_argument, NULL, 'p' }, { "force", no_argument, NULL, 'f' }, { "version", no_argument, NULL, 'v' }, { "clear", required_argument, NULL, 'c' }, { "brchr", required_argument, NULL, OPT_BRCHR }, { NULL, 0, NULL, 0 } }; static const char optstr[] = "hd:vw:l:fL:b:n:p:c:i:s:"; /* dont run without any argument */ if (argc == 1) print_usage_chreipl_exit(); if (strcmp(argv[1], "fcp") == 0) set_target_type(TT_FCP, 0); else if (strcmp(argv[1], "ccw") == 0) set_target_type(TT_CCW, 0); else if (strcmp(argv[1], "eckd") == 0) set_target_type(TT_ECKD, 0); else if (strcmp(argv[1], "nss") == 0) set_target_type(TT_NSS, 0); else if (strcmp(argv[1], "nvme") == 0) set_target_type(TT_NVME, 0); else if (strcmp(argv[1], "node") == 0) set_target_type(TT_NODE, 0); else set_target_type_auto(argv[1]); l.reipl_clear = -1; while ((opt = getopt_long(argc, argv, optstr, long_opts, &idx)) != -1) { switch (opt) { case 'h': print_usage_chreipl_exit(); case 'd': set_device(optarg); break; case 'i': set_nvme_fid(optarg); break; case 'l': set_lun(optarg); break; case 's': set_nvme_nsid(optarg); break; case 'w': set_wwpn(optarg); break; case 'L': set_loadparm(optarg); break; case 'b': set_bootprog(optarg); break; case 'n': set_nss_name(optarg); break; case 'p': set_bootparms(optarg); break; case 'f': l.force_set = 1; break; case 'c': set_reipl_clear(optarg); break; case OPT_BRCHR: set_brchr(optarg); break; case 'v': print_version_exit(); default: print_help_hint_exit(); } } if (!is_root()) ERR_EXIT("You must be root to perform this operation"); if (!l.target_type_set) ERR_EXIT("No valid target specified"); /* * optind is a index which points to the first unrecognized * command line argument. In case of no auto action we have to * skip the action argument. */ if (!l.target_type_auto_mode) optind += 1; if (argc - optind > 0) parse_pos_args(&argv[optind], argc - optind); } static void check_exists(const char *path, const char *attr) { char *fpath; fpath = util_path_sysfs("firmware/%s", path); if (access(fpath, F_OK) != 0) ERR_EXIT("System does not allow one to set %s", attr); free(fpath); } static void write_str_optional(char *string, char *file, int exit_on_fail, const char *attr) { if (write_str_errno(string, file) && exit_on_fail) ERR_EXIT("System does not allow one to set %s", attr); } /* * Check if device is on the cio_ignore blacklist * * IMPLEMENTATION: * * "cio_ignore --is-ignored " returns 0 if the device is ignored, * 1 for internal errors, and 2 if the device is not ignored. * * We get the "cio_ignore" exit status by the return code of the system() * function via WEXITSTATUS(). * * If no shell is available or the "cio_ignore" tool is not available * we get system() rc != 0 and WEXITSTATUS() = 127. */ static int is_ignored(const char *busid) { const char *fmt = "cio_ignore --is-ignored %s > /dev/null 2>&1"; char cmd[256]; int rc; snprintf(cmd, sizeof(cmd), fmt, busid); rc = system(cmd); if ((rc != -1) && (WEXITSTATUS(rc) == 0)) return 1; return 0; } static void chreipl_ccw(void) { check_ccw_opts(); if (!ccw_is_device(l.busid) && !l.force_set) { if (is_ignored(l.busid)) ERR_EXIT("Device is on cio_ignore list, try \"cio_ignore -r %s\"?", l.busid); ERR_EXIT("Could not find DASD CCW device \"%s\"", l.busid); } if (l.bootparms_set && strlen(l.bootparms) > BOOTPARMS_CCW_MAX) { ERR_EXIT("Maximum boot parameter length exceeded (%zu/%u)", strlen(l.bootparms), BOOTPARMS_CCW_MAX); } if (l.reipl_clear >= 0) { check_exists("reipl/ccw/clear", "CCW re-IPL clear attribute"); write_str(l.reipl_clear ? "1" : "0", "reipl/ccw/clear"); } /* * On old systems that use CCW reipl loadparm cannot be set */ write_str_optional(l.loadparm, "reipl/ccw/loadparm", l.loadparm_set, "loadparm"); write_str_optional(l.bootparms, "reipl/ccw/parm", l.bootparms_set, "boot parameters"); write_str(l.busid, "reipl/ccw/device"); write_str("ccw", "reipl/reipl_type"); print_ccw(0); } static void chreipl_eckd(void) { check_eckd_opts(); if (!ccw_is_device(l.busid) && !l.force_set) { if (is_ignored(l.busid)) ERR_EXIT("Device is on cio_ignore list, try \"cio_ignore -r %s\"?", l.busid); ERR_EXIT("Could not find DASD ECKD device \"%s\"", l.busid); } if (l.reipl_clear >= 0) { check_exists("reipl/eckd/clear", "ECKD re-IPL clear attribute"); write_str(l.reipl_clear ? "1" : "0", "reipl/eckd/clear"); } if (!l.brchr_set) l.brchr = "auto"; write_str(l.brchr, "reipl/eckd/br_chr"); if (!l.bootprog_set) sprintf(l.bootprog, "0"); write_str(l.bootprog, "reipl/eckd/bootprog"); write_str(l.busid, "reipl/eckd/device"); write_str_optional(l.loadparm, "reipl/eckd/loadparm", l.loadparm_set, "loadparm"); write_str("eckd", "reipl/reipl_type"); print_eckd(0, "eckd"); } static void chreipl_fcp(void) { check_fcp_opts(); if (!fcp_is_device(l.busid) && !l.force_set) { if (is_ignored(l.busid)) ERR_EXIT("Device is on cio_ignore list, try \"cio_ignore -r %s\"?", l.busid); ERR_EXIT("Could not find FCP device \"%s\"", l.busid); } check_exists("reipl/fcp/device", "\"fcp\" re-IPL target"); if (l.bootparms_set && strlen(l.bootparms) > BOOTPARMS_FCP_MAX) { ERR_EXIT("Maximum boot parameter length exceeded (%zu/%u)", strlen(l.bootparms), BOOTPARMS_FCP_MAX); } if (l.reipl_clear >= 0) { check_exists("reipl/fcp/clear", "FCP re-IPL clear attribute"); write_str(l.reipl_clear ? "1" : "0", "reipl/fcp/clear"); } /* * On old systems the FCP reipl loadparm cannot be set */ write_str_optional(l.loadparm, "reipl/fcp/loadparm", l.loadparm_set, "loadparm"); write_str_optional(l.bootparms, "reipl/fcp/scp_data", l.bootparms_set, "boot parameters"); write_str(l.busid, "reipl/fcp/device"); write_str(l.wwpn, "reipl/fcp/wwpn"); write_str(l.lun, "reipl/fcp/lun"); /* * set the boot record logical block address. Master boot * record. It is always 0 for Linux */ write_str("0", "reipl/fcp/br_lba"); if (!l.bootprog_set) sprintf(l.bootprog, "0"); write_str(l.bootprog, "reipl/fcp/bootprog"); write_str("fcp", "reipl/reipl_type"); print_fcp(0, 0); } static void chreipl_nvme(void) { check_nvme_opts(); if (!nvme_is_device(l.fid, l.nsid) && !l.force_set) { ERR_EXIT("Could not find NVME device with fid %s and nsid %s", l.fid, l.nsid); } check_exists("reipl/nvme/fid", "\"nvme\" re-IPL target"); if (l.bootparms_set && strlen(l.bootparms) > BOOTPARMS_FCP_MAX) { ERR_EXIT("Maximum boot parameter length exceeded (%zu/%u)", strlen(l.bootparms), BOOTPARMS_FCP_MAX); } if (l.reipl_clear >= 0) { check_exists("reipl/nvme/clear", "NVME re-IPL clear attribute"); write_str(l.reipl_clear ? "1" : "0", "reipl/nvme/clear"); } write_str_optional(l.loadparm, "reipl/nvme/loadparm", l.loadparm_set, "loadparm"); write_str_optional(l.bootparms, "reipl/nvme/scp_data", l.bootparms_set, "boot parameters"); write_str(l.fid, "reipl/nvme/fid"); write_str(l.nsid, "reipl/nvme/nsid"); /* * set the boot record logical block address. Master boot * record. It is always 0 for Linux */ write_str("0", "reipl/nvme/br_lba"); if (!l.bootprog_set) sprintf(l.bootprog, "0"); write_str(l.bootprog, "reipl/nvme/bootprog"); write_str("nvme", "reipl/reipl_type"); print_nvme(0, 0); } static void chreipl_nss(void) { check_nss_opts(); check_exists("reipl/nss/name", "\"nss\" re-IPL target"); if (l.bootparms_set && strlen(l.bootparms) > BOOTPARMS_NSS_MAX) { ERR_EXIT("Maximum boot parameter length exceeded (%zu/%u)", strlen(l.bootparms), BOOTPARMS_NSS_MAX); } write_str_optional(l.bootparms, "reipl/nss/parm", l.bootparms_set, "boot parameters"); write_str(l.nss_name, "reipl/nss/name"); write_str("nss", "reipl/reipl_type"); print_nss(0); } static void chreipl_node(void) { char *path; if (!l.dev_set) ERR_EXIT("No device node specified"); path = util_path_sysfs("block/%s/device", l.dev); if (chdir(path) != 0) ERR_EXIT("Could not find device \"%s\"", l.dev); free(path); switch (l.reipl_type) { case REIPL_CCW: ccw_busid_get(l.dev, l.busid); l.busid_set = 1; chreipl_ccw(); break; case REIPL_ECKD: ccw_busid_get(l.dev, l.busid); l.busid_set = 1; chreipl_eckd(); break; case REIPL_FCP: fcp_wwpn_get(l.dev, l.wwpn); l.wwpn_set = 1; fcp_lun_get(l.dev, l.lun); l.lun_set = 1; fcp_busid_get(l.dev, l.busid); l.busid_set = 1; chreipl_fcp(); break; case REIPL_NVME: nvme_fid_get(l.dev, l.fid); l.fid_set = 1; nvme_nsid_get(l.dev, l.nsid); l.nsid_set = 1; chreipl_nvme(); break; default: ERR_EXIT("Internal error: chreipl_node"); } } void cmd_chreipl(int argc, char *argv[]) { parse_chreipl_options(argc, argv); switch (l.target_type) { case TT_CCW: chreipl_ccw(); break; case TT_ECKD: chreipl_eckd(); break; case TT_FCP: chreipl_fcp(); break; case TT_NVME: chreipl_nvme(); break; case TT_NSS: chreipl_nss(); break; case TT_NODE: chreipl_node(); break; } } s390-tools-2.38.0/ipl_tools/cmd_chshut.c000066400000000000000000000103241502674226300177400ustar00rootroot00000000000000/* * ipl_tools - Linux for System z reipl and shutdown tools * * Command: chshut * * Copyright IBM Corp. 2008, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include "ipl_tools.h" static const char *const usage_chshut = "Usage: %s TRIGGER ACTION [COMMAND] [OPTIONS]\n" "\n" "Change the shutdown actions for Linux on System z.\n" "\n" "TRIGGER specifies when the action is performed:\n" " halt System has been shut down (e.g. shutdown -h -H now)\n" " poff System has been shut down for power off (e.g. shutdown -h -P now)\n" " reboot System has been shut down for reboot (e.g. shutdown -r)\n" " Note: Depending on the distribution, \"halt\" might be mapped to \"poff\".\n" "\n" "ACTION specifies the action to be performed:\n" " ipl IPL with previous settings\n" " reipl IPL with re-IPL settings (see chreipl)\n" " stop Stop all CPUs\n" " vmcmd Run z/VM CP command defined by COMMAND\n" "\n" "COMMAND defines the z/VM CP command to issue.\n" "\n" "OPTIONS:\n" " -h, --help Print this help, then exit\n" " -v, --version Print version information, then exit\n"; static void __noreturn print_usage_chshut_exit(void) { printf(usage_chshut, g.prog_name); exit(0); } static void parse_chshut_options(int argc, char *argv[]) { int opt, idx; const struct option long_opts[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'v' }, { NULL, 0, NULL, 0 } }; while ((opt = getopt_long(argc, argv, "hv", long_opts, &idx)) != -1) { switch (opt) { case 'h': print_usage_chshut_exit(); case 'v': print_version_exit(); default: print_help_hint_exit(); } } if (!is_root()) ERR_EXIT("You must be root to perform this operation"); } static struct shutdown_trigger *shutdown_trigger_get(const char *trigger) { int i; for (i = 0; shutdown_trigger_vec[i]; i++) { if (strcmp(trigger, shutdown_trigger_vec[i]->name) != 0) continue; if (shutdown_trigger_vec[i]->available) return shutdown_trigger_vec[i]; ERR_EXIT("Shutdown trigger \"%s\" is not available on " "your system", trigger); } ERR_EXIT("Unknown shutdown trigger \"%s\" specified", trigger); } static struct shutdown_action *shutdown_action_get(const char *action) { int i; for (i = 0; shutdown_action_vec[i]; i++) { if (strcmp(action, shutdown_action_vec[i]->name) == 0) return shutdown_action_vec[i]; } ERR_EXIT("Unknown shutdown action \"%s\" specified", action); } /* * Multiple CP commands can be specified via "vmcmd XY1 vmcmd XY2 ..." */ static void vmcmd_set(struct shutdown_trigger *st, int argc, char *argv[]) { char vmcmd[1024], path[PATH_MAX]; int first = 1, i; int vmcmd_length = 0; if (is_lpar()) ERR_EXIT("vmcmd works only under z/VM"); memset(vmcmd, 0, sizeof(vmcmd)); for (i = 2; i < argc; i++) { if (strcmp(argv[i], "vmcmd") != 0) ERR_EXIT("Invalid vmcmd command specification"); if (i == argc - 1) ERR_EXIT("vmcmd needs an additional argument"); if (!first) { strcat(vmcmd, "\n"); vmcmd_length++; } else { first = 0; } vmcmd_length += strlen(argv[i + 1]); if (vmcmd_length >= 127) ERR_EXIT("The vmcmd command must not exceed 127 " "characters"); strcat(vmcmd, argv[i + 1]); i++; } sprintf(path, "vmcmd/%s", st->name_sysfs); write_str(vmcmd, path); } void cmd_chshut(int argc, char *argv[]) { struct shutdown_trigger *st; struct shutdown_action *sa; char path[PATH_MAX]; parse_chshut_options(argc, argv); if (argc < 2) { ERR("No trigger specified"); print_help_hint_exit(); } shutdown_init(); st = shutdown_trigger_get(argv[1]); if (st == &shutdown_trigger_panic || st == &shutdown_trigger_restart) ERR_EXIT("Please use \"service dumpconf\" for " "configuring the %s trigger", st->name); if (argc < 3) { ERR("No action specified"); print_help_hint_exit(); } sa = shutdown_action_get(argv[2]); if (sa == &shutdown_action_vmcmd) { vmcmd_set(st, argc, argv); } else if (argc != 3) { ERR("Too many parameters specified"); print_help_hint_exit(); } sprintf(path, "shutdown_actions/%s", st->name_sysfs); if (write_str_errno(argv[2], path)) ERR_EXIT_ERRNO("Could not set \"%s\"", path); } s390-tools-2.38.0/ipl_tools/cmd_lsreipl.c000066400000000000000000000162631502674226300201240ustar00rootroot00000000000000/* * ipl_tools - Linux for System z reipl and shutdown tools * * Command: lsreipl * * Copyright IBM Corp. 2008, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include "lib/util_path.h" #include "lib/util_file.h" #include "lib/util_libc.h" #include "ipl_tools.h" static struct { int ipl_set; /* --ipl has been specified */ } l; static const char *const usage_lsreipl = "Usage: %s [OPTIONS]\n" "\n" "Show re-IPL or IPL settings.\n" "\n" "OPTIONS:\n" " -i, --ipl Print the IPL setting\n" " -h, --help Print this help, then exit\n" " -v, --version Print version information, then exit\n"; static void __noreturn print_usage_lsreipl_exit(void) { printf(usage_lsreipl, g.prog_name); exit(0); } static const char *get_ipl_banner(int show_ipl) { if (show_ipl) return "IPL type:"; else return "Re-IPL type:"; } void print_nss(int show_ipl) { char *dir = show_ipl ? "ipl" : "reipl/nss"; char *path_bootparms = util_path_sysfs("firmware/%s/parm", dir); printf("%-12s nss\n", get_ipl_banner(show_ipl)); print_fw_str("Name: %s\n", dir, "name"); if (access(path_bootparms, R_OK) == 0) print_fw_str("Bootparms: \"%s\"\n", dir, "parm"); free(path_bootparms); } void print_fcp(int show_ipl, int dump) { char *dir = show_ipl ? "ipl" : "reipl/fcp"; char *path_bootparms = util_path_sysfs("firmware/%s/scp_data", dir); char *path_loadparm = util_path_sysfs("firmware/%s/loadparm", dir); char *path_reipl_clear = util_path_sysfs("firmware/reipl/fcp/clear"); char *path_secure_boot = util_path_sysfs("firmware/ipl/secure"); char *loadparm; if (dump) printf("%-12s fcp_dump\n", get_ipl_banner(show_ipl)); else printf("%-12s fcp\n", get_ipl_banner(show_ipl)); print_fw_str("WWPN: %s\n", dir, "wwpn"); print_fw_str("LUN: %s\n", dir, "lun"); print_fw_str("Device: %s\n", dir, "device"); print_fw_str("bootprog: %s\n", dir, "bootprog"); print_fw_str("br_lba: %s\n", dir, "br_lba"); if (access(path_loadparm, R_OK) == 0) { loadparm = util_file_read_text_file(path_loadparm, 1); util_strstrip(loadparm); printf("Loadparm: \"%s\"\n", loadparm); free(loadparm); } if (access(path_bootparms, R_OK) == 0) print_fw_str("Bootparms: \"%s\"\n", dir, "scp_data"); if (!show_ipl && access(path_reipl_clear, R_OK) == 0) print_fw_str("clear: %s\n", dir, "clear"); if (access(path_secure_boot, R_OK) == 0) print_fw_str("Secure boot: %s\n", "ipl", "secure"); free(path_bootparms); free(path_loadparm); free(path_reipl_clear); free(path_secure_boot); } void print_nvme(int show_ipl, int dump) { char *dir = show_ipl ? "ipl" : "reipl/nvme"; char *path_bootparms = util_path_sysfs("firmware/%s/scp_data", dir); char *path_loadparm = util_path_sysfs("firmware/%s/loadparm", dir); char *path_reipl_clear = util_path_sysfs("firmware/reipl/nvme/clear"); char *path_secure_boot = util_path_sysfs("firmware/ipl/secure"); char *loadparm; if (dump) printf("%-12s nvme_dump\n", get_ipl_banner(show_ipl)); else printf("%-12s nvme\n", get_ipl_banner(show_ipl)); print_fw_str("FID: %s\n", dir, "fid"); print_fw_str("NSID: %s\n", dir, "nsid"); print_fw_str("bootprog: %s\n", dir, "bootprog"); print_fw_str("br_lba: %s\n", dir, "br_lba"); if (access(path_loadparm, R_OK) == 0) { loadparm = util_file_read_text_file(path_loadparm, 1); util_strstrip(loadparm); printf("Loadparm: \"%s\"\n", loadparm); free(loadparm); } if (access(path_bootparms, R_OK) == 0) print_fw_str("Bootparms: \"%s\"\n", dir, "scp_data"); if (!show_ipl && access(path_reipl_clear, R_OK) == 0) print_fw_str("clear: %s\n", dir, "clear"); if (access(path_secure_boot, R_OK) == 0) print_fw_str("Secure boot: %s\n", "ipl", "secure"); free(path_bootparms); free(path_loadparm); free(path_reipl_clear); free(path_secure_boot); } void print_ccw(int show_ipl) { char *dir = show_ipl ? "ipl" : "reipl/ccw"; char *path_loadparm = util_path_sysfs("firmware/%s/loadparm", dir); char *path_bootparms = util_path_sysfs("firmware/%s/parm", dir); char *path_reipl_clear = util_path_sysfs("firmware/reipl/ccw/clear"); char *loadparm; printf("%-12s ccw\n", get_ipl_banner(show_ipl)); print_fw_str("Device: %s\n", dir, "device"); if (access(path_loadparm, R_OK) == 0) { loadparm = util_file_read_text_file(path_loadparm, 1); util_strstrip(loadparm); printf("Loadparm: \"%s\"\n", loadparm); free(loadparm); } if (access(path_bootparms, R_OK) == 0) print_fw_str("Bootparms: \"%s\"\n", dir, "parm"); if (!show_ipl && access(path_reipl_clear, R_OK) == 0) print_fw_str("clear: %s\n", dir, "clear"); free(path_loadparm); free(path_bootparms); free(path_reipl_clear); } void print_eckd(int show_ipl, const char *name) { char *dir = show_ipl ? "ipl" : "reipl/eckd"; char *path_loadparm = util_path_sysfs("firmware/%s/loadparm", dir); char *path_secure_boot = util_path_sysfs("firmware/ipl/secure"); char *loadparm; printf("%-12s %s\n", get_ipl_banner(show_ipl), name); print_fw_str("Device: %s\n", dir, "device"); print_fw_str("bootprog: %s\n", dir, "bootprog"); print_fw_str("br_chr: %s\n", dir, "br_chr"); print_fw_str("Bootparm: \"%s\"\n", dir, "scp_data"); if (access(path_loadparm, R_OK) == 0) { loadparm = util_file_read_text_file(path_loadparm, 1); util_strstrip(loadparm); printf("Loadparm: \"%s\"\n", loadparm); free(loadparm); } if (!show_ipl) print_fw_str("clear: %s\n", dir, "clear"); if (access(path_secure_boot, R_OK) == 0) print_fw_str("Secure boot: %s\n", "ipl", "secure"); free(path_loadparm); free(path_secure_boot); } static void parse_lsreipl_options(int argc, char *argv[]) { int opt, idx; const struct option long_opts[] = { { "help", no_argument, NULL, 'h' }, { "ipl", no_argument, NULL, 'i' }, { "version", no_argument, NULL, 'v' }, { NULL, 0, NULL, 0 } }; while ((opt = getopt_long(argc, argv, "hvi", long_opts, &idx)) != -1) { switch (opt) { case 'i': l.ipl_set = 1; break; case 'h': print_usage_lsreipl_exit(); case 'v': print_version_exit(); default: print_help_hint_exit(); } } /* don't run with too many arguments */ if (optind != argc) ERR_EXIT("Invalid positional parameter \"%s\" specified", argv[optind]); } void cmd_lsreipl(int argc, char *argv[]) { char *reipl_type_str; parse_lsreipl_options(argc, argv); if (l.ipl_set) reipl_type_str = read_fw_str("ipl/ipl_type"); else reipl_type_str = read_fw_str("reipl/reipl_type"); if (strcmp(reipl_type_str, "fcp") == 0) print_fcp(l.ipl_set, 0); else if (strcmp(reipl_type_str, "fcp_dump") == 0) print_fcp(l.ipl_set, 1); else if (strcmp(reipl_type_str, "nvme") == 0) print_nvme(l.ipl_set, 0); else if (strcmp(reipl_type_str, "nvme_dump") == 0) print_nvme(l.ipl_set, 1); else if (strcmp(reipl_type_str, "ccw") == 0) print_ccw(l.ipl_set); else if (strcmp(reipl_type_str, "eckd") == 0 || strcmp(reipl_type_str, "eckd_dump") == 0) print_eckd(l.ipl_set, reipl_type_str); else if (strcmp(reipl_type_str, "nss") == 0) print_nss(l.ipl_set); else printf("%s: %s (unknown)\n", get_ipl_banner(l.ipl_set), reipl_type_str); free(reipl_type_str); exit(0); } s390-tools-2.38.0/ipl_tools/cmd_lsshut.c000066400000000000000000000061121502674226300177640ustar00rootroot00000000000000/* * ipl_tools - Linux for System z reipl and shutdown tools * * Command: lsshut * * Copyright IBM Corp. 2008, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include "lib/util_path.h" #include "lib/util_file.h" #include "ipl_tools.h" static const char *const usage_lsshut = "Usage: %s [OPTIONS]\n" "\n" "Print the shutdown action configuration for Linux on System z.\n" "\n" "OPTIONS:\n" " -h, --help Print this help, then exit\n" " -v, --version Print version information, then exit\n"; static __noreturn void print_usage_lsshut_exit(void) { printf(usage_lsshut, g.prog_name); exit(0); } static void parse_lsshut_options(int argc, char *argv[]) { int opt, idx; const struct option long_opts[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'v' }, { NULL, 0, NULL, 0 } }; while ((opt = getopt_long(argc, argv, "hv", long_opts, &idx)) != -1) { switch (opt) { case 'h': print_usage_lsshut_exit(); case 'v': print_version_exit(); default: print_help_hint_exit(); } } /* don't run with too many arguments */ if (optind != argc) ERR_EXIT("Invalid positional parameter \"%s\" specified", argv[optind]); } /* * VMCMDs can have up to 128 characters. Newlines mark the end of a CP command. * Therefore we can have up to 64 single CP commands (with one character). * With quotes (2) and commas (1) we can have at most 4 * 64 = 256 characters * for the output string. */ static void read_vmcmd(char *str, const char *path) { char *ptr_old, *ptr; char tmp[512]; char *buf; *str = 0; buf = read_fw_str(path); ptr_old = ptr = buf; while ((ptr = strchr(ptr_old, '\n'))) { *ptr = 0; sprintf(tmp, "\"%s\",", ptr_old); strcat(str, tmp); ptr_old = ptr + 1; } sprintf(tmp, "\"%s\"", ptr_old); strcat(str, tmp); free(buf); } static void print_kdump(void) { struct stat sb; char *path; char *tmp; path = util_path_sysfs("kernel/kexec_crash_loaded"); if (stat(path, &sb) != 0) { free(path); return; } tmp = util_file_read_text_file(path, 1); if (strncmp(tmp, "1", 1) == 0) printf("kdump,"); free(path); free(tmp); } static void shutdown_trigger_print(struct shutdown_trigger *trigger) { char cmd[1024], path[PATH_MAX]; char *tmp; sprintf(path, "shutdown_actions/%s", trigger->name_sysfs); printf("%-16s ", trigger->name_print); if ((trigger == &shutdown_trigger_panic || trigger == &shutdown_trigger_restart)) print_kdump(); tmp = read_fw_str(path); if (strncmp(tmp, "vmcmd", strlen("vmcmd")) == 0) { sprintf(path, "vmcmd/%s", trigger->name_sysfs); read_vmcmd(cmd, path); printf("vmcmd (%s)\n", cmd); } else { printf("%s\n", tmp); } free(tmp); } void cmd_lsshut(int argc, char *argv[]) { int i; parse_lsshut_options(argc, argv); shutdown_init(); printf("Trigger Action\n"); printf("========================\n"); for (i = 0; shutdown_trigger_vec[i]; i++) { if (!shutdown_trigger_vec[i]->available) continue; shutdown_trigger_print(shutdown_trigger_vec[i]); } } s390-tools-2.38.0/ipl_tools/fcp.c000066400000000000000000000037151502674226300163750ustar00rootroot00000000000000/* * ipl_tools - Linux for System z reipl and shutdown tools * * FCP device functions * * Copyright IBM Corp. 2008, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include "lib/util_libc.h" #include "lib/util_path.h" #include "ipl_tools.h" /* * Check if the specified device number is a valid device number * which can be found in the /sys/bus/ccw/drivers/zfcp/ structure */ int fcp_is_device(const char *devno) { char *path; path = util_path_sysfs("bus/ccw/drivers/zfcp/%s", devno); if (chdir(path) != 0) { free(path); return 0; } free(path); return 1; } /* * Return the wwpn of a device */ void fcp_wwpn_get(const char *device, char *wwpn) { char buf[20]; char *path; FILE *fh; int rc; path = util_path_sysfs("block/%s/device/wwpn", device); fh = fopen(path, "r"); if (fh == NULL) ERR_EXIT_ERRNO("Could not open \"%s\"", path); rc = fscanf(fh, "%s", buf); if (rc <= 0) ERR_EXIT("Could not lookup WWPN \"%s\"", path); util_strlcpy(wwpn, buf, 20); fclose(fh); free(path); } /* * Return the lun of a device */ void fcp_lun_get(const char *device, char *lun) { char buf[20]; char *path; FILE *fh; int rc; path = util_path_sysfs("block/%s/device/fcp_lun", device); fh = fopen(path, "r"); if (fh == NULL) ERR_EXIT_ERRNO("Could not open \"%s\"", path); rc = fscanf(fh, "%s", buf); if (rc <= 0) ERR_EXIT("Could not lookup LUN \"%s\"", path); util_strlcpy(lun, buf, 20); fclose(fh); free(path); } /* * Return the device number of a device */ void fcp_busid_get(const char *device, char *devno) { char buf[4096]; char *path; FILE *fh; int rc; path = util_path_sysfs("block/%s/device/hba_id", device); fh = fopen(path, "r"); if (fh == NULL) ERR_EXIT_ERRNO("Could not open \"%s\"", path); rc = fscanf(fh, "%s", buf); if (rc <= 0) ERR_EXIT("Could not find device \"%s\"", path); strcpy(devno, buf); fclose(fh); free(path); } s390-tools-2.38.0/ipl_tools/ipl_tools.h000066400000000000000000000060661502674226300176400ustar00rootroot00000000000000/* * ipl_tools - Linux for System z reipl tools (shutdown actions) * * Common macro definitions and declarations * * Copyright IBM Corp. 2008, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef IPL_TOOLS_H #define IPL_TOOLS_H #include #include #include #include #include #include #include #include #include #include #include "lib/zt_common.h" #define IPL_TYPE_LEN_MAX 100 #define NSS_NAME_LEN_MAX 8 extern struct globals { char prog_name[256]; /* Program name */ } g; /* * Commands */ void cmd_lsshut(int argc, char *argv[]); void cmd_chshut(int argc, char *argv[]); void cmd_lsreipl(int argc, char *argv[]); void cmd_chreipl(int argc, char *argv[]); void print_ccw(int show_ipl); void print_fcp(int show_ipl, int dump); void print_nvme(int show_ipl, int dump); void print_nss(int show_ipl); void print_eckd(int show_ipl, const char *name); /* * Helper */ int is_lpar(void); int is_root(void); void write_str(char *string, char *file); int write_str_errno(char *string, char *file); char *read_fw_str(const char *file); void print_fw_str(const char *fmt, const char *dir, const char *file); void __noreturn print_version_exit(void); void __noreturn print_help_hint_exit(void); /* * FCP */ int fcp_is_device(const char *devno); void fcp_lun_get(const char *device, char *lun); void fcp_wwpn_get(const char *device, char *wwpn); void fcp_busid_get(const char *device, char *devno); /* * NVME */ #define FID_MAX_LEN 11 /* 8 characters + 0x + null */ #define NVME_DEV_MAX_LEN 15 /* "nvme" + u32 in decimal + null */ #define NVME_PATH_MAX (PATH_MAX + NAME_MAX + 1) void nvme_fid_get(const char *device, char *fid); void nvme_nsid_get(const char *device, char *nsid); int nvme_is_device(char *fid_str, char *nsid_str); /* * CCW */ int ccw_is_device(const char *devno); int ccw_is_virtio_device(const char *device); void ccw_busid_get(const char *device, char *devno); /* * Shutdown trigger */ struct shutdown_trigger { const char *name; const char *name_print; const char *name_sysfs; int available; }; extern struct shutdown_trigger shutdown_trigger_panic; extern struct shutdown_trigger shutdown_trigger_restart; extern struct shutdown_trigger *shutdown_trigger_vec[]; extern void shutdown_init(void); /* * Shutdown actions */ struct shutdown_action { const char *name; }; extern struct shutdown_action shutdown_action_vmcmd; extern struct shutdown_action *shutdown_action_vec[]; /* * Error and print functions */ #define ERR(x...) \ do { \ fprintf(stderr, "%s: ", g.prog_name); \ fprintf(stderr, x); \ fprintf(stderr, "\n"); \ } while (0) #define ERR_EXIT(x...) \ do { \ ERR(x); \ exit(1); \ } while (0) #define ERR_EXIT_ERRNO(x...) \ do { \ fflush(stdout); \ fprintf(stderr, "%s: ", g.prog_name); \ fprintf(stderr, x); \ fprintf(stderr, " (%s)", strerror(errno)); \ fprintf(stderr, "\n"); \ exit(1); \ } while (0) #endif /* IPL_TOOLS_H */ s390-tools-2.38.0/ipl_tools/main.c000066400000000000000000000022431502674226300165440ustar00rootroot00000000000000/* * ipl_tools - Linux for System z reipl and shutdown tools * * Main functions * * Copyright IBM Corp. 2008, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include "lib/util_libc.h" #include "lib/zt_common.h" #include "ipl_tools.h" struct globals g; void __noreturn print_help_hint_exit(void) { fprintf(stderr, "Try '%s' --help' for more information.\n", g.prog_name); exit(1); } void __noreturn print_version_exit(void) { printf("%s: Linux on System z shutdown actions version %s\n", g.prog_name, RELEASE_STRING); printf("Copyright IBM Corp. 2008, 2017\n"); exit(0); } int main(int argc, char *argv[]) { util_strlcpy(g.prog_name, argv[0], sizeof(g.prog_name)); if (strstr(argv[0], "chreipl") != NULL) { cmd_chreipl(argc, argv); return 0; } if (strstr(argv[0], "chshut") != NULL) { cmd_chshut(argc, argv); return 0; } if (strstr(argv[0], "lsreipl") != NULL) { cmd_lsreipl(argc, argv); return 0; } if (strstr(argv[0], "lsshut") != NULL) { cmd_lsshut(argc, argv); return 0; } ERR_EXIT("Invalid program name \"%s\"", argv[0]); return 1; } s390-tools-2.38.0/ipl_tools/man/000077500000000000000000000000001502674226300162265ustar00rootroot00000000000000s390-tools-2.38.0/ipl_tools/man/chreipl.8000066400000000000000000000276651502674226300177650ustar00rootroot00000000000000.\" Copyright 2017 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH CHREIPL 8 "July 2010" "s390-tools" .SH NAME chreipl \- change the re-IPL configuration for Linux on System z .SH SYNOPSIS \fBchreipl\fP [TARGET] [OPTIONS] .SH DESCRIPTION Use the \fBchreipl\fP tool to modify the re-IPL configuration for Linux on System z. You can configure a certain boot device and, for zipl boot menu configurations, the boot menu entry that will be used for the next reboot. Also kernel parameters for the next Linux kernel can be defined. Initial program load (IPL) is the mainframe synonym for what is called "boot" under Linux. Accordingly re-IPL can be translated to "reboot" in the non-mainframe context. Normally for reboot the last IPL device is used to restart the system. To reboot from another IPL device, you first have to change the re-IPL settings with \fBchreipl\fP and then run the .BR reboot (8) command. All settings made with \fBchreipl\fP are preserved over reboots until they are changed again. .SH TARGET The first argument specifies the re-IPL target: .RS 3 .TP 8 .RB "- " ccw : Specify a DASD CCW device for reboot .TP .RB "- " eckd : Specify a DASD ECKD device for reboot .TP .RB "- " fcp : Specify a FCP device for reboot .TP .RB "- " nvme : Specify an NVMe device for reboot .TP .RB "- " nss : Specify a named saved system (NSS) for reboot .TP .RB "- " node : Specify a device for reboot using a device node or directory .RE .PP If the target specification is non-ambiguous it can be omitted. See section \fBAUTOTARGET\fP for more information. Ambiguous setups are very rare, e.g. the name of a device node theoretically could be the same as a bus-ID. .SH OPTIONS .TP .BR "\-f" " or " "\-\-force" With this option, you can force the re-IPL from a target device even if the target cannot be verified by the system. This is the case, for example, if the device is on the cio_ignore blacklist. .B Note: Use this option with great care. You can specify non-existing devices, which will cause the re-IPL to fail. .TP .BR "\-p" " or " "\-\-bootparms" Specifies boot parameters for the next reboot. The boot parameters which are typically kernel parameters are appended to the kernel parameter line. If you specify the boot parameters with a leading equal sign (=), the boot parameters replace all parameters on the kernel parameter line. To remove boot parameters, specify an empty string for this option. Depending on the chreipl target a different maximum number of characters is allowed for boot parameters. Under LPAR it is not possible to specify boot parameters for the ccw target. .B Note: When replacing all parameters, you might inadvertently omit parameters that the boot configuration requires. Read .B /proc/cmdline to find out with which parameters a running Linux instance has been started. .TP .BR "\-h" " or " "\-\-help" Print help information, then exit. .TP .BR "\-v" " or " "\-\-version" Print version information, then exit. .PP All other options are specific to the re-IPL target and are discussed below in the respective section. For a more detailed description of the Linux on System z IPL mechanisms see .BR zipl.conf (6). The mandatory options (e.g. device, wwpn, lun, etc.) can also be specified as positional parameters. .SH AUTOTARGET For the ccw, fcp, and node targets \fBchreipl\fP can find automatically the correct re-IPL target. To do this, omit the re-IPL target parameter and start specifying the required positional parameters. .PP \fBExamples:\fP .br 1. Next time reboot from the DASD device /dev/dasda using the first boot configuration: .br \fB# chreipl /dev/dasda -L 1\fP .br 2. Next time reboot from the CCW device with the bus-ID 0.0.7e78 and empty loadparm: \fB# chreipl 0.0.7e78 \fP 3. Next time reboot from the SCSI disk with FCP bus-ID 0.0.1700, WWPN 0x500507630300c562, and LUN 0x401040b300000000. In addition to that append kernel parameter "mem=" to restrict memory to 512 MB: \fB# chreipl 0.0.1700 0x500507630300c562 0x401040b300000000 -p "mem=512M"\fP 4. Next time reboot from the NVMe device with function id 0x13, namespace 1: \fB# chreipl nvme 0x13 1 .SH ccw Use the ccw re-IPL target for DASD devices that are accessed by the hardware using channel command word (CCW) channels. .TP .BR "\-d" " or " "\-\-device" Specifies the CCW bus-ID. If the bus-ID starts with "0.0." this prefix can be omitted and the four digit short notation can be used (e.g. 5000 is the same as 0.0.5000). .TP .BR "\-L" " or " "\-\-loadparm" Specifies an entry in the .BR zipl (8) boot menu, and an active namespace in the installed zIPL environment block for evaluation of zIPL environment variables in the kernel command line. This option requires an argument, which has to be a string of the form "nSX", where optional number 'n' indicates the boot menu entry, and optional token 'SX' specifies the active namespace. This token consists of a mandatory leading capital 'S' and of an optional trailing character 'X', which is either any decimal digit, indicating ID of the active site, or a capital 'S'. In the last case, ID of the active namespace is determined as the SSID (Subchannel Set ID). If the menu entry is omitted in the argument, then the default menu entry is used. If the 'X' is omitted, or the whole token 'SX' is omitted, then the common namespace of the installed zIPL environment block will be activated. .TP .BR "\-c" " or " "\-\-clear" Specify whether memory should be cleared on re-IPL. Possible values are 0 to disable and 1 to enable memory clearing on re-IPL. Memory clearing is supported if the "clear" attribute is present in /sys/firmware/reipl/ccw/. .PP \fBExamples:\fP .br 1. Next time reboot from the CCW device with the bus-ID 0.0.7e78 and empty loadparm: \fB# chreipl ccw 0.0.7e78\fP 2. Next time reboot from the CCW device with the bus-ID 0.0.7e78 using the first entry of the zipl boot menu and the second site namespace in the installed zIPL environment block for evaluation of zIPL environment variables in the kernel command line: \fB# chreipl ccw -d 0.0.7e78 -L 1S2\fP .SH eckd Use the eckd re-IPL target for DASD devices that are accessed by the hardware using channel command word (CCW) channels. In contrast to the ccw re-IPL target, the eckd target uses a list-directed bootloader that supports secure boot. .TP .BR "\-d" " or " "\-\-device" Specifies the device bus-ID of the DASD. If the bus-ID starts with "0.0." this prefix can be omitted and the four digit short notation can be used (e.g. 5000 is the same as 0.0.5000). .TP .BR "\-b" " or " "\-\-bootprog" Specifies an entry in the ECKD boot configuration by defining the IPL boot program selector. If omitted, '0' is used. .TP .BR "\-\-brchr" Specifies the block on the DASD where the boot record is located. If omitted, 0 is used. The specification comprises comma-separated values for cylinder, head, and record: "--brchr C,H,R". Alternatively, specifying "0" or "auto" makes the bootloader determine the boot record location from the volume label. .TP .BR "\-L" " or " "\-\-loadparm" The loadparm for the eckd re-IPL target is not used to control the ECKD boot configuration that is defined by the .BR zipl (8) boot menu. Instead it can be used to control higher level boot loaders like GRUB. For more details refer to distribution specific documentation. .TP .BR "\-c" " or " "\-\-clear" Controls whether memory is cleared on re-IPL. Possible values are 0 to disable and 1 to enable memory clearing on re-IPL. Memory clearing is supported if the "clear" attribute is present in /sys/firmware/reipl/eckd/. .PP \fBExamples:\fP .br 1. Next time reboot from the ECKD device with the bus-ID 0.0.7e78 and boot program selector 0: \fB# chreipl eckd 0.0.7e78\fP 2. Next time reboot from the ECKD device with the bus-ID 0.0.7e78 and boot program selector 2: \fB# chreipl eckd -d 0.0.7e78 -b 2\fP 3. Next time reboot from the ECKD device with the bus-ID 0.0.7e78 and tell the bootloader that the bootrecord is located in Cylinder 3, Head 0, Record 1: \fB# chreipl eckd -d 0.0.7e78 --brchr 3,0,1\fP .SH fcp Use the fcp re-IPL target for SCSI disks that are accessed by the hardware using Fibre Channel Protocol (FCP) channels. .TP .BR "\-d" " or " "\-\-device" Specifies the bus-ID of the FCP adapter that should be used to access the SCSI re-IPL device. If the bus-ID starts with "0.0." this prefix can be omitted and the four digit short notation can be used (e.g. 5000 is the same as 0.0.5000). .TP .BR "\-w" " or " "\-\-wwpn" Specifies the world wide port name (WWPN) for the FCP attached SCSI disk. .TP .BR "\-l" " or " "\-\-lun" Specifies the logical unit number (LUN) for the FCP attached SCSI disk. .TP .BR "\-b" " or " "\-\-bootprog" Specifies an entry in the FCP boot configuration by defining the IPL boot program selector. If omitted, '0' will be used. .TP .BR "\-L" " or " "\-\-loadparm" The loadparm for the fcp re-IPL target is not used to control the FCP boot configuration that is defined by the .BR zipl (8) boot menu. Instead it can be used to control higher level boot loaders like GRUB. For more details refer to distribution specific documentation. .TP .BR "\-c" " or " "\-\-clear" Specify whether memory should be cleared on re-IPL. Possible values are 0 to disable and 1 to enable memory clearing on re-IPL. Memory clearing is supported if the "clear" attribute is present in /sys/firmware/reipl/fcp/. .PP \fBExamples:\fP .br 1. Next time reboot from the SCSI disk with FCP bus-ID 0.0.1700, WWPN 0x500507630300c562, LUN 0x401040b300000000, and boot program selector 0: .br \fB# chreipl fcp 0.0.1700 0x500507630300c562 0x401040b300000000\fP .br 2. Use same configuration as (1) but choose boot program selector 2 and use options instead of positional parameters: .br \fB# chreipl fcp -d 0.0.1700 -w 0x5005076... -l 0x401040b3... -b 2\fP .SH nvme Use the nvme re-IPL target for specifying an NVMe disk for reboot. .TP .BR "\-i" " or " "\-\-fid" PCI Function ID of NVME IPL device (hex). .TP .BR "\-s" " or " "\-\-nsid" Namespace ID of the NVME IPL device (decimal, default 1). .TP .BR "\-b" " or " "\-\-bootprog" Specifies an entry in the boot configuration by defining the IPL boot program selector. If omitted, '0' will be used. .TP .BR "\-L" " or " "\-\-loadparm" The loadparm for the nvme re-IPL target is not used to control the boot configuration that is defined by the .BR zipl (8) boot menu. Instead it can be used to control higher level boot loaders like GRUB. For more details refer to distribution specific documentation. .TP .BR "\-c" " or " "\-\-clear" Specify whether memory should be cleared on re-IPL. Possible values are 0 to disable and 1 to enable memory clearing on re-IPL. Memory clearing is supported if the "clear" attribute is present in /sys/firmware/reipl/nvme/. .PP \fBExamples:\fP .br 1. Next time reboot from the NVMe disk with function-id 0x13 and namespace 1: .br \fB# chreipl nvme 0x13 1\fP .br 2. Use same configuration as (1) but choose boot program selector 2 and use options instead of positional parameters: .br \fB# chreipl nvme -i 0x13 -s 1 -b 2\fP .SH nss Use the nss re-IPL target to specify z/VM named saved systems (NSS) for reboot. .TP .BR "\-n" " or " "\-\-name" Specifies the name of the NSS. .PP \fBExamples:\fP .br Use the NSS named LINUX1 for the next reboot: \fB# chreipl nss LINUX1\fP .SH node You can identify DASD, SCSI, or NVMe re-IPL devices indirectly through a device node or directory. The chreipl tool then determines the information that you would otherwise have to specify with the ccw or fcp target. .PP \fBExamples:\fP .br 1. Next time reboot from the DASD device /dev/dasda: .br \fB# chreipl node /dev/dasda\fP .br 2. Next time reboot from the SCSI disk /dev/sda: .br \fB# chreipl node /dev/sda\fP 3. Next time reboot from the device where directory /mnt/boot is located: .br \fB# chreipl node /mnt/boot\fP 4. Next time reboot from the NVMe device represented by /dev/nvme0n1 .br \fB# chreipl node /dev/nvme0n1\fP .SH SEE ALSO .BR lsreipl (8), .BR zipl (8), .BR zipl.conf (5), .BR zipl-editenv (8) .BR reboot (8) s390-tools-2.38.0/ipl_tools/man/chshut.8000066400000000000000000000035361502674226300176240ustar00rootroot00000000000000.\" Copyright 2017 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH CHSHUT 8 "Sept 2011" "s390-tools" .SH NAME chshut \- change the shutdown actions for Linux on System z .SH SYNOPSIS \fBchshut\fR TRIGGER ACTION [VM Command] .SH DESCRIPTION Use \fBchshut\fR to configure the Linux on System z shutdown actions. The tool handles up to three parameters. The first parameter specifies the shutdown trigger. A shutdown trigger is an event that will stop Linux. The following shutdown triggers are supported: "halt", "poff", and "reboot". There are two other shutdown triggers "panic" and "restart" that are controlled by the .BR dumpconf (8) service script. The second parameter specifies the shutdown action that you want to run if the specified trigger occurs. Valid action arguments are "ipl", "reipl", "stop", and "vmcmd". If you have chosen "vmcmd" as action, a third parameter is required for the CP command you want to execute under z/VM. .B Note: VM CP commands, device addresses, and guest names must be uppercase. .SH OPTIONS .TP \fB-h\fR or \fB--help\fR Print help information, then exit. .TP \fB-v\fR or \fB--version\fR Print version information, then exit. .SH EXAMPLES Log off the z/VM guest if the Linux .BR poweroff (8) command was executed successfully: \fB# chshut poff vmcmd LOGOFF\fR If the system is halted it should start again: \fB# chshut halt ipl\fR If the Linux .BR poweroff (8) command is executed, send a message to guest MASTER and automatically log off the guest: \fB# chshut poff vmcmd "MSG MASTER Going down" vmcmd "LOGOFF"\fR .SH NOTES For most Linux distributions, "halt" is mapped by default to "power off". In this case you have to use the shutdown trigger "poff" instead of "halt". .SH SEE ALSO .BR dumpconf (8) .BR lsshut (8) s390-tools-2.38.0/ipl_tools/man/lsreipl.8000066400000000000000000000021521502674226300177710ustar00rootroot00000000000000.\" Copyright 2017 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH LSREIPL 8 "July 2010" "s390-tools" .SH NAME lsreipl \- list the re-IPL configuration for Linux on System z .SH SYNOPSIS \fBlsreipl\fR [OPTION] .SH DESCRIPTION \fBlsreipl\fR lists the re-IPL (and IPL) configuration for Linux on System z. Using this tool you can see from which device your system will boot after you issue the reboot command. Furthermore you can query the system for information about the current boot device. IPL (initial program load) is the mainframe synonym for what is called "boot" in Linux. Accordingly re-IPL can be translated to "reboot" in the non mainframe context. .SH OPTIONS .TP \fB-i\fR or \fB--ipl\fR Print the IPL instead of the re-IPL configuration. This lists the configuration of the device from which your present Linux installation was booted. .TP \fB-h\fR or \fB--help\fR Print help information, then exit. .TP \fB-v\fR or \fB--version\fR Print version information, then exit. .SH SEE ALSO .BR chreipl (8) s390-tools-2.38.0/ipl_tools/man/lsshut.8000066400000000000000000000016051502674226300176430ustar00rootroot00000000000000.\" Copyright 2017 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH LSSHUT 8 "Sept 2011" "s390-tools" .SH NAME lsshut \- print the shutdown action configuration for Linux on System z .SH SYNOPSIS \fBlsshut\fR [OPTIONS] .SH DESCRIPTION The \fBlsshut\fR command lists the Linux on System z shutdown action configuration. This configuration handles what the system should do for the following shutdown triggers: "halt", "poff", "reboot", "restart", and "panic". If kdump is enabled on your system, "restart" and "panic" also show the "kdump" action together with the action that is run in case of a "kdump" failure. .SH OPTIONS .TP \fB-h\fR or \fB--help\fR Print help information, then exit. .TP \fB-v\fR or \fB--version\fR Print version information, then exit. .SH SEE ALSO .BR chshut (8) s390-tools-2.38.0/ipl_tools/nvme.c000066400000000000000000000105441502674226300165700ustar00rootroot00000000000000/* * ipl_tools - Linux for System z reipl and shutdown tools * * NVMe device functions * * Copyright IBM Corp. 2020 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include "lib/util_libc.h" #include "lib/util_file.h" #include "lib/util_path.h" #include "ipl_tools.h" static void nvme_dev_from_bdev(char *dev_name) { char *delim = strrchr(dev_name, 'n'); if (delim) *delim = 0; } /* * Return the fid of a device */ void nvme_fid_get(const char *device, char *fid) { char nvme_dev[NVME_DEV_MAX_LEN]; char buf[FID_MAX_LEN]; char *path; /* * An NVMe may present multiple namespaces and thus block devices, even * before partitioning, so we need the nvme part of the block * device name to get to the PCI function ID. */ util_strlcpy(nvme_dev, device, sizeof(nvme_dev)); nvme_dev_from_bdev(nvme_dev); path = util_path_sysfs("class/nvme/%s/device/function_id", nvme_dev); if (util_file_read_line(buf, FID_MAX_LEN, path)) ERR_EXIT_ERRNO("Could not read from \"%s\"", path); util_strlcpy(fid, buf, FID_MAX_LEN); free(path); } /* * Return the nsid of a device */ void nvme_nsid_get(const char *device, char *nsid) { char buf[FID_MAX_LEN]; char *path; path = util_path_sysfs("block/%s/nsid", device); if (util_file_read_line(buf, FID_MAX_LEN, path)) ERR_EXIT_ERRNO("Could not read from \"%s\"", path); util_strlcpy(nsid, buf, FID_MAX_LEN); free(path); } static int next_entry(DIR *dir, char *in_path, char *out_path, unsigned char entry_type) { struct dirent *dirent; char temp_path[NVME_PATH_MAX]; while ((dirent = readdir(dir)) != NULL) { if (strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0 || dirent->d_type != entry_type) continue; /* Resolve the symlink, if needed */ if (dirent->d_type == DT_LNK) { snprintf(temp_path, sizeof(temp_path), "%s/%s", in_path, dirent->d_name); if (!realpath(temp_path, out_path)) ERR_EXIT_ERRNO("Could not resolve link %s", temp_path); return 1; } snprintf(out_path, NVME_PATH_MAX, "%s/%s", in_path, dirent->d_name); return 1; } return 0; } static int nvme_getdev_by_fid(char *fidstr, char *devpath) { char temp_path[PATH_MAX + 19], real_path[PATH_MAX]; u_int64_t target_fid, curfid; char *sys_path; DIR *dir; char *end; int rc = -1; sys_path = util_path_sysfs("class/nvme"); target_fid = strtoul(fidstr, &end, 16); if (*end) ERR_EXIT("Invalid function_id given %s", fidstr); dir = opendir(sys_path); if (!dir) ERR_EXIT("Could not open %s", sys_path); errno = 0; while (next_entry(dir, sys_path, real_path, DT_LNK)) { snprintf(temp_path, sizeof(temp_path), "%s/%s", real_path, "device/function_id"); if (access(temp_path, F_OK)) continue; if (util_file_read_ul(&curfid, 16, temp_path)) ERR_EXIT("Invalid function_id found in %s", temp_path); if (curfid == target_fid) { strncpy(devpath, real_path, PATH_MAX); rc = 0; break; } } closedir(dir); free(sys_path); return rc; } static int nvme_getdev_by_nsid(char *nsid_str, char *path, char *dev_path) { char full_path[NVME_PATH_MAX+1], nsid_path[sizeof(full_path)+5]; char *end; u_int64_t nsid, curnsid; DIR *dir; nsid = strtoul(nsid_str, &end, 10); if (*end) ERR_EXIT_ERRNO("Invalid namespace id given %s", nsid_str); dir = opendir(path); if (!dir) ERR_EXIT_ERRNO("Could not open %s", path); errno = 0; while (next_entry(dir, path, full_path, DT_DIR)) { snprintf(nsid_path, sizeof(nsid_path), "%s/%s", full_path, "nsid"); if (access(nsid_path, F_OK)) continue; if (util_file_read_ul(&curnsid, 10, nsid_path)) ERR_EXIT("Invalid namespace id found in %s", nsid_path); if (curnsid == nsid) { strncpy(dev_path, full_path, NVME_PATH_MAX+1); closedir(dir); return 0; } } closedir(dir); return -1; } static int nvme_getdev(char *fid_str, char *nsid_str, char *dev_path) { char path_tmp[NVME_PATH_MAX]; if (nvme_getdev_by_fid(fid_str, path_tmp)) return -1; return nvme_getdev_by_nsid(nsid_str, path_tmp, dev_path); } /* * Check if the specified fid and nsid leads to a valid nvme device */ int nvme_is_device(char *fid_str, char *nsid_str) { char path_tmp[NVME_PATH_MAX+1]; return !(nvme_getdev(fid_str, nsid_str, path_tmp)); } s390-tools-2.38.0/ipl_tools/proc.c000066400000000000000000000214471502674226300165720ustar00rootroot00000000000000/* * ipl_tools - Linux for System z reipl and shutdown tools * * Scanner for /proc files * * Copyright IBM Corp. 2001, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include "ipl_tools.h" #include "proc.h" static const char proc_part_filename[] = "/proc/partitions"; static const char proc_dev_filename[] = "/proc/devices"; struct file_buffer { char* buffer; off_t pos; size_t length; }; #define INITIAL_FILE_BUFFER_SIZE 1024 /* Read file into buffer without querying its size (necessary for reading files * from /proc or /sys). Upon success, return zero and set BUFFER to point to * the file buffer and SIZE (if non-null) to contain the file size. Return * non-zero otherwise. Add a null-byte at the end of the buffer if * NIL_TERMINATE is non-zero. */ static int misc_read_special_file(const char* filename, char** buffer, size_t* size, int nil_terminate) { FILE* file; char* data; char* new_data; size_t count; size_t current_size; int current; file = fopen(filename, "r"); if (file == NULL) ERR_EXIT_ERRNO("Could not open \"%s\"", filename); current_size = INITIAL_FILE_BUFFER_SIZE; count = 0; data = (char *) malloc(current_size); if (data == NULL) { fclose(file); return -1; } current = fgetc(file); while (current != EOF || nil_terminate) { if (current == EOF) { current = 0; nil_terminate = 0; } data[count++] = (char) current; if (count >= current_size) { new_data = (char *) malloc(current_size * 2); if (new_data == NULL) { free(data); fclose(file); return -1; } memcpy(new_data, data, current_size); free(data); data = new_data; current_size *= 2; } current = fgetc(file); } fclose(file); *buffer = data; if (size) *size = count; return 0; } /* Get the contents of a file and fill in the respective fields of * FILE. Return 0 on success, non-zero otherwise. */ static int get_file_buffer(struct file_buffer* file, const char *filename) { int rc; rc = misc_read_special_file(filename, &file->buffer, &file->length, 0); file->pos = 0; return rc; } /* Free resources allocated for file buffer identified by * FILE. */ static void free_file_buffer(struct file_buffer* file) { free(file->buffer); file->buffer = NULL; file->pos = 0; file->length = 0; } /* Return character at current FILE buffer position or EOF if at end of * file. */ static int current_char(struct file_buffer* file) { if (file->buffer != NULL) if (file->pos < (off_t) file->length) return file->buffer[file->pos]; return EOF; } /* Advance the current file pointer of file buffer FILE until the current * character is no longer a whitespace or until the end of line or file is * reached. Return 0 if at least one whitespace character was encountered, * non-zero otherwise. */ static int skip_whitespaces(struct file_buffer* file) { int rc; rc = -1; while ((current_char(file) != '\n') && isspace(current_char(file))) { rc = 0; file->pos++; } return rc; } /* Scan a positive integer number at the current position of file buffer FILE * and advance the position respectively. Upon success, return zero and set * NUMBER to contain the scanned number. Return non-zero otherwise. */ static int scan_number(struct file_buffer* file, size_t* number) { int rc; size_t old_number; *number=0; rc = -1; while (isdigit(current_char(file))) { rc = 0; old_number = *number; *number = *number * 10 + current_char(file) - '0'; /* Check for overflow */ if (old_number > *number) { rc = -1; break; } file->pos++; } return rc; } /* Scan a device node name at the current position of file buffer FILE and * advance the position respectively. Upon success, return zero and set * NAME to contain a copy of the scanned name. Return non-zero otherwise. */ static int scan_name(struct file_buffer* file, char** name) { off_t start_pos; start_pos = file->pos; while (!isspace(current_char(file)) && (current_char(file) != EOF)) file->pos++; if (file->pos > start_pos) { *name = (char *) malloc(file->pos - start_pos + 1); if (*name == NULL) return -1; memcpy((void *) *name, (void *) &file->buffer[start_pos], file->pos - start_pos); (*name)[file->pos - start_pos] = 0; return 0; } else return -1; } /* Scan for the specified STRING at the current position of file buffer FILE * and advance the position respectively. Upon success, return zero. Return * non-zero otherwise. */ static int scan_string(struct file_buffer* file, const char *string) { int i; for (i=0; string[i] && (current_char(file) == string[i]); i++, file->pos++); if (string[i] == '\0') return 0; return -1; } /* Advance the current file position to beginning of next line in file buffer * FILE or to end of file. */ static void skip_line(struct file_buffer* file) { while ((current_char(file) != '\n') && (current_char(file) != EOF)) file->pos++; if (current_char(file) == '\n') file->pos++; } /* Return non-zero if the current file position of file buffer FILE is at the * end of file. Return zero otherwise. */ static int eof(struct file_buffer* file) { return file->pos >= (off_t) file->length; } /* Scan a line of the specified /proc/partitions FILE buffer and advance the * current file position pointer respectively. If the current line matches * the correct pattern, fill in the corresponding data into ENTRY and return 0. * Return non-zero otherwise. */ static int scan_part_entry(struct file_buffer* file, struct proc_part_entry* entry) { int rc; size_t dev_major; size_t dev_minor; size_t blockcount; char* name; /* Scan for: (\s*)(\d+)(\s+)(\d+)(\s+)(\d+)(\s+)(\S+)(\.*)$ */ skip_whitespaces(file); rc = scan_number(file, &dev_major); if (rc) return rc; rc = skip_whitespaces(file); if (rc) return rc; rc = scan_number(file, &dev_minor); if (rc) return rc; rc = skip_whitespaces(file); if (rc) return rc; rc = scan_number(file, &blockcount); if (rc) return rc; rc = skip_whitespaces(file); if (rc) return rc; rc = scan_name(file, &name); if (rc) return rc; skip_line(file); entry->device = makedev(dev_major, dev_minor); entry->blockcount = blockcount; entry->name = name; return 0; } /* Release resources associated with ENTRY. */ void proc_part_free_entry(struct proc_part_entry* entry) { free(entry->name); entry->name = NULL; } /* Scan a line of the specified /proc/devices FILE buffer and advance the * current file position pointer respectively. If the current line matches * the correct pattern, fill in the corresponding data into ENTRY and return 0. * Return non-zero otherwise. */ static int scan_dev_entry(struct file_buffer* file, struct proc_dev_entry* entry, int blockdev) { int rc; size_t dev_major; char* name; /* Scan for: (\s*)(\d+)(\s+)(\S+)(\.*)$ */ skip_whitespaces(file); rc = scan_number(file, &dev_major); if (rc) return rc; rc = skip_whitespaces(file); if (rc) return rc; rc = scan_name(file, &name); if (rc) return rc; skip_line(file); entry->device = makedev(dev_major, 0); entry->name = name; entry->blockdev = blockdev; return 0; } /* Release resources associated with ENTRY. */ void proc_dev_free_entry(struct proc_dev_entry* entry) { free(entry->name); entry->name = NULL; } /* Scan /proc/partitions for an entry matching DEVICE. When there is a match, * store entry data in ENTRY and return 0. Return non-zero otherwise. */ int proc_part_get_entry(dev_t device, struct proc_part_entry* entry) { struct file_buffer file; int rc; rc = get_file_buffer(&file, proc_part_filename); if (rc) return rc; rc = -1; while (!eof(&file)) { if (scan_part_entry(&file, entry) == 0) { if (entry->device == device) { rc = 0; break; } proc_part_free_entry(entry); } else skip_line(&file); } free_file_buffer(&file); return rc; } /* Scan /proc/devices for a blockdevice (BLOCKDEV is 1) or a character * device (BLOCKDEV is 0) with a major number matching the major number of DEV. * When there is a match, store entry data in ENTRY and return 0. Return * non-zero otherwise. */ int proc_dev_get_entry(dev_t device, int blockdev, struct proc_dev_entry* entry) { struct file_buffer file; int rc; int scan_blockdev = 0; rc = get_file_buffer(&file, proc_dev_filename); if (rc) return rc; rc = -1; while (!eof(&file)) { if (scan_string(&file, "Block") == 0) { skip_line(&file); scan_blockdev = 1; continue; } else if (scan_dev_entry(&file, entry, scan_blockdev) == 0) { if ((major(entry->device) == major(device)) && blockdev == scan_blockdev) { rc = 0; break; } proc_dev_free_entry(entry); } else skip_line(&file); } free_file_buffer(&file); return rc; } s390-tools-2.38.0/ipl_tools/proc.h000066400000000000000000000013751502674226300165750ustar00rootroot00000000000000/* * ipl_tools - Linux for System z reipl and shutdown tools * * Scanner for /proc files * * Copyright IBM Corp. 2001, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef PROC_H #define PROC_H #include struct proc_part_entry { dev_t device; size_t blockcount; char* name; }; struct proc_dev_entry { int blockdev; dev_t device; char *name; }; int proc_part_get_entry(dev_t device, struct proc_part_entry* entry); void proc_part_free_entry(struct proc_part_entry* entry); int proc_dev_get_entry(dev_t dev, int blockdev, struct proc_dev_entry* entry); void proc_dev_free_entry(struct proc_dev_entry* entry); #endif /* not PROC_H */ s390-tools-2.38.0/ipl_tools/shutdown.c000066400000000000000000000041351502674226300174750ustar00rootroot00000000000000/* * ipl_tools - Linux for System z reipl and shutdown tools * * Shutdown actions and triggers common functions * * Copyright IBM Corp. 2008, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include "ipl_tools.h" static struct shutdown_trigger shutdown_trigger_halt = { .name = "halt", .name_print = "Halt", .name_sysfs = "on_halt", }; static struct shutdown_trigger shutdown_trigger_poff = { .name = "poff", .name_print = "Power off", .name_sysfs = "on_poff", }; static struct shutdown_trigger shutdown_trigger_reboot = { .name = "reboot", .name_print = "Reboot", .name_sysfs = "on_reboot", }; struct shutdown_trigger shutdown_trigger_restart = { .name = "restart", .name_print = "Restart", .name_sysfs = "on_restart", }; struct shutdown_trigger shutdown_trigger_panic = { .name = "panic", .name_print = "Panic", .name_sysfs = "on_panic", }; struct shutdown_trigger *shutdown_trigger_vec[] = { &shutdown_trigger_halt, &shutdown_trigger_poff, &shutdown_trigger_reboot, &shutdown_trigger_restart, &shutdown_trigger_panic, NULL, }; static struct shutdown_action shutdown_action_ipl = { .name = "ipl", }; static struct shutdown_action shutdown_action_reipl = { .name = "reipl", }; static struct shutdown_action shutdown_action_dump = { .name = "dump", }; static struct shutdown_action shutdown_action_dump_reipl = { .name = "dump_reipl", }; static struct shutdown_action shutdown_action_stop = { .name = "stop", }; struct shutdown_action shutdown_action_vmcmd = { .name = "vmcmd", }; struct shutdown_action *shutdown_action_vec[] = { &shutdown_action_ipl, &shutdown_action_reipl, &shutdown_action_dump, &shutdown_action_dump_reipl, &shutdown_action_stop, &shutdown_action_vmcmd, NULL, }; void shutdown_init(void) { char path[PATH_MAX]; struct stat sb; int i; for (i = 0; shutdown_trigger_vec[i]; i++) { sprintf(path, "/sys/firmware/shutdown_actions/%s", shutdown_trigger_vec[i]->name_sysfs); if (stat(path, &sb) == 0) shutdown_trigger_vec[i]->available = 1; } } s390-tools-2.38.0/ipl_tools/system.c000066400000000000000000000043411502674226300171450ustar00rootroot00000000000000/* * ipl_tools - Linux for System z reipl and shutdown tools * * Shared system functions * * Copyright IBM Corp. 2008, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include "lib/util_path.h" #include "lib/util_file.h" #include "ipl_tools.h" /* * Check if we are running in an LPAR environment */ int is_lpar(void) { size_t bytes_read; char buf[2048]; int rc = 0; FILE *fh; fh = fopen("/proc/cpuinfo", "r"); if (fh == NULL) ERR_EXIT_ERRNO("Could not open \"/proc/cpuinfo\""); bytes_read = fread(buf, 1, sizeof(buf), fh); if (bytes_read == 0) ERR_EXIT("Could not read \"/proc/cpuinfo\""); buf[bytes_read] = '\0'; if (strstr(buf, "version = FF") == NULL) rc = 1; fclose(fh); return rc; } /* * Check whether we are started as root */ int is_root(void) { if (geteuid() == 0) return 1; else return 0; } /* * Read a string from a particular /sys/firmware file */ char *read_fw_str(const char *file) { char *string; char *path; path = util_path_sysfs("firmware/%s", file); string = util_file_read_text_file(path, 1); free(path); return string; } /* * Print content of a file (path = dir/file) */ void print_fw_str(const char *fmt, const char *dir, const char *file) { char path[PATH_MAX]; char *str; snprintf(path, sizeof(path), "%s/%s", dir, file); str = read_fw_str(path); printf(fmt, str); free(str); } /* * Write a string to a file */ void write_str(char *string, char *file) { char value[4096]; char *path; int fh; path = util_path_sysfs("firmware/%s", file); snprintf(value, sizeof(value), "%s\n", string); fh = open(path, O_WRONLY); if (fh < 0) ERR_EXIT_ERRNO("Could not open \"%s\"", file); if (write(fh, value, strlen(value)) < 0) ERR_EXIT_ERRNO("Could not set \"%s\"", file); close(fh); free(path); } /* * Write a string to a file and return ERRNO */ int write_str_errno(char *string, char *file) { char value[4096]; char *path; int fh; path = util_path_sysfs("firmware/%s", file); snprintf(value, sizeof(value), "%s\n", string); fh = open(path, O_WRONLY); if (fh < 0) return errno; if (write(fh, value, strlen(value)) < 0) return errno; close(fh); free(path); return 0; } s390-tools-2.38.0/iucvterm/000077500000000000000000000000001502674226300153055ustar00rootroot00000000000000s390-tools-2.38.0/iucvterm/Makefile000066400000000000000000000007411502674226300167470ustar00rootroot00000000000000#! /usr/bin/make -f include ../common.mak GETTEXT_TEXTDOMAIN = iucvterm export GETTEXT_TEXTDOMAIN SUBDIRS = src po doc bin etc test RECURSIVE_TARGETS = all-recursive install-recursive clean-recursive \ check-recursive all: all-recursive install: install-recursive clean: clean-recursive $(RECURSIVE_TARGETS): @target=`echo $@ |sed s/-recursive//`; \ for d in $(SUBDIRS); do \ (cd $$d && $(MAKE) $$target) || exit 1; \ done .PHONY: install clean s390-tools-2.38.0/iucvterm/bin/000077500000000000000000000000001502674226300160555ustar00rootroot00000000000000s390-tools-2.38.0/iucvterm/bin/Makefile000066400000000000000000000016761502674226300175270ustar00rootroot00000000000000#! /usr/bin/make -f include ../../common.mak BIN_SCRIPTS = ts-shell SBIN_SCRIPTS = chiucvallow SCRIPTS = $(BIN_SCRIPTS) $(SBIN_SCRIPTS) all: check: install: $(SCRIPTS) for prg in $(BIN_SCRIPTS); do \ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 $$prg $(DESTDIR)$(USRBINDIR) ; \ done for prg in $(SBIN_SCRIPTS); do \ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 $$prg $(DESTDIR)$(USRSBINDIR) ; \ done $(INSTALL) -g $(GROUP) -o $(OWNER) -d $(DESTDIR)$(VARDIR)/log/ts-shell ln -f -s chiucvallow $(DESTDIR)$(USRSBINDIR)/lsiucvallow clean: -rm -f *.o $(SCRIPTS) %: %.in real_usrbin=$(USRBINDIR); \ real_var=$(VARDIR); \ my_sysconf=$(SYSCONFDIR)/iucvterm; \ $(SED) -e "s#@sysconf_path@#$$my_sysconf#g" \ -e "s#@iucvconn_path@#$$real_usrbin#g" \ -e "s#@var_path@#$$real_var#g" \ -e 's#@S390_TOOLS_RELEASE@#$(S390_TOOLS_RELEASE)#g' \ < $< > $@ perl: ts-shell perl -c $< scripts: $(SCRIPTS) .PHONY: install clean perl s390-tools-2.38.0/iucvterm/bin/chiucvallow.in000077500000000000000000000147271502674226300207430ustar00rootroot00000000000000#!/bin/bash # # z/VM IUCV HVC device driver -- Edit z/VM user ID filter # # Copyright IBM Corp. 2009, 2017 # # s390-tools is free software; you can redistribute it and/or modify # it under the terms of the MIT license. See LICENSE for details. # PRG=`basename $0` # sysfs file that contains the z/VM user ID filter for the # z/VM IUCV HVC device driver sysfs_hvc_iucv_allow="/sys/module/kernel/parameters/hvc_iucv_allow" # older kernel versions do not have the "kernel" directory test -f $sysfs_hvc_iucv_allow || \ sysfs_hvc_iucv_allow="/sys/module/hvc_iucv/parameters/hvc_iucv_allow" show_help(){ cat <<"EoHelp" Usage: chiucvallow [-h|--help] [-v|--version] chiucvallow -l|--list chiucvallow -c|--clear chiucvallow -e|--edit [] chiucvallow -s|--set chiucvallow -V|--verify Options: -h|--help Print help information, then exit. -v|--version Print version information, then exit. -l|--list List current z/VM user ID filter. -c|--clear Clear the z/VM user ID filter. -e|--edit [] Edit the z/VM user ID filter. -s|--set Set the z/VM user ID filter from a filter file. -V|--verify Verify the z/VM user ID filter file. The list, edit, set and clear options require root authority. EoHelp } show_version(){ cat <&2 exit ${2:-1} } hvciucv_available(){ test -r "$sysfs_hvc_iucv_allow" \ && cat "$sysfs_hvc_iucv_allow" >/dev/null 2>&1 if test $? -gt 0; then cat >&2 </dev/null |wc -c` if test "$fsize" -gt 4095; then printf "$PRG: The z/VM user ID filter exceeds the maximum size (%d of %d bytes)\n" \ $fsize 4095 >&2 return 1 fi while read userid; do # skip empty lines and lines starting with '#' echo "$userid" |grep -q -E '^(\s*$|#)' && continue printf "Verify z/VM user ID: %-8s : " "$userid" if echo -n "$userid" |grep -q -E -i "$regex"; then printf "OK\n" else printf "FAILED\n" failed=$((failed + 1)) fi count=$((count + 1)) done <$filename test $count -gt 500 && \ error "The z/VM user ID filter exceeds the maximum of 500 user IDs" printf "\n$PRG: Verification summary: verified=%d failed=%d size=%d bytes\n" \ $count $failed $fsize test $failed -eq 0 || return 2 } # # Edit the z/VM user ID filter edit_filter(){ local fromfile="$1" local context=$2 local tmpfile=`mktemp /tmp/hvc_iucv_allow.XXXXXX` local md5file=`mktemp /tmp/hvc_iucv_allow.md5.XXXXXX` if test -w $tmpfile && test -w $md5file; then :; else error "Creating temporary files failed" fi # save list in temp file if test -r "$fromfile"; then cat $fromfile; else list_filter; fi > $tmpfile # check whether to open editor if test "x$context" != xnoeditor; then md5sum $tmpfile > $md5file # save checksum to track changes ${EDITOR:-vi} $tmpfile # open editor if md5sum --status -c $md5file; then cat </dev/null; then :; else cat >&2 < $sysfs_hvc_iucv_allow if test x$? != x0; then cat >&2 </dev/null exit 0 } # # Clear z/VM user ID filter clear_filter(){ echo > $sysfs_hvc_iucv_allow } # # Check whether we run as root, otherwise complain and exit for_root_only(){ local euid=`id -u 2>/dev/null` test "x$euid" = x0 && return error "You need root authority to use option '$1'" } lock_operation(){ ( flock -nx 9 \ || error "The filter is currently being changed. Try again later." $@ ) 9>/var/lock/hvc_iucv_allow } # Common options case $1 in -h|--help) show_help exit 0 ;; -v|--version) show_version exit 0 ;; esac # check the name under which we have been called case `basename $0` in lsiucvallow) exec chiucvallow --list ;; esac # chiucvallow program options case $1 in -l|--list) for_root_only "$1" hvciucv_available list_filter ;; -e|--edit) for_root_only "$1" hvciucv_available lock_operation edit_filter "$2" ;; -c|--clear) for_root_only "$1" hvciucv_available lock_operation clear_filter ;; -s|--set) for_root_only "$1" test -n "$2" \ || error "This option requires a file as argument" test -r "$2" \ || error "The specified file must be readable" hvciucv_available lock_operation edit_filter "$2" noeditor ;; -V|--verify) test -n "$2" \ || error "This option requires a file as argument" verify_filter "$2" exit $? ;; '') echo "$PRG: One or more arguments are missing" >&2 echo "Try '$0 --help' for more information." >&2 exit 201 ;; *) echo "$PRG: Invalid option -- '$1'" >&2 echo "Try '$0 --help' for more information." >&2 exit 201 ;; esac exit 0 s390-tools-2.38.0/iucvterm/bin/ts-shell.in000077500000000000000000000423001502674226300201420ustar00rootroot00000000000000#! /usr/bin/perl -W # # ts-shell - Simple terminal server shell to access systems over IUCV # # This script can be used as login shell for users to restrict # IUCV-based terminal access to other systems. # # Copyright IBM Corp. 2008, 2017 # # s390-tools is free software; you can redistribute it and/or modify # it under the terms of the MIT license. See LICENSE for details. # use strict; use warnings; use File::Basename; use Getopt::Long qw(:config no_ignore_case); use Term::ReadLine; use POSIX; $ENV{'PERL_RL'} = " o=0"; # use best avail. readline $ENV{'PATH'} = "/bin:/sbin:/usr/bin:/usr/sbin"; $ENV{'LESSSECURE'} = 1; # let less run in "secure" mode $ENV{'PAGER'} = $ENV{'PAGER'} || "/usr/bin/less"; my $ts_shell = fileparse($0, qr/\.[^.]+/); $SIG{__WARN__} = sub { print STDERR "$ts_shell: $_[0]"; }; $SIG{__DIE__} = sub { print STDERR "$ts_shell: $_[0]"; exit 255; }; # Terminal server configuration settings my %config = ( # general options 'conffile' => "@sysconf_path@/ts-shell.conf", 'authfile' => "@sysconf_path@/ts-authorization.conf", 'sysfile' => "@sysconf_path@/ts-systems.conf", 'auditfile' => "@sysconf_path@/ts-logsys.conf", 'auditdir' => "@var_path@/log/ts-shell", 'iucvconn' => "@iucvconn_path@/iucvconn", 'prompt' => getpwuid($>) . '@'."$ts_shell> ", # runtime options 'rl' => undef, # terminal readline (rl) 'user' => getpwuid($>), # user name 'groups' => [], # user groups (list ref) 'service' => "lnxhvc0", # default service 'auth_func' => sub { return -1; }, # no authorization 'regex' => [], # re to match systems (list ref) 'systems' => [], # system list (list reference) 'services' => [qw/lnxhvc0/], # services list (completion) 'termsys' => {}, # global ts sys list (hash ref) 'auditsys' => {}, # audit ts sys list (hash ref) ); sub main(); sub help(); sub usage(); sub version(); sub intro(); sub auth_global($$); sub auth_regex($$); sub auth_list($$); sub get_auditlog_file($$$); sub cmd_connect($\%); sub cmd_service($\%); sub cmd_list(\%); sub rl_cmd_completion($$$); sub list_regex_match($); sub updateConfiguration(\%$); sub loadAuthorization(\%); sub readFile($$); sub log_debug($); sub log_error($); sub log_info($); sub pager($); # main() - Terminal server program # # The program loads the system authorizations for the effective user id, # and then initializes the term readline environment and starts the shell. # sub main() { unless (GetOptions("v|version" => sub { version(); exit 0; }, "h|help" => sub { usage(); exit 0; })) { log_error "Enter '$ts_shell --help' for more information"; exit 1; } unless (-t STDIN) { log_error "The $ts_shell requires a terminal to run on"; exit 1; } unless ($config{user}) { log_error "Resolving the name of user ID $> failed"; exit 2; } # update ts-shell configuration unless (readFile($config{conffile}, sub { updateConfiguration(%config, $_); })) { log_error "Reading the ts-shell configuration file " . "$config{conffile} failed: $!"; exit 3; } # load list of systems the ts-shell is allowed to use; and # hash the systems names in uppercase unless (readFile($config{sysfile}, sub { $config{termsys}->{uc $_} = 1; })) { log_error "Reading $config{sysfile} failed: $!"; exit 4; } # load list of systems for for that auditing should be enabled # hash the system names in uppercase unless (readFile($config{auditfile}, sub { $config{auditsys}->{uc $_} = 1; })) { log_error "Reading $config{auditsys} failed: $!"; exit 5; } # retrieve user group membership and load authorizations my %uniq_groups = (); # $) returns egid twice, so we hash group names to avoid dups foreach(map {$_ = getgrgid($_)} split / /, $)) { $uniq_groups{$_} = 1; } push @{$config{groups}}, keys %uniq_groups; # finally, load user specific authorization data from file unless (loadAuthorization(%config)) { log_error "Reading $config{authfile} failed: $!"; exit 5; } # set up terminal readline $config{rl} = new Term::ReadLine 'Terminal Server Shell'; $config{rl}->Attribs->{'completion_function'} = \&rl_cmd_completion; select ($config{rl}->OUT || \*STDOUT); # setup signal handler to ignore SIGINT my $sigint = POSIX::SigAction->new(sub { return 1; }, POSIX::SigSet->new(), &POSIX::SA_NODEFER); POSIX::sigaction(&POSIX::SIGINT, $sigint); log_debug "User: $config{user} / Groups: $) [@{$config{groups}}]"; log_debug("Using Term::ReadLine backend: " . $config{rl}->ReadLine); intro(); SHELL: while (defined($_ = $config{rl}->readline($config{prompt}))) { chomp; s/^\s+|\s+$//; next if /^$/; SWITCH: { /^(?:q|quit|exit)/ and last SHELL; /^help/ and help(), last SWITCH; /^version/ and version(), last SWITCH; /^(?:list|ls)/ and cmd_list(%config), last SWITCH; /^terminal\s*(\w*)/ and cmd_service($1, %config), last SWITCH; if (/^connect\s*(\w*$|\w+\s+\w+)/) { cmd_connect($1, %config); last SWITCH; } log_error "$_ is not a known command"; } $config{rl}->addhistory($_) if (/\S/ && !$config{rl}->Features->{autohistory}); } exit 0; } # updateConfiguration() - Load ts-shell configuration from ts-shell.conf # # %cfg: Hash reference to terminal server configuration # $line: Configuration line (key = value pair) # # The routine updates the ts-shell configuration based on configuration lines # from the ts-shell.conf file. # sub updateConfiguration(\%$) { my ($cfg, $line) = @_; my ($option, $value) = split /\s*=\s*/, $line; $cfg->{sysfile} = $value if $option =~ /^ts-systems$/; $cfg->{authfile} = $value if $option =~ /^ts-authorization$/; $cfg->{auditdir} = $value if $option =~ /^transcript-directory$/; $cfg->{auditfile} = $value if $option =~ /^transcript-systems$/; } # loadAuthorization() - Load system authorizations from file # # $cfg: Hash reference to terminal server configuration # # The routine parses the authorization data for the effective user # and stores the information in the configuration hash. Further, it # sets the auth_func reference to either auth_list() or auth_regex() # to abstract authorization checks. # sub loadAuthorization(\%) { my $cfg = shift(); return 0 unless open(AUTH, "<$cfg->{authfile}"); AUTH_ENT: while () { chomp; next if /^#/; # ignore comments next if /^\s*$/; # skip empty lines s/^\s+|\s+$//g; # trim my $authorized = 0; my ($key, $val) = split /\s*=\s*/; # read authorization configuration for user and its groups if ($key =~ /^$cfg->{user}$/) { $authorized = 1; log_debug "Found user: $key"; } elsif ($key =~ /^@(\S+)$/) { my $group = $1; $authorized = 1 if grep {/^${group}$/} @{$cfg->{groups}}; log_debug "Found group: $key" if $authorized; } # skip line if there was no auth data for current user next unless $authorized; # build authorization policy and set auth_func if ($val =~ /^list:\s*(.+)$/) { if (@{$cfg->{regex}}) { log_error "Authorization by list is ignored because " ."$cfg->{user} uses regular expressions " ."($cfg->{authfile}:$.)"; next AUTH_ENT; } $cfg->{auth_func} = \&auth_list; push @{$cfg->{systems}}, split /\s*[;,]\s*/, $1; } elsif ($val =~ /^file:(.+)$/) { if (@{$cfg->{regex}}) { log_error "Authorization by list is ignored because " . "$cfg->{user} uses regular expressions " . "($cfg->{authfile}:$.)"; next AUTH_ENT; } $cfg->{auth_func} = \&auth_list; unless(readFile($1, sub { push @{$cfg->{systems}}, $_; })) { log_error "Reading $1 failed: $! " . "($cfg->{authfile}:$.)"; } } elsif ($val =~ /^regex:(.+)$/) { if (@{$cfg->{systems}}) { log_error "Authorization by regular expression " . "is ignored because $cfg->{user} " . "uses lists ($cfg->{authfile}:$.)"; next AUTH_ENT; } # check regex syntax and complain if it is not correct my $re = eval "qr/$1/io"; if ($@) { log_error "An authorization entry is not a valid" . " regular expression " . "($cfg->{authfile}:$.):\n$@"; next AUTH_ENT; } $cfg->{auth_func} = \&auth_regex; push @{$cfg->{regex}}, $re; } else { log_error "An authorization entry is not valid " . "($cfg->{authfile}:$.)"; } log_debug "'$key' => '$val'"; } close(AUTH); return 1; } # readFile() - Helper routine to read data from file # # The routine reads the file content, line by line, skips # comments and empty lines. It calls a routine for each line # containing data. The routine can access the data either # using the first parameter or using the context variable $_. # # Error handling is up to the caller; the routine exits with # zero if the file could not be opened. open() should set $!. # # $file: File name # $sub: Reference to sub routine # sub readFile($$) { my ($file, $sub) = @_; return 0 unless open(CONF, "<$file"); while () { chomp; next if /^#/; # ignore comments next if /^\s*$/; # skip empty lines s/^\s+|\s+$//g; # trim &{$sub}($_); # execute sub in current context } close(CONF); return 1; } # auth_global() - Authorization check using global system list # # $guest: Scalar reference to the guest name string # $cfg: Hash reference to terminal server configuration # sub auth_global($$) { my ($guest, $cfg) = @_; return -1 unless $cfg->{termsys}; # unstricted return 1 if exists $cfg->{termsys}->{'[*ALL*]'}; # if restricted, check if system has been hashed (in uppercase) return 1 if exists $cfg->{termsys}->{uc $$guest}; # guest has not been defined, so deny authorization request return -2; } # auth_regex() - Authorization check using a regex # # $guest: Scalar reference to the guest name string # $cfg: Hash reference to terminal server configuration # sub auth_regex($$) { my ($guest, $cfg) = @_; return -1 unless @{$cfg->{regex}}; foreach my $re (@{$cfg->{regex}}) { return 1 if $$guest =~ /$re/; # $re has been compiled as //io } return 0; } # auth_list() - Authorization check using a list of systems # # $guest: Scalar reference to the guest name string # $cfg: Hash reference to terminal server configuration # sub auth_list($$) { my ($guest, $cfg) = @_; return -1 unless @{$cfg->{systems}}; return 1 if grep(/^\s*\Q$$guest\E\s*$/i, @{$cfg->{systems}}); return 0; } # get_auditlog_file() - Set file path for session transcript # # Return false if an error has occurred; otherwise true. # # $filepath: Scalar reference to file path variable # $guest: Name of the virtual guest machine # $cfg: Hash reference to terminal server configuration sub get_auditlog_file($$$) { my ($filepath, $guest, $cfg) = @_; unless (exists $cfg->{auditsys}->{'[*ALL*]'} || exists $cfg->{auditsys}->{uc $guest}) { $$filepath = ""; return 1; # no transcript, return success } # check and create log directory for user unless (-d "$cfg->{auditdir}/$cfg->{user}") { unless (mkdir "$cfg->{auditdir}/$cfg->{user}") { log_error "Creating the session transcript directory " . "$cfg->{auditdir}/$cfg->{user} failed: $!"; $$filepath = ""; return 0; # return error } } $$filepath = "$cfg->{auditdir}/$cfg->{user}/" . uc $guest . "_"; $$filepath .= strftime '%F-%H%M%S', localtime; log_debug "Session transcript file: $$filepath"; return 1; # return success } # cmd_connect() - Perform auth check; and on success, connect to system # # $guest: Name of the virtual guest machine # $cfg: Hash reference to terminal server configuration # sub cmd_connect($\%) { my ($params, $cfg) = @_; my $service = $cfg->{service}; unless (length($params)) { log_error "The z/VM guest name is missing"; return; } my ($guest, $srv) = split /\s+/, $params; $service = $srv if $srv && $srv =~ /\w{1,8}/; # check authorization: # The auth_func contract is to return a code, that must be one of: # -2: ts-shell is not permitted to connect to the guest # -1: The user does not have any authorization policy # 0: User does not have authorization to connect to guest # 1: User is permitted to connect to guest (success) # Other return codes are not allowed! # # call auth_func to check authorization my $rc = &{$cfg->{auth_func}}(\$guest, $cfg); # if the user is authorized, finally check if the system is # (globally) restricted to be used by the terminal server $rc = auth_global(\$guest, $cfg) if $rc > 0; if ($rc == -2) { log_error "$ts_shell is not configured to connect to $guest"; return; } if ($rc == -1) { log_error "You are not authorized to connect to " . "any z/VM virtual machines"; return; } if ($rc == 0) { log_error "You are not authorized to connect to $guest"; return; } # construct iucvconn command line my @iucvconn = ("$cfg->{iucvconn}"); # check if the terminal session to $guest requires session logging my $session_path = ""; # return if get_auditlog_file() failed return unless get_auditlog_file(\$session_path, $guest, $cfg); push @iucvconn, "-s", $session_path if $session_path; # add $guest name and terminal identifier (service) push @iucvconn, $guest, $service; log_info "Connecting to $guest (terminal identifier: $service)..."; if (-x $cfg->{iucvconn}) { system @iucvconn; log_info "Connection ended"; } else { log_error "Running $cfg->{iucvconn} failed: $!"; } } # cmd_service() - Show / set service/terminal name to identify remote terminal # # $new: New terminal name; can be empty # %cfg: Hash reference to terminal server configuration # sub cmd_service($\%) { my ($new, $cfg) = @_; unless ($new) { print "$cfg->{service}\n"; return; } if ($new =~ /\w{1,8}/) { $cfg->{service} = $new; # push new service for cmd completion unless (grep /$new/, @{$cfg->{services}}) { push @{$cfg->{services}}, $new; } } else { log_error("Terminal identifier $new is not valid"); log_error("Terminal identifiers consist of up to " . "eight alphanumerical characters"); } } # cmd_list() - List system authorization for effective user # # $cfg: Hash reference to terminal server configuration # sub cmd_list(\%) { my $cfg = shift(); if (@{$cfg->{regex}}) { pager sub { print "Regular expressions for your authorization:\n"; print "$_\n" foreach (@{$cfg->{regex}}); my $matches = list_regex_match($cfg); if (@$matches) { print "\nYou are authorized to connect to ". "these z/VM guest virtual machines:\n"; print "$_\n" foreach (@$matches); } }; } elsif (@{$cfg->{systems}}) { pager sub { foreach (@{$cfg->{systems}}) { printf "$_\n" if auth_global(\$_, $cfg) > 0; } }; } else { log_error "You are not authorized to connect to " . "any z/VM virtual machines"; } } sub log_debug($) { print STDERR "[DEBUG] " . shift() . "\n" if $ENV{'TS_SHELL_DEBUG'}; } sub log_error($) { print STDERR "$ts_shell: " . shift() . "\n"; } sub log_info($) { print "$ts_shell: " . shift() . "\n"; } sub pager($) { my $eval = shift(); unless (open(PAGER, "|$ENV{'PAGER'}")) { eval &$eval; return; } my $old_out = select PAGER; eval &$eval; select $old_out; close (PAGER); } sub intro() { print < Connect to specified z/VM guest virtual machine. terminal [] Display or set the terminal identifier. q | quit | exit Exit the current shell session. help Display help information. version Display version information. EoHelp } sub usage() { printf <{termsys}->{'[*ALL*]'}; # get a list of systems if ts shell runs unrestricted foreach my $re (@{$cfg->{regex}}) { foreach (grep (/$re/, keys %{$cfg->{termsys}})) { $uniq_sys{$_} = 1; } } push @result, (keys %uniq_sys); return \@result; } # rl_cmd_completion() - Term::ReadLine completion for ts-shell commands # # $text: Text to complete # $line: Line buffer # $start: offset # sub rl_cmd_completion($$$) { my ($text, $line, $start) = @_; my @cmds = qw/help version list connect terminal quit exit/; # complete commands return grep (/^$text/, @cmds) unless $start; # complete terminal names for service and optionally for connect command if ($line =~ /^(?:terminal|connect\s+\w+)\s+(?:\w+)?$/) { return grep (/^$text/, @{$config{services}}); } # complete systems for connect command if ($line =~ /^connect\s+(\w+)?$/) { my $systems = []; # list ref for completion # complete for simple lists if (@{$config{systems}}) { foreach my $sys (@{$config{systems}}) { if (auth_global(\$sys, \%config) > 0) { push @$systems, $sys; } } # complete if ts shell uses regex's } elsif (@{$config{regex}}) { $systems = list_regex_match(\%config); } return grep {/^$text/} @$systems; } return (); } # start the terminal server shell &main(); __DATA__ __END__ # vim: set ai noet ts=8 sw=8 tw=80: s390-tools-2.38.0/iucvterm/doc/000077500000000000000000000000001502674226300160525ustar00rootroot00000000000000s390-tools-2.38.0/iucvterm/doc/Makefile000066400000000000000000000010071502674226300175100ustar00rootroot00000000000000#! /usr/bin/make -f include ../../common.mak MANS = iucvconn.1 iucvtty.1 ts-shell.1 hvc_iucv.9 chiucvallow.8 ttyrun.8 all: check: install: install-man install-man: $(MANS) for man in $(MANS); do \ msection=`echo $$man |sed 's/.*\.\([1-9]\)$$/man\1/'` ; \ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 -D $$man $(DESTDIR)$(MANDIR)/$$msection/$$man ; \ done clean: pdf: $(MANS) for man in $(MANS); do \ man -t ./$$man |ps2pdf -sPAPERSIZE=a4 - $${man}.pdf ; \ done .PHONY: install-man install clean pdf s390-tools-2.38.0/iucvterm/doc/chiucvallow.8000066400000000000000000000070501502674226300204650ustar00rootroot00000000000000.\" chiucvallow.8 .\" .\" .\" Copyright IBM Corp. 2009, 2017 .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" ---------------------------------------------------------------------- .TH chiucvallow "8" "March 2015" "s390-tools" "z/VM IUCV HVC device driver" . . .ds f \fBchiucvallow\fP . . .SH NAME lsiucvallow, chiucvallow \- List, change, and verify z/VM user ID filter settings . . . .SH SYNOPSIS .B lsiucvallow .br .B \*f .BR \-l | \-\-list .B \*f .BR \-e | \-\-edit .RI " [" filter ] .br .B \*f .BR \-s | \-\-set .I filter .br .B \*f .BR \-c | \-\-clear .B \*f .BR \-V | \-\-verify .I filter . . . .SH DESCRIPTION The \*f program lists, verifies, and changes the z/VM user ID filter of the z/VM IUCV hypervisor console (HVC) device driver. The z/VM user ID filter lists the z/VM guest virtual machines (user IDs) that are permitted to establish IUCV communication paths with the z/VM IUCV HVC device driver. The filter list is a comma-separated list of z/VM user IDs as specified by the \fBhvc_iucv_allow\fP kernel parameter. A filter entry might end with an asterisk (*) to match multiple z/VM user IDs. Use \*f to change the filter list at runtime. \*f works with the currently active filter or with filter files that list each z/VM user ID on a separate line. Filter files might also contain comment lines starting with '#'. .br You can edit and verify existing filter files and replace the current z/VM user ID filter. . . . .SH OPTIONS .TP .BR \-h ", " \-\-help Display a short help text, then exit. . .TP .BR \-v ", " \-\-version Display the version of \*f, then exit. . .TP .BR \-l ", " \-\-list Display the z/VM user IDs contained in the current filter. Using this parameter requires root authority. . .TP .BR \-e ", " \-\-edit " [" \fIfilter\fP ] Edit the current z/VM user ID filter. \fIfilter\fP specifies a filter file listing z/VM user IDs. If \fIfilter\fP is not specified, the entries of the current z/VM user ID filter are used. The filter is opened temporarily in an editor program. When the editor is closed, the changed filter is verified and, if verification succeeds, the current filter is replaced. Otherwise a backup copy is saved for correcting errors. Using this parameter requires root authority. . .TP .BR \-c ", " \-\-clear Clear the current z/VM user ID filter by deleting all user ID entries. Using this parameter requires root authority. . .TP .BR \-s ", " \-\-set " " "\fIfilter\fP" Replace the current filter with the filter entries specified by \fIfilter\fP. The current filter can be replaced only after the new filter has been successfully verified. Using this parameter requires root authority. . .TP .BR \-V ", " \-\-verify " " "\fIfilter\fP" Verify the z/VM user ID filter specified by \fIfilter\fP. The verification process checks the following rules: .RS 10 .IP "\(bu" 2 z/VM user IDs consist of up to eight alphanumeric characters or underscores (_) .IP "\(bu" 2 The total number of z/VM user IDs in the filter must not exceed 500 .IP "\(bu" 2 The filter size in bytes must not exceed 4095 bytes .RE . . . .SH "FILES" .TP .B /sys/module/kernel/parameters/hvc_iucv_allow sysfs attribute for accessing the \fBhvc_iucv_allow\fP kernel parameter. Use \*f to modify and access this sysfs attribute. . . . .SH "ENVIRONMENT" .TP .B EDITOR Specifies an editor program that is used for editing z/VM user ID filters. If \fBEDITOR\fP is not set or empty, .BR vi (1) is used. . . . .SH "SEE ALSO" .BR hvc_iucv (9) .I "Linux on System z - Device Drivers, Features, and Commands" s390-tools-2.38.0/iucvterm/doc/hvc_iucv.9000066400000000000000000000242411502674226300177550ustar00rootroot00000000000000.\" hvc_iucv.9 .\" .\" z/VM IUCV hypervisor console (HVC) device driver .\" .\" Copyright IBM Corp. 2008, 2017 .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .\" ------------------------------------------------------------------------- .TH "hvc_iucv" "9" "March 2015" "s390-tools" "z/VM IUCV HVC device driver" . . . .SH NAME hvc_iucv \- Introduction to the z/VM IUCV hypervisor console (HVC) device driver . . . .SH SYNOPSIS .BR hvc_iucv "=" \fInumber\fP .BR hvc_iucv_allow "=" "\fIcomma-separated list of z/VM user IDs" . . . .SH DESCRIPTION The Hypervisor Console (HVC) device driver is a generic TTY device driver providing terminal devices with pluggable transport device drivers. The z/VM IUCV hypervisor console (HVC) device driver is a transport plug-in that uses z/VM IUCV communication to establish terminal connections and to transfer terminal data. The kernel parameter \fBhvc_iucv\fP controls the number of HVC terminals managed by the z/VM IUCV HVC device driver. \fInumber\fP is an integer in the range 0 to 8. If \fInumber\fP is zero, the z/VM IUCV HVC device driver is switched off; otherwise up to \fInumber\fP z/VM IUCV HVC terminal devices are created. .\"(The maximum of 8 is a hypervisor console layer constant and might change .\"in future kernel versions.) The \fBhvc_iucv_allow\fP kernel parameter optionally specifies a comma separated list of z/VM user IDs. If the kernel parameter has been specified, the z/VM IUCV HVC device driver accepts IUCV connections from listed z/VM user IDs only. The first z/VM IUCV HVC terminal device is registered automatically for use as the Linux console. The .BR iucvconn (1) program establishes connections to z/VM IUCV HVC terminal devices. If a terminal is disconnected, output written by Linux is not displayed or saved. Therefore, a newly opened connection to a terminal is always blank. For most applications, like login or shell prompts, it is sufficient to press "Return" to obtain a new prompt. . . . .SH USAGE .SS "Connecting to z/VM IUCV HVC terminal devices" Connections to z/VM IUCV HVC terminal devices are established with the IUCV terminal application .BR iucvconn (1). .PP To connect to the first z/VM IUCV HVC terminal device on the z/VM guest virtual machine "LNX1234", run: .PP .ft CW .in +0.25in .nf iucvconn LNX1234 lnxhvc0 .fi .in -0.25in .ft .PP The \fBlnxhvc\fP\fIn\fP identifier refers to the IUCV name of z/VM IUCV HVC terminal devices; where \fIn\fP corresponds to the terminal device number. .PP For z/VM IUCV HVC terminal devices, .BR iucvconn (1) does not set the terminal environment. See section "Setting up the terminal environment" below for further instructions. .PP The terminal connection is disconnected if the terminal session ends by logging off (for example, by typing exit). Alternatively, .BR iucvconn (1) provides an escape function for disconnecting from the terminal at any time. See also the manual page of .BR iucvconn (1). . .TP .B Note: If the first z/VM HVC terminal device is configured as the preferred Linux console, a logoff might be followed by a new login prompt. End the terminal session by using the disconnect escape function. Reconnecting always resumes the existing terminal session. Explicitly logging off before using the disconnect escape function assures that users must log in again. . . .SS "Using the magic sysrequest function on the first z/VM IUCV HVC terminal" The first z/VM IUCV HVC terminal, which can be used as Linux console, supports the "magic sysrequest" function if the Linux kernel has been built with \f(CWCONFIG_MAGIC_SYSRQ\fP. .PP To invoke the "magic sysrequest" function, press "Ctrl\^+\^o" followed by a second character that designates the debugging or emergency function. .PP The most important magic sysrequest functions are: .RS 4 .IP "0 .. 9" 8 Set the Linux console log level and control which kernel message are written to Linux console devices. . .IP "b" 8 Re-IPL immediately (without syncing or unmounting file systems). The re-IPL configuration is used for the re-IPL of the Linux system. . .IP "s" 8 Emergency sync all file file systems. . .IP "u" 8 Emergency remount all mounted file systems read-only. . .IP "t" 8 Show task info. . .IP "m" 8 Show memory. . .IP "e" 8 Send the TERM signal to end all tasks except .BR init (8). . .IP "i" 8 Send the KILL signal to end all tasks except .BR init (8). . .RE .PP See \fBsysrq.txt\fP of the Linux kernel documentation for a more complete list of functions. .PP This feature can be switched on or off during runtime by echoing "1" (on) or "0" (off) to \fB/proc/sys/kernel/sysrq\fP. . . . .SH CONFIGURATION .SS "Using the z/VM IUCV HVC terminal device as Linux console" The first z/VM IUCV HVC terminal device can display kernel messages and it can also be used as the preferred Linux console (i.e. become \fB/dev/console\fP). The preferred console is used as the initial input and output device, beginning at the stage of the boot process when the .BR init (8) program is called. Messages issued by programs that run at this stage are only displayed on the preferred console. .TP .B Note: Console messages are not displayed if the z/VM IUCV HVC terminal is not connected. If console messages flood the terminal, the z/VM HVC terminal device driver might discard console messages if they come in too fast. .PP To use the z/VM IUCV hypervisor console as the preferred console, append the \fBconsole=hvc0\fP parameter to the kernel command line. To display console messages only and use another device as preferred console, append \fBconsole=hvc0\fP, followed by an additional \fBconsole=\fP parameter that explicitly defines the preferred console, e.g. \fBconsole=ttyS0\fP. .TP .B Note: The last \fBconsole=\fP parameter designates the device for use as the preferred console. For Linux on System z, the default preferred console device is \fBttyS0\fP. . . .SS "Restricting terminal connections using a z/VM user ID filter" The z/VM IUCV HVC device driver can filter incoming connection requests based on the user ID of the originating z/VM guest virtual machine. The z/VM user ID filter is specified as a comma separated list of user IDs for the \fBhvc_iucv_allow\fP kernel parameter. If an user ID ends with an asterisk (*), only the characters up to the asterisk must match. You can use the asterisk to match multiple z/VM user IDs. The kernel parameter can be set initially on the kernel command line or through a sysfs attribute at runtime. For changing the parameter value at runtime, see the .BR chiucvallow (8) command. If the z/VM user ID filter is changed at runtime, the new filter applies only to new incoming connection requests. Existing terminal connections remain active until they get closed. Any re-connection attempt is subject to the new z/VM user ID filter. .TP .B Note: If z/VM user ID filtering is active, connection attempts from the z/VM guest virtual machine to itself are also subject to filtering. If the local z/VM user ID is not listed, local connection requests are refused. . . .SS "Configuring HVC terminals for user logins" Typically, \fBgetty\fP programs initialize terminals and prepare terminals for user logins. For each HVC terminal device, a \fBgetty\fP program must be started. To configure and start \fBgetty\fP on a HVC terminal, open .BR /etc/inittab (5), and add a new entry similar to this one: .ft CW .in +0.25in .nf h0:2345:respawn:/sbin/mingetty hvc0 .fi .in -0.25in .ft Add new entries for each HVC terminal device that is designated for user logins. You can use .BR ttyrun (8) to start a getty program on each HVC terminal device and to prevent respawns through the .BR init (8) program when a HVC terminal is not available. You can then change the number of HVC terminal devices without adding or removing entries every time. See .BR inittab (5) about the format of \fBinittab\fP entries; see .BR getty (8), or .BR mingetty (8) for options that are related to terminal setup and user login. . . .SS "Setting up HVC terminals for root logins" To allow root logins on HVC terminals, ensure that the respective terminal device names are listed in .BR /etc/securetty (5)\fR.\fP HVC terminal device names start with \fBhvc\fP followed by an integer in the range 0 to 7. . . .SS "Setting up the terminal environment" The terminal environment is changed by assigning the terminal name to the \fBTERM\fP environment variable. This can be done manually, or by a getty program. Complete the following steps for setting up the terminal environment for a z/VM IUCV HVC terminal: .RS 4 .IP "1." 4 To display the current terminal name, issue the command: .ft CW .in +0.25in .nf user@host:~$ echo $TERM xterm .fi .in -0.25in .ft .IP "2." 4 To connect to a z/VM IUCV HVC terminal, run .BR iucvconn (1) and log in as usual: .ft CW .in +0.25in .nf user@host:~$ iucvconn MYLNX01 lnxhvc0 ... user@MYLNX01:~$ .fi .in -0.25in .ft .IP "3." 4 To assign the terminal name from step 1. to the terminal environment variable, issue the following command: .ft CW .in +0.25in .nf user@MYLNX01:~$ export TERM=xterm .fi .in -0.25in .ft .RE If unsure which terminal name to use, assign "\f(CWlinux\fP" to the terminal environment variable. .PP For getty programs, a sample terminal environment configuration might look like: .ft CW .in +0.25in .nf h0:2345:respawn:/sbin/agetty -L 9600 hvc0 linux .fi .in -0.25in .ft The terminal name typically follows the terminal device name. In the example, .BR agetty (8) sets the terminal environment variable to "linux" before .BR login (1) is started. Some getty programs do not support the terminal name parameter, see the manual page of the respective getty program for more information. . . . .SH "DIAGNOSTICS" The z/VM IUCV hypervisor console device driver uses the kernel message infrastructure for its messages. The message prefix is \fBhvc_iucv\fP. . . . .SH "SEE ALSO" .BR iucvconn (1), .BR chiucvallow (8), .BR af_iucv (7), .BR bootparam (7), .BR inittab (5), .BR getty (8), .BR agetty (8), .BR mingetty (8), .BR ttyrun (8), .BR login (1), .BR securetty (5) See .BR kernel-parameters.txt " and" .BR sysrq.txt of the Linux kernel documentation for more information about kernel boot parameters and the magic sysrequest function. .I "Linux on System z - Device Drivers, Features, and Commands" s390-tools-2.38.0/iucvterm/doc/iucvconn.1000066400000000000000000000146211502674226300177640ustar00rootroot00000000000000.\" iucvconn.1 .\" .\" .\" Copyright IBM Corp. 2008, 2017 .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" ---------------------------------------------------------------------- .TH iucvconn "1" "March 2009" "s390-tools" "IUCV terminal applications" . .ds i \fBiucvconn\fP . . . .SH NAME iucvconn \- start terminal connection over z/VM IUCV . . . .SH SYNOPSIS .B iucvconn .RB [ \-h | \-\-help ] .br .B iucvconn .RB [ \-v | \-\-version ] .br .B iucvconn .RB [ \-e | \-\-escape-char .IR escape_char ] .RB [ \-s | \-\-sessionlog .IR log_file ] .I vm_guest .I terminal_id . . . .SH DESCRIPTION The \*i application establishes a terminal connection to another Linux instance. The Linux instance where \*i runs and the target Linux instance must be z/VM guest operating systems of the same z/VM instance. The communication path between both virtual machines is based on the z/VM Inter-User Communication Vehicle (IUCV). Because z/VM IUCV is independent from TCP/IP, Linux instances with no external network can be accessed. .PP \*i accesses the terminal specified by \fIterminal_id\fP on the z/VM guest virtual machine \fIvm_guest\fP. The \fIterminal_id\fP is similar to a port number in TCP/IP communications. . \fIterminal_id\fP is case-sensitive and consists of up to 8 alphanumeric characters. \fIterminal_id\fP must either identify a running \fBiucvtty\fP instance, or a terminal device provided by the z/VM IUCV hypervisor console (HVC) device driver. . . . .SH OPTIONS .TP .BR \-h ", " \-\-help Display a short help text, then exit. . .TP .BR \-v ", " \-\-version Display the version number of \*i, then exit. . .TP .BR \-e ", " \-\-escape-char\~\fIescape_char\fP Set an escape character for this terminal session. You need an escape character to access special \*i functions. The default escape character is underscore (_). If \fIescape_char\fP is set to "none", escaping is not possible. The escape character can be the closing bracket (]), the caret (^), the underscore (_), or any alphabetical character except C, D, Q, S and Z. The \fIescape_char\fP character is not case-sensitive. To invoke a \*i function, press Ctrl\^+\^\fIescape_char\fP, followed by the function character. For example, press "Ctrl\^+\^_ d" to disconnect the terminal session and exit \*i. For details, see section "ESCAPE CHARACTERS" below. . .TP .BR \-s ", " \-\-sessionlog\~\fIlogfile\fP Create a transcript of the terminal session and write session data to three different files. \fIlog_file\fP contains the raw terminal data stream. The .I log_file\fB.timing\fP file contains timing data that can be used for replaying the raw terminal data stream using realistic output delays. Additional terminal session information is logged to .IR log_file\fB.info\fP "." If any of these files already exist, \*i exits with an error. To proceed either delete the files or choose another file name for \fIlog_file\fP. . . . .SH "ESCAPE CHARACTERS" \*i supports functions through the use of an escape character. The default escape character (\fIescape_char\fP) is underscore (_). To enter the "escape mode" of \*i, press the Control and the \fIescape_char\fP key together ("Ctrl\^+\^_") In the "escape mode", \*i expects a single character designating the special function. The special functions are (assuming \fIescape_char\fP is set to underscore (_)): .RS 4 .IP "\fBCtrl\^+\^_\fP" 8 Send the escape character to the connected terminal. .IP "\fB.\fP or \fBd\fP" 8 Disconnect and exit \*i. .IP "\fBr\fP" 8 Force resizing of the connected terminal. .RE .PP Any other single character is ignored and is not send to the terminal. If multiple characters are entered together, \*i leaves the "escape mode" and sends the characters to the connected terminal. . .\" FIXME: change "connected terminal" to terminal . . .SH "EXAMPLES" To access the "lnxterm" terminal on the Linux instance in z/VM guest virtual machine LNXSYS01: .PP .ft CW .in +0.25in .nf iucvconn LNXSYS01 lnxterm .fi .in -0.25in .ft .PP To access the first z/VM IUCV HVC terminal on the Linux instance in z/VM guest virtual machine LNXSYS02: .PP .ft CW .in +0.25in .nf iucvconn LNXSYS02 lnxhvc0 .fi .in -0.25in .ft .PP To create a transcript of the terminal session to the Linux instance in z/VM guest virtual machine LNXSYS99: .PP .ft CW .in +0.25in .nf iucvconn -s ~/transcripts/lnxsys99 LNXSYS99 lnxhvc0 .fi .in -0.25in .ft . . . .SH ENVIRONMENT .TP .B TERM The terminal environment variable contains the name of the terminal (e.g. linux, xterm, ...) and also specifies the terminal capabilities. \*i transfers the value of the \fBTERM\fP environment variable at connection startup to the target system. The \fBiucvtty\fP application sets the \fBTERM\fP environment variable on the target system before the login program is started. Setting the \fBTERM\fP environment variable ensures that the terminal capabilities are the same on both sides of the connection. The z/VM IUCV hypervisor console (HVC) device driver ignores the content of the \fBTERM\fP environment variable transferred by \*i. See the .BR hvc_iucv (9) manual page for instructions to configure the terminal environment. . . . .SH DIAGNOSTICS If the Linux kernel does not include kernel support for the AF_IUCV network addressing family, \*i exits and displays the message .I 'AF_IUCV address family is not available: Address family not supported by .IR protocol' "." . . . .SH SECURITY .SS Linux The \*i program does not require superuser privileges and can be used by regular users. Each connection attempt and failure is logged to the .BR authpriv syslog facility. .\".PP .\"Depending on the login program started by the \fBiucvtty\fP program, .\"the user must authenticate to the remote system. .PP Terminal session transcripts facilitate logging and auditing of terminal activity on remote systems or recording and playing back terminal sessions, for example for education and presentations. . . .SS z/VM guest virtual machine The z/VM guest virtual machine must have authorization to establish IUCV communication paths to other z/VM guest virtual machines. See the .BR af_iucv (7) manual page for details about IUCV authorizations. . . . .SH NOTES The Linux instance where \*i runs and the target Linux instances must be z/VM guest operating systems of the same z/VM instance. . . . .SH "SEE ALSO" .BR iucvtty (1), .BR hvc_iucv (9), .BR af_iucv (7), .BR scriptreplay (1) .I "Linux on System z - Device Drivers, Features, and Commands" s390-tools-2.38.0/iucvterm/doc/iucvtty.1000066400000000000000000000117161502674226300176510ustar00rootroot00000000000000.\" iucvtty.1 .\" .\" .\" Copyright IBM Corp. 2008, 2017 .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" ---------------------------------------------------------------------- .TH iucvtty "1" "March 2009" "s390-tools" "IUCV terminal applications" . .ds t \fBiucvtty\fP .ds i \fBiucvconn\fP . . . .SH NAME iucvtty \- allow remote logins over z/VM IUCV . . . .SH SYNOPSIS .B iucvtty .RB [ \-h | \-\-help ] .br .B iucvtty .RB [ \-v | \-\-version ] .br .B iucvtty .RB [ \-a | \-\-allow-from .IR regex ] .IR terminal_id .RB [\-\- .IR login_program " [" login_options ]] . . . .SH DESCRIPTION The \*t application provides full-screen terminal access to a Linux instance running as a z/VM guest operating system. The \*i application is used to connect to a running \*t instance. The terminal connection is based on the z/VM Inter-User Communication Vehicle (IUCV). z/VM IUCV establishes a communication path between two z/VM guest virtual machines on the same z/VM instance. Because z/VM IUCV is independent from TCP/IP, Linux instances with no external network can be accessed. \fIterminal_id\fP identifies the z/VM IUCV connection and is similar to a port number in TCP/IP communications. \fIterminal_id\fP is case-sensitive and consists of up to 8 alphanumeric characters. It must be specified as a parameter in connection requests against a \*t instance. When a connection is established, \*t starts a login program. When the login program ends, \*t also exits. Consider an .BR inittab (5) entry .\"or an upstart job file to ensure that \*t is restarted and ready for the next terminal session. . . . .SH OPTIONS .TP .BR \-\^h ", " \-\^\-help Display a short help text and exit. . .TP .BR \-\^v ", " \-\^\-version Display the version number of \*t, then exit. . .TP .BR \-\^a ", " \-\^\-allow-from " " \fIregex\fP Limit permissions for incoming connections to z/VM user IDs that match the regular expression \fIregex\fP. The connection is refused if the ID does not match. If this parameter is omitted, connections are permitted from any z/VM user ID. . .TP .I login_program \fIlogin_program\fP specifies the absolute path to the login program to be started when a connection is established. The default is .BR /bin/login (1). .br The \fIlogin_program\fP must be separated by two dashes (--). . .TP .I login_options Depending on the particular login program used, \fIlogin_options\fP specifies additional options. . . . .SH EXAMPLES To allow remote logins using the terminal identifier "lnxterm": .PP .ft CW .in +0.25in .nf iucvtty lnxterm .fi .in -0.25in .ft To only allow users from LNXSYS01 to connect to terminal "lnxterm": .PP .ft CW .in +0.25in .nf iucvtty -a LNXSYS01 lnxterm .fi .in -0.25in .ft To only allow users from LNXSYS10 through LNXSYS19 to connect to terminal "lnxterm": .PP .ft CW .in +0.25in .nf iucvtty -a "LNXSYS1[0-9]" lnxterm .fi .in -0.25in .ft To use \fB/sbin/sulogin\fP instead of \fB/bin/login\fP for terminal "suterm": .PP .ft CW .in +0.25in .nf iucvtty suterm -- /sbin/sulogin .fi .in -0.25in .ft An entry in \fB/etc/inittab\fP to facilitate user logins on terminal "lnxterm" with \fB/bin/login\fP could be: .PP .ft CW .in +0.25in .nf t1:2345:respawn:/usr/bin/iucvtty lnxterm .fi .in -0.25in .ft An entry in \fB/etc/inittab\fP to facilitate user logins on terminal "suterm" with \fB/sbin/sulogin\fP in single user mode could be: .PP .ft CW .in +0.25in .nf s1:S:respawn:/usr/bin/iucvtty suterm -- /sbin/sulogin .fi .in -0.25in .ft . . . .SH DIAGNOSTICS If the Linux kernel does not include kernel support for the AF_IUCV network addressing family, \*t exits and displays the message .I 'AF_IUCV address family is not available: Address family not supported by .IR protocol' "." . . . .SH SECURITY .SS Linux The \*t program can be used by regular users. Depending on the particular login program, \*t must be started with superuser privileges for user authentication and authorization. For instance, \fB/bin/login\fP requires superuser privileges and, thus, regular users must use a different login program. Each connection attempt is logged to the \fBauthpriv\fP syslog facility. \*t uses pseudo-terminal (pts) devices to communicate with the login program. For security reasons, some login programs, like \fB/bin/login\fP, do not permit root logins on pseudo-terminal devices (see also .BR /etc/securetty "(5))." To permit root logins, consider using HVC terminal devices that are provided by the z/VM IUCV hypervisor console (HVC) device driver. .\"Enabling root logins on pseudo-terminal devices can compromise system security. .\"To avoid this potential security exposure, consider using HVC terminal devices .\"that are provided by the z/VM IUCV hypervisor console (HVC) device driver. . . . .SS z/VM guest virtual machine See the .BR af_iucv (7) manual page for details about IUCV authorizations. . . . .SH "SEE ALSO" .BR iucvconn (1), .BR login (1), .BR pts (4), .BR regex (7), .BR securetty (5), .BR af_iucv (7), .BR hvc_iucv (9) s390-tools-2.38.0/iucvterm/doc/ts-shell.1000066400000000000000000000404451502674226300176760ustar00rootroot00000000000000.\" ts-shell.1 .\" .\" .\" Copyright IBM Corp. 2008, 2017 .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" ---------------------------------------------------------------------- .TH "ts-shell" "1" "March 2009" "s390-tools" "Terminal Server over IUCV" . . . .SH "NAME" ts\-shell \- Login shell for terminal servers over z/VM IUCV . . . .SH "SYNOPSIS" .B ts\-shell .RB [ \-h | \-\-help ] .br .B ts\-shell .RB [ \-v | \-\-version ] . . . .SH "DESCRIPTION" \fBts-shell\fP is a login shell for terminal server environments using the IUCV terminal applications. \fBts-shell\fP authorizes Linux users based on user names and group memberships for accessing terminals. Linux users can list the authorizations and access terminals. If a user is authorized to access a terminal, \fBts-shell\fP establishes the terminal connection using the .BR iucvconn (1) program. Apart from \fBiucvconn\fP the IUCV terminal applications include \fBiucvtty\fP. .BR iucvtty (1) provides full-screen terminal access to a Linux instance running as a z/VM guest operating system. .BR iucvconn (1) can also establish terminal connections to z/VM IUCV hypervisor console (HVC) device drivers. The Hypervisor Console (HVC) is a generic TTY device driver for the Linux kernel providing terminals. One of the terminals can be used as the Linux console. The Linux instances where \fBts-shell\fP and \fBiucvconn\fP run and the target Linux instance must be z/VM guest operating systems of the same z/VM instance. Because z/VM IUCV is independent from TCP/IP, you can access Linux instances with no external network connection. . . . . .SH "OPTIONS" .TP .BR \-\^h ", " \-\^\-help Display a short help text, then exit. . .TP .BR \-\^v ", " \-\^\-version Display the version information, then exit. . . . .SH "USAGE" .SS "Terminal server shell commands" The terminal server shell provides the following commands: .PP .TP 4 .B list The \fBlist\fP command lists z/VM guest virtual machines to which the Linux user is authorized to connect. The output of the \fBlist\fP command depends on the configured authorization method which can be "list" or "regex". The available authorization methods are explained in section "Configure terminal authorization for Linux users". The output for "list" authorization is a list of z/VM guest virtual machines, for example: .ft CW .in +0.25i .nf user@ts-shell> list guest1 guest2 guest3 guest5 .fi .in -0.25i .ft The output for "regex" authorization is a list of one or more regular expressions, for example: .ft CW .in +0.25i .nf user@ts-shell> list Regular expressions for your authorization: (?i-xsm:lnx\\w{5}) (?i-xsm:^palim$) .fi .in -0.25i .ft If \fBts-shell\fP is configured to connect to particular z/VM guest virtual machines only, the output for "regex" authorization is followed by a list of the user IDs that match at least one of the regular expressions: .ft CW .in +0.25i .nf user@ts-shell> list Regular expressions for your authorization: (?i-xsm:lnx\\w{5}) (?i-xsm:^palim$) You are authorized to connect to these z/VM guest virtual machines: LNXSYS42 LNXSYS01 .fi .in -0.25i .ft . .TP 4 .B connect \fIvm_guest\fP \fR[\fP\fIterminal_id\fP\fR]\fP \fBconnect\fP establishes a terminal connection to a particular z/VM guest virtual machine specified as \fIvm_guest\fP. \fIvm_guest\fP consists of up to eight alphanumeric characters. An optional terminal identifier can be specified with \fIterminal_id\fP. If not specified, the default terminal identifier is used. To change the default terminal identifier, use the \fBterminal\fP command. In the following example, a user opens a terminal connection to the Linux instance in z/VM guest virtual machine LNXSYS01: .ft CW .in +0.25i .nf user@ts-shell> connect LNXSYS01 ts-shell: Connecting to LNXSYS01 (terminal identifier: lnxterm)... ... ts-shell: Connection ended .fi .in -0.25i .ft . .TP 4 .B terminal \fR[\fP\fIidentifier\fP\fR]\fP The \fBterminal\fP command displays or sets the default terminal identifier that is used by subsequent \fBconnect\fP commands. \fIidentifier\fP is case-sensitive and consists of up to eight alphanumeric characters. If \fBterminal\fP is called with the \fIidentifier\fP being specified, \fIidentifier\fP is set as the new default terminal identifier. If \fIidentifier\fP is not specified, the current default terminal identifier is displayed: .ft CW .in +0.25i .nf user@ts-shell> terminal lnxterm .fi .in -0.25i .ft . .TP 4 .BR quit ", " exit Exit the terminal server shell session. . .TP 4 .B help Display the help about terminal server shell commands. . .TP 4 .B version Display the \fBts-shell\fP version. . . . .SH "CONFIGURATION" To set up a Linux system as a terminal server and to use \fBts-shell\fP for Linux users, complete the following configuration steps: .IP "1." 4 Authorize the terminal server z/VM guest virtual machine for IUCV. .IP "2." 4 Create a terminal server shell configuration file. .IP "3." 4 List z/VM guest virtual machines providing terminal access over IUCV. .IP "4." 4 Configure terminal session transcripts. .IP "5." 4 Configure terminal authorizations for Linux users. .IP "6." 4 Install \fBts-shell\fP as the login shell for Linux users. . . .SS "Authorize the terminal server z/VM guest virtual machine for IUCV" The z/VM guest virtual machine on which the terminal server shell runs needs particular authorization to establish IUCV communication paths to other z/VM guest virtual machines. A typical \fBIUCV\fP authorization statement in the z/VM directory entry of the terminal server z/VM guest virtual machine might be: .PP .ft CW .in +0.25in .nf IUCV ANY OPTION MAXCONN 256 .fi .in -0.25in .ft .PP The example allows the terminal server shell to establish IUCV communication paths with any z/VM guest virtual machine. The number of IUCV connections is limited to 256. See the .BR af_iucv (7) manual page for further details. . . .SS "Create a terminal server shell configuration file" When \fBts-shell\fP starts, it reads its configuration from the \fB/etc/iucvterm/ts-shell.conf\fP configuration file. The file contains configuration options that specify further configuration files with lists of z/VM guest virtual machines and terminal authorization definitions. .PP Supported configuration options (with default settings) are: .RS 4 .TP .BR ts-systems " = " \fI/etc/iucvterm/ts-systems.conf\fP The \fBts-systems\fP configuration option specifies a file that lists z/VM guest virtual machines. \fBts-shell\fP permits connections to these z/VM guest virtual machines only. See also section "List z/VM guest virtual machines providing terminal access over IUCV". . .TP .BR ts-authorization " = " \fI/etc/iucvterm/ts-authorization.conf\fP The \fBts-authorization\fP option specifies a file containing the terminal authorization definitions for Linux users. See section "Configure terminal authorization for Linux users" about the file format. . .TP .BR transcript-systems " = " \fI/etc/iucvterm/ts-audit-systems.conf\fP The \fBtranscript-systems\fP option specifies a file that lists z/VM guest virtual machines for which terminal sessions are logged. See section "Configure terminal session transcripts" for details. . .TP .BR transcript-directory " = " \fI/var/log/ts-shell\fP The \fBtranscript-directory\fP option specifies a directory where the terminal session transcripts are saved. See section "Configure terminal session transcripts" for details. . .RE . . .SS "List z/VM guest virtual machines providing terminal access over IUCV" \fBts-shell\fP establishes terminal connections only if a Linux user has been authorized. In some cases, the administrator might want to explicitly restrict connections to particular z/VM guest virtual machines independent of the user. The \fBts-systems\fP configuration option specifies a file that lists z/VM guest virtual machines to which \fBts-shell\fP is permitted to connect. The file lists each z/VM guest virtual machine on a separate line. If a line contains "[*ALL*]", \fBts-shell\fP is permitted to connect to any z/VM guest virtual machine. . .TP .B Note: The \fBts-systems\fP options applies to the \fBts-shell\fP program only. If necessary, further restrictions can be configured for the z/VM guest virtual machine itself using the \fBIUCV\fP z/VM directory statement. See the section about IUCV authorizations in the .BR af_iucv (7) manual page. . . .SS "Create lists of z/VM guest virtual machines" A convenient method for creating lists of z/VM guest virtual machines is to use the information from the z/VM user directory, which contains all the names of the z/VM guest virtual machines that are defined on a z/VM operating system instance. For example, to create a list of all z/VM guest virtual machines with names that start with "LINUX" and are followed by digits, use: .ft CW .in +0.25in .nf vmur receive -H -t 1234 -O |grep -E "^USER LINUX[0-9]+" |cut -d" " -f2 .fi .in -0.25in .ft Spool ID 1234 refers to the z/VM user directory file in the z/VM virtual reader device. .br The output of the command can be saved in a file. The file can then be specified for the .BR ts-systems " or " transcript-systems configuration options in the \fBts-shell.conf\fP file. In addition, use these files to configure list authorizations. . . .SS "Configure terminal session transcripts" \fBts-shell\fP can create transcripts of terminal sessions to z/VM guest virtual machines. The \fBts-audit-systems.conf\fP configuration file lists z/VM guest virtual machines for which terminal sessions are logged. If the file contains "[*ALL*]", each terminal session is logged. To create a list of z/VM guest virtual machines, see section "Create lists of z/VM guest virtual machines". For saving the terminal session transcripts, \fBts-shell\fP requires a directory that is specified by the \fBtranscript-directory\fP option in the \fBts-shell.conf\fP configuration file. .TP .B Note: The terminal session transcript directory must be writable by all \fBts-shell\fP users. The system administrator might use a "ts-shell" group containing all \fBts-shell\fP users as members. The directory can be made writable for the "ts-shell" group only. .PP \fBts-shell\fP uses a combination of the Linux user name, z/VM guest virtual machine and a time stamp for creating new terminal session transcript files. The format is as follows: .br .RS 4 .RI "/var/log/ts-shell/" user_name "/" VMGUEST "_" YY "-" MM "-" DD "-" HHMMSS .RE .PP Terminal session transcripts consist of three different files: the raw terminal data stream, timing data information and connection information. See .BR iucvconn (1) for more details about terminal session transcripts. . . .SS "Configure terminal authorizations for Linux users" \fBts-shell\fP performs authorization checks for Linux users before connecting to z/VM guest virtual machines. The authorization configuration grants Linux users or groups to establish terminal connections only to particular z/VM guest virtual machines. These authorization definitions are stored in the \fBts-authorization.conf\fP configuration file. This configuration file consists of authorization mappings where mappings can be created for Linux users or groups. For the specification of z/VM guest virtual machines, a list or regular expression is used. .br A Linux user is referenced by the user name; a Linux group is referenced by the group name and prefixed with "@". Here is an example of a Linux user and group authorization: .PP .ft CW .in +0.25in .nf alice = list:guest01,guest02 @users = list:guest03,guest04 .fi .in -0.25in .ft .PP To create lists of z/VM guest virtual machines, use the following prefixes: .RS 4 .IP "\fIlist:\fP" 8 followed by a comma-separated list of names. . .IP "\fIfile:\fP" 8 followed by a file path. The file lists z/VM guest virtual machines, each name on a separate line. .RE . .PP The following example shows the usage of the \fIfile:\fP prefix: .PP .ft CW .in +0.25in .nf @testgrp = file:/etc/iucvterm/auth/test-systems.list @prodgrp = file:/etc/iucvterm/auth/production-systems.list .fi .in -0.25in .ft .PP See section "Create lists of z/VM guest virtual machines" above about creating lists of z/VM guest virtual machines with names that match a specific pattern. . .PP Instead of listing each z/VM guest virtual machine individually, regular expressions can be used to match names of z/VM guest virtual machines. If naming schemes exist for z/VM guest virtual machines, using regular expressions might be more efficient and allow for future additions. .br The \fIregex:\fP prefix starts the definition of a regular expression to match the names of z/VM guest virtual machines. The regular expression must be a Perl-compatible or an extended regular expression (ERE) as documented in POSIX. Basic regular expressions (BRE) cannot be used. See .BR regex (7) for POSIX extended regular expressions; and the Perl reference manual .BR perlre about regular expression in Perl. To authorize user bob for all z/VM guest virtual machines with names that start with "lnx" and are followed with at least three but not more than five alphanumeric characters, use: .PP .ft CW .in +0.25in .nf bob = regex:lnx\\w{3,5} .fi .in -0.25in .ft .PP . If a naming scheme exists for z/VM guest virtual machines belonging to the test or production environment: authorize all users in the "testgrp" group for all systems in the test environment; and respectively, authorize all users in the "prodgrp" group for all systems in the production environment: .PP .ft CW .in +0.25in .nf @testgrp = regex:test\\w+ @prodgrp = regex:prod\\w+ .fi .in -0.25in .ft . .PP You can have multiple authorizations for the same user, either directly through user authorizations or indirectly through authorizations for groups that the user is a member of. Be aware that \fBts-shell\fP accepts only one type of authorization, list or regex, for a particular user. The first type of authorization that is found for a user sets the authorization type for this user. Further authorizations of the same type are accumulated. Authorizations of the other type are ignored. Example: .PP .ft CW .in +0.25in .nf @users = list:guest01,guest03,guest05 alice = list:guest02,guest04 eve = regex:guest0[7890] .fi .in -0.25in .ft .PP If both alice and eve are members of group users, alice is authorized for guest01, guest02, guest03, guest04, and guest05. For eve, the regular expression is ignored and the authorizations are for guest01, guest03, guest05 as defined for the group. . . . .SS "Install ts-shell as login shell for Linux users" To use the \fBts-shell\fP as the login shell for Linux users, follow these steps: .IP "1." 4 Add the path of the \fBts-shell\fP program to the \fI/etc/shells\fP file that contains the list of valid login shells: .PP .ft CW .in +0.25in .nf echo $(which ts-shell) >> /etc/shells .fi .in -0.25in .ft .PP . .IP "2." 4 Change the login shell of a particular Linux user using the .BR chsh (1) program: .PP .ft CW .in +0.25in .nf chsh -s $(which ts-shell) alice .fi .in -0.25in .ft . . . .SH "FILES" .TP .B /etc/iucvterm/ts-shell.conf General terminal server shell configuration file. . .TP .BR /etc/iucvterm/ts-systems.conf ", " /etc/iucvterm/unrestricted.conf The \fBts-systems.conf\fP file lists z/VM guest virtual machines to which connections are permitted. \fBunrestricted.conf\fP contains "[*ALL*]" to permit .BR ts-shell (1) to connect to any z/VM guest virtual machine. The \fBts-systems\fP configuration option in the \fBts-shell.conf\fP file might specify one of these files. . .TP .B /etc/iucvterm/ts-authorization.conf The \fBts-authorization.conf\fP file grants Linux users or groups to establish terminal connections only to particular z/VM guest virtual machines. . .TP .B /etc/iucvterm/ts-audit-systems.conf The \fBts-audit-systems.conf\fP file lists z/VM guest virtual machines for which terminal sessions are logged. . .TP .B /var/log/ts-shell Directory for saving terminal session transcripts. . . . .SH "ENVIRONMENT" .TP .B PAGER The \fBPAGER\fP environment variable designates a program used as pager for the \fBlist\fP command of the terminal server shell. If \fBPAGER\fP is not set or empty, .BR less (1) is used. . .TP .B LESSSECURE \fBts-shell\fP sets this variable to run .BR less (1) in "secure" mode. See the SECURITY section in the .BR less (1) man page. . . . .SH "SEE ALSO" .BR iucvconn (1), .BR iucvtty (1), .BR af_iucv (7), .BR less (1), .BR chsh (1), .BR shells (5), .BR regex (7), .BR perlre .I "Linux on System z - Device Drivers, Features, and Commands" .br .I "z/VM CP Planning and Administration" s390-tools-2.38.0/iucvterm/doc/ts-shell/000077500000000000000000000000001502674226300176055ustar00rootroot00000000000000s390-tools-2.38.0/iucvterm/doc/ts-shell/README.ts-shell000066400000000000000000000107321502674226300222220ustar00rootroot00000000000000Terminal server setup ===================== This README file provides additional information for configuring a terminal server. Installation overview --------------------- The s390-tools package installs the ts-shell(1) program along with a set of configuration files in `/etc/iucvterm`. The ts-shell program is pre-configured to connect to any other z/VM guest virtual machines (if the z/VM guest virtual machine has the respective IUCV authorizations). The administrator can change the ts-shell configuration to restrict connections to particular z/VM guest virtual machines only. There are no user authorizations by default. The administrator must edit `/etc/iucvterm/ts-authorization.conf` to assign authorizations. See the manual page for ts-shell(1) and the `authorization-sample.conf` file in the documentation directory for configuration examples. In addition, the documentation directory also contains the iucvconn_on_login sample program that is an alternative to ts-shell. Setup considerations for the terminal server shell (ts-shell) ------------------------------------------------------------- Adding new ts-shell users ~~~~~~~~~~~~~~~~~~~~~~~~~ The ts-shell installation creates a system group ts-shell. If you intend to use ts-shell as a login shell for users, ensure that these users are all members of ts-shell. To add existing users to group ts-shell, use +usermod -G ts-shell 'username'+. The ts-shell configuration files and `/var/log/ts-shell` are readable only by members of the *ts-shell* group. Enabling terminal session transcripts ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ts-shell(1) can be configured to create transcripts of terminal sessions to particular z/VM guest virtual machines. The transcripts are written to log files in the `/var/log/ts-shell` directory. NOTE: The `/var/log/ts-shell` directory permission has the set-group-ID bit set. Sub-directories that are created by different users will inherit the group ownership of the `/var/log/ts-shell` directory. See the ts-shell(1) manual page for more information about terminal session transcripts. Setting up command completion (optional) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *ts-shell* supports completion for commands and, if possible, for z/VM guest virtual machines. The completion is triggered using the 'Tab' key. Command completion is an optional feature. To enable command completion, install one of the following Perl ReadLine libraries: - Term::ReadLine::Gnu - Term::ReadLine::Perl Check your Linux distribution for packages that provide these Perl ReadLine interfaces. Setup a terminal server using the iucvconn_on_login program ----------------------------------------------------------- *iucvconn_on_login* is a sample script that uses the iucvconn(1) program to establish an IUCV terminal connection to a Linux instance that runs as z/VM guest operating system. It can be used as an alternative to set up a terminal server. The idea of iucvconn_on_login is to establish a terminal connection at login time. The z/VM guest virtual machine to which iucvconn_on_login will connect is the name of the Linux user that logs in. For example, if you log in as Linux user "lxguest1", iucvconn_on_login is started and establishes a terminal connection to the z/VM guest virtual machine lxguest1. Adding new users for z/VM guest virtual machines ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To set up a terminal server using the iucvconn_on_login program, create a new Linux user for each z/VM guest virtual machine using the z/VM user ID as name. Set the login shell of those users to *iucvconn_on_login*. NOTE: User names are case-sensitive in Linux. To ensure consistency, always use either lower or upper case characters for your user names. To create a new user for z/VM guest virtual machine LXGUEST1 ---- useradd -s /usr/bin/iucvconn_on_login lxguest1 ---- If you log in as user 'lxguest1' on the terminal server (for example using ssh(1)), *iucvconn_on_login* immediately establishes the terminal connection to the z/VM virtual guest virtual machine 'lxguest1'. To access the default terminal ('lnxhvc0') on the Linux instance in z/VM guest virtual machine LXGUEST1 ---- ssh lxguest1@terminal.server ---- To access the terminal 'lnxterm' on the Linux instance in z/VM guest virtual machine LXGUEST1 ---- ssh -t lxguest1@terminal.server lnxterm ---- For ssh(1), you must use the "-t" parameter if you want to specify an alternate terminal identifier. The "-t" parameter ensures that ssh starts iucvconn_on_login on a terminal. s390-tools-2.38.0/iucvterm/doc/ts-shell/authorization-sample.conf000066400000000000000000000014331502674226300246340ustar00rootroot00000000000000# Terminal server authorization configuration # # See ts-shell(1) manual page for file format. ## Examples -- System lists # alice is authorized to connect to guest1 .. guest5 alice = list:guest1,guest2,guest3,guest4,guest5 ## bob is authorized to connect to systems listed in the file "test-systems.list" bob = file:/etc/iucvterm/systems/test-systems.list # authorize all members of the Linux 'admins' group to connect to systems # listed in the file "admin-systems.list" @admins = file:/etc/iucvterm/groups/admin-systems.list ## Examples -- Regular expressions # eve has authorization to connect to systems with # a) names starting with 'guest', followed by exactly 2 digits # b) names starting with 'lnx', followed by 5 alphanumeric characters eve = regex:(?:guest\d{2}|lnx\w{5}) s390-tools-2.38.0/iucvterm/doc/ts-shell/iucvconn_on_login000077500000000000000000000022541502674226300232460ustar00rootroot00000000000000#! /bin/sh # # iucvconn_on_login - start terminal connection at login # # Shell script to connect to a Linux guest operation system # using the iucvconn(1) program. # The z/VM guest virtual machine to which iucvconn_on_login will connect # is the name of the Linux user that logs in. # # Copyright IBM Corp. 2008, 2017 # # s390-tools is free software; you can redistribute it and/or modify # it under the terms of the MIT license. See LICENSE for details. # prog_name=`basename $0` guest_name=${USER:-`whoami 2>/dev/null`} terminal=lnxhvc0 iucvconn=`command -v iucvconn 2>/dev/null` __error() { printf "$prog_name: $@\n" >&2 exit 1 } # check if we have been called with -c to specify an alternate # terminal identifier. This can be used by ssh: "ssh -t guest@ts my_term" case "$1" in -c) test -n "$2" && terminal=$2 ;; esac test -t 1 || __error "The $prog_name program requires a terminal to run on" test "x$guest_name" = x && \ __error "Failed to determine the target z/VM guest virtual machine" test -x "$iucvconn" || __error "Failed to run the 'iucvconn' program" printf "$prog_name: Connecting to $guest_name (terminal ID: $terminal)\n\n" exec $iucvconn $guest_name $terminal s390-tools-2.38.0/iucvterm/doc/ts-shell/sample-system.list000066400000000000000000000004651502674226300233120ustar00rootroot00000000000000# Terminal server shell -- system list # # This file lists z/VM guest virtual machines, each name on a separate line. # Empty lines or lines starting with '#' are ignored. # See ts-shell(1) for more information. # #------|<--------------------------------------------------- LNXSYSA LNXSYSB LNXSYS01 LNXSYS42 s390-tools-2.38.0/iucvterm/doc/ttyrun.8000066400000000000000000000070331502674226300175130ustar00rootroot00000000000000.\" ttyrun.8 .\" .\" .\" Copyright IBM Corp. 2010, 2017 .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" ------------------------------------------------------------------------- .TH "ttyrun" "8" "December 2011" "s390-tools" "System Management Commands" . .ds s ttyrun . . .SH NAME ttyrun \- Start a program if a specified terminal device is available . . . .SH SYNOPSIS .B \*s .RB [ \-V | \-\-verbose ] .RB [ \-e | \-\-exitstatus .IR status ] .I term .I program .RI [ "program_options" ] .br .B \*s .RB [ \-h | \-\-help ] .br .B \*s .RB [ \-v | \-\-version ] . . . .SH DESCRIPTION \fB\*s\fP is typically started during system initialization and is used to prevent a respawn through the .BR init (8) program when a terminal is not available. \fIterm\fP is the name of the terminal device and is a path relative to the \f(CW/dev\fP directory, for example, specify \f(CWhvc0\fP for \f(CW/dev/hvc0\fP. .br If the specified terminal device can be opened, \fB\*s\fP starts the specified program. If the terminal device cannot be opened, the behavior of \fB\*s\fP depends on the \fB\-e\fP option: . .RS 2 .IP "\(bu" 2 If the \fB\-e\fP option has been specified, \fB\*s\fP exits with the specified return value, or .IP "\(bu" 2 If the \fB\-e\fP option has not been specified, \fB\*s\fP sleeps until it receives a signal that causes an exit. .RE .PP \fIprogram\fP is an absolute path to the program to be started by \fB\*s\fP and \fIprogram_options\fP specify additional arguments. Depending on the program, arguments might be required. The variable \f(CW%t\fP in the \fIprogram_options\fP is resolved to the terminal device specified with \fIterm\fP. . . . .SH OPTIONS .TP .BR \-e ", " \-\-exitstatus\~\fIstatus\fP Specifies an exit status that is returned when the terminal device is not available. \fIstatus\fP must be an integer in the range 1 to 255. You can use this status value in an upstart job file to prevent respawning. . .TP .BR \-V ", " \-\-verbose Displays syslog messages. . .TP .BR \-h ", " \-\-help Displays a short help text, then exits. . .TP .BR \-v ", " \-\-version Displays the version number of \fB\*s\fP, then exits. . . . .SH "RETURN VALUES" \fB\*s\fP exits with one of the following return values to report an error condition: .TP .B 1 \fB\*s\fP has been started with an argument that is not valid or required but missing. .TP .B 2 \fB\*s\fP could open the file specified for \fIterm\fP but the file is not a terminal device. .TP .B 3 \fB\*s\fP could not start the specified program. .PP The return values 1 to 3 might also be returned when the \fB\-e\fP option is used and the terminal device is not available. .TP .B 4 \- 255 The terminal device is not available and the \fB\-e\fP option specifies an exit status in this range. . . . .SH "EXAMPLES" .SS inittab To start \fB/sbin/agetty\fP on terminal device "hvc1", specify: .PP .ft CW .in +0.25in .nf h1:2345:respawn:/sbin/\*s hvc1 /sbin/agetty -L 9600 %t linux .fi .in -0.25in .ft . .SS upstart job/event files To start \fB/sbin/agetty\fP on terminal device "hvc1", add the following settings to the job file: .PP .ft CW .in +0.25in .nf respawn normal exit 42 exec /sbin/\*s -e 42 hvc1 /sbin/agetty -L 9600 %t linux .fi .in -0.25in .ft .PP With the normal exit statement, you specify an exit status that will prevent upstart from respawning the program. To prevent respawning with \fB\*s\fP, you must specify the same value for the \fB\-e\fP option. . . . .SH "SEE ALSO" .BR agetty (8), .BR mingetty (8), .BR inittab (5), .BR events (5) s390-tools-2.38.0/iucvterm/etc/000077500000000000000000000000001502674226300160605ustar00rootroot00000000000000s390-tools-2.38.0/iucvterm/etc/Makefile000066400000000000000000000012131502674226300175150ustar00rootroot00000000000000#! /usr/bin/make -f include ../../common.mak IUCVTERM_DIR = $(SYSCONFDIR)/iucvterm CONFIG_FILES = ts-shell.conf unrestricted.conf \ ts-authorization.conf ts-audit-systems.conf all: check: install: $(CONFIG_FILES) $(INSTALL) -g $(GROUP) -o $(OWNER) -d $(DESTDIR)$(IUCVTERM_DIR) ; \ for cnf in $(CONFIG_FILES); do \ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 $$cnf $(DESTDIR)$(IUCVTERM_DIR) ; \ done clean: -rm -f *.o ts-shell.conf %: %.in my_sysconf=$(IUCVTERM_DIR); \ cat $< \ |sed -e "s#@sysconf_path@#$$my_sysconf#g" \ -e 's#@S390_TOOLS_RELEASE@#$(S390_TOOLS_RELEASE)#g' \ > $@ .PHONY: install clean perl s390-tools-2.38.0/iucvterm/etc/ts-audit-systems.conf000066400000000000000000000001201502674226300221570ustar00rootroot00000000000000# List of z/VM guest virtual machines # for which terminal session are logged # s390-tools-2.38.0/iucvterm/etc/ts-authorization.conf000066400000000000000000000002571502674226300222570ustar00rootroot00000000000000# Terminal server authorization configuration # # See ts-shell(1) manual page for file format syntax. # # See also authorization-sample.conf in the documentation directory. # s390-tools-2.38.0/iucvterm/etc/ts-shell.conf.in000066400000000000000000000013621502674226300210710ustar00rootroot00000000000000# Terminal server shell (ts-shell) configuration file # # See ts-shell(1) manual page for file format syntax. # System and authorization settings # --------------------------------- # ts-system # file path to the ts-systems.conf configuration file ts-systems = @sysconf_path@/unrestricted.conf # ts-authorization # file path to the ts-authorization.conf configuration file ts-authorization = @sysconf_path@/ts-authorization.conf # Session transcript settings # --------------------------- # transcript-systems # file path to a list of systems for which session transcript are created transcript-systems = @sysconf_path@/ts-audit-systems.conf # transcript-directory # directory path for saving transcripts transcript-directory = /var/log/ts-shell s390-tools-2.38.0/iucvterm/etc/unrestricted.conf000066400000000000000000000001251502674226300214400ustar00rootroot00000000000000# Allow connections to all z/VM guest virtual machines on this z/VM instance [*ALL*] s390-tools-2.38.0/iucvterm/include/000077500000000000000000000000001502674226300167305ustar00rootroot00000000000000s390-tools-2.38.0/iucvterm/include/af_iucv.h000066400000000000000000000012521502674226300205150ustar00rootroot00000000000000/* * Copyright IBM Corp. 2006, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. * IUCV protocol stack for Linux on zSeries * Version 1.0 * */ #ifndef __AFIUCV_H #define __AFIUCV_H #include #ifndef AF_IUCV # define AF_IUCV 32 # define PF_IUCV AF_IUCV #endif /* IUCV socket address */ struct sockaddr_iucv { sa_family_t siucv_family; unsigned short siucv_port; /* Reserved */ unsigned int siucv_addr; /* Reserved */ char siucv_nodeid[8]; /* Reserved */ char siucv_user_id[8]; /* Guest User Id */ char siucv_name[8]; /* Application Name */ }; #endif s390-tools-2.38.0/iucvterm/include/iucvterm/000077500000000000000000000000001502674226300205665ustar00rootroot00000000000000s390-tools-2.38.0/iucvterm/include/iucvterm/config.h000066400000000000000000000021201502674226300221770ustar00rootroot00000000000000/* * iucvtty / iucvconn - IUCV Terminal Applications * * Configuration structure and command line processing function * * Copyright IBM Corp. 2008, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef __IUCVTTY_CONFIG_H_ #define __IUCVTTY_CONFIG_H_ enum iucvterm_prg { PROG_IUCV_TTY = 0, PROG_IUCV_CONN = 1, }; struct iucvterm_cfg { char client_re[129]; /* Regexp to match incoming clients */ char host[9]; /* IUCV target host name */ char service[9]; /* IUCV service name */ char **cmd_parms; /* ptr to commandline parms */ char *sessionlog; /* ptr to session log file path */ unsigned char esc_char; /* Escape character */ unsigned int flags; /* Configuration flags */ }; /* configuration flags */ #define CFG_F_CHKCLNT 0x0001 /* Check for permitted clients */ /* configuration macros */ #define CFG_CHKCLNT(c) ((c)->flags & CFG_F_CHKCLNT) extern void parse_options(enum iucvterm_prg, struct iucvterm_cfg *, int, char **); #endif /* __IUCVTTY_CONFIG_H_ */ s390-tools-2.38.0/iucvterm/include/iucvterm/functions.h000066400000000000000000000045031502674226300227510ustar00rootroot00000000000000/* * iucvtty / iucvconn - IUCV Terminal Applications * * Definition of common functions * * Copyright IBM Corp. 2008, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef __FUNCTIONS_H_ #define __FUNCTIONS_H_ #include #include "af_iucv.h" #include "inttypes.h" #include "iucvterm/config.h" #include "iucvterm/proto.h" /* Message buffer: message header + 4096 bytes of data */ #define MSG_BUFFER_SIZE (MSG_DATA_OFFSET + (4096)) /* Error macros */ #define print_error(s) program_error(PRG_COMPONENT, (s)) #define iucvtty_error(m) \ do { \ uint32_t *err = (uint32_t *) (m)->data; \ iucv_msg_error(PRG_COMPONENT, *err); \ } while (0); /* Error codes */ /* Unable to fork new process */ #define ERR_FORK 105 /* Cannot execute login program */ #define ERR_CANNOT_EXEC_LOGIN 106 /* Cannot set up terminal as a login terminal */ #define ERR_SETUP_LOGIN_TTY 107 /* Client (vm guest) is not authorized */ #define ERR_NOT_AUTHORIZED 110 extern int iucvtty_handle_req(int); extern int iucvtty_socket(struct sockaddr_iucv *, const char *, const char *); extern int iucvtty_tx_termenv(int, char *); extern int iucvtty_rx_termenv(int, void *, size_t); extern int iucvtty_tx_winsize(int, int); extern int iucvtty_tx_data(int, int, struct iucvtty_msg *, size_t); extern int iucvtty_tx_error(int, uint32_t); extern int iucvtty_copy_data(int, struct iucvtty_msg *); extern int iucvtty_read_data(int, struct iucvtty_msg *, size_t); extern int iucvtty_read_msg(int, struct iucvtty_msg *, size_t, size_t *); extern int iucvtty_write_msg(int, struct iucvtty_msg *); extern void iucvtty_skip_msg_residual(int, size_t *); extern ssize_t __write(int, const void*, size_t); extern int strmatch(const char *, const char *); extern int is_regex_valid(const char *); extern int is_client_allowed(const char *, const struct iucvterm_cfg *); extern void userid_cpy(char [9], const char [8]); extern void iucv_msg_error(const char *, uint32_t); extern void program_error(const char *, const char *); /* Audit/session log */ extern int open_session_log(const char *); extern ssize_t write_session_log(const void*, size_t); extern void write_session_info(const char *, ...); extern void close_session_log(void); #endif /* __FUNCTIONS_H_ */ s390-tools-2.38.0/iucvterm/include/iucvterm/gettext.h000066400000000000000000000033101502674226300224200ustar00rootroot00000000000000/* * iucvtty / iucvconn - IUCV Terminal Applications * * National language support (NLS) functions * * Copyright IBM Corp. 2008, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef __IUCVTTY_GETTEXT_H_ #define __IUCVTTY_GETTEXT_H_ #ifdef USE_NLS # include # include #endif /* Gettext constants (should be supplied from Makefile) */ #ifndef GETTEXT_TEXTDOMAIN # define GETTEXT_TEXTDOMAIN "iucvterm" #endif #ifndef GETTEXT_NLS_PATH # define GETTEXT_NLS_PATH "/usr/share/locale" #endif /* Gettext macros */ #ifdef USE_NLS # define _(translatable) gettext(translatable) #else # define _(translatable) (translatable) #endif #define N_(translatable) (translatable) /** * gettext_setup_locale() - Init gettext text domain using a specific locale * @locale: Locale to be set. * * The function sets the program locale for LC_MESSAGES and then initializes * gettext. * * The @locale parameter is directly passed to the setlocale() function. * If @locale is "", LC_MESSAGES is set according to the environment variable. */ static inline int gettext_init_locale(const char *locale) { #ifdef USE_NLS if (setlocale(LC_MESSAGES, locale) == NULL) return -1; if (bindtextdomain(GETTEXT_TEXTDOMAIN, GETTEXT_NLS_PATH) == NULL) return -1; if (textdomain(GETTEXT_TEXTDOMAIN) == NULL) return -1; #endif return 0; } /** * gettext_setup() - Initialize gettext text domain * * Calls gettext_setup_locale() with "" as locale parameter value. */ static inline int gettext_init(void) { #ifdef USE_NLS return gettext_init_locale(""); #else return 0; #endif } #endif /* __IUCVTTY_GETTEXT_H_ */ s390-tools-2.38.0/iucvterm/include/iucvterm/proto.h000066400000000000000000000055141502674226300221070ustar00rootroot00000000000000/* * iucvtty / iucvconn - IUCV Terminal Applications * * Structure and function definitions for the IUCV terminal message protocol * * Copyright IBM Corp. 2008, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef __IUCVTTY_PROTO_H_ #define __IUCVTTY_PROTO_H_ #include #include #include #include "lib/util_base.h" /* Standard macros */ #ifndef offsetof # define offsetof(type, member) ((size_t) &((type *)0)->member) #endif /* Packet version magic */ #define MSG_VERSION 0x02 /* Message types */ #define MSG_TYPE_ERROR 0x01 /* Error message. */ #define MSG_TYPE_TERMENV 0x02 /* Terminal environment variable. */ #define MSG_TYPE_TERMIOS 0x04 /* Terminal IO struct update. */ #define MSG_TYPE_WINSIZE 0x08 /* Terminal window size update. */ #define MSG_TYPE_DATA 0x10 /* Terminal data. */ struct iucvtty_msg { uint8_t version; /* Message version */ uint8_t type; /* Message type */ uint16_t datalen; /* Number of bytes in payload */ uint8_t data[]; /* Payload buffer */ } __attribute__((packed)); #define MSG_DATA_OFFSET (offsetof(struct iucvtty_msg, data)) #define msg_size(m) (MSG_DATA_OFFSET + (m)->datalen) /** * msg_cpy_from() - Copy data to message * @msg: IUCV terminal message * @src: Pointer to source data buffer * @len: Length of source data buffer * * Copies @len bytes from @src to the message data buffer. The caller must * ensure not to overwrite the message data buffer. * Finally, the message datalen is set to @len */ static inline void msg_cpy_from(struct iucvtty_msg *msg, const void *src, size_t len) { memcpy(msg->data, src, len); msg->datalen = len; } /** * msg_cpy_to() - Copy data from a message * @msg: IUCV terminal message * @dst: Destination buffer * @len: Destination buffer length. * * Copies up to min(@len, message datalen) number of bytes from the IUCV * terminal message to the destination buffer. */ static inline void msg_cpy_to(const struct iucvtty_msg *msg, void *dst, size_t len) { memcpy(dst, msg->data, MIN(msg->datalen, len)); } /** * msg_alloc() - Allocates a new iucv terminal message. * @type: Message type * @size: Message data size * * The function allocates a new iucv terminal message with the given data size * @size. (The total message size is @size plus MSG_DATA_OFFSET.) * The new message is initialized with the specific @type. */ static inline struct iucvtty_msg *msg_alloc(uint8_t type, uint16_t size) { struct iucvtty_msg *m; m = malloc(size + MSG_DATA_OFFSET); if (m != NULL) { m->version = MSG_VERSION; m->type = type; m->datalen = size; } return m; } /** * msg_free() - free an allocated iucv tty message */ static inline void msg_free(struct iucvtty_msg *m) { free(m); } #endif /* __IUCVTTY_PROTO_H_ */ s390-tools-2.38.0/iucvterm/po/000077500000000000000000000000001502674226300157235ustar00rootroot00000000000000s390-tools-2.38.0/iucvterm/po/Makefile000066400000000000000000000027451502674226300173730ustar00rootroot00000000000000#! /usr/bin/make -f # # This Makefile creates a message.po template for translation input. # # To create a localized message.po file from the template, run # msginit -i progname.pot -l de_DE # --no-translator # # To finally create the binary message.mo file, run # msgfmt de.po -o /usr/share/locale/de/LC_MESSAGES/progname.mo # #include ../../common.mak ifndef GETTEXT_TEXTDOMAIN GETTEXT_TEXTDOMAIN = iucvterm endif XGETTEXT = xgettext XGETTEXT_ARGS = --keyword=print_error POTFILES = POTFILES.in all: $(GETTEXT_TEXTDOMAIN).pot check: install: clean: -rm -f $(GETTEXT_TEXTDOMAIN).pot $(GETTEXT_TEXTDOMAIN).pot: @$(XGETTEXT) --default-domain=$(GETTEXT_TEXTDOMAIN) --directory='..' \ --add-comments --keyword=_ --keyword=N_ \ --flag=g_strdup_printf:1:c-format \ --flag=g_string_printf:2:c-format \ --flag=g_string_append_printf:2:c-format \ --flag=g_error_new:3:c-format \ --flag=g_set_error:4:c-format \ --flag=g_markup_printf_escaped:1:c-format \ --flag=g_log:3:c-format \ --flag=g_print:1:c-format \ --flag=g_printerr:1:c-format \ --flag=g_printf:1:c-format \ --flag=g_fprintf:2:c-format \ --flag=g_sprintf:2:c-format \ --flag=g_snprintf:3:c-format \ --flag=g_scanner_error:2:c-format \ --flag=g_scanner_warn:2:c-format \ --files-from=$(POTFILES) $(XGETTEXT_ARGS) \ && test ! -f $(GETTEXT_TEXTDOMAIN).po \ || ( rm -f $(GETTEXT_TEXTDOMAIN).pot \ && mv $(GETTEXT_TEXTDOMAIN).po $(GETTEXT_TEXTDOMAIN).pot ) .PHONY: install clean $(GETTEXT_TEXTDOMAIN).pot s390-tools-2.38.0/iucvterm/po/POTFILES.in000066400000000000000000000000721502674226300174770ustar00rootroot00000000000000src/iucvconn.c src/functions.c src/iucvtty.c src/getopt.c s390-tools-2.38.0/iucvterm/src/000077500000000000000000000000001502674226300160745ustar00rootroot00000000000000s390-tools-2.38.0/iucvterm/src/Makefile000066400000000000000000000014571502674226300175430ustar00rootroot00000000000000#! /usr/bin/make -f include ../../common.mak ifndef GETTEXT_TEXTDOMAIN GETTEXT_TEXTDOMAIN = iucvterm endif ALL_CPPFLAGS += -I../include ALL_CPPFLAGS += -DUSE_NLS -DGETTEXT_TEXTDOMAIN=\"$(GETTEXT_TEXTDOMAIN)\" #ALL_CPPFLAGS += -D__DEBUG__ PROGRAMS = iucvconn iucvtty SYSTOOLS = ttyrun all: $(PROGRAMS) $(SYSTOOLS) check: install: all for prg in $(PROGRAMS); do \ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 $$prg $(DESTDIR)$(USRBINDIR) ; \ done for prg in $(SYSTOOLS); do \ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 $$prg $(DESTDIR)$(BINDIR) ; \ done clean: -rm -f *.o $(PROGRAMS) $(SYSTOOLS) iucvconn: iucvconn.o getopt.o auditlog.o functions.o iucvtty: LDLIBS = -lutil iucvtty: iucvtty.o getopt.o auditlog.o functions.o ttyrun: GETTEXT_TEXTDOMAIN = ttyrun ttyrun: ttyrun.o .PHONY: install clean s390-tools-2.38.0/iucvterm/src/auditlog.c000066400000000000000000000117461502674226300200610ustar00rootroot00000000000000/* * iucvtty / iucvconn - IUCV Terminal Applications * * Functions for session logging/auditing. * The session log and timing data files adhere to the format * described in script(1). * * Copyright IBM Corp. 2008, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #include "iucvterm/functions.h" #define OPEN_FILEMODE (O_WRONLY | O_CREAT | O_EXCL) #define OPEN_FILEMASK (S_IRUSR | S_IWUSR | S_IRGRP) static int script_fd = -1; /* fd of typescript file */ static int timing_fd = -1; /* fd of timing data file */ static FILE *info_file = NULL; /* FILE of info file */ static struct timeval last_tv; /* tv to calculate timing */ /** * print_on_time() - Append formatted time string to a message. * @prefix: message * * Returns a new buffer starting with @prefix and appends * a formatted string representation of the current time. * Returns NULL if memory allocation has failed. * * The caller must free the returned buffer after use. */ static char *print_on_time(const char *prefix) { char *buf = malloc(64 + strlen(prefix) + 1); time_t t = time(NULL); if (buf != NULL) { if (t == (time_t) -1) sprintf(buf, "%s\n", prefix); else { sprintf(buf, "%s on ", prefix); ctime_r(&t, buf + strlen(prefix) + 4); } } return buf; } /** * write_session_info() - Write data to the session info file * @format: Format string, shall not be NULL * * Writes informational messages to the session info file. * The info message is prefixed with a timestamp as returned by the * time(2) syscall. */ void write_session_info(const char *format, ...) { va_list ap; if (info_file == NULL) return; fprintf(info_file, "%lu ", time(NULL)); va_start(ap, format); vfprintf(info_file, format, ap); va_end(ap); if (strrchr(format, '\n') == NULL) fprintf(info_file, "\n"); } /** * write_session_log() - Write session data to the session log file * @buf: Pointer to a buffer with data to log * @len: Copy up to @len bytes from @buf * * The routines writes up to @len bytes of data from buffer @buf to * the session transcript; write appropriate timing data to the timing * file. */ ssize_t write_session_log(const void* buf, size_t len) { ssize_t rc; int count; char data[64] = ""; struct timeval curr_tv; long time_diff; /* immediately return if there is no fd to write to */ if (script_fd == -1) return -1; rc = __write(script_fd, buf, len); if (rc < 0) return rc; /* calculate delay and write timing info */ if (gettimeofday(&curr_tv, NULL)) time_diff = 1000000; /* one second (in usecs) */ else { time_diff = (curr_tv.tv_sec - last_tv.tv_sec) * 1000000 + curr_tv.tv_usec - last_tv.tv_usec; last_tv = curr_tv; /* reset last timeval */ } count = sprintf(data, "%.6f %zu\n", (double) time_diff / (double) 1000000, len); rc = __write(timing_fd, data, (count < 0) ? 0 : count ); if (rc < 0) return rc; return 0; } /** * close_session_log() - Close session logging * * The routine writes a trailer to the session log file and * closes the session log, timing and info file descriptor. */ void close_session_log(void) { char *trailer; if (script_fd > 0) { trailer = print_on_time("Script done"); if (trailer != NULL) { __write(script_fd, trailer, strlen(trailer)); write_session_info(trailer); free(trailer); } close(script_fd); } if (timing_fd > 0) close(timing_fd); if (info_file != NULL) fclose(info_file); } /** * open_session_log() - Open session logging * @filepath: File path to the session transcript * * Opens the session, timing and info log file. * If the session specified by @filepath already exists; or one of the * files cannot be opened successfully, return an error. */ int open_session_log(const char *filepath) { char *buf; int old_errno; int info_fd; buf = calloc(11 + strlen(filepath), sizeof(char)); if (buf == NULL) goto out_no_mem; script_fd = open(filepath, OPEN_FILEMODE, OPEN_FILEMASK); if (script_fd == -1) goto out_error_open; sprintf(buf, "%s.timing", filepath); timing_fd = open(buf, OPEN_FILEMODE, OPEN_FILEMASK); if (timing_fd == -1) goto out_error_open; sprintf(buf, "%s.info", filepath); info_fd = open(buf, OPEN_FILEMODE, OPEN_FILEMASK); if (info_fd == -1) goto out_error_open; info_file = fdopen(info_fd, "w"); if (info_file == NULL) goto out_error_open; if(gettimeofday(&last_tv, NULL)) goto out_error_open; free(buf); buf = print_on_time("Script started"); if (buf != NULL) { __write(script_fd, buf, strlen(buf)); write_session_info(buf); free(buf); } return 0; out_error_open: old_errno = errno; /* preserve errno */ free(buf); close_session_log(); errno = old_errno; /* restore errno from failed open call */ out_no_mem: return -1; } s390-tools-2.38.0/iucvterm/src/functions.c000066400000000000000000000305111502674226300202500ustar00rootroot00000000000000/* * iucvtty / iucvconn - IUCV Terminal Applications * * Common functions * * Copyright IBM Corp. 2008, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include "af_iucv.h" #include "iucvterm/config.h" #include "iucvterm/functions.h" #include "iucvterm/gettext.h" #include "iucvterm/proto.h" /* Global program component for iucv terminal tools */ #define PRG_COMPONENT "iucvterm" /** * __write() - Write data * @fd: File descriptor * @buf: Pointer to data buffer * @len: Buffer length * * Write @len number of bytes from the buffer @buf to the file * descriptor @fd. The routines handles EINTR and partially writes. * Returns the error code from the underlying write(2) syscall. */ ssize_t __write(int fd, const void *buf, size_t len) { ssize_t rc; size_t written = 0; while (written < len) { rc = write(fd, buf + written, len - written); if (rc == -1 && errno == EINTR) continue; if (rc <= 0) return rc; written += rc; } return written; } #ifdef __DEBUG__ static void __dump_msg(int fd, const struct iucvtty_msg *m, char dir) { write_session_info("%c: (fd=%d) MSG: ver=%02x type=%02x datalen=%u\n", dir, fd, m->version, m->type, (uint16_t) m->datalen); } #endif /** * iucvtty_socket() - Creates and return an IUCV socket * @sai: AF_IUCV socket address structure * @host: z/VM guest name * @service: Terminal name passed as additional data to @host after connect * * This function sets up the struct sockaddr_iucv with the specified * VM guest virtual machine and terminal information. * Finally, it returns an AF_IUCV socket. */ int iucvtty_socket(struct sockaddr_iucv *sai, const char *host, const char *service) { char temp[9]; memset(sai, 0, sizeof(struct sockaddr_iucv)); sai->siucv_family = AF_IUCV; if (host != NULL) { snprintf(temp, 9, "%-8s", host); memcpy(sai->siucv_user_id, temp, 8); } else memset(sai->siucv_user_id, ' ', 8); if (service != NULL) { snprintf(temp, 9, "%-8s", service); memcpy(sai->siucv_name, temp, 8); } else memset(sai->siucv_name, ' ', 8); return socket(PF_IUCV, SOCK_STREAM, 0); } /** * iucvtty_tx_termenv() - Send terminal environment variable * @dest: File descriptor to output data * @dflt: TERM environment string ('\0' terminated) * * Copy terminal environment variable to destination @dest. */ int iucvtty_tx_termenv(int dest, char *dflt) { struct iucvtty_msg *msg; char *term = getenv("TERM"); size_t len; int rc; if (term == NULL && dflt != NULL) term = dflt; len = 0; if (term != NULL) len = 1 + strlen(term); /* Note: The server console tool waits for terminal environment * information: the message is sent even if it is empty */ msg = msg_alloc(MSG_TYPE_TERMENV, len); if (msg == NULL) return -1; msg_cpy_from(msg, term, len); rc = iucvtty_write_msg(dest, msg); msg_free(msg); return rc; } /** * iucvtty_rx_termenv() - Receive terminal environment variable * @fd: File descriptor to read data from * @buf: Buffer to store the terminal environment variable * @len: Size of buffer @buf */ int iucvtty_rx_termenv(int fd, void *buf, size_t len) { int rc; size_t skip; struct iucvtty_msg *msg = msg_alloc(MSG_TYPE_TERMENV, len); if (msg == NULL) return -1; skip = 0; rc = iucvtty_read_msg(fd, msg, msg_size(msg), &skip); iucvtty_skip_msg_residual(fd, &skip); if (!rc) { if (msg->datalen == 0) memset(buf, 0, MIN(1u, len)); else msg_cpy_to(msg, buf, len); } msg_free(msg); return rc; } /** * iucvtty_tx_data() - Send terminal data * @from: File descriptor to read data from * @msg: Pointer to iucv tty message buffer * @len: Size of message buffer * * This routine reads data from file descriptor @from and stores them in * a data array of the specified iucv tty message @msg. It reads up to * @len - MSG_DATA_OFFSET bytes from fd @from. */ int iucvtty_read_data(int from, struct iucvtty_msg *msg, size_t len) { ssize_t r; r = read(from, msg->data, len - MSG_DATA_OFFSET); if (r == -1 && errno == EINTR) /* REVIEW: loop if EINTR ? */ r = read(from, msg->data, len - MSG_DATA_OFFSET); if (r <= 0) return -1; msg->version = MSG_VERSION; msg->type = MSG_TYPE_DATA; msg->datalen = (uint16_t) r; return 0; } /** * iucvtty_tx_data() - Send terminal data * @dest: File descriptor to send data to * @from: File descriptor to read data from * @msg: Pointer to iucv tty message buffer * @len: Size of message buffer * * This routine reads data from file descriptor @from and stores them in * a data array of the specified iucv tty message @msg. It reads up to * @len - MSG_DATA_OFFSET bytes from fd @from. * Finally, the iucv tty message written to file descriptor @dest. */ int iucvtty_tx_data(int dest, int from, struct iucvtty_msg *msg, size_t len) { if (iucvtty_read_data(from, msg, len)) return -1; if (iucvtty_write_msg(dest, msg)) return -1; return 0; } /** * iucvtty_tx_winsize() - Send terminal window size information. * @dest: Destination * @from: Terminal file descriptor to request winsize * * Sends the terminal window size from terminal file descriptor * @from to the destination @dest. * If the window size is not retrieved, the routine will not fail. * The routine fails if there is a problem sending the window size * to @dest. The return codes are specified by iucvtty_write_msg(). */ int iucvtty_tx_winsize(int dest, int from) { int rc; struct iucvtty_msg *msg = msg_alloc(MSG_TYPE_WINSIZE, sizeof(struct winsize)); if (msg == NULL) return -1; rc = 0; if (ioctl(from, TIOCGWINSZ, msg->data) > -1) rc = iucvtty_write_msg(dest, msg); msg_free(msg); return rc; } /** * iucvtty_tx_error() - Send an error code * @dest: Destination * @errCode: Error code */ int iucvtty_tx_error(int dest, uint32_t errCode) { struct iucvtty_msg *msg; int rc; msg = msg_alloc(MSG_TYPE_ERROR, sizeof(errCode)); if (msg == NULL) return -1; msg_cpy_from(msg, &errCode, sizeof(errCode)); rc = iucvtty_write_msg(dest, msg); msg_free(msg); return rc; } /** * iucvtty_copy_data() - Copy IUCV message data * @dest: Destination to copy data to * @msg: IUCV message */ int iucvtty_copy_data(int dest, struct iucvtty_msg *msg) { if (__write(dest, msg->data, msg->datalen) <= 0) return -1; return 0; } /** * iucvtty_skip_msg_residual() - Skip (receive & forget) count number of bytes * @fd: File descriptor * @residual: Residual of an iucv tty message received by iucvtty_read_msg() * * See iucvtty_read_msg() for an explanation when to use this routine. * Note: The @residual parameter shall not be NULL. */ void iucvtty_skip_msg_residual(int fd, size_t *residual) { char b; size_t i; if (*residual <= 0) return; for (i = 0; i < *residual; i++) if (read(fd, &b, 1) <= 0) break; *residual = 0; } /** * iucvtty_read_msg() - Read/Receive an IUCV message * @fd: File descriptor to read from * @msg: Pointer to IUCV message buffer * @len: IUCV message data len * @residual: Status to be used by next call * * The function reads up to @len bytes from file descriptor @fd. * If the received message is larger than @len bytes, the @residual value * is set to the number of bytes remaining. * The function shall then be re-called to create a new message and receive * the next chunk of size @residual; or the remaining characters must be * skipped using the iucvtty_skip_msg() routine. * Note: The @len parameter shall be greater than MSG_DATA_OFFSET. * The @residual parameter shall not be NULL. */ int iucvtty_read_msg(int fd, struct iucvtty_msg *msg, size_t len, size_t *residual) { int rc; ssize_t r; /* number of bytes read from fd */ if (*residual) len = MIN(len - MSG_DATA_OFFSET, *residual); while (1) { if (*residual) { r = read(fd, msg->data, len); if (r > 0) msg->datalen = r; } else r = read(fd, msg, len); if (r == -1 && errno == EINTR) continue; if (r <= 0) { rc = -1; goto out_read_error; } break; /* exit loop for a successful read */ } #ifdef __DEBUG__ if (!*residual) __dump_msg(fd, msg, 'R'); #endif /* (re)calculate next chunk */ if (*residual) *residual -= msg->datalen; else if (msg->datalen > (r - MSG_DATA_OFFSET)) { /* calculate pending msg data and update datalen */ *residual = msg->datalen - (r - MSG_DATA_OFFSET); msg->datalen = r - MSG_DATA_OFFSET; } /* check for a sane message */ if (msg->version != MSG_VERSION) { fprintf(stderr, _("%s: %s\n"), PRG_COMPONENT, _("The version of the received data " "message is not supported\n")); rc = -2; goto out_read_error; } rc = 0; out_read_error: return rc; } /** * iucvtty_write_msg() - Write/Send IUCV message * @fd: File descriptor * @msg: Pointer to IUCV message */ int iucvtty_write_msg(int fd, struct iucvtty_msg *msg) { msg->version = MSG_VERSION; if (__write(fd, msg, msg_size(msg)) <= 0) return -1; #ifdef __DEBUG__ __dump_msg(fd, msg, 'S'); #endif return 0; } /** * iucv_msg_error() - Reports an IUCV message error * @comp: Program component * @errnum: IUCV message error code */ void iucv_msg_error(const char *comp, uint32_t errnum) { const char *translated; switch (errnum) { case ERR_FORK: translated = _("Creating a new process to run the " "login program failed"); break; case ERR_CANNOT_EXEC_LOGIN: translated = _("Running the login program failed"); break; case ERR_SETUP_LOGIN_TTY: translated = _("Setting up a terminal for user login failed"); break; case ERR_NOT_AUTHORIZED: translated = _("The z/VM guest virtual machine is not " "permitted to connect"); break; default: translated = _("The specified error code is not known"); break; } fprintf(stderr, "%s: %s (%s=%" PRIu32 ")\r\n", comp, translated, _("error code"), errnum); } /** * program_error() - Report an program/syscall error. * @comp: Program component name * @d: Error message, subject to gettext translation */ void program_error(const char *comp, const char *d) { fprintf(stderr, _("%s: %s: %s\n"), comp, _(d), strerror(errno)); } /** * __regerror - Report an error from a previous regex api call * @error: Error code * @re: Reference to the used regular expression */ static inline void __regerror(int error, const regex_t *re) { char errbuf[81]; regerror(error, re, errbuf, 81); fprintf(stderr, _("The regular expression has an error: %s\n"), errbuf); return; } /** * is_regex_valid() - Check if the specified regex is syntactically correct. * @re: String representation of the regular expression * * Returns zero on success, otherwise -1. */ int is_regex_valid(const char *re) { regex_t regex; int rc; if (re == NULL) return -1; rc = regcomp(®ex, re, REG_EXTENDED | REG_ICASE | REG_NOSUB); if (rc) { __regerror(rc, ®ex); rc = -1; } regfree(®ex); return rc; } /** * strmatch() - Match a string using a regular expression * @str: String to match * @re: Regular expression * * Returns zero on success, -1 on error or if @str is NULL; and * 1 if the regular expression did not match the string. */ int strmatch(const char *str, const char *re) { regex_t regex; regmatch_t pmatch[1]; size_t nmatch = 0; int rc; if (re == NULL) return -1; rc = regcomp(®ex, re, REG_EXTENDED | REG_ICASE | REG_NOSUB); if (rc) { __regerror(rc, ®ex); regfree(®ex); return -1; } rc = regexec(®ex, str, nmatch, pmatch, 0); if (rc == REG_NOMATCH) rc = 1; regfree(®ex); return rc; } /** * is_client_allowed() - Check if the client is allowed to connect. * @client: Client name * @cfg: Pointer to the IUCV terminal configuration structure * * The return code is identical to strmatch(). If client checking is * disabled, the function returns zero. */ int is_client_allowed(const char *client, const struct iucvterm_cfg *cfg) { if (!CFG_CHKCLNT(cfg)) return 0; return strmatch(client, cfg->client_re); } /** * userid_cpy() - Copy z/VM user ID and skip trailing spaces. * @dest: Destination buffer * @userid: z/VM user ID */ void userid_cpy(char dest[9], const char userid[8]) { ssize_t pos; /* find pos of last character (pos 0..7) or -1 if user ID is empty */ for (pos = 7; pos >= 0; pos--) if (userid[pos] != ' ') break; if (pos >= 0) memcpy(dest, userid, pos + 1); dest[pos + 1] = '\0'; } s390-tools-2.38.0/iucvterm/src/getopt.c000066400000000000000000000135571502674226300175550ustar00rootroot00000000000000/* * iucvtty / iucvconn - IUCV Terminal Applications * * Processing of command line arguments * * Copyright IBM Corp. 2008, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include "lib/zt_common.h" #include "iucvterm/config.h" #include "iucvterm/functions.h" #include "iucvterm/gettext.h" static const char iucvtty_usage[] = N_( "Usage: %s [-h|--help] [-v|--version]\n" " %s [-a ] [-- []]\n\n" "Options:\n" " -h, --help Print this help, then exit.\n" " -v, --version Print version information, then exit.\n" " -a, --allow-from Permit connections from particular z/VM guests only.\n" " A z/VM guest is permitted if regex matches its name.\n" ); static const char iucvconn_usage[] = N_( "Usage: %s [-h|--help] [-v|--version]\n" " %s [-e esc] [-s ] \n\n" "Options:\n" " -h, --help Print this help, then exit.\n" " -v, --version Print version information, then exit.\n" " -s, --sessionlog Write terminal session to file.\n" " -e, --escape-char Escape character (can be one of: A-Y, ], ^ or _)\n" " Characters C, D, Q, S, Z and [ are not allowed.\n" ); static const struct option iucvterm_long_opts[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'v' }, { "allow-from", required_argument, NULL, 'a' }, { "sessionlog", required_argument, NULL, 's' }, { "escape-char", required_argument, NULL, 'e' }, { NULL, no_argument, NULL, 0 } }; struct tool_info { char name[10]; char optstring[10]; const char *usage; unsigned char reqNonOpts; }; /* program specific command line settings */ static const struct tool_info iucv_tool[2] = { { .name = "iucvtty", .optstring = "-hva:", .usage = iucvtty_usage, .reqNonOpts = 1, }, { .name = "iucvconn", .optstring = "-hvs:e:", .usage = iucvconn_usage, .reqNonOpts = 2, } }; static void __noreturn usage_exit(const struct tool_info *prg, int is_error, const char *msg) { FILE *file = is_error ? stderr : stdout; if (msg != NULL) fprintf(file, _("%s: %s\n"), prg->name, msg); fprintf(file, _(prg->usage), prg->name, prg->name); exit(is_error ? 1 : 0); /* rc=1 .. invalid args */ } static void __noreturn version_exit(const struct tool_info *prg) { printf(_("%s: IUCV Terminal Applications, version %s\n"), prg->name, RELEASE_STRING); printf(_("Copyright IBM Corp. 2008, 2017\n")); exit(0); } static void cpy_or_exit(char *dest, const char *src, size_t size, const struct tool_info *prg, const char *param) { if (strlen(src) >= size){ fprintf(stderr, _("%s: %s exceeds the maximum of %zu characters\n"), prg->name, param, size - 1); exit(1); } strncpy(dest, src, size); dest[size - 1] = 0; } static void set_esc_or_exit(const struct tool_info *prg, const char val, unsigned char *esc) { unsigned char upval = toupper(val); /* range of valid escape keys: A-Z [ \ ] ^ _ */ if (upval < 'A' || upval > '_') usage_exit(prg, 1, _("The specified character is not a " "valid escape character")); switch (upval) { case 'C': /* interrupt (ISIG) */ case 'D': /* EoF / EoT */ case 'Q': /* XON */ case 'S': /* XOFF */ case 'Z': /* suspend (shell) */ case '[': /* ESC */ usage_exit(prg, 1, _("The specified character is not a " "valid escape character")); default: *esc = upval ^ 0100; /* see ascii(7) */ break; } } void parse_options(enum iucvterm_prg prg, struct iucvterm_cfg *config, int argc, char **argv) { int c; int index; int nonOpts = 0; config->cmd_parms = NULL; config->sessionlog = NULL; config->esc_char = '_' ^ 0100; /* Ctrl-_ (0x1f) */ config->flags = 0; while (1) { index = -1; c = getopt_long(argc, argv, iucv_tool[prg].optstring, iucvterm_long_opts, &index); if (c == -1) break; switch (c) { case 1: if (nonOpts >= iucv_tool[prg].reqNonOpts) { usage_exit(&iucv_tool[prg], 1, NULL); break; } switch (nonOpts) { case 0: if (prg == PROG_IUCV_CONN) cpy_or_exit(config->host, optarg, sizeof(config->host), &iucv_tool[prg], _("")); else cpy_or_exit(config->service, optarg, sizeof(config->service), &iucv_tool[prg], _("")); break; case 1: cpy_or_exit(config->service, optarg, sizeof(config->service), &iucv_tool[prg], _("")); break; default: usage_exit(&iucv_tool[prg], 1, NULL); break; } ++nonOpts; break; case 'a':/* max 80 */ cpy_or_exit(config->client_re, optarg, sizeof(config->client_re), &iucv_tool[prg], _("")); if (is_regex_valid(config->client_re)) exit(1); config->flags |= CFG_F_CHKCLNT; break; case 'e': switch (strlen(optarg)) { case 1: set_esc_or_exit(&iucv_tool[prg], optarg[0], &config->esc_char); break; case 4: if (memcmp(optarg, "none", 4) == 0) { config->esc_char = 0; break; } /* fall through */ default: usage_exit(&iucv_tool[prg], 1, _("The escape character must be a " "single character or 'none'")); } break; case 's': config->sessionlog = optarg; break; case 'h': usage_exit(&iucv_tool[prg], 0, NULL); case 'v': version_exit(&iucv_tool[prg]); case '?': printf(_("Try '%s --help' for more information.\n"), iucv_tool[prg].name); exit(1); break; } } if (optind < argc) /* save additional parameters */ config->cmd_parms = argv + optind; if (nonOpts < iucv_tool[prg].reqNonOpts) /* not enough args */ usage_exit(&iucv_tool[prg], 1, _("The command does not have enough arguments")); } s390-tools-2.38.0/iucvterm/src/iucvconn.c000066400000000000000000000203441502674226300200670ustar00rootroot00000000000000/* * iucvconn - Application that establishes a terminal connection over IUCV * * Core application * * Copyright IBM Corp. 2008, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #include "lib/util_base.h" #include "iucvterm/config.h" #include "iucvterm/functions.h" #include "iucvterm/gettext.h" #define SYSLOG_IDENT "iucvconn" #define PRG_COMPONENT SYSLOG_IDENT #define DEFAULT_TERM "linux" #define AUDIT(f, ...) do { \ syslog(LOG_INFO, (f), __VA_ARGS__); \ write_session_info((f), __VA_ARGS__); \ } while (0); /* escape mode actions */ enum esc_action_t { DISCONNECT, /* Disconnect from terminal */ RESIZE, /* Force terminal resizing */ SEND, /* Send data (default action) */ IGNORE, /* Ignore escape character */ }; static volatile sig_atomic_t resize_tty; static struct termios ios_orig; /* store original termio settings */ /** * sig_handler() - Signal handler * @sig: Signal number */ static void sig_handler(int sig) { switch (sig) { case SIGWINCH: resize_tty = sig; break; case SIGTERM: tcsetattr(STDIN_FILENO, TCSANOW, &ios_orig); close_session_log(); _exit(0); break; } } /** * get_msg_char() - Returns single character from message * @msg: The IUCV terminal message * * Returns the (single) character of an IUCV terminal message * if @msg is of type MSG_TYPE_DATA and contains a single character * (datalen == 1). Otherwise the routine returns zero. */ static unsigned char get_msg_char(const struct iucvtty_msg *msg) { if (msg->type != MSG_TYPE_DATA || msg->datalen != 1) return 0; return msg->data[0]; } /** * is_esc_char() - Check message for escape character * @msg: The IUCV terminal message * @esc: The escape character * * Returns 1 if @msg contains the escape character @esc only; otherwise the * function returns 0. */ static int is_esc_char(const struct iucvtty_msg *msg, unsigned char esc) { if (!esc) return 0; return (get_msg_char(msg) == esc) ? 1 : 0; } /** * get_action() - Returns the action from an escaped character * @msg: The IUCV terminal message * @esc: The escape character * * Returns the appropriate action of the escaped character. * The action is derived from the single character stored in the IUCV terminal * message @msg. If the escape character is recognized, SEND is returned for * sending the "escaped" escape character to the terminal. * * If it contains multiple characters, the default SEND action is used to * indicate that the escape mode is done and to force sending the complete data * characters (e.g. entered by copy & paste). * * NOTE: This routine must be called in "escape mode". */ static enum esc_action_t get_action(const struct iucvtty_msg *msg, unsigned char esc) { if (is_esc_char(msg, esc)) return SEND; switch (get_msg_char(msg)) { case 0: return SEND; case '.': case 'd': return DISCONNECT; case 'r': return RESIZE; default: return IGNORE; } } /** * iucvtty_worker() - Handle server connection * @terminal: IUCV TTY server file descriptor */ static int iucvtty_worker(int terminal, const struct iucvterm_cfg *cfg) { struct iucvtty_msg *msg; fd_set set; size_t chunk; int in_esc_mode; enum esc_action_t action; /* setup buffers */ msg = malloc(MSG_BUFFER_SIZE); if (msg == NULL) { print_error("Allocating memory for the data buffer failed"); return -1; } /* multiplex i/o between login program and socket */ chunk = 0; in_esc_mode = 0; /* escape mode state */ action = SEND; while (1) { if (resize_tty) { iucvtty_tx_winsize(terminal, STDIN_FILENO); resize_tty = 0; /* clear signal flag */ } FD_ZERO(&set); FD_SET(terminal, &set); FD_SET(STDIN_FILENO, &set); if (select(MAX(STDIN_FILENO, terminal) + 1, &set, NULL, NULL, NULL) == -1) { if (errno == EINTR) continue; break; } if (FD_ISSET(terminal, &set)) { if (iucvtty_read_msg(terminal, msg, MSG_BUFFER_SIZE, &chunk)) break; switch (msg->type) { case MSG_TYPE_DATA: iucvtty_copy_data(STDOUT_FILENO, msg); write_session_log(msg->data, msg->datalen); break; case MSG_TYPE_ERROR: iucvtty_error(msg); break; } } if (FD_ISSET(STDIN_FILENO, &set)) { if (iucvtty_read_data(STDIN_FILENO, msg, MSG_BUFFER_SIZE)) break; if (in_esc_mode) { in_esc_mode = 0; /* reset */ action = get_action(msg, cfg->esc_char); } else { if (is_esc_char(msg, cfg->esc_char)) { in_esc_mode = 1; action = IGNORE; } else action = SEND; } /* handle escape mode */ switch (action) { case SEND: /* non-escape mode (default) */ if (iucvtty_write_msg(terminal, msg)) goto out_worker_loop; break; case DISCONNECT:/* disconnect */ goto out_worker_loop; case RESIZE: /* force terminal resize */ iucvtty_tx_winsize(terminal, STDIN_FILENO); break; case IGNORE: break; } } } out_worker_loop: free(msg); return 0; } /** * main() - IUCV CONN program startup */ int main(int argc, char *argv[]) { int rc; int server; struct sockaddr_iucv addr; struct termios ios; struct sigaction sigact; struct passwd *passwd; struct iucvterm_cfg conf; /* gettext initialization */ gettext_init(); /* parse command line options */ parse_options(PROG_IUCV_CONN, &conf, argc, argv); /* open session audit log */ if (conf.sessionlog != NULL) if (open_session_log(conf.sessionlog)) { print_error("Creating the terminal session " "log files failed"); return 1; } /* open socket and connect to server */ server = iucvtty_socket(&addr, conf.host, conf.service); if (server == -1) { print_error((errno == EAFNOSUPPORT) ? N_("The AF_IUCV address family is not available") : N_("Creating the AF_IUCV socket failed")); return 1; } /* syslog */ openlog(SYSLOG_IDENT, LOG_PID, LOG_AUTHPRIV); /* get user information for syslog */ passwd = getpwuid(geteuid()); if (connect(server, (struct sockaddr *) &addr, sizeof(addr)) == -1) { switch (errno) { case EAGAIN: print_error("The new connection would exceed the " "maximum number of IUCV connections"); break; case ENETUNREACH: print_error("The target z/VM guest virtual machine " "is not logged on"); break; case EACCES: print_error("The IUCV authorizations do not permit " "connecting to the target z/VM guest"); break; default: print_error("Connecting to the z/VM guest virtual " "machine failed"); break; } AUDIT("Connection to %s/%s failed for user %s (uid=%i)", conf.host, conf.service, (passwd != NULL) ? passwd->pw_name : "n/a", geteuid()); rc = 2; goto return_on_error; } AUDIT("Established connection to %s/%s for user %s (uid=%i)", conf.host, conf.service, (passwd != NULL) ? passwd->pw_name : "n/a", geteuid()); /* send client parameters */ iucvtty_tx_termenv(server, DEFAULT_TERM); iucvtty_tx_winsize(server, STDIN_FILENO); /* register signal handler */ sigemptyset(&sigact.sa_mask); sigact.sa_flags = SA_RESTART; sigact.sa_handler = sig_handler; sigaction(SIGWINCH, &sigact, NULL); sigaction(SIGTERM, &sigact, NULL); /* modify terminal settings */ if (tcgetattr(STDIN_FILENO, &ios_orig)) { print_error("Getting the terminal I/O settings failed"); rc = 3; goto return_on_error; } memcpy(&ios, &ios_orig, sizeof(ios)); /* put terminal into raw mode */ cfmakeraw(&ios); /* NOTE: If the TTY driver (ldisc) runs in TTY_DRIVER_REAL_RAW, * we need to do the input character processing here; * that means to translate CR into CR + NL (ICRNL). * Define TTY_REAL_RAW in for that case. */ #ifdef TTY_REAL_RAW ios.c_iflag |= ICRNL; /* | IGNPAR | IGNBRK; */ #endif tcflush(STDIN_FILENO, TCIOFLUSH); if (tcsetattr(STDIN_FILENO, TCSANOW, &ios)) { print_error("Modifying the terminal I/O settings failed"); rc = 4; goto return_on_error; } iucvtty_worker(server, &conf); tcsetattr(STDIN_FILENO, TCSANOW, &ios_orig); rc = 0; return_on_error: close(server); closelog(); close_session_log(); return rc; } s390-tools-2.38.0/iucvterm/src/iucvtty.c000066400000000000000000000157051502674226300177570ustar00rootroot00000000000000/* * iucvtty - Application that provides a full-screen terminal for iucvconn * * Core application * * Copyright IBM Corp. 2008, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/util_base.h" #include "af_iucv.h" #include "iucvterm/config.h" #include "iucvterm/functions.h" #include "iucvterm/gettext.h" #define SYSLOG_IDENT "iucvtty" #define PRG_COMPONENT SYSLOG_IDENT #define TERM_BUFSIZE 256 #define TERM_DEFAULT "linux" static volatile sig_atomic_t sig_shutdown; /** * sig_handler() - Signal handler * @sig: Signal number. */ static void sig_handler(int sig) { sig_shutdown = sig; } /** * exec_login_prog() - execute a login program * @cmd: Path to the (login) program executable */ static int exec_login_prog(char *cmd[]) { int rc; if (cmd != NULL) rc = execv(cmd[0], cmd); else rc = execl("/bin/login", "/bin/login", (char *) NULL); return rc; } /** * iucvtty_worker() - Handle an incoming client connection * @client: Client file descriptor * @master: PTY master file descriptor * @slave: PTY slave file descriptor * @cfg: IUCV TTY configuration structure. */ static int iucvtty_worker(int client, int master, int slave, const struct iucvterm_cfg *cfg) { int rc; struct iucvtty_msg *msg; pid_t child; fd_set set; size_t chunk; char term_env[TERM_BUFSIZE]; /* flush pending terminal data */ tcflush(master, TCIOFLUSH); /* read terminal parameters from client */ if (iucvtty_rx_termenv(client, term_env, TERM_BUFSIZE)) sprintf(term_env, TERM_DEFAULT); /* start login program */ child = fork(); if (child == -1) { print_error("Creating a new process to run the " "login program failed"); iucvtty_tx_error(client, ERR_FORK); return 1; /* return from worker */ } if (child == 0) { /* child process */ closelog(); /* close syslog */ /* setup terminal */ if (login_tty(slave)) { print_error("Setting up a terminal for user login failed"); iucvtty_tx_error(client, ERR_SETUP_LOGIN_TTY); exit(2); } setenv("TERM", term_env, 1); if (exec_login_prog(cfg->cmd_parms)) { print_error("Running the login program failed"); iucvtty_tx_error(client, ERR_CANNOT_EXEC_LOGIN); } exit(3); /* we only reach here if exec has failed */ } /* setup buffers */ msg = malloc(MSG_BUFFER_SIZE); if (msg == NULL) { print_error("Allocating memory for the data buffer failed"); rc = 2; goto out_kill_login; } /* multiplex i/o between login program and socket. */ rc = 0; chunk = 0; while (!sig_shutdown) { FD_ZERO(&set); FD_SET(client, &set); FD_SET(master, &set); if (select(MAX(master, client) + 1, &set, NULL, NULL, NULL) == -1) { if (errno == EINTR) continue; break; } if (FD_ISSET(client, &set)) { if (iucvtty_read_msg(client, msg, MSG_BUFFER_SIZE, &chunk)) break; switch (msg->type) { case MSG_TYPE_DATA: iucvtty_copy_data(master, msg); break; case MSG_TYPE_WINSIZE: if (msg->datalen != sizeof(struct winsize)) break; if (ioctl(master, TIOCSWINSZ, (struct winsize *) msg->data)) print_error("Resizing the terminal " "window failed"); break; case MSG_TYPE_TERMIOS: /* ignored */ break; case MSG_TYPE_ERROR: iucvtty_error(msg); break; } } if (FD_ISSET(master, &set)) if (iucvtty_tx_data(client, master, msg, MSG_BUFFER_SIZE)) break; } free(msg); out_kill_login: /* ensure the chld is terminated before calling waitpid: * - in case a sigterm has been received, * - or a sigchld from other than the chld */ kill(child, SIGKILL); /* cause a sigchld */ waitpid(child, NULL, 0); return rc; } /** * main() - IUCV TTY program startup */ int main(int argc, char *argv[]) { struct iucvterm_cfg conf; /* program configuration */ struct sockaddr_iucv saddr, caddr; /* IUCV socket address info */ char client_host[9]; /* client guest name */ int server, client; /* socket file descriptors */ int master, slave; /* pre-allocated PTY fds */ struct sigaction sigact; /* signal handler */ int rc; socklen_t len; /* gettext initialization */ gettext_init(); /* parse command line arguments */ parse_options(PROG_IUCV_TTY, &conf, argc, argv); /* create server socket... */ server = iucvtty_socket(&saddr, NULL, conf.service); if (server == -1) { print_error((errno == EAFNOSUPPORT) ? N_("The AF_IUCV address family is not available") : N_("Creating the AF_IUCV socket failed")); return 1; } if (bind(server, (struct sockaddr *) &saddr, sizeof(saddr)) == -1) { print_error("Binding the AF_IUCV socket failed"); close(server); return 1; } if (listen(server, 1) == -1) { print_error("Listening for incoming connections failed"); close(server); return 1; } /* pre-allocate PTY master/slave file descriptors */ if (openpty(&master, &slave, NULL, NULL, NULL)) { print_error("Opening a new PTY master/slave device pair failed"); close(server); return 1; } /* set close-on-exec for file descriptors */ fcntl(master, F_SETFD, FD_CLOEXEC); fcntl(server, F_SETFD, FD_CLOEXEC); /* syslog */ openlog(SYSLOG_IDENT, LOG_PID, LOG_AUTHPRIV); syslog(LOG_INFO, "Listening on terminal ID: %s, using pts device: %s", conf.service, ttyname(slave)); rc = 0; len = sizeof(struct sockaddr_iucv); /* accept a new client connection */ client = accept(server, (struct sockaddr *) &caddr, &len); if (client == -1) { print_error("An incoming connection could not be accepted"); rc = 2; goto exit_on_error; } /* check if client is allowed to connect */ userid_cpy(client_host, caddr.siucv_user_id); if (is_client_allowed(client_host, &conf)) { iucvtty_tx_error(client, ERR_NOT_AUTHORIZED); syslog(LOG_WARNING, "Rejected client connection from %s; " "Client is not allowed to connect.", client_host); rc = 3; } else { /* client is allowed to connect */ syslog(LOG_INFO, "Accepted client connection from %s", client_host); /* set close-on-exec for client socket */ fcntl(client, F_SETFD, FD_CLOEXEC); /* close server socket */ close(server); /* setup signal handler to notify shutdown signal */ sigemptyset(&sigact.sa_mask); sigact.sa_flags = SA_RESTART; sigact.sa_handler = sig_handler; if (sigaction(SIGCHLD, &sigact, NULL) || sigaction(SIGTERM, &sigact, NULL) || sigaction(SIGINT, &sigact, NULL) || sigaction(SIGPIPE, &sigact, NULL)) { print_error("Registering a signal handler failed"); rc = 4; goto exit_on_error; } /* handle client terminal connection */ rc = iucvtty_worker(client, master, slave, &conf); } close(client); exit_on_error: close(slave); close(master); closelog(); return rc; } s390-tools-2.38.0/iucvterm/src/ttyrun.c000066400000000000000000000117411502674226300176110ustar00rootroot00000000000000/* * ttyrun - Start a program if a specified terminal device is available * * * ttyrun is typically used to prevent a respawn through the init(8) * program when a terminal is not available. * ttyrun runs the specific program if the specified terminal device * can be opened successfully. Otherwise the program enters a sleep or * exits with a specified return value. * * Example: To start /sbin/agetty on terminal device hvc1, use: * * h1:2345:respawn:/sbin/ttyrun hvc1 /sbin/agetty -L 9600 %t linux * * Note: %t is resolved to the terminal device "hvc1" before /sbin/agetty * is started. * * Return values: * 1 - invalid argument or parameter is missing * 2 - terminal does not resolve to a terminal device * 3 - starting the specified program failed * 1..255 - terminal is not available and the return code is * specified with the -e option * * Copyright 2017 IBM Corp. * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #include "lib/zt_common.h" #define TTY_ESCAPE_STR "%t" #define EXIT_INVALID_ARG 1 #define EXIT_NO_TERMINAL 2 #define EXIT_EXEC_FAILED 3 static const char usage[] = "Usage: %s [-e status] []\n" " %s [-h|--help] [-v|--version]\n" "\n" "Start the program if the specified terminal device is available.\n" "If the terminal device cannot be opened, sleep until a signal is received\n" "that causes an exit or exit with the return value specified with status.\n" "\n" "-e, --exitstatus Specifies an exit status in the range 1 to 255.\n" "-V, --verbose Displays syslog messages.\n" "-h, --help Displays this help, then exits.\n" "-v, --version Displays version information, then exits.\n"; static void __noreturn help_exit(const char *prg) { printf(usage, prg, prg); exit(EXIT_SUCCESS); } static void __noreturn version_exit(const char *prg) { printf("%s: Start a program if a terminal device is available, " "version %s\n", prg, RELEASE_STRING); printf("Copyright IBM Corp. 2010, 2017\n"); exit(EXIT_SUCCESS); } static void err_exit(const char *prg, const char *msg) { fprintf(stderr, "%s: %s\n", prg, msg); exit(EXIT_INVALID_ARG); } static void wait_and_exit(void) { /* sleep until a signal is received, then exit */ pause(); exit(EXIT_SUCCESS); } static const struct option prog_opts[] = { { "help", no_argument, NULL, 'h'}, { "version", no_argument, NULL, 'v'}, { "exitstatus", required_argument, NULL, 'e'}, { "verbose", no_argument, NULL, 'V'}, { NULL, no_argument, NULL, 0 }, }; int main(int argc, char *argv[]) { int rc, tty, i, c, index, done, term_index, verbose; char terminal[PATH_MAX] = ""; unsigned long exitstatus; /* parse command options */ if (argc == 1) err_exit(argv[0], "One or more options are required but missing"); exitstatus = done = term_index = verbose = 0; while (!done) { c = getopt_long(argc, argv, "-hve:V", prog_opts, NULL); switch (c) { case -1: done = 1; break; case 1: /* the first non-optional argument must be the * terminal device */ if (!strncmp(optarg, "/", 1)) strncpy(terminal, optarg, PATH_MAX - 1); else snprintf(terminal, PATH_MAX, "/dev/%s", optarg); terminal[PATH_MAX - 1] = 0; term_index = optind - 1; done = 1; break; case 'e': errno = 0; exitstatus = strtoul(optarg, (char **) NULL, 10); if (errno == ERANGE) err_exit(argv[0], "The exit status must be " "an integer in the range 1 to 255"); if (!exitstatus || exitstatus > 255) err_exit(argv[0], "The exit status must be " "in the range 1 to 255"); break; case 'V': verbose = 1; break; case 'h': help_exit(argv[0]); case 'v': version_exit(argv[0]); case '?': fprintf(stderr, "Try %s --help for more information\n", argv[0]); exit(EXIT_INVALID_ARG); } } index = optind; /* check terminal */ if (!strlen(terminal)) err_exit(argv[0], "You must specify the name of " "a terminal device"); /* any program to start? */ if (index == argc) err_exit(argv[0], "You must specify a program to start"); /* open and check terminal device */ tty = open(terminal, O_NOCTTY | O_RDONLY | O_NONBLOCK); if (tty == -1) { if (verbose) { openlog(argv[0], LOG_PID, LOG_DAEMON); syslog(LOG_INFO, "Could not open tty %s (%s)", terminal, strerror(errno)); closelog(); } /* enter wait or exit */ if (exitstatus) exit(exitstatus); wait_and_exit(); } rc = !isatty(tty); close(tty); if (rc) exit(EXIT_NO_TERMINAL); /* start getty program */ for (i = index; i < argc; i++) if (!strcmp(argv[i], TTY_ESCAPE_STR) && term_index) argv[i] = argv[term_index]; if (execv(argv[index], argv + index)) exit(EXIT_EXEC_FAILED); exit(EXIT_SUCCESS); } s390-tools-2.38.0/iucvterm/test/000077500000000000000000000000001502674226300162645ustar00rootroot00000000000000s390-tools-2.38.0/iucvterm/test/Makefile000066400000000000000000000015431502674226300177270ustar00rootroot00000000000000#! /usr/bin/make -f include ../../common.mak ALL_CPPFLAGS += -I../include ALL_CFLAGS += -g PROGRAMS = test_afiucv TEST_PROGRAMS = test_functions test_allow TEST_PROGRAMS_NORUN = test_auditlog test_afiucv: test_afiucv.o test_functions: test_functions.o test_common.o ../src/functions.o test_allow: test_allow.o test_common.o ../src/functions.o test_auditlog: test_auditlog.o test_common.o ../src/auditlog.o \ ../src/functions.o all: $(PROGRAMS) check: $(TEST_PROGRAMS) $(TEST_PROGRAMS_NORUN) @for prg in $(TEST_PROGRAMS); do \ failed=0 ;\ echo ; echo "=== RUN : $$prg ===" ;\ ./$$prg || failed=$$? ;\ if test x$$failed = x0; then \ echo "=== PASS: $$prg ===" ;\ else \ echo "=== FAIL: $$prg (rc=$$failed) ===" ;\ fi ;\ done install: clean: -rm -f *.o $(PROGRAMS) $(TEST_PROGRAMS) $(TEST_PROGRAMS_NORUN) .PHONY: install clean s390-tools-2.38.0/iucvterm/test/test.h000066400000000000000000000011361502674226300174150ustar00rootroot00000000000000/* * test_common - Test program for the IUCV Terminal Applications * * Definition of common functions for test programs * * Copyright IBM Corp. 2008, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef __TEST_H_ #define __TEST_H_ #include #include #include "lib/util_base.h" #include "iucvterm/proto.h" #define __fail() assert(0); extern int __socketpair(int sv[2]); extern int __msgcmp(const struct iucvtty_msg *, const struct iucvtty_msg *); #endif /* __TEST_H_ */ s390-tools-2.38.0/iucvterm/test/test_afiucv.c000066400000000000000000000012241502674226300207430ustar00rootroot00000000000000/* * test_afiucv - Test program for the IUCV Terminal Applications * * Test program to check if the AF_IUCV family is supported by * the running Linux kernel * * Copyright IBM Corp. 2008, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include "af_iucv.h" int main(void) { int sk; sk = socket(AF_IUCV, SOCK_STREAM, 0); if (sk == -1) { if (errno == EAFNOSUPPORT) perror("AF_IUCV address family not supported"); return -1; } else close(sk); return 0; } s390-tools-2.38.0/iucvterm/test/test_allow.c000066400000000000000000000037121502674226300206100ustar00rootroot00000000000000/* * test_allow - Test program for the IUCV Terminal Applications * * Test program to check common functions that use the regex api * * Copyright IBM Corp. 2008, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include "iucvterm/config.h" #include "iucvterm/functions.h" #include "test.h" static int test_userid_cpy(char padded[9]) { char uid[8]; char id_str[9]; size_t len; memcpy(uid, padded, 8); userid_cpy(id_str, uid); len = strlen(id_str); assert(0 == memcmp(padded, id_str, len)); if (strchr(id_str, ' ') != NULL) __fail(); return len; } int main(void) { char re_ok[] = "^T63[[:digit:]]{1,5}$"; char re_wrong[] = "^t63[[:alpha:]+$"; char re_short[] = "^lnx[[:digit:]]{3}$"; char id_short[9]; char user_id[8]; /* redirect stderr to /dev/null to avoid regex error output */ freopen("/dev/null", "w", stderr); assert(-1 == is_regex_valid(re_wrong)); assert(-1 == is_regex_valid(NULL)); assert(0 == is_regex_valid(re_ok)); assert(0 == is_regex_valid(re_short)); /* simple */ assert(0 == strmatch("T6345050", "T6345050")); /* using re */ assert(0 == strmatch("T6345050", re_ok)); assert(0 == strmatch("t6345050", re_ok)); /* icase */ assert(-1 == strmatch("T6345050", NULL)); assert(-1 == strmatch("T6345050", re_wrong)); assert(1 == strmatch("T634505A", re_ok)); /* test userid_cpy() function */ snprintf(id_short, 9, "%-8s", ""); assert(0 == test_userid_cpy(id_short)); snprintf(id_short, 9, "1"); assert(1 == test_userid_cpy(id_short)); snprintf(id_short, 9, "12345678"); assert(8 == test_userid_cpy(id_short)); snprintf(id_short, 9, "ABCD"); assert(4 == test_userid_cpy(id_short)); /* check for user IDs < 8 characters */ snprintf(id_short, 9, "%-8s", "LNX001"); memcpy(user_id, id_short, 8); userid_cpy(id_short, user_id); assert(0 == strmatch(id_short, re_short)); return 0; } s390-tools-2.38.0/iucvterm/test/test_auditlog.c000066400000000000000000000031601502674226300212770ustar00rootroot00000000000000/* * test_auditlog - Test program for the IUCV Terminal Applications * * Test program for the session logging functions used by iucvconn(1). * * Copyright IBM Corp. 2008, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include "iucvterm/functions.h" #include "test.h" #define BUF_SIZE 1024 static void do_cleanup(const char *filename) { char tmp[64]; if (access(filename, W_OK) == 0) unlink(filename); snprintf(tmp, 64, "%s.timing", filename); if (access(tmp, W_OK) == 0) unlink(tmp); snprintf(tmp, 64, "%s.info", filename); if (access(tmp, W_OK) == 0) unlink(tmp); } int main(int argc, char* argv[]){ char *filepath = NULL; char *buf; char tmpfile[64]; int interactive; ssize_t rc; if (argc < 2) { fprintf(stderr, "Usage: %s \n", argv[0]); sprintf(tmpfile, "/tmp/test_auditlog.a1b2c3.%u", getpid()); fprintf(stderr, "now using file %s\n", tmpfile); filepath = tmpfile; /* cleanup stale file */ if (access(tmpfile, W_OK) == 0) do_cleanup(tmpfile); interactive = 0; } else { interactive = 1; filepath = argv[1]; } buf = malloc(BUF_SIZE); rc = open_session_log(filepath); assert(rc == 0); write_session_info("The name of the log file is: %s\n", filepath); sprintf(buf, "This is an entry\n"); rc = write_session_log(buf, strlen(buf)); assert(rc == 0); close_session_log(); free(buf); if (!interactive) do_cleanup(tmpfile); return 0; } s390-tools-2.38.0/iucvterm/test/test_chiucvallow.sh000077500000000000000000000073501502674226300222070ustar00rootroot00000000000000#!/bin/sh # # Test program for chiucvallow # # # Copyright IBM Corp. 2009, 2017 # # s390-tools is free software; you can redistribute it and/or modify # it under the terms of the MIT license. See LICENSE for details. # # set -x # test data file test_data=`mktemp /tmp/chiucvallow.testdata.XXXXXX` LOG_FILE=/dev/null TEST_PROG=${1:-../bin/chiucvallow.in} # run test program (we can savely use the .in version here) tprg() { test -x $TEST_PROG || chmod +x $TEST_PROG $TEST_PROG $@ >$LOG_FILE 2>&1 return $? } failed() { echo $1 exit 3 } # Init test -r "$test_data" || failed "Failed to create temporary file" trap "rm -f $test_data" EXIT TERM INT # 1: Test special characters cat > $test_data <<'EoData' # user ID filter with some exotic user ID names # # z/VM user ID consists mainly of alphanumeric characters. # Additional characters like @#_-$ are also allowed. However, @ and # are not # recommended. Additional characters can be configured. # # To use a common set of all the values possible, the iucv terminal tools # uses alphnumeric and underscore. The chiucvallow program also allows to use # the dollar sign ($). # # All other characters must not be used. # # NORMAL # underscores are allowed WITH_ # dollar sign is allowed for chiucvallow only WITH$ $VAR $ALLOC$ $$$$$$$$ 12345678 EoData tprg -V $test_data || failed "1: Failed: verify special z/VM user ID chars" # 2: Test special characters cat > $test_data <<'EoData' # the following entry fails -HYPHEN EoData tprg -V $test_data && failed "2: Failed: verify special z/VM user ID chars" # 3: Test maximum number of filter entries for i in `seq 1 500`; do printf "ID%05d\n" $i ; done > $test_data tprg -V $test_data || failed "3: Failed: verify correct numbers of filter entries" # 4: Test verification fails because of too many filter entries for i in `seq 1 501`; do printf "ID%05d\n" $i ; done > $test_data tprg -V $test_data && failed "4: Failed: verify correct numbers of filter entries" # 5: Test verification fails because of too many filter entries for i in `seq 1 510`; do printf "ID%05d\n" $i ; done > $test_data tprg -V $test_data && failed "5: Failed: verify correct numbers of filter entries" # 6: Test filter size (final size of filter entries, not file size) cat > $test_data <<'EoData' # z/VM user ID filter file for testing chiucvallow # # This file defines 500 user IDs with few comments to # increase the file size. # # The comments must not be ignored for file size calculation, because # comments are not part of the hvc_iucv_allow sysfs attribute. # # The size of the z/VM user ID filer that is reported by chiucvallow # must be: 4000 bytes = 7 * 500 + 500 # (500 user IDs a 7 bytes (in length) + 500 carriage returns) EoData for i in `seq 1 500`; do printf "ID%05d\n" $i ; done >> $test_data echo '# End of z/VM user ID filter file ' >> $test_data test `stat -c%s $filename 2>/dev/null || echo 0` -ge 4095 \ && failed "6: Failed to create test pre-req" tprg -V $test_data || failed "6: Failed: verify filter with correct filter size" # 7: Test filter size that exceed 4K (500 * 8 + 500 = 4500) for i in `seq 1 500`; do printf "ID%06d\n" $i ; done > $test_data tprg -V $test_data && failed "7: Failed: verify filter with incorred filter size" # 8: Test valid wildcard filter entries cat > $test_data <<'EoData' # z/VM user ID filter for testing chiucvallow # # This file contains wildcard filter entries that must be considered as # valid. # ID100* A* AB* ABC* ABCD* ABCDE* ABCDEF* ABCDEFG* 12345678 EoData tprg -V $test_data || failed "8: Failed: verify filter with valid wildcards" # 9: Test wildcard filter entries that are not valid for ent in "*" "*A" "ABC*A" "A2345678*"; do echo "$ent" > $test_data tprg -V $test_data && failed "9: Failed: verified invalid wildcard: '$ent'" done exit 0 s390-tools-2.38.0/iucvterm/test/test_common.c000066400000000000000000000015421502674226300207610ustar00rootroot00000000000000/* * test_common library - Test program lib for the IUCV Terminal Applications * * Common functions for test programs * * Copyright IBM Corp. 2008, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include "iucvterm/proto.h" int __socketpair(int sv[2]) { int rc; rc = socketpair(AF_LOCAL, SOCK_STREAM, 0, sv); if (rc) perror("Could not create socketpair"); assert(rc == 0); return rc; } int __msgcmp(const struct iucvtty_msg *m1, const struct iucvtty_msg *m2) { if (m1->type != m2->type) return 1; if (m1->datalen != m2->datalen) return 2; if (0 != memcmp(m1->data, m2->data, m1->datalen)) return 3; return 0; } s390-tools-2.38.0/iucvterm/test/test_functions.c000066400000000000000000000135111502674226300215000ustar00rootroot00000000000000/* * test_functions - Test program for the IUCV Terminal Applications * * Test program for the common functions used by the * IUCV Terminal Applications * * Copyright IBM Corp. 2008, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include "iucvterm/functions.h" #include "iucvterm/proto.h" #include "test.h" #define BUF_SIZE 256 static int __testReadWriteMessage(int[2]); static int __testReadWriteMessage_chunks(int[2]); static int __testReadWriteMessage_nochunks(int[2]); static int __testTransmitData(int[2]); static int __testTermEnvVar(int[2]); static int __testWinsize(int[2]); static int __testCopyData(int[2]); int main(void) { int sockv[2]; __socketpair(sockv); assert(!__testReadWriteMessage(sockv)); assert(!__testReadWriteMessage_chunks(sockv)); assert(!__testReadWriteMessage_nochunks(sockv)); assert(!__testTermEnvVar(sockv)); assert(!__testWinsize(sockv)); assert(!__testCopyData(sockv)); assert(!__testTransmitData(sockv)); close(sockv[0]); close(sockv[1]); return 0; } static int __testReadWriteMessage_chunks(int sv[2]) { struct iucvtty_msg *msg[2]; char data[] = "Hallo\0test1\0test2\0test3\0test4\0test5\0test6\0test7\0test8"; size_t chunk; int i; /* setup msg to be sent */ msg[0] = msg_alloc(MSG_TYPE_DATA, BUF_SIZE); msg[1] = msg_alloc(MSG_TYPE_DATA, BUF_SIZE); /* send msg */ msg_cpy_from(msg[0], data, sizeof(data)); if (iucvtty_write_msg(sv[0], msg[0])) return 1; /* read msg */ chunk = 0; for (i = 0; i < 9; i++) { if (iucvtty_read_msg(sv[1], msg[1], 6 + MSG_DATA_OFFSET, &chunk)) return 2; assert(msg[1]->datalen == 6); assert(0 == memcmp(msg[1]->data, data + (i * 6), msg[1]->datalen)); /* printf("chunk=%i datalen=%u data='%s'\n", chunk, msg[1]->datalen, msg[1]->data); */ } assert(chunk == 0); msg_free(msg[0]); msg_free(msg[1]); return 0; } static int __testReadWriteMessage_nochunks(int sv[2]) { struct iucvtty_msg *msg[2]; char data[] = "Hallo\0test1\0test2\0test3\0test4\0test5\0test6\0test7\0test8"; int i; size_t residual = 0; /* setup msg to be sent */ msg[0] = msg_alloc(MSG_TYPE_DATA, BUF_SIZE); msg[1] = msg_alloc(MSG_TYPE_DATA, BUF_SIZE); /* send and read msg .. with chunks disabled. * The iucvtty_read_msg() shall ignore any data more than the given * length. */ i = 7; while (i--) { msg_cpy_from(msg[0], data, sizeof(data)); if (iucvtty_write_msg(sv[0], msg[0])) return 1; /* read msg */ if (iucvtty_read_msg(sv[1], msg[1], 6 + MSG_DATA_OFFSET, &residual)) return 2; assert(0 == memcmp(msg[1]->data, data, msg[1]->datalen)); iucvtty_skip_msg_residual(sv[1], &residual); assert(0 == residual); /*printf("datalen=%u data='%s'\n", msg[1]->datalen, msg[1]->data);*/ } msg_free(msg[0]); msg_free(msg[1]); return 0; } static int __testReadWriteMessage(int sv[2]) { struct iucvtty_msg *msg[2]; char data[10] = "Hallo"; size_t chunk = 0; /* setup msg to be sent */ msg[0] = msg_alloc(MSG_TYPE_DATA, BUF_SIZE); msg[1] = msg_alloc(MSG_TYPE_DATA, BUF_SIZE); /* send msg */ msg_cpy_from(msg[0], data, sizeof(data)); if (iucvtty_write_msg(sv[0], msg[0])) return 1; /* read msg */ if (iucvtty_read_msg(sv[1], msg[1], BUF_SIZE, &chunk)) return 2; iucvtty_skip_msg_residual(sv[1], &chunk); /* compare msg */ if (__msgcmp(msg[0], msg[1])) return 3; msg_free(msg[0]); msg_free(msg[1]); return 0; } static int __testTransmitData(int sv[2]) { struct iucvtty_msg *msg; char data[] = "Vah __testTransmitData hai."; size_t chunk = 0; /* I. write something to sv[0] * II. tx_data: read from sv[1], write to sv[0] * III. read iucvtty_msg from sv[1] */ assert(sizeof(data) == write(sv[0], data, sizeof(data))); msg = msg_alloc(MSG_TYPE_DATA, BUF_SIZE); assert(0 == iucvtty_tx_data(sv[0], sv[1], msg, BUF_SIZE)); assert(0 == iucvtty_read_msg(sv[1], msg, BUF_SIZE, &chunk)); assert(MSG_TYPE_DATA == msg->type); assert(sizeof(data) == msg->datalen); assert(0 == memcmp(&data, msg->data, msg->datalen)); msg_free(msg); return 0; } static int __testTermEnvVar(int sv[2]) { #define __BUF_SIZE 81 char term[__BUF_SIZE] = "my_default_term"; char buf[__BUF_SIZE]; char *orig_term = getenv("TERM"); /* I. */ if (orig_term != NULL) { assert(0 == iucvtty_tx_termenv(sv[0], NULL)); assert(0 == iucvtty_rx_termenv(sv[1], buf, __BUF_SIZE)); assert(0 == strncmp(orig_term, buf, MIN(__BUF_SIZE, strlen(orig_term)))); } unsetenv("TERM"); /* clear term */ /* II. */ assert(0 == iucvtty_tx_termenv(sv[0], term)); assert(0 == iucvtty_rx_termenv(sv[1], buf, __BUF_SIZE)); assert(0 == strncmp(buf, term, __BUF_SIZE)); /* III. */ assert(0 == iucvtty_tx_termenv(sv[0], NULL)); assert(0 == iucvtty_rx_termenv(sv[1], buf, __BUF_SIZE)); assert(0 == strncmp("", buf, 1)); setenv("TERM", orig_term, 1); /* restore original term */ return 0; #undef __BUF_SIZE } static int __testWinsize(int sv[2]) { struct iucvtty_msg *msg; struct winsize winsz; size_t chunk = 0; msg = msg_alloc(MSG_TYPE_DATA, BUF_SIZE); assert(0 == ioctl(STDIN_FILENO, TIOCGWINSZ, &winsz)); assert(0 == iucvtty_tx_winsize(sv[0], STDIN_FILENO)); assert(0 == iucvtty_read_msg(sv[1], msg, BUF_SIZE, &chunk)); assert(MSG_TYPE_WINSIZE == msg->type); assert(sizeof(struct winsize) == msg->datalen); assert(0 == memcmp(&winsz, msg->data, msg->datalen)); msg_free(msg); return 0; } static int __testCopyData(int sv[2]) { struct iucvtty_msg *msg; char data[] = "palim palim"; char buf[BUF_SIZE]; msg = msg_alloc(MSG_TYPE_DATA, BUF_SIZE); msg_cpy_from(msg, data, sizeof(data)); assert(0 == iucvtty_copy_data(sv[0], msg)); assert(sizeof(data) == read(sv[1], &buf, BUF_SIZE)); assert(0 == memcmp(data, buf, sizeof(data))); msg_free(msg); return 0; } s390-tools-2.38.0/libap/000077500000000000000000000000001502674226300145365ustar00rootroot00000000000000s390-tools-2.38.0/libap/Makefile000066400000000000000000000005661502674226300162050ustar00rootroot00000000000000include ../common.mak lib = libap.a check-dep-json: touch check-dep-json ifneq (${HAVE_JSONC},0) $(call check_dep, \ "libap", \ "json-c/json.h", \ "json-c-devel", \ "HAVE_JSONC=0") ALL_CPPFLAGS += -DHAVE_JSONC endif all: $(lib) objects = ap.o $(lib): $(objects) $(objects): check-dep-json install: all clean: rm -f *.o check-dep-lock check-dep-json $(lib) s390-tools-2.38.0/libap/ap.c000066400000000000000000000557211502674226300153140ustar00rootroot00000000000000/* * libap - A collection of tools for ap/vfio-ap management * * Copyright IBM Corp. 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_JSONC #include #endif /* HAVE_JSONC */ #include "lib/ap.h" #include "lib/util_file.h" #include "lib/util_libc.h" #include "lib/util_lockfile.h" #include "lib/util_panic.h" #include "lib/util_path.h" #include "lib/util_udev.h" static const char default_mask[AP_MASK_SIZE] = "0x0000000000000000000000000000000000000000000000000000000000000000"; /* * Return sysfs path to a bus attribute * Note: caller is responsible for freeing the returned string */ static char *path_get_bus_attr(const char *bus, const char *attr) { return util_path_sysfs("bus/%s/%s", bus, attr); } /* * Compare two vfio_ap nodes based upon their id value. Return: * -1: a < b * 1: a > b * 0: a == b */ static int vfio_ap_node_cmp(void *a, void *b, void *UNUSED(data)) { struct vfio_ap_node *a_node = a, *b_node = b; if (a_node->id < b_node->id) return -1; return (a_node->id > b_node->id); } static void vfio_ap_node_add_tail(struct util_list *list, unsigned int val) { struct vfio_ap_node *node = util_zalloc(sizeof(struct vfio_ap_node)); node->id = val; util_list_add_tail(list, node); } /* Remove duplicate entries from a sorted vfio_ap_node list */ static void vfio_ap_node_remove_dupes(struct util_list *list) { struct vfio_ap_node *node, *check, *next; util_list_iterate(list, node) { /* Remove any subsequent duplicates */ check = util_list_next(list, node); while (check) { next = util_list_next(list, check); if (node->id == check->id) { util_list_remove(list, check); free(check); } check = next; } } } static bool starts_with(const char *str, const char *s) { size_t len = strlen(s); return (strncmp(str, s, len) == 0); } /* * Pass a comma-delimited string of IDs (adapter, domains or control domains) * and add these IDs to the input list. The resulting list will be sorted * and duplicates removed before returning. */ static void modify_device_attr(struct util_list *list, char *value) { unsigned int val; char *curr; curr = strtok(value, ","); if (curr == NULL) return; /* Create list from input setting */ while (curr != NULL) { val = strtol(curr, NULL, 0); vfio_ap_node_add_tail(list, val); curr = strtok(NULL, ","); } /* Cleanup the list */ util_list_sort(list, vfio_ap_node_cmp, NULL); vfio_ap_node_remove_dupes(list); } /* * Pass a comma-delimited string of masks (adapters,domains,controls) and * for each ON bit in these masks add the associated ID to the device * lists. */ static void modify_device_ap_config(struct vfio_ap_device *dev, char *value) { char *mask, *adapters, *domains, *controls; mask = util_strdup(value); adapters = strtok(mask, ","); domains = strtok(NULL, ","); controls = strtok(NULL, ","); util_assert((!strtok(NULL, ",")) && adapters && domains && controls, "Invalid ap_config attribute encountered %s", value); /* * ap_config overwrites the current list of adapters, domains and * control domains. Clear the current lists before generating new ones * based upon the input mask values. */ ap_list_remove_all(dev->adapters); ap_list_remove_all(dev->domains); ap_list_remove_all(dev->controls); ap_mask_to_list(adapters, dev->adapters); ap_mask_to_list(domains, dev->domains); ap_mask_to_list(controls, dev->controls); } static void load_attr_to_device(struct vfio_ap_device *dev, char *attr, const char *value) { char *v = util_strdup(value); if (strcmp(attr, "assign_adapter") == 0) modify_device_attr(dev->adapters, v); else if (strcmp(attr, "assign_domain") == 0) modify_device_attr(dev->domains, v); else if (strcmp(attr, "assign_control_domain") == 0) modify_device_attr(dev->controls, v); else if (strcmp(attr, "ap_config") == 0) modify_device_ap_config(dev, v); free(v); } /** * Print the contents of the vfio-ap device struct to stderr. Used for * debugging. * * @param[in] dev vfio-ap device strcucture to print */ void print_ap_device(struct vfio_ap_device *dev) { struct vfio_ap_node *node; warnx("Device %s:", dev->uuid); warnx("Type: %s", dev->type); if (dev->manual) warnx("Start: MANUAL"); else warnx("Start: AUTO"); if (util_list_is_empty(dev->adapters)) { warnx("Adapters: (none)"); } else { warnx("Adapters:"); util_list_iterate(dev->adapters, node) { warnx(" %u", node->id); } } if (util_list_is_empty(dev->domains)) { warnx("Domains: (none)"); } else { warnx("Domains:"); util_list_iterate(dev->domains, node) { warnx(" %u", node->id); } } if (util_list_is_empty(dev->controls)) { warnx("Controls: (none)"); } else { warnx("Controls:"); util_list_iterate(dev->controls, node) { warnx(" %u", node->id); } } } /** * Determine if the input string is a valid Universally Unique Identifier * * @param[in] uuid Character string to inspect * * @retval true Specified string is a valid UUID * @retval false Specified string is not a valid UUID */ bool is_valid_uuid(const char *uuid) { uint32_t s1, s2, s3, s4; uint64_t s5; char d; return (strlen(uuid) == 36 && sscanf(uuid, "%8x-%4x-%4x-%4x-%12x %c", &s1, &s2, &s3, &s4, (unsigned int *) &s5, &d) == 5); } /* * For an input string of hex characters, find the nth character and return its * numeric value to the caller. */ static int get_hexbyte_value(int n, const char *hexbytestr) { int i = 0, v = 0; char c; if (strncmp(hexbytestr, "0x", 2) == 0) i = 2; /* * The specified mask is only valid if it includes at least 64 hex * digits. The specified mask may optionally include a leading '0x' */ util_assert((strlen(hexbytestr) >= (size_t)(AP_MASK_SIZE - i - 1)), "Invalid hex string provided for mask: %s", hexbytestr); c = hexbytestr[i + n / 4]; if (c >= '0' && c <= '9') v = c - '0'; else if (c >= 'a' && c <= 'f') v = 10 + c - 'a'; else if (c >= 'A' && c <= 'F') v = 10 + c - 'A'; else util_assert(false, "Could not parse hex digit '%c'", c); return v; } /** * For an input hex string, determine if the specified nth bit is ON or OFF. * * @param[in] n Bit number to test * @param[in, out] hexbytestr Character string of hex characters * * @retval 0 Specified bit is OFF * @retval != 0 Specified bit is ON */ int ap_test_bit(int n, const char *hexbytestr) { int v; v = get_hexbyte_value(n, hexbytestr); return v & (1 << (3 - (n % 4))); } /** * For an input hex string, set the nth bit true/false * * @param[in] n Bit number to set * @param[in, out] hexbytestr Character string of hex characters * @param[in] val Bit is to be set ON (true) or OFF (false) */ void ap_set_bit(int n, char *hexbytestr, bool val) { char c = 0; int v, m, i = 0; v = get_hexbyte_value(n, hexbytestr); /* Calculate the bit mask */ m = (1 << (3 - (n % 4))); /* Return if bit already at correct value */ if (((val) && ((v & m) != 0)) || (!val && ((v & m) == 0))) return; if (val) v = v + m; else v = v - m; if (v < 10) c = '0' + v; else if (v >= 10 && v <= 15) c = 'a' + (v - 10); else util_assert(false, "Could not set bit value '%d'", v); if (strncmp(hexbytestr, "0x", 2) == 0) i = 2; /* Set the new value */ hexbytestr[i + n / 4] = c; } /** * Return sysfs path to vfio_ap mdev * Note: caller is responsible for freeing the returned string * * @param[in] uuid Character string containing an mdev UUID * * @retval != 0 sysfs path for a vfio-ap device with this UUID */ char *path_get_vfio_ap_mdev(const char *uuid) { return util_path_sysfs("%s/%s", VFIO_AP_PARENT_PATH, uuid); } /** * Return path to mdevctl config file for specified UUID * Note: caller is responsible for freeing the returned string * * @param[in] uuid Character string containing an mdev UUID * * @retval != 0 config file path for vfio-ap device with this UUID */ char *path_get_vfio_ap_mdev_config(const char *uuid) { char *path; util_asprintf(&path, "%s/%s", VFIO_AP_CONFIG_PATH, uuid); return path; } /** * Return sysfs path to vfio_ap mdev attribute (matrix, remove, ...) * Note: caller is responsible for freeing the returned string * * @param[in] uuid Character string containing an mdev UUID * @param[in] attr Character string containing device attribute name * * @retval != 0 sysfs path to specified attribute for this device */ char *path_get_vfio_ap_attr(const char *uuid, const char *attr) { return util_path_sysfs("%s/%s/%s", VFIO_AP_PARENT_PATH, uuid, attr); } /** * Return path to ap udev config file * Note: caller is responsible for freeing the returned string * * @retval != 0 path to the ap udev config file */ char *path_get_ap_udev(void) { char *path; util_asprintf(&path, "%s", AP_UDEV_FILE); return path; } /** * Take one line from the active 'matrix' attribute and parse it * into a list of adapters and domains. Each line of the 'matrix' * attribute is presented as a "adapter.domain" (e.g. "03.0005") where the * numeric values are always hexadecimal. * In a case where no adapters are assigned, a valid string might be ".0005" * In a case where no domains are assigned, a valid string might be "03." * * @param[in, out] dev Vfio-ap struct that will be updated * @param[in] matrix Character string to parse */ void vfio_ap_parse_matrix(struct vfio_ap_device *dev, char *matrix) { char *curr; int val; if (!matrix) return; if (*matrix != '.') { /* Handle a device with adapters */ curr = strtok(matrix, "."); val = strtol(curr, NULL, 16); vfio_ap_node_add_tail(dev->adapters, val); curr = strtok(NULL, "\n"); } else { /* Handle a device with no adapters */ curr = strtok(matrix + 1, "\n"); } /* Leave now if the device has no domains */ if (!curr) return; /* Get the domain */ val = strtol(curr, NULL, 16); vfio_ap_node_add_tail(dev->domains, val); } /** * Function to sort the results of repeated vfio_ap_parse_matrix calls * * @param[in, out] dev Vfio-ap struct whose lists will be sorted */ void vfio_ap_sort_matrix_results(struct vfio_ap_device *dev) { /* Sort the lists for later use */ util_list_sort(dev->adapters, vfio_ap_node_cmp, NULL); util_list_sort(dev->domains, vfio_ap_node_cmp, NULL); /* Run the lists and delete duplicates */ vfio_ap_node_remove_dupes(dev->adapters); vfio_ap_node_remove_dupes(dev->domains); } /** * Take the string provided by the active 'control_domains' attribute and * parse it into a list of control domains * * @param[in, out] dev Vfio-ap struct that will be updated * @param[in] control Character string to parse */ void vfio_ap_parse_control(struct vfio_ap_device *dev, char *control) { char *curr; int val; curr = strtok(control, "\n"); while (curr != NULL) { val = strtol(curr, NULL, 16); vfio_ap_node_add_tail(dev->controls, val); curr = strtok(NULL, "\n"); } } /** * Determine if the specified device is currently active. If so, see if * it is enabled for dynamic configuration support. * * @param[in] dev Vfio-ap struct * * @retval True Device is active and enabled for dynamic config * @retval False Device is not active OR no dynamic config support */ bool vfio_ap_need_dynamic_config(struct vfio_ap_device *dev) { char *attr = path_get_vfio_ap_attr(dev->uuid, "ap_config"); if (!attr) return false; if (!util_path_is_readable(attr)) return false; return true; } #ifdef HAVE_JSONC /** * For a given path, read in the contents. If no path is provided, get the * input from stdin instead. * * @param[in] path Path to mdevctl config file * @param[in, out] dev Vfio-ap struct that will be updated * * @retval 0 Config read successfully, dev updated * @retval -1 Failed to read config, dev may have partial info */ int vfio_ap_read_device_config(const char *path, struct vfio_ap_device *dev) { json_object *root, *type, *start, *attrs, *attr; int i, len, rc = 0; const char *val; if (path == NULL) root = json_object_from_fd(STDIN_FILENO); else root = json_object_from_file(path); if (root == NULL) return -1; if (json_object_object_get_ex(root, "mdev_type", &type)) { val = json_object_get_string(type); if (!val) goto err; dev->type = util_strdup(val); if (strcmp(val, "vfio_ap-passthrough") != 0) goto err; } if (json_object_object_get_ex(root, "start", &start)) { val = json_object_get_string(start); if (!val) goto err; if (strcmp(val, "auto") == 0) dev->manual = false; else if (strcmp(val, "manual") == 0) dev->manual = true; else goto err; } if (json_object_object_get_ex(root, "attrs", &attrs)) { len = json_object_array_length(attrs); for (i = 0; i < len; i++) { attr = json_object_array_get_idx(attrs, i); json_object_object_foreach(attr, key, setting) { val = json_object_get_string(setting); load_attr_to_device(dev, key, val); } } } out: json_object_put(root); return rc; err: rc = -1; goto out; } #else int vfio_ap_read_device_config(const char *path, struct vfio_ap_device *dev) { return -1; } #endif /* HAVE_JSONC */ /** * Allocate and initialize a vfio-ap device structure. * * @retval !=0 Address of the new vfio-ap device structure */ struct vfio_ap_device *vfio_ap_device_new(void) { struct vfio_ap_device *dev; dev = util_zalloc(sizeof(struct vfio_ap_device)); dev->manual = false; dev->type = NULL; dev->uuid = NULL; dev->adapters = util_list_new(struct vfio_ap_node, node); dev->domains = util_list_new(struct vfio_ap_node, node); dev->controls = util_list_new(struct vfio_ap_node, node); return dev; } /** * Re-initialize a vfio-ap device structure, leaving the structure allocated. * * @param[in, out] dev Vfio-ap struct that will be re-initialized */ void vfio_ap_device_clear(struct vfio_ap_device *dev) { if (dev == NULL) return; dev->manual = false; if (dev->type) { free(dev->type); dev->type = NULL; } if (dev->uuid) { free(dev->uuid); dev->uuid = NULL; } ap_list_remove_all(dev->adapters); ap_list_remove_all(dev->domains); ap_list_remove_all(dev->controls); } /** * Clear and release a vfio-ap device structure. * * @param[in, out] dev Vfio-ap struct that will be freed */ void vfio_ap_device_free(struct vfio_ap_device *dev) { if (dev == NULL) return; vfio_ap_device_clear(dev); util_list_free(dev->adapters); util_list_free(dev->domains); util_list_free(dev->controls); free(dev); } static int read_sysfs_mask(const char *path, char *mask, int size) { return util_file_read_line(mask, size, "%s", path); } /** * Get the apmask and aqmask from sysfs * * @param[in, out] ap Buffer to hold apmask contents * @param[in, out] aq Buffer to hold aqmask contents * @param[in] size Size of the mask buffers * * @retval 0 Both mask values read successfully * @retval != 0 Failed to read one or both mask values */ int ap_read_sysfs_masks(char *ap, char *aq, int size) { char *path; int rc = 0; path = path_get_bus_attr("ap", "apmask"); rc = read_sysfs_mask(path, ap, size); free(path); if (rc != 0) goto out; path = path_get_bus_attr("ap", "aqmask"); rc = read_sysfs_mask(path, aq, size); free(path); out: return rc; } /** * Get the apmask and aqmask from udev, falling back to sysfs if a udev rule * is not available or does not provide values for both masks. * The values in read_ap and read_aq tell the caller whether each mask was * successfully loaded from udev or if the sysfs value was substituted * Note: both ap and aq must point to a string that is at least AP_MASK_SIZE * in length. * * @param[in] path Path to the ap udev file * @param[in, out] ap Buffer to hold apmask contents * @param[in, out] aq Buffer to hold aqmask contents * @param[out] read_ap Specifies if an apmask value was read from udev * @param[out] read_aq Specifies if an aqmask value was read from udev * * @retval true Udev file read successfully or did not exist * @retval false Udev file exists but error was encountered */ bool ap_read_udev_masks(char *path, char *ap, char *aq, bool *read_ap, bool *read_aq) { struct util_udev_entry_node *entry; struct util_udev_file *file = NULL; struct util_udev_line_node *line; char sysap[AP_MASK_SIZE]; char sysaq[AP_MASK_SIZE]; int rc; /* Assume we fail to read both masks */ *read_ap = *read_aq = false; /* If a udev file doesn't exist, quietly use the active masks */ if (!util_path_exists(path)) goto out; rc = util_udev_read_file(path, &file); /* If errors were encountered reading the udev file, exit now */ if (rc) return false; util_list_iterate(&file->lines, line) { entry = util_list_start(&line->entries); /* Skip comments and empty lines. */ if (!entry) continue; if (starts_with(entry->key, "ATTR{")) { if (strstr(entry->key, "apmask")) { util_strlcpy(ap, entry->value, AP_MASK_SIZE); *read_ap = true; } else if (strstr(entry->key, "aqmask")) { util_strlcpy(aq, entry->value, AP_MASK_SIZE); *read_aq = true; } } } util_udev_free_file(file); out: /* If we didn't read in masks, use current sysfs values */ if ((!*read_ap) || (!*read_aq)) { rc = ap_read_sysfs_masks(sysap, sysaq, AP_MASK_SIZE); if (rc != 0) return false; if (!*read_ap) strcpy(ap, sysap); if (!*read_aq) strcpy(aq, sysaq); } return true; } /** * For a given bitmask, create a list of vfio_ap_node entries corresponding * to the ON bits in the mask. * * @param[in] mask Character string of hex characters * @param[in, out] list List to be updated with entries for each ON bit */ void ap_mask_to_list(char *mask, struct util_list *list) { int i; if (mask == NULL || list == NULL) return; for (i = 0; i <= AP_MAX_MASK_VALUE; i++) { if (ap_test_bit(i, mask)) vfio_ap_node_add_tail(list, i); } /* Could have duplicates if the input list was not empty */ util_list_sort(list, vfio_ap_node_cmp, NULL); vfio_ap_node_remove_dupes(list); } /** * For the specified list, remove all elements and free each node * * @param[in, out] list List that will have all entries removed */ void ap_list_remove_all(struct util_list *list) { struct vfio_ap_node *node; while (!util_list_is_empty(list)) { node = util_list_start(list); util_list_remove(list, node); free(node); } } static unsigned int random_delay(void) { static bool libap_seed = true; struct timeval t; if (libap_seed) { gettimeofday(&t, NULL); srand((unsigned int)((t.tv_sec + t.tv_usec) % UINT_MAX)); libap_seed = false; } return AP_LOCK_DELAY_US + (rand() % AP_LOCK_VARIANCE_US); } /** * Return a mask of assigned adapters for the specified vfio_ap device. * Note: caller is responsible for freeing the returned string * * @param[in] dev Vfio-ap struct to get adapter mask from * @param[in, out] size Size of mask buffer returned * * @retval != 0 Adapter mask (hex string) * @retval 0 Failed to generate a mask */ char *vfio_ap_device_get_adapter_mask(struct vfio_ap_device *dev, int *size) { struct vfio_ap_node *node; char *mask; if (!dev || !size) return NULL; mask = util_strdup(default_mask); *size = AP_MASK_SIZE; util_list_iterate(dev->adapters, node) { ap_set_bit(node->id, mask, true); } return mask; } /** * Return a mask of assigned domains for the specified vfio_ap device. * Note: caller is responsible for freeing the returned string * * @param[in] dev Vfio-ap struct to get domain mask from * @param[in, out] size Size of mask buffer returned * * @retval != 0 Domain mask (hex string) * @retval 0 Failed to generate a mask */ char *vfio_ap_device_get_domain_mask(struct vfio_ap_device *dev, int *size) { struct vfio_ap_node *node; char *mask; if (!dev || !size) return NULL; mask = util_strdup(default_mask); *size = AP_MASK_SIZE; util_list_iterate(dev->domains, node) { ap_set_bit(node->id, mask, true); } return mask; } /** * Return a mask of assigned control domains for the specified vfio_ap device. * Note: caller is responsible for freeing the returned string * * @param[in] dev Vfio-ap struct to get control domain mask from * @param[in, out] size Size of mask buffer returned * * @retval != 0 Control domain mask (hex string) * @retval 0 Failed to generate a mask */ char *vfio_ap_device_get_control_mask(struct vfio_ap_device *dev, int *size) { struct vfio_ap_node *node; char *mask; if (!dev || !size) return NULL; mask = util_strdup(default_mask); *size = AP_MASK_SIZE; util_list_iterate(dev->controls, node) { ap_set_bit(node->id, mask, true); } return mask; } /** * Acquire the ap config lock using this Process ID * * @retval 0 Lock acquired on behalf of this process * * @retval != 0 Error, lock was not obtained */ int ap_get_lock(void) { unsigned int delay = random_delay(); return util_lockfile_lock_cw(AP_LOCKFILE, AP_LOCK_RETRIES, delay, delay); } /** * Acquire the ap config lock using the Parent Process ID -- intended for use * by the mdevctl callout ap-check utility * * @retval 0 Lock acquired on behalf of parent process * @retval != 0 Error, lock was not obtained */ int ap_get_lock_callout(void) { unsigned int delay = random_delay(); return util_lockfile_parent_lock_cw(AP_LOCKFILE, AP_LOCK_RETRIES, delay, delay); } /** * Attempt to acquire the ap config lock using the Parent Process ID without * waiting/retries. Detect if the attempt was rejected because the lock is * already held by the Parent Process ID. * * @retval 0 Lock acquired on behalf of parent process * @retval 1 Lock not obtained, already held by parent * @retval != 0 Lock was not obtained, other error */ int ap_try_lock_callout(void) { int pid, ppid, rc; if (util_lockfile_parent_lock(AP_LOCKFILE, 0)) { /* Lock is already held, let's peek at the owner */ ppid = getppid(); rc = util_lockfile_peek_owner(AP_LOCKFILE, &pid); if (rc || pid != ppid) { /* We didn't get the lock, unknown or other owner */ return 2; } /* Signify that the lock is already held by the caller */ return 1; } return 0; } /** * Release the ap config lock * * @retval 0 Lock successfully released or file didn't exist * @retval != 0 Error removing the lockfile */ int ap_release_lock(void) { return util_lockfile_release(AP_LOCKFILE); } int ap_release_lock_callout(void) { return util_lockfile_parent_release(AP_LOCKFILE); } s390-tools-2.38.0/libccw/000077500000000000000000000000001502674226300147125ustar00rootroot00000000000000s390-tools-2.38.0/libccw/Makefile000066400000000000000000000002001502674226300163420ustar00rootroot00000000000000include ../common.mak lib = libccw.a all: $(lib) objects = ccw.o $(lib): $(objects) install: all clean: rm -f *.o $(lib) s390-tools-2.38.0/libccw/ccw.c000066400000000000000000000017321502674226300156350ustar00rootroot00000000000000/** * ccw - Channel Command Word function library * * Process ccw_devid * * Copyright 2017 IBM Corp. * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include "lib/ccw.h" /** * Parse a string into a ccw_devid structure * * @param[in,out] devid Pointer to ccw_devid structure to be initialized * @parm[in] id String to parse * * @returns true if the input string has been parsed successfully; * otherwise false. */ bool ccw_parse_str(struct ccw_devid *devid, const char *id) { unsigned int cssid, ssid, devno; char d; if (strncasecmp(id, "0x", 2) == 0) return false; if (sscanf(id, "%4x %c", &devno, &d) == 1) { cssid = 0; ssid = 0; } else if (sscanf(id, "%2x.%1x.%4x %c", &cssid, &ssid, &devno, &d) != 3) { return false; } ccw_devid_init(devid, cssid, ssid, devno); return true; } s390-tools-2.38.0/libcpumf/000077500000000000000000000000001502674226300152505ustar00rootroot00000000000000s390-tools-2.38.0/libcpumf/Makefile000066400000000000000000000005331502674226300167110ustar00rootroot00000000000000include ../common.mak lib = libcpumf.a examples = libcpumf_example all: $(lib) examples: $(lib) $(examples) objects = libcpumf_pmutype.o libcpumf_cpuset.o libcpumf_support.o \ libcpumf_ctrset.o $(lib): $(objects) install: all libcpumf_example: libcpumf_example.o $(lib) $(rootdir)/libutil/libutil.a clean: rm -f *.o $(lib) $(examples) s390-tools-2.38.0/libcpumf/libcpumf_cpuset.c000066400000000000000000000031231502674226300205770ustar00rootroot00000000000000/* Copyright IBM Corp. 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include "lib/libcpumf.h" #include "lib/util_file.h" #include "lib/util_libc.h" #include "lib/util_path.h" int libcpumf_cpuset(const char *parm, cpu_set_t *mask) { char *cp, *buffer = util_strdup(parm); char *cp2 = buffer; int to, from, rc; /* Check for invalid characters, such as 11.12 instead 11-12 * but allow blanks and newline. Newline is appended * when the string is taken from sysfs files, for example * /sys/devices/system/cpu/online */ if (strspn(buffer, "0123456789-,\n ") != strlen(buffer)) { errno = EINVAL; rc = -1; goto out; } CPU_ZERO(mask); for (; (cp = strtok(buffer, ",")); buffer = NULL) { char *dash = strchr(cp, '-'); /* Range character? */ bool is_ok; if (dash) { rc = sscanf(cp, "%d-%d", &from, &to); is_ok = rc == 2; } else { rc = sscanf(cp, "%d", &to); from = to; is_ok = rc == 1; } if (!is_ok) { errno = ERANGE; rc = -1; goto out; } for (; from <= to; ++from) CPU_SET(from, mask); } rc = 0; out: free(cp2); return rc; } int libcpumf_cpuset_fn(const char *filename, cpu_set_t *mask) { char *path = util_path_sysfs(filename); char txt[PATH_MAX]; int ret = util_file_read_line(txt, sizeof(txt), "%s", path); free(path); /* Read out file, one line expected */ if (!ret) ret = libcpumf_cpuset(txt, mask); return ret; } s390-tools-2.38.0/libcpumf/libcpumf_ctrset.c000066400000000000000000000024101502674226300205760ustar00rootroot00000000000000/* Copyright IBM Corp. 2022, 2024 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include "lib/libcpumf.h" int libcpumf_ctrset(int ctr, int cfvn, int csvn) { /* Governs basic and problem state counters */ switch (cfvn) { case 1: if (ctr >= 0 && ctr < 32) return CPUMF_CTRSET_BASIC; if (ctr >= 32 && ctr < 38) return CPUMF_CTRSET_PROBLEM_STATE; break; case 3: if (ctr >= 0 && ctr < 32) return CPUMF_CTRSET_BASIC; if (ctr >= 32 && ctr < 34) return CPUMF_CTRSET_PROBLEM_STATE; break; } /* Governs crypto, extended and MT-Diagnositc counters */ switch (csvn) { case 1 ... 5: if (ctr >= 64 && ctr < 80) return CPUMF_CTRSET_CRYPTO; if ((csvn == 1 && ctr >= 128 && ctr < 160) || (csvn == 2 && ctr >= 128 && ctr < 176) || (ctr >= 128 && ctr < 256)) return CPUMF_CTRSET_EXTENDED; break; case 6 ... 8: if (ctr >= 64 && ctr < 84) return CPUMF_CTRSET_CRYPTO; if (ctr >= 128 && ctr < 288) return CPUMF_CTRSET_EXTENDED; break; } if (csvn >= 3 && ctr >= 448 && ctr < 496) return CPUMF_CTRSET_MT_DIAG; return CPUMF_CTRSET_NONE; } s390-tools-2.38.0/libcpumf/libcpumf_example.c000066400000000000000000000047031502674226300207340ustar00rootroot00000000000000/* Copyright IBM Corp. 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include "lib/libcpumf.h" int main(void) { unsigned long min, max, speed, sfb_min, sfb_max; int rc, pmu, cfvn, csvn, auth; char *pmuname; cpu_set_t set; pmu = libcpumf_pmutype(S390_CPUMF_CF); if (pmu >= 0) printf("PMU %stype %d\n", S390_CPUMF_CF, pmu); else printf("PMU %stype error %d\n", S390_CPUMF_CF, errno); pmu = libcpumf_pmutype(S390_CPUMF_SF); if (pmu >= 0) printf("PMU %stype %d\n", S390_CPUMF_SF, pmu); else printf("PMU %stype error %d\n", S390_CPUMF_SF, errno); pmu = libcpumf_pmutype(S390_CPUMF_CFDIAG); if (pmu >= 0) printf("PMU %stype %d\n", S390_CPUMF_CFDIAG, pmu); else printf("PMU %stype error %d\n", S390_CPUMF_CFDIAG, errno); rc = libcpumf_cpuset_fn(S390_CPUS_ONLINE, &set); if (rc == 0) { puts("Online CPUs:"); for (int i = 0; i < CPU_SETSIZE; ++i) if (CPU_ISSET(i, &set)) printf("%d ", i); putchar('\n'); } rc = libcpumf_cpuset("0-7,9,11-12 ,15", &set); if (rc == 0) { puts("String CPUs:"); for (int i = 0; i < CPU_SETSIZE; ++i) if (CPU_ISSET(i, &set)) printf("%d ", i); putchar('\n'); } else { printf("libcpumf_cpuset input invalid %d\n", errno); } printf("CPUMCF support %d\n", libcpumf_have_cpumcf()); rc = libcpumf_cpumcf_info(&cfvn, &csvn, &auth); printf("libcpumf_cpumcf_info %d", rc); if (rc) printf(" cfvn %d csvn %d authorization %#x", cfvn, csvn, auth); putchar('\n'); printf("CPUMSF support %d\n", libcpumf_have_cpumsf()); rc = libcpumf_cpumsf_info(&min, &max, &speed, &cfvn, &csvn); printf("libcpumf_cpumsf_info %d", rc); if (rc) printf(" min %ld max %ld speed %#lx basic %d diag %d", min, max, speed, cfvn, csvn); putchar('\n'); printf("CPUMSF have sfb %d\n", libcpumf_have_sfb()); rc = libcpumf_sfb_info(&sfb_min, &sfb_max); printf("libcpumf_sfb_info %d", rc); if (rc) printf(" sfb_min %lu sfb_max %lu", sfb_min, sfb_max); putchar('\n'); printf("PAI crypto support %d\n", libcpumf_have_pai_crypto()); rc = libcpumf_pmuname(10, &pmuname); if (rc) { printf("PMU type 10 PMU name lookup error %d\n", rc); } else { printf("PMU type 10 PMU name %s\n", pmuname); free(pmuname); } printf("PAI NNPA support %d\n", libcpumf_have_pai_nnpa()); printf("PAI EXTENSION support %d\n", libcpumf_have_pai_ext()); return EXIT_SUCCESS; } s390-tools-2.38.0/libcpumf/libcpumf_pmutype.c000066400000000000000000000027221502674226300210030ustar00rootroot00000000000000/* Copyright IBM Corp. 2022, 2025 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include "lib/util_file.h" #include "lib/util_libc.h" #include "lib/util_path.h" #include "lib/util_scandir.h" #include "lib/libcpumf.h" int libcpumf_pmutype(const char *dirname) { int ret = -1; FILE *file; char *fn; fn = util_path_sysfs("%s/type", dirname); file = fopen(fn, "r"); free(fn); if (file) { /* Read out a single number from that file */ if (fscanf(file, "%u", &ret) != 1) /* Unexpected format error, set errno */ errno = -ERANGE; fclose(file); } return ret; } int libcpumf_pmuname(unsigned int wanted_type, char **name) { struct dirent **de_vec; unsigned int type; char *dirname; char *path; int count; int rc; path = util_path_sysfs("devices"); count = util_scandir(&de_vec, alphasort, path, "(pai|cpum_).*"); free(path); *name = NULL; for (int i = 0; i < count; i++) { if (de_vec[i]->d_type == DT_DIR) { dirname = de_vec[i]->d_name; path = util_path_sysfs("devices/%s/type", dirname); rc = util_file_read_ui(&type, 10, path); if (rc) warn("Failed to open %s", path); free(path); if (!rc && type == wanted_type) { *name = util_strdup(dirname); goto out; } } } out: util_scandir_free(de_vec, count); return *name ? 0 : -1; } s390-tools-2.38.0/libcpumf/libcpumf_support.c000066400000000000000000000065461502674226300210240ustar00rootroot00000000000000/* Copyright IBM Corp. 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include "lib/libcpumf.h" #include "lib/util_path.h" #define SERVICELEVEL "/proc/service_levels" bool libcpumf_cpumcf_info(int *cfvn, int *csvn, int *auth) { char *linep = NULL; bool rc = false; size_t line_sz; ssize_t nbytes; FILE *fp; fp = fopen(SERVICELEVEL, "r"); if (!fp) err(EXIT_FAILURE, SERVICELEVEL); while ((nbytes = getline(&linep, &line_sz, fp)) != EOF) { if (!strncmp(linep, "CPU-MF: Counter facility:", 25)) { int cnt = sscanf(linep, "CPU-MF: Counter facility:" " version=%d.%d authorization=%x", cfvn, csvn, auth); if (cnt != 3) { warnx("Can not parse line %s", linep); goto out; } rc = true; break; } } out: fclose(fp); free(linep); return rc; } bool libcpumf_have_cpumcf(void) { int cfvn, csvn, auth; return libcpumf_cpumcf_info(&cfvn, &csvn, &auth); } bool libcpumf_cpumsf_info(unsigned long *min, unsigned long *max, unsigned long *speed, int *basic_sz, int *diag_sz) { char *linep = NULL; bool rc = true; size_t line_sz; ssize_t nbytes; int hit = 0; FILE *fp; fp = fopen(SERVICELEVEL, "r"); if (!fp) err(EXIT_FAILURE, SERVICELEVEL); while ((nbytes = getline(&linep, &line_sz, fp)) != EOF) { int ok; if (!strncmp(linep, "CPU-MF: Sampling facility: min", 30)) { ok = sscanf(linep, "CPU-MF: Sampling facility:" " min_rate=%ld max_rate=%ld cpu_speed=%ld", min, max, speed); if (ok != 3) { warnx("Can not parse line %s", linep); goto out; } hit += 1; } if (!strncmp(linep, "CPU-MF: Sampling facility: mode=basic", 37)) { ok = sscanf(linep, "CPU-MF: Sampling facility:" " mode=basic sample_size=%u", basic_sz); if (ok != 1) { warnx("Can not parse line %s", linep); goto out; } hit += 1; } if (!strncmp(linep, "CPU-MF: Sampling facility: mode=diag", 36)) { ok = sscanf(linep, "CPU-MF: Sampling facility:" " mode=diagnostic sample_size=%u", diag_sz); if (ok != 1) { warnx("Can not parse line %s", linep); goto out; } hit += 1; } } out: fclose(fp); free(linep); if (hit != 3) rc = false; return rc; } bool libcpumf_have_cpumsf(void) { unsigned long a, b, c; int basic_sz, diag_sz; return libcpumf_cpumsf_info(&a, &b, &c, &basic_sz, &diag_sz); } bool libcpumf_have_sfb(void) { unsigned long a, b; return libcpumf_sfb_info(&a, &b); } bool libcpumf_sfb_info(unsigned long *min, unsigned long *max) { int rc = false; char *path; FILE *fp; path = util_path_sysfs(S390_CPUMSF_BUFFERSZ); fp = fopen(path, "r"); if (!fp) err(EXIT_FAILURE, "%s", path); if (fscanf(fp, "%lu,%lu", min, max) == 2) rc = true; fclose(fp); free(path); return rc; } static bool libcpumf_have_pai_sysfs(char *p) { char *path; bool ret; path = util_path_sysfs(p); ret = util_path_exists(path); free(path); return ret; } bool libcpumf_have_pai_crypto(void) { return libcpumf_have_pai_sysfs(S390_SYSFS_PAI_CRYPTO); } bool libcpumf_have_pai_ext(void) { return libcpumf_have_pai_sysfs(S390_SYSFS_PAI_EXT); } bool libcpumf_have_pai_nnpa(void) { return libcpumf_have_pai_sysfs(S390_SYSFS_PAI_NNPA); } s390-tools-2.38.0/libdasd/000077500000000000000000000000001502674226300150515ustar00rootroot00000000000000s390-tools-2.38.0/libdasd/Makefile000066400000000000000000000002231502674226300165060ustar00rootroot00000000000000include ../common.mak lib = libdasd.a all: $(lib) objects = dasd_sys.o dasd_ioctl.o $(lib): $(objects) install: all clean: rm -f *.o $(lib) s390-tools-2.38.0/libdasd/dasd_ioctl.c000066400000000000000000000240271502674226300173270ustar00rootroot00000000000000/* * dasd_ioctl - Library for dasd-related ioctls * * DASD related helper functions for accessing device information via ioctls * * Copyright IBM Corp. 2013, 2017 * Copyright Red Hat Inc. 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include "lib/dasd_base.h" #define COULD_NOT_OPEN_WARN(device) \ warn("Could not open device '%s'", device) #define COULD_NOT_CLOSE_WARN \ warn("Unable to close device") #define RUN_IOCTL(fd, req, argp) \ do { \ int rc = ioctl(fd, req, argp); \ if (rc != 0) { \ if (rc == -1) \ rc = errno; \ if (rc != EBADF) \ dasd_close_device(fd); \ return rc; \ } \ } while (0) static int dasd_open_device(const char *device, int flags) { int fd; fd = open(device, flags); if (fd == -1) COULD_NOT_OPEN_WARN(device); return fd; } static void dasd_close_device(int fd) { if (close(fd) != 0) COULD_NOT_CLOSE_WARN; } /* * Get DASD block size information for device * * @param[in] device node device node's name * @param[out] blksize the block size * * @retval 0 in case of success * @retval errno in case of failure * */ int dasd_get_blocksize(const char *device, unsigned int *blksize) { int fd; fd = dasd_open_device(device, O_RDONLY); RUN_IOCTL(fd, BLKSSZGET, blksize); dasd_close_device(fd); return 0; } /* * Get DASD block size information (in bytes) for device * * @param[in] device node device node's name * @param[out] blksize block size in bytes * * @retval 0 in case of success * @retval errno in case of failure */ int dasd_get_blocksize_in_bytes(const char *device, unsigned long long *blksize) { int fd; fd = dasd_open_device(device, O_RDONLY); RUN_IOCTL(fd, BLKGETSIZE64, blksize); dasd_close_device(fd); return 0; } /* * Get DASD disk information for device * * @param[in] device node device node's name * @param[out] info pointer to dasd information * * @retval 0 in case of success * @retval errno in case of failure */ int dasd_get_info(const char *device, dasd_information2_t *info) { int fd; fd = dasd_open_device(device, O_RDONLY); RUN_IOCTL(fd, BIODASDINFO2, info); dasd_close_device(fd); return 0; } /* * Get DASD disk geometry information for device * * @param[in] device node device node's name * @param[out] geo pointer to device geometry information * * @retval errno in case of failure * @retval 0 in case of success */ int dasd_get_geo(const char *device, struct hd_geometry *geo) { int fd; fd = dasd_open_device(device, O_RDONLY); RUN_IOCTL(fd, HDIO_GETGEO, geo); dasd_close_device(fd); return 0; } /* * Get DASD read/write status information for device * * @param[in] device node device node's name * @param[out] ro read-only status (1 for ro, 0 for rw) * * @retval 0 in case of success * @retval errno in case of failure */ int dasd_is_ro(const char *device, bool *ro) { int fd, val; fd = dasd_open_device(device, O_RDONLY); RUN_IOCTL(fd, BLKROGET, &val); *ro = (val != 0) ? true : false; dasd_close_device(fd); return 0; } /* * Disable DASD disk. * * @param[in] device node device node's name * * @retval 0 in case of success * @retval errno in case of failure * * Note: Before BIODASDFMT can be called, a DASD has to be disabled (or rather * put into a BASIC state) via BIODASDDISABLE. However, if you disable the DASD, * close the file descriptor, and then try to reopen it, it won't work as the * device isn't fully usable anymore. For BIODASDFMT to work, the file * descriptor opened for BIODASDISABLE has to be kept open until BIODASDFMT has * finished. */ int dasd_disk_disable(const char *device, int *fd) { *fd = dasd_open_device(device, O_RDWR); RUN_IOCTL(*fd, BIODASDDISABLE, NULL); return 0; } /* * Enable DASD disk * * @param[in] device node device node's name * * @retval 0 in case of success * @retval errno in case of failure */ int dasd_disk_enable(int fd) { RUN_IOCTL(fd, BIODASDENABLE, NULL); dasd_close_device(fd); return 0; } /* * Format DASD disk * * @param[in] fd device node's file descriptor * @param[in] p format options * * @retval 0 in case of success * @retval errno in case of failure */ int dasd_format_disk(int fd, format_data_t *p) { RUN_IOCTL(fd, BIODASDFMT, p); return 0; } /* * Check DASD format * * @param[in] device node device node's name * @param[in] p format params * * @retval 0 in case of success * @retval errno in case of failure */ int dasd_check_format(const char *device, format_check_t *p) { int fd; fd = dasd_open_device(device, O_RDONLY); RUN_IOCTL(fd, BIODASDCHECKFMT, p); dasd_close_device(fd); return 0; } /* * Release Allocated Space * * @param[in] fd device node's file descriptor * @param[in] r format options * * @retval 0 in case of success * @retval errno in case of failure */ int dasd_release_space(const char *device, format_data_t *r) { int fd; fd = dasd_open_device(device, O_RDONLY); RUN_IOCTL(fd, BIODASDRAS, r); dasd_close_device(fd); return 0; } /* * Reread partition table * * @param[in] device node device node's name * @param[in] ntries maximum number of tries * * @retval 0 in case of success * @retval errno in case of failure */ int dasd_reread_partition_table(const char *device, int ntries) { int i, fd, err = 0; fd = dasd_open_device(device, O_RDONLY); /* * If the BLKRRPART ioctl fails, it is most likely due to the device * just being in use by udev. So it is worthwhile to retry the ioctl * after a second as it is likely to succeed. */ for (i = 0; i < ntries; i++) { if (ioctl(fd, BLKRRPART, NULL) != 0) { err = errno; sleep(1); } else { err = 0; break; } } dasd_close_device(fd); return err; } /* * Reserve DASD disk. * * @param[in] device node device node's name * * @retval 0 in case of success * @retval errno in case of failure * */ int dasd_disk_reserve(const char *device) { int fd; fd = dasd_open_device(device, O_RDONLY); RUN_IOCTL(fd, BIODASDRSRV, NULL); dasd_close_device(fd); return 0; } /* * Release DASD disk * * @param[in] device node device node's name * * @retval 0 in case of success * @retval errno in case of failure */ int dasd_disk_release(const char *device) { int fd; fd = dasd_open_device(device, O_RDONLY); RUN_IOCTL(fd, BIODASDRLSE, NULL); dasd_close_device(fd); return 0; } /* * Unconditionally reserve DASD disk * * An existing reserve lock is lifted (steal lock) and the device * is reserved. * * @param[in] device node device node's name * * @retval 0 in case of success * @retval errno in case of failure */ int dasd_slock(const char *device) { int fd; fd = dasd_open_device(device, O_RDONLY); RUN_IOCTL(fd, BIODASDSLCK, NULL); dasd_close_device(fd); return 0; } /* * Get the caching algorithm used for the channel programs of this device. * * @param[in] device node device node's name * @param[out] attrib_data pointer to dasd attrib data with: * 'cache' is the caching mode * 'no_cyl' the number of cylinders to be cached. * * @retval 0 in case of success * @retval errno in case of failure */ int dasd_get_cache(const char *device, attrib_data_t *attrib_data) { int fd; fd = dasd_open_device(device, O_RDONLY); RUN_IOCTL(fd, BIODASDGATTR, attrib_data); dasd_close_device(fd); return 0; } /* * Set the caching algorithm used for the channel programs of this device. * * @param[in] device node device node's name * @param[in] attrib_data pointer to dasd attrib data with: * 'cache' is the caching mode * 'no_cyl' the number of cylinders to be cached. * * @retval 0 in case of success * @retval errno in case of failure */ int dasd_set_cache(const char *device, attrib_data_t *attrib_data) { int fd; fd = dasd_open_device(device, O_RDONLY); RUN_IOCTL(fd, BIODASDSATTR, attrib_data); dasd_close_device(fd); return 0; } /* * Get reserve status of device. * * @param[in] device node device node's name * * @retval errno in case of failure * @retval 0 unreserved * @retval 1 implicit reserved * @retval 2 other reservation * @retval 3 reserved */ int dasd_query_reserve(const char *device) { struct dasd_snid_ioctl_data snid = { 0 }; int fd; fd = dasd_open_device(device, O_RDONLY); RUN_IOCTL(fd, BIODASDSNID, &snid); dasd_close_device(fd); return snid.data.path_state.reserve; } /* * Get and print the profiling info of the device. * * @param[in] device node device node's name * @param[in] dasd_profile_info pointer to dasd profile info * * @retval 0 in case of success * @retval errno in case of failure */ int dasd_profile(const char *device, dasd_profile_info_t *dasd_profile_info) { int fd; fd = dasd_open_device(device, O_RDONLY); RUN_IOCTL(fd, BIODASDPRRD, dasd_profile_info); dasd_close_device(fd); return 0; } /* * Reset the profiling counters of the device. * * @param[in] device node device node's name * * @retval 0 in case of success * @retval errno in case of failure */ int dasd_reset_profile(const char *device) { int fd; fd = dasd_open_device(device, O_RDONLY); RUN_IOCTL(fd, BIODASDPRRST, NULL); dasd_close_device(fd); return 0; } /* * Initiate the swap of a copy pairs primary,secondary relation. * The old secondary will become the new primary and vice versa. * * @param[in] device node device node's name * @param[in] copy_pair data pointer to dasd copypair data with: * 'primary' old primary, becoming secondary * 'secondary' old secondary, becoming primary. * * @retval errno in case of failure * @retval 0 in case of success * @retval 1 swap data invalid * @retval 2 no active device found * @retval 3 wrong primary specified * @retval 4 secondary device not found * @retval 5 swap already running */ int dasd_copy_swap(const char *device, struct dasd_copypair_swap_data *data) { int fd; fd = dasd_open_device(device, O_RDONLY); RUN_IOCTL(fd, BIODASDPPRCSWAP, data); dasd_close_device(fd); return 0; } s390-tools-2.38.0/libdasd/dasd_sys.c000066400000000000000000000111131502674226300170230ustar00rootroot00000000000000/* * dasd - Library for DASD related functions * * DASD related helper functions for accessing device information via sysfs * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include "lib/dasd_base.h" #include "lib/dasd_sys.h" #include "lib/util_file.h" #include "lib/util_path.h" #include "lib/util_sys.h" /** * Get raw-track access mode status * * The "devnode" parameter can be any valid relative or absolute path to * a DASD device node, for example: * * - /dev/dasda * - /dev/disk/by-path/ccw-0.0.bf20 * * @param[in] devnode Device node of interest * * @retval 1 Raw-track access mode is enabled * @retval 0 Raw-track access mode is disabled or * cannot be determined */ int dasd_sys_raw_track_access(char *devnode) { char busid[DASD_BUS_ID_SIZE]; char *path; FILE *fp; int rc; if (util_sys_get_dev_addr(devnode, busid) != 0) return 0; path = util_path_sysfs("bus/ccw/devices/%s/raw_track_access", busid); fp = fopen(path, "r"); if (!fp) { free(path); return 0; } rc = fgetc(fp) - '0'; fclose(fp); free(path); return (rc == 1) ? 1 : 0; } /** * Is volume extent space efficient a.k.a. thin-provisioned * * The "devnode" parameter can be any valid relative or absolute path to * a DASD device node, for example: * * - /dev/dasda * - /dev/disk/by-path/ccw-0.0.bf20 * * @param[in] devnode Device node of interest * * @retval 1 Volume is extent space efficient * @retval 0 Volume is not extent space efficient or * cannot be determined */ int dasd_sys_ese(char *devnode) { char busid[DASD_BUS_ID_SIZE]; char *path; FILE *fp; int rc; if (util_sys_get_dev_addr(devnode, busid) != 0) return 0; path = util_path_sysfs("bus/ccw/devices/%s/ese", busid); fp = fopen(path, "r"); if (!fp) { free(path); return 0; } rc = fgetc(fp) - '0'; fclose(fp); free(path); return (rc == 1) ? 1 : 0; } static int dasd_get_pm_from_chpid(char *busid, unsigned int chpid, int *mask) { unsigned int val; int count, i; char *path; FILE *fp; path = util_path_sysfs("bus/ccw/devices/%s/../chpids", busid); *mask = 0; fp = fopen(path, "r"); if (!fp) { free(path); return ENODEV; } for (i = 0; i < 8; i++) { count = fscanf(fp, " %x", &val); if (count != 1) { fclose(fp); return EIO; } if (val == chpid) *mask = 0x80 >> i; } fclose(fp); free(path); return 0; } /** * reset chpid * * The "devnode" parameter can be any valid relative or absolute path to * a DASD device node, for example: * * - /dev/dasda * - /dev/disk/by-path/ccw-0.0.bf20 * * @param[in] devnode Device node of interest * @param[in] chpid The chpid to reset * If NULL all chpids will be reset * * @return 0 on success, otherwise one of the following error codes: * - EINVAL No valid chpid specified. * - ENODEV Could not open device. * - ENOENT Specified chpid not found. * - EIO Other I/O error * */ int dasd_reset_chpid(char *devnode, char *chpid_char) { unsigned int chpid; char busid[DASD_BUS_ID_SIZE]; int mask, rc; char *endptr; char *path; FILE *fp; if (util_sys_get_dev_addr(devnode, busid) != 0) return ENODEV; if (!chpid_char) { path = util_path_sysfs("bus/ccw/devices/%s/path_reset", busid); fp = fopen(path, "w"); if (!fp) { free(path); return ENODEV; } fprintf(fp, "%s", "all\n"); fclose(fp); free(path); return 0; } errno = 0; chpid = strtoul(chpid_char, &endptr, 16); if (errno || (endptr && (*endptr != '\0'))) return EINVAL; rc = dasd_get_pm_from_chpid(busid, chpid, &mask); if (rc) return rc; if (!mask) return ENOENT; path = util_path_sysfs("bus/ccw/devices/%s/path_reset", busid); fp = fopen(path, "w"); if (!fp) { free(path); return ENODEV; } fprintf(fp, "%02x", mask); fclose(fp); free(path); return 0; } /** * Read amount of host with access to \p device * * The \p device can be any valid relative or absolute path to a DASD device * node, for example: * * - /dev/dasda * - /dev/disk/by-path/ccw-0.0.bf20 * * @param[in] device Device node of interest * * @retval n Number of hosts with access to \p device * @retval 0 Value could not be determined */ int dasd_get_host_access_count(char *device) { char busid[DASD_BUS_ID_SIZE]; char *path; long value; if (util_sys_get_dev_addr(device, busid) != 0) return 0; path = util_path_sysfs("bus/ccw/devices/%s/host_access_count", busid); if (util_file_read_l(&value, 10, path)) value = 0; free(path); return value; } s390-tools-2.38.0/libekmfweb/000077500000000000000000000000001502674226300155565ustar00rootroot00000000000000s390-tools-2.38.0/libekmfweb/Makefile000066400000000000000000000103461502674226300172220ustar00rootroot00000000000000include ../common.mak VERSION = 1.0 VERM = $(shell echo $(VERSION) | cut -d '.' -f 1) ifneq (${HAVE_OPENSSL},0) ifneq (${HAVE_JSONC},0) ifneq (${HAVE_LIBCURL},0) BUILD_TARGETS += libekmfweb.so.$(VERSION) INSTALL_TARGETS += install-libekmfweb.so.$(VERSION) else BUILD_TARGETS += skip-libekmfweb-curl INSTALL_TARGETS += skip-libekmfweb-curl endif else BUILD_TARGETS += skip-libekmfweb-jsonc INSTALL_TARGETS += skip-libekmfweb-jsonc endif else BUILD_TARGETS += skip-libekmfweb-openssl INSTALL_TARGETS += skip-libekmfweb-openssl endif libs = $(rootdir)/libseckey/libseckey.a TMPFILE := $(shell mktemp) detect-openssl-version.dep: echo "#include " > $(TMPFILE) echo "#include " >> $(TMPFILE) echo "#ifndef OPENSSL_VERSION_PREREQ" >> $(TMPFILE) echo " #if defined(OPENSSL_VERSION_MAJOR) && defined(OPENSSL_VERSION_MINOR)" >> $(TMPFILE) echo " #define OPENSSL_VERSION_PREREQ(maj, min) \\" >> $(TMPFILE) echo " ((OPENSSL_VERSION_MAJOR << 16) + \\" >> $(TMPFILE) echo " OPENSSL_VERSION_MINOR >= ((maj) << 16) + (min))" >> $(TMPFILE) echo " #else" >> $(TMPFILE) echo " #define OPENSSL_VERSION_PREREQ(maj, min) \\" >> $(TMPFILE) echo " (OPENSSL_VERSION_NUMBER >= (((maj) << 28) | \\" >> $(TMPFILE) echo " ((min) << 20)))" >> $(TMPFILE) echo " #endif" >> $(TMPFILE) echo "#endif" >> $(TMPFILE) echo "#if !OPENSSL_VERSION_PREREQ(1, 1)" >> $(TMPFILE) echo " #error openssl version 1.1 is required" >> $(TMPFILE) echo "#endif" >> $(TMPFILE) echo "static void __attribute__((unused)) test(void) {" >> $(TMPFILE) echo " EVP_PKEY_meth_remove(NULL);" >> $(TMPFILE) echo "}" >> $(TMPFILE) mv $(TMPFILE) $@ CURL_CONFIG ?= curl-config check-dep-libekmfweb: detect-openssl-version.dep $(call check_dep, \ "libekmfweb", \ "detect-openssl-version.dep", \ "openssl-devel version >= 1.1.1", \ "HAVE_OPENSSL=0", \ -I. `$(PKG_CONFIG) --cflags --libs libcrypto` -DOPENSSL_SUPPRESS_DEPRECATED) $(call check_dep, \ "libekmfweb", \ "json-c/json.h", \ "json-c-devel", \ "HAVE_JSONC=0") $(call check_dep, \ "libekmfweb", \ "curl/curl.h", \ "libcurl-devel", \ "HAVE_LIBCURL=0" \ `$(PKG_CONFIG) --cflags --libs libcurl`) $(CURL_CONFIG) --ssl-backends | grep OpenSSL >/dev/null 2>&1 || { echo "Error: libcurl is not built with the OpenSSL backend"; exit 1; } touch check-dep-libekmfweb skip-libekmfweb-openssl: echo " SKIP libekmfweb due to HAVE_OPENSSL=0" skip-libekmfweb-jsonc: echo " SKIP libekmfweb due to HAVE_JSONC=0" skip-libekmfweb-curl: echo " SKIP libekmfweb due to HAVE_LIBCURL=0" all: $(BUILD_TARGETS) ekmfweb.o: check-dep-libekmfweb ekmfweb.c utilities.h cca.h $(rootdir)include/ekmfweb/ekmfweb.h utilities.o: check-dep-libekmfweb utilities.c utilities.h $(rootdir)include/ekmfweb/ekmfweb.h cca.o: check-dep-libekmfweb cca.c cca.h utilities.h $(rootdir)include/ekmfweb/ekmfweb.h libekmfweb.so.$(VERSION): ALL_CFLAGS += -fPIC `$(PKG_CONFIG) --cflags json-c libcurl libcrypto libssl` libekmfweb.so.$(VERSION): LDLIBS = `$(PKG_CONFIG) --libs json-c libcurl libcrypto libssl` -ldl libekmfweb.so.$(VERSION): ALL_LDFLAGS += -shared -Wl,--version-script=libekmfweb.map \ -Wl,-z,defs,-Bsymbolic -Wl,-soname,libekmfweb.so.$(VERM) libekmfweb.so.$(VERSION): ekmfweb.o utilities.o cca.o $(libs) $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ ln -srf libekmfweb.so.$(VERSION) libekmfweb.so.$(VERM) ln -srf libekmfweb.so.$(VERSION) libekmfweb.so install-libekmfweb.so.$(VERSION): libekmfweb.so.$(VERSION) $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 -T libekmfweb.so.$(VERSION) $(DESTDIR)$(SOINSTALLDIR)/libekmfweb.so.$(VERSION) ln -srf $(DESTDIR)$(SOINSTALLDIR)/libekmfweb.so.$(VERSION) $(DESTDIR)$(SOINSTALLDIR)/libekmfweb.so.$(VERM) ln -srf $(DESTDIR)$(SOINSTALLDIR)/libekmfweb.so.$(VERSION) $(DESTDIR)$(SOINSTALLDIR)/libekmfweb.so $(INSTALL) -d -m 755 $(DESTDIR)$(USRINCLUDEDIR)/ekmfweb $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 $(rootdir)include/ekmfweb/ekmfweb.h $(DESTDIR)$(USRINCLUDEDIR)/ekmfweb install: all $(INSTALL_TARGETS) clean: rm -f *.o libekmfweb.so* check-dep-libekmfweb detect-openssl-version.dep .PHONY: all install clean skip-libekmfweb-openssl skip-libekmfweb-jsonc \ skip-libekmfweb-curl install-libekmfweb.so.$(VERSION) s390-tools-2.38.0/libekmfweb/cca.c000066400000000000000000000371161502674226300164600ustar00rootroot00000000000000/* * libekmfweb - EKMFWeb client library * * Copyright IBM Corp. 2020 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include "lib/zt_common.h" #include "libseckey/sk_utilities.h" #include #include #include #include "cca.h" #include "utilities.h" #define pr_verbose(verbose, fmt...) do { \ if (verbose) \ warnx(fmt); \ } while (0) /* Internal CCA definitions */ #define CCA_KEYWORD_SIZE 8 #define CCA_KEY_ID_SIZE 64 struct cca_ecc_pub_key_value_struct { uint8_t curve_type; uint8_t reserved; uint16_t curve_length; uint16_t public_key_len; } __packed; #define CCA_PRIME_CURVE 0x00 #define CCA_BRAINPOOL_CURVE 0x01 struct cca_token_header { uint8_t token_identifier; uint8_t token_version1; /* Used for PKA key tokens */ uint16_t token_length; uint8_t token_version2; /* Used for symmetric key tokens */ uint8_t reserved[3]; } __packed; /* Key token identifiers */ #define CCA_TOKEN_ID_NULL 0x00 #define CCA_TOKEN_ID_INTERNAL_SYMMETRIC 0x01 #define CCA_TOKEN_ID_EXTERNAL_SYMMETRIC 0x02 #define CCA_TOKEN_ID_EXTERNAL_PKA 0x1e #define CCA_TOKEN_ID_INTERNAL_PKA 0x1f /* Key token versions */ #define CCA_TOKEN_VERS1_V0 0x00 #define CCA_TOKEN_VERS2_DES_V0 0x00 #define CCA_TOKEN_VERS2_DES_V1 0x01 #define CCA_TOKEN_VERS2_AES_DATA 0x04 #define CCA_TOKEN_VERS2_AES_CIPHER 0x05 /** * Gets the CCA library function entry points from the library handle */ static int _cca_get_library_functions(const struct ekmf_cca_lib *cca_lib, struct cca_lib *cca) { if (cca_lib == NULL || cca == NULL) return -EINVAL; cca->dll_CSNDPKB = (CSNDPKB_t)dlsym(cca_lib->cca_lib, "CSNDPKB"); cca->dll_CSNBKTB2 = (CSNBKTB2_t)dlsym(cca_lib->cca_lib, "CSNBKTB2"); cca->dll_CSNDEDH = (CSNDEDH_t)dlsym(cca_lib->cca_lib, "CSNDEDH"); cca->dll_CSNDSYI2 = (CSNDSYI2_t)dlsym(cca_lib->cca_lib, "CSNDSYI2"); if (cca->dll_CSNDPKB == NULL || cca->dll_CSNBKTB2 == NULL || cca->dll_CSNDEDH == NULL || cca->dll_CSNDSYI2 == NULL) return -EIO; return 0; } /** * Import a CCA key from a JSON object representing a key as JSON Web Key (JWK, * see RFC7517). The JWK can either be an ECC public key (kty=EC), or an * symmetric key (kty=oct) containing an CCA external variable length key token * (alg=A256KW-CCA). * * @param cca_lib the CCA library structure * @param jwk the JWT JSON object containing the key to import * @param key_token a buffer to store the imported key token * @param key_token_length On entry: the size of the buffer * On return: the size of the key token * @param verbose if true, verbose messages are printed * * @returns a negative errno in case of an error, 0 if success. */ int cca_import_key_from_json_web_key(const struct ekmf_cca_lib *cca_lib, json_object *jwk, unsigned char *key_token, size_t *key_token_length, bool verbose) { long return_code, reason_code, rule_array_count, exit_data_len = 0; long key_value_struct_length, private_key_name_length = 0; struct cca_ecc_pub_key_value_struct *key_value_struct = NULL; unsigned char private_key_name[CCA_KEY_ID_SIZE] = { 0, }; unsigned char rule_array[1 * CCA_KEYWORD_SIZE] = { 0, }; const struct sk_ec_curve_info *curve_info; unsigned char *exit_data = NULL; size_t prime_len, q_len, len; unsigned char *param2 = NULL; struct cca_token_header *hdr; const char *kty, *crv, *alg; struct cca_lib cca; long token_length; unsigned char *q; long param1 = 0; int nid, rc = 0; if (cca_lib == NULL || jwk == NULL || key_token == NULL || key_token_length == NULL) return -EINVAL; rc = _cca_get_library_functions(cca_lib, &cca); if (rc != 0) { pr_verbose(verbose, "Failed to get CCA functions from library"); return rc; } memset(key_token, 0, *key_token_length); kty = json_get_string(jwk, "kty"); if (kty == NULL) { pr_verbose(verbose, "JWK does not contain field 'kty'"); rc = -EIO; goto out; } if (strcmp(kty, "EC") == 0) { crv = json_get_string(jwk, "crv"); if (crv == NULL) { pr_verbose(verbose, "JWK does not contain field 'crv'"); rc = -EIO; goto out; } nid = EC_curve_nist2nid(crv); if (nid == NID_undef) { pr_verbose(verbose, "curve '%s' not supported", crv); rc = -EIO; goto out; } curve_info = SK_UTIL_ec_get_curve_info(nid); if (curve_info == NULL) { pr_verbose(verbose, "Unsupported curve: %d", nid); rc = -EINVAL; goto out; } prime_len = curve_info->prime_len; q_len = 1 + 2 * prime_len; key_value_struct_length = sizeof(struct cca_ecc_pub_key_value_struct) + q_len; key_value_struct = (struct cca_ecc_pub_key_value_struct *) malloc(key_value_struct_length); if (key_value_struct == NULL) { pr_verbose(verbose, "malloc failed"); rc = -ENOMEM; goto out; } memset(key_value_struct, 0, sizeof(*key_value_struct)); switch (curve_info->type) { case SK_EC_TYPE_PRIME: key_value_struct->curve_type = CCA_PRIME_CURVE; break; case SK_EC_TYPE_BRAINPOOL: key_value_struct->curve_type = CCA_BRAINPOOL_CURVE; break; default: pr_verbose(verbose, "Unsupported curve: %d", nid); rc = -EINVAL; goto out; } key_value_struct->curve_length = curve_info->prime_bits; key_value_struct->public_key_len = q_len; q = ((unsigned char *)key_value_struct) + sizeof(struct cca_ecc_pub_key_value_struct); q[0] = POINT_CONVERSION_UNCOMPRESSED; len = prime_len; rc = json_object_get_base64url(jwk, "x", &q[1], &len); if (rc != 0) { pr_verbose(verbose, "Failed to get and decode x"); goto out; } if (len != prime_len) { /* RFC 7517: Must be full size of a coordinate */ pr_verbose(verbose, "x coordinate length is wrong"); rc = -EINVAL; goto out; } len = prime_len; rc = json_object_get_base64url(jwk, "y", &q[1 + prime_len], &len); if (rc != 0) { pr_verbose(verbose, "Failed to get and decode y"); goto out; } if (len != prime_len) { /* RFC 7517: Must be full size of a coordinate */ pr_verbose(verbose, "y coordinate length is wrong"); rc = -EINVAL; goto out; } rule_array_count = 1; memcpy(rule_array, "ECC-PUBL", CCA_KEYWORD_SIZE); token_length = *key_token_length; cca.dll_CSNDPKB(&return_code, &reason_code, &exit_data_len, exit_data, &rule_array_count, rule_array, &key_value_struct_length, (unsigned char *)key_value_struct, &private_key_name_length, private_key_name, ¶m1, param2, ¶m1, param2, ¶m1, param2, ¶m1, param2, ¶m1, param2, &token_length, key_token); if (return_code != 0) { pr_verbose(verbose, "CCA CSNDPKB (EC KEY TOKEN BUILD) " "failed: return_code: %ld reason_code: %ld", return_code, reason_code); return -EIO; } *key_token_length = token_length; } else if (strcmp(kty, "oct") == 0) { alg = json_get_string(jwk, "alg"); if (alg == NULL) { pr_verbose(verbose, "JWK does not contain field 'alg'"); rc = -EIO; goto out; } if (strcmp(alg, "A256KW-CCA") != 0) { pr_verbose(verbose, "JWK alg is not A256KW-CCA"); rc = -EIO; goto out; } rc = json_object_get_base64url(jwk, "k", key_token, key_token_length); if (rc != 0) { pr_verbose(verbose, "failed to get and decode k"); goto out; } /* Ensure that this is an CCA external AES CIPHER key token */ if (*key_token_length < sizeof(struct cca_token_header)) { pr_verbose(verbose, "key token is too small"); rc = -EIO; goto out; } hdr = (struct cca_token_header *)key_token; if (hdr->token_identifier != CCA_TOKEN_ID_EXTERNAL_SYMMETRIC || hdr->token_version2 != CCA_TOKEN_VERS2_AES_CIPHER || *key_token_length < hdr->token_length) { pr_verbose(verbose, "key token is not a valid CCA " "external AES CIPHER key"); rc = -EIO; goto out; } } else { pr_verbose(verbose, "Key type '%s' not supported", kty); rc = -EIO; goto out; } out: if (key_value_struct != NULL) free(key_value_struct); return rc; } /** * Drives an AES-256 key using the ED-DH key derivation method using a local ECC * private/public key pair, a foreign public ECC key, and a shared party * information data. The derived key is an internal CCA AES key token containing * the derived key in its IMPORTER key form. * * @param cca_lib the CCA library structure * @param priv_ecc_keyf_token the ECC private key token * @param priv_ecc_key_token_length the length of the ECC private key token * @param pub_ecc_key_token the ECC public key token of the other side * @param pub_ecc_key_token_length the length of the ECC public key token * @param party_info the shared data used on both sides * @param party_info_length the length of the shared data * @param kdf the key derivation function to use * @param derived_key_token a buffer to store the derived key token * @param derived_key_token_length On entry: the size of the buffer * On return: the size of the derived key token * @param verbose if true, verbose messages are printed * * @returns a negative errno in case of an error, 0 if success. */ int cca_ec_dh_derive_importer(const struct ekmf_cca_lib *cca_lib, const unsigned char *priv_ecc_key_token, size_t priv_ecc_key_token_length, const unsigned char *pub_ecc_key_token, size_t pub_ecc_key_token_length, const unsigned char *party_info, size_t party_info_length, enum cca_kdf kdf, unsigned char *derived_key_token, size_t *derived_key_token_length, bool verbose) { long return_code, reason_code, rule_array_count, exit_data_len = 0; long priv_length, pub_length, info_length, derived_length; unsigned char rule_array[3 * CCA_KEYWORD_SIZE] = { 0, }; unsigned char key_name[CCA_KEY_ID_SIZE] = { 0, }; long key_name_length = 0, key_skeleton_length; unsigned char *exit_data = NULL; unsigned char *param2 = NULL; long key_bit_length = 256; struct cca_lib cca; long param1 = 0; int rc; if (cca_lib == NULL || priv_ecc_key_token == NULL || pub_ecc_key_token == NULL || party_info == NULL || derived_key_token == NULL || derived_key_token_length == NULL) return -EINVAL; rc = _cca_get_library_functions(cca_lib, &cca); if (rc != 0) { pr_verbose(verbose, "Failed to get CCA functions from library"); return rc; } memset(derived_key_token, 0, *derived_key_token_length); rule_array_count = 3; memcpy(rule_array, "INTERNAL", CCA_KEYWORD_SIZE); memcpy(rule_array + CCA_KEYWORD_SIZE, "AES ", CCA_KEYWORD_SIZE); memcpy(rule_array + 2 * CCA_KEYWORD_SIZE, "IMPORTER", CCA_KEYWORD_SIZE); key_skeleton_length = *derived_key_token_length; cca.dll_CSNBKTB2(&return_code, &reason_code, &exit_data_len, exit_data, &rule_array_count, rule_array, ¶m1, param2, &key_name_length, key_name, ¶m1, param2, ¶m1, param2, ¶m1, param2, &key_skeleton_length, derived_key_token); if (return_code != 0) { pr_verbose(verbose, "CCA CSNBKTB2 (KEY TOKEN BUILD2) failed: " "return_code: %ld reason_code: %ld", return_code, reason_code); return -EIO; } switch (kdf) { case CCA_KDF_ANS_X9_63_CCA: rule_array_count = 1; memcpy(rule_array, "DERIV01 ", CCA_KEYWORD_SIZE); break; case CCA_KDF_ANS_X9_63_SHA224: rule_array_count = 2; memcpy(rule_array, "DERIV02 ", CCA_KEYWORD_SIZE); memcpy(rule_array + CCA_KEYWORD_SIZE, "SHA-224 ", CCA_KEYWORD_SIZE); break; case CCA_KDF_ANS_X9_63_SHA256: rule_array_count = 2; memcpy(rule_array, "DERIV02 ", CCA_KEYWORD_SIZE); memcpy(rule_array + CCA_KEYWORD_SIZE, "SHA-256 ", CCA_KEYWORD_SIZE); break; case CCA_KDF_ANS_X9_63_SHA384: rule_array_count = 2; memcpy(rule_array, "DERIV02 ", CCA_KEYWORD_SIZE); memcpy(rule_array + CCA_KEYWORD_SIZE, "SHA-384 ", CCA_KEYWORD_SIZE); break; case CCA_KDF_ANS_X9_63_SHA512: rule_array_count = 2; memcpy(rule_array, "DERIV02 ", CCA_KEYWORD_SIZE); memcpy(rule_array + CCA_KEYWORD_SIZE, "SHA-512 ", CCA_KEYWORD_SIZE); break; default: pr_verbose(verbose, "Invalid CCA KDF: %d", kdf); return -EINVAL; } priv_length = priv_ecc_key_token_length; pub_length = pub_ecc_key_token_length; derived_length = *derived_key_token_length; info_length = party_info_length; cca.dll_CSNDEDH(&return_code, &reason_code, &exit_data_len, exit_data, &rule_array_count, rule_array, &priv_length, (unsigned char *)priv_ecc_key_token, ¶m1, param2, &pub_length, (unsigned char *)pub_ecc_key_token, ¶m1, param2, &info_length, (unsigned char *)party_info, &key_bit_length, ¶m1, param2, ¶m1, param2, ¶m1, param2, ¶m1, param2, ¶m1, param2, ¶m1, param2, &derived_length, derived_key_token); if (return_code != 0) { pr_verbose(verbose, "CCA CSNDEDH (EC DIFFIE-HELLMAN) failed: " "return_code: %ld reason_code: %ld", return_code, reason_code); return -EIO; } *derived_key_token_length = derived_length; return 0; } /** * Imports an CCA external variable length AES key token using a wrapping key * in IMPORTER key form. * * @param cca_lib the CCA library structure * @param external_key_token the external key to import * @param external_key_token_length the length of the external key * @param importer_key_token the wrapping key in IMPORTER key form * @param importer_key_token_length the length of the wrapping key * @param imported_key_token a buffer to store the derived key token * @param imported_key_token_length On entry: the size of the buffer * On return: the size of the imported key token * @param verbose if true, verbose messages are printed * * @returns a negative errno in case of an error, 0 if success. */ int cca_import_external_key(const struct ekmf_cca_lib *cca_lib, const unsigned char *external_key_token, size_t external_key_token_length, const unsigned char *importer_key_token, size_t importer_key_token_length, unsigned char *imported_key_token, size_t *imported_key_token_length, bool verbose) { long return_code, reason_code, rule_array_count, exit_data_len = 0; unsigned char rule_array[2 * CCA_KEYWORD_SIZE] = { 0, }; unsigned char key_name[CCA_KEY_ID_SIZE] = { 0, }; long ext_length, importer_length, imp_length; unsigned char *exit_data = NULL; long key_name_length = 0; struct cca_lib cca; int rc; if (cca_lib == NULL || external_key_token == NULL || importer_key_token == NULL || imported_key_token == NULL || imported_key_token_length == NULL) return -EINVAL; rc = _cca_get_library_functions(cca_lib, &cca); if (rc != 0) { pr_verbose(verbose, "Failed to get CCA functions from library"); return rc; } memset(imported_key_token, 0, *imported_key_token_length); rule_array_count = 2; memcpy(rule_array, "AES ", CCA_KEYWORD_SIZE); memcpy(rule_array + CCA_KEYWORD_SIZE, "AESKW ", CCA_KEYWORD_SIZE); ext_length = external_key_token_length; importer_length = importer_key_token_length; imp_length = *imported_key_token_length; cca.dll_CSNDSYI2(&return_code, &reason_code, &exit_data_len, exit_data, &rule_array_count, rule_array, &ext_length, (unsigned char *)external_key_token, &importer_length, (unsigned char *)importer_key_token, &key_name_length, key_name, &imp_length, imported_key_token); if (return_code != 0) { pr_verbose(verbose, "CCA CSNDSYI2 (SYMM. KEY IMPORT) failed: " "return_code: %ld reason_code: %ld", return_code, reason_code); return -EIO; } *imported_key_token_length = imp_length; return 0; } s390-tools-2.38.0/libekmfweb/cca.h000066400000000000000000000116041502674226300164570ustar00rootroot00000000000000/* * libekmfweb - EKMFWeb client library * * Copyright IBM Corp. 2020 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef CCA_H #define CCA_H #include #include #include #include #include "ekmfweb/ekmfweb.h" /* CCA PKA Key Token Build function */ typedef void (*CSNDPKB_t)(long *return_code, long *reason_code, long *exit_data_length, unsigned char *exit_data, long *rule_array_count, unsigned char *rule_array, long *key_values_structure_length, unsigned char *key_values_structure, long *key_name_ln, unsigned char *key_name, long *reserved_1_length, unsigned char *reserved_1, long *reserved_2_length, unsigned char *reserved_2, long *reserved_3_length, unsigned char *reserved_3, long *reserved_4_length, unsigned char *reserved_4, long *reserved_5_length, unsigned char *reserved_5, long *token_length, unsigned char *token); /* CCA Key Token Build2 function */ typedef void (*CSNBKTB2_t)(long *return_code, long *reason_code, long *exit_data_length, unsigned char *exit_data, long *rule_array_count, unsigned char *rule_array, long *clear_key_bit_length, unsigned char *clear_key_value, long *key_name_length, unsigned char *key_name, long *user_associated_data_length, unsigned char *user_associated_data, long *token_data_length, unsigned char *token_data, long *verb_data_length, unsigned char *verb_data, long *target_key_token_length, unsigned char *target_key_token); /* CCA EC Diffie-Hellman function */ typedef void (*CSNDEDH_t)(long *return_code, long *reason_code, long *exit_data_length, unsigned char *exit_data, long *rule_array_count, unsigned char *rule_array, long *private_key_identifier_length, unsigned char *private_key_identifier, long *private_KEK_key_identifier_length, unsigned char *private_KEK_key_identifier, long *public_key_identifier_length, unsigned char *public_key_identifier, long *chaining_vector_length, unsigned char *chaining_vector, long *party_info_length, unsigned char *party_info, long *key_bit_length, long *reserved_1_length, unsigned char *reserved_1, long *reserved_2_length, unsigned char *reserved_2, long *reserved_3_length, unsigned char *reserved_3, long *reserved_4_length, unsigned char *reserved_4, long *reserved_5_length, unsigned char *reserved_5, long *output_KEK_key_identifier_length, unsigned char *output_KEK_key_identifier, long *output_key_identifier_length, unsigned char *output_key_identifier); /* CCA Symmetric Key Import2 function */ typedef void (*CSNDSYI2_t)(long *return_code, long *reason_code, long *exit_data_length, unsigned char *exit_data, long *rule_array_count, unsigned char *rule_array, long *enciphered_key_length, unsigned char *enciphered_key, long *transport_key_identifier_length, unsigned char *transport_key_identifier, long *key_name_length, unsigned char *key_name, long *target_key_identifier_length, unsigned char *target_key_identifier); struct cca_lib { CSNDPKB_t dll_CSNDPKB; CSNBKTB2_t dll_CSNBKTB2; CSNDEDH_t dll_CSNDEDH; CSNDSYI2_t dll_CSNDSYI2; }; #define CCA_MAX_PKA_KEY_TOKEN_SIZE 3500 #define CCA_MAX_SYM_KEY_TOKEN_SIZE 725 int cca_import_key_from_json_web_key(const struct ekmf_cca_lib *cca_lib, json_object *jwk, unsigned char *key_token, size_t *key_token_length, bool verbose); enum cca_kdf { CCA_KDF_ANS_X9_63_CCA = 1, /* CCA DERIVE01 method */ CCA_KDF_ANS_X9_63_SHA224 = 2, /* CCA DERIVE02 method with SHA-224 */ CCA_KDF_ANS_X9_63_SHA256 = 3, /* CCA DERIVE02 method with SHA-256 */ CCA_KDF_ANS_X9_63_SHA384 = 4, /* CCA DERIVE02 method with SHA-284 */ CCA_KDF_ANS_X9_63_SHA512 = 5, /* CCA DERIVE02 method with SHA-512 */ }; int cca_ec_dh_derive_importer(const struct ekmf_cca_lib *cca_lib, const unsigned char *priv_ecc_key_token, size_t priv_ecc_key_token_length, const unsigned char *pub_ecc_key_token, size_t pub_ecc_key_token_length, const unsigned char *party_info, size_t party_info_length, enum cca_kdf kdf, unsigned char *derived_key_token, size_t *derived_key_token_length, bool verbose); int cca_import_external_key(const struct ekmf_cca_lib *cca_lib, const unsigned char *external_key_token, size_t external_key_token_length, const unsigned char *importer_key_token, size_t importer_key_token_length, unsigned char *imported_key_token, size_t *imported_key_token_length, bool verbose); #endif s390-tools-2.38.0/libekmfweb/ekmfweb.c000066400000000000000000004734071502674226300173610ustar00rootroot00000000000000/* * libekmfweb - EKMFWeb client library * * Copyright IBM Corp. 2020 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef JSON_C_TO_STRING_NOSLASHESCAPE #define JSON_C_TO_STRING_NOSLASHESCAPE (1 << 4) #endif #include "lib/zt_common.h" #include "libseckey/sk_openssl.h" #include "ekmfweb/ekmfweb.h" #include "utilities.h" #include "cca.h" #define DEFAULT_SESSION_EC_KEY_CURVE NID_secp521r1 #define MAX_KEY_BLOB_SIZE CCA_MAX_PKA_KEY_TOKEN_SIZE #define MAX_SYM_KEY_BLOB_SIZE CCA_MAX_SYM_KEY_TOKEN_SIZE #define EKMF_URI_SYSTEM_PUBKEY "/api/v1/system/publicKey" #define EKMF_URI_SYSTEM_SETTINGS "/api/v1/system/settings" #define EKMF_URI_SYSTEM_FEATURES "/api/v1/system/features" #define EKMF_URI_SYSTEM_LOGIN "/api/v1/system/login" #define EKMF_URI_KEYS_GENERATE "/api/v1/keys" #define EKMF_URI_KEYS_EXPORT "/api/v1/keys/%s/export" #define EKMF_URI_KEYS_TAGS "/api/v1/keys/%s/tags" #define EKMF_URI_KEYS_EXPORT_CONTROL "/api/v1/keys/%s/exportControl" #define EKMF_URI_KEYS_SET_TAG "/api/v1/keys/%s/tags/%s" #define EKMF_URI_KEYS_GET "/api/v1/keys/%s" #define EKMF_URI_KEYS_SET_STATE "/api/v1/keys/%s" #define EKMF_URI_KEYS_LIST "/api/v1/keys" \ "?state=%s" \ "&orderBy=%s" \ "&namePattern=%s" \ "&tags=%s" #define EKMF_URI_KEYS_LIST_STATE "&state=" #define EKMF_URI_TEMPLATE_GET "/api/v1/templates/%s" #define EKMF_URI_TEMPLATE_LIST "/api/v1/templates" \ "?templateStates=%s" \ "&orderBy=%s" \ "&namePattern=%s" #define EKMF_URI_TEMPLATE_SEQNO "/api/v1/templates/%s/sequenceNumber" #define LIST_ELEMENTS_PER_PAGE 20 #define TEMPLATE_STATE_ACTIVE "ACTIVE" #define KEY_STATE_ACTIVE "ACTIVE" #define KEY_ALGORITHM_AES "AES" #define KEYSTORE_TYPE_PERV_ENCR "PERVASIVE_ENCRYPTION" #define ORDER_BY_NAME_ASC "name%3Aasc" #define ORDER_BY_LABEL_ASC "label%3Aasc" #define SETTING_ID_IDENTITY_TEMPLATE "ekmf.web.public.identity.template" #define SETTING_ID_XTS_KEY1_TEMPLATE "ekmf.web.public.xts.template.1" #define SETTING_ID_XTS_KEY2_TEMPLATE "ekmf.web.public.xts.template.2" #define SETTING_ID_NON_XTS_TEMPLATE "ekmf.web.public.non-xts.template" #define FEATURE_ID_PERVASIVE_ENCRYPTION \ "com.ibm.ccc.ekmf.web.features.PervasiveEncryptionFeature" void __attribute__ ((constructor)) ekmf_init(void); void __attribute__ ((destructor)) ekmf_exit(void); #define pr_verbose(verbose, fmt...) do { \ if (verbose) \ warnx(fmt); \ } while (0) #define CURL_ERROR_CHECK(rc, text, verbose, label) \ do { \ if (rc != CURLE_OK) { \ pr_verbose(verbose, "%s: %s", text, \ curl_easy_strerror(rc)); \ goto label; \ } \ } while (0) #define JSON_CHECK_OBJ(obj, type, rc_var, rc, text, verbose, label) \ do { \ if (obj == NULL || \ !json_object_is_type(obj, type)) { \ rc_var = rc; \ pr_verbose(verbose, "%s: %s", text, \ strerror(-rc_var)); \ goto label; \ } \ } while (0) #define JSON_CHECK_ERROR(cond, rc_var, rc, text, verbose, label) \ do { \ if (cond) { \ rc_var = rc; \ pr_verbose(verbose, "%s: %s", text, \ strerror(-rc_var)); \ goto label; \ } \ } while (0) struct curl_header_cb_data { struct curl_slist **headers; bool error; bool verbose; }; struct curl_write_cb_data { json_tokener *tok; json_object *obj; bool error; bool verbose; }; struct curl_sslctx_cb_data { const char *tls_server_cert; bool error; bool verbose; }; #define CURL_CERTINFO_CERT "Cert:" #define HTTP_HDR_CONTENT_TYPE "Content-Type:" static const char *accepted_content_types[] = { "application/json", "text/x-json", NULL}; struct ext_lib_info { struct sk_ext_lib ext_lib; union { struct sk_ext_cca_lib cca; /* Used if type = EXT_LIB_CCA */ struct sk_ext_ep11_lib ep11; /* Used if type = EXT_LIB_EP11 */ }; }; static void _ekmf_copy_ext_lib(const struct ekmf_ext_lib *ekmf_ext_lib, struct ext_lib_info *ext_lib_info) { switch (ekmf_ext_lib->type) { case EKMF_EXT_LIB_CCA: ext_lib_info->ext_lib.type = SK_EXT_LIB_CCA; ext_lib_info->ext_lib.cca = &ext_lib_info->cca; ext_lib_info->cca.cca_lib = ekmf_ext_lib->cca->cca_lib; break; default: ext_lib_info->ext_lib.type = 0; } } static void _ekmf_copy_key_gen_info(const struct ekmf_key_gen_info *ekmf_info, struct sk_key_gen_info *sk_info) { switch (ekmf_info->type) { case EKMF_KEY_TYPE_ECC: sk_info->type = SK_KEY_TYPE_EC; sk_info->ec.curve_nid = ekmf_info->params.ecc.curve_nid; break; case EKMF_KEY_TYPE_RSA: sk_info->type = SK_KEY_TYPE_RSA; sk_info->rsa.modulus_bits = ekmf_info->params.rsa.modulus_bits; sk_info->rsa.pub_exp = ekmf_info->params.rsa.pub_exp; sk_info->rsa.x9_31 = 0; break; default: sk_info->type = 0; break; } } static void _ekmf_copy_pss_params( const struct ekmf_rsa_pss_params *ekmf_pss_params, struct sk_rsa_pss_params *rsa_pss_params) { if (ekmf_pss_params != NULL) { rsa_pss_params->mgf_digest_nid = ekmf_pss_params->mgf_digest_nid; rsa_pss_params->salt_len = ekmf_pss_params->salt_len; } else { rsa_pss_params->mgf_digest_nid = NID_undef; rsa_pss_params->salt_len = 0; } } /** * Extract the public key from a certificate in PEM format and store it into a * PEM file */ static int _ekmf_extract_pubkey(const char *cert, const char *pub_key_pem, bool verbose) { EVP_PKEY *pkey = NULL; X509 *x509 = NULL; FILE *fp = NULL; BIO *b = NULL; int rc; b = BIO_new_mem_buf(cert, -1); if (b == NULL) { pr_verbose(verbose, "BIO_new_mem_buf failed"); return -ENOMEM; } x509 = PEM_read_bio_X509(b, NULL, NULL, NULL); if (x509 == NULL) { pr_verbose(verbose, "PEM_read_bio_X509 failed"); rc = -EIO; goto out; } pkey = X509_get0_pubkey(x509); if (pkey == NULL) { pr_verbose(verbose, "PEM_read_bio_X509 failed"); rc = -EIO; goto out; } fp = fopen(pub_key_pem, "w"); if (fp == NULL) { rc = -errno; pr_verbose(verbose, "File '%s': %s", pub_key_pem, strerror(-rc)); goto out; } if (!PEM_write_PUBKEY(fp, pkey)) { pr_verbose(verbose, "PEM_write_PUBKEY failed"); rc = -EIO; goto out; } out: if (fp != NULL) fclose(fp); if (x509 != NULL) X509_free(x509); if (b != NULL) BIO_free(b); return 0; } /** * Process the attributes of a certificate supplied by curl and writes the * PEM-format certificate attribute into the specified file pointer. */ static int _ekmf_process_certificate(FILE *fp, struct curl_slist *slist, const char *pub_key_pem, bool verbose) { char *cert; int rc; for (; slist != NULL; slist = slist->next) { pr_verbose(verbose, "%s", slist->data); if (strncmp(slist->data, CURL_CERTINFO_CERT, strlen(CURL_CERTINFO_CERT)) == 0) { cert = slist->data + strlen(CURL_CERTINFO_CERT); if (fp != NULL) { if (fwrite(cert, strlen(cert), 1, fp) != 1) { rc = -errno; pr_verbose(verbose, "fwrite failed: %s", strerror(-rc)); return rc; } } if (pub_key_pem != NULL) { rc = _ekmf_extract_pubkey(cert, pub_key_pem, verbose); if (rc != 0) return rc; } } } return 0; } /** * Callback called during curl_easy_perform() to handle received data. * Parse the (potentially partial) JSON data. */ static size_t _ekmf_dummy_write_cb(void *UNUSED(contents), size_t size, size_t nmemb, void *UNUSED(userp)) { return size * nmemb; } /** * Connects to the specified server url and obtains the servers certificate * and its chain of signing certificates and stores them in the specified * PEM files. * * @param config the configuration structure. Only the base_url must * be specified, all others are optional. * @param server_cert_pem Optional: name of a PEM file to store the servers * certificate * @param server_pubkey_pem Optional: name of a PEM file to store the servers * public key (can be used for public key pinning) * @param ca_bundle_pem Optional: name of a PEM file to store the CA * certificate chain as a bundle * @param verified On return: If the server 's certificate has been * verified using the CA specification from the config * (if ca = NULL: default system CAs, otherwise path * or file to CAs). * @param error_msg on return: If not NULL, then a textual error message * is returned in case of a failing request. The caller * must free the error string when it is not NULL. * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. */ int ekmf_get_server_cert_chain(const struct ekmf_config *config, const char *server_cert_pem, const char *server_pubkey_pem, const char *ca_bundle_pem, bool *verified, char **error_msg, bool verbose) { char error_str[CURL_ERROR_SIZE] = { 0 }; struct curl_certinfo *ci; long do_verify = 1; FILE *fp = NULL; struct stat sb; int i, rc = 0; CURL *curl; if (config == NULL) return -EINVAL; if (error_msg != NULL) *error_msg = NULL; pr_verbose(verbose, "Getting certificate chain for '%s'", config->base_url); curl = curl_easy_init(); if (curl == NULL) { pr_verbose(verbose, "curl_easy_init failed"); return CURLE_FAILED_INIT; } rc = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_str); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_ERRORBUFFER", verbose, out); rc = curl_easy_setopt(curl, CURLOPT_VERBOSE, verbose ? 1 : 0); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_VERBOSE", verbose, out); rc = curl_easy_setopt(curl, CURLOPT_URL, config->base_url); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_URL", verbose, out); if (config->tls_ca != NULL) { if (stat(config->tls_ca, &sb) != 0) { rc = -errno; pr_verbose(verbose, "stat failed on '%s': %s", config->tls_ca, strerror(-rc)); goto out; } if (S_ISDIR(sb.st_mode)) { rc = curl_easy_setopt(curl, CURLOPT_CAPATH, config->tls_ca); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_CAPATH", verbose, out); } else { rc = curl_easy_setopt(curl, CURLOPT_CAINFO, config->tls_ca); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_CAINFO", verbose, out); } } rc = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 0L); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_FOLLOWLOCATION", verbose, out); if (config->tls_client_cert != NULL) { rc = curl_easy_setopt(curl, CURLOPT_SSLCERT, config->tls_client_cert); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_SSLCERT", verbose, out); rc = curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "PEM"); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_SSLCERTTYPE", verbose, out); } if (config->tls_client_key != NULL) { rc = curl_easy_setopt(curl, CURLOPT_SSLKEY, config->tls_client_key); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_SSLKEY", verbose, out); rc = curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, "PEM"); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_SSLKEYTYPE", verbose, out); if (config->tls_client_key_passphrase != NULL) { rc = curl_easy_setopt(curl, CURLOPT_KEYPASSWD, config->tls_client_key_passphrase); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_KEYPASSWD", verbose, out); } } retry: rc = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, do_verify); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_SSL_VERIFYPEER", verbose, out); rc = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_SSL_VERIFYHOST", verbose, out); rc = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, _ekmf_dummy_write_cb); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_WRITEFUNCTION", verbose, out); rc = curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_CERTINFO", verbose, out); rc = curl_easy_perform(curl); if (rc == CURLE_SSL_CACERT && do_verify != 0) { do_verify = 0; goto retry; } CURL_ERROR_CHECK(rc, "curl_easy_perform", verbose, out); /* * If the first try worked, the server certificate could be verified * with the specified or default CA. */ if (verified != NULL) *verified = do_verify != 0; rc = curl_easy_getinfo(curl, CURLINFO_CERTINFO, &ci); CURL_ERROR_CHECK(rc, "curl_easy_getinfo CURLINFO_CERTINFO", verbose, out); if (server_cert_pem != NULL) { fp = fopen(server_cert_pem, "w"); if (fp == NULL) { rc = -errno; pr_verbose(verbose, "File '%s': %s", server_cert_pem, strerror(-rc)); goto out; } } pr_verbose(verbose, "%d certificates", ci->num_of_certs); /* * Process all certificates in the list. * First one is the server certificate, all following are * CA certificates */ for (i = 0; i < ci->num_of_certs; i++) { pr_verbose(verbose, "Certificate %d:", i); rc = _ekmf_process_certificate(fp, ci->certinfo[i], i == 0 ? server_pubkey_pem : NULL, verbose); if (rc != 0) break; if (i == 0) { if (fp != NULL) fclose(fp); fp = NULL; /* * Save CA-chain if requested, but only if the * server certificate wasn't verified by the specified * or default CA. */ if (ci->num_of_certs > 1 && ca_bundle_pem != NULL && do_verify == 0) { fp = fopen(ca_bundle_pem, "w"); if (fp == NULL) { rc = -errno; pr_verbose(verbose, "File '%s': %s", ca_bundle_pem, strerror(-rc)); break; } } } } out: if (fp != NULL) fclose(fp); if (rc > 0 && error_msg != NULL && *error_msg == NULL) { pr_verbose(verbose, "Error: %s", error_str); if (asprintf(error_msg, "CURL: %s", strlen(error_str) > 0 ? error_str : curl_easy_strerror(rc)) < 0) { pr_verbose(verbose, "asprintf failed"); rc = -ENOMEM; } } curl_easy_cleanup(curl); if (rc > 0) rc = -EIO; return rc; } /** * Callback called during curl_easy_perform() to handle received headers. * Check for the expected response content type. */ static size_t _ekmf_header_cb(void *contents, size_t size, size_t nmemb, void *userp) { struct curl_header_cb_data *cb = (struct curl_header_cb_data *)userp; size_t num = size * nmemb; size_t ofs; char *hdr = contents; char *val, *str; int i; if (num < 2) return num; if (cb->headers != NULL) { str = strndup((char *)contents, num - 2); if (str == NULL) { pr_verbose(cb->verbose, "strndup failed"); return 0; } *cb->headers = curl_slist_append(*cb->headers, str); free(str); if (*cb->headers == NULL) { pr_verbose(cb->verbose, "curl_slist_append failed"); return 0; } } if (num < strlen(HTTP_HDR_CONTENT_TYPE)) goto out; if (strncasecmp(hdr, HTTP_HDR_CONTENT_TYPE, strlen(HTTP_HDR_CONTENT_TYPE)) != 0) goto out; ofs = strlen(HTTP_HDR_CONTENT_TYPE); val = hdr + ofs; while (*val == ' ' && ofs < num) { ofs++; val++; } if (ofs >= num) goto out; for (i = 0; accepted_content_types[i] != NULL; i++) { if (num - ofs >= strlen(accepted_content_types[i]) && strncasecmp(val, accepted_content_types[i], strlen(accepted_content_types[i])) == 0) goto out; } cb->error = true; pr_verbose(cb->verbose, "Unexpected response Content-Type: %.*s", (int)(num - ofs), val); return 0; out: return num; } /** * Callback called during curl_easy_perform() to handle received data. * Parse the (potentially partial) JSON data. */ static size_t _ekmf_write_cb(void *contents, size_t size, size_t nmemb, void *userp) { struct curl_write_cb_data *cb = (struct curl_write_cb_data *)userp; enum json_tokener_error jerr; size_t num = size * nmemb; pr_verbose(cb->verbose, "Response Data: ->%.*s<-", (int)num, (char *)contents); if (cb->obj != NULL) { pr_verbose(cb->verbose, "JSON data already complete, but " "additional data received"); cb->error = true; return 0; } cb->obj = json_tokener_parse_ex(cb->tok, (const char *)contents, num); if (cb->obj == NULL) { jerr = json_tokener_get_error(cb->tok); if (jerr == json_tokener_continue) goto out; cb->error = true; pr_verbose(cb->verbose, "json_tokener_parse_ex failed: %s", json_tokener_error_desc(jerr)); return 0; } out: return num; } /** * Extracts the EKMFWeb API error information form the response object. */ static int _ekmf_get_api_error(json_object *response_obj, char **error_msg) { json_object *field; int code; if (response_obj == NULL || error_msg == NULL) return -EINVAL; if (!json_object_is_type(response_obj, json_type_object)) return -EBADMSG; if (!json_object_object_get_ex(response_obj, "code", &field)) return -EBADMSG; if (!json_object_is_type(field, json_type_int)) return -EBADMSG; code = json_object_get_int(field); if (!json_object_object_get_ex(response_obj, "message", &field)) return -EBADMSG; if (!json_object_is_type(field, json_type_string)) return -EBADMSG; if (asprintf(error_msg, "EKMFWeb: %d: %s", code, json_object_get_string(field)) < 0) return -ENOMEM; return 0; } /** * This callback called before the SSL handshake is performed. * It adds a pinned server certificate to the SSL certificate store, so * that it is treated as trusted, although it might be self-signed. */ static CURLcode _ekmf_sslctx_cb(CURL *UNUSED(curl), void *sslctx, void *parm) { struct curl_sslctx_cb_data *sslctx_cb = parm; SSL_CTX *ssl_ctx = (SSL_CTX *)sslctx; X509_STORE *store; X509 *cert = NULL; int rc; if (ssl_ctx == NULL || sslctx_cb == NULL) return CURLE_ABORTED_BY_CALLBACK; if (sslctx_cb->tls_server_cert == NULL) return CURLE_OK; store = SSL_CTX_get_cert_store(ssl_ctx); if (store == NULL) { pr_verbose(sslctx_cb->verbose, "Failed to get SSL Store"); return CURLE_ABORTED_BY_CALLBACK; } rc = read_x509_certificate(sslctx_cb->tls_server_cert, &cert); if (rc != 0) { pr_verbose(sslctx_cb->verbose, "Failed to read the server " "certificate from file '%s'", sslctx_cb->tls_server_cert); return CURLE_ABORTED_BY_CALLBACK; } rc = X509_STORE_add_cert(store, cert); if (rc != 1) { pr_verbose(sslctx_cb->verbose, "Failed to add server " "certificate to SSL Store"); X509_free(cert); return CURLE_ABORTED_BY_CALLBACK; } X509_free(cert); return CURLE_OK; } /** * Perform an HTTP request to the url constructed from the base_url in config * and th uri specified using the specified HTTP request. * The config structure contains information about TLS certificates. * If specified, it serializes the request data (JSON) and sends it to the * server. The response data (JSON) is parsed and returned in the response data. * If the response content type is not JSON, then an error is returned. * The HTTP status code is returned in status_code. * * @param config the configuration structure * @param uri the uri (and query parameters) to concatenate to the * base_url from the config structure. * @param request the HTTP request to perform (e.g. GET, PUT, POST) * @param request_data the JSON data to be sent with the request. * @param request_headers a NULL terminated list of pointers to HTTP headers * to send along with the request. Can be NULL. * @param login_token if not NULL, a Bearer token to authorize with * @param response_data on return the JSON response data is returned. When * no longer needed, it must be released using * json_object_put() * @param response headers address of a curl_slist to add response headers to, * or NULL to not return any headers. * @param status_code on return the HTTP status code is returned * @param error_msg on return: If not NULL, then a textual error message * is returned in case of a failing request. The caller * must free the error string when it is not NULL. * @param curl a CURL handle to perform the request with. * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno or a positive CURL error code in * case of an error */ static int _ekmf_perform_request(const struct ekmf_config *config, const char *uri, const char *request, json_object *request_data, char **request_headers, const char *login_token, json_object **response_data, struct curl_slist **response_headers, long *status_code, char **error_msg, CURL *curl, bool verbose) { const struct curl_tlssessioninfo *info = NULL; struct curl_header_cb_data header_cb = { 0 }; struct curl_sslctx_cb_data sslctx_cb = { 0 }; struct curl_write_cb_data write_cb = { 0 }; char error_str[CURL_ERROR_SIZE] = { 0 }; struct curl_slist *list = NULL; char *url = NULL; const char *str; struct stat sb; char *auth_hdr; int i, rc; if (config == NULL || uri == NULL || request == NULL || status_code == NULL || curl == NULL) return -EINVAL; if (error_msg != NULL) *error_msg = NULL; if (asprintf(&url, "%s%s", config->base_url, uri) < 0) { pr_verbose(verbose, "asprintf failed"); return -ENOMEM; } pr_verbose(verbose, "Performing request for '%s'", url); curl_easy_reset(curl); /* * The CURLOPT_SSL_CTX_FUNCTION callback only works with the OpenSSL * curl backend. Check that OpenSSL is the current curl backend. */ rc = curl_easy_getinfo(curl, CURLINFO_TLS_SSL_PTR, &info); CURL_ERROR_CHECK(rc, "curl_easy_getinfo CURLINFO_TLS_SSL_PTR", verbose, out); if (info->backend != CURLSSLBACKEND_OPENSSL) { pr_verbose(verbose, "libcurl is not using the OpenSSL backend"); rc = -EIO; goto out; } rc = curl_easy_setopt(curl, CURLOPT_VERBOSE, verbose ? 1 : 0); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_VERBOSE", verbose, out); rc = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_str); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_ERRORBUFFER", verbose, out); rc = curl_easy_setopt(curl, CURLOPT_URL, url); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_URL", verbose, out); rc = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, config->tls_verify_peer ? 1L : 0L); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_SSL_VERIFYPEER", verbose, out); rc = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, config->tls_verify_host ? 2L : 0L); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_SSL_VERIFYHOST", verbose, out); if (config->tls_ca != NULL) { if (stat(config->tls_ca, &sb) != 0) { rc = -errno; pr_verbose(verbose, "stat failed on '%s': %s", config->tls_ca, strerror(-rc)); goto out; } if (S_ISDIR(sb.st_mode)) { rc = curl_easy_setopt(curl, CURLOPT_CAPATH, config->tls_ca); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_CAPATH", verbose, out); } else { rc = curl_easy_setopt(curl, CURLOPT_CAINFO, config->tls_ca); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_CAINFO", verbose, out); } } if (config->tls_client_cert != NULL) { rc = curl_easy_setopt(curl, CURLOPT_SSLCERT, config->tls_client_cert); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_SSLCERT", verbose, out); rc = curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "PEM"); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_SSLCERTTYPE", verbose, out); } if (config->tls_client_key != NULL) { rc = curl_easy_setopt(curl, CURLOPT_SSLKEY, config->tls_client_key); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_SSLKEY", verbose, out); rc = curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, "PEM"); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_SSLKEYTYPE", verbose, out); if (config->tls_client_key_passphrase != NULL) { rc = curl_easy_setopt(curl, CURLOPT_KEYPASSWD, config->tls_client_key_passphrase); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_KEYPASSWD", verbose, out); } } if (config->tls_issuer_cert != NULL) { rc = curl_easy_setopt(curl, CURLOPT_ISSUERCERT, config->tls_issuer_cert); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_ISSUERCERT", verbose, out); } if (config->tls_pinned_pubkey != NULL) { rc = curl_easy_setopt(curl, CURLOPT_PINNEDPUBLICKEY, config->tls_pinned_pubkey); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_PINNEDPUBLICKEY", verbose, out); } if (config->max_redirs > 0) { rc = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_FOLLOWLOCATION", verbose, out); rc = curl_easy_setopt(curl, CURLOPT_MAXREDIRS, config->max_redirs); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_MAXREDIRS", verbose, out); rc = curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_POSTREDIR", verbose, out); } else { rc = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 0L); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_FOLLOWLOCATION", verbose, out); } if (strcmp(request, "GET") == 0) { rc = curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, NULL); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_CUSTOMREQUEST", verbose, out); rc = curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_HTTPGET", verbose, out); } else { rc = curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, request); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_CUSTOMREQUEST", verbose, out); if (request_data != NULL) { rc = curl_easy_setopt(curl, CURLOPT_POST, 1L); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_POST", verbose, out); list = curl_slist_append(list, "Content-Type: application/json;charset=UTF-8"); if (list == NULL) { pr_verbose(verbose, "curl_slist_append failed"); rc = -ENOMEM; goto out; } /* * The memory returned by json_object_to_json_string_ext * is freed when the JSON object is freed. */ str = json_object_to_json_string_ext(request_data, JSON_C_TO_STRING_PLAIN | JSON_C_TO_STRING_NOSLASHESCAPE); pr_verbose(verbose, "Request Data: ->%s<-", str); rc = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, str); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_POSTFIELDS", verbose, out); } } list = curl_slist_append(list, "Accept: application/json"); if (list == NULL) { pr_verbose(verbose, "curl_slist_append failed"); rc = -ENOMEM; goto out; } list = curl_slist_append(list, "Accept-Charset: UTF-8"); if (list == NULL) { pr_verbose(verbose, "curl_slist_append failed"); rc = -ENOMEM; goto out; } /* Disable "Expect: 100-continue" */ list = curl_slist_append(list, "Expect:"); if (list == NULL) { pr_verbose(verbose, "curl_slist_append failed"); rc = -ENOMEM; goto out; } if (login_token != NULL) { /* * Note: We could alternatively use CURLOPT_XOAUTH2_BEARER, with * CURLOPT_HTTPAUTH using CURLAUTH_BEARER, but this seems to * cause a memory leak in some curl versions. */ if (asprintf(&auth_hdr, "Authorization: Bearer %s", login_token) < 0) { pr_verbose(verbose, "asprintf failed"); rc = -ENOMEM; goto out; } list = curl_slist_append(list, auth_hdr); free(auth_hdr); if (list == NULL) { pr_verbose(verbose, "curl_slist_append failed"); rc = -ENOMEM; goto out; } } for (i = 0; request_headers != NULL && request_headers[i] != NULL; i++) { list = curl_slist_append(list, request_headers[i]); if (list == NULL) { pr_verbose(verbose, "curl_slist_append failed"); rc = -ENOMEM; goto out; } } rc = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_HTTPHEADER", verbose, out); header_cb.headers = response_headers; header_cb.verbose = verbose; write_cb.verbose = verbose; write_cb.tok = json_tokener_new(); if (write_cb.tok == NULL) { pr_verbose(verbose, "json_tokener_new failed"); rc = -ENOMEM; goto out; } sslctx_cb.tls_server_cert = config->tls_server_cert; sslctx_cb.verbose = verbose; rc = curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, _ekmf_header_cb); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_HEADERFUNCTION", verbose, out); rc = curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void *)&header_cb); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_HEADERDATA", verbose, out); rc = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, _ekmf_write_cb); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_WRITEFUNCTION", verbose, out); rc = curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&write_cb); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_WRITEDATA", verbose, out); if (config->tls_server_cert != NULL) { rc = curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, _ekmf_sslctx_cb); CURL_ERROR_CHECK(rc, "curl_easy_setopt " "CURLOPT_SSL_CTX_FUNCTION", verbose, out); rc = curl_easy_setopt(curl, CURLOPT_SSL_CTX_DATA, &sslctx_cb); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_SSL_CTX_DATA", verbose, out); } rc = curl_easy_perform(curl); if (rc != CURLE_OK) { pr_verbose(verbose, "curl_easy_perform for '%s' failed: %s", url, curl_easy_strerror(rc)); pr_verbose(verbose, "Error: %s", error_str); if (header_cb.error) { pr_verbose(verbose, "Unexpected Content-Type"); rc = -EBADMSG; if (error_msg != NULL && *error_msg == NULL) { if (asprintf(error_msg, "Unexpected response " "Content-Type") < 0) error_msg = NULL; } } if (write_cb.error) { pr_verbose(verbose, "JSON parsing failed"); rc = -EBADMSG; if (error_msg != NULL && *error_msg == NULL) { if (asprintf(error_msg, "Failed to JSON parse " "the response content") < 0) error_msg = NULL; } } goto out; } rc = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, status_code); CURL_ERROR_CHECK(rc, "curl_easy_getinfo CURLINFO_RESPONSE_CODE", verbose, out); if (*status_code >= 400 && write_cb.obj != NULL && error_msg != NULL && *error_msg == NULL) { rc = _ekmf_get_api_error(write_cb.obj, error_msg); json_object_put(write_cb.obj); write_cb.obj = NULL; if (rc != 0) goto out; } if (response_data != NULL) { *response_data = write_cb.obj; } else { if (write_cb.obj != NULL) json_object_put(write_cb.obj); } out: if (write_cb.tok != NULL) json_tokener_free(write_cb.tok); if (url != NULL) free(url); if (list != NULL) curl_slist_free_all(list); if (rc > 0 && error_msg != NULL && *error_msg == NULL) { if (asprintf(error_msg, "CURL: %s", strlen(error_str) > 0 ? error_str : curl_easy_strerror(rc)) < 0) { pr_verbose(verbose, "asprintf failed"); rc = -ENOMEM; } } curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, NULL); return rc; } /** * Allocates or reuses a CURL handle. If curl_handle is not NULL, and * points to a non-NULL CURL handle, it is used, otherwise a new CURL handle * is allocated. */ static int _ekmf_get_curl_handle(CURL **curl_handle, CURL **curl) { if (curl == NULL) return -EINVAL; if (curl_handle != NULL) *curl = *curl_handle; if (*curl == NULL) *curl = curl_easy_init(); if (*curl == NULL) return -EIO; return 0; } /** * Releases a CURL handle. If curl_handle is not NULL, then the used CURL * handle is passed back via *curl_handle. If curl_handle is NULL, then the * used CURL handle is destroyed. */ static void _ekmf_release_curl_handle(CURL **curl_handle, CURL *curl) { if (curl == NULL) return; if (curl_handle != NULL) *curl_handle = curl; else curl_easy_cleanup(curl); } /** * Print the certificate(s) contained in the specified PEM file. * * @param cert_pem the file name of the PEM file to print * @param verbose if true, verbose messages are printed * * @returns -EIO if the file could not be opened. -ENOENT if the PEM file * does not contain any certificates. 0 if success. */ int ekmf_print_certificates(const char *cert_pem, bool verbose) { int rc = -ENOENT; X509 *cert; FILE *fp; if (cert_pem == NULL) return -EINVAL; fp = fopen(cert_pem, "r"); if (fp == NULL) { pr_verbose(verbose, "File '%s': %s", cert_pem, strerror(errno)); return -EIO; } while (1) { cert = PEM_read_X509(fp, NULL, NULL, NULL); if (cert == NULL) break; X509_print_ex_fp(stdout, cert, 0, X509_FLAG_NO_EXTENSIONS); X509_free(cert); rc = 0; } fclose(fp); return rc; } /** * Checks if the login token stored in the file denoted by field login_token * of the config structure is valid or not. The file (if existent) contains a * JSON Web Token (JWT, see RFC7519). It is valid if the current date and time * is before its expiration time ("exp" claim), and after or equal its * not-before time ("nbf" claim). * Note: The signature (if any) of the JWT is not checked, nor any other JWT * fields. * * @param config the configuration structure * @param valid On return: true if the token is valid, false if not * @param login_token On return: If not NULL: the login token, if the * token is still valid. The returned string must * be freed by the caller when no longer needed. * @param verbose if true, verbose messages are printed * * @returns a negative errno in case of an error, 0 if success. */ int ekmf_check_login_token(const struct ekmf_config *config, bool *valid, char **login_token, bool verbose) { json_object *jwt_payload = NULL; json_object *exp_claim = NULL; json_object *nbf_claim = NULL; char *token = NULL; size_t count, size; int64_t exp, nbf; FILE *fp = NULL; struct stat sb; int rc = 0; time_t now; if (config == NULL || valid == NULL) return -EINVAL; if (config->login_token == NULL) { *valid = false; return 0; } if (login_token != NULL) *login_token = NULL; pr_verbose(verbose, "Reading login token from file : '%s'", config->login_token); if (stat(config->login_token, &sb)) { rc = -errno; pr_verbose(verbose, "stat on file %s failed: '%s'", config->login_token, strerror(-rc)); return rc; } size = sb.st_size; if (size == 0) { pr_verbose(verbose, "File %s is empty", config->login_token); rc = -EIO; goto out; } token = (char *)malloc(size + 1); if (token == NULL) { pr_verbose(verbose, "Failed to allocate a buffer"); return -ENOMEM; } fp = fopen(config->login_token, "r"); if (fp == NULL) { rc = -errno; pr_verbose(verbose, "Failed to open file %s: '%s'", config->login_token, strerror(-rc)); goto out; } count = fread(token, 1, size, fp); if (count != size) { pr_verbose(verbose, "Failed to read the token"); rc = -EIO; goto out; } token[size] = '\0'; if (token[size - 1] == '\n') token[size - 1] = '\0'; fclose(fp); fp = NULL; time(&now); *valid = true; rc = parse_json_web_token(token, NULL, &jwt_payload, NULL, NULL); if (rc != 0) { pr_verbose(verbose, "parse_json_web_token failed"); goto out; } if (json_object_object_get_ex(jwt_payload, "exp", &exp_claim) && json_object_is_type(exp_claim, json_type_int)) { exp = json_object_get_int64(exp_claim); if (exp == 0) { pr_verbose(verbose, "failed to get value from exp claim"); rc = -EIO; goto out; } if (now > exp) { pr_verbose(verbose, "JWT is expired"); *valid = false; } } if (json_object_object_get_ex(jwt_payload, "nbf", &nbf_claim) && json_object_is_type(nbf_claim, json_type_int)) { nbf = json_object_get_int64(nbf_claim); if (nbf == 0) { pr_verbose(verbose, "failed to get value from nbf claim"); rc = -EIO; goto out; } if (now <= nbf) { pr_verbose(verbose, "JWT is not yet valid"); *valid = false; } } if (login_token != NULL && *valid) { *login_token = token; token = NULL; } out: if (jwt_payload != NULL) json_object_put(jwt_payload); if (token != NULL) free(token); if (fp != NULL) fclose(fp); return rc; } /** * Performs a login of the specified user with a passcode. On success the * returned login token is stored in the file denoted by field login_token * of the config structure, so that it can be used by subsequent requests. * * To perform a single request, set curl_handle to NULL. This will cause the * function to initialize a new CURL handle, use it, and destroy it. * If you plan to perform multiple requests to the same host, supply the address * of a CURL pointer that is initially NULL. This function will then initialize * a new CURL handle on the first call. On subsequent calls, pass in the address * of the same CURL pointer so that the CURL handle is reused. After the last * request, the CURL handle must be destroyed by calling ekmf_curl_destroy). * * @param config the configuration structure * @param curl_handle address of a CURL handle used for reusing the same * CURL handle with multiple requests. * @param user_id the user-ID to log-in. * @param passcode the passcode to log-in the user. * @param error_msg on return: If not NULL, then a textual error message * is returned in case of a failing request. The caller * must free the error string when it is not NULL. * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. * -EACCES is returned, if the passcode is no longer valid. */ int ekmf_login(const struct ekmf_config *config, CURL **curl_handle, const char *user_id, const char *passcode, char **error_msg, bool verbose) { json_object *response_obj = NULL; json_object *request_obj = NULL; const char *login_token, *tok; CURL *curl = NULL; long status_code; FILE *fp = NULL; size_t count; int rc; if (config == NULL || user_id == NULL || passcode == NULL) return -EINVAL; rc = _ekmf_get_curl_handle(curl_handle, &curl); if (rc != 0) { pr_verbose(verbose, "Failed to get CURL handle"); rc = -EIO; goto out; } request_obj = json_object_new_object(); JSON_CHECK_ERROR(request_obj == NULL, rc, -ENOMEM, "Failed to generate JSON object", verbose, out); rc = json_object_object_add_ex(request_obj, "userId", json_object_new_string(user_id), 0); JSON_CHECK_ERROR(rc != 0, rc, -EIO, "Failed to add data to JSON object", verbose, out); rc = json_object_object_add_ex(request_obj, "passcode", json_object_new_string(passcode), 0); JSON_CHECK_ERROR(rc != 0, rc, -EIO, "Failed to add data to JSON object", verbose, out); rc = _ekmf_perform_request(config, EKMF_URI_SYSTEM_LOGIN, "POST", request_obj, NULL, NULL, &response_obj, NULL, &status_code, error_msg, curl, verbose); if (rc != 0) { pr_verbose(verbose, "Failed perform the REST call"); if (rc > 0) rc = -EIO; goto out; } switch (status_code) { case 200: break; case 410: pr_verbose(verbose, "The passcode is no longer valid"); rc = -EACCES; goto out; default: pr_verbose(verbose, "REST Call failed with HTTP status code: " "%ld", status_code); rc = -EIO; goto out; } JSON_CHECK_OBJ(response_obj, json_type_object, rc, -EIO, "No or invalid response content", verbose, out); login_token = json_get_string(response_obj, "authorizationToken"); JSON_CHECK_ERROR(login_token == NULL, rc, -EBADMSG, "Invalid response content", verbose, out); if (strncmp(login_token, "Bearer ", 7) != 0) { rc = -EBADMSG; pr_verbose(verbose, "Received token is not a Bearer token"); goto out; } tok = &login_token[7]; while (*tok == ' ') tok++; fp = fopen(config->login_token, "w"); if (fp == NULL) { rc = -errno; pr_verbose(verbose, "Failed to open file %s: '%s'", config->login_token, strerror(-rc)); goto out; } count = fwrite(tok, 1, strlen(tok), fp); if (count != strlen(tok)) { pr_verbose(verbose, "Failed to write the token"); rc = -EIO; goto out; } pr_verbose(verbose, "Login token successfully updated in file '%s'", config->login_token); out: _ekmf_release_curl_handle(curl_handle, curl); if (request_obj != NULL) json_object_put(request_obj); if (response_obj != NULL) json_object_put(response_obj); if (fp != NULL) fclose(fp); return rc; } /** * Request the EKMFWeb server's public signing key and store it into PEM file * specified in field server_pubkey of the config structure. * * To perform a single request, set curl_handle to NULL. This will cause the * function to initialize a new CURL handle, use it, and destroy it. * If you plan to perform multiple requests to the same host, supply the address * of a CURL pointer that is initially NULL. This function will then initialize * a new CURL handle on the first call. On subsequent calls, pass in the address * of the same CURL pointer so that the CURL handle is reused. After the last * request, the CURL handle must be destroyed by calling ekmf_curl_destroy). * * @param config the configuration structure * @param curl_handle address of a CURL handle used for reusing the same * CURL handle with multiple requests. * @param error_msg on return: If not NULL, then a textual error message * is returned in case of a failing request. The caller * must free the error string when it is not NULL. * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. * -EACCES is returned, if no or no valid login token is available. */ int ekmf_get_public_key(const struct ekmf_config *config, CURL **curl_handle, char **error_msg, bool verbose) { json_object *response_obj = NULL; char *login_token = NULL; bool token_valid = false; EVP_PKEY *pkey = NULL; CURL *curl = NULL; long status_code; int rc; if (config == NULL) return -EINVAL; rc = SK_OPENSSL_init(verbose); if (rc != 0) { pr_verbose(verbose, "Failed to initialize secure key support: " "%s", strerror(-rc)); return rc; } rc = ekmf_check_login_token(config, &token_valid, &login_token, verbose); if (rc != 0 || !token_valid) { pr_verbose(verbose, "No valid login token available"); rc = -EACCES; goto out; } rc = _ekmf_get_curl_handle(curl_handle, &curl); if (rc != 0) { pr_verbose(verbose, "Failed to get CURL handle"); rc = -EIO; goto out; } rc = _ekmf_perform_request(config, EKMF_URI_SYSTEM_PUBKEY, "GET", NULL, NULL, login_token, &response_obj, NULL, &status_code, error_msg, curl, verbose); if (rc != 0) { pr_verbose(verbose, "Failed perform the REST call"); if (rc > 0) rc = -EIO; goto out; } switch (status_code) { case 200: break; case 401: pr_verbose(verbose, "Not authorized"); rc = -EACCES; goto out; default: pr_verbose(verbose, "REST Call failed with HTTP status code: " "%ld", status_code); rc = -EIO; goto out; } JSON_CHECK_OBJ(response_obj, json_type_object, rc, -EIO, "No or invalid response content", verbose, out); rc = json_web_key_as_pkey(response_obj, EVP_PKEY_RSA, &pkey); if (rc != 0) { pr_verbose(verbose, "Failed convert the JWK to PKEY"); goto out; } rc = write_public_key(config->ekmf_server_pubkey, pkey); if (rc != 0) { pr_verbose(verbose, "Failed to write public key '%s': %s", config->ekmf_server_pubkey, strerror(-rc)); goto out; } pr_verbose(verbose, "EKMFWeb public key written to file '%s'", config->ekmf_server_pubkey); out: _ekmf_release_curl_handle(curl_handle, curl); if (response_obj != NULL) json_object_put(response_obj); if (login_token != NULL) free(login_token); if (pkey != NULL) EVP_PKEY_free(pkey); SK_OPENSSL_term(); return rc; } /** * Finds the setting with the specified ID in the settings array, and returns * its value if found, or NULL otherwise. The returned string is allocated * and must be freed by the caller when no lnger used. * * @param settings_array the JSON array containing the settings * @param config the setting ID to get * @param verbose if true, verbose messages are printed * * @returns an allocated string conmtaining the setting value, or NULL */ static char *_ekmf_find_setting(json_object *settings_array, const char *setting_id, bool verbose) { const char *id, *value; json_object *obj; int rc = 0, i; JSON_CHECK_OBJ(settings_array, json_type_array, rc, -EIO, "No settings array", verbose, out); for (i = 0; i < (int)json_object_array_length(settings_array); i++) { obj = json_object_array_get_idx(settings_array, i); JSON_CHECK_OBJ(obj, json_type_object, rc, -EIO, "No settings object", verbose, out); id = json_get_string(obj, "id"); JSON_CHECK_ERROR(id == NULL, rc, -EIO, "No id field in settings object", verbose, out); if (strcmp(id, setting_id) != 0) continue; value = json_get_string(obj, "value"); JSON_CHECK_ERROR(value == NULL, rc, -EIO, "No value field in settings object", verbose, out); return strdup(value); } pr_verbose(verbose, "Setting '%s' not found", setting_id); out: return NULL; } /** * Retrieves settings from the EKMFWeb server, such as the template names for * generating keys in EKMFWeb. * * To perform a single request, set curl_handle to NULL. This will cause the * function to initialize a new CURL handle, use it, and destroy it. * If you plan to perform multiple requests to the same host, supply the address * of a CURL pointer that is initially NULL. This function will then initialize * a new CURL handle on the first call. On subsequent calls, pass in the address * of the same CURL pointer so that the CURL handle is reused. After the last * request, the CURL handle must be destroyed by calling ekmf_curl_destroy). * * @param config the configuration structure * @param curl_handle address of a CURL handle used for reusing the same * CURL handle with multiple requests. * @param identity_template on return: If not NULL, the name of the template * used to generate identity keys with. The caller * must free the error string when it is not NULL. * @param xts_key1_template on return: If not NULL, the name of the template * used to generate the first XTS key with. The caller * must free the error string when it is not NULL. * @param xts_key1_template on return: If not NULL, the name of the template * used to generate the second XTS key with. The caller * must free the error string when it is not NULL. * @param xts_key1_template on return: If not NULL, the name of the template * used to generate a non-XTS key with. The caller * must free the error string when it is not NULL. * @param error_msg on return: If not NULL, then a textual error message * is returned in case of a failing request. The caller * must free the error string when it is not NULL. * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. * -EACCES is returned, if no or no valid login token is available. */ int ekmf_get_settings(const struct ekmf_config *config, CURL **curl_handle, char **identity_template, char **xts_key1_template, char **xts_key2_template, char **non_xts_template, char **error_msg, bool verbose) { json_object *response_obj = NULL; char *login_token = NULL; bool token_valid = false; CURL *curl = NULL; long status_code; int rc; if (config == NULL) return -EINVAL; if (identity_template == NULL && xts_key1_template == NULL && xts_key2_template == NULL && non_xts_template == NULL) return 0; rc = ekmf_check_login_token(config, &token_valid, &login_token, verbose); if (rc != 0 || !token_valid) { pr_verbose(verbose, "No valid login token available"); rc = -EACCES; goto out; } rc = _ekmf_get_curl_handle(curl_handle, &curl); if (rc != 0) { pr_verbose(verbose, "Failed to get CURL handle"); rc = -EIO; goto out; } rc = _ekmf_perform_request(config, EKMF_URI_SYSTEM_SETTINGS, "GET", NULL, NULL, login_token, &response_obj, NULL, &status_code, error_msg, curl, verbose); if (rc != 0) { pr_verbose(verbose, "Failed perform the REST call"); if (rc > 0) rc = -EIO; goto out; } switch (status_code) { case 200: break; case 401: pr_verbose(verbose, "Not authorized"); rc = -EACCES; goto out; default: pr_verbose(verbose, "REST Call failed with HTTP status code: " "%ld", status_code); rc = -EIO; goto out; } JSON_CHECK_OBJ(response_obj, json_type_array, rc, -EIO, "No or invalid response content", verbose, out); if (identity_template != NULL) { *identity_template = _ekmf_find_setting(response_obj, SETTING_ID_IDENTITY_TEMPLATE, verbose); if (*identity_template == NULL) { if (error_msg != NULL) { if (asprintf(error_msg, "The EKMF Web setting " "'Identity Key Template Name' " "must be configured") < 0) error_msg = NULL; } rc = -EINVAL; goto out; } } if (xts_key1_template != NULL) { *xts_key1_template = _ekmf_find_setting(response_obj, SETTING_ID_XTS_KEY1_TEMPLATE, verbose); if (*xts_key1_template == NULL) { if (error_msg != NULL) { if (asprintf(error_msg, "The EKMF Web setting " "'XTS Key Template Name (Key 1)' " "must be configured") < 0) error_msg = NULL; } rc = -EINVAL; goto out; } } if (xts_key2_template != NULL) { *xts_key2_template = _ekmf_find_setting(response_obj, SETTING_ID_XTS_KEY2_TEMPLATE, verbose); if (*identity_template == NULL) { if (error_msg != NULL) { if (asprintf(error_msg, "The EKMF Web setting " "'XTS Key Template Name (Key 2)' " "must be configured") < 0) error_msg = NULL; } rc = -EINVAL; goto out; } } if (non_xts_template != NULL) { *non_xts_template = _ekmf_find_setting(response_obj, SETTING_ID_NON_XTS_TEMPLATE, verbose); if (*non_xts_template == NULL) { if (error_msg != NULL) { if (asprintf(error_msg, "The EKMF Web setting " "'Non-XTS Key Template Name' " "must be configured") < 0) error_msg = NULL; } rc = -EINVAL; goto out; } } out: _ekmf_release_curl_handle(curl_handle, curl); if (response_obj != NULL) json_object_put(response_obj); if (login_token != NULL) free(login_token); if (rc != 0) { if (identity_template != NULL && *identity_template != NULL) { free(*identity_template); *identity_template = NULL; } if (xts_key1_template != NULL && *xts_key1_template != NULL) { free(*xts_key1_template); *xts_key1_template = NULL; } if (xts_key2_template != NULL && *xts_key2_template != NULL) { free(*xts_key2_template); *xts_key2_template = NULL; } if (non_xts_template != NULL && *non_xts_template != NULL) { free(*non_xts_template); *non_xts_template = NULL; } } return rc; } /** * Checks if the EKMFWeb server has the required Pervasive Encryption feature * installed * * To perform a single request, set curl_handle to NULL. This will cause the * function to initialize a new CURL handle, use it, and destroy it. * If you plan to perform multiple requests to the same host, supply the address * of a CURL pointer that is initially NULL. This function will then initialize * a new CURL handle on the first call. On subsequent calls, pass in the address * of the same CURL pointer so that the CURL handle is reused. After the last * request, the CURL handle must be destroyed by calling ekmf_curl_destroy). * * @param config the configuration structure * @param curl_handle address of a CURL handle used for reusing the same * CURL handle with multiple requests. * @param error_msg on return: If not NULL, then a textual error message * is returned in case of a failing request. The caller * must free the error string when it is not NULL. * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. * -ENOTSUP is returned, if the feature is not installed. */ int ekmf_check_feature(const struct ekmf_config *config, CURL **curl_handle, char **error_msg, bool verbose) { json_object *response_obj = NULL, *obj; bool found = false; CURL *curl = NULL; long status_code; const char *id; int rc, i; if (config == NULL) return -EINVAL; rc = _ekmf_get_curl_handle(curl_handle, &curl); if (rc != 0) { pr_verbose(verbose, "Failed to get CURL handle"); rc = -EIO; goto out; } rc = _ekmf_perform_request(config, EKMF_URI_SYSTEM_FEATURES, "GET", NULL, NULL, NULL, &response_obj, NULL, &status_code, error_msg, curl, verbose); if (rc != 0) { pr_verbose(verbose, "Failed perform the REST call"); if (rc > 0) rc = -EIO; goto out; } switch (status_code) { case 200: break; default: pr_verbose(verbose, "REST Call failed with HTTP status code: " "%ld", status_code); rc = -EIO; goto out; } JSON_CHECK_OBJ(response_obj, json_type_array, rc, -EIO, "No or invalid response content", verbose, out); for (i = 0; i < (int)json_object_array_length(response_obj); i++) { obj = json_object_array_get_idx(response_obj, i); JSON_CHECK_OBJ(obj, json_type_object, rc, -EIO, "No features object", verbose, out); id = json_get_string(obj, "id"); JSON_CHECK_ERROR(id == NULL, rc, -EIO, "No id field in settings object", verbose, out); if (strcmp(id, FEATURE_ID_PERVASIVE_ENCRYPTION) != 0) continue; found = true; break; } if (!found) { pr_verbose(verbose, "Feature '%s' is not installed", FEATURE_ID_PERVASIVE_ENCRYPTION); rc = -ENOTSUP; if (asprintf(error_msg, "EKMF Web feature " "'Pervasive Encryption' is not installed.")) { pr_verbose(verbose, "asprintf failed"); rc = -ENOMEM; } goto out; } out: _ekmf_release_curl_handle(curl_handle, curl); if (response_obj != NULL) json_object_put(response_obj); return rc; } /** * Build the party info JSON object as base64(sha256(key_uuid|timestamp)). * Digest_nid specifies the digest to use, ot 0 to use the default (SHA256). * The function returns the party info JSON object, as well as the raw party * info. */ static int _ekmf_build_party_info(const char *key_uuid, const char *timestamp, int digest_nid, unsigned char *party_info, size_t *party_info_length, json_object **party_info_obj, bool verbose) { unsigned int digest_len; EVP_MD_CTX *ctx = NULL; const EVP_MD *md; int rc; md = EVP_get_digestbynid(digest_nid != 0 ? digest_nid : NID_sha256); if (md == NULL) { pr_verbose(verbose, "Failed to get specified digest"); rc = -EINVAL; goto out; } if (*party_info_length < (size_t)EVP_MD_size(md)) { pr_verbose(verbose, "Party info buffer is too small"); return -ERANGE; goto out; } ctx = EVP_MD_CTX_create(); if (ctx == NULL) { pr_verbose(verbose, "Failed to allocate MD context"); rc = -ENOMEM; goto out; } rc = EVP_DigestInit_ex(ctx, md, NULL); if (rc != 1) { pr_verbose(verbose, "Failed to initialize MD context"); rc = -EIO; goto out; } rc = EVP_DigestUpdate(ctx, key_uuid, strlen(key_uuid)); if (rc != 1) { pr_verbose(verbose, "Failed to add data to the MD context"); rc = -EIO; goto out; } rc = EVP_DigestUpdate(ctx, timestamp, strlen(timestamp)); if (rc != 1) { pr_verbose(verbose, "Failed to add data to the MD context"); rc = -EIO; goto out; } rc = EVP_DigestFinal_ex(ctx, party_info, &digest_len); if (rc != 1) { pr_verbose(verbose, "Failed to finalize the MD context"); rc = -EIO; goto out; } *party_info_length = digest_len; *party_info_obj = json_object_new_base64url(party_info, digest_len); rc = 0; out: if (ctx != NULL) EVP_MD_CTX_destroy(ctx); return rc; } /** * Builds a (detached) JSON Web Signature using the secure identity key from * the payload and returns a signature JSON object */ static int _ekmf_build_signature(unsigned char *key_blob, size_t key_blob_length, json_object *payload_obj, json_object **signature_obj, int digest_nid, bool use_rsa_pss, const char *jws_kid, const struct ekmf_ext_lib *ext_lib, bool verbose) { struct sk_rsa_pss_params rsa_pss_params; struct ext_lib_info ext_lib_info; EVP_PKEY_CTX *pkey_ctx = NULL; EVP_MD_CTX *md_ctx = NULL; EVP_PKEY *pkey = NULL; const char *payload; const char *jws_alg; int rc, curve_nid; char *jws = NULL; BIO *b; _ekmf_copy_ext_lib(ext_lib, &ext_lib_info); rc = SK_OPENSSL_get_secure_key_as_pkey(key_blob, key_blob_length, use_rsa_pss, &pkey, &ext_lib_info.ext_lib, verbose); if (rc != 0) { pr_verbose(verbose, "Failed to get the identity PKEY"); goto out; } /* * Only the following combinations are allowed per RFC7518 for JSON * Web Signatures (JWS) using ECC or RSA identity keys: * alg=ES256: ECDSA using P-256 and SHA-256 * alg=ES384: ECDSA using P-384 and SHA-384 * alg=ES512: ECDSA using P-521 and SHA-512 * alg=RS256: RSA-PKCS1 using SHA-256 * alg=RS384: RSA-PKCS1 using SHA-384 * alg=RS512: RSA-PKCS1 using SHA-512 * alg=PS256: RSA-PSS using SHA-256, MGF1 with SHA-256, salt=digest * alg=PS384: RSA-PSS using SHA-384, MGF1 with SHA-384, salt=digest * alg=PS512: RSA-PSS using SHA-512, MGF1 with SHA-512, salt=digest */ switch (EVP_PKEY_id(pkey)) { case EVP_PKEY_EC: curve_nid = SK_OPENSSL_get_curve_from_ec_pkey(pkey); switch (curve_nid) { case NID_secp521r1: digest_nid = NID_sha512; jws_alg = "ES512"; break; case NID_secp384r1: digest_nid = NID_sha384; jws_alg = "ES384"; break; case NID_X9_62_prime256v1: digest_nid = NID_sha256; jws_alg = "ES256"; break; default: pr_verbose(verbose, "Unsupported curve"); rc = -EINVAL; goto out; } break; case EVP_PKEY_RSA: switch (digest_nid) { case NID_sha256: jws_alg = "RS256"; break; case NID_sha384: jws_alg = "RS384"; break; case NID_sha512: case 0: jws_alg = "RS512"; digest_nid = NID_sha512; break; default: pr_verbose(verbose, "Unsupported digest"); rc = -EINVAL; goto out; } break; case EVP_PKEY_RSA_PSS: switch (digest_nid) { case NID_sha256: jws_alg = "PS256"; break; case NID_sha384: jws_alg = "PS384"; break; case NID_sha512: case 0: jws_alg = "PS512"; digest_nid = NID_sha512; break; default: pr_verbose(verbose, "Unsupported digest"); rc = -EINVAL; goto out; } rsa_pss_params.mgf_digest_nid = digest_nid; rsa_pss_params.salt_len = RSA_PSS_SALTLEN_DIGEST; break; default: pr_verbose(verbose, "Unsupported key type"); rc = -EINVAL; goto out; } rc = SK_OPENSSL_setup_sign_context(pkey, false, digest_nid, &rsa_pss_params, &md_ctx, &pkey_ctx, verbose); if (rc != 0) { pr_verbose(verbose, "Failed to setup secure key sign context"); goto out; } payload = json_object_to_json_string_ext(payload_obj, JSON_C_TO_STRING_PLAIN | JSON_C_TO_STRING_NOSLASHESCAPE); if (payload == NULL) { pr_verbose(verbose, "Failed to get the payload string"); rc = -EIO; goto out; } if (verbose) { pr_verbose(verbose, "JWS Payload: ->%s<-", payload); pr_verbose(verbose, "JWS alg: %s", jws_alg); pr_verbose(verbose, "Public signing key:"); b = BIO_new_fp(stderr, BIO_NOCLOSE); PEM_write_bio_PUBKEY(b, pkey); BIO_free(b); } rc = create_json_web_signature(jws_alg, false, jws_kid, (unsigned char *)payload, strlen(payload), true, md_ctx, &jws); if (rc != 0) { pr_verbose(verbose, "Failed to build the JWS"); goto out; } *signature_obj = json_object_new_string(jws); rc = 0; out: if (md_ctx != NULL) EVP_MD_CTX_free(md_ctx); if (pkey != NULL) EVP_PKEY_free(pkey); if (jws != NULL) free(jws); return rc; } /** * Verifies the (detached) JSON Web Signature using the server's public signing * key and the response payload. * Note: This function removes the signature field from the response JSON * object! */ static int _ekmf_verify_signature(json_object *response_obj, EVP_PKEY *server_pubkey, bool verbose) { json_object *signature_obj = NULL; const char *sign_payload; BIO *b; int rc; if (response_obj == NULL) return -EINVAL; json_object_object_get_ex(response_obj, "signature", &signature_obj); JSON_CHECK_OBJ(signature_obj, json_type_string, rc, -EIO, "Failed to get the response signature", verbose, out); json_object_get(signature_obj); /* Take ownership */ json_object_object_del(response_obj, "signature"); sign_payload = json_object_to_json_string_ext(response_obj, JSON_C_TO_STRING_PLAIN | JSON_C_TO_STRING_NOSLASHESCAPE); if (sign_payload == NULL) { rc = -ENOMEM; goto out; } if (verbose) { pr_verbose(verbose, "JWS Payload: ->%s<-", sign_payload); pr_verbose(verbose, "Public signing key:"); b = BIO_new_fp(stderr, BIO_NOCLOSE); PEM_write_bio_PUBKEY(b, server_pubkey); BIO_free(b); } rc = verify_json_web_signature(json_object_get_string(signature_obj), (const unsigned char *)sign_payload, strlen(sign_payload), server_pubkey); if (rc != 0) { pr_verbose(verbose, "Signature verify of response failed"); goto out; } pr_verbose(verbose, "Signature of response successfully verified"); out: if (signature_obj != NULL) json_object_put(signature_obj); return rc; } /** * Import the key retrieved from EKMFWeb. */ static int _ekmf_import_key(unsigned char *req_sess_key, size_t req_sess_key_length, unsigned char *req_party_info, size_t req_party_info_length, unsigned char *resp_party_info, size_t resp_party_info_length, json_object *resp_sess_jwk_obj, json_object *resp_exp_jwk_obj, unsigned char *key_blob, size_t *key_blob_length, const struct ekmf_ext_lib *ext_lib, bool verbose) { size_t resp_sess_ec_key_length, resp_exported_key_length; unsigned char resp_exported_key[MAX_SYM_KEY_BLOB_SIZE]; unsigned char transport_key[MAX_SYM_KEY_BLOB_SIZE]; unsigned char resp_sess_key[MAX_KEY_BLOB_SIZE]; size_t party_info_length, transport_key_length; unsigned char *party_info = NULL; int rc; party_info_length = req_party_info_length + resp_party_info_length; party_info = malloc(party_info_length); if (party_info == NULL) { pr_verbose(verbose, "Failed to allocate memory"); rc = -ENOMEM; goto out; } memcpy(party_info, req_party_info, req_party_info_length); memcpy(party_info + req_party_info_length, resp_party_info, resp_party_info_length); switch (ext_lib->type) { case EKMF_EXT_LIB_CCA: resp_sess_ec_key_length = sizeof(resp_sess_key); rc = cca_import_key_from_json_web_key(ext_lib->cca, resp_sess_jwk_obj, resp_sess_key, &resp_sess_ec_key_length, verbose); if (rc != 0) { pr_verbose(verbose, "Failed to import the session EC " "key"); goto out; } transport_key_length = sizeof(transport_key); rc = cca_ec_dh_derive_importer(ext_lib->cca, req_sess_key, req_sess_key_length, resp_sess_key, resp_sess_ec_key_length, party_info, party_info_length, CCA_KDF_ANS_X9_63_CCA, transport_key, &transport_key_length, verbose); if (rc != 0) { pr_verbose(verbose, "Failed to derive transport key"); goto out; } resp_exported_key_length = sizeof(resp_exported_key); rc = cca_import_key_from_json_web_key(ext_lib->cca, resp_exp_jwk_obj, resp_exported_key, &resp_exported_key_length, verbose); if (rc != 0) { pr_verbose(verbose, "Failed to import the exported " "key"); goto out; } rc = cca_import_external_key(ext_lib->cca, resp_exported_key, resp_exported_key_length, transport_key, transport_key_length, key_blob, key_blob_length, verbose); if (rc != 0) { pr_verbose(verbose, "Failed to unwrap the exported " "key with the transport key"); goto out; } break; default: pr_verbose(verbose, "Invalid ext lib type: %d", ext_lib->type); return -EINVAL; } out: if (party_info != NULL) free(party_info); return rc; } /* * Callback function for SK_OPENSSL_get_public_from_secure_key. Construct * a JSON Web key from a public EC key. */ static int _ekmf_pub_key_as_json_web_key(const struct sk_pub_key_info *pub_key, void *private) { json_object **jwk_ret = private; json_object *jwk_obj = NULL; int rc = 0; if (pub_key->type != SK_KEY_TYPE_EC) return -EINVAL; /* construct the JWK */ jwk_obj = json_object_new_object(); if (jwk_obj == NULL) return -ENOMEM; /* * Note: The order of the fields is important, EKMFWeb expects it in * exactly this order! */ rc = json_object_object_add_ex(jwk_obj, "kty", json_object_new_string("EC"), 0); rc |= json_object_object_add_ex(jwk_obj, "crv", json_object_new_string( EC_curve_nid2nist(pub_key->ec.curve_nid)), 0); rc |= json_object_object_add_ex(jwk_obj, "x", json_object_new_base64url(pub_key->ec.x, pub_key->ec.prime_len), 0); rc |= json_object_object_add_ex(jwk_obj, "y", json_object_new_base64url(pub_key->ec.y, pub_key->ec.prime_len), 0); if (rc != 0) { json_object_put(jwk_obj); return -EIO; } *jwk_ret = jwk_obj; return 0; } /** * Requests a key to be retrieved from EKMFweb and imported under the current * HSM's master key. * * To perform a single request, set curl_handle to NULL. This will cause the * function to initialize a new CURL handle, use it, and destroy it. * If you plan to perform multiple requests to the same host, supply the address * of a CURL pointer that is initially NULL. This function will then initialize * a new CURL handle on the first call. On subsequent calls, pass in the address * of the same CURL pointer so that the CURL handle is reused. After the last * request, the CURL handle must be destroyed by calling ekmf_curl_destroy). * * @param config the configuration structure * @param curl_handle address of a CURL handle used for reusing the same * CURL handle with multiple requests. * @param key_uuid the UUID of the key to retrieve * @param sess_ec_curve_nid The OpenSSL nid of the EC curve used for the session * ECC key. If 0, then the default curve is used. * @param sign_rsa_digest_nid The OpenSSL nid of a digest used to sign the * request with if the identity key is an RSA-type key. * If 0, then the default digest is used. * Ignored for ECC-type identity keys. * @param use_rsa_pss If true, and the identity key is an RSA-type key, * use RSA-PSS to sign the request. * @param signature_kid the Key ID for the signature of the request * @param key_blob a buffer to store the retrieved key blob to * @param key_blob_length On entry: the size ofthe buffer * On return: the size of the key blob retrieved * @param error_msg on return: If not NULL, then a textual error message * is returned in case of a failing request. The caller * must free the error string when it is not NULL. * @param ext_lib External secure key crypto library to use * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. * -EACCES is returned, if no or no valid login token is available. * -EPERM is returned if the login token does not have permission to * retrieve the key */ int ekmf_retrieve_key(const struct ekmf_config *config, CURL **curl_handle, const char *key_uuid, int sess_ec_curve_nid, int sign_rsa_digest_nid, bool use_rsa_pss, const char *signature_kid, unsigned char *key_blob, size_t *key_blob_length, char **error_msg, const struct ekmf_ext_lib *ext_lib, bool verbose) { size_t req_party_info_length, resp_party_info_length; unsigned char req_party_info[SHA512_DIGEST_LENGTH]; size_t req_sess_ec_key_length, identity_key_length; unsigned char req_sess_ec_key[MAX_KEY_BLOB_SIZE]; unsigned char identity_key[MAX_KEY_BLOB_SIZE]; json_object *resp_originator_obj = NULL; json_object *resp_addl_info_obj = NULL; json_object *req_party_info_obj = NULL; json_object *req_originator_obj = NULL; json_object *req_timestamp_obj = NULL; json_object *req_addl_info_obj = NULL; json_object *req_signature_obj = NULL; unsigned char *resp_party_info = NULL; json_object *resp_sess_jwk_obj = NULL; json_object *req_sess_jwk_obj = NULL; json_object *resp_exp_jwk_obj = NULL; struct ext_lib_info ext_lib_info; json_object *response_obj = NULL; json_object *request_obj = NULL; struct sk_key_gen_info gen_info; EVP_PKEY *server_pubkey = NULL; char *escaped_uuid = NULL; char *login_token = NULL; bool token_valid = false; CURL *curl = NULL; long status_code; char *uri = NULL; int rc; if (config == NULL || key_uuid == NULL || key_blob == NULL || key_blob_length == NULL || ext_lib == NULL) return -EINVAL; _ekmf_copy_ext_lib(ext_lib, &ext_lib_info); rc = SK_OPENSSL_init(verbose); if (rc != 0) { pr_verbose(verbose, "Failed to initialize secure key support: " "%s", strerror(-rc)); return rc; } rc = ekmf_check_login_token(config, &token_valid, &login_token, verbose); if (rc != 0 || !token_valid) { pr_verbose(verbose, "No valid login token available"); rc = -EACCES; goto out; } rc = _ekmf_get_curl_handle(curl_handle, &curl); if (rc != 0) { pr_verbose(verbose, "Failed to get CURL handle"); rc = -EIO; goto out; } rc = read_public_key(config->ekmf_server_pubkey, &server_pubkey); if (rc != 0) { pr_verbose(verbose, "Failed to read EKMFWeb server's public key" " '%s': %s", config->ekmf_server_pubkey, strerror(-rc)); goto out; } identity_key_length = sizeof(identity_key); rc = read_key_blob(config->identity_secure_key, identity_key, &identity_key_length); if (rc != 0) { pr_verbose(verbose, "Failed to read identity key from file " "'%s': %s", config->identity_secure_key, strerror(-rc)); goto out; } gen_info.type = SK_KEY_TYPE_EC; gen_info.ec.curve_nid = sess_ec_curve_nid != 0 ? sess_ec_curve_nid : DEFAULT_SESSION_EC_KEY_CURVE; req_sess_ec_key_length = sizeof(req_sess_ec_key); rc = SK_OPENSSL_generate_secure_key(req_sess_ec_key, &req_sess_ec_key_length, &gen_info, &ext_lib_info.ext_lib, verbose); if (rc != 0) { pr_verbose(verbose, "Failed to generate a session EC " "key"); goto out; } rc = SK_OPENSSL_get_public_from_secure_key(req_sess_ec_key, req_sess_ec_key_length, _ekmf_pub_key_as_json_web_key, &req_sess_jwk_obj, &ext_lib_info.ext_lib, verbose); if (rc != 0 || req_sess_jwk_obj == NULL) { pr_verbose(verbose, "Failed to generate session JWK"); goto out; } req_timestamp_obj = get_json_timestamp(); JSON_CHECK_ERROR(req_timestamp_obj == NULL, rc, -EIO, "Failed to generate timestamp", verbose, out); req_party_info_length = sizeof(req_party_info); rc = _ekmf_build_party_info(key_uuid, json_object_get_string(req_timestamp_obj), NID_sha256, req_party_info, &req_party_info_length, &req_party_info_obj, verbose); if (rc != 0) { pr_verbose(verbose, "Failed to build the party info"); goto out; } /* * Note: The order of the fields is important, EKMFWeb expects it in * exactly this order! */ req_addl_info_obj = json_object_new_object(); JSON_CHECK_ERROR(req_addl_info_obj == NULL, rc, -ENOMEM, "Failed to generate JSON object", verbose, out); rc = json_object_object_add_ex(req_addl_info_obj, "kdf", json_object_new_string("ANS-X9.63-CCA"), 0); JSON_CHECK_ERROR(rc != 0, rc, -EIO, "Failed to add data to JSON object", verbose, out); rc = json_object_object_add_ex(req_addl_info_obj, "requestedKey", json_object_new_string(key_uuid), 0); JSON_CHECK_ERROR(rc != 0, rc, -EIO, "Failed to add data to JSON object", verbose, out); rc = json_object_object_add_ex(req_addl_info_obj, "timestamp", req_timestamp_obj, 0); JSON_CHECK_ERROR(rc != 0, rc, -EIO, "Failed to add data to JSON object", verbose, out); req_timestamp_obj = NULL; req_originator_obj = json_object_new_object(); JSON_CHECK_ERROR(req_originator_obj == NULL, rc, -ENOMEM, "Failed to generate JSON object", verbose, out); rc = json_object_object_add_ex(req_originator_obj, "session", req_sess_jwk_obj, 0); JSON_CHECK_ERROR(rc != 0, rc, -EIO, "Failed to add data to JSON object", verbose, out); req_sess_jwk_obj = NULL; rc = json_object_object_add_ex(req_originator_obj, "partyInfo", req_party_info_obj, 0); JSON_CHECK_ERROR(rc != 0, rc, -EIO, "Failed to add data to JSON object", verbose, out); req_party_info_obj = NULL; request_obj = json_object_new_object(); JSON_CHECK_ERROR(request_obj == NULL, rc, -ENOMEM, "Failed to generate JSON object", verbose, out); rc = json_object_object_add_ex(request_obj, "originator", req_originator_obj, 0); JSON_CHECK_ERROR(rc != 0, rc, -EIO, "Failed to add data to JSON object", verbose, out); req_originator_obj = NULL; rc = json_object_object_add_ex(request_obj, "additionalInfo", req_addl_info_obj, 0); JSON_CHECK_ERROR(rc != 0, rc, -EIO, "Failed to add data to JSON object", verbose, out); req_addl_info_obj = NULL; rc = _ekmf_build_signature(identity_key, identity_key_length, request_obj, &req_signature_obj, sign_rsa_digest_nid, use_rsa_pss, signature_kid, ext_lib, verbose); if (rc != 0) { pr_verbose(verbose, "Failed to build the signature"); goto out; } rc = json_object_object_add_ex(request_obj, "signature", req_signature_obj, 0); JSON_CHECK_ERROR(rc != 0, rc, -EIO, "Failed to add data to JSON object", verbose, out); req_signature_obj = NULL; escaped_uuid = curl_easy_escape(curl, key_uuid, 0); if (escaped_uuid == NULL) { pr_verbose(verbose, "Failed to url-escape the key uuid"); rc = -EIO; goto out; } if (asprintf(&uri, EKMF_URI_KEYS_EXPORT, escaped_uuid) < 0) { pr_verbose(verbose, "asprintf failed"); rc = -ENOMEM; goto out; } rc = _ekmf_perform_request(config, uri, "POST", request_obj, NULL, login_token, &response_obj, NULL, &status_code, error_msg, curl, verbose); if (rc != 0) { pr_verbose(verbose, "Failed perform the REST call"); if (rc > 0) rc = -EIO; goto out; } switch (status_code) { case 200: break; case 400: pr_verbose(verbose, "Bad request"); rc = -EBADMSG; goto out; case 401: pr_verbose(verbose, "Not authorized"); rc = -EACCES; goto out; case 403: pr_verbose(verbose, "Insufficient permissions"); rc = -EPERM; goto out; case 404: pr_verbose(verbose, "Not found"); rc = -ENOENT; goto out; default: pr_verbose(verbose, "REST Call failed with HTTP status code: " "%ld", status_code); rc = -EIO; goto out; } JSON_CHECK_OBJ(response_obj, json_type_object, rc, -EBADMSG, "No or invalid response", verbose, out); rc = _ekmf_verify_signature(response_obj, server_pubkey, verbose); if (rc != 0) goto out; json_object_object_get_ex(response_obj, "originator", &resp_originator_obj); JSON_CHECK_OBJ(resp_originator_obj, json_type_object, rc, -EBADMSG, "Failed to get the response originator", verbose, out); json_object_object_get_ex(resp_originator_obj, "session", &resp_sess_jwk_obj); JSON_CHECK_OBJ(resp_sess_jwk_obj, json_type_object, rc, -EBADMSG, "Failed to get the response session key", verbose, out); rc = json_object_get_base64url(resp_originator_obj, "partyInfo", NULL, &resp_party_info_length); if (rc != 0) { pr_verbose(verbose, "Failed to get the response partyInfo"); goto out; } resp_party_info = malloc(resp_party_info_length); if (resp_party_info == NULL) { rc = -ENOMEM; goto out; } rc = json_object_get_base64url(resp_originator_obj, "partyInfo", resp_party_info, &resp_party_info_length); if (rc != 0) { pr_verbose(verbose, "Failed to get the response partyInfo"); goto out; } json_object_object_get_ex(response_obj, "additionalInfo", &resp_addl_info_obj); JSON_CHECK_OBJ(resp_addl_info_obj, json_type_object, rc, -EBADMSG, "Failed to get the response addl.info", verbose, out); json_object_object_get_ex(resp_addl_info_obj, "exportedKey", &resp_exp_jwk_obj); JSON_CHECK_OBJ(resp_exp_jwk_obj, json_type_object, rc, -EBADMSG, "Failed to get the response exported key", verbose, out); rc = _ekmf_import_key(req_sess_ec_key, req_sess_ec_key_length, req_party_info, req_party_info_length, resp_party_info, resp_party_info_length, resp_sess_jwk_obj, resp_exp_jwk_obj, key_blob, key_blob_length, ext_lib, verbose); if (rc != 0) { pr_verbose(verbose, "Failed to import the retrieved key"); goto out; } out: _ekmf_release_curl_handle(curl_handle, curl); if (req_sess_jwk_obj != NULL) json_object_put(req_sess_jwk_obj); if (req_timestamp_obj != NULL) json_object_put(req_timestamp_obj); if (req_addl_info_obj != NULL) json_object_put(req_addl_info_obj); if (req_party_info_obj != NULL) json_object_put(req_party_info_obj); if (req_originator_obj != NULL) json_object_put(req_originator_obj); if (req_signature_obj != NULL) json_object_put(req_signature_obj); if (request_obj != NULL) json_object_put(request_obj); if (response_obj != NULL) json_object_put(response_obj); if (uri != NULL) free(uri); if (login_token != NULL) free(login_token); if (server_pubkey != NULL) EVP_PKEY_free(server_pubkey); if (resp_party_info != NULL) free(resp_party_info); if (escaped_uuid != NULL) curl_free(escaped_uuid); SK_OPENSSL_term(); return rc; } /** * Callback function for the _ekmf_list_request function. This callback * is called for each result element. The curl handle can be used to perform * further requests within the callback. However, the curl handle must not be * closed/destroyed! */ typedef int (*ekmf_element_cb_t)(CURL *curl, json_object *element, void *private, bool verbose); /** * Performs a list request (GET) on a base list_uri and iterates over * multiple pages. The response of a list request is expected to be a * JSON array of elements. For each element, the element callback is called * with the element. * The list_uri must contain anything required to list the desired objects, * except the page and perPage UTL parameters. Those are added by this function. */ static int _ekmf_list_request(const struct ekmf_config *config, const char *list_uri, CURL *curl, ekmf_element_cb_t element_cb, void *private, const char *login_token, char **error_msg, bool verbose) { json_object *response_obj = NULL; json_object *element_obj; int num, i, rc = 0; unsigned int page; char *uri = NULL; long status_code; bool has_query; if (config == NULL || list_uri == NULL || element_cb == NULL || curl == NULL) return -EINVAL; has_query = strchr(list_uri, '?') != NULL; for (page = 1; ; page++) { if (asprintf(&uri, "%s%sperPage=%u&page=%u", list_uri, has_query ? "&" : "?", LIST_ELEMENTS_PER_PAGE, page) < 0) { pr_verbose(verbose, "asprintf failed"); rc = -ENOMEM; goto out; } rc = _ekmf_perform_request(config, uri, "GET", NULL, NULL, login_token, &response_obj, NULL, &status_code, error_msg, curl, verbose); free(uri); uri = NULL; if (rc != 0) { pr_verbose(verbose, "Failed perform the REST call"); if (rc > 0) rc = -EIO; goto out; } switch (status_code) { case 200: break; case 400: pr_verbose(verbose, "Bad request"); rc = -EBADMSG; goto out; case 401: pr_verbose(verbose, "Not authorized"); rc = -EACCES; goto out; case 403: pr_verbose(verbose, "Insufficient permissions"); rc = -EPERM; goto out; default: pr_verbose(verbose, "REST Call failed with HTTP " "status code: %ld", status_code); rc = -EIO; goto out; } JSON_CHECK_OBJ(response_obj, json_type_array, rc, -EIO, "No or invalid response content", verbose, out); num = json_object_array_length(response_obj); if (num == 0) break; for (i = 0; i < num; i++) { element_obj = json_object_array_get_idx(response_obj, i); if (element_obj == NULL) { pr_verbose(verbose, "Failed to get array " "element for index %d", i); rc = -EBADMSG; goto out; } rc = element_cb(curl, element_obj, private, verbose); if (rc != 0) { pr_verbose(verbose, "Element-callback failed " "for index %d", i); goto out; } } if (response_obj != NULL) json_object_put(response_obj); response_obj = NULL; if (num < LIST_ELEMENTS_PER_PAGE) break; } out: if (uri != NULL) free(uri); if (response_obj != NULL) json_object_put(response_obj); return rc; } struct ekmf_template_cb_data_t { ekmf_template_cb_t template_cb; void *cb_private; }; /** * Callback for template list function. Builds the template info structure * and calls the application callback. */ static int _ekmf_template_cb(CURL *curl, json_object *element, void *private, bool verbose) { struct ekmf_template_cb_data_t *cb_data = private; struct ekmf_template_info template = { 0 }; int rc; if (cb_data->template_cb == NULL) { pr_verbose(verbose, "No template callback function"); return -EINVAL; } rc = json_build_template_info(element, &template, false); if (rc != 0) { pr_verbose(verbose, "Failed to build template info"); goto out; } rc = cb_data->template_cb(curl, &template, cb_data->cb_private); if (rc != 0) { pr_verbose(verbose, "Template callback rc: %d", rc); goto out; } out: free_tag_def_list(&template.label_tags, false); return rc; } /** * List available key templates. Only templates in state ACTIVE, with key * algorithm AES and keystore type PERVASIVE_ENCRYPTION are listed. The * templates are ordered by name in ascending order. * * To perform a single request, set curl_handle to NULL. This will cause the * function to initialize a new CURL handle, use it, and destroy it. * If you plan to perform multiple requests to the same host, supply the address * of a CURL pointer that is initially NULL. This function will then initialize * a new CURL handle on the first call. On subsequent calls, pass in the address * of the same CURL pointer so that the CURL handle is reused. After the last * request, the CURL handle must be destroyed by calling ekmf_curl_destroy). * * @param config the configuration structure * @param curl_handle address of a CURL handle used for reusing the same * CURL handle with multiple requests. * @param template_cb a callback function that is called for each template * found * @param private a pointer that is passed as-is to the callback * @param name_pattern a pattern to filter by name, or NULL to list all. * @param state the state of the templates to list. If NULL then * templates in state 'ACTIVE' are listed * @param error_msg on return: If not NULL, then a textual error message * is returned in case of a failing request. The caller * must free the error string when it is not NULL. * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. * -EACCES is returned, if no or no valid login token is available. * -EPERM is returned if the login token does not have permission to * list the templates */ int ekmf_list_templates(const struct ekmf_config *config, CURL **curl_handle, ekmf_template_cb_t template_cb, void *private, const char *name_pattern, const char *state, char **error_msg, bool verbose) { struct ekmf_template_cb_data_t cb_data; char *escaped_name_pattern = NULL; char *escaped_state = NULL; char *login_token = NULL; bool token_valid = false; CURL *curl = NULL; char *uri = NULL; int rc; if (config == NULL || template_cb == NULL) return -EINVAL; rc = ekmf_check_login_token(config, &token_valid, &login_token, verbose); if (rc != 0 || !token_valid) { pr_verbose(verbose, "No valid login token available"); rc = -EACCES; goto out; } rc = _ekmf_get_curl_handle(curl_handle, &curl); if (rc != 0) { pr_verbose(verbose, "Failed to get CURL handle"); rc = -EIO; goto out; } cb_data.template_cb = template_cb; cb_data.cb_private = private; escaped_name_pattern = curl_easy_escape(curl, name_pattern != NULL ? name_pattern : "*", 0); if (escaped_name_pattern == NULL) { pr_verbose(verbose, "Failed to url-escape the name pattern"); rc = -EIO; goto out; } escaped_state = curl_easy_escape(curl, state != NULL ? state : TEMPLATE_STATE_ACTIVE, 0); if (escaped_state == NULL) { pr_verbose(verbose, "Failed to url-escape the state"); rc = -EIO; goto out; } if (asprintf(&uri, EKMF_URI_TEMPLATE_LIST, escaped_state, ORDER_BY_NAME_ASC, escaped_name_pattern) < 0) { pr_verbose(verbose, "asprintf failed"); rc = -ENOMEM; goto out; } rc = _ekmf_list_request(config, uri, curl, _ekmf_template_cb, &cb_data, login_token, error_msg, verbose); if (rc != 0) { pr_verbose(verbose, "Failed to perform the list request"); if (rc > 0) rc = -EIO; goto out; } out: _ekmf_release_curl_handle(curl_handle, curl); if (login_token != NULL) free(login_token); if (uri != NULL) free(uri); if (escaped_name_pattern != NULL) curl_free(escaped_name_pattern); if (escaped_state != NULL) curl_free(escaped_state); return rc; } /** * Get a template by its UUID. * * To perform a single request, set curl_handle to NULL. This will cause the * function to initialize a new CURL handle, use it, and destroy it. * If you plan to perform multiple requests to the same host, supply the address * of a CURL pointer that is initially NULL. This function will then initialize * a new CURL handle on the first call. On subsequent calls, pass in the address * of the same CURL pointer so that the CURL handle is reused. After the last * request, the CURL handle must be destroyed by calling ekmf_curl_destroy). * * @param config the configuration structure * @param curl_handle address of a CURL handle used for reusing the same * CURL handle with multiple requests. * @param template_uuid the UUID of the template to get * @param template an address of a template info pointer. On return * the pointer is updated to point to a newly allocated * template info struct. It must be freed by the caller * using ekmf_free_template_info when no longer needed. * @param error_msg on return: If not NULL, then a textual error message * is returned in case of a failing request. The caller * must free the error string when it is not NULL. * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. * -EACCES is returned, if no or no valid login token is available. * -EPERM is returned if the login token does not have permission to * get the template */ int ekmf_get_template(const struct ekmf_config *config, CURL **curl_handle, const char *template_uuid, struct ekmf_template_info **template, char **error_msg, bool verbose) { json_object *response_obj = NULL; char *escaped_uuid = NULL; char *login_token = NULL; bool token_valid = false; CURL *curl = NULL; char *uri = NULL; long status_code; int rc; if (config == NULL || template_uuid == NULL || template == NULL) return -EINVAL; *template = NULL; rc = ekmf_check_login_token(config, &token_valid, &login_token, verbose); if (rc != 0 || !token_valid) { pr_verbose(verbose, "No valid login token available"); rc = -EACCES; goto out; } rc = _ekmf_get_curl_handle(curl_handle, &curl); if (rc != 0) { pr_verbose(verbose, "Failed to get CURL handle"); rc = -EIO; goto out; } escaped_uuid = curl_easy_escape(curl, template_uuid, 0); if (escaped_uuid == NULL) { pr_verbose(verbose, "Failed to url-escape the template uuid"); rc = -EIO; goto out; } if (asprintf(&uri, EKMF_URI_TEMPLATE_GET, escaped_uuid) < 0) { pr_verbose(verbose, "asprintf failed"); rc = -ENOMEM; goto out; } rc = _ekmf_perform_request(config, uri, "GET", NULL, NULL, login_token, &response_obj, NULL, &status_code, error_msg, curl, verbose); if (rc != 0) { pr_verbose(verbose, "Failed perform the REST call"); if (rc > 0) rc = -EIO; goto out; } switch (status_code) { case 200: break; case 400: pr_verbose(verbose, "Bad request"); rc = -EBADMSG; goto out; case 401: pr_verbose(verbose, "Not authorized"); rc = -EACCES; goto out; case 403: pr_verbose(verbose, "Insufficient permissions"); rc = -EPERM; goto out; case 404: pr_verbose(verbose, "Not found"); rc = -ENOENT; goto out; default: pr_verbose(verbose, "REST Call failed with HTTP status code: " "%ld", status_code); rc = -EIO; goto out; } JSON_CHECK_OBJ(response_obj, json_type_object, rc, -EBADMSG, "No or invalid response", verbose, out); *template = calloc(1, sizeof(struct ekmf_template_info)); if (*template == NULL) { pr_verbose(verbose, "calloc failed"); rc = -ENOMEM; goto out; } rc = json_build_template_info(response_obj, *template, true); if (rc != 0) { pr_verbose(verbose, "Failed to build template info"); goto out; } out: _ekmf_release_curl_handle(curl_handle, curl); if (response_obj != NULL) json_object_put(response_obj); if (login_token != NULL) free(login_token); if (uri != NULL) free(uri); if (escaped_uuid != NULL) curl_free(escaped_uuid); if (rc != 0 && *template != NULL) { free_template_info(*template); free(*template); *template = NULL; } return rc; } /** * Get the last used sequence number of a template by its UUID. * * To perform a single request, set curl_handle to NULL. This will cause the * function to initialize a new CURL handle, use it, and destroy it. * If you plan to perform multiple requests to the same host, supply the address * of a CURL pointer that is initially NULL. This function will then initialize * a new CURL handle on the first call. On subsequent calls, pass in the address * of the same CURL pointer so that the CURL handle is reused. After the last * request, the CURL handle must be destroyed by calling ekmf_curl_destroy). * * @param config the configuration structure * @param curl_handle address of a CURL handle used for reusing the same * CURL handle with multiple requests. * @param template_uuid the UUID of the template to get * @param seqNumber On return: the last used sequence number of this * template. * @param error_msg on return: If not NULL, then a textual error message * is returned in case of a failing request. The caller * must free the error string when it is not NULL. * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. * -EACCES is returned, if no or no valid login token is available. * -EPERM is returned if the login token does not have permission to * get the template */ int ekmf_get_last_seq_no(const struct ekmf_config *config, CURL **curl_handle, const char *template_uuid, unsigned int *seqNumber, char **error_msg, bool verbose) { json_object *response_obj = NULL, *field = NULL; char *escaped_uuid = NULL; char *login_token = NULL; bool token_valid = false; CURL *curl = NULL; char *uri = NULL; long status_code; int rc; if (config == NULL || template_uuid == NULL || seqNumber == NULL) return -EINVAL; *seqNumber = 0; rc = ekmf_check_login_token(config, &token_valid, &login_token, verbose); if (rc != 0 || !token_valid) { pr_verbose(verbose, "No valid login token available"); rc = -EACCES; goto out; } rc = _ekmf_get_curl_handle(curl_handle, &curl); if (rc != 0) { pr_verbose(verbose, "Failed to get CURL handle"); rc = -EIO; goto out; } escaped_uuid = curl_easy_escape(curl, template_uuid, 0); if (escaped_uuid == NULL) { pr_verbose(verbose, "Failed to url-escape the template uuid"); rc = -EIO; goto out; } if (asprintf(&uri, EKMF_URI_TEMPLATE_SEQNO, escaped_uuid) < 0) { pr_verbose(verbose, "asprintf failed"); rc = -ENOMEM; goto out; } rc = _ekmf_perform_request(config, uri, "GET", NULL, NULL, login_token, &response_obj, NULL, &status_code, error_msg, curl, verbose); if (rc != 0) { pr_verbose(verbose, "Failed perform the REST call"); if (rc > 0) rc = -EIO; goto out; } switch (status_code) { case 200: break; case 400: pr_verbose(verbose, "Bad request"); rc = -EBADMSG; goto out; case 401: pr_verbose(verbose, "Not authorized"); rc = -EACCES; goto out; case 403: pr_verbose(verbose, "Insufficient permissions"); rc = -EPERM; goto out; case 404: pr_verbose(verbose, "Not found"); rc = -ENOENT; goto out; default: pr_verbose(verbose, "REST Call failed with HTTP status code: " "%ld", status_code); rc = -EIO; goto out; } JSON_CHECK_OBJ(response_obj, json_type_object, rc, -EBADMSG, "No or invalid response", verbose, out); json_object_object_get_ex(response_obj, "lastSequenceNumber", &field); JSON_CHECK_OBJ(field, json_type_int, rc, -EBADMSG, "Invalid response", verbose, out); *seqNumber = json_object_get_int(field); out: _ekmf_release_curl_handle(curl_handle, curl); if (response_obj != NULL) json_object_put(response_obj); if (login_token != NULL) free(login_token); if (uri != NULL) free(uri); if (escaped_uuid != NULL) curl_free(escaped_uuid); return rc; } /** * Clones a template info structure by making a deep copy of all strings and * arrays. * The copied template info must be freed using ekmf_free_template_info() by * the caller. * * @param src the source template info structure * @param dest the destination template info structure * * @returns zero for success, a negative errno in case of an error */ int ekmf_clone_template_info(const struct ekmf_template_info *src, struct ekmf_template_info **dest) { if (src == NULL || dest == NULL) return -EINVAL; *dest = calloc(1, sizeof(struct ekmf_template_info)); if (*dest == NULL) return -ENOMEM; return clone_template_info(src, *dest); } /** * Free a template info structure. * * @param template the template to free */ void ekmf_free_template_info(struct ekmf_template_info *template) { free_template_info(template); free(template); } /** * Gets the custom tags of a key by key-uuid. The custom tags are returned as * JSON array. The returned JSON array must be freed by the caller using * json_object_put(). */ static int _ekmf_get_custom_tags(const struct ekmf_config *config, const char *key_uuid, CURL *curl, json_object **custom_tags, const char *login_token, char **error_msg, bool verbose) { json_object *response_obj = NULL; char *escaped_uuid = NULL; char *uri = NULL; long status_code; int rc; if (config == NULL || key_uuid == NULL || custom_tags == NULL || curl == NULL) return -EINVAL; escaped_uuid = curl_easy_escape(curl, key_uuid, 0); if (escaped_uuid == NULL) { pr_verbose(verbose, "Failed to url-escape the key uuid"); rc = -EIO; goto out; } if (asprintf(&uri, EKMF_URI_KEYS_TAGS, escaped_uuid) < 0) { pr_verbose(verbose, "asprintf failed"); rc = -ENOMEM; goto out; } rc = _ekmf_perform_request(config, uri, "GET", NULL, NULL, login_token, &response_obj, NULL, &status_code, error_msg, curl, verbose); if (rc != 0) { pr_verbose(verbose, "Failed perform the REST call"); if (rc > 0) rc = -EIO; goto out; } switch (status_code) { case 200: break; case 400: pr_verbose(verbose, "Bad request"); rc = -EBADMSG; goto out; case 401: pr_verbose(verbose, "Not authorized"); rc = -EACCES; goto out; case 403: pr_verbose(verbose, "Insufficient permissions"); rc = -EPERM; goto out; default: pr_verbose(verbose, "REST Call failed with HTTP status code: " "%ld", status_code); rc = -EIO; goto out; } JSON_CHECK_OBJ(response_obj, json_type_array, rc, -EIO, "No or invalid response content", verbose, out); *custom_tags = response_obj; rc = 0; out: if (uri != NULL) free(uri); if (escaped_uuid != NULL) curl_free(escaped_uuid); if (rc != 0 && response_obj != NULL) json_object_put(response_obj); return rc; } /** * Gets the export control infos of a key by key-uuid. The export control info * is returned as JSON object. The returned JSON object must be freed by the * caller using json_object_put(). */ static int _ekmf_get_export_control(const struct ekmf_config *config, const char *key_uuid, CURL *curl, json_object **export_control, const char *login_token, char **error_msg, bool verbose) { json_object *response_obj = NULL; char *escaped_uuid = NULL; char *uri = NULL; long status_code; int rc; if (config == NULL || key_uuid == NULL || export_control == NULL || curl == NULL) return -EINVAL; escaped_uuid = curl_easy_escape(curl, key_uuid, 0); if (escaped_uuid == NULL) { pr_verbose(verbose, "Failed to url-escape the key uuid"); rc = -EIO; goto out; } if (asprintf(&uri, EKMF_URI_KEYS_EXPORT_CONTROL, escaped_uuid) < 0) { pr_verbose(verbose, "asprintf failed"); rc = -ENOMEM; goto out; } rc = _ekmf_perform_request(config, uri, "GET", NULL, NULL, login_token, &response_obj, NULL, &status_code, error_msg, curl, verbose); if (rc != 0) { pr_verbose(verbose, "Failed perform the REST call"); if (rc > 0) rc = -EIO; goto out; } switch (status_code) { case 200: break; case 400: pr_verbose(verbose, "Bad request"); rc = -EBADMSG; goto out; case 401: pr_verbose(verbose, "Not authorized"); rc = -EACCES; goto out; case 403: pr_verbose(verbose, "Insufficient permissions"); rc = -EPERM; goto out; default: pr_verbose(verbose, "REST Call failed with HTTP status code: " "%ld", status_code); rc = -EIO; goto out; } JSON_CHECK_OBJ(response_obj, json_type_object, rc, -EIO, "No or invalid response content", verbose, out); *export_control = response_obj; rc = 0; out: if (uri != NULL) free(uri); if (escaped_uuid != NULL) curl_free(escaped_uuid); if (rc != 0 && response_obj != NULL) json_object_put(response_obj); return rc; } /** * Get the custom tags for a key and build the key info structure */ static int _ekmf_build_key_info(const struct ekmf_config *config, CURL *curl, const char *login_token, json_object *obj, struct ekmf_key_info *key, bool copy, char **error_msg, bool verbose) { json_object *export_control = NULL; json_object *custom_tags = NULL; int rc; rc = _ekmf_get_custom_tags(config, json_get_string(obj, "keyId"), curl, &custom_tags, login_token, error_msg, verbose); if (rc != 0) { pr_verbose(verbose, "Failed to get the custom tags for key %s", json_get_string(obj, "keyId")); goto out; } rc = _ekmf_get_export_control(config, json_get_string(obj, "keyId"), curl, &export_control, login_token, error_msg, verbose); if (rc != 0) { pr_verbose(verbose, "Failed to get the custom tags for key %s", json_get_string(obj, "keyId")); goto out; } rc = json_build_key_info(obj, custom_tags, export_control, key, copy); if (rc != 0) { pr_verbose(verbose, "Failed to build key info"); goto out; } out: /* * Add custom tags and export control JSON objects to the key object, * so that these objects are also owned by the key object, and thus are * freed together with it, when the caller puts/frees the key object. */ if (custom_tags != NULL) json_object_object_add_ex(obj, "_custom_tags_", custom_tags, 0); if (export_control != NULL) json_object_object_add_ex(obj, "_export_control_", export_control, 0); return rc; } struct ekmf_key_cb_data_t { const struct ekmf_config *config; const char *login_token; char **error_msg; ekmf_key_cb_t key_cb; void *cb_private; }; /** * Callback for key list function. Builds the key info structure * and calls the application callback. */ static int _ekmf_key_cb(CURL *curl, json_object *element, void *private, bool verbose) { struct ekmf_key_cb_data_t *cb_data = private; struct ekmf_key_info key = { 0 }; int rc; if (cb_data->key_cb == NULL) { pr_verbose(verbose, "No key callback function"); return -EINVAL; } rc = _ekmf_build_key_info(cb_data->config, curl, cb_data->login_token, element, &key, false, cb_data->error_msg, verbose); if (rc != 0) { pr_verbose(verbose, "Failed to build key info"); goto out; } rc = cb_data->key_cb(curl, &key, cb_data->cb_private); if (rc != 0) { pr_verbose(verbose, "Key callback rc: %d", rc); goto out; } out: free_tag_list(&key.label_tags, false); free_tag_list(&key.custom_tags, false); free_export_control(&key.export_control, false); return rc; } /** * Builds the state URL parameter(s) from a comma separated list of states * * @param curl the curl handle * @param states a comma separaed list of states * * @returns an allocated URL parameter value, or NULL in case of an error */ static char *_ekmf_build_state_filter(CURL *curl, const char *states) { char *list, *tok, *ret = NULL, *tmp; char *escaped_state; if (states == NULL) goto error; list = strdup(states); if (list == NULL) goto error; tok = strtok(list, ","); while (tok != NULL) { escaped_state = curl_easy_escape(curl, tok, 0); if (escaped_state == NULL) goto error; if (asprintf(&tmp, "%s%s%s", ret != NULL ? ret : "", ret == NULL ? "" : EKMF_URI_KEYS_LIST_STATE, escaped_state) < 0) tmp = NULL; curl_free(escaped_state); if (tmp == NULL) goto error; if (ret != NULL) free(ret); ret = tmp; tok = strtok(NULL, ","); } free(list); return ret; error: if (ret != NULL) free(ret); return NULL; } /** * List available keys. The keys are ordered by name in ascending order. * * To perform a single request, set curl_handle to NULL. This will cause the * function to initialize a new CURL handle, use it, and destroy it. * If you plan to perform multiple requests to the same host, supply the address * of a CURL pointer that is initially NULL. This function will then initialize * a new CURL handle on the first call. On subsequent calls, pass in the address * of the same CURL pointer so that the CURL handle is reused. After the last * request, the CURL handle must be destroyed by calling ekmf_curl_destroy). * * @param config the configuration structure * @param curl_handle address of a CURL handle used for reusing the same * CURL handle with multiple requests. * @param key_cb a callback function that is called for each key * found * @param private a pointer that is passed as-is to the callback * @param name_pattern a pattern to filter by name, or NULL to list all. * @param states the states of the keys to list, or NULL to list keys * in ACTIVE state only. Multiple states can be * specified separated by comma. * @param tags a list of custom tags to use as filter, or NULL * @param error_msg on return: If not NULL, then a textual error message * is returned in case of a failing request. The caller * must free the error string when it is not NULL. * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. * -EACCES is returned, if no or no valid login token is available. * -EPERM is returned if the login token does not have permission to * list the keys */ int ekmf_list_keys(const struct ekmf_config *config, CURL **curl_handle, ekmf_key_cb_t key_cb, void *private, const char *name_pattern, const char *states, const struct ekmf_tag_list *tags, char **error_msg, bool verbose) { struct ekmf_key_cb_data_t cb_data; char *escaped_name_pattern = NULL; json_object *tags_obj = NULL; char *state_filter = NULL; char *escaped_tags = NULL; char *login_token = NULL; bool token_valid = false; CURL *curl = NULL; char *uri = NULL; size_t i; int rc; if (config == NULL || key_cb == NULL) return -EINVAL; rc = ekmf_check_login_token(config, &token_valid, &login_token, verbose); if (rc != 0 || !token_valid) { pr_verbose(verbose, "No valid login token available"); rc = -EACCES; goto out; } rc = _ekmf_get_curl_handle(curl_handle, &curl); if (rc != 0) { pr_verbose(verbose, "Failed to get CURL handle"); rc = -EIO; goto out; } cb_data.config = config; cb_data.login_token = login_token; cb_data.error_msg = error_msg; cb_data.key_cb = key_cb; cb_data.cb_private = private; escaped_name_pattern = curl_easy_escape(curl, name_pattern != NULL ? name_pattern : "*", 0); if (escaped_name_pattern == NULL) { pr_verbose(verbose, "Failed to url-escape the name pattern"); rc = -EIO; goto out; } state_filter = _ekmf_build_state_filter(curl, states != NULL ? states : KEY_STATE_ACTIVE); if (state_filter == NULL) { pr_verbose(verbose, "Failed to build the state filter"); rc = -EIO; goto out; } tags_obj = json_object_new_object(); JSON_CHECK_ERROR(tags_obj == NULL, rc, -ENOMEM, "Failed to generate JSON object", verbose, out); for (i = 0; tags != NULL && i < tags->num_tags; i++) { if (tags->tags[i].name == NULL || tags->tags[i].value == NULL) { rc = -EINVAL; goto out; } rc = json_object_object_add_ex(tags_obj, tags->tags[i].name, json_object_new_string( tags->tags[i].value), 0); JSON_CHECK_ERROR(rc != 0, rc, -EIO, "Failed to add data to " "JSON object", verbose, out); } escaped_tags = curl_easy_escape(curl, json_object_to_json_string_ext( tags_obj, JSON_C_TO_STRING_PLAIN | JSON_C_TO_STRING_NOSLASHESCAPE), 0); if (escaped_tags == NULL) { pr_verbose(verbose, "Failed to url-escape the tags"); rc = -EIO; goto out; } if (asprintf(&uri, EKMF_URI_KEYS_LIST, state_filter, ORDER_BY_LABEL_ASC, escaped_name_pattern, escaped_tags) < 0) { pr_verbose(verbose, "asprintf failed"); rc = -ENOMEM; goto out; } rc = _ekmf_list_request(config, uri, curl, _ekmf_key_cb, &cb_data, login_token, error_msg, verbose); if (rc != 0) { pr_verbose(verbose, "Failed to perform the list request"); if (rc > 0) rc = -EIO; goto out; } out: _ekmf_release_curl_handle(curl_handle, curl); if (login_token != NULL) free(login_token); if (uri != NULL) free(uri); if (state_filter != NULL) free(state_filter); if (escaped_name_pattern != NULL) curl_free(escaped_name_pattern); if (escaped_tags != NULL) curl_free(escaped_tags); if (tags_obj != NULL) json_object_put(tags_obj); return rc; } /** * Get information about a key by its UUID. * * To perform a single request, set curl_handle to NULL. This will cause the * function to initialize a new CURL handle, use it, and destroy it. * If you plan to perform multiple requests to the same host, supply the address * of a CURL pointer that is initially NULL. This function will then initialize * a new CURL handle on the first call. On subsequent calls, pass in the address * of the same CURL pointer so that the CURL handle is reused. After the last * request, the CURL handle must be destroyed by calling ekmf_curl_destroy). * * @param config the configuration structure * @param curl_handle address of a CURL handle used for reusing the same * CURL handle with multiple requests. * @param key_uuid the UUID of the key to get info for * @param key an address of a key info pointer. On return * the pointer is updated to point to a newly allocated * key info struct. It must be freed by the caller * using ekmf_free_key_info when no longer needed. * @param error_msg on return: If not NULL, then a textual error message * is returned in case of a failing request. The caller * must free the error string when it is not NULL. * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. * -EACCES is returned, if no or no valid login token is available. * -EPERM is returned if the login token does not have permission to * get the key info */ int ekmf_get_key_info(const struct ekmf_config *config, CURL **curl_handle, const char *key_uuid, struct ekmf_key_info **key, char **error_msg, bool verbose) { json_object *response_obj = NULL; char *escaped_uuid = NULL; char *login_token = NULL; bool token_valid = false; CURL *curl = NULL; char *uri = NULL; long status_code; int rc; if (config == NULL || key_uuid == NULL || key == NULL) return -EINVAL; *key = NULL; rc = ekmf_check_login_token(config, &token_valid, &login_token, verbose); if (rc != 0 || !token_valid) { pr_verbose(verbose, "No valid login token available"); rc = -EACCES; goto out; } rc = _ekmf_get_curl_handle(curl_handle, &curl); if (rc != 0) { pr_verbose(verbose, "Failed to get CURL handle"); rc = -EIO; goto out; } escaped_uuid = curl_easy_escape(curl, key_uuid, 0); if (escaped_uuid == NULL) { pr_verbose(verbose, "Failed to url-escape the key uuid"); rc = -EIO; goto out; } if (asprintf(&uri, EKMF_URI_KEYS_GET, escaped_uuid) < 0) { pr_verbose(verbose, "asprintf failed"); rc = -ENOMEM; goto out; } rc = _ekmf_perform_request(config, uri, "GET", NULL, NULL, login_token, &response_obj, NULL, &status_code, error_msg, curl, verbose); if (rc != 0) { pr_verbose(verbose, "Failed perform the REST call"); if (rc > 0) rc = -EIO; goto out; } switch (status_code) { case 200: break; case 400: pr_verbose(verbose, "Bad request"); rc = -EBADMSG; goto out; case 401: pr_verbose(verbose, "Not authorized"); rc = -EACCES; goto out; case 403: pr_verbose(verbose, "Insufficient permissions"); rc = -EPERM; goto out; case 404: pr_verbose(verbose, "Not found"); rc = -ENOENT; goto out; default: pr_verbose(verbose, "REST Call failed with HTTP status code: " "%ld", status_code); rc = -EIO; goto out; } JSON_CHECK_OBJ(response_obj, json_type_object, rc, -EBADMSG, "No or invalid response", verbose, out); *key = calloc(1, sizeof(struct ekmf_key_info)); if (*key == NULL) { pr_verbose(verbose, "calloc failed"); rc = -ENOMEM; goto out; } rc = _ekmf_build_key_info(config, curl, login_token, response_obj, *key, true, error_msg, verbose); if (rc != 0) { pr_verbose(verbose, "Failed to build template info"); goto out; } out: _ekmf_release_curl_handle(curl_handle, curl); if (response_obj != NULL) json_object_put(response_obj); if (login_token != NULL) free(login_token); if (uri != NULL) free(uri); if (escaped_uuid != NULL) curl_free(escaped_uuid); if (rc != 0 && *key != NULL) { free_key_info(*key); free(*key); *key = NULL; } return rc; } /** * Changes the state of a key identified by its UUID. To update a key, * the timestamp from the last update is required. This can be found in * the key info struct in field update_on. * * To perform a single request, set curl_handle to NULL. This will cause the * function to initialize a new CURL handle, use it, and destroy it. * If you plan to perform multiple requests to the same host, supply the address * of a CURL pointer that is initially NULL. This function will then initialize * a new CURL handle on the first call. On subsequent calls, pass in the address * of the same CURL pointer so that the CURL handle is reused. After the last * request, the CURL handle must be destroyed by calling ekmf_curl_destroy). * * @param config the configuration structure * @param curl_handle address of a CURL handle used for reusing the same * CURL handle with multiple requests. * @param key_uuid the UUID of the key to get info for * @param new_state the new state of the key * @param updated_on the timestamp of the last update (must match) * @param error_msg on return: If not NULL, then a textual error message * is returned in case of a failing request. The caller * must free the error string when it is not NULL. * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. * -EACCES is returned, if no or no valid login token is available. * -EPERM is returned if the login token does not have permission to * update the key. * -EAGAIN is returned if the timestamp does not match, indicating that * the key has been updated in the meantime. */ int ekmf_set_key_state(const struct ekmf_config *config, CURL **curl_handle, const char *key_uuid, const char *new_state, const char *updated_on, char **error_msg, bool verbose) { char *request_headers[2] = { NULL, NULL }; json_object *request_obj = NULL; char *escaped_uuid = NULL; char *login_token = NULL; bool token_valid = false; char *if_match_hdr = NULL; CURL *curl = NULL; char *uri = NULL; long status_code; int rc; if (config == NULL || key_uuid == NULL || new_state == NULL || updated_on == NULL) return -EINVAL; rc = ekmf_check_login_token(config, &token_valid, &login_token, verbose); if (rc != 0 || !token_valid) { pr_verbose(verbose, "No valid login token available"); rc = -EACCES; goto out; } rc = _ekmf_get_curl_handle(curl_handle, &curl); if (rc != 0) { pr_verbose(verbose, "Failed to get CURL handle"); rc = -EIO; goto out; } request_obj = json_object_new_object(); JSON_CHECK_ERROR(request_obj == NULL, rc, -ENOMEM, "Failed to generate JSON object", verbose, out); rc = json_object_object_add_ex(request_obj, "state", json_object_new_string(new_state), 0); JSON_CHECK_ERROR(rc != 0, rc, -EIO, "Failed to add data to JSON object", verbose, out); escaped_uuid = curl_easy_escape(curl, key_uuid, 0); if (escaped_uuid == NULL) { pr_verbose(verbose, "Failed to url-escape the key uuid"); rc = -EIO; goto out; } if (asprintf(&uri, EKMF_URI_KEYS_SET_STATE, escaped_uuid) < 0) { pr_verbose(verbose, "asprintf failed"); rc = -ENOMEM; goto out; } if (asprintf(&if_match_hdr, "If-Match : %s", updated_on) < 0) { pr_verbose(verbose, "asprintf failed"); rc = -ENOMEM; goto out; } request_headers[0] = if_match_hdr; rc = _ekmf_perform_request(config, uri, "PATCH", request_obj, request_headers, login_token, NULL, NULL, &status_code, error_msg, curl, verbose); if (rc != 0) { pr_verbose(verbose, "Failed perform the REST call"); if (rc > 0) rc = -EIO; goto out; } switch (status_code) { case 204: break; case 400: pr_verbose(verbose, "Bad request"); rc = -EBADMSG; goto out; case 401: pr_verbose(verbose, "Not authorized"); rc = -EACCES; goto out; case 403: pr_verbose(verbose, "Insufficient permissions"); rc = -EPERM; goto out; case 404: pr_verbose(verbose, "Not found"); rc = -ENOENT; goto out; case 409: pr_verbose(verbose, "Key was updated in the meantime"); rc = -EAGAIN; goto out; default: pr_verbose(verbose, "REST Call failed with HTTP status code: " "%ld", status_code); rc = -EIO; goto out; } out: _ekmf_release_curl_handle(curl_handle, curl); if (request_obj != NULL) json_object_put(request_obj); if (login_token != NULL) free(login_token); if (uri != NULL) free(uri); if (escaped_uuid != NULL) curl_free(escaped_uuid); if (if_match_hdr != NULL) free(if_match_hdr); return rc; } /** * Sets (adds/changes) a custom tag of a key identified by its UUID. To update * a key, the timestamp from the last update is required. This can be found in * the key info struct in field update_on. * * @param config the configuration structure * @param curl the CURL handle * @param login_token the login token to authenticate * @param key_uuid the UUID of the key to get info for * @param tag the tag to set * @param updated_on the timestamp of the last update (must match) * @param delete if true, the tag is deleted, otherwise it is updated * @param etag On return: the new update timestamp returned via * the etag HTTP header. Must be freed by the caller. * @param error_msg on return: If not NULL, then a textual error message * is returned in case of a failing request. The caller * must free the error string when it is not NULL. * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. * -EACCES is returned, if no or no valid login token is available. * -EPERM is returned if the login token does not have permission to * update the key. * -EAGAIN is returned if the timestamp does not match, indicating that * the key has been updated in the meantime. */ static int _ekmf_set_key_tag(const struct ekmf_config *config, CURL *curl, const char *login_token, const char *key_uuid, const struct ekmf_tag *tag, const char *updated_on, bool delete, char **etag, char **error_msg, bool verbose) { struct curl_slist *response_headers = NULL; char *request_headers[2] = { NULL, NULL }; json_object *request_obj = NULL; char *escaped_tag_name = NULL; char *escaped_uuid = NULL; char *if_match_hdr = NULL; char *uri = NULL; long status_code; int rc; if (config == NULL || curl == NULL || login_token == NULL || key_uuid == NULL || tag == NULL || updated_on == NULL || etag == NULL) return -EINVAL; *etag = NULL; if (!delete) { request_obj = json_object_new_object(); JSON_CHECK_ERROR(request_obj == NULL, rc, -ENOMEM, "Failed to generate JSON object", verbose, out); rc = json_object_object_add_ex(request_obj, "value", json_object_new_string(tag->value), 0); JSON_CHECK_ERROR(rc != 0, rc, -EIO, "Failed to add data to " "JSON object", verbose, out); } escaped_uuid = curl_easy_escape(curl, key_uuid, 0); if (escaped_uuid == NULL) { pr_verbose(verbose, "Failed to url-escape the key uuid"); rc = -EIO; goto out; } escaped_tag_name = curl_easy_escape(curl, tag->name, 0); if (escaped_tag_name == NULL) { pr_verbose(verbose, "Failed to url-escape the tag name"); rc = -EIO; goto out; } if (asprintf(&uri, EKMF_URI_KEYS_SET_TAG, escaped_uuid, escaped_tag_name) < 0) { pr_verbose(verbose, "asprintf failed"); rc = -ENOMEM; goto out; } if (asprintf(&if_match_hdr, "If-Match : %s", updated_on) < 0) { pr_verbose(verbose, "asprintf failed"); rc = -ENOMEM; goto out; } request_headers[0] = if_match_hdr; rc = _ekmf_perform_request(config, uri, delete ? "DELETE" : "PUT", request_obj, request_headers, login_token, NULL, &response_headers, &status_code, error_msg, curl, verbose); if (rc != 0) { pr_verbose(verbose, "Failed perform the REST call"); if (rc > 0) rc = -EIO; goto out; } switch (status_code) { case 200: case 204: break; case 400: pr_verbose(verbose, "Bad request"); rc = -EBADMSG; goto out; case 401: pr_verbose(verbose, "Not authorized"); rc = -EACCES; goto out; case 403: pr_verbose(verbose, "Insufficient permissions"); rc = -EPERM; goto out; case 404: pr_verbose(verbose, "Not found"); rc = -ENOENT; goto out; case 409: pr_verbose(verbose, "Key was updated in the meantime"); rc = -EAGAIN; goto out; default: pr_verbose(verbose, "REST Call failed with HTTP status code: " "%ld", status_code); rc = -EIO; goto out; } *etag = get_http_header_value(response_headers, "Etag"); if (*etag == NULL) { pr_verbose(verbose, "No ETag in response headers"); rc = -EBADMSG; goto out; } out: if (request_obj != NULL) json_object_put(request_obj); if (uri != NULL) free(uri); if (escaped_uuid != NULL) curl_free(escaped_uuid); if (escaped_tag_name != NULL) curl_free(escaped_tag_name); if (if_match_hdr != NULL) free(if_match_hdr); if (response_headers != NULL) curl_slist_free_all(response_headers); return rc; } /** * Sets (changed/adds) custom tags of a key identified by its UUID. To update a * key, the timestamp from the last update is required. This can be found in * the key info struct in field update_on. * * To perform a single request, set curl_handle to NULL. This will cause the * function to initialize a new CURL handle, use it, and destroy it. * If you plan to perform multiple requests to the same host, supply the address * of a CURL pointer that is initially NULL. This function will then initialize * a new CURL handle on the first call. On subsequent calls, pass in the address * of the same CURL pointer so that the CURL handle is reused. After the last * request, the CURL handle must be destroyed by calling ekmf_curl_destroy). * * @param config the configuration structure * @param curl_handle address of a CURL handle used for reusing the same * CURL handle with multiple requests. * @param key_uuid the UUID of the key to get info for * @param tags a list of tags to set * @param updated_on the timestamp of the last update (must match) * @param new_updated_on on return: if not NULL, the new timestamp of the * current update. Can be used for subsequent updates * on the key. * @param error_msg on return: If not NULL, then a textual error message * is returned in case of a failing request. The caller * must free the error string when it is not NULL. * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. * -EACCES is returned, if no or no valid login token is available. * -EPERM is returned if the login token does not have permission to * update the key. * -EAGAIN is returned if the timestamp does not match, indicating that * the key has been updated in the meantime. */ int ekmf_set_key_tags(const struct ekmf_config *config, CURL **curl_handle, const char *key_uuid, const struct ekmf_tag_list *tags, const char *updated_on, char **new_updated_on, char **error_msg, bool verbose) { char *update_ts = (char *)updated_on; char *login_token = NULL; bool token_valid = false; CURL *curl = NULL; char *etag = NULL; size_t i; int rc; if (config == NULL || key_uuid == NULL || tags == NULL || updated_on == NULL) return -EINVAL; rc = ekmf_check_login_token(config, &token_valid, &login_token, verbose); if (rc != 0 || !token_valid) { pr_verbose(verbose, "No valid login token available"); rc = -EACCES; goto out; } rc = _ekmf_get_curl_handle(curl_handle, &curl); if (rc != 0) { pr_verbose(verbose, "Failed to get CURL handle"); rc = -EIO; goto out; } for (i = 0; i < tags->num_tags; i++) { rc = _ekmf_set_key_tag(config, curl, login_token, key_uuid, &tags->tags[i], update_ts, false, &etag, error_msg, verbose); if (rc != 0) { pr_verbose(verbose, "Failed to set tag '%s'", tags->tags[i].name); goto out; } if (update_ts != NULL && update_ts != updated_on) free(update_ts); update_ts = etag; etag = NULL; } if (new_updated_on != NULL) *new_updated_on = strdup(update_ts); out: _ekmf_release_curl_handle(curl_handle, curl); if (login_token != NULL) free(login_token); if (update_ts != NULL && update_ts != updated_on) free(update_ts); if (etag != NULL) free(etag); return rc; } /** * Deletes custom tags of a key identified by its UUID. To update a * key, the timestamp from the last update is required. This can be found in * the key info struct in field update_on. * * To perform a single request, set curl_handle to NULL. This will cause the * function to initialize a new CURL handle, use it, and destroy it. * If you plan to perform multiple requests to the same host, supply the address * of a CURL pointer that is initially NULL. This function will then initialize * a new CURL handle on the first call. On subsequent calls, pass in the address * of the same CURL pointer so that the CURL handle is reused. After the last * request, the CURL handle must be destroyed by calling ekmf_curl_destroy). * * @param config the configuration structure * @param curl_handle address of a CURL handle used for reusing the same * CURL handle with multiple requests. * @param key_uuid the UUID of the key to get info for * @param tags a list of tags to delete. Only the name of the tags * must be present in the tag structs of the list, the * values are ignored. * @param updated_on the timestamp of the last update (must match) * @param new_updated_on on return: if not NULL, the new timestamp of the * current update. Can be used for subsequent updates * on the key. * @param error_msg on return: If not NULL, then a textual error message * is returned in case of a failing request. The caller * must free the error string when it is not NULL. * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. * -EACCES is returned, if no or no valid login token is available. * -EPERM is returned if the login token does not have permission to * update the key. * -EAGAIN is returned if the timestamp does not match, indicating that * the key has been updated in the meantime. */ int ekmf_delete_key_tags(const struct ekmf_config *config, CURL **curl_handle, const char *key_uuid, const struct ekmf_tag_list *tags, const char *updated_on, char **new_updated_on, char **error_msg, bool verbose) { char *update_ts = (char *)updated_on; char *login_token = NULL; bool token_valid = false; CURL *curl = NULL; char *etag = NULL; size_t i; int rc; if (config == NULL || key_uuid == NULL || tags == NULL || updated_on == NULL) return -EINVAL; rc = ekmf_check_login_token(config, &token_valid, &login_token, verbose); if (rc != 0 || !token_valid) { pr_verbose(verbose, "No valid login token available"); rc = -EACCES; goto out; } rc = _ekmf_get_curl_handle(curl_handle, &curl); if (rc != 0) { pr_verbose(verbose, "Failed to get CURL handle"); rc = -EIO; goto out; } for (i = 0; i < tags->num_tags; i++) { rc = _ekmf_set_key_tag(config, curl, login_token, key_uuid, &tags->tags[i], update_ts, true, &etag, error_msg, verbose); if (rc != 0) { pr_verbose(verbose, "Failed to delete tag '%s'", tags->tags[i].name); goto out; } if (update_ts != NULL && update_ts != updated_on) free(update_ts); update_ts = etag; etag = NULL; } if (new_updated_on != NULL) *new_updated_on = strdup(update_ts); out: _ekmf_release_curl_handle(curl_handle, curl); if (login_token != NULL) free(login_token); if (update_ts != NULL && update_ts != updated_on) free(update_ts); if (etag != NULL) free(etag); return rc; } /** * Clones a key info structure by making a deep copy of all strings and * arrays. * The copied key info must be freed using ekmf_free_key_info() by * the caller. * * @param src the source key info structure * @param dest the destination key info structure * * @returns zero for success, a negative errno in case of an error */ int ekmf_clone_key_info(const struct ekmf_key_info *src, struct ekmf_key_info **dest) { if (src == NULL || dest == NULL) return -EINVAL; *dest = calloc(1, sizeof(struct ekmf_key_info)); if (*dest == NULL) return -ENOMEM; return clone_key_info(src, *dest); } /** * Free a key info structure. * * @param key the key info to free */ void ekmf_free_key_info(struct ekmf_key_info *key) { free_key_info(key); free(key); } /** * Build the export control JSON object. * * @param exporting_key the key to add as exporting key * @param expctl_obj on return: the export control JSON object * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. */ static int _ekmf_build_export_control(const char *exporting_key, json_object **expctl_obj, bool verbose) { json_object *exp_keys = NULL; json_object *exp_ref = NULL; char *href = NULL; int rc = 0; *expctl_obj = json_object_new_object(); JSON_CHECK_ERROR(*expctl_obj == NULL, rc, -ENOMEM, "Failed to generate JSON object", verbose, out); rc = json_object_object_add_ex(*expctl_obj, "exportAllowed", json_object_new_boolean(true), 0); JSON_CHECK_ERROR(rc != 0, rc, -EIO, "Failed to add data to " "JSON object", verbose, out); exp_keys = json_object_new_array(); JSON_CHECK_ERROR(exp_keys == NULL, rc, -ENOMEM, "Failed to generate JSON object", verbose, out); exp_ref = json_object_new_object(); JSON_CHECK_ERROR(exp_ref == NULL, rc, -ENOMEM, "Failed to generate JSON object", verbose, out); rc = json_object_object_add_ex(exp_ref, "rel", json_object_new_string("exportAllowedWithKey"), 0); JSON_CHECK_ERROR(rc != 0, rc, -EIO, "Failed to add data to " "JSON object", verbose, out); JSON_CHECK_ERROR(asprintf(&href, "/keys/%s", exporting_key) < 0, rc, -ENOMEM, "Failed to allocate string", verbose, out); rc = json_object_object_add_ex(exp_ref, "href", json_object_new_string(href), 0); JSON_CHECK_ERROR(rc != 0, rc, -EIO, "Failed to add data to " "JSON object", verbose, out); rc = json_object_array_add(exp_keys, exp_ref); JSON_CHECK_ERROR(rc != 0, rc, -EIO, "Failed to add data to " "JSON object", verbose, out); exp_ref = NULL; rc = json_object_object_add_ex(*expctl_obj, "allowedKeys", exp_keys, 0); JSON_CHECK_ERROR(rc != 0, rc, -EIO, "Failed to add data to " "JSON object", verbose, out); exp_keys = NULL; out: if (exp_keys != NULL) json_object_put(exp_keys); if (exp_ref != NULL) json_object_put(exp_ref); if (href != NULL) free(href); if (rc != 0 && *expctl_obj != NULL) { json_object_put(*expctl_obj); *expctl_obj = NULL; } return rc; } /** * Base64-encodes the data * * @param data the data to encode * @param data_size the size of the data in bytes * * @returns the encoded data or NULL in case of an error. * The caller must free the string when no longer needed. */ static char *_ekmf_base64_encode(const unsigned char *data, size_t data_size) { int outlen, len; char *out; outlen = (data_size / 3) * 4; if (data_size % 3 > 0) outlen += 4; out = calloc(outlen + 1, 1); if (out == NULL) return NULL; len = EVP_EncodeBlock((unsigned char *)out, data, data_size); if (len != outlen) { free(out); return NULL; } out[outlen] = '\0'; return out; } /** * Build the key material JSON object * * @param certificate the certificate to generate an identity key from * @param certificate_size the size of the certificate * @param keymat_obj on return: the key material JSON object * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. */ static int _ekmf_build_key_material(const unsigned char *certificate, size_t certificate_size, json_object **keymat_obj, bool verbose) { char *payload = NULL; int rc = 0; *keymat_obj = json_object_new_object(); JSON_CHECK_ERROR(*keymat_obj == NULL, rc, -ENOMEM, "Failed to generate JSON object", verbose, out); rc = json_object_object_add_ex(*keymat_obj, "type", json_object_new_string("ENCODED-CERTIFICATE"), 0); JSON_CHECK_ERROR(rc != 0, rc, -EIO, "Failed to add data to " "JSON object", verbose, out); payload = _ekmf_base64_encode(certificate, certificate_size); JSON_CHECK_ERROR(*keymat_obj == NULL, rc, -EIO, "Failed to base64 encode the certificate", verbose, out); rc = json_object_object_add_ex(*keymat_obj, "payload", json_object_new_string(payload), 0); JSON_CHECK_ERROR(rc != 0, rc, -EIO, "Failed to add data to " "JSON object", verbose, out); out: if (rc != 0 && *keymat_obj != NULL) { json_object_put(*keymat_obj); *keymat_obj = NULL; } if (payload != NULL) free(payload); return rc; } /** * Generates a new key in EKMFWeb * * To perform a single request, set curl_handle to NULL. This will cause the * function to initialize a new CURL handle, use it, and destroy it. * If you plan to perform multiple requests to the same host, supply the address * of a CURL pointer that is initially NULL. This function will then initialize * a new CURL handle on the first call. On subsequent calls, pass in the address * of the same CURL pointer so that the CURL handle is reused. After the last * request, the CURL handle must be destroyed by calling ekmf_curl_destroy). * * @param config the configuration structure * @param curl_handle address of a CURL handle used for reusing the same * CURL handle with multiple requests. * @param template the name of the template to generate the key with * @param description Optional: a textual description of the key (can be * NULL) * @param label_tags list of label tags. The label tags are required as * defined in the template * @param custom_tags Optional: list of custom tags (can be NULL) * @param exporting_key Optional: The uuid of the key that is allowed to * export the newly generated key (can be NULL). * @param certificate Optional: The certificate to generate an identity * key from. Should be NULL for generating AES keys. * @param certificate_size Optional: the size of the certificate. Required if * certificate is not NULL. * @param key_info Optional: On return: If not NULL, a key info struct * is returned here containing key information. This * must be freed by the caller with ekmf_free_key_info * when no longer needed. * @param error_msg on return: If not NULL, then a textual error message * is returned in case of a failing request. The caller * must free the error string when it is not NULL. * @param verbose if true, verbose messages are printed * * @returns zero for success, a negative errno in case of an error. * -EACCES is returned, if no or no valid login token is available. * -EPERM is returned if the login token does not have permission to * generate keys */ int ekmf_generate_key(const struct ekmf_config *config, CURL **curl_handle, const char *template, const char *description, const struct ekmf_tag_list *label_tags, const struct ekmf_tag_list *custom_tags, const char *exporting_key, const unsigned char *certificate, size_t certificate_size, struct ekmf_key_info **key_info, char **error_msg, bool verbose) { json_object *response_obj = NULL; json_object *request_obj = NULL; json_object *expctl_obj = NULL; json_object *keymat_obj = NULL; json_object *tags_obj = NULL; char *login_token = NULL; bool token_valid = false; CURL *curl = NULL; long status_code; int rc; if (config == NULL || template == NULL || label_tags == NULL || label_tags->num_tags == 0 || label_tags->tags == NULL) return -EINVAL; if (custom_tags != NULL && custom_tags->num_tags > 0 && custom_tags->tags == NULL) return -EINVAL; if (certificate != NULL && certificate_size == 0) return -EINVAL; if (key_info != NULL) *key_info = NULL; rc = ekmf_check_login_token(config, &token_valid, &login_token, verbose); if (rc != 0 || !token_valid) { pr_verbose(verbose, "No valid login token available"); rc = -EACCES; goto out; } rc = _ekmf_get_curl_handle(curl_handle, &curl); if (rc != 0) { pr_verbose(verbose, "Failed to get CURL handle"); rc = -EIO; goto out; } request_obj = json_object_new_object(); JSON_CHECK_ERROR(request_obj == NULL, rc, -ENOMEM, "Failed to generate JSON object", verbose, out); rc = json_object_object_add_ex(request_obj, "templateName", json_object_new_string(template), 0); JSON_CHECK_ERROR(rc != 0, rc, -EIO, "Failed to add data to JSON object", verbose, out); rc = build_json_tag_list(label_tags, &tags_obj); if (rc != 0) { pr_verbose(verbose, "Failed to build label tag JSON object"); goto out; } rc = json_object_object_add_ex(request_obj, "labelTags", tags_obj, 0); JSON_CHECK_ERROR(rc != 0, rc, -EIO, "Failed to add data to JSON object", verbose, out); tags_obj = NULL; if (description != NULL) { rc = json_object_object_add_ex(request_obj, "description", json_object_new_string(description), 0); JSON_CHECK_ERROR(rc != 0, rc, -EIO, "Failed to add data to " "JSON object", verbose, out); } if (certificate != NULL) { rc = _ekmf_build_key_material(certificate, certificate_size, &keymat_obj, verbose); if (rc != 0) goto out; rc = json_object_object_add_ex(request_obj, "keyMaterial", keymat_obj, 0); JSON_CHECK_ERROR(rc != 0, rc, -EIO, "Failed to add data to JSON object", verbose, out); keymat_obj = NULL; } if (custom_tags != NULL && custom_tags->num_tags > 0) { rc = build_json_tag_list(custom_tags, &tags_obj); if (rc != 0) { pr_verbose(verbose, "Failed to build custom tag JSON " "object"); goto out; } rc = json_object_object_add_ex(request_obj, "customTags", tags_obj, 0); JSON_CHECK_ERROR(rc != 0, rc, -EIO, "Failed to add data to " "JSON object", verbose, out); tags_obj = NULL; } if (exporting_key != NULL) { rc = _ekmf_build_export_control(exporting_key, &expctl_obj, verbose); if (rc != 0) goto out; rc = json_object_object_add_ex(request_obj, "exportControl", expctl_obj, 0); JSON_CHECK_ERROR(rc != 0, rc, -EIO, "Failed to add data to JSON object", verbose, out); expctl_obj = NULL; } rc = _ekmf_perform_request(config, EKMF_URI_KEYS_GENERATE, "POST", request_obj, NULL, login_token, &response_obj, NULL, &status_code, error_msg, curl, verbose); if (rc != 0) { pr_verbose(verbose, "Failed perform the REST call"); if (rc > 0) rc = -EIO; goto out; } switch (status_code) { case 201: break; case 400: pr_verbose(verbose, "Bad request"); rc = -EBADMSG; goto out; case 401: pr_verbose(verbose, "Not authorized"); rc = -EACCES; goto out; case 403: pr_verbose(verbose, "Insufficient permissions"); rc = -EPERM; goto out; case 409: pr_verbose(verbose, "A key with this label exist already"); rc = -EEXIST; goto out; default: pr_verbose(verbose, "REST Call failed with HTTP status code: " "%ld", status_code); rc = -EIO; goto out; } JSON_CHECK_OBJ(response_obj, json_type_object, rc, -EBADMSG, "No or invalid response", verbose, out); if (key_info != NULL) { *key_info = calloc(1, sizeof(struct ekmf_key_info)); if (*key_info == NULL) { pr_verbose(verbose, "calloc failed"); rc = -ENOMEM; goto out; } rc = _ekmf_build_key_info(config, curl, login_token, response_obj, *key_info, true, error_msg, verbose); if (rc != 0) { pr_verbose(verbose, "Failed to build the key info"); goto out; } } out: _ekmf_release_curl_handle(curl_handle, curl); if (request_obj != NULL) json_object_put(request_obj); if (response_obj != NULL) json_object_put(response_obj); if (login_token != NULL) free(login_token); if (tags_obj != NULL) json_object_put(tags_obj); if (expctl_obj != NULL) json_object_put(expctl_obj); if (keymat_obj != NULL) json_object_put(keymat_obj); if (rc != 0 && key_info != NULL && *key_info != NULL) { free(*key_info); *key_info = NULL; } return rc; } /** * Generate a secure identity key used to identify the client to EKMFWeb. * The secure key blob is stored in a file specified in field * identity_secure_key of the config structure. If an secure key already exists * at that location, it is overwritten. * * @param config the configuration structure. Only field * identity_secure_key must be specified, all others * are optional. * @param info key generation info, such as key type (ECC or RSA) * and key parameters. * @param ext_lib External secure key crypto library to use * @param verbose if true, verbose messages are printed * * @returns a negative errno in case of an error, 0 if success. */ int ekmf_generate_identity_key(const struct ekmf_config *config, const struct ekmf_key_gen_info *info, const struct ekmf_ext_lib *ext_lib, bool verbose) { unsigned char key_blob[MAX_KEY_BLOB_SIZE]; size_t key_blob_size = sizeof(key_blob); struct ext_lib_info ext_lib_info; struct sk_key_gen_info gen_info; int rc; if (config == NULL || info == NULL || ext_lib == NULL) return -EINVAL; if (config->identity_secure_key == NULL) return -EINVAL; _ekmf_copy_ext_lib(ext_lib, &ext_lib_info); _ekmf_copy_key_gen_info(info, &gen_info); rc = SK_OPENSSL_generate_secure_key(key_blob, &key_blob_size, &gen_info, &ext_lib_info.ext_lib, verbose); if (rc != 0) { pr_verbose(verbose, "Failed to generate a key: rc: %d - %s", rc, strerror(-rc)); return rc; } rc = write_key_blob(config->identity_secure_key, key_blob, key_blob_size); if (rc != 0) { pr_verbose(verbose, "Failed to write the key to file '%s' " "rc: %d - %s", config->identity_secure_key, rc, strerror(-rc)); return rc; } pr_verbose(verbose, "Secure identity key generated (%lu bytes) " "and written to file '%s'", key_blob_size, config->identity_secure_key); return 0; } /** * Re-encipher the secure identity key (form field identity_secure_key in * config) used to identify the client to EKMFWeb. * The secure key blob is encrypted using the HSM master key. Whenever the HSM * master key is being changed, the secure identity key must be re-enciphered. * You can either pro-actively re-encipher a secure key once the new master key * has been prepared (but not yet made active): to_new = true; or you can * re-encipher a secure key when the HSM master key has already been changed: * to_new = false. This requires that the HSM still has the old master key. * Not all HSMs support this. * * For pro-active re-encipherment it is suggested to store the re-enciphered * secure key on a separate place, until the new HSM master key has been made * active. Specify a file name in reenc_secure_key to do so. For an in-place * re-encipherment, set reenc_secure_key = NULL. * * @param config the configuration structure. Only field * identity_secure_key must be specified, all others * are optional. * @param to_new If true: the identity key is re-enciphered from the * current to the new master key. * If false: the identity key is re-enciphered from the * old to the current master key. * @param reenc_secure_key if not NULL, then the re-enciphered secure key is * stored into the filename specified here. Otherwise * the re-enciphered secure key replaces the original * secure identity key. * @param ext_lib External secure key crypto library to use * @param verbose if true, verbose messages are printed * * @returns a negative errno in case of an error, 0 if success. * A -ENODEV indicates that the master keys are not loaded. */ int ekmf_reencipher_identity_key(const struct ekmf_config *config, bool to_new, const char *reenc_secure_key, const struct ekmf_ext_lib *ext_lib, bool verbose) { unsigned char key_blob[MAX_KEY_BLOB_SIZE]; size_t key_blob_size = sizeof(key_blob); struct ext_lib_info ext_lib_info; const char *out_file; int rc; if (config == NULL || ext_lib == NULL) return -EINVAL; if (config->identity_secure_key == NULL) return -EINVAL; _ekmf_copy_ext_lib(ext_lib, &ext_lib_info); rc = read_key_blob(config->identity_secure_key, key_blob, &key_blob_size); if (rc != 0) { pr_verbose(verbose, "Failed to read identity key from file " "'%s': %s", config->identity_secure_key, strerror(-rc)); return rc; } rc = SK_OPENSSL_reencipher_secure_key(key_blob, key_blob_size, to_new, &ext_lib_info.ext_lib, verbose); if (rc != 0) { pr_verbose(verbose, "Failed to re-encipher the secure identity " "key from file '%s': %s", config->identity_secure_key, strerror(-rc)); return rc; } out_file = reenc_secure_key != NULL ? reenc_secure_key : config->identity_secure_key; rc = write_key_blob(out_file, key_blob, key_blob_size); if (rc != 0) { pr_verbose(verbose, "Failed to write identity key to file " "'%s': %s", out_file, strerror(-rc)); return rc; } return 0; } /** * Generate a certificate signing request using the secure identity key (field * identity_secure_key in config structure) with the specified subject name, * certificate extensions (if any), and writes the CSR to the specified file * in PEM format. * * To renew an existing certificate, specify renew_cert = true. In this case * the existing certificate (field sign_certificate in config struct) is read, * and the subject name is extracted from it. Any specified subject name RDNs * are added to the CSR. Also, the extensions are taken from the existing * certificate, and any specified extensions are added to the CSR. * * The CSR is signed using the secure identity key (field identity_secure_key in * config structure) with an signing algorithm matching the identity key (ECDSA, * RSA-PKCS, or RSA-PSS if rsa_pss is true), and the specified digest. If the * digest nid is zero, then a default digest is used. * * @param config the configuration structure. Only field * identity_secure_key must be specified, all others * are optional. * @param subject_rdns an array of strings, each string representing an * RDN in the form '[+]type=value'. If the type is * prepended with a '+', then this RDN is added to the * previous one. * @param num_subject_rdns number of RDN elements in the array. * @param subject_utf8 if true, RDNs of type MBSTRING_UTF8 are created, * otherwise type is MBSTRING_ASC is used. * @param renew_cert_filename if not NULL, specifies the file name of a PEM file * containing an existing certificate that is renewed * @param extensions an array of strings, each string representing an * certificate extension in the form 'type=value'. * @param num_extensions number of extension elements in the array. * @param digest_nid the OpenSSL digest nid to use with the signature * algorithm, or 0 to use the default * @param rsa_pss_params if not NULL and the identity key is an RSA key, then * the CSR is signed with RSA-PSS using the specified * PSS parameters. Ignored if the identity key is an EC * key * @param csr_pem_filename the name of the PEM file to which the CSR is written * @param new_hdr if true, output "NEW" in the PEM header lines * @param ext_lib External secure key crypto library to use * @param verbose if true, verbose messages are printed * * @returns a negative errno in case of an error, 0 if success: * -EINVAL: invalid parameter * -ENOMEM: Failed to allocate memory * -EBADMSG: an RDN or extension is not formatted correctly * -EIO: OpenSSL failed to create the CSR * -EEXIST: if one of the RDN name entries or extensions to add is a * duplicate * -ENOTSUP: the specified digest is not supported * any other errno from file I/O routines */ int ekmf_generate_csr(const struct ekmf_config *config, const char *subject_rdns[], size_t num_subject_rdns, bool subject_utf8, const char *renew_cert_filename, const char *extensions[], size_t num_extensions, int digest_nid, struct ekmf_rsa_pss_params *rsa_pss_params, const char *csr_pem_filename, bool new_hdr, const struct ekmf_ext_lib *ext_lib, bool verbose) { unsigned char key_blob[MAX_KEY_BLOB_SIZE]; size_t key_blob_size = sizeof(key_blob); struct sk_rsa_pss_params pss_params; struct ext_lib_info ext_lib_info; X509_REQ *req = NULL; X509 *cert = NULL; int rc; if (config == NULL || ext_lib == NULL || csr_pem_filename == NULL) return -EINVAL; if (config->identity_secure_key == NULL) return -EINVAL; if (renew_cert_filename == NULL && (subject_rdns == NULL || num_subject_rdns == 0)) return -EINVAL; if (num_extensions != 0 && extensions == NULL) return -EINVAL; _ekmf_copy_ext_lib(ext_lib, &ext_lib_info); rc = SK_OPENSSL_init(verbose); if (rc != 0) { pr_verbose(verbose, "Failed to initialize secure key support: " "%s", strerror(-rc)); return rc; } rc = read_key_blob(config->identity_secure_key, key_blob, &key_blob_size); if (rc != 0) { pr_verbose(verbose, "Failed to read identity key from file " "'%s': %s", config->identity_secure_key, strerror(-rc)); goto out; } if (renew_cert_filename != NULL) { rc = read_x509_certificate(renew_cert_filename, &cert); if (rc != 0) { pr_verbose(verbose, "Failed to open renew cert file " "'%s': %s", renew_cert_filename, strerror(-rc)); goto out; } } _ekmf_copy_pss_params(rsa_pss_params, &pss_params); rc = SK_OPENSSL_generate_csr(key_blob, key_blob_size, subject_rdns, num_subject_rdns, subject_utf8, cert, extensions, num_extensions, digest_nid, &pss_params, &req, &ext_lib_info.ext_lib, verbose); if (rc != 0) { pr_verbose(verbose, "SK_OPENSSL_generate_csr failed " "'%s': %s", csr_pem_filename, strerror(-rc)); goto out; } rc = write_x509_request(csr_pem_filename, req, new_hdr); if (rc != 0) { pr_verbose(verbose, "Failed to write CSR to file " "'%s': %s", csr_pem_filename, strerror(-rc)); goto out; } pr_verbose(verbose, "Certificate Signing Request created successfully " "into '%s'", csr_pem_filename); out: if (cert != NULL) X509_free(cert); if (req != NULL) X509_REQ_free(req); SK_OPENSSL_term(); return rc; } /** * Generate a self signed certificate using the secure identity key (field * identity_secure_key in config structure) with the specified subject name, * certificate extensions (if any), and writes the certificate the specified * file in PEM format. * * To renew an existing certificate, specify renew_cert = true. In this case * the existing certificate (field sign_certificate in config struct) is read, * and the subject name is extracted from it. Any specified subject name RDNs * are added to the certificate. Also, the extensions are taken from the * existing certificate, and any specified extensions are added to the new * certificate. * * The certificate is signed using the secure identity key (field * identity_secure_key in config structure) with an signing algorithm matching * the identity key (ECDSA, RSA-PKCS, or RSA-PSS if rsa_pss is true), and the * specified digest. If the digest nid is zero, then a default digest is used. * * @param config the configuration structure. Only field * identity_secure_key must be specified, all others * are optional. * @param subject_rdns an array of strings, each string representing an * RDN in the form '[+]type=value'. If the type is * prepended with a '+', then this RDN is added to the * previous one. * @param num_subject_rdns number of RDN elements in the array. * @param subject_utf8 if true, RDNs of type MBSTRING_UTF8 are created, * otherwise type is MBSTRING_ASC is used. * @param renew_cert_filename if not NULL, specifies the file name of a PEM file * containing an existing certificate that is renewed * @param extensions an array of strings, each string representing an * certificate extension in the form 'type=value'. * @param num_extensions number of extension elements in the array. * @param validity_days number if day from the current date how long the * certificate is valid. * @param digest_nid the OpenSSL digest nid to use with the signature * algorithm, or 0 to use the default * @param rsa_pss_params if not NULL and the identity key is an RSA key, then * the certificate is signed with RSA-PSS using the * specified PSS parameters. Ignored if the identity * key is an EC key * @param cert_pem_filename the name of the PEM file to which the Certificate * is written * @param ext_lib External secure key crypto library to use * @param verbose if true, verbose messages are printed * * @returns a negative errno in case of an error, 0 if success. * -EINVAL: invalid parameter * -ENOMEM: Failed to allocate memory * -EBADMSG: an RDN or extension is not formatted correctly * -EIO: OpenSSL failed to create the certificate * -EEXIST: if one of the RDN name entries or extensions to add is a * duplicate * -ENOTSUP: the specified digest is not supported * any other errno from file I/O routines */ int ekmf_generate_ss_cert(const struct ekmf_config *config, const char *subject_rdns[], size_t num_subject_rdns, bool subject_utf8, const char *renew_cert_filename, const char *extensions[], size_t num_extensions, int validity_days, int digest_nid, struct ekmf_rsa_pss_params *rsa_pss_params, const char *cert_pem_filename, const struct ekmf_ext_lib *ext_lib, bool verbose) { unsigned char key_blob[MAX_KEY_BLOB_SIZE]; size_t key_blob_size = sizeof(key_blob); struct sk_rsa_pss_params pss_params; struct ext_lib_info ext_lib_info; X509 *rcert = NULL; X509 *cert = NULL; int rc; if (config == NULL || ext_lib == NULL || cert_pem_filename == NULL) return -EINVAL; if (config->identity_secure_key == NULL) return -EINVAL; if (renew_cert_filename == NULL && (subject_rdns == NULL || num_subject_rdns == 0)) return -EINVAL; if (num_extensions != 0 && extensions == NULL) return -EINVAL; _ekmf_copy_ext_lib(ext_lib, &ext_lib_info); rc = SK_OPENSSL_init(verbose); if (rc != 0) { pr_verbose(verbose, "Failed to initialize secure key support: " "%s", strerror(-rc)); return rc; } rc = read_key_blob(config->identity_secure_key, key_blob, &key_blob_size); if (rc != 0) { pr_verbose(verbose, "Failed to read identity key from file " "'%s': %s", config->identity_secure_key, strerror(-rc)); goto out; } if (renew_cert_filename != NULL) { rc = read_x509_certificate(renew_cert_filename, &rcert); if (rc != 0) { pr_verbose(verbose, "Failed to open renew cert file " "'%s': %s", renew_cert_filename, strerror(-rc)); goto out; } } _ekmf_copy_pss_params(rsa_pss_params, &pss_params); rc = SK_OPENSSL_generate_ss_cert(key_blob, key_blob_size, subject_rdns, num_subject_rdns, subject_utf8, rcert, extensions, num_extensions, validity_days, digest_nid, &pss_params, &cert, &ext_lib_info.ext_lib, verbose); if (rc != 0) { pr_verbose(verbose, "SK_OPENSSL_generate_ss_cert failed " "'%s': %s", cert_pem_filename, strerror(-rc)); goto out; } rc = write_x509_certificate(cert_pem_filename, cert); if (rc != 0) { pr_verbose(verbose, "Failed to write Certificate to file " "'%s': %s", cert_pem_filename, strerror(-rc)); goto out; } pr_verbose(verbose, "Self-signed Certificate created successfully " "into '%s'", cert_pem_filename); out: if (cert != NULL) X509_free(cert); if (rcert != NULL) X509_free(rcert); SK_OPENSSL_term(); return rc; } /** * Close the connection to the EKMFWeb server by destroying the CURL handle. * * @param curl_handle the CURL handle to destroy */ void ekmf_curl_destroy(CURL *curl_handle) { if (curl_handle == NULL) return; curl_easy_cleanup(curl_handle); } /** * Library constructor */ void __attribute__ ((constructor)) ekmf_init(void) { CURLsslset rc; /* * Ensure that curl uses OpenSSL as SSL backend. If curl has already * been itialized by the calling application, the backend can't be * changed anymore, but we continue anyway. However, it will later be * checked if curl uses the OpenSSL backend, and a HTTPS connection * will fail if it is not using the OpenSSL backend. */ rc = curl_global_sslset(CURLSSLBACKEND_OPENSSL, NULL, NULL); if (rc != CURLSSLSET_OK && rc != CURLSSLSET_TOO_LATE) errx(EXIT_FAILURE, "libekmfweb: libcurl was not built with " "the OpenSSL backend"); curl_global_init(CURL_GLOBAL_ALL); } /** * Library destructor */ void __attribute__ ((destructor)) ekmf_exit(void) { curl_global_cleanup(); } s390-tools-2.38.0/libekmfweb/libekmfweb.map000066400000000000000000000014521502674226300203660ustar00rootroot00000000000000LIBEKMFWEB_1.0 { global: ekmf_get_server_cert_chain; ekmf_print_certificates; ekmf_check_login_token; ekmf_login; ekmf_generate_identity_key; ekmf_reencipher_identity_key; ekmf_generate_csr; ekmf_generate_ss_cert; ekmf_get_public_key; ekmf_get_settings; ekmf_check_feature; ekmf_retrieve_key; ekmf_list_templates; ekmf_get_template; ekmf_get_last_seq_no; ekmf_clone_template_info; ekmf_free_template_info; ekmf_list_keys; ekmf_get_key_info; ekmf_set_key_state; ekmf_set_key_tags; ekmf_delete_key_tags; ekmf_clone_key_info; ekmf_free_key_info; ekmf_generate_key; ekmf_curl_destroy; local: *; }; s390-tools-2.38.0/libekmfweb/utilities.c000066400000000000000000002025761502674226300177510ustar00rootroot00000000000000/* * libekmfweb - EKMFWeb client library * * Copyright IBM Corp. 2020 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/zt_common.h" #include "libseckey/sk_openssl.h" #include "libseckey/sk_utilities.h" #include "utilities.h" #ifndef JSON_C_TO_STRING_NOSLASHESCAPE #define JSON_C_TO_STRING_NOSLASHESCAPE (1 << 4) #endif /** * Decodes a Base64URL encoded string. Base64URL is like Base64, but using a * URL and Filename Safe Alphabet, not using characters like '+', '/', or '='. * * The function converts the Base64URL input into standard Base64 data and then * decodes it using OpenSSL EVP_DecodeBlock. * * @param output a buffer to store the output. If NULL, then the * required size in bytes is returned in outlen and * no decoding is performed. * @param outlen on entry: Size of the output buffer in bytes. * on exit: Size of the decoded data in bytes. * @param input the Base64URL encoded data to decode * @param inlen the size of the input data in bytes * * @returns zero for success, a negative errno in case of an error: * -EINVAL: a function parameter is invalid * -ERANGE: the output buffer size is too small. outlen contains the * required size on return. * -ENOMEM: failed to allocate memory * -EIO: OpenSSL failed to decode the data */ int decode_base64url(unsigned char *output, size_t *outlen, const char *input, size_t inlen) { size_t raw_outlen, padded_outlen, padded_inlen; char *padded_input = NULL; char *padded_output; int len, rc = 0; size_t i; if (input == NULL || outlen == NULL) return -EINVAL; /* Base64URL might optionally include padding chars ('='), remove it */ for (; inlen > 0 && input[inlen - 1] == '='; inlen--) ; /* Might need to pad with standard base64 padding character '=' */ padded_inlen = inlen; if ((inlen % 4) > 0) padded_inlen += 4 - (inlen % 4); /* Also the output will be padded while decoding */ padded_outlen = (padded_inlen / 4) * 3; raw_outlen = (inlen / 4) * 3 + ((inlen % 4) >= 2 ? (inlen % 4) - 1 : 0); if (output == NULL) /* size query */ goto out; if (*outlen < raw_outlen) { rc = -ERANGE; goto out; } padded_input = (char *)malloc(padded_inlen + padded_outlen); if (padded_input == NULL) return -ENOMEM; padded_output = padded_input + padded_inlen; memcpy(padded_input, input, inlen); memset(padded_input + inlen, '=', padded_inlen - inlen); /* Replace '-' by '+', and '_' by '/' */ for (i = 0; i < inlen; i++) { if (padded_input[i] == '-') padded_input[i] = '+'; else if (padded_input[i] == '_') padded_input[i] = '/'; } len = EVP_DecodeBlock((unsigned char *)padded_output, (unsigned char *)padded_input, padded_inlen); if (len != (int)padded_inlen * 3 / 4) { rc = -EIO; goto out; } memcpy(output, padded_output, raw_outlen); out: *outlen = raw_outlen; if (padded_input != NULL) free(padded_input); return rc; } /** * Encodes a data using Base64URL. Base64URL is like Base64, but using a * URL and Filename Safe Alphabet, not using characters like '+', '/', or '='. * * The function encodes the data into standard Base64 data using OpenSSL * EVP_EncodeBlock and then converts it into Base64URL data * * @param output a buffer to store the output. If NULL, then the * required size in bytes is returned in outlen and * no encoding is performed. * The NUL character is added to the output at the end * of the encoded data. * @param outlen on entry: Size of the output buffer in bytes. * on exit: Size of the encoded data in bytes, * including the NUL character. * @param input the data to Base64URL-encode * @param inlen the size of the input data in bytes * * @returns zero for success, a negative errno in case of an error: * -EINVAL: a function parameter is invalid * -ERANGE: the output buffer size is too small. outlen contains the * required size on return. * -ENOMEM: failed to allocate memory * -EIO: OpenSSL failed to decode the data */ int encode_base64url(char *output, size_t *outlen, const unsigned char *input, size_t inlen) { size_t padded_outlen, raw_outlen, ofs = 0; char *padded_output = NULL; int len, rc = 0; if (input == NULL || outlen == NULL) return -EINVAL; padded_outlen = (inlen / 3) * 4; if (inlen % 3 > 0) padded_outlen += 4; raw_outlen = (inlen / 3) * 4 + ((inlen % 3) > 0 ? (inlen % 3) + 1 : 0); if (output == NULL) goto out; if (*outlen < raw_outlen + 1) { rc = -ERANGE; goto out; } padded_output = (char *)malloc(padded_outlen + 1); if (padded_output == NULL) return -ENOMEM; len = EVP_EncodeBlock((unsigned char *)padded_output, (unsigned char *)input, inlen); if (len != (int)padded_outlen) { rc = -EIO; goto out; } /* Replace '+' by '-', and '/' by '_', and stop at first '=' */ for (ofs = 0; ofs < padded_outlen; ofs++) { if (padded_output[ofs] == '+') padded_output[ofs] = '-'; else if (padded_output[ofs] == '/') padded_output[ofs] = '_'; else if (padded_output[ofs] == '=') break; } if (ofs != raw_outlen) { rc = -EIO; goto out; } memcpy(output, padded_output, raw_outlen); output[raw_outlen] = '\0'; out: *outlen = raw_outlen + 1; if (padded_output != NULL) free(padded_output); return rc; } /** * Parses a JSON Web Token (JWT) and extracts the header and payload parts as * parsed JSON object. The returned JSON objects must be freed by the caller * using json_object_put() when no longer needed. The returned signature must * also be freed by the caller when no longer needed. * * @param token the JSON Web Tolken to parse * @param header_obj if not NULL, the parsed JWT header as JSON Object * @param payload_obj if not NULL, the parsed JWT payload as JSON Object * @param signature if not NULL, a pointer to the decoded signature * @param signature_len if not NULL, the size of the signature in bytes * * @returns zero for success, a negative errno in case of an error: * -EINVAL: a function parameter is invalid * -EBADMSG: If the token could not be parsed into parts * -ENOMEM: failed to allocate memory * -EIO: Base64 parsing error */ int parse_json_web_token(const char *token, json_object **header_obj, json_object **payload_obj, unsigned char **signature, size_t *signature_len) { json_object *hdr = NULL, *pld = NULL, *b64_obj = NULL; char *ch, *header, *payload, *json = NULL; size_t header_len, payload_len, json_len; bool b64 = true; int rc = 0; if (token == NULL) return -EINVAL; if (header_obj != NULL) *header_obj = NULL; if (payload_obj != NULL) *payload_obj = NULL; if (signature != NULL) *signature = NULL; if (signature_len != NULL) *signature_len = 0; /* * A JSON Web Token consists of several parts, each part Base64URL * encoded, and separated by a colon '.' from each other. * The first part is the JOSE (JSON Object Signing and Encryption) * Header. The second part is the JWS Payload containing the JWS claims, * and the following parts (if any) are used for JWS Signature, or JWE * Encryption (not considered here). */ header = (char *)token; ch = strchr(token, '.'); if (ch == NULL) { rc = -EBADMSG; goto out; } header_len = ch - token; payload = ++ch; ch = strchr(ch, '.'); if (ch == NULL) { rc = -EBADMSG; goto out; } payload_len = ch - payload; ch++; rc = decode_base64url(NULL, &json_len, header, header_len); if (rc != 0) goto out; json = malloc(json_len + 1); if (json == NULL) { rc = -ENOMEM; goto out; } rc = decode_base64url((unsigned char *)json, &json_len, header, header_len); if (rc != 0) goto out; json[json_len] = '\0'; hdr = json_tokener_parse(json); if (hdr == NULL) { rc = -EIO; goto out; } if (json_object_object_get_ex(hdr, "b64", &b64_obj) && json_object_is_type(b64_obj, json_type_boolean)) b64 = json_object_get_boolean(b64_obj); free(json); json = NULL; if (payload_obj != NULL) { if (b64) { rc = decode_base64url(NULL, &json_len, payload, payload_len); if (rc != 0) goto out; json = malloc(json_len + 1); if (json == NULL) { rc = -ENOMEM; goto out; } rc = decode_base64url((unsigned char *)json, &json_len, payload, payload_len); if (rc != 0) goto out; json[json_len] = '\0'; } else { json = strndup(payload, payload_len); } pld = json_tokener_parse(json); if (pld == NULL) { rc = -EIO; goto out; } free(json); json = NULL; } if (signature != NULL && signature_len != NULL) { rc = decode_base64url(NULL, signature_len, ch, strlen(ch)); if (rc != 0) goto out; *signature = malloc(*signature_len); if (*signature == NULL) { rc = -ENOMEM; goto out; } rc = decode_base64url(*signature, signature_len, ch, strlen(ch)); if (rc != 0) goto out; } out: if (header_obj != NULL && rc == 0) *header_obj = hdr; else json_object_put(hdr); if (payload_obj != NULL && rc == 0) *payload_obj = pld; else json_object_put(pld); if (signature != NULL && rc != 0) { free(signature); signature = NULL; *signature_len = 0; } if (json != NULL) free(json); return rc; } /** * Creates a JSON Web Signature object with the specified parts and returns a * a character string containing the serialized JWS (see RFC 7515 for details) * * @param algorithm the JWS algorithm (e.g. ES512) (in the JWS header) * @param b64 the b64 property of the JWS header. If b64 is true, * then the payload (if any) is base64url encoded, * if false, the payload (if any) is used as-is. * @param kid the Key ID JWS header field (can be NULL) * @param payload the JWS payload. * @param payload_len the length of the payload in bytes * @param detached_payload if true a JWS with detached payload is created (see * RFC 7515 Appendix F) * @param md_ctx An OpenSSL MD that has been set up with the desired * digest and signing algorithm, options, and key * @param jws On return: a C-string allocated by this function * containing the serialized JWS. The caller must * free the memory used by the returned string. * * @returns zero for success, a negative errno in case of an error */ int create_json_web_signature(const char *algorithm, bool b64, const char *kid, const unsigned char *payload, size_t payload_len, bool detached_payload, EVP_MD_CTX *md_ctx, char **jws) { const struct sk_ec_curve_info *curve_info; unsigned char *signature = NULL; json_object *header_obj = NULL; json_object *crit_obj = NULL; size_t signature_b64_len = 0; size_t payload_b64_len = 0; char *signature_b64 = NULL; size_t header_b64_len = 0; size_t signature_len = 0; char *payload_b64 = NULL; ECDSA_SIG *ec_sig = NULL; char *header_b64 = NULL; const unsigned char *p; const char *header; size_t prime_len; EVP_PKEY *pkey; int rc; if (algorithm == NULL || payload == NULL || md_ctx == NULL || jws == NULL) return -EINVAL; pkey = EVP_PKEY_CTX_get0_pkey(EVP_MD_CTX_pkey_ctx(md_ctx)); if (pkey == NULL) { rc = -EIO; goto out; } header_obj = json_object_new_object(); if (header_obj == NULL) { rc = -ENOMEM; goto out; } /* * Note: The order of the fields is important, EKMFWeb expects it in * exactly this order! */ rc = json_object_object_add_ex(header_obj, "alg", json_object_new_string(algorithm), 0); if (kid != NULL) rc |= json_object_object_add_ex(header_obj, "kid", json_object_new_string(kid), 0); rc |= json_object_object_add_ex(header_obj, "b64", json_object_new_boolean(b64), 0); crit_obj = json_object_new_array(); rc |= (crit_obj == NULL ? -1 : 0); rc |= json_object_array_add(crit_obj, json_object_new_string("b64")); rc |= json_object_object_add_ex(header_obj, "crit", crit_obj, 0); crit_obj = NULL; if (rc != 0) { rc = -EIO; goto out; } header = json_object_to_json_string_ext(header_obj, JSON_C_TO_STRING_PLAIN | JSON_C_TO_STRING_NOSLASHESCAPE); if (header == NULL) { rc = -ENOMEM; goto out; } rc = encode_base64url(NULL, &header_b64_len, (unsigned char *)header, strlen(header)); if (rc != 0) goto out; header_b64 = malloc(header_b64_len); if (header_b64 == NULL) { rc = -ENOMEM; goto out; } rc = encode_base64url(header_b64, &header_b64_len, (unsigned char *)header, strlen(header)); if (rc != 0) goto out; if (b64) { rc = encode_base64url(NULL, &payload_b64_len, payload, payload_len); if (rc != 0) goto out; payload_b64 = malloc(payload_b64_len); if (payload_b64 == NULL) { rc = -ENOMEM; goto out; } rc = encode_base64url(payload_b64, &payload_b64_len, payload, payload_len); if (rc != 0) goto out; } /* Sign: BASE64URL(UTF8(JWSHeader)) | '.' | [BASE64URL](JWS Payload) */ rc = EVP_DigestSignUpdate(md_ctx, header_b64, strlen(header_b64)); if (rc != 1) { rc = -EIO; goto out; } rc = EVP_DigestSignUpdate(md_ctx, ".", 1); if (rc != 1) { rc = -EIO; goto out; } if (b64) rc = EVP_DigestSignUpdate(md_ctx, payload_b64, strlen(payload_b64)); else rc = EVP_DigestSignUpdate(md_ctx, payload, payload_len); if (rc != 1) { rc = -EIO; goto out; } signature_len = EVP_PKEY_size(pkey); signature = malloc(signature_len); if (signature == NULL) { rc = -ENOMEM; goto out; } rc = EVP_DigestSignFinal(md_ctx, signature, &signature_len); if (rc != 1) { rc = -EIO; goto out; } switch (EVP_PKEY_id(pkey)) { case EVP_PKEY_EC: curve_info = SK_UTIL_ec_get_curve_info( SK_OPENSSL_get_curve_from_ec_pkey(pkey)); if (curve_info == NULL) { rc = -EINVAL; goto out; } prime_len = curve_info->prime_len; p = signature; if (d2i_ECDSA_SIG(&ec_sig, &p, signature_len) == NULL) { rc = -EIO; goto out; } if (signature_len < 2 * prime_len) { rc = -EINVAL; goto out; } memset(signature, 0, signature_len); BN_bn2binpad(ECDSA_SIG_get0_r(ec_sig), signature, prime_len); BN_bn2binpad(ECDSA_SIG_get0_s(ec_sig), signature + prime_len, prime_len); signature_len = 2 * prime_len; break; case EVP_PKEY_RSA: case EVP_PKEY_RSA_PSS: /* No signature encoding for RSA */ break; default: rc = -EINVAL; goto out; } rc = encode_base64url(NULL, &signature_b64_len, signature, signature_len); if (rc != 0) goto out; signature_b64 = malloc(signature_b64_len); if (signature_b64 == NULL) { rc = -ENOMEM; goto out; } rc = encode_base64url(signature_b64, &signature_b64_len, signature, signature_len); if (rc != 0) goto out; if (detached_payload) { if (asprintf(jws, "%s..%s", header_b64, signature_b64) < 0) { rc = -ENOMEM; goto out; } } else if (b64) { if (asprintf(jws, "%s.%s.%s", header_b64, payload_b64, signature_b64) < 0) { rc = -ENOMEM; goto out; } } else { if (asprintf(jws, "%s.%.*s.%s", header_b64, (int)payload_len, payload, signature_b64) < 0) { rc = -ENOMEM; goto out; } } rc = 0; out: if (header_obj != NULL) json_object_put(header_obj); if (header_b64 != NULL) free(header_b64); if (payload_b64 != NULL) free(payload_b64); if (signature != NULL) free(signature); if (signature_b64 != NULL) free(signature_b64); if (ec_sig != NULL) ECDSA_SIG_free(ec_sig); return rc; } /** * Verifies a JSON Web Signature object (see RFC 7515 for details). * * @param jws the JWS string * @param payload if not NULL: the detached JWS payload. * @param payload_len the length of the detached payload in bytes * @param md_ctx An OpenSSL MD that has been set up with the desired * digest and signing algorithm, options, and key * * @returns zero for success, a negative errno in case of an error */ int verify_json_web_signature(const char *jws, const unsigned char *payload, size_t payload_len, EVP_PKEY *pkey) { size_t header_len, hdr_pld_len, payload_b64_len, signature_len = 0; unsigned char *signature = NULL, *der = NULL, *sig = NULL; json_object *header_obj = NULL, *b64_obj = NULL; int der_len, rc, curve_nid = 0, digest_nid = 0; struct sk_rsa_pss_params rsa_pss_params; bool b64 = true, rsa_pss = false; EVP_MD_CTX *md_ctx = NULL; EVP_PKEY_CTX *pctx = NULL; ECDSA_SIG *ec_sig = NULL; char *payload_b64 = NULL; BIGNUM *bn_r = NULL; BIGNUM *bn_s = NULL; const char *alg; size_t sig_len; char *ch; if (jws == NULL || pkey == NULL) return -EINVAL; rc = parse_json_web_token(jws, &header_obj, NULL, &signature, &signature_len); if (rc != 0) goto out; ch = strchr(jws, '.'); if (ch == NULL) { rc = -EBADMSG; goto out; } header_len = ch - jws; ch = strchr(++ch, '.'); if (ch == NULL) { rc = -EBADMSG; goto out; } hdr_pld_len = ch - jws; if (EVP_PKEY_id(pkey) == EVP_PKEY_EC) { curve_nid = SK_OPENSSL_get_curve_from_ec_pkey(pkey); } alg = json_get_string(header_obj, "alg"); if (alg == NULL) { rc = -EIO; goto out; } /* * Only the following combinations are allowed per RFC7518 for JSON * Web Signatures (JWS) using ECC or RSA signing keys: * alg=ES256: ECDSA using P-256 and SHA-256 * alg=ES384: ECDSA using P-384 and SHA-384 * alg=ES512: ECDSA using P-521 and SHA-512 * alg=RS256: RSA-PKCS1 using SHA-256 * alg=RS384: RSA-PKCS1 using SHA-384 * alg=RS512: RSA-PKCS1 using SHA-512 * alg=PS256: RSA-PSS using SHA-256, MGF1 with SHA-256, salt=digest * alg=PS384: RSA-PSS using SHA-384, MGF1 with SHA-384, salt=digest * alg=PS512: RSA-PSS using SHA-512, MGF1 with SHA-512, salt=digest */ if (strncmp(alg, "ES", 2) == 0) { if (EVP_PKEY_id(pkey) != EVP_PKEY_EC) { rc = EINVAL; goto out; } if ((strncmp(alg + 2, "512", 3) == 0 && curve_nid != NID_secp521r1) || (strncmp(alg + 2, "384", 3) == 0 && curve_nid != NID_secp384r1) || (strncmp(alg + 2, "256", 3) == 0 && curve_nid != NID_X9_62_prime256v1)) { rc = EINVAL; goto out; } } else if (strncmp(alg, "RS", 2) == 0) { if (EVP_PKEY_id(pkey) != EVP_PKEY_RSA) { rc = EINVAL; goto out; } } else if (strncmp(alg, "PS", 2) == 0) { if (EVP_PKEY_id(pkey) != EVP_PKEY_RSA && EVP_PKEY_id(pkey) != EVP_PKEY_RSA_PSS) { rc = EINVAL; goto out; } rsa_pss = true; } else { rc = -ENOTSUP; goto out; } if (strncmp(alg + 2, "512", 3) == 0) digest_nid = NID_sha512; else if (strncmp(alg + 2, "384", 3) == 0) digest_nid = NID_sha384; else if (strncmp(alg + 2, "256", 3) == 0) digest_nid = NID_sha256; if (digest_nid == 0) { rc = -ENOTSUP; goto out; } memset(&rsa_pss_params, 0, sizeof(rsa_pss_params)); if (rsa_pss) { rsa_pss_params.mgf_digest_nid = digest_nid; rsa_pss_params.salt_len = RSA_PSS_SALTLEN_DIGEST; } rc = SK_OPENSSL_setup_sign_context(pkey, true, digest_nid, &rsa_pss_params, &md_ctx, &pctx, false); if (rc != 0) goto out; switch (EVP_PKEY_id(pkey)) { case EVP_PKEY_EC: ec_sig = ECDSA_SIG_new(); if (ec_sig == NULL) { rc = -ENOMEM; goto out; } bn_r = BN_bin2bn(signature, signature_len / 2, NULL); bn_s = BN_bin2bn(signature + signature_len / 2, signature_len / 2, NULL); if (bn_r == NULL || bn_s == NULL) { rc = -EIO; goto out; } if (ECDSA_SIG_set0(ec_sig, bn_r, bn_s) != 1) { rc = -EIO; goto out; } bn_r = NULL; bn_s = NULL; der_len = i2d_ECDSA_SIG(ec_sig, &der); if (der_len <= 0) { rc = -EIO; goto out; } sig = der; sig_len = der_len; break; case EVP_PKEY_RSA: case EVP_PKEY_RSA_PSS: /* No signature encoding for RSA */ sig = signature; sig_len = signature_len; break; default: rc = -EINVAL; goto out; } if (payload != NULL && payload_len > 0) { /* Detached payload */ if (json_object_object_get_ex(header_obj, "b64", &b64_obj) && json_object_is_type(b64_obj, json_type_boolean)) b64 = json_object_get_boolean(b64_obj); if (b64) { rc = encode_base64url(NULL, &payload_b64_len, payload, payload_len); if (rc != 0) goto out; payload_b64 = malloc(payload_b64_len); if (payload_b64 == NULL) { rc = -ENOMEM; goto out; } rc = encode_base64url(payload_b64, &payload_b64_len, payload, payload_len); if (rc != 0) goto out; } /* Take header plus '.' as is */ rc = EVP_DigestVerifyUpdate(md_ctx, jws, header_len + 1); if (rc != 1) { rc = -EIO; goto out; } if (b64) rc = EVP_DigestVerifyUpdate(md_ctx, payload_b64, payload_b64_len); else rc = EVP_DigestVerifyUpdate(md_ctx, payload, payload_len); if (rc != 1) { rc = -EIO; goto out; } } else { /* Take header plus '.' plus payload as is */ rc = EVP_DigestVerifyUpdate(md_ctx, jws, hdr_pld_len); if (rc != 1) { rc = -EIO; goto out; } } rc = EVP_DigestVerifyFinal(md_ctx, sig, sig_len); if (rc != 1) { rc = -EIO; goto out; } rc = 0; out: if (header_obj != NULL) json_object_put(header_obj); if (signature != NULL) free(signature); if (payload_b64 != NULL) free(payload_b64); if (ec_sig != NULL) ECDSA_SIG_free(ec_sig); if (der != NULL) OPENSSL_free(der); if (bn_r != NULL) BN_free(bn_r); if (bn_s != NULL) BN_free(bn_s); if (md_ctx != NULL) EVP_MD_CTX_free(md_ctx); return rc; } /** * Builds a JSON Object containing a timestamp value in ISO 8601 format, e.g. * { "timestamp": "2020-04-27T10:02:18.123Z" }. The time is expressed in UTC, * regardless of the local time zone. * * @returns a JSON object containing the timestamp, or NULL in case of an error. */ json_object *get_json_timestamp(void) { char timestamp[100]; struct timeval tv; struct tm *tm; char temp[20]; if (gettimeofday(&tv, NULL) != 0) return NULL; tm = gmtime(&tv.tv_sec); if (strftime(timestamp, sizeof(timestamp), "%FT%T", tm) == 0) return NULL; snprintf(temp, sizeof(temp), ".%06ldZ", tv.tv_usec); strcat(timestamp, temp); return json_object_new_string(timestamp); } /** * If copy is true, returns a copy of str (via strdup), else returns str itself. * If str is NULL, then NULL is returned. */ static char *cond_strdup(const char *str, bool copy) { if (str == NULL) return NULL; if (copy) return strdup(str); else return (char *)str; } /** * Returns the start of the UUId part of a href link. * Returns NULL if href is NULL, or if the UUID is not found. * The returned pointer (if not NULL) is within the specified href string! */ static const char *get_uuid_from_href(const char *href) { const char *ch; if (href == NULL) return NULL; ch = strrchr(href, '/'); if (ch == NULL) return NULL; return ch + 1; } /** * Builds a list of tag definitions from a JSON array. * * @param array a JSON array of tag definitions * @param tag_def_list the tag definition list to build * @param copy if true, the string values are copied (via strdup), * if false, the string values re-use the JSON object's * string buffer (see json_object_get_string). * * @returns zero for success, a negative errno in case of an error */ int json_build_tag_def_list(json_object *array, struct ekmf_tag_def_list *tag_def_list, bool copy) { const char *descr; json_object *obj; int rc = 0; size_t i; if (array == NULL || tag_def_list == NULL || !json_object_is_type(array, json_type_array)) return -EINVAL; tag_def_list->num_tag_defs = json_object_array_length(array); tag_def_list->tag_defs = calloc(tag_def_list->num_tag_defs, sizeof(struct ekmf_tag_definition)); if (tag_def_list->tag_defs == NULL) return -ENOMEM; for (i = 0; i < tag_def_list->num_tag_defs; i++) { obj = json_object_array_get_idx(array, i); if (obj == NULL) { rc = -EBADMSG; goto out; } tag_def_list->tag_defs[i].name = cond_strdup( json_get_string(obj, "name"), copy); if (tag_def_list->tag_defs[i].name == NULL) { rc = -ENOMEM; goto out; } descr = json_get_string(obj, "description"); if (descr != NULL) { tag_def_list->tag_defs[i].description = cond_strdup(descr, copy); if (tag_def_list->tag_defs[i].description == NULL) { rc = -ENOMEM; goto out; } } } out: if (rc != 0) free_tag_def_list(tag_def_list, copy); return rc; } /** * Clones (copies) a tag definition list * * @param src the source tag definition list * @param dest the destination tag definition list * * @returns zero for success, a negative errno in case of an error */ int clone_tag_def_list(const struct ekmf_tag_def_list *src, struct ekmf_tag_def_list *dest) { int rc = 0; size_t i; if (src == NULL || dest == NULL) return -EINVAL; dest->num_tag_defs = src->num_tag_defs; dest->tag_defs = calloc(dest->num_tag_defs, sizeof(struct ekmf_tag_definition)); if (dest->tag_defs == NULL) return -ENOMEM; for (i = 0; i < dest->num_tag_defs; i++) { dest->tag_defs[i].name = cond_strdup(src->tag_defs[i].name, true); if (dest->tag_defs[i].name == NULL) { rc = -ENOMEM; goto out; } if (src->tag_defs[i].description != NULL) { dest->tag_defs[i].description = strdup(src->tag_defs[i].description); if (dest->tag_defs[i].description != NULL) { rc = -ENOMEM; goto out; } } } out: if (rc != 0) free_tag_def_list(dest, true); return rc; } /** * Free a tag definition list * * @param tag_def_list the tag definition list to free * @param free_tags if true, the tag name and description string s are * freed, otherwise only the array is freed. */ void free_tag_def_list(struct ekmf_tag_def_list *tag_def_list, bool free_tags) { size_t i; if (tag_def_list == NULL || tag_def_list->tag_defs == NULL) return; for (i = 0; free_tags && i < tag_def_list->num_tag_defs; i++) { free((char *)tag_def_list->tag_defs[i].name); free((char *)tag_def_list->tag_defs[i].description); } free(tag_def_list->tag_defs); tag_def_list->tag_defs = NULL; tag_def_list->num_tag_defs = 0; } /** * Builds a template info structure from a JSON object. * * @param obj a JSON object containing the template info * @param template the template info struct build * @param copy if true, the string values are copied (via strdup), * if false, the string values re-use the JSON object's * string buffer (see json_object_get_string). * * @returns zero for success, a negative errno in case of an error */ int json_build_template_info(json_object *obj, struct ekmf_template_info *template, bool copy) { json_object *field, *label_tags = NULL; int rc; if (obj == NULL || template == NULL || !json_object_is_type(obj, json_type_object)) return -EINVAL; template->name = cond_strdup(json_get_string(obj, "name"), copy); template->uuid = cond_strdup(json_get_string(obj, "templateId"), copy); template->key_type = cond_strdup(json_get_string(obj, "keyType"), copy); template->algorithm = cond_strdup(json_get_string(obj, "algorithm"), copy); if (json_object_object_get_ex(obj, "keyLength", &field) && json_object_is_type(field, json_type_int)) template->key_size = json_object_get_int(field); template->state = cond_strdup(json_get_string(obj, "templateState"), copy); template->key_state = cond_strdup(json_get_string(obj, "keyState"), copy); template->label_template = cond_strdup(json_get_string(obj, "labelTemplate"), copy); if (json_object_object_get_ex(obj, "exportAllowed", &field) && json_object_is_type(field, json_type_boolean)) template->export_allowed = json_object_get_boolean(field); template->keystore_type = cond_strdup(json_get_string(obj, "keystoreType"), copy); template->curve = cond_strdup(json_get_string(obj, "curve"), copy); template->created_on = cond_strdup(json_get_string(obj, "createdOn"), copy); template->updated_on = cond_strdup(json_get_string(obj, "updatedOn"), copy); if (template->name == NULL || template->uuid == NULL || template->algorithm == NULL || template->label_template == NULL || template->state == NULL || template->key_state == NULL || template->keystore_type == NULL || template->created_on == NULL || template->updated_on == NULL) { rc = -ENOMEM; goto out; } json_object_object_get_ex(obj, "labelTags", &label_tags); rc = json_build_tag_def_list(label_tags, &template->label_tags, copy); if (rc != 0) goto out; out: if (rc != 0) { free_tag_def_list(&template->label_tags, copy); if (copy) free_template_info(template); } return rc; } /** * Clones (copies) a template info structure * * @param src the source template info structure * @param dest the destination template info structure * * @returns zero for success, a negative errno in case of an error */ int clone_template_info(const struct ekmf_template_info *src, struct ekmf_template_info *dest) { int rc; if (src == NULL || dest == NULL) return -EINVAL; dest->name = cond_strdup(src->name, true); dest->uuid = cond_strdup(src->uuid, true); dest->key_type = cond_strdup(src->key_type, true); dest->algorithm = cond_strdup(src->algorithm, true); dest->key_size = src->key_size; dest->state = cond_strdup(src->state, true); dest->key_state = cond_strdup(src->key_state, true); dest->label_template = cond_strdup(src->label_template, true); dest->export_allowed = src->export_allowed; dest->keystore_type = cond_strdup(src->keystore_type, true); dest->curve = cond_strdup(src->curve, true); dest->created_on = cond_strdup(src->created_on, true); dest->updated_on = cond_strdup(src->updated_on, true); if (dest->name == NULL || dest->uuid == NULL || dest->algorithm == NULL || dest->state == NULL || dest->key_state == NULL || dest->label_template == NULL || dest->keystore_type == NULL || dest->created_on == NULL || dest->updated_on == NULL) { rc = -ENOMEM; goto out; } rc = clone_tag_def_list(&src->label_tags, &dest->label_tags); if (rc != 0) goto out; out: if (rc != 0) free_template_info(dest); return rc; } /** * Free a template info structure * * @param template the template to free */ void free_template_info(struct ekmf_template_info *template) { if (template == NULL) return; free((char *)template->name); free((char *)template->uuid); free((char *)template->key_type); free((char *)template->algorithm); free((char *)template->state); free((char *)template->key_state); free((char *)template->label_template); free((char *)template->keystore_type); free((char *)template->curve); free((char *)template->created_on); free((char *)template->updated_on); free_tag_def_list(&template->label_tags, true); } /** * Builds a list of tags from a JSON array. * * @param array a JSON array of tags * @param tag_list the tag list to build * @param copy if true, the string values are copied (via strdup), * if false, the string values re-use the JSON object's * string buffer (see json_object_get_string). * * @returns zero for success, a negative errno in case of an error */ int json_build_tag_list(json_object *array, struct ekmf_tag_list *tag_list, bool copy) { json_object *obj; size_t i; int rc = 0; if (array == NULL || tag_list == NULL || !json_object_is_type(array, json_type_array)) return -EINVAL; tag_list->num_tags = json_object_array_length(array); tag_list->tags = calloc(tag_list->num_tags, sizeof(struct ekmf_tag)); if (tag_list->tags == NULL) return -ENOMEM; for (i = 0; i < tag_list->num_tags; i++) { obj = json_object_array_get_idx(array, i); if (obj == NULL) return -EBADMSG; tag_list->tags[i].name = cond_strdup( json_get_string(obj, "name"), copy); tag_list->tags[i].value = cond_strdup( json_get_string(obj, "value"), copy); if (tag_list->tags[i].name == NULL || tag_list->tags[i].value == NULL) { rc = -ENOMEM; goto out; } } out: if (rc != 0) free_tag_list(tag_list, copy); return rc; } /** * Builds an JSON array with tag objects from a list of tags. * The returned JSON object must be freed using json_object_put by the caller. */ /** * Builds an JSON array with tag objects from a list of tags. * The returned JSON object must be freed using json_object_put by the caller. * * @param tag_list the tag list * @param tags_obj On return: a JSOn array containig the tags * * @returns zero for success, a negative errno in case of an error */ int build_json_tag_list(const struct ekmf_tag_list *tag_list, json_object **tags_obj) { json_object *tag_obj = NULL; int rc = 0; size_t i; if (tag_list == NULL || tags_obj == NULL) return -EINVAL; *tags_obj = json_object_new_array(); if (*tags_obj == NULL) return -ENOMEM; for (i = 0; i < tag_list->num_tags; i++) { if (tag_list->tags[i].name == NULL || tag_list->tags[i].value == NULL) { rc = -EINVAL; goto out; } tag_obj = json_object_new_object(); if (tag_obj == NULL) { rc = -ENOMEM; goto out; } rc = json_object_object_add_ex(tag_obj, "name", json_object_new_string(tag_list->tags[i].name), 0); if (rc != 0) { rc = -ENOMEM; goto out; } rc = json_object_object_add_ex(tag_obj, "value", json_object_new_string(tag_list->tags[i].value), 0); if (rc != 0) { rc = -ENOMEM; goto out; } rc = json_object_array_add(*tags_obj, tag_obj); if (rc != 0) { rc = -ENOMEM; goto out; } tag_obj = NULL; } rc = 0; out: if (rc != 0) { if (*tags_obj != NULL) json_object_put(*tags_obj); if (tag_obj != NULL) json_object_put(tag_obj); } return rc; } /** * Clones (copies) a tag list * * @param src the source tag list * @param dest the destination tag list * * @returns zero for success, a negative errno in case of an error */ int clone_tag_list(const struct ekmf_tag_list *src, struct ekmf_tag_list *dest) { size_t i; int rc = 0; if (src == NULL || dest == NULL) return -EINVAL; dest->num_tags = src->num_tags; if (dest->num_tags == 0) { dest->tags = NULL; return 0; } dest->tags = calloc(dest->num_tags, sizeof(struct ekmf_tag)); if (dest->tags == NULL) return -ENOMEM; for (i = 0; i < dest->num_tags; i++) { dest->tags[i].name = cond_strdup(src->tags[i].name, true); if (dest->tags[i].name == NULL) { rc = -ENOMEM; goto out; } dest->tags[i].value = cond_strdup(src->tags[i].value, true); if (dest->tags[i].value == NULL) { rc = -ENOMEM; goto out; } } out: if (rc != 0) free_tag_list(dest, true); return rc; } /** * Free a tag list * * @param tag_list the tag list to free * @param free_tags if true, the tag name and value string s are * freed, otherwise only the array is freed. */ void free_tag_list(struct ekmf_tag_list *tag_list, bool free_tags) { size_t i; if (tag_list == NULL || tag_list->tags == NULL) return; for (i = 0; free_tags && i < tag_list->num_tags; i++) { free((char *)tag_list->tags[i].name); free((char *)tag_list->tags[i].value); } free(tag_list->tags); tag_list->tags = NULL; tag_list->num_tags = 0; } /** * Builds the export control information from a JSON object. * * @param export_control the JSON oibject * @param tag_def_list the tag list to build * @param copy if true, the string values are copied (via strdup), * if false, the string values re-use the JSON object's * string buffer (see json_object_get_string). * * @returns zero for success, a negative errno in case of an error */ int json_build_export_control(json_object *export_control, struct ekmf_export_control *export_info, bool copy) { json_object *obj, *array; size_t i; int rc = 0; if (export_control == NULL || export_info == NULL || !json_object_is_type(export_control, json_type_object)) return -EINVAL; if (!json_object_object_get_ex(export_control, "exportAllowed", &obj) || !json_object_is_type(obj, json_type_boolean)) return -EINVAL; export_info->export_allowed = json_object_get_boolean(obj); if (!json_object_object_get_ex(export_control, "allowedKeys", &array) || !json_object_is_type(array, json_type_array)) return -EINVAL; export_info->num_exporting_keys = json_object_array_length(array); export_info->exporting_keys = calloc(export_info->num_exporting_keys, sizeof(struct ekmf_exporting_key)); if (export_info->exporting_keys == NULL) return -ENOMEM; for (i = 0; i < export_info->num_exporting_keys; i++) { obj = json_object_array_get_idx(array, i); if (obj == NULL) return -EBADMSG; export_info->exporting_keys[i].name = cond_strdup( json_get_string(obj, "title"), copy); export_info->exporting_keys[i].uuid = cond_strdup( get_uuid_from_href( json_get_string(obj, "href")), copy); if (export_info->exporting_keys[i].name == NULL || export_info->exporting_keys[i].uuid == NULL) { rc = -ENOMEM; goto out; } } out: if (rc != 0) free_export_control(export_info, copy); return rc; } /** * Clones (copies) an export control info * * @param src the source export control * @param dest the destination export control * * @returns zero for success, a negative errno in case of an error */ int clone_export_control(const struct ekmf_export_control *src, struct ekmf_export_control *dest) { size_t i; int rc = 0; if (src == NULL || dest == NULL) return -EINVAL; dest->export_allowed = src->export_allowed; dest->num_exporting_keys = src->num_exporting_keys; if (dest->num_exporting_keys == 0) { dest->exporting_keys = NULL; return 0; } dest->exporting_keys = calloc(dest->num_exporting_keys, sizeof(struct ekmf_exporting_key)); if (dest->exporting_keys == NULL) return -ENOMEM; for (i = 0; i < dest->num_exporting_keys; i++) { dest->exporting_keys[i].name = cond_strdup(src->exporting_keys[i].name, true); if (dest->exporting_keys[i].name == NULL) { rc = -ENOMEM; goto out; } dest->exporting_keys[i].uuid = cond_strdup(src->exporting_keys[i].uuid, true); if (dest->exporting_keys[i].uuid == NULL) { rc = -ENOMEM; goto out; } } out: if (rc != 0) free_export_control(dest, true); return rc; } /** * Free export control infos * * @param export_control the export control infos to free * @param free_tags if true, the exporting keys name and uuid strings * are freed, otherwise only the array is freed. */ void free_export_control(struct ekmf_export_control *export_control, bool free_keys) { size_t i; if (export_control == NULL || export_control->exporting_keys == NULL) return; for (i = 0; free_keys && i < export_control->num_exporting_keys; i++) { free((char *)export_control->exporting_keys[i].name); free((char *)export_control->exporting_keys[i].uuid); } free(export_control->exporting_keys); export_control->exporting_keys = NULL; export_control->num_exporting_keys = 0; } /** * Builds a key info structure from a JSON object. * * @param obj a JSON object containing the key info * @param custom_tags a JSON array containing the custom tags * @param export_control a JSON object containing the export_control infos * @param key the key info struct to build * @param copy if true, the string values are copied (via strdup), * if false, the string values re-use the JSON object's * string buffer (see json_object_get_string). * * @returns zero for success, a negative errno in case of an error */ int json_build_key_info(json_object *obj, json_object *custom_tags, json_object *export_control, struct ekmf_key_info *key, bool copy) { json_object *field, *label_tags = NULL; int rc = 0; if (obj == NULL || custom_tags == NULL || key == NULL || !json_object_is_type(obj, json_type_object) || !json_object_is_type(custom_tags, json_type_array)) return -EINVAL; key->label = cond_strdup(json_get_string(obj, "label"), copy); key->description = cond_strdup(json_get_string(obj, "description"), copy); key->uuid = cond_strdup(json_get_string(obj, "keyId"), copy); key->key_type = cond_strdup(json_get_string(obj, "type"), copy); key->algorithm = cond_strdup(json_get_string(obj, "algorithm"), copy); if (json_object_object_get_ex(obj, "length", &field) && json_object_is_type(field, json_type_int)) key->key_size = json_object_get_int(field); else rc = -EBADMSG; key->state = cond_strdup(json_get_string(obj, "state"), copy); key->keystore_type = cond_strdup(json_get_string(obj, "keystoreType"), copy); if (json_object_object_get_ex(obj, "template", &field) && json_object_is_type(field, json_type_object)) { key->template = cond_strdup(json_get_string(field, "title"), copy); key->template_uuid = cond_strdup(get_uuid_from_href( json_get_string(field, "href")), copy); } else { rc = -EBADMSG; } key->activate_on = cond_strdup(json_get_string(obj, "activationDate"), copy); key->expires_on = cond_strdup(json_get_string(obj, "expirationDate"), copy); key->created_on = cond_strdup(json_get_string(obj, "createdOn"), copy); key->updated_on = cond_strdup(json_get_string(obj, "updatedOn"), copy); if (rc != 0 || key->label == NULL || key->uuid == NULL || key->algorithm == NULL || key->state == NULL || key->keystore_type == NULL || key->template == NULL || key->template_uuid == NULL || key->activate_on == NULL || key->expires_on == NULL || key->created_on == NULL || key->updated_on == NULL) { rc = (rc != 0 ? rc : -ENOMEM); goto out; } json_object_object_get_ex(obj, "labelTags", &label_tags); rc = json_build_tag_list(label_tags, &key->label_tags, copy); if (rc != 0) goto out; rc = json_build_tag_list(custom_tags, &key->custom_tags, copy); if (rc != 0) goto out; rc = json_build_export_control(export_control, &key->export_control, copy); if (rc != 0) goto out; out: if (rc != 0) { free_tag_list(&key->label_tags, copy); free_tag_list(&key->custom_tags, copy); free_export_control(&key->export_control, copy); if (copy) free_key_info(key); } return rc; } /** * Clones (copies) a key info structure * * @param src the source key info structure * @param dest the destination key info structure * * @returns zero for success, a negative errno in case of an error */ int clone_key_info(const struct ekmf_key_info *src, struct ekmf_key_info *dest) { int rc; if (src == NULL || dest == NULL) return -EINVAL; dest->label = cond_strdup(src->label, true); dest->description = cond_strdup(src->description, true); dest->uuid = cond_strdup(src->uuid, true); dest->key_type = cond_strdup(src->key_type, true); dest->algorithm = cond_strdup(src->algorithm, true); dest->key_size = src->key_size; dest->state = cond_strdup(src->state, true); dest->keystore_type = cond_strdup(src->keystore_type, true); dest->template = cond_strdup(src->template, true); dest->template_uuid = cond_strdup(src->template_uuid, true); dest->activate_on = cond_strdup(src->activate_on, true); dest->expires_on = cond_strdup(src->expires_on, true); dest->created_on = cond_strdup(src->created_on, true); dest->updated_on = cond_strdup(src->updated_on, true); if (dest->label == NULL || dest->uuid == NULL || dest->algorithm == NULL || dest->state == NULL || dest->keystore_type == NULL || dest->template == NULL || dest->template_uuid == NULL || dest->activate_on == NULL || dest->expires_on == NULL || dest->created_on == NULL || dest->updated_on == NULL) { rc = -ENOMEM; goto out; } rc = clone_tag_list(&src->label_tags, &dest->label_tags); if (rc != 0) goto out; rc = clone_tag_list(&src->custom_tags, &dest->custom_tags); if (rc != 0) goto out; out: if (rc != 0) free_key_info(dest); return rc; } /** * Free a key info structure * * @param key the key info to free */ void free_key_info(struct ekmf_key_info *key) { if (key == NULL) return; free((char *)key->label); free((char *)key->description); free((char *)key->uuid); free((char *)key->key_type); free((char *)key->algorithm); free((char *)key->state); free((char *)key->keystore_type); free((char *)key->template); free((char *)key->template_uuid); free((char *)key->activate_on); free((char *)key->expires_on); free((char *)key->created_on); free((char *)key->updated_on); free_tag_list(&key->label_tags, true); free_tag_list(&key->custom_tags, true); free_export_control(&key->export_control, true); } /** * Finds the specified HTTP header in the list of HTTP headers. Returns a newly * allocated string containing the header value. The caller must free the string * when no longer needed. * * @param headers the list of HTTP headers * @param name the name of the header to look for. * * @returns a newly allocated string, or NULL if the header is not found */ char *get_http_header_value(const struct curl_slist *headers, const char *name) { const struct curl_slist *hdr ; char *ch; if (headers == NULL || name == NULL) return NULL; for (hdr = headers; hdr != NULL; hdr = hdr->next) { if (hdr->data == NULL) continue; ch = strchr(hdr->data, ':'); if (ch == NULL) continue; if (strncasecmp(hdr->data, name, ch - hdr->data) != 0) continue; for (ch++; *ch == ' '; ch++) ; return strdup(ch); } return NULL; } /** * Converts a JSON Web Key (ECC or RSA) into a OpenSSL PKEY * * @param jwk The JSON Web Key to convert * @param pkey_type If the JWK contains an RSA key, then the pkey_type * can be EVP_PKEY_RSA or EVP_PKEY_RSA_PSS * @param pkey On return: the OpenSSL PKEY * * @returns zero for success, a negative errno in case of an error */ int json_web_key_as_pkey(json_object *jwk, int pkey_type, EVP_PKEY **pkey) { unsigned char *x = NULL, *y = NULL, *n = NULL, *e = NULL; const struct sk_ec_curve_info *curve_info; struct sk_pub_key_info pub_key = { 0 }; size_t prime_len, len, n_len, e_len; const char *kty, *crv; int nid, rc = 0; if (jwk == NULL || pkey == NULL) return -EINVAL; *pkey = NULL; kty = json_get_string(jwk, "kty"); if (kty == NULL) { rc = -EIO; goto out; } if (strcmp(kty, "EC") == 0) { crv = json_get_string(jwk, "crv"); if (crv == NULL) { rc = -EIO; goto out; } nid = EC_curve_nist2nid(crv); if (nid == NID_undef) { rc = -EIO; goto out; } curve_info = SK_UTIL_ec_get_curve_info(nid); if (curve_info == NULL) { rc = -EIO; goto out; } prime_len = curve_info->prime_len; if (prime_len == 0) { rc = -EIO; goto out; } x = malloc(prime_len); y = malloc(prime_len); if (x == NULL || y == NULL) { rc = -ENOMEM; goto out; } len = prime_len; rc = json_object_get_base64url(jwk, "x", x, &len); if (rc != 0) goto out; if (len != prime_len) { /* RFC 7517: Must be full size of a coordinate */ rc = -EINVAL; goto out; } len = prime_len; rc = json_object_get_base64url(jwk, "y", y, &len); if (rc != 0) goto out; if (len != prime_len) { /* RFC 7517: Must be full size of a coordinate */ rc = -EINVAL; goto out; } pub_key.type = SK_KEY_TYPE_EC; pub_key.ec.curve_nid = nid; pub_key.ec.prime_len = prime_len; pub_key.ec.x = x; pub_key.ec.y = y; } else if (strcmp(kty, "RSA") == 0) { n_len = 0; rc = json_object_get_base64url(jwk, "n", NULL, &n_len); if (rc != 0) goto out; n = malloc(n_len); if (n == NULL) { rc = -ENOMEM; goto out; } rc = json_object_get_base64url(jwk, "n", n, &n_len); if (rc != 0) goto out; e_len = 0; rc = json_object_get_base64url(jwk, "e", NULL, &e_len); if (rc != 0) goto out; e = malloc(e_len); if (e == NULL) { rc = -ENOMEM; goto out; } rc = json_object_get_base64url(jwk, "e", e, &e_len); if (rc != 0) goto out; pub_key.type = SK_KEY_TYPE_RSA; pub_key.rsa.modulus = n; pub_key.rsa.modulus_len = n_len; pub_key.rsa.pub_exp = e; pub_key.rsa.pub_exp_len = e_len; } else { return -EIO; } rc = SK_OPENSSL_get_pkey(NULL, 0, &pub_key, pkey_type == EVP_PKEY_RSA_PSS, NULL, NULL, pkey, false); if (rc != 0) goto out; out: if (x != NULL) free(x); if (y != NULL) free(y); if (n != NULL) free(n); if (e != NULL) free(e); return rc; } /** * Write a secure key blob to the specified file. * * @param filename the name of the file to write to * @param key_blob the key blob to write * @param key_blob_len the size of the key blob in bytes * * @returns zero for success, a negative errno in case of an error: * -EINVAL: invalid parameter * -EIO: error during writing out the key blob * any other errno as returned by fopen */ int write_key_blob(const char *filename, unsigned char *key_blob, size_t key_blob_len) { size_t count; FILE *fp; if (filename == NULL || key_blob == NULL || key_blob_len == 0) return -EINVAL; fp = fopen(filename, "w"); if (fp == NULL) return -errno; count = fwrite(key_blob, 1, key_blob_len, fp); if (count != key_blob_len) { fclose(fp); return -EIO; } fclose(fp); return 0; } /** * Read a secure key blob from the specified file. * * @param filename the name of the file to write to * @param key_blob a buffer to read the key blob to. If NULL, then * only the size of the key blob is returned in * key_blob_len. * @param key_blob_len On entry: the size of the buffer in bytes * On return: the size of the key blob read * * @returns zero for success, a negative errno in case of an error: * -EINVAL: invalid parameter * -ERANGE: The supplied buffer is too short. key_blob_len is set to * the required size. * -EIO: error during reading in the key blob * any other errno as returned by stat or fopen */ int read_key_blob(const char *filename, unsigned char *key_blob, size_t *key_blob_len) { size_t count, size; struct stat sb; FILE *fp; if (filename == NULL || key_blob_len == NULL) return -EINVAL; if (stat(filename, &sb)) return -errno; size = sb.st_size; if (key_blob == NULL) { *key_blob_len = size; return 0; } if (size > *key_blob_len) { *key_blob_len = size; return -ERANGE; } fp = fopen(filename, "r"); if (fp == NULL) return -errno; count = fread(key_blob, 1, size, fp); if (count != size) { fclose(fp); return -EIO; } *key_blob_len = size; fclose(fp); return 0; } /** * Reads a X.509 certificate from the specified PEM file. * * @param pem_filename the name of the PEM file to read * @param cert on Return: the X.509 certificate object * * @returns zero for success, a negative errno in case of an error: * -EINVAL: invalid parameter * -EIO: error during reading in the certificate * any other errno as returned by fopen */ int read_x509_certificate(const char *pem_filename, X509 **cert) { FILE *fp; if (pem_filename == NULL || cert == NULL) return -EINVAL; fp = fopen(pem_filename, "r"); if (fp == NULL) return -errno; *cert = PEM_read_X509(fp, NULL, NULL, NULL); fclose(fp); if (*cert == NULL) return -EIO; return 0; } /** * Writes a X.509 certificate to the specified PEM file. * * @param pem_filename the name of the PEM file to write to * @param cert the X.509 certificate object to write * * @returns zero for success, a negative errno in case of an error: * -EINVAL: invalid parameter * -EIO: error during writing out the certificate * any other errno as returned by fopen */ int write_x509_certificate(const char *pem_filename, X509 *cert) { FILE *fp; int rc; if (pem_filename == NULL || cert == NULL) return -EINVAL; fp = fopen(pem_filename, "w"); if (fp == NULL) return -errno; rc = PEM_write_X509(fp, cert); fclose(fp); if (rc != 1) return -EIO; return 0; } /** * Writes a X.509 certificate signing request to the specified PEM file. * * @param pem_filename the name of the PEM file to write to * @param req the X.509 request object to write * @param new_hdr if true, output "NEW" in the PEM header lines * * @returns zero for success, a negative errno in case of an error: * -EINVAL: invalid parameter * -EIO: error during writing out the certificate * any other errno as returned by fopen */ int write_x509_request(const char *pem_filename, X509_REQ *req, bool new_hdr) { FILE *fp; int rc; if (pem_filename == NULL || req == NULL) return -EINVAL; fp = fopen(pem_filename, "w"); if (fp == NULL) return -errno; if (new_hdr) rc = PEM_write_X509_REQ_NEW(fp, req); else rc = PEM_write_X509_REQ(fp, req); fclose(fp); if (rc != 1) return -EIO; return 0; } /** * Reads a public key from the specified PEM file. * * @param pem_filename the name of the PEM file to read * @param pkey on Return: the PKEY object * * @returns zero for success, a negative errno in case of an error: * -EINVAL: invalid parameter * -EIO: error during reading in the certificate * any other errno as returned by fopen */ int read_public_key(const char *pem_filename, EVP_PKEY **pkey) { FILE *fp; if (pem_filename == NULL || pkey == NULL) return -EINVAL; fp = fopen(pem_filename, "r"); if (fp == NULL) return -errno; *pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL); fclose(fp); if (*pkey == NULL) return -EIO; return 0; } /** * Writes apublic key to the specified PEM file. * * @param pem_filename the name of the PEM file to write to * @param pkey the PKEY object to write * * @returns zero for success, a negative errno in case of an error: * -EINVAL: invalid parameter * -EIO: error during writing out the certificate * any other errno as returned by fopen */ int write_public_key(const char *pem_filename, EVP_PKEY *pkey) { FILE *fp; int rc; if (pem_filename == NULL || pkey == NULL) return -EINVAL; fp = fopen(pem_filename, "w"); if (fp == NULL) return -errno; rc = PEM_write_PUBKEY(fp, pkey); fclose(fp); if (rc != 1) return -EIO; return 0; } /** * Checks if an exact duplicate of the name entry is part of the name already. */ static bool is_duplicate_name_entry(X509_NAME *name, X509_NAME_ENTRY *entry) { X509_NAME_ENTRY *ne; int count, i; count = X509_NAME_entry_count(name); for (i = 0; i < count; i++) { ne = X509_NAME_get_entry(name, i); if (ne == NULL) break; if (OBJ_cmp(X509_NAME_ENTRY_get_object(entry), X509_NAME_ENTRY_get_object(ne)) == 0 && ASN1_STRING_cmp(X509_NAME_ENTRY_get_data(entry), X509_NAME_ENTRY_get_data(ne)) == 0) return true; } return false; } /** * Parse an array of relative distinguished names and builds an X.509 subject * name. The RDNs are created with type MBSTRING_ASC, unless utf8 is requested, * then they are created with MBSTRING_UTF8. * To create a multiple-RDS name, prepend the RDS to add to the previous RDS * with a '+' character. * * @param name the X.509 name created. If *name is not NULL, then * the RDNs are added to the existing X.509 name. * @param rdns an array of strings, each string representing an * RDN in the form '[+]type=value'. If the type is * prepended with a '+', then this RDN is added to the * previous one. * @param num_rdns number of elements in the array. * @param utf8 if true, RDNs of type MBSTRING_UTF8 are created, * otherwise type is MBSTRING_ASC is used. * * @returns zero for success, a negative errno in case of an error: * -EINVAL: invalid parameter * -EBADMSG: an RDN is not formatted correctly * -EIO: OpenSSL failed to create an X.509 name entry * -EEXIST: if one of the name entries to add is a duplicate */ int build_subject_name(X509_NAME **name, const char *rdns[], size_t num_rdns, bool utf8) { char *rdn, *type, *value; X509_NAME_ENTRY *ne; X509_NAME *n; int rc = 0; bool multi; size_t i; if (name == NULL || rdns == NULL) return -EINVAL; if (*name != NULL) n = *name; else n = X509_NAME_new(); if (n == NULL) return -ENOMEM; for (i = 0; i < num_rdns; i++) { if (rdns[i] == NULL) { rc = -EINVAL; break; } rdn = strdup(rdns[i]); if (rdn == NULL) { rc = -ENOMEM; break; } multi = (rdn[0] == '+'); type = &rdn[multi ? 1 : 0]; for (value = type; *value != '=' && *value != '\0'; value++) ; if (*value != '=') { rc = -EBADMSG; free(rdn); break; } *value = '\0'; value++; ne = X509_NAME_ENTRY_create_by_txt(NULL, type, utf8 ? MBSTRING_UTF8 : MBSTRING_ASC, (unsigned char *)value, -1); if (ne == NULL) { rc = -EBADMSG; free(rdn); break; } if (is_duplicate_name_entry(n, ne)) { rc = -EEXIST; X509_NAME_ENTRY_free(ne); free(rdn); break; } rc = X509_NAME_add_entry(n, ne, -1, multi ? -1 : 0); free(rdn); X509_NAME_ENTRY_free(ne); if (rc != 1) { rc = -EIO; break; } rc = 0; } if (rc == 0) *name = n; else if (*name == NULL) X509_NAME_free(n); return rc; } /** * Compares X509 Extensions by their nid */ static int X509_EXTENSION_compfunc(const X509_EXTENSION * const *a, const X509_EXTENSION * const *b) { return (OBJ_obj2nid(X509_EXTENSION_get_object((X509_EXTENSION *)*a)) - OBJ_obj2nid(X509_EXTENSION_get_object((X509_EXTENSION *)*b))); } /** * Parse an array of textual X.509 certificate extensions and adds them to * either an X.509 certificate signing request, or an X.509 certificate. * * When adding extensions, a check is performed if an extension with the same * nid is already added. If so, a duplicate extension is not added, even if * its value is different from the existing one. * * @param cert the X.509 certificate to add the extensions to. * Either req or cert can be specified. * @param req the X.509 certificate signing request to add the * extensions to. Either req or cert can be specified. * @param exts an array of strings, each string representing an * certificate extension in the form 'type=value'. * can be NULL if num_exts is zero. * @param num_exts number of elements in the array. * @param addl_exts a stack of extensions to add (can be NULL) * * @returns zero for success, a negative errno in case of an error: * -EINVAL: invalid parameter * -EBADMSG: an extension is not formatted correctly * -EIO: OpenSSL failed to create an X.509 extension * -EEXIST: if one of the extensions to add is a duplicate */ int build_certificate_extensions(X509 *cert, X509_REQ *req, const char *exts[], size_t num_exts, const STACK_OF(X509_EXTENSION) *addl_exts) { STACK_OF(X509_EXTENSION) *sk_ext; char *ext, *type, *value; X509V3_CTX x509v3_ctx; int count, k, rc = 0; X509_EXTENSION *ex; size_t i; if (num_exts > 0 && exts == NULL) return -EINVAL; if (cert == NULL && req == NULL) return -EINVAL; if (cert != NULL && req != NULL) return -EINVAL; sk_ext = sk_X509_EXTENSION_new_null(); if (sk_ext == NULL) return -ENOMEM; sk_X509_EXTENSION_set_cmp_func(sk_ext, X509_EXTENSION_compfunc); for (i = 0; exts != NULL && i < num_exts; i++) { if (exts[i] == NULL) { rc = -EINVAL; break; } ext = strdup(exts[i]); if (ext == NULL) { rc = -ENOMEM; break; } type = &ext[0]; for (value = type; *value != '=' && *value != '\0'; value++) ; if (*value != '=') { rc = -EBADMSG; free(ext); break; } *value = '\0'; value++; rc = -EBADMSG; ex = X509V3_EXT_conf(NULL, NULL, type, value); if (ex != NULL) { if (sk_X509_EXTENSION_find(sk_ext, ex) >= 0) { rc = -EEXIST; free(ext); break; } rc = sk_X509_EXTENSION_push(sk_ext, ex); if (rc < 1) { rc = -EIO; free(ext); break; } rc = 0; } free(ext); } if (rc != 0) goto out; if (addl_exts != NULL) { count = sk_X509_EXTENSION_num(addl_exts); for (k = 0; k < count; k++) { ex = sk_X509_EXTENSION_value(addl_exts, k); if (ex != NULL) { if (sk_X509_EXTENSION_find(sk_ext, ex) >= 0) { rc = -EEXIST; break; } rc = sk_X509_EXTENSION_push(sk_ext, X509_EXTENSION_dup(ex)); if (rc < 1) { rc = -EIO; break; } rc = 0; } } } if (rc != 0) goto out; if (req != NULL && sk_X509_EXTENSION_num(sk_ext) > 0) { if (X509_REQ_add_extensions(req, sk_ext) != 1) rc = -EIO; sk_X509_EXTENSION_pop_free(sk_ext, X509_EXTENSION_free); sk_ext = NULL; goto out; } if (cert != NULL && sk_X509_EXTENSION_num(sk_ext) > 0) { X509V3_set_ctx_nodb(&x509v3_ctx); X509V3_set_ctx(&x509v3_ctx, cert, cert, NULL, NULL, 0); rc = 0; while ((ex = sk_X509_EXTENSION_pop(sk_ext)) != NULL) { if (rc == 0) { if (X509_add_ext(cert, ex, -1) != 1) rc = -EIO; } X509_EXTENSION_free(ex); } } out: if (sk_ext != NULL) sk_X509_EXTENSION_pop_free(sk_ext, X509_EXTENSION_free); return rc; } /** * Generates a serial number of a specified bit size by random and sets it * as serial number into the certificate. * * @param cert the certificate to set the serial number for * @param sn_bit_size the size of the serial number in bits * * @returns zero for success, a negative errno in case of an error: * -EINVAL: invalid parameter * -EIO: error during serial number generation */ int generate_x509_serial_number(X509 *cert, size_t sn_bit_size) { ASN1_INTEGER *ai = NULL; BIGNUM *bn = NULL; int rc; if (cert == NULL) return -EINVAL; bn = BN_new(); if (bn == NULL) return -ENOMEM; rc = BN_rand(bn, sn_bit_size, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY); if (rc != 1) { rc = -EIO; goto out; } ai = X509_get_serialNumber(cert); if (ai == NULL) { rc = -EIO; goto out; } if (BN_to_ASN1_INTEGER(bn, ai) == NULL) { rc = -EIO; goto out; } rc = 0; out: if (bn != NULL) BN_free(bn); return rc; } /** * Gets a String field from a JSON object. * * @param obj the JSON object * @param name the name of the String field to get * * @returns the contents of the String field or NULL. * Note: The memory returned is owned by the JSON object, and must not be freed * by the caller. It is valid until the JSON object is freed, which also * frees the memory used for the string value. */ const char *json_get_string(json_object *obj, const char *name) { json_object *field; if (!json_object_object_get_ex(obj, name, &field) || !json_object_is_type(field, json_type_string)) return NULL; return json_object_get_string(field); } /** * Gets a base64url field form a JSON object, decodes it and returns the * decoded data. * * @param obj the JSON object * @param name the name of the String field to get * @param data buffer to return the decoded data, or NULL to * return only the required buffer size * @param data_len on entry: the size of tne buffer * on exit: the size of the decoded data * @returns zero for success, a negative errno in case of an error: * -EINVAL: a function parameter is invalid * -ENOMEM: failed to allocate memory * -EIO: OpenSSL failed to calculate the y coordinate */ int json_object_get_base64url(json_object *obj, const char *name, unsigned char *data, size_t *data_len) { const char *b64; b64 = json_get_string(obj, name); if (b64 == NULL) return -ENOENT; return decode_base64url(data, data_len, b64, strlen(b64)); } /** * Base64URL encodes the data and creates a JSON string object of it * * @param data the data to base64url encode * @param len the length of the data * @returns a new JSON object, or NULL in case of an error */ json_object *json_object_new_base64url(const unsigned char *data, size_t len) { json_object *ret = NULL; char *b64 = NULL; size_t b64len; int rc; rc = encode_base64url(NULL, &b64len, data, len); if (rc != 0) goto out; b64 = malloc(b64len); if (b64 == NULL) goto out; rc = encode_base64url(b64, &b64len, data, len); if (rc != 0) goto out; ret = json_object_new_string(b64); out: if (b64 != NULL) free(b64); return ret; } #ifdef IMPLEMENT_LOCAL_JSON_OBJECT_OBJECT_ADD /** * JSON-C of version 0.12 does not have json_object_object_add_ex(), and * json_object_object_add does not return a return code, so implement * json_object_object_add_ex here instead. */ int json_object_object_add_ex(struct json_object *obj, const char *const key, struct json_object *const val, const unsigned int UNUSED(opts)) { json_object_object_add(obj, key, val); return 0; } #endif s390-tools-2.38.0/libekmfweb/utilities.h000066400000000000000000000102641502674226300177450ustar00rootroot00000000000000/* * libekmfweb - EKMFWeb client library * * Copyright IBM Corp. 2020 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef UTILITIES_H #define UTILITIES_H #include #include #include #include #include #include #include #include "ekmfweb/ekmfweb.h" int decode_base64url(unsigned char *output, size_t *outlen, const char *input, size_t inlen); int encode_base64url(char *output, size_t *outlen, const unsigned char *input, size_t inlen); int parse_json_web_token(const char *token, json_object **header_obj, json_object **payload_obj, unsigned char **signature, size_t *signature_len); int create_json_web_signature(const char *algorithm, bool b64, const char *kid, const unsigned char *payload, size_t payload_len, bool detached_payload, EVP_MD_CTX *md_ctx, char **jws); int verify_json_web_signature(const char *jws, const unsigned char *payload, size_t payload_len, EVP_PKEY *pkey); json_object *get_json_timestamp(void); int json_build_tag_def_list(json_object *array, struct ekmf_tag_def_list *tag_def_list, bool copy); int clone_tag_def_list(const struct ekmf_tag_def_list *src, struct ekmf_tag_def_list *dest); void free_tag_def_list(struct ekmf_tag_def_list *tag_def_list, bool free_tags); int json_build_template_info(json_object *obj, struct ekmf_template_info *template, bool copy); int clone_template_info(const struct ekmf_template_info *src, struct ekmf_template_info *dest); void free_template_info(struct ekmf_template_info *template); int json_build_tag_list(json_object *array, struct ekmf_tag_list *tag_list, bool copy); int build_json_tag_list(const struct ekmf_tag_list *tag_list, json_object **tags_obj); int clone_tag_list(const struct ekmf_tag_list *src, struct ekmf_tag_list *dest); void free_tag_list(struct ekmf_tag_list *tag_list, bool free_tags); int json_build_export_control(json_object *export_control, struct ekmf_export_control *export_info, bool copy); int clone_export_control(const struct ekmf_export_control *src, struct ekmf_export_control *dest); void free_export_control(struct ekmf_export_control *export_control, bool free_keys); int json_build_key_info(json_object *obj, json_object *custom_tags, json_object *export_control, struct ekmf_key_info *key, bool copy); int clone_key_info(const struct ekmf_key_info *src, struct ekmf_key_info *dest); void free_key_info(struct ekmf_key_info *key); char *get_http_header_value(const struct curl_slist *headers, const char *name); int json_web_key_as_pkey(json_object *jwk, int pkey_type, EVP_PKEY **pkey); int write_key_blob(const char *filename, unsigned char *key_blob, size_t key_blob_len); int read_key_blob(const char *filename, unsigned char *key_blob, size_t *key_blob_len); int read_x509_certificate(const char *pem_filename, X509 **cert); int write_x509_certificate(const char *pem_filename, X509 *cert); int write_x509_request(const char *pem_filename, X509_REQ *req, bool new_hdr); int read_public_key(const char *pem_filename, EVP_PKEY **pkey); int write_public_key(const char *pem_filename, EVP_PKEY *pkey); int build_subject_name(X509_NAME **name, const char *rdns[], size_t num_rdns, bool utf8); int build_certificate_extensions(X509 *cert, X509_REQ *req, const char *exts[], size_t num_exts, const STACK_OF(X509_EXTENSION) *addl_exts); int generate_x509_serial_number(X509 *cert, size_t sn_bit_size); const char *json_get_string(json_object *obj, const char *name); int json_object_get_base64url(json_object *obj, const char *name, unsigned char *data, size_t *data_len); json_object *json_object_new_base64url(const unsigned char *data, size_t len); #ifndef JSON_C_OBJECT_ADD_KEY_IS_NEW #define JSON_C_OBJECT_ADD_KEY_IS_NEW (1 << 1) #define IMPLEMENT_LOCAL_JSON_OBJECT_OBJECT_ADD int json_object_object_add_ex(struct json_object *obj, const char *const key, struct json_object *const val, const unsigned int opts); #endif #endif s390-tools-2.38.0/libkmipclient/000077500000000000000000000000001502674226300162755ustar00rootroot00000000000000s390-tools-2.38.0/libkmipclient/Makefile000066400000000000000000000132661502674226300177450ustar00rootroot00000000000000include ../common.mak VERSION = 1.0 VERM = $(shell echo $(VERSION) | cut -d '.' -f 1) ifneq (${HAVE_OPENSSL},0) ifneq (${HAVE_JSONC},0) ifneq (${HAVE_LIBXML2},0) ifneq (${HAVE_LIBCURL},0) BUILD_TARGETS += libkmipclient.so.$(VERSION) INSTALL_TARGETS += install-libkmipclient.so.$(VERSION) else BUILD_TARGETS += skip-libkmipclient-curl INSTALL_TARGETS += skip-libkmipclient-curl endif else BUILD_TARGETS += skip-libkmipclient-xml INSTALL_TARGETS += skip-libkmipclient-xml endif else BUILD_TARGETS += skip-libkmipclient-jsonc INSTALL_TARGETS += skip-libkmipclient-jsonc endif else BUILD_TARGETS += skip-libkmipclient-openssl INSTALL_TARGETS += skip-libkmipclient-openssl endif TMPFILE := $(shell mktemp) detect-openssl-version.dep: echo "#include " > $(TMPFILE) echo "#include " >> $(TMPFILE) echo "#ifndef OPENSSL_VERSION_PREREQ" >> $(TMPFILE) echo " #if defined(OPENSSL_VERSION_MAJOR) && defined(OPENSSL_VERSION_MINOR)" >> $(TMPFILE) echo " #define OPENSSL_VERSION_PREREQ(maj, min) \\" >> $(TMPFILE) echo " ((OPENSSL_VERSION_MAJOR << 16) + \\" >> $(TMPFILE) echo " OPENSSL_VERSION_MINOR >= ((maj) << 16) + (min))" >> $(TMPFILE) echo " #else" >> $(TMPFILE) echo " #define OPENSSL_VERSION_PREREQ(maj, min) \\" >> $(TMPFILE) echo " (OPENSSL_VERSION_NUMBER >= (((maj) << 28) | \\" >> $(TMPFILE) echo " ((min) << 20)))" >> $(TMPFILE) echo " #endif" >> $(TMPFILE) echo "#endif" >> $(TMPFILE) echo "#if !OPENSSL_VERSION_PREREQ(1, 1)" >> $(TMPFILE) echo " #error openssl version 1.1 is required" >> $(TMPFILE) echo "#endif" >> $(TMPFILE) echo "static void __attribute__((unused)) test(void) {" >> $(TMPFILE) echo " EVP_PKEY_meth_remove(NULL);" >> $(TMPFILE) echo "}" >> $(TMPFILE) mv $(TMPFILE) $@ CURL_CONFIG ?= curl-config check-dep-libkmipclient: detect-openssl-version.dep $(call check_dep, \ "libkmipclient", \ "detect-openssl-version.dep", \ "openssl-devel version >= 1.1.1", \ "HAVE_OPENSSL=0", \ -I. `$(PKG_CONFIG) --cflags --libs libcrypto` -DOPENSSL_SUPPRESS_DEPRECATED) $(call check_dep, \ "libkmipclient", \ "json-c/json.h", \ "json-c-devel", \ "HAVE_JSONC=0") $(call check_dep, \ "libkmipclient", \ "libxml/tree.h", \ "libxml2-devel", \ "HAVE_LIBXML2=0", \ `$(PKG_CONFIG) --cflags --libs libxml-2.0`) $(call check_dep, \ "libkmipclient", \ "curl/curl.h", \ "libcurl-devel", \ "HAVE_LIBCURL=0" \ `$(PKG_CONFIG) --cflags --libs libcurl`) $(CURL_CONFIG) --ssl-backends | grep OpenSSL >/dev/null 2>&1 || { echo "Error: libcurl is not built with the OpenSSL backend"; exit 1; } touch check-dep-libkmipclient skip-libkmipclient-openssl: echo " SKIP libkmipclient due to HAVE_OPENSSL=0" skip-libkmipclient-jsonc: echo " SKIP libkmipclient due to HAVE_JSONC=0" skip-libkmipclient-xml: echo " SKIP libkmipclient due to HAVE_LIBXML2=0" skip-libkmipclient-curl: echo " SKIP libkmipclient due to HAVE_LIBCURL=0" all: $(BUILD_TARGETS) kmip.o: check-dep-libkmipclient kmip.c kmip.h utils.h $(rootdir)include/kmipclient/kmipclient.h request.o: check-dep-libkmipclient request.c kmip.h names.h $(rootdir)include/kmipclient/kmipclient.h response.o: check-dep-libkmipclient response.c kmip.h names.h $(rootdir)include/kmipclient/kmipclient.h attribute.o: check-dep-libkmipclient attribute.c kmip.h names.h $(rootdir)include/kmipclient/kmipclient.h key.o: check-dep-libkmipclient key.c kmip.h names.h $(rootdir)include/kmipclient/kmipclient.h ttlv.o: check-dep-libkmipclient ttlv.c kmip.h utils.h $(rootdir)include/kmipclient/kmipclient.h json.o: check-dep-libkmipclient json.c kmip.h names.h utils.h $(rootdir)include/kmipclient/kmipclient.h xml.o: check-dep-libkmipclient xml.c kmip.h names.h utils.h $(rootdir)include/kmipclient/kmipclient.h https.o: check-dep-libkmipclient https.c kmip.h utils.h $(rootdir)include/kmipclient/kmipclient.h tls.o: check-dep-libkmipclient tls.c kmip.h utils.h $(rootdir)include/kmipclient/kmipclient.h names.o: check-dep-libkmipclient names.c names.h utils.h $(rootdir)include/kmipclient/kmipclient.h utils.o: check-dep-libkmipclient utils.c names.h utils.h $(rootdir)include/kmipclient/kmipclient.h libkmipclient.so.$(VERSION): ALL_CFLAGS += -fPIC `$(PKG_CONFIG) --cflags json-c libcrypto libssl libxml-2.0 libcurl` libkmipclient.so.$(VERSION): LDLIBS = `$(PKG_CONFIG) --libs json-c libcrypto libssl libxml-2.0 libcurl` libkmipclient.so.$(VERSION): ALL_LDFLAGS += -shared -Wl,--version-script=libkmipclient.map \ -Wl,-z,defs,-Bsymbolic -Wl,-soname,libkmipclient.so.$(VERM) libkmipclient.so.$(VERSION): kmip.o request.o response.o attribute.o key.o ttlv.o json.o \ xml.o https.o tls.o names.o utils.o $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@ ln -srf libkmipclient.so.$(VERSION) libkmipclient.so.$(VERM) ln -srf libkmipclient.so.$(VERSION) libkmipclient.so install-libkmipclient.so.$(VERSION): libkmipclient.so.$(VERSION) $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 -T libkmipclient.so.$(VERSION) $(DESTDIR)$(SOINSTALLDIR)/libkmipclient.so.$(VERSION) ln -srf $(DESTDIR)$(SOINSTALLDIR)/libkmipclient.so.$(VERSION) $(DESTDIR)$(SOINSTALLDIR)/libkmipclient.so.$(VERM) ln -srf $(DESTDIR)$(SOINSTALLDIR)/libkmipclient.so.$(VERSION) $(DESTDIR)$(SOINSTALLDIR)/libkmipclient.so $(INSTALL) -d -m 755 $(DESTDIR)$(USRINCLUDEDIR)/kmipclient $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 $(rootdir)include/kmipclient/kmipclient.h $(DESTDIR)$(USRINCLUDEDIR)/kmipclient install: all $(INSTALL_TARGETS) clean: rm -f *.o libkmipclient.so* check-dep-libkmipclient detect-openssl-version.dep .PHONY: all install clean skip-libkmipclient-openssl skip-libkmipclient-jsonc \ skip-libkmipclient-xml skip-libkmipclient-curl install-libkmipclient.so.$(VERSION) s390-tools-2.38.0/libkmipclient/attribute.c000066400000000000000000003617011502674226300204540ustar00rootroot00000000000000/* * libkmipclient - KMIP client library * * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #define _XOPEN_SOURCE #define _DEFAULT_SOURCE #include #include #include #include #include "kmip.h" #include "names.h" /** * Constructs a Template Attribute node (KMIP v1.x): * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Template-Attribute Yes Structure v1.x only * Name No Structure v1.x only * ... may be repeated * Attribute No Structure v1.x only * ... may be repeated * * Also applies to Common Template-Attribute, Private Key Template-Attribute, * Public Key Template-Attribute. * * @param tag the template-attribute tag * @param num_names the number of names in the array (can be 0) * @param names array of name nodes * @param num_attrs the number of attributes in the array (can be 0) * @param attrs array of attribute nodes * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ static struct kmip_node *kmip_new_template_attribute_v1( enum kmip_tag tag, unsigned int num_names, struct kmip_node **names, unsigned int num_attrs, struct kmip_node **attrs) { struct kmip_node *tmpl; unsigned int i; int rc; if (num_names > 0 && names == NULL) return NULL; if (num_attrs > 0 && attrs == NULL) return NULL; switch (tag) { case KMIP_TAG_TEMPLATE_ATTRIBUTE: case KMIP_TAG_COMMON_TEMPLATE_ATTRIBUTE: case KMIP_TAG_PRIVATE_KEY_TEMPLATE_ATTRIBUTE: case KMIP_TAG_PUBLIC_KEY_TEMPLATE_ATTRIBUTE: break; default: return NULL; } tmpl = kmip_node_new_structure_va(KMIP_TAG_TEMPLATE_ATTRIBUTE, NULL, 0); if (tmpl == NULL) return NULL; for (i = 0; i < num_names; i++) { rc = kmip_node_add_structure_element(tmpl, names[i]); if (rc != 0) goto error; } for (i = 0; i < num_attrs; i++) { rc = kmip_node_add_structure_element(tmpl, attrs[i]); if (rc != 0) goto error; } return tmpl; error: kmip_node_free(tmpl); return NULL; } /** * Gets information from a Template Attribute node (KMIP v1.x): * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Template-Attribute Yes Structure v1.x only * Name No Structure v1.x only * ... may be repeated * Attribute No Structure v1.x only * ... may be repeated * * Also applies to Common Template-Attribute, Private Key Template-Attribute, * Public Key Template-Attribute. * * @param node the KMIP node * @param num_names On return: The number of names (can be NULL) * @param name_index the index of the name item to return * @param name On return: the name item of the specified index. * Function returns -ENOENT if no name is available. * Can be NULL, then no name entry is returned. * @param num_attrs On return: The number of attributes (can be NULL) * @param attr_index the index of the attribute item to return * @param attr On return: the attribute item of the specified * index. Function returns -ENOENT if no attribute is * available. Can be NULL, then no attribute entry is * returned. * * @returns 0 on success, or a negative errno in case of an error. * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ static int kmip_get_template_attribute_v1(const struct kmip_node *node, unsigned int *num_names, unsigned int name_index, struct kmip_node **name, unsigned int *num_attrs, unsigned int attr_index, struct kmip_node **attr) { if (node == NULL) return -EINVAL; switch (kmip_node_get_tag(node)) { case KMIP_TAG_TEMPLATE_ATTRIBUTE: case KMIP_TAG_COMMON_TEMPLATE_ATTRIBUTE: case KMIP_TAG_PRIVATE_KEY_TEMPLATE_ATTRIBUTE: case KMIP_TAG_PUBLIC_KEY_TEMPLATE_ATTRIBUTE: break; default: return -EBADMSG; } if (num_names != NULL) *num_names = kmip_node_get_structure_element_by_tag_count( node, KMIP_TAG_NAME); if (name != NULL) { *name = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_NAME, name_index); if (*name == NULL) return -ENOENT; } if (num_attrs != NULL) *num_attrs = kmip_node_get_structure_element_by_tag_count( node, KMIP_TAG_ATTRIBUTE); if (attr != NULL) { *attr = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_ATTRIBUTE, attr_index); if (*attr == NULL) { if (name != NULL && *name != NULL) { kmip_node_free(*name); *name = NULL; } return -ENOENT; } } return 0; } /** * Constructs an Attribute node (KMIP v1.x): * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Attribute Yes Structure v1.x only * Attribute Name Yes Text String v1.x only * Attribute Index No Integer v1.x only * Attribute Value Yes v1.x only * * @param name the name of the attribute * @param index the index of the attribute. If < 0 then this field * is omitted * @param value the attribute value node * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ static struct kmip_node *kmip_new_attribute_v1(const char *name, int32_t index, struct kmip_node *value) { struct kmip_node *attr = NULL, *nam, *idx = NULL; if (name == NULL || value == NULL) return NULL; nam = kmip_node_new_text_string(KMIP_TAG_ATTRIBUTE_NAME, NULL, name); if (index >= 0) idx = kmip_node_new_integer(KMIP_TAG_ATTRIBUTE_INDEX, NULL, index); if (nam == NULL || (index >= 0 && idx == NULL)) goto out; attr = kmip_node_new_structure_va(KMIP_TAG_ATTRIBUTE, NULL, 3, nam, idx, value); out: kmip_node_free(nam); kmip_node_free(idx); return attr; } /** * Gets the information from an Attribute node (KMIP v1.x): * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Attribute Yes Structure v1.x only * Attribute Name Yes Text String v1.x only * Attribute Index No Integer v1.x only * Attribute Value Yes v1.x only * * @param node the KMIP node * @param name On return: the attribute name (can be NULL) * @param index On return: the attribute index (can be NULL) * @param value On return: the attribute value (can be NULL) * * @returns 0 on success, or a negative errno in case of an error * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ static int kmip_get_attribute_v1(const struct kmip_node *node, const char **name, int32_t *index, struct kmip_node **value) { struct kmip_node *nam, *idx, *val; int rc = 0; if (node == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_ATTRIBUTE) return -EBADMSG; nam = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_ATTRIBUTE_NAME, 0); idx = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_ATTRIBUTE_INDEX, 0); val = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_ATTRIBUTE_VALUE, 0); if (nam == NULL || val == NULL) { rc = -EBADMSG; goto out; } if (name != NULL) *name = kmip_node_get_text_string(nam); if (index != NULL) *index = (idx != NULL ? kmip_node_get_integer(idx) : 0); if (value != NULL) *value = val; out: kmip_node_free(nam); kmip_node_free(idx); if (value == NULL || rc != 0) kmip_node_free(val); return rc; } /** * Constructs a (Vendor) Attribute node (KMIP v2.x): * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Attribute Yes Structure v2.x only * Vendor Identification Yes Text String v2.x only * Attribute Name Yes Text String v2.x only * Attribute Value Yes v2.x only * * @param vendor_id the vendor identification of the attribute * @param name the name of the attribute * @param value the attribute value node * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_vendor_attribute(const char *vendor_id, const char *name, struct kmip_node *value) { struct kmip_node *attr = NULL, *nam, *vend = NULL; if (vendor_id == NULL || name == NULL || value == NULL) return NULL; vend = kmip_node_new_text_string(KMIP_TAG_VENDOR_IDENTIFICATION, NULL, vendor_id); nam = kmip_node_new_text_string(KMIP_TAG_ATTRIBUTE_NAME, NULL, name); if (nam == NULL || vend == NULL) goto out; attr = kmip_node_new_structure_va(KMIP_TAG_ATTRIBUTE, NULL, 3, vend, nam, value); out: kmip_node_free(nam); kmip_node_free(vend); return attr; } /** * Gets the information from a (Vendor) Attribute node (KMIP v2.x): * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Attribute Yes Structure v2.x only * Vendor Identification Yes Text String v2.x only * Attribute Name Yes Text String v2.x only * Attribute Value Yes v2.x only * * @param node the KMIP node * @param vendor_id On return: the vendor identification (can be NULL) * @param name On return: the attribute name (can be NULL) * @param value On return: the attribute value (can be NULL) * * @returns 0 on success, or a negative errno in case of an error * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ int kmip_get_vendor_attribute(const struct kmip_node *node, const char **vendor_id, const char **name, struct kmip_node **value) { struct kmip_node *vend, *nam, *val; int rc = 0; if (node == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_ATTRIBUTE) return -EBADMSG; vend = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_VENDOR_IDENTIFICATION, 0); nam = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_ATTRIBUTE_NAME, 0); val = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_ATTRIBUTE_VALUE, 0); if (vend == NULL || nam == NULL || val == NULL) { rc = -EBADMSG; goto out; } if (vendor_id != NULL) *vendor_id = kmip_node_get_text_string(vend); if (name != NULL) *name = kmip_node_get_text_string(nam); if (value != NULL) *value = val; out: kmip_node_free(vend); kmip_node_free(nam); if (value == NULL || rc != 0) kmip_node_free(val); return rc; } /** * Constructs an Attributes node (KMIP v2.x): * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Attributes Yes Structure v2.x only * No v2.x only * ... may be repeated * * Also applies to Common Attributes, Private Key Attributes, * Public Key Attributes * * @param tag the attributes tag * @param attrs_count the number of attributes following (can be 0) * @param attrs the array of attributes * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ static struct kmip_node *kmip_new_attributes_v2(enum kmip_tag tag, unsigned int attrs_count, struct kmip_node **attrs) { switch (tag) { case KMIP_TAG_ATTRIBUTES: case KMIP_TAG_COMMON_ATTRIBUTES: case KMIP_TAG_PRIVATE_KEY_ATTRIBUTES: case KMIP_TAG_PUBLIC_KEY_ATTRIBUTES: break; default: return NULL; } return kmip_node_new_structure(KMIP_TAG_ATTRIBUTES, NULL, attrs_count, attrs); } /** * Gets the information from an Attributes node (KMIP v2.x): * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Attributes Yes Structure v2.x only * No v2.x only * ... may be repeated * * Also applies to Common Attributes, Private Key Attributes, * Public Key Attributes * * @param node the KMIP node * @param num_attrs On return: The number of attributes (can be NULL) * @param attr_index the index of the attribute to return * @param value On return: the attribute item of the specified * index. Function returns -ENOENT if no attribute is * available. Can be NULL, then no attribute entry is * returned. * * @returns 0 on success, or a negative errno in case of an error * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ static int kmip_get_attributes_v2(const struct kmip_node *node, unsigned int *num_attrs, unsigned int attr_index, struct kmip_node **attr) { if (node == NULL) return -EINVAL; switch (kmip_node_get_tag(node)) { case KMIP_TAG_ATTRIBUTES: case KMIP_TAG_COMMON_ATTRIBUTES: case KMIP_TAG_PRIVATE_KEY_ATTRIBUTES: case KMIP_TAG_PUBLIC_KEY_ATTRIBUTES: break; default: return -EBADMSG; } if (num_attrs != NULL) *num_attrs = kmip_node_get_structure_element_count(node); if (attr == NULL) return 0; *attr = kmip_node_get_structure_element_by_index(node, attr_index); if (*attr == NULL) return -ENOENT; return 0; } /** * Split a KMIP v1.x custom attribute name into a vendor-id and attribute * name for a KMIP v2.x vendor attribute. * * @param name the custom attribute name. This string is being * modified during splitting. If the contents is still * needed, the caller should copy it first. * @param vendor_id On return: the vendor-ID string. This point to * inside the passed name string from the 1st argument. * @param attr_name On return: the vendor attribute name string. * This point to inside the passed name string from * the 1st argument. * * @returns 0 on success, or a negative errno in case of an error */ static int kmip_split_v1_custom_attr_name(char *name, char **vendor_id, char **attr_name) { char *tok; if (name == NULL || vendor_id == NULL || attr_name == NULL) return -EINVAL; /* * KMIP v1.x custom attribute names in the form 'x|y--' * are transformed into a KMIP v2.x vendor attribute with vendor id * and name . If no vendor id is found, then the vendor * id is set to 'x' or 'y', and the name is the remaining name string. */ if (strncmp(name, "x-", 2) != 0 && strncmp(name, "y-", 2) != 0) return -EBADMSG; name[1] = 0; tok = strchr(name + 2, '-'); if (tok != NULL) { *tok = 0; *vendor_id = name + 2; *attr_name = tok + 1; } else { *vendor_id = name; *attr_name = name + 2; } return 0; } /** * Builds a KMIP v1.x custom attribute name from a KMIP v2.x vendor-id and * attribute name. * * @param vendor_id the vendor-ID string * @param attr_name the vendor attribute name string * * @returns a newly allocated custom attribute name string, or NULL in case of * an error. The returned string must be freed by the caller. */ char *kmip_build_v1_custom_attr_name(const char *vendor_id, const char *attr_name) { char *custom_name = NULL; int rc; if (vendor_id == NULL || attr_name == NULL) return NULL; if (strcmp(vendor_id, "x") == 0 || strcmp(vendor_id, "y") == 0) rc = asprintf(&custom_name, "%s-%s", vendor_id, attr_name); else rc = asprintf(&custom_name, "x-%s-%s", vendor_id, attr_name); if (rc <= 0 || custom_name == NULL) return NULL; return custom_name; } /** * Converts a KMIP v1.x Attribute into a KMIP v2.x Attribute * * @param v1_attr the KMIP v1.x attribute to convert * @param v2_attr On return: the KMIP v2.x attribute * * @returns 0 on success, or a negative errno in case of an error */ int kmip_v2_attr_from_v1_attr(struct kmip_node *v1_attr, struct kmip_node **v2_attr) { char *copy, *vendor_id, *attr_name; struct kmip_node *value, *cloned_value; enum kmip_tag v2_tag; const char *name; int rc; if (v1_attr == NULL || v2_attr == NULL) return -EINVAL; rc = kmip_get_attribute_v1(v1_attr, &name, NULL, &value); if (rc != 0) return rc; if (strncmp(name, "x-", 2) == 0 || strncmp(name, "y-", 2) == 0) { /* Special handling for Custom Attribute */ copy = strdup(name); if (copy == NULL) { kmip_node_free(value); return -ENOMEM; } rc = kmip_split_v1_custom_attr_name(copy, &vendor_id, &attr_name); if (rc != 0) { kmip_node_free(value); free(copy); return rc; } cloned_value = kmip_node_clone(value); kmip_node_free(value); if (cloned_value == NULL) { free(copy); return -ENOMEM; } *v2_attr = kmip_new_vendor_attribute(vendor_id, attr_name, cloned_value); free(copy); kmip_node_free(cloned_value); return 0; } v2_tag = kmip_attr_tag_by_v1_attr_name(name); if (v2_tag == 0) { kmip_node_free(value); return -EBADMSG; } cloned_value = kmip_node_clone(value); kmip_node_free(value); if (cloned_value == NULL) return -ENOMEM; cloned_value->tag = v2_tag; *v2_attr = cloned_value; return 0; } /** * Converts a KMIP v2.x Attribute into a KMIP v1.x Attribute * * @param v2_attr the KMIP v2.x attribute to convert * @param v1_attr On return: the KMIP v1.x attribute * * @returns 0 on success, or a negative errno in case of an error */ int kmip_v1_attr_from_v2_attr(struct kmip_node *v2_attr, struct kmip_node **v1_attr) { struct kmip_node *attr_value, *cloned_value; const char *attr_name, *vendor_id; char *custom_name; int rc; if (v2_attr == NULL || v1_attr == NULL) return -EINVAL; if (v2_attr->tag == KMIP_TAG_ATTRIBUTE) { /* Special handling for v2.x Vendor Attribute */ rc = kmip_get_vendor_attribute(v2_attr, &vendor_id, &attr_name, &attr_value); if (rc != 0) return rc; custom_name = kmip_build_v1_custom_attr_name(vendor_id, attr_name); if (custom_name == NULL) { kmip_node_free(attr_value); return -EBADMSG; } cloned_value = kmip_node_clone(attr_value); kmip_node_free(attr_value); if (cloned_value == NULL) { free(custom_name); return -ENOMEM; } *v1_attr = kmip_new_attribute_v1(custom_name, -1, cloned_value); kmip_node_free(cloned_value); free(custom_name); if (*v1_attr == NULL) return -ENOMEM; return 0; } attr_name = kmip_v1_attr_name_by_tag(v2_attr->tag); if (attr_name == NULL) return -EBADMSG; cloned_value = kmip_node_clone(v2_attr); if (cloned_value == NULL) return -ENOMEM; /* Modify the cloned v2 attr and use it as value of the v1 attr */ cloned_value->tag = KMIP_TAG_ATTRIBUTE_VALUE; *v1_attr = kmip_new_attribute_v1(attr_name, -1, cloned_value); kmip_node_free(cloned_value); if (*v1_attr == NULL) return -ENOMEM; return 0; } /** * Constructs an Attributes node (KMIP v2.x) or a Template Attribute node * (KMIP v1.x) from a list of attributes in KMIP v2.x style, dependent on the * protocol version specified: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Attributes Yes Structure v2.x only * No v2.x only * ... may be repeated * * Also applies to Common Attributes, Private Key Attributes, * Public Key Attributes * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Template-Attribute Yes Structure v1.x only * Name No Structure v1.x only * ... may be repeated * Attribute No Structure v1.x only * ... may be repeated * * Also applies to Common Template-Attribute, Private Key Template-Attribute, * Public Key Template-Attribute. * * @param version the protocol version. If null, the current default * protocol version is used. * @param v2_tag the attributes tag * @param attrs_count the number of attributes following * @param v2_attrs the array of attributes (as KMIP v2.x attributes) * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_attributes(const struct kmip_version *version, enum kmip_tag v2_tag, unsigned int attrs_count, struct kmip_node **v2_attrs) { struct kmip_node *attrs = NULL, *attr; struct kmip_node **v1_attrs = NULL; enum kmip_tag v1_tag = 0; unsigned int i; int rc; if (version == NULL) version = kmip_get_default_protocol_version(); if (version->major == 1) { /* KMIP v1.x: translate v1-tag to v2-tag */ switch (v2_tag) { case KMIP_TAG_ATTRIBUTES: v1_tag = KMIP_TAG_TEMPLATE_ATTRIBUTE; break; case KMIP_TAG_COMMON_ATTRIBUTES: v1_tag = KMIP_TAG_COMMON_TEMPLATE_ATTRIBUTE; break; case KMIP_TAG_PRIVATE_KEY_ATTRIBUTES: v1_tag = KMIP_TAG_PRIVATE_KEY_TEMPLATE_ATTRIBUTE; break; case KMIP_TAG_PUBLIC_KEY_ATTRIBUTES: v1_tag = KMIP_TAG_PUBLIC_KEY_TEMPLATE_ATTRIBUTE; break; default: return NULL; } if (attrs_count > 0) { v1_attrs = calloc(attrs_count, sizeof(struct kmip_node *)); if (v1_attrs == NULL) return NULL; } for (i = 0; i < attrs_count; i++) { attr = v2_attrs[i]; if (attr == NULL) goto error; rc = kmip_v1_attr_from_v2_attr(attr, &v1_attrs[i]); if (rc != 0) goto error; } attrs = kmip_new_template_attribute_v1(v1_tag, 0, NULL, attrs_count, v1_attrs); error: for (i = 0; i < attrs_count; i++) { if (v1_attrs[i] == NULL) continue; kmip_node_free(v1_attrs[i]); } if (v1_attrs != NULL) free(v1_attrs); } else { /* KMIP >= v2.0 */ attrs = kmip_new_attributes_v2(v2_tag, attrs_count, v2_attrs); } return attrs; } /** * Constructs an Attributes node (KMIP v2.x) or a Template Attribute node * (KMIP v1.x) from a list of attributes in KMIP v2.x style, dependent on the * protocol version specified: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Attributes Yes Structure v2.x only * No v2.x only * ... may be repeated * * Also applies to Common Attributes, Private Key Attributes, * Public Key Attributes * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Template-Attribute Yes Structure v1.x only * Name No Structure v1.x only * ... may be repeated * Attribute No Structure v1.x only * ... may be repeated * * Also applies to Common Template-Attribute, Private Key Template-Attribute, * Public Key Template-Attribute. * * @param version the protocol version. If null, the current default * protocol version is used. * @param v2_tag the attributes tag * @param attrs_count the number of attributes following * @param the attributes (as KMIP v2.x attribute) * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_attributes_va(const struct kmip_version *version, enum kmip_tag v2_tag, unsigned int attrs_count, ...) { struct kmip_node *ret, **attrs = NULL; unsigned int i, k; va_list ap; if (attrs_count > 0) { attrs = calloc(attrs_count, sizeof(struct kmip_node *)); if (attrs == NULL) return NULL; } va_start(ap, attrs_count); for (i = 0, k = 0; i < attrs_count; i++) { attrs[k] = va_arg(ap, struct kmip_node *); if (attrs[k] != NULL) k++; } va_end(ap); ret = kmip_new_attributes(version, v2_tag, k, attrs); if (attrs != NULL) free(attrs); return ret; } /** * Gets the information from an Attributes node (KMIP v2.x) or a Template * Attribute node (KMIP v1.x). The returned attribute is always in KMIP v2.x * style. * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Attributes Yes Structure v2.x only * No v2.x only * ... may be repeated * * Also applies to Common Attributes, Private Key Attributes, * Public Key Attributes * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Template-Attribute Yes Structure v1.x only * Name No Structure v1.x only * ... may be repeated * Attribute No Structure v1.x only * ... may be repeated * * Also applies to Common Template-Attribute, Private Key Template-Attribute, * Public Key Template-Attribute. * * @param node the KMIP node * @param num_attrs On return: the number of attributes (can be NULL). * @param attr_index the index of the attribute to return * @param value On return: the attribute item of the specified index * (as a KMIP v2.x attribute). * Function returns -ENOENT if no attribute is * available. Can be NULL, then no attribute entry is * returned. * * @returns 0 on success, or a negative errno in case of an error * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ int kmip_get_attributes(const struct kmip_node *node, unsigned int *num_attrs, unsigned int attr_index, struct kmip_node **attr) { struct kmip_node *v1_attr, *v2_attr; int rc; if (node == NULL) return -EINVAL; switch (kmip_node_get_tag(node)) { case KMIP_TAG_TEMPLATE_ATTRIBUTE: case KMIP_TAG_COMMON_TEMPLATE_ATTRIBUTE: case KMIP_TAG_PRIVATE_KEY_TEMPLATE_ATTRIBUTE: case KMIP_TAG_PUBLIC_KEY_TEMPLATE_ATTRIBUTE: /* KMIP v1.x template attributes */ if (attr == NULL) { rc = kmip_get_template_attribute_v1(node, NULL, 0, NULL, num_attrs, 0, NULL); return rc; } rc = kmip_get_template_attribute_v1(node, NULL, 0, NULL, num_attrs, attr_index, &v1_attr); if (rc != 0) return rc; rc = kmip_v2_attr_from_v1_attr(v1_attr, &v2_attr); kmip_node_free(v1_attr); if (rc != 0) return rc; *attr = v2_attr; break; case KMIP_TAG_ATTRIBUTES: case KMIP_TAG_COMMON_ATTRIBUTES: case KMIP_TAG_PRIVATE_KEY_ATTRIBUTES: case KMIP_TAG_PUBLIC_KEY_ATTRIBUTES: /* KMIP v2.x attributes */ rc = kmip_get_attributes_v2(node, num_attrs, attr_index, attr); if (rc != 0) return rc; break; default: return -EBADMSG; } return 0; } /** * Constructs an Attribute Reference node (KMIP v2.x): * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Attribute Reference Yes Enumeration v2.x only * or * Attribute Reference Yes Structure v2.x only * Vendor Identification Yes Text String v2.x only * Attribute Name Yes Text String v2.x only * * @param attr_tag the attribute tag * @param vendor_id the vendor identification of the attribute * @param name the name of the attribute * * Either the attr_tag or the vendor_id and name can be specified, but not both. * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_attribute_reference(enum kmip_tag attr_tag, const char *vendor_id, const char *name) { struct kmip_node *ref = NULL, *nam, *vend = NULL; if (attr_tag == 0 && (vendor_id == NULL || name == NULL)) return NULL; if (attr_tag != 0 && (vendor_id != NULL || name != NULL)) return NULL; if (attr_tag != 0) return kmip_node_new_enumeration(KMIP_TAG_ATTRIBUTE_REFERENCE, NULL, attr_tag); vend = kmip_node_new_text_string(KMIP_TAG_VENDOR_IDENTIFICATION, NULL, vendor_id); nam = kmip_node_new_text_string(KMIP_TAG_ATTRIBUTE_NAME, NULL, name); if (nam == NULL || vend == NULL) goto out; ref = kmip_node_new_structure_va(KMIP_TAG_ATTRIBUTE_REFERENCE, NULL, 2, vend, nam); out: kmip_node_free(nam); kmip_node_free(vend); return ref; } /** * Gets the information from a Attribute Reference node (KMIP v2.x): * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Attribute Reference Yes Enumeration v2.x only * or * Attribute Reference Yes Structure v2.x only * Vendor Identification Yes Text String v2.x only * Attribute Name Yes Text String v2.x only * * @param node the KMIP node * @param attr_tag On return: The attribute tag (can be NULL) * @param vendor_id On return: the vendor identification (can be NULL) * @param name On return: the attribute name (can be NULL) * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_attribute_reference(const struct kmip_node *node, enum kmip_tag *attr_tag, const char **vendor_id, const char **name) { struct kmip_node *vend, *nam; int rc = 0; if (node == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_ATTRIBUTE_REFERENCE) return -EBADMSG; if (kmip_node_get_type(node) == KMIP_TYPE_ENUMERATION) { if (attr_tag != NULL) *attr_tag = kmip_node_get_enumeration(node); if (vendor_id != NULL) vendor_id = NULL; if (name != NULL) *name = NULL; return 0; } if (kmip_node_get_type(node) != KMIP_TYPE_STRUCTURE) return -EBADMSG; if (attr_tag != NULL) *attr_tag = 0; vend = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_VENDOR_IDENTIFICATION, 0); nam = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_ATTRIBUTE_NAME, 0); if (vend == NULL || nam == NULL) { rc = -EBADMSG; goto out; } if (vendor_id != NULL) *vendor_id = kmip_node_get_text_string(vend); if (name != NULL) *name = kmip_node_get_text_string(nam); out: kmip_node_free(vend); kmip_node_free(nam); return rc; } /** * Constructs an Current or New Attribute node (KMIP v2.x): * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Current Attribute Yes Structure v2.x only * or * New Attribute Yes Structure v2.x only * * @param new_attr if true a New Attribute structure, if false a * Current Attribute structure is created * @param attr the KMIP v2.x attribute * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_current_new_attribute(bool new_attr, struct kmip_node *attr) { enum kmip_tag tag; if (attr == NULL) return NULL; tag = (new_attr ? KMIP_TAG_NEW_ATTRIBUTE : KMIP_TAG_CURRENT_ATTRIBUTE); return kmip_node_new_structure_va(tag, NULL, 1, attr); } /** * Constructs an Attribute Name node (KMIP v1.x) from a KMIP v2.x Attribute * Reference: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Attribute Name Yes Text String v1.x only * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Attribute Reference Yes Enumeration v2.x only * or * Attribute Reference Yes Structure v2.x only * Vendor Identification Yes Text String v2.x only * Attribute Name Yes Text String v2.x only * * @param v2_attr_ref the attribute reference node (as of KMIP v2.x) * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_attribute_name_v1( const struct kmip_node *v2_attr_ref) { const char *vendor_id = NULL, *attr_name = NULL; enum kmip_tag attr_tag = 0; struct kmip_node *ret; char *name = NULL; int rc; rc = kmip_get_attribute_reference(v2_attr_ref, &attr_tag, &vendor_id, &attr_name); if (rc != 0) return NULL; if (attr_tag != 0) { attr_name = kmip_v1_attr_name_by_tag(attr_tag); if (attr_name == NULL) return NULL; return kmip_node_new_text_string(KMIP_TAG_ATTRIBUTE_NAME, NULL, attr_name); } if (vendor_id == NULL || attr_name == NULL) return NULL; /* Special handling for v2.x Vendor Attribute */ name = kmip_build_v1_custom_attr_name(vendor_id, attr_name); if (name == NULL) return NULL; ret = kmip_node_new_text_string(KMIP_TAG_ATTRIBUTE_NAME, NULL, name); free(name); return ret; } /** * Gets the information from an Attribute Name node (KMIP v1.x) and returns * a KMIP v2.x Attribute Reference: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Attribute Name Yes Text String v1.x only * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Attribute Reference Yes Enumeration v2.x only * or * Attribute Reference Yes Structure v2.x only * Vendor Identification Yes Text String v2.x only * Attribute Name Yes Text String v2.x only * * @param node the KMIP node * @param v2_attr_ref On return: the attribute reference node (as of * KMIP v2.x) * * @returns 0 on success, or a negative errno in case of an error. * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ int kmip_get_attribute_name_v1(const struct kmip_node *node, struct kmip_node **v2_attr_ref) { char *copy, *vendor_id, *attr_name; enum kmip_tag attr_tag; const char *name; int rc; if (node == NULL || v2_attr_ref == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_ATTRIBUTE_NAME) return -EBADMSG; name = kmip_node_get_text_string(node); if (name == NULL) return -EBADMSG; if (strncmp(name, "x-", 2) == 0 || strncmp(name, "y-", 2) == 0) { /* Special handling for Custom Attribute */ copy = strdup(name); if (copy == NULL) return -ENOMEM; rc = kmip_split_v1_custom_attr_name(copy, &vendor_id, &attr_name); if (rc != 0) { free(copy); return rc; } *v2_attr_ref = kmip_new_attribute_reference(0, vendor_id, attr_name); free(copy); if (*v2_attr_ref == NULL) return -ENOMEM; return 0; } attr_tag = kmip_attr_tag_by_v1_attr_name(name); if (attr_tag == 0) return -EBADMSG; *v2_attr_ref = kmip_new_attribute_reference(attr_tag, NULL, NULL); if (*v2_attr_ref == NULL) return -ENOMEM; return 0; } /** * Constructs a Unique Identifier attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Unique Identifier Yes Text String v1.0 * Enumeration v2.0 * Integer v2.0 * * @param text_id the unique identifier as text string (or NULL) * @param enum_id the unique identifier as enumeration (or 0) * @param int_id the unique identifier as integer * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_unique_identifier(const char *text_id, enum kmip_unique_identifier enum_id, int32_t int_id) { if (text_id != NULL && enum_id != 0) return NULL; if (text_id != NULL) return kmip_node_new_text_string(KMIP_TAG_UNIQUE_IDENTIFIER, NULL, text_id); if (enum_id != 0) return kmip_node_new_enumeration(KMIP_TAG_UNIQUE_IDENTIFIER, NULL, enum_id); return kmip_node_new_integer(KMIP_TAG_UNIQUE_IDENTIFIER, NULL, int_id); } /** * Gets the information from a Unique Identifier attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Unique Identifier Yes Text String v1.0 * Enumeration v2.0 * Integer v2.0 * * @param node the KMIP node * @param text_id the unique identifier as text string (can be NULL) * @param enum_id the unique identifier as enumeration (can be NULL) * @param int_id the unique identifier as integer (can be NULL) * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_unique_identifier(const struct kmip_node *node, const char **text_id, enum kmip_unique_identifier *enum_id, int32_t *int_id) { if (node == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_UNIQUE_IDENTIFIER) return -EBADMSG; if (text_id != NULL) { if (kmip_node_get_type(node) == KMIP_TYPE_TEXT_STRING) *text_id = kmip_node_get_text_string(node); else *text_id = NULL; } if (enum_id != NULL) { if (kmip_node_get_type(node) == KMIP_TYPE_ENUMERATION) *enum_id = kmip_node_get_enumeration(node); else *enum_id = 0; } if (int_id != NULL) { if (kmip_node_get_type(node) == KMIP_TYPE_INTEGER) *int_id = kmip_node_get_integer(node); else *int_id = 0; } return 0; } /** * Constructs a Name attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Name Yes Structure v1.0 * Name Value Yes Text String v1.0 * Name Type Yes Enumeration v1.0 * * @param value the value of the name * @param type the type of the name * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_name(const char *value, enum kmip_name_type type) { struct kmip_node *name = NULL, *val, *typ; if (value == NULL) return NULL; val = kmip_node_new_text_string(KMIP_TAG_NAME_VALUE, NULL, value); typ = kmip_node_new_enumeration(KMIP_TAG_NAME_TYPE, NULL, type); if (val == NULL || typ == NULL) goto out; name = kmip_node_new_structure_va(KMIP_TAG_NAME, NULL, 2, val, typ); out: kmip_node_free(val); kmip_node_free(typ); return name; } /** * Gets the information from a Name attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Name Yes Structure v1.0 * Name Value Yes Text String v1.0 * Name Type Yes Enumeration v1.0 * * @param node the KMIP node * @param value On return: the name value (can be NULL) * @param type On return: the name type (can be NULL) * * @returns 0 on success, or a negative errno in case of an error * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ int kmip_get_name(const struct kmip_node *node, const char **value, enum kmip_name_type *type) { struct kmip_node *val, *typ; int rc = 0; if (node == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_NAME) return -EBADMSG; val = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_NAME_VALUE, 0); typ = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_NAME_TYPE, 0); if (val == NULL || typ == NULL) { rc = -EBADMSG; goto out; } if (value != NULL) *value = kmip_node_get_text_string(val); if (type != NULL) *type = kmip_node_get_enumeration(typ); out: kmip_node_free(val); kmip_node_free(typ); return rc; } /** * Constructs a Alternative Name attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Alternative Name Yes Structure v1.2 * Alternative Name Value Yes Text String v1.2 * Alternative Name Type Yes Enumeration v1.2 * * @param value the value of the alternative name * @param type the type of the alternative name * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_alternative_name(const char *value, enum kmip_alternative_name_type type) { struct kmip_node *name = NULL, *val, *typ; if (value == NULL) return NULL; val = kmip_node_new_text_string(KMIP_TAG_ALTERNATE_NAME_VALUE, NULL, value); typ = kmip_node_new_enumeration(KMIP_TAG_ALTERNATE_NAME_TYPE, NULL, type); if (val == NULL || typ == NULL) goto out; name = kmip_node_new_structure_va(KMIP_TAG_ALTERNATE_NAME, NULL, 2, val, typ); out: kmip_node_free(val); kmip_node_free(typ); return name; } /** * Gets the information from an Alternative Name attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Alternative Name Yes Structure v1.2 * Alternative Name Value Yes Text String v1.2 * Alternative Name Type Yes Enumeration v1.2 * * @param node the KMIP node * @param value On return: the alternative name value (can be NULL) * @param type On return: the alternative name type (can be NULL) * * @returns 0 on success, or a negative errno in case of an error * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ int kmip_get_alternative_name(const struct kmip_node *node, const char **value, enum kmip_alternative_name_type *type) { struct kmip_node *val, *typ; int rc = 0; if (node == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_ALTERNATE_NAME) return -EBADMSG; val = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_ALTERNATE_NAME_VALUE, 0); typ = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_ALTERNATE_NAME_TYPE, 0); if (val == NULL || typ == NULL) { rc = -EBADMSG; goto out; } if (value != NULL) *value = kmip_node_get_text_string(val); if (type != NULL) *type = kmip_node_get_enumeration(typ); out: kmip_node_free(val); kmip_node_free(typ); return rc; } /** * Constructs a Object Type attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Object Type Yes Enumeration v1.0 * * @param obj_type the object type * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_object_type(enum kmip_object_type obj_type) { return kmip_node_new_enumeration(KMIP_TAG_OBJECT_TYPE, NULL, obj_type); } /** * Gets the information from a Object Type attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Object Type Yes Enumeration v1.0 * * * @param node the KMIP node * @param obj_type the object type * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_object_type(const struct kmip_node *node, enum kmip_object_type *obj_type) { if (node == NULL || obj_type == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_OBJECT_TYPE) return -EBADMSG; *obj_type = kmip_node_get_enumeration(node); return 0; } /** * Constructs a Cryptographic Algorithm attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Cryptographic Algorithm Yes Enumeration v1.0 * * @param algo the cryptographic algorithm * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_cryptographic_algorithm(enum kmip_crypto_algo algo) { return kmip_node_new_enumeration(KMIP_TAG_CRYPTOGRAPHIC_ALGORITHM, NULL, algo); } /** * Gets the information from a Cryptographic Algorithm attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Cryptographic Algorithm Yes Enumeration v1.0 * * * @param node the KMIP node * @param algo the cryptographic algorithm * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_cryptographic_algorithm(const struct kmip_node *node, enum kmip_crypto_algo *algo) { if (node == NULL || algo == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_CRYPTOGRAPHIC_ALGORITHM) return -EBADMSG; *algo = kmip_node_get_enumeration(node); return 0; } /** * Constructs a Cryptographic Length attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Cryptographic Length Yes Integer v1.0 * * @param length the cryptographic length * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_cryptographic_length(int32_t length) { return kmip_node_new_integer(KMIP_TAG_CRYPTOGRAPHIC_LENGTH, NULL, length); } /** * Gets the information from a Cryptographic Length attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Cryptographic Length Yes Integer v1.0 * * * @param node the KMIP node * @param length the cryptographic length * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_cryptographic_length(const struct kmip_node *node, int32_t *length) { if (node == NULL || length == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_CRYPTOGRAPHIC_LENGTH) return -EBADMSG; *length = kmip_node_get_integer(node); return 0; } /** * Constructs a Certificate Type attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Certificate Type Yes Enumeration v1.0 * * @param type the certificate type * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_certificate_type(enum kmip_certificate_type type) { return kmip_node_new_enumeration(KMIP_TAG_CERTIFICATE_TYPE, NULL, type); } /** * Gets the information from a Certificate Type attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Certificate Type Yes Enumeration v1.0 * * * @param node the KMIP node * @param type the certificate type * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_certificate_type(const struct kmip_node *node, enum kmip_certificate_type *type) { if (node == NULL || type == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_CERTIFICATE_TYPE) return -EBADMSG; *type = kmip_node_get_enumeration(node); return 0; } /** * Constructs a Cryptographic Usage Mask attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Cryptographic Usage Mask Yes Integer v1.0 * * @param usage_mask the cryptographic usage mask * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_cryptographic_usage_mask(int32_t usage_mask) { return kmip_node_new_integer(KMIP_TAG_CRYPTOGRAPHIC_USAGE_MASK, NULL, usage_mask); } /** * Gets the information from a Cryptographic Usage Mask attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Cryptographic Usage Mask Yes Integer v1.0 * * * @param node the KMIP node * @param usage_mask the cryptographic usage mask * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_cryptographic_usage_mask(const struct kmip_node *node, int32_t *usage_mask) { if (node == NULL || usage_mask == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_CRYPTOGRAPHIC_USAGE_MASK) return -EBADMSG; *usage_mask = kmip_node_get_integer(node); return 0; } /** * Constructs a State attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * State Yes Enumeration v1.0 * * @param state the state * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_state(enum kmip_state state) { return kmip_node_new_enumeration(KMIP_TAG_STATE, NULL, state); } /** * Gets the information from a State attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * State Yes Enumeration v1.0 * * * @param node the KMIP node * @param state the state * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_state(const struct kmip_node *node, enum kmip_state *state) { if (node == NULL || state == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_STATE) return -EBADMSG; *state = kmip_node_get_enumeration(node); return 0; } /** * Constructs a Initial Date attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Initial Date Yes Date-Time v1.0 * * @param date the date * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_initial_date(int64_t date) { return kmip_node_new_enumeration(KMIP_TAG_INITIAL_DATE, NULL, date); } /** * Gets the information from a Initial Date attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Initial Date Yes Date-Time v1.0 * * * @param node the KMIP node * @param date the date * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_initial_date(const struct kmip_node *node, int64_t *date) { if (node == NULL || date == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_INITIAL_DATE) return -EBADMSG; *date = kmip_node_get_date_time(node); return 0; } /** * Constructs a Activation Date attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Activation Date Yes Date-Time v1.0 * * @param date the date * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_activation_date(int64_t date) { return kmip_node_new_enumeration(KMIP_TAG_ACTIVATION_DATE, NULL, date); } /** * Gets the information from a Activation Date attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Activation Date Yes Date-Time v1.0 * * * @param node the KMIP node * @param date the date * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_activation_date(const struct kmip_node *node, int64_t *date) { if (node == NULL || date == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_ACTIVATION_DATE) return -EBADMSG; *date = kmip_node_get_date_time(node); return 0; } /** * Constructs a Deactivation Date attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Deactivation Date Yes Date-Time v1.0 * * @param date the date * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_deactivation_date(int64_t date) { return kmip_node_new_enumeration(KMIP_TAG_DEACTIVATION_DATE, NULL, date); } /** * Gets the information from a Deactivation Date attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Deactivation Date Yes Date-Time v1.0 * * * @param node the KMIP node * @param date the date * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_deactivation_date(const struct kmip_node *node, int64_t *date) { if (node == NULL || date == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_DEACTIVATION_DATE) return -EBADMSG; *date = kmip_node_get_date_time(node); return 0; } /** * Constructs a Destroy Date attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Destroy Date Yes Date-Time v1.0 * * @param date the date * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_destroy_date(int64_t date) { return kmip_node_new_enumeration(KMIP_TAG_DESTROY_DATE, NULL, date); } /** * Gets the information from a Destroy Date attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Destroy Date Yes Date-Time v1.0 * * * @param node the KMIP node * @param date the date * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_destroy_date(const struct kmip_node *node, int64_t *date) { if (node == NULL || date == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_DESTROY_DATE) return -EBADMSG; *date = kmip_node_get_date_time(node); return 0; } /** * Constructs a Compromise Date attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Compromise Date Yes Date-Time v1.0 * * @param date the date * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_compromise_date(int64_t date) { return kmip_node_new_enumeration(KMIP_TAG_COMPROMIZE_DATE, NULL, date); } /** * Gets the information from a Compromise Date attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Compromise Date Yes Date-Time v1.0 * * * @param node the KMIP node * @param date the date * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_compromise_date(const struct kmip_node *node, int64_t *date) { if (node == NULL || date == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_COMPROMIZE_DATE) return -EBADMSG; *date = kmip_node_get_date_time(node); return 0; } /** * Constructs a Compromise Occurrence Date attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Compromise Occurrence Date Yes Date-Time v1.0 * * @param date the date * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_compromise_occurrence_date(int64_t date) { return kmip_node_new_enumeration(KMIP_TAG_COMPROMISE_OCCURRENCE_DATE, NULL, date); } /** * Gets the information from a Compromise Occurrence Date attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Compromise Occurrence Date Yes Date-Time v1.0 * * * @param node the KMIP node * @param date the date * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_compromise_occurrence_date(const struct kmip_node *node, int64_t *date) { if (node == NULL || date == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_COMPROMISE_OCCURRENCE_DATE) return -EBADMSG; *date = kmip_node_get_date_time(node); return 0; } /** * Constructs a Last Change Date attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Last Change Date Yes Date-Time v1.0 * * @param date the date * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_last_change_date(int64_t date) { return kmip_node_new_enumeration(KMIP_TAG_LAST_CHANGE_DATE, NULL, date); } /** * Gets the information from a Last Change Date attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Last Change Date Yes Date-Time v1.0 * * * @param node the KMIP node * @param date the date * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_last_change_date(const struct kmip_node *node, int64_t *date) { if (node == NULL || date == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_LAST_CHANGE_DATE) return -EBADMSG; *date = kmip_node_get_date_time(node); return 0; } /** * Constructs a Original Creation Date attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Original Creation Date Yes Date-Time v1.0 * * @param date the date * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_original_creation_date(int64_t date) { return kmip_node_new_enumeration(KMIP_TAG_ORIGINAL_CREATION_DATE, NULL, date); } /** * Gets the information from a Original Creation Date attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Original Creation Date Yes Date-Time v1.0 * * * @param node the KMIP node * @param date the date * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_original_creation_date(const struct kmip_node *node, int64_t *date) { if (node == NULL || date == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_ORIGINAL_CREATION_DATE) return -EBADMSG; *date = kmip_node_get_date_time(node); return 0; } /** * Constructs a Archive Date attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Archive Date Yes Date-Time v1.0 * * @param date the date * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_archive_date(int64_t date) { return kmip_node_new_enumeration(KMIP_TAG_ARCHIVE_DATE, NULL, date); } /** * Gets the information from a Archive Date attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Archive Date Yes Date-Time v1.0 * * * @param node the KMIP node * @param date the date * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_archive_date(const struct kmip_node *node, int64_t *date) { if (node == NULL || date == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_ARCHIVE_DATE) return -EBADMSG; *date = kmip_node_get_date_time(node); return 0; } /** * Constructs a Process Start Date attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Process Start Date Yes Date-Time v1.0 * * @param date the date * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_process_start_date(int64_t date) { return kmip_node_new_enumeration(KMIP_TAG_PROCESS_START_DATE, NULL, date); } /** * Gets the information from a Process Start Date attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Process Start Date Yes Date-Time v1.0 * * * @param node the KMIP node * @param date the date * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_process_start_date(const struct kmip_node *node, int64_t *date) { if (node == NULL || date == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_PROCESS_START_DATE) return -EBADMSG; *date = kmip_node_get_date_time(node); return 0; } /** * Constructs a Protect Stop Date attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Protect Stop Date Yes Date-Time v1.0 * * @param date the date * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_protect_stop_date(int64_t date) { return kmip_node_new_enumeration(KMIP_TAG_PROTECT_STOP_DATE, NULL, date); } /** * Gets the information from a Protect Stop Date attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Protect Stop Date Yes Date-Time v1.0 * * @param node the KMIP node * @param date the date * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_protect_stop_date(const struct kmip_node *node, int64_t *date) { if (node == NULL || date == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_PROTECT_STOP_DATE) return -EBADMSG; *date = kmip_node_get_date_time(node); return 0; } /** * Constructs a Cryptographic Parameters attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Cryptographic Parameters Structure v1.0 * Block Cipher Mode No Enumeration v1.0 * Padding Method No Enumeration v1.0 * Hashing Algorithm No Enumeration v1.0 * Key Role Type No Enumeration v1.0 * Digital Signature Algorithm No Enumeration v1.2 * Cryptographic Algorithm No Enumeration v1.2 * Random IV No Boolean v1.2 * IV Length No Integer v1.2 * Tag Length No Integer v1.2 * Fixed Field Length No Integer v1.2 * Invocation Field Length No Integer v1.2 * Counter Length No Integer v1.2 * Initial Counter Value No Integer v1.2 * Salt Length No Integer v1.4 * Mask Generator No Enumeration v1.4 * Mask Generator Hashing Alg No Enumeration v1.4 * P Source No Byte String v1.4 * Trailer Field No Integer v1.4 * * @param version the protocol version. If null, the current default * protocol version is used. * @param mode the block cipher mode (ignored if 0) * @param padding the padding method (ignored if 0) * @param hash_algo the hashing algorithm (ignored if 0) * @param key_role the key role type (ignored if 0) * @param signature_algo the signature algorithm (ignored if 0) * @param crypto_algo the cryptographic algorithm (ignored if 0) * @param random_iv true if a random IV is used (ignored if NULL) * @param iv_length the IV length (ignored if NULL) * @param tag_length the tag length (ignored if NULL) * @param fixed_field_length the fixed field length (ignored if NULL) * @param invoc_field_length the invocation field length (ignored if NULL) * @param counter_length the counter length (ignored if NULL) * @param init_counter_value the initial counter value (ignored if NULL) * @param salt_length the salt length (ignored if NULL) * @param mgf the mask generator (ignored if 0) * @param mgf_hash_algo the mask generator hash algorithm (ignored if 0) * @param trailer_field the trailer field (ignored if NULL) * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_cryptographic_parameters( const struct kmip_version *version, enum kmip_block_cipher_mode mode, enum kmip_padding_method padding, enum kmip_hashing_algo hash_algo, enum kmip_key_role_type key_role, enum kmip_signature_algo signature_algo, enum kmip_crypto_algo crypto_algo, bool *random_iv, int32_t *iv_length, int32_t *tag_length, int32_t *fixed_field_length, int32_t *invoc_field_length, int32_t *counter_length, int32_t *init_counter_value, int32_t *salt_length, enum kmip_mask_generator mgf, enum kmip_hashing_algo mgf_hash_algo, int32_t *trailer_field) { struct kmip_node *icv = NULL, *salt = NULL, *mg = NULL, *mghash = NULL; struct kmip_node *ret = NULL, *cmod = NULL, *pad = NULL, *hash = NULL; struct kmip_node *krl = NULL, *sig = NULL, *algo = NULL, *riv = NULL; struct kmip_node *iv = NULL, *tag = NULL, *ffl = NULL, *ifl = NULL; struct kmip_node *cnt = NULL, *trl = NULL; if (version == NULL) version = kmip_get_default_protocol_version(); if (mode != 0) { cmod = kmip_node_new_enumeration(KMIP_TAG_BLOCK_CIPHER_MODE, NULL, mode); if (cmod == NULL) goto out; } if (padding != 0) { pad = kmip_node_new_enumeration(KMIP_TAG_PADDING_METHOD, NULL, padding); if (pad == NULL) goto out; } if (hash_algo != 0) { hash = kmip_node_new_enumeration(KMIP_TAG_HASHING_ALGORITHM, NULL, hash_algo); if (hash == NULL) goto out; } if (key_role != 0) { krl = kmip_node_new_enumeration(KMIP_TAG_KEY_ROLE_TYPE, NULL, key_role); if (krl == NULL) goto out; } if (version->major == 1 && version->minor < 2) goto create; if (signature_algo != 0) { sig = kmip_node_new_enumeration( KMIP_TAG_DIGITAL_SIGNATURE_ALGORITHM, NULL, signature_algo); if (sig == NULL) goto out; } if (crypto_algo != 0) { algo = kmip_node_new_enumeration( KMIP_TAG_CRYPTOGRAPHIC_ALGORITHM, NULL, crypto_algo); if (algo == NULL) goto out; } if (random_iv != NULL) { riv = kmip_node_new_boolean(KMIP_TAG_RANDOM_IV, NULL, *random_iv); if (riv == NULL) goto out; } if (iv_length != NULL) { iv = kmip_node_new_integer(KMIP_TAG_IV_LENGTH, NULL, *iv_length); if (iv == NULL) goto out; } if (tag_length != NULL) { tag = kmip_node_new_integer(KMIP_TAG_TAG_LENGTH, NULL, *tag_length); if (tag == NULL) goto out; } if (fixed_field_length != NULL) { ffl = kmip_node_new_integer(KMIP_TAG_FIXED_FIELD_LENGTH, NULL, *fixed_field_length); if (ffl == NULL) goto out; } if (invoc_field_length != NULL) { ifl = kmip_node_new_integer(KMIP_TAG_INVOCATION_FIELD_LENGTH, NULL, *invoc_field_length); if (ifl == NULL) goto out; } if (counter_length != NULL) { cnt = kmip_node_new_integer(KMIP_TAG_COUNTER_LENGTH, NULL, *counter_length); if (cnt == NULL) goto out; } if (init_counter_value != NULL) { icv = kmip_node_new_integer(KMIP_TAG_INITIAL_COUNTER_VALUE, NULL, *init_counter_value); if (icv == NULL) goto out; } if (version->major == 1 && version->minor < 4) goto create; if (salt_length != NULL) { salt = kmip_node_new_integer(KMIP_TAG_SALT_LENGTH, NULL, *salt_length); if (salt == NULL) goto out; } if (mgf != 0) { mg = kmip_node_new_enumeration(KMIP_TAG_MASK_GENERATOR, NULL, mgf); if (mg == NULL) goto out; } if (mgf_hash_algo != 0) { mghash = kmip_node_new_enumeration( KMIP_TAG_MASK_GENERATOR_HASHING_ALGORITHM, NULL, mgf_hash_algo); if (mghash == NULL) goto out; } if (trailer_field != NULL) { trl = kmip_node_new_integer(KMIP_TAG_TRAILER_FIELD, NULL, *trailer_field); if (trl == NULL) goto out; } create: ret = kmip_node_new_structure_va(KMIP_TAG_CRYPTOGRAPHIC_PARAMETERS, NULL, 17, cmod, pad, hash, krl, sig, algo, riv, iv, tag, ffl, ifl, cnt, icv, salt, mg, mghash, trl); out: kmip_node_free(cmod); kmip_node_free(pad); kmip_node_free(hash); kmip_node_free(krl); kmip_node_free(sig); kmip_node_free(algo); kmip_node_free(riv); kmip_node_free(iv); kmip_node_free(tag); kmip_node_free(ffl); kmip_node_free(ifl); kmip_node_free(cnt); kmip_node_free(icv); kmip_node_free(salt); kmip_node_free(mg); kmip_node_free(mghash); kmip_node_free(trl); return ret; } /** * Gets information from a Cryptographic Parameter attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Cryptographic Parameters Structure v1.0 * Block Cipher Mode No Enumeration v1.0 * Padding Method No Enumeration v1.0 * Hashing Algorithm No Enumeration v1.0 * Key Role Type No Enumeration v1.0 * Digital Signature Algorithm No Enumeration v1.2 * Cryptographic Algorithm No Enumeration v1.2 * Random IV No Boolean v1.2 * IV Length No Integer v1.2 * Tag Length No Integer v1.2 * Fixed Field Length No Integer v1.2 * Invocation Field Length No Integer v1.2 * Counter Length No Integer v1.2 * Initial Counter Value No Integer v1.2 * Salt Length No Integer v1.4 * Mask Generator No Enumeration v1.4 * Mask Generator Hashing Alg No Enumeration v1.4 * P Source No Byte String v1.4 * Trailer Field No Integer v1.4 * * @param node the KMIP node * @param mode On return: the block cipher mode (0 if not avail, * can be NULL) * @param padding On return: the padding method (0 if not avail, * can be NULL) * @param hash_algo On return: the hashing algorithm (0 if not avail, * can be NULL) * @param key_role On return: the key role type (0 if not avail, * can be NULL) * @param signature_algo On return: the signature algorithm (0 if not avail, * can be NULL) * @param crypto_algo On return: the cryptographic algorithm (0 if not * avail, can be NULL) * @param random_iv On return: true if a random IV is used (false if * not avail, can be NULL) * @param iv_length On return: the IV length (-1 if not avail, can be * NULL) * @param tag_length On return: the tag length (-1 if not avail, can be * NULL) * @param fixed_field_length On return: the fixed field length (-1 if not avail, * can be NULL) * @param invoc_field_length On return: the invocation field length (-1 if not * avail, can be NULL) * @param counter_length On return: the counter length (-1 if not avail, * can be NULL) * @param init_counter_value On return: the initial counter value (0 if not * avail, can be NULL) * @param salt_length On return: the salt length (-1 if not avail, * can be NULL) * @param mgf On return: the mask generator (0 if not avail, * can be NULL) * @param mgf_hash_algo On return: the mask generator hash algorithm (0 if * not avail, can be NULL) * @param trailer_field On return: the trailer field (0 if not avail, * can be NULL) * * @returns 0 on success, or a negative errno in case of an error. * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ int kmip_get_cryptographic_parameter(const struct kmip_node *node, enum kmip_block_cipher_mode *mode, enum kmip_padding_method *padding, enum kmip_hashing_algo *hash_algo, enum kmip_key_role_type *key_role, enum kmip_signature_algo *signature_algo, enum kmip_crypto_algo *crypto_algo, bool *random_iv, int32_t *iv_length, int32_t *tag_length, int32_t *fixed_field_length, int32_t *invoc_field_length, int32_t *counter_length, int32_t *init_counter_value, int32_t *salt_length, enum kmip_mask_generator *mgf, enum kmip_hashing_algo *mgf_hash_algo, int32_t *trailer_field) { struct kmip_node *n; if (node == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_CRYPTOGRAPHIC_PARAMETERS) return -EBADMSG; if (mode != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_BLOCK_CIPHER_MODE, 0); *mode = (n != NULL ? kmip_node_get_enumeration(n) : 0); kmip_node_free(n); } if (padding != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_PADDING_METHOD, 0); *padding = (n != NULL ? kmip_node_get_enumeration(n) : 0); kmip_node_free(n); } if (hash_algo != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_HASHING_ALGORITHM, 0); *hash_algo = (n != NULL ? kmip_node_get_enumeration(n) : 0); kmip_node_free(n); } if (key_role != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_KEY_ROLE_TYPE, 0); *key_role = (n != NULL ? kmip_node_get_enumeration(n) : 0); kmip_node_free(n); } if (signature_algo != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_DIGITAL_SIGNATURE_ALGORITHM, 0); *signature_algo = (n != NULL ? kmip_node_get_enumeration(n) : 0); kmip_node_free(n); } if (crypto_algo != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_CRYPTOGRAPHIC_ALGORITHM, 0); *crypto_algo = (n != NULL ? kmip_node_get_enumeration(n) : 0); kmip_node_free(n); } if (random_iv != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_RANDOM_IV, 0); *random_iv = (n != NULL ? kmip_node_get_boolean(n) : false); kmip_node_free(n); } if (iv_length != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_IV_LENGTH, 0); *iv_length = (n != NULL ? kmip_node_get_integer(n) : -1); kmip_node_free(n); } if (tag_length != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_TAG_LENGTH, 0); *tag_length = (n != NULL ? kmip_node_get_integer(n) : -1); kmip_node_free(n); } if (fixed_field_length != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_FIXED_FIELD_LENGTH, 0); *fixed_field_length = (n != NULL ? kmip_node_get_integer(n) : -1); kmip_node_free(n); } if (invoc_field_length != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_INVOCATION_FIELD_LENGTH, 0); *invoc_field_length = (n != NULL ? kmip_node_get_integer(n) : -1); kmip_node_free(n); } if (counter_length != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_COUNTER_LENGTH, 0); *counter_length = (n != NULL ? kmip_node_get_integer(n) : -1); kmip_node_free(n); } if (init_counter_value != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_INITIAL_COUNTER_VALUE, 0); *init_counter_value = (n != NULL ? kmip_node_get_integer(n) : 0); kmip_node_free(n); } if (salt_length != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_SALT_LENGTH, 0); *salt_length = (n != NULL ? kmip_node_get_integer(n) : -1); kmip_node_free(n); } if (mgf != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_MASK_GENERATOR, 0); *mgf = (n != NULL ? kmip_node_get_enumeration(n) : 0); kmip_node_free(n); } if (mgf_hash_algo != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_MASK_GENERATOR_HASHING_ALGORITHM, 0); *mgf_hash_algo = (n != NULL ? kmip_node_get_enumeration(n) : 0); kmip_node_free(n); } if (trailer_field != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_TRAILER_FIELD, 0); *trailer_field = (n != NULL ? kmip_node_get_integer(n) : 0); kmip_node_free(n); } return 0; } /** * Constructs a Cryptographic Domain Parameters attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Cryptographic Domain Params Yes Structure v1.0 * Qlength No Integer v1.0 * Recommended Curve No Enumeration v1.0 * * @param qlength the Q length (ignored of <= 0) * @param curve the curve (ignored if 0) * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_cryptographic_domain_parameters( int32_t qlength, enum kmip_recommended_curve curve) { struct kmip_node *ret = NULL, *qlen = NULL, *crv = NULL; if (qlength > 0) { qlen = kmip_node_new_integer(KMIP_TAG_Q_LENGTH, NULL, qlength); if (qlen == NULL) goto out; } if (curve != 0) { crv = kmip_node_new_enumeration(KMIP_TAG_RECOMMENDED_CURVE, NULL, curve); if (crv == NULL) goto out; } ret = kmip_node_new_structure_va( KMIP_TAG_CRYPTOGRAPHIC_DOMAIN_PARAMETERS, NULL, 2, qlen, crv); out: kmip_node_free(qlen); kmip_node_free(crv); return ret; } /** * Gets the information from a Cryptographic Domain Parameters attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Cryptographic Domain Params Yes Structure v1.0 * Qlength No Integer v1.0 * Recommended Curve No Enumeration v1.0 * * @param node the KMIP node * @param qlength On return: the Q length (-1 if not avail, can be * NULL) * @param curve On return: the curve (0 if not avail, can be NULL) * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_cryptographic_domain_parameters(const struct kmip_node *node, int32_t *qlength, enum kmip_recommended_curve *curve) { struct kmip_node *n; if (node == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_CRYPTOGRAPHIC_DOMAIN_PARAMETERS) return -EBADMSG; if (qlength != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_Q_LENGTH, 0); *qlength = (n != NULL ? kmip_node_get_integer(n) : -1); kmip_node_free(n); } if (curve != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_RECOMMENDED_CURVE, 0); *curve = (n != NULL ? kmip_node_get_enumeration(n) : 0); kmip_node_free(n); } return 0; } /** * Constructs a Digital Signature Algorithm attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Digital Signature Algorithm Yes Enumeration v1.2 * * @param signature_algo the signature algorithm * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_digital_signature_algorithm( enum kmip_signature_algo signature_algo) { return kmip_node_new_enumeration(KMIP_TAG_DIGITAL_SIGNATURE_ALGORITHM, NULL, signature_algo); } /** * Gets the information from a Digital Signature Algorithm attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Digital Signature Algorithm Yes Enumeration v1.2 * * @param node the KMIP node * @param signature_algo the signature algorithm * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_digital_signature_algorithm(const struct kmip_node *node, enum kmip_signature_algo *signature_algo) { if (node == NULL || signature_algo == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_DIGITAL_SIGNATURE_ALGORITHM) return -EBADMSG; *signature_algo = kmip_node_get_enumeration(node); return 0; } /** * Constructs a Object Group attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Object Group Yes Text String v1.0 * * @param group the object group * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_object_group(const char *group) { return kmip_node_new_text_string(KMIP_TAG_OBJECT_GROUP, NULL, group); } /** * Gets the information from a Object Group attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Object Group Yes Text String v1.0 * * @param node the KMIP node * @param group the object group * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_object_group(const struct kmip_node *node, const char **group) { if (node == NULL || group == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_OBJECT_GROUP) return -EBADMSG; *group = kmip_node_get_text_string(node); return 0; } /** * Constructs a Revocation Reason attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Revocation Reason Yes Structure v1.0 * Revocation Reason Code Yes Enumeration v1.0 * Revocation Message No Text String v1.0 * * @param reason the revocation reason code * @param message the revocation message (can be NULL) * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_revocation_reason(enum kmip_revoke_reason reason, const char *message) { struct kmip_node *ret = NULL, *rsn, *msg = NULL; rsn = kmip_node_new_enumeration(KMIP_TAG_REVOCATION_REASON_CODE, NULL, reason); if (rsn == NULL) goto out; if (message != NULL) { msg = kmip_node_new_text_string(KMIP_TAG_REVOCATION_MESSAGE, NULL, message); if (msg == NULL) goto out; } ret = kmip_node_new_structure_va(KMIP_TAG_REVOCATION_REASON, NULL, 2, rsn, msg); out: kmip_node_free(rsn); kmip_node_free(msg); return ret; } /** * Gets the information from a Revocation Reason attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Revocation Reason Yes Structure v1.0 * Revocation Reason Code Yes Enumeration v1.0 * Revocation Message No Text String v1.0 * * @param node the KMIP node * @param reason the revocation reason code * @param message the revocation message (can be NULL) * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_revocation_reason(const struct kmip_node *node, enum kmip_revoke_reason *reason, const char **message) { struct kmip_node *n; if (node == NULL || reason == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_REVOCATION_REASON) return -EBADMSG; n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_REVOCATION_REASON_CODE, 0); if (n == NULL) return -EBADMSG; *reason = kmip_node_get_enumeration(n); kmip_node_free(n); if (message != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_REVOCATION_MESSAGE, 0); *message = (n != NULL ? kmip_node_get_text_string(n) : NULL); kmip_node_free(n); } return 0; } /** * Constructs a Contact Information attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Contact Information Yes Text String v1.0 * * @param contact the contact information * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_contact_information(const char *contact) { return kmip_node_new_text_string(KMIP_TAG_CONTACT_INFORMATION, NULL, contact); } /** * Gets the information from a Contact Information attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Contact Information Yes Text String v1.0 * * @param node the KMIP node * @param contact the contact information * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_contact_information(const struct kmip_node *node, const char **contact) { if (node == NULL || contact == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_CONTACT_INFORMATION) return -EBADMSG; *contact = kmip_node_get_text_string(node); return 0; } /** * Constructs a Description attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Description Yes Text String v1.4 * * @param description the description * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_description(const char *description) { return kmip_node_new_text_string(KMIP_TAG_DESCRIPTION, NULL, description); } /** * Gets the information from a Description attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Description Yes Text String v1.4 * * @param node the KMIP node * @param description the description * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_description(const struct kmip_node *node, const char **description) { if (node == NULL || description == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_DESCRIPTION) return -EBADMSG; *description = kmip_node_get_text_string(node); return 0; } /** * Constructs a Comment attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Comment Yes Text String v1.4 * * @param comment the comment * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_comment(const char *comment) { return kmip_node_new_text_string(KMIP_TAG_COMMENT, NULL, comment); } /** * Gets the information from a Comment attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Comment Yes Text String v1.4 * * @param node the KMIP node * @param comment the comment * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_comment(const struct kmip_node *node, const char **comment) { if (node == NULL || comment == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_COMMENT) return -EBADMSG; *comment = kmip_node_get_text_string(node); return 0; } /** * Constructs a Key Format Type attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Key Format Type Yes Enumeration v2.0 * * @param type the key format type * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_key_format_type(enum kmip_key_format_type type) { return kmip_node_new_enumeration(KMIP_TAG_KEY_FORMAT_TYPE, NULL, type); } /** * Gets the information from a Key Format Type attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Key Format Type Yes Enumeration v2.0 * * @param node the KMIP node * @param type the key format type * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_key_format_type(const struct kmip_node *node, enum kmip_key_format_type *type) { if (node == NULL || type == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_KEY_FORMAT_TYPE) return -EBADMSG; *type = kmip_node_get_enumeration(node); return 0; } /** * Constructs a Protection Level attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Protection Level Yes Enumeration v2.0 * * @param level the protection level * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_protection_level(enum kmip_protection_level level) { return kmip_node_new_enumeration(KMIP_TAG_PROTECTION_LEVEL, NULL, level); } /** * Gets the information from a Protection Level attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Protection Level Yes Enumeration v2.0 * * @param node the KMIP node * @param level the protection level * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_protection_level(const struct kmip_node *node, enum kmip_protection_level *level) { if (node == NULL || level == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_PROTECTION_LEVEL) return -EBADMSG; *level = kmip_node_get_enumeration(node); return 0; } /** * Constructs a Protection Period attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Protection Period Yes Interval v2.0 * * @param period the protection period * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_protection_period(uint32_t period) { return kmip_node_new_interval(KMIP_TAG_PROTECTION_PERIOD, NULL, period); } /** * Gets the information from a Protection Period attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Protection Period Yes Interval v2.0 * * @param node the KMIP node * @param period the protection period * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_protection_period(const struct kmip_node *node, uint32_t *period) { if (node == NULL || period == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_PROTECTION_PERIOD) return -EBADMSG; *period = kmip_node_get_interval(node); return 0; } /** * Constructs a Protection Storage Mask attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Protection Storage Mask Yes Integer v2.0 * * @param protection_mask the protection mask * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_protection_storage_mask(int32_t protection_mask) { return kmip_node_new_integer(KMIP_TAG_PROTECTION_STORAGE_MASK, NULL, protection_mask); } /** * Gets the information from a Protection Storage Mask attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Protection Storage Mask Yes Integer v2.0 * * @param node the KMIP node * @param protection_mask the protection mask * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_protection_storage_mask(const struct kmip_node *node, int32_t *protection_mask) { if (node == NULL || protection_mask == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_PROTECTION_STORAGE_MASK) return -EBADMSG; *protection_mask = kmip_node_get_integer(node); return 0; } /** * Constructs a Fresh attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Fresh Yes Boolean v1.2 * * @param fresh the fresh value * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_fresh(bool fresh) { return kmip_node_new_boolean(KMIP_TAG_FRESH, NULL, fresh); } /** * Gets the information from a Fresh attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Fresh Yes Boolean v1.2 * * @param node the KMIP node * @param fresh the fresh value * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_fresh(const struct kmip_node *node, bool *fresh) { if (node == NULL || fresh == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_FRESH) return -EBADMSG; *fresh = kmip_node_get_boolean(node); return 0; } /** * Constructs a Key Value Present attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Key Value Present Yes Boolean v1.2 * * @param present the present value * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_key_value_present(bool present) { return kmip_node_new_boolean(KMIP_TAG_KEY_VALUE_PRESENT, NULL, present); } /** * Gets the information from a Key Value Present attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Key Value Present Yes Boolean v1.2 * * @param node the KMIP node * @param present the present value * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_key_value_present(const struct kmip_node *node, bool *present) { if (node == NULL || present == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_KEY_VALUE_PRESENT) return -EBADMSG; *present = kmip_node_get_boolean(node); return 0; } /** * Constructs a Short Unique Identifier attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Short Unique Identifier Yes Byte String v2.0 * * @param short_uid the short unique identifier * @param short_uid_len the length of the short unique identifier * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_short_unique_identifier( const unsigned char *short_uid, uint32_t short_uid_len) { return kmip_node_new_byte_string(KMIP_TAG_SHORT_UNIQUE_IDENTIFIER, NULL, short_uid, short_uid_len); } /** * Gets the information from a Short Unique Identifier attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Short Unique Identifier Yes Byte String v2.0 * * @param node the KMIP node * @param short_uid the short unique identifier * @param short_uid_len the length of the short unique identifier * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_short_unique_identifier(const struct kmip_node *node, const unsigned char **short_uid, uint32_t *short_uid_len) { if (node == NULL || short_uid == NULL || short_uid_len == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_SHORT_UNIQUE_IDENTIFIER) return -EBADMSG; *short_uid = kmip_node_get_byte_string(node, short_uid_len); return 0; } /** * Constructs a Application Specific Information attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Application Specific Info. Yes Structure v1.0 * Application Namespace Yes Text String v1.0 * Application Data Yes/No Text String v1.0 * * @param name_space the application namespace * @param data the application data * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_application_specific_information( const char *name_space, const char *data) { struct kmip_node *ret = NULL, *ns, *d = NULL; if (name_space == NULL) return NULL; ns = kmip_node_new_text_string(KMIP_TAG_APPLICATION_NAMESPACE, NULL, name_space); if (ns == NULL) goto out; if (data != NULL) { d = kmip_node_new_text_string(KMIP_TAG_APPLICATION_DATA, NULL, data); if (d == NULL) goto out; } ret = kmip_node_new_structure_va(KMIP_TAG_APPLICATION_DATA, NULL, 2, ns, d); out: kmip_node_free(ns); kmip_node_free(d); return ret; } /** * Gets the information from a Application Specific Information attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Application Specific Info. Yes Structure v1.0 * Application Namespace Yes Text String v1.0 * Application Data Yes/No Text String v1.0 * * @param node the KMIP node * @param name_space the application namespace * @param data the application data (can be NULL) * * @returns 0 on success, or a negative errno in case of an error * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ int kmip_get_application_specific_information(const struct kmip_node *node, const char **name_space, const char **data) { struct kmip_node *n; if (node == NULL || name_space == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_APPLICATION_DATA) return -EBADMSG; n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_APPLICATION_NAMESPACE, 0); if (n == NULL) return -EBADMSG; *name_space = kmip_node_get_text_string(n); kmip_node_free(n); if (data != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_APPLICATION_DATA, 0); if (n == NULL) return -EBADMSG; *data = kmip_node_get_text_string(n); kmip_node_free(n); } return 0; } /** * Constructs a Key Value Location attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Key Value Location Yes Structure v1.2 * Key Value Location Value Yes Text String v1.2 * Key Value Location Type Yes Enumeration v1.2 * * @param value the value of the key value location * @param type the type of the key value location * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_key_value_location(const char *value, enum kmip_key_value_location_type type) { struct kmip_node *name = NULL, *val, *typ; if (value == NULL) return NULL; val = kmip_node_new_text_string(KMIP_TAG_KEY_VALUE_LOCATION_VALUE, NULL, value); typ = kmip_node_new_enumeration(KMIP_TAG_KEY_VALUE_LOCATION_TYPE, NULL, type); if (val == NULL || typ == NULL) goto out; name = kmip_node_new_structure_va(KMIP_TAG_KEY_VALUE_LOCATION, NULL, 2, val, typ); out: kmip_node_free(val); kmip_node_free(typ); return name; } /** * Gets the information from a Key Value Location attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Key Value Location Yes Structure v1.2 * Key Value Location Value Yes Text String v1.2 * Key Value Location Type Yes Enumeration v1.2 * * @param node the KMIP node * @param value the value of the key value location * @param type the type of the key value location * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_key_value_location(const struct kmip_node *node, const char **value, enum kmip_key_value_location_type *type) { struct kmip_node *val, *typ; int rc = 0; if (node == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_KEY_VALUE_LOCATION) return -EBADMSG; val = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_KEY_VALUE_LOCATION_VALUE, 0); typ = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_KEY_VALUE_LOCATION_TYPE, 0); if (val == NULL || typ == NULL) { rc = -EBADMSG; goto out; } if (value != NULL) *value = kmip_node_get_text_string(val); if (type != NULL) *type = kmip_node_get_enumeration(typ); out: kmip_node_free(val); kmip_node_free(typ); return rc; } /** * Constructs a Digest attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Digest Yes Structure v1.0 * Hashing Algorithm Yes Enumeration v1.0 * Digest Value Yes Byte String v1.0 * * @param hash_algo the hashing algorithm * @param digest the digest value * @param digest_len the digest length * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_digest(enum kmip_hashing_algo hash_algo, const unsigned char *digest, uint32_t digest_len) { struct kmip_node *ret = NULL, *algo, *val; if (digest == NULL || digest_len == 0) return NULL; algo = kmip_node_new_enumeration(KMIP_TAG_HASHING_ALGORITHM, NULL, hash_algo); val = kmip_node_new_byte_string(KMIP_TAG_DIGEST_VALUE, NULL, digest, digest_len); if (algo == NULL || val == NULL) goto out; ret = kmip_node_new_structure_va(KMIP_TAG_DIGEST, NULL, 2, algo, val); out: kmip_node_free(algo); kmip_node_free(val); return ret; } /** * Gets the information from a Digest attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Digest Yes Structure v1.0 * Hashing Algorithm Yes Enumeration v1.0 * Digest Value Yes Byte String v1.0 * * @param node the KMIP node * @param hash_algo the hashing algorithm (can be NULL) * @param digest the digest value (can be NULL) * @param digest_len the digest length (can be NULL) * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_digest(const struct kmip_node *node, enum kmip_hashing_algo *hash_algo, const unsigned char **digest, uint32_t *digest_len) { struct kmip_node *algo, *val; int rc = 0; if (node == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_DIGEST) return -EBADMSG; algo = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_HASHING_ALGORITHM, 0); val = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_DIGEST_VALUE, 0); if (algo == NULL || val == NULL) { rc = -EBADMSG; goto out; } if (hash_algo != NULL) *hash_algo = kmip_node_get_enumeration(algo); if (digest != NULL) *digest = kmip_node_get_byte_string(val, digest_len); out: kmip_node_free(algo); kmip_node_free(val); return rc; } /** * Constructs a Sensitive attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Sensitive Yes Boolean v1.4 * * @param sensitive the sensitive value * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_sensitive(bool sensitive) { return kmip_node_new_boolean(KMIP_TAG_SENSITIVE, NULL, sensitive); } /** * Gets the information from a Sensitive attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Sensitive Yes Boolean v1.4 * * @param node the KMIP node * @param sensitive the sensitive value * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_sensitive(const struct kmip_node *node, bool *sensitive) { if (node == NULL || sensitive == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_SENSITIVE) return -EBADMSG; *sensitive = kmip_node_get_boolean(node); return 0; } /** * Constructs a Always Sensitive attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Always Sensitive Yes Boolean v1.4 * * @param sensitive the sensitive value * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_always_sensitive(bool sensitive) { return kmip_node_new_boolean(KMIP_TAG_ALWAYS_SENSITIVE, NULL, sensitive); } /** * Gets the information from a Always Sensitive attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Always Sensitive Yes Boolean v1.4 * * @param node the KMIP node * @param sensitive the sensitive value * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_always_sensitive(const struct kmip_node *node, bool *sensitive) { if (node == NULL || sensitive == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_ALWAYS_SENSITIVE) return -EBADMSG; *sensitive = kmip_node_get_boolean(node); return 0; } /** * Constructs a Extractable attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Extractable Yes Boolean v1.4 * * @param extractable the extractable value * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_extractable(bool extractable) { return kmip_node_new_boolean(KMIP_TAG_EXTRACTABLE, NULL, extractable); } /** * Gets the information from a Extractable attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Extractable Yes Boolean v1.4 * * @param node the KMIP node * @param extractable the extractable value * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_extractable(const struct kmip_node *node, bool *extractable) { if (node == NULL || extractable == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_EXTRACTABLE) return -EBADMSG; *extractable = kmip_node_get_boolean(node); return 0; } /** * Constructs a Never Extractable attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Never Extractable Yes Boolean v1.4 * * @param extractable the extractable value * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_never_extractable(bool extractable) { return kmip_node_new_boolean(KMIP_TAG_NEVER_EXTRACTABLE, NULL, extractable); } /** * Gets the information from a Never Extractable attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Never Extractable Yes Boolean v1.4 * * @param node the KMIP node * @param extractable the extractable value * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_never_extractable(const struct kmip_node *node, bool *extractable) { if (node == NULL || extractable == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_NEVER_EXTRACTABLE) return -EBADMSG; *extractable = kmip_node_get_boolean(node); return 0; } /** * Constructs a Link attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Link Yes Structure v1.0 * Link Type Yes Enumeration v1.0 * Linked Object Identifier Yes Text String v1.0 * Enumeration v2.0 * Integer v2.0 * * @param type the link type * @param obj_id the linked object identifier * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_link(enum kmip_link_type type, struct kmip_node *obj_id) { struct kmip_node *ret = NULL, *typ; if (obj_id == NULL) return NULL; typ = kmip_node_new_enumeration(KMIP_TAG_LINK_TYPE, NULL, type); if (typ == NULL) return NULL; ret = kmip_node_new_structure_va(KMIP_TAG_LINK, NULL, 2, typ, obj_id); kmip_node_free(typ); return ret; } /** * Gets the information from a Link attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Link Yes Structure v1.0 * Link Type Yes Enumeration v1.0 * Linked Object Identifier Yes Text String v1.0 * Enumeration v2.0 * Integer v2.0 * * @param node the KMIP node * @param type the link type * @param obj_id the linked object identifier * * @returns 0 on success, or a negative errno in case of an error * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ int kmip_get_link(const struct kmip_node *node, enum kmip_link_type *type, struct kmip_node **obj_id) { struct kmip_node *n; if (type == NULL || obj_id == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_LINK) return -EBADMSG; n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_LINK_TYPE, 0); if (n == NULL) return -EBADMSG; *type = kmip_node_get_enumeration(n); kmip_node_free(n); *obj_id = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_LINKED_OBJECT_IDENTIFIER, 0); if (*obj_id == NULL) return -EBADMSG; return 0; } /** * Constructs a Linked Object Identifier attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Linked Object Identifier Yes Text String v1.0 * Enumeration v2.0 * Integer v2.0 * * @param text_id the linked identifier as text string (or NULL) * @param enum_id the linked identifier as enumeration (or 0) * @param int_id the linked identifier as integer * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_linked_object_identifier(const char *text_id, enum kmip_unique_identifier enum_id, int32_t int_id) { if (text_id != NULL && enum_id != 0) return NULL; if (text_id != NULL) return kmip_node_new_text_string( KMIP_TAG_LINKED_OBJECT_IDENTIFIER, NULL, text_id); if (enum_id != 0) return kmip_node_new_enumeration( KMIP_TAG_LINKED_OBJECT_IDENTIFIER, NULL, enum_id); return kmip_node_new_integer(KMIP_TAG_LINKED_OBJECT_IDENTIFIER, NULL, int_id); } /** * Gets the information from a Linked Object Identifier attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Linked Object Identifier Yes Text String v1.0 * Enumeration v2.0 * Integer v2.0 * * @param node the KMIP node * @param text_id the linked identifier as text string (can be NULL) * @param enum_id the linked identifier as enumeration (can be NULL) * @param int_id the linked identifier as integer (can be NULL) * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_linked_object_identifier(const struct kmip_node *node, const char **text_id, enum kmip_unique_identifier *enum_id, int32_t *int_id) { if (node == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_LINKED_OBJECT_IDENTIFIER) return -EBADMSG; if (text_id != NULL) { if (kmip_node_get_type(node) == KMIP_TYPE_TEXT_STRING) *text_id = kmip_node_get_text_string(node); else *text_id = NULL; } if (enum_id != NULL) { if (kmip_node_get_type(node) == KMIP_TYPE_ENUMERATION) *enum_id = kmip_node_get_enumeration(node); else *enum_id = 0; } if (int_id != NULL) { if (kmip_node_get_type(node) == KMIP_TYPE_INTEGER) *int_id = kmip_node_get_integer(node); else *int_id = 0; } return 0; } /** * Constructs a Operation Policy Name attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Operation Policy Name Yes Text String v1.x only * * @param policy the policy name * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_operation_policy_name(const char *policy) { return kmip_node_new_text_string(KMIP_TAG_OPERATION_POLICY_NAME, NULL, policy); } /** * Gets the information from a Operation Policy Name attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Operation Policy Name Yes Text String v1.x only * * @param node the KMIP node * @param policy the policy name * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_operation_policy_name(const struct kmip_node *node, const char **policy) { if (node == NULL || policy == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_OPERATION_POLICY_NAME) return -EBADMSG; *policy = kmip_node_get_text_string(node); return 0; } /** * Constructs a Lease Time attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Lease Time Yes Interval v1.0 * * @param lease_time the lease time * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_lease_time(uint32_t lease_time) { return kmip_node_new_interval(KMIP_TAG_LEASE_TIME, NULL, lease_time); } /** * Gets the information from a Lease Time attribute node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Lease Time Yes Interval v1.0 * * @param node the KMIP node * @param lease_time the lease time * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_lease_time(const struct kmip_node *node, uint32_t *lease_time) { if (node == NULL || lease_time == NULL) return -EBADMSG; if (kmip_node_get_tag(node) != KMIP_TAG_LEASE_TIME) return -EBADMSG; *lease_time = kmip_node_get_interval(node); return 0; } s390-tools-2.38.0/libkmipclient/https.c000066400000000000000000000541041502674226300176070ustar00rootroot00000000000000/* * libkmipclient - KMIP client library * * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include "lib/zt_common.h" #include "kmip.h" #include "utils.h" #define HTTP_HDR_CONTENT_TYPE "Content-Type:" #define CURL_ERROR_CHECK(rc, text, debug, label) \ do { \ if ((rc) != CURLE_OK) { \ kmip_debug((debug), "%s: %s", (text), \ curl_easy_strerror((rc))); \ goto label; \ } \ } while (0) struct curl_sslctx_cb_data { const struct kmip_connection *conn; bool debug; }; struct curl_write_cb_data { const struct kmip_connection *conn; bool error; bool debug; union { struct { json_tokener *tok; json_object *resp_obj; } json; struct { xmlParserCtxtPtr ctx; } xml; struct { BIO *resp_mem_bio; } ttlv; }; }; struct curl_header_cb_data { const struct kmip_connection *conn; bool error; bool debug; }; /** * Initializes a new HTTPS connection to a KMIP server. * * @param connn The KMIP connection * @param debug if true, debug messages are printed * * @returns 0 in case of success, or a negative errno value */ int kmip_connection_https_init(struct kmip_connection *conn, bool debug) { const char *content_type, *accept, *server, *tok; const struct curl_tlssessioninfo *info = NULL; bool port_found = false; struct stat sb; int rc; if (conn == NULL) return -EINVAL; if (strncmp(conn->config.server, "https://", 8) != 0) { kmip_debug(debug, "Server must start with 'https://'"); return -EINVAL; } /* Find port (if any) and beginning of uri */ server = conn->config.server + 8; if (*server == '[') { /* IPv6 address enclosed in square brackets */ tok = strchr(server, ']'); if (tok == NULL) { kmip_debug(debug, "malformed IPv6 address"); return -EINVAL; } tok++; port_found = (*tok == ':'); } else { /* hostname or IPv4 address */ tok = strchr(server, ':'); port_found = (tok != NULL); } conn->https.curl = curl_easy_init(); if (conn->https.curl == NULL) { kmip_debug(debug, "curl_easy_init failed"); return -EIO; } /* * The CURLOPT_SSL_CTX_FUNCTION callback only works with the OpenSSL * curl backend. Check that OpenSSL is the current curl backend. */ rc = curl_easy_getinfo(conn->https.curl, CURLINFO_TLS_SSL_PTR, &info); CURL_ERROR_CHECK(rc, "curl_easy_getinfo CURLINFO_TLS_SSL_PTR", debug, out); if (info->backend != CURLSSLBACKEND_OPENSSL) { kmip_debug(debug, "libcurl is not using the OpenSSL backend"); rc = -EIO; goto out; } rc = curl_easy_setopt(conn->https.curl, CURLOPT_VERBOSE, debug ? 1 : 0); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_VERBOSE", debug, out); rc = curl_easy_setopt(conn->https.curl, CURLOPT_URL, conn->config.server); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_URL", debug, out); if (!port_found) { rc = curl_easy_setopt(conn->https.curl, CURLOPT_PORT, KMIP_DEFAULT_HTTPS_PORT_NUM); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_URL", debug, out); } rc = curl_easy_setopt(conn->https.curl, CURLOPT_SSL_VERIFYPEER, conn->config.tls_verify_peer ? 1L : 0L); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_SSL_VERIFYPEER", debug, out); rc = curl_easy_setopt(conn->https.curl, CURLOPT_SSL_VERIFYHOST, conn->config.tls_verify_host ? 2L : 0L); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_SSL_VERIFYHOST", debug, out); if (conn->config.tls_ca != NULL) { if (stat(conn->config.tls_ca, &sb) != 0) { rc = -errno; kmip_debug(debug, "stat failed on '%s': %s", conn->config.tls_ca, strerror(-rc)); goto out; } if (S_ISDIR(sb.st_mode)) { rc = curl_easy_setopt(conn->https.curl, CURLOPT_CAPATH, conn->config.tls_ca); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_CAPATH", debug, out); } else { rc = curl_easy_setopt(conn->https.curl, CURLOPT_CAINFO, conn->config.tls_ca); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_CAINFO", debug, out); } } if (conn->config.tls_issuer_cert != NULL) { rc = curl_easy_setopt(conn->https.curl, CURLOPT_ISSUERCERT, conn->config.tls_issuer_cert); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_ISSUERCERT", debug, out); } if (conn->config.tls_pinned_pubkey != NULL) { rc = curl_easy_setopt(conn->https.curl, CURLOPT_PINNEDPUBLICKEY, conn->config.tls_pinned_pubkey); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_PINNEDPUBLICKEY", debug, out); } rc = curl_easy_setopt(conn->https.curl, CURLOPT_FOLLOWLOCATION, 0L); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_FOLLOWLOCATION", debug, out); if (conn->config.tls_cipher_list != NULL) { rc = curl_easy_setopt(conn->https.curl, CURLOPT_SSL_CIPHER_LIST, conn->config.tls_cipher_list); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_SSL_CIPHER_LIST", debug, out); } if (conn->config.tls13_cipher_list != NULL) { rc = curl_easy_setopt(conn->https.curl, CURLOPT_TLS13_CIPHERS, conn->config.tls13_cipher_list); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_TLS13_CIPHERS", debug, out); } switch (conn->config.encoding) { case KMIP_ENCODING_TTLV: content_type = "Content-Type: application/octet-stream"; accept = "Accept: application/octet-stream"; break; case KMIP_ENCODING_JSON: content_type = "Content-Type: application/json;charset=UTF-8"; accept = "Accept: application/json"; break; case KMIP_ENCODING_XML: content_type = "Content-Type: text/xml;charset=UTF-8"; accept = "Accept: text/xml"; break; default: kmip_debug(debug, "invalid encoding: %d", conn->config.encoding); rc = -EINVAL; goto out; } conn->https.headers = curl_slist_append(conn->https.headers, content_type); if (conn->https.headers == NULL) { kmip_debug(debug, "curl_slist_append failed"); rc = -ENOMEM; goto out; } conn->https.headers = curl_slist_append(conn->https.headers, accept); if (conn->https.headers == NULL) { kmip_debug(debug, "curl_slist_append failed"); rc = -ENOMEM; goto out; } conn->https.headers = curl_slist_append(conn->https.headers, "Accept-Charset: UTF-8"); if (conn->https.headers == NULL) { kmip_debug(debug, "curl_slist_append failed"); rc = -ENOMEM; goto out; } /* Disable "Expect: 100-continue" */ conn->https.headers = curl_slist_append(conn->https.headers, "Expect:"); if (conn->https.headers == NULL) { kmip_debug(debug, "curl_slist_append failed"); rc = -ENOMEM; goto out; } /* As per KMIP HTTPS profile: Cache-Control: no-cache */ conn->https.headers = curl_slist_append(conn->https.headers, "Cache-Control: no-cache"); if (conn->https.headers == NULL) { kmip_debug(debug, "curl_slist_append failed"); rc = -ENOMEM; goto out; } rc = curl_easy_setopt(conn->https.curl, CURLOPT_HTTPHEADER, conn->https.headers); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_HTTPHEADER", debug, out); rc = 0; out: if (rc != 0) kmip_connection_https_term(conn); return rc; } /** * This callback called before the SSL handshake is performed. * It sets the client certificate and private key into the context. * It also adds a pinned server certificate to the SSL certificate store, so * that it is treated as trusted, although it might be self-signed. */ static CURLcode mkip_connection_https_sslctx_cb(CURL *UNUSED(curl), void *sslctx, void *parm) { struct curl_sslctx_cb_data *sslctx_cb = parm; SSL_CTX *ssl_ctx = (SSL_CTX *)sslctx; const struct kmip_connection *conn; X509_STORE *store; X509 *cert = NULL; FILE *fp; int rc; if (ssl_ctx == NULL || sslctx_cb == NULL || sslctx_cb->conn == NULL) return CURLE_ABORTED_BY_CALLBACK; conn = sslctx_cb->conn; if (SSL_CTX_use_certificate_file(sslctx, conn->config.tls_client_cert, SSL_FILETYPE_PEM) != 1) { kmip_debug(sslctx_cb->debug, "Failed to load the client " "certificate '%s'", conn->config.tls_client_cert); if (sslctx_cb->debug) ERR_print_errors_fp(stderr); return CURLE_ABORTED_BY_CALLBACK; } if (SSL_CTX_use_PrivateKey(ssl_ctx, conn->config.tls_client_key) != 1) { kmip_debug(sslctx_cb->debug, "Failed to set the client key"); if (sslctx_cb->debug) ERR_print_errors_fp(stderr); return CURLE_ABORTED_BY_CALLBACK; } if (conn->config.tls_server_cert == NULL) return CURLE_OK; store = SSL_CTX_get_cert_store(ssl_ctx); if (store == NULL) { kmip_debug(sslctx_cb->debug, "Failed to get SSL Store"); if (sslctx_cb->debug) ERR_print_errors_fp(stderr); return CURLE_ABORTED_BY_CALLBACK; } fp = fopen(conn->config.tls_server_cert, "r"); if (fp == NULL) { rc = -errno; kmip_debug(sslctx_cb->debug, "Failed to read server cert '%s': %s", conn->config.tls_server_cert, strerror(-rc)); return CURLE_ABORTED_BY_CALLBACK; } cert = PEM_read_X509(fp, NULL, NULL, NULL); fclose(fp); if (cert == NULL) { kmip_debug(sslctx_cb->debug, "Failed to read the server " "certificate from file '%s'", conn->config.tls_server_cert); if (sslctx_cb->debug) ERR_print_errors_fp(stderr); return CURLE_ABORTED_BY_CALLBACK; } if (sslctx_cb->debug) { kmip_debug(sslctx_cb->debug, "Pinned server certificate:"); X509_print_ex_fp(stderr, cert, XN_FLAG_COMPAT, X509_FLAG_COMPAT); } rc = X509_STORE_add_cert(store, cert); if (rc != 1) { kmip_debug(sslctx_cb->debug, "Failed to add server " "certificate to SSL Store"); if (sslctx_cb->debug) ERR_print_errors_fp(stderr); X509_free(cert); return CURLE_ABORTED_BY_CALLBACK; } X509_free(cert); return CURLE_OK; } /** * Callback called during curl_easy_perform() to handle received headers. * Check for the expected response content type. */ static size_t mkip_connection_https_header_cb(void *contents, size_t size, size_t nmemb, void *userp) { struct curl_header_cb_data *cb = (struct curl_header_cb_data *)userp; size_t num = size * nmemb; const char *content_type; char *hdr = contents; size_t ofs; char *val; if (num < strlen(HTTP_HDR_CONTENT_TYPE)) goto out; if (strncasecmp(hdr, HTTP_HDR_CONTENT_TYPE, strlen(HTTP_HDR_CONTENT_TYPE)) != 0) goto out; ofs = strlen(HTTP_HDR_CONTENT_TYPE); val = hdr + ofs; while (*val == ' ' && ofs < num) { ofs++; val++; } if (ofs >= num) goto out; switch (cb->conn->config.encoding) { case KMIP_ENCODING_TTLV: content_type = "application/octet-stream"; break; case KMIP_ENCODING_JSON: content_type = "application/json"; break; case KMIP_ENCODING_XML: content_type = "text/xml"; break; default: return 0; } if (num - ofs >= strlen(content_type) && strncasecmp(val, content_type, strlen(content_type)) == 0) goto out; cb->error = true; kmip_debug(cb->debug, "Unexpected response Content-Type: %.*s", (int)(num - ofs), val); return 0; out: return num; } /** * Callback called during curl_easy_perform() to handle received data. * Parse the (potentially partial) KMIP data. */ static size_t mkip_connection_https_write_cb(void *contents, size_t size, size_t nmemb, void *userp) { struct curl_write_cb_data *cb = (struct curl_write_cb_data *)userp; enum json_tokener_error jerr; size_t num = size * nmemb; int rc; switch (cb->conn->config.encoding) { case KMIP_ENCODING_TTLV: kmip_debug(cb->debug, "Response Data (TTLV): %lu bytes", num); if (cb->debug) kmip_print_dump(__func__, (unsigned char *)contents, num, 2); if (BIO_write(cb->ttlv.resp_mem_bio, contents, num) != (int)num) { cb->error = true; kmip_debug(cb->debug, "BIO_write failed"); return 0; } break; case KMIP_ENCODING_JSON: kmip_debug(cb->debug, "Response Data (JSON):"); kmip_debug(cb->debug, " ->%*s<-", (int)num, (char *)contents); if (cb->json.resp_obj != NULL) { kmip_debug(cb->debug, "JSON data already complete, but " "additional data received"); cb->error = true; return 0; } cb->json.resp_obj = json_tokener_parse_ex(cb->json.tok, (const char *)contents, num); if (cb->json.resp_obj == NULL) { jerr = json_tokener_get_error(cb->json.tok); if (jerr == json_tokener_continue) goto out; cb->error = true; kmip_debug(cb->debug, "json_tokener_parse_ex failed: %s", json_tokener_error_desc(jerr)); return 0; } break; case KMIP_ENCODING_XML: kmip_debug(cb->debug, "Response Data (XML):"); kmip_debug(cb->debug, " ->%*s<-", (int)num, (char *)contents); rc = xmlParseChunk(cb->xml.ctx, (const char *)contents, num, 0); if (rc != XML_ERR_OK) { cb->error = true; kmip_debug(cb->debug, "xmlParseChunk failed: %d", rc); return 0; } break; } out: return num; } /** * Perform a request over the KMIP connection * * @param conn the KMIP connection * @param request the request to send * @param response On return: the received response. Must be freed by * the caller. * * @param debug if true, debug messages are printed * * @returns 0 in case of success, or a negative errno value */ int kmip_connection_https_perform(struct kmip_connection *conn, struct kmip_node *request, struct kmip_node **response, bool debug) { struct curl_sslctx_cb_data sslctx_cb = { 0 }; struct curl_header_cb_data header_cb = { 0 }; struct curl_write_cb_data write_cb = { 0 }; char error_str[CURL_ERROR_SIZE] = { 0 }; json_object *req_json_obj = NULL; xmlNode *req_xml_obj = NULL; xmlDoc *req_xml_doc = NULL; BIO *req_mem_bio = NULL; char *req_buff = NULL; int req_buff_size = 0; long status_code; size_t size; int rc; if (conn == NULL || request == NULL || response == NULL) return -EINVAL; *response = NULL; rc = curl_easy_setopt(conn->https.curl, CURLOPT_ERRORBUFFER, error_str); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_ERRORBUFFER", debug, out); /* Setup SSL Context callback */ sslctx_cb.conn = conn; sslctx_cb.debug = debug; rc = curl_easy_setopt(conn->https.curl, CURLOPT_SSL_CTX_FUNCTION, mkip_connection_https_sslctx_cb); CURL_ERROR_CHECK(rc, "curl_easy_setopt " "CURLOPT_SSL_CTX_FUNCTION", debug, out); rc = curl_easy_setopt(conn->https.curl, CURLOPT_SSL_CTX_DATA, &sslctx_cb); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_SSL_CTX_DATA", debug, out); /* Setup write callback to handle received data */ write_cb.conn = conn; write_cb.debug = debug; switch (conn->config.encoding) { case KMIP_ENCODING_TTLV: write_cb.ttlv.resp_mem_bio = BIO_new(BIO_s_mem()); if (write_cb.ttlv.resp_mem_bio == NULL) { kmip_debug(debug, "BIO_new failed"); rc = -ENOMEM; goto out; } break; case KMIP_ENCODING_JSON: write_cb.json.tok = json_tokener_new(); if (write_cb.json.tok == NULL) { kmip_debug(debug, "json_tokener_new failed"); rc = -EIO; goto out; } break; case KMIP_ENCODING_XML: write_cb.xml.ctx = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL); if (write_cb.xml.ctx == NULL) { kmip_debug(debug, "xmlCreatePushParserCtxt failed"); rc = -EIO; goto out; } break; } rc = curl_easy_setopt(conn->https.curl, CURLOPT_WRITEFUNCTION, mkip_connection_https_write_cb); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_WRITEFUNCTION", debug, out); rc = curl_easy_setopt(conn->https.curl, CURLOPT_WRITEDATA, (void *)&write_cb); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_WRITEDATA", debug, out); /* Setup header callback to check content type */ header_cb.conn = conn; header_cb.debug = debug; rc = curl_easy_setopt(conn->https.curl, CURLOPT_HEADERFUNCTION, mkip_connection_https_header_cb); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_HEADERFUNCTION", debug, out); rc = curl_easy_setopt(conn->https.curl, CURLOPT_HEADERDATA, (void *)&header_cb); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_HEADERDATA", debug, out); /* Setup POST request and post data */ rc = curl_easy_setopt(conn->https.curl, CURLOPT_CUSTOMREQUEST, "POST"); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_CUSTOMREQUEST", debug, out); rc = curl_easy_setopt(conn->https.curl, CURLOPT_POST, 1L); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_POST", debug, out); switch (conn->config.encoding) { case KMIP_ENCODING_TTLV: req_mem_bio = BIO_new(BIO_s_mem()); if (req_mem_bio == NULL) { kmip_debug(debug, "BIO_new failed"); rc = -ENOMEM; goto out; } rc = kmip_encode_ttlv(request, req_mem_bio, &size, debug); if (rc != 0) { kmip_debug(debug, "kmip_encode_ttlv failed"); goto out; } req_buff_size = BIO_get_mem_data(req_mem_bio, &req_buff); kmip_debug(debug, "Request Data (TTLV): %d bytes", req_buff_size); if (debug) kmip_print_dump(__func__, (unsigned char *)req_buff, req_buff_size, 2); break; case KMIP_ENCODING_JSON: rc = kmip_encode_json(request, &req_json_obj, debug); if (rc != 0) { kmip_debug(debug, "kmip_encode_json failed"); goto out; } /* * The memory returned by json_object_to_json_string_ext * is freed when the JSON object is freed. */ req_buff = (char *)json_object_to_json_string_ext(req_json_obj, JSON_C_TO_STRING_PLAIN | JSON_C_TO_STRING_NOSLASHESCAPE); if (req_buff == NULL) { kmip_debug(debug, "json_object_to_json_string_ext failed"); rc = -EIO; goto out; } req_buff_size = strlen(req_buff); kmip_debug(debug, "Request Data (JSON):"); kmip_debug(debug, " ->%*s<-", req_buff_size, req_buff); break; case KMIP_ENCODING_XML: req_xml_doc = xmlNewDoc((xmlChar *)"1.0"); if (req_xml_doc == NULL) { kmip_debug(debug, "xmlNewDoc failed"); rc = -EIO; goto out; } rc = kmip_encode_xml(request, &req_xml_obj, debug); if (rc != 0) { kmip_debug(debug, "kmip_encode_xml failed"); goto out; } xmlDocSetRootElement(req_xml_doc, req_xml_obj); req_xml_obj = NULL; xmlDocDumpFormatMemoryEnc(req_xml_doc, (xmlChar **)&req_buff, &req_buff_size, "UTF-8", 0); if (req_buff == NULL || req_buff_size == 0) { kmip_debug(debug, "xmlDocDumpFormatMemoryEnc failed"); rc = -EIO; goto out; } kmip_debug(debug, "Request Data (XML):"); kmip_debug(debug, " ->%*s<-", req_buff_size, req_buff); break; } rc = curl_easy_setopt(conn->https.curl, CURLOPT_POSTFIELDSIZE, req_buff_size); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_POSTFIELDSIZE", debug, out); rc = curl_easy_setopt(conn->https.curl, CURLOPT_POSTFIELDS, req_buff); CURL_ERROR_CHECK(rc, "curl_easy_setopt CURLOPT_POSTFIELDS", debug, out); /* Perform the request */ rc = curl_easy_perform(conn->https.curl); if (rc != CURLE_OK) { kmip_debug(debug, "curl_easy_perform for '%s' failed: %s", conn->config.server, curl_easy_strerror(rc)); kmip_debug(debug, "Error: %s", error_str); if (header_cb.error) { kmip_debug(debug, "Unexpected Content-Type"); rc = -EBADMSG; } if (write_cb.error) { kmip_debug(debug, "JSON/XML parsing failed"); rc = -EBADMSG; } rc = -EIO; goto out; } /* Check response */ rc = curl_easy_getinfo(conn->https.curl, CURLINFO_RESPONSE_CODE, &status_code); CURL_ERROR_CHECK(rc, "curl_easy_getinfo CURLINFO_RESPONSE_CODE", debug, out); kmip_debug(debug, "HTTP status code: %d", status_code); if (status_code != 200) { rc = -EBADMSG; goto out; } /* Process received data */ switch (conn->config.encoding) { case KMIP_ENCODING_TTLV: rc = kmip_decode_ttlv(write_cb.ttlv.resp_mem_bio, NULL, response, debug); if (rc != 0) { kmip_debug(debug, "kmip_decode_ttlv failed"); goto out; } break; case KMIP_ENCODING_JSON: if (write_cb.json.resp_obj == NULL) { kmip_debug(debug, "JSON content not wellformed"); rc = -EBADMSG; goto out; } rc = kmip_decode_json(write_cb.json.resp_obj, NULL, response, debug); if (rc != 0) { kmip_debug(debug, "kmip_decode_json failed"); goto out; } break; case KMIP_ENCODING_XML: rc = xmlParseChunk(write_cb.xml.ctx, "", 0, 1); if (rc != XML_ERR_OK || !write_cb.xml.ctx->wellFormed || write_cb.xml.ctx->myDoc == NULL) { kmip_debug(debug, "XML content not wellformed"); rc = -EBADMSG; goto out; } rc = kmip_decode_xml(xmlDocGetRootElement( write_cb.xml.ctx->myDoc), NULL, response, debug); if (rc != 0) { kmip_debug(debug, "kmip_decode_xml failed"); goto out; } break; } rc = 0; out: /* Cleanup */ switch (conn->config.encoding) { case KMIP_ENCODING_TTLV: if (req_mem_bio != NULL) BIO_free(req_mem_bio); if (write_cb.ttlv.resp_mem_bio != NULL) BIO_free(write_cb.ttlv.resp_mem_bio); break; case KMIP_ENCODING_JSON: if (write_cb.json.tok != NULL) json_tokener_free(write_cb.json.tok); if (write_cb.json.resp_obj != NULL) json_object_put(write_cb.json.resp_obj); if (req_json_obj != NULL) json_object_put(req_json_obj); break; case KMIP_ENCODING_XML: if (write_cb.xml.ctx != NULL) { xmlFreeDoc(write_cb.xml.ctx->myDoc); xmlFreeParserCtxt(write_cb.xml.ctx); } if (req_xml_doc != NULL) xmlFreeDoc(req_xml_doc); if (req_xml_obj != NULL) xmlFreeNode(req_xml_obj); if (req_buff != NULL) xmlFree(req_buff); break; } if (rc != 0 && *response != NULL) { kmip_node_free(*response); *response = NULL; } curl_easy_setopt(conn->https.curl, CURLOPT_SSL_CTX_FUNCTION, NULL); curl_easy_setopt(conn->https.curl, CURLOPT_SSL_CTX_DATA, NULL); curl_easy_setopt(conn->https.curl, CURLOPT_WRITEFUNCTION, NULL); curl_easy_setopt(conn->https.curl, CURLOPT_WRITEDATA, NULL); curl_easy_setopt(conn->https.curl, CURLOPT_HEADERFUNCTION, NULL); curl_easy_setopt(conn->https.curl, CURLOPT_HEADERDATA, NULL); curl_easy_setopt(conn->https.curl, CURLOPT_ERRORBUFFER, NULL); curl_easy_setopt(conn->https.curl, CURLOPT_POSTFIELDS, NULL); curl_easy_setopt(conn->https.curl, CURLOPT_POSTFIELDSIZE, -1); return rc; } /** * Terminates a HTTPS KMIP connection. * * @param conn the KMIP connection to free */ void kmip_connection_https_term(struct kmip_connection *conn) { if (conn == NULL) return; if (conn->https.curl != NULL) curl_easy_cleanup(conn->https.curl); conn->https.curl = NULL; if (conn->https.headers != NULL) curl_slist_free_all(conn->https.headers); conn->https.headers = NULL; } s390-tools-2.38.0/libkmipclient/json.c000066400000000000000000000370121502674226300174150ustar00rootroot00000000000000/* * libkmipclient - KMIP client library * * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include "kmip.h" #include "names.h" #include "utils.h" #define KMIP_JSON_TAG "tag" #define KMIP_JSON_NAME "name" #define KMIP_JSON_TYPE "type" #define KMIP_JSON_VALUE "value" /** * Decode a KMIP node from the data in a JSON object using the JSON encoding. * * @param obj the JSON object to decode * @param parent the parent node or NULL if no parent exists. * @param node On return: the decoded node. The newly allocated * node has a reference count of 1. * @param debug if true, debug messages are printed * * @returns 0 in case of success, or a negative errno value */ int kmip_decode_json(const json_object *obj, struct kmip_node *parent, struct kmip_node **node, bool debug) { json_object *tag_obj, *type_obj, *value_obj, *name_obj; enum kmip_tag tag, v1_attr_tag = 0; enum json_type value_type; struct kmip_node *n, *e; const char *str; int rc, num, i; int64_t int64; if (obj == NULL || node == NULL) return -EINVAL; if (!json_object_is_type(obj, json_type_object)) { kmip_debug(debug, "Object is not a JSON object"); return -EINVAL; } n = calloc(1, sizeof(struct kmip_node)); if (n == NULL) { kmip_debug(debug, "calloc failed"); return -ENOMEM; } n->ref_count = 1; tag_obj = json_object_object_get(obj, KMIP_JSON_TAG); if (tag_obj == NULL || !json_object_is_type(tag_obj, json_type_string)) { kmip_debug(debug, "Missing or invalid '%s' in JSON object", KMIP_JSON_TAG); rc = -EBADMSG; goto out; } str = json_object_get_string(tag_obj); n->tag = kmip_tag_by_name_or_hex(str); if (n->tag == 0) { kmip_debug(debug, "Unknown 'tag' in JSON object: '%s'", str); rc = -EBADMSG; goto out; } name_obj = json_object_object_get(obj, KMIP_JSON_NAME); if (name_obj != NULL) { if (!json_object_is_type(name_obj, json_type_string)) { kmip_debug(debug, "Invalid '%s' in JSON object", KMIP_JSON_NAME); rc = -EBADMSG; goto out; } n->name = strdup(json_object_get_string(tag_obj)); } type_obj = json_object_object_get(obj, KMIP_JSON_TYPE); if (type_obj == NULL) { n->type = KMIP_TYPE_STRUCTURE; } else { if (!json_object_is_type(type_obj, json_type_string)) { kmip_debug(debug, "Missing or invalid '%s' in JSON object", KMIP_JSON_TYPE); rc = -EBADMSG; goto out; } str = json_object_get_string(type_obj); n->type = kmip_type_by_name_or_hex(str); if (n->type == 0) { kmip_debug(debug, "Unknown 'type' in JSON object: '%s'", str); rc = -EBADMSG; goto out; } } value_obj = json_object_object_get(obj, KMIP_JSON_VALUE); if (value_obj == NULL) { kmip_debug(debug, "Missing '%s' in JSON object", KMIP_JSON_VALUE); rc = -EBADMSG; goto out; } value_type = json_object_get_type(value_obj); /* * KMIP v1.x attribute values may be Enumerations or Integer Masks. * To correctly decode them, we need to know the tag. This is contained * in a Attribute Name node, which is an element of our parent node. */ if (n->tag == KMIP_TAG_ATTRIBUTE_VALUE) v1_attr_tag = kmip_find_v1_attribute_name_tag(parent); tag = (v1_attr_tag != 0 ? v1_attr_tag : n->tag); kmip_debug(debug, "tag: 0x%x type: 0x%x value_type: %d,", n->tag, n->type, value_type); switch (n->type) { case KMIP_TYPE_STRUCTURE: switch (value_type) { case json_type_null: break; case json_type_array: num = json_object_array_length(value_obj); for (i = 0; i < num; i++) { rc = kmip_decode_json( json_object_array_get_idx(value_obj, i), n, &e, debug); if (rc != 0) { kmip_debug(debug, "Failed to parse " "array element %d", i); goto out; } rc = kmip_node_add_structure_element(n, e); kmip_node_free(e); if (rc != 0) { kmip_debug(debug, "kmip_node_structure_add_element " "failed: rc: %d", rc); goto out; } } break; default: kmip_debug(debug, "Invalid JSON type %d for node type " "0x%x", value_type, n->type); rc = -EBADMSG; goto out; } break; case KMIP_TYPE_INTEGER: case KMIP_TYPE_LONG_INTEGER: switch (value_type) { case json_type_int: case json_type_double: int64 = json_object_get_int64(value_obj); break; case json_type_string: str = json_object_get_string(value_obj); if (n->type == KMIP_TYPE_INTEGER && kmip_is_tag_mask(tag)) { rc = kmip_parse_mask(tag, str, '|', &int64); if (rc != 0) { kmip_debug(debug, "Failed to parse " "mask string '%s'", str); goto out; } } else { rc = kmip_parse_hex_int(str, &int64); if (rc != 0) { kmip_debug(debug, "Failed to parse " "hex string '%s'", str); goto out; } } break; default: kmip_debug(debug, "Invalid JSON type %d for node type " "0x%x", value_type, n->type); rc = -EBADMSG; goto out; } if (n->type == KMIP_TYPE_INTEGER) n->integer_value = int64; else n->long_value = int64; break; case KMIP_TYPE_INTERVAL: switch (value_type) { case json_type_int: case json_type_double: n->interval_value = json_object_get_int64(value_obj); break; case json_type_string: str = json_object_get_string(value_obj); rc = kmip_parse_hex_int(str, &int64); if (rc != 0) { kmip_debug(debug, "Failed to parse " "hex string '%s'", str); goto out; } n->interval_value = int64; break; default: kmip_debug(debug, "Invalid JSON type %d for node type " "0x%x", value_type, n->type); rc = -EBADMSG; goto out; } break; case KMIP_TYPE_BIG_INTEGER: switch (value_type) { case json_type_int: case json_type_double: int64 = htobe64(json_object_get_int64(value_obj)); rc = kmip_decode_bignum((const unsigned char *)&int64, sizeof(int64), &n->big_integer_value); if (rc != 0) { kmip_debug(debug, "kmip_decode_bignum failed"); goto out; } break; case json_type_string: str = json_object_get_string(value_obj); rc = kmip_parse_bignum(str, true, &n->big_integer_value); if (rc != 0) { kmip_debug(debug, "Failed to parse bignum string '%s'", str); goto out; } break; default: kmip_debug(debug, "Invalid JSON type %d for node type " "0x%x", value_type, n->type); rc = -EBADMSG; goto out; } break; case KMIP_TYPE_ENUMERATION: switch (value_type) { case json_type_int: case json_type_double: n->enumeration_value = json_object_get_int64(value_obj); break; case json_type_string: str = json_object_get_string(value_obj); rc = kmip_enum_value_by_tag_name_or_hex(tag, str, &n->enumeration_value); if (rc != 0) { kmip_debug(debug, "Failed to parse enumeration '%s'", str); goto out; } break; default: kmip_debug(debug, "Invalid JSON type %d for node type " "0x%x", value_type, n->type); rc = -EBADMSG; goto out; } break; case KMIP_TYPE_BOOLEAN: switch (value_type) { case json_type_boolean: n->boolean_value = json_object_get_boolean(value_obj); break; case json_type_string: str = json_object_get_string(value_obj); rc = kmip_parse_hex_int(str, &int64); if (rc != 0) { kmip_debug(debug, "Failed to parse hex string '%s'", str); goto out; } n->boolean_value = (int64 != 0); break; default: kmip_debug(debug, "Invalid JSON type %d for node type " "0x%x", value_type, n->type); rc = -EBADMSG; goto out; } break; case KMIP_TYPE_TEXT_STRING: switch (value_type) { case json_type_string: n->text_value = strdup( json_object_get_string(value_obj)); if (n->text_value == NULL) { rc = -ENOMEM; goto out; } n->length = strlen(n->text_value); break; default: kmip_debug(debug, "Invalid JSON type %d for node type " "0x%x", value_type, n->type); rc = -EBADMSG; goto out; } break; case KMIP_TYPE_BYTE_STRING: switch (value_type) { case json_type_string: str = json_object_get_string(value_obj); rc = kmip_parse_hex(str, false, &n->bytes_value, &n->length); if (rc != 0) { kmip_debug(debug, "Failed to parse hex string '%s'", str); goto out; } break; default: kmip_debug(debug, "Invalid JSON type %d for node type " "0x%x", value_type, n->type); rc = -EBADMSG; goto out; } break; case KMIP_TYPE_DATE_TIME: switch (value_type) { case json_type_int: case json_type_double: n->date_time_value = json_object_get_int64(value_obj); break; case json_type_string: str = json_object_get_string(value_obj); rc = kmip_parse_timestamp(str, &n->date_time_value); if (rc != 0) { kmip_debug(debug, "Failed to parse time stamp '%s'", str); goto out; } break; default: kmip_debug(debug, "Invalid JSON type %d for node type " "0x%x", value_type, n->type); rc = -EBADMSG; goto out; } break; case KMIP_TYPE_DATE_TIME_EXTENDED: switch (value_type) { case json_type_int: case json_type_double: n->date_time_ext_value = json_object_get_int64(value_obj); break; case json_type_string: str = json_object_get_string(value_obj); rc = kmip_parse_hex_int(str, &int64); if (rc != 0) { kmip_debug(debug, "Failed to parse hex string '%s'", str); goto out; } n->date_time_ext_value = int64; break; default: kmip_debug(debug, "Invalid JSON type %d for node type " "0x%x", value_type, n->type); rc = -EBADMSG; goto out; } break; default: kmip_debug(debug, "unknown type: 0x%x", n->type); rc = -EBADMSG; goto out; } *node = n; rc = 0; out: if (rc != 0) kmip_node_free(n); return rc; } /** * Encode a KMIP node into a JSON object using the JSON encoding. * * @param node the node to encode * @param obj On return: the JSON object * @param debug if true, debug messages are printed * * @returns 0 in case of success, or a negative errno value */ int kmip_encode_json(const struct kmip_node *node, json_object **obj, bool debug) { json_object *ret_obj = NULL, *memb_obj, *elem_obj; enum kmip_tag tag, v1_attr_tag = 0; struct kmip_node *element; char outstr[200] = { 0 }; const char *str; int64_t int64; struct tm *tm; char *tmp; char *s; int rc; if (node == NULL || obj == NULL) return -EINVAL; kmip_debug(debug, "tag: 0x%x type: 0x%x", node->tag, node->type); ret_obj = json_object_new_object(); if (ret_obj == NULL) { kmip_debug(debug, "Failed to allocate a JSON object"); return -ENOMEM; } memb_obj = json_object_new_string( kmip_tag_name_or_hex_by_tag(node->tag, outstr)); if (memb_obj == NULL) { kmip_debug(debug, "Failed to build JSON object for tag"); rc = -ENOMEM; goto out; } rc = json_object_object_add(ret_obj, KMIP_JSON_TAG, memb_obj); if (rc != 0) { kmip_debug(debug, "Failed to add JSON object for tag"); rc = -EIO; goto out; } if (node->name != NULL) { memb_obj = json_object_new_string(node->name); if (memb_obj == NULL) { kmip_debug(debug, "Failed to build JSON object for name"); rc = -ENOMEM; goto out; } rc = json_object_object_add(ret_obj, KMIP_JSON_NAME, memb_obj); if (rc != 0) { kmip_debug(debug, "Failed to add JSON object for name"); rc = -EIO; goto out; } } if (node->type != KMIP_TYPE_STRUCTURE) { str = kmip_type_name_by_type(node->type); if (str == NULL) { kmip_debug(debug, "unknown type 0x%x", node->type); rc = -EINVAL; goto out; } memb_obj = json_object_new_string(str); if (memb_obj == NULL) { kmip_debug(debug, "Failed to build JSON object for type"); rc = -ENOMEM; goto out; } rc = json_object_object_add(ret_obj, KMIP_JSON_TYPE, memb_obj); if (rc != 0) { kmip_debug(debug, "Failed to add JSON object for type"); rc = -EIO; goto out; } } /* * KMIP v1.x attribute values may be Enumerations or Integer Masks. * To correctly encode them, we need to know the tag. This is contained * in a Attribute Name node, which is an element of our parent node. */ if (node->tag == KMIP_TAG_ATTRIBUTE_VALUE) v1_attr_tag = kmip_find_v1_attribute_name_tag(node->parent); tag = (v1_attr_tag != 0 ? v1_attr_tag : node->tag); switch (node->type) { case KMIP_TYPE_STRUCTURE: memb_obj = json_object_new_array(); if (memb_obj == NULL) { kmip_debug(debug, "Failed to build JSON object for value array"); rc = -ENOMEM; goto out; } element = node->structure_value; while (element != NULL) { rc = kmip_encode_json(element, &elem_obj, debug); if (rc != 0) { kmip_debug(debug, "kmip_encode_json failed"); goto out; } rc = json_object_array_add(memb_obj, elem_obj); if (rc != 0) { kmip_debug(debug, "json_object_array_add failed"); rc = EIO; goto out; } element = element->next; } break; case KMIP_TYPE_INTEGER: if (kmip_is_tag_mask(tag) && node->integer_value != 0) { rc = kmip_format_mask(tag, node->integer_value, '|', &tmp); if (rc != 0) { kmip_debug(debug, "kmip_format_mask failed"); goto out; } memb_obj = json_object_new_string(tmp); free(tmp); } else { memb_obj = json_object_new_int(node->integer_value); } break; case KMIP_TYPE_INTERVAL: memb_obj = json_object_new_int(node->interval_value); break; case KMIP_TYPE_LONG_INTEGER: case KMIP_TYPE_DATE_TIME_EXTENDED: if (node->type == KMIP_TYPE_LONG_INTEGER) int64 = node->long_value; else int64 = node->date_time_ext_value; /* any values >= 2^52 must be represented as hex strings */ if (int64 < 4503599627370496 && int64 > -4503599627370496) { memb_obj = json_object_new_int64(int64); } else { rc = kmip_format_hex((const unsigned char *)&int64, sizeof(int64), true, &tmp); if (rc != 0) { kmip_debug(debug, "kmip_format_hex failed"); goto out; } memb_obj = json_object_new_string(tmp); free(tmp); } break; case KMIP_TYPE_BIG_INTEGER: rc = kmip_format_bignum(node->big_integer_value, true, &s); if (rc != 0) { kmip_debug(debug, "kmip_format_bignum failed"); goto out; } memb_obj = json_object_new_string(s); free(s); break; case KMIP_TYPE_ENUMERATION: str = kmip_enum_name_by_tag_value(tag, node->enumeration_value); if (str != NULL) memb_obj = json_object_new_string(str); else memb_obj = json_object_new_int(node->enumeration_value); break; case KMIP_TYPE_BOOLEAN: memb_obj = json_object_new_boolean(node->boolean_value); break; case KMIP_TYPE_TEXT_STRING: memb_obj = json_object_new_string(node->text_value); break; case KMIP_TYPE_BYTE_STRING: rc = kmip_format_hex(node->bytes_value, node->length, false, &s); if (rc != 0) { kmip_debug(debug, "kmip_format_hex_long failed"); goto out; } memb_obj = json_object_new_string(s); free(s); break; case KMIP_TYPE_DATE_TIME: tm = gmtime((time_t *)&node->date_time_value); strftime(outstr, sizeof(outstr), KMIP_ISO8601_TIMESTAMP_UTC, tm); memb_obj = json_object_new_string(outstr); break; default: kmip_debug(debug, "unknown type: 0x%x", node->type); rc = -EINVAL; goto out; } if (memb_obj == NULL) { rc = -ENOMEM; goto out; } rc = json_object_object_add(ret_obj, KMIP_JSON_VALUE, memb_obj); if (rc != 0) { kmip_debug(debug, "Failed to add JSON object for value"); rc = -EIO; goto out; } rc = 0; *obj = ret_obj; out: if (rc != 0) json_object_put(ret_obj); return rc; } s390-tools-2.38.0/libkmipclient/key.c000066400000000000000000001302221502674226300172310ustar00rootroot00000000000000/* * libkmipclient - KMIP client library * * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #define _XOPEN_SOURCE #define _DEFAULT_SOURCE #include #include #include #include #include "kmip.h" #include "names.h" /** * Constructs a Key Block node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Key Block Structure v1.0 * Key Format Type Yes Enumeration v1.0 * Key Compression Type No Enumeration v1.0 * Key Value Yes various v1.0 * Cryptographic Algorithm Yes Enumeration v1.0 * Cryptographic Length Yes Integer v1.0 * Key Wrapping Data No Structure v1.0 * * @param format_type the key format type * @param format_type the key compression type (if 0 it is ignored) * @param key_value the key value node * @param algorithm the key algorithm (if 0 it is ignored) * @param length the cryptographic length (if <= 0 it is ignored) * @param wrappig_data the key wrapping data (can be NULL) * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_key_block(enum kmip_key_format_type format_type, enum kmip_key_compression_type compr_type, struct kmip_node *key_value, enum kmip_crypto_algo algorithm, int32_t length, struct kmip_node *wrappig_data) { struct kmip_node *ret = NULL, *fmt, *cmp = NULL, *algo = NULL; struct kmip_node *len = NULL; if (format_type == 0 || key_value == NULL) return NULL; fmt = kmip_node_new_enumeration(KMIP_TAG_KEY_FORMAT_TYPE, NULL, format_type); if (fmt == NULL) goto out; if (compr_type != 0) { cmp = kmip_node_new_enumeration(KMIP_TAG_KEY_COMPRESSION_TYPE, NULL, compr_type); if (cmp == NULL) goto out; } if (algorithm != 0) { algo = kmip_node_new_enumeration( KMIP_TAG_CRYPTOGRAPHIC_ALGORITHM, NULL, algorithm); if (algo == NULL) goto out; } if (length > 0) { len = kmip_node_new_integer(KMIP_TAG_CRYPTOGRAPHIC_LENGTH, NULL, length); if (len == NULL) goto out; } ret = kmip_node_new_structure_va(KMIP_TAG_KEY_BLOCK, NULL, 6, fmt, cmp, key_value, algo, len, wrappig_data); out: kmip_node_free(fmt); kmip_node_free(cmp); kmip_node_free(algo); kmip_node_free(len); return ret; } /** * Gets information from a Key Block node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Key Block Structure v1.0 * Key Format Type Yes Enumeration v1.0 * Key Compression Type No Enumeration v1.0 * Key Value Yes various v1.0 * Cryptographic Algorithm Yes Enumeration v1.0 * Cryptographic Length Yes Integer v1.0 * Key Wrapping Data No Structure v1.0 * * @param node the KMIP node * @param format_type On return: the key format type * @param format_type On return: the key compression type (0 if not avail, * can be NULL) * @param key_value On return: the key value node (can be NULL) * @param algorithm On return: the key algorithm (0 if not avail, can * be NULL) * @param length On return: the cryptographic length (0 if not avail, * can be NULL) * @param wrappig_data On return: the key wrapping data (can be NULL) * * @returns 0 on success, or a negative errno in case of an error. * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ int kmip_get_key_block(const struct kmip_node *node, enum kmip_key_format_type *format_type, enum kmip_key_compression_type *compr_type, struct kmip_node **key_value, enum kmip_crypto_algo *algorithm, int32_t *length, struct kmip_node **wrappig_data) { struct kmip_node *n; if (node == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_KEY_BLOCK) return -EBADMSG; if (format_type != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_KEY_FORMAT_TYPE, 0); if (n == NULL) return -EBADMSG; *format_type = kmip_node_get_enumeration(n); kmip_node_free(n); } if (compr_type != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_KEY_COMPRESSION_TYPE, 0); if (n != NULL) *compr_type = kmip_node_get_enumeration(n); else *compr_type = 0; kmip_node_free(n); } if (algorithm != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_CRYPTOGRAPHIC_ALGORITHM, 0); if (n != NULL) *algorithm = kmip_node_get_enumeration(n); else *algorithm = 0; kmip_node_free(n); } if (length != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_CRYPTOGRAPHIC_LENGTH, 0); if (n != NULL) *length = kmip_node_get_integer(n); else *length = -1; kmip_node_free(n); } if (key_value != NULL) { *key_value = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_KEY_VALUE, 0); if (*key_value == NULL) return -EBADMSG; } if (wrappig_data != NULL) *wrappig_data = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_KEY_WRAPPING_DATA, 0); return 0; } /** * Constructs a Key Value node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Key Value Structure v1.0 * Key Material Yes various v1.0 * Attribute No Structure v1.x only * ... may be repeated * Attributes No Structure v2.x only * * @param version the protocol version. If null, the current default * protocol version is used. * @param key_material the key material node * @param attrs_count the number of attributes following (can be 0) * @param v2_attrs the array of attributes (as KMIP v2.x attribute) * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_key_value(const struct kmip_version *version, struct kmip_node *key_material, unsigned int attrs_count, struct kmip_node **v2_attrs) { struct kmip_node *ret = NULL, *v2_attr, *v1_attr, *attrs = NULL; unsigned int i; int rc; if (key_material == NULL) return NULL; if (attrs_count > 0 && v2_attrs == NULL) return NULL; if (version == NULL) version = kmip_get_default_protocol_version(); if (version->major == 1) { /* KMIP v1.x */ ret = kmip_node_new_structure_va(KMIP_TAG_KEY_VALUE, NULL, 1, key_material); if (ret == NULL) return NULL; for (i = 0; i < attrs_count; i++) { v2_attr = v2_attrs[i]; if (v2_attr == NULL) continue; rc = kmip_v1_attr_from_v2_attr(v2_attr, &v1_attr); if (rc != 0) goto error; rc = kmip_node_add_structure_element(ret, v1_attr); kmip_node_free(v1_attr); if (rc != 0) goto error; } } else { /* KMIP >= v2.0 */ if (attrs_count > 0) { attrs = kmip_new_attributes(version, KMIP_TAG_ATTRIBUTES, attrs_count, v2_attrs); if (attrs == NULL) return NULL; } ret = kmip_node_new_structure_va(KMIP_TAG_KEY_VALUE, NULL, 2, key_material, attrs); kmip_node_free(attrs); } return ret; error: kmip_node_free(ret); return NULL; } /** * Constructs a Key Value node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Key Value Structure v1.0 * Key Material Yes various v1.0 * Attribute No Structure v1.x only * ... may be repeated * Attributes No Structure v2.x only * * @param version the protocol version. If null, the current default * protocol version is used. * @param key_material the key material node * @param attrs_count the number of attributes following (can be 0) * @param the attributes (as KMIP v2.x attribute) * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_key_value_va(const struct kmip_version *version, struct kmip_node *key_material, unsigned int attrs_count, ...) { struct kmip_node *ret, **attrs = NULL; unsigned int i, k; va_list ap; if (attrs_count > 0) { attrs = calloc(attrs_count, sizeof(struct kmip_node *)); if (attrs == NULL) return NULL; } va_start(ap, attrs_count); for (i = 0, k = 0; i < attrs_count; i++) { attrs[k] = va_arg(ap, struct kmip_node *); if (attrs[k] != NULL) k++; } va_end(ap); ret = kmip_new_key_value(version, key_material, k, attrs); if (attrs != NULL) free(attrs); return ret; } /** *Gets information from a Key Value node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Key Value (wrapped key value) Byte String v1.0 * Key Value (plaintext key value) Structure v1.0 * Key Material Yes various v1.0 * Attribute No Structure v1.x only * ... may be repeated * Attributes No Structure v2.x only * * @param node the KMIP node * @param key_material On return: the key material node (can be NULL) * @param num_attrs On return: the number of attributes (can be NULL). * @param index the index of the attribute to get * @param v2_attr On return: the attribute (as v2.x attribute) at the * specified index. Function returns -ENOENT if no * attribute is available at the index. * * @returns 0 on success, or a negative errno in case of an error. * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ int kmip_get_key_value(const struct kmip_node *node, struct kmip_node **key_material, unsigned int *num_attrs, unsigned int index, struct kmip_node **v2_attr) { struct kmip_node *attr; int rc; if (node == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_KEY_VALUE) return -EBADMSG; if (key_material != NULL) { switch (kmip_node_get_type(node)) { case KMIP_TYPE_BYTE_STRING: /* Wrapped key value */ *key_material = (struct kmip_node *)node; kmip_node_upref(*key_material); break; case KMIP_TYPE_STRUCTURE: /* plaintext key value */ *key_material = kmip_node_get_structure_element_by_index(node, 0); if (*key_material == NULL) return -EBADMSG; switch (kmip_node_get_type(*key_material)) { case KMIP_TYPE_BYTE_STRING: /* Raw, Opaque, PKCS1, PKCS8, ECPrivateKey */ break; case KMIP_TYPE_STRUCTURE: /* Transparent key formats */ switch (kmip_node_get_tag(*key_material)) { /* Transparent key formats: TAG_KEY_MATERIAL */ case KMIP_TAG_KEY_MATERIAL: break; default: rc = -EBADMSG; goto error; } break; default: rc = -EBADMSG; goto error; } break; default: return -EBADMSG; } } if (v2_attr == NULL || kmip_node_get_type(node) != KMIP_TYPE_STRUCTURE) return 0; attr = kmip_node_get_structure_element_by_index(node, 1); if (attr == NULL) { rc = -ENOENT; goto error; } if (kmip_node_get_tag(attr) == KMIP_TAG_ATTRIBUTES) { /* Its already a KMIP v2.x attributes structure */ rc = kmip_get_attributes(attr, num_attrs, index, v2_attr); kmip_node_free(attr); if (rc != 0) goto error; return 0; } /* Must be a KMIP v1.x attribute then */ kmip_node_free(attr); if (num_attrs != NULL) *num_attrs = kmip_node_get_structure_element_count(node) - 1; if (v2_attr == NULL) return 0; attr = kmip_node_get_structure_element_by_index(node, index + 1); if (attr == NULL) { rc = -ENOENT; goto error; } rc = kmip_v2_attr_from_v1_attr(attr, v2_attr); kmip_node_free(attr); if (rc != 0) goto error; return 0; error: if (key_material != NULL) { kmip_node_free(*key_material); *key_material = NULL; } return rc; } /** * Constructs a Key Wrapping Data node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Key Wrapping Data Structure v1.0 * Wrapping Method Yes Enumeration v1.0 * Encryption Key Information No Structure v1.0 * MAC/Signature Key Info. No Structure v1.0 * MAC/Signature No Byte String v1.0 * IV/Counter/Nonce No Byte String v1.0 * Encoding Option No Enumeration v1.2 * * @param version the protocol version. If null, the current default * protocol version is used. * @param wrap_method the key wrapping method * @param encr_key_info the encryption key info node (can be NULL) * @param mac_sign_key_info the MAC/Sign key info node (can be NULL) * @param mac_signature MAC/signature (can be NULL) * @param mac_signature_len the length of the MAC/Signature * @param iv_counter_nonce IV/Counter/Nonce (can be NULL) * @param iv_counter_nonce_len the length of theIV/Counter/Nonce * @param encoding the encoding option (can be 0, defaults to TTLV) * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_key_wrapping_data( const struct kmip_version *version, enum kmip_wrapping_method wrap_method, struct kmip_node *encr_key_info, struct kmip_node *mac_sign_key_info, const unsigned char *mac_signature, uint32_t mac_signature_len, const unsigned char *iv_counter_nonce, uint32_t iv_counter_nonce_len, enum kmip_encoding_option encoding) { struct kmip_node *ret = NULL, *wmeth, *mac = NULL, *iv = NULL; struct kmip_node *enc = NULL; if (wrap_method == 0) return NULL; if (version == NULL) version = kmip_get_default_protocol_version(); wmeth = kmip_node_new_enumeration(KMIP_TAG_WRAPPING_METHOD, NULL, wrap_method); if (wmeth == NULL) goto out; if (mac_signature != NULL && mac_signature_len > 0) { mac = kmip_node_new_byte_string(KMIP_TAG_MAC_SIGNATURE, NULL, mac_signature, mac_signature_len); if (mac == NULL) goto out; } if (iv_counter_nonce != NULL && iv_counter_nonce_len > 0) { iv = kmip_node_new_byte_string(KMIP_TAG_IV_COUNTER_NONCE, NULL, iv_counter_nonce, iv_counter_nonce_len); if (iv == NULL) goto out; } if (encoding != 0 && (version->major > 1 || (version->major == 1 && version->minor > 1))) { enc = kmip_node_new_enumeration(KMIP_TAG_ENCODING_OPTION, NULL, encoding); if (enc == NULL) goto out; } ret = kmip_node_new_structure_va(KMIP_TAG_KEY_WRAPPING_DATA, NULL, 6, wmeth, encr_key_info, mac_sign_key_info, mac, iv, enc); out: kmip_node_free(wmeth); kmip_node_free(mac); kmip_node_free(iv); kmip_node_free(enc); return ret; } /** *Gets information from a Key Wrapping Data node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Key Wrapping Data Structure v1.0 * Wrapping Method Yes Enumeration v1.0 * Encryption Key Information No Structure v1.0 * MAC/Signature Key Info. No Structure v1.0 * MAC/Signature No Byte String v1.0 * IV/Counter/Nonce No Byte String v1.0 * Encoding Option No Enumeration v1.2 * * @param node the KMIP node * @param wrap_method On return: the key wrapping method (can be NULL) * @param encr_key_info On return: the encryption key info node * (can be NULL) * @param mac_sign_key_info On return: the MAC/Sign key info node (can be NULL) * @param mac_signature On return: MAC/signature (can be NULL) * @param mac_signature_len On return: the length of the MAC/Signature * (can be NULL) * @param iv_counter_nonce On return: IV/Counter/Nonce (can be NULL) * @param iv_counter_nonce_len On return: the length of theIV/Counter/Nonce * (can be NULL) * @param encoding On return: the encoding option (can be NULL) * * @returns 0 on success, or a negative errno in case of an error. * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ int kmip_get_key_wrapping_data(const struct kmip_node *node, enum kmip_wrapping_method *wrap_method, struct kmip_node **encr_key_info, struct kmip_node **mac_sign_key_info, const unsigned char **mac_signature, uint32_t *mac_signature_len, const unsigned char **iv_counter_nonce, uint32_t *iv_counter_nonce_len, enum kmip_encoding_option *encoding) { struct kmip_node *n; if (node == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_KEY_WRAPPING_DATA) return -EBADMSG; if (wrap_method != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_WRAPPING_METHOD, 0); if (n == NULL) return -EBADMSG; *wrap_method = kmip_node_get_enumeration(n); kmip_node_free(n); } if (mac_signature != NULL && mac_signature_len != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_MAC_SIGNATURE, 0); if (n != NULL) { *mac_signature = kmip_node_get_byte_string(n, mac_signature_len); } else { *mac_signature = NULL; *mac_signature_len = 0; } kmip_node_free(n); } if (iv_counter_nonce != NULL && iv_counter_nonce_len != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_IV_COUNTER_NONCE, 0); if (n != NULL) { *iv_counter_nonce = kmip_node_get_byte_string(n, iv_counter_nonce_len); } else { *iv_counter_nonce = NULL; *iv_counter_nonce_len = 0; } kmip_node_free(n); } if (encoding != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_ENCODING_OPTION, 0); *encoding = (n != NULL ? kmip_node_get_enumeration(n) : 0); kmip_node_free(n); } if (encr_key_info != NULL) *encr_key_info = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_ENCRYPTION_KEY_INFORMATION, 0); if (mac_sign_key_info != NULL) *mac_sign_key_info = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_MAC_SIGNATURE_KEY_INFORMATION, 0); return 0; } /** * Constructs a Key Wrapping Specification node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Key Wrapping Specification Structure v1.0 * Wrapping Method Yes Enumeration v1.0 * Encryption Key Information No Structure v1.0 * MAC/Signature Key Info. No Structure v1.0 * Attribute Name No Text String v1.0 * ... may be repeated * Encoding Option No Enumeration v1.2 * * @param version the protocol version. If null, the current default * protocol version is used. * @param wrap_method the key wrapping method * @param encr_key_info the encryption key info node (can be NULL) * @param mac_sign_key_info the MAC/Sign key info node (can be NULL) * @param encoding the encoding option (can be 0, defaults to TTLV) * @param attr_name_count the number of attribute names following * @param attr_names the array of attributes names (as const char *) * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_key_wrapping_specification( const struct kmip_version *version, enum kmip_wrapping_method wrap_method, struct kmip_node *encr_key_info, struct kmip_node *mac_sign_key_info, enum kmip_encoding_option encoding, unsigned int attr_name_count, const char **attr_names) { struct kmip_node *ret = NULL, *wmeth, *enc = NULL, *name; unsigned int i; int rc; if (wrap_method == 0) return NULL; if (version == NULL) version = kmip_get_default_protocol_version(); wmeth = kmip_node_new_enumeration(KMIP_TAG_WRAPPING_METHOD, NULL, wrap_method); if (wmeth == NULL) goto out; ret = kmip_node_new_structure_va(KMIP_TAG_KEY_WRAPPING_SPECIFICATION, NULL, 3, wmeth, encr_key_info, mac_sign_key_info); for (i = 0; i < attr_name_count; i++) { if (attr_names[i] == NULL) continue; name = kmip_node_new_text_string(KMIP_TAG_ATTRIBUTE_NAME, NULL, attr_names[i]); if (name == NULL) goto error; rc = kmip_node_add_structure_element(ret, name); kmip_node_free(name); if (rc != 0) goto error; } if (encoding != 0 && (version->major > 1 || (version->major == 1 && version->minor > 1))) { enc = kmip_node_new_enumeration(KMIP_TAG_ENCODING_OPTION, NULL, encoding); if (enc == NULL) goto error; rc = kmip_node_add_structure_element(ret, enc); if (rc != 0) goto error; } goto out; error: kmip_node_free(ret); ret = NULL; out: kmip_node_free(wmeth); kmip_node_free(enc); return ret; } /** * Constructs a Key Wrapping Specification node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Key Wrapping Specification Structure v1.0 * Wrapping Method Yes Enumeration v1.0 * Encryption Key Information No Structure v1.0 * MAC/Signature Key Info. No Structure v1.0 * Attribute Name No Text String v1.0 * ... may be repeated * Encoding Option No Enumeration v1.2 * * @param version the protocol version. If null, the current default * protocol version is used. * @param wrap_method the key wrapping method * @param encr_key_info the encryption key info node (can be NULL) * @param mac_sign_key_info the MAC/Sign key info node (can be NULL) * @param encoding the encoding option (can be 0, defaults to TTLV) * @param attr_name_count the number of atribute names following * @param the attributes names (as const char *) * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_key_wrapping_specification_va( const struct kmip_version *version, enum kmip_wrapping_method wrap_method, struct kmip_node *encr_key_info, struct kmip_node *mac_sign_key_info, enum kmip_encoding_option encoding, unsigned int attr_name_count, ...) { const char **names = NULL; struct kmip_node *ret; unsigned int i; va_list ap; if (attr_name_count > 0) { names = calloc(attr_name_count, sizeof(const char *)); if (names == NULL) return NULL; } va_start(ap, attr_name_count); for (i = 0; i < attr_name_count; i++) names[i] = va_arg(ap, const char *); va_end(ap); ret = kmip_new_key_wrapping_specification(version, wrap_method, encr_key_info, mac_sign_key_info, encoding, attr_name_count, names); if (names != NULL) free(names); return ret; } /** *Gets information from a Key Wrapping Specification node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Key Wrapping Specification Structure v1.0 * Wrapping Method Yes Enumeration v1.0 * Encryption Key Information No Structure v1.0 * MAC/Signature Key Info. No Structure v1.0 * Attribute Name No Text String v1.0 * ... may be repeated * Encoding Option No Enumeration v1.2 * * @param node the KMIP node * @param wrap_method On return: the key wrapping method (can be NULL) * @param encr_key_info On return: the encryption key info node * (can be NULL) * @param mac_sign_key_info On return: the MAC/Sign key info node (can be NULL) * @param encoding On return: the encoding option (can be NULL) * @param num_attr_names On return: the number of attributes (can be NULL). * @param attr_name_index The index of the attribute name to return * @param attr_name On return: the attribute name at the specified index * (can be NULL). Function returns -ENOENT if no name * is available at the index. * * @returns 0 on success, or a negative errno in case of an error. * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ int kmip_get_key_wrapping_specification(const struct kmip_node *node, enum kmip_wrapping_method *wrap_method, struct kmip_node **encr_key_info, struct kmip_node **mac_sign_key_info, enum kmip_encoding_option *encoding, unsigned int *num_attr_names, unsigned int attr_name_index, const char **attr_name) { struct kmip_node *n; if (node == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_KEY_WRAPPING_SPECIFICATION) return -EBADMSG; if (wrap_method != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_WRAPPING_METHOD, 0); if (n == NULL) return -EBADMSG; *wrap_method = kmip_node_get_enumeration(n); kmip_node_free(n); } if (encoding != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_ENCODING_OPTION, 0); *encoding = (n != NULL ? kmip_node_get_enumeration(n) : 0); kmip_node_free(n); } if (num_attr_names != NULL) *num_attr_names = kmip_node_get_structure_element_by_tag_count( node, KMIP_TAG_ATTRIBUTE_NAME); if (attr_name != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_ATTRIBUTE_NAME, attr_name_index); if (n == NULL) return -ENOENT; *attr_name = kmip_node_get_text_string(n); kmip_node_free(n); } if (encr_key_info != NULL) *encr_key_info = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_ENCRYPTION_KEY_INFORMATION, 0); if (mac_sign_key_info != NULL) *mac_sign_key_info = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_MAC_SIGNATURE_KEY_INFORMATION, 0); return 0; } /** * Constructs a Encryption Key Information or MAC/Signature Key Information * node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Key Information Structure v1.0 * Unique Identifier Yes Text String v1.0 * Enumeration v2.0 * Integer v2.0 * Cryptographic Parameters No Structure v1.0 * * @param mac_sign if true a MAC/Signature Key Information node is * created, otherwise a Encryption Key Information * node. * @param unique_id the unique ID node * @param crypto_params the cryptographic parameters node (can be NULL) * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_key_info(bool mac_sign, struct kmip_node *unique_id, struct kmip_node *crypto_params) { enum kmip_tag tag; if (unique_id == NULL) return NULL; tag = (mac_sign ? KMIP_TAG_MAC_SIGNATURE_KEY_INFORMATION : KMIP_TAG_ENCRYPTION_KEY_INFORMATION); return kmip_node_new_structure_va(tag, NULL, 2, unique_id, crypto_params); } /** * Gets the information from an Encryption Key Information or MAC/Signature Key * Information node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Key Information Structure v1.0 * Unique Identifier Yes Text String v1.0 * Enumeration v2.0 * Integer v2.0 * Cryptographic Parameters No Structure v1.0 * * @param node the KMIP node * @param unique_id On return: the unique ID node (can be NULL) * @param crypto_params On return: the cryptographic parameters node (can * be NULL) * * @returns 0 on success, or a negative errno in case of an error * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ int kmip_get_key_info(const struct kmip_node *node, struct kmip_node **unique_id, struct kmip_node **crypto_params) { if (node == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_ENCRYPTION_KEY_INFORMATION && kmip_node_get_tag(node) != KMIP_TAG_MAC_SIGNATURE_KEY_INFORMATION) return -EBADMSG; if (unique_id != NULL) { *unique_id = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_UNIQUE_IDENTIFIER, 0); if (*unique_id == NULL) return -EBADMSG; } if (crypto_params != NULL) *crypto_params = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_CRYPTOGRAPHIC_PARAMETERS, 0); return 0; } /** * Constructs a Transparent Symmetric Key node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Key Material Structure v1.0 * Key Yes Byte String v1.0 * * @param key the key * @param key_length the key length * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_transparent_symmetric_key(const unsigned char *key, uint32_t key_length) { struct kmip_node *k, *ret; if (key == NULL || key_length == 0) return NULL; k = kmip_node_new_byte_string(KMIP_TAG_KEY, NULL, key, key_length); if (k == NULL) return NULL; ret = kmip_node_new_structure_va(KMIP_TAG_KEY_MATERIAL, NULL, 1, k); kmip_node_free(k); return ret; } /** * Gets the information from a Transparent Symmetric Key node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Key Material Structure v1.0 * Key Yes Byte String v1.0 * * @param node the KMIP node * @param key On return: the key * @param key_length On return: the key length * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_transparent_symmetric_key(const struct kmip_node *node, const unsigned char **key, uint32_t *key_length) { struct kmip_node *k; if (node == NULL || key == NULL || key_length == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_KEY_MATERIAL) return -EBADMSG; k = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_KEY, 0); if (k == NULL) return -EBADMSG; *key = kmip_node_get_byte_string(k, key_length); kmip_node_free(k); return 0; } /** * Constructs a Transparent RSA Public Key node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Key Material Structure v1.0 * Modulus Yes Big Integer v1.0 * Public Exponent Yes Big Integer v1.0 * * @param modulus the modulus as OpenSSL BIGNUM * @param pub_ext the public exponent as OpenSSL BIGNUM * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_transparent_rsa_public_key(const BIGNUM *modulus, const BIGNUM *pub_exp) { struct kmip_node *mod, *exp, *ret = NULL; if (modulus == NULL || pub_exp == NULL) return NULL; mod = kmip_node_new_bigint(KMIP_TAG_MODULUS, NULL, modulus); exp = kmip_node_new_bigint(KMIP_TAG_PUBLIC_EXPONENT, NULL, pub_exp); if (mod == NULL || exp == NULL) goto out; ret = kmip_node_new_structure_va(KMIP_TAG_KEY_MATERIAL, NULL, 2, mod, exp); out: kmip_node_free(mod); kmip_node_free(exp); return ret; } /** * Gets the information from a Transparent RSA Public Key node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Key Material Structure v1.0 * Modulus Yes Big Integer v1.0 * Public Exponent Yes Big Integer v1.0 * * @param node the KMIP node * @param modulus On return: the modulus as OpenSSL BIGNUM * @param pub_ext On return: the public exponent as OpenSSL BIGNUM * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_transparent_rsa_public_key(const struct kmip_node *node, const BIGNUM **modulus, const BIGNUM **pub_exp) { struct kmip_node *n; if (node == NULL || modulus == NULL || pub_exp == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_KEY_MATERIAL) return -EBADMSG; n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_MODULUS, 0); if (n == NULL) return -EBADMSG; *modulus = kmip_node_get_bigint(n); kmip_node_free(n); n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_PUBLIC_EXPONENT, 0); if (n == NULL) return -EBADMSG; *pub_exp = kmip_node_get_bigint(n); kmip_node_free(n); return 0; } /** * Constructs a PKCS#1 Public Key node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Key Material Byte String v1.0 * * @param pub_key the public key as OpenSSL PKEY * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_pkcs1_public_key(EVP_PKEY *pub_key) { struct kmip_node *ret = NULL; unsigned char *buf = NULL; int len; if (pub_key == NULL) return NULL; len = i2d_PublicKey(pub_key, &buf); if (len <= 0) return NULL; ret = kmip_node_new_byte_string(KMIP_TAG_KEY_MATERIAL, NULL, buf, len); OPENSSL_free(buf); return ret; } /** * Gets the information from a PKCS#1 Public Key node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Key Material Byte String v1.0 * * @param node the KMIP node * @param algo the algorithm of the key * @param pub_key On return: the public key as OpenSSL PKEY. Must be * freed by the caller using EVP_PKEY_free() when no * longer needed. * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_pkcs1_public_key(const struct kmip_node *node, enum kmip_crypto_algo algo, EVP_PKEY **pub_key) { const unsigned char *buf; uint32_t len = 0; int type; if (node == NULL || pub_key == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_KEY_MATERIAL) return -EBADMSG; buf = kmip_node_get_byte_string(node, &len); if (buf == NULL || len == 0) return -EBADMSG; switch (algo) { case KMIP_CRYPTO_ALGO_RSA: type = EVP_PKEY_RSA; break; case KMIP_CRYPTO_ALGO_DSA: type = EVP_PKEY_DSA; break; case KMIP_CRYPTO_ALGO_ECDSA: type = EVP_PKEY_EC; break; default: return -EINVAL; } *pub_key = d2i_PublicKey(type, NULL, &buf, len); if (*pub_key == NULL) return -EIO; return 0; } /** * Constructs a PKCS#8 Public Key node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Key Material Byte String v1.0 * * @param pub_key the public key as OpenSSL PKEY * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_pkcs8_public_key(EVP_PKEY *pub_key) { struct kmip_node *ret = NULL; unsigned char *buf = NULL; int len; if (pub_key == NULL) return NULL; len = i2d_PUBKEY(pub_key, &buf); if (len <= 0) return NULL; ret = kmip_node_new_byte_string(KMIP_TAG_KEY_MATERIAL, NULL, buf, len); OPENSSL_free(buf); return ret; } /** * Gets the information from a PKCS#8 Public Key node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Key Material Byte String v1.0 * * @param node the KMIP node * @param pub_key On return: the public key as OpenSSL PKEY. Must be * freed by the caller using EVP_PKEY_free() when no * longer needed. * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_pkcs8_public_key(const struct kmip_node *node, EVP_PKEY **pub_key) { const unsigned char *buf; uint32_t len = 0; if (node == NULL || pub_key == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_KEY_MATERIAL) return -EBADMSG; buf = kmip_node_get_byte_string(node, &len); if (buf == NULL || len == 0) return -EBADMSG; *pub_key = d2i_PUBKEY(NULL, &buf, len); if (*pub_key == NULL) return -EIO; return 0; } /** * Constructs a Raw Key node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Key Material Byte String v1.0 * * @param key the raw key * @param key_len the length of the key * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_raw_key(const unsigned char *key, uint32_t key_len) { if (key == NULL) return NULL; return kmip_node_new_byte_string(KMIP_TAG_KEY_MATERIAL, NULL, key, key_len); } /** * Gets the information from a Raw Key node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Key Material Byte String v1.0 * * @param node the KMIP node * @param key On return: the raw key * @param key_len On return: the length of the key * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_raw_key(const struct kmip_node *node, const unsigned char **key, uint32_t *key_len) { if (node == NULL || key == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_KEY_MATERIAL) return -EBADMSG; *key = kmip_node_get_byte_string(node, key_len); if (*key == NULL) return -EBADMSG; return 0; } /** * Constructs a Symmetric Key node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Symmetric Key Structure v1.0 * Key Block Yes Structure v1.0 * * @param keyblock the key block node * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_symmetric_key(struct kmip_node *keyblock) { if (keyblock == NULL) return NULL; return kmip_node_new_structure_va(KMIP_TAG_SYMMETRIC_KEY, NULL, 1, keyblock); } /** * Gets the information from a Symmetric Key node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Symmetric Key Structure v1.0 * Key Block Yes Structure v1.0 * * @param node the KMIP node * @param keyblock On return: the key block node * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_symmetric_key(const struct kmip_node *node, struct kmip_node **keyblock) { if (node == NULL || keyblock == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_SYMMETRIC_KEY) return -EBADMSG; *keyblock = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_KEY_BLOCK, 0); if (*keyblock == NULL) return -EBADMSG; return 0; } /** * Constructs a Public Key node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Public Key Structure v1.0 * Key Block Yes Structure v1.0 * * @param keyblock the key block node * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_public_key(struct kmip_node *keyblock) { if (keyblock == NULL) return NULL; return kmip_node_new_structure_va(KMIP_TAG_PUBLIC_KEY, NULL, 1, keyblock); } /** * Gets the information from a Public Key node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Public Key Structure v1.0 * Key Block Yes Structure v1.0 * * @param node the KMIP node * @param keyblock On return: the key block node * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_public_key(const struct kmip_node *node, struct kmip_node **keyblock) { if (node == NULL || keyblock == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_PUBLIC_KEY) return -EBADMSG; *keyblock = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_KEY_BLOCK, 0); if (*keyblock == NULL) return -EBADMSG; return 0; } s390-tools-2.38.0/libkmipclient/kmip.c000066400000000000000000001206311502674226300174040ustar00rootroot00000000000000/* * libkmipclient - KMIP client library * * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #define _XOPEN_SOURCE #define _DEFAULT_SOURCE #include #include #include #include #include #include #include "kmip.h" #include "utils.h" void __attribute__ ((constructor)) kmip_init(void); void __attribute__ ((destructor)) kmip_exit(void); /** * Constructs a new KMIP node with the specified tag and type, and an optional * name. The newly allocated node has a reference count of 1. * * @param tag the tag of the new node * @param name Optional: the name of the node (only used with JSON * or XML encoding). Can be NULL. * @param type the type of the new node * * @returns the allocated node, or NULL in case of an error */ static struct kmip_node *kmip_node_new(enum kmip_tag tag, const char *name, enum kmip_type type) { struct kmip_node *node; node = calloc(1, sizeof(struct kmip_node)); if (node == NULL) return NULL; node->ref_count = 1; node->tag = tag; node->type = type; if (name != NULL) { node->name = strdup(name); if (node->name == NULL) { free(node); return NULL; } } return node; } /** * Returns the tag of a KMIP node * * @param node the KMIP node * * @returns the tag */ enum kmip_tag kmip_node_get_tag(const struct kmip_node *node) { if (node == NULL) return 0; return node->tag; } /** * Returns the type of a KMIP node * * @param node the KMIP node * * @returns the type */ enum kmip_type kmip_node_get_type(const struct kmip_node *node) { if (node == NULL) return 0; return node->type; } /** * Returns the name of a KMIP node * * @param node the KMIP node * * @returns a copy of the name. The caller must free the returnd string. */ char *kmip_node_get_name(const struct kmip_node *node) { if (node == NULL) return NULL; if (node->name == NULL) return NULL; return strdup(node->name); } /** * Constructs a new KMIP node of type structure with the specified tag, and an * optional name, and the elements. The reference count of each added element is * increased. * * @param tag the tag of the new node * @param name Optional: the name of the node (only used with JSON * or XML encoding). Can be NULL. * @param num_elements the number of elements to add * @param elements the array elements to add. * * @returns the allocated node, or NULL in case of an error */ struct kmip_node *kmip_node_new_structure(enum kmip_tag tag, const char *name, unsigned int num_elements, struct kmip_node **elements) { struct kmip_node *node; int rc; node = kmip_node_new(tag, name, KMIP_TYPE_STRUCTURE); if (node == NULL) return NULL; rc = kmip_node_add_structure_elements(node, num_elements, elements); if (rc != 0) { kmip_node_free(node); return NULL; } return node; } /** * Constructs a new KMIP node of type structure with the specified tag, and an * optional name, and the elements. The reference count of each added element is * increased. * * @param tag the tag of the new node * @param name Optional: the name of the node (only used with JSON * or XML encoding). Can be NULL. * @param num_elements the number of elements following as variable args * @param the elements to add. Elements may be NULL, those * are skipped * * @returns the allocated node, or NULL in case of an error */ struct kmip_node *kmip_node_new_structure_va(enum kmip_tag tag, const char *name, unsigned int num_elements, ...) { struct kmip_node *node, **elements = NULL; unsigned int i; va_list ap; if (num_elements > 0) { elements = calloc(num_elements, sizeof(struct kmip_node *)); if (elements == NULL) return NULL; } va_start(ap, num_elements); for (i = 0; i < num_elements; i++) elements[i] = va_arg(ap, struct kmip_node *); va_end(ap); node = kmip_node_new_structure(tag, name, num_elements, elements); if (elements != NULL) free(elements); return node; } /** * Add an element to a KMIP node (which must be of type KMIP_TYPE_STRUCTURE). * The element is added as the last element. The reference count of the added * element is increased. * * @param node the structure node to add the element to * @param element the element to add * * @returns 0 in case of success, or a negative errno value */ int kmip_node_add_structure_element(struct kmip_node *node, struct kmip_node *element) { if (node == NULL || element == NULL) return -EINVAL; return kmip_node_add_structure_elements(node, 1, &element); } /** * Add elements to a KMIP node (which must be of type KMIP_TYPE_STRUCTURE). * The elements are added after the last element. The reference count of the * added elements is increased. * * @param node the structure node to add the element to * @param num_elements the number of elements to add * @param elements the array elements to add * * @returns 0 in case of success, or a negative errno value */ int kmip_node_add_structure_elements(struct kmip_node *node, unsigned int num_elements, struct kmip_node **elements) { struct kmip_node *element, *last; unsigned int i; if (node == NULL || (num_elements > 0 && elements == NULL)) return -EINVAL; if (node->type != KMIP_TYPE_STRUCTURE) return -EINVAL; if (node->structure_value == NULL) { last = NULL; } else { last = node->structure_value; while (last->next != NULL) last = last->next; } for (i = 0; i < num_elements; i++) { element = elements[i]; if (element == NULL) continue; kmip_node_upref(element); element->parent = node; element->next = NULL; if (last == NULL) node->structure_value = element; else last->next = element; last = element; } return 0; } /** * Returns the number of elements of a KMIP node of type structure * * @param node the KMIP node * * @returns the number of elements, or -1 if the node is not of type structure */ unsigned int kmip_node_get_structure_element_count(const struct kmip_node *node) { struct kmip_node *element; unsigned int i; if (node == NULL) return -1; if (node->type != KMIP_TYPE_STRUCTURE) return -1; element = node->structure_value; for (i = 0; element != NULL; i++) element = element->next; return i; } /** * Returns an element of a KMIP node of type structure * * @param node the KMIP node * @param index the index of the element to return * * @returns the element or NULL if no element is available at the specified * index, or the node is not of type structure. * The reference count of the returned element is increased. The caller must * free the element via kmip_node_free() when no longer needed. */ struct kmip_node *kmip_node_get_structure_element_by_index( const struct kmip_node *node, unsigned int index) { struct kmip_node *element; unsigned int i; if (node == NULL) return NULL; if (node->type != KMIP_TYPE_STRUCTURE) return NULL; element = node->structure_value; for (i = 0; i < index && element != NULL; i++) element = element->next; if (element != NULL) kmip_node_upref(element); return element; } /** * Returns the number of elements of a KMIP node of type structure of a * certain tag * * @param node the KMIP node * @param tag the tag to find * * @returns the number of elements, or -1 if the node is not of type structure */ unsigned int kmip_node_get_structure_element_by_tag_count( const struct kmip_node *node, enum kmip_tag tag) { struct kmip_node *element; unsigned int i; if (node == NULL) return -1; if (node->type != KMIP_TYPE_STRUCTURE) return -1; element = node->structure_value; for (i = 0; element != NULL; element = element->next) { if (element->tag != tag) continue; i++; } return i; } /** * Find a structure element by its tag. If multiple elements with the matching * tag are found, then the num'th one is returned. * * @param node the structure node to find the elements in * @param tag the tag to find * @param index the index of elements with the same tag to return. * * @returns the element node, or NULL if no element with the tag was found. * The reference count of the returned element is increased. The caller must * free the element via kmip_node_free() when no longer needed. */ struct kmip_node *kmip_node_get_structure_element_by_tag( const struct kmip_node *node, enum kmip_tag tag, unsigned int index) { struct kmip_node *e; if (node == NULL) return NULL; if (node->type != KMIP_TYPE_STRUCTURE) return NULL; e = node->structure_value; while (e != NULL) { if (e->tag == tag) { if (index == 0) { kmip_node_upref(e); return e; } index--; } e = e->next; } return NULL; } /** * Constructs a new KMIP node of type integer with the specified tag, and an * optional name, and the integer value * * @param tag the tag of the new node * @param name Optional: the name of the node (only used with JSON * or XML encoding). Can be NULL. * @param value the value * * @returns the allocated node, or NULL in case of an error */ struct kmip_node *kmip_node_new_integer(enum kmip_tag tag, const char *name, int32_t value) { struct kmip_node *node; node = kmip_node_new(tag, name, KMIP_TYPE_INTEGER); if (node == NULL) return NULL; node->integer_value = value; node->length = sizeof(int32_t); return node; } /** * Returns the value of a KMIP node of type integer * * @param node the KMIP node * * @returns the value of the node, or 0 if the node is not of type integer */ int32_t kmip_node_get_integer(const struct kmip_node *node) { if (node == NULL) return 0; if (node->type != KMIP_TYPE_INTEGER) return 0; return node->integer_value; } /** * Constructs a new KMIP node of type long integer with the specified tag, and * an optional name, and the long integer value * * @param tag the tag of the new node * @param name Optional: the name of the node (only used with JSON * or XML encoding). Can be NULL. * @param value the value * * @returns the allocated node, or NULL in case of an error */ struct kmip_node *kmip_node_new_long(enum kmip_tag tag, const char *name, int64_t value) { struct kmip_node *node; node = kmip_node_new(tag, name, KMIP_TYPE_LONG_INTEGER); if (node == NULL) return NULL; node->long_value = value; node->length = sizeof(int64_t); return node; } /** * Returns the value of a KMIP node of type long integer * * @param node the KMIP node * * @returns the value of the node, or 0 if the node is not of type long integer */ int64_t kmip_node_get_long(const struct kmip_node *node) { if (node == NULL) return 0; if (node->type != KMIP_TYPE_LONG_INTEGER) return 0; return node->long_value; } /** * Constructs a new KMIP node of type big integer with the specified tag, and * an optional name, and the big integer value * * @param tag the tag of the new node * @param name Optional: the name of the node (only used with JSON * or XML encoding). Can be NULL. * @param value the value as OpenSSL BIGNUM (can be NULL) * * @returns the allocated node, or NULL in case of an error */ struct kmip_node *kmip_node_new_bigint(enum kmip_tag tag, const char *name, const BIGNUM *value) { struct kmip_node *node; node = kmip_node_new(tag, name, KMIP_TYPE_BIG_INTEGER); if (node == NULL) return NULL; if (value != NULL) { node->big_integer_value = BN_dup(value); node->length = kmip_encode_bignum_length(value); } return node; } /** * Returns the value of a KMIP node of type big integer * * @param node the KMIP node * * @returns the value of the node, or NULL if the node is not of type big * integer, or no BIGNUM is set. The returned BIGNUM still belongs to the node, * and must not be freed by the caller. It is freed together with the node it * was obtained from. */ const BIGNUM *kmip_node_get_bigint(const struct kmip_node *node) { if (node == NULL) return NULL; if (node->type != KMIP_TYPE_BIG_INTEGER) return NULL; return node->big_integer_value; } /** * Constructs a new KMIP node of type enumeration with the specified tag, and * an optional name, and the enumeration value * * @param tag the tag of the new node * @param name Optional: the name of the node (only used with JSON * or XML encoding). Can be NULL. * @param enumeration the enumeration value * * @returns the allocated node, or NULL in case of an error */ struct kmip_node *kmip_node_new_enumeration(enum kmip_tag tag, const char *name, uint32_t enumeration) { struct kmip_node *node; node = kmip_node_new(tag, name, KMIP_TYPE_ENUMERATION); if (node == NULL) return NULL; node->enumeration_value = enumeration; node->length = sizeof(uint32_t); return node; } /** * Returns the value of a KMIP node of type enumeration * * @param node the KMIP node * * @returns the value of the node, or 0 if the node is not of type enumeration */ uint32_t kmip_node_get_enumeration(const struct kmip_node *node) { if (node == NULL) return 0; if (node->type != KMIP_TYPE_ENUMERATION) return 0; return node->enumeration_value; } /** * Constructs a new KMIP node of type boolean with the specified tag, and * an optional name, and the boolean value * * @param tag the tag of the new node * @param name Optional: the name of the node (only used with JSON * or XML encoding). Can be NULL. * @param value the value * * @returns the allocated node, or NULL in case of an error */ struct kmip_node *kmip_node_new_boolean(enum kmip_tag tag, const char *name, bool value) { struct kmip_node *node; node = kmip_node_new(tag, name, KMIP_TYPE_BOOLEAN); if (node == NULL) return NULL; node->boolean_value = value; node->length = sizeof(uint64_t); return node; } /** * Returns the value of a KMIP node of type boolean * * @param node the KMIP node * * @returns the value of the node, or 0 if the node is not of type boolean */ bool kmip_node_get_boolean(const struct kmip_node *node) { if (node == NULL) return false; if (node->type != KMIP_TYPE_BOOLEAN) return false; return node->boolean_value; } /** * Constructs a new KMIP node of type text string with the specified tag, and * an optional name, and the text string value * * @param tag the tag of the new node * @param name Optional: the name of the node (only used with JSON * or XML encoding). Can be NULL. * @param value the value (can be NULL) * * @returns the allocated node, or NULL in case of an error */ struct kmip_node *kmip_node_new_text_string(enum kmip_tag tag, const char *name, const char *value) { struct kmip_node *node; node = kmip_node_new(tag, name, KMIP_TYPE_TEXT_STRING); if (node == NULL) return NULL; if (value != NULL) { node->text_value = strdup(value); if (node->text_value == NULL) { free(node); return NULL; } node->length = strlen(value); } return node; } /** * Returns the value of a KMIP node of type text string * * @param node the KMIP node * * @returns the value of the node, or NULL if the node is not of type text * string or no string is set. The returned string still belongs to the node, * and must not be freed by the caller. It is freed together with the node it * was obtained from. */ const char *kmip_node_get_text_string(const struct kmip_node *node) { if (node == NULL) return NULL; if (node->type != KMIP_TYPE_TEXT_STRING) return NULL; return node->text_value; } /** * Constructs a new KMIP node of type byte string with the specified tag, and * an optional name, and the byte string value * * @param tag the tag of the new node * @param name Optional: the name of the node (only used with JSON * or XML encoding). Can be NULL. * @param value the byte string (can be NULL) * @param length the length of the byte string * * @returns the allocated node, or NULL in case of an error */ struct kmip_node *kmip_node_new_byte_string(enum kmip_tag tag, const char *name, const unsigned char *value, uint32_t length) { struct kmip_node *node; node = kmip_node_new(tag, name, KMIP_TYPE_BYTE_STRING); if (node == NULL) return NULL; if (value != NULL && length > 0) { node->bytes_value = malloc(length); if (node->bytes_value == NULL) { free(node); return NULL; } memcpy(node->bytes_value, value, length); node->length = length; } return node; } /** * Returns the value of a KMIP node of type byte string * * @param node the KMIP node * @param length On return, the length of the byte string * * @returns the value of the node, or NULL if the node is not of type byte * string or no string is set. The returned string still belongs to the node, * and must not be freed by the caller. It is freed together with the node it * was obtained from. */ const unsigned char *kmip_node_get_byte_string(const struct kmip_node *node, uint32_t *length) { if (node == NULL) return NULL; if (node->type != KMIP_TYPE_BYTE_STRING) return NULL; if (length != NULL) *length = node->length; return node->bytes_value; } /** * Constructs a new KMIP node of type date and time with the specified tag, and * an optional name, and the date and time value * * @param tag the tag of the new node * @param name Optional: the name of the node (only used with JSON * or XML encoding). Can be NULL. * @param value the value * * @returns the allocated node, or NULL in case of an error */ struct kmip_node *kmip_node_new_date_time(enum kmip_tag tag, const char *name, int64_t value) { struct kmip_node *node; node = kmip_node_new(tag, name, KMIP_TYPE_DATE_TIME); if (node == NULL) return NULL; node->date_time_value = value; node->length = sizeof(int64_t); return node; } /** * Returns the value of a KMIP node of type date and time * * @param node the KMIP node * * @returns the value of the node, or 0 if the node is not of type date and time */ int64_t kmip_node_get_date_time(const struct kmip_node *node) { if (node == NULL) return 0; if (node->type != KMIP_TYPE_DATE_TIME) return 0; return node->date_time_value; } /** * Constructs a new KMIP node of type interval with the specified tag, and * an optional name, and the interval value * * @param tag the tag of the new node * @param name Optional: the name of the node (only used with JSON * or XML encoding). Can be NULL. * @param value the value * * @returns the allocated node, or NULL in case of an error */ struct kmip_node *kmip_node_new_interval(enum kmip_tag tag, const char *name, uint32_t value) { struct kmip_node *node; node = kmip_node_new(tag, name, KMIP_TYPE_INTERVAL); if (node == NULL) return NULL; node->interval_value = value; node->length = sizeof(uint32_t); return node; } /** * Returns the value of a KMIP node of type interval * * @param node the KMIP node * * @returns the value of the node, or 0 if the node is not of type interval */ uint32_t kmip_node_get_interval(const struct kmip_node *node) { if (node == NULL) return 0; if (node->type != KMIP_TYPE_INTERVAL) return 0; return node->interval_value; } /** * Constructs a new KMIP node of type date and time extended with the specified * tag, and an optional name, and the date and time value * * @param tag the tag of the new node * @param name Optional: the name of the node (only used with JSON * or XML encoding). Can be NULL. * @param value the value * * @returns the allocated node, or NULL in case of an error */ struct kmip_node *kmip_node_new_date_time_ext(enum kmip_tag tag, const char *name, int64_t value) { struct kmip_node *node; node = kmip_node_new(tag, name, KMIP_TYPE_DATE_TIME_EXTENDED); if (node == NULL) return NULL; node->date_time_ext_value = value; node->length = sizeof(int64_t); return node; } /** * Returns the value of a KMIP node of type date and time extended * * @param node the KMIP node * * @returns the value of the node, or 0 if the node is not of type date and time * extended */ int64_t kmip_node_get_date_time_ext(const struct kmip_node *node) { if (node == NULL) return 0; if (node->type != KMIP_TYPE_DATE_TIME_EXTENDED) return 0; return node->date_time_ext_value; } /** * Clones (copies) a KMIP node with all its data and elements (in case of a * structure node). * * @param node the KMIP node to clone * * @returns the cloned node, or NULL in case of an error */ struct kmip_node *kmip_node_clone(const struct kmip_node *node) { struct kmip_node *clone, *element, *cloned_element; int rc; clone = kmip_node_new(node->tag, node->name, node->type); if (clone == NULL) return NULL; switch (clone->type) { case KMIP_TYPE_STRUCTURE: element = node->structure_value; while (element != NULL) { cloned_element = kmip_node_clone(element); if (cloned_element == NULL) goto error; rc = kmip_node_add_structure_element(clone, cloned_element); kmip_node_free(cloned_element); if (rc != 0) goto error; element = element->next; } break; case KMIP_TYPE_INTEGER: clone->integer_value = node->integer_value; break; case KMIP_TYPE_LONG_INTEGER: clone->long_value = node->long_value; break; case KMIP_TYPE_BIG_INTEGER: clone->big_integer_value = BN_dup(node->big_integer_value); if (clone->big_integer_value == NULL) goto error; break; case KMIP_TYPE_ENUMERATION: clone->enumeration_value = node->enumeration_value; break; case KMIP_TYPE_BOOLEAN: clone->boolean_value = node->boolean_value; break; case KMIP_TYPE_TEXT_STRING: if (node->text_value != NULL) { clone->text_value = strdup(node->text_value); if (node->text_value == NULL) goto error; clone->length = strlen(clone->text_value); } break; case KMIP_TYPE_BYTE_STRING: if (node->bytes_value != NULL && node->length > 0) { clone->bytes_value = malloc(node->length); if (clone->bytes_value == NULL) goto error; memcpy(clone->bytes_value, node->bytes_value, node->length); clone->length = node->length; } break; case KMIP_TYPE_DATE_TIME: clone->date_time_value = node->date_time_value; break; case KMIP_TYPE_INTERVAL: clone->interval_value = node->interval_value; break; case KMIP_TYPE_DATE_TIME_EXTENDED: clone->date_time_ext_value = node->date_time_ext_value; break; default: goto error; } return clone; error: kmip_node_free(clone); return NULL; } /** * Increments the reference count of a KMIP node * * @param node the node to increase the reference count for */ void kmip_node_upref(struct kmip_node *node) { if (node == NULL) return; __sync_add_and_fetch((unsigned long *)&node->ref_count, 1); } /** * Free a KMIP node, including its value (structure elements, etc) * * @param node the node to free */ void kmip_node_free(struct kmip_node *node) { struct kmip_node *element, *next; unsigned long ref_count = 0; if (node == NULL) return; if (node->ref_count > 0) ref_count = __sync_sub_and_fetch( (unsigned long *)&node->ref_count, 1); if (ref_count > 0) return; switch (node->type) { case KMIP_TYPE_STRUCTURE: element = node->structure_value; while (element != NULL) { next = element->next; /* * Unchain the element from the parent and next element, * even if the element itself might not be freed (due * to reference count). But the parent is freed, and * thus the chain of elements is not longer existent. */ element->parent = NULL; element->next = NULL; kmip_node_free(element); element = next; } break; case KMIP_TYPE_BIG_INTEGER: BN_free(node->big_integer_value); break; case KMIP_TYPE_TEXT_STRING: free(node->text_value); break; case KMIP_TYPE_BYTE_STRING: free(node->bytes_value); break; default: break; } free(node->name); free(node); } static struct kmip_version default_protocol_version = { .major = KMIP_DEFAULT_PROTOCOL_VERSION_MAJOR, .minor = KMIP_DEFAULT_PROTOCOL_VERSION_MINOR }; /** * Sets the default KMIP protocol version * * @param version the version to set */ void kmip_set_default_protocol_version(const struct kmip_version *version) { if (version == NULL) return; default_protocol_version.major = version->major; default_protocol_version.minor = version->minor; } /** * Sets the default KMIP protocol version * * @returns the default KMIP protocol version */ const struct kmip_version *kmip_get_default_protocol_version(void) { return &default_protocol_version; } /** * Constructs a new connection to a KMIP server using the specified connection * configuration. The strings specified in the configuration are copied into the * newly allocated connection, they can be freed by the caller after the new * connection has been allocated. The reference count in the PKEY specified * in the configuration is increased. The caller can free its PKEY as needed. * * @param config the connection configuration * @param connection On return: a newly allocated KMIP connection * @param debug if true, debug messages are printed * * @returns 0 in case of success, or a negative errno value */ int kmip_connection_new(const struct kmip_conn_config *config, struct kmip_connection **connection, bool debug) { struct kmip_connection *conn = NULL; int rc; if (config == NULL || connection == NULL) return -EINVAL; *connection = NULL; switch (config->encoding) { case KMIP_ENCODING_TTLV: /* TTLV can be used with both, plain-TLS and HTTPS */ switch (config->transport) { case KMIP_TRANSPORT_PLAIN_TLS: case KMIP_TRANSPORT_HTTPS: break; default: kmip_debug(debug, "Invalid transport: %d", config->transport); return -EINVAL; } break; case KMIP_ENCODING_JSON: case KMIP_ENCODING_XML: /* JSON(XML can only be used with HTTPS */ switch (config->transport) { case KMIP_TRANSPORT_PLAIN_TLS: kmip_debug(debug, "JSON/XML encode can only be used " "with HTTPS transport"); return -EINVAL; case KMIP_TRANSPORT_HTTPS: break; default: kmip_debug(debug, "Invalid transport: %d", config->transport); return -EINVAL; } break; default: kmip_debug(debug, "Invalid encoding: %d", config->encoding); return -EINVAL; } if (config->server == NULL) { kmip_debug(debug, "KMIP Server must be specified"); return -EINVAL; } if (config->tls_client_key == NULL) { kmip_debug(debug, "Client key must be specified"); return -EINVAL; } if (config->tls_client_cert == NULL) { kmip_debug(debug, "Client certificate must be specified"); return -EINVAL; } conn = calloc(1, sizeof(struct kmip_connection)); if (conn == NULL) { kmip_debug(debug, "calloc failed"); return -ENOMEM; } conn->config.encoding = config->encoding; conn->config.transport = config->transport; kmip_debug(debug, "encoding: %d", conn->config.encoding); kmip_debug(debug, "transport: %d", conn->config.transport); conn->config.server = strdup(config->server); if (conn->config.server == NULL) { kmip_debug(debug, "strdup failed"); rc = -ENOMEM; goto out; } kmip_debug(debug, "server: '%s'", conn->config.server); conn->config.tls_client_key = config->tls_client_key; if (EVP_PKEY_up_ref(conn->config.tls_client_key) != 1) { kmip_debug(debug, "EVP_PKEY_up_ref failed"); rc = -EIO; goto out; } kmip_debug(debug, "client key: %p", conn->config.tls_client_key); conn->config.tls_client_cert = strdup(config->tls_client_cert); if (conn->config.tls_client_cert == NULL) { kmip_debug(debug, "strdup failed"); rc = -ENOMEM; goto out; } kmip_debug(debug, "client cert: '%s'", conn->config.tls_client_cert); if (config->tls_ca != NULL) { conn->config.tls_ca = strdup(config->tls_ca); if (conn->config.tls_ca == NULL) { kmip_debug(debug, "strdup failed"); rc = -ENOMEM; goto out; } kmip_debug(debug, "CA: '%s'", conn->config.tls_ca); } if (config->tls_issuer_cert != NULL) { conn->config.tls_issuer_cert = strdup(config->tls_issuer_cert); if (conn->config.tls_issuer_cert == NULL) { kmip_debug(debug, "strdup failed"); rc = -ENOMEM; goto out; } kmip_debug(debug, "issuer cert: '%s'", conn->config.tls_issuer_cert); } if (config->tls_pinned_pubkey != NULL) { conn->config.tls_pinned_pubkey = strdup(config->tls_pinned_pubkey); if (conn->config.tls_pinned_pubkey == NULL) { kmip_debug(debug, "strdup failed"); rc = -ENOMEM; goto out; } kmip_debug(debug, "pinned pubkey: '%s'", conn->config.tls_pinned_pubkey); } if (config->tls_server_cert != NULL) { conn->config.tls_server_cert = strdup(config->tls_server_cert); if (conn->config.tls_server_cert == NULL) { kmip_debug(debug, "strdup failed"); rc = -ENOMEM; goto out; } kmip_debug(debug, "server cert: '%s'", conn->config.tls_server_cert); } conn->config.tls_verify_peer = config->tls_verify_peer; conn->config.tls_verify_host = config->tls_verify_host; kmip_debug(debug, "verify peer: %d", conn->config.tls_verify_peer); kmip_debug(debug, "verify host: %d", conn->config.tls_verify_host); if (config->tls_cipher_list != NULL) { conn->config.tls_cipher_list = strdup(config->tls_cipher_list); if (conn->config.tls_cipher_list == NULL) { kmip_debug(debug, "strdup failed"); rc = -ENOMEM; goto out; } kmip_debug(debug, "TLS cipher list: '%s'", conn->config.tls_cipher_list); } if (config->tls13_cipher_list != NULL) { conn->config.tls13_cipher_list = strdup(config->tls13_cipher_list); if (conn->config.tls13_cipher_list == NULL) { kmip_debug(debug, "strdup failed"); rc = -ENOMEM; goto out; } kmip_debug(debug, "TLSv1.3 cipher list: '%s'", conn->config.tls13_cipher_list); } switch (conn->config.transport) { case KMIP_TRANSPORT_PLAIN_TLS: rc = kmip_connection_tls_init(conn, debug); if (rc != 0) { kmip_debug(debug, "kmip_connection_tls_init failed"); goto out; } break; case KMIP_TRANSPORT_HTTPS: rc = kmip_connection_https_init(conn, debug); if (rc != 0) { kmip_debug(debug, "kmip_connection_https_init failed"); goto out; } break; default: kmip_debug(debug, "Invalid transport: %d", conn->config.transport); rc = -EINVAL; goto out; } *connection = conn; rc = 0; out: if (rc != 0) kmip_connection_free(conn); return rc; } /** * Perform a request over the KMIP connection * * @param connection the KMIP connection * @param request the request to send * @param response On return: the received response. Must be freed by * the caller. * * @param debug if true, debug messages are printed * * @returns 0 in case of success, or a negative errno value */ int kmip_connection_perform(struct kmip_connection *connection, struct kmip_node *request, struct kmip_node **response, bool debug) { int rc; if (connection == NULL || request == NULL || response == NULL) return -EINVAL; kmip_debug(debug, "KMIP Request:"); kmip_node_dump(request, debug); switch (connection->config.transport) { case KMIP_TRANSPORT_PLAIN_TLS: rc = kmip_connection_tls_perform(connection, request, response, debug); break; case KMIP_TRANSPORT_HTTPS: rc = kmip_connection_https_perform(connection, request, response, debug); break; default: return -EINVAL; } if (rc == 0 && *response != NULL) { kmip_debug(debug, "KMIP Response:"); kmip_node_dump(*response, debug); } return rc; } /** * Terminates and frees a KMIP connection. * * @param connection the KMIP connection to free */ void kmip_connection_free(struct kmip_connection *connection) { if (connection == NULL) return; switch (connection->config.transport) { case KMIP_TRANSPORT_PLAIN_TLS: kmip_connection_tls_term(connection); break; case KMIP_TRANSPORT_HTTPS: kmip_connection_https_term(connection); break; default: break; } free((void *)connection->config.server); EVP_PKEY_free(connection->config.tls_client_key); free((void *)connection->config.tls_client_cert); if (connection->config.tls_ca != NULL) free((void *)connection->config.tls_ca); if (connection->config.tls_issuer_cert != NULL) free((void *)connection->config.tls_issuer_cert); if (connection->config.tls_pinned_pubkey != NULL) free((void *)connection->config.tls_pinned_pubkey); if (connection->config.tls_server_cert != NULL) free((void *)connection->config.tls_server_cert); if (connection->config.tls_cipher_list != NULL) free((void *)connection->config.tls_cipher_list); if (connection->config.tls13_cipher_list != NULL) free((void *)connection->config.tls13_cipher_list); free(connection); } /** * Retrieves the serevr's certificate, public key and certificate chain * * @param server the KMIP server. * For Plain-TLS transport, only the hostname and * optional port number. * For HTTPS transport, an URL in the form * 'https://hostname[:port]/uri' * @param transport the transport mode * @param ca Optional: File name of the CA bundle PEM file, or a * name of a directory the multiple CA certificates. * If this is NULL, then the default system path for * CA certificates is used. * @param client_key the client key as an OpenSSL PKEY object. * @param client_cert File name of the client certificate PEM file * @param server_cert_pem File name of a PEM file into which the server * certificate is written. If NULL then ignored. * @param server_pubkey_pem File name of a PEM file into which the server * public key is written. If NULL then ignored. * @param cert_chain_pem File name of a PEM file into which the certificate * chain (excluding the server certificate) is written. * If NULL then ignored. * @param verified On return: If the server 's certificate has been * verified using the CA specification (if ca = NULL: * default system CAs, otherwise path or file to CAs). * @param debug if true, debug messages are printed * * @returns 0 in case of success, or a negative errno value */ int kmip_connection_get_server_cert(const char *server, enum kmip_transport transport, const char *ca, EVP_PKEY *client_key, const char *client_cert, const char *server_cert_pem, const char *server_pubkey_pem, const char *cert_chain_pem, bool *verified, bool debug) { struct kmip_conn_config config = { 0 }; struct kmip_connection *conn = NULL; int rc, numcerts, i, port_found = 0; char *hostname = NULL, *tok, *tok2; STACK_OF(X509) *chain; bool do_verify = true; FILE *fp = NULL; X509 *cert; if (server == NULL || client_key == NULL || client_cert == NULL) return -EINVAL; config.encoding = KMIP_ENCODING_TTLV; config.transport = KMIP_TRANSPORT_PLAIN_TLS; config.tls_ca = ca; config.tls_client_key = client_key; config.tls_client_cert = client_cert; config.tls_verify_host = false; config.tls_verify_peer = false; config.tls_cipher_list = NULL; config.tls13_cipher_list = NULL; if (transport == KMIP_TRANSPORT_HTTPS) { if (strncmp(server, "https://", 8) != 0) { kmip_debug(debug, "Server must start with 'https://'"); return -EINVAL; } server += 8; /* Find port (if any) and beginning of uri */ if (*server == '[') { /* IPv6 address enclosed in square brackets */ tok = strchr(server, ']'); if (tok == NULL) { kmip_debug(debug, "malformed IPv6 address"); return -EINVAL; } tok++; if (*tok == ':') { port_found = 1; tok2 = strchr(tok, '/'); if (tok2 == NULL) tok2 = tok + strlen(tok); } else { tok2 = strchr(tok, '/'); if (tok2 == NULL) tok2 = tok + strlen(tok); } } else { /* hostname or IPv4 address */ tok = strchr(server, ':'); if (tok != NULL) { port_found = 1; tok2 = strchr(tok, '/'); if (tok2 == NULL) tok2 = tok + strlen(tok); } else { tok2 = strchr(server, '/'); if (tok2 == NULL) tok2 = (char *)server + strlen(server); } } hostname = calloc(1, tok2 - server + (!port_found ? 5 : 1)); if (hostname == NULL) { kmip_debug(debug, "calloc failed"); return -ENOMEM; } strncpy(hostname, server, tok2 - server); if (!port_found) { strcat(hostname, ":"); strcat(hostname, KMIP_DEFAULT_HTTPS_PORT); } config.server = hostname; } else { config.server = server; } retry: config.tls_verify_peer = do_verify; rc = kmip_connection_new(&config, &conn, debug); if (rc != 0) { kmip_debug(debug, "kmip_connection_new failed (do_verify: %d)", do_verify); if (do_verify) { /* * If peer verification failed (e.g. due to a self * signed server certificate), try again without peer * verification. */ do_verify = false; goto retry; } goto out; } if (verified != NULL) *verified = do_verify; chain = SSL_get_peer_cert_chain(conn->plain_tls.ssl); if (chain == NULL) { kmip_debug(debug, "SSL_get_peer_cert_chain failed"); rc = -EIO; goto out; } numcerts = sk_X509_num(chain); for (i = 0; i < numcerts; i++) { cert = sk_X509_value(chain, i); if (cert == NULL) break; if (debug) { kmip_debug(debug, "%d. Certificate:", i); X509_print_ex_fp(stderr, cert, XN_FLAG_COMPAT, X509_FLAG_COMPAT); } if (i == 0 && server_cert_pem != NULL) { fp = fopen(server_cert_pem, "w"); if (fp == NULL) { rc = -errno; kmip_debug(debug, "Failed to open %s for write", server_cert_pem, strerror(-rc)); goto out; } if (PEM_write_X509(fp, cert) != 1) { kmip_debug(debug, "PEM_write_X509 failed to " "write to %s", server_cert_pem); rc = -EIO; goto out; } fclose(fp); fp = NULL; if (server_pubkey_pem != NULL) { fp = fopen(server_pubkey_pem, "w"); if (fp == NULL) { rc = -errno; kmip_debug(debug, "Failed to open %s " "for write", server_pubkey_pem, strerror(-rc)); goto out; } if (PEM_write_PUBKEY(fp, X509_get0_pubkey(cert)) != 1) { kmip_debug(debug, "PEM_write_PUBKEY " "failed to write to %s", server_pubkey_pem); rc = -EIO; goto out; } fclose(fp); fp = NULL; } continue; } if (i > 0 && cert_chain_pem != NULL) { if (fp == NULL) fp = fopen(cert_chain_pem, "w"); if (fp == NULL) { rc = -errno; kmip_debug(debug, "Failed to open %s for write", cert_chain_pem, strerror(-rc)); goto out; } if (PEM_write_X509(fp, cert) != 1) { kmip_debug(debug, "PEM_write_X509 failed to " "write to %s", cert_chain_pem); rc = -EIO; goto out; } } } rc = 0; out: if (fp != NULL) fclose(fp); if (conn != NULL) kmip_connection_free(conn); if (hostname != NULL) free(hostname); return rc; } /** * Library constructor */ void __attribute__ ((constructor)) kmip_init(void) { CURLsslset rc; /* * Ensure that curl uses OpenSSL as SSL backend. If curl has already * been itialized by the calling application, the backend can't be * changed anymore, but we continue anyway. However, it will later be * checked if curl uses the OpenSSL backend, and a HTTPS connection * will fail if it is not using the OpenSSL backend. */ rc = curl_global_sslset(CURLSSLBACKEND_OPENSSL, NULL, NULL); if (rc != CURLSSLSET_OK && rc != CURLSSLSET_TOO_LATE) errx(EXIT_FAILURE, "libkmipclient: libcurl was not built with " "the OpenSSL backend"); curl_global_init(CURL_GLOBAL_ALL); } /** * Library destructor */ void __attribute__ ((destructor)) kmip_exit(void) { curl_global_cleanup(); } s390-tools-2.38.0/libkmipclient/kmip.h000066400000000000000000000062231502674226300174110ustar00rootroot00000000000000/* * libkmipclient - KMIP client library * * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef KMIP_H #define KMIP_H #include #include #include #include #include #include #include #include #include "kmipclient/kmipclient.h" /* KMIP Connection related structures */ #define KMIP_DEFAULT_PROTOCOL_VERSION_MAJOR 1 #define KMIP_DEFAULT_PROTOCOL_VERSION_MINOR 0 struct kmip_connection { struct kmip_conn_config config; union { struct { SSL_CTX *ssl_ctx; SSL *ssl; BIO *bio; } plain_tls; struct { CURL **curl; struct curl_slist *headers; } https; }; }; /* KMIP node related structures */ struct kmip_node { enum kmip_tag tag; enum kmip_type type; unsigned int length; char *name; /* optional, only used for JSON and XML encoding */ union { struct kmip_node *structure_value; int32_t integer_value; int64_t long_value; BIGNUM *big_integer_value; uint32_t enumeration_value; bool boolean_value; char *text_value; unsigned char *bytes_value; int64_t date_time_value; uint32_t interval_value; int64_t date_time_ext_value; }; struct kmip_node *parent; struct kmip_node *next; volatile unsigned long ref_count; }; /* Attribute related internal functions */ int kmip_v2_attr_from_v1_attr(struct kmip_node *v1_attr, struct kmip_node **v2_attr); int kmip_v1_attr_from_v2_attr(struct kmip_node *v2_attr, struct kmip_node **v1_attr); char *kmip_build_v1_custom_attr_name(const char *vendor_id, const char *attr_name); struct kmip_node *kmip_new_attribute_name_v1( const struct kmip_node *v2_attr_ref); int kmip_get_attribute_name_v1(const struct kmip_node *node, struct kmip_node **v2_attr_ref); /* Connection related internal functions */ int kmip_connection_tls_init(struct kmip_connection *connection, bool debug); int kmip_connection_tls_perform(struct kmip_connection *connection, struct kmip_node *request, struct kmip_node **response, bool debug); void kmip_connection_tls_term(struct kmip_connection *connection); int kmip_connection_https_init(struct kmip_connection *connection, bool debug); int kmip_connection_https_perform(struct kmip_connection *connection, struct kmip_node *request, struct kmip_node **response, bool debug); void kmip_connection_https_term(struct kmip_connection *connection); /* KIMP decoding and encoding internal functions */ int kmip_decode_ttlv(BIO *bio, size_t *size, struct kmip_node **node, bool debug); int kmip_encode_ttlv(struct kmip_node *node, BIO *bio, size_t *size, bool debug); int kmip_decode_json(const json_object *obj, struct kmip_node *parent, struct kmip_node **node, bool debug); int kmip_encode_json(const struct kmip_node *node, json_object **obj, bool debug); int kmip_decode_xml(const xmlNode *xml, struct kmip_node *parent, struct kmip_node **node, bool debug); int kmip_encode_xml(const struct kmip_node *node, xmlNode **xml, bool debug); #endif s390-tools-2.38.0/libkmipclient/libkmipclient.map000066400000000000000000000177771502674226300216450ustar00rootroot00000000000000LIBKMIPCLIENT_1.0 { global: kmip_node_clone; kmip_node_upref; kmip_node_free; kmip_node_get_tag; kmip_node_get_type; kmip_node_get_name; kmip_node_dump; kmip_node_new_structure; kmip_node_new_structure_va; kmip_node_get_structure_element_count; kmip_node_get_structure_element_by_index; kmip_node_get_structure_element_by_tag_count; kmip_node_get_structure_element_by_tag; kmip_node_add_structure_element; kmip_node_add_structure_elements; kmip_node_new_integer; kmip_node_get_integer; kmip_node_new_long; kmip_node_get_long; kmip_node_new_bigint; kmip_node_get_bigint; kmip_node_new_enumeration; kmip_node_get_enumeration; kmip_node_new_boolean; kmip_node_get_boolean; kmip_node_new_text_string; kmip_node_get_text_string; kmip_node_new_byte_string; kmip_node_get_byte_string; kmip_node_new_date_time; kmip_node_get_date_time; kmip_node_new_interval; kmip_node_get_interval; kmip_node_new_date_time_ext; kmip_node_get_date_time_ext; kmip_set_default_protocol_version; kmip_get_default_protocol_version; kmip_new_protocol_version; kmip_new_profile_version; kmip_new_request_header; kmip_new_request_batch_item; kmip_new_request; kmip_new_request_va; kmip_new_query_request_payload; kmip_new_query_request_payload_va; kmip_new_discover_versions_payload; kmip_new_discover_versions_payload_va; kmip_new_protection_storage_masks; kmip_new_protection_storage_masks_va; kmip_new_create_request_payload; kmip_new_create_request_payload_va; kmip_new_get_attribute_list_request_payload; kmip_new_get_attributes_request_payload; kmip_new_get_attributes_request_payload_va; kmip_new_add_attribute_request_payload; kmip_new_modify_attribute_request_payload; kmip_new_set_attribute_v2_request_payload; kmip_new_delete_attribute_request_payload; kmip_new_activate_request_payload; kmip_new_destroy_request_payload; kmip_new_archive_request_payload; kmip_new_recover_request_payload; kmip_new_revoke_request_payload; kmip_new_locate_request_payload; kmip_new_locate_request_payload_va; kmip_new_register_request_payload; kmip_new_register_request_payload_va; kmip_new_get_request_payload; kmip_get_protocol_version; kmip_get_profile_version; kmip_get_response_header; kmip_get_response_batch_item; kmip_get_response; kmip_get_query_response_payload; kmip_get_discover_versions_response_payload; kmip_get_create_response_payload; kmip_get_get_attribute_list_response_payload; kmip_get_get_attributes_response_payload; kmip_get_add_attribute_response_payload; kmip_get_modify_attribute_response_payload; kmip_get_set_attribute_v2_response_payload; kmip_get_delete_attribute_response_payload; kmip_get_activate_response_payload; kmip_get_destroy_response_payload; kmip_get_archive_response_payload; kmip_get_recover_response_payload; kmip_get_revoke_response_payload; kmip_get_activate_response_payload; kmip_get_locate_response_payload; kmip_get_register_response_payload; kmip_get_get_response_payload; kmip_new_attributes; kmip_new_attributes_va; kmip_get_attributes; kmip_new_vendor_attribute; kmip_get_vendor_attribute; kmip_new_attribute_reference; kmip_get_attribute_reference; kmip_new_current_new_attribute; kmip_new_unique_identifier; kmip_get_unique_identifier; kmip_new_name; kmip_get_name; kmip_new_alternative_name; kmip_get_alternative_name; kmip_new_object_type; kmip_get_object_type; kmip_new_cryptographic_algorithm; kmip_get_cryptographic_algorithm; kmip_new_cryptographic_length; kmip_get_cryptographic_length; kmip_new_certificate_type; kmip_get_certificate_type; kmip_new_cryptographic_usage_mask; kmip_get_cryptographic_usage_mask; kmip_new_state; kmip_get_state; kmip_new_initial_date; kmip_get_initial_date; kmip_new_activation_date; kmip_get_activation_date; kmip_new_deactivation_date; kmip_get_deactivation_date; kmip_new_destroy_date; kmip_get_destroy_date; kmip_new_compromise_date; kmip_get_compromise_date; kmip_new_compromise_occurrence_date; kmip_get_compromise_occurrence_date; kmip_new_last_change_date; kmip_get_last_change_date; kmip_new_original_creation_date; kmip_get_original_creation_date; kmip_new_archive_date; kmip_get_archive_date; kmip_new_process_start_date; kmip_get_process_start_date; kmip_new_protect_stop_date; kmip_get_protect_stop_date; kmip_new_cryptographic_parameters; kmip_get_cryptographic_parameter; kmip_new_cryptographic_domain_parameters; kmip_get_cryptographic_domain_parameters; kmip_new_digital_signature_algorithm; kmip_get_digital_signature_algorithm; kmip_new_object_group; kmip_get_object_group; kmip_new_revocation_reason; kmip_get_revocation_reason; kmip_new_contact_information; kmip_get_contact_information; kmip_new_description; kmip_get_description; kmip_new_comment; kmip_get_comment; kmip_new_key_format_type; kmip_get_key_format_type; kmip_new_protection_level; kmip_get_protection_level; kmip_new_protection_period; kmip_get_protection_period; kmip_new_protection_storage_mask; kmip_get_protection_storage_mask; kmip_new_fresh; kmip_get_fresh; kmip_new_key_value_present; kmip_get_key_value_present; kmip_new_short_unique_identifier; kmip_get_short_unique_identifier; kmip_new_application_specific_information; kmip_get_application_specific_information; kmip_new_key_value_location; kmip_get_key_value_location; kmip_new_digest; kmip_get_digest; kmip_new_sensitive; kmip_get_sensitive; kmip_new_always_sensitive; kmip_get_always_sensitive; kmip_new_extractable; kmip_get_extractable; kmip_new_never_extractable; kmip_get_never_extractable; kmip_new_link; kmip_get_link; kmip_new_linked_object_identifier; kmip_get_linked_object_identifier; kmip_new_operation_policy_name; kmip_get_operation_policy_name; kmip_new_lease_time; kmip_get_lease_time; kmip_new_key_block; kmip_get_key_block; kmip_new_key_value; kmip_new_key_value_va; kmip_get_key_value; kmip_new_key_wrapping_data; kmip_get_key_wrapping_data; kmip_new_key_wrapping_specification; kmip_new_key_wrapping_specification_va; kmip_get_key_wrapping_specification; kmip_new_key_info; kmip_get_key_info; kmip_new_transparent_symmetric_key; kmip_get_transparent_symmetric_key; kmip_new_transparent_rsa_public_key; kmip_get_transparent_rsa_public_key; kmip_new_pkcs1_public_key; kmip_get_pkcs1_public_key; kmip_new_pkcs8_public_key; kmip_get_pkcs8_public_key; kmip_new_raw_key; kmip_get_raw_key; kmip_new_symmetric_key; kmip_get_symmetric_key; kmip_new_public_key; kmip_get_public_key; kmip_connection_new; kmip_connection_perform; kmip_connection_free; kmip_connection_get_server_cert; local: *; };s390-tools-2.38.0/libkmipclient/names.c000066400000000000000000003145541502674226300175600ustar00rootroot00000000000000/* * libkmipclient - KMIP client library * * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #define _XOPEN_SOURCE #define _DEFAULT_SOURCE #include #include #include "names.h" #include "utils.h" static const struct kmip_enum kmip_tags[] = { { .val = KMIP_TAG_ACTIVATION_DATE, .name = "ActivationDate" }, { .val = KMIP_TAG_APPLICATION_DATA, .name = "ApplicationData" }, { .val = KMIP_TAG_APPLICATION_NAMESPACE, .name = "ApplicationNamespace" }, { .val = KMIP_TAG_APPLICATION_SPECIFIC_INFORMATION, .name = "ApplicationSpecificInformation" }, { .val = KMIP_TAG_ARCHIVE_DATE, .name = "ArchiveDate" }, { .val = KMIP_TAG_ASYNCHRONOUS_CORRELATION_VALUE, .name = "AsynchronousCorrelationValue" }, { .val = KMIP_TAG_ASYNCHRONOUS_INDICATOR, .name = "AsynchronousIndicator" }, { .val = KMIP_TAG_ATTRIBUTE, .name = "Attribute" }, { .val = KMIP_TAG_ATTRIBUTE_INDEX, .name = "AttributeIndex" }, { .val = KMIP_TAG_ATTRIBUTE_NAME, .name = "AttributeName" }, { .val = KMIP_TAG_ATTRIBUTE_VALUE, .name = "AttributeValue" }, { .val = KMIP_TAG_AUTHENTICATION, .name = "Authentication" }, { .val = KMIP_TAG_BATCH_COUNT, .name = "BatchCount" }, { .val = KMIP_TAG_BATCH_ERROR_CONTINUATION_OPTION, .name = "BatchErrorContinuationOption" }, { .val = KMIP_TAG_BATCH_ITEM, .name = "BatchItem" }, { .val = KMIP_TAG_BATCH_ORDER_OPTION, .name = "BatchOrderOption" }, { .val = KMIP_TAG_BLOCK_CIPHER_MODE, .name = "BlockCipherMode" }, { .val = KMIP_TAG_CANCELATION_RESULT, .name = "CancelationResult" }, { .val = KMIP_TAG_CERTIFICATE, .name = "Certificate" }, { .val = KMIP_TAG_CERTIFICATE_IDENTIFIER, .name = "CertificateIndentifyer" }, { .val = KMIP_TAG_CERTIFICATE_ISSUER, .name = "CertificateIssuer" }, { .val = KMIP_TAG_CERTIFICATE_ISSUER_ALTERNATIVE_NAME, .name = "CertificateIssuerAlternativeName" }, { .val = KMIP_TAG_CERTIFICATE_ISSUER_DISTINGUISHED_NAME, .name = "CertificateIssuerDistinguishedName" }, { .val = KMIP_TAG_CERTIFICATE_REQUEST, .name = "CertificateRequest" }, { .val = KMIP_TAG_CERTIFICATE_REQUEST_TYPE, .name = "CertificateRequestType" }, { .val = KMIP_TAG_CERTIFICATE_SUBJECT, .name = "CertificateSubject" }, { .val = KMIP_TAG_CERTIFICATE_SUBJECT_ALTERNATIVE_NAME, .name = "CertificateSubjectAlternativeName" }, { .val = KMIP_TAG_CERTIFICATE_TYPE, .name = "CertificateType" }, { .val = KMIP_TAG_CERTIFICATE_VALUE, .name = "CertificateValue" }, { .val = KMIP_TAG_COMMON_TEMPLATE_ATTRIBUTE, .name = "CommonTemplateAttribute" }, { .val = KMIP_TAG_COMPROMIZE_DATE, .name = "CompromizeDate" }, { .val = KMIP_TAG_COMPROMISE_OCCURRENCE_DATE, .name = "CompromiseOccurrenceDate" }, { .val = KMIP_TAG_CONTACT_INFORMATION, .name = "ContactInformation" }, { .val = KMIP_TAG_CREDENTIAL, .name = "Credential" }, { .val = KMIP_TAG_CREDENTIAL_TYPE, .name = "CredentialType" }, { .val = KMIP_TAG_CREDENTIAL_VALUE, .name = "CredentialValue" }, { .val = KMIP_TAG_CRITICALITY_INDICATOR, .name = "CriticalityIndicator" }, { .val = KMIP_TAG_CRT_Coefficient, .name = "CrtCoefficient" }, { .val = KMIP_TAG_CRYPTOGRAPHIC_ALGORITHM, .name = "CryptographicAlgorithm" }, { .val = KMIP_TAG_CRYPTOGRAPHIC_DOMAIN_PARAMETERS, .name = "CryptographicDomainParameters" }, { .val = KMIP_TAG_CRYPTOGRAPHIC_LENGTH, .name = "CryptographicLength" }, { .val = KMIP_TAG_CRYPTOGRAPHIC_PARAMETERS, .name = "CryptographicParameters" }, { .val = KMIP_TAG_CRYPTOGRAPHIC_USAGE_MASK, .name = "CryptographicUsageMask" }, { .val = KMIP_TAG_CUSTOM_ATTRIBUTE, .name = "CustomAttribute" }, { .val = KMIP_TAG_D, .name = "D" }, { .val = KMIP_TAG_DEACTIVATION_DATE, .name = "DeactivationDate" }, { .val = KMIP_TAG_DERIVATION_DATE, .name = "DerivationDate" }, { .val = KMIP_TAG_DERIVATION_DATA, .name = "DerivationData" }, { .val = KMIP_TAG_DERIVATION_PARAMETERS, .name = "DerivationParameters" }, { .val = KMIP_TAG_DESTROY_DATE, .name = "DestroyDate" }, { .val = KMIP_TAG_DIGEST, .name = "Digest" }, { .val = KMIP_TAG_DIGEST_VALUE, .name = "DigestValue" }, { .val = KMIP_TAG_ENCRYPTION_KEY_INFORMATION, .name = "EncryptionKeyInformation" }, { .val = KMIP_TAG_G, .name = "G" }, { .val = KMIP_TAG_HASHING_ALGORITHM, .name = "HashingAlgorithm" }, { .val = KMIP_TAG_INITIAL_DATE, .name = "InitialDate" }, { .val = KMIP_TAG_INITIALIZATION_VECTOR, .name = "InitializationVector" }, { .val = KMIP_TAG_ISSUER, .name = "Issuer" }, { .val = KMIP_TAG_ITERATION_COUNT, .name = "IterationCount" }, { .val = KMIP_TAG_IV_COUNTER_NONCE, .name = "IvCounterNonce" }, { .val = KMIP_TAG_J, .name = "J" }, { .val = KMIP_TAG_KEY, .name = "Key" }, { .val = KMIP_TAG_KEY_BLOCK, .name = "KeyBlock" }, { .val = KMIP_TAG_KEY_COMPRESSION_TYPE, .name = "KeyCompressionType" }, { .val = KMIP_TAG_KEY_FORMAT_TYPE, .name = "KeyFormatType" }, { .val = KMIP_TAG_KEY_MATERIAL, .name = "KeyMaterial" }, { .val = KMIP_TAG_KEY_PART_IDENTIFIER, .name = "KeyPartIdentifier" }, { .val = KMIP_TAG_KEY_VALUE, .name = "KeyValue" }, { .val = KMIP_TAG_KEY_WRAPPING_DATA, .name = "KeyWrappingData" }, { .val = KMIP_TAG_KEY_WRAPPING_SPECIFICATION, .name = "KeyWrappingSpecification" }, { .val = KMIP_TAG_LAST_CHANGE_DATE, .name = "LastChangeDate" }, { .val = KMIP_TAG_LEASE_TIME, .name = "LeaseTime" }, { .val = KMIP_TAG_LINK, .name = "Link" }, { .val = KMIP_TAG_LINK_TYPE, .name = "LinkType" }, { .val = KMIP_TAG_LINKED_OBJECT_IDENTIFIER, .name = "LinkedObjectIdentifier" }, { .val = KMIP_TAG_MAC_SIGNATURE, .name = "MACSignature" }, { .val = KMIP_TAG_MAC_SIGNATURE_KEY_INFORMATION, .name = "MACSignatureKeyInformation" }, { .val = KMIP_TAG_MAXIMUM_ITEMS, .name = "MaximumItems" }, { .val = KMIP_TAG_MAXIMUM_RESPONSE_SIZE, .name = "MaximumResponseSize" }, { .val = KMIP_TAG_MESSAGE_EXTENSION, .name = "MessageExtension" }, { .val = KMIP_TAG_MODULUS, .name = "Modulus" }, { .val = KMIP_TAG_NAME, .name = "Name" }, { .val = KMIP_TAG_NAME_TYPE, .name = "NameType" }, { .val = KMIP_TAG_NAME_VALUE, .name = "NameValue" }, { .val = KMIP_TAG_OBJECT_GROUP, .name = "ObjectGroup" }, { .val = KMIP_TAG_OBJECT_TYPE, .name = "ObjectType" }, { .val = KMIP_TAG_OFFSET, .name = "Offset" }, { .val = KMIP_TAG_OPAQUE_DATA_TYPE, .name = "OpaqueDataType" }, { .val = KMIP_TAG_OPAQUE_DATA_VALUE, .name = "OpaqueDataValue" }, { .val = KMIP_TAG_OPAQUE_OBJECT, .name = "OpaqueObject" }, { .val = KMIP_TAG_OPERATION, .name = "Operation" }, { .val = KMIP_TAG_OPERATION_POLICY_NAME, .name = "OperationPolicyName" }, { .val = KMIP_TAG_P, .name = "P" }, { .val = KMIP_TAG_PADDING_METHOD, .name = "PaddingMethod" }, { .val = KMIP_TAG_PRIME_EXPONENT_P, .name = "PrimeExponentP" }, { .val = KMIP_TAG_PRIME_EXPONENT_Q, .name = "PrimeExponentQ" }, { .val = KMIP_TAG_PRIME_FIELD_SIZE, .name = "PrimeFieldSize" }, { .val = KMIP_TAG_PRIVATE_EXPONENT, .name = "PrivateExponent" }, { .val = KMIP_TAG_PRIVATE_KEY, .name = "PrivateKey" }, { .val = KMIP_TAG_PRIVATE_KEY_TEMPLATE_ATTRIBUTE, .name = "PrivateKeyTemplateAttribute" }, { .val = KMIP_TAG_PRIVATE_KEY_UNIQUE_IDENTIFIER, .name = "PrivateKeyUniqueIdentifier" }, { .val = KMIP_TAG_PROCESS_START_DATE, .name = "ProcessStartDate" }, { .val = KMIP_TAG_PROTECT_STOP_DATE, .name = "ProtectStopDate" }, { .val = KMIP_TAG_PROTOCOL_VERSION, .name = "ProtocolVersion" }, { .val = KMIP_TAG_PROTOCOL_VERSION_MAJOR, .name = "ProtocolVersionMajor" }, { .val = KMIP_TAG_PROTOCOL_VERSION_MINOR, .name = "ProtocolVersionMinor" }, { .val = KMIP_TAG_PUBLIC_EXPONENT, .name = "PublicExponent" }, { .val = KMIP_TAG_PUBLIC_KEY, .name = "PublicKey" }, { .val = KMIP_TAG_PUBLIC_KEY_TEMPLATE_ATTRIBUTE, .name = "PublicKeyTemplateAttribute" }, { .val = KMIP_TAG_PUBLIC_KEY_UNIQUE_IDENTIFIER, .name = "PublicKeyUniqueIdentifier" }, { .val = KMIP_TAG_PUT_FUNCTION, .name = "PutFunction" }, { .val = KMIP_TAG_Q, .name = "Q" }, { .val = KMIP_TAG_Q_STRING, .name = "QString" }, { .val = KMIP_TAG_Q_LENGTH, .name = "QLength" }, { .val = KMIP_TAG_QUERY_FUNCTION, .name = "QueryFunction" }, { .val = KMIP_TAG_RECOMMENDED_CURVE, .name = "RecommendedCurve" }, { .val = KMIP_TAG_REPLACED_UNIQUE_IDENTIFIER, .name = "ReplacedUniqueIdentifier" }, { .val = KMIP_TAG_REQUEST_HEADER, .name = "RequestHeader" }, { .val = KMIP_TAG_REQUEST_MESSAGE, .name = "RequestMessage" }, { .val = KMIP_TAG_REQUEST_PAYLOAD, .name = "RequestPayload" }, { .val = KMIP_TAG_RESPONSE_HEADER, .name = "ResponseHeader" }, { .val = KMIP_TAG_RESPONSE_MESSAGE, .name = "ResponseMessage" }, { .val = KMIP_TAG_RESPONSE_PAYLOAD, .name = "ResponsePayload" }, { .val = KMIP_TAG_RESULT_MESSAGE, .name = "ResultMessage" }, { .val = KMIP_TAG_RESULT_REASON, .name = "ResultReason" }, { .val = KMIP_TAG_RESULT_STATUS, .name = "ResultStatus" }, { .val = KMIP_TAG_REVOCATION_MESSAGE, .name = "RevocationMessage" }, { .val = KMIP_TAG_REVOCATION_REASON, .name = "RevocationReason" }, { .val = KMIP_TAG_REVOCATION_REASON_CODE, .name = "RevocationReasonCode" }, { .val = KMIP_TAG_KEY_ROLE_TYPE, .name = "KeyRoleType" }, { .val = KMIP_TAG_SALT, .name = "Salt" }, { .val = KMIP_TAG_SECRET_DATA, .name = "SecretData" }, { .val = KMIP_TAG_SECRET_DATA_TYPE, .name = "SecretDataType" }, { .val = KMIP_TAG_SERIAL_NUMBER, .name = "SerialNumber" }, { .val = KMIP_TAG_SERVER_INFORMATION, .name = "ServerInformation" }, { .val = KMIP_TAG_SPLIT_KEY, .name = "SplitKey" }, { .val = KMIP_TAG_SPLIT_KEY_METHOD, .name = "SplitKeyMethod" }, { .val = KMIP_TAG_SPLIT_KEY_PARTS, .name = "SplitKeyParts" }, { .val = KMIP_TAG_SPLIT_KEY_THRESHOLD, .name = "SplitKeyThreshold" }, { .val = KMIP_TAG_STATE, .name = "State" }, { .val = KMIP_TAG_STORAGE_STATUS_MASK, .name = "StorageStatusMask" }, { .val = KMIP_TAG_SYMMETRIC_KEY, .name = "SymmetricKey" }, { .val = KMIP_TAG_TEMPLATE, .name = "Template" }, { .val = KMIP_TAG_TEMPLATE_ATTRIBUTE, .name = "TemplateAttribute" }, { .val = KMIP_TAG_TIME_STAMP, .name = "TimeStamp" }, { .val = KMIP_TAG_UNIQUE_BATCH_ITEM_ID, .name = "UniqueBatchItemId" }, { .val = KMIP_TAG_UNIQUE_IDENTIFIER, .name = "UniqueIdentifier" }, { .val = KMIP_TAG_USAGE_LIMITS, .name = "UsageLimits" }, { .val = KMIP_TAG_USAGE_LIMITS_COUNT, .name = "UsageLimitsCount" }, { .val = KMIP_TAG_USAGE_LIMITS_TOTAL, .name = "UsageLimitsTotal" }, { .val = KMIP_TAG_USAGE_LIMITS_UNIT, .name = "UsageLimitsUnit" }, { .val = KMIP_TAG_USERNAME, .name = "Username" }, { .val = KMIP_TAG_VALIDITY_DATE, .name = "ValidityDate" }, { .val = KMIP_TAG_VALIDITY_INDICATOR, .name = "ValidityIndicator" }, { .val = KMIP_TAG_VENDOR_EXTENSION, .name = "VendorExtension" }, { .val = KMIP_TAG_VENDOR_IDENTIFICATION, .name = "VendorIdentification" }, { .val = KMIP_TAG_WRAPPING_METHOD, .name = "WrappingMethod" }, { .val = KMIP_TAG_X, .name = "X" }, { .val = KMIP_TAG_Y, .name = "Y" }, { .val = KMIP_TAG_PASSWORD, .name = "Password" }, { .val = KMIP_TAG_DEVICE_IDENTIFIER, .name = "DeviceIdentifier" }, { .val = KMIP_TAG_ENCODING_OPTION, .name = "EncodingOption" }, { .val = KMIP_TAG_EXTENSION_INFORMATION, .name = "ExtensionInformation" }, { .val = KMIP_TAG_EXTENSION_NAME, .name = "ExtensionName" }, { .val = KMIP_TAG_EXTENSION_TAG, .name = "ExtensionTag" }, { .val = KMIP_TAG_EXTENSION_TYPE, .name = "ExtensionType" }, { .val = KMIP_TAG_FRESH, .name = "Fresh" }, { .val = KMIP_TAG_MACHINE_IDENTIFIER, .name = "MachineIdentifier" }, { .val = KMIP_TAG_MEDIA_IDENTIFIER, .name = "MediaIdentifier" }, { .val = KMIP_TAG_NETWORK_IDENTIFIER, .name = "NetworkIdentifier" }, { .val = KMIP_TAG_OBJECT_GROUP_MEMBER, .name = "ObjectGroupMember" }, { .val = KMIP_TAG_CERTIFICATE_LENGTH, .name = "CertificateLength" }, { .val = KMIP_TAG_DIGITAL_SIGNATURE_ALGORITHM, .name = "DigitalSignatureAlgorithm" }, { .val = KMIP_TAG_CERTIFICATE_SERIAL_NUMBER, .name = "CertificateSerialNumber" }, { .val = KMIP_TAG_DEVICE_SERIAL_NUMBER, .name = "DeviceSerialNumber" }, { .val = KMIP_TAG_ISSUER_ALTERNATE_NAME, .name = "IssuerAlternateName" }, { .val = KMIP_TAG_ISSUER_DISTINGUISHED_NAME, .name = "IssuerDistinguishedName" }, { .val = KMIP_TAG_SUBJECT_ALTERNATE_NAME, .name = "SubjectAlternateName" }, { .val = KMIP_TAG_SUBJECT_DISTINGUISHED_NAME, .name = "SubjectDistinguishedName" }, { .val = KMIP_TAG_X_509_CERTIFICATE_IDENTIFIER, .name = "X_509CertificateIdentifier" }, { .val = KMIP_TAG_X_509_CERTIFICATE_ISSUER, .name = "X_509CertificateIssuer" }, { .val = KMIP_TAG_X_509_CERTIFICATE_SUBJECT, .name = "X_509CertificateSubject" }, { .val = KMIP_TAG_KEY_VALUE_LOCATION, .name = "KeyValueLocation" }, { .val = KMIP_TAG_KEY_VALUE_LOCATION_VALUE, .name = "KeyValueLocationValue" }, { .val = KMIP_TAG_KEY_VALUE_LOCATION_TYPE, .name = "KeyValueLocationType" }, { .val = KMIP_TAG_KEY_VALUE_PRESENT, .name = "KeyValuePresent" }, { .val = KMIP_TAG_ORIGINAL_CREATION_DATE, .name = "OriginalCreationDate" }, { .val = KMIP_TAG_PGP_KEY, .name = "PGPKey" }, { .val = KMIP_TAG_PGP_KEY_VERSION, .name = "PGPKeyVersion" }, { .val = KMIP_TAG_ALTERNATE_NAME, .name = "AlternateName" }, { .val = KMIP_TAG_ALTERNATE_NAME_VALUE, .name = "AlternateNameValue" }, { .val = KMIP_TAG_ALTERNATE_NAME_TYPE, .name = "AlternateNameType" }, { .val = KMIP_TAG_DATA, .name = "Data" }, { .val = KMIP_TAG_SIGNATURE_DATA, .name = "SignatureData" }, { .val = KMIP_TAG_DATA_LENGTH, .name = "DataLength" }, { .val = KMIP_TAG_RANDOM_IV, .name = "RandomIV" }, { .val = KMIP_TAG_MAC_DATA, .name = "MACData" }, { .val = KMIP_TAG_ATTESTATION_TYPE, .name = "AttestationType" }, { .val = KMIP_TAG_NONCE, .name = "Nonce" }, { .val = KMIP_TAG_NONCE_ID, .name = "NonceId" }, { .val = KMIP_TAG_NONCE_VALUE, .name = "NonceValue" }, { .val = KMIP_TAG_ATTESTATION_MEASUREMENT, .name = "AttestationMeasurement" }, { .val = KMIP_TAG_ATTESTATION_ASSERTION, .name = "AttestationAssertion" }, { .val = KMIP_TAG_IV_LENGTH, .name = "IVLength" }, { .val = KMIP_TAG_TAG_LENGTH, .name = "TagLength" }, { .val = KMIP_TAG_FIXED_FIELD_LENGTH, .name = "FixedFieldLength" }, { .val = KMIP_TAG_COUNTER_LENGTH, .name = "CounterLength" }, { .val = KMIP_TAG_INITIAL_COUNTER_VALUE, .name = "InitialCounterValue" }, { .val = KMIP_TAG_INVOCATION_FIELD_LENGTH, .name = "InvocationFieldLength" }, { .val = KMIP_TAG_ATTESTATION_CAPABLE_INDICATOR, .name = "AttestationCapableIndicator" }, { .val = KMIP_TAG_OFFSET_ITEMS, .name = "OffsetItems" }, { .val = KMIP_TAG_LOCATED_ITEMS, .name = "LocatedItems" }, { .val = KMIP_TAG_CORRELATION_VALUE, .name = "CorrelationValue" }, { .val = KMIP_TAG_INIT_INDICATOR, .name = "InitIndicator" }, { .val = KMIP_TAG_FINAL_INDICATOR, .name = "FinalIndicator" }, { .val = KMIP_TAG_RNG_PARAMETERS, .name = "RNGParameters" }, { .val = KMIP_TAG_RNG_ALGORITHM, .name = "RNGAlgorithm" }, { .val = KMIP_TAG_DRBG_ALGORITHM, .name = "DRBGAlgorithm" }, { .val = KMIP_TAG_FIPS186_VARIANT, .name = "Fips186Variant" }, { .val = KMIP_TAG_PREDICTION_RESISTANCE, .name = "PredictionResistance" }, { .val = KMIP_TAG_RANDOM_NUMBER_GENERATOR, .name = "RandomNumberGenerator" }, { .val = KMIP_TAG_VALIDATION_INFORMATION, .name = "ValidationInformation" }, { .val = KMIP_TAG_VALIDATION_AUTHORITY_TYPE, .name = "ValidationAuthorityType" }, { .val = KMIP_TAG_VALIDATION_AUTHORITY_COUNTRY, .name = "ValidationAuthorityCountry" }, { .val = KMIP_TAG_VALIDATION_AUTHORITY_URI, .name = "ValidationAuthorityURI" }, { .val = KMIP_TAG_VALIDATION_VERSION_MAJOR, .name = "ValidationVersionMajor" }, { .val = KMIP_TAG_VALIDATION_VERSION_MINOR, .name = "ValidationVersionMinor" }, { .val = KMIP_TAG_VALIDATION_TYPE, .name = "ValidationType" }, { .val = KMIP_TAG_VALIDATION_LEVEL, .name = "ValidationLevel" }, { .val = KMIP_TAG_VALIDATION_CERTIFICATE_IDENTIFIER, .name = "ValidationCertificateIdentifier" }, { .val = KMIP_TAG_VALIDATION_CERTIFICATE_URI, .name = "ValidationCertificateURI" }, { .val = KMIP_TAG_VALIDATION_VENDOR_URI, .name = "ValidationVendorURI" }, { .val = KMIP_TAG_VALIDATION_PROFILE, .name = "ValidationProfile" }, { .val = KMIP_TAG_PROFILE_INFORMATION, .name = "ProfileInformation" }, { .val = KMIP_TAG_PROFILE_NAME, .name = "ProfileName" }, { .val = KMIP_TAG_SERVER_URI, .name = "ServerURI" }, { .val = KMIP_TAG_SERVER_PORT, .name = "ServerPort" }, { .val = KMIP_TAG_STREAMING_CAPABILITY, .name = "StreamingCapability" }, { .val = KMIP_TAG_ASYNCHRONOUS_CAPABILITY, .name = "AsynchronousCapability" }, { .val = KMIP_TAG_ATTESTATION_CAPABILITY, .name = "AttestationCapability" }, { .val = KMIP_TAG_UNWRAP_MODE, .name = "UnwrapMode" }, { .val = KMIP_TAG_DESTROY_ACTION, .name = "DestroyAction" }, { .val = KMIP_TAG_SHREDDING_ALGORITHM, .name = "ShreddingAlgorithm" }, { .val = KMIP_TAG_RNG_MODE, .name = "RNGMode" }, { .val = KMIP_TAG_CLIENT_REGISTRATION_METHOD, .name = "ClientRegistrationMethod" }, { .val = KMIP_TAG_CAPABILITY_INFORMATION, .name = "CapabilityInformation" }, { .val = KMIP_TAG_KEY_WRAP_TYPE, .name = "KeyWrapType" }, { .val = KMIP_TAG_BATCH_UNDO_CAPABILITY, .name = "BatchUndoCapability" }, { .val = KMIP_TAG_BATCH_CONTINUE_CAPABILITY, .name = "BatchContinueCapability" }, { .val = KMIP_TAG_PKCS_12_FRIENDLY_NAME, .name = "PKCS_12FriendlyName" }, { .val = KMIP_TAG_DESCRIPTION, .name = "Description" }, { .val = KMIP_TAG_COMMENT, .name = "Comment" }, { .val = KMIP_TAG_AUTHENTICATED_ENCRYPTION_ADDITIONAL_DATA, .name = "AuthenticatedEncryptionAdditionalDat" }, { .val = KMIP_TAG_AUTHENTICTAED_ENCRYPTION_TAG, .name = "AuthentictaedEncryptionTag" }, { .val = KMIP_TAG_SALT_LENGTH, .name = "SaltLength" }, { .val = KMIP_TAG_MASK_GENERATOR, .name = "MaskGenerator" }, { .val = KMIP_TAG_MASK_GENERATOR_HASHING_ALGORITHM, .name = "MaskGeneratorHashingAlgorithm" }, { .val = KMIP_TAG_P_SOURCE, .name = "PSource" }, { .val = KMIP_TAG_TRAILER_FIELD, .name = "TrailerField" }, { .val = KMIP_TAG_CLIENT_CORRELATION_VALUE, .name = "ClientCorrelationValue" }, { .val = KMIP_TAG_SERVER_CORRELATION_VALUE, .name = "ServerCorrelationValue" }, { .val = KMIP_TAG_DIGESTED_DATA, .name = "DigestedData" }, { .val = KMIP_TAG_CERTIFICATE_SUBJECT_CN, .name = "CertificateSubjectCN" }, { .val = KMIP_TAG_CERTIFICATE_SUBJECT_O, .name = "CertificateSubjectO" }, { .val = KMIP_TAG_CERTIFICATE_SUBJECT_OU, .name = "CertificateSubjectOU" }, { .val = KMIP_TAG_CERTIFICATE_SUBJECT_EMAIL, .name = "CertificateSubjectEmail" }, { .val = KMIP_TAG_CERTIFICATE_SUBJECT_C, .name = "CertificateSubjectC" }, { .val = KMIP_TAG_CERTIFICATE_SUBJECT_ST, .name = "CertificateSubjectST" }, { .val = KMIP_TAG_CERTIFICATE_SUBJECT_L, .name = "CertificateSubjectL" }, { .val = KMIP_TAG_CERTIFICATE_SUBJECT_UID, .name = "CertificateSubjectUID" }, { .val = KMIP_TAG_CERTIFICATE_SUBJECT_SERIAL_NUMBER, .name = "CertificateSubjectSerialNumber" }, { .val = KMIP_TAG_CERTIFICATE_SUBJECT_TITLE, .name = "CertificateSubjectTitle" }, { .val = KMIP_TAG_CERTIFICATE_SUBJECT_DC, .name = "CertificateSubjectDC" }, { .val = KMIP_TAG_CERTIFICATE_SUBJECT_DN_QUALIFIER, .name = "CertificateSubjectDNQualifier" }, { .val = KMIP_TAG_CERTIFICATE_ISSUER_CN, .name = "CertificateIssuerCN" }, { .val = KMIP_TAG_CERTIFICATE_ISSUER_O, .name = "CertificateIssuerO" }, { .val = KMIP_TAG_CERTIFICATE_ISSUER_OU, .name = "CertificateIssuerOU" }, { .val = KMIP_TAG_CERTIFICATE_ISSUER_EMAIL, .name = "CertificateIssuerEmail" }, { .val = KMIP_TAG_CERTIFICATE_ISSUER_C, .name = "CertificateIssuerC" }, { .val = KMIP_TAG_CERTIFICATE_ISSUER_ST, .name = "CertificateIssuerST" }, { .val = KMIP_TAG_CERTIFICATE_ISSUER_L, .name = "CertificateIssuerL" }, { .val = KMIP_TAG_CERTIFICATE_ISSUER_UID, .name = "CertificateIssuerUID" }, { .val = KMIP_TAG_CERTIFICATE_ISSUER_SERIAL_NUMBER, .name = "CertificateIssuerSerialNumber" }, { .val = KMIP_TAG_CERTIFICATE_ISSUER_TITLE, .name = "CertificateIssuerTitle" }, { .val = KMIP_TAG_CERTIFICATE_ISSUER_DC, .name = "CertificateIssuerDC" }, { .val = KMIP_TAG_CERTIFICATE_ISSUER_DN_QUALIFIER, .name = "CertificateIssuerDNQualifier" }, { .val = KMIP_TAG_SENSITIVE, .name = "Sensitive" }, { .val = KMIP_TAG_ALWAYS_SENSITIVE, .name = "AlwaysSensitive" }, { .val = KMIP_TAG_EXTRACTABLE, .name = "Extractable" }, { .val = KMIP_TAG_NEVER_EXTRACTABLE, .name = "NeverExtractable" }, { .val = KMIP_TAG_REPLACE_EXISTING, .name = "ReplaceExisting" }, { .val = KMIP_TAG_ATTRIBUTES, .name = "Attributes" }, { .val = KMIP_TAG_COMMON_ATTRIBUTES, .name = "CommonAttributes" }, { .val = KMIP_TAG_PRIVATE_KEY_ATTRIBUTES, .name = "PrivateKeyAttributes" }, { .val = KMIP_TAG_PUBLIC_KEY_ATTRIBUTES, .name = "PublicKeyAttributes" }, { .val = KMIP_TAG_EXTENSION_ENUMERATION, .name = "ExtensionEnumeration" }, { .val = KMIP_TAG_EXTENSION_ATTRIBUTE, .name = "ExtensionAttribute" }, { .val = KMIP_TAG_EXTENSION_PARENT_STRUCTURE_TAG, .name = "ExtensionParentStructureTag" }, { .val = KMIP_TAG_EXTENSION_DESCRIPTION, .name = "ExtensionDescription" }, { .val = KMIP_TAG_SERVER_NAME, .name = "ServerName" }, { .val = KMIP_TAG_SERVER_SERIAL_NUMBER, .name = "ServerSerialNumber" }, { .val = KMIP_TAG_SERVER_VERSION, .name = "ServerVersion" }, { .val = KMIP_TAG_SERVER_LOAD, .name = "ServerLoad" }, { .val = KMIP_TAG_PRODUCT_NAME, .name = "ProductName" }, { .val = KMIP_TAG_BUILD_LEVEL, .name = "BuildLevel" }, { .val = KMIP_TAG_BUILD_DATE, .name = "BuildDate" }, { .val = KMIP_TAG_CLUSTER_INFO, .name = "ClusterInfo" }, { .val = KMIP_TAG_ALTERNATE_FAILOVER_ENDPOINTS, .name = "AlternateFailoverEndpoints" }, { .val = KMIP_TAG_SHORT_UNIQUE_IDENTIFIER, .name = "ShortUniqueIdentifier" }, { .val = KMIP_TAG_TAG, .name = "Tag" }, { .val = KMIP_TAG_CERTIFICATE_REQUEST_UNIQUE_IDENTIFIER, .name = "CertificateRequestUniqueIdentifier" }, { .val = KMIP_TAG_NIST_KEY_TYPE, .name = "NISTKeyType" }, { .val = KMIP_TAG_ATTRIBUTE_REFERENCE, .name = "AttributeReference" }, { .val = KMIP_TAG_CURRENT_ATTRIBUTE, .name = "CurrentAttribute" }, { .val = KMIP_TAG_NEW_ATTRIBUTE, .name = "NewAttribute" }, { .val = KMIP_TAG_CERTIFICATE_REQUEST_VALUE, .name = "CertificateRequestValue" }, { .val = KMIP_TAG_LOG_MESSAGE, .name = "LogMessage" }, { .val = KMIP_TAG_PROFILE_VERSION, .name = "ProfileVersion" }, { .val = KMIP_TAG_PROFILE_VERSION_MAJOR, .name = "ProfileVersionMajor" }, { .val = KMIP_TAG_PROFILE_VERSION_MINOR, .name = "ProfileVersionMinor" }, { .val = KMIP_TAG_PROTECTION_LEVEL, .name = "ProtectionLevel" }, { .val = KMIP_TAG_PROTECTION_PERIOD, .name = "ProtectionPeriod" }, { .val = KMIP_TAG_QUANTUM_SAFE, .name = "QuantumSafe" }, { .val = KMIP_TAG_QUANTUM_SAFE_CAPABILITY, .name = "QuantumSafeCapability" }, { .val = KMIP_TAG_TICKET, .name = "Ticket" }, { .val = KMIP_TAG_TICKET_TYPE, .name = "TicketType" }, { .val = KMIP_TAG_TICKET_VALUE, .name = "TicketValue" }, { .val = KMIP_TAG_REQUEST_COUNT, .name = "RequestCount" }, { .val = KMIP_TAG_RIGHTS, .name = "Rights" }, { .val = KMIP_TAG_OBJECTS, .name = "Objects" }, { .val = KMIP_TAG_OPERATIONS, .name = "Operations" }, { .val = KMIP_TAG_RIGHT, .name = "Right" }, { .val = KMIP_TAG_ENDPOINT_ROLE, .name = "EndpointRole" }, { .val = KMIP_TAG_DEFAULTS_INFORMATION, .name = "DefaultsInformation" }, { .val = KMIP_TAG_OBJECT_DEFAULTS, .name = "ObjectDefaults" }, { .val = KMIP_TAG_EPHEMERAL, .name = "Ephemeral" }, { .val = KMIP_TAG_SERVER_HASHED_PASSWORD, .name = "ServerHashedPassword" }, { .val = KMIP_TAG_ONE_TIME_PASSWORD, .name = "OneTimePassword" }, { .val = KMIP_TAG_HASHED_PASSWORD, .name = "HashedPassword" }, { .val = KMIP_TAG_ADJUSTMENT_TYPE, .name = "AdjustmentType" }, { .val = KMIP_TAG_PKCS_11_INTERFACE, .name = "PKCS_11Interface" }, { .val = KMIP_TAG_PKCS_11_FUNCTION, .name = "PKCS_11Function" }, { .val = KMIP_TAG_PKCS_11_INPUT_PARAMETERS, .name = "PKCS_11InputParameters" }, { .val = KMIP_TAG_PKCS_11_OUTPUT_PARAMETERS, .name = "PKCS_11OutputParameters" }, { .val = KMIP_TAG_PKCS_11_RETURN_CODE, .name = "PKCS_11ReturnCode" }, { .val = KMIP_TAG_PROTECTION_STORAGE_MASK, .name = "ProtectionStorageMask" }, { .val = KMIP_TAG_PROTECTION_STORAGE_MASKS, .name = "ProtectionStorageMasks" }, { .val = KMIP_TAG_INTEROP_FUNCTION, .name = "InteropFunction" }, { .val = KMIP_TAG_INTEROP_IDENTIFIER, .name = "InteropIdentifier" }, { .val = KMIP_TAG_ADJUSTMENT_VALUE, .name = "AdjustmentValue" }, { .val = KMIP_TAG_COMMON_PROTECTION_STORAGE_MASKS, .name = "CommonProtectionStorageMasks" }, { .val = KMIP_TAG_PRIVATE_PROTECTION_STORAGE_MASKS, .name = "PrivateProtectionStorageMasks" }, { .val = KMIP_TAG_PUBLIC_PROTECTION_STORAGE_MASKS, .name = "PublicProtectionStorageMasks" }, { .val = KMIP_TAG_OBJECT_GROUPS, .name = "ObjectGroups" }, { .val = KMIP_TAG_OBJECT_TYPES, .name = "ObjectTypes" }, { .val = KMIP_TAG_CONSTRAINTS, .name = "Constraints" }, { .val = KMIP_TAG_CONSTRAINT, .name = "Constraint" }, { .val = KMIP_TAG_ROTATE_INTERVAL, .name = "RotateInterval" }, { .val = KMIP_TAG_ROTATE_AUTOMATIC, .name = "RotateAutomatic" }, { .val = KMIP_TAG_ROTATE_OFFSET, .name = "RotateOffset" }, { .val = KMIP_TAG_ROTATE_DATE, .name = "RotateDate" }, { .val = KMIP_TAG_ROTATE_GENERATION, .name = "RotateGeneration" }, { .val = KMIP_TAG_ROTATE_NAME, .name = "RotateName" }, { .val = KMIP_TAG_ROTATE_NAME_VALUE, .name = "RotateNameValue" }, { .val = KMIP_TAG_ROTATE_NAME_TYPE, .name = "RotateNameType" }, { .val = KMIP_TAG_ROTATE_LATEST, .name = "RotateLatest" }, { .val = KMIP_TAG_ASYNCHRONOUS_REQUEST, .name = "AsynchronousRequest" }, { .val = KMIP_TAG_SUBMISSION_DATE, .name = "SubmissionDate" }, { .val = KMIP_TAG_PROCESSING_STAGE, .name = "ProcessingStage" }, { .val = KMIP_TAG_ASYNCHRONOUS_CORRELATION_VALUES, .name = "AsynchronousCorrelationValues" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_types[] = { { .val = KMIP_TYPE_STRUCTURE, .name = "Structure" }, { .val = KMIP_TYPE_INTEGER, .name = "Integer" }, { .val = KMIP_TYPE_LONG_INTEGER, .name = "LongInteger" }, { .val = KMIP_TYPE_BIG_INTEGER, .name = "BigInteger" }, { .val = KMIP_TYPE_ENUMERATION, .name = "Enumeration" }, { .val = KMIP_TYPE_BOOLEAN, .name = "Boolean" }, { .val = KMIP_TYPE_TEXT_STRING, .name = "TextString" }, { .val = KMIP_TYPE_BYTE_STRING, .name = "ByteString" }, { .val = KMIP_TYPE_DATE_TIME, .name = "DateTime" }, { .val = KMIP_TYPE_INTERVAL, .name = "Interval" }, { .val = KMIP_TYPE_DATE_TIME_EXTENDED, .name = "DateTimeExtended" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_operations[] = { { .val = KMIP_OPERATION_CREATE, .name = "Create" }, { .val = KMIP_OPERATION_CREATE_KEY_PAIR, .name = "CreateKeyPair" }, { .val = KMIP_OPERATION_REGISTER, .name = "Register" }, { .val = KMIP_OPERATION_RE_KEY, .name = "ReKey" }, { .val = KMIP_OPERATION_DERIVE_KEY, .name = "DeriveKey" }, { .val = KMIP_OPERATION_CERTIFY, .name = "Certify" }, { .val = KMIP_OPERATION_RE_CERTIFY, .name = "ReCertify" }, { .val = KMIP_OPERATION_LOCATE, .name = "Locate" }, { .val = KMIP_OPERATION_CHECK, .name = "Check" }, { .val = KMIP_OPERATION_GET, .name = "Get" }, { .val = KMIP_OPERATION_GET_ATTRIBUTES, .name = "GetAttributes" }, { .val = KMIP_OPERATION_GET_ATTRIBUTE_LIST, .name = "GetAttributeList" }, { .val = KMIP_OPERATION_ADD_ATTRIBUTE, .name = "AddAttribute" }, { .val = KMIP_OPERATION_MODIFY_ATTRIBUTE, .name = "ModifyAttribute" }, { .val = KMIP_OPERATION_DELETE_ATTRIBUTE, .name = "DeleteAttribute" }, { .val = KMIP_OPERATION_OBTAIN_LEASE, .name = "ObtainLease" }, { .val = KMIP_OPERATION_GET_USAGE_ALLOCATION, .name = "GetUsageAllocation" }, { .val = KMIP_OPERATION_ACTIVATE, .name = "Activate" }, { .val = KMIP_OPERATION_REVOKE, .name = "Revoke" }, { .val = KMIP_OPERATION_DESTROY, .name = "Destroy" }, { .val = KMIP_OPERATION_ARCHIVE, .name = "Archive" }, { .val = KMIP_OPERATION_RECOVER, .name = "Recover" }, { .val = KMIP_OPERATION_VALIDATE, .name = "Validate" }, { .val = KMIP_OPERATION_QUERY, .name = "Query" }, { .val = KMIP_OPERATION_CANCEL, .name = "Cancel" }, { .val = KMIP_OPERATION_POLL, .name = "Poll" }, { .val = KMIP_OPERATION_NOTIFY, .name = "Notify" }, { .val = KMIP_OPERATION_PUT, .name = "Put" }, { .val = KMIP_OPERATION_RE_KEY_KEY_PAIR, .name = "ReKeyKeyPair" }, { .val = KMIP_OPERATION_DISCOVER_VERSIONS, .name = "DiscoverVersions" }, { .val = KMIP_OPERATION_ENCRYPT, .name = "Encrypt" }, { .val = KMIP_OPERATION_DECRYPT, .name = "Decrypt" }, { .val = KMIP_OPERATION_SIGN, .name = "Sign" }, { .val = KMIP_OPERATION_SIGNATURE_VERIFY, .name = "SignatureVerify" }, { .val = KMIP_OPERATION_MAC, .name = "MAC" }, { .val = KMIP_OPERATION_MAC_VERIFY, .name = "MACVerify" }, { .val = KMIP_OPERATION_RNG_RETRIEVE, .name = "RNGRetrieve" }, { .val = KMIP_OPERATION_RNG_SEED, .name = "RNGSeed" }, { .val = KMIP_OPERATION_HASH, .name = "Hash" }, { .val = KMIP_OPERATION_CREATE_SPLIT_KEY, .name = "CreateSplitKey" }, { .val = KMIP_OPERATION_JOIN_SPLIT_KEY, .name = "JoinSplitKey" }, { .val = KMIP_OPERATION_IMPORT, .name = "Import" }, { .val = KMIP_OPERATION_EXPORT, .name = "Export" }, { .val = KMIP_OPERATION_LOG, .name = "Log" }, { .val = KMIP_OPERATION_LOGIN, .name = "Login" }, { .val = KMIP_OPERATION_LOGOUT, .name = "Logout" }, { .val = KMIP_OPERATION_DELEGATE_LOGIN, .name = "DelegateLogin" }, { .val = KMIP_OPERATION_ADJUST_ATTRIBUTE, .name = "AdjustAttribute" }, { .val = KMIP_OPERATION_SET_ATTRIBUTE, .name = "SetAttribute" }, { .val = KMIP_OPERATION_SET_ENDPOINT_ROLE, .name = "SetEndpointRole" }, { .val = KMIP_OPERATION_PKS_11, .name = "PKCS_11" }, { .val = KMIP_OPERATION_INTEROP, .name = "Interop" }, { .val = KMIP_OPERATION_RE_PROVISION, .name = "ReProvision" }, { .val = KMIP_OPERATION_SET_DEFAULTS, .name = "SetDefaults" }, { .val = KMIP_OPERATION_SET_CONSTRAINTS, .name = "SetConstraints" }, { .val = KMIP_OPERATION_GET_CONSTRAINTS, .name = "GetConstraints" }, { .val = KMIP_OPERATION_QUERY_ASYNCHRONOUS_REQUESTS, .name = "QueryAsynchronousRequests" }, { .val = KMIP_OPERATION_PROCESS, .name = "Process" }, { .val = KMIP_OPERATION_PING, .name = "Ping" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_batch_error_cont_options[] = { { .val = KMIP_BATCH_ERR_CONT_CONTINUE, .name = "Continue" }, { .val = KMIP_BATCH_ERR_CONT_STOP, .name = "Stop" }, { .val = KMIP_BATCH_ERR_CONT_UNDO, .name = "Undo" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_crypto_usage_masks[] = { { .val = KMIP_CRY_USAGE_MASK_SIGN, .name = "Sign" }, { .val = KMIP_CRY_USAGE_MASK_VERIFY, .name = "Verify" }, { .val = KMIP_CRY_USAGE_MASK_ENCRYPT, .name = "Encrypt" }, { .val = KMIP_CRY_USAGE_MASK_DECRYPT, .name = "Decrypt" }, { .val = KMIP_CRY_USAGE_MASK_WRAP_KEY, .name = "WrapKey" }, { .val = KMIP_CRY_USAGE_MASK_UNWRAP_KEY, .name = "UnwrapKey" }, { .val = KMIP_CRY_USAGE_MASK_EXPORT, .name = "Export" }, { .val = KMIP_CRY_USAGE_MASK_MAC_GENERATE, .name = "MACGenerate" }, { .val = KMIP_CRY_USAGE_MASK_MAC_VERIFY, .name = "MACVerify" }, { .val = KMIP_CRY_USAGE_MASK_DERIVE_KEY, .name = "DeriveKey" }, { .val = KMIP_CRY_USAGE_MASK_CONTENT_COMMITMENT, .name = "ContentCommitmentNonRepudiation" }, { .val = KMIP_CRY_USAGE_MASK_KEY_AGREEMENT, .name = "KeyAgreement" }, { .val = KMIP_CRY_USAGE_MASK_CERTIFICATE_SIGN, .name = "CertificateSign" }, { .val = KMIP_CRY_USAGE_MASK_CLR_SIGN, .name = "CRLSign" }, { .val = KMIP_CRY_USAGE_MASK_GENERATE_CRYPTOGRAM, .name = "GenerateCryptogram" }, { .val = KMIP_CRY_USAGE_MASK_VALIDATE_CRYPTOGRAM, .name = "ValidateCryptogram" }, { .val = KMIP_CRY_USAGE_MASK_TRANSLATE_ENCRYPT, .name = "TranslateEncrypt" }, { .val = KMIP_CRY_USAGE_MASK_TRANSLATE_DECRYPT, .name = "TranslateDecrypt" }, { .val = KMIP_CRY_USAGE_MASK_TRANSLATE_WRAP, .name = "TranslateWrap" }, { .val = KMIP_CRY_USAGE_MASK_TRANSLATE_UNWRAP, .name = "TranslateUnwrap" }, { .val = KMIP_CRY_USAGE_MASK_AUTHENTICATE, .name = "Authenticate" }, { .val = KMIP_CRY_USAGE_MASK_UNRESTRICTED, .name = "Unrestricted" }, { .val = KMIP_CRY_USAGE_MASK_FPE_ENCRYPT, .name = "FPEEncrypt" }, { .val = KMIP_CRY_USAGE_MASK_FPE_DECRYPT, .name = "FPEDecrypt" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_result_statuses[] = { { .val = KMIP_RESULT_STATUS_SUCCESS, .name = "Success" }, { .val = KMIP_RESULT_STATUS_OPERATION_FAILED, .name = "OperationFailed" }, { .val = KMIP_RESULT_STATUS_OPERATION_PENDING, .name = "OperationPending" }, { .val = KMIP_RESULT_STATUS_OPERATION_UNDONE, .name = "OperationUndone" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_result_reasons[] = { { .val = KMIP_RESULT_REASON_ITEM_NOT_FOUND, .name = "ItemNotFound" }, { .val = KMIP_RESULT_REASON_RESPONSE_TOO_LARGE, .name = "ResponseTooLarge" }, { .val = KMIP_RESULT_REASON_AUTH_NOT_SUCCESSFUL, .name = "AuthenticationNotSuccessful" }, { .val = KMIP_RESULT_REASON_INVALID_MESSAGE, .name = "InvalidMessage" }, { .val = KMIP_RESULT_REASON_OPERATION_NOT_SUCCESSFUL, .name = "OperationNotSupported" }, { .val = KMIP_RESULT_REASON_MISSING_DATA, .name = "MissingData" }, { .val = KMIP_RESULT_REASON_INVALIUD_FIELD, .name = "InvalidField" }, { .val = KMIP_RESULT_REASON_FEATURE_NOT_SUPPORTED, .name = "FeatureNotSupported" }, { .val = KMIP_RESULT_REASON_OP_CANCELED_BY_REQUESTOR, .name = "OperationCanceledByRequeste" }, { .val = KMIP_RESULT_REASON_CRYPTOGRAPHIC_FAILURE, .name = "CryptographicFailure" }, { .val = KMIP_RESULT_REASON_ILLEGAL_OPERATION, .name = "IllegalOperation" }, { .val = KMIP_RESULT_REASON_PERMISSION_DENIED, .name = "PermissionDenied" }, { .val = KMIP_RESULT_REASON_OBJECT_ARCHIVED, .name = "ObjectArchived" }, { .val = KMIP_RESULT_REASON_INDEX_OUT_OF_BOUNDS, .name = "IndexOutOfBounds" }, { .val = KMIP_RESULT_REASON_APP_NAMESPACE_NOT_SUPPORTED, .name = "ApplicationNamespaceNotSupported" }, { .val = KMIP_RESULT_REASON_KEY_FORMAT_TYPE_NOT_SUPPORTED, .name = "KeyFormatTypeNotSupported" }, { .val = KMIP_RESULT_REASON_KEY_COMPRESSION_TYPE_NOT_SUPPORTED, .name = "KeyCompressionTypeNotSupported" }, { .val = KMIP_RESULT_REASON_ENCODING_OPTION_ERROR, .name = "EncodingOptionError" }, { .val = KMIP_RESULT_REASON_KEY_VALUE_NOT_PRESENT, .name = "KeyValueNotPresent" }, { .val = KMIP_RESULT_REASON_ATTESTATION_REQUIRED, .name = "AttestationRequired" }, { .val = KMIP_RESULT_REASON_ATTESTATION_FAILED, .name = "AttestationFailed" }, { .val = KMIP_RESULT_REASON_SENSITIVE, .name = "Sensitive" }, { .val = KMIP_RESULT_REASON_NOT_EXTRACTABLE, .name = "NotExtractable" }, { .val = KMIP_RESULT_REASON_OBJECT_ALREADY_EXISTS, .name = "ObjectAlreadyExists" }, { .val = KMIP_RESULT_REASON_INVALID_TICKET, .name = "InvalidTicket" }, { .val = KMIP_RESULT_REASON_USAGE_LIMIT_EXCEEDED, .name = "UsageLimitExceeded" }, { .val = KMIP_RESULT_REASON_NUMERIC_RANGE, .name = "NumericRange" }, { .val = KMIP_RESULT_REASON_INVALID_DATA_TYPE, .name = "InvalidDataType" }, { .val = KMIP_RESULT_REASON_READ_ONLY_ATTRIBUTE, .name = "ReadOnlyAttribute" }, { .val = KMIP_RESULT_REASON_MULTI_VALUED_ATTRIBUTE, .name = "MultiValuedAttribute" }, { .val = KMIP_RESULT_REASON_UNSUPPORTED_ATTRIBUTE, .name = "UnsupportedAttribute" }, { .val = KMIP_RESULT_REASON_ATTRIBUTE_INSTANCE_NOT_FOUND, .name = "AttributeInstanceNotFound" }, { .val = KMIP_RESULT_REASON_ATTRIBUTE_NOT_FOUND, .name = "AttributeNotFound" }, { .val = KMIP_RESULT_REASON_ATTRIBUTE_READ_ONLY, .name = "AttributeReadOnly" }, { .val = KMIP_RESULT_REASON_ATTRIBUTE_SINGLE_VALUED, .name = "AttributeSingleValued" }, { .val = KMIP_RESULT_REASON_BAD_CRYPTOGRAPHIC_PARAMETERS, .name = "BadCryptographicParameters" }, { .val = KMIP_RESULT_REASON_BAD_PASSWORD, .name = "BadPassword" }, { .val = KMIP_RESULT_REASON_CODEC_ERROR, .name = "CodecError" }, { .val = KMIP_RESULT_REASON_ILLEGAL_OBJECT_TYPE, .name = "IllegalObjectType" }, { .val = KMIP_RESULT_REASON_INCOMPATIBLE_CRYPTO_USAGE_MASK, .name = "IncompatibleCryptographicUsageMask" }, { .val = KMIP_RESULT_REASON_INTERNAL_SERVER_ERROR, .name = "InternalServerError" }, { .val = KMIP_RESULT_REASON_INVALID_ASYNC_CORRELATION_VALUE, .name = "InvalidAsynchronousCorrelationValue" }, { .val = KMIP_RESULT_REASON_INVALID_ATTRIBUTE, .name = "InvalidAttribute" }, { .val = KMIP_RESULT_REASON_INVALID_ATTRIBUTE_VALUE, .name = "InvalidAttributeValue" }, { .val = KMIP_RESULT_REASON_INVALID_CORRELATION_VALUE, .name = "InvalidCorrelationValue" }, { .val = KMIP_RESULT_REASON_INVALID_CSR, .name = "InvalidCSR" }, { .val = KMIP_RESULT_REASON_INVALID_OBJECT_TYPE, .name = "InvalidObjectType" }, { .val = KMIP_RESULT_REASON_KEY_WRAP_TYPE_NOT_SUPPORTED, .name = "KeyWrapTypeNotSupported" }, { .val = KMIP_RESULT_REASON_MISSING_INITIALIZATION_VECTOR, .name = "MissingInitializationVector" }, { .val = KMIP_RESULT_REASON_NOT_UNIQUE_NAME_ATTRIBUTE, .name = "NonUniqueNameAttribute" }, { .val = KMIP_RESULT_REASON_OBJECT_DESTROYED, .name = "ObjectDestroyed" }, { .val = KMIP_RESULT_REASON_OBJECT_NOT_FOUND, .name = "ObjectNotFound" }, { .val = KMIP_RESULT_REASON_NOT_AUTHORISED, .name = "NotAuthorised" }, { .val = KMIP_RESULT_REASON_SERVER_LIMIT_EXCEEDED, .name = "ServerLimitExceeded" }, { .val = KMIP_RESULT_REASON_UNKNOWN_ENUMERATION, .name = "UnknownEnumeration" }, { .val = KMIP_RESULT_REASON_UNKNOWN_MESSAGE_EXTENSION, .name = "UnknownMessageExtension" }, { .val = KMIP_RESULT_REASON_UNKNOWN_TAG, .name = "UnknownTag" }, { .val = KMIP_RESULT_REASON_UNSUPPORTED_CRYPTO_PARAMETERS, .name = "UnsupportedCryptographicParameters" }, { .val = KMIP_RESULT_REASON_UNSUPPORTED_PROTOCOL_VERSION, .name = "UnsupportedProtocolVersion" }, { .val = KMIP_RESULT_REASON_WRAPPING_OBJECT_ARCHIVED, .name = "WrappingObjectArchived" }, { .val = KMIP_RESULT_REASON_WRAPPING_OBJECT_DESTROYED, .name = "WrappingObjectDestroyed" }, { .val = KMIP_RESULT_REASON_WRAPPING_OBJECT_NOT_FOUND, .name = "WrappingObjectNotFound" }, { .val = KMIP_RESULT_REASON_WRONG_KEY_LIFECYCLE_STATE, .name = "WrongKeyLifecycleState" }, { .val = KMIP_RESULT_REASON_PROTECTION_STORAGE_UNAVAILABLE, .name = "ProtectionStorageUnavailable" }, { .val = KMIP_RESULT_REASON_PKCS_11_CODE_ERROR, .name = "PKCS_11CodecError" }, { .val = KMIP_RESULT_REASON_PKCS_11_INVALID_FUNCTION, .name = "PKCS_11InvalidFunction" }, { .val = KMIP_RESULT_REASON_PKCS_11_INVALID_INTERFACE, .name = "PKCS_11InvalidInterface" }, { .val = KMIP_RESULT_REASON_PRIVATE_PROT_STORAGE_UNAVAILABLE, .name = "PrivateProtectionStorageUnavailable" }, { .val = KMIP_RESULT_REASON_PUBLIC_PROT_STORAGE_UNAVAILABLE, .name = "PublicProtectionStorageUnavailable" }, { .val = KMIP_RESULT_REASON_UNKNOWN_OBJECT_GROUP, .name = "UnknownObjectGroup" }, { .val = KMIP_RESULT_REASON_CONSTRAINT_VIOLATION, .name = "ConstraintViolation" }, { .val = KMIP_RESULT_REASON_DUPLICATE_PROCESS_REQUEST, .name = "DuplicateProcessRequest" }, { .val = KMIP_RESULT_REASON_GENERAL_FAILURE, .name = "GeneralFailure" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_query_functions[] = { { .val = KMIP_QUERY_OPERATIONS, .name = "QueryOperations" }, { .val = KMIP_QUERY_OBJECTS, .name = "QueryObjects" }, { .val = KMIP_QUERY_SERVER_INFORMATION, .name = "QueryServerInformation" }, { .val = KMIP_QUERY_APPLICATION_NAMESPACES, .name = "QueryApplicationNamespaces" }, { .val = KMIP_QUERY_EXTENSION_LIST, .name = "QueryExtensionList" }, { .val = KMIP_QUERY_EXTENSION_MAP, .name = "QueryExtensionMap" }, { .val = KMIP_QUERY_ATTESTATION_TYPES, .name = "QueryAttestationTypes" }, { .val = KMIP_QUERY_QUERY_RNGS, .name = "QueryRNGs" }, { .val = KMIP_QUERY_VALIDATIONS, .name = "QueryValidations" }, { .val = KMIP_QUERY_PROFILES, .name = "QueryProfiles" }, { .val = KMIP_QUERY_CAPABILITIES, .name = "QueryCapabilities" }, { .val = KMIP_QUERY_CLIENT_REGISTRATION_METHODS, .name = "QueryClientRegistrationMethods" }, { .val = KMIP_QUERY_DEFAULTS_INFORMATION, .name = "QueryDefaultsInformation" }, { .val = KMIP_QUERY_STORAGE_PROTECTION_MASKS, .name = "QueryStorageProtectionMasks" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_name_types[] = { { .val = KMIP_NAME_TYPE_UNINTERPRETED_TEXT_STRING, .name = "UninterpretedTextString" }, { .val = KMIP_NAME_TYPE_URI, .name = "URI" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_alternate_name_types[] = { { .val = KMIP_ALT_NAME_TYPE_UNINTERPRETED_TEXT_STRING, .name = "UninterpretedTextString" }, { .val = KMIP_ALT_NAME_TYPE_URI, .name = "URI" }, { .val = KMIP_ALT_NAME_TYPE_OBJECT_SERIAL_NUMBER, .name = "ObjectSerialNumber" }, { .val = KMIP_ALT_NAME_TYPE_EMAIL_ADDRESS, .name = "EmailAddress" }, { .val = KMIP_ALT_NAME_TYPE_DNS_NAME, .name = "DNSName" }, { .val = KMIP_ALT_NAME_TYPE_X_500_DISTINGUISHED_NAME, .name = "X_500DistinguishedName" }, { .val = KMIP_ALT_NAME_TYPE_IP_ADDRESS, .name = "IPAddress" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_unique_identifiers[] = { { .val = KMIP_UNIQUE_ID_ID_PLACEHOLDER, .name = "IDPlaceholder" }, { .val = KMIP_UNIQUE_ID_CERTIFY, .name = "Certify" }, { .val = KMIP_UNIQUE_ID_CREATE, .name = "Create" }, { .val = KMIP_UNIQUE_ID_CREATE_KEY_PAIR, .name = "CreateKeyPair" }, { .val = KMIP_UNIQUE_ID_CREATE_KEY_PAIR_PRIVATE, .name = "CreateKeyPairPrivateKey" }, { .val = KMIP_UNIQUE_ID_CREATE_KEY_PAIR_PUBLIC, .name = "CreateKeyPairPublicKey" }, { .val = KMIP_UNIQUE_ID_CREATE_SPLIT_KEY, .name = "CreateSplitKey" }, { .val = KMIP_UNIQUE_ID_DERIVE_KEY, .name = "DeriveKey" }, { .val = KMIP_UNIQUE_ID_IMPORT, .name = "Import" }, { .val = KMIP_UNIQUE_ID_JOIN_SPLIT_KEY, .name = "JoinSplitKey" }, { .val = KMIP_UNIQUE_ID_LOCATE, .name = "Locate" }, { .val = KMIP_UNIQUE_ID_REGISTER, .name = "Register" }, { .val = KMIP_UNIQUE_ID_RE_KEY, .name = "ReKey" }, { .val = KMIP_UNIQUE_ID_RE_CERTIFY, .name = "ReCertify" }, { .val = KMIP_UNIQUE_ID_RE_KEY_KEY_PAIR, .name = "ReKeyKeyPair" }, { .val = KMIP_UNIQUE_ID_RE_KEY_KEY_PAIR_PRIVATE, .name = "ReKeyKeyPairPrivateKey" }, { .val = KMIP_UNIQUE_ID_RE_KEY_KEY_PAIR_PUBLIC, .name = "ReKeyKeyPairPublicKey" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_object_types[] = { { .val = KMIP_OBJECT_TYPE_CERTIFICATE, .name = "Certificate" }, { .val = KMIP_OBJECT_TYPE_SYMMETRIC_KEY, .name = "SymmetricKey" }, { .val = KMIP_OBJECT_TYPE_PUBLIC_KEY, .name = "PublicKey" }, { .val = KMIP_OBJECT_TYPE_PRIVATE_KEY, .name = "PrivateKey" }, { .val = KMIP_OBJECT_TYPE_SPLIT_KEY, .name = "SplitKey" }, { .val = KMIP_OBJECT_TYPE_TEMPLATE, .name = "Template" }, { .val = KMIP_OBJECT_TYPE_SECRET_DATA, .name = "SecretData" }, { .val = KMIP_OBJECT_TYPE_OPAQUE_OBJECT, .name = "OpaqueObject" }, { .val = KMIP_OBJECT_TYPE_PGP_KEY, .name = "PGPKey" }, { .val = KMIP_OBJECT_TYPE_CERTIFICATE_REQUEST, .name = "CertificateRequest" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_crypto_algos[] = { { .val = KMIP_CRYPTO_ALGO_DES, .name = "DES" }, { .val = KMIP_CRYPTO_ALGO_3DES, .name = "3DES" }, { .val = KMIP_CRYPTO_ALGO_AES, .name = "AES" }, { .val = KMIP_CRYPTO_ALGO_RSA, .name = "RSA" }, { .val = KMIP_CRYPTO_ALGO_DSA, .name = "DSA" }, { .val = KMIP_CRYPTO_ALGO_ECDSA, .name = "ECDSA" }, { .val = KMIP_CRYPTO_ALGO_HMAC_SHA1, .name = "HMAC_SHA1" }, { .val = KMIP_CRYPTO_ALGO_HMAC_SHA224, .name = "HMAC_SHA224" }, { .val = KMIP_CRYPTO_ALGO_HMAC_SHA256, .name = "HMAC_SHA256" }, { .val = KMIP_CRYPTO_ALGO_HMAC_SHA384, .name = "HMAC_SHA384" }, { .val = KMIP_CRYPTO_ALGO_HMAC_SHA512, .name = "HMAC_SHA512" }, { .val = KMIP_CRYPTO_ALGO_HMAC_MD5, .name = "HMAC_MD5" }, { .val = KMIP_CRYPTO_ALGO_DH, .name = "DH" }, { .val = KMIP_CRYPTO_ALGO_ECDH, .name = "ECDH" }, { .val = KMIP_CRYPTO_ALGO_ECMQV, .name = "ECMQV" }, { .val = KMIP_CRYPTO_ALGO_BLOWFISH, .name = "Blowfish" }, { .val = KMIP_CRYPTO_ALGO_CAMELLIA, .name = "Camellia" }, { .val = KMIP_CRYPTO_ALGO_CAST5, .name = "CAST5" }, { .val = KMIP_CRYPTO_ALGO_IDEA, .name = "IDEA" }, { .val = KMIP_CRYPTO_ALGO_MARS, .name = "MARS" }, { .val = KMIP_CRYPTO_ALGO_RC2, .name = "RC2" }, { .val = KMIP_CRYPTO_ALGO_RC4, .name = "RC4" }, { .val = KMIP_CRYPTO_ALGO_RC5, .name = "RC5" }, { .val = KMIP_CRYPTO_ALGO_SKIPJACK, .name = "SKIPJACK" }, { .val = KMIP_CRYPTO_ALGO_TWOFISH, .name = "Twofish" }, { .val = KMIP_CRYPTO_ALGO_EC, .name = "EC" }, { .val = KMIP_CRYPTO_ALGO_ONE_TIME_PAD, .name = "OneTimePad" }, { .val = KMIP_CRYPTO_ALGO_CHACHA20, .name = "ChaCha20" }, { .val = KMIP_CRYPTO_ALGO_POLY1305, .name = "Poly1305" }, { .val = KMIP_CRYPTO_ALGO_CHACHA20_POLY1305, .name = "ChaCha20Poly1305" }, { .val = KMIP_CRYPTO_ALGO_SHA3_224, .name = "SHA3_224" }, { .val = KMIP_CRYPTO_ALGO_SHA3_256, .name = "SHA3_256" }, { .val = KMIP_CRYPTO_ALGO_SHA3_384, .name = "SHA3_384" }, { .val = KMIP_CRYPTO_ALGO_SHA3_512, .name = "SHA3_512" }, { .val = KMIP_CRYPTO_ALGO_HMAC_SHA3_224, .name = "HMAC_SHA3_224" }, { .val = KMIP_CRYPTO_ALGO_HMAC_SHA3_256, .name = "HMAC_SHA3_256" }, { .val = KMIP_CRYPTO_ALGO_HMAC_SHA3_384, .name = "HMAC_SHA3_384" }, { .val = KMIP_CRYPTO_ALGO_HMAC_SHA3_512, .name = "HMAC_SHA3_512" }, { .val = KMIP_CRYPTO_ALGO_SHAKE_128, .name = "SHAKE_128" }, { .val = KMIP_CRYPTO_ALGO_SHAKE_256, .name = "SHAKE_256" }, { .val = KMIP_CRYPTO_ALGO_ARIA, .name = "ARIA" }, { .val = KMIP_CRYPTO_ALGO_SEED, .name = "SEED" }, { .val = KMIP_CRYPTO_ALGO_SM2, .name = "SM2" }, { .val = KMIP_CRYPTO_ALGO_SM3, .name = "SM3" }, { .val = KMIP_CRYPTO_ALGO_SM4, .name = "SM4" }, { .val = KMIP_CRYPTO_ALGO_GOST_R34_10_2012, .name = "GOSTR34_10_2012" }, { .val = KMIP_CRYPTO_ALGO_GOST_R34_11_2012, .name = "GOSTR34_11_2012" }, { .val = KMIP_CRYPTO_ALGO_GOST_R34_13_2015, .name = "GOSTR34_13_2015" }, { .val = KMIP_CRYPTO_ALGO_GOST_28147_89, .name = "GOST28147_89" }, { .val = KMIP_CRYPTO_ALGO_XMSS, .name = "XMSS" }, { .val = KMIP_CRYPTO_ALGO_SPHINCS_256, .name = "SPHINCS_256" }, { .val = KMIP_CRYPTO_ALGO_MCELIECE, .name = "McEliece" }, { .val = KMIP_CRYPTO_ALGO_MCELIECE_6960119, .name = "McEliece_6960119" }, { .val = KMIP_CRYPTO_ALGO_MCELIECE_8192128, .name = "McEliece_8192128" }, { .val = KMIP_CRYPTO_ALGO_ED25519, .name = "Ed25519" }, { .val = KMIP_CRYPTO_ALGO_ED448, .name = "Ed448" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_certificate_types[] = { { .val = KMIP_CERTIFICATE_TYPE_X_509, .name = "X_509" }, { .val = KMIP_CERTIFICATE_TYPE_PGP, .name = "PGP" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_states[] = { { .val = KMIP_STATE_PRE_ACTIVE, .name = "PreActive" }, { .val = KMIP_STATE_ACTIVE, .name = "Active" }, { .val = KMIP_STATE_DEACTIVATED, .name = "Deactivated" }, { .val = KMIP_STATE_COMPROMISED, .name = "Compromised" }, { .val = KMIP_STATE_DESTROYED, .name = "Destroyed" }, { .val = KMIP_STATE_DESTROYED_COMPROMISED, .name = "DestroyedCompromised" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_protection_storage_masks[] = { { .val = KMIP_PROT_STORAGE_MASK_SOFTWARE, .name = "Software" }, { .val = KMIP_PROT_STORAGE_MASK_HARDWARE, .name = "Hardware" }, { .val = KMIP_PROT_STORAGE_MASK_ON_PROCESSOR, .name = "OnProcessor" }, { .val = KMIP_PROT_STORAGE_MASK_ON_SYSTEM, .name = "OnSystem" }, { .val = KMIP_PROT_STORAGE_MASK_OFF_SYSTEM, .name = "OffSystem" }, { .val = KMIP_PROT_STORAGE_MASK_HYPERVISOR, .name = "Hypervisor" }, { .val = KMIP_PROT_STORAGE_MASK_OPERATING_SYSTEM, .name = "OperatingSystem" }, { .val = KMIP_PROT_STORAGE_MASK_CONTAINER, .name = "Container" }, { .val = KMIP_PROT_STORAGE_MASK_ON_PREMISES, .name = "OnPremises" }, { .val = KMIP_PROT_STORAGE_MASK_OFF_PREMISES, .name = "OffPremises" }, { .val = KMIP_PROT_STORAGE_MASK_SELF_MANAGED, .name = "SelfManaged" }, { .val = KMIP_PROT_STORAGE_MASK_OUTSOURCED, .name = "Outsourced" }, { .val = KMIP_PROT_STORAGE_MASK_VALIDATED, .name = "Validated" }, { .val = KMIP_PROT_STORAGE_MASK_SAME_JURISDICATION, .name = "SameJurisdiction" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_revoke_reasons[] = { { .val = KMIP_REVOK_RSN_UNSPECIFIED, .name = "Unspecified" }, { .val = KMIP_REVOK_RSN_KEY_COMPROMISE, .name = "KeyCompromise" }, { .val = KMIP_REVOK_RSN_CA_COMPROMISE, .name = "CACompromise" }, { .val = KMIP_REVOK_RSN_AFFILIATION_CHANGED, .name = "AffiliationChanged" }, { .val = KMIP_REVOK_RSN_SUPERSEDED, .name = "Superseded" }, { .val = KMIP_REVOK_RSN_CESSATION_OF_OPERATION, .name = "CessationOfOperation" }, { .val = KMIP_REVOK_RSN_PRIVILEGE_WITHDRAWN, .name = "PrivilegeWithdrawn" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_object_group_members[] = { { .val = KMIP_OBJ_GROUP_MEMBER_FRESH, .name = "GroupMemberFresh" }, { .val = KMIP_OBJ_GROUP_MEMBER_DEFAULT, .name = "GroupMemberDefault" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_storage_status_masks[] = { { .val = KMIP_STORAGE_STATUS_MASK_ONLINE, .name = "OnLineStorage" }, { .val = KMIP_STORAGE_STATUS_MASK_ARCHIVAL, .name = "ArchivalStorage" }, { .val = KMIP_STORAGE_STATUS_MASK_DESTTROYED, .name = "DestroyedStorage" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_key_format_types[] = { { .val = KMIP_KEY_FORMAT_TYPE_RAW, .name = "Raw" }, { .val = KMIP_KEY_FORMAT_TYPE_OPAQUE, .name = "Opaque" }, { .val = KMIP_KEY_FORMAT_TYPE_PKCS_1, .name = "PKCS_1" }, { .val = KMIP_KEY_FORMAT_TYPE_PKCS_8, .name = "PKCS_8" }, { .val = KMIP_KEY_FORMAT_TYPE_X_509, .name = "X_509" }, { .val = KMIP_KEY_FORMAT_TYPE_EC_PRIVATE_KEY, .name = "ECPrivateKey" }, { .val = KMIP_KEY_FORMAT_TYPE_TRANSPARENT_SYMMETRIC_KEY, .name = "TransparentSymmetricKey" }, { .val = KMIP_KEY_FORMAT_TYPE_TRANSPARENT_DSA_PRIVATE_KEY, .name = "TransparentDSAPrivateKey" }, { .val = KMIP_KEY_FORMAT_TYPE_TRANSPARENT_DSA_PUBLIC_KEY, .name = "TransparentDSAPublicKey" }, { .val = KMIP_KEY_FORMAT_TYPE_TRANSPARENT_RSA_PRIVATE_KEY, .name = "TransparentRSAPrivateKey" }, { .val = KMIP_KEY_FORMAT_TYPE_TRANSPARENT_RSA_PUBLIC_KEY, .name = "TransparentRSAPublicKey" }, { .val = KMIP_KEY_FORMAT_TYPE_TRANSPARENT_DH_PRIVATE_KEY, .name = "TransparentDHPrivateKey" }, { .val = KMIP_KEY_FORMAT_TYPE_TRANSPARENT_DH_PUBLIC_KEY, .name = "TransparentDHPublicKey" }, { .val = KMIP_KEY_FORMAT_TYPE_TRANSPARENT_ECDSA_PRIVATE_KEY, .name = "TransparentECDSAPrivateKey" }, { .val = KMIP_KEY_FORMAT_TYPE_TRANSPARENT_ECDSA_PUBLIC_KEY, .name = "TransparentECDSAPublicKey" }, { .val = KMIP_KEY_FORMAT_TYPE_TRANSPARENT_ECDH_PRIVATE_KEY, .name = "TransparentECDHPrivateKey" }, { .val = KMIP_KEY_FORMAT_TYPE_TRANSPARENT_ECDH_PUBLIC_KEY, .name = "TransparentECDHPublicKey" }, { .val = KMIP_KEY_FORMAT_TYPE_TRANSPARENT_ECMQV_PRIVATE_KEY, .name = "TransparentECMQVPrivateKey" }, { .val = KMIP_KEY_FORMAT_TYPE_TRANSPARENT_ECMQV_PUBLIC_KEY, .name = "TransparentECMQVPublicKey" }, { .val = KMIP_KEY_FORMAT_TYPE_TRANSPARENT_EC_PRIVATE_KEY, .name = "TransparentECPrivateKey" }, { .val = KMIP_KEY_FORMAT_TYPE_TRANSPARENT_EC_PUBLIC_KEY, .name = "TransparentECPublicKey" }, { .val = KMIP_KEY_FORMAT_TYPE_PKCS_12, .name = "PKCS_12" }, { .val = KMIP_KEY_FORMAT_TYPE_PKCS_10, .name = "PKCS_10" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_key_compression_types[] = { { .val = KMIP_KEY_COMPRESSION_TYPE_EC_PUBKEY_UNCOMPRESSED, .name = "ECPublicKeyTypeUncompressed" }, { .val = KMIP_KEY_COMPRESSION_TYPE_EC_PUBKEY_COMPRESSED_PRIME, .name = "ECPublicKeyTypeX9_62CompressedPrime" }, { .val = KMIP_KEY_COMPRESSION_TYPE_EC_PUBKEY_COMPRESSED_CHAR2, .name = "ECPublicKeyTypeX9_62CompressedChar2" }, { .val = KMIP_KEY_COMPRESSION_TYPE_EC_PUBKEY_HYBID, .name = "ECPublicKeyTypeX9_62Hybrid" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_wrapping_methods[] = { { .val = KMIP_WRAPPING_METHOD_ENCRYPT, .name = "Encrypt" }, { .val = KMIP_WRAPPING_METHOD_MAC_SIGN, .name = "MAC_sign" }, { .val = KMIP_WRAPPING_METHOD_ENCRYPT_THEN_MAC_SIGN, .name = "EncryptThenMAC_sign" }, { .val = KMIP_WRAPPING_METHOD_MAC_SIGN_THEN_ENCRYPT, .name = "MAC_signThenEncrypt" }, { .val = KMIP_WRAPPING_METHOD_TR_31, .name = "TR_31" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_key_wrap_types[] = { { .val = KMIP_KEY_WRAP_TYPE_NOT_WRAPPED, .name = "NotWrapped" }, { .val = KMIP_KEY_WRAP_TYPE_AS_REGISTERED, .name = "AsRegistered" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_block_cipher_modes[] = { { .val = KMIP_BLOCK_CIPHER_MODE_CBC, .name = "CBC" }, { .val = KMIP_BLOCK_CIPHER_MODE_ECB, .name = "ECB" }, { .val = KMIP_BLOCK_CIPHER_MODE_PCBC, .name = "PCBC" }, { .val = KMIP_BLOCK_CIPHER_MODE_CFB, .name = "CFB" }, { .val = KMIP_BLOCK_CIPHER_MODE_OFB, .name = "OFB" }, { .val = KMIP_BLOCK_CIPHER_MODE_CTR, .name = "CTR" }, { .val = KMIP_BLOCK_CIPHER_MODE_CMAC, .name = "CMAC" }, { .val = KMIP_BLOCK_CIPHER_MODE_CCM, .name = "CCM" }, { .val = KMIP_BLOCK_CIPHER_MODE_GCM, .name = "GCM" }, { .val = KMIP_BLOCK_CIPHER_MODE_CBC_MAC, .name = "CBC_MAC" }, { .val = KMIP_BLOCK_CIPHER_MODE_XTS, .name = "XTS" }, { .val = KMIP_BLOCK_CIPHER_MODE_AES_KEY_WRAP_PADDING, .name = "AESKeyWrapPadding" }, { .val = KMIP_BLOCK_CIPHER_MODE_NIST_KEY_WRAP, .name = "NISTKeyWrap" }, { .val = KMIP_BLOCK_CIPHER_MODE_X9_102_AESKW, .name = "X9_102AESKW" }, { .val = KMIP_BLOCK_CIPHER_MODE_X9_102_TDKW, .name = "X9_102TDKW" }, { .val = KMIP_BLOCK_CIPHER_MODE_X9_102_AKW1, .name = "X9_102AKW1" }, { .val = KMIP_BLOCK_CIPHER_MODE_X9_102_AKW2, .name = "X9_102AKW2" }, { .val = KMIP_BLOCK_CIPHER_MODE_AEAD, .name = "AEAD" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_padding_methods[] = { { .val = KMIP_PADDING_METHOD_NONE, .name = "None" }, { .val = KMIP_PADDING_METHOD_OAEP, .name = "OAEP" }, { .val = KMIP_PADDING_METHOD_PKCS5, .name = "PKCS5" }, { .val = KMIP_PADDING_METHOD_SSL3, .name = "SSL3" }, { .val = KMIP_PADDING_METHOD_ZEROS, .name = "Zeros" }, { .val = KMIP_PADDING_METHOD_ANSI_X9_23, .name = "ANSIX9_23" }, { .val = KMIP_PADDING_METHOD_ISO_10126, .name = "ISO10126" }, { .val = KMIP_PADDING_METHOD_PKCS_1_5, .name = "PKCS1V1_5" }, { .val = KMIP_PADDING_METHOD_X9_31, .name = "X9_31" }, { .val = KMIP_PADDING_METHOD_PSS, .name = "PSS" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_hashing_algos[] = { { .val = KMIP_HASHING_ALGO_MD2, .name = "MD2" }, { .val = KMIP_HASHING_ALGO_MD4, .name = "MD4" }, { .val = KMIP_HASHING_ALGO_MD5, .name = "MD5" }, { .val = KMIP_HASHING_ALGO_SHA_1, .name = "SHA_1" }, { .val = KMIP_HASHING_ALGO_SHA_224, .name = "SHA_224" }, { .val = KMIP_HASHING_ALGO_SHA_256, .name = "SHA_256" }, { .val = KMIP_HASHING_ALGO_SHA_384, .name = "SHA_384" }, { .val = KMIP_HASHING_ALGO_SHA_512, .name = "SHA_512" }, { .val = KMIP_HASHING_ALGO_RIPEMD_160, .name = "RIPEMD_160" }, { .val = KMIP_HASHING_ALGO_TIGER, .name = "Tiger" }, { .val = KMIP_HASHING_ALGO_WIRLPOOL, .name = "Whirlpool" }, { .val = KMIP_HASHING_ALGO_SHA_512_224, .name = "SHA_512_224" }, { .val = KMIP_HASHING_ALGO_SHA_512_256, .name = "SHA_512_256" }, { .val = KMIP_HASHING_ALGO_SHA_3_224, .name = "SHA3_224" }, { .val = KMIP_HASHING_ALGO_SHA_3_256, .name = "SHA3_256" }, { .val = KMIP_HASHING_ALGO_SHA_3_384, .name = "SHA3_384" }, { .val = KMIP_HASHING_ALGO_SHA_3_512, .name = "SHA3_512" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_key_role_types[] = { { .val = KMIP_KEY_ROLE_TYPE_BDK, .name = "BDK" }, { .val = KMIP_KEY_ROLE_TYPE_CVK, .name = "CVK" }, { .val = KMIP_KEY_ROLE_TYPE_DEK, .name = "DEK" }, { .val = KMIP_KEY_ROLE_TYPE_KMAC, .name = "KMAC" }, { .val = KMIP_KEY_ROLE_TYPE_MKSMC, .name = "MKSMC" }, { .val = KMIP_KEY_ROLE_TYPE_MKSMI, .name = "MKSMI" }, { .val = KMIP_KEY_ROLE_TYPE_MKDAC, .name = "_MKDAC" }, { .val = KMIP_KEY_ROLE_TYPE_MKDN, .name = "MKDN" }, { .val = KMIP_KEY_ROLE_TYPE_MKCP, .name = "MKCP" }, { .val = KMIP_KEY_ROLE_TYPE_MKOTH, .name = "MKOTH" }, { .val = KMIP_KEY_ROLE_TYPE_KEK, .name = "KEK" }, { .val = KMIP_KEY_ROLE_TYPE_MAC16609, .name = "MAC16609" }, { .val = KMIP_KEY_ROLE_TYPE_MAC97971, .name = "MAC97971" }, { .val = KMIP_KEY_ROLE_TYPE_MAC97972, .name = "MAC97972" }, { .val = KMIP_KEY_ROLE_TYPE_MAC97973, .name = "MAC97973" }, { .val = KMIP_KEY_ROLE_TYPE_MAC97974, .name = "MAC97974" }, { .val = KMIP_KEY_ROLE_TYPE_MAC97975, .name = "MAC97975" }, { .val = KMIP_KEY_ROLE_TYPE_ZPK, .name = "ZPK" }, { .val = KMIP_KEY_ROLE_TYPE_PVKIBM, .name = "PVKIBM" }, { .val = KMIP_KEY_ROLE_TYPE_PVKPVV, .name = "PVKPVV" }, { .val = KMIP_KEY_ROLE_TYPE_PVKOTH, .name = "PVKOTH" }, { .val = KMIP_KEY_ROLE_TYPE_DUKPT, .name = "DUKPT" }, { .val = KMIP_KEY_ROLE_TYPE_IV, .name = "IV" }, { .val = KMIP_KEY_ROLE_TYPE_TRKBK, .name = "TRKBK" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_sinature_algos[] = { { .val = KMIP_SIGNATURE_ALGO_MD2_WITH_RSA_ENCRYPTION, .name = "MD2WithRSAEncryption" }, { .val = KMIP_SIGNATURE_ALGO_MD5_WITH_RSA_ENCRYPTION, .name = "MD5WithRSAEncryption" }, { .val = KMIP_SIGNATURE_ALGO_SHA_1_WITH_RSA_ENCRYPTION, .name = "SHA_1WithRSAEncryption" }, { .val = KMIP_SIGNATURE_ALGO_SHA_224_WITH_RSA_ENCRYPTION, .name = "SHA_244WithRSAEncryption" }, { .val = KMIP_SIGNATURE_ALGO_SHA_256_WITH_RSA_ENCRYPTION, .name = "SHA_256WithRSAEncryption" }, { .val = KMIP_SIGNATURE_ALGO_SHA_384_WITH_RSA_ENCRYPTION, .name = "SHA_384WithRSAEncryption" }, { .val = KMIP_SIGNATURE_ALGO_SHA_512_WITH_RSA_ENCRYPTION, .name = "SHA_512WithRSAEncryption" }, { .val = KMIP_SIGNATURE_ALGO_RSASSA_PSS, .name = "RSASSA_PSS" }, { .val = KMIP_SIGNATURE_ALGO_DSA_WITH_SHA_1, .name = "DSAWithSHA_1" }, { .val = KMIP_SIGNATURE_ALGO_DSA_WITH_SHA_244, .name = "DSAWithSHA224" }, { .val = KMIP_SIGNATURE_ALGO_DSA_WITH_SHA_256, .name = "DSAWithSHA256" }, { .val = KMIP_SIGNATURE_ALGO_ECDSA_WITH_SHA_1, .name = "ECDSAWithSHA_1" }, { .val = KMIP_SIGNATURE_ALGO_ECDSA_WITH_SHA_224, .name = "ECDSAWithSHA224" }, { .val = KMIP_SIGNATURE_ALGO_ECDSA_WITH_SHA_256, .name = "ECDSAWithSHA256" }, { .val = KMIP_SIGNATURE_ALGO_ECDSA_WITH_SHA_384, .name = "ECDSAWithSHA384" }, { .val = KMIP_SIGNATURE_ALGO_ECDSA_WITH_SHA_512, .name = "ECDSAWithSHA512" }, { .val = KMIP_SIGNATURE_ALGO_SHA3_256_WITH_RSA_ENCRYPTION, .name = "SHA3_256WithRSAEncryption" }, { .val = KMIP_SIGNATURE_ALGO_SHA3_385_WITH_RSA_ENCRYPTION, .name = "SHA3_384WithRSAEncryption" }, { .val = KMIP_SIGNATURE_ALGO_SHA3_512_WITH_RSA_ENCRYPTION, .name = "SHA3_512WithRSAEncryption" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_mask_generators[] = { { .val = KMIP_MASK_GENERATOR_MGF1, .name = "MGF1" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_encoding_options[] = { { .val = KMIP_ENCODING_OPTION_NO, .name = "NoEncoding" }, { .val = KMIP_ENCODING_OPTION_TTLV, .name = "TTLVEncoding" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_recommended_cuurves[] = { { .val = KMIP_REC_CURVE_P_192, .name = "P192" }, { .val = KMIP_REC_CURVE_K_163, .name = "K163" }, { .val = KMIP_REC_CURVE_B_163, .name = "B163" }, { .val = KMIP_REC_CURVE_P_224, .name = "P224" }, { .val = KMIP_REC_CURVE_K_223, .name = "K223" }, { .val = KMIP_REC_CURVE_B_223, .name = "B223" }, { .val = KMIP_REC_CURVE_P_256, .name = "P256" }, { .val = KMIP_REC_CURVE_K_283, .name = "K283" }, { .val = KMIP_REC_CURVE_B_283, .name = "B283" }, { .val = KMIP_REC_CURVE_P_384, .name = "P384" }, { .val = KMIP_REC_CURVE_K_409, .name = "K409" }, { .val = KMIP_REC_CURVE_B_409, .name = "B409" }, { .val = KMIP_REC_CURVE_P_521, .name = "P521" }, { .val = KMIP_REC_CURVE_K_571, .name = "K571" }, { .val = KMIP_REC_CURVE_B_571, .name = "B571" }, { .val = KMIP_REC_CURVE_SECP112R1, .name = "Secp112r1" }, { .val = KMIP_REC_CURVE_SECP112R2, .name = "Secp112r2" }, { .val = KMIP_REC_CURVE_SECP128R1, .name = "Secp128r1" }, { .val = KMIP_REC_CURVE_SECP128R2, .name = "Secp128r2" }, { .val = KMIP_REC_CURVE_SECP160K1, .name = "Secp160k1" }, { .val = KMIP_REC_CURVE_SECP160R1, .name = "Secp160r1" }, { .val = KMIP_REC_CURVE_SECP160R2, .name = "Secp160r2" }, { .val = KMIP_REC_CURVE_SECP192K1, .name = "Secp192k1" }, { .val = KMIP_REC_CURVE_SECP224K1, .name = "Secp224k1" }, { .val = KMIP_REC_CURVE_SECP256K1, .name = "Secp256k1" }, { .val = KMIP_REC_CURVE_SECT113R1, .name = "Sect113r1" }, { .val = KMIP_REC_CURVE_SECT113R2, .name = "Sect113r2" }, { .val = KMIP_REC_CURVE_SECT131R1, .name = "Sect131r1" }, { .val = KMIP_REC_CURVE_SECT131R2, .name = "Sect131r2" }, { .val = KMIP_REC_CURVE_SECT163R1, .name = "Sect163r1" }, { .val = KMIP_REC_CURVE_SECT193R1, .name = "Sect193r1" }, { .val = KMIP_REC_CURVE_SECT193R2, .name = "Sect193r2" }, { .val = KMIP_REC_CURVE_SECT239K1, .name = "Sect239k1" }, { .val = KMIP_REC_CURVE_ANSIX9P192V2, .name = "Ansix9p192v2" }, { .val = KMIP_REC_CURVE_ANSIX9P192V3, .name = "Ansix9p192v3" }, { .val = KMIP_REC_CURVE_ANSIX9P239V1, .name = "Ansix9p239v1" }, { .val = KMIP_REC_CURVE_ANSIX9P239V2, .name = "Ansix9p239v2" }, { .val = KMIP_REC_CURVE_ANSIX9P239V3, .name = "Ansix9p239v3" }, { .val = KMIP_REC_CURVE_ANSIX9C2PNB163V1, .name = "Ansix9c2pnb163v1" }, { .val = KMIP_REC_CURVE_ANSIX9C2PNB163V2, .name = "Ansix9c2pnb163v2" }, { .val = KMIP_REC_CURVE_ANSIX9C2PNB163V3, .name = "Ansix9c2pnb163v3" }, { .val = KMIP_REC_CURVE_ANSIX9C2PNB176V1, .name = "Ansix9c2pnb176v1" }, { .val = KMIP_REC_CURVE_ANSIX9C2TNB191V1, .name = "Ansix9c2tnb191v1" }, { .val = KMIP_REC_CURVE_ANSIX9C2TNB191V2, .name = "Ansix9c2tnb191v2" }, { .val = KMIP_REC_CURVE_ANSIX9C2TNB191V3, .name = "Ansix9c2tnb191v3" }, { .val = KMIP_REC_CURVE_ANSIX9C2PNB208W1, .name = "Ansix9c2pnb208w1" }, { .val = KMIP_REC_CURVE_ANSIX9C2TNB239V1, .name = "Ansix9c2tnb239v1" }, { .val = KMIP_REC_CURVE_ANSIX9C2TNB239V2, .name = "Ansix9c2tnb239v2" }, { .val = KMIP_REC_CURVE_ANSIX9C2TNB239V3, .name = "Ansix9c2tnb239v3" }, { .val = KMIP_REC_CURVE_ANSIX9C2PNB272W1, .name = "Ansix9c2pnb272w1" }, { .val = KMIP_REC_CURVE_ANSIX9C2PNB304W1, .name = "Ansix9c2pnb304w1" }, { .val = KMIP_REC_CURVE_ANSIX9C2TNB359V1, .name = "Ansix9c2tnb359v1" }, { .val = KMIP_REC_CURVE_ANSIX9C2PNB368W1, .name = "Ansix9c2pnb368w1" }, { .val = KMIP_REC_CURVE_ANSIX9C2TNB431R1, .name = "Ansix9c2tnb431r1" }, { .val = KMIP_REC_CURVE_BRAINPOOLP160R1, .name = "Brainpoolp160r1" }, { .val = KMIP_REC_CURVE_BRAINPOOLP160T1, .name = "Brainpoolp160t1" }, { .val = KMIP_REC_CURVE_BRAINPOOLP192R1, .name = "Brainpoolp192r1" }, { .val = KMIP_REC_CURVE_BRAINPOOLP192T1, .name = "Brainpoolp192t1" }, { .val = KMIP_REC_CURVE_BRAINPOOLP224R1, .name = "Brainpoolp224r1" }, { .val = KMIP_REC_CURVE_BRAINPOOLP224T1, .name = "Brainpoolp224t1" }, { .val = KMIP_REC_CURVE_BRAINPOOLP256R1, .name = "Brainpoolp256r1" }, { .val = KMIP_REC_CURVE_BRAINPOOLP256T1, .name = "Brainpoolp256t1" }, { .val = KMIP_REC_CURVE_BRAINPOOLP320R1, .name = "Brainpoolp320r1" }, { .val = KMIP_REC_CURVE_BRAINPOOLP320T1, .name = "Brainpoolp320t1" }, { .val = KMIP_REC_CURVE_BRAINPOOLP384R1, .name = "Brainpoolp384r1" }, { .val = KMIP_REC_CURVE_BRAINPOOLP384T1, .name = "Brainpoolp384t1" }, { .val = KMIP_REC_CURVE_BRAINPOOLP512R1, .name = "Brainpoolp512r1" }, { .val = KMIP_REC_CURVE_BRAINPOOLP512T1, .name = "Brainpoolp512t1" }, { .val = KMIP_REC_CURVE_CURVE25519, .name = "Curve25519" }, { .val = KMIP_REC_CURVE_CURVE448, .name = "Curve448" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_protection_levels[] = { { .val = KMIP_PROTECTION_LEVEL_HIGH, .name = "High" }, { .val = KMIP_PROTECTION_LEVEL_LOW, .name = "Low" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_key_value_location_types[] = { { .val = KMIP_KEY_VAL_LOC_TYPE_UNINTERPRETED_TEXT_STRING, .name = "UninterpretedTextString" }, { .val = KMIP_KEY_VAL_LOC_TYPE_URI, .name = "URI" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_link_types[] = { { .val = KMIP_LINK_TYPE_CERTIFICATE, .name = "CertificateLink" }, { .val = KMIP_LINK_TYPE_PUBLIC_KEY, .name = "PublicKeyLink" }, { .val = KMIP_LINK_TYPE_PRIVATE_KEY, .name = "PrivateKeyLink" }, { .val = KMIP_LINK_TYPE_DERIVATION_BASE_OBJECT, .name = "DerivationBaseObjectLink" }, { .val = KMIP_LINK_TYPE_DERIVED_KEY, .name = "DerivedKeyLink" }, { .val = KMIP_LINK_TYPE_REPLACEMENT_OBJECT, .name = "ReplacementObjectLink" }, { .val = KMIP_LINK_TYPE_REPLACED_OBJECT, .name = "ReplacedObjectLink" }, { .val = KMIP_LINK_TYPE_PARENT, .name = "ParentLink" }, { .val = KMIP_LINK_TYPE_CHILD, .name = "ChildLink" }, { .val = KMIP_LINK_TYPE_PREVIOUS, .name = "PreviousLink" }, { .val = KMIP_LINK_TYPE_NEXT, .name = "NextLink" }, { .val = KMIP_LINK_TYPE_PKCS_12_CERTIFICATE, .name = "PKCS_12CertificateLink" }, { .val = KMIP_LINK_TYPE_PKCS_12_PASSWORD, .name = "PKCS_12PasswordLink" }, { .val = KMIP_LINK_TYPE_WRAPPING_KEY, .name = "WrappingKeyLink" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_client_registration_methods[] = { { .val = KMIP_CLIENT_REG_METH_UNSPECIFIED, .name = "Unspecified" }, { .val = KMIP_CLIENT_REG_METH_SERVER_PRE_GENERATED, .name = "ServerPreGenerated" }, { .val = KMIP_CLIENT_REG_METH_SERVER_ON_DEMAND, .name = "ServerOnDemand" }, { .val = KMIP_CLIENT_REG_METH_CLIENT_GENERATED, .name = "ClientGenerated" }, { .val = KMIP_CLIENT_REG_METH_CLIENT_REGISTERED, .name = "ClientRegistered" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_rng_algorithms[] = { { .val = KMIP_RNG_ALGO_UNSPECIFIED, .name = "Unspecified" }, { .val = KMIP_RNG_ALGO_FIPS_186_2, .name = "FIPS186_2" }, { .val = KMIP_RNG_ALGO_DRBG, .name = "DRBG" }, { .val = KMIP_RNG_ALGO_NRBG, .name = "NRBG" }, { .val = KMIP_RNG_ALGO_ANSI_X9_31, .name = "ANSIX9_31" }, { .val = KMIP_RNG_ALGO_ANSI_X9_62, .name = "ANSIX9_62" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_drbg_algorithms[] = { { .val = KMIP_DRBG_ALGO_UNSPECIFIED, .name = "Unspecified" }, { .val = KMIP_DRBG_ALGO_DUAL_EC, .name = "Dual_EC" }, { .val = KMIP_DRBG_ALGO_HASH, .name = "Hash" }, { .val = KMIP_DRBG_ALGO_HMAC, .name = "HMAC" }, { .val = KMIP_DRBG_ALGO_CTR, .name = "CTR" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_fips186_variations[] = { { .val = KMIP_FIPS186_VARI_UNSPECIFIED, .name = "Unspecified" }, { .val = KMIP_FIPS186_VARI_GP_X_ORIGINAL, .name = "GPxOriginal" }, { .val = KMIP_FIPS186_VARI_GP_X_CHANGE_NOTICE, .name = "GPxChangeNotice" }, { .val = KMIP_FIPS186_VARI_X_ORIGINAL, .name = "XOriginal" }, { .val = KMIP_FIPS186_VARI_X_CHANGE_NOTICE, .name = "XChangeNotice" }, { .val = KMIP_FIPS186_VARI_K_ORIGINAL, .name = "KOriginal" }, { .val = KMIP_FIPS186_VARI_K_CHANGE_NOTICE, .name = "KChangeNotice" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_validation_authority_types[] = { { .val = KMIP_VALIDATION_AUTH_TYPE_UNSPECIFIED, .name = "Unspecified" }, { .val = KMIP_VALIDATION_AUTH_TYPE_NIST_CMVP, .name = "NISTCMVP" }, { .val = KMIP_VALIDATION_AUTH_TYPE_COMMON_CRITERIA, .name = "CommonCriteria" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_validation_types[] = { { .val = KMIP_VALIDATION_TYPE_UNSPECIFIED, .name = "Unspecified" }, { .val = KMIP_VALIDATION_TYPE_HARDWARE, .name = "Hardware" }, { .val = KMIP_VALIDATION_TYPE_SOFTWARE, .name = "Software" }, { .val = KMIP_VALIDATION_TYPE_FIRMWARE, .name = "Firmware" }, { .val = KMIP_VALIDATION_TYPE_HYBRID, .name = "Hybrid" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_unwrap_modes[] = { { .val = KMIP_UNWRAP_MODE_UNSPECIFIED, .name = "Unspecified" }, { .val = KMIP_UNWRAP_MODE_PROCESSED, .name = "Processed" }, { .val = KMIP_UNWRAP_MODE_NOT_PROCESSED, .name = "NotProcessed" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_destroy_actions[] = { { .val = KMIP_DESTROY_ACTION_UNSPECIFIED, .name = "Unspecified" }, { .val = KMIP_DESTROY_ACTION_KEY_MATERIAL_DELETED, .name = "KeyMaterialDeleted" }, { .val = KMIP_DESTROY_ACTION_KEY_MATERIAL_SHREDDED, .name = "KeyMaterialShredded" }, { .val = KMIP_DESTROY_ACTION_META_DATA_DELETED, .name = "MetaDataDeleted" }, { .val = KMIP_DESTROY_ACTION_META_DATA_SHREDDED, .name = "MetaDataShredded" }, { .val = KMIP_DESTROY_ACTION_DELETED, .name = "Deleted" }, { .val = KMIP_DESTROY_ACTION_SHREDDED, .name = "Shredded" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_shredding_algorithms[] = { { .val = KMIP_SHREDDING_ALGO_UNSPECIFIED, .name = "Unspecified" }, { .val = KMIP_SHREDDING_ALGO_CRYPTOGRAPHIC, .name = "Cryptographic" }, { .val = KMIP_SHREDDING_ALGO_UNSUPPORTED, .name = "Unsupported" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_rng_modes[] = { { .val = KMIP_RNG_MODE_UNSPECIFIED, .name = "Unspecified" }, { .val = KMIP_RNG_MODE_SHARED_INSTANTIATION, .name = "SharedInstantiation" }, { .val = KMIP_RNG_MODE_NON_SHARED_INSTANCIATION, .name = "NonSharedInstantiation" }, { .val = 0, .name = NULL }, }; static const struct kmip_enum kmip_profile_names[] = { { .val = KMIP_PROFILE_BASELINE_SERVER_BASIC_KMIP_V1_2, .name = "BaselineServerBasicKMIPV1_2" }, { .val = KMIP_PROFILE_BASELINE_SERVER_TLS_V1_2_KMIP_V1_2, .name = "BaselineServerTLSV1_2KMIPV1_2" }, { .val = KMIP_PROFILE_BASELINE_CLIENT_BASIC_KMIP_V1_2, .name = "BaselineClientBasicKMIPV1_2" }, { .val = KMIP_PROFILE_BASELINE_CLIENT_TLS_V1_2_KMIP_V1_2, .name = "BaselineClientTLSV1_2KMIPV1_2" }, { .val = KMIP_PROFILE_COMPLETE_SERVER_BASIC_KMIP_V1_2, .name = "CompleteServerBasicKMIPV1_2" }, { .val = KMIP_PROFILE_COMPLETE_SERVER_TLS_V1_2_KMIP_V1_2, .name = "CompleteServerTLSV1_2KMIPV1_2" }, { .val = KMIP_PROFILE_TAPE_LIBRARY_CLIENT_KMIP_V1_0, .name = "TapeLibraryClientKMIPV1_0" }, { .val = KMIP_PROFILE_TAPE_LIBRARY_CLIENT_KMIP_V1_1, .name = "TapeLibraryClientKMIPV1_1" }, { .val = KMIP_PROFILE_TAPE_LIBRARY_CLIENT_KMIP_V1_2, .name = "TapeLibraryClientKMIPV1_2" }, { .val = KMIP_PROFILE_TAPE_LIBRARY_SERVER_KMIP_V1_0, .name = "TapeLibraryServerKMIPV1_0" }, { .val = KMIP_PROFILE_TAPE_LIBRARY_SERVER_KMIP_V1_1, .name = "TapeLibraryServerKMIPV1_1" }, { .val = KMIP_PROFILE_TAPE_LIBRARY_SERVER_KMIP_V1_2, .name = "TapeLibraryServerKMIPV1_2" }, { .val = KMIP_PROFILE_SYMMETRIC_KEY_LIFECYCLE_CLIENT_KMIP_V1_0, .name = "SymmetricKeyLifecycleClientKMIPV1_0" }, { .val = KMIP_PROFILE_SYMMETRIC_KEY_LIFECYCLE_CLIENT_KMIP_V1_1, .name = "SymmetricKeyLifecycleClientKMIPV1_1" }, { .val = KMIP_PROFILE_SYMMETRIC_KEY_LIFECYCLE_CLIENT_KMIP_V1_2, .name = "SymmetricKeyLifecycleClientKMIPV1_2" }, { .val = KMIP_PROFILE_SYMMETRIC_KEY_LIFECYCLE_SERVER_KMIP_V1_0, .name = "SymmetricKeyLifecycleServerKMIPV1_0" }, { .val = KMIP_PROFILE_SYMMETRIC_KEY_LIFECYCLE_SERVER_KMIP_V1_1, .name = "SymmetricKeyLifecycleServerKMIPV1_1" }, { .val = KMIP_PROFILE_SYMMETRIC_KEY_LIFECYCLE_SERVER_KMIP_V1_2, .name = "SymmetricKeyLifecycleServerKMIPV1_2" }, { .val = KMIP_PROFILE_ASYMMETRIC_KEY_LIFECYCLE_CLIENT_KMIP_V1_0, .name = "AsymmetricKeyLifecycleClientKMIPV1_0" }, { .val = KMIP_PROFILE_ASYMMETRIC_KEY_LIFECYCLE_CLIENT_KMIP_V1_1, .name = "AsymmetricKeyLifecycleClientKMIPV1_1" }, { .val = KMIP_PROFILE_ASYMMETRIC_KEY_LIFECYCLE_CLIENT_KMIP_V1_2, .name = "AsymmetricKeyLifecycleClientKMIPV1_2" }, { .val = KMIP_PROFILE_ASYMMETRIC_KEY_LIFECYCLE_SERVER_KMIP_V1_0, .name = "AsymmetricKeyLifecycleServerKMIPV1_0" }, { .val = KMIP_PROFILE_ASYMMETRIC_KEY_LIFECYCLE_SERVER_KMIP_V1_1, .name = "AsymmetricKeyLifecycleServerKMIPV1_1" }, { .val = KMIP_PROFILE_ASYMMETRIC_KEY_LIFECYCLE_SERVER_KMIP_V1_2, .name = "AsymmetricKeyLifecycleServerKMIPV1_2" }, { .val = KMIP_PROFILE_BASIC_CRYPTOGRAPHIC_CLIENT_KMIP_V1_2, .name = "BasicCryptographicClientKMIPV1_2" }, { .val = KMIP_PROFILE_BASIC_CRYPTOGRAPHIC_SERVER_KMIP_V1_2, .name = "BasicCryptographicServerKMIPV1_2" }, { .val = KMIP_PROFILE_ADVANCED_CRYPTOGRAPHIC_CLIENT_KMIP_V1_2, .name = "AdvancedCryptographicClientKMIPV1_2" }, { .val = KMIP_PROFILE_ADVANCED_CRYPTOGRAPHIC_SERVER_KMIP_V1_2, .name = "AdvancedCryptographicServerKMIPV1_2" }, { .val = KMIP_PROFILE_RNG_CRYPTOGRAPHIC_CLIENT_KMIP_V1_2, .name = "RNGCryptographicClientKMIPV1_2" }, { .val = KMIP_PROFILE_RNG_CRYPTOGRAPHIC_SERVER_KMIP_V1_2, .name = "RNGCryptographicServerKMIPV1_2" }, { .val = KMIP_PROFILE_BASIC_SYMMETRIC_KEY_FOUNDRY_CLIENT_KMIP_V1_0, .name = "BasicSymmetricKeyFoundryClientKMIPV1_0" }, { .val = KMIP_PROFILE_INTERMEDIATE_SYMMETRIC_KEY_FOUNDRY_CLIENT_KMIP_V1_0, .name = "IntermediateSymmetricKeyFoundryClientKMIPV1_0" }, { .val = KMIP_PROFILE_ADVANCED_SYMMETRIC_KEY_FOUNDRY_CLIENT_KMIP_V1_0, .name = "AdvancedSymmetricKeyFoundryClientKMIPV1_0" }, { .val = KMIP_PROFILE_BASIC_SYMMETRIC_KEY_FOUNDRY_CLIENT_KMIP_V1_1, .name = "BasicSymmetricKeyFoundryClientKMIPV1_1" }, { .val = KMIP_PROFILE_INTERMEDIATE_SYMMETRIC_KEY_FOUNDRY_CLIENT_KMIP_V1_1, .name = "IntermediateSymmetricKeyFoundryClientKMIPV1_1" }, { .val = KMIP_PROFILE_ADVANCED_SYMMETRIC_KEY_FOUNDRY_CLIENT_KMIP_V1_1, .name = "AdvancedSymmetricKeyFoundryClientKMIPV1_1" }, { .val = KMIP_PROFILE_BASIC_SYMMETRIC_KEY_FOUNDRY_CLIENT_KMIP_V1_2, .name = "BasicSymmetricKeyFoundryClientKMIPV1_2" }, { .val = KMIP_PROFILE_INTERMEDIATE_SYMMETRIC_KEY_FOUNDRY_CLIENT_KMIP_V1_2, .name = "IntermediateSymmetricKeyFoundryClientKMIPV1_2" }, { .val = KMIP_PROFILE_ADVANCED_SYMMETRIC_KEY_FOUNDRY_CLIENT_KMIP_V1_2, .name = "AdvancedSymmetricKeyFoundryClientKMIPV1_2" }, { .val = KMIP_PROFILE_SYMMETRIC_KEY_FOUNDRY_SERVER_KMIP_V1_0, .name = "SymmetricKeyFoundryServerKMIPV1_0" }, { .val = KMIP_PROFILE_SYMMETRIC_KEY_FOUNDRY_SERVER_KMIP_V1_1, .name = "SymmetricKeyFoundryServerKMIPV1_1" }, { .val = KMIP_PROFILE_SYMMETRIC_KEY_FOUNDRY_SERVER_KMIP_V1_2, .name = "SymmetricKeyFoundryServerKMIPV1_2" }, { .val = KMIP_PROFILE_OPAQUE_MANAGED_OBJECT_STORE_CLIENT_KMIP_V1_0, .name = "OpaqueManagedObjectStoreClientKMIPV1_0" }, { .val = KMIP_PROFILE_OPAQUE_MANAGED_OBJECT_STORE_CLIENT_KMIP_V1_1, .name = "OpaqueManagedObjectStoreClientKMIPV1_1" }, { .val = KMIP_PROFILE_OPAQUE_MANAGED_OBJECT_STORE_CLIENT_KMIP_V1_2, .name = "OpaqueManagedObjectStoreClientKMIPV1_2" }, { .val = KMIP_PROFILE_OPAQUE_MANAGED_OBJECT_STORE_SERVER_KMIP_V1_0, .name = "OpaqueManagedObjectStoreServerKMIPV1_0" }, { .val = KMIP_PROFILE_OPAQUE_MANAGED_OBJECT_STORE_SERVER_KMIP_V1_1, .name = "OpaqueManagedObjectStoreServerKMIPV1_1" }, { .val = KMIP_PROFILE_OPAQUE_MANAGED_OBJECT_STORE_SERVER_KMIP_V1_2, .name = "OpaqueManagedObjectStoreServerKMIPV1_2" }, { .val = KMIP_PROFILE_SUITE_B_MINLOS_128_CLIENT_KMIP_V1_0, .name = "SuiteBMinLOS_128ClientKMIPV1_0" }, { .val = KMIP_PROFILE_SUITE_B_MINLOS_128_CLIENT_KMIP_V1_1, .name = "SuiteBMinLOS_128ClientKMIPV1_1" }, { .val = KMIP_PROFILE_SUITE_B_MINLOS_128_CLIENT_KMIP_V1_2, .name = "SuiteBMinLOS_128ClientKMIPV1_2" }, { .val = KMIP_PROFILE_SUITE_B_MINLOS_128_SERVER_KMIP_V1_0, .name = "SuiteBMinLOS_128ServerKMIPV1_0" }, { .val = KMIP_PROFILE_SUITE_B_MINLOS_128_SERVER_KMIP_V1_1, .name = "SuiteBMinLOS_128ServerKMIPV1_1" }, { .val = KMIP_PROFILE_SUITE_B_MINLOS_128_SERVER_KMIP_V1_2, .name = "SuiteBMinLOS_128ServerKMIPV1_2" }, { .val = KMIP_PROFILE_SUITE_B_MINLOS_192_CLIENT_KMIP_V1_0, .name = "SuiteBMinLOS_192ClientKMIPV1_0" }, { .val = KMIP_PROFILE_SUITE_B_MINLOS_192_CLIENT_KMIP_V1_1, .name = "SuiteBMinLOS_192ClientKMIPV1_1" }, { .val = KMIP_PROFILE_SUITE_B_MINLOS_192_CLIENT_KMIP_V1_2, .name = "SuiteBMinLOS_192ClientKMIPV1_2" }, { .val = KMIP_PROFILE_SUITE_B_MINLOS_192_SERVER_KMIP_V1_0, .name = "SuiteBMinLOS_192ServerKMIPV1_0" }, { .val = KMIP_PROFILE_SUITE_B_MINLOS_192_SERVER_KMIP_V1_1, .name = "SuiteBMinLOS_192ServerKMIPV1_1" }, { .val = KMIP_PROFILE_SUITE_B_MINLOS_192_SERVER_KMIP_V1_2, .name = "SuiteBMinLOS_192ServerKMIPV1_2" }, { .val = KMIP_PROFILE_STORAGE_ARRAY_WITH_SELF_ENCRYPTING_DRIVE_CLIENT_KMIP_V1_0, .name = "StorageArrayWithSelfEncryptingDriveClientKMIPV1_0" }, { .val = KMIP_PROFILE_STORAGE_ARRAY_WITH_SELF_ENCRYPTING_DRIVE_CLIENT_KMIP_V1_1, .name = "StorageArrayWithSelfEncryptingDriveClientKMIPV1_1" }, { .val = KMIP_PROFILE_STORAGE_ARRAY_WITH_SELF_ENCRYPTING_DRIVE_CLIENT_KMIP_V1_2, .name = "StorageArrayWithSelfEncryptingDriveClientKMIPV1_2" }, { .val = KMIP_PROFILE_STORAGE_ARRAY_WITH_SELF_ENCRYPTING_DRIVE_SERVER_KMIP_V1_0, .name = "StorageArrayWithSelfEncryptingDriveServerKMIPV1_0" }, { .val = KMIP_PROFILE_STORAGE_ARRAY_WITH_SELF_ENCRYPTING_DRIVE_SERVER_KMIP_V1_1, .name = "StorageArrayWithSelfEncryptingDriveServerKMIPV1_1" }, { .val = KMIP_PROFILE_STORAGE_ARRAY_WITH_SELF_ENCRYPTING_DRIVE_SERVER_KMIP_V1_2, .name = "StorageArrayWithSelfEncryptingDriveServerKMIPV1_2" }, { .val = KMIP_PROFILE_HTTPS_CLIENT_KMIP_V1_0, .name = "HTTPSClientKMIPV1_0" }, { .val = KMIP_PROFILE_HTTPS_CLIENT_KMIP_V1_1, .name = "HTTPSClientKMIPV1_1" }, { .val = KMIP_PROFILE_HTTPS_CLIENT_KMIP_V1_2, .name = "HTTPSClientKMIPV1_2" }, { .val = KMIP_PROFILE_HTTPS_SERVER_KMIP_V1_0, .name = "HTTPSServerKMIPV1_0" }, { .val = KMIP_PROFILE_HTTPS_SERVER_KMIP_V1_1, .name = "HTTPSServerKMIPV1_1" }, { .val = KMIP_PROFILE_HTTPS_SERVER_KMIP_V1_2, .name = "HTTPSServerKMIPV1_2" }, { .val = KMIP_PROFILE_JSON_CLIENT_KMIP_V1_0, .name = "JSONClientKMIPV1_0" }, { .val = KMIP_PROFILE_JSON_CLIENT_KMIP_V1_1, .name = "JSONClientKMIPV1_1" }, { .val = KMIP_PROFILE_JSON_CLIENT_KMIP_V1_2, .name = "JSONClientKMIPV1_2" }, { .val = KMIP_PROFILE_JSON_SERVER_KMIP_V1_0, .name = "JSONServerKMIPV1_0" }, { .val = KMIP_PROFILE_JSON_SERVER_KMIP_V1_1, .name = "JSONServerKMIPV1_1" }, { .val = KMIP_PROFILE_JSON_SERVER_KMIP_V1_2, .name = "JSONServerKMIPV1_2" }, { .val = KMIP_PROFILE_XML_CLIENT_KMIP_V1_0, .name = "XMLClientKMIPV1_0" }, { .val = KMIP_PROFILE_XML_CLIENT_KMIP_V1_1, .name = "XMLClientKMIPV1_1" }, { .val = KMIP_PROFILE_XML_CLIENT_KMIP_V1_2, .name = "XMLClientKMIPV1_2" }, { .val = KMIP_PROFILE_XML_SERVER_KMIP_V1_0, .name = "XMLServerKMIPV1_0" }, { .val = KMIP_PROFILE_XML_SERVER_KMIP_V1_1, .name = "XMLServerKMIPV1_1" }, { .val = KMIP_PROFILE_XML_SERVER_KMIP_V1_2, .name = "XMLServerKMIPV1_2" }, { .val = KMIP_PROFILE_BASELINE_SERVER_BASIC_KMIP_V1_3, .name = "BaselineServerBasicKMIPV1_3" }, { .val = KMIP_PROFILE_BASELINE_SERVER_TLS_V1_2_KMIP_V1_3, .name = "BaselineServerTLSV1_2KMIPV1_3" }, { .val = KMIP_PROFILE_BASELINE_CLIENT_BASIC_KMIP_V1_3, .name = "BaselineClientBasicKMIPV1_3" }, { .val = KMIP_PROFILE_BASELINE_CLIENT_TLS_V1_2_KMIP_V1_3, .name = "BaselineClientTLSV1_2KMIPV1_3" }, { .val = KMIP_PROFILE_COMPLETE_SERVER_BASIC_KMIP_V1_3, .name = "CompleteServerBasicKMIPV1_3" }, { .val = KMIP_PROFILE_COMPLETE_SERVER_TLS_V1_2_KMIP_V1_3, .name = "CompleteServerTLSV1_2KMIPV1_3" }, { .val = KMIP_PROFILE_TAPE_LIBRARY_CLIENT_KMIP_V1_3, .name = "TapeLibraryClientKMIPV1_3" }, { .val = KMIP_PROFILE_TAPE_LIBRARY_SERVER_KMIP_V1_3, .name = "TapeLibraryServerKMIPV1_3" }, { .val = KMIP_PROFILE_SYMMETRIC_KEY_LIFECYCLE_CLIENT_KMIP_V1_3, .name = "SymmetricKeyLifecycleClientKMIPV1_3" }, { .val = KMIP_PROFILE_SYMMETRIC_KEY_LIFECYCLE_SERVER_KMIP_V1_3, .name = "SymmetricKeyLifecycleServerKMIPV1_3" }, { .val = KMIP_PROFILE_ASYMMETRIC_KEY_LIFECYCLE_CLIENT_KMIP_V1_3, .name = "AsymmetricKeyLifecycleClientKMIPV1_3" }, { .val = KMIP_PROFILE_ASYMMETRIC_KEY_LIFECYCLE_SERVER_KMIP_V1_3, .name = "AsymmetricKeyLifecycleServerKMIPV1_3" }, { .val = KMIP_PROFILE_BASIC_CRYPTOGRAPHIC_CLIENT_KMIP_V1_3, .name = "BasicCryptographicClientKMIPV1_3" }, { .val = KMIP_PROFILE_BASIC_CRYPTOGRAPHIC_SERVER_KMIP_V1_3, .name = "BasicCryptographicServerKMIPV1_3" }, { .val = KMIP_PROFILE_ADVANCED_CRYPTOGRAPHIC_CLIENT_KMIP_V1_3, .name = "AdvancedCryptographicClientKMIPV1_3" }, { .val = KMIP_PROFILE_ADVANCED_CRYPTOGRAPHIC_SERVER_KMIP_V1_3, .name = "AdvancedCryptographicServerKMIPV1_3" }, { .val = KMIP_PROFILE_RNG_CRYPTOGRAPHIC_CLIENT_KMIP_V1_3, .name = "RNGCryptographicClientKMIPV1_3" }, { .val = KMIP_PROFILE_RNG_CRYPTOGRAPHIC_SERVER_KMIP_V1_3, .name = "RNGCryptographicServerKMIPV1_3" }, { .val = KMIP_PROFILE_BASIC_SYMMETRIC_KEY_FOUNDRY_CLIENT_KMIP_V1_3, .name = "BasicSymmetricKeyFoundryClientKMIPV1_3" }, { .val = KMIP_PROFILE_INTERMEDIATE_SYMMETRIC_KEY_FOUNDRY_CLIENT_KMIP_V1_3, .name = "IntermediateSymmetricKeyFoundryClientKMIPV1_3" }, { .val = KMIP_PROFILE_ADVANCED_SYMMETRIC_KEY_FOUNDRY_CLIENT_KMIP_V1_3, .name = "AdvancedSymmetricKeyFoundryClientKMIPV1_3" }, { .val = KMIP_PROFILE_SYMMETRIC_KEY_FOUNDRY_SERVER_KMIP_V1_3, .name = "SymmetricKeyFoundryServerKMIPV1_3" }, { .val = KMIP_PROFILE_OPAQUE_MANAGED_OBJECT_STORE_CLIENT_KMIP_V1_3, .name = "OpaqueManagedObjectStoreClientKMIPV1_3" }, { .val = KMIP_PROFILE_OPAQUE_MANAGED_OBJECT_STORE_SERVER_KMIP_V1_3, .name = "OpaqueManagedObjectStoreServerKMIPV1_3" }, { .val = KMIP_PROFILE_SUITE_B_MINLOS_128_CLIENT_KMIP_V1_3, .name = "SuiteBMinLOS_128ClientKMIPV1_3" }, { .val = KMIP_PROFILE_SUITE_B_MINLOS_128_SERVER_KMIP_V1_3, .name = "SuiteBMinLOS_128ServerKMIPV1_3" }, { .val = KMIP_PROFILE_SUITE_B_MINLOS_192_CLIENT_KMIP_V1_3, .name = "SuiteBMinLOS_192ClientKMIPV1_3" }, { .val = KMIP_PROFILE_SUITE_B_MINLOS_192_SERVER_KMIP_V1_3, .name = "SuiteBMinLOS_192ServerKMIPV1_3" }, { .val = KMIP_PROFILE_STORAGE_ARRAY_WITH_SELF_ENCRYPTING_DRIVE_CLIENT_KMIP_V1_3, .name = "StorageArrayWithSelfEncryptingDriveClientKMIPV1_3" }, { .val = KMIP_PROFILE_STORAGE_ARRAY_WITH_SELF_ENCRYPTING_DRIVE_SERVER_KMIP_V1_3, .name = "StorageArrayWithSelfEncryptingDriveServerKMIPV1_3" }, { .val = KMIP_PROFILE_HTTPS_CLIENT_KMIP_V1_3, .name = "HTTPSClientKMIPV1_3" }, { .val = KMIP_PROFILE_HTTPS_SERVER_KMIP_V1_3, .name = "HTTPSServerKMIPV1_3" }, { .val = KMIP_PROFILE_JSON_CLIENT_KMIP_V1_3, .name = "JSONClientKMIPV1_3" }, { .val = KMIP_PROFILE_JSON_SERVER_KMIP_V1_3, .name = "JSONServerKMIPV1_3" }, { .val = KMIP_PROFILE_XML_CLIENT_KMIP_V1_3, .name = "XMLClientKMIPV1_3" }, { .val = KMIP_PROFILE_XML_SERVER_KMIP_V1_3, .name = "XMLServerKMIPV1_3" }, { .val = KMIP_PROFILE_BASELINE_SERVER_BASIC_KMIP_V1_4, .name = "BaselineServerBasicKMIPV1_4" }, { .val = KMIP_PROFILE_BASELINE_SERVER_TLS_V1_2_KMIP_V1_4, .name = "BaselineServerTLSV1_2KMIPV1_4" }, { .val = KMIP_PROFILE_BASELINE_CLIENT_BASIC_KMIP_V1_4, .name = "BaselineClientBasicKMIPV1_4" }, { .val = KMIP_PROFILE_BASELINE_CLIENT_TLS_V1_2_KMIP_V1_4, .name = "BaselineClientTLSV1_2KMIPV1_4" }, { .val = KMIP_PROFILE_COMPLETE_SERVER_BASIC_KMIP_V1_4, .name = "CompleteServerBasicKMIPV1_4" }, { .val = KMIP_PROFILE_COMPLETE_SERVER_TLS_V1_2_KMIP_V1_4, .name = "CompleteServerTLSV1_2KMIPV1_4" }, { .val = KMIP_PROFILE_TAPE_LIBRARY_CLIENT_KMIP_V1_4, .name = "TapeLibraryClientKMIPV1_4" }, { .val = KMIP_PROFILE_TAPE_LIBRARY_SERVER_KMIP_V1_4, .name = "TapeLibraryServerKMIPV1_4" }, { .val = KMIP_PROFILE_SYMMETRIC_KEY_LIFECYCLE_CLIENT_KMIP_V1_4, .name = "SymmetricKeyLifecycleClientKMIPV1_4" }, { .val = KMIP_PROFILE_SYMMETRIC_KEY_LIFECYCLE_SERVER_KMIP_V1_4, .name = "SymmetricKeyLifecycleServerKMIPV1_4" }, { .val = KMIP_PROFILE_ASYMMETRIC_KEY_LIFECYCLE_CLIENT_KMIP_V1_4, .name = "AsymmetricKeyLifecycleClientKMIPV1_4" }, { .val = KMIP_PROFILE_ASYMMETRIC_KEY_LIFECYCLE_SERVER_KMIP_V1_4, .name = "AsymmetricKeyLifecycleServerKMIPV1_4" }, { .val = KMIP_PROFILE_BASIC_CRYPTOGRAPHIC_CLIENT_KMIP_V1_4, .name = "BasicCryptographicClientKMIPV1_4" }, { .val = KMIP_PROFILE_BASIC_CRYPTOGRAPHIC_SERVER_KMIP_V1_4, .name = "BasicCryptographicServerKMIPV1_4" }, { .val = KMIP_PROFILE_ADVANCED_CRYPTOGRAPHIC_CLIENT_KMIP_V1_4, .name = "AdvancedCryptographicClientKMIPV1_4" }, { .val = KMIP_PROFILE_ADVANCED_CRYPTOGRAPHIC_SERVER_KMIP_V1_4, .name = "AdvancedCryptographicServerKMIPV1_4" }, { .val = KMIP_PROFILE_RNG_CRYPTOGRAPHIC_CLIENT_KMIP_V1_4, .name = "RNGCryptographicClientKMIPV1_4" }, { .val = KMIP_PROFILE_RNG_CRYPTOGRAPHIC_SERVER_KMIP_V1_4, .name = "RNGCryptographicServerKMIPV1_4" }, { .val = KMIP_PROFILE_BASIC_SYMMETRIC_KEY_FOUNDRY_CLIENT_KMIP_V1_4, .name = "BasicSymmetricKeyFoundryClientKMIPV1_4" }, { .val = KMIP_PROFILE_INTERMEDIATE_SYMMETRIC_KEY_FOUNDRY_CLIENT_KMIP_V1_4, .name = "IntermediateSymmetricKeyFoundryClientKMIPV1_4" }, { .val = KMIP_PROFILE_ADVANCED_SYMMETRIC_KEY_FOUNDRY_CLIENT_KMIP_V1_4, .name = "AdvancedSymmetricKeyFoundryClientKMIPV1_4" }, { .val = KMIP_PROFILE_SYMMETRIC_KEY_FOUNDRY_SERVER_KMIP_V1_4, .name = "SymmetricKeyFoundryServerKMIPV1_4" }, { .val = KMIP_PROFILE_OPAQUE_MANAGED_OBJECT_STORE_CLIENT_KMIP_V1_4, .name = "OpaqueManagedObjectStoreClientKMIPV1_4" }, { .val = KMIP_PROFILE_OPAQUE_MANAGED_OBJECT_STORE_SERVER_KMIP_V1_4, .name = "OpaqueManagedObjectStoreServerKMIPV1_4" }, { .val = KMIP_PROFILE_SUITE_B_MINLOS_128_CLIENT_KMIP_V1_4, .name = "SuiteBMinLOS_128ClientKMIPV1_4" }, { .val = KMIP_PROFILE_SUITE_B_MINLOS_128_SERVER_KMIP_V1_4, .name = "SuiteBMinLOS_128ServerKMIPV1_4" }, { .val = KMIP_PROFILE_SUITE_B_MINLOS_192_CLIENT_KMIP_V1_4, .name = "SuiteBMinLOS_192ClientKMIPV1_4" }, { .val = KMIP_PROFILE_SUITE_B_MINLOS_192_SERVER_KMIP_V1_4, .name = "SuiteBMinLOS_192ServerKMIPV1_4" }, { .val = KMIP_PROFILE_STORAGE_ARRAY_WITH_SELF_ENCRYPTING_DRIVE_CLIENT_KMIP_V1_4, .name = "StorageArrayWithSelfEncryptingDriveClientKMIPV1_4" }, { .val = KMIP_PROFILE_STORAGE_ARRAY_WITH_SELF_ENCRYPTING_DRIVE_SERVER_KMIP_V1_4, .name = "StorageArrayWithSelfEncryptingDriveServerKMIPV1_4" }, { .val = KMIP_PROFILE_HTTPS_CLIENT_KMIP_V1_4, .name = "HTTPSClientKMIPV1_4" }, { .val = KMIP_PROFILE_HTTPS_SERVER_KMIP_V1_4, .name = "HTTPSServerKMIPV1_4" }, { .val = KMIP_PROFILE_JSON_CLIENT_KMIP_V1_4, .name = "JSONClientKMIPV1_4" }, { .val = KMIP_PROFILE_JSON_SERVER_KMIP_V1_4, .name = "JSONServerKMIPV1_4" }, { .val = KMIP_PROFILE_XML_CLIENT_KMIP_V1_4, .name = "XMLClientKMIPV1_4" }, { .val = KMIP_PROFILE_XML_SERVER_KMIP_V1_4, .name = "XMLServerKMIPV1_4" }, { .val = KMIP_PROFILE_COMPLETE_SERVER_BASIC, .name = "CompleteServerBasic" }, { .val = KMIP_PROFILE_COMPLETE_SERVER_TLS_V1_2, .name = "CompleteServerTLSV1_2" }, { .val = KMIP_PROFILE_TAPE_LIBRARY_CLIENT, .name = "TapeLibraryClient" }, { .val = KMIP_PROFILE_TAPE_LIBRARY_SERVER, .name = "TapeLibraryServer" }, { .val = KMIP_PROFILE_SYMMETRIC_KEY_LIFECYCLE_CLIENT, .name = "SymmetricKeyLifecycleClient" }, { .val = KMIP_PROFILE_SYMMETRIC_KEY_LIFECYCLE_SERVER, .name = "SymmetricKeyLifecycleServer" }, { .val = KMIP_PROFILE_ASYMMETRIC_KEY_LIFECYCLE_CLIENT, .name = "AsymmetricKeyLifecycleClient" }, { .val = KMIP_PROFILE_ASYMMETRIC_KEY_LIFECYCLE_SERVER, .name = "AsymmetricKeyLifecycleServer" }, { .val = KMIP_PROFILE_BASIC_CRYPTOGRAPHIC_CLIENT, .name = "BasicCryptographicClient" }, { .val = KMIP_PROFILE_BASIC_CRYPTOGRAPHIC_SERVER, .name = "BasicCryptographicServer" }, { .val = KMIP_PROFILE_ADVANCED_CRYPTOGRAPHIC_CLIENT, .name = "AdvancedCryptographicClient" }, { .val = KMIP_PROFILE_ADVANCED_CRYPTOGRAPHIC_SERVER, .name = "AdvancedCryptographicServer" }, { .val = KMIP_PROFILE_RNG_CRYPTOGRAPHIC_CLIENT, .name = "RNGCryptographicClient" }, { .val = KMIP_PROFILE_RNG_CRYPTOGRAPHIC_SERVER, .name = "RNGCryptographicServer" }, { .val = KMIP_PROFILE_BASIC_SYMMETRIC_KEY_FOUNDRY_CLIENT, .name = "BasicSymmetricKeyFoundryClient" }, { .val = KMIP_PROFILE_INTERMEDIATE_SYMMETRIC_KEY_FOUNDRY_CLIENT, .name = "IntermediateSymmetricKeyFoundryClient" }, { .val = KMIP_PROFILE_ADVANCED_SYMMETRIC_KEY_FOUNDRY_CLIENT, .name = "AdvancedSymmetricKeyFoundryClient" }, { .val = KMIP_PROFILE_SYMMETRIC_KEY_FOUNDRY_SERVER, .name = "SymmetricKeyFoundryServer" }, { .val = KMIP_PROFILE_OPAQUE_MANAGED_OBJECT_STORE_CLIENT, .name = "OpaqueManagedObjectStoreClient" }, { .val = KMIP_PROFILE_OPAQUE_MANAGED_OBJECT_STORE_SERVER, .name = "OpaqueManagedObjectStoreServer" }, { .val = KMIP_PROFILE_STORAGE_ARRAY_WITH_SELF_ENCRYPTING_DRIVE_CLIENT, .name = "StorageArrayWithSelfEncryptingDriveClient" }, { .val = KMIP_PROFILE_STORAGE_ARRAY_WITH_SELF_ENCRYPTING_DRIVE_SERVER, .name = "StorageArrayWithSelfEncryptingDriveServer" }, { .val = KMIP_PROFILE_HTTPS_CLIENT, .name = "HTTPSClient" }, { .val = KMIP_PROFILE_HTTPS_SERVER, .name = "HTTPSServer" }, { .val = KMIP_PROFILE_JSON_CLIENT, .name = "JSONClient" }, { .val = KMIP_PROFILE_JSON_SERVER, .name = "JSONServer" }, { .val = KMIP_PROFILE_XML_CLIENT, .name = "XMLClient" }, { .val = KMIP_PROFILE_XML_SERVER, .name = "XMLServer" }, { .val = KMIP_PROFILE_AES_XTS_CLIENT, .name = "AESXTSClient" }, { .val = KMIP_PROFILE_AES_XTS_SERVER, .name = "AESXTSServer" }, { .val = KMIP_PROFILE_QUANTUM_SAFE_CLIENT, .name = "QuantumSafeClient" }, { .val = KMIP_PROFILE_QUANTUM_SAFE_SERVER, .name = "QuantumSafeServer" }, { .val = KMIP_PROFILE_PKCS_11_CLIENT, .name = "PKCS_11Client" }, { .val = KMIP_PROFILE_PKCS_11_SERVER, .name = "PKCS_11Server" }, { .val = KMIP_PROFILE_BASELINE_CLIENT, .name = "BaselineClient" }, { .val = KMIP_PROFILE_BASELINE_SERVER, .name = "BaselineServer" }, { .val = KMIP_PROFILE_COMPLETE_SERVER, .name = "CompleteServer" }, { .val = 0, .name = NULL }, }; struct kmip_enum_info { enum kmip_tag tag; const struct kmip_enum *enum_info; bool is_mask; }; static const struct kmip_enum_info enum_info[] = { { .tag = KMIP_TAG_ATTRIBUTE_REFERENCE, .enum_info = kmip_tags, .is_mask = false }, { .tag = KMIP_TAG_OPERATION, .enum_info = kmip_operations, .is_mask = false }, { .tag = KMIP_TAG_BATCH_ERROR_CONTINUATION_OPTION, .enum_info = kmip_batch_error_cont_options, .is_mask = false }, { .tag = KMIP_TAG_CRYPTOGRAPHIC_USAGE_MASK, .enum_info = kmip_crypto_usage_masks, .is_mask = true }, { .tag = KMIP_TAG_RESULT_STATUS, .enum_info = kmip_result_statuses, .is_mask = false }, { .tag = KMIP_TAG_RESULT_REASON, .enum_info = kmip_result_reasons, .is_mask = false }, { .tag = KMIP_TAG_QUERY_FUNCTION, .enum_info = kmip_query_functions, .is_mask = false }, { .tag = KMIP_TAG_NAME_TYPE, .enum_info = kmip_name_types, .is_mask = false }, { .tag = KMIP_TAG_ALTERNATE_NAME_TYPE, .enum_info = kmip_alternate_name_types, .is_mask = false }, { .tag = KMIP_TAG_UNIQUE_IDENTIFIER, .enum_info = kmip_unique_identifiers, .is_mask = false }, { .tag = KMIP_TAG_OBJECT_TYPE, .enum_info = kmip_object_types, .is_mask = false }, { .tag = KMIP_TAG_CRYPTOGRAPHIC_ALGORITHM, .enum_info = kmip_crypto_algos, .is_mask = false }, { .tag = KMIP_TAG_CERTIFICATE_TYPE, .enum_info = kmip_certificate_types, .is_mask = false }, { .tag = KMIP_TAG_STATE, .enum_info = kmip_states, .is_mask = false }, { .tag = KMIP_TAG_PROTECTION_STORAGE_MASK, .enum_info = kmip_protection_storage_masks, .is_mask = true }, { .tag = KMIP_TAG_REVOCATION_REASON_CODE, .enum_info = kmip_revoke_reasons, .is_mask = false }, { .tag = KMIP_TAG_OBJECT_GROUP_MEMBER, .enum_info = kmip_object_group_members, .is_mask = false }, { .tag = KMIP_TAG_STORAGE_STATUS_MASK, .enum_info = kmip_storage_status_masks, .is_mask = true }, { .tag = KMIP_TAG_KEY_FORMAT_TYPE, .enum_info = kmip_key_format_types, .is_mask = false }, { .tag = KMIP_TAG_KEY_COMPRESSION_TYPE, .enum_info = kmip_key_compression_types, .is_mask = false }, { .tag = KMIP_TAG_WRAPPING_METHOD, .enum_info = kmip_wrapping_methods, .is_mask = false }, { .tag = KMIP_TAG_KEY_WRAP_TYPE, .enum_info = kmip_key_wrap_types, .is_mask = false }, { .tag = KMIP_TAG_BLOCK_CIPHER_MODE, .enum_info = kmip_block_cipher_modes, .is_mask = false }, { .tag = KMIP_TAG_PADDING_METHOD, .enum_info = kmip_padding_methods, .is_mask = false }, { .tag = KMIP_TAG_HASHING_ALGORITHM, .enum_info = kmip_hashing_algos, .is_mask = false }, { .tag = KMIP_TAG_MASK_GENERATOR_HASHING_ALGORITHM, .enum_info = kmip_hashing_algos, .is_mask = false }, { .tag = KMIP_TAG_KEY_ROLE_TYPE, .enum_info = kmip_key_role_types, .is_mask = false }, { .tag = KMIP_TAG_DIGITAL_SIGNATURE_ALGORITHM, .enum_info = kmip_sinature_algos, .is_mask = false }, { .tag = KMIP_TAG_MASK_GENERATOR, .enum_info = kmip_mask_generators, .is_mask = false }, { .tag = KMIP_TAG_ENCODING_OPTION, .enum_info = kmip_encoding_options, .is_mask = false }, { .tag = KMIP_TAG_RECOMMENDED_CURVE, .enum_info = kmip_recommended_cuurves, .is_mask = false }, { .tag = KMIP_TAG_PROTECTION_LEVEL, .enum_info = kmip_protection_levels, .is_mask = false }, { .tag = KMIP_TAG_KEY_VALUE_LOCATION_TYPE, .enum_info = kmip_key_value_location_types, .is_mask = false }, { .tag = KMIP_TAG_LINK_TYPE, .enum_info = kmip_link_types, .is_mask = false }, { .tag = KMIP_TAG_CLIENT_REGISTRATION_METHOD, .enum_info = kmip_client_registration_methods, .is_mask = false }, { .tag = KMIP_TAG_RNG_ALGORITHM, .enum_info = kmip_rng_algorithms, .is_mask = false }, { .tag = KMIP_TAG_DRBG_ALGORITHM, .enum_info = kmip_drbg_algorithms, .is_mask = false }, { .tag = KMIP_TAG_FIPS186_VARIANT, .enum_info = kmip_fips186_variations, .is_mask = false }, { .tag = KMIP_TAG_VALIDATION_AUTHORITY_TYPE, .enum_info = kmip_validation_authority_types, .is_mask = false }, { .tag = KMIP_TAG_VALIDATION_TYPE, .enum_info = kmip_validation_types, .is_mask = false }, { .tag = KMIP_TAG_UNWRAP_MODE, .enum_info = kmip_unwrap_modes, .is_mask = false }, { .tag = KMIP_TAG_DESTROY_ACTION, .enum_info = kmip_destroy_actions, .is_mask = false }, { .tag = KMIP_TAG_SHREDDING_ALGORITHM, .enum_info = kmip_shredding_algorithms, .is_mask = false }, { .tag = KMIP_TAG_RNG_MODE, .enum_info = kmip_rng_modes, .is_mask = false }, { .tag = KMIP_TAG_PROFILE_NAME, .enum_info = kmip_profile_names, .is_mask = false }, { .tag = 0, .enum_info = NULL, .is_mask = false }, }; static const struct kmip_enum kmip_v1_attribute_names[] = { { .val = KMIP_TAG_UNIQUE_IDENTIFIER, .name = "Unique Identifier" }, { .val = KMIP_TAG_NAME, .name = "Name" }, { .val = KMIP_TAG_OBJECT_TYPE, .name = "Object Type" }, { .val = KMIP_TAG_CRYPTOGRAPHIC_ALGORITHM, .name = "Cryptographic Algorithm" }, { .val = KMIP_TAG_CRYPTOGRAPHIC_DOMAIN_PARAMETERS, .name = "Cryptographic Domain Parameters" }, { .val = KMIP_TAG_CRYPTOGRAPHIC_LENGTH, .name = "Cryptographic Length" }, { .val = KMIP_TAG_CRYPTOGRAPHIC_PARAMETERS, .name = "Cryptographic Parameters" }, { .val = KMIP_TAG_CERTIFICATE_TYPE, .name = "Certificate Type" }, { .val = KMIP_TAG_CERTIFICATE_IDENTIFIER, .name = "Certificate Identifier" }, { .val = KMIP_TAG_CERTIFICATE_ISSUER, .name = "Certificate Issuer" }, { .val = KMIP_TAG_CERTIFICATE_SUBJECT, .name = "Certificate Subject" }, { .val = KMIP_TAG_DIGEST, .name = "Digest" }, { .val = KMIP_TAG_OPERATION_POLICY_NAME, .name = "Operation Policy Name" }, { .val = KMIP_TAG_CRYPTOGRAPHIC_USAGE_MASK, .name = "Cryptographic Usage Mask" }, { .val = KMIP_TAG_LEASE_TIME, .name = "Lease Time" }, { .val = KMIP_TAG_USAGE_LIMITS, .name = "Usage Limits" }, { .val = KMIP_TAG_STATE, .name = "State" }, { .val = KMIP_TAG_INITIAL_DATE, .name = "Initial Date" }, { .val = KMIP_TAG_ACTIVATION_DATE, .name = "Activation Date" }, { .val = KMIP_TAG_PROCESS_START_DATE, .name = "Process Start Date" }, { .val = KMIP_TAG_PROTECT_STOP_DATE, .name = "Protect Stop Date" }, { .val = KMIP_TAG_DEACTIVATION_DATE, .name = "Deactivation Date" }, { .val = KMIP_TAG_DESTROY_DATE, .name = "Destroy Date" }, { .val = KMIP_TAG_COMPROMISE_OCCURRENCE_DATE, .name = "Compromise Occurrence Date" }, { .val = KMIP_TAG_COMPROMIZE_DATE, .name = "Compromise Date" }, { .val = KMIP_TAG_REVOCATION_REASON, .name = "Revocation Reason" }, { .val = KMIP_TAG_ARCHIVE_DATE, .name = "Archive Date" }, { .val = KMIP_TAG_OBJECT_GROUP, .name = "Object Group" }, { .val = KMIP_TAG_LINK, .name = "Link" }, { .val = KMIP_TAG_APPLICATION_SPECIFIC_INFORMATION, .name = "Application Specific Information" }, { .val = KMIP_TAG_CONTACT_INFORMATION, .name = "Contact Information" }, { .val = KMIP_TAG_LAST_CHANGE_DATE, .name = "Last Change Date" }, { .val = KMIP_TAG_CUSTOM_ATTRIBUTE, .name = "Custom Attribute" }, { .val = KMIP_TAG_ALTERNATE_NAME, .name = "Alternative Name" }, { .val = KMIP_TAG_KEY_VALUE_PRESENT, .name = "Key Value Present" }, { .val = KMIP_TAG_KEY_VALUE_LOCATION, .name = "Key Value Location" }, { .val = KMIP_TAG_ORIGINAL_CREATION_DATE, .name = "Original Creation Date" }, { .val = KMIP_TAG_RANDOM_NUMBER_GENERATOR, .name = "Random Number Generator" }, { .val = KMIP_TAG_PKCS_12_FRIENDLY_NAME, .name = "PKCS#12 Friendly Name" }, { .val = KMIP_TAG_DESCRIPTION, .name = "Description" }, { .val = KMIP_TAG_COMMENT, .name = "Comment" }, { .val = KMIP_TAG_SENSITIVE, .name = "Sensitive" }, { .val = KMIP_TAG_ALWAYS_SENSITIVE, .name = "Always Sensitive" }, { .val = KMIP_TAG_EXTRACTABLE, .name = "Extractable" }, { .val = KMIP_TAG_NEVER_EXTRACTABLE, .name = "Never Extractable" }, /* * KMIP v2.x attribute names (just for reference, will most likely * not appear in a v1.x attribute list */ { .val = KMIP_TAG_CERTIFICATE_SUBJECT_CN, .name = "Certificate Subject CN" }, { .val = KMIP_TAG_CERTIFICATE_SUBJECT_O, .name = "Certificate Subject O" }, { .val = KMIP_TAG_CERTIFICATE_SUBJECT_OU, .name = "Certificate Subject OU" }, { .val = KMIP_TAG_CERTIFICATE_SUBJECT_EMAIL, .name = "Certificate Subject Email" }, { .val = KMIP_TAG_CERTIFICATE_SUBJECT_C, .name = "Certificate Subject C" }, { .val = KMIP_TAG_CERTIFICATE_SUBJECT_ST, .name = "Certificate Subject ST" }, { .val = KMIP_TAG_CERTIFICATE_SUBJECT_L, .name = "Certificate Subject L" }, { .val = KMIP_TAG_CERTIFICATE_SUBJECT_UID, .name = "Certificate Subject UID" }, { .val = KMIP_TAG_CERTIFICATE_SUBJECT_SERIAL_NUMBER, .name = "Certificate Subject Serial Number" }, { .val = KMIP_TAG_CERTIFICATE_SUBJECT_TITLE, .name = "Certificate Subject Title" }, { .val = KMIP_TAG_CERTIFICATE_SUBJECT_DC, .name = "Certificate Subject DC" }, { .val = KMIP_TAG_CERTIFICATE_SUBJECT_DN_QUALIFIER, .name = "Certificate Subject DN Qualifier" }, { .val = KMIP_TAG_CERTIFICATE_ISSUER_CN, .name = "Certificate Issuer CN" }, { .val = KMIP_TAG_CERTIFICATE_ISSUER_O, .name = "Certificate Issuer O" }, { .val = KMIP_TAG_CERTIFICATE_ISSUER_OU, .name = "Certificate Issuer OU" }, { .val = KMIP_TAG_CERTIFICATE_ISSUER_EMAIL, .name = "Certificate Issuer Email" }, { .val = KMIP_TAG_CERTIFICATE_ISSUER_C, .name = "Certificate Issuer C" }, { .val = KMIP_TAG_CERTIFICATE_ISSUER_ST, .name = "Certificate Issuer ST" }, { .val = KMIP_TAG_CERTIFICATE_ISSUER_L, .name = "Certificate Issuer L" }, { .val = KMIP_TAG_CERTIFICATE_ISSUER_UID, .name = "Certificate Issuer UID" }, { .val = KMIP_TAG_CERTIFICATE_ISSUER_SERIAL_NUMBER, .name = "Certificate Issuer Serial Number" }, { .val = KMIP_TAG_CERTIFICATE_ISSUER_TITLE, .name = "Certificate Issuer Title" }, { .val = KMIP_TAG_CERTIFICATE_ISSUER_DC, .name = "Certificate Issuer DC" }, { .val = KMIP_TAG_CERTIFICATE_ISSUER_DN_QUALIFIER, .name = "Certificate Issuer DN Qualifier" }, { .val = KMIP_TAG_CERTIFICATE_TYPE, .name = "Certificate Type" }, { .val = KMIP_TAG_CERTIFICATE_LENGTH, .name = "Certificate Length" }, { .val = KMIP_TAG_DIGITAL_SIGNATURE_ALGORITHM, .name = "Digital Signature Algorithm" }, { .val = KMIP_TAG_FRESH, .name = "Fresh" }, { .val = KMIP_TAG_KEY_FORMAT_TYPE, .name = "Key Format Type" }, { .val = KMIP_TAG_NIST_KEY_TYPE, .name = "NIST Key Type" }, { .val = KMIP_TAG_OPAQUE_DATA_TYPE, .name = "Opaque Data Type" }, { .val = KMIP_TAG_PROTECTION_LEVEL, .name = "Protection Level" }, { .val = KMIP_TAG_PROTECTION_PERIOD, .name = "Protection Period" }, { .val = KMIP_TAG_PROTECTION_STORAGE_MASK, .name = "Protection Storage Mask" }, { .val = KMIP_TAG_QUANTUM_SAFE, .name = "Quantum Safe" }, { .val = KMIP_TAG_SHORT_UNIQUE_IDENTIFIER, .name = "Short Unique Identifier" }, { .val = KMIP_TAG_ATTRIBUTE, .name = "Vendor Attribute" }, { .val = KMIP_TAG_X_509_CERTIFICATE_IDENTIFIER, .name = "X.509 Certificate Identifier" }, { .val = KMIP_TAG_X_509_CERTIFICATE_ISSUER, .name = "X.509 Certificate Issuer" }, { .val = KMIP_TAG_X_509_CERTIFICATE_SUBJECT, .name = "X.509 Certificate Subject" }, { .val = 0, .name = NULL }, }; /** * Return the name of the enumeration value, or NULL if the value is unknown */ static const char *kmip_enum_name_by_value(const struct kmip_enum *info, uint32_t val) { unsigned int i; for (i = 0; info[i].name != NULL; i++) { if (info[i].val == val) return info[i].name; } return NULL; } /** * Return the enumeration value specified as enumeration name, or in hex * notation. */ int kmip_enum_value_by_name_or_hex(const struct kmip_enum *info, const char *name, uint32_t *value) { unsigned int i; int64_t val; int rc; if (name == NULL || value == NULL) return -EINVAL; for (i = 0; info[i].name != NULL; i++) { if (strcmp(info[i].name, name) == 0) { *value = info[i].val; return 0; } } rc = kmip_parse_hex_int(name, &val); if (rc != 0) return rc; *value = val; return 0; } /* * Get the name of an enumeration value belonging to the specified tag */ const char *kmip_enum_name_by_tag_value(enum kmip_tag tag, uint32_t val) { int i; for (i = 0; enum_info[i].enum_info != NULL; i++) { if (enum_info[i].tag != tag) continue; return kmip_enum_name_by_value(enum_info[i].enum_info, val); } return NULL; } /** * Return the enumeration value belonging to the specified tag, specified as * enumeration name, or in hex notation. */ int kmip_enum_value_by_tag_name_or_hex(enum kmip_tag tag, const char *name, uint32_t *value) { int64_t val; int i, rc; if (name == NULL || value == NULL) return -EINVAL; for (i = 0; enum_info[i].enum_info != NULL; i++) { if (enum_info[i].tag != tag) continue; return kmip_enum_value_by_name_or_hex(enum_info[i].enum_info, name, value); } rc = kmip_parse_hex_int(name, &val); if (rc != 0) return rc; *value = val; return 0; } /** * Return the name of the tag, or NULL if the tag is unknown */ const char *kmip_tag_name_by_tag(enum kmip_tag tag) { return kmip_enum_name_by_value(kmip_tags, tag); } /** * Return the name or the hex representation of the tag */ const char *kmip_tag_name_or_hex_by_tag(enum kmip_tag tag, char tmp_buff[20]) { const char *str; str = kmip_enum_name_by_value(kmip_tags, tag); if (str == NULL) { sprintf(tmp_buff, "0x%06x", tag); str = tmp_buff; } return str; } /** * Return the tag value specified as tag name, or in hex notation. Returns 0 * in case of an error. */ enum kmip_tag kmip_tag_by_name_or_hex(const char *name) { uint32_t val; int rc; rc = kmip_enum_value_by_name_or_hex(kmip_tags, name, &val); if (rc != 0) return 0; return val; } /** * Return the name of the type, or NULL if the type is unknown */ const char *kmip_type_name_by_type(enum kmip_type type) { return kmip_enum_name_by_value(kmip_types, type); } /** * Return the type value specified as type name, or in hex notation. Returns 0 * in case of an error. */ enum kmip_type kmip_type_by_name_or_hex(const char *name) { uint32_t val; int rc; rc = kmip_enum_value_by_name_or_hex(kmip_types, name, &val); if (rc != 0) return 0; return val; } /** * Return the KMIP v1.x attribute name of the tag, or NULL if the tag is unknown */ const char *kmip_v1_attr_name_by_tag(enum kmip_tag attr_tag) { return kmip_enum_name_by_value(kmip_v1_attribute_names, attr_tag); } /** * Return the attribute tag value specified as KMIP v1.x attribute name. * Returns 0 in case of an error. */ enum kmip_tag kmip_attr_tag_by_v1_attr_name(const char *name) { uint32_t val; int rc; rc = kmip_enum_value_by_name_or_hex(kmip_v1_attribute_names, name, &val); if (rc != 0) return 0; return val; } /** * Returns true if the tag is a mask */ bool kmip_is_tag_mask(enum kmip_tag tag) { int i; for (i = 0; enum_info[i].enum_info != NULL; i++) { if (enum_info[i].tag != tag) continue; return enum_info[i].is_mask; } return false; } /** * Returns the enumeration info for the specified tag, or NULL, if the tag * is not associated with an enumeration. */ const struct kmip_enum *kmip_enum_info_by_tag(enum kmip_tag tag) { int i; for (i = 0; enum_info[i].enum_info != NULL; i++) { if (enum_info[i].tag != tag) continue; return enum_info[i].enum_info; } return NULL; } s390-tools-2.38.0/libkmipclient/names.h000066400000000000000000000022411502674226300175500ustar00rootroot00000000000000/* * libkmipclient - KMIP client library * * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef NAMES_H #define NAMES_H #include #include "kmip.h" struct kmip_enum { uint32_t val; const char *name; }; const struct kmip_enum *kmip_enum_info_by_tag(enum kmip_tag tag); bool kmip_is_tag_mask(enum kmip_tag tag); int kmip_enum_value_by_name_or_hex(const struct kmip_enum *info, const char *name, uint32_t *value); const char *kmip_enum_name_by_tag_value(enum kmip_tag tag, uint32_t val); int kmip_enum_value_by_tag_name_or_hex(enum kmip_tag tag, const char *name, uint32_t *value); const char *kmip_tag_name_by_tag(enum kmip_tag tag); const char *kmip_tag_name_or_hex_by_tag(enum kmip_tag tag, char tmp_buff[20]); enum kmip_tag kmip_tag_by_name_or_hex(const char *name); const char *kmip_type_name_by_type(enum kmip_type type); enum kmip_type kmip_type_by_name_or_hex(const char *name); const char *kmip_v1_attr_name_by_tag(enum kmip_tag attr_tag); enum kmip_tag kmip_attr_tag_by_v1_attr_name(const char *name); #endif s390-tools-2.38.0/libkmipclient/request.c000066400000000000000000001611571502674226300201440ustar00rootroot00000000000000/* * libkmipclient - KMIP client library * * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #define _XOPEN_SOURCE #define _DEFAULT_SOURCE #include #include #include "kmip.h" #include "names.h" /** * Constructs a Protocol Version node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Protocol Version Structure v1.0 * Protocol Version Major Yes Integer v1.0 * Protocol Version Minor Yes Integer v1.0 * * @param version the protocol version * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_protocol_version(const struct kmip_version *version) { struct kmip_node *ret = NULL, *maj, *min; if (version == NULL) return NULL; maj = kmip_node_new_integer(KMIP_TAG_PROTOCOL_VERSION_MAJOR, NULL, version->major); min = kmip_node_new_integer(KMIP_TAG_PROTOCOL_VERSION_MINOR, NULL, version->minor); if (maj == NULL || min == NULL) goto out; ret = kmip_node_new_structure_va(KMIP_TAG_PROTOCOL_VERSION, NULL, 2, maj, min); out: kmip_node_free(maj); kmip_node_free(min); return ret; } /** * Constructs a Profile Version node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Profile Version Structure v1.0 * Profile Version Major Yes Integer v1.0 * Profile Version Minor Yes Integer v1.0 * * @param version the profile version * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_profile_version(const struct kmip_version *version) { struct kmip_node *ret = NULL, *maj, *min; if (version == NULL) return NULL; maj = kmip_node_new_integer(KMIP_TAG_PROFILE_VERSION_MAJOR, NULL, version->major); min = kmip_node_new_integer(KMIP_TAG_PROFILE_VERSION_MINOR, NULL, version->minor); if (maj == NULL || min == NULL) goto out; ret = kmip_node_new_structure_va(KMIP_TAG_PROFILE_VERSION, NULL, 2, maj, min); out: kmip_node_free(maj); kmip_node_free(min); return ret; } /** * Constructs a Request Header node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Request Header Yes Structure v1.0 * Protocol Version Yes Structure v1.0 * Maximum Response Size No Integer v1.0 * Client Correlation Value No Text String v1.4 * Server Correlation Value No Text String v1.4 * Asynchronous Indicator No Boolean v1.0 * Attestation Capable Indic. No Boolean v1.2 * Attestation Type No Enumeration v1.2 * ... may be repeated * Authentication No Structure v1.0 * Batch Error Cont. Option No Enumeration v1.0 * Batch Order Option No Boolean v1.0 * Time Stamp No Date Time v1.0 * Batch Count Yes Integer v1.0 * * @param version the protocol version. If NULL, the default * protocol version is used * @param max_response_size the maximum response size. Ignored if <= 0. * @param client_corr_value the client correlation value. Ignored if NULL. * @param server_corr_value the server correlation value. Ignored if NULL. * @param asynchronous if true the request is asynchronous * @param authentication the authentication node (can be NULL) * @param batch_err_opt the batch error continuation option. Ignored if 0, * or if batch_count is less than 2. * @param batch_order_option the batch order option (true = execute in order) * @param batch_count the batch count * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_request_header(const struct kmip_version *version, int32_t max_response_size, const char *client_corr_value, const char *server_corr_value, bool asynchronous, struct kmip_node *authentication, enum kmip_batch_error_cont_option batch_err_opt, bool batch_order_option, int32_t batch_count) { struct kmip_node *ret = NULL, *err = NULL, *async = NULL, *tim = NULL; struct kmip_node *max = NULL, *cnt = NULL, *ord = NULL, *ver = NULL; struct kmip_node *ccorr = NULL, *scorr = NULL; if (version == NULL) version = kmip_get_default_protocol_version(); ver = kmip_new_protocol_version(version); if (ver == NULL) goto out; if (max_response_size > 0) { max = kmip_node_new_integer(KMIP_TAG_MAXIMUM_RESPONSE_SIZE, NULL, max_response_size); if (max == NULL) goto out; } if (version->major == 1 && version->minor <= 3) { client_corr_value = NULL; server_corr_value = NULL; } if (client_corr_value) { ccorr = kmip_node_new_text_string( KMIP_TAG_CLIENT_CORRELATION_VALUE, NULL, client_corr_value); if (ccorr == NULL) goto out; } if (server_corr_value) { scorr = kmip_node_new_text_string( KMIP_TAG_SERVER_CORRELATION_VALUE, NULL, server_corr_value); if (scorr == NULL) goto out; } if (asynchronous) { async = kmip_node_new_boolean(KMIP_TAG_ASYNCHRONOUS_INDICATOR, NULL, asynchronous); if (async == NULL) goto out; } if (batch_err_opt != 0 && batch_count > 1) { err = kmip_node_new_enumeration( KMIP_TAG_BATCH_ERROR_CONTINUATION_OPTION, NULL, batch_err_opt); if (err == NULL) goto out; } ord = kmip_node_new_boolean(KMIP_TAG_BATCH_ORDER_OPTION, NULL, batch_order_option); if (ord == NULL) goto out; tim = kmip_node_new_date_time(KMIP_TAG_TIME_STAMP, NULL, time(NULL)); if (tim == NULL) goto out; cnt = kmip_node_new_integer(KMIP_TAG_BATCH_COUNT, NULL, batch_count); if (cnt == NULL) goto out; ret = kmip_node_new_structure_va(KMIP_TAG_REQUEST_HEADER, NULL, 10, ver, max, ccorr, scorr, authentication, async, err, ord, tim, cnt); out: kmip_node_free(ver); kmip_node_free(max); kmip_node_free(ccorr); kmip_node_free(scorr); kmip_node_free(async); kmip_node_free(err); kmip_node_free(ord); kmip_node_free(tim); kmip_node_free(cnt); return ret; } /** * Constructs a Request Batch Item node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Batch Item Yes Structure v1.0 * Operation Yes Enumeration v1.0 * Ephemeral No Boolean v2.0 * Unique Batch Item ID No Byte String v1.0 * Request Payload Yes Structure v1.0 * Message Extension No Structure v1.0 * * @param operation the operation * @param authentication A batch_id (can be NULL, req. if batch count > 0) * @param batch_id_length the size of the batch ID * @param payload the payload node * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_request_batch_item(enum kmip_operation operation, unsigned char *batch_id, uint32_t batch_id_length, struct kmip_node *payload) { struct kmip_node *ret = NULL, *op, *bid = NULL; if (payload == NULL) return NULL; op = kmip_node_new_enumeration(KMIP_TAG_OPERATION, NULL, operation); if (op == NULL) return NULL; if (batch_id != NULL && batch_id_length > 0) { bid = kmip_node_new_byte_string(KMIP_TAG_UNIQUE_BATCH_ITEM_ID, NULL, batch_id, batch_id_length); if (bid == NULL) goto out; } ret = kmip_node_new_structure_va(KMIP_TAG_BATCH_ITEM, NULL, 3, op, bid, payload); out: kmip_node_free(op); kmip_node_free(bid); return ret; } /** * Constructs a Request Message node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Request Message Yes Structure v1.0 * Request Header Yes Structure v1.0 * Batch Item Yes Structure v1.0 * ... may be repeated * * @param request_header the request header node * @param batch_count the number of batch items to add * @parambatch_items array of batch items * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_request(struct kmip_node *request_header, int32_t batch_count, struct kmip_node **batch_items) { struct kmip_node *ret; int rc = 0; if (request_header == NULL) return NULL; ret = kmip_node_new_structure_va(KMIP_TAG_REQUEST_MESSAGE, NULL, 1, request_header); if (ret == NULL) return NULL; rc = kmip_node_add_structure_elements(ret, batch_count, batch_items); if (rc != 0) goto error; return ret; error: kmip_node_free(ret); return NULL; } /** * Constructs a Request Message node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Request Message Yes Structure v1.0 * Request Header Yes Structure v1.0 * Batch Item Yes Structure v1.0 * ... may be repeated * * @param request_header the request header node * @param batch_count the number of batch items following * @param batch items (struct kmip_node *) * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_request_va(struct kmip_node *request_header, int32_t batch_count, ...) { struct kmip_node *ret, **bis = NULL; va_list ap; int32_t i; if (request_header == NULL) return NULL; if (batch_count > 0) { bis = calloc(batch_count, sizeof(struct kmip_node *)); if (bis == NULL) return NULL; } va_start(ap, batch_count); for (i = 0; i < batch_count; i++) bis[i] = va_arg(ap, struct kmip_node *); va_end(ap); ret = kmip_new_request(request_header, batch_count, bis); if (bis != NULL) free(bis); return ret; } /** * Constructs a Query request payload: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Query Function Yes Enumeration v1.0 * ... may be repeated * * @param query_count the number of query function items following * @param functions query function items * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_query_request_payload(unsigned int query_count, const enum kmip_query_function *functions) { struct kmip_node *rpl, *qf = NULL; unsigned int i; int rc = 0; if (query_count > 0 && functions == NULL) return NULL; rpl = kmip_node_new_structure_va(KMIP_TAG_REQUEST_PAYLOAD, NULL, 0); if (rpl == NULL) return NULL; for (i = 0; i < query_count; i++) { qf = kmip_node_new_enumeration(KMIP_TAG_QUERY_FUNCTION, NULL, functions[i]); if (qf == NULL) goto error; rc = kmip_node_add_structure_element(rpl, qf); if (rc != 0) break; kmip_node_free(qf); qf = NULL; } if (rc != 0) goto error; return rpl; error: kmip_node_free(rpl); kmip_node_free(qf); return NULL; } /** * Constructs a Query request payload: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Query Function Yes Enumeration v1.0 * ... may be repeated * * @param query_count the number of query function items following * @param query items (enum kmip_query_function) * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_query_request_payload_va(unsigned int query_count, ...) { enum kmip_query_function *qfs = NULL; struct kmip_node *rpl; unsigned int i; va_list ap; if (query_count > 0) { qfs = calloc(query_count, sizeof(enum kmip_query_function)); if (qfs == NULL) return NULL; } va_start(ap, query_count); for (i = 0; i < query_count; i++) qfs[i] = va_arg(ap, enum kmip_query_function); va_end(ap); rpl = kmip_new_query_request_payload(query_count, qfs); if (qfs != NULL) free(qfs); return rpl; } static const struct kmip_version kmip_versions[] = { { .major = 1, .minor = 0 }, { .major = 1, .minor = 1 }, { .major = 1, .minor = 2 }, { .major = 1, .minor = 3 }, { .major = 1, .minor = 4 }, { .major = 2, .minor = 0 }, { .major = 2, .minor = 1 }, }; /** * Constructs a Discover Versions request payload: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Protocol Version No Structure v1.2 * ... may be repeated * * @param version_count the number of version items following. If -1 then * all currently supported versions are added. * @param versions array of version items * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_discover_versions_payload(int version_count, const struct kmip_version *versions) { struct kmip_node *rpl, *ver = NULL; int rc = 0; int i; if (version_count > 0 && versions == NULL) return NULL; rpl = kmip_node_new_structure_va(KMIP_TAG_REQUEST_PAYLOAD, NULL, 0); if (rpl == NULL) return NULL; if (version_count < 0) { versions = kmip_versions; version_count = sizeof(kmip_versions) / sizeof(struct kmip_version); } for (i = 0; i < version_count; i++) { ver = kmip_new_protocol_version(&versions[i]); if (ver == NULL) goto error; rc = kmip_node_add_structure_element(rpl, ver); if (rc != 0) break; kmip_node_free(ver); ver = NULL; } if (rc != 0) goto error; return rpl; error: kmip_node_free(rpl); kmip_node_free(ver); return NULL; } /** * Constructs a Discover Versions request payload: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Protocol Version No Structure v1.2 * ... may be repeated * * @param version_count the number of version items following. If -1 then * all currently supported versions are added. * @param version items (struct kmip_version) * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_discover_versions_payload_va(int version_count, ...) { struct kmip_version *versions = NULL; struct kmip_node *rpl; va_list ap; int i; if (version_count > 0) { versions = calloc(version_count, sizeof(struct kmip_version)); if (versions == NULL) return NULL; } va_start(ap, version_count); for (i = 0; i < version_count; i++) versions[i] = *va_arg(ap, struct kmip_version *); va_end(ap); rpl = kmip_new_discover_versions_payload(version_count, versions); if (versions != NULL) free(versions); return rpl; } /** * Constructs a Protection Storage Masks node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Protection Storage Masks Yes Structure v2.0 * Protection Storage Mask Yes Integer v2.0 * ... may be repeated * * @param masks_count the number of protection storage masks * @param masks array of mask items * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_protection_storage_masks(unsigned int masks_count, int32_t *masks) { struct kmip_node *ret, *psm; unsigned int i; int rc = 0; if (masks_count > 0 && masks == NULL) return NULL; ret = kmip_node_new_structure_va(KMIP_TAG_PROTECTION_STORAGE_MASKS, NULL, 0); if (ret == NULL) return NULL; for (i = 0; i < masks_count; i++) { psm = kmip_node_new_integer(KMIP_TAG_PROTECTION_STORAGE_MASK, NULL, masks[i]); if (psm == NULL) break; rc = kmip_node_add_structure_element(ret, psm); if (rc != 0) break; kmip_node_free(psm); psm = NULL; } if (rc != 0) goto error; return ret; error: kmip_node_free(ret); kmip_node_free(psm); return NULL; } /** * Constructs a Protection Storage Masks node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Protection Storage Masks Yes Structure v2.0 * Protection Storage Mask Yes Integer v2.0 * ... may be repeated * * @param masks_count the number of protection storage masks following * @param mask items (int32_t) * * @returns the allocated node, or NULL in case of an error. */ struct kmip_node *kmip_new_protection_storage_masks_va(unsigned int masks_count, ...) { int32_t *masks = NULL; struct kmip_node *ret; unsigned int i; va_list ap; if (masks_count > 0) { masks = calloc(masks_count, sizeof(int32_t)); if (masks == NULL) return NULL; } va_start(ap, masks_count); for (i = 0; i < masks_count; i++) masks[i] = va_arg(ap, int32_t); va_end(ap); ret = kmip_new_protection_storage_masks(masks_count, masks); if (masks != NULL) free(masks); return ret; } /** * Constructs a Create request payload: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Object Type Yes Enumeration v1.0 * Template-Attribute Yes(v1.x) Structure v1.x only * Attributes Yes(v2.x) Structure v2.x only * Protection Storage Masks No Structure v2.x * * @param version the protocol version. If null, the current default * protocol version is used. * @param obj_type the object type to create * @paran prot_storage_masks the protection storage masks (can be NULL) * @param attrs_count the number of attributes following (can be 0) * @param attrs the array of attributes (as KMIP v2.x attribute) * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_create_request_payload( const struct kmip_version *version, enum kmip_object_type obj_type, struct kmip_node *prot_storage_masks, unsigned int attrs_count, struct kmip_node **attrs) { struct kmip_node *rpl = NULL, *otyp = NULL, *att; if (attrs_count > 0 && attrs == NULL) return NULL; if (version == NULL) version = kmip_get_default_protocol_version(); otyp = kmip_new_object_type(obj_type); if (otyp == NULL) return NULL; if (version->major < 2) prot_storage_masks = NULL; att = kmip_new_attributes(version, KMIP_TAG_ATTRIBUTES, attrs_count, attrs); if (att == NULL) goto out; rpl = kmip_node_new_structure_va(KMIP_TAG_REQUEST_PAYLOAD, NULL, 3, otyp, att, prot_storage_masks); out: kmip_node_free(otyp); kmip_node_free(att); return rpl; } /** * Constructs a Create request payload: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Object Type Yes Enumeration v1.0 * Template-Attribute Yes(v1.x) Structure v1.x only * Attributes Yes(v2.x) Structure v2.x only * Protection Storage Masks No Structure v2.x * * @param version the protocol version. If null, the current default * protocol version is used. * @param obj_type the object type to create * @paran prot_storage_masks the protection storage masks (can be NULL) * @param attrs_count the number of attributes following (can be 0) * @param the attributes (as KMIP v2.x attribute) * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_create_request_payload_va( const struct kmip_version *version, enum kmip_object_type obj_type, struct kmip_node *prot_storage_masks, unsigned int attrs_count, ...) { struct kmip_node *ret, **attrs = NULL; unsigned int i, k; va_list ap; if (attrs_count > 0) { attrs = calloc(attrs_count, sizeof(struct kmip_node *)); if (attrs == NULL) return NULL; } va_start(ap, attrs_count); for (i = 0, k = 0; i < attrs_count; i++) { attrs[k] = va_arg(ap, struct kmip_node *); if (attrs[k] != NULL) k++; } va_end(ap); ret = kmip_new_create_request_payload(version, obj_type, prot_storage_masks, k, attrs); if (attrs != NULL) free(attrs); return ret; } /** * Constructs a Get Attribute List request payload: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Unique Identifier No Text String v1.0 * Enumeration v2.0 * Integer v2.0 * * @param unique_id the unique id of the object to address (can be NULL) * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_get_attribute_list_request_payload( struct kmip_node *unique_id) { struct kmip_node *rpl; rpl = kmip_node_new_structure_va(KMIP_TAG_REQUEST_PAYLOAD, NULL, 1, unique_id); return rpl; } /** * Constructs a Get Attributes request payload: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Unique Identifier No Text String v1.0 * Enumeration v2.0 * Integer v2.0 * Attribute Name No Text String v1.x only * ... may be repeated * Attribute Reference No Enumeration v2.x only * Structure v2.x only * ... may be repeated * * @param version the protocol version. If null, the current default * protocol version is used. * @param unique_id the unique id of the object to address * @param num_attrs number of attribute references following * @param attr_refs array of attribute references (struct kmip_node *) * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_get_attributes_request_payload( const struct kmip_version *version, struct kmip_node *unique_id, unsigned int num_attrs, struct kmip_node **attr_refs) { struct kmip_node *rpl, *v2_attr_ref, *v1_attr_name; unsigned int i; int rc = 0; if (num_attrs > 0 && attr_refs == NULL) return NULL; if (version == NULL) version = kmip_get_default_protocol_version(); rpl = kmip_node_new_structure_va(KMIP_TAG_REQUEST_PAYLOAD, NULL, 1, unique_id); for (i = 0; i < num_attrs; i++) { v2_attr_ref = attr_refs[i]; if (v2_attr_ref == NULL) continue; if (version->major == 1) { /* KMIP v1.x */ v1_attr_name = kmip_new_attribute_name_v1(v2_attr_ref); if (v1_attr_name == NULL) { rc = -EBADMSG; break; } rc = kmip_node_add_structure_element(rpl, v1_attr_name); kmip_node_free(v1_attr_name); } else { /* KMIP >= v2.0 */ rc = kmip_node_add_structure_element(rpl, v2_attr_ref); } if (rc != 0) break; } if (rc != 0) goto error; return rpl; error: kmip_node_free(rpl); return NULL; } /** * Constructs a Get Attributes request payload: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Unique Identifier No Text String v1.0 * Enumeration v2.0 * Integer v2.0 * Attribute Name No Text String v1.x only * ... may be repeated * Attribute Reference No Enumeration v2.x only * Structure v2.x only * ... may be repeated * * @param version the protocol version. If null, the current default * protocol version is used. * @param unique_id the unique id of the object to address * @param num_attrs number of attribute references following * @param attribute references (struct kmip_node *) * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_get_attributes_request_payload_va( const struct kmip_version *version, struct kmip_node *unique_id, unsigned int num_attrs, ...) { struct kmip_node *ret, **attr_refs = NULL; unsigned int i, k; va_list ap; if (num_attrs > 0) { attr_refs = calloc(num_attrs, sizeof(struct kmip_node *)); if (attr_refs == NULL) return NULL; } va_start(ap, num_attrs); for (i = 0, k = 0; i < num_attrs; i++) { attr_refs[k] = va_arg(ap, struct kmip_node *); if (attr_refs[k] != NULL) k++; } va_end(ap); ret = kmip_new_get_attributes_request_payload(version, unique_id, k, attr_refs); if (attr_refs != NULL) free(attr_refs); return ret; } /** * Constructs a Add Attribute request payload: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Unique Identifier No Text String v1.0 * Enumeration v2.0 * Integer v2.0 * Attribute Yes Structure v1.x only * or * New Attribute Yes Structure v2.x only * * @param version the protocol version. If null, the current default * protocol version is used. * @param unique_id the unique id of the object to address * @param v2_attr the attribute to add (as KMIP v2.x attribute) * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_add_attribute_request_payload( const struct kmip_version *version, struct kmip_node *unique_id, struct kmip_node *v2_attr) { struct kmip_node *rpl, *new_attr, *v1_attr; int rc; if (v2_attr == NULL) return NULL; if (version == NULL) version = kmip_get_default_protocol_version(); if (version->major == 1) { /* KMIP v1.x */ rc = kmip_v1_attr_from_v2_attr(v2_attr, &v1_attr); if (rc != 0) return NULL; rpl = kmip_node_new_structure_va(KMIP_TAG_REQUEST_PAYLOAD, NULL, 2, unique_id, v1_attr); kmip_node_free(v1_attr); } else { /* KMIP >= v2.0 */ new_attr = kmip_new_current_new_attribute(true, v2_attr); if (new_attr == NULL) return NULL; rpl = kmip_node_new_structure_va(KMIP_TAG_REQUEST_PAYLOAD, NULL, 2, unique_id, new_attr); kmip_node_free(new_attr); } return rpl; } /** * Constructs a Modify Attribute request payload: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Unique Identifier No Text String v1.0 * Enumeration v2.0 * Integer v2.0 * Attribute Yes Structure v1.x only * or * Current Attribute No Structure v2.x only * New Attribute Yes Structure v2.x only * * @param version the protocol version. If null, the current default * protocol version is used. * @param unique_id the unique id of the object to address * @param v2_current the current attribute (as KMIP v2.x attribute). * Can be NULL, ignored for KMIP v1.x. * @param v2_attr the attribute to modify (as KMIP v2.x attribute) * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_modify_attribute_request_payload( const struct kmip_version *version, struct kmip_node *unique_id, struct kmip_node *v2_current, struct kmip_node *v2_attr) { struct kmip_node *rpl, *new_attr, *cur_attr = NULL, *v1_attr; int rc; if (v2_attr == NULL) return NULL; if (version == NULL) version = kmip_get_default_protocol_version(); if (version->major == 1) { /* KMIP v1.x */ rc = kmip_v1_attr_from_v2_attr(v2_attr, &v1_attr); if (rc != 0) return NULL; rpl = kmip_node_new_structure_va(KMIP_TAG_REQUEST_PAYLOAD, NULL, 2, unique_id, v1_attr); kmip_node_free(v1_attr); } else { /* KMIP >= v2.0 */ new_attr = kmip_new_current_new_attribute(true, v2_attr); if (new_attr == NULL) return NULL; if (v2_current != NULL) { cur_attr = kmip_new_current_new_attribute(false, v2_current); if (cur_attr == NULL) { kmip_node_free(new_attr); return NULL; } } rpl = kmip_node_new_structure_va(KMIP_TAG_REQUEST_PAYLOAD, NULL, 3, unique_id, cur_attr, new_attr); kmip_node_free(new_attr); if (cur_attr != NULL) kmip_node_free(cur_attr); } return rpl; } /** * Constructs a Set Attribute request payload: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Unique Identifier No Text String v1.0 * Enumeration v2.0 * Integer v2.0 * New Attribute Yes Structure v2.x only * * KMIP v1.x does not have a Set Attribute operation. * * @param unique_id the unique id of the object to address * @param v2_attr the attribute to set (as KMIP v2.x attribute) * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_set_attribute_v2_request_payload( struct kmip_node *unique_id, struct kmip_node *v2_attr) { struct kmip_node *rpl, *new_attr; if (v2_attr == NULL) return NULL; new_attr = kmip_new_current_new_attribute(true, v2_attr); if (new_attr == NULL) return NULL; rpl = kmip_node_new_structure_va(KMIP_TAG_REQUEST_PAYLOAD, NULL, 2, unique_id, new_attr); kmip_node_free(new_attr); return rpl; } /** * Constructs a Delete Attribute request payload: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Unique Identifier No Text String v1.0 * Enumeration v2.0 * Integer v2.0 * Attribute Name Yes Text String v1.x only * Attribute Index No Integer v1.x only * or * Current Attribute No Structure v2.x only * Attribute Reference No Structure v2.x only * * @param version the protocol version. If null, the current default * protocol version is used. * @param unique_id the unique id of the object to address * @param v2_current the current attribute (as KMIP v2.x attribute). * Can be NULL. * @param attr_ref the attribute to modify (as KMIP v2.x attribute * reference). Either v2_current or attr_ref can be * specified. * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_delete_attribute_request_payload( const struct kmip_version *version, struct kmip_node *unique_id, struct kmip_node *v2_current, struct kmip_node *attr_ref) { struct kmip_node *rpl, *cur_attr = NULL, *nam = NULL; const char *vendor_id, *attr_name, *name; char *custom_name = NULL; int rc; if (v2_current != NULL && attr_ref != NULL) return NULL; if (v2_current == NULL && attr_ref == NULL) return NULL; if (version == NULL) version = kmip_get_default_protocol_version(); if (version->major == 1) { /* KMIP v1.x */ if (v2_current != NULL) { if (kmip_node_get_tag(v2_current) == KMIP_TAG_ATTRIBUTE) { /* Special handling for v2.x Vendor Attribute */ rc = kmip_get_vendor_attribute(v2_current, &vendor_id, &attr_name, NULL); if (rc != 0) return NULL; custom_name = kmip_build_v1_custom_attr_name( vendor_id, attr_name); if (custom_name == NULL) return NULL; name = custom_name; } else { name = kmip_v1_attr_name_by_tag( kmip_node_get_tag(v2_current)); } nam = kmip_node_new_text_string(KMIP_TAG_ATTRIBUTE_NAME, NULL, name); if (custom_name != NULL) free(custom_name); } else if (attr_ref != NULL) { nam = kmip_new_attribute_name_v1(attr_ref); } if (nam == NULL) return NULL; rpl = kmip_node_new_structure_va(KMIP_TAG_REQUEST_PAYLOAD, NULL, 2, unique_id, nam); kmip_node_free(nam); } else { /* KMIP >= v2.0 */ if (v2_current != NULL) { cur_attr = kmip_new_current_new_attribute(false, v2_current); if (cur_attr == NULL) return NULL; } rpl = kmip_node_new_structure_va(KMIP_TAG_REQUEST_PAYLOAD, NULL, 3, unique_id, cur_attr, attr_ref); if (cur_attr != NULL) kmip_node_free(cur_attr); } return rpl; } /** * Constructs an Activate request payload: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Unique Identifier No Text String v1.0 * Enumeration v2.0 * Integer v2.0 * * @param unique_id the unique id of the object to address * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_activate_request_payload(struct kmip_node *unique_id) { return kmip_node_new_structure_va(KMIP_TAG_REQUEST_PAYLOAD, NULL, 1, unique_id); } /** * Constructs an Destroy request payload: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Unique Identifier No Text String v1.0 * Enumeration v2.0 * Integer v2.0 * * @param unique_id the unique id of the object to address * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_destroy_request_payload(struct kmip_node *unique_id) { return kmip_node_new_structure_va(KMIP_TAG_REQUEST_PAYLOAD, NULL, 1, unique_id); } /** * Constructs an Archive request payload: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Unique Identifier No Text String v1.0 * Enumeration v2.0 * Integer v2.0 * * @param unique_id the unique id of the object to address * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_archive_request_payload(struct kmip_node *unique_id) { return kmip_node_new_structure_va(KMIP_TAG_REQUEST_PAYLOAD, NULL, 1, unique_id); } /** * Constructs an Recover request payload: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Unique Identifier No Text String v1.0 * Enumeration v2.0 * Integer v2.0 * * @param unique_id the unique id of the object to address * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_recover_request_payload(struct kmip_node *unique_id) { return kmip_node_new_structure_va(KMIP_TAG_REQUEST_PAYLOAD, NULL, 1, unique_id); } /** * Constructs an Revoke request payload: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Unique Identifier No Text String v1.0 * Enumeration v2.0 * Integer v2.0 * Revocation Reason Yes Structure v1.0 * Revocation Reason Code Yes Enumeration v1.0 * Revocation Message No Text String v1.0 * Compromise Occurrence Date No Date Time v1.0 * * @param unique_id the unique id of the object to address * @param rsn the revocation reason * @param message the revocation message (can be NULL) * @param compromise_date the date when he compromise happened (can be 0) * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_revoke_request_payload(struct kmip_node *unique_id, enum kmip_revoke_reason rsn, const char *message, uint64_t compromise_date) { struct kmip_node *rsn_code, *reason = NULL, *rsn_msg = NULL; struct kmip_node *rpl = NULL, *date = NULL; rsn_code = kmip_node_new_enumeration(KMIP_TAG_REVOCATION_REASON_CODE, NULL, rsn); if (rsn_code == NULL) return NULL; if (message != NULL) { rsn_msg = kmip_node_new_text_string(KMIP_TAG_REVOCATION_MESSAGE, NULL, message); if (rsn_msg == NULL) goto out; } reason = kmip_node_new_structure_va(KMIP_TAG_REVOCATION_REASON, NULL, 2, rsn_code, rsn_msg); if (reason == NULL) goto out; switch (rsn) { case KMIP_REVOK_RSN_KEY_COMPROMISE: case KMIP_REVOK_RSN_CA_COMPROMISE: if (compromise_date != 0) { date = kmip_node_new_date_time( KMIP_TAG_COMPROMISE_OCCURRENCE_DATE, NULL, compromise_date); if (date == NULL) goto out; } break; default: /*Compromise date is ignored on other reasons */ break; } rpl = kmip_node_new_structure_va(KMIP_TAG_REQUEST_PAYLOAD, NULL, 3, unique_id, reason, date); out: kmip_node_free(rsn_code); kmip_node_free(rsn_msg); kmip_node_free(reason); kmip_node_free(date); return rpl; } /** * Constructs an Locate request payload: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Maximum Items No Integer v1.0 * Offset Items No Integer v1.3 * Storage Status Mask No Integer v1.0 * Object Group Member No Enumeration v1.2 * Attribute No Structure v1.x only * ... may be repeated * Attributes Yes Structure v2.x only * * @param version the protocol version. If null, the current default * protocol version is used. * @param max_items the maximum numbers of items to return. If <= 0 * then no limit is assumed. * @param offset_items the number of items to skip If <= 0 then no offset * is assumed. Ignored for KMIP <= v1.2. * @param storage_status the storage status filter. If 0, then no filter is * used and only on-line objects are returned. * @param obj_group the object group filter. If 0 then no object group * filter is used. Ignored for KMIP <= v1.1. * @param attrs_count the number of attributes following (can be 0) * @param attrs the array of attributes (as KMIP v2.x attribute) * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_locate_request_payload( const struct kmip_version *version, int32_t max_items, int32_t offset_items, enum kmip_storage_status_mask storage_status, enum kmip_object_group_member obj_group, unsigned int attrs_count, struct kmip_node **attrs) { struct kmip_node *max = NULL, *ofs = NULL, *stm = NULL, *grp = NULL; struct kmip_node *rpl = NULL, *att, *v2_attr, *v1_attr; unsigned int i; int rc; if (attrs_count > 0 && attrs == NULL) return NULL; if (version == NULL) version = kmip_get_default_protocol_version(); if (max_items > 0) { max = kmip_node_new_integer(KMIP_TAG_MAXIMUM_ITEMS, NULL, max_items); if (max == NULL) return NULL; } if (offset_items > 0 && (version->major > 1 || (version->major == 1 && version->minor > 2))) { ofs = kmip_node_new_integer(KMIP_TAG_OFFSET_ITEMS, NULL, offset_items); if (ofs == NULL) goto out; } if (storage_status != 0) { stm = kmip_node_new_integer(KMIP_TAG_STORAGE_STATUS_MASK, NULL, storage_status); if (stm == NULL) goto out; } if (obj_group > 0 && (version->major > 1 || version->minor > 1)) { grp = kmip_node_new_enumeration(KMIP_TAG_OBJECT_GROUP_MEMBER, NULL, obj_group); if (grp == NULL) goto out; } if (version->major == 1) { /* KMIP v1.x */ rpl = kmip_node_new_structure_va(KMIP_TAG_REQUEST_PAYLOAD, NULL, 4, max, ofs, stm, grp); if (rpl == NULL) goto out; for (i = 0; i < attrs_count; i++) { v2_attr = attrs[i]; if (v2_attr == NULL) continue; rc = kmip_v1_attr_from_v2_attr(v2_attr, &v1_attr); if (rc != 0) goto error; rc = kmip_node_add_structure_element(rpl, v1_attr); kmip_node_free(v1_attr); if (rc != 0) goto error; } } else { /* KMIP >= v2.0 */ att = kmip_new_attributes(version, KMIP_TAG_ATTRIBUTES, attrs_count, attrs); if (att == NULL) goto out; rpl = kmip_node_new_structure_va(KMIP_TAG_REQUEST_PAYLOAD, NULL, 5, max, ofs, stm, grp, att); kmip_node_free(att); } goto out; error: kmip_node_free(rpl); rpl = NULL; out: kmip_node_free(max); kmip_node_free(ofs); kmip_node_free(stm); kmip_node_free(grp); return rpl; } /** * Constructs an Locate request payload: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Maximum Items No Integer v1.0 * Offset Items No Integer v1.3 * Storage Status Mask No Integer v1.0 * Object Group Member No Enumeration v1.2 * Attribute No Structure v1.x only * ... may be repeated * Attributes Yes Structure v2.x only * * @param version the protocol version. If null, the current default * protocol version is used. * @param max_items the maximum numbers of items to return. If <= 0 * then no limit is assumed. * @param offset_items the number of items to skip If <= 0 then no offset * is assumed. Ignored for KMIP <= v1.2. * @param storage_status the storage status filter. If 0, then no filter is * used and only on-line objects are returned. * @param obj_group the object group filter. If 0 then no object group * filter is used. Ignored for KMIP <= v1.1. * @param attrs_count the number of attributes following (can be 0) * @param the attributes (as KMIP v2.x attribute) * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_locate_request_payload_va( const struct kmip_version *version, int32_t max_items, int32_t offset_items, enum kmip_storage_status_mask storage_status, enum kmip_object_group_member obj_group, unsigned int attrs_count, ...) { struct kmip_node *ret, **attrs = NULL; unsigned int i, k; va_list ap; if (attrs_count > 0) { attrs = calloc(attrs_count, sizeof(struct kmip_node *)); if (attrs == NULL) return NULL; } va_start(ap, attrs_count); for (i = 0, k = 0; i < attrs_count; i++) { attrs[k] = va_arg(ap, struct kmip_node *); if (attrs[k] != NULL) k++; } va_end(ap); ret = kmip_new_locate_request_payload(version, max_items, offset_items, storage_status, obj_group, k, attrs); if (attrs != NULL) free(attrs); return ret; } /** * Constructs an Register request payload: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Object Type Yes Enumeration v1.0 * Template-Attribute Yes(v1.x) Structure v1.x only * Attributes Yes(v2.x) Structure v2.x only * Yes Structure v1.0 * Protection Storage Masks No Structure v2.x * * @param version the protocol version. If null, the current default * protocol version is used. * @param obj_type the object type to register * @param object the object to register * @paran prot_storage_masks the protection storage masks (can be NULL) * @param attrs_count the number of attributes following (can be 0) * @param attrs the array of attributes (as KMIP v2.x attribute) * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_register_request_payload( const struct kmip_version *version, enum kmip_object_type obj_type, struct kmip_node *object, struct kmip_node *prot_storage_masks, unsigned int attrs_count, struct kmip_node **attrs) { struct kmip_node *rpl = NULL, *otyp = NULL, *att; if (object == NULL) return NULL; if (attrs_count > 0 && attrs == NULL) return NULL; if (version == NULL) version = kmip_get_default_protocol_version(); otyp = kmip_new_object_type(obj_type); if (otyp == NULL) return NULL; if (version->major < 2) prot_storage_masks = NULL; att = kmip_new_attributes(version, KMIP_TAG_ATTRIBUTES, attrs_count, attrs); if (att == NULL) goto out; rpl = kmip_node_new_structure_va(KMIP_TAG_REQUEST_PAYLOAD, NULL, 4, otyp, att, object, prot_storage_masks); out: kmip_node_free(otyp); kmip_node_free(att); return rpl; } /** * Constructs an Register request payload: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Object Type Yes Enumeration v1.0 * Template-Attribute Yes(v1.x) Structure v1.x only * Attributes Yes(v2.x) Structure v2.x only * Yes Structure v1.0 * Protection Storage Masks No Structure v2.x * * @param version the protocol version. If null, the current default * protocol version is used. * @param obj_type the object type to register * @param object the object to register * @paran prot_storage_masks the protection storage masks (can be NULL) * @param attrs_count the number of attributes following (can be 0) * @param the attributes (as KMIP v2.x attribute) * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_register_request_payload_va( const struct kmip_version *version, enum kmip_object_type obj_type, struct kmip_node *object, struct kmip_node *prot_storage_masks, unsigned int attrs_count, ...) { struct kmip_node *ret, **attrs = NULL; unsigned int i, k; va_list ap; if (attrs_count > 0) { attrs = calloc(attrs_count, sizeof(struct kmip_node *)); if (attrs == NULL) return NULL; } va_start(ap, attrs_count); for (i = 0, k = 0; i < attrs_count; i++) { attrs[k] = va_arg(ap, struct kmip_node *); if (attrs[k] != NULL) k++; } va_end(ap); ret = kmip_new_register_request_payload(version, obj_type, object, prot_storage_masks, k, attrs); if (attrs != NULL) free(attrs); return ret; } /** * Constructs an Get request payload: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Unique Identifier No Text String v1.0 * Enumeration v2.0 * Integer v2.0 * Key Format Type No Enumeration v1.0 * Key Wrap Type No Enumeration v1.4 * Key Compression Type No Enumeration v1.0 * Key Wrapping Specification No Structure v1.0 * * @param version the protocol version. If null, the current default * protocol version is used. * @param unique_id the unique id of the object to get * @param format_type the format type of the key (ignored if 0) * @paran wrap_type the wrap type (ignored if 0) * @param compr_type the compression type (ignored if 0) * @param wrap_specification the key wrapping specification node (can be NULL) * * @returns the allocated node, or NULL in case of an error. * The reference counts of the nodes specified as parameters which are added to * the newly allocated node are increased. The caller must free its reference * via kmip_node_free() if no longer needed. */ struct kmip_node *kmip_new_get_request_payload( const struct kmip_version *version, struct kmip_node *unique_id, enum kmip_key_format_type format_type, enum kmip_key_wrap_type wrap_type, enum kmip_key_compression_type compr_type, struct kmip_node *wrap_specification) { struct kmip_node *rpl = NULL, *fmt = NULL, *wt = NULL, *cmpt = NULL; if (version == NULL) version = kmip_get_default_protocol_version(); if (format_type != 0) { fmt = kmip_node_new_enumeration(KMIP_TAG_KEY_FORMAT_TYPE, NULL, format_type); if (fmt == NULL) goto out; } if (wrap_type != 0 && (version->major > 1 || (version->major == 1 && version->minor > 3))) { wt = kmip_node_new_enumeration(KMIP_TAG_KEY_WRAP_TYPE, NULL, wrap_type); if (wt == NULL) goto out; } if (compr_type != 0) { cmpt = kmip_node_new_enumeration(KMIP_TAG_KEY_COMPRESSION_TYPE, NULL, compr_type); if (cmpt == NULL) goto out; } rpl = kmip_node_new_structure_va(KMIP_TAG_REQUEST_PAYLOAD, NULL, 5, unique_id, fmt, wt, cmpt, wrap_specification); out: kmip_node_free(fmt); kmip_node_free(wt); kmip_node_free(cmpt); return rpl; } s390-tools-2.38.0/libkmipclient/response.c000066400000000000000000001313631502674226300203060ustar00rootroot00000000000000/* * libkmipclient - KMIP client library * * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #define _XOPEN_SOURCE #define _DEFAULT_SOURCE #include #include #include "kmip.h" /** * Gets the version information from a Protocol Version node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Protocol Version Structure v1.0 * Protocol Version Major Yes Integer v1.0 * Protocol Version Minor Yes Integer v1.0 * * @param node the KMIP node * @param version On return: the protocol version * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_protocol_version(const struct kmip_node *node, struct kmip_version *version) { struct kmip_node *maj, *min; int rc = 0; if (node == NULL || version == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_PROTOCOL_VERSION) return -EBADMSG; maj = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_PROTOCOL_VERSION_MAJOR, 0); min = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_PROTOCOL_VERSION_MINOR, 0); if (maj == NULL || min == NULL) { rc = -EBADMSG; goto out; } version->major = kmip_node_get_integer(maj); version->minor = kmip_node_get_integer(min); out: kmip_node_free(maj); kmip_node_free(min); return rc; } /** * Gets the version information from a Profile Version node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Profile Version Structure v1.0 * Profile Version Major Yes Integer v1.0 * Profile Version Minor Yes Integer v1.0 * * @param node the KMIP node * @param version On return: the profile version * * @returns 0 on success, or a negative errno in case of an error */ int kmip_get_profile_version(const struct kmip_node *node, struct kmip_version *version) { struct kmip_node *maj, *min; int rc = 0; if (node == NULL || version == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_PROFILE_VERSION) return -EBADMSG; maj = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_PROFILE_VERSION_MAJOR, 0); min = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_PROFILE_VERSION_MINOR, 0); if (maj == NULL || min == NULL) { rc = -EBADMSG; goto out; } version->major = kmip_node_get_integer(maj); version->minor = kmip_node_get_integer(min); out: kmip_node_free(maj); kmip_node_free(min); return rc; } /** * Gets information from a Response Header node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Response Header Yes Structure v1.0 * Protocol Version Yes Structure v1.0 * Time Stamp No Date Time v1.0 * Nonce No Structure v1.2 * Server Hashed Password No Byte String v2.0 * Attestation Type No Enumeration v1.2 * ... may be repeated * Client Correlation Value No Text String v1.4 * Server Correlation Value No Text String v1.4 * Batch Count Yes Integer v1.0 * * @param node the KMIP node * @param version the protocol version (can be NULL) * @param time_stamp the time stamp (can be NULL) * @param client_corr_value the client correlation value. Can be NULL. * @param server_corr_value the server correlation value. Can be NULL. * @param batch_count the batch count (can be NULL) * * @returns 0 on success, or a negative errno in case of an error. * The reference count of the returned node is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ int kmip_get_response_header(const struct kmip_node *node, struct kmip_version *version, int64_t *time_stamp, const char **client_corr_value, const char **server_corr_value, int32_t *batch_count) { struct kmip_node *n; int rc; if (node == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_RESPONSE_HEADER) return -EBADMSG; if (time_stamp != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_TIME_STAMP, 0); if (n == NULL) return -EBADMSG; *time_stamp = kmip_node_get_date_time(n); kmip_node_free(n); } if (batch_count != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_BATCH_COUNT, 0); if (n == NULL) return -EBADMSG; *batch_count = kmip_node_get_integer(n); kmip_node_free(n); } if (version != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_PROTOCOL_VERSION, 0); if (n == NULL) return -EBADMSG; rc = kmip_get_protocol_version(n, version); kmip_node_free(n); if (rc != 0) return rc; } if (client_corr_value != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_CLIENT_CORRELATION_VALUE, 0); if (n == NULL) return -EBADMSG; *client_corr_value = kmip_node_get_text_string(n); kmip_node_free(n); } if (server_corr_value != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_SERVER_CORRELATION_VALUE, 0); if (n == NULL) return -EBADMSG; *server_corr_value = kmip_node_get_text_string(n); kmip_node_free(n); } return 0; } /** * Gets information from a Response Batch Item node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Batch Item Yes Structure v1.0 * Operation Yes Enumeration v1.0 * Unique Batch Item ID No Byte String v1.0 * Result Status Yes Enumeration v1.0 * Result Reason No/Yes Enumeration v1.0 * Result Message No/Yes Text String v1.0 * Asynchronous Correl. Value No/Yes Byte String v1.0 * Response Payload Yes Structure v1.0 * Message Extension No Structure v1.0 * * @param node the KMIP node * @param operation the operation (can be NULL) * @param batch_id the batch ID (can be NULL) * @param batch_id_length the batch ID length (can be NULL) * @param status the result status (can be NULL) * @param reason the result reason (can be NULL) * @param message the result message (can be NULL) * @param async_corr_value the asynchronous correlation value (can be NULL) * @param async_corr_value_len the length if the async corr. value (can be NULL) * @param payload the response payload (can be NULL) * * @returns 0 on success, or a negative errno in case of an error. * The reference count of the returned node is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ int kmip_get_response_batch_item(const struct kmip_node *node, enum kmip_operation *operation, const unsigned char **batch_id, uint32_t *batch_id_length, enum kmip_result_status *status, enum kmip_result_reason *reason, const char **message, const unsigned char **async_corr_value, uint32_t *async_corr_value_len, struct kmip_node **payload) { struct kmip_node *n; if (node == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_BATCH_ITEM) return -EBADMSG; if (operation != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_OPERATION, 0); if (n == NULL) return -EBADMSG; *operation = kmip_node_get_enumeration(n); kmip_node_free(n); } if (batch_id != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_UNIQUE_BATCH_ITEM_ID, 0); if (n == NULL) { *batch_id = NULL; if (batch_id_length != NULL) *batch_id_length = 0; } else { *batch_id = kmip_node_get_byte_string(n, batch_id_length); kmip_node_free(n); } } if (status != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_RESULT_STATUS, 0); if (n == NULL) return -EBADMSG; *status = kmip_node_get_enumeration(n); kmip_node_free(n); } if (reason != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_RESULT_REASON, 0); *reason = (n == NULL ? 0 : kmip_node_get_enumeration(n)); kmip_node_free(n); } if (message != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_RESULT_MESSAGE, 0); *message = (n == NULL ? NULL : kmip_node_get_text_string(n)); kmip_node_free(n); } if (async_corr_value != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_ASYNCHRONOUS_CORRELATION_VALUE, 0); if (n != NULL) { *async_corr_value = kmip_node_get_byte_string(n, async_corr_value_len); } else { *async_corr_value = NULL; if (async_corr_value_len != NULL) *async_corr_value_len = 0; } kmip_node_free(n); } if (payload != NULL) *payload = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_RESPONSE_PAYLOAD, 0); return 0; } /** * Gets information from a Response Message node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Response Message Yes Structure v1.0 * Response Header Yes Structure v1.0 * Batch Item Yes Structure v1.0 * ... may be repeated * * @param node the KMIP node * @param response_header the response header (can be NULL) * @param batch_index the index of the response batch item to return * @param batch_item the batch item (can be NULL) * @returns 0 on success, or a negative errno in case of an error. * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ int kmip_get_response(const struct kmip_node *node, struct kmip_node **response_header, unsigned int batch_index, struct kmip_node **batch_item) { if (node == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_RESPONSE_MESSAGE) return -EBADMSG; if (response_header != NULL) { *response_header = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_RESPONSE_HEADER, 0); if (*response_header == NULL) return -EBADMSG; } if (batch_item != NULL) *batch_item = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_BATCH_ITEM, batch_index); return 0; } struct kmip_query_info { enum kmip_query_function query_function; enum kmip_tag result_tag; }; static const struct kmip_query_info query_info[] = { { .query_function = KMIP_QUERY_OPERATIONS, .result_tag = KMIP_TAG_OPERATION, }, { .query_function = KMIP_QUERY_OBJECTS, .result_tag = KMIP_TAG_OBJECT_TYPE, }, { .query_function = KMIP_QUERY_SERVER_INFORMATION, .result_tag = KMIP_TAG_VENDOR_IDENTIFICATION, }, { .query_function = KMIP_QUERY_SERVER_INFORMATION, .result_tag = KMIP_TAG_SERVER_INFORMATION, }, { .query_function = KMIP_QUERY_APPLICATION_NAMESPACES, .result_tag = KMIP_TAG_APPLICATION_NAMESPACE, }, { .query_function = KMIP_QUERY_EXTENSION_LIST, .result_tag = KMIP_TAG_EXTENSION_INFORMATION, }, { .query_function = KMIP_QUERY_EXTENSION_MAP, .result_tag = KMIP_TAG_EXTENSION_INFORMATION, }, { .query_function = KMIP_QUERY_ATTESTATION_TYPES, .result_tag = KMIP_TAG_ATTESTATION_TYPE, }, { .query_function = KMIP_QUERY_QUERY_RNGS, .result_tag = KMIP_TAG_RNG_PARAMETERS, }, { .query_function = KMIP_QUERY_VALIDATIONS, .result_tag = KMIP_TAG_VALIDATION_INFORMATION, }, { .query_function = KMIP_QUERY_PROFILES, .result_tag = KMIP_TAG_PROFILE_INFORMATION, }, { .query_function = KMIP_QUERY_CAPABILITIES, .result_tag = KMIP_TAG_CAPABILITY_INFORMATION, }, { .query_function = KMIP_QUERY_CLIENT_REGISTRATION_METHODS, .result_tag = KMIP_TAG_CLIENT_REGISTRATION_METHOD, }, { .query_function = KMIP_QUERY_DEFAULTS_INFORMATION, .result_tag = KMIP_TAG_DEFAULTS_INFORMATION, }, { .query_function = KMIP_QUERY_STORAGE_PROTECTION_MASKS, .result_tag = KMIP_TAG_PROTECTION_STORAGE_MASKS, }, { .query_function = 0, .result_tag = 0, }, }; /** * Gets information from a Query response payload node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Operation No Enumeration v1.0 * ... may be repeated * Object Type No Enumeration v1.0 * ... may be repeated * Vendor Identification No Text String v1.0 * Server Information No Structure v1.0 * Application Namespace No Text String v1.0 * ... may be repeated * Extension Information No Structure v1.2 * ... may be repeated * Attestation Type No Enumeration v1.2 * ... may be repeated * RNG Parameters No Structure v1.3 * ... may be repeated * Profile Information No Structure v1.3 * ... may be repeated * Validation Information No Structure v1.3 * ... may be repeated * Capability Information No Structure v1.3 * ... may be repeated * Client Registration Method No Enumeration v1.3 * ... may be repeated * Defaults Information No Structure v2.0 * Protection Storage Masks No Structure v2.0 * * @param node the KMIP node * @param query_function the query function to get the results for * @param num_results On return: the number of result items of the * specified query function (can be NULL). * @param result_index the index of the query result item to return * @param result On return: the query result item of the specified * query function and index. Function returns -ENOENT * if no result is available. Can be NULL. * * @returns 0 on success, or a negative errno in case of an error. * The reference count of the returned node is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ int kmip_get_query_response_payload(const struct kmip_node *node, enum kmip_query_function query_function, unsigned int *num_results, unsigned int result_index, struct kmip_node **result) { enum kmip_tag result_tag = 0; unsigned int i; if (node == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_RESPONSE_PAYLOAD) return -EBADMSG; for (i = 0; query_info[i].query_function != 0; i++) { if (query_info[i].query_function == query_function) { result_tag = query_info[i].result_tag; break; } } if (result_tag == 0) return -EBADMSG; if (num_results != NULL) { *num_results = kmip_node_get_structure_element_by_tag_count( node, result_tag); /* * KMIP_QUERY_SERVER_INFORMATION may return 2 different result * tags, count both of them. */ if (query_function == KMIP_QUERY_SERVER_INFORMATION) { *num_results += kmip_node_get_structure_element_by_tag_count( node, KMIP_TAG_SERVER_INFORMATION); } } if (result == NULL) return 0; *result = kmip_node_get_structure_element_by_tag(node, result_tag, result_index); if (*result == NULL) { /* * KMIP_QUERY_SERVER_INFORMATION may return 2 different result * tags, return both of them. */ if (query_function == KMIP_QUERY_SERVER_INFORMATION) { if (result_index > 0) result_index -= 1; *result = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_SERVER_INFORMATION, result_index); if (*result != NULL) return 0; } return -ENOENT; } return 0; } /** * Gets information from a Discover Versions response payload node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Protocol Version No Structure v1.2 * ... may be repeated * * @param node the KMIP node * @param num_versions On return: the number of versions (can be NULL) * @param index the index of the version item to return * @param version On return: the version item of the specified * index. Function returns -ENOENT if no version is * available at that index. (can be NULL). * * @returns 0 on success, or a negative errno in case of an error. */ int kmip_get_discover_versions_response_payload(const struct kmip_node *node, unsigned int *num_versions, unsigned int index, struct kmip_version *version) { struct kmip_node *n; int rc; if (node == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_RESPONSE_PAYLOAD) return -EBADMSG; if (num_versions != NULL) *num_versions = kmip_node_get_structure_element_count(node); if (version == NULL) return 0; n = kmip_node_get_structure_element_by_index(node, index); if (n == NULL) return -ENOENT; rc = kmip_get_protocol_version(n, version); kmip_node_free(n); return rc; } /** * Gets information from a Create response payload node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Object Type Yes Enumeration v1.0 * Unique Identifier Yes Text String v1.0 * Enumeration v2.0 * Integer v2.0 * Template-Attribute No Structure v1.x only * * * @param node the KMIP node * @param obj_type the object type of the created object (can be NULL) * @param unique_id the unique id node (can be NULL) * @param num_attrs On return: the number of attributes (can be NULL). * @param attr_index the index of the attribute to get * @param attributes the attribute (implicitly set by the server) at the * specified index (as v2.x attributes) (can be NULL) * * @returns 0 on success, or a negative errno in case of an error. * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ int kmip_get_create_response_payload(const struct kmip_node *node, enum kmip_object_type *obj_type, struct kmip_node **unique_id, unsigned int *num_attrs, unsigned int attr_index, struct kmip_node **attribute) { struct kmip_node *n; int rc; if (node == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_RESPONSE_PAYLOAD) return -EBADMSG; if (obj_type != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_OBJECT_TYPE, 0); if (n == NULL) return -EBADMSG; *obj_type = kmip_node_get_enumeration(n); kmip_node_free(n); } if (unique_id != NULL) *unique_id = kmip_node_get_structure_element_by_tag( node, KMIP_TAG_UNIQUE_IDENTIFIER, 0); if (attribute == NULL && num_attrs == NULL) return 0; n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_TEMPLATE_ATTRIBUTE, 0); if (n == NULL) { if (num_attrs != NULL) *num_attrs = 0; if (attribute == NULL) return 0; rc = -ENOENT; goto error; } rc = kmip_get_attributes(n, num_attrs, attr_index, attribute); kmip_node_free(n); if (rc != 0) goto error; return 0; error: if (unique_id != NULL) { kmip_node_free(*unique_id); *unique_id = NULL; } return rc; } /** * Gets information from a Get Attribute List response payload node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Unique Identifier Yes Text String v1.0 * Enumeration v2.0 * Integer v2.0 * Attribute Name Yes Text String v1.x only * ... may be repeated * Attribute Reference Yes Enumeration v2.x only * Structure v2.x only * ... may be repeated * * @param node the KMIP node * @param unique_id the unique id node (can be NULL) * @param num_attr_refs On return: the number of attribute references * (can be NULL). * @param index the index of the attribute reference to get * @param attr_ref the attribute (as v2.x attribute reference) at the * specified index. Function returns -ENOENT if no * attribute is available at the index. * * @returns 0 on success, or a negative errno in case of an error. * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ int kmip_get_get_attribute_list_response_payload(const struct kmip_node *node, struct kmip_node **unique_id, unsigned int *num_attr_refs, unsigned int index, struct kmip_node **attr_ref) { struct kmip_node *n; int rc; if (node == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_RESPONSE_PAYLOAD) return -EBADMSG; if (unique_id != NULL) *unique_id = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_UNIQUE_IDENTIFIER, 0); if (num_attr_refs != NULL) *num_attr_refs = kmip_node_get_structure_element_count(node) - 1; if (attr_ref == NULL) return 0; n = kmip_node_get_structure_element_by_index(node, index + 1); if (n == NULL) { if (unique_id != NULL) { kmip_node_free(*unique_id); *unique_id = NULL; } return -ENOENT; } if (kmip_node_get_tag(n) == KMIP_TAG_ATTRIBUTE_REFERENCE) { /* Its already a KMIP v2.x attribute reference */ *attr_ref = n; return 0; } /* Must be a KMIP v1.x attribute name then */ rc = kmip_get_attribute_name_v1(n, attr_ref); kmip_node_free(n); if (rc != 0) { if (unique_id != NULL) { kmip_node_free(*unique_id); *unique_id = NULL; } return rc; } return 0; } /** * Gets information from a Get Attributes response payload node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Unique Identifier Yes Text String v1.0 * Enumeration v2.0 * Integer v2.0 * Attribute No Structure v1.x only * ... may be repeated * Attributes Yes Structure v2.x only * * @param node the KMIP node * @param unique_id the unique id node (can be NULL) * @param num_attrs On return: the number of attributes (can be NULL). * @param index the index of the attribute to get * @param v2_attr the attribute (as v2.x attribute) at the * specified index. Function returns -ENOENT if no * attribute is available at the index. * * @returns 0 on success, or a negative errno in case of an error. * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ int kmip_get_get_attributes_response_payload(const struct kmip_node *node, struct kmip_node **unique_id, unsigned int *num_attrs, unsigned int index, struct kmip_node **v2_attr) { struct kmip_node *attr; int rc; if (node == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_RESPONSE_PAYLOAD) return -EBADMSG; if (unique_id != NULL) *unique_id = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_UNIQUE_IDENTIFIER, 0); if (v2_attr == NULL && num_attrs == NULL) return 0; attr = kmip_node_get_structure_element_by_index(node, 1); if (attr == NULL) { if (num_attrs != NULL) *num_attrs = 0; if (v2_attr == NULL) return 0; rc = -ENOENT; goto error; } if (kmip_node_get_tag(attr) == KMIP_TAG_ATTRIBUTES) { /* Its already a KMIP v2.x attributes structure */ rc = kmip_get_attributes(attr, num_attrs, index, v2_attr); kmip_node_free(attr); if (rc != 0) goto error; return 0; } /* Must be a KMIP v1.x attribute then */ kmip_node_free(attr); if (num_attrs != NULL) *num_attrs = kmip_node_get_structure_element_count(node) - 1; if (v2_attr == NULL) return 0; attr = kmip_node_get_structure_element_by_index(node, index + 1); if (attr == NULL) { rc = -ENOENT; goto error; } rc = kmip_v2_attr_from_v1_attr(attr, v2_attr); kmip_node_free(attr); if (rc != 0) goto error; return 0; error: if (unique_id != NULL) { kmip_node_free(*unique_id); *unique_id = NULL; } return rc; } /** * Gets information from a response payload node that include a unique id and * an attribute. * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Unique Identifier Yes Text String v1.0 * Enumeration v2.0 * Integer v2.0 * Attribute Yes Structure v1.x only * * @param node the KMIP node * @param unique_id the unique id node (can be NULL) * @param v2_attr the added attribute (as v2.x attribute). * For KMIP v1.y no attribute is returned. * * @returns 0 on success, or a negative errno in case of an error. * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ static int kmip_get_unique_id_attribute_response_payload( const struct kmip_node *node, struct kmip_node **unique_id, struct kmip_node **v2_attr) { struct kmip_node *attr; int rc; if (node == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_RESPONSE_PAYLOAD) return -EBADMSG; if (unique_id != NULL) *unique_id = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_UNIQUE_IDENTIFIER, 0); if (v2_attr == NULL) return 0; /* KMIP v2.x does not send a attribute in the reply, but v1.x does */ attr = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_ATTRIBUTE, 0); if (attr == NULL) { *v2_attr = NULL; return 0; } rc = kmip_v2_attr_from_v1_attr(attr, v2_attr); kmip_node_free(attr); if (rc != 0) goto error; return 0; error: if (unique_id != NULL) { kmip_node_free(*unique_id); *unique_id = NULL; } return rc; } /** * Gets information from a Add Attribute response payload node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Unique Identifier Yes Text String v1.0 * Enumeration v2.0 * Integer v2.0 * Attribute Yes Structure v1.x only * * @param node the KMIP node * @param unique_id the unique id node (can be NULL) * @param v2_attr the added attribute (as v2.x attribute). * For KMIP v1.y no attribute is returned. * * @returns 0 on success, or a negative errno in case of an error. * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ int kmip_get_add_attribute_response_payload(const struct kmip_node *node, struct kmip_node **unique_id, struct kmip_node **v2_attr) { return kmip_get_unique_id_attribute_response_payload(node, unique_id, v2_attr); } /** * Gets information from a Modify Attribute response payload node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Unique Identifier Yes Text String v1.0 * Enumeration v2.0 * Integer v2.0 * Attribute Yes Structure v1.x only * * @param node the KMIP node * @param unique_id the unique id node (can be NULL) * @param v2_attr the modified attribute (as v2.x attribute). * For KMIP v1.y no attribute is returned. * * @returns 0 on success, or a negative errno in case of an error. * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ int kmip_get_modify_attribute_response_payload(const struct kmip_node *node, struct kmip_node **unique_id, struct kmip_node **v2_attr) { return kmip_get_unique_id_attribute_response_payload(node, unique_id, v2_attr); } /** * Gets information from a Set Attribute response payload node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Unique Identifier Yes Text String v1.0 * Enumeration v2.0 * Integer v2.0 * * KMIP v1.x does not have a Set Attribute operation. * * @param node the KMIP node * @param unique_id the unique id node (can be NULL) * * @returns 0 on success, or a negative errno in case of an error. * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ int kmip_get_set_attribute_v2_response_payload(const struct kmip_node *node, struct kmip_node **unique_id) { return kmip_get_unique_id_attribute_response_payload(node, unique_id, NULL); } /** * Gets information from a Delete Attribute response payload node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Unique Identifier Yes Text String v1.0 * Enumeration v2.0 * Integer v2.0 * Attribute Yes Structure v1.x only * * @param node the KMIP node * @param unique_id the unique id node (can be NULL) * @param v2_attr the modified attribute (as v2.x attribute). * For KMIP v1.y no attribute is returned. * * @returns 0 on success, or a negative errno in case of an error. * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ int kmip_get_delete_attribute_response_payload(const struct kmip_node *node, struct kmip_node **unique_id, struct kmip_node **v2_attr) { return kmip_get_unique_id_attribute_response_payload(node, unique_id, v2_attr); } /** * Gets information from a response payload node that only includes a unique id: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Unique Identifier Yes Text String v1.0 * Enumeration v2.0 * Integer v2.0 * * @param node the KMIP node * @param unique_id the unique id node (can be NULL) * * @returns 0 on success, or a negative errno in case of an error. * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ static int kmip_get_unique_id_response_payload(const struct kmip_node *node, struct kmip_node **unique_id) { if (node == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_RESPONSE_PAYLOAD) return -EBADMSG; if (unique_id != NULL) *unique_id = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_UNIQUE_IDENTIFIER, 0); return 0; } /** * Gets information from a Activate response payload node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Unique Identifier Yes Text String v1.0 * Enumeration v2.0 * Integer v2.0 * * @param node the KMIP node * @param unique_id the unique id node (can be NULL) * * @returns 0 on success, or a negative errno in case of an error. * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ int kmip_get_activate_response_payload(const struct kmip_node *node, struct kmip_node **unique_id) { return kmip_get_unique_id_response_payload(node, unique_id); } /** * Gets information from a Destroy response payload node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Unique Identifier Yes Text String v1.0 * Enumeration v2.0 * Integer v2.0 * * @param node the KMIP node * @param unique_id the unique id node (can be NULL) * * @returns 0 on success, or a negative errno in case of an error. * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ int kmip_get_destroy_response_payload(const struct kmip_node *node, struct kmip_node **unique_id) { return kmip_get_unique_id_response_payload(node, unique_id); } /** * Gets information from a Archive response payload node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Unique Identifier Yes Text String v1.0 * Enumeration v2.0 * Integer v2.0 * * @param node the KMIP node * @param unique_id the unique id node (can be NULL) * * @returns 0 on success, or a negative errno in case of an error. * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ int kmip_get_archive_response_payload(const struct kmip_node *node, struct kmip_node **unique_id) { return kmip_get_unique_id_response_payload(node, unique_id); } /** * Gets information from a Recover response payload node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Unique Identifier Yes Text String v1.0 * Enumeration v2.0 * Integer v2.0 * * @param node the KMIP node * @param unique_id the unique id node (can be NULL) * * @returns 0 on success, or a negative errno in case of an error. * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ int kmip_get_recover_response_payload(const struct kmip_node *node, struct kmip_node **unique_id) { return kmip_get_unique_id_response_payload(node, unique_id); } /** * Gets information from a Revoke response payload node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Unique Identifier Yes Text String v1.0 * Enumeration v2.0 * Integer v2.0 * * @param node the KMIP node * @param unique_id the unique id node (can be NULL) * * @returns 0 on success, or a negative errno in case of an error. * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ int kmip_get_revoke_response_payload(const struct kmip_node *node, struct kmip_node **unique_id) { return kmip_get_unique_id_response_payload(node, unique_id); } /** * Gets information from a Locate response payload node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Located Items No Integer v2.0 * Unique Identifier Yes Text String v1.0 * Enumeration v2.0 * Integer v2.0 * ... may be repated * * @param node the KMIP node * @param located_items On return: the total number of located items. * Only available since KMIP v2.x. If not available, * it is returned as -1. May be NULL. * @param num_items On return: the returned number of located items. * May be NULL. * @param index The index of the returned item. * @param unique_id the unique id node at the specified index. * Function returns -ENOENT if no item is available at * the index. May be NULL. * * @returns 0 on success, or a negative errno in case of an error. * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ int kmip_get_locate_response_payload(const struct kmip_node *node, int32_t *located_items, unsigned int *num_items, unsigned int index, struct kmip_node **unique_id) { struct kmip_node *n; if (node == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_RESPONSE_PAYLOAD) return -EBADMSG; if (located_items != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_LOCATED_ITEMS, 0); *located_items = (n != NULL ? kmip_node_get_integer(n) : -1); kmip_node_free(n); } if (num_items != NULL) *num_items = kmip_node_get_structure_element_by_tag_count(node, KMIP_TAG_UNIQUE_IDENTIFIER); if (unique_id != NULL) { *unique_id = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_UNIQUE_IDENTIFIER, index); if (*unique_id == NULL) return -ENOENT; } return 0; } /** * Gets information from a Register response payload node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Unique Identifier Yes Text String v1.0 * Enumeration v2.0 * Integer v2.0 * Template-Attribute No Structure v1.x only * * * @param node the KMIP node * @param obj_type the object type of the created object (can be NULL) * @param unique_id the unique id node (can be NULL) * @param num_attrs On return: the number of attributes (can be NULL). * @param attr_index the index of the attribute to get * @param attributes the attribute (implicitly set by the server) at the * specified index (as v2.x attributes) (can be NULL) * * @returns 0 on success, or a negative errno in case of an error. * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ int kmip_get_register_response_payload(const struct kmip_node *node, struct kmip_node **unique_id, unsigned int *num_attrs, unsigned int attr_index, struct kmip_node **attribute) { struct kmip_node *attrs; int rc; if (node == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_RESPONSE_PAYLOAD) return -EBADMSG; if (unique_id != NULL) *unique_id = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_UNIQUE_IDENTIFIER, 0); if (attribute == NULL && num_attrs == NULL) return 0; attrs = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_TEMPLATE_ATTRIBUTE, 0); if (attrs == NULL) { if (num_attrs != NULL) *num_attrs = 0; if (attribute == NULL) return 0; rc = -ENOENT; goto error; } rc = kmip_get_attributes(attrs, num_attrs, attr_index, attribute); kmip_node_free(attrs); if (rc != 0) goto error; return 0; error: if (unique_id != NULL) { kmip_node_free(*unique_id); *unique_id = NULL; } return rc; } /** * Gets information from a Get response payload node: * * Object Required Encoding KMIP version * --------------------------------------------------------------------- * Payload Yes Structure v1.0 * Object Type Yes Enumeration v1.0 * Unique Identifier Yes Text String v1.0 * Enumeration v2.0 * Integer v2.0 * Yes Structure v1.0 * * @param node the KMIP node * @param obj_type the object type of the created object (can be NULL) * @param unique_id the unique id node (can be NULL) * @param object the object (can be NULL) * * @returns 0 on success, or a negative errno in case of an error. * The reference count of the returned nodes is increased. The caller must * free the node via kmip_node_free() when no longer needed. */ int kmip_get_get_response_payload(const struct kmip_node *node, enum kmip_object_type *obj_type, struct kmip_node **unique_id, struct kmip_node **object) { struct kmip_node *n; int rc = 0; if (node == NULL) return -EINVAL; if (kmip_node_get_tag(node) != KMIP_TAG_RESPONSE_PAYLOAD) return -EBADMSG; if (obj_type != NULL) { n = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_OBJECT_TYPE, 0); if (n == NULL) return -EBADMSG; *obj_type = kmip_node_get_enumeration(n); kmip_node_free(n); } if (unique_id != NULL) { *unique_id = kmip_node_get_structure_element_by_tag(node, KMIP_TAG_UNIQUE_IDENTIFIER, 0); if (*unique_id == NULL) return -EBADMSG; } if (object == NULL) return 0; *object = kmip_node_get_structure_element_by_index(node, 2); if (*object == NULL) { rc = -EBADMSG; goto error; } switch (kmip_node_get_tag(*object)) { case KMIP_TAG_CERTIFICATE: case KMIP_TAG_CERTIFICATE_REQUEST: case KMIP_TAG_OPAQUE_OBJECT: case KMIP_TAG_PGP_KEY: case KMIP_TAG_PRIVATE_KEY: case KMIP_TAG_PUBLIC_KEY: case KMIP_TAG_SECRET_DATA: case KMIP_TAG_SYMMETRIC_KEY: break; default: kmip_node_free(*object); *object = NULL; rc = -EBADMSG; goto error; } return 0; error: if (unique_id != NULL && *unique_id != NULL) { kmip_node_free(*unique_id); *unique_id = NULL; } return rc; } s390-tools-2.38.0/libkmipclient/tls.c000066400000000000000000000305671502674226300172560ustar00rootroot00000000000000/* * libkmipclient - KMIP client library * * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include "kmip.h" #include "utils.h" #ifndef OPENSSL_VERSION_PREREQ #if defined(OPENSSL_VERSION_MAJOR) && defined(OPENSSL_VERSION_MINOR) #define OPENSSL_VERSION_PREREQ(maj, min) \ ((OPENSSL_VERSION_MAJOR << 16) + \ OPENSSL_VERSION_MINOR >= ((maj) << 16) + (min)) #else #define OPENSSL_VERSION_PREREQ(maj, min) \ (OPENSSL_VERSION_NUMBER >= (((maj) << 28) | \ ((min) << 20))) #endif #endif /** * Verify the pinned public key of the server of a plain TLS KMIP connection * * @param conn the KMIP connection to free * @param cert_pubkey the server certificate's public key * @param debug if true, debug messages are printed */ static int kmip_connection_tls_verify_pinned_pubkey( struct kmip_connection *conn, EVP_PKEY *cert_pubkey, bool debug) { EVP_PKEY *pinned_key = NULL; int rc = 0; FILE *fp; fp = fopen(conn->config.tls_pinned_pubkey, "r"); if (fp == NULL) { rc = -errno; kmip_debug(debug, "Failed to read pinned public key '%s': %s", conn->config.tls_pinned_pubkey, strerror(-rc)); return rc; } pinned_key = PEM_read_PUBKEY(fp, NULL, NULL, NULL); fclose(fp); if (pinned_key == NULL) { kmip_debug(debug, "PEM_read_PUBKEY failed: '%s'", conn->config.tls_pinned_pubkey); if (debug) ERR_print_errors_fp(stderr); return -EIO; } #if !OPENSSL_VERSION_PREREQ(3, 0) if (EVP_PKEY_cmp(pinned_key, cert_pubkey) != 1) { #else if (EVP_PKEY_eq(pinned_key, cert_pubkey) != 1) { #endif kmip_debug(debug, "Server public key does not match the pinned " "public key '%s'", conn->config.tls_pinned_pubkey); rc = -EPERM; } EVP_PKEY_free(pinned_key); return rc; } /** * Verify the pinned server certificate key of the server of a plain TLS KMIP * connection * * @param conn the KMIP connection to free * @param server_cert the server certificate * @param debug if true, debug messages are printed */ static int kmip_connection_tls_verify_pinned_cert( struct kmip_connection *conn, X509 *server_cert, bool debug) { X509 *pinned_cert = NULL; int rc = 0; FILE *fp; fp = fopen(conn->config.tls_server_cert, "r"); if (fp == NULL) { rc = -errno; kmip_debug(debug, "Failed to read pinned server cert: %s", conn->config.tls_server_cert, strerror(-rc)); return rc; } pinned_cert = PEM_read_X509(fp, NULL, NULL, NULL); fclose(fp); if (pinned_cert == NULL) { kmip_debug(debug, "PEM_read_X509 failed: '%s'", conn->config.tls_server_cert); if (debug) ERR_print_errors_fp(stderr); return -EIO; } if (X509_cmp(pinned_cert, server_cert) != 0) { kmip_debug(debug, "Server certificate does not match the " "pinned certificate '%s'", conn->config.tls_server_cert); rc = -EPERM; } X509_free(pinned_cert); return rc; } /** * Verify the issuer certificate key of the server of a plain TLS KMIP * connection * * @param conn the KMIP connection to free * @param server_cert the server certificate * @param debug if true, debug messages are printed */ static int kmip_connection_tls_verify_issuer_cert( struct kmip_connection *conn, X509 *server_cert, bool debug) { X509 *issuer_cert = NULL; int rc = 0; FILE *fp; fp = fopen(conn->config.tls_issuer_cert, "r"); if (fp == NULL) { rc = -errno; kmip_debug(debug, "Failed to read issuer cert '%s': %s", conn->config.tls_issuer_cert, strerror(-rc)); return rc; } issuer_cert = PEM_read_X509(fp, NULL, NULL, NULL); fclose(fp); if (issuer_cert == NULL) { kmip_debug(debug, "PEM_read_X509 failed: '%s'", conn->config.tls_issuer_cert); if (debug) ERR_print_errors_fp(stderr); return -EIO; } if (X509_check_issued(issuer_cert, server_cert) != X509_V_OK) { kmip_debug(debug, "The server certificate was not issued by " "certificate '%s'", conn->config.tls_issuer_cert); rc = -EPERM; } X509_free(issuer_cert); return rc; } /** * Verify the server of a plain TLS KMIP connection * * @param conn the KMIP connection to free * @param debug if true, debug messages are printed */ static int kmip_connection_tls_verify_server(struct kmip_connection *conn, bool debug) { X509 *server_cert; int rc; server_cert = SSL_get_peer_certificate(conn->plain_tls.ssl); if (server_cert == NULL) { kmip_debug(debug, "SSL_get_peer_certificate failed"); if (debug) ERR_print_errors_fp(stderr); rc = -EIO; goto out; } if (conn->config.tls_issuer_cert != NULL) { rc = kmip_connection_tls_verify_issuer_cert(conn, server_cert, debug); if (rc != 0) { kmip_debug(debug, "kmip_connection_tls_verify_issuer_cert " "failed"); goto out; } } if (conn->config.tls_server_cert != NULL) { rc = kmip_connection_tls_verify_pinned_cert(conn, server_cert, debug); if (rc != 0) { kmip_debug(debug, "kmip_connection_tls_verify_pinned_cert " "failed"); goto out; } } if (conn->config.tls_pinned_pubkey != NULL) { rc = kmip_connection_tls_verify_pinned_pubkey(conn, X509_get0_pubkey(server_cert), debug); if (rc != 0) { kmip_debug(debug, "kmip_connection_tls_pinned_pubkey failed"); goto out; } } rc = 0; out: if (server_cert != NULL) X509_free(server_cert); return 0; } /** * Initializes a new plain TLS connection to a KMIP server. * * @param conn The KMIP connection * @param debug if true, debug messages are printed * * @returns 0 in case of success, or a negative errno value */ int kmip_connection_tls_init(struct kmip_connection *conn, bool debug) { char *hostname = NULL, *port = NULL, *tok; struct stat sb; int rc; if (conn == NULL) return -EINVAL; conn->plain_tls.ssl_ctx = SSL_CTX_new(TLS_client_method()); if (conn->plain_tls.ssl_ctx == NULL) { kmip_debug(debug, "SSL_CTX_new failed"); if (debug) ERR_print_errors_fp(stderr); return -EIO; } if (SSL_CTX_use_certificate_file(conn->plain_tls.ssl_ctx, conn->config.tls_client_cert, SSL_FILETYPE_PEM) != 1) { kmip_debug(debug, "Loading the client certificate from '%s' " "failed", conn->config.tls_client_cert); if (debug) ERR_print_errors_fp(stderr); rc = -EIO; goto out; } if (SSL_CTX_use_PrivateKey(conn->plain_tls.ssl_ctx, conn->config.tls_client_key) != 1) { kmip_debug(debug, "Setting the client key from PKEY %p " "failed", conn->config.tls_client_key); if (debug) ERR_print_errors_fp(stderr); rc = -EIO; goto out; } if (conn->config.tls_ca != NULL) { if (stat(conn->config.tls_ca, &sb) != 0) { rc = -errno; kmip_debug(debug, "stat failed on '%s': %s", conn->config.tls_ca, strerror(-rc)); goto out; } if (S_ISDIR(sb.st_mode)) { if (SSL_CTX_load_verify_locations( conn->plain_tls.ssl_ctx, NULL, conn->config.tls_ca) != 1) { kmip_debug(debug, "Setting the verify location " "to '%s' failed", conn->config.tls_ca); if (debug) ERR_print_errors_fp(stderr); rc = -EIO; goto out; } } else { if (SSL_CTX_load_verify_locations( conn->plain_tls.ssl_ctx, conn->config.tls_ca, NULL) != 1) { kmip_debug(debug, "Setting the verify location " "to '%s' failed", conn->config.tls_ca); if (debug) ERR_print_errors_fp(stderr); rc = -EIO; goto out; } } } conn->plain_tls.bio = BIO_new_buffer_ssl_connect(conn->plain_tls.ssl_ctx); if (conn->plain_tls.bio == NULL) { kmip_debug(debug, "BIO_new_ssl_connect failed"); if (debug) ERR_print_errors_fp(stderr); rc = -EIO; goto out; } BIO_get_ssl(conn->plain_tls.bio, &conn->plain_tls.ssl); if (conn->plain_tls.ssl == NULL) { kmip_debug(debug, "BIO_get_ssl failed"); if (debug) ERR_print_errors_fp(stderr); rc = -EIO; goto out; } hostname = strdup(conn->config.server); if (hostname == NULL) { kmip_debug(debug, "strdup failed"); rc = -ENOMEM; goto out; } /* Split port number from hostname, if specified */ if (hostname[0] == '[') { /* IPv6 address enclosed in square brackets */ tok = strchr(hostname, ']'); if (tok == NULL) { kmip_debug(debug, "malformed IPv6 address"); rc = -EINVAL; goto out; } tok++; if (*tok == ':') { port = tok + 1; *tok = 0; } } else { /* hostname or IPv4 address */ tok = strchr(hostname, ':'); if (tok != NULL) { port = tok + 1; *tok = 0; } } kmip_debug(debug, "hostname: '%s'", hostname); if (port == NULL) { port = KMIP_DEFAULT_PLAIN_TLS_PORT; kmip_debug(debug, "port: default (%s)", port); } else { kmip_debug(debug, "port: %s", port); } if (conn->config.tls_verify_host) { SSL_set_hostflags(conn->plain_tls.ssl, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); if (SSL_set1_host(conn->plain_tls.ssl, hostname) != 1) { kmip_debug(debug, "SSL_set1_host failed"); if (debug) ERR_print_errors_fp(stderr); rc = -EIO; goto out; } } SSL_set_verify(conn->plain_tls.ssl, (conn->config.tls_verify_peer || conn->config.tls_verify_host) ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL); if (conn->config.tls_cipher_list != NULL) { if (SSL_set_cipher_list(conn->plain_tls.ssl, conn->config.tls_cipher_list) != 1) { kmip_debug(debug, "SSL_set_cipher_list failed"); if (debug) ERR_print_errors_fp(stderr); rc = -EIO; goto out; } } if (conn->config.tls13_cipher_list != NULL) { if (SSL_set_ciphersuites(conn->plain_tls.ssl, conn->config.tls13_cipher_list) != 1) { kmip_debug(debug, "SSL_set_ciphersuites failed"); if (debug) ERR_print_errors_fp(stderr); rc = -EIO; goto out; } } SSL_set_mode(conn->plain_tls.ssl, SSL_MODE_AUTO_RETRY); BIO_set_conn_hostname(conn->plain_tls.bio, hostname); BIO_set_conn_port(conn->plain_tls.bio, port); if (BIO_do_connect(conn->plain_tls.bio) != 1) { kmip_debug(debug, "BIO_do_connect failed"); if (debug) ERR_print_errors_fp(stderr); rc = -EIO; goto out; } kmip_debug(debug, "TLS connection established using %s", SSL_get_cipher_name(conn->plain_tls.ssl)); rc = kmip_connection_tls_verify_server(conn, debug); if (rc != 0) { kmip_debug(debug, "kmip_connection_tls_verify_server failed"); if (debug) ERR_print_errors_fp(stderr); rc = -EIO; goto out; } rc = 0; out: if (rc != 0) kmip_connection_tls_term(conn); if (hostname != NULL) free(hostname); return rc; } /** * Perform a request over the KMIP connection * * @param conn n the KMIP connection * @param request the request to send * @param response On return: the received response. Must be freed by * the caller. * * @param debug if true, debug messages are printed * * @returns 0 in case of success, or a negative errno value */ int kmip_connection_tls_perform(struct kmip_connection *conn, struct kmip_node *request, struct kmip_node **response, bool debug) { size_t size; int rc; if (conn == NULL || request == NULL || response == NULL) return -EINVAL; *response = NULL; /* Send out the request */ rc = kmip_encode_ttlv(request, conn->plain_tls.bio, &size, debug); if (rc != 0) { kmip_debug(debug, "kmip_encode_ttlv failed"); goto out; } if (BIO_flush(conn->plain_tls.bio) != 1) { kmip_debug(debug, "BIO_flush failed"); goto out; } kmip_debug(debug, "%lu bytes sent", size); /* receive the response */ rc = kmip_decode_ttlv(conn->plain_tls.bio, NULL, response, debug); if (rc != 0 || *response == NULL) { kmip_debug(debug, "kmip_decode_ttlv failed"); goto out; } rc = 0; out: if (rc != 0) { if (BIO_reset(conn->plain_tls.bio) != 1) kmip_debug(debug, "BIO_reset failed"); } return rc; } /** * Terminates a plain TLS KMIP connection. * * @param conn the KMIP connection to free */ void kmip_connection_tls_term(struct kmip_connection *conn) { if (conn == NULL) return; if (conn->plain_tls.bio != NULL) { BIO_ssl_shutdown(conn->plain_tls.bio); BIO_free_all(conn->plain_tls.bio); } if (conn->plain_tls.ssl_ctx != NULL) SSL_CTX_free(conn->plain_tls.ssl_ctx); conn->plain_tls.bio = NULL; conn->plain_tls.ssl_ctx = NULL; conn->plain_tls.ssl = NULL; } s390-tools-2.38.0/libkmipclient/ttlv.c000066400000000000000000000271431502674226300174410ustar00rootroot00000000000000/* * libkmipclient - KMIP client library * * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include "kmip.h" #include "utils.h" #define KMIP_TTLV_HEADER_LENGTH 8 #define KMIP_TTLV_BLOCK_LENGTH 8 /** * Decode a KMIP node from the data in BIO using the TTLV encoding. * * @param bio the OpenSSL bio to read the data from * @param size Optional: If not NULL: * On entry: The number of bytes available to read * On return: decremented by the number of bytes read * If NULL, it is assumed that we can read from bio * as many bytes as needed. * @param node On return: the decoded node. The newly allocated * node has a reference count of 1. * @param debug if true, debug messages are printed * * @returns 0 in case of success, or a negative errno value */ int kmip_decode_ttlv(BIO *bio, size_t *size, struct kmip_node **node, bool debug) { unsigned char padding[KMIP_TTLV_BLOCK_LENGTH]; unsigned char ttlv[KMIP_TTLV_HEADER_LENGTH]; size_t value_len, pad_len; struct kmip_node *n, *e; void *value = NULL; uint32_t int32; uint64_t int64; int rc; if (bio == NULL || node == NULL) return -EINVAL; if (size != NULL) kmip_debug(debug, "size: %lu", *size); else kmip_debug(debug, "size: unknown"); if (size != NULL && *size < sizeof(ttlv)) { kmip_debug(debug, "length %u > available size %lu", sizeof(ttlv), *size); return -EMSGSIZE; } if (BIO_read(bio, ttlv, sizeof(ttlv)) != sizeof(ttlv)) { kmip_debug(debug, "BIO_read failed"); return -EIO; } if (size != NULL) *size -= sizeof(ttlv); n = calloc(1, sizeof(struct kmip_node)); if (n == NULL) { kmip_debug(debug, "calloc failed"); return -ENOMEM; } n->ref_count = 1; /* Tag: 3-byte binary unsigned integer, transmitted big endian */ n->tag |= (uint32_t)(ttlv[0] << 16); n->tag |= (uint32_t)(ttlv[1] << 8); n->tag |= (uint32_t)(ttlv[2]); /* Type: 1 byte containing a coded value that indicates the data type */ n->type = ttlv[3]; /* Length: 32-bit binary integer, transmitted big-endian */ n->length |= (uint32_t)(ttlv[4] << 24); n->length |= (uint32_t)(ttlv[5] << 16); n->length |= (uint32_t)(ttlv[6] << 8); n->length |= (uint32_t)(ttlv[7]); kmip_debug(debug, "tag: 0x%x type: 0x%x, length: %u", n->tag, n->type, n->length); switch (n->type) { case KMIP_TYPE_STRUCTURE: value_len = n->length; break; case KMIP_TYPE_BIG_INTEGER: case KMIP_TYPE_TEXT_STRING: case KMIP_TYPE_BYTE_STRING: value_len = n->length; value = calloc(1, value_len + 1); if (value == NULL) { kmip_debug(debug, "calloc failed"); rc = -ENOMEM; goto out; } break; case KMIP_TYPE_INTEGER: case KMIP_TYPE_ENUMERATION: case KMIP_TYPE_INTERVAL: value_len = sizeof(int32); value = &int32; break; case KMIP_TYPE_LONG_INTEGER: case KMIP_TYPE_BOOLEAN: case KMIP_TYPE_DATE_TIME: case KMIP_TYPE_DATE_TIME_EXTENDED: value_len = sizeof(int64); value = &int64; break; default: kmip_debug(debug, "unknown type: 0x%x", n->type); rc = -EBADMSG; goto out; } if (n->length != value_len) { kmip_debug(debug, "length %u not as expected (%lu)", n->length, value_len); rc = -EBADMSG; goto out; } if (size != NULL && *size < n->length) { kmip_debug(debug, "length %u > available size %lu", n->length, *size); rc = -EMSGSIZE; goto out; } if (n->type != KMIP_TYPE_STRUCTURE && value_len > 0) { if (BIO_read(bio, value, value_len) != (int)value_len) { kmip_debug(debug, "BIO_read failed"); rc = -EIO; goto out; } } if (size != NULL) *size -= value_len; if ((value_len % KMIP_TTLV_BLOCK_LENGTH) != 0) { pad_len = KMIP_TTLV_BLOCK_LENGTH - (value_len % KMIP_TTLV_BLOCK_LENGTH); kmip_debug(debug, "pad_len: %lu", pad_len); if (BIO_read(bio, padding, pad_len) != (int)pad_len) { kmip_debug(debug, "BIO_read failed (padding)"); rc = -EIO; goto out; } if (size != NULL) *size -= pad_len; } switch (n->type) { case KMIP_TYPE_STRUCTURE: while (value_len > 0) { rc = kmip_decode_ttlv(bio, &value_len, &e, debug); if (rc != 0) { kmip_debug(debug, "kmip_decode_ttlv failed: " "rc: %d", rc); goto out; } rc = kmip_node_add_structure_element(n, e); kmip_node_free(e); if (rc != 0) { kmip_debug(debug, "kmip_node_structure_add_element " "failed: rc: %d", rc); goto out; } } break; case KMIP_TYPE_INTEGER: n->integer_value = be32toh(int32); break; case KMIP_TYPE_LONG_INTEGER: n->long_value = be64toh(int64); break; case KMIP_TYPE_BIG_INTEGER: rc = kmip_decode_bignum(value, value_len, &n->big_integer_value); if (rc != 0) { kmip_debug(debug, "kmip_decode_bignum failed"); goto out; } free(value); value = NULL; break; case KMIP_TYPE_ENUMERATION: n->enumeration_value = be32toh(int32); break; case KMIP_TYPE_BOOLEAN: n->boolean_value = int64 != 0; break; case KMIP_TYPE_TEXT_STRING: n->text_value = value; break; case KMIP_TYPE_BYTE_STRING: n->bytes_value = value; break; case KMIP_TYPE_DATE_TIME: n->date_time_value = be64toh(int64); break; case KMIP_TYPE_INTERVAL: n->interval_value = be32toh(int32); break; case KMIP_TYPE_DATE_TIME_EXTENDED: n->date_time_ext_value = be64toh(int64); break; default: kmip_debug(debug, "unknown type: 0x%x", n->type); rc = -EBADMSG; goto out; } *node = n; rc = 0; out: if (rc != 0) { switch (n->type) { case KMIP_TYPE_BIG_INTEGER: case KMIP_TYPE_TEXT_STRING: case KMIP_TYPE_BYTE_STRING: free(value); break; default: break; } kmip_node_free(n); } return rc; } /** * Gets the length of the value part of a KMIP node (in TTLV encoding) */ static int kmip_node_get_length(struct kmip_node *node, size_t *length) { struct kmip_node *element; size_t len; int rc; if (node == NULL || length == NULL) return -EINVAL; switch (node->type) { case KMIP_TYPE_STRUCTURE: *length = 0; element = node->structure_value; while (element != NULL) { rc = kmip_node_get_length(element, &len); if (rc != 0) return rc; *length += KMIP_TTLV_HEADER_LENGTH + len; if ((len % KMIP_TTLV_BLOCK_LENGTH) != 0) *length += KMIP_TTLV_BLOCK_LENGTH - (len % KMIP_TTLV_BLOCK_LENGTH); element = element->next; } break; case KMIP_TYPE_INTEGER: case KMIP_TYPE_ENUMERATION: case KMIP_TYPE_INTERVAL: *length = sizeof(int32_t); break; case KMIP_TYPE_LONG_INTEGER: case KMIP_TYPE_BOOLEAN: case KMIP_TYPE_DATE_TIME: case KMIP_TYPE_DATE_TIME_EXTENDED: *length = sizeof(int64_t); break; case KMIP_TYPE_BIG_INTEGER: *length = kmip_encode_bignum_length(node->big_integer_value); /* BIG INTEGERS must be a multiple of 8 bytes long */ if ((*length % KMIP_BIG_INTEGER_BLOCK_LENGTH) != 0) *length += KMIP_BIG_INTEGER_BLOCK_LENGTH - (*length % KMIP_BIG_INTEGER_BLOCK_LENGTH); break; case KMIP_TYPE_BYTE_STRING: *length = node->length; break; case KMIP_TYPE_TEXT_STRING: if (node->text_value != NULL) *length = strlen(node->text_value); else *length = 0; break; default: return -EINVAL; } return 0; } /** * Encode a KMIP node into a BIO using the TTLV encoding. * * @param node the node to encode * @param bio the OpenSSL bio to write the data to * @param size On return: the number of bytes written to BIO * @param debug if true, debug messages are printed * * @returns 0 in case of success, or a negative errno value */ int kmip_encode_ttlv(struct kmip_node *node, BIO *bio, size_t *size, bool debug) { const unsigned char padding[KMIP_TTLV_BLOCK_LENGTH] = { 0 }; unsigned char ttlv[KMIP_TTLV_HEADER_LENGTH]; size_t len, elem_len, value_len, pad_len; struct kmip_node *element; void *value = NULL; uint32_t int32; uint64_t int64; int rc; if (bio == NULL || node == NULL || size == NULL) return -EINVAL; kmip_debug(debug, "tag: 0x%x type: 0x%x, length: %u", node->tag, node->type, node->length); *size = 0; /* Update node's length field to match node's current data */ rc = kmip_node_get_length(node, &len); if (rc != 0) { kmip_debug(debug, "kmip_node_get_length failed"); return rc; } node->length = len; /* Tag: 3-byte binary unsigned integer, transmitted big endian */ ttlv[0] = (node->tag & 0xff0000) >> 16; ttlv[1] = (node->tag & 0xff00) >> 8; ttlv[2] = (node->tag & 0xff); /* Type: 1 byte containing a coded value that indicates the data type */ ttlv[3] = node->type; /* Length: 32-bit binary integer, transmitted big-endian */ ttlv[4] = (node->length & 0xff000000) >> 24; ttlv[5] = (node->length & 0xff0000) >> 16; ttlv[6] = (node->length & 0xff00) >> 8; ttlv[7] = (node->length & 0xff); if (BIO_write(bio, ttlv, sizeof(ttlv)) != sizeof(ttlv)) { kmip_debug(debug, "BIO_write failed"); return -EIO; } *size += sizeof(ttlv); switch (node->type) { case KMIP_TYPE_STRUCTURE: value_len = 0; element = node->structure_value; while (element != NULL) { rc = kmip_encode_ttlv(element, bio, &elem_len, debug); if (rc != 0) { kmip_debug(debug, "kmip_encode_ttlv failed"); return rc; } value_len += elem_len; element = element->next; } if (value_len != node->length) { kmip_debug(debug, "written length %lu not as expected " "(%u)", len, node->length); return -EIO; } break; case KMIP_TYPE_INTEGER: int32 = htobe32(node->integer_value); value_len = sizeof(int32); value = &int32; break; case KMIP_TYPE_LONG_INTEGER: int64 = htobe64(node->long_value); value_len = sizeof(int64); value = &int64; break; case KMIP_TYPE_BIG_INTEGER: value_len = node->length; /* was already calculated above */ value = malloc(value_len); if (value == NULL) { kmip_debug(debug, "malloc failed"); return -ENOMEM; } rc = kmip_encode_bignum(node->big_integer_value, value, value_len); if (rc != 0) { kmip_debug(debug, "kmip_encode_bignum failed"); goto out; } break; case KMIP_TYPE_ENUMERATION: int32 = htobe32(node->enumeration_value); value_len = sizeof(int32); value = &int32; break; case KMIP_TYPE_BOOLEAN: int64 = node->boolean_value ? 1 : 0; value_len = sizeof(int64); value = &int64; break; case KMIP_TYPE_TEXT_STRING: value_len = node->length; value = node->text_value; break; case KMIP_TYPE_BYTE_STRING: value_len = node->length; value = node->bytes_value; break; case KMIP_TYPE_DATE_TIME: int64 = htobe64(node->date_time_value); value_len = sizeof(int64); value = &int64; break; case KMIP_TYPE_INTERVAL: int32 = htobe32(node->interval_value); value_len = sizeof(int32); value = &int32; break; case KMIP_TYPE_DATE_TIME_EXTENDED: int64 = htobe64(node->date_time_ext_value); value_len = sizeof(int64); value = &int64; break; default: kmip_debug(debug, "unknown type: 0x%x", node->type); return -EINVAL; } if (value != NULL) { if (BIO_write(bio, value, value_len) != (int)value_len) { kmip_debug(debug, "BIO_write failed"); rc = -EIO; goto out; } } *size += value_len; if ((value_len % KMIP_TTLV_BLOCK_LENGTH) != 0) { pad_len = KMIP_TTLV_BLOCK_LENGTH - (value_len % KMIP_TTLV_BLOCK_LENGTH); kmip_debug(debug, "pad_len: %lu", pad_len); if (BIO_write(bio, padding, pad_len) != (int)pad_len) { kmip_debug(debug, "BIO_write failed (padding)"); rc = -EIO; goto out; } *size += pad_len; } kmip_debug(debug, "size: %lu", *size); rc = 0; out: if (node->type == KMIP_TYPE_BIG_INTEGER) free(value); return rc; } s390-tools-2.38.0/libkmipclient/utils.c000066400000000000000000000355671502674226300176210ustar00rootroot00000000000000/* * libkmipclient - KMIP client library * * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #define _XOPEN_SOURCE #define _DEFAULT_SOURCE #include #include #include #include #include #include "utils.h" #include "names.h" /** * Print a debug message */ void kmip_print_debug(const char *func, const char *fmt, ...) { char tmp_fmt[200]; va_list ap; if (snprintf(tmp_fmt, sizeof(tmp_fmt), "DBG: %s: %s", func, fmt) > (int)sizeof(tmp_fmt)) return; va_start(ap, fmt); vwarnx(tmp_fmt, ap); va_end(ap); } /** * Parse a decimal string into a 64 bit signed value */ int kmip_parse_decimal_int(const char *str, int64_t *val) { long long v; char *endptr; if (str == NULL) return -EINVAL; errno = 0; v = strtoll(str, &endptr, 10); if ((errno == ERANGE && (v == LLONG_MAX || v == LLONG_MIN)) || (errno != 0 && v == 0)) return -EBADMSG; if (endptr == str || *endptr != 0) return -EBADMSG; *val = v; return 0; } /** * Parse a decimal string into a 64 bit unsigned value */ int kmip_parse_decimal_uint(const char *str, uint64_t *val) { unsigned long long v; char *endptr; if (str == NULL) return -EINVAL; errno = 0; v = strtoull(str, &endptr, 10); if ((errno == ERANGE && (v == 0 || v == ULLONG_MAX)) || (errno != 0 && v == 0)) return -EBADMSG; if (endptr == str || *endptr != 0) return -EBADMSG; *val = v; return 0; } /** * Parse a hex string into a 64 bit signed value */ int kmip_parse_hex_int(const char *str, int64_t *val) { long long v; char *endptr; if (str == NULL) return -EINVAL; if (strncmp(str, "0x", 2) != 0) return -EBADMSG; errno = 0; v = strtoll(str, &endptr, 16); if ((errno == ERANGE && (v == LLONG_MAX || v == LLONG_MIN)) || (errno != 0 && v == 0)) return -EBADMSG; if (endptr == str || *endptr != 0) return -EBADMSG; *val = v; return 0; } /** * Parse a hex string into a variable length signed big integer. * On return, val and length is set. The buffer returned in val must be freed * by the caller. */ int kmip_parse_hex(const char *str, bool has_prefix, unsigned char **val, uint32_t *length) { unsigned char *buf; BIGNUM *b = NULL; int len; if (str == NULL) return -EINVAL; if (has_prefix && strncmp(str, "0x", 2) != 0) return -EBADMSG; len = BN_hex2bn(&b, str + (has_prefix ? 2 : 0)); if (len <= 0) return -EBADMSG; if (len < (int)strlen(str) - (has_prefix ? 2 : 0)) return -EBADMSG; len = len / 2 + (len % 2 > 0 ? 1 : 0); buf = calloc(1, len); if (buf == NULL) { BN_free(b); return -ENOMEM; } if (BN_bn2binpad(b, buf, len) != len) { BN_free(b); free(buf); return -EIO; } *val = buf; *length = len; BN_free(b); return 0; } /** * Format a hex string from the byte array specified in val. The caller must * free the returned str. */ int kmip_format_hex(const unsigned char *val, uint32_t length, bool prefix, char **str) { uint32_t str_len, i; char tmp[4]; char *ret; str_len = length * 2 + (prefix ? 2 : 0) + 1; ret = calloc(1, str_len); if (ret == NULL) return -ENOMEM; if (prefix) strcat(ret, "0x"); for (i = 0; i < length; i++) { sprintf(tmp, "%02x", val[i]); strcat(ret, tmp); } *str = ret; return 0; } /** * Parse a hex string into a big number. * On return, val and length is set. The buffer returned in val must be freed * by the caller. */ int kmip_parse_bignum(const char *str, bool has_prefix, BIGNUM **bn) { unsigned char *buf; uint32_t len; int rc; if (str == NULL) return -EINVAL; rc = kmip_parse_hex(str, has_prefix, &buf, &len); if (rc != 0) return rc; rc = kmip_decode_bignum(buf, len, bn); free(buf); return rc; } /** * Format a hex string from a big number. The caller must free the returned str. */ int kmip_format_bignum(const BIGNUM *bn, bool prefix, char **str) { unsigned char *buf; uint32_t len; int rc; len = kmip_encode_bignum_length(bn); /* BIG INTEGERS must be a multiple of 8 bytes long */ if ((len % KMIP_BIG_INTEGER_BLOCK_LENGTH) != 0) len += KMIP_BIG_INTEGER_BLOCK_LENGTH - (len % KMIP_BIG_INTEGER_BLOCK_LENGTH); buf = malloc(len); if (buf == NULL) return -ENOMEM; rc = kmip_encode_bignum(bn, buf, len); if (rc != 0) { free(buf); return -EIO; } rc = kmip_format_hex(buf, len, prefix, str); free(buf); return rc; } /** * Decode a binary big integer in two's complement form into an OpenSSL BIGNUM. */ int kmip_decode_bignum(const unsigned char *data, uint32_t length, BIGNUM **bn) { unsigned char *tmp = (unsigned char *)data; int i, neg = 0, rc = 0; if (data == NULL || bn == NULL) return -EINVAL; if (data[0] & 0x80) { neg = 1; tmp = calloc(1, length); if (tmp == NULL) return -ENOMEM; for (i = 0; i < (int)length; i++) tmp[i] = ~data[i]; for (i = length - 1; i >= 0; i--) { tmp[i]++; if (tmp[i] != 0x00) break; } } *bn = BN_bin2bn(tmp, length, NULL); if (*bn == NULL) { rc = -EIO; goto out; } BN_set_negative(*bn, neg); out: if (neg) free(tmp); return rc; } /** * Returns the length required by a binary big integer in two's complement form */ uint32_t kmip_encode_bignum_length(const BIGNUM *bn) { uint32_t length; if (bn == NULL) return 0; length = BN_num_bytes(bn); if (BN_is_negative(bn) && BN_is_bit_set(bn, (length * 8) - 1)) length += 1; return length; } /** * Encode an OpenSSL BIGNUM to a binary big integer in two's complement form, * in the desired length. */ int kmip_encode_bignum(const BIGNUM *bn, unsigned char *data, uint32_t length) { int i; if (bn == NULL || data == NULL) return -EINVAL; if (BN_bn2binpad(bn, data, length) != (int)length) return -EIO; if (BN_is_negative(bn)) { for (i = 0; i < (int)length; i++) data[i] = ~data[i]; for (i = length - 1; i >= 0; i--) { data[i]++; if (data[i] != 0x00) break; } } return 0; } /** * Parse a timestamp in ISO8601 format and return it as time_t value */ int kmip_parse_timestamp(const char *str, int64_t *val) { struct tm tm = { 0 }; char *p; int rc; rc = kmip_parse_hex_int(str, val); if (rc == 0) return 0; if (rc != -EBADMSG) return rc; p = strptime(str, KMIP_ISO8601_TIMESTAMP_TZ, &tm); if (p == NULL) p = strptime(str, KMIP_ISO8601_TIMESTAMP, &tm); if (p == NULL || *p != 0) return -EBADMSG; /* Adjust according to the parsed time zone */ tm.tm_sec -= tm.tm_gmtoff; tm.tm_gmtoff = 0; tm.tm_isdst = 0; *val = (time_t)timegm(&tm); return 0; } /** * Parses a mask specification of the specified tag and separator character */ int kmip_parse_mask(enum kmip_tag tag, const char *str, char separator, int64_t *val) { const struct kmip_enum *info; char *save_ptr, *s, *tok; char delimiter[2]; uint32_t enum_val; int rc = 0; info = kmip_enum_info_by_tag(tag); if (info == NULL) return kmip_parse_hex_int(str, val); *val = 0; s = strdup(str); if (s == NULL) return -ENOMEM; delimiter[0] = separator; delimiter[1] = 0; tok = strtok_r(s, delimiter, &save_ptr); while (tok != NULL) { rc = kmip_enum_value_by_name_or_hex(info, tok, &enum_val); if (rc != 0) break; *val |= enum_val; tok = strtok_r(NULL, delimiter, &save_ptr); } free(s); return rc; } static int kmip_append_string(char **str, int *str_len, char separator, const char *append) { int new_len; char *tmp; if (str == NULL || str_len == NULL) return -EINVAL; if (*str == NULL) *str_len = 0; new_len = *str_len; if (*str == NULL) new_len++; else if (separator != 0) new_len++; if (append != NULL) new_len += strlen(append); tmp = realloc(*str, new_len); if (tmp == NULL) return -ENOMEM; if (*str == NULL) memset(tmp, 0, new_len); else if (separator != 0) strncat(tmp, &separator, 1); if (append != NULL) strcat(tmp, append); *str = tmp; *str_len = new_len; return 0; } /** * Format a mask specification of the specified tag and separator character */ int kmip_format_mask(enum kmip_tag tag, int32_t value, char separator, char **str) { const struct kmip_enum *info; int rc = 0, i, s_len = 0; char *s = NULL, *tmp; info = kmip_enum_info_by_tag(tag); if (info == NULL || value == 0) return kmip_format_hex((const unsigned char *)&value, sizeof(value), true, str); /* Process all known mask bits */ for (i = 0; value != 0 && info[i].name != NULL; i++) { if (value & info[i].val) { rc = kmip_append_string(&s, &s_len, separator, info[i].name); if (rc != 0) goto out; value &= ~info[i].val; } } /* Any bits left in the value? */ if (value != 0) { rc = kmip_format_hex((const unsigned char *)&value, sizeof(value), true, &tmp); if (rc != 0) goto out; rc = kmip_append_string(&s, &s_len, separator, tmp); free(tmp); if (rc != 0) goto out; } *str = s; out: if (rc != 0) free(s); return rc; } void kmip_print_dump(const char *func, unsigned char *data, size_t size, unsigned int indent) { char outstr[200], hexstr[4]; size_t i; if (data == NULL) return; strcpy(outstr, ""); for (i = 0; i < size; i++) { sprintf(hexstr, "%02x ", data[i]); strcat(outstr, hexstr); if (i % 16 == 15) { kmip_print_debug(func, "%*s%s", indent, "", outstr); strcpy(outstr, ""); } } if (i % 16 != 0) kmip_print_debug(func, "%*s%s", indent, "", outstr); } static void kmip_print_bignum(const char *func, const BIGNUM *bn, unsigned int indent) { unsigned char *buf; uint32_t len; int rc; if (bn == NULL) return; len = kmip_encode_bignum_length(bn); buf = malloc(len); if (buf == NULL) return; rc = kmip_encode_bignum(bn, buf, len); if (rc != 0) { free(buf); return; } kmip_print_dump(func, buf, len, indent); free(buf); } static void kmip_node_dump_int(struct kmip_node *node, unsigned int indent) { enum kmip_tag tag, v1_attr_tag = 0; struct kmip_node *element; char outstr[200] = { 0 }; struct tm *tm; const char *s; char *tmp; time_t t; int rc; if (node == NULL) return; s = kmip_tag_name_by_tag(node->tag); kmip_print_debug("kmip_node_dump", "%*sTag: %s (0x%x)", indent, "", s ? s : "UNKNOWN", node->tag); s = kmip_type_name_by_type(node->type); kmip_print_debug("kmip_node_dump", "%*s Type: %s (0x%x)", indent, "", s ? s : "UNKNOWN", node->type); if (node->name != NULL) kmip_print_debug("kmip_node_dump", "%*s Name: %s", indent, "", node->name); /* * KMIP v1.x attribute values may be Enumerations or Integer Masks. * To correctly print them, we need to know the tag. This is contained * in a Attribute Name node, which is an element of our parent node. */ if (node->tag == KMIP_TAG_ATTRIBUTE_VALUE) v1_attr_tag = kmip_find_v1_attribute_name_tag(node->parent); tag = (v1_attr_tag != 0 ? v1_attr_tag : node->tag); switch (node->type) { case KMIP_TYPE_STRUCTURE: kmip_print_debug("kmip_node_dump", "%*s Elements (%u):", indent, "", kmip_node_get_structure_element_count(node)); element = node->structure_value; while (element != NULL) { kmip_node_dump_int(element, indent + 4); element = element->next; } break; case KMIP_TYPE_INTEGER: if (kmip_is_tag_mask(tag)) { rc = kmip_format_mask(tag, node->integer_value, '|', &tmp); if (rc == 0) { kmip_print_debug("kmip_node_dump", "%*s " "Value: %s (0x%x)", indent, "", tmp, node->integer_value); free(tmp); break; } } kmip_print_debug("kmip_node_dump", "%*s Value: %d (0x%x)", indent, "", node->integer_value, node->integer_value); break; case KMIP_TYPE_LONG_INTEGER: kmip_print_debug("kmip_node_dump", "%*s Value: %ld (0x%lx)", indent, "", node->long_value, node->long_value); break; case KMIP_TYPE_BIG_INTEGER: kmip_print_debug("kmip_node_dump", "%*s Value: (%u bytes)", indent, "", kmip_encode_bignum_length( node->big_integer_value)); kmip_print_bignum("kmip_node_dump", node->big_integer_value, indent + 4); break; case KMIP_TYPE_ENUMERATION: s = kmip_enum_name_by_tag_value(tag, node->enumeration_value); kmip_print_debug("kmip_node_dump", "%*s Value: %s (0x%x)", indent, "", s ? s : "UNKNOWN", node->enumeration_value); break; case KMIP_TYPE_BOOLEAN: kmip_print_debug("kmip_node_dump", "%*s Value: %s", indent, "", node->boolean_value ? "True" : "False"); break; case KMIP_TYPE_TEXT_STRING: kmip_print_debug("kmip_node_dump", "%*s Value: '%s' " "(%u characters)", indent, "", node->text_value, strlen(node->text_value)); break; case KMIP_TYPE_BYTE_STRING: kmip_print_debug("kmip_node_dump", "%*s Value: (%u bytes)", indent, "", node->length); kmip_print_dump("kmip_node_dump", node->bytes_value, node->length, indent + 4); break; case KMIP_TYPE_DATE_TIME: tm = gmtime((time_t *)&node->date_time_value); if (tm != NULL) strftime(outstr, sizeof(outstr), KMIP_ISO8601_TIMESTAMP_UTC, tm); else strcpy(outstr, "INVALID"); kmip_print_debug("kmip_node_dump", "%*s Value: %s (0x%lx)", indent, "", outstr, node->date_time_value); break; case KMIP_TYPE_INTERVAL: kmip_print_debug("kmip_node_dump", "%*s Value: %d (0x%x)", indent, "", node->interval_value, node->interval_value); break; case KMIP_TYPE_DATE_TIME_EXTENDED: t = (time_t)node->date_time_ext_value / 1000000; tm = gmtime(&t); if (tm != NULL) strftime(outstr, sizeof(outstr), KMIP_ISO8601_TIMESTAMP_UTC, tm); else strcpy(outstr, "INVALID"); kmip_print_debug("kmip_node_dump", "%*s Value: %s %lu (0x%lx)", indent, "", outstr, node->date_time_ext_value % 1000000, node->date_time_ext_value); break; default: break; } } /** * Dump a KMIP node * * @param node the node to free * @param debug if false, the function is a no-op */ void kmip_node_dump(struct kmip_node *node, bool debug) { if (node == NULL || !debug) return; kmip_node_dump_int(node, 0); } /** * Find a KMIP v1.x Attribute Name node in the elements of the specified parent * node, and return the tag value of the attribute name. * * @param parent the parent node of the attribute name and value * * @returns the tag value of the attribute name, or 0 if not found, or unknown * attribute name */ enum kmip_tag kmip_find_v1_attribute_name_tag(struct kmip_node *parent) { struct kmip_node *e; if (parent == NULL) return 0; if (parent->tag != KMIP_TAG_ATTRIBUTE) return 0; if (parent->type != KMIP_TYPE_STRUCTURE) return 0; e = parent->structure_value; while (e != NULL) { /* * A KMIP v2.x Vendor Attribute looks similar to a KMIP v1.x * Attribute, but has a Vendor Identification node. If we find * a Vendor Identification node, then it can't be a KMIP v1.x * Attribute. */ if (e->tag == KMIP_TAG_VENDOR_IDENTIFICATION && e->type == KMIP_TYPE_TEXT_STRING) return 0; if (e->tag == KMIP_TAG_ATTRIBUTE_NAME && e->type == KMIP_TYPE_TEXT_STRING) return kmip_attr_tag_by_v1_attr_name(e->text_value); e = e->next; } return 0; } s390-tools-2.38.0/libkmipclient/utils.h000066400000000000000000000035621502674226300176140ustar00rootroot00000000000000/* * libkmipclient - KMIP client library * * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef UTILS_H #define UTILS_H #include #include #include #include "kmip.h" #define KMIP_BIG_INTEGER_BLOCK_LENGTH 8 #define KMIP_ISO8601_TIMESTAMP_UTC "%FT%TZ" #define KMIP_ISO8601_TIMESTAMP_TZ "%FT%T%z" #define KMIP_ISO8601_TIMESTAMP "%FT%T" #define kmip_debug(debug, fmt...) \ do { \ if (debug) \ kmip_print_debug(__func__, fmt); \ } while (0) void kmip_print_debug(const char *func, const char *fmt, ...); void kmip_print_dump(const char *func, unsigned char *data, size_t size, unsigned int indent); int kmip_parse_decimal_int(const char *str, int64_t *val); int kmip_parse_decimal_uint(const char *str, uint64_t *val); int kmip_parse_hex_int(const char *str, int64_t *val); int kmip_parse_hex(const char *str, bool has_prefix, unsigned char **val, uint32_t *length); int kmip_format_hex(const unsigned char *val, uint32_t length, bool prefix, char **str); int kmip_parse_bignum(const char *str, bool has_prefix, BIGNUM **bn); int kmip_format_bignum(const BIGNUM *bn, bool prefix, char **str); int kmip_decode_bignum(const unsigned char *data, uint32_t length, BIGNUM **bn); uint32_t kmip_encode_bignum_length(const BIGNUM *bn); int kmip_encode_bignum(const BIGNUM *bn, unsigned char *data, uint32_t length); int kmip_parse_timestamp(const char *str, int64_t *val); int kmip_parse_mask(enum kmip_tag tag, const char *str, char separator, int64_t *val); int kmip_format_mask(enum kmip_tag tag, int32_t value, char separator, char **str); void kmip_node_dump(struct kmip_node *node, bool debug); enum kmip_tag kmip_find_v1_attribute_name_tag(struct kmip_node *parent); #endif s390-tools-2.38.0/libkmipclient/xml.c000066400000000000000000000275411502674226300172520ustar00rootroot00000000000000/* * libkmipclient - KMIP client library * * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include "kmip.h" #include "names.h" #include "utils.h" #define KMIP_XML_TTLV "TTLV" #define KMIP_XML_TAG "tag" #define KMIP_XML_NAME "name" #define KMIP_XML_TYPE "type" #define KMIP_XML_VALUE "value" /** * Decode a KMIP node from the data in the XML node using the XML encoding. * * @param xml the XML node to decode * @param parent the parent node or NULL if no parent exists. * @param node On return: the decoded node.The newly allocated * node has a reference count of 1. * @param debug if true, debug messages are printed * * @returns 0 in case of success, or a negative errno value */ int kmip_decode_xml(const xmlNode *xml, struct kmip_node *parent, struct kmip_node **node, bool debug) { char *tag_attr = NULL, *name_attr = NULL, *type_attr = NULL; enum kmip_tag tag, v1_attr_tag = 0; char *tag_name, *value_attr = NULL; struct kmip_node *n = NULL, *e; uint64_t uint64; xmlNode *child; int64_t int64; int rc = 0, i; if (xml == NULL || node == NULL) return -EINVAL; if (xml->type != XML_ELEMENT_NODE) { kmip_debug(debug, "Invalid XML node type: %d", xml->type); return -EINVAL; } n = calloc(1, sizeof(struct kmip_node)); if (n == NULL) { kmip_debug(debug, "calloc failed"); return -ENOMEM; } n->ref_count = 1; if (strcmp((char *)xml->name, KMIP_XML_TTLV) == 0) { tag_attr = (char *)xmlGetProp(xml, (xmlChar *)KMIP_XML_TAG); if (tag_attr == NULL) { kmip_debug(debug, "Missing '%s' attribute in XML node", KMIP_XML_TAG); rc = -EBADMSG; goto out; } tag_name = tag_attr; } else { tag_name = (char *)xml->name; } n->tag = kmip_tag_by_name_or_hex(tag_name); if (n->tag == 0) { kmip_debug(debug, "Unknown 'tag' in XML object: '%s'", tag_name); rc = -EBADMSG; goto out; } name_attr = (char *)xmlGetProp(xml, (xmlChar *)KMIP_XML_NAME); if (name_attr != NULL) n->name = strdup(name_attr); type_attr = (char *)xmlGetProp(xml, (xmlChar *)KMIP_XML_TYPE); if (type_attr == NULL) { n->type = KMIP_TYPE_STRUCTURE; } else { n->type = kmip_type_by_name_or_hex(type_attr); if (n->type == 0) { kmip_debug(debug, "Unknown 'type' in XML object: '%s'", type_attr); rc = -EBADMSG; goto out; } } value_attr = (char *)xmlGetProp(xml, (xmlChar *)KMIP_XML_VALUE); if (n->type != KMIP_TYPE_STRUCTURE && value_attr == NULL) { kmip_debug(debug, "Missing '%s' attribute in XML node", KMIP_XML_VALUE); rc = -EBADMSG; goto out; } /* * KMIP v1.x attribute values may be Enumerations or Integer Masks. * To correctly decode them, we need to know the tag. This is contained * in a Attribute Name node, which is an element of our parent node. */ if (n->tag == KMIP_TAG_ATTRIBUTE_VALUE) v1_attr_tag = kmip_find_v1_attribute_name_tag(parent); tag = (v1_attr_tag != 0 ? v1_attr_tag : n->tag); kmip_debug(debug, "tag: 0x%x type: 0x%x", n->tag, n->type); switch (n->type) { case KMIP_TYPE_STRUCTURE: for (child = xml->children, i = 0; child != NULL; child = child->next, i++) { if (child->type != XML_ELEMENT_NODE) continue; rc = kmip_decode_xml(child, n, &e, debug); if (rc != 0) { kmip_debug(debug, "Failed to parse child " "element %d", i); goto out; } rc = kmip_node_add_structure_element(n, e); kmip_node_free(e); if (rc != 0) { kmip_debug(debug, "kmip_node_structure_add_element " "failed: rc: %d", rc); goto out; } } break; case KMIP_TYPE_INTEGER: case KMIP_TYPE_LONG_INTEGER: case KMIP_TYPE_DATE_TIME_EXTENDED: if (n->type == KMIP_TYPE_INTEGER && kmip_is_tag_mask(tag)) { rc = kmip_parse_mask(tag, value_attr, ' ', &int64); if (rc != 0) { kmip_debug(debug, "Failed to parse " "mask string '%s'", value_attr); goto out; } } else { rc = kmip_parse_decimal_int(value_attr, &int64); if (rc != 0) { kmip_debug(debug, "Failed to parse " "decimal string '%s'", value_attr); goto out; } } switch (n->type) { case KMIP_TYPE_INTEGER: n->integer_value = int64; break; case KMIP_TYPE_LONG_INTEGER: n->long_value = int64; break; case KMIP_TYPE_DATE_TIME_EXTENDED: n->date_time_ext_value = int64; break; default: break; } break; case KMIP_TYPE_INTERVAL: rc = kmip_parse_decimal_uint(value_attr, &uint64); if (rc != 0) { kmip_debug(debug, "Failed to parse " "decimal string '%s'", value_attr); goto out; } n->interval_value = uint64; break; case KMIP_TYPE_BIG_INTEGER: rc = kmip_parse_bignum(value_attr, false, &n->big_integer_value); if (rc != 0) { kmip_debug(debug, "Failed to parse bignum string '%s'", value_attr); goto out; } break; case KMIP_TYPE_ENUMERATION: rc = kmip_enum_value_by_tag_name_or_hex(tag, value_attr, &n->enumeration_value); if (rc != 0) { kmip_debug(debug, "Failed to parse enumeration '%s'", value_attr); goto out; } break; case KMIP_TYPE_BOOLEAN: n->boolean_value = (strcmp(value_attr, "true") == 0 || strcmp(value_attr, "1") == 0); break; case KMIP_TYPE_TEXT_STRING: n->text_value = strdup(value_attr); if (n->text_value == NULL) { rc = -ENOMEM; goto out; } n->length = strlen(n->text_value); break; case KMIP_TYPE_BYTE_STRING: rc = kmip_parse_hex(value_attr, false, &n->bytes_value, &n->length); if (rc != 0) { kmip_debug(debug, "Failed to parse hex string '%s'", value_attr); goto out; } break; case KMIP_TYPE_DATE_TIME: rc = kmip_parse_timestamp(value_attr, &n->date_time_value); if (rc != 0) { kmip_debug(debug, "Failed to parse time stamp '%s'", value_attr); goto out; } break; default: kmip_debug(debug, "unknown type: 0x%x", n->type); rc = -EBADMSG; goto out; } *node = n; rc = 0; out: if (rc != 0 && n != NULL) kmip_node_free(n); if (tag_attr != NULL) xmlFree(tag_attr); if (name_attr != NULL) xmlFree(name_attr); if (type_attr != NULL) xmlFree(type_attr); if (value_attr != NULL) xmlFree(value_attr); return rc; } /** * Encode a KMIP node into an XML node using the XML encoding. * * @param node the node to encode * @param xml On return: the XML node * @param debug if true, debug messages are printed * * @returns 0 in case of success, or a negative errno value */ int kmip_encode_xml(const struct kmip_node *node, xmlNode **xml, bool debug) { enum kmip_tag tag, v1_attr_tag = 0; xmlNode *ret_xml = NULL, *elem_xml; struct kmip_node *element; const char *tag_name; char tmp_str[50]; const char *str; struct tm *tm; xmlAttr *attr; char *tmp; int rc; if (node == NULL || xml == NULL) return -EINVAL; kmip_debug(debug, "tag: 0x%x type: 0x%x", node->tag, node->type); tag_name = kmip_tag_name_by_tag(node->tag); if (tag_name != NULL) ret_xml = xmlNewNode(NULL, (xmlChar *)tag_name); else ret_xml = xmlNewNode(NULL, (xmlChar *)KMIP_XML_TTLV); if (ret_xml == NULL) { kmip_debug(debug, "Failed to allocate a XML node"); return -ENOMEM; } if (tag_name == NULL) { sprintf(tmp_str, "0x%06x", node->tag); attr = xmlSetProp(ret_xml, (xmlChar *)KMIP_XML_TAG, (xmlChar *)tmp_str); if (attr == NULL) { kmip_debug(debug, "Failed to add '%s' attribute to XML node", KMIP_XML_TAG); rc = -ENOMEM; goto out; } if (node->name != NULL) { attr = xmlSetProp(ret_xml, (xmlChar *)KMIP_XML_NAME, (xmlChar *)node->name); if (attr == NULL) { kmip_debug(debug, "Failed to add '%s' " "attribute to XML node", KMIP_XML_NAME); rc = -ENOMEM; goto out; } } } if (node->type != KMIP_TYPE_STRUCTURE) { str = kmip_type_name_by_type(node->type); if (str == NULL) { kmip_debug(debug, "unknown type 0x%x", node->type); rc = -EINVAL; goto out; } attr = xmlSetProp(ret_xml, (xmlChar *)KMIP_XML_TYPE, (xmlChar *)str); if (attr == NULL) { kmip_debug(debug, "Failed to add '%s' attribute to XML node", KMIP_XML_TYPE); rc = -ENOMEM; goto out; } } /* * KMIP v1.x attribute values may be Enumerations or Integer Masks. * To correctly encode them, we need to know the tag. This is contained * in a Attribute Name node, which is an element of our parent node. */ if (node->tag == KMIP_TAG_ATTRIBUTE_VALUE) v1_attr_tag = kmip_find_v1_attribute_name_tag(node->parent); tag = (v1_attr_tag != 0 ? v1_attr_tag : node->tag); switch (node->type) { case KMIP_TYPE_STRUCTURE: element = node->structure_value; while (element != NULL) { rc = kmip_encode_xml(element, &elem_xml, debug); if (rc != 0) { kmip_debug(debug, "kmip_encode_xml failed"); goto out; } if (xmlAddChild(ret_xml, elem_xml) == NULL) { kmip_debug(debug, "xmlAddChild failed"); rc = -EIO; goto out; } element = element->next; } attr = NULL; break; case KMIP_TYPE_INTEGER: if (kmip_is_tag_mask(tag) && node->integer_value != 0) { rc = kmip_format_mask(tag, node->integer_value, ' ', &tmp); if (rc != 0) { kmip_debug(debug, "kmip_format_mask failed"); goto out; } attr = xmlSetProp(ret_xml, (xmlChar *)KMIP_XML_VALUE, (xmlChar *)tmp); free(tmp); } else { sprintf(tmp_str, "%d", node->integer_value); attr = xmlSetProp(ret_xml, (xmlChar *)KMIP_XML_VALUE, (xmlChar *)tmp_str); } break; case KMIP_TYPE_INTERVAL: sprintf(tmp_str, "%u", node->interval_value); attr = xmlSetProp(ret_xml, (xmlChar *)KMIP_XML_VALUE, (xmlChar *)tmp_str); break; case KMIP_TYPE_LONG_INTEGER: sprintf(tmp_str, "%ld", node->long_value); attr = xmlSetProp(ret_xml, (xmlChar *)KMIP_XML_VALUE, (xmlChar *)tmp_str); break; case KMIP_TYPE_BIG_INTEGER: rc = kmip_format_bignum(node->big_integer_value, false, &tmp); if (rc != 0) { kmip_debug(debug, "kmip_format_bignum failed"); goto out; } attr = xmlSetProp(ret_xml, (xmlChar *)KMIP_XML_VALUE, (xmlChar *)tmp); free(tmp); break; case KMIP_TYPE_ENUMERATION: str = kmip_enum_name_by_tag_value(tag, node->enumeration_value); if (str != NULL) { attr = xmlSetProp(ret_xml, (xmlChar *)KMIP_XML_VALUE, (xmlChar *)str); } else { sprintf(tmp_str, "0x%08x", node->enumeration_value); attr = xmlSetProp(ret_xml, (xmlChar *)KMIP_XML_VALUE, (xmlChar *)tmp_str); } break; case KMIP_TYPE_BOOLEAN: attr = xmlSetProp(ret_xml, (xmlChar *)KMIP_XML_VALUE, (xmlChar *)(node->boolean_value ? "true" : "false")); break; case KMIP_TYPE_TEXT_STRING: attr = xmlSetProp(ret_xml, (xmlChar *)KMIP_XML_VALUE, (xmlChar *)node->text_value); break; case KMIP_TYPE_BYTE_STRING: rc = kmip_format_hex(node->bytes_value, node->length, false, &tmp); if (rc != 0) { kmip_debug(debug, "kmip_format_hex_long failed"); goto out; } attr = xmlSetProp(ret_xml, (xmlChar *)KMIP_XML_VALUE, (xmlChar *)tmp); free(tmp); break; case KMIP_TYPE_DATE_TIME: tm = gmtime((time_t *)&node->date_time_value); strftime(tmp_str, sizeof(tmp_str), KMIP_ISO8601_TIMESTAMP_UTC, tm); attr = xmlSetProp(ret_xml, (xmlChar *)KMIP_XML_VALUE, (xmlChar *)tmp_str); break; case KMIP_TYPE_DATE_TIME_EXTENDED: sprintf(tmp_str, "%ld", node->date_time_ext_value); attr = xmlSetProp(ret_xml, (xmlChar *)KMIP_XML_VALUE, (xmlChar *)tmp_str); break; default: kmip_debug(debug, "unknown type: 0x%x", node->type); rc = -EINVAL; goto out; } if (attr == NULL && node->type != KMIP_TYPE_STRUCTURE) { kmip_debug(debug, "Failed to add '%s' " "attribute to XML node", KMIP_XML_VALUE); rc = -ENOMEM; goto out; } rc = 0; *xml = ret_xml; out: if (rc != 0) xmlFreeNode(ret_xml); return rc; } s390-tools-2.38.0/libpv/000077500000000000000000000000001502674226300145635ustar00rootroot00000000000000s390-tools-2.38.0/libpv/Makefile000066400000000000000000000040511502674226300162230ustar00rootroot00000000000000# Common definitions include ../common.mak .DEFAULT_GOAL := all LIB := libpv.a GLIB2_CFLAGS := $(shell $(PKG_CONFIG) --silence-errors --cflags glib-2.0) GLIB2_LIBS := $(shell $(PKG_CONFIG) --silence-errors --libs glib-2.0) LIBCRYPTO_CFLAGS := $(shell $(PKG_CONFIG) --silence-errors --cflags libcrypto) LIBCRYPTO_LIBS := $(shell $(PKG_CONFIG) --silence-errors --libs libcrypto) LDLIBS += $(GLIB2_LIBS) $(LIBCRYPTO_LIBS) WARNINGS := -Wall -Wextra -Wshadow \ -Wcast-align -Wwrite-strings -Wmissing-prototypes \ -Wmissing-declarations -Wredundant-decls -Wnested-externs \ -Wno-long-long -Wuninitialized -Wconversion -Wstrict-prototypes \ -Wpointer-arith -Wno-error=inline \ -Wno-unused-function -Wno-unused-parameter -Wno-unused-variable \ $(NULL) ALL_CFLAGS += -DOPENSSL_API_COMPAT=0x10101000L \ $(GLIB2_CFLAGS) \ $(LIBCRYPTO_CFLAGS) \ $(WARNINGS) \ $(NULL) BUILD_TARGETS := skip-$(LIB) ifneq (${HAVE_OPENSSL},0) ifneq (${HAVE_GLIB2},0) BUILD_TARGETS := $(LIB) endif endif sources := $(wildcard *.c) objects := $(patsubst %.c,%.o,$(sources)) all: $(BUILD_TARGETS) $(LIB): $(objects) $(LIB): ALL_CFLAGS += -fPIC $(objects): .check-dep-$(LIB) install: all clean: rm -f -- $(objects) rm -f -- $(LIB) rm -f -- .check-dep-$(LIB) .detect-openssl.dep.c skip-$(LIB): echo " SKIP $(LIB) due to unresolved dependencies" .PHONY: all install clean skip-$(LIB) install-$(LIB) .detect-openssl.dep.c: echo "#include " > $@ echo "#if OPENSSL_VERSION_NUMBER < 0x10101000L" >> $@ echo " #error openssl version 1.1.1 is required" >> $@ echo "#endif" >> $@ echo "static void __attribute__((unused)) test(void) {" >> $@ echo " EVP_MD_CTX *ctx = EVP_MD_CTX_new();" >> $@ echo " EVP_MD_CTX_free(ctx);" >> $@ echo "}" >> $@ .check-dep-$(LIB): .detect-openssl.dep.c $(call check_dep, \ "$(LIB)", \ "glib.h", \ "glib2-devel / libglib2.0-dev", \ "HAVE_GLIB2=0") $(call check_dep, \ "$(LIB)", \ $^, \ "openssl-devel / libssl-dev version >= 1.1.1", \ "HAVE_OPENSSL=0", \ "-I.") touch $@ s390-tools-2.38.0/libpv/config.h000066400000000000000000000005341502674226300162030ustar00rootroot00000000000000/* * Config file. * Must be include before any other header. * * Copyright IBM Corp. 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. * */ #ifndef LIBPV_CONFIG_H #define LIBPV_CONFIG_H #define GETTEXT_PACKAGE "libpv" #endif /* LIBPV_CONFIG_H */ s390-tools-2.38.0/libpv/crypto.c000066400000000000000000000212511502674226300162500ustar00rootroot00000000000000/* * Cryptography functions * * Copyright IBM Corp. 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ /* Must be included before any other header */ #include "config.h" #include #include #include #include #include "lib/zt_common.h" #include "libpv/crypto.h" #include "libpv/glib-helper.h" char *pv_get_openssl_errors(void) { char *ret; char *buf; BIO *bio; long len; bio = BIO_new(BIO_s_mem()); ERR_print_errors(bio); len = BIO_get_mem_data(bio, &buf); if (len <= 0 || !buf) ret = g_strdup("Cannot receive OpenSSL error message."); else ret = g_strndup(buf, (size_t)len); BIO_free(bio); return ret; } int pv_BIO_reset(BIO *b) { int rc = BIO_reset(b); if (rc != 1 && !(BIO_method_type(b) == BIO_TYPE_FILE && rc == 0)) return -1; return 1; } static int64_t pv_gcm_encrypt_decrypt(GBytes *input, GBytes *aad, const PvCipherParms *parms, GBytes **output, GBytes **tagp, enum PvCryptoMode mode, GError **error) { const uint8_t *in_data, *aad_data = NULL, *iv_data, *key_data; size_t in_size, aad_size = 0, iv_size, key_size, out_size; const EVP_CIPHER *cipher = parms->cipher; const size_t tag_size = parms->tag_size; gboolean encrypt = mode == PV_ENCRYPT; g_autoptr(EVP_CIPHER_CTX) ctx = NULL; g_autofree uint8_t *out_data = NULL; g_autofree uint8_t *tag_data = NULL; const GBytes *key = parms->key; const GBytes *iv = parms->iv; int cipher_block_size; int64_t ret = -1; int len = -1; GBytes *tag; g_assert(tagp); g_assert(cipher); g_assert(key); g_assert(iv); tag = *tagp; in_data = g_bytes_get_data((GBytes *)input, &in_size); if (aad) aad_data = g_bytes_get_data((GBytes *)aad, &aad_size); iv_data = g_bytes_get_data((GBytes *)iv, &iv_size); key_data = g_bytes_get_data((GBytes *)key, &key_size); out_size = in_size; cipher_block_size = EVP_CIPHER_block_size(cipher); /* Checks for later casts */ g_assert(aad_size <= INT_MAX); g_assert(in_size <= INT_MAX); g_assert(iv_size <= INT_MAX); g_assert(cipher_block_size > 0); ctx = EVP_CIPHER_CTX_new(); if (!ctx) g_abort(); if (tag_size == 0 || (tag_size % (size_t)cipher_block_size != 0)) { g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, "Passed tag size is incorrect"); return -1; } /* Has the passed key the correct size? */ if (EVP_CIPHER_key_length(cipher) != (int)key_size) { g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, "Passed key has incorrect size: %ld != %d", key_size, EVP_CIPHER_key_length(cipher)); return -1; } /* First, set the cipher algorithm so we can verify our key/IV lengths */ if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, encrypt) != 1) { g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, "EVP_CIPHER_CTX_new failed"); return -1; } /* Set IV length */ if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, (int)iv_size, NULL) != 1) { g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, "EVP_CIPHER_CTX_ex failed"); return -1; } /* Initialise key and IV */ if (EVP_CipherInit_ex(ctx, NULL, NULL, key_data, iv_data, encrypt) != 1) { g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, "EVP_CipherInit_ex failed"); return -1; } /* Allocate output data */ out_data = g_malloc0(out_size); if (encrypt) tag_data = g_malloc0(tag_size); if (aad_size > 0) { /* Provide any AAD data */ if (EVP_CipherUpdate(ctx, NULL, &len, aad_data, (int)aad_size) != 1) { g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, "EVP_CipherUpdate failed"); return -1; } g_assert(len == (int)aad_size); } /* Provide data to be en/decrypted */ if (EVP_CipherUpdate(ctx, out_data, &len, in_data, (int)in_size) != 1) { g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, "EVP_CipherUpdate failed"); return -1; } ret = len; if (!encrypt) { const uint8_t *tmp_tag_data = NULL; size_t tmp_tag_size = 0; if (tag) tmp_tag_data = g_bytes_get_data(tag, &tmp_tag_size); if (tag_size != tmp_tag_size) { g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, "Getting the GCM tag failed"); return -1; } /* Set expected tag value */ if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, (int)tmp_tag_size, (uint8_t *)tmp_tag_data) != 1) { g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, "Setting the GCM tag failed"); return -1; } } /* Finalize the en/decryption */ if (EVP_CipherFinal_ex(ctx, (uint8_t *)out_data + len, &len) != 1) { if (encrypt) g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, "Encrypting failed (EVP_CipherFinal_ex)"); else g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_MATCH_TAG, "Verifying the GCM tag failed"); return -1; } ret += len; if (encrypt) { /* Get the tag */ if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, (int)tag_size, tag_data) != 1) { g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, "Getting the GCM tag failed"); return -1; } g_assert(!*tagp); *tagp = g_bytes_new_take(g_steal_pointer(&tag_data), tag_size); } g_assert(ret == (int)out_size); g_assert(out_size == in_size); g_assert(!*output); *output = pv_sec_gbytes_new_take(g_steal_pointer(&out_data), out_size); return ret; } int64_t pv_gcm_encrypt(GBytes *plain, GBytes *aad, const PvCipherParms *parms, GBytes **cipher, GBytes **tag, GError **error) { pv_wrapped_g_assert(plain); pv_wrapped_g_assert(parms); pv_wrapped_g_assert(cipher); pv_wrapped_g_assert(tag); return pv_gcm_encrypt_decrypt(plain, aad, parms, cipher, tag, PV_ENCRYPT, error); } int64_t pv_gcm_decrypt(GBytes *cipher, GBytes *aad, GBytes *tag, const PvCipherParms *parms, GBytes **plain, GError **error) { pv_wrapped_g_assert(cipher); pv_wrapped_g_assert(tag); pv_wrapped_g_assert(parms); pv_wrapped_g_assert(plain); return pv_gcm_encrypt_decrypt(cipher, aad, parms, plain, &tag, PV_DECRYPT, error); } GBytes *pv_hkdf_extract_and_expand(size_t derived_key_len, GBytes *key, GBytes *salt, GBytes *info, const EVP_MD *md, GError **error) { const unsigned char *salt_data, *key_data, *info_data; g_autoptr(EVP_PKEY_CTX) ctx = NULL; size_t salt_len, key_len, info_len; g_autofree unsigned char *derived_key = NULL; g_assert(derived_key_len > 0); pv_wrapped_g_assert(key); pv_wrapped_g_assert(salt); pv_wrapped_g_assert(info); pv_wrapped_g_assert(md); ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); if (!ctx) g_abort(); if (EVP_PKEY_derive_init(ctx) != 1) { g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_HKDF_FAIL, "FAILED to derive key via HKDF"); return NULL; } if (EVP_PKEY_CTX_hkdf_mode(ctx, EVP_PKEY_HKDEF_MODE_EXTRACT_AND_EXPAND) != 1) { g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_HKDF_FAIL, "FAILED to derive key via HKDF"); return NULL; } if (EVP_PKEY_CTX_set_hkdf_md(ctx, md) != 1) { g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_HKDF_FAIL, "FAILED to derive key via HKDF"); return NULL; } salt_data = g_bytes_get_data(salt, &salt_len); if (salt_len > INT_MAX) { g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_HKDF_FAIL, "FAILED to derive key via HKDF"); return NULL; } if (EVP_PKEY_CTX_set1_hkdf_salt(ctx, salt_data, (int)salt_len) != 1) { g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_HKDF_FAIL, "FAILED to derive key via HKDF"); return NULL; } key_data = g_bytes_get_data(key, &key_len); if (key_len > INT_MAX) { g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_HKDF_FAIL, "FAILED to derive key via HKDF"); return NULL; } if (EVP_PKEY_CTX_set1_hkdf_key(ctx, key_data, (int)key_len) != 1) { g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_HKDF_FAIL, "FAILED to derive key via HKDF"); return NULL; } info_data = g_bytes_get_data(info, &info_len); if (info_len > INT_MAX) { g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_HKDF_FAIL, "FAILED to derive key via HKDF"); return NULL; } if (EVP_PKEY_CTX_add1_hkdf_info(ctx, (unsigned char *)info_data, (int)info_len) != 1) { g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_HKDF_FAIL, "FAILED to derive key via HKDF"); return NULL; } derived_key = g_malloc0(derived_key_len); if (EVP_PKEY_derive(ctx, derived_key, &derived_key_len) != 1) { g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_HKDF_FAIL, "FAILED to derive key via HKDF"); return NULL; } return pv_sec_gbytes_new_take(g_steal_pointer(&derived_key), derived_key_len); } GQuark pv_crypto_error_quark(void) { return g_quark_from_static_string("pv-crypto-error-quark"); } s390-tools-2.38.0/libpv/glib-helper.c000066400000000000000000000076151502674226300171320ustar00rootroot00000000000000/* * Glib convenience functions * * Copyright IBM Corp. 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ /* Must be included before any other header */ #include "config.h" #include #include #include "libpv/glib-helper.h" struct __data { void *data; size_t size; GFreeFunc free_func; }; static void __data_clear_and_free(void *p) { struct __data *ptr = p; if (!ptr) return; if (ptr->data) { OPENSSL_cleanse(ptr->data, ptr->size); ptr->free_func(ptr->data); } g_free(ptr); } static GBytes *pv_sec_gbytes_new_take_func(void *data, size_t size, GFreeFunc free_func) { struct __data *tmp = g_new(struct __data, 1); tmp->data = data; tmp->size = size; tmp->free_func = free_func; return g_bytes_new_with_free_func(data, size, __data_clear_and_free, tmp); } GBytes *pv_sec_gbytes_new_take(void *data, size_t size) { return pv_sec_gbytes_new_take_func(data, size, g_free); } GBytes *pv_sec_gbytes_new(const void *data, size_t size) { g_autofree void *tmp_data = NULL; g_return_val_if_fail(data || size != 0, NULL); tmp_data = g_malloc(size); memcpy(tmp_data, data, size); return pv_sec_gbytes_new_take(g_steal_pointer(&tmp_data), size); } int pv_file_seek(FILE *file, long offset, int whence, GError **error) { int cached_errno; int ret = fseek(file, offset, whence); if (ret) { cached_errno = errno; g_set_error(error, PV_GLIB_HELPER_ERROR, PV_GLIB_HELPER_FILE_ERROR, "Cannot seek: %s", g_strerror(cached_errno)); } return ret; } size_t pv_file_write(FILE *file, const void *ptr, size_t size, GError **error) { int cached_errno; size_t n = fwrite(ptr, 1, size, file); if (n != size) { cached_errno = errno; g_set_error(error, PV_GLIB_HELPER_ERROR, PV_GLIB_HELPER_FILE_ERROR, "Cannot write: %s", g_strerror(cached_errno)); } return n; } long pv_file_close(FILE *file, GError **error) { int cached_errno; int ret = fclose(file); if (ret) { cached_errno = errno; g_set_error(error, PV_GLIB_HELPER_ERROR, PV_GLIB_HELPER_FILE_ERROR, "Cannot close: %s", g_strerror(cached_errno)); } return ret; } void pv_auto_close_file(FILE *file) { if (!file) return; (void)pv_file_close(file, NULL); } long pv_file_tell(FILE *file, GError **error) { int cached_errno; long n = ftell(file); if (n < 0) { cached_errno = errno; g_set_error(error, PV_GLIB_HELPER_ERROR, PV_GLIB_HELPER_FILE_ERROR, "Cannot tell: %s", g_strerror(cached_errno)); } return n; } FILE *pv_file_open(const char *filename, const char *mode, GError **error) { FILE *file = fopen(filename, mode); int cached_errno; if (!file) { cached_errno = errno; g_set_error(error, PV_GLIB_HELPER_ERROR, PV_GLIB_HELPER_FILE_ERROR, "Cannot open '%s'. %s", filename, g_strerror(cached_errno)); return NULL; } return file; } GBytes *pv_file_get_content_as_g_bytes(const char *filename, GError **error) { g_autofree char *data = NULL; size_t data_size; if (!g_file_get_contents(filename, &data, &data_size, error)) return NULL; return g_bytes_new_take(g_steal_pointer(&data), data_size); } GBytes *pv_file_get_content_as_secure_bytes(const char *filename) { g_autoptr(FILE) f = fopen(filename, "rb"); g_autofree char *data = NULL; ssize_t file_size; size_t data_size; if (!f) return NULL; fseek(f, 0, SEEK_END); file_size = ftell(f); if (file_size < 0) return NULL; data_size = (size_t)file_size; fseek(f, 0, SEEK_SET); data = g_malloc0(data_size); if (data_size != fread(data, 1, data_size, f)) return NULL; return pv_sec_gbytes_new_take(g_steal_pointer(&data), data_size); } void *pv_gbytes_memcpy(void *dst, size_t dst_size, GBytes *src, size_t *copied) { size_t src_size; const void *src_data = g_bytes_get_data(src, &src_size); if (dst_size < src_size) return NULL; if (copied) *copied = src_size; return memcpy(dst, src_data, src_size); } s390-tools-2.38.0/libseckey/000077500000000000000000000000001502674226300154215ustar00rootroot00000000000000s390-tools-2.38.0/libseckey/Makefile000066400000000000000000000044351502674226300170670ustar00rootroot00000000000000include ../common.mak lib = libseckey.a ifneq (${HAVE_OPENSSL},0) BUILD_TARGETS = $(lib) else BUILD_TARGETS = skip-libseckey endif TMPFILE := $(shell mktemp) detect-openssl-version.dep: echo "#include " > $(TMPFILE) echo "#include " >> $(TMPFILE) echo "#ifndef OPENSSL_VERSION_PREREQ" >> $(TMPFILE) echo " #if defined(OPENSSL_VERSION_MAJOR) && defined(OPENSSL_VERSION_MINOR)" >> $(TMPFILE) echo " #define OPENSSL_VERSION_PREREQ(maj, min) \\" >> $(TMPFILE) echo " ((OPENSSL_VERSION_MAJOR << 16) + \\" >> $(TMPFILE) echo " OPENSSL_VERSION_MINOR >= ((maj) << 16) + (min))" >> $(TMPFILE) echo " #else" >> $(TMPFILE) echo " #define OPENSSL_VERSION_PREREQ(maj, min) \\" >> $(TMPFILE) echo " (OPENSSL_VERSION_NUMBER >= (((maj) << 28) | \\" >> $(TMPFILE) echo " ((min) << 20)))" >> $(TMPFILE) echo " #endif" >> $(TMPFILE) echo "#endif" >> $(TMPFILE) echo "#if !OPENSSL_VERSION_PREREQ(1, 1)" >> $(TMPFILE) echo " #error openssl version 1.1 is required" >> $(TMPFILE) echo "#endif" >> $(TMPFILE) echo "static void __attribute__((unused)) test(void) {" >> $(TMPFILE) echo " EVP_PKEY_meth_remove(NULL);" >> $(TMPFILE) echo "}" >> $(TMPFILE) mv $(TMPFILE) $@ check-dep-libseckey: detect-openssl-version.dep $(call check_dep, \ "libseckey", \ "detect-openssl-version.dep", \ "openssl-devel version >= 1.1.1", \ "HAVE_OPENSSL=0", \ -I. -lcrypto -DOPENSSL_SUPPRESS_DEPRECATED) touch check-dep-libseckey objects = sk_openssl.o sk_pkeymeth.o sk_provider.o sk_utilities.o sk_cca.o sk_ep11.o headers = $(rootdir)include/libseckey/sk_openssl.h $(rootdir)include/libseckey/sk_utilities.h \ $(rootdir)include/libseckey/sk_cca.h $(rootdir)include/libseckey/sk_ep11.h ALL_CFLAGS += -fPIC $(lib): $(objects) sk_openssl.o: check-dep-libseckey sk_openssl.c $(headers) sk_pkeymeth.o: check-dep-libseckey sk_pkeymeth.c $(headers) sk_provider.o: check-dep-libseckey sk_provider.c $(headers) sk_cca.o: check-dep-libseckey sk_cca.c $(headers) sk_ep11.o: check-dep-libseckey sk_ep11.c $(headers) all: $(BUILD_TARGETS) skip-libseckey: echo " SKIP libseckey due to HAVE_OPENSSL=0" install: all clean: rm -f *.o $(lib) detect-openssl-version.dep check-dep-libseckey .PHONY: all install clean skip-libseckey s390-tools-2.38.0/libseckey/sk_cca.c000066400000000000000000001342701502674226300170170ustar00rootroot00000000000000/* * libseckey - Secure key library * * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include "lib/zt_common.h" #include "libseckey/sk_cca.h" #include "libseckey/sk_openssl.h" #include "libseckey/sk_utilities.h" /* Internal CCA definitions */ /* CCA PKA Key Generate function */ typedef void (*CSNDPKG_t)(long *return_code, long *reason_code, long *exit_data_length, unsigned char *exit_data, long *rule_array_count, unsigned char *rule_array, long *regeneration_data_length, unsigned char *regeneration_data, long *skeleton_key_token_length, unsigned char *skeleton_key_token, unsigned char *transport_key_identifier, long *generated_key_identifier_length, unsigned char *generated_key_identifier); /* CCA PKA Key Token Build function */ typedef void (*CSNDPKB_t)(long *return_code, long *reason_code, long *exit_data_length, unsigned char *exit_data, long *rule_array_count, unsigned char *rule_array, long *key_values_structure_length, unsigned char *key_values_structure, long *key_name_ln, unsigned char *key_name, long *reserved_1_length, unsigned char *reserved_1, long *reserved_2_length, unsigned char *reserved_2, long *reserved_3_length, unsigned char *reserved_3, long *reserved_4_length, unsigned char *reserved_4, long *reserved_5_length, unsigned char *reserved_5, long *token_length, unsigned char *token); /* CCA PKA Key Token Change function */ typedef void (*CSNDKTC_t)(long *return_code, long *reason_code, long *exit_data_length, unsigned char *exit_data, long *rule_array_count, unsigned char *rule_array, long *key_identifier_length, unsigned char *key_identifier); /* CCA Digital Signature Generate function */ typedef void (*CSNDDSG_t)(long *return_code, long *reason_code, long *exit_data_length, unsigned char *exit_data, long *rule_array_count, unsigned char *rule_array, long *PKA_private_key_identifier_length, unsigned char *PKA_private_key_identifier, long *hash_length, unsigned char *hash, long *signature_field_length, long *signature_bit_length, unsigned char *signature_field); /* PKA Decrypt */ typedef void (*CSNDPKD_t)(long *return_code, long *reason_code, long *exit_data_length, unsigned char *exit_data, long *rule_array_count, unsigned char *rule_array, long *PKA_enciphered_keyvalue_length, unsigned char *PKA_enciphered_keyvalue, long *data_structure_length, unsigned char *data_structure, long *PKA_key_identifier_length, unsigned char *PKA_key_identifier, long *target_keyvalue_length, unsigned char *target_keyvalue); struct cca_lib { CSNDPKG_t dll_CSNDPKG; CSNDPKB_t dll_CSNDPKB; CSNDKTC_t dll_CSNDKTC; CSNDDSG_t dll_CSNDDSG; CSNDPKD_t dll_CSNDPKD; }; #define CCA_KEYWORD_SIZE 8 #define CCA_KEY_ID_SIZE 64 struct cca_ec_key_pair_value_struct { uint8_t curve_type; uint8_t reserved; uint16_t curve_length; uint16_t priv_key_length; uint16_t public_key_len; } __packed; struct cca_rsa_key_pair_value_struct { uint16_t modulus_bit_length; uint16_t modulus_length; uint16_t public_exp_length; uint16_t reserved; uint16_t p_length; uint16_t q_length; uint16_t dp_length; uint16_t dq_length; uint16_t u_length; unsigned char public_exponent[3]; } __packed; struct cca_ec_pub_key_value_struct { uint8_t curve_type; uint8_t reserved; uint16_t curve_length; uint16_t public_key_len; } __packed; #define CCA_PRIME_CURVE 0x00 #define CCA_BRAINPOOL_CURVE 0x01 struct cca_token_header { uint8_t token_identifier; uint8_t token_version1; /* Used for PKA key tokens */ uint16_t token_length; uint8_t token_version2; /* Used for symmetric key tokens */ uint8_t reserved[3]; } __packed; /* Key token identifiers */ #define CCA_TOKEN_ID_NULL 0x00 #define CCA_TOKEN_ID_EXTERNAL_PKA 0x1e #define CCA_TOKEN_ID_INTERNAL_PKA 0x1f /* Key token versions */ #define CCA_TOKEN_VERS1_V0 0x00 struct cca_section_header { uint8_t section_identifier; uint8_t section_version; uint16_t section_length; } __packed; #define CCA_SECTION_ID_RSA_ME_1024_PRIV 0x02 #define CCA_SECTION_ID_RSA_PUBL 0x04 #define CCA_SECTION_ID_RSA_CRT_2048_PRIV 0x05 #define CCA_SECTION_ID_RSA_ME_1024_OPK_PRIV 0x06 #define CCA_SECTION_ID_RSA_CRT_4096_OPK_PRIV 0x08 #define CCA_SECTION_ID_RSA_ME_4096_PRIV 0x09 #define CCA_SECTION_ID_EC_PRIV 0x20 #define CCA_SECTION_ID_EC_PUBL 0x21 #define CCA_SECTION_ID_RSA_ME_1024_EOPK_PRIV 0x30 #define CCA_SECTION_ID_RSA_CRT_4096_EOPK_PRIV 0x31 struct cca_ec_pub_key_section { struct cca_section_header section_header; uint8_t reserved1[4]; uint8_t curve_type; uint8_t reserved2; uint16_t prime_bits_length; uint16_t pub_key_length; /* Incl. compression indication byte */ /* Public key of length pub_key_length */ } __packed; struct cca_rsa_pub_key_section { struct cca_section_header section_header; uint16_t reserved1; uint16_t pub_exp_length; uint16_t modulus_bits_length; uint16_t modulus_length; /* if 0 -> see priv key section */ /* Public exponent of length pub_exp_length */ /* Modulus of length modulus_length */ } __packed; struct cca_rsa_crt_priv_key_section { struct cca_section_header section_header; uint16_t assoc_data_length; uint16_t payload_length; uint16_t reserved1; uint8_t assoc_data_version; uint8_t key_format; uint8_t key_source; uint8_t reserved2; uint8_t hash_type; uint8_t hash[32]; uint8_t reserved3[3]; uint8_t key_usage; uint8_t format_restriction; uint16_t p_length; uint16_t q_length; uint16_t dp_length; uint16_t dq_length; uint16_t u_length; uint16_t modulus_length; uint32_t reserved4; uint8_t opk[48]; uint8_t kvp[16]; uint16_t reserved6; /* Public modulus in length modulus_length */ /* Encrypted payload (AESKW-wrapped key material) */ } __packed; #define POINT_CONVERSION_ODD_EVEN 0x01 /** * Gets the CCA library function entry points from the library handle */ static int sk_cca_get_library_functions(const struct sk_ext_cca_lib *cca_lib, struct cca_lib *cca) { if (cca_lib == NULL || cca == NULL) return -EINVAL; cca->dll_CSNDPKG = (CSNDPKG_t)dlsym(cca_lib->cca_lib, "CSNDPKG"); cca->dll_CSNDPKB = (CSNDPKB_t)dlsym(cca_lib->cca_lib, "CSNDPKB"); cca->dll_CSNDKTC = (CSNDKTC_t)dlsym(cca_lib->cca_lib, "CSNDKTC"); cca->dll_CSNDDSG = (CSNDDSG_t)dlsym(cca_lib->cca_lib, "CSNDDSG"); cca->dll_CSNDPKD = (CSNDPKD_t)dlsym(cca_lib->cca_lib, "CSNDPKD"); if (cca->dll_CSNDPKG == NULL || cca->dll_CSNDPKB == NULL || cca->dll_CSNDKTC == NULL || cca->dll_CSNDDSG == NULL || cca->dll_CSNDPKD == NULL) return -EIO; return 0; } /** * Generates an CCA EC key of the specified curve type and length using the * CCA host library. * * @param cca_lib the CCA library structure * @param curve_nid the nid specifying the curve. * @param key_token a buffer to store the generated key token * @param key_token_length On entry: the size of the buffer * On return: the size of the key token * @param debug if true, debug messages are printed * * @returns a negative errno in case of an error, 0 if success. */ int SK_CCA_generate_ec_key_pair(const struct sk_ext_cca_lib *cca_lib, int curve_nid, unsigned char *key_token, size_t *key_token_length, bool debug) { long return_code, reason_code, rule_array_count, exit_data_len = 0; unsigned char transport_key_identifier[CCA_KEY_ID_SIZE] = { 0 }; unsigned char key_skeleton[CCA_MAX_PKA_KEY_TOKEN_SIZE] = { 0 }; long key_value_structure_length, private_key_name_length = 0; unsigned char regeneration_data[CCA_KEY_ID_SIZE] = { 0 }; struct cca_ec_key_pair_value_struct key_value_structure; unsigned char private_key_name[CCA_KEY_ID_SIZE] = { 0 }; unsigned char rule_array[3 * CCA_KEYWORD_SIZE] = { 0 }; long regeneration_data_length = 0, key_skeleton_length; const struct sk_ec_curve_info *curve; unsigned char *exit_data = NULL; unsigned char *param2 = NULL; struct cca_lib cca; long token_length; long param1 = 0; int rc; if (cca_lib == NULL || key_token == NULL || key_token_length == NULL) return -EINVAL; if (key_token == NULL) { *key_token_length = CCA_MAX_PKA_KEY_TOKEN_SIZE; return 0; } sk_debug(debug, "curve_nid: %d", curve_nid); rc = sk_cca_get_library_functions(cca_lib, &cca); if (rc != 0) { sk_debug(debug, "ERROR: Failed to get CCA functions from library"); return rc; } memset(key_token, 0, *key_token_length); token_length = *key_token_length; memset(&key_value_structure, 0, sizeof(key_value_structure)); curve = SK_UTIL_ec_get_curve_info(curve_nid); if (curve == NULL) { sk_debug(debug, "ERROR: Unsupported curve: %d", curve_nid); return -EINVAL; } switch (curve->type) { case SK_EC_TYPE_PRIME: key_value_structure.curve_type = CCA_PRIME_CURVE; break; case SK_EC_TYPE_BRAINPOOL: key_value_structure.curve_type = CCA_BRAINPOOL_CURVE; break; default: sk_debug(debug, "ERROR: Unknown curve type: %d", curve->type); return -EINVAL; } key_value_structure.curve_length = curve->prime_bits; key_value_structure_length = sizeof(key_value_structure); rule_array_count = 3; memcpy(rule_array, "ECC-PAIR", CCA_KEYWORD_SIZE); memcpy(rule_array + CCA_KEYWORD_SIZE, "KEY-MGMT", CCA_KEYWORD_SIZE); memcpy(rule_array + 2 * CCA_KEYWORD_SIZE, "ECC-VER1", CCA_KEYWORD_SIZE); key_skeleton_length = sizeof(key_skeleton); cca.dll_CSNDPKB(&return_code, &reason_code, &exit_data_len, exit_data, &rule_array_count, rule_array, &key_value_structure_length, (unsigned char *)&key_value_structure, &private_key_name_length, private_key_name, ¶m1, param2, ¶m1, param2, ¶m1, param2, ¶m1, param2, ¶m1, param2, &key_skeleton_length, key_skeleton); if (return_code != 0) { sk_debug(debug, "ERROR: CCA CSNDPKB (EC KEY TOKEN BUILD) " "failed: return_code: %ld reason_code: %ld", return_code, reason_code); return -EIO; } rule_array_count = 1; memset(rule_array, 0, sizeof(rule_array)); memcpy(rule_array, "MASTER ", (size_t)CCA_KEYWORD_SIZE); cca.dll_CSNDPKG(&return_code, &reason_code, NULL, NULL, &rule_array_count, rule_array, ®eneration_data_length, regeneration_data, &key_skeleton_length, key_skeleton, transport_key_identifier, &token_length, key_token); if (return_code != 0) { sk_debug(debug, "ERROR: CCA CSNDPKG (EC KEY GENERATE) failed: " "return_code: %ld reason_code: %ld", return_code, reason_code); return -EIO; } *key_token_length = token_length; return 0; } /** * Generates an CCA RSA key of the specified key size and optionally the * specified public exponent using the CCA host library. * * @param cca_lib the CCA library structure * @param modulus_bits the size of the key in bits (512, 1024, 2048, 4096) * @param pub_exp the public exponent or zero. Possible values are: * 3, 5, 17, 257, or 65537. Specify zero to choose the * exponent by random (only possible for modulus_bits * up to 2048). * @param key_token a buffer to store the generated key token * @param key_token_length On entry: the size of the buffer * On return: the size of the key token * @param debug if true, debug messages are printed * * @returns a negative errno in case of an error, 0 if success. */ int SK_CCA_generate_rsa_key_pair(const struct sk_ext_cca_lib *cca_lib, size_t modulus_bits, unsigned int pub_exp, unsigned char *key_token, size_t *key_token_length, bool debug) { long return_code, reason_code, rule_array_count, exit_data_len = 0; unsigned char transport_key_identifier[CCA_KEY_ID_SIZE] = { 0 }; unsigned char key_skeleton[CCA_MAX_PKA_KEY_TOKEN_SIZE] = { 0 }; long key_value_structure_length, private_key_name_length = 0; unsigned char regeneration_data[CCA_KEY_ID_SIZE] = { 0 }; struct cca_rsa_key_pair_value_struct key_value_structure; unsigned char private_key_name[CCA_KEY_ID_SIZE] = { 0 }; unsigned char rule_array[2 * CCA_KEYWORD_SIZE] = { 0 }; long regeneration_data_length = 0, key_skeleton_length; unsigned char *exit_data = NULL; unsigned char *param2 = NULL; struct cca_lib cca; long token_length; long param1 = 0; int rc; if (cca_lib == NULL || key_token == NULL || key_token_length == NULL) return -EINVAL; if (key_token == NULL) { *key_token_length = CCA_MAX_PKA_KEY_TOKEN_SIZE; return 0; } sk_debug(debug, "modulus_bits: %lu pub_exp: %u", modulus_bits, pub_exp); rc = sk_cca_get_library_functions(cca_lib, &cca); if (rc != 0) { sk_debug(debug, "ERROR: Failed to get CCA functions from library"); return rc; } memset(key_token, 0, *key_token_length); token_length = *key_token_length; memset(&key_value_structure, 0, sizeof(key_value_structure)); key_value_structure.modulus_bit_length = modulus_bits; switch (pub_exp) { case 0: if (modulus_bits > 2048) { sk_debug(debug, "ERROR: Cannot auto-generate public " "exponent for keys > 2048"); return -EINVAL; } key_value_structure.public_exp_length = 0; break; case 3: key_value_structure.public_exp_length = 1; key_value_structure.public_exponent[0] = 3; break; case 5: key_value_structure.public_exp_length = 1; key_value_structure.public_exponent[0] = 5; break; case 17: key_value_structure.public_exp_length = 1; key_value_structure.public_exponent[0] = 17; break; case 257: key_value_structure.public_exp_length = 2; key_value_structure.public_exponent[0] = 0x01; key_value_structure.public_exponent[0] = 0x01; break; case 65537: key_value_structure.public_exp_length = 3; key_value_structure.public_exponent[0] = 0x01; key_value_structure.public_exponent[1] = 0x00; key_value_structure.public_exponent[2] = 0x01; break; default: sk_debug(debug, "ERROR: Invalid public exponent: %d", pub_exp); return -EINVAL; } key_value_structure_length = sizeof(key_value_structure) + key_value_structure.public_exp_length; rule_array_count = 2; memcpy(rule_array, "RSA-AESC", CCA_KEYWORD_SIZE); memcpy(rule_array + CCA_KEYWORD_SIZE, "KEY-MGMT", CCA_KEYWORD_SIZE); key_skeleton_length = sizeof(key_skeleton); cca.dll_CSNDPKB(&return_code, &reason_code, &exit_data_len, exit_data, &rule_array_count, rule_array, &key_value_structure_length, (unsigned char *)&key_value_structure, &private_key_name_length, private_key_name, ¶m1, param2, ¶m1, param2, ¶m1, param2, ¶m1, param2, ¶m1, param2, &key_skeleton_length, key_skeleton); if (return_code != 0) { sk_debug(debug, "ERROR: CCA CSNDPKB (RSA KEY TOKEN BUILD) " "failed: return_code: %ld reason_code: %ld", return_code, reason_code); return -EIO; } rule_array_count = 1; memset(rule_array, 0, sizeof(rule_array)); memcpy(rule_array, "MASTER ", (size_t)CCA_KEYWORD_SIZE); cca.dll_CSNDPKG(&return_code, &reason_code, NULL, NULL, &rule_array_count, rule_array, ®eneration_data_length, regeneration_data, &key_skeleton_length, key_skeleton, transport_key_identifier, &token_length, key_token); if (return_code != 0) { sk_debug(debug, "ERROR: CCA CSNDPKG (RSA KEY GENERATE) failed: " "return_code: %ld reason_code: %ld", return_code, reason_code); return -EIO; } *key_token_length = token_length; return 0; } /** * Finds a specific section of a CCA internal PKA key token. */ static const void *sk_cca_get_pka_section(const unsigned char *key_token, size_t key_token_length, unsigned int section_id, bool debug) { const struct cca_section_header *section_hdr; const struct cca_token_header *token_hdr; size_t ofs; if (key_token == NULL) return NULL; sk_debug(debug, "section_id: %x", section_id); if (key_token_length < sizeof(struct cca_token_header)) { sk_debug(debug, "ERROR: key token length too small"); return NULL; } token_hdr = (struct cca_token_header *)key_token; if (token_hdr->token_length > key_token_length) { sk_debug(debug, "ERROR: key token length too small"); return NULL; } if (token_hdr->token_identifier != CCA_TOKEN_ID_INTERNAL_PKA) { sk_debug(debug, "ERROR: not an internal PKA token"); return NULL; } if (token_hdr->token_version1 != CCA_TOKEN_VERS1_V0) { sk_debug(debug, "ERROR: invalid token version"); return NULL; } ofs = sizeof(struct cca_token_header); section_hdr = (struct cca_section_header *)&key_token[ofs]; while (section_hdr->section_identifier != section_id) { ofs += section_hdr->section_length; if (ofs >= token_hdr->token_length) { sk_debug(debug, "ERROR: section %u not found", section_id); return NULL; } section_hdr = (struct cca_section_header *)&key_token[ofs]; } if (ofs + section_hdr->section_length > token_hdr->token_length) { sk_debug(debug, "ERROR: section exceed the token length"); return NULL; } return section_hdr; } /** * Queries the PKEY type of the key token. * * @param key_token the key token containing an CCA EC key * @param key_token_length the size of the key token * @param pkey_type On return: the PKEY type of the key token * * @returns a negative errno in case of an error, 0 if success. */ int SK_CCA_get_key_type(const unsigned char *key_token, size_t key_token_length, int *pkey_type) { if (key_token == NULL || pkey_type == NULL) return -EINVAL; if (sk_cca_get_pka_section(key_token, key_token_length, CCA_SECTION_ID_EC_PUBL, false) != NULL) *pkey_type = EVP_PKEY_EC; else if (sk_cca_get_pka_section(key_token, key_token_length, CCA_SECTION_ID_RSA_PUBL, false) != NULL) *pkey_type = EVP_PKEY_RSA; else return -EINVAL; return 0; } /** * Sign data using RSA. * * @param key_token the RSA key token * @param key_token_length the length of the key token * @param sig a buffer to store the signature on return. * @param siglen on input: the size if the signature buffer * on return: the size of the signature * @param tbs the data to be signed. * @param tbslen the size of the data to be signed * @param padding_type the OpenSSL padding type (RSA_X931_PADDING or * RSA_PKCS1_PADDING) * @param digest_nid the OpenSSL nid of the message digest used to * produce the data to be signed * @param private the CCA library structure * @param debug if true, debug messages are printed * * @returns a negative errno in case of an error, 0 if success. */ static int sk_cca_rsa_sign(const unsigned char *key_token, size_t key_token_length, unsigned char *sig, size_t *siglen, const unsigned char *tbs, size_t tbslen, int padding_type, int md_nid, void *private, bool debug) { long return_code, reason_code, rule_array_count, exit_data_len = 0; long token_length, hash_length, sign_bit_length, sign_length; unsigned char rule_array[4 * CCA_KEYWORD_SIZE] = { 0 }; const struct sk_ext_cca_lib *cca_lib = private; unsigned char *hash = NULL, *buf = NULL; const struct sk_digest_info *digest; unsigned char *exit_data = NULL; struct cca_lib cca; int rc; if (cca_lib == NULL || key_token == NULL || sig == NULL || siglen == NULL || tbs == NULL) return -EINVAL; sk_debug(debug, "tbslen: %lu siglen: %lu padding_type: %d md_nid: %d", tbslen, *siglen, padding_type, md_nid); rc = sk_cca_get_library_functions(cca_lib, &cca); if (rc != 0) { sk_debug(debug, "ERROR: Failed to get CCA functions from library"); return rc; } digest = SK_UTIL_get_digest_info(md_nid); if (digest == NULL) { sk_debug(debug, "ERROR: Invalid digest nid: %d", md_nid); return -EINVAL; } if (tbslen != digest->digest_size) { sk_debug(debug, "ERROR: Invalid data length: %lu", tbslen); return -EINVAL; } rule_array_count = 2; memcpy(rule_array, "RSA ", CCA_KEYWORD_SIZE); memcpy(rule_array + CCA_KEYWORD_SIZE, "HASH ", CCA_KEYWORD_SIZE); switch (padding_type) { case RSA_X931_PADDING: hash = (unsigned char *)tbs; hash_length = tbslen; memcpy(rule_array + 2 * CCA_KEYWORD_SIZE, "X9.31 ", CCA_KEYWORD_SIZE); memcpy(rule_array + 3 * CCA_KEYWORD_SIZE, digest->cca_keyword, CCA_KEYWORD_SIZE); rule_array_count = 4; break; case RSA_PKCS1_PADDING: hash_length = digest->der_size + tbslen; buf = (unsigned char *)malloc(hash_length); if (buf == NULL) { sk_debug(debug, "ERROR: malloc failed"); return -ENOMEM; } memcpy(buf, digest->der, digest->der_size); memcpy(buf + digest->der_size, tbs, tbslen); hash = buf; memcpy(rule_array + 2 * CCA_KEYWORD_SIZE, "PKCS-1.1", CCA_KEYWORD_SIZE); rule_array_count = 3; break; default: sk_debug(debug, "ERROR: Invalid padding type: %d", padding_type); return -EINVAL; } token_length = key_token_length; sign_length = *siglen; cca.dll_CSNDDSG(&return_code, &reason_code, &exit_data_len, exit_data, &rule_array_count, rule_array, &token_length, (unsigned char *)key_token, &hash_length, hash, &sign_length, &sign_bit_length, sig); if (return_code != 0) { sk_debug(debug, "ERROR: CCA CSNDDSG (DIG. SIGNATURE CREATE, " "RSA) failed: return_code: %ld reason_code: %ld", return_code, reason_code); rc = -EIO; goto out; } *siglen = sign_length; rc = 0; sk_debug(debug, "siglen: %lu", *siglen); out: if (buf != NULL) free(buf); return rc; } /** * Sign data using RSA-PSS. * * @param key_token the RSA key token * @param key_token_length the length of the key token * @param sig a buffer to store the signature on return. * @param siglen on input: the size if the signature buffer * on return: the size of the signature * @param tbs the data to be signed. * @param tbslen the size of the data to be signed * @param digest_nid the OpenSSL nid of the message digest used to * produce the data to be signed * @param mgf_digest_nid the OpenSSL nid of the mask generation function for * PSS padding * @param saltlen the length of the salt for PSS * @param private the CCA library structure * @param debug if true, debug messages are printed * * @returns a negative errno in case of an error, 0 if success. */ static int sk_cca_rsa_pss_sign(const unsigned char *key_token, size_t key_token_length, unsigned char *sig, size_t *siglen, const unsigned char *tbs, size_t tbslen, int digest_nid, int mgf_digest_nid, int saltlen, void *private, bool debug) { long return_code, reason_code, rule_array_count, exit_data_len = 0; long token_length, hash_length, sign_bit_length, sign_length; unsigned char rule_array[4 * CCA_KEYWORD_SIZE] = { 0 }; const struct sk_ext_cca_lib *cca_lib = private; const struct sk_digest_info *digest; unsigned char *exit_data = NULL; unsigned char *buf = NULL; struct cca_lib cca; uint32_t salt_len; int rc; if (cca_lib == NULL || key_token == NULL || sig == NULL || siglen == NULL || tbs == NULL) return -EINVAL; sk_debug(debug, "tbslen: %lu siglen: %lu digest_nid: %d " "mgf_digest_nid: %d saltlen: %d", tbslen, *siglen, digest_nid, mgf_digest_nid, saltlen); rc = sk_cca_get_library_functions(cca_lib, &cca); if (rc != 0) { sk_debug(debug, "ERROR: Failed to get CCA functions from library"); return rc; } if (mgf_digest_nid != digest_nid) { sk_debug(debug, "ERROR: Mgf nid must be the same as the " "message digest nid"); return -EINVAL; } digest = SK_UTIL_get_digest_info(digest_nid); if (digest == NULL || digest->cca_keyword == NULL) { sk_debug(debug, "ERROR: Invalid digest nid: %d", digest_nid); return -EINVAL; } if (tbslen != digest->digest_size) { sk_debug(debug, "ERROR: Invalid data length: %lu", tbslen); return -EINVAL; } rule_array_count = 4; memcpy(rule_array, "RSA ", CCA_KEYWORD_SIZE); memcpy(rule_array + CCA_KEYWORD_SIZE, "PKCS-PSS", CCA_KEYWORD_SIZE); memcpy(rule_array + 2 * CCA_KEYWORD_SIZE, "HASH ", CCA_KEYWORD_SIZE); memcpy(rule_array + 3 * CCA_KEYWORD_SIZE, digest->cca_keyword, CCA_KEYWORD_SIZE); hash_length = sizeof(uint32_t) + tbslen; buf = (unsigned char *)malloc(hash_length); if (buf == NULL) { sk_debug(debug, "ERROR: malloc failed"); return -ENOMEM; } salt_len = saltlen; memcpy(buf, &salt_len, sizeof(uint32_t)); memcpy(buf + sizeof(uint32_t), tbs, tbslen); token_length = key_token_length; sign_length = *siglen; cca.dll_CSNDDSG(&return_code, &reason_code, &exit_data_len, exit_data, &rule_array_count, rule_array, &token_length, (unsigned char *)key_token, &hash_length, buf, &sign_length, &sign_bit_length, sig); if (return_code != 0) { sk_debug(debug, "ERROR: CCA CSNDDSG (DIG. SIGNATURE CREATE, " "RSA-PSS) failed: return_code: %ld reason_code: %ld", return_code, reason_code); rc = -EIO; goto out; } *siglen = sign_length; rc = 0; sk_debug(debug, "siglen: %lu", *siglen); out: free(buf); return rc; } /** * Decrypt data using RSA. * * @param key_token the RSA key token * @param key_token_length the length of the key token * @param to a buffer to store the decrypted data on return. * @param tolen on input: the size if the to buffer * on return: the size of the decrypted data * @param from the data to be decrypted. * @param fromlen the size of the data to be decrypted * @param padding_type the OpenSSL padding type * @param private the CCA library structure * @param debug if true, debug messages are printed * * @returns a negative errno in case of an error, 0 if success. */ static int sk_cca_rsa_decrypt(const unsigned char *key_token, size_t key_token_length, unsigned char *to, size_t *tolen, const unsigned char *from, size_t fromlen, int padding_type, void *private, bool debug) { long return_code, reason_code, rule_array_count, exit_data_len = 0; long token_length, from_length, to_length, data_struct_len = 0; unsigned char rule_array[3 * CCA_KEYWORD_SIZE] = { 0 }; const struct sk_ext_cca_lib *cca_lib = private; unsigned char *data_struct = NULL; unsigned char *exit_data = NULL; struct cca_lib cca; int rc; if (cca_lib == NULL || key_token == NULL || to == NULL || tolen == NULL || from == NULL) return -EINVAL; sk_debug(debug, "fromlen: %lu tolen: %lu padding_type: %d", fromlen, *tolen, padding_type); rc = sk_cca_get_library_functions(cca_lib, &cca); if (rc != 0) { sk_debug(debug, "ERROR: Failed to get CCA functions from library"); return rc; } rule_array_count = 1; switch (padding_type) { case RSA_PKCS1_PADDING: memcpy(rule_array, "PKCS-1.2", CCA_KEYWORD_SIZE); break; default: sk_debug(debug, "ERROR: Invalid padding type: %d", padding_type); return -EINVAL; } token_length = key_token_length; from_length = fromlen; to_length = *tolen; if (to_length > from_length) to_length = from_length; cca.dll_CSNDPKD(&return_code, &reason_code, &exit_data_len, exit_data, &rule_array_count, rule_array, &from_length, (unsigned char *)from, &data_struct_len, data_struct, &token_length, (unsigned char *)key_token, &to_length, to); if (return_code != 0) { sk_debug(debug, "ERROR: CCA CSNDPKD (PKA DECRYPT) " "failed: return_code: %ld reason_code: %ld", return_code, reason_code); rc = -EIO; goto out; } *tolen = to_length; rc = 0; sk_debug(debug, "tolen: %lu", *tolen); out: return rc; } /** * Decrypt data using RSA OAEP. * * @param key_token the RSA key token * @param key_token_length the length of the key token * @param to a buffer to store the decrypted data on return. * @param tolen on input: the size if the to buffer * on return: the size of the decrypted data * @param from the data to be decrypted. * @param fromlen the size of the data to be decrypted * @param oaep_md_nid the OpenSSL nid of the OAEP hashing algorithm * @param mgfmd_nid the OpenSSL nid of the mask generation function * @param label the label for OAEP * @param label_len the length of the label for OAEP * @param private the CCA library structure * @param debug if true, debug messages are printed * * @returns a negative errno in case of an error, 0 if success. */ static int sk_cca_rsa_decrypt_oaep(const unsigned char *key_token, size_t key_token_length, unsigned char *to, size_t *tolen, const unsigned char *from, size_t fromlen, int oaep_md_nid, int mgfmd_nid, unsigned char *UNUSED(label), int label_len, void *private, bool debug) { long return_code, reason_code, rule_array_count, exit_data_len = 0; long token_length, from_length, to_length, data_struct_len = 0; unsigned char rule_array[3 * CCA_KEYWORD_SIZE] = { 0 }; const struct sk_ext_cca_lib *cca_lib = private; unsigned char *data_struct = NULL; const struct sk_digest_info *digest; unsigned char *exit_data = NULL; struct cca_lib cca; int rc; if (cca_lib == NULL || key_token == NULL || to == NULL || tolen == NULL || from == NULL) return -EINVAL; sk_debug(debug, "fromlen: %lu tolen: %lu oaep_md_nid: %d mgfmd_nid: %d", fromlen, *tolen, oaep_md_nid, mgfmd_nid); rc = sk_cca_get_library_functions(cca_lib, &cca); if (rc != 0) { sk_debug(debug, "ERROR: Failed to get CCA functions from library"); return rc; } if (label_len != 0) { sk_debug(debug, "ERROR: CCA does not support non-empty OAEP " "label"); return -EINVAL; } if (oaep_md_nid != mgfmd_nid) { sk_debug(debug, "ERROR: Mgf nid must be the same as the oaep " "nid"); return -EINVAL; } digest = SK_UTIL_get_digest_info(mgfmd_nid); if (digest == NULL || digest->cca_keyword == NULL) { sk_debug(debug, "ERROR: Invalid mgf nid: %d", mgfmd_nid); return -EINVAL; } rule_array_count = 2; memcpy(rule_array, "PKCSOAEP", CCA_KEYWORD_SIZE); memcpy(rule_array + CCA_KEYWORD_SIZE, digest->cca_keyword, CCA_KEYWORD_SIZE); token_length = key_token_length; from_length = fromlen; to_length = *tolen; if (to_length > from_length) to_length = from_length; cca.dll_CSNDPKD(&return_code, &reason_code, &exit_data_len, exit_data, &rule_array_count, rule_array, &from_length, (unsigned char *)from, &data_struct_len, data_struct, &token_length, (unsigned char *)key_token, &to_length, to); if (return_code != 0) { sk_debug(debug, "ERROR: CCA CSNDPKD (PKA DECRYPT) " "failed: return_code: %ld reason_code: %ld", return_code, reason_code); rc = -EIO; goto out; } *tolen = to_length; rc = 0; sk_debug(debug, "tolen: %lu", *tolen); out: return rc; } /** * Sign data using ECDSA. * * @param key_token the RSA key token * @param key_token_length the length of the key token * @param sig a buffer to store the signature on return. * @param siglen on input: the size if the signature buffer * on return: the size of the signature * @param tbs the data to be signed. * @param tbslen the size of the data to be signed * @param digest_nid the OpenSSL nid of the message digest used to * produce the data to be signed * @param private the CCA library structure * @param debug if true, debug messages are printed * * @returns a negative errno in case of an error, 0 if success. */ static int sk_cca_ecdsa_sign(const unsigned char *key_token, size_t key_token_length, unsigned char *sig, size_t *siglen, const unsigned char *tbs, size_t tbslen, int digest_nid, void *private, bool debug) { long return_code, reason_code, rule_array_count, exit_data_len = 0; long token_length, hash_length, sign_bit_length, sign_length; unsigned char rule_array[2 * CCA_KEYWORD_SIZE] = { 0 }; const struct sk_ext_cca_lib *cca_lib = private; unsigned char *exit_data = NULL; struct cca_lib cca; int rc; if (cca_lib == NULL || key_token == NULL || sig == NULL || siglen == NULL || tbs == NULL) return -EINVAL; sk_debug(debug, "tbslen: %lu siglen: %lu digest_nid: %d", tbslen, *siglen, digest_nid); rc = sk_cca_get_library_functions(cca_lib, &cca); if (rc != 0) { sk_debug(debug, "ERROR: Failed to get CCA functions from library"); return rc; } rule_array_count = 2; memcpy(rule_array, "ECDSA ", CCA_KEYWORD_SIZE); memcpy(rule_array + CCA_KEYWORD_SIZE, "HASH ", CCA_KEYWORD_SIZE); hash_length = tbslen; token_length = key_token_length; sign_length = *siglen; cca.dll_CSNDDSG(&return_code, &reason_code, &exit_data_len, exit_data, &rule_array_count, rule_array, &token_length, (unsigned char *)key_token, &hash_length, (unsigned char *)tbs, &sign_length, &sign_bit_length, sig); if (return_code != 0) { sk_debug(debug, "ERROR: CCA CSNDDSG (DIG. SIGNATURE CREATE, " "ECDSA) failed: return_code: %ld reason_code: %ld", return_code, reason_code); return -EIO; } rc = SK_UTIL_build_ecdsa_signature(sig, sign_length, sig, siglen); if (rc != 0) { sk_debug(debug, "ERROR: build_ecdsa_signature failed"); return -EIO; } sk_debug(debug, "siglen: %lu", *siglen); return 0; } static const struct sk_funcs sk_cca_funcs = { .rsa_sign = sk_cca_rsa_sign, .rsa_pss_sign = sk_cca_rsa_pss_sign, .rsa_decrypt = sk_cca_rsa_decrypt, .rsa_decrypt_oaep = sk_cca_rsa_decrypt_oaep, .ecdsa_sign = sk_cca_ecdsa_sign, }; struct pub_key_cb_data { const struct sk_ext_cca_lib *cca_lib; const unsigned char *key_token; size_t key_token_length; bool rsa_pss; EVP_PKEY *pkey; bool debug; }; /* * Callback for generating an PKEY from a secure key */ static int sk_cca_get_secure_key_as_pkey_cb( const struct sk_pub_key_info *pub_key, void *private) { struct pub_key_cb_data *data = private; int rc; if (pub_key == NULL || data == NULL) return -EINVAL; rc = SK_OPENSSL_get_pkey(data->key_token, data->key_token_length, pub_key, data->rsa_pss, &sk_cca_funcs, data->cca_lib, &data->pkey, data->debug); if (rc != 0) { sk_debug(data->debug, "ERROR: SK_OPENSSL_get_pkey failed"); return rc; } sk_debug(data->debug, "pkey: %p", data->pkey); return 0; } /** * Extracts the public key from a CCA internal RSA or EC key token, and returns * it as OpenSSL PKEY. * * @param cca_lib the CCA library structure * @param key_token the key token containing an CCA secure key * @param key_token_length the size of the key token * @param rsa_pss For RSA public keys: create a RSA-PSS type PKEY * @param pkey On return: a PKEY containing the public key * @param debug if true, debug messages are printed * * @returns a negative errno in case of an error, 0 if success. */ int SK_CCA_get_secure_key_as_pkey(const struct sk_ext_cca_lib *cca_lib, const unsigned char *key_token, size_t key_token_length, bool rsa_pss, EVP_PKEY **pkey, bool debug) { struct pub_key_cb_data data; int rc; sk_debug(debug, "rsa_pss: %d", rsa_pss); data.cca_lib = cca_lib; data.key_token = key_token; data.key_token_length = key_token_length; data.rsa_pss = rsa_pss; data.pkey = NULL; data.debug = debug; rc = SK_CCA_get_public_from_secure_key(key_token, key_token_length, sk_cca_get_secure_key_as_pkey_cb, &data, debug); if (rc != 0) { sk_debug(debug, "ERROR: SK_CCA_get_public_from_secure_key failed"); return rc; } sk_debug(debug, "pkey: %p", data.pkey); *pkey = data.pkey; return 0; } /** * Extracts the public key from a CCA internal RSA or EC key token, and calls * the specified callback function with the public key information. * * @param key_token the key token containing an CCA secure key * @param key_token_length the size of the key token * @param pub_key_cb the callback function to call with the public key * @param private a private pointer passed as is to the callback * @param debug if true, debug messages are printed * * @returns a negative errno in case of an error, 0 if success. */ static int sk_cca_get_public_from_ec_key(const unsigned char *key_token, size_t key_token_length, sk_pub_key_func_t pub_key_cb, void *private, bool debug) { struct cca_ec_pub_key_section *ec_pub_section; struct sk_pub_key_info pub_key = { 0 }; const struct sk_ec_curve_info *curve; const unsigned char *ec_pub_key; unsigned char *buf = NULL; int y_bit = 0; int rc = 0; if (key_token == NULL || pub_key_cb == NULL) return -EINVAL; pub_key.type = SK_KEY_TYPE_EC; ec_pub_section = (struct cca_ec_pub_key_section *) sk_cca_get_pka_section(key_token, key_token_length, CCA_SECTION_ID_EC_PUBL, debug); if (ec_pub_section == NULL) return -EINVAL; if (ec_pub_section->section_header.section_version != 0x00) { sk_debug(debug, "ERROR: invalid EC public key section version"); return -EINVAL; } if (ec_pub_section->section_header.section_length < sizeof(struct cca_ec_pub_key_section)) { sk_debug(debug, "ERROR: invalid EC public key section length"); return -EINVAL; } ec_pub_key = ((unsigned char *)ec_pub_section) + sizeof(struct cca_ec_pub_key_section); sk_debug(debug, "CCA curve_type: %u", ec_pub_section->curve_type); if (ec_pub_section->curve_type == CCA_PRIME_CURVE) pub_key.ec.curve_nid = SK_UTIL_ec_get_prime_curve_by_prime_bits( ec_pub_section->prime_bits_length); else if (ec_pub_section->curve_type == CCA_BRAINPOOL_CURVE) pub_key.ec.curve_nid = SK_UTIL_ec_get_brainpool_curve_by_prime_bits( ec_pub_section->prime_bits_length); else pub_key.ec.curve_nid = 0; sk_debug(debug, "curve_nid: %d", pub_key.ec.curve_nid); curve = SK_UTIL_ec_get_curve_info(pub_key.ec.curve_nid); if (pub_key.ec.curve_nid == 0 || curve == NULL) { sk_debug(debug, "ERROR: unsupported curve: %d", pub_key.ec.curve_nid); rc = -EIO; goto out; } pub_key.ec.prime_len = curve->prime_len; sk_debug(debug, "prime_len: %lu", pub_key.ec.prime_len); if (ec_pub_section->pub_key_length != 2 * pub_key.ec.prime_len + 1) { sk_debug(debug, "ERROR: invalid public key length"); return -EINVAL; } pub_key.ec.x = ec_pub_key + 1; /* First byte of public key contains indication of key compression */ switch (ec_pub_key[0]) { case POINT_CONVERSION_COMPRESSED: case POINT_CONVERSION_COMPRESSED + POINT_CONVERSION_ODD_EVEN: /* Compressed form, only x is available */ y_bit = (ec_pub_key[0] & POINT_CONVERSION_ODD_EVEN) ? 1 : 0; buf = malloc(pub_key.ec.prime_len); if (buf == NULL) { sk_debug(debug, "ERROR: malloc failed"); rc = -ENOMEM; goto out; } rc = SK_UTIL_ec_calculate_y_coordinate(pub_key.ec.curve_nid, pub_key.ec.prime_len, pub_key.ec.x, y_bit, buf); if (rc != 0) { sk_debug(debug, "ERROR: ec_calculate_y_coordinate " "failed"); goto out; } pub_key.ec.y = buf; break; case POINT_CONVERSION_UNCOMPRESSED: case POINT_CONVERSION_HYBRID: case POINT_CONVERSION_HYBRID + POINT_CONVERSION_ODD_EVEN: /* Uncompressed or hybrid, x and y are available */ pub_key.ec.y = pub_key.ec.x + pub_key.ec.prime_len; break; default: sk_debug(debug, "ERROR: invalid compression indication"); rc = -EIO; goto out; } rc = pub_key_cb(&pub_key, private); if (rc != 0) { sk_debug(debug, "ERROR: pub_key_cb failed"); goto out; } out: if (buf != NULL) free(buf); return rc; } /** * Extracts the public key from a CCA internal RSA or EC key token, and calls * the specified callback function with the public key information. * * @param key_token the key token containing an CCA secure key * @param key_token_length the size of the key token * @param pub_key_cb the callback function to call with the public key * @param private a private pointer passed as is to the callback * @param debug if true, debug messages are printed * * @returns a negative errno in case of an error, 0 if success. */ static int sk_cca_get_public_from_rsa_key(const unsigned char *key_token, size_t key_token_length, sk_pub_key_func_t pub_key_cb, void *private, bool debug) { const struct cca_rsa_crt_priv_key_section *rsa_priv_section; const struct cca_rsa_pub_key_section *rsa_pub_section; struct sk_pub_key_info pub_key = { 0 }; int rc = 0; if (key_token == NULL || pub_key_cb == NULL) return -EINVAL; pub_key.type = SK_KEY_TYPE_RSA; rsa_pub_section = (struct cca_rsa_pub_key_section *) sk_cca_get_pka_section(key_token, key_token_length, CCA_SECTION_ID_RSA_PUBL, debug); if (rsa_pub_section == NULL) return -EINVAL; if (rsa_pub_section->section_header.section_version != 0x00) { sk_debug(debug, "ERROR: invalid RSA public key section version"); return -EINVAL; } if (rsa_pub_section->section_header.section_length < sizeof(struct cca_ec_pub_key_section)) { sk_debug(debug, "ERROR: invalid RSA public key section length"); return -EINVAL; } pub_key.rsa.pub_exp = ((unsigned char *)rsa_pub_section) + sizeof(struct cca_rsa_pub_key_section); pub_key.rsa.pub_exp_len = rsa_pub_section->pub_exp_length; pub_key.rsa.modulus = pub_key.rsa.pub_exp + rsa_pub_section->pub_exp_length; pub_key.rsa.modulus_len = rsa_pub_section->modulus_length; /* * The public key section may have a modulus_length of zero, need to * get the modulus from the private key section instead. */ if (rsa_pub_section->modulus_length == 0) { rsa_priv_section = (struct cca_rsa_crt_priv_key_section *) sk_cca_get_pka_section(key_token, key_token_length, CCA_SECTION_ID_RSA_CRT_4096_EOPK_PRIV, debug); if (rsa_priv_section == NULL) return -EINVAL; if (rsa_priv_section->section_header.section_version != 0x00) { sk_debug(debug, "ERROR: invalid RSA private key " "section version"); return -EINVAL; } if (rsa_priv_section->section_header.section_length < sizeof(struct cca_rsa_crt_priv_key_section)) { sk_debug(debug, "ERROR: invalid RSA private key " "section length"); return -EINVAL; } pub_key.rsa.modulus = ((unsigned char *)rsa_priv_section) + sizeof(struct cca_rsa_crt_priv_key_section); pub_key.rsa.modulus_len = rsa_priv_section->modulus_length; } rc = pub_key_cb(&pub_key, private); if (rc != 0) { sk_debug(debug, "ERROR: pub_key_cb failed"); goto out; } out: return rc; } /** * Extracts the public key from a CCA internal RSA or EC key token, and calls * the specified callback function with the public key information. * * @param key_token the key token containing an CCA secure key * @param key_token_length the size of the key token * @param pub_key_cb the callback function to call with the public key * @param private a private pointer passed as is to the callback * @param debug if true, debug messages are printed * * @returns a negative errno in case of an error, 0 if success. */ int SK_CCA_get_public_from_secure_key(const unsigned char *key_token, size_t key_token_length, sk_pub_key_func_t pub_key_cb, void *private, bool debug) { int rc, pkey_type; rc = SK_CCA_get_key_type(key_token, key_token_length, &pkey_type); if (rc != 0) { sk_debug(debug, "ERROR: Failed to get the CCA key type: %s", strerror(-rc)); return rc; } sk_debug(debug, "pkey_type: %d", pkey_type); switch (pkey_type) { case EVP_PKEY_EC: rc = sk_cca_get_public_from_ec_key(key_token, key_token_length, pub_key_cb, private, debug); if (rc != 0) { sk_debug(debug, "ERROR: sk_cca_get_public_from_ec_key failed"); return rc; } break; case EVP_PKEY_RSA: case EVP_PKEY_RSA_PSS: rc = sk_cca_get_public_from_rsa_key(key_token, key_token_length, pub_key_cb, private, debug); if (rc != 0) { sk_debug(debug, "ERROR: sk_cca_get_public_from_rsa_key failed"); return rc; } break; default: sk_debug(debug, "ERROR: Invalid key type: %d", pkey_type); return -EIO; } return 0; } /** * Reenciphers a CCA secure key with a new CCA master key * * @param cca_lib the CCA library structure * @param key_token the key token containing an CCA secure key * @param key_token_length the size of the key token * @param to_new if true, reencipher with the MK in then NEW register * @param debug if true, debug messages are printed * * @returns a negative errno in case of an error, 0 if success. */ int SK_CCA_reencipher_key(const struct sk_ext_cca_lib *cca_lib, unsigned char *key_token, size_t key_token_length, bool to_new, bool debug) { long return_code, reason_code, rule_array_count, exit_data_len = 0; unsigned char rule_array[2 * CCA_KEYWORD_SIZE] = { 0 }; unsigned char *exit_data = NULL; struct cca_lib cca; long token_length; int rc, type; if (cca_lib == NULL || key_token == NULL) return -EINVAL; sk_debug(debug, "to_new: %d", to_new); rc = sk_cca_get_library_functions(cca_lib, &cca); if (rc != 0) { sk_debug(debug, "ERROR: Failed to get CCA functions from library"); return rc; } rc = SK_CCA_get_key_type(key_token, key_token_length, &type); if (rc != 0) { sk_debug(debug, "ERROR: Failed to determine the key token type"); return rc; } rule_array_count = 2; switch (type) { case EVP_PKEY_EC: memcpy(rule_array, "ECC ", CCA_KEYWORD_SIZE); break; case EVP_PKEY_RSA: case EVP_PKEY_RSA_PSS: memcpy(rule_array, "RSA ", CCA_KEYWORD_SIZE); break; default: sk_debug(debug, "ERROR: Invalid key token type: %d", type); return -EINVAL; } if (to_new) memcpy(rule_array + CCA_KEYWORD_SIZE, "RTNMK ", CCA_KEYWORD_SIZE); else memcpy(rule_array + CCA_KEYWORD_SIZE, "RTCMK ", CCA_KEYWORD_SIZE); token_length = key_token_length; cca.dll_CSNDKTC(&return_code, &reason_code, &exit_data_len, exit_data, &rule_array_count, rule_array, &token_length, key_token); if (return_code != 0) { sk_debug(debug, "ERROR: CCA CSNDKTC (PKA KEY TOKEN CHANGE) " "failed: return_code: %ld reason_code: %ld", return_code, reason_code); if (return_code == 12 && reason_code == 764) { sk_debug(debug, "ERROR: The master keys are not loaded"); return -ENODEV; } return -EIO; } return 0; } s390-tools-2.38.0/libseckey/sk_ep11.c000066400000000000000000001324661502674226300170440ustar00rootroot00000000000000/* * libseckey - Secure key library * * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include "lib/zt_common.h" #include "libseckey/sk_ep11.h" #include "libseckey/sk_openssl.h" #include "libseckey/sk_utilities.h" /* EP11 library definitions */ #define XCP_SERIALNR_CHARS 8 #define XCP_ADMCTR_BYTES ((size_t) (128/8)) #define XCP_KEYCSUM_BYTES (256/8) #define XCP_ADM_REENCRYPT 25 /* transform blobs to next WK */ #define CKR_VENDOR_DEFINED 0x80000000 #define CKR_IBM_WKID_MISMATCH (CKR_VENDOR_DEFINED + 0x10001) typedef struct XCPadmresp { uint32_t fn; uint32_t domain; uint32_t domainInst; /* module ID || module instance */ unsigned char module[XCP_SERIALNR_CHARS + XCP_SERIALNR_CHARS]; unsigned char modNr[XCP_SERIALNR_CHARS]; unsigned char modInst[XCP_SERIALNR_CHARS]; unsigned char tctr[XCP_ADMCTR_BYTES]; /* transaction counter */ CK_RV rv; uint32_t reason; const unsigned char *payload; size_t pllen; } *XCPadmresp_t; typedef struct CK_IBM_DOMAIN_INFO { CK_ULONG domain; CK_BYTE wk[XCP_KEYCSUM_BYTES]; CK_BYTE nextwk[XCP_KEYCSUM_BYTES]; CK_ULONG flags; CK_BYTE mode[8]; } CK_IBM_DOMAIN_INFO; #define CK_IBM_DOM_COMMITTED_NWK 8 #define CK_IBM_XCPQ_DOMAIN 3 typedef CK_RV (*m_GenerateKeyPair_t)(CK_MECHANISM_PTR mech, CK_ATTRIBUTE_PTR public, CK_ULONG pubattrs, CK_ATTRIBUTE_PTR private, CK_ULONG prvattrs, const unsigned char *pin, size_t pinlen, unsigned char *key, size_t *klen, unsigned char *pubkey, size_t *pklen, target_t target); typedef CK_RV (*m_SignSingle_t)(const unsigned char *key, size_t klen, CK_MECHANISM_PTR pmech, CK_BYTE_PTR data, CK_ULONG dlen, CK_BYTE_PTR sig, CK_ULONG_PTR slen, target_t target); typedef CK_RV (*m_DecryptSingle_t)(const unsigned char *key, size_t klen, CK_MECHANISM_PTR mech, CK_BYTE_PTR cipher, CK_ULONG clen, CK_BYTE_PTR plain, CK_ULONG_PTR plen, target_t target); typedef CK_RV (*m_get_xcp_info_t)(CK_VOID_PTR pinfo, CK_ULONG_PTR infbytes, unsigned int query, unsigned int subquery, target_t target); typedef unsigned long (*m_admin_t)(unsigned char *resp1, size_t *r1len, unsigned char *resp2, size_t *r2len, const unsigned char *cmd, size_t clen, const unsigned char *sigs, size_t slen, target_t target); typedef long (*xcpa_cmdblock_t)(unsigned char *blk, size_t blen, unsigned int fn, const struct XCPadmresp *minf, const unsigned char *tctr, const unsigned char *payload, size_t plen); typedef long (*xcpa_internal_rv_t)(const unsigned char *rsp, size_t rlen, struct XCPadmresp *rspblk, CK_RV *rv); struct ep11_lib { m_GenerateKeyPair_t dll_m_GenerateKeyPair; m_SignSingle_t dll_m_SignSingle; m_DecryptSingle_t dll_m_DecryptSingle; m_get_xcp_info_t dll_m_get_xcp_info; m_admin_t dll_m_admin; xcpa_cmdblock_t dll_xcpa_cmdblock; xcpa_internal_rv_t dll_xcpa_internal_rv; }; #define TOKTYPE_NON_CCA 0 #define PKEY_TYPE_EP11_ECC 6 #define PKEY_TYPE_EP11_RSA 7 #define PKEY_TYPE_EP11_HVER 0 #define PKEY_TYPE_EP11_FLAG_X9_31 0x01 /* * EP11 secure key blobs of type PKEY_TYPE_EP11_ECC and PKEY_TYPE_EP11_RSA * are EP11 blobs prepended by this header (aligned with definition in kernel * header arch/s390/include/uapi/asm/pkey.h): */ struct ep11kblob_header { uint8_t type; /* always 0x00 (TOKTYPE_NON_CCA) */ uint8_t hver; /* header version, currently needs to be 0x00 */ uint16_t len; /* total length in bytes (including this header) */ uint8_t version; /* PKEY_TYPE_EP11_ECC or PKEY_TYPE_EP11_RSA */ uint8_t flags; /* Flags, see PKEY_TYPE_EP11_FLAG */ uint16_t bitlen; /* clear key bit len, 0 for unknown */ uint8_t res0[8]; } __packed; /* Followed by len - sizeof(struct ep11kblob_header) bytes EP11 key blob */ /* Followed by secure key size - len bytes SPKI (public key) */ #define POINT_CONVERSION_ODD_EVEN 0x01 /** * Gets the Ep11 library function entry points from the library handle */ static int sk_ep11_get_library_functions(const struct sk_ext_ep11_lib *ep11_lib, struct ep11_lib *ep11) { if (ep11_lib == NULL || ep11 == NULL) return -EINVAL; ep11->dll_m_GenerateKeyPair = (m_GenerateKeyPair_t) dlsym(ep11_lib->ep11_lib, "m_GenerateKeyPair"); ep11->dll_m_SignSingle = (m_SignSingle_t) dlsym(ep11_lib->ep11_lib, "m_SignSingle"); ep11->dll_m_DecryptSingle = (m_DecryptSingle_t) dlsym(ep11_lib->ep11_lib, "m_DecryptSingle"); ep11->dll_m_get_xcp_info = (m_get_xcp_info_t) dlsym(ep11_lib->ep11_lib, "m_get_xcp_info"); ep11->dll_m_admin = (m_admin_t) dlsym(ep11_lib->ep11_lib, "m_admin"); ep11->dll_xcpa_cmdblock = (xcpa_cmdblock_t) dlsym(ep11_lib->ep11_lib, "xcpa_cmdblock"); ep11->dll_xcpa_internal_rv = (xcpa_internal_rv_t) dlsym(ep11_lib->ep11_lib, "xcpa_internal_rv"); if (ep11->dll_m_GenerateKeyPair == NULL || ep11->dll_m_SignSingle == NULL || ep11->dll_m_DecryptSingle == NULL || ep11->dll_m_get_xcp_info == NULL || ep11->dll_m_admin == NULL || ep11->dll_xcpa_cmdblock == NULL || ep11->dll_xcpa_internal_rv == NULL) return -EIO; return 0; } /** * Generates an EP11 asymmetric key using the specified key type, mechanism, and * templates. */ static int sk_ep11_generate_key_pair(const struct sk_ext_ep11_lib *ep11_lib, CK_MECHANISM *mech, CK_ATTRIBUTE *pub_tmpl, CK_ULONG pub_tmpl_num, CK_ATTRIBUTE *priv_tmpl, CK_ULONG priv_tmpl_num, unsigned char *key_token, size_t *key_token_length, bool debug) { unsigned char spki[EP11_MAX_KEY_TOKEN_SIZE] = { 0 }; struct ep11kblob_header *hdr; size_t blob_size, spki_size; struct ep11_lib ep11; unsigned char *blob; size_t tok_len; CK_RV rv; int rc; if (ep11_lib == NULL || key_token == NULL || key_token_length == NULL) return -EINVAL; if (key_token == NULL) { *key_token_length = EP11_MAX_KEY_TOKEN_SIZE; return 0; } if (*key_token_length <= sizeof(struct ep11kblob_header)) { sk_debug(debug, "ERROR: key token too short"); return -EINVAL; } hdr = (struct ep11kblob_header *)key_token; memset(hdr, 0, sizeof(struct ep11kblob_header)); hdr->type = TOKTYPE_NON_CCA; hdr->hver = PKEY_TYPE_EP11_HVER; blob_size = *key_token_length - sizeof(struct ep11kblob_header); spki_size = sizeof(spki); blob = key_token + sizeof(struct ep11kblob_header); rc = sk_ep11_get_library_functions(ep11_lib, &ep11); if (rc != 0) { sk_debug(debug, "ERROR: Failed to get EP11 functions from library"); return rc; } sk_debug(debug, "mech: 0x%x", mech->mechanism); rv = ep11.dll_m_GenerateKeyPair(mech, pub_tmpl, pub_tmpl_num, priv_tmpl, priv_tmpl_num, NULL, 0, blob, &blob_size, spki, &spki_size, ep11_lib->target); if (rv != CKR_OK) { sk_debug(debug, "ERROR: m_GenerateKeyPair failed: rc: 0x%x", rv); return -EIO; } sk_debug(debug, "blob_size: %lu spki_len: %lu", blob_size, spki_size); hdr->len = sizeof(struct ep11kblob_header) + blob_size; tok_len = sizeof(struct ep11kblob_header) + blob_size + spki_size; sk_debug(debug, "tok_len: %lu", tok_len); if (tok_len > *key_token_length) { sk_debug(debug, "ERROR: key token too short"); return -EINVAL; } memcpy(blob + blob_size, spki, spki_size); *key_token_length = tok_len; return 0; } /** * Generates an EP11 EC key of the specified curve type and length using the * Ep11 host library. * * @param ep11_lib the Ep11 library structure * @param curve_nid the nid specifying the curve. * @param key_token a buffer to store the generated key token * @param key_token_length On entry: the size of the buffer * On return: the size of the key token * @param debug if true, debug messages are printed * * @returns a negative errno in case of an error, 0 if success. */ int SK_EP11_generate_ec_key_pair(const struct sk_ext_ep11_lib *ep11_lib, int curve_nid, unsigned char *key_token, size_t *key_token_length, bool debug) { CK_MECHANISM mech = { .mechanism = CKM_EC_KEY_PAIR_GEN, .pParameter = NULL, .ulParameterLen = 0 }; struct ep11kblob_header *hdr; CK_BBOOL _false = false; CK_BBOOL _true = true; CK_ATTRIBUTE pub_tmpl[] = { { CKA_EC_PARAMS, NULL, 0 }, { CKA_VERIFY, &_true, sizeof(_true) }, }; CK_ULONG pub_tmpl_num = sizeof(pub_tmpl) / sizeof(CK_ATTRIBUTE); CK_ATTRIBUTE priv_tmpl[] = { { CKA_SENSITIVE, &_true, sizeof(_true) }, { CKA_SIGN, &_true, sizeof(_true) }, { CKA_DERIVE, &_false, sizeof(_false) }, }; CK_ULONG priv_tmpl_num = sizeof(priv_tmpl) / sizeof(CK_ATTRIBUTE); const struct sk_ec_curve_info *curve; int rc; if (ep11_lib == NULL || key_token == NULL || key_token_length == NULL) return -EINVAL; sk_debug(debug, "curve_nid: %d", curve_nid); curve = SK_UTIL_ec_get_curve_info(curve_nid); if (curve == NULL) { sk_debug(debug, "ERROR: Curve %d not supported", curve_nid); return -EIO; } pub_tmpl[0].pValue = (void *)curve->der; pub_tmpl[0].ulValueLen = curve->der_size; rc = sk_ep11_generate_key_pair(ep11_lib, &mech, pub_tmpl, pub_tmpl_num, priv_tmpl, priv_tmpl_num, key_token, key_token_length, debug); if (rc != 0) { sk_debug(debug, "ERROR: sk_ep11_generate_key_pair failed"); return -rc; } hdr = (struct ep11kblob_header *)key_token; hdr->version = PKEY_TYPE_EP11_ECC; hdr->bitlen = curve->prime_bits; return 0; } /** * Generates an EP11 RSA key of the specified key size and optionally the * specified public exponent using the EP11 host library. * * @param ep11_lib the EP11 library structure * @param modulus_bits the size of the key in bits (512, 1024, 2048, 4096) * @param pub_exp the public exponent or zero. Possible values are: * 3, 5, 17, 257, or 65537. Specify zero to choose the * exponent by random. * @param x9_31 if true, generate a X9.31 RSA key * @param key_token a buffer to store the generated key token * @param key_token_length On entry: the size of the buffer * On return: the size of the key token * @param debug if true, debug messages are printed * * @returns a negative errno in case of an error, 0 if success. */ int SK_EP11_generate_rsa_key_pair(const struct sk_ext_ep11_lib *ep11_lib, size_t modulus_bits, unsigned int pub_exp, bool x9_31, unsigned char *key_token, size_t *key_token_length, bool debug) { CK_MECHANISM mech = { .mechanism = CKM_RSA_PKCS_KEY_PAIR_GEN, .pParameter = NULL, .ulParameterLen = 0 }; CK_ULONG mod_bits = modulus_bits; struct ep11kblob_header *hdr; CK_BBOOL _true = true; CK_ATTRIBUTE pub_tmpl[] = { { CKA_MODULUS_BITS, &mod_bits, sizeof(mod_bits) }, { CKA_VERIFY, &_true, sizeof(_true) }, { CKA_ENCRYPT, &_true, sizeof(_true) }, { CKA_WRAP, &_true, sizeof(_true) }, { CKA_PUBLIC_EXPONENT, &pub_exp, sizeof(pub_exp) }, }; CK_ULONG pub_tmpl_num = sizeof(pub_tmpl) / sizeof(CK_ATTRIBUTE); CK_ATTRIBUTE priv_tmpl[] = { { CKA_SENSITIVE, &_true, sizeof(_true) }, { CKA_SIGN, &_true, sizeof(_true) }, { CKA_DECRYPT, &_true, sizeof(_true) }, { CKA_UNWRAP, &_true, sizeof(_true) }, }; CK_ULONG priv_tmpl_num = sizeof(priv_tmpl) / sizeof(CK_ATTRIBUTE); int rc; if (ep11_lib == NULL || key_token == NULL || key_token_length == NULL) return -EINVAL; sk_debug(debug, "modulus_bits: %lu pub_exp: %u x9_31: %d", modulus_bits, pub_exp, x9_31); if (pub_exp == 0) pub_tmpl_num--; if (x9_31) mech.mechanism = CKM_RSA_X9_31_KEY_PAIR_GEN; rc = sk_ep11_generate_key_pair(ep11_lib, &mech, pub_tmpl, pub_tmpl_num, priv_tmpl, priv_tmpl_num, key_token, key_token_length, debug); if (rc != 0) { sk_debug(debug, "ERROR: sk_ep11_generate_key_pair failed"); return -rc; } hdr = (struct ep11kblob_header *)key_token; hdr->version = PKEY_TYPE_EP11_RSA; if (x9_31) hdr->flags |= PKEY_TYPE_EP11_FLAG_X9_31; hdr->bitlen = modulus_bits; return 0; } /* * Parses a DER encoded tag, returns the tag id, and sets the tag length and * value length. */ static unsigned char sk_ep11_parse_der_tag(const unsigned char *data, size_t data_len, size_t *tag_len, const unsigned char **value, size_t *value_len) { size_t num, i; if (data == NULL || data_len < 2) return 0; if (data[1] & 0x80) { num = data[1] & 0x7f; if (num > sizeof(size_t)) return 0; *value_len = data[2]; for (i = 1; i < num; i++) { *value_len <<= 8; *value_len |= data[2 + i]; } *value = &data[2 + num]; *tag_len = 2 + num + *value_len; } else { *value_len = data[1] & 0x7f; *value = &data[2]; *tag_len = 2 + *value_len; } if (*tag_len > data_len) return 0; return data[0]; } /* * Extract data from an SPKI * SubjectPublicKeyInfo ::= SEQUENCE { * algorithm AlgorithmIdentifier, * subjectPublicKey BIT STRING * } * * AlgorithmIdentifier ::= SEQUENCE { * algorithm OBJECT IDENTIFIER, * parameters ANY DEFINED BY algorithm OPTIONAL * } */ static int sk_ep11_parse_spki(const unsigned char *spki, size_t spki_len, enum sk_key_type *keytype, const unsigned char **params, size_t *params_len, const unsigned char **pub_key, size_t *pub_key_len) { size_t tag_len, seq1_len, seq2_tag_len, seq2_len, oid_len; const unsigned char *seq1, *seq2, *oid; ASN1_OBJECT *obj = NULL; unsigned char tag; int algo_nid; /* Outer sequence */ tag = sk_ep11_parse_der_tag(spki, spki_len, &tag_len, &seq1, &seq1_len); if (tag != 0x30) /* SEQUENCE */ return -EINVAL; /* Inner sequence */ tag = sk_ep11_parse_der_tag(seq1, seq1_len, &seq2_tag_len, &seq2, &seq2_len); if (tag != 0x30) /* SEQUENCE */ return -EINVAL; /* Algorithm OID */ tag = sk_ep11_parse_der_tag(seq2, seq2_len, &tag_len, &oid, &oid_len); if (tag != 0x06) /* OID */ return -EINVAL; oid = seq2; if (d2i_ASN1_OBJECT(&obj, &oid, tag_len) == NULL) return -EIO; algo_nid = OBJ_obj2nid(obj); ASN1_OBJECT_free(obj); switch (algo_nid) { case NID_rsaEncryption: *keytype = SK_KEY_TYPE_RSA; break; case NID_X9_62_id_ecPublicKey: *keytype = SK_KEY_TYPE_EC; break; default: return -EINVAL; } /* Parameters */ *params = seq2 + tag_len; *params_len = seq2_len - tag_len; /* Public key */ tag = sk_ep11_parse_der_tag(seq1 + seq2_tag_len, seq1_len - seq2_tag_len, &tag_len, pub_key, pub_key_len); if (tag != 0x03) /* BITSTRING */ return -EINVAL; /* skip unsused-bits byte */ (*pub_key)++; (*pub_key_len)--; return 0; } static bool sk_ep11_valid_ep11_blob(const unsigned char *key_token, size_t key_token_length) { const struct ep11kblob_header *hdr = (struct ep11kblob_header *)key_token; if (key_token == NULL) return false; if (key_token_length <= sizeof(struct ep11kblob_header)) return false; if (hdr->len > key_token_length || hdr->type != TOKTYPE_NON_CCA || hdr->hver != PKEY_TYPE_EP11_HVER || (hdr->version != PKEY_TYPE_EP11_ECC && hdr->version != PKEY_TYPE_EP11_RSA)) return false; return true; } /** * Queries the PKEY type of the key token. * * @param key_token the key token containing an Ep11 EC key * @param key_token_length the size of the key token * @param pkey_type On return: the PKEY type of the key token * * @returns a negative errno in case of an error, 0 if success. */ int SK_EP11_get_key_type(const unsigned char *key_token, size_t key_token_length, int *pkey_type) { const struct ep11kblob_header *hdr = (struct ep11kblob_header *)key_token; size_t params_len, pub_key_len, spki_size; const unsigned char *params, *pub_key; enum sk_key_type type; int rc; if (key_token == NULL || pkey_type == NULL) return -EINVAL; if (!sk_ep11_valid_ep11_blob(key_token, key_token_length)) return -EINVAL; spki_size = key_token_length - hdr->len; if (spki_size <= 0) return -EINVAL; rc = sk_ep11_parse_spki(key_token + hdr->len, spki_size, &type, ¶ms, ¶ms_len, &pub_key, &pub_key_len); if (rc != 0) return rc; switch (type) { case SK_KEY_TYPE_RSA: *pkey_type = EVP_PKEY_RSA; break; case SK_KEY_TYPE_EC: *pkey_type = EVP_PKEY_EC; break; default: *pkey_type = -1; return -EINVAL; } return 0; } /** * Returns the EP11 private key blob of the key token. * * @param key_token the key token containing an Ep11 EC key * @param key_token_length the size of the key token * * @returns the address of the EP11 key blob, or NULL in case of an error */ const unsigned char *SK_EP11_get_key_blob(const unsigned char *key_token, size_t key_token_length) { if (!sk_ep11_valid_ep11_blob(key_token, key_token_length)) return NULL; return key_token + sizeof(struct ep11kblob_header); } /** * Returns the EP11 private key blob size of the key token. * * @param key_token the key token containing an Ep11 EC key * @param key_token_length the size of the key token * * @returns the size of the EP11 key blob, or 0 in case of an error */ size_t SK_EP11_get_key_blob_size(const unsigned char *key_token, size_t key_token_length) { const struct ep11kblob_header *hdr = (struct ep11kblob_header *)key_token; if (!sk_ep11_valid_ep11_blob(key_token, key_token_length)) return 0; return hdr->len - sizeof(struct ep11kblob_header); } /** * Sign data using RSA. * * @param key_token the RSA key token * @param key_token_length the length of the key token * @param sig a buffer to store the signature on return. * @param siglen on input: the size if the signature buffer * on return: the size of the signature * @param tbs the data to be signed. * @param tbslen the size of the data to be signed * @param padding_type the OpenSSL padding type (RSA_X931_PADDING or * RSA_PKCS1_PADDING) * @param digest_nid the OpenSSL nid of the message digest used to * produce the data to be signed * @param private the CCA library structure * @param debug if true, debug messages are printed * * @returns a negative errno in case of an error, 0 if success. */ static int sk_ep11_rsa_sign(const unsigned char *key_token, size_t key_token_length, unsigned char *sig, size_t *siglen, const unsigned char *tbs, size_t tbslen, int padding_type, int md_nid, void *private, bool debug) { const struct ep11kblob_header *hdr = (struct ep11kblob_header *)key_token; const unsigned char *blob = key_token + sizeof(struct ep11kblob_header); CK_MECHANISM mech = { .mechanism = CKM_RSA_PKCS, .pParameter = NULL, .ulParameterLen = 0 }; const struct sk_ext_ep11_lib *ep11_lib = private; const struct sk_digest_info *digest; unsigned char *msg = NULL; struct ep11_lib ep11; size_t msg_len; CK_RV rv; int rc; if (ep11_lib == NULL || key_token == NULL || sig == NULL || siglen == NULL || tbs == NULL) return -EINVAL; if (!sk_ep11_valid_ep11_blob(key_token, key_token_length)) return -EINVAL; sk_debug(debug, "tbslen: %lu siglen: %lu padding_type: %d md_nid: %d", tbslen, *siglen, padding_type, md_nid); rc = sk_ep11_get_library_functions(ep11_lib, &ep11); if (rc != 0) { sk_debug(debug, "ERROR: Failed to get EP11 functions from library"); return rc; } digest = SK_UTIL_get_digest_info(md_nid); if (digest == NULL) { sk_debug(debug, "ERROR: Invalid digest nid: %d", md_nid); return -EINVAL; } if (tbslen != digest->digest_size) { sk_debug(debug, "ERROR: Invalid data length: %lu", tbslen); return -EINVAL; } switch (padding_type) { case RSA_PKCS1_PADDING: msg_len = digest->der_size + tbslen; msg = (unsigned char *)malloc(msg_len); if (msg == NULL) { sk_debug(debug, "ERROR: malloc failed"); return -ENOMEM; } memcpy(msg, digest->der, digest->der_size); memcpy(msg + digest->der_size, tbs, tbslen); tbs = msg; tbslen = msg_len; break; case RSA_X931_PADDING: mech.mechanism = CKM_RSA_X9_31; if ((hdr->flags && PKEY_TYPE_EP11_FLAG_X9_31) == 0) { sk_debug(debug, "ERROR: no RSA X9.31 key"); return -EINVAL; } msg_len = tbslen + 2; msg = (unsigned char *)malloc(msg_len); if (msg == NULL) { sk_debug(debug, "ERROR: malloc failed"); return -ENOMEM; } memcpy(msg, tbs, tbslen); msg[tbslen] = digest->x9_31_md; msg[tbslen + 1] = 0xcc; tbs = msg; tbslen = msg_len; break; default: sk_debug(debug, "ERROR: Invalid padding type: %d", padding_type); return -EINVAL; } rv = ep11.dll_m_SignSingle(blob, hdr->len - sizeof(*hdr), &mech, (CK_BYTE_PTR)tbs, tbslen, sig, siglen, ep11_lib->target); if (rv != CKR_OK) { sk_debug(debug, "ERROR: m_SignSingle failed: rc: 0x%x", rv); rc = -EIO; goto out; } rc = 0; sk_debug(debug, "siglen: %lu", *siglen); out: if (msg != NULL) free(msg); return rc; } /** * Sign data using RSA-PSS. * * @param key_token the RSA key token * @param key_token_length the length of the key token * @param sig a buffer to store the signature on return. * @param siglen on input: the size if the signature buffer * on return: the size of the signature * @param tbs the data to be signed. * @param tbslen the size of the data to be signed * @param digest_nid the OpenSSL nid of the message digest used to * produce the data to be signed * @param mgf_digest_nid the OpenSSL nid of the mask generation function for * PSS padding * @param saltlen the length of the salt for PSS * @param private the CCA library structure * @param debug if true, debug messages are printed * * @returns a negative errno in case of an error, 0 if success. */ static int sk_ep11_rsa_pss_sign(const unsigned char *key_token, size_t key_token_length, unsigned char *sig, size_t *siglen, const unsigned char *tbs, size_t tbslen, int digest_nid, int mgf_digest_nid, int saltlen, void *private, bool debug) { const struct ep11kblob_header *hdr = (struct ep11kblob_header *)key_token; const unsigned char *blob = key_token + sizeof(struct ep11kblob_header); CK_RSA_PKCS_PSS_PARAMS pss_params; CK_MECHANISM mech = { .mechanism = CKM_RSA_PKCS_PSS, .pParameter = &pss_params, .ulParameterLen = sizeof(pss_params) }; const struct sk_ext_ep11_lib *ep11_lib = private; const struct sk_digest_info *digest; struct ep11_lib ep11; CK_RV rv; int rc; if (ep11_lib == NULL || key_token == NULL || sig == NULL || siglen == NULL || tbs == NULL) return -EINVAL; if (!sk_ep11_valid_ep11_blob(key_token, key_token_length)) return -EINVAL; sk_debug(debug, "tbslen: %lu siglen: %lu digest_nid: %d " "mgf_digest_nid: %d saltlen: %d", tbslen, *siglen, digest_nid, mgf_digest_nid, saltlen); rc = sk_ep11_get_library_functions(ep11_lib, &ep11); if (rc != 0) { sk_debug(debug, "ERROR: Failed to get EP11 functions from library"); return rc; } if (mgf_digest_nid != digest_nid) { sk_debug(debug, "ERROR: Mgf nid must be the same as the " "message digest nid"); return -EINVAL; } digest = SK_UTIL_get_digest_info(digest_nid); if (digest == NULL || digest->pkcs11_mech == 0 || digest->pkcs11_mgf == 0) { sk_debug(debug, "ERROR: Invalid digest nid: %d", digest_nid); return -EINVAL; } if (tbslen != digest->digest_size) { sk_debug(debug, "ERROR: Invalid data length: %lu", tbslen); return -EINVAL; } if (saltlen != (int)digest->digest_size) { sk_debug(debug, "ERROR: saltlen must be size of digest"); return -EINVAL; } pss_params.hashAlg = digest->pkcs11_mech; pss_params.mgf = digest->pkcs11_mgf; pss_params.sLen = saltlen; sk_debug(debug, "pss_params.hashAlg: 0x%x", pss_params.hashAlg); sk_debug(debug, "pss_params.mgf: 0x%x", pss_params.mgf); rv = ep11.dll_m_SignSingle(blob, hdr->len - sizeof(*hdr), &mech, (CK_BYTE_PTR)tbs, tbslen, sig, siglen, ep11_lib->target); if (rv != CKR_OK) { sk_debug(debug, "ERROR: m_SignSingle failed: rc: 0x%x", rv); return -EIO; } sk_debug(debug, "siglen: %lu", *siglen); return 0; } /** * Decrypt data using RSA. * * @param key_token the RSA key token * @param key_token_length the length of the key token * @param to a buffer to store the decrypted data on return. * @param tolen on input: the size if the to buffer * on return: the size of the decrypted data * @param from the data to be decrypted. * @param fromlen the size of the data to be decrypted * @param padding_type the OpenSSL padding type * @param private the CCA library structure * @param debug if true, debug messages are printed * * @returns a negative errno in case of an error, 0 if success. */ static int sk_ep11_rsa_decrypt(const unsigned char *key_token, size_t key_token_length, unsigned char *to, size_t *tolen, const unsigned char *from, size_t fromlen, int padding_type, void *private, bool debug) { const struct ep11kblob_header *hdr = (struct ep11kblob_header *)key_token; const unsigned char *blob = key_token + sizeof(struct ep11kblob_header); CK_MECHANISM mech = { .mechanism = CKM_RSA_PKCS, .pParameter = NULL, .ulParameterLen = 0 }; const struct sk_ext_ep11_lib *ep11_lib = private; struct ep11_lib ep11; CK_RV rv; int rc; if (ep11_lib == NULL || key_token == NULL || to == NULL || tolen == NULL || from == NULL) return -EINVAL; if (!sk_ep11_valid_ep11_blob(key_token, key_token_length)) return -EINVAL; sk_debug(debug, "fromlen: %lu tolen: %lu padding_type: %d", fromlen, *tolen, padding_type); rc = sk_ep11_get_library_functions(ep11_lib, &ep11); if (rc != 0) { sk_debug(debug, "ERROR: Failed to get EP11 functions from library"); return rc; } switch (padding_type) { case RSA_PKCS1_PADDING: break; default: sk_debug(debug, "ERROR: Invalid padding type: %d", padding_type); return -EINVAL; } rv = ep11.dll_m_DecryptSingle(blob, hdr->len - sizeof(*hdr), &mech, (CK_BYTE_PTR)from, fromlen, to, tolen, ep11_lib->target); if (rv != CKR_OK) { sk_debug(debug, "ERROR: m_DecryptSingle failed: rc: 0x%x", rv); return -EIO; } sk_debug(debug, "tolen: %lu", *tolen); return 0; } /** * Decrypt data using RSA OAEP. * * @param key_token the RSA key token * @param key_token_length the length of the key token * @param to a buffer to store the decrypted data on return. * @param tolen on input: the size if the to buffer * on return: the size of the decrypted data * @param from the data to be decrypted. * @param fromlen the size of the data to be decrypted * @param oaep_md_nid the OpenSSL nid of the OAEP hashing algorithm * @param mgfmd_nid the OpenSSL nid of the mask generation function * @param label the label for OAEP * @param label_len the length of the label for OAEP * @param private the CCA library structure * @param debug if true, debug messages are printed * * @returns a negative errno in case of an error, 0 if success. */ static int sk_ep11_rsa_decrypt_oaep(const unsigned char *key_token, size_t key_token_length, unsigned char *to, size_t *tolen, const unsigned char *from, size_t fromlen, int oaep_md_nid, int mgfmd_nid, unsigned char *label, int label_len, void *private, bool debug) { const struct ep11kblob_header *hdr = (struct ep11kblob_header *)key_token; const unsigned char *blob = key_token + sizeof(struct ep11kblob_header); CK_RSA_PKCS_OAEP_PARAMS oaep_params; CK_MECHANISM mech = { .mechanism = CKM_RSA_PKCS_OAEP, .pParameter = &oaep_params, .ulParameterLen = sizeof(oaep_params) }; const struct sk_ext_ep11_lib *ep11_lib = private; const struct sk_digest_info *digest; struct ep11_lib ep11; CK_RV rv; int rc; if (ep11_lib == NULL || key_token == NULL || to == NULL || tolen == NULL || from == NULL) return -EINVAL; if (!sk_ep11_valid_ep11_blob(key_token, key_token_length)) return -EINVAL; sk_debug(debug, "fromlen: %lu tolen: %lu oaep_md_nid: %d mgfmd_nid: %d", fromlen, *tolen, oaep_md_nid, mgfmd_nid); rc = sk_ep11_get_library_functions(ep11_lib, &ep11); if (rc != 0) { sk_debug(debug, "ERROR: Failed to get EP11 functions from library"); return rc; } if (mgfmd_nid != oaep_md_nid) { sk_debug(debug, "ERROR: Mgf nid must be the same as the " "OAEP digest nid"); return -EINVAL; } digest = SK_UTIL_get_digest_info(oaep_md_nid); if (digest == NULL || digest->pkcs11_mech == 0 || digest->pkcs11_mgf == 0) { sk_debug(debug, "ERROR: Invalid digest nid: %d", oaep_md_nid); return -EINVAL; } oaep_params.hashAlg = digest->pkcs11_mech; oaep_params.mgf = digest->pkcs11_mgf; oaep_params.source = label_len > 0 ? CKZ_DATA_SPECIFIED : 0; oaep_params.pSourceData = label_len > 0 ? label : NULL; oaep_params.ulSourceDataLen = label_len; sk_debug(debug, "oaep_params.hashAlg: 0x%x", oaep_params.hashAlg); sk_debug(debug, "oaep_params.mgf: 0x%x", oaep_params.mgf); sk_debug(debug, "oaep_params.source: 0x%x", oaep_params.source); sk_debug(debug, "oaep_params.ulSourceDataLen: %lu", oaep_params.ulSourceDataLen); rv = ep11.dll_m_DecryptSingle(blob, hdr->len - sizeof(*hdr), &mech, (CK_BYTE_PTR)from, fromlen, to, tolen, ep11_lib->target); if (rv != CKR_OK) { sk_debug(debug, "ERROR: m_DecryptSingle failed: rc: 0x%x", rv); return -EIO; } sk_debug(debug, "tolen: %lu", *tolen); return 0; } /** * Sign data using ECDSA. * * @param key_token the RSA key token * @param key_token_length the length of the key token * @param sig a buffer to store the signature on return. * @param siglen on input: the size if the signature buffer * on return: the size of the signature * @param tbs the data to be signed. * @param tbslen the size of the data to be signed * @param digest_nid the OpenSSL nid of the message digest used to * produce the data to be signed * @param private the CCA library structure * @param debug if true, debug messages are printed * * @returns a negative errno in case of an error, 0 if success. */ static int sk_ep11_ecdsa_sign(const unsigned char *key_token, size_t key_token_length, unsigned char *sig, size_t *siglen, const unsigned char *tbs, size_t tbslen, int digest_nid, void *private, bool debug) { const struct ep11kblob_header *hdr = (struct ep11kblob_header *)key_token; const unsigned char *blob = key_token + sizeof(struct ep11kblob_header); CK_MECHANISM mech = { .mechanism = CKM_ECDSA, .pParameter = NULL, .ulParameterLen = 0 }; const struct sk_ext_ep11_lib *ep11_lib = private; struct ep11_lib ep11; CK_ULONG sig_len; CK_RV rv; int rc; if (ep11_lib == NULL || key_token == NULL || sig == NULL || siglen == NULL || tbs == NULL) return -EINVAL; if (!sk_ep11_valid_ep11_blob(key_token, key_token_length)) return -EINVAL; sk_debug(debug, "tbslen: %lu siglen: %lu digest_nid: %d", tbslen, *siglen, digest_nid); rc = sk_ep11_get_library_functions(ep11_lib, &ep11); if (rc != 0) { sk_debug(debug, "ERROR: Failed to get EP11 functions from library"); return rc; } sig_len = *siglen; rv = ep11.dll_m_SignSingle(blob, hdr->len - sizeof(*hdr), &mech, (CK_BYTE_PTR)tbs, tbslen, sig, &sig_len, ep11_lib->target); if (rv != CKR_OK) { sk_debug(debug, "ERROR: m_SignSingle failed: rc: 0x%x", rv); return -EIO; } rc = SK_UTIL_build_ecdsa_signature(sig, sig_len, sig, siglen); if (rc != 0) { sk_debug(debug, "ERROR: build_ecdsa_signature failed"); return -EIO; } sk_debug(debug, "siglen: %lu", *siglen); return 0; } static const struct sk_funcs sk_ep11_funcs = { .rsa_sign = sk_ep11_rsa_sign, .rsa_pss_sign = sk_ep11_rsa_pss_sign, .rsa_decrypt = sk_ep11_rsa_decrypt, .rsa_decrypt_oaep = sk_ep11_rsa_decrypt_oaep, .ecdsa_sign = sk_ep11_ecdsa_sign, }; struct pub_key_cb_data { const struct sk_ext_ep11_lib *ep11_lib; const unsigned char *key_token; size_t key_token_length; bool rsa_pss; EVP_PKEY *pkey; bool debug; }; /* * Callback for generating an PKEY from a secure key */ static int sk_ep11_get_secure_key_as_pkey_cb( const struct sk_pub_key_info *pub_key, void *private) { struct pub_key_cb_data *data = private; int rc; if (pub_key == NULL || data == NULL) return -EINVAL; rc = SK_OPENSSL_get_pkey(data->key_token, data->key_token_length, pub_key, data->rsa_pss, &sk_ep11_funcs, data->ep11_lib, &data->pkey, data->debug); if (rc != 0) { sk_debug(data->debug, "ERROR: SK_OPENSSL_get_pkey failed"); return rc; } sk_debug(data->debug, "pkey: %p", data->pkey); return 0; } /** * Extracts the public key from a Ep11 RSA or EC key token, and returns * it as OpenSSL PKEY. * * @param ep11_lib the EP11 library structure * @param key_token the key token containing an EP11 secure key * @param key_token_length the size of the key token * @param rsa_pss For RSA public keys: create a RSA-PSS type PKEY * @param pkey On return: a PKEY containing the public key * @param debug if true, debug messages are printed * * @returns a negative errno in case of an error, 0 if success. */ int SK_EP11_get_secure_key_as_pkey(const struct sk_ext_ep11_lib *ep11_lib, const unsigned char *key_token, size_t key_token_length, bool rsa_pss, EVP_PKEY **pkey, bool debug) { struct pub_key_cb_data data; int rc; sk_debug(debug, "rsa_pss: %d", rsa_pss); data.ep11_lib = ep11_lib; data.key_token = key_token; data.key_token_length = key_token_length; data.rsa_pss = rsa_pss; data.pkey = NULL; data.debug = debug; rc = SK_EP11_get_public_from_secure_key(key_token, key_token_length, sk_ep11_get_secure_key_as_pkey_cb, &data, debug); if (rc != 0) { sk_debug(debug, "ERROR: SK_EP11_get_public_from_secure_key failed"); return rc; } sk_debug(debug, "pkey: %p", data.pkey); *pkey = data.pkey; return 0; } static int sk_ep11_get_public_from_ec_key(const unsigned char *pub_key, size_t pub_key_len, const unsigned char *params, size_t params_len, sk_pub_key_func_t pub_key_cb, void *private, bool debug) { struct sk_pub_key_info pub_key_info = { 0 }; const struct sk_ec_curve_info *curve; unsigned char *buf = NULL; ASN1_OBJECT *obj = NULL; int y_bit = 0; int rc = 0; /* * ECParameters ::= CHOICE { * namedCurve OBJECT IDENTIFIER * -- implicitCurve NULL * -- specifiedCurve SpecifiedECDomain * } * * EC PublicKey ECPoint */ if (d2i_ASN1_OBJECT(&obj, ¶ms, params_len) == NULL) { sk_debug(debug, "ERROR: d2i_ASN1_OBJECT failed"); return -EIO; } pub_key_info.ec.curve_nid = OBJ_obj2nid(obj); ASN1_OBJECT_free(obj); sk_debug(debug, "curve_nid: %d", pub_key_info.ec.curve_nid); curve = SK_UTIL_ec_get_curve_info(pub_key_info.ec.curve_nid); if (curve == NULL) { sk_debug(debug, "ERROR: unsupported curve"); return -EIO; } pub_key_info.ec.prime_len = curve->prime_len; sk_debug(debug, "prime_len: %lu", pub_key_info.ec.prime_len); if (pub_key_len != 2 * pub_key_info.ec.prime_len + 1) { sk_debug(debug, "ERROR: invalid public key length"); return -EINVAL; } pub_key_info.ec.x = pub_key + 1; /* First byte of public key contains indication of key compression */ switch (pub_key[0]) { case POINT_CONVERSION_COMPRESSED: case POINT_CONVERSION_COMPRESSED + POINT_CONVERSION_ODD_EVEN: /* Compressed form, only x is available */ y_bit = (pub_key[0] & POINT_CONVERSION_ODD_EVEN) ? 1 : 0; buf = malloc(pub_key_info.ec.prime_len); if (buf == NULL) { sk_debug(debug, "ERROR: malloc failed"); rc = -ENOMEM; goto out; } rc = SK_UTIL_ec_calculate_y_coordinate( pub_key_info.ec.curve_nid, pub_key_info.ec.prime_len, pub_key_info.ec.x, y_bit, buf); if (rc != 0) { sk_debug(debug, "ERROR: ec_calculate_y_coordinate " "failed"); goto out; } pub_key_info.ec.y = buf; break; case POINT_CONVERSION_UNCOMPRESSED: case POINT_CONVERSION_HYBRID: case POINT_CONVERSION_HYBRID + POINT_CONVERSION_ODD_EVEN: /* Uncompressed or hybrid, x and y are available */ pub_key_info.ec.y = pub_key_info.ec.x + pub_key_info.ec.prime_len; break; default: sk_debug(debug, "ERROR: invalid compression indication"); rc = -EIO; goto out; } pub_key_info.type = SK_KEY_TYPE_EC; rc = pub_key_cb(&pub_key_info, private); if (rc != 0) { sk_debug(debug, "ERROR: pub_key_cb failed"); goto out; } out: if (buf != NULL) free(buf); return rc; } /* * Extracts the public key from a EP11 RSA key blob, and calls * the specified callback function with the public key information. */ static int sk_ep11_get_public_from_rsa_key(const unsigned char *pub_key, size_t pub_key_len, sk_pub_key_func_t pub_key_cb, void *private, bool debug) { struct sk_pub_key_info pub_key_info = { 0 }; const unsigned char *seq; size_t tag_len, seq_len; unsigned char tag; int rc; /* * RSAPublicKey ::= SEQUENCE { * modulus INTEGER, -- n * publicExponent INTEGER -- e * } */ tag = sk_ep11_parse_der_tag(pub_key, pub_key_len, &tag_len, &seq, &seq_len); if (tag != 0x30) { /* SEQUENCE */ sk_debug(debug, "ERROR: failed to parse SEQUENCE"); return -EINVAL; } tag = sk_ep11_parse_der_tag(seq, seq_len, &tag_len, &pub_key_info.rsa.modulus, &pub_key_info.rsa.modulus_len); if (tag != 0x02) { /* INTEGER */ sk_debug(debug, "ERROR: failed to parse INTEGER (modulus)"); return -EINVAL; } tag = sk_ep11_parse_der_tag(seq + tag_len, seq_len - tag_len, &tag_len, &pub_key_info.rsa.pub_exp, &pub_key_info.rsa.pub_exp_len); if (tag != 0x02) { /* INTEGER */ sk_debug(debug, "ERROR: failed to parse INTEGER (pub-exp)"); return -EINVAL; } pub_key_info.type = SK_KEY_TYPE_RSA; rc = pub_key_cb(&pub_key_info, private); if (rc != 0) { sk_debug(debug, "ERROR: pub_key_cb failed"); return rc; } return 0; } /** * Extracts the public key from a EP11 RSA or EC key blob, and calls * the specified callback function with the public key information. * * @param key_token the key token containing an EP11 secure key * @param key_token_length the size of the key token * @param pub_key_cb the callback function to call with the public key * @param private a private pointer passed as is to the callback * @param debug if true, debug messages are printed * * @returns a negative errno in case of an error, 0 if success. */ int SK_EP11_get_public_from_secure_key(const unsigned char *key_token, size_t key_token_length, sk_pub_key_func_t pub_key_cb, void *private, bool debug) { const struct ep11kblob_header *hdr = (struct ep11kblob_header *)key_token; size_t params_len, pub_key_len, spki_size; const unsigned char *params, *pub_key; enum sk_key_type type; int rc; if (key_token == NULL || pub_key_cb == NULL) return -EINVAL; if (!sk_ep11_valid_ep11_blob(key_token, key_token_length)) return -EINVAL; spki_size = key_token_length - hdr->len; if (spki_size <= 0) return -EINVAL; rc = sk_ep11_parse_spki(key_token + hdr->len, spki_size, &type, ¶ms, ¶ms_len, &pub_key, &pub_key_len); if (rc != 0) { sk_debug(debug, "ERROR: sk_ep11_parse_spki failed"); return rc; } sk_debug(debug, "type: %d", type); switch (type) { case SK_KEY_TYPE_EC: rc = sk_ep11_get_public_from_ec_key(pub_key, pub_key_len, params, params_len, pub_key_cb, private, debug); if (rc != 0) { sk_debug(debug, "ERROR: sk_ep11_get_public_from_ec_key failed"); return rc; } break; case SK_KEY_TYPE_RSA: rc = sk_ep11_get_public_from_rsa_key(pub_key, pub_key_len, pub_key_cb, private, debug); if (rc != 0) { sk_debug(debug, "ERROR: sk_ep11_get_public_from_rsa_key failed"); return rc; } break; default: sk_debug(debug, "ERROR: Invalid key type: %d", type); return -EIO; } return 0; } /* * Checks that the specified target is a single APQN target, and extracts the * card and domain from it. */ static int sk_ep11_target_get_apqn(target_t target, unsigned short *card, unsigned short *domain) { if ((target & 0x8000000000000000L) != 0) return -ENODEV; *card = (target & 0x0000FFFF00000000) >> 32; *domain = target & 0x00000000000FFFF; return 0; } /** * Reenciphers an EP11 secure key with a new EP11 master key. * The target passed in via ep11_lib must be a single APQN target, and the * domain and card numbers must be specified. * * @param ep11_lib the EP11 library structure * @param key_token the key token containing an EP11 secure key * @param key_token_length the size of the key token * @param debug if true, debug messages are printed * * @returns a negative errno in case of an error, 0 if success. */ int SK_EP11_reencipher_key(const struct sk_ext_ep11_lib *ep11_lib, unsigned char *key_token, size_t key_token_length, bool debug) { const struct ep11kblob_header *hdr = (struct ep11kblob_header *)key_token; unsigned char *blob = key_token + sizeof(struct ep11kblob_header); CK_BYTE resp[EP11_MAX_KEY_TOKEN_SIZE]; CK_BYTE req[EP11_MAX_KEY_TOKEN_SIZE]; unsigned short card, domain; CK_IBM_DOMAIN_INFO dinf; struct XCPadmresp lrb; struct XCPadmresp rb; struct ep11_lib ep11; CK_ULONG dinf_len; size_t resp_len; long req_len; CK_RV rv; int rc; if (ep11_lib == NULL || key_token == NULL) return -EINVAL; if (!sk_ep11_valid_ep11_blob(key_token, key_token_length)) return -EINVAL; rc = sk_ep11_target_get_apqn(ep11_lib->target, &card, &domain); if (rc != 0) { sk_debug(debug, "ERROR: Need a single-APQN target for reencipher"); return rc; } rc = sk_ep11_get_library_functions(ep11_lib, &ep11); if (rc != 0) { sk_debug(debug, "ERROR: Failed to get EP11 functions from library"); return rc; } dinf_len = sizeof(dinf); rv = ep11.dll_m_get_xcp_info(&dinf, &dinf_len, CK_IBM_XCPQ_DOMAIN, 0, ep11_lib->target); if (rv != CKR_OK) { sk_debug(debug, "Failed to query domain information for " "APQN %02X.%04X: m_get_xcp_info rc: 0x%lx", card, domain, rv); return -EIO; } if ((dinf.flags & CK_IBM_DOM_COMMITTED_NWK) == 0) { sk_debug(debug, "The NEW master key register of APQN %02X.%04X " "is not in COMMITTED state", card, domain); return -ENODEV; } memset(&rb, 0, sizeof(rb)); memset(&lrb, 0, sizeof(lrb)); rb.domain = domain; lrb.domain = domain; resp_len = sizeof(resp); req_len = ep11.dll_xcpa_cmdblock(req, sizeof(req), XCP_ADM_REENCRYPT, &rb, NULL, blob, hdr->len - sizeof(*hdr)); if (req_len < 0) { sk_debug(debug, "Failed to build XCP command block"); return -EIO; } rv = ep11.dll_m_admin(resp, &resp_len, NULL, NULL, req, req_len, NULL, 0, ep11_lib->target); if (rv != CKR_OK || resp_len == 0) { sk_debug(debug, "Command XCP_ADM_REENCRYPT failed. " "rc = 0x%lx, resp_len = %ld", rv, resp_len); return -EIO; } rc = ep11.dll_xcpa_internal_rv(resp, resp_len, &lrb, &rv); if (rc != 0) { sk_debug(debug, "Failed to parse response. rc = %d", rc); return -EIO; } if (rv != CKR_OK) { sk_debug(debug, "Failed to re-encrypt the EP11 secure key. " "rc = 0x%lx", rv); switch (rv) { case CKR_IBM_WKID_MISMATCH: sk_debug(debug, "The EP11 secure key is currently " "encrypted under a different master that does " "not match the master key in the CURRENT " "master key register of APQN %02X.%04X", card, domain); break; } return -EIO; } if (hdr->len - sizeof(*hdr) != lrb.pllen) { sk_debug(debug, "Re-encrypted EP11 secure key size has " "changed: org-len: %lu, new-len: %lu", hdr->len - sizeof(*hdr), lrb.pllen); return -EIO; } memcpy(blob, lrb.payload, lrb.pllen); /* re-encipher MACed SPKI */ rb.domain = domain; lrb.domain = domain; resp_len = sizeof(resp); req_len = ep11.dll_xcpa_cmdblock(req, sizeof(req), XCP_ADM_REENCRYPT, &rb, NULL, key_token + hdr->len, key_token_length - hdr->len); if (req_len < 0) { sk_debug(debug, "Failed to build XCP command block"); return -EIO; } rv = ep11.dll_m_admin(resp, &resp_len, NULL, NULL, req, req_len, NULL, 0, ep11_lib->target); if (rv != CKR_OK || resp_len == 0) { sk_debug(debug, "Command XCP_ADM_REENCRYPT failed. " "rc = 0x%lx, resp_len = %ld", rv, resp_len); return -EIO; } rc = ep11.dll_xcpa_internal_rv(resp, resp_len, &lrb, &rv); if (rc != 0) { sk_debug(debug, "Failed to parse response. rc = %d", rc); return -EIO; } if (rv != CKR_OK) { sk_debug(debug, "Failed to re-encrypt the EP11 secure key. " "rc = 0x%lx", rv); switch (rv) { case CKR_IBM_WKID_MISMATCH: sk_debug(debug, "The EP11 secure key is currently " "encrypted under a different master that does " "not match the master key in the CURRENT " "master key register of APQN %02X.%04X", card, domain); break; } return -EIO; } if (key_token_length - hdr->len != lrb.pllen) { sk_debug(debug, "Re-encrypted EP11 secure key size has " "changed: org-len: %lu, new-len: %lu", hdr->len - sizeof(*hdr), lrb.pllen); return -EIO; } memcpy(key_token + hdr->len, lrb.payload, lrb.pllen); return 0; } s390-tools-2.38.0/libseckey/sk_openssl.c000066400000000000000000000652621502674226300177600ustar00rootroot00000000000000/* * libseckey - Secure key library * * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/zt_common.h" #include "libseckey/sk_openssl.h" #include "libseckey/sk_utilities.h" #include "libseckey/sk_cca.h" #include "libseckey/sk_ep11.h" #define SERIAL_NUMBER_BIT_SIZE 159 int sk_openssl_get_pkey_ec(const unsigned char *secure_key, size_t secure_key_size, int nid, size_t prime_len, const unsigned char *x, const unsigned char *y, const struct sk_funcs *sk_funcs, const void *private, EVP_PKEY **pkey, bool debug); int sk_openssl_get_pkey_rsa(const unsigned char *secure_key, size_t secure_key_size, const unsigned char *modulus, size_t modulus_length, const unsigned char *pub_exp, size_t pub_exp_length, int pkey_type, const struct sk_funcs *sk_funcs, const void *private, EVP_PKEY **pkey, bool debug); /** * Generate a secure key using the specified secure key crypto library. * * @param secure_key A buffer where the secure key is stored to. If NULL, * the required buffer size is returned in * secure_key_size (size query). * @param secure_key_size On entry, the size of the buffer specified with * secure_key (ignored if secure_key is NULL), * on exit the size of the secure key. * @param info key generation info, such as key type (EC or RSA) * and key parameters. * @param ext_lib External secure key crypto library to use * @param debug if true, debug messages are printed * * @returns a negative errno in case of an error, 0 if success. */ int SK_OPENSSL_generate_secure_key(unsigned char *secure_key, size_t *secure_key_size, const struct sk_key_gen_info *info, const struct sk_ext_lib *ext_lib, bool debug) { int rc; if (info == NULL || ext_lib == NULL || secure_key_size == NULL) return -EINVAL; sk_debug(debug, "ext-lib type: %d key type: %d", ext_lib->type, info->type); switch (ext_lib->type) { case SK_EXT_LIB_CCA: switch (info->type) { case SK_KEY_TYPE_EC: rc = SK_CCA_generate_ec_key_pair(ext_lib->cca, info->ec.curve_nid, secure_key, secure_key_size, debug); break; case SK_KEY_TYPE_RSA: rc = SK_CCA_generate_rsa_key_pair(ext_lib->cca, info->rsa.modulus_bits, info->rsa.pub_exp, secure_key, secure_key_size, debug); break; default: sk_debug(debug, "ERROR: Invalid key type: %d", info->type); return -EINVAL; } break; case SK_EXT_LIB_EP11: switch (info->type) { case SK_KEY_TYPE_EC: rc = SK_EP11_generate_ec_key_pair(ext_lib->ep11, info->ec.curve_nid, secure_key, secure_key_size, debug); break; case SK_KEY_TYPE_RSA: rc = SK_EP11_generate_rsa_key_pair(ext_lib->ep11, info->rsa.modulus_bits, info->rsa.pub_exp, info->rsa.x9_31, secure_key, secure_key_size, debug); break; default: sk_debug(debug, "ERROR: Invalid key type: %d", info->type); return -EINVAL; } break; default: sk_debug(debug, "ERROR: Invalid ext-lib type: %d", ext_lib->type); return -EINVAL; } if (rc != 0) { sk_debug(debug, "ERROR: Failed to generate a key: rc: %d - %s", rc, strerror(-rc)); return rc; } return 0; } /** * Reenciphers a secure key with a new master key using the specified secure * key crypto library. * * @param secure_key the key token containing an secure key * @param secure_key_size the size of the key token * @param to_new if true, reencipher with the MK in then NEW register * @param ext_lib External secure key crypto library to use * @param debug if true, debug messages are printed * * @returns a negative errno in case of an error, 0 if success. */ int SK_OPENSSL_reencipher_secure_key(unsigned char *secure_key, size_t secure_key_size, bool to_new, const struct sk_ext_lib *ext_lib, bool debug) { int rc; if (ext_lib == NULL || secure_key == NULL) return -EINVAL; sk_debug(debug, "ext-lib type: %d to_new: %d", ext_lib->type, to_new); switch (ext_lib->type) { case SK_EXT_LIB_CCA: rc = SK_CCA_reencipher_key(ext_lib->cca, secure_key, secure_key_size, to_new, debug); break; case SK_EXT_LIB_EP11: rc = SK_EP11_reencipher_key(ext_lib->ep11, secure_key, secure_key_size, debug); break; default: sk_debug(debug, "ERROR: Invalid ext lib type: %d", ext_lib->type); return -EINVAL; } if (rc != 0) { sk_debug(debug, "ERROR: Failed to reencipher a key: rc: %d - %s", rc, strerror(-rc)); return rc; } return 0; } /** * Extracts the public key from a secure key, and returns it as OpenSSL PKEY. * * @param secure_key the key token containing an secure key * @param secure_key_size the size of the key token * @param rsa_pss if the secure key is a RSA key, return a PKEY of * type EVP_PKEY_RSA_PSS * @param pkey On return: a PKEY containing the public key * @param ext_lib External secure key crypto library to use * @param debug if true, debug messages are printed * * @returns a negative errno in case of an error, 0 if success. */ int SK_OPENSSL_get_secure_key_as_pkey(const unsigned char *secure_key, size_t secure_key_size, bool rsa_pss, EVP_PKEY **pkey, const struct sk_ext_lib *ext_lib, bool debug) { int rc; if (ext_lib == NULL || secure_key == NULL || pkey == NULL) return -EINVAL; sk_debug(debug, "ext-lib type: %d rsa_pss: %d", ext_lib->type, rsa_pss); switch (ext_lib->type) { case SK_EXT_LIB_CCA: rc = SK_CCA_get_secure_key_as_pkey(ext_lib->cca, secure_key, secure_key_size, rsa_pss, pkey, debug); break; case SK_EXT_LIB_EP11: rc = SK_EP11_get_secure_key_as_pkey(ext_lib->ep11, secure_key, secure_key_size, rsa_pss, pkey, debug); break; default: sk_debug(debug, "ERROR: Invalid ext lib type: %d", ext_lib->type); return -EINVAL; } if (rc != 0) { sk_debug(debug, "ERROR: Failed to get PKEY: rc: %d - %s", rc, strerror(-rc)); return rc; } sk_debug(debug, "pkey: %p", *pkey); return 0; } /** * Extracts the public key from a secure key, and calls the specified callback * function with the public key information. * * @param secure_key the key token containing an secure key * @param secure_key_size the size of the key token * @param pub_key_cb the callback function to call with the public key * @param private a private pointer passed as is to the callback * @param ext_lib External secure key crypto library to use * @param debug if true, debug messages are printed * * @returns a negative errno in case of an error, 0 if success. */ int SK_OPENSSL_get_public_from_secure_key(const unsigned char *secure_key, size_t secure_key_size, sk_pub_key_func_t pub_key_cb, void *private, const struct sk_ext_lib *ext_lib, bool debug) { int rc; if (ext_lib == NULL || secure_key == NULL || pub_key_cb == NULL) return -EINVAL; sk_debug(debug, "ext-lib type: %d", ext_lib->type); switch (ext_lib->type) { case SK_EXT_LIB_CCA: rc = SK_CCA_get_public_from_secure_key(secure_key, secure_key_size, pub_key_cb, private, debug); break; case SK_EXT_LIB_EP11: rc = SK_EP11_get_public_from_secure_key(secure_key, secure_key_size, pub_key_cb, private, debug); break; default: sk_debug(debug, "ERROR: Invalid ext lib type: %d", ext_lib->type); return -EINVAL; } if (rc != 0) { sk_debug(debug, "ERROR: Failed to get PKEY: rc: %d - %s", rc, strerror(-rc)); return rc; } return 0; } /** * Converts a key given by the public key infos into an OpenSSL PKEY and * attaches the secure key together with secure key functions and private * pointer to it. If no secure key is provided, a public key only PKEY is * returned. * * @param secure_key the secure key blob. * If NULL, a clear key PKEY is created. * @param secure_key_size the size of the secure key blob (ignored if * secure_key is NULL) * @param pub_key the public key info * @param rsa_pss For RSA public keys: create a RSA-PSS type PKEY * @param sk_funcs the secure key functions to operate with the key. * Ignored if secure_key is NULL, required otherwise. * @param private a private pointer that is passed to the secure key * functions (can be NULL) * @param pkey On return: A PKEY containing the EC public key. * @param debug true to enable internal debugging * * @returns zero for success, a negative errno in case of an error: * -EINVAL: a function parameter is invalid * -ENOMEM: failed to allocate memory * -EIO: OpenSSL failed to generate the PKEY * -ENOENT: OpenSSL does not know/support the curve (nid) */ int SK_OPENSSL_get_pkey(const unsigned char *secure_key, size_t secure_key_size, const struct sk_pub_key_info *pub_key, bool rsa_pss, const struct sk_funcs *sk_funcs, const void *private, EVP_PKEY **pkey, bool debug) { int rc; if (pub_key == NULL) return -EINVAL; sk_debug(debug, "key type: %d", pub_key->type); switch (pub_key->type) { case SK_KEY_TYPE_EC: rc = sk_openssl_get_pkey_ec(secure_key, secure_key_size, pub_key->ec.curve_nid, pub_key->ec.prime_len, pub_key->ec.x, pub_key->ec.y, sk_funcs, private, pkey, debug); if (rc != 0) { sk_debug(debug, "ERROR: SK_OPENSSL_get_pkey_ec failed"); return rc; } break; case SK_KEY_TYPE_RSA: rc = sk_openssl_get_pkey_rsa(secure_key, secure_key_size, pub_key->rsa.modulus, pub_key->rsa.modulus_len, pub_key->rsa.pub_exp, pub_key->rsa.pub_exp_len, rsa_pss ? EVP_PKEY_RSA_PSS : EVP_PKEY_RSA, sk_funcs, private, pkey, debug); if (rc != 0) { sk_debug(debug, "ERROR: SK_OPENSSL_get_pkey_rsa failed"); return rc; } break; default: sk_debug(debug, "ERROR: Invalid key type: %d", pub_key->type); return -EINVAL; } sk_debug(debug, "pkey: %p", pkey); return 0; } /** * Sets up a digest sign context with the specified PKEY. * * @param pkey the PKEY to use * @param verify if true a verify context is created, otherwise a * sign context * @param digest_nid the NID of the digest algorithm to use. If * NID_undef, then the PKEY's default digest is used. * @param rsa_pss_params RSA-PSS parameters (if PKEY is a RSA-PSS key) * @param md_ctx On return: the MD context that was set up * @param pkey_ctx On return: the PKEY context that was set up. * Note: The PKEY context value returned must not be * freed by the application, it will be freed * automatically when the MD context is freed. * @param debug if true, debug messages are printed * * @returns a negative errno in case of an error, 0 if success. */ int SK_OPENSSL_setup_sign_context(EVP_PKEY *pkey, bool verify, int digest_nid, struct sk_rsa_pss_params *rsa_pss_params, EVP_MD_CTX **md_ctx, EVP_PKEY_CTX **pkey_ctx, bool debug) { EVP_PKEY_CTX *pctx = NULL; const EVP_MD *md = NULL; int rc, default_nid; EVP_MD_CTX *ctx; sk_debug(debug, "pkey: %p verify: %d digest_nid: %d", pkey, verify, digest_nid); ctx = EVP_MD_CTX_new(); if (ctx == NULL) { sk_debug(debug, "ERROR: Failed to allocate the digest context"); rc = -ENOMEM; goto out; } if (digest_nid != NID_undef) { md = EVP_get_digestbynid(digest_nid); if (md == NULL) { sk_debug(debug, "ERROR: Requested digest not supported"); rc = -ENOTSUP; goto out; } if (EVP_PKEY_get_default_digest_nid(pkey, &default_nid) == 2 && default_nid == 0) { sk_debug(debug, "ERROR: The signing algorithm requires " "there to be no digest"); md = NULL; } } if (verify) rc = EVP_DigestVerifyInit(ctx, &pctx, md, NULL, pkey); else rc = EVP_DigestSignInit(ctx, &pctx, md, NULL, pkey); if (rc != 1) { sk_debug(debug, "ERROR: Failed to initialize the signing " "operation"); rc = -EIO; goto out; } if (EVP_PKEY_id(pkey) == EVP_PKEY_RSA_PSS && rsa_pss_params != NULL) { sk_debug(debug, "RSA-PSS: saltlen: %d mgf_digest_nid: %d", rsa_pss_params->salt_len, rsa_pss_params->mgf_digest_nid); rc = EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING); if (rc != 1) { sk_debug(debug, "ERROR: Failed to set the PSS padding mode"); rc = -EIO; goto out; } rc = EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, rsa_pss_params->salt_len); if (rc != 1) { sk_debug(debug, "ERROR: Failed to set the PSS salt length"); rc = -EIO; goto out; } if (rsa_pss_params->mgf_digest_nid != 0) { md = EVP_get_digestbynid( rsa_pss_params->mgf_digest_nid); if (md == NULL) { sk_debug(debug, "ERROR: Requested MGF digest not " "supported"); rc = -ENOENT; goto out; } rc = EVP_PKEY_CTX_set_rsa_mgf1_md(pctx, md); if (rc != 1) { sk_debug(debug, "ERROR: Failed to set the MGF md"); rc = -EIO; goto out; } } } rc = 0; *md_ctx = ctx; *pkey_ctx = pctx; out: if (rc != 0 && ctx != NULL) EVP_MD_CTX_free(ctx); return rc; } /** * Generate a certificate signing request using the secure key with the * specified subject name, certificate extensions (if any), and writes the * CSR to the specified file in PEM format. * * To renew an existing certificate, specify the existing certificate file with * renew_cert_filename, and the subject name is extracted from it. Any specified * subject name RDNs are added to the CSR. Also, the extensions are taken from * the existing certificate, and any specified extensions are added to the CSR. * * The CSR is signed using the secure key with an signing algorithm matching * the secure key type (ECDSA, RSA-PKCS, or RSA-PSS if rsa_pss_params is not * NULL), and the specified digest. If the digest nid is NID_undef, then a * default digest is used. * * @param secure_key the key token containing an secure key * @param secure_key_size the size of the key token * @param subject_rdns an array of strings, each string representing an * RDN in the form '[+]type=value'. If the type is * prepended with a '+', then this RDN is added to the * previous one. * @param num_subject_rdns number of RDN elements in the array. * @param subject_utf8 if true, RDNs of type MBSTRING_UTF8 are created, * otherwise type is MBSTRING_ASC is used. * @param renew_cert if not NULL, specifies the an existing certificate * that is renewed. * @param extensions an array of strings, each string representing an * certificate extension in the form 'type=value'. * @param num_extensions number of extension elements in the array. * @param digest_nid the OpenSSL digest nid to use with the signature * algorithm, or NID_undef to use the default * @param rsa_pss_params if not NULL and the secure key is an RSA key, then * the CSR is signed with RSA-PSS using the specified * PSS parameters. Ignored if the secure key is an EC * key * @param csr On return: the generated CSR. Must be freed by the * caller using X509_REQ_free. * @param ext_lib External secure key crypto library to use * @param debug if true, debug messages are printed * * @returns a negative errno in case of an error, 0 if success: * -EINVAL: invalid parameter * -ENOMEM: Failed to allocate memory * -EBADMSG: an RDN or extension is not formatted correctly * -EIO: OpenSSL failed to create the CSR * -EEXIST: if one of the RDN name entries or extensions to add is a * duplicate * -ENOTSUP: the specified digest is not supported * any other errno from file I/O routines */ int SK_OPENSSL_generate_csr(const unsigned char *secure_key, size_t secure_key_size, const char *subject_rdns[], size_t num_subject_rdns, bool subject_utf8, const X509 *renew_cert, const char *extensions[], size_t num_extensions, int digest_nid, struct sk_rsa_pss_params *rsa_pss_params, X509_REQ **csr, const struct sk_ext_lib *ext_lib, bool debug) { const STACK_OF(X509_EXTENSION) *cert_exts = NULL; X509_NAME *subject_name = NULL; EVP_PKEY_CTX *pkey_ctx = NULL; EVP_MD_CTX *md_ctx = NULL; EVP_PKEY *pkey = NULL; X509_REQ *req = NULL; int rc; if (secure_key == NULL || ext_lib == NULL || csr == NULL) return -EINVAL; if (renew_cert == NULL && (subject_rdns == NULL || num_subject_rdns == 0)) return -EINVAL; if (num_extensions != 0 && extensions == NULL) return -EINVAL; rc = SK_OPENSSL_get_secure_key_as_pkey(secure_key, secure_key_size, rsa_pss_params != NULL, &pkey, ext_lib, debug); if (rc != 0) { sk_debug(debug, "ERROR: SK_OPENSSL_get_secure_key_as_pkey failed"); goto out; } req = X509_REQ_new(); if (req == NULL) { sk_debug(debug, "ERROR: X509_REQ_new failed"); rc = -ENOMEM; goto out; } rc = X509_REQ_set_version(req, 0L); if (rc != 1) { sk_debug(debug, "ERROR: X509_REQ_set_version failed: rc: %d", rc); rc = -EIO; goto out; } if (renew_cert != NULL) { subject_name = X509_NAME_dup(X509_get_subject_name(renew_cert)); cert_exts = X509_get0_extensions(renew_cert); } if (subject_rdns != NULL && num_subject_rdns > 0) { rc = SK_UTIL_build_subject_name(&subject_name, subject_rdns, num_subject_rdns, subject_utf8); if (rc != 0) { sk_debug(debug, "ERROR: Failed to parse the subject " "name RDNs: %s", strerror(-rc)); goto out; } } if (subject_name == NULL) { rc = -EINVAL; sk_debug(debug, "ERROR: Subject name can not be empty"); goto out; } rc = X509_REQ_set_subject_name(req, subject_name); if (rc != 1) { rc = -EIO; sk_debug(debug, "ERROR: Failed to set subject name into request"); goto out; } rc = SK_UTIL_build_certificate_extensions(NULL, req, extensions, num_extensions, cert_exts); if (rc != 0) { sk_debug(debug, "ERROR: Failed to parse the extensions: " "%s", strerror(-rc)); goto out; } rc = X509_REQ_set_pubkey(req, pkey); if (rc != 1) { sk_debug(debug, "ERROR: Failed to set the public key"); rc = -EIO; goto out; } rc = SK_OPENSSL_setup_sign_context(pkey, false, digest_nid, rsa_pss_params, &md_ctx, &pkey_ctx, debug); if (rc != 0) { sk_debug(debug, "ERROR: SK_OPENSSL_setup_sign_context failed"); goto out; } rc = X509_REQ_sign_ctx(req, md_ctx); if (rc <= 0) { sk_debug(debug, "ERROR: Failed to perform the signing operation"); rc = -EIO; goto out; } if (debug) { sk_debug(debug, "Certificate Signing Request created:"); X509_REQ_print_fp(stderr, req); } *csr = req; req = NULL; rc = 0; out: if (md_ctx != NULL) EVP_MD_CTX_free(md_ctx); if (subject_name != NULL) X509_NAME_free(subject_name); if (req != NULL) X509_REQ_free(req); if (pkey != NULL) EVP_PKEY_free(pkey); return rc; } /** * Generate a self signed certificate using the secure key with the * specified subject name, certificate extensions (if any), and writes the * certificate to the specified file in PEM format. * * To renew an existing certificate, specify the existing certificate file with * renew_cert_filename, and the subject name is extracted from it. Any specified * subject name RDNs are added to the certificate. Also, the extensions are * taken from the existing certificate, and any specified extensions are added * to the certificate. * * The certificate is signed using the secure key with an signing algorithm * matching the secure key type (ECDSA, RSA-PKCS, or RSA-PSS if rsa_pss_params * is not NULL), and the specified digest. If the digest nid is NID_undef, then * a default digest is used. * * @param secure_key the key token containing an secure key * @param secure_key_size the size of the key token * @param subject_rdns an array of strings, each string representing an * RDN in the form '[+]type=value'. If the type is * prepended with a '+', then this RDN is added to the * previous one. * @param num_subject_rdns number of RDN elements in the array. * @param subject_utf8 if true, RDNs of type MBSTRING_UTF8 are created, * otherwise type is MBSTRING_ASC is used. * @param renew_cert if not NULL, specifies an existing certificate that * is renewed * @param extensions an array of strings, each string representing an * certificate extension in the form 'type=value'. * @param num_extensions number of extension elements in the array. * @param validity_days number if day from the current date how long the * certificate is valid. * @param digest_nid the OpenSSL digest nid to use with the signature * algorithm, or NID_undef to use the default * @param rsa_pss_params if not NULL and the secure key is an RSA key, then * the certificate is signed with RSA-PSS using the * specified PSS parameters. Ignored if the secure * key is an EC key * @param ss_cert On return: The generated self signed certificate. * Must be freed by the caller using X509_free. * @param ext_lib External secure key crypto library to use * @param debug if true, debug messages are printed * * @returns a negative errno in case of an error, 0 if success. * -EINVAL: invalid parameter * -ENOMEM: Failed to allocate memory * -EBADMSG: an RDN or extension is not formatted correctly * -EIO: OpenSSL failed to create the certificate * -EEXIST: if one of the RDN name entries or extensions to add is a * duplicate * -ENOTSUP: the specified digest is not supported * any other errno from file I/O routines */ int SK_OPENSSL_generate_ss_cert(const unsigned char *secure_key, size_t secure_key_size, const char *subject_rdns[], size_t num_subject_rdns, bool subject_utf8, const X509 *renew_cert, const char *extensions[], size_t num_extensions, int validity_days, int digest_nid, struct sk_rsa_pss_params *rsa_pss_params, X509 **ss_cert, const struct sk_ext_lib *ext_lib, bool debug) { const STACK_OF(X509_EXTENSION) *cert_exts = NULL; X509_NAME *subject_name = NULL; EVP_PKEY_CTX *pkey_ctx = NULL; EVP_MD_CTX *md_ctx = NULL; EVP_PKEY *pkey = NULL; X509 *cert = NULL; int rc; if (ext_lib == NULL || ss_cert == NULL) return -EINVAL; if (secure_key == NULL) return -EINVAL; if (renew_cert == NULL && (subject_rdns == NULL || num_subject_rdns == 0)) return -EINVAL; if (num_extensions != 0 && extensions == NULL) return -EINVAL; rc = SK_OPENSSL_get_secure_key_as_pkey(secure_key, secure_key_size, rsa_pss_params != NULL, &pkey, ext_lib, debug); if (rc != 0) { sk_debug(debug, "ERROR: SK_OPENSSL_get_secure_key_as_pkey failed"); goto out; } cert = X509_new(); if (cert == NULL) { sk_debug(debug, "ERROR: X509_new failed"); rc = -ENOMEM; goto out; } rc = X509_set_version(cert, 2L); if (rc != 1) { sk_debug(debug, "ERROR: X509_set_version failed: rc: %d", rc); rc = -EIO; goto out; } rc = SK_UTIL_generate_x509_serial_number(cert, SERIAL_NUMBER_BIT_SIZE); if (rc != 0) { sk_debug(debug, "ERROR: Failed to set the serial number: %s", strerror(-rc)); goto out; } if (renew_cert != NULL) { subject_name = X509_NAME_dup(X509_get_subject_name(renew_cert)); cert_exts = X509_get0_extensions(renew_cert); } if (subject_rdns != NULL && num_subject_rdns > 0) { rc = SK_UTIL_build_subject_name(&subject_name, subject_rdns, num_subject_rdns, subject_utf8); if (rc != 0) { sk_debug(debug, "ERROR: Failed to parse the subject " "name RDNs: %s", strerror(-rc)); goto out; } } if (subject_name == NULL) { rc = -EINVAL; sk_debug(debug, "ERROR: Subject name can not be empty"); goto out; } rc = X509_set_subject_name(cert, subject_name); if (rc != 1) { rc = -EIO; sk_debug(debug, "ERROR: Failed to set subject name into cert"); goto out; } rc = X509_set_issuer_name(cert, subject_name); if (rc != 1) { rc = -EIO; sk_debug(debug, "ERROR: Failed to set issuer name into cert"); goto out; } rc = SK_UTIL_build_certificate_extensions(cert, NULL, extensions, num_extensions, cert_exts); if (rc != 0) { sk_debug(debug, "ERROR: Failed to parse the extensions: " "%s", strerror(-rc)); goto out; } if (X509_gmtime_adj(X509_getm_notBefore(cert), 0) == NULL) { rc = -EIO; sk_debug(debug, "ERROR: Failed to set notBefore time inti cert"); goto out; } if (X509_time_adj_ex(X509_getm_notAfter(cert), validity_days, 0, NULL) == NULL) { rc = -EIO; sk_debug(debug, "ERROR: Failed to set notAfter time into cert"); goto out; } rc = X509_set_pubkey(cert, pkey); if (rc != 1) { sk_debug(debug, "ERROR: Failed to set the public key"); rc = -EIO; goto out; } rc = SK_OPENSSL_setup_sign_context(pkey, false, digest_nid, rsa_pss_params, &md_ctx, &pkey_ctx, debug); if (rc != 0) { sk_debug(debug, "ERROR: SK_OPENSSL_setup_sign_context failed"); goto out; } rc = X509_sign_ctx(cert, md_ctx); if (rc <= 0) { sk_debug(debug, "ERROR: Failed to perform the signing operation"); rc = -EIO; goto out; } if (debug) { sk_debug(debug, "Self-signed Certificate created:"); X509_print_fp(stderr, cert); } *ss_cert = cert; cert = NULL; rc = 0; out: if (md_ctx != NULL) EVP_MD_CTX_free(md_ctx); if (subject_name != NULL) X509_NAME_free(subject_name); if (cert != NULL) X509_free(cert); if (pkey != NULL) EVP_PKEY_free(pkey); return rc; } s390-tools-2.38.0/libseckey/sk_pkeymeth.c000066400000000000000000000713511502674226300201170ustar00rootroot00000000000000/* * libseckey - Secure key library * * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/zt_common.h" #include "libseckey/sk_openssl.h" #include "libseckey/sk_utilities.h" /* * This source file is only used with OpenSSL < 3.0. * PKEY method functions are deprecated since OpenSSL 3.0 */ #if !OPENSSL_VERSION_PREREQ(3, 0) static int sk_pkey_data_ec_index = -1; static int sk_pkey_data_rsa_index = -1; static const EVP_PKEY_METHOD *sk_pkey_meth_default_method_ec; static const EVP_PKEY_METHOD *sk_pkey_meth_default_method_rsa; static const EVP_PKEY_METHOD *sk_pkey_meth_default_method_rsa_pss; static int sk_pkey_meth_lib = -1; int sk_openssl_get_pkey_ec(const unsigned char *secure_key, size_t secure_key_size, int nid, size_t prime_len, const unsigned char *x, const unsigned char *y, const struct sk_funcs *sk_funcs, const void *private, EVP_PKEY **pkey, bool debug); int sk_openssl_get_pkey_rsa(const unsigned char *secure_key, size_t secure_key_size, const unsigned char *modulus, size_t modulus_length, const unsigned char *pub_exp, size_t pub_exp_length, int pkey_type, const struct sk_funcs *sk_funcs, const void *private, EVP_PKEY **pkey, bool debug); #define sk_debug_data(data, fmt...) sk_debug(data->debug, fmt) static void sk_pkey_meth_put_error(int err, const char *file, int line, char *fmt, ...) { char text[200]; va_list ap; va_start(ap, fmt); ERR_put_error(sk_pkey_meth_lib, 0, err, file, line); vsnprintf(text, sizeof(text), fmt, ap); ERR_add_error_data(1, text); va_end(ap); } #define put_error_data(data, err, fmt...) \ do { \ sk_debug_data(data, "ERROR: "fmt); \ sk_pkey_meth_put_error(err, __FILE__, __LINE__, \ fmt); \ } while (0) /* Secure key PKEY extra data */ struct sk_pkey_data { unsigned char *key_blob; size_t key_blob_size; struct sk_funcs *funcs; void *private; bool debug; }; /* * Free secure key PKEY data attached to a PKEY */ static void sk_pkey_data_free(void *UNUSED(parent), void *ptr, CRYPTO_EX_DATA *UNUSED(ad), int UNUSED(idx), long UNUSED(argl), void *UNUSED(argp)) { struct sk_pkey_data *data = ptr; if (data == NULL) return; sk_debug_data(data, "data: %p", data); if (data != NULL) { if (data->key_blob != NULL) OPENSSL_free(data->key_blob); OPENSSL_free(data); } } /* * Duplicate secure key PKEY data attached to a PKEY */ static int sk_pkey_data_dup(CRYPTO_EX_DATA *UNUSED(to), const CRYPTO_EX_DATA *UNUSED(from), void *from_d, int UNUSED(idx), long UNUSED(argl), void *UNUSED(argp)) { struct sk_pkey_data *from_data; struct sk_pkey_data *data; void **pptr = from_d; from_data = *pptr; sk_debug_data(from_data, "from_data: %p", from_data); data = OPENSSL_zalloc(sizeof(struct sk_pkey_data)); if (data == NULL) { put_error_data(from_data, ERR_R_MALLOC_FAILURE, "OPENSSL_malloc failed"); return 0; } memcpy(data, from_data, sizeof(struct sk_pkey_data)); data->key_blob = OPENSSL_malloc(data->key_blob_size); if (data->key_blob == NULL) { put_error_data(from_data, ERR_R_MALLOC_FAILURE, "OPENSSL_malloc failed"); OPENSSL_free(data); return 0; } memcpy(data->key_blob, from_data->key_blob, data->key_blob_size); *pptr = data; sk_debug_data(from_data, "data: %p", data); return 1; } /* * Get the default PKEY method for a POKEY id */ static const EVP_PKEY_METHOD *sk_pkey_meth_get_default_method(int pkey_id) { switch (pkey_id) { case EVP_PKEY_EC: return sk_pkey_meth_default_method_ec; case EVP_PKEY_RSA: return sk_pkey_meth_default_method_rsa; case EVP_PKEY_RSA_PSS: return sk_pkey_meth_default_method_rsa_pss; default: return NULL; } } static int sk_pkey_meth_get_pkey_data(EVP_PKEY_CTX *ctx, EVP_PKEY **pkey, struct sk_pkey_data **sk_data) { *pkey = EVP_PKEY_CTX_get0_pkey(ctx); if (*pkey == NULL) return 0; switch (EVP_PKEY_id(*pkey)) { case EVP_PKEY_EC: *sk_data = EC_KEY_get_ex_data(EVP_PKEY_get0_EC_KEY(*pkey), sk_pkey_data_ec_index); break; case EVP_PKEY_RSA: case EVP_PKEY_RSA_PSS: *sk_data = RSA_get_ex_data(EVP_PKEY_get0_RSA(*pkey), sk_pkey_data_rsa_index); break; default: return 0; } return 1; } static int sk_pkey_meth_sign_init(EVP_PKEY_CTX *ctx) { int (*sign_init)(EVP_PKEY_CTX *ctx); const EVP_PKEY_METHOD *default_meth; struct sk_pkey_data *sk_data; EVP_PKEY *pkey; if (!sk_pkey_meth_get_pkey_data(ctx, &pkey, &sk_data)) return 0; if (sk_data == NULL) { /* If no secure key is attached, call default implementation */ default_meth = sk_pkey_meth_get_default_method( EVP_PKEY_id(pkey)); if (default_meth == NULL) return 0; EVP_PKEY_meth_get_sign(default_meth, &sign_init, NULL); if (sign_init == NULL) return 1; return sign_init(ctx); } sk_debug_data(sk_data, "sk_data: %p pkey: %p type: %d", sk_data, pkey, EVP_PKEY_id(pkey)); return 1; } static int sk_pkey_meth_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen, const unsigned char *tbs, size_t tbslen) { int (*sign)(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen, const unsigned char *tbs, size_t tbslen); int sig_sz, pad_mode, hlen, saltlen, max_saltlen; const EVP_PKEY_METHOD *default_meth; struct sk_pkey_data *sk_data; int rc, md_type, mgf_md_type; const EVP_MD *sigmd, *mgf1md; const EC_KEY *ec; EVP_PKEY *pkey; if (!sk_pkey_meth_get_pkey_data(ctx, &pkey, &sk_data)) return 0; if (sk_data == NULL) { /* If no secure key is attached, call default implementation */ default_meth = sk_pkey_meth_get_default_method( EVP_PKEY_id(pkey)); if (default_meth == NULL) return 0; EVP_PKEY_meth_get_sign(default_meth, NULL, &sign); if (sign == NULL) return 0; return sign(ctx, sig, siglen, tbs, tbslen); } /* A secure key is attached, implement secure key sign */ sk_debug_data(sk_data, "sk_data: %p pkey: %p type: %d", sk_data, pkey, EVP_PKEY_id(pkey)); if (sig == NULL) { *siglen = EVP_PKEY_size(pkey); sk_debug_data(sk_data, "siglen: %lu", *siglen); return 1; } if (*siglen < (size_t)EVP_PKEY_size(pkey)) { put_error_data(sk_data, ERR_R_PASSED_INVALID_ARGUMENT, "signature buffer too small"); return 0; } *siglen = EVP_PKEY_size(pkey); if (sk_data->funcs == NULL) { put_error_data(sk_data, ERR_R_PASSED_NULL_PARAMETER, "no secure key funcs"); return 0; } if (EVP_PKEY_CTX_get_signature_md(ctx, &sigmd) != 1) return 0; md_type = sigmd != NULL ? EVP_MD_type(sigmd) : NID_sha1; switch (EVP_PKEY_id(pkey)) { case EVP_PKEY_RSA: if (sk_data->funcs->rsa_sign == NULL) { put_error_data(sk_data, ERR_R_PASSED_NULL_PARAMETER, "no secure key sign function"); return 0; } if (EVP_PKEY_CTX_get_rsa_padding(ctx, &pad_mode) != 1) return 0; rc = sk_data->funcs->rsa_sign(sk_data->key_blob, sk_data->key_blob_size, sig, siglen, tbs, tbslen, pad_mode, md_type, sk_data->private, sk_data->debug); break; case EVP_PKEY_RSA_PSS: if (sk_data->funcs->rsa_pss_sign == NULL) { put_error_data(sk_data, ERR_R_PASSED_NULL_PARAMETER, "no secure key sign function"); return 0; } if (EVP_PKEY_CTX_get_rsa_padding(ctx, &pad_mode) != 1) return 0; if (EVP_PKEY_CTX_get_rsa_mgf1_md(ctx, &mgf1md) != 1) return 0; if (EVP_PKEY_CTX_get_rsa_pss_saltlen(ctx, &saltlen) != 1) return 0; hlen = sigmd != NULL ? EVP_MD_size(sigmd) : SHA_DIGEST_LENGTH; if (mgf1md != NULL) { mgf_md_type = EVP_MD_type(mgf1md); hlen = EVP_MD_size(mgf1md); } else { mgf_md_type = md_type; } /* * We should be using RSA_bits(EVP_PKEY_get0_RSA(pkey)) here, * but EVP_PKEY_get0_RSA(pkey) does not work with PKEY/type * EVP_PKEY_RSA_PSS on older OpenSSL versions, so we fall back * on EVP_PKEY_bits in this case. */ max_saltlen = (EVP_PKEY_get0_RSA(pkey) != NULL ? RSA_bits(EVP_PKEY_get0_RSA(pkey)) : EVP_PKEY_bits(pkey)) / 8 - hlen - 2; switch (saltlen) { case RSA_PSS_SALTLEN_DIGEST: saltlen = hlen; break; case RSA_PSS_SALTLEN_AUTO: case RSA_PSS_SALTLEN_MAX: saltlen = max_saltlen; break; default: break; } if (saltlen > max_saltlen || saltlen < 0) { put_error_data(sk_data, ERR_R_PASSED_INVALID_ARGUMENT, "invalid salt length: %d", saltlen); return 0; } rc = sk_data->funcs->rsa_pss_sign(sk_data->key_blob, sk_data->key_blob_size, sig, siglen, tbs, tbslen, md_type, mgf_md_type, saltlen, sk_data->private, sk_data->debug); break; case EVP_PKEY_EC: ec = EVP_PKEY_get0_EC_KEY(pkey); if (ec == NULL) { put_error_data(sk_data, ERR_R_INTERNAL_ERROR, "EVP_PKEY_get0_EC_KEY failed"); return 0; } sig_sz = ECDSA_size(ec); if (sig == NULL) { *siglen = (size_t)sig_sz; return 0; } if (*siglen < (size_t)sig_sz) { put_error_data(sk_data, ERR_R_PASSED_INVALID_ARGUMENT, "siglen too small"); return 0; } if (sk_data->funcs->ecdsa_sign == NULL) { put_error_data(sk_data, ERR_R_PASSED_NULL_PARAMETER, "no secure key sign function"); return 0; } rc = sk_data->funcs->ecdsa_sign(sk_data->key_blob, sk_data->key_blob_size, sig, siglen, tbs, tbslen, md_type, sk_data->private, sk_data->debug); break; default: rc = -1; put_error_data(sk_data, ERR_R_INTERNAL_ERROR, "invalid PKEY type"); } if (rc != 0) { put_error_data(sk_data, ERR_R_OPERATION_FAIL, "secure key sign operation failed"); return 0; } sk_debug_data(sk_data, "siglen: %lu", *siglen); return 1; } static int sk_pkey_meth_decrypt_init(EVP_PKEY_CTX *ctx) { int (*decrypt_init)(EVP_PKEY_CTX *ctx); const EVP_PKEY_METHOD *default_meth; struct sk_pkey_data *sk_data; EVP_PKEY *pkey; if (!sk_pkey_meth_get_pkey_data(ctx, &pkey, &sk_data)) return 0; if (sk_data == NULL) { /* If no secure key is attached, call default implementation */ default_meth = sk_pkey_meth_get_default_method( EVP_PKEY_id(pkey)); if (default_meth == NULL) return 0; EVP_PKEY_meth_get_decrypt(default_meth, &decrypt_init, NULL); if (decrypt_init == NULL) return 1; return decrypt_init(ctx); } sk_debug_data(sk_data, "sk_data: %p pkey: %p type: %d", sk_data, pkey, EVP_PKEY_id(pkey)); return 1; } static int sk_pkey_meth_decrypt(EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen, const unsigned char *in, size_t inlen) { int (*decrypt)(EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen, const unsigned char *in, size_t inlen); int rc, pad_mode, label_len, md_type, mgfmd_type; const EVP_PKEY_METHOD *default_meth; struct sk_pkey_data *sk_data; const EVP_MD *md, *mgf1md; unsigned char *label; EVP_PKEY *pkey; if (!sk_pkey_meth_get_pkey_data(ctx, &pkey, &sk_data)) return 0; if (sk_data == NULL) { /* If no secure key is attached, call default implementation */ default_meth = sk_pkey_meth_get_default_method( EVP_PKEY_id(pkey)); if (default_meth == NULL) return 0; EVP_PKEY_meth_get_decrypt(default_meth, NULL, &decrypt); if (decrypt == NULL) return 0; return decrypt(ctx, out, outlen, in, inlen); } /* A secure key is attached, implement secure key decrypt */ sk_debug_data(sk_data, "sk_data: %p pkey: %p type: %d", sk_data, pkey, EVP_PKEY_id(pkey)); if (out == NULL) { *outlen = EVP_PKEY_size(pkey); sk_debug_data(sk_data, "outlen: %lu", *outlen); return 1; } if (*outlen < (size_t)EVP_PKEY_size(pkey)) { put_error_data(sk_data, ERR_R_PASSED_INVALID_ARGUMENT, "output buffer too small"); return 0; } *outlen = EVP_PKEY_size(pkey); if (sk_data->funcs == NULL) { put_error_data(sk_data, ERR_R_PASSED_NULL_PARAMETER, "no secure key funcs"); return 0; } switch (EVP_PKEY_id(pkey)) { case EVP_PKEY_RSA: if (EVP_PKEY_CTX_get_rsa_padding(ctx, &pad_mode) != 1) return 0; switch (pad_mode) { case RSA_PKCS1_OAEP_PADDING: label_len = EVP_PKEY_CTX_get0_rsa_oaep_label(ctx, &label); if (label_len < 0) return 0; if (EVP_PKEY_CTX_get_rsa_oaep_md(ctx, &md) != 1) return 0; if (EVP_PKEY_CTX_get_rsa_mgf1_md(ctx, &mgf1md) != 1) return 0; md_type = md != NULL ? EVP_MD_type(md) : NID_sha1; mgfmd_type = mgf1md != NULL ? EVP_MD_type(mgf1md) : md_type; if (sk_data->funcs->rsa_decrypt_oaep == NULL) { put_error_data(sk_data, ERR_R_PASSED_NULL_PARAMETER, "no secure key decrypt function"); return 0; } rc = sk_data->funcs->rsa_decrypt_oaep( sk_data->key_blob, sk_data->key_blob_size, out, outlen, in, inlen, md_type, mgfmd_type, label, label_len, sk_data->private, sk_data->debug); break; default: if (sk_data->funcs->rsa_decrypt == NULL) { put_error_data(sk_data, ERR_R_PASSED_NULL_PARAMETER, "no secure key decrypt function"); return 0; } rc = sk_data->funcs->rsa_decrypt(sk_data->key_blob, sk_data->key_blob_size, out, outlen, in, inlen, pad_mode, sk_data->private, sk_data->debug); break; } break; case EVP_PKEY_RSA_PSS: case EVP_PKEY_EC: default: /* encrypt not supported */ put_error_data(sk_data, ERR_R_INTERNAL_ERROR, "invalid PKEY type"); return 0; } if (rc != 0) { put_error_data(sk_data, ERR_R_OPERATION_FAIL, "secure key decrypt operation failed"); return 0; } sk_debug_data(sk_data, "outlen: %lu", *outlen); return 1; } static int sk_pkey_meth_derive_init(EVP_PKEY_CTX *ctx) { int (*derive_init)(EVP_PKEY_CTX *ctx); const EVP_PKEY_METHOD *default_meth; struct sk_pkey_data *sk_data; EVP_PKEY *pkey; if (!sk_pkey_meth_get_pkey_data(ctx, &pkey, &sk_data)) return 0; if (sk_data == NULL) { /* If no secure key is attached, call default implementation */ default_meth = sk_pkey_meth_get_default_method( EVP_PKEY_id(pkey)); if (default_meth == NULL) return 0; EVP_PKEY_meth_get_derive(default_meth, &derive_init, NULL); if (derive_init == NULL) return 1; return derive_init(ctx); } /* We can not derive with a secure key attached */ put_error_data(sk_data, ERR_R_OPERATION_FAIL, "secure key derive not supported"); return 0; } static int sk_pkey_meth_derive(EVP_PKEY_CTX *ctx, unsigned char *key, size_t *keylen) { int (*derive)(EVP_PKEY_CTX *ctx, unsigned char *key, size_t *keylen); const EVP_PKEY_METHOD *default_meth; struct sk_pkey_data *sk_data; EVP_PKEY *pkey; if (!sk_pkey_meth_get_pkey_data(ctx, &pkey, &sk_data)) return 0; if (sk_data == NULL) { /* If no secure key is attached, call default implementation */ default_meth = sk_pkey_meth_get_default_method( EVP_PKEY_id(pkey)); if (default_meth == NULL) return 0; EVP_PKEY_meth_get_derive(default_meth, NULL, &derive); if (derive == NULL) return 1; return derive(ctx, key, keylen); } /* We can not derive with a secure key attached */ put_error_data(sk_data, ERR_R_OPERATION_FAIL, "secure key derive not supported"); return 0; } static int sk_pkey_meth_setup(int pkey_id, bool debug) { const EVP_PKEY_METHOD *default_meth; EVP_PKEY_METHOD *pkey_meth; int flags = 0; sk_debug(debug, "pkey_id: %d", pkey_id); default_meth = sk_pkey_meth_get_default_method(pkey_id); if (default_meth == NULL) { sk_debug(debug, "ERROR: get_default_pkey_method failed"); return -EIO; } EVP_PKEY_meth_get0_info(NULL, &flags, default_meth); pkey_meth = EVP_PKEY_meth_new(pkey_id, flags); if (pkey_meth == NULL) { sk_debug(debug, "ERROR: EVP_PKEY_meth_new failed"); return -ENOMEM; } /* Inherit all functions from the default PKEY method */ EVP_PKEY_meth_copy(pkey_meth, default_meth); /* Override private key operations */ EVP_PKEY_meth_set_sign(pkey_meth, sk_pkey_meth_sign_init, sk_pkey_meth_sign); EVP_PKEY_meth_set_decrypt(pkey_meth, sk_pkey_meth_decrypt_init, sk_pkey_meth_decrypt); EVP_PKEY_meth_set_derive(pkey_meth, sk_pkey_meth_derive_init, sk_pkey_meth_derive); /* * Do not provide signctx and digestsign functions, even if the default * method would support them. If not available, it will fall back to * regular sign function usage, which we do override. */ EVP_PKEY_meth_set_signctx(pkey_meth, NULL, NULL); EVP_PKEY_meth_set_digestsign(pkey_meth, NULL); /* Add this as the preferred PKEY method */ if (EVP_PKEY_meth_add0(pkey_meth) != 1) { sk_debug(debug, "ERROR: EVP_PKEY_meth_add0 failed"); return -EIO; } return 0; } static int sk_pkey_meth_cleanup(int pkey_id) { const EVP_PKEY_METHOD *pkey_meth; pkey_meth = EVP_PKEY_meth_find(pkey_id); if (pkey_meth == NULL) return -ENOENT; if (EVP_PKEY_meth_remove(pkey_meth) != 1) return -EIO; EVP_PKEY_meth_free((EVP_PKEY_METHOD *)pkey_meth); return 0; } static int sk_pkey_meth_setup_pkey(EVP_PKEY *pkey, const unsigned char *secure_key, size_t secure_key_size, const struct sk_funcs *funcs, const void *private, bool debug) { struct sk_pkey_data *data; EC_KEY *ec; RSA *rsa; if (pkey == NULL || secure_key == NULL || secure_key_size == 0 || funcs == NULL) return -EINVAL; sk_debug(debug, "pkey: %p type: %d", pkey, EVP_PKEY_id(pkey)); if (sk_pkey_data_ec_index < 0 || sk_pkey_data_rsa_index < 0) { sk_debug(debug, "sk_pkey_meth support not initialized"); return -ENODEV; } data = OPENSSL_zalloc(sizeof(struct sk_pkey_data)); if (data == NULL) { sk_debug(debug, "OPENSSL_zalloc failed"); return -ENOMEM; } data->key_blob = OPENSSL_malloc(secure_key_size); if (data->key_blob == NULL) { sk_debug(debug, "OPENSSL_malloc failed"); OPENSSL_free(data); return -ENOMEM; } memcpy(data->key_blob, secure_key, secure_key_size); data->key_blob_size = secure_key_size; data->funcs = (struct sk_funcs *)funcs; data->private = (void *)private; data->debug = debug; switch (EVP_PKEY_id(pkey)) { case EVP_PKEY_EC: ec = EVP_PKEY_get0_EC_KEY(pkey); if (ec == NULL) { sk_debug(debug, "EVP_PKEY_get0_EC_KEY failed"); return -EIO; } if (!EC_KEY_set_ex_data(ec, sk_pkey_data_ec_index, data)) { sk_debug(debug, "EC_KEY_set_ex_data failed"); return -EIO; } break; case EVP_PKEY_RSA: case EVP_PKEY_RSA_PSS: rsa = EVP_PKEY_get0_RSA(pkey); if (rsa == NULL) { sk_debug(debug, "EVP_PKEY_get0_RSA failed"); return -EIO; } if (!RSA_set_ex_data(rsa, sk_pkey_data_rsa_index, data)) { sk_debug(debug, "RSA_set_ex_data failed"); return -EIO; } break; } return 0; } /** * Initializes the secure key support for OpenSSL. * * @param debug true to enable internal debugging * * @returns zero for success, a negative errno in case of an error: * -ENOMEM: failed to allocate memory * -EIO: OpenSSL failed for various reasons */ int SK_OPENSSL_init(bool debug) { int rc; if (sk_pkey_data_ec_index < 0) sk_pkey_data_ec_index = CRYPTO_get_ex_new_index( CRYPTO_EX_INDEX_EC_KEY, 0, NULL, NULL, sk_pkey_data_dup, sk_pkey_data_free); if (sk_pkey_data_ec_index < 0) { sk_debug(debug, "ERROR: CRYPTO_get_ex_new_index(EC) failed"); return -EIO; } if (sk_pkey_data_rsa_index < 0) sk_pkey_data_rsa_index = CRYPTO_get_ex_new_index( CRYPTO_EX_INDEX_RSA, 0, NULL, NULL, sk_pkey_data_dup, sk_pkey_data_free); if (sk_pkey_data_rsa_index < 0) { sk_debug(debug, "ERROR: CRYPTO_get_ex_new_index(RSA) failed"); return -EIO; } sk_pkey_meth_default_method_ec = EVP_PKEY_meth_find(EVP_PKEY_EC); if (sk_pkey_meth_default_method_ec == NULL) { sk_debug(debug, "ERROR: EVP_PKEY_meth_find(EC) failed"); return -EIO; } sk_pkey_meth_default_method_rsa = EVP_PKEY_meth_find(EVP_PKEY_RSA); if (sk_pkey_meth_default_method_ec == NULL) { sk_debug(debug, "ERROR: EVP_PKEY_meth_find(RSA) failed"); return -EIO; } sk_pkey_meth_default_method_rsa_pss = EVP_PKEY_meth_find(EVP_PKEY_RSA_PSS); if (sk_pkey_meth_default_method_ec == NULL) { sk_debug(debug, "ERROR: EVP_PKEY_meth_find(RSA-PSS) failed"); return -EIO; } rc = sk_pkey_meth_setup(EVP_PKEY_EC, debug); if (rc != 0) { sk_debug(debug, "ERROR: sk_pkey_meth_setup(EC) " "failed"); return rc; } rc = sk_pkey_meth_setup(EVP_PKEY_RSA, debug); if (rc != 0) { sk_debug(debug, "ERROR: sk_pkey_meth_setup(RSA) " "failed"); return rc; } rc = sk_pkey_meth_setup(EVP_PKEY_RSA_PSS, debug); if (rc != 0) { sk_debug(debug, "ERROR: sk_pkey_meth_setup(RSA-PSS) " "failed"); return rc; } if (sk_pkey_meth_lib <= 0) sk_pkey_meth_lib = ERR_get_next_error_library(); if (sk_pkey_meth_lib <= 0) { sk_debug(debug, "ERROR: ERR_get_next_error_library failed"); return -EIO; } sk_debug(debug, "sk_pkey_meth support initialized"); return 0; } /** * Terminate the secure key support for OpenSSL. */ void SK_OPENSSL_term(void) { if (sk_pkey_data_ec_index >= 0) CRYPTO_free_ex_index(CRYPTO_EX_INDEX_EC_KEY, sk_pkey_data_ec_index); sk_pkey_data_ec_index = -1; if (sk_pkey_data_rsa_index >= 0) CRYPTO_free_ex_index(CRYPTO_EX_INDEX_RSA, sk_pkey_data_rsa_index); sk_pkey_data_rsa_index = -1; sk_pkey_meth_cleanup(EVP_PKEY_EC); sk_pkey_meth_cleanup(EVP_PKEY_RSA); sk_pkey_meth_cleanup(EVP_PKEY_RSA_PSS); sk_pkey_meth_default_method_ec = NULL; sk_pkey_meth_default_method_rsa = NULL; sk_pkey_meth_default_method_rsa_pss = NULL; sk_pkey_meth_lib = -1; } /** * Converts an EC key given by the nid and the x and y coordinates into an * OpenSSL PKEY and attaches the secure key together with secure key functions * and private pointer to it. If no secure key is provided, a public EC key * only PKEY is returned. * * @param secure_key the secure key blob. * If NULL, a clear key PKEY is created. * @param secure_key_size the size of the secure key blob (ignored if * secure_key is NULL) * @param nid the OpenSSL nid of the EC curve used * @param prime_len the length of the prime in bytes. This is also the * length of the x and y coordinates. * @param x the x coordinate as big endian binary number in * prime_len size * @param y the y coordinate as big endian binary number in * prime_len size * @param sk_funcs the secure key functions to operate with the key. * Ignored if secure_key is NULL, required otherwise. * @param private a private pointer that is passed to the secure key * functions (can be NULL) * @param pkey On return: A PKEY containing the EC public key. * @param debug true to enable internal debugging * * @returns zero for success, a negative errno in case of an error: * -EINVAL: a function parameter is invalid * -ENOMEM: failed to allocate memory * -EIO: OpenSSL failed to generate the PKEY * -ENOENT: OpenSSL does not know/support the curve (nid) */ int sk_openssl_get_pkey_ec(const unsigned char *secure_key, size_t secure_key_size, int nid, size_t prime_len, const unsigned char *x, const unsigned char *y, const struct sk_funcs *sk_funcs, const void *private, EVP_PKEY **pkey, bool debug) { BIGNUM *bn_x = NULL, *bn_y = NULL; EC_GROUP *group = NULL; EC_KEY *ec = NULL; int rc; if (pkey == NULL || x == NULL || y == NULL) return -EINVAL; if (secure_key != NULL && (secure_key_size == 0 || sk_funcs == NULL)) return -EINVAL; *pkey = NULL; bn_x = BN_bin2bn(x, prime_len, NULL); bn_y = BN_bin2bn(y, prime_len, NULL); if (bn_x == NULL || bn_y == NULL) { sk_debug(debug, "ERROR: BN_bin2bn failed"); rc = -ENOMEM; goto out; } group = EC_GROUP_new_by_curve_name(nid); if (group == NULL) { sk_debug(debug, "ERROR: EC_GROUP_new_by_curve_name failed"); rc = -ENOENT; goto out; } ec = EC_KEY_new(); if (ec == NULL) { sk_debug(debug, "ERROR: EC_KEY_new failed"); rc = -ENOMEM; goto out; } rc = EC_KEY_set_group(ec, group); if (rc != 1) { sk_debug(debug, "ERROR: EC_KEY_set_group failed"); rc = -EIO; goto out; } rc = EC_KEY_set_public_key_affine_coordinates(ec, bn_x, bn_y); if (rc != 1) { sk_debug(debug, "ERROR: EC_KEY_set_public_key_affine_coordinates failed"); rc = -EIO; goto out; } *pkey = EVP_PKEY_new(); if (*pkey == NULL) { sk_debug(debug, "ERROR: EVP_PKEY_new failed"); rc = -ENOMEM; goto out; } rc = EVP_PKEY_assign_EC_KEY(*pkey, ec); if (rc != 1) { sk_debug(debug, "ERROR: EVP_PKEY_assign_EC_KEY failed"); rc = -EIO; goto out; } if (secure_key != NULL) { rc = sk_pkey_meth_setup_pkey(*pkey, secure_key, secure_key_size, sk_funcs, private, debug); if (rc != 0) { sk_debug(debug, "ERROR: sk_pkey_meth_setup_pkey failed"); goto out; } } rc = 0; sk_debug(debug, "pkey created: %p", *pkey); out: if (bn_x != NULL) BN_free(bn_x); if (bn_y != NULL) BN_free(bn_y); if (group != NULL) EC_GROUP_free(group); if (rc != 0 && *pkey != NULL) { EVP_PKEY_free(*pkey); *pkey = NULL; } return rc; } /** * Converts an RSA key given by the modulus and public exponent into an * OpenSSL PKEY and attaches the secure key together with secure key functions * and private pointer to it. If no secure key is provided, a public RSA key * only PKEY is returned. * * @param secure_key the secure key blob. * If NULL, a clear key PKEY is created. * @param secure_key_size the size of the secure key blob (ignored if * secure_key is NULL) * @param modulus the modulus as big endian number * @param modulus_length the length of the modulus in bytes * @param pub_exp the public exponent as big endian number * @param pub_exp_length the length of the public exponent in bytes * @param pkey_type the PKEY type (EVP_PKEY_RSA or EVP_PKEY_RSA_PSS) * @param sk_funcs the secure key functions to operate with the key. * Ignored if secure_key is NULL, required otherwise. * @param private a private pointer that is passed to the secure key * functions (can be NULL) * @param pkey On return: A PKEY containing the RSA public key. * @param debug true to enable internal debugging * * @returns zero for success, a negative errno in case of an error: * -EINVAL: a function parameter is invalid * -ENOMEM: failed to allocate memory * -EIO: OpenSSL failed to generate the PKEY */ int sk_openssl_get_pkey_rsa(const unsigned char *secure_key, size_t secure_key_size, const unsigned char *modulus, size_t modulus_length, const unsigned char *pub_exp, size_t pub_exp_length, int pkey_type, const struct sk_funcs *sk_funcs, const void *private, EVP_PKEY **pkey, bool debug) { BIGNUM *bn_modulus = NULL, *bn_pub_exp = NULL; RSA *rsa; int rc; if (pkey == NULL || modulus == NULL || pub_exp == NULL) return -EINVAL; if (secure_key != NULL && (secure_key_size == 0 || sk_funcs == NULL)) return -EINVAL; if (pkey_type != EVP_PKEY_RSA && pkey_type != EVP_PKEY_RSA_PSS) return -EINVAL; *pkey = NULL; bn_modulus = BN_bin2bn(modulus, modulus_length, NULL); bn_pub_exp = BN_bin2bn(pub_exp, pub_exp_length, NULL); if (bn_modulus == NULL || bn_pub_exp == NULL) { sk_debug(debug, "ERROR: BN_bin2bn failed"); rc = -ENOMEM; goto out; } rsa = RSA_new(); if (rsa == NULL) { sk_debug(debug, "ERROR: RSA_new failed"); rc = -ENOMEM; goto out; } rc = RSA_set0_key(rsa, bn_modulus, bn_pub_exp, NULL); if (rc != 1) { sk_debug(debug, "ERROR: RSA_set0_key failed"); rc = -EIO; goto out; } *pkey = EVP_PKEY_new(); if (*pkey == NULL) { sk_debug(debug, "ERROR: EVP_PKEY_new failed"); rc = -ENOMEM; goto out; } rc = EVP_PKEY_assign(*pkey, pkey_type, rsa); if (rc != 1) { sk_debug(debug, "ERROR: EVP_PKEY_assign failed"); rc = -EIO; goto out; } if (secure_key != NULL) { rc = sk_pkey_meth_setup_pkey(*pkey, secure_key, secure_key_size, sk_funcs, private, debug); if (rc != 0) { sk_debug(debug, "ERROR: sk_pkey_meth_setup_pkey failed"); goto out; } } rc = 0; sk_debug(debug, "pkey created: %p", *pkey); out: if (rc != 0 && bn_modulus != NULL) BN_free(bn_modulus); if (rc != 0 && bn_pub_exp != NULL) BN_free(bn_pub_exp); return rc; } /** * Get the curve NID of the EC pkey */ int SK_OPENSSL_get_curve_from_ec_pkey(EVP_PKEY *pkey) { if (EVP_PKEY_id(pkey) != EVP_PKEY_EC) return NID_undef; return EC_GROUP_get_curve_name(EC_KEY_get0_group( EVP_PKEY_get0_EC_KEY(pkey))); } #endif s390-tools-2.38.0/libseckey/sk_provider.c000066400000000000000000004707221502674226300201300ustar00rootroot00000000000000/* * libseckey - Secure key library * * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/zt_common.h" #include "libseckey/sk_openssl.h" #include "libseckey/sk_utilities.h" /* * This source file is only used with OpenSSL >= 3.0 */ #if OPENSSL_VERSION_PREREQ(3, 0) #include #include #include #include #include #include "openssl/param_build.h" #include static OSSL_LIB_CTX *sk_prov_securekey_libctx; static OSSL_LIB_CTX *sk_prov_previous_libctx; static OSSL_PROVIDER *sk_prov_securekey_provider; static OSSL_PROVIDER *sk_prov_default_provider; #define SK_PROV_NAME "securekey" #define SK_PROV_DESCRIPTION "Secure key provider" #define SK_PROV_VERSION "1.0" #define SK_PROV_RSA_DEFAULT_MD "SHA-1" #define SK_PROV_EC_DEFAULT_MD_NID "SHA-1" #define SK_PROV_PKEY_PARAM_SK_BLOB "sk-blob" #define SK_PROV_PKEY_PARAM_SK_FUNCS "sk-funcs" #define SK_PROV_PKEY_PARAM_SK_PRIVATE "sk-private" #define SK_CONF_CACHED_PARAMS_OP_KEY_EXPORT 0 /* 16 possible selections */ #define SK_CONF_CACHED_PARAMS_OP_KEY_IMPORT 16 /* 16 possible selections */ #define SK_CONF_CACHED_PARAMS_OP_KEY_GET 32 #define SK_CONF_CACHED_PARAMS_OP_KEY_SET 33 #define SK_CONF_CACHED_PARAMS_OP_COUNT 34 #define SK_CONF_CACHED_PARAMS_ALGO_RSA 0 #define SK_CONF_CACHED_PARAMS_ALGO_RSA_PSS 1 #define SK_CONF_CACHED_PARAMS_ALGO_EC 2 #define SK_CONF_CACHED_PARAMS_ALGO_COUNT 3 #define SK_PROV_CACHED_PARAMS_COUNT \ (SK_CONF_CACHED_PARAMS_ALGO_COUNT * SK_CONF_CACHED_PARAMS_OP_COUNT) struct sk_prov_ctx { const OSSL_CORE_HANDLE *handle; OSSL_FUNC_core_get_libctx_fn *c_get_libctx; OSSL_FUNC_core_new_error_fn *c_new_error; OSSL_FUNC_core_set_error_debug_fn *c_set_error_debug; OSSL_FUNC_core_vset_error_fn *c_vset_error; OSSL_PROVIDER *default_provider; void *default_provctx; const OSSL_ALGORITHM *cached_default_algos[OSSL_OP__HIGHEST]; const OSSL_PARAM *cached_parms[SK_PROV_CACHED_PARAMS_COUNT]; bool debug; }; struct sk_prov_key { struct sk_prov_ctx *provctx; int type; /* EVP_PKEY_xxx types */ void *default_key; /* shadow key of default provider */ unsigned char *secure_key; size_t secure_key_size; struct sk_funcs *funcs; void *private; unsigned int ref_count; }; struct sk_prov_op_ctx { struct sk_prov_ctx *provctx; int type; /* EVP_PKEY_xxx types */ const char *propq; void *default_op_ctx; /* shadow context of default provider */ void (*default_op_ctx_free)(void *default_op_ctx); struct sk_prov_key *key; int operation; OSSL_FUNC_signature_sign_fn *sign_fn; EVP_MD_CTX *mdctx; EVP_MD *md; }; #define sk_debug_ctx(ctx, fmt...) sk_debug(ctx->debug, fmt) #define sk_debug_key(key, fmt...) sk_debug(key->provctx->debug, fmt) #define sk_debug_op_ctx(ctx, fmt...) sk_debug(ctx->provctx->debug, fmt) int sk_openssl_get_pkey_ec(const unsigned char *secure_key, size_t secure_key_size, int nid, size_t prime_len, const unsigned char *x, const unsigned char *y, const struct sk_funcs *sk_funcs, const void *private, EVP_PKEY **pkey, bool debug); int sk_openssl_get_pkey_rsa(const unsigned char *secure_key, size_t secure_key_size, const unsigned char *modulus, size_t modulus_length, const unsigned char *pub_exp, size_t pub_exp_length, int pkey_type, const struct sk_funcs *sk_funcs, const void *private, EVP_PKEY **pkey, bool debug); static OSSL_FUNC_provider_teardown_fn sk_prov_teardown; static OSSL_FUNC_provider_gettable_params_fn sk_prov_gettable_params; static OSSL_FUNC_provider_get_params_fn sk_prov_get_params; static OSSL_FUNC_provider_query_operation_fn sk_prov_query; static OSSL_FUNC_provider_get_reason_strings_fn sk_prov_get_reason_strings; static OSSL_FUNC_provider_get_capabilities_fn sk_prov_prov_get_capabilities; static OSSL_FUNC_keymgmt_free_fn sk_prov_keymgmt_free; static OSSL_FUNC_keymgmt_gen_cleanup_fn sk_prov_keymgmt_gen_cleanup; static OSSL_FUNC_keymgmt_load_fn sk_prov_keymgmt_load; static OSSL_FUNC_keymgmt_gen_set_template_fn sk_prov_keymgmt_gen_set_template; static OSSL_FUNC_keymgmt_gen_set_params_fn sk_prov_keymgmt_gen_set_params; static OSSL_FUNC_keymgmt_gen_fn sk_prov_keymgmt_gen; static OSSL_FUNC_keymgmt_get_params_fn sk_prov_keymgmt_get_params; static OSSL_FUNC_keymgmt_set_params_fn sk_prov_keymgmt_set_params; static OSSL_FUNC_keymgmt_has_fn sk_prov_keymgmt_has; static OSSL_FUNC_keymgmt_match_fn sk_prov_keymgmt_match; static OSSL_FUNC_keymgmt_validate_fn sk_prov_keymgmt_validate; static OSSL_FUNC_keymgmt_export_fn sk_prov_keymgmt_export; static OSSL_FUNC_keymgmt_import_fn sk_prov_keymgmt_import; static OSSL_FUNC_keymgmt_new_fn sk_prov_keymgmt_rsa_new; static OSSL_FUNC_keymgmt_new_fn sk_prov_keymgmt_rsa_pss_new; static OSSL_FUNC_keymgmt_new_fn sk_prov_keymgmt_ec_new; static OSSL_FUNC_keymgmt_gen_init_fn sk_prov_keymgmt_rsa_gen_init; static OSSL_FUNC_keymgmt_gen_init_fn sk_prov_keymgmt_rsa_pss_gen_init; static OSSL_FUNC_keymgmt_gen_init_fn sk_prov_keymgmt_ec_gen_init; static OSSL_FUNC_keymgmt_gen_settable_params_fn sk_prov_keymgmt_rsa_gen_settable_params; static OSSL_FUNC_keymgmt_gen_settable_params_fn sk_prov_keymgmt_rsa_pss_gen_settable_params; static OSSL_FUNC_keymgmt_gen_settable_params_fn sk_prov_keymgmt_ec_gen_settable_params; static OSSL_FUNC_keymgmt_query_operation_name_fn sk_prov_keymgmt_rsa_query_operation_name; static OSSL_FUNC_keymgmt_query_operation_name_fn sk_prov_keymgmt_ec_query_operation_name; static OSSL_FUNC_keymgmt_gettable_params_fn sk_prov_keymgmt_rsa_gettable_params; static OSSL_FUNC_keymgmt_gettable_params_fn sk_prov_keymgmt_rsa_pss_gettable_params; static OSSL_FUNC_keymgmt_gettable_params_fn sk_prov_keymgmt_ec_gettable_params; static OSSL_FUNC_keymgmt_settable_params_fn sk_prov_keymgmt_rsa_settable_params; static OSSL_FUNC_keymgmt_settable_params_fn sk_prov_keymgmt_rsa_pss_settable_params; static OSSL_FUNC_keymgmt_settable_params_fn sk_prov_keymgmt_ec_settable_params; #ifdef OSSL_FUNC_KEYMGMT_EXPORT_TYPES_EX static OSSL_FUNC_keymgmt_export_types_ex_fn sk_prov_keymgmt_rsa_export_types_ex; static OSSL_FUNC_keymgmt_export_types_ex_fn sk_prov_keymgmt_rsa_pss_export_types_ex; static OSSL_FUNC_keymgmt_export_types_ex_fn sk_prov_keymgmt_ec_export_types_ex; static OSSL_FUNC_keymgmt_import_types_ex_fn sk_prov_keymgmt_rsa_import_types_ex; static OSSL_FUNC_keymgmt_import_types_ex_fn sk_prov_keymgmt_rsa_pss_import_types_ex; static OSSL_FUNC_keymgmt_import_types_ex_fn sk_prov_keymgmt_ec_import_types_ex; #else static OSSL_FUNC_keymgmt_export_types_fn sk_prov_keymgmt_rsa_export_types; static OSSL_FUNC_keymgmt_export_types_fn sk_prov_keymgmt_rsa_pss_export_types; static OSSL_FUNC_keymgmt_export_types_fn sk_prov_keymgmt_ec_export_types; static OSSL_FUNC_keymgmt_import_types_fn sk_prov_keymgmt_rsa_import_types; static OSSL_FUNC_keymgmt_import_types_fn sk_prov_keymgmt_rsa_pss_import_types; static OSSL_FUNC_keymgmt_import_types_fn sk_prov_keymgmt_ec_import_types; #endif static OSSL_FUNC_keyexch_newctx_fn sk_prov_keyexch_ec_newctx; static OSSL_FUNC_keyexch_dupctx_fn sk_prov_keyexch_ec_dupctx; static OSSL_FUNC_keyexch_init_fn sk_prov_keyexch_ec_init; static OSSL_FUNC_keyexch_set_peer_fn sk_prov_keyexch_ec_set_peer; static OSSL_FUNC_keyexch_derive_fn sk_prov_keyexch_ec_derive; static OSSL_FUNC_keyexch_set_ctx_params_fn sk_prov_keyexch_ec_set_ctx_params; static OSSL_FUNC_keyexch_settable_ctx_params_fn sk_prov_keyexch_ec_settable_ctx_params; static OSSL_FUNC_keyexch_get_ctx_params_fn sk_prov_keyexch_ec_get_ctx_params; static OSSL_FUNC_keyexch_gettable_ctx_params_fn sk_prov_keyexch_ec_gettable_ctx_params; static OSSL_FUNC_signature_newctx_fn sk_prov_sign_rsa_newctx; static OSSL_FUNC_signature_newctx_fn sk_prov_sign_ec_newctx; static OSSL_FUNC_signature_dupctx_fn sk_prov_sign_op_dupctx; static OSSL_FUNC_signature_sign_init_fn sk_prov_sign_op_sign_init; static OSSL_FUNC_signature_sign_fn sk_prov_sign_rsa_sign; static OSSL_FUNC_signature_sign_fn sk_prov_sign_ec_sign; static OSSL_FUNC_signature_verify_init_fn sk_prov_sign_op_verify_init; static OSSL_FUNC_signature_verify_fn sk_prov_sign_op_verify; static OSSL_FUNC_signature_verify_recover_init_fn sk_prov_sign_op_verify_recover_init; static OSSL_FUNC_signature_verify_recover_fn sk_prov_sign_op_verify_recover; static OSSL_FUNC_signature_digest_sign_init_fn sk_prov_sign_rsa_digest_sign_init; static OSSL_FUNC_signature_digest_sign_init_fn sk_prov_sign_ec_digest_sign_init; static OSSL_FUNC_signature_digest_sign_update_fn sk_prov_sign_op_digest_sign_update; static OSSL_FUNC_signature_digest_sign_final_fn sk_prov_sign_op_digest_sign_final; static OSSL_FUNC_signature_digest_verify_init_fn sk_prov_sign_op_digest_verify_init; static OSSL_FUNC_signature_digest_verify_update_fn sk_prov_sign_op_digest_verify_update; static OSSL_FUNC_signature_digest_verify_final_fn sk_prov_sign_op_digest_verify_final; static OSSL_FUNC_signature_get_ctx_params_fn sk_prov_sign_op_get_ctx_params; static OSSL_FUNC_signature_gettable_ctx_params_fn sk_prov_sign_rsa_gettable_ctx_params; static OSSL_FUNC_signature_gettable_ctx_params_fn sk_prov_sign_ec_gettable_ctx_params; static OSSL_FUNC_signature_set_ctx_params_fn sk_prov_sign_op_set_ctx_params; static OSSL_FUNC_signature_settable_ctx_params_fn sk_prov_sign_rsa_settable_ctx_params; static OSSL_FUNC_signature_settable_ctx_params_fn sk_prov_sign_ec_settable_ctx_params; static OSSL_FUNC_signature_get_ctx_md_params_fn sk_prov_sign_op_get_ctx_md_params; static OSSL_FUNC_signature_gettable_ctx_md_params_fn sk_prov_sign_rsa_gettable_ctx_md_params; static OSSL_FUNC_signature_gettable_ctx_md_params_fn sk_prov_sign_ec_gettable_ctx_md_params; static OSSL_FUNC_signature_set_ctx_md_params_fn sk_prov_sign_op_set_ctx_md_params; static OSSL_FUNC_signature_settable_ctx_md_params_fn sk_prov_sign_rsa_settable_ctx_md_params; static OSSL_FUNC_signature_settable_ctx_md_params_fn sk_prov_sign_ec_settable_ctx_md_params; static OSSL_FUNC_asym_cipher_newctx_fn sk_prov_asym_rsa_newctx; static OSSL_FUNC_asym_cipher_dupctx_fn sk_prov_asym_op_dupctx; static OSSL_FUNC_asym_cipher_freectx_fn sk_prov_op_freectx; static OSSL_FUNC_asym_cipher_get_ctx_params_fn sk_prov_asym_op_get_ctx_params; static OSSL_FUNC_asym_cipher_gettable_ctx_params_fn sk_prov_asym_rsa_gettable_ctx_params; static OSSL_FUNC_asym_cipher_set_ctx_params_fn sk_prov_asym_op_set_ctx_params; static OSSL_FUNC_asym_cipher_settable_ctx_params_fn sk_prov_asym_rsa_settable_ctx_params; static OSSL_FUNC_asym_cipher_encrypt_init_fn sk_prov_asym_op_encrypt_init; static OSSL_FUNC_asym_cipher_encrypt_fn sk_prov_asym_op_encrypt; static OSSL_FUNC_asym_cipher_decrypt_init_fn sk_prov_asym_op_decrypt_init; static OSSL_FUNC_asym_cipher_decrypt_fn sk_prov_asym_rsa_decrypt; #define SK_PROV_ERR_INTERNAL_ERROR 1 #define SK_PROV_ERR_MALLOC_FAILED 2 #define SK_PROV_ERR_INVALID_PARAM 3 #define SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING 4 #define SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED 5 #define SK_PROV_ERR_OPRATION_NOT_INITIALIZED 6 #define SK_PROV_ERR_MISSING_PARAMETER 7 #define SK_PROV_ERR_INVALID_PADDING 8 #define SK_PROV_ERR_INVALID_MD 9 #define SK_PROV_ERR_INVALID_SALTLEN 10 #define SK_PROV_ERR_SECURE_KEY_FUNC_FAILED 11 static const OSSL_ITEM sk_prov_reason_strings[] = { { SK_PROV_ERR_INTERNAL_ERROR, "Internal error" }, { SK_PROV_ERR_MALLOC_FAILED, "Memory allocation failed" }, { SK_PROV_ERR_INVALID_PARAM, "Invalid parameter encountered" }, { SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "A function inherited from default provider is missing" }, { SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "A function inherited from default provider has failed" }, { SK_PROV_ERR_OPRATION_NOT_INITIALIZED, "An operation context has not been initialized" }, { SK_PROV_ERR_MISSING_PARAMETER, "A parameter of a key or a context is missing" }, { SK_PROV_ERR_INVALID_PADDING, "An invalid or unknown padding is used" }, { SK_PROV_ERR_INVALID_MD, "An invalid or unknown digest is used" }, { SK_PROV_ERR_INVALID_SALTLEN, "An invalid salt length is used" }, { SK_PROV_ERR_SECURE_KEY_FUNC_FAILED, "A secure key function has failed" }, {0, NULL } }; static void sk_prov_put_error(struct sk_prov_ctx *provctx, int err, const char *file, int line, const char *func, char *fmt, ...) { va_list ap; if (provctx == NULL) return; va_start(ap, fmt); provctx->c_new_error(provctx->handle); provctx->c_set_error_debug(provctx->handle, file, line, func); provctx->c_vset_error(provctx->handle, err, fmt, ap); va_end(ap); } #define put_error_ctx(ctx, err, fmt...) \ do { \ sk_debug_ctx(ctx, "ERROR: "fmt); \ sk_prov_put_error(ctx, err, __FILE__, \ __LINE__, __func__, fmt); \ } while (0) #define put_error_key(key, err, fmt...) \ put_error_ctx(key->provctx, err, fmt) #define put_error_op_ctx(ctx, err, fmt...) \ put_error_ctx(ctx->provctx, err, fmt) static void sk_prov_keymgmt_upref(struct sk_prov_key *key); static struct sk_prov_key *sk_prov_keymgmt_new(struct sk_prov_ctx *provctx, int type); static int sk_prov_keymgmt_get_bits(struct sk_prov_key *key); typedef void (*func_t)(void); static func_t sk_prov_get_default_func(struct sk_prov_ctx *provctx, int operation_id, const char *algorithm, int function_id) { const OSSL_ALGORITHM *default_algos, *algs; const OSSL_DISPATCH *default_impl, *impl; int algolen = strlen(algorithm); int no_cache = 0, query = 0; func_t func = NULL; const char *found; if (provctx == NULL || provctx->default_provider == NULL || operation_id <= 0 || operation_id > OSSL_OP__HIGHEST) return NULL; sk_debug_ctx(provctx, "operation_id: %d, algo: %s, func: %d", operation_id, algorithm, function_id); default_algos = provctx->cached_default_algos[operation_id]; if (default_algos == NULL) { default_algos = OSSL_PROVIDER_query_operation( provctx->default_provider, operation_id, &no_cache); query = 1; } for (algs = default_algos; algs != NULL && algs->algorithm_names != NULL; algs++) { found = strcasestr(algs->algorithm_names, algorithm); if (found == NULL) continue; if (found[algolen] != '\0' && found[algolen] != ':') continue; if (found != algs->algorithm_names && found[-1] != ':') continue; default_impl = algs->implementation; for (impl = default_impl; impl->function_id != 0; impl++) { if (impl->function_id == function_id) { func = impl->function; break; } } break; } if (query == 1 && default_algos != NULL) OSSL_PROVIDER_unquery_operation(provctx->default_provider, operation_id, default_algos); if (no_cache == 0 && provctx->cached_default_algos[operation_id] == NULL) provctx->cached_default_algos[operation_id] = default_algos; sk_debug_ctx(provctx, "func: %p", func); return func; } static const char *sk_prov_get_algo(int pkey_type, bool sign) { switch (pkey_type) { case EVP_PKEY_RSA: return "RSA"; case EVP_PKEY_RSA_PSS: return "RSA-PSS"; case EVP_PKEY_EC: if (sign) return "ECDSA"; else return "EC"; default: return NULL; } } static func_t sk_prov_get_default_keymgmt_func(struct sk_prov_ctx *provctx, int pkey_type, int function_id) { return sk_prov_get_default_func(provctx, OSSL_OP_KEYMGMT, sk_prov_get_algo(pkey_type, false), function_id); } static func_t sk_prov_get_default_keyexch_func(struct sk_prov_ctx *provctx, int function_id) { return sk_prov_get_default_func(provctx, OSSL_OP_KEYEXCH, "ECDH", function_id); } static func_t sk_prov_get_default_asym_func(struct sk_prov_ctx *provctx, int pkey_type, int function_id) { return sk_prov_get_default_func(provctx, OSSL_OP_ASYM_CIPHER, sk_prov_get_algo(pkey_type, false), function_id); } static func_t sk_prov_get_default_sign_func(struct sk_prov_ctx *provctx, int pkey_type, int function_id) { return sk_prov_get_default_func(provctx, OSSL_OP_SIGNATURE, sk_prov_get_algo(pkey_type, true), function_id); } static int sk_prov_get_cached_params_index(int pkey_type, int operation, int selection) { int ofs = 0; if (operation < 0 || operation >= SK_CONF_CACHED_PARAMS_OP_COUNT) return -1; switch (operation) { case SK_CONF_CACHED_PARAMS_OP_KEY_EXPORT: case SK_CONF_CACHED_PARAMS_OP_KEY_IMPORT: if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) ofs += 1; if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) ofs += 2; if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) ofs += 4; if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0) ofs += 8; break; } switch (pkey_type) { case EVP_PKEY_RSA: return (SK_CONF_CACHED_PARAMS_ALGO_RSA * SK_CONF_CACHED_PARAMS_OP_COUNT) + operation + ofs; case EVP_PKEY_RSA_PSS: return (SK_CONF_CACHED_PARAMS_ALGO_RSA_PSS * SK_CONF_CACHED_PARAMS_OP_COUNT) + operation + ofs; case EVP_PKEY_EC: return (SK_CONF_CACHED_PARAMS_ALGO_EC * SK_CONF_CACHED_PARAMS_OP_COUNT) + operation + ofs; } return -1; } static const OSSL_PARAM *sk_prov_get_cached_params(struct sk_prov_ctx *provctx, int pkey_type, int operation, int selection) { int index; sk_debug_ctx(provctx, "pkey_type: %d operation: %d selection: %x", pkey_type, operation, selection); index = sk_prov_get_cached_params_index(pkey_type, operation, selection); if (index < 0) { put_error_ctx(provctx, SK_PROV_ERR_INTERNAL_ERROR, "Invalid type, operation or selection"); return NULL; } return provctx->cached_parms[index]; } static const OSSL_PARAM *sk_prov_cached_params_build( struct sk_prov_ctx *provctx, int pkey_type, int operation, int selection, const OSSL_PARAM *params1, const OSSL_PARAM *params2) { int index, count = 0, i, k = 0; OSSL_PARAM *params; sk_debug_ctx(provctx, "pkey_type: %d operation: %d selection: %x", pkey_type, operation, selection); index = sk_prov_get_cached_params_index(pkey_type, operation, selection); if (index < 0) { put_error_ctx(provctx, SK_PROV_ERR_INTERNAL_ERROR, "Invalid type, operation or selection"); return NULL; } if (provctx->cached_parms[index] != NULL) { OPENSSL_free((void *)provctx->cached_parms[index]); provctx->cached_parms[index] = NULL; } for (i = 0; params1 != NULL && params1[i].key != NULL; i++, count++) ; for (i = 0; params2 != NULL && params2[i].key != NULL; i++, count++) ; sk_debug_ctx(provctx, "count: %d", count); count++; /* End marker */ params = OPENSSL_zalloc(sizeof(OSSL_PARAM) * count); if (params == NULL) { put_error_ctx(provctx, SK_PROV_ERR_MALLOC_FAILED, "OPENSSL_zalloc failed"); return NULL; } for (i = 0; params1 != NULL && params1[i].key != NULL; i++, k++) { params[k] = params1[i]; sk_debug_ctx(provctx, "param %d: %s", k, params[k].key); } for (i = 0; params2 != NULL && params2[i].key != NULL; i++, k++) { params[k] = params2[i]; sk_debug_ctx(provctx, "param %d: %s", k, params[k].key); } params[k] = OSSL_PARAM_construct_end(); provctx->cached_parms[index] = params; return provctx->cached_parms[index]; } #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-function" static bool sk_prov_check_uint_param(const OSSL_PARAM params[], const char *param_name, const struct sk_prov_key *key, int key_type, unsigned int expected_value) { const OSSL_PARAM *p; unsigned int value; if (key == NULL) return true; if (key->type != key_type) return true; p = OSSL_PARAM_locate_const(params, param_name); if (p == NULL) return true; if (OSSL_PARAM_get_uint(p, &value) != 1) return true; return value == expected_value; } #pragma GCC diagnostic pop static struct sk_prov_op_ctx *sk_prov_op_newctx(struct sk_prov_ctx *provctx, const char *propq, int type) { struct sk_prov_op_ctx *ctx; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "propq: %s type: %d", propq != NULL ? propq : "", type); ctx = OPENSSL_zalloc(sizeof(struct sk_prov_op_ctx)); if (ctx == NULL) { put_error_ctx(provctx, SK_PROV_ERR_MALLOC_FAILED, "OPENSSL_zalloc failed"); return NULL; } ctx->provctx = provctx; ctx->type = type; if (propq != NULL) { ctx->propq = OPENSSL_strdup(propq); if (ctx->propq == NULL) { put_error_ctx(provctx, SK_PROV_ERR_MALLOC_FAILED, "OPENSSL_strdup failed"); OPENSSL_free(ctx); return NULL; } } sk_debug_ctx(provctx, "ctx: %p", ctx); return ctx; } static void sk_prov_op_freectx(void *vctx) { struct sk_prov_op_ctx *ctx = vctx; if (ctx == NULL) return; sk_debug_op_ctx(ctx, "ctx: %p", ctx); if (ctx->default_op_ctx != NULL && ctx->default_op_ctx_free != NULL) ctx->default_op_ctx_free(ctx->default_op_ctx); if (ctx->key != NULL) sk_prov_keymgmt_free(ctx->key); if (ctx->propq != NULL) OPENSSL_free((void *)ctx->propq); if (ctx->mdctx != NULL) EVP_MD_CTX_free(ctx->mdctx); if (ctx->md != NULL) EVP_MD_free(ctx->md); OPENSSL_free(ctx); } static struct sk_prov_op_ctx *sk_prov_op_dupctx(struct sk_prov_op_ctx *ctx) { struct sk_prov_op_ctx *new_ctx; if (ctx == NULL) return NULL; sk_debug_op_ctx(ctx, "ctx: %p", ctx); new_ctx = sk_prov_op_newctx(ctx->provctx, ctx->propq, ctx->type); if (new_ctx == NULL) { sk_debug_op_ctx(ctx, "ERROR: sk_prov_op_newctx failed"); return NULL; } new_ctx->operation = ctx->operation; new_ctx->default_op_ctx_free = ctx->default_op_ctx_free; new_ctx->sign_fn = ctx->sign_fn; if (ctx->mdctx != NULL) { new_ctx->mdctx = EVP_MD_CTX_new(); if (new_ctx->mdctx == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_MALLOC_FAILED, "EVP_MD_CTX_new failed"); sk_prov_op_freectx(new_ctx); return NULL; } if (!EVP_MD_CTX_copy_ex(new_ctx->mdctx, ctx->mdctx)) { sk_debug_op_ctx(ctx, "ERROR: EVP_MD_CTX_copy_ex failed"); sk_prov_op_freectx(new_ctx); return NULL; } }; if (ctx->md != NULL) { new_ctx->md = ctx->md; EVP_MD_up_ref(ctx->md); } if (ctx->key != NULL) { new_ctx->key = ctx->key; sk_prov_keymgmt_upref(ctx->key); } sk_debug_op_ctx(ctx, "new_ctx: %p", new_ctx); return new_ctx; } static int sk_prov_op_init(struct sk_prov_op_ctx *ctx, struct sk_prov_key *key, int operation) { if (ctx == NULL) return 0; sk_debug_op_ctx(ctx, "ctx: %p key: %p operation: %d", ctx, key, operation); if (key != NULL) { switch (ctx->type) { case EVP_PKEY_RSA: case EVP_PKEY_RSA_PSS: if (key->type != EVP_PKEY_RSA && key->type != EVP_PKEY_RSA_PSS) { put_error_op_ctx(ctx, SK_PROV_ERR_INTERNAL_ERROR, "key type mismatch: ctx type: " "%d key type: %d", ctx->type, key->type); return 0; } break; case EVP_PKEY_EC: if (key->type != EVP_PKEY_EC) { put_error_op_ctx(ctx, SK_PROV_ERR_INTERNAL_ERROR, "key type mismatch: ctx type: " "%d key type: %d", ctx->type, key->type); return 0; } break; default: put_error_op_ctx(ctx, SK_PROV_ERR_INTERNAL_ERROR, "key type unknown: ctx type: " "%d key type: %d", ctx->type, key->type); return 0; } } if (key != NULL) sk_prov_keymgmt_upref(key); if (ctx->key != NULL) sk_prov_keymgmt_free(ctx->key); ctx->key = key; ctx->operation = operation; return 1; } static struct sk_prov_op_ctx *sk_prov_asym_op_newctx( struct sk_prov_ctx *provctx, int pkey_type) { OSSL_FUNC_asym_cipher_freectx_fn *default_freectx_fn; OSSL_FUNC_asym_cipher_newctx_fn *default_newctx_fn; struct sk_prov_op_ctx *ctx; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "pkey_type: %d", pkey_type); default_newctx_fn = (OSSL_FUNC_asym_cipher_newctx_fn *) sk_prov_get_default_asym_func(provctx, pkey_type, OSSL_FUNC_ASYM_CIPHER_NEWCTX); if (default_newctx_fn == NULL) { put_error_ctx(provctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default newctx_fn"); return NULL; } default_freectx_fn = (OSSL_FUNC_asym_cipher_freectx_fn *) sk_prov_get_default_asym_func(provctx, pkey_type, OSSL_FUNC_ASYM_CIPHER_FREECTX); if (default_freectx_fn == NULL) { put_error_ctx(provctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default freectx_fn"); return NULL; } ctx = sk_prov_op_newctx(provctx, NULL, pkey_type); if (ctx == NULL) { sk_debug_ctx(provctx, "ERROR: sk_prov_op_newctx failed"); return NULL; } ctx->default_op_ctx = default_newctx_fn(provctx->default_provctx); if (ctx->default_op_ctx == NULL) { put_error_ctx(provctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_newctx_fn failed"); sk_prov_op_freectx(ctx); return NULL; } ctx->default_op_ctx_free = default_freectx_fn; sk_debug_ctx(provctx, "ctx: %p", ctx); return ctx; } static void *sk_prov_asym_op_dupctx(void *vctx) { OSSL_FUNC_asym_cipher_dupctx_fn *default_dupctx_fn; struct sk_prov_op_ctx *ctx = vctx; struct sk_prov_op_ctx *new_ctx; if (ctx == NULL) return NULL; sk_debug_op_ctx(ctx, "ctx: %p", ctx); default_dupctx_fn = (OSSL_FUNC_asym_cipher_dupctx_fn *) sk_prov_get_default_asym_func(ctx->provctx, ctx->type, OSSL_FUNC_ASYM_CIPHER_DUPCTX); if (default_dupctx_fn == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default dupctx_fn"); return NULL; } new_ctx = sk_prov_op_dupctx(ctx); if (new_ctx == NULL) { sk_debug_op_ctx(ctx, "ERROR: sk_prov_op_dupctx failed"); return NULL; } new_ctx->default_op_ctx = default_dupctx_fn(ctx->default_op_ctx); if (new_ctx->default_op_ctx == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_dupctx_fn failed"); sk_prov_op_freectx(new_ctx); return NULL; } sk_debug_op_ctx(ctx, "new_ctx: %p", new_ctx); return new_ctx; } static int sk_prov_asym_op_get_ctx_params(void *vctx, OSSL_PARAM params[]) { OSSL_FUNC_asym_cipher_get_ctx_params_fn *default_get_params_fn; struct sk_prov_op_ctx *ctx = vctx; const OSSL_PARAM *p; if (ctx == NULL) return 0; sk_debug_op_ctx(ctx, "ctx: %p", ctx); for (p = params; p != NULL && p->key != NULL; p++) sk_debug_op_ctx(ctx, "param: %s", p->key); default_get_params_fn = (OSSL_FUNC_asym_cipher_get_ctx_params_fn *) sk_prov_get_default_asym_func(ctx->provctx, ctx->type, OSSL_FUNC_ASYM_CIPHER_GET_CTX_PARAMS); /* default_get_params_fn is optional */ if (default_get_params_fn != NULL) { if (!default_get_params_fn(ctx->default_op_ctx, params)) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_get_params_fn failed"); return 0; } } return 1; } static int sk_prov_asym_op_set_ctx_params(void *vctx, const OSSL_PARAM params[]) { OSSL_FUNC_asym_cipher_set_ctx_params_fn *default_set_params_fn; struct sk_prov_op_ctx *ctx = vctx; const OSSL_PARAM *p; if (ctx == NULL) return 0; sk_debug_op_ctx(ctx, "ctx: %p", ctx); for (p = params; p != NULL && p->key != NULL; p++) sk_debug_op_ctx(ctx, "param: %s", p->key); #ifdef OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION /* OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION is for RSA decrypt only */ if (!sk_prov_check_uint_param(params, OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION, ctx->key, EVP_PKEY_RSA, 0) || !sk_prov_check_uint_param(params, OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION, ctx->key, EVP_PKEY_RSA_PSS, 0)) { put_error_op_ctx(ctx, SK_PROV_ERR_INVALID_PARAM, "Implicit rejection is not supported"); return 0; } #endif default_set_params_fn = (OSSL_FUNC_asym_cipher_set_ctx_params_fn *) sk_prov_get_default_asym_func(ctx->provctx, ctx->type, OSSL_FUNC_ASYM_CIPHER_SET_CTX_PARAMS); /* default_set_params_fn is optional */ if (default_set_params_fn != NULL) { if (!default_set_params_fn(ctx->default_op_ctx, params)) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_set_params_fn failed"); return 0; } } return 1; } static const OSSL_PARAM *sk_prov_asym_op_gettable_ctx_params( struct sk_prov_op_ctx *ctx, struct sk_prov_ctx *provctx, int pkey_type) { OSSL_FUNC_asym_cipher_gettable_ctx_params_fn *default_gettable_params_fn; const OSSL_PARAM *params = NULL, *p; if (ctx == NULL || provctx == NULL) return NULL; sk_debug_ctx(provctx, "pkey_type: %d", pkey_type); default_gettable_params_fn = (OSSL_FUNC_asym_cipher_gettable_ctx_params_fn *) sk_prov_get_default_asym_func(provctx, pkey_type, OSSL_FUNC_ASYM_CIPHER_GETTABLE_CTX_PARAMS); /* default_gettable_params_fn is optional */ if (default_gettable_params_fn != NULL) params = default_gettable_params_fn(ctx->default_op_ctx, provctx->default_provctx); for (p = params; p != NULL && p->key != NULL; p++) sk_debug_ctx(provctx, "param: %s", p->key); return params; } static const OSSL_PARAM *sk_prov_asym_op_settable_ctx_params( struct sk_prov_op_ctx *ctx, struct sk_prov_ctx *provctx, int pkey_type) { OSSL_FUNC_asym_cipher_settable_ctx_params_fn *default_settable_params_fn; const OSSL_PARAM *params = NULL, *p; if (ctx == NULL || provctx == NULL) return NULL; sk_debug_ctx(provctx, "pkey_type: %d", pkey_type); default_settable_params_fn = (OSSL_FUNC_asym_cipher_settable_ctx_params_fn *) sk_prov_get_default_asym_func(provctx, pkey_type, OSSL_FUNC_ASYM_CIPHER_SETTABLE_CTX_PARAMS); /* default_settable_params_fn is optional */ if (default_settable_params_fn != NULL) params = default_settable_params_fn(ctx->default_op_ctx, provctx->default_provctx); for (p = params; p != NULL && p->key != NULL; p++) sk_debug_ctx(provctx, "param: %s", p->key); return params; } static EVP_MD *sk_prov_asym_op_get_oaep_md(struct sk_prov_op_ctx *ctx) { char mdprops[256], mdname[50]; OSSL_PARAM ctx_params[] = { OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST, &mdname, sizeof(mdname)), OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST_PROPS, &mdprops, sizeof(mdprops)), OSSL_PARAM_END }; EVP_MD *md; sk_debug_op_ctx(ctx, "ctx: %p", ctx); if (!sk_prov_asym_op_get_ctx_params(ctx, ctx_params) || !OSSL_PARAM_modified(&ctx_params[0]) || !OSSL_PARAM_modified(&ctx_params[1])) { sk_debug_op_ctx(ctx, "sk_prov_asym_op_get_ctx_params failed"); if (ctx->md != NULL) { sk_debug_op_ctx(ctx, "use digest from context: %s", EVP_MD_name(ctx->md)); EVP_MD_up_ref(ctx->md); return ctx->md; } sk_debug_op_ctx(ctx, "use default"); strcpy(mdname, SK_PROV_RSA_DEFAULT_MD); strcpy(mdprops, ""); } md = EVP_MD_fetch((OSSL_LIB_CTX *)ctx->provctx->c_get_libctx( ctx->provctx->handle), mdname, mdprops[0] != '\0' ? mdprops : ctx->propq); if (md == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, "EVP_MD_fetch failed to fetch '%s' using " "property query '%s'", mdname, mdprops[0] != '\0' ? mdprops : ctx->propq != NULL ? ctx->propq : ""); return NULL; } sk_debug_op_ctx(ctx, "md: %s", EVP_MD_name(md)); return md; } static EVP_MD *sk_prov_asym_op_get_mgf_md(struct sk_prov_op_ctx *ctx) { char mdprops[256], mdname[50]; OSSL_PARAM ctx_params[] = { OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST, &mdname, sizeof(mdname)), OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST_PROPS, &mdprops, sizeof(mdprops)), OSSL_PARAM_END }; EVP_MD *md; sk_debug_op_ctx(ctx, "ctx: %p", ctx); if (!sk_prov_asym_op_get_ctx_params(ctx, ctx_params) || !OSSL_PARAM_modified(&ctx_params[0]) || !OSSL_PARAM_modified(&ctx_params[1])) { sk_debug_op_ctx(ctx, "sk_prov_asym_op_get_ctx_params failed, " "using oaep digest"); return sk_prov_asym_op_get_oaep_md(ctx); } md = EVP_MD_fetch((OSSL_LIB_CTX *)ctx->provctx->c_get_libctx( ctx->provctx->handle), mdname, mdprops[0] != '\0' ? mdprops : ctx->propq); if (md == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, "EVP_MD_fetch failed to fetch '%s' using " "property query '%s'", mdname, mdprops[0] != '\0' ? mdprops : ctx->propq != NULL ? ctx->propq : ""); return NULL; } sk_debug_op_ctx(ctx, "md: %s", EVP_MD_name(md)); return md; } static int sk_prov_asym_op_get_oaep_label(struct sk_prov_op_ctx *ctx, unsigned char **label) { OSSL_PARAM ctx_params[] = { OSSL_PARAM_octet_ptr(OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL, label, 0), OSSL_PARAM_END }; int oaep_label_len; sk_debug_op_ctx(ctx, "ctx: %p", ctx); if (!sk_prov_asym_op_get_ctx_params(ctx, ctx_params) || !OSSL_PARAM_modified(&ctx_params[0])) { put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, "sk_prov_asym_op_get_ctx_params failed to " "get OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL"); return -1; } oaep_label_len = ctx_params[0].return_size; sk_debug_op_ctx(ctx, "oaep_label: %p oaep_label_len: %d", *label, oaep_label_len); return oaep_label_len; } static int sk_prov_parse_padding(const char *padding) { if (strcmp(padding, OSSL_PKEY_RSA_PAD_MODE_NONE) == 0) return RSA_NO_PADDING; if (strcmp(padding, OSSL_PKEY_RSA_PAD_MODE_PKCSV15) == 0) return RSA_PKCS1_PADDING; if (strcmp(padding, OSSL_PKEY_RSA_PAD_MODE_OAEP) == 0) return RSA_PKCS1_OAEP_PADDING; if (strcmp(padding, OSSL_PKEY_RSA_PAD_MODE_X931) == 0) return RSA_X931_PADDING; if (strcmp(padding, OSSL_PKEY_RSA_PAD_MODE_PSS) == 0) return RSA_PKCS1_PSS_PADDING; return -1; } static int sk_prov_asym_op_get_padding(struct sk_prov_op_ctx *ctx) { char padding[50]; OSSL_PARAM ctx_params[] = { OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_PAD_MODE, &padding, sizeof(padding)), OSSL_PARAM_END }; sk_debug_op_ctx(ctx, "ctx: %p", ctx); if (!sk_prov_asym_op_get_ctx_params(ctx, ctx_params) || !OSSL_PARAM_modified(&ctx_params[0])) { put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, "sk_prov_asym_op_get_ctx_params failed to " "get OSSL_PKEY_PARAM_PAD_MODE"); return -1; } sk_debug_op_ctx(ctx, "padding: %s", padding); return sk_prov_parse_padding(padding); } static int sk_prov_asym_op_encrypt_init(void *vctx, void *vkey, const OSSL_PARAM params[]) { OSSL_FUNC_asym_cipher_encrypt_init_fn *default_encrypt_init_fn; struct sk_prov_op_ctx *ctx = vctx; struct sk_prov_key *key = vkey; const OSSL_PARAM *p; if (ctx == NULL || key == NULL) return 0; sk_debug_op_ctx(ctx, "ctx: %p key: %p", ctx, key); for (p = params; p != NULL && p->key != NULL; p++) sk_debug_op_ctx(ctx, "param: %s", p->key); default_encrypt_init_fn = (OSSL_FUNC_asym_cipher_encrypt_init_fn *) sk_prov_get_default_asym_func(ctx->provctx, ctx->type, OSSL_FUNC_ASYM_CIPHER_ENCRYPT_INIT); if (default_encrypt_init_fn == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default encrypt_init_fn"); return 0; } if (!sk_prov_op_init(ctx, key, EVP_PKEY_OP_ENCRYPT)) { sk_debug_op_ctx(ctx, "ERROR: sk_prov_op_init failed"); return 0; } if (!default_encrypt_init_fn(ctx->default_op_ctx, key->default_key, params)) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_encrypt_init_fn failed"); return 0; } return 1; } static int sk_prov_asym_op_decrypt_init(void *vctx, void *vkey, const OSSL_PARAM params[]) { OSSL_FUNC_asym_cipher_decrypt_init_fn *default_decrypt_init_fn; struct sk_prov_op_ctx *ctx = vctx; struct sk_prov_key *key = vkey; const OSSL_PARAM *p; #ifdef OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION unsigned int implicit_rejection = 0; OSSL_PARAM set_params[] = { OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION, &implicit_rejection), OSSL_PARAM_END }; #endif if (ctx == NULL || key == NULL) return 0; sk_debug_op_ctx(ctx, "ctx: %p key: %p", ctx, key); for (p = params; p != NULL && p->key != NULL; p++) sk_debug_op_ctx(ctx, "param: %s", p->key); #ifdef OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION /* OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION is for RSA decrypt only */ if (!sk_prov_check_uint_param(params, OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION, key, EVP_PKEY_RSA, 0) || !sk_prov_check_uint_param(params, OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION, key, EVP_PKEY_RSA_PSS, 0)) { put_error_op_ctx(ctx, SK_PROV_ERR_INVALID_PARAM, "Implicit rejection is not supported"); return 0; } #endif default_decrypt_init_fn = (OSSL_FUNC_asym_cipher_decrypt_init_fn *) sk_prov_get_default_asym_func(ctx->provctx, ctx->type, OSSL_FUNC_ASYM_CIPHER_DECRYPT_INIT); if (default_decrypt_init_fn == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default decrypt_init_fn"); return 0; } if (!sk_prov_op_init(ctx, key, EVP_PKEY_OP_DECRYPT)) { sk_debug_op_ctx(ctx, "ERROR: sk_prov_op_init failed"); return 0; } if (!default_decrypt_init_fn(ctx->default_op_ctx, key->default_key, params)) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_decrypt_init_fn failed"); return 0; } #ifdef OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION if ((key->type == EVP_PKEY_RSA || key->type == EVP_PKEY_RSA_PSS) && key->secure_key != NULL) { /* * By default, implicit rejection is enabled for the default * provider. We currently do not support implicit rejection with * secure keys, so disable implicit rejection in the default * provider operation context to report its status correctly * with get-params. */ implicit_rejection = 0; if (!sk_prov_asym_op_set_ctx_params(ctx, set_params)) { sk_debug_op_ctx(ctx, "ERROR: sk_prov_asym_op_set_ctx_params " "failed"); return 0; } } #endif return 1; } static int sk_prov_asym_op_encrypt(void *vctx, unsigned char *out, size_t *outlen, size_t outsize, const unsigned char *in, size_t inlen) { OSSL_FUNC_asym_cipher_encrypt_fn *default_encrypt_fn; struct sk_prov_op_ctx *ctx = vctx; if (ctx == NULL || in == NULL || outlen == NULL) return 0; sk_debug_op_ctx(ctx, "ctx: %p key: %p inlen: %lu outsize: %lu", ctx, ctx->key, inlen, outsize); default_encrypt_fn = (OSSL_FUNC_asym_cipher_encrypt_fn *) sk_prov_get_default_asym_func(ctx->provctx, ctx->type, OSSL_FUNC_ASYM_CIPHER_ENCRYPT); if (default_encrypt_fn == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default encrypt_fn"); return 0; } if (!default_encrypt_fn(ctx->default_op_ctx, out, outlen, outsize, in, inlen)) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_encrypt_fn failed"); return 0; } sk_debug_op_ctx(ctx, "outlen: %lu", *outlen); return 1; } static int sk_prov_asym_op_decrypt(struct sk_prov_op_ctx *ctx, unsigned char *out, size_t *outlen, size_t outsize, const unsigned char *in, size_t inlen) { OSSL_FUNC_asym_cipher_decrypt_fn *default_decrypt_fn; if (ctx == NULL || in == NULL || outlen == NULL) return 0; sk_debug_op_ctx(ctx, "ctx: %p key: %p inlen: %lu outsize: %lu", ctx, ctx->key, inlen, outsize); default_decrypt_fn = (OSSL_FUNC_asym_cipher_decrypt_fn *) sk_prov_get_default_asym_func(ctx->provctx, ctx->type, OSSL_FUNC_ASYM_CIPHER_DECRYPT); if (default_decrypt_fn == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default decrypt_fn"); return 0; } if (!default_decrypt_fn(ctx->default_op_ctx, out, outlen, outsize, in, inlen)) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_decrypt_fn failed"); return 0; } sk_debug_op_ctx(ctx, "outlen: %lu", *outlen); return 1; } static struct sk_prov_op_ctx *sk_prov_sign_op_newctx( struct sk_prov_ctx *provctx, const char *propq, int pkey_type) { OSSL_FUNC_signature_freectx_fn *default_freectx_fn; OSSL_FUNC_signature_newctx_fn *default_newctx_fn; struct sk_prov_op_ctx *ctx; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "propq: %s pkey_type: %d", propq != NULL ? propq : "", pkey_type); default_newctx_fn = (OSSL_FUNC_signature_newctx_fn *) sk_prov_get_default_sign_func(provctx, pkey_type, OSSL_FUNC_SIGNATURE_NEWCTX); if (default_newctx_fn == NULL) { put_error_ctx(provctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default newctx_fn"); return NULL; } default_freectx_fn = (OSSL_FUNC_signature_freectx_fn *) sk_prov_get_default_sign_func(provctx, pkey_type, OSSL_FUNC_SIGNATURE_FREECTX); if (default_freectx_fn == NULL) { put_error_ctx(provctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default freectx_fn"); return NULL; } ctx = sk_prov_op_newctx(provctx, propq, pkey_type); if (ctx == NULL) { sk_debug_ctx(provctx, "ERROR: sk_prov_op_newctx failed"); return NULL; } ctx->default_op_ctx = default_newctx_fn(provctx->default_provctx, propq); if (ctx->default_op_ctx == NULL) { put_error_ctx(provctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_newctx_fn failed"); sk_prov_op_freectx(ctx); return NULL; } ctx->default_op_ctx_free = default_freectx_fn; sk_debug_ctx(provctx, "ctx: %p", ctx); return ctx; } static void *sk_prov_sign_op_dupctx(void *vctx) { OSSL_FUNC_signature_dupctx_fn *default_dupctx_fn; struct sk_prov_op_ctx *ctx = vctx; struct sk_prov_op_ctx *new_ctx; if (ctx == NULL) return NULL; sk_debug_op_ctx(ctx, "ctx: %p", ctx); default_dupctx_fn = (OSSL_FUNC_signature_dupctx_fn *) sk_prov_get_default_sign_func(ctx->provctx, ctx->type, OSSL_FUNC_SIGNATURE_DUPCTX); if (default_dupctx_fn == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default dupctx_fn"); return NULL; } new_ctx = sk_prov_op_dupctx(ctx); if (new_ctx == NULL) { sk_debug_op_ctx(ctx, "ERROR: sk_prov_op_dupctx failed"); return NULL; } new_ctx->default_op_ctx = default_dupctx_fn(ctx->default_op_ctx); if (new_ctx->default_op_ctx == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_dupctx_fn failed"); sk_prov_op_freectx(new_ctx); return NULL; } sk_debug_op_ctx(ctx, "new_ctx: %p", new_ctx); return new_ctx; } static int sk_prov_sign_op_get_ctx_params(void *vctx, OSSL_PARAM params[]) { OSSL_FUNC_signature_get_ctx_params_fn *default_get_params_fn; struct sk_prov_op_ctx *ctx = vctx; const OSSL_PARAM *p; if (ctx == NULL) return 0; sk_debug_op_ctx(ctx, "ctx: %p", ctx); for (p = params; p != NULL && p->key != NULL; p++) sk_debug_op_ctx(ctx, "param: %s", p->key); default_get_params_fn = (OSSL_FUNC_signature_get_ctx_params_fn *) sk_prov_get_default_sign_func(ctx->provctx, ctx->type, OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS); /* default_get_params_fn is optional */ if (default_get_params_fn != NULL) { if (!default_get_params_fn(ctx->default_op_ctx, params)) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_get_params_fn failed"); return 0; } } return 1; } static int sk_prov_sign_op_set_ctx_params(void *vctx, const OSSL_PARAM params[]) { OSSL_FUNC_signature_set_ctx_params_fn *default_set_params_fn; struct sk_prov_op_ctx *ctx = vctx; const OSSL_PARAM *p; if (ctx == NULL) return 0; sk_debug_op_ctx(ctx, "ctx: %p", ctx); for (p = params; p != NULL && p->key != NULL; p++) sk_debug_op_ctx(ctx, "param: %s", p->key); #ifdef OSSL_SIGNATURE_PARAM_NONCE_TYPE /* OSSL_SIGNATURE_PARAM_NONCE_TYPE is used for EC sign ops only */ if (!sk_prov_check_uint_param(params, OSSL_SIGNATURE_PARAM_NONCE_TYPE, ctx->key, EVP_PKEY_EC, 0)) { put_error_op_ctx(ctx, SK_PROV_ERR_INVALID_PARAM, "Deterministic signature is not supported"); return 0; } #endif default_set_params_fn = (OSSL_FUNC_signature_set_ctx_params_fn *) sk_prov_get_default_sign_func(ctx->provctx, ctx->type, OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS); /* default_set_params_fn is optional */ if (default_set_params_fn != NULL) { if (!default_set_params_fn(ctx->default_op_ctx, params)) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_set_params_fn failed"); return 0; } } return 1; } static const OSSL_PARAM *sk_prov_sign_op_gettable_ctx_params( struct sk_prov_op_ctx *ctx, struct sk_prov_ctx *provctx, int pkey_type) { OSSL_FUNC_signature_gettable_ctx_params_fn *default_gettable_params_fn; const OSSL_PARAM *params = NULL, *p; if (ctx == NULL || provctx == NULL) return NULL; sk_debug_ctx(provctx, "pkey_type: %d", pkey_type); default_gettable_params_fn = (OSSL_FUNC_signature_gettable_ctx_params_fn *) sk_prov_get_default_sign_func(provctx, pkey_type, OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS); /* default_gettable_params_fn is optional */ if (default_gettable_params_fn != NULL) params = default_gettable_params_fn(ctx->default_op_ctx, provctx->default_provctx); for (p = params; p != NULL && p->key != NULL; p++) sk_debug_ctx(provctx, "param: %s", p->key); return params; } static const OSSL_PARAM *sk_prov_sign_op_settable_ctx_params( struct sk_prov_op_ctx *ctx, struct sk_prov_ctx *provctx, int pkey_type) { OSSL_FUNC_signature_settable_ctx_params_fn *default_settable_params_fn; const OSSL_PARAM *params = NULL, *p; if (ctx == NULL || provctx == NULL) return NULL; sk_debug_ctx(provctx, "pkey_type: %d", pkey_type); default_settable_params_fn = (OSSL_FUNC_signature_settable_ctx_params_fn *) sk_prov_get_default_sign_func(provctx, pkey_type, OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS); /* default_settable_params_fn is optional */ if (default_settable_params_fn != NULL) params = default_settable_params_fn(ctx->default_op_ctx, provctx->default_provctx); for (p = params; p != NULL && p->key != NULL; p++) sk_debug_ctx(provctx, "param: %s", p->key); return params; } static int sk_prov_sign_op_get_ctx_md_params(void *vctx, OSSL_PARAM params[]) { OSSL_FUNC_signature_get_ctx_md_params_fn *default_get_md_params_fn; struct sk_prov_op_ctx *ctx = vctx; const OSSL_PARAM *p; if (ctx == NULL) return 0; sk_debug_op_ctx(ctx, "ctx: %p", ctx); for (p = params; p != NULL && p->key != NULL; p++) sk_debug_op_ctx(ctx, "param: %s", p->key); default_get_md_params_fn = (OSSL_FUNC_signature_get_ctx_md_params_fn *) sk_prov_get_default_sign_func(ctx->provctx, ctx->type, OSSL_FUNC_SIGNATURE_GET_CTX_MD_PARAMS); /* default_get_md_params_fn is optional */ if (default_get_md_params_fn != NULL) { if (!default_get_md_params_fn(ctx->default_op_ctx, params)) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_get_md_params_fn failed"); return 0; } } return 1; } static int sk_prov_sign_op_set_ctx_md_params(void *vctx, const OSSL_PARAM params[]) { OSSL_FUNC_signature_set_ctx_md_params_fn *default_set_md_params_fn; struct sk_prov_op_ctx *ctx = vctx; const OSSL_PARAM *p; if (ctx == NULL) return 0; sk_debug_op_ctx(ctx, "ctx: %p", ctx); for (p = params; p != NULL && p->key != NULL; p++) sk_debug_op_ctx(ctx, "param: %s", p->key); default_set_md_params_fn = (OSSL_FUNC_signature_set_ctx_md_params_fn *) sk_prov_get_default_sign_func(ctx->provctx, ctx->type, OSSL_FUNC_SIGNATURE_SET_CTX_MD_PARAMS); /* default_set_md_params_fn is optional */ if (default_set_md_params_fn != NULL) { if (!default_set_md_params_fn(ctx->default_op_ctx, params)) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_set_md_params_fn failed"); return 0; } } /* Also set parameters in own MD context */ if (ctx->mdctx) return EVP_MD_CTX_set_params(ctx->mdctx, params); return 1; } static const OSSL_PARAM *sk_prov_sign_op_gettable_ctx_md_params( struct sk_prov_op_ctx *ctx, int pkey_type) { OSSL_FUNC_signature_gettable_ctx_md_params_fn *default_gettable_md_params_fn; const OSSL_PARAM *params = NULL, *p; if (ctx == NULL) return NULL; sk_debug_op_ctx(ctx, "pkey_type: %d", pkey_type); default_gettable_md_params_fn = (OSSL_FUNC_signature_gettable_ctx_md_params_fn *) sk_prov_get_default_sign_func(ctx->provctx, pkey_type, OSSL_FUNC_SIGNATURE_GETTABLE_CTX_MD_PARAMS); /* default_gettable_params_fn is optional */ if (default_gettable_md_params_fn != NULL) params = default_gettable_md_params_fn(ctx->default_op_ctx); for (p = params; p != NULL && p->key != NULL; p++) sk_debug_op_ctx(ctx, "param: %s", p->key); return params; } static const OSSL_PARAM *sk_prov_sign_op_settable_ctx_md_params( struct sk_prov_op_ctx *ctx, int pkey_type) { OSSL_FUNC_signature_settable_ctx_md_params_fn *default_settable_md_params_fn; const OSSL_PARAM *params = NULL, *p; if (ctx == NULL) return NULL; sk_debug_op_ctx(ctx, "pkey_type: %d", pkey_type); default_settable_md_params_fn = (OSSL_FUNC_signature_settable_ctx_md_params_fn *) sk_prov_get_default_sign_func(ctx->provctx, pkey_type, OSSL_FUNC_SIGNATURE_SETTABLE_CTX_MD_PARAMS); /* default_settable_md_params_fn is optional */ if (default_settable_md_params_fn != NULL) params = default_settable_md_params_fn(ctx->default_op_ctx); for (p = params; p != NULL && p->key != NULL; p++) sk_debug_op_ctx(ctx, "param: %s", p->key); return params; } static EVP_MD *sk_prov_sign_op_get_md(struct sk_prov_op_ctx *ctx) { char mdprops[256], mdname[50]; OSSL_PARAM ctx_params[] = { OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, &mdname, sizeof(mdname)), OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PROPERTIES, &mdprops, sizeof(mdprops)), OSSL_PARAM_END }; EVP_MD *md; sk_debug_op_ctx(ctx, "ctx: %p", ctx); if (!sk_prov_sign_op_get_ctx_params(ctx, ctx_params) || !OSSL_PARAM_modified(&ctx_params[0]) || !OSSL_PARAM_modified(&ctx_params[1])) { sk_debug_op_ctx(ctx, "sk_prov_sign_op_get_ctx_params failed"); if (ctx->md != NULL) { sk_debug_op_ctx(ctx, "use digest from context: %s", EVP_MD_name(ctx->md)); EVP_MD_up_ref(ctx->md); return ctx->md; } sk_debug_op_ctx(ctx, "use default"); strcpy(mdname, SK_PROV_RSA_DEFAULT_MD); strcpy(mdprops, ""); } md = EVP_MD_fetch((OSSL_LIB_CTX *)ctx->provctx->c_get_libctx( ctx->provctx->handle), mdname, mdprops[0] != '\0' ? mdprops : ctx->propq); if (md == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, "EVP_MD_fetch failed to fetch '%s' using " "property query '%s'", mdname, mdprops[0] != '\0' ? mdprops : ctx->propq != NULL ? ctx->propq : ""); return NULL; } sk_debug_op_ctx(ctx, "md: %s", EVP_MD_name(md)); return md; } static EVP_MD *sk_prov_sign_op_get_mgf_md(struct sk_prov_op_ctx *ctx) { char mdprops[256], mdname[50]; OSSL_PARAM ctx_params[] = { OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_MGF1_DIGEST, &mdname, sizeof(mdname)), OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_MGF1_PROPERTIES, &mdprops, sizeof(mdprops)), OSSL_PARAM_END }; EVP_MD *md; sk_debug_op_ctx(ctx, "ctx: %p", ctx); if (!sk_prov_sign_op_get_ctx_params(ctx, ctx_params) || !OSSL_PARAM_modified(&ctx_params[0]) || !OSSL_PARAM_modified(&ctx_params[1])) { sk_debug_op_ctx(ctx, "sk_prov_sign_op_get_ctx_params failed, " "using signature digest"); return sk_prov_sign_op_get_md(ctx); } md = EVP_MD_fetch((OSSL_LIB_CTX *)ctx->provctx->c_get_libctx( ctx->provctx->handle), mdname, mdprops[0] != '\0' ? mdprops : ctx->propq); if (md == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, "EVP_MD_fetch failed to fetch '%s' using " "property query '%s'", mdname, mdprops[0] != '\0' ? mdprops : ctx->propq != NULL ? ctx->propq : ""); return NULL; } sk_debug_op_ctx(ctx, "md: %s", EVP_MD_name(md)); return md; } static int sk_prov_sign_op_get_padding(struct sk_prov_op_ctx *ctx) { char padding[50]; OSSL_PARAM ctx_params[] = { OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_PAD_MODE, &padding, sizeof(padding)), OSSL_PARAM_END }; sk_debug_op_ctx(ctx, "ctx: %p", ctx); if (!sk_prov_sign_op_get_ctx_params(ctx, ctx_params) || !OSSL_PARAM_modified(&ctx_params[0])) { put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, "sk_prov_sign_op_get_ctx_params failed to " "get OSSL_PKEY_PARAM_PAD_MODE"); return -1; } sk_debug_op_ctx(ctx, "padding: %s", padding); return sk_prov_parse_padding(padding); } static int sk_prov_sign_op_get_pss_saltlen(struct sk_prov_op_ctx *ctx, struct sk_prov_key *key, EVP_MD *mgf_md) { char saltlen[50]; OSSL_PARAM ctx_params[] = { OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PSS_SALTLEN, &saltlen, sizeof(saltlen)), OSSL_PARAM_END }; int salt_len, rsa_bits, max_saltlen; sk_debug_op_ctx(ctx, "ctx: %p", ctx); if (!sk_prov_sign_op_get_ctx_params(ctx, ctx_params) || !OSSL_PARAM_modified(&ctx_params[0])) { put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, "sk_prov_sign_op_get_ctx_params failed to " "get OSSL_SIGNATURE_PARAM_PSS_SALTLEN"); return -1; } sk_debug_op_ctx(ctx, "saltlen: %s", saltlen); rsa_bits = sk_prov_keymgmt_get_bits(key); if (rsa_bits <= 0) { sk_debug_op_ctx(ctx, "ERROR: sk_prov_keymgmt_get_bits failed"); return -1; } max_saltlen = rsa_bits / 8 - EVP_MD_size(mgf_md) - 2; if (strcmp(saltlen, OSSL_PKEY_RSA_PSS_SALT_LEN_DIGEST) == 0) salt_len = EVP_MD_size(mgf_md); else if (strcmp(saltlen, OSSL_PKEY_RSA_PSS_SALT_LEN_MAX) == 0) salt_len = max_saltlen; else if (strcmp(saltlen, OSSL_PKEY_RSA_PSS_SALT_LEN_AUTO) == 0) salt_len = max_saltlen; #ifdef OSSL_PKEY_RSA_PSS_SALT_LEN_AUTO_DIGEST_MAX else if (strcmp(saltlen, OSSL_PKEY_RSA_PSS_SALT_LEN_AUTO_DIGEST_MAX) == 0) salt_len = MIN(max_saltlen, EVP_MD_size(mgf_md)); #endif else salt_len = atoi(saltlen); if (salt_len > max_saltlen || salt_len < 0) { put_error_op_ctx(ctx, SK_PROV_ERR_INVALID_SALTLEN, "invalid salt len: %d", saltlen); return -1; } sk_debug_op_ctx(ctx, "salt_len: %d", salt_len); return salt_len; } static int sk_prov_sign_op_sign_init(void *vctx, void *vkey, const OSSL_PARAM params[]) { OSSL_FUNC_signature_sign_init_fn *default_sign_init_fn; struct sk_prov_op_ctx *ctx = vctx; struct sk_prov_key *key = vkey; const OSSL_PARAM *p; if (ctx == NULL || key == NULL) return 0; sk_debug_op_ctx(ctx, "ctx: %p key: %p", ctx, key); for (p = params; p != NULL && p->key != NULL; p++) sk_debug_op_ctx(ctx, "param: %s", p->key); #ifdef OSSL_SIGNATURE_PARAM_NONCE_TYPE /* OSSL_SIGNATURE_PARAM_NONCE_TYPE is used for EC sign ops only */ if (!sk_prov_check_uint_param(params, OSSL_SIGNATURE_PARAM_NONCE_TYPE, key, EVP_PKEY_EC, 0)) { put_error_op_ctx(ctx, SK_PROV_ERR_INVALID_PARAM, "Deterministic signature is not supported"); return 0; } #endif default_sign_init_fn = (OSSL_FUNC_signature_sign_init_fn *) sk_prov_get_default_sign_func(ctx->provctx, ctx->type, OSSL_FUNC_SIGNATURE_SIGN_INIT); if (default_sign_init_fn == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default sign_init_fn"); return 0; } if (!sk_prov_op_init(ctx, key, EVP_PKEY_OP_SIGN)) { sk_debug_op_ctx(ctx, "ERROR: sk_prov_op_init failed"); return 0; } if (!default_sign_init_fn(ctx->default_op_ctx, key->default_key, params)) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_sign_init_fn failed"); return 0; } return 1; } static int sk_prov_sign_op_verify_init(void *vctx, void *vkey, const OSSL_PARAM params[]) { OSSL_FUNC_signature_verify_init_fn *default_verify_init_fn; struct sk_prov_op_ctx *ctx = vctx; struct sk_prov_key *key = vkey; const OSSL_PARAM *p; if (ctx == NULL || key == NULL) return 0; sk_debug_op_ctx(ctx, "ctx: %p key: %p", ctx, key); for (p = params; p != NULL && p->key != NULL; p++) sk_debug_op_ctx(ctx, "param: %s", p->key); default_verify_init_fn = (OSSL_FUNC_signature_verify_init_fn *) sk_prov_get_default_sign_func(ctx->provctx, ctx->type, OSSL_FUNC_SIGNATURE_VERIFY_INIT); if (default_verify_init_fn == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default verify_init_fn"); return 0; } if (!sk_prov_op_init(ctx, key, EVP_PKEY_OP_VERIFY)) { sk_debug_op_ctx(ctx, "ERROR: sk_prov_op_init failed"); return 0; } if (!default_verify_init_fn(ctx->default_op_ctx, key->default_key, params)) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_verify_init_fn failed"); return 0; } return 1; } static int sk_prov_sign_op_verify_recover_init(void *vctx, void *vkey, const OSSL_PARAM params[]) { OSSL_FUNC_signature_verify_recover_init_fn *default_verify_recover_init_fn; struct sk_prov_op_ctx *ctx = vctx; struct sk_prov_key *key = vkey; const OSSL_PARAM *p; if (ctx == NULL || key == NULL) return 0; sk_debug_op_ctx(ctx, "ctx: %p key: %p", ctx, key); for (p = params; p != NULL && p->key != NULL; p++) sk_debug_op_ctx(ctx, "param: %s", p->key); default_verify_recover_init_fn = (OSSL_FUNC_signature_verify_recover_init_fn *) sk_prov_get_default_sign_func(ctx->provctx, ctx->type, OSSL_FUNC_SIGNATURE_VERIFY_RECOVER_INIT); if (default_verify_recover_init_fn == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default verify_recover_init_fn"); return 0; } if (!sk_prov_op_init(ctx, key, EVP_PKEY_OP_VERIFYRECOVER)) { sk_debug_op_ctx(ctx, "ERROR: sk_prov_op_init failed"); return 0; } if (!default_verify_recover_init_fn(ctx->default_op_ctx, key->default_key, params)) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_verify_recover_init_fn failed"); return 0; } return 1; } static int sk_prov_sign_op_sign(struct sk_prov_op_ctx *ctx, unsigned char *sig, size_t *siglen, size_t sigsize, const unsigned char *tbs, size_t tbslen) { OSSL_FUNC_signature_sign_fn *default_sign_fn; if (ctx == NULL || tbs == NULL || siglen == NULL) return 0; sk_debug_op_ctx(ctx, "ctx: %p key: %p tbslen: %lu sigsize: %lu", ctx, ctx->key, tbslen, sigsize); default_sign_fn = (OSSL_FUNC_signature_sign_fn *) sk_prov_get_default_sign_func(ctx->provctx, ctx->type, OSSL_FUNC_SIGNATURE_SIGN); if (default_sign_fn == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default sign_fn"); return 0; } if (!default_sign_fn(ctx->default_op_ctx, sig, siglen, sigsize, tbs, tbslen)) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_sign_fn failed"); return 0; } sk_debug_op_ctx(ctx, "siglen: %lu", *siglen); return 1; } static int sk_prov_sign_op_verify(void *vctx, const unsigned char *sig, size_t siglen, const unsigned char *tbs, size_t tbslen) { OSSL_FUNC_signature_verify_fn *default_verify_fn; struct sk_prov_op_ctx *ctx = vctx; if (ctx == NULL || tbs == NULL || sig == NULL) return 0; sk_debug_op_ctx(ctx, "ctx: %p key: %p tbslen: %lu siglen: %lu", ctx, ctx->key, tbslen, siglen); default_verify_fn = (OSSL_FUNC_signature_verify_fn *) sk_prov_get_default_sign_func(ctx->provctx, ctx->type, OSSL_FUNC_SIGNATURE_VERIFY); if (default_verify_fn == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default verify_fn"); return 0; } if (!default_verify_fn(ctx->default_op_ctx, sig, siglen, tbs, tbslen)) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_verify_fn failed"); return 0; } return 1; } static int sk_prov_sign_op_verify_recover(void *vctx, unsigned char *rout, size_t *routlen, size_t routsize, const unsigned char *sig, size_t siglen) { OSSL_FUNC_signature_verify_recover_fn *default_verify_recover_fn; struct sk_prov_op_ctx *ctx = vctx; if (ctx == NULL || routlen == NULL || sig == NULL) return 0; sk_debug_op_ctx(ctx, "ctx: %p key: %p routsize: %lu siglen: %lu", ctx, ctx->key, routsize, siglen); default_verify_recover_fn = (OSSL_FUNC_signature_verify_recover_fn *) sk_prov_get_default_sign_func(ctx->provctx, ctx->type, OSSL_FUNC_SIGNATURE_VERIFY_RECOVER); if (default_verify_recover_fn == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default verify_recover_fn"); return 0; } if (!default_verify_recover_fn(ctx->default_op_ctx, rout, routlen, routsize, sig, siglen)) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_verify_recover_fn failed"); return 0; } sk_debug_op_ctx(ctx, "routlen: %lu", *routlen); return 1; } static int sk_prov_sign_op_digest_sign_init(struct sk_prov_op_ctx *ctx, const char *mdname, struct sk_prov_key *key, const OSSL_PARAM params[], OSSL_FUNC_signature_sign_fn *sign_fn) { OSSL_FUNC_signature_digest_sign_init_fn *default_digest_sign_init_fn; const OSSL_PARAM *p; if (ctx == NULL || key == NULL || sign_fn == NULL) return 0; sk_debug_op_ctx(ctx, "ctx: %p mdname: %s key: %p", ctx, mdname != NULL ? mdname : "", key); for (p = params; p != NULL && p->key != NULL; p++) sk_debug_op_ctx(ctx, "param: %s", p->key); #ifdef OSSL_SIGNATURE_PARAM_NONCE_TYPE /* OSSL_SIGNATURE_PARAM_NONCE_TYPE is used for EC sign ops only */ if (!sk_prov_check_uint_param(params, OSSL_SIGNATURE_PARAM_NONCE_TYPE, key, EVP_PKEY_EC, 0)) { put_error_op_ctx(ctx, SK_PROV_ERR_INVALID_PARAM, "Deterministic signature is not supported"); return 0; } #endif default_digest_sign_init_fn = (OSSL_FUNC_signature_digest_sign_init_fn *) sk_prov_get_default_sign_func(ctx->provctx, ctx->type, OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT); if (default_digest_sign_init_fn == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default digest_sign_init_fn"); return 0; } if (!sk_prov_op_init(ctx, key, EVP_PKEY_OP_SIGN)) { sk_debug_op_ctx(ctx, "ERROR: sk_prov_op_init failed"); return 0; } if (!default_digest_sign_init_fn(ctx->default_op_ctx, mdname, key->default_key, params)) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_digest_sign_init_fn failed"); return 0; } /* For clear key, the default provider has already handled it */ if (ctx->key->secure_key == NULL) return 1; ctx->sign_fn = sign_fn; if (ctx->mdctx != NULL) EVP_MD_CTX_free(ctx->mdctx); ctx->mdctx = EVP_MD_CTX_new(); if (ctx->mdctx == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_MALLOC_FAILED, "EVP_MD_CTX_new failed"); return 0; } if (ctx->md != NULL) EVP_MD_free(ctx->md); if (mdname != NULL) ctx->md = EVP_MD_fetch( (OSSL_LIB_CTX *)ctx->provctx->c_get_libctx( ctx->provctx->handle), mdname, ctx->propq); else ctx->md = sk_prov_sign_op_get_md(ctx); if (ctx->md == NULL) { sk_debug_op_ctx(ctx, "ERROR: Failed to get digest sign digest"); EVP_MD_CTX_free(ctx->mdctx); ctx->mdctx = NULL; return 0; } return EVP_DigestInit_ex2(ctx->mdctx, ctx->md, params); } static int sk_prov_sign_op_digest_sign_update(void *vctx, const unsigned char *data, size_t datalen) { OSSL_FUNC_signature_digest_sign_update_fn *default_digest_sign_update_fn; struct sk_prov_op_ctx *ctx = vctx; if (ctx == NULL) return 0; sk_debug_op_ctx(ctx, "ctx: %p key: %p datalen: %lu", ctx, ctx->key, datalen); if (ctx->key == NULL || ctx->operation != EVP_PKEY_OP_SIGN) { put_error_op_ctx(ctx, SK_PROV_ERR_OPRATION_NOT_INITIALIZED, "digest sign operation not initialized"); return 0; } /* For secure key, don't pass it to the default provider */ if (ctx->key->secure_key != NULL) goto secure_key; default_digest_sign_update_fn = (OSSL_FUNC_signature_digest_sign_update_fn *) sk_prov_get_default_sign_func(ctx->provctx, ctx->type, OSSL_FUNC_SIGNATURE_DIGEST_SIGN_UPDATE); if (default_digest_sign_update_fn == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default digest_sign_update_fn"); return 0; } if (!default_digest_sign_update_fn(ctx->default_op_ctx, data, datalen)) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_digest_sign_update_fn failed"); return 0; } return 1; secure_key: if (ctx->mdctx == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_OPRATION_NOT_INITIALIZED, "digest sign operation not initialized"); return 0; } return EVP_DigestUpdate(ctx->mdctx, data, datalen); } static int sk_prov_sign_op_digest_sign_final(void *vctx, unsigned char *sig, size_t *siglen, size_t sigsize) { OSSL_FUNC_signature_digest_sign_final_fn *default_digest_sign_final_fn; unsigned char digest[EVP_MAX_MD_SIZE]; struct sk_prov_op_ctx *ctx = vctx; unsigned int dlen = 0; if (ctx == NULL || siglen == NULL) return 0; sk_debug_op_ctx(ctx, "ctx: %p key: %p sigsize: %lu", ctx, ctx->key, sigsize); if (ctx->key == NULL || ctx->operation != EVP_PKEY_OP_SIGN) { put_error_op_ctx(ctx, SK_PROV_ERR_OPRATION_NOT_INITIALIZED, "digest sign operation not initialized"); return 0; } /* For secure key, don't pass it to the default provider */ if (ctx->key->secure_key != NULL) goto secure_key; default_digest_sign_final_fn = (OSSL_FUNC_signature_digest_sign_final_fn *) sk_prov_get_default_sign_func(ctx->provctx, ctx->type, OSSL_FUNC_SIGNATURE_DIGEST_SIGN_FINAL); if (default_digest_sign_final_fn == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default digest_sign_final_fn"); return 0; } if (!default_digest_sign_final_fn(ctx->default_op_ctx, sig, siglen, sigsize)) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_digest_sign_final_fn failed"); return 0; } sk_debug_op_ctx(ctx, "siglen: %lu", *siglen); return 1; secure_key: if (ctx->mdctx == NULL || ctx->sign_fn == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_OPRATION_NOT_INITIALIZED, "digest sign operation not initialized"); return 0; } if (sig != NULL) { if (!EVP_DigestFinal_ex(ctx->mdctx, digest, &dlen)) { sk_debug_op_ctx(ctx, "ERROR: EVP_DigestFinal_ex failed"); return 0; } } if (!ctx->sign_fn(ctx, sig, siglen, sigsize, digest, (size_t)dlen)) { sk_debug_op_ctx(ctx, "ERROR: sign_fn failed"); return 0; } sk_debug_op_ctx(ctx, "siglen: %lu", *siglen); return 1; } static int sk_prov_sign_op_digest_verify_init(void *vctx, const char *mdname, void *vkey, const OSSL_PARAM params[]) { OSSL_FUNC_signature_digest_verify_init_fn *default_digest_verify_init_fn; struct sk_prov_op_ctx *ctx = vctx; struct sk_prov_key *key = vkey; const OSSL_PARAM *p; if (ctx == NULL || key == NULL) return 0; sk_debug_op_ctx(ctx, "ctx: %p mdname: %s key: %p", ctx, mdname != NULL ? mdname : "", key); for (p = params; p != NULL && p->key != NULL; p++) sk_debug_op_ctx(ctx, "param: %s", p->key); default_digest_verify_init_fn = (OSSL_FUNC_signature_digest_verify_init_fn *) sk_prov_get_default_sign_func(ctx->provctx, ctx->type, OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_INIT); if (default_digest_verify_init_fn == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default digest_verify_init_fn"); return 0; } if (!sk_prov_op_init(ctx, key, EVP_PKEY_OP_VERIFY)) { sk_debug_op_ctx(ctx, "ERROR: sk_prov_op_init failed"); return 0; } if (!default_digest_verify_init_fn(ctx->default_op_ctx, mdname, key->default_key, params)) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_digest_verify_init_fn failed"); return 0; } return 1; } static int sk_prov_sign_op_digest_verify_update(void *vctx, const unsigned char *data, size_t datalen) { OSSL_FUNC_signature_digest_verify_update_fn *default_digest_verify_update_fn; struct sk_prov_op_ctx *ctx = vctx; if (ctx == NULL) return 0; sk_debug_op_ctx(ctx, "ctx: %p key: %p datalen: %lu", ctx, ctx->key, datalen); default_digest_verify_update_fn = (OSSL_FUNC_signature_digest_verify_update_fn *) sk_prov_get_default_sign_func(ctx->provctx, ctx->type, OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_UPDATE); if (default_digest_verify_update_fn == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default digest_verify_update_fn"); return 0; } if (!default_digest_verify_update_fn(ctx->default_op_ctx, data, datalen)) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_digest_verify_update_fn failed"); return 0; } return 1; } static int sk_prov_sign_op_digest_verify_final(void *vctx, const unsigned char *sig, size_t siglen) { OSSL_FUNC_signature_digest_verify_final_fn *default_digest_verify_final_fn; struct sk_prov_op_ctx *ctx = vctx; if (ctx == NULL || sig == NULL) return 0; sk_debug_op_ctx(ctx, "ctx: %p key: %p siglen: %lu", ctx, ctx->key, siglen); default_digest_verify_final_fn = (OSSL_FUNC_signature_digest_verify_final_fn *) sk_prov_get_default_sign_func(ctx->provctx, ctx->type, OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_FINAL); if (default_digest_verify_final_fn == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default digest_verify_final_fn"); return 0; } if (!default_digest_verify_final_fn(ctx->default_op_ctx, sig, siglen)) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_digest_verify_final_fn failed"); return 0; } return 1; } static void sk_prov_keymgmt_upref(struct sk_prov_key *key) { sk_debug_key(key, "key: %p", key); key->ref_count++; sk_debug_key(key, "ref_count: %u", key->ref_count); } static unsigned int sk_prov_keymgmt_downref(struct sk_prov_key *key) { sk_debug_key(key, "key: %p ", key); if (key->ref_count > 0) key->ref_count--; sk_debug_key(key, "ref_count: %u", key->ref_count); return key->ref_count; } static struct sk_prov_key *sk_prov_keymgmt_new(struct sk_prov_ctx *provctx, int type) { OSSL_FUNC_keymgmt_new_fn *default_new_fn; struct sk_prov_key *key; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "provctx: %p type: %d", provctx, type); default_new_fn = (OSSL_FUNC_keymgmt_new_fn *) sk_prov_get_default_keymgmt_func(provctx, type, OSSL_FUNC_KEYMGMT_NEW); if (default_new_fn == NULL) { put_error_ctx(provctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default new_fn"); return NULL; } key = OPENSSL_zalloc(sizeof(struct sk_prov_key)); if (key == NULL) { put_error_ctx(provctx, SK_PROV_ERR_MALLOC_FAILED, "OPENSSL_zalloc failed"); return NULL; } key->provctx = provctx; key->type = type; key->default_key = default_new_fn(provctx->default_provctx); if (key->default_key == NULL) { put_error_ctx(provctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_new_fn failed"); OPENSSL_free(key); return NULL; } sk_prov_keymgmt_upref(key); sk_debug_ctx(provctx, "key: %p", key); return key; } static void sk_prov_keymgmt_free(void *vkey) { OSSL_FUNC_keymgmt_free_fn *default_free_fn; struct sk_prov_key *key = vkey; if (key == NULL) return; sk_debug_key(key, "key: %p", key); if (sk_prov_keymgmt_downref(key) > 0) return; sk_debug_key(key, "free key: %p", key); default_free_fn = (OSSL_FUNC_keymgmt_free_fn *) sk_prov_get_default_keymgmt_func(key->provctx, key->type, OSSL_FUNC_KEYMGMT_FREE); if (default_free_fn == NULL) sk_debug_key(key, "no default free_fn"); else default_free_fn(key->default_key); if (key->secure_key != NULL) OPENSSL_free(key->secure_key); OPENSSL_free(key); } static int sk_prov_keymgmt_match(const void *vkey1, const void *vkey2, int selection) { OSSL_FUNC_keymgmt_match_fn *default_match_fn; const struct sk_prov_key *key1 = vkey1; const struct sk_prov_key *key2 = vkey2; if (key1 == NULL || key2 == NULL) return 0; sk_debug_key(key1, "key1: %p key2: %p", key1, key2); default_match_fn = (OSSL_FUNC_keymgmt_match_fn *) sk_prov_get_default_keymgmt_func(key1->provctx, key1->type, OSSL_FUNC_KEYMGMT_MATCH); if (default_match_fn == NULL) { put_error_key(key1, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default match_fn"); return 0; } if (key1->type != key2->type) return 0; if (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) { /* match everything except private key */ return default_match_fn(key1->default_key, key2->default_key, selection & (~OSSL_KEYMGMT_SELECT_PRIVATE_KEY)); } if (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) { if (key1->secure_key_size != key2->secure_key_size) return 0; if (key1->secure_key_size > 0) { if (memcmp(key1->secure_key, key2->secure_key, key1->secure_key_size) != 0) return 0; selection &= (~OSSL_KEYMGMT_SELECT_PRIVATE_KEY); } } return default_match_fn(key1->default_key, key2->default_key, selection); } static int sk_prov_keymgmt_validate(const void *vkey, int selection, int checktype) { OSSL_FUNC_keymgmt_validate_fn *default_validate_fn; const struct sk_prov_key *key = vkey; int default_selection = selection; if (key == NULL) return 0; sk_debug_key(key, "key: %p selection: %x checktype: %x", key, selection, checktype); default_validate_fn = (OSSL_FUNC_keymgmt_validate_fn *) sk_prov_get_default_keymgmt_func(key->provctx, key->type, OSSL_FUNC_KEYMGMT_VALIDATE); if (default_validate_fn == NULL) { put_error_key(key, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default validate_fn"); return 0; } /* A secure key doesn't have the private parts in the default key */ if (key->secure_key != NULL) default_selection &= (~OSSL_KEYMGMT_SELECT_PRIVATE_KEY); return default_validate_fn(key->default_key, default_selection, checktype); } static int sk_prov_keymgmt_get_params(void *vkey, OSSL_PARAM params[]) { OSSL_FUNC_keymgmt_get_params_fn *default_get_params_fn; struct sk_prov_key *key = vkey; OSSL_PARAM *p; if (key == NULL) return 0; sk_debug_key(key, "key: %p", key); for (p = params; p != NULL && p->key != NULL; p++) sk_debug_key(key, "param: %s", p->key); default_get_params_fn = (OSSL_FUNC_keymgmt_get_params_fn *) sk_prov_get_default_keymgmt_func(key->provctx, key->type, OSSL_FUNC_KEYMGMT_GET_PARAMS); /* default_get_params_fn is optional */ if (default_get_params_fn != NULL) { if (!default_get_params_fn(key->default_key, params)) { put_error_key(key, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_get_params_fn failed"); return 0; } } if (key->secure_key == NULL) return 1; p = OSSL_PARAM_locate(params, SK_PROV_PKEY_PARAM_SK_BLOB); if (p != NULL && !OSSL_PARAM_set_octet_string(p, key->secure_key, key->secure_key_size)) { put_error_key(key, SK_PROV_ERR_INTERNAL_ERROR, "OSSL_PARAM_set_octet_string failed"); return 0; } p = OSSL_PARAM_locate(params, SK_PROV_PKEY_PARAM_SK_FUNCS); if (p != NULL && !OSSL_PARAM_set_octet_ptr(p, key->funcs, sizeof(struct sk_funcs))) { put_error_key(key, SK_PROV_ERR_INTERNAL_ERROR, "OSSL_PARAM_set_octet_ptr failed"); return 0; } p = OSSL_PARAM_locate(params, SK_PROV_PKEY_PARAM_SK_PRIVATE); if (p != NULL && !OSSL_PARAM_set_octet_ptr(p, key->private, 0)) { put_error_key(key, SK_PROV_ERR_INTERNAL_ERROR, "OSSL_PARAM_set_octet_ptr failed"); return 0; } return 1; } static int sk_prov_keymgmt_set_params(void *vkey, const OSSL_PARAM params[]) { OSSL_FUNC_keymgmt_set_params_fn *default_set_params_fn; struct sk_prov_key *key = vkey; const OSSL_PARAM *p; size_t len; if (key == NULL) return 0; sk_debug_key(key, "key: %p", key); for (p = params; p != NULL && p->key != NULL; p++) sk_debug_key(key, "param: %s", p->key); default_set_params_fn = (OSSL_FUNC_keymgmt_set_params_fn *) sk_prov_get_default_keymgmt_func(key->provctx, key->type, OSSL_FUNC_KEYMGMT_SET_PARAMS); /* default_set_params_fn is optional */ if (default_set_params_fn != NULL) { if (!default_set_params_fn(key->default_key, params)) { put_error_key(key, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_set_params_fn failed"); return 0; } } if (key->secure_key == NULL) return 1; p = OSSL_PARAM_locate_const(params, SK_PROV_PKEY_PARAM_SK_FUNCS); if (p != NULL && !OSSL_PARAM_get_octet_string_ptr(p, (const void **)&key->funcs, &len)) { put_error_key(key, SK_PROV_ERR_INTERNAL_ERROR, "OSSL_PARAM_get_octet_string_ptr failed"); return 0; } p = OSSL_PARAM_locate_const(params, SK_PROV_PKEY_PARAM_SK_PRIVATE); if (p != NULL && !OSSL_PARAM_get_octet_string_ptr(p, (const void **)&key->private, &len)) { put_error_key(key, SK_PROV_ERR_INTERNAL_ERROR, "OSSL_PARAM_get_octet_string_ptr failed"); return 0; } return 1; } #define SK_PROV_SECURE_KEY_FUNC_PARMS \ OSSL_PARAM_octet_ptr(SK_PROV_PKEY_PARAM_SK_FUNCS, NULL, 0), \ OSSL_PARAM_octet_ptr(SK_PROV_PKEY_PARAM_SK_PRIVATE, NULL, 0) #define SK_PROV_SECURE_KEY_PARMS \ OSSL_PARAM_octet_string(SK_PROV_PKEY_PARAM_SK_BLOB, NULL, 0), \ SK_PROV_SECURE_KEY_FUNC_PARMS static const OSSL_PARAM sk_prov_key_settable_params[] = { SK_PROV_SECURE_KEY_FUNC_PARMS, OSSL_PARAM_END }; static const OSSL_PARAM sk_prov_key_gettable_params[] = { SK_PROV_SECURE_KEY_PARMS, OSSL_PARAM_END }; static const OSSL_PARAM *sk_prov_keymgmt_gettable_params( struct sk_prov_ctx *provctx, int pkey_type) { OSSL_FUNC_keymgmt_gettable_params_fn *default_gettable_params_fn; const OSSL_PARAM *default_parms = NULL, *params, *p; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "pkey_type: %d", pkey_type); params = sk_prov_get_cached_params(provctx, pkey_type, SK_CONF_CACHED_PARAMS_OP_KEY_GET, 0); if (params != NULL) { for (p = params; p != NULL && p->key != NULL; p++) sk_debug_ctx(provctx, "param: %s", p->key); return params; } default_gettable_params_fn = (OSSL_FUNC_keymgmt_gettable_params_fn *) sk_prov_get_default_keymgmt_func(provctx, pkey_type, OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS); /* default_gettable_params_fn is optional */ if (default_gettable_params_fn != NULL) default_parms = default_gettable_params_fn(provctx->default_provctx); return sk_prov_cached_params_build(provctx, pkey_type, SK_CONF_CACHED_PARAMS_OP_KEY_GET, 0, default_parms, sk_prov_key_gettable_params); } static const OSSL_PARAM *sk_prov_keymgmt_settable_params( struct sk_prov_ctx *provctx, int pkey_type) { OSSL_FUNC_keymgmt_settable_params_fn *default_settable_params_fn; const OSSL_PARAM *default_parms = NULL, *params, *p; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "pkey_type: %d", pkey_type); params = sk_prov_get_cached_params(provctx, pkey_type, SK_CONF_CACHED_PARAMS_OP_KEY_SET, 0); if (params != NULL) { for (p = params; p != NULL && p->key != NULL; p++) sk_debug_ctx(provctx, "param: %s", p->key); return params; } default_settable_params_fn = (OSSL_FUNC_keymgmt_settable_params_fn *) sk_prov_get_default_keymgmt_func(provctx, pkey_type, OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS); /* default_settable_params_fn is optional */ if (default_settable_params_fn != NULL) default_parms = default_settable_params_fn(provctx->default_provctx); return sk_prov_cached_params_build(provctx, pkey_type, SK_CONF_CACHED_PARAMS_OP_KEY_SET, 0, default_parms, sk_prov_key_settable_params); } static int sk_prov_keymgmt_has(const void *vkey, int selection) { OSSL_FUNC_keymgmt_has_fn *default_has_fn; const struct sk_prov_key *key = vkey; int default_selection = selection; if (key == NULL) return 0; sk_debug_key(key, "key: %p selection: %x", key, selection); default_has_fn = (OSSL_FUNC_keymgmt_has_fn *) sk_prov_get_default_keymgmt_func(key->provctx, key->type, OSSL_FUNC_KEYMGMT_HAS); if (default_has_fn == NULL) { put_error_key(key, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default has_fn"); return 0; } /* A secure key doesn't have the private parts in the default key */ if (key->secure_key != NULL) default_selection &= (~OSSL_KEYMGMT_SELECT_PRIVATE_KEY); return default_has_fn(key->default_key, default_selection); } struct sk_prov_export_cb { struct sk_prov_key *key; OSSL_CALLBACK *param_callback; void *cbarg; }; static int sk_prov_keymgmt_export_cb(const OSSL_PARAM params[], void *arg) { struct sk_prov_export_cb *cb_data = arg; OSSL_PARAM *np, *new_params; struct sk_prov_key *key; const OSSL_PARAM *p; int rc, count, i; if (cb_data == NULL) return 0; key = cb_data->key; sk_debug_key(key, "key: %p", key); for (count = 0, p = params; p != NULL && p->key != NULL; p++, count++) ; count += 3 + 1; /* 3 addl params plus end marker */ new_params = OPENSSL_zalloc(count * sizeof(OSSL_PARAM)); if (new_params == NULL) { put_error_key(key, SK_PROV_ERR_MALLOC_FAILED, "OPENSSL_zalloc failed"); return 0; } for (i = 0, p = params; p != NULL && p->key != NULL; p++, i++) { sk_debug_key(key, "param: key: %s", p->key); new_params[i] = *p; } np = &new_params[i++]; *np = OSSL_PARAM_construct_octet_string(SK_PROV_PKEY_PARAM_SK_BLOB, key->secure_key, key->secure_key_size); sk_debug_key(key, "param: key: %s", np->key); np = &new_params[i++]; *np = OSSL_PARAM_construct_octet_ptr(SK_PROV_PKEY_PARAM_SK_FUNCS, (void **)key->funcs, sizeof(struct sk_funcs)); sk_debug_key(key, "param: key: %s", np->key); np = &new_params[i++]; *np = OSSL_PARAM_construct_octet_ptr(SK_PROV_PKEY_PARAM_SK_PRIVATE, key->private, 0); sk_debug_key(key, "param: key: %s", np->key); np = &new_params[i++]; *np = OSSL_PARAM_construct_end(); rc = cb_data->param_callback(new_params, cb_data->cbarg); if (rc != 1) sk_debug_key(key, "ERROR: param_callback failed"); OPENSSL_free(new_params); return rc; } static int sk_prov_keymgmt_export(void *vkey, int selection, OSSL_CALLBACK *param_callback, void *cbarg) { OSSL_FUNC_keymgmt_export_fn *default_export_fn; struct sk_prov_export_cb cb_data; struct sk_prov_key *key = vkey; if (key == NULL || param_callback == NULL) return 0; sk_debug_key(key, "key: %p selection: %x", key, selection); default_export_fn = (OSSL_FUNC_keymgmt_export_fn *) sk_prov_get_default_keymgmt_func(key->provctx, key->type, OSSL_FUNC_KEYMGMT_EXPORT); if (default_export_fn == NULL) { put_error_key(key, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default export_fn"); return 0; } if (key->secure_key == NULL || (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) == 0) { /* * Clear key, or no private key selected, call default_export_fn * with original callback */ if (!default_export_fn(key->default_key, selection, param_callback, cbarg)) { put_error_key(key, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_export_fn failed"); return 0; } return 1; } /* Let the callback add our 3 addl. params */ cb_data.key = key; cb_data.param_callback = param_callback; cb_data.cbarg = cbarg; if (!default_export_fn(key->default_key, selection, sk_prov_keymgmt_export_cb, &cb_data)) { put_error_key(key, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_export_fn failed"); return 0; } return 1; } static int sk_prov_keymgmt_import(void *vkey, int selection, const OSSL_PARAM params[]) { const OSSL_PARAM *p_blob, *p_funcs, *p_private, *p; OSSL_FUNC_keymgmt_import_fn *default_import_fn; struct sk_prov_key *key = vkey; size_t len; if (key == NULL) return 0; sk_debug_key(key, "key: %p selection: %x", key, selection); for (p = params; p != NULL && p->key != NULL; p++) sk_debug_key(key, "param: %s", p->key); default_import_fn = (OSSL_FUNC_keymgmt_import_fn *) sk_prov_get_default_keymgmt_func(key->provctx, key->type, OSSL_FUNC_KEYMGMT_IMPORT); if (default_import_fn == NULL) { put_error_key(key, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default import_fn"); return 0; } if (!default_import_fn(key->default_key, selection, params)) { put_error_key(key, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_import_fn failed"); return 0; } if (key->secure_key != NULL) OPENSSL_free(key->secure_key); key->secure_key = NULL; key->secure_key_size = 0; key->funcs = NULL; key->private = NULL; p_blob = OSSL_PARAM_locate_const(params, SK_PROV_PKEY_PARAM_SK_BLOB); p_funcs = OSSL_PARAM_locate_const(params, SK_PROV_PKEY_PARAM_SK_FUNCS); p_private = OSSL_PARAM_locate_const(params, SK_PROV_PKEY_PARAM_SK_PRIVATE); if (p_blob != NULL && p_funcs != NULL && p_private != NULL) { if (!OSSL_PARAM_get_octet_string(p_blob, (void **)&key->secure_key, 0, &key->secure_key_size)) { put_error_key(key, SK_PROV_ERR_INTERNAL_ERROR, "OSSL_PARAM_get_octet_string failed"); return 0; } if (!OSSL_PARAM_get_octet_string_ptr(p_funcs, (const void **)&key->funcs, &len)) { put_error_key(key, SK_PROV_ERR_INTERNAL_ERROR, "OSSL_PARAM_get_octet_string_ptr failed"); return 0; } if (!OSSL_PARAM_get_octet_string_ptr(p_private, (const void **)&key->private, &len)) { put_error_key(key, SK_PROV_ERR_INTERNAL_ERROR, "OSSL_PARAM_get_octet_string_ptr failed"); return 0; } } return 1; } static const OSSL_PARAM sk_prov_imexport_types[] = { SK_PROV_SECURE_KEY_PARMS, OSSL_PARAM_END }; static const OSSL_PARAM *sk_prov_keymgmt_export_types( struct sk_prov_ctx *provctx, int selection, int pkey_type) { OSSL_FUNC_keymgmt_export_types_fn *default_export_types_fn; const OSSL_PARAM *default_parms = NULL, *params; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "selection: %d pkey_type: %d", selection, pkey_type); params = sk_prov_get_cached_params(provctx, pkey_type, SK_CONF_CACHED_PARAMS_OP_KEY_EXPORT, selection); if (params != NULL) return params; default_export_types_fn = (OSSL_FUNC_keymgmt_export_types_fn *) sk_prov_get_default_keymgmt_func(provctx, pkey_type, OSSL_FUNC_KEYMGMT_EXPORT_TYPES); /* default_export_types_fn is optional */ if (default_export_types_fn != NULL) default_parms = default_export_types_fn(selection); return sk_prov_cached_params_build(provctx, pkey_type, SK_CONF_CACHED_PARAMS_OP_KEY_EXPORT, selection, default_parms, sk_prov_imexport_types); } static const OSSL_PARAM *sk_prov_keymgmt_import_types( struct sk_prov_ctx *provctx, int selection, int pkey_type) { OSSL_FUNC_keymgmt_import_types_fn *default_import_types_fn; const OSSL_PARAM *default_parms = NULL, *params; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "selection: %d pkey_type: %d", selection, pkey_type); params = sk_prov_get_cached_params(provctx, pkey_type, SK_CONF_CACHED_PARAMS_OP_KEY_IMPORT, selection); if (params != NULL) return params; default_import_types_fn = (OSSL_FUNC_keymgmt_import_types_fn *) sk_prov_get_default_keymgmt_func(provctx, pkey_type, OSSL_FUNC_KEYMGMT_EXPORT_TYPES); /* default_import_types_fn is optional */ if (default_import_types_fn != NULL) default_parms = default_import_types_fn(selection); return sk_prov_cached_params_build(provctx, pkey_type, SK_CONF_CACHED_PARAMS_OP_KEY_IMPORT, selection, default_parms, sk_prov_imexport_types); } static struct sk_prov_op_ctx *sk_prov_keymgmt_gen_init( struct sk_prov_ctx *provctx, int selection, const OSSL_PARAM params[], int pkey_type) { OSSL_FUNC_keymgmt_gen_cleanup_fn *default_gen_cleanup_fn; OSSL_FUNC_keymgmt_gen_init_fn *default_gen_init_fn; struct sk_prov_op_ctx *genctx; const OSSL_PARAM *p; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "selection: %x type: %d", selection, pkey_type); for (p = params; p != NULL && p->key != NULL; p++) sk_debug_ctx(provctx, "param: %s", p->key); default_gen_init_fn = (OSSL_FUNC_keymgmt_gen_init_fn *) sk_prov_get_default_keymgmt_func(provctx, pkey_type, OSSL_FUNC_KEYMGMT_GEN_INIT); if (default_gen_init_fn == NULL) { put_error_ctx(provctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default gen_init_fn"); return NULL; } default_gen_cleanup_fn = (OSSL_FUNC_keymgmt_gen_cleanup_fn *) sk_prov_get_default_keymgmt_func(provctx, pkey_type, OSSL_FUNC_KEYMGMT_GEN_CLEANUP); if (default_gen_cleanup_fn == NULL) { put_error_ctx(provctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default gen_cleanup_fn"); return NULL; } genctx = sk_prov_op_newctx(provctx, NULL, pkey_type); if (genctx == NULL) { put_error_ctx(provctx, SK_PROV_ERR_INTERNAL_ERROR, "sk_prov_op_newctx failed"); return NULL; } if (!sk_prov_op_init(genctx, NULL, EVP_PKEY_OP_KEYGEN)) { put_error_ctx(provctx, SK_PROV_ERR_INTERNAL_ERROR, "sk_prov_op_init failed"); sk_prov_op_freectx(genctx); return NULL; } genctx->default_op_ctx = default_gen_init_fn(provctx->default_provctx, selection, params); if (genctx->default_op_ctx == NULL) { put_error_ctx(provctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_gen_init_fn failed"); sk_prov_op_freectx(genctx); return NULL; } genctx->default_op_ctx_free = default_gen_cleanup_fn; sk_debug_ctx(provctx, "genctx: %p", genctx); return genctx; } static void sk_prov_keymgmt_gen_cleanup(void *vgenctx) { struct sk_prov_op_ctx *genctx = vgenctx; if (genctx == NULL) return; sk_debug_op_ctx(genctx, "genctx: %p", genctx); sk_prov_op_freectx(genctx); } static int sk_prov_keymgmt_gen_set_params(void *vgenctx, const OSSL_PARAM params[]) { OSSL_FUNC_keymgmt_gen_set_params_fn *default_gen_set_params_fn; struct sk_prov_op_ctx *genctx = vgenctx; const OSSL_PARAM *p; if (genctx == NULL) return 0; sk_debug_op_ctx(genctx, "genctx: %p", genctx); for (p = params; p != NULL && p->key != NULL; p++) sk_debug_op_ctx(genctx, "param: %s", p->key); default_gen_set_params_fn = (OSSL_FUNC_keymgmt_gen_set_params_fn *) sk_prov_get_default_keymgmt_func(genctx->provctx, genctx->type, OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS); /* default_gen_set_params_fn is optional */ if (default_gen_set_params_fn != NULL) { if (!default_gen_set_params_fn(genctx->default_op_ctx, params)) { put_error_op_ctx(genctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_gen_set_params_fn failed"); return 0; } } return 1; } static const OSSL_PARAM *sk_prov_keymgmt_gen_settable_params( struct sk_prov_op_ctx *genctx, struct sk_prov_ctx *provctx, int pkey_type) { OSSL_FUNC_keymgmt_gen_settable_params_fn *default_gen_settable_params_fn; const OSSL_PARAM *params = NULL, *p; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "pkey_type: %d", pkey_type); default_gen_settable_params_fn = (OSSL_FUNC_keymgmt_gen_settable_params_fn *) sk_prov_get_default_keymgmt_func(provctx, pkey_type, OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS); /* default_gen_settable_params_fn is optional */ if (default_gen_settable_params_fn != NULL) params = default_gen_settable_params_fn(genctx->default_op_ctx, provctx->default_provctx); for (p = params; p != NULL && p->key != NULL; p++) sk_debug_ctx(provctx, "param: %s", p->key); return params; } static int sk_prov_keymgmt_gen_set_template(void *vgenctx, void *vtempl) { OSSL_FUNC_keymgmt_gen_set_template_fn *default_gen_set_template_fn; struct sk_prov_op_ctx *genctx = vgenctx; struct sk_prov_key *templ = vtempl; if (genctx == NULL || templ == NULL) return 0; sk_debug_op_ctx(genctx, "genctx: %p templ: %p", genctx, templ); default_gen_set_template_fn = (OSSL_FUNC_keymgmt_gen_set_template_fn *) sk_prov_get_default_keymgmt_func(genctx->provctx, genctx->type, OSSL_FUNC_KEYMGMT_GEN_SET_TEMPLATE); if (default_gen_set_template_fn == NULL) { put_error_op_ctx(genctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default get_set_template_fn"); return 0; } return default_gen_set_template_fn(genctx->default_op_ctx, templ->default_key); } static void *sk_prov_keymgmt_gen(void *vgenctx, OSSL_CALLBACK *osslcb, void *cbarg) { OSSL_FUNC_keymgmt_gen_fn *default_gen_fn; struct sk_prov_op_ctx *genctx = vgenctx; struct sk_prov_key *key; if (genctx == NULL) return NULL; sk_debug_op_ctx(genctx, "genctx: %p", genctx); default_gen_fn = (OSSL_FUNC_keymgmt_gen_fn *) sk_prov_get_default_keymgmt_func(genctx->provctx, genctx->type, OSSL_FUNC_KEYMGMT_GEN); if (default_gen_fn == NULL) { put_error_op_ctx(genctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default gen_fn"); return NULL; } key = OPENSSL_zalloc(sizeof(struct sk_prov_key)); if (key == NULL) { put_error_op_ctx(genctx, SK_PROV_ERR_MALLOC_FAILED, "OPENSSL_zalloc failed"); return NULL; } key->provctx = genctx->provctx; key->type = genctx->type; key->default_key = default_gen_fn(genctx->default_op_ctx, osslcb, cbarg); if (key->default_key == NULL) { put_error_op_ctx(genctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_gen_fn failed"); OPENSSL_free(key); return NULL; } sk_prov_keymgmt_upref(key); sk_debug_op_ctx(genctx, "key: %p", key); return key; } static void *sk_prov_keymgmt_load(const void *reference, size_t reference_sz) { struct sk_prov_key *key; if (reference == NULL) return NULL; if (reference_sz == sizeof(struct sk_prov_key)) { /* The contents of the reference is the address to our object */ key = *(struct sk_prov_key **)reference; /* We grabbed, so we detach it */ *(struct sk_prov_key **)reference = NULL; return key; } return NULL; } static int sk_prov_keymgmt_get_size(struct sk_prov_key *key) { int size = 0; OSSL_PARAM key_params[] = { OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, &size), OSSL_PARAM_END }; sk_debug_key(key, "key: %p", key); if (!sk_prov_keymgmt_get_params(key, key_params) || !OSSL_PARAM_modified(&key_params[0]) || size <= 0) { put_error_key(key, SK_PROV_ERR_MISSING_PARAMETER, "sk_prov_keymgmt_get_params failed to " "get OSSL_PKEY_PARAM_MAX_SIZE"); return -1; } sk_debug_key(key, "size: %d", size); return size; } static int sk_prov_keymgmt_get_bits(struct sk_prov_key *key) { int bits = 0; OSSL_PARAM key_params[] = { OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, &bits), OSSL_PARAM_END }; sk_debug_key(key, "key: %p", key); if (!sk_prov_keymgmt_get_params(key, key_params) || !OSSL_PARAM_modified(&key_params[0]) || bits <= 0) { put_error_key(key, SK_PROV_ERR_MISSING_PARAMETER, "sk_prov_keymgmt_get_params failed to " "get OSSL_PKEY_PARAM_BITS"); return -1; } sk_debug_key(key, "bits: %d", bits); return bits; } static void *sk_prov_keymgmt_rsa_new(void *vprovctx) { struct sk_prov_ctx *provctx = vprovctx; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "provctx: %p", provctx); return sk_prov_keymgmt_new(provctx, EVP_PKEY_RSA); } static const char *sk_prov_keymgmt_rsa_query_operation_name(int operation_id) { switch (operation_id) { case OSSL_OP_SIGNATURE: case OSSL_OP_ASYM_CIPHER: return "RSA"; } return NULL; } static const OSSL_PARAM *sk_prov_keymgmt_rsa_gettable_params(void *vprovctx) { struct sk_prov_ctx *provctx = vprovctx; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "provctx: %p", provctx); return sk_prov_keymgmt_gettable_params(provctx, EVP_PKEY_RSA); } static const OSSL_PARAM *sk_prov_keymgmt_rsa_settable_params(void *vprovctx) { struct sk_prov_ctx *provctx = vprovctx; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "provctx: %p", provctx); return sk_prov_keymgmt_settable_params(provctx, EVP_PKEY_RSA); } #ifdef OSSL_FUNC_KEYMGMT_EXPORT_TYPES_EX static const OSSL_PARAM *sk_prov_keymgmt_rsa_export_types_ex(void *vprovctx, int selection) { struct sk_prov_ctx *provctx = vprovctx; return sk_prov_keymgmt_export_types(provctx, selection, EVP_PKEY_RSA); } static const OSSL_PARAM *sk_prov_keymgmt_rsa_import_types_ex(void *vprovctx, int selection) { struct sk_prov_ctx *provctx = vprovctx; return sk_prov_keymgmt_import_types(provctx, selection, EVP_PKEY_RSA); } #else static const OSSL_PARAM *sk_prov_keymgmt_rsa_export_types(int selection) { struct sk_prov_ctx *provctx = OSSL_PROVIDER_get0_provider_ctx(sk_prov_securekey_provider); return sk_prov_keymgmt_export_types(provctx, selection, EVP_PKEY_RSA); } static const OSSL_PARAM *sk_prov_keymgmt_rsa_import_types(int selection) { struct sk_prov_ctx *provctx = OSSL_PROVIDER_get0_provider_ctx(sk_prov_securekey_provider); return sk_prov_keymgmt_import_types(provctx, selection, EVP_PKEY_RSA); } #endif static void *sk_prov_keymgmt_rsa_gen_init(void *vprovctx, int selection, const OSSL_PARAM params[]) { struct sk_prov_ctx *provctx = vprovctx; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "provctx: %p", provctx); return sk_prov_keymgmt_gen_init(provctx, selection, params, EVP_PKEY_RSA); } static const OSSL_PARAM *sk_prov_keymgmt_rsa_gen_settable_params(void *vgenctx, void *vprovctx) { struct sk_prov_op_ctx *genctx = vgenctx; struct sk_prov_ctx *provctx = vprovctx; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "provctx: %p", provctx); return sk_prov_keymgmt_gen_settable_params(genctx, provctx, EVP_PKEY_RSA); } static void *sk_prov_keymgmt_rsa_pss_new(void *vprovctx) { struct sk_prov_ctx *provctx = vprovctx; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "provctx: %p", provctx); return sk_prov_keymgmt_new(provctx, EVP_PKEY_RSA_PSS); } static const OSSL_PARAM *sk_prov_keymgmt_rsa_pss_gettable_params(void *vprovctx) { struct sk_prov_ctx *provctx = vprovctx; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "provctx: %p", provctx); return sk_prov_keymgmt_gettable_params(provctx, EVP_PKEY_RSA_PSS); } static const OSSL_PARAM *sk_prov_keymgmt_rsa_pss_settable_params(void *vprovctx) { struct sk_prov_ctx *provctx = vprovctx; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "provctx: %p", provctx); return sk_prov_keymgmt_settable_params(provctx, EVP_PKEY_RSA_PSS); } #ifdef OSSL_FUNC_KEYMGMT_EXPORT_TYPES_EX static const OSSL_PARAM *sk_prov_keymgmt_rsa_pss_export_types_ex(void *vprovctx, int selection) { struct sk_prov_ctx *provctx = vprovctx; return sk_prov_keymgmt_export_types(provctx, selection, EVP_PKEY_RSA_PSS); } static const OSSL_PARAM *sk_prov_keymgmt_rsa_pss_import_types_ex(void *vprovctx, int selection) { struct sk_prov_ctx *provctx = vprovctx; return sk_prov_keymgmt_import_types(provctx, selection, EVP_PKEY_RSA_PSS); } #else static const OSSL_PARAM *sk_prov_keymgmt_rsa_pss_export_types(int selection) { struct sk_prov_ctx *provctx = OSSL_PROVIDER_get0_provider_ctx(sk_prov_securekey_provider); return sk_prov_keymgmt_export_types(provctx, selection, EVP_PKEY_RSA_PSS); } static const OSSL_PARAM *sk_prov_keymgmt_rsa_pss_import_types(int selection) { struct sk_prov_ctx *provctx = OSSL_PROVIDER_get0_provider_ctx(sk_prov_securekey_provider); return sk_prov_keymgmt_import_types(provctx, selection, EVP_PKEY_RSA_PSS); } #endif static void *sk_prov_keymgmt_rsa_pss_gen_init(void *vprovctx, int selection, const OSSL_PARAM params[]) { struct sk_prov_ctx *provctx = vprovctx; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "provctx: %p", provctx); return sk_prov_keymgmt_gen_init(provctx, selection, params, EVP_PKEY_RSA_PSS); } static const OSSL_PARAM *sk_prov_keymgmt_rsa_pss_gen_settable_params( void *vgenctx, void *vprovctx) { struct sk_prov_op_ctx *genctx = vgenctx; struct sk_prov_ctx *provctx = vprovctx; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "provctx: %p", provctx); return sk_prov_keymgmt_gen_settable_params(genctx, provctx, EVP_PKEY_RSA_PSS); } static void *sk_prov_keymgmt_ec_new(void *vprovctx) { struct sk_prov_ctx *provctx = vprovctx; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "provctx: %p", provctx); return sk_prov_keymgmt_new(provctx, EVP_PKEY_EC); } static const char *sk_prov_keymgmt_ec_query_operation_name(int operation_id) { switch (operation_id) { case OSSL_OP_KEYEXCH: return "ECDH"; case OSSL_OP_SIGNATURE: return "ECDSA"; } return NULL; } static const OSSL_PARAM *sk_prov_keymgmt_ec_gettable_params(void *vprovctx) { struct sk_prov_ctx *provctx = vprovctx; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "provctx: %p", provctx); return sk_prov_keymgmt_gettable_params(provctx, EVP_PKEY_EC); } static const OSSL_PARAM *sk_prov_keymgmt_ec_settable_params(void *vprovctx) { struct sk_prov_ctx *provctx = vprovctx; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "provctx: %p", provctx); return sk_prov_keymgmt_settable_params(provctx, EVP_PKEY_EC); } #ifdef OSSL_FUNC_KEYMGMT_EXPORT_TYPES_EX static const OSSL_PARAM *sk_prov_keymgmt_ec_export_types_ex(void *vprovctx, int selection) { struct sk_prov_ctx *provctx = vprovctx; return sk_prov_keymgmt_export_types(provctx, selection, EVP_PKEY_EC); } static const OSSL_PARAM *sk_prov_keymgmt_ec_import_types_ex(void *vprovctx, int selection) { struct sk_prov_ctx *provctx = vprovctx; return sk_prov_keymgmt_import_types(provctx, selection, EVP_PKEY_EC); } #else static const OSSL_PARAM *sk_prov_keymgmt_ec_export_types(int selection) { struct sk_prov_ctx *provctx = OSSL_PROVIDER_get0_provider_ctx(sk_prov_securekey_provider); return sk_prov_keymgmt_export_types(provctx, selection, EVP_PKEY_EC); } static const OSSL_PARAM *sk_prov_keymgmt_ec_import_types(int selection) { struct sk_prov_ctx *provctx = OSSL_PROVIDER_get0_provider_ctx(sk_prov_securekey_provider); return sk_prov_keymgmt_import_types(provctx, selection, EVP_PKEY_EC); } #endif static void *sk_prov_keymgmt_ec_gen_init(void *vprovctx, int selection, const OSSL_PARAM params[]) { struct sk_prov_ctx *provctx = vprovctx; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "provctx: %p", provctx); return sk_prov_keymgmt_gen_init(provctx, selection, params, EVP_PKEY_EC); } static const OSSL_PARAM *sk_prov_keymgmt_ec_gen_settable_params(void *vgenctx, void *vprovctx) { struct sk_prov_op_ctx *genctx = vgenctx; struct sk_prov_ctx *provctx = vprovctx; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "provctx: %p", provctx); return sk_prov_keymgmt_gen_settable_params(genctx, provctx, EVP_PKEY_EC); } static void *sk_prov_keyexch_ec_newctx(void *vprovctx) { OSSL_FUNC_keyexch_freectx_fn *default_freectx_fn; OSSL_FUNC_keyexch_newctx_fn *default_newctx_fn; struct sk_prov_ctx *provctx = vprovctx; struct sk_prov_op_ctx *ctx; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "provctx: %p", provctx); default_newctx_fn = (OSSL_FUNC_keyexch_newctx_fn *) sk_prov_get_default_keyexch_func(provctx, OSSL_FUNC_KEYEXCH_NEWCTX); if (default_newctx_fn == NULL) { put_error_ctx(provctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default newctx_fn"); return NULL; } default_freectx_fn = (OSSL_FUNC_keyexch_freectx_fn *) sk_prov_get_default_keyexch_func(provctx, OSSL_FUNC_KEYEXCH_FREECTX); if (default_freectx_fn == NULL) { put_error_ctx(provctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default freectx_fn"); return NULL; } ctx = sk_prov_op_newctx(provctx, NULL, EVP_PKEY_EC); if (ctx == NULL) { sk_debug_ctx(provctx, "ERROR: sk_prov_op_newctx failed"); return NULL; } ctx->default_op_ctx = default_newctx_fn(provctx->default_provctx); if (ctx->default_op_ctx == NULL) { put_error_ctx(provctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_newctx_fn failed"); sk_prov_op_freectx(ctx); return NULL; } ctx->default_op_ctx_free = default_freectx_fn; sk_debug_ctx(provctx, "ctx: %p", ctx); return ctx; } static void *sk_prov_keyexch_ec_dupctx(void *vctx) { OSSL_FUNC_keyexch_dupctx_fn *default_dupctx_fn; struct sk_prov_op_ctx *ctx = vctx; struct sk_prov_op_ctx *new_ctx; if (ctx == NULL) return NULL; sk_debug_op_ctx(ctx, "ctx: %p", ctx); default_dupctx_fn = (OSSL_FUNC_keyexch_dupctx_fn *) sk_prov_get_default_keyexch_func(ctx->provctx, OSSL_FUNC_KEYEXCH_DUPCTX); if (default_dupctx_fn == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default dupctx_fn"); return NULL; } new_ctx = sk_prov_op_dupctx(ctx); if (new_ctx == NULL) { sk_debug_op_ctx(ctx, "ERROR: sk_prov_op_dupctx failed"); return NULL; } new_ctx->default_op_ctx = default_dupctx_fn(ctx->default_op_ctx); if (new_ctx->default_op_ctx == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_dupctx_fn failed"); sk_prov_op_freectx(new_ctx); return NULL; } sk_debug_op_ctx(ctx, "new_ctx: %p", new_ctx); return new_ctx; } static int sk_prov_keyexch_ec_init(void *vctx, void *vkey, const OSSL_PARAM params[]) { OSSL_FUNC_keyexch_init_fn *default_init_fn; struct sk_prov_op_ctx *ctx = vctx; struct sk_prov_key *key = vkey; const OSSL_PARAM *p; if (ctx == NULL || key == NULL) return 0; sk_debug_op_ctx(ctx, "ctx: %p key: %p", ctx, key); for (p = params; p != NULL && p->key != NULL; p++) sk_debug_op_ctx(ctx, "param: %s", p->key); default_init_fn = (OSSL_FUNC_keyexch_init_fn *) sk_prov_get_default_keyexch_func(ctx->provctx, OSSL_FUNC_KEYEXCH_INIT); if (default_init_fn == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default init_fn"); return 0; } if (!sk_prov_op_init(ctx, key, EVP_PKEY_OP_DERIVE)) { sk_debug_op_ctx(ctx, "ERROR: sk_prov_op_init failed"); return 0; } if (!default_init_fn(ctx->default_op_ctx, key->default_key, params)) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_init_fn failed"); return 0; } return 1; } static int sk_prov_keyexch_ec_set_peer(void *vctx, void *vpeerkey) { OSSL_FUNC_keyexch_set_peer_fn *default_set_peer_fn; struct sk_prov_key *peerkey = vpeerkey; struct sk_prov_op_ctx *ctx = vctx; if (ctx == NULL || peerkey == NULL) return 0; sk_debug_op_ctx(ctx, "ctx: %p key: %p peerkey: %p", ctx, ctx->key, peerkey); default_set_peer_fn = (OSSL_FUNC_keyexch_set_peer_fn *) sk_prov_get_default_keyexch_func(ctx->provctx, OSSL_FUNC_KEYEXCH_SET_PEER); if (default_set_peer_fn == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default set_peer_fn"); return 0; } if (ctx->key == NULL || ctx->operation != EVP_PKEY_OP_DERIVE) { put_error_op_ctx(ctx, SK_PROV_ERR_OPRATION_NOT_INITIALIZED, "derive operation not initialized"); return 0; } if (!default_set_peer_fn(ctx->default_op_ctx, peerkey->default_key)) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_set_peer_fn failed"); return 0; } return 1; } static int sk_prov_keyexch_ec_derive(void *vctx, unsigned char *secret, size_t *secretlen, size_t outlen) { OSSL_FUNC_keyexch_derive_fn *default_derive_fn; struct sk_prov_op_ctx *ctx = vctx; if (ctx == NULL || secretlen == NULL) return 0; sk_debug_op_ctx(ctx, "ctx: %p key: %p outlen: %lu", ctx, ctx->key, outlen); default_derive_fn = (OSSL_FUNC_keyexch_derive_fn *) sk_prov_get_default_keyexch_func(ctx->provctx, OSSL_FUNC_KEYEXCH_DERIVE); if (default_derive_fn == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_MISSING, "no default derive_fn"); return 0; } if (ctx->key == NULL || ctx->operation != EVP_PKEY_OP_DERIVE) { put_error_op_ctx(ctx, SK_PROV_ERR_OPRATION_NOT_INITIALIZED, "derive operation not initialized"); return 0; } if (!default_derive_fn(ctx->default_op_ctx, secret, secretlen, outlen)) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_derive_fn failed"); return 0; } sk_debug_op_ctx(ctx, "secretlen: %lu", *secretlen); return 1; } static int sk_prov_keyexch_ec_set_ctx_params(void *vctx, const OSSL_PARAM params[]) { OSSL_FUNC_keyexch_set_ctx_params_fn *default_set_params_fn; struct sk_prov_op_ctx *ctx = vctx; const OSSL_PARAM *p; if (ctx == NULL) return 0; sk_debug_op_ctx(ctx, "ctx: %p", ctx); for (p = params; p != NULL && p->key != NULL; p++) sk_debug_op_ctx(ctx, "param: %s", p->key); default_set_params_fn = (OSSL_FUNC_keyexch_set_ctx_params_fn *) sk_prov_get_default_keyexch_func(ctx->provctx, OSSL_FUNC_KEYEXCH_SET_CTX_PARAMS); /* default_set_params_fn is optional */ if (default_set_params_fn != NULL) { if (!default_set_params_fn(ctx->default_op_ctx, params)) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_set_params_fn failed"); return 0; } } return 1; } static const OSSL_PARAM *sk_prov_keyexch_ec_settable_ctx_params(void *vctx, void *vprovctx) { OSSL_FUNC_keyexch_settable_ctx_params_fn *default_settable_params_fn; struct sk_prov_ctx *provctx = vprovctx; const OSSL_PARAM *params = NULL, *p; struct sk_prov_op_ctx *ctx = vctx; if (ctx == NULL || provctx == NULL) return NULL; default_settable_params_fn = (OSSL_FUNC_keyexch_settable_ctx_params_fn *) sk_prov_get_default_keyexch_func(provctx, OSSL_FUNC_KEYEXCH_SETTABLE_CTX_PARAMS); /* default_settable_params_fn is optional */ if (default_settable_params_fn != NULL) params = default_settable_params_fn(ctx->default_op_ctx, provctx->default_provctx); for (p = params; p != NULL && p->key != NULL; p++) sk_debug_ctx(provctx, "param: %s", p->key); return params; } static int sk_prov_keyexch_ec_get_ctx_params(void *vctx, OSSL_PARAM params[]) { OSSL_FUNC_keyexch_get_ctx_params_fn *default_get_params_fn; struct sk_prov_op_ctx *ctx = vctx; const OSSL_PARAM *p; if (ctx == NULL) return 0; sk_debug_op_ctx(ctx, "ctx: %p", ctx); for (p = params; p != NULL && p->key != NULL; p++) sk_debug_op_ctx(ctx, "param: %s", p->key); default_get_params_fn = (OSSL_FUNC_keyexch_get_ctx_params_fn *) sk_prov_get_default_keyexch_func(ctx->provctx, OSSL_FUNC_KEYEXCH_GET_CTX_PARAMS); /* default_get_params_fn is optional */ if (default_get_params_fn != NULL) { if (!default_get_params_fn(ctx->default_op_ctx, params)) { put_error_op_ctx(ctx, SK_PROV_ERR_DEFAULT_PROV_FUNC_FAILED, "default_get_params_fn failed"); return 0; } } return 1; } static const OSSL_PARAM *sk_prov_keyexch_ec_gettable_ctx_params(void *vctx, void *vprovctx) { OSSL_FUNC_keyexch_gettable_ctx_params_fn *default_gettable_params_fn; struct sk_prov_ctx *provctx = vprovctx; const OSSL_PARAM *params = NULL, *p; struct sk_prov_op_ctx *ctx = vctx; if (ctx == NULL || provctx == NULL) return NULL; default_gettable_params_fn = (OSSL_FUNC_keyexch_gettable_ctx_params_fn *) sk_prov_get_default_keyexch_func(provctx, OSSL_FUNC_KEYEXCH_GETTABLE_CTX_PARAMS); /* default_settable_params_fn is optional */ if (default_gettable_params_fn != NULL) params = default_gettable_params_fn(ctx->default_op_ctx, provctx->default_provctx); for (p = params; p != NULL && p->key != NULL; p++) sk_debug_ctx(provctx, "param: %s", p->key); return params; } static void *sk_prov_asym_rsa_newctx(void *vprovctx) { struct sk_prov_ctx *provctx = vprovctx; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "provctx: %p", provctx); return sk_prov_asym_op_newctx(provctx, EVP_PKEY_RSA); } static const OSSL_PARAM *sk_prov_asym_rsa_gettable_ctx_params(void *vctx, void *vprovctx) { struct sk_prov_ctx *provctx = vprovctx; struct sk_prov_op_ctx *ctx = vctx; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "provctx: %p", provctx); return sk_prov_asym_op_gettable_ctx_params(ctx, provctx, EVP_PKEY_RSA); } static const OSSL_PARAM *sk_prov_asym_rsa_settable_ctx_params(void *vctx, void *vprovctx) { struct sk_prov_ctx *provctx = vprovctx; struct sk_prov_op_ctx *ctx = vctx; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "provctx: %p", provctx); return sk_prov_asym_op_settable_ctx_params(ctx, provctx, EVP_PKEY_RSA); } static int sk_prov_asym_rsa_decrypt(void *vctx, unsigned char *out, size_t *outlen, size_t outsize, const unsigned char *in, size_t inlen) { int rsa_size, pad_mode, oaep_label_len = 0, rc; EVP_MD *oaep_md = NULL, *mgf_md = NULL; struct sk_prov_op_ctx *ctx = vctx; unsigned char *oaep_label = NULL; unsigned char *tmp = NULL; struct sk_prov_key *key; struct sk_funcs *funcs; if (ctx == NULL || in == NULL || outlen == NULL) return 0; sk_debug_op_ctx(ctx, "ctx: %p key: %p inlen: %lu outsize: %lu", ctx, ctx->key, inlen, outsize); if (ctx->key == NULL || ctx->operation != EVP_PKEY_OP_DECRYPT) { put_error_op_ctx(ctx, SK_PROV_ERR_OPRATION_NOT_INITIALIZED, "decrypt operation not initialized"); return 0; } /* For clear key, let the default provider handle it */ if (ctx->key->secure_key == NULL) return sk_prov_asym_op_decrypt(ctx, out, outlen, outsize, in, inlen); funcs = ctx->key->funcs; if (funcs == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, "no secure key funcs"); return 0; } key = ctx->key; rsa_size = sk_prov_keymgmt_get_size(key); if (rsa_size <= 0) { sk_debug_op_ctx(ctx, "sk_prov_keymgmt_get_size failed"); return 0; } if (out == NULL) { tmp = OPENSSL_zalloc(rsa_size); if (tmp == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_MALLOC_FAILED, "OPENSSL_zalloc failed"); return 0; } out = tmp; outsize = rsa_size; } if (outsize < (size_t)rsa_size) { put_error_op_ctx(ctx, SK_PROV_ERR_INVALID_PARAM, "output buffer length invalid"); return 0; } pad_mode = sk_prov_asym_op_get_padding(ctx); switch (pad_mode) { case RSA_NO_PADDING: case RSA_PKCS1_PADDING: case RSA_X931_PADDING: break; case RSA_PKCS1_OAEP_PADDING: oaep_label_len = sk_prov_asym_op_get_oaep_label(ctx, &oaep_label); if (oaep_label_len < 0) { sk_debug_op_ctx(ctx, "ERROR: sk_prov_rsa_asym_get_oaep_label failed"); rc = 0; goto out; } oaep_md = sk_prov_asym_op_get_oaep_md(ctx); if (oaep_md == NULL) { sk_debug_op_ctx(ctx, "ERROR: sk_prov_asym_op_get_oaep_md failed"); rc = 0; goto out; } mgf_md = sk_prov_asym_op_get_mgf_md(ctx); if (mgf_md == NULL) { sk_debug_op_ctx(ctx, "ERROR: sk_prov_asym_op_get_mgf_md failed"); rc = 0; goto out; } break; default: put_error_op_ctx(ctx, SK_PROV_ERR_INVALID_PADDING, "unknown/unsupported padding: %d", pad_mode); return 0; } *outlen = outsize; switch (pad_mode) { case RSA_PKCS1_OAEP_PADDING: if (funcs->rsa_decrypt_oaep == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, "no secure key decrypt function"); rc = 0; goto out; } rc = funcs->rsa_decrypt_oaep(key->secure_key, key->secure_key_size, out, outlen, in, inlen, EVP_MD_type(oaep_md), EVP_MD_type(mgf_md), oaep_label, oaep_label_len, key->private, ctx->provctx->debug); break; default: if (funcs->rsa_decrypt == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, "no secure key decrypt function"); rc = 0; goto out; } rc = funcs->rsa_decrypt(key->secure_key, key->secure_key_size, out, outlen, in, inlen, pad_mode, key->private, ctx->provctx->debug); break; } if (tmp != NULL) { OPENSSL_cleanse(tmp, outsize); OPENSSL_free(tmp); } if (rc != 0) { put_error_op_ctx(ctx, SK_PROV_ERR_SECURE_KEY_FUNC_FAILED, "Secure key encrypt operation failed: rc: %d", rc); rc = 0; goto out; } rc = 1; sk_debug_op_ctx(ctx, "outlen: %lu", *outlen); out: if (oaep_md != NULL) EVP_MD_free(oaep_md); if (mgf_md != NULL) EVP_MD_free(mgf_md); return rc; } static void *sk_prov_sign_rsa_newctx(void *vprovctx, const char *propq) { struct sk_prov_ctx *provctx = vprovctx; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "provctx: %p propq: %s", provctx, propq != NULL ? propq : ""); return sk_prov_sign_op_newctx(provctx, propq, EVP_PKEY_RSA); } static const OSSL_PARAM *sk_prov_sign_rsa_gettable_ctx_params(void *vctx, void *vprovctx) { struct sk_prov_ctx *provctx = vprovctx; struct sk_prov_op_ctx *ctx = vctx; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "provctx: %p", provctx); return sk_prov_sign_op_gettable_ctx_params(ctx, provctx, EVP_PKEY_RSA); } static const OSSL_PARAM *sk_prov_sign_rsa_settable_ctx_params(void *vctx, void *vprovctx) { struct sk_prov_ctx *provctx = vprovctx; struct sk_prov_op_ctx *ctx = vctx; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "provctx: %p", provctx); return sk_prov_sign_op_settable_ctx_params(ctx, provctx, EVP_PKEY_RSA); } static const OSSL_PARAM *sk_prov_sign_rsa_gettable_ctx_md_params(void *vctx) { struct sk_prov_op_ctx *ctx = vctx; if (ctx == NULL) return NULL; sk_debug_op_ctx(ctx, "ctx: %p", ctx); return sk_prov_sign_op_gettable_ctx_md_params(ctx, EVP_PKEY_RSA); } static const OSSL_PARAM *sk_prov_sign_rsa_settable_ctx_md_params(void *vctx) { struct sk_prov_op_ctx *ctx = vctx; if (ctx == NULL) return NULL; sk_debug_op_ctx(ctx, "ctx: %p", ctx); return sk_prov_sign_op_settable_ctx_md_params(ctx, EVP_PKEY_RSA); } static int sk_prov_sign_rsa_sign(void *vctx, unsigned char *sig, size_t *siglen, size_t sigsize, const unsigned char *tbs, size_t tbslen) { EVP_MD *sign_md = NULL, *mgf_md = NULL; int rsa_size, pad_mode, salt_len, rc; struct sk_prov_op_ctx *ctx = vctx; struct sk_prov_key *key; struct sk_funcs *funcs; if (ctx == NULL || siglen == NULL || tbs == NULL) return 0; sk_debug_op_ctx(ctx, "ctx: %p key: %p tbslen: %lu sigsize: %lu", ctx, ctx->key, tbslen, sigsize); if (ctx->key == NULL || ctx->operation != EVP_PKEY_OP_SIGN) { put_error_op_ctx(ctx, SK_PROV_ERR_OPRATION_NOT_INITIALIZED, "sign operation not initialized"); return 0; } /* For clear key, let the default provider handle it */ if (ctx->key->secure_key == NULL) return sk_prov_sign_op_sign(ctx, sig, siglen, sigsize, tbs, tbslen); key = ctx->key; rsa_size = sk_prov_keymgmt_get_size(key); if (rsa_size <= 0) { sk_debug_op_ctx(ctx, "ERROR: sk_prov_keymgmt_get_size failed"); return 0; } if (sig == NULL) { *siglen = rsa_size; sk_debug_op_ctx(ctx, "siglen: %lu", *siglen); return 1; } if (sigsize < (size_t)rsa_size) { put_error_op_ctx(ctx, SK_PROV_ERR_INVALID_PARAM, "signature length invalid"); return 0; } funcs = ctx->key->funcs; if (funcs == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, "no secure key funcs"); return 0; } sign_md = sk_prov_sign_op_get_md(ctx); pad_mode = sk_prov_sign_op_get_padding(ctx); if (sign_md == NULL) { /* Sign without a signature digest, fall back to no padding */ pad_mode = RSA_NO_PADDING; } else { if (tbslen != (size_t)EVP_MD_size(sign_md)) { put_error_op_ctx(ctx, SK_PROV_ERR_INVALID_PARAM, "tbslen must be size of digest"); rc = 0; goto out; } } *siglen = rsa_size; switch (pad_mode) { case RSA_PKCS1_PADDING: case RSA_X931_PADDING: if (sign_md == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, "padding needs a signature digest"); rc = 0; goto out; } /* fall through */ case RSA_NO_PADDING: if (funcs->rsa_sign == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, "no secure key sign function"); rc = 0; goto out; } rc = funcs->rsa_sign(key->secure_key, key->secure_key_size, sig, siglen, tbs, tbslen, pad_mode, sign_md != NULL ? EVP_MD_type(sign_md) : NID_undef, key->private, ctx->provctx->debug); break; case RSA_PKCS1_PSS_PADDING: if (sign_md == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, "PSS padding needs a signature digest"); rc = 0; goto out; } mgf_md = sk_prov_sign_op_get_mgf_md(ctx); if (mgf_md == NULL) { sk_debug_op_ctx(ctx, "ERROR sk_prov_sign_op_get_mgf_md failed"); rc = 0; goto out; } salt_len = sk_prov_sign_op_get_pss_saltlen(ctx, key, mgf_md); if (salt_len < 0) { sk_debug_op_ctx(ctx, "ERROR: sk_prov_sign_op_get_pss_saltlen failed"); rc = 0; goto out; } if (funcs->rsa_pss_sign == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, "no secure key sign function"); rc = 0; goto out; } rc = funcs->rsa_pss_sign(key->secure_key, key->secure_key_size, sig, siglen, tbs, tbslen, EVP_MD_type(sign_md), EVP_MD_type(mgf_md), salt_len, key->private, ctx->provctx->debug); break; default: put_error_op_ctx(ctx, SK_PROV_ERR_INVALID_PADDING, "unknown/unsupported padding: %d", pad_mode); rc = 0; goto out; } if (rc != 0) { put_error_op_ctx(ctx, SK_PROV_ERR_SECURE_KEY_FUNC_FAILED, "Secure key sign operation failed: rc: %d", rc); rc = 0; goto out; } rc = 1; sk_debug_op_ctx(ctx, "siglen: %lu", *siglen); out: if (sign_md != NULL) EVP_MD_free(sign_md); if (mgf_md != NULL) EVP_MD_free(mgf_md); return rc; } static int sk_prov_sign_rsa_digest_sign_init(void *vctx, const char *mdname, void *vkey, const OSSL_PARAM params[]) { struct sk_prov_op_ctx *ctx = vctx; struct sk_prov_key *key = vkey; sk_debug_op_ctx(ctx, "ctx: %p mdname: %s key: %p", ctx, mdname != NULL ? mdname : "", key); return sk_prov_sign_op_digest_sign_init(ctx, mdname, key, params, sk_prov_sign_rsa_sign); } static void *sk_prov_sign_ec_newctx(void *vprovctx, const char *propq) { struct sk_prov_ctx *provctx = vprovctx; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "provctx: %p propq: %s", provctx, propq != NULL ? propq : ""); return sk_prov_sign_op_newctx(provctx, propq, EVP_PKEY_EC); } static const OSSL_PARAM *sk_prov_sign_ec_gettable_ctx_params(void *vctx, void *vprovctx) { struct sk_prov_op_ctx *ctx = vctx; struct sk_prov_ctx *provctx = vprovctx; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "provctx: %p", provctx); return sk_prov_sign_op_gettable_ctx_params(ctx, provctx, EVP_PKEY_EC); } static const OSSL_PARAM *sk_prov_sign_ec_settable_ctx_params(void *vctx, void *vprovctx) { struct sk_prov_op_ctx *ctx = vctx; struct sk_prov_ctx *provctx = vprovctx; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "provctx: %p", provctx); return sk_prov_sign_op_settable_ctx_params(ctx, provctx, EVP_PKEY_EC); } static const OSSL_PARAM *sk_prov_sign_ec_gettable_ctx_md_params(void *vctx) { struct sk_prov_op_ctx *ctx = vctx; if (ctx == NULL) return NULL; sk_debug_op_ctx(ctx, "ctx: %p", ctx); return sk_prov_sign_op_gettable_ctx_md_params(ctx, EVP_PKEY_EC); } static const OSSL_PARAM *sk_prov_sign_ec_settable_ctx_md_params(void *vctx) { struct sk_prov_op_ctx *ctx = vctx; if (ctx == NULL) return NULL; sk_debug_op_ctx(ctx, "ctx: %p", ctx); return sk_prov_sign_op_settable_ctx_md_params(ctx, EVP_PKEY_EC); } static int sk_prov_sign_ec_sign(void *vctx, unsigned char *sig, size_t *siglen, size_t sigsize, const unsigned char *tbs, size_t tbslen) { struct sk_prov_op_ctx *ctx = vctx; struct sk_prov_key *key; EVP_MD *sign_md = NULL; struct sk_funcs *funcs; int ec_size, rc; if (ctx == NULL || siglen == NULL || tbs == NULL) return 0; sk_debug_op_ctx(ctx, "ctx: %p key: %p tbslen: %lu sigsize: %lu", ctx, ctx->key, tbslen, sigsize); if (ctx->key == NULL || ctx->operation != EVP_PKEY_OP_SIGN) { put_error_op_ctx(ctx, SK_PROV_ERR_OPRATION_NOT_INITIALIZED, "sign operation not initialized"); return 0; } /* For clear key, let the default provider handle it */ if (ctx->key->secure_key == NULL) return sk_prov_sign_op_sign(ctx, sig, siglen, sigsize, tbs, tbslen); key = ctx->key; ec_size = sk_prov_keymgmt_get_size(key); if (ec_size <= 0) { sk_debug_op_ctx(ctx, "ERROR: sk_prov_keymgmt_get_size failed"); return 0; } if (sig == NULL) { *siglen = ec_size; sk_debug_op_ctx(ctx, "siglen: %lu", *siglen); return 1; } if (sigsize < (size_t)ec_size) { put_error_op_ctx(ctx, SK_PROV_ERR_INVALID_PARAM, "signature length invalid"); return 0; } funcs = ctx->key->funcs; if (funcs == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, "no secure key funcs"); return 0; } sign_md = sk_prov_sign_op_get_md(ctx); if (sign_md == NULL) { sk_debug_op_ctx(ctx, "ERROR: sk_prov_sign_op_get_md failed"); return 0; } if (tbslen != (size_t)EVP_MD_size(sign_md)) { put_error_op_ctx(ctx, SK_PROV_ERR_INVALID_PARAM, "tbslen must be size of digest"); rc = 0; goto out; } *siglen = ec_size; if (funcs->ecdsa_sign == NULL) { put_error_op_ctx(ctx, SK_PROV_ERR_MISSING_PARAMETER, "no secure key sign function"); rc = 0; goto out; } rc = funcs->ecdsa_sign(key->secure_key, key->secure_key_size, sig, siglen, tbs, tbslen, EVP_MD_type(sign_md), key->private, ctx->provctx->debug); if (rc != 0) { put_error_op_ctx(ctx, SK_PROV_ERR_SECURE_KEY_FUNC_FAILED, "Secure key sign operation failed: rc: %d", rc); rc = 0; goto out; } rc = 1; sk_debug_op_ctx(ctx, "siglen: %lu", *siglen); out: if (sign_md != NULL) EVP_MD_free(sign_md); return rc; } static int sk_prov_sign_ec_digest_sign_init(void *vctx, const char *mdname, void *vkey, const OSSL_PARAM params[]) { struct sk_prov_op_ctx *ctx = vctx; struct sk_prov_key *key = vkey; sk_debug_op_ctx(ctx, "ctx: %p mdname: %s key: %p", ctx, mdname != NULL ? mdname : "", key); return sk_prov_sign_op_digest_sign_init(ctx, mdname, key, params, sk_prov_sign_ec_sign); } static const OSSL_DISPATCH sk_prov_rsa_signature_functions[] = { /* Signature context constructor, descructor */ { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))sk_prov_sign_rsa_newctx }, { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))sk_prov_op_freectx }, { OSSL_FUNC_SIGNATURE_DUPCTX, (void (*)(void))sk_prov_sign_op_dupctx }, /* Signing */ { OSSL_FUNC_SIGNATURE_SIGN_INIT, (void (*)(void))sk_prov_sign_op_sign_init }, { OSSL_FUNC_SIGNATURE_SIGN, (void (*)(void))sk_prov_sign_rsa_sign }, /* Verifying */ { OSSL_FUNC_SIGNATURE_VERIFY_INIT, (void (*)(void))sk_prov_sign_op_verify_init }, { OSSL_FUNC_SIGNATURE_VERIFY, (void (*)(void))sk_prov_sign_op_verify }, /* Verify recover */ { OSSL_FUNC_SIGNATURE_VERIFY_RECOVER_INIT, (void (*)(void))sk_prov_sign_op_verify_recover_init }, { OSSL_FUNC_SIGNATURE_VERIFY_RECOVER, (void (*)(void))sk_prov_sign_op_verify_recover }, /* Digest Sign */ { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT, (void (*)(void))sk_prov_sign_rsa_digest_sign_init }, { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_UPDATE, (void (*)(void))sk_prov_sign_op_digest_sign_update }, { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_FINAL, (void (*)(void))sk_prov_sign_op_digest_sign_final }, /* Digest Verify */ { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_INIT, (void (*)(void))sk_prov_sign_op_digest_verify_init }, { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_UPDATE, (void (*)(void))sk_prov_sign_op_digest_verify_update }, { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_FINAL, (void (*)(void))sk_prov_sign_op_digest_verify_final }, /* Signature parameters */ { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, (void (*)(void))sk_prov_sign_op_get_ctx_params }, { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS, (void (*)(void))sk_prov_sign_rsa_gettable_ctx_params }, { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, (void (*)(void))sk_prov_sign_op_set_ctx_params }, { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS, (void (*)(void))sk_prov_sign_rsa_settable_ctx_params }, /* MD parameters */ { OSSL_FUNC_SIGNATURE_GET_CTX_MD_PARAMS, (void (*)(void))sk_prov_sign_op_get_ctx_md_params }, { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_MD_PARAMS, (void (*)(void))sk_prov_sign_rsa_gettable_ctx_md_params }, { OSSL_FUNC_SIGNATURE_SET_CTX_MD_PARAMS, (void (*)(void))sk_prov_sign_op_set_ctx_md_params }, { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_MD_PARAMS, (void (*)(void))sk_prov_sign_rsa_settable_ctx_md_params }, { 0, NULL } }; static const OSSL_DISPATCH sk_prov_ecdsa_signature_functions[] = { /* Signature context constructor, descructor */ { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))sk_prov_sign_ec_newctx }, { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))sk_prov_op_freectx }, { OSSL_FUNC_SIGNATURE_DUPCTX, (void (*)(void))sk_prov_sign_op_dupctx }, /* Signing */ { OSSL_FUNC_SIGNATURE_SIGN_INIT, (void (*)(void))sk_prov_sign_op_sign_init }, { OSSL_FUNC_SIGNATURE_SIGN, (void (*)(void))sk_prov_sign_ec_sign }, /* Verifying */ { OSSL_FUNC_SIGNATURE_VERIFY_INIT, (void (*)(void))sk_prov_sign_op_verify_init }, { OSSL_FUNC_SIGNATURE_VERIFY, (void (*)(void))sk_prov_sign_op_verify }, /* Verify recover */ { OSSL_FUNC_SIGNATURE_VERIFY_RECOVER_INIT, (void (*)(void))sk_prov_sign_op_verify_recover_init }, { OSSL_FUNC_SIGNATURE_VERIFY_RECOVER, (void (*)(void))sk_prov_sign_op_verify_recover }, /* Digest Sign */ { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT, (void (*)(void))sk_prov_sign_ec_digest_sign_init }, { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_UPDATE, (void (*)(void))sk_prov_sign_op_digest_sign_update }, { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_FINAL, (void (*)(void))sk_prov_sign_op_digest_sign_final }, /* Digest Verify */ { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_INIT, (void (*)(void))sk_prov_sign_op_digest_verify_init }, { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_UPDATE, (void (*)(void))sk_prov_sign_op_digest_verify_update }, { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_FINAL, (void (*)(void))sk_prov_sign_op_digest_verify_final }, /* Signature parameters */ { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, (void (*)(void))sk_prov_sign_op_get_ctx_params }, { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS, (void (*)(void))sk_prov_sign_ec_gettable_ctx_params }, { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, (void (*)(void))sk_prov_sign_op_set_ctx_params }, { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS, (void (*)(void))sk_prov_sign_ec_settable_ctx_params }, /* MD parameters */ { OSSL_FUNC_SIGNATURE_GET_CTX_MD_PARAMS, (void (*)(void))sk_prov_sign_op_get_ctx_md_params }, { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_MD_PARAMS, (void (*)(void))sk_prov_sign_ec_gettable_ctx_md_params }, { OSSL_FUNC_SIGNATURE_SET_CTX_MD_PARAMS, (void (*)(void))sk_prov_sign_op_set_ctx_md_params }, { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_MD_PARAMS, (void (*)(void))sk_prov_sign_ec_settable_ctx_md_params }, { 0, NULL } }; static const OSSL_ALGORITHM sk_prov_signature[] = { { "RSA:rsaEncryption", "provider="SK_PROV_NAME, sk_prov_rsa_signature_functions, NULL }, { "ECDSA", "provider="SK_PROV_NAME, sk_prov_ecdsa_signature_functions, NULL }, { NULL, NULL, NULL, NULL } }; static const OSSL_DISPATCH sk_prov_rsa_asym_cipher_functions[] = { /* RSA context constructor, descructor */ { OSSL_FUNC_ASYM_CIPHER_NEWCTX, (void (*)(void))sk_prov_asym_rsa_newctx }, { OSSL_FUNC_ASYM_CIPHER_FREECTX, (void (*)(void))sk_prov_op_freectx }, { OSSL_FUNC_ASYM_CIPHER_DUPCTX, (void (*)(void))sk_prov_asym_op_dupctx }, /* RSA context set/get parameters */ { OSSL_FUNC_ASYM_CIPHER_GET_CTX_PARAMS, (void (*)(void))sk_prov_asym_op_get_ctx_params }, { OSSL_FUNC_ASYM_CIPHER_GETTABLE_CTX_PARAMS, (void (*)(void))sk_prov_asym_rsa_gettable_ctx_params }, { OSSL_FUNC_ASYM_CIPHER_SET_CTX_PARAMS, (void (*)(void))sk_prov_asym_op_set_ctx_params }, { OSSL_FUNC_ASYM_CIPHER_SETTABLE_CTX_PARAMS, (void (*)(void))sk_prov_asym_rsa_settable_ctx_params }, /* RSA encrypt */ { OSSL_FUNC_ASYM_CIPHER_ENCRYPT_INIT, (void (*)(void))sk_prov_asym_op_encrypt_init }, { OSSL_FUNC_ASYM_CIPHER_ENCRYPT, (void (*)(void))sk_prov_asym_op_encrypt }, /* RSA decrypt */ { OSSL_FUNC_ASYM_CIPHER_DECRYPT_INIT, (void (*)(void))sk_prov_asym_op_decrypt_init }, { OSSL_FUNC_ASYM_CIPHER_DECRYPT, (void (*)(void))sk_prov_asym_rsa_decrypt }, { 0, NULL } }; static const OSSL_ALGORITHM sk_prov_asym_cipher[] = { { "RSA:rsaEncryption", "provider="SK_PROV_NAME, sk_prov_rsa_asym_cipher_functions, NULL }, { NULL, NULL, NULL, NULL } }; static const OSSL_DISPATCH sk_prov_rsa_keymgmt_functions[] = { /* Constructor, destructor */ { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))sk_prov_keymgmt_rsa_new }, { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))sk_prov_keymgmt_free }, /* Key generation and loading */ { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))sk_prov_keymgmt_rsa_gen_init }, { OSSL_FUNC_KEYMGMT_GEN_SET_TEMPLATE, (void (*)(void))sk_prov_keymgmt_gen_set_template }, { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void (*)(void))sk_prov_keymgmt_gen_set_params }, { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, (void (*)(void))sk_prov_keymgmt_rsa_gen_settable_params }, { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))sk_prov_keymgmt_gen }, { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))sk_prov_keymgmt_gen_cleanup }, { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))sk_prov_keymgmt_load }, /* Key object checking */ { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))sk_prov_keymgmt_has }, { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))sk_prov_keymgmt_match }, { OSSL_FUNC_KEYMGMT_VALIDATE, (void (*)(void))sk_prov_keymgmt_validate }, { OSSL_FUNC_KEYMGMT_QUERY_OPERATION_NAME, (void (*)(void))sk_prov_keymgmt_rsa_query_operation_name }, /* Key object information */ { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))sk_prov_keymgmt_get_params }, { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))sk_prov_keymgmt_rsa_gettable_params }, { OSSL_FUNC_KEYMGMT_SET_PARAMS, (void (*) (void))sk_prov_keymgmt_set_params }, { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*) (void))sk_prov_keymgmt_rsa_settable_params }, /* Import and export routines */ { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))sk_prov_keymgmt_export }, #ifdef OSSL_FUNC_KEYMGMT_EXPORT_TYPES_EX { OSSL_FUNC_KEYMGMT_EXPORT_TYPES_EX, (void (*)(void))sk_prov_keymgmt_rsa_export_types_ex }, #else { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))sk_prov_keymgmt_rsa_export_types }, #endif { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))sk_prov_keymgmt_import }, #ifdef OSSL_FUNC_KEYMGMT_EXPORT_TYPES_EX { OSSL_FUNC_KEYMGMT_IMPORT_TYPES_EX, (void (*)(void))sk_prov_keymgmt_rsa_import_types_ex }, #else { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))sk_prov_keymgmt_rsa_import_types }, #endif /* No copy function, OpenSSL will use export/import to copy instead */ { 0, NULL } }; static const OSSL_DISPATCH sk_prov_rsapss_keymgmt_functions[] = { /* Constructor, destructor */ { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))sk_prov_keymgmt_rsa_pss_new }, { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))sk_prov_keymgmt_free }, /* Key generation and loading */ { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))sk_prov_keymgmt_rsa_pss_gen_init }, { OSSL_FUNC_KEYMGMT_GEN_SET_TEMPLATE, (void (*)(void))sk_prov_keymgmt_gen_set_template }, { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void (*)(void))sk_prov_keymgmt_gen_set_params }, { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, (void (*)(void))sk_prov_keymgmt_rsa_pss_gen_settable_params }, { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))sk_prov_keymgmt_gen }, { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))sk_prov_keymgmt_gen_cleanup }, { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))sk_prov_keymgmt_load }, /* Key object checking */ { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))sk_prov_keymgmt_has }, { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))sk_prov_keymgmt_match }, { OSSL_FUNC_KEYMGMT_VALIDATE, (void (*)(void))sk_prov_keymgmt_validate }, { OSSL_FUNC_KEYMGMT_QUERY_OPERATION_NAME, (void (*)(void))sk_prov_keymgmt_rsa_query_operation_name }, /* Key object information */ { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))sk_prov_keymgmt_get_params }, { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))sk_prov_keymgmt_rsa_pss_gettable_params }, { OSSL_FUNC_KEYMGMT_SET_PARAMS, (void (*) (void))sk_prov_keymgmt_set_params }, { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*) (void))sk_prov_keymgmt_rsa_pss_settable_params }, /* Import and export routines */ { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))sk_prov_keymgmt_export }, #ifdef OSSL_FUNC_KEYMGMT_EXPORT_TYPES_EX { OSSL_FUNC_KEYMGMT_EXPORT_TYPES_EX, (void (*)(void))sk_prov_keymgmt_rsa_pss_export_types_ex }, #else { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))sk_prov_keymgmt_rsa_pss_export_types }, #endif { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))sk_prov_keymgmt_import }, #ifdef OSSL_FUNC_KEYMGMT_EXPORT_TYPES_EX { OSSL_FUNC_KEYMGMT_IMPORT_TYPES_EX, (void (*)(void))sk_prov_keymgmt_rsa_pss_import_types_ex }, #else { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))sk_prov_keymgmt_rsa_pss_import_types }, #endif /* No copy function, OpenSSL will use export/import to copy instead */ { 0, NULL } }; static const OSSL_DISPATCH sk_prov_ec_keymgmt_functions[] = { /* Constructor, destructor */ { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))sk_prov_keymgmt_ec_new }, { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))sk_prov_keymgmt_free }, /* Key generation and loading */ { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))sk_prov_keymgmt_ec_gen_init }, { OSSL_FUNC_KEYMGMT_GEN_SET_TEMPLATE, (void (*)(void))sk_prov_keymgmt_gen_set_template }, { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void (*)(void))sk_prov_keymgmt_gen_set_params }, { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, (void (*)(void))sk_prov_keymgmt_ec_gen_settable_params }, { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))sk_prov_keymgmt_gen }, { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))sk_prov_keymgmt_gen_cleanup }, { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))sk_prov_keymgmt_load }, /* Key object checking */ { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))sk_prov_keymgmt_has }, { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))sk_prov_keymgmt_match }, { OSSL_FUNC_KEYMGMT_VALIDATE, (void (*)(void))sk_prov_keymgmt_validate }, { OSSL_FUNC_KEYMGMT_QUERY_OPERATION_NAME, (void (*)(void))sk_prov_keymgmt_ec_query_operation_name }, /* Key object information */ { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))sk_prov_keymgmt_get_params }, { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))sk_prov_keymgmt_ec_gettable_params }, { OSSL_FUNC_KEYMGMT_SET_PARAMS, (void (*) (void))sk_prov_keymgmt_set_params }, { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*) (void))sk_prov_keymgmt_ec_settable_params }, /* Import and export routines */ { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))sk_prov_keymgmt_export }, #ifdef OSSL_FUNC_KEYMGMT_EXPORT_TYPES_EX { OSSL_FUNC_KEYMGMT_EXPORT_TYPES_EX, (void (*)(void))sk_prov_keymgmt_ec_export_types_ex }, #else { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))sk_prov_keymgmt_ec_export_types }, #endif { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))sk_prov_keymgmt_import }, #ifdef OSSL_FUNC_KEYMGMT_EXPORT_TYPES_EX { OSSL_FUNC_KEYMGMT_IMPORT_TYPES_EX, (void (*)(void))sk_prov_keymgmt_ec_import_types_ex }, #else { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))sk_prov_keymgmt_ec_import_types }, #endif /* No copy function, OpenSSL will use export/import to copy instead */ { 0, NULL } }; static const OSSL_ALGORITHM sk_prov_keymgmt[] = { { "RSA:rsaEncryption", "provider="SK_PROV_NAME, sk_prov_rsa_keymgmt_functions, NULL }, { "RSA-PSS:RSASSA-PSS", "provider="SK_PROV_NAME, sk_prov_rsapss_keymgmt_functions, NULL }, { "EC:id-ecPublicKey", "provider="SK_PROV_NAME, sk_prov_ec_keymgmt_functions, NULL }, { NULL, NULL, NULL, NULL } }; static const OSSL_DISPATCH sk_prov_ec_keyexch_functions[] = { /* Context management */ { OSSL_FUNC_KEYEXCH_NEWCTX, (void (*)(void))sk_prov_keyexch_ec_newctx }, { OSSL_FUNC_KEYEXCH_FREECTX, (void (*)(void))sk_prov_op_freectx }, { OSSL_FUNC_KEYEXCH_DUPCTX, (void (*)(void))sk_prov_keyexch_ec_dupctx }, /* Shared secret derivation */ { OSSL_FUNC_KEYEXCH_INIT, (void (*)(void))sk_prov_keyexch_ec_init }, { OSSL_FUNC_KEYEXCH_SET_PEER, (void (*)(void))sk_prov_keyexch_ec_set_peer }, { OSSL_FUNC_KEYEXCH_DERIVE, (void (*)(void))sk_prov_keyexch_ec_derive }, /* Key Exchange parameters */ { OSSL_FUNC_KEYEXCH_SET_CTX_PARAMS, (void (*)(void))sk_prov_keyexch_ec_set_ctx_params }, { OSSL_FUNC_KEYEXCH_SETTABLE_CTX_PARAMS, (void (*)(void))sk_prov_keyexch_ec_settable_ctx_params }, { OSSL_FUNC_KEYEXCH_GET_CTX_PARAMS, (void (*)(void))sk_prov_keyexch_ec_get_ctx_params }, { OSSL_FUNC_KEYEXCH_GETTABLE_CTX_PARAMS, (void (*)(void))sk_prov_keyexch_ec_gettable_ctx_params }, { 0, NULL } }; /* * Although ECDH key derivation is not supported for secure keys (would result * in a secure symmetric key, which OpenSSL can't handle), the provider still * must implement the ECDH key exchange functions and proxy them all to the * default provider. OpenSSL common code requires that the key management * provider and the key exchange provider for a derive operation is the same. * So for clear EC keys created with this provider, we do support the ECDH * operation by proxy'ing it to the default provider. */ static const OSSL_ALGORITHM sk_prov_keyexch[] = { { "ECDH", "provider="SK_PROV_NAME, sk_prov_ec_keyexch_functions, NULL }, { NULL, NULL, NULL, NULL } }; static const OSSL_PARAM sk_prov_param_types[] = { OSSL_PARAM_DEFN(OSSL_PROV_PARAM_NAME, OSSL_PARAM_UTF8_PTR, NULL, 0), OSSL_PARAM_DEFN(OSSL_PROV_PARAM_VERSION, OSSL_PARAM_UTF8_PTR, NULL, 0), OSSL_PARAM_DEFN(OSSL_PROV_PARAM_BUILDINFO, OSSL_PARAM_UTF8_PTR, NULL, 0), OSSL_PARAM_DEFN(OSSL_PROV_PARAM_STATUS, OSSL_PARAM_INTEGER, NULL, 0), OSSL_PARAM_END }; static const OSSL_PARAM *sk_prov_gettable_params(void *vprovctx) { struct sk_prov_ctx *provctx = vprovctx; if (provctx == NULL) return NULL; sk_debug_ctx(provctx, "provctx: %p", provctx); return sk_prov_param_types; } static int sk_prov_get_params(void *vprovctx, OSSL_PARAM params[]) { struct sk_prov_ctx *provctx = vprovctx; OSSL_PARAM *p; if (provctx == NULL) return 0; sk_debug_ctx(provctx, "provctx: %p", provctx); p = OSSL_PARAM_locate(params, OSSL_PROV_PARAM_NAME); if (p != NULL && !OSSL_PARAM_set_utf8_ptr(p, SK_PROV_DESCRIPTION)) { put_error_ctx(provctx, SK_PROV_ERR_INTERNAL_ERROR, "OSSL_PARAM_set_utf8_ptr failed"); return 0; } p = OSSL_PARAM_locate(params, OSSL_PROV_PARAM_VERSION); if (p != NULL && !OSSL_PARAM_set_utf8_ptr(p, SK_PROV_VERSION)) { put_error_ctx(provctx, SK_PROV_ERR_INTERNAL_ERROR, "OSSL_PARAM_set_utf8_ptr failed"); return 0; } p = OSSL_PARAM_locate(params, OSSL_PROV_PARAM_BUILDINFO); if (p != NULL && !OSSL_PARAM_set_utf8_ptr(p, SK_PROV_VERSION)) { put_error_ctx(provctx, SK_PROV_ERR_INTERNAL_ERROR, "OSSL_PARAM_set_utf8_ptr failed"); return 0; } p = OSSL_PARAM_locate(params, OSSL_PROV_PARAM_STATUS); if (p != NULL && !OSSL_PARAM_set_int(p, 1)) { put_error_ctx(provctx, SK_PROV_ERR_INTERNAL_ERROR, "OSSL_PARAM_set_int failed"); return 0; } return 1; } static const OSSL_ALGORITHM *sk_prov_query(void *vprovctx, int operation_id, int *no_cache) { struct sk_prov_ctx *provctx = vprovctx; if (provctx == NULL) return NULL; *no_cache = 0; sk_debug_ctx(provctx, "provctx: %p operation_id: %d", provctx, operation_id); switch (operation_id) { case OSSL_OP_KEYMGMT: return sk_prov_keymgmt; case OSSL_OP_KEYEXCH: return sk_prov_keyexch; case OSSL_OP_SIGNATURE: return sk_prov_signature; case OSSL_OP_ASYM_CIPHER: return sk_prov_asym_cipher; } return NULL; } static void sk_prov_teardown(void *vprovctx) { struct sk_prov_ctx *provctx = vprovctx; int i; if (provctx == NULL) return; sk_debug_ctx(provctx, "provctx: %p", provctx); for (i = 0; i < SK_PROV_CACHED_PARAMS_COUNT; i++) { if (provctx->cached_parms[i] != NULL) OPENSSL_free((void *)provctx->cached_parms[i]); } OPENSSL_free(provctx); } static const OSSL_ITEM *sk_prov_get_reason_strings(void *vprovctx) { struct sk_prov_ctx *provctx = vprovctx; sk_debug_ctx(provctx, "provctx: %p", provctx); return sk_prov_reason_strings; } static int sk_prov_prov_get_capabilities(void *vprovctx, const char *capability, OSSL_CALLBACK *cb, void *arg) { struct sk_prov_ctx *provctx = vprovctx; sk_debug_ctx(provctx, "provctx: %p capability: %s", provctx, capability); if (provctx->default_provider == NULL) return 0; return OSSL_PROVIDER_get_capabilities(provctx->default_provider, capability, cb, arg); } /* Functions we provide to the core */ static const OSSL_DISPATCH sk_prov_dispatch_table[] = { { OSSL_FUNC_PROVIDER_TEARDOWN, (void (*)(void))sk_prov_teardown }, { OSSL_FUNC_PROVIDER_GETTABLE_PARAMS, (void (*)(void))sk_prov_gettable_params }, { OSSL_FUNC_PROVIDER_GET_PARAMS, (void (*)(void))sk_prov_get_params }, { OSSL_FUNC_PROVIDER_QUERY_OPERATION, (void (*)(void))sk_prov_query }, { OSSL_FUNC_PROVIDER_GET_REASON_STRINGS, (void (*)(void))sk_prov_get_reason_strings }, { OSSL_FUNC_PROVIDER_GET_CAPABILITIES, (void (*)(void))sk_prov_prov_get_capabilities }, { 0, NULL } }; static int sk_provider_init(const OSSL_CORE_HANDLE *handle, const OSSL_DISPATCH *in, const OSSL_DISPATCH **out, void **provctx) { OSSL_FUNC_core_set_error_debug_fn *c_set_error_debug = NULL; OSSL_FUNC_core_get_libctx_fn *c_get_libctx = NULL; OSSL_FUNC_core_vset_error_fn *c_vset_error = NULL; OSSL_FUNC_core_new_error_fn *c_new_error = NULL; struct sk_prov_ctx *ctx; if (handle == NULL || in == NULL || out == NULL || provctx == NULL) return 0; for (; in->function_id != 0; in++) { switch (in->function_id) { case OSSL_FUNC_CORE_GET_LIBCTX: c_get_libctx = OSSL_FUNC_core_get_libctx(in); break; case OSSL_FUNC_CORE_NEW_ERROR: c_new_error = OSSL_FUNC_core_new_error(in); break; case OSSL_FUNC_CORE_SET_ERROR_DEBUG: c_set_error_debug = OSSL_FUNC_core_set_error_debug(in); break; case OSSL_FUNC_CORE_VSET_ERROR: c_vset_error = OSSL_FUNC_core_vset_error(in); break; default: /* Just ignore anything we don't understand */ break; } } if (c_get_libctx == NULL) return 0; ctx = OPENSSL_zalloc(sizeof(struct sk_prov_ctx)); if (ctx == NULL) { c_new_error(handle); c_set_error_debug(handle, __FILE__, __LINE__, __func__); c_vset_error(handle, SK_PROV_ERR_MALLOC_FAILED, "Failed to allocate provider context", NULL); return 0; } ctx->handle = handle; ctx->c_get_libctx = c_get_libctx; ctx->c_new_error = c_new_error; ctx->c_set_error_debug = c_set_error_debug; ctx->c_vset_error = c_vset_error; *provctx = ctx; *out = sk_prov_dispatch_table; return 1; } /** * Initializes the secure key support for OpenSSL. * * @param debug true to enable internal debugging * * @returns zero for success, a negative errno in case of an error: * -ENOMEM: failed to allocate memory * -EIO: OpenSSL failed for various reasons */ int SK_OPENSSL_init(bool debug) { struct sk_prov_ctx *provctx; if (sk_prov_securekey_libctx == NULL) sk_prov_securekey_libctx = OSSL_LIB_CTX_new(); if (sk_prov_securekey_libctx == NULL) { sk_debug(debug, "ERROR: OSSL_LIB_CTX_new failed"); return -ENOMEM; } if (OSSL_PROVIDER_add_builtin(sk_prov_securekey_libctx, SK_PROV_NAME, sk_provider_init) != 1) { sk_debug(debug, "ERROR: OSSL_PROVIDER_add_builtin failed"); return -EIO; } if (sk_prov_securekey_provider == NULL) { sk_prov_securekey_provider = OSSL_PROVIDER_load(sk_prov_securekey_libctx, SK_PROV_NAME); if (sk_prov_securekey_provider == NULL) { sk_debug(debug, "ERROR: OSSL_PROVIDER_load(" SK_PROV_NAME") failed"); return -EIO; } } provctx = OSSL_PROVIDER_get0_provider_ctx(sk_prov_securekey_provider); provctx->debug = debug; if (sk_prov_default_provider == NULL) { sk_prov_default_provider = OSSL_PROVIDER_load(sk_prov_securekey_libctx, "default"); if (sk_prov_default_provider == NULL) { sk_debug(debug, "ERROR: OSSL_PROVIDER_load(default) failed"); SK_OPENSSL_term(); return -EIO; } } provctx->default_provider = sk_prov_default_provider; provctx->default_provctx = OSSL_PROVIDER_get0_provider_ctx(sk_prov_default_provider); sk_prov_previous_libctx = OSSL_LIB_CTX_set0_default(sk_prov_securekey_libctx); if (sk_prov_previous_libctx == NULL) { sk_debug(debug, "ERROR: OSSL_LIB_CTX_set0_default failed"); SK_OPENSSL_term(); return -EIO; } /* Prefer the secure key provider, but allow to fall back to default */ if (!EVP_set_default_properties(sk_prov_securekey_libctx, "?provider="SK_PROV_NAME)) { sk_debug(debug, "ERROR: EVP_set_default_properties failed"); SK_OPENSSL_term(); return -EIO; } sk_debug(debug, "sk_provider support initialized, provctx: %p", provctx); return 0; } /** * Terminate the secure key support for OpenSSL. */ void SK_OPENSSL_term(void) { if (sk_prov_securekey_provider != NULL) OSSL_PROVIDER_unload(sk_prov_securekey_provider); sk_prov_securekey_provider = NULL; if (sk_prov_default_provider != NULL) OSSL_PROVIDER_unload(sk_prov_default_provider); sk_prov_default_provider = NULL; if (sk_prov_previous_libctx != NULL) OSSL_LIB_CTX_set0_default(sk_prov_previous_libctx); sk_prov_previous_libctx = NULL; if (sk_prov_securekey_libctx != NULL) OSSL_LIB_CTX_free(sk_prov_securekey_libctx); sk_prov_securekey_libctx = NULL; } static int sk_openssl_pkey_from_data(OSSL_PARAM_BLD *bld, int pkey_type, EVP_PKEY **pkey, bool debug) { OSSL_PARAM *params = NULL; EVP_PKEY_CTX *pctx = NULL; const char *key_name; int rc = 0; switch (pkey_type) { case EVP_PKEY_EC: key_name = "EC"; break; case EVP_PKEY_RSA: key_name = "RSA"; break; case EVP_PKEY_RSA_PSS: key_name = "RSA-PSS"; break; default: sk_debug(debug, "ERROR: unsupported PKEY type"); return -EINVAL; } params = OSSL_PARAM_BLD_to_param(bld); if (params == NULL) { sk_debug(debug, "ERROR: OSSL_PARAM_BLD_to_param failed"); rc = -EIO; goto out; } pctx = EVP_PKEY_CTX_new_from_name(NULL, key_name, "provider="SK_PROV_NAME); if (pctx == NULL) { sk_debug(debug, "ERROR: EVP_PKEY_CTX_new_from_name failed"); return -EIO; } if (EVP_PKEY_fromdata_init(pctx) <= 0) { sk_debug(debug, "ERROR: EVP_PKEY_fromdata_init failed"); rc = -EIO; goto out; } if (EVP_PKEY_fromdata(pctx, pkey, EVP_PKEY_KEYPAIR, params) <= 0) { sk_debug(debug, "ERROR: EVP_PKEY_fromdata failed"); rc = -EIO; goto out; } out: if (pctx != NULL) EVP_PKEY_CTX_free(pctx); if (params != NULL) OSSL_PARAM_free(params); return rc; } /** * Converts an EC key given by the nid and the x and y coordinates into an * OpenSSL PKEY and attaches the secure key together with secure key functions * and private pointer to it. If no secure key is provided, a public EC key * only PKEY is returned. * * @param secure_key the secure key blob. * If NULL, a clear key PKEY is created. * @param secure_key_size the size of the secure key blob (ignored if * secure_key is NULL) * @param nid the OpenSSL nid of the EC curve used * @param prime_len the length of the prime in bytes. This is also the * length of the x and y coordinates. * @param x the x coordinate as big endian binary number in * prime_len size * @param y the y coordinate as big endian binary number in * prime_len size * @param sk_funcs the secure key functions to operate with the key. * Ignored if secure_key is NULL, required otherwise. * @param private a private pointer that is passed to the secure key * functions (can be NULL) * @param pkey On return: A PKEY containing the EC public key. * @param debug true to enable internal debugging * * @returns zero for success, a negative errno in case of an error: * -EINVAL: a function parameter is invalid * -ENOMEM: failed to allocate memory * -EIO: OpenSSL failed to generate the PKEY * -ENOENT: OpenSSL does not know/support the curve (nid) */ int sk_openssl_get_pkey_ec(const unsigned char *secure_key, size_t secure_key_size, int nid, size_t prime_len, const unsigned char *x, const unsigned char *y, const struct sk_funcs *sk_funcs, const void *private, EVP_PKEY **pkey, bool debug) { BIGNUM *bn_x = NULL, *bn_y = NULL; point_conversion_form_t form; unsigned char *pub_key = NULL; OSSL_PARAM_BLD *bld = NULL; EC_GROUP *group = NULL; EC_POINT *point = NULL; size_t pub_key_len; int rc; if (pkey == NULL || x == NULL || y == NULL) return -EINVAL; if (secure_key != NULL && (secure_key_size == 0 || sk_funcs == NULL)) return -EINVAL; *pkey = NULL; group = EC_GROUP_new_by_curve_name(nid); if (group == NULL) { sk_debug(debug, "ERROR: EC_GROUP_new_by_curve_name failed"); rc = -ENOENT; goto out; } bn_x = BN_bin2bn(x, prime_len, NULL); bn_y = BN_bin2bn(y, prime_len, NULL); if (bn_x == NULL || bn_y == NULL) { sk_debug(debug, "ERROR: BN_bin2bn failed"); rc = -ENOMEM; goto out; } point = EC_POINT_new(group); if (point == NULL) { sk_debug(debug, "ERROR: EC_POINT_new failed"); rc = -ENOMEM; goto out; } if (!EC_POINT_set_affine_coordinates(group, point, bn_x, bn_y, NULL)) { sk_debug(debug, "ERROR: EC_POINT_set_affine_coordinates failed"); rc = -EIO; goto out; } form = EC_GROUP_get_point_conversion_form(group); pub_key_len = EC_POINT_point2buf(group, point, form, &pub_key, NULL); if (pub_key_len == 0) { sk_debug(debug, "ERROR: EC_POINT_point2buf failed"); rc = -EIO; goto out; } bld = OSSL_PARAM_BLD_new(); if (bld == NULL) { sk_debug(debug, "ERROR: OSSL_PARAM_BLD_new failed"); rc = -ENOMEM; goto out; } if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_GROUP_NAME, OBJ_nid2sn(nid), 0) || !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_PUB_X, bn_x) || !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_PUB_Y, bn_y) || !OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY, pub_key, pub_key_len)) { sk_debug(debug, "ERROR: OSSL_PARAM_BLD_push_xxx failed"); rc = -EIO; goto out; } if (secure_key != NULL) { if (!OSSL_PARAM_BLD_push_octet_string(bld, SK_PROV_PKEY_PARAM_SK_BLOB, secure_key, secure_key_size) || !OSSL_PARAM_BLD_push_octet_ptr(bld, SK_PROV_PKEY_PARAM_SK_FUNCS, (void *)sk_funcs, sizeof(struct sk_funcs)) || !OSSL_PARAM_BLD_push_octet_ptr(bld, SK_PROV_PKEY_PARAM_SK_PRIVATE, (void *)private, 0)) { sk_debug(debug, "ERROR: OSSL_PARAM_BLD_push_xxx failed"); rc = -EIO; goto out; } } rc = sk_openssl_pkey_from_data(bld, EVP_PKEY_EC, pkey, debug); if (rc != 0) goto out; rc = 0; sk_debug(debug, "pkey created: %p", *pkey); out: if (group != NULL) EC_GROUP_free(group); if (point != NULL) EC_POINT_free(point); if (bn_x != NULL) BN_free(bn_x); if (bn_y != NULL) BN_free(bn_y); if (bld != NULL) OSSL_PARAM_BLD_free(bld); if (pub_key != NULL) OPENSSL_free(pub_key); return rc; } /** * Converts an RSA key given by the modulus and public exponent into an * OpenSSL PKEY and attaches the secure key together with secure key functions * and private pointer to it. If no secure key is provided, a public RSA key * only PKEY is returned. * * @param secure_key the secure key blob. * If NULL, a clear key PKEY is created. * @param secure_key_size the size of the secure key blob (ignored if * secure_key is NULL) * @param modulus the modulus as big endian number * @param modulus_length the length of the modulus in bytes * @param pub_exp the public exponent as big endian number * @param pub_exp_length the length of the public exponent in bytes * @param pkey_type the PKEY type (EVP_PKEY_RSA or EVP_PKEY_RSA_PSS) * @param sk_funcs the secure key functions to operate with the key. * Ignored if secure_key is NULL, required otherwise. * @param private a private pointer that is passed to the secure key * functions (can be NULL) * @param pkey On return: A PKEY containing the RSA public key. * @param debug true to enable internal debugging * * @returns zero for success, a negative errno in case of an error: * -EINVAL: a function parameter is invalid * -ENOMEM: failed to allocate memory * -EIO: OpenSSL failed to generate the PKEY */ int sk_openssl_get_pkey_rsa(const unsigned char *secure_key, size_t secure_key_size, const unsigned char *modulus, size_t modulus_length, const unsigned char *pub_exp, size_t pub_exp_length, int pkey_type, const struct sk_funcs *sk_funcs, const void *private, EVP_PKEY **pkey, bool debug) { BIGNUM *bn_modulus = NULL, *bn_pub_exp = NULL; OSSL_PARAM_BLD *bld = NULL; int rc; if (pkey == NULL || modulus == NULL || pub_exp == NULL) return -EINVAL; if (secure_key != NULL && (secure_key_size == 0 || sk_funcs == NULL)) return -EINVAL; if (pkey_type != EVP_PKEY_RSA && pkey_type != EVP_PKEY_RSA_PSS) return -EINVAL; *pkey = NULL; bn_modulus = BN_bin2bn(modulus, modulus_length, NULL); bn_pub_exp = BN_bin2bn(pub_exp, pub_exp_length, NULL); if (bn_modulus == NULL || bn_pub_exp == NULL) { sk_debug(debug, "ERROR: BN_bin2bn failed"); rc = -ENOMEM; goto out; } bld = OSSL_PARAM_BLD_new(); if (bld == NULL) { sk_debug(debug, "ERROR: OSSL_PARAM_BLD_new failed"); rc = -ENOMEM; goto out; } if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_N, bn_modulus) || !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_E, bn_pub_exp)) { sk_debug(debug, "ERROR: OSSL_PARAM_BLD_push_xxx failed"); rc = -EIO; goto out; } if (secure_key != NULL) { if (!OSSL_PARAM_BLD_push_octet_string(bld, SK_PROV_PKEY_PARAM_SK_BLOB, secure_key, secure_key_size) || !OSSL_PARAM_BLD_push_octet_ptr(bld, SK_PROV_PKEY_PARAM_SK_FUNCS, (void *)sk_funcs, sizeof(struct sk_funcs)) || !OSSL_PARAM_BLD_push_octet_ptr(bld, SK_PROV_PKEY_PARAM_SK_PRIVATE, (void *)private, 0)) { sk_debug(debug, "ERROR: OSSL_PARAM_BLD_push_xxx failed"); rc = -EIO; goto out; } } rc = sk_openssl_pkey_from_data(bld, pkey_type, pkey, debug); if (rc != 0) goto out; rc = 0; sk_debug(debug, "pkey created: %p", *pkey); out: if (bn_modulus != NULL) BN_free(bn_modulus); if (bn_pub_exp != NULL) BN_free(bn_pub_exp); if (bld != NULL) OSSL_PARAM_BLD_free(bld); return rc; } /** * Get the curve NID of the EC pkey */ int SK_OPENSSL_get_curve_from_ec_pkey(EVP_PKEY *pkey) { size_t curve_len; char curve[80]; if (EVP_PKEY_id(pkey) != EVP_PKEY_EC) return NID_undef; if (!EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME, curve, sizeof(curve), &curve_len)) return NID_undef; return OBJ_sn2nid(curve); } #endif s390-tools-2.38.0/libseckey/sk_utilities.c000066400000000000000000000676321502674226300203130ustar00rootroot00000000000000/* * libseckey - Secure key library * * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/zt_common.h" #include "libseckey/sk_utilities.h" #include "libseckey/sk_ep11.h" void SK_UTIL_warnx(const char *func, const char *fmt, ...) { char tmp_fmt[200]; va_list ap; if (snprintf(tmp_fmt, sizeof(tmp_fmt), "DBG: %s: %s", func, fmt) > (int)sizeof(tmp_fmt)) return; va_start(ap, fmt); vwarnx(tmp_fmt, ap); va_end(ap); } static const unsigned char der_prime192v1[] = { 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x01 }; static const unsigned char der_secp224r1[] = { 0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x21 }; static const unsigned char der_prime256v1[] = { 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07 }; static const unsigned char der_secp384r1[] = { 0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22 }; static const unsigned char der_secp521r1[] = { 0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x23 }; static const unsigned char der_brainpoolP160r1[] = { 0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x01 }; static const unsigned char der_brainpoolP192r1[] = { 0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x03 }; static const unsigned char der_brainpoolP224r1[] = { 0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x05 }; static const unsigned char der_brainpoolP256r1[] = { 0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07 }; static const unsigned char der_brainpoolP320r1[] = { 0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x09 }; static const unsigned char der_brainpoolP384r1[] = { 0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B }; static const unsigned char der_brainpoolP512r1[] = { 0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D }; static const struct sk_ec_curve_info ec_curve_list[] = { { .curve_nid = NID_X9_62_prime192v1, .type = SK_EC_TYPE_PRIME, .prime_bits = 192, .prime_len = 24, .der = der_prime192v1, .der_size = sizeof(der_prime192v1) }, { .curve_nid = NID_secp224r1, .type = SK_EC_TYPE_PRIME, .prime_bits = 224, .prime_len = 28, .der = der_secp224r1, .der_size = sizeof(der_secp224r1)}, { .curve_nid = NID_X9_62_prime256v1, .type = SK_EC_TYPE_PRIME, .prime_bits = 256, .prime_len = 32, .der = der_prime256v1, .der_size = sizeof(der_prime256v1)}, { .curve_nid = NID_secp384r1, .type = SK_EC_TYPE_PRIME, .prime_bits = 384, .prime_len = 48, .der = der_secp384r1, .der_size = sizeof(der_secp384r1)}, { .curve_nid = NID_secp521r1, .type = SK_EC_TYPE_PRIME, .prime_bits = 521, .prime_len = 66, .der = der_secp521r1, .der_size = sizeof(der_secp521r1)}, { .curve_nid = NID_brainpoolP160r1, .type = SK_EC_TYPE_BRAINPOOL, .prime_bits = 160, .prime_len = 20, .der = der_brainpoolP160r1, .der_size = sizeof(der_brainpoolP160r1)}, { .curve_nid = NID_brainpoolP192r1, .type = SK_EC_TYPE_BRAINPOOL, .prime_bits = 192, .prime_len = 24, .der = der_brainpoolP192r1, .der_size = sizeof(der_brainpoolP192r1)}, { .curve_nid = NID_brainpoolP224r1, .type = SK_EC_TYPE_BRAINPOOL, .prime_bits = 224, .prime_len = 28, .der = der_brainpoolP224r1, .der_size = sizeof(der_brainpoolP224r1)}, { .curve_nid = NID_brainpoolP256r1, .type = SK_EC_TYPE_BRAINPOOL, .prime_bits = 256, .prime_len = 32, .der = der_brainpoolP256r1, .der_size = sizeof(der_brainpoolP256r1)}, { .curve_nid = NID_brainpoolP320r1, .type = SK_EC_TYPE_BRAINPOOL, .prime_bits = 320, .prime_len = 40, .der = der_brainpoolP320r1, .der_size = sizeof(der_brainpoolP320r1)}, { .curve_nid = NID_brainpoolP384r1, .type = SK_EC_TYPE_BRAINPOOL, .prime_bits = 384, .prime_len = 48, .der = der_brainpoolP384r1, .der_size = sizeof(der_brainpoolP384r1)}, { .curve_nid = NID_brainpoolP512r1, .type = SK_EC_TYPE_BRAINPOOL, .prime_bits = 512, .prime_len = 64, .der = der_brainpoolP512r1, .der_size = sizeof(der_brainpoolP512r1)}, }; static const int ec_curve_num = sizeof(ec_curve_list) / sizeof(struct sk_ec_curve_info); /** * Returns the curve info of the specified curve, or NULL if the curve * is not known. * * @param nid the OpenSSL nid of the EC curve * * @returns the address of the curve info or NULL if the curve was not found */ const struct sk_ec_curve_info *SK_UTIL_ec_get_curve_info(int curve_nid) { int i; for (i = 0; i < ec_curve_num; i++) { if (ec_curve_list[i].curve_nid == curve_nid) return &ec_curve_list[i]; } return NULL; } /** * Returns the nid of the Prime curve by its specified prime bit size, or 0 * if the curve is not known. * * @param prime_bits the prime bit size of the curve to search for * * @returns the OpenSSL nid of the EC curve or 0 if the curve was not found */ int SK_UTIL_ec_get_prime_curve_by_prime_bits(size_t prime_bits) { int i; for (i = 0; i < ec_curve_num; i++) { if (ec_curve_list[i].type == SK_EC_TYPE_PRIME && ec_curve_list[i].prime_bits == prime_bits) return ec_curve_list[i].curve_nid; } return 0; } /** * Returns the nid of the Brainpool curve by its specified prime bit size, or 0 * if the curve is not known. * * @param prime_bits the prime bit size of the curve to search for * * @returns the OpenSSL nid of the EC curve or 0 if the curve was not found */ int SK_UTIL_ec_get_brainpool_curve_by_prime_bits(size_t prime_bits) { int i; for (i = 0; i < ec_curve_num; i++) { if (ec_curve_list[i].type == SK_EC_TYPE_BRAINPOOL && ec_curve_list[i].prime_bits == prime_bits) return ec_curve_list[i].curve_nid; } return 0; } /** * Calculates the y coordinate of a point on an EC curve using the x coordinate * and the y bit. x and y must be supplied by the caller with prime_len bytes. * On return y contains the calculated y coordinate. * * @param nid the OpenSSL nid of the EC curve used * @param prime_len the length of the prime in bytes. This is also the * length of the x and y coordinates. * @param x the x coordinate as big endian binary number in * prime_len size * @param y_bit the y-bit to identify which of the two possible * values for y should be used * @param y buffer to store the y coordinate as big endian * binary number in prime_len size. * @returns zero for success, a negative errno in case of an error: * -EINVAL: a function parameter is invalid * -ENOMEM: failed to allocate memory * -EIO: OpenSSL failed to calculate the y coordinate * -ENOENT: OpenSSL does not know/support the curve (nid) */ int SK_UTIL_ec_calculate_y_coordinate(int nid, size_t prime_len, const unsigned char *x, int y_bit, unsigned char *y) { EC_GROUP *group = NULL; EC_POINT *point = NULL; BIGNUM *bn_x = NULL; BIGNUM *bn_y = NULL; BN_CTX *ctx = NULL; int rc = 0; if (x == NULL || y == NULL) return -EINVAL; bn_x = BN_bin2bn(x, prime_len, NULL); if (bn_x == NULL) { rc = -EIO; goto out; } group = EC_GROUP_new_by_curve_name(nid); if (group == NULL) { rc = -ENOENT; goto out; } point = EC_POINT_new(group); if (point == NULL) { rc = -EIO; goto out; } bn_y = BN_new(); if (bn_y == NULL) { rc = -ENOMEM; goto out; } ctx = BN_CTX_new(); if (ctx == NULL) { rc = -ENOMEM; goto out; } if (!EC_POINT_set_compressed_coordinates(group, point, bn_x, y_bit, ctx)) { rc = -EIO; goto out; } if (!EC_POINT_is_on_curve(group, point, ctx)) { rc = -EIO; goto out; } if (!EC_POINT_get_affine_coordinates(group, point, bn_x, bn_y, ctx)) { rc = -EIO; goto out; } if (BN_bn2binpad(bn_y, y, prime_len) <= 0) { rc = -EIO; goto out; } out: if (ctx != NULL) BN_CTX_free(ctx); if (point != NULL) EC_POINT_free(point); if (group != NULL) EC_GROUP_free(group); if (bn_x != NULL) BN_free(bn_x); if (bn_y != NULL) BN_free(bn_y); return rc; } static const unsigned char der_DigestInfo_SHA1[] = { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14, }; static const unsigned char der_DigestInfo_SHA224[] = { 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1C, }; static const unsigned char der_DigestInfo_SHA256[] = { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20, }; static const unsigned char der_DigestInfo_SHA384[] = { 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30, }; static const unsigned char der_DigestInfo_SHA512[] = { 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40, }; static const unsigned char der_DigestInfo_SHA3_224[] = { 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x07, 0x05, 0x00, 0x04, 0x1C, }; static const unsigned char der_DigestInfo_SHA3_256[] = { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x08, 0x05, 0x00, 0x04, 0x20, }; static const unsigned char der_DigestInfo_SHA3_384[] = { 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x09, 0x05, 0x00, 0x04, 0x30, }; static const unsigned char der_DigestInfo_SHA3_512[] = { 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0a, 0x05, 0x00, 0x04, 0x40, }; static const struct sk_digest_info digest_list[] = { { .digest_nid = NID_sha1, .digest_size = SHA_DIGEST_LENGTH, .cca_keyword = "SHA-1 ", .der = der_DigestInfo_SHA1, .der_size = sizeof(der_DigestInfo_SHA1), .pkcs11_mech = CKM_SHA_1, .pkcs11_mgf = CKG_MGF1_SHA1, .x9_31_md = 0x33, }, { .digest_nid = NID_sha224, .digest_size = SHA224_DIGEST_LENGTH, .cca_keyword = "SHA-224 ", .der = der_DigestInfo_SHA224, .der_size = sizeof(der_DigestInfo_SHA224), .pkcs11_mech = CKM_SHA224, .pkcs11_mgf = CKG_MGF1_SHA224, .x9_31_md = 0, }, { .digest_nid = NID_sha256, .digest_size = SHA256_DIGEST_LENGTH, .cca_keyword = "SHA-256 ", .der = der_DigestInfo_SHA256, .der_size = sizeof(der_DigestInfo_SHA256), .pkcs11_mech = CKM_SHA256, .pkcs11_mgf = CKG_MGF1_SHA256, .x9_31_md = 0x34, }, { .digest_nid = NID_sha384, .digest_size = SHA384_DIGEST_LENGTH, .cca_keyword = "SHA-384 ", .der = der_DigestInfo_SHA384, .der_size = sizeof(der_DigestInfo_SHA384), .pkcs11_mech = CKM_SHA384, .pkcs11_mgf = CKG_MGF1_SHA384, .x9_31_md = 0x36, }, { .digest_nid = NID_sha512, .digest_size = SHA512_DIGEST_LENGTH, .cca_keyword = "SHA-512 ", .der = der_DigestInfo_SHA512, .der_size = sizeof(der_DigestInfo_SHA512), .pkcs11_mech = CKM_SHA512, .pkcs11_mgf = CKG_MGF1_SHA512, .x9_31_md = 0x35, }, { .digest_nid = NID_sha3_224, .digest_size = SHA224_DIGEST_LENGTH, .cca_keyword = NULL, .der = der_DigestInfo_SHA3_224, .der_size = sizeof(der_DigestInfo_SHA3_224), .pkcs11_mech = CKM_IBM_SHA3_224, .pkcs11_mgf = CKG_IBM_MGF1_SHA3_224, .x9_31_md = 0, }, { .digest_nid = NID_sha3_256, .digest_size = SHA256_DIGEST_LENGTH, .cca_keyword = NULL, .der = der_DigestInfo_SHA3_256, .der_size = sizeof(der_DigestInfo_SHA3_256), .pkcs11_mech = CKM_IBM_SHA3_256, .pkcs11_mgf = CKG_IBM_MGF1_SHA3_256, .x9_31_md = 0, }, { .digest_nid = NID_sha3_384, .digest_size = SHA384_DIGEST_LENGTH, .cca_keyword = NULL, .der = der_DigestInfo_SHA3_384, .der_size = sizeof(der_DigestInfo_SHA3_384), .pkcs11_mech = CKM_IBM_SHA3_384, .pkcs11_mgf = CKG_IBM_MGF1_SHA3_384, .x9_31_md = 0, }, { .digest_nid = NID_sha3_512, .digest_size = SHA512_DIGEST_LENGTH, .cca_keyword = NULL, .der = der_DigestInfo_SHA3_512, .der_size = sizeof(der_DigestInfo_SHA3_512), .pkcs11_mech = CKM_IBM_SHA3_512, .pkcs11_mgf = CKG_IBM_MGF1_SHA3_512, .x9_31_md = 0, }, }; static const int digest_list_num = sizeof(digest_list) / sizeof(struct sk_digest_info); /** * Returns the digest info of the specified digest nid, or NULL if the digest * is not known. * * @param nid the OpenSSL nid of the digest * * @returns the address of the digest info or NULL if the digest was not found */ const struct sk_digest_info *SK_UTIL_get_digest_info(int digest_nid) { int i; for (i = 0; i < digest_list_num; i++) { if (digest_list[i].digest_nid == digest_nid) return &digest_list[i]; } return NULL; } /** * Checks if an exact duplicate of the name entry is part of the name already. */ static bool SK_UTILS_is_duplicate_name_entry(const X509_NAME *name, const X509_NAME_ENTRY *entry) { X509_NAME_ENTRY *ne; int count, i; count = X509_NAME_entry_count(name); for (i = 0; i < count; i++) { ne = X509_NAME_get_entry(name, i); if (ne == NULL) break; if (OBJ_cmp(X509_NAME_ENTRY_get_object(entry), X509_NAME_ENTRY_get_object(ne)) == 0 && ASN1_STRING_cmp(X509_NAME_ENTRY_get_data(entry), X509_NAME_ENTRY_get_data(ne)) == 0) return true; } return false; } /** * Parse an array of relative distinguished names and builds an X.509 subject * name. The RDNs are created with type MBSTRING_ASC, unless utf8 is requested, * then they are created with MBSTRING_UTF8. * To create a multiple-RDS name, prepend the RDS to add to the previous RDS * with a '+' character. * * @param name the X.509 name created. If *name is not NULL, then * the RDNs are added to the existing X.509 name. * @param rdns an array of strings, each string representing an * RDN in the form '[+]type=value'. If the type is * prepended with a '+', then this RDN is added to the * previous one. * @param num_rdns number of elements in the array. * @param utf8 if true, RDNs of type MBSTRING_UTF8 are created, * otherwise type is MBSTRING_ASC is used. * * @returns zero for success, a negative errno in case of an error: * -EINVAL: invalid parameter * -EBADMSG: an RDN is not formatted correctly * -EIO: OpenSSL failed to create an X.509 name entry * -EEXIST: if one of the name entries to add is a duplicate */ int SK_UTIL_build_subject_name(X509_NAME **name, const char *rdns[], size_t num_rdns, bool utf8) { char *rdn, *type, *value; X509_NAME_ENTRY *ne; X509_NAME *n; int rc = 0; bool multi; size_t i; if (name == NULL || rdns == NULL) return -EINVAL; if (*name != NULL) n = *name; else n = X509_NAME_new(); if (n == NULL) return -ENOMEM; for (i = 0; i < num_rdns; i++) { if (rdns[i] == NULL) { rc = -EINVAL; break; } rdn = strdup(rdns[i]); if (rdn == NULL) { rc = -ENOMEM; break; } multi = (rdn[0] == '+'); type = &rdn[multi ? 1 : 0]; for (value = type; *value != '=' && *value != '\0'; value++) ; if (*value != '=') { rc = -EBADMSG; free(rdn); break; } *value = '\0'; value++; ne = X509_NAME_ENTRY_create_by_txt(NULL, type, utf8 ? MBSTRING_UTF8 : MBSTRING_ASC, (unsigned char *)value, -1); if (ne == NULL) { rc = -EBADMSG; free(rdn); break; } if (SK_UTILS_is_duplicate_name_entry(n, ne)) { rc = -EEXIST; X509_NAME_ENTRY_free(ne); free(rdn); break; } rc = X509_NAME_add_entry(n, ne, -1, multi ? -1 : 0); free(rdn); X509_NAME_ENTRY_free(ne); if (rc != 1) { rc = -EIO; break; } rc = 0; } if (rc == 0) *name = n; else if (*name == NULL) X509_NAME_free(n); return rc; } /** * Compares X509 Extensions by their nid */ static int X509_EXTENSION_compfunc(const X509_EXTENSION * const *a, const X509_EXTENSION * const *b) { return (OBJ_obj2nid(X509_EXTENSION_get_object((X509_EXTENSION *)*a)) - OBJ_obj2nid(X509_EXTENSION_get_object((X509_EXTENSION *)*b))); } /** * Parse an array of textual X.509 certificate extensions and adds them to * either an X.509 certificate signing request, or an X.509 certificate. * * When adding extensions, a check is performed if an extension with the same * nid is already added. If so, a duplicate extension is not added, even if * its value is different from the existing one. * * @param cert the X.509 certificate to add the extensions to. * Either req or cert can be specified. * @param req the X.509 certificate signing request to add the * extensions to. Either req or cert can be specified. * @param exts an array of strings, each string representing an * certificate extension in the form 'type=value'. * can be NULL if num_exts is zero. * @param num_exts number of elements in the array. * @param addl_exts a stack of extensions to add (can be NULL) * * @returns zero for success, a negative errno in case of an error: * -EINVAL: invalid parameter * -EBADMSG: an extension is not formatted correctly * -EIO: OpenSSL failed to create an X.509 extension * -EEXIST: if one of the extensions to add is a duplicate */ int SK_UTIL_build_certificate_extensions(X509 *cert, X509_REQ *req, const char *exts[], size_t num_exts, const STACK_OF(X509_EXTENSION) *addl_exts) { STACK_OF(X509_EXTENSION) *sk_ext; char *ext, *type, *value; X509V3_CTX x509v3_ctx; int count, k, rc = 0; X509_EXTENSION *ex; size_t i; if (num_exts > 0 && exts == NULL) return -EINVAL; if (cert == NULL && req == NULL) return -EINVAL; if (cert != NULL && req != NULL) return -EINVAL; sk_ext = sk_X509_EXTENSION_new_null(); if (sk_ext == NULL) return -ENOMEM; sk_X509_EXTENSION_set_cmp_func(sk_ext, X509_EXTENSION_compfunc); for (i = 0; exts != NULL && i < num_exts; i++) { if (exts[i] == NULL) { rc = -EINVAL; break; } ext = strdup(exts[i]); if (ext == NULL) { rc = -ENOMEM; break; } type = &ext[0]; for (value = type; *value != '=' && *value != '\0'; value++) ; if (*value != '=') { rc = -EBADMSG; free(ext); break; } *value = '\0'; value++; rc = -EBADMSG; ex = X509V3_EXT_conf(NULL, NULL, type, value); if (ex != NULL) { if (sk_X509_EXTENSION_find(sk_ext, ex) >= 0) { rc = -EEXIST; X509_EXTENSION_free(ex); free(ext); break; } rc = sk_X509_EXTENSION_push(sk_ext, ex); if (rc < 1) { rc = -EIO; X509_EXTENSION_free(ex); free(ext); break; } rc = 0; } free(ext); } if (rc != 0) goto out; if (addl_exts != NULL) { count = sk_X509_EXTENSION_num(addl_exts); for (k = 0; k < count; k++) { ex = sk_X509_EXTENSION_value(addl_exts, k); if (ex != NULL) { if (sk_X509_EXTENSION_find(sk_ext, ex) >= 0) { rc = -EEXIST; break; } rc = sk_X509_EXTENSION_push(sk_ext, X509_EXTENSION_dup(ex)); if (rc < 1) { rc = -EIO; break; } rc = 0; } } } if (rc != 0) goto out; if (req != NULL && sk_X509_EXTENSION_num(sk_ext) > 0) { if (X509_REQ_add_extensions(req, sk_ext) != 1) rc = -EIO; sk_X509_EXTENSION_pop_free(sk_ext, X509_EXTENSION_free); sk_ext = NULL; goto out; } if (cert != NULL && sk_X509_EXTENSION_num(sk_ext) > 0) { X509V3_set_ctx_nodb(&x509v3_ctx); X509V3_set_ctx(&x509v3_ctx, cert, cert, NULL, NULL, 0); rc = 0; while ((ex = sk_X509_EXTENSION_pop(sk_ext)) != NULL) { if (rc == 0) { if (X509_add_ext(cert, ex, -1) != 1) rc = -EIO; } X509_EXTENSION_free(ex); } } out: if (sk_ext != NULL) sk_X509_EXTENSION_pop_free(sk_ext, X509_EXTENSION_free); return rc; } /** * Generates a serial number of a specified bit size by random and sets it * as serial number into the certificate. * * @param cert the certificate to set the serial number for * @param sn_bit_size the size of the serial number in bits * * @returns zero for success, a negative errno in case of an error: * -EINVAL: invalid parameter * -EIO: error during serial number generation */ int SK_UTIL_generate_x509_serial_number(X509 *cert, size_t sn_bit_size) { ASN1_INTEGER *ai = NULL; BIGNUM *bn = NULL; int rc; if (cert == NULL) return -EINVAL; bn = BN_new(); if (bn == NULL) return -ENOMEM; rc = BN_rand(bn, sn_bit_size, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY); if (rc != 1) { rc = -EIO; goto out; } ai = X509_get_serialNumber(cert); if (ai == NULL) { rc = -EIO; goto out; } if (BN_to_ASN1_INTEGER(bn, ai) == NULL) { rc = -EIO; goto out; } rc = 0; out: if (bn != NULL) BN_free(bn); return rc; } /** * Builds an DER encoded signature from a raw signature. * * @param raw_sig the raw signature to encode * @param raw_sig_len the size of the raw signature (2 times prime len) * @param sig a buffer for storing he encoded signature. If * NULL, then required size is returend in sig_len. * @param sig_len On entry: the size of the buffer in sig. * On exit: the size of the encoded sigature. * * @returns zero for success, a negative errno in case of an error: * -ERANGE: signature buffer is too small * -EIO: error during signature encoding */ int SK_UTIL_build_ecdsa_signature(const unsigned char *raw_sig, size_t raw_sig_len, unsigned char *sig, size_t *sig_len) { unsigned char *der = NULL; ECDSA_SIG *ec_sig = NULL; BIGNUM *bn_r = NULL; BIGNUM *bn_s = NULL; int rc = 0, der_len; ec_sig = ECDSA_SIG_new(); if (ec_sig == NULL) { rc = -ENOMEM; goto out; } bn_r = BN_bin2bn(raw_sig, raw_sig_len / 2, NULL); bn_s = BN_bin2bn(raw_sig + raw_sig_len / 2, raw_sig_len / 2, NULL); if (bn_r == NULL || bn_s == NULL) { rc = -EIO; goto out; } if (ECDSA_SIG_set0(ec_sig, bn_r, bn_s) != 1) { rc = -EIO; goto out; } bn_r = NULL; bn_s = NULL; der_len = i2d_ECDSA_SIG(ec_sig, NULL); if (der_len <= 0) { rc = -EIO; goto out; } if (sig == NULL) { *sig_len = der_len; goto out; } if (der_len > (int)*sig_len) { rc = -ERANGE; goto out; } memset(sig, 0, *sig_len); der = sig; der_len = i2d_ECDSA_SIG(ec_sig, &der); if (der_len <= 0) { rc = -EIO; goto out; } *sig_len = der_len; out: if (ec_sig != NULL) ECDSA_SIG_free(ec_sig); if (bn_r != NULL) BN_free(bn_r); if (bn_s != NULL) BN_free(bn_s); return rc; } /** * Reads a X.509 certificate from the specified PEM file. * * @param pem_filename the name of the PEM file to read * @param cert on Return: the X.509 certificate object * * @returns zero for success, a negative errno in case of an error: * -EINVAL: invalid parameter * -EIO: error during reading in the certificate * any other errno as returned by fopen */ int SK_UTIL_read_x509_certificate(const char *pem_filename, X509 **cert) { FILE *fp; if (pem_filename == NULL || cert == NULL) return -EINVAL; fp = fopen(pem_filename, "r"); if (fp == NULL) return -errno; *cert = PEM_read_X509(fp, NULL, NULL, NULL); fclose(fp); if (*cert == NULL) return -EIO; return 0; } /** * Writes a X.509 certificate to the specified PEM file. * * @param pem_filename the name of the PEM file to write to * @param cert the X.509 certificate object to write * * @returns zero for success, a negative errno in case of an error: * -EINVAL: invalid parameter * -EIO: error during writing out the certificate * any other errno as returned by fopen */ int SK_UTIL_write_x509_certificate(const char *pem_filename, X509 *cert) { FILE *fp; int rc; if (pem_filename == NULL || cert == NULL) return -EINVAL; fp = fopen(pem_filename, "w"); if (fp == NULL) return -errno; rc = PEM_write_X509(fp, cert); fclose(fp); if (rc != 1) return -EIO; return 0; } /** * Writes a X.509 certificate signing request to the specified PEM file. * * @param pem_filename the name of the PEM file to write to * @param req the X.509 request object to write * @param new_hdr if true, output "NEW" in the PEM header lines * * @returns zero for success, a negative errno in case of an error: * -EINVAL: invalid parameter * -EIO: error during writing out the certificate * any other errno as returned by fopen */ int SK_UTIL_write_x509_request(const char *pem_filename, X509_REQ *req, bool new_hdr) { FILE *fp; int rc; if (pem_filename == NULL || req == NULL) return -EINVAL; fp = fopen(pem_filename, "w"); if (fp == NULL) return -errno; if (new_hdr) rc = PEM_write_X509_REQ_NEW(fp, req); else rc = PEM_write_X509_REQ(fp, req); fclose(fp); if (rc != 1) return -EIO; return 0; } /** * Reads a secure key from the specified file. * * @param filename the name of the file to read * @param key_blob on Return: the key blob * @param key_blob_len on Entry: the size of the buffer, * on Return: the size of the key blob read * * @returns zero for success, a negative errno in case of an error */ int SK_UTIL_read_key_blob(const char *filename, unsigned char *key_blob, size_t *key_blob_len) { size_t count, size; struct stat sb; FILE *fp; if (filename == NULL || key_blob_len == NULL) return -EINVAL; if (stat(filename, &sb)) return -errno; size = sb.st_size; if (key_blob == NULL) { *key_blob_len = size; return 0; } if (size > *key_blob_len) { *key_blob_len = size; return -ERANGE; } fp = fopen(filename, "r"); if (fp == NULL) return -errno; count = fread(key_blob, 1, size, fp); if (count != size) { fclose(fp); return -EIO; } *key_blob_len = size; fclose(fp); return 0; } /** * Writes a secure key to the specified file. * * @param filename the name of the file to write * @param key_blob the key blob * @param key_blob_len the size of the key blob * * @returns zero for success, a negative errno in case of an error */ int SK_UTIL_write_key_blob(const char *filename, unsigned char *key_blob, size_t key_blob_len) { size_t count; FILE *fp; if (filename == NULL || key_blob == NULL || key_blob_len == 0) return -EINVAL; fp = fopen(filename, "w"); if (fp == NULL) return -errno; count = fwrite(key_blob, 1, key_blob_len, fp); if (count != key_blob_len) { fclose(fp); return -EIO; } fclose(fp); return 0; } /** * Reads a public key from the specified PEM file. * * @param pem_filename the name of the PEM file to read * @param pkey on Return: the PKEY object * * @returns zero for success, a negative errno in case of an error: * -EINVAL: invalid parameter * -EIO: error during reading in the certificate * any other errno as returned by fopen */ int SK_UTIL_read_public_key(const char *pem_filename, EVP_PKEY **pkey) { FILE *fp; if (pem_filename == NULL || pkey == NULL) return -EINVAL; fp = fopen(pem_filename, "r"); if (fp == NULL) return -errno; *pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL); fclose(fp); if (*pkey == NULL) return -EIO; return 0; } /** * Writes a public key to the specified PEM file. * * @param pem_filename the name of the PEM file to write to * @param pkey the PKEY object to write * * @returns zero for success, a negative errno in case of an error: * -EINVAL: invalid parameter * -EIO: error during writing out the certificate * any other errno as returned by fopen */ int SK_UTIL_write_public_key(const char *pem_filename, EVP_PKEY *pkey) { FILE *fp; int rc; if (pem_filename == NULL || pkey == NULL) return -EINVAL; fp = fopen(pem_filename, "w"); if (fp == NULL) return -errno; rc = PEM_write_PUBKEY(fp, pkey); fclose(fp); if (rc != 1) return -EIO; return 0; } s390-tools-2.38.0/libutil/000077500000000000000000000000001502674226300151135ustar00rootroot00000000000000s390-tools-2.38.0/libutil/Makefile000066400000000000000000000005421502674226300165540ustar00rootroot00000000000000include ../common.mak lib := libutil.a sources := $(filter-out %_example.c,$(wildcard *.c)) objects := $(patsubst %.c,%.o,$(sources)) examples := $(patsubst %.c,%,$(wildcard *_example.c)) all: $(lib) examples: $(examples) $(examples): %: %.o $(lib) $(lib): $(objects) $(lib): ALL_CFLAGS += -fPIC install: all clean: rm -f *.o $(lib) $(examples) s390-tools-2.38.0/libutil/util_arch.c000066400000000000000000000073511502674226300172370ustar00rootroot00000000000000/* * util - Utility function library * * General architecture helpers * * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include "lib/util_path.h" #include "lib/util_file.h" #include "lib/util_arch.h" #define PROC_SYSINFO "/proc/sysinfo" #define PROC_SYSINFO_TYPE "Type:" #define HSA_SIZE_32M (32 * 1024 * 1024) #define HSA_SIZE_512M (512 * 1024 * 1024) /** * Get the type of the underlying architecture. * * @returns Type of the underlying architecture */ int util_arch_machine_type(void) { int type = UTIL_ARCH_MACHINE_TYPE_UNKNOWN; char *line = NULL; FILE *fp; fp = fopen(PROC_SYSINFO, "r"); if (!fp) return type; while (fscanf(fp, "%m[^\n]\n", &line) == 1) { int n = sscanf(line, PROC_SYSINFO_TYPE "%d", &type); free(line); if (n == 1) break; } fclose(fp); return type; } /** * Returns a human-readable string of the current machine type. * * @returns Pointer to a read-only string corresponding to the current machine * type */ const char *util_arch_machine_type_str(void) { return util_arch_machine_type_to_str(util_arch_machine_type()); } /** * Convert a machine type to a human-readable string. * * @param[in] type Type of the underlying architecture * * @returns Pointer to a read-only string corresponding to the given machine * type */ const char *util_arch_machine_type_to_str(int type) { switch (type) { case UTIL_ARCH_MACHINE_TYPE_Z10_EC: return "IBM System z10 EC"; case UTIL_ARCH_MACHINE_TYPE_Z10_BC: return "IBM System z10 BC"; case UTIL_ARCH_MACHINE_TYPE_ZE_196: return "IBM zEnterprise 196"; case UTIL_ARCH_MACHINE_TYPE_ZE_114: return "IBM zEnterprise 114"; case UTIL_ARCH_MACHINE_TYPE_ZE_EC12: return "IBM zEnterprise EC12"; case UTIL_ARCH_MACHINE_TYPE_ZE_BC12: return "IBM zEnterprise BC12"; case UTIL_ARCH_MACHINE_TYPE_Z13: return "IBM z13"; case UTIL_ARCH_MACHINE_TYPE_Z13_S: return "IBM z13s"; case UTIL_ARCH_MACHINE_TYPE_Z14: return "IBM z14"; case UTIL_ARCH_MACHINE_TYPE_Z14_ZR1: return "IBM z14 ZR1"; case UTIL_ARCH_MACHINE_TYPE_Z15: case UTIL_ARCH_MACHINE_TYPE_Z15_T02: return "IBM z15"; case UTIL_ARCH_MACHINE_TYPE_Z16: case UTIL_ARCH_MACHINE_TYPE_Z16_A02: return "IBM z16"; case UTIL_ARCH_MACHINE_TYPE_Z17: case UTIL_ARCH_MACHINE_TYPE_Z17_2: return "IBM z17"; default: return "Unknown machine type"; } } /** * Returns the maximum size of HSA memory in bytes on this architecture. * * @returns Maximum HSA size in bytes */ unsigned long util_arch_hsa_maxsize(void) { unsigned long hsa_size = 0; char *path; int rc; path = util_path_sysfs("firmware/dump/dump_area_size"); if (util_path_exists(path)) { rc = util_file_read_ul(&hsa_size, 10, path); if (rc) hsa_size = 0; } free(path); /* * Fall back in case of failed attempt to obtain dump area size * from sysfs for some reason (e.g. no kernel support of * the sysfs attribute /sys/firmware/dump/dump_area_size). * For all machine types starting with z15 we can safely assume * at least 512M of dump area size, otherwise, only 32M can be * safely assumed. */ if (!hsa_size) { switch (util_arch_machine_type()) { case UTIL_ARCH_MACHINE_TYPE_Z10_EC: case UTIL_ARCH_MACHINE_TYPE_Z10_BC: case UTIL_ARCH_MACHINE_TYPE_ZE_196: case UTIL_ARCH_MACHINE_TYPE_ZE_114: case UTIL_ARCH_MACHINE_TYPE_ZE_EC12: case UTIL_ARCH_MACHINE_TYPE_ZE_BC12: case UTIL_ARCH_MACHINE_TYPE_Z13: case UTIL_ARCH_MACHINE_TYPE_Z13_S: case UTIL_ARCH_MACHINE_TYPE_Z14: case UTIL_ARCH_MACHINE_TYPE_Z14_ZR1: hsa_size = HSA_SIZE_32M; break; default: hsa_size = HSA_SIZE_512M; } } return hsa_size; } s390-tools-2.38.0/libutil/util_arch_example.c000066400000000000000000000027231502674226300207500ustar00rootroot00000000000000/** * util_arch_example - Example program for util_arch * * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include //! [code] #include "lib/util_arch.h" #include "lib/util_opt.h" #include "lib/util_prg.h" static const struct util_prg prg = { .desc = "Example for util_arch.", .copyright_vec = { { .owner = "IBM Corp.", .pub_first = 2021, .pub_last = 2021, }, UTIL_PRG_COPYRIGHT_END } }; static struct util_opt opt_vec[] = { UTIL_OPT_HELP, UTIL_OPT_VERSION, UTIL_OPT_END }; int main(int argc, char *argv[]) { int mach_type; unsigned long hsa_maxsize; util_prg_init(&prg); util_opt_init(opt_vec, NULL); while (1) { int opt = util_opt_getopt_long(argc, argv); if (opt == -1) break; switch (opt) { case 'h': util_prg_print_help(); util_opt_print_help(); exit(EXIT_SUCCESS); case 'v': util_prg_print_version(); exit(EXIT_SUCCESS); case '?': default: fprintf(stderr, "Try '--help' for more information.\n"); exit(EXIT_FAILURE); } } mach_type = util_arch_machine_type(); hsa_maxsize = util_arch_hsa_maxsize(); printf("Machine type: %s (%d)\n", util_arch_machine_type_to_str(mach_type), mach_type); printf("This machine type: %s\n", util_arch_machine_type_str()); printf("HSA max. size: %lu MiB\n", hsa_maxsize / (1024 * 1024)); return EXIT_SUCCESS; } //! [code] s390-tools-2.38.0/libutil/util_base.c000066400000000000000000000111601502674226300172250ustar00rootroot00000000000000/* * util - Utility function library * * General helper functions * * Copyright IBM Corp. 2013, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include "lib/util_base.h" #include "lib/util_libc.h" /* * Print hexdump for buffer with variable group parameter */ void util_hexdump_grp(FILE *fh, const char *tag, const void *data, int grp, int count, int indent) { const char *buf = data; int i, first = 1; for (i = 0; i < count; i++) { if (first) { fprintf(fh, "%*s", indent, ""); if (tag) fprintf(fh, "%s: ", tag); fprintf(fh, "%08x: ", i); first = 0; } fprintf(fh, "%02x", buf[i]); if (i % 16 == 15 || i + 1 == count) { fprintf(fh, "\n"); first = 1; } else if (i % grp == grp - 1) { fprintf(fh, " "); } } } /* * Print hexdump for buffer with fix grp parameter */ void util_hexdump(FILE *fh, const char *tag, const void *data, int count) { util_hexdump_grp(fh, tag, data, sizeof(long), count, 0); } #define MAX_CHARS_PER_LINE 80 /* * Print string with indentation * * Print a string while accounting for a given indent value, characters per line * limit, and line breaks ('\n') within the string. The first line has to be * indented manually. * * @param[in] str String that should be printed * @param[in] indent Indentation for printing */ void util_print_indented(const char *str, int indent) { char *word, *line, *desc, *desc_ptr; int word_len, pos = indent; desc = desc_ptr = util_strdup(str); line = strsep(&desc, "\n"); while (line) { word = strsep(&line, " "); pos = indent; while (word) { word_len = strlen(word); if (pos + word_len + 1 > MAX_CHARS_PER_LINE) { printf("\n%*s", indent, ""); pos = indent; } if (pos == indent) printf("%s", word); else printf(" %s", word); pos += word_len + 1; word = strsep(&line, " "); } if (desc) printf("\n%*s", indent, ""); line = strsep(&desc, "\n"); } printf("\n"); free(desc_ptr); } /** * Determines the absolute name of an s390-tools system directory * (it could be data, or library directory) */ static const char *util_sysdir(const char *env_var, const char *default_dir) { return secure_getenv(env_var) ?: default_dir; } /** * Determines the absolute name of a file installed in the s390-tools * system directory */ static const char *util_sysdir_path(const char *filename, const char *dirname) { static char libdir_pathname[PATH_MAX]; int ret; ret = snprintf(libdir_pathname, sizeof(libdir_pathname), "%s/%s", dirname, filename); if (ret < 0 || ret >= (int)sizeof(libdir_pathname)) errx(EXIT_FAILURE, "Could not compose absolute pathname of %s and %s", dirname, filename); return libdir_pathname; } /** * Determines the absolute name of a s390-tools library directory * * Resources are handled by the library * * @returns Pointer to a buffer, which contains the null-terminated name */ const char *util_libdir(void) { return util_sysdir("S390TOOLS_LIBDIR", TOOLS_LIBDIR); } /** * Determines the absolute name of a file installed in the s390-tools * library directory by its relative name. * * Resources are handled by the library. A second call of this function * overwrites previously returned data. Care must be taken when attempting * to do a second call. * * @param[in] filename Null-terminated file name relative to the s390-tools * library directory * @returns Pointer to a buffer of PATH_MAX size, which contains the * null-terminated absolute name */ const char *util_libdir_path(const char *filename) { return util_sysdir_path(filename, util_libdir()); } /** * Determines the absolute name of a s390-tools data directory * * Resources are handled by the library * * @returns Pointer to a buffer which contains the null-terminated name */ const char *util_datadir(void) { return util_sysdir("S390TOOLS_DATADIR", TOOLS_DATADIR); } /** * Determines the absolute name of a file installed in the s390-tools * data directory by its relative name. * * Resources are handled by the library. A second call of this function * overwrites previously returned data. Care must be taken when attempting * to do a second call. * * @param[in] filename Null-terminated file name relative to the s390-tools * data directory * @returns Pointer to a buffer of PATH_MAX size, which contains the * null-terminated absolute name */ const char *util_datadir_path(const char *filename) { return util_sysdir_path(filename, util_datadir()); } s390-tools-2.38.0/libutil/util_base_example.c000066400000000000000000000017141502674226300207440ustar00rootroot00000000000000/** * util_base_example - Example program for util_base * * Copyright 2017 IBM Corp. * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ //! [code] #include #include "lib/util_base.h" #define INDENTATION 24 const char *msg = "There is a theory which states that if ever anybody discovers exactly " "what the Universe is for and why it is here, it will instantly disappear " "and be replaced by something even more ...\n" "\n" " ... bizarre\n" " ... and inexplicable.\n" "\n" "There is another theory which states that this has already happened."; /* * Demonstrate util_base functions */ int main(void) { printf("TEST: util_print_indented(msg, %d)\n", INDENTATION); printf("----------------------------------\n"); printf("%-*s", INDENTATION, "Douglas Adams:"); util_print_indented(msg, INDENTATION); return EXIT_SUCCESS; } //! [code] s390-tools-2.38.0/libutil/util_file.c000066400000000000000000000370521502674226300172420ustar00rootroot00000000000000/* * util - Utility function library * * Read and write files * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #include "lib/util_base.h" #include "lib/util_exit_code.h" #include "lib/util_file.h" #include "lib/util_libc.h" #include "lib/util_panic.h" #include "lib/util_prg.h" #define READ_CHUNK_SIZE 4096 extern const char *toolname; /* * Read the first line of a file into given buffer */ static int file_gets(char *str, size_t size, const char *path) { char *p, *end; FILE *fp; int rc; /* In case of error we always return empty string */ str[0] = 0; /* Read the string */ fp = fopen(path, "r"); if (!fp) return -1; p = fgets(str, size, fp); if (p) { /* Check for end of line */ end = memchr(str, '\n', size); if (end) *end = 0; else str[size - 1] = 0; } if (strlen(str) == 0) { rc = -1; goto out_fclose; } rc = 0; out_fclose: fclose(fp); return rc; } /** * Read the first line of a file * * If read is successful 'str' contains first line of a file without the * trailing newline. If read fails, an empty string is returned for 'str' * The resulting string will always be null-terminated. * * @param[out] str Result buffer * @param[in] size Size of the result buffer * @param[in] fmt Format string for generation of the path name * @param[in] ... Parameters for format string * * @retval 0 File was read * @retval -1 Error while reading file */ int util_file_read_line(char *const str, size_t size, const char *fmt, ...) { char path[PATH_MAX]; va_list ap; /* Construct the file name */ UTIL_VSPRINTF(path, fmt, ap); return file_gets(str, size, path); } /* * Write string to a file */ static int file_puts(const char *str, const char *path) { FILE *fp; int rc; /* write the string */ fp = fopen(path, "w"); if (!fp) return -1; if (fputs(str, fp) == EOF) { rc = -1; goto out_fclose; } rc = 0; out_fclose: if (fclose(fp)) return -1; return rc; } /** * Write string to a file without the terminating null byte * * @param[in] str Content is to be written * @param[in] fmt Format string for generation of the path name * @param[in] ... Parameters for format string * * @retval 0 Write was successful * @retval -1 Error while writing file */ int util_file_write_s(const char *str, const char *fmt, ...) { char path[PATH_MAX]; va_list ap; /* Construct the file name */ UTIL_VSPRINTF(path, fmt, ap); return file_puts(str, path); } /** * Write signed long value to a file according to given base * * @param[in] val Value is to be written * @param[in] base Base for conversion, either 8, 10, or 16 * @param[in] fmt Format string for generation of the path name * @param[in] ... Parameters for format string * * @retval 0 Write was successful * @retval -1 Error while writing file */ int util_file_write_l(long val, int base, const char *fmt, ...) { char *str, path[PATH_MAX]; va_list ap; int rc; /* Construct the file name */ UTIL_VSPRINTF(path, fmt, ap); switch (base) { case 8: util_asprintf(&str, "%lo", val); break; case 10: util_asprintf(&str, "%ld", val); break; case 16: util_asprintf(&str, "%lx", val); break; default: util_panic("Invalid base: %d\n", base); } rc = file_puts(str, path); free(str); return rc; } /** * Write signed long long value to a file according to given base * * @param[in] val Value is to be written * @param[in] base Base for conversion, either 8, 10, or 16 * @param[in] fmt Format string for generation of the path name * @param[in] ... Parameters for format string * * @retval 0 Write was successful * @retval -1 Error while writing file */ int util_file_write_ll(long long val, int base, const char *fmt, ...) { char *str, path[PATH_MAX]; va_list ap; int rc; /* Construct the file name */ UTIL_VSPRINTF(path, fmt, ap); switch (base) { case 8: util_asprintf(&str, "%llo", val); break; case 10: util_asprintf(&str, "%lld", val); break; case 16: util_asprintf(&str, "%llx", val); break; default: util_panic("Invalid base: %d\n", base); } rc = file_puts(str, path); free(str); return rc; } /** * Write unsigned long value to a file according to given base * * @param[in] val Value is to be written * @param[in] base Base for conversion, either 8, 10, or 16 * @param[in] fmt Format string for generation of the path name * @param[in] ... Parameters for format string * * @retval 0 Write was successful * @retval -1 Error while writing file */ int util_file_write_ul(unsigned long val, int base, const char *fmt, ...) { char *str, path[PATH_MAX]; va_list ap; int rc; /* Construct the file name */ UTIL_VSPRINTF(path, fmt, ap); switch (base) { case 8: util_asprintf(&str, "%lo", val); break; case 10: util_asprintf(&str, "%lu", val); break; case 16: util_asprintf(&str, "%lx", val); break; default: util_panic("Invalid base: %d\n", base); } rc = file_puts(str, path); free(str); return rc; } /** * Write unsigned long long value to a file according to given base * * @param[in] val Value is to be written * @param[in] base Base for conversion, either 8, 10, or 16 * @param[in] fmt Format string for generation of the path name * @param[in] ... Parameters for format string * * @retval 0 Write was successful * @retval -1 Error while writing file */ int util_file_write_ull(unsigned long long val, int base, const char *fmt, ...) { char *str, path[PATH_MAX]; va_list ap; int rc; /* Construct the file name */ UTIL_VSPRINTF(path, fmt, ap); switch (base) { case 8: util_asprintf(&str, "%llo", val); break; case 10: util_asprintf(&str, "%llu", val); break; case 16: util_asprintf(&str, "%llx", val); break; default: util_panic("Invalid base: %d\n", base); } rc = file_puts(str, path); free(str); return rc; } /** * Read a file and convert it to signed int according to given base * * @param[out] val Buffer for value * @param[in] base Base for conversion, either 8, 10, or 16 * @param[in] fmt Format string for generation of the path name * @param[in] ... Parameters for format string * * @retval 0 Integer has been read correctly * @retval -1 Error while reading file */ int util_file_read_i(int *val, int base, const char *fmt, ...) { char path[PATH_MAX], buf[512]; va_list ap; int count; /* Construct the file name */ UTIL_VSPRINTF(path, fmt, ap); if (file_gets(buf, sizeof(buf), path)) return -1; switch (base) { case 8: count = sscanf(buf, "%o", val); break; case 10: count = sscanf(buf, "%d", val); break; case 16: count = sscanf(buf, "%x", val); break; default: util_panic("Invalid base: %d\n", base); } return (count == 1) ? 0 : -1; } /** * Read a file and convert it to signed long according to given base * * @param[out] val Buffer for value * @param[in] base Base for conversion, either 8, 10, or 16 * @param[in] fmt Format string for generation of the path name * @param[in] ... Parameters for format string * * @retval 0 Long integer has been read correctly * @retval -1 Error while reading file */ int util_file_read_l(long *val, int base, const char *fmt, ...) { char path[PATH_MAX], buf[512]; va_list ap; int count; /* Construct the file name */ UTIL_VSPRINTF(path, fmt, ap); if (file_gets(buf, sizeof(buf), path)) return -1; switch (base) { case 8: count = sscanf(buf, "%lo", val); break; case 10: count = sscanf(buf, "%ld", val); break; case 16: count = sscanf(buf, "%lx", val); break; default: util_panic("Invalid base: %d\n", base); } return (count == 1) ? 0 : -1; } /** * Read a file and convert it to signed long long according to given base * * @param[out] val Buffer for value * @param[in] base Base for conversion, either 8, 10, or 16 * @param[in] fmt Format string for generation of the path name * @param[in] ... Parameters for format string * * @retval 0 Long integer has been read correctly * @retval -1 Error while reading file */ int util_file_read_ll(long long *val, int base, const char *fmt, ...) { char path[PATH_MAX], buf[512]; va_list ap; int count; /* Construct the file name */ UTIL_VSPRINTF(path, fmt, ap); if (file_gets(buf, sizeof(buf), path)) return -1; switch (base) { case 8: count = sscanf(buf, "%llo", val); break; case 10: count = sscanf(buf, "%lld", val); break; case 16: count = sscanf(buf, "%llx", val); break; default: util_panic("Invalid base: %d\n", base); } return (count == 1) ? 0 : -1; } /** * Read a file and convert it to unsigned int according to given base * * @param[out] val Buffer for value * @param[in] base Base for conversion, either 8, 10, or 16 * @param[in] fmt Format string for generation of the path name * @param[in] ... Parameters for format string * * @retval 0 Integer has been read correctly * @retval -1 Error while reading file */ int util_file_read_ui(unsigned int *val, int base, const char *fmt, ...) { char path[PATH_MAX], buf[512]; va_list ap; int count; /* Construct the file name */ UTIL_VSPRINTF(path, fmt, ap); if (file_gets(buf, sizeof(buf), path)) return -1; switch (base) { case 8: count = sscanf(buf, "%o", val); break; case 10: count = sscanf(buf, "%u", val); break; case 16: count = sscanf(buf, "%x", val); break; default: util_panic("Invalid base: %d\n", base); } return (count == 1) ? 0 : -1; } /** * Read a file and convert it to unsigned long according to given base * * @param[out] val Buffer for value * @param[in] base Base for conversion, either 8, 10, or 16 * @param[in] fmt Format string for generation of the path name * @param[in] ... Parameters for format string * * @retval 0 Long integer has been read correctly * @retval -1 Error while reading file */ int util_file_read_ul(unsigned long *val, int base, const char *fmt, ...) { char path[PATH_MAX], buf[512]; va_list ap; int count; /* Construct the file name */ UTIL_VSPRINTF(path, fmt, ap); if (file_gets(buf, sizeof(buf), path)) return -1; switch (base) { case 8: count = sscanf(buf, "%lo", val); break; case 10: count = sscanf(buf, "%lu", val); break; case 16: count = sscanf(buf, "%lx", val); break; default: util_panic("Invalid base: %d\n", base); } return (count == 1) ? 0 : -1; } /** * Read a file and convert it to unsigned long long according to given base * * @param[out] val Buffer for value * @param[in] base Base for conversion, either 8, 10, or 16 * @param[in] fmt Format string for generation of the path name * @param[in] ... Parameters for format string * * @retval 0 Long integer has been read correctly * @retval -1 Error while reading file */ int util_file_read_ull(unsigned long long *val, int base, const char *fmt, ...) { char path[PATH_MAX], buf[512]; va_list ap; int count; /* Construct the file name */ UTIL_VSPRINTF(path, fmt, ap); if (file_gets(buf, sizeof(buf), path)) return -1; switch (base) { case 8: count = sscanf(buf, "%llo", val); break; case 10: count = sscanf(buf, "%llu", val); break; case 16: count = sscanf(buf, "%llx", val); break; default: util_panic("Invalid base: %d\n", base); } return (count == 1) ? 0 : -1; } /** * Read a file and convert it according to format string * * @param[in] path File name to read * @param[in] fmt Format string for parsing the content * @param[out] ... Parameters for format string * * @retval != -1 Number of values parsed correctly * @retval -1 Error while reading file */ int util_file_read_va(const char *path, const char *fmt, ...) { char buf[512]; va_list ap; int ret; if (file_gets(buf, sizeof(buf), path)) return -1; va_start(ap, fmt); ret = vsscanf(buf, fmt, ap); va_end(ap); if (ret == EOF) return -1; return ret; } /** * Print an error message indicating an out-of-memory situation and exit. */ static void oom(void) { fprintf(stderr, "Out of memory\n"); /* We can't rely on our clean-up routines to work reliably during an * OOM situation, so just exit here. */ exit(UTIL_EXIT_OUT_OF_MEMORY); } /** * Read all data from @fd and return address of resulting buffer in * @buffer_ptr. If @size_ptr is non-zero, use it to store the size of the * resulting buffer. Return %UTIL_EXIT_OK on success. * * @param[in] fd File descriptor to read data from * @param[in, out] buffer_ptr Buffer to read data into * @param[in, out] size_ptr Buffer to save size of data read into buffer_ptr * * @retval 0 read was successful * @retval UTIL_EXIT_RUNTIME_ERROR error while reading file */ util_exit_code_t util_file_read_fd_buf(FILE *fd, void **buffer_ptr, size_t *size_ptr) { char *buffer = NULL; size_t done = 0; while (!feof(fd)) { buffer = realloc(buffer, done + READ_CHUNK_SIZE); if (!buffer) oom(); done += fread(&buffer[done], 1, READ_CHUNK_SIZE, fd); if (ferror(fd)) { free(buffer); return UTIL_EXIT_RUNTIME_ERROR; } } buffer = realloc(buffer, done); if (!buffer && done > 0) oom(); *buffer_ptr = buffer; if (size_ptr) *size_ptr = done; return UTIL_EXIT_OK; } /** * Read text from @fd and return resulting NULL-terminated text buffer. * If @chomp is non-zero, remove trailing newline character. Return %NULL * on error or when unprintable characters are read. * * @param[in] fd File descriptor to read data from * @param[in] chomp Flag to indicate trailing newlines should be removed * * @retval NULL Error reading from fd * @retval != 0 Data read successfully, buffer returned */ char *util_file_read_fd(FILE *fd, int chomp) { char *buffer; size_t done, i; if (util_file_read_fd_buf(fd, (void **) &buffer, &done)) return NULL; /* Prevent over-read if buffer is larger than amount of read characters */ if (buffer) done = MIN(done, strnlen(buffer, done)); /* Check if this is a text file at all (required to filter out * binary sysfs attributes). */ for (i = 0; i < done; i++) { if (!isgraph(buffer[i]) && !isspace(buffer[i])) { free(buffer); return NULL; } } /* Remove trailing new-line character if requested. */ if (chomp && done > 0 && buffer[done - 1] == '\n') done--; /* NULL-terminate. */ buffer = realloc(buffer, done + 1); if (!buffer) oom(); buffer[done] = 0; return buffer; } /** * Read file as text and return NULL-terminated contents. Remove trailing * newline if CHOMP is specified. * * @param[in] path Path to the file that will be read from * @param[in] chomp Flag to indicate trailing newlines should be removed * * @retval NULL Error reading from file * @retval != 0 File read successfully, contents returned in buffer */ char *util_file_read_text_file(const char *path, int chomp) { char *buffer = NULL; FILE *fd; fd = fopen(path, "r"); if (!fd) goto out; buffer = util_file_read_fd(fd, chomp); fclose(fd); out: if (!buffer) { fprintf(stderr, "Could not read file %s: %s\n", path, strerror(errno)); } return buffer; } s390-tools-2.38.0/libutil/util_file_example.c000066400000000000000000000035671502674226300207610ustar00rootroot00000000000000/** * util_file_example - Example program for util_file * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ //! [code] #include #include #include #include #include "lib/util_file.h" /* * Write buffer to file and read it back again */ int main(void) { char buf_wr[4096], buf_rd[4096]; unsigned long long value_ull; long value_l; /* Generate input */ sprintf(buf_wr, "Say something interesting!\nSecond line\n"); printf("Write.....:\n%s", buf_wr); /* Write string to file */ if (util_file_write_s(buf_wr, "/tmp/%s", "testfile")) { perror("util_file_write_s failed\n"); return EXIT_FAILURE; } /* Read back first line of file */ if (util_file_read_line(buf_rd, sizeof(buf_rd), "/tmp/%s", "testfile")) { perror("util_file_read_line failed\n"); return EXIT_FAILURE; } printf("Read......: %s\n", buf_rd); /* Write long to file */ printf("Write.....: %ld\n", 4711L); if (util_file_write_l(4711L, 10, "/tmp/%s", "testfile")) { perror("util_file_write failed\n"); return EXIT_FAILURE; } /* Read back long from file */ if (util_file_read_l(&value_l, 10, "/tmp/%s", "testfile")) { perror("util_file_read_l failed\n"); return EXIT_FAILURE; } printf("Read......: %ld\n", value_l); /* Write long long hexadecimal to file */ printf("Write.....: 0x%llx\n", 0x4712ULL); if (util_file_write_ull(0x4712ULL, 16, "/tmp/%s", "testfile")) { perror("util_file_write failed\n"); return EXIT_FAILURE; } /* Read back long long hexadecimal from file */ if (util_file_read_ull(&value_ull, 16, "/tmp/%s", "testfile")) { perror("util_file_read_ull failed\n"); return EXIT_FAILURE; } printf("Read......: 0x%llx\n", value_ull); /* Remove file */ unlink("/tmp/testfile"); return EXIT_SUCCESS; } //! [code] s390-tools-2.38.0/libutil/util_fmt.c000066400000000000000000000406731502674226300171140ustar00rootroot00000000000000/* * util - Utility function library * * Format structured data as key-value pairs, JSON, or CSV * * Copyright IBM Corp. 2024 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/util_base.h" #include "lib/util_fmt.h" #include "lib/util_libc.h" #include "lib/util_rec.h" #include "lib/zt_common.h" struct obj_t { char *name; bool is_list; bool is_row; bool is_prefix; unsigned int index; }; struct key_t { char *name; bool persist; }; static struct { enum util_fmt_t type; FILE *fd; int fileno; /* Format control. */ bool hide_prefix; bool hide_inval; bool quote_all; bool do_filter; bool do_warn; bool hide_meta; bool handle_int; int api_level; const char *nl; /* JSON specifics. */ unsigned int ind_base; unsigned int ind_width; char ind_char; bool meta_done; /* CSV specifics. */ struct util_rec *csv_rec; bool csv_hdr; bool csv_data; /* State. */ unsigned int lvl; struct obj_t *objs; unsigned int num_objs; struct key_t *keys; unsigned int num_keys; struct sigaction old_int; struct sigaction old_term; /* Methods. */ void (*obj_start)(struct obj_t *parent, struct obj_t *obj); void (*obj_end)(struct obj_t *parent, struct obj_t *obj); void (*map)(struct obj_t *parent, unsigned int mflags, const char *key, const char *val); void (*term)(void); } f; #define fwarn(fmt, ...) \ do { if (f.do_warn) warnx(fmt, ##__VA_ARGS__); } while (0) /* Map format name to format ID. */ static const struct { const char *name; enum util_fmt_t fmt; } formats[] = { { "json", FMT_JSON }, { "json-seq", FMT_JSONSEQ }, { "pairs", FMT_PAIRS }, { "csv", FMT_CSV }, }; /* Signal mask for blocking INT and TERM signals. */ static sigset_t no_int_mask; bool util_fmt_name_to_type(const char *name, enum util_fmt_t *type) { unsigned int i; for (i = 0; i < ARRAY_SIZE(formats); i++) { if (strcasecmp(name, formats[i].name) == 0) { *type = formats[i].fmt; return true; } } return false; } static void safe_write(const char *str) { size_t done, todo; ssize_t rc; if (f.fileno < 0) return; for (done = 0; (todo = strlen(&str[done])) > 0; done += (size_t)rc) { rc = write(f.fileno, &str[done], todo); if (rc <= 0) return; } } static void _indent(unsigned int off, bool safe) { unsigned int num, i; if (f.type == FMT_JSONSEQ) return; num = f.ind_base + off; if (f.type == FMT_JSON && f.lvl > 0) num += f.lvl - 1; for (i = 0; i < num * f.ind_width; i++) { if (!safe) { fputc(f.ind_char, f.fd); } else if (f.fileno >= 0) { if (write(f.fileno, &f.ind_char, 1) <= 0) return; } } } #define indent(x) _indent(x, false) static void obj_free(struct obj_t *obj) { free(obj->name); memset(obj, 0, sizeof(*obj)); } static void disable_int(sigset_t *saved) { if (f.handle_int) sigprocmask(SIG_BLOCK, &no_int_mask, saved); } static void enable_int(sigset_t *saved) { if (f.handle_int) { /* Ensure latest updates are flushed to file descriptor. */ fflush(f.fd); sigprocmask(SIG_SETMASK, saved, NULL); } } static void int_handler(int signum) { struct sigaction *old; if (f.term) f.term(); /* Re-install and call original handler. */ old = (signum == SIGINT) ? &f.old_int : &f.old_term; sigaction(signum, old, NULL); raise(signum); } static void setup_int_handler(void) { struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_handler = &int_handler; sigaction(SIGINT, &act, &f.old_int); sigaction(SIGTERM, &act, &f.old_term); } static void remove_int_handler(void) { sigaction(SIGINT, &f.old_int, NULL); sigaction(SIGTERM, &f.old_term, NULL); } void util_fmt_exit(void) { unsigned int i; if (f.handle_int) remove_int_handler(); if (f.lvl > 0) fwarn("%s before remaining %d util_obj_end()", __func__, f.lvl); for (i = 0; i < f.num_keys; i++) free(f.keys[i].name); free(f.keys); for (i = 0; i < f.num_objs; i++) obj_free(&f.objs[i]); free(f.objs); if (f.type == FMT_CSV) util_rec_free(f.csv_rec); } void util_fmt_set_indent(unsigned int base, unsigned int width, char ind_char) { f.ind_base = base; f.ind_width = width; f.ind_char = ind_char; } static unsigned int to_hex(char *str, int val, unsigned int num_digits) { int digit; char *c; for (c = str + num_digits - 1; c >= str; c--) { digit = (val & 0xf); val >>= 4; *c = (char)((digit >= 10) ? digit - 10 + 'a' : digit + '0'); } return num_digits; } static char get_escape(const char *map, char c) { int i; for (i = 0; map[i] && map[i + 1]; i += 2) { if (map[i] == c) return map[i + 1]; } return 0; } struct quote_params { const char *double_chars; const char *esc_map; char hex_char; unsigned int hex_digits; unsigned int max_width_per_char; }; static char *do_quote(const char *str, const struct quote_params *p) { unsigned int from, to; char *q, esc, c; /* Start with worst-case length assuming every char is replaced. */ q = util_zalloc(strlen(str) * p->max_width_per_char + /* "" nul */ 3); to = 0; q[to++] = '"'; for (from = 0; (c = str[from]); from++) { if (p->double_chars && strchr(p->double_chars, c)) { /* Escape characters by doubling them ("" in CSV). */ q[to++] = c; q[to++] = c; } else if (p->esc_map && (esc = get_escape(p->esc_map, c))) { /* Escape characters with backslash + letter. */ q[to++] = '\\'; q[to++] = esc; } else if (p->hex_char && !isprint(c)) { /* Escape characters with backslash + hex code. */ q[to++] = '\\'; q[to++] = p->hex_char; to += to_hex(&q[to], c, p->hex_digits); } else { q[to++] = c; } } q[to++] = '"'; return util_realloc(q, (size_t)to + 1); } static char *csv_quote(const char *str) { static const struct quote_params csv_quote_params = { .double_chars = "\"", .esc_map = NULL, .hex_char = 0, .hex_digits = 0, .max_width_per_char = 2 /* " => "" */, }; return do_quote(str, &csv_quote_params); } static void add_key(const char *name, bool persist) { struct key_t key; char *hdr; key.name = util_strdup(name); key.persist = persist; util_add_array(&f.keys, &f.num_keys, key); if (f.type == FMT_CSV) { if (f.quote_all) { hdr = csv_quote(name); util_rec_def(f.csv_rec, name, UTIL_REC_ALIGN_LEFT, 0, hdr); free(hdr); } else { util_rec_def(f.csv_rec, name, UTIL_REC_ALIGN_LEFT, 0, name); } util_rec_set(f.csv_rec, name, "\"\""); f.csv_hdr = true; } } static struct key_t *get_key(const char *name) { unsigned int i; for (i = 0; i < f.num_keys; i++) { if (strcmp(name, f.keys[i].name) == 0) return &f.keys[i]; } return NULL; } void util_fmt_add_key(const char *fmt, ...) { va_list args; char *key; va_start(args, fmt); util_vasprintf(&key, fmt, args); va_end(args); /* Only add unique keys. */ if (!get_key(key)) add_key(key, true); free(key); } static bool update_key(const char *name, bool persist) { struct key_t *key; bool rc = true; key = get_key(name); if (key) { key->persist = persist; } else if (!f.do_filter) { add_key(name, persist); } else { fwarn("util_fmt_pair for key '%s' without util_fmt_add_key()", name); rc = false; } return rc; } static struct obj_t *curr_obj(int off) { int lvl = (int)f.lvl - 1 + off; return lvl < 0 ? NULL : &f.objs[lvl]; } static void _util_fmt_obj_end(void); /* * By s390-tools convention, all tool output must be contained in an extra * top-level object that includes tool-invocation meta-data. */ static void emit_meta_object(void) { unsigned int quoted = FMT_PERSIST | FMT_QUOTE, unquoted = FMT_PERSIST; char hostname[HOST_NAME_MAX + 1] = { 0 }, date[30]; struct timeval tv; struct tm *tm; f.meta_done = true; util_fmt_obj_start(FMT_DEFAULT, NULL); util_fmt_obj_start(FMT_PREFIX, "meta"); /* * "meta": { * "api_level": 1, * "version": "2.32.0", * "host": "localhost", * "time_epoch": 1714392976, * "time": "2024-04-29 14:16:16+0200", * } */ util_fmt_pair(unquoted, "api_level", "%d", f.api_level); util_fmt_pair(quoted, "version", "%s", RELEASE_STRING); gethostname(hostname, sizeof(hostname) - 1); util_fmt_pair(quoted, "host", "%s", hostname); gettimeofday(&tv, NULL); util_fmt_pair(unquoted, "time_epoch", "%llu", tv.tv_sec); tm = localtime(&tv.tv_sec); if (!strftime(date, sizeof(date), "%F %T%z", tm)) date[0] = 0; util_fmt_pair(quoted, "time", "%s", date); _util_fmt_obj_end(); if (f.type == FMT_JSONSEQ) { /* Tool meta-data is a separate object for JSONSEQ. */ util_fmt_obj_end(); } } void util_fmt_obj_start(unsigned int oflags, const char *fmt, ...) { struct obj_t *parent, *obj; char *name = NULL; sigset_t set; va_list args; if (!f.hide_meta && !f.meta_done && f.lvl == 0) { emit_meta_object(); /* * Allow override of top-level key name for supplementary * output formats. */ if (!fmt) name = util_strdup(program_invocation_short_name); } if (fmt) { va_start(args, fmt); util_vasprintf(&name, fmt, args); va_end(args); } f.lvl++; if (f.lvl > f.num_objs) util_expand_array(&f.objs, &f.num_objs); parent = curr_obj(-1); obj = curr_obj(0); obj->name = name; obj->is_list = (oflags & FMT_LIST); obj->is_row = (oflags & FMT_ROW); obj->is_prefix = (oflags & FMT_PREFIX); obj->index = 0; if (f.obj_start) { disable_int(&set); f.obj_start(parent, obj); enable_int(&set); } if (parent) parent->index++; } static void _util_fmt_obj_end(void) { struct obj_t *obj, *parent; sigset_t set; if (f.lvl == 0) { fwarn("%s without util_fmt_obj_start", __func__); return; } parent = curr_obj(-1); obj = curr_obj(0); if (f.obj_end) { disable_int(&set); f.obj_end(parent, obj); enable_int(&set); } f.lvl--; obj_free(obj); } void util_fmt_obj_end(void) { _util_fmt_obj_end(); if (f.lvl == 1 && f.meta_done && f.type != FMT_JSONSEQ) { /* Emit closure for top-level meta-container object. */ util_fmt_obj_end(); } } static char *add_prefix(const char *str, bool full) { struct obj_t *obj; unsigned int i; char *prefix; prefix = util_strdup(""); for (i = 0; i < f.lvl; i++) { obj = &f.objs[i]; if (!full && !obj->is_prefix) continue; if (obj->name) { if (*prefix) util_concatf(&prefix, "."); util_concatf(&prefix, "%s", obj->name); } if (obj->is_list && full) util_concatf(&prefix, "[%d]", obj->index - 1); } if (*prefix) util_concatf(&prefix, "."); util_concatf(&prefix, "%s", str); return prefix; } void util_fmt_pair(unsigned int mflags, const char *key, const char *fmt, ...) { char *val, *prefixed_key; struct obj_t *obj; bool is_filtered; sigset_t set; va_list args; obj = curr_obj(0); if (!obj) { fwarn("%s before util_fmt_obj_start", __func__); return; } /* Filter by key. */ if (f.do_filter) { prefixed_key = add_prefix(key, false); is_filtered = !get_key(prefixed_key); free(prefixed_key); if (is_filtered) return; } /* Filter by validity. */ if (f.hide_inval && (mflags & FMT_INVAL)) return; va_start(args, fmt); util_vasprintf(&val, fmt, args); va_end(args); if (f.map) { disable_int(&set); f.map(obj, mflags, key, val); enable_int(&set); } obj->index++; free(val); } static char *pairs_quote(const char *str) { static const struct quote_params pairs_quote_params = { .double_chars = NULL, .esc_map = "\"\"$$``\\\\\aa\bb\ee\ff\nn\rr\tt\vv", .hex_char = 'x', .hex_digits = 2, .max_width_per_char = 4 /* '\x' + 2 hex_digits */, }; return do_quote(str, &pairs_quote_params); } static void pairs_map(struct obj_t *UNUSED(obj), unsigned int mflags, const char *key, const char *val) { char *full_key, *qval = NULL; if (mflags & FMT_INVAL) val = ""; indent(0); if (f.quote_all || (mflags & FMT_QUOTE)) qval = pairs_quote(val); if (f.hide_prefix) { fprintf(f.fd, "%s=%s\n", key, qval ?: val); } else { full_key = add_prefix(key, true); fprintf(f.fd, "%s=%s\n", full_key, qval ?: val); free(full_key); } free(qval); } static char *json_quote(const char *str) { static const struct quote_params json_quote_params = { .double_chars = NULL, .esc_map = "\"\"\\\\\bb\ff\nn\rr\tt", .hex_char = 'u', .hex_digits = 4, .max_width_per_char = 6 /* '\u' + 4 hex_digits */, }; return do_quote(str, &json_quote_params); } static void json_obj_start(struct obj_t *parent, struct obj_t *obj) { char *key; if (!parent && f.type == FMT_JSONSEQ) { /* Emit leading record separator according to RFC 7464. */ fprintf(f.fd, "\x1e"); } if (parent && parent->index > 0) fprintf(f.fd, ",%s", f.nl); indent(0); if (parent && !parent->is_list && obj->name) { key = json_quote(obj->name); fprintf(f.fd, "%s: ", key); free(key); } fprintf(f.fd, obj->is_list ? "[%s" : "{%s", f.nl); } static void json_obj_end(struct obj_t *parent, struct obj_t *obj) { if (obj->index > 0) fprintf(f.fd, "%s", f.nl); indent(0); fprintf(f.fd, obj->is_list ? "]" : "}"); if (!parent) fprintf(f.fd, "\n"); } /* * Ensure syntactically correct JSON by emitting all pending closure elements. * Called in signal context - only use signal-safe functions. */ static void json_term(void) { struct obj_t *obj, *parent; for (; f.lvl > 0; f.lvl--) { obj = curr_obj(0); parent = curr_obj(-1); if (obj->index > 0) safe_write(f.nl); _indent(0, true); safe_write(obj->is_list ? "]" : "}"); if (!parent) safe_write(f.nl); } } static void json_map(struct obj_t *parent, unsigned int mflags, const char *key, const char *val) { char *qkey, *qval = NULL; qkey = json_quote(key); if (mflags & FMT_INVAL) qval = util_strdup("null"); else if (f.quote_all || (mflags & FMT_QUOTE)) qval = json_quote(val); if (parent->index > 0) fprintf(f.fd, ",%s", f.nl); indent(1); fprintf(f.fd, "%s: %s", qkey, qval ?: val); free(qval); free(qkey); } static void csv_obj_start(struct obj_t *UNUSED(parent), struct obj_t *obj) { if (!obj->is_row) return; } static void csv_obj_end(struct obj_t *UNUSED(parent), struct obj_t *obj) { unsigned int i; if (!(obj->is_row || (f.lvl == 1 && f.csv_data))) return; if (f.csv_hdr) { /* Print row with CSV header. */ indent(0); util_rec_print_hdr(f.csv_rec); f.csv_hdr = false; } /* Print row with CSV data. */ indent(0); util_rec_print(f.csv_rec); f.csv_data = false; /* Reset non-persistent fields. */ for (i = 0; i < f.num_keys; i++) { if (!f.keys[i].persist) util_rec_set(f.csv_rec, f.keys[i].name, "\"\""); } } static void csv_map(struct obj_t *UNUSED(obj), unsigned int mflags, const char *key, const char *val) { char *qval = NULL, *prefixed_key; /* Use empty string for invalid values. */ if (mflags & FMT_INVAL) val = ""; /* Quote value if requested. */ if (f.quote_all || (mflags & FMT_QUOTE)) qval = csv_quote(val); /* Process key and value. */ prefixed_key = add_prefix(key, false); if (update_key(prefixed_key, mflags & FMT_PERSIST)) { util_rec_set(f.csv_rec, prefixed_key, "%s", qval ?: val); f.csv_data = true; } free(prefixed_key); free(qval); } static bool hide_meta_env(void) { char *v; v = secure_getenv("FMT_NOMETA"); return (v && strcmp(v, "1") == 0); } void util_fmt_init(FILE *fd, enum util_fmt_t type, unsigned int flags, int api_level) { memset(&f, 0, sizeof(f)); f.type = type; f.fd = fd; f.fileno = fileno(fd); f.hide_prefix = (flags & FMT_NOPREFIX); f.hide_inval = !(flags & FMT_KEEPINVAL); f.hide_meta = (flags & FMT_NOMETA) || hide_meta_env(); f.quote_all = (flags & FMT_QUOTEALL); f.do_filter = (flags & FMT_FILTER); f.do_warn = (flags & FMT_WARN); f.handle_int = (flags & FMT_HANDLEINT); f.api_level = api_level; if (type == FMT_JSONSEQ) f.nl = ""; else f.nl = "\n"; f.ind_width = 2; f.ind_char = ' '; f.meta_done = false; switch (type) { case FMT_PAIRS: f.map = &pairs_map; break; case FMT_JSON: case FMT_JSONSEQ: f.obj_start = &json_obj_start; f.obj_end = &json_obj_end; f.map = &json_map; f.term = &json_term; break; case FMT_CSV: f.obj_start = &csv_obj_start; f.obj_end = &csv_obj_end; f.map = &csv_map; f.csv_rec = util_rec_new_csv(","); f.csv_hdr = true; f.csv_data = false; break; } /* Ensure consistent number format for callers that use setlocale(). */ setlocale(LC_NUMERIC, "C"); if (f.handle_int) { setup_int_handler(); sigemptyset(&no_int_mask); sigaddset(&no_int_mask, SIGINT); sigaddset(&no_int_mask, SIGTERM); } } s390-tools-2.38.0/libutil/util_fmt_example.c000066400000000000000000000133211502674226300206150ustar00rootroot00000000000000/* * util_fmt_example - Example program for util_fmt * * Copyright IBM Corp. 2024 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include "lib/util_base.h" #include "lib/util_fmt.h" #define API_LEVEL 1 static void meta_example(enum util_fmt_t format) { util_fmt_init(stdout, format, FMT_DEFAULT, API_LEVEL); /* * First call to util_fmt_obj_start() automatically adds meta-data * object as required by s390-tools convention. */ util_fmt_obj_start(FMT_DEFAULT, NULL); util_fmt_pair(FMT_QUOTE, "key", "value"); util_fmt_obj_end(); util_fmt_exit(); } static void simple_example(enum util_fmt_t format, int fmt_flags) { /* * Note: Meta-data is excluded in this example for readability but * must be included in actual tool output. */ util_fmt_init(stdout, format, fmt_flags | FMT_NOMETA, API_LEVEL); /* * { * "child": { * "key": "value", * "invalid":"invalidvalue" <== Marked as invalid * } * } */ util_fmt_obj_start(FMT_DEFAULT, NULL); util_fmt_obj_start(FMT_DEFAULT, "child"); util_fmt_pair(FMT_QUOTE, "key", "value"); util_fmt_pair(FMT_QUOTE | FMT_INVAL, "invalid", "invalidvalue"); util_fmt_obj_end(); util_fmt_obj_end(); util_fmt_exit(); } static void list_example(enum util_fmt_t format, int flags) { int i; /* * Note: Meta-data is excluded in this example for readability but * must be included in actual tool output. */ util_fmt_init(stdout, format, flags | FMT_NOMETA, API_LEVEL); /* * "cond","key" * condvalue0,value0 * "",value1 * "",value2 * "",value3 */ util_fmt_obj_start(FMT_DEFAULT, NULL); util_fmt_obj_start(FMT_LIST, "list"); for (i = 0; i < 4; i++) { util_fmt_obj_start(FMT_ROW, NULL); if (i == 0) util_fmt_pair(flags, "cond", "condvalue%d", i); util_fmt_pair(FMT_DEFAULT, "key", "value%d", i); util_fmt_obj_end(); } util_fmt_obj_end(); util_fmt_obj_end(); util_fmt_exit(); } #define NUM_KEYS 4 static void vary_example(enum util_fmt_t format, bool add) { const char *keys[NUM_KEYS] = { "key_a", "key_b", "key_c", "key_d" }; int i; /* * Note: Meta-data is excluded in this example for readability but * must be included in actual tool output. */ util_fmt_init(stdout, format, FMT_NOMETA, API_LEVEL); if (add) { /* Make keys known before starting output. */ for (i = 0; i < NUM_KEYS; i++) util_fmt_add_key(keys[i]); } util_fmt_obj_start(FMT_LIST, "list"); for (i = 0; i < 4; i++) { util_fmt_obj_start(FMT_ROW, NULL); util_fmt_pair(FMT_DEFAULT, keys[i], "value%d", i); util_fmt_obj_end(); } util_fmt_obj_end(); util_fmt_exit(); } static void filter_example(enum util_fmt_t format) { /* * Note: Meta-data is excluded in this example for readability but * must be included in actual tool output. */ util_fmt_init(stdout, format, FMT_FILTER | FMT_NOMETA, API_LEVEL); util_fmt_add_key("key_a"); /* * { * "key_a": "value_a", * "key_b": "value_b" <== Not announced via util_fmt_add_key() * } */ util_fmt_obj_start(FMT_DEFAULT, NULL); util_fmt_pair(FMT_QUOTE, "key_a", "value_a"); util_fmt_pair(FMT_QUOTE, "key_b", "value_b"); util_fmt_obj_end(); util_fmt_exit(); } static void prefix_example(enum util_fmt_t format, bool do_prefix) { /* * Note: Meta-data is excluded in this example for readability but * must be included in actual tool output. */ util_fmt_init(stdout, format, FMT_NOMETA, API_LEVEL); /* * { * "key": "value0", * "obj1": { // Marked as prefix object * "key": "value1" * } * } */ util_fmt_obj_start(FMT_DEFAULT, "obj0"); util_fmt_pair(FMT_QUOTE, "key", "value0"); util_fmt_obj_start(do_prefix ? FMT_PREFIX : FMT_DEFAULT, "obj1"); util_fmt_pair(FMT_QUOTE, "key", "value1"); util_fmt_obj_end(); util_fmt_obj_end(); util_fmt_exit(); } static void announce(const char *example_name) { static int example_number; int i; if (example_number++ > 0) printf("\n"); printf("%d. %s\n====", example_number, example_name); for (i = strlen(example_name); i > 0; i--) printf("="); printf("\n"); } int main(int UNUSED(argc), char *UNUSED(argv[])) { announce("JSON output"); simple_example(FMT_JSON, FMT_KEEPINVAL); announce("JSON without invalid pairs"); simple_example(FMT_JSON, FMT_DEFAULT); announce("JSON formatted as sequence"); simple_example(FMT_JSONSEQ, FMT_DEFAULT); announce("Pairs output"); simple_example(FMT_PAIRS, FMT_KEEPINVAL); announce("Pairs output without invalid pairs"); simple_example(FMT_PAIRS, FMT_DEFAULT); announce("Pairs without prefix"); simple_example(FMT_PAIRS, FMT_NOPREFIX); announce("CSV output"); simple_example(FMT_CSV, FMT_KEEPINVAL); announce("CSV list output"); list_example(FMT_CSV, FMT_DEFAULT); announce("CSV list with persistent cond value"); list_example(FMT_CSV, FMT_PERSIST); announce("JSON with filtered key"); filter_example(FMT_JSON); announce("Pairs with filtered key"); filter_example(FMT_PAIRS); announce("CSV with filtered key"); filter_example(FMT_CSV); announce("CSV list with varying keys"); vary_example(FMT_CSV, false); announce("CSV list with pre-announced varying keys"); vary_example(FMT_CSV, true); announce("JSON output with meta-data"); meta_example(FMT_JSON); announce("JSON sequence output with meta-data"); meta_example(FMT_JSONSEQ); announce("Pairs output with meta-data"); meta_example(FMT_PAIRS); announce("CSV output with meta-data"); meta_example(FMT_CSV); announce("JSON output with duplicate keys"); prefix_example(FMT_JSON, false); announce("CSV output with duplicate keys"); prefix_example(FMT_CSV, false); announce("CSV output with duplicate keys distinguished by prefix"); prefix_example(FMT_CSV, true); return 0; } s390-tools-2.38.0/libutil/util_libc.c000066400000000000000000000135521502674226300172330ustar00rootroot00000000000000/* * util - Utility function library * * Handle standard errors for libc functions * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include "lib/util_base.h" #include "lib/util_libc.h" #include "lib/util_panic.h" /* * Return size as string of largest unit, e.g. 1025 = "1 KiB" */ static void format_size(char *str, size_t size) { static const char * const unit_vec[] = {"byte", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"}; unsigned int i; for (i = 0; i < ARRAY_SIZE(unit_vec); i++) { if (size / 1024 == 0) { sprintf(str, "%zu %s", size, unit_vec[i]); return; } size /= 1024; } sprintf(str, "huge"); } static void __util_oom(const char *func, const char *file, int line, size_t size) { char size_str[256]; fprintf(stderr, "%s: Failed to allocate memory", program_invocation_short_name); if (size > 0) { format_size(size_str, size); fprintf(stderr, " (%s)", size_str); } fprintf(stderr, " at %s:%d %s()\n", file, line, func); exit(EXIT_FAILURE); } /* * Allocate memory or exit in case of failure */ void *__util_malloc(const char *func, const char *file, int line, size_t size) { void *buf; buf = malloc(size); if (buf == NULL) __util_oom(func, file, line, size); return buf; } /* * Allocate zero-initialized memory or exit in case of failure */ void *__util_zalloc(const char *func, const char *file, int line, size_t size) { void *buf = __util_malloc(func, file, line, size); memset(buf, 0, size); return buf; } /* * Re-allocate memory or exit in case of failure */ void *__util_realloc(const char *func, const char *file, int line, void *ptr, size_t size) { void *buf; buf = realloc(ptr, size); if (buf == NULL) __util_oom(func, file, line, size); return buf; } /* * Duplicate a string buffer or exit in case of failure */ void *__util_strdup(const char *func, const char *file, int line, const char *str) { void *buf = strdup(str); if (buf == NULL) __util_oom(func, file, line, strlen(str) + 1); return buf; } /** * Concatenate two strings or exit in case of failure * * The first string \a str1 is resized and a copy of the second * string \a str2 is appended to it. * * Therefore the first string \a str1 must either have been allocated * using malloc(), calloc(), or realloc() or must be NULL. * * @param[in] str1 Pointer to first string to concatenate, which * becomes invalid * @param[in] str2 Constant pointer to second string to concatenate * * @returns Pointer to concatenated string */ char *util_strcat_realloc(char *str1, const char *str2) { char *buf; if (str1) { buf = util_realloc(str1, strlen(str1) + strlen(str2) + 1); strcat(buf, str2); } else { buf = util_strdup(str2); } return buf; } /** * Concatenate a string with the result of a format string expansion * * @param[in, out] str1 Pointer to pointer to first string * @param[in] fmt Format string for generation of the second string * @param[in] ... Parameters for format string */ void util_concatf(char **str1, const char *fmt, ...) { va_list args; char *str2; va_start(args, fmt); util_vasprintf(&str2, fmt, args); va_end(args); *str1 = util_strcat_realloc(*str1, str2); free(str2); } /** * Convert string to uppercase * * String \a str is converted to uppercase * * @param[in,out] str String to convert */ void util_str_toupper(char *str) { int i; for (i = 0; str[i] != '\0'; i++) str[i] = toupper(str[i]); } /** * Convert string to lowercase * * String \a str is converted to lowercase * * @param[in,out] str String to convert */ void util_str_tolower(char *str) { int i; for (i = 0; str[i] != '\0'; i++) str[i] = tolower(str[i]); } /* * Print to newly allocated string or exit in case of failure */ int __util_vasprintf(const char *func, const char *file, int line, char **strp, const char *fmt, va_list ap) { int rc; rc = vasprintf(strp, fmt, ap); if (rc == -1) __util_oom(func, file, line, 0); return rc; } /* * Print to newly allocated string or exit in case of failure */ int __util_asprintf(const char *func, const char *file, int line, char **strp, const char *fmt, ...) { va_list ap; int rc; va_start(ap, fmt); rc = __util_vasprintf(func, file, line, strp, fmt, ap); va_end(ap); return rc; } /* * Print to string buffer or exit in case of failure */ int __util_vsprintf(const char *func, const char *file, int line, char *str, const char *fmt, va_list ap) { int rc; rc = vsprintf(str, fmt, ap); if (rc == -1) __util_assert("rc != -1", func, file, line, rc != -1, "Could not format string\n"); return rc; } /** * Strip leading and trailing spaces from string * * Remove string \a s leading and trailing spaces * * @param[in,out] s String to manipulate * * @returns Pointer to first non-space character in string \a s */ char *util_strstrip(char *s) { size_t size; char *end; size = strlen(s); if (!size) return s; end = s + size - 1; while (end >= s && isspace(*end)) end--; *(end + 1) = '\0'; while (*s && isspace(*s)) s++; return s; } /** * Copy \a src to buffer \a dest of size \a size. At most size - 1 * chars will be copied. \a dest will always be NUL terminated. * * Note: If the return value is greater than or equal to size truncation * occurred. * * @param[in] dest Destination buffer * @param[in] src Source string * @param[in] size Size of destination buffer * * @returns strlen Length of \a src string */ size_t util_strlcpy(char *dest, const char *src, size_t size) { size_t str_len = strlen(src); size_t len; if (size) { len = MIN(size - 1, str_len); memcpy(dest, src, len); dest[len] = '\0'; } return str_len; } s390-tools-2.38.0/libutil/util_libc_example.c000066400000000000000000000040201502674226300207340ustar00rootroot00000000000000/** * util_libc_example - Example program for util_libc * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ //! [code] #include #include #include #include "lib/util_libc.h" #include "lib/util_panic.h" #define EXAMPLE_WORD " /sys/devices/system/cpu " /* * Demonstrate that out of memory is automatically handled via panic() */ int main(void) { unsigned long ulong_max = (unsigned long)-1; void *ptr; char *zeroes, *str; char buffer[sizeof(EXAMPLE_WORD)]; strcat(buffer, EXAMPLE_WORD); fprintf(stderr, "Try to remove leading and trailing spaces from " "\"%s\"\nresult = \"%s\"\n", EXAMPLE_WORD, util_strstrip(buffer)); /* Use util_strcat_realloc() for string concatenation */ fprintf(stderr, "Try to concatenate \"Hello\", \", \" and \"world!\": "); str = util_strdup("Hello"); str = util_strcat_realloc(str, ", "); str = util_strcat_realloc(str, "world!"); fprintf(stderr, "result = \"%s\"\n", str); free(str); /* Use util_concatf() for string concatenation */ fprintf(stderr, "Try to concatenate \"list\" plus comma-separated list of numbers 1 to 3: "); str = NULL; util_concatf(&str, "list:"); for (int i = 1; i <= 3; i++) util_concatf(&str, "%s%d", (i > 1 ? "," : ""), i); fprintf(stderr, "result = %s\n", str); /* list:part1,part2,part3 */ /* One byte allocation should work */ fprintf(stderr, "Try to allocate 1 byte: "); ptr = util_malloc(1); fprintf(stderr, "done\n"); /* One byte zeroed-allocation should work */ fprintf(stderr, "Try to allocate 1 byte initialized with zeroes: "); zeroes = util_zalloc(1); fprintf(stderr, "done\n"); util_assert(*zeroes == 0, "Garbage found in zero initialized memory\n"); /* The next allocation will probably fail */ fprintf(stderr, "Try to allocate %lu bytes:\n", ulong_max); ptr = util_malloc(ulong_max); fprintf(stderr, "You should not see me (ptr=%p)!\n", ptr); return EXIT_FAILURE; } //! [code] s390-tools-2.38.0/libutil/util_list.c000066400000000000000000000114671502674226300173000ustar00rootroot00000000000000/* * util - Utility function library * * Linked list functions * * Copyright IBM Corp. 2013, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include "lib/util_libc.h" #include "lib/util_list.h" /* * Node to entry */ static inline void *n2e(struct util_list *list, struct util_list_node *node) { return ((void *) node) - list->offset; } /* * Entry to node */ static inline struct util_list_node *e2n(struct util_list *list, void *entry) { return entry + list->offset; } /* * Initialize linked list */ void util_list_init_offset(struct util_list *list, unsigned long offset) { memset(list, 0, sizeof(*list)); list->offset = offset; } /* * Create new linked list */ struct util_list *util_list_new_offset(unsigned long offset) { struct util_list *list = util_malloc(sizeof(*list)); util_list_init_offset(list, offset); return list; } /* * Free linked list */ void util_list_free(struct util_list *list) { free(list); } /* * Add new element to end of list */ void util_list_add_tail(struct util_list *list, void *entry) { struct util_list_node *node = e2n(list, entry); node->next = NULL; if (!list->start) { list->start = node; node->prev = NULL; } else { list->end->next = node; node->prev = list->end; } list->end = node; } /* * Add new element to front of list */ void util_list_add_head(struct util_list *list, void *entry) { struct util_list_node *node = e2n(list, entry); node->prev = NULL; node->next = NULL; if (!list->start) { list->end = node; } else { list->start->prev = node; node->next = list->start; } list->start = node; } /* * Add new element (entry) after an existing element (list_entry) */ void util_list_add_next(struct util_list *list, void *entry, void *list_entry) { struct util_list_node *node = e2n(list, entry); struct util_list_node *list_node = e2n(list, list_entry); node->next = list_node->next; node->prev = list_node; if (list_node->next) list_node->next->prev = node; else list->end = node; list_node->next = node; } /* * Add new element (entry) before an existing element (list_entry) */ void util_list_add_prev(struct util_list *list, void *entry, void *list_entry) { struct util_list_node *node = e2n(list, entry); struct util_list_node *list_node = e2n(list, list_entry); node->prev = list_node->prev; node->next = list_node; if (list_node->prev) list_node->prev->next = node; else list->start = node; list_node->prev = node; } /* * Remove element from list */ void util_list_remove(struct util_list *list, void *entry) { struct util_list_node *node = e2n(list, entry); if (list->start == node) list->start = node->next; if (list->end == node) list->end = node->prev; if (node->prev) node->prev->next = node->next; if (node->next) node->next->prev = node->prev; } /* * Get first element of list */ void *util_list_start(struct util_list *list) { if (!list->start) return NULL; return ((void *) list->start) - list->offset; } /* * Get last element of list */ void *util_list_end(struct util_list *list) { if (!list->end) return NULL; return n2e(list, list->end); } /* * Get next element after entry */ void *util_list_next(struct util_list *list, void *entry) { struct util_list_node *node; if (!entry) return NULL; node = e2n(list, entry); node = node->next; if (!node) return NULL; return n2e(list, node); } /* * Get previous element before entry */ void *util_list_prev(struct util_list *list, void *entry) { struct util_list_node *node; if (!entry) return NULL; node = e2n(list, entry); node = node->prev; if (!node) return NULL; return n2e(list, node); } /* * Get number of list entries */ unsigned long util_list_len(struct util_list *list) { unsigned long cnt = 0; void *entry; util_list_iterate(list, entry) cnt++; return cnt; } /* * Sort table (bubble sort) */ void util_list_sort(struct util_list *list, util_list_cmp_fn cmp_fn, void *data) { struct util_list_node *node1, *node2; unsigned long list_cnt, i, j; void *entry1, *entry2; list_cnt = util_list_len(list); for (i = 1; i < list_cnt; i++) { node1 = list->start; for (j = 0; j < list_cnt - i; j++) { node2 = node1->next; entry1 = n2e(list, node1); entry2 = n2e(list, node2); if (cmp_fn(entry1, entry2, data) > 0) { node1->next = node2->next; if (node1->next) node1->next->prev = node1; else list->end = node1; node2->next = node1; node2->prev = node1->prev; if (node2->prev) node2->prev->next = node2; else list->start = node2; node1->prev = node2; } else { node1 = node2; } } } } /* * Check if list is empty */ int util_list_is_empty(struct util_list *list) { return list->start == NULL; } s390-tools-2.38.0/libutil/util_lockfile.c000066400000000000000000000236361502674226300201160ustar00rootroot00000000000000/* * util - Utility function library * * Created file-based locks * * Copyright IBM Corp. 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include "lib/util_libc.h" #include "lib/util_lockfile.h" #include "lib/util_panic.h" #define WAITPID 120 /* Time to wait for pid to be written */ #define DEF_WAITINC_US 5000000 /* Additional time to wait each retry */ #define DEF_MAXWAIT_US 60000000 /* Maximum wait between retries */ #define BUFSIZE 40 /* Buffer must be large enough to fit pid string */ /** * Check if there is a process that exists with the specified identifier. * * @param[in] pid Process Identifier to check * * @retval true Process exists or we lack privelege to check * @retval false Process does not exist */ static bool pid_exists(int pid) { int rc; /* Use sig 0 to determine if the owner is still alive */ rc = kill(pid, 0); if (rc != 0) { switch (errno) { case EPERM: /* Privilege issue, just assume PID exists */ break; case ESRCH: /* PID does not exist, this lock is stale */ return false; default: util_assert(false, "Unexpected return from kill: %d\n", rc); break; } } return true; } /** * Check for an existing lock that is deemed stale (either the associated PID * is gone or no PID has been written to the file in a reasonable timeframe). * In the case a stale lock is found, remove it. * * @param[in] lockfile Path to the lock file * * @retval 0 Either no lock or stale lock was found and removed * @retval 1 Lock is held by another PID and is not stale */ static int handle_stale_lock(char *lockfile) { int fd, rc, pid, len; struct stat info; char buf[BUFSIZE]; time_t curr; fd = open(lockfile, O_RDONLY); if (fd >= 0) { /* Lock exists, see who owns it */ len = read(fd, buf, sizeof(buf)); if (len > 0) { buf[len] = 0; /* Ensure null terminated string */ pid = atoi(buf); if (!pid_exists(pid)) { /* Stale lock detected unlink and retry now */ close(fd); unlink(lockfile); return 0; } else if (pid != 0) { /* Lock is held by an active pid, delay */ close(fd); return 1; } /* * If we reach this point, the PID was 0 which is * unexpected. Proceed under the assumption that the * proper PID hasn't been written yet. */ } /* * PID hasn't been written yet? Either a bad lock or a very * new one. */ time(&curr); rc = fstat(fd, &info); close(fd); if (rc != 0) { /* Can't read file anymore, retry now */ return 0; } if (curr > info.st_mtime + WAITPID) { /* * PID should be in the file within 2 minutes, * something went wrong. Treat as stale. */ unlink(lockfile); return 0; } /* Otherwise, assume file was newly created and delay */ return 1; } /* Couldn't open, try again immediately */ return 0; } /** * Attempt to create a lockfile at the specified path. * * @param[in] lockfile Path to the lock file * @param[in] retries Number of times to retry if lock fails initially * @param[in] waitinc How many micro-seconds to extend wait time before * additional retry * @param[in] maxwait Maximum wait time before retry * @param[in] pid PID to use for lock ownership * * @retval 0 Lock created with PID as owner * @retval !=0 Lock was not created */ static int do_lockfile_lock(char *lockfile, unsigned int retries, int pid, unsigned int waitinc, unsigned int maxwait) { unsigned int tries = retries + 1; int fd, plen, len, rc = 0; unsigned int snooze = 0; char buf[BUFSIZE]; char *tpath; if (!lockfile) return UTIL_LOCKFILE_ERR; plen = snprintf(buf, sizeof(buf), "%d\n", pid); if (plen < 0 || (plen > ((int)sizeof(buf) - 1))) return UTIL_LOCKFILE_ERR; /* Allocate temporary lock file with a sufficiently unique path */ len = util_asprintf(&tpath, "%s%05d%02x", lockfile, getpid(), (unsigned int)time(NULL) & 255); if (len < 0) return UTIL_LOCKFILE_ERR; /* Open the temporary lockfile, write the specified pid */ fd = open(tpath, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0644); if (fd < 0) { rc = UTIL_LOCKFILE_ERR; goto out; } len = write(fd, buf, plen); if (close(fd) != 0) { rc = UTIL_LOCKFILE_ERR; goto cleanup; } if (len != plen) { /* Failed to write the temp lockfile, bail out */ rc = UTIL_LOCKFILE_ERR; goto cleanup; } /* Link the temprorary file to the real path */ do { rc = link(tpath, lockfile); if (rc == 0) { /* Lock successfully acquired */ rc = UTIL_LOCKFILE_OK; goto cleanup; } /* Lock already held - check for stale lock */ rc = handle_stale_lock(lockfile); /* Only wait if the lock was not stale */ if (rc != 0) { tries--; if (tries > 0) { snooze += waitinc; snooze = (snooze > maxwait) ? maxwait : snooze; usleep(snooze); } } } while (tries > 0); /* Exhausted specified number of retries, exit on failure */ rc = UTIL_LOCKFILE_LOCK_FAIL; cleanup: unlink(tpath); free(tpath); out: return rc; } /** * Attempt to release a lockfile at the specified path. * * @param[in] lockfile Path to the lock file * @param[in] pid PID that should own the lock * * @retval 0 Lock released * @retval !=0 Lock was not released or did not exist */ static int do_lockfile_release(char *lockfile, int pid) { int fd, len, lpid; char buf[BUFSIZE]; if (!lockfile) return UTIL_LOCKFILE_ERR; /* Open lockfile, read the owning pid if it exists */ fd = open(lockfile, O_RDONLY); if (fd < 0) return UTIL_LOCKFILE_RELEASE_NONE; len = read(fd, buf, sizeof(buf)); close(fd); if (len <= 0) return UTIL_LOCKFILE_RELEASE_FAIL; buf[len] = 0; lpid = atoi(buf); /* Only release the lock if its held by the right pid */ if (pid != lpid) return UTIL_LOCKFILE_RELEASE_FAIL; unlink(lockfile); return 0; } /** * Attempt to create a lockfile owned by this process at the specified path. * * @param[in] lockfile Path to the lock file * @param[in] retries Number of times to retry if lock fails initially * * @retval 0 Lock created * @retval !=0 Lock was not created */ int util_lockfile_lock(char *lockfile, int retries) { return do_lockfile_lock(lockfile, retries, getpid(), DEF_WAITINC_US, DEF_MAXWAIT_US); } /** * Attempt to create a lockfile owned by this process at the specified path * using a custom wait/retry time. * * @param[in] lockfile Path to the lock file * @param[in] retries Number of times to retry if lock fails initially * @param[in] waitinc How many micro-seconds to extend wait time before * additional retry * @param[in] maxwait Maximum wait time before retry * * @retval 0 Lock created * @retval !=0 Lock was not created */ int util_lockfile_lock_cw(char *lockfile, int retries, unsigned int waitinc, unsigned int maxwait) { return do_lockfile_lock(lockfile, retries, getpid(), waitinc, maxwait); } /** * Attempt to create a lockfile owned by the parent of this process at the * specified path. * * @param[in] lockfile Path to the lock file * @param[in] retries Number of times to retry if lock fails initially * * @retval 0 Lock created * @retval !=0 Lock was not created */ int util_lockfile_parent_lock(char *lockfile, int retries) { return do_lockfile_lock(lockfile, retries, getppid(), DEF_WAITINC_US, DEF_MAXWAIT_US); } /** * Attempt to create a lockfile owned by the parent of this process at the * specified path using a custom wait/retry time. * * @param[in] lockfile Path to the lock file * @param[in] retries Number of times to retry if lock fails initially * @param[in] waitinc How many micro-seconds to extend wait time before * additional retry * @param[in] maxwait Maximum wait time before retry * * @retval 0 Lock created * @retval !=0 Lock was not created */ int util_lockfile_parent_lock_cw(char *lockfile, int retries, unsigned int waitinc, unsigned int maxwait) { return do_lockfile_lock(lockfile, retries, getppid(), waitinc, maxwait); } /** * Attempt to release a lockfile owned by this process at the specified path. * * @param[in] lockfile Path to the lock file * * @retval 0 Lock released * @retval !=0 Lock was not released or did not exist */ int util_lockfile_release(char *lockfile) { return do_lockfile_release(lockfile, getpid()); } /** * Attempt to release a lockfile owned by the parent of this process at the * specified path. * * @param[in] lockfile Path to the lock file * * @retval 0 Lock released * @retval !=0 Lock was not released or did not exist */ int util_lockfile_parent_release(char *lockfile) { return do_lockfile_release(lockfile, getppid()); } /** * Return the pid that owns the specified lockfile. * * @param[in] lockfile Path to the lock file * @param[in,out] pid Buffer to place owning pid * * @retval 0 pid provided in buffer * @retval !=0 Error, no pid provided */ int util_lockfile_peek_owner(char *lockfile, int *pid) { char buf[BUFSIZE]; int fd, len; if (!lockfile || !pid) return UTIL_LOCKFILE_ERR; /* Open lockfile, read the owning pid if it exists */ fd = open(lockfile, O_RDONLY); if (fd < 0) return UTIL_LOCKFILE_ERR; len = read(fd, buf, sizeof(buf)); close(fd); if (len <= 0) return UTIL_LOCKFILE_ERR; buf[len] = 0; *pid = atoi(buf); return 0; } s390-tools-2.38.0/libutil/util_lockfile_example.c000066400000000000000000000132501502674226300216200ustar00rootroot00000000000000/** * util_lockfile_example - Example program for util_lockfile * * Copyright IBM Corp. 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ //! [code] #include #include #include "lib/util_opt.h" #include "lib/util_prg.h" #include "lib/util_lockfile.h" static const struct util_prg prg = { .desc = "Example for util_lockfile.", .copyright_vec = { { .owner = "IBM Corp.", .pub_first = 2022, .pub_last = 2022, }, UTIL_PRG_COPYRIGHT_END } }; static struct util_opt opt_vec[] = { UTIL_OPT_SECTION("OPTIONS"), { .option = { "file", required_argument, NULL, 'f' }, .argument = "PATH", .desc = "Use the specified path for a lockfile.", }, { .option = { "lock", required_argument, NULL, 'l' }, .argument = "RETRIES", .desc = "Acquire the specified file lock using the parent " "process id of this process. If not immediately " "successful, retry for the specified number of times", }, { .option = { "release", 0, NULL, 'r' }, .desc = "Release the specified lock file using the parent " "process id", }, { .option = { "lock-and-release", required_argument, NULL, 'L' }, .argument = "RETRIES", .desc = "Acquire the specified file lock using this process " "id, then release it. If not immediately successful, " "retry for the specified number of times.", }, UTIL_OPT_HELP, UTIL_OPT_VERSION, UTIL_OPT_END }; enum prg_action { ACTION_NONE = 0, ACTION_LOCK, ACTION_RELEASE, ACTION_LOCK_AND_RELEASE, }; static void print_single_action_error(void) { warnx("Only a single action (--lock, --lock-parent, --release, " "--release-parent) is allowed"); } int main(int argc, char *argv[]) { enum prg_action action_id = ACTION_NONE; int opt, rc, retries = 0; char *endp, *path = NULL; util_prg_init(&prg); util_opt_init(opt_vec, NULL); while (1) { opt = util_opt_getopt_long(argc, argv); if (opt == -1) break; switch (opt) { case 'f': path = optarg; break; case 'L': if (action_id != ACTION_NONE) { print_single_action_error(); return EXIT_FAILURE; } retries = strtol(optarg, &endp, 0); if (*optarg == '\0' || *endp != '\0' || retries < 0) { warnx("Invalid retry value for " "--lock-and-release: '%s'", optarg); util_prg_print_parse_error(); return EXIT_FAILURE; } action_id = ACTION_LOCK_AND_RELEASE; break; case 'l': if (action_id != ACTION_NONE) { print_single_action_error(); return EXIT_FAILURE; } retries = strtol(optarg, &endp, 0); if (*optarg == '\0' || *endp != '\0' || retries < 0) { warnx("Invalid retry value for " "--parent-lock: '%s'", optarg); util_prg_print_parse_error(); return EXIT_FAILURE; } action_id = ACTION_LOCK; break; case 'r': if (action_id != ACTION_NONE) { print_single_action_error(); return EXIT_FAILURE; } action_id = ACTION_RELEASE; break; case 'h': util_prg_print_help(); util_opt_print_help(); exit(EXIT_SUCCESS); case 'v': util_prg_print_version(); exit(EXIT_SUCCESS); default: util_opt_print_parse_error(opt, argv); return EXIT_FAILURE; } } if (!path) { warnx("--file is required, see --help for more information"); return EXIT_FAILURE; } if (action_id == ACTION_NONE) { warnx("One of the following actions must be specified: " "--lock, --release, --lock-and-release"); return EXIT_FAILURE; } /* Determine which util_lockfile function to call */ switch (action_id) { case ACTION_LOCK: rc = util_lockfile_parent_lock(path, retries); switch (rc) { case UTIL_LOCKFILE_OK: printf("lock acquired successfully\n"); break; case UTIL_LOCKFILE_LOCK_FAIL: warnx("lock was not acquired; already held"); return EXIT_FAILURE; case UTIL_LOCKFILE_ERR: warnx("lock was not acquired; file error"); return EXIT_FAILURE; default: warnx("Unknown util_lockfile rc %d", rc); return EXIT_FAILURE; } break; case ACTION_RELEASE: rc = util_lockfile_parent_release(path); switch (rc) { case UTIL_LOCKFILE_OK: printf("lock released successfully\n"); break; case UTIL_LOCKFILE_RELEASE_NONE: warnx("lock file did not exist"); return EXIT_FAILURE; case UTIL_LOCKFILE_RELEASE_FAIL: warnx("lock was not held by the specified pid"); return EXIT_FAILURE; case UTIL_LOCKFILE_ERR: warnx("lock was not released; file error"); return EXIT_FAILURE; default: warnx("Unknown util_lockfile rc %d", rc); return EXIT_FAILURE; } break; case ACTION_LOCK_AND_RELEASE: rc = util_lockfile_lock(path, retries); switch (rc) { case UTIL_LOCKFILE_OK: printf("lock acquired successfully, holding for 2 seconds\n"); break; case UTIL_LOCKFILE_LOCK_FAIL: warnx("lock was not acquired; already held"); return EXIT_FAILURE; case UTIL_LOCKFILE_ERR: warnx("lock was not acquired; file error"); return EXIT_FAILURE; default: warnx("Unknown util_lockfile rc %d", rc); return EXIT_FAILURE; } /* Briefly sleep while holding the lock */ sleep(2); rc = util_lockfile_release(path); switch (rc) { case UTIL_LOCKFILE_OK: printf("lock released successfully\n"); break; case UTIL_LOCKFILE_RELEASE_NONE: warnx("lock file did not exist"); return EXIT_FAILURE; case UTIL_LOCKFILE_RELEASE_FAIL: warnx("lock was not held by the specified pid"); return EXIT_FAILURE; case UTIL_LOCKFILE_ERR: warnx("lock was not released; file error"); return EXIT_FAILURE; default: warnx("Unknown util_lockfile rc %d", rc); return EXIT_FAILURE; } break; default: warnx("Unknown util_lockfile action"); return EXIT_FAILURE; } return EXIT_SUCCESS; } //! [code] s390-tools-2.38.0/libutil/util_log.c000066400000000000000000000030031502674226300170710ustar00rootroot00000000000000/* * Multi-level message logging * * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include "lib/util_log.h" static int current_log_level = UTIL_LOG_INFO; static const char *log_level_to_str(int log_level) { switch (log_level) { case UTIL_LOG_ERROR: return "ERROR"; case UTIL_LOG_WARN: return "WARN"; case UTIL_LOG_INFO: return "INFO"; case UTIL_LOG_DEBUG: return "DEBUG"; case UTIL_LOG_TRACE: return "TRACE"; default: return "UNKNW"; } } static void log_helper(FILE *fp, int log_level, const char *fmt, va_list args) { if (current_log_level < log_level) return; fprintf(fp, "%5s: ", log_level_to_str(log_level)); vfprintf(fp, fmt, args); } /** * Changes the current log level * * @param[in] log_level A new log level to be set as the current one */ void util_log_set_level(int log_level) { current_log_level = log_level; } /** * Outputs the given message on stderr * * The given message is printed only if the current log level is >= than the given one. * * @param[in] log_level Log level starting from which the given message shall be printed * @param[in] fmt Format string for generation of the log message * @param[in] ... Parameters for format string */ void util_log_print(int log_level, const char *fmt, ...) { va_list args; va_start(args, fmt); log_helper(stderr, log_level, fmt, args); va_end(args); } s390-tools-2.38.0/libutil/util_log_example.c000066400000000000000000000033051502674226300206110ustar00rootroot00000000000000/** * util_log_example - Example program for util_log * * Copyright IBM Corp. 2021 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ //! [code] #include "lib/util_opt.h" #include "lib/util_prg.h" #include "lib/util_log.h" static const struct util_prg prg = { .desc = "Example for util_log.", .copyright_vec = { { .owner = "IBM Corp.", .pub_first = 2021, .pub_last = 2021, }, UTIL_PRG_COPYRIGHT_END } }; static struct util_opt opt_vec[] = { { .option = { "verbose", no_argument, NULL, 'V' }, .desc = "Print verbose messages to stderr. " "This option may be given multiple times and " "each time this option is given the verbosity level is " "increased.", }, UTIL_OPT_HELP, UTIL_OPT_VERSION, UTIL_OPT_END }; int main(int argc, char *argv[]) { int verbose = -1; util_prg_init(&prg); util_opt_init(opt_vec, NULL); while (1) { int opt = util_opt_getopt_long(argc, argv); if (opt == -1) break; switch (opt) { case 'h': util_prg_print_help(); util_opt_print_help(); exit(EXIT_SUCCESS); case 'v': util_prg_print_version(); exit(EXIT_SUCCESS); case 'V': verbose++; break; case '?': default: fprintf(stderr, "Try '--help' for more information.\n"); exit(EXIT_FAILURE); } } util_log_set_level(verbose); util_log_print(UTIL_LOG_ERROR, "This is an ERROR message\n"); util_log_print(UTIL_LOG_WARN, "This is a WARN message\n"); util_log_print(UTIL_LOG_INFO, "This is an INFO message\n"); util_log_print(UTIL_LOG_DEBUG, "This is a DEBUG message\n"); util_log_print(UTIL_LOG_TRACE, "This is a TRACE message\n"); return EXIT_SUCCESS; } //! [code] s390-tools-2.38.0/libutil/util_opt.c000066400000000000000000000170731502674226300171260ustar00rootroot00000000000000/* * util - Utility function library * * Parse the command line options * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include "lib/util_base.h" #include "lib/util_libc.h" #include "lib/util_opt.h" #include "lib/util_panic.h" #include "lib/util_prg.h" /* * Private data */ /// @cond static struct util_opt_l { /* Option character string for getopt_long() */ char *opt_str; /* Option array for getopt_long() */ struct option *option_vec; /* Original util_opt array */ struct util_opt *opt_vec; /* Length of longest option string */ int opt_max; /* Command used for parsing */ const char *command; } l; /// @endcond #define util_opt_iterate(opt) \ for (opt = &l.opt_vec[0]; opt->desc != NULL; opt++) #define MAX_OPTLEN 256 static int opt_max_len(void); static bool opt_is_active(struct util_opt *opt); /** * Initialize the command line options * * Build short option string and long option array to be used for getopt_long(). * The ":" prefix is added to the short option string for handling of "missing * required arguments". * * @param[in] opt_vec Option array * @param[in] opt_prefix Optional option string prefix */ void util_opt_init(struct util_opt *opt_vec, const char *opt_prefix) { int i, j, count; char *str; size_t prefix_len = opt_prefix ? strlen(opt_prefix) : 0; opterr = 0; /* Get number of options */ for (i = 0, count = 0; opt_vec[i].desc != NULL; i++) if (opt_is_active(&opt_vec[i])) count++; /* * Allocate short option string for worst case when all options have * optional parameters e.g "x::" and long option string. */ l.opt_str = util_malloc(sizeof(char) * count * 3 + 2 + prefix_len); l.option_vec = util_malloc(sizeof(struct option) * (count + 1)); l.opt_vec = opt_vec; str = l.opt_str; if (opt_prefix) { strcpy(str, opt_prefix); str += prefix_len; } /* Force getopt_long() to return ':' for missing required arguments */ *str++ = ':'; /* Construction of input structures for getopt_long() function. */ for (i = 0, j = 0; opt_vec[i].desc != NULL; i++) { if (!opt_is_active(&opt_vec[i])) continue; if (opt_vec[i].flags & UTIL_OPT_FLAG_SECTION) continue; if (!(opt_vec[i].flags & UTIL_OPT_FLAG_NOLONG)) { memcpy(&l.option_vec[j++], &opt_vec[i].option, sizeof(struct option)); } if (opt_vec[i].flags & UTIL_OPT_FLAG_NOSHORT) continue; *str++ = opt_vec[i].option.val; switch (opt_vec[i].option.has_arg) { case no_argument: break; case required_argument: *str++ = ':'; break; case optional_argument: *str++ = ':'; *str++ = ':'; break; default: util_panic("Unexpected \"has_arg\" parameter: %d\n", opt_vec[i].option.has_arg); } } /* Add end marker to option array and short option string */ memset(&l.option_vec[j], 0, sizeof(struct option)); *str = '\0'; } /** * Set the current command for command line option processing * * @param[in] command The current command or NULL for no command */ void util_opt_set_command(const char *command) { l.command = command; } /* * Return true, if option belongs to current command setting */ static bool opt_is_active(struct util_opt *opt) { if (!opt->command || !l.command) return true; return (strcmp(opt->command, l.command) == 0); } /** * Wrapper for getopt_long * * @param[in] argc Count of command line parameters * @param[in] argv Array of command line parameters */ int util_opt_getopt_long(int argc, char *argv[]) { struct util_opt *opt; int val; val = getopt_long(argc, argv, l.opt_str, l.option_vec, NULL); switch (val) { case ':': case '?': case -1: break; default: if (!l.command) break; util_opt_iterate(opt) { if (!opt_is_active(opt)) continue; if (opt->option.val == val) goto out; } /* No valid option found for command */ val = '?'; if (optarg) optind--; break; } out: return val; } /* * Format option name: Add short, long option and argument (as applicable) */ static void format_opt(char *buf, size_t maxlen, const struct util_opt *opt) { int has_arg, flags, rc; char val, *arg_str; const char *name; has_arg = opt->option.has_arg; name = opt->option.name; val = opt->option.val; flags = opt->flags; /* Prepare potential option argument string */ if (has_arg == optional_argument) { if (flags & UTIL_OPT_FLAG_NOLONG) util_asprintf(&arg_str, "[%s]", opt->argument); else util_asprintf(&arg_str, "[=%s]", opt->argument); } else if (has_arg == required_argument) { util_asprintf(&arg_str, " %s", opt->argument); } else { util_asprintf(&arg_str, ""); } /* Format the option */ if (flags & UTIL_OPT_FLAG_NOLONG) rc = snprintf(buf, maxlen, "-%c%s", val, arg_str); else if (flags & UTIL_OPT_FLAG_NOSHORT) rc = snprintf(buf, maxlen, " --%s%s", name, arg_str); else rc = snprintf(buf, maxlen, "-%c, --%s%s", val, name, arg_str); util_assert(rc < (int)maxlen, "Option too long: %s\n", name); free(arg_str); } /* * Return true, if option is to be printed for the current command setting */ static bool should_print_opt(const struct util_opt *opt) { if (l.command) { /* Print only options that belong to command */ return opt->command ? !strcmp(opt->command, l.command) : false; } else { /* Print only common options (standard for non-command tools) */ return opt->command ? false : true; } } /* * Return size of the longest formatted option */ static int opt_max_len(void) { const struct util_opt *opt; unsigned int max = 0; char opt_str[MAX_OPTLEN]; util_opt_iterate(opt) { if (opt->flags & UTIL_OPT_FLAG_SECTION) continue; if (!should_print_opt(opt)) continue; format_opt(opt_str, MAX_OPTLEN, opt); max = MAX(max, strlen(opt_str)); } return max; } /** * Print an option name, followed by a description indented to fit the * longest option name */ void util_opt_print_indented(const char *opt, const char *desc) { printf(" %-*s ", l.opt_max + 1, opt); util_print_indented(desc, 3 + l.opt_max); } /** * Print the usage of the command line options to the console */ void util_opt_print_help(void) { char opt_str[MAX_OPTLEN]; struct util_opt *opt; int first = 1; /* * Create format string: " -%c, --%-s %s" * * Example: * * -p, --print STRING Print STRING to console */ l.opt_max = opt_max_len(); util_opt_iterate(opt) { if (!should_print_opt(opt)) continue; if (opt->flags & UTIL_OPT_FLAG_SECTION) { printf("%s%s\n", first ? "" : "\n", opt->desc); first = 0; continue; } format_opt(opt_str, MAX_OPTLEN, opt); util_opt_print_indented(opt_str, opt->desc); } } /** * Print option parsing error message * * This function should be used when the return code of the * util_opt_getopt_long() function returns a character that does * not match any of the expected options. * * @param[in] opt Short option returned by getopt_long() * @param[in] argv Option array */ void util_opt_print_parse_error(char opt, char *argv[]) { char optopt_str[3]; switch (opt) { case ':': /* A required option argument has not been specified */ util_prg_print_required_arg(argv[optind - 1]); break; case '?': /* An invalid option has been specified */ if (optopt) { /* Short option */ sprintf(optopt_str, "-%c", optopt); util_prg_print_invalid_option(optopt_str); } else { /* Long option */ util_prg_print_invalid_option(argv[optind - 1]); } break; default: util_panic("Option '%c' should not be handled here\n", opt); } } s390-tools-2.38.0/libutil/util_opt_command_example.c000066400000000000000000000122711502674226300223320ustar00rootroot00000000000000/* * util_opt_command_example - Example program for util_opt with commands * * Copyright 2017 IBM Corp. * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ //! [code] #include #include #include #include #include "lib/util_opt.h" #include "lib/util_prg.h" #define OPT_NOSHORT 256 #define COMMAND_PULL "pull" #define COMMAND_PUSH "push" /* * Define the command line options */ static struct util_opt opt_vec[] = { { .desc = "OPTIONS", .flags = UTIL_OPT_FLAG_SECTION, .command = COMMAND_PULL, }, { .option = { "single", no_argument, NULL, 's'}, .desc = "A single option without an argument", .command = COMMAND_PULL, }, { .option = { "req_arg", required_argument, NULL, 'r'}, .argument = "REQ_ARG", .desc = "Option with a required argument REQ_ARG", .command = COMMAND_PULL, }, { .option = { "test", required_argument, NULL, 't'}, .argument = "TEST", .desc = "Option 'test' with a required argument TEST for pull", .command = COMMAND_PULL, }, { .desc = "OPTIONS", .flags = UTIL_OPT_FLAG_SECTION, .command = COMMAND_PUSH, }, { .option = { "noshort", no_argument, NULL, OPT_NOSHORT}, .desc = "Option with only a long name", .flags = UTIL_OPT_FLAG_NOSHORT, .command = COMMAND_PUSH, }, { .option = { NULL, no_argument, NULL, 'l'}, .desc = "Option with only a short name", .flags = UTIL_OPT_FLAG_NOLONG, .command = COMMAND_PUSH, }, { .option = { "test", no_argument, NULL, 't'}, .desc = "Option 'test' without an argument for push", .command = COMMAND_PUSH, }, UTIL_OPT_SECTION("COMMON OPTIONS"), /* Standard option: -h,--help */ UTIL_OPT_HELP, /* Standard option: -v,--version */ UTIL_OPT_VERSION, /* End-marker for option vector */ UTIL_OPT_END }; static char usage_global_start[] = "Usage: util_opt_command_example [COMMAND] [OPTIONS]\n" "\n" "Demonstrate how programs with commands can use the \"util_opt\" library.\n" "\n" "COMMANDS\n" " push Push some content to somewhere\n" " pull Pull some content from somewhere\n"; static char usage_global_end[] = "For more information use 'util_opt_command_example COMMAND --help'.\n"; static char usage_push_start[] = "Usage: util_opt_command_example push [OPTIONS]\n" "\n" "Push some content to somewhere.\n"; static char usage_pull_start[] = "Usage: util_opt_command_example pull [OPTIONS]\n" "\n" "Pull some content from somewhere.\n"; /* * Print help header for command or program */ static void command_print_help_start(const char *command) { if (command == NULL) printf("%s", usage_global_start); else if (strcmp(command, COMMAND_PUSH) == 0) printf("%s", usage_push_start); else if (strcmp(command, COMMAND_PULL) == 0) printf("%s", usage_pull_start); printf("\n"); } /* * Print help footer */ static void command_print_help_end(const char *command) { if (command == NULL) printf("\n%s", usage_global_end); } /* * Parse the command line options with util_opt functions */ int main(int argc, char *argv[]) { int c, my_argc = argc; char **my_argv = argv; char *command = NULL; /* The command name is the very first argument */ if (argc >= 2 && strncmp(argv[1], "-", 1) != 0) { command = argv[1]; my_argc--; my_argv = &argv[1]; if (strcasecmp(command, COMMAND_PULL) != 0 && strcasecmp(command, COMMAND_PUSH) != 0) { fprintf(stderr, "%s: Invalid command '%s'\n", program_invocation_short_name, argv[1]); util_prg_print_parse_error(); return EXIT_FAILURE; } } /* Set the current command (if any) */ util_opt_set_command(command); util_prg_set_command(command); /* Install option vector */ util_opt_init(opt_vec, NULL); /* Parse all options specified in my_argv[] */ while (1) { /* Get the next option 'c' from my_argv[] */ c = util_opt_getopt_long(my_argc, my_argv); /* No more options on command line? */ if (c == -1) break; /* Find the right action for option 'c' */ switch (c) { case 'h': command_print_help_start(command); util_opt_print_help(); command_print_help_end(command); return EXIT_SUCCESS; case 'v': printf("Specified: --version\n"); return EXIT_SUCCESS; case 's': printf("Specified: --single\n"); break; case 'r': printf("Specified: --req_arg %s\n", optarg); break; case OPT_NOSHORT: printf("Specified: --noshort\n"); break; case 'l': printf("Specified: -l\n"); break; case 't': printf("Specified: --test %s\n", optarg ? optarg : ""); break; default: util_opt_print_parse_error(c, my_argv); return EXIT_FAILURE; } } if (optind < my_argc) { util_prg_print_arg_error(my_argv[optind]); return EXIT_FAILURE; } if (command == NULL) { fprintf(stderr, "%s: Command is required\n", program_invocation_short_name); util_prg_print_parse_error(); return EXIT_FAILURE; } if (strcasecmp(command, COMMAND_PULL) == 0) { printf("Run the pull command\n"); return EXIT_SUCCESS; } else if (!strcasecmp(command, COMMAND_PUSH)) { printf("Run the push command\n"); return EXIT_SUCCESS; } fprintf(stderr, "%s: Invalid command '%s'\n", program_invocation_short_name, argv[1]); util_prg_print_parse_error(); return EXIT_FAILURE; } //! [code] s390-tools-2.38.0/libutil/util_opt_example.c000066400000000000000000000062121502674226300206320ustar00rootroot00000000000000/* * util_opt_example - Example program for util_opt * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ //! [code] #include #include #include "lib/util_opt.h" #define OPT_NOSHORT 256 /* * Define the command line options */ static struct util_opt opt_vec[] = { UTIL_OPT_SECTION("OPTION WITHOUT ARGUMENTS"), /* Our own options */ { .option = { "single", no_argument, NULL, 's'}, .desc = "A single option without an argument", }, UTIL_OPT_SECTION("OPTIONS WITH ARGUMENTS"), { .option = { "req_arg", required_argument, NULL, 'r'}, .argument = "REQ_ARG", .desc = "Option with a required argument REQ_ARG", }, { /* * NOTE: For specifying an optional parameter OPT_ARG use * either "-oOPT_ARG" or "--opt_arg=OPT_ARG" on the commandline. * Specifying "-o OPT_ARG" or "--opt_arg OPT_ARG" will not work. */ .option = { "opt_arg", optional_argument, NULL, 'o'}, .argument = "OPT_ARG", .desc = "Option with an optional argument OPT_ARG. " \ "We don't recommend using this feature.", }, UTIL_OPT_SECTION("OPTION WITHOUT SHORT OPTION"), { .option = { "noshort", no_argument, NULL, OPT_NOSHORT}, .desc = "Option with only a long name", .flags = UTIL_OPT_FLAG_NOSHORT, }, UTIL_OPT_SECTION("OPTION WITHOUT LONG OPTION"), { .option = { NULL, no_argument, NULL, 'l'}, .desc = "Option with only a short name", .flags = UTIL_OPT_FLAG_NOLONG, }, UTIL_OPT_SECTION("OPTION WITH MANUALLY FORMATTED DESCRIPTION"), { .option = { "manual", no_argument, NULL, 'm' }, .desc = "Option descriptions can be formatted\n" \ "using the new line character '\\n' to:\n" \ " - Display descriptions more meaningful\n" \ " - Create lists within the description", }, UTIL_OPT_SECTION("STANDARD OPTIONS"), /* Standard option: -h,--help */ UTIL_OPT_HELP, /* Standard option: -v,--version */ UTIL_OPT_VERSION, /* End-marker for option vector */ UTIL_OPT_END }; /* * Parse the command line options with util_opt functions */ int main(int argc, char *argv[]) { int c; /* Install option vector */ util_opt_init(opt_vec, NULL); /* Parse all options specified in argv[] */ while (1) { /* Get the next option 'c' from argv[] */ c = util_opt_getopt_long(argc, argv); /* No more options on command line? */ if (c == -1) break; /* Find the right action for option 'c' */ switch (c) { case 'h': util_opt_print_help(); return EXIT_SUCCESS; case 'v': printf("Specified: --version\n"); return EXIT_SUCCESS; case 's': printf("Specified: --single\n"); break; case 'o': if (optarg != NULL) printf("Specified: --opt_arg %s\n", optarg); else printf("Specified: --opt_arg [without arg]\n"); break; case 'r': printf("Specified: --req_arg %s\n", optarg); break; case OPT_NOSHORT: printf("Specified: --noshort\n"); break; case 'l': printf("Specified: -l\n"); break; case 'm': printf("Specified: --manual\n"); break; default: util_opt_print_parse_error(c, argv); return EXIT_FAILURE; } } return EXIT_SUCCESS; } //! [code] s390-tools-2.38.0/libutil/util_panic.c000066400000000000000000000057371502674226300174220ustar00rootroot00000000000000/* * util - Utility function library * * Collect FFDC data for unexpected errors * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include "lib/util_base.h" #include "lib/util_panic.h" /* * Obtain a backtrace and print it to stderr * * To get symbols, link the code with "-rdynamic". */ static void print_backtrace(void) { void *array[256]; size_t i, size; char **strings; fprintf(stderr, "Backtrace:\n\n"); size = backtrace(array, ARRAY_SIZE(array)); strings = backtrace_symbols(array, size); if (strings == NULL) { fprintf(stderr, " Could not obtain backtrace (ENOMEM)\n"); return; } for (i = 0; i < size; i++) fprintf(stderr, " %s\n", strings[i]); free(strings); } /* * Check for core ulimit */ static void ulimit_core_check(void) { struct rlimit limit; if (getrlimit(RLIMIT_CORE, &limit) != 0) return; if (limit.rlim_cur != 0) return; fprintf(stderr, "Core dump size is zero. To get a full core dump use 'ulimit -c unlimited'.\n"); } /* * Print FFDC data and then abort */ static void panic_finish(const char *func, const char *file, int line, const char *fmt, va_list ap) { /* Write panic error string */ fprintf(stderr, "\n"); fprintf(stderr, "Error string:\n"); fprintf(stderr, "\n"); fprintf(stderr, " "); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); /* Write file, line number, and function name */ fprintf(stderr, "Location:\n\n"); fprintf(stderr, " %s:%d: %s()\n", file, line, func); fprintf(stderr, "\n"); /* Print the function backtrace */ print_backtrace(); fprintf(stderr, "\n"); ulimit_core_check(); fprintf(stderr, "----------------------------------------------------------------------->8-----\n"); abort(); } /* * Do panic processing if the assumption is not true */ void __util_assert(const char *assertion_str, const char *func, const char *file, int line, int assumption, const char *fmt, ...) { va_list ap; if (assumption) return; va_start(ap, fmt); fprintf(stderr, "---8<-------------------------------------------------------------------------\n"); fprintf(stderr, "ASSERTION FAILED: The application terminated due to an internal or OS error\n"); fprintf(stderr, "\n"); fprintf(stderr, "The following assumption was *not* true:\n"); fprintf(stderr, "\n"); fprintf(stderr, " %s\n", assertion_str); panic_finish(func, file, line, fmt, ap); } /* * Do panic processing */ void __noreturn __util_panic(const char *func, const char *file, int line, const char *fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr, "---8<-------------------------------------------------------------------------\n"); fprintf(stderr, "PANIC: The application terminated due to an unrecoverable error\n"); panic_finish(func, file, line, fmt, ap); while(1); } s390-tools-2.38.0/libutil/util_panic_example.c000066400000000000000000000042161502674226300211240ustar00rootroot00000000000000/** * util_panic_example - Example program for util_panic * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ //! [code] #include #include #include #include #include #include #include "lib/zt_common.h" #include "lib/util_panic.h" /* Make functions noinline to have a nice backtrace */ /* * Test util_panic() */ __noinline void panic_test_func(void) { fprintf(stderr, "Testing util_panic() now\n"); util_panic("Adieu beautiful world ...\n"); fprintf(stderr, "You should not see this\n"); } /* * Test util_panic() with errno handling */ __noinline void panic_errno_test_func(void) { const char *file = "i_do_not_exist"; if (fopen("i_do_not_exist", "r") == NULL) { util_panic("Open file \"%s\" failed: %s\n", file, strerror(errno)); } fprintf(stderr, "You should not see this\n"); } /* * Test util_assert() */ __noinline void assert_test_func(void) { char *drink_actual = "beer"; fprintf(stderr, "Testing util_assert() now\n"); util_assert(strcmp(drink_actual, "water") == 0, "We expected \"%s\" but got \"%s\"\n", "water", drink_actual); fprintf(stderr, "You should not see this\n"); } /* * Demonstrate util_panic() and util_assert() */ int main(int argc, char *argv[]) { struct rlimit rlim_unlimited = {-1, -1}; struct rlimit rlim_zero = {0, 0}; if (argc != 2) goto fail; if (strcmp(argv[1], "util_panic") == 0) { fprintf(stderr, "Disable core files: ulimit -c 0\n"); setrlimit(RLIMIT_CORE, &rlim_zero); /* Do the panic */ panic_test_func(); } else if (strcmp(argv[1], "util_panic_errno") == 0) { setrlimit(RLIMIT_CORE, &rlim_unlimited); /* Do the panic */ panic_errno_test_func(); } else if (strcmp(argv[1], "util_assert") == 0) { fprintf(stderr, "Enable core files: ulimit -c unlimited\n"); setrlimit(RLIMIT_CORE, &rlim_unlimited); /* Do the assertion */ assert_test_func(); } fail: fprintf(stderr, "Usage: %s util_panic|util_panic_errno|util_assert\n", argv[0]); return EXIT_FAILURE; } //! [code] s390-tools-2.38.0/libutil/util_part.c000066400000000000000000000162551502674226300172730ustar00rootroot00000000000000/* * util - Utility function library * * Partition detection functions * * Copyright IBM Corp. 2013, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include "lib/util_part.h" #define GPT_SIGNATURE 0x4546492050415254ULL /* EFI PART */ #define MBR_SIGNATURE 0x55aa #define MBR_PART_TYPE_DOS_EXT 0x05 /* DOS extended partition */ #define MBR_PART_TYPE_WIN98_EXT 0x0f /* Windows 98 extended partition */ #define MBR_PART_TYPE_LINUX_EXT 0x85 /* Linux extended partition */ #define MBR_PART_TYPE_GPT 0xee /* GPT partition */ #define MBR_EXT_PART_NUM_FIRST 5 /* Partition number for first logical vol */ /* * MBR/MSDOS partition entry */ struct mbr_part_entry { uint8_t status; uint8_t chs_start[3]; uint8_t type; uint8_t chs_end[3]; uint32_t blk_start; uint32_t blk_cnt; } __attribute__((packed)); /* * Master Boot Record (MBR) */ struct mbr { uint8_t reserved[0x1be]; struct mbr_part_entry part_entry_vec[4]; uint16_t signature; } __attribute__((packed)); /* * GUID Partition Table (GPT) header */ struct gpt { uint64_t signature; uint32_t version; uint32_t hdr_size; uint32_t hdr_crc; uint32_t reserved1; uint64_t blk_cur; uint64_t blk_back; uint64_t blk_first; uint64_t blk_last; uint8_t guid[16]; uint64_t part_tab_blk_start; uint32_t part_tab_cnt; uint32_t part_tab_entry_size; uint32_t part_tab_crc; } __attribute__((packed)); /* * GPT partition entry */ struct gpt_part_entry { uint8_t type[16]; uint8_t guid[16]; uint64_t blk_start; uint64_t blk_end; uint64_t attr; char name[72]; } __attribute__((packed)); /* * Check for extended partition */ static int mbr_part_is_ext(uint8_t type) { if ((type == MBR_PART_TYPE_DOS_EXT) || (type == MBR_PART_TYPE_WIN98_EXT) || (type == MBR_PART_TYPE_LINUX_EXT)) return 1; return 0; } /* * Check if disk has a classic MBR partion table */ static int mbr_table_valid(struct mbr *mbr) { return mbr->signature == MBR_SIGNATURE; } /* * Search partition in logical volumes of an extended partition */ static int mbr_table_ext_search(int fh, size_t blk_start_mbr, size_t blk_start, size_t blk_cnt, size_t blk_size, int part_num) { size_t start, cnt, start_next; struct mbr mbr; /* Read MBR for logical volume */ if (lseek(fh, blk_start_mbr * blk_size, SEEK_SET) == (off_t)-1) return -1; if (read(fh, &mbr, sizeof(mbr)) == -1) return -1; /* Check for invalid MBR or last entry */ if (mbr.signature != MBR_SIGNATURE) return -1; if (mbr.part_entry_vec[0].blk_start == 0) return -1; /* First entry contains a relative offset for current logical volume */ start = blk_start_mbr + le32toh(mbr.part_entry_vec[0].blk_start); cnt = le32toh(mbr.part_entry_vec[0].blk_cnt); if ((start <= blk_start) && (cnt >= (blk_start - start) + blk_cnt)) return part_num; /* Second entry contains relative offset for next logical volume */ start_next = le32toh(mbr.part_entry_vec[1].blk_start); if (start_next == 0) return 0; start_next += blk_start_mbr; /* Recursively search for next logical volume in chain */ return mbr_table_ext_search(fh, start_next, blk_start, blk_cnt, blk_size, part_num + 1); } /* * Search partition in MBR partition table */ static int mbr_table_search(int fh, struct mbr *mbr, size_t blk_start, size_t blk_cnt, size_t blk_size, int *part_ext) { int part_num_ext, part_num; size_t start, cnt; uint8_t type; for (part_num = 1; part_num <= 4; part_num++) { type = mbr->part_entry_vec[part_num - 1].type; start = le32toh(mbr->part_entry_vec[part_num - 1].blk_start); cnt = le32toh(mbr->part_entry_vec[part_num - 1].blk_cnt); if (start == 0) /* Empty slot */ continue; /* * The kernel sets count for extended partitions explicitly. * Therefore we do not check count here. */ if (mbr_part_is_ext(type) && (start <= blk_start)) { *part_ext = 1; return part_num; } if ((start <= blk_start) && (cnt >= (blk_start - start) + blk_cnt)) return part_num; if (!mbr_part_is_ext(type)) continue; part_num_ext = mbr_table_ext_search(fh, start, blk_start, blk_cnt, blk_size, MBR_EXT_PART_NUM_FIRST); if (part_num_ext != 0) return part_num_ext; } return 0; } /* * Search partition in GPT partition table */ static int gpt_table_search(int fh, struct gpt *gpt, size_t blk_start, size_t blk_cnt, size_t blk_size) { size_t start, end, part_tab_blk_start, blk_end; uint32_t part_tab_cnt, part_tab_entry_size; struct gpt_part_entry *part_entry; unsigned int part_num; blk_end = blk_start + blk_cnt - 1; part_tab_entry_size = le32toh(gpt->part_tab_entry_size); part_tab_cnt = le32toh(gpt->part_tab_cnt); part_tab_blk_start = le64toh(gpt->part_tab_blk_start); if (lseek(fh, part_tab_blk_start * blk_size, SEEK_SET) == (off_t)-1) return -1; for (part_num = 1; part_num <= part_tab_cnt; part_num++) { char buf[part_tab_entry_size]; part_entry = (struct gpt_part_entry *) buf; if (read(fh, buf, sizeof(buf)) == -1) return -1; start = le64toh(part_entry->blk_start); end = le64toh(part_entry->blk_end); if (start == 0) /* Empty slot */ continue; if ((start <= blk_start) && (end >= blk_end)) return part_num; } return 0; } /* * Check if disk has a GPT partition table */ static int gpt_table_valid(struct gpt *gpt, struct mbr *mbr) { int cnt, part_num; uint32_t start; uint8_t type; if (gpt->signature != GPT_SIGNATURE) return 0; /* Check for protective MBR (one reserved GPT partition) */ for (part_num = 1, cnt = 0; part_num <= 4; part_num++) { start = le32toh(mbr->part_entry_vec[part_num - 1].blk_start); type = mbr->part_entry_vec[part_num - 1].type; if (!start) continue; if (type != MBR_PART_TYPE_GPT) return 0; if (++cnt > 1) return 0; } return 1; } /* * Search for partition with given start block and count * * Return partition number when found, 0 when not found, and on error -1 * Set "part_ext" to 1 for extended partitions otherwise to 0. */ int util_part_search_fh(int fh, size_t blk_start, size_t blk_cnt, size_t blk_size, int *part_ext) { struct gpt gpt; struct mbr mbr; if (lseek(fh, 0, SEEK_SET) == (off_t)-1) return -1; if (read(fh, &mbr, sizeof(mbr)) == -1) return -1; if (lseek(fh, blk_size, SEEK_SET) == (off_t)-1) return -1; if (read(fh, &gpt, sizeof(gpt)) == -1) return -1; *part_ext = 0; if (gpt_table_valid(&gpt, &mbr)) return gpt_table_search(fh, &gpt, blk_start, blk_cnt, blk_size); if (mbr_table_valid(&mbr)) return mbr_table_search(fh, &mbr, blk_start, blk_cnt, blk_size, part_ext); return -1; } /* * Search for partition with given start block and count * * Return partition number when found, 0 when not found, and on error -1 * Set "part_ext" to 1 for extended partitions otherwise to 0. */ int util_part_search(const char *device, size_t blk_start, size_t blk_cnt, size_t blk_size, int *part_ext) { int rc, fh; fh = open(device, O_RDONLY); if (fh == -1) return -1; rc = util_part_search_fh(fh, blk_start, blk_cnt, blk_size, part_ext); close(fh); return rc; } s390-tools-2.38.0/libutil/util_path.c000066400000000000000000000126401502674226300172530ustar00rootroot00000000000000/* * util - Utility function library * * Work with paths * * Copyright IBM Corp. 2016, 2019 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include "lib/util_base.h" #include "lib/util_libc.h" #include "lib/util_path.h" #include "lib/util_prg.h" /* * Verify that directory exists */ static void verify_dir(const char *dir) { struct stat sb; int rc; rc = stat(dir, &sb); if (rc < 0) err(EXIT_FAILURE, "Could not access directory: %s", dir); if (!S_ISDIR(sb.st_mode)) errx(EXIT_FAILURE, "Is not a directory: %s", dir); } /* * Return sysfs mount point * * The caller must free the memory for mount_point. */ static void sys_mount_point(char **mount_point) { char *dir; /* Check the environment variable */ dir = secure_getenv("SYSFS_ROOT"); if (dir) *mount_point = util_strdup(dir); else *mount_point = util_strdup("/sys"); verify_dir(*mount_point); } /** * Construct a sysfs path * * The arguments of the function are used to specify a subdirectory under * sysfs root. * * @param[in] fmt Format string for path * @param[in] ... Variable arguments for format string * * @returns Allocated path */ char *util_path_sysfs(const char *fmt, ...) { char *path, *fmt_path, *sysfs; va_list ap; va_start(ap, fmt); util_vasprintf(&fmt_path, fmt, ap); va_end(ap); sys_mount_point(&sysfs); /* Format and return full sysfs path */ util_asprintf(&path, "%s/%s", sysfs, fmt_path); free(fmt_path); free(sysfs); return path; } /** * Test if path exists and is readable * * This function has the same semantics as "-r path" in bash. * * @param[in] fmt Format string for path to test * @param[in] ... Variable arguments for format string * * @returns true Path exists and is readable * false Otherwise */ bool util_path_is_readable(const char *fmt, ...) { va_list ap; char *path; bool rc; UTIL_VASPRINTF(&path, fmt, ap); rc = access(path, R_OK) == 0; free(path); return rc; } /** * Test if path exists and is writable * * This function has the same semantics as "-w path" in bash. * * @param[in] fmt Format string for path to test * @param[in] ... Variable arguments for format string * * @returns true Path exists and is writable * false Otherwise */ bool util_path_is_writable(const char *fmt, ...) { va_list ap; char *path; bool rc; UTIL_VASPRINTF(&path, fmt, ap); rc = access(path, W_OK) == 0; free(path); return rc; } /** * Test if path exists and is a regular file * * This function has the same semantics as "-f path" in bash. * * @param[in] fmt Format string for path to test * @param[in] ... Variable arguments for format string * * @returns true Path exists and is a regular file * false Otherwise */ bool util_path_is_reg_file(const char *fmt, ...) { bool rc = false; struct stat sb; va_list ap; char *path; UTIL_VASPRINTF(&path, fmt, ap); if (stat(path, &sb) == 0) rc = S_ISREG(sb.st_mode); free(path); return rc; } /** * Test if path exists and is a directory * * This function has the same semantics as "-d path" in bash. * * @param[in] fmt Format string for path to test * @param[in] ... Variable arguments for format string * * @returns true Path exists and is a directory * false Otherwise */ bool util_path_is_dir(const char *fmt, ...) { bool rc = false; struct stat sb; va_list ap; char *path; UTIL_VASPRINTF(&path, fmt, ap); if (stat(path, &sb) == 0) rc = S_ISDIR(sb.st_mode); free(path); return rc; } /** * Test if path to directory or file exists * * This function has the same semantics as "-e path" in bash. * * @param[in] fmt Format string for path to test * @param[in] ... Variable arguments for format string * * @returns true Path exists * false Otherwise */ bool util_path_exists(const char *fmt, ...) { va_list ap; char *path; bool rc; UTIL_VASPRINTF(&path, fmt, ap); rc = access(path, F_OK) == 0; free(path); return rc; } /** * Test if path exists, is a regular file, and permission is read-only * * @param[in] fmt Format string for path to test * @param[in] ... Variable arguments for format string * * @returns true Path exists, is a regular file, and permission does * not allow any write but allows read * false Otherwise */ bool util_path_is_readonly_file(const char *fmt, ...) { bool rc = false; struct stat sb; va_list ap; char *path; UTIL_VASPRINTF(&path, fmt, ap); if (stat(path, &sb) == 0) { rc = S_ISREG(sb.st_mode) && (sb.st_mode & 0222) == 0 && (sb.st_mode & 0444) != 0; } free(path); return rc; } /** * Test if path exists, is a regular file, and permission is write-only * * @param[in] fmt Format string for path to test * @param[in] ... Variable arguments for format string * * @returns true Path exists, is a regular file, and permission does * not allow any read but allows write * false Otherwise */ bool util_path_is_writeonly_file(const char *fmt, ...) { bool rc = false; struct stat sb; va_list ap; char *path; UTIL_VASPRINTF(&path, fmt, ap); if (stat(path, &sb) == 0) { rc = S_ISREG(sb.st_mode) && (sb.st_mode & 0222) != 0 && (sb.st_mode & 0444) == 0; } free(path); return rc; } s390-tools-2.38.0/libutil/util_path_example.c000066400000000000000000000047061502674226300207720ustar00rootroot00000000000000/** * util_path_example - Example program for util_path * * Copyright IBM Corp. 2016, 2019 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ //! [code] #include #include #include #include #include #include #include "lib/util_path.h" #include "lib/util_prg.h" /* * Define program description */ const struct util_prg prg = { .desc = "Sample for util_dirlibrary", .copyright_vec = { { .owner = "IBM Corp.", .pub_first = 2001, .pub_last = 2019, }, UTIL_PRG_COPYRIGHT_END } }; /* * Test util_path_sysfs() */ static void test_util_path_sysfs(void) { const char * const subsys_vec[] = {"cpu", "memory"}; char *path; int i; /* Construct sysfs paths for the subsystems */ for (i = 0; i < 2; i++) { path = util_path_sysfs("devices/system/%s", subsys_vec[i]); printf("Path for %6s: \"%s\"\n", subsys_vec[i], path); free(path); } } /* * Test path */ static void test_path(const char *path) { printf("%-20s: ", path); if (util_path_exists(path)) printf("exists=yes "); else printf("exists=no "); if (util_path_is_readable(path)) printf("read=yes "); else printf("read=no "); if (util_path_is_writable(path)) printf("write=yes "); else printf("write=no "); if (util_path_is_reg_file(path)) printf("reg_file=yes "); else printf("reg_file=no "); if (util_path_is_dir(path)) printf("dir=yes "); else printf("dir=no "); if (util_path_is_readonly_file(path)) printf("read-only_file=yes "); else printf("read-only_file=no "); if (util_path_is_writeonly_file(path)) printf("write-only_file=yes"); else printf("write-only_file=no "); printf("\n"); } /* * Test util_path_is_xxx() */ static void test_util_path_is_xxx(void) { test_path("util_path_example.c"); test_path("/tmp"); test_path("i_do_not_exist"); } /* * Usage: util_path_example sysfs [MOUNT_POINT] | is_xxx */ int main(int argc, char *argv[]) { util_prg_init(&prg); if (argc < 2) goto out_fail; if (strcmp(argv[1], "sysfs") == 0) { if (argc == 3) { /* Change sysfs path via SYSFS_ROOT env variable */ setenv("SYSFS_ROOT", argv[2], 1); } test_util_path_sysfs(); } else if (strcmp(argv[1], "is_xxx") == 0) { test_util_path_is_xxx(); } else { goto out_fail; } return EXIT_SUCCESS; out_fail: errx(EXIT_FAILURE, "Usage: %s sysfs [MOUNT_POINT] | is_xxx", argv[0]); } //! [code] s390-tools-2.38.0/libutil/util_prg.c000066400000000000000000000057701502674226300171150ustar00rootroot00000000000000/* * util - Utility function library * * Print standard program messages * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include "lib/util_base.h" #include "lib/util_prg.h" #include "lib/zt_common.h" /* * Private data */ static struct util_prg_l { const struct util_prg *prg; /* Command used for parsing */ const char *command; } l; /** * Set the current command for command line option processing * * @param[in] command The current command or NULL for no command */ void util_prg_set_command(const char *command) { l.command = command; } /** * Print program usage information for the --help option */ void util_prg_print_help(void) { /* Print usage */ printf("Usage: %s", program_invocation_short_name); if (l.prg->command_args) printf(" %s", l.prg->command_args); printf(" [OPTIONS]"); if (l.prg->args) printf(" %s", l.prg->args); /* Print usage description */ printf("\n\n"); util_print_indented(l.prg->desc, 0); printf("\n"); } /** * Print program version information for the --version option */ void util_prg_print_version(void) { const struct util_prg_copyright *copyright; printf("%s version %s\n", program_invocation_short_name, RELEASE_STRING); copyright = l.prg->copyright_vec; while (copyright->owner) { if (copyright->pub_first == copyright->pub_last) printf("Copyright %s %d\n", copyright->owner, copyright->pub_first); else printf("Copyright %s %d, %d\n", copyright->owner, copyright->pub_first, copyright->pub_last); copyright++; } } /* * Ask user to use the --help option */ void util_prg_print_parse_error(void) { if (l.command) fprintf(stderr, "Try '%s %s --help' for more information.\n", program_invocation_short_name, l.command); else fprintf(stderr, "Try '%s --help' for more information.\n", program_invocation_short_name); } /** * An option has been specified that is not supported * * @param[in] option Option string (short or long) */ void util_prg_print_invalid_option(const char *opt_name) { fprintf(stderr, "%s: Invalid option '%s'\n", program_invocation_short_name, opt_name); util_prg_print_parse_error(); } /** * A required argument for an option is missing * * @param[in] option Option string */ void util_prg_print_required_arg(const char *opt_name) { fprintf(stderr, "%s: Option '%s' requires an argument\n", program_invocation_short_name, opt_name); util_prg_print_parse_error(); } /** * A superfluous invalid positional argument has been specified * * @param[in] arg_name Name of the invalid argument */ void util_prg_print_arg_error(const char *arg_name) { fprintf(stderr, "%s: Invalid argument '%s'\n", program_invocation_short_name, arg_name); util_prg_print_parse_error(); } /** * Initialize the program module * * @param[in] prg Program description */ void util_prg_init(const struct util_prg *prg) { l.prg = prg; } s390-tools-2.38.0/libutil/util_prg_example.c000066400000000000000000000041131502674226300206160ustar00rootroot00000000000000/** * util_prg_example - Example program for util_prg * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ //! [code] #include #include #include "lib/util_prg.h" /* * Program description */ const struct util_prg prg = { .desc = "Sample for util_prg library.", .args = "[POS_ARGS]", .copyright_vec = { { .owner = "IBM Corp.", .pub_first = 2001, .pub_last = 2017, }, { .owner = "Another Corp.", .pub_first = 2017, .pub_last = 2017, }, UTIL_PRG_COPYRIGHT_END } }; static const struct option opt_list[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'v' }, { NULL, no_argument, NULL, 0 }, }; /* * Demonstrate the util_prg_print() functions */ int main(int argc, char *argv[]) { const char *file_name = "i_do_not_exist"; char optopt_str[3]; FILE *fp; int opt; /* Disable the getopt_long() error messages */ opterr = 0; util_prg_init(&prg); while ((opt = getopt_long(argc, argv, "vhe", opt_list, NULL)) != -1) { switch (opt) { case 'v': util_prg_print_version(); return EXIT_SUCCESS; case 'h': util_prg_print_help(); printf(" -e Try to open non-exisiting file\n"); printf(" -h Print this help, then exit\n"); printf(" -v Print version information, then exit\n"); return EXIT_SUCCESS; case 'e': fp = fopen(file_name, "r"); if (!fp) { err(EXIT_FAILURE, "Open of '%s' failed", file_name); } return EXIT_SUCCESS; case ':': /* Option requires an argument */ util_prg_print_required_arg(argv[optind - 1]); return EXIT_FAILURE; case '?': sprintf(optopt_str, "-%c", optopt); util_prg_print_invalid_option(optopt_str); return EXIT_FAILURE; } } if (argc > 2) { util_prg_print_arg_error(argv[2]); return EXIT_FAILURE; } if (argc == 2) { printf("Positional parameter specified: %s\n", argv[1]); return EXIT_SUCCESS; } errx(EXIT_FAILURE, "Specify either -h, -v, -e, or one positional " "parameter"); } //! [code] s390-tools-2.38.0/libutil/util_proc.c000066400000000000000000000221501502674226300172570ustar00rootroot00000000000000/* * s390-tools/zipl/src/proc.c * Scanner for the /proc/ files * * Copyright IBM Corp. 2001, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. * */ #include #include #include #include #include #include #include #include "lib/util_proc.h" static const char util_proc_part_filename[] = "/proc/partitions"; static const char util_proc_dev_filename[] = "/proc/devices"; struct file_buffer { char *buffer; off_t pos; size_t length; }; #define INITIAL_FILE_BUFFER_SIZE 1024 /* Read file into buffer without querying its size (necessary for reading files * from /proc). Upon success, return zero and set BUFFER to point to * the file buffer and SIZE (if non-null) to contain the file size. Return * non-zero otherwise. Add a null-byte at the end of the buffer if * NIL_TERMINATE is non-zero. */ static int util_proc_read_special_file(const char *filename, char **buffer, size_t *size, int nil_terminate) { FILE *file; char *data; char *new_data; size_t count; size_t current_size; int current; file = fopen(filename, "r"); if (file == NULL) { printf("Could not open %s\n", filename); return -1; } current_size = INITIAL_FILE_BUFFER_SIZE; count = 0; data = (char *) malloc(current_size); if (data == NULL) { printf("Could not allocate %zu bytes of memory", current_size); fclose(file); return -1; } current = fgetc(file); while (current != EOF || nil_terminate) { if (current == EOF) { current = 0; nil_terminate = 0; } data[count++] = (char) current; if (count >= current_size) { new_data = (char *) malloc(current_size * 2); if (new_data == NULL) { printf("Could not allocate %zu bytes of memory", current_size * 2); free(data); fclose(file); return -1; } memcpy(new_data, data, current_size); free(data); data = new_data; current_size *= 2; } current = fgetc(file); } fclose(file); *buffer = data; if (size) *size = count; return 0; } /* Get the contents of a file and fill in the respective fields of * FILE. Return 0 on success, non-zero otherwise. */ static int get_file_buffer(struct file_buffer *file, const char *filename) { int rc; rc = util_proc_read_special_file(filename, &file->buffer, &file->length, 0); file->pos = 0; return rc; } /* Free resources allocated for file buffer identified by * FILE. */ static void free_file_buffer(struct file_buffer *file) { if (file->buffer != NULL) { free(file->buffer); file->buffer = NULL; file->pos = 0; file->length = 0; } } /* Return character at current FILE buffer position or EOF if at end of * file. */ static int current_char(struct file_buffer *file) { if (file->buffer != NULL) if (file->pos < (off_t) file->length) return file->buffer[file->pos]; return EOF; } /* Advance the current file pointer of file buffer FILE until the current * character is no longer a whitespace or until the end of line or file is * reached. Return 0 if at least one whitespace character was encountered, * non-zero otherwise. */ static int skip_whitespaces(struct file_buffer *file) { int rc; rc = -1; while ((current_char(file) != '\n') && isspace(current_char(file))) { rc = 0; file->pos++; } return rc; } /* Scan a positive integer number at the current position of file buffer FILE * and advance the position respectively. Upon success, return zero and set * NUMBER to contain the scanned number. Return non-zero otherwise. */ static int scan_number(struct file_buffer *file, size_t *number) { int rc; size_t old_number; *number = 0; rc = -1; while (isdigit(current_char(file))) { rc = 0; old_number = *number; *number = *number * 10 + current_char(file) - '0'; /* Check for overflow */ if (old_number > *number) { rc = -1; break; } file->pos++; } return rc; } /* Scan a device node name at the current position of file buffer FILE and * advance the position respectively. Upon success, return zero and set * NAME to contain a copy of the scanned name. Return non-zero otherwise. */ static int scan_name(struct file_buffer *file, char **name) { off_t start_pos; start_pos = file->pos; while (!isspace(current_char(file)) && (current_char(file) != EOF)) file->pos++; if (file->pos > start_pos) { *name = (char *) malloc(file->pos - start_pos + 1); if (*name == NULL) return -1; memcpy((void *) *name, (void *) &file->buffer[start_pos], file->pos - start_pos); (*name)[file->pos - start_pos] = 0; return 0; } else return -1; } /* Scan for the specified STRING at the current position of file buffer FILE * and advance the position respectively. Upon success, return zero. Return * non-zero otherwise. */ static int scan_string(struct file_buffer *file, const char *string) { int i; i = 0; for (i = 0; string[i] && (current_char(file) == string[i]); i++, file->pos++) ; if (string[i] == '\0') return 0; return -1; } /* Advance the current file position to beginning of next line in file buffer * FILE or to end of file. */ static void skip_line(struct file_buffer *file) { while ((current_char(file) != '\n') && (current_char(file) != EOF)) file->pos++; if (current_char(file) == '\n') file->pos++; } /* Return non-zero if the current file position of file buffer FILE is at the * end of file. Return zero otherwise. */ static int eof(struct file_buffer *file) { return file->pos >= (off_t) file->length; } /* Scan a line of the specified /proc/partitions FILE buffer and advance the * current file position pointer respectively. If the current line matches * the correct pattern, fill in the corresponding data into ENTRY and return 0. * Return non-zero otherwise. */ static int scan_part_entry(struct file_buffer *file, struct util_proc_part_entry *entry) { int rc; size_t dev_major; size_t dev_minor; size_t blockcount; char *name; /* Scan for: (\s*)(\d+)(\s+)(\d+)(\s+)(\d+)(\s+)(\S+)(\.*)$ */ skip_whitespaces(file); rc = scan_number(file, &dev_major); if (rc) return rc; rc = skip_whitespaces(file); if (rc) return rc; rc = scan_number(file, &dev_minor); if (rc) return rc; rc = skip_whitespaces(file); if (rc) return rc; rc = scan_number(file, &blockcount); if (rc) return rc; rc = skip_whitespaces(file); if (rc) return rc; rc = scan_name(file, &name); if (rc) return rc; skip_line(file); entry->device = makedev(dev_major, dev_minor); entry->blockcount = blockcount; entry->name = name; return 0; } /* Release resources associated with ENTRY. */ void util_proc_part_free_entry(struct util_proc_part_entry *entry) { if (entry->name != NULL) { free(entry->name); entry->name = NULL; } } /* Scan a line of the specified /proc/devices FILE buffer and advance the * current file position pointer respectively. If the current line matches * the correct pattern, fill in the corresponding data into ENTRY and return 0. * Return non-zero otherwise. */ static int scan_dev_entry(struct file_buffer *file, struct util_proc_dev_entry *entry, int blockdev) { int rc; size_t dev_major; char *name; /* Scan for: (\s*)(\d+)(\s+)(\S+)(\.*)$ */ skip_whitespaces(file); rc = scan_number(file, &dev_major); if (rc) return rc; rc = skip_whitespaces(file); if (rc) return rc; rc = scan_name(file, &name); if (rc) return rc; skip_line(file); entry->device = makedev(dev_major, 0); entry->name = name; entry->blockdev = blockdev; return 0; } /* Release resources associated with ENTRY. */ void util_proc_dev_free_entry(struct util_proc_dev_entry *entry) { if (entry->name != NULL) { free(entry->name); entry->name = NULL; } } /* Scan /proc/partitions for an entry matching DEVICE. When there is a match, * store entry data in ENTRY and return 0. Return non-zero otherwise. */ int util_proc_part_get_entry(dev_t device, struct util_proc_part_entry *entry) { struct file_buffer file; int rc; rc = get_file_buffer(&file, util_proc_part_filename); if (rc) return rc; rc = -1; while (!eof(&file)) { if (scan_part_entry(&file, entry) == 0) { if (entry->device == device) { rc = 0; break; } util_proc_part_free_entry(entry); } else skip_line(&file); } free_file_buffer(&file); return rc; } /* Scan /proc/devices for a blockdevice (BLOCKDEV is 1) or a character * device (BLOCKDEV is 0) with a major number matching the major number of DEV. * When there is a match, store entry data in ENTRY and return 0. Return * non-zero otherwise. */ int util_proc_dev_get_entry(dev_t device, int blockdev, struct util_proc_dev_entry *entry) { struct file_buffer file; int rc; int scan_blockdev = 0; rc = get_file_buffer(&file, util_proc_dev_filename); if (rc) return rc; rc = -1; while (!eof(&file)) { if (scan_string(&file, "Block") == 0) { skip_line(&file); scan_blockdev = 1; continue; } else if (scan_dev_entry(&file, entry, scan_blockdev) == 0) { if ((major(entry->device) == major(device)) && blockdev == scan_blockdev) { rc = 0; break; } util_proc_dev_free_entry(entry); } else skip_line(&file); } free_file_buffer(&file); return rc; } s390-tools-2.38.0/libutil/util_rec.c000066400000000000000000000334251502674226300170740ustar00rootroot00000000000000/** * util - Utility function library * * Print records in different output formats * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include "lib/util_base.h" #include "lib/util_libc.h" #include "lib/util_list.h" #include "lib/util_panic.h" #include "lib/util_prg.h" #include "lib/util_rec.h" /* * Field structure containing the string value and secondary information */ struct util_rec_fld { char *key; /* Name of the filed */ char *hdr; /* Content of the header */ size_t len; /* Length of string argz array */ char *val; /* The value of the field */ enum util_rec_align align; /* Alignment of the field */ int width; /* Field width */ struct util_list_node node; /* Pointers to previous and next field */ }; /* * Print formats */ struct rec_fmt { enum { REC_FMT_WIDE, REC_FMT_LONG, REC_FMT_CSV, } type; union { struct wide_p { char *hdr_sep; char *col_sep; int argz_sep; } wide_p; struct long_p { char *hdr_sep; char *col_sep; int argz_sep; char *key; int key_size; int val_size; } long_p; struct csv_p { char *col_sep; int argz_sep; } csv_p; } d; int indent; }; /* * Record structure (internal representation) */ /// @cond struct util_rec { struct util_list *list; /* List of the fields */ struct rec_fmt fmt; /* Output format */ }; /// @endcond struct util_list *__util_rec_get_list(struct util_rec *rec) { return rec->list; } /* * Get the field according to a distinct key */ static struct util_rec_fld *rec_get_fld(struct util_rec *rec, const char *key) { struct util_rec_fld *fld; util_list_iterate(rec->list, fld) { if (!strcmp(fld->key, key)) return fld; } return NULL; } /** * Return the key name of a field * * @param[in] fld Field for query * * @returns Pointer to key string */ const char *util_rec_fld_get_key(struct util_rec_fld *fld) { return fld->key; } /** * Create a new record with "wide" output format * * @param[in] hdr_sep Header separator or %NULL for no separator * * @returns Pointer to the created record */ struct util_rec *util_rec_new_wide(const char *hdr_sep) { struct util_rec *rec = util_malloc(sizeof(struct util_rec)); rec->list = util_list_new(struct util_rec_fld, node); rec->fmt.type = REC_FMT_WIDE; rec->fmt.d.wide_p.hdr_sep = hdr_sep ? util_strdup(hdr_sep) : NULL; rec->fmt.d.wide_p.argz_sep = ','; rec->fmt.indent = 0; return rec; } /* * Print the indentation characters */ static inline void rec_print_indention(int indent) { if (indent <= 0) return; printf("%*s", indent, ""); } /* * Print record separator in "wide" output format */ static void rec_print_wide_separator(struct util_rec *rec) { const char *hdr_sep = rec->fmt.d.wide_p.hdr_sep; int size = 0, field_count = 0; struct util_rec_fld *fld; char *buf; if (!hdr_sep) return; util_list_iterate(rec->list, fld) { if (fld->hdr) { size += fld->width; field_count++; } } size += field_count - 1; buf = util_malloc(size + 1); memset(buf, (int)hdr_sep[0], size); buf[size] = 0; rec_print_indention(rec->fmt.indent); printf("%s\n", buf); free(buf); } /* * Print record header in "wide" output format */ static void rec_print_wide_hdr(struct util_rec *rec) { const char *hdr_sep = rec->fmt.d.wide_p.hdr_sep; int col_nr = 0, size = 0, field_count = 0; struct util_rec_fld *fld; char *buf; rec_print_indention(rec->fmt.indent); util_list_iterate(rec->list, fld) { if (col_nr) printf(" "); if (fld->hdr) { if (fld->align == UTIL_REC_ALIGN_LEFT) printf("%-*s", fld->width, fld->hdr); else printf("%*s", fld->width, fld->hdr); size += fld->width; field_count++; } col_nr++; } printf("\n"); if (!hdr_sep) return; size += field_count - 1; if (hdr_sep) { buf = util_malloc(size + 1); memset(buf, (int)hdr_sep[0], size); buf[size] = 0; rec_print_indention(rec->fmt.indent); printf("%s\n", buf); free(buf); } } /* * Print record field values in "wide" output format */ static void rec_print_wide(struct util_rec *rec) { const char argz_sep = rec->fmt.d.wide_p.argz_sep; struct util_rec_fld *fld; char argz_str[PAGE_SIZE]; int fld_count = 0; char *entry; rec_print_indention(rec->fmt.indent); util_list_iterate(rec->list, fld) { if (!fld->hdr) continue; if (fld_count) printf(" "); entry = fld->val; if (argz_count(fld->val, fld->len) > 1) { strcpy(argz_str, entry); while ((entry = argz_next(fld->val, fld->len, entry))) strcat(strncat(argz_str, &argz_sep, 1), entry); entry = argz_str; } if (fld->align == UTIL_REC_ALIGN_LEFT) printf("%-*s", fld->width, entry); else printf("%*s", fld->width, entry); fld_count++; } printf("\n"); } /* * Free private memory of record */ static void rec_free_wide(struct util_rec *rec) { free(rec->fmt.d.wide_p.hdr_sep); } /** * Create a new record with "long" output format * * @param[in] hdr_sep Header separator or %NULL for no separator * @param[in] col_sep Column separator * @param[in] key Primary key of record * @param[in] key_size Width of left column i.e. keys * @param[in] val_size Width of right column i.e. values * * @returns Pointer to the created record */ struct util_rec *util_rec_new_long(const char *hdr_sep, const char *col_sep, const char *key, int key_size, int val_size) { struct util_rec *rec = util_malloc(sizeof(struct util_rec)); rec->list = util_list_new(struct util_rec_fld, node); rec->fmt.type = REC_FMT_LONG; rec->fmt.d.long_p.hdr_sep = hdr_sep ? util_strdup(hdr_sep) : NULL; rec->fmt.d.long_p.col_sep = util_strdup(col_sep); rec->fmt.d.long_p.key = util_strdup(key); rec->fmt.d.long_p.key_size = key_size; rec->fmt.d.long_p.val_size = val_size; rec->fmt.d.long_p.argz_sep = ' '; rec->fmt.indent = 0; return rec; } /* * Print field header in "long" output format */ static void rec_print_long_hdr(struct util_rec *rec) { struct long_p *p = &rec->fmt.d.long_p; struct util_rec_fld *fld; int len = 0; char *buf; fld = rec_get_fld(rec, p->key); util_assert(fld != NULL, "Record not found\n"); util_assert(fld->hdr != NULL, "Header for field not found\n"); rec_print_indention(rec->fmt.indent); if (p->col_sep) { printf("%-*s %s %-*s\n", p->key_size, fld->hdr, p->col_sep, fld->width, fld->val); len = p->key_size + p->val_size + 3; } else { printf("%-*s %-*s\n", p->key_size, fld->hdr, fld->width, fld->val); len = p->key_size + p->val_size + 1; } if (!p->hdr_sep) return; buf = util_malloc(len + 1); memset(buf, p->hdr_sep[0], len); buf[len] = 0; rec_print_indention(rec->fmt.indent); printf("%s\n", buf); free(buf); } /* * Print record field values in "long" output format */ static void rec_print_long(struct util_rec *rec) { struct long_p *p = &rec->fmt.d.long_p; struct util_rec_fld *fld; char *item = NULL; rec_print_long_hdr(rec); util_list_iterate(rec->list, fld) { if (!fld->hdr) continue; if (!strcmp(p->key, fld->key)) continue; if (!fld->val) continue; rec_print_indention(rec->fmt.indent); item = argz_next(fld->val, fld->len, item); if (p->col_sep) { printf(" %-*s %s %s\n", p->key_size - 8, fld->hdr, p->col_sep, item); while ((item = argz_next(fld->val, fld->len, item))) { rec_print_indention(rec->fmt.indent); printf(" %-*s %c %s\n", p->key_size - 8, "", p->argz_sep, item); } } else { printf(" %-*s %s\n", p->key_size - 8, fld->hdr, fld->val); while ((item = argz_next(fld->val, fld->len, item))) { rec_print_indention(rec->fmt.indent); printf(" %-*s %s\n", p->key_size - 8, "", item); } } } printf("\n"); } /* * Free private memory of record */ static void rec_free_long(struct util_rec *rec) { free(rec->fmt.d.long_p.hdr_sep); free(rec->fmt.d.long_p.col_sep); free(rec->fmt.d.long_p.key); } /** * Create a new record with "csv" output format * * @param[in] col_sep Column separator * * @returns Pointer to the created record */ struct util_rec *util_rec_new_csv(const char *col_sep) { struct util_rec *rec = util_malloc(sizeof(struct util_rec)); rec->list = util_list_new(struct util_rec_fld, node); rec->fmt.type = REC_FMT_CSV; rec->fmt.d.csv_p.col_sep = util_strdup(col_sep); rec->fmt.d.csv_p.argz_sep = ' '; rec->fmt.indent = 0; return rec; } /* * Print record header in "csv" output format */ static void rec_print_csv_hdr(struct util_rec *rec) { const char *col_sep = rec->fmt.d.csv_p.col_sep; struct util_rec_fld *fld; int fld_count = 0; rec_print_indention(rec->fmt.indent); util_list_iterate(rec->list, fld) { if (fld_count) printf("%c", *col_sep); if (fld->hdr) { printf("%s", fld->hdr); fld_count++; } } printf("\n"); } /* * Print record field values in "csv" output format */ static void rec_print_csv(struct util_rec *rec) { const char argz_sep = rec->fmt.d.csv_p.argz_sep; const char *col_sep = rec->fmt.d.csv_p.col_sep; struct util_rec_fld *fld; int fld_count = 0; char *item = NULL; rec_print_indention(rec->fmt.indent); util_list_iterate(rec->list, fld) { item = argz_next(fld->val, fld->len, item); if (fld_count) printf("%c", *col_sep); if (fld->hdr) { printf("%s", item); while ((item = argz_next(fld->val, fld->len, item))) printf("%c%s", argz_sep, item); fld_count++; } } printf("\n"); } /* * Free private memory of record */ static void rec_free_csv(struct util_rec *rec) { free(rec->fmt.d.csv_p.col_sep); } /** * Define a new field for the record * * @param[in] rec Pointer of the record * @param[in] key Key of the filed is to be created. It should be unique * within the record. * @param[in] align Alignment of field * @param[in] width Width of field * @param[in] hdr This information is printed in record headers. If it is * NULL, the field is prohibited form printing completely. */ void util_rec_def(struct util_rec *rec, const char *key, enum util_rec_align align, int width, const char *hdr) { struct util_rec_fld *fld = util_malloc(sizeof(struct util_rec_fld)); fld->key = util_strdup(key); fld->hdr = util_strdup(hdr); fld->val = NULL; fld->align = align; fld->width = width; util_list_add_tail(rec->list, fld); } /** * Free record and associated fields * * @param[in] rec Record pointer */ void util_rec_free(struct util_rec *rec) { struct util_rec_fld *fld, *tmp; util_list_iterate_safe(rec->list, fld, tmp) { util_list_remove(rec->list, fld); free(fld->key); free(fld->hdr); free(fld->val); free(fld); } util_list_free(rec->list); switch (rec->fmt.type) { case REC_FMT_WIDE: rec_free_wide(rec); break; case REC_FMT_LONG: rec_free_long(rec); break; case REC_FMT_CSV: rec_free_csv(rec); break; } free(rec); } /** * Print record field values according to output format * * @param[in] rec Record pointer */ void util_rec_print(struct util_rec *rec) { switch (rec->fmt.type) { case REC_FMT_WIDE: rec_print_wide(rec); break; case REC_FMT_LONG: rec_print_long(rec); break; case REC_FMT_CSV: rec_print_csv(rec); break; } } /** * Print record header according to output format * * @param[in] rec Record pointer */ void util_rec_print_hdr(struct util_rec *rec) { switch (rec->fmt.type) { case REC_FMT_WIDE: rec_print_wide_hdr(rec); break; case REC_FMT_LONG: break; case REC_FMT_CSV: rec_print_csv_hdr(rec); break; } } /** * Print record separator according to output format * * @param[in] rec Record pointer */ void util_rec_print_separator(struct util_rec *rec) { switch (rec->fmt.type) { case REC_FMT_WIDE: rec_print_wide_separator(rec); break; case REC_FMT_LONG: break; case REC_FMT_CSV: break; } } /** * Set a field value to an argz vector * * @param[in] rec Record pointer * @param[in] key Key of the desired field * @param[in] argz Pointer to the series of strings * @param[in] len Length of the argz buffer */ void util_rec_set_argz(struct util_rec *rec, const char *key, const char *argz, size_t len) { struct util_rec_fld *fld; char *val; fld = rec_get_fld(rec, key); if (!fld) return; val = util_malloc(len); val = memcpy(val, argz, len); free(fld->val); fld->val = val; fld->len = len; } /** * Set a field value to a formatted string * * @param[in] rec Record pointer * @param[in] key Key of the desired field * @param[in] fmt Format string for generation of value string * @param[in] ... Parameters for format string * * @returns Pointer to the field which was modified or NULL in the case of * any error. */ void util_rec_set(struct util_rec *rec, const char *key, const char *fmt, ...) { struct util_rec_fld *fld; va_list ap; char *str; util_assert(fmt != NULL, "Parameter 'fmt' pointer must not be NULL\n"); fld = rec_get_fld(rec, key); if (!fld) return; UTIL_VASPRINTF(&str, fmt, ap); free(fld->val); fld->val = str; fld->len = strlen(str) + 1; } /** * Return the string value of a desired field. If the field value stored in argz * format, pointer to the first argz element is returned. * * @param[in] rec Record pointer * @param[in] key Key of the field * * @returns If the desired field was found, the pointer to its value. * NULL in the case of any error or if the field is empty. */ const char *util_rec_get(struct util_rec *rec, const char *key) { struct util_rec_fld *fld = rec_get_fld(rec, key); return (fld != NULL) ? fld->val : NULL; } /** * Sets the indentation of the record * * @param[in] rec Record pointer * @param[in] indent Number of characters to indent */ void util_rec_set_indent(struct util_rec *rec, int indent) { rec->fmt.indent = indent; } s390-tools-2.38.0/libutil/util_rec_example.c000066400000000000000000000043471502674226300206100ustar00rootroot00000000000000/** * util_rec_example - Example program for util_rec * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ //! [code] #include #include #include #include "lib/util_rec.h" /* * Print three records in specified format */ static void print_records(const char *format, struct util_rec *rec) { static const char * const size_vec[] = {"small", "medium", "large"}; static const char * const name_vec[] = {"zero", "one", "two"}; int i; printf("###########################################################\n"); printf("# %s\n\n", format); /* Define fields of record */ util_rec_def(rec, "number", UTIL_REC_ALIGN_LEFT, 6, "Number"); util_rec_def(rec, "name", UTIL_REC_ALIGN_LEFT, 10, "Name"); util_rec_def(rec, "size", UTIL_REC_ALIGN_RIGHT, 15, "Size"); /* Print record header (is a nop for long format) */ util_rec_print_hdr(rec); for (i = 0; i < 3; i++) { /* Fill fields of record with values */ util_rec_set(rec, "number", "%d", i); util_rec_set(rec, "name", name_vec[i]); util_rec_set(rec, "size", size_vec[i]); /* Print the record */ util_rec_print(rec); } /* Print a separator line (is a nop for long and csv format) */ util_rec_print_separator(rec); printf("\n"); } /* * Print keys for record fields */ static void print_fields(struct util_rec *rec) { struct util_rec_fld *fld; int i = 1; printf("###########################################################\n"); printf("# Keys of record fields\n"); util_rec_iterate(rec, fld) { printf("Field %d : %s\n", i++, util_rec_fld_get_key(fld)); } } /* * Print records in "wide", "long", and "csv" format */ int main(void) { struct util_rec *rec; rec = util_rec_new_wide("-"); print_records("Wide format", rec); util_rec_free(rec); rec = util_rec_new_wide("-"); util_rec_set_indent(rec, 4); print_records("Wide format with indentation", rec); util_rec_free(rec); rec = util_rec_new_long("-", ":", "number", 30, 20); print_records("Long format", rec); util_rec_free(rec); rec = util_rec_new_csv(","); print_records("CSV format", rec); print_fields(rec); util_rec_free(rec); return EXIT_SUCCESS; } //! [code] s390-tools-2.38.0/libutil/util_scandir.c000066400000000000000000000110261502674226300177370ustar00rootroot00000000000000/* * util - Utility function library * * Scan a directory for matching entries * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include "lib/util_base.h" #include "lib/util_libc.h" #include "lib/util_panic.h" #include "lib/util_scandir.h" #include "lib/zt_common.h" /// @cond struct util_scandir_filter { regex_t reg_buf; }; /// @endcond /* * Check directory entry */ static int filter_regexp(const struct dirent *de, void *data) { struct util_scandir_filter *filter = data; regmatch_t pmatch[1]; if (regexec(&filter->reg_buf, de->d_name, (size_t) 1, pmatch, 0) == 0) return 1; return 0; } typedef int (*__compar_fn_t) (const void *, const void *); /* * Return sorted "struct dirent" array for entries that match "filter_fn" */ static int __scandir(struct dirent ***de_vec, const char *path, int (*filter_fn)(const struct dirent *, void *), void *filter_data, int (*compar_fn)(const struct dirent **, const struct dirent **)) { struct dirent *de, *de_new, **de_vec_new = NULL; int count = 0; DIR *dirp; *de_vec = NULL; dirp = opendir(path); if (!dirp) return -1; while ((de = readdir(dirp))) { if (filter_fn(de, filter_data) == 0) continue; de_new = util_malloc(sizeof(*de_new)); *de_new = *de; de_vec_new = realloc(de_vec_new, sizeof(void *) * (count + 1)); de_vec_new[count++] = de_new; } closedir(dirp); if (compar_fn) qsort(de_vec_new, count, sizeof(void *), (__compar_fn_t) compar_fn); *de_vec = de_vec_new; return count; } /* * Return sorted "struct dirent" array for entries that match "pattern" */ static int scandir_regexp(struct dirent ***de_vec, const char *path, const char *pattern, int compar_fn(const struct dirent **, const struct dirent **)) { struct util_scandir_filter filter; char err_buf[256]; int count, rc; rc = regcomp(&filter.reg_buf, pattern, REG_EXTENDED); if (rc) { regerror(rc, &filter.reg_buf, err_buf, sizeof(err_buf)); util_panic("Function regcomp(%s) failed: %s\n", pattern, err_buf); } count = __scandir(de_vec, path, filter_regexp, &filter, compar_fn); regfree(&filter.reg_buf); return count; } /** * Compare two hexadecimal string dirents numerically * * @param[in] de1 First directory entry * @param[in] de2 Second directory entry * * @retval -1 de1 < de2 * @retval 0 de1 = de2 * @retval 1 de1 > de2 */ int util_scandir_hexsort(const struct dirent **de1, const struct dirent **de2) { unsigned long val1 = strtoul((*de1)->d_name, NULL, 16); unsigned long val2 = strtoul((*de2)->d_name, NULL, 16); if (val1 < val2) return -1; if (val1 == val2) return 0; return 1; } /** * Construct a list of direcotry entries using POSIX regular expressions * * A desired directory in sysfs is scanned for entries of a given name * pattern. The name pattern is constructed with sprintf() using a format * string and variable argument list. After constructing the pattern it * is used with regcomp() and regexec() to find matching entries. * The returned list of matches consist of an array of pointers to * directory entries. The entries as well as the pointer array itself are * allocated by the function and has to be released by the user via free. * * @param[out] de_vec Vector of matched directory entries, or NULL * @param[in] compar_fn Callback function for sorting the entry list * @param[in] path Path to the directory to scan * @param[in] fmt Format string, describes the search pattern as POSIX regex * @param[in] ... Values for format string * * @returns Number of returned directory entries, or -1 on error */ int util_scandir(struct dirent ***de_vec, int compar_fn(const struct dirent **first, const struct dirent **second), const char *path, const char *fmt, ...) { char *pattern; va_list ap; int rc; va_start(ap, fmt); rc = vasprintf(&pattern, fmt, ap); va_end(ap); if (rc < 0) return -1; rc = scandir_regexp(de_vec, path, pattern, compar_fn); free(pattern); return rc; } /** * Free list of directory entries * * @param[in] de_vec Vector of directory entries * @param[in] count Count of directory entries */ void util_scandir_free(struct dirent **de_vec, int count) { util_ptr_vec_free((void **) de_vec, count); } s390-tools-2.38.0/libutil/util_scandir_example.c000066400000000000000000000026421502674226300214560ustar00rootroot00000000000000/** * util_dir_example - Example program for util_dir * * Copyright IBM Corp. 2016, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ //! [code] #include #include #include "lib/util_scandir.h" /* * Print real sysfs cpu directory contents */ static void show_cpu_dir(const char *sys_cpu_path) { char cmd_ls[256]; sprintf(cmd_ls, "ls -l %s", sys_cpu_path); printf("$ %s\n", cmd_ls); fflush(stdout); if (system(cmd_ls)) { perror("system() failed"); exit(EXIT_FAILURE); } printf("\n"); } /* * Show all CPUs on Linux system */ int main(void) { struct dirent **de_vec; int count, i; const char *path = "/sys/devices/system/cpu"; const char *prefix = "cpu"; show_cpu_dir(path); /* * Process all files that match regular expression "cpu[0-9]+" * and sort them alphabetically. Note that the regular expression * is constructed with a variable argument list. */ count = util_scandir(&de_vec, alphasort, path, "%s[0-9]+", prefix); if (count == -1) { perror("util_dir_scan failed"); return EXIT_FAILURE; } /* Print all directories */ printf("Found cpus:\n\n"); for (i = 0; i < count; i++) { if (de_vec[i]->d_type != DT_DIR) continue; printf(" - %s\n", de_vec[i]->d_name); } /* Free directory entries */ util_scandir_free(de_vec, count); return EXIT_SUCCESS; } //! [code] s390-tools-2.38.0/libutil/util_sys.c000066400000000000000000000076601502674226300171430ustar00rootroot00000000000000/* * util - Utility function library * * SysFS helper functions * * Copyright IBM Corp. 2019 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include "lib/util_file.h" #include "lib/util_libc.h" #include "lib/util_path.h" #include "lib/util_sys.h" /* lstat() doesn't work for sysfs files, a fixed size is therefore inevitable */ #define READLINK_SIZE 256 #define PAGE_SIZE 4096 /** * Return the partition number of a given partition. * * @param[in] dev Device node of interest * * @retval int Partition number of the device * @retval -1 Error when trying to read the partition number. */ int util_sys_get_partnum(dev_t dev) { int partnum = -1; char *path; path = util_path_sysfs("dev/block/%u:%u/partition", major(dev), minor(dev)); if (util_file_read_i(&partnum, 10, path)) { warnx("Could not read from path '%s'", path); goto out; } if (partnum <= 0) { warnx("Bad partition number in '%s'", path); partnum = -1; goto out; } out: free(path); return partnum; } /** * Determine if the given device is a partition. * * @param[in] dev Device node of interest * * @retval true Device is partition * @retval false Device is not a partition */ bool util_sys_dev_is_partition(dev_t dev) { bool is_part; char *path; path = util_path_sysfs("dev/block/%u:%u/partition", major(dev), minor(dev)); is_part = util_path_exists(path); free(path); return is_part; } /** * Determine base device * * This function determines the base device \p base_dev of a given * device \p dev. If \p dev is a base device, \p base_dev becomes \p dev. * * @param[in] dev Device node of interest * @param[out] base_dev Identified base device * * @retval 0 Success * @retval -1 Error while reading device information or * constructed path */ int util_sys_get_base_dev(dev_t dev, dev_t *base_dev) { int base_major, base_minor; char buf[PAGE_SIZE]; char *path; /* check if the device already is a base device */ if (!util_sys_dev_is_partition(dev)) { *base_dev = makedev(major(dev), minor(dev)); return 0; } path = util_path_sysfs("dev/block/%d:%d/../dev", major(dev), minor(dev)); if (util_file_read_line(buf, sizeof(buf), path)) { warnx("Could not read from path '%s'", path); free(path); return -1; } free(path); if (sscanf(buf, "%i:%i", &base_major, &base_minor) != 2) { warn("Could not parse major:minor from string '%s'", buf); return -1; } *base_dev = makedev(base_major, base_minor); return 0; } /** * Identify device address * * Identifying the device address with this function works for almost any * character and block device (e.g. NVMe, SCSI, DASD, etc). * The user must provide a buffer that is large enough for the desired device * address to be read into \p addr. * * @param[in] dev Device node of interest * @param[out] addr Identified device address * * @retval 0 Success * @retval -1 Error while reading device information or * constructed path */ int util_sys_get_dev_addr(const char *dev, char *addr) { char device[READLINK_SIZE], *result; unsigned int maj, min; struct stat s; ssize_t len; dev_t base; char *path; if (stat(dev, &s) != 0) return -1; if (util_sys_get_base_dev(s.st_rdev, &base)) return -1; maj = major(base); min = minor(base); if (S_ISBLK(s.st_mode)) path = util_path_sysfs("dev/block/%u:%u/device", maj, min); else if (S_ISCHR(s.st_mode)) path = util_path_sysfs("dev/char/%u:%u/device", maj, min); else return -1; len = readlink(path, device, READLINK_SIZE - 1); free(path); if (len != -1) device[len] = '\0'; else return -1; result = strrchr(device, '/'); if (result) result++; else result = device; strcpy(addr, result); return 0; } s390-tools-2.38.0/libutil/util_udev.c000066400000000000000000000163541502674226300172700ustar00rootroot00000000000000/* * util - Utility function library * * UDEV helper functions * * Copyright IBM Corp. 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include "lib/util_exit_code.h" #include "lib/util_file.h" #include "lib/util_libc.h" #include "lib/util_list.h" #include "lib/util_path.h" #include "lib/util_udev.h" /* Create a newly allocated udev entry. */ static struct util_udev_entry_node *util_udev_entry_node_new(const char *key, const char *op, const char *value) { struct util_udev_entry_node *entry; entry = util_zalloc(sizeof(struct util_udev_entry_node)); entry->key = util_strdup(key); entry->op = util_strdup(op); entry->value = util_strdup(value); return entry; } /* Release resources associated with udev entry. */ static void util_udev_entry_node_free(struct util_udev_entry_node *entry) { if (!entry) return; free(entry->key); free(entry->op); free(entry->value); free(entry); } /* Create a newly allocated udev line. */ static struct util_udev_line_node *util_udev_line_node_new(void) { struct util_udev_line_node *line; line = util_zalloc(sizeof(struct util_udev_line_node)); util_list_init(&line->entries, struct util_udev_entry_node, node); return line; } /* Release resources associated with udev line. */ static void util_udev_line_node_free(struct util_udev_line_node *line) { struct util_udev_entry_node *e, *n; if (!line) return; util_list_iterate_safe(&line->entries, e, n) { util_list_remove(&line->entries, e); util_udev_entry_node_free(e); } free(line->line); free(line); } /* Create a newly allocated udev file. */ static struct util_udev_file *util_udev_file_new(void) { struct util_udev_file *file; file = util_zalloc(sizeof(struct util_udev_file)); util_list_init(&file->lines, struct util_udev_line_node, node); return file; } /** * Release resources associated with udev file. * * @param[in, out] file Udev file structure to be freed */ void util_udev_free_file(struct util_udev_file *file) { struct util_udev_line_node *l, *n; if (!file) return; util_list_iterate_safe(&file->lines, l, n) { util_list_remove(&file->lines, l); util_udev_line_node_free(l); } free(file); } /** * Print the contents of a udev file to stdout. Used for debugging. * * @param[in] file Udev file structure to print */ void util_udev_file_print(struct util_udev_file *file) { struct util_udev_line_node *l; struct util_udev_entry_node *e; printf("util_udev_file at %p\n", (void *) file); if (!file) return; util_list_iterate(&file->lines, l) { printf(" util_udev_line_node at %p\n", (void *) l); printf(" line='%s'\n", l->line); util_list_iterate(&l->entries, e) { printf(" util_udev_entry at %p\n", (void *) e); printf(" '%s' '%s' '%s'\n", e->key, e->op, e->value); } } } static void skip_whitespace(const char **s_ptr) { const char *s = *s_ptr; while (*s && isspace(*s)) s++; *s_ptr = s; } static char *parse_key(const char **s_ptr) { const char *s, *e; char *key; s = *s_ptr; /* Parse \w+(\{[^\}]*\})? */ e = s; while (*e && (isalnum(*e) || *e == '_')) e++; if (*e == '{') { while (*e && *e != '}') e++; if (*e == '}') e++; } if (e == s) return NULL; /* s points to key start, e to character after key end. */ key = util_zalloc(e - s + 1); memcpy(key, s, e - s); *s_ptr = e; return key; } static char *parse_op(const char **s_ptr) { const char *ops[] = { "==", "!=", "=", "+=", ":=", NULL }; const char *entry; size_t len; int i; entry = *s_ptr; for (i = 0; ops[i]; i++) { len = strlen(ops[i]); if (strncmp(entry, ops[i], len) == 0) { *s_ptr += len; return util_strdup(ops[i]); } } return NULL; } static char *parse_value(const char **s_ptr) { const char *s, *e; char *value; /* Parse: ^\s*(.*)\s*$ */ s = *s_ptr; skip_whitespace(&s); e = s; while (*e) e++; e--; while (e > s && isspace(*e)) e--; e++; *s_ptr = e; /* Remove quotes. */ if ((*s == '"' && *(e - 1) == '"') || (*s == '\'' && *(e - 1) == '\'')) { s++; e--; } /* s points to value start, e to character after value end. */ value = util_zalloc(e - s + 1); memcpy(value, s, e - s); return value; } static bool parse_util_udev_entry(struct util_udev_line_node *line, const char *entry) { char *key = NULL, *op = NULL, *value = NULL; struct util_udev_entry_node *e; bool rc = false; /* Parse: ^\s*(\w+)\s*(==|!=|=|\+=|:=)\s*"?([^"]*)"\s*$ */ /* Parse key. */ skip_whitespace(&entry); key = parse_key(&entry); if (!key) goto out; /* Parse operator. */ skip_whitespace(&entry); op = parse_op(&entry); if (!op) goto out; /* Parse value. */ skip_whitespace(&entry); value = parse_value(&entry); if (!value) goto out; skip_whitespace(&entry); /* Check for unrecognized characters at end of entry. */ if (*entry != 0) goto out; /* Add entry to list. */ e = util_udev_entry_node_new(key, op, value); util_list_add_tail(&line->entries, e); rc = true; out: free(key); free(op); free(value); return rc; } static void replace_unquoted(char *s, char from, char to) { char quoted = 0; for (; *s; s++) { if (quoted) { /* Skip until quote end is found. */ if (*s == quoted) quoted = 0; continue; } if (*s == '"' || *s == '\'') { quoted = *s; continue; } if (*s == from) *s = to; } } static bool parse_util_udev_line(struct util_udev_file *file, const char *line) { char *copy, *curr, *next; struct util_udev_line_node *l; int i; bool result = true; l = util_udev_line_node_new(); l->line = util_strdup(line); /* Check for empty lines and comment lines. */ for (i = 0; line[i] && isspace(line[i]); i++); if (line[i] == 0 || line[i] == '#') goto ok; /* Parse each comma-separated entry. */ copy = util_strdup(line); /* A hack to differentiate between quoted and unquoted commas. */ replace_unquoted(copy, ',', 1); next = copy; while ((curr = strsep(&next, "\1"))) { if (!parse_util_udev_entry(l, curr)) { result = false; break; } } free(copy); ok: if (result) util_list_add_tail(&file->lines, l); else util_udev_line_node_free(l); return result; } /** * Create a new util_udev_file structure and read the contents of a specified * udev file into that structure. * * @param[in] path Path to the udev file that will be read in * @param[in, out] file_ptr A buffer to store resulting udev file structure * * @retval 0 Udev file read successfully * @retval UTIL_EXIT_RUNTIME_ERROR Error reading the udev file */ util_exit_code_t util_udev_read_file(const char *path, struct util_udev_file **file_ptr) { char *text, *curr, *next; struct util_udev_file *file; int once = 0; text = util_file_read_text_file(path, 0); if (!text) return UTIL_EXIT_RUNTIME_ERROR; file = util_udev_file_new(); /* Iterate over each line. */ next = text; while ((curr = strsep(&next, "\n"))) { if (parse_util_udev_line(file, curr)) continue; if (!once) { once = 1; fprintf(stderr, "Unrecognized udev rule in %s:\n", path); } fprintf(stderr, "%s\n", curr); } free(text); *file_ptr = file; return UTIL_EXIT_OK; } s390-tools-2.38.0/libvmcp/000077500000000000000000000000001502674226300151035ustar00rootroot00000000000000s390-tools-2.38.0/libvmcp/Makefile000066400000000000000000000004061502674226300165430ustar00rootroot00000000000000include ../common.mak lib = libvmcp.a examples = vmcp_example all: $(lib) examples: $(lib) $(examples) objects = vmcp.o $(lib): $(objects) install: all vmcp_example: vmcp_example.o $(lib) $(rootdir)/libutil/libutil.a clean: rm -f *.o $(lib) $(examples) s390-tools-2.38.0/libvmcp/vmcp.c000066400000000000000000000067361502674226300162300ustar00rootroot00000000000000/* * vmcp - z/VM CP function library * * Copyright IBM Corp. 2018 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include "lib/util_libc.h" #include "lib/vmcp.h" #define VMCP_DEVICE_NODE "/dev/vmcp" #define VMCP_GETSIZE _IOR(0x10, 3, int) #define VMCP_SETBUF _IOW(0x10, 2, int) #define VMCP_GETCODE _IOR(0x10, 1, int) /* * Read at most COUNT bytes from FD into memory at location BUF. * Return number of bytes read on success, -1 on error. */ static ssize_t read_buffer(int fd, char *buf, ssize_t count) { ssize_t ret, done; for (done = 0; done < (ssize_t)count; done += ret) { ret = read(fd, &buf[done], count - done); if (ret == -1 && errno == EINTR) continue; if (ret == -1) return -1; if (ret == 0) break; } return done; } /** * Submit a command to z/VM CP and return the response, the size in bytes * of the response and CP command response code. * * The member response contains the response from CP. It is a pointer * to a malloc'ed buffer which must be freed by the caller when the return * code is zero or VMCP_ERR_TOOSMALL. * * @param[in,out] cmd Pointer to struct vmcp_parm. * @param[in] cpcmd Pointer to CP command string. * @param[in] do_upper If true convert cpcmd string to upper case. * @param[out] cprc Return code of the CP command. * @param[out] response Return buffer, has been allocated via malloc() * must be freed by caller, see below. * @param[out] response_size Size of the response in bytes, can be larger * than buffer_size. * * @returns Zero on success or a negative error number on failure. * @retval VMCP_SUCCESS Success valid members: cprc, response, * response_size * @retval VMCP_ERR_OPEN Error opening VMCP device, valid members: none * @retval VMCP_ERR_SETBUF Error setting buffer, valid members: none * @retval VMCP_ERR_GETCODE Error getting cp exit code, valid members: none * @retval VMCP_ERR_WRITE Error write CP command, valid members: none * @retval VMCP_ERR_GETSIZE Error reading response size, * valid members: cprc * @retval VMCP_ERR_READ Error reading resp * @retval VMCP_ERR_TOOSMALL Error response buffer too small, response * truncated, valid members: cprc, response, * response_size */ int vmcp(struct vmcp_parm *cmd) { int rc = VMCP_SUCCESS, fd, len; char *cpcmd; cmd->response = NULL; cmd->response_size = 0; cmd->cprc = 0; fd = open(VMCP_DEVICE_NODE, O_RDWR); if (fd == -1) return VMCP_ERR_OPEN; cpcmd = util_strdup(cmd->cpcmd); /* Exits on failure */ if (cmd->do_upper) util_str_toupper(cpcmd); if (ioctl(fd, VMCP_SETBUF, &cmd->buffer_size) == -1) { rc = VMCP_ERR_SETBUF; goto fail; } if (write(fd, cpcmd, strlen(cpcmd)) == -1) { rc = VMCP_ERR_WRITE; goto fail; } if (ioctl(fd, VMCP_GETCODE, &cmd->cprc) == -1) { rc = VMCP_ERR_GETCODE; goto fail; } if (ioctl(fd, VMCP_GETSIZE, &cmd->response_size) == -1) { rc = VMCP_ERR_GETSIZE; goto fail; } if (cmd->response_size <= cmd->buffer_size) { len = cmd->response_size; } else { len = cmd->buffer_size; rc = VMCP_ERR_TOOSMALL; } /* Exits on failure */ cmd->response = (char *)util_zalloc(len + 1); if (read_buffer(fd, cmd->response, len) == -1) { rc = VMCP_ERR_READ; free(cmd->response); cmd->response = NULL; } fail: close(fd); free(cpcmd); return rc; } s390-tools-2.38.0/libvmcp/vmcp_example.c000066400000000000000000000025501502674226300177310ustar00rootroot00000000000000/** * vmcp_example - Example program for vmcp.c * * Copyright 2018 IBM Corp. * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ //! [code] #include #include #include #include "lib/vmcp.h" /* * Demonstrate vmcp() function usage */ int main(void) { struct vmcp_parm cp; int rc; cp.cpcmd = "q osa"; cp.buffer_size = VMCP_DEFAULT_BUFSZ; cp.do_upper = true; rc = vmcp(&cp); switch (rc) { case VMCP_SUCCESS: printf("%s\n", cp.response); free(cp.response); break; case VMCP_ERR_OPEN: errx(EXIT_FAILURE, "Could not open device %s", VMCP_DEVICE_NODE); break; case VMCP_ERR_SETBUF: errx(EXIT_FAILURE, "Could not set buffer size"); break; case VMCP_ERR_GETCODE: errx(EXIT_FAILURE, "Could not query return code"); break; case VMCP_ERR_GETSIZE: errx(EXIT_FAILURE, "Could not query response size"); break; case VMCP_ERR_WRITE: errx(EXIT_FAILURE, "Could not issue CP command"); break; case VMCP_ERR_READ: errx(EXIT_FAILURE, "Could not read CP response"); break; case VMCP_ERR_TOOSMALL: free(cp.response); printf("Response truncated (requires %u bytes)\n", cp.response_size); errx(EXIT_FAILURE, "Response buffer (%u bytes) too small", cp.buffer_size); break; } return EXIT_SUCCESS; } //! [code] s390-tools-2.38.0/libvtoc/000077500000000000000000000000001502674226300151115ustar00rootroot00000000000000s390-tools-2.38.0/libvtoc/Makefile000066400000000000000000000002021502674226300165430ustar00rootroot00000000000000include ../common.mak lib = libvtoc.a all: $(lib) objects = vtoc.o $(lib): $(objects) install: all clean: rm -f *.o $(lib) s390-tools-2.38.0/libvtoc/vtoc.c000066400000000000000000000761611502674226300162430ustar00rootroot00000000000000/* * Copyright IBM Corp. 2001, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. * */ #include "lib/vtoc.h" static unsigned char EBCtoASC[256] = { /* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */ 0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F, /* 0x08 -GE -SPS -RPT VT FF CR SO SI */ 0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, /* 0x10 DLE DC1 DC2 DC3 -RES -NL BS -POC -ENP ->LF */ 0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07, /* 0x18 CAN EM -UBS -CU1 -IFS -IGS -IRS -ITB -IUS */ 0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, /* 0x20 -DS -SOS FS -WUS -BYP LF ETB ESC -INP */ 0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B, /* 0x28 -SA -SFE -SM -CSP -MFA ENQ ACK BEL -SW */ 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07, /* 0x30 ---- ---- SYN -IR -PP -TRN -NBS EOT */ 0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04, /* 0x38 -SBS -IT -RFF -CU3 DC4 NAK ---- SUB */ 0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A, /* 0x40 SP RSP ---- */ 0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86, /* 0x48 . < ( + | */ 0x87, 0xA4, 0x9B, 0x2E, 0x3C, 0x28, 0x2B, 0x7C, /* 0x50 & ---- */ 0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07, /* 0x58 ! $ * ) ; */ 0x8D, 0xE1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAA, /* 0x60 - / ---- ---- ---- ---- */ 0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F, /* 0x68 ---- , % _ > ? */ 0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, /* 0x70 --- ---- ---- ---- ---- ---- ---- */ 0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, /* 0x78 * ` : # @ ' = " */ 0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, /* 0x80 * a b c d e f g */ 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x88 h i ---- ---- ---- */ 0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1, /* 0x90 j k l m n o p */ 0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, /* 0x98 q r ---- ---- */ 0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07, /* 0xA0 ~ s t u v w x */ 0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, /* 0xA8 y z ---- ---- ---- ---- */ 0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07, /* 0xB0 ^ ---- ---- */ 0x5E, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC, /* 0xB8 ---- [ ] ---- ---- ---- ---- */ 0xAB, 0x07, 0x5B, 0x5D, 0x07, 0x07, 0x07, 0x07, /* 0xC0 { A B C D E F G */ 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0xC8 H I ---- ---- */ 0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07, /* 0xD0 } J K L M N O P */ 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, /* 0xD8 Q R ---- */ 0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98, /* 0xE0 \ S T U V W X */ 0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, /* 0xE8 Y Z ---- ---- ---- ---- */ 0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07, /* 0xF0 0 1 2 3 4 5 6 7 */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0xF8 8 9 ---- ---- ---- ---- ---- */ 0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07 }; static unsigned char ASCtoEBC[256] = { /*00 NL SH SX EX ET NQ AK BL */ 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, /*08 BS HT LF VT FF CR SO SI */ 0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, /*10 DL D1 D2 D3 D4 NK SN EB */ 0x10, 0x11, 0x12, 0x13, 0x3C, 0x15, 0x32, 0x26, /*18 CN EM SB EC FS GS RS US */ 0x18, 0x19, 0x3F, 0x27, 0x1C, 0x1D, 0x1E, 0x1F, /*20 SP ! " # $ % & ' */ 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, /*28 ( ) * + , - . / */ 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61, /*30 0 1 2 3 4 5 6 7 */ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, /*38 8 9 : ; < = > ? */ 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F, /*40 @ A B C D E F G */ 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, /*48 H I J K L M N O */ 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, /*50 P Q R S T U V W */ 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, /*58 X Y Z [ \ ] ^ _ */ 0xE7, 0xE8, 0xE9, 0xAD, 0xE0, 0xBD, 0x5F, 0x6D, /*60 ` a b c d e f g */ 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /*68 h i j k l m n o */ 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, /*70 p q r s t u v w */ 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, /*78 x y z { | } ~ DL */ 0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0xFF }; enum failure {unable_to_open, unable_to_seek, unable_to_write, unable_to_read}; static char buffer[85]; /* * */ static void vtoc_error(enum failure why, char *s1, char *s2) { switch (why) { case unable_to_open: fprintf(stderr, "\n%s opening device '%s' failed.\n%s\n", VTOC_ERROR, s1, s2); break; case unable_to_seek: fprintf(stderr, "\n%s seeking device '%s' failed.\n%s\n", VTOC_ERROR, s1, s2); break; case unable_to_write: fprintf(stderr, "\n%s writing to device '%s' failed,\n%s\n", VTOC_ERROR, s1, s2); break; case unable_to_read: fprintf(stderr, "\n%s reading from device '%s' failed.\n%s\n", VTOC_ERROR, s1, s2); break; default: fprintf(stderr, "\nFatal error\n"); } exit(1); } /* * */ char * vtoc_ebcdic_enc (char *source, char *target, int l) { int i; for (i = 0; i < l; i++) target[i]=ASCtoEBC[(unsigned char)(source[i])]; return target; } /* * */ char * vtoc_ebcdic_dec (char *source, char *target, int l) { int i; for (i = 0; i < l; i++) target[i]=EBCtoASC[(unsigned char)(source[i])]; return target; } /* * */ void vtoc_set_extent (extent_t *ext, u_int8_t typeind, u_int8_t seqno, cchh_t *lower, cchh_t *upper) { ext->typeind = typeind; ext->seqno = seqno; memcpy(&ext->llimit,lower,sizeof(cchh_t)); memcpy(&ext->ulimit,upper,sizeof(cchh_t)); } /* * */ void vtoc_set_cchh (cchh_t *addr, u_int32_t cc, u_int16_t hh) { addr->cc = (u_int16_t) cc; addr->hh = cc >> 16; addr->hh <<= 4; addr->hh |= hh; } u_int32_t vtoc_get_cyl_from_cchh(cchh_t *addr) { u_int32_t cyl; /*decode cylinder for large volumes */ cyl = addr->hh & 0xFFF0; cyl <<= 12; cyl |= addr->cc; return cyl; } u_int16_t vtoc_get_head_from_cchh(cchh_t *addr) { /* decode heads for large volumes */ return addr->hh & 0x000F; } /* * */ static void vtoc_set_ttr (ttr_t *addr, u_int16_t tt, u_int8_t r) { addr->tt = tt; addr->r = r; } /* * */ void vtoc_set_cchhb (cchhb_t *addr, u_int32_t cc, u_int16_t hh, u_int8_t b) { addr->cc = (u_int16_t) cc; addr->hh = cc >> 16; addr->hh <<= 4; addr->hh |= hh; addr->b = b; } u_int32_t vtoc_get_cyl_from_cchhb(cchhb_t *addr) { u_int32_t cyl; /* decode cylinder for large volumes */ cyl = addr->hh & 0xFFF0; cyl <<= 12; cyl |= addr->cc; return cyl; } u_int16_t vtoc_get_head_from_cchhb(cchhb_t *addr) { /* decode heads for large volumes */ return addr->hh & 0x000F; } /* * some functions to convert cyl-cyl-head-head addresses to * block or track numbers * Note: Record zero is special, so first block on a track is * in record 1! */ u_int64_t cchhb2blk(cchhb_t *p, struct hd_geometry *geo) { return (u_int64_t) vtoc_get_cyl_from_cchhb(p) * geo->heads * geo->sectors + vtoc_get_head_from_cchhb(p) * geo->sectors + p->b; } u_int64_t cchh2blk (cchh_t *p, struct hd_geometry *geo) { return (u_int64_t) vtoc_get_cyl_from_cchh(p) * geo->heads * geo->sectors + vtoc_get_head_from_cchh(p) * geo->sectors; } u_int32_t cchh2trk (cchh_t *p, struct hd_geometry *geo) { return vtoc_get_cyl_from_cchh(p) * geo->heads + vtoc_get_head_from_cchh(p); } /* * */ void vtoc_set_date (labeldate_t * d, u_int8_t year, u_int16_t day) { d->year = year; d->day = day; } /* * initializes the volume label with EBCDIC blanks */ void vtoc_volume_label_init (volume_label_t *vlabel) { sprintf(buffer, "%84s", " "); vtoc_ebcdic_enc(buffer, buffer, 84); memcpy(vlabel, buffer, 84); } /* * reads the volume label from dasd */ int vtoc_read_volume_label (char *device, unsigned long vlabel_start, volume_label_t *vlabel) { int f, rc; if ((f = open(device, O_RDONLY)) < 0) { vtoc_error(unable_to_open, device, "Could not read volume label."); } if (lseek(f, vlabel_start, SEEK_SET) < 0) { close(f); vtoc_error(unable_to_seek, device, "Could not read volume label."); } rc = read(f, vlabel, sizeof(volume_label_t)); if (rc != sizeof(volume_label_t)) { close(f); vtoc_error(unable_to_read, device, "Could not read volume label."); } close(f); return 0; } /* * writes the volume label to dasd */ int vtoc_write_volume_label (char *device, unsigned long vlabel_start, volume_label_t *vlabel) { int rc, f; if ((f = open(device, O_WRONLY)) < 0) { vtoc_error(unable_to_open, device, "Could not write volume label."); } if (lseek(f, vlabel_start, SEEK_SET) < 0) { close(f); vtoc_error(unable_to_seek, device, "Could not write volume label."); } rc = write(f, vlabel, sizeof(volume_label_t)); if (rc != sizeof(volume_label_t)) { close(f); vtoc_error(unable_to_write, device, "Could not write volume label."); } close(f); return 0; } /* * takes a string as input, converts it to uppercase, translates * it to EBCDIC and fills it up with spaces before it copies it * as volume serial to the volume label */ void vtoc_volume_label_set_volser (volume_label_t *vlabel, char *volser) { int j, i = strlen(volser); char s[VOLSER_LENGTH + 1]; strcpy(s, " "); vtoc_ebcdic_enc(s, s, VOLSER_LENGTH); memcpy(vlabel->volid, s, VOLSER_LENGTH); if (i > VOLSER_LENGTH) i = VOLSER_LENGTH; strncpy(s, volser, i); for (j=0; jvolid, s, i); return; } /* * returns the volume serial number right after it is translated * to ASCII */ char * vtoc_volume_label_get_volser (volume_label_t *vlabel, char *volser) { vtoc_ebcdic_dec(vlabel->volid, volser, VOLSER_LENGTH); volser[VOLSER_LENGTH] = '\0'; return volser; } /* * sets the volume label key right after * it has been translated to EBCDIC */ void vtoc_volume_label_set_key (volume_label_t *vlabel, char *key) { char s[4]; vtoc_ebcdic_enc(key, s, 4); strncpy(vlabel->volkey, s, 4); return; } /* * sets the volume label identifier right * after it has been translated to EBCDIC */ void vtoc_volume_label_set_label (volume_label_t *vlabel, char *lbl) { char s[4]; vtoc_ebcdic_enc(lbl, s, 4); strncpy(vlabel->vollbl, s, 4); return; } /* * returns the volume label key = the label identifier * right after it has been translated to ASCII */ char * vtoc_volume_label_get_label (volume_label_t *vlabel, char *lbl) { vtoc_ebcdic_dec(vlabel->vollbl, lbl, 4); lbl[4] = '\0'; return lbl; } /* * reads either a format4 label or a format1 label * from the specified position */ void vtoc_read_label (char *device, unsigned long position, format1_label_t *f1, format4_label_t *f4, format5_label_t *f5, format7_label_t *f7) { int f,t; if ((f = open(device, O_RDONLY)) < 0) vtoc_error(unable_to_open, device, "Could not read VTOC labels."); if (lseek(f, position, SEEK_SET) < 0) { close(f); vtoc_error(unable_to_seek, device, "Could not read VTOC labels."); } if (f1 != NULL) { t = sizeof(format1_label_t); if (read(f, f1, t) != t) { close(f); vtoc_error(unable_to_read, device, "Could not read VTOC FMT1 DSCB."); } } if (f4 != NULL) { t = sizeof(format4_label_t); if (read(f, f4, t) != t) { close(f); vtoc_error(unable_to_read, device, "Could not read VTOC FMT4 DSCB."); } } if (f5 != NULL) { t = sizeof(format5_label_t); if (read(f, f5, t) != t) { close(f); vtoc_error(unable_to_read, device, "Could not read VTOC FMT5 DSCB."); } } if (f7 != NULL) { t = sizeof(format7_label_t); if (read(f, f7, t) != t) { close(f); vtoc_error(unable_to_read, device, "Could not read VTOC FMT7 DSCB."); } } close(f); } /* * writes either a FMT1, FMT4 or FMT5 label * to the specified position */ void vtoc_write_label (char *device, unsigned long position, format1_label_t *f1, format4_label_t *f4, format5_label_t *f5, format7_label_t *f7, format9_label_t *f9) { int f,t; if ((f = open(device, O_WRONLY)) == -1) { vtoc_error(unable_to_open, device, "Could not write VTOC labels."); } if (lseek(f, position, SEEK_SET) == -1) { close(f); vtoc_error(unable_to_seek, device, "Could not write VTOC labels."); } if (f1 != NULL) { t = sizeof(format1_label_t); if (write(f, f1, t) != t) { close(f); vtoc_error(unable_to_write, device, "Could not write VTOC FMT1 DSCB."); } } if (f4 != NULL) { t = sizeof(format4_label_t); if (write(f, f4, t) != t) { close(f); vtoc_error(unable_to_write, device, "Could not write VTOC FMT4 DSCB."); } } if (f5 != NULL) { t = sizeof(format5_label_t); if (write(f, f5, t) != t) { close(f); vtoc_error(unable_to_write, device, "Could not write VTOC FMT5 DSCB."); } } if (f7 != NULL) { t = sizeof(format7_label_t); if (write(f, f7, t) != t) { close(f); vtoc_error(unable_to_write, device, "Could not write VTOC FMT7 DSCB."); } } if (f9 != NULL) { t = sizeof(format9_label_t); if (write(f, f9, t) != t) { close(f); vtoc_error(unable_to_write, device, "Could not write VTOC FMT9 DSCB."); } } close(f); } /* * initializes a format4 label */ void vtoc_init_format4_label ( format4_label_t *f4, unsigned int compat_cylinders, unsigned int real_cylinders, unsigned int tracks, unsigned int blocks, unsigned int blksize, u_int16_t dev_type) { int i; cchh_t lower = {VTOC_START_CC, VTOC_START_HH}; cchh_t upper = {VTOC_START_CC, VTOC_START_HH}; for (i=0; i<44; i++) f4->DS4KEYCD[i] = 0x04; f4->DS4IDFMT = 0xf4; vtoc_set_cchhb(&f4->DS4HPCHR, 0x0000, 0x0000, 0x00); f4->DS4DSREC = blocks - 2; /* free space starts right behind VTOC vtoc_set_cchh(&f4->DS4HCCHH, VTOC_START_CC, VTOC_START_HH + 1);*/ vtoc_set_cchh(&f4->DS4HCCHH, 0x0000, 0x0000); f4->DS4NOATK = 0x0000; f4->DS4VTOCI = 0x00; f4->DS4NOEXT = 0x01; f4->DS4SMSFG = 0x00; f4->DS4DEVAC = 0x00; /* -- begin f4->DS4DEVCT -- */ f4->DS4DEVCT.DS4DSCYL = compat_cylinders; f4->DS4DEVCT.DS4DSTRK = tracks; switch (dev_type) { case DASD_3380_TYPE: f4->DS4DEVCT.DS4DEVTK = DASD_3380_VALUE; break; case DASD_3390_TYPE: f4->DS4DEVCT.DS4DEVTK = DASD_3390_VALUE; break; case DASD_9345_TYPE: f4->DS4DEVCT.DS4DEVTK = DASD_9345_VALUE; break; default: f4->DS4DEVCT.DS4DEVTK = blocks * blksize;; } f4->DS4DEVCT.DS4DEVI = 0x00; f4->DS4DEVCT.DS4DEVL = 0x00; f4->DS4DEVCT.DS4DEVK = 0x00; f4->DS4DEVCT.DS4DEVFG = 0x30; f4->DS4DEVCT.DS4DEVTL = 0x0000; f4->DS4DEVCT.DS4DEVDT = blocks; f4->DS4DEVCT.DS4DEVDB = 0x00; /* -- end f4->DS4DEVCT -- */ bzero(f4->DS4AMTIM, sizeof(f4->DS4AMTIM)); bzero(f4->DS4AMCAT, sizeof(f4->DS4AMCAT)); bzero(f4->DS4R2TIM, sizeof(f4->DS4R2TIM)); bzero(f4->res1, sizeof(f4->res1)); bzero(f4->DS4F6PTR, sizeof(f4->DS4F6PTR)); /* -- begin f4lbl->DS4VTOCE -- */ vtoc_set_extent(&f4->DS4VTOCE, 0x01, 0x00, &lower, &upper); /* -- end f4lbl->DS4VTOCE -- */ bzero(f4->res2, sizeof(f4->res2)); f4->DS4EFLVL = 0x00; bzero(&f4->DS4EFPTR, sizeof(f4->DS4EFPTR)); bzero(&f4->res3, sizeof(f4->res3)); f4->DS4DCYL = real_cylinders; bzero(f4->res4, sizeof(f4->res4)); f4->DS4DEVF2 = 0x40; /* allow format 8 and 9 labels */ bzero(&f4->res5, sizeof(f4->res5)); } /* * initializes a format5 label */ void vtoc_init_format5_label (format5_label_t *f5) { int i; bzero(f5, sizeof(format5_label_t)); for (i=0; i<4; i++) f5->DS5KEYID[i] = 0x05; f5->DS5FMTID = 0xf5; } /* * initializes a format7 label */ void vtoc_init_format7_label (format7_label_t *f7) { int i; bzero(f7, sizeof(format7_label_t)); for (i=0; i<4; i++) f7->DS7KEYID[i] = 0x07; f7->DS7FMTID = 0xf7; } /* * helper function that initializes a most parts of a * format1 or format 8 label, all but the field DS1FMTID */ static void vtoc_init_format_1_8_label ( unsigned int blksize, extent_t *part_extent, format1_label_t *f1) { struct tm * creatime; time_t t; char str[80]; /* get actual date */ t = time(NULL); creatime = gmtime(&t); bzero(f1->DS1DSNAM, sizeof(f1->DS1DSNAM)); sprintf(str, "PART .NEW "); vtoc_ebcdic_enc(str, str, 44); memcpy(f1->DS1DSNAM, str, 44); memcpy((char *) f1->DS1DSSN, " ", 6); f1->DS1VOLSQ = 0x0001; vtoc_set_date(&f1->DS1CREDT, (u_int8_t) creatime->tm_year, (u_int16_t) creatime->tm_yday); /* expires never - 99 365 */ vtoc_set_date(&f1->DS1EXPDT, 0x63, 0x016D); f1->DS1NOEPV = 0x01; f1->DS1NOBDB = 0x00; f1->DS1FLAG1 = 0x00; vtoc_ebcdic_enc("IBM LINUX ", str, 13); memcpy((char *)f1->DS1SYSCD, str, 13); vtoc_set_date(&f1->DS1REFD, (u_int8_t) creatime->tm_year, (u_int16_t) creatime->tm_yday); f1->DS1SMSFG = 0x00; f1->DS1SCXTF = 0x00; f1->DS1SCXTV = 0x0000; f1->DS1DSRG1 = 0x00; f1->DS1DSRG2 = 0x00; f1->DS1RECFM = 0x88; f1->DS1OPTCD = 0x00; f1->DS1BLKL = blksize; f1->DS1LRECL = blksize; f1->DS1KEYL = 0x00; f1->DS1RKP = 0x0000; f1->DS1DSIND = 0x80; /* last volume for this dataset */ f1->DS1SCAL1 = 0x80; bzero(&f1->DS1SCAL3, sizeof(f1->DS1SCAL3)); vtoc_set_ttr(&f1->DS1LSTAR, 0x0000, 0x00); f1->DS1TRBAL = 0x00; bzero(&f1->res1, sizeof(f1->res1)); memcpy(&f1->DS1EXT1, part_extent, sizeof(extent_t)); bzero(&f1->DS1EXT2, sizeof(extent_t)); bzero(&f1->DS1EXT3, sizeof(extent_t)); vtoc_set_cchhb(&f1->DS1PTRDS, 0x0000, 0x0000, 0x00); } void vtoc_init_format1_label ( unsigned int blksize, extent_t *part_extent, format1_label_t *f1) { vtoc_init_format_1_8_label(blksize, part_extent, f1); f1->DS1FMTID = 0xf1; } void vtoc_init_format8_label ( unsigned int blksize, extent_t *part_extent, format1_label_t *f8) { vtoc_init_format_1_8_label(blksize, part_extent, f8); f8->DS1FMTID = 0xf8; } void vtoc_update_format8_label ( cchhb_t *associated_f9, format1_label_t *f8) { memcpy(&f8->DS1PTRDS, associated_f9, sizeof(*associated_f9)); } void vtoc_init_format9_label ( format9_label_t *f9) { f9->DS9KEYID = 0x09; f9->DS9SUBTY = 0x01; f9->DS9NUMF9 = 1; f9->DS9FMTID = 0xf9; } /* * do some updates to the VTOC format4 label */ void vtoc_update_format4_label ( format4_label_t *f4, cchhb_t *highest_f1, u_int16_t unused_update) { /* update highest address of a format 1 label */ memcpy(&f4->DS4HPCHR, highest_f1, sizeof(cchhb_t)); /* update unused DSCB count */ f4->DS4DSREC = unused_update; } /* * reorganizes all extents within a FMT5 label */ static void vtoc_reorganize_FMT5_extents (format5_label_t *f5) { ds5ext_t *ext, *last, tmp; int i, j; for (i=0; i<26; i++) { if (i==0) last = &f5->DS5AVEXT; else if ((i > 0) && (i < 8)) last = &f5->DS5EXTAV[i-1]; else last = &f5->DS5MAVET[i-8]; for (j=i; j<26; j++) { if (j==0) ext = &f5->DS5AVEXT; else if ((j > 0) && (j < 8)) ext = &f5->DS5EXTAV[j-1]; else ext = &f5->DS5MAVET[j-8]; if (((ext->t > 0) && (last->t == 0)) || ((ext->t > 0) && (ext->t < last->t))) { tmp.t = last->t; tmp.fc = last->fc; tmp.ft = last->ft; last->t = ext->t; last->fc = ext->fc; last->ft = ext->ft; ext->t = tmp.t; ext->fc = tmp.fc; ext->ft = tmp.ft; } } } } /* * add a free space extent description to the VTOC FMT5 DSCB */ void vtoc_update_format5_label_add (format5_label_t *f5, int verbose, int trk, u_int16_t a, u_int16_t b, u_int8_t c) { ds5ext_t *ext = NULL, *tmp = NULL; int i; for (i=0; i<26; i++) { if (i==0) ext = &f5->DS5AVEXT; else if ((i > 0) && (i < 8)) ext = &f5->DS5EXTAV[i-1]; else ext = &f5->DS5MAVET[i-8]; if (((a < ext->t) && (a + b*trk + c > ext->t)) || ((a > ext->t) && (ext->t + ext->fc*trk + ext->ft > a))) { printf("BUG: overlapping free space extents " \ "in FMT5 DSCB!\nexiting...\n"); exit(1); } if ((ext->t + ext->fc + ext->ft) == 0x0000) { ext->t = a; ext->fc = b; ext->ft = c; tmp = ext; if (verbose) printf("FMT5 add extent: " \ "add new extent\n"); break; } } if (tmp == NULL) { /* BUG: no free extent found */ printf("BUG: no free FMT5 DSCB extent found!\nexiting...\n"); exit(1); } for (i=0; i<26; i++) { if (i==0) ext = &f5->DS5AVEXT; else if ((i > 0) && (i < 8)) ext = &f5->DS5EXTAV[i-1]; else ext = &f5->DS5MAVET[i-8]; if ((ext->t + ext->fc + ext->ft) == 0x0000) continue; if ((ext->t + ext->fc*trk + ext->ft) == tmp->t) { /* this extent precedes the new one */ ext->fc += (tmp->fc + (tmp->ft + ext->ft)/trk); ext->ft = (tmp->ft + ext->ft) % trk; bzero(tmp, sizeof(ds5ext_t)); tmp = ext; if (verbose) printf("FMT5 add extent: " \ "merge with predecessor\n"); i = -1; continue; } if ((tmp->t + tmp->fc*trk + tmp->ft) == ext->t) { /* this extent succeeds the new one */ ext->t = tmp->t; ext->fc += (tmp->fc + (tmp->ft + ext->ft)/trk); ext->ft = (tmp->ft + ext->ft) % trk; bzero(tmp, sizeof(ds5ext_t)); tmp = ext; if (verbose) printf("FMT5 add extent: " \ "merge with successor\n"); i = -1; continue; } } } /* * remove a free space extent description from the VTOC FMT5 DSCB */ void vtoc_update_format5_label_del (format5_label_t *f5, int verbose, int trk, u_int16_t a, u_int16_t b, u_int8_t c) { ds5ext_t *ext; int i, counter=0; for (i=0; i<26; i++) { if (i==0) ext = &f5->DS5AVEXT; else if ((i > 0) && (i < 8)) ext = &f5->DS5EXTAV[i-1]; else ext = &f5->DS5MAVET[i-8]; if ((a == ext->t) && (b == ext->fc) && (c == ext->ft)) { /* fills up whole free space gap */ bzero(ext, sizeof(ds5ext_t)); if (verbose) printf("FMT5 del extent: fills whole gap\n"); counter++; break; } if ((a == ext->t) && ((b < ext->fc) || (c < ext->ft))) { /* left-bounded in free space gap */ ext->t = ext->t + b*trk + c; if (c > ext->ft) { ext->fc -= (b + 1); ext->ft -= (c - trk); } else { ext->fc -= b; ext->ft -= c; } if (verbose) printf("FMT5 del extent: left bounded\n"); counter++; break; } if ((ext->t < a) && ((ext->t + ext->fc*trk + ext->ft) == (a + b*trk + c))) { /* right-bounded in free space gap */ if (c > ext->ft) { ext->fc -= (b + 1); ext->ft -= (c - trk); } else { ext->fc -= b; ext->ft -= c; } if (verbose) printf("FMT5 del extent: right bounded\n"); counter++; break; } if ((a > ext->t) && ((ext->t + ext->fc*trk + ext->ft) > (a + b*trk + c))) { /* partition devides free space into 2 pieces */ u_int16_t x = a + b*trk + c; u_int16_t w,y; u_int8_t z; w = (ext->t + ext->fc*trk + ext->ft) - (a + b*trk + c); y = w / trk; z = w % trk; ext->fc = (a - ext->t) / trk; ext->ft = (a - ext->t) % trk; vtoc_update_format5_label_add(f5, verbose, trk, x, y, z); if (verbose) printf("FMT5 del extent: 2 pieces\n"); counter++; break; } if ((a < ext->t) && (a + b*trk + c > ext->t) && (a + b*trk + c < ext->t + ext->fc*trk + ext->ft)) { printf("BUG: corresponding free space extent " \ "doesn't match free space currently shown " \ "in FMT5 DSCB!\nexiting...\n"); exit(1); } if ((a > ext->t) && (a < ext->t + ext->fc*trk + ext->ft) && (a + b*trk + c > ext->t + ext->fc*trk + ext->ft)) { printf("BUG: specified free space extent for " \ "deleting doesn't match free space " \ "currently shown in FMT5 DSCB!\n" \ "exiting...\n"); exit(1); } } if (counter > 0) return; printf("BUG: specified free space extent for " \ "deleting not found in FMT5 DSCB!\n" \ "exiting...\n"); exit(1); } /* * reorganizes all extents within a FMT7 label */ static void vtoc_reorganize_FMT7_extents (format7_label_t *f7) { ds7ext_t *ext, *last, tmp; int i, j; for (i=0; i<16; i++) { if (i<5) last = &f7->DS7EXTNT[i]; else last = &f7->DS7ADEXT[i-5]; for (j=i; j<16; j++) { if (j<5) ext = &f7->DS7EXTNT[j]; else ext = &f7->DS7ADEXT[j-5]; if (((ext->a > 0) && (last->a == 0)) || ((ext->a > 0) && (ext->a < last->a))) { tmp.a = last->a; tmp.b = last->b; last->a = ext->a; last->b = ext->b; ext->a = tmp.a; ext->b = tmp.b; } } } } /* * add a free space extent description to the VTOC FMT7 DSCB */ void vtoc_update_format7_label_add (format7_label_t *f7, int verbose, u_int32_t a, u_int32_t b) { ds7ext_t *ext = NULL, *tmp = NULL; int i; for (i=0; i<16; i++) { if (i<5) ext = &f7->DS7EXTNT[i]; else ext = &f7->DS7ADEXT[i-5]; if (((a < ext->a) && (b > ext->a) && (b < ext->b)) || ((a > ext->a) && (a < ext->b) && (b > ext->b))) { printf("BUG: overlapping free space extents " "in FMT7 DSCB!\nexiting...\n"); exit(1); } if ((ext->a + ext->b) == 0x00000000) { ext->a = a; ext->b = b; tmp = ext; if (verbose) printf("FMT7 add extent: add new extent\n"); break; } } if (tmp == NULL) { printf("BUG: no free FMT7 DSCB extent found!\nexiting...\n"); exit(1); } for (i=0; i<16; i++) { if (i<5) ext = &f7->DS7EXTNT[i]; else ext = &f7->DS7ADEXT[i-5]; if ((ext->a + ext->b) == 0x00000000) continue; if ((ext->b) == tmp->a) { /* this extent precedes the new one */ ext->b = tmp->b; bzero(tmp, sizeof(ds7ext_t)); tmp = ext; if (verbose) printf("FMT7 add extent: " \ "merge with predecessor\n"); i = -1; continue; } if (ext->a == (tmp->b)) { /* this extent succeeds the new one */ ext->a = tmp->a; bzero(tmp, sizeof(ds7ext_t)); tmp = ext; if (verbose) printf("FMT7 add extent: " \ "merge with successor\n"); i = -1; continue; } } } /* * remove a free space extent description from the VTOC FMT7 DSCB */ void vtoc_update_format7_label_del (format7_label_t *f7, int verbose, u_int32_t a, u_int32_t b) { ds7ext_t *ext; int i, counter=0; for (i=0; i<16; i++) { if (i<5) ext = &f7->DS7EXTNT[i]; else ext = &f7->DS7ADEXT[i-5]; if ((a == ext->a) && (b == ext->b)) { /* fills up whole free space gap */ bzero(ext, sizeof(ds7ext_t)); if (verbose) printf("FMT7 del extent: fills whole gap\n"); counter++; break; } if ((a == ext->a) && (b < ext->b)) { /* left-bounded in free space gap */ ext->a = b; if (verbose) printf("FMT7 add extent: left-bounded\n"); counter++; break; } if ((a > ext->a) && (b == ext->b)) { /* right-bounded in free space gap */ ext->b = a; if (verbose) printf("FMT7 add extent: right-bounded\n"); counter++; break; } if ((a > ext->a) && (b < ext->b)) { /* partition devides free space into 2 pieces */ vtoc_update_format7_label_add(f7, verbose, b, ext->b); ext->b = a; if (verbose) printf("FMT7 add extent: 2 pieces\n"); counter++; break; } if (((a < ext->a) && (b > ext->a)) || ((a < ext->b) && (b > ext->b))) { printf("BUG: specified free space extent for deleting " "doesn't match free space currently shown in " "FMT7 DSCB!\nexiting...\n"); exit(1); } } if (counter > 0) return; printf("BUG: specified free space extent for deleting not found " "in FMT7 DSCB!\nexiting...\n"); exit(1); } /* * */ void vtoc_set_freespace(format4_label_t *f4, format5_label_t *f5, format7_label_t *f7, char ch, int verbose, u_int32_t start, u_int32_t stop, u_int32_t cyl, u_int32_t trk) { if ((cyl * trk) > BIG_DISK_SIZE) { if (ch == '+') { vtoc_update_format7_label_add(f7, verbose, start, /* ds7ext RTA + 1 */ stop + 1); } else if (ch == '-') { vtoc_update_format7_label_del(f7, verbose, start, /* ds7ext RTA + 1 */ stop + 1); } else { printf("BUG: syntax error in vtoc_set_freespace.\n"); } vtoc_reorganize_FMT7_extents (f7); f4->DS4VTOCI = 0xa0; f4->DS4EFLVL = 0x07; vtoc_set_cchhb(&f4->DS4EFPTR, 0x0000, 0x0001, 0x03); } else { u_int16_t x,y; u_int8_t z; x = (u_int16_t) start; y = (u_int16_t) ((stop - start + 1) / trk); z = (u_int8_t) ((stop - start + 1) % trk); if (ch == '+') { vtoc_update_format5_label_add(f5, verbose, trk, x, y, z); } else if (ch == '-') { vtoc_update_format5_label_del(f5, verbose, trk, x, y, z); } else { printf("BUG: syntax error in vtoc_set_freespace.\n"); } vtoc_reorganize_FMT5_extents (f5); } } s390-tools-2.38.0/libzds/000077500000000000000000000000001502674226300147365ustar00rootroot00000000000000s390-tools-2.38.0/libzds/Makefile000066400000000000000000000003741502674226300164020ustar00rootroot00000000000000include ../common.mak ALL_CFLAGS += -D_FILE_OFFSET_BITS=64 ifneq (${HAVE_CURL},0) ALL_CFLAGS += -DHAVE_CURL endif lib = libzds.a all: $(lib) objects = libzds.o $(lib): $(objects) install: all clean: rm -f *.o $(lib) .PHONY: all install clean s390-tools-2.38.0/libzds/libzds.c000066400000000000000000003733701502674226300164060ustar00rootroot00000000000000/** * @file libzds.c * This is the implementation of the internal library libzds. * Please note that this library should currently only be used * by programs in the s390-tools package. It is not yet meant * for external use as interfaces and definitions may change * without further notice. * * Copyright IBM Corp. 2013, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #ifdef HAVE_CURL #include #endif /* HAVE_CURL */ #include "lib/util_libc.h" #include "lib/dasd_base.h" #include "lib/dasd_sys.h" #include "lib/libzds.h" #include "lib/vtoc.h" /** @cond PRIVATE */ /******************************************************************************/ /* libzds structure definitions */ /******************************************************************************/ /** * @brief Maximum size of a volume serial string (NOT including one byte for * 0-termination) */ #define MAXVOLSER 6 /* * The following structures are declared in libzds.h but defined here in * the .c file to keep them opaque to the user of the library. */ struct errorlog { struct util_list *entries; }; /** * @brief Size of the message buffer in errormsg * */ #define ERRORMSG 240 #define BUSIDSIZE 8 #define EBCDIC_SP 0x40 #define EBCDIC_LF 0x25 /** * @brief An internal structure that represents an entry in the error log. */ struct errormsg { /** @brief List head to store a list of errormsg in struct errorlog */ struct util_list_node list; /** @brief error code that was associated with this message*/ int error; /** @brief a descriptive message text */ char text[ERRORMSG]; }; struct dscbiterator { /** @brief The raw_vtoc this iterator refers to */ struct raw_vtoc *rawvtoc; /** @brief Index to the vtocindex array in rawvtoc */ unsigned int i; }; struct dasdhandle { /** @brief The struct dasd this context relates to */ struct dasd *dasd; /** @brief File descriptor for the block device. * Should be -1 when the device not open */ int fd; /** @brief Detailed error messages in case of a problem */ struct errorlog *log; }; struct pdsmember { /** @brief List head that is used to store a list of members in * struct dataset */ struct util_list_node list; /** @brief Member name, converted from EBCDIC to ASCII */ char name[MEMBERNAMELENGTH]; /** @brief The track the member starts in, relative to the data set. * * @note This number is relative to the data set, with track 0 * being the first track of the data set. It is independent * of the DASD geometry or extent location. */ unsigned short track; /** @brief First record of the member starts in, relative to the * start track.*/ unsigned char record; /** @brief Marks if pdsmember is an alias (we make no distinction * between a regular member and an alias). */ unsigned char is_alias; /** @brief Detailed error messages in case of a problem */ struct errorlog *log; }; /** * @brief An internal structure that represents part of a multi volume data set * * Data sets can be spread over several DASD devices (multi volume data set), * and this structure represents one such part. Each data set has at least one * datasetpart. */ struct datasetpart { /** @brief The dasd that this part resides on */ struct dasd *dasdi; /** @brief Pointer to the respective format 1 DSCB in the raw_vtoc * of that dasd */ format1_label_t *f1; /** @brief Each part can consist of up to MAXEXTENTS (16) extents */ extent_t ext[MAXEXTENTS]; }; struct dataset { /** @brief List head that is used to store a list of data sets in * struct zdsroot */ struct util_list_node list; /** @brief Data set name, translated from EBCDIC to ASCII, 0-terminated * and with any blank padding removed */ char name[MAXDSNAMELENGTH]; /** @brief Array of data set parts this data set consists of. * * We use just an regular array as the number of parts is limited. * Each part has a specific position, as defined by the DS1VOLSQ * value in the parts format 1 label. */ struct datasetpart *dsp[MAXVOLUMESPERDS]; /** @brief Number of parts this data set has * * @note This is the number of data set parts we have already found. * As long as there are still gaps in the dsp array, dspcount may be * smaller than the largest index of an element in the dsp array. */ int dspcount; /** @brief Flag that is set to 1 if we have all parts * * @note: In cases where a dataset consists of only one part, the * the fist part should be flagged as last part as well in DS1DSIND, * but this seems not to be reliable. So, as long as we only have only * found one part in position 0, we may set iscomplete, even if we * have no 'last part' marker found. */ int iscomplete; /** @brief If a data set is a partitioned data set (PDS), then this * contains a list of members, otherwise the list is empty */ struct util_list *memberlist; /** @brief Detailed error messages in case of a problem */ struct errorlog *log; }; struct memberiterator { /** @brief Data set that holds the members */ struct dataset *ds; /** @brief The last selected member. */ struct pdsmember *memberi; }; struct dsiterator { /** @brief zdsroot that holds the data sets */ struct zdsroot *zdsroot; /** @brief The last selected data set */ struct dataset *dsi; }; struct dasditerator { /** @brief zdsroot that holds the dasds */ struct zdsroot *zdsroot; /** @brief The last selected dasd */ struct dasd *dasdi; }; struct zdsroot { /** @brief list of dasds */ struct util_list *dasdlist; /** @brief list of data sets */ struct util_list *datasetlist; /** @brief Detailed error messages in case of a problem */ struct errorlog *log; }; /** * @brief Internal structure to keep track of offsets in the data set */ struct seekelement { /** @brief Data set part this element refers to */ unsigned char dsp_no; /** @brief The extent on that part/dasd */ unsigned char ext_seq_no; /** @brief The starting track on that part/dasd */ unsigned int bufstarttrk; /** @brief The absolute offset in the data set */ long long databufoffset; }; /** * @brief Default value for the tracks value in dshandle */ #define TRACK_BUFFER_DEFAULT 128 struct dshandle { /** @brief Data set this context relates to */ struct dataset *ds; /** @brief Pointer to member, only applicable to PDS */ struct pdsmember *member; /** @brief One dasdhandle per data set part * * The dshandle functions do not read directly from the devices, * instead they use the dasdhandle interfacesw. */ struct dasdhandle *dasdhandle[MAXVOLUMESPERDS]; /** @brief A multiplier that is used to determine the various buffer sizes. Number of tracks in one track frame. */ unsigned int tracks_per_frame; /** @brief Flag: While interpreting the data, keep the record * descriptor words in the data stream */ int keepRDW; /** @brief Flag that is set between open and close */ int is_open; /** @brief This flag is set when during interpretation of the track * buffer the end of the data is found */ int eof_reached; /* The following values describe our current position within the data * set */ /** @brief Index number of the current data set part */ int dsp_no; /** @brief The sequence number of the current extent in the current * data set part */ int ext_seq_no; /** @brief Start buffer interpretation at this record. * * Data set members may start in the middle of a track. So we need * to know with which record to start. */ unsigned char startrecord; /** @brief The first track of the extent that dsp_no and ext_seq_no * point to */ unsigned int extstarttrk; /** @brief The last track of the extent that dsp_no and ext_seq_no * point to */ unsigned int extendtrk; /** @brief Start of the area that is currently in the rawbuffer */ unsigned int bufstarttrk; /** @brief End of the area that is currently in the rawbuffer */ unsigned int bufendtrk; /** @brief Running number of the current track frame */ long long frameno; /** @brief Buffer for the raw track images */ char *rawbuffer; /** @brief Buffer for the extracted user data */ char *databuffer; /** @brief Size of the rawbuffer */ long long rawbufmax; /** @brief Size of the databuffer */ long long databufmax; /** @brief Size of the currently used part of the rawbuffer */ long long rawbufsize; /** @brief Size of the currently used part of the databuffer */ long long databufsize; /** @brief Current position of the databuffer relative to the begin * of the data set */ long long databufoffset; /** @brief Current position in the databuffer */ long long bufpos; /** @brief Buffer for seek data points */ struct seekelement *seekbuf; /** @brief Total number of elements in seekbuf */ unsigned long long seek_count; /** @brief Number of used elements in seekbuf */ unsigned long long seek_current; /** @brief Modulo that determines which track frame is stored in the * seek buffer * * Example: If skip is 2, then every 2'nd frame is stored. */ unsigned long long skip; /** @brief Detailed error messages in case of a problem */ struct errorlog *log; char *session_ref; iconv_t *iconv; char *convbuffer; }; /** @endcond */ /******************************************************************************/ /* BASIC level functions */ /******************************************************************************/ static void dasd_free(struct dasd *dasd); static void dataset_free_memberlist(struct dataset *ds); static void errorlog_free(struct errorlog *log); static void errorlog_clear(struct errorlog *log); static int errorlog_add_message(struct errorlog **log, struct errorlog *oldlog, int error_code, const char *message_format, ...) __attribute__ ((format (printf, 4, 5))); /** * Since the zdsroot is the root for all the other data structures, * this should be one of the first functions to call. * @param[out] root Reference to a pointer variable in which the newly * allocated structure will be returned. * @return 0 on success, otherwise one of the following error codes: * - ENOMEM Could not allocate structure due to lack of memory. */ int lzds_zdsroot_alloc(struct zdsroot **root) { struct zdsroot *tmproot; *root = NULL; tmproot = malloc(sizeof(*tmproot)); if (!tmproot) return ENOMEM; memset(tmproot, 0, sizeof(*tmproot)); tmproot->dasdlist = util_list_new(struct dasd, list); tmproot->datasetlist = util_list_new(struct dataset, list); *root = tmproot; return 0; } /** * It should be noted that this frees all structures that are owned by the * root structure as well. For example, a pointer to a struct dasd that * has been returned by lzds_zdsroot_add_device is not valid anymore. * * @param[in] root Reference to the zdsroot structure that is to be freed. */ void lzds_dslist_free(struct zdsroot *root) { struct dataset *ds, *nextds; int i; util_list_iterate_safe(root->datasetlist, ds, nextds) { util_list_remove(root->datasetlist, ds); dataset_free_memberlist(ds); for (i = 0; i < MAXVOLUMESPERDS; ++i) free(ds->dsp[i]); errorlog_free(ds->log); free(ds); } } /** * It should be noted that this frees all structures that are owned by the * root structure as well. For example, a pointer to a struct dasd that * has been returned by lzds_zdsroot_add_device is not valid anymore. * * @param[in] root Reference to the zdsroot structure that is to be freed. */ void lzds_zdsroot_free(struct zdsroot *root) { struct dasd *dasd, *nextdasd; if (!root) return; util_list_iterate_safe(root->dasdlist, dasd, nextdasd) { util_list_remove(root->dasdlist, dasd); dasd_free(dasd); } util_list_free(root->dasdlist); lzds_dslist_free(root); util_list_free(root->datasetlist); errorlog_free(root->log); free(root); } /** * @brief Subroutine of lzds_zdsroot_add_device * * This function determines some basic DASD geometry information and stores * it in the struct dasd for later use. * * @param[in] dasd Reference to the dasd to work on. * @return 0 on success, otherwise one of the following error codes: * - EIO Some error prevented us from gaining this information * */ static int dasd_read_geometry(struct dasd *dasd) { unsigned long long size_in_bytes; errorlog_clear(dasd->log); if (dasd_get_blocksize_in_bytes(dasd->device, &size_in_bytes) != 0) return errorlog_add_message( &dasd->log, NULL, EIO, "read geometry: could not get size from device %s\n", dasd->device); /* label_block and heads are simply hard coded with the correct values * for ECKD DASDs. This makes us independent from any DASD specific * ioctls like BIODASDINFO and allows us to work on DASD images via * loopback device. */ dasd->label_block = 2; dasd->heads = 15; dasd->cylinders = (size_in_bytes / (dasd->heads * RAWTRACKSIZE)); return 0; } /** * @brief Subroutine of lzds_zdsroot_add_device * * This function goes through the list of dasds in root and verifies that * the a dasd with the given device name is not yet present. * @param[in] root Reference to the zdsroot structure the new struct dasd * is to be added to. * @param[in] devnode String that holds the name of the device node, * e.g. "/dev/dasdb". * @return true if matching dasd has been found, false if not */ static int zdsroot_is_duplicate_device(struct zdsroot *root, const char *devnode) { struct dasd *dasd; dasd = NULL; lzds_zdsroot_get_dasd_by_node_name(root, devnode, &dasd); return !(dasd == NULL); } /** * This function creates a new struct dasd and adds it to the root. * It can later be traversed using the dasditerator functions. * * @param[in] root Reference to the zdsroot structure the new struct dasd * is to be added to. * @param[in] devnode String that holds the name of the device node, * e.g. "/dev/dasdb". * @param[out] dasd Reference to a pointer variable in which the newly * allocated structure will be returned. * This pointer is returned for the convenience of the user, * to be used with follow on calls, e.g to lzds_dasd_read_vlabel. * * @return 0 on success, otherwise one of the following error codes: * - ENOMEM Could not allocate structure due to lack of memory. * - ENOTTY The used ioctl is not supported by the device (i.e. the * device is not a DASD.) * - EIO Some other error prevented us from gaining this information * * @note It is not guaranteed that ENOTTY is returned when the device is * not a DASD. It depends on the device whether ENOTTY or EIO is returned. */ int lzds_zdsroot_add_device(struct zdsroot *root, const char *devnode, struct dasd **dasd) { struct dasd *dasdtmp; int rc; errorlog_clear(root->log); if (zdsroot_is_duplicate_device(root, devnode)) { return errorlog_add_message( &root->log, NULL, EINVAL, "add device: duplicate device %s\n", devnode); } dasdtmp = malloc(sizeof(*dasdtmp)); if (!dasdtmp) return ENOMEM; memset(dasdtmp, 0, sizeof(*dasdtmp)); dasdtmp->device = strdup(devnode); dasdtmp->inusefd = open(dasdtmp->device, O_RDONLY); if (dasdtmp->inusefd < 0) { errorlog_add_message( &root->log, dasdtmp->log, EIO, "add device: could open device %s\n", dasdtmp->device); dasd_free(dasdtmp); return EIO; } rc = dasd_read_geometry(dasdtmp); if (rc) { errorlog_add_message( &root->log, dasdtmp->log, EIO, "add device: could not read device data from %s\n", dasdtmp->device); close(dasdtmp->inusefd); dasd_free(dasdtmp); return EIO; } util_list_add_tail(root->dasdlist, dasdtmp); if (dasd) *dasd = dasdtmp; return 0; } /** * @param[in] dasd A dasd on which an error occurred. * @param[out] log Reference to a variable in which the errorlog * is returned. */ void lzds_dasd_get_errorlog(struct dasd *dasd, struct errorlog **log) { *log = dasd->log; } /** * @brief Subroutine of lzds_zdsroot_free. Frees the struct dasd and everything * that belogns to it. * * @param[in] dasd Pointer to the struct dasd that is to be freed. */ static void dasd_free(struct dasd *dasd) { free(dasd->device); free(dasd->vlabel); if (dasd->rawvtoc) { free(dasd->rawvtoc->rawdata); free(dasd->rawvtoc->vtocindex); errorlog_free(dasd->rawvtoc->log); free(dasd->rawvtoc); } errorlog_free(dasd->log); close(dasd->inusefd); free(dasd); } /** * @param[in] zdsroot Reference to struct zdsroot that the iterator will be * bound to. The iterator will traverse the dasds stored * in this zdsroot. * @param[out] it Reference to a pointer variable in which the newly allocated * structure will be returned. * @return 0 on success, otherwise one of the following error codes: * - ENOMEM Could not allocate structure due to lack of memory. */ int lzds_zdsroot_alloc_dasditerator(struct zdsroot *zdsroot, struct dasditerator **it) { *it = malloc(sizeof(struct dasditerator)); if (*it) { (*it)->dasdi = NULL; (*it)->zdsroot = zdsroot; return 0; } return ENOMEM; } /** * @param[in] it Pointer to the struct dasditerator that is to be freed. */ void lzds_dasditerator_free(struct dasditerator *it) { free(it); } /** * @param[out] it Reference to the struct dasditerator we use to traverse the * dasd list. * @param[out] dasd Reference to a pointer variable in which the next dasd in * the sequence will be returned. If there is no next DASD, * this variable will be set to NULL. * @return 0 on success, otherwise one of the following error codes: * - EPERM The end of the list has been reached. There is no further dasd. */ int lzds_dasditerator_get_next_dasd(struct dasditerator *it, struct dasd **dasd) { struct dasd *dasdtmp; if (!it->dasdi) dasdtmp = util_list_start(it->zdsroot->dasdlist); else dasdtmp = util_list_next(it->zdsroot->dasdlist, it->dasdi); *dasd = dasdtmp; if (!dasdtmp) return EPERM; it->dasdi = dasdtmp; return 0; } /** * @param[in] dasd The struct dasd we want to know the device of. * @param[out] device Reference to a pointer variable in which the device * string will be returned. This string holds the device * name as it was given to lzds_zdsroot_add_device. */ void lzds_dasd_get_device(struct dasd *dasd, char **device) { *device = dasd->device; } /** * @param[in] root Reference to the zdsroot that holds the dasd. * @param[in] device Pointer to a character string that holds the device node * name that we are looking for. It must be the same name as * previously given to lzds_zdsroot_add_device * @param[out] dasd Reference to a pointer variable in which the found struct * dasd will be returned. If no dasd was found, * this will be set to NULL * @return 0 on success, otherwise one of the following error codes: * - ENOMEM Could not allocate internal structures due to lack of memory. * - ENODEV No matching struct dasd was found. */ int lzds_zdsroot_get_dasd_by_node_name(struct zdsroot *root, const char *device, struct dasd **dasd) { struct dasditerator *dasdit; int rc; struct dasd *tempdasd; char *dasddev; errorlog_clear(root->log); rc = lzds_zdsroot_alloc_dasditerator(root, &dasdit); if (rc) return ENOMEM; rc = ENODEV; *dasd = NULL; while (!lzds_dasditerator_get_next_dasd(dasdit, &tempdasd)) { lzds_dasd_get_device(tempdasd, &dasddev); if (!strcmp(device, dasddev)) { rc = 0; *dasd = tempdasd; break; } } lzds_dasditerator_free(dasdit); return rc; } /** * @param[in] root A zdsroot on which an error occurred. * @param[out] log Reference to a variable in which the errorlog * is returned. */ void lzds_zdsroot_get_errorlog(struct zdsroot *root, struct errorlog **log) { *log = root->log; } /** * @brief free storage for a single error message * * @param[in] msg The message to be freed */ static void errormsg_free(struct errormsg *msg) { free(msg); } /** * @brief allocate storage for a single error message * * @param[out] msg Reference to a pointer variable in which the newly allocated * structure will be returned. * @return 0 on success, otherwise one of the following error codes: * - ENOMEM Could not allocate structure due to lack of memory. */ static int errormsg_alloc(struct errormsg **msg) { struct errormsg *tmpmsg; *msg = NULL; tmpmsg = malloc(sizeof(*tmpmsg)); if (!tmpmsg) return ENOMEM; memset(tmpmsg, 0, sizeof(*tmpmsg)); *msg = tmpmsg; return 0; } /** * @brief remove and free all messages from a given errolog * * After this operation new messages can be added to the log. * * @param[in] log The message log to be cleared. This may be NULL. */ static void errorlog_clear(struct errorlog *log) { struct errormsg *msg, *nextmsg; if (!log) return; util_list_iterate_safe(log->entries, msg, nextmsg) { util_list_remove(log->entries, msg); errormsg_free(msg); } } /** * @brief free storage for an error log, including all messages * * @param[in] log The error log to be freed. This may be NULL. */ static void errorlog_free(struct errorlog *log) { if (!log) return; errorlog_clear(log); util_list_free(log->entries); free(log); } /** * @brief allocate storage for an error log * * @param[out] log Reference to a pointer variable in which the newly allocated * structure will be returned. * @return 0 on success, otherwise one of the following error codes: * - ENOMEM Could not allocate structure due to lack of memory. */ static int errorlog_alloc(struct errorlog **log) { struct errorlog *tmplog; *log = NULL; tmplog = malloc(sizeof(*tmplog)); if (!tmplog) return ENOMEM; memset(tmplog, 0, sizeof(*tmplog)); tmplog->entries = util_list_new(struct errormsg, list); *log = tmplog; return 0; } /** * @brief add a new message to the front of a log. * * @param[out] log A reference to a errorlog pointer variable. If a log already * exists, old messages are cleared, otherwise a new log will * be created. * @param[in] oldlog A log that already contains messages, usually from a call * to a subordinate function. This may be the same errorlog as * referenced by log, in which case the existing messages * are retained. This may also be NULL. * @param[in] error_code The error code that will be stored in the new errormsg. * This is also the return value. * @param[in] message_format A format string for the message string * (see vsnprintf man page). * @param[in] ... A variable number of further parameters. * Must match the message_format string. */ static int errorlog_add_message(struct errorlog **log, struct errorlog *oldlog, int error_code, const char *message_format, ...) { struct errormsg *msg, *nextmsg; struct errorlog *tmplog; va_list ap; int rc; if (!log) return error_code; if (log && !*log) { errorlog_alloc(&tmplog); if (!tmplog) return error_code; *log = tmplog; } else { tmplog = *log; } if (tmplog != oldlog) { errorlog_clear(tmplog); if (oldlog) { util_list_iterate_safe(oldlog->entries, msg, nextmsg) { util_list_remove(oldlog->entries, msg); util_list_add_tail(tmplog->entries, msg); } } } if (!message_format) return error_code; rc = errormsg_alloc(&msg); if (rc) return error_code; va_start(ap, message_format); vsnprintf(msg->text, ERRORMSG - 1, message_format, ap); va_end(ap); msg->error = error_code; util_list_add_head(tmplog->entries, msg); return error_code; } /** * This is pretty a very simple implementation that just goes through * the list of messages in the log and for each message it prints * "rc : " * * @param[in] log A log that contains messages. * @param[in] stream The stream that these messages will be printed to. */ int lzds_errorlog_fprint(struct errorlog *log, FILE *stream) { struct errormsg *msg; int rc; if (!log) return 0; util_list_iterate(log->entries, msg) { rc = fprintf(stream, "rc %d: %s", msg->error, msg->text); if (rc < 0) return -rc; } return 0; } /******************************************************************************/ /* LOW level functions */ /******************************************************************************/ /** * @param[in] dasd The DASD to whose geometry we refer to. * @param[in] p Cylinder and head address * @param[out] track The sequential track number for the given * cylinder and head address. */ void lzds_dasd_cchh2trk(struct dasd *dasd, cchh_t *p, unsigned int *track) { *track = vtoc_get_cyl_from_cchh(p) * dasd->heads + vtoc_get_head_from_cchh(p); } /** * @param[in] dasd The DASD to whose geometry we refer to. * @param[out] cylinders The number of cylinders that DASD has */ void lzds_dasd_get_cylinders(struct dasd *dasd, unsigned int *cylinders) { *cylinders = dasd->cylinders; } /** * @param[in] dasd The DASD to whose geometry we refer to * @param[out] heads The number of heads that DASD has */ void lzds_dasd_get_heads(struct dasd *dasd, unsigned int *heads) { *heads = dasd->heads; } /** * @param[in] dasd Reference to struct dasd that represents * the DASD that we want to read from. * @param[out] dasdh Reference to a pointer variable in which the newly * allocated structure will be returned. * @return 0 on success, otherwise one of the following error codes: * - ENOMEM Could not allocate structure due to lack of memory. */ int lzds_dasd_alloc_dasdhandle(struct dasd *dasd, struct dasdhandle **dasdh) { struct dasdhandle *dasdhtmp; dasdhtmp = malloc(sizeof(*dasdhtmp)); if (!dasdhtmp) return ENOMEM; memset(dasdhtmp, 0, sizeof(*dasdhtmp)); dasdhtmp->fd = -1; dasdhtmp->dasd = dasd; *dasdh = dasdhtmp; return 0; } /** * @param[in] dasdh Pointer to the struct dasdhandle that is to be freed. */ void lzds_dasdhandle_free(struct dasdhandle *dasdh) { if (!dasdh) return; /* we close the file descriptor in case it wasn't done properly */ lzds_dasdhandle_close(dasdh); errorlog_free(dasdh->log); free(dasdh); } /** * @param[in] dasdh The dasd handle for the dasd that is to be opened. * @return 0 on success, otherwise one of the following error codes: * - EIO Could not open underlying device. */ int lzds_dasdhandle_open(struct dasdhandle *dasdh) { errorlog_clear(dasdh->log); dasdh->fd = open(dasdh->dasd->device, O_RDONLY | O_DIRECT); if (dasdh->fd < 0) { dasdh->fd = -1; return errorlog_add_message( &dasdh->log, NULL, EIO, "dasdhandle: could not open %s, errno %d\n", dasdh->dasd->device, errno); } return 0; } /** * @param[in] dasdh The dasdhandle that has to be closed * @return 0 on success, otherwise one of the following error codes: * - EIO Error when closing underlying dasd device. */ int lzds_dasdhandle_close(struct dasdhandle *dasdh) { int rc; errorlog_clear(dasdh->log); rc = 0; if (dasdh->fd >= 0) rc = close(dasdh->fd); dasdh->fd = -1; if (rc) return errorlog_add_message( &dasdh->log, NULL, EIO, "dasdhandle: could not close %s\n", dasdh->dasd->device); return 0; } /** * @param[in] dasdh The dasdhandle we are reading from * @param[in] starttrck First track to read * @param[in] endtrck Last track to read * @param[out] trackdata Target buffer we read into, must have at least the * size (endtrk - starttrk + 1) * RAWTRACKSIZE * @return 0 on success, otherwise one of the following error codes: * - EINVAL starttrck or endtrck are not within the boundaries of the * underlying DASD device. * - EPROTO Could not read a full track image * - EIO Other I/O error */ int lzds_dasdhandle_read_tracks_to_buffer(struct dasdhandle *dasdh, unsigned int starttrck, unsigned int endtrck, char *trackdata) { off_t trckseek; ssize_t residual; off_t rc; ssize_t count; unsigned int cylinders; unsigned int heads; errorlog_clear(dasdh->log); /* verify that endtrck is not beyond the end of the dasd */ lzds_dasd_get_cylinders(dasdh->dasd, &cylinders); lzds_dasd_get_heads(dasdh->dasd, &heads); if (starttrck > endtrck || endtrck >= cylinders * heads) return errorlog_add_message( &dasdh->log, NULL, EINVAL, "dasdhandle read tracks: start %u, end %u is" " out of bounds for device %s\n", starttrck, endtrck, dasdh->dasd->device); /* * Compute seek address of the first track and number of tracks * to be read. Please note that geo.sectors does not match our raw * track size of 16*4KB, so we use the RAWTRACKSIZE explicitly */ trckseek = (off_t)starttrck * RAWTRACKSIZE; /* residual is the number of bytes we still have to read */ residual = (off_t)(endtrck - starttrck + 1) * RAWTRACKSIZE; rc = lseek(dasdh->fd, trckseek, SEEK_SET); if (rc < 0) return errorlog_add_message( &dasdh->log, NULL, EINVAL, "dasdhandle read tracks: seek to %llu, failed" " for device %s\n", (unsigned long long)trckseek, dasdh->dasd->device); while (residual) { count = read(dasdh->fd, trackdata, residual); if (count < 0) return errorlog_add_message( &dasdh->log, NULL, EIO, "dasdhandle read tracks: read failed" " for device %s, start %u, end %u\n", dasdh->dasd->device, starttrck, endtrck); if (count % RAWTRACKSIZE) /* No full track read */ return errorlog_add_message( &dasdh->log, NULL, EPROTO, "dasdhandle read tracks: read returned " "unaligned data for device %s," "start %u, end %u\n", dasdh->dasd->device, starttrck, endtrck); residual -= count; trackdata += count; } return 0; } /******************************************************************************/ /* MID level functions */ /******************************************************************************/ /** * @brief Helper function that iterates through the records in a track buffer. * * @param[in] buffer Address of the track buffer * @param[in] size Size of the buffer * @param[in,out] record Pointer that has the current record pointer as input * and gets a pointer to the next record as output. * If it the current record pointer is null, then the * pointer to the first record is returned. * @return 0 on success, otherwise one of the following error codes: * - ENOENT If we have reached the end of the buffer and there are no * further records */ static int buffer_get_next_record(char *buffer, size_t size, char **record) { char *data, *next_record; unsigned long offset; unsigned int record_size; struct eckd_count *ecount; /* If *record contains no record yet, then we return the first record */ if (!*record) { *record = buffer; return 0; } data = *record; ecount = (struct eckd_count *)data; record_size = sizeof(*ecount) + ecount->kl + ecount->dl; data += record_size; next_record = NULL; while (!next_record) { /* check if we have reached the end of the buffer */ if (data >= buffer + size) { *record = NULL; return ENOENT; } /* If the 'next' record is the pseudo record, then we have * reached the end of data in this track and we have to jump * to the start of the next track to find the next record. */ if ((*(unsigned long long *)data) == ENDTOKEN) { offset = (unsigned long)data - (unsigned long)buffer; offset &= ~(RAWTRACKSIZE - 1); offset += RAWTRACKSIZE; data = buffer + offset; continue; } next_record = data; } *record = next_record; return 0; } /** * @brief Helper function that does the whole open/read/close cycle in one go. * * @param[in] dasd Pointer to struct dasd that represents * the DASD that we want to read from. * @param[in] starttrck First track to read * @param[in] endtrck Last track to read * @param[out] trackdata Target buffer we read into, must have at least the * size (endtrk - starttrk + 1) * RAWTRACKSIZE * @return 0 on success, otherwise one of the following error codes: * - ENOMEM Could not allocate internal structure due to lack of memory. * - EINVAL starttrck or endtrck are not within the boundaries of the * underlying DASD device. * - EPROTO Could not read a full track image * - EIO Other I/O error */ static int dasd_read_tracks(struct dasd *dasd, unsigned int starttrck, unsigned int endtrck, char *trackdata) { struct dasdhandle *dasdh; int rc, rc2; rc = lzds_dasd_alloc_dasdhandle(dasd, &dasdh); if (rc) return errorlog_add_message( &dasd->log, dasd->log, rc, "dasd read tracks: could not allocate dasdhandle\n"); rc = lzds_dasdhandle_open(dasdh); if (rc) { errorlog_add_message( &dasd->log, dasdh->log, rc, "dasd read tracks: could not open dasdhandle\n"); lzds_dasdhandle_free(dasdh); return rc; } rc = lzds_dasdhandle_read_tracks_to_buffer(dasdh, starttrck, endtrck, trackdata); if (rc) errorlog_add_message( &dasd->log, dasdh->log, rc, "dasd read tracks: read error\n"); rc2 = lzds_dasdhandle_close(dasdh); /* report close error only if we had no read error */ if (rc2 && !rc) { errorlog_add_message( &dasd->log, dasdh->log, rc, "dasd read tracks: could not close dasdhandle\n"); rc = rc2; } lzds_dasdhandle_free(dasdh); return rc; } /** * @brief Helper function that reads a volume label from a DASD. * * @param[in] dasd Pointer to struct dasd that represents * the DASD that we want to read from. * @param[out] vlabel Buffer to read the label into. * @return 0 on success, otherwise one of the following error codes: * - ENOMEM Could not allocate internal structure due to lack of memory. * - EIO Other I/O error */ static int dasd_read_vlabel_to_buffer(struct dasd *dasd, struct volume_label *vlabel) { int rc; unsigned int i; char *trackdata, *record; struct volume_label *label; struct eckd_count *ecount; unsigned long labelend, trackend; size_t label_size; trackdata = memalign(4096, RAWTRACKSIZE); /* page align for O_DIRECT */ if (!trackdata) return ENOMEM; rc = dasd_read_tracks(dasd, 0, 0, trackdata); if (rc) { free(trackdata); return errorlog_add_message( &dasd->log, dasd->log, EIO, "read vlabel: could not read track 0\n"); } /* fist step, find label record */ record = NULL; label = NULL; ecount = NULL; i = 0; while (!buffer_get_next_record(trackdata, RAWTRACKSIZE, &record)) { if (i == (dasd->label_block + 1)) { ecount = (struct eckd_count *)record; label = (struct volume_label *)(ecount + 1); break; } ++i; } if (!ecount || !label) { free(trackdata); return errorlog_add_message( &dasd->log, dasd->log, EPROTO, "read vlabel: could not find label record\n"); } /* verify record layout */ memset(vlabel, 0, sizeof(*vlabel)); labelend = (unsigned long)label + ecount->kl + ecount->dl; trackend = (unsigned long)trackdata + RAWTRACKSIZE; if ((ecount->kl + ecount->dl == 84) && (labelend <= trackend)) { /* VOL1 label */ memcpy(vlabel, label, ecount->kl + ecount->dl); } else if ((ecount->kl == 0) && (labelend <= trackend)) { /* LNX1 / CMS1 label */ label_size = MIN(ecount->dl, sizeof(*vlabel) - 4); memcpy(&vlabel->vollbl, label, label_size); } else { free(trackdata); return errorlog_add_message( &dasd->log, dasd->log, EPROTO, "read vlabel: record layout does not match VOL1" " label\n"); } free(trackdata); return 0; } /** * @param[in] dasd Pointer to struct dasd that represents * the DASD that we want to read from. * @return 0 on success, otherwise one of the following error codes: * - ENOMEM Could not allocate internal structure due to lack of memory. * - EIO Other I/O error */ int lzds_dasd_read_vlabel(struct dasd *dasd) { struct volume_label *vlabel; int rc; errorlog_clear(dasd->log); free(dasd->vlabel); dasd->vlabel = NULL; vlabel = malloc(sizeof(*vlabel)); if (!vlabel) return ENOMEM; rc = dasd_read_vlabel_to_buffer(dasd, vlabel); if (rc) free(vlabel); else dasd->vlabel = vlabel; return rc; } /** * @param[in] dasd Reference to struct dasd that we want to get the label * from. * @param[out] vlabel Reference to a pointer variable in which the struct * volume_label will be returned. * @return 0 on success, otherwise one of the following error codes: * - EINVAL The volume lable has not yet been read from the device. */ int lzds_dasd_get_vlabel(struct dasd *dasd, struct volume_label **vlabel) { *vlabel = dasd->vlabel; if (*vlabel) return 0; else return EINVAL; } /** * @param[in] rawvtoc Reference to struct raw_vtoc that the iterator will be * bound to. The iterator will traverse the DSCBs stored * in this raw_vtoc. * @param[out] it Reference to a pointer variable in which the newly allocated * structure will be returned. * @return 0 on success, otherwise one of the following error codes: * - ENOMEM Could not allocate structure due to lack of memory. */ int lzds_raw_vtoc_alloc_dscbiterator(struct raw_vtoc *rawvtoc, struct dscbiterator **it) { *it = malloc(sizeof(**it)); if (*it) { (*it)->i = rawvtoc->vtocrecno - 1; (*it)->rawvtoc = rawvtoc; return 0; } return ENOMEM; } /** * @param[in] it Pointer to the struct dscbiterator that is to be freed. */ void lzds_dscbiterator_free(struct dscbiterator *it) { free(it); } /** * @param[out] it Reference to the struct dscb iterator we use to traverse * the VTOC. * @param[out] dscb Reference to a pointer variable in which the next dscb in * the sequence will be returned. If there is no next dscb, * this variable will be set to NULL. * @return 0 on success, otherwise one of the following error codes: * - EPERM There is no further DSCB in the VTOC. */ int lzds_dscbiterator_get_next_dscb(struct dscbiterator *it, struct dscb **dscb) { struct eckd_count *ecount; unsigned int i; i = it->i + 1; while (i < it->rawvtoc->vtocindexcount) { ecount = (struct eckd_count *)(it->rawvtoc->vtocindex[i]); if (ecount && (ecount->kl == 44) && (ecount->dl == 96)) break; else ++i; } if (i < it->rawvtoc->vtocindexcount) { it->i = i; *dscb = (struct dscb *)(it->rawvtoc->vtocindex[it->i] + sizeof(*ecount)); return 0; } else { *dscb = NULL; return EPERM; } } /** * @brief Subroutine of lzds_raw_vtoc_get_dscb_from_cchhb * * This function takes a cylinder, head, block address as it can be * found in DSCBs and returns an index to the matching entry in the * raw_vtoc vtocindex. * * The cchhb2blk function of the libvtoc does not work for raw devices * as the 'sectors per track' value in the geo structure has no meaning * for a raw DASD. We need to take this value from the context, * e.g. from the format 4 label of the VTOC. * Since this computation is very specialized, we can go all the way and * just compute the index to the vtoc array. * * @param[in] rv The raw_vtoc we refer to. * @param[in] p The cylinder, head, block address structure. * @return index to the vtocindex array */ static long long vtocindex_from_cchhb(struct raw_vtoc *rv, cchhb_t *p) { long long recno; recno = (long long) vtoc_get_cyl_from_cchhb(p) * rv->dasd->heads * rv->vtoc_rec_per_track + vtoc_get_head_from_cchhb(p) * rv->vtoc_rec_per_track + p->b; return recno - (rv->vtoctrackoffset * rv->vtoc_rec_per_track); } /** * @note A cchhb address within a VTOC dscb is often set to zero to * indicate that this entry does not point anywhere. For example this * is the case at the end of a format 3 dscb chain. This special case * is handled by setting the dscb pointer to NULL and having a return * value of 0 (no error). * * @param[in] rv The raw_vtoc we refer to. * @param[in] p The cylinder, head, block address of the DSCB. * @param[out] dscb Reference to a pointer variable in which a pointer to * the respective dscb in the raw_vtoc will be returned. * @return 0 on success, otherwise one of the following error codes: * - EINVAL The address in *p refers to a record that is not a valid DSCB. * - ERANGE The cylinder, head, block address lies not within the VTOC. */ int lzds_raw_vtoc_get_dscb_from_cchhb(struct raw_vtoc *rv, cchhb_t *p, struct dscb **dscb) { long long index; char *record; errorlog_clear(rv->log); index = vtocindex_from_cchhb(rv, p); *dscb = NULL; if (!p->cc && !p->hh && !p->b) return 0; /* record zero is part of the track image, but not a dscb */ if (!p->b) return errorlog_add_message( &rv->log, NULL, EINVAL, "raw vtoc: DSCB address is empty\n"); if (index < rv->vtocrecno || index >= rv->vtocindexcount) return errorlog_add_message( &rv->log, NULL, ERANGE, "raw vtoc: DSCB address is outside VTOC\n"); record = rv->vtocindex[vtocindex_from_cchhb(rv, p)]; if (!record) return errorlog_add_message( &rv->log, NULL, EINVAL, "raw vtoc: DSCB address points to nonexistent DSCB\n"); *dscb = (struct dscb *)(record + sizeof(struct eckd_count)); return 0; } /** * @param[in] dasd The struct dasd that represents the device we want to read * the VTOC from. * @return 0 on success, otherwise one of the following error codes: * - ENOMEM Could not allocate internal structure due to lack of memory. * - EINVAL The volume label has not yet been read or it is not valid. * - EPROTO The VTOC data is not in a valid format. * - EIO Other I/O error */ int lzds_dasd_read_rawvtoc(struct dasd *dasd, struct raw_vtoc *rawvtoc) { unsigned long long vtoctrckno, vtocrecno; unsigned int vtoctrack_start, vtoctrack_end, vtocindexsize; unsigned int vtoc_rec_per_track; unsigned int i; int rc; char *record; struct eckd_count *ecount; format4_label_t *f4; unsigned long long rawvtocsize; volume_label_t *vlabel = NULL; char *trackdata = NULL; errorlog_clear(dasd->log); rc = lzds_dasd_get_vlabel(dasd, &vlabel); if (rc) { errorlog_add_message( &dasd->log, NULL, rc, "read VTOC: there is no volume label data available\n"); goto cleanup; } /* verify that we have a proper VOL1 label */ if (!is_vol1(vlabel->volkey) || !is_vol1(vlabel->vollbl)) { rc = EINVAL; errorlog_add_message( &dasd->log, NULL, rc, "read VTOC: volume label is not a VOL1 label\n"); goto cleanup; } /* The label contains the address of the first block of the vtoc. */ vtoctrckno = (unsigned long long) vtoc_get_cyl_from_cchhb(&vlabel->vtoc) * dasd->heads + vtoc_get_head_from_cchhb(&vlabel->vtoc); vtocrecno = vlabel->vtoc.b; /* We do not know how large the VTOC is, yet. So first, we read only * one track of the VTOC to access the format 4 DSCB in the first record * of the VTOC. */ trackdata = memalign(4096, RAWTRACKSIZE); /* page align for O_DIRECT */ if (!trackdata) { rc = ENOMEM; goto cleanup; } rc = dasd_read_tracks(dasd, vtoctrckno, vtoctrckno, trackdata); if (rc) { errorlog_add_message( &dasd->log, dasd->log, rc, "read VTOC: error when reading VTOC start\n"); goto cleanup; } record = NULL; f4 = NULL; i = 0; while (!buffer_get_next_record(trackdata, RAWTRACKSIZE, &record)) { if (i == vtocrecno) { f4 = (format4_label_t *)(record + 8); ecount = (struct eckd_count *)record; break; } ++i; } /* verify that the found record has the expected format */ if (!(f4 && (ecount->kl == 44) && (ecount->dl == 96) && (f4->DS4KEYCD[0] == 0x04) && (f4->DS4KEYCD[43] == 0x04) && (f4->DS4IDFMT == 0xf4))) { rc = EPROTO; errorlog_add_message( &dasd->log, NULL, rc, "read VTOC: could not find format 4 DSCB\n"); goto cleanup; } /* We have found a format 4 label at the position indicated by the * label. * How to determine the size of the VTOC: * - DS4VTOCE contains the VTOC extent, or in other words, lower and * uper boundary of the VTOC * * Searching through the VTOC tracks record by record is tedious, so * we build an array of pointers to the DSCBs, our VTOC index: * Number of entries in the index is the number of tracks times the * number of DSCBS per track plus one for record zero */ lzds_dasd_cchh2trk(dasd, &f4->DS4VTOCE.llimit, &vtoctrack_start); lzds_dasd_cchh2trk(dasd, &f4->DS4VTOCE.ulimit, &vtoctrack_end); vtoc_rec_per_track = (f4->DS4DEVCT.DS4DEVDT + 1); /* A VTOC consists of whole tracks, so the index size is number of * tracks multiplied by records per track */ vtocindexsize = (vtoctrack_end - vtoctrack_start + 1) * vtoc_rec_per_track; rawvtocsize = ((unsigned long long)vtoctrack_end - vtoctrack_start + 1) * RAWTRACKSIZE; f4 = NULL; record = NULL; free(trackdata); trackdata = memalign(4096, rawvtocsize); /* page align for O_DIRECT */ if (!trackdata) { rc = ENOMEM; goto cleanup; } /* read in the full VTOC from disk into memory */ rc = dasd_read_tracks(dasd, vtoctrack_start, vtoctrack_end, trackdata); if (rc) { errorlog_add_message( &dasd->log, dasd->log, rc, "read VTOC: error when reading VTOC\n"); goto cleanup; } rawvtoc->rawdata = trackdata; rawvtoc->rawdatasize = rawvtocsize; rawvtoc->vtoc_rec_per_track = vtoc_rec_per_track; rawvtoc->vtoctrackoffset = vtoctrack_start; rawvtoc->vtocrecno = vtocrecno; rawvtoc->vtocindexcount = vtocindexsize; /* Now parse all VTOC tracks in memory and create an index of * all records (including record 0) */ rawvtoc->vtocindex = malloc(sizeof(char *) * vtocindexsize); if (!rawvtoc->vtocindex) { rc = ENOMEM; goto cleanup; } memset(rawvtoc->vtocindex, 0, (sizeof(char *) * vtocindexsize)); record = NULL; f4 = NULL; i = 0; while (!buffer_get_next_record(trackdata, rawvtocsize, &record)) { /* verify that we do not get too many records */ if (i >= vtocindexsize) { rc = EPROTO; errorlog_add_message( &dasd->log, NULL, rc, "read VTOC: too many records in VTOC\n"); goto cleanup; } rawvtoc->vtocindex[i] = record; ++i; } return 0; cleanup: free(trackdata); return rc; } /** * @param[in] dasd The struct dasd that represents the device we want to read * the VTOC from. * @return 0 on success, otherwise one of the following error codes: * - ENOMEM Could not allocate internal structure due to lack of memory. * - EINVAL The volume label has not yet been read or it is not valid. * - EPROTO The VTOC data is not in a valid format. * - EIO Other I/O error */ int lzds_dasd_alloc_rawvtoc(struct dasd *dasd) { struct raw_vtoc *rawvtoc = NULL; int rc; /* cleanup the old rawvtoc structures before we read new ones */ rawvtoc = dasd->rawvtoc; dasd->rawvtoc = NULL; if (rawvtoc) { free(rawvtoc->rawdata); free(rawvtoc->vtocindex); free(rawvtoc); } rawvtoc = malloc(sizeof(*rawvtoc)); if (!rawvtoc) return ENOMEM; memset(rawvtoc, 0, sizeof(*rawvtoc)); rawvtoc->dasd = dasd; rc = lzds_dasd_read_rawvtoc(dasd, rawvtoc); if (rc) { free(rawvtoc->vtocindex); free(rawvtoc); } else { dasd->rawvtoc = rawvtoc; } return rc; } /** * @param[in] dasd Pointer to the struct dasd we want to get the raw_vtoc from. * @param[out] vtoc Reference to a pointer variable in which a pointer to * the previously read struct raw_vtoc will be returned. * @return 0 on success, otherwise one of the following error codes: * - EINVAL The VTOC has not yet been read. */ int lzds_dasd_get_rawvtoc(struct dasd *dasd, struct raw_vtoc **vtoc) { errorlog_clear(dasd->log); *vtoc = dasd->rawvtoc; if (!*vtoc) return EINVAL; else return 0; } /******************************************************************************/ /* HIGH level functions */ /******************************************************************************/ /** * @param[in] zdsroot Reference to struct zdsroot that the iterator will be * bound to. The iterator will traverse the data sets stored * in this zdsroot. * @param[out] it Reference to a pointer variable in which the newly allocated * structure will be returned. * @return 0 on success, otherwise one of the following error codes: * - ENOMEM Could not allocate structure due to lack of memory. */ int lzds_zdsroot_alloc_dsiterator(struct zdsroot *zdsroot, struct dsiterator **it) { *it = malloc(sizeof(struct dsiterator)); if (*it) { (*it)->dsi = NULL; (*it)->zdsroot = zdsroot; return 0; } return ENOMEM; } /** * @param[in] it Pointer to the struct dsiterator that is to be freed. */ void lzds_dsiterator_free(struct dsiterator *it) { free(it); } /** * @param[in] it Reference to the struct dsiterator we use to traverse the * data set list. * @param[out] ds Reference to a pointer variable in which the next * data set in the sequence will be returned. If there * is no next data set, this variable will be set to NULL. * @return 0 on success, otherwise one of the following error codes: * - EPERM The end of the list has been reached. There is no further dataset. */ int lzds_dsiterator_get_next_dataset(struct dsiterator *it, struct dataset **ds) { struct dataset *dstmp; if (!it->dsi) dstmp = util_list_start(it->zdsroot->datasetlist); else dstmp = util_list_next(it->zdsroot->datasetlist, it->dsi); *ds = dstmp; if (!dstmp) return EPERM; it->dsi = dstmp; return 0; } /** * @param[in] root Reference to struct zdsroot that holds the list of data * sets that this function shall search through. * @param[in] name Name of the data set. * @param[out] ds Reference to a pointer variable in which the found dataset * structure will be returned. If no data set was found, this * variable will be set to NULL * @return 0 on success, otherwise one of the following error codes: * - ENOMEM Could not internal structure due to lack of memory. * - ENOENT A dataset with the given name was not found. */ int lzds_zdsroot_find_dataset(struct zdsroot *root, const char *name, struct dataset **ds) { struct dsiterator *dsit; struct dataset *tempds; int rc; errorlog_clear(root->log); *ds = NULL; rc = lzds_zdsroot_alloc_dsiterator(root, &dsit); if (rc) return ENOMEM; while (!lzds_dsiterator_get_next_dataset(dsit, &tempds)) { if (!strcmp(tempds->name, name)) { *ds = tempds; break; } } lzds_dsiterator_free(dsit); if (!*ds) return ENOENT; return 0; } /** * @param[in] ds Reference to the struct dataset that the iterator will be * bound to. The iterator will traverse the members stored * in this data set. * @param[out] it Reference to a pointer variable in which the newly allocated * structure will be returned. * @return 0 on success, otherwise one of the following error codes: * - ENOMEM Could not allocate structure due to lack of memory. * - EINVAL Failed to allocate a memberiterator because the data set does * not support members (is not a PDS). */ int lzds_dataset_alloc_memberiterator(struct dataset *ds, struct memberiterator **it) { if (!ds->memberlist) { *it = NULL; return errorlog_add_message( &ds->log, NULL, EINVAL, "alloc memberiterator: this data set has no members\n"); } *it = malloc(sizeof(struct memberiterator)); if (*it) { (*it)->memberi = NULL; (*it)->ds = ds; return 0; } return ENOMEM; } /** * @param[in] it Pointer to the struct meberiterator that is to be freed. */ void lzds_memberiterator_free(struct memberiterator *it) { free(it); } /** * @param[out] it Reference to the struct memberiterator we use to traverse * the member list. * @param[out] member Reference to a pointer variable in which the next member * in the sequence will be returned. If there is no next * member, this variable will be set to NULL. * @return 0 on success, otherwise one of the following error codes: * - EPERM The end of the list has been reached. There is no further dasd. */ int lzds_memberiterator_get_next_member(struct memberiterator *it, struct pdsmember **member) { struct pdsmember *memtmp; if (!it->memberi) memtmp = util_list_start(it->ds->memberlist); else memtmp = util_list_next(it->ds->memberlist, it->memberi); *member = memtmp; if (!memtmp) return EPERM; it->memberi = memtmp; return 0; } /** * @brief Subroutine of raw_vtoc_get_datasetpart_from_dscb * * Check the validity of the extent and copy it to the extent array in the * datasetpart. * @param[in] extent Pointer to the extent that is to be copied. * @param[in] dsp The target datasetpart. * @return 0 on success, otherwise one of the following error codes: * - EPROTO The extent is not valid. */ static int copy_extent_to_datasetpart(extent_t *extent, struct datasetpart *dsp) { /* sanity check: if the extent is valid then make sure that seqno * will not cause us to go beyond the array limits */ if (extent->typeind && extent->seqno >= MAXEXTENTS) return EPROTO; if (extent->typeind) dsp->ext[extent->seqno] = *extent; return 0; } /** * @brief Subroutine of raw_vtoc_get_datasetpart_from_dscb */ static int raw_vtoc_add_extent_error_message(struct raw_vtoc *rv) { return errorlog_add_message( &rv->log, NULL, EPROTO, "vtoc: an extent descriptor is not valid \n"); } /** * @brief Subroutine of create_dataset_from_dscb * * This function copies the necessary data from a format 1/8 DSCB * into a given datasetpart structure. * @param[in] rv The raw_vtoc that f1 belongs to. * @param[in] f1 The f1/f8 DSCB that the datasetpart is based on. * @param[in] dsp The target datasetpart. * @return 0 on success, otherwise one of the following error codes: * - EPROTO Invalid data in the DSCB or dependent DSCBs. */ static int raw_vtoc_get_datasetpart_from_dscb(struct raw_vtoc *rv, format1_label_t *f1, struct datasetpart *dsp) { format3_label_t *f3; format9_label_t *f9; struct dscb *dscb; int rc, j; errorlog_clear(rv->log); memset(dsp, 0, sizeof(*dsp)); dsp->f1 = f1; /* Find the first format 3 DSCB that is chained format 1 or 8 DSCB. * In a format 8 dscb we will first have one or more format 9 * DSCBs that we need to pass over. */ rc = lzds_raw_vtoc_get_dscb_from_cchhb(rv, &f1->DS1PTRDS, &dscb); while (!rc && dscb && dscb->fmtid == 0xf9) { f9 = (format9_label_t *)dscb; rc = lzds_raw_vtoc_get_dscb_from_cchhb(rv, &f9->DS9PTRDS, &dscb); } if (rc) return errorlog_add_message( &rv->log, rv->log, EPROTO, "vtoc: format 9 DSCB chain not valid \n"); /* We may or may not have a format 3 DSCB */ f3 = (dscb && dscb->fmtid == 0xf3) ? (format3_label_t *)dscb : NULL; /* In any case we have three extents in the f1/8 label itself */ rc = copy_extent_to_datasetpart(&f1->DS1EXT1, dsp); if (rc) return raw_vtoc_add_extent_error_message(rv); rc = copy_extent_to_datasetpart(&f1->DS1EXT2, dsp); if (rc) return raw_vtoc_add_extent_error_message(rv); rc = copy_extent_to_datasetpart(&f1->DS1EXT3, dsp); if (rc) return raw_vtoc_add_extent_error_message(rv); /* now follow the f3 chain */ while (f3) { if (f3->DS3FMTID != 0xf3) return errorlog_add_message( &rv->log, rv->log, EPROTO, "vtoc: format 3 DSCB not valid \n"); for (j = 0; j < 4; ++j) { rc = copy_extent_to_datasetpart(&f3->DS3EXTNT[j], dsp); if (rc) return raw_vtoc_add_extent_error_message(rv); } for (j = 0; j < 9; ++j) { rc = copy_extent_to_datasetpart(&f3->DS3ADEXT[j], dsp); if (rc) return raw_vtoc_add_extent_error_message(rv); } rc = lzds_raw_vtoc_get_dscb_from_cchhb(rv, &f3->DS3PTRDS, (struct dscb **)&f3); if (rc) return errorlog_add_message( &rv->log, rv->log, EPROTO, "vtoc: format 3 DSCB reference not valid\n"); } return 0; } /** * @brief Subroutine of lzds_zdsroot_extract_datasets_from_dasd * * This functions takes the data of a format 1/8 label, fills in * a given struct dataset and creates exactly one dataset part. * In case of a multi volume data set this part may not be the the * first in the ds->dsp array, but is placed according to its * volume sequence number! * @param[in] dasd The dasd the data set belongs to. * @param[in] f1 The f1/f8 DSCB that the dataset(part) is based on. * @param[in] ds A dataset structure that will be filled with data, * in particular a data set part. * @return 0 on success, otherwise one of the following error codes: * - ENOMEM Could not allocate structure due to lack of memory. * - EPROTO Invalid data in the DSCB: An extent is not valid. */ static int create_dataset_from_dscb(struct dasd *dasd, format1_label_t *f1, struct dataset *ds) { struct datasetpart *dsp; char *end; int rc; int dspindex; errorlog_clear(dasd->log); memset(ds, 0, sizeof(*ds)); dsp = malloc(sizeof(*dsp)); if (!dsp) return ENOMEM; /* convert EBCDIC fixed length name into ascii 0-terminated string */ strncpy(ds->name, f1->DS1DSNAM, MAXDSNAMELENGTH - 1); vtoc_ebcdic_dec(ds->name, ds->name, MAXDSNAMELENGTH - 1); end = strchr(ds->name, ' '); if (end) *end = 0; rc = raw_vtoc_get_datasetpart_from_dscb(dasd->rawvtoc, f1, dsp); if (rc) { free(dsp); return errorlog_add_message( &dasd->log, dasd->rawvtoc->log, rc, "create data sets: get data set part failed for %s\n", ds->name); } dsp->dasdi = dasd; dspindex = f1->DS1VOLSQ - 1; if (dspindex < 0 || dspindex >= MAXVOLUMESPERDS) { free(dsp); return errorlog_add_message( &dasd->log, NULL, EPROTO, "create data sets: data set sequence number " "out of bounds failed for %s\n", ds->name); } ds->dsp[dspindex] = dsp; ds->dspcount = 1; /* Note: we cannot tell the difference between the first volume of * a multi volume data set and a single volume data set, * so the following is just a first assumption */ if (dspindex == 0) ds->iscomplete = 1; else ds->iscomplete = 0; return 0; } /** * @brief Subroutine of extract_members_from_track * * Take the information from a pds_member_entry, create a new pdsmember * and add it to the datasets memberlist * @param[in] ds The dataset that the new struct pdsmember will be added to. * @param[in] memberentry The PDS directory entry that describes the member. * @return 0 on success, otherwise one of the following error codes: * - ENOMEM Could not allocate structure due to lack of memory. */ static int dataset_add_member(struct dataset *ds, struct pds_member_entry *memberentry) { char name[9]; char *end; struct pdsmember *member; /* convert name to ascii and truncate trailing spaces */ strncpy(name, memberentry->name, 8); name[8] = 0; vtoc_ebcdic_dec(name, name, 8); end = strchr(name, ' '); if (end) *end = 0; member = malloc(sizeof(*member)); if (!member) return ENOMEM; memset(member, 0, sizeof(*member)); strcpy(member->name, name); member->track = memberentry->track; member->record = memberentry->record; member->is_alias = memberentry->is_alias; util_list_add_tail(ds->memberlist, member); return 0; } /** * @brief Helper function that removes and frees all elements in the * member list in a struct dataset. * * @param[in] ds The dataset whose memberlist is to be freed. */ static void dataset_free_memberlist(struct dataset *ds) { struct pdsmember *member, *next; if (!ds->memberlist) return; util_list_iterate_safe(ds->memberlist, member, next) { util_list_remove(ds->memberlist, member); errorlog_free(member->log); free(member); } util_list_free(ds->memberlist); ds->memberlist = NULL; } /** * @brief Helper function that just checks if the type of an extend * indicates that it contains user data or not. * * @param[in] ext The extent that gets evaluated. * @return 1 if the extent contains user data, 0 otherwise. */ static int extent_contains_userdata(extent_t *ext) { return ((ext->typeind == 0x01) || (ext->typeind == 0x81)); } /** * @brief Subroutine of dataset_member_analysis. * * This function parses one track of a PDS directory and adds all found * members to the dataset. A PDS directory may span more than one track. * The variable dirend is used to indicate the end of the directory. * * @note In case of an error there is no cleanup done for the data set. * * @param[in] trackdata The raw track that contains the PDS directory. * @param[in] ds The dataset the found members will be added to. * @param[out] dirend If the end of the directory is found, dirend is * set to 1, else it is 0. * @return 0 on success, otherwise one of the following error codes: * - ENOMEM Could not allocate structure due to lack of memory. * - EPROTO The track layout is not valid. */ static int extract_members_from_track(char *trackdata, struct dataset *ds, int *dirend) { char *record, *data; int r; struct eckd_count *ecount; int used_bytes, residual, user_data_size; struct pds_member_entry *member; int rc; *dirend = 0; record = NULL; r = 0; while (!buffer_get_next_record(trackdata, RAWTRACKSIZE, &record)) { /* jump over record zero */ if (r == 0) { ++r; continue; } data = record; ecount = (struct eckd_count *)data; /* sanity check: do key and data length match the format of * a directory record? */ if ((ecount->kl != PDS_DIR_KL) || (ecount->dl != PDS_DIR_DL)) return errorlog_add_message( &ds->log, NULL, EPROTO, "member analysis: directory record layout" " not valid, offset %lu\n", (unsigned long)ecount - (unsigned long)trackdata); data += sizeof(*ecount); /* compare key to directory end token */ if ((*(unsigned long long *)data) == ENDTOKEN) *dirend = 1; data += ecount->kl; /* First element in the data area are two bytes that denote how * may bytes of the data area are used for directory entries. * This number includes the first two bytes. */ used_bytes = (*(unsigned short *)data); residual = used_bytes - sizeof(unsigned short); data += sizeof(unsigned short); /* Loop over directory entries in record */ while (residual > 0) { /* A pseudo directory entry marks directory end */ if ((*(unsigned long long *)data) == ENDTOKEN) { *dirend = 1; /* should already be set */ break; } member = (struct pds_member_entry *)data; rc = dataset_add_member(ds, member); if (rc) return rc; /* A directory entry may contain a user data part * that follows the pds_member_entry structure. */ user_data_size = 2 * member->user_data_count; data += sizeof(*member) + user_data_size; residual -= (sizeof(*member) + user_data_size); } ++r; if (*dirend) break; } return 0; } /** * @brief Subroutine of lzds_zdsroot_extract_datasets_from_dasd. * * This function checks if a data set is a PDS, analyzes the PDS directory * and creates a corresponding list of struct pdsmember in the dataset. * * @param[in] ds The dataset that is to be analyzed and the found * members will be added to. * @return 0 on success, otherwise one of the following error codes: * - ENOMEM Could not allocate structure due to lack of memory. * - EPROTO The track layout is not valid. * - EINVAL An internal error happened. * - EIO An error happened while reading data from disk. */ static int dataset_member_analysis(struct dataset *ds) { char *trackdata; unsigned int extstarttrk, extendtrk, currenttrack; int j; int dirend; struct datasetpart *dsp; struct dasd *dasd; struct dasdhandle *dasdh; int rc, rc2; int issupported; errorlog_clear(ds->log); rc2 = 0; /* a partitioned data set has only one volume, so we only need dsp[0] */ dsp = ds->dsp[0]; /* if it is not a partitioned data set, do nothing */ if (!dsp || !(dsp->f1->DS1DSRG1 & 0x02)) return 0; /* do not do member analysis if we do not support the format (PDSE) */ lzds_dataset_get_is_supported(ds, &issupported); if (!issupported) return 0; dasd = dsp->dasdi; dataset_free_memberlist(ds); ds->memberlist = util_list_new(struct pdsmember, list); /* track buffer must be page aligned for O_DIRECT */ trackdata = memalign(4096, RAWTRACKSIZE); if (!trackdata) return ENOMEM; rc = lzds_dasd_alloc_dasdhandle(dasd, &dasdh); if (rc) goto out1; rc = lzds_dasdhandle_open(dasdh); if (rc) { errorlog_add_message( &ds->log, dasdh->log, rc, "member analysis: could not open dasdhandle\n"); goto out2; } dirend = 0; /* loop over all extents in dataset*/ for (j = 0; j < MAXEXTENTS; ++j) { if (!extent_contains_userdata(&dsp->ext[j])) continue; lzds_dasd_cchh2trk(dasd, &dsp->ext[j].llimit, &extstarttrk); lzds_dasd_cchh2trk(dasd, &dsp->ext[j].ulimit, &extendtrk); currenttrack = extstarttrk; /* loop over tracks in extent */ while (currenttrack <= extendtrk) { rc = lzds_dasdhandle_read_tracks_to_buffer( dasdh, currenttrack, currenttrack, trackdata); if (rc) { errorlog_add_message( &ds->log, dasdh->log, rc, "member analysis: read error\n"); goto out4; } rc = extract_members_from_track(trackdata, ds, &dirend); if (rc) { errorlog_add_message( &ds->log, ds->log, rc, "member analysis: error " "extracting members from track %u\n", currenttrack); goto out4; } currenttrack++; if (dirend) break; } if (dirend) break; } rc = 0; goto out3; out4: dataset_free_memberlist(ds); out3: rc2 = lzds_dasdhandle_close(dasdh); /* report close error only if we had no read error */ if (rc2 && !rc) { errorlog_add_message( &ds->log, dasdh->log, rc, "member analysis: could not close dasdhandle\n"); rc = rc2; } out2: lzds_dasdhandle_free(dasdh); out1: free(trackdata); rc = rc ? rc : rc2; return rc; } /** * @brief Subroutine of zdsroot_merge_dataset * * Merge two dataset structures that are two halves of a multi volume data set. * All datasetparts of the second dataset are copied to the first dataset. * * @param[in] baseds The dataset that the data will be merged into. * @param[in] newds The dataset that will be merged with baseds. * This strucure can be freed after the merge, but do not * free the data set parts it contained, as those belong * to baseds now. * @return 0 on success, otherwise one of the following error codes: * - EPROTO The data is not mergable because of conflicting entries. */ static int dataset_merge_dataset(struct dataset *baseds, struct dataset *newds) { int k, l, dspcount; for (k = 0; k < MAXVOLUMESPERDS; ++k) { /* if both datasets have a part in position k, * then something is wrong */ if (baseds->dsp[k] && newds->dsp[k]) return errorlog_add_message( &baseds->log, NULL, EPROTO, "merge dataset: " "part %d was previously found on device %s\n", k, baseds->dsp[k]->dasdi->device); /* if the new data set has a part that is not present in the * base data set, than copy the dsp pointer to the base */ if (!baseds->dsp[k] && newds->dsp[k]) { /* Each format 1/8 DSCB of a part in a multi volume data * set has a reference to the volume serial of the first * volume. Need to verify that the new data set parts * refer to the correct volume serial in f1->DS1DSSN. * Since dsp[0] may not be set yet, we loop over the * base dsp array until we find an entry. */ for (l = 0; l < MAXVOLUMESPERDS; ++l) { if (!baseds->dsp[l]) continue; if (memcmp(baseds->dsp[l]->f1->DS1DSSN, newds->dsp[k]->f1->DS1DSSN, MAXVOLSER)) return errorlog_add_message( &baseds->log, NULL, EPROTO, "merge dataset: part %d has incompatible base volume serial\n", k); else break; } baseds->dsp[k] = newds->dsp[k]; baseds->dspcount++; } } /* check for completeness: * If element (dspcount - 1) exists and is the last part in a multi * volume data set, then all other parts must have been found as well. */ dspcount = baseds->dspcount; if (baseds->dsp[dspcount - 1] && (baseds->dsp[dspcount - 1]->f1->DS1DSIND & 0x80)) baseds->iscomplete = 1; else baseds->iscomplete = 0; /* The last statement is only true for a correct multi volume data set. * Since the data on the DASDs may be incorrect and we will rely later * on the fact that the first dspcount elements of the dsp array are * valid, we must make sure that they are all filled. */ if (baseds->iscomplete) for (l = 0; l < baseds->dspcount; ++l) if (!baseds->dsp[l]) { baseds->iscomplete = 0; return errorlog_add_message( &baseds->log, NULL, EPROTO, "merge dataset: inconsistent data set" " part list at index %d\n", l); } return 0; } /** * @brief Subroutine of lzds_zdsroot_extract_datasets_from_dasd * * Takes the data from newds and merges it with a matching dataset in * root. If no matching dataset exists yet, a new struct dataset is * created, so that the caller of this function can release newds in * any case. * It is important to note that while newds is just a temporary * structure that can be released after the function returns, the * elements and structures that are contained by newds (e.g the * datasetparts) are transferred to the struct dataset in root and must * not be released. * * @param[in] root The zdsroot that the dataset will be merged into. * @param[in] newds The dataset that will be merged. * @return 0 on success, otherwise one of the following error codes: * - ENOMEM Could not allocate internal structure due to lack of memory. * - EPROTO The data is not mergable because of conflicting entries. */ static int zdsroot_merge_dataset(struct zdsroot *root, struct dataset *newds) { struct dataset *rootds; int rc; /* first, try to find a matching data set in the old list */ rc = lzds_zdsroot_find_dataset(root, newds->name, &rootds); if (!rc) { /* match found */ rc = dataset_merge_dataset(rootds, newds); if (rc) return errorlog_add_message( &root->log, rootds->log, rc, "merge dataset: " "merge with existing data set failed\n"); } else if (rc == ENOENT) { /* no match found */ rootds = malloc(sizeof(*rootds)); if (!rootds) return ENOMEM; memcpy(rootds, newds, sizeof(*rootds)); util_list_add_tail(root->datasetlist, rootds); } else return rc; return 0; } /** * This function finds all data set descriptions in the VTOC of the * dasd and creates respective struct dataset representations. These * struct dataset are stored in the zdsroot and can later be traversed * using a dsiterator. In case that it finds a dataset that is * already present in the zdsroot, it verifies that both are parts of * the same multivolume data set and then merges the new data with the * existing struct dataset. If the conflicting data sets are indeed * individual data sets and not parts of a single one, the function * returns an error. * * @param[in] root The zdsroot that the dataset will be merged into. * @param[in] dasd The datasets found in this dasd will be merged. * @return 0 on success, otherwise one of the following error codes: * - ENOMEM Could not allocate structure due to lack of memory. * - EPROTO The data is not mergable because of conflicting entries, * or invalid data in the VTOC of the dasd. */ int lzds_zdsroot_extract_datasets_from_dasd(struct zdsroot *root, struct dasd *dasd) { format1_label_t *f1; struct dscb *dscb; struct dscbiterator *it; int rc; struct dataset tmpds; int i; errorlog_clear(root->log); memset(&tmpds, 0, sizeof(tmpds)); rc = lzds_raw_vtoc_alloc_dscbiterator(dasd->rawvtoc, &it); if (rc) return ENOMEM; while (!lzds_dscbiterator_get_next_dscb(it, &dscb)) { if (dscb->fmtid == 0xf1 || dscb->fmtid == 0xf8) { f1 = (format1_label_t *)dscb; rc = create_dataset_from_dscb(dasd, f1, &tmpds); if (rc) { errorlog_add_message( &root->log, dasd->log, rc, "extract data sets: " "creating dataset failed for %s\n", dasd->device); break; } rc = dataset_member_analysis(&tmpds); if (rc) { errorlog_add_message( &root->log, tmpds.log, rc, "extract data sets: " "member analysis failed for %s\n", tmpds.name); break; } rc = zdsroot_merge_dataset(root, &tmpds); if (rc) { errorlog_add_message( &root->log, root->log, rc, "extract data sets: " "merge dataset failed for %s\n", tmpds.name); break; } } } if (rc) { dataset_free_memberlist(&tmpds); for (i = 0; i < MAXVOLUMESPERDS; ++i) free(tmpds.dsp[i]); errorlog_free(tmpds.log); } lzds_dscbiterator_free(it); return rc; } /** * @brief Subroutine of lzds_dataset_get_size_in_tracks * * Computes the number of tracks in a given extent. * Returns 0 for anything but user data. * @param[in] ext The extent we want to know the size of. * @param[in] dasd The dasd that the extent is located on. * @return Number of tracks the extent contains */ static unsigned int get_extent_size_in_tracks(extent_t *ext, struct dasd *dasd) { unsigned int starttrck, endtrck; if (!extent_contains_userdata(ext)) return 0; lzds_dasd_cchh2trk(dasd, &ext->llimit, &starttrck); lzds_dasd_cchh2trk(dasd, &ext->ulimit, &endtrck); return endtrck - starttrck + 1; } /** * @param[in] ds The dataset we we want to know the size of. * @param[out] tracks Reference to a return buffer for the number of tracks. */ void lzds_dataset_get_size_in_tracks(struct dataset *ds, unsigned long long *tracks) { unsigned long long sumtracks; int i, j; *tracks = 0; sumtracks = 0; for (i = 0; i < MAXVOLUMESPERDS; ++i) if (ds->dsp[i]) for (j = 0; j < MAXEXTENTS; ++j) sumtracks += get_extent_size_in_tracks( &ds->dsp[i]->ext[j], ds->dsp[i]->dasdi); *tracks = sumtracks; } /** * @param[in] member The PDS member we want to know the name of. * @param[out] name Reference to a pointer variable in which a pointer to * the name string will be returned. */ void lzds_pdsmember_get_name(struct pdsmember *member, char **name) { *name = member->name; } /** * @param[in] ds The dataset we want to know the name of. * @param[out] name Reference to a pointer variable in which a pointer to * the name string will be returned. */ void lzds_dataset_get_name(struct dataset *ds, char **name) { *name = ds->name; } /** * @param[in] ds Is this dataset a PDS? * @param[out] ispds Reference to a pointer variable in which * 1 (true) or 0 (false) is returned. */ void lzds_dataset_get_is_PDS(struct dataset *ds, int *ispds) { if (ds->dsp[0]->f1->DS1DSRG1 & 0x02) /* is PDS */ *ispds = 1; else *ispds = 0; } /** * The returned DSCB belongs always to the first volume of a data set. * * @param[in] ds The dataset we want to know the DSCB of. * @param[out] f1 Reference to a pointer variable in which a pointer to * the format 1 DSCB will be returned. */ void lzds_dataset_get_format1_dscb(struct dataset *ds, format1_label_t **f1) { *f1 = ds->dsp[0]->f1; } /** * @param[in] ds Is this dataset complete? * @param[out] iscomplete Reference to a pointer variable in which * 1 (true) or 0 (false) is returned. */ void lzds_dataset_get_is_complete(struct dataset *ds, int *iscomplete) { *iscomplete = ds->iscomplete; } /** * @param[in] ds Is this dataset supported? * @param[out] issupported Reference to a pointer variable in which * 1 (true) or 0 (false) is returned. */ void lzds_dataset_get_is_supported(struct dataset *ds, int *issupported) { int complete, org_supported, format_supported, not_ext_fmt; char DS1RECFM; if (!ds->dsp[0]) { *issupported = 0; return; } /* do we have all parts of the data set? */ lzds_dataset_get_is_complete(ds, &complete); /* is this a supported organisation (PS or PDS)?*/ org_supported = 0; if ((ds->dsp[0]->f1->DS1DSRG1 & 0x40) || /* PS */ (ds->dsp[0]->f1->DS1DSRG1 & 0x02)) /* PDS */ org_supported = 1; /* extended format datasets are not supported */ not_ext_fmt = 0; if (!(ds->dsp[0]->f1->DS1SMSFG & 0x0C)) not_ext_fmt = 1; /* fixed, variable or undefined length records are supported */ DS1RECFM = ds->dsp[0]->f1->DS1RECFM; format_supported = 0; if (DS1RECFM & 0xC0) format_supported = 1; /* track overflow (legacy) is not supported */ if ((DS1RECFM & 0x20)) format_supported = 0; /* all other RECFM flags are modifiers of the above and are supported */ *issupported = complete && org_supported && format_supported && not_ext_fmt; return; } /** * @param[in] ds The dataset that is searched for the member. * @param[in] membername The name of the member (ASCII string). * @param[out] member Reference to a pointer variable in which the found * pdsmember is returned. If no member is found, this * is set to NULL. * @return 0 on success, otherwise one of the following error codes: * - ENOMEM Could not allocate internal structure due to lack of memory. * - ENOENT No matching member was found. */ int lzds_dataset_get_member_by_name(struct dataset *ds, char *membername, struct pdsmember **member) { struct memberiterator *it; struct pdsmember *tmpmember; int rc; errorlog_clear(ds->log); *member = NULL; rc = lzds_dataset_alloc_memberiterator(ds, &it); if (rc) return ENOMEM; while (!lzds_memberiterator_get_next_member(it, &tmpmember)) { if (!strcmp(tmpmember->name, membername)) { *member = tmpmember; break; } } lzds_memberiterator_free(it); if (!*member) return ENOENT; return 0; } /** * @param[in] dsh Pointer to structure that is to be freed. */ void lzds_dshandle_free(struct dshandle *dsh) { int i; if (!dsh) return; for (i = 0; i < MAXVOLUMESPERDS; ++i) if (dsh->dasdhandle[i]) lzds_dasdhandle_free(dsh->dasdhandle[i]); free(dsh->databuffer); free(dsh->rawbuffer); if (dsh->seekbuf) free(dsh->seekbuf); errorlog_free(dsh->log); free(dsh); } /** * @param[in] ds The dataset we want to read from. * @param[in] tracks_per_frame The number of tracks that the internal buffers * can hold. If 0, then the default value 128 is used. * @param[out] dsh Reference to a pointer variable which will be used * to store the new dshandle. * @return 0 on success, otherwise one of the following error codes: * - ENOMEM Could not allocate structure due to lack of memory. */ int lzds_dataset_alloc_dshandle(struct dataset *ds, unsigned int tracks_per_frame, struct dshandle **dsh) { struct dshandle *dshtmp; int i, rc; dshtmp = malloc(sizeof(*dshtmp)); if (!dshtmp) return ENOMEM; memset(dshtmp, 0, sizeof(*dshtmp)); for (i = 0; i < ds->dspcount; ++i) { rc = lzds_dasd_alloc_dasdhandle(ds->dsp[i]->dasdi, &dshtmp->dasdhandle[i]); if (rc) { lzds_dshandle_free(dshtmp); return rc; } } if (tracks_per_frame) dshtmp->tracks_per_frame = tracks_per_frame; else dshtmp->tracks_per_frame = TRACK_BUFFER_DEFAULT; dshtmp->rawbufmax = dshtmp->tracks_per_frame * RAWTRACKSIZE; /* track buffer must be page aligned for O_DIRECT */ dshtmp->rawbuffer = memalign(4096, dshtmp->rawbufmax); if (!dshtmp->rawbuffer) { lzds_dshandle_free(dshtmp); return ENOMEM; } dshtmp->databufmax = dshtmp->tracks_per_frame * MAXRECSIZE; dshtmp->databuffer = malloc(dshtmp->databufmax); if (!dshtmp->databuffer) { lzds_dshandle_free(dshtmp); return ENOMEM; } dshtmp->ds = ds; *dsh = dshtmp; return 0; } /** * The number of user data bytes per track is not predictable as record * sizes and number of records per track may vary. Seeking forward will * always require us to read all the data between the current position * and the seek target. To improve performance of seeking backwards * we can buffer previous positions in the data set. * For a given seek buffer size and the known number of tracks of the * data set, we can compute how many track frames we need to skip if * we and to store track frames in regular intervals. * * @param[in] dsh The dshandle we want to modify. * @param[in] seek_buffer_size The maximum number of bytes to be allocated * for the seek buffer. * @return 0 on success, otherwise one of the following error codes: * - ENOMEM Could not allocate structure due to lack of memory. */ int lzds_dshandle_set_seekbuffer(struct dshandle *dsh, unsigned long long seek_buffer_size) { unsigned long long totaltracks; size_t entries, frames; unsigned int extents, skip; struct dataset *ds; int i, j; unsigned long long buf_count; errorlog_clear(dsh->log); if (dsh->seekbuf) free(dsh->seekbuf); dsh->seekbuf = NULL; dsh->seek_count = 0; dsh->seek_current = 0; dsh->skip = 0; if (!seek_buffer_size) return 0; ds = dsh->ds; lzds_dataset_get_size_in_tracks(ds, &totaltracks); /* compute the total number of extents */ extents = 0; for (i = 0; i < ds->dspcount; ++i) for (j = 0; j < MAXEXTENTS; ++j) if (ds->dsp[i]->ext[j].typeind != 0x00) ++extents; entries = seek_buffer_size / sizeof(struct seekelement); /* track frames at the end of an extent may be shorter, * increasing the maximum number of frames we need to read */ frames = (totaltracks / dsh->tracks_per_frame) + 1 + extents; skip = (frames / entries) + 1; buf_count = (frames / skip) + 1; dsh->seekbuf = malloc(buf_count * sizeof(struct seekelement)); if (!dsh->seekbuf) return ENOMEM; memset(dsh->seekbuf, 0, buf_count * sizeof(struct seekelement)); dsh->seek_count = buf_count; dsh->skip = skip; return 0; } /** * If dsh points to a partitioned data set, the library needs to know * which member of that PDS should be read. So this function must be * called before lzds_dshandle_open. This setting cannot be changed * for open dsh, so this function must not be used after * lzds_dshandle_open, unless the dsh has been closed with * lzds_dsh_close again. * * @pre The dsh must not be open when this function is called. * * @param[in] dsh The dshandle we want to modify. * @param[in] membername The name of the member that shall be read via * this handle. * @return 0 on success, otherwise one of the following error codes: * - ENOMEM Could not allocate internal structure due to lack of memory. * - ENOENT No matching member was found. * - EBUSY The handle is already open. * - EINVAL The data set is not a PDS. */ int lzds_dshandle_set_member(struct dshandle *dsh, char *membername) { int ispds, rc; struct pdsmember *member; errorlog_clear(dsh->log); if (dsh->is_open) return errorlog_add_message( &dsh->log, NULL, EBUSY, "dshandle: cannot set member while handle is open\n"); dsh->member = NULL; lzds_dataset_get_is_PDS(dsh->ds, &ispds); if (!ispds) return errorlog_add_message( &dsh->log, NULL, EINVAL, "dshandle: cannot set member, not a PDS\n"); rc = lzds_dataset_get_member_by_name(dsh->ds, membername, &member); if (rc) return errorlog_add_message( &dsh->log, NULL, rc, "dshandle: could not find member %s in dataset %s\n", membername, dsh->ds->name); dsh->member = member; return 0; } /** * @param[in] dsh The dshandle that we want to know the member of. * @param[out] member Reference to a pointer variable in which the found * pdsmember is returned. If no member has been set * before, this is set to NULL. */ void lzds_dshandle_get_member(struct dshandle *dsh, struct pdsmember **member) { *member = dsh->member; } /** * @pre The dsh must not be open when this function is called. * * @param[in] dsh The dshandle we want to modify. * @param[in] keepRDW Set this to 1 to enable the keep RDW feature or * 0 to disable it. * @return 0 on success, otherwise one of the following error codes: * - EBUSY The handle is already open. */ int lzds_dshandle_set_keepRDW(struct dshandle *dsh, int keepRDW) { errorlog_clear(dsh->log); if (dsh->is_open) return errorlog_add_message( &dsh->log, NULL, EBUSY, "dshandle: cannot set RDW while handle is open\n"); dsh->keepRDW = keepRDW; return 0; } /** * @pre The dsh must not be open when this function is called. * * @param[in] dsh The dshandle we want to modify. * @param[in] iconv_t The iconv handle for codepage conversion. * * @return 0 on success, otherwise one of the following error codes: * - EBUSY The handle is already open. */ int lzds_dshandle_set_iconv(struct dshandle *dsh, iconv_t *iconv) { errorlog_clear(dsh->log); if (dsh->is_open) return errorlog_add_message( &dsh->log, NULL, EBUSY, "dshandle: cannot set iconv while handle is open\n"); /* * if conversion is enabled the returned data might in worst case * be 4 times the size of the input buffer. So realloc the buffer. * If for whatever very unlikely reason the converted size is still * larger the conversion will fail. */ if (iconv) { dsh->databufmax *= 4; dsh->databuffer = util_realloc(dsh->databuffer, dsh->databufmax); } dsh->iconv = iconv; return 0; } /** * @param[in] dsh The dshandle that we want to know the member of. * @param[out] keepRDW Reference to a variable in which the previously * set keepRDW value is returned. */ void lzds_dshandle_get_keepRDW(struct dshandle *dsh, int *keepRDW) { *keepRDW = dsh->keepRDW; } /** * @brief Helper function that initializes the given handle so that it * points to the beginning of the dataset or member. * * @param[in] dsh The dshandle that keeps track of the I/O operations. * @return 0 on success, otherwise one of the following error codes: * - EPROTO The dataset data is inconsistent. */ static int initialize_buffer_positions_for_first_read(struct dshandle *dsh) { unsigned long long tracksum, extentsize; unsigned int starttrck, endtrck; int j; /* make sure that read knows that we have no ready data in our buffer */ dsh->bufpos = 0; dsh->databufsize = 0; dsh->databufoffset = 0; dsh->eof_reached = 0; /* we need to set the bufendtrk and sequence number so, * that the current track buffer seems to end with the * track that comes before the first track of the * data set or member */ /* When we read the first track frame this will be incremented to 0 */ dsh->frameno = -1; /* We allways start with data set part 0. Partitioned * data sets have only one part, so this correct for * both partitioned and non partitioned data sets. */ dsh->dsp_no = 0; /* for a non partitioned data set we just need to set the * extentsequence number to -1 so read will start with the * first track of extent number 0 */ if (!dsh->member) { dsh->ext_seq_no = -1; dsh->bufstarttrk = 0; dsh->bufendtrk = 0; dsh->extstarttrk = 0; dsh->extendtrk = 0; return 0; } /* sanity check: a partitioned data set cannot be a multi volume data * set. */ if (dsh->ds->dspcount != 1) return errorlog_add_message( &dsh->log, NULL, EPROTO, "initialize read buffer: dataset %s is inconsistent," " PDS must not span more than one volume\n", dsh->ds->name); /* For a partitioned data set we need to find the correct start * track and point the current buffer just before it. * As we always need to read full tracks, any additional * record offset will be set explicitly and handled during * track interpretation. */ dsh->startrecord = dsh->member->record; /* member->track is an offset based on the start of the data set * I will have to add up extents until I have got the right number * of tracks */ tracksum = 0; /* Note: No need to loop over all data set parts, a PDS has only one */ for (j = 0; j < MAXEXTENTS; ++j) { if (!extent_contains_userdata(&dsh->ds->dsp[0]->ext[j])) continue; lzds_dasd_cchh2trk(dsh->ds->dsp[0]->dasdi, &dsh->ds->dsp[0]->ext[j].llimit, &starttrck); lzds_dasd_cchh2trk(dsh->ds->dsp[0]->dasdi, &dsh->ds->dsp[0]->ext[j].ulimit, &endtrck); extentsize = endtrck - starttrck + 1; /* If offset in the extent (member->track - tracksum) == 0, * then we must set the dsh buffer to the end of the previous * extent, so that rdf_read will start with the first track * of the next extent. * However, since rdf_read checks for bufendtrk < extendtrk * we can set both to 0 and do not need a special case for the * first extend. */ if (dsh->member->track == tracksum) { dsh->ext_seq_no = j - 1; dsh->bufendtrk = 0; dsh->extendtrk = 0; break; } /* If the offset is within the current extent an not the * special case above, then we can need to adjust the dsh so, * as if we have just already read data up to the track before * our target track */ if (dsh->member->track < tracksum + extentsize) { dsh->ext_seq_no = j; dsh->extstarttrk = starttrck; dsh->extendtrk = endtrck; dsh->bufstarttrk = dsh->extstarttrk; dsh->bufendtrk = dsh->bufstarttrk + (dsh->member->track - tracksum) - 1; break; } tracksum += extentsize; } return 0; } /** * @param[in] dsh The dshandle that keeps track of the I/O operations. */ void lzds_dshandle_close(struct dshandle *dsh) { int i; for (i = 0; i < MAXVOLUMESPERDS; ++i) if (dsh->dasdhandle[i]) lzds_dasdhandle_close(dsh->dasdhandle[i]); free(dsh->convbuffer); free(dsh->iconv); dsh->is_open = 0; } #ifdef HAVE_CURL struct response_data { char *session_ref; unsigned long statuscode; }; static size_t parse_response_callback(void *data, size_t size, size_t member, void *target) { struct response_data *response = target; if (strstr(data, "HTTP/1.1 500 Internal Server Error")) { response->statuscode = 500; } else if (strstr(data, "HTTP/1.1 200 OK")) { response->statuscode = 200; } else sscanf(data, "X-IBM-Session-Ref: %m[^\n]\n", &response->session_ref); return size*member; } static size_t write_discard_callback(void *UNUSED(data), size_t size, size_t member, void *UNUSED(target)) { /* do nothing just pretend all data has been processed */ return size*member; } CURL *lzds_prepare_curl(char *url) { CURL *curl; curl = curl_easy_init(); if (!curl) return NULL; curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY); curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 0L); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_discard_callback); return curl; } /** * Ping the z/OSMS REST server. * Used to check if the server is responding and accessible and to prevent * the ENQ from timing out. If not used it would be automatically released * after 10 minutes. * * @param[in] dsh The dshandle that keeps track of the I/O operations. * server The URL to the z/OSMF REST services * @return 1 on success, 0 otherwise */ int lzds_rest_ping(struct dshandle *dsh, char *server) { struct curl_slist *list = NULL; char *release; CURLcode res; size_t size; CURL *curl; char *url; url = util_strcat_realloc(NULL, server); url = util_strcat_realloc(url, "restfiles/ping"); curl = lzds_prepare_curl(url); if (!curl) { free(url); return 0; } list = curl_slist_append(list, "X-CSRF-ZOSMF-HEADER: none"); if (dsh && dsh->session_ref) { size = sizeof("X-IBM-Session-Ref: ") + strlen(dsh->session_ref); release = util_zalloc(size); snprintf(release, size, "X-IBM-Session-Ref: %s", dsh->session_ref); list = curl_slist_append(list, release); } curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); res = curl_easy_perform(curl); curl_slist_free_all(list); curl_easy_cleanup(curl); if (res == CURLE_OK) { free(url); return 1; } fprintf(stderr, "URL: %s\n", url); fprintf(stderr, "Error: %s\n", curl_easy_strerror(res)); free(url); return 0; } /** * Mark the dataset as in use for z/OS. * Use z/OSMF REST services to read a small amount of data and get an exclusive * ENQ that prevents z/OS applications from writing to the dataset in parallel * until the ENQ is released. * * @param[in] dsh The dshandle that keeps track of the I/O operations. * server The URL to the z/OSMF REST services * @return 0 on success, otherwise one of the following error codes: * - ENOTSUP Unable to setup curl and therefore no further access possible. * - EPERM ENQ not obtained and therefore access is not allowed. */ int lzds_rest_get_enq(struct dshandle *dsh, char *server) { struct curl_slist *list = NULL; struct response_data response; int first_run; CURLcode res; CURL *curl; char *url; int rc; url = util_strcat_realloc(NULL, server); url = util_strcat_realloc(url, "restfiles/ds/"); url = util_strcat_realloc(url, dsh->ds->name); memset(&response, 0, sizeof(response)); /* * in the first run provide a range statement to read only 1 record of * the dataset to get an ENQ. * For the unlikely case that the dataset is empty * "500 Internal Server Error" will be returned. * If this is the case give it a second try without a range statement */ first_run = 1; list = curl_slist_append(list, "X-IBM-Record-Range: 0-1"); retry: rc = 1; curl = lzds_prepare_curl(url); if (!curl) { free(url); return errorlog_add_message( &dsh->log, NULL, ENOTSUP, "curl handle not established for dataset %s\n", dsh->ds->name); } curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, parse_response_callback); curl_easy_setopt(curl, CURLOPT_HEADERDATA, &response); list = curl_slist_append(list, "X-CSRF-ZOSMF-HEADER: none"); list = curl_slist_append(list, "X-IBM-Obtain-ENQ: EXCLU"); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); res = curl_easy_perform(curl); if (res != CURLE_OK) { rc = errorlog_add_message(&dsh->log, NULL, ECONNREFUSED, "Error: %s\n", curl_easy_strerror(res)); } else { if (first_run && response.statuscode == 500) { curl_slist_free_all(list); curl_easy_cleanup(curl); first_run = 0; list = NULL; goto retry; } /* expect that the callback function found a reference string, double check */ if (response.statuscode == 200 && response.session_ref) { dsh->session_ref = response.session_ref; rc = 0; } else { rc = errorlog_add_message( &dsh->log, NULL, EPERM, "no session ref obtained for dataset %s rest rc %ld\n", dsh->ds->name, response.statuscode); } } free(url); curl_slist_free_all(list); curl_easy_cleanup(curl); return rc; } /** * Mark the dataset as no longer in use for z/OS. * Use z/OSMF REST services to read a small amount of data and release the exclusive * ENQ that was previously obtained. * * @param[in] dsh The dshandle that keeps track of the I/O operations. * server The URL to the z/OSMF REST services * @return 0 on success, otherwise one of the following error codes: * - ENOTSUP Unable to release the ENQ. */ int lzds_rest_release_enq(struct dshandle *dsh, char *server) { struct curl_slist *list = NULL; struct response_data response; char *release; int first_run; CURLcode res; CURL *curl; char *url; if (!dsh->session_ref) { fprintf(stderr, "No ENQ to release.\n"); return 0; } url = util_strcat_realloc(NULL, server); url = util_strcat_realloc(url, "restfiles/ds/"); url = util_strcat_realloc(url, dsh->ds->name); release = util_strcat_realloc(NULL, "X-IBM-Session-Ref: "); release = util_strcat_realloc(release, dsh->session_ref); memset(&response, 0, sizeof(response)); /* * in the first run provide a range statement to read only 1 record of * the dataset to release the ENQ. * For the unlikely case that the dataset is empty * "500 Internal Server Error" will be returned. * If this is the case give it a second try without a range statement */ first_run = 1; list = curl_slist_append(list, "X-IBM-Record-Range: 0-1"); retry: curl = lzds_prepare_curl(url); if (!curl) { free(url); free(release); return errorlog_add_message( &dsh->log, NULL, ENOTSUP, "curl handle not established for dataset %s\n", dsh->ds->name); } list = curl_slist_append(list, "X-CSRF-ZOSMF-HEADER: none"); list = curl_slist_append(list, "X-IBM-Release-ENQ: true"); list = curl_slist_append(list, release); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, parse_response_callback); curl_easy_setopt(curl, CURLOPT_HEADERDATA, &response); res = curl_easy_perform(curl); if (res != CURLE_OK) { errorlog_add_message(&dsh->log, NULL, ENOTSUP, "Error: %s\n", curl_easy_strerror(res)); } else if (first_run && response.statuscode == 500) { curl_slist_free_all(list); curl_easy_cleanup(curl); first_run = 0; list = NULL; goto retry; } curl_slist_free_all(list); curl_easy_cleanup(curl); free(dsh->session_ref); free(release); free(url); dsh->session_ref = NULL; return res; } #endif /* HAVE_CURL */ /** * This makes the data set context ready for read operations. * All settings on the dsh must be done before it is opened. * @pre For a partitioned data set a member must be set before * this function is called. * * @param[in] dsh The dshandle that keeps track of the I/O operations. * @return 0 on success, otherwise one of the following error codes: * - ENOTSUP The dataset is of a type that is not supported. * - EINVAL Tried to open a PDS without setting a member before.. * - EIO Could not open underlying device. */ int lzds_dshandle_open(struct dshandle *dsh) { int i, rc; int ispds, issupported; /* sanity check: Open will fail if the data set type is not supported. * We do this check here and not during dshandle creation, as it may * depend on settings on the dshandle that the user has to make * between creation and open. */ errorlog_clear(dsh->log); lzds_dataset_get_is_supported(dsh->ds, &issupported); if (!issupported) return errorlog_add_message( &dsh->log, NULL, ENOTSUP, "data set open: data set %s is not supported\n", dsh->ds->name); lzds_dataset_get_is_PDS(dsh->ds, &ispds); if (ispds && !dsh->member) return errorlog_add_message( &dsh->log, NULL, EINVAL, "data set open: a member must be set" " before PDS %s can be opened\n", dsh->ds->name); rc = initialize_buffer_positions_for_first_read(dsh); if (rc) return errorlog_add_message( &dsh->log, dsh->log, rc, "data set open: error when initializing buffers" " for data set %s\n", dsh->ds->name); for (i = 0; i < dsh->ds->dspcount; ++i) { rc = lzds_dasdhandle_open(dsh->dasdhandle[i]); if (rc) { errorlog_add_message( &dsh->log, dsh->dasdhandle[i]->log, rc, "data set open: error opening DASD " "for data set %s\n", dsh->ds->name); lzds_dshandle_close(dsh); return rc; } } if (dsh->iconv) dsh->convbuffer = util_zalloc(dsh->databufmax); dsh->is_open = 1; return 0; } /** * @brief subroutine of parse_fixed_record for codepage conversion * * Converts the provided data from one codepage to another using iconv. * Stores converted data directly in the target buffer. * Adds a linebreak at the end of each record to end the line. * Also remove trailing spaces. * * @param[in] dsh The dshandle that keeps track of the I/O operations. * @param[in] rec Pointer to the record buffer. * @param[in] targetdata Pointer to the data buffer. * @return Number of copied data bytes on success, * otherwise one of the following (negative) error codes: * - -EPROTO The record is malformed. */ static ssize_t convert_fixed_record(struct dshandle *dsh, char *rec, char *targetdata) { struct eckd_count *ecount = (struct eckd_count *)rec; size_t in_count, out_count, max_count; int reclen, blocksize, reccount; char *inbuf, *outbuf; char *src, *target; size_t rc; int i; blocksize = ecount->dl; reclen = dsh->ds->dsp[0]->f1->DS1LRECL; reccount = blocksize / reclen; outbuf = targetdata; out_count = max_count = (unsigned long)dsh->databuffer + dsh->databufmax - (unsigned long)targetdata; in_count = 0; inbuf = dsh->convbuffer; /* skip block header */ src = (rec + sizeof(*ecount) + ecount->kl); target = inbuf; /* for each record aka line */ for (i = 0; i < reccount; i++) { /* remove trailing spaces */ while (reclen && (*(src + reclen - 1) == EBCDIC_SP)) reclen--; /* move remaining data and add linebreak at end of record */ memcpy(target, src, reclen); target += reclen; *target = EBCDIC_LF; target++; /* count how much chars remain after whitespace cleanup */ in_count += reclen + 1; /* reset for next line */ reclen = dsh->ds->dsp[0]->f1->DS1LRECL; src += reclen; } /* convert directly into target buffer */ rc = iconv(*(dsh->iconv), &inbuf, &in_count, &outbuf, &out_count); if ((rc == (size_t) -1) || (in_count != 0)) return -errorlog_add_message( &dsh->log, NULL, EPROTO, "fixed record parser: codepage conversion failed\n"); /* return how much was written in the target buffer */ return max_count - out_count; } /** * @brief subroutine of parse_variable_record for codepage conversion * * Converts the record data from one codepage to another using iconv. * Stores converted data directly in the target buffer * * @param[in] dsh The dshandle that keeps track of the I/O operations. * @param[in] reclen Length of the record. * @param[in] rec Pointer to the record buffer. * @param[in] targetdata Pointer to the data buffer. * @return Number of copied data bytes on success, * otherwise one of the following (negative) error codes: * - -EPROTO The record is malformed. */ static ssize_t convert_variable_record(struct dshandle *dsh, int reclen, char *rec, char *targetdata) { size_t in_count, out_count, max_count; char *inbuf, *outbuf; size_t rc; inbuf = rec; outbuf = targetdata; in_count = reclen + 1; out_count = max_count = (unsigned long)dsh->databuffer + dsh->databufmax - (unsigned long)targetdata; /* * we can not overwrite the track end marker since it is still used * for this case we have to make a copy of the source data to add the * linebreak */ if (inbuf[reclen] == 0xFF) { inbuf = dsh->convbuffer; memcpy(inbuf, rec, reclen); } /* add linebreak */ inbuf[reclen] = 0x25; rc = iconv(*(dsh->iconv), &inbuf, &in_count, &outbuf, &out_count); if ((rc == (size_t) -1) || (in_count != 0)) return -errorlog_add_message( &dsh->log, NULL, EPROTO, "variable record parser: codepage conversion failed\n"); /* return how much was written in the target buffer */ return max_count - out_count; } /** * @brief subroutine of dshandle_extract_data_from_trackbuffer * * @param[in] dsh The dshandle that keeps track of the I/O operations. * @param[in] rec Pointer to the raw record. * @param[in] targetdata Pointer to the data buffer. * @return Number of copied data bytes on success, * otherwise one of the following (negative) error codes: * - -EPROTO The record is malformed. */ static ssize_t parse_fixed_record(struct dshandle *dsh, char *rec, char *targetdata) { struct eckd_count *ecount; int count; ecount = (struct eckd_count *)rec; /* Make sure that we do not copy data beyond the end of * the data buffer */ if ((unsigned long)targetdata + ecount->dl > (unsigned long)dsh->databuffer + dsh->databufmax) return - errorlog_add_message( &dsh->log, NULL, EPROTO, "fixed record to long for target buffer\n"); if (dsh->iconv) { count = convert_fixed_record(dsh, rec, targetdata); } else { memcpy(targetdata, (rec + sizeof(*ecount) + ecount->kl), ecount->dl); count = ecount->dl; } return count; } /** * @brief subroutine of dshandle_extract_data_from_trackbuffer * * @param[in] dsh The dshandle that keeps track of the I/O operations. * @param[in] rec Pointer to the raw record. * @param[in] targetdata Pointer to the data buffer. * @param[in] keepRDW Flag that specifies if the RDW should be copied to * the data buffer or or not. * @return Number of copied data bytes on success, * otherwise one of the following (negative) error codes: * - -EPROTO The record is malformed. */ static ssize_t parse_variable_record(struct dshandle *dsh, char *rec, char *targetdata, int keepRDW) { struct eckd_count *ecount; unsigned int blocklength, segmentlength, residual; char *data; struct segment_header *blockhead; struct segment_header *seghead; size_t totaldatalength; int count; /* We must not rely on the data in rec, as it was read from disk and * may be broken. Wherever we interprete the data we must have sanity * checks. */ ecount = (struct eckd_count *)rec; totaldatalength = 0; /* An empty record is expected at the end of dataset or member */ if (ecount->dl == 0) return 0; /* If the data area is not zero but to small to contain a segment header * then the record contents cannot be valid. */ if (ecount->dl < sizeof(struct segment_header)) return - errorlog_add_message( &dsh->log, NULL, EPROTO, "variable record parser: record length to small\n"); data = (rec + sizeof(*ecount) + ecount->kl); blockhead = (struct segment_header *)data; blocklength = blockhead->length; /* If the length in the block descriptor is 0, then the block contains * no data. Not sure if this is a valid case, but we tolerate it. */ if (!blocklength) return totaldatalength; /* If blocklength is to small to contain the block descriptor or to * large to fit in the data area, then the block descriptor is broken */ if ((blocklength < sizeof(*blockhead)) || (blocklength > ecount->dl)) return - errorlog_add_message( &dsh->log, NULL, EPROTO, "variable record parser: block length to small\n"); data += sizeof(*blockhead); residual = blocklength - sizeof(*blockhead); while (residual) { seghead = (struct segment_header *)data; segmentlength = seghead->length; if (seghead->nullsegment || !segmentlength) { /* null segment found -> end of data in block */ return totaldatalength; } /* If segmentlength is to small to contain the record descriptor * descriptor or to large to fit in the residual data area, then * the record descriptor is broken */ if ((residual < segmentlength) || (segmentlength < sizeof(*seghead))) return - errorlog_add_message( &dsh->log, NULL, EPROTO, "variable record parser: segment length %d " "inconsistent at offset %lu\n", segmentlength, (unsigned long)seghead - (unsigned long)rec); residual -= segmentlength; if (!keepRDW) { data += sizeof(*seghead); segmentlength -= sizeof(*seghead); } /* Make sure that we do not copy data beyond the end of * the data buffer */ if ((unsigned long)targetdata + segmentlength > (unsigned long)dsh->databuffer + dsh->databufmax) return - errorlog_add_message( &dsh->log, NULL, EPROTO, "variable record parser: " "record to long for target buffer\n"); if (dsh->iconv) { count = convert_variable_record(dsh, segmentlength, data, targetdata); if (count < 0) return count; totaldatalength += count; targetdata += count; } else { memcpy(targetdata, data, segmentlength); totaldatalength += segmentlength; targetdata += segmentlength; } data += segmentlength; } return totaldatalength; } /** * @brief subroutine of lzds_dshandle_read * * Parses the raw track buffer in dsh and copies the user data to * the databuffer in dsh. * * @param[in] dsh The dshandle that keeps track of the I/O operations. * @return 0 on success, otherwise one of the following error codes: * - EPROTO The raw track data is malformed. */ static int dshandle_extract_data_from_trackbuffer(struct dshandle *dsh) { char *track; size_t i, trckcount; struct eckd_count *ecount; char *rawdata, *targetdata; unsigned int record; char DS1RECFM; ssize_t tdsize; DS1RECFM = dsh->ds->dsp[0]->f1->DS1RECFM; trckcount = dsh->rawbufsize / RAWTRACKSIZE; track = dsh->rawbuffer; targetdata = dsh->databuffer; dsh->databufsize = 0; /* Record zero is not part of the regular data, so I must not copy its * data. In case of a PDS member, we may need to skip a few extra * records on the first track. In this case startrecord is already set * and will be reset to 1 after the first track has been read. */ if (!dsh->startrecord) dsh->startrecord = 1; for (i = 0; i < trckcount && !dsh->eof_reached; ++i) { record = 0; rawdata = track; while (!dsh->eof_reached) { tdsize = 0; if (record >= dsh->startrecord) { /* fixed or undefined record size */ if ((DS1RECFM & 0x80)) tdsize = parse_fixed_record(dsh, rawdata, targetdata); /* variable records */ if (!(DS1RECFM & 0x80) && (DS1RECFM & 0x40)) tdsize = parse_variable_record(dsh, rawdata, targetdata, dsh->keepRDW); if (tdsize < 0) return errorlog_add_message( &dsh->log, dsh->log, EPROTO, "data extraction: error at " "record %u, offset %lu\n", record, (unsigned long)rawdata - (unsigned long)dsh->rawbuffer); targetdata += tdsize; dsh->databufsize += tdsize; } ecount = (struct eckd_count *)rawdata; rawdata += sizeof(*ecount) + ecount->kl + ecount->dl; /* An empty record marks the end of a member / data set * We need to take startrecord into account or we might * find the end marker of the previous member. */ if ((record >= dsh->startrecord) && (!ecount->kl) && (!ecount->dl)) dsh->eof_reached = 1; ++record; if ((*(unsigned long long *)rawdata) == ENDTOKEN) break; if ((unsigned long)rawdata >= (unsigned long)track + RAWTRACKSIZE) return errorlog_add_message( &dsh->log, NULL, EPROTO, "data extraction: run over end of" " track buffer\n"); } dsh->startrecord = 1; track += RAWTRACKSIZE; } return 0; } /** * @brief subroutine of lzds_dshandle_read * * Find the next range of extents and prepare dsh for the next read. * The return value indicates whether there is more data to read or not. * * @pre: For the first call to this function, dsh should be set to the * last track before the first track to read. * If the first track to read is the first track in the dataset * then set dsh->ext_seq_no to -1. * * @param[in] dsh The dshandle that keeps track of the I/O operations. * * @return * 0 when there is no further raw data available, * 1 when there is more data available and dsh is prepared */ static int dshandle_prepare_for_next_read_tracks(struct dshandle *dsh) { int found, dsp_no, ext_seq_no; /* If there are still unread tracks in the current extent, we just need * to point dsh to the next range of tracks */ if (dsh->bufendtrk < dsh->extendtrk) { dsh->bufstarttrk = dsh->bufendtrk + 1; dsh->bufendtrk = dsh->bufstarttrk + (dsh->rawbufmax / RAWTRACKSIZE) - 1; dsh->bufendtrk = MIN(dsh->bufendtrk, dsh->extendtrk); dsh->rawbufsize = (dsh->bufendtrk - dsh->bufstarttrk + 1) * RAWTRACKSIZE; dsh->databufoffset = dsh->databufoffset + dsh->databufsize; dsh->databufsize = 0; dsh->bufpos = 0; dsh->frameno++; return 1; } /* There are no more tracks left in the current extent. * Loop over data set parts and extends in these parts until a valid * extent is found or the end of the data set is reached */ ext_seq_no = dsh->ext_seq_no; dsp_no = dsh->dsp_no; found = 0; while (!found) { ++ext_seq_no; if (ext_seq_no >= MAXEXTENTS) { ext_seq_no = 0; ++dsp_no; } if (dsp_no >= dsh->ds->dspcount) break; if (extent_contains_userdata( &dsh->ds->dsp[dsp_no]->ext[ext_seq_no])) found = 1; } if (!found) return 0; /* We have found the next valid extent. Get lower and upper track * limits and set dsh to the first range of tracks */ dsh->ext_seq_no = ext_seq_no; dsh->dsp_no = dsp_no; lzds_dasd_cchh2trk(dsh->ds->dsp[dsp_no]->dasdi, &dsh->ds->dsp[dsp_no]->ext[ext_seq_no].llimit, &dsh->extstarttrk); lzds_dasd_cchh2trk(dsh->ds->dsp[dsp_no]->dasdi, &dsh->ds->dsp[dsp_no]->ext[ext_seq_no].ulimit, &dsh->extendtrk); dsh->bufstarttrk = dsh->extstarttrk; dsh->bufendtrk = dsh->bufstarttrk + (dsh->rawbufmax / RAWTRACKSIZE) - 1; dsh->bufendtrk = MIN(dsh->bufendtrk, dsh->extendtrk); dsh->rawbufsize = (dsh->bufendtrk - dsh->bufstarttrk + 1) * RAWTRACKSIZE; dsh->databufoffset = dsh->databufoffset + dsh->databufsize; dsh->databufsize = 0; dsh->bufpos = 0; dsh->frameno++; return 1; } /** * @brief subroutine of lzds_dshandle_read * * As we progress in reading data from the dataset, we store * track/data offsets in the dshandle for late use by the * lzds_dshandle_lseek and related operations. * * @param[in] dsh The dshandle that keeps track of the I/O operations. * * @return 0 on success, otherwise one of the following error codes: * - EPROTO The existing seek buffer data is inconsistent. * - EINVAL The existing seek buffer data is inconsistent. * - ERANGE We try to add more elements than the prepared buffer can hold. */ static int dshandle_store_trackframe(struct dshandle *dsh) { unsigned long long index; /* if we have no skip or seekbuf we cannot store anything */ if (!dsh->skip || !dsh->seekbuf) return 0; /* if this is a frame we want to skip, just return 0 */ if (dsh->frameno % dsh->skip) return 0; /* make sure we do not access elements beyond the end of the buffer */ if (dsh->seek_current >= dsh->seek_count) return errorlog_add_message( &dsh->log, NULL, ERANGE, "store track frame: frame list size is inconsistent\n"); /* our seek code relies on the fact that element n refers to frame * n * skip, so we need to make sure we that we do not leave gaps */ index = dsh->frameno / dsh->skip; if (index > dsh->seek_current) return errorlog_add_message( &dsh->log, NULL, EPROTO, "store track frame: frame list inconsistent\n"); /* if we have visited this frame before, return */ if (index < dsh->seek_current) { if (dsh->seekbuf[index].dsp_no != dsh->dsp_no || dsh->seekbuf[index].ext_seq_no != dsh->ext_seq_no || dsh->seekbuf[index].bufstarttrk != dsh->bufstarttrk || dsh->seekbuf[index].databufoffset != dsh->databufoffset) return errorlog_add_message( &dsh->log, NULL, EINVAL, "store track frame: frame data inconsistent\n"); else return 0; } /* the seek_current = index case */ dsh->seekbuf[index].dsp_no = dsh->dsp_no; dsh->seekbuf[index].ext_seq_no = dsh->ext_seq_no; dsh->seekbuf[index].bufstarttrk = dsh->bufstarttrk; dsh->seekbuf[index].databufoffset = dsh->databufoffset; dsh->seek_current++; return 0; } /** * @param[in] dsh The dshandle that keeps track of the I/O operations. * @param[in] buf The target buffer for the read data. * @param[in] size The number of bytes that are to be read. * @param[out] rcsize Reference to a variable in which the actual number * of read bytes is returned. * If this is 0, the end of the file is reached. * @return 0 on success, otherwise one of the following error codes: * - EINVAL The data in dsh is inconsistent. * - ERANGE The data in dsh is inconsistent. * - EPROTO The data read from the disk does not conform to the * expected format. * - EIO I/O error when reading from device. */ int lzds_dshandle_read(struct dshandle *dsh, char *buf, size_t size, ssize_t *rcsize) { ssize_t copysize; int rc; errorlog_clear(dsh->log); if (!dsh->is_open) return errorlog_add_message( &dsh->log, NULL, EINVAL, "data set read: dshandle is not open\n"); *rcsize = 0; while (*rcsize < (long long)size) { if (dsh->bufpos >= dsh->databufsize) { /* need to fill dsh data buffer */ if (dsh->eof_reached) break; /* end of data in data set reached */ if (!dshandle_prepare_for_next_read_tracks(dsh)) break; /* end of data set extents reached */ rc = lzds_dasdhandle_read_tracks_to_buffer( dsh->dasdhandle[dsh->dsp_no], dsh->bufstarttrk, dsh->bufendtrk, dsh->rawbuffer); if (rc) return errorlog_add_message( &dsh->log, dsh->dasdhandle[dsh->dsp_no]->log, rc, "data set read: error reading data set" " %s\n", dsh->ds->name); rc = dshandle_extract_data_from_trackbuffer(dsh); if (rc) return errorlog_add_message( &dsh->log, dsh->log, rc, "data set read: extracting data set " "%s from %s, tracks %u to %u\n", dsh->ds->name, dsh->dasdhandle[dsh->dsp_no]->dasd->device, dsh->bufstarttrk, dsh->bufendtrk); rc = dshandle_store_trackframe(dsh); if (rc) return errorlog_add_message( &dsh->log, dsh->log, rc, "data set read: storing track frame " "%s\n", dsh->ds->name); } /* if databuf has data to copy */ if (dsh->bufpos < dsh->databufsize) { /* copy data from databuf to buf */ copysize = MIN(((long long)size - *rcsize), (dsh->databufsize - dsh->bufpos)); memcpy(buf, &dsh->databuffer[dsh->bufpos], copysize); buf += copysize; dsh->bufpos += copysize; *rcsize += copysize; } } return 0; } /** * @brief subroutine of lzds_dshandle_lseek * * Find the closest buffered seekelement that starts before offset * * @param[in] dsh The dshandle that keeps track of the I/O operations. * @param[in] offset The data offset in the dataset that we want to reach. * @param[out] se_index Reference to a variable in which the found index * to dsh->seekbuf is returned. * * @return 0 on success, otherwise one of the following error codes: * - EINVAL There is no seekbuffer available. */ static int dshandle_find_seekelement(struct dshandle *dsh, off_t offset, long long *se_index) { unsigned long long low, high, index; if (!dsh->seek_current) return EINVAL; /* special case for the last element in the list */ if (dsh->seekbuf[dsh->seek_current - 1].databufoffset <= offset) { *se_index = dsh->seek_current - 1; return 0; } /* search starts with 'high' set to the second to last element */ index = 0; high = dsh->seek_current - 2; low = 0; *se_index = 0; index = (high + low) / 2; while (low != high) { if (dsh->seekbuf[index].databufoffset <= offset) { low = index; index = (high + low + 1) / 2; } else { high = index - 1; index = (high + low) / 2; } } *se_index = low; return 0; } /** * @brief subroutine of lzds_dshandle_lseek * * Reset the internel buffers etc, so that the next read will read * the track frame pointed to by the seekelement. * * @param[in] dsh The dshandle that keeps track of the I/O operations. * @param[out] se_index Index to the seekelement in dsh->seekbuf. */ static void dshandle_reset_buffer_position_to_seekelement( struct dshandle *dsh, long long se_index) { /* make sure that read knows that we have no ready data in our buffer */ dsh->bufpos = 0; dsh->databufsize = 0; dsh->eof_reached = 0; /* we need to set the bufendtrk and sequence number so, * that the current track buffer seems to end with the * track that comes before the first track of the * data set or member */ /* framno will be incremented during read, so do a -1 here */ dsh->frameno = (se_index * dsh->skip) - 1; dsh->databufoffset = dsh->seekbuf[se_index].databufoffset; dsh->dsp_no = dsh->seekbuf[se_index].dsp_no; /* For a partitioned data set we need to find the correct start * track and point the current buffer just before it. * As we always need to read full tracks, any additional * record offset will be set explicitly and handled during * track interpretation. */ if (dsh->member && (dsh->frameno == -1)) dsh->startrecord = dsh->member->record; /* In most cases our track frame will be in the middle of the * disk, so we set bufendtrk to the last track before our track * frame. In the special case that the track frame begins * on track 0, we set the ext_seq_no to that of the frame -1, * so that the read code will advance to the next extend and * the first track of that extent */ if (!dsh->seekbuf[se_index].bufstarttrk) { dsh->ext_seq_no = dsh->seekbuf[se_index].ext_seq_no - 1; dsh->bufendtrk = 0; dsh->extendtrk = 0; return; } dsh->ext_seq_no = dsh->seekbuf[se_index].ext_seq_no; lzds_dasd_cchh2trk(dsh->ds->dsp[dsh->dsp_no]->dasdi, &dsh->ds->dsp[dsh->dsp_no]->ext[dsh->ext_seq_no].llimit, &dsh->extstarttrk); lzds_dasd_cchh2trk(dsh->ds->dsp[dsh->dsp_no]->dasdi, &dsh->ds->dsp[dsh->dsp_no]->ext[dsh->ext_seq_no].ulimit, &dsh->extendtrk); dsh->bufstarttrk = 0; dsh->bufendtrk = dsh->seekbuf[se_index].bufstarttrk - 1; dsh->rawbufsize = 0; dsh->databufoffset = dsh->seekbuf[se_index].databufoffset; dsh->databufsize = 0; dsh->bufpos = 0; return; } /** * It is not possible to seek beyond the end of the data, but an * attempt to do so is a common occurrence as we may not know the * actual data size beforehand. In this case, the returned rcoffset * is smaller than offset and points to the offset directly following * the last data byte. * * @param[in] dsh The dshandle that keeps track of the I/O operations. * @param[in] offset The data offset in the dataset that we want to reach. * @param[out] rcoffset Reference to a variable in which the actual offset * is returned. * * @return 0 on success, otherwise one of the following error codes: * - EINVAL The data in dsh is inconsistent. * - ERANGE The data in dsh is inconsistent. * - EPROTO The data read from the disk does not conform to the * expected format. * - EIO I/O error when reading from device. */ int lzds_dshandle_lseek(struct dshandle *dsh, long long offset, long long *rcoffset) { char foo; ssize_t rcsize; int rc; long long se_index; errorlog_clear(dsh->log); if (dsh->databufoffset <= offset && offset < dsh->databufoffset + dsh->databufsize) { /* offset is within the current track frame */ dsh->bufpos = offset - dsh->databufoffset; *rcoffset = offset; return 0; } /* need to seek to some other track frame */ if (!dshandle_find_seekelement(dsh, offset, &se_index)) { /* do not reset our context if we can seek forward from * our current position */ if (!(dsh->seekbuf[se_index].databufoffset < dsh->databufoffset && dsh->databufoffset <= offset)) { dshandle_reset_buffer_position_to_seekelement(dsh, se_index); } } else if (offset < dsh->databufoffset) { /* if we have no seekbuffer, we can only reset to the * start of the data set */ rc = initialize_buffer_positions_for_first_read(dsh); if (rc) return errorlog_add_message( &dsh->log, dsh->log, rc, "data set seek: error when initializing buffers" " for data set %s\n", dsh->ds->name); } /* from here on we just need to go forward by reading track * frames until we find a frame that contains the offset */ while (dsh->databufoffset + dsh->databufsize <= offset) { dsh->bufpos = dsh->databufsize; rc = lzds_dshandle_read(dsh, &foo, sizeof(foo), &rcsize); if (rc || !rcsize) { *rcoffset = dsh->databufoffset + dsh->databufsize; if (rc) errorlog_add_message( &dsh->log, dsh->log, rc, "data set seek: error reading data from" " data set %s\n", dsh->ds->name); return rc; } } dsh->bufpos = offset - dsh->databufoffset; *rcoffset = offset; return 0; } /** * @param[in] dsh The dshandle that keeps track of the I/O operations. * @param[out] offset Reference to a variable in which the current offset * is returned. */ void lzds_dshandle_get_offset(struct dshandle *dsh, long long *offset) { *offset = dsh->databufoffset + dsh->bufpos; } /** * @param[in] dsh The dshandle that keeps track of the I/O operations. * @param[out] log Reference to a variable in which the errorlog * is returned. */ void lzds_dshandle_get_errorlog(struct dshandle *dsh, struct errorlog **log) { *log = dsh->log; } /******************************************************************************/ /* libzds helper functions */ /******************************************************************************/ /** * This function takes the DS1RECFM byte as defined for the format 1 DSCB, and * creates a string of the usual characters F, V, U, T, B, S, A, and M. * * @param[in] DS1RECFM Input byte. * @param[out] buffer Buffer for the output string. * The buffer must be at least 7 characters long. */ void lzds_DS1RECFM_to_recfm(char DS1RECFM, char *buffer) { if ((DS1RECFM & 0x80) && !(DS1RECFM & 0x40)) *buffer++ = 'F'; /* fixed records */ if (!(DS1RECFM & 0x80) && (DS1RECFM & 0x40)) *buffer++ = 'V'; /* variable records */ if ((DS1RECFM & 0x80) && (DS1RECFM & 0x40)) *buffer++ = 'U'; /* undefined length records */ if ((DS1RECFM & 0x20)) *buffer++ = 'T'; /* track overflow (legacy) */ if ((DS1RECFM & 0x10)) *buffer++ = 'B'; /* blocked */ if ((DS1RECFM & 0x08)) *buffer++ = 'S'; /* standard records */ if ((DS1RECFM & 0x04) && !(DS1RECFM & 0x02)) *buffer++ = 'A'; /* ISO / ANSI control characters */ if (!(DS1RECFM & 0x04) && (DS1RECFM & 0x02)) *buffer++ = 'M'; /* machine control characters */ *buffer = 0; /* The combinations ((DS1RECFM & 0x04) && (DS1RECFM & 0x02)) * and (DS1RECFM & 0x01) are reserved * * If we count only one byte for the mutual exclusive F, V and U, * three bytes for T, B and S, * one byte for the mutual exclusive A and M, * one byte for a possible future definition of 0x01, and * one byte for the zero termination, * then we get a required buffer length of 7 bytes */ } int lzds_analyse_open_count(struct zdsroot *root, int warn) { struct dasd *dasd; int value; int rc = 0; util_list_iterate(root->dasdlist, dasd) { value = dasd_get_host_access_count(dasd->device); if (value < 0) { fprintf(stderr, "Hosts access information not available for disk %s.\n", dasd->device); rc = value; continue; } if (value == 1) continue; if (warn) fprintf(stderr, "\nWARNING:\n" "Disk %s is online on operating system instances in %d different LPARs.\n" "Ensure that the disk is not being used by a system outside your LPAR.\n" "Note: Your installation might include z/VM systems that are configured to\n" "automatically vary on disks, regardless of whether they are subsequently used.\n", dasd->device, value); else { fprintf(stderr, "\nERROR:\n" "Disk %s is online on operating system instances in %d different LPARs.\n" "Ensure that the disk is not being used by a system outside your LPAR.\n" "Note: Your installation might include z/VM systems that are configured to\n" "automatically vary on disks, regardless of whether they are subsequently used.\n", dasd->device, value); rc = -EACCES; } } return rc; } s390-tools-2.38.0/libzpci/000077500000000000000000000000001502674226300151035ustar00rootroot00000000000000s390-tools-2.38.0/libzpci/Makefile000066400000000000000000000005101502674226300165370ustar00rootroot00000000000000include ../common.mak lib = libzpci.a all: $(lib) objects = pci_list.o pci_sclp.o examples := $(patsubst %.c,%,$(wildcard *_example.c)) examples: $(examples) $(examples): %: %.o $(lib) $(rootdir)/libutil/libutil.a $(lib): ALL_CFLAGS += -fPIC -std=c11 $(lib): $(objects) install: all clean: rm -f *.o $(lib) $(examples) s390-tools-2.38.0/libzpci/libzpci_example.c000066400000000000000000000021661502674226300204230ustar00rootroot00000000000000#include #include #include #include #include "lib/util_list.h" #include "lib/pci_list.h" static void zpci_print(struct zpci_dev *zdev) { char *pci_addr = zpci_pci_addr(zdev); int i; if (!zdev->conf) { printf("fid: %8x address: %s\n", zdev->fid, pci_addr); } else { printf("fid: %8x address: %s uid: %4x%s pchid: %4x vfn: %4d port: %1d pft: %s ", zdev->fid, pci_addr, zdev->uid, (zdev->uid_is_unique) ? " (unique)" : "", zdev->pchid, zdev->vfn, zdev->port, zpci_pft_str(zdev)); if (zdev->num_netdevs) { printf("netdevs: "); for (i = 0; i < zdev->num_netdevs; i++) { printf("%s (%s)", zdev->netdevs[i].name, zpci_operstate_str(zdev->netdevs[i].operstate)); if (i + 1 < zdev->num_netdevs) printf(", "); } } printf("\n"); } free(pci_addr); } int main(void) { struct util_list *zpci_list; struct zpci_dev *zdev; zpci_list = zpci_dev_list(); if (!zpci_list) errx(EXIT_FAILURE, "Error getting list of zPCI devices"); util_list_iterate(zpci_list, zdev) zpci_print(zdev); zpci_free_dev_list(zpci_list); return EXIT_SUCCESS; } s390-tools-2.38.0/libzpci/pci_list.c000066400000000000000000000215011502674226300170540ustar00rootroot00000000000000/** * libzpci - Functions to handle zPCI devices and their properties * * Copyright IBM Corp. 2023 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include "lib/pci_list.h" #include "lib/util_file.h" #include "lib/util_libc.h" #include "lib/util_list.h" #include "lib/util_path.h" #include "lib/util_scandir.h" /** * Get the function type name for the given device * * The device type name is suitable for presentation to a user. * * @param[in] zdev The device in question * * @return a string representing the PCI device type */ const char *zpci_pft_str(struct zpci_dev *zdev) { switch (zdev->pft) { case ZPCI_PFT_UNCLASSIFIED: return "unclassified"; case ZPCI_PFT_ROCE_EXPRESS: return "RoCE Express"; case ZPCI_PFT_ROCE_EXPRESS2: return "RoCE Express-2"; case ZPCI_PFT_CNW: return "Cloud Network Adapter"; case ZPCI_PFT_NETH: return "Network Express Hybrid"; case ZPCI_PFT_NETD: return "Network Express Dedicated"; case ZPCI_PFT_NVME: return "NVMe"; case ZPCI_PFT_ISM: return "ISM"; default: return "unknown"; } } /** * Get a textual representation of the device's PCI address * * The representation has extended "DDDD:bb:dd.f" format used * by Linux tooling such as lspci. * * @param[in] zdev The device in question * * @return the string representing the PCI address */ char *zpci_pci_addr(struct zpci_dev *zdev) { uint8_t bus = zdev->bdf.bus; uint8_t dev = zdev->bdf.dev; uint8_t fn = zdev->bdf.fn; char *pci_addr; util_asprintf(&pci_addr, "%04x:%02x:%02x.%x", zdev->domain_nr, bus, dev, fn); return pci_addr; } /** * Get an operationanl state value from its state name * * The state names follow RFC 2863 and the values match * IF_OPER_* in linux/if.h. * * @param[in] oper_str The name of the operational state * * @return the operational state value */ operstate_t zpci_operstate_from_str(const char *oper_str) { if (!strcmp(oper_str, "notpresent")) return IF_OPER_NOTPRESENT; else if (!strcmp(oper_str, "down")) return IF_OPER_DOWN; else if (!strcmp(oper_str, "lowerlayerdown")) return IF_OPER_LOWERLAYERDOWN; else if (!strcmp(oper_str, "testing")) return IF_OPER_TESTING; else if (!strcmp(oper_str, "dormant")) return IF_OPER_DORMANT; else if (!strcmp(oper_str, "up")) return IF_OPER_UP; else return IF_OPER_UNKNOWN; } /** * Get an operationanl state name from its value * * The state names follow RFC 2863 and the values match * IF_OPER_* in linux/if.h. * * @param[in] state The value of the operational state * * @return the operational state name string representation */ const char *zpci_operstate_str(operstate_t state) { switch (state) { case IF_OPER_NOTPRESENT: return "notpresent"; case IF_OPER_DOWN: return "down"; case IF_OPER_LOWERLAYERDOWN: return "lowerlayerdown"; case IF_OPER_TESTING: return "testing"; case IF_OPER_DORMANT: return "dormant"; case IF_OPER_UP: return "up"; case IF_OPER_UNKNOWN: default: return "unknown"; }; } static int zpci_populate_from_slot_dir(struct zpci_dev *zdev, const char *slot_dir, const char *slot_name) { char buf_addr[11]; /* "dddd:bb:dd\0" */ uint8_t bus, df; uint32_t domain; int val, rc; rc = sscanf(slot_name, "%x", &zdev->fid); if (rc != 1) return -EINVAL; rc = util_file_read_line(buf_addr, sizeof(buf_addr), "%s/%s/address", slot_dir, slot_name); if (rc) { warn("Reading address from slot %s/%s", slot_dir, slot_name); return rc; } rc = sscanf(buf_addr, "%04x:%02hhx:%02hhx", &domain, &bus, &df); if (rc != 3) return -EINVAL; zdev->domain_nr = domain; zdev->bdf.val = (((uint16_t)bus) << 8) | df; rc = util_file_read_i(&val, 10, "%s/%s/power", slot_dir, slot_name); if (rc) { warn("Reading power from slot %s/%s", slot_dir, slot_name); return rc; } zdev->conf = val > 0; return 0; } static int zpci_populate_netdevices(struct zpci_dev *zdev, const char *dev_dir) { const char *netdev_patt = "en.*"; struct dirent **de_vec; int count, i, rc = 0; char *net_dir; char buf[16]; /* "lowerlayerdown" */ util_asprintf(&net_dir, "%s/net", dev_dir); count = util_scandir(&de_vec, alphasort, net_dir, netdev_patt); if (count == -1) { warn("Reading netdevice information for %s/net failed", dev_dir); rc = -EINVAL; goto out_net_dir; } /* A directory per netdev */ for (i = 0; i < count; i++) { if (de_vec[i]->d_type != DT_DIR) { rc = -EINVAL; goto out_scan_dir; } } zdev->num_netdevs = count; if (!count) goto out_scan_dir; zdev->netdevs = util_zalloc(sizeof(struct zpci_netdev) * zdev->num_netdevs); for (i = 0; i < count; i++) { zdev->netdevs[i].name = util_strdup(de_vec[i]->d_name); rc = util_file_read_line(buf, sizeof(buf), "%s/%s/operstate", net_dir, zdev->netdevs[i].name); if (rc) { /* If operstate is not readable just set to unknown */ zdev->netdevs[i].operstate = IF_OPER_UNKNOWN; rc = 0; continue; } zdev->netdevs[i].operstate = zpci_operstate_from_str(buf); } out_scan_dir: util_scandir_free(de_vec, count); out_net_dir: free(net_dir); return rc; } static int zpci_populate_from_dev_dir(struct zpci_dev *zdev) { char *pci_addr = zpci_pci_addr(zdev); int rc, val; char *path; path = util_path_sysfs("bus/pci/devices/%s", pci_addr); if (!path) { rc = -EINVAL; goto out_pci_addr; } if (!util_path_exists(path)) { rc = -ENODEV; goto out_path; } rc = util_file_read_i(&val, 16, "%s/uid", path); if (rc) goto out_path; zdev->uid = val; /* In old Linux versions uid_is_unique doesn't exist * so don't treat this as an error. */ rc = util_file_read_i(&val, 10, "%s/uid_is_unique", path); if (!rc) zdev->uid_is_unique = !!val; rc = util_file_read_i(&val, 16, "%s/pchid", path); if (rc) goto out_path; zdev->pchid = val; rc = util_file_read_i(&val, 16, "%s/vfn", path); if (rc) goto out_path; zdev->vfn = val; rc = util_file_read_i(&val, 10, "%s/port", path); if (rc) goto out_path; zdev->port = val; rc = util_file_read_i(&val, 16, "%s/pft", path); if (rc) goto out_path; zdev->pft = val; if (util_path_is_readable("%s/net", path)) { rc = zpci_populate_netdevices(zdev, path); if (rc) goto out_path; } out_path: free(path); out_pci_addr: free(pci_addr); return rc; } /** * Get a list of all configured and standby PCI devices * * @return a list of struct zpci_dev in case of success, * NULL in case of failure */ struct util_list *zpci_dev_list(void) { char *path = util_path_sysfs("bus/pci/slots/"); const char *zpci_slot_patt = "[0-9a-f]{8}"; struct util_list *zpci_list = NULL; struct dirent **de_vec; struct zpci_dev *zdev; int count, i, rc; count = util_scandir(&de_vec, alphasort, path, zpci_slot_patt); if (count == -1) { warn("util_scandir failed"); goto error_path; } zpci_list = util_list_new(struct zpci_dev, entry); for (i = 0; i < count; i++) { if (de_vec[i]->d_type != DT_DIR) continue; zdev = util_zalloc(sizeof(*zdev)); rc = zpci_populate_from_slot_dir(zdev, path, de_vec[i]->d_name); if (rc) { free(zdev); continue; } if (zdev->conf) { rc = zpci_populate_from_dev_dir(zdev); if (rc) { free(zdev); continue; } } util_list_add_tail(zpci_list, zdev); } util_scandir_free(de_vec, count); error_path: free(path); return zpci_list; } /** * Free a PCI device struct * * This frees both the struct zpci_dev and its associated netdevs array * * @param[in] zdev The device struct to free */ void zpci_free_dev(struct zpci_dev *zdev) { int i; if (zdev->num_netdevs) { for (i = 0; i < zdev->num_netdevs; i++) free(zdev->netdevs[i].name); free(zdev->netdevs); } free(zdev); } /** * Free a PCI device list * * This frees all elements in the list * * @param[in] zpci_list The device list to free */ void zpci_free_dev_list(struct util_list *zpci_list) { struct zpci_dev *zdev, *tmp; util_list_iterate_safe(zpci_list, zdev, tmp) { util_list_remove(zpci_list, zdev); zpci_free_dev(zdev); } util_list_free(zpci_list); } /** * Find a PCI device given the name of a netdev * * This function allows finding a PCI device when only the name of one * of its netdevs is known. * * @param[in] zpci_list The device list to search * @param[in] netdev_name The name of the netdev * @param[out] netdev Pointer to store the netdev or NULL if * only the PCI device is needed * * @return The PCI device if one is found NULL otherwise */ struct zpci_dev *zpci_find_by_netdev(struct util_list *zpci_list, char *netdev_name, struct zpci_netdev **netdev) { struct zpci_dev *zdev = NULL; int i; util_list_iterate(zpci_list, zdev) { for (i = 0; i < zdev->num_netdevs; i++) { if (!strcmp(zdev->netdevs[i].name, netdev_name)) { if (netdev) *netdev = &zdev->netdevs[i]; return zdev; } } } return NULL; } s390-tools-2.38.0/libzpci/pci_sclp.c000066400000000000000000000027721502674226300170530ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "lib/pci_sclp.h" #include "lib/util_path.h" static int zpci_sclp_report(char *pci_addr, struct zpci_report_error *report) { size_t r_size = sizeof(*report); char *path; FILE *fp; path = util_path_sysfs("bus/pci/devices/%s/report_error", pci_addr); fp = fopen(path, "w"); free(path); if (!fp) return -ENODEV; if (fwrite(report, 1, r_size, fp) != r_size) return -EIO; if (fclose(fp)) return -EIO; return 0; } /** * Issue an SCLP Adapter Error Notification event with a specific action * qualifier and optional log data. * * The logged data is truncated if needed. * * @return the number of bytes of the data which were actually logged * or a negative value on error. */ int zpci_sclp_issue_action(char *pci_addr, int action, char *data, size_t length, u64 err_log_id) { struct zpci_report_error report = {0}; size_t copy_length = 0; int ret; /* Data is truncated to fit in the report */ if (data) copy_length = MIN(length, sizeof(report.data.log_data)); report.header.version = 1; report.header.action = action; report.header.length = offsetof(struct zpci_report_error_data, log_data) + copy_length; report.data.timestamp = (__u64)time(NULL); report.data.err_log_id = err_log_id; if (data) memcpy(report.data.log_data, data, copy_length); ret = zpci_sclp_report(pci_addr, &report); if (ret) return ret; return copy_length; } s390-tools-2.38.0/lsstp/000077500000000000000000000000001502674226300146145ustar00rootroot00000000000000s390-tools-2.38.0/lsstp/Makefile000066400000000000000000000006041502674226300162540ustar00rootroot00000000000000include ../common.mak libs = $(rootdir)/libutil/libutil.a all: lsstp lsstp: lsstp.o $(libs) install: all $(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) \ $(DESTDIR)$(MANDIR)/man8 $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 lsstp $(DESTDIR)$(BINDIR) $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 lsstp.8 \ $(DESTDIR)$(MANDIR)/man8 clean: rm -f *.o *~ lsstp core .PHONY: all install clean s390-tools-2.38.0/lsstp/lsstp.8000066400000000000000000000046111502674226300160540ustar00rootroot00000000000000.\" Copyright 2020 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH LSSTP 8 "Jul 2020" "s390-tools" "Linux Administrator's Manual" .SH NAME .B "lsstp " \- Show STP configuration information .SH SYNOPSIS .BI "lsstp " .SH DESCRIPTION .B lsstp displays information about the current Server Time Protocol (STP) configuration like coordinated time network (CTN) ID, timing state and leap seconds. .SH OUTPUT .TP .B STP online Indication of the online state .TP .B CTN ID The ID of the coordinated time network. If it can be decoded as EBCDIC it is shown as an EBCDIC string, otherwise a hexadecimal representation is shown. .TP .B CTN Type The type of timing network. .IP .B No CTN STP is not configured for attachment to a CTN. .IP .B STP-only STP is configured and attached to a CTN with only STP nodes. .IP .B Mixed STP is configured and attached to a CTN with both STP and external time reference (ETR) nodes. .TP .B Stratum The number of servers in the timing path between the local STP clock and the selected primary time server. .TP .B Timing mode .IP .B Local The Time-of-day (TOD) clock is stepped by the local hardware oscillator and is not steered by the STP facility. .IP .B ETR The TOD clock is synchronized with an attached 9037 Sysplex Timer. .IP .B STP The TOD clock is steered by the STP facility to maintain synchronization with a Coordinated Server Time (CST). .IP .B Uninitialized The TOD clock is not initialized. The STP facility is allowed to perform a step adjustment to the TOD clock for synchronization. .TP .B Timing state The synchronization state of the STP facility. Can be unsynchronized, synchronized or stopped. .TP .B DST offset The daylight savings time offset relative to UTC in minutes. .TP .B Timezone offset The offset of the local time relative to UTC in minutes. .TP .B Time offset The total time offset at the server. This field is only valid in mixed CTN configurations. .TP .B Active leap seconds The number of leap seconds that are currently in effect at the STP facility. .TP .B Leap second at If a leap second insertion or deletion is scheduled in the STP facility, this field shows the day and time of the scheduled change. .SH OPTIONS .TP .BI "-v|--version" Print version number. .TP .BI "-h|--help" Print usage text. .SH AUTHORS Sven Schnelle s390-tools-2.38.0/lsstp/lsstp.c000066400000000000000000000122141502674226300161250ustar00rootroot00000000000000/* * lsstp - Display STP system information * * Copyright IBM Corp. 2020, 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include "lib/util_opt.h" #include "lib/util_file.h" #include "lib/util_prg.h" #include "lib/util_path.h" static const struct util_prg prg = { .desc = "Display STP system information", .args = "", .copyright_vec = { { .owner = "IBM Corp.", .pub_first = 2020, }, UTIL_PRG_COPYRIGHT_END } }; static struct util_opt opt_vec[] = { UTIL_OPT_HELP, UTIL_OPT_VERSION, UTIL_OPT_END }; struct stp_parms { uint64_t ctn_id; unsigned int online; unsigned int leap_seconds; int leap_seconds_diff; unsigned int leap_seconds_utc; unsigned int stratum; unsigned int ctn_type; unsigned int timing_mode; unsigned int timing_state; int dst_offset; int time_offset; int time_zone_offset; }; static int convert_ctn_id(char *in, char *out) { iconv_t ic; size_t inlen = sizeof(unsigned long long); size_t outlen = sizeof(unsigned long long); ic = iconv_open("ISO-8859-1", "EBCDIC-US"); if (ic == (iconv_t)-1) { warn("Could not initialize EBCDIC to ISO-8859-1 conversion table"); return -1; } if (iconv(ic, &in, &inlen, (char **)&out, &outlen) == (size_t)-1) { warn("Code page translation EBCDIC to ISO-8859-1 failed"); iconv_close(ic); return -1; } iconv_close(ic); return 0; } static const char *ctn_type_str(int type) { switch (type) { case 0: return "No CTN defined"; case 1: return "STP-only"; case 2: return "mixed"; default: return "unknown"; } } static const char *tmd_to_str(int mode) { switch (mode) { case 0: return "Local"; case 1: return "ETR"; case 2: return "STP"; case 15: return "Uninitialized"; default: return "unknown"; } } static const char *tst_to_str(int mode) { switch (mode) { case 0: return "Unsynchronized"; case 1: return "Synchronized"; case 2: return "Physical clock stopped"; default: return "unknown"; } } static const char *yesno_str(int val) { return val ? "yes" : "no"; } #define read_sysfs_attr(attr, parm, func, base) \ do { \ path = util_path_sysfs("devices/system/stp/%s", attr); \ ret = func(parm, base, path); \ if (ret) { \ fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno)); \ free(path); \ exit(EXIT_FAILURE); \ } \ free(path); \ } while (0) int main(int argc, char **argv) { struct stp_parms parm = { 0 }; char ctn_id[32] = { 0 }; char *path; int ret, c; util_prg_init(&prg); util_opt_init(opt_vec, NULL); for (;;) { c = util_opt_getopt_long(argc, argv); if (c == -1) break; switch (c) { case 'v': util_prg_print_version(); exit(EXIT_SUCCESS); case 'h': util_prg_print_help(); util_opt_print_help(); exit(EXIT_SUCCESS); default: fprintf(stderr, "Try 'lsstp --help' for more information.\n"); exit(EXIT_FAILURE); } } read_sysfs_attr("online", &parm.online, util_file_read_ui, 10); if (!parm.online) { printf("STP disabled\n"); goto out; } read_sysfs_attr("ctn_id", &parm.ctn_id, util_file_read_ul, 16); read_sysfs_attr("ctn_type", &parm.ctn_type, util_file_read_ui, 10); read_sysfs_attr("stratum", &parm.stratum, util_file_read_ui, 10); read_sysfs_attr("leap_seconds", &parm.leap_seconds, util_file_read_ui, 10); read_sysfs_attr("timing_mode", &parm.timing_mode, util_file_read_ui, 10); read_sysfs_attr("timing_state", &parm.timing_state, util_file_read_ui, 10); read_sysfs_attr("dst_offset", &parm.dst_offset, util_file_read_i, 10); read_sysfs_attr("time_offset", &parm.time_offset, util_file_read_i, 10); read_sysfs_attr("time_zone_offset", &parm.time_zone_offset, util_file_read_i, 10); if (convert_ctn_id((char *)&parm.ctn_id, ctn_id)) snprintf(ctn_id, sizeof(ctn_id)-1, "%016" PRIx64, parm.ctn_id); printf("STP online: %s\n" "CTN ID: %s\n" "CTN type: %s\n" "Stratum: %d\n" "Timing mode: %s\n" "Timing state: %s\n" "DST offset: %d\n" "Timezone offset: %d\n" "Time offset: %d\n" "Active leap seconds: %d\n", yesno_str(parm.online), ctn_id, ctn_type_str(parm.ctn_type), parm.stratum, tmd_to_str(parm.timing_mode), tst_to_str(parm.timing_state), parm.dst_offset, parm.time_zone_offset, parm.time_offset, parm.leap_seconds); printf("Scheduled leap second: "); path = util_path_sysfs("devices/system/stp/leap_seconds_scheduled"); if (util_file_read_va(path, "%d,%d", &parm.leap_seconds_utc, &parm.leap_seconds_diff) == 2 && parm.leap_seconds_diff && parm.leap_seconds_utc) { time_t lsoup = parm.leap_seconds_utc; printf("%s at: %s UTC", parm.leap_seconds_diff > 0 ? "insertion" : "deletion", ctime(&lsoup)); } else { printf("-\n"); } free(path); return 0; out: return 1; } s390-tools-2.38.0/man/000077500000000000000000000000001502674226300142225ustar00rootroot00000000000000s390-tools-2.38.0/man/Makefile000066400000000000000000000006411502674226300156630ustar00rootroot00000000000000include ../common.mak MANS = dumpconf.8 prandom.4 af_iucv.7 all: clean: install: $(MANS) for man in $(MANS); do \ msection=`echo $$man |sed 's/.*\.\([1-8]\)$$/man\1/'` ; \ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 -D $$man $(DESTDIR)$(MANDIR)/$$msection/$$man ; \ done pdf: $(MANS) for man in $(MANS); do \ man -t ./$$man |ps2pdf -sPAPERSIZE=a4 - $${man}.pdf ; \ done .PHONY: all install clean pdf s390-tools-2.38.0/man/af_iucv.7000066400000000000000000000374371502674226300157440ustar00rootroot00000000000000.\" af_iucv.7 .\" .\" .\" Copyright IBM Corp. 2008, 2017 .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" ---------------------------------------------------------------------- .TH AF_IUCV 7 "August 2011" "s390-tools" "Linux Programmer's Manual" .SH NAME AF_IUCV \- Sockets for z/VM IUCV and HiperSockets communication . . . .SH SYNOPSIS .B #include .br .B #include .PP .IB iucv_stream_socket " = socket(AF_IUCV, SOCK_STREAM, 0);" .br .IB iucv_packet_socket " = socket(AF_IUCV, SOCK_SEQPACKET, 0);" . . . .SH DESCRIPTION The AF_IUCV address family provides an addressing mode for communications between applications that run on System z mainframes. This addressing mode can be used for connections through real HiperSockets and through the z/VM Inter-User Communication Vehicle (IUCV). .PP HiperSockets facilitate connections between applications across LPARs within a System z mainframe. In particular, an application running on an instance of Linux on System z can communicate with: .RS 2 .IP "\(bu" 2 Itself .IP "\(bu" 2 Other applications running on the same Linux instance .IP "\(bu" 2 An application on an instance of Linux on System z in another LPAR .RE .PP IUCV facilitates connections between applications across z/VM guest virtual machines within a z/VM system. In particular, an application running on Linux on z/VM can communicate with: .RS 2 .IP "\(bu" 2 Itself .IP "\(bu" 2 Other applications running on the same Linux instance .IP "\(bu" 2 Applications running on other instances of Linux on z/VM within the same z/VM system .IP "\(bu" 2 Applications running on a z/VM guest other than Linux within the same z/VM system .IP "\(bu" 2 The z/VM control program (CP) .RE .PP The AF_IUCV address family supports stream-oriented sockets (\f(CWSOCK_STREAM\fP) and connection-oriented datagram sockets (\f(CWSOCK_SEQPACKET\fP). Stream-oriented sockets can fragment data over several packets. Sockets of type SOCK_SEQPACKET always map a particular socket write or read operation to a single packet. . . .SS Features For all instances of Linux on System z, the AF_IUCV address family provides: .RS 2 .IP "\(bu" 2 Multiple outgoing socket connections for real HiperSockets in layer3 mode .IP "\(bu" 2 Multiple incoming socket connections for real HiperSockets in layer3 mode .RE .PP For instances of Linux on z/VM, the AF_IUCV address family also provides: .RS 2 .IP "\(bu" 2 Multiple outgoing socket connections for IUCV .IP "\(bu" 2 Multiple incoming socket connections for IUCV .IP "\(bu" 2 Socket communication with applications utilizing CMS AF_IUCV support .RE . . . . .SH "ADDRESS FORMAT" An AF_IUCV socket is represented by the following format: .PP .RS 8 .ft CW .nf #define AF_IUCV 32 struct sockaddr_iucv { sa_family_t siucv_family; /* AF_IUCV */ unsigned short siucv_port; /* reserved */ unsigned int siucv_addr; /* reserved */ char siucv_nodeid[8]; /* reserved */ char siucv_user_id[8]; /* user id */ char siucv_name[8]; /* application name */ }; .fi .ft .RE .PP .TP .B siucv_family is set to .BR AF_IUCV (= 32) . .TP .B siucv_port, siucv_addr, siucv_nodeid are reserved for future use. The .B siucv_port and .B siucv_addr fields must be zero. The .B siucv_nodeid field must be set to exactly eight blanks. . .TP .B siucv_user_id specifies a HiperSockets device or a z/VM guest virtual machine. This specification implicitly sets the connection type for the socket to a HiperSockets connection or to a z/VM IUCV connection. This field must be eight characters long and, if necessary, padded with blanks on the right. For HiperSockets connections, the .B siucv_user_id field specifies the identifier that is set with the \fBhsuid\fP sysfs attribute of the HiperSockets device. For .BR bind (2) this is the identifier of a local device, and for .BR connect (2) this is the identifier of the HiperSockets device of the communication peer. For IUCV connections, the .B siucv_user_id field specifies a z/VM user ID. For .BR bind (2) this is the identifier of the local z/VM guest virtual machine, and for .BR connect (2) this is the identifier of the z/VM guest virtual machine for the communication peer. .RS .TP .B Tip: For .BR bind (2) you can also specify eight blanks. The AF_IUCV address family support then automatically substitutes the local z/VM user ID for you. .RE . .TP .B siucv_name is set to the application name by which the socket is known. Servers advertise application names and clients use these application names to connect to servers. This field must be eight characters long, and if necessary, padded with blanks on the right. Similar to TCP or UDP ports, application names distinguish distinct applications on the same operating system instance. Do not call .BR bind (2) for names beginning with \fBlnxhvc\fP. These names are reserved for the z/VM IUCV HVC device driver (see also .BR hvc_iucv (9)). . . . .SH "SOCKET OPTIONS" Socket options can be set with .BR setsockopt (2) and read with .BR getsockopt (2) by specifying \f(CWSOL_IUCV\fP as the socket level. .TP .B SO_IPRMDATA_MSG Enables the application to send up to seven bytes of socket data in the parameter list of an IUCV message. Use this option for IUCV connections to increase performance when transferring small amounts of data. For HiperSockets connections, this option has no effect. To send data in the parameter list, specify a non-zero integer value. .RS .TP .B Note: Use this option with care, older AF_IUCV versions do not support receiving socket data in the parameter list and shut down the socket on which a parameter list message has been received. .RE . .TP .B SO_MSGLIMIT Modifies the message limit for communication paths. The message limit specifies the maximum number of outstanding messages that are allowed for established connections. For IUCV connections this setting can be lowered by z/VM when a connection is established. The message limit is an integer value in range 1 to 65535. The default value is 65535 for IUCV connections and 128 for HiperSockets connections. The message limit must be set before .BR connect "(2) or " listen (2) is called for sockets. .br For sockets that are already connected or listening for connections, the message limit cannot be changed. .br New sockets created by .BR accept (2) inherit the message limit that has been set for the listening socket. .BR getsockopt (2) returns the default message limit or the limit that has been set. For connected sockets, the current message limit is returned. For IUCV connections, there are two parameters that specify the message limit: .BR getsockopt (2) and the z/VM IUCV MSGLIMIT parameter. If the two parameters specify different values for the message limit, the lower value is used. See the "SETUP FOR IUCV CONNECTIONS" section for setting IUCV MSGLIMIT authorizations. . .TP .B SO_MSGSIZE .BR getsockopt (2) returns the maximum message size a bound AF_IUCV socket can handle. The maximum message size for connections through HiperSockets depends on the MTU size of the underlying HiperSockets connection. .br For sockets that are not yet bound the maximum message size cannot be determined. . . .SH "ANCILLARY DATA" Ancillary data is sent and received using .BR sendmsg (2) and .BR recvmsg (2)\fR.\fP To send ancillary data, set the \fBcmsg_level\fP field of struct \fBcmsghdr\fP to \f(CWSOL_IUCV\fP and the \fBcmsg_type\fP field to a type of ancillary data that is supported by the AF_IUCV address family. .br For more information see .BR cmsg (3). Currently, the only supported type is: .TP .B SCM_IUCV_TRGCLS Send or receive IUCV target class information. The IUCV target class can be used to classify and identify an IUCV message at the IUCV protocol level. If the target class is not specified as ancillary data, it is set to zero. The target class is a number of type \fBuint32_t\fP. . . . .SH "SETUP FOR HIPERSOCKETS CONNECTIONS" This section applies to HiperSockets connections and explains the configuration of a HiperSockets device used for AF_IUCV address family support. .PP To run an AF_IUCV socket application using HiperSockets connections, the socket must be bound to a particular HiperSockets device configured with layer3 mode. Use the \f(CWhsuid\fP attribute of a HiperSockets device to identify it to the AF_IUCV address family support. .PP The identifier must adhere to these rules: .RS 2 .IP \(bu 2 It must be 1 to 8 characters. .IP \(bu 2 It must be unique across your environment. .IP \(bu 2 It must not match any z/VM user ID in your environment. .RE .PP To set an identifier, issue a command like this: .PP .RS 8 .ft CW echo \fIidentifier\fP > /sys/devices/qeth/\fI\fP/hsuid .ft .RE .PP You can then address this device by specifying the hsuid as the value for the \fBsiucv_user_id\fP field in the \fBsockaddr_iucv\fP addressing structure. .PP For example, to use "MYHOST01" to bind AF_IUCV sockets to the HiperSockets device with bus-ID 0.0.8000, run: .PP .RS 8 .ft CW .nf echo "MYHOST01" > /sys/devices/qeth/0.0.8000/hsuid .fi .ft .RE . . . .SH "SETUP FOR IUCV CONNECTIONS" This section applies to z/VM IUCV connections and provides an overview of the required IUCV statements for your z/VM guest virtual machines. For details and for general IUCV setup information for z/VM guest virtual machines see .I z/VM CP Programming Services and .IR "z/VM CP Planning and Administration" . . . .SS "Granting IUCV authorizations" Use the .B IUCV directory control statement to grant the necessary authorizations. . .TP .B IUCV ALLOW allows any other z/VM guest virtual machine to establish a communication path with this z/VM guest virtual machine. With this statement, no further authorization is required for the z/VM guest virtual machine that initiates the communication. . .TP .B IUCV ANY allows this z/VM guest virtual machine to establish a communication path with any other z/VM guest virtual machine. . .TP .B IUCV \fIuser_ID\fP allows this z/VM guest virtual machine to establish a communication path to the z/VM guest virtual machine with the z/VM user ID \fIuser_ID\fP. .PP You can specify multiple IUCV statements. To any of these IUCV statements you can append the .B MSGLIMIT \fIlimit\fP parameter. \fIlimit\fP specifies the maximum number of outstanding messages that are allowed for each connection authorized by this statement. If no value is specified for \fBMSGLIMIT\fP, the maximum, 65535, is used. . . .SS "Setting a connection limit" Use the \fBOPTION\fP statement to limit the number of concurrent connections. .TP .B OPTION MAXCONN \fImaxno\fP \fImaxno\fP specifies the maximum number of IUCV connections allowed for this virtual machine. The default is 64. The maximum is 65535. . . .SS "Example" These sample statements allow any z/VM guest virtual machine to connect to your z/VM guest virtual machine with a maximum of 10\^000 outstanding messages for each incoming connection. Your z/VM guest virtual machine is permitted to connect to all other z/VM guest virtual machines. The total number of connections for your z/VM guest virtual machine cannot exceed 100. .ft CW .in +0.25i .nf IUCV ALLOW MSGLIMIT 10000 IUCV ANY OPTION MAXCONN 100 .fi .in -0.25i .ft . . . . .SH ERRORS Several socket operations return error conditions that have a special meaning in the context of AF_IUCV. Those error conditions, and the respective descriptions are listed below. See the manual page of the respective socket operation for a complete list of errors. .TP .B ECONNREFUSED .BR connect (2) called but the target system is not listening on the application name. . .TP .B ENETUNREACH .BR connect (2) called but the target z/VM guest virtual machine is not logged on. Ensure that the z/VM guest virtual machine to which your application wants to connect is logged on. . .TP .B EAGAIN .BR connect (2) called but the maximum number of IUCV connections is exceeded for the calling or for the target z/VM guest virtual machine. This error can be temporary and the application might try again after some time. If the error occurs repeatedly, increase the maximum number of connections (for one or both z/VM guest virtual machines). See the "SETUP FOR IUCV CONNECTIONS" section about the required authorization statement. .B sendmsg (2) called but the maximum number of outstanding messages for the socket connection is reached, for example, if data is available that has not yet been received by the communication peer. If necessary, increase the message limit using the .BR setsockopt (2) function for HiperSockets and IUCV connections. In addition, increase the IUCV message limit as as explained in section "Granting IUCV authorizations". . .TP .B EACCES .BR connect (2) called but the calling z/VM guest virtual machine is missing IUCV authorization. See the "SETUP FOR IUCV CONNECTIONS" section about required IUCV authorizations. . .TP .B ENODEV .BR connect (2) or .BR sendmsg (2) called but the HiperSockets device bound to the AF_IUCV socket does not exist. . .TP .B ENETDOWN .BR connect (2) or .BR sendmsg (2) called but the HiperSockets device bound to the AF_IUCV socket is not activated. . .TP .B EBADFD .BR connect (2) called but for HiperSockets connections the AF_IUCV socket is not bound or, for IUCV connections, the socket is neither in open nor in bound state. .BR bind (2) called but the AF_IUCV socket is no longer in open state. .BR accept (2) called but the AF_IUCV socket is not listening. .BR getsockopt (2) called but the AF_IUCV socket is not bound. .TP .B EINVAL .BR connect (2) or .BR bind (2) called but the \fBsiucv_family\fP field of the specified \fBsockaddr_iucv\fP structure is not set to \fBAF_IUCV\fP. .BR listen (2) called but the AF_IUCV socket has not yet been bound to an address. Always call .BR bind (2) before .BR listen (2). .BR setsockopt (2) called with option \fBSO_MSGLIMIT\fP for sockets that are already connected. . .TP .B ENOPROTOOPT .BR setsockopt (2) or .BR getsockopt (2) called but the socket level has not been set to \f(CWSOL_IUCV\fP, or the specified socket option is not supported. . .TP .B EOPNOTSUPP .BR sendmsg (2) or .BR recvmsg (2) might have been called with the .I MSG_OOB flag set. AF_IUCV does not support sending or receiving \fIout-of-band\fP data on its sockets. For \f(CWSOCK_SEQPACKET\fP sockets, .BR sendmsg (2) called without the .I MSG_EOR flag set. AF_IUCV does not support segmentation, and thus, the "end-of-record" (\fIMSG_EOR\fP) flag must always be set. . .TP .B EPROTONOSUPPORT .BR socket (2) called with a protocol that is not supported. The socket protocol parameter must be either zero or \f(CWPF_IUCV\fP. . .TP .B EAFNOSUPPORT .BR socket (2) called with \f(CWAF_IUCV\fP but the AF_IUCV address family is not supported by the current Linux kernel. Ensure that your Linux kernel has been compiled with support for the latest version of the AF_IUCV address family. . .TP .B EADDRINUSE .BR bind (2) called with an \fBsiucv_name\fP already used for another AF_IUCV socket. . .PP Other errors can be generated by the generic socket layer. See the respective manual pages for more information. . . . .SH "SEE ALSO" .BR connect (2), .BR recvmsg (2), .BR sendmsg (2), .BR socket (2), .BR setsockopt (2), .BR getsockopt (2), .BR cmsg (3), .BR socket (7) .I "Linux on System z - Device Drivers, Features, and Commands" .br .I "z/VM CP Planning and Administration" .br .I "z/VM CP Programming Services" . . . .SH "HISTORY" .TP .B AF_IUCV, version 1.0 .RS 4 .IP "\(bu" 2 Initial version. .RE . .TP .B AF_IUCV, version 1.1 .RS 4 .IP "\(bu" 2 Support for sending socket data in the parameter list of an IUCV message (\f(CWSO_IPRMDATA_MSG\fP). .IP "\(bu" 2 Access the target class of an IUCV message as ancillary data using .BR sendmsg "(2) and " recvmsg (2). .IP "\(bu" 2 Support for \f(CWSOCK_SEQPACKET\fP sockets to facilitate development of native IUCV applications that interact with AF_IUCV. .RE . .TP .B AF_IUCV, version 1.2 .RS 4 .IP "\(bu" 2 Support for HiperSockets connections. .RE s390-tools-2.38.0/man/dumpconf.8000066400000000000000000000107051502674226300161310ustar00rootroot00000000000000.\" Copyright 2017 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH DUMPCONF 8 "Sept 2011" "s390-tools" .SH NAME dumpconf \- Configure panic and PSW restart actions for Linux on System z .SH SYNOPSIS .br \fBdumpconf\fR [start|stop|status] .br \fBdumpconf\fR [-h|-v] .SH DESCRIPTION \fBdumpconf\fR reads the /etc/sysconfig/dumpconf file and establishes the action to be taken if a kernel panic occurs or PSW restart is triggered. The following keywords can be used in the dumpconf file: .TP \fB - ON_PANIC:\fR Shutdown action in case of a kernel panic or a PSW restart. Possible values are 'dump', 'reipl', 'dump_reipl', 'stop' and 'vmcmd': .br dump: Trigger dump according to the configuration in /etc/sysconfig/dumpconf. .br reipl: Trigger re-IPL according to the configuration under /sys/firmware/reipl. .br dump_reipl: First trigger dump according to the configuration in /etc/sysconfig/dumpconf, then trigger re-IPL according to the configuration under /sys/firmware/reipl. .br stop: Stop Linux and enter disabled wait (default). .br vmcmd: Trigger CP command according to the 'VMCMD_X' configuration in /etc/sysconfig/dumpconf. .TP \fB - DUMP_TYPE:\fR Type of dump device. Possible values are 'ccw', 'eckd', 'fcp' and 'nvme'. .TP \fB - DEVICE:\fR Device number of dump device. .TP \fB - WWPN\fR WWPN for SCSI dump device. .TP \fB - LUN\fR LUN for SCSI dump device. .TP \fB - FID\fR Function ID for NVMe dump device. .TP \fB - NSID\fR Namespace ID for NVMe dump device. .TP \fB - BOOTPROG:\fR Boot program selector. .TP \fB - BR_CHR:\fR Boot record location in "C,H,R" format (comma separated values for Cylinder, Head and Record) or "auto". .TP \fB - BR_LBA:\fR Boot record logical block address. .TP \fB - SCP_DATA:\fR SCP data for SCSI, NVMe and ECKD dump devices. .TP \fB - VMCMD_1, VMCMD_2 ... VMCMD_8:\fR Up to eight CP commands, which are executed in case of a kernel panic or PSW restart. .TP \fB - DELAY_MINUTES:\fR Number of minutes the activation of dumpconf is to be delayed. If this keyword is omitted, the default is zero, which means that dumpconf activates immediately during system startup. Specify a non-zero delay time only if you specified shutdown action "reipl" or "dump_reipl". These actions might cause a reboot loop if the Linux kernel crashes persistently during (or shortly after) each reboot. A non-zero delay time causes dumpconf to sleep in the background until the delay time has expired. In this case messages are written to /var/log/messages. By default (DELAY_MINUTES is omitted or zero) dumpconf runs in the foreground and informational messages are written to sysout, while error messages are written to syserr. Example: If you specified DELAY_MINUTES=10 and your Linux system crashes within 10 minutes after the reboot, then dumpconf is not yet active and the default action (stop) is triggered. .SH COMMANDS .TP \fBstart\fR Enable configuration defined in /etc/sysconfig/dumpconf. .TP \fBstop\fR Disable dump configuration. .TP \fBstatus\fR Show current configuration. .SH OPTIONS .TP \fB-h\fR or \fB--help\fR Print usage information, then exit. .TP \fB-v\fR or \fB--version\fR Print version information, then exit. .SH PSW Restart PSW Restart can be triggered by the operator under z/VM with the CP command "#cp system restart" and under LPAR on the HMC with "Recovery/PSW Restart". .SH EXAMPLES: The following are examples of the /etc/sysconfig/dumpconf file: .br # .br # Example configuration for a CCW dump device (DASD) .br # .br ON_PANIC=dump_reipl .br DUMP_TYPE=ccw .br DEVICE=0.0.1234 .br DELAY_MINUTES=5 .br # .br # Example configuration for an ECKD dump device (DASD) .br # .br ON_PANIC=dump .br DUMP_TYPE=eckd .br DEVICE=0.0.1004 .br BOOTPROG=0 .br BR_CHR=auto .br # .br # Example configuration for an FCP dump device (SCSI Disk) .br # .br ON_PANIC=dump .br DUMP_TYPE=fcp .br DEVICE=0.0.2345 .br WWPN=0x5005076303004712 .br LUN=0x4713000000000000 .br BOOTPROG=0 .br BR_LBA=0 .br # .br # Example configuration for an NVMe dump device (NVMe Disk) .br # .br ON_PANIC=dump .br DUMP_TYPE=nvme .br FID=0x0300 .br NSID=0x0001 .br BOOTPROG=0 .br BR_LBA=0 .br # .br # Example configuration for CP commands .br # .br ON_PANIC=vmcmd .br VMCMD_1="MESSAGE * Starting VMDUMP" .br VMCMD_2="VMDUMP" .br VMCMD_3="IPL 3456" # .br # Example config for re-IPL .br # .br ON_PANIC=reipl .br DELAY_MINUTES=5 .SH SEE ALSO Linux on System z: Using the Dump Tools s390-tools-2.38.0/man/prandom.4000066400000000000000000000026331502674226300157530ustar00rootroot00000000000000.\" Copyright IBM Corp. 2007, 2017 .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH PRANDOM 4 "Jan 2007" "s390-tools" .SH NAME prandom \- kernel pseudo random number generator device for s390 .SH DESCRIPTION The character special file \fI/dev/prandom\fP provides an interface to the pseudo random number generator. The s390 pseudo random number generator uses the hardware accelerated cryptographic assist functions which are integrated in the CPU. \fI/dev/prandom\fP is available starting with the z9 processor. .LP Reading from \fI/dev/prandom\fP is non-blocking. Any amount of data could be read from the device. .LP The s390 pseudo random number generator provides cryptographically secure pseudo random numbers following the algorithm in ANSI X9.17. Entropy is added periodically to the generator to protect against a compromised key. .LP .SH CONFIGURATION The \fI/dev/prandom\fP device node is generated by udev while loading the corresponding kernel module or while booting a kernel with the generator built-in. By default \fI/dev/prandom\fP is readable only by root. If it should be readable by every user add the following to /etc/rules.d/50-udev.rules: .nf KERNEL=="prandom", MODE="0444", OPTIONS="last_rule" .fi .SH FILES /dev/prandom .SH SEE ALSO Linux on zSeries: Device Drivers, Features and Commands s390-tools-2.38.0/mon_tools/000077500000000000000000000000001502674226300154605ustar00rootroot00000000000000s390-tools-2.38.0/mon_tools/Makefile000066400000000000000000000010731502674226300171210ustar00rootroot00000000000000include ../common.mak ALL_CFLAGS += -Wno-address-of-packed-member all: mon_fsstatd mon_procd mon_fsstatd: mon_fsstatd.o mon_procd: mon_procd.o install: all $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 mon_fsstatd \ $(DESTDIR)$(USRSBINDIR) $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 mon_fsstatd.8 \ $(DESTDIR)$(MANDIR)/man8 $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 mon_procd \ $(DESTDIR)$(USRSBINDIR) $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 mon_procd.8 \ $(DESTDIR)$(MANDIR)/man8 clean: rm -f *.o *~ mon_fsstatd mon_procd core .PHONY: all install clean s390-tools-2.38.0/mon_tools/mon_fsstatd.8000066400000000000000000000015121502674226300200710ustar00rootroot00000000000000.\" Copyright 2017 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH MON_FSSTATD 8 "Dec 2006" "s390-tools" .SH NAME mon_fsstatd \- Filesystem statistics monitor. .SH SYNOPSIS \fBmon_fsstatd\fR [-h] [-v] [-a] [-i \fI\fR] .SH DESCRIPTION \fBmon_fsstatd\fR is a daemon that writes filesystem utilization data to the z/VM monitor stream. .SH OPTIONS .TP \fB-h\fR or \fB--help\fR Print usage message and exit. .TP \fB-v\fR or \fB--version\fR Print Version information and exit. .TP \fB-a\fR or \fB--attach\fR Run in foreground. .TP \fB-i\fR \fI\fR or \fB--interval\fR=\fI\fR Set polling interval in seconds. .SH AUTHOR .nf This man-page was written by Melissa Howland . .fi s390-tools-2.38.0/mon_tools/mon_fsstatd.c000066400000000000000000000267731502674226300201640ustar00rootroot00000000000000/* * mon_fsstatd - Write file system utilization data to the z/VM monitor stream * * Copyright IBM Corp. 2006, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mon_fsstatd.h" static int attach; static int mw_dev; static char small_mon_record[SMALL_MON_RECORD_LEN]; static char large_mon_record[LARGE_MON_RECORD_LEN]; static long sample_interval = 60; static const char *pid_file = "/run/mon_fsstatd.pid"; struct mw_name_lens { __u16 mw_name_len; __u16 mw_dir_len; __u16 mw_type_len; __u16 mw_fsdata_len; __u16 mw_total; }; /* * Clean up when SIGTERM or SIGINT received */ void stop_fsstatd(int UNUSED(a)) { remove(pid_file); exit(0); } /* * Set up for handling SIGTERM or SIGINT */ static void fsstatd_handle_signals(void) { struct sigaction act; act.sa_flags = 0; sigemptyset(&act.sa_mask); act.sa_handler = stop_fsstatd; if (sigaction(SIGTERM, &act, NULL) < 0) { fprintf(stderr, "sigaction( SIGTERM, ... ) failed - " "reason %s\n", strerror(errno)); exit(1); } act.sa_handler = stop_fsstatd; if (sigaction(SIGINT, &act, NULL) < 0) { fprintf(stderr, "sigaction( SIGINT, ... ) failed - " "reason %s\n", strerror(errno)); exit(1); } } /* * Open /dev/monwriter */ static void fsstatd_open_monwriter(void) { mw_dev = open("/dev/monwriter", O_EXCL | O_RDWR); if (mw_dev == -1) { printf("cannot open /dev/monwriter: %s\n", strerror(errno)); exit(1); } } /* * Store daemon's pid so it can be stopped */ static int store_pid(void) { FILE *f = fopen(pid_file, "w"); if (!f) { syslog(LOG_ERR, "cannot open pid file %s: %s", pid_file, strerror(errno)); return -1; } fprintf(f, "%d\n", getpid()); fclose(f); return 0; } /* * Stop sampling of any buffers that are not longer needed */ static void stop_unused(int curr_max, int prev_max) { struct monwrite_hdr *mw_hdrp; int i; mw_hdrp = (struct monwrite_hdr *)small_mon_record; mw_hdrp->mon_function = MONWRITE_STOP_INTERVAL; mw_hdrp->applid = FSSTATD_APPLID; mw_hdrp->hdrlen = sizeof(struct monwrite_hdr); mw_hdrp->datalen = 0; for (i = 0; i < prev_max - curr_max; i += 2) { mw_hdrp->mod_level = curr_max + i; if (write(mw_dev, mw_hdrp, sizeof(struct monwrite_hdr)) == -1) syslog(LOG_ERR, "write error on STOP: %s\n", strerror(errno)); } } /* * Calculate lengths of data to be written to monitor stream */ static struct mw_name_lens fsstatd_get_lens(struct mntent *ent) { struct mw_name_lens name_lens; name_lens.mw_name_len = strlen(ent->mnt_fsname); name_lens.mw_dir_len = strlen(ent->mnt_dir); name_lens.mw_type_len = strlen(ent->mnt_type); /* if name & dir too long to fit both, truncate them */ if (name_lens.mw_name_len + name_lens.mw_dir_len + name_lens.mw_type_len > MAX_NAMES_LEN) { if (name_lens.mw_name_len > MAX_NAME_LEN) name_lens.mw_name_len = MAX_NAME_LEN; if (name_lens.mw_dir_len > MAX_DIR_LEN) name_lens.mw_dir_len = MAX_DIR_LEN; if (name_lens.mw_type_len > MAX_TYPE_LEN) name_lens.mw_type_len = MAX_TYPE_LEN; } /* total fs data to be written */ name_lens.mw_fsdata_len = sizeof(__u16) + name_lens.mw_name_len + sizeof(__u16) + name_lens.mw_dir_len + sizeof(__u16) + name_lens.mw_type_len + sizeof(struct fsstatd_data); /* total monitor data to be written in monitor record */ name_lens.mw_total = sizeof(struct fsstatd_hdr) + name_lens.mw_fsdata_len; return name_lens; } /* * Write fs data for ent to monitor stream */ static void fsstatd_write_ent(struct mntent *ent, time_t curr_time, int *small_maxp, int *big_maxp, struct statvfs buf) { struct monwrite_hdr *mw_hdrp; struct fsstatd_hdr *mw_fshdrp; struct fsstatd_data *mw_fsdatap; char *mw_tmpp; char *mw_bufp; struct mw_name_lens mw_lens; int write_len; mw_lens = fsstatd_get_lens(ent); if ((mw_lens.mw_total + sizeof(struct monwrite_hdr)) <= sizeof(small_mon_record)) { mw_bufp = small_mon_record; memset(&small_mon_record, 0, sizeof(small_mon_record)); mw_hdrp = (struct monwrite_hdr *)mw_bufp; mw_hdrp->datalen = sizeof(small_mon_record) - sizeof(struct monwrite_hdr); write_len = sizeof(small_mon_record); mw_hdrp->mod_level = *small_maxp; *small_maxp += 2; } else { mw_bufp = large_mon_record; memset(&large_mon_record, 0, sizeof(large_mon_record)); mw_hdrp = (struct monwrite_hdr *)mw_bufp; mw_hdrp->datalen = sizeof(large_mon_record) - sizeof(struct monwrite_hdr); write_len = sizeof(large_mon_record); mw_hdrp->mod_level = *big_maxp; *big_maxp += 2; } /* fill in rest of monwrite_hdr */ mw_tmpp = mw_bufp; mw_hdrp->applid = FSSTATD_APPLID; mw_hdrp->hdrlen = sizeof(struct monwrite_hdr); mw_hdrp->mon_function = MONWRITE_START_INTERVAL; /* fill in fsstatd_hdr */ mw_tmpp += sizeof(struct monwrite_hdr); mw_fshdrp = (struct fsstatd_hdr *)mw_tmpp; mw_fshdrp->time_stamp = (__u64) curr_time; mw_fshdrp->fsstat_data_len = (__u16) mw_lens.mw_fsdata_len; mw_fshdrp->fsstat_data_offset = (__u16) sizeof(struct fsstatd_hdr); /* fill in fs name, dir name and fs type and lengths */ mw_tmpp += sizeof(struct fsstatd_hdr); memcpy(mw_tmpp, &mw_lens.mw_name_len, sizeof(__u16)); mw_tmpp += sizeof(__u16); strncpy(mw_tmpp, ent->mnt_fsname, mw_lens.mw_name_len); mw_tmpp += mw_lens.mw_name_len; memcpy(mw_tmpp, &mw_lens.mw_dir_len, sizeof(__u16)); mw_tmpp += sizeof(__u16); strncpy(mw_tmpp, ent->mnt_dir, mw_lens.mw_dir_len); mw_tmpp += mw_lens.mw_dir_len; memcpy(mw_tmpp, &mw_lens.mw_type_len, sizeof(__u16)); mw_tmpp += sizeof(__u16); strncpy(mw_tmpp, ent->mnt_type, mw_lens.mw_type_len); /* fill in fsstatd_data */ mw_tmpp += mw_lens.mw_type_len; mw_fsdatap = (struct fsstatd_data *)mw_tmpp; mw_fsdatap->fs_bsize = (__u64)buf.f_bsize; mw_fsdatap->fs_frsize = (__u64) buf.f_frsize; mw_fsdatap->fs_blocks = (__u64) buf.f_blocks; mw_fsdatap->fs_bfree = (__u64) buf.f_bfree; mw_fsdatap->fs_bavail = (__u64) buf.f_bavail; mw_fsdatap->fs_files = (__u64) buf.f_files; mw_fsdatap->fs_ffree = (__u64) buf.f_ffree; mw_fsdatap->fs_favail = (__u64) buf.f_favail; mw_fsdatap->fs_flag = (__u64) buf.f_flag; if (write(mw_dev, mw_bufp, write_len) == -1) syslog(LOG_ERR, "write error: %s\n", strerror(errno)); } /* * Run as background process */ static void fsstatd_daemonize(void) { int pipe_fds[2], startup_rc = 1; pid_t pid; if (pipe(pipe_fds) == -1) { syslog(LOG_ERR, "pipe error: %s\n", strerror(errno)); exit(1); } /* Fork off the parent process */ pid = fork(); if (pid < 0) { syslog(LOG_ERR, "fork error: %s\n", strerror(errno)); exit(1); } if (pid > 0) { /* Wait for startup return code from daemon */ if (read(pipe_fds[0], &startup_rc, sizeof(startup_rc)) == -1) syslog(LOG_ERR, "pipe read error: %s\n", strerror(errno)); /* With startup_rc == 0, pid file was written at this point */ exit(startup_rc); } /* Change the file mode mask */ umask(0); /* Catch SIGINT and SIGTERM to clean up pid file on exit */ fsstatd_handle_signals(); /* Create a new SID for the child process */ if (setsid() < 0) { syslog(LOG_ERR, "setsid error: %s\n", strerror(errno)); goto notify_parent; } /* Change the current working directory */ if (chdir("/") < 0) { syslog(LOG_ERR, "chdir error: %s\n", strerror(errno)); goto notify_parent; } /* Close out the standard file descriptors */ close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); /* Store daemon pid */ if (store_pid() < 0) goto notify_parent; startup_rc = 0; notify_parent: /* Inform waiting parent about startup return code */ if (write(pipe_fds[1], &startup_rc, sizeof(startup_rc)) == -1) { syslog(LOG_ERR, "pipe write error: %s\n", strerror(errno)); exit(1); } if (startup_rc != 0) exit(startup_rc); } static int fsstatd_do_work(void) { FILE *mnttab; time_t curr_time; struct statvfs buf; struct mntent *ent; int result; int curr_small_max, prev_small_max; int curr_big_max, prev_big_max; /* * small buffers use even mod_levels, * big buffers use odd mod_levels */ prev_small_max = 0; prev_big_max = 1; syslog(LOG_INFO, "sample interval: %lu\n", sample_interval); while (1) { time(&curr_time); mnttab = fopen("/etc/mtab", "r"); if (mnttab == NULL) { syslog(LOG_ERR, "cannot open /etc/mtab: %s\n", strerror(errno)); break; } curr_small_max = 0; curr_big_max = 1; ent = getmntent(mnttab); if (ent == NULL) { syslog(LOG_ERR, "getmntent error: %s\n", strerror(errno)); fclose(mnttab); break; } while (ent) { /* Only sample physical filesystem size data */ if ((strncmp(ent->mnt_type, "autofs", 6) == 0 || strncmp(ent->mnt_type, "none", 4) == 0 || strncmp(ent->mnt_type, "proc", 4) == 0 || strncmp(ent->mnt_type, "subfs", 5) == 0 || strncmp(ent->mnt_type, "nfsd", 4) == 0 || strncmp(ent->mnt_type, "tmpfs", 5) == 0 || strncmp(ent->mnt_type, "sysfs", 5) == 0 || strncmp(ent->mnt_type, "pstore", 6) == 0 || strncmp(ent->mnt_type, "cgroup", 6) == 0 || strncmp(ent->mnt_type, "mqueue", 6) == 0 || strncmp(ent->mnt_type, "devpts", 6) == 0 || strncmp(ent->mnt_type, "debugfs", 7) == 0 || strncmp(ent->mnt_type, "devtmpfs", 8) == 0 || strncmp(ent->mnt_type, "configfs", 8) == 0 || strncmp(ent->mnt_type, "selinuxfs", 9) == 0 || strncmp(ent->mnt_type, "hugetlbfs", 9) == 0 || strncmp(ent->mnt_type, "securityfs", 10) == 0 || strncmp(ent->mnt_type, "rpc_pipefs", 10) == 0 || strncmp(ent->mnt_type, "binfmt_misc", 11) == 0 || strncmp(ent->mnt_type, "ignore", 6) == 0)) { ent = getmntent(mnttab); continue; } result = statvfs(ent->mnt_dir, &buf); if (result != 0) { syslog(LOG_ERR, "statvfs error on %s: %s\n", ent->mnt_dir, strerror(errno)); ent = getmntent(mnttab); continue; } if (buf.f_blocks > 0) fsstatd_write_ent(ent, curr_time, &curr_small_max, &curr_big_max, buf); ent = getmntent(mnttab); } if (curr_small_max < prev_small_max) stop_unused(curr_small_max, prev_small_max); if (curr_big_max < prev_big_max) stop_unused(curr_big_max, prev_big_max); prev_small_max = curr_small_max; prev_big_max = curr_big_max; fclose(mnttab); sleep(sample_interval); } return 1; } /* Parse options */ static int parse_options(int argc, char **argv) { int opt; do { opt = getopt_long(argc, argv, opt_string, options, NULL); switch (opt) { case -1: /* Reached end of parameter list. */ break; case 'h': printf("%s", help_text); exit(0); case 'v': printf("mon_fsstatd: version %s\n", RELEASE_STRING); printf("Copyright IBM Corp. 2006, 2017\n"); exit(0); case 'a': attach = 1; break; case 'i': sample_interval = strtol(optarg, NULL, 10); if (sample_interval <= 0) { fprintf(stderr, "Error: Invalid interval " "(needs to be greater than 0)\n"); return(1); } break; default: fprintf(stderr, "Try ' --help' for more" " information.\n"); return(1) ; } } while (opt != -1); return(0); } int main(int argc, char **argv) { int rc; rc = parse_options(argc, argv); if (rc > 0) return rc; fsstatd_open_monwriter(); openlog("mon_fsstatd", 0, LOG_DAEMON); if (!attach) fsstatd_daemonize(); rc = fsstatd_do_work(); close(mw_dev); return rc; } s390-tools-2.38.0/mon_tools/mon_fsstatd.h000066400000000000000000000041551502674226300201570ustar00rootroot00000000000000/* * mon_fsstatd - Write file system utilization data to the z/VM monitor stream * * Definitions used by mon_fsstatd * * Copyright IBM Corp. 2006, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef __mon_fsstatd_h__ #define __mon_fsstatd_h__ #include #include #include #include "lib/zt_common.h" /* mon_function values */ #define MONWRITE_START_INTERVAL 0x00 /* start interval recording */ #define MONWRITE_STOP_INTERVAL 0x01 /* stop interval or config recording */ #define MAX_REC_LEN 4010 #define MAX_NAMES_LEN 3900 #define MAX_NAME_LEN 1800 #define MAX_DIR_LEN 1800 #define MAX_TYPE_LEN 256 #define FSSTATD_APPLID 0x01 /* Assume usually lengths of name, dir and type <= 512 bytes total */ #define SMALL_MON_RECORD_LEN 602 #define LARGE_MON_RECORD_LEN 4010 struct monwrite_hdr { unsigned char mon_function; unsigned short applid; unsigned char record_num; unsigned short version; unsigned short release; unsigned short mod_level; unsigned short datalen; unsigned char hdrlen; } __attribute__((packed)); struct fsstatd_hdr { __u64 time_stamp; __u16 fsstat_data_len; __u16 fsstat_data_offset; } __attribute__((packed)); struct fsstatd_data { __u64 fs_bsize; __u64 fs_frsize; __u64 fs_blocks; __u64 fs_bfree; __u64 fs_bavail; __u64 fs_files; __u64 fs_ffree; __u64 fs_favail; __u64 fs_flag; }; static struct option options[] = { {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'v'}, {"attach", no_argument, NULL, 'a'}, {"interval", required_argument, NULL, 'i'}, {NULL, 0, NULL, 0} }; static const char opt_string[] = "+hvai:"; static const char help_text[] = "mon_fsstatd: Daemon that writes file system utilization information\n" "to the z/VM monitor stream.\n" "\n" "Usage: mon_fstatd [OPTIONS]\n" "\n" "Options:\n" "-h, --help Print this help, then exit\n" "-v, --version Print version information, then exit\n" "-a, --attach Run in foreground\n" "-i, --interval= Sample interval\n"; #endif s390-tools-2.38.0/mon_tools/mon_procd.8000066400000000000000000000014371502674226300175360ustar00rootroot00000000000000.\" Copyright 2017 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH MON_PROCD 8 "MAR 2007" "s390-tools" .SH NAME mon_procd \- Process data monitor. .SH SYNOPSIS \fBmon_procd\fR [-h] [-v] [-a] [-i \fI\fR] .SH DESCRIPTION \fBmon_procd\fR is a daemon that writes process data to the z/VM monitor stream. .SH OPTIONS .TP \fB-h\fR or \fB--help\fR Print usage message and exit. .TP \fB-v\fR or \fB--version\fR Print Version information and exit. .TP \fB-a\fR or \fB--attach\fR Run in foreground. .TP \fB-i\fR \fI\fR or \fB--interval\fR=\fI\fR Set polling interval in seconds. .SH AUTHOR .nf This man-page was written by Hongjie Yang . .fi s390-tools-2.38.0/mon_tools/mon_procd.c000066400000000000000000000627751502674226300176250ustar00rootroot00000000000000/* * mon_procd - Write process data to the z/VM monitor stream * * Copyright IBM Corp. 2007, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mon_procd.h" struct name_lens_t { __u16 ruser_len; __u16 euser_len; __u16 egroup_len; __u16 wchan_len; __u16 cmd_len; __u16 cmdline_len; }; static int num_cpus; static int curr_small_max, curr_big_max; static int prev_small_max, prev_big_max; static unsigned int pg_to_kb_shift, sort_tbl_size; static float e_time; static struct timeval prev_time, curr_time; static struct cpudata_t cpudata; static struct proc_sum_t proc_sum; static struct task_sort_t *prev_sort_tbl, *curr_sort_tbl; static struct name_lens_t name_lens; static int attach; static int mw_dev; static char *temp; static char fname[32]; static char buf[BUF_SIZE]; static char mon_record[MAX_REC_LEN]; static long sample_interval = 60; static const char *pid_file = "/run/mon_procd.pid"; /* * Clean up when SIGTERM or SIGINT received */ void stop_procd(int UNUSED(a)) { remove(pid_file); exit(0); } /* * Set up for handling SIGTERM or SIGINT */ static void procd_handle_signals(void) { struct sigaction act; act.sa_flags = 0; sigemptyset( &act.sa_mask ); act.sa_handler = stop_procd; if (sigaction(SIGTERM, &act, NULL) < 0) { fprintf(stderr, "sigaction( SIGTERM, ... ) failed - " "reason %s\n", strerror( errno ) ); exit(1); } act.sa_handler = stop_procd; if (sigaction(SIGINT, &act, NULL) < 0) { fprintf(stderr, "sigaction( SIGINT, ... ) failed - " "reason %s\n", strerror( errno ) ); exit(1); } } /* * Open /dev/monwriter */ static void procd_open_monwriter(void) { mw_dev = open("/dev/monwriter", O_EXCL | O_RDWR); if (mw_dev == -1) { printf("cannot open /dev/monwriter: %s\n", strerror(errno)); exit(1); } } /* * Store daemon's pid so it can be stopped */ static int store_pid(void) { FILE *f = fopen(pid_file, "w"); if (!f) { syslog(LOG_ERR, "cannot open pid file %s: %s", pid_file, strerror(errno)); return -1; } fprintf(f, "%d\n", getpid()); fclose(f); return 0; } /* * Stop sampling of any buffers that are not longer needed */ static void stop_unused(int curr_max, int prev_max) { struct monwrite_hdr *mw_hdrp; int i; mw_hdrp = (struct monwrite_hdr *)mon_record; mw_hdrp->applid = PROCD_APPLID; mw_hdrp->record_num = TASK_FLAG; mw_hdrp->hdrlen = sizeof(struct monwrite_hdr); mw_hdrp->mon_function = MONWRITE_STOP_INTERVAL; mw_hdrp->datalen = 0; for (i = 0; i < prev_max - curr_max; i += 2) { mw_hdrp->mod_level = curr_max + i; if (write(mw_dev, mw_hdrp, sizeof(struct monwrite_hdr)) == -1) syslog(LOG_ERR, "write error on STOP: %s\n", strerror(errno)); } } /* * Write process data to monitor stream */ static void procd_write_ent(void *entry, int size, char flag) { struct monwrite_hdr *mw_hdrp; struct procd_hdr *mw_pchdrp; char *mw_tmpp; char *mw_bufp; int write_len; mw_bufp = mon_record; mw_hdrp = (struct monwrite_hdr *)mw_bufp; write_len = size + sizeof(struct monwrite_hdr) + sizeof(struct procd_hdr); /* fill in monwrite_hdr */ if (flag == TASK_FLAG) { if (write_len <= SMALL_MON_RECORD_LEN) { write_len = SMALL_MON_RECORD_LEN; mw_hdrp->mod_level = curr_small_max; curr_small_max += 2; } else { write_len = MAX_REC_LEN; mw_hdrp->mod_level = curr_big_max; curr_big_max += 2; } } else { mw_hdrp->mod_level = 0; } mw_hdrp->datalen = write_len - sizeof(struct monwrite_hdr); mw_hdrp->applid = PROCD_APPLID; mw_hdrp->record_num = flag; mw_hdrp->hdrlen = sizeof(struct monwrite_hdr); mw_hdrp->mon_function = MONWRITE_START_INTERVAL; /* fill in procd_hdr */ mw_tmpp = mw_bufp + sizeof(struct monwrite_hdr); mw_pchdrp = (struct procd_hdr *)mw_tmpp; mw_pchdrp->time_stamp = (__u64) curr_time.tv_sec; mw_pchdrp->data_len = (__u16) size; mw_pchdrp->data_offset = (__u16) sizeof(struct procd_hdr); /* fill in entry information */ mw_tmpp += sizeof(struct procd_hdr); if (flag == SUM_FLAG) memcpy(mw_tmpp, entry, size); else memcpy(mw_tmpp, entry, sizeof(struct task_t)); if (write(mw_dev, mw_bufp, write_len) == -1) syslog(LOG_ERR, "write error: %s\n", strerror(errno)); } /* * Open and read a file into a buffer, terminated with buf[size/num] = '\0' */ static int read_file(char *fname, char *buf, int size) { int num, fp; fp = open(fname, O_RDONLY); if (!fp) return -1; num = read(fp, buf, size); if (num < 0) { close(fp); return -1; } buf[num] = '\0'; close(fp); return num; } /* * Get uptime */ static void read_uptime(void) { double uptm; if (read_file("/proc/uptime", buf, sizeof(buf) - 1) <= 0) return; if (sscanf(buf, "%lf ", &uptm) < 1) { syslog(LOG_ERR, "bad data in %s \n", fname); return; } proc_sum.uptime = (__u64)uptm; } /* * Get number of users */ static void count_users(void) { struct utmp *temp; proc_sum.users = 0; setutent(); while ((temp = getutent())) { if (temp->ut_type == USER_PROCESS) proc_sum.users++; } endutent(); } /* * Get load averages */ static void read_loadavg(void) { int ret_num; if (read_file("/proc/loadavg", buf, sizeof(buf) - 1) <= 0) return; ret_num = sscanf(buf, "%s %s %s", proc_sum.loadavg_1, proc_sum.loadavg_5, proc_sum.loadavg_15); if (ret_num < 3) syslog(LOG_ERR, "bad data in %s \n", fname); } /* * Calculate the state percentages for a CPU */ static void cal_cpu(struct cpudata_t *cpudt, struct cpu_t *cpu) { __u64 total_time; total_time = cpudt->usr - cpudt->usr_prev; total_time += cpudt->sys - cpudt->sys_prev; total_time += cpudt->nice - cpudt->nice_prev; if (cpudt->idle - cpudt->idle_prev > 0) total_time += cpudt->idle - cpudt->idle_prev; total_time += cpudt->iowt - cpudt->iowt_prev; total_time += cpudt->irq - cpudt->irq_prev; total_time += cpudt->sirq - cpudt->sirq_prev; total_time += cpudt->steal - cpudt->steal_prev; if (total_time < 1) total_time = 1; cpu->num_cpus = cpudt->id; cpu->puser = (__u16)((cpudt->usr - cpudt->usr_prev) * 10000 / total_time); cpu->psystem = (__u16)((cpudt->sys - cpudt->sys_prev) * 10000 / total_time); cpu->pnice = (__u16)((cpudt->nice - cpudt->nice_prev) * 10000 / total_time); if (cpudt->idle - cpudt->idle_prev > 0) cpu->pidle = (__u16)((cpudt->idle - cpudt->idle_prev) * 10000 / total_time); else cpu->pidle = 0; cpu->piowait = (__u16)((cpudt->iowt - cpudt->iowt_prev) * 10000 / total_time); cpu->pirq = (__u16)((cpudt->irq - cpudt->irq_prev) * 10000 / total_time); cpu->psoftirq = (__u16)((cpudt->sirq - cpudt->sirq_prev) * 10000 / total_time); cpu->psteal = (__u16)((cpudt->steal - cpudt->steal_prev) * 10000 / total_time); cpudt->usr_prev = cpudt->usr; cpudt->sys_prev = cpudt->sys; cpudt->nice_prev = cpudt->nice; cpudt->idle_prev = cpudt->idle; cpudt->iowt_prev = cpudt->iowt; cpudt->irq_prev = cpudt->irq; cpudt->sirq_prev = cpudt->sirq; cpudt->steal_prev = cpudt->steal; } /* * Get CPU summary information */ static void read_cpu(void) { unsigned long long u, n, s, i, w, x, y, z; if (read_file("/proc/stat", buf, sizeof(buf) - 1) <= 0) return; u = n = s = i = w = x = y = z = 0; temp = strstr(buf, "cpu"); if (temp) sscanf(temp, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu", &u, &n, &s, &i, &w, &x, &y, &z); else syslog(LOG_ERR, "no cpu in /proc/stat\n"); cpudata.usr = (__u64)u; cpudata.nice = (__u64)n; cpudata.sys = (__u64)s; cpudata.idle = (__u64)i; cpudata.iowt = (__u64)w; cpudata.irq = (__u64)x; cpudata.sirq = (__u64)y; cpudata.steal = (__u64)z; cpudata.id = (__u32)num_cpus; cal_cpu(&cpudata, &proc_sum.cpu); } /* * Get memory information */ static void read_mem(void) { unsigned long long mtotal, mfree, mbuf; unsigned long long stotal, sfree, scached; if (read_file("/proc/meminfo", buf, sizeof(buf) - 1) <= 0) return; mtotal = mfree = mbuf = stotal = sfree = scached = 0; temp = strstr(buf, "MemTotal"); if (temp) sscanf(temp, "MemTotal: %Lu kB", &mtotal); else syslog(LOG_ERR, "no MemTotal in /proc/meminfo\n"); temp = strstr(buf, "MemFree"); if (temp) sscanf(temp, "MemFree: %Lu kB", &mfree); else syslog(LOG_ERR, "no MemFree in /proc/meminfo\n"); temp = strstr(buf, "Buffers"); if (temp) sscanf(temp, "Buffers: %Lu kB", &mbuf); else syslog(LOG_ERR, "no Buffers in /proc/meminfo\n"); temp = strstr(buf, "SwapTotal"); if (temp) sscanf(temp, "SwapTotal: %Lu kB", &stotal); else syslog(LOG_ERR, "no SwapTotal in /proc/meminfo\n"); temp = strstr(buf, "SwapFree"); if (temp) sscanf(temp, "SwapFree: %Lu kB", &sfree); else syslog(LOG_ERR, "no SwapFree in /proc/meminfo\n"); temp = strstr(buf, "Cached"); if (temp) sscanf(temp, "Cached: %Lu kB", &scached); else syslog(LOG_ERR, "no Cached in /proc/meminfo\n"); proc_sum.mem.total = (__u64)mtotal; proc_sum.mem.free = (__u64)mfree; proc_sum.mem.buffers = (__u64)mbuf; proc_sum.swap.total = (__u64)stotal; proc_sum.swap.free = (__u64)sfree; proc_sum.swap.cached = (__u64)scached; proc_sum.mem.used = proc_sum.mem.total - proc_sum.mem.free; proc_sum.swap.used = proc_sum.swap.total - proc_sum.swap.free; } /* * Get virtual memory information */ static void read_vmem(void) { unsigned long long pgin, pgout, swpin, swpout; if (read_file("/proc/vmstat", buf, sizeof(buf) - 1) <= 0) return; pgin = pgout = swpin = swpout = 0; temp = strstr(buf, "pgpgin"); if (temp) sscanf(temp, "pgpgin %Lu", &pgin); else syslog(LOG_ERR, "no pgpgin in /proc/vmstat\n"); temp = strstr(buf, "pgpgout"); if (temp) sscanf(temp, "pgpgout %Lu", &pgout); else syslog(LOG_ERR, "no pgpgout in /proc/vmstat\n"); temp = strstr(buf, "pswpin"); if (temp) sscanf(temp, "pswpin %Lu", &swpin); else syslog(LOG_ERR, "no pswpin in /proc/vmstat\n"); temp = strstr(buf, "pswpout"); if (temp) sscanf(temp, "pswpout %Lu", &swpout); else syslog(LOG_ERR, "no pswpout in /proc/vmstat\n"); proc_sum.mem.pgpgin = (__u64)(pgin << pg_to_kb_shift); proc_sum.mem.pgpgout = (__u64)(pgout << pg_to_kb_shift); proc_sum.swap.pswpin = (__u64)swpin; proc_sum.swap.pswpout = (__u64)swpout; } /* * Get process summary information */ static void read_summary(void) { read_uptime(); count_users(); read_loadavg(); read_cpu(); read_mem(); read_vmem(); } /* * Get memory information for a task */ static int read_statm(struct task_t *task) { long size, res, sh, trs, lrs, drs, dt; snprintf(fname, sizeof(fname), "/proc/%u/statm", task->pid); if (read_file(fname, buf, sizeof(buf) - 1) == -1) return 0; sscanf(buf, "%ld %ld %ld %ld %ld %ld %ld", &size, &res, &sh, &trs, &lrs, &drs, &dt); task->size = (__u64)(size << pg_to_kb_shift); task->resident = (__u64)(res << pg_to_kb_shift); task->swap = task->size - task->resident; task->share = (__u64)(sh << pg_to_kb_shift); task->trs = (__u64)(trs << pg_to_kb_shift); task->drs = (__u64)(drs << pg_to_kb_shift); task->dt = (__u64)dt; task->pmem = (__u16)(task->resident * 10000 / proc_sum.mem.total); return 1; } /* * Get status information for a task from /proc/.../status */ static int read_status(struct task_t *task) { int ruid, euid, egid; char *lenp, *namep; struct passwd *pwd; struct group *grp; snprintf(fname, sizeof(fname), "/proc/%u/status", task->pid); if (read_file(fname, buf, sizeof(buf) - 1) == -1) return 0; ruid = euid = egid = 0; temp = strstr(buf, "Uid"); if (temp) sscanf(temp, "Uid: %d %d", &ruid, &euid); else syslog(LOG_ERR, "no Uid in /proc/%u/status\n", task->pid); temp = strstr(buf, "Gid"); if (temp) sscanf(temp, "Gid: %*d %d", &egid); else syslog(LOG_ERR, "no Gid in /proc/%u/status\n", task->pid); task->euid = (__u16)euid; lenp = mon_record + sizeof(struct monwrite_hdr); lenp += sizeof(struct procd_hdr); lenp += sizeof(struct task_t); namep = lenp + sizeof(__u16); pwd = getpwuid(ruid); if (!pwd) name_lens.ruser_len = sprintf(namep, "%u", ruid); else { name_lens.ruser_len = strlen(pwd->pw_name); if (name_lens.ruser_len > MAX_NAME_LEN) name_lens.ruser_len = MAX_NAME_LEN; memcpy(namep, pwd->pw_name, name_lens.ruser_len); } memcpy(lenp, &name_lens.ruser_len, sizeof(__u16)); lenp = namep + name_lens.ruser_len; namep = lenp + sizeof(__u16); pwd = getpwuid(task->euid); if (!pwd) name_lens.euser_len = sprintf(namep, "%u", task->euid); else { name_lens.euser_len = strlen(pwd->pw_name); if (name_lens.euser_len > MAX_NAME_LEN) name_lens.euser_len = MAX_NAME_LEN; memcpy(namep, pwd->pw_name, name_lens.euser_len); } memcpy(lenp, &name_lens.euser_len, sizeof(__u16)); lenp = namep + name_lens.euser_len; namep = lenp + sizeof(__u16); grp = getgrgid(egid); if (!grp) name_lens.egroup_len = sprintf(namep, "%u", egid); else { name_lens.egroup_len = strlen(grp->gr_name); if (name_lens.egroup_len > MAX_NAME_LEN) name_lens.egroup_len = MAX_NAME_LEN; memcpy(namep, grp->gr_name, name_lens.egroup_len); } memcpy(lenp, &name_lens.egroup_len, sizeof(__u16)); return 1; } /* * Calculate percentage of CPU used by a task since last sampling */ static void cal_task_pcpu(struct task_t *task, const unsigned long long tics) { unsigned int i, size; __u64 etics; if (proc_sum.task.total + 1 >= sort_tbl_size) { sort_tbl_size = sort_tbl_size * 5 / 4 + 100; size = sort_tbl_size * sizeof(struct task_sort_t); curr_sort_tbl = realloc(curr_sort_tbl, size); if (!curr_sort_tbl) { fprintf(stderr, "Allocating memory failed - " "reason %s\n", strerror(errno)); exit(1); } prev_sort_tbl = realloc(prev_sort_tbl, size); if (!prev_sort_tbl) { fprintf(stderr, "Allocating memory failed - " "reason %s\n", strerror(errno)); exit(1); } } curr_sort_tbl[proc_sum.task.total].pid = task->pid; curr_sort_tbl[proc_sum.task.total].tics = (__u64)tics; etics = (__u64)tics; for (i = 0; i < sort_tbl_size; i++) { if (prev_sort_tbl[i].pid == task->pid) { etics -= prev_sort_tbl[i].tics; break; } } task->pcpu = (__u16)((etics * 10000 / Hertz) / (e_time * num_cpus)); if (task->pcpu > 9999) task->pcpu = 9999; } /* * Get status information for a task from /proc/.../stat */ static int read_stat(struct task_t *task) { unsigned long long maj_flt = 0, utime = 0, stime = 0, cutime = 0, cstime = 0; unsigned long flags = 0, pri = 0, nice = 0; char *cmd_start, *cmd_end, *cmdlenp, *cmdp; int ppid = 0, tty = 0, proc = 0, rc; snprintf(fname, sizeof(fname), "/proc/%u/stat", task->pid); if (read_file(fname, buf, sizeof(buf) - 1) == -1) return 0; cmd_start = strchr(buf, '(') + 1; cmd_end = strrchr(cmd_start, ')'); name_lens.cmd_len = cmd_end - cmd_start; cmdlenp = mon_record + sizeof(struct monwrite_hdr); cmdlenp += sizeof(struct procd_hdr); cmdlenp += sizeof(struct task_t); cmdlenp += sizeof(__u16) + name_lens.ruser_len; cmdlenp += sizeof(__u16) + name_lens.euser_len; cmdlenp += sizeof(__u16) + name_lens.egroup_len; cmdlenp += sizeof(__u16) + name_lens.wchan_len; if (name_lens.cmd_len <= 0) name_lens.cmd_len = 0; else { cmdp = cmdlenp + sizeof(__u16); if (name_lens.cmd_len > MAX_NAME_LEN) name_lens.cmd_len = MAX_NAME_LEN; memcpy(cmdp, cmd_start, name_lens.cmd_len); } memcpy(cmdlenp, &name_lens.cmd_len, sizeof(__u16)); cmd_end += 2; rc = sscanf(cmd_end, "%c %d %*d %*d %d %*d " "%lu %*s %*s %Lu %*s " "%Lu %Lu %Lu %Lu " "%ld %ld " "%*d %*s " "%*s %*s %*s " "%*s %*s %*s %*s %*s %*s " "%*s %*s %*s %*s " "%*s %*s %*s " "%*d %d " "%*s %*s", &task->state, &ppid, &tty, &flags, &maj_flt, &utime, &stime, &cutime, &cstime, &pri, &nice, &proc); if (rc != 12) syslog(LOG_ERR, "bad data in %s \n", fname); task->ppid = (__u32)ppid; task->tty = (__u16)tty; task->flags = (__u32)flags; task->maj_flt = (__u64)maj_flt; task->priority = (__s16)pri; task->nice = (__s16)nice; task->processor = (__u32)proc; task->total_time = (__u64)((utime + stime) * 100 / Hertz); task->ctotal_time = (__u64)((utime + stime + cutime + cstime) * 100 / Hertz); cal_task_pcpu(task, utime + stime); return 1; } /* * Get the sleeping in function of a task */ static int read_wchan(struct task_t *task) { int num; char *wchanlenp, *wchanp; snprintf(fname, sizeof(fname), "/proc/%u/wchan", task->pid); num = read_file(fname, buf, sizeof(buf) -1); if (num < 0) return 0; wchanlenp = mon_record + sizeof(struct monwrite_hdr); wchanlenp += sizeof(struct procd_hdr); wchanlenp += sizeof(struct task_t); wchanlenp += sizeof(__u16) + name_lens.ruser_len; wchanlenp += sizeof(__u16) + name_lens.euser_len; wchanlenp += sizeof(__u16) + name_lens.egroup_len; wchanp = wchanlenp + sizeof(__u16); name_lens.wchan_len = num; if (num == 0) { memcpy(wchanlenp, &name_lens.wchan_len, sizeof(__u16)); return 1; } buf[num] = '\0'; if (buf[0] == '0' && buf[1] == '\0') { memcpy(wchanlenp, &name_lens.wchan_len, sizeof(__u16)); wchanp[0] = '-'; return 1; } if (buf[0] == 's' && !strncmp(buf, "sys_", 4)) { name_lens.wchan_len -= 4; if (name_lens.wchan_len > MAX_NAME_LEN) name_lens.wchan_len = MAX_NAME_LEN; memcpy(wchanlenp, &name_lens.wchan_len, sizeof(__u16)); memcpy(wchanp, buf + 4, name_lens.wchan_len); return 1; } if (buf[0] == 'd' && !strncmp(buf, "do_", 3)) { name_lens.wchan_len -= 3; if (name_lens.wchan_len > MAX_NAME_LEN) name_lens.wchan_len = MAX_NAME_LEN; memcpy(wchanlenp, &name_lens.wchan_len, sizeof(__u16)); memcpy(wchanp, buf + 3, name_lens.wchan_len); return 1; } temp = buf; while (*temp == '_') { temp++; name_lens.wchan_len--; } if (name_lens.wchan_len > MAX_NAME_LEN) name_lens.wchan_len = MAX_NAME_LEN; memcpy(wchanlenp, &name_lens.wchan_len, sizeof(__u16)); memcpy(wchanp, temp, name_lens.wchan_len); return 1; } /* * Get command line of a task */ static int read_cmdline(struct task_t *task) { int i, num; char *cmdlnlenp, *cmdlinep; snprintf(fname, sizeof(fname), "/proc/%u/cmdline", task->pid); num = read_file(fname, buf, sizeof(buf) - 1); if (num == -1) return 0; for (i = 0; i < num; i++) { if (buf[i] < ' ' || buf[i] > '~') buf[i] = ' '; } name_lens.cmdline_len = num; cmdlnlenp = mon_record + sizeof(struct monwrite_hdr); cmdlnlenp += sizeof(struct procd_hdr); cmdlnlenp += sizeof(struct task_t); cmdlnlenp += sizeof(__u16) + name_lens.ruser_len; cmdlnlenp += sizeof(__u16) + name_lens.euser_len; cmdlnlenp += sizeof(__u16) + name_lens.egroup_len; cmdlnlenp += sizeof(__u16) + name_lens.wchan_len; cmdlnlenp += sizeof(__u16) + name_lens.cmd_len; cmdlinep = cmdlnlenp + sizeof(__u16); memcpy(cmdlnlenp, &name_lens.cmdline_len, sizeof(__u16)); if (name_lens.cmdline_len > 0) memcpy(cmdlinep, buf, name_lens.cmdline_len); return 1; } /* * Usage sorting help function */ static int sort_usage(const void *et1, const void *et2) { return ((struct task_sort_t *)et2)->cpu_mem_usage - ((struct task_sort_t *)et1)->cpu_mem_usage; } /* * Adjust task state counters */ static void task_count(char oldst, char newst) { if (oldst == newst) return; switch (oldst) { case 'R': proc_sum.task.running--; break; case 'S': case 'D': proc_sum.task.sleeping--; break; case 'T': proc_sum.task.stopped--; break; case 'Z': proc_sum.task.zombie--; break; } switch (newst) { case 'R': proc_sum.task.running++; break; case 'S': case 'D': proc_sum.task.sleeping++; break; case 'T': proc_sum.task.stopped++; break; case 'Z': proc_sum.task.zombie++; break; } } /* * Read and calculate memory and cpu usages of a task */ static void task_usage(struct task_t *task) { long res; unsigned long long utime, stime; snprintf(fname, sizeof(fname), "/proc/%u/statm", task->pid); if (read_file(fname, buf, sizeof(buf) - 1) == -1) return; sscanf(buf, "%*s %ld %*s %*s %*s %*s %*s", &res); task->resident = (__u64)(res << pg_to_kb_shift); task->pmem = (__u16)(task->resident * 10000 / proc_sum.mem.total); snprintf(fname, sizeof(fname), "/proc/%u/stat", task->pid); if (read_file(fname, buf, sizeof(buf) - 1) == -1) return; sscanf(buf, "%*s %*s " "%c %*s %*s %*s %*s %*s " "%*s %*s %*s %*s %*s " "%Lu %Lu ", &task->state, &utime, &stime); cal_task_pcpu(task, utime + stime); curr_sort_tbl[proc_sum.task.total].cpu_mem_usage = task->pcpu + task->pmem; curr_sort_tbl[proc_sum.task.total].state = task->state; task_count('\0', task->state); proc_sum.task.total++; } /* * read tasks information and write to monitor stream */ static void read_tasks(void) { int size; unsigned int i = 0, j = 0; DIR *proc_dir; struct direct *entry; struct task_t task; proc_dir = opendir("/proc"); if (!proc_dir) { syslog(LOG_ERR, "failed /proc open: %s ", strerror(errno)); return; } while ((entry = readdir(proc_dir))) { if (!isdigit(entry->d_name[0]) || !atoi(entry->d_name)) continue; memset(&task, 0, sizeof(struct task_t)); task.pid = atoi(entry->d_name); task_usage(&task); } closedir(proc_dir); if (proc_sum.task.total > MAX_TASK_REC) qsort(curr_sort_tbl, proc_sum.task.total, sizeof(struct task_sort_t), sort_usage); /* only write up to top 100 processes data to monitor stream */ while ((i < proc_sum.task.total) && (j < MAX_TASK_REC)) { memset(&task, 0, sizeof(struct task_t)); memset(mon_record, 0, sizeof(mon_record)); task.pid = curr_sort_tbl[i].pid; if (read_statm(&task) && read_status(&task) && read_wchan(&task) && read_stat(&task) && read_cmdline(&task)) { task_count(curr_sort_tbl[i].state, task.state); size = sizeof(struct task_t); size += sizeof(struct name_lens_t); size += name_lens.ruser_len + name_lens.euser_len; size += name_lens.egroup_len + name_lens.wchan_len; size += name_lens.cmd_len + name_lens.cmdline_len; procd_write_ent(&task, size, TASK_FLAG); j++; } else task_count(curr_sort_tbl[i].state, '\0'); i++; } proc_sum.task.total -= i - j; } /* * Run as background process */ static void procd_daemonize(void) { int pipe_fds[2], startup_rc = 1; pid_t pid; if (pipe(pipe_fds) == -1) { syslog(LOG_ERR, "pipe error: %s\n", strerror(errno)); exit(1); } /* Fork off the parent process */ pid = fork(); if (pid < 0) { syslog(LOG_ERR, "fork error: %s\n", strerror(errno)); exit(1); } if (pid > 0) { /* Wait for startup return code from daemon */ if (read(pipe_fds[0], &startup_rc, sizeof(startup_rc)) == -1) syslog(LOG_ERR, "pipe read error: %s\n", strerror(errno)); /* With startup_rc == 0, pid file was written at this point */ exit(startup_rc); } /* Change the file mode mask */ umask(0); /* Catch SIGINT and SIGTERM to clean up pid file on exit */ procd_handle_signals(); /* Create a new SID for the child process */ if (setsid() < 0) { syslog(LOG_ERR, "setsid error: %s\n", strerror(errno)); goto notify_parent; } /* Change the current working directory */ if (chdir("/") < 0) { syslog(LOG_ERR, "chdir error: %s\n", strerror(errno)); goto notify_parent; } /* Close out the standard file descriptors */ close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); /* Store daemon pid */ if (store_pid() < 0) goto notify_parent; startup_rc = 0; notify_parent: /* Inform waiting parent about startup return code */ if (write(pipe_fds[1], &startup_rc, sizeof(startup_rc)) == -1) { syslog(LOG_ERR, "pipe write error: %s\n", strerror(errno)); exit(1); } if (startup_rc != 0) exit(startup_rc); } static int procd_do_work(void) { int pgsize; struct timezone tz; struct task_sort_t *tmp; prev_small_max = 0; prev_big_max = 1; num_cpus = sysconf(_SC_NPROCESSORS_ONLN); if (num_cpus < 1) num_cpus = 1; pg_to_kb_shift = 0; pgsize = getpagesize(); while (pgsize > 1024) { pgsize >>= 1; pg_to_kb_shift++; } syslog(LOG_INFO, "procd sample interval: %lu\n", sample_interval); while (1) { gettimeofday(&curr_time, &tz); e_time = (curr_time.tv_sec - prev_time.tv_sec) + (float)(curr_time.tv_usec - prev_time.tv_usec) / 1000000.0; memset(&proc_sum, 0, sizeof(struct proc_sum_t)); tmp = prev_sort_tbl; prev_sort_tbl = curr_sort_tbl; curr_sort_tbl = tmp; curr_small_max = 0; curr_big_max = 1; read_summary(); read_tasks(); memset(mon_record, 0, sizeof(mon_record)); procd_write_ent(&proc_sum, sizeof(struct proc_sum_t), SUM_FLAG); if (curr_small_max < prev_small_max) stop_unused(curr_small_max, prev_small_max); if (curr_big_max < prev_big_max) stop_unused(curr_big_max, prev_big_max); prev_small_max = curr_small_max; prev_big_max = curr_big_max; prev_time.tv_sec = curr_time.tv_sec; prev_time.tv_usec = curr_time.tv_usec; sleep(sample_interval); } return 1; } /* Parse options */ static int parse_options(int argc, char **argv) { int opt; do { opt = getopt_long(argc, argv, opt_string, options, NULL); switch (opt) { case -1: /* Reached end of parameter list. */ break; case 'h': printf("%s", help_text); exit(0); case 'v': printf( "mon_procd: version %s\n", RELEASE_STRING); printf("Copyright IBM Corp. 2007, 2017\n"); exit(0); case 'a': attach = 1; break; case 'i': sample_interval = strtol(optarg, NULL, 10); if (sample_interval <= 0) { fprintf(stderr, "Error: Invalid interval " "(needs to be greater than 0)\n"); return(1); } break; default: fprintf(stderr, "Try ' --help' for more" " information.\n"); return(1); } } while (opt != -1); return(0); } int main(int argc, char **argv) { int rc; attach = 0; rc = parse_options(argc, argv); if (rc > 0) return rc; procd_open_monwriter(); openlog("mon_procd", 0, LOG_DAEMON); if (!attach) procd_daemonize(); rc = procd_do_work(); close(mw_dev); return rc; } s390-tools-2.38.0/mon_tools/mon_procd.h000066400000000000000000000062321502674226300176140ustar00rootroot00000000000000/* * mon_procd - Write process data to the z/VM monitor strea * * Definitions used by mon_procd * * Copyright IBM Corp. 2007, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef __mon_procd_h__ #define __mon_procd_h__ #include #include "lib/zt_common.h" /* mon_function values */ #define MONWRITE_START_INTERVAL 0x00 /* start interval recording */ #define MONWRITE_STOP_INTERVAL 0x01 /* stop interval or config recording */ #define SMALL_MON_RECORD_LEN 512 #define MAX_REC_LEN 1500 #define PROCD_APPLID 0x02 #define SUM_FLAG 0x00 #define TASK_FLAG 0x01 #define BUF_SIZE 4096 #define MAX_NAME_LEN 64 #define MAX_CMD_LEN 1024 #define MAX_TASK_REC 100 #define Hertz 100 struct monwrite_hdr { unsigned char mon_function; unsigned short applid; unsigned char record_num; unsigned short version; unsigned short release; unsigned short mod_level; unsigned short datalen; unsigned char hdrlen; } __attribute__((packed)); struct procd_hdr { __u64 time_stamp; __u16 data_len; __u16 data_offset; } __attribute__((packed)); struct task_sum_t { __u32 total; __u32 running; __u32 sleeping; __u32 stopped; __u32 zombie; }; struct cpu_t { __u32 num_cpus; __u16 puser; __u16 pnice; __u16 psystem; __u16 pidle; __u16 piowait; __u16 pirq; __u16 psoftirq; __u16 psteal; }; struct mem_t { __u64 total; __u64 used; __u64 free; __u64 buffers; __u64 pgpgin; __u64 pgpgout; }; struct swap_t { __u64 total; __u64 used; __u64 free; __u64 cached; __u64 pswpin; __u64 pswpout; }; struct proc_sum_t { __u64 uptime; __u32 users; char loadavg_1[6]; char loadavg_5[6]; char loadavg_15[6]; struct task_sum_t task; struct cpu_t cpu; struct mem_t mem; struct swap_t swap; } __attribute__((packed)); struct task_t { __u32 pid; __u32 ppid; __u32 euid; __u16 tty; __s16 priority; __s16 nice; __u32 processor; __u16 pcpu; __u16 pmem; __u64 total_time; __u64 ctotal_time; __u64 size; __u64 swap; __u64 resident; __u64 trs; __u64 drs; __u64 share; __u64 dt; __u64 maj_flt; char state; __u32 flags; } __attribute__((packed)); struct cpudata_t { __u32 id; __u64 usr; __u64 nice; __u64 sys; __u64 idle; __u64 iowt; __u64 irq; __u64 sirq; __u64 steal; __u64 usr_prev; __u64 nice_prev; __u64 sys_prev; __u64 idle_prev; __u64 iowt_prev; __u64 irq_prev; __u64 sirq_prev; __u64 steal_prev; }; struct task_sort_t { __u32 pid; __u64 tics; __u16 cpu_mem_usage; char state; }; static struct option options[] = { {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'v'}, {"attach", no_argument, NULL, 'a'}, {"interval", required_argument, NULL, 'i'}, {NULL, 0, NULL, 0} }; static const char opt_string[] = "+hvai:"; static const char help_text[] = "mon_procd: Daemon that writes process data information\n" "to the z/VM monitor stream.\n" "\n" "Usage: mon_procd [OPTIONS]\n" "\n" "Options:\n" "-h, --help Print this help, then exit\n" "-v, --version Print version information, then exit\n" "-a, --attach Run in foreground\n" "-i, --interval= Sample interval\n"; #endif s390-tools-2.38.0/netboot/000077500000000000000000000000001502674226300151215ustar00rootroot00000000000000s390-tools-2.38.0/netboot/Dockerfile000066400000000000000000000005671502674226300171230ustar00rootroot00000000000000# # Sample Dockerfile to build PXE-style boot image for KVM on s390 # FROM s390x/ubuntu:16.04 RUN apt-get update && apt-get install -y \ linux-image-4.4.0-78-generic \ make \ wget \ bzip2 \ linux-headers-4.4.0-78-generic \ gcc \ kexec-tools \ file RUN mkdir /netboot COPY . /netboot RUN cd /netboot && make -f Makefile.pxelinux.0 KERNEL_VERSION=4.4.0-78-generic s390-tools-2.38.0/netboot/Makefile000066400000000000000000000015361502674226300165660ustar00rootroot00000000000000# Install the netboot image build scripts as samples include ../common.mak SCRIPTS = mk-s390image mk-pxelinux-ramfs NETBOOT_SAMPLEDIR=$(TOOLS_DATADIR)/netboot all: install: install-scripts $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 \ Dockerfile Makefile.pxelinux.0 README.md mk-s390image.1 \ $(DESTDIR)$(NETBOOT_SAMPLEDIR) install-scripts: $(SCRIPTS) @if [ ! -d $(DESTDIR)$(NETBOOT_SAMPLEDIR) ]; then \ mkdir -p $(DESTDIR)$(NETBOOT_SAMPLEDIR); \ chown $(OWNER):$(GROUP) $(DESTDIR)$(NETBOOT_SAMPLEDIR); \ chmod 755 $(DESTDIR)$(NETBOOT_SAMPLEDIR); \ fi; \ for i in $^; do \ $(SED) -e 's/%S390_TOOLS_VERSION%/$(S390_TOOLS_RELEASE)/' \ < $$i >$(DESTDIR)$(NETBOOT_SAMPLEDIR)/$$i; \ chown $(OWNER):$(GROUP) $(DESTDIR)$(NETBOOT_SAMPLEDIR)/$$i; \ chmod 755 $(DESTDIR)$(NETBOOT_SAMPLEDIR)/$$i; \ done .PHONY: all install clean install-scripts s390-tools-2.38.0/netboot/Makefile.pxelinux.0000066400000000000000000000024231502674226300205730ustar00rootroot00000000000000# Sample Makefile to produce a pxe-style netboot image # matching the currently active kernel. # If the image is to be build for a different kernel version # the KERNEL_VERSION variable must be overridden # To use a local busybox installation, change BBINSTALL # to point to the local busybox install path ifeq ("$(KERNEL_VERSION)","") KERNEL_VERSION=$(shell uname -r) endif export KERNEL_VERSION filetype=$(shell file $1 | grep "Linux S390") s390_check=$(if $(call filetype,$1), $1) prefixes=image vmlinux vmlinuz kernel_images=$(foreach prefix, $(prefixes), \ $(call s390_check,/boot/$(prefix)-$(KERNEL_VERSION))) KERNEL_IMAGE=$(firstword $(kernel_images)) ifeq ($(KERNEL_IMAGE),) $(error Could not find a kernel image under /boot) endif BUSYBOX=busybox-1.32.0 BBINSTALL=$(BUSYBOX)/_install all: $(KERNEL_IMAGE) pxelinux.initramfs /bin/bash mk-s390image $(KERNEL_IMAGE) pxelinux.0 -r pxelinux.initramfs pxelinux.initramfs: $(BBINSTALL) /bin/bash mk-pxelinux-ramfs -b $< -k $(KERNEL_VERSION) $@ $(BUSYBOX)/_install: wget https://busybox.net/downloads/$(BUSYBOX).tar.bz2 tar xjf $(BUSYBOX).tar.bz2 $(MAKE) -C $(BUSYBOX) defconfig $(MAKE) -C $(BUSYBOX) install install: clean: $(RM) pxelinux.0 pxelinux.initramfs $(BUSYBOX).tar.bz2 $(RM) -r $(BUSYBOX) .PHONY: all install clean s390-tools-2.38.0/netboot/README.md000066400000000000000000000111201502674226300163730ustar00rootroot00000000000000# How to build a PXELINUX-style network boot image for KVM ## Synopsis To build a PXELINUX-style netboot image usable for KVM a s390 Linux system with access to the internet is required. Running the following command will generate the netboot image pxlinux.0: ` $ make -f Makefile.pxelinux.0` Alternatively you can use docker to build the image: ``` $ docker build -t pxelinux0 . $ docker run --rm -v $(pwd):/out pxelinux0 cp /netboot/pxelinux.0 /out $ docker rmi pxelinux0 ``` The resulting file pxelinux.0 must be copied to the system acting as DHCP/BOOTP server for the KVM installation. ## Full Description Starting with QEMU 2.10 it is possible to boot s390 virtual machines over a network interface using DHCP. As usual for DHCP/BOOTP a single bootable image is copied from the boot server, loaded into memory and booted. In order to boot a Linux Operating System, it is typically necessary to load a kernel together with an initial ramdisk (initramfs) and optionally specify some kernel command line parameters. Alternatively, on s390 it is possible to load a single file consisting of the kernel image followed by an initial ramdisk. Such single boot images can be provided by a Linux distributor, e.g. on the installation media. Single boot images can also easily be built from pre-existing kernel/initramfs pairs by concatenating these files. In order to allow the kernel to find the ramdisk, it is necessary to update the 8 bytes at location 0x10408 with the offset value of the ramdisk in the new binary, and the 8 bytes at location 0x10410 with the size of the ramdisk. Both values need to be updated in binary, big endian format. Since PXELINUX, the PXE boot implementation provided by the Syslinux project, has introduced a popular way to set up network boot servers for Linux, it is desirable that s390 network boot setups can be done in a similar way. A boot image simulating a PXELINUX-like boot for s390 can be easily constructed by combining a Linux kernel with a small fit-to-purpose initial ramdisk as described above. For practical purposes, using the host kernel is a reasonable way for this kind of approach. If possible, the initial ramdisk should be independent of the host, which is not always possible, as the kernel might require modules for e.g. virtio network and block devices. ### Example: Building a PXELINUX-style boot image The approach described below consists of bundling some shell scripts, busybox and the kexec binary bundled into the initial ramdisk. The init process can be a simple shell script that will mount a few essential file systems, like /dev, /proc, and /sys, start a DHCP client (e.g. busybox's udchpc) and then invoke another script to perform the network boot. udchpc will invoke the script /usr/share/udhcpc/default.script in response to DHCP server messages to perform configuration actions. The sample default.script delivered with busybox can be used for that purpose, but needs to be extended to evaluate the bootp specific DHCP options (most important the tftp server address) and store them for use by the boot script. The boot script itself has to retrieve the PXELINUX configuration from the tftp server according to the rules described [here][1] then retrieve the remote kernel and initial ramdisk and finally use kexec to boot the network kernel. In essence, the following steps are performed to produce the initial ramdisk: 1. Create a skeleton initramfs directory structure 2. Create the init script, the boot script and the DHCP default script 3. Copy kexec and it's dependencies from the host into the initramfs 4. Copy virtio network and block modules of the host's active kernel into the initramfs 5. Copy the busybox binaries into the initramfs. 6. Copy the DHCP configuration and PXE boot scripts to the initramfs 7. Build the ramdisk (in compressed CPIO format) 8. Concatenate the kernel image and the initial ramdisk, and adjust the ramdisk offset as described above. Steps 1 to 7 are performed by the sample script mk-pxelinux-ramfs, while step 8 is done with the help of mk-s390image. The binary resulting from the procedure described above can now be deployed to a DHCP/BOOTP server. This server should also act as a TFTP server for the PXELINUX configuration and binary files needed to complete the network boot. Alternatively, it is possible to use programs like [petitboot][2] or [pxe-kexec][3] in the initial ramdisk, as these provide more sophisticated and robust processing of PXELINUX-style configurations. [1]: http://www.syslinux.org/wiki/index.php?title=PXELINUX [2]: https://github.com/open-power/petitboot [3]: https://sourceforge.net/projects/pxe-kexec.berlios/ s390-tools-2.38.0/netboot/mk-pxelinux-ramfs000077500000000000000000000225411502674226300204420ustar00rootroot00000000000000#!/bin/bash # # netboot - PXE-style boot for KVM on s390 # # Sample script to build an initramfs image suitable for performing a # PXELINUX-style boot for KVM guests from a DHCP/BOOTP Server. # Has to be executed on the KVM host where it will be deployed and needs # to be re-run after kernel updates on the host, unless the virtio # drivers are statically built into the host kernel. # # The script requires a busybox install tree, e.g. resulting from a build # from source, after make defconfig && make install # # To keep things simple, we don't include udev but use devtmpfs # which means the host kernel must have been built with CONFIG_DEVTMPFS=y # # Sample invocation: # # ./mk-pxelinux-ramfs -b /downloads/busyboxdir pxelinux.initramfs # # Copyright IBM Corp. 2017 # # s390-tools is free software; you can redistribute it and/or modify # it under the terms of the MIT license. See LICENSE for details. # Variables cmd=$(basename $0) busyboxdir= builddir= initramfs= success=no # Cleanup on exit cleanup() { if [ -n $builddir ] then rm -rf $builddir fi } trap cleanup EXIT # Usage usage() { cat <<-EOF Usage: $cmd -b BUSYBOX_DIR [-k KERNEL_VERSION] INITRAMFS_FILE Build a PXELINUX style boot initramfs INITRAMFS_FILE using a busybox installed in BUSYBOX_DIR and kernel modules from the currently running kernel or from the kernel version specified with the '-k KERNEL_VERSION' option. OPTIONS -b Search installed busybox in directory BUSYBOX_DIR -k Use KERNEL_VERSION instead of currently running kernel -h Print this help, then exit -v Print version information, then exit EOF } printversion() { cat <<-EOD $cmd: version %S390_TOOLS_VERSION% Copyright IBM Corp. 2017 EOD } # Get shared objects for binary sharedobjs() { ldd $1 | sed -e 's?[^/]*??' -e 's/(.*)//' } # Check args args=$(getopt b:k:hv $*) if [ $? = 0 ] then set -- $args while [ -n $1 ] do case $1 in -b) busyboxdir=$2; shift 2;; -k) kernelversion=$2; shift 2;; -h) usage; exit 0;; -v) printversion; exit 0;; --) shift; break;; *) echo "$cmd: Unexpected argument $1, exiting..." >&2; exit 1;; esac done fi if [ $# != 1 -o "$busyboxdir" = "" ] then usage >&2 exit 1 fi # Full output file path initramfs=$(readlink -m $(dirname $1))/$(basename $1) # Exit on error set -e # Module locations if [ -n $kernelversion ]; then moddir=/lib/modules/$kernelversion else moddir=/lib/modules/$(uname -r) fi netdir=$moddir/kernel/drivers/net blkdir=$moddir/kernel/drivers/block # Setup build directory builddir=$(mktemp -d) echo "$cmd: Building in $builddir" ramfsdirs="/bin /dev /etc /lib64 /lib /mnt /proc /run /sbin /sys /tmp /usr /var" for d in $ramfsdirs do mkdir -p $builddir/$d done # Kexec echo "$cmd: Copying kexec" # Install both binary and required shared libraries OLDPATH=$PATH PATH=$OLDPATH:/sbin:/usr/sbin kexec_bin=$(command -v kexec) kexec_sos=$(sharedobjs $kexec_bin) PATH=$OLDPATH cp $kexec_bin $builddir/sbin for so in $kexec_sos do mkdir -p $builddir/$(dirname $so) cp $so $builddir/$(dirname $so) done # virtio module(s), if present echo "$cmd: Copying virtio modules" mkdir -p $builddir/$netdir mkdir -p $builddir/$blkdir set +e cp $netdir/virtio_net.ko* $builddir/$netdir 2> /dev/null cp $blkdir/virtio_blk.ko* $builddir/$blkdir 2> /dev/null set -e # Busybox (+ dependencies) echo "$cmd: Copying busybox files" cp -a $busyboxdir/* $builddir busybox_sos=$(sharedobjs $busyboxdir/bin/busybox) for so in $busybox_sos do mkdir -p $builddir/$(dirname $so) cp $so $builddir/$(dirname $so) done # ad_packet module(s), if present echo "$cmd: Copying af_packet modules" packetdir=$moddir/kernel/net/packet mkdir -p $builddir/$packetdir set +e cp $packetdir/* $builddir/$packetdir 2> /dev/null set -e # Init script echo "$cmd: Making init script" # --- begin init script cat <<'EOF' > $builddir/init #!/bin/sh /bin/mount -t devtmpfs none /dev /bin/mount -t proc none /proc /bin/mount -t sysfs none /sys /bin/mount -t tmpfs none /run /sbin/modprobe virtio_net /sbin/udhcpc -O pxeconffile -O pxepathprefix -x 93:001F & /sbin/pxeboot.script EOF # --- end init script chmod +x $builddir/init # udhcpc script echo "$cmd: Making DHCP script" mkdir -p $builddir/usr/share/udhcpc # -- begin dhcp script cat <<'EOF' > $builddir/usr/share/udhcpc/default.script #!/bin/sh # Setup name resolution and PXE boot configuration # called by udhcpc RESOLVCONF="/etc/resolv.conf" PXECONF="/etc/pxe.conf" ccidr() { # clumsy netmask to cidr transformation # with minimal sanity checking OLDIFS=$IFS IFS=. c=0 n=4 for i in $1 do n=$(/usr/bin/expr $n - 1) case $i in 255) c=$(/usr/bin/expr $c + 8);; 254) c=$(/usr/bin/expr $c + 7); break;; 252) c=$(/usr/bin/expr $c + 6); break;; 248) c=$(/usr/bin/expr $c + 5); break;; 240) c=$(/usr/bin/expr $c + 4); break;; 224) c=$(/usr/bin/expr $c + 3); break;; 192) c=$(/usr/bin/expr $c + 2); break;; 128) c=$(/usr/bin/expr $c + 1); break;; 0) break;; *) c=0; break;; esac if [ $n = 0 ] then break fi done IFS=$OLDIFS echo $c } echo "DHCP response $1: " case "$1" in deconfig) echo " interface: $interface" /sbin/ip route flush table all /sbin/ip addr flush $interface /sbin/ip link set $interface up /bin/rm -f $PXECONF $RESOLVCONF ;; renew|bound) echo " interface: $interface $ip $subnet" echo " router: $router" echo " domain: $domain $dns" echo " tftp: $siaddr" echo " pxepathprefix: $pxepathprefix" # flush routes /sbin/ip route flush table all # setup if link /sbin/ip addr flush $interface /sbin/ip link set $interface up # setup if addr if [ -n "$subnet" ] then maskedip="$ip"/$(ccidr $subnet) else maskedip="ip" fi /sbin/ip addr add $maskedip broadcast $broadcast dev $interface # setup default routes if [ -n "$router" ] then /sbin/ip route add default via $router fi # setup resolv.conf if [ -n "$domain" ] then echo "search $domain" > $RESOLVCONF for i in $dns do echo " nameserver $i" >> $RESOLVCONF done fi # pxe control if [ -n "$siaddr" ] then echo "siaddr=$siaddr" > $PXECONF echo "interface=$interface" >> $PXECONF echo "ip=$ip" >> $PXECONF echo "pxepathprefix=$pxepathprefix" >> $PXECONF fi ;; nak) ;; *) exit 1 ;; esac exit 0 EOF # -- end dhcp script chmod +x $builddir/usr/share/udhcpc/default.script # pxeboot script echo "$cmd: Making PXE boot script" # -- begin pxeboot script cat <<'EOF' > $builddir/sbin/pxeboot.script #!/bin/sh # Perform a PXE style boot using kexec # Supports only super-simple config files set -e # Check if a valid pxe conf is available # so far a non configurable 600 sec timeout PXE_CONF=/etc/pxe.conf TIMEOUT=600 WAITED=0 echo "waiting for pxe config from DHCP (max $TIMEOUT sec)" while [ ! -f $PXE_CONF ] do sleep 1 WAITED=$(($WAITED + 1)) if [ $WAITED -gt $TIMEOUT ] then echo Error waiting for PXE configuration from DHCP exit fi done # Source the DHCP-generated TFTP info # Currently we are just looking for siaddr . $PXE_CONF # Retrieve the config (default only for now) CONFIGS="" if [ -n "$siaddr" ]; then # Enable UUID based config on s390 if [ $(/bin/uname -m) = "s390x" ] then set +e uuid=$(/bin/grep UUID /proc/sysinfo | tail -1 | tr -d ' ' | cut -d ':' -f 2) 2> /dev/null set -e else # not caring for other arches right now uuid="" fi CONFIGS="$CONFIGS $uuid" # Enable MAC based config ifaddr=$(/bin/cat /sys/class/net/$interface/address | tr ':' '-') CONFIGS="$CONFIGS 01-$ifaddr" # Enable IP based config iphex=$(printf %02X $(echo $ip | tr '.' ' ')) for i in 8 7 6 5 4 3 2 1 do CONFIGS="$CONFIGS $(echo $iphex | cut -c 1-$i)" done # Finally enable default config CONFIGS="$CONFIGS default" set +e for c in $CONFIGS do echo "fetching config pxelinux.cfg/$c from $siaddr" if /usr/bin/tftp -g -l /tmp/config -r ${pxepathprefix}pxelinux.cfg/$c $siaddr then break fi done fi if [ ! -f /tmp/config ] then echo no config found exit fi # Simple config file parsing, only one entry allowed kernel=$(/bin/grep -i "^[[:space:]]*kernel" /tmp/config | sed "s/^[[:space:]]*kernel[[:space:]]*//I") initrd=$(/bin/grep -i "^[[:space:]]*initrd" /tmp/config | sed "s/^[[:space:]]*initrd[[:space:]]*//I") append=$(/bin/grep -i "^[[:space:]]*append" /tmp/config | sed "s/^[[:space:]]*append[[:space:]]*//I") ipappend=$(/bin/grep -i "^[[:space:]]*ipappend" /tmp/config | sed "s/^[[:space:]]*ipappend[[:space:]]*//I") if [ -z "$kernel" ] then echo no kernel statement found in config exit else echo fetch kernel $kernel from $siaddr /usr/bin/tftp -g -l /tmp/kernel -r $pxepathprefix$kernel $siaddr fi if [ -n "$initrd" ] then echo fetch initrd $initrd from $siaddr /usr/bin/tftp -g -l /tmp/initrd -r $pxepathprefix$initrd $siaddr INITRD="--initrd=/tmp/initrd" else INITRD="" fi if [ -z "$append" ]; then echo "Kexec load: kexec -l /tmp/kernel $INITRD" kexec -l /tmp/kernel $INITRD else if [ "$ipappend" = "2" ]; then $append="$append BOOTIF=01-$ifaddr" fi echo "Kexec load: kexec -l /tmp/kernel $INITRD --append=\"$append\"" kexec -l /tmp/kernel $INITRD --append="$append" fi kexec -e EOF # -- end pxeboot script chmod +x $builddir/sbin/pxeboot.script # The final initramfs echo Building initramfs cd $builddir find . | cpio -o -Hnewc | gzip - > $initramfs cd $OLDPWD s390-tools-2.38.0/netboot/mk-s390image000077500000000000000000000074621502674226300171660ustar00rootroot00000000000000#!/bin/bash # # netboot - PXE-style boot for KVM on s390 # # Sample script to build a single s390 boot image consisting of # kernel, an initial ramdisk and kernel parameters from # individual components. Note that bash is required to run this script! # # Sample invocation: # # ./mk-s390image /boot/image -r /boot/initrd image # # The resulting image can be used to build a bootable # ISO or as firmware image for KVM. # # Copyright IBM Corp. 2017 # # s390-tools is free software; you can redistribute it and/or modify # it under the terms of the MIT license. See LICENSE for details. # Offsets OFFS_INITRD_START_BYTES=66568 OFFS_INITRD_SIZE_BYTES=66576 OFFS_COMMANDLINE_BYTES=66688 MAX_PARMFILE_SIZE=896 # Variables cmd=$(basename $0) kernel= ramdisk= parmfile= image= binval= success=no # Cleanup on exit cleanup() { if [ -n "$binval" ] then rm -f $binval fi if [ -n "$image" -a $success = no ] then rm $image fi } trap cleanup EXIT # Usage usage() { cat <<-EOF Usage: $cmd KERNEL BOOT_IMAGE [-r RAMDISK] [-p PARMFILE] Build an s390 image BOOT_IMAGE suitable for CD/tape/network boot or as a KVM firmware image using a stripped Linux kernel file KERNEL. OPTIONS -p Use PARMFILE with kernel parameters in the image -r Include RAMDISK in the image -h Print this help, then exit -v Print version information, then exit EOF } printversion() { cat <<-EOD $cmd: version %S390_TOOLS_VERSION% Copyright IBM Corp. 2017 EOD } # Convert decimal number to big endian doubleword dec2be64() { local num=$1 local b local i for i in $(seq 1 8) do b="\\x$(printf '%x' $(expr $num % 256))$b" num=$(expr $num / 256) || true done printf $b } # Do the image build dobuild() { local i local kernel_size local ramdisk_size local ramdisk_offset local parmfile_size # check whether all specified files exist for i in $kernel $ramdisk $parmfile do if [ ! -f $i ] then echo "$cmd: File $i not found" >&2 return 1 fi done if ! file -b $(readlink -f $kernel) | grep "Linux S390" > /dev/null then echo "$cmd: Unrecognized file format for $kernel" >&2 return 1 fi # from now on we SHOULD only fail on disk shortage # or file permissions, let the shell handle that set -e # copy over kernel padded with zeroes to page boundary dd if=$kernel of=$image bs=4096 conv=sync status=none # append ramdisk if specified if [ "$ramdisk" != "" ] then ramdisk_size=$(du -b -L $ramdisk | cut -f1) kernel_size=$(du -b -L $kernel | cut -f1) ramdisk_offset=$(du -b -L $image | cut -f1) cat $ramdisk >> $image binval=$(mktemp) dec2be64 $ramdisk_offset > $binval dd seek=$OFFS_INITRD_START_BYTES if=$binval of=$image bs=1 \ count=8 conv=notrunc status=none dec2be64 $ramdisk_size > $binval dd seek=$OFFS_INITRD_SIZE_BYTES if=$binval of=$image bs=1 \ count=8 conv=notrunc status=none fi # set cmdline if [ "$parmfile" != "" ] then parmfile_size=$(du -b -L $parmfile | cut -f1) if [ $parmfile_size -le $MAX_PARMFILE_SIZE ] then # Clear any previous parameters dd seek=$OFFS_COMMANDLINE_BYTES bs=1 count=$MAX_PARMFILE_SIZE \ if=/dev/zero of=$image conv=notrunc status=none dd seek=$OFFS_COMMANDLINE_BYTES bs=1 if=$parmfile \ of=$image conv=notrunc status=none else echo "$cmd: Size $parmfile_size of $parmfile exceeds command line limit of $MAX_PARMFILE_SIZE" >&2 return 1 fi fi # we've done it success=yes } # check args and build args=$(getopt "r:p:hv" $*) if [ $? = 0 ] then set -- $args while [ $1 != "" ] do case $1 in -r) ramdisk=$2; shift 2;; -p) parmfile=$2; shift 2;; -h) usage; exit 0;; -v) printversion; exit 0;; --) shift; break;; *) echo "$cmd: Unexpected argument $1, exiting..." >&2; exit 1;; esac done fi if [ $# = 2 ] then kernel=$1 image=$2 dobuild exit 0 fi # something wasn't right usage >&2 exit 1 s390-tools-2.38.0/netboot/mk-s390image.1000066400000000000000000000010671502674226300173150ustar00rootroot00000000000000.TH MK-S390IMAGE "1" "November 2020" "s390-tools" "User Commands" .SH NAME mk-s390image \- tool for creating bootable image .SH SYNOPSIS .B mk-s390image \fI\,KERNEL BOOT_IMAGE \/\fR[\fI\,-r RAMDISK\/\fR] [\fI\,-p PARMFILE\/\fR] .SH DESCRIPTION Build an s390 image BOOT_IMAGE suitable for CD/tape/network boot or as a KVM firmware image using a stripped Linux kernel file KERNEL. .PP .SH OPTIONS .TP \fB\-p\fR Use PARMFILE with kernel parameters in the image .TP \fB\-r\fR Include RAMDISK in the image .TP \fB\-h\fR Print usage message, then exit s390-tools-2.38.0/opticsmon/000077500000000000000000000000001502674226300154625ustar00rootroot00000000000000s390-tools-2.38.0/opticsmon/Makefile000066400000000000000000000037161502674226300171310ustar00rootroot00000000000000include ../common.mak TESTS := tests/ libs =$(rootdir)/libzpci/libzpci.a $(rootdir)/libutil/libutil.a ifneq (${HAVE_OPENSSL},0) check_dep_openssl: $(call check_dep, \ "opticsmon", \ "openssl/evp.h", \ "openssl-devel", \ "HAVE_OPENSSL=0") BUILDTARGET += check_dep_openssl endif # HAVE_OPENSSL ifneq (${HAVE_LIBNL3},0) check_dep_libnl3: $(call check_dep, \ "opticsmon", \ "netlink/socket.h", \ "libnl3-devel", \ "HAVE_LIBNL3=0") BUILDTARGET += check_dep_libnl3 endif # HAVE_LIBNL3 ifeq (${HAVE_OPENSSL},0) all: $(SKIP) HAVE_OPENSSL=0 install: $(SKIP) HAVE_OPENSSL=0 else ifeq (${HAVE_LIBNL3},0) all: $(SKIP) HAVE_LIBNL3=0 install: $(SKIP) HAVE_LIBNL3=0 else ifneq ($(shell sh -c 'command -v pkg-config'),) LIB_CFLAGS += $(shell pkg-config --silence-errors --cflags libnl-3.0) LIB_CFLAGS += $(shell pkg-config --silence-errors --cflags libnl-genl-3.0) LIB_CFLAGS += $(shell pkg-config --silence-errors --cflags libnl-route-3.0) LIB_CFLAGS += $(shell pkg-config --silence-errors --cflags libcrypto) LIB_LFLAGS += $(shell pkg-config --silence-errors --libs libnl-3.0) LIB_LFLAGS += $(shell pkg-config --silence-errors --libs libnl-genl-3.0) LIB_LFLAGS += $(shell pkg-config --silence-errors --libs libnl-route-3.0) LIB_LFLAGS += $(shell pkg-config --silence-errors --libs libcrypto) else LIB_CFLAGS += -I /usr/include/libnl3/ LIB_LFLAGS += -lnl-route-3 -lnl-genl-3 -lnl-3 LIB_CFLAGS += -I /usr/include/openssl/ LIB_LFLAGS += -lcrypto endif ALL_CPPFLAGS += $(LIB_CFLAGS) LDLIBS += $(LIB_LFLAGS) BUILDTARGET += opticsmon all: ${BUILDTARGET} opticsmon: opticsmon.o optics_info.o optics_sclp.o ethtool.o link_mon.o $(libs) install: all $(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8 $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 opticsmon $(DESTDIR)$(BINDIR) $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 opticsmon.8 \ $(DESTDIR)$(MANDIR)/man8 endif # HAVE_OPENSSL3=0 or HAVE_LIBNL3=0 clean: rm -f *.o *~ opticsmon core .PHONY: all install clean s390-tools-2.38.0/opticsmon/ethtool.c000066400000000000000000000144221502674226300173070ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/util_libc.h" #include "ethtool.h" static int ethtool_nl_cb(struct nl_msg *msg, void *arg) { struct nlattr *attrs[ETHTOOL_A_MODULE_EEPROM_DATA + 1] = {}; struct nlmsghdr *hdr = nlmsg_hdr(msg); struct optics **oi = arg; int rc = 0; size_t len; rc = genlmsg_parse(hdr, 0, attrs, ETHTOOL_A_MODULE_EEPROM_DATA, NULL); if (rc) { nl_perror(rc, "genlmsg parse"); return NL_STOP; } len = nla_len(attrs[ETHTOOL_A_MODULE_EEPROM_DATA]); /* Extend optics info*/ if (!(*oi)->raw) (*oi)->raw = util_malloc(len); else (*oi)->raw = util_realloc((*oi)->raw, (*oi)->size + len); memcpy((*oi)->raw + (*oi)->size, nla_data(attrs[ETHTOOL_A_MODULE_EEPROM_DATA]), len); (*oi)->size += len; return NL_OK; } int ethtool_nl_connect(struct ethtool_nl_ctx *ctx) { struct nl_sock *sk; int ethtool_id; int rc = 0; sk = nl_socket_alloc(); if (!sk) { nl_perror(NLE_NOMEM, "alloc"); return EXIT_FAILURE; } rc = genl_connect(sk); if (rc) { nl_perror(rc, "connect"); rc = EXIT_FAILURE; goto err_free; } ethtool_id = genl_ctrl_resolve(sk, ETHTOOL_GENL_NAME); if (ethtool_id < 0) { if (ethtool_id == -NLE_OBJ_NOTFOUND) fprintf(stderr, "Ethtool netlink family not found\n"); else nl_perror(ethtool_id, "ctrl resolve"); rc = EXIT_FAILURE; goto err_close; } ctx->sk = sk; ctx->ethtool_id = ethtool_id; return rc; err_close: nl_close(sk); err_free: nl_socket_free(sk); return rc; } void ethtool_nl_close(struct ethtool_nl_ctx *ctx) { nl_close(ctx->sk); nl_socket_free(ctx->sk); } static int ethtool_nl_put_req_hdr(struct ethtool_nl_ctx *ctx, struct nl_msg *msg, uint8_t cmd, const char *netdev) { struct nlattr *opts; void *user_hdr; int rc = 0; user_hdr = genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, ctx->ethtool_id, 0, NLM_F_REQUEST | NLM_F_ACK, cmd, ETHTOOL_GENL_VERSION); if (!user_hdr) { fprintf(stderr, "genlmsg put failed\n"); return EXIT_FAILURE; } opts = nla_nest_start(msg, ETHTOOL_A_MODULE_EEPROM_HEADER); if (!opts) { fprintf(stderr, "nla nest for start failed\n"); return EXIT_FAILURE; } NLA_PUT_STRING(msg, ETHTOOL_A_HEADER_DEV_NAME, netdev); nla_nest_end(msg, opts); return rc; nla_put_failure: nla_nest_cancel(msg, opts); return EXIT_FAILURE; } static int ethtool_nl_put_eeprom_get_attrs(struct nl_msg *msg, uint8_t addr, uint8_t page, uint32_t offset) { NLA_PUT_U32(msg, ETHTOOL_A_MODULE_EEPROM_LENGTH, SFF8636_PAGE_SIZE); NLA_PUT_U8(msg, ETHTOOL_A_MODULE_EEPROM_PAGE, page); NLA_PUT_U32(msg, ETHTOOL_A_MODULE_EEPROM_OFFSET, offset); NLA_PUT_U8(msg, ETHTOOL_A_MODULE_EEPROM_BANK, 0); NLA_PUT_U8(msg, ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS, addr); return 0; nla_put_failure: return EXIT_FAILURE; } static int ethtool_nl_get_page(struct ethtool_nl_ctx *ctx, const char *netdev, uint8_t addr, uint8_t page, uint32_t offset) { struct nl_msg *msg; int rc = 0; msg = nlmsg_alloc(); if (!msg) { nl_perror(NLE_NOMEM, "nlmsg alloc"); return -ENOMEM; } ethtool_nl_put_req_hdr(ctx, msg, ETHTOOL_MSG_MODULE_EEPROM_GET, netdev); ethtool_nl_put_eeprom_get_attrs(msg, addr, page, offset); rc = nl_send_auto(ctx->sk, msg); if (rc < 0) { nl_perror(rc, "Failed to send netlink message"); rc = -EIO; goto free_msg; } rc = nl_recvmsgs_default(ctx->sk); if (rc < 0) { if (rc == -NLE_NODEV) { rc = -ENODEV; } else { nl_perror(rc, "Failed to receive netlink message"); rc = -EIO; } goto free_msg; } /* Ethtool netlink sends ACKs need to pick them up */ rc = nl_wait_for_ack(ctx->sk); if (rc < 0) { nl_perror(rc, "Failed to wait for netlink ack"); rc = -EIO; goto free_msg; } free_msg: nlmsg_free(msg); return rc; } static int ethtool_nl_get_sfp(struct ethtool_nl_ctx *ctx, const char *netdev, struct optics *oi) { int rc = 0; /* Page A0h upper */ rc = ethtool_nl_get_page(ctx, netdev, SFF8079_I2C_ADDRESS_LOW, 0x0, SFF8636_PAGE_SIZE); if (rc < 0) return rc; /* If page A2h is not present we're done */ if (!(oi->raw[SFF8472_DIAGNOSTICS_TYPE_OFFSET] & SFF8472_DIAGNOSTICS_TYPE_MASK)) return 0; /* Page A2h lower */ rc = ethtool_nl_get_page(ctx, netdev, SFF8079_I2C_ADDRESS_HIGH, 0x0, 0); if (rc < 0) return rc; /* Page A2h upper */ rc = ethtool_nl_get_page(ctx, netdev, SFF8079_I2C_ADDRESS_HIGH, 0x0, SFF8636_PAGE_SIZE); if (rc < 0) return rc; return 0; } static int ethtool_nl_get_qsfp(struct ethtool_nl_ctx *ctx, const char *netdev, struct optics *oi) { int rc = 0; /* Page 00h upper */ rc = ethtool_nl_get_page(ctx, netdev, SFF8079_I2C_ADDRESS_LOW, 0x0, SFF8636_PAGE_SIZE); if (rc) return rc; /* Page 01h */ if (oi->raw[SFF8636_PAGE_OFFSET] & SFF8636_P01H) { /* Page 01h upper only */ rc = ethtool_nl_get_page(ctx, netdev, SFF8079_I2C_ADDRESS_LOW, 0x1, SFF8636_PAGE_SIZE); if (rc < 0) return rc; } /* Page 02h */ if (oi->raw[SFF8636_PAGE_OFFSET] & SFF8636_P02H) { /* Page 02h upper only */ rc = ethtool_nl_get_page(ctx, netdev, SFF8079_I2C_ADDRESS_LOW, 0x2, SFF8636_PAGE_SIZE); if (rc < 0) return rc; } /* Page 03h is present if flatmem is not set */ if (!(oi->raw[SFF8636_STATUS_2_OFFSET] & SFF8636_STATUS_FLAT_MEM)) { /* Page 03h upper only */ rc = ethtool_nl_get_page(ctx, netdev, SFF8079_I2C_ADDRESS_LOW, 0x3, SFF8636_PAGE_SIZE); if (rc < 0) return rc; } return 0; } int ethtool_nl_get_optics(struct ethtool_nl_ctx *ctx, const char *netdev, struct optics **oi) { int rc = 0; int type; *oi = util_zalloc(sizeof(**oi)); nl_socket_modify_cb(ctx->sk, NL_CB_VALID, NL_CB_CUSTOM, ethtool_nl_cb, oi); /* Page 00h lower */ rc = ethtool_nl_get_page(ctx, netdev, SFF8079_I2C_ADDRESS_LOW, 0x0, 0); if (rc < 0) goto out_err_free_oi; type = optics_type(*oi); switch (type) { case OPTICS_TYPE_SFP: rc = ethtool_nl_get_sfp(ctx, netdev, *oi); break; case OPTICS_TYPE_QSFP28: rc = ethtool_nl_get_qsfp(ctx, netdev, *oi); break; }; if (rc < 0) goto out_err_free_oi; return rc; out_err_free_oi: free(*oi); *oi = NULL; return rc; } s390-tools-2.38.0/opticsmon/ethtool.h000066400000000000000000000004571502674226300173170ustar00rootroot00000000000000#pragma once #include "optics_info.h" struct ethtool_nl_ctx { struct nl_sock *sk; int ethtool_id; }; int ethtool_nl_connect(struct ethtool_nl_ctx *ctx); void ethtool_nl_close(struct ethtool_nl_ctx *ctx); int ethtool_nl_get_optics(struct ethtool_nl_ctx *ctx, const char *netdev, struct optics **oi); s390-tools-2.38.0/opticsmon/link_mon.c000066400000000000000000000040141502674226300174330ustar00rootroot00000000000000/* * Copyright IBM Corp. 2024 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include "link_mon.h" #define MAX_EVENTS 32 static void nl_obj_parsed_cb(struct nl_object *obj, void *arg) { struct link_mon_nl_ctx *ctx = arg; struct rtnl_link *link; struct zpci_netdev netdev; if (strcmp(nl_object_get_type(obj), "route/link") != 0) return; link = (struct rtnl_link *)obj; netdev.name = rtnl_link_get_name(link); netdev.operstate = rtnl_link_get_operstate(link); ctx->cb(&netdev, ctx->arg); } static int nl_rtnl_lnkgrp_cb(struct nl_msg *msg, void *arg) { if (nl_msg_parse(msg, &nl_obj_parsed_cb, arg) < 0) fprintf(stderr, "<> Unknown message type\n"); return NL_STOP; } void link_mon_nl_waitfd_read(struct link_mon_nl_ctx *ctx) { nl_recvmsgs_default(ctx->sk); } int link_mon_nl_waitfd_getfd(struct link_mon_nl_ctx *ctx) { return nl_socket_get_fd(ctx->sk); } int link_mon_nl_waitfd_create(struct link_mon_nl_ctx *ctx, link_mon_nl_cb cb, void *arg) { int ret = 0, rc = 0; ctx->sk = nl_socket_alloc(); if (!ctx->sk) return -ENOMEM; ctx->cb = cb; ctx->arg = arg; nl_socket_disable_seq_check(ctx->sk); nl_socket_modify_cb(ctx->sk, NL_CB_VALID, NL_CB_CUSTOM, nl_rtnl_lnkgrp_cb, ctx); rc = nl_connect(ctx->sk, NETLINK_ROUTE); if (rc < 0) { ret = rc; goto err_free; } rc = nl_socket_add_membership(ctx->sk, RTNLGRP_LINK); if (rc < 0) { ret = rc; goto err_close; } rc = rtnl_link_alloc_cache(ctx->sk, AF_UNSPEC, &ctx->cache); if (rc < 0) { ret = rc; goto err_close; } nl_cache_mngt_provide(ctx->cache); return 0; err_close: nl_close(ctx->sk); err_free: nl_socket_free(ctx->sk); return ret; } void link_mon_nl_waitfd_destroy(struct link_mon_nl_ctx *ctx) { nl_cache_free(ctx->cache); nl_close(ctx->sk); nl_socket_free(ctx->sk); } s390-tools-2.38.0/opticsmon/link_mon.h000066400000000000000000000013241502674226300174410ustar00rootroot00000000000000/* * Copyright IBM Corp. 2024 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #pragma once #include #include #include "lib/pci_list.h" typedef void (*link_mon_nl_cb)(struct zpci_netdev *, void *arg); struct link_mon_nl_ctx { /* private fields */ struct nl_sock *sk; struct nl_cache *cache; link_mon_nl_cb cb; void *arg; }; int link_mon_nl_waitfd_create(struct link_mon_nl_ctx *ctx, link_mon_nl_cb cb, void *arg); void link_mon_nl_waitfd_read(struct link_mon_nl_ctx *ctx); void link_mon_nl_waitfd_destroy(struct link_mon_nl_ctx *ctx); int link_mon_nl_waitfd_getfd(struct link_mon_nl_ctx *ctx); s390-tools-2.38.0/opticsmon/optics_info.c000066400000000000000000000100351502674226300201410ustar00rootroot00000000000000#include #include "optics_info.h" #define OPTICS_TYPE_OFFSET 0x0 #define OPTICS_SFP_LOS_IMPLEMENTED_OFFSET 0x41 #define OPTICS_SFP_LOS_IMPLEMENTED_MASK 0x2 #define OPTICS_SFP_A2H_OFFSET 0x100 #define OPTICS_SFP_LOS_OFFSET (OPTICS_SFP_A2H_OFFSET + 0x6e) #define OPTICS_SFP_DATA_NOT_READY_MASK 0x1 #define OPTICS_SFP_TX_FAULT_MASK 0x4 #define OPTICS_SFP_RX_LOS_MASK 0x2 #define OPTICS_QSFP28_LOS_IMPLEMENTED_OFFSET 0xC3 #define OPTICS_QSFP28_TX_LOS_IMPLEMENTED_MASK 0x2 #define OPTICS_QSFP28_TX_FAULT_IMPLEMENTED_MASK 0x8 #define OPTICS_QSFP28_LOS_OFFSET 0x3 #define OPTICS_QSFP28_LOS_MASK 0xf #define OPTICS_QSFP28_TX_LOS_MASK 0xf0 #define OPTICS_QSFP28_TX_LOS_SHIFT 0x4 #define OPTICS_QSFP28_TX_FAULT_OFFSET 0x4 #define OPTICS_QSFP28_TX_FAULT_MASK 0xf const char *optics_type_str(enum optics_type type) { switch (type) { case OPTICS_TYPE_UNKNOWN: return "unknown"; case OPTICS_TYPE_SFP: return "SFP/SFP+/SFP28"; case OPTICS_TYPE_QSFP28: return "QSFP28"; }; return "n.a."; } enum optics_type optics_type(struct optics *oi) { if (!oi || !oi->raw || oi->size < OPTICS_TYPE_OFFSET + 1) return OPTICS_TYPE_UNKNOWN; switch (oi->raw[OPTICS_TYPE_OFFSET]) { case (uint8_t)OPTICS_TYPE_SFP: return OPTICS_TYPE_SFP; case (uint8_t)OPTICS_TYPE_QSFP28: return OPTICS_TYPE_QSFP28; default: return OPTICS_TYPE_UNKNOWN; }; } bool optics_los_implemented(struct optics *oi) { enum optics_type type = optics_type(oi); uint8_t implemented; if (type == OPTICS_TYPE_SFP) { if (oi->size < OPTICS_SFP_LOS_IMPLEMENTED_OFFSET + 1) return false; implemented = oi->raw[OPTICS_SFP_LOS_IMPLEMENTED_OFFSET]; return !!(implemented & OPTICS_SFP_LOS_IMPLEMENTED_MASK); } else if (type == OPTICS_TYPE_QSFP28) { if (oi->size < OPTICS_QSFP28_LOS_OFFSET + 1) return false; if (oi->size < OPTICS_QSFP28_LOS_IMPLEMENTED_OFFSET) return false; implemented = oi->raw[OPTICS_QSFP28_LOS_IMPLEMENTED_OFFSET]; /* * No RX LoS implemented flag take TX LOS implemented like * ethtool */ return !!(implemented & OPTICS_QSFP28_TX_LOS_IMPLEMENTED_MASK); } return false; } enum optics_los optics_rx_los(struct optics *oi) { enum optics_los los = OPTICS_UNKNOWN_LOS; enum optics_type type = optics_type(oi); if (!optics_los_implemented(oi)) return los; if (type == OPTICS_TYPE_SFP) { los = oi->raw[OPTICS_SFP_LOS_OFFSET]; if (los & OPTICS_SFP_DATA_NOT_READY_MASK) return OPTICS_UNKNOWN_LOS; if (los & OPTICS_SFP_RX_LOS_MASK) return OPTICS_LOS; else return OPTICS_NO_LOS; } else if (type == OPTICS_TYPE_QSFP28) { los = oi->raw[OPTICS_QSFP28_LOS_OFFSET]; if (los & OPTICS_QSFP28_LOS_MASK) los = OPTICS_LOS; else los = OPTICS_NO_LOS; } return los; } const char *optics_los_str(enum optics_los los) { switch (los) { case OPTICS_LOS: return "yes"; case OPTICS_NO_LOS: return "no"; case OPTICS_UNAVAILABLE_LOS: return "unavailable"; default: return "unknown"; } } enum optics_los optics_tx_fault(struct optics *oi) { enum optics_los los = OPTICS_UNKNOWN_LOS; enum optics_type type = optics_type(oi); if (!optics_los_implemented(oi)) return los; if (type == OPTICS_TYPE_SFP) { los = oi->raw[OPTICS_SFP_LOS_OFFSET]; if (los & OPTICS_SFP_DATA_NOT_READY_MASK) return OPTICS_UNKNOWN_LOS; if (los & OPTICS_SFP_TX_FAULT_MASK) return OPTICS_LOS; else return OPTICS_NO_LOS; } else if (type == OPTICS_TYPE_QSFP28) { los = oi->raw[OPTICS_QSFP28_TX_FAULT_OFFSET]; if (los & OPTICS_QSFP28_TX_FAULT_MASK) los = OPTICS_LOS; else los = OPTICS_NO_LOS; } return los; } enum optics_los optics_tx_los(struct optics *oi) { enum optics_los los = OPTICS_UNKNOWN_LOS; enum optics_type type = optics_type(oi); if (!optics_los_implemented(oi)) return los; if (type == OPTICS_TYPE_SFP) { return OPTICS_UNAVAILABLE_LOS; } else if (type == OPTICS_TYPE_QSFP28) { los = oi->raw[OPTICS_QSFP28_LOS_OFFSET]; if (los & OPTICS_QSFP28_TX_LOS_MASK) los = OPTICS_LOS; else los = OPTICS_NO_LOS; } return los; } void optics_free(struct optics *oi) { free(oi->raw); free(oi); } s390-tools-2.38.0/opticsmon/optics_info.h000066400000000000000000000023341502674226300201510ustar00rootroot00000000000000#pragma once #include #include #include #define SFF8079_I2C_ADDRESS_LOW 0x50 #define SFF8079_I2C_ADDRESS_HIGH 0x51 #define SFF8472_DIAGNOSTICS_TYPE_OFFSET 0x5C #define SFF8472_DIAGNOSTICS_TYPE_MASK (1 << 6) #define SFF8636_PAGE_SIZE 0x80 #define SFF8636_QSFP28_LENGTH 0x100 #define SFF8636_STATUS_2_OFFSET 0x02 #define SFF8636_STATUS_FLAT_MEM (1 << 2) #define SFF8636_PAGE_OFFSET 0xC3 #define SFF8636_P01H (1 << 6) #define SFF8636_P02H (1 << 7) enum optics_type { OPTICS_TYPE_UNKNOWN = 0x0, /* Unknown or unsupported */ OPTICS_TYPE_SFP = 0x3, /* SFP/SFP+/SFP28 and later with SFF-8472 management interface */ OPTICS_TYPE_QSFP28 = 0x11 /* QSFP28 (SFF-8665 et al.)*/ }; enum optics_los { OPTICS_NO_LOS = 0x0, OPTICS_LOS = 0x1, OPTICS_UNKNOWN_LOS = 0x2, OPTICS_UNAVAILABLE_LOS = 0x3, }; struct optics { size_t size; uint8_t *raw; }; enum optics_type optics_type(struct optics *oi); const char *optics_type_str(enum optics_type type); const char *optics_los_str(enum optics_los los); enum optics_los optics_rx_los(struct optics *oi); enum optics_los optics_tx_los(struct optics *oi); enum optics_los optics_tx_fault(struct optics *oi); void optics_free(struct optics *oi); s390-tools-2.38.0/opticsmon/optics_sclp.c000066400000000000000000000022231502674226300201470ustar00rootroot00000000000000#include #include #include #include "lib/pci_sclp.h" #include "lib/util_libc.h" #include "optics_sclp.h" static struct sclp_optics_data *init_sclp_optics_data(struct optics *oi, size_t *length) { struct sclp_optics_data *od; *length = sizeof(*od) + oi->size; od = util_zalloc(*length); od->module_present = optics_type(oi) != OPTICS_TYPE_UNKNOWN; od->rx_los = optics_rx_los(oi) == OPTICS_LOS; od->tx_fault = optics_tx_fault(oi) == OPTICS_LOS; switch (optics_type(oi)) { case OPTICS_TYPE_SFP: od->data_identifier = 1; break; case OPTICS_TYPE_QSFP28: od->data_identifier = 2; break; default: od->data_identifier = 0; } memcpy(od->data, oi->raw, oi->size); return od; } int sclp_issue_optics_report(struct zpci_dev *zdev, struct optics *oi) { struct sclp_optics_data *od; size_t length; char *pci_addr; int rc; if (zdev->pft != ZPCI_PFT_NETD) return -ENOTSUP; od = init_sclp_optics_data(oi, &length); pci_addr = zpci_pci_addr(zdev); rc = zpci_sclp_issue_action(pci_addr, SCLP_ERRNOTIFY_AQ_OPTICS_DATA, (char *)od, length, SCLP_ERRNOTIFY_ID_OPTICSMON); free(pci_addr); free(od); return rc; } s390-tools-2.38.0/opticsmon/optics_sclp.h000066400000000000000000000006741502674226300201640ustar00rootroot00000000000000#include #include "lib/pci_list.h" #include "optics_info.h" struct sclp_optics_data { /* Status */ uint32_t module_present : 1; uint32_t rx_los : 1; uint32_t tx_fault : 1; uint32_t reserved_status : 29; /* Data Identifier */ uint32_t data_identifier; /* Reserved */ uint64_t reserved[3]; /* Additional Log Data */ uint8_t data[]; } __packed; int sclp_issue_optics_report(struct zpci_dev *zdev, struct optics *oi); s390-tools-2.38.0/opticsmon/opticsmon.8000066400000000000000000000042551502674226300175740ustar00rootroot00000000000000.\" Copyright IBM Corp. 2024 .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .\" Macro for inserting an option description prologue. .\" .OD [] [args] .de OD . ds args " . if !'\\$3'' .as args \fI\\$3\fP . if !'\\$4'' .as args \\$4 . if !'\\$5'' .as args \fI\\$5\fP . if !'\\$6'' .as args \\$6 . if !'\\$7'' .as args \fI\\$7\fP . PD 0 . if !'\\$2'' .IP "\fB\-\\$2\fP \\*[args]" 4 . if !'\\$1'' .IP "\fB\-\-\\$1\fP \\*[args]" 4 . PD .. .\" Macro for inserting code line. .\" .CL .de CL . ds pfont \fP . nh . na . ft CW \\$* . ft \\*[pfont] . ad . hy . br .. . .TH opticsmon 8 "Oct 2024" s390-tools zpcictl . .SH NAME opticsmon - Monitor optical modules for directly attached PCI based NICs . . .SH SYNOPSIS .B "opticsmon" .I "OPTIONS" . . .SH DESCRIPTION Use .B opticsmon to monitor the health of the optical modules of directly attached PCI based NICs. When executed without the \fB--daemon\fR option it will collect optical module data from all available PCI network interface physical functions and print a summary in JSON format. Add the \fB--send-report\fR option to report this data to the support element. . . .SH OPTIONS .SS Operation Options .OD daemon "d" Run continuously and report on link state changes and periodically .PP . .OD send-report "r" Report the optics health data to the Support Element (SE) .PP . .OD quiet "q" Be quiet and don't print optics health summary .PP . .OD interval "i" "seconds" Interval in seconds at which to collect monitoring data in the absence of link state changes. A value larger than 24 hours (86400 seconds) is clamped down to 24 hours. .PP . .OD module-info "" Include a base64 encoded binary dump of the module's SFF-8636/8472/8024 standard data for each netdev. This matches "ethtool --module-info raw on". .B Example: Extract module information for the first adapter .CL opticsmon --module-info | jq -r '.adapters[0].netdevs[0].optics.module_info' | base64 -d | hexdump -C .PP .PP . .SS General Options .OD help "h" "" Print usage information, then exit. .PP . .OD version "v" "" Print version information, then exit. .PP s390-tools-2.38.0/opticsmon/opticsmon.c000066400000000000000000000254711502674226300176520ustar00rootroot00000000000000/* * opticsmon - Report optics monitoring data to firmware * * Copyright IBM Corp. 2024 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include "lib/util_list.h" #include "lib/pci_list.h" #include "lib/util_prg.h" #include "lib/util_opt.h" #include "lib/util_fmt.h" #include "lib/util_libc.h" #include #include "optics_info.h" #include "optics_sclp.h" #include "ethtool.h" #include "link_mon.h" #define API_LEVEL 1 struct options { bool monitor; bool report; bool module_info; bool quiet; uint32_t interval_seconds; }; struct opticsmon_ctx { struct options opts; struct ethtool_nl_ctx ethtool_ctx; struct link_mon_nl_ctx lctx; struct util_list *zpci_list; }; static const struct util_prg prg = { .desc = "Use opticsmon to monitor the health of the optical modules\n" "of directly attached PCI based NICs", .copyright_vec = { { .owner = "IBM Corp.", .pub_first = 2024, .pub_last = 2024, }, UTIL_PRG_COPYRIGHT_END } }; #define OPT_DUMP 128 static struct util_opt opt_vec[] = { UTIL_OPT_SECTION("OPERATION OPTIONS"), { .option = { "monitor", no_argument, NULL, 'm' }, .desc = "Run continuously and report on link state changes " "collecting optics health data when a change is detected", }, { .option = { "send-report", no_argument, NULL, 'r' }, .desc = "Report the optics health data to the Support Element", }, { .option = { "quiet", no_argument, NULL, 'q' }, .desc = "Be quiet and don't print optics health summary", }, { .option = { "module-info", no_argument, NULL, OPT_DUMP }, .desc = "Include a base64 encoded binary dump of the module's " "SFF-8636/8472/8024 standard data for each netdev. " "This matches \"ethtool --module-info raw on\"", .flags = UTIL_OPT_FLAG_NOSHORT, }, UTIL_OPT_SECTION("OPTIONS WITH ARGUMENTS"), { .option = { "interval", required_argument, NULL, 'i' }, .argument = "seconds", .desc = "Interval in seconds at which to collect monitoring data " "in the absence of link state changes. A value larger than " "24 hours (86400 seconds) is clamped down to 24 hours.", }, UTIL_OPT_SECTION("GENERAL OPTIONS"), UTIL_OPT_HELP, UTIL_OPT_VERSION, UTIL_OPT_END }; static void parse_cmdline(int argc, char *argv[], struct options *opts) { uint32_t seconds; int cmd, ret; util_prg_init(&prg); util_opt_init(opt_vec, NULL); do { cmd = util_opt_getopt_long(argc, argv); switch (cmd) { case 'm': opts->monitor = true; break; case 'r': opts->report = true; break; case 'q': opts->quiet = true; break; case OPT_DUMP: opts->module_info = true; break; case 'i': ret = sscanf(optarg, "%u", &seconds); if (ret != 1) { fprintf(stderr, "Failed to parse interval argument \"%s\" as seconds\n", optarg); exit(EXIT_FAILURE); } if (seconds < 86400) opts->interval_seconds = seconds; break; case 'h': util_prg_print_help(); util_opt_print_help(); exit(EXIT_SUCCESS); case 'v': util_prg_print_version(); exit(EXIT_SUCCESS); case -1: /* End of options string */ break; } } while (cmd != -1); } static int module_info_pair(struct optics *oi) { size_t b64_calclen, b64len; int rc = EXIT_SUCCESS; char *b64; b64_calclen = (oi->size / 3) * 4; if (oi->size % 3 > 0) b64_calclen += 4; b64 = util_zalloc(b64_calclen + 1); /* adds NUL byte */ b64len = EVP_EncodeBlock((unsigned char *)b64, oi->raw, oi->size); if (b64len != b64_calclen) { fprintf(stderr, "encoding base64 via openssl failed\n"); rc = EXIT_FAILURE; goto out; } util_fmt_pair(FMT_QUOTE, "module_info", b64); out: free(b64); return rc; } static void optics_json_print(struct opticsmon_ctx *ctx, struct zpci_netdev *nd, struct optics *oi) { util_fmt_obj_start(FMT_DEFAULT, "netdev"); util_fmt_pair(FMT_QUOTE, "name", nd->name); util_fmt_pair(FMT_QUOTE, "operstate", zpci_operstate_str(nd->operstate)); util_fmt_obj_start(FMT_DEFAULT, "optics"); util_fmt_pair(FMT_QUOTE, "type", optics_type_str(optics_type(oi))); util_fmt_pair(FMT_QUOTE, "rx_los", optics_los_str(optics_rx_los(oi))); util_fmt_pair(FMT_QUOTE, "tx_los", optics_los_str(optics_tx_los(oi))); util_fmt_pair(FMT_QUOTE, "tx_fault", optics_los_str(optics_rx_los(oi))); if (ctx->opts.module_info) module_info_pair(oi); util_fmt_obj_end(); util_fmt_obj_end(); } static int dump_adapter_data(struct opticsmon_ctx *ctx, struct zpci_dev *zdev) { struct optics **ois; int num_ois = 0; char *pci_addr; int i, rc; ois = util_zalloc(sizeof(ois[0]) * zdev->num_netdevs); for (i = 0; i < zdev->num_netdevs; i++) { rc = ethtool_nl_get_optics(&ctx->ethtool_ctx, zdev->netdevs[i].name, &ois[i]); if (rc) goto free_ois; num_ois++; } if (!ctx->opts.quiet) { util_fmt_obj_start(FMT_DEFAULT, "adapter"); util_fmt_pair(FMT_QUOTE, "pft", zpci_pft_str(zdev)); util_fmt_obj_start(FMT_DEFAULT, "ids"); util_fmt_pair(FMT_QUOTE, "fid", "0x%0x", zdev->fid); if (zdev->uid_is_unique) util_fmt_pair(FMT_QUOTE, "uid", "0x%0x", zdev->uid); pci_addr = zpci_pci_addr(zdev); util_fmt_pair(FMT_QUOTE, "pci_address", pci_addr); free(pci_addr); util_fmt_obj_end(); util_fmt_obj_start(FMT_LIST, "netdevs"); for (i = 0; i < zdev->num_netdevs; i++) optics_json_print(ctx, &zdev->netdevs[i], ois[i]); util_fmt_obj_end(); /* netdevs list */ util_fmt_obj_end(); /* adapter */ fflush(stdout); } if (ctx->opts.report) { for (i = 0; i < zdev->num_netdevs; i++) { rc = sclp_issue_optics_report(zdev, ois[i]); if (rc == -ENOTSUP) { fprintf(stderr, "Skipping %s which does not support reporting\n", zdev->netdevs[i].name); } else if (rc < 0) { fprintf(stderr, "Error issuing SCLP for optics data failed: %s\n", strerror(-rc)); } } } free_ois: for (i = 0; i < num_ois; i++) optics_free(ois[i]); free(ois); return rc; } static void zpci_list_reload(struct util_list **zpci_list) { if (*zpci_list) zpci_free_dev_list(*zpci_list); *zpci_list = zpci_dev_list(); } static void dump_all_adapter_data(struct opticsmon_ctx *ctx) { struct zpci_dev *zdev; zpci_list_reload(&ctx->zpci_list); util_list_iterate(ctx->zpci_list, zdev) { /* Filter non-NIC devices and VFs */ if (zpci_is_vf(zdev) || !zdev->num_netdevs) continue; dump_adapter_data(ctx, zdev); } } static int oneshot_mode(struct opticsmon_ctx *ctx) { util_fmt_init(stdout, FMT_JSON, FMT_DEFAULT, API_LEVEL); if (!ctx->opts.quiet) util_fmt_obj_start(FMT_LIST, "adapters"); dump_all_adapter_data(ctx); if (!ctx->opts.quiet) util_fmt_obj_end(); util_fmt_exit(); return EXIT_SUCCESS; } void on_link_change(struct zpci_netdev *netdev, void *arg) { struct opticsmon_ctx *ctx = arg; struct zpci_netdev *found_netdev; struct zpci_dev *zdev = NULL; int reloads = 1; do { if (ctx->zpci_list) { zdev = zpci_find_by_netdev(ctx->zpci_list, netdev->name, &found_netdev); if (zdev) { /* Skip data collection if operational state is * unchanged */ if (found_netdev->operstate == netdev->operstate) return; /* Update operation state for VFs even though * they are skipped just for a consistent view */ found_netdev->operstate = netdev->operstate; /* Only collect optics data for PFs */ if (!zpci_is_vf(zdev)) dump_adapter_data(ctx, zdev); return; } } /* Could be uninitalized list or a new device, retry after reload */ zpci_list_reload(&ctx->zpci_list); reloads--; } while (reloads > 0); } #define MAX_EVENTS 8 static int monitor_wait_loop(struct opticsmon_ctx *ctx, int sigfd, int timerfd) { struct epoll_event events[MAX_EVENTS]; struct signalfd_siginfo fdsi; int i, nlfd, epfd, nfds; struct epoll_event ev; uint64_t expirations; ssize_t sread; epfd = epoll_create1(EPOLL_CLOEXEC); ev.events = EPOLLIN; ev.data.fd = sigfd; if (epoll_ctl(epfd, EPOLL_CTL_ADD, sigfd, &ev) == -1) return -EIO; ev.events = EPOLLIN; ev.data.fd = timerfd; if (epoll_ctl(epfd, EPOLL_CTL_ADD, timerfd, &ev) == -1) return -EIO; nlfd = link_mon_nl_waitfd_getfd(&ctx->lctx); ev.events = EPOLLIN; ev.data.fd = nlfd; if (epoll_ctl(epfd, EPOLL_CTL_ADD, nlfd, &ev) == -1) return -EIO; while (1) { nfds = epoll_wait(epfd, events, MAX_EVENTS, -1); if (nfds < 0) return nfds; for (i = 0; i < nfds; i++) { /* signal fd */ if (events[i].data.fd == sigfd) { sread = read(sigfd, &fdsi, sizeof(fdsi)); if (sread != sizeof(fdsi)) return -EIO; switch (fdsi.ssi_signo) { case SIGINT: case SIGTERM: case SIGQUIT: return 0; /* Unexpected signal */ default: return -EIO; } /* timer fd */ } else if (events[i].data.fd == timerfd) { sread = read(timerfd, &expirations, sizeof(uint64_t)); if (sread != sizeof(uint64_t)) return -EIO; if (!expirations) continue; dump_all_adapter_data(ctx); /* netlink fd */ } else if (events[i].data.fd == nlfd) { link_mon_nl_waitfd_read(&ctx->lctx); } } } return 0; } static int monitor_mode(struct opticsmon_ctx *ctx) { struct itimerspec timerspec; int sigfd, timerfd, ret; sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGQUIT); sigaddset(&mask, SIGTERM); if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) return -EIO; sigfd = signalfd(-1, &mask, 0); if (sigfd == -1) { fprintf(stderr, "Failed to create signalfd\n"); return -EIO; } timerfd = timerfd_create(CLOCK_MONOTONIC, 0); if (timerfd == -1) { fprintf(stderr, "Failed to create timerfd\n"); ret = -EIO; goto close_signalfd; } /* Set initial expiration to 1 ns so we gather optics data at startup */ timerspec.it_value.tv_sec = 0; timerspec.it_value.tv_nsec = 1; timerspec.it_interval.tv_sec = ctx->opts.interval_seconds; timerspec.it_interval.tv_nsec = 0; ret = timerfd_settime(timerfd, 0, &timerspec, NULL); if (ret == -1) { fprintf(stderr, "Failed to arm timer\n"); goto close_timerfd; } util_fmt_init(stdout, FMT_JSONSEQ, FMT_DEFAULT, API_LEVEL); ret = link_mon_nl_waitfd_create(&ctx->lctx, on_link_change, ctx); if (ret) { fprintf(stderr, "Failed to create link monitoring socket\n"); goto close_timerfd; } monitor_wait_loop(ctx, sigfd, timerfd); link_mon_nl_waitfd_destroy(&ctx->lctx); util_fmt_exit(); close_signalfd: close(sigfd); close_timerfd: close(timerfd); return ret; } int main(int argc, char **argv) { struct opticsmon_ctx ctx = { .opts = { .interval_seconds = 86400 } }; int ret; parse_cmdline(argc, argv, &ctx.opts); ethtool_nl_connect(&ctx.ethtool_ctx); if (ctx.opts.monitor) ret = monitor_mode(&ctx); else ret = oneshot_mode(&ctx); ethtool_nl_close(&ctx.ethtool_ctx); if (ctx.zpci_list) zpci_free_dev_list(ctx.zpci_list); return ret; } s390-tools-2.38.0/osasnmpd/000077500000000000000000000000001502674226300152735ustar00rootroot00000000000000s390-tools-2.38.0/osasnmpd/Makefile000066400000000000000000000016251502674226300167370ustar00rootroot00000000000000include ../common.mak NET_SNMP_CONFIG = net-snmp-config LDLIBS = `$(NET_SNMP_CONFIG) --agent-libs` # On some Linux systems `net-snmp-config --agent-libs` introduces -pie, # therefore add -fPIC to prevent link failures. ALL_CFLAGS += -fPIC ALL_CFLAGS += `$(NET_SNMP_CONFIG) --cflags` OBJS = ibmOSAMib.o ibmOSAMibUtil.o osasnmpd.o ifeq (${HAVE_SNMP},0) all: $(SKIP) HAVE_SNMP=0 install: $(SKIP) HAVE_SNMP=0 else check_dep: $(call check_dep, \ "osasnmpd", \ "net-snmp/net-snmp-config.h", \ "net-snmp-devel or libsnmp-dev", \ "HAVE_SNMP=0") all: check_dep osasnmpd osasnmpd: $(OBJS) install: all $(INSTALL) -d -m 755 $(DESTDIR)$(USRSBINDIR) $(DESTDIR)$(MANDIR)/man8 $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 osasnmpd \ $(DESTDIR)$(USRSBINDIR) $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 osasnmpd.8 \ $(DESTDIR)$(MANDIR)/man8 endif clean: rm -f $(OBJS) osasnmpd core .PHONY: all install clean s390-tools-2.38.0/osasnmpd/ibmOSAMib.c000066400000000000000000000742721502674226300172150ustar00rootroot00000000000000/* * osasnmpd - IBM OSA-Express network card SNMP subagent * * Basic MIB implementation module for the OSA-E subagent * * The code in this module is typical for a net-snmp MIB implementation * information on how this works because the MIB layout is retrieved during * startup of the subagent, the magic identifier is not used within this * implementation. The var_ function uses the vp->type instead to distinct * the OIDs. * * Copyright 2017 IBM Corp. * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include "lib/zt_common.h" #include "ibmOSAMibUtil.h" #include "ibmOSAMib.h" /* ptr to OSA Express MIB information stored in linked lists */ TABLE_OID* oid_list_head; /* ptr to interface information on this system */ IF_LIST* if_list; int ifNumber; /********************************************************************** * init_ibmOSAMib(): * Initialization routine. This function is called when the agent * starts up. * parameters: * IN uses global MIB data * OUT none * returns: none *********************************************************************/ void init_ibmOSAMib(void) { int i, sd, /* socket descriptor */ error_code, /* holds errno value */ osaexp_num, /* number of OSA Express devices */ retc; /* return code from register_tables */ struct ifreq ifr; /* request structure for ioctl */ IPA_CMD_REG* ipa_reg_mib; /* structure for IPA REGISTER MIB command header */ char* buffer; /* a data buffer */ char time_buf[TIME_BUF_SIZE]; /* date/time buffer */ /* init head for Toplevel OID linked list */ oid_list_head = init_oid_list(); if ( oid_list_head == NULL ) { fprintf( stderr, "init_ibmOSAMib(): " "malloc() for OID list head failed\n" "Cannot start subagent...exiting...\n"); exit(1); } /* GET net-snmp ifNumber/ifIndex/ifDescr from IF-MIB for all interfaces */ /* on this system */ ifNumber = query_IF_MIB( &if_list ); if ( ifNumber < 0 ) { fprintf( stderr, "init_ibmOSAMib(): " "could not obtain interface info from IF-MIB\n" "check if: snmpd daemon is started and subagent " "access control is correct\n" "see agent log file for more details\n" "Cannot start subagent...exiting...\n"); exit(1); } else if ( ifNumber == 0 ) { get_time( time_buf ); snmp_log( LOG_ERR, "%s init_ibmOSAMib(): " "SNMP reports no devices within IF-MIB" " - starting subagent anyway\n", time_buf ); return; } /* end if */ /* query OSA-E device driver for OSA-E devices and mark them in IF-MIB interface list */ osaexp_num = query_OSA_EXP ( &if_list, ifNumber ); if ( osaexp_num == 0 ) { get_time( time_buf ); snmp_log( LOG_ERR, "%s init_ibmOSAMib(): none of the %d interfaces is a real " "OSA-E device - starting subagent anyway\n", time_buf, ifNumber); return; } /* end if */ /* allocate area, that should contain retrieved MIB data for a single interface */ buffer = (char*) malloc ( MIB_AREA_LEN ); if ( buffer == NULL ) { fprintf( stderr, "init_ibmOSAMib(): " "malloc() for REGISTER MIB data buffer " "failed\ninit_ibmOSAMib(): requested %d bytes\n" "Cannot start subagent...exiting...\n", MIB_AREA_LEN ); exit(1); } /* end if */ /* open socket for ioctl */ sd = socket( AF_INET, SOCK_STREAM, 0 ); if ( sd < 0 ) { error_code = errno; fprintf( stderr, "init_ibmOSAMIB(): " "error opening socket() - reason %s\n" "Cannot start subagent...exiting...\n", strerror( error_code ) ); exit(1); } /* end if */ /* walk through interface list and query MIB data for all OSA-E devices */ /* register MIB data with subagent driving code afterwards */ for ( i=0; i < ifNumber; i++ ) { if ( if_list[i].is_OSAEXP == TRUE ) { /* clear buffer */ memset( buffer, 0, MIB_AREA_LEN ); /* setup ioctl buffer with request and input parameters */ ipa_reg_mib = (IPA_CMD_REG*) buffer; /* map command structure */ ipa_reg_mib->ioctl_cmd.data_len = /* length of IPA data area */ MIB_AREA_LEN - offsetof( IOCTL_CMD_HDR, ipa_cmd_hdr ); ipa_reg_mib->ioctl_cmd.req_len = /* length of IPA subcommand */ sizeof( ipa_reg_mib->ioctl_cmd ); ipa_reg_mib->ioctl_cmd.ipa_cmd_hdr.request = IPA_REG_MIB; /* IPA subcommand code */ ipa_reg_mib->ioctl_cmd.ipa_cmd_hdr.ifIndex = if_list[i].ifIndex; /* assign IF-MIB ifIndex */ ipa_reg_mib->ioctl_cmd.ipa_cmd_hdr.ret_code = 0; ipa_reg_mib->ioctl_cmd.ipa_cmd_hdr.seq_num = 0; /* sequence number not used */ /* do ioctl */ strcpy( ifr.ifr_name, if_list[i].if_Name ); /* add interface name */ ifr.ifr_ifru.ifru_data = (char*) buffer; /* add data buffer */ if ( ioctl( sd, SIOC_QETH_ADP_SET_SNMP_CONTROL, &ifr ) < 0 ) { error_code = errno; /* see if we got a common I/O error */ if ( error_code == -EIO ) { get_time( time_buf ); snmp_log( LOG_ERR, "%s init_ibmOSAMib(): " "ioctl() failed - reason %s for interface %s\n" "init_ibmOSAMib(): start subagent anyway\n", time_buf, strerror( error_code ), if_list[i].if_Name ); close( sd ); free( buffer ); return; break; } /* end if */ /* let's see, if we got a return code from IPAssists */ /* or if MIB buffer is exhausted */ switch ( ipa_reg_mib->ioctl_cmd.ipa_cmd_hdr.ret_code ) { case IPA_FAILED: fprintf( stderr, "init_ibmOSAMib(): " "ioctl() failed - IPA command failed " "init_ibmOSAMib(): " "Can't get MIB information for network interface %s\n" "Cannot start subagent...exiting...\n", if_list[i].if_Name ); break; case IPA_NOT_SUPP: fprintf( stderr, "init_ibmOSAMib(): " "ioctl() failed - IPA command not supported " "init_ibmOSAMib(): " "Can't get MIB information for network interface %s\n" "Cannot start subagent...exiting...\n", if_list[i].if_Name ); break; case IPA_NO_DATA: get_time( time_buf ); snmp_log( LOG_ERR, "%s init_ibmOSAMib(): " "ioctl() failed - valid IPA command, but no" " SNMP data is available for interface %s\n" "init_ibmOSAMib(): start subagent anyway\n", time_buf, if_list[i].if_Name ); close( sd ); free( buffer ); return; break; case -ENOMEM: /* should not happen in the near future ;-) */ fprintf( stderr, "init_ibmOSAMib(): " "ioctl() failed - MIB data size > " "constant MIB_AREA_LEN\n" "init_ibmOSAMib(): " "Enlarge constant for MIB_AREA_LEN within " "ibmOSAMibDefs.h and recompile the subagent\n" "init_ibmOSAMib(): " "Can't get MIB information for network interfaces\n" "Cannot start subagent...exiting...\n" ); break; default: fprintf( stderr, "init_ibmOSAMib(): " "ioctl() failed - reason %s\n" "init_ibmOSAMib(): " "Can't get MIB information for network interface %s\n" "Cannot start subagent...exiting...\n", strerror( error_code ), if_list[i].if_Name ); break; } /* end switch */ exit(1); } else if( ipa_reg_mib->ioctl_cmd.ipa_cmd_hdr.ret_code != 0 ) { /* now check IPA SNMP subcommand return code */ switch ( ipa_reg_mib->ioctl_cmd.ipa_cmd_hdr.ret_code ) { case IPA_SNMP_INV_TOPOID: case IPA_SNMP_INV_GROUP: case IPA_SNMP_INV_SUFFIX: case IPA_SNMP_INV_INST: case IPA_SNMP_OID_NREAD: case IPA_SNMP_OID_NWRIT: get_time( time_buf ); snmp_log( LOG_ERR, "%s init_ibmOSAMib(): " "IPA SNMP subcommand failed\n" "init_ibmOSAMib(): " "IPA SNMP subcommand return code 0x%x\n" "init_ibmOSAMib(): " "Can't get MIB information for network interface %s\n" "Cannot start subagent...exiting...\n", time_buf, ipa_reg_mib->ioctl_cmd.ipa_cmd_hdr.ret_code, if_list[i].if_Name ); break; case IPA_SNMP_NOT_SUPP: get_time( time_buf ); snmp_log( LOG_ERR, "%s init_ibmOSAMib(): " "IPA SNMP subcommand failed - subcommand 0x%x " "not supported\ninit_ibmOSAMib(): " "Can't get MIB information for network interface %s\n" "Cannot start subagent...exiting...\n", time_buf, ipa_reg_mib->ioctl_cmd.ipa_cmd_hdr.request, if_list[i].if_Name ); break; case IPA_SNMP_NO_DATA: get_time( time_buf ); snmp_log( LOG_ERR, "%s init_ibmOSAMib(): " "IPA SNMP subcommand failed - no data available\n" "init_ibmOSAMib(): " "Can't get MIB information for network interface %s\n" "Cannot start subagent...exiting...\n", time_buf, if_list[i].if_Name ); break; default: get_time( time_buf ); snmp_log( LOG_ERR, "%s init_ibmOSAMib(): " "IPA SNMP subcommand failed - undefined return code" " 0x%x\ninit_ibmOSAMib(): " "Can't get MIB information for network interface %s\n" "Cannot start subagent...exiting...\n", time_buf, ipa_reg_mib->ioctl_cmd.ipa_cmd_hdr.ret_code, if_list[i].if_Name ); break; } /* end switch */ exit(1); } /* end if */ /* save microcode level */ if_list[i].ipa_ver = ipa_reg_mib->ioctl_cmd.ipa_cmd_hdr.ipa_ver; /* register initial table information, that we got from IPAssists */ retc = register_tables ( buffer, oid_list_head ); if ( retc != 0 ) { fprintf( stderr, "init_ibmOSAMib(): " "register MIB data with subagent driving " "code failed\ninit_ibmOSAMib(): for ifIndex %d ifDescr %s\n" "check agent log file for more details\n" "Cannot start subagent...exiting...\n", if_list[i].ifIndex, if_list[i].if_Name ); exit(1); } /* end if */ } /* end if */ } /* end for */ /* log IPA microcode level per interface */ for ( i=0; i < ifNumber; i++ ) { if ( if_list[i].is_OSAEXP == TRUE ) snmp_log( LOG_INFO, "OSA-E microcode level is %x for interface %s\n", if_list[i].ipa_ver, if_list[i].if_Name ); } /* end for */ /* free resources */ close( sd ); free( buffer ); } /* end init_ibmOSAMib */ /********************************************************************** * var_ibmOSAMib(): * This function is called every time the agent gets a request for * any MIB data for the IBM OSA express MIB. It's up to this function * to return the appropriate data back to the subagent driving code. * This function supports all standard SNMIv2 data types. * parameters: * IN variable vp - entry in variableN array * INOUT oid *name - OID from original request/OID being returned * INOUT size_t *length - length of orig. OID/length of ret. OID * IN int exact - exact/inexact request * OUT size_t *var_len - length of answer being returned * OUT WriteMethod **write_method - unused * returns: NULL - vp entry does not match or instance wrong * - something within ioctl handling failed * else data returned as answer *********************************************************************/ unsigned char* var_ibmOSAMib( struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { /* variables for returning data back to the subagent driving code */ static long long_ret; static unsigned char octetstr_buf[MAX_GET_DATA]; static oid objid[MAX_OID_LEN]; static struct counter64 osa_counter64; long *ptr_long; int *ptr_int; unsigned char *ptr_uchar; int ifIndex; /* IF-MIB ifIndex of the OSA device that is queried for data */ int offset; /* offset to returned data portion within GET command area */ char time_buf[TIME_BUF_SIZE]; /* date/time buffer */ IPA_CMD_GET *get_cmd; /* area for GET command */ IPA_GET_DATA *get_res; /* pointer to offset where data portion starts */ void *tmp_ptr; /* * This function compares the full OID that is passed in to the registered * OIDs from this subagent. * It is the IBM OSA Express specific version of the default * header_simple_table() function, that is normally used in case of a simple * table. Place a mutual exlusion lock around this operation to avoid * interfering threads, when updating the internal MIB table thru thread * update_mib_info() */ ifIndex = header_osa_table( vp, name, length, exact, var_len, write_method, oid_list_head ); if ( ifIndex == MATCH_FAILED ) return NULL; /* issue ioctl to query Get/Getnext request data */ offset = do_GET_ioctl ( ifIndex, name, *length, &get_cmd ); if ( offset < 0 ) { return NULL; } /* * return the result to subagent driving code */ /* map data portion returned by IPAssists */ /* # ptr GET command area + offset returned data portion */ /* # align PTR to 4 byte bdy where data portion starts */ tmp_ptr = (char*) get_cmd; tmp_ptr += offset; get_res = (IPA_GET_DATA*) (PTR_ALIGN4( tmp_ptr )); switch( vp->type ) { case ASN_INTEGER: case ASN_COUNTER: case ASN_GAUGE: case ASN_TIMETICKS: /* ASN_UNSIGNED is same as ASN_GAUGE (RFC1902) */ if ( get_res->len == sizeof(int) ) { ptr_int = (int*) get_res->data; long_ret = (long) *ptr_int; } else { ptr_long = (long*) get_res->data; long_ret = (long) *ptr_long; } /* end if */ free( get_cmd ); return (unsigned char *) &long_ret; break; case ASN_COUNTER64: if ( get_res->len > 8 ) { get_time( time_buf ); snmp_log( LOG_ERR, "%s var_ibmOSAMib(): " "IPA data length for ASN_COUNTER64 > 8 bytes\n" "var_ibmOSAMib(): rejected Get/Getnext request\n", time_buf ); free( get_cmd ); return NULL; } /* end if */ /* IPA returns 8 bytes for COUNTER64 */ ptr_int = (int*) get_res->data; osa_counter64.high = (int) *ptr_int; ptr_int++; osa_counter64.low = (int) *ptr_int; *var_len = sizeof( osa_counter64 ); free( get_cmd ); return (unsigned char *) &osa_counter64; break; case ASN_OPAQUE: /* old v1 type/included for compatibility */ case ASN_OCTET_STR: /* used for Binary data */ /* case Display String is handled within var_DisplayStr() */ if ( get_res->len > MAX_GET_DATA ) { get_time( time_buf ); snmp_log( LOG_ERR, "%s var_ibmOSAMib(): " "IPA data length %d for ASN_OCTET_STR > " "MAX_GET_DATA (%d bytes)\n" "var_ibmOSAMib(): rejected Get/Getnext request\n", time_buf, get_res->len, MAX_GET_DATA ); free( get_cmd ); return NULL; } /* end if */ *var_len = get_res->len; ptr_uchar = (unsigned char*) get_res->data; memcpy( octetstr_buf, ptr_uchar, *var_len ); free( get_cmd ); return (unsigned char *) octetstr_buf; break; case ASN_IPADDRESS: /* IPA IpAddress within 4 bytes hex data */ ptr_int = (int*) get_res->data; long_ret = (long) *ptr_int; free( get_cmd ); return (unsigned char *) &long_ret; break; case ASN_OBJECT_ID: /* IPA returned ObjectId as character string, have to convert... */ *var_len = str_to_oid_conv ( get_res->data, objid ); if ( *var_len == 0 ) { get_time( time_buf ); snmp_log( LOG_ERR, "%s var_ibmOSAMib(): IPA returned bad ObjectId - " "cannot convert net-snmp oid type\n" "var_ibmOSAMib(): rejected Get/Getnext request\n", time_buf ); free( get_cmd ); return NULL; } /* end if */ *var_len = (*var_len) * sizeof( oid ); free( get_cmd ); return (unsigned char *) &objid; break; default: get_time( time_buf ); snmp_log( LOG_ERR, "%s var_ibmOSAMib(): " "got a not known ASN data type %x\n" "var_ibmOSAMib(): " "rejected Get/Getnext request\n", time_buf, vp->type ); } /* end switch */ free( get_cmd ); return NULL; } /* end var_ibmOSAMib */ /********************************************************************** * var_DisplayStr(): * This function handles the special case for Display Strings, which are * a textual convention to Octet Strings. The binary data case for * Octet Strings is handled within var_ibmOSAMib(). * It's up to this function to return the appropriate data back to the * subagent driving code. * parameters: * IN variable vp - entry in variableN array * INOUT oid *name - OID from original request/OID being returned * INOUT size_t *length - length of orig. OID/length of ret. OID * IN int exact - exact/inexact request * OUT size_t *var_len - length of answer being returned * OUT WriteMethod **write_method - unused * returns: NULL - vp entry does not match or instance wrong * - something within ioctl handling failed * else data returned as answer *********************************************************************/ unsigned char* var_DisplayStr( struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { /* variables for returning a display string to the subagent driving code */ static char string[SPRINT_MAX_LEN]; char time_buf[TIME_BUF_SIZE]; /* date/time buffer */ int ifIndex; /* IF-MIB ifIndex of the OSA device that is queried for data */ int offset; /* offset to returned data portion within GET command area */ IPA_CMD_GET *get_cmd; /* area for GET command */ IPA_GET_DATA *get_res; /* pointer to offset where data portion starts */ void *tmp_ptr; /* * This function compares the full OID that is passed in to the registered * OIDs from this subagent. * It is the IBM OSA Express specific version of the default * header_simple_table() function, that is normally used in case of a simple * table. Place a mutual exlusion lock around this operation to avoid * interfering threads, when updating the internal MIB table thru thread * update_mib_info() */ ifIndex = header_osa_table( vp, name, length, exact, var_len, write_method, oid_list_head ); if ( ifIndex == MATCH_FAILED ) return NULL; /* issue ioctl to query Get/Getnext request data */ offset = do_GET_ioctl ( ifIndex, name, *length, &get_cmd ); if ( offset < 0 ) { return NULL; } /* * return the result to subagent driving code */ /* map data portion returned by IPAssists */ /* # ptr GET command area + offset returned data portion */ /* # align PTR to 4 byte bdy where data portion starts */ tmp_ptr = (char*) get_cmd; tmp_ptr += offset; get_res = (IPA_GET_DATA*) (PTR_ALIGN4( tmp_ptr )); if ( vp->type == ASN_OCTET_STR) { if ( get_res->len >= SPRINT_MAX_LEN ) { get_time( time_buf ); snmp_log( LOG_ERR, "%s var_DisplayStr(): " "IPA data length %d for Display " "String >= SPRINT_MAX_LEN (%d bytes)\n" "var_ibmOSAMib(): rejected Get/Getnext request\n" ,time_buf, get_res->len, SPRINT_MAX_LEN ); free( get_cmd ); return NULL; } /* end if */ strncpy( string, get_res->data, get_res->len ); string[ get_res->len ] = '\0'; *var_len = strlen( string ); free( get_cmd ); return (unsigned char *) string; } else { get_time( time_buf ); snmp_log( LOG_ERR, "%s var_DisplayStr(): " "expected a Display String here, " "but got a different ASN data type: %x\n" "var_DisplayStr(): " "rejected Get/Getnext request\n", time_buf, vp->type ); } /* end if */ free( get_cmd ); return NULL; } /* end var_DisplayStr */ /********************************************************************** * do_GET_ioctl() * This function handles the communication with an OSA Express Card * to query the appropriate MIB information from IPAssists. * An ioctl is used in order to qet the appropriate information. * parameters: * IN int ifIndex - IF-MIB interface index * IN oid *name - OID being returned * IN size_t len - length of ret. OID * INOUT IPA_CMD_GET** cmd - GET command area * returns: cmd_len - return offset to returned data * -1 - ioctl() was not successful *********************************************************************/ int do_GET_ioctl ( int ifIndex, oid *name, size_t len, IPA_CMD_GET **cmd ) { int sd; /* socket descriptor */ int i, error_code; char oid_str[MAX_OID_STR_LEN]; /* may hold an OID as string */ char time_buf[TIME_BUF_SIZE]; /* date/time buffer */ char device[IFNAME_MAXLEN] = "not_found"; /* device name for ioctl */ struct ifreq ifr; /* request structure for ioctl */ /* search device name in in global interface list for ifIndex */ for ( i=0; i < ifNumber; i++ ) { if ( if_list[i].ifIndex == ifIndex ) { strcpy( device, if_list[i].if_Name ); break; } } /* end for */ if ( strcmp( device, "not_found" ) == 0 ) { get_time( time_buf ); snmp_log( LOG_ERR, "%s do_GET_ioctl(): " "ifIndex %d is not recorded in " "interface list\n" "OSA Subagent MIB information may be incomplete!\n" ,time_buf, ifIndex ); return -1; } /* * query IPAssists for data appropriate to the OID that we just validated */ /* convert Get/GetNext OID to a string used by IPA */ if( oid_to_str_conv ( name, len, oid_str ) == FALSE ) { get_time( time_buf ); snmp_log( LOG_ERR, "%s do_GET_ioctl(): " "cannot convert OID to string object\n" "do_GET_ioctl(): rejected request\n", time_buf ); return -1; } /* allocate memory for Get/GetNext command area */ *cmd = ( IPA_CMD_GET* ) malloc( GET_AREA_LEN ); if ( *cmd == NULL ) { get_time( time_buf ); snmp_log( LOG_ERR, "%s do_GET_ioctl(): " "malloc() for GET command area failed\n" "do_GET_ioctl(): rejected request for .%s\n", time_buf, oid_str ); return -1; } /* end if */ /* set up input parameters in Get/GetNext command area */ /* size of IPA data area */ (*cmd)->ioctl_cmd.data_len = GET_AREA_LEN - offsetof( IOCTL_CMD_HDR, ipa_cmd_hdr ); /* size of IPA GET subcommand padded to 4-byte bdy */ (*cmd)->ioctl_cmd.req_len = (sizeof((*cmd)->ioctl_cmd) + strlen( oid_str ) + 1 + 3)&(~3); /* set up input parameters in Get/GetNext command area */ (*cmd)->ioctl_cmd.ipa_cmd_hdr.request = IPA_GET_OID; /* IPA subcommand code */ (*cmd)->ioctl_cmd.ipa_cmd_hdr.ifIndex = ifIndex; /* assign IF-MIB ifIndex */ (*cmd)->ioctl_cmd.ipa_cmd_hdr.ret_code = 0; (*cmd)->ioctl_cmd.ipa_cmd_hdr.seq_num = 0; /* sequence# is not used */ strcpy( (*cmd)->full_oid, oid_str ); /* requested OID */ /* (fully qualified) */ /* * issue Get/GetNext command against IPAssists */ /* create socket for ioctl */ sd = socket( AF_INET, SOCK_STREAM, 0 ); if ( sd < 0 ) { error_code = errno; get_time( time_buf ); snmp_log(LOG_ERR, "%s do_GET_ioctl(): " "error opening socket() - reason %s\n" "do_GET_ioctl(): rejected request for .%s\n", time_buf, strerror( error_code ), oid_str ); free( *cmd ); return -1; } /* end if */ /* do ioctl */ strcpy( ifr.ifr_name, device ); ifr.ifr_ifru.ifru_data = (char*) (*cmd); if ( ioctl( sd, SIOC_QETH_ADP_SET_SNMP_CONTROL, &ifr ) < 0 ) { error_code = errno; get_time( time_buf ); /* see if we got a common I/O error */ if ( error_code == -EIO ) { snmp_log( LOG_ERR, "%s do_GET_ioctl(): " "ioctl() failed - reason %s\n" "do_GET_ioctl(): rejected request for .%s\n", time_buf, strerror( error_code ), oid_str ); close( sd ); free( *cmd ); return -1; } /* end if */ /* let's see, if we got a return code from IPAssists */ /* or if MIB buffer is exhausted */ switch ( (*cmd)->ioctl_cmd.ipa_cmd_hdr.ret_code ) { case IPA_FAILED: snmp_log( LOG_ERR, "%s do_GET_ioctl(): " "ioctl() failed - IPA command failed\n" "do_GET_ioctl(): rejected request for .%s\n", time_buf, oid_str ); break; case IPA_NOT_SUPP: snmp_log( LOG_ERR, "%s do_GET_ioctl(): " "ioctl() failed - IPA command not supported\n" "do_GET_ioctl(): rejected request for .%s\n", time_buf, oid_str ); break; case IPA_NO_DATA: snmp_log( LOG_ERR, "%s do_GET_ioctl(): " "ioctl() failed - valid IPA command, but no " "SNMP data is available\n" "do_GET_ioctl(): rejected request for .%s\n", time_buf, oid_str ); break; case -ENOMEM: snmp_log( LOG_ERR, "%s do_GET_ioctl(): " "ioctl() failed - response data > " "constant MAX_GET_DATA %d\n" "do_GET_ioctl(): rejected request for .%s\n", time_buf, MAX_GET_DATA, oid_str ); break; default: snmp_log(LOG_ERR, "%s do_GET_ioctl(): " "ioctl() failed - reason %s\n" "do_GET_ioctl(): rejected request for .%s\n", time_buf, strerror( error_code ), oid_str ); break; } /* end switch */ close( sd ); free( *cmd ); return -1; } /* end if */ /* close socket */ close( sd ); /* now check IPA SNMP subcommand return code */ switch ( (*cmd)->ioctl_cmd.ipa_cmd_hdr.ret_code ) { case IPA_SNMP_SUCCESS: /* return offset to data portion */ return ( sizeof( IPA_CMD_GET ) + strlen( oid_str ) + 1 ); break; case IPA_SNMP_INV_TOPOID: case IPA_SNMP_INV_GROUP: case IPA_SNMP_INV_SUFFIX: case IPA_SNMP_INV_INST: case IPA_SNMP_OID_NREAD: case IPA_SNMP_OID_NWRIT: get_time( time_buf ); snmp_log( LOG_ERR, "%s do_GET_ioctl(): " "IPA SNMP subcommand failed - cannot handle OID\n" "do_GET_ioctl(): IPA SNMP subcommand return code 0x%x\n" "do_GET_ioctl(): rejected request for .%s\n", time_buf, (*cmd)->ioctl_cmd.ipa_cmd_hdr.ret_code, oid_str ); break; case IPA_SNMP_NOT_SUPP: get_time( time_buf ); snmp_log( LOG_ERR, "%s do_GET_ioctl(): " "IPA SNMP subcommand failed - subcommand 0x%x not supported\n" "do_GET_ioctl(): rejected request for .%s\n", time_buf, (*cmd)->ioctl_cmd.ipa_cmd_hdr.request, oid_str ); break; case IPA_SNMP_NO_DATA: get_time( time_buf ); snmp_log( LOG_ERR, "%s do_GET_ioctl(): " "IPA SNMP subcommand failed - no data available\n" "do_GET_ioctl(): rejected request for .%s\n", time_buf, oid_str ); break; default: get_time( time_buf ); snmp_log( LOG_ERR, "%s do_GET_ioctl(): " "IPA SNMP subcommand failed - undefined return code 0x%x\n" "do_GET_ioctl(): rejected request for .%s\n", time_buf, (*cmd)->ioctl_cmd.ipa_cmd_hdr.ret_code, oid_str ); break; } /* end switch */ /* return error */ free( *cmd ); return -1; } /* end do_GET_ioctl */ /********************************************************************** * write_ibmOSAMib(): * !!! Set processing is not supported in version 1.0.0 !!! * !!! Function is defind as a skeleton for later use !!! * This function handles any SET requests raised against the * ibmOSAMib. * The flow of actions is to preserve proper transaction handling * with other transactions in the same set request. * parameters: * IN int action - current action state * IN u_char *var_val - new variable value * IN u_char var_val_type - data type of above variable * IN size_t var_val_len - length of variable value * IN u_char *statP - value that a GET request would return * for this variable * IN oid *name - OID to be set * IN size_t name_len - len of OID to be set * returns: SNMP_ERR_WRONGTYPE - wrong data type passed in * SNMP_ERR_GENERR - general error occurred * SNMP_ERR_UNDOFAILED - undo operation failed * SNMP_ERR_NOERROR - variable set successful *********************************************************************/ int write_ibmOSAMib( int action, u_char *UNUSED(var_val), u_char UNUSED(var_val_type), size_t UNUSED(var_val_len), u_char *UNUSED(statP), oid *UNUSED(name), size_t UNUSED(name_len) ) { /* static unsigned char string[SPRINT_MAX_LEN]; */ /* int size; */ switch ( action ) { case RESERVE1: /* check to see that everything is possible */ break; case RESERVE2: /* allocate needed memory here */ break; case FREE: /* Release any resources that have been allocated */ break; case ACTION: /* Actually make the change requested. Note that anything done here must be reversible in the UNDO case */ break; case UNDO: /* Back out any changes made in the ACTION case */ break; case COMMIT: /* Things are working well, so it's now safe to make the change permanently. Make sure that anything done here can't fail! */ break; } return SNMP_ERR_NOERROR; } /* end write_ibmOSAMib */ s390-tools-2.38.0/osasnmpd/ibmOSAMib.h000066400000000000000000000021301502674226300172020ustar00rootroot00000000000000/* * osasnmpd - IBM OSA-Express network card SNMP subagent * * Include file for the OSA-E subagent MIB implementaton module. * Defines function prototypes of the basic functions in the MIB * implementation module. * * Copyright IBM Corp. 2002, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef _MIBGROUP_IBMOSAMIB_H #define _MIBGROUP_IBMOSAMIB_H /* we may use header_generic and header_simple_table from the util_funcs module */ config_require(util_funcs); /* function prototypes */ void init_ibmOSAMib(void); /* register MIB data */ FindVarMethod var_ibmOSAMib; /* handle GET and GETNEXT requests */ /* for all SMIv2 standard types */ FindVarMethod var_DisplayStr; /* handle special case Display String */ WriteMethod write_ibmOSAMib; /* handle SET requests */ /* ioctl for Get/Getnext processing */ int do_GET_ioctl ( int, oid*, size_t, IPA_CMD_GET** ); #endif /* _MIBGROUP_IBMOSAMIB_H */ s390-tools-2.38.0/osasnmpd/ibmOSAMibDefs.h000066400000000000000000000172341502674226300200170ustar00rootroot00000000000000/* * osasnmpd - IBM OSA-Express network card SNMP subagent * * Defines constants and data structures used by the OSA-E subagent. * * Copyright IBM Corp. 2002, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #ifndef NETSNMP_DS_APPLICATION_ID #define NETSNMP_DS_APPLICATION_ID DS_APPLICATION_ID #endif #ifndef NETSNMP_DS_AGENT_ROLE #define NETSNMP_DS_AGENT_ROLE DS_AGENT_ROLE #endif #ifndef NETSNMP_DS_AGENT_X_SOCKET #define NETSNMP_DS_AGENT_X_SOCKET DS_AGENT_X_SOCKET #endif /* version number of this agent */ /* default log file - don't change it here, use parameter -l */ #define OSAE_LOGFILE "/var/log/osasnmpd.log" /* definitions for subagent to master agent definition */ #define NET_SNMP_PEERNAME "localhost" #define NET_SNMP_COMMUNITY "public" /* need this for OSA Express ioctl's */ #define QETH_PROCFILE "/proc/qeth" #define QETH_IOC_MAGIC 'Z' #define QETH_IOCPROC_REGISTER _IOW(QETH_IOC_MAGIC, 1, int) #define QETH_UPDATE_MIB_SIGNAL SIGUSR1 #define QETH_QUERY_IPA_DATA _IOWR(QETH_IOC_MAGIC, 7, int ) #define QETH_CHECK_OSA_DEVICE _IOWR(QETH_IOC_MAGIC, 8, int ) #define IFNAME_MAXLEN 16 /* max length for linux interface names */ #define SUFFIX_MAXLEN 13 /* max length of suffix length for net-snmp */ #define MIB_AREA_LEN 25000 /* default size for register MIB data */ #define MAX_GET_DATA 4094 /* maximum GET response data length */ #define GET_AREA_LEN MAX_GET_DATA + 512 /* size for GET command area length */ #define TIME_BUF_SIZE 128 /* buffer size for date and time string */ #define MAX_OID_STR_LEN MAX_OID_LEN * 5 /* max OID string size */ /* definitions for 2.6 qeth */ #define QETH_SYSFILE "/sys/bus/ccwgroup/drivers/qeth/notifier_register" #define SIOC_QETH_ADP_SET_SNMP_CONTROL (SIOCDEVPRIVATE + 5) #define SIOC_QETH_GET_CARD_TYPE (SIOCDEVPRIVATE + 6) /* some definitions for the linked lists compare and delete functions */ #define OID_FOUND 0 #define OID_NOT_FOUND 1 #define UNEXP_ERROR -1 #define INDEX_FOUND 0 #define INDEX_NOT_FOUND 1 #define IF_ENTRY 0 #define IND_LIST 1 /* additional access types and data types used by IPAssists */ #define IPA_WRONLY 0xF2 #define IPA_DISPLAYSTR ((u_char)0x09) /* IPAssists SNMP subcommand codes */ #define IPA_REG_MIB 0x04 #define IPA_GET_OID 0x10 #define IPA_SET_OID 0x11 /*#define IPA_QUERY_ALERT 0x20*/ /*#define IPA_SET_TRAP 0x21*/ /* IPAssists command return codes */ #define IPA_SUCCESS 0x00 #define IPA_FAILED 0x01 #define IPA_NOT_SUPP 0x04 #define IPA_NO_DATA 0x08 /* IPAssists SNMP subcommand return codes */ #define IPA_SNMP_SUCCESS 0x00 #define IPA_SNMP_INV_TOPOID 0x01 #define IPA_SNMP_INV_GROUP 0x02 #define IPA_SNMP_INV_SUFFIX 0x04 #define IPA_SNMP_INV_INST 0x08 #define IPA_SNMP_OID_NREAD 0x10 #define IPA_SNMP_OID_NWRIT 0x20 #define IPA_SNMP_NOT_SUPP 0x40 #define IPA_SNMP_NO_DATA 0x80 #define PTR_ALIGN4(addr) ((long)((addr))+3)&(~3) /* align ptr 4-byte bdy */ /***************************************************************/ /* structure used for getting OSA-Express interfaces via ioctl */ /***************************************************************/ #define NAME_FILLED_IN 0x00000001 #define IFINDEX_FILLED_IN 0x00000002 /* version 0 */ typedef struct dev_list { char device_name[IFNAME_MAXLEN]; /* OSA-Exp device name (e.g. eth0) */ int if_index; /* interface index from kernel */ __u32 flags; /* device charateristics */ } __attribute__((packed)) DEV_LIST; typedef struct osaexp_dev_ver0 { __u32 version; /* structure version */ __u32 valid_fields; /* bitmask of fields that are really filled */ __u32 qeth_version; /* qeth driver version */ __u32 number_of_devices; /* number of OSA Express devices */ struct dev_list devices[0]; /* list of OSA Express devices */ } __attribute__((packed)) OSAEXP_DEV_VER0; /***************************************************************/ /* ioctl data structure for IPAssists SNMP processing */ /***************************************************************/ typedef struct ioctl_cmd_hdr { int data_len; /* total length of buffer passed to ioctl */ /* following the first 16 bytes */ /* in this structure (i.e. starts at token) */ int req_len; /* length of IPAssists SNMP request */ int reserved1; /* unused */ int reserved2; /* unused */ struct { char token[16]; /* not used */ int request; /* IPA subcommand code */ int ifIndex; /* IF-MIB ifIndex value for interface */ int ret_code; /* IPA return code */ int ipa_ver; /* IPA microcode level (4 hex digits to be shown as xx.yy) */ int seq_num; /* sequence number (currently not used) */ } ipa_cmd_hdr; } __attribute__((packed)) IOCTL_CMD_HDR; /***************************************************************/ /* structures for GET/GETNEXT IPAssists processing */ /***************************************************************/ typedef struct ipa_cmd_get { IOCTL_CMD_HDR ioctl_cmd; /* IOCTL command header */ char full_oid[0]; /* fully qualified OID for GET/GETNEXT */ } __attribute__((packed)) IPA_CMD_GET; typedef struct ipa_get_data { int len; /* length of returned data from IPA */ char data[0]; /* data returned by IPA */ } __attribute__((packed)) IPA_GET_DATA; /******************************************************************/ /* struct for IPAssists register MIB data processing */ /******************************************************************/ typedef struct ipa_cmd_reg { IOCTL_CMD_HDR ioctl_cmd; /* IPA subcommand header */ int table_cnt; /* number of table toplevel OIDs */ } __attribute__((packed)) IPA_CMD_REG; /***************************************************************/ /* linked list for table OID housekeeping */ /***************************************************************/ typedef struct table_oid { oid *pObjid; /* registered table OID */ size_t length; /* length of subtree OID */ struct variable13 *var13ptr; /* ptr to variable_x list */ struct reg_indices *ind_list; /* ptr to registered indices */ struct table_oid *next; /* ptr to next entry in list */ } TABLE_OID; /***************************************************************/ /* linked list for OSA Express interfaces housekeeping */ /***************************************************************/ typedef struct reg_indices { char *full_index; /* full index portion from IPA */ int ifIndex; /* ifIndex from IF-MIB */ struct reg_indices *next; /* ptr to next entry in list */ } REG_INDICES; /*******************************************************************/ /* this list keeps information queried from the IF-MIB */ /*******************************************************************/ typedef struct if_List { int kerIndex; /* Linux kernel ifIndex */ int ifIndex; /* IF-MIB ifIndex */ short is_OSAEXP; /* TRUE if an OSA Express device */ char if_Name[IFNAME_MAXLEN]; /* interface name (e.g. eth0) */ int ipa_ver; /* IPA microcode level */ } IF_LIST; s390-tools-2.38.0/osasnmpd/ibmOSAMibUtil.c000066400000000000000000001644201502674226300200460ustar00rootroot00000000000000/* * osasnmpd - IBM OSA-Express network card SNMP subagent * * Collection of utility functions used by the MIB implementation * module. * * Copyright IBM Corp. 2002, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include "ibmOSAMibUtil.h" #include "ibmOSAMib.h" /* ptr to interface information on this system */ extern IF_LIST* if_list; extern int ifNumber; extern TABLE_OID* oid_list_head; /* proc file filedescriptor. opened in osasnmpd.c */ extern int proc_fd; /********************************************************************** * str_to_oid_conv(): * This function converts an OID string in an OID u_long array used * by net-snmp. * parameters: * IN char* uc_oid: OID string separated by dots * OUT oid* ul_oid: OID u_long array * returns: int count - number of OID identifiers found within OID * string; 0 if OID string was invalid *********************************************************************/ int str_to_oid_conv ( char* uc_oid, oid* ul_oid ) { short valid = TRUE; int count = 0; char* pos_strt = uc_oid; char* pos_end; /* got non-empty oid string */ if ( strlen( uc_oid ) > 0 ) { do { /* found a dot, skip it */ if ( *pos_strt == '.' ) { pos_strt++; if ( *pos_strt == '\0') /* found ending dot - valid - */ break; } /* found no dot and but expected one, indicate invalid OID */ else if (count > 0) { valid = FALSE; break; } /* end if */ /* convert oid digit into data type oid (unsigned long) */ ul_oid[count] = (oid) strtoul( pos_strt, &pos_end, 10); /* check result from conversion */ if (pos_strt == pos_end || ul_oid[count] == ULONG_MAX) { valid = FALSE; break; } /* adjust to next OID digit */ pos_strt = pos_end; count++; } while ( *pos_end != '\0' && count < MAX_OID_LEN ); } else /* indicate invalid OID */ valid = FALSE; /* if OID was valid, return number of OID identifiers */ if (!valid) return 0; else return count; } /* end str_to_oid_conv() */ /********************************************************************** * oid_to_str_conv(): * This function converts a net-snmp OID u_long array into an OID * string separated by dots. * parameters: * IN oid* ul_oid: OID u_long array * IN size_t length: OID length * OUT char* uc_oid: OID string including dots (no leading dot) * returns: TRUE - conversion was successful * FALSE - conversion failed *********************************************************************/ int oid_to_str_conv (oid* ul_oid, size_t length, char* uc_oid ) { #define MAX_CHARS 50 /* size of buffer */ int i; short valid = TRUE; char buffer[MAX_CHARS]; /* buffer used for conversion */ /* got invalid OID length */ if ( length != 0 && length <= MAX_OID_LEN ) { /* init return string */ uc_oid[0] = '\0'; for ( i=0; i < (int)length; i++) { /* convert and append OID digit to return string */ if (i == 0) sprintf( buffer, "%lu", ul_oid[i] ); else sprintf( buffer, ".%lu", ul_oid[i] ); strcat( uc_oid, buffer ); } /* end for */ } else /* indicate invalid OID */ valid = FALSE; /* if OID conversion was successful, indicate success */ return valid; } /* end oid_to_str_conv() */ /********************************************************************** * init_oid_list(): * This function initializes the head of the linked list structure to * maintain the IPAssists MIB information. It is called at subagent * startup. * * parameters: * INOUT none * returns: TABLE_OID* - head of linked list (pseudo node) * NULL - if malloc() for head failed * *********************************************************************/ TABLE_OID* init_oid_list () { TABLE_OID* head; /* allocate list head */ head = (TABLE_OID*) malloc( sizeof *head ); if ( head != NULL ) head->next = NULL; return head; } /* init_oid_list() */ /********************************************************************** * search_oid(): * This function searches the linked OID list for a given OID and * returns a ptr to the element if it is an exact match, otherwise the * previous (smaller) OID in the list is returned. * * parameters: * IN oid* s_oid - Toplevel OID to search for * IN size_t len - length of this OID * IN TABLE_OID* lhead - ptr to list head * OUT TABLE_OID** curr - ptr to entry in list * returns: 0 - exact match - OID exists in list * 1 - not found, curr point to possible insertion point * -1 - got unexpected return code from snmp_oid_compare() * *********************************************************************/ int search_oid ( oid* s_oid, size_t len, TABLE_OID* lhead, TABLE_OID** curr ) { /* loop through list and compare OIDs */ /* fyi - snmp_oid_compare() is a function from the net-snmp agent extension API */ for( *curr=lhead; (*curr)->next != NULL; *curr=(*curr)->next ) { switch ( snmp_oid_compare( s_oid, len, (*curr)->next->pObjid, (*curr)->next->length )) { case 0: /* exact OID match - curr->next points to entry */ *curr = (*curr)->next; return OID_FOUND; break; case 1: /* search OID still greater - goto next entry */ break; case -1: /* next entry is greater than search OID */ return OID_NOT_FOUND; break; default: /* unexpected return code from snmp_oid_compare */ return UNEXP_ERROR; } /* end switch */ } /* end for */ /* we're at the head or the end of linked list */ /* curr points to head or last element in list, good insertion points */ return OID_NOT_FOUND; } /* search_oid() */ /********************************************************************** * search_top_oid(): * This function searches for a fully qualified OID a matching Toplevel * OID from the linked list. * It returns a pointer to the element if it is an exact match. * Otherwise the return OID is set to NULL. * * parameters: * IN oid* s_oid - Fully qualified OID * IN size_t len - length of this OID * IN TABLE_OID* lhead - ptr to list head * OUT TABLE_OID** curr - ptr to entry in list * returns: 0 - exact match - appropriate Toplevel OID found * 1 - not found, curr set to NULL * -1 - got unexpected return code from snmp_oid_compare() * *********************************************************************/ int search_top_oid ( oid* s_oid, size_t len, TABLE_OID* lhead, TABLE_OID** curr ) { /* loop through list and compare OIDs */ /* snmp_oid_compare() is a taken from the net-snmp agent extension API */ for( *curr=lhead; (*curr)->next != NULL; *curr=(*curr)->next ) { /* fully qualified OID must be greater than our Toplevel OID */ if ( len > (*curr)->next->length ) { switch ( snmp_oid_compare( s_oid, (*curr)->next->length , (*curr)->next->pObjid, (*curr)->next->length )) { case 0: /* exact OID match - curr->next points to entry */ *curr = (*curr)->next; return OID_FOUND; break; case 1: /* search OID still greater - goto next entry */ break; case -1: /* next entry is greater than search OID */ *curr = NULL; return OID_NOT_FOUND; break; default: /* unexpected return code from snmp_oid_compare */ *curr = NULL; return UNEXP_ERROR; } /* end switch */ } /* end if */ } /* end for */ /* we're at the head or the end of linked list */ /* curr points to head or last element in list */ *curr = NULL; return OID_NOT_FOUND; } /* search_top_oid() */ /********************************************************************** * oid_insert_after(): * This function initializes and adds a new entry to the OID linked * list after entry pre_oid. * * parameters: * IN oid* i_oid - Toplevel OID to add * IN size_t len - length of this OID * IN TABLE_OID* pre_oid - add Toplevel OID after this entry * returns: TABLE_OID* - PTR to newly inserted entry * NULL - if malloc() for new entry failed *********************************************************************/ TABLE_OID* oid_insert_after ( oid* i_oid, size_t len, TABLE_OID* pre_oid ) { TABLE_OID *new_entry; new_entry = (TABLE_OID*) malloc( sizeof *new_entry ); if ( new_entry == NULL ) return NULL; /* allocate head for index list */ new_entry->ind_list = init_ind_list(); if ( new_entry->ind_list == NULL ) { free ( new_entry ); return NULL; } /* end if */ /* assign OID and length */ new_entry->pObjid = i_oid; new_entry->length = len; new_entry->var13ptr = NULL; /* insert */ new_entry->next = pre_oid->next; pre_oid->next = new_entry; return new_entry; } /* oid_insert_after()*/ /********************************************************************** * delete_oid(): * This function deletes a Toplevel OID entry from the Toplevel OID * linked list. * * parameters: * IN oid *d_oid - Toplevel OID to delete * IN size_t len - length of this OID * IN TABLE_OID *lhead - head of Toplevel OID linked list * returns: none * *********************************************************************/ int delete_oid( oid* d_oid, size_t len, TABLE_OID* lhead ) { TABLE_OID *curr, *del_entry; /* loop through list and compare OIDs */ /* snmp_oid_compare() is taken from the net-snmp agent extension API */ for( curr=lhead; curr->next != NULL; curr=curr->next ) { if ( snmp_oid_compare( d_oid, len, curr->next->pObjid, curr->next->length ) == 0 ) { del_entry = curr->next; curr->next = curr->next->next; free( del_entry->pObjid ); free( del_entry->var13ptr ); /* index list not empty, free index list first */ if ( del_entry->ind_list->next != NULL ) delete_index( del_entry->ind_list, 0, IND_LIST ); /* free index list head */ free( del_entry->ind_list ); free( del_entry ); break; } /* end if */ } /* end for */ return 0; } /* delete_oid() */ /********************************************************************** * clear_oid_list(): * This function removes all index entries from the Toplevel OID * linked list. * * parameters: * IN TABLE_OID *lhead - head of Toplevel OID linked list * returns: none * *********************************************************************/ int clear_oid_list( TABLE_OID* lhead ) { TABLE_OID *curr, *clr_entry; /* loop through list and clean entries */ for( curr=lhead; curr->next != NULL; curr=curr->next ) { clr_entry = curr->next; /* index list not empty, free index list first */ if ( clr_entry->ind_list->next != NULL ) delete_index( clr_entry->ind_list, 0, IND_LIST ); } /* end for */ return 0; } /* clear_oid_list() */ /********************************************************************** * init_ind_list(): * This function initializes the head of the linked list structure to * maintain index portion information. This is called when a new * Toplevel OID is inserted in the Toplevel OID linked list. * * parameters: * INOUT none * returns: TABLE_OID* - head of linked list (pseudo node) * NULL - if malloc() for head failed * *********************************************************************/ REG_INDICES* init_ind_list ( ) { REG_INDICES* ihead; /* allocate list head */ ihead = (REG_INDICES*) malloc( sizeof *ihead ); if ( ihead != NULL ) ihead->next = NULL; return ihead; } /* init_ind_list() */ /********************************************************************** * index_insert_after(): * This function adds a new index entry to the index linked list * * parameters: * IN char* i_index - index to add * IN int ifIndex - appropriate IF-MIB ifIndex * IN REG_INDICES* pre_ind - add index after this entry * returns: REG_INDICES* - PTR to newly inserted entry * NULL - if malloc() for new entry failed *********************************************************************/ REG_INDICES* index_insert_after ( char* i_index, int ifIndex, REG_INDICES* pre_ind ) { REG_INDICES *new_entry; new_entry = (REG_INDICES*) malloc( sizeof *new_entry ); if ( new_entry == NULL ) return NULL; /* assign index and ifIndex */ new_entry->full_index = i_index; new_entry->ifIndex = ifIndex; /* insert */ new_entry->next = pre_ind->next; pre_ind->next = new_entry; return new_entry; } /* index_insert_after()*/ /********************************************************************** * delete_index(): * This function deletes an entry in the index linked list indexed by * ifIndex or removes the whole index linked list if desired. * * parameters: * IN REG_INDICES *lhead - head of index linked list * IN int ifIndex - ifIndex to remove from list * IN int del_type - deletion type (IF_ENTRY/IND_LIST) * returns: none * *********************************************************************/ int delete_index( REG_INDICES* lhead, int ifIndex, int del_type ) { REG_INDICES *curr, *del_entry; if (del_type == IF_ENTRY ) { /* loop through list and compare ifIndex */ curr = lhead; while ( curr->next != NULL ) { if ( curr->next->ifIndex == ifIndex ) { del_entry = curr->next; curr->next = curr->next->next; free( del_entry->full_index ); free( del_entry ); } /* end if */ else curr=curr->next; } /* end while */ } else if ( del_type == IND_LIST ) { /* loop through list delete one entry after another */ curr = lhead; while ( curr->next !=NULL ) { del_entry = curr->next; curr->next = curr->next->next; free( del_entry->full_index ); free( del_entry ); } /* end while */ } /* end if */ return 0; } /* delete_index() */ /********************************************************************** * search_index(): * This function searches the linked index list for a given index and * returns a ptr to the element if it is an exact match, otherwise the * previous (smaller) index in the list is returned. * * parameters: * IN index* s_index - index to search for * IN REG_INDICES* lhead - ptr to list head * OUT REG_INDICES** curr - ptr to entry in list * returns: 0 - exact match - index exists in list * 1 - not found, curr point to possible insertion point * *********************************************************************/ int search_index ( char* s_index, REG_INDICES* lhead, REG_INDICES** curr ) { int oid_len1, oid_len2; oid ind_oid1[MAX_OID_LEN]; /* temporary net-snmp oids */ oid ind_oid2[MAX_OID_LEN]; /* loop through list and compare indices */ for( *curr=lhead; (*curr)->next != NULL; *curr=(*curr)->next ) { oid_len1 = str_to_oid_conv( (char*) s_index, ind_oid1 ); oid_len2 = str_to_oid_conv( (char*) (*curr)->next->full_index, ind_oid2 ); switch ( snmp_oid_compare( ind_oid1, oid_len1, ind_oid2, oid_len2 ) ) { case 0: /* exact index match - curr->next points to entry */ /* exact index match - curr-> points to entry */ *curr = (*curr)->next; return INDEX_FOUND; break; case 1: /* search index still greater - goto next entry */ continue; break; case -1: /* next entry is greater than search index */ /* next entry is greater than search index */ return INDEX_NOT_FOUND; break; default: /* unexpected return code from snmp_oid_compare */ return UNEXP_ERROR; } /* end switch */ } /* end for */ /* we're at the head or the end of linked list */ /* curr points to head or last element in list, good insertion points */ return INDEX_NOT_FOUND; } /* search_index() */ /********************************************************************** * register_tables() * Parses MIB information returned by IPAssists and registers OID * suffixes with net-snmp subagent driving code using the REGISTER_MIB * macro. Also puts data from the MIB information returned by IPAssists * into local data structures to maintain index information. * * This functions only maps the header of the IPAssists MIB info. * The table data is parsed through the void mib_data_ptr. * * IPAssists MIB information layout: * * header information: * INOUT int request * INOUT int interface number/ifIndex * INOUT int return code from IPAssists * INOUT int IPAssists version * INOUT int sequence number of this response * OUT int number of returned table information for that interface * * Repeated information: * OUT string Toplevel OID for a table (including dots) * OUT int number of OID suffixes for that table * * Repeated OID suffix information for a single table and every index: * OUT int object access type * OUT int object data type * OUT string OID suffix for object * OUT string OID index portion * * parameters: * IN struct table: MIB information returned by IPAssists * IN TABLE_OID* lhead: head of toplevel OID linked list * returns: 0 - SUCCESS * -1 - Registration not performed due to error; * an appropriate log entry is made * *********************************************************************/ int register_tables ( void* mib_data, TABLE_OID* lhead ) { int i, j, suf_def, suf_cnt, src_oid, src_ind; int oid_len, oid_acc, oid_typ; oid t_oid[MAX_OID_LEN]; /* temporary net-snmp oid */ oid *top_oid; char *new_index; char toid_str[MAX_OID_STR_LEN]; char time_buf[TIME_BUF_SIZE]; /* date/time buffer */ IPA_CMD_REG *mib_data_hdr; /* ptr to IPAssists hdr information */ char *last_suffix; /* last suffix in suffix list */ void *mib_data_ptr; /* ptr to parse IPAssists MIB info */ TABLE_OID *oid_ptr, *ins_oid = NULL; /* ptr into Toplevel OID linked list */ REG_INDICES *ind_ptr; /* ptr into index linked list */ /* ptr to net-snmp variable13 structs that will contain the suffix information registered with subagent driving code */ struct variable13 *table_vars, *new_area; /* point to beginning of MIB information (header) */ mib_data_hdr = ( IPA_CMD_REG* ) mib_data; /* set parse ptr behind header information; first Toplevel OID */ mib_data_ptr = ( char* ) mib_data_hdr + sizeof( IPA_CMD_REG ); /************************************/ /* loop through returned table data */ /************************************/ for ( i=0; i < mib_data_hdr->table_cnt; i++ ) { /****************************************************/ /* get Toplevel oid that we want to register under */ /****************************************************/ /* adjust parse ptr to next Toplevel OID (skip padding) */ mib_data_ptr = (int*) (PTR_ALIGN4(mib_data_ptr)); /* convert string OID to net-snmp oid type */ strncpy( toid_str, (char*) mib_data_ptr, MAX_OID_STR_LEN ); toid_str[ MAX_OID_STR_LEN-1 ] = '\0'; oid_len = str_to_oid_conv( (char*) mib_data_ptr, t_oid ); if ( oid_len == 0 ) { get_time( time_buf ); snmp_log( LOG_ERR, "%s register_tables(): str to oid conversion failed " "(Toplvl OID) .%s\nOSA Subagent MIB information may be incomplete!\n" ,time_buf, toid_str ); return -1; } /* end if */ /* save Toplevel OID in order to register it later */ top_oid = (oid*) malloc ( oid_len * sizeof(oid) ); if ( top_oid == NULL ) { get_time( time_buf ); snmp_log( LOG_ERR, "%s register_tables(): malloc() for variable top_oid failed\n" "register_tables(): Toplevel OID .%s\n" "OSA Subagent MIB information may be incomplete!\n", time_buf, toid_str ); return -1; } /* end if */ memcpy( top_oid, t_oid, oid_len * sizeof(oid) ); /* is retrieved Toplevel OID already in linked list? */ src_oid = search_oid( top_oid, oid_len, lhead, &oid_ptr ); if ( src_oid == UNEXP_ERROR ) { get_time( time_buf ); snmp_log( LOG_ERR, "%s register_tables(): Unexpected return code from" " function search_oid() for Toplevel OID .%s\n" "OSA Subagent MIB information may be incomplete!\n", time_buf, toid_str ); free( top_oid ); return -1; } /* put this Toplevel OID into the linked list, if OID was not found */ else if ( src_oid == OID_NOT_FOUND ) { ins_oid = oid_insert_after( top_oid, oid_len, oid_ptr ); if ( ins_oid == NULL ) { get_time( time_buf ); snmp_log( LOG_ERR, "%s register_tables(): malloc() for new entry in " "OID list failed for Toplevel OID .%s\nOSA Subagent " "MIB information may be incomplete!\n", time_buf, toid_str ); free( top_oid ); return -1; } /* end if */ } /* end if */ /* if OID was found, set insert OID to exact match OID */ else /* src_oid == OID_FOUND */ ins_oid = oid_ptr; /****************************************************************/ /* add suffixes to variable_x structure for macro REGISTER_MIB */ /****************************************************************/ last_suffix = NULL; /* initialize compare ptr */ table_vars = NULL; /* initialize variable_x ptr */ suf_def = 0; /* number of suffixes */ /* point to suffix entries */ mib_data_ptr += (strlen(mib_data_ptr)+1); mib_data_ptr = (int*) (PTR_ALIGN4(mib_data_ptr)); /* get number of attached suffix OIDs */ suf_cnt = *((int*) mib_data_ptr); /* loop through suffix list */ for ( j=0;j < suf_cnt; j++ ) { /* adjust parse ptr to access type (align pointer if it follows a string) */ if ( j == 0 ) mib_data_ptr += sizeof( int ); else { mib_data_ptr += (strlen(mib_data_ptr)+1); mib_data_ptr = (int*) (PTR_ALIGN4(mib_data_ptr)); } /* end if */ /* save object access type for registering later */ oid_acc = *((int*) mib_data_ptr); /* adjust ptr and save object data type for registering later */ mib_data_ptr += sizeof( int ); oid_typ = *((int*) mib_data_ptr); /* adjust ptr to OID suffix string */ mib_data_ptr += sizeof( int ); /* need to register suffixes only, if I have a new Toplevel OID */ if ( src_oid == OID_NOT_FOUND ) { /* init last_suffix here, if loop entered the first time */ if ( j==0 ) last_suffix = (char*) mib_data_ptr; /* check wether last and current OID suffixes match */ /* yes-don't need to register suffix again, goto register index portion */ /* no -add suffix to variable_x list */ if ( (strcmp( last_suffix, (char*) mib_data_ptr ) !=0) || j==0 ) { suf_def++; /* increase count of suffix definitions */ last_suffix = (char*) mib_data_ptr; /* save suffix */ /* allocate one entry within variable_x struct */ new_area = (struct variable13*) realloc( table_vars, suf_def * sizeof( struct variable13)); if ( new_area == NULL ) { get_time( time_buf ); snmp_log( LOG_ERR, "%s register_tables(): " "realloc() for variable_x structure failed\n" "register_tables(): for Toplevel OID .%s\n" "OSA Subagent MIB information may be incomplete!\n", time_buf, toid_str ); /* remove all memory for this Toplevel OID allocated so far */ free(table_vars); delete_oid( ins_oid->pObjid, ins_oid->length, lhead ); return -1; } /* end if */ else /* reassign table_vars */ table_vars = new_area; /* convert suffix OID string to net-snmp oid type */ oid_len = str_to_oid_conv( (char*) mib_data_ptr, t_oid ); if ( oid_len == 0 ) { get_time( time_buf ); snmp_log( LOG_ERR, "%s register_tables(): " "suffix str to oid conversion failed " "for Toplevel OID .%s\nOSA Subagent MIB information may " "be incomplete!\n", time_buf, toid_str ); /* remove all memory for this Toplevel OID allocated so far */ free(table_vars); delete_oid( ins_oid->pObjid, ins_oid->length, lhead ); return -1; } /* end if */ /*******************************/ /* set up variable_x structure */ /*******************************/ table_vars[suf_def-1].magic = 0; /* magic is not used */ if ( oid_typ == IPA_DISPLAYSTR ) /* set object type */ table_vars[suf_def-1].type = ASN_OCTET_STR; else table_vars[suf_def-1].type = oid_typ; if ( oid_acc == IPA_WRONLY ) /* have no WRITE ONLY */ table_vars[suf_def-1].acl = RWRITE; /* set to type RWRITE */ else table_vars[suf_def-1].acl = oid_acc; /* set given object type */ if ( oid_typ == IPA_DISPLAYSTR ) /* set callback functions */ table_vars[suf_def-1].findVar = var_DisplayStr; else table_vars[suf_def-1].findVar = var_ibmOSAMib; table_vars[suf_def-1].namelen = oid_len; /* OID suffix len */ if ( oid_len <= SUFFIX_MAXLEN ) /* set OID suffix */ memcpy( table_vars[suf_def-1].name, t_oid, oid_len * sizeof( oid )); else { get_time( time_buf ); snmp_log( LOG_ERR, "%s register_tables(): OID suffix length exceeded " "for Toplevel OID .%s\nSuffix OID length %d\n" "OSA Subagent MIB information may be incomplete!\n", time_buf, toid_str, oid_len ); /* remove all memory for this Toplevel OID allocated so far */ free(table_vars); delete_oid( ins_oid->pObjid, ins_oid->length, lhead ); return -1; } /* end if */ } /* end if */ } /* end if register_suffix part */ /****************************************************************/ /* add index to index linked list under this Toplevel OID entry */ /****************************************************************/ /* adjust ptr to index portion */ mib_data_ptr = (char*) (mib_data_ptr + (strlen(mib_data_ptr)+1)); /* search index list for this index */ src_ind = search_index( (char*) mib_data_ptr, ins_oid->ind_list, &ind_ptr ); /* if index is not found in the linked list, add index */ if ( src_ind == INDEX_NOT_FOUND ) { new_index = (char*) malloc( strlen( (char*) mib_data_ptr ) + 1 ); if ( new_index == NULL ) { get_time( time_buf ); snmp_log( LOG_ERR, "%s register_tables(): " "malloc() for new index entry failed\n" "register_tables(): for Toplevel OID .%s\n" "OSA Subagent MIB information may be incomplete!\n", time_buf, toid_str); if ( src_oid == OID_NOT_FOUND ) { free(table_vars); delete_oid( ins_oid->pObjid, ins_oid->length, lhead ); } return -1; } /* end if */ strcpy( new_index, (char*) mib_data_ptr ); index_insert_after( new_index, mib_data_hdr->ioctl_cmd.ipa_cmd_hdr.ifIndex, ind_ptr ); } /* end if */ } /* end for (j) */ /* new Toplevel OID?: yes, then register with agent to add this table */ /* note - register_mib() is taken from the net-snmp agent extension API */ if ( src_oid == OID_NOT_FOUND ) { if ( register_mib( "ibmOSAMib",(struct variable*) table_vars, sizeof(struct variable13), suf_def, top_oid, ins_oid->length ) != MIB_REGISTERED_OK ) { get_time( time_buf ); snmp_log( LOG_ERR, "%s register_tables(): " "API function register_mib() failed\n" "register_tables(): for Toplevel OID .%s\n" "OSA Subagent MIB information may be incomplete!\n", time_buf, toid_str ); /* remove all memory for this Toplevel OID allocated so far */ free(table_vars); delete_oid( ins_oid->pObjid, ins_oid->length, lhead ); return -1; } /* end if */ else { get_time( time_buf ); snmp_log( LOG_INFO, "%s registered Toplevel OID .%s\n", time_buf, toid_str ); } /* end if */ } /* end if */ /* save variable_x ptr to free allocated memory later, if needed */ ins_oid->var13ptr = table_vars; /* adjust ptr to next Toplevel OID */ mib_data_ptr = (char*) (mib_data_ptr + (strlen(mib_data_ptr)+1)); } /* end for (i) */ return 0; } /* end register_tables() */ /************************************************************************ * header_osa_table() * Compare 'name' to vp->name for the best match or an exact match. * Store result OID in 'name', including the index that matches best. * Return matching net-snmp ifIndex to caller. * * parameters: * IN struct variable *vp - ptr to registered subagent MIB data * INOUT oid *name - fully instantiated OID name * INOUT size_t *length - length of this OID * IN int exact - TRUE if an GET match is desired * OUT size_t *var_len - hook for size of returned data type * IN WriteMethod **write_method - hook for write method (UNUSED) * IN TABLE_OID *lhead - ptr to Toplevel OID list head * * returns: * ifIndex - ifIndex in this OID * -1 (MATCH_FAILED) - no suitable match found * ************************************************************************/ int header_osa_table( struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method, TABLE_OID *lhead ) { int interface = -1; int i, res, conv; size_t index_len; /* length of index portion */ oid newname[MAX_OID_LEN]; /* temporary return OID */ oid buffer[MAX_OID_LEN]; /* temporary converion buffer */ char name_index[MAX_OID_STR_LEN]; char time_buf[TIME_BUF_SIZE]; /* date/time buffer */ /* ptr into OID and index linked lists */ TABLE_OID *ptr_oid; REG_INDICES *ptr_ind; int found_OID = FALSE; /* some debugging calls */ DEBUGMSGTL(("ibmOSAMib-Subagent", "header_osa_table: ")); DEBUGMSGOID(("ibmOSAMib-Subagent", name, *length)); DEBUGMSG(("ibmOSAMib-Subagent"," exact=%d\n", exact)); /* OID compare */ /* set 'res' to -1 name < vp->name */ /* 1 name > vp->name */ /* 0 exact match */ for ( i=0, res=0; (i < (int) vp->namelen) && (i < (int) (*length)) && !res; i++ ) { if ( name[i] != vp->name[i] ) { if ( name[i] < vp->name[i] ) res = -1; else res = 1; } /* end if */ } /* end for */ DEBUGMSG(("ibmOSAMib-Subagent", " snmp_oid_compare: %d\n", res)); /* (GETNEXT AND search OID still greater) OR (GET AND not-exact OIDs) */ /* yes - indicate match failed */ if ( (!exact && (res > 0)) || (exact && (res != 0)) ) { if (var_len) *var_len = 0; return MATCH_FAILED; } /* end if */ /* init temporary return OID */ memset(newname, 0, sizeof(newname)); /******************************************/ /* handle GET requests with matching OIDs */ /******************************************/ if ( exact && res == 0 ) { /* got a too short OID */ if ( (*length) <= vp->namelen ) { if (var_len) *var_len = 0; return MATCH_FAILED; } else /* determine length of attached index */ index_len = (int) (*length) - (int) vp->namelen; DEBUGMSG(("ibmOSAMib-Subagent", " header_osa_table - GET index length: %zd\n", index_len)); /* search our internal linked list for a matching Toplevel OID */ res = search_top_oid ( (oid*) vp->name, (size_t) vp->namelen, lhead, &ptr_oid ); /* found a matching Toplevel OID, now look for index portion */ if ( res == OID_FOUND ) { DEBUGMSGOID(("ibmOSAMib-Subagent:header_osa_table - Toplevel OID found", ptr_oid->pObjid, ptr_oid->length)); /* convert index portion to string */ conv = oid_to_str_conv ( (oid*) &name[vp->namelen], index_len, name_index ); DEBUGMSG(("ibmOSAMib-Subagent"," index portion=%s\n", name_index)); if (conv == TRUE) { res = search_index ( name_index, (REG_INDICES*) ptr_oid->ind_list, &ptr_ind ); /* found a matching index, OID for GET request exists! */ if ( res == INDEX_FOUND ) { DEBUGMSG(("ibmOSAMib-Subagent"," index found in linked list\n")); /* return appropriate ifIndex responsible for that OID */ interface = ptr_ind->ifIndex; /* set up return OID */ memmove( newname, name, (*length) * sizeof(oid) ); found_OID = TRUE; } /* end if */ } /* end if */ } /* end if */ } /* end if */ /***************************/ /* handle GETNEXT requests */ /***************************/ else if ( !exact && res <= 0 ) { /* OID too short or switched to next suffix */ /* adjust length and attach first index */ if ( (int) (*length) <= (int) vp->namelen || res < 0 ) { DEBUGMSG(("ibmOSAMib-Subagent", " GETNEXT - length <= vp->namelen OR snmp_oid_compare < 0")); /* search our internal linked list for a matching Toplevel OID */ res = search_top_oid ( (oid*) vp->name, vp->namelen, lhead, &ptr_oid ); /* found a matching Toplevel OID, now look for index portion */ if ( res == OID_FOUND ) { DEBUGMSGOID(("ibmOSAMib-Subagent:header_osa_table - Toplevel OID found", ptr_oid->pObjid, ptr_oid->length)); /* set up return OID */ memmove( newname, vp->name, (int) vp->namelen * sizeof(oid) ); *length = vp->namelen; /* retrieve first index under this Toplevel OID and attach to newname */ if ( ptr_oid->ind_list->next != NULL ) { index_len = str_to_oid_conv ( ptr_oid->ind_list->next->full_index, buffer ); if ( index_len != 0 && ( (vp->namelen + index_len) <= MAX_OID_LEN ) ) { memmove ( &newname[vp->namelen], buffer, index_len * sizeof(oid) ); *length = *length + index_len; DEBUGMSG(("ibmOSAMib-Subagent"," index portion to attach=%s\n" ,ptr_oid->ind_list->next->full_index )); /* return appropriate ifIndex responsible for that OID */ interface = ptr_oid->ind_list->next->ifIndex; found_OID = TRUE; } else { get_time( time_buf ); snmp_log( LOG_ERR, "%s header_osa_table(): " "(GETNEXT-1) index list corrupted\n" "OSA Subagent MIB information may be incomplete!\n", time_buf ); } /* end if */ } /* end if */ } /* end if */ } /* 'true' GETNEXT case */ else { DEBUGMSG(("ibmOSAMib-Subagent", " GETNEXT - length > vp->namelen")); /* search our internal linked list for a matching Toplevel OID */ res = search_top_oid ( (oid*) name, (int) (*length), lhead, &ptr_oid ); /* found a matching Toplevel OID, now look for index portion */ if ( res == OID_FOUND ) { DEBUGMSGOID(("ibmOSAMib-Subagent: " "header_osa_table - Toplevel OID found", ptr_oid->pObjid, ptr_oid->length)); /* search the index attached to 'name' in the index list */ /* determine length of attached index and convert to a string */ index_len = (int) (*length) - (int) vp->namelen; conv = oid_to_str_conv ( (oid*) &name[vp->namelen], index_len, name_index ); if (conv == TRUE) { res = search_index ( name_index, (REG_INDICES*) ptr_oid->ind_list, &ptr_ind ); /* next index is the one we're looking for */ /* if next index is NULL; return MATCH FAILED(goto next suffix) */ if( ptr_ind->next != NULL ) { index_len = str_to_oid_conv( ptr_ind->next->full_index, buffer); if ( index_len != 0 && ( (vp->namelen + index_len) <= MAX_OID_LEN ) ) { DEBUGMSG(("ibmOSAMib-Subagent"," index portion to attach=%s\n" ,ptr_ind->next->full_index )); /* set up return OID */ *length = vp->namelen; memmove( newname, name, vp->namelen * sizeof(oid) ); memmove( &newname[vp->namelen], buffer, index_len * sizeof(oid) ); *length = *length + index_len; /* return appropriate ifIndex responsible for that OID */ interface = ptr_ind->next->ifIndex; found_OID = TRUE; } /* end if */ else { get_time( time_buf ); snmp_log( LOG_ERR, "%s header_osa_table(): " "(GETNEXT-2) index list corrupted\n" "OSA Subagent MIB information may be incomplete!\n", time_buf ); } /* end if */ } /* end if */ } /* end */ } else { get_time( time_buf ); snmp_log( LOG_ERR, "%s header_osa_table(): " "(GETNEXT-2) Toplevel OID not found\n" "OSA Subagent MIB information may be incomplete!\n", time_buf ); } /* end if */ } /* end if - handle GETNEXT requests */ } /* return MATCH_FAILED, if no appropriate OID was found */ if ( !found_OID ) { if (var_len) *var_len = 0; return MATCH_FAILED; } else { /* finalize OID to be returned */ memmove(name, newname, (*length) * sizeof(oid)); if (write_method) *write_method = 0; if (var_len) *var_len = sizeof(long); /* default data type long */ return interface; } /* end if */ } /* end header_osa_table */ /************************************************************************ * update_mib_info() * Whenever a network interface appears or disappears the MIB and/or * interface information has to be updated, this fuction is called. * * parameters: * none * returns: * none * ************************************************************************/ void update_mib_info () { TABLE_OID *mib_info = oid_list_head; char time_buf[TIME_BUF_SIZE]; /* date/time buffer */ int i, retc, sd, /* socket descriptors */ error_code, /* used to save errno */ if_num, /* number of network interfaces */ osaexp_num; /* number of OSA-E devices */ struct ifreq ifr; /* request structure for ioctl */ IPA_CMD_REG* ipa_reg_mib; /* structure for IPA REGISTER MIB command header */ IF_LIST* tmp_list; /* temporary interfaces list */ char* buffer; /* a data buffer */ /* Retrieve ifNumber/ifIndex/ifDescr newly from IF-MIB for all interfaces */ /* retrieve data in temporary list first */ if_num = query_IF_MIB( &tmp_list ); if ( if_num < 0 ) { get_time( time_buf ); snmp_log( LOG_ERR, "%s update_mib_info(): " "could not get interface info from IF-MIB\n" "update_mib_info(): check previous messages for more details\n" "update_mib_info(): keeping original interface lists\n", time_buf ); return; } /* query OSA-E device driver for OSA-E devices * and mark them in IF-MIB interface list * */ osaexp_num = query_OSA_EXP( &tmp_list, if_num ); if ( osaexp_num == 0 ) { get_time( time_buf ); snmp_log( LOG_ERR, "%s update_mib_info(): " "no or bad OSA Express devices reported" " - continue processing\n" "update_mib_info(): " "if available, see previous message for reason\n" "update_mib_info(): freeing interface lists\n", time_buf ); free( tmp_list ); /* remove all index entries from Toplevel OID * linked list and store ifNumber * */ clear_oid_list( mib_info ); ifNumber = if_num; return; } /* end if */ /* allocate area, that should contain retrieved MIB * data for a single interface * */ buffer = (char*) malloc( MIB_AREA_LEN ); if ( buffer == NULL ) { get_time( time_buf ); snmp_log( LOG_ERR, "%s update_mib_info(): " "malloc() for REGISTER MIB data buffer " "failed\nupdate_mib_info(): requested %d bytes\n" "update_mib_info(): keeping original interface lists\n", time_buf, MIB_AREA_LEN ); free( tmp_list ); return; } /* end if */ /* socket for query MIB information ioctl */ sd = socket( AF_INET, SOCK_STREAM, 0 ); if ( sd < 0 ) { error_code = errno; get_time( time_buf ); snmp_log(LOG_ERR, "%s update_mib_info(): " "error opening socket() - reason %s\n" "update_mib_info(): cannot update OSA-E MIB information\n" "update_mib_info(): keeping original interface lists\n", time_buf, strerror( error_code ) ); free( tmp_list ); free( buffer ); return; } /* end if */ /* free original interface list and assign the new one (global data) */ if ( if_list != NULL ) free( if_list ); ifNumber = if_num; if_list = tmp_list; /* free entire MIB lists that we maintain so far */ clear_oid_list( mib_info ); /* walk through interface list and query MIB data * for all OSA-E devices register MIB data with * subagent driving code afterwards * */ for ( i=0; i < ifNumber; i++ ) { if ( if_list[i].is_OSAEXP == TRUE ) { /* clear buffer */ memset( buffer, 0, MIB_AREA_LEN ); /* setup ioctl buffer with request and input parameters */ /* map command structure */ ipa_reg_mib = (IPA_CMD_REG*) buffer; /* size of IPA data area */ ipa_reg_mib->ioctl_cmd.data_len = MIB_AREA_LEN - offsetof( IOCTL_CMD_HDR, ipa_cmd_hdr ); /* length of IPA subcommand */ ipa_reg_mib->ioctl_cmd.req_len = sizeof( ipa_reg_mib->ioctl_cmd.ipa_cmd_hdr ); /* set input parameters for IPA Register MIB command */ ipa_reg_mib->ioctl_cmd.ipa_cmd_hdr.request = IPA_REG_MIB; ipa_reg_mib->ioctl_cmd.ipa_cmd_hdr.ifIndex = if_list[i].ifIndex; ipa_reg_mib->ioctl_cmd.ipa_cmd_hdr.ret_code = 0; ipa_reg_mib->ioctl_cmd.ipa_cmd_hdr.seq_num = 0; /* do ioctl() */ strcpy( ifr.ifr_name, if_list[i].if_Name ); /* add interface name */ ifr.ifr_ifru.ifru_data = (char*) buffer; /* add data buffer */ if ( ioctl( sd,SIOC_QETH_ADP_SET_SNMP_CONTROL, &ifr ) < 0 ) { error_code = errno; get_time( time_buf ); /* see if we got a common I/O error */ if ( error_code == -EIO ) { snmp_log( LOG_ERR, "%s update_mib_info(): " "ioctl() failed - reason %s for interface %s\n" "update_mib_info(): " "MIB information may be incomplete\n", time_buf, strerror( error_code ), if_list[i].if_Name ); continue; break; } /* end if */ /* let's see, if we got a return code from IPAssists */ /* or if MIB buffer is exhausted */ switch ( ipa_reg_mib->ioctl_cmd.ipa_cmd_hdr.ret_code ) { case IPA_FAILED: snmp_log( LOG_ERR, "%s update_mib_info(): " "ioctl() failed - IPA command failed\n" "update_mib_info(): for interface %s\n" "update_mib_info(): MIB information may be incomplete\n", time_buf, if_list[i].if_Name ); continue; break; case IPA_NOT_SUPP: snmp_log( LOG_ERR, "%s update_mib_info(): " "ioctl() failed - IPA command not supported\n" "update_mib_info(): for interface %s\n" "update_mib_info(): MIB information may be incomplete\n", time_buf, if_list[i].if_Name ); continue; break; case IPA_NO_DATA: snmp_log( LOG_ERR, "%s update_mib_info(): " "ioctl() failed - valid IPA command, but no" "SNMP data is available for interface %s\n" "update_mib_info(): MIB information may be incomplete\n", time_buf, if_list[i].if_Name ); continue; break; case -ENOMEM: /* should not happen in the near future ;-) */ snmp_log( LOG_ERR, "%s update_mib_info(): " "ioctl() failed - MIB data size > " "constant MIB_AREA_LEN for interface %s\n" "update_mib_info(): " "Enlarge constant for MIB_AREA_LEN " "within ibmOSAMibDefs.h and " "recompile the subagent\n" "update_mib_info(): MIB information may be incomplete\n", time_buf, if_list[i].if_Name ); continue; break; default: snmp_log( LOG_ERR, "%s update_mib_info(): " "ioctl() failed - reason %s " "for interface %s\n" "update_mib_info(): MIB information may be incomplete\n", time_buf, strerror( error_code ), if_list[i].if_Name ); continue; break; } /* end switch */ } else if ( ipa_reg_mib->ioctl_cmd.ipa_cmd_hdr.ret_code != 0 ) { get_time( time_buf ); /* now check IPA SNMP subcommand return code */ switch ( ipa_reg_mib->ioctl_cmd.ipa_cmd_hdr.ret_code ) { case IPA_SNMP_INV_TOPOID: case IPA_SNMP_INV_GROUP: case IPA_SNMP_INV_SUFFIX: case IPA_SNMP_INV_INST: case IPA_SNMP_OID_NREAD: case IPA_SNMP_OID_NWRIT: snmp_log( LOG_ERR, "%s update_mib_info(): " "IPA SNMP subcommand failed - " "return code 0x%x for interface %s\n" "update_mib_info(): MIB information may be incomplete\n", time_buf, ipa_reg_mib->ioctl_cmd.ipa_cmd_hdr.ret_code, if_list[i].if_Name ); continue; break; case IPA_SNMP_NOT_SUPP: snmp_log( LOG_ERR, "%s update_mib_info(): " "IPA SNMP subcommand failed - " "subcommand 0x%x for interface %s\n" "update_mib_info(): MIB information may be incomplete\n", time_buf, ipa_reg_mib->ioctl_cmd.ipa_cmd_hdr.request, if_list[i].if_Name ); continue; break; case IPA_SNMP_NO_DATA: snmp_log( LOG_ERR, "%s update_mib_info(): " "IPA SNMP subcommand failed - " "no data available for interface %s\n" "update_mib_info(): MIB information may be incomplete\n", time_buf, if_list[i].if_Name ); continue; break; default: snmp_log( LOG_ERR, "%s update_mib_info(): " "IPA SNMP subcommand failed - " "undefined return code 0x%x for interface %s\n" "update_mib_info(): MIB information may be incomplete\n", time_buf, ipa_reg_mib->ioctl_cmd.ipa_cmd_hdr.ret_code, if_list[i].if_Name ); continue; break; } /* end switch */ } /* end if */ /* register MIB tables information, that we got from IPAssists */ retc = register_tables ( buffer, mib_info ); if ( retc != 0 ) { get_time( time_buf ); snmp_log( LOG_ERR, "%s update_mib_info(): " "register MIB data with subagent " "driving code failed\nupdate_mib_info(): for interface %s\n" "update_mib_info(): check previous messages for " "more details\nupdate_mib_info(): MIB information may be " "incomplete\n", time_buf, if_list[i].if_Name ); continue; } /* end if */ } /* end if */ } /* end for */ get_time( time_buf ); snmp_log( LOG_INFO, "%s *** OSA-E interface change indicated ***\n" "%s *** Reinitialized MIB information ***\n", time_buf, time_buf ); close( sd ); free( buffer ); } /* end update_mib_info */ /********************************************************************** * query_IF_MIB(): * Retrieve ifIndex values for all OSA devices residing on this * system. Use SNMP to query the master agent for ifNumber and * ifIndex/ifDescr values from the standard IF-MIB. * Then figure out which of these devices are OSA Express devices. * parameters: * INOUT IF_LIST** ifList: List of network interfaces on this system * returns: int if_Number - number of network interfaces on this system * system (>=0) * -1 -an error occurred , no valid info avail *********************************************************************/ int query_IF_MIB ( IF_LIST** ifList ) { char time_buf[TIME_BUF_SIZE]; /* date/time buffer */ /* data structures for SNMP API */ struct snmp_session session, *ss; struct snmp_pdu *pdu; struct snmp_pdu *response; struct variable_list *vars; void *sessp; int status, have_info, valid; /* define static OIDs for if_Number, ifIndex and ifDescr from IF-MIB */ size_t ifNumber_OIDlen = 9, ifIndex_OIDlen = 11, ifDescr_OIDlen = 11; oid ifNumber_OID[9] = { 1,3,6,1,2,1,2,1,0 }, ifIndex_OID[11] = { 1,3,6,1,2,1,2,2,1,1,0 }, ifDescr_OID[11] = { 1,3,6,1,2,1,2,2,1,2,0 }; int i, if_Number, count; /* Initialize the SNMP library */ DEBUGMSGTL(("query_IF_MIB"," about to query IF-MIB information using SNMP\n")); /* Initialize a "session" for localhost */ have_info = TRUE; valid = TRUE; if_Number = 0; snmp_sess_init( &session ); session.peername = NET_SNMP_PEERNAME; session.version = SNMP_VERSION_2c; /* use SNMPv2c */ /* SNMPv2c community name */ session.community = (unsigned char*)NET_SNMP_COMMUNITY; session.community_len = strlen((const char*)session.community); session.timeout = 300 * 1000000L; /* Open the session */ sessp = snmp_sess_open( &session ); if ( sessp == NULL ) { get_time( time_buf ); snmp_log(LOG_ERR, "%s query_IF_MIB(): snmp_sess_open() failed\n", time_buf ); return -1; } /* end if */ ss = snmp_sess_session( sessp ); /* Create and send synchronous GET PDU for if_Number */ pdu = snmp_pdu_create(SNMP_MSG_GET); snmp_add_null_var( pdu, ifNumber_OID, ifNumber_OIDlen ); status = snmp_sess_synch_response( sessp, pdu, &response); /* Process the response and get ifIndex/ifDescr table entries */ if (status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR) { /* get value for if_Number */ if ( response->variables->val.integer == NULL ) { get_time( time_buf ); snmp_log(LOG_ERR, "%s query_IF_MIB(): Get(if_Number) Error in packet\n" "Reason: response->variables->val.integer == NULL\n", time_buf); snmp_free_pdu(response); return -1; } /* end if */ count = (int) *(response->variables->val.integer); snmp_free_pdu(response); if ( count == 0 ) { snmp_sess_close( sessp ); return 0; } /* end if */ /* allocate memory for interface list */ *ifList = (IF_LIST*) malloc((sizeof(IF_LIST) * count)); if (*ifList == NULL) { get_time( time_buf ); snmp_log(LOG_ERR, "%s query_IF_MIB(): malloc() for ifList structure failed!\n", time_buf); snmp_sess_close( sessp ); return -1; } /* end if */ /* if have interfaces, issue GETNEXT PDUs for ifIndex/ifDescr table entries */ for ( i=0; i < count; i++ ) { pdu = snmp_pdu_create( SNMP_MSG_GETNEXT ); snmp_add_null_var( pdu, ifIndex_OID, ifIndex_OIDlen ); snmp_add_null_var( pdu, ifDescr_OID, ifDescr_OIDlen ); status = snmp_sess_synch_response( sessp, pdu, &response ); if (status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR) { /* set device to default value false; * device type is determined later * */ (*ifList)[i].is_OSAEXP = FALSE; (*ifList)[i].kerIndex = 0; (*ifList)[i].ifIndex = 0; /* get values for ifIndex/ifDescr from response PDU * FIXME: (workaround) compare ifDescr values * and omit double entries * */ for(vars = response->variables; vars; vars = vars->next_variable) { if( vars->type == ASN_INTEGER ) (*ifList)[if_Number].ifIndex = (int) *(vars->val.integer); else if( vars->type == ASN_OCTET_STR && vars->val_len <= IFNAME_MAXLEN ) { if ( if_Number == 0 ) { strncpy((char*)((*ifList)[0]).if_Name, (const char*)vars->val.string, vars->val_len); (*ifList)[0].if_Name[vars->val_len] = '\0'; } else { if (strncmp((const char*)(*ifList)[if_Number-1].if_Name, (const char*)vars->val.string, vars->val_len) !=0) { strncpy((char*)(*ifList)[if_Number].if_Name, (const char*)vars->val.string, vars->val_len); (*ifList)[if_Number].if_Name[vars->val_len] = '\0'; valid = TRUE; } else valid = FALSE; } /* end if */ } else { get_time( time_buf ); snmp_log(LOG_ERR, "%s query_IF_MIB(): GetNext(ifIndex;ifDescr)" " response PDU has invalid data\n", time_buf ); free( *ifList ); snmp_free_pdu(response); snmp_sess_close( sessp ); return -1; } /* end if */ } /* end for */ if ( valid == TRUE ) if_Number++; } else { /* FAILURE GETNEXT */ if (status == STAT_SUCCESS) { get_time( time_buf ); snmp_log(LOG_ERR, "%s query_IF_MIB(): GetNext(ifIndex,ifDescr) " "Error in packet\n" "Reason: %s\n",time_buf, snmp_errstring(response->errstat)); } else snmp_sess_perror("query_IF_MIB(): GetNext(ifIndex;ifDescr)", ss); if (response) snmp_free_pdu(response); free( *ifList ); have_info = FALSE; break; } /* end if */ /* store current ifIndex in OIDs for next GetNext request */ ifIndex_OID[ifIndex_OIDlen-1] = (*ifList)[i].ifIndex; ifDescr_OID[ifDescr_OIDlen-1] = (*ifList)[i].ifIndex; snmp_free_pdu( response ); } /* end for */ } else { /* FAILURE GET if_Number */ if (status == STAT_SUCCESS) { get_time( time_buf ); snmp_log(LOG_ERR, "%s query_IF_MIB(): Get(if_Number) Error in packet\n" "Reason: %s\n", time_buf, snmp_errstring(response->errstat)); } else snmp_sess_perror("query_IF_MIB(): Get(if_Number)", ss); if (response) snmp_free_pdu(response); have_info = FALSE; } /* end if */ /* Cleanup */ snmp_sess_close( sessp ); if ( have_info ) return ( if_Number ); else return -1; } /* end query_IF_MIB() */ /********************************************************************** * query_OSA_EXP(): * Retrieve device characteristics for OSA Express devices and mark * OSA Express devices in the interface list from IF-MIB. * parameters: * INOUT IF_LIST** ifList: List of network interfaces on this system * IN int if_Number: number of network interfaces * returns: int num - number of OSA Express devices found on this * system (>=0) *********************************************************************/ unsigned int query_OSA_EXP ( IF_LIST** ifList, int if_Number ) { int j, num = 0; char time_buf[TIME_BUF_SIZE]; /* date/time buffer */ int sd; struct ifreq ifr; /*open socket to get information if device is an OSA device*/ if ( (sd = socket(AF_INET,SOCK_STREAM,0)) < 0 ) { snmp_log( LOG_ERR,"%s query_OSA_EXP(): " "socket failed\n" "query_OSA_EXP(): cancel init or update " "of MIB information\n",time_buf ); return 0; } /* walk through IF-MIB interface list and mark OSA Express interfaces */ for ( j=0; j < if_Number; j++ ) { int ret=0; memset(&ifr,0,sizeof(ifr)); strncpy(ifr.ifr_name, (*ifList)[j].if_Name,IFNAME_MAXLEN); ret = ioctl(sd, SIOC_QETH_GET_CARD_TYPE, &ifr); if (ret>0) { (*ifList)[j].is_OSAEXP = TRUE; (*ifList)[j].kerIndex = (*ifList)[j].ifIndex; num++; } } /* end for */ close(sd); return num; } /* end query_OSA_EXP() */ /********************************************************************** * get_time(): * gets current time of day string and returns it as * MONTH DAY HH:MM:SS * returns: string with date and time *********************************************************************/ int get_time( char* buffer ) { time_t curtime; struct tm *loctime; curtime = time(NULL); loctime = localtime( &curtime ); strftime( buffer, TIME_BUF_SIZE, "%b %d %T", loctime ); return 0; } /* end get_time () */ static const char* usage_text[] = { "Usage: osasnmpd [-h] [-v] [-l LOGFILE] [-A] [-f] [-L] [-P PIDFILE]", " [-x SOCKADDR]", "", "-h, --help This usage message", "-v, --version Version information", "-l, --logfile LOGFILE Print warnings/messages to LOGFILE", "-A, --append Append to the logfile rather than truncating it", "-L, --stderrlog Print warnings/messages to stdout/err", "-f, --nofork Do not fork() from the calling shell", "-P, --pidfile PIDFILE Save the process ID of the subagent in PIDFILE", "-x, --sockaddr SOCKADDR Bind AgentX port to this address", "" }; /********************************************************************** * usage(): * prints help message with descriptions of available parameters * IN name of subagent * returns: none *********************************************************************/ void usage() { unsigned int i; for (i=0; i < sizeof(usage_text) / sizeof(usage_text[0]); i++) printf("%s\n", usage_text[i]); exit(0); } /* end usage() */ s390-tools-2.38.0/osasnmpd/ibmOSAMibUtil.h000066400000000000000000000057511502674226300200540ustar00rootroot00000000000000/* * osasnmpd - IBM OSA-Express network card SNMP subagent * * Prototypes for the utility functions within ibmOSAMibUtil.c * It should be included in every module that belonging to the * subagent. * * Copyright IBM Corp. 2002, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* included because of "parse error before `get_myaddr'" */ /* in version 4.2.1 this headerfile was included from */ /* ucd-snmp-includes.h */ /* include local defintions of data structures for OSA Express subagent */ #include "ibmOSAMibDefs.h" /************************ * function prototypes * ************************/ /* convert OID string returned by IPAssists to net-snmp oid type */ int str_to_oid_conv ( char*, oid* ); /* convert net-snmp oid type to OID string used by IPAssists */ int oid_to_str_conv ( oid*, size_t, char* ); /* convert IPA access type to net-snmp access type */ int get_acc_type ( int ); /* create pseudo node for toplevel OID linked list */ TABLE_OID* init_oid_list(); /* create pseudo node for index linked list */ REG_INDICES* init_ind_list(); /* inserts an OID after a given entry into the linked list */ TABLE_OID* oid_insert_after ( oid*, size_t, TABLE_OID* ); /* delete a whole Toplevel OID from the linked list including indices */ int delete_oid( oid*, size_t, TABLE_OID* ); /* remove index entries from Toplevel OID list */ int clear_oid_list( TABLE_OID* ); /* searches an OID in the OID linked list */ int search_oid ( oid*, size_t, TABLE_OID*, TABLE_OID** ); /* searches a Toplevel OID in a fully qualified OID */ int search_top_oid ( oid*, size_t, TABLE_OID*, TABLE_OID** ); /* inserts an index into the index linked list */ REG_INDICES* index_insert_after ( char*, int, REG_INDICES* ); /* delete a single entry from the index list or the whole list */ int delete_index( REG_INDICES*, int, int ); /* searches an index in the OID linked list */ int search_index ( char*, REG_INDICES*, REG_INDICES** ); /* main MIB information registry function */ int register_tables ( void*, TABLE_OID* ); /* validates request and sets up fully qualified query/set OID */ int header_osa_table( struct variable*, oid*, size_t*,int, size_t*, WriteMethod**, TABLE_OID* ); /* functions checking for interface changes and updating MIB information */ void update_mib_info ( ); /* retrieves interface information from IF-MIB */ int query_IF_MIB( IF_LIST** ); /* retrieves OSA Express interface information from kernel */ unsigned int query_OSA_EXP ( IF_LIST** ,int ); /* get time of day */ int get_time( char* ); /* display help message */ void usage(); s390-tools-2.38.0/osasnmpd/osasnmpd.8000066400000000000000000000036231502674226300172140ustar00rootroot00000000000000.\" Copyright 2017 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH OSASNMPD 8 "Apr 2006" "s390-tools" .SH NAME osasnmpd \- IBM OSA-Express network card SNMP subagent. .SH SYNOPSIS \fBosasnmpd\fR [-h] [-v] [-f] [-l \fIlogfile\fR | -L] [-A] [-P \fIpidfile\fR] [-x \fIagentx-socket\fR] .SH DESCRIPTION \fBosasnmpd\fR is an SNMP subagent for the net-snmp 5.1.x package. It supports the MIBs provided by an IBM OSA-Express network card. The subagent communicates via the AgentX protocol with the snmpd daemon. Hence the snmpd daemon must be already started on the same system before osasnmpd can start successfully. .SH OPTIONS .TP \fB-h\fR Print usage message and exit. .TP \fB-v\fR Print Version information and exit. .TP \fB-f\fR Don't fork() from the calling shell. .TP \fB-l\fR \fIlogfile\fR Logs all output from the subagent (including stdout/stderr) to \fIlogfile\fR. .br (By default logfile=/var/log/osasnmpd.log) .TP \fB-A\fR Append to the logfile rather than truncating it. .TP \fB-L\fR Print warnings/messages to stdout/stderr. .TP \fB-P\fR \fIpidfile\fR Save the process ID of the subagent in \fIpidfile\fR. .TP \fB-x\fR \fIagentx-socket\fR Uses the specified socket as AgentX connection rather than the default '/var/agentx/master'. The socket can either be a Unix domain socket path, or the address of a network interface. If a network address of the form inet-addr:port is given, then the subagent will use the port specified. If a network address of the form inet-addr is given, then the subagent will use the default AgentX port, 705. The agentx sockets of the snmpd daemon and osasnmpd must match. .SH AUTHOR .nf This man-page was written by Thomas Weber and is maintained by Ursula Braun .fi .SH SEE ALSO .PP snmpd(1), snmp.conf(5), snmpd.conf(5) s390-tools-2.38.0/osasnmpd/osasnmpd.c000066400000000000000000000175421502674226300172740ustar00rootroot00000000000000/* * osasnmpd - IBM OSA-Express network card SNMP subagent * * OSA-E subagent main module * * This subagent extends the net-snmp master agent to support * the Direct SNMP feature of zSeries OSA-E network cards. * net-snmp AgentX support must be enabled to allow the * subagent connecting to the master agent (snmpd). * * Copyright 2017 IBM Corp. * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include "lib/zt_common.h" #include "ibmOSAMibUtil.h" #include "ibmOSAMib.h" /* osasnmpd version */ #define COPYRIGHT "Copyright IBM Corp. 2003, 2017" /* ptr to OSA Express MIB information stored in linked lists */ extern TABLE_OID* oid_list_head; sig_atomic_t keep_running = 1; sig_atomic_t interfaces_changed = 0; /* signal handler for SIGTERM or SIGINT */ RETSIGTYPE stop_server(int UNUSED(a)) { keep_running = 0; } /* signal handler for SIGUSR1 * which is send by the kernel if the interface information changes */ RETSIGTYPE on_interfaces_changed( int UNUSED(a) ) { interfaces_changed++; } static void sysfs_register() { int sys_fd; char buf[10] = {0, }; sys_fd = open( QETH_SYSFILE, O_WRONLY ); if (sys_fd < 0) { fprintf(stderr, "open(%s) failed - reason %s\n", QETH_SYSFILE, strerror( errno ) ); exit(1); } sprintf(buf, "%d", QETH_UPDATE_MIB_SIGNAL); /* register our process to receive SIGUSR1 on interface changes */ if(write(sys_fd, buf, 10) < 0){ fprintf(stderr, "registration with qeth driver failed - " "reason %s\n", strerror( errno ) ); close(sys_fd); exit(1); } close(sys_fd); } static void sysfs_unregister() { int sys_fd; sys_fd = open( QETH_SYSFILE, O_WRONLY ); if (sys_fd < 0) { fprintf(stderr, "open(%s) failed - reason %s\n", QETH_SYSFILE, strerror( errno ) ); exit(1); } /* unregister our process to receive SIGUSR1 on interface changes */ if(write(sys_fd, "unregister", 11) < 0){ fprintf(stderr, "deregistration with qeth driver failed - " "reason %s\n", strerror( errno ) ); close(sys_fd); exit(1); } close(sys_fd); } static struct option longopts[] = { {"help",no_argument,0,'h'}, {"version",no_argument,0,'v'}, {"append",no_argument,0,'A'}, {"stderrlog",no_argument,0,'L'}, {"nofork",no_argument,0,'f'}, {"logfile",required_argument,0,'l'}, {"pidfile",required_argument,0,'P'}, {"sockaddr",required_argument,0,'x'}, {0,0,0,0} }; #define OPTSTRING "hvALfl:A:P:x:" /* * main routine */ int main( int argc, char *argv[] ) { TABLE_OID* li_ptr; char oidstr[MAX_OID_STR_LEN]; char logfile[PATH_MAX + 1]; char pid_file[PATH_MAX + 1]; FILE *PID; struct sigaction act; int res,c,longIndex,rc; unsigned char rel_a, rel_b, rel_c; struct utsname buf; char suffix[sizeof(buf.release)]; /* default settings, may be overridden by parameters */ int std_err_log = 0; /* 0=turn off stderr logging; 1=turn on */ /* if turned off; use file logging */ int dont_zero_log = 0; /* 0=clear logfile; 1=append to logfile */ int dont_fork = 0; /* 0=dont detach from shell; 1=detach */ int pid_file_set = 0; strcpy( logfile, OSAE_LOGFILE ); /* default log file */ /* check for parameters */ while((c = getopt_long(argc, argv, OPTSTRING, longopts, &longIndex)) != -1) { switch (c) { case 'h': usage(); exit(0); case 'v': printf( "osasnmpd: version %s\n", RELEASE_STRING); printf( "%s\n" , COPYRIGHT ); exit(0); case 'l': if (strlen(optarg) > PATH_MAX) { fprintf( stderr, "osasnmpd: logfile "\ "path too long (limit %d "\ "chars)\n", PATH_MAX); exit(1); } strncpy(logfile, optarg, PATH_MAX); break; case 'A': dont_zero_log = 1; break; case 'L': std_err_log=1; break; case 'f': dont_fork = 1; break; case 'P': if (strlen(optarg) > PATH_MAX) { fprintf( stderr, "osasnmpd: logfile "\ "path too long (limit %d "\ "chars)\n", PATH_MAX); exit(1); } strncpy(pid_file,optarg,PATH_MAX); pid_file_set = 1; break; case 'x': netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_X_SOCKET, optarg); break; default: fprintf(stderr, "Try 'osasnmpd --help' for more" " information.\n"); exit(1); } /* end switch */ } /* end-while */ while (optind < argc) { fprintf(stderr, "osasnmpd: unrecognized argument '%s'\n", argv[optind++]); fprintf(stderr, "Try 'osasnmpd --help' for more" " information.\n"); exit(1); } /* detach from shell (default) */ if (!dont_fork && fork() != 0) exit(0); /* create a pidfile if requested */ if (pid_file_set) { if ((PID = fopen(pid_file, "w")) == NULL) { snmp_log_perror("fopen"); fprintf(stderr, "osasnmpd: cannot create PIDFILE\n"); exit(1); } else { fprintf(PID, "%d\n", (int) getpid() ); fclose(PID); } } /* enable logging to stderr or logfile */ if ( !std_err_log ) { snmp_disable_stderrlog(); snmp_enable_filelog( logfile, dont_zero_log ); } else { snmp_enable_stderrlog(); } snmp_log(LOG_INFO, "IBM OSA-E NET-SNMP 5.1.x subagent version %s\n", RELEASE_STRING ); /* make us a agentx client. */ netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, 1); /* initialize the agent library */ if ( init_agent("osasnmpd") != 0 ) { fprintf(stderr, "osasnmpd: init_agent() failed\n" "osasnmpd: check subagent logfile for detailed " "information\n" ); exit(1); } /* initialize OSA Express MIB module here */ init_ibmOSAMib(); /* osasnmpd will be used to read osasnmpd.conf files. */ init_snmp("osasnmpd"); act.sa_flags = 0; sigemptyset( &act.sa_mask ); /* handle termination requests (kill -TERM or kill -INT) */ act.sa_handler = stop_server; if( sigaction( SIGTERM, &act, NULL ) < 0 ){ fprintf(stderr, "sigaction( SIGTERM, ... ) failed - " "reason %s\n", strerror( errno ) ); exit( 1 ); } act.sa_handler = stop_server; if( sigaction( SIGINT, &act, NULL ) < 0 ){ fprintf(stderr, "sigaction( SIGINT, ... ) failed - reason %s\n", strerror( errno ) ); exit( 1 ); } /* handle iterface count change requests ( kill -SIGUSR1 ) */ act.sa_handler = on_interfaces_changed; if( sigaction( SIGUSR1, &act, NULL ) ){ fprintf(stderr, "sigaction( SIGUSR1, ... ) failed - " "reason %s\n", strerror( errno ) ); exit( 1 ); } rc = uname(&buf); if (!rc) sscanf(buf.release, "%c.%c.%c-%s", &rel_a, &rel_b, &rel_c, suffix); if(KERNEL_VERSION(2,6,22) >= KERNEL_VERSION(rel_a, rel_b, rel_c)) sysfs_register(); signal(SIGALRM, update_mib_info); snmp_log(LOG_INFO, "Initialization of OSA-E subagent successful...\n"); /* subagent main loop, that calls * agent_check_and_process() in blocking mode * */ while(keep_running) { if( interfaces_changed > 0 ){ interfaces_changed = 0; alarm(0); /* cancel a potentially scheduled alarm */ update_mib_info(); /* reschedule another update_mib_info() since netsnmp does not update the interface counter immediately, but within the next 60 seconds */ alarm(70); } else agent_check_and_process(1); } snmp_log(LOG_INFO, "Received TERM or STOP signal...shutting down " "agent...\n"); /* unregister all Toplevel OIDs we support */ for(li_ptr = oid_list_head; li_ptr->next != NULL; li_ptr =li_ptr->next){ oid_to_str_conv((oid*)li_ptr->next->pObjid, li_ptr->next->length, oidstr); snmp_log(LOG_INFO, "unregister Toplevel OID .%s.....", oidstr ); res = unregister_mib(li_ptr->next->pObjid, li_ptr->next->length); if (res == MIB_UNREGISTERED_OK) snmp_log(LOG_INFO, "successful\n"); else snmp_log(LOG_INFO, "failed %d\n",res); } if(KERNEL_VERSION(2,6,22) >= KERNEL_VERSION(rel_a, rel_b, rel_c)) sysfs_unregister(); snmp_shutdown("osasnmpd"); return 0; } s390-tools-2.38.0/qetharp/000077500000000000000000000000001502674226300151135ustar00rootroot00000000000000s390-tools-2.38.0/qetharp/Makefile000066400000000000000000000004541502674226300165560ustar00rootroot00000000000000include ../common.mak all: qetharp qetharp: qetharp.o install: all $(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man8 $(DESTDIR)$(BINDIR) $(INSTALL) -m 755 qetharp $(DESTDIR)$(BINDIR) $(INSTALL) -m 644 qetharp.8 $(DESTDIR)$(MANDIR)/man8 clean: rm -f qetharp *.o *~ core .PHONY: all install clean s390-tools-2.38.0/qetharp/qeth26.h000066400000000000000000000041411502674226300163750ustar00rootroot00000000000000/* * qetharp - Read and flush the ARP cache on OSA Express network cards * * ioctl definitions for qeth driver * * Copyright IBM Corp. 2001, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef __ASM_S390_QETH_IOCTL_H__ #define __ASM_S390_QETH_IOCTL_H__ #include #define SIOC_QETH_ARP_SET_NO_ENTRIES (SIOCDEVPRIVATE) #define SIOC_QETH_ARP_QUERY_INFO (SIOCDEVPRIVATE + 1) #define SIOC_QETH_ARP_ADD_ENTRY (SIOCDEVPRIVATE + 2) #define SIOC_QETH_ARP_REMOVE_ENTRY (SIOCDEVPRIVATE + 3) #define SIOC_QETH_ARP_FLUSH_CACHE (SIOCDEVPRIVATE + 4) #define SIOC_QETH_ADP_SET_SNMP_CONTROL (SIOCDEVPRIVATE + 5) #define SIOC_QETH_GET_CARD_TYPE (SIOCDEVPRIVATE + 6) struct qeth_arp_cache_entry { __u8 macaddr[6]; __u8 reserved1[2]; __u8 ipaddr[16]; /* for both IPv4 and IPv6 */ __u8 reserved2[32]; } __attribute__ ((packed)); struct qeth_arp_qi_entry7 { __u8 media_specific[32]; __u8 macaddr_type; __u8 ipaddr_type; __u8 macaddr[6]; __u8 ipaddr[4]; } __attribute__((packed)); struct qeth_arp_qi_entry7_short { __u8 macaddr_type; __u8 ipaddr_type; __u8 macaddr[6]; __u8 ipaddr[4]; } __attribute__((packed)); struct qeth_arp_qi_entry5 { __u8 media_specific[32]; __u8 macaddr_type; __u8 ipaddr_type; __u8 ipaddr[4]; } __attribute__((packed)); struct qeth_arp_qi_entry5_short { __u8 macaddr_type; __u8 ipaddr_type; __u8 ipaddr[4]; } __attribute__((packed)); /* * can be set by user if no "media specific information" is wanted * -> saves a lot of space in user space buffer */ #define QETH_QARP_STRIP_ENTRIES 0x8000 #define QETH_QARP_REQUEST_MASK 0x00ff /* data sent to user space as result of query arp ioctl */ #define QETH_QARP_USER_DATA_SIZE 20000 #define QETH_QARP_MASK_OFFSET 4 #define QETH_QARP_ENTRIES_OFFSET 6 struct qeth_arp_query_user_data { union { __u32 data_len; /* set by user space program */ __u32 no_entries; /* set by kernel */ } u; __u16 mask_bits; char *entries; } __attribute__((packed)); #endif /* __ASM_S390_QETH_IOCTL_H__ */ s390-tools-2.38.0/qetharp/qetharp.8000066400000000000000000000075731502674226300166640ustar00rootroot00000000000000.\" Copyright 2017 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH QETHARP 8 "Nov 2011" "s390-tools" .SH NAME qetharp \- querying and modifying ARP data. .SH SYNOPSIS .TP 8 .B qetharp .RB [ -hv] .br .RB [ -[c|n][6]q .IR interface ] .br .RB [ -p .IR interface ] .br .RB [ -a .IR interface .RB -i .IR IP_address .RB -m .IR MAC_address ] .br .RB [ -d .IR interface .RB -i .IR IP_address ] .SH DESCRIPTION \fBqetharp\fR queries ARP data, such as MAC and IP addresses, from an OSA hardware ARP cache or a HiperSockets ARP cache. For OSA hardware, qetharp can also modify the cache. The command applies only to devices in layer3 mode. It supports IPv6 for HiperSockets only. .SH OPTIONS .TP \fB-q\fR or \fB--query \fIinterface\fR shows the ARP information about the specified network interface. Depending on the device that the interface has been assigned to, this information is obtained from an OSA feature's ARP cache or a HiperSockets ARP cache. The default command output shows symbolic host names and only includes numerical address for host names that cannot be resolved. Use the \fB-n\fR option to show numerical addresses instead of host names. By default, qetharp omits IPv6 related information. Use the \fB-6\fR option to include IPv6 information for HiperSockets. .TP \fB-n\fR or \fB--numeric\fR shows numerical addresses instead of trying to resolve the addresses to the symbolic host names. This option can only be used with the \fB-q\fR option. .TP \fB-c\fR or \fB--compact\fR limits the output to numerical addresses only. This option can only be used with the \fB-q\fR option. .TP \fB-6\fR or \fB--ipv6\fR includes IPv6 information for HiperSockets. For real HiperSockets, shows the IPv6 addresses. For guest LAN HiperSockets, shows the IPv6 to MAC address mappings. This option can only be used with the \fB-q\fR option. .TP \fB-p\fR or \fB--purge \fIinterface\fR flushes the ARP cache of the OSA. The cache contains dynamic ARP entries, which the OSA adapter creates through ARP queries. After flushing the cache, the OSA adapter creates new dynamic entries. This option only works with OSA devices. .TP \fB-a\fR or \fB--add \fIinterface\fR adds a static ARP entry to the OSA adapter card. This option requires an IP address and a MAC address (\fB-i\fR and \fB-m\fR options). .TP \fB-d\fR or \fB--delete \fIinterface\fR deletes a static ARP entry from the OSA adapter card. This command requires an IP address (\fB-i\fR option). .TP \fB-i\fR or \fB--ip \fIIP_address\fR specifies an IP address to be added to or removed from the OSA adapter card. \fB-m\fR or \fB--mac \fIMAC_address\fR specifies a MAC address to be added to the OSA adapter card. .TP \fB-v\fR or \fB--version\fR shows version information. .TP \fB-h\fR or \fB--help\fR shows usage information for qetharp. .SH RETURN CODES qetharp writes the response to stdout and any error message to stderr. The command completes with one of the following return codes: .TP .BR "0" The qetharp command ran successfully. .TP .BR "1" An error occurred. .SH EXAMPLE .TP \fBqetharp -q eth0\fR shows all ARP entries of OSA. .TP \fBqetharp -nq eth0\fR shows all ARP entries of OSA without resolving host names. .TP \fBqetharp -6q hsi0\fR shows all ARP entries of the HiperSockets interface including IPv6 entries. .TP \fBqetharp -n6q hsi0\fR shows all ARP entries of the HiperSockets interface including IPv6 entries without resolving host names. .TP \fBqetharp -p eth0\fR flushes the OSA ARP cache for eth0. .TP \fBqetharp -a eth0 -i 1.2.3.4 -m aa:bb:cc:dd:ee:ff\fR adds a static ARP entry for the IP address 1.2.3.4 to the OSA ARP cache, using a MAC address of aa:bb:cc:dd:ee:ff .TP \fBqetharp -d eth0 -i 1.2.3.4\fR deletes the static ARP entry for the IP address 1.2.3.4 from the OSA ARP cache. .SH AUTHOR .nf This man-page was written by Frank Pavlic s390-tools-2.38.0/qetharp/qetharp.c000066400000000000000000000343121502674226300167260ustar00rootroot00000000000000/* * qetharp - Read and flush the ARP cache on OSA Express network cards * * Copyright IBM Corp. 2001, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/zt_common.h" #include "qetharp.h" #ifndef QETH_QARP_WITH_IPV6 #define IP_TYPE() ipaddr_type #define QETH_QARP_WITH_IPV6 0x4000 #define QETH_QARP_MEDIASPECIFIC_BYTES 32 enum qeth_arp_ipaddrtype { QETHARP_IP_ADDR_V4 = 1, QETHARP_IP_ADDR_V6 = 2, }; struct qeth_arp_entrytype { __u8 mac; __u8 ip; }; struct qeth_arp_qi_entry5_ipv6 { __u8 media_specific[QETH_QARP_MEDIASPECIFIC_BYTES]; struct qeth_arp_entrytype type; __u8 ipaddr[16]; } __attribute__((packed)); struct qeth_arp_qi_entry5_short_ipv6 { struct qeth_arp_entrytype type; __u8 ipaddr[16]; } __attribute__((packed)); struct qeth_arp_qi_entry7_ipv6 { __u8 media_specific[QETH_QARP_MEDIASPECIFIC_BYTES]; struct qeth_arp_entrytype type; __u8 macaddr[6]; __u8 ipaddr[16]; } __attribute__((packed)); struct qeth_arp_qi_entry7_short_ipv6 { struct qeth_arp_entrytype type; __u8 macaddr[6]; __u8 ipaddr[16]; } __attribute__((packed)); #else #define IP_TYPE() type.ip #endif /***************************************************** * Function implementation * *****************************************************/ static inline void qeth_hex_dump(unsigned char *buf, int len) { int i; for (i = 0; i < len; i++) { if (i && !(i % 16)) printf("\n"); printf("%02x ", *(buf + i)); } printf("\n"); } static void show_header() { printf("%-40.40s%-20.20s%-10.10s%-16.16s\n", "Address","HWaddress","HWType","Iface"); } static int ip_to_str(char *tmpbuff, __u8 ipaddr_type, __u8 *ip) { int rc; if (ipaddr_type == IP_VERSION_4) { sprintf(tmpbuff,"%d.%d.%d.%d",ip[0],ip[1],ip[2],ip[3]); } else if (ipaddr_type == IP_VERSION_6) { sprintf(tmpbuff, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x" ":%02x%02x:%02x%02x:%02x%02x", ip[0],ip[1],ip[2],ip[3],ip[4], ip[5],ip[6],ip[7],ip[8],ip[9], ip[10],ip[11],ip[12],ip[13],ip[14], ip[15]); } else { rc = 1; goto out; } rc = 0; out: return rc; } static int lookup_hostname(const char * addr, char *hostname, size_t buflen) { struct addrinfo *addrinfo; int rc; if (getaddrinfo(addr, NULL, NULL, &addrinfo)) { rc = 1; } else { if (getnameinfo(addrinfo->ai_addr, addrinfo->ai_addrlen, hostname, buflen, NULL, 0, 0)) { rc = 1; } else { rc = 0; } freeaddrinfo(addrinfo); } return rc; } static void show_entry5(__u8 ipaddr_type, __u8 *ip, struct option_info *opin) { char tmpbuff[50]; if (ip_to_str(tmpbuff, ipaddr_type, ip)) { printf("unknown entry format\n"); goto out; } if (opin->compact_output == OPTION_INFO_COMPACT_OUTPUT) { printf("%s\n", tmpbuff); } else { char *name; char fqhn[NI_MAXHOST]; if (opin->host_resolution) { name = tmpbuff; } else { if (lookup_hostname(tmpbuff, fqhn, sizeof(fqhn))) { name = tmpbuff; } else { name = fqhn; } } printf("%-40.40s%-20.20s%-10.10s%-16.16s\n", name, "", "hiper", opin->dev_name); } out: return; } static int get_arp_from_hipersockets(struct qeth_arp_query_user_data *udata, struct option_info *opin) { struct qeth_arp_qi_entry5 *entry; struct qeth_arp_qi_entry5_short *entry_s; __u32 bytes_done; int i; bytes_done = 6; if (udata->mask_bits & QETH_QARP_STRIP_ENTRIES) { for (i = 0; i < (int)udata->u.no_entries; i++) { entry_s = (struct qeth_arp_qi_entry5_short *) (((char *)udata) + bytes_done); show_entry5(entry_s->IP_TYPE(), entry_s->ipaddr,opin); bytes_done += entry_s->IP_TYPE() == IP_VERSION_4 ? sizeof(struct qeth_arp_qi_entry5_short) : sizeof(struct qeth_arp_qi_entry5_short_ipv6); } } else { for (i = 0; i < (int)udata->u.no_entries; i++) { entry = (struct qeth_arp_qi_entry5 *) (((char *)udata) + 6 + i * sizeof(*entry)); show_entry5(entry->IP_TYPE(), entry->ipaddr, opin); } } return 0; } static void show_entry7(__u8 ipaddr_type, __u8 *ip, __u8 *mac, unsigned short flags, struct option_info *opin) { char tmpbuff[50]; if (ip_to_str(tmpbuff, ipaddr_type, ip)) { printf("unknown entry format\n"); goto out; } if (opin->compact_output == OPTION_INFO_COMPACT_OUTPUT) { printf("%s\n", tmpbuff); } else { char *name; char macstrbuf[20]; char fqhn[NI_MAXHOST]; if (opin->host_resolution) { name = tmpbuff; } else { if (lookup_hostname(tmpbuff, fqhn, sizeof(fqhn))) { name = tmpbuff; } else { name = fqhn; } } sprintf(macstrbuf,"%02x:%02x:%02x:%02x:%02x:%02x", mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]); printf("%-40.40s%-20.20s%-10.10s%-16.16s\n", name, macstrbuf, (flags==OSACARD_FLAGS)? "ether": (flags==OSA_TR_FLAGS)? "tr":"n/a", opin->dev_name); } out: return; } static int get_arp_from_osacard(struct qeth_arp_query_user_data *udata, unsigned short flags, struct option_info *opin) { struct qeth_arp_qi_entry7 *entry; struct qeth_arp_qi_entry7_short *entry_s; size_t bytes_done = 0; int i; if (udata->mask_bits & QETH_QARP_STRIP_ENTRIES) { for (i = 0; i < (int)udata->u.no_entries; i++){ entry_s = (struct qeth_arp_qi_entry7_short *) (((char *)udata) + 6 + bytes_done); show_entry7(entry_s->IP_TYPE(), entry_s->ipaddr, entry_s->macaddr, flags, opin); bytes_done += entry_s->IP_TYPE() == IP_VERSION_4 ? sizeof(struct qeth_arp_qi_entry7_short) : sizeof(struct qeth_arp_qi_entry7_short_ipv6); } } else { for (i = 0; i < (int)udata->u.no_entries; i++){ entry = (struct qeth_arp_qi_entry7 *) (((char *)udata) + 6 + bytes_done); show_entry7(entry->IP_TYPE(), entry->ipaddr, entry->macaddr, flags, opin); bytes_done += entry->IP_TYPE() == IP_VERSION_4 ? sizeof(struct qeth_arp_qi_entry7_short) : sizeof(struct qeth_arp_qi_entry7_short_ipv6); } } return 0; } static int qetharp_purge(struct option_info *opin) { int sd; struct ifreq ifr; if (!opin->dev_name) { printf("\nError: no interface specified!\n"); return 1; } if ((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("Socket failed: %m\n"); return 1; } strcpy(ifr.ifr_name, opin->dev_name); if (ioctl(sd, SIOC_QETH_ARP_FLUSH_CACHE, &ifr) < 0) { close(sd); perror("\nUnsuccessful"); return 1; } return 0; } static int qetharp_add(struct option_info *opin) { int sd; struct ifreq ifr; struct qeth_arp_cache_entry arp_entry; unsigned int i1,i2,i3,i4,i5,i6,r; memset(&arp_entry, 0, sizeof(struct qeth_arp_cache_entry)); if (!opin->dev_name) { printf("\nError: no interface specified!\n"); return 1; } if (!opin->ip_addr) { printf("\nError: no ip address specified!\n"); return 1; } r=sscanf(opin->ip_addr,"%u.%u.%u.%u",&i1,&i2,&i3,&i4); if ( (r!=4) || (i1>255) || (i2>255) || (i3>255) || (i4>255) ) { printf("\nError: invalid ip address specified!\n"); return 1; } arp_entry.ipaddr[0]=i1; arp_entry.ipaddr[1]=i2; arp_entry.ipaddr[2]=i3; arp_entry.ipaddr[3]=i4; if (!opin->mac_addr) { printf("\nError: no MAC address specified!\n"); return 1; } r=sscanf(opin->mac_addr,"%x:%x:%x:%x:%x:%x",&i1,&i2,&i3,&i4,&i5,&i6); if ( (r!=6) || (i1>255) || (i2>255) || (i3>255) || (i4>255) || (i5>255) || (i6>255) ) { printf("\nError: invalid MAC address specified!\n"); return 1; } arp_entry.macaddr[0]=i1; arp_entry.macaddr[1]=i2; arp_entry.macaddr[2]=i3; arp_entry.macaddr[3]=i4; arp_entry.macaddr[4]=i5; arp_entry.macaddr[5]=i6; if ((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("Socket failed: %m\n"); return 1; } strcpy(ifr.ifr_name, opin->dev_name); ifr.ifr_ifru.ifru_data = (void*)&arp_entry; if (ioctl(sd, SIOC_QETH_ARP_ADD_ENTRY, &ifr) < 0) { close(sd); perror("\nUnsuccessful"); return 1; } return 0; } static int qetharp_delete(struct option_info *opin) { int sd; struct ifreq ifr; struct qeth_arp_cache_entry arp_entry; unsigned int i1,i2,i3,i4,r; memset(&arp_entry,0,sizeof(struct qeth_arp_cache_entry)); if (!opin->dev_name) { printf("\nError: no interface specified!\n"); return 1; } if (!opin->ip_addr) { printf("\nError: no ip address specified!\n"); return 1; } r=sscanf(opin->ip_addr,"%u.%u.%u.%u",&i1,&i2,&i3,&i4); if ( (r!=4) || (i1>255) || (i2>255) || (i3>255) || (i4>255) ) { printf("\nError: invalid ip address specified!\n"); return 1; } arp_entry.ipaddr[0]=i1; arp_entry.ipaddr[1]=i2; arp_entry.ipaddr[2]=i3; arp_entry.ipaddr[3]=i4; if ((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("Socket failed: %m\n"); return 1; } strcpy(ifr.ifr_name, opin->dev_name); ifr.ifr_ifru.ifru_data = (void*)&arp_entry; if (ioctl(sd, SIOC_QETH_ARP_REMOVE_ENTRY, &ifr) < 0) { close(sd); perror("\nUnsuccessful"); return 1; } return 0; } static int qetharp_query(struct option_info *opin) { int sd; struct ifreq ifr; struct qeth_arp_query_user_data *udata; int memsize,result; unsigned short mask_bits; if (!opin->dev_name) { printf("\nError: no interface specified!\n"); return 1; } if ((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("Socket failed: %m\n"); return 1; } strcpy(ifr.ifr_name, opin->dev_name); memsize = QETH_QARP_USER_DATA_SIZE; udata = malloc(QETH_QARP_USER_DATA_SIZE); memcpy(&udata->u.data_len, &memsize, sizeof(int)); udata->mask_bits = QETH_QARP_STRIP_ENTRIES; if (opin->ipv6) { udata->mask_bits |= QETH_QARP_WITH_IPV6; } ifr.ifr_ifru.ifru_data = (char *) udata; if (ioctl(sd, SIOC_QETH_ARP_QUERY_INFO, &ifr) < 0) { close(sd); perror("\nUnsuccessful"); return 1; } if (opin->compact_output!=OPTION_INFO_COMPACT_OUTPUT) { show_header(); } if (!udata->u.no_entries) { /* rational: mask_bits are not defined in that case */ return 0; } mask_bits = udata->mask_bits & QETH_QARP_REQUEST_MASK; if (mask_bits == HIPERSOCKET_FLAGS) result = get_arp_from_hipersockets(udata, opin); else if (mask_bits == OSACARD_FLAGS) result = get_arp_from_osacard(udata, mask_bits, opin); else if (mask_bits == OSA_TR_FLAGS) result = get_arp_from_osacard(udata, mask_bits, opin); else { perror("\nReceived entries with invalid format"); return 1; } free(udata); return result; } static void qetharp_usage(void) { printf("qetharp [-[nc6]q interface]|[-p interface]|\n" \ "\t\t[-a interface -i ip-addr -m MAC-addr]|\n" \ "\t\t[-d interface -i ip-addr] [-h] [-v ]\n\n"); printf("where:\n" \ "\tq: prints ARP entries found on the card\n" \ "\tn: in conjunction with the -q option it shows\n" \ "\t\tnumerical addresses instead of trying to\n" \ "\t\tresolve IP addresses to host names.\n" \ "\tc: in conjunction with the -q option it shows\n" \ "\t\tonly numerical addresses without any\n" \ "\t\tother information.\n" \ "\t6: in conjunction with the -q option it shows\n" \ "\t\tIPv6 related entries, if applicable\n" \ "\tp: flushes the ARP table of the card\n" \ "\ta: add static ARP entry\n" \ "\td: delete static ARP entry\n" \ "\tv: prints version information\n" "\th: prints this usage information\n"); } static int qetharp_parse_info(struct option_info *opin) { if (opin->dev_name && (strlen(opin->dev_name) >= IFNAMSIZ)) { printf("\nError: interface name too long\n"); return 1; } if ((opin->purge_flag+opin->query_flag+ opin->add_flag+opin->delete_flag)==0) { qetharp_usage(); return 1; } if ((opin->purge_flag+opin->query_flag+ opin->add_flag+opin->delete_flag)!=1) { printf("\nUse only one of the options '-a', " \ "'-d', '-p' and 'q' per call.\n"); return 1; } if (opin->purge_flag && (opin->query_flag || opin->host_resolution)) { printf("\nError in using '-p' option:\n" \ "\tYou can not use '-p' option in conjunction with " \ "'-q' or '-n'.\n"); return 1; } if (opin->purge_flag) { return qetharp_purge(opin); } if ((opin->host_resolution) && !(opin->query_flag)) { printf("\nError in using '-n' option:\n" \ "\t'-q' option missing!\n"); return 1; } if ((opin->ipv6) && !(opin->query_flag)) { printf("\nError in using '-6' option:\n" \ "\t'-q' option missing!\n"); return 1; } if (opin->query_flag) { return qetharp_query(opin); } if (opin->add_flag) { if ((!opin->ip_flag)||(!opin->mac_flag)) { printf("\nError in using '-a' option:\n" \ "\t'-i' or '-m' option missing!\n"); return 1; } return qetharp_add(opin); } if (opin->delete_flag) { if (!opin->ip_flag) { printf("\nError in using '-d' option:\n" \ "\t'-i' option missing!\n"); return 1; } return qetharp_delete(opin); } return 0; } int main(int argc, char **argv) { int index,c,result; struct option_info info; result=0; memset(&info, 0, sizeof(info)); c = getopt_long(argc, argv, QETHARP_GETOPT_STRING, qetharp_options, &index); if (c == -1 ) { qetharp_usage(); exit(0); } while (c != -1) { switch (c) { case 'h': qetharp_usage(); exit(0); case 'v': printf("qetharp: version %s\n", RELEASE_STRING); printf( "%s\n",COPYRIGHT ); exit(0); case 'q': info.dev_name = optarg; info.query_flag = OPTION_INFO_QUERY; break; case 'n': info.host_resolution = OPTION_INFO_NO_RESOLUTION; break; case '6': info.ipv6 = OPTION_INFO_IPV6; break; case 'p': info.dev_name = optarg; info.purge_flag = OPTION_INFO_PURGE; break; case 'c': info.compact_output = OPTION_INFO_COMPACT_OUTPUT; break; case 'a': info.dev_name = optarg; info.add_flag = OPTION_INFO_ADD; break; case 'd': info.dev_name = optarg; info.delete_flag = OPTION_INFO_DELETE; break; case 'i': info.ip_addr = optarg; info.ip_flag = OPTION_INFO_IP; break; case 'm': info.mac_addr = optarg; info.mac_flag = OPTION_INFO_MAC; break; default: fprintf(stderr, "Try 'qetharp --help' for more" " information.\n"); exit(1); } c = getopt_long(argc, argv,QETHARP_GETOPT_STRING, qetharp_options,&index); } result = qetharp_parse_info(&info); return result; } s390-tools-2.38.0/qetharp/qetharp.h000066400000000000000000000062331502674226300167340ustar00rootroot00000000000000/* * qetharp - Read and flush the ARP cache on OSA Express network cards * * Copyright IBM Corp. 2001, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef __QETHARP_H__ #define __QETHARP_H__ #include #include #include "qeth26.h" #define ifr_name ifr_ifrn.ifrn_name /* interface name */ #define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */ #define ifr_addr ifr_ifru.ifru_addr /* address */ #define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-p lnk */ #define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */ #define ifr_netmask ifr_ifru.ifru_netmask /* interface net mask */ #define ifr_flags ifr_ifru.ifru_flags /* flags */ #define ifr_metric ifr_ifru.ifru_ivalue /* metric */ #define ifr_mtu ifr_ifru.ifru_mtu /* mtu */ #define ifr_map ifr_ifru.ifru_map /* device map */ #define ifr_slave ifr_ifru.ifru_slave /* slave device */ #define ifr_data ifr_ifru.ifru_data /* for use by interface */ #define ifr_ifindex ifr_ifru.ifru_ivalue /* interface index */ #define ifr_bandwidth ifr_ifru.ifru_ivalue /* link bandwidth */ #define ifr_qlen ifr_ifru.ifru_ivalue /* Queue length */ #define ifr_newname ifr_ifru.ifru_newname /* New name */ /***************************************************** * Declarations for OSA Relevant Things * *****************************************************/ #define DATA_SIZE 20000 #define HIPERSOCKET_FLAGS 5 #define OSACARD_FLAGS 7 #define OSA_TR_FLAGS 0xf #define MAC_LENGTH 6 #define IPV4_LENGTH 4 #define IPV6_LENGTH 16 #define IP_VERSION_4 1 #define IP_VERSION_6 2 /***************************************************** * Declarations for parsing options * *****************************************************/ #define QETHARP_GETOPT_STRING "p:q:a:d:i:m:n6chv" #define OPTION_INFO_QUERY 1 #define OPTION_INFO_PURGE 1 #define OPTION_INFO_NO_RESOLUTION 1 #define OPTION_INFO_COMPACT_OUTPUT 1 #define OPTION_INFO_ADD 1 #define OPTION_INFO_DELETE 1 #define OPTION_INFO_IP 1 #define OPTION_INFO_MAC 1 #define OPTION_INFO_IPV6 1 /***************************************************** * Declarations for version string * *****************************************************/ #define COPYRIGHT "Copyright IBM Corp. 2003, 2017" static struct option qetharp_options[]= { { "query", 1, 0, 'q'}, { "purge", 1, 0, 'p'}, { "ipv6", 0, 0, '6'}, { "numeric", 0, 0, 'n'}, { "compact", 0, 0, 'c'}, { "add", 1, 0, 'a'}, { "delete", 1, 0, 'd'}, { "ip", 1, 0, 'i'}, { "mac", 1, 0, 'm'}, { "help", 0, 0, 'h'}, { "version", 0, 0, 'v'}, {0,0,0,0} }; struct option_info { int purge_flag; int query_flag; int host_resolution; int compact_output; int ipv6; int add_flag; int delete_flag; int ip_flag; int mac_flag; char *dev_name; char *ip_addr; char *mac_addr; }; #endif /* __QETHARP_H__ */ s390-tools-2.38.0/qethconf/000077500000000000000000000000001502674226300152565ustar00rootroot00000000000000s390-tools-2.38.0/qethconf/Makefile000066400000000000000000000006661502674226300167260ustar00rootroot00000000000000include ../common.mak all: install: qethconf $(SED) -e 's/%S390_TOOLS_VERSION%/$(S390_TOOLS_RELEASE)/' \ < qethconf >$(DESTDIR)$(BINDIR)/qethconf; \ chown $(OWNER):$(GROUP) $(DESTDIR)$(BINDIR)/qethconf; \ chmod 755 $(DESTDIR)$(BINDIR)/qethconf; \ $(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8 $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 qethconf.8 \ $(DESTDIR)$(MANDIR)/man8 clean: .PHONY: all install clean s390-tools-2.38.0/qethconf/qethconf000077500000000000000000000513651502674226300170250ustar00rootroot00000000000000#!/bin/bash # # qethconf - Tool to configure IPA, VIPA, and Proxy ARP for OSA-Express cards # # This shell script simplifies the usage of the OSA-Express functions # # - IP address takeover (IPA) # - Proxy ARP # - Virtual IP address (VIPA) # # IP addresses in IPv4 or IPv6 format must be no longer specified in # hexadecimal format. The script compounds the commands for the functions # according to the given parameters and sends it to the appropriate # OSA-Express device driver /proc file. # # Copyright 2017 IBM Corp. # # s390-tools is free software; you can redistribute it and/or modify # it under the terms of the MIT license. See LICENSE for details. # script_name=${0##*/} # name of this script vipa_ext="_vipa" # command extension for VIPA parp_ext="_rxip" # command extension for Proxy ARP proc_file="/proc/qeth_ipa_takeover" # kernel 2.4 qeth proc file for these features sys_file="/sys/devices/qeth" # kernel 2.6 sys fs file for these features sys_ifnames="/sys/class/net" # include all available if_names echo_cmd="" # echo command to be build # # exit script on error and print usage hint # function __exit_on_error { echo "Try '$script_name --help' for more information." exit 1 } # # error message if layer2 is enabled # function __layer2_enabled { # # check interface name if specified # if [ -z "$1" ]; then echo $script_name": interface name required for function $cmd_type" __exit_on_error elif [ ! -e $sys_ifnames/$1 ]; then echo $script_name": interface does not exist" __exit_on_error fi # # check if layer 2 is enables # if [ -e /sys/class/net/$1/device/layer2 ]; then if [ "`cat /sys/class/net/$1/device/layer2`" != 0 ]; then if [ "$2" != 'list' ]; then echo "$script_name: OSA layer 2 enabled for device $1 !" echo " IPA, VIPA, PARP not supported." exit 1 else return 1 fi fi fi } # # function for printing the usage message # function __usage { printf '\n%s %s %s\n\n' "Usage:" $script_name "TYPE CMD [IPADDR] [INTERFACE]" printf ' %s\n' "Description:" printf ' %s\n' "Shell script to configure IPA, VIPA and Proxy ARP for OSA-Express" printf ' %s\n\n' "cards in layer 3 mode." printf ' %s\n\n' "Parameter:" printf ' %s\t\t%s\n' "TYPE" "" printf '\t\t%s\n' "choose one of these keywords for feature" printf '\t\t%s\n' "ipa - IP address takeover" printf '\t\t%s\n' "vipa - Virtual IP address" printf '\t\t%s\n' "parp|rxip - Proxy ARP" printf '\t\t%s\n' "list_all - list all available entries" printf '\t\t%s\n' "list_msg - list messages and explanation" printf '\n %s\t\t%s\n' "CMD" "" printf '\t\t%s\n' "where" printf '\t\t%s\n' "add - add an IP address or address range" printf '\t\t%s\n' "del - delete an IP address or address range" printf '\t\t%s\n' "inv4 - inverts the selection of address ranges for IPv4" printf '\t\t%s\n' " (only IPA)" printf '\t\t%s\n' "inv6 - inverts the selection of address ranges for IPv6" printf '\t\t%s\n' " (only IPA)" printf '\t\t%s\n' "list - list defined entries per feature" printf '\n %s\t%s\n' "IPADDR" "[-x][/]" printf '\t\t%s\n' "required for commands add and del" printf '\t\t%s\n' "addr - IP address in IPv4 or IPv6 format" printf '\t\t%s\n' " e.g. 192.168.10.38 or FE80::800:5A12:3459" printf '\t\t%s\n' " use option -x for hexadecimal format" printf '\t\t%s\n' " e.g. c0a80a26" printf '\t\t%s\n' "mask_bits - number of bits which are set in the" printf '\t\t%s\n' " network mask (required for IPA)" printf '\n %s\t%s\n' "INTERFACE" "interface name to which the address or address range" printf '\t\t%s\n' "is bound, e.g eth0" exit 0 } function PrintVersion { echo "$script_name: version %S390_TOOLS_VERSION%" echo "Copyright IBM Corp. 2003, 2017" } # # prints a row from /proc/qeth_ipa_takeover # function __print_line { local ip_num= if [ -n "$(echo $raw_cmd | grep '4')" ]; then # IPv4 # convert hex IPv4 address to human format for i in 0 2 4 6; do # convert hex digits to uppercase (bc needs this) # use bc for hex to dec conversion ip_num=`echo ${ipinfo:i:2} | tr '[a-f]' '[A-F]'` ip_num=`echo "ibase=16; $ip_num" | bc` fmt_line="$fmt_line$ip_num" if [ "$i" -ne 6 ]; then fmt_line="$fmt_line." fi done else # IPv6 # convert IPv6 address to human readable format for i in 0 4 8 12 16 20 24 28; do ip_num=${ipinfo:i:4} fmt_line="$fmt_line$(echo $ip_num | sed -e 's/0000/0/')" if [ "$i" -ne 28 ]; then fmt_line="$fmt_line:" fi done fi # add mask bits and interface, if existent if [ -n "$(echo $ipinfo| grep '/')" ]; then fmt_line="$fmt_line/$(echo ${ipinfo##*/} | sed -e 's/:/ /')" elif [ -n "$(echo $ipinfo| grep ':')" ]; then fmt_line="$fmt_line ${ipinfo##*:}" fi # finally echo line to stdout echo $fmt_line } # # list defined entries per IPA/VIPA/PARP function # function __list_entries { local cmd_type=$1 local fmt_line= local device_list= local number_of_devices= declare -i count=0 # # list all available entries for all devices # if [ "$1" = list_all ];then cmd_type="ipa vipa rxip" fi for j in ${cmd_type} do device_list="`cat $sys_file/*.*.*/if_name`" for i in ${device_list} do __layer2_enabled "$i" "list" if [ "$?" -ne 0 ]; then continue fi case "$j" in ipa ) cmd_type_temp=ipa_takeover;; parp ) cmd_type_temp=rxip j=rxip;; * ) cmd_type_temp=$j;; esac fmt_line=/sys/class/net/$i/device/$cmd_type_temp/ for k in 4 6 do # # here we get the data from the sysfs # if [ -f /sys/class/net/$i/device/$cmd_type_temp/add$k ]; then cat_output="`cat /sys/class/net/$i/device/$cmd_type_temp/add$k`" for l in ${cat_output} do if [ -n "$cat_output" ]; then if [ "$j" = rxip ]; then j=parp fi echo "$j add $l $i" count=count+1 fi done fi done done done if [ "$count" -eq 0 ]; then echo $script_name": currently no $cmd_type entries defined" fi exit 0 } # # list messages and explanation # function __list_msgs { local cmd_type=$1 printf '\n\t\t%s\n' "Operation not permitted." printf '\t\t%s\n' "OSA layer2 mode is enabled for the device. IPA, VIPA," printf '\t\t%s\n' "and PARP are not supported with layer 2." printf '\n\t\t%s\n' "Out of memory." printf '\t\t%s\n' "There is not enough free memory to allocate an entry in sysfs." printf '\n\t\t%s\n' "File exists." printf '\t\t%s\n' "The entry to be added (e.g. IP address) does already exist" printf '\n\t\t%s\n' "Invalid argument." printf '\t\t%s\n' "At least one of the following specifications was not valid:" printf '\t\t%s\n' "- The IP address format" printf '\t\t%s\n' "- The mask bits for the IP address" printf '\t\t%s\n' "- The value in the field for IP address inversion" exit 0 } # # IP address conversion - converts IP address to hexadecimal format # function __ip_conv { declare -i count=0 declare -i zeropad=8 ip_temp= ip_shortened="false" strt_end_col="false" ipv4_rule='^[[:digit:]]\{1,3\}\.[[:digit:]]\{1,3\}\.[[:digit:]]\{1,3\}\.[[:digit:]]\{1,3\}$' ipv6_rule='^[[:xdigit:]:.]\+$' # if IP address is given as hex input; option -x # convert it to decimal if [ -n "$(echo $ip_addr | grep '\-x')" ]; then # # hex input given # ip_addr=${ip_addr##-x} if [ -n "$(echo $ip_addr | grep '^[[:xdigit:]]\{8\}$')" ]; then ip_ver=4 elif [ -n "$(echo $ip_addr | grep '^[[:xdigit:]]\{32\}$')" ]; then ip_ver=6 else echo $script_name": bad IP address" __exit_on_error fi ip_hex=$ip_addr # # convert hex given IPv4 address to decimal for kernel 2.6 # ip_addr_length=${#ip_addr} if [ $ip_addr_length = 8 ]; then ip_hex="" while [ $ip_addr_length -gt 0 ]; do ip_addr_length=$(($ip_addr_length-2)) ip_temp='0x'`expr substr $ip_addr 1 2` ip_addr=`expr substr $ip_addr 3 $ip_addr_length` # # conversion from hex to decimal # ip_hex_temp="$(printf '%02d' $ip_temp)" if [ `expr substr $ip_hex_temp 1 1` = 0 ]; then ip_hex_temp=`expr substr $ip_hex_temp 2 1` fi if [ $ip_addr_length -ge 2 ]; then ip_hex_temp="$ip_hex_temp." fi ip_hex=$ip_hex$ip_hex_temp done else # # add ':' signs to hex given IPv6 address # ip_hex="" ip_addr_length=${#ip_addr} while [ $ip_addr_length -gt 0 ]; do ip_temp=`expr substr $ip_addr 1 4` if [ $ip_addr_length -gt 4 ]; then ip_temp="$ip_temp:" fi ip_addr_length=$(($ip_addr_length-4)) ip_addr=`expr substr $ip_addr 5 $ip_addr_length` ip_hex=$ip_hex$ip_temp done fi else # # IPv4 format # ip_addr_orig=$ip_addr if [ -n "$(echo $ip_addr | grep "$ipv4_rule")" ]; then ip_ver=4 until [ -z "$(echo $ip_addr | grep '\.')" ]; do ip_temp="${ip_addr%%.*}" ip_temp=$(echo $ip_temp | sed -e 's/0*//') if [ -z "$ip_temp" ]; then ip_hex=$ip_hex"00" else ip_hex="$ip_hex$(printf '%02x' $ip_temp)" fi ip_addr=${ip_addr#*.} done ip_temp=$(echo $ip_addr | sed -e 's/0*//') if [ -z "$ip_temp" ]; then ip_hex=$ip_hex"00" else ip_hex="$ip_hex$(printf '%02x' $ip_temp)" fi if [ "${#ip_hex}" -gt 8 ]; then echo $script_name": bad IPv4 address" __exit_on_error fi ip_hex=$ip_addr_orig # # IPv6 format # elif [ -n "$(echo $ip_addr | grep "$ipv6_rule")" ]; then # check for IPv4-compatible address or IPv4-mapped address if [ -n "$(echo $ip_addr | grep '\.')" ]; then ipv4_part=${ip_addr##*:} ipv6_part=`echo ${ip_addr%:*} | tr '[a-f]' '[A-F]'`":" if [ -z "$(echo $ipv4_part | grep "$ipv4_rule")" ]; then echo $script_name": bad IP address" __exit_on_error fi ip_temp="$(printf '%02x' ${ipv4_part%%.*})" ipv4_part=${ipv4_part#*.} ip_temp=$ip_temp"$(printf '%02x' ${ipv4_part%%.*})" ipv4_part=${ipv4_part#*.} ip_temp=$ip_temp":""$(printf '%02x' ${ipv4_part%%.*})" ipv4_part=${ipv4_part#*.} ip_temp=$ip_temp"$(printf '%02x' $ipv4_part)" if [ "${#ip_temp}" -gt 9 ]; then echo $script_name": bad IPv6 address" __exit_on_error fi ip_addr=$ipv6_part$ip_temp fi # count number of colons within IP address ip_temp=$ip_addr until [ -z "$(echo $ip_temp | grep ':')" ]; do ip_temp=${ip_temp#*:} count=count+1 done # test number of allowed colons if [ "$count" -gt 7 ] || [ "$count" -lt 2 ]; then echo $script_name": bad IP address" __exit_on_error fi # # have to increase count for zero padding for starting/ending colon # if [ -z "${ip_addr%::*}" ]; then zeropad=zeropad+1 strt_end_col="true" fi if [ -z "${ip_addr#*::}" ]; then zeropad=zeropad+1 strt_end_col="true" fi # # loop through IPv6 address and convert it to 16 byte hex input # ip_ver=6 ip_addr=$ip_addr":" # use colon as end marker here while [ -n "$(echo $ip_addr | grep ':')" ]; do ip_temp=${ip_addr%%:*} if [ -z "$ip_temp" ]; then # found IPv6 double colon shortcut - add missing 0s if [ "$ip_shortened" = false ]; then zeropad=zeropad-$count while [ $zeropad -ne 0 ]; do ip_hex=$ip_hex"0000": zeropad=zeropad-1 done ip_shortened="true" if [ $strt_end_col = "true" ]; then ip_addr=${ip_addr#:} fi elif [ ! $ip_addr = ":" ]; then echo $script_name": IPv6 double colon shortcut can be used only once" __exit_on_error fi fi sign=":" case "${#ip_temp}" in 1 ) ip_hex=$ip_hex"000"$ip_temp$sign;; 2 ) ip_hex=$ip_hex"00"$ip_temp$sign ;; 3 ) ip_hex=$ip_hex"0"$ip_temp$sign ;; 4 ) ip_hex="$ip_hex$ip_temp$sign" ;; 0 ) ;; * ) echo $script_name": bad IPv6 address" __exit_on_error ;; esac ip_addr=${ip_addr#*:} done ip_hex=${ip_hex%*:} # # test if given IPv6 address was too short # if [ "$count" -lt 7 -a "$ip_shortened" = false ]; then echo $script_name": given IPv6 is too short" __exit_on_error fi else echo $script_name": IP address wrong or missing" __exit_on_error fi fi } # # builds echo command according to the given parameters # function __build_cmd { # input parameters local cmd_type=$1 local cmd_parm=$2 local ip_addr=$3 local interface=$4 local mask_bits= ip_ver= local ip_hex= # # allow also shortcut rxip for Proxy ARP # if [ $cmd_type = rxip ]; then cmd_type=parp fi # # check if mask bits are given for parameter IPADDR # if [ -z "$ip_addr" ]; then echo $script_name": IP address parameter missing" __exit_on_error elif [ -n "$(echo $ip_addr | grep '/')" ]; then if [ $cmd_type = ipa ]; then mask_bits=${ip_addr##*/} ip_addr=${ip_addr%%/*} if [ -z "$(echo $mask_bits | grep '^[[:digit:]]\{1,3\}$')" ]; then echo $script_name": invalid mask bits specified" __exit_on_error fi else echo $script_name": mask bits not allowed for $cmd_type" __exit_on_error fi elif [ $cmd_type = ipa ]; then echo $script_name": mask bits required for function $cmd_type" __exit_on_error fi __layer2_enabled $interface __ip_conv # # assemble command # echo_cmd="$echo_cmd$ip_ver $ip_hex" if [ -n "$mask_bits" ]; then if [ "$ip_ver" = 4 ]; then if [ "$mask_bits" -gt 32 ]; then echo $script_name": invalid mask bits specified" __exit_on_error fi else if [ "$mask_bits" -gt 128 ]; then echo $script_name": invalid mask bits specified" __exit_on_error fi fi echo_cmd="${echo_cmd}/$mask_bits" fi } #--------------------------------------- # -- main -- #--------------------------------------- # # parse options (currently none avail) # while [ -n "$(echo $1 | grep '-')" ]; do case "$1" in -v | --version ) PrintVersion exit 0 ;; * ) __usage ;; esac shift done # # check if target proc or sysfs file exists - # otherwise the OSA-E device driver is not loaded # if [ ! -e "$sys_file" ]; then echo $script_name": No QDIO device found" echo "Try 'man $script_name' for more information." exit 1 fi # # parse arguments TYPE and CMD # if [ "$#" -lt 1 -o "$#" -gt 4 ]; then echo $script_name": invalid number of parameters specified" __exit_on_error else case "$1" in ipa ) case "$2" in add | del ) echo_cmd="$2" __build_cmd "$@";; inv4 | inv6 ) if [ "$#" -gt 3 ]; then echo $script_name": too many parameters for $2 command" __exit_on_error else if [ -z "$3" ]; then echo $script_name": interface name required for function $cmd_type" __exit_on_error else __layer2_enabled $3 if [ $2 = inv4 ]; then echo_cmd="invert4 toggle" else echo_cmd="invert6 toggle" fi fi fi;; list ) if [ "$#" -gt 2 ]; then echo $script_name": too many parameters for $2 command" __exit_on_error else __list_entries "$@" fi;; * ) echo $script_name": invalid CMD parameter specified" __exit_on_error ;; esac ;; vipa ) case "$2" in add | del ) echo_cmd="$2" __build_cmd "$@";; list ) if [ "$#" -gt 2 ]; then echo $script_name": too many parameters for $2 command" __exit_on_error else __list_entries "$@" fi;; * ) echo $script_name": invalid CMD parameter specified" __exit_on_error ;; esac;; parp | rxip ) case "$2" in add | del ) echo_cmd="$2" __build_cmd "$@";; list ) if [ "$#" -gt 2 ]; then echo $script_name": too many parameters for $2 command" __exit_on_error else __list_entries "$@" fi;; * ) echo $script_name": invalid CMD parameter specified" __exit_on_error ;; esac;; list_all ) if [ "$#" -gt 1 ]; then echo $script_name": too many parameters for $2 command" __exit_on_error else __list_entries "$@" fi;; list_msg ) if [ "$#" -gt 1 ]; then echo $script_name": too many parameters for $2 command" __exit_on_error else __list_msgs "$@" fi;; * ) echo $script_name": invalid TYPE parameter specified" __exit_on_error ;; esac fi # # finally echo cmd to appropriate OSA-Express proc or sysfs file # and check if command was successful # if [ "$2" = inv4 ] || [ "$2" = inv6 ]; then sys_fs_dir="/sys/class/net/$3/device" else sys_fs_dir="/sys/class/net/$4/device" fi echo_cmd_temp=($echo_cmd) # # build sysfs path # case "$1" in ipa ) proc_file=$sys_fs_dir/ipa_takeover/${echo_cmd_temp[0]} echo_cmd=${echo_cmd_temp[1]} ;; vipa ) proc_file=$sys_fs_dir/vipa/${echo_cmd_temp[0]} echo_cmd=${echo_cmd_temp[1]} ;; parp | rxip ) proc_file=$sys_fs_dir/rxip/${echo_cmd_temp[0]} echo_cmd=${echo_cmd_temp[1]} ;; esac # # check if entry to delete exists # if [ "$2" = del ]; then case "$1" in ipa ) if ! grep -sqi "${echo_cmd_temp[1]}" $sys_fs_dir/ipa_takeover/add$ip_ver; then echo $script_name": no such $1 entry found." exit 0 fi;; vipa ) if ! grep -sqi "${echo_cmd_temp[1]}" $sys_fs_dir/$1/add$ip_ver; then echo $script_name": no such $1 entry found." exit 0 fi;; parp | rxip ) if ! grep -sqi "${echo_cmd_temp[1]}" $sys_fs_dir/rxip/add$ip_ver; then echo $script_name": no such $1 entry found." exit 0 fi;; esac fi # # does proc or sysfs entry exsist ? # if [ -f $proc_file ]; then echo $echo_cmd > $proc_file if [ "$?" -ne 0 ]; then echo $script_name": The qethconf command failed." echo "Enter 'qethconf list_msg' for details." exit 1 fi else echo $script_name": device does not exist or is not capable of $1." __exit_on_error fi if [ "$1" = rxip ]; then message_cmd=parp else message_cmd=$1 fi case "$2" in add ) echo "$script_name: Added $echo_cmd to sysfs entry $proc_file." echo "$script_name: For verification please use \"qethconf $message_cmd list\" " ;; del ) echo "$script_name: Deleted $echo_cmd from sysfs entry $proc_file." echo "$script_name: For verification please use \"qethconf $message_cmd list\" " ;; inv4 | inv6 ) if [ "`cat $proc_file`" != 0 ]; then echo "$script_name: Negating the following IP address takeover settings:" if [ "$2" = inv4 ]; then echo "`cat ${proc_file/invert4/add4}`" else echo "`cat ${proc_file/invert6/add6}`" fi else echo "$script_name: The following IP address takeover settings are valid again:" if [ "$2" = inv4 ]; then echo "`cat ${proc_file/invert4/add4}`" else echo "`cat ${proc_file/invert6/add6}`" fi fi;; esac s390-tools-2.38.0/qethconf/qethconf.8000066400000000000000000000046341502674226300171650ustar00rootroot00000000000000.\" Copyright 2017 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH QETHCONF 8 "Apr 2006" "s390-tools" .SH "NAME" qethconf \- Configure QETH functions IPA, VIPA and Proxy ARP. .SH "SYNOPSIS" \fBqethconf\fR \fITYPE\fR \fICMD\fR [[\-x]\fIIPADDR\fR[/\fIMASKBITS\fR]] [\fIINTERFACE\fR] .SH "DESCRIPTION" \fBqethconf\fR simplifies usage of QETH IPA, VIPA and Proxy ARP functions for OSA-Express cards in layer 3 mode. .TP \fITYPE\fR .br Choose one of these keywords for function: .br ipa \- IP address takeover (IPA) .br vipa \- Virtual IP address (VIPA) .br parp|rxip \- Proxy ARP .br list_all \- List all available IPA, VIPA and Proxy ARP entries from the sysfs .br list_msg \- List messages and explanation of errors during command execution .TP \fICMD\fR .br where .br add \- Add an IP address or address range. .br del \- Delete an IP address or address range. .br inv4 \- Invert the selection of address ranges for IPv4 (only IPA). .br inv6 \- Invert the selection of address ranges for IPv6 (only IPA). .br list \- List TYPE specific entries from the sysfs .TP \fIIPADDR\fR Required for commands add and del. .br IP address in IPv4 or IPv6 format, .br e.g. 192.168.10.38 or FE80::1:800:23e7:f5db .br Use preceding option \-x for hexadecimal input format, e.g. \-xc0a80a26 .TP \fIMASKBITS\fR Number of bits set in the network mask. Network masks can be used to specify address ranges, e.g. 192.168.10.0/24. This subparameter is only valid for function IPA. .TP \fIINTERFACE\fR Interface name to which the address or address range is bound, e.g. eth0. This parameter is required for add, del and the inv4 and inv6 command. .SH "RESTRICTIONS" .nf Needs at least bash version 2.0. .SH "EXAMPLES" .nf qethconf ipa add 192.168.10.0/24 eth0 qethconf ipa add \-xc0a80a00/24 eth1 qethconf ipa inv4 eth0 qethconf parp add 10.0.0.2 eth0 (or qethconf rxip add 10.0.0.2 eth0) qethconf vipa del 192.168.10.2 tr0 qethconf vipa add FE80::1:800:23e7:f5db eth1 qethconf list_all .SH "AUTHOR" .nf This man\-page was written by Thomas Weber. .SH "SEE ALSO" .PP See the appropriate chapters for QETH IPA, VIPA and Proxy ARP in the "Device Drivers, Features, and Commands" manual for Linux on System z. s390-tools-2.38.0/qethqoat/000077500000000000000000000000001502674226300152755ustar00rootroot00000000000000s390-tools-2.38.0/qethqoat/Makefile000066400000000000000000000006171502674226300167410ustar00rootroot00000000000000include ../common.mak all: qethqoat libs = $(rootdir)/libutil/libutil.a qethqoat: qethqoat.o $(libs) install: all $(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8 $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 qethqoat $(DESTDIR)$(BINDIR) $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 qethqoat.8 $(DESTDIR)$(MANDIR)/man8 clean: rm -f *.o *~ qethqoat core .PHONY: all install clean s390-tools-2.38.0/qethqoat/qethqoat.8000066400000000000000000000025361502674226300172220ustar00rootroot00000000000000.\" Copyright 2017 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH QETHQOAT 8 "April 2012" "s390-tools" .SH NAME qethqoat \- query the OSA address table .SH SYNOPSIS .B qethqoat .RB [ \-hv ] .br .B qethqoat .RB [ \-r "] [" \-s .IR scope ] .I interface .br .B qethqoat .RB \-f .I file .SH DESCRIPTION Use \fBqethqoat\fP to query the OSA address table and display physical and logical device information. .SH OPTIONS .TP .BR \-v ", " \-\-version Prints the version number of qethqoat and exits. .TP .BR \-h ", " \-\-help Displays the help information for the command. .TP .BR \-r ", " \-\-raw Writes raw data to stdout. .TP .BR \-f ", " \-\-file Reads input from file. .TP .BR \-s ", " \-\-scope Defines the scope of the query. Following scopes are supported. \fB0\fP Query the level of the OSA address table \fB1\fP Interface (default) .SH RETURN CODES qethqoat writes the response to stdout and any error message to stderr. The command completes with one of the following return codes: .TP .BR "0" The qethqoat command ran successfully. .TP .BR "1" An error occurred. .SH EXAMPLE .TP To display physical and logical device information for interface eth0 issue: \fBqethqoat eth0\fP .SH AUTHOR .nf Written by Frank Blaschka .fi s390-tools-2.38.0/qethqoat/qethqoat.c000066400000000000000000000402641502674226300172750ustar00rootroot00000000000000/* * qethqoat - Query the OSA address table and display physical and logical * device information * * Copyright IBM Corp. 2012, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/util_libc.h" #include "lib/zt_common.h" #include "qethqoat.h" static iconv_t l_iconv_ebcdic_ascii; static void hex_dump(char *buf, int len) { int i; for (i = 0; i < len; i++) { if (i && !(i % 16)) printf("\n"); printf("%02x ", *(buf + i)); } printf("\n"); } static int mac_is_zero(__u8 *mac) { return !(mac[0] | mac[1] | mac[2] | mac[3] | mac[4] | mac[5]); } static void ebctoasc(char *in, char *out, size_t size) { size_t size_out = size; size_t size_in = size; size_t rc; rc = iconv(l_iconv_ebcdic_ascii, &in, &size_in, &out, &size_out); if (rc == (size_t) -1) fprintf(stderr, "Code page translation EBCDIC-ASCII failed\n"); } static void print_version() { printf("qethqoat: Tool to query the OSA address table version %s\n", RELEASE_STRING); printf("Copyright IBM Corp. 2012, 2017\n"); } static void print_ip4(struct qeth_qoat_des_ip4 *ip4, int hdr) { struct in_addr ia; ia.s_addr = ip4->ip4_address; if (hdr) printf("\n%-39s %s\n%-39s %s\n", "IPv4 Address:", "IPA Flags:", "-------------", "----------"); printf("%-39s 0x%08x\n", inet_ntoa(ia), ip4->flags); } static void print_ip4mc(struct qeth_qoat_des_ip4mc *ip4mc, int hdr) { struct in_addr ia; ia.s_addr = ip4mc->ip4_mc_address; if (hdr) printf("\n%-39s %s\n%-39s %s\n", "IPv4 Multicast Address:", "MAC Address:", "-----------------------", "------------"); printf("%-39s %02x:%02x:%02x:%02x:%02x:%02x\n", inet_ntoa(ia), ip4mc->ip4_mc_mac[0], ip4mc->ip4_mc_mac[1], ip4mc->ip4_mc_mac[2], ip4mc->ip4_mc_mac[3], ip4mc->ip4_mc_mac[4], ip4mc->ip4_mc_mac[5]); } static void print_ip6(struct qeth_qoat_des_ip6 *ip6, int hdr) { char tmp[128]; struct in6_addr ia; memcpy(&ia.s6_addr, &ip6->ip6_address, 16); inet_ntop(AF_INET6, &ia, tmp, 128); if (hdr) printf("\n%-39s %s\n%-39s %s\n", "IPv6 Address:", "IPA Flags:", "-------------", "----------"); printf("%-39s 0x%08x\n", tmp, ip6->flags); } static void print_ip6mc(struct qeth_qoat_des_ip6mc *ip6mc, int hdr) { char tmp[128]; struct in6_addr ia; memcpy(&ia.s6_addr, &ip6mc->ip6_mc_address, 16); inet_ntop(AF_INET6, &ia, tmp, 128); if (hdr) printf("\n%-39s %s\n%-39s %s\n", "IPv6 Multicast Address:", "MAC Address:", "-----------------------", "------------"); printf("%-39s %02x:%02x:%02x:%02x:%02x:%02x\n", tmp, ip6mc->ip6_mc_mac[0], ip6mc->ip6_mc_mac[1], ip6mc->ip6_mc_mac[2], ip6mc->ip6_mc_mac[3], ip6mc->ip6_mc_mac[4], ip6mc->ip6_mc_mac[5]); } static void print_vmac(struct qeth_qoat_des_vmac *vmac, int hdr) { if (hdr) printf("\nvmac\n----\n"); printf("%02x:%02x:%02x:%02x:%02x:%02x\n", vmac->vmac[0], vmac->vmac[1], vmac->vmac[2], vmac->vmac[3], vmac->vmac[4], vmac->vmac[5]); } static void print_vlan(struct qeth_qoat_des_vlan *vlan, int hdr) { if (hdr) printf("\nvlan\n----\n"); printf("%d\n", vlan->vlanid); } static void print_gmac(struct qeth_qoat_des_gmac *gmac, int hdr) { if (hdr) printf("\ngmac\n----\n"); printf("%02x:%02x:%02x:%02x:%02x:%02x\n", gmac->gmac[0], gmac->gmac[1], gmac->gmac[2], gmac->gmac[3], gmac->gmac[4], gmac->gmac[5]); } static void print_aiq(struct qeth_qoat_des_aiq *aiq, int hdr) { if (hdr) printf("\naiq routing variables\n---------------------\n"); printf("0x%x %d %d\n", aiq->protocol, aiq->src_port, aiq->des_port); } static void print_physical(struct qeth_qoat_physical *phdr) { char *speed, *media, *jumbo, *osagen, *chpid_type; char tmp[128]; printf("PCHID: 0x%04x\n", phdr->pchid); printf("CHPID: 0x%02x\n", phdr->chpid); printf("Manufacturer MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n", phdr->physical_mac[0], phdr->physical_mac[1], phdr->physical_mac[2], phdr->physical_mac[3], phdr->physical_mac[4], phdr->physical_mac[5]); printf("Configured MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n", phdr->logical_mac[0], phdr->logical_mac[1], phdr->logical_mac[2], phdr->logical_mac[3], phdr->logical_mac[4], phdr->logical_mac[5]); printf("Data device sub-channel address: 0x%04x\n", phdr->data_sub_channel); printf("CULA: 0x%02x\n", phdr->cula); printf("Unit address: 0x%02x\n", phdr->unit_address); printf("Physical port number: %d\n", phdr->physical_port); printf("Number of output queues: %d\n", phdr->nr_out_queues); printf("Number of input queues: %d\n", phdr->nr_in_queues); printf("Number of active input queues: %d\n", phdr->nr_active_in_queues); switch (phdr->interface_flags_chpid_type) { case OAT_IFF_CHPID_TYPE_OSD: chpid_type = "OSD"; break; case OAT_IFF_CHPID_TYPE_OSX: chpid_type = "OSX"; break; case OAT_IFF_CHPID_TYPE_OSM: chpid_type = "OSM"; break; default: sprintf(tmp, "unknown (0x%x)", phdr->interface_flags_chpid_type); chpid_type = tmp; } printf("CHPID Type: %s\n", chpid_type); printf("Interface flags: 0x%08x\n", phdr->interface_flags); switch (phdr->osa_gen) { case OAT_OSA_GEN_OSAE3: osagen = "OSA-Express3"; break; case OAT_OSA_GEN_OSAE4S: osagen = "OSA-Express4S"; break; case OAT_OSA_GEN_OSAE5S: osagen = "OSA-Express5S"; break; case OAT_OSA_GEN_OSAE6S: osagen = "OSA-Express6S"; break; case OAT_OSA_GEN_OSAE7S: osagen = "OSA-Express7S"; break; default: sprintf(tmp, "unknown (0x%x)", phdr->osa_gen); osagen = tmp; } printf("OSA Generation: %s\n", osagen); switch (phdr->port_speed) { case OAT_PORT_SPEED_10mbs_half: speed = "10 Mb/s / half duplex"; break; case OAT_PORT_SPEED_10mbs_full: speed = "10 Mb/s / full duplex"; break; case OAT_PORT_SPEED_100mbs_half: speed = "100 Mb/s / half duplex"; break; case OAT_PORT_SPEED_100mbs_full: speed = "100 Mb/s / full duplex"; break; case OAT_PORT_SPEED_1000mbs_half: speed = "1000 Mb/s / half duplex"; break; case OAT_PORT_SPEED_1000mbs_full: speed = "1000 Mb/s / full duplex"; break; case OAT_PORT_SPEED_NA: speed = "NA / NA"; break; case OAT_PORT_SPEED_10gbs_full: speed = "10 Gb/s / full duplex"; break; case OAT_PORT_SPEED_25gbs_full: speed = "25 Gb/s / full duplex"; break; case OAT_PORT_SPEED_UNKNOWN: speed = "unknown / unknown"; break; default: sprintf(tmp, "(0x%x) / (0x%x)", phdr->port_speed, phdr->port_speed); speed = tmp; } printf("Port speed/mode: %s\n", speed); switch (phdr->port_media) { case OAT_PORT_MEDIA_COPPER: media = "copper"; break; case OAT_PORT_MEDIA_MULTI_MODE: media = "multi mode (SR/SX)"; break; case OAT_PORT_MEDIA_SINGLE_MODE: media = "single mode (LR/LX)"; break; default: sprintf(tmp, "unknown (0x%x)", phdr->port_media); media = tmp; } printf("Port media type: %s\n", media); if (phdr->port_media_att & OAT_PORT_MEDIA_ATT_JUMBO) jumbo = "yes"; else jumbo = "no"; printf("Jumbo frames: %s\n", jumbo); printf("Firmware: 0x%08x\n", phdr->firmware); printf("\n"); } static void print_logical(struct qeth_qoat_logical *lhdr) { char prouter[] = "primary"; char srouter[] = "secondary"; char nrouter[] = "no"; char port_name[8]; char *router; if (lhdr->ip4_primary_router) router = prouter; else if (lhdr->ip4_secondary_router) router = srouter; else router = nrouter; printf("IPv4 router: %s %s\n", router, lhdr->ip4_active_router ? "active" : ""); if (lhdr->ip6_primary_router) router = prouter; else if (lhdr->ip6_secondary_router) router = srouter; else router = nrouter; printf("IPv6 router: %s %s\n", router, lhdr->ip6_active_router ? "active" : ""); printf("IPv4 vmac router: %s\n", lhdr->ip4_vmac_router ? "yes" : "no"); printf("IPv6 vmac router: %s\n", lhdr->ip6_vmac_router ? "yes" : "no"); printf("Connection isolation: %s\n", lhdr->isolation_f ? "active" : "not active"); printf("Connection isolation VEPA: %s\n", lhdr->isolation_vepa ? "yes" : "no"); if (lhdr->ip4_global_vlan_active) printf("IPv4 global vlan id: %d\n", lhdr->ip4_global_vlanid); if (lhdr->ip4_vmac_active) printf("IPv4 l3 vmac: %02x:%02x:%02x:%02x:%02x:%02x" " %s generated\n", lhdr->ip4_vmac[0], lhdr->ip4_vmac[1], lhdr->ip4_vmac[2], lhdr->ip4_vmac[3], lhdr->ip4_vmac[4], lhdr->ip4_vmac[4], lhdr->ip4_vmac_source ? "OSA" : "Host"); if (lhdr->ip6_global_vlan_active) printf("IPv6 global vlan id: %d\n", lhdr->ip6_global_vlanid); if (lhdr->ip6_vmac_active) printf("IPv6 l3 vmac: %02x:%02x:%02x:%02x:%02x:%02x" " %s generated\n", lhdr->ip6_vmac[0], lhdr->ip6_vmac[1], lhdr->ip6_vmac[2], lhdr->ip6_vmac[3], lhdr->ip6_vmac[4], lhdr->ip6_vmac[4], lhdr->ip6_vmac_source ? "OSA" : "Host"); if (lhdr->port_name_f) { ebctoasc((char *)lhdr->port_name, port_name, 8); printf("Port name: %.8s\n", port_name); } printf("IPv4 assists enabled: 0x%08x\n", lhdr->ip4_ass_enabled); printf("IPv6 assists enabled: 0x%08x\n", lhdr->ip6_ass_enabled); printf("IPv4 outbound checksum enabled: 0x%08x\n", lhdr->out_csum_enabled); printf("IPv6 outbound checksum enabled: 0x%08x\n", lhdr->out_csum_enabled6); printf("IPv4 inbound checksum enabled: 0x%08x\n", lhdr->in_csum_enabled); printf("IPv6 inbound checksum enabled: 0x%08x\n", lhdr->in_csum_enabled6); if (lhdr->l2_vlanid) printf("L2 vlan id: %d\n", lhdr->l2_vlanid); if (!mac_is_zero(lhdr->l2_vmac)) printf("L2 vmac: %02x:%02x:%02x:%02x:%02x:%02x\n", lhdr->l2_vmac[0], lhdr->l2_vmac[1], lhdr->l2_vmac[2], lhdr->l2_vmac[3], lhdr->l2_vmac[4], lhdr->l2_vmac[5]); } static void parse_descriptor(struct qeth_qoat_hdr *oat_hdr, struct qeth_print_hdr *phdr, char *buf, int *processed) { int i; char *ptr; *processed += oat_hdr->len; for (i = 0; i < oat_hdr->type.descriptor.reply_entry_count; i++) { ptr = buf + *processed; switch (oat_hdr->type.descriptor.des_type) { case OAT_DES_TYPE_IP4: print_ip4((struct qeth_qoat_des_ip4 *)ptr, phdr->ip4_h); phdr->ip4_h = 0; break; case OAT_DES_TYPE_IP4MC: print_ip4mc((struct qeth_qoat_des_ip4mc *)ptr, phdr->ip4mc_h); phdr->ip4mc_h = 0; break; case OAT_DES_TYPE_IP6: print_ip6((struct qeth_qoat_des_ip6 *)ptr, phdr->ip6_h); phdr->ip6_h = 0; break; case OAT_DES_TYPE_IP6MC: print_ip6mc((struct qeth_qoat_des_ip6mc *)ptr, phdr->ip6mc_h); phdr->ip6mc_h = 0; break; case OAT_DES_TYPE_VMAC: print_vmac((struct qeth_qoat_des_vmac *)ptr, phdr->vmac_h); phdr->vmac_h = 0; break; case OAT_DES_TYPE_VLAN: print_vlan((struct qeth_qoat_des_vlan *)ptr, phdr->vlan_h); phdr->vlan_h = 0; break; case OAT_DES_TYPE_GMAC: print_gmac((struct qeth_qoat_des_gmac *)ptr, phdr->gmac_h); phdr->gmac_h = 0; break; case OAT_DES_TYPE_AIQ: print_aiq((struct qeth_qoat_des_aiq *)ptr, phdr->aiq_h); phdr->aiq_h = 0; break; default: printf("Unknown descriptor (0x%x)\n", oat_hdr->type.descriptor.des_type); hex_dump(ptr, oat_hdr->type.descriptor.reply_entry_len); } *processed += oat_hdr->type.descriptor.reply_entry_len; } } static int print_IPA_error(int rc) { switch (rc) { case 0x0: break; case 0x4: fprintf(stderr, "Error: Command not supported\n"); break; case 0x8: fprintf(stderr, "Error: Invalid/unsupported sub_command/scope\n"); break; case 0x10: fprintf(stderr, "Error: No active data connection\n"); break; case 0x14: fprintf(stderr, "Error: OSA temporary resource shortage\n"); break; default: return 1; } return 0; } static void parse_data(char *buf, int len) { int buffer_processed; int frame_processed; struct qeth_qoat_ipa_reply *ipa_hdr; struct qeth_qoat_hdr *oat_hdr; struct qeth_print_hdr phdr = {1, 1, 1, 1, 1, 1, 1, 1}; buffer_processed = 0; do { frame_processed = 0; ipa_hdr = (struct qeth_qoat_ipa_reply *) (buf + buffer_processed); if (print_IPA_error(ipa_hdr->rc)) fprintf(stderr, "OSA reported error code 0x%x\n", ipa_hdr->rc); if (ipa_hdr->subcommand == 0) { printf("Supported Scope mask: 0x%08x\n", ipa_hdr->supported_scope); printf("Supported Descriptor hdr types: 0x%08x\n", ipa_hdr->supported_descriptor); } frame_processed += sizeof(struct qeth_qoat_ipa_reply); if (frame_processed >= ipa_hdr->len) break; do { oat_hdr = (struct qeth_qoat_hdr *) (buf + buffer_processed + frame_processed); switch (oat_hdr->hdr_type) { case OAT_HDR_TYPE_PHYSICAL: print_physical(&oat_hdr->type.physical); frame_processed += oat_hdr->len; break; case OAT_HDR_TYPE_LOGICAL: print_logical(&oat_hdr->type.logical); frame_processed += oat_hdr->len; break; case OAT_HDR_TYPE_DESCRIPTOR: parse_descriptor(oat_hdr, &phdr, buf + buffer_processed, &frame_processed); break; default: printf("Unknown oat hdr (0x%x)\n", oat_hdr->hdr_type); return; } } while (frame_processed < ipa_hdr->len); buffer_processed += ipa_hdr->len; } while (buffer_processed < len); } static void printusage() { fprintf(stdout, "Usage: qethqoat [-h] [-v]\n" " qethqoat [-r] [-s scope] interface\n" " qethqoat -f file\n\n" "Use qethqoat to query the OSA address table and display " "physical and logical\ndevice information\n\n" "-h, --help Displays the help information.\n" "-r, --raw Writes raw data to stdout.\n" "-f, --file Reads input from file.\n" "-v, --version Prints the version number.\n" "-s, --scope Defines the scope of the query.\n" "\t 0 Query the level of the OSA address table\n" "\t 1 Interface (default)\n" ); } static const struct option qethqoat_opts[] = { { "help", 0, 0, 'h'}, { "raw", 0, 0, 'r'}, { "file", 1, 0, 'f'}, { "version", 0, 0, 'v'}, { "scope", 1, 0, 's'}, { 0, 0, 0, 0} }; static const char qethqoat_opts_str[] = "vhrf:s:"; int main(int argc, char **argv) { struct qoat_opts opts; int sd, c, rc, index; struct ifreq ifr; struct qeth_query_oat_data oat_data; size_t datalen = 131072; opts.raw = 0; opts.scope = 1; opts.file = NULL; opts.ifname = NULL; while ((c = getopt_long(argc, argv, qethqoat_opts_str, qethqoat_opts, &index)) != -1) { switch (c) { case 'h': printusage(); return 0; case 'r': opts.raw = 1; break; case 'f': opts.file = optarg; break; case 's': opts.scope = atoi(optarg); break; case 'v': print_version(); return 0; default: printusage(); return 1; } } if (optind == argc) { if (!opts.file) { /* No -f file, interface name needed */ printusage(); return 1; } } else { if (opts.file) { /* Have -f file, no interface name allowed */ printusage(); return 1; } opts.ifname = argv[optind]; if (strlen(opts.ifname) >= IFNAMSIZ) { fprintf(stderr, "qethqoat: Interface name too long\n"); return 1; } } oat_data.command = 0; oat_data.ptr = (__u64)(unsigned long)malloc(datalen); if (!oat_data.ptr) { perror("qethqoat"); return 1; } oat_data.buffer_len = datalen; oat_data.response_len = 0; if (opts.file) { FILE *rf = fopen(opts.file, "r"); if (!rf) { perror("qethqoat"); free((void *)(unsigned long)oat_data.ptr); return 1; } oat_data.response_len = fread( (char *)(unsigned long)oat_data.ptr, sizeof(char), oat_data.buffer_len, rf); fclose(rf); goto parse; } sd = socket(AF_INET, SOCK_STREAM, 0); if (sd < 0) { perror("qethqoat"); free((void *)(unsigned long)oat_data.ptr); return 1; } util_strlcpy(ifr.ifr_name, opts.ifname, IFNAMSIZ); oat_data.command = opts.scope; ifr.ifr_ifru.ifru_data = (void *)&oat_data; rc = ioctl(sd, SIOC_QETH_QUERY_OAT, &ifr); if (rc) { if (print_IPA_error(rc)) perror("qethqoat"); close(sd); free((void *)(unsigned long)oat_data.ptr); return 1; } close(sd); parse: if (opts.raw) { fwrite((char *)(unsigned long)oat_data.ptr, sizeof(char), oat_data.response_len, stdout); } else { l_iconv_ebcdic_ascii = iconv_open("ISO-8859-1", "EBCDIC-US"); if (l_iconv_ebcdic_ascii == (iconv_t) -1) { perror("qethqoat"); free((void *)(unsigned long)oat_data.ptr); return 1; } parse_data((char *)(unsigned long)oat_data.ptr, oat_data.response_len); } free((void *)(unsigned long)oat_data.ptr); return 0; } s390-tools-2.38.0/qethqoat/qethqoat.h000066400000000000000000000117351502674226300173030ustar00rootroot00000000000000/* * qethqoat - Query the OSA address table and display physical and logical * device information * * Copyright IBM Corp. 2012, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef _QETHQOAT_H #define _QETHQOAT_H #include #define SIOC_QETH_QUERY_OAT (SIOCDEVPRIVATE + 7) struct qeth_query_oat_data { __u32 command; /* scope of the query */ __u32 buffer_len; /* length of the buffer */ __u32 response_len; /* length of the response in the buffer */ __u64 ptr; /* pointer to buffer */ }; struct qeth_qoat_ipa_reply { __u16 len; __u8 reserved1[2]; __u32 command; __u16 rc; __u8 frames_total; __u8 frames_seq; __u8 reserved2[4]; __u32 subcommand; __u8 reserved3[4]; __u32 supported_scope; __u32 supported_descriptor; } __attribute__((packed)); struct qeth_qoat_physical { __u16 pchid; __u16 chpid; __u8 physical_mac[6]; __u8 logical_mac[6]; __u16 data_sub_channel; __u8 cula; __u8 unit_address; __u16 physical_port; __u16 nr_out_queues; __u16 nr_in_queues; __u16 nr_active_in_queues; #define OAT_IFF_CHPID_TYPE_OSD 0x0 #define OAT_IFF_CHPID_TYPE_OSX 0x1 #define OAT_IFF_CHPID_TYPE_OSM 0x2 __u32 interface_flags_chpid_type:4; __u32 interface_flags:28; #define OAT_OSA_GEN_OSAE3 0x01 #define OAT_OSA_GEN_OSAE4S 0x02 #define OAT_OSA_GEN_OSAE5S 0x03 #define OAT_OSA_GEN_OSAE6S 0x04 #define OAT_OSA_GEN_OSAE7S 0x05 __u8 osa_gen; #define OAT_PORT_SPEED_UNKNOWN 0x00 #define OAT_PORT_SPEED_10mbs_half 0x01 #define OAT_PORT_SPEED_10mbs_full 0x02 #define OAT_PORT_SPEED_100mbs_half 0x03 #define OAT_PORT_SPEED_100mbs_full 0x04 #define OAT_PORT_SPEED_1000mbs_half 0x05 #define OAT_PORT_SPEED_1000mbs_full 0x06 #define OAT_PORT_SPEED_NA 0x07 #define OAT_PORT_SPEED_10gbs_full 0x08 #define OAT_PORT_SPEED_25gbs_full 0x0A __u8 port_speed; #define OAT_PORT_MEDIA_COPPER 0x01 #define OAT_PORT_MEDIA_MULTI_MODE 0x02 #define OAT_PORT_MEDIA_SINGLE_MODE 0x04 __u8 port_media; #define OAT_PORT_MEDIA_ATT_JUMBO 0x80 __u8 port_media_att; __u32 firmware; __u8 reserved1[24]; } __attribute__((packed)); struct qeth_qoat_logical { __u8 ip4_primary_router:1; __u8 ip4_secondary_router:1; __u8 ip4_active_router:1; __u8 ip6_primary_router:1; __u8 ip6_secondary_router:1; __u8 ip6_active_router:1; __u8 ip4_vmac_router:1; __u8 ip6_vmac_router:1; __u8 ip4_vmac_active:1; __u8 ip4_vmac_source:1; __u8 ip4_global_vlan_active:1; __u8 ip6_vmac_active:1; __u8 ip6_vmac_source:1; __u8 ip6_global_vlan_active:1; __u8 reserved1:2; __u8 port_name_f:1; __u8 isolation_f:1; __u8 isolation_vepa:1; __u8 reserved2:5; __u8 reserved3; __u16 ip4_global_vlanid; __u8 ip4_vmac[6]; __u16 ip6_global_vlanid; __u8 ip6_vmac[6]; __u8 port_name[8]; __u32 ip4_ass_enabled; __u32 ip6_ass_enabled; __u32 out_csum_enabled; __u32 out_csum_enabled6; __u32 in_csum_enabled; __u32 in_csum_enabled6; __u32 reserved4; __u16 l2_vlanid; __u8 l2_vmac[6]; __u16 nr_des; __u8 reserved5[14]; } __attribute__((packed)); struct qeth_qoat_des_ip4 { __u32 ip4_address; __u32 flags; } __attribute__((packed)); struct qeth_qoat_des_ip4mc { __u32 ip4_mc_address; __u8 ip4_mc_mac[6]; __u8 reserved[6]; } __attribute__((packed)); struct qeth_qoat_des_ip6 { __u8 ip6_address[16]; __u32 flags; __u8 reserved[4]; } __attribute__((packed)); struct qeth_qoat_des_ip6mc { __u8 ip6_mc_address[16]; __u8 ip6_mc_mac[6]; __u8 reserved[2]; } __attribute__((packed)); struct qeth_qoat_des_vmac { __u8 vmac[6]; __u8 reserved[2]; } __attribute__((packed)); struct qeth_qoat_des_vlan { __u16 vlanid; __u8 reserved[2]; } __attribute__((packed)); struct qeth_qoat_des_gmac { __u8 gmac[6]; __u8 reserved[2]; } __attribute__((packed)); struct qeth_qoat_des_aiq { __u32 protocol; __u8 src_address[16]; __u8 des_address[16]; __u16 src_port; __u16 des_port; } __attribute__((packed)); struct qeth_qoat_descriptor { #define OAT_DES_TYPE_IP4 0x00000001 #define OAT_DES_TYPE_IP4MC 0x00000002 #define OAT_DES_TYPE_IP6 0x00000004 #define OAT_DES_TYPE_IP6MC 0x00000008 #define OAT_DES_TYPE_VMAC 0x00000100 #define OAT_DES_TYPE_VLAN 0x00000200 #define OAT_DES_TYPE_GMAC 0x00000400 #define OAT_DES_TYPE_AIQ 0x00010000 __u32 des_type; __u32 rv_type; __u16 rv_version; __u16 qid; __u32 reply_entry_len; __u16 reply_entry_version; __u16 reply_entry_count; __u16 dh; __u8 reserved[10]; } __attribute__((packed)); struct qeth_qoat_hdr { #define OAT_HDR_TYPE_PHYSICAL 0x0004 #define OAT_HDR_TYPE_LOGICAL 0x0008 #define OAT_HDR_TYPE_DESCRIPTOR 0x0010 __u16 hdr_type; __u16 len; __u16 version; __u8 reserved1[6]; __u32 ec; union { struct qeth_qoat_physical physical; struct qeth_qoat_logical logical; struct qeth_qoat_descriptor descriptor; } type; } __attribute__((packed)); struct qeth_print_hdr { int ip4_h; int ip4mc_h; int ip6_h; int ip6mc_h; int vmac_h; int vlan_h; int gmac_h; int aiq_h; }; struct qoat_opts { int raw; char *ifname; int scope; char *file; }; #endif s390-tools-2.38.0/rust/000077500000000000000000000000001502674226300144445ustar00rootroot00000000000000s390-tools-2.38.0/rust/.gitignore000066400000000000000000000003411502674226300164320ustar00rootroot00000000000000# Generated by Cargo # will have compiled files and executables debug/ target/ # These are backup files generated by rustfmt *.rs.bk # Generated during make build can be removed at any point .check-dep-pvtools .check-cargo s390-tools-2.38.0/rust/Cargo.lock000066400000000000000000000725411502674226300163620ustar00rootroot00000000000000# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "anstream" version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", "windows-sys 0.59.0", ] [[package]] name = "anyhow" version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] name = "autocfg" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bit-set" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" dependencies = [ "bit-vec", ] [[package]] name = "bit-vec" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitflags" version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be" [[package]] name = "bitvec" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ "funty", "radium", "tap", "wyz", ] [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" version = "1.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8293772165d9345bdaaa39b45b2109591e63fe5e6fbc23c6ff930a048aa310b" dependencies = [ "shlex", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" version = "4.5.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" dependencies = [ "clap_builder", "clap_derive", ] [[package]] name = "clap_builder" version = "4.5.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" dependencies = [ "anstream", "anstyle", "clap_lex", "strsim", "terminal_size", ] [[package]] name = "clap_complete" version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9647a559c112175f17cf724dc72d3645680a883c58481332779192b0d8e7a01" dependencies = [ "clap", ] [[package]] name = "clap_derive" version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ "heck", "proc-macro2", "quote", "syn", ] [[package]] name = "clap_lex" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "colorchoice" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "cpacfinfo" version = "0.12.0" dependencies = [ "anyhow", "cc", "clap", "lazy_static", "libc", "serde", "serde_json", "utils", "zerocopy", ] [[package]] name = "curl" version = "0.4.47" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9fb4d13a1be2b58f14d60adba57c9834b78c62fd86c3e76a148f732686e9265" dependencies = [ "curl-sys", "libc", "openssl-probe", "openssl-sys", "schannel", "socket2", "windows-sys 0.52.0", ] [[package]] name = "curl-sys" version = "0.4.78+curl-8.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eec768341c5c7789611ae51cf6c459099f22e64a5d5d0ce4892434e33821eaf" dependencies = [ "cc", "libc", "libz-sys", "openssl-sys", "pkg-config", "vcpkg", "windows-sys 0.52.0", ] [[package]] name = "darling" version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ "darling_core", "darling_macro", ] [[package]] name = "darling_core" version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", "syn", ] [[package]] name = "darling_macro" version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", "syn", ] [[package]] name = "deku" version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9711031e209dc1306d66985363b4397d4c7b911597580340b93c9729b55f6eb" dependencies = [ "bitvec", "deku_derive", "no_std_io2", "rustversion", ] [[package]] name = "deku_derive" version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58cb0719583cbe4e81fb40434ace2f0d22ccc3e39a74bb3796c22b451b4f139d" dependencies = [ "darling", "proc-macro-crate", "proc-macro2", "quote", "syn", ] [[package]] name = "enum_dispatch" version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" dependencies = [ "once_cell", "proc-macro2", "quote", "syn", ] [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", "windows-sys 0.59.0", ] [[package]] name = "fastrand" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foreign-types" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ "foreign-types-shared", ] [[package]] name = "foreign-types-shared" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "funty" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "getrandom" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] name = "getrandom" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" dependencies = [ "cfg-if", "libc", "r-efi", "wasi 0.14.2+wasi-0.2.4", ] [[package]] name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "ident_case" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "indexmap" version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", ] [[package]] name = "indexmap" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", "hashbrown 0.15.2", ] [[package]] name = "is_terminal_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itoa" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libz-sys" version = "1.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" dependencies = [ "cc", "libc", "pkg-config", "vcpkg", ] [[package]] name = "linux-raw-sys" version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "log" version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "no_std_io2" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a3564ce7035b1e4778d8cb6cacebb5d766b5e8fe5a75b9e441e33fb61a872c6" dependencies = [ "memchr", ] [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "once_cell" version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "openssl" version = "0.10.70" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6" dependencies = [ "bitflags", "cfg-if", "foreign-types", "libc", "once_cell", "openssl-macros", "openssl-sys", ] [[package]] name = "openssl-macros" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "openssl-probe" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" version = "0.9.105" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc" dependencies = [ "cc", "libc", "pkg-config", "vcpkg", ] [[package]] name = "pkg-config" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "ppv-lite86" version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ "zerocopy", ] [[package]] name = "proc-macro-crate" version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ "toml_edit", ] [[package]] name = "proc-macro2" version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] [[package]] name = "proptest" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50" dependencies = [ "bit-set", "bit-vec", "bitflags", "lazy_static", "num-traits", "rand 0.8.5", "rand_chacha 0.3.1", "rand_xorshift", "regex-syntax", "rusty-fork", "tempfile", "unarray", ] [[package]] name = "pvapconfig" version = "0.12.0" dependencies = [ "clap", "clap_complete", "lazy_static", "openssl", "rand 0.9.1", "regex", "s390_pv_core", "serde", "serde_yaml", "utils", ] [[package]] name = "pvattest" version = "0.12.0" dependencies = [ "anyhow", "base64", "byteorder", "clap", "clap_complete", "curl", "log", "openssl", "s390_pv", "serde", "serde_json", "serde_yaml", "utils", "zerocopy", ] [[package]] name = "pvimg" version = "0.12.0" dependencies = [ "anyhow", "clap", "clap_complete", "deku", "deku_derive", "enum_dispatch", "log", "openssl", "proptest", "s390_pv", "serde", "serde_json", "thiserror", "utils", ] [[package]] name = "pvsecret" version = "0.12.0" dependencies = [ "anyhow", "clap", "clap_complete", "log", "s390_pv", "serde_yaml", "utils", ] [[package]] name = "quick-error" version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" [[package]] name = "radium" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha 0.3.1", "rand_core 0.6.4", ] [[package]] name = "rand" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", ] [[package]] name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", "rand_core 0.6.4", ] [[package]] name = "rand_chacha" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", "rand_core 0.9.3", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom 0.2.16", ] [[package]] name = "rand_core" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ "getrandom 0.3.2", ] [[package]] name = "rand_xorshift" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" dependencies = [ "rand_core 0.6.4", ] [[package]] name = "regex" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rustix" version = "0.38.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", "windows-sys 0.59.0", ] [[package]] name = "rustversion" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" [[package]] name = "rusty-fork" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" dependencies = [ "fnv", "quick-error", "tempfile", "wait-timeout", ] [[package]] name = "ryu" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "s390_pv" version = "0.12.0" dependencies = [ "byteorder", "curl", "enum_dispatch", "foreign-types", "log", "openssl", "openssl-sys", "s390_pv_core", "serde", "serde_test", "thiserror", "zerocopy", ] [[package]] name = "s390_pv_core" version = "0.12.0" dependencies = [ "byteorder", "lazy_static", "libc", "log", "regex", "serde", "serde_test", "thiserror", "zerocopy", ] [[package]] name = "schannel" version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ "windows-sys 0.59.0", ] [[package]] name = "serde" version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "serde_test" version = "1.0.177" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f901ee573cab6b3060453d2d5f0bae4e6d628c23c0a962ff9b5f1d7c8d4f1ed" dependencies = [ "serde", ] [[package]] name = "serde_yaml" version = "0.9.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9d684e3ec7de3bf5466b32bd75303ac16f0736426e5a4e0d6e489559ce1249c" dependencies = [ "indexmap 1.9.3", "itoa", "ryu", "serde", "unsafe-libyaml", ] [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "socket2" version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", ] [[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tap" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" dependencies = [ "cfg-if", "fastrand", "getrandom 0.2.16", "once_cell", "rustix", "windows-sys 0.59.0", ] [[package]] name = "terminal_size" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef" dependencies = [ "rustix", "windows-sys 0.59.0", ] [[package]] name = "thiserror" version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "toml_datetime" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap 2.6.0", "toml_datetime", "winnow", ] [[package]] name = "unarray" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unsafe-libyaml" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1865806a559042e51ab5414598446a5871b561d21b6764f2eabb0dd481d880a6" [[package]] name = "utf8parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "utils" version = "0.12.0" dependencies = [ "clap", "libc", "log", "s390_pv", "serde", ] [[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "wait-timeout" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" dependencies = [ "libc", ] [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi" version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" dependencies = [ "wit-bindgen-rt", ] [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets", ] [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] [[package]] name = "wit-bindgen-rt" version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ "bitflags", ] [[package]] name = "wyz" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" dependencies = [ "tap", ] [[package]] name = "zerocopy" version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" dependencies = [ "proc-macro2", "quote", "syn", ] s390-tools-2.38.0/rust/Cargo.toml000066400000000000000000000010001502674226300163630ustar00rootroot00000000000000[workspace] members = [ "cpacfinfo", "pv", "pv_core", "pvapconfig", "pvattest", "pvimg", "pvsecret", "utils", ] resolver = "2" [workspace.package] edition = "2021" license = "MIT" rust-version = "1.75.0" [workspace.lints.rust] missing_docs = { level = "deny", priority = 1 } missing_debug_implementations = "warn" non_ascii_idents = "warn" nonstandard-style = "warn" trivial_numeric_casts = "warn" unstable_features = "warn" unused = "warn" unused_import_braces = "warn" unused_qualifications = "warn" s390-tools-2.38.0/rust/Makefile000066400000000000000000000124061502674226300161070ustar00rootroot00000000000000include ../common.mak HAVE_CARGO ?= 1 HAVE_OPENSSL ?= 1 HAVE_LIBCURL ?= 1 INSTALL_TARGETS := skip-build BUILD_TARGETS := skip-build PV_BUILD_TARGETS := skip-pv-build CARGO_TARGETS := PV_TARGETS := CARGO_TEST_TARGETS := ifneq (${HAVE_CARGO},0) CARGO_TARGETS := BUILD_TARGETS = $(CARGO_TARGETS) INSTALL_TARGETS := install-rust-tools install-man install-shell-completions CARGO_TEST_TARGETS = $(addsuffix .test, $(CARGO_TARGETS)) ifeq ($(HOST_ARCH),s390x) CARGO_TARGETS += cpacfinfo else BUILD_TARGETS += skip-cpacfinfo endif #HOSTARCH ifneq (${HAVE_OPENSSL},0) ifneq (${HAVE_LIBCURL},0) PV_TARGETS := pvsecret pvattest pvimg ifeq ($(HOST_ARCH),s390x) PV_TARGETS += pvapconfig else BUILD_TARGETS += skip-pvapconfig endif #HOSTARCH PV_BUILD_TARGETS := $(PV_TARGETS) CARGO_TEST_TARGETS += $(addsuffix .test,pv $(PV_TARGETS)) endif #LIBCURL endif #OPENSSL TEST_TARGETS := $(addsuffix _build,$(CARGO_TEST_TARGETS)) endif #CARGO pvimg-bootloaders: $(MAKE) -C pvimg/boot/ .PHONY: pvimg-bootloaders PVIMG_PKGDATADIR := $(TOOLS_DATADIR)/pvimg export PVIMG_PKGDATADIR BUILD_TARGETS += $(PV_BUILD_TARGETS) pvimg-bootloaders INSTALL_TARGETS += pvimg-bootloaders # build release targets by default ifeq ("${D}","0") ALL_CARGOFLAGS += --release endif # the cc crate uses these variables to compile c code. It does not open a shell # to call the compiler, so no echo etc. allowed here, just a path to a program $(BUILD_TARGETS) $(TEST_TARGETS) rust-test: CC = $(CC_SILENT) $(BUILD_TARGETS) $(TEST_TARGETS) rust-test: AR = $(AR_SILENT) $(PV_TARGETS): .check-dep-pvtools $(PV_TARGETS) $(CARGO_TARGETS): .check-cargo .no-cross-compile $(CARGO_BUILD) --bin $@ $(ALL_CARGOFLAGS) .PHONY: $(PV_TARGETS) $(CARGO_TARGETS) $(TEST_TARGETS): ALL_CARGOFLAGS += --no-run $(CARGO_TEST_TARGETS) $(TEST_TARGETS): .check-cargo .no-cross-compile $(CARGO_TEST) --package $(basename $@) --all-features $(ALL_CARGOFLAGS) .PHONY: $(TEST_TARGETS) $(CARGO_TEST_TARGETS) skip-build: echo " SKIP rust-tools due to unresolved dependencies" skip-pv-build: echo " SKIP rust-pv-tools due to unresolved dependencies" skip-pvapconfig: echo " SKIP pvapconfig due to unsupported architecture (s390x only)" skip-cpacfinfo: echo " SKIP cpacfinfo due to unsupported architecture (s390x only)" all: $(BUILD_TARGETS) install: $(INSTALL_TARGETS) $(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR) $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 pvattest/tools/pvextract-hdr $(DESTDIR)$(USRBINDIR) $(INSTALL) -d -m 755 $(DESTDIR)$(PVIMG_PKGDATADIR) $(MAKE) -C pvimg/boot install ln -sf pvimg $(DESTDIR)$(USRBINDIR)/genprotimg print-rust-targets: echo $(BUILD_TARGETS) clean: ifneq (${HAVE_CARGO},0) $(CARGO_CLEAN) ${ALL_CARGOFLAGS} endif # CARGO $(MAKE) -C pvimg/boot/ clean $(RM) -- .check-dep-pvtools .detect-openssl.dep.c .check-cargo rust-test: $(CARGO_TEST_TARGETS) install-rust-tools: $(BUILD_TARGETS) $(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR) $(foreach target,$(CARGO_TARGETS),\ $(INSTALL) target/release/$(target) $(DESTDIR)$(USRBINDIR);) $(foreach target,$(PV_TARGETS),\ $(INSTALL) target/release/$(target) $(DESTDIR)$(USRBINDIR);) install-man: $(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man1 $(foreach target,$(CARGO_TARGETS),\ $(INSTALL) -m 644 $(target)/man/*.1 -t $(DESTDIR)$(MANDIR)/man1;) $(foreach target,$(PV_TARGETS),\ $(INSTALL) -m 644 $(target)/man/*.1 -t $(DESTDIR)$(MANDIR)/man1;) ln -sf pvimg-create.1 $(DESTDIR)$(MANDIR)/man1/genprotimg.1 install-shell-completions: install-bash-completion install-zsh-completion install-bash-completion: $(PV_TARGETS) $(INSTALL) -d -m 755 $(DESTDIR)$(BASHCOMPLETIONDIR) $(foreach target,$(PV_TARGETS),\ $(INSTALL) -m 0644 --preserve-timestamps -- $(shell find $(shell ls --sort=time --reverse --directory target/release/build/$(target)-*/out/ |tail -n1) -name '*.bash') $(DESTDIR)$(BASHCOMPLETIONDIR);) install-zsh-completion: $(PV_TARGETS) $(INSTALL) -d -m 755 $(DESTDIR)$(ZSHCOMPLETIONDIR) $(foreach target,$(PV_TARGETS),\ $(INSTALL) -m 0644 --preserve-timestamps -- $(shell find $(shell ls --sort=time --reverse --directory target/release/build/$(target)-*/out/ |tail -n1) -regex '.*/_[a-zA-Z0-9]+') $(DESTDIR)$(ZSHCOMPLETIONDIR);) .PHONY: all install clean skip-build install-rust-tools print-rust-targets install-man rust-test install-bash-completion install-zsh-completion install-shell-completions .check-cargo: ifeq ($(shell command -v $(CARGO)),) $(call check_dep, \ "rust/cargo", \ "invalid-incl", \ "cargo", \ "HAVE_CARGO=0") endif touch $@ .no-cross-compile: ifneq ($(HOST_ARCH), $(BUILD_ARCH)) $(error Cross compiling is not supported for rust code. Specify HAVE_CARGO=0 to disable rust compilation) endif .PHONY: .no-cross-compile .detect-openssl.dep.c: echo "#include " > $@ echo "#if OPENSSL_VERSION_NUMBER < 0x10101000L" >> $@ echo " #error openssl version 1.1.1 is required" >> $@ echo "#endif" >> $@ echo "static void __attribute__((unused)) test(void) {" >> $@ echo " EVP_MD_CTX *ctx = EVP_MD_CTX_new();" >> $@ echo " EVP_MD_CTX_free(ctx);" >> $@ echo "}" >> $@ .check-dep-pvtools: .detect-openssl.dep.c $(call check_dep, \ "Rust-pv", \ $^, \ "openssl-devel / libssl-dev version >= 1.1.1", \ "HAVE_OPENSSL=0", \ "-I.") $(call check_dep, \ "Rust-pv", \ "curl/curl.h", \ "libcurl-devel", \ "HAVE_LIBCURL=0") touch $@ s390-tools-2.38.0/rust/README.md000066400000000000000000000131431502674226300157250ustar00rootroot00000000000000# s390-tools tools written in rust ## Setting up rust development and build environment Please refer to the official documentation to set up a working rust environment: https://www.rust-lang.org/learn/get-started The minimum supported Rust version (MSRV) is 1.75. ## Building rust code ### s390-tools build system If `cargo` is installed a simple `make` should do the job. Note that, compiling rust programs take significantly longer than C code. To closely monitor the progress use `make V=1` By default release builds are made. With `make CARGOFLAGS=` one can pass additional flags to cargo. With `make HAVE_CARGO=0` one can turn of any compilation that requires cargo. With `make CARGO=<...>` one can set the cargo binary ### cargo If you need to run cargo directly, `cd` to each project you want to build and issue your cargo commands. Do **NOT** forget to specify `--release` if you are building tools for a release. The s390-tools expect the environment variable `S390_TOOLS_RELEASE` to be present at build time. This is the version string the rust tools provide. Tip: You can use `make version` to get the version string. ## Internal Libraries * __utils__ _Library for rust tools that bundles common stuff for the 390-tools_ * provides a macro to get the `S390_TOOLS_RELEASE` string * provides macros for compile time assertions * __pv_core__ _Library for pv tools, providing uvdevice access and utilities to send, receive and interpret various UV-calls._ * __pv__ _Library for pv tools, providing uvdevice access, encryption utilities, and utilities for generating UV-request_ * requires openssl and libcurl * reexports ann symbols from __pv_core__ * if no encryption utilities required, use __pv_core__ ## Writing new tools We encourage to use Rust for new tools. However, for some use cases it makes sense to use C and C is still allowed to be used for a new tool/library. Exiting tools may be rewritten in Rust. ### What (third-party) crates can be used for s390-tools? A huge list of libraries are made available through Rusts' ecosystem and is one of many upsides. However, just like with Coding Style Guidelines, it is important to limit the usage of those libraries so that within a project, everyone is on the same page and that code written in Rust uses similar approaches. It makes it easier for code review and maintainability in general. The following list of crates should cover a wide variety of use cases. This list is a start, but can change over time. * [anyhow](https://crates.io/crates/anyhow) * Flexible concrete Error type built on std::error::Error * [base64](https://crates.io/crates/base64) * Encodes and decodes base64 as bytes or utf8 * [byteorder](https://crates.io/crates/byteorder) * Library for reading/writing numbers in big-endian and little-endian. * [cfg-if](https://crates.io/crates/cfg-if) * A macro to ergonomically define an item depending on a large number of #[cfg] parameters. Structured like an if-else chain, the first matching branch is the item that gets emitted. * [clap](https://crates.io/crates/clap) * A simple to use, efficient, and full-featured Command Line Argument Parser * [curl](https://crates.io/crates/curl) * Rust bindings to libcurl for making HTTP requests * [deku](https://crates.io/crates/deku) * Bit level serialization/deserialization proc-macro for structs * [libc](https://crates.io/crates/libc) * Raw FFI bindings to platform libraries like libc. * [log](https://crates.io/crates/log) * A lightweight logging facade for Rust * [openssl](https://crates.io/crates/openssl) * OpenSSL bindings * [serde](https://crates.io/crates/serde) * A generic serialization/deserialization framework * [serde_jsonl](https://crates.io/crates/serde_json) * A JSON serialization file format * [serde_yaml](https://crates.io/crates/serde_yaml) * YAML data format for Serde * [thiserror](https://crates.io/crates/thiserror) * derive(Error) * [zerocopy](https://crates.io/crates/zerocopy) * Utilities for zero-copy parsing and serialization Dependencies used by the crates listed above can be used, too. ### Add new tool To add a new tool issue `cargo new $TOOLNAME` in the `rust` directory. Add the tool to the _s390-tools_ build system: ```Makefile CARGO_TARGETS := $TOOLNAME ``` Add the library to the _s390-tools_ test list: ```Makefile CARGO_TEST_TARGETS := $LIBNAME ``` Add the tool/library to the cargo workspace: ```toml [workspace] members = [ "pv", "pvsecret", "$TOOLNAME", "$LIBNAME" "utils", ] ``` ### Versions Do not communicate the version defined in the `toml` file by default. Use `release_string` from the `rust/utils` crate instead: ```rust use utils::release_string; fn print_version() { println!( "{} version {}\nCopyright IBM Corp. 2023", env!("CARGO_PKG_NAME"), // collapses into the crates name release_string!() // this (very likely) collapses into a compile time constant ); } ``` ### Unsafe rust rust allows you to write unsafe rust. Try to avoid it, it can make rust _unsafe_. If you need to, e.g. interacting with other languages like C, keep the `unsafe` block as small as possible and add a reasoning using `// SAFETY: `why this code is safe. Example: ```rust // Get the raw pointer and do an ioctl. // // SAFETY: the passed pointer points to a valid memory region that // contains the expected C-struct. The struct outlives this function. unsafe { let ptr: *mut ffi::uvio_ioctl_cb = cb as *mut _; rc = ioctl(raw_fd, cmd, ptr); } ``` ### Coding style Make `cargo fmt` and `cargo clippy` happy! ### Testing Prefer writing tests using rustdoc. Use explicit rust tests for more edge case tests. s390-tools-2.38.0/rust/cpacfinfo/000077500000000000000000000000001502674226300163745ustar00rootroot00000000000000s390-tools-2.38.0/rust/cpacfinfo/Cargo.toml000066400000000000000000000007431502674226300203300ustar00rootroot00000000000000[package] name = "cpacfinfo" version = "0.12.0" edition.workspace = true license.workspace = true rust-version.workspace = true [dependencies] anyhow = "1.0.95" clap = { version = "4.5", features = ["derive"] } libc = "0.2" serde = { version = "1.0.217", features = ["derive"] } serde_json = "1.0" utils = { path = "../utils" } zerocopy = { version="0.8", features = ["derive"] } [build-dependencies] cc = "1.2" clap = { version ="4.5", features = ["derive"]} lazy_static = "1.5" s390-tools-2.38.0/rust/cpacfinfo/build.rs000066400000000000000000000006631502674226300200460ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 fn main() { #[cfg(target_arch = "s390x")] cc::Build::new().file("src/stfle.c").compile("stfle"); #[cfg(not(target_arch = "s390x"))] { println!("cargo:warning=cpacfinfo will have no functionality on non s390x architectures!"); cc::Build::new().file("src/noop.c").compile("stfle"); } println!("cargo:rerun-if-changed=src/stfle.c") } s390-tools-2.38.0/rust/cpacfinfo/man/000077500000000000000000000000001502674226300171475ustar00rootroot00000000000000s390-tools-2.38.0/rust/cpacfinfo/man/cpacfinfo.1000066400000000000000000000121111502674226300211550ustar00rootroot00000000000000.\" Copyright 2024, 2024 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH CPACFINFO 1 "AUG 2024" "s390-tools" .SH NAME .B cpacfinfo \- tool to provide information about CPACF .SH SYNOPSIS .B cpacfinfo [OPTIONS] [,...] [--format ] .SH DESCRIPTION .B cpacfinfo is able to provide information about the MSA levels supported by the system as well as which CPACF instructions and functions are available. .SH OPTIONS .TP 8 .B \-m/\-\-msa Displays which MSA levels are enabled and how many functions of the ones introduced by this level are available. Can be combined with .B \-f/\-\-functions to list all functions under the corresponding MSA level. Can be combined with .B \-i/\-\-instructions to provide only specific instructions. Can be combined with .B \-a/\-\-available -n/\-\-not-available to filter the displayed function. Ignores option .B \-q/\-\-quiet. Can be combined with .B \-\-format to convert to JSON format. .TP 8 .B \-f/\-\-functions Displays subfunctions of CPACF instructions. Can be combined with .B \-a/\-\-available and .B \-n/\-\-not-available to filter what functions are displayed. By default .B \-f/\-\-functions will only provide information about available functions. Therefore .B cpacfinfo \-f/\-\-functions and .B cpacfinfo \-f/\-\-functions \-\-available show the same output. Use options .B \-f/\-\-functions \-a/\-\-available \-n/\-\-not-available together to show information about every function known by the tool. Additionally .B cpacfinfo shows available functions not known to the tool as "UNKNOWN". .TP 8 .B \-i/\-\-instructions ,... Restricts output of CPACF instructions to be listed. Multiple instructions can be supplied separated by "," to only show the supplied instructions in the output. Other options like .B \-\-quiet \-f/\-\-functions \-m/\-\-msa or filters like .B \-a/\-\-available \-n/\-\-not-available can still be used to further limit output. .nr PI 2n Possible values for .B : .RS .IP \[bu] 2 .B km : introduced with MSA .IP \[bu] 2 .B kmc : introduced with MSA .IP \[bu] 2 .B kimd : introduced with MSA .IP \[bu] 2 .B klmd : introduced with MSA .IP \[bu] 2 .B kmac : introduced with MSA .IP \[bu] 2 .B pckmo : introduced with MSA 3 .IP \[bu] 2 .B kmf : introduced with MSA 4 .IP \[bu] 2 .B kmctr : introduced with MSA 4 .IP \[bu] 2 .B kmo : introduced with MSA 4 .IP \[bu] 2 .B pcc : introduced with MSA 4 .IP \[bu] 2 .B prno : introduced with MSA 5 .IP \[bu] 2 .B kma : introduced with MSA 8 .IP \[bu] 2 .B kdsa : introduced with MSA 9 .RE .TP 8 .B \-a/\-\-available Displays the CPACF subfunctions available on the system. .B cpacfinfo \-f/\-\-functions is the same as \fBcpacfinfo \-f/\-\-functions \-a/\-\-available\fR. If .B -n/\-\-not-available is specified no available functions are shown. To get available as well as functions not available use .B cpacfinfo \-f/\-\-functions \-a/\-\-available \-n/\-\-not-available. .TP 8 .B \-n/\-\-not-available Displays the CPACF subfunctions known by the tool that are not-available on the system. Depending on the Hardware not all MSA levels might be available which can be checked with the .B \-m/\-\-msa option. In such cases it might not be obvious which functions of which instructions are available. This option together with the .B \-a/\-\-available option shows all possible functions. .TP 8 .B \-q/\-\-quiet Suppresses the output of the Query Authentication Information. By default .B cpacfinfo outputs the Query Authentication Information for every Instruction. To keep outputs of other options clean and minimal this can be disabled with this option. On machines that do not provide /sys/devices/system/cpu/cpacf/ output will always look like this option is supplied. .TP 8 .B \-\-format Format output in the specified format. [default: 'human'] If option .B \-m/\-\-msa is specified the JSON output will equal to .B cpacfinfo -m without any other specified options. If .B \-m/\-\-msa is NOT specified the JSON output will equal to .B cpacfinfo -f without any other specified options. .nr PI 2n Possible values for .B : .RS .IP \[bu] 2 .B human : Use human readable format. .IP \[bu] 2 .B json : Use JSON format. .RE .TP 8 .B \-v/\-\-version Print version information and exit. .TP 8 .B \-h/\-\-help Print help (see a summary with \-h') .SH Query Authentication Information The Query Authentication Information is available since MSA 13 and if it is available .B cpacfinfo will display it by default. Query Authentication Information is available for every CPACF instruction and contains the following: .nr PI 2n .RS .IP \[bu] 2 .B Format : This format specifies how the binary blob read from sysfs is to be parsed and says nothing about the Query Authentication Information itself. .IP \[bu] 2 .B Hash length : The hash length specifies the length of the hash in bytes. .IP \[bu] 2 .B IFCL version : The IFCL version specifies the instruction's firmware code level. .IP \[bu] 2 .B Hash : The hash of the firmware code level of the corresponding instruction. .RE s390-tools-2.38.0/rust/cpacfinfo/src/000077500000000000000000000000001502674226300171635ustar00rootroot00000000000000s390-tools-2.38.0/rust/cpacfinfo/src/cli.rs000066400000000000000000000045241502674226300203050ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use clap::Parser; use crate::msa::InstructionKind; #[derive(Clone, PartialEq, clap::ValueEnum, Default)] pub enum Format { /// Human-focused, non-parsable output format #[default] Human, /// Use JSON format Json, } /// Command line interface to get information about CP Assist for Cryptographic Functions (CPACF) #[derive(Parser)] pub struct Cli { /// Print version information and exit #[arg(short, long, exclusive(true))] pub version: bool, /// Provide information about the Message Security Assist (MSA) /// /// Shows which MSA levels are available and how many functions of the ones introduced by /// this level are available. /// Compatible with option -f/--functions to list all functions under the corresponding MSA /// level. #[arg(short, long, conflicts_with("quiet"))] pub msa: bool, /// Shows available functions sorted by instructions /// /// Provides information about the subfunctions of an instruction. /// Functions not known to cpacfinfo are displayed as "UNKNOWN". #[arg(short, long)] pub functions: bool, /// Filter instructions to provide in output /// /// Multiple instructions can be supplied separated by "," to only show the supllied /// instructions in the output. #[arg(short, long, num_args = 1.., value_delimiter = ',')] pub instructions: Vec, /// Shows available functions /// /// Adds available functions to subfunction output. #[arg(short, long)] pub available: bool, /// Shows functions that are not available /// /// Adds non-available functions to subfunction output. #[arg(short, long = "not-available")] pub not_available: bool, /// Suppresses the Query Authentication Information output of Instructions /// /// By default cpacfinfo outputs the Query Authentication Information for every Instruction. /// To keep outputs of other options clean and minimal this can be disabled with this /// option. #[arg(short, long)] pub quiet: bool, /// Converts human readable output to JSON format /// /// Default is human to produce human readable output. When set to json will produce json /// output. #[arg(long, value_enum, default_value_t)] pub format: Format, } s390-tools-2.38.0/rust/cpacfinfo/src/main.rs000066400000000000000000000211041502674226300204530ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 mod cli; mod msa; mod query; mod stfle; use anyhow::bail; use anyhow::Result as anyhowRes; use clap::Parser; use std::io::ErrorKind; use std::result::Result::Ok; use utils::print_version; use crate::cli::{Cli, Format}; use crate::msa::*; use crate::query::*; use crate::stfle::*; /// producing -m/--msa output fn out_msa(args: &Cli, levels: &Vec, instructions: &Vec) { // produce json output if args.format == Format::Json { println!("{}", serde_json::to_string(levels).unwrap()); return; } // produce human readable output for lvl in levels { // print current level println!("{lvl}"); // if -f/--functions is not specified continue to next level if !args.functions { continue; } // print all functions introduced by the current level sorted by instruction for ins in instructions { // skip instructions for which the current level introduces no new functions if !args.instructions.contains(&ins.kind) && !args.instructions.is_empty() || !ins.info.available { continue; } // filter all functions that do not fit the command line arguments let funcs_to_be_printed = ins .funcs .iter() .filter(|func| { !(!func.available && !args.not_available || func.available && !args.available && args.not_available) }) .filter(|func| func.msa == lvl.msa_level); // print all functions matching the command line arguments let mut ins_printed = false; for func in funcs_to_be_printed { if !ins_printed { println!("\t{ins}"); ins_printed = true; } println!("\t\t{func}"); } } } } /// produces output for all cpacfinfo commands that do not contain the -m/--msa flag fn out_instructions(args: &Cli, instructions: &Vec) { // produce json output if args.format == Format::Json { println!("{}", serde_json::to_string(instructions).unwrap()); return; } // produce human readable output for ins in instructions { if !args.instructions.contains(&ins.kind) && !args.instructions.is_empty() || !ins.info.available { continue; } println!("{ins}"); // --no-auth-info/-n suppresses the Authentication Information output if !args.quiet && ins.info.qai_available { println!("{}", ins.info.qai); } else if !args.quiet { println!("Query Authentication Information not available for {ins} instruction! (potentially insufficient machine level)"); } // --functions/-f lists functions of instructions if args.functions { ins.funcs .iter() .filter(|func| { !(!func.available && !args.not_available || func.available && !args.available && args.not_available) }) .for_each(|func| println!("\t{func}")); println!(); } } } fn main() -> anyhowRes<()> { /* ---- PARSE COMMAND LINE ARGUMENTS ---- */ let args: Cli = Cli::parse(); /* ---- PRINT VERSION STRING ---- */ if args.version { print_version!("2024"); return Ok(()); } /* ---- SET CONSTANTS ---- */ let mut instructions = Vec::new(); init_instructions(&mut instructions); let mut levels = Vec::new(); for lvl in 0..MSA_LEVEL_COUNT { let temp = match num2msa(lvl) { Some(l) => l, None => panic!("programming error"), }; let stfle_bit = msa2stfle(&temp); levels.push(MsaLevel::new(temp, stfle_bit)); let idx_of_last_element = levels.len() - 1; update_msa_function_count(&args, &mut levels[idx_of_last_element], &instructions); } /* ---- GET INFORMATION ---- */ // get stfle bits let stfle_bits = Stfle::new()?; // check stfle bits for available MSA levels for lvl in &mut levels { match lvl.stfle_bit { Some(bit) => lvl.enabled = stfle_bits.check_bit_in_stfle(bit), None => continue, } } // check if SYSFS_PATH is available match check_sysfs() { true => (), false => return Ok(()), } // run query function (fc 0) for every instruction to check available functions for ins in &mut instructions { if stfle_bits.check_bit_in_stfle(ins.info.stfle_bit) { ins.info.available = true; // run query; save result in param let mut param = match query(&ins.kind, QUERY_FUNCTION_CODE) { Ok(pb) => match pb { Param::QueryParam(_) => pb, Param::QaiParam(_) => panic!("programming error"), }, Err(e) => match e.kind() { ErrorKind::NotFound => { println!("Warning: Not able to retrieve subfunction information from sysfs for {ins} instruction"); continue; } _ => return Err(e.into()), }, }; // check if bit for functions of current instruction is set in param for func in &mut ins.funcs { if !param.check_bit_in_param(func.function_code as usize) { continue; } func.available = true; // unset the bit in param to later see if any unsupported functions may be available param.unset_bit_in_param(func.function_code); // check if qai is available if func.function_code == QAI_FUNCTION_CODE { ins.info.qai_available = true; } // sync MsaLevel struct for lvl in &mut levels { if lvl.msa_level == func.msa { lvl.enabled = true; lvl.available_functions += 1; if args.instructions.is_empty() || args.instructions.contains(&ins.kind) { lvl.dynamic_available_functions += 1; } break; } } } // look for any unsupported functions that my be available for i in 0..NUMBER_FUNC_CODES { // every bit in param that is 1 is an unsupported function if !param.check_bit_in_param(i) { continue; } // add function to instruction as UNKNOWN Instruction::add(ins, Function::new(i as u8, Msa::UNKNOWN, "UNKNOWN")); // set function as available match ins.funcs.last_mut() { Some(ret) => ret.available = true, None => panic!("programming error"), } } // if query authentication information (fc 127) available run query authentication // information if ins.info.qai_available { // get qai from sysfs let param = match query(&ins.kind, QAI_FUNCTION_CODE) { Ok(pb) => match pb { Param::QueryParam(_) => panic!("programming error"), Param::QaiParam(_) => pb, }, Err(e) => match e.kind() { ErrorKind::NotFound => { println!("Warning: Not able to retrieve Query Authentication Information from sysfs for {ins} instruction"); continue; } _ => return Err(e.into()), }, }; // parse qai information into QueryAuthInfo struct match param.parse_qai_based_on_format(&mut ins.info.qai) { Ok(true) => (), Ok(false) => println!("WARNING: format {} in query authentication information of instruction {} is UNKNOWN", ins.info.qai.format, ins.kind), Err(e) => bail!(e.to_string()), } } } } /* ---- OUTPUT ---- */ match args.msa { true => out_msa(&args, &levels, &instructions), false => out_instructions(&args, &instructions), } Ok(()) } s390-tools-2.38.0/rust/cpacfinfo/src/msa.rs000066400000000000000000000602271502674226300203200ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use crate::cli::Cli; use core::fmt::{Display, Formatter, Result}; use serde::{Serialize, Serializer}; use utils::HexSlice; /// Number of total function codes (0 to 127) pub const NUMBER_FUNC_CODES: usize = 128; /// Number of MSA levels starting with MSA (0) - MSA 13 pub const MSA_LEVEL_COUNT: u8 = 14; /// enum of all supported instructions #[derive(PartialEq, Clone, clap::ValueEnum, Serialize)] #[allow(clippy::upper_case_acronyms)] pub enum InstructionKind { /// introduced with MSA KM, /// introduced with MSA KMC, /// introduced with MSA KIMD, /// introduced with MSA KLMD, /// introduced with MSA KMAC, /// introduced with MSA 3 PCKMO, /// introduced with MSA 4 KMF, /// introduced with MSA 4 KMCTR, /// introduced with MSA 4 KMO, /// introduced with MSA 4 PCC, /// introduced with MSA 5 PRNO, /// introduced with MSA 8 KMA, /// introduced with MSA 9 KDSA, } /// enum of all MSA levels #[derive(Clone, Default, PartialEq)] #[allow(clippy::upper_case_acronyms)] pub enum Msa { MSA, MSA1, MSA2, MSA3, MSA4, MSA5, MSA6, MSA7, MSA8, MSA9, MSA10, MSA11, MSA12, MSA13, #[default] UNKNOWN, } impl Serialize for Msa { fn serialize(&self, serializer: S) -> std::result::Result where S: Serializer, { match *self { Self::MSA => serializer.serialize_unit_variant("Msa", 0, "0"), Self::MSA1 => serializer.serialize_unit_variant("Msa", 1, "1"), Self::MSA2 => serializer.serialize_unit_variant("Msa", 2, "2"), Self::MSA3 => serializer.serialize_unit_variant("Msa", 3, "3"), Self::MSA4 => serializer.serialize_unit_variant("Msa", 4, "4"), Self::MSA5 => serializer.serialize_unit_variant("Msa", 5, "5"), Self::MSA6 => serializer.serialize_unit_variant("Msa", 6, "6"), Self::MSA7 => serializer.serialize_unit_variant("Msa", 7, "7"), Self::MSA8 => serializer.serialize_unit_variant("Msa", 8, "8"), Self::MSA9 => serializer.serialize_unit_variant("Msa", 9, "9"), Self::MSA10 => serializer.serialize_unit_variant("Msa", 10, "10"), Self::MSA11 => serializer.serialize_unit_variant("Msa", 11, "11"), Self::MSA12 => serializer.serialize_unit_variant("Msa", 12, "12"), Self::MSA13 => serializer.serialize_unit_variant("Msa", 13, "13"), Self::UNKNOWN => serializer.serialize_unit_variant("Msa", 14, "UNKNOWN"), } } } impl Display for Msa { fn fmt(&self, f: &mut Formatter) -> Result { match *self { Self::MSA => write!(f, "MSA "), Self::MSA1 => write!(f, "MSA 1"), Self::MSA2 => write!(f, "MSA 2"), Self::MSA3 => write!(f, "MSA 3"), Self::MSA4 => write!(f, "MSA 4"), Self::MSA5 => write!(f, "MSA 5"), Self::MSA6 => write!(f, "MSA 6"), Self::MSA7 => write!(f, "MSA 7"), Self::MSA8 => write!(f, "MSA 8"), Self::MSA9 => write!(f, "MSA 9"), Self::MSA10 => write!(f, "MSA 10"), Self::MSA11 => write!(f, "MSA 11"), Self::MSA12 => write!(f, "MSA 12"), Self::MSA13 => write!(f, "MSA 13"), Self::UNKNOWN => write!(f, "UNKNOWN"), } } } /// converts Instruction enum to a string representation impl Display for InstructionKind { fn fmt(&self, f: &mut Formatter) -> Result { match *self { Self::KM => write!(f, "KM"), Self::KMC => write!(f, "KMC"), Self::KIMD => write!(f, "KIMD"), Self::KLMD => write!(f, "KLMD"), Self::KMAC => write!(f, "KMAC"), Self::PCKMO => write!(f, "PCKMO"), Self::KMF => write!(f, "KMF"), Self::KMCTR => write!(f, "KMCTR"), Self::KMO => write!(f, "KMO"), Self::PCC => write!(f, "PCC"), Self::PRNO => write!(f, "PRNO"), Self::KMA => write!(f, "KMA"), Self::KDSA => write!(f, "KDSA"), } } } #[derive(Serialize, Default)] pub struct MsaLevel { pub msa_level: Msa, total_functions: u8, pub available_functions: u8, #[serde(skip)] dynamic_total_functions: u8, #[serde(skip)] pub dynamic_available_functions: u8, pub stfle_bit: Option, pub enabled: bool, } impl MsaLevel { pub fn new(msa_level: Msa, stfle_bit: Option) -> Self { Self { msa_level, stfle_bit, ..Default::default() } } } impl Display for MsaLevel { fn fmt(&self, f: &mut Formatter) -> Result { write!(f, "{} ", self.msa_level)?; match self.stfle_bit { Some(bit) => write!(f, "STFLE bit [ {:>3} ] : ", bit)?, None => write!(f, " : ")?, } match self.enabled { true => write!(f, " AVAILABLE")?, false => write!(f, "NOT AVAILABLE")?, } write!( f, " ( {:>2} / {:<2} functions available )", self.dynamic_available_functions, self.dynamic_total_functions ) } } #[derive(Serialize, Clone, Default)] pub struct Function { name: String, pub function_code: u8, pub available: bool, #[serde(skip)] pub msa: Msa, } impl Function { pub fn new(fc: u8, msa: Msa, name: &str) -> Self { Self { function_code: fc, name: name.to_string(), msa, ..Default::default() } } } impl Display for Function { fn fmt(&self, f: &mut Formatter) -> Result { write!(f, "({:3}) ", self.function_code)?; match self.available { true => write!(f, "[ AVAILABLE]")?, false => write!(f, "[NOT AVAILABLE]")?, } write!(f, " {}", self.name) } } #[derive(Serialize, Default)] pub struct QueryAuthInfo { pub format: u8, pub hash_len: u16, pub version: u32, // #[serde(with = "hex::serde")] #[serde(serialize_with = "ser_hex")] pub hash: Vec, } impl Display for QueryAuthInfo { fn fmt(&self, f: &mut Formatter) -> Result { write!(f, " Format: {}", self.format)?; if self.format != 0 { writeln!(f, " (unknown format)")?; return Ok(()); } write!(f, "; Hash length: {}", self.hash_len)?; writeln!(f, "; IFCL version: {}", self.version)?; writeln!(f, " Hash:")?; for chunk in self.hash.chunks(16) { writeln!(f, " {:-}", HexSlice::from(chunk))?; } Ok(()) } } #[derive(Serialize, Default)] pub struct InstructionInfo { pub name: String, pub available: bool, pub stfle_bit: u8, #[serde(skip)] pub qai_available: bool, pub qai: QueryAuthInfo, } impl InstructionInfo { fn new(stfle_bit: u8, name: &str) -> Self { Self { stfle_bit, name: name.to_string(), ..Default::default() } } } #[derive(Serialize)] pub struct Instruction { pub kind: InstructionKind, pub info: InstructionInfo, pub funcs: Vec, } impl Instruction { fn new(instruction: InstructionKind, stfle_bit: u8, name: &str) -> Self { Self { kind: instruction, info: InstructionInfo::new(stfle_bit, name), funcs: Vec::new(), } } pub fn add(&mut self, func: Function) { self.funcs.push(func); } } impl Display for Instruction { fn fmt(&self, f: &mut Formatter) -> Result { write!(f, "{} ({})", self.info.name, self.kind) } } /// returns stfle bit based on given MSA level pub fn msa2stfle(msa_level: &Msa) -> Option { match msa_level { Msa::MSA => Some(17), Msa::MSA3 => Some(76), Msa::MSA4 => Some(77), Msa::MSA5 => Some(57), Msa::MSA8 => Some(146), Msa::MSA9 => Some(155), Msa::MSA12 => Some(86), _ => None, } } /// returns MSA level based on given u8 pub fn num2msa(num: u8) -> Option { match num { 0 => Some(Msa::MSA), 1 => Some(Msa::MSA1), 2 => Some(Msa::MSA2), 3 => Some(Msa::MSA3), 4 => Some(Msa::MSA4), 5 => Some(Msa::MSA5), 6 => Some(Msa::MSA6), 7 => Some(Msa::MSA7), 8 => Some(Msa::MSA8), 9 => Some(Msa::MSA9), 10 => Some(Msa::MSA10), 11 => Some(Msa::MSA11), 12 => Some(Msa::MSA12), 13 => Some(Msa::MSA13), _ => None, } } /// Initializes all functions known by cpacfinfo #[rustfmt::skip] pub fn init_instructions(instructions: &mut Vec) { let mut km = Instruction::new(InstructionKind::KM, 17, "Cipher Message"); km.add(Function::new(0, Msa::MSA, "KM-Query")); km.add(Function::new(1, Msa::MSA, "KM-DEA")); km.add(Function::new(2, Msa::MSA, "KM-TDEA-128")); km.add(Function::new(3, Msa::MSA, "KM-TDEA-192")); km.add(Function::new(9, Msa::MSA3, "KM-Encrypted-DEA")); km.add(Function::new(10, Msa::MSA3, "KM-Encrypted-TDEA-128")); km.add(Function::new(11, Msa::MSA3, "KM-Encrypted-TDEA-192")); km.add(Function::new(18, Msa::MSA1, "KM-AES-128")); km.add(Function::new(19, Msa::MSA2, "KM-AES-192")); km.add(Function::new(20, Msa::MSA2, "KM-AES-256")); km.add(Function::new(26, Msa::MSA3, "KM-Encrypted-AES-128")); km.add(Function::new(27, Msa::MSA3, "KM-Encrypted-AES-192")); km.add(Function::new(28, Msa::MSA3, "KM-Encrypted-AES-256")); km.add(Function::new(50, Msa::MSA4, "KM-XTS-AES-128")); km.add(Function::new(52, Msa::MSA4, "KM-XTS-AES-256")); km.add(Function::new(58, Msa::MSA4, "KM-XTS-Encrypted-AES-128")); km.add(Function::new(60, Msa::MSA4, "KM-XTS-Encrypted-AES-256")); km.add(Function::new(82, Msa::MSA10, "KM-FULL-XTS-AES-128")); km.add(Function::new(84, Msa::MSA10, "KM-FULL-XTS-AES-256")); km.add(Function::new(90, Msa::MSA10, "KM-FULL-XTS-Encrypted-AES-128")); km.add(Function::new(92, Msa::MSA10, "KM-FULL-XTS-Encrypted-AES-256")); km.add(Function::new(127, Msa::MSA13, "KM-Query-Authentication-Information")); let mut kmc = Instruction::new(InstructionKind::KMC, 17, "Cipher Message with Chaining"); kmc.add(Function::new(0, Msa::MSA, "KMC-Query")); kmc.add(Function::new(1, Msa::MSA, "KMC-DEA")); kmc.add(Function::new(2, Msa::MSA, "KMC-TDEA-128")); kmc.add(Function::new(3, Msa::MSA, "KMC-TDEA-192")); kmc.add(Function::new(9, Msa::MSA3, "KMC-Encrypted-DEA")); kmc.add(Function::new(10, Msa::MSA3, "KMC-Encrypted-TDEA-128")); kmc.add(Function::new(11, Msa::MSA3, "KMC-Encrypted-TDEA-192")); kmc.add(Function::new(18, Msa::MSA1, "KMC-AES-128")); kmc.add(Function::new(19, Msa::MSA2, "KMC-AES-192")); kmc.add(Function::new(20, Msa::MSA2, "KMC-AES-256")); kmc.add(Function::new(26, Msa::MSA3, "KMC-Encrypted-AES-128")); kmc.add(Function::new(27, Msa::MSA3, "KMC-Encrypted-AES-192")); kmc.add(Function::new(28, Msa::MSA3, "KMC-Encrypted-AES-256")); kmc.add(Function::new(67, Msa::MSA1, "KMC-PRNG")); kmc.add(Function::new(127, Msa::MSA13, "KMC-Query-Authentication-Information")); let mut kimd = Instruction::new(InstructionKind::KIMD, 17, "Compute Intermediate Message Digest"); kimd.add(Function::new(0, Msa::MSA, "KIMD-Query")); kimd.add(Function::new(1, Msa::MSA, "KIMD-SHA-1")); kimd.add(Function::new(2, Msa::MSA1, "KIMD-SHA-256")); kimd.add(Function::new(3, Msa::MSA2, "KIMD-SHA-512")); kimd.add(Function::new(32, Msa::MSA6, "KIMD-SHA3-224")); kimd.add(Function::new(33, Msa::MSA6, "KIMD-SHA3-256")); kimd.add(Function::new(34, Msa::MSA6, "KIMD-SHA3-384")); kimd.add(Function::new(35, Msa::MSA6, "KIMD-SHA3-512")); kimd.add(Function::new(36, Msa::MSA6, "KIMD-SHAKE-128")); kimd.add(Function::new(37, Msa::MSA6, "KIMD-SHAKE-256")); kimd.add(Function::new(65, Msa::MSA4, "KIMD-GHASH")); kimd.add(Function::new(127, Msa::MSA13, "KIMD-Query-Authentication-Information")); let mut klmd = Instruction::new(InstructionKind::KLMD, 17, "Compute Last Message Digest"); klmd.add(Function::new(0, Msa::MSA, "KLMD-Query")); klmd.add(Function::new(1, Msa::MSA, "KLMD-SHA-1")); klmd.add(Function::new(2, Msa::MSA1, "KLMD-SHA-256")); klmd.add(Function::new(3, Msa::MSA2, "KLMD-SHA-512")); klmd.add(Function::new(32, Msa::MSA6, "KLMD-SHA3-224")); klmd.add(Function::new(33, Msa::MSA6, "KLMD-SHA3-256")); klmd.add(Function::new(34, Msa::MSA6, "KLMD-SHA3-384")); klmd.add(Function::new(35, Msa::MSA6, "KLMD-SHA3-512")); klmd.add(Function::new(36, Msa::MSA6, "KLMD-SHAKE-128")); klmd.add(Function::new(37, Msa::MSA6, "KLMD-SHAKE-256")); klmd.add(Function::new(127, Msa::MSA13, "KLMD-Query-Authentication-Information")); let mut kmac = Instruction::new(InstructionKind::KMAC, 17, "Compute Message Authentication Code"); kmac.add(Function::new(0, Msa::MSA, "KMAC-Query")); kmac.add(Function::new(1, Msa::MSA, "KMAC-DEA")); kmac.add(Function::new(2, Msa::MSA, "KMAC-TDEA-128")); kmac.add(Function::new(3, Msa::MSA, "KMAC-TDEA-192")); kmac.add(Function::new(9, Msa::MSA3, "KMAC-Encrypted-DEA")); kmac.add(Function::new(10, Msa::MSA3, "KMAC-Encrypted-TDEA-128")); kmac.add(Function::new(11, Msa::MSA3, "KMAC-Encrypted-TDEA-192")); kmac.add(Function::new(18, Msa::MSA4, "KMAC-AES-128")); kmac.add(Function::new(19, Msa::MSA4, "KMAC-AES-192")); kmac.add(Function::new(20, Msa::MSA4, "KMAC-AES-256")); kmac.add(Function::new(26, Msa::MSA4, "KMAC-Encrypted-AES-128")); kmac.add(Function::new(27, Msa::MSA4, "KMAC-Encrypted-AES-192")); kmac.add(Function::new(28, Msa::MSA4, "KMAC-Encrypted-AES-256")); kmac.add(Function::new(112, Msa::MSA11, "KMAC-HMAC-SHA-224")); kmac.add(Function::new(113, Msa::MSA11, "KMAC-HMAC-SHA-256")); kmac.add(Function::new(114, Msa::MSA11, "KMAC-HMAC-SHA-384")); kmac.add(Function::new(115, Msa::MSA11, "KMAC-HMAC-SHA-512")); kmac.add(Function::new(120, Msa::MSA11, "KMAC-HMAC-Encrypted-SHA-224")); kmac.add(Function::new(121, Msa::MSA11, "KMAC-HMAC-Encrypted-SHA-256")); kmac.add(Function::new(122, Msa::MSA11, "KMAC-HMAC-Encrypted-SHA-384")); kmac.add(Function::new(123, Msa::MSA11, "KMAC-HMAC-Encrypted-SHA-512")); kmac.add(Function::new(127, Msa::MSA13, "KMAC-Query-Authentication-Information")); let mut pckmo = Instruction::new(InstructionKind::PCKMO, 76, "Perform Cryptographic Key Management Operation"); pckmo.add(Function::new(0, Msa::MSA3, "PCKMO-Query")); pckmo.add(Function::new(1, Msa::MSA3, "PCKMO-Encrypt-DEA-Key")); pckmo.add(Function::new(2, Msa::MSA3, "PCKMO-Encrypt-TDEA-128-Key")); pckmo.add(Function::new(3, Msa::MSA3, "PCKMO-Encrypt-TDEA-192-Key")); pckmo.add(Function::new(18, Msa::MSA3, "PCKMO-Encrypt-AES-128-Key")); pckmo.add(Function::new(19, Msa::MSA3, "PCKMO-Encrypt-AES-192-Key")); pckmo.add(Function::new(20, Msa::MSA3, "PCKMO-Encrypt-AES-256-Key")); pckmo.add(Function::new(21, Msa::MSA10, "PCKMO-AES-XTS-128-Double")); pckmo.add(Function::new(22, Msa::MSA10, "PCKMO-AES-XTS-256-Double")); pckmo.add(Function::new(32, Msa::MSA9, "PCKMO-Encrypt-ECC-P256-Key")); pckmo.add(Function::new(33, Msa::MSA9, "PCKMO-Encrypt-ECC-P384-Key")); pckmo.add(Function::new(34, Msa::MSA9, "PCKMO-Encrypt-ECC-P521-Key")); pckmo.add(Function::new(40, Msa::MSA9, "PCKMO-Encrypt-ECC-Ed25519-Key")); pckmo.add(Function::new(41, Msa::MSA9, "PCKMO-Encrypt-ECC-Ed448-Key")); pckmo.add(Function::new(118, Msa::MSA11, "PCKMO-Encrypted-HMAC-512-KEY")); pckmo.add(Function::new(122, Msa::MSA11, "PCKMO-Encrypted-HMAC-1024-KEY")); pckmo.add(Function::new(127, Msa::MSA13, "PCKMO-Query-Authentication-Information")); let mut kmf = Instruction::new(InstructionKind::KMF, 77, "Cipher Message with Cipher Feedback"); kmf.add(Function::new(0, Msa::MSA4, "KMF-Query")); kmf.add(Function::new(1, Msa::MSA4, "KMF-DEA")); kmf.add(Function::new(2, Msa::MSA4, "KMF-TDEA-128")); kmf.add(Function::new(3, Msa::MSA4, "KMF-TDEA-192")); kmf.add(Function::new(9, Msa::MSA4, "KMF-Encrypted-DEA")); kmf.add(Function::new(10, Msa::MSA4, "KMF-Encrypted-TDEA-128")); kmf.add(Function::new(11, Msa::MSA4, "KMF-Encrypted-TDEA-192")); kmf.add(Function::new(18, Msa::MSA4, "KMF-AES-128")); kmf.add(Function::new(19, Msa::MSA4, "KMF-AES-192")); kmf.add(Function::new(20, Msa::MSA4, "KMF-AES-256")); kmf.add(Function::new(26, Msa::MSA4, "KMF-Encrypted-AES-128")); kmf.add(Function::new(27, Msa::MSA4, "KMF-Encrypted-AES-192")); kmf.add(Function::new(28, Msa::MSA4, "KMF-Encrypted-AES-256")); kmf.add(Function::new(127, Msa::MSA13, "KMF-Query-Authentication-Information")); let mut kmctr = Instruction::new(InstructionKind::KMCTR, 77, "Cipher Message with Counter"); kmctr.add(Function::new(0, Msa::MSA4, "KMCTR-Query")); kmctr.add(Function::new(1, Msa::MSA4, "KMCTR-DEA")); kmctr.add(Function::new(2, Msa::MSA4, "KMCTR-TDEA-128")); kmctr.add(Function::new(3, Msa::MSA4, "KMCTR-TDEA-192")); kmctr.add(Function::new(9, Msa::MSA4, "KMCTR-Encrypted-DEA")); kmctr.add(Function::new(10, Msa::MSA4, "KMCTR-Encrypted-TDEA-128")); kmctr.add(Function::new(11, Msa::MSA4, "KMCTR-Encrypted-TDEA-192")); kmctr.add(Function::new(18, Msa::MSA4, "KMCTR-AES-128")); kmctr.add(Function::new(19, Msa::MSA4, "KMCTR-AES-192")); kmctr.add(Function::new(20, Msa::MSA4, "KMCTR-AES-256")); kmctr.add(Function::new(26, Msa::MSA4, "KMCTR-Encrypted-AES-128")); kmctr.add(Function::new(27, Msa::MSA4, "KMCTR-Encrypted-AES-192")); kmctr.add(Function::new(28, Msa::MSA4, "KMCTR-Encrypted-AES-256")); kmctr.add(Function::new(127, Msa::MSA13, "KMCTR-Query-Authentication-Information")); let mut kmo = Instruction::new(InstructionKind::KMO, 77, "Cipher Message with Output Feedback"); kmo.add(Function::new(0, Msa::MSA4, "KMO-Query")); kmo.add(Function::new(1, Msa::MSA4, "KMO-DEA")); kmo.add(Function::new(2, Msa::MSA4, "KMO-TDEA-128")); kmo.add(Function::new(3, Msa::MSA4, "KMO-TDEA-192")); kmo.add(Function::new(9, Msa::MSA4, "KMO-Encrypted-DEA")); kmo.add(Function::new(10, Msa::MSA4, "KMO-Encrypted-TDEA-128")); kmo.add(Function::new(11, Msa::MSA4, "KMO-Encrypted-TDEA-192")); kmo.add(Function::new(18, Msa::MSA4, "KMO-AES-128")); kmo.add(Function::new(19, Msa::MSA4, "KMO-AES-192")); kmo.add(Function::new(20, Msa::MSA4, "KMO-AES-256")); kmo.add(Function::new(26, Msa::MSA4, "KMO-Encrypted-AES-128")); kmo.add(Function::new(27, Msa::MSA4, "KMO-Encrypted-AES-192")); kmo.add(Function::new(28, Msa::MSA4, "KMO-Encrypted-AES-256")); kmo.add(Function::new(127, Msa::MSA13, "KMO-Query-Authentication-Information")); let mut pcc = Instruction::new(InstructionKind::PCC, 77, "Perform Cryptographic Computation"); pcc.add(Function::new(0, Msa::MSA4, "PCC-Query")); pcc.add(Function::new(1, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-DEA")); pcc.add(Function::new(2, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-TDEA-128")); pcc.add(Function::new(3, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-TDEA-192")); pcc.add(Function::new(9, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-Encrypted-DEA")); pcc.add(Function::new(10, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-Encrypted-TDEA-128")); pcc.add(Function::new(11, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-Encrypted-TDEA-192")); pcc.add(Function::new(18, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-AES-128")); pcc.add(Function::new(19, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-AES-192")); pcc.add(Function::new(20, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-AES-256")); pcc.add(Function::new(26, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-Encrypted-AES-128")); pcc.add(Function::new(27, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-Encrypted-AES-192")); pcc.add(Function::new(28, Msa::MSA4, "PCC-Compute-Last-Block-CMAC-Using-Encrypted-AES-256")); pcc.add(Function::new(50, Msa::MSA4, "PCC-Compute-XTS-Parameter-Using-AES-128")); pcc.add(Function::new(52, Msa::MSA4, "PCC-Compute-XTS-Parameter-Using-AES-256")); pcc.add(Function::new(58, Msa::MSA4, "PCC-Compute-XTS-Parameter-Using-Encrypted-AES-128")); pcc.add(Function::new(60, Msa::MSA4, "PCC-Compute-XTS-Parameter-Using-Encrypted-AES-256")); pcc.add(Function::new(64, Msa::MSA9, "PCC-Scalar-Multiply-P256")); pcc.add(Function::new(65, Msa::MSA9, "PCC-Scalar-Multiply-P384")); pcc.add(Function::new(66, Msa::MSA9, "PCC-Scalar-Multiply-P521")); pcc.add(Function::new(72, Msa::MSA9, "PCC-Scalar-Multiply-Ed25519")); pcc.add(Function::new(73, Msa::MSA9, "PCC-Scalar-Multiply-Ed448")); pcc.add(Function::new(80, Msa::MSA9, "PCC-Scalar-Multiply-X25519")); pcc.add(Function::new(81, Msa::MSA9, "PCC-Scalar-Multiply-X448")); pcc.add(Function::new(127, Msa::MSA13, "PCC-Query-Authentication-Information")); let mut prno = Instruction::new(InstructionKind::PRNO, 57, "Perform Random Number Operation"); prno.add(Function::new(0, Msa::MSA5, "PRNO-Query")); prno.add(Function::new(3, Msa::MSA5, "PRNO-SHA-512-DRNG")); prno.add(Function::new(112, Msa::MSA7, "PRNO-TRNG-Query-Raw-to-Conditioned-Ratio")); prno.add(Function::new(114, Msa::MSA7, "PRNO-TRNG")); prno.add(Function::new(127, Msa::MSA13, "PRNO-Query-Authentication-Information")); let mut kma = Instruction::new(InstructionKind::KMA, 146, "Cipher Message with Authentication"); kma.add(Function::new(0, Msa::MSA8, "KMA-Query")); kma.add(Function::new(18, Msa::MSA8, "KMA-GCM-AES-128")); kma.add(Function::new(19, Msa::MSA8, "KMA-GCM-AES-192")); kma.add(Function::new(20, Msa::MSA8, "KMA-GCM-AES-256")); kma.add(Function::new(26, Msa::MSA8, "KMA-GCM-Encrypted-AES-128")); kma.add(Function::new(27, Msa::MSA8, "KMA-GCM-Encrypted-AES-192")); kma.add(Function::new(28, Msa::MSA8, "KMA-GCM-Encrypted-AES-256")); kma.add(Function::new(127, Msa::MSA13, "KMA-Query-Authentication-Information")); let mut kdsa = Instruction::new(InstructionKind::KDSA, 155, "Compute Digital Signature Authentication"); kdsa.add(Function::new(0, Msa::MSA9, "KDSA-Query")); kdsa.add(Function::new(1, Msa::MSA9, "KDSA-ECDSA-Verify-P256")); kdsa.add(Function::new(2, Msa::MSA9, "KDSA-ECDSA-Verify-P384")); kdsa.add(Function::new(3, Msa::MSA9, "KDSA-ECDSA-Verify-P521")); kdsa.add(Function::new(9, Msa::MSA9, "KDSA-ECDSA-Sign-P256")); kdsa.add(Function::new(10, Msa::MSA9, "KDSA-ECDSA-Sign-P384")); kdsa.add(Function::new(11, Msa::MSA9, "KDSA-ECDSA-Sign-P521")); kdsa.add(Function::new(17, Msa::MSA9, "KDSA-Encrypted-ECDSA-Sign-P256")); kdsa.add(Function::new(18, Msa::MSA9, "KDSA-Encrypted-ECDSA-Sign-P384")); kdsa.add(Function::new(19, Msa::MSA9, "KDSA-Encrypted-ECDSA-Sign-P521")); kdsa.add(Function::new(32, Msa::MSA9, "KDSA-EdDSA-Verify-Ed25519")); kdsa.add(Function::new(36, Msa::MSA9, "KDSA-EdDSA-Verify-Ed448")); kdsa.add(Function::new(40, Msa::MSA9, "KDSA-EdDSA-Sign-Ed25519")); kdsa.add(Function::new(44, Msa::MSA9, "KDSA-EdDSA-Sign-Ed448")); kdsa.add(Function::new(48, Msa::MSA9, "KDSA-Encrypted-EdDSA-Sign-Ed25519")); kdsa.add(Function::new(52, Msa::MSA9, "KDSA-Encrypted-EdDSA-Sign-Ed448")); kdsa.add(Function::new(127, Msa::MSA13, "KDSA-Query-Authentication-Information")); instructions.push(km); instructions.push(kmc); instructions.push(kimd); instructions.push(klmd); instructions.push(kmac); instructions.push(pckmo); instructions.push(kmf); instructions.push(kmctr); instructions.push(kmo); instructions.push(pcc); instructions.push(prno); instructions.push(kma); instructions.push(kdsa); } /// number of functions introduced by a level is dynamically counted to ease extension pub fn update_msa_function_count(args: &Cli, msa: &mut MsaLevel, ins: &Vec) { for i in ins { let num_of_funcs_in_level = i.funcs.iter().filter(|f| f.msa == msa.msa_level).count() as u8; msa.total_functions += num_of_funcs_in_level; if args.instructions.is_empty() || args.instructions.contains(&i.kind) { msa.dynamic_total_functions += num_of_funcs_in_level; } } } pub fn ser_hex(data: &Vec, ser: S) -> std::result::Result { HexSlice::from(data).serialize(ser) } s390-tools-2.38.0/rust/cpacfinfo/src/noop.c000066400000000000000000000002501502674226300202770ustar00rootroot00000000000000#include #include uint32_t stfle(uint64_t __attribute__((unused)) * stfle_fac_list, uint32_t __attribute__((unused)) size) { return 0; } s390-tools-2.38.0/rust/cpacfinfo/src/query.rs000066400000000000000000000203161502674226300207000ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::fs::File; use std::io::Error; use std::io::Read; use std::ops::Index; use std::result::Result; use zerocopy::FromBytes; use crate::msa::InstructionKind; use crate::msa::QueryAuthInfo; /// Path to sysfs in which the query and qai informations are fetched from const SYSFS_PATH: &str = "/sys/devices/system/cpu/cpacf/"; /// Every Instruction has a Query function to get information about what functions are available pub const QUERY_FUNCTION_CODE: u8 = 0; /// Number of bytes returned by this Query pub const QUERY_PARAM_SIZE_IN_BYTES: usize = 16; /// Starting with MSA 13 every Instruction has a Query Authentication Information function to get /// information about the running firmware pub const QAI_FUNCTION_CODE: u8 = 127; /// Number of bytes returned by this Query Authentication Information pub const QAI_PARAM_SIZE_IN_BYTES: usize = 256; /// Query authentication information format identifier const FORMAT_0: u8 = 0; #[derive(FromBytes)] #[repr(C)] struct QaiFmt0 { res00: [u8; 6], hash_length: u16, res08: [u8; 4], version: u32, hash: [u8; 64], } #[allow(clippy::large_enum_variant)] pub enum Param { QueryParam([u8; QUERY_PARAM_SIZE_IN_BYTES]), QaiParam([u8; QAI_PARAM_SIZE_IN_BYTES]), } impl Index for Param { type Output = u8; fn index(&self, index: u8) -> &Self::Output { match self { Self::QueryParam(p) => &p[index as usize], Self::QaiParam(p) => &p[index as usize], } } } impl Param { pub fn len(&self) -> usize { match self { Self::QueryParam(_) => QUERY_PARAM_SIZE_IN_BYTES, Self::QaiParam(_) => QAI_PARAM_SIZE_IN_BYTES, } } /// check if a specific bit is 1 in param pub fn check_bit_in_param(&self, check_bit: usize) -> bool { // get correct byte of param let byte = check_bit / 8; // get correct byte of param if byte >= self.len() { return false; } // get correct bit of param let bit = 8 - ((check_bit % 8) + 1); // return if specified bit is set match self { Self::QueryParam(param) => (param[byte] & (1 << bit)) > 0, Self::QaiParam(param) => (param[byte] & (1 << bit)) > 0, } } /// set given bit in param to 0 pub fn unset_bit_in_param(&mut self, flip_bit: u8) { // get correct byte of param let byte = flip_bit / 8; if byte as usize >= self.len() { return; } // get correct bit of param let bit = 8 - ((flip_bit % 8) + 1); // build template to logically AND against param byte // i.e. (flip_bit = 0) template = 1000 0000 let mut template: u8 = 1 << bit; // flip all bits in template // i.e. (flip_bit = 0) template = 0111 1111 template = !template; // set bit to 0 while not changing any other bit // i.e. (flip_bit = 0) 0111 1111 & xxxx xxxx = 0xxx xxxx match self { Self::QueryParam(c) => c[byte as usize] &= template, Self::QaiParam(c) => c[byte as usize] &= template, } } /// set all bytes of self to value #[cfg(test)] pub fn set_param_to(&mut self, value: u8) { match self { Self::QueryParam(ref mut content) => *content = [value; QUERY_PARAM_SIZE_IN_BYTES], Self::QaiParam(ref mut content) => *content = [value; QAI_PARAM_SIZE_IN_BYTES], } } // Outsourced for potential future formats to be easily added in this match statement pub fn parse_qai_based_on_format( &self, qai: &mut QueryAuthInfo, ) -> Result { match self { Self::QueryParam(_) => panic!("programming error"), Self::QaiParam(bin) => { // The third byte of the param block specifies which format to use to parse the rest qai.format = bin[3]; // for new formats add a match case here along with a parsing function match qai.format { FORMAT_0 => { parse_qai_format_0(qai, bin); Ok(true) } _ => Ok(false), } } } } } // check if SYSFS_PATH exists pub fn check_sysfs() -> bool { match std::path::Path::new(SYSFS_PATH).exists() { true => true, false => { println!("Warning: There seems to be an insufficient kernel level running (sysfs interface {SYSFS_PATH} is missing)\nNo information can be fetched from sysfs, application exits early."); false } } } /// parsing the information supplied by sysfs into QueryAuthInfo struct /// /// The following box shows the qai block with named fields each with a length in bytes. /// The length of field IFCL Hash depends on IFCL Hash Length and is either 32 or 64 bytes long. /// In case of a 32 bytes length the latter 32 bytes of the 64 bytes Hash are filled with zeros. /// /// | BYTE | BYTE | BYTE | BYTE | /// ----------------------------------------------------------------- /// | RESERVED (3) FORMAT (1) | /// | RESERVED (2) IFCL HASH LENGTH (2) | /// | RESERVED (4) | /// | IFCL VERSION (4) | /// | IFCL HASH (32 / 64) | /// | RESERVED (176) | /// ----------------------------------------------------------------- fn parse_qai_format_0(qai: &mut QueryAuthInfo, param: &[u8]) { // parse param to temporary struct to ease further conversion let (tmp, _) = QaiFmt0::read_from_prefix(param).expect("programming error"); // parse from temporary struct qai.hash_len = tmp.hash_length; qai.version = tmp.version; // depending on the parsed hash length the hash is parsed qai.hash = vec![0; qai.hash_len as usize]; qai.hash .as_mut_slice() .copy_from_slice(&tmp.hash[..qai.hash_len as usize]); } /// cpacfinfo does not execute the actual instruction with function code but uses information /// provided by the sysfs pub fn query(ins: &InstructionKind, fc: u8) -> Result { // query dependent file names let auth_info; let mut param; match fc { QUERY_FUNCTION_CODE => { auth_info = ""; param = Param::QueryParam([0; QUERY_PARAM_SIZE_IN_BYTES]); } QAI_FUNCTION_CODE => { auth_info = "_auth_info"; param = Param::QaiParam([0; QAI_PARAM_SIZE_IN_BYTES]); } _ => panic!("programming error"), }; // depending on which query is performed the bytes to be read from sysfs vary let bytes_to_be_read = param.len(); // build filepath let filepath = format!( "{SYSFS_PATH}{}_query{auth_info}_raw", ins.to_string().to_lowercase() ); // open file let mut f = File::open(filepath)?; // read file let res = match param { Param::QueryParam(ref mut c) => read_file_to_buf(&mut f, c), Param::QaiParam(ref mut c) => read_file_to_buf(&mut f, c), }; let bytes_read = res?; match bytes_read == bytes_to_be_read { true => Ok(param), false => Err(Error::new(std::io::ErrorKind::UnexpectedEof, "test")), } } fn read_file_to_buf(file: &mut File, buf: &mut [u8]) -> Result { file.read(buf) /* match file.read(buf) { Result::Ok(bytes_read) => Ok(bytes_read), Err(e) => Err(e), } */ } #[cfg(test)] #[test] fn test_param_funcs() { // initialize param with all ones let mut param = Param::QueryParam([0; QUERY_PARAM_SIZE_IN_BYTES]); const NUMBER_OF_BITS: usize = 8 * QUERY_PARAM_SIZE_IN_BYTES; for i in 0..NUMBER_OF_BITS { // reset param to all ones param.set_param_to(0xFF); assert!(param.check_bit_in_param(i)); // set one bit to zero param.unset_bit_in_param(i as u8); // check if that bit is zero assert!(!param.check_bit_in_param(i)); } } s390-tools-2.38.0/rust/cpacfinfo/src/stfle.c000066400000000000000000000006221502674226300204440ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 #include uint32_t stfle(uint64_t *stfle_fac_list, uint32_t size) { uint32_t reg0 = size - 1; asm volatile(" lgr %%r0,%[reg0]\n" " .insn s,0xb2b00000,%[list]\n" /* stfle */ " lgr %[reg0],%%r0\n" : [reg0] "+&d"(reg0), [list] "+Q"(*stfle_fac_list) : : "memory", "cc", "r0"); return reg0; } s390-tools-2.38.0/rust/cpacfinfo/src/stfle.rs000066400000000000000000000047661502674226300206630ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use anyhow::Error; /// Specifies the number of u64 values needed to store the stfle block pub const STFLE_LEN: usize = 3; pub struct Stfle { data: [u64; STFLE_LEN], } impl Stfle { /// Constructs a STFLE block, and saves the STFLE information in the structure. pub fn new() -> Result { let mut ret = Self { data: [0; STFLE_LEN], }; // SAFETY: this call is safe because ret can store 64 bits * 3 which equals the required 192 // bits. let rc = unsafe { stfle(&mut ret.data[0], STFLE_LEN as u32) }; let rc = match rc { 0 => { println!("Unable to fetch STFLE which is only available on s390x architecture"); return Ok(ret); } rc if rc as usize >= STFLE_LEN => STFLE_LEN as u32, rc => rc + 1, }; if rc != STFLE_LEN as u32 { println!("Partial read of STFLE, information might be incomplete"); } Ok(ret) } /// check specific bit in stfle (accounts for big-endianness of stfle) pub fn check_bit_in_stfle(&self, check_bit: u8) -> bool { // stfle is big endian while check_bit is little endian let byte = (check_bit / 64) as usize; if byte >= STFLE_LEN { return false; } // conversion from little endian check_bit to big endian let bit = ((check_bit / 64 + 1) * 64 - 1) - check_bit; self.data[byte] & 1 << bit > 0 } } // STFLE bits cannot be retrieved from the system but have to be fetched by running the STFLE // instruction of Z. This is done in linked C code extern "C" { /// Retrieve STFLE bits into list /// /// List is in big-endian when returned. /// @list is to return the outcome of the stfle operation. Pointer must be able to store /// 192 bits. /// @doublewords specifies the length of the pointer @list as a number of elements behind /// the pointer. fn stfle(list: *mut u64, doublewords: u32) -> u32; } #[cfg(test)] #[test] fn test_check_bit_in_stfle() { let mut stfle = match Stfle::new() { Ok(ret) => ret, Err(e) => panic!("{e}"), }; for b in 0..(STFLE_LEN * 64) { // set bit stfle.data[b / 64] = u64::pow(2, (63 - (b - ((b / 64) * 64))) as u32); // check if bit is set assert!(stfle.check_bit_in_stfle(b as u8)); // reset stfle stfle.data = [0; STFLE_LEN]; } } s390-tools-2.38.0/rust/pv/000077500000000000000000000000001502674226300150715ustar00rootroot00000000000000s390-tools-2.38.0/rust/pv/Cargo.toml000066400000000000000000000015431502674226300170240ustar00rootroot00000000000000[package] name = "s390_pv" version = "0.12.0" edition.workspace = true license.workspace = true rust-version.workspace = true description = "s390-tools IBM Secure Execution utilities" keywords = ["s390", "s390x", "IBM_Secure_Execution"] repository = "https://github.com/ibm-s390-linux/s390-tools/tree/master/rust" categories = ["hardware-support"] readme = "README.md" [lints] workspace = true [dependencies] byteorder = "1.5" curl = "0.4.47" enum_dispatch = "0.3.13" foreign-types = "0.3.2" log = { version = "0.4.25", features = ["std", "release_max_level_debug"] } openssl = "0.10.70" openssl-sys = "0.9.105" serde = { version = "1.0.217", features = ["derive"] } thiserror = "2.0.11" zerocopy = { version="0.8", features = ["derive"] } pv_core = { path = "../pv_core", package = "s390_pv_core", version = "0.12.0" } [dev-dependencies] serde_test = "1.0.177" s390-tools-2.38.0/rust/pv/README.md000066400000000000000000000016221502674226300163510ustar00rootroot00000000000000 # s390_pv - library for pv-tools This library is intended to be used by tools and libraries that are used for creating and managing [IBM Secure Execution](https://www.ibm.com/docs/en/linux-on-systems?topic=virtualization-secure-execution) guests. `pv` provides abstraction layers for encryption, secure memory management, and accessing the uvdevice. If your project is not targeted to provide tooling for and/or managing of IBM Secure execution guests, do **not** use this crate. ## OpenSSL 1.1.0+ is required If you do not need any OpenSSL features use [s390_pv_core](https://crates.io/crates/s390_pv_core). This crate reexports all symbols from `s390_pv_core`. If your project uses this crate do **not** include `s390_pv_core` as well. ## Import crate The recommended way of importing this crate is: ```bash cargo add s390_pv --rename pv ``` s390-tools-2.38.0/rust/pv/src/000077500000000000000000000000001502674226300156605ustar00rootroot00000000000000s390-tools-2.38.0/rust/pv/src/brcb.rs000066400000000000000000000303561502674226300171450ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023, 2024 use std::{ io::{Read, Seek, SeekFrom::Current}, mem::size_of, }; use log::{debug, warn}; use zerocopy::{BigEndian, FromBytes, Immutable, IntoBytes, KnownLayout, U32, U64}; // (SE) boot request control block aka SE header use crate::{assert_size, request::MagicValue, static_assert, Error, Result, PAGESIZE}; /// Struct containing all SE-header tags. /// /// Contains: /// Page List Digest (pld) /// Address List Digest (ald) /// Tweak List Digest (tld) /// SE-Header Tag (tag) #[repr(C)] #[derive(Debug, Clone, Copy, IntoBytes, PartialEq, Eq, FromBytes, Immutable, KnownLayout)] pub struct BootHdrTags { pld: [u8; BootHdrHead::DIGEST_SIZE], ald: [u8; BootHdrHead::DIGEST_SIZE], tld: [u8; BootHdrHead::DIGEST_SIZE], tag: [u8; BootHdrHead::TAG_SIZE], } assert_size!(BootHdrTags, 0xd0); impl AsRef<[u8]> for BootHdrTags { fn as_ref(&self) -> &[u8] { self.as_bytes() } } impl TryFrom> for BootHdrTags { type Error = Error; fn try_from(value: Vec) -> Result { Self::ref_from_bytes(&value) .map_err(|_| Error::InvBootHdrSize(value.len())) .copied() } } /// Struct representing the Secure Execution boot image metadata #[allow(unused)] #[repr(packed)] #[derive(Debug, Clone, FromBytes, IntoBytes, PartialEq, Eq, Immutable, KnownLayout)] pub struct SeImgMetaData { /// Magic value magic: [u8; 8], /// Secure Execution header offset in the image hdr_off: U64, /// Version version: U32, /// IPIB offset in the image ipib_off: U64, } assert_size!(SeImgMetaData, 28); impl SeImgMetaData { /// Address in the Secure Execution boot image pub const OFFSET: u64 = 0xc000; /// V1 of the Secure Execution boot image metadata const V1: u32 = 0x1; /// Create v1 Secure Execution image metadata. pub fn new_v1(hdr_off: u64, ipib_off: u64) -> Self { Self { magic: Self::MAGIC, version: Self::V1.into(), hdr_off: hdr_off.into(), ipib_off: ipib_off.into(), } } fn seek_start(img: &mut R) -> Result where R: Read + Seek, { const BUF_SIZE: i64 = 8; static_assert!(SeImgMetaData::MAGIC.len() == BUF_SIZE as usize); let mut buf = [0; BUF_SIZE as usize]; match img.seek(std::io::SeekFrom::Start(Self::OFFSET)) { Ok(it) => it, Err(_) => return Ok(false), }; match img.read_exact(&mut buf) { Ok(it) => it, Err(_) => return Ok(false), } if Self::starts_with_magic(&buf) { // go back to the beginning of the metadata img.seek(Current(-BUF_SIZE))?; return Ok(true); } Ok(false) } /// Gets the bytes of this value. #[inline(always)] pub fn as_bytes(&self) -> &[u8] { ::as_bytes(self) } /// Returns the version of this [`SeImgMetaData`]. pub fn version(&self) -> u32 { self.version.into() } } /// Magic value for the metadata of a Secure Execution boot image impl MagicValue<8> for SeImgMetaData { // ASCII `SeImgLnx` const MAGIC: [u8; 8] = [0x53, 0x65, 0x49, 0x6d, 0x67, 0x4c, 0x6e, 0x78]; } /// Magic value for a SE-(boot)header #[derive(Debug)] pub struct BootHdrMagic; impl MagicValue<8> for BootHdrMagic { const MAGIC: [u8; 8] = [0x49, 0x42, 0x4d, 0x53, 0x65, 0x63, 0x45, 0x78]; } /// Tries to seek to the start of the Secure Execution header. /// /// Returns `false` if no Secure Execution header found, `true` otherwise. /// /// # Errors /// /// In the very unlikely case an IO error can appear when seeking to the /// beginning of the header. pub fn seek_se_hdr_start(img: &mut R) -> Result where R: Read + Seek, { let max_iter: usize; const BUF_SIZE: i64 = 8; static_assert!(BootHdrMagic::MAGIC.len() == BUF_SIZE as usize); let old_position = img.stream_position()?; if !SeImgMetaData::seek_start(img)? { // Search from the previous position. img.seek(std::io::SeekFrom::Start(old_position))?; max_iter = 0x15; } else { let mut img_metadata_bytes = vec![0u8; size_of::()]; // read in the header img.read_exact(&mut img_metadata_bytes)?; // Cannot fail because the buffer has the same size as SeImgMetaData. let img_metadata = SeImgMetaData::ref_from_bytes(&img_metadata_bytes).unwrap(); let img_metadata_version = img_metadata.version(); if img_metadata_version != SeImgMetaData::V1 { warn!("Unknown Secure Execution boot image version {img_metadata_version}"); } img.seek(std::io::SeekFrom::Start(img_metadata.hdr_off.into()))?; max_iter = 1; } let mut buf = [0; BUF_SIZE as usize]; for _ in 0..max_iter { match img.read_exact(&mut buf) { Ok(it) => it, Err(_) => return Ok(false), }; if BootHdrMagic::starts_with_magic(&buf) { // go back to the beginning of the header img.seek(Current(-BUF_SIZE))?; return Ok(true); } // goto next page start // or report invalid file format if file ends "early" match img.seek(Current(PAGESIZE as i64 - BUF_SIZE)) { Ok(it) => it, Err(_) => return Ok(false), }; } Ok(false) } impl BootHdrTags { /// Returns a reference to the SE-header tag of this [`BootHdrTags`]. pub fn tag(&self) -> &[u8; 16] { &self.tag } /// Creates a new [`BootHdrTags`]. Useful for writing tests. #[doc(hidden)] pub const fn new(pld: [u8; 64], ald: [u8; 64], tld: [u8; 64], tag: [u8; 16]) -> Self { Self { ald, tld, pld, tag } } /// Deserializes a (SE) boot header and extracts the tags. /// /// Searches for the header; if found extracts the tags. /// /// # Errors /// /// This function will return an error if the header could not be found in /// `img` or is invalid. pub fn from_se_image(img: &mut R) -> Result where R: Read + Seek, { if !seek_se_hdr_start(img)? { debug!("No boot hdr found"); return Err(Error::InvBootHdr); } // read in the header let mut hdr = vec![0u8; size_of::()]; img.read_exact(&mut hdr)?; // Very unlikely - seek_se_hdr_start should point to a header or error-out if !BootHdrMagic::starts_with_magic(&hdr) { debug!("Inv magic"); return Err(Error::InvBootHdr); } let hdr_head = match BootHdrHead::read_from_prefix(hdr.as_mut_slice()) { Ok((hdr, _)) => hdr, Err(_) => { debug!("Boot hdr is too small"); return Err(Error::InvBootHdr); } }; // Some sanity checks if hdr_head.version.get() != 0x100 { debug!("Unsupported hdr-version: {:0>4x}", hdr_head.version.get()); return Err(Error::InvBootHdr); } // go to the Boot header tag img.seek(Current( hdr_head.size.get() as i64 - size_of::() as i64 - BootHdrHead::TAG_SIZE as i64, ))?; // read in the tag let mut tag = [0u8; BootHdrHead::TAG_SIZE]; img.read_exact(tag.as_mut_slice())?; Ok(Self { pld: hdr_head.pld, ald: hdr_head.ald, tld: hdr_head.tld, tag, }) } } #[repr(C)] #[derive(Debug, Clone, FromBytes)] struct BootHdrHead { magic: U64, version: U32, size: U32, iv: [u8; 12], res1: u32, nks: U64, sea: U64, nep: U64, pcf: U64, user_pubkey: [u8; 160], pld: [u8; Self::DIGEST_SIZE], ald: [u8; Self::DIGEST_SIZE], tld: [u8; Self::DIGEST_SIZE], } assert_size!(BootHdrHead, 0x1A0); impl BootHdrHead { const DIGEST_SIZE: usize = 0x40; const TAG_SIZE: usize = 0x10; } #[cfg(test)] mod tests { use std::io::Cursor; use super::*; use crate::{get_test_asset, Error}; const EXP_HDR: BootHdrTags = BootHdrTags { pld: [ 0xbe, 0x94, 0xb5, 0xea, 0xb3, 0xc1, 0xb1, 0x18, 0xc7, 0x57, 0xd7, 0xdb, 0x7e, 0xa0, 0xf6, 0x5d, 0x9b, 0x64, 0x82, 0x3a, 0x8d, 0xc5, 0x5b, 0xf8, 0xa8, 0x72, 0x5b, 0x58, 0x07, 0x2d, 0x9d, 0x42, 0x58, 0xc5, 0x3e, 0x8a, 0x5d, 0xa8, 0x2d, 0xfb, 0x21, 0x92, 0xd9, 0x1d, 0x07, 0xbc, 0x1c, 0x39, 0xb9, 0x5d, 0x63, 0x21, 0xd3, 0xba, 0x16, 0xa7, 0x51, 0xa6, 0xe3, 0xe3, 0x2f, 0x3e, 0x01, 0x61, ], ald: [ 0x28, 0x58, 0xc3, 0x36, 0x8b, 0x2a, 0x0a, 0xf0, 0xc5, 0xea, 0x0f, 0xde, 0x79, 0x05, 0xeb, 0x15, 0xaf, 0x9c, 0xd1, 0xdd, 0x73, 0x71, 0x65, 0x93, 0x3c, 0xda, 0xa2, 0xb8, 0x50, 0xb6, 0xa8, 0xe2, 0xf0, 0xf4, 0x2c, 0x7b, 0x36, 0xdd, 0x53, 0x81, 0x09, 0x62, 0x88, 0xdc, 0x09, 0x2d, 0xaa, 0x8a, 0x6f, 0xac, 0xec, 0x25, 0x34, 0x13, 0x7b, 0xc9, 0x4c, 0xa8, 0x0b, 0xda, 0x4f, 0xcb, 0x93, 0x28, ], tld: [ 0x48, 0x60, 0xeb, 0xcf, 0x7b, 0x9d, 0x24, 0xeb, 0x90, 0x9a, 0x79, 0x53, 0x56, 0xad, 0x32, 0xc9, 0x36, 0xb6, 0x21, 0x65, 0x98, 0x8a, 0x9f, 0xfc, 0xd6, 0x61, 0x70, 0xdb, 0xc5, 0x90, 0xc2, 0x30, 0x10, 0xd7, 0x95, 0x2f, 0xa8, 0x82, 0xd1, 0xbb, 0x79, 0x55, 0x8f, 0x9b, 0xe0, 0xa5, 0x49, 0xd8, 0xd7, 0xa9, 0x4a, 0xe7, 0x20, 0xe5, 0xc0, 0x76, 0x0a, 0x82, 0x5d, 0x47, 0x9f, 0xe6, 0x7a, 0xf5, ], tag: [ 0x92, 0x30, 0x9d, 0x45, 0x89, 0xb9, 0xa8, 0x5b, 0x42, 0x7f, 0x87, 0x53, 0x17, 0x1d, 0x15, 0x20, ], }; #[test] fn from_se_image_hdr() { let bin_hdr = get_test_asset!("exp/secure_guest.hdr"); let hdr_tags = BootHdrTags::from_se_image(&mut Cursor::new(*bin_hdr)).unwrap(); assert_eq!(hdr_tags, EXP_HDR); } #[test] fn from_se_image_fail() { let bin_hdr = get_test_asset!("exp/secure_guest.hdr"); let short_hdr = &bin_hdr[1..]; assert!(matches!( BootHdrTags::from_se_image(&mut Cursor::new(short_hdr)), Err(Error::InvBootHdr) )); // mess up magic let mut bin_hdr_copy = *bin_hdr; bin_hdr_copy.swap(0, 1); assert!(matches!( BootHdrTags::from_se_image(&mut Cursor::new(bin_hdr_copy)), Err(Error::InvBootHdr) )); // header is at a non expected position let mut img = vec![0u8; PAGESIZE]; img[0x008..0x288].copy_from_slice(bin_hdr); assert!(matches!( BootHdrTags::from_se_image(&mut Cursor::new(img)), Err(Error::InvBootHdr) )); } #[test] fn from_se_image_img() { let mut img = vec![0u8; 0x13000]; let bin_hdr = get_test_asset!("exp/secure_guest.hdr"); img[0x12000..0x12280].copy_from_slice(bin_hdr); let hdr_tags = BootHdrTags::from_se_image(&mut Cursor::new(img)).unwrap(); assert_eq!(hdr_tags, EXP_HDR); } #[test] fn tags_convert_u8() { let bin_hdr = get_test_asset!("exp/secure_guest.hdr"); let hdr_tags = BootHdrTags::from_se_image(&mut Cursor::new(*bin_hdr)).unwrap(); let ser: &[u8] = hdr_tags.as_ref(); let mut ser = ser.to_vec(); let der: BootHdrTags = ser.clone().try_into().unwrap(); assert_eq!(hdr_tags, der); ser.pop(); let der: Result = ser.clone().try_into(); assert!(matches!(der, Err(Error::InvBootHdrSize(_)))); ser.push(17); ser.push(17); let der: Result = ser.clone().try_into(); assert!(matches!(der, Err(Error::InvBootHdrSize(_)))); } #[test] fn se_img_metadata() { let metadata = SeImgMetaData::new_v1(0x14000, 0x16000); let data = [ 83, 101, 73, 109, 103, 76, 110, 120, 0, 0, 0, 0, 0, 1, 64, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 96, 0, ]; assert_eq!(metadata.as_bytes(), &data); assert_eq!(SeImgMetaData::ref_from_bytes(&data), Ok(&metadata)); assert_eq!(metadata.version(), SeImgMetaData::V1); } } s390-tools-2.38.0/rust/pv/src/crypto.rs000066400000000000000000000575661502674226300175710ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023, 2024 use std::{convert::TryInto, fmt::Display, ops::Range}; use enum_dispatch::enum_dispatch; use openssl::{ derive::Deriver, ec::{EcGroup, EcKey}, hash::{DigestBytes, MessageDigest}, md::MdRef, nid::Nid, pkey::{HasPublic, Id, PKey, PKeyRef, Private, Public}, pkey_ctx::{HkdfMode, PkeyCtx}, rand::rand_bytes, rsa::Padding, sign::{Signer, Verifier}, symm::{decrypt_aead as openssl_decrypt_aead, encrypt_aead as openssl_encrypt_aead, Cipher}, }; use pv_core::request::Confidential; use crate::{error::Result, Error}; /// An AES256-GCM key that will purge itself out of the memory when going out of scope pub type Aes256GcmKey = Confidential<[u8; SymKeyType::AES_256_GCM_KEY_LEN]>; /// An AES256-XTS key that will purge itself out of the memory when going out of scope pub type Aes256XtsKey = Confidential<[u8; SymKeyType::AES_256_XTS_KEY_LEN]>; /// SHA-512 digest length (in bytes) pub const SHA_512_HASH_LEN: usize = 64; #[allow(dead_code)] pub(crate) const SHA_256_HASH_LEN: u32 = 32; #[allow(dead_code)] pub(crate) type Sha256Hash = [u8; SHA_256_HASH_LEN as usize]; /// Types of symmetric keys, to specify during construction. #[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum SymKeyType { /// AES 256 GCM key (32 bytes) Aes256Gcm, /// AES 256 XTS key (64 bytes) Aes256Xts, } impl SymKeyType { #[deprecated] #[allow(non_upper_case_globals)] /// AES 256 GCM key (32 bytes) pub const Aes256: Self = Self::Aes256Gcm; /// AES256-GCM key length (in bytes) pub const AES_256_GCM_KEY_LEN: usize = 32; /// AES256-GCM IV length (in bytes) pub const AES_256_GCM_IV_LEN: usize = 12; /// AES256-GCM tag size (in bytes) pub const AES_256_GCM_TAG_LEN: usize = 16; /// AES256-XTS key length (in bytes) pub const AES_256_XTS_KEY_LEN: usize = 64; /// AES256-XTS tweak length (in bytes) pub const AES_256_XTS_TWEAK_LEN: usize = 16; /// AES256 GCM Block length pub const AES_256_GCM_BLOCK_LEN: usize = 16; /// Returns the tag length of the [`SymKeyType`] if it is an AEAD key pub const fn tag_len(&self) -> Option { match self { SymKeyType::Aes256Gcm => Some(Self::AES_256_GCM_TAG_LEN), SymKeyType::Aes256Xts => None, } } /// Returns true if the [`SymKeyType`] is an AEAD key pub const fn is_aead(&self) -> bool { self.tag_len().is_some() } } impl Display for SymKeyType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let s = match self { Self::Aes256Gcm => "AES-256-GCM", Self::Aes256Xts => "AES-256-XTS", }; write!(f, "{s}") } } impl From for Nid { fn from(value: SymKeyType) -> Self { match value { SymKeyType::Aes256Gcm => Self::AES_256_GCM, SymKeyType::Aes256Xts => Self::AES_256_XTS, } } } /// The `enum_dispatch` macros needs at least one local trait to be implemented. #[allow(unused)] #[enum_dispatch(SymKey)] trait SymKeyTrait {} /// Types of symmetric keys #[non_exhaustive] #[enum_dispatch()] #[derive(Debug, Clone, PartialEq, Eq)] pub enum SymKey { /// AES 256 GCM key (32 bytes) Aes256(Aes256GcmKey), /// AES 256 XTS key (64 bytes) Aes256Xts(Aes256XtsKey), } impl SymKey { /// Generates a random symmetric key. /// /// * `key_tp` - type of the symmetric key /// /// # Errors /// /// This function will return an error if the Key cannot be generated. pub fn random(key_tp: SymKeyType) -> Result { match key_tp { SymKeyType::Aes256Gcm => Ok(Self::Aes256(random_array().map(|v| v.into())?)), SymKeyType::Aes256Xts => Ok(Self::Aes256Xts(random_array().map(|v| v.into())?)), } } /// Returns a reference to the value of this [`SymKey`]. pub fn value(&self) -> &[u8] { match self { Self::Aes256(key) => key.value(), Self::Aes256Xts(key) => key.value(), } } /// Return the key type of this [`SymKey`]. pub fn key_type(&self) -> SymKeyType { match self { Self::Aes256(_) => SymKeyType::Aes256Gcm, Self::Aes256Xts(_) => SymKeyType::Aes256Xts, } } /// Try to create a symmetric key using the provided data. /// /// * `key_tp` - type of the symmetric key /// * `data` - raw key data /// /// # Errors /// /// This function will return an error if the key cannot be created, e.g. /// because the provided data is too small or too large. pub fn try_from_data(key_tp: SymKeyType, data: Confidential>) -> Result { match key_tp { SymKeyType::Aes256Gcm => Ok(Self::Aes256(data.try_into()?)), SymKeyType::Aes256Xts => Ok(Self::Aes256Xts(data.try_into()?)), } } } /// Performs an hkdf according to RFC 5869. /// See [`OpenSSL HKDF`]() /// /// # Errors /// /// This function will return an OpenSSL error if the key could not be generated. pub(crate) fn hkdf_rfc_5869( md: &MdRef, ikm: &[u8], salt: &[u8], info: &[u8], ) -> Result<[u8; COUNT]> { let mut ctx = PkeyCtx::new_id(Id::HKDF)?; ctx.derive_init()?; ctx.set_hkdf_mode(HkdfMode::EXTRACT_THEN_EXPAND)?; ctx.set_hkdf_md(md)?; ctx.set_hkdf_salt(salt)?; ctx.set_hkdf_key(ikm)?; ctx.add_hkdf_info(info)?; let mut res = [0; COUNT]; ctx.derive(Some(&mut res))?; Ok(res) } /// Derive a symmetric AES 256 GCM key from a private and a public key. /// /// # Errors /// /// This function will return an error if something went bad in OpenSSL. pub fn derive_aes256_gcm_key(k1: &PKeyRef, k2: &PKeyRef) -> Result { let mut der = Deriver::new(k1)?; der.set_peer(k2)?; let mut key = der.derive_to_vec()?; key.extend([0, 0, 0, 1]); let secr = Confidential::new(key); // Panic: does not panic as SHA256 digest is 32 bytes long Ok(Aes256GcmKey::new( hash(MessageDigest::sha256(), secr.value())? .as_ref() .try_into() .unwrap(), )) } /// Generate a random array. /// /// # Errors /// /// This function will return an error if the entropy source fails or is not available. pub fn random_array() -> Result<[u8; COUNT]> { let mut rand = [0; COUNT]; rand_bytes(&mut rand)?; Ok(rand) } /// Generate a new random EC key. /// /// # Errors /// /// This function will return an error if the key could not be generated by OpenSSL. pub fn gen_ec_key(nid: Nid) -> Result> { let group = EcGroup::from_curve_name(nid)?; let key: EcKey = EcKey::generate(&group)?; PKey::from_ec_key(key).map_err(Error::Crypto) } /// Result type for an AES encryption in GCM mode.. #[derive(PartialEq, Eq, Debug)] pub struct AeadEncryptionResult { /// The result. /// /// [`Vec`] with the following content: /// 1. `aad` /// 2. `encr(conf)` /// 3. `aes gcm tag` pub(crate) buf: Vec, /// The position of the authenticated data in [`Self::buf`] pub(crate) aad_range: Range, /// The position of the encrypted data in [`Self::buf`] pub(crate) encr_range: Range, /// The position of the tag in [`Self::buf`] pub(crate) tag_range: Range, } /// Result type for an AES decryption in GCM mode.. #[derive(PartialEq, Eq, Debug)] pub struct AeadDecryptionResult { /// The result. /// /// [`Vec`] with the following content: /// 1. `aad` /// 2. `decr(conf)` /// 3. `aes gcm tag` buf: Confidential>, /// The position of the authenticated data in [`Self::buf`] aad_range: Range, /// The position of the authenticated data in [`Self::buf`] data_range: Range, /// The position of the tag in [`Self::buf`] tag_range: Range, } impl AeadEncryptionResult { /// Deconstruct the result to just the resulting data w/o ranges. pub fn into_buf(self) -> Vec { let Self { buf, .. } = self; buf } /// Deconstruct the result into all parts: additional authenticated data, /// cipher data, and tag. #[allow(unused)] // here for completeness pub(crate) fn into_parts(self) -> (Vec, Vec, Vec) { let Self { buf, aad_range, encr_range, tag_range, } = self; ( buf[aad_range].to_vec(), buf[encr_range].to_vec(), buf[tag_range].to_vec(), ) } /// Deconstruct the result to the resulting ciphered data w/o ranges. #[allow(unused)] // here for completeness pub(crate) fn into_cipher(self) -> Vec { let Self { buf, aad_range: _, encr_range, .. } = self; buf[encr_range].to_vec() } } impl AeadDecryptionResult { /// Deconstruct the result to just the resulting data w/o ranges. pub fn into_buf(self) -> Confidential> { let Self { buf, .. } = self; buf } /// Deconstruct the result into all parts: additional data, plain data, and tag. #[allow(unused)] // here for completeness pub(crate) fn into_parts(self) -> (Vec, Confidential>, Vec) { let Self { buf, aad_range, data_range, tag_range, } = self; ( buf.value()[aad_range].to_vec(), Confidential::new(buf.value()[data_range].to_vec()), buf.value()[tag_range].to_vec(), ) } /// Deconstruct the result to the resulting data w/o ranges. #[allow(unused)] // here for completeness pub(crate) fn into_plain(self) -> Confidential> { let Self { buf, aad_range: _, data_range, .. } = self; Confidential::new(buf.value()[data_range].to_vec()) } } /// Encrypt confidential Data with a symmetric key and provida a gcm tag. /// /// * `key` - symmetric key used for encryption /// * `iv` - initialisation vector /// * `aad` - additional authentic data /// * `conf` - data to be encrypted /// * `tag_len` - length of the authentication tag to generate (in bytes) /// /// # Errors /// /// This function will return an error if the data could not be encrypted by OpenSSL. pub fn encrypt_aead( key: &SymKey, iv: &[u8], aad: &[u8], conf: &[u8], ) -> Result { let tag_len = key.key_type().tag_len().ok_or(Error::NoAeadKey)?; let nid = key.key_type().into(); let cipher = Cipher::from_nid(nid).ok_or(Error::UnsupportedCipher(nid))?; let mut tag = vec![0x0u8; tag_len]; let encr = openssl_encrypt_aead(cipher, key.value(), Some(iv), aad, conf, &mut tag)?; let mut buf = vec![0; aad.len() + encr.len() + tag.len()]; let aad_range = Range { start: 0, end: aad.len(), }; let encr_range = Range { start: aad.len(), end: aad.len() + encr.len(), }; let tag_range = Range { start: aad.len() + encr.len(), end: aad.len() + encr.len() + tag.len(), }; buf[aad_range.clone()].copy_from_slice(aad); buf[encr_range.clone()].copy_from_slice(&encr); buf[tag_range.clone()].copy_from_slice(&tag); Ok(AeadEncryptionResult { buf, aad_range, encr_range, tag_range, }) } /// Decrypt encrypted data with a symmetric key compare the GCM-tag. /// /// * `key` - symmetric key used for encryption /// * `iv` - initialisation vector /// * `aad` - additional authenticated data /// * `encr` - encrypted data /// * `tag` - GCM-tag to compare with /// /// # Returns /// [`Vec`] with the decrypted data /// /// # Errors /// /// This function will return an error if the data could not be encrypted by OpenSSL. pub fn decrypt_aead( key: &SymKey, iv: &[u8], aad: &[u8], encr: &[u8], tag: &[u8], ) -> Result { match key { SymKey::Aes256(_) => {} SymKey::Aes256Xts(_) => return Err(Error::NoAeadKey), }; let nid = key.key_type().into(); let cipher = Cipher::from_nid(nid).ok_or(Error::UnsupportedCipher(nid))?; let decr = openssl_decrypt_aead(cipher, key.value(), Some(iv), aad, encr, tag).map_err(|ssl_err| { // Empty error-stack -> no internal ssl error but decryption failed. // Very likely due to a tag mismatch. if ssl_err.errors().is_empty() { Error::GcmTagMismatch } else { Error::Crypto(ssl_err) } })?; let mut conf = Confidential::new(vec![0; aad.len() + decr.len() + tag.len()]); let aad_range = Range { start: 0, end: aad.len(), }; let data_range = Range { start: aad.len(), end: aad.len() + decr.len(), }; let tag_range = Range { start: aad.len() + decr.len(), end: aad.len() + decr.len() + tag.len(), }; let buf = conf.value_mut(); buf[aad_range.clone()].copy_from_slice(aad); buf[data_range.clone()].copy_from_slice(&decr); buf[tag_range.clone()].copy_from_slice(tag); Ok(AeadDecryptionResult { buf: conf, aad_range, data_range, tag_range, }) } /// Calculate the hash of a slice. /// /// # Errors /// /// This function will return an error if OpenSSL could not compute the hash. pub(crate) fn hash(t: MessageDigest, data: &[u8]) -> Result { openssl::hash::hash(t, data).map_err(Error::Crypto) } /// Calculate the HMAC of the given message. pub(crate) fn calculate_hmac( hmac_key: &PKeyRef, dgst: MessageDigest, msg: &[u8], ) -> Result> { match hmac_key.id() { Id::HMAC => Signer::new(dgst, hmac_key)? .sign_oneshot_to_vec(msg) .map_err(Error::Crypto), _ => Err(Error::UnsupportedSigningKey), } } /// Calculate a digital signature scheme. /// /// Calculates the digital signature of the provided message using the signing key. [`Id::EC`], /// and [`Id::RSA`] keys are supported. For [`Id::RSA`] [`Padding::PKCS1_PSS`] is used. /// /// # Errors /// /// This function will return an error if OpenSSL could not compute the signature. pub(crate) fn sign_msg( skey: &PKeyRef, dgst: MessageDigest, msg: &[u8], ) -> Result> { match skey.id() { Id::EC => { let mut sgn = Signer::new(dgst, skey)?; sgn.sign_oneshot_to_vec(msg).map_err(Error::Crypto) } Id::RSA => { let mut sgn = Signer::new(dgst, skey)?; sgn.set_rsa_padding(Padding::PKCS1_PSS)?; sgn.sign_oneshot_to_vec(msg).map_err(Error::Crypto) } _ => Err(Error::UnsupportedSigningKey), } } /// Verify the digital signature of a message. /// /// Verifies the digital signature of the provided message using the signing key. /// [`Id::EC`] and [`Id::RSA`] keys are supported. For [`Id::RSA`] [`Padding::PKCS1_PSS`] is used. /// /// # Returns /// true if signature could be verified, false otherwise /// /// # Errors /// /// This function will return an error if OpenSSL could not compute the signature. pub(crate) fn verify_signature( skey: &PKeyRef, dgst: MessageDigest, msg: &[u8], sign: &[u8], ) -> Result { match skey.id() { Id::EC => { let mut ctx = Verifier::new(dgst, skey)?; ctx.update(msg)?; ctx.verify(sign).map_err(Error::Crypto) } Id::RSA => { let mut ctx = Verifier::new(dgst, skey)?; ctx.set_rsa_padding(Padding::PKCS1_PSS)?; ctx.verify_oneshot(sign, msg).map_err(Error::Crypto) } _ => Err(Error::UnsupportedVerificationKey), } } #[cfg(test)] mod tests { use super::*; use crate::{get_test_asset, test_utils::*, PvCoreError}; #[test] fn sign_ec() { let (ec_key, _) = get_test_keys(); let data = "sample".as_bytes(); let sign = sign_msg(&ec_key, MessageDigest::sha512(), data).unwrap(); assert!(sign.len() <= 139, "value is: {}", sign.len()); assert!(verify_signature(&ec_key, MessageDigest::sha512(), data, &sign).unwrap()); } #[test] fn sign_rsa_2048() { let keypair = get_test_asset!("keys/rsa2048key.pem"); let keypair = PKey::private_key_from_pem(keypair).unwrap(); let data = "sample".as_bytes(); let sign = sign_msg(&keypair, MessageDigest::sha512(), data).unwrap(); assert_eq!(256, sign.len()); assert!(verify_signature(&keypair, MessageDigest::sha512(), data, &sign).unwrap()); } #[test] fn sign_rsa_3072() { let keypair = get_test_asset!("keys/rsa3072key.pem"); let keypair = PKey::private_key_from_pem(keypair).unwrap(); let data = "sample".as_bytes(); let sign = sign_msg(&keypair, MessageDigest::sha512(), data).unwrap(); assert_eq!(384, sign.len()); assert!(verify_signature(&keypair, MessageDigest::sha512(), data, &sign).unwrap()); } #[test] fn derive_aes256_gcm_key() { let (cust_key, host_key) = get_test_keys(); let exp_key: Aes256GcmKey = [ 0x75, 0x32, 0x77, 0x55, 0x8f, 0x3b, 0x60, 0x3, 0x41, 0x9e, 0xf2, 0x49, 0xae, 0x3c, 0x4b, 0x55, 0xaa, 0xd7, 0x7d, 0x9, 0xd9, 0x7f, 0xdd, 0x1f, 0xc8, 0x8f, 0xd8, 0xf0, 0xcf, 0x22, 0xf1, 0x49, ] .into(); let calc_key = super::derive_aes256_gcm_key(&cust_key, &host_key).unwrap(); assert_eq!(&calc_key, &exp_key); } #[test] fn hkdf_rfc_5869() { use openssl::md::Md; // RFC 6869 test vector 1 let ikm = [0x0bu8; 22]; let salt: [u8; 13] = [ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, ]; let info: [u8; 10] = [0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9]; let exp: [u8; 42] = [ 0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a, 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c, 0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf, 0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, 0x58, 0x65, ]; let res: [u8; 42] = super::hkdf_rfc_5869(Md::sha256(), &ikm, &salt, &info).unwrap(); assert_eq!(exp, res); } #[test] fn encrypt_decrypt_aes_256_gcm() { let aes_gcm_key = [ 0xee, 0xbc, 0x1f, 0x57, 0x48, 0x7f, 0x51, 0x92, 0x1c, 0x04, 0x65, 0x66, 0x5f, 0x8a, 0xe6, 0xd1, 0x65, 0x8b, 0xb2, 0x6d, 0xe6, 0xf8, 0xa0, 0x69, 0xa3, 0x52, 0x02, 0x93, 0xa5, 0x72, 0x07, 0x8f, ]; let aes_gcm_iv = [ 0x99, 0xaa, 0x3e, 0x68, 0xed, 0x81, 0x73, 0xa0, 0xee, 0xd0, 0x66, 0x84, ]; let aes_gcm_plain = Confidential::new(vec![ 0xf5, 0x6e, 0x87, 0x05, 0x5b, 0xc3, 0x2d, 0x0e, 0xeb, 0x31, 0xb2, 0xea, 0xcc, 0x2b, 0xf2, 0xa5, ]); let aes_gcm_aad = [ 0x4d, 0x23, 0xc3, 0xce, 0xc3, 0x34, 0xb4, 0x9b, 0xdb, 0x37, 0x0c, 0x43, 0x7f, 0xec, 0x78, 0xde, ]; let aes_gcm_ciphertext = [ 0xf7, 0x26, 0x44, 0x13, 0xa8, 0x4c, 0x0e, 0x7c, 0xd5, 0x36, 0x86, 0x7e, 0xb9, 0xf2, 0x17, 0x36, ]; let aes_gcm_tag = [ 0x67, 0xba, 0x05, 0x10, 0x26, 0x2a, 0xe4, 0x87, 0xd7, 0x37, 0xee, 0x62, 0x98, 0xf7, 0x7e, 0x0c, ]; let aes_gcm_res = [aes_gcm_aad, aes_gcm_ciphertext, aes_gcm_tag].concat(); let key = SymKey::Aes256(aes_gcm_key.into()); let AeadEncryptionResult { buf, aad_range, encr_range, tag_range, } = encrypt_aead(&key, &aes_gcm_iv, &aes_gcm_aad, aes_gcm_plain.value()).unwrap(); assert_eq!(buf, aes_gcm_res); let conf = decrypt_aead( &key, &aes_gcm_iv, &buf[aad_range], &buf[encr_range], &buf[tag_range], ) .unwrap(); assert_eq!(&conf.buf.value()[conf.aad_range], &aes_gcm_aad); assert_eq!(&conf.buf.value()[conf.data_range], aes_gcm_plain.value()); assert_eq!(&conf.buf.value()[conf.tag_range], &aes_gcm_tag); let (aad, ciphertext, tag) = encrypt_aead(&key, &aes_gcm_iv, &aes_gcm_aad, aes_gcm_plain.value()) .unwrap() .into_parts(); assert_eq!(aes_gcm_aad, aad.as_slice()); assert_eq!(aes_gcm_ciphertext, ciphertext.as_slice()); assert_eq!(aes_gcm_tag, tag.as_slice()); let (aad2, plaintext, tag2) = decrypt_aead(&key, &aes_gcm_iv, &aad, &ciphertext, &tag) .unwrap() .into_parts(); assert_eq!(aes_gcm_aad, aad2.as_slice()); assert_eq!(aes_gcm_plain, plaintext); assert_eq!(aes_gcm_tag, tag2.as_slice()); } #[test] fn aes_gcm_fails_wrong_keytype() { let aes_gcm_iv = [ 0x99, 0xaa, 0x3e, 0x68, 0xed, 0x81, 0x73, 0xa0, 0xee, 0xd0, 0x66, 0x84, ]; let aes_gcm_plain = Confidential::new(vec![ 0xf5, 0x6e, 0x87, 0x05, 0x5b, 0xc3, 0x2d, 0x0e, 0xeb, 0x31, 0xb2, 0xea, 0xcc, 0x2b, 0xf2, 0xa5, ]); let aes_gcm_aad = [ 0x4d, 0x23, 0xc3, 0xce, 0xc3, 0x34, 0xb4, 0x9b, 0xdb, 0x37, 0x0c, 0x43, 0x7f, 0xec, 0x78, 0xde, ]; let key = SymKey::random(SymKeyType::Aes256Xts).unwrap(); encrypt_aead(&key, &aes_gcm_iv, &aes_gcm_aad, aes_gcm_plain.value()).expect_err(""); } #[test] fn hmac_sha512_rfc_4868() { // use a test vector with key=64bytes of RFC 4868: // https://www.rfc-editor.org/rfc/rfc4868.html#section-2.7.2.3 let key = [0xb; 64]; let data = [0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65]; let exp = vec![ 0x63, 0x7e, 0xdc, 0x6e, 0x01, 0xdc, 0xe7, 0xe6, 0x74, 0x2a, 0x99, 0x45, 0x1a, 0xae, 0x82, 0xdf, 0x23, 0xda, 0x3e, 0x92, 0x43, 0x9e, 0x59, 0x0e, 0x43, 0xe7, 0x61, 0xb3, 0x3e, 0x91, 0x0f, 0xb8, 0xac, 0x28, 0x78, 0xeb, 0xd5, 0x80, 0x3f, 0x6f, 0x0b, 0x61, 0xdb, 0xce, 0x5e, 0x25, 0x1f, 0xf8, 0x78, 0x9a, 0x47, 0x22, 0xc1, 0xbe, 0x65, 0xae, 0xa4, 0x5f, 0xd4, 0x64, 0xe8, 0x9f, 0x8f, 0x5b, ]; let pkey = PKey::hmac(&key).unwrap(); let hmac = calculate_hmac(&pkey, MessageDigest::sha512(), &data).unwrap(); assert_eq!(hmac, exp); } #[test] fn from_symkeytype() { assert_eq!( >::into(SymKeyType::Aes256Gcm), Nid::AES_256_GCM ); assert_eq!( >::into(SymKeyType::Aes256Xts), Nid::AES_256_XTS ); } #[test] fn key_type() { assert_eq!( SymKey::random(SymKeyType::Aes256Gcm).unwrap().key_type(), SymKeyType::Aes256Gcm ); assert_eq!( SymKey::random(SymKeyType::Aes256Xts).unwrap().key_type(), SymKeyType::Aes256Xts ); } #[test] fn try_from_and_into() { let data = [0x1u8; 32]; let key: SymKey = Aes256GcmKey::new(data).into(); assert_eq!(key.value(), &data); let key_aes: Aes256GcmKey = key.try_into().expect("should not fail"); assert_eq!(key_aes.value(), &data); } #[test] fn try_from_data() { let data = [0x3u8; 32]; let key = SymKey::try_from_data(SymKeyType::Aes256Gcm, Confidential::new(data.into())) .expect("should not fail"); assert_eq!(&data, key.value()); let key_aes: Aes256GcmKey = key.try_into().expect("should not fail"); assert_eq!(&data, key_aes.value()); assert!(matches!( SymKey::try_from_data(SymKeyType::Aes256Gcm, Confidential::new([0x4u8; 33].into())), Err(Error::PvCore(PvCoreError::LengthMismatch { expected: 32, actual: 33 })) )); } } s390-tools-2.38.0/rust/pv/src/error.rs000066400000000000000000000125231502674226300173620ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023, 2024 use std::path::PathBuf; use crate::secret::UserDataType; /// Result type for this crate pub type Result = std::result::Result; /// Error cases for this crate #[allow(missing_docs)] #[derive(thiserror::Error, Debug)] #[non_exhaustive] pub enum Error { #[error("Invalid SE header provided")] InvBootHdr, #[error("Host-key verification failed: {0}")] HkdVerify(HkdVerifyErrorType), #[error("No host-key provided")] NoHostkey, #[error("Too many host-keys provided")] ManyHostkeys, #[error("Cannot load {ty} from {path}")] X509Load { path: PathBuf, ty: &'static str, source: openssl::error::ErrorStack, }, #[error("Internal (unexpected) error: {0}, caused by {1}")] InternalSsl(&'static str, #[source] openssl::error::ErrorStack), #[error("Signing is only supported for EC and RSA keys")] UnsupportedSigningKey, #[error("Verifying signatures is only supported for EC and RSA keys")] UnsupportedVerificationKey, #[error("Provided binary request is too small")] BinRequestSmall, #[error("No Configuration UID found: {0}")] NoCuid(String), // errors from request types #[error("Customer Communication Key must be 32 bytes long")] CckSize, #[error("Decryption failed. Probably due to a GCM tag mismatch.")] GcmTagMismatch, #[error("Invalid {0} user-data for signing provided. Max {max} bytes allowed", max=.0.max())] AsrcbInvSgnUserData(UserDataType), #[error("Unsupported user data signing key provided. Only EC(secp521r1) and RSA(2048 & 3072 bit) are supported")] BinAsrcbUnsupportedUserDataSgnKey, #[error("No user-key for verification provided and user-data is signed")] BinAsrcbNoUserDataSgnKey, #[error("Input does not contain an add-secret request version 1")] BinAsrcbInvVersion, #[error("Provided user-data key type ({key}) does not match with the user-data ({kind})")] AsrcbUserDataKeyMismatch { key: String, kind: UserDataType }, #[error( "The user-defined request signature could not be verified with the provided certificate" )] AsrcbUserDataSgnFail, #[error("The provided Host Key Document in '{hkd}' is not in PEM or DER format")] HkdNotPemOrDer { hkd: String, source: openssl::error::ErrorStack, }, #[error("The provided host key document in {0} contains no certificate!")] NoHkdInFile(String), #[error("Invalid input size ({0}) for boot hdr")] InvBootHdrSize(usize), #[error("Input does not contain an attestation request")] NoArcb, #[error("The attestation request has an unknown version (.0)")] BinArcbInvVersion(u32), #[error( "The attestation request encrypted sice is to0 small (.0). Request probably tampered with." )] BinArcbSeaSmall(u32), #[error("The input is missing the Configuration UID entry. It is probably not an attestation response")] AttExCuidMissing, #[error( "Attestation flags indicating that the additional data contains {0}, but no data was provided." )] AddDataMissing(&'static str), #[error("An ASCII string was expected, but non-ASCII characters were received.")] NonAscii, #[error("Incorrect {what} for a {kind}. Is: {value}; expected: {exp}")] RetrInvKey { what: &'static str, kind: String, value: String, exp: String, }, #[error("Invalid data from OpenSSL")] InvalSslData, // errors from other crates #[error(transparent)] PvCore(#[from] pv_core::Error), #[error(transparent)] Io(#[from] std::io::Error), #[error(transparent)] Crypto(#[from] openssl::error::ErrorStack), #[error(transparent)] Curl(#[from] curl::Error), #[error("No Authenticated Encryption with Associated Data (AEAD) key")] NoAeadKey, #[error("Unsupported cipher: {:?}", .0.as_raw())] UnsupportedCipher(Nid), } // used in macros #[doc(hidden)] impl Error { pub const CERT: &'static str = "certificate"; pub const CRL: &'static str = "CRL"; } /// Error cases for verifying host-key documents #[allow(missing_docs)] #[derive(thiserror::Error, Debug, PartialEq, Eq)] #[non_exhaustive] pub enum HkdVerifyErrorType { #[error("Signature verification failed")] Signature, #[error("No valid CRL found")] NoCrl, #[error("Host-key document is revoked.")] HkdRevoked, #[error("Not enough bits of security. ({0}, {1} expected)")] SecurityBits(u32, u32), #[error("Authority Key Id mismatch")] Akid, #[error("CRL has no validity period")] NoValidityPeriod, #[error("Specify one IBM Z signing key")] NoIbmSignKey, #[error("Specify only one IBM Z signing key")] ManyIbmSignKeys, #[error("Before validity period")] BeforeValidity, #[error("After validity period")] AfterValidity, #[error("Issuer mismatch")] IssuerMismatch, #[error("No CRL distribution points found")] NoCrlDP, #[error("The IBM Z signing key could not be verified. Error occurred at level {1}")] IbmSignInvalid(#[source] openssl::x509::X509VerifyResult, u32), } macro_rules! bail_hkd_verify { ($var: tt) => { return Err($crate::Error::HkdVerify($crate::HkdVerifyErrorType::$var)) }; } pub(crate) use bail_hkd_verify; use openssl::nid::Nid; s390-tools-2.38.0/rust/pv/src/lib.rs000066400000000000000000000067461502674226300170110ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023, 2024 #![doc = include_str!("../README.md")] //! # Library for Protected Virtualization (PV) related tools //! //! This crate provides functionalities for creating add-secret requests. Also provides support for //! sending those requests, list all stored secrets, and lock the secret store. //! //! ## Create //! [`secret::AddSecretRequest`] //! //! ## Add //! [`uv::UvDevice`] and [`uv::AddCmd`] //! //! ## List //! [`uv::UvDevice`] and [`uv::ListCmd`] //! //! ## Lock //! [`uv::UvDevice`] and [`uv::LockCmd`] //! //! # Attestation //! //! This crate provides functionalities for creating, performing, and verifying Attestation //! measurements for _IBM Secure Execution for Linux_. See: //! //! ## Create //! [`attest::AttestationRequest`] //! //! ## Perform //! [`uv::UvDevice`] and [`uv::AttestationCmd`] //! //! # Verify //! [`attest::AttestationItems`], [`attest::AttestationMeasurement`] mod brcb; mod crypto; mod error; mod openssl_extensions; mod pem_utils; mod req; mod utils; mod uvattest; mod uvsecret; mod verify; /// utility functions for writing TESTS!!! // hide any test helpers on docs! #[doc(hidden)] #[allow(dead_code)] pub mod test_utils; pub use pv_core::{assert_size, static_assert}; const PAGESIZE: usize = 0x1000; /// Definitions and functions for interacting with the Ultravisor pub mod uv { pub use pv_core::uv::*; } /// Functionalities for creating attestation requests pub mod attest { pub use pv_core::attest::*; pub use crate::uvattest::{ additional::AdditionalData, arcb::{ AttestationAuthenticated, AttestationFlags, AttestationRequest, AttestationVersion, }, attest::{AttestationItems, AttestationMeasurement}, }; } /// Definitions and functions to write objects in PEM format pub mod pem { pub use crate::pem_utils::Pem; } /// Miscellaneous functions and definitions pub mod misc { pub use pv_core::misc::*; pub use crate::utils::read_certs; } pub use error::{Error, Result}; pub use pv_core::{Error as PvCoreError, FileAccessErrorType, FileIoErrorType}; pub use crate::error::HkdVerifyErrorType; /// Functionalities to build UV requests pub mod request { pub use crate::{ brcb::{seek_se_hdr_start, BootHdrTags, SeImgMetaData}, crypto::{ decrypt_aead, derive_aes256_gcm_key, encrypt_aead, gen_ec_key, random_array, AeadDecryptionResult, AeadEncryptionResult, Aes256GcmKey, Aes256XtsKey, SymKey, SymKeyType, SHA_512_HASH_LEN, }, req::{EcPubKeyCoord, Encrypt, Keyslot, ReqEncrCtx, Request}, verify::{CertVerifier, HkdVerifier, NoVerifyHkd}, }; /// Reexports some useful OpenSSL symbols pub mod openssl { pub use openssl::{error::ErrorStack, hash::DigestBytes, nid::Nid, pkey, x509}; // rust-OpenSSL does not define these NIDs #[allow(missing_docs)] pub const NID_ED25519: Nid = Nid::from_raw(openssl_sys::NID_ED25519); #[allow(missing_docs)] pub const NID_ED448: Nid = Nid::from_raw(openssl_sys::NID_ED448); } pub use pv_core::request::*; } /// Functionalities for creating add-secret requests pub mod secret { pub use pv_core::secret::*; pub use crate::uvsecret::{ asrcb::{AddSecretFlags, AddSecretRequest, AddSecretVersion}, ext_secret::ExtSecret, guest_secret::GuestSecret, retr_secret::{IbmProtectedKey, RetrievedSecret}, user_data::verify_asrcb_and_get_user_data, }; } s390-tools-2.38.0/rust/pv/src/openssl_extensions/000077500000000000000000000000001502674226300216225ustar00rootroot00000000000000s390-tools-2.38.0/rust/pv/src/openssl_extensions/akid.rs000066400000000000000000000053331502674226300231040ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 use std::fmt; use foreign_types::{foreign_type, ForeignType, ForeignTypeRef}; use openssl::x509::{X509CrlRef, X509Ref}; use std::ffi::c_int; mod ffi { extern "C" { pub fn X509_check_akid( issuer: *const openssl_sys::X509, akid: *const openssl_sys::AUTHORITY_KEYID, ) -> super::c_int; } } foreign_type! { type CType = openssl_sys::AUTHORITY_KEYID; fn drop = openssl_sys::AUTHORITY_KEYID_free; /// An `Authority Key Identifier`. pub struct Akid; /// Reference to `Akid` pub struct AkidRef; } #[derive(Copy, Clone, PartialEq, Eq)] pub struct AkidCheckResult(c_int); impl fmt::Debug for AkidCheckResult { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt.debug_struct("AkidCheckResult") .field("code", &self.0) .finish() } } impl AkidCheckResult { pub const OK: Self = Self(openssl_sys::X509_V_OK); /// Creates an `AkidCheckResult` from a raw error number. unsafe fn from_raw(err: c_int) -> Self { Self(err) } } impl AkidRef { /// Check if the `Akid` matches the issuer pub fn check(&self, issuer: &X509Ref) -> AkidCheckResult { unsafe { let res = ffi::X509_check_akid(issuer.as_ptr(), self.as_ptr()); AkidCheckResult::from_raw(res) } } } pub trait AkidExtension { fn akid(&self) -> Option; } impl AkidExtension for X509Ref { fn akid(&self) -> Option { unsafe { let ptr = openssl_sys::X509_get_ext_d2i( self.as_ptr(), openssl_sys::NID_authority_key_identifier, std::ptr::null_mut(), std::ptr::null_mut(), ); if ptr.is_null() { None } else { Some(Akid::from_ptr(ptr as *mut _)) } } } } impl AkidExtension for X509CrlRef { fn akid(&self) -> Option { unsafe { let ptr = openssl_sys::X509_CRL_get_ext_d2i( self.as_ptr(), openssl_sys::NID_authority_key_identifier, std::ptr::null_mut(), std::ptr::null_mut(), ); if ptr.is_null() { None } else { Some(Akid::from_ptr(ptr as *mut _)) } } } } #[cfg(test)] mod test { use crate::test_utils::load_gen_cert; use super::*; #[test] fn akid() { let cert = load_gen_cert("ibm.crt"); let ca = load_gen_cert("root_ca.crt"); let akid = cert.akid().unwrap(); let res = akid.check(&ca); assert_eq!(res, AkidCheckResult::OK); } } s390-tools-2.38.0/rust/pv/src/openssl_extensions/bio.rs000066400000000000000000000046661502674226300227550ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use core::slice; use openssl::error::ErrorStack; use openssl_sys::BIO_new_mem_buf; use std::ffi::c_int; use std::{marker::PhantomData, ptr}; pub struct BioMem(*mut openssl_sys::BIO); impl Drop for BioMem { fn drop(&mut self) { // SAFETY: Pointer is valid. The pointer value is dropped after the free. unsafe { openssl_sys::BIO_free_all(self.0); } } } impl BioMem { pub fn new() -> Result { openssl_sys::init(); // SAFETY: Returns a valid pointer or null. null-case is tested right after this. let bio = unsafe { openssl_sys::BIO_new(openssl_sys::BIO_s_mem()) }; match bio.is_null() { true => Err(ErrorStack::get()), false => Ok(Self(bio)), } } pub fn as_ptr(&self) -> *mut openssl_sys::BIO { self.0 } /// Copies the content of this slice into a Vec pub fn to_vec(&self) -> Vec { let buf; // SAFTEY: BIO provides a continuous memory that can be used to build a slice. unsafe { let mut ptr = ptr::null_mut(); let len = openssl_sys::BIO_get_mem_data(self.0, &mut ptr); buf = slice::from_raw_parts(ptr as *const _ as *const _, len as usize) } buf.to_vec() } } pub struct BioMemSlice<'a>(*mut openssl_sys::BIO, PhantomData<&'a [u8]>); impl Drop for BioMemSlice<'_> { fn drop(&mut self) { // SAFETY: Pointer is valid. The pointer value is dropped after the free. unsafe { openssl_sys::BIO_free_all(self.0); } } } impl<'a> BioMemSlice<'a> { pub fn new(buf: &'a [u8]) -> Result, ErrorStack> { openssl_sys::init(); // SAFETY: `buf` is a slice (i.e. pointer+size) pointing to a valid memory region. // So the resulting bio is valid. Lifetime of the slice is connected by this Rust // structure. assert!(buf.len() <= c_int::MAX as usize); let bio = unsafe { { let r = BIO_new_mem_buf(buf.as_ptr() as *const _, buf.len() as c_int); match r.is_null() { true => Err(ErrorStack::get()), false => Ok(r), } }? }; Ok(BioMemSlice(bio, PhantomData)) } pub fn as_ptr(&self) -> *mut openssl_sys::BIO { self.0 } } s390-tools-2.38.0/rust/pv/src/openssl_extensions/crl.rs000066400000000000000000000065471502674226300227640ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 pub use crate::openssl_extensions::stackable_crl::*; use foreign_types::{ForeignType, ForeignTypeRef}; use openssl::{ error::ErrorStack, stack::{Stack, StackRef}, x509::{ store::{X509StoreBuilderRef, X509StoreRef}, X509CrlRef, X509NameRef, X509Ref, X509StoreContextRef, X509, }, }; pub fn opt_to_ptr(o: Option<&T>) -> *mut T::CType { match o { None => std::ptr::null_mut(), Some(p) => p.as_ptr(), } } mod ffi { extern "C" { pub fn X509_STORE_CTX_get1_crls( ctx: *mut openssl_sys::X509_STORE_CTX, nm: *mut openssl_sys::X509_NAME, ) -> *mut openssl_sys::stack_st_X509_CRL; pub fn X509_STORE_add_crl( xs: *mut openssl_sys::X509_STORE, x: *mut openssl_sys::X509_CRL, ) -> std::ffi::c_int; } } pub trait X509StoreExtension { fn add_crl(&mut self, crl: &X509CrlRef) -> Result<(), ErrorStack>; } impl X509StoreExtension for X509StoreBuilderRef { fn add_crl(&mut self, crl: &X509CrlRef) -> Result<(), ErrorStack> { unsafe { { let r = ffi::X509_STORE_add_crl(self.as_ptr(), crl.as_ptr()); if r <= 0 { Err(ErrorStack::get()) } else { Ok(()) } } } } } pub trait X509StoreContextExtension { fn init_opt( &mut self, trust: &X509StoreRef, cert: Option<&X509Ref>, cert_chain: Option<&StackRef>, with_context: F, ) -> Result where F: FnOnce(&mut X509StoreContextRef) -> Result; fn crls(&mut self, subj: &X509NameRef) -> Result, ErrorStack>; } impl X509StoreContextExtension for X509StoreContextRef { fn init_opt( &mut self, trust: &X509StoreRef, cert: Option<&X509Ref>, cert_chain: Option<&StackRef>, with_context: F, ) -> Result where F: FnOnce(&mut Self) -> Result, { struct Cleanup<'a>(&'a mut X509StoreContextRef); impl Drop for Cleanup<'_> { fn drop(&mut self) { unsafe { openssl_sys::X509_STORE_CTX_cleanup(self.0.as_ptr()); } } } unsafe { { let r = openssl_sys::X509_STORE_CTX_init( self.as_ptr(), trust.as_ptr(), opt_to_ptr(cert), opt_to_ptr(cert_chain), ); if r <= 0 { Err(ErrorStack::get()) } else { Ok(r) } }?; } let cleanup = Cleanup(self); with_context(cleanup.0) } /// Get all Certificate Revocation Lists with the subject currently stored fn crls(&mut self, subj: &X509NameRef) -> Result, ErrorStack> { unsafe { { let r = ffi::X509_STORE_CTX_get1_crls(self.as_ptr(), subj.as_ptr()); if r.is_null() { Err(ErrorStack::get()) } else { Ok(Stack::from_ptr(r)) } } } } } s390-tools-2.38.0/rust/pv/src/openssl_extensions/mod.rs000066400000000000000000000003331502674226300227460ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 #![doc(hidden)] /// Extensions to the rust-openssl crate mod akid; mod bio; mod crl; mod stackable_crl; pub use akid::*; pub use bio::*; pub use crl::*; s390-tools-2.38.0/rust/pv/src/openssl_extensions/stackable_crl.rs000066400000000000000000000057071502674226300247720ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 use crate::openssl_extensions::bio::BioMemSlice; use foreign_types::{ForeignType, ForeignTypeRef}; use openssl::{ error::ErrorStack, stack::Stackable, x509::{X509Crl, X509CrlRef}, }; use std::ptr; #[derive(Debug)] pub struct StackableX509Crl(*mut openssl_sys::X509_CRL); impl ForeignType for StackableX509Crl { type CType = openssl_sys::X509_CRL; type Ref = X509CrlRef; unsafe fn from_ptr(ptr: *mut openssl_sys::X509_CRL) -> Self { Self(ptr) } fn as_ptr(&self) -> *mut openssl_sys::X509_CRL { self.0 } } impl Drop for StackableX509Crl { fn drop(&mut self) { unsafe { (openssl_sys::X509_CRL_free)(self.0) } } } impl ::std::ops::Deref for StackableX509Crl { type Target = X509CrlRef; fn deref(&self) -> &X509CrlRef { unsafe { ForeignTypeRef::from_ptr(self.0) } } } impl ::std::ops::DerefMut for StackableX509Crl { fn deref_mut(&mut self) -> &mut X509CrlRef { unsafe { ForeignTypeRef::from_ptr_mut(self.0) } } } #[allow(clippy::explicit_auto_deref)] impl ::std::borrow::Borrow for StackableX509Crl { fn borrow(&self) -> &X509CrlRef { &**self } } #[allow(clippy::explicit_auto_deref)] impl ::std::convert::AsRef for StackableX509Crl { fn as_ref(&self) -> &X509CrlRef { &**self } } impl Stackable for StackableX509Crl { type StackType = openssl_sys::stack_st_X509_CRL; } impl StackableX509Crl { pub fn stack_from_pem(pem: &[u8]) -> Result, ErrorStack> { unsafe { openssl_sys::init(); let bio = BioMemSlice::new(pem)?; let mut crls = vec![]; loop { let r = openssl_sys::PEM_read_bio_X509_CRL( bio.as_ptr(), ptr::null_mut(), None, ptr::null_mut(), ); if r.is_null() { let err = openssl_sys::ERR_peek_last_error(); if openssl_sys::ERR_GET_LIB(err) == openssl_sys::ERR_LIB_PEM && openssl_sys::ERR_GET_REASON(err) == openssl_sys::PEM_R_NO_START_LINE { openssl_sys::ERR_clear_error(); break; } return Err(ErrorStack::get()); } else { crls.push(X509Crl::from_ptr(r)); } } Ok(crls) } } } impl From for StackableX509Crl { fn from(value: X509Crl) -> Self { unsafe { openssl_sys::X509_CRL_up_ref(value.as_ptr()); Self::from_ptr(value.as_ptr()) } } } impl From for X509Crl { fn from(value: StackableX509Crl) -> Self { unsafe { openssl_sys::X509_CRL_up_ref(value.as_ptr()); Self::from_ptr(value.as_ptr()) } } } s390-tools-2.38.0/rust/pv/src/pem_utils.rs000066400000000000000000000140001502674226300202220ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use crate::Result; use crate::{openssl_extensions::BioMem, Error}; use openssl::error::ErrorStack; use pv_core::request::Confidential; use std::{ ffi::{c_char, CString}, fmt::Display, }; mod ffi { use openssl_sys::BIO; use std::ffi::{c_char, c_int, c_long, c_uchar}; extern "C" { pub fn PEM_write_bio( bio: *mut BIO, name: *const c_char, header: *const c_char, data: *const c_uchar, len: c_long, ) -> c_int; } } /// Thin wrapper around [`CString`] only containing ASCII chars. #[derive(Debug)] struct AsciiCString(CString); impl AsciiCString { /// Convert from string /// /// # Returns /// Error if string is not ASCII or contains null chars pub(crate) fn from_str(s: &str) -> Result { match s.is_ascii() { true => Ok(Self(CString::new(s).map_err(|_| Error::NonAscii)?)), false => Err(Error::NonAscii), } } fn as_ptr(&self) -> *const c_char { self.0.as_ptr() } } /// Helper struct to construct the PEM format #[derive(Debug)] struct InnerPem<'d> { name: AsciiCString, header: Option, data: &'d [u8], } impl<'d> InnerPem<'d> { fn new(name: &str, header: Option, data: &'d [u8]) -> Result { Ok(Self { name: AsciiCString::from_str(name)?, header: match header { Some(h) => Some(AsciiCString::from_str(&h)?), None => None, }, data, }) } /// Generate PEM representation of the data fn to_pem(&self) -> Result> { let bio = BioMem::new()?; let hdr_ptr = match self.header { // avoid moving variable -> use reference Some(ref h) => h.as_ptr(), None => std::ptr::null(), }; // SAFETY: // All pointers point to valid C strings or memory regions let rc = unsafe { ffi::PEM_write_bio( bio.as_ptr(), self.name.as_ptr(), hdr_ptr, self.data.as_ptr(), self.data.len() as std::ffi::c_long, ) }; match rc { 1 => Err(Error::InternalSsl("Could not write PEM", ErrorStack::get())), _ => Ok(bio.to_vec()), } } } /// Data in PEM format /// /// Displays into a printable PEM structure. /// Must be constructed from another structure in this library. /// /// ```rust,ignore /// let pem: Pem = ...; /// println!("PEM {pem}"); /// ``` /// ```PEM ///-----BEGIN ----- ///
/// /// ///-----END ----- #[derive(Debug)] pub struct Pem { pem: Confidential, } #[allow(unused)] impl Pem { /// Create a new PEM structure. /// /// # Errors /// /// This function will return an error if name or header contain non-ASCII chars, or OpenSSL /// could not generate the PEM (very likely due to OOM). pub(crate) fn new(name: &str, header: H, data: D) -> Result where D: AsRef<[u8]>, H: Into>, { let mut header = header.into(); let header = match header { Some(h) if h.ends_with('\n') => Some(h), Some(h) if h.is_empty() => None, Some(mut h) => { h.push('\n'); Some(h) } None => None, }; let inner_pem = InnerPem::new(name, header, data.as_ref())?; // Create the PEM format eagerly so that to_string/display cannot fail because of ASCII or OpenSSL Errors // Both error should be very unlikely // OpenSSL should be able to create PEM if there is enough memory and produce a non-null // terminated ASCII-string // Unwrap succeeds it's all ASCII // Std lib implements all the conversations without a copy let pem = CString::new(inner_pem.to_pem()?) .map_err(|_| Error::NonAscii)? .into_string() .unwrap() .into(); Ok(Self { pem }) } /// Converts the PEM-data into a byte vector. /// /// This consumes the `PEM`. #[inline] #[must_use = "`self` will be dropped if the result is not used"] pub fn into_bytes(self) -> Confidential> { self.pem.into_inner().into_bytes().into() } } impl Display for Pem { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.pem.value().fmt(f) } } #[cfg(test)] mod test { use super::*; #[test] fn no_data() { const EXP: &str = "-----BEGIN PEM test-----\ntest hdr value: 17\n\n-----END PEM test-----\n"; let test_pem = Pem::new("PEM test", "test hdr value: 17".to_string(), []).unwrap(); let pem_str = test_pem.to_string(); assert_eq!(pem_str, EXP); } #[test] fn no_hdr() { const EXP: &str = "-----BEGIN PEM test-----\ndmVyeSBzZWNyZXQga2V5\n-----END PEM test-----\n"; let test_pem = Pem::new("PEM test", None, "very secret key").unwrap(); let pem_str = test_pem.to_string(); assert_eq!(pem_str, EXP); } #[test] fn some_data() { const EXP: &str= "-----BEGIN PEM test-----\ntest hdr value: 17\n\ndmVyeSBzZWNyZXQga2V5\n-----END PEM test-----\n"; let test_pem = Pem::new( "PEM test", "test hdr value: 17".to_string(), "very secret key", ) .unwrap(); let pem_str = test_pem.to_string(); assert_eq!(pem_str, EXP); } #[test] fn data_linebreak() { const EXP: &str= "-----BEGIN PEM test-----\ntest hdr value: 17\n\ndmVyeSBzZWNyZXQga2V5\n-----END PEM test-----\n"; let test_pem = Pem::new( "PEM test", "test hdr value: 17\n".to_string(), "very secret key", ) .unwrap(); let pem_str = test_pem.to_string(); assert_eq!(pem_str, EXP); } } s390-tools-2.38.0/rust/pv/src/req.rs000066400000000000000000000561301502674226300170220ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 use std::mem::size_of; use openssl::{ bn::{BigNum, BigNumContext}, ec::{EcGroup, EcGroupRef, EcKey, EcPointRef}, error::ErrorStack, hash::{DigestBytes, MessageDigest}, nid::Nid, pkey::{PKey, PKeyRef, Private, Public}, }; use pv_core::request::{RequestMagic, RequestVersion}; use zerocopy::{BigEndian, FromBytes, Immutable, IntoBytes, KnownLayout, U32}; use crate::{ assert_size, crypto::{ decrypt_aead, derive_aes256_gcm_key, encrypt_aead, gen_ec_key, hash, random_array, AeadEncryptionResult, SymKey, SymKeyType, }, misc::to_u32, request::Confidential, Error, Result, }; /// Encrypt a _secret_ using self and a given private key. pub trait Encrypt { /// Encrypts `secret` using `self` and `priv_key` the encryption. /// /// # Returns /// the encrypted data. /// /// # Errors /// /// This function will return an error if OpenSSL could not encrypt the secret. fn encrypt(&self, secret: &[u8], priv_key: &PKeyRef) -> Result> { let mut res = Vec::with_capacity(80); self.encrypt_to(secret, priv_key, &mut res)?; Ok(res) } /// Encrypts `secret` using `self` and `priv_key` the encryption. /// Appends the encrypted data to `to` /// /// # Returns /// The encrypted data. /// /// # Errors /// /// This function will return an error if OpenSSL could not encrypt the secret. fn encrypt_to( &self, secret: &[u8], priv_key: &PKeyRef, to: &mut Vec, ) -> Result<()>; } /// Types of Authenticated Data #[allow(missing_debug_implementations)] pub enum Aad<'a> { /// Authenticated Keyslot Ks(&'a Keyslot), /// Unchanged authenticated data Plain(&'a [u8]), /// Authenticated data that has to be encrypted in beforehand Encr(&'a dyn Encrypt), } /// IBM Z Host key-slot /// /// Layout in binary format: /// ```none /// _______________________________________________________________ /// | Public Host Key Hash (32) | /// | Wrapped(=Encrypted) Request Protection Key(32) | /// | Key Slot Tag (16) | /// |_____________________________________________________________| /// ``` #[derive(Debug, Clone)] pub struct Keyslot(PKey); impl Keyslot { /// Size of a host-key hash pub const PHKH_SIZE: u32 = 0x20; /// Creates a new Keyslot from the provided public key pub fn new(hostkey: PKey) -> Self { Self(hostkey) } } impl Encrypt for Keyslot { /// Encrypts the given request protection key `prot_key`. /// /// The AES256 encryption key is derived from `self` as public key, and `priv_key` as private /// key. /// /// # Returns /// The encrypted Keyslot. /// /// # Errors /// /// This function will return an error if OpenSSL could not encrypt the secret. fn encrypt_to( &self, prot_key: &[u8], priv_key: &PKeyRef, to: &mut Vec, ) -> Result<()> { let derived_key = derive_aes256_gcm_key(priv_key, &self.0)?; let mut wrpk_and_kst = encrypt_aead(&derived_key.into(), &[0; 12], &[], prot_key)?.into_buf(); let phk: EcPubKeyCoord = self.0.as_ref().try_into()?; to.reserve(80); to.extend_from_slice(&hash(MessageDigest::sha256(), phk.as_ref())?); to.append(&mut wrpk_and_kst); Ok(()) } } /// Context used to manage the encryption of requests. /// Intended to be used by [`Request`] implementations #[derive(Debug)] pub struct ReqEncrCtx { iv: [u8; 12], priv_key: PKey, prot_key: SymKey, } impl ReqEncrCtx { /// Create a new encryption context that uses AES256. /// /// * `iv` - Initialization vector for the request encryption /// * `priv_key` - Private key to wrap [`Keyslot`] /// * `prot_key` - Symmetric key for request encryption. Part of [`Keyslot`] /// /// If an argument is set to `None` a ranom is generated /// /// # Errors /// /// This function will return an error if OpenSSL could not generate a random value. pub fn new_aes_256(iv: I, priv_key: P, prot_key: S) -> Result where I: Into>, P: Into>>, S: Into>, { let iv = iv.into().unwrap_or(random_array()?); let priv_key = priv_key.into().unwrap_or(gen_ec_key(Nid::SECP521R1)?); let prot_key = prot_key .into() .unwrap_or(SymKey::random(SymKeyType::Aes256Gcm)?); Ok(Self { iv, priv_key, prot_key, }) } /// Create a new encryption context with random input values. /// /// # Errors /// /// This function will return an error if OpenSSL could not generate a random value. pub fn random(ket_tp: SymKeyType) -> Result { match ket_tp { SymKeyType::Aes256Gcm => Self::new_aes_256(None, None, None), SymKeyType::Aes256Xts => Err(Error::NoAeadKey), } } /// Build the authenticated data for a request. /// # Returns /// ```none /// _______________________________________________________________ /// | MAGIC (8) Version Number (4) Size (4)| /// | IV (12) Reserved (4)| /// | Reserved (7) Num keyslots (1) Reserved(4) Encr Size (4)| /// | --------------------------------------------------- | /// | Request type dependent AAD data | /// |-------------------------------------------------------------| /// ``` pub fn build_aad( &self, version: RequestVersion, aad: &Vec, encr_size: usize, magic: O, ) -> Result> where O: Into>, { self.build_aad_impl(version, aad, encr_size, magic.into()) } /// Concrete implementation for [`ReqEncrCtx::build_aad`]. fn build_aad_impl( &self, version: RequestVersion, aad: &Vec, encr_size: usize, magic: Option, ) -> Result> { let nks = aad.iter().filter(|a| matches!(a, Aad::Ks(_))).count(); let nks: u8 = match nks { 0 => Err(Error::NoHostkey), n if n > u8::MAX as usize => Err(Error::ManyHostkeys), n => Ok(n as u8), }?; let mut auth_data: Vec = Vec::with_capacity(2048); // reserve space for the request header auth_data.resize(size_of::(), 0); for a in aad { match a { Aad::Plain(p) => auth_data.extend_from_slice(p), Aad::Ks(ks) => { ks.encrypt_to(self.prot_key.value(), &self.priv_key, &mut auth_data)? } Aad::Encr(e) => { e.encrypt_to(self.prot_key.value(), &self.priv_key, &mut auth_data)? } } } let rql = to_u32(auth_data.len() + encr_size + 16).ok_or_else(|| { pv_core::Error::Specification("Configured request size to large".to_string()) })?; let sea = to_u32(encr_size) .ok_or_else(|| pv_core::Error::Specification("Encrypted size to large".to_string()))?; let req_hdr = RequestHdr::new(version, rql, self.iv, nks, sea, magic); // copy request header to the start of the request auth_data[..size_of::()].copy_from_slice(req_hdr.as_bytes()); Ok(auth_data) } /// Get the public coordinates from the private key (Customer private key) /// # Errors /// /// This function will return an error if the public key could not be extracted by OpenSSL. /// Very unlikely. pub fn key_coords(&self) -> Result { self.priv_key.as_ref().try_into().map_err(Error::Crypto) } /// Encrypt confidential Data with this encryption context and provide a GCM tag. /// /// * `aad` - additional authentic data /// * `conf` - data to be encrypted /// /// # Returns /// [`Vec`] with the following content: /// 1. `aad` /// 2. `encr(conf)` /// 3. `aes gcm tag` /// /// # Errors /// /// This function will return an error if the data could not be encrypted by OpenSSL. pub(crate) fn encrypt_aead(&self, aad: &[u8], conf: &[u8]) -> Result { encrypt_aead(&self.prot_key, &self.iv, aad, conf) } /// Returns a reference to the request protection key of this [`ReqEncrCtx`]. pub fn prot_key(&self) -> &SymKey { &self.prot_key } } /// Public key components of an [`openssl::ec::EcKey`] key. #[repr(C)] #[derive(Debug, Clone)] pub struct EcPubKeyCoord([u8; 160]); impl AsRef<[u8]> for EcPubKeyCoord { fn as_ref(&self) -> &[u8] { self.0.as_slice() } } const ECDH_PUB_KEY_COORD_POINT_SIZE: usize = 0x50; impl EcPubKeyCoord { /// Returns the SHA256 hash of the [`EcPubKeyCoord`]. /// /// If [`EcPubKeyCoord`] was built from a host-key, this value is the public host-key hash. pub fn sha256(&self) -> Result { hash(MessageDigest::sha256(), self.as_ref()) } /// Construct a [`EcPubKeyCoord`] /// /// # Safety /// This function is marked unsafe, because data not representing two EC points violates the /// invariant of this struct. pub unsafe fn from_data(data: [u8; 160]) -> Self { EcPubKeyCoord(data) } } /// Get the pub ECDH coordinates in the format the Ultravisor expects it: /// The two coordinates are padded to 80 bytes each. fn get_pub_ecdh_points(pkey: &EcPointRef, grp: &EcGroupRef) -> Result<[u8; 160], ErrorStack> { let mut x = BigNum::new()?; let mut y = BigNum::new()?; let mut bn_ctx = BigNumContext::new()?; pkey.affine_coordinates(grp, &mut x, &mut y, &mut bn_ctx)?; let mut coord: Vec = x.to_vec_padded(ECDH_PUB_KEY_COORD_POINT_SIZE as i32)?; coord.append(&mut y.to_vec_padded(ECDH_PUB_KEY_COORD_POINT_SIZE as i32)?); Ok(coord.try_into().unwrap()) } impl TryFrom for PKey { type Error = ErrorStack; fn try_from(value: EcPubKeyCoord) -> Result { let ecdh = value.as_ref(); let grp = EcGroup::from_curve_name(Nid::SECP521R1)?; let x = BigNum::from_slice(&ecdh[..ECDH_PUB_KEY_COORD_POINT_SIZE])?; let y = BigNum::from_slice(&ecdh[ECDH_PUB_KEY_COORD_POINT_SIZE..])?; let ec_key = EcKey::from_public_key_affine_coordinates(&grp, &x, &y)?; Self::from_ec_key(ec_key) } } macro_rules! ecdh_from { ($type: ty) => { impl TryFrom<&PKeyRef<$type>> for EcPubKeyCoord { type Error = ErrorStack; fn try_from(key: &PKeyRef<$type>) -> Result { let k = key.ec_key()?; k.check_key()?; let grp = k.group(); let pub_key = k.public_key(); let coord = get_pub_ecdh_points(pub_key, grp)?; Ok(Self(coord)) } } impl TryFrom> for EcPubKeyCoord { type Error = ErrorStack; fn try_from(key: PKey<$type>) -> Result { let key_ref = key.as_ref(); key_ref.try_into() } } }; } ecdh_from!(Private); ecdh_from!(Public); /// Representation of the shared parts of the request header. /// Used by [`ReqEncrCtx`] #[repr(C)] #[derive(Debug, Copy, Clone, IntoBytes, FromBytes, Immutable)] struct RequestHdr { magic: [u8; 8], rqvn: U32, rql: U32, iv: [u8; 12], reserved1c: [u8; 4], reserved20: [u8; 7], nks: u8, reserved28: u32, sea: U32, } assert_size!(RequestHdr, 48); impl RequestHdr { fn new(rqvn: u32, rql: u32, iv: [u8; 12], nks: u8, sea: u32, magic: Option<[u8; 8]>) -> Self { Self { magic: magic.unwrap_or_default(), rqvn: rqvn.into(), rql: rql.into(), iv, reserved1c: [0; 4], reserved20: [0; 7], nks, reserved28: 0, sea: sea.into(), } } } /// A trait representing a request for the Ultravisor. /// /// All requests share a few things: /// * All requests need to be encrypted on a trusted machine /// * All requests have at least one Hostkeyslot /// /// The encryption setup is handled by [`ReqEncrCtx`]. Implementers need to pass the data to the /// `ReqEncrCtx` when implementing `encrypt`. A hostkey should be represented by [`Keyslot`] during /// encryption. /// /// An UV request consists of an authenticated area (AAD), an encrypted area (Encr) and a 16 byte /// tag. The AAD contains a general header and Request type defined data (including Keyslots). It /// is encrypted with an Request protection key (symmetric). This key is encrypted with a /// (generated) private key and the public key of the host system (Host key) /// ```none /// _______________________________________________________________ /// | MAGIC (8) Version Number (4) Size (4)| /// | IV (12) Reserved (4)| /// | Reserved (7) Num keyslots (1) Reserved(4) Encr Size (4)| /// | --------------------------------------------------- | /// | Request type dependent AAD data | /// | ---------------------------------------------------- | /// | Encrypted (request type dependent) data | /// | ---------------------------------------------------- | /// | AES GCM Tag (16) | /// |_____________________________________________________________| /// ``` pub trait Request { /// Encrypt the request into its binary format /// /// # Errors /// /// This function will return an error if the encryption fails, the request does not have at /// least a hostkey, or other implementation dependent contracts are not met. fn encrypt(&self, ctx: &ReqEncrCtx) -> Result>; /// Add a host-key to this request /// /// Must be called at least once, otherwise {`Request::encrypt`} will fail fn add_hostkey(&mut self, hostkey: PKey); } /// A struct to represent some parts of a binary/encrypted request. #[derive(Debug)] #[allow(clippy::len_without_is_empty)] pub(crate) struct BinReqValues<'a> { iv: &'a [u8], aad: &'a [u8], req_dep_aad: &'a [u8], encr: &'a [u8], tag: &'a [u8], version: u32, len: usize, } impl<'a> BinReqValues<'a> { pub(crate) const TAG_LEN: usize = SymKeyType::AES_256_GCM_TAG_LEN; /// Get the locations from this request. /// /// Does minimal sanity test, just tests to prevent panics. /// `req` may be larger than the actual request. pub(crate) fn get(req: &'a [u8]) -> Result { let (hdr, _) = RequestHdr::read_from_prefix(req).map_err(|_| Error::BinRequestSmall)?; let rql = hdr.rql.get() as usize; let sea = hdr.sea.get() as usize; if rql < req.len() || sea + Self::TAG_LEN > rql { return Err(Error::BinRequestSmall); } let aad_size = rql - sea - Self::TAG_LEN; if aad_size < size_of::() { return Err(Error::BinRequestSmall); } let iv = &req[0x10..0x1c]; let aad = &req[..aad_size]; let req_dep_aad = &req[size_of::()..aad_size]; let encr = &req[aad_size..(aad_size + sea)]; let tag = &req[rql - Self::TAG_LEN..]; Ok(Self { iv, aad, req_dep_aad, encr, tag, version: hdr.rqvn.get(), len: rql, }) } /// Returns the version of this [`BinReqValues`]. pub(crate) fn version(&self) -> u32 { self.version } /// Returns the length of this [`BinReqValues`]. pub(crate) fn len(&self) -> usize { self.len } /// Returns the size of the encrypted area pub(crate) fn sea(&self) -> u32 { self.encr.len() as u32 } /// Decrypts the encrypted area with the provided key pub(crate) fn decrypt(&self, key: &SymKey) -> Result>> { let result = decrypt_aead(key, self.iv, self.aad, self.encr, self.tag)?; Ok(result.into_plain()) } /// Returns a reference to the request dependent authenticated area of this [`BinReqValues`] /// already interpreted. /// /// If target struct is larger than the request depended-AAD None is returned. See /// [`FromBytes::ref_from_prefix`] pub(crate) fn req_dep_aad(&self) -> Option<&T> where T: FromBytes + Sized + Immutable + KnownLayout, { T::ref_from_prefix(self.req_dep_aad).map(|s| s.0).ok() } /// Returns a reference to the tag of this [`BinReqValues`]. pub(crate) fn tag(&self) -> &[u8] { self.tag } } #[cfg(test)] mod tests { use super::*; use crate::{get_test_asset, request::SymKey, test_utils::*}; static TEST_MAGIC: [u8; 8] = 0x12345689abcdef00u64.to_be_bytes(); #[test] fn encr_build_aad() { let (cust_key, host_key) = get_test_keys(); let ks = Keyslot::new(host_key); let ctx = ReqEncrCtx::new_aes_256( Some([0x11; 12]), Some(cust_key), Some(SymKey::Aes256([0x17; 32].into())), ) .unwrap(); let v = [0x55; 8]; let aad = Aad::Plain(&v); let aad = ctx .build_aad(0x200, &vec![aad, Aad::Ks(&ks)], 16, Some(TEST_MAGIC)) .unwrap(); let mut aad_exp = vec![ 0x12, 0x34, 0x56, 0x89, 0xab, 0xcd, 0xef, 0, // progr 0, 0, 2, 0, // vers 0, 0, 0, 168, // size 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, // iv 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // res 1, // nks 0, 0, 0, 0, // res 0, 0, 0, 16, // sea 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, // aad ]; aad_exp.extend_from_slice(get_test_asset!("exp/keyslot.bin")); assert_eq!(&aad, &aad_exp); } #[test] fn encr_build_aad_nks_no() { let ctx = ReqEncrCtx::new_aes_256(Some([0x11; 12]), None, None).unwrap(); let aad = Vec::::new(); let aad = ctx.build_aad(0x200, &aad, 16, Some(TEST_MAGIC)); assert!(matches!(aad, Err(Error::NoHostkey))); } #[test] fn encr_build_aad_nks_many() { let (_, host_key) = get_test_keys(); let ctx = ReqEncrCtx::new_aes_256(Some([0x11; 12]), None, None).unwrap(); let ks: Vec = (0..257).map(|_| Keyslot::new(host_key.clone())).collect(); let mut aad = Vec::::new(); ks.iter().for_each(|ks| aad.push(Aad::Ks(ks))); let aad = ctx.build_aad(0x200, &aad, 16, Some(TEST_MAGIC)); assert!(matches!(aad, Err(Error::ManyHostkeys))); } #[test] fn encr_build_aad_nks() { let (_, host_key) = get_test_keys(); let ctx = ReqEncrCtx::new_aes_256(Some([0x11; 12]), None, None).unwrap(); let ks = [ Keyslot::new(host_key.clone()), Keyslot::new(host_key.clone()), Keyslot::new(host_key), ]; let mut aad = Vec::::new(); ks.iter().for_each(|ks| aad.push(Aad::Ks(ks))); let aad = ctx.build_aad(0x200, &aad, 16, Some(TEST_MAGIC)).unwrap(); assert_eq!(aad.get(39).unwrap(), &3u8); } #[test] fn req_hdr() { let hdr = RequestHdr::new(0x200, 22, [0x11; 12], 15, 44, None); let hdr_bin = hdr.as_bytes(); let hdr_bin_exp = [ 0u8, 0, 0, 0, 0, 0, 0, 0, // magic 0, 0, 2, 0, // vers 0, 0, 0, 22, // size 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, // iv 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // res 15, // nks 0, 0, 0, 0, // res 0, 0, 0, 44, // sea ]; assert_eq!(hdr_bin, &hdr_bin_exp); } #[test] fn req_hdr2() { let mut hdr = RequestHdr::new(0x200, 0x1234, [0x11; 12], 15, 44, Some(TEST_MAGIC)); let hdr_bin = hdr.as_mut_bytes(); let hdr_bin_exp = [ 0x12, 0x34, 0x56, 0x89, 0xab, 0xcd, 0xef, 0, // magic 0, 0, 2, 0, // vers 0, 0, 0x12, 0x34, // size 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, // iv 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // res 15, // nks 0, 0, 0, 0, // res 0, 0, 0, 44, // sea ]; assert_eq!(hdr_bin, &hdr_bin_exp); } #[test] fn keyslot() { let (cust_key, host_key) = get_test_keys(); let exp_keyslot = get_test_asset!("exp/keyslot.bin").to_vec(); let keyslot = Keyslot::new(host_key); let encr_ks = keyslot.encrypt(&[0x17u8; 32], &cust_key).unwrap(); assert_eq!(exp_keyslot, encr_ks); let encr_ks = keyslot.encrypt(&[0x16u8; 32], &cust_key).unwrap(); assert_ne!(exp_keyslot, encr_ks); } #[test] fn ec_pub_ec_coord_from() { let (cust_key, _) = get_test_keys(); let pub_key = get_test_asset!("keys/public_cust.bin"); assert_eq!(pub_key.len(), 160); let ec_coord: EcPubKeyCoord = cust_key.as_ref().try_into().unwrap(); assert_eq!(ec_coord.as_ref(), pub_key); } #[test] fn ec_pub_ec_coord_hash() { let exp = [ 0x5e, 0xe9, 0x05, 0xa9, 0xbe, 0x70, 0x36, 0x68, 0x15, 0xa4, 0x56, 0x41, 0xaf, 0xae, 0x00, 0x97, 0x3b, 0x1f, 0x45, 0x29, 0x2f, 0x43, 0xbc, 0xd7, 0x63, 0x8e, 0xe2, 0xa7, 0x3f, 0xd7, 0xc4, 0x5e, ]; let (cust_key, _) = get_test_keys(); let ec_coord: EcPubKeyCoord = cust_key.as_ref().try_into().unwrap(); let hash = ec_coord.sha256().unwrap(); assert_eq!(hash.as_ref(), &exp); } #[test] fn conversion_ecdh_and_vice_versa() { let (_, cust_pub) = get_test_keys(); let phk: EcPubKeyCoord = cust_pub.clone().try_into().unwrap(); assert_eq!( phk.as_ref(), &[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 118, 136, 28, 216, 75, 139, 109, 231, 18, 60, 126, 144, 14, 223, 120, 231, 247, 182, 132, 153, 145, 70, 177, 38, 59, 168, 184, 108, 132, 71, 240, 138, 182, 212, 105, 194, 177, 40, 237, 158, 28, 53, 1, 88, 5, 172, 211, 211, 2, 51, 211, 145, 34, 247, 226, 248, 170, 28, 43, 20, 123, 120, 131, 180, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 195, 69, 86, 194, 92, 249, 47, 41, 206, 102, 189, 68, 17, 77, 107, 123, 60, 120, 225, 58, 63, 144, 189, 185, 0, 64, 246, 135, 110, 82, 98, 247, 120, 166, 26, 147, 125, 27, 52, 128, 46, 178, 87, 227, 78, 6, 114, 221, 95, 42, 52, 122, 221, 170, 40, 32, 53, 9, 42, 112, 195, 92, 46, 121, 115 ] ); let cust_pub_back: PKey = phk.try_into().unwrap(); assert!(cust_pub.public_eq(&cust_pub_back)); } } s390-tools-2.38.0/rust/pv/src/test_utils.rs000066400000000000000000000072221502674226300204300ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 // DO NOT USE ANY OF THESE ITEMS IN PRODUCTION CODE // USED FOR INTERNAL UNIT AND FVT TESTING ONLY!!! use std::{ fs, path::{Path, PathBuf}, }; use openssl::{ bn::BigNum, ec::{EcGroup, EcKey}, error::ErrorStack, nid::Nid, pkey::{PKey, Private, Public}, x509::{X509Crl, X509}, }; /// TEST ONLY! Loads the specified asset into the binary at compile time. /// /// For testing-assets only! /// The asset must be present at `{crate}/test/assets/{file}` #[doc(hidden)] #[macro_export] macro_rules! get_test_asset { ($file:expr) => { include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/assets/", $file)) }; } pub fn get_cert_asset_path>(path: P) -> PathBuf { let mut p = PathBuf::from(env!("CARGO_MANIFEST_DIR")); p.push("tests"); p.push("assets"); p.push("cert"); p.push(path); println!("CERT path: {}", p.to_str().unwrap()); p } /// TEST ONLY! Load an cert /// /// panic on errors pub fn get_cert_asset>(path: P) -> Vec { let p = get_cert_asset_path(path); fs::read(p).unwrap() } /// TEST ONLY! Load cert found in the asset path /// /// panic on errors pub fn load_gen_cert>(asset_path: P) -> X509 { let buf = get_cert_asset(asset_path); let mut cert = X509::from_der(&buf) .map(|crt| vec![crt]) .or_else(|_| X509::stack_from_pem(&buf)) .unwrap(); assert_eq!(cert.len(), 1); cert.pop().unwrap() } /// TEST ONLY! Load the CRL found in the asset path /// /// panic on errors pub fn load_gen_crl>(asset_path: P) -> X509Crl { let buf = get_cert_asset(asset_path); X509Crl::from_der(&buf) .or_else(|_| X509Crl::from_pem(&buf)) .unwrap() } /// TEST ONLY! Get a fixed private/public pair and a fixed host-key document /// /// Intended for TESTING only. All parts of the key including the private key are checked in git and /// visible for the public pub fn get_test_key_and_cert() -> (PKey, X509) { let pub_key = get_test_asset!("keys/public_cust.bin"); let priv_key = get_test_asset!("keys/private_cust.bin"); let host_key = get_test_asset!("keys/host.pem.crt"); assert_eq!(pub_key.len(), 160); assert_eq!(priv_key.len(), 80); let cust_key = get_keypair(pub_key, priv_key).unwrap(); let host_key = X509::from_pem(host_key).unwrap(); (cust_key, host_key) } /// TEST ONLY! Get a fixed private/public pair and a fixed public key /// /// Intended for TESTING only. All parts of the key including the private key are checked in git and /// visible for the public pub fn get_test_keys() -> (PKey, PKey) { let (cust_key, host) = get_test_key_and_cert(); (cust_key, host.public_key().unwrap()) } fn read_ecdh_pubkey(coords: &[u8]) -> Result, ErrorStack> { assert!(coords.len() == 160); let x = BigNum::from_slice(&coords[..80])?; let y = BigNum::from_slice(&coords[80..])?; let group = EcGroup::from_curve_name(Nid::SECP521R1)?; let key = EcKey::from_public_key_affine_coordinates(&group, &x, &y)?; PKey::from_ec_key(key) } fn get_keypair(pub_coords: &[u8], priv_num: &[u8]) -> Result, ErrorStack> { assert!(pub_coords.len() == 160); assert!(priv_num.len() == 80); let pub_key = read_ecdh_pubkey(pub_coords)?; let pub_key = pub_key.ec_key()?; let pub_key = pub_key.public_key(); let priv_key = BigNum::from_slice(priv_num)?; let group = EcGroup::from_curve_name(Nid::SECP521R1)?; let key = EcKey::from_private_components(&group, &priv_key, pub_key)?; key.check_key()?; PKey::from_ec_key(key) } s390-tools-2.38.0/rust/pv/src/utils.rs000066400000000000000000000034661502674226300173770ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 use crate::{Error, Result}; use openssl::{ error::ErrorStack, x509::{X509Crl, X509}, }; /// Read all CRLs from the buffer and parse them into a vector. /// /// # Errors /// /// This function will return an error if the underlying OpenSSL implementation cannot parse `buf` /// as `DER` or `PEM`. pub fn read_crls>(buf: T) -> Result> { use crate::openssl_extensions::StackableX509Crl; X509Crl::from_der(buf.as_ref()) .map(|crl| vec![crl]) .or_else(|_| StackableX509Crl::stack_from_pem(buf.as_ref())) .map_err(Error::Crypto) } /// Read all certificates from the buffer and parse them into a vector. /// /// # Errors /// /// This function will return an error if the underlying OpenSSL implementation cannot parse `buf` pub fn read_certs>(buf: T) -> Result, ErrorStack> { X509::from_der(buf.as_ref()) .map(|crt| vec![crt]) .or_else(|_| X509::stack_from_pem(buf.as_ref())) } #[cfg(test)] mod tests { use crate::test_utils::*; #[test] fn read_crls() { let crl = get_cert_asset("ibm.crl"); let crl_der = get_cert_asset("der.crl"); let fail = get_cert_asset("ibm.crt"); assert_eq!(super::read_crls(crl).unwrap().len(), 1); assert_eq!(super::read_crls(crl_der).unwrap().len(), 1); assert_eq!(super::read_crls(fail).unwrap().len(), 0); } #[test] fn read_certs() { let crt = get_cert_asset("ibm.crt"); let crt_der = get_cert_asset("der.crt"); let fail = get_cert_asset("ibm.crl"); assert_eq!(super::read_certs(crt).unwrap().len(), 1); assert_eq!(super::read_certs(crt_der).unwrap().len(), 1); assert_eq!(super::read_certs(fail).unwrap().len(), 0); } } s390-tools-2.38.0/rust/pv/src/uvattest.rs000066400000000000000000000002151502674226300201030ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 pub mod additional; pub mod arcb; pub mod attest; type AttNonce = [u8; 16]; s390-tools-2.38.0/rust/pv/src/uvattest/000077500000000000000000000000001502674226300175375ustar00rootroot00000000000000s390-tools-2.38.0/rust/pv/src/uvattest/additional.rs000066400000000000000000000227461502674226300222300ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use serde::Serialize; use std::fmt::Display; use crate::req::Keyslot; use crate::static_assert; use crate::{Error, Result}; use super::arcb::AttestationFlags; /// Hash for additional-data stuff used for parsing [`AdditionalData`] pub(super) const PHKH_SIZE: u32 = 0x20; static_assert!(Keyslot::PHKH_SIZE == PHKH_SIZE); pub(super) const SECRET_STORE_HASH_SIZE: u32 = 0x40; pub(super) const FW_STATE_SIZE: u32 = 0x140; /// Additional-data of an Attestation Request #[derive(Serialize, Debug)] #[serde(default)] pub struct AdditionalData where T: Serialize, { #[serde(skip_serializing_if = "Option::is_none")] image_phkh: Option, #[serde(skip_serializing_if = "Option::is_none")] attestation_phkh: Option, #[serde(skip_serializing_if = "Option::is_none")] secret_store_hash: Option, #[serde(skip_serializing_if = "Option::is_none")] firmware_state: Option, #[serde(skip_serializing_if = "Option::is_none")] unrecognized: Option, } impl Display for AdditionalData where T: Display + Serialize, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn write_field( f: &mut std::fmt::Formatter<'_>, name: &'static str, s: &Option, ) -> std::fmt::Result { if let Some(hash) = s { writeln!(f, "{name}")?; match f.alternate() { true => writeln!(f, "{hash:#}")?, false => writeln!(f, "{hash}")?, }; } Ok(()) } write_field(f, "Image PHKH", &self.image_phkh)?; write_field(f, "Attestation PHKH", &self.attestation_phkh)?; write_field(f, "Secret store hash", &self.secret_store_hash)?; write_field(f, "Firmware state", &self.firmware_state)?; write_field(f, "Unrecognized", &self.unrecognized)?; Ok(()) } } fn read_value<'a>( data: &'a [u8], size: u32, read: bool, name: &'static str, ) -> Result<(Option<&'a [u8]>, &'a [u8])> { let size = size as usize; match read { true if data.len() >= size => Ok((Some(&data[..size]), &data[size..])), true => Err(Error::AddDataMissing(name)), false => Ok((None, data)), } } impl AdditionalData { /// Provides a reference to the image public host key hash, if any. /// /// This is the hash of the public host key of the corresponding private machine key that /// decrypted the Secure Execution guest. /// Contains a value if that value was requested by the attestation request. pub fn image_public_host_key_hash(&self) -> Option<&T> { self.image_phkh.as_ref() } /// Provides a reference to the attestation public host key hash, if any. /// /// This is the hash of the public host key of the corresponding private machine key that /// decrypted the Attestation request. /// Contains a value if that value was requested by the attestation request. pub fn attestation_public_host_key_hash(&self) -> Option<&T> { self.attestation_phkh.as_ref() } /// Provides a reference to the secret store hash, if any. /// /// This is a hash over the state of the guest's UV secret store. A SHA512 hash over the /// concatenated request tags of all Add-secret requests and a 0 or 1 byte for the locked /// state. /// /// SHA512 (n*[Add-secret-request]|locked?) pub fn secret_store_hash(&self) -> Option<&T> { self.secret_store_hash.as_ref() } /// Provides a reference to the firmware state, if any. /// /// This represents the state of selected firmware parts to be interpreted by an IBM service. pub fn firmware_state(&self) -> Option<&T> { self.firmware_state.as_ref() } /// Provides a reference to the data not known by this implementation. pub fn unrecognized(&self) -> Option<&T> { self.unrecognized.as_ref() } } impl<'a, T: Serialize + From<&'a [u8]> + Sized> AdditionalData { /// Create Additional data from the u8-slice variant pub fn from_other(other: AdditionalData<&'a [u8]>) -> Self { let AdditionalData { image_phkh, attestation_phkh, secret_store_hash, firmware_state: firmware_hash, unrecognized, } = other; Self { image_phkh: image_phkh.map(|i| i.into()), attestation_phkh: attestation_phkh.map(|i| i.into()), secret_store_hash: secret_store_hash.map(|i| i.into()), firmware_state: firmware_hash.map(|i| i.into()), unrecognized: unrecognized.map(|i| i.into()), } } /// Create from a slice of additional-data /// /// `data`: Unstructured additional-data /// `flags`: Flags indicating which additional-data field is present. /// /// # Error /// /// Fails if there is a mismatch between the data and the flags. Should not happen after a /// successful attestation verification. pub fn from_slice_sized(data: &'a [u8], flags: &AttestationFlags) -> Result { AdditionalData::<&'a [u8]>::from_slice(data, flags).map(Self::from_other) } } impl<'a> AdditionalData<&'a [u8]> { /// Create from a slice of additional-data /// /// `data`: Unstructured additional-data /// `flags`: Flags indicating which additional-data field is present. /// /// # Error /// /// Fails if there is a mismatch between the data and the flags. Should not happen after a /// successful attestation verification. pub fn from_slice(data: &'a [u8], flags: &AttestationFlags) -> Result { let (image_phkh, data) = read_value(data, PHKH_SIZE, flags.image_phkh(), "Image PHKH")?; let (attestation_phkh, data) = read_value(data, PHKH_SIZE, flags.attest_phkh(), "Attestation PHKH")?; let (secret_store_hash, data) = read_value( data, SECRET_STORE_HASH_SIZE, flags.secret_store_hash(), "Secret store state", )?; let (firmware_state, data) = read_value(data, FW_STATE_SIZE, flags.firmware_state(), "Firmware hash")?; let unrecognized = (!data.is_empty()).then_some(data); Ok(Self { image_phkh, attestation_phkh, secret_store_hash, firmware_state, unrecognized, }) } } #[cfg(test)] mod test { use serde_test::Token; use super::*; #[test] fn ser() { let add = AdditionalData { image_phkh: 0_u8.into(), attestation_phkh: 1_u8.into(), secret_store_hash: 2_u8.into(), firmware_state: 3_u8.into(), unrecognized: 4_u8.into(), }; serde_test::assert_ser_tokens( &add, &[ Token::Struct { name: "AdditionalData", len: 5, }, Token::Str("image_phkh"), Token::Some, Token::U8(0), Token::Str("attestation_phkh"), Token::Some, Token::U8(1), Token::Str("secret_store_hash"), Token::Some, Token::U8(2), Token::Str("firmware_state"), Token::Some, Token::U8(3), Token::Str("unrecognized"), Token::Some, Token::U8(4), Token::StructEnd, ], ); } #[test] fn ser_no_miss() { let add = AdditionalData { image_phkh: 0_u8.into(), attestation_phkh: None, secret_store_hash: None, firmware_state: None, unrecognized: 2_u8.into(), }; serde_test::assert_ser_tokens( &add, &[ Token::Struct { name: "AdditionalData", len: 2, }, Token::Str("image_phkh"), Token::Some, Token::U8(0), Token::Str("unrecognized"), Token::Some, Token::U8(2), Token::StructEnd, ], ); } #[test] fn ser_no_unrec() { let add = AdditionalData { image_phkh: 0_u8.into(), attestation_phkh: 1_u8.into(), secret_store_hash: None, firmware_state: None, unrecognized: None, }; serde_test::assert_ser_tokens( &add, &[ Token::Struct { name: "AdditionalData", len: 2, }, Token::Str("image_phkh"), Token::Some, Token::U8(0), Token::Str("attestation_phkh"), Token::Some, Token::U8(1), Token::StructEnd, ], ); } #[test] fn ser_no() { let add: AdditionalData = AdditionalData { image_phkh: None, attestation_phkh: None, secret_store_hash: None, firmware_state: None, unrecognized: None, }; serde_test::assert_ser_tokens( &add, &[ Token::Struct { name: "AdditionalData", len: 0, }, Token::StructEnd, ], ); } } s390-tools-2.38.0/rust/pv/src/uvattest/arcb.rs000066400000000000000000000366461502674226300210330ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use openssl::pkey::{PKey, Public}; use std::mem::size_of; use zerocopy::{BigEndian, FromBytes, Immutable, IntoBytes, KnownLayout, U32}; use crate::{ assert_size, attest::{AttestationMagic, AttestationMeasAlg}, crypto::random_array, misc::Flags, req::{Aad, BinReqValues, Keyslot, ReqEncrCtx}, request::{Confidential, MagicValue, Request, RequestVersion, SymKey, Zeroize}, static_assert, uv::UvFlags, Error, Result, }; use super::{ additional::{FW_STATE_SIZE, PHKH_SIZE, SECRET_STORE_HASH_SIZE}, AttNonce, }; #[cfg(doc)] use crate::{ request::SymKeyType, uv::AttestationCmd, verify::{CertVerifier, HkdVerifier}, }; /// Retrieve Attestation Request Control Block /// /// An ARCB holds an Attestation Measurement key to attest a SE-guest. /// The (architectural optional) nonce is always used and freshly generated for a new /// [`AttestationRequest`]. /// /// Layout: /// ```none /// _______________________________________________________________ /// | generic header (48) /// | --------------------------------------------------- | /// | Plaintext Attestation flags (8) | /// | Measurement Algorithm Identifier (4) | /// | Reserved(4) | /// | Customer Public Key (160) generated for each request | /// | N Keyslots(80 each) | /// | --------------------------------------------------- | /// | Measurement key (64) | Encrypted /// | Optional Nonce (0 or 16) | Encrypted /// | --------------------------------------------------- | /// | AES GCM Tag (16) | /// |_____________________________________________________________| /// ``` /// /// # Example /// Create an Attestation request with default flags (= use a nonce) /// /// ```rust,no_run /// # use s390_pv::attest::{AttestationFlags, AttestationMeasAlg, AttestationRequest, AttestationVersion}; /// # use s390_pv::request::{SymKeyType, Request, ReqEncrCtx}; /// # fn main() -> s390_pv::Result<()> { /// let att_version = AttestationVersion::One; /// let meas_alg = AttestationMeasAlg::HmacSha512; /// let mut arcb = AttestationRequest::new(att_version, meas_alg, AttestationFlags::default())?; /// // read-in hostkey document(s). Not verified for brevity. /// let hkd = s390_pv::misc::read_certs(&std::fs::read("host-key-document.crt")?)?; /// // IBM issued HKD certificates typically have one X509 /// let hkd = hkd.first().unwrap().public_key()?; /// arcb.add_hostkey(hkd); /// // you can add multiple hostkeys /// // arcb.add_hostkey(another_hkd); /// // encrypt it /// let ctx = ReqEncrCtx::random(SymKeyType::Aes256)?; /// let arcb = arcb.encrypt(&ctx)?; /// # Ok(()) /// # } /// ``` /// # See Also /// /// * [`AttestationFlags`] /// * [`AttestationMeasAlg`] /// * [`AttestationVersion`] /// * [`SymKeyType`] /// * [`Request`] /// * [`ReqEncrCtx`] /// * [`AttestationCmd`] /// * [`HkdVerifier`], [`CertVerifier`] #[derive(Debug)] pub struct AttestationRequest { version: AttestationVersion, aad: AttestationAuthenticated, keyslots: Vec, conf: Confidential, } impl AttestationRequest { /// Create a new retrieve attestation measurement request pub fn new( version: AttestationVersion, mai: AttestationMeasAlg, mut flags: AttestationFlags, ) -> Result { // This implementation enforces using a nonce flags.set_nonce(); Ok(Self { version, aad: AttestationAuthenticated::new(flags, mai), keyslots: vec![], conf: ReqConfData::random()?, }) } /// Returns a reference to the flags of this [`AttestationRequest`]. pub fn flags(&self) -> &AttestationFlags { &self.aad.flags } /// Returns a copy of the confidential data of this [`AttestationRequest`]. /// /// Gives a copy of the confidential data of this request for further /// processing. This data should be never exposed in cleartext to anyone but /// the creator and the verifier of this request. pub fn confidential_data(&self) -> AttestationConfidential { let conf = self.conf.value(); AttestationConfidential::new(conf.meas_key.to_vec(), conf.nonce.into()) } fn aad(&self, ctx: &ReqEncrCtx) -> Result> { let cust_pub_key = ctx.key_coords()?; let mut aad: Vec = Vec::with_capacity(self.keyslots.len() + 2); aad.push(Aad::Plain(self.aad.as_bytes())); aad.push(Aad::Plain(cust_pub_key.as_ref())); self.keyslots.iter().for_each(|k| aad.push(Aad::Ks(k))); ctx.build_aad( self.version.into(), &aad, size_of::(), AttestationMagic::MAGIC, ) } /// Checks for magic and returns [`BinReqValues`] fn bin_values(arcb: &[u8]) -> Result { if !AttestationMagic::starts_with_magic(arcb) { return Err(Error::NoArcb); } let values = BinReqValues::get(arcb)?; match values.version().try_into()? { AttestationVersion::One => (), }; Ok(values) } /// Returns the authenticated area of an binary attestation request. /// /// # Error /// /// Returns an error if the request is malformed. pub fn auth_bin(arcb: &[u8]) -> Result { let values = Self::bin_values(arcb)?; let auth: &AttestationAuthenticated = values.req_dep_aad().ok_or(Error::BinRequestSmall)?; Ok(auth.to_owned()) } /// Decrypts the request and extracts the authenticated and confidential data. /// /// Deconstructs the `arcb` and decrypts it using `arpk`. /// /// # Error /// /// Returns an error if the request is malformed or the decryption failed. pub fn decrypt_bin( arcb: &[u8], arpk: &SymKey, ) -> Result<(AttestationAuthenticated, AttestationConfidential)> { let values = Self::bin_values(arcb)?; let auth = Self::auth_bin(arcb)?; let mai = auth.mai.try_into()?; let keysize = match mai { v @ AttestationMeasAlg::HmacSha512 => v.exp_size(), } as usize; if keysize > values.sea() as usize { return Err(Error::BinArcbSeaSmall(values.sea())); } let decr = values.decrypt(arpk)?; // size sanitized by fence before let meas_key = &decr.value()[..keysize]; let nonce = if decr.value().len() == size_of::() { Some( (&decr.value()[keysize..decr.value().len()]) .try_into() .unwrap(), ) } else { None }; let conf = AttestationConfidential::new(meas_key.to_vec(), nonce); Ok((auth.to_owned(), conf)) } } /// Confidential Data of an attestation request /// /// contains a measurement key and an optional nonce #[derive(Debug)] pub struct AttestationConfidential { measurement_key: Confidential>, nonce: Option>, } impl AttestationConfidential { /// Returns a reference to the measurement key of this [`AttestationConfidential`]. pub fn measurement_key(&self) -> &[u8] { self.measurement_key.value() } /// Returns a reference to the nonce of this [`AttestationConfidential`]. pub fn nonce(&self) -> &Option> { &self.nonce } fn new(measurement_key: Vec, nonce: Option) -> Self { Self { measurement_key: measurement_key.into(), nonce: nonce.map(Confidential::new), } } } impl Request for AttestationRequest { fn encrypt(&self, ctx: &ReqEncrCtx) -> Result> { let conf = self.conf.value().as_bytes(); let aad = self.aad(ctx)?; ctx.encrypt_aead(&aad, conf).map(|res| res.into_buf()) } fn add_hostkey(&mut self, hostkey: PKey) { self.keyslots.push(Keyslot::new(hostkey)) } } /// Versions for [`AttestationRequest`] #[repr(u32)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum AttestationVersion { /// Version 1 (= 0x0100) One = 0x0100, } impl TryFrom for AttestationVersion { type Error = Error; fn try_from(value: u32) -> Result { if value == Self::One as u32 { Ok(Self::One) } else { Err(Error::BinArcbInvVersion(value)) } } } impl From for RequestVersion { fn from(val: AttestationVersion) -> Self { val as Self } } /// Authenticated additional Data of an [`AttestationRequest`] #[repr(C)] #[derive(Debug, IntoBytes, FromBytes, Clone, Copy, Immutable, KnownLayout)] pub struct AttestationAuthenticated { flags: AttestationFlags, mai: U32, res: u32, } assert_size!(AttestationAuthenticated, 0x10); impl AttestationAuthenticated { fn new(flags: AttestationFlags, mai: AttestationMeasAlg) -> Self { Self { flags, mai: mai.into(), res: 0, } } /// Returns a reference to the flags of this [`AttestationAuthenticated`]. pub fn flags(&self) -> &AttestationFlags { &self.flags } /// Returns the [`AttestationMeasAlg`] of this [`AttestationAuthenticated`]. /// /// # Panics /// /// Panics if the library failed to set up the MAI correctly. pub fn mai(&self) -> AttestationMeasAlg { AttestationMeasAlg::try_from(self.mai).expect("ReqAuthData invariant hurt. Invalid MAI") } } /// Attestation flags #[repr(C)] #[derive(Default, Debug, IntoBytes, FromBytes, Clone, Copy, Immutable)] pub struct AttestationFlags(UvFlags); static_assert!(AttestationFlags::FLAG_TO_ADD_SIZE.len() < 64); impl AttestationFlags { /// Maps the flag to the (maximum) required size for the additional data pub(crate) const FLAG_TO_ADD_SIZE: [u32; 6] = [ 0, 0, PHKH_SIZE, PHKH_SIZE, SECRET_STORE_HASH_SIZE, FW_STATE_SIZE, ]; /// Returns the maximum size this flag requires for additional data pub fn expected_additional_size(&self) -> u32 { Self::FLAG_TO_ADD_SIZE .iter() .enumerate() .fold(0, |size, (b, s)| size + self.0.is_set(b as u8) as u32 * s) } /// Flag 1 - use a nonce /// /// This attestation implementation forces the use of a nonce, so this will always be on and /// the function is non-public fn set_nonce(&mut self) { self.0.set_bit(1); } /// Flag 2 - request the image public host-key hash /// /// Asks the Ultravisor to provide the host-key hash that unpacked the SE-image to be added in /// additional data. Requires 32 bytes. pub fn set_image_phkh(&mut self) { self.0.set_bit(2); } /// Check weather the image public host key hash flag is on pub fn image_phkh(&self) -> bool { self.0.is_set(2) } /// Flag 3 - request the attestation public host-key hash /// /// Asks the Ultravisor to provide the host-key hash that unpacked the attestation request to /// be added in additional data. Requires 32 bytes. pub fn set_attest_phkh(&mut self) { self.0.set_bit(3); } /// Check weather the attestation public host key hash flag is on pub fn attest_phkh(&self) -> bool { self.0.is_set(3) } /// Flag 4 - request the state of the secret store /// /// Asks the Ultravisor to provide the hash of the added secret requests. Requires 64 bytes. pub fn set_secret_store_hash(&mut self) { self.0.set_bit(4); } /// Check weather the hash of the added secret requests flag is on pub fn secret_store_hash(&self) -> bool { self.0.is_set(4) } /// Flag 5 - request the firmware hash /// /// Asks the Ultravisor to provide the hash of the firmware. Requires 320 bytes. pub fn set_firmware_state(&mut self) { self.0.set_bit(5); } /// Check weather the hash of the added secret requests flag is on pub fn firmware_state(&self) -> bool { self.0.is_set(5) } } #[repr(C)] #[derive(Debug, IntoBytes, Immutable)] struct ReqConfData { meas_key: [u8; 64], nonce: AttNonce, } assert_size!(ReqConfData, 80); impl ReqConfData { fn random() -> Result> { Ok(Confidential::new(Self { meas_key: random_array()?, nonce: random_array()?, })) } } impl Zeroize for ReqConfData { fn zeroize(&mut self) { self.meas_key.zeroize(); self.nonce.zeroize(); } } #[cfg(test)] mod test { use super::*; use crate::{get_test_asset, request::SymKey, test_utils::get_test_keys}; const ARPK: [u8; 32] = [0x17; 32]; const NONCE: [u8; 16] = [0xab; 16]; const MEAS: [u8; 64] = [0x77; 64]; fn mk_arcb() -> Vec { let (cust_key, host_key) = get_test_keys(); let ctx = ReqEncrCtx::new_aes_256( Some([0x55; 12]), Some(cust_key), Some(SymKey::Aes256(ARPK.into())), ) .unwrap(); let mut flags = AttestationFlags::default(); flags.set_image_phkh(); flags.set_attest_phkh(); let mut arcb = AttestationRequest::new( AttestationVersion::One, AttestationMeasAlg::HmacSha512, flags, ) .unwrap(); // manually set confidential data (API does not allow this) arcb.conf.value_mut().nonce = NONCE; arcb.conf.value_mut().meas_key = MEAS; arcb.add_hostkey(host_key); arcb.encrypt(&ctx).unwrap() } #[test] fn arcb() { let request = mk_arcb(); let exp = get_test_asset!("exp/arcb.bin"); assert_eq!(request, exp); } #[test] fn auth_bin() { let request = mk_arcb(); let auth_bin = AttestationRequest::auth_bin(&request).unwrap(); let exp = &request[0x30..0x40]; assert_eq!(exp, auth_bin.as_bytes()); } #[test] fn decrypt_bin() { let request = mk_arcb(); let arpk = SymKey::Aes256(ARPK.into()); let (_, conf) = AttestationRequest::decrypt_bin(&request, &arpk).unwrap(); assert_eq!(conf.measurement_key(), &MEAS); assert_eq!(conf.nonce().as_ref().unwrap().value(), &NONCE); } #[test] fn decrypt_bin_fail_magic() { let arpk = SymKey::Aes256(ARPK.into()); let mut tamp_arcb = mk_arcb(); // tamper magic tamp_arcb[0] = 17; let ret = AttestationRequest::decrypt_bin(&tamp_arcb, &arpk); assert!(matches!(ret, Err(Error::NoArcb))); } #[test] fn decrypt_bin_fail_mai() { let arpk = SymKey::Aes256(ARPK.into()); let mut tamp_arcb = mk_arcb(); // tamper MAI tamp_arcb[0x3b] = 17; let ret = AttestationRequest::decrypt_bin(&tamp_arcb, &arpk); println!("{ret:?}"); assert!(matches!( ret, Err(Error::PvCore(pv_core::Error::BinArcbInvAlgorithm(17))) )); } #[test] fn decrypt_bin_fail_aad() { let arpk = SymKey::Aes256(ARPK.into()); let mut tamp_arcb = mk_arcb(); // tamper AAD tamp_arcb[0x3c] = 17; let ret = AttestationRequest::decrypt_bin(&tamp_arcb, &arpk); assert!(matches!(ret, Err(Error::GcmTagMismatch))); } } s390-tools-2.38.0/rust/pv/src/uvattest/attest.rs000066400000000000000000000216021502674226300214120ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use super::AttNonce; use crate::{ attest::AttestationMeasAlg, brcb::BootHdrTags, crypto::calculate_hmac, request::Confidential, uv::ConfigUid, Result, }; use openssl::{ hash::MessageDigest, pkey::{PKeyRef, Private}, }; use std::mem::size_of; use zerocopy::{BigEndian, IntoBytes, U16, U32}; #[cfg(doc)] use crate::attest::AttestationRequest; /// Holds the data to be measured. /// /// The Attestation measurement is an authentication code of the following data: /// /// ```none /// |-------------------------------| /// | From SE-header: | /// | Page List Digest (64) | /// | Address List Digest (64) | /// | Tweak List Digest (64) | /// | SE Header Tag (16) | /// | Configuration Unique Id (16) | /// | user-data length (2) | /// | zeros (2) | /// | additional data length (4) | /// | user-data (0-256) | /// | optional nonce (0 or 16) | /// | additional data (0+) | /// |-------------------------------| /// ``` #[derive(Debug)] pub struct AttestationItems(Confidential>); // tags: BootHdrTags, // cuid: ConfigUid, // user_data_len: U16, // res: u16, // additional_len: U32, // user_data: Vec, // nonce: Option<[u8; 16]>, // additional: Vec, impl AttestationItems { /// Create a new attestation item struct. /// /// * `tags`: The tags from the SE header /// * `cuid`: The Configuration Unique Id from the SE guest for which the Measurement was /// calculated /// * `user`: up to 256 bytes of arbitrary data generated on the SE-guest before measuring /// * `nonce`: technically optional nonce, but [`AttestationRequest`] enforces it /// * `additional`: additional data generated by the Firmware depending on the Attestation flags /// /// If size values of `user` or `additional` are longer than 16/32 bit they are silently /// truncated. `user-data` is limited to 256 bytes architecture wise, and additional data is /// limited to 8 pages by the uvdevice. Larger sizes will produce invalid measurements /// anyhow. pub fn new( tags: &BootHdrTags, cuid: &ConfigUid, user: Option<&[u8]>, nonce: Option<&AttNonce>, additional: Option<&[u8]>, ) -> Self { // expectations are ensured by ExchangeCtx invariants let user = user.unwrap_or(&[]); let user_data_len: U16 = (user.len() as u16).into(); let additional = additional.unwrap_or(&[]); let additional_len: U32 = (additional.len() as u32).into(); let size = size_of::() // PLD ALD TLD TAG + size_of::() + size_of::() // user_len + size_of::() // reserved + size_of::() // additional_len + user.len() + match nonce { Some(_) => size_of::(), None => 0, } + additional.len(); let mut items = Vec::with_capacity(size); items.extend_from_slice(tags.as_bytes()); items.extend_from_slice(cuid.as_bytes()); items.extend_from_slice(user_data_len.as_bytes()); items.extend_from_slice(&[0, 0]); items.extend_from_slice(additional_len.as_bytes()); items.extend_from_slice(user); if let Some(nonce) = nonce { items.extend_from_slice(nonce); } items.extend_from_slice(additional); assert!(items.len() == size); Self(items.into()) } } /// Holds an attestation measurement #[derive(Debug)] #[allow(clippy::len_without_is_empty)] pub struct AttestationMeasurement(Vec); impl AttestationMeasurement { /// Calculate an attestation measurement pub fn calculate( items: AttestationItems, mai: AttestationMeasAlg, meas_key: &PKeyRef, ) -> Result { match mai { AttestationMeasAlg::HmacSha512 => { calculate_hmac(meas_key, MessageDigest::sha512(), items.0.value()).map(Self) } } } /// Returns the length of the [`AttestationMeasurement`]. pub fn len(&self) -> usize { self.0.len() } /// Securely compares the calculated measurement with a given one /// /// Exists early when sizes do not match pub fn eq_secure(&self, other: &[u8]) -> bool { if self.len() != other.len() { return false; } openssl::memcmp::eq(&self.0, other) } } impl AsRef<[u8]> for AttestationMeasurement { fn as_ref(&self) -> &[u8] { self.0.as_ref() } } impl From> for AttestationMeasurement { fn from(value: Vec) -> Self { Self(value) } } #[cfg(test)] mod test { use super::*; use openssl::pkey::PKey; const M_KEY: [u8; 64] = [0x41; 64]; const BOOT_HDR_TAGS: BootHdrTags = BootHdrTags::new([1; 64], [2; 64], [3; 64], [4; 16]); const CUID: [u8; 16] = [5; 16]; const USER: [u8; 256] = [7; 256]; const NONCE: [u8; 16] = [8; 16]; const ADDITIONAL: [u8; 128] = [9; 128]; // just for better output in case of a test failure impl PartialEq<[u8]> for AttestationMeasurement { fn eq(&self, other: &[u8]) -> bool { self.eq_secure(other) } } #[test] fn measurement_all() { const EXP_HMAC: [u8; 64] = [ 0x88, 0x79, 0x4c, 0x62, 0xcc, 0xe7, 0xbc, 0xf2, 0x62, 0x16, 0xde, 0xb3, 0xf4, 0x8f, 0x13, 0xfe, 0xa6, 0x37, 0x4b, 0x6d, 0x7e, 0x35, 0xbc, 0xc5, 0xc2, 0xce, 0x68, 0x12, 0x1d, 0xb6, 0xf4, 0x5d, 0xfc, 0x8c, 0x17, 0x18, 0x56, 0x46, 0x35, 0x49, 0x40, 0x8b, 0xf8, 0xe7, 0xd1, 0xac, 0xa1, 0x1e, 0xfa, 0xd0, 0xa8, 0x78, 0xaf, 0x97, 0xdc, 0x9e, 0x21, 0xa1, 0xfc, 0x2a, 0x32, 0xf3, 0xa6, 0x75, ]; let items = AttestationItems::new( &BOOT_HDR_TAGS, &CUID, Some(&USER), Some(&NONCE), Some(&ADDITIONAL), ); let key = PKey::hmac(&M_KEY).unwrap(); let meas = AttestationMeasurement::calculate(items, AttestationMeasAlg::HmacSha512, &key).unwrap(); assert_eq!(meas, EXP_HMAC[..]); assert!(meas.eq_secure(&EXP_HMAC[..])); } #[test] fn measurement_user_add() { const EXP_HMAC: [u8; 64] = [ 0xfb, 0xd4, 0xf7, 0x38, 0xa3, 0x90, 0xed, 0xd9, 0x47, 0xcd, 0x4f, 0x11, 0xaf, 0x3a, 0x2f, 0x3b, 0xab, 0x2f, 0xdf, 0x8b, 0xf8, 0x9b, 0xf8, 0x1b, 0xeb, 0x49, 0x51, 0x17, 0xf4, 0x38, 0x2c, 0xf4, 0x2f, 0x07, 0x30, 0xc8, 0xc7, 0xd9, 0xe3, 0xca, 0x27, 0xfb, 0x25, 0xad, 0xfc, 0xeb, 0x21, 0x22, 0x4f, 0x57, 0xfd, 0xb3, 0x98, 0xdc, 0xf4, 0x1a, 0x83, 0xc1, 0x46, 0xe6, 0xa2, 0x3d, 0xb7, 0x60, ]; let items = AttestationItems::new(&BOOT_HDR_TAGS, &CUID, Some(&USER), None, Some(&ADDITIONAL)); let key = PKey::hmac(&M_KEY).unwrap(); let meas = AttestationMeasurement::calculate(items, AttestationMeasAlg::HmacSha512, &key).unwrap(); assert_eq!(meas, EXP_HMAC[..]); assert!(meas.eq_secure(&EXP_HMAC[..])); } #[test] fn measurement_add() { const EXP_HMAC: [u8; 64] = [ 0x63, 0x67, 0x1f, 0xbf, 0x29, 0x50, 0x36, 0xeb, 0x10, 0x23, 0xea, 0x71, 0xf7, 0x18, 0x2e, 0x7d, 0x63, 0x43, 0xdc, 0x7b, 0x2d, 0xa5, 0x84, 0xe8, 0x24, 0xd0, 0xa7, 0xd1, 0x98, 0xab, 0x9c, 0xde, 0xd7, 0x56, 0xc9, 0x3b, 0x39, 0x05, 0x0f, 0xfb, 0x76, 0x45, 0x55, 0xb0, 0x1f, 0x88, 0xcb, 0x82, 0x01, 0x7a, 0x6a, 0x15, 0xc7, 0xe0, 0xba, 0xfc, 0x60, 0x05, 0xf1, 0xe4, 0xf7, 0x8a, 0xa1, 0x24, ]; let items = AttestationItems::new(&BOOT_HDR_TAGS, &CUID, None, None, Some(&ADDITIONAL)); let key = PKey::hmac(&M_KEY).unwrap(); let meas = AttestationMeasurement::calculate(items, AttestationMeasAlg::HmacSha512, &key).unwrap(); assert_eq!(meas, EXP_HMAC[..]); assert!(meas.eq_secure(&EXP_HMAC[..])); } #[test] fn measurement_minimal() { const EXP_HMAC: [u8; 64] = [ 0xc5, 0xc3, 0x4c, 0x93, 0x83, 0x5d, 0x1e, 0xc2, 0x3f, 0x5c, 0x2d, 0x77, 0x8d, 0xfa, 0x20, 0x12, 0x9b, 0x11, 0xb3, 0x05, 0x60, 0x17, 0x42, 0xcb, 0x2f, 0x38, 0xe0, 0xed, 0x98, 0x94, 0xdc, 0xdb, 0x73, 0xfc, 0x86, 0x95, 0xab, 0x6a, 0x8d, 0xba, 0xd0, 0x74, 0x40, 0x73, 0xdd, 0xc8, 0x1a, 0x5e, 0xaa, 0xfa, 0x52, 0xe4, 0xa1, 0x5a, 0xf8, 0xde, 0xb8, 0xd7, 0x61, 0x09, 0x19, 0x22, 0x84, 0x7f, ]; let items = AttestationItems::new(&BOOT_HDR_TAGS, &CUID, None, None, None); let key = PKey::hmac(&M_KEY).unwrap(); let meas = AttestationMeasurement::calculate(items, AttestationMeasAlg::HmacSha512, &key).unwrap(); assert_eq!(meas, EXP_HMAC[..]); assert!(meas.eq_secure(&EXP_HMAC[..])); } } s390-tools-2.38.0/rust/pv/src/uvsecret.rs000066400000000000000000000006101502674226300200630ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 //! Provides functionality to manage the UV secret store. //! //! Provides functionality to build `add-secret` requests. //! Also provides interfaces, to dispatch `Add Secret`, `Lock Secret Store`, //! and `List Secrets` requests, pub mod asrcb; pub mod ext_secret; pub mod guest_secret; pub mod retr_secret; pub mod user_data; s390-tools-2.38.0/rust/pv/src/uvsecret/000077500000000000000000000000001502674226300175205ustar00rootroot00000000000000s390-tools-2.38.0/rust/pv/src/uvsecret/asrcb.rs000066400000000000000000000240401502674226300211600ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 use super::{guest_secret::ListableSecretHdr, user_data::UserData}; use crate::{ assert_size, crypto::{hkdf_rfc_5869, AeadEncryptionResult}, misc::Flags, req::{Aad, BinReqValues, Keyslot, ReqEncrCtx}, request::{BootHdrTags, Confidential, Request}, secret::{ExtSecret, GuestSecret}, uv::{ConfigUid, UvFlags}, Result, }; use openssl::{ md::Md, pkey::{PKey, Private, Public}, }; use pv_core::{request::RequestVersion, secret::AddSecretMagic, uv::SecretId}; use zerocopy::{Immutable, IntoBytes}; /// Authenticated data w/o user data #[repr(C)] #[derive(Debug, Clone, Copy, IntoBytes, Immutable)] struct ReqAuthData { flags: UvFlags, boot_tags: BootHdrTags, cuid: ConfigUid, reserved90: [u8; 0x100], } assert_size!(ReqAuthData, 0x1e8); impl ReqAuthData { fn new>(boot_tags: BootHdrTags, flags: F) -> Self { Self { flags: flags.into(), boot_tags, cuid: [0; 0x10], reserved90: [0; 0x100], } } } #[derive(Debug)] struct ReqConfData { secret: GuestSecret, extension_secret: Confidential<[u8; 32]>, } impl ReqConfData { fn to_bytes(&self) -> Confidential> { let secret = self.secret.confidential(); let mut v = vec![0; secret.len() + 32]; if !secret.is_empty() { v[..secret.len()].copy_from_slice(secret); } v[secret.len()..32 + secret.len()] .copy_from_slice(self.extension_secret.value().as_slice()); v.into() } } /// Flags for [`AddSecretRequest`] #[derive(Default, Clone, Copy, Debug)] pub struct AddSecretFlags(UvFlags); impl AddSecretFlags { /// Enables the disable-dump flag /// /// After the request was dispatched successfully, /// the UV will not provide any dump decryption information for the SE-guest anymore. pub fn set_disable_dump(&mut self) { self.0.set_bit(0) } } impl From<&u64> for AddSecretFlags { fn from(v: &u64) -> Self { Self(v.into()) } } impl From for UvFlags { fn from(f: AddSecretFlags) -> Self { f.0 } } /// Versions for [`AddSecretRequest`] #[repr(u32)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum AddSecretVersion { /// Version 1 (= 0x0100) One = 0x0100, #[cfg(not(doc))] #[cfg(any(debug_assertions, test))] /// Only for testing Inv = 0, } impl From for RequestVersion { fn from(val: AddSecretVersion) -> Self { val as Self } } /// Add-secret request Control Block /// /// An ASRCB wraps a secret to securely transport it to the Ultravisor. /// /// Layout: /// ```none /// _______________________________________________________________ /// | generic header (48) /// | --------------------------------------------------- | /// | Plaintext Add-Secret flags (8) | /// | SE header tags: PLD(64) ALD(64) TLD(64) HeaderTag(16) | /// | Configuration unique ID(16) (Attestation) | /// | Optional, defaults to 0 | /// | Reserved(256) | /// | User Data(512) (reserved) | /// | Customer Public Key (160) generated for each request | /// | N Keyslots(80 each) | /// | Secret header (Secret dependent) | /// | --------------------------------------------------- | /// | Secret to add (Secret type dependent)(may be 0 bytes) | Encrypted /// | Extension secret(32) Optional, defaults to 0 | Encrypted /// | --------------------------------------------------- | /// | AES GCM Tag (16) | /// |_____________________________________________________________| /// ``` #[derive(Debug)] pub struct AddSecretRequest { version: AddSecretVersion, aad: ReqAuthData, keyslots: Vec, conf: ReqConfData, user_data: UserData, } impl AddSecretRequest { /// Offset of the user-data in the add-secret request in bytes pub(super) const V1_USER_DATA_OFFS: usize = 0x218; /// Create a new add-secret request. /// /// The request has no extension secret, no configuration UID, no host-keys, /// and no user data pub fn new( version: AddSecretVersion, secret: GuestSecret, boot_tags: BootHdrTags, flags: AddSecretFlags, ) -> Self { Self { conf: ReqConfData { extension_secret: Confidential::new([0; 32]), secret, }, aad: ReqAuthData::new(boot_tags, flags), keyslots: vec![], version, user_data: UserData::Null, } } /// Sets the Configuration Unique Id of this [`AddSecretRequest`]. pub fn set_cuid(&mut self, cuid: ConfigUid) { self.aad.cuid = cuid; } /// Sets the extension secret of this [`AddSecretRequest`]. /// /// # Errors /// /// This function will return an error if the key derivation fails for a [`ExtSecret::Derived`]. pub fn set_ext_secret(&mut self, ext_secret: ExtSecret) -> Result<()> { const DER_EXT_SECRET_INFO: &[u8] = "IBM Z Ultravisor Add-Secret".as_bytes(); self.conf.extension_secret = match ext_secret { ExtSecret::Simple(s) => s, ExtSecret::Derived(cck) => hkdf_rfc_5869( Md::sha512(), cck.value(), self.aad.boot_tags.tag(), DER_EXT_SECRET_INFO, )? .into(), }; Ok(()) } /// Returns a reference to the guest secret of this [`AddSecretRequest`]. pub fn guest_secret(&self) -> &GuestSecret { &self.conf.secret } /// Add user-data to the Add-Secret request /// /// (Signed) user-data is a non-architectual feature. It allows to add arbitrary /// data (message) to the request, that is signed optionally with an user defined key. /// Allowed keys are: /// - no key (up to 512 bytes of message) /// - EC SECP521R1 (up to 256 byte message) /// - RSA 2048 bit (up to 256 byte message) /// - RSA 3072 bit (up to 128 byte message) /// /// The signature can be verified during the verification of the secret-request on the target /// machine. pub fn set_user_data>>( &mut self, msg: T, skey: Option>, ) -> Result<()> { self.user_data = UserData::new(skey, msg.into())?; Ok(()) } /// Compiles the authenticated area of this request fn aad(&self, ctx: &ReqEncrCtx, conf_len: usize) -> Result> { let cust_pub_key = ctx.key_coords()?; let secr_auth = self.conf.secret.auth(); let user_data = self.user_data.data(); let mut aad: Vec = Vec::with_capacity(5 + self.keyslots.len()); aad.push(Aad::Plain(self.aad.as_bytes())); if let Some(data) = user_data.0 { aad.push(Aad::Plain(data)); } if let Some(data) = &user_data.1 { aad.push(Aad::Plain(data)); } aad.push(Aad::Plain(cust_pub_key.as_ref())); self.keyslots.iter().for_each(|k| aad.push(Aad::Ks(k))); aad.push(Aad::Plain(secr_auth.get())); ctx.build_aad(self.version.into(), &aad, conf_len, self.user_data.magic()) } #[doc(hidden)] #[cfg(any(debug_assertions, test))] pub fn aad_and_conf(&self, ctx: &ReqEncrCtx) -> Result<(Vec, Vec)> { let conf = self.conf.to_bytes(); let aad = self.aad(ctx, conf.value().len())?; Ok((aad, conf.value().to_owned())) } #[doc(hidden)] #[cfg(any(debug_assertions, test))] pub fn no_encrypt(&self, ctx: &ReqEncrCtx) -> Result> { let (mut res, mut conf) = self.aad_and_conf(ctx)?; res.append(&mut conf); res.append(&mut vec![0x24; 32]); Ok(res) } /// Encrypts data, sign request with user-provided signing key, insert signature into aad, /// calculate request tag fn encrypt_with_signed_user_data(&self, ctx: &ReqEncrCtx) -> Result> { // encrypt data w/o aead let conf = self.conf.to_bytes(); let aad = self.aad(ctx, conf.value().len())?; let AeadEncryptionResult { mut buf, aad_range, encr_range, .. } = ctx.encrypt_aead(&aad, conf.value())?; drop(aad); // sign aad+encrypted data (w/o tag) with user signning key // add signature to authenticated data starting with USER_DATA_OFFS self.user_data.sign( &mut buf[aad_range.start..encr_range.end], Self::V1_USER_DATA_OFFS, )?; // encrypt again with signed data buf[encr_range.clone()].copy_from_slice(conf.value()); ctx.encrypt_aead(&buf[aad_range], &buf[encr_range]) .map(|res| res.into_buf()) } /// Get a copy of the secret ID if any pub fn bin_id(asrcb: &[u8]) -> Result> { AddSecretMagic::try_from_bytes(asrcb)?; BinReqValues::get(asrcb) .map(|req| req.req_dep_aad::().map(|a| a.id.clone())) } /// Get a copy of the add secret request tag pub fn bin_tag(asrcb: &[u8]) -> Result> { AddSecretMagic::try_from_bytes(asrcb)?; BinReqValues::get(asrcb).map(|v| v.tag().to_vec()) } } impl Request for AddSecretRequest { fn encrypt(&self, ctx: &ReqEncrCtx) -> Result> { match self.user_data { UserData::Null | UserData::Unsigned(_) => { let conf = self.conf.to_bytes(); let aad = self.aad(ctx, conf.value().len())?; ctx.encrypt_aead(&aad, conf.value()) .map(|res| res.into_buf()) } _ => self.encrypt_with_signed_user_data(ctx), } } fn add_hostkey(&mut self, hostkey: PKey) { self.keyslots.push(Keyslot::new(hostkey)) } } s390-tools-2.38.0/rust/pv/src/uvsecret/ext_secret.rs000066400000000000000000000007721502674226300222410ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 use crate::request::Confidential; /// Extension Secret for [`crate::secret::AddSecretRequest`] #[derive(Debug, Clone)] pub enum ExtSecret { /// A bytepattern that must be equal for each request targeting the same SE-guest instance Simple(Confidential<[u8; 32]>), // contains the secret /// A secret that is derived from the Customer communication key from the SE-header Derived(Confidential<[u8; 32]>), // contains the cck } s390-tools-2.38.0/rust/pv/src/uvsecret/guest_secret.rs000066400000000000000000000576251502674226300226010ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 #[allow(unused_imports)] // used for more convenient docstring use super::asrcb::AddSecretRequest; use crate::{ assert_size, crypto::{hash, random_array, SymKeyType}, request::{ openssl::{NID_ED25519, NID_ED448}, Confidential, }, uv::{ AesSizes, AesXtsSizes, EcCurves, HmacShaSizes, ListableSecretType, RetrievableSecret, RetrieveCmd, SecretId, }, Error, Result, }; use openssl::{ hash::MessageDigest, nid::Nid, pkey::{Id, PKey, PKeyRef, Private}, }; use pv_core::static_assert; use serde::{Deserialize, Serialize}; use std::fmt::Display; use zerocopy::{BigEndian, KnownLayout}; use zerocopy::{FromBytes, Immutable, IntoBytes, U16, U32}; const ASSOC_SECRET_SIZE: usize = 32; const CCK_SIZE: usize = 32; /// Maximum size of a plain-text secret payload (8190) pub(crate) const MAX_SIZE_PLAIN_PAYLOAD: usize = RetrieveCmd::MAX_SIZE - 2; static_assert!(MAX_SIZE_PLAIN_PAYLOAD == 8190); /// A Secret to be added in [`AddSecretRequest`] #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] pub enum GuestSecret { /// No guest secret Null, /// Association secret used to associate an extension card to a SE guest /// /// Create Associations using [`GuestSecret::association`] Association { /// Name of the secret name: String, /// SHA256 hash of [`GuestSecret::Association::name`] id: SecretId, /// Confidential actual association secret (32 bytes) #[serde(skip)] secret: Confidential<[u8; ASSOC_SECRET_SIZE]>, }, /// Retrievable key /// /// Create Retrievables using [`GuestSecret::retrievable`] /// Secret size is always valid for the type/kind Retrievable { /// Retrievable secret type kind: RetrievableSecret, /// Name of the secret name: String, /// SHA256 hash of [`GuestSecret::RetrievableKey::name`] id: SecretId, /// Confidential actual retrievable secret #[serde(skip)] secret: Confidential>, }, /// CCK update /// /// Create CCK updates using [`GuestSecret::update_cck`] UpdateCck { /// Confidential actual CCK (32 bytes) #[serde(skip)] secret: Confidential<[u8; CCK_SIZE]>, }, } macro_rules! retr_constructor { ($(#[$err:meta])* | $(#[$kind:meta])* => $type: ty, $func: ident) => { /// Create a new $(#[$kind])* /// [`GuestSecret::Retrievable`] secret. /// /// * `name` - Name of the secret. Will be hashed into a 32 byte id /// * `secret` - the secret value /// /// # Errors /// $(#[$err])* pub fn $func(name: &str, secret: $type) -> Result { let (kind, secret) = $func(secret)?; Ok(Self::Retrievable { kind, name: name.to_string(), id: Self::name_to_id(name)?, secret, }) } }; } impl GuestSecret { /// Hashes the name with sha256 pub fn name_to_id(name: &str) -> Result { let id: [u8; SecretId::ID_SIZE] = hash(MessageDigest::sha256(), name.as_bytes())? .to_vec() .try_into() .unwrap(); Ok(id.into()) } /// Create a new [`GuestSecret::Association`]. /// /// * `name` - Name of the secret. Will be hashed into a 32 byte id /// * `secret` - Value of the secret. Random if [`Option::None`] /// /// # Errors /// /// This function will return an error if OpenSSL cannot create a hash. pub fn association(name: &str, secret: O) -> Result where O: Into>, { let secret = match secret.into() { Some(s) => s, None => random_array()?, }; Ok(Self::Association { name: name.to_string(), id: Self::name_to_id(name)?, secret: secret.into(), }) } retr_constructor!(#[doc = r"This function will return an error if the secret is larger than 8 pages"] | #[doc = r"plaintext"] => Confidential>, plaintext); retr_constructor!(#[doc = r"This function will return an error if OpenSSL cannot create a hash or the secret size is invalid"] | #[doc = r"AES Key"] => Confidential>, aes); retr_constructor!(#[doc = r"This function will return an error if OpenSSL cannot create a hash or the secret size is invalid"] | #[doc = r"AES-XTS Key"] => Confidential>, aes_xts); retr_constructor!(#[doc = r"This function will return an error if OpenSSL cannot create a hash or the secret size is invalid"] | #[doc = r"HMAC-SHA Key"] => Confidential>, hmac_sha); retr_constructor!(#[doc = r"This function will return an error if OpenSSL cannot create a hash or the curve is invalid"] | #[doc = r"EC PRIVATE Key"] => PKey, ec); /// Create a new [`GuestSecret::UpdateCck`]. /// /// * `secret` - New CCK. pub fn update_cck(secret: [u8; CCK_SIZE]) -> Self { Self::UpdateCck { secret: secret.into(), } } /// Use the name as ID, do not hash it pub fn no_hash_name(&mut self) { match self { Self::Null | Self::UpdateCck { .. } => (), Self::Association { name, ref mut id, .. } | Self::Retrievable { name, ref mut id, .. } => id.clone_from(&SecretId::from_string(name)), } } /// Reference to the confidential data pub fn confidential(&self) -> &[u8] { match &self { Self::Null => &[], Self::Association { secret, .. } => secret.value().as_slice(), Self::Retrievable { secret, .. } => secret.value(), Self::UpdateCck { secret, .. } => secret.value(), } } /// Creates the non-confidential part of the secret ad-hoc pub(crate) fn auth(&self) -> SecretAuth { match &self { Self::Null => SecretAuth::Null, Self::UpdateCck { .. } => SecretAuth::UpdateCck, // Panic: other secret types are list-able -> no panic listable => { SecretAuth::Listable(ListableSecretHdr::from_guest_secret(listable).unwrap()) } } } /// Returns the UV type ID fn kind(&self) -> u16 { match self { // Null is not listable, but the ListableSecretType provides the type constant (1) Self::Null => ListableSecretType::NULL, Self::Association { .. } => ListableSecretType::ASSOCIATION, Self::Retrievable { kind, .. } => kind.into(), Self::UpdateCck { .. } => ListableSecretType::UPDATE_CCK, } } /// Size of the secret value fn secret_len(&self) -> u32 { match self { Self::Null => 0, Self::Association { secret, .. } => secret.value().len() as u32, Self::Retrievable { secret, .. } => secret.value().len() as u32, Self::UpdateCck { secret } => secret.value().len() as u32, } } /// Returns the ID of the secret type (if any) fn id(&self) -> Option { match self { Self::Null | Self::UpdateCck { .. } => None, Self::Association { id, .. } | Self::Retrievable { id, .. } => Some(id.to_owned()), } } } type RetrKeyInfo = (RetrievableSecret, Confidential>); fn extend_to_multiple(mut key: Vec, multiple: usize) -> Confidential> { match key.len().checked_rem(multiple) { Some(0) | None => key, Some(m) => { key.resize(key.len() + multiple - m, 0); key } } .into() } /// Get a plain-text key /// /// ```none /// size U16 | payload (0-8190) bytes /// ``` fn plaintext(inp: Confidential>) -> Result { let key_len = inp.value().len(); if key_len > MAX_SIZE_PLAIN_PAYLOAD { return Err(Error::RetrInvKey { what: "key size", value: key_len.to_string(), kind: RetrievableSecret::PlainText.to_string(), exp: RetrievableSecret::PlainText.expected(), }); } let mut key = Vec::with_capacity(2 + inp.value().len()); let key_len: U16 = (key_len as u16).into(); key.extend_from_slice(key_len.as_bytes()); key.extend_from_slice(inp.value()); let key = extend_to_multiple(key, SymKeyType::AES_256_GCM_BLOCK_LEN); Ok((RetrievableSecret::PlainText, key)) } /// Get an AES-key fn aes(key: Confidential>) -> Result { let key_len = key.value().len() as u32; let bit_size = bitsize(key_len); match AesSizes::from_bits(bit_size) { Some(size) => Ok((RetrievableSecret::Aes(size), key)), None => { // Use some AES type to get exp sizes and name let kind = RetrievableSecret::Aes(AesSizes::Bits128); Err(Error::RetrInvKey { what: "key size", value: bit_size.to_string(), kind: format!("{kind:#}"), exp: kind.expected(), }) } } } /// Get an AES-XTS-key fn aes_xts(key: Confidential>) -> Result { let key_len = key.value().len() as u32; let bit_size = bitsize(key_len / 2); match AesXtsSizes::from_bits(bit_size) { Some(size) => Ok((RetrievableSecret::AesXts(size), key)), None => { // Use some AES-XTS type to get exp sizes and name let kind = RetrievableSecret::AesXts(AesXtsSizes::Bits128); Err(Error::RetrInvKey { what: "key size", value: bit_size.to_string(), kind: format!("{kind:#}"), exp: kind.expected(), }) } } } /// Get an HMAC-SHA-key fn hmac_sha(key: Confidential>) -> Result { let key_len = key.value().len() as u32; let size = bitsize(key_len / 2); match HmacShaSizes::from_sha_size(size) { Some(size) => Ok((RetrievableSecret::HmacSha(size), key)), None => { // Use some HMAC type to get exp sizes and name let kind = RetrievableSecret::HmacSha(HmacShaSizes::Sha256); Err(Error::RetrInvKey { what: "key size", value: size.to_string(), kind: format!("{kind:#}"), exp: kind.expected(), }) } } } /// Get an EC-private-key fn ec(key: PKey) -> Result { // reads & left-pads Edward EC keys fn pad_ed_key(pkey: &PKeyRef, curve: &EcCurves) -> Result> { let raw_key = pkey.raw_private_key()?; match raw_key.len().cmp(&curve.exp_key_size()) { std::cmp::Ordering::Less => { let mut key = Vec::with_capacity(curve.exp_key_size()); key.extend_from_slice(&vec![0u8; curve.exp_key_size() - raw_key.len()]); key.extend_from_slice(&raw_key); Ok(key) } std::cmp::Ordering::Equal => Ok(raw_key), std::cmp::Ordering::Greater => Err(Error::InvalSslData), } } let nid = match key.id() { Id::EC => key.ec_key()?.group().curve_name().unwrap_or(Nid::UNDEF), id @ (Id::ED25519 | Id::ED448) => Nid::from_raw(id.as_raw()), _ => Nid::UNDEF, }; let kind = match nid { Nid::X9_62_PRIME256V1 => EcCurves::Secp256R1, Nid::SECP384R1 => EcCurves::Secp384R1, Nid::SECP521R1 => EcCurves::Secp521R1, NID_ED25519 => EcCurves::Ed25519, NID_ED448 => EcCurves::Ed448, nid => { // Use some EC type to get exp sizes and name let ec = RetrievableSecret::Ec(EcCurves::Secp521R1); return Err(Error::RetrInvKey { what: "curve or format", kind: format!("{ec:#}"), value: nid.long_name()?.to_string(), exp: ec.expected(), }); } }; let key = match key.id() { Id::EC => key .ec_key()? .private_key() .to_vec_padded(kind.exp_key_size() as i32)?, // ED keys are not handled via the EC struct in OpenSSL. Id::ED25519 | Id::ED448 => pad_ed_key(&key, &kind)?, _ => unreachable!(), }; Ok((RetrievableSecret::Ec(kind), key.into())) } #[inline(always)] const fn bitsize(bytesize: u32) -> u32 { bytesize * 8 } impl Display for GuestSecret { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Null => write!(f, "Meta"), gs => { let kind: U16 = gs.kind().into(); let st: ListableSecretType = kind.get().into(); write!(f, "{st}") } } } } #[derive(Debug)] pub(crate) enum SecretAuth { Null, Listable(ListableSecretHdr), UpdateCck, } impl SecretAuth { const NULL_HDR: NullSecretHdr = NullSecretHdr::new(); const UPDATE_CCK_HDR: UpdateCckHdr = UpdateCckHdr::new(); pub fn get(&self) -> &[u8] { match self { Self::Null => Self::NULL_HDR.as_bytes(), Self::Listable(h) => h.as_bytes(), Self::UpdateCck => Self::UPDATE_CCK_HDR.as_bytes(), } } } #[repr(C)] #[derive(Debug, IntoBytes, FromBytes, Immutable, KnownLayout)] struct NullSecretHdr { res0: u16, kind: U16, secret_len: U32, res8: u64, } assert_size!(NullSecretHdr, 0x10); impl NullSecretHdr { const fn new() -> Self { Self { res0: 0, kind: U16::new(ListableSecretType::NULL), secret_len: U32::ZERO, res8: 0, } } } #[repr(C)] #[derive(Debug, IntoBytes, FromBytes, Immutable, KnownLayout)] pub(crate) struct ListableSecretHdr { res0: u16, kind: U16, secret_len: U32, res8: u64, pub(crate) id: SecretId, } assert_size!(ListableSecretHdr, 0x30); impl ListableSecretHdr { fn from_guest_secret(gs: &GuestSecret) -> Option { Some(Self { res0: 0, kind: gs.kind().into(), secret_len: gs.secret_len().into(), res8: 0, id: gs.id()?, }) } } #[repr(C)] #[derive(Debug, IntoBytes, Default, Immutable)] struct UpdateCckHdr { res0: u16, kind: U16, secret_len: U32, res8: u64, res10: [u8; 0x20], } assert_size!(UpdateCckHdr, 0x30); impl UpdateCckHdr { const fn new() -> Self { Self { res0: 0, kind: U16::new(ListableSecretType::UPDATE_CCK), secret_len: U32::new(CCK_SIZE as u32), res8: 0, res10: [0; 0x20], } } } #[cfg(test)] mod test { use super::HmacShaSizes as HmacSizes; use super::RetrievableSecret::*; use super::*; use openssl::ec::{EcGroup, EcKey}; use pv_core::uv::AesSizes; use serde_test::{assert_tokens, Token}; #[test] fn association() { let secret_value = [0x11; 32]; let exp_id = [ 0x75, 0xad, 0x01, 0xb4, 0x03, 0xa9, 0xe4, 0x59, 0x5d, 0xf0, 0x7a, 0xce, 0x38, 0x12, 0x97, 0x99, 0xdd, 0xad, 0x90, 0x8a, 0x8f, 0x82, 0xf9, 0xc3, 0x2c, 0xdd, 0x7d, 0x53, 0xef, 0xc7, 0x3c, 0x62, ]; let name = "association secret".to_string(); let secret = GuestSecret::association("association secret", secret_value).unwrap(); let exp = GuestSecret::Association { name, id: exp_id.into(), secret: secret_value.into(), }; assert_eq!(secret, exp); } macro_rules! retr_test { ($name: ident, $func: ident, $size: expr, $exp_kind: expr) => { #[test] fn $name() { let secret_value = vec![0x11; $size]; let name = "test retr secret".to_string(); let secret = GuestSecret::$func(&name, secret_value.clone().into()).unwrap(); let exp_id = [ 0x61, 0x2c, 0xd6, 0x3e, 0xa8, 0xf2, 0xc1, 0x15, 0xc1, 0xe, 0x15, 0xb8, 0x8a, 0x90, 0x16, 0xc1, 0x55, 0xef, 0x9c, 0x7c, 0x2c, 0x8e, 0x56, 0xd0, 0x78, 0x4c, 0x8a, 0x1d, 0xc9, 0x3a, 0x80, 0xba, ]; let exp = GuestSecret::Retrievable { kind: $exp_kind, name, id: exp_id.into(), secret: secret_value.into(), }; assert_eq!(exp, secret); } }; } retr_test!(retr_aes_128, aes, 16, Aes(AesSizes::Bits128)); retr_test!(retr_aes_192, aes, 24, Aes(AesSizes::Bits192)); retr_test!(retr_aes_256, aes, 32, Aes(AesSizes::Bits256)); retr_test!(retr_aes_xts_128, aes_xts, 32, AesXts(AesXtsSizes::Bits128)); retr_test!(retr_aes_xts_256, aes_xts, 64, AesXts(AesXtsSizes::Bits256)); retr_test!(retr_aes_hmac_256, hmac_sha, 64, HmacSha(HmacSizes::Sha256)); retr_test!(retr_aes_hmac_512, hmac_sha, 128, HmacSha(HmacSizes::Sha512)); #[test] fn update_cck() { let new_cck = [11; 32]; let req = GuestSecret::update_cck(new_cck); let exp = GuestSecret::UpdateCck { secret: new_cck.into(), }; assert_eq!(req, exp); } #[test] fn plaintext_no_pad() { let key = vec![0, 14, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7]; let name = "PLAINTEXT_PAD".to_string(); let secret = GuestSecret::plaintext(&name, key[2..].to_vec().into()).unwrap(); let exp_id = [ 15, 123, 176, 210, 135, 231, 220, 232, 148, 93, 198, 195, 165, 212, 214, 129, 45, 1, 94, 11, 167, 18, 151, 15, 120, 254, 13, 109, 173, 186, 37, 74, ]; let exp = GuestSecret::Retrievable { kind: PlainText, name, id: exp_id.into(), secret: key.into(), }; assert_eq!(secret, exp); } #[test] fn plaintext_pad() { let key = vec![0, 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0]; let name = "PLAINTEXT_PAD".to_string(); let secret = GuestSecret::plaintext(&name, key[2..12].to_vec().into()).unwrap(); let exp_id = [ 15, 123, 176, 210, 135, 231, 220, 232, 148, 93, 198, 195, 165, 212, 214, 129, 45, 1, 94, 11, 167, 18, 151, 15, 120, 254, 13, 109, 173, 186, 37, 74, ]; let exp = GuestSecret::Retrievable { kind: PlainText, name, id: exp_id.into(), secret: key.into(), }; assert_eq!(secret, exp); } #[track_caller] fn gen_ec(nid: Nid) -> PKey { let group = EcGroup::from_curve_name(nid).unwrap(); let key = EcKey::generate(&group).unwrap(); PKey::from_ec_key(key).unwrap() } #[track_caller] fn test_ec(grp: Nid, exp_kind: EcCurves, exp_len: usize) { let key = match grp { NID_ED25519 => PKey::generate_ed25519().unwrap(), NID_ED448 => PKey::generate_ed448().unwrap(), nid => gen_ec(nid), }; let (kind, key) = ec(key).unwrap(); assert_eq!(kind, Ec(exp_kind)); assert_eq!(key.value().len(), exp_len); } #[test] fn retr_ec() { test_ec(Nid::X9_62_PRIME256V1, EcCurves::Secp256R1, 32); test_ec(Nid::SECP384R1, EcCurves::Secp384R1, 48); test_ec(Nid::SECP521R1, EcCurves::Secp521R1, 80); test_ec(NID_ED25519, EcCurves::Ed25519, 32); test_ec(NID_ED448, EcCurves::Ed448, 64); } #[test] fn retr_ec_pad() { let pkey = PKey::generate_ed448().unwrap(); let (_, key) = ec(pkey).unwrap(); assert_eq!(key.value()[..7], [0; 7]); let pkey = gen_ec(Nid::SECP521R1); let (_, key) = ec(pkey).unwrap(); assert_eq!(key.value()[..14], [0; 14]); } #[test] fn asc_parse() { let id = [ 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, ]; let asc = GuestSecret::Association { name: "test123".to_string(), id: id.into(), secret: [0; 32].into(), }; assert_tokens( &asc, &[ Token::StructVariant { name: "GuestSecret", variant: "Association", len: 2, }, Token::String("name"), Token::String("test123"), Token::String("id"), Token::String("0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"), Token::StructVariantEnd, ], ); } #[test] fn retrievable_parse() { let id = [ 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, ]; let asc = GuestSecret::Retrievable { kind: PlainText, name: "test123".to_string(), id: id.into(), secret: vec![].into(), }; assert_tokens( &asc, &[ Token::StructVariant { name: "GuestSecret", variant: "Retrievable", len: 3, }, Token::String("kind"), Token::String("3 (PLAINTEXT)"), Token::String("name"), Token::String("test123"), Token::String("id"), Token::String("0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"), Token::StructVariantEnd, ], ); } #[test] fn update_cck_parse() { let cck = GuestSecret::UpdateCck { secret: [0; 32].into(), }; assert_tokens( &cck, &[ Token::StructVariant { name: "GuestSecret", variant: "UpdateCck", len: 0, }, Token::StructVariantEnd, ], ) } #[test] fn guest_secret_bin_null() { let gs = GuestSecret::Null; let gs_bytes = gs.auth(); let exp = vec![0u8, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; assert_eq!(exp, gs_bytes.get()); assert_eq!(&Vec::::new(), gs.confidential()) } #[test] fn guest_secret_bin_asoc() { let gs = GuestSecret::Association { name: "test".to_string(), id: [1; 32].into(), secret: [2; 32].into(), }; let gs_bytes_auth = gs.auth(); let mut exp = vec![0u8, 0, 0, 2, 0, 0, 0, 0x20, 0, 0, 0, 0, 0, 0, 0, 0]; exp.extend([1; 32]); assert_eq!(exp, gs_bytes_auth.get()); assert_eq!(&[2; 32], gs.confidential()); } #[test] fn guest_secret_bin_retr() { let gs = GuestSecret::Retrievable { kind: PlainText, name: "test".to_string(), id: [1; 32].into(), secret: vec![2; 32].into(), }; let auth = gs.auth(); let gs_bytes_auth = auth.get(); let mut exp = vec![0u8, 0, 0, 3, 0, 0, 0, 0x20, 0, 0, 0, 0, 0, 0, 0, 0]; exp.extend([1; 32]); assert_eq!(exp, gs_bytes_auth); assert_eq!(&[2; 32], gs.confidential()); } #[test] fn guest_secret_bin_cck() { let gs = GuestSecret::UpdateCck { secret: [2; 32].into(), }; let gs_bytes_auth = gs.auth(); let mut exp = vec![0u8, 0, 0, 0x16, 0, 0, 0, 0x20]; exp.extend([0; 40]); assert_eq!(exp, gs_bytes_auth.get()); assert_eq!(&[2; 32], gs.confidential()); } } s390-tools-2.38.0/rust/pv/src/uvsecret/retr_secret.rs000066400000000000000000000160341502674226300224130ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use crate::{crypto::SymKeyType, pem::Pem, uvsecret::guest_secret::MAX_SIZE_PLAIN_PAYLOAD, Result}; use log::warn; use pv_core::{ request::Confidential, uv::{ListableSecretType, RetrievableSecret, RetrieveCmd}, }; use zerocopy::BigEndian; use zerocopy::{FromBytes, U16}; /// An IBM Protected Key /// /// A protected key, writeable as pem. /// /// Will convert into PEM as: /// ```PEM ///-----BEGIN IBM PROTECTED KEY----- ///kind: /// /// ///-----END IBM PROTECTED KEY----- /// ``` #[derive(Debug, PartialEq, Eq)] pub struct IbmProtectedKey { kind: ListableSecretType, key: Confidential>, } impl IbmProtectedKey { /// Get the binary representation of the key. pub fn data(&self) -> &[u8] { self.key.value() } /// Converts a [`IbmProtectedKey`] into a vector. pub fn into_bytes(self) -> Confidential> { self.key } /// Get the data in PEM format. /// /// # Errors /// /// This function will return an error if the PEM conversion failed (very unlikely). pub fn to_pem(&self) -> Result { Pem::new( "IBM PROTECTED KEY", format!("kind: {}", self.kind), self.key.value(), ) } fn new(kind: ListableSecretType, key: K) -> Self where K: Into>>, { Self { kind, key: key.into(), } } } impl From for RetrievedSecret { fn from(value: RetrieveCmd) -> Self { let kind = value.meta_data().stype(); let key = value.into_key(); match kind { ListableSecretType::Retrievable(RetrievableSecret::PlainText) => { // Will not run into default, retrieve has a granularity of 16 bytes and 16 bytes is the // minimum size let len = U16::::read_from_prefix(key.value()) .unwrap_or_default() .0 .get() as usize; // Test if the plain text secret has a size: // 1. len <= 8190 // 2. first two bytes are max 15 less than buffer-size+2 i.e. smaller than the // block length // 3. bytes after len + 2 are zero match len <= MAX_SIZE_PLAIN_PAYLOAD && key.value().len() - (len + 2) < SymKeyType::AES_256_GCM_BLOCK_LEN && key.value()[len + 2..].iter().all(|c| *c == 0) { false => Self::Plaintext(key), true => Self::Plaintext(key.value()[2..len + 2].to_vec().into()), } } kind => { match kind { ListableSecretType::Retrievable(_) => (), _ => warn!("Retrieved an unretrievable Secret! Will continue; interpreting it as a protected key."), } Self::ProtectedKey(IbmProtectedKey::new(kind, key)) } } } } /// A retrieved Secret. #[derive(Debug, PartialEq, Eq)] pub enum RetrievedSecret { /// A plaintext secret Plaintext(Confidential>), /// An [`IbmProtectedKey`] ProtectedKey(IbmProtectedKey), } impl RetrievedSecret { /// Create a new IBM PROTECTED KEY object pub fn from_cmd(cmd: RetrieveCmd) -> Self { cmd.into() } /// Get the binary representation of the key. pub fn data(&self) -> &[u8] { match self { RetrievedSecret::Plaintext(p) => p.value(), RetrievedSecret::ProtectedKey(p) => p.data(), } } /// Converts a [`IbmProtectedKey`] into a vector. pub fn into_bytes(self) -> Confidential> { match self { RetrievedSecret::Plaintext(p) => p, RetrievedSecret::ProtectedKey(p) => p.into_bytes(), } } /// Get the data in PEM format. /// /// # Errors /// /// This function will return an error if the PEM conversion failed (very unlikely). pub fn to_pem(&self) -> Result { match self { RetrievedSecret::Plaintext(p) => Pem::new("PLAINTEXT SECRET", None, p.value()), RetrievedSecret::ProtectedKey(p) => p.to_pem(), } } } #[cfg(test)] mod test { use super::*; use pv_core::uv::*; fn mk_retr(secret: &[u8]) -> RetrievedSecret { let entry = SecretEntry::new( 0, ListableSecretType::Retrievable(RetrievableSecret::PlainText), SecretId::default(), secret.len() as u32, ); let mut cmd = RetrieveCmd::from_entry(entry).unwrap(); cmd.data().unwrap().copy_from_slice(secret); RetrievedSecret::from_cmd(cmd) } #[test] fn from_retr_cmd() { let secret = vec![0, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0, 0, 0, 0]; let prot_key = mk_retr(&secret); let exp = RetrievedSecret::Plaintext(secret[2..12].to_vec().into()); assert_eq!(prot_key, exp); } #[test] fn from_retr_inv_size() { let secret = vec![0x20; 32]; let prot_key = mk_retr(&secret); let exp = RetrievedSecret::Plaintext(secret.into()); assert_eq!(prot_key, exp); } #[test] fn from_retr_inv_no_zero_after_end() { let secret = vec![0, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 1, 0, 0, 0]; let prot_key = mk_retr(&secret); let exp = RetrievedSecret::Plaintext(secret.into()); assert_eq!(prot_key, exp); } #[test] fn from_retr_inv_to_much_padding() { let secret = vec![ 0, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]; let prot_key = mk_retr(&secret); let exp = RetrievedSecret::Plaintext(secret.into()); assert_eq!(prot_key, exp); } #[test] fn from_retr_0_size() { let secret = vec![0x00; 32]; let prot_key = mk_retr(&secret); let exp = RetrievedSecret::Plaintext(secret.into()); assert_eq!(prot_key, exp); } #[test] fn plain_text_pem() { let exp = "\ -----BEGIN PLAINTEXT SECRET-----\n\ ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER\n\ -----END PLAINTEXT SECRET-----\n"; let prot = RetrievedSecret::Plaintext(vec![17; 48].into()); let pem = prot.to_pem().unwrap(); let pem_str = pem.to_string(); assert_eq!(pem_str, exp); } #[test] fn prot_key_pem() { let exp = "\ -----BEGIN IBM PROTECTED KEY-----\n\ kind: AES-128-KEY\n\n\ ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER\n\ -----END IBM PROTECTED KEY-----\n"; let prot = IbmProtectedKey::new( ListableSecretType::Retrievable(RetrievableSecret::Aes(AesSizes::Bits128)), vec![17; 48], ); let pem = prot.to_pem().unwrap(); let pem_str = pem.to_string(); assert_eq!(pem_str, exp); } } s390-tools-2.38.0/rust/pv/src/uvsecret/user_data.rs000066400000000000000000000502001502674226300220320ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use crate::assert_size; use crate::{ crypto::{sign_msg, verify_signature}, req::BinReqValues, request::{ openssl::pkey::{HasParams, HasPublic, Id, PKey, PKeyRef, Private, Public}, RequestMagic, }, secret::{AddSecretMagic, AddSecretRequest, AddSecretVersion, UserDataType}, Error, Result, }; use openssl::hash::MessageDigest; use openssl::nid::Nid; use zerocopy::{BigEndian, FromBytes, IntoBytes, KnownLayout, U16}; /// User data. /// /// User defined data can be: /// - 512 bytes arbitrary data /// - 256 bytes arbitrary data + EC(secp521r1) signature /// ```none /// LAYOUT /// |------------------------| /// | user-data (256) | /// | ec signature (139) | /// | reserved (5) | /// | signature size (2) (BE)| /// | reserved (110) | /// |------------------------| /// ``` /// - 256 bytes arbitrary data + RSA2048 signature /// ```none /// LAYOUT /// |---------------------| /// | user-data (256) | /// | rsa signature (256) | /// |---------------------| /// ``` /// - 128 bytes arbitrary data + RSA3072 signature /// ```none /// LAYOUT /// |---------------------| /// | user-data (128) | /// | rsa signature (384) | /// |---------------------| /// ``` /// /// Ensures that the data+signature fits into 512 bytes /// must be created via functions! #[derive(Debug, Clone)] pub(super) enum UserData { Null, Unsigned(Vec), Signed(SignedUserData), } #[repr(C)] #[derive(Debug, IntoBytes, FromBytes, KnownLayout)] struct EcUserData { data: [u8; 256], signature: [u8; EC_SIGN_MAX_SIZE], res_18b: [u8; 5], sgn_size: U16, res_192: [u8; 110], } assert_size!(EcUserData, USER_DATA_SIZE); const USER_DATA_SIZE: usize = 0x200; const EC_SIGN_MAX_SIZE: usize = 139; impl EcUserData { // Sets the signature to this data. // //# Panic // Panics if `sgn` is longer than 139 bytes fn set_signature(&mut self, sgn: &[u8]) { debug_assert!(sgn.len() <= EC_SIGN_MAX_SIZE); self.signature.fill(0); self.signature[..sgn.len()].copy_from_slice(sgn); self.res_18b.fill(0); self.sgn_size = (sgn.len() as u16).into(); self.res_192.fill(0); } } #[derive(Debug, Clone)] pub(super) struct SignedUserData { sign_key: PKey, data: Vec, } impl UserData { const USER_DATA_SIZE: usize = 0x200; fn user_data_type(sign_key: &PKeyRef

) -> Result { fn check_curve(pkey: &PKeyRef

) -> Result { let nid = pkey.ec_key()?.group().curve_name(); match nid { Some(nid) => Ok(nid == Nid::SECP521R1), None => Ok(false), } } match sign_key.id() { Id::EC if check_curve(sign_key)? => Ok(UserDataType::SgnEcSECP521R1), Id::RSA if sign_key.rsa()?.size() == 2048 / 8 => Ok(UserDataType::SgnRsa2048), Id::RSA if sign_key.rsa()?.size() == 3072 / 8 => Ok(UserDataType::SgnRsa3072), _ => Err(Error::BinAsrcbUnsupportedUserDataSgnKey), } } pub(super) fn magic(&self) -> RequestMagic { let magic: AddSecretMagic = self.data_type().into(); magic.get() } /// Creates new user data /// /// Verifies that the provided data + signature fits into 512 bytes /// /// # Error /// An error is reported if the provided data and the signature would not fit into 512 bytes /// An error is reported if the key is not of type RSA (2048|3072) or EC(specp521r1) pub(super) fn new(sign_key: Option>, data: Vec) -> Result { let sign_key = match sign_key { None => { return match data.len() > UserDataType::Unsigned.max() { true => Err(Error::AsrcbInvSgnUserData(UserDataType::Unsigned)), false => Ok(Self::Unsigned(data)), }; } Some(skey) => skey, }; let kind = Self::user_data_type(&sign_key)?; // does the data fit into the arbitrary buffer? if data.len() > kind.max() { return Err(Error::AsrcbInvSgnUserData(kind)); } Ok(Self::Signed(SignedUserData { sign_key, data })) } /// Signs data in buf, writes signature to `buf+user_data_offset+sign_offset` if applicable. /// /// Uses [`MessageDigest::sha512`] as digest. Does not modify the abritary user data buffer. /// /// * buf: user data buffer, must be at least 512 bytes long /// /// # Panic /// panics if `buf` is smaller than 512 bytes /// /// # Errors /// Returns an error if signature could not be calculated. /// It is considered no error if no signature is required by user data type pub(super) fn sign(&self, buf: &mut [u8], user_data_offset: usize) -> Result<()> { // get signing info or return if no signature is required let signed_data = match self { Self::Null | Self::Unsigned(_) => return Ok(()), Self::Signed(s) => s, }; debug_assert!(buf.len() >= USER_DATA_SIZE); // clear the signature area let sgn_offset = user_data_offset + self.data_type().max(); buf[sgn_offset..user_data_offset + USER_DATA_SIZE].fill(0); // calculate signature let sgn = sign_msg(&signed_data.sign_key, MessageDigest::sha512(), buf)?; // insert signature if let UserDataType::SgnEcSECP521R1 = self.data_type() { // Panic: will not panic buffer is 512+ bytes long let (buf_ec, _) = EcUserData::mut_from_prefix(&mut buf[user_data_offset..]).unwrap(); buf_ec.set_signature(&sgn); } else { // Panic: will not panic buffer is 512+ bytes long buf[sgn_offset..sgn_offset + sgn.len()].copy_from_slice(&sgn); } Ok(()) } fn data_type(&self) -> UserDataType { match self { Self::Null => UserDataType::Null, Self::Unsigned(_) => UserDataType::Unsigned, Self::Signed(data) => Self::user_data_type(&data.sign_key).unwrap(), } } /// returns a slice for the abitraty user data as first tuple part if User data is available /// the second part contains a vector, created on the fly, which contains enough zeros to fill /// the missing bytes to fill 512 bytes of space or None if the first slice already contains /// 512 bytes pub(super) fn data(&self) -> (Option<&[u8]>, Option>) { let buf = match self { Self::Null => None, Self::Unsigned(d) => Some(d), Self::Signed(SignedUserData { data, .. }) => Some(data), }; let remaining_size = Self::USER_DATA_SIZE - buf.map(|b| b.len()).unwrap_or(0); let remaining = match remaining_size > 0 { true => Some(vec![0; remaining_size]), false => None, }; (buf.map(|b| b.as_ref()), remaining) } } fn format_vrfy_key(key: &PKeyRef) -> String { let id = key.id(); match key.rsa() { Ok(key) => format!("RSA {}", key.size() * 8), Err(_) if id == Id::EC => "EC".to_string(), Err(_) => "Unknown".to_string(), } } fn check_key_format(kind: UserDataType, key: &PKeyRef) -> Result<()> { let other_kind = UserData::user_data_type(key).map_err(|_| Error::AsrcbUserDataKeyMismatch { key: format_vrfy_key(key), kind, })?; if other_kind == kind { Ok(()) } else { Err(Error::AsrcbUserDataKeyMismatch { key: format_vrfy_key(key), kind, }) } } /// Verify the user data contained in the add-secret request. /// /// First checks that the provided data contains a sound add-secret request. /// Then performs the inverse action that happened during the add-secret generation with user-data /// signature: /// - extract and replace the signature with zeros /// - verify the signature of the request until, but not including the request tag /// /// # Returns /// /// Extracrted user-data if available /// /// # Errors /// /// returns an error if /// - No sound add-secret request presented /// - Sinned user-data indicated, but no key provided /// - Another keytype provided than indicated in the request /// - Signature could not be verified by the provided key /// - any OpenSSL error that might happen during the verification process pub fn verify_asrcb_and_get_user_data( mut asrcb: Vec, key: Option>, ) -> Result>> { // check that the provided buffer contains an Add Secret request let magic = AddSecretMagic::try_from_bytes(&asrcb)?; let req = BinReqValues::get(&asrcb)?; if req.version() != AddSecretVersion::One as u32 { return Err(Error::BinAsrcbInvVersion); } // preventing the two lines after the truncate from panicking let req_len = req.len(); if asrcb.len() < req_len || req_len < AddSecretRequest::V1_USER_DATA_OFFS + UserData::USER_DATA_SIZE { return Err(pv_core::Error::NoAsrcb.into()); } // forget the tag (and all additional data that might be behind the tag) asrcb.truncate(req_len - BinReqValues::TAG_LEN); // get a mutable refrenence on the 512 bytes of user data let (_, user_data) = asrcb.split_at_mut(AddSecretRequest::V1_USER_DATA_OFFS); let user_data = &mut user_data[..UserData::USER_DATA_SIZE]; // depending on the user_data_type do: // Null -> exit w/o user data // Unsigned -> exit return all user data // Signed -> // - check that provided key matches user data keytype // - extract user data& signature let (key, user_data) = match (key, magic.kind()) { (_, UserDataType::Null) => return Ok(None), (None, UserDataType::Unsigned) => return Ok(Some(user_data.to_vec())), (Some(key), UserDataType::Unsigned) => { return Err(Error::AsrcbUserDataKeyMismatch { key: format_vrfy_key(&key), kind: UserDataType::Unsigned, }) } (Some(key), _) => { check_key_format(magic.kind(), &key)?; (key, VerifiedUserData::new(user_data, magic.kind())) } (None, _) => return Err(Error::BinAsrcbNoUserDataSgnKey), }; match verify_signature(&key, MessageDigest::sha512(), &asrcb, user_data.signature())? { false => Err(Error::AsrcbUserDataSgnFail), true => Ok(Some(user_data.into())), } } // Internal representation of the 512 bytes of user-data, signing-algorithm agnostic struct VerifiedUserData { data: Vec, signature: Vec, } impl VerifiedUserData { /// Reads user-data from buf depending on the indicated user data type. /// Overwrites the signature in the buf with zeros. /// /// #Panics /// /// Panics it provided buffer is smaller that 512 bytes or kind is Null or Unsigned fn new(buf: &mut [u8], kind: UserDataType) -> Self { assert!(buf.len() >= 0x200); let (ret, sgn) = match kind { UserDataType::SgnEcSECP521R1 => { let ( EcUserData { data, signature, sgn_size, .. }, _, ) = EcUserData::mut_from_prefix(buf).unwrap(); let data_len: usize = data.len(); let data = data.to_vec(); let mut signature = signature.to_vec(); signature.truncate(sgn_size.get() as usize); (Self { data, signature }, &mut buf[data_len..]) } UserDataType::SgnRsa2048 => ( Self { data: buf[..0x100].to_vec(), signature: buf[0x100..].to_vec(), }, &mut buf[0x100..], ), UserDataType::SgnRsa3072 => ( Self { data: buf[..0x80].to_vec(), signature: buf[0x80..].to_vec(), }, &mut buf[0x80..], ), UserDataType::Null => unreachable!(), UserDataType::Unsigned => unreachable!(), }; // overwrite signature field with zeros sgn.fill(0); ret } fn signature(&self) -> &[u8] { self.signature.as_ref() } } impl From for Vec { fn from(value: VerifiedUserData) -> Self { value.data } } #[cfg(test)] mod test { use super::*; use crate::{get_test_asset, test_utils::get_test_keys}; #[test] fn sign_null() { let mut buf = vec![17; 0x200]; let user_data = UserData::Null; let (data, _) = user_data.data(); assert!(data.is_none()); user_data.sign(&mut buf, 0).unwrap(); // sign should not touch the buffer assert_eq!(buf, vec![17; 0x200]); } #[test] fn sign_unsigned() { let user_data = UserData::Unsigned(vec![0x11; 0x200]); let (data, _) = user_data.data(); assert_eq!(data.unwrap(), &[0x11; 0x200]); let mut buf = vec![17; 0x200]; user_data.sign(&mut buf, 0).unwrap(); // sign should not touch the buffer assert_eq!(buf, vec![17; 0x200]); } #[test] fn sign_rsa2048() { let rsa = get_test_asset!("keys/rsa2048key.pem"); let rsa = PKey::private_key_from_pem(rsa).unwrap(); let mut buf = vec![0x17; 0x200]; let user_data = UserData::new(Some(rsa.clone()), vec![0x11; 0x100]).unwrap(); let (data, _) = user_data.data(); let data = data.unwrap(); buf[..0x100].copy_from_slice(data); user_data.sign(&mut buf, 0).unwrap(); let vrf_user_data = VerifiedUserData::new(&mut buf, UserDataType::SgnRsa2048); let res = verify_signature( &rsa, MessageDigest::sha512(), &buf, vrf_user_data.signature(), ) .unwrap(); assert!(res); } #[test] fn sign_rsa3072() { let rsa = get_test_asset!("keys/rsa3072key.pem"); let rsa = PKey::private_key_from_pem(rsa).unwrap(); let mut buf = vec![0x17; 0x200]; let user_data = UserData::new(Some(rsa.clone()), vec![0x11; 0x80]).unwrap(); let (data, _) = user_data.data(); let data = data.unwrap(); buf[..0x80].copy_from_slice(data); user_data.sign(&mut buf, 0).unwrap(); let vrf_user_data = VerifiedUserData::new(&mut buf, UserDataType::SgnRsa3072); let res = verify_signature( &rsa, MessageDigest::sha512(), &buf, vrf_user_data.signature(), ) .unwrap(); assert!(res); } #[test] fn sign_rsa4096_fail() { let rsa = get_test_asset!("keys/rsa4096key.pem"); let rsa = PKey::private_key_from_pem(rsa).unwrap(); let user_data = UserData::new(Some(rsa.clone()), vec![]); assert!(matches!( user_data.unwrap_err(), Error::BinAsrcbUnsupportedUserDataSgnKey )); } #[test] fn sign_ec() { let (ec, _) = get_test_keys(); let mut buf = vec![0x11; 0x200]; let user_data = UserData::new(Some(ec.clone()), vec![0x11; 0x80]).unwrap(); let (data, _) = user_data.data(); let data = data.unwrap(); buf[..0x80].copy_from_slice(data); user_data.sign(&mut buf, 0).unwrap(); let buf_ec = EcUserData::mut_from_bytes(&mut buf).unwrap(); let EcUserData { data, signature, res_18b, sgn_size, res_192, } = buf_ec; assert_eq!(data, &[0x11u8; 256]); assert_ne!(signature, &[0x11u8; 139]); assert_eq!(res_18b, &[0u8; 5]); assert!(sgn_size.get() <= 139); assert_eq!(res_192, &[0u8; 110]); let vrf_user_data = VerifiedUserData::new(&mut buf, UserDataType::SgnEcSECP521R1); let res = verify_signature( &ec, MessageDigest::sha512(), &buf, vrf_user_data.signature(), ) .unwrap(); assert!(res); } #[test] fn sign_ec_fail() { let ec = get_test_asset!("keys/ecsecp256k1.pem"); let ec = PKey::private_key_from_pem(ec).unwrap(); let user_data = UserData::new(Some(ec.clone()), vec![]); assert!(matches!( user_data.unwrap_err(), Error::BinAsrcbUnsupportedUserDataSgnKey )); } #[test] fn check_format() { let (_, ec) = get_test_keys(); check_key_format(UserDataType::SgnEcSECP521R1, &ec).unwrap(); let res = check_key_format(UserDataType::SgnRsa2048, &ec); assert!(matches!(res, Err(Error::AsrcbUserDataKeyMismatch { .. }))); let rsa = get_test_asset!("keys/rsa2048key.pub.pem"); let rsa = PKey::public_key_from_pem(rsa).unwrap(); check_key_format(UserDataType::SgnRsa2048, &rsa).unwrap(); let rsa = get_test_asset!("keys/rsa3072key.pub.pem"); let rsa = PKey::public_key_from_pem(rsa).unwrap(); check_key_format(UserDataType::SgnRsa3072, &rsa).unwrap(); let res = check_key_format(UserDataType::SgnRsa2048, &rsa); assert!(matches!(res, Err(Error::AsrcbUserDataKeyMismatch { .. }))); let rsa = get_test_asset!("keys/rsa4096key.pem"); let rsa = PKey::private_key_from_pem(rsa).unwrap(); let rsa = PKey::public_key_from_pem(&rsa.public_key_to_pem().unwrap()).unwrap(); let res = check_key_format(UserDataType::SgnRsa2048, &rsa); assert!(matches!(res, Err(Error::AsrcbUserDataKeyMismatch { .. }))); } #[test] fn kind() { let (ec, _) = get_test_keys(); let kind = UserData::user_data_type(&ec).unwrap(); assert_eq!(kind, UserDataType::SgnEcSECP521R1); let rsa = get_test_asset!("keys/rsa2048key.pem"); let rsa = PKey::private_key_from_pem(rsa).unwrap(); let kind = UserData::user_data_type(&rsa).unwrap(); assert_eq!(kind, UserDataType::SgnRsa2048); let rsa = get_test_asset!("keys/rsa3072key.pem"); let rsa = PKey::private_key_from_pem(rsa).unwrap(); let kind = UserData::user_data_type(&rsa).unwrap(); assert_eq!(kind, UserDataType::SgnRsa3072); let rsa = get_test_asset!("keys/rsa4096key.pem"); let rsa = PKey::private_key_from_pem(rsa).unwrap(); let kind = UserData::user_data_type(&rsa).unwrap_err(); assert!(matches!(kind, Error::BinAsrcbUnsupportedUserDataSgnKey)); } #[test] fn new() { let (ec, _) = get_test_keys(); let user_data = UserData::new( Some(ec.clone()), vec![1; UserDataType::SgnEcSECP521R1.max()], ) .unwrap(); assert!(matches!(user_data, UserData::Signed(_))); let user_data = UserData::new(Some(ec), vec![1; UserDataType::SgnEcSECP521R1.max() + 1]); assert!(matches!( user_data, Err(Error::AsrcbInvSgnUserData(UserDataType::SgnEcSECP521R1)) )); let user_data = UserData::new(None, vec![1; UserDataType::Unsigned.max()]).unwrap(); assert!(matches!(user_data, UserData::Unsigned(_))); let user_data = UserData::new(None, vec![1; UserDataType::Unsigned.max() + 1]); assert!(matches!( user_data, Err(Error::AsrcbInvSgnUserData(UserDataType::Unsigned)) )); } #[test] fn data() { let (ec, _) = get_test_keys(); let data_in = vec![1; UserDataType::SgnEcSECP521R1.max()]; let user_data = UserData::new(Some(ec.clone()), data_in.clone()).unwrap(); let exp_pad = Some(vec![0; UserData::USER_DATA_SIZE - data_in.len()]); let (data_out, pad) = user_data.data(); assert_eq!(data_out, Some(data_in.as_ref())); assert_eq!(pad, exp_pad); let data_in = vec![1; UserDataType::SgnEcSECP521R1.max() - 1]; let user_data = UserData::new(Some(ec.clone()), data_in.clone()).unwrap(); let exp_pad = Some(vec![0; UserData::USER_DATA_SIZE - data_in.len()]); let (data_out, pad) = user_data.data(); assert_eq!(data_out, Some(data_in.as_ref())); assert_eq!(pad, exp_pad); } } s390-tools-2.38.0/rust/pv/src/verify.rs000066400000000000000000000156771502674226300175520ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 use crate::openssl_extensions::{StackableX509Crl, X509StoreContextExtension, X509StoreExtension}; use core::slice; use log::{debug, trace}; use openssl::error::ErrorStack; use openssl::stack::Stack; use openssl::x509::store::X509Store; use openssl::x509::{CrlStatus, X509NameRef, X509Ref, X509StoreContext, X509StoreContextRef, X509}; use std::path::Path; #[cfg(not(test))] use helper::download_first_crl_from_x509; #[cfg(test)] use test::download_first_crl_from_x509; use crate::error::bail_hkd_verify; use crate::misc::{read_certs, read_file}; use crate::Result; mod helper; mod test; /// A `HkdVerifier` verifies that a host-key document(HKD) can be trusted. /// /// If the verification fails the HKD should not be used to create requests. pub trait HkdVerifier { /// Checks if the given host-key document can be trusted. /// /// # Errors /// /// This function will return an error if the host-key document cannot be /// trusted. Refer to the concrete Error type for the specific reason. fn verify(&self, hkd: &X509Ref) -> Result<()>; } /// A verifier that does not verify and accepts all given host-keys as valid. #[derive(Debug)] pub struct NoVerifyHkd; impl HkdVerifier for NoVerifyHkd { fn verify(&self, _hkd: &X509Ref) -> Result<()> { Ok(()) } } /// A verifier that checks the host-key document against a chain of trust. pub struct CertVerifier { store: X509Store, ibm_z_sign_key: X509, offline: bool, } impl std::fmt::Debug for CertVerifier { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str("CertVerifier") } } impl HkdVerifier for CertVerifier { /// This function verifies a host-key /// document. To do so multiple steps are required: /// /// 1. `issuer(host_key`) == `subject(ibm_z_sign_key`) /// 2. Signature verification /// 3. @hkd must not be expired /// 4. @hkd must not be revoked fn verify(&self, hkd: &X509Ref) -> Result<()> { helper::verify_hkd_options(hkd, &self.ibm_z_sign_key)?; // verify that the HKD was signed with the key of the IBM signing key if !hkd.verify(self.ibm_z_sign_key.public_key()?.as_ref())? { bail_hkd_verify!(Signature); } // Find matching CRL for sign key in the store or download them let crls = self.hkd_crls(hkd)?; // Verify that the CRLs are still valid let mut verified_crls = Vec::with_capacity(crls.len()); for crl in &crls { if helper::verify_crl(crl, &self.ibm_z_sign_key).is_some() { verified_crls.push(crl.to_owned()); } } // Test if HKD was revoked (min1 required) if verified_crls.is_empty() { bail_hkd_verify!(NoCrl); } for crl in verified_crls { match crl.get_by_serial(hkd.serial_number()) { CrlStatus::NotRevoked => (), _ => bail_hkd_verify!(HkdRevoked), } } debug!("HKD: verified"); Ok(()) } } impl CertVerifier { fn quirk_crls( ctx: &mut X509StoreContextRef, subject: &X509NameRef, ) -> Result, ErrorStack> { match ctx.crls(subject) { Ok(ret) if !ret.is_empty() => return Ok(ret), _ => (), } // Armonk/Poughkeepsie fixup trace!("quirk_crls: Try Locality"); if let Some(locality_subject) = helper::armonk_locality_fixup(subject) { match ctx.crls(&locality_subject) { Ok(ret) if !ret.is_empty() => return Ok(ret), _ => (), } // reorder trace!("quirk_crls: Try Locality+Reorder"); if let Ok(locality_ordered_subject) = helper::reorder_x509_names(&locality_subject) { match ctx.crls(&locality_ordered_subject) { Ok(ret) if !ret.is_empty() => return Ok(ret), _ => (), } } } // reorder unchanged locality subject trace!("quirk_crls: Try Reorder"); if let Ok(ordered_subject) = helper::reorder_x509_names(subject) { match ctx.crls(&ordered_subject) { Ok(ret) if !ret.is_empty() => return Ok(ret), _ => (), } } // nothing found, return empty stack Stack::new() } /// Download the CRLs that a HKD refers to. pub fn hkd_crls(&self, hkd: &X509Ref) -> Result> { let mut ctx = X509StoreContext::new()?; // Unfortunately we cannot use a dedicated function here and have to use a closure (E0434) // Otherwise, we cannot refer to self // Search for local CRLs let mut crls = ctx.init_opt(&self.store, None, None, |ctx| { let subject = self.ibm_z_sign_key.subject_name(); Self::quirk_crls(ctx, subject) })?; if !self.offline { // Try to download a CRL if defined in the HKD if let Some(crl) = download_first_crl_from_x509(hkd)? { crl.into_iter().try_for_each(|c| crls.push(c.into()))?; } } Ok(crls) } } impl CertVerifier { /// Create a `CertVerifier`. /// /// * `cert_paths` - Paths to certificates for the chain of trust /// * `crl_paths` - Paths to certificate revocation lists for the chain of trust /// * `root_ca_path` - Path to the root of trust /// * `offline` - if set to true the verification process will not try to download CRLs from the /// internet. /// /// # Errors /// /// This function will return an error if the chain of trust could not be established. pub fn new( cert_paths: &[P], crl_paths: &[Q], root_ca_path: Option, offline: bool, ) -> Result where P: AsRef, Q: AsRef, R: AsRef, { let mut store = helper::store_setup(root_ca_path, crl_paths, cert_paths)?; let mut untr_certs = Vec::with_capacity(cert_paths.len()); for path in cert_paths { let mut crt = read_certs(&read_file(path, "certificate")?)?; if !offline { for c in &crt { if let Some(crl) = download_first_crl_from_x509(c)? { crl.iter().try_for_each(|c| store.add_crl(c))?; } } } untr_certs.append(&mut crt); } // remove the IBM signing certificate from chain. // We have to verify them separately as they are not marked as intermediate certs let (ibm_z_sign_key, chain) = helper::extract_ibm_sign_key(untr_certs)?; let store = store.build(); helper::verify_chain(&store, &chain, slice::from_ref(&ibm_z_sign_key))?; Ok(Self { store, ibm_z_sign_key, offline, }) } } s390-tools-2.38.0/rust/pv/src/verify/000077500000000000000000000000001502674226300171645ustar00rootroot00000000000000s390-tools-2.38.0/rust/pv/src/verify/helper.rs000066400000000000000000000365501502674226300210220ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 use crate::error::bail_hkd_verify; use crate::openssl_extensions::{AkidCheckResult, AkidExtension}; use crate::HkdVerifyErrorType::*; use crate::{Error, Result}; use log::debug; use openssl::{ asn1::{Asn1Time, Asn1TimeRef}, error::ErrorStack, nid::Nid, ssl::SslFiletype, stack::Stack, x509::{ store::{File, X509Lookup, X509StoreBuilder, X509StoreRef}, verify::{X509VerifyFlags, X509VerifyParam}, X509CrlRef, X509Name, X509NameRef, X509PurposeId, X509Ref, X509StoreContext, X509StoreContextRef, X509VerifyResult, X509, }, }; use std::path::Path; use std::str::from_utf8; use std::{cmp::Ordering, ffi::c_int}; /// Minimum security level for the keys/certificates used to establish a chain of /// trust (see /// for details). const SECURITY_LEVEL: usize = 2; const SECURITY_BITS_ARRAY: [u32; 6] = [0, 80, 112, 128, 192, 256]; const SECURITY_BITS: u32 = SECURITY_BITS_ARRAY[SECURITY_LEVEL]; const SECURITY_CHAIN_MAX_LEN: c_int = 2; /// Verifies that the HKD /// * has enough security bits /// * is inside its validity period /// * the Authority Key ID matches the Signing Key ID of the [`sign_key`] pub fn verify_hkd_options(hkd: &X509Ref, sign_key: &X509Ref) -> Result<()> { let hk_pkey = hkd.public_key()?; let security_bits = hk_pkey.security_bits(); if SECURITY_BITS > 0 && SECURITY_BITS > security_bits { return Err(Error::HkdVerify(SecurityBits(security_bits, SECURITY_BITS))); } // TODO rust-openssl fix X509::not.after/before() impl to return Option& not panic on nullptr // from C? try_... rust-openssl // verify that the HKD is still valid check_validity_period(hkd.not_before(), hkd.not_after())?; // verify that the AKID of the hkd matches the SKID of the issuer if let Some(akid) = hkd.akid() { if akid.check(sign_key) != AkidCheckResult::OK { bail_hkd_verify!(Akid); } } Ok(()) } pub fn verify_crl(crl: &X509CrlRef, issuer: &X509Ref) -> Option<()> { let last = crl.last_update(); let next = crl.next_update()?; check_validity_period(last, next).ok()?; if let Some(akid) = crl.akid() { if akid.check(issuer) != AkidCheckResult::OK { return None; } } match crl.verify(issuer.public_key().ok()?.as_ref()).ok()? { true => Some(()), false => None, } } /// Setup the x509Store such that it can be used it for verifying certificates pub fn store_setup, Q: AsRef, R: AsRef>( root_ca_path: Option

, crl_paths: &[Q], cert_w_crl_paths: &[R], ) -> Result { let mut x509store = X509StoreBuilder::new()?; match root_ca_path { None => x509store.set_default_paths()?, Some(p) => load_root_ca(p, &mut x509store)?, } for crl in crl_paths { load_crl_to_store(&mut x509store, crl, true).map_err(|source| Error::X509Load { path: crl.as_ref().into(), ty: Error::CRL, source, })?; } for crl in cert_w_crl_paths { load_crl_to_store(&mut x509store, crl, false).map_err(|source| Error::X509Load { path: crl.as_ref().into(), ty: Error::CRL, source, })?; } let mut param = X509VerifyParam::new()?; let flags = X509VerifyFlags::X509_STRICT | X509VerifyFlags::CRL_CHECK | X509VerifyFlags::CRL_CHECK_ALL | X509VerifyFlags::TRUSTED_FIRST | X509VerifyFlags::CHECK_SS_SIGNATURE | X509VerifyFlags::POLICY_CHECK; param.set_depth(SECURITY_CHAIN_MAX_LEN); param.set_auth_level(SECURITY_LEVEL as i32); param.set_purpose(X509PurposeId::ANY)?; param.set_flags(flags)?; x509store.set_param(¶m)?; Ok(x509store) } /// Verify that the given IBM signing keys can be trusted /// -> check the chain: `IBMsignKey`<-InterCA(s)<-`RootCA` pub fn verify_chain( store: &X509StoreRef, untrusted_certs: &Stack, sign_keys: &[X509], ) -> Result<()> { fn verify_fun(ctx: &mut X509StoreContextRef) -> std::result::Result { // verify certificate let res = ctx.verify_cert()?; if !res { debug!("Failed to verify the singing key with the chain of trust"); return Ok(res); } // verify that the chain is as expected let chain = match ctx.chain() { Some(c) => c, None => { debug!("No verification chain in verify-context. (openssl BUG)"); ctx.set_error(X509VerifyResult::APPLICATION_VERIFICATION); return Ok(false); } }; if chain.len() < SECURITY_CHAIN_MAX_LEN as usize { debug!("Verification expects one root and at least one intermediate certificate",); ctx.set_error(X509VerifyResult::APPLICATION_VERIFICATION); Ok(false) } else { Ok(true) } } let mut store_ctx = X509StoreContext::new()?; for sign_key in sign_keys { // (rust)OpenSSL should not error out on `X509_verify_cert`\ // (Internal (probably unrecoverable) error like OOM) if !store_ctx .init(store, sign_key, untrusted_certs, verify_fun) .map_err(|e| Error::InternalSsl("The IBM Z signing key could not be verified.", e))? { return Err(Error::HkdVerify(IbmSignInvalid( store_ctx.error(), store_ctx.error_depth(), ))); } } Ok(()) } /// Consumes and splits the given vector into a single IBM Z signing key and other certificates /// /// Error if not exactly one IBM Z signing key available pub fn extract_ibm_sign_key(certs: Vec) -> Result<(X509, Stack)> { let ibm_z_sign_key = get_ibm_z_sign_key(&certs)?; let mut chain = Stack::::new()?; for x in certs.into_iter().filter(|x| !is_ibm_signing_cert(x)) { chain.push(x)?; } Ok((ibm_z_sign_key, chain)) } // Name Entry values of an IBM Z key signing cert // Asn1StringRef::as_slice aka ASN1_STRING_get0_data gives a string without \0 delimiter const IBM_Z_COMMON_NAME: &[u8; 43usize] = b"International Business Machines Corporation"; const IBM_Z_COUNTRY_NAME: &[u8; 2usize] = b"US"; const IBM_Z_LOCALITY_NAME_POUGHKEEPSIE: &[u8; 12usize] = b"Poughkeepsie"; const IBM_Z_LOCALITY_NAME_ARMONK: &[u8; 6usize] = b"Armonk"; const IBM_Z_ORGANIZATIONAL_UNIT_NAME_SUFFIX: &str = "Key Signing Service"; const IBM_Z_ORGANIZATION_NAME: &[u8; 43usize] = b"International Business Machines Corporation"; const IBM_Z_STATE: &[u8; 8usize] = b"New York"; const IMB_Z_ENTRY_COUNT: usize = 6; fn name_data_eq(entries: &X509NameRef, nid: Nid, rhs: &[u8]) -> bool { let mut it = entries.entries_by_nid(nid); match it.next() { None => false, Some(entry) => entry.data().as_slice() == rhs, } } fn is_ibm_signing_cert(cert: &X509) -> bool { let subj = cert.subject_name(); if subj.entries().count() != IMB_Z_ENTRY_COUNT || !name_data_eq(subj, Nid::COUNTRYNAME, IBM_Z_COUNTRY_NAME) || !name_data_eq(subj, Nid::STATEORPROVINCENAME, IBM_Z_STATE) || !(name_data_eq(subj, Nid::LOCALITYNAME, IBM_Z_LOCALITY_NAME_POUGHKEEPSIE) || name_data_eq(subj, Nid::LOCALITYNAME, IBM_Z_LOCALITY_NAME_ARMONK)) || !name_data_eq(subj, Nid::ORGANIZATIONNAME, IBM_Z_ORGANIZATION_NAME) || !name_data_eq(subj, Nid::COMMONNAME, IBM_Z_COMMON_NAME) { return false; } match subj.entries_by_nid(Nid::ORGANIZATIONALUNITNAME).next() { None => false, Some(entry) => match entry.data().as_utf8() { Err(_) => false, Ok(s) => s .as_bytes() .ends_with(IBM_Z_ORGANIZATIONAL_UNIT_NAME_SUFFIX.as_bytes()), }, } } fn get_ibm_z_sign_key(certs: &[X509]) -> Result { let mut ibm_sign_keys = certs.iter().filter(|x| is_ibm_signing_cert(x)).cloned(); match ibm_sign_keys.next() { None => bail_hkd_verify!(NoIbmSignKey), Some(k) => match ibm_sign_keys.next() { None => Ok(k), Some(_) => bail_hkd_verify!(ManyIbmSignKeys), }, } } fn load_root_ca>(path: P, x509_store: &mut X509StoreBuilder) -> Result<()> { let lu = x509_store.add_lookup(X509Lookup::::file())?; // Try to load cert as PEM file match lu.load_cert_file(&path, SslFiletype::PEM) { Ok(_) => lu .load_crl_file(&path, SslFiletype::PEM) .map(|_| ()) .or(Ok(())), // Not a PEM file? try ASN1 Err(_) => lu .load_cert_file(&path, SslFiletype::ASN1) .map(|_| ()) .map_err(|source| Error::X509Load { path: path.as_ref().into(), ty: Error::CERT, source, }), } } fn load_crl_to_store>( x509_store: &mut X509StoreBuilder, path: P, err_out_empty_crl: bool, ) -> std::result::Result<(), ErrorStack> { let lu = x509_store.add_lookup(X509Lookup::::file())?; // Try to load cert as PEM file if lu.load_crl_file(&path, SslFiletype::PEM).is_err() { // Not a PEM file? try read as ASN1 let res = lu.load_crl_file(path, SslFiletype::ASN1); if err_out_empty_crl { res?; } } Ok(()) } /// Run through the forest of the distribution points and find them pub fn x509_dist_points(cert: &X509Ref) -> Vec { let mut res = Vec::::with_capacity(1); let dps = match cert.crl_distribution_points() { Some(d) => d, None => return res, }; for dp in dps { let dp_nm = match dp.distpoint() { Some(nm) => nm, None => continue, }; let dp_gns = match dp_nm.fullname() { Some(gns) => gns, None => continue, }; for dp_gn in dp_gns { match dp_gn.uri() { Some(uri) => res.push(uri.to_string()), None => continue, }; } } res } /// Searches for CRL Distribution points and downloads the CRL. Stops after the first successful /// download. /// /// Error if something bad(=unexpected) happens /// CRL not available at all URIs and unexpected format at all URIs are mapped to Ok(None) #[cfg(not(test))] pub fn download_first_crl_from_x509(cert: &X509Ref) -> Result>> { use crate::utils::read_crls; use curl::easy::{Easy2, Handler, WriteError}; use std::time::Duration; const CRL_TIMEOUT_MAX: Duration = Duration::from_secs(3); struct Buf(Vec); impl Handler for Buf { fn write(&mut self, data: &[u8]) -> std::result::Result { self.0.extend_from_slice(data); Ok(data.len()) } } for dist_point in x509_dist_points(cert) { // A typical certificate is about 1200 bytes long let mut handle = Easy2::new(Buf(Vec::with_capacity(1500))); handle.url(&dist_point)?; handle.get(true)?; handle.follow_location(true)?; handle.timeout(CRL_TIMEOUT_MAX)?; handle.useragent("s390-tools-pv-crl")?; if handle.perform().is_err() { continue; } match read_crls(&handle.get_ref().0) { Err(_) => continue, Ok(crl) if crl.is_empty() => continue, Ok(crl) => return Ok(Some(crl)), } } Ok(None) } fn check_validity_period(not_before: &Asn1TimeRef, not_after: &Asn1TimeRef) -> Result<()> { let now = Asn1Time::days_from_now(0)?; if let Ordering::Less = now.compare(not_before)? { bail_hkd_verify!(BeforeValidity); } match now.compare(not_after)? { Ordering::Less => Ok(()), _ => bail_hkd_verify!(AfterValidity), } } const NIDS_CORRECT_ORDER: [Nid; 6] = [ Nid::COUNTRYNAME, Nid::ORGANIZATIONNAME, Nid::ORGANIZATIONALUNITNAME, Nid::LOCALITYNAME, Nid::STATEORPROVINCENAME, Nid::COMMONNAME, ]; /// Workaround to fix the mismatch between issuer name of the /// IBM Z signing CRLs and the IBM Z signing key subject name. pub fn reorder_x509_names(subject: &X509NameRef) -> std::result::Result { let mut correct_subj = X509Name::builder()?; for nid in NIDS_CORRECT_ORDER { if let Some(name) = subject.entries_by_nid(nid).next() { correct_subj.append_entry(name)?; } } Ok(correct_subj.build()) } /// Workaround for potential locality mismatches between CRLs and Certs /// # Return /// fixed subject or none if locality was not Armonk or any OpenSSL error pub fn armonk_locality_fixup(subject: &X509NameRef) -> Option { if !name_data_eq(subject, Nid::LOCALITYNAME, IBM_Z_LOCALITY_NAME_ARMONK) { return None; } let mut ret = X509Name::builder().ok()?; for entry in subject.entries() { match entry.object().nid() { nid @ Nid::LOCALITYNAME => ret .append_entry_by_nid(nid, from_utf8(IBM_Z_LOCALITY_NAME_POUGHKEEPSIE).ok()?) .ok()?, _ => { ret.append_entry(entry).ok()?; } } } Some(ret.build()) } #[cfg(test)] /// tests for some private functions mod test { use super::*; use crate::test_utils::*; use std::time::{Duration, SystemTime}; fn sys_to_asn1_time(syst: SystemTime) -> Asn1Time { let secs = syst .duration_since(SystemTime::UNIX_EPOCH) .unwrap() .as_secs(); Asn1Time::from_unix(secs as i64).unwrap() } #[test] fn check_validity_period() { let day = Duration::from_secs(60 * 60 * 24); let yesterday = sys_to_asn1_time(SystemTime::now() - day); let tomorrow = sys_to_asn1_time(SystemTime::now() + day); assert!(super::check_validity_period(&yesterday, &tomorrow).is_ok()); assert!(matches!( super::check_validity_period(&tomorrow, &tomorrow), Err(Error::HkdVerify(BeforeValidity)) )); assert!(matches!( super::check_validity_period(&yesterday, &yesterday), Err(Error::HkdVerify(AfterValidity)) )); } #[test] fn is_ibm_z_sign_key() { let ibm_crt = load_gen_cert("ibm.crt"); let no_ibm_crt = load_gen_cert("inter_ca.crt"); let ibm_wrong_subj = load_gen_cert("ibm_wrong_subject.crt"); assert!(is_ibm_signing_cert(&ibm_crt)); assert!(!is_ibm_signing_cert(&no_ibm_crt)); assert!(!is_ibm_signing_cert(&ibm_wrong_subj)); } #[test] fn get_ibm_z_sign_key() { let ibm_crt = load_gen_cert("ibm.crt"); let ibm_wrong_subj = load_gen_cert("ibm_wrong_subject.crt"); let no_sign_crt = load_gen_cert("inter_ca.crt"); assert!(super::get_ibm_z_sign_key(&[ibm_crt.clone()]).is_ok()); assert!(matches!( super::get_ibm_z_sign_key(&[ibm_crt.clone(), ibm_crt.clone()]), Err(Error::HkdVerify(ManyIbmSignKeys)) )); assert!(matches!( super::get_ibm_z_sign_key(&[ibm_wrong_subj]), Err(Error::HkdVerify(NoIbmSignKey)) )); assert!(matches!( super::get_ibm_z_sign_key(&[no_sign_crt.clone()]), Err(Error::HkdVerify(NoIbmSignKey)) )); assert!(super::get_ibm_z_sign_key(&[ibm_crt, no_sign_crt]).is_ok(),); } } s390-tools-2.38.0/rust/pv/src/verify/test.rs000066400000000000000000000077511502674226300205230ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 #![cfg(test)] use super::{helper, helper::*, *}; use crate::{utils::read_crls, Error, HkdVerifyErrorType::*}; use openssl::{stack::Stack, x509::X509Crl}; use std::path::Path; use crate::test_utils::*; // Mock function pub fn download_first_crl_from_x509(cert: &X509Ref) -> Result>> { fn mock_download>(path: P) -> Result> { read_crls(std::fs::read(path)?) } for dist_point in x509_dist_points(cert) { { let path = get_cert_asset_path(&dist_point); let crls = if let Ok(buf) = mock_download(&path) { buf } else { continue; }; return Ok(Some(crls)); } } Ok(None) } #[test] fn store_setup() { let ibm_path = get_cert_asset_path("ibm.crt"); let inter_path = get_cert_asset_path("inter.crt"); let crls: [String; 0] = []; let store = helper::store_setup(None::, &crls, &[&ibm_path, &inter_path]); assert!(store.is_ok()); } #[test] fn verify_chain_online() { let ibm_crt = get_cert_asset_path("ibm.crt"); let inter_crt = get_cert_asset_path("inter_ca.crt"); let root_crt = get_cert_asset_path("root_ca.chained.crt"); let crls: [String; 0] = []; let ret = CertVerifier::new(&[&ibm_crt, &inter_crt], &crls, Some(&root_crt), false); assert!(ret.is_ok(), "CertVerifier::new failed: {ret:?}"); } #[test] fn verify_chain_offline() { let ibm_crt = load_gen_cert("ibm.crt"); let inter_crl = get_cert_asset_path("inter_ca.crl"); let inter_crt = load_gen_cert("inter_ca.crt"); let root_crt = get_cert_asset_path("root_ca.chained.crt"); let certs: [String; 0] = []; let store = helper::store_setup(Some(&root_crt), &[&inter_crl], &certs) .unwrap() .build(); let mut sk = Stack::::new().unwrap(); sk.push(inter_crt).unwrap(); assert!(verify_chain(&store, &sk, &[ibm_crt]).is_ok()); } #[test] fn dist_points() { let crt = load_gen_cert("ibm.crt"); let res = x509_dist_points(&crt); let exp = vec!["inter_ca.crl"]; assert_eq!(res, exp); } fn verify(offline: bool, ibm_crt: &'static str, ibm_crl: &'static str, hkd: &'static str) { let root_crt = get_cert_asset_path("root_ca.chained.crt"); let inter_crt = get_cert_asset_path("inter_ca.crt"); let inter_crl = get_cert_asset_path("inter_ca.crl"); let ibm_crt = get_cert_asset_path(ibm_crt); let ibm_crl = get_cert_asset_path(ibm_crl); let hkd_revoked = load_gen_cert("host_rev.crt"); let hkd_exp = load_gen_cert("host_crt_expired.crt"); let hkd = load_gen_cert(hkd); let crls = [&ibm_crl, &inter_crl]; let verifier = CertVerifier::new( &[&ibm_crt, &inter_crt], if offline { &crls } else { &[] }, Some(&root_crt), offline, ) .unwrap(); let res = verifier.verify(&hkd); assert!(res.is_ok(), "Verify failed: res: {res:?}"); assert!(matches!( verifier.verify(&hkd_revoked), Err(Error::HkdVerify(HkdRevoked)) )); assert!(matches!( verifier.verify(&hkd_exp), Err(Error::HkdVerify(AfterValidity)) )); } #[test] fn verify_online() { verify(false, "ibm.crt", "ibm.crl", "host.crt") } #[test] fn verify_offline() { verify(true, "ibm.crt", "ibm.crl", "host.crt") } #[test] fn verify_armonk_crt_online() { verify(false, "ibm_armonk.crt", "ibm.crl", "host.crt") } #[test] fn verify_armonk_crt_offline() { verify(true, "ibm_armonk.crt", "ibm.crl", "host.crt") } #[test] fn verify_armonk_crl_online() { verify(false, "ibm_armonk.crt", "ibm_armonk.crl", "host.crt") } #[test] fn verify_armonk_crl_offline() { verify(true, "ibm_armonk.crt", "ibm_armonk.crl", "host.crt") } #[test] fn verify_armonk_hkd_online() { verify(false, "ibm_armonk.crt", "ibm_armonk.crl", "host_armonk.crt") } #[test] fn verify_armonk_hkd_offline() { verify(true, "ibm_armonk.crt", "ibm_armonk.crl", "host_armonk.crt") } s390-tools-2.38.0/rust/pv/tests/000077500000000000000000000000001502674226300162335ustar00rootroot00000000000000s390-tools-2.38.0/rust/pv/tests/add_secret_request.rs000066400000000000000000000170421502674226300224520ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 #![allow(missing_docs)] use openssl::{ ec::{EcGroup, EcKey}, nid::Nid, pkey::Private, }; use s390_pv::{ get_test_asset, request::{ openssl::pkey::{PKey, Public}, BootHdrTags, ReqEncrCtx, Request, SymKey, }, secret::{ verify_asrcb_and_get_user_data, AddSecretFlags, AddSecretRequest, AddSecretVersion, ExtSecret, GuestSecret, }, test_utils::get_test_keys, uv::ConfigUid, Result, }; const TAGS: BootHdrTags = BootHdrTags::new([1; 64], [2; 64], [3; 64], [4; 16]); const CUID: ConfigUid = [0x42u8; 16]; const ASSOC_SECRET: [u8; 32] = [0x11; 32]; const ASSOC_ID: &str = "add_secret_request"; fn create_asrcb( guest_secret: GuestSecret, ext_secret: Option, flags: AddSecretFlags, cuid: Option, hkd: PKey, ctx: &ReqEncrCtx, ) -> Result> { let mut asrcb = AddSecretRequest::new(AddSecretVersion::One, guest_secret, TAGS, flags); if let Some(s) = ext_secret { asrcb.set_ext_secret(s)? }; if let Some(c) = cuid { asrcb.set_cuid(c); }; asrcb.add_hostkey(hkd); asrcb.encrypt(ctx) } fn get_crypto() -> (PKey, ReqEncrCtx) { let (cust_key, host_key) = get_test_keys(); let ctx = ReqEncrCtx::new_aes_256( Some([0x55; 12]), Some(cust_key), Some(SymKey::Aes256([0x17; 32].into())), ) .unwrap(); (host_key, ctx) } fn gen_asrcb( guest_secret: GuestSecret, ext_secret: E, flags: AddSecretFlags, cuid: bool, ) -> Result> where E: Into>, { let (host_key, ctx) = get_crypto(); let cuid = match cuid { true => Some(CUID), false => None, }; create_asrcb(guest_secret, ext_secret.into(), flags, cuid, host_key, &ctx) } fn association() -> GuestSecret { GuestSecret::association(ASSOC_ID, ASSOC_SECRET).unwrap() } fn ext_simple() -> ExtSecret { ExtSecret::Simple([0x17; 32].into()) } fn ext_derived() -> ExtSecret { ExtSecret::Derived([0; 32].into()) } fn no_flag() -> AddSecretFlags { AddSecretFlags::default() } fn create_signed_asrcb(skey: PKey, user_data: Vec) -> Vec { let (host_key, ctx) = get_crypto(); let mut asrcb = AddSecretRequest::new(AddSecretVersion::One, GuestSecret::Null, TAGS, no_flag()); asrcb.add_hostkey(host_key); asrcb.set_user_data(user_data, Some(skey)).unwrap(); asrcb.encrypt(&ctx).unwrap() } #[test] fn null_none_default_ncuid_one_user_unsgn() { let user_data_orig = vec![0x56; 0x183]; let (host_key, ctx) = get_crypto(); let mut asrcb = AddSecretRequest::new(AddSecretVersion::One, GuestSecret::Null, TAGS, no_flag()); asrcb.add_hostkey(host_key); asrcb.set_user_data(user_data_orig.clone(), None).unwrap(); let asrcb = asrcb.encrypt(&ctx).unwrap(); let user_data = verify_asrcb_and_get_user_data(asrcb, None).unwrap(); assert_eq!( user_data_orig.as_slice(), &user_data.as_ref().unwrap()[..user_data_orig.len()] ); } #[test] fn null_none_default_ncuid_one_user_ec() { let (usr_sgn_key, _) = get_test_keys(); let usr_vrfy_key = usr_sgn_key.ec_key().unwrap(); let usr_vrfy_key = usr_vrfy_key.public_key(); let usr_vrfy_key = PKey::from_ec_key( EcKey::from_public_key( &EcGroup::from_curve_name(Nid::SECP521R1).unwrap(), usr_vrfy_key, ) .unwrap(), ) .unwrap(); let user_data_orig = vec![0x56; 0x100]; let asrcb = create_signed_asrcb(usr_sgn_key, user_data_orig.clone()); let user_data = verify_asrcb_and_get_user_data(asrcb, Some(usr_vrfy_key)).unwrap(); assert_eq!( user_data_orig.as_slice(), &user_data.as_ref().unwrap()[..user_data_orig.len()] ); } #[test] fn null_none_default_ncuid_one_user_rsa2048() { let usr_sgn_key = get_test_asset!("keys/rsa2048key.pem"); let usr_sgn_key = PKey::private_key_from_pem(usr_sgn_key).unwrap(); let user_data_orig = vec![0x56; 0x100]; let asrcb = create_signed_asrcb(usr_sgn_key, user_data_orig.clone()); let usr_vrfy_key = get_test_asset!("keys/rsa2048key.pub.pem"); let usr_vrfy_key = PKey::public_key_from_pem(usr_vrfy_key).unwrap(); let user_data = verify_asrcb_and_get_user_data(asrcb, Some(usr_vrfy_key)).unwrap(); assert_eq!( user_data_orig.as_slice(), &user_data.as_ref().unwrap()[..user_data_orig.len()] ); } #[test] fn null_none_default_ncuid_one_user_rsa3072() { let usr_sgn_key = get_test_asset!("keys/rsa3072key.pem"); let usr_sgn_key = PKey::private_key_from_pem(usr_sgn_key).unwrap(); let user_data_orig = vec![0x56; 0x80]; let asrcb = create_signed_asrcb(usr_sgn_key, user_data_orig.clone()); let usr_vrfy_key = get_test_asset!("keys/rsa3072key.pub.pem"); let usr_vrfy_key = PKey::public_key_from_pem(usr_vrfy_key).unwrap(); let user_data = verify_asrcb_and_get_user_data(asrcb, Some(usr_vrfy_key)).unwrap(); assert_eq!( user_data_orig.as_slice(), &user_data.as_ref().unwrap()[..user_data_orig.len()] ); } #[test] fn null_none_default_cuid_one() { let asrcb = gen_asrcb(GuestSecret::Null, None, no_flag(), true).unwrap(); let exp = get_test_asset!("exp/asrcb/null_none_default_cuid_one"); assert_eq!(asrcb, exp); } #[test] fn assoc_none_default_cuid_one() { let asrcb = gen_asrcb(association(), None, no_flag(), true).unwrap(); let exp = get_test_asset!("exp/asrcb/assoc_none_default_cuid_one"); assert_eq!(asrcb, exp); } #[test] fn null_simple_default_cuid_one() { let asrcb = gen_asrcb(GuestSecret::Null, ext_simple(), no_flag(), true).unwrap(); let exp = get_test_asset!("exp/asrcb/null_simple_default_cuid_one"); assert_eq!(asrcb, exp); } #[test] fn assoc_simple_default_cuid_one() { let asrcb = gen_asrcb(association(), ext_simple(), no_flag(), true).unwrap(); let exp = get_test_asset!("exp/asrcb/assoc_simple_default_cuid_one"); assert_eq!(asrcb, exp); } #[test] fn null_derived_default_cuid_one() { let asrcb = gen_asrcb(GuestSecret::Null, ext_derived(), no_flag(), true).unwrap(); let exp = get_test_asset!("exp/asrcb/null_derived_default_cuid_one"); assert_eq!(asrcb, exp); } #[test] fn assoc_derived_default_cuid_one() { let asrcb = gen_asrcb(association(), ext_derived(), no_flag(), true).unwrap(); let exp = get_test_asset!("exp/asrcb/assoc_derived_default_cuid_one"); assert_eq!(asrcb, exp); } #[test] fn null_none_dump_cuid_one() { let mut flags = no_flag(); flags.set_disable_dump(); let asrcb = gen_asrcb(GuestSecret::Null, None, flags, true).unwrap(); let exp = get_test_asset!("exp/asrcb/null_none_dump_cuid_one"); assert_eq!(asrcb, exp); } #[test] fn null_none_default_ncuid_one() { let asrcb = gen_asrcb(GuestSecret::Null, None, no_flag(), false).unwrap(); let exp = get_test_asset!("exp/asrcb/null_none_default_ncuid_one"); assert_eq!(asrcb, exp); } #[test] fn null_none_default_cuid_seven() { let (hkd, ctx) = get_crypto(); let mut asrcb = AddSecretRequest::new(AddSecretVersion::One, GuestSecret::Null, TAGS, no_flag()); (0..7).for_each(|_| asrcb.add_hostkey(hkd.clone())); asrcb.set_cuid(CUID); let asrcb = asrcb.encrypt(&ctx).unwrap(); let exp = get_test_asset!("exp/asrcb/null_none_default_cuid_seven"); assert_eq!(asrcb, exp); } #[test] fn verify_no_user_data() { let req = get_test_asset!("exp/asrcb/null_none_default_ncuid_one"); assert!(matches!( verify_asrcb_and_get_user_data(req.to_vec(), None), Ok(None) )) } s390-tools-2.38.0/rust/pv/tests/assets/000077500000000000000000000000001502674226300175355ustar00rootroot00000000000000s390-tools-2.38.0/rust/pv/tests/assets/cert/000077500000000000000000000000001502674226300204725ustar00rootroot00000000000000s390-tools-2.38.0/rust/pv/tests/assets/cert/der.crl000066400000000000000000000016001502674226300217430ustar00rootroot000000000000000|0d0  *H  01 0 UUS1402U +International Business Machines Corporation1402U +International Business Machines Corporation10U New York10U Poughkeepsie1'0%U IBM Z Host Key Signing Service 240311155239Z23881223155239Z0<0+ 240320155239Z0%2/.Qʾ[n)C'I 240320155239Z#0!0U#0Ì]Πe+gu0  *H  ɯrδ1}ݚNBk?jnc?BjΠşVU \i}t( Pf ^Hrˊ6~izY!0֎ n MȖbW82 *cVaٳ /H hM/k)4lVl]3QMz =B4Y$_SxoJ|pBj3$hg6G ]e*WFG9Z s%Y`5%\6' /ŕPF#Xc^dGHKZK=jilS{%Y&>lA8[gg|7[Cc(ְr+\;eN_"ܬn} #|𬢆N7K@HⓞBHF[Ǣ > P:]_W~+w?35BQM*C=2MWIĒs_ h5 e-`)0es390-tools-2.38.0/rust/pv/tests/assets/cert/der.crt000066400000000000000000000032651502674226300217640ustar00rootroot00000000000000003`xUUg0  *H  01 0 UUS1402U +International Business Machines Corporation1402U +International Business Machines Corporation10U New York10 U Armonk10U IBM Z Intermediate CA0  240321145238Z23881223145238Z01 0 UUS1402U +International Business Machines Corporation1402U +International Business Machines Corporation10U New York10U Poughkeepsie1'0%U IBM Z Host Key Signing Service0"0  *H 0 ڜFJ? S924um6C 0W4-}1|H^0.,je[]XfI` IhVEcPlL̎P3S!BңѭLNcx0RNyUp)6Kvq2yhjiೝV=ߞNY9:I\0{x9Hq]fbgeM0|#ˆK?FFRWnt^fxt͸,3ok⬌t5Pܻz }v00U00 inter_ca.crl0 U00U0U% 0 +0U#0fzC4f0UÌ]Πe+gu0  *H  _{d$Of/d%:rz"DPZEM;V9v4{h xu--泎osh!Q9v3+ixV?9 ͆h /?׾11&idJ\LwJfؕ?zxޖ(>FFw>˥e.9ev~5AZ0" 0 CWUqOva 31'Q\e7 gS,8=ϓ͓(5^bMtvN2 ːXӂg;ǵ&vg2mOh[/A|>,l"ζ:g%Ub[0S8zfp;.b~5^4=?Vo?pfV&a3f,Z[QG}9Ҳ]NM=" ѣZq2ke+Q2%Pϋ4$Yw6.¼,SΈlb/ȳ 8^i ^9F#$5uE=39.0.0 s390-tools-2.38.0/rust/pv/tests/assets/cert/host.crt000066400000000000000000000033711502674226300221650ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIE+DCCAuCgAwIBAgIUBSLhuGTvxPbggG70ISL2R6DDGZcwDQYJKoZIhvcNAQEN BQAwgcwxCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y azEVMBMGA1UEBwwMUG91Z2hrZWVwc2llMScwJQYDVQQLDB5JQk0gWiBIb3N0IEtl eSBTaWduaW5nIFNlcnZpY2UwIBcNMjQwMzIxMTQ1MjM5WhgPMjM4ODEyMjMxNDUy MzlaMIG2MQswCQYDVQQGEwJVUzE0MDIGA1UECgwrSW50ZXJuYXRpb25hbCBCdXNp bmVzcyBNYWNoaW5lcyBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrSW50ZXJuYXRpb25h bCBCdXNpbmVzcyBNYWNoaW5lcyBDb3Jwb3JhdGlvbjERMA8GA1UECAwITmV3IFlv cmsxDzANBgNVBAcMBkFybW9uazEXMBUGA1UECwwOSUJNIFogSG9zdCBLZXkwgZsw EAYHKoZIzj0CAQYFK4EEACMDgYYABAHGO0MnpQa6Q2IxgqV7AGwd3OwBnYOJjYJF hzrwY+wQacmJjWeNyHahBCxu4bM8vDr70SF5vZFrWpcWpc9JTY5AagFCFDqIfSvL J6lKJuCog5RfMsWJpG2j/MnK7MxG+Ph0R+ItmLFbWFxCV5YOT43olhwYZr/pd9qH PAD96UEDM8JanKNsMGowGAYDVR0fBBEwDzANoAugCYYHaWJtLmNybDAOBgNVHQ8B Af8EBAMCAwgwHwYDVR0jBBgwFoAUw4weXbTWAZisD86gZSugZ6V1FNkwHQYDVR0O BBYEFL4m5UxVbUdEl9yg4sjiWKO/EAxyMA0GCSqGSIb3DQEBDQUAA4ICAQBwaPG5 Mg3iKtxR6ncteH+YmtMHW4/wB/341pTKFsKBYsMNWuCC5AKbNrshCNbFbctqhLrB LmEpmza3/Pk6izO4AozHNl0tRec/HnQr2gonfI48HBDRiV2f40x0gJG9gGCiJy7o 6iKZDYUnjfnhXVC67RwLMEiIKbeOAWQ9hHqegUjYdaaIlhyiHLMuWMceidvG76nN 2eyJUNEouT4+UvquD2oqSitB3ZLhWRqPOQn57ME1b0QYF240PN8r21YtzPmSI+s/ ej04EcQZrlJId6GtU7YwD1767hVw84v/QjPbMqnYQbxX8n3IvOf541rQ0UdjBFc9 UhbnSn32IGFrRlL1y3MPBF6hLPcpW4P0QrUijc6gZ+x6SNFho8n+dk53F7RvMi1l SLgJl7x8pUeqBn5QKMcYYsZG39oZmQj4xHjAABx2hRWayDscvROiQpvLHRtLVmk7 +hq4Q/jalc2cNHZSwLX6Tv5P+8waTnXg8YNEHeAAgcw1lD+uw5HgusjGD4USE7Hq Q6EDGzC+Ny3u2+35XWbNaKWVthtKAIcZ9B4LjdJXeQFGcOMr6yV5rKfOFQwXo8bS rNy57tiva8KM2weSfRil4f146Rsb3TJzUdlkaN+NVIY0YImiC+rR0qa6Iv6JCio1 F2lu8m/aRHQQF5J5fD7ge6v7F2D6K3qT9tTlrw== -----END CERTIFICATE----- s390-tools-2.38.0/rust/pv/tests/assets/cert/host.key000066400000000000000000000006001502674226300221550ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIA8rPNC6rxZR+GxSxb qWRreFGnWRQGd22nHWKAvQmrA5GlXTtWQMoL8il9Jb1OnrQPPo620nQpzA1GXo4U BUqgYoShgYkDgYYABAHGO0MnpQa6Q2IxgqV7AGwd3OwBnYOJjYJFhzrwY+wQacmJ jWeNyHahBCxu4bM8vDr70SF5vZFrWpcWpc9JTY5AagFCFDqIfSvLJ6lKJuCog5Rf MsWJpG2j/MnK7MxG+Ph0R+ItmLFbWFxCV5YOT43olhwYZr/pd9qHPAD96UEDM8Ja nA== -----END PRIVATE KEY----- s390-tools-2.38.0/rust/pv/tests/assets/cert/host2.crt000066400000000000000000000033711502674226300222470ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIE+DCCAuCgAwIBAgIUC3KzCH9KUb8ZaY6J/97gqebLoIYwDQYJKoZIhvcNAQEN BQAwgcwxCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y azEVMBMGA1UEBwwMUG91Z2hrZWVwc2llMScwJQYDVQQLDB5JQk0gWiBIb3N0IEtl eSBTaWduaW5nIFNlcnZpY2UwIBcNMjQwMzIxMTQ1MjM5WhgPMjM4ODEyMjMxNDUy MzlaMIG2MQswCQYDVQQGEwJVUzE0MDIGA1UECgwrSW50ZXJuYXRpb25hbCBCdXNp bmVzcyBNYWNoaW5lcyBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrSW50ZXJuYXRpb25h bCBCdXNpbmVzcyBNYWNoaW5lcyBDb3Jwb3JhdGlvbjERMA8GA1UECAwITmV3IFlv cmsxDzANBgNVBAcMBkFybW9uazEXMBUGA1UECwwOSUJNIFogSG9zdCBLZXkwgZsw EAYHKoZIzj0CAQYFK4EEACMDgYYABAE3KDrdCdVeBV34NicA8AoP7hItcwxgXHOQ F+V02N6b5AR7w3YDHE/JSzSoZZIYiqdy8SmeD5GtwHLV8tLQ8xnadgHT0BesGS02 w0H5foGzzZOHZW3AfbdH4MpssR4Bf1jqL0jw6eV+oiMStDuZ44zri7PFjvVByt6M oeSvh5qAXCQG/6NsMGowGAYDVR0fBBEwDzANoAugCYYHaWJtLmNybDAOBgNVHQ8B Af8EBAMCAwgwHwYDVR0jBBgwFoAUw4weXbTWAZisD86gZSugZ6V1FNkwHQYDVR0O BBYEFL8FyjCu1iRcjwx4pzfo+VIQ5NdbMA0GCSqGSIb3DQEBDQUAA4ICAQASxSXu RxNw/kSwqedNq9jOTHb5FATNykBIVSuXS9BB2qkjcTVDXNnlsSBrPL/CumRp/TFD 5VsB2rLhESmUrpghQrODeFvyFE52yVhxvcNCyjz7yIQZvc4qofMQMsg3o0rSqp2s lu1PUbcrL2aCG1yxB3isObVqiWaiRdnPxL8aX3Qt6BszlwWUgaFoaH0uZxlgVGKV C+dXrn5WkNRVd2ouHSLQE6fIUYIf/TrV+AKu804IEoFRIvMUCqQRUHsj5toKhfDb 6tl/Xd+EiPCYbnhR2J01I08yxMExvYXfXapY7JJjlWTHKFKaxLoqv++NZRM1bW6s uyLWP735Qb+0AmhZ6TfeJM7H77LpQK0WCylaNVJWWjXt9UsnNdirCbp/jpKF8bnG 2PkjBHKruvCakqw1bDq8eDv9In1Ki+Um4gp7OfjYvcN8zxvGQofgj++UaCy982iX WSq14iUyrRDVu8zWghL/F1lUx7ab8UV+OrmZuCALVHZ76YVdwmJXGYll1OBbJbgL 5xze6p77vKzbgNyABWmR6TlHq/nFDhj9kKirpQaI7WHyOtsGpc7sqd0tT+CeOhNf l3xXyFPb6N58aSC2cY0W0Nq6X/mWIgMqHY9lzYLmoLBFFZlIjqWzwyVcajmjrtaK rlfs0e9f9DvVV8bMTXFMUBlWrmYDrROKpYLqhg== -----END CERTIFICATE----- s390-tools-2.38.0/rust/pv/tests/assets/cert/host2.key000066400000000000000000000006001502674226300222370ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIAQNfN5BLLLAdhlwGk Ve8w2t+9x4LWR9st6CLCYHnTgc2gr4+HqgcMwmNuj3cA8ENBvIShUnHkX2E+9CyP 3W1ZN8OhgYkDgYYABAE3KDrdCdVeBV34NicA8AoP7hItcwxgXHOQF+V02N6b5AR7 w3YDHE/JSzSoZZIYiqdy8SmeD5GtwHLV8tLQ8xnadgHT0BesGS02w0H5foGzzZOH ZW3AfbdH4MpssR4Bf1jqL0jw6eV+oiMStDuZ44zri7PFjvVByt6MoeSvh5qAXCQG /w== -----END PRIVATE KEY----- s390-tools-2.38.0/rust/pv/tests/assets/cert/host_armonk.crt000066400000000000000000000033711502674226300235340ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIE+TCCAuGgAwIBAgIUWlKUd1HKU8R7cjv5NJdWhHzhT5cwDQYJKoZIhvcNAQEN BQAwgcYxCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y azEPMA0GA1UEBwwGQXJtb25rMScwJQYDVQQLDB5JQk0gWiBIb3N0IEtleSBTaWdu aW5nIFNlcnZpY2UwIBcNMjQwMzIxMTQ1MjM5WhgPMjM4ODEyMjMxNDUyMzlaMIG2 MQswCQYDVQQGEwJVUzE0MDIGA1UECgwrSW50ZXJuYXRpb25hbCBCdXNpbmVzcyBN YWNoaW5lcyBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrSW50ZXJuYXRpb25hbCBCdXNp bmVzcyBNYWNoaW5lcyBDb3Jwb3JhdGlvbjERMA8GA1UECAwITmV3IFlvcmsxDzAN BgNVBAcMBkFybW9uazEXMBUGA1UECwwOSUJNIFogSG9zdCBLZXkwgZswEAYHKoZI zj0CAQYFK4EEACMDgYYABAHGO0MnpQa6Q2IxgqV7AGwd3OwBnYOJjYJFhzrwY+wQ acmJjWeNyHahBCxu4bM8vDr70SF5vZFrWpcWpc9JTY5AagFCFDqIfSvLJ6lKJuCo g5RfMsWJpG2j/MnK7MxG+Ph0R+ItmLFbWFxCV5YOT43olhwYZr/pd9qHPAD96UED M8JanKNzMHEwHwYDVR0fBBgwFjAUoBKgEIYOaWJtX2FybW9uay5jcmwwDgYDVR0P AQH/BAQDAgMIMB8GA1UdIwQYMBaAFMOMHl201gGYrA/OoGUroGeldRTZMB0GA1Ud DgQWBBS+JuVMVW1HRJfcoOLI4lijvxAMcjANBgkqhkiG9w0BAQ0FAAOCAgEACPbD hFuibNamlcpnVG7mgnO56RB/K5otOCGS0hbR3OFUGY1ZDQ++M44OF/b+eNYHgjsr ER4r2cne3qBjOArFj/toEDGM2l/DFWDnpOvh1ZItJjpQe07OGn+KpTLf/ZB0Q5D7 jV/ddjJ0GGyxessjfUvmCT4BVWn1bXKJFSgujic7lgMf8WBGwW+WW3eZecYsh/Cn rHWfQYqtvzB1uM89bwZwN+lvHz/QHTreAShDAiCY9M31cemvUvC58z8jPKngFhuf C7ZMUXbzu2jYWo3EowzvcRpO1KqlLfNHjGLbaBaZWP2ocK2IUTrsAbr7PTdcAqjX TRIds4JlbidA8OziEXFTcM2xShS6WISsO+9JXk5Xxc3+xscjSamHTMwRm2LaKjrr d8mOxMs4/5547CBsBFMqu98tiD18K0yqdJiJJRDDOvqVeCvDE6y/pzKaSHIIyA0P ATnjQzvgqbcwLrdoJ4WNdTpZieoisUR0oMAKUViPwd0xStzNX6K89PSd5c0Bqbhj TwCpaT+DfcvlWk4wqEa9rLn+Su1SUfMSD2dITynLW0UexiKPHZ5epOPu5ho6cOk/ fe/N1cX7SyOjhErWLwqr974YM0EPiR6f5lNooYXMPbatq8lcG0gpEg5pggHhsLiY LGfvu2jQJKGnrBYM3IcRGUsc+VZ6CNTJjaIIbGg= -----END CERTIFICATE----- s390-tools-2.38.0/rust/pv/tests/assets/cert/host_crt_expired.crt000066400000000000000000000033651502674226300245600ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIE9jCCAt6gAwIBAgIUSpwJMAovpO3Z9SuY7Zw+/pv3bLEwDQYJKoZIhvcNAQEN BQAwgcwxCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y azEVMBMGA1UEBwwMUG91Z2hrZWVwc2llMScwJQYDVQQLDB5JQk0gWiBIb3N0IEtl eSBTaWduaW5nIFNlcnZpY2UwHhcNMjIwMzIyMTU1MjM5WhcNMjMwMzIyMTU1MjM5 WjCBtjELMAkGA1UEBhMCVVMxNDAyBgNVBAoMK0ludGVybmF0aW9uYWwgQnVzaW5l c3MgTWFjaGluZXMgQ29ycG9yYXRpb24xNDAyBgNVBAMMK0ludGVybmF0aW9uYWwg QnVzaW5lc3MgTWFjaGluZXMgQ29ycG9yYXRpb24xETAPBgNVBAgMCE5ldyBZb3Jr MQ8wDQYDVQQHDAZBcm1vbmsxFzAVBgNVBAsMDklCTSBaIEhvc3QgS2V5MIGbMBAG ByqGSM49AgEGBSuBBAAjA4GGAAQBxjtDJ6UGukNiMYKlewBsHdzsAZ2DiY2CRYc6 8GPsEGnJiY1njch2oQQsbuGzPLw6+9Eheb2Ra1qXFqXPSU2OQGoBQhQ6iH0ryyep SibgqIOUXzLFiaRto/zJyuzMRvj4dEfiLZixW1hcQleWDk+N6JYcGGa/6XfahzwA /elBAzPCWpyjbDBqMBgGA1UdHwQRMA8wDaALoAmGB2libS5jcmwwDgYDVR0PAQH/ BAQDAgMIMB8GA1UdIwQYMBaAFMOMHl201gGYrA/OoGUroGeldRTZMB0GA1UdDgQW BBS+JuVMVW1HRJfcoOLI4lijvxAMcjANBgkqhkiG9w0BAQ0FAAOCAgEAd9NdTLDK fWPdOg78c+CaG2XVnIzBy6niur4vM1DPJYbwabTYbpJM2jtevMo3h96jCd6AfHzv cFZ1BzpYsWtjbDFhiqowmKnlFlOIoIDXCaG7vBvDWc4iWGG9PHMWEouSQWxg77bZ A8l7+K0VdRWYCqwxxClK8oK3PqGyGqELRDWwlBb0kRi9XRmB+2BjOdZOQvmGka/R l8hZWHTs+SPHZ+ySiJn55/w7gN/LCJs1XX70jPrzu01lDPTqVIrGKBtWBMKFKR7g A4r3EThfj85Xq1T45rvD0ozlkyVmdMtSk3z+fKCt5+qEHAu52kq2Ps7xQviAYkIK f+yNf1a/Ly79QoqEibRXuKMk/oWoqv7f5vT5HqoWEWZFIYbdbYTBIwkw3mHwZRZx CcyiukHw2W/ZwM6p60gXWZwrQxwVkdhxWnAHrAToL6O8M9Fk4aVWUkFNjqVUjTWS HLCxv8mCPujXIneqXyiJOpAPKu2kjmoFwfisjevNUmO5EqUHUba98Ne7rdiWweP3 nwcBBKIQ2MVZ/lPqfPtqFOHqBzCbqozVw1HzE2/ImnYsK8SGqwBnSJyTt+WjM/8S 4ulkjVDIwWFwUkOWjM2yorp3u2tFJF2sMv+3AR3mp6aDjF7sCzYpLaZag6oDmwOF nR+VwEbTC8gnl21swWSjtUC8I0EidfxzCpc= -----END CERTIFICATE----- s390-tools-2.38.0/rust/pv/tests/assets/cert/host_invalid_signing_key.crt000066400000000000000000000034051502674226300262570ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIFAzCCAuugAwIBAgIUELm+ubD3lNPrFxtCj86wMh7lYEcwDQYJKoZIhvcNAQEN BQAwgckxCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y azEVMBMGA1UEBwwMUG91Z2hrZWVwc2llMSQwIgYDVQQLDBtLZXkgU2lnbmluZyBT ZXJ2aWNlIEludmFsaWQwIBcNMjQwMzIxMTQ1MjM5WhgPMjM4ODEyMjMxNDUyMzla MIG2MQswCQYDVQQGEwJVUzE0MDIGA1UECgwrSW50ZXJuYXRpb25hbCBCdXNpbmVz cyBNYWNoaW5lcyBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrSW50ZXJuYXRpb25hbCBC dXNpbmVzcyBNYWNoaW5lcyBDb3Jwb3JhdGlvbjERMA8GA1UECAwITmV3IFlvcmsx DzANBgNVBAcMBkFybW9uazEXMBUGA1UECwwOSUJNIFogSG9zdCBLZXkwgZswEAYH KoZIzj0CAQYFK4EEACMDgYYABAB/2mDXj/QP7fcpxLTNfFfuh/X1Y/RS8XK+Y4v+ 0nisTQhe3MK8BZ7D0bcB872s8EJCYZ1OYr5O2epQgv2hbk4T1wCGKe+Pjf2QHTts wXMpPf87R3etF+zWR+CYH+5N5uQhjW9Ueeq7wve3A61e+Atz7IzTKVlM29kT0XvI sYtazn6usqN6MHgwJgYDVR0fBB8wHTAboBmgF4YVaWJtX3dyb25nX3N1YmplY3Qu Y3JsMA4GA1UdDwEB/wQEAwIDCDAfBgNVHSMEGDAWgBTXVfLQH2D0KccIHhK6M5MR TITvtDAdBgNVHQ4EFgQUWdGEEpvjFJ4SNUillC09iBfcTEUwDQYJKoZIhvcNAQEN BQADggIBACfZ7+KtT0Hl4Y/JBwCtUqCRRt4kCM5rXqORswkJ6RNmx1OtBHPlkUEI E5HrE0dZlaECfqMkOBxhbBqOSpoL/xjz1gDHILWXEbWchuFnU4EAgYvJS7nLAHHO swJCEP8VDM3y5lUWni8sXs6ro/H5tAkm260+xbn0BFF99l+M124ZUawB+tDg7E9b MNG0eWtfeiZzWBBhoxhQoD03gFADWxTU1EK4JgCl7frsj4PjK7TMKTV/Gd/Bd5l0 KreFW5orupd3YBqDeUYI6p1l9+uThottwrA4z/POygxvhqgoqNA/+evMWX3wxNza ComAmWffB5EJQFEKcpdASwEhyjBMap15GdJVBFGYzXAlksQz7k1fr4h0ebuYtcpb dvcDyeIxjsg5XiP32494wogLUjHioi5mwIrGn8/9V6JLRGfKBHnmejHBR/cbMpuv rpl0ZZks3bicmBVTKFyj1ORRGX1cXsbtCbPBJEmBeR5IOFYBeaQHrqTcaEBz4lWe K2OT6ebcqtsCzDSmjsoeAX/CWRVaIzLzMc9SqE8lZ68ajXjbt/59I7EBeOenttO5 mg9mokwDqoeM0bXyT3qdT4s9fhy0BPgnBqJHKIc9afzV79Iz1uJ/E+TZJ+tzhbAU n4fFxIHOdbyaOLCLT0+O74FZCQq8FUUnL9QVY5dXTH2458IUiD20 -----END CERTIFICATE----- s390-tools-2.38.0/rust/pv/tests/assets/cert/host_invalid_signing_key.key000066400000000000000000000006001502674226300262510ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIB48Wl8pvlD/o6F7YG sFfCXNQRbKbppIrywEBkfNG0VzfXazBOLD41bcdPEBh1111kSYgn46uUVxjabyLp BgQthf2hgYkDgYYABAB/2mDXj/QP7fcpxLTNfFfuh/X1Y/RS8XK+Y4v+0nisTQhe 3MK8BZ7D0bcB872s8EJCYZ1OYr5O2epQgv2hbk4T1wCGKe+Pjf2QHTtswXMpPf87 R3etF+zWR+CYH+5N5uQhjW9Ueeq7wve3A61e+Atz7IzTKVlM29kT0XvIsYtazn6u sg== -----END PRIVATE KEY----- s390-tools-2.38.0/rust/pv/tests/assets/cert/host_rev.crt000066400000000000000000000033711502674226300230410ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIE+DCCAuCgAwIBAgIUMi8utJhRyr5bbvABnCn/Q9/vJ0kwDQYJKoZIhvcNAQEN BQAwgcwxCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y azEVMBMGA1UEBwwMUG91Z2hrZWVwc2llMScwJQYDVQQLDB5JQk0gWiBIb3N0IEtl eSBTaWduaW5nIFNlcnZpY2UwIBcNMjQwMzIxMTQ1MjM5WhgPMjM4ODEyMjMxNDUy MzlaMIG2MQswCQYDVQQGEwJVUzE0MDIGA1UECgwrSW50ZXJuYXRpb25hbCBCdXNp bmVzcyBNYWNoaW5lcyBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrSW50ZXJuYXRpb25h bCBCdXNpbmVzcyBNYWNoaW5lcyBDb3Jwb3JhdGlvbjERMA8GA1UECAwITmV3IFlv cmsxDzANBgNVBAcMBkFybW9uazEXMBUGA1UECwwOSUJNIFogSG9zdCBLZXkwgZsw EAYHKoZIzj0CAQYFK4EEACMDgYYABAHkrwf4hZ7M5ahoYHT0u1Xgl28qxURcdNmW kYDA5u1Y2mGXQq85BakAOyfxl/FF/cBrLm0eLVFpePumqgPna3El1gAs9p2SFwN7 4MmcLhbwPmGnWmzVrOh9cNS988XYf01E74966r4MKpAGxSNPKDwu4doXFVAYRH/w tcgbjlk+Qi3c96NsMGowGAYDVR0fBBEwDzANoAugCYYHaWJtLmNybDAOBgNVHQ8B Af8EBAMCAwgwHwYDVR0jBBgwFoAUw4weXbTWAZisD86gZSugZ6V1FNkwHQYDVR0O BBYEFG+41urs6GhYzOrJD/MJDWMSrUlvMA0GCSqGSIb3DQEBDQUAA4ICAQAnowYb R1qgnpH5FLgkj3LndAAfFmJMlTrNcmtafjaLZHUmxxO1tFjb7vvGgu9LggwwSRoN y0Qsu0mywXd/ntEuWDyD735aGtUE9xnmluoj4ILBnln195VuvUoWYjCy0XsGZg6G UL8bOaZgbvXQWDcdXfpYnM9C5Uj4mB3LFDdasd7nDv2F9vZlHNkd0uGmZvBco3SX GNFETOaY5HjU3jLlCmD74p7G9e//+153R2aXEww3SVVJyRowO7SpwH88DNjl2a6A OLWoA3oRrX4rDQI66LYv0RZMDCTvzY+HxOQHXeswosA8D96GhpClb1X0wLZUp6gR pLzVzaE8RFnSg52jzjI3ehFIoSK0x9R1KPzKbYDh7Q8Sc5sOSe6r9Q01BdpBOUjp iTKhgbUEbcIwcaKLIGeSBe0+Yh1uQpqBtOPectUp9bz+hEUDzG6QMS5VFxAGuZWY 2iA97jxHsUBEgMBZha7z/hQ68Pst+KaIV/24By8lKm0rWpLSKOXVAW1lWZrlpIcx Le8+dmy9/JFJXmmN/pmAEVVsK5dYtVXt6ikVi+LPr/7fCfvt2zk88wiUp8iz5jgQ pNYT8MPOMf+yzzoqPXjGmrT5znuHnCg0OSh0u8NILWQPQl8M6KNiI8lzcVBy6Djq fmnQlaxP1Ey+wrznWmBu5zesU8uMyRTxIfnEzg== -----END CERTIFICATE----- s390-tools-2.38.0/rust/pv/tests/assets/cert/host_rev.key000066400000000000000000000006001502674226300230310ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIBLz0ExeBJrxd5gA7C YFJLVuNd4CPNc/jsOLIMnIbLHSegdOtQn9tief97k5rZdz/MQzWp7rCmsLUYw/fM ktUQjJKhgYkDgYYABAHkrwf4hZ7M5ahoYHT0u1Xgl28qxURcdNmWkYDA5u1Y2mGX Qq85BakAOyfxl/FF/cBrLm0eLVFpePumqgPna3El1gAs9p2SFwN74MmcLhbwPmGn WmzVrOh9cNS988XYf01E74966r4MKpAGxSNPKDwu4doXFVAYRH/wtcgbjlk+Qi3c 9w== -----END PRIVATE KEY----- s390-tools-2.38.0/rust/pv/tests/assets/cert/host_uri_na.crt000066400000000000000000000033751502674226300235260ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIE/TCCAuWgAwIBAgIUPRltRSFZSbucmw20u9TN7WHxG8UwDQYJKoZIhvcNAQEN BQAwgcwxCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y azEVMBMGA1UEBwwMUG91Z2hrZWVwc2llMScwJQYDVQQLDB5JQk0gWiBIb3N0IEtl eSBTaWduaW5nIFNlcnZpY2UwIBcNMjQwMzIxMTQ1MjM5WhgPMjM4ODEyMjMxNDUy MzlaMIG2MQswCQYDVQQGEwJVUzE0MDIGA1UECgwrSW50ZXJuYXRpb25hbCBCdXNp bmVzcyBNYWNoaW5lcyBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrSW50ZXJuYXRpb25h bCBCdXNpbmVzcyBNYWNoaW5lcyBDb3Jwb3JhdGlvbjERMA8GA1UECAwITmV3IFlv cmsxDzANBgNVBAcMBkFybW9uazEXMBUGA1UECwwOSUJNIFogSG9zdCBLZXkwgZsw EAYHKoZIzj0CAQYFK4EEACMDgYYABAHGO0MnpQa6Q2IxgqV7AGwd3OwBnYOJjYJF hzrwY+wQacmJjWeNyHahBCxu4bM8vDr70SF5vZFrWpcWpc9JTY5AagFCFDqIfSvL J6lKJuCog5RfMsWJpG2j/MnK7MxG+Ph0R+ItmLFbWFxCV5YOT43olhwYZr/pd9qH PAD96UEDM8JanKNxMG8wHQYDVR0fBBYwFDASoBCgDoYMbm90YXZhaWxhYmxlMA4G A1UdDwEB/wQEAwIDCDAfBgNVHSMEGDAWgBTDjB5dtNYBmKwPzqBlK6BnpXUU2TAd BgNVHQ4EFgQUviblTFVtR0SX3KDiyOJYo78QDHIwDQYJKoZIhvcNAQENBQADggIB AGlM/E6RfiQV8si38/IieHw2TsZDNKluXA/edGN4d9i5ztIB+G57IgvAR9GBeRTB AxANycBGOWy+VXsUij3UgpoK3NiZJ+XvgFYqx0ZVUX3uPmU6R0Ko5cHGmSEVV6KS 5ToGLqSZdftybGt9NYKg+LGxzIhT15tgUENkOrFVRSlKVn1zAP+sa2vOEtH5aX6S ins3hHQBuhGKSR/z2I5IzEVhnx0FJxpFO32QBdmXUdgjlHcpiIy1BaQRTca/U4iJ Q+fnzn5UP5v29ftr/PRh0W9rPVeS8ge+vuPC1eriRL4WWnkMFm0QljERqhg48NWk zT8mnVn0E3vQ2Y71FKg85ov0Sza8/OpBgAgnoyCDUIp8YD7BrU1viLEoqLtlvsoH l/+mU/CnCe5749C6OBppKhdrIRSG8bh6mIeco1ALDNIaqGoUtdhib0f+YKeCc0zC MFjvoSranndvwO+z/qdL7Feay8uAjgq1GPNuj/xaEbzrZz0yF2viMXfK62kgwq+W ebg2D1FHVuKXQ2q3IXmwcyLOEUScUcEYDsM5eDenoScgIE2FfHvoGOdJVLsQ9U+8 2HTb7ntvbc6ANCTcnt51s5/PfD7UaHeJiSxRpU6rjxnqKZ2mEjbF/Ha8AjImQrrd jpjNgH6H5p7ZQqc/4DGa/Q2B/rfvmn5bAgOhicEKMzm1 -----END CERTIFICATE----- s390-tools-2.38.0/rust/pv/tests/assets/cert/ibm.chained.crt000066400000000000000000000070751502674226300233560ustar00rootroot00000000000000-----BEGIN X509 CRL----- MIIDfDCCAWQCAQEwDQYJKoZIhvcNAQENBQAwgcwxCzAJBgNVBAYTAlVTMTQwMgYD VQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9u MTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBv cmF0aW9uMREwDwYDVQQIDAhOZXcgWW9yazEVMBMGA1UEBwwMUG91Z2hrZWVwc2ll MScwJQYDVQQLDB5JQk0gWiBIb3N0IEtleSBTaWduaW5nIFNlcnZpY2UXDTI0MDMx MTE1NTIzOVoYDzIzODgxMjIzMTU1MjM5WjA8MBMCAgIrFw0yNDAzMjAxNTUyMzla MCUCFDIvLrSYUcq+W27wAZwp/0Pf7ydJFw0yNDAzMjAxNTUyMzlaoCMwITAfBgNV HSMEGDAWgBTDjB5dtNYBmKwPzqBlK6BnpXUU2TANBgkqhkiG9w0BAQ0FAAOCAgEA hMmvyXLOtDG7faP93ZqMh061Qms/at5uYz/eAA9CGfS4E+hq2uQaic6gEMWf5NkH 9xRWB1UM9sLR5Ai/Fn+MqsbeXK+1ommOfYx0KL6J8JihHAlQpGYJXu683dxIB3LL ijZ+od5pes5ZIRKtMNaO4+meDG6e5guXDeFNyJaNYqFXOJcy8OIL4O3T840qY1aY YcHZs/sNwC/hSAloTeUQ6S+ga/EplzSDumz5VrRsEhVd3wScM1FNeqwMvA4VPUIa NOtZJNLJBLhfmetTeO/wb/y3Ekp8sXAf8UJqABczJODSaGc2RwULmxJd2Q5lESpX RkfvObqTWgpzpyV/1OhZYDUlXJqiADYnyAsQL8WV2uFQ5fRG7baVDiNYn4uE92Of FN7jXmT+7UcHSO5L0VqHS6Ia6j2NammKlbhsAw7dU3uxEgCJvBYlWSY+z9Fs+O1B hNU42fL5W7JnoGfqG+LqfDdbG0MG+GMo1rDDcvrEK73F41w7mLmZhfYAZY26xU6Q XyLcrG4YfR6FCyOs53zwrKKG9x3ETqY3S0DSSOKTnuvIGZBCSOxGpqdb7pQax6IJ PqsM+IzFC1C0Ol0eX1ee734rCHfNP6XEM7ucjBweNUJRrU0Q5CrGGJzEQz0ym/JN 81f/kQJJosSSc1+j8iDiaKkUgcQ1DWWZLYZgvCkwZcY= -----END X509 CRL----- -----BEGIN CERTIFICATE----- MIIGsTCCBJmgAwIBAgIUMwTHYM3peBjwVRi6iICr6FVn0cMwDQYJKoZIhvcNAQEL BQAwgb0xCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y azEPMA0GA1UEBwwGQXJtb25rMR4wHAYDVQQLDBVJQk0gWiBJbnRlcm1lZGlhdGUg Q0EwIBcNMjQwMzIxMTQ1MjM4WhgPMjM4ODEyMjMxNDUyMzhaMIHMMQswCQYDVQQG EwJVUzE0MDIGA1UECgwrSW50ZXJuYXRpb25hbCBCdXNpbmVzcyBNYWNoaW5lcyBD b3Jwb3JhdGlvbjE0MDIGA1UEAwwrSW50ZXJuYXRpb25hbCBCdXNpbmVzcyBNYWNo aW5lcyBDb3Jwb3JhdGlvbjERMA8GA1UECAwITmV3IFlvcmsxFTATBgNVBAcMDFBv dWdoa2VlcHNpZTEnMCUGA1UECwweSUJNIFogSG9zdCBLZXkgU2lnbmluZyBTZXJ2 aWNlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxNqcFB9GSj+nC1PV OTIAvzTuo3X7bTbohEPrIDDsEcGyVzQtfagI9vkxhhGLhXxIXrkwEI4u75DeLBdq ZR3KFJ3QD+jHW12EWNpmSYu272CEAJeb/SBJaFbh40WbY1BsTP+3zI5QMwVTmCH5 QqODD+GU0fDSo9Gti0yX4rxOY+54jDC77AhSlU55rYruBoIXVe4CcPUpqzZLgqB2 cTJ5g7lokGqc6w9pveCznVYGPfcdDt+ePGssOvrfBOJnn6N44tTRAG623BrpctXi t3IAhG60y6CqF29BjV0RKOv09nOBiph2a3lAiKYrpAt92BETFB7KSQManUrrvWOD mYEcoVkzvrVrB3WnCcHs6lsjQg1FcNrGcDzV9SmX0BIhM+fasxZyHWc08pr3NrRn Eks7lRqY+TS8DAeZYJt/2M0Jr32d6Bh+WdyGFFUj6sBPtCaC2VnSkxOgKXeWf/c5 EUXIQT2YXEkNK9CP8Kqs8IerEIpfceelQReA5QcvNruaJNktWLKJgYTrdLdfOP0u s+9JJHWcFH3tv5906CA+Tlm53Dk6SaRc/DB7lu94yTkcSOpxXZnximZi0GcPZYmZ TaswE7d8HCPLhks/RkZSV2764Kl08xle65APiWZ4dM24uSyu75Izb4hrwgIA4qyM dDVQnI3cu6utmKUPyHoJtH26dr0CAwEAAaOBlTCBkjAdBgNVHR8EFjAUMBKgEKAO hgxpbnRlcl9jYS5jcmwwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwEwYD VR0lBAwwCgYIKwYBBQUHAwMwHwYDVR0jBBgwFoAU+f7h8O2ttBHtwI4OZnpD9wU0 9GYwHQYDVR0OBBYEFMOMHl201gGYrA/OoGUroGeldRTZMA0GCSqGSIb3DQEBCwUA A4ICAQBfBZZ7ZPkRjRgk+0/7CGam2AAv/GSuma8lOnJ6IpBEUL0D01reRe9NO1Y5 iXasGZ80e95oC7WtePt1LS2I5rOOb3No0iHcUTm5lZV2M/ObAOwraQLXePCRVgU/ OQzNhtRoCpqgL80/1Ne+MTHj1yZp2GRKXOdMd39KZoG92JU/h556GQ6reN6WKNs+ wbFG+JKiRnfvPsul6J5lG4QuObCAZXa0fhcQNUHyWjCGIhMa8AuhkDDlIEOBV1Vx T/ixdoFhDIoz0xExFidR5uqYXKZlgzcMZ4JT7x0Vs/YsOIg9z5PNkyjt8Kjrxvqs NZ1eYuNNxdXxdJZ2x04y8AzLkLRYntOCks5nEamQtzv0ice1Jg52qNJnMm0Y0U9o 7BjlWy/pp8dBfIA+FyxsIs62hAE6Z/vuJa1VuWJblrT1MFM48Bh64p1m5uDXAZ9w Oy7n6YDpzWKWqn6XNV6FoDSkkwaOwB6bPaqAP1ZvP3BmVhednyYRYY21M2aOmxos 7rpaW1FHgYV96Dm60rL7XRtOEYZNPSIPDO/Ro1oWs3EFwjLLAWtlK1HdMswlnVDX 6M+LNCRZA3foNi6xvf7CvPkshtQe3FOfiw/OiPBsYsYREC+90Ml/oeTIsyCaOF6n 5+dpjCBeOUaxzfeQI4WCrLG6+iQZNXWgBkW/PHaPZTldRWzvdQ== -----END CERTIFICATE----- s390-tools-2.38.0/rust/pv/tests/assets/cert/ibm.crl000066400000000000000000000023571502674226300217520ustar00rootroot00000000000000-----BEGIN X509 CRL----- MIIDfDCCAWQCAQEwDQYJKoZIhvcNAQENBQAwgcwxCzAJBgNVBAYTAlVTMTQwMgYD VQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9u MTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBv cmF0aW9uMREwDwYDVQQIDAhOZXcgWW9yazEVMBMGA1UEBwwMUG91Z2hrZWVwc2ll MScwJQYDVQQLDB5JQk0gWiBIb3N0IEtleSBTaWduaW5nIFNlcnZpY2UXDTI0MDMx MTE1NTIzOVoYDzIzODgxMjIzMTU1MjM5WjA8MBMCAgIrFw0yNDAzMjAxNTUyMzla MCUCFDIvLrSYUcq+W27wAZwp/0Pf7ydJFw0yNDAzMjAxNTUyMzlaoCMwITAfBgNV HSMEGDAWgBTDjB5dtNYBmKwPzqBlK6BnpXUU2TANBgkqhkiG9w0BAQ0FAAOCAgEA hMmvyXLOtDG7faP93ZqMh061Qms/at5uYz/eAA9CGfS4E+hq2uQaic6gEMWf5NkH 9xRWB1UM9sLR5Ai/Fn+MqsbeXK+1ommOfYx0KL6J8JihHAlQpGYJXu683dxIB3LL ijZ+od5pes5ZIRKtMNaO4+meDG6e5guXDeFNyJaNYqFXOJcy8OIL4O3T840qY1aY YcHZs/sNwC/hSAloTeUQ6S+ga/EplzSDumz5VrRsEhVd3wScM1FNeqwMvA4VPUIa NOtZJNLJBLhfmetTeO/wb/y3Ekp8sXAf8UJqABczJODSaGc2RwULmxJd2Q5lESpX RkfvObqTWgpzpyV/1OhZYDUlXJqiADYnyAsQL8WV2uFQ5fRG7baVDiNYn4uE92Of FN7jXmT+7UcHSO5L0VqHS6Ia6j2NammKlbhsAw7dU3uxEgCJvBYlWSY+z9Fs+O1B hNU42fL5W7JnoGfqG+LqfDdbG0MG+GMo1rDDcvrEK73F41w7mLmZhfYAZY26xU6Q XyLcrG4YfR6FCyOs53zwrKKG9x3ETqY3S0DSSOKTnuvIGZBCSOxGpqdb7pQax6IJ PqsM+IzFC1C0Ol0eX1ee734rCHfNP6XEM7ucjBweNUJRrU0Q5CrGGJzEQz0ym/JN 81f/kQJJosSSc1+j8iDiaKkUgcQ1DWWZLYZgvCkwZcY= -----END X509 CRL----- s390-tools-2.38.0/rust/pv/tests/assets/cert/ibm.crt000066400000000000000000000045161502674226300217610ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIGsTCCBJmgAwIBAgIUMwTHYM3peBjwVRi6iICr6FVn0cMwDQYJKoZIhvcNAQEL BQAwgb0xCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y azEPMA0GA1UEBwwGQXJtb25rMR4wHAYDVQQLDBVJQk0gWiBJbnRlcm1lZGlhdGUg Q0EwIBcNMjQwMzIxMTQ1MjM4WhgPMjM4ODEyMjMxNDUyMzhaMIHMMQswCQYDVQQG EwJVUzE0MDIGA1UECgwrSW50ZXJuYXRpb25hbCBCdXNpbmVzcyBNYWNoaW5lcyBD b3Jwb3JhdGlvbjE0MDIGA1UEAwwrSW50ZXJuYXRpb25hbCBCdXNpbmVzcyBNYWNo aW5lcyBDb3Jwb3JhdGlvbjERMA8GA1UECAwITmV3IFlvcmsxFTATBgNVBAcMDFBv dWdoa2VlcHNpZTEnMCUGA1UECwweSUJNIFogSG9zdCBLZXkgU2lnbmluZyBTZXJ2 aWNlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxNqcFB9GSj+nC1PV OTIAvzTuo3X7bTbohEPrIDDsEcGyVzQtfagI9vkxhhGLhXxIXrkwEI4u75DeLBdq ZR3KFJ3QD+jHW12EWNpmSYu272CEAJeb/SBJaFbh40WbY1BsTP+3zI5QMwVTmCH5 QqODD+GU0fDSo9Gti0yX4rxOY+54jDC77AhSlU55rYruBoIXVe4CcPUpqzZLgqB2 cTJ5g7lokGqc6w9pveCznVYGPfcdDt+ePGssOvrfBOJnn6N44tTRAG623BrpctXi t3IAhG60y6CqF29BjV0RKOv09nOBiph2a3lAiKYrpAt92BETFB7KSQManUrrvWOD mYEcoVkzvrVrB3WnCcHs6lsjQg1FcNrGcDzV9SmX0BIhM+fasxZyHWc08pr3NrRn Eks7lRqY+TS8DAeZYJt/2M0Jr32d6Bh+WdyGFFUj6sBPtCaC2VnSkxOgKXeWf/c5 EUXIQT2YXEkNK9CP8Kqs8IerEIpfceelQReA5QcvNruaJNktWLKJgYTrdLdfOP0u s+9JJHWcFH3tv5906CA+Tlm53Dk6SaRc/DB7lu94yTkcSOpxXZnximZi0GcPZYmZ TaswE7d8HCPLhks/RkZSV2764Kl08xle65APiWZ4dM24uSyu75Izb4hrwgIA4qyM dDVQnI3cu6utmKUPyHoJtH26dr0CAwEAAaOBlTCBkjAdBgNVHR8EFjAUMBKgEKAO hgxpbnRlcl9jYS5jcmwwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwEwYD VR0lBAwwCgYIKwYBBQUHAwMwHwYDVR0jBBgwFoAU+f7h8O2ttBHtwI4OZnpD9wU0 9GYwHQYDVR0OBBYEFMOMHl201gGYrA/OoGUroGeldRTZMA0GCSqGSIb3DQEBCwUA A4ICAQBfBZZ7ZPkRjRgk+0/7CGam2AAv/GSuma8lOnJ6IpBEUL0D01reRe9NO1Y5 iXasGZ80e95oC7WtePt1LS2I5rOOb3No0iHcUTm5lZV2M/ObAOwraQLXePCRVgU/ OQzNhtRoCpqgL80/1Ne+MTHj1yZp2GRKXOdMd39KZoG92JU/h556GQ6reN6WKNs+ wbFG+JKiRnfvPsul6J5lG4QuObCAZXa0fhcQNUHyWjCGIhMa8AuhkDDlIEOBV1Vx T/ixdoFhDIoz0xExFidR5uqYXKZlgzcMZ4JT7x0Vs/YsOIg9z5PNkyjt8Kjrxvqs NZ1eYuNNxdXxdJZ2x04y8AzLkLRYntOCks5nEamQtzv0ice1Jg52qNJnMm0Y0U9o 7BjlWy/pp8dBfIA+FyxsIs62hAE6Z/vuJa1VuWJblrT1MFM48Bh64p1m5uDXAZ9w Oy7n6YDpzWKWqn6XNV6FoDSkkwaOwB6bPaqAP1ZvP3BmVhednyYRYY21M2aOmxos 7rpaW1FHgYV96Dm60rL7XRtOEYZNPSIPDO/Ro1oWs3EFwjLLAWtlK1HdMswlnVDX 6M+LNCRZA3foNi6xvf7CvPkshtQe3FOfiw/OiPBsYsYREC+90Ml/oeTIsyCaOF6n 5+dpjCBeOUaxzfeQI4WCrLG6+iQZNXWgBkW/PHaPZTldRWzvdQ== -----END CERTIFICATE----- s390-tools-2.38.0/rust/pv/tests/assets/cert/ibm.key000066400000000000000000000063101502674226300217530ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDE2pwUH0ZKP6cL U9U5MgC/NO6jdfttNuiEQ+sgMOwRwbJXNC19qAj2+TGGEYuFfEheuTAQji7vkN4s F2plHcoUndAP6MdbXYRY2mZJi7bvYIQAl5v9IEloVuHjRZtjUGxM/7fMjlAzBVOY IflCo4MP4ZTR8NKj0a2LTJfivE5j7niMMLvsCFKVTnmtiu4GghdV7gJw9SmrNkuC oHZxMnmDuWiQapzrD2m94LOdVgY99x0O3548ayw6+t8E4mefo3ji1NEAbrbcGuly 1eK3cgCEbrTLoKoXb0GNXREo6/T2c4GKmHZreUCIpiukC33YERMUHspJAxqdSuu9 Y4OZgRyhWTO+tWsHdacJwezqWyNCDUVw2sZwPNX1KZfQEiEz59qzFnIdZzTymvc2 tGcSSzuVGpj5NLwMB5lgm3/YzQmvfZ3oGH5Z3IYUVSPqwE+0JoLZWdKTE6Apd5Z/ 9zkRRchBPZhcSQ0r0I/wqqzwh6sQil9x56VBF4DlBy82u5ok2S1YsomBhOt0t184 /S6z70kkdZwUfe2/n3ToID5OWbncOTpJpFz8MHuW73jJORxI6nFdmfGKZmLQZw9l iZlNqzATt3wcI8uGSz9GRlJXbvrgqXTzGV7rkA+JZnh0zbi5LK7vkjNviGvCAgDi rIx0NVCcjdy7q62YpQ/Iegm0fbp2vQIDAQABAoICACjHosq2WeDaw9egY80H80ip BgC92bqbw9pWmZhfGiBpkHDpQb1EuKq1H9HGw7EA+JsK4Q6k7cs6JxRaO3O8Epdn w+jvz5C5VEf14ne85kY7p8zZm3p6HErtqLjkvBZ8XBstZF2PY8TWByW+b/0ik22Q TTyal4BRWRLA6LwTR2gDSFy+Yce5R6ZfjexqGfomLABkoIPW/e1Wf5kNdcvoaWzk 1WkJdP1rAXxQ1tSys/NVRGNG5uhcSVrkipvf7RiQjGPtM2jUBrSdLJxZ+7AGAV5t 8q02WNYm+Zz32SyxiNsfPYhBIlMMWNaWGfdPXCANFXc3E6/BYVuRYDmpKmN78DqN XrSCsJOahg7A9f2JIVKmreC/WmHi0+eL0fCcMHK95CN415mYLagg1bLhOwu0cdjq peSx3+xlEWENDm0B4mYQaPDylHht++Giswg9VpZeIPstgvNARoiCgRCOiR3mXVBg ZX+BpH45RSZ0hqdhkssiwP3iOQH0xYe1kEvzcaCeNrGc8cZy4dOcZjYso1XNZEQY vnUzAfo0TasiMES/RiNuy7Zf0oTP1UV7QootsqPQrZCFf3eixC91pq4Lh2URUseo 4yOOI1URBWU5U2RuykzYWsqs/xB+CfJy6dhn6jCjm5lZFt1/wEKj5+7JZbZWkH8A G2HHxKIQWz5d2KjbQBkxAoIBAQDwUtb6mrJ/JBvMubdR2Tkyog9lT7qeR848ZZqS ybGEuT1Ohhd7vtjiIQzMLX7s6XIUoSE3AWFJJzqFrr0f1HkwShBNlvF77We3jski p7u4bsJ91mjHBWl/bAKW9s3YA6PRakk8k215mwORf+trhFkcOJ63DHIPxBnPDQEI VBBMvp5ogHHIh5wTql+c4OpdEb5s0PuesKFzjdzKtvPashaP3ebt2nM8Nai86UKQ WTcZxMLk36XSLFcW2vZlmlkQ7q8gMM3BIliR2ofmB0AlPBwOY/gUHG+tIBbuQWoX tw4DJPY3Y9uWRqAcWOkSbQpAEOUVzyK5nrwXwnH7vOA9HOiZAoIBAQDRsd7x3NGL Gp8oY8XULwIrZfyQmsk+nozBNuo2Q40qoXBoReIUF212UMMYc2iX8bMo1jBCzmnl 3m2BA6aVbpxwlp14Ybe78F8ucQN6Dx5wmT7QyDYxdKyMAYOnOHWdW1ic1y2HjKY3 vinswhuI93C4Dkf4mxFtP+aWT3RpQxS7DsztBTKux7fACoU+OKQxrmZTkPSVhuEE DXIov09q3awEvB/R+dDzGj6NXwxQ+Kr3yLl137RvVWcvrObyysKLfnVx+Of0Kol/ 6VD82BBWDcgveu62JPSZ8ckY4eQbi3yl5mOAbL3vre+AQYuDiuOo2NiPOsAimGaz kv9gXZQ1leHFAoIBAQDsGHYavN+fCFpHRixSvJT0qUF2xl0QInr52teAXaWIPnN8 MT/g0h1ACjgIXqnTFYR9v85hu3lX5LIZoxEptBNa3Wgm0aNrnE/IhP4UjbRd/HIW Lg3BeA+snu/sX4raLLlDgqdwW2WxkhhvWLxvZBYnI3jJW/CyjHTOdHgPNobM3nfB Mm6WEqPCrh5AgLW9uTDatnR78gqq+zNt806eC1ce/2FfSrzq6cxbys0aAoufRS4y q4S8ddMZIQPvzTKy78ocVdXNZ3Cb2ZSo53adHfByMsQE/eq4qk3cw2b25V9et3Er +W5AtCGXt8FB2N25Et/8DQKQWOFwdhaEuYmSgFQBAoIBAEgmEBRdqsdWyI4oDggc iH2QIJ9McpOWD83m7Bzxjx+s3jUyXkAVc3czAH1oMAOfiMozL/W8eZk6t6idLfLa VP80A0hJLuN+J/GdttmHXCzXvVIuoN7RSxD88GRXu7gBlvKX4rVxwjsJtfkdLEYr BOB/IWo7SHqzcs4i1mXlS7u0svOWR0L1upZbyE6JRI1HeTOle1H7T26Khc3ZTSTy 0l3qsHQZpTgPvpf7rQwrEwAgUxdoefeYheFUdz3wX5GJWDV3s69B939IMrJcUPqa 0Vbs0DdhbbuOAmgKSOblTmTyaSflwlA2I2KYqrz+y6frvE9DopoEn8mHeVCZwgXR 5RUCggEBAMv2kXszsi3rgnlpFObRRnjyE5g5U3As1ii6FzUHm/aurUYqAZyMMnE+ RdKX1zBUfTUXqijSpl7avZrf7DVTWR42kiCJM7PM0lrcFUcYFCIs0x7Qk2j3YIpm 17vOFMpkZyYdWIniw3ihmWQTM3s3qTfdAbZPztnSInnjp+aXzU/tbfsmOmROcLY7 V0xYNWeMs+P1axoEC7zGmhCAaN/A+zDY34VLHGDrR4dVKCnFUQFoRwqfE6PtICFL 4fvdbbhwmU3sxY48y8ws/JyMfWachcFg/zbHVRR/K4j0sGfJB2NKu49jCcwPFaFT jLfytXEfbNWBGcgHySC4HUwnpUVxN44= -----END PRIVATE KEY----- s390-tools-2.38.0/rust/pv/tests/assets/cert/ibm_armonk.crl000066400000000000000000000023471502674226300233200ustar00rootroot00000000000000-----BEGIN X509 CRL----- MIIDdjCCAV4CAQEwDQYJKoZIhvcNAQENBQAwgcYxCzAJBgNVBAYTAlVTMTQwMgYD VQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9u MTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBv cmF0aW9uMREwDwYDVQQIDAhOZXcgWW9yazEPMA0GA1UEBwwGQXJtb25rMScwJQYD VQQLDB5JQk0gWiBIb3N0IEtleSBTaWduaW5nIFNlcnZpY2UXDTI0MDMxMTE1NTIz OVoYDzIzODgxMjIzMTU1MjM5WjA8MBMCAgIrFw0yNDAzMjAxNTUyMzlaMCUCFDIv LrSYUcq+W27wAZwp/0Pf7ydJFw0yNDAzMjAxNTUyMzlaoCMwITAfBgNVHSMEGDAW gBTDjB5dtNYBmKwPzqBlK6BnpXUU2TANBgkqhkiG9w0BAQ0FAAOCAgEAgvfpzZbh /utz4zq6/KQdFW25wK9tqPkwFut9vdsOAGMkRRcX699Rk3Km1TpgBVGRKAlsQKfS IMq1FtNwbKMv+ycZHXDewk/i7dd2A0eUNzbDxD/WJ7mBdUHJtzkvIKwBmU7SkwdM yBRA20H8xKioveDrms+MM3a4oLNRzkZI/FWkSqjCDh9lI6igZNt3J29vkcdIlu+P Maa7Cpwh1ELrQbwE3NVgbr172D9qzQ+/NiRlM/Hla/lqGELcUgwVgkNMBFEMo7iK XsWy9WQ+/0RExo7n09jk9D4tCyAizGae72Xai8IJQD4tSVNpv6ZtNHZBBKQhkjGy PXGM8tHC91Tr8E9CSVVOrtxVit5htxjnTkJLg/9XnoAkxGNbUFbvCm+zIOzkmlJ4 Drvvt9W4tVfkcfILHKmy4PG/0JU/tAygpr2Hk39e/Lcym64O9gz2g0pU4On/qD6e 91Q4822RIYLDK/IO3H30CbO25nBChZ4z6KBMWXHG8mTJ5m1IbwZTPxBlL8t47P9v 2ce2XDkcPzYpk/wqJ4xxQYhuhV9RaJreEnnqCZ9ER7L+heoJcCZcnr56wT4mNd/a Kfdm0Oxm8CFCMj1djVJQ7+ghsOY3RAehQ75m2Od5BRM+Vv3gYtkzPWTkWjDUn3us 2KwZvR+lNaIVXPlQqBlIWJvehWZexLzTkA0= -----END X509 CRL----- s390-tools-2.38.0/rust/pv/tests/assets/cert/ibm_armonk.crt000066400000000000000000000045061502674226300233270ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIGqzCCBJOgAwIBAgIUWYLchAxfdv8uD28Im9DK+Lo8pokwDQYJKoZIhvcNAQEL BQAwgb0xCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y azEPMA0GA1UEBwwGQXJtb25rMR4wHAYDVQQLDBVJQk0gWiBJbnRlcm1lZGlhdGUg Q0EwIBcNMjQwMzIxMTQ1MjM4WhgPMjM4ODEyMjMxNDUyMzhaMIHGMQswCQYDVQQG EwJVUzE0MDIGA1UECgwrSW50ZXJuYXRpb25hbCBCdXNpbmVzcyBNYWNoaW5lcyBD b3Jwb3JhdGlvbjE0MDIGA1UEAwwrSW50ZXJuYXRpb25hbCBCdXNpbmVzcyBNYWNo aW5lcyBDb3Jwb3JhdGlvbjERMA8GA1UECAwITmV3IFlvcmsxDzANBgNVBAcMBkFy bW9uazEnMCUGA1UECwweSUJNIFogSG9zdCBLZXkgU2lnbmluZyBTZXJ2aWNlMIIC IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxNqcFB9GSj+nC1PVOTIAvzTu o3X7bTbohEPrIDDsEcGyVzQtfagI9vkxhhGLhXxIXrkwEI4u75DeLBdqZR3KFJ3Q D+jHW12EWNpmSYu272CEAJeb/SBJaFbh40WbY1BsTP+3zI5QMwVTmCH5QqODD+GU 0fDSo9Gti0yX4rxOY+54jDC77AhSlU55rYruBoIXVe4CcPUpqzZLgqB2cTJ5g7lo kGqc6w9pveCznVYGPfcdDt+ePGssOvrfBOJnn6N44tTRAG623BrpctXit3IAhG60 y6CqF29BjV0RKOv09nOBiph2a3lAiKYrpAt92BETFB7KSQManUrrvWODmYEcoVkz vrVrB3WnCcHs6lsjQg1FcNrGcDzV9SmX0BIhM+fasxZyHWc08pr3NrRnEks7lRqY +TS8DAeZYJt/2M0Jr32d6Bh+WdyGFFUj6sBPtCaC2VnSkxOgKXeWf/c5EUXIQT2Y XEkNK9CP8Kqs8IerEIpfceelQReA5QcvNruaJNktWLKJgYTrdLdfOP0us+9JJHWc FH3tv5906CA+Tlm53Dk6SaRc/DB7lu94yTkcSOpxXZnximZi0GcPZYmZTaswE7d8 HCPLhks/RkZSV2764Kl08xle65APiWZ4dM24uSyu75Izb4hrwgIA4qyMdDVQnI3c u6utmKUPyHoJtH26dr0CAwEAAaOBlTCBkjAdBgNVHR8EFjAUMBKgEKAOhgxpbnRl cl9jYS5jcmwwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAww CgYIKwYBBQUHAwMwHwYDVR0jBBgwFoAU+f7h8O2ttBHtwI4OZnpD9wU09GYwHQYD VR0OBBYEFMOMHl201gGYrA/OoGUroGeldRTZMA0GCSqGSIb3DQEBCwUAA4ICAQC5 dH5QKZHynK7vGHOtol3brWgAWKkUVj+uqok/AUyGF5hkcXI2AVmu5nWV16Z6/c0b rD37YMPCCnZorQQg5g3c3H9In3NzTYWj1q2YT13yQ5PaD56vkwfPQKlmY2kMb/v8 Y5Ho2LFjhKKOpzP77CsYMs9ZdXs6VKGZ5dSmOAOvJ4AcGaRPs3jXVz3EZFgc+ytK 705mWrAgTYI3xcemxBTwILWAVCoqqirWrDNd3jicQo4Ks/H07RtLuVNY8kXUJEN9 UMG7Ggzc/rTlvV/PUsJaQl8lunPDdbUBLsXE1iWaaAxmRTTQaDX8Ygq8NFZgGSrk E/dnJXcnJyV/5GH22Ho4JVVtADkP1wh3TKcojiDfM2WlzatSOMPdeISsUQ/D+VSm GuSOxPkS0wj5XUpoJz2bKvXNMH1Mdp9sMfOlkMe47iTmU5gK4PJMoj2NZ7zW9u6p pYXQz9LhdwoTJZBJXVYSA1q+sIIy8u3vFXEeN88FuoY8yS8t8qjwGEcKP1oojFxV ibj9cv87Vcd+tgXAqwkjxGQVbDZPBlL6OWCYoEluqWGnbUD1mUp2y8K99ZlzoT44 i2ipEFyZmVPrmwgKRlz1UYDIsQvtvVw91Oi+DRNP1u+9D++Mnz2itGixHh1Hob25 nQl1/SS4PfgMlYscbHYOfLAzOnW02FvrJZgQAQUpgg== -----END CERTIFICATE----- s390-tools-2.38.0/rust/pv/tests/assets/cert/ibm_expired.crt000066400000000000000000000045121502674226300234750ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIGrzCCBJegAwIBAgIUU5pxAXMrXmWzioXpyoQoipFwsH4wDQYJKoZIhvcNAQEL BQAwgb0xCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y azEPMA0GA1UEBwwGQXJtb25rMR4wHAYDVQQLDBVJQk0gWiBJbnRlcm1lZGlhdGUg Q0EwHhcNMjIwMzIyMTU1MjM4WhcNMjMwMzIyMTU1MjM4WjCBzDELMAkGA1UEBhMC VVMxNDAyBgNVBAoMK0ludGVybmF0aW9uYWwgQnVzaW5lc3MgTWFjaGluZXMgQ29y cG9yYXRpb24xNDAyBgNVBAMMK0ludGVybmF0aW9uYWwgQnVzaW5lc3MgTWFjaGlu ZXMgQ29ycG9yYXRpb24xETAPBgNVBAgMCE5ldyBZb3JrMRUwEwYDVQQHDAxQb3Vn aGtlZXBzaWUxJzAlBgNVBAsMHklCTSBaIEhvc3QgS2V5IFNpZ25pbmcgU2Vydmlj ZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMTanBQfRko/pwtT1Tky AL807qN1+2026IRD6yAw7BHBslc0LX2oCPb5MYYRi4V8SF65MBCOLu+Q3iwXamUd yhSd0A/ox1tdhFjaZkmLtu9ghACXm/0gSWhW4eNFm2NQbEz/t8yOUDMFU5gh+UKj gw/hlNHw0qPRrYtMl+K8TmPueIwwu+wIUpVOea2K7gaCF1XuAnD1Kas2S4KgdnEy eYO5aJBqnOsPab3gs51WBj33HQ7fnjxrLDr63wTiZ5+jeOLU0QButtwa6XLV4rdy AIRutMugqhdvQY1dESjr9PZzgYqYdmt5QIimK6QLfdgRExQeykkDGp1K671jg5mB HKFZM761awd1pwnB7OpbI0INRXDaxnA81fUpl9ASITPn2rMWch1nNPKa9za0ZxJL O5UamPk0vAwHmWCbf9jNCa99negYflnchhRVI+rAT7QmgtlZ0pMToCl3ln/3ORFF yEE9mFxJDSvQj/CqrPCHqxCKX3HnpUEXgOUHLza7miTZLViyiYGE63S3Xzj9LrPv SSR1nBR97b+fdOggPk5Zudw5OkmkXPwwe5bveMk5HEjqcV2Z8YpmYtBnD2WJmU2r MBO3fBwjy4ZLP0ZGUldu+uCpdPMZXuuQD4lmeHTNuLksru+SM2+Ia8ICAOKsjHQ1 UJyN3LurrZilD8h6CbR9una9AgMBAAGjgZUwgZIwHQYDVR0fBBYwFDASoBCgDoYM aW50ZXJfY2EuY3JsMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMBMGA1Ud JQQMMAoGCCsGAQUFBwMDMB8GA1UdIwQYMBaAFPn+4fDtrbQR7cCODmZ6Q/cFNPRm MB0GA1UdDgQWBBTDjB5dtNYBmKwPzqBlK6BnpXUU2TANBgkqhkiG9w0BAQsFAAOC AgEAo+WGpxIz8s0UY8gOlCYeGr6SRYbgH7bcFVaV2fJf2eacv/YSaryc4PNwFnO4 +e/LvE8908xq57Kl0xpCzp4bO941/aJkJuyr59TOe1nhcH0z0Yik9Dt1zRxxy0XO uVUSYuYEmKlWNgsWAGCmcHAz+wQxn2qjpD8k/GAe8Rr7O7r/EnSt6HvVKt3dl+zx rv88gKKJKWrbn7zn4/c/E3XG9RxunCsYGtOfUfk8tGeWtemoGNmXlCLCmKR4JKjk mEEM16veHHdD5k9MPj6YQH/jh1pDOD11o8GlOd/rEQCpFWeg23Z0uEq5j+qr1FI7 Fjk4HvTtwGU7QXn+QV9bSjhE6svdWVbmADwb3Z/UU//UzgEAZDPb/n4cC95vk/Lt Wwq5MzHBIyeHladxzLE7LuVA9dmkv7dnST7L5ZAqQYcT5c7vxkrTHD53ZTojPMD2 Lmd3ZIcRIdFbPChY/+ajuBaNpi+gBwQaQ0J4b70fKW7oUxOpgSjh12ACNOYmVRWb sCzSiIaxkD0pvIYLgNXcY2CekuWxO+4DLGNGPPAHeovue0yRgNKTC14AHsLWh04c aWdomQToTHFxJcoVk2LiE5nwg5Xu8BoNuMag1jl/ZQVsl4LrL4bItwplWdH/bSyi 9VFeSwPGTiNverBtv/aF4q7O6JWB7/zUh2GqYQcILcaIy4E= -----END CERTIFICATE----- s390-tools-2.38.0/rust/pv/tests/assets/cert/ibm_invalid_hash.crl000066400000000000000000000022761502674226300244630ustar00rootroot00000000000000-----BEGIN X509 CRL----- MIIDVzCCAT8CAQEwDQYJKoZIhvcNAQENBQAwgcwxCzAJBgNVBAYTAlVTMTQwMgYD VQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9u MTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBv cmF0aW9uMREwDwYDVQQIDAhOZXcgWW9yazEVMBMGA1UEBwwMUG91Z2hrZWVwc2ll MScwJQYDVQQLDB5JQk0gWiBIb3N0IEtleSBTaWduaW5nIFNlcnZpY2UXDTI0MDMx MTE1NTIzOVoYDzIzODgxMjIzMTU1MjM5WjA8MBMCAgIrFw0yNDAzMjAxNTUyMzla MCUCFAUi4bhk78T24IBu9CEi9kegwxmXFw0yNDAzMjAxNTUyMzlaMA0GCSqGSIb3 DQEBDQUAA4ICAQCH2paQrJTqVzDjBOixREnhV0Ned2SHHwuIxZPeA1LrA1MR8KeY RCNiBxaOg2fkIe7WbPbiFVmosRq1kMjOJrgajvI6CG4is5F37KhW+Q/8siNcc8y3 oiu+b8O5wp7vfUq9f4bxh78ytS+cHJpxHLoYzlp1f6aha2x2kUqDQzEf3ghIXEAN y0Jlmn2OqeZ/cCnWG+QF8Yb9OMlnR6rTpwk4ml0TvMa1/LhBUMj7vva0BmXuTunW eEr/wxmTMyJsRKNXgqRDLil+hnNzIP7HOmofyad9dyHv73gOkmu1GizwSB/AEVoB J5N/LUeCh4cG3YJYYpw7HaAHUnka2SDiuL3MD7VD5ONXiwcZIplT7I9vJHaBimoz kUTr3BkzFHas+KiPuGLm25Pxc0G8mVZl0jzoIJe0JS4bi+NCqeQ3C5s4WOZ476Bs 5PSWbkJ8GwUxtXciOxHVKvgnBkc55bng2VEJlRLDfERdcTa1yobvJBRNZ8J/gDu5 aWvRJs+cQ+1Bj4oq3ejV+Be5UFEVEFfQd4wFixvZcr781aDhYg744Ig0n3oZE0pg ye9V55q5yra6K1eertb63y1WvIrpfvNBM5p7mFbYBc5vnoH44mAhwbsCL5Mkjcxp 0XjPSUsE9OyORtN92YLrZztEXIZmjw7otzQCQmcj/0ri3X+M1RLUTh+BVQ== -----END X509 CRL----- s390-tools-2.38.0/rust/pv/tests/assets/cert/ibm_outdated_early.crl000066400000000000000000000022361502674226300250330ustar00rootroot00000000000000-----BEGIN X509 CRL----- MIIDQDCCASgCAQEwDQYJKoZIhvcNAQENBQAwgcwxCzAJBgNVBAYTAlVTMTQwMgYD VQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9u MTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBv cmF0aW9uMREwDwYDVQQIDAhOZXcgWW9yazEVMBMGA1UEBwwMUG91Z2hrZWVwc2ll MScwJQYDVQQLDB5JQk0gWiBIb3N0IEtleSBTaWduaW5nIFNlcnZpY2UYDzMwMjMw NzIzMTU1MjM5WhgPMzAyNDA3MjIxNTUyMzlaoCMwITAfBgNVHSMEGDAWgBTDjB5d tNYBmKwPzqBlK6BnpXUU2TANBgkqhkiG9w0BAQ0FAAOCAgEATxMm63G11oaOvRm9 nb20MXyXaqejudryJLcKSAUNNcvVpfj4zO4iG7Eiipq5svrEHmb8aNELKb7eHVY0 uT5V1hd3xELkQqVS8rxcD2rQfoE8B3kkeuKtgI0yxC4rkrt8recajh8au/FSoknq Ts1CKsc0ghFo01gIv4fgWC7eWFrfw51T/bZW4AN1QsAT0wEqcYjuDTZpwXU+8JsB czQ1wWoKJPCAbh6ZOd8R/DEywffWSVjS+8hrmIc6TAwfu7SOBSvDLh7kKZeEgZ6Y lwp1R9EcRBYLlyclg4E7w/WL2DY426Lo+0aSIWcFwGbU3kWwFNXCKIpYvVQZ4QPT 2lZfuRVMe1ZgRdPRz5wkiEYYIGFoe3cOC16jHGi+Vs9JQX/EgoRiy/YYxZdckuxA 961S5h4bbYyB9aNH1vNn9Kp2r8oDGD6xucXM9cTXDsXvLLSFUxcFqoOdixYqFzX+ EsZM5A33pMq5zKLkRD8JI2lP7nT0wdyaDjuH4ION7y90tJiq1Q1GPfmJ6+mp/3/I QF1Mh8axL59Rgz/KrzszP4+kn3NuhvKQOjL3QDo5TSXEmsdjERFHsmOuTwB9E4iH EIBu7bz9GfVMDUL8LSOK8Tq9Gj9Zzy8kYe2BuwNOTTDzQAUnPZK/m6p1wm/K3vjL 2pjdFJmfRBtPNSRXL2hY2MYdZDk= -----END X509 CRL----- s390-tools-2.38.0/rust/pv/tests/assets/cert/ibm_outdated_late.crl000066400000000000000000000022321502674226300246400ustar00rootroot00000000000000-----BEGIN X509 CRL----- MIIDPDCCASQCAQEwDQYJKoZIhvcNAQENBQAwgcwxCzAJBgNVBAYTAlVTMTQwMgYD VQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9u MTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBv cmF0aW9uMREwDwYDVQQIDAhOZXcgWW9yazEVMBMGA1UEBwwMUG91Z2hrZWVwc2ll MScwJQYDVQQLDB5JQk0gWiBIb3N0IEtleSBTaWduaW5nIFNlcnZpY2UXDTIyMDMy MjE1NTIzOVoXDTIzMDMyMjE1NTIzOVqgIzAhMB8GA1UdIwQYMBaAFMOMHl201gGY rA/OoGUroGeldRTZMA0GCSqGSIb3DQEBDQUAA4ICAQAGD8ryV/GUC+s4qfqchMZA QYYOBcV7lS9i8zFrdB7UJuhCL/gXzrmldFsi6hW95PBNBtADT75UQ4JzNNVKYXWe wTbEOG54N7Ff4LdbbCRNjqpyOkqQgRWZOgiTeHSeiNLU+P98HZVKFIfiOS6Rs6zB 5UNdYwCGz9kkeiR7xSpp0z86jI8WSxHR6e7CH78Ax/9dpGAyKYYj8LY0l7igxlIL yRgu0S81VsprcHKuGp5wcnabOWO7lEUCTFbYa7Cgc1+avUl1vaCmMqUsvydurCU+ BCN8Zhc7noOXm0AE+58r9yy30aW9n+NUmP7uX6Eibb5NXtVZRVe83Ltk2Rbi9HRM hkcO0X37mXu+jcqw714i/r0mUsfGtC54IRMCgyIgWJ3TDdJ9ORo0rkrYRXVzKHdb 0nqXEsrYecaCNcnx8iygC2YEwAN9WH8cvvLTJQcL+j57xcJVqOSbhXTEE5LfiHpa DO+dc9A+THZNO+o8GEZ3teTxylOVctiO9CkUkICmVZ9VDA73qOzHI8DJxpCAwT0I whm1QfCROAXYFW9enTuCGDZGPoyTf+tVrAGLmCWLewBV6W3z+7OBfNomktuI4Z0+ opvwIJwYX3emeZ/5gDuYmwnvNYkC0GRQ/994430b/4Rssv32SCjToJ+Ko5pz4x18 YUtbQcKxpJzRDSkccck83A== -----END X509 CRL----- s390-tools-2.38.0/rust/pv/tests/assets/cert/ibm_rev.crt000066400000000000000000000045161502674226300226350ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIGsTCCBJmgAwIBAgIUXvZ6XWXXrTAFn66B61xZNya2iYMwDQYJKoZIhvcNAQEL BQAwgb0xCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y azEPMA0GA1UEBwwGQXJtb25rMR4wHAYDVQQLDBVJQk0gWiBJbnRlcm1lZGlhdGUg Q0EwIBcNMjQwMzIxMTQ1MjM4WhgPMjM4ODEyMjMxNDUyMzhaMIHMMQswCQYDVQQG EwJVUzE0MDIGA1UECgwrSW50ZXJuYXRpb25hbCBCdXNpbmVzcyBNYWNoaW5lcyBD b3Jwb3JhdGlvbjE0MDIGA1UEAwwrSW50ZXJuYXRpb25hbCBCdXNpbmVzcyBNYWNo aW5lcyBDb3Jwb3JhdGlvbjERMA8GA1UECAwITmV3IFlvcmsxFTATBgNVBAcMDFBv dWdoa2VlcHNpZTEnMCUGA1UECwweSUJNIFogSG9zdCBLZXkgU2lnbmluZyBTZXJ2 aWNlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxNqcFB9GSj+nC1PV OTIAvzTuo3X7bTbohEPrIDDsEcGyVzQtfagI9vkxhhGLhXxIXrkwEI4u75DeLBdq ZR3KFJ3QD+jHW12EWNpmSYu272CEAJeb/SBJaFbh40WbY1BsTP+3zI5QMwVTmCH5 QqODD+GU0fDSo9Gti0yX4rxOY+54jDC77AhSlU55rYruBoIXVe4CcPUpqzZLgqB2 cTJ5g7lokGqc6w9pveCznVYGPfcdDt+ePGssOvrfBOJnn6N44tTRAG623BrpctXi t3IAhG60y6CqF29BjV0RKOv09nOBiph2a3lAiKYrpAt92BETFB7KSQManUrrvWOD mYEcoVkzvrVrB3WnCcHs6lsjQg1FcNrGcDzV9SmX0BIhM+fasxZyHWc08pr3NrRn Eks7lRqY+TS8DAeZYJt/2M0Jr32d6Bh+WdyGFFUj6sBPtCaC2VnSkxOgKXeWf/c5 EUXIQT2YXEkNK9CP8Kqs8IerEIpfceelQReA5QcvNruaJNktWLKJgYTrdLdfOP0u s+9JJHWcFH3tv5906CA+Tlm53Dk6SaRc/DB7lu94yTkcSOpxXZnximZi0GcPZYmZ TaswE7d8HCPLhks/RkZSV2764Kl08xle65APiWZ4dM24uSyu75Izb4hrwgIA4qyM dDVQnI3cu6utmKUPyHoJtH26dr0CAwEAAaOBlTCBkjAdBgNVHR8EFjAUMBKgEKAO hgxpbnRlcl9jYS5jcmwwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwEwYD VR0lBAwwCgYIKwYBBQUHAwMwHwYDVR0jBBgwFoAU+f7h8O2ttBHtwI4OZnpD9wU0 9GYwHQYDVR0OBBYEFMOMHl201gGYrA/OoGUroGeldRTZMA0GCSqGSIb3DQEBCwUA A4ICAQBlx15n00tEAP8moDUehCtLLJx7CCG/V7gcldTZACXpOYkcuRcKW9GlXqQv N30rTZpIZv3XIQEhhWhMJ3dqJRC0l2CbNlrIcM9t5p7GrYS/I/HGamKIU09jfud2 1/FimjvBOQKr583vTLL0kr3Aosd3S8jHGA7Clal/85SntQN0kDxnceo01aCUhnxg Lkrd2+N0wPYGW5DZR6jk4Y/GiOO+q/ANO+tm4szT8RNwC5sNectpaI+ZRNlUHCdM DtZO5HkBADQ7PdZ2x51gliS5l9w7obFY62TG6LgtKFJgqEhqsHMIp+/OwmKbpP/p urUDJFCZGWD+lBkaxyy+VsPlvddU7gnXSm0wXoCxpXerRwWFb9/Kc5Q+kQB4nCkv bHm/zAkhjoRSjWYPcLL3F/9P858W9QlkhNdcoq73EIuc3FHUMdQUm+rjxGxfO76h fXoR2uBGlESO+gKL2iC9E7KKB+o07hdmafEnE8mGTRcGmVPp8e5SS7LfN8lfHez+ 3gBo2/lW1+2wBmBNIeBkkBpUI69DmNZvMliGNl/3LAIR/slQ6ZYXR/2dLeyhqwRt QSj5e6Jw2fg3ekG11R98OGoP3ZC/N1dTTh82O571ZBGxIWA6zPDrWj7tX0oW+JK0 Of23BiH+4sex36HshqiyZ/faq4AtO62uII5mRQzlAwE2QdXI1g== -----END CERTIFICATE----- s390-tools-2.38.0/rust/pv/tests/assets/cert/ibm_wrong_issuer.crl000066400000000000000000000021251502674226300245510ustar00rootroot00000000000000-----BEGIN X509 CRL----- MIIDCTCB8gIBATANBgkqhkiG9w0BAQ0FADCBvTELMAkGA1UEBhMCVVMxNDAyBgNV BAoMK0ludGVybmF0aW9uYWwgQnVzaW5lc3MgTWFjaGluZXMgQ29ycG9yYXRpb24x NDAyBgNVBAMMK0ludGVybmF0aW9uYWwgQnVzaW5lc3MgTWFjaGluZXMgQ29ycG9y YXRpb24xETAPBgNVBAgMCE5ldyBZb3JrMQ8wDQYDVQQHDAZBcm1vbmsxHjAcBgNV BAsMFUlCTSBaIEludGVybWVkaWF0ZSBDQRcNMjQwMzExMTU1MjM5WhgPMjM4ODEy MjMxNTUyMzlaMA0GCSqGSIb3DQEBDQUAA4ICAQC1oMFXyJ2mAz0el6drt4KtH38d FfDvsjc6hTgiPbmQW4NmLuSKnNLOyOulsRaV7Kl7hxQpd+Gyimqbtb1ObeqLvnyb W5kX90k94l8M/laZT1mZm39AGR8xW8HDtuJgwliI7RujX6iWuyrJCxM4qpAHe0Jw GGQd7ZlzEZ/4xMsZV0lPSsA9CPUn4HyZ2FWj3DJjfXloEOu+krcpmvmL3EROUi/h F6bsViKRmoQoe5GJFLdG4eVSgc9+ejaM23n8C0tmAhz7FKOYOqU7pS9+hvCyJ2Ul c8S3V7hzTlz1G4AJAeSJtCnV/hkpt+yj0eQa0Q92I0NZY3UWT8dOMZUxpHO91Th5 4nfaF6FVSO/AQcpr4FPElYObp+RigqWf44v3vrriHTYi9QntKaNZq5XLgx8EkgBx 1dTM7mDJ9W21ZWGEbRtLRaKt71RVsVKVLh5CPQrFEzENOTuy66QD26LNC2BfE3TJ tsMgjm14Fm1mC91roj7sOsO3MUl4YKh6cdKFtfryWr5Du+7x9/6vjabuay/BopC9 BF/q8ITHh5Shs0h9OpOeunGFbcTnYOSgOpDLwC4++hur8p0BzClJkt2Z4yZGuKe+ lh66PA/UkHtC3E8NDxVRkd+NsejSJUzRR0+1ruEoDEGP4jZKL83UpOFjIWGlIBsM 2gwwqV/LJsywfW1kFg== -----END X509 CRL----- s390-tools-2.38.0/rust/pv/tests/assets/cert/ibm_wrong_subject.crl000066400000000000000000000022661502674226300247040ustar00rootroot00000000000000-----BEGIN X509 CRL----- MIIDUjCCAToCAQEwDQYJKoZIhvcNAQENBQAwgckxCzAJBgNVBAYTAlVTMTQwMgYD VQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9u MTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBv cmF0aW9uMREwDwYDVQQIDAhOZXcgWW9yazEVMBMGA1UEBwwMUG91Z2hrZWVwc2ll MSQwIgYDVQQLDBtLZXkgU2lnbmluZyBTZXJ2aWNlIEludmFsaWQXDTI0MDMxMTE1 NTIzOVoYDzIzODgxMjIzMTU1MjM5WjAVMBMCAgIrFw0yNDAzMjAxNTUyMzlaoCMw ITAfBgNVHSMEGDAWgBTXVfLQH2D0KccIHhK6M5MRTITvtDANBgkqhkiG9w0BAQ0F AAOCAgEAkHC9VxzSFGrY4NWoMLkunjaRQ5qmc4+3PFDiZLaGQua5QMJixMo+QIp4 RfCLAkvI4VzMdoJXhl4+sV0mocn1HWDBxFUwfsvV4h84o8bEUK+bizrFsEoN58R0 J3yfbikgj4P7WFQdU7p7bbAkmoKejt5+gu5etV2royIh2mjckCN7WnYzvhfRUBYz v+93R1usMMQIVId6l6k4DuBqaFyip8AKXVoXj4KbmwXZ8n+ILd5gWV3WKYo/ffCh h2g+jaS8JS8CYqtVGEb7F0zQwdPe1tHA/SBFBCNVqzHos+yET1m0Cn1zoYPvkEu9 U2OVK5tqffEyrkN8hRm7LT2NIYji/z4VOIY/sGA1SPO7HNzoheAyHR7u9ortGMYv 2Q69SEqtA7N6kMX3y/dL95sI/E3EJp5Z27jMMP7+aJ53F/5xmIqkZarA0ETuBic/ KlNjG0WJilUMH7D7emTxksTdZOC8OKjg6gPY5GhVmlQ/LRvJQtoXqC84UQV2ul8R chvU55EUB67247g/WAusCjutISXnKQ4RGPaH9eOu1yN7StiXtJyiZ8R14ZihhC82 ZzxjfDDk6ATR87bkAeDYJvubLJkVtUyUK0vgklNsyA+hwbdr5E5Q7IaRW2Bk/tnt K9ANhMjaerwcR20aOmA7pVuMGXU2MhFIleQzl+Yfv8N1CDECw3Y= -----END X509 CRL----- s390-tools-2.38.0/rust/pv/tests/assets/cert/ibm_wrong_subject.crt000066400000000000000000000045121502674226300247100ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIGrjCCBJagAwIBAgIUOJ2eXoc9SJ1pRCC4x452LX35eNowDQYJKoZIhvcNAQEL BQAwgb0xCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y azEPMA0GA1UEBwwGQXJtb25rMR4wHAYDVQQLDBVJQk0gWiBJbnRlcm1lZGlhdGUg Q0EwIBcNMjQwMzIxMTQ1MjM5WhgPMjM4ODEyMjMxNDUyMzlaMIHJMQswCQYDVQQG EwJVUzE0MDIGA1UECgwrSW50ZXJuYXRpb25hbCBCdXNpbmVzcyBNYWNoaW5lcyBD b3Jwb3JhdGlvbjE0MDIGA1UEAwwrSW50ZXJuYXRpb25hbCBCdXNpbmVzcyBNYWNo aW5lcyBDb3Jwb3JhdGlvbjERMA8GA1UECAwITmV3IFlvcmsxFTATBgNVBAcMDFBv dWdoa2VlcHNpZTEkMCIGA1UECwwbS2V5IFNpZ25pbmcgU2VydmljZSBJbnZhbGlk MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAw7LcEUPdhr0FKp+muH7w Bj5Mbktl4e4kpIMqc2OaTxtwM4RDUDpLwy9X4PuEpGYEgOwOQYyd6udfInpjVVTy JBnwxdfpgHfsdqamkrZDqjGWHeSw4332D8gGMNFLmbCuHEzVmr1XLYQFVYMTuWex VFvL9ctA4gv5xO9f6oL7fVE7X420qb7IzntrxJHKsQA/IheJkZHYvPpBLs9Xlwje tb5OwyhdbTIsfR4V3vHdQ3shwD9TFbc+6MTDryWZThlDDUA8f+iO+euAe6QjLDnL JgLwnXd4EmGyePDhBM8oSex5o3/vzJVmJkpb40pvloOG11qlXGaqJxOLH2jjsNKx iC4l7oAHP0tXIw5f8hn+pk5vQNIcIHguXIT65ZR0IFRE9tjciLOtxndEn976jmqN Qu6Ajx5DiImwzp4wHjJOoVWmKCM9BklFRhwJvZ2xTmBGvC20jX0OgEaCeWV+E7UK 2pzfGwwkWdXC7ihIszhGNP/lLlE3IflS0MHSxtie5fUrwuoZ+bVkKYClkLYeKrBT MLfszTuWg+rJfGmCwvYWGG9gcIRc1T3EuOiNpN4YJ5HZyD4rSI82fSzbrAtAPWPk 6iVdmo9Ban0pQhMzyH2Gu07OnvBpmH2ADLzFpugjWafW157//GCNVgjrKVN/JbuC 0nne/OTbQgCO1mFOy6RI1RcCAwEAAaOBlTCBkjAdBgNVHR8EFjAUMBKgEKAOhgxp bnRlcl9jYS5jcmwwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0l BAwwCgYIKwYBBQUHAwMwHwYDVR0jBBgwFoAU+f7h8O2ttBHtwI4OZnpD9wU09GYw HQYDVR0OBBYEFNdV8tAfYPQpxwgeErozkxFMhO+0MA0GCSqGSIb3DQEBCwUAA4IC AQATY32D+AMy1/x1WLGbQcvEkcT5+UEGp82FEVamXy4eM+zctzemEufjSszEpRod N5UgiNGDjuD5ttZSc37XHwtOwqZv2V0tu2jclxQQCswPjLhkecpIwSFJW9Y7IQPo +4FKry6q+vyo0sLA+pIhTT+OHK8ijy9PQEx9YOHgr0HTTTG8UZ/6oqx70lvx9KcA x9PhFb5TWaOhOds+v7NEPxslwrMRQ4jOz92bGeBjSXBzxwohoKKWcLWIy5ktJsFS PhVztHa+on4AXdDJVifU4lYIPTRZBX491EnIpEaZtyEED319SNlj691+5SgDNHRw Ysk+xcVYy8RmL84qfTNce+t2eEr0vjnkUqVNt6ps3UvkK2jvlehBlfPW/Dy/r/Er q1KNvc1Uiza3ryf2dAMDWL2VBIKMZb+d88cEuLHIgmCXrxtcyxCBTWCK6t4cnKsQ J82B4XQ9IRCCJDTjGEQLxsjYt9jwHeUIK0l/sseBeohxMfDptfRQ8hWt7c5SvvNy xRGJ1adli7LiJ3+gnPcyO+D+3DPtKtgY4y8Aed5P3oDOQGNdUznSP2qexUpT9GO0 fJaRI8Bc3Kj1zLkS+mtYHeOBu5eyLhR0fSBpKr3/3RoKj2/NI3SvlBbJ6dD5jQBs 4qEi8T70wjhDqITPyqZV4auy/0/h89JXDB/qAg162p8EJA== -----END CERTIFICATE----- s390-tools-2.38.0/rust/pv/tests/assets/cert/ibm_wrong_subject.key000066400000000000000000000063101502674226300247060ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDDstwRQ92GvQUq n6a4fvAGPkxuS2Xh7iSkgypzY5pPG3AzhENQOkvDL1fg+4SkZgSA7A5BjJ3q518i emNVVPIkGfDF1+mAd+x2pqaStkOqMZYd5LDjffYPyAYw0UuZsK4cTNWavVcthAVV gxO5Z7FUW8v1y0DiC/nE71/qgvt9UTtfjbSpvsjOe2vEkcqxAD8iF4mRkdi8+kEu z1eXCN61vk7DKF1tMix9HhXe8d1DeyHAP1MVtz7oxMOvJZlOGUMNQDx/6I7564B7 pCMsOcsmAvCdd3gSYbJ48OEEzyhJ7Hmjf+/MlWYmSlvjSm+Wg4bXWqVcZqonE4sf aOOw0rGILiXugAc/S1cjDl/yGf6mTm9A0hwgeC5chPrllHQgVET22NyIs63Gd0Sf 3vqOao1C7oCPHkOIibDOnjAeMk6hVaYoIz0GSUVGHAm9nbFOYEa8LbSNfQ6ARoJ5 ZX4TtQranN8bDCRZ1cLuKEizOEY0/+UuUTch+VLQwdLG2J7l9SvC6hn5tWQpgKWQ th4qsFMwt+zNO5aD6sl8aYLC9hYYb2BwhFzVPcS46I2k3hgnkdnIPitIjzZ9LNus C0A9Y+TqJV2aj0FqfSlCEzPIfYa7Ts6e8GmYfYAMvMWm6CNZp9bXnv/8YI1WCOsp U38lu4LSed785NtCAI7WYU7LpEjVFwIDAQABAoICAAqhxnv3pGrkDQpORygB2Xd1 XgCl/wCByCLZ7236bNE8a+GYn3GV4TTW9x7Fe2TVGAyLBpFAGvo+nLgKTyg9J7SX ZjHRc6Gjokil8CnLVizCaeXw3T1WxA4Cb3eqf0F3zFXERNyVyc0yvXlyWBl8DTHI lPGjG8DMJsMwwBTFDfW2epPL5pNMRquVH/s7cnggS83F2pb6hfxqWi05XYhaloLo Nm463KyIi8s2XbjRihRW9bP0nMZywKuzuO/kioooLDDlmwPV9iKUzVOqTLDj6OoF Qd5ENdVF0oTojUkOGiG+A7PCyRvjx/tvkcNs8VgLiEFd7trwxvC9ipLnx2r9X2Cu zi8iQJ901lz6cj5tR5VBttM2MaLFtELTa4HDmVMf6cNuMNZO8eULNmV2EX5K/L5b JD9Y1fhctQr2+pEvhgela+hi4XC/jZ/HBxDg2gg1lzt7MNjODi/fISz/zGexGQW0 R9KElISntcpsyG8bqH2LQFQ7XzgAF1qrUC8Z9KFONGRGGZViH/K/aeH00PqCEwG/ kv5MH+JgS+Rb2AM6HT6SK4rchAkpINPDIsD//dh4vdEfPQ/ysICf3yqACSYgu0cl kaTyNQE/Oga1tDOAbC8YlxPRmFHCybz8gXG4iyfFaXLGaZB+UXwZCMKOBsWbid4v l+1wRY/EcEuxgU6itVv9AoIBAQDo38kPbhvcT7I56++EKVuEfCzwAnXV/kttXRQk QQP9b7C6ZVsEgHz9/sNWzwrp0+28JXMZIggAt0kE783hitDlh7mRIUqjYoU6FcKs J3qaHR/Dn5TeDFlMeXmbDOsXPLRDTFdvX9+We6gbAUY1NjbK/CS+fOIigzw/jplS DIPxpsPLQ79/lUvr842Fn9HAIoPWq6bhMW4qibh8clJInMO9VTz4TF0FnmkPgl3Y R63h5zngQiOAtWCIleN/9F4G7VvrDqYHAHX54vL5Vz+dgtsynJIcpQlIJ5Z6w4hU q04aHuxDBkNPNRfTUIo/Jb1ghxK0m8JEZb9FE5TmZ2PDH0l9AoIBAQDXIfwjSYez fahZtDCis9skiaRBz2Vw+EUruXyEShER2mPaW2Of/cxAjE+cRPC4aGuseHs+4TWR CMnLU8lsrrw80HhdRjd11Gvr/o5qlqgxnw3zNSWQzwld+QcyxpzOgIxIR4kFV+1z VoG5PO+vARhcZH2tXrrWdjnCTchaMV74Sffb8bHD0p0PfcAg3CdSzMhynnMGp3wN PBZXqk0T+C9isaXKxL8HcDJxgcJHEyEZp9ygQtzQo1BPluVTWPJRQQvP6VLNyNl2 NISvmYJSwRQ3yuqEvWE4eSge9kaNp2eUrUz5O3t1NyOa4qioVPnlCH7Glaj6nyNS Ler2dszcqz0jAoIBAQCnY9SeZsNYBWFTCSjNkvzZqniSvPH+tB97qSBFPwajMZmT Ii/eeI1f9bRrvb9WfKOyTikBs9iUyyqNheIzcRjfJERa8dc4wiSJsAKSxH34MV8X uqnDQpUdx2OF9C84MMZSaZmf0QZioNghMTVKIoIYPk4bLqFNtY+lD9ddhumA1iJ/ BV+tUZ+VJyhfGJYoyaaCtDfsPx+1K/GUYoiK8UQx4AdHY3yqAUf7gfX69OskKpUL gf8LEVUmWLeCziCYUh10RL9K4SXsTRnh6Lkte1Ycdzb/qBGX7/zWmZ5xXgFx/TBO rT3MvZk2p/n3kiUiMXVcpWlqyMhH2t95DnBDXUXxAoIBABcS0Q0j1MceghDk75Zz vdxEWvan/NRJ/Hk9EqrJmt0UVENWK/A78000/1DeYAcXQ/0iiu1qkCk3DRWererX Lt9C6LXwUwBzQQP1sGakM/PmgEOGfrnySqnmjKwYezb2uJUD/yEwlgoZzB4G+BoH /wnhyUzv7RAVbAp39zYdN3dfz5KqcIt1Kl0/+nxLwHfz+Me6UNH04qw0tpy+ajfr FYH7VbHSuj5c/TwJU8H1vkYXJ+WUZkSGT5XJtFzlHFA0rsSk3LmvggtYhKaky0J+ 7OEzBUYpXaUF8ZSoi1akDsr2b1wH0iz3Nf1ls/sh6g9zgs1fvdjreolU3W+DvGMq hosCggEAWvrHup9KFfS7QznGOLjOdil+HT8TqbVjFa51S7gd5A2B3XpC2mDk9XPO uxvUjXxuH18hRlLHP2uERlIsOl37iPJ2jY4C1SUQb9xNmSarfUKwsgDhJKBfPNIC LXmXQgs/gcL+Sm/l6Az4slsV/9xg1Sp4EG/3bBM8i4VRRcpKZqwttZ/pArzYNrYp LbZeRCm34iPDAelganc4cX/wzlXa36j0Eh0PHd1SvQhKb9lcxtbV4EFhxD9qC1VF URy6m7FJrA0r5Hrh4SuSagOpbPCJTeXP0Ysvx/tsl7DR1kIpGkMGhYHMScbKjGwe ZAbU3ynucdvh2dgqN4agS//dmuRo0A== -----END PRIVATE KEY----- s390-tools-2.38.0/rust/pv/tests/assets/cert/inter_ca.chained.crt000066400000000000000000000067541502674226300243760ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIGhjCCBG6gAwIBAgIUOkSdvHg4/HXtknNMhIFkuPv/ghMwDQYJKoZIhvcNAQEL BQAwgbUxCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y azEPMA0GA1UEBwwGQXJtb25rMRYwFAYDVQQLDA1JQk0gWiBSb290IENBMCAXDTI0 MDMyMTE0NTIzOFoYDzIzODgxMjIzMTQ1MjM4WjCBvTELMAkGA1UEBhMCVVMxNDAy BgNVBAoMK0ludGVybmF0aW9uYWwgQnVzaW5lc3MgTWFjaGluZXMgQ29ycG9yYXRp b24xNDAyBgNVBAMMK0ludGVybmF0aW9uYWwgQnVzaW5lc3MgTWFjaGluZXMgQ29y cG9yYXRpb24xETAPBgNVBAgMCE5ldyBZb3JrMQ8wDQYDVQQHDAZBcm1vbmsxHjAc BgNVBAsMFUlCTSBaIEludGVybWVkaWF0ZSBDQTCCAiIwDQYJKoZIhvcNAQEBBQAD ggIPADCCAgoCggIBAMheyYWl/STLJ0iwlrqNRyURatdeC8oDpKFdpglYHAs/jo3s fWNySCnaw6NCe0vxFLpqcK8VMNFRGu/XhR/kZ1YR3V4mLwF1Wa5v7a7J9swq50Fk CsLtaU5vq/h6rIpy0NLnmN5KgqChrMh9IwZ+Mc8sqc/0BFFJsuCCGu0TNlGVOhmN AbdS3s7wEUwT023CKn47G3pVqeaErEB9honz1I71g5/jNKGe5CLCV35ExzsrzU43 atyJ0jgh15PYCXDTdsRccSmEs2S6Xh2o4ZhlqioWB+tKxGsdxq8Ri4soy6yyooOz T/3X5CHpKxiI2P9z38Pr9egPcNPPVMGDhzwHz7p3iBPg0RcWd5VP2nimLJsdGWK0 bkU7zlQ3R2NelSIW9Hr8MVASihmELvX+AcC6KhTpHHhf3CTPgcAfV2fE9U84Xl2i shmoEsUQTUx97qKUOKRfY6o+WMBnVkzlqWj+s52ndiT+0KNLTDtvlejEFf1VSF43 IkS9UJK+XxxEnvIBzNKI5EbWlG5Z38/nKv6pjTXFi3aZR4cdmI/0XfAjLTkrTBaW lkguEjt+/cxPYJOqt50ldI9kle8XTu/HibmcbU2wYIF21CBjE/hLk/KY3FQlnTFL y7x3bM0CuTfJ8Noy59f3l56fwPOpaQWqhqO+UoFkbFlbROAiVxfb8KlyZPdNAgMB AAGjgYEwfzAcBgNVHR8EFTATMBGgD6ANhgtyb290X2NhLmNybDAPBgNVHRMBAf8E BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBTWntryJpCwZqZJ1H3n znJYhbfoKjAdBgNVHQ4EFgQU+f7h8O2ttBHtwI4OZnpD9wU09GYwDQYJKoZIhvcN AQELBQADggIBAE8gulfly5+EC8DX3K02qEYPoQwVbVhD0wGrlAhgJiakDvPlX6/K vSe/1nNRG87jXvXdDiuJ6F4iKZpeJndzvx/8ZEmllyyxDwb3UOmylwW/o3/Uh6fY kiVBfW6uNNB0BfDKcXDDZgKjTg3kLT5z8m4u8rPoIPFkLFl9AuAq82Ll6NQ+xZFP lZ3K6HN+ntVnIGP4XkOgEYPxjJO3yTGle8VBqLfo/JKwbZtKfNXxSAMRXiP02SQg D9yshxkonQYWog2hHz8oDuQQNbzaAFlxnY914av/XwxP8TwEfGNchNtAtrlGRlx8 PjMfp3Mnbz71yp+L2We2/A7njIPbEcn0FIBedpNyyBON5Cd6Xqx18otmMTtUILZ3 SUKeYmLp8soVMEmmnWz6y1a4bCKwo6hA8oSoq5ydIeWy/jI9v7DF0S/qZTz3c2Q1 a3aniuug2FRAxuU/8fSlMrE4672d3505SbHUblhy9XzQ4+sWjkDtYnY89kyY4BTu W5n6JlVoewZGOjlJ9/6mV6BVLQ74IeiytWtdH5uOQ1wroi5Kq+EroGgFmPSQCIvN XNDvBCNFN/O1+/eVZd6JNx6NMO7DrWql3GFMtJE8u/SWXA9vit3pfmeDrosZ4SDg ZPt7+JfzITUh2UMaPsQsPF8G8/tYmxDbELE5LjFcQ24ps362rG+q2rfA -----END CERTIFICATE----- -----BEGIN X509 CRL----- MIIDbTCCAVUCAQEwDQYJKoZIhvcNAQENBQAwgb0xCzAJBgNVBAYTAlVTMTQwMgYD VQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9u MTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBv cmF0aW9uMREwDwYDVQQIDAhOZXcgWW9yazEPMA0GA1UEBwwGQXJtb25rMR4wHAYD VQQLDBVJQk0gWiBJbnRlcm1lZGlhdGUgQ0EXDTI0MDMxMTE1NTIzOFoYDzIzODgx MjIzMTU1MjM4WjA8MBMCAgG8Fw0yNDAzMjAxNTUyMzhaMCUCFF72el1l160wBZ+u getcWTcmtomDFw0yNDAzMjAxNTUyMzhaoCMwITAfBgNVHSMEGDAWgBT5/uHw7a20 Ee3Ajg5mekP3BTT0ZjANBgkqhkiG9w0BAQ0FAAOCAgEAbGRxfJj3wsZ9iUsYTO3W 7+hNbZ+nRaokZT1UgprzDTMmQKWp5HRyvAsTtzxeJZ4NDEqP2mg8imvmSUSnLSmR pdq7vUdk7lKvdV++fZo4XIRF/pqv7+8Nz8iZvxINGhFaJDUUPPQSFcLm00JIUMzn 9nh5JkCkKFuk34DgHDR3Zn+nM6R+gAuaDsBgv3xnU6PKVW796JPbz3yrN9fma9Pw P27ICXVyOH2oH7p/E7oNB/J0YxKcD5bjaFkzVHsMExCzeyGTA56qtdN2O1Oxiw2z L1Yitj1c+2/P29vhCw0IuxKjduL15Qu5Px5BT+B6V3cVUPbn9fYlDjSFAHxyrGno X3QnVzCChVoHuS+Og/QwEx6AcTSEbl4E47XQK0gr1cG7ayOZoDO3rqGQ+eO6kREM LpX2lHPofzMBk9lGPfAZX41pXUlshT0irrwFbIt3OTGfvU5x2wAjCap1InzvFS9J 4vEFHcLeHAi5ztlnYNIkB9/kja3ogpSCbcO6WoveJeHCTsXk5K4qIOSvoLYEdRE1 Pn2EJStyULZW9Sv1JH2puyZ2d2Y7cl6DqCZ5D4tFsyFFsMUNlBJQSxKoPDYnGsi8 DOTxrwhdxG/mSwn/NoYjZdC0Y+NJyBs1RvLvBZLdgzWS8I+uvyuwTfn27tP7GT6Z 8hmLPBMvUOyczXdMD6b1mfQ= -----END X509 CRL----- s390-tools-2.38.0/rust/pv/tests/assets/cert/inter_ca.crl000066400000000000000000000023331502674226300227610ustar00rootroot00000000000000-----BEGIN X509 CRL----- MIIDbTCCAVUCAQEwDQYJKoZIhvcNAQENBQAwgb0xCzAJBgNVBAYTAlVTMTQwMgYD VQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9u MTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBv cmF0aW9uMREwDwYDVQQIDAhOZXcgWW9yazEPMA0GA1UEBwwGQXJtb25rMR4wHAYD VQQLDBVJQk0gWiBJbnRlcm1lZGlhdGUgQ0EXDTI0MDMxMTE1NTIzOFoYDzIzODgx MjIzMTU1MjM4WjA8MBMCAgG8Fw0yNDAzMjAxNTUyMzhaMCUCFF72el1l160wBZ+u getcWTcmtomDFw0yNDAzMjAxNTUyMzhaoCMwITAfBgNVHSMEGDAWgBT5/uHw7a20 Ee3Ajg5mekP3BTT0ZjANBgkqhkiG9w0BAQ0FAAOCAgEAbGRxfJj3wsZ9iUsYTO3W 7+hNbZ+nRaokZT1UgprzDTMmQKWp5HRyvAsTtzxeJZ4NDEqP2mg8imvmSUSnLSmR pdq7vUdk7lKvdV++fZo4XIRF/pqv7+8Nz8iZvxINGhFaJDUUPPQSFcLm00JIUMzn 9nh5JkCkKFuk34DgHDR3Zn+nM6R+gAuaDsBgv3xnU6PKVW796JPbz3yrN9fma9Pw P27ICXVyOH2oH7p/E7oNB/J0YxKcD5bjaFkzVHsMExCzeyGTA56qtdN2O1Oxiw2z L1Yitj1c+2/P29vhCw0IuxKjduL15Qu5Px5BT+B6V3cVUPbn9fYlDjSFAHxyrGno X3QnVzCChVoHuS+Og/QwEx6AcTSEbl4E47XQK0gr1cG7ayOZoDO3rqGQ+eO6kREM LpX2lHPofzMBk9lGPfAZX41pXUlshT0irrwFbIt3OTGfvU5x2wAjCap1InzvFS9J 4vEFHcLeHAi5ztlnYNIkB9/kja3ogpSCbcO6WoveJeHCTsXk5K4qIOSvoLYEdRE1 Pn2EJStyULZW9Sv1JH2puyZ2d2Y7cl6DqCZ5D4tFsyFFsMUNlBJQSxKoPDYnGsi8 DOTxrwhdxG/mSwn/NoYjZdC0Y+NJyBs1RvLvBZLdgzWS8I+uvyuwTfn27tP7GT6Z 8hmLPBMvUOyczXdMD6b1mfQ= -----END X509 CRL----- s390-tools-2.38.0/rust/pv/tests/assets/cert/inter_ca.crt000066400000000000000000000044211502674226300227710ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIGhjCCBG6gAwIBAgIUOkSdvHg4/HXtknNMhIFkuPv/ghMwDQYJKoZIhvcNAQEL BQAwgbUxCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y azEPMA0GA1UEBwwGQXJtb25rMRYwFAYDVQQLDA1JQk0gWiBSb290IENBMCAXDTI0 MDMyMTE0NTIzOFoYDzIzODgxMjIzMTQ1MjM4WjCBvTELMAkGA1UEBhMCVVMxNDAy BgNVBAoMK0ludGVybmF0aW9uYWwgQnVzaW5lc3MgTWFjaGluZXMgQ29ycG9yYXRp b24xNDAyBgNVBAMMK0ludGVybmF0aW9uYWwgQnVzaW5lc3MgTWFjaGluZXMgQ29y cG9yYXRpb24xETAPBgNVBAgMCE5ldyBZb3JrMQ8wDQYDVQQHDAZBcm1vbmsxHjAc BgNVBAsMFUlCTSBaIEludGVybWVkaWF0ZSBDQTCCAiIwDQYJKoZIhvcNAQEBBQAD ggIPADCCAgoCggIBAMheyYWl/STLJ0iwlrqNRyURatdeC8oDpKFdpglYHAs/jo3s fWNySCnaw6NCe0vxFLpqcK8VMNFRGu/XhR/kZ1YR3V4mLwF1Wa5v7a7J9swq50Fk CsLtaU5vq/h6rIpy0NLnmN5KgqChrMh9IwZ+Mc8sqc/0BFFJsuCCGu0TNlGVOhmN AbdS3s7wEUwT023CKn47G3pVqeaErEB9honz1I71g5/jNKGe5CLCV35ExzsrzU43 atyJ0jgh15PYCXDTdsRccSmEs2S6Xh2o4ZhlqioWB+tKxGsdxq8Ri4soy6yyooOz T/3X5CHpKxiI2P9z38Pr9egPcNPPVMGDhzwHz7p3iBPg0RcWd5VP2nimLJsdGWK0 bkU7zlQ3R2NelSIW9Hr8MVASihmELvX+AcC6KhTpHHhf3CTPgcAfV2fE9U84Xl2i shmoEsUQTUx97qKUOKRfY6o+WMBnVkzlqWj+s52ndiT+0KNLTDtvlejEFf1VSF43 IkS9UJK+XxxEnvIBzNKI5EbWlG5Z38/nKv6pjTXFi3aZR4cdmI/0XfAjLTkrTBaW lkguEjt+/cxPYJOqt50ldI9kle8XTu/HibmcbU2wYIF21CBjE/hLk/KY3FQlnTFL y7x3bM0CuTfJ8Noy59f3l56fwPOpaQWqhqO+UoFkbFlbROAiVxfb8KlyZPdNAgMB AAGjgYEwfzAcBgNVHR8EFTATMBGgD6ANhgtyb290X2NhLmNybDAPBgNVHRMBAf8E BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBTWntryJpCwZqZJ1H3n znJYhbfoKjAdBgNVHQ4EFgQU+f7h8O2ttBHtwI4OZnpD9wU09GYwDQYJKoZIhvcN AQELBQADggIBAE8gulfly5+EC8DX3K02qEYPoQwVbVhD0wGrlAhgJiakDvPlX6/K vSe/1nNRG87jXvXdDiuJ6F4iKZpeJndzvx/8ZEmllyyxDwb3UOmylwW/o3/Uh6fY kiVBfW6uNNB0BfDKcXDDZgKjTg3kLT5z8m4u8rPoIPFkLFl9AuAq82Ll6NQ+xZFP lZ3K6HN+ntVnIGP4XkOgEYPxjJO3yTGle8VBqLfo/JKwbZtKfNXxSAMRXiP02SQg D9yshxkonQYWog2hHz8oDuQQNbzaAFlxnY914av/XwxP8TwEfGNchNtAtrlGRlx8 PjMfp3Mnbz71yp+L2We2/A7njIPbEcn0FIBedpNyyBON5Cd6Xqx18otmMTtUILZ3 SUKeYmLp8soVMEmmnWz6y1a4bCKwo6hA8oSoq5ydIeWy/jI9v7DF0S/qZTz3c2Q1 a3aniuug2FRAxuU/8fSlMrE4672d3505SbHUblhy9XzQ4+sWjkDtYnY89kyY4BTu W5n6JlVoewZGOjlJ9/6mV6BVLQ74IeiytWtdH5uOQ1wroi5Kq+EroGgFmPSQCIvN XNDvBCNFN/O1+/eVZd6JNx6NMO7DrWql3GFMtJE8u/SWXA9vit3pfmeDrosZ4SDg ZPt7+JfzITUh2UMaPsQsPF8G8/tYmxDbELE5LjFcQ24ps362rG+q2rfA -----END CERTIFICATE----- s390-tools-2.38.0/rust/pv/tests/assets/cert/inter_ca.invalid_date.crl000066400000000000000000000022421502674226300254020ustar00rootroot00000000000000-----BEGIN X509 CRL----- MIIDRDCCASwCAQEwDQYJKoZIhvcNAQENBQAwgb0xCzAJBgNVBAYTAlVTMTQwMgYD VQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9u MTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBv cmF0aW9uMREwDwYDVQQIDAhOZXcgWW9yazEPMA0GA1UEBwwGQXJtb25rMR4wHAYD VQQLDBVJQk0gWiBJbnRlcm1lZGlhdGUgQ0EXDTI0MDMxOTE1NTIzOFoXDTI0MDMy MDE1NTIzOFowFTATAgIBvBcNMjQwMzIwMTU1MjM4WqAjMCEwHwYDVR0jBBgwFoAU +f7h8O2ttBHtwI4OZnpD9wU09GYwDQYJKoZIhvcNAQENBQADggIBACgakUDSN7qq ATkeMC0PpwymGBu6i3Jd641BKDhwe0yG9rDa1ppm1W9z/z64LrzKSSeXJ8Hc07wP E94ODXqouo0WlGK4HnxGJ2QwHPJG69bYfcJREQw0Df/dTjlatyIc95QSsmChKixu 3goFe1laa421bUDIFr111Zv+4DV/LDoPZSQvRe0xHvouZNAQ0rQ8T4gVJkU6w/dW 636DvyXpW6/LyBTH4Nf4PHWjP6PTgzOW8+yX0RwCpHegwAA0ehEJZ8HAvW7VQWA3 uDdfB8j9uxyKQeeIkYLkK/Ds5P0nN8Kd/6OKGlF+9mRCpEQ0n3tgQa1q7tn9blVL 9yu3mmTbRB3YTRUCy/B7Wc8AwYE+k56wZoYpa7GI7mRYe7Vwhs6PeFmJLcZ3ByOl 1eeR6yIRzSbS50rVNOeaez4o0hjGEj82OJpK1THtB8pnCCIE5eZ8NI37WZGcPvMU KbNC+JjX+rlcnK7NTdS7eQZjcKOYVuDZ0lW5n+TaXKTLL24tuA+oV1SbRZOZN7QE 5/31A/1dCg2NDLKLNsSlq3A2DAJE5SOMSNCq4V+WGp36LAlKlNvYfHrcRxBEM6bp Qa4xCrcUINJ9x51wd0h5+5DilGdpoukSNwnSbMTBG7degdh2DGT4MqJ2Y1+puL9n 7EmMLPMyTYK/tB4ZsbgV/z05Dyngi3Ut -----END X509 CRL----- s390-tools-2.38.0/rust/pv/tests/assets/cert/inter_ca.invalid_signer.crl000066400000000000000000000022461502674226300257600ustar00rootroot00000000000000-----BEGIN X509 CRL----- MIIDRjCCAS4CAQEwDQYJKoZIhvcNAQENBQAwgb0xCzAJBgNVBAYTAlVTMTQwMgYD VQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9u MTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBv cmF0aW9uMREwDwYDVQQIDAhOZXcgWW9yazEPMA0GA1UEBwwGQXJtb25rMR4wHAYD VQQLDBVJQk0gWiBJbnRlcm1lZGlhdGUgQ0EXDTI0MDMxMTE1NTIzOFoYDzIzODgx MjIzMTU1MjM4WjAVMBMCAgG8Fw0yNDAzMjAxNTUyMzhaoCMwITAfBgNVHSMEGDAW gBTWntryJpCwZqZJ1H3nznJYhbfoKjANBgkqhkiG9w0BAQ0FAAOCAgEAFSRriQnY 3LIc9AHCGYZM+BFfaDnQCiwUx2PuYownMGOyDOu8xjBBIgNgLsNsqVLBQqP/tOdz 9WpIQd2VXbj8o2UaHZullRbJNJoNHzuC0KsdYuvZVooJ46nJONCo6RwsapiLlXnf Y/c//Ynqmk8xBZZpB4pAZKKq0D3eoeNFPJGWSwRa5AfQx5qpbikntK6khIBb8c+4 NfK6VMpEsHZQUG25fdLIpDOV2nnhRd2Xbcu/THFeCV2VnI8yoormOeiJznRJq1bv C4+jmW4NUAvFUFbcBRDpuq8LTIQUYuQYs018aCOqTTpMTTV5Q1iWuTZtAbO91UJp APEtEJH+/Wd/xB5ABk19bMiyxbCQeoLci7US0YEzASeGLH3Z59swwMvkGUhc2UvE Jtpx0unupgdA3lCpa/nDYwuFYCqmZ1kKHYtELC7WUldzd8K/J8XkNF4UIreTTD/0 kLC3UZEKuoIj3Lv4GrnWKcIdQ51jUMdbTUeIdWFzjqdxFq4i6oqgWA5Hjcr3yDcM ATjxVDLxeLC+fJf6/SQu+7dOWKPKzIS8BcufmZpkRoxrt5qmgoJHuceps5pdfa0B dy8xEb9FnGNSuLec1rcUPPFgCioSgDFoQiU0aoToW/kDVTA6/PHFh6TD6Qynmc4K PtMu/kCMWULgMi68svcnA7NxpoKAsCnQH6c= -----END X509 CRL----- s390-tools-2.38.0/rust/pv/tests/assets/cert/inter_ca.key000066400000000000000000000063101502674226300227700ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDIXsmFpf0kyydI sJa6jUclEWrXXgvKA6ShXaYJWBwLP46N7H1jckgp2sOjQntL8RS6anCvFTDRURrv 14Uf5GdWEd1eJi8BdVmub+2uyfbMKudBZArC7WlOb6v4eqyKctDS55jeSoKgoazI fSMGfjHPLKnP9ARRSbLgghrtEzZRlToZjQG3Ut7O8BFME9Ntwip+Oxt6VanmhKxA fYaJ89SO9YOf4zShnuQiwld+RMc7K81ON2rcidI4IdeT2Alw03bEXHEphLNkul4d qOGYZaoqFgfrSsRrHcavEYuLKMussqKDs0/91+Qh6SsYiNj/c9/D6/XoD3DTz1TB g4c8B8+6d4gT4NEXFneVT9p4piybHRlitG5FO85UN0djXpUiFvR6/DFQEooZhC71 /gHAuioU6Rx4X9wkz4HAH1dnxPVPOF5dorIZqBLFEE1Mfe6ilDikX2OqPljAZ1ZM 5alo/rOdp3Yk/tCjS0w7b5XoxBX9VUheNyJEvVCSvl8cRJ7yAczSiORG1pRuWd/P 5yr+qY01xYt2mUeHHZiP9F3wIy05K0wWlpZILhI7fv3MT2CTqredJXSPZJXvF07v x4m5nG1NsGCBdtQgYxP4S5PymNxUJZ0xS8u8d2zNArk3yfDaMufX95een8DzqWkF qoajvlKBZGxZW0TgIlcX2/CpcmT3TQIDAQABAoICAB9LwM2tbLHdwkvF+zD1ppZZ V1b+szgxI/ppSmj8uFqgaXds5/PLso5JA1QhaukkZVCtld6e4HJdKOgrwTkHP4Wv wiP1slNXvTNz+4uYs4HVtKufwNeL5e4Qnqtvm7n/L3M1pciYmjkVL4vcEceul8CM cRSQQEljCburzqFXZh1NgdbiUZGM8cygLg90LEqhMGppeIP908zzzYTAJm6vJTTU D6Q+RGb3DpqIQMqx2u14zLcqDDiYlTtbu9R1gpn7CXqnlqw9tBhoTJF04pGfVXn/ 2WpMkgvKCZGoG6PXciKN/zizXevf19vdE7RgEYaq1lk/ZngBy2Vl5Y7ZKJr9fFoU hdZq+w8UroAAzRIiLrbnGCt0+tK3rBC/DvtffNjvwjvphGaxKiz1pZdegZ4TsO+y zd+Aa6Gb/XCf5TIakFaYXPTpKj0UMi/TVKshblC9JWr3cuL3EoGJe4XtqxS0cGiT llLDTbiS4eSAM6S6Mek55jsD8KCGBrROp+A0Wc7nUinWfKXThJYvzOm4E0gSlvbN Fg9LqlbK47vbtSyAmfNcdl+C5oHVwE5p5AkZV8hNQOVusfs0aLufvWC+NCGLyJ/d wuMmFmx2XkrkhD+BsuhP1grkSjJLpKfufCzP9NBKXRzDMReiOI40AjL5Rm9ZVOc4 99B1CuyCiWG4BPUVlj3ZAoIBAQDs0nMc6lOY2VVkAYe6B5SmF94nMUzmegcIC4Hi oP0+4X8M2GfAiOKAb9QaMgrNnjOOeomYMUtfUhueRkXhvt5ssmalYCTLAuskApJ0 UM34UfpY3P3V3i69hOSdpPje6bsorTMOWheNiMz5XKTWaVnk27Ou2fAuChHuLbmC ApeMEYd/VgbwgdtOpLaJGPt0cQ1FhbSsoac4tHMLwN9GpVegts/WZZb0bzjXDLEY RcfxcYCO0FJvfaIyfr/JEnw0CsLrkDdIVcsG+QiebygxV0CiDzCeHUgd0uLNq+X6 fXqqQh8KUMHVHuFwGpEljpxvHqXQFq5SaEYqq7m9tGTcrHS5AoIBAQDYmKgHWG9a lz+usrwoxuHkzPJ+lpqslEVNs60nPf3aI6+bYjsOI7MNT7QkGQXdQ/aErWqzAali 566f3sUsDz2z/jaMGZYElqgF5sQdIAFTihPS5fkX7mcF5Nqst2tQz1rogEwWgJ14 XSQFCogASkxTEjD+/1kmuaSsc0NuOSkh4Wcxohla/7GQaiWnTBLnZ9axobLmmT76 vf4mGqAWmNNenHRkUY+ZoYF1xLpR/60OKzQTdUrL5NZB3bL4n3+ViOI0JE3VZYRQ PP3/tTr/NwwhTbkxpw/pNhbFIItPM1fzmfieGfgZlqmj5EXgwtGG2qlFPgR+9tWw uJwf4bHUerU1AoIBAQDDXRj5UZWFEkUPKvP80yQdoLLzQgheWWxThqqven9v0DYv MpbkjgfMTHR856th1JBTiWLY2lGDYQjHmNEtWXfD06g55QZ1MwMnwbdvnPlNUNNl W2lLdO66CVdPdTiZK9fpxnfH7ype5+uwCm0xM9ekpFmmdMNeN5BzG+VdCyZZtlxA /4baAUYZqmeq0aEefeSk5ZgWkYSRkssVdxa6AMw5GJZ8F3JgUyBgx2eQzoAS/b9A ETrwHoQfg9BS35z2kaobCe2RDrVeGzKxAKH3kjMPfdhtl1pWwBG5+YTPD5SRv9o7 eENuvPrcsA3tHaiPQoknEI7eDIdVzDR9+sL6CIqBAoIBAQCcY90vMJN2fa7lnPhv GOsSIUkWTffwlD5WFF3577DTSOEK+KpbUzt9aQdQ6SBq2x+sPOrFxXVgjJhxppAH eBm9VNhd4DuJpJ49ZJpFzU4n25LkvFhXBzQr9UpPW0CJYK7rIXfO557LwbS4TxpT 21GwyXnHJOhiJbjZK4CMnYkthWrVU42rPuQeugXl/e/IVhmWuIJMLmpi8bwIF0Fw D3jO33jK1nkxHIQ1XI6LlmiFynwXcKFJBzoM5N7M1Z1xgxyROYVZh6s5pFBhyaGH lV+UzGHjkBTU9tEKFK2Byji/E3WH8ohJMZfbVn9+Aiz3ifqenGDyq5cvupxACN41 5UPhAoIBAC51vwgPKuFNXU/pipkRMr7S+Ew7N5rVij3dJhcaq0RMNm24zneKZoC2 7b7zKd92zYV4RgviUZEiSmiGXbEvVkNrSZ0BESRt8C7dfqDZx8E3+LMH2Fz2Q3CJ EJjEGiQuDQuyx2ICs5ETvIFzxy0A+uRbsrjJHbikizvPbX6qY1F9q4m3GAxU1mlv Nj548F/eL13gCUVPfmcbkDXKXDjeMydogPqcH20/d+88Cl5puFV1KwBptmJ/2WVP fcVKpeLL1DvWsd9UikzYuLXP3tGs47aPPKqX20lQoZWW+su6uWXi62WuOSPckHF0 /y+kPmf1RQh8JbiNv7ddFdj4eUReLOo= -----END PRIVATE KEY----- s390-tools-2.38.0/rust/pv/tests/assets/cert/root_ca.chained.crt000066400000000000000000000065671502674226300242420ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIGXzCCBEegAwIBAgIUd6BiIEGe+cX502NGGPVuMHrV0ucwDQYJKoZIhvcNAQEL BQAwgbUxCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y azEPMA0GA1UEBwwGQXJtb25rMRYwFAYDVQQLDA1JQk0gWiBSb290IENBMCAXDTI0 MDMyMTE0NTIzN1oYDzIzODgxMjIzMTQ1MjM3WjCBtTELMAkGA1UEBhMCVVMxNDAy BgNVBAoMK0ludGVybmF0aW9uYWwgQnVzaW5lc3MgTWFjaGluZXMgQ29ycG9yYXRp b24xNDAyBgNVBAMMK0ludGVybmF0aW9uYWwgQnVzaW5lc3MgTWFjaGluZXMgQ29y cG9yYXRpb24xETAPBgNVBAgMCE5ldyBZb3JrMQ8wDQYDVQQHDAZBcm1vbmsxFjAU BgNVBAsMDUlCTSBaIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK AoICAQDMw/UE5XNiZpjuCRtyq4A9PDBuJJgyUl14xvN8lcsLHGK9b/WkQdDxUXsN QbvS2HVkg2X6AGHwixk/3U0mzgzImZhmh3i0IbFn7L2eqpuvgP82BJpveq58Sjsk OHYuG2JqSv63reKtK8pLXtKZnSV9vFUAwxWSuqA68FLwDgjGMK/Iy3Hh+cwnpffn IMqJ70Nlxc7z4KKO4nrO/+rIQSqe0UpjEEaK4Eb3YXWWontaah3/sw+uAbVncJMV +AacHQzi130sJJeLe4p+8DD9EYX112DEj6WFrHpyEqhsGXvLO7uJRzMqJ1WVz47x j8wK+GPm66KCeOM+wm65u49vNHDotlQzoclIzLDiegQo/3ob5YRTFNDJzbNN6AAE zF8K7f8t2TNYeWgPOmXLEVmFalOSNTZ5sLrJipsYs7uT7CT1orW3PvM16v+knigJ NuryIdr/+hFgRh7Sr4U/6H6fDuZGJXgZeUSNM7S1UI0iFTZyKAP/3EQrhRWlIROs ql9rFnH0LWaPcmk66Yyhq6Va0dmLBSjGPWAHLguKyHkMvhJfMd/1miGfz9Rz9LbE 6CqOK8sf6EQNWTgmpTdXgjmR9dvMk3ki2aar0u+IamzfIx3y0OPSlpaO8mFMucZe 3iaeG/l84sUR93NgVI2dCdtEinCQviudV+QwRdLzZQQNM5zLzwIDAQABo2MwYTAP BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBTWntry JpCwZqZJ1H3nznJYhbfoKjAdBgNVHQ4EFgQU1p7a8iaQsGamSdR9585yWIW36Cow DQYJKoZIhvcNAQELBQADggIBAHw/+RKyDRBsvWksX4ji95W+4uo292psPzpeusjw Ztl25D4jssgjNbEiNwyYgV9e9BCse9hOkFwAE6ogwBAel7POX8XjizaJcwSs/GaC 2ORQ/KYRpMsypENZ4HZQbc8j9ROqTDD45B+9/5nAA6le/7wd0yS8hbum/b78vAN1 98Ja8wfQIq1PE0heFELSRR2hAmcGkxIo1tBqP/CnAzOpBZ2ovPJ75oeGlnpk/ATY rUJPTY0UPGp9ZSq/l+t88onDewjCblMMMBeJSEklP2CFlE1jGx3QJtnThiJ9WQKG TMakgaAERrcvXs6Lx2GFuk7kCOJsWJtvT6CJVCUtEBHNGY3GqyQwNrE3OEVQ6CFb CNNarrRKlArBISExLY3xn0CcuWr8GewsIvwzmJBX+u2Xs0s7RYFc4S4s/SbRLqi4 Gn77E7dG711nKHXwDxYOuDAL0MNBT4aDephQK+rF7GlHMLTUPWKYCwmjh2vzmRWJ fZkojybvX6g8+7fKmwTUn5LXlcnmjv9KYN8LgfHSQhvRaGtGph0X8mVH3eCs4MGG OUU6p9wdPS3MDBHRzn0MXEhq25r+xT829lcHdoxkMVorVDTDD7hOm3HBsF/LOh6c /sA1LZnnfy+YZt+Gn5cE5CSK3ddmzRbrB+ZHWbJahegJktWq2rrnw9ipfRln7DJF fsHf -----END CERTIFICATE----- -----BEGIN X509 CRL----- MIIDPjCCASYCAQEwDQYJKoZIhvcNAQENBQAwgbUxCzAJBgNVBAYTAlVTMTQwMgYD VQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9u MTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBv cmF0aW9uMREwDwYDVQQIDAhOZXcgWW9yazEPMA0GA1UEBwwGQXJtb25rMRYwFAYD VQQLDA1JQk0gWiBSb290IENBFw0yNDAzMTExNTUyMzdaGA8yMzg4MTIyMzE1NTIz N1owFTATAgIBTRcNMjQwMzIwMTU1MjM3WqAjMCEwHwYDVR0jBBgwFoAU1p7a8iaQ sGamSdR9585yWIW36CowDQYJKoZIhvcNAQENBQADggIBAIs4Y7Qsk2TAJQqdNQeM 3Gjx2fYFHbliDRVQPU5OP19ppeUzFGRL/SRplCH4rvEj4bBwQZcsNdmzxYyFbWcz lU38l5/mgkLkPdHzRnbPmVraWqKzuvNrTQ3Lj5VcgOJ95UAOwOIg2jWZlkIAUgO/ cayWJK7l0gOIDCKSyuI6haM5D6wNAwfyKZ4WkBgYulCeqRmsMePvsj/WYL1tcR6Y 1WSN6FtvLn9u2vqrJ879BEH5RDQSkCXsxBqKFnOpLf/gpRYhRp+3JpSACXOzy1wr 8KxlvZ/14UOhz4x8VZ9GJTzLydzLPIhNTwcl1PJyD3pOhASxfdSXuR8f3jJvAGv5 kaj9uO23sKU4PzLfr/DPFkhKsp+vShoSGtnZjj7ewYW9Y/PY0D9TX8CWIcwKuJ8U XTWRj9s/FLTlqfrbJXvSaRd0rihGQI+mSwar5cnBuxULgw4V35YmTiY8wCf3+k9c tGRRUj5A2fVs9NWWTReBatoou8Rnuraz6ctqwFsxJQSGS1JpfECznGq2wKqLstKn WCMEAKehcGqLlSco7Hg2qRNgDw5AAC058drRf9L70geV4Tf00iXjK19aHSJ1wH+p 7hUcTCiQ7413ztC7HawqsRW3Yzszucj+6vqF8ZYMh+9oEzzWJEt/piKakIwbxwKk cf5JE3KE7X+LQS3p4+sw/wP4 -----END X509 CRL----- s390-tools-2.38.0/rust/pv/tests/assets/cert/root_ca.crl000066400000000000000000000022321502674226300226210ustar00rootroot00000000000000-----BEGIN X509 CRL----- MIIDPjCCASYCAQEwDQYJKoZIhvcNAQENBQAwgbUxCzAJBgNVBAYTAlVTMTQwMgYD VQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9u MTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBv cmF0aW9uMREwDwYDVQQIDAhOZXcgWW9yazEPMA0GA1UEBwwGQXJtb25rMRYwFAYD VQQLDA1JQk0gWiBSb290IENBFw0yNDAzMTExNTUyMzdaGA8yMzg4MTIyMzE1NTIz N1owFTATAgIBTRcNMjQwMzIwMTU1MjM3WqAjMCEwHwYDVR0jBBgwFoAU1p7a8iaQ sGamSdR9585yWIW36CowDQYJKoZIhvcNAQENBQADggIBAIs4Y7Qsk2TAJQqdNQeM 3Gjx2fYFHbliDRVQPU5OP19ppeUzFGRL/SRplCH4rvEj4bBwQZcsNdmzxYyFbWcz lU38l5/mgkLkPdHzRnbPmVraWqKzuvNrTQ3Lj5VcgOJ95UAOwOIg2jWZlkIAUgO/ cayWJK7l0gOIDCKSyuI6haM5D6wNAwfyKZ4WkBgYulCeqRmsMePvsj/WYL1tcR6Y 1WSN6FtvLn9u2vqrJ879BEH5RDQSkCXsxBqKFnOpLf/gpRYhRp+3JpSACXOzy1wr 8KxlvZ/14UOhz4x8VZ9GJTzLydzLPIhNTwcl1PJyD3pOhASxfdSXuR8f3jJvAGv5 kaj9uO23sKU4PzLfr/DPFkhKsp+vShoSGtnZjj7ewYW9Y/PY0D9TX8CWIcwKuJ8U XTWRj9s/FLTlqfrbJXvSaRd0rihGQI+mSwar5cnBuxULgw4V35YmTiY8wCf3+k9c tGRRUj5A2fVs9NWWTReBatoou8Rnuraz6ctqwFsxJQSGS1JpfECznGq2wKqLstKn WCMEAKehcGqLlSco7Hg2qRNgDw5AAC058drRf9L70geV4Tf00iXjK19aHSJ1wH+p 7hUcTCiQ7413ztC7HawqsRW3Yzszucj+6vqF8ZYMh+9oEzzWJEt/piKakIwbxwKk cf5JE3KE7X+LQS3p4+sw/wP4 -----END X509 CRL----- s390-tools-2.38.0/rust/pv/tests/assets/cert/root_ca.crt000066400000000000000000000043351502674226300226370ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIGXzCCBEegAwIBAgIUd6BiIEGe+cX502NGGPVuMHrV0ucwDQYJKoZIhvcNAQEL BQAwgbUxCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y azEPMA0GA1UEBwwGQXJtb25rMRYwFAYDVQQLDA1JQk0gWiBSb290IENBMCAXDTI0 MDMyMTE0NTIzN1oYDzIzODgxMjIzMTQ1MjM3WjCBtTELMAkGA1UEBhMCVVMxNDAy BgNVBAoMK0ludGVybmF0aW9uYWwgQnVzaW5lc3MgTWFjaGluZXMgQ29ycG9yYXRp b24xNDAyBgNVBAMMK0ludGVybmF0aW9uYWwgQnVzaW5lc3MgTWFjaGluZXMgQ29y cG9yYXRpb24xETAPBgNVBAgMCE5ldyBZb3JrMQ8wDQYDVQQHDAZBcm1vbmsxFjAU BgNVBAsMDUlCTSBaIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK AoICAQDMw/UE5XNiZpjuCRtyq4A9PDBuJJgyUl14xvN8lcsLHGK9b/WkQdDxUXsN QbvS2HVkg2X6AGHwixk/3U0mzgzImZhmh3i0IbFn7L2eqpuvgP82BJpveq58Sjsk OHYuG2JqSv63reKtK8pLXtKZnSV9vFUAwxWSuqA68FLwDgjGMK/Iy3Hh+cwnpffn IMqJ70Nlxc7z4KKO4nrO/+rIQSqe0UpjEEaK4Eb3YXWWontaah3/sw+uAbVncJMV +AacHQzi130sJJeLe4p+8DD9EYX112DEj6WFrHpyEqhsGXvLO7uJRzMqJ1WVz47x j8wK+GPm66KCeOM+wm65u49vNHDotlQzoclIzLDiegQo/3ob5YRTFNDJzbNN6AAE zF8K7f8t2TNYeWgPOmXLEVmFalOSNTZ5sLrJipsYs7uT7CT1orW3PvM16v+knigJ NuryIdr/+hFgRh7Sr4U/6H6fDuZGJXgZeUSNM7S1UI0iFTZyKAP/3EQrhRWlIROs ql9rFnH0LWaPcmk66Yyhq6Va0dmLBSjGPWAHLguKyHkMvhJfMd/1miGfz9Rz9LbE 6CqOK8sf6EQNWTgmpTdXgjmR9dvMk3ki2aar0u+IamzfIx3y0OPSlpaO8mFMucZe 3iaeG/l84sUR93NgVI2dCdtEinCQviudV+QwRdLzZQQNM5zLzwIDAQABo2MwYTAP BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBTWntry JpCwZqZJ1H3nznJYhbfoKjAdBgNVHQ4EFgQU1p7a8iaQsGamSdR9585yWIW36Cow DQYJKoZIhvcNAQELBQADggIBAHw/+RKyDRBsvWksX4ji95W+4uo292psPzpeusjw Ztl25D4jssgjNbEiNwyYgV9e9BCse9hOkFwAE6ogwBAel7POX8XjizaJcwSs/GaC 2ORQ/KYRpMsypENZ4HZQbc8j9ROqTDD45B+9/5nAA6le/7wd0yS8hbum/b78vAN1 98Ja8wfQIq1PE0heFELSRR2hAmcGkxIo1tBqP/CnAzOpBZ2ovPJ75oeGlnpk/ATY rUJPTY0UPGp9ZSq/l+t88onDewjCblMMMBeJSEklP2CFlE1jGx3QJtnThiJ9WQKG TMakgaAERrcvXs6Lx2GFuk7kCOJsWJtvT6CJVCUtEBHNGY3GqyQwNrE3OEVQ6CFb CNNarrRKlArBISExLY3xn0CcuWr8GewsIvwzmJBX+u2Xs0s7RYFc4S4s/SbRLqi4 Gn77E7dG711nKHXwDxYOuDAL0MNBT4aDephQK+rF7GlHMLTUPWKYCwmjh2vzmRWJ fZkojybvX6g8+7fKmwTUn5LXlcnmjv9KYN8LgfHSQhvRaGtGph0X8mVH3eCs4MGG OUU6p9wdPS3MDBHRzn0MXEhq25r+xT829lcHdoxkMVorVDTDD7hOm3HBsF/LOh6c /sA1LZnnfy+YZt+Gn5cE5CSK3ddmzRbrB+ZHWbJahegJktWq2rrnw9ipfRln7DJF fsHf -----END CERTIFICATE----- s390-tools-2.38.0/rust/pv/tests/assets/cert/root_ca.key000066400000000000000000000063101502674226300226320ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDMw/UE5XNiZpju CRtyq4A9PDBuJJgyUl14xvN8lcsLHGK9b/WkQdDxUXsNQbvS2HVkg2X6AGHwixk/ 3U0mzgzImZhmh3i0IbFn7L2eqpuvgP82BJpveq58SjskOHYuG2JqSv63reKtK8pL XtKZnSV9vFUAwxWSuqA68FLwDgjGMK/Iy3Hh+cwnpffnIMqJ70Nlxc7z4KKO4nrO /+rIQSqe0UpjEEaK4Eb3YXWWontaah3/sw+uAbVncJMV+AacHQzi130sJJeLe4p+ 8DD9EYX112DEj6WFrHpyEqhsGXvLO7uJRzMqJ1WVz47xj8wK+GPm66KCeOM+wm65 u49vNHDotlQzoclIzLDiegQo/3ob5YRTFNDJzbNN6AAEzF8K7f8t2TNYeWgPOmXL EVmFalOSNTZ5sLrJipsYs7uT7CT1orW3PvM16v+knigJNuryIdr/+hFgRh7Sr4U/ 6H6fDuZGJXgZeUSNM7S1UI0iFTZyKAP/3EQrhRWlIROsql9rFnH0LWaPcmk66Yyh q6Va0dmLBSjGPWAHLguKyHkMvhJfMd/1miGfz9Rz9LbE6CqOK8sf6EQNWTgmpTdX gjmR9dvMk3ki2aar0u+IamzfIx3y0OPSlpaO8mFMucZe3iaeG/l84sUR93NgVI2d CdtEinCQviudV+QwRdLzZQQNM5zLzwIDAQABAoICAADiwh8UzQR6dCPndy5uTn41 UfJQBzaEg7H/jlMWJMw2AblXECV3QWuh0hnzFFhrpkpahSjpMoNDXscXcnt9/bEq dO3QnTWORcGw1PsoOscuFCyMJYzg53tTKOFVuzEk3i6eh26M+oOMQnJEBT4z31Ml auq3bVL2qrXGj11JekE9Oa1xL9tt5LOxNJrT2fxxxAVfLy9/48Qhd62Ijt/x5DjO p/c4vU1hff0Y91TA/C6eceXGxQUYLcw2QPSh34lyWLlsfDaiK+OnE6jL6jJWDpHL Ljh7dJhY8CipKwBYtd/hsMR9wdtnUyf0P2aNHDFZ9LitgUT2N+lwFuGHza2J+QkO sJndo0bNZCRbcfyaATpqQK50fLHpSOkIaA0uNf8IPsgRHnC9oHimIM70/VB4xN5K 8UJb02rVvAWsV2ZJEzhBXt3ivMwH6d+HKVerkjdhuKxUSO0x4ZfArZI0md6M2Etf b2UbMOyyTknWiJcYD65151rThGeyKRSW1GalvrwWwcAAWyabf61a681brpY0t/u4 aZXBdg41DiZ8bnheK706+l7hpCt/Zg3d+HPVtG8xWreFZzWMPNAmuVcdqtEOqdhe aW6xDR/7ekdVjtzFboTVuq12JvMgucfXoYjqNA1TxvXIdl1eSWZzEnfXhDxDezpI ZUXfR1jdmyEucFXAlkjVAoIBAQD094bU92/iTkV3ySqDqd1a9gdfMw9ewi18TzWM fCtE+dWgvXE1RmzRmHaeKjElJaeAAc/ggXen1ecJmCRsD1wjItyP1/j/IzY/7yzK KtLV3w3oArENFwZKa3HeaHsapwladJBVC0CpaRnfO52VKDa/1gvDQsojOkDhngCH MKfRd5x4c4+WOuvTpfyQASLXJcurlIhabCa/MGIvhMRXcspGkkttZqxhLGoioAmS VcKhje8UuDLnBxCFrmIOVl5Hf8wT4TfOYWq6J5F/v/32dq8Rdf/W8KRIq0N5JDnC xFKGkm8QEFxNuSzzfykMcEhKGYXAYFKGIBRhM+nNGuFyqMB7AoIBAQDV/Oguqp74 4inMMQTP8i3aYlASfU/l2OkNQE7JbxIClf52TzOCPy8v86FjIyx2iYPv2osI3RGi yVdDIYPXOBEQZQV7eHS3kuO5/lFw6psQVjMf0qdE9hGuZx3MnXQgol7mUEDS7Rjc e1YSkYPpdSd2y1Gsb84iZcbBgBPZsdjy81ZBrfP8bo3BVBtqQIngWCXvM3gWmBH/ duNxtpvNBFruX2ZwaKcRJjfZ7V6iy2fzEYH84FPyiwj/COCtSfDgkFjCmEa2jepQ +5r1VXFjeWwonfYeiwZ/WlIGDKhHNZG4XCni05CA2ZejDLmI3IKaMjukWNI49hhS ypLJj5n9a8O9AoIBAHrPfbFXO6hhRUKAf+fySR9JKPj5SENhZhxkOZxsw+SYvBkR 4Kes3vFVVmKFFI5jCk6KmgUb4zkpa+LLe7cRHEghOiKDTDndFV2IMxRH09uVMAuZ DdpaKLU2mKfACbea9n164sFAGvLT1jysTpxwxMH4NX13BpASrGRwKiOcqQXCyZc5 mErgz0oUdUJcz3K9aBZlKNbsUaCYSSE5lpWg3vfycA4w40r0UWF6ilHq3ODRn4Lr tdlNGWNrwY0ej7WYxF9TEf9Np4wcOj2pq1Dcv3gpiFHh4vrrobAiETMr6ZO95iBP k3cD1x1cKuApipRbp0qC/9xuSMlSlWxWhaBOKfECggEAb0NdBka4+fe+e+lQ8z6d ENvlfnehv4UVSEqTrLEP0EBlWua7hZGM24X1+DIlwEyoSWJ8wFMSBG5j7QfUIWeJ l9ivDRAIwBqkReUyO2AA2HG5i2Zgir7XWrNLD0UfSIikh2RbEFEviBSpIGaBDDZa Gq6E/P/1UnVQ7vPFXn/WqhxUUTo9jpd9JXSx/IEqL3gl4UYFvtm7IfWTNWEZiXQm Q6NfBDumAoi4qZt+hW710bDcwbtyar5YIyNejzvO/zSOsj+zJOCNYSYx4DZZCrvr vQLFIgRvkHBKDdMu/DeiWRWywbn3fMemzKSlI8BkOAC+eimkxPFQnFuwDxWXn+kU kQKCAQEA0EkmMxpI8eC47YeHXKBiACMsRiyiItXal7TAwy/ZQMg/nxrDB85f5L8B Zucw2UsA40/5EN/L6n10iaWBQzz7q3TcI15MmIzoBA7Bh9IJI9Oz1+oeOn01b9u0 7cb+5Shh9Tmu2G4Z/9LE7wB8q9sU/BtJns6owFbGHL9q6ndtl7YVQ7uVzUQjNQph qp2DvA8qZYBkNRNZwYAMOJaAO8uhDBSR2wm9XiZ3Q9kar0Tx6sR9WK2pjZTE3okJ RUqzQS0EXQ2XLepP3rhYtjAjtlnx9K3jniIHciyFV2rW0SaGH4VvE/nRvCM12q/8 p+5MT0G3vVdRQM0Poco3B8DElAoMTQ== -----END PRIVATE KEY----- s390-tools-2.38.0/rust/pv/tests/assets/exp/000077500000000000000000000000001502674226300203315ustar00rootroot00000000000000s390-tools-2.38.0/rust/pv/tests/assets/exp/arcb.bin000066400000000000000000000006201502674226300217300ustar00rootroot00000000000000UUUUUUUUUUUUPp]4RE{ FNn#gbL2ř_EkfmZd.zTP@XPS:8yg( ΃e씲éؾcJF_o\GO==pA\%ќ8a3؋1؂խK1)Kis390-tools-2.38.0/rust/pv/tests/assets/exp/asrcb/000077500000000000000000000000001502674226300214235ustar00rootroot00000000000000s390-tools-2.38.0/rust/pv/tests/assets/exp/asrcb/assoc_derived_default_cuid_one000066400000000000000000000026101502674226300275300ustar00rootroot00000000000000asrcbMUUUUUUUUUUUU@BBBBBBBBBBBBBBBB]4RE{ FNn#gbL2ř_EkfmZd.zTP@XPS:8yg( ΃e씲éؾcJF_o\GO==pA\%ќ8a3؋1؂խKs390-tools-2.38.0/rust/pv/tests/assets/exp/asrcb/null_none_default_cuid_one000066400000000000000000000025101502674226300267060ustar00rootroot00000000000000asrcbMHUUUUUUUUUUUU BBBBBBBBBBBBBBBB]4RE{ FNn#gbL2ř_EkfmZd.zTP@XPS:8yg( ΃e씲éؾcJF_o\GO==pA\%ќ8a3؋1؂խK2s390-tools-2.38.0/rust/pv/tests/assets/exp/asrcb/null_none_default_ncuid_one000066400000000000000000000025101502674226300270640ustar00rootroot00000000000000asrcbMHUUUUUUUUUUUU ]4RE{ FNn#gbL2ř_EkfmZd.zTP@XPS:8yg( ΃e씲éؾcJF_o\GO==pA\%ќ8a3؋1؂խK]-!9]c!ӺQ/>a(X6* ysqe<ڢP,{6S b -o%4{L O˓(H`{$됚ySV26!eapŐ0ו/ѻyUIשJ v ]Gzx)f)oYhx 14LU-)/Yg /[@  match ty { IbmSignInvalid(err, _d) => &&err.as_raw() == e, _ => false, }, e => panic!("Unexpected error type: {e:?}"), }) .count() == 0 { panic!("Error {obs:?} did not match one of the expected {exp_raw:?}"); } } #[test] fn verifier_new() { let root_chn_crt = get_cert_asset_path("root_ca.chained.crt"); let root_crt = get_cert_asset_path("root_ca.crt"); let inter_crt = get_cert_asset_path("inter_ca.crt"); let inter_fake_crt = get_cert_asset_path("fake_inter_ca.crt"); let inter_fake_crl = get_cert_asset_path("fake_inter_ca.crl"); let inter_crl = get_cert_asset_path("inter_ca.crl"); let ibm_crt = get_cert_asset_path("ibm.crt"); let ibm_early_crt = get_cert_asset_path("ibm_outdated_early.crl"); let ibm_late_crt = get_cert_asset_path("ibm_outdated_late.crl"); let ibm_rev_crt = get_cert_asset_path("ibm_rev.crt"); let empty: [String; 0] = []; // Too many signing keys let verifier = CertVerifier::new(&[&ibm_crt, &ibm_rev_crt], &empty, None::, true); assert!(matches!(verifier, Err(Error::HkdVerify(ManyIbmSignKeys)))); // No CRL for each X509 let verifier = CertVerifier::new( &[&inter_crt, &ibm_crt], &[&inter_crl], Some(&root_crt), false, ); verify_sign_error(3, verifier.unwrap_err()); let verifier = CertVerifier::new(&[&inter_crt, &ibm_crt], &empty, Some(&root_chn_crt), false); verify_sign_error(3, verifier.unwrap_err()); // Wrong intermediate (or ibm key) let verifier = CertVerifier::new( &[&inter_fake_crt, &ibm_crt], &[&inter_fake_crl], Some(&root_chn_crt), true, ); // Depending on the OpenSSL version different error codes can appear verify_sign_error_slice(&[20, 30], verifier.unwrap_err()); // Wrong root ca let verifier = CertVerifier::new(&[&inter_crt, &ibm_crt], &[&inter_crl], None::, true); verify_sign_error(20, verifier.unwrap_err()); // Correct signing key + intermediate cert let _verifier = CertVerifier::new( &[&inter_crt, &ibm_crt], &[&inter_crl], Some(&root_chn_crt), false, ) .unwrap(); // No intermediate key let verifier = CertVerifier::new(&[&ibm_crt], &empty, Some(&root_chn_crt), false); verify_sign_error(20, verifier.unwrap_err()); // IBM Sign outdated let verifier = CertVerifier::new( &[&inter_crt, &ibm_early_crt], &[&inter_crl], Some(&root_chn_crt), false, ); assert!(matches!(verifier, Err(Error::HkdVerify(NoIbmSignKey)))); let verifier = CertVerifier::new( &[&inter_crt, &ibm_late_crt], &[&inter_crl], Some(&root_chn_crt), false, ); assert!(matches!(verifier, Err(Error::HkdVerify(NoIbmSignKey)))); // Revoked let verifier = CertVerifier::new( &[&inter_crt, &ibm_rev_crt], &[&inter_crl], Some(&root_chn_crt), false, ); verify_sign_error(23, verifier.unwrap_err()); } s390-tools-2.38.0/rust/pv_core/000077500000000000000000000000001502674226300161015ustar00rootroot00000000000000s390-tools-2.38.0/rust/pv_core/Cargo.toml000066400000000000000000000015031502674226300200300ustar00rootroot00000000000000[package] name = "s390_pv_core" version = "0.12.0" edition.workspace = true license.workspace = true rust-version.workspace = true description = "s390-tools IBM Secure Execution core utilities" keywords = ["s390", "s390x", "IBM_Secure_Execution"] repository = "https://github.com/ibm-s390-linux/s390-tools/tree/master/rust" categories = ["hardware-support"] readme = "README.md" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lints] workspace = true [dependencies] libc = "0.2.169" log = { version = "0.4.25", features = ["std", "release_max_level_debug"] } thiserror = "2.0.11" zerocopy = {version = "0.8", features = ["derive"]} serde = { version = "1.0.217", features = ["derive"]} byteorder = "1.5" regex = "1.10" [dev-dependencies] serde_test = "1.0.177" lazy_static = "1.5" s390-tools-2.38.0/rust/pv_core/README.md000066400000000000000000000016511502674226300173630ustar00rootroot00000000000000 # `s390_pv_core` - basic library for pv-tools This library is intended to be used by tools and libraries that are used for creating and managing [IBM Secure Execution](https://www.ibm.com/docs/en/linux-on-systems?topic=virtualization-secure-execution) guests. `s390_pv_core` provides abstraction layers for secure memory management, logging, and accessing the uvdevice. If your project is not targeted to provide tooling for and/or managing of IBM Secure execution guests, do **not** use this crate. It does not provide any cryptographic operations through OpenSSL. For this use [s390_pv](https://crates.io/crates/s390_pv_core) which reexports all symbols from this crate. If your project uses `s390_pv` crate do **not** include `s390_pv_core` as well. ## Import crate The recommended way of importing this crate is: ```bash cargo add s390_pv_core --rename pv_core ``` s390-tools-2.38.0/rust/pv_core/src/000077500000000000000000000000001502674226300166705ustar00rootroot00000000000000s390-tools-2.38.0/rust/pv_core/src/apdevice.rs000066400000000000000000000404641502674226300210260ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 // //! AP support functions // use crate::{ utils::{pv_guest_bit_set, read_file_string, write_file}, Error, Result, }; use regex::Regex; use std::fmt; use std::thread; use std::time; const PATH_SYS_DEVICES_AP: &str = "/sys/devices/ap"; /// Regular expression for AP queue directories pub const RE_QUEUE_DIR: &str = r"^([[:xdigit:]]{2})\.([[:xdigit:]]{4})$"; const RE_CARD_TYPE: &str = r"^CEX([3-8])([ACP])$"; const RE_EP11_MKVP: &str = r"WK\s+CUR:\s+(\S+)\s+(\S+)"; const RE_CCA_AES_MKVP: &str = r"AES\s+CUR:\s+(\S+)\s+(\S+)"; const RE_CCA_APKA_MKVP: &str = r"APKA\s+CUR:\s+(\S+)\s+(\S+)"; const SYS_BUS_AP_BIND_POLL_MS: u64 = 500; const SYS_BUS_AP_BIND_TIMEOUT_MS: u64 = 10000; const SYS_BUS_AP_ASSOC_POLL_MS: u64 = 500; const SYS_BUS_AP_ASSOC_TIMEOUT_MS: u64 = 10000; /// APQN mode #[derive(Debug, Clone, PartialEq, Eq)] pub enum ApqnMode { /// Accelerator mode Accel, /// EP11 (Enterprise PKCS #11) coprocessor mode Ep11, /// Common Cryptographic Architecture (CCA) coprocessor mode Cca, } /// Info on an APQN configured for accelerator #[derive(Debug, Clone)] pub struct ApqnInfoAccel { // empty } /// Info on an APQN configured for EP11 coprocessor #[derive(Debug, Clone)] pub struct ApqnInfoEp11 { /// Serial number of the Crypto Express adapter as a case-sensitive ASCII string pub serialnr: String, /// Master key verification pattern as hex string pub mkvp: String, // may be an empty string if no WK set } /// Info on an APQN configured for CCA coprocessor #[derive(Debug, Clone)] #[allow(dead_code)] pub struct ApqnInfoCca { /// Serial number of the Crypto Express adapter as a case-sensitive ASCII string pub serialnr: String, /// Master key verification pattern as hex string for AES pub mkvp_aes: String, // may be an empty string if no MK set /// Master key verification pattern as hex string for asymmetric public key algorithms pub mkvp_apka: String, // may be an empty string if no MK set } /// Info for an APQN's mode #[derive(Debug, Clone)] pub enum ApqnInfo { /// Info on an APQN configured for accelerator Accel(ApqnInfoAccel), /// Info on an APQN configured for EP11 coprocessor Ep11(ApqnInfoEp11), /// Info on an APQN configured for CCA coprocessor #[allow(dead_code)] Cca(ApqnInfoCca), } macro_rules! parse_error { ($subject:expr, $content:expr) => { Error::ParseError { subject: $subject, content: $content, } }; } impl ApqnInfo { fn accel_info(_carddir: &str, _queuedir: &str) -> Result { Ok(Self::Accel(ApqnInfoAccel {})) } fn cca_info(carddir: &str, queuedir: &str) -> Result { let serialnr_str = read_file_string(format!("{carddir}/serialnr"), "serialnr")?; let serialnr = serialnr_str.trim().to_string(); let mkvps = read_file_string(format!("{carddir}/{queuedir}/mkvps"), "mkvps")?; let mut aes_mkvp = String::new(); let re_cca_aes_mkvp = Regex::new(RE_CCA_AES_MKVP).unwrap(); if !re_cca_aes_mkvp.is_match(&mkvps) { return Err(parse_error!(format!("APQN {queuedir} MKVPs"), mkvps)); } else { let caps = re_cca_aes_mkvp.captures(&mkvps).unwrap(); if caps.get(1).unwrap().as_str().to_lowercase() == "valid" { aes_mkvp = caps.get(2).unwrap().as_str().to_lowercase(); if aes_mkvp.starts_with("0x") { aes_mkvp = String::from(&aes_mkvp[2..]); } } } let mut apka_mkvp = String::new(); let re_cca_apka_mkvp = Regex::new(RE_CCA_APKA_MKVP).unwrap(); if !re_cca_apka_mkvp.is_match(&mkvps) { return Err(parse_error!(format!("APQN {queuedir} MKVPs"), mkvps)); } else { let caps = re_cca_apka_mkvp.captures(&mkvps).unwrap(); if caps.get(1).unwrap().as_str().to_lowercase() == "valid" { apka_mkvp = caps.get(2).unwrap().as_str().to_lowercase(); if apka_mkvp.starts_with("0x") { apka_mkvp = String::from(&apka_mkvp[2..]); } } } Ok(Self::Cca(ApqnInfoCca { serialnr, mkvp_aes: aes_mkvp, mkvp_apka: apka_mkvp, })) } fn ep11_info(carddir: &str, queuedir: &str) -> Result { let serialnr_str = read_file_string(format!("{carddir}/serialnr"), "serialnr")?; let serialnr = serialnr_str.trim().to_string(); let mkvps = read_file_string(format!("{carddir}/{queuedir}/mkvps"), "mkvps")?; let mut mkvp = String::new(); let re_ep11_mkvp = Regex::new(RE_EP11_MKVP).unwrap(); if !re_ep11_mkvp.is_match(&mkvps) { return Err(parse_error!(format!("APQN {queuedir} MKVPs"), mkvps)); } else { let caps = re_ep11_mkvp.captures(&mkvps).unwrap(); if caps.get(1).unwrap().as_str().to_lowercase() == "valid" { mkvp = caps.get(2).unwrap().as_str().to_lowercase(); if mkvp.starts_with("0x") { mkvp = String::from(&mkvp[2..]); } if mkvp.len() > 32 { mkvp = String::from(&mkvp[..32]) } } } Ok(Self::Ep11(ApqnInfoEp11 { serialnr, mkvp })) } /// Get mode-specific info pub fn info(mode: &ApqnMode, carddir: &str, queuedir: &str) -> Result { match mode { ApqnMode::Accel => Self::accel_info(carddir, queuedir), ApqnMode::Cca => Self::cca_info(carddir, queuedir), ApqnMode::Ep11 => Self::ep11_info(carddir, queuedir), } } } /// `Apqn` encodes an adjunct processor queue number. #[derive(Debug, Clone)] pub struct Apqn { /// Name of the APQN #[allow(dead_code)] pub name: String, /// Card number pub card: u32, /// Domain number pub domain: u32, /// CryptoExpress generation pub gen: u32, /// Mode that adapter is configured to use pub mode: ApqnMode, /// Mode-specific info pub info: Option, } impl TryFrom<&str> for Apqn { type Error = Error; /// Create an `Apqn` struct from a CARD.DOMAIN-formatted APQN /// string, such as `28.0014`. Will not populate `info` upon /// failure to read it. Other failures to read required information /// are treated as an Error. /// # Panics /// Panics if the compilation of a static regular expression fails /// or a regex capture that is already format-checked does not /// parse, e.g. when the capture `([[:xdigit:]]{2})` does not /// parse as hex string. fn try_from(name: &str) -> Result { let re_card_type = Regex::new(RE_CARD_TYPE).unwrap(); let re_queue_dir = Regex::new(RE_QUEUE_DIR).unwrap(); let caps = re_queue_dir .captures(name) .ok_or_else(|| parse_error!("queue".to_string(), name.to_string()))?; let cardstr = caps.get(1).unwrap().as_str(); let card = u32::from_str_radix(cardstr, 16).unwrap(); let domstr = caps.get(2).unwrap().as_str(); let domain = u32::from_str_radix(domstr, 16).unwrap(); let path = format!("{PATH_SYS_DEVICES_AP}/card{cardstr}"); let card_type = read_file_string(format!("{path}/type"), "card type").map(|s| s.trim().to_string())?; let caps = re_card_type .captures(&card_type) .ok_or_else(|| parse_error!("card type".to_string(), card_type.to_string()))?; let gen = caps.get(1).unwrap().as_str().parse::().unwrap(); let mode = match caps.get(2).unwrap().as_str().parse::().unwrap() { 'A' => ApqnMode::Accel, 'C' => ApqnMode::Cca, 'P' => ApqnMode::Ep11, _ => unreachable!("Code inconsistency between regex RE_CARD_TYPE and evaluation code."), }; // the UV blocks requests to CCA cards within SE guest with AP // pass-through support. However, filter out CCA cards as // these cards cause hangs during information gathering. if mode == ApqnMode::Cca && pv_guest_bit_set() { return Err(Error::CcaSeIncompatible(card)); } match read_file_string(format!("{path}/{name}/online"), "AP queue online status") .map(|s| s.trim().parse::()) { Ok(Ok(1)) => {} _ => return Err(Error::ApOffline { card, domain }), } // For the MKVP and serialnr to fetch from the APQN within a SE // guest the APQN needs to be bound to the guest. So if the APQN // is not bound, temporarily bind it here until the info has // been retrieved. let mut tempbound = false; if pv_guest_bit_set() { let cbs = get_apqn_bind_state(card, domain)?; if cbs == BindState::Unbound { set_apqn_bind_state(card, domain, BindState::Bound)?; tempbound = true; } } let info = ApqnInfo::info(&mode, &path, name).ok(); if tempbound { set_apqn_bind_state(card, domain, BindState::Unbound)?; } Ok(Apqn { name: name.to_string(), card, domain, gen, mode, info, }) } } impl fmt::Display for Apqn { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "({},{})", self.card, self.domain) } } impl Apqn { /// Read bind state of the APQN. pub fn bind_state(&self) -> Result { get_apqn_bind_state(self.card, self.domain) } /// Set bind state of the APQN. pub fn set_bind_state(&self, state: BindState) -> Result<()> { set_apqn_bind_state(self.card, self.domain, state) } /// Read associate state of the APQN. pub fn associate_state(&self) -> Result { get_apqn_associate_state(self.card, self.domain) } /// Set associate state of the APQN. pub fn set_associate_state(&self, state: AssocState) -> Result<()> { set_apqn_associate_state(self.card, self.domain, state) } } /// Bind state of an APQN #[derive(Debug, PartialEq, Eq)] pub enum BindState { /// APQN is bound Bound, /// APQN is unbound Unbound, /// APQN does not support bind NotSupported, } /// Query bind state for this APQN. /// /// Returns a BindState enum as defined above or on failure /// an error string. Does NOT print any error messages. pub fn get_apqn_bind_state(card: u32, dom: u32) -> Result { let path = format!( "{}/card{:02x}/{:02x}.{:04x}/se_bind", PATH_SYS_DEVICES_AP, card, card, dom ); let state_str = read_file_string(path, "se_bind attribute")?; let state = state_str.trim(); match state { "bound" => Ok(BindState::Bound), "unbound" => Ok(BindState::Unbound), "-" => Ok(BindState::NotSupported), _ => Err(Error::UnknownBindState(state.to_string())), } } /// Bind or unbind an APQN. /// /// The action is determined by the BindState given in. /// But of course only Bound and Unbound is supported - otherwise /// this function panics! /// The function actively loops over the bind state until /// the requested bind state is reached or a timeout has /// occurred (SYS_BUS_AP_BIND_TIMEOUT_MS). /// On success () is returned, on failure an error string /// is returned. Does NOT print any error messages. /// # Panics /// Panics if a desired bind state other than Bound or Unbound is given. pub fn set_apqn_bind_state(card: u32, dom: u32, state: BindState) -> Result<()> { let ctx = "bind APQN"; let path = format!( "{}/card{:02x}/{:02x}.{:04x}/se_bind", PATH_SYS_DEVICES_AP, card, card, dom ); match state { BindState::Bound => write_file(path, 1.to_string(), ctx), BindState::Unbound => write_file(path, 0.to_string(), ctx), _ => panic!("set_apqn_bind_state called with invalid BindState."), }?; let mut ms: u64 = 0; loop { thread::sleep(time::Duration::from_millis(SYS_BUS_AP_BIND_POLL_MS)); ms += SYS_BUS_AP_BIND_POLL_MS; if ms >= SYS_BUS_AP_BIND_TIMEOUT_MS { break Err(Error::Timeout(format!( "setting APQN({card},{dom}) bind state" ))); } let newstate = get_apqn_bind_state(card, dom)?; if newstate == state { return Ok(()); } } } /// Association state of an APQN #[derive(Debug, PartialEq, Eq)] pub enum AssocState { /// Associated with index Associated(u16), /// Association pending AssociationPending, /// Not associated Unassociated, /// APQN does not support association NotSupported, } /// Query association state for this APQN. /// /// Returns an AssocState enum as defined above or on failure /// an error string. Does NOT print any error messages. pub fn get_apqn_associate_state(card: u32, dom: u32) -> Result { let path = format!( "{}/card{:02x}/{:02x}.{:04x}/se_associate", PATH_SYS_DEVICES_AP, card, card, dom ); let state_str = read_file_string(path, "se_associate attribute")?; let state = state_str.trim(); match state.strip_prefix("associated ") { Some(prefix) => Ok(AssocState::Associated(prefix.parse()?)), _ => match state { "association pending" => Ok(AssocState::AssociationPending), "unassociated" => Ok(AssocState::Unassociated), "-" => Ok(AssocState::NotSupported), _ => Err(Error::UnknownAssocState(state.to_string())), }, } } fn set_apqn_associate_state_associate(card: u32, dom: u32, idx: u16) -> Result<()> { let path = format!( "{}/card{:02x}/{:02x}.{:04x}/se_associate", PATH_SYS_DEVICES_AP, card, card, dom ); write_file(path, idx.to_string(), "associate APQN")?; let mut ms: u64 = 0; loop { thread::sleep(time::Duration::from_millis(SYS_BUS_AP_ASSOC_POLL_MS)); ms += SYS_BUS_AP_ASSOC_POLL_MS; if ms >= SYS_BUS_AP_ASSOC_TIMEOUT_MS { break Err(Error::Timeout(format!( "setting APQN({card},{dom}) association index {idx} state", ))); } match get_apqn_associate_state(card, dom)? { AssocState::Associated(i) if i == idx => return Ok(()), AssocState::Associated(i) => { return Err(Error::WrongAssocState { card, domain: dom, desired: idx, actual: i, }) } _ => {} } } } fn set_apqn_associate_state_unbind(card: u32, dom: u32) -> Result<()> { let bindpath = format!( "{}/card{:02x}/{:02x}.{:04x}/se_bind", PATH_SYS_DEVICES_AP, card, card, dom ); write_file(bindpath, 0.to_string(), "unbind APQN")?; let mut ms: u64 = 0; loop { thread::sleep(time::Duration::from_millis(SYS_BUS_AP_ASSOC_POLL_MS)); ms += SYS_BUS_AP_ASSOC_POLL_MS; if ms >= SYS_BUS_AP_ASSOC_TIMEOUT_MS { break Err(Error::Timeout(format!( "setting APQN({card},{dom}) association unbind state", ))); } let newstate = get_apqn_associate_state(card, dom)?; if newstate == AssocState::Unassociated { return Ok(()); } } } /// Associate or Unassociate an APQN. /// /// The action is determined by the AssocState given in. /// But of course only Associated and Unassociated is supported /// otherwise this function panics! /// The function actively loops over the association state until /// the requested state is reached or a timeout has /// occurred (SYS_BUS_AP_ASSOC_TIMEOUT_MS). /// The unassociate is in fact a unbind. So the code triggers /// an unbind and then loops over the sysfs se_associate until /// "unassociated" is reached. /// On success () is returned, on failure an error string /// is returned. Does NOT print any error messages. /// # Panics /// Panics if a desired bind state other than Associated or /// Unassociated is given. pub fn set_apqn_associate_state(card: u32, dom: u32, state: AssocState) -> Result<()> { match state { AssocState::Associated(idx) => set_apqn_associate_state_associate(card, dom, idx), AssocState::Unassociated => set_apqn_associate_state_unbind(card, dom), _ => panic!("set_apqn_associate_state called with invalid AssocState."), } } s390-tools-2.38.0/rust/pv_core/src/confidential.rs000066400000000000000000000176151502674226300217070ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023, 2024 use std::fmt::Debug; use crate::Error; /// Trait for securely zeroizing memory. /// /// To be used with [`Confidential`] pub trait Zeroize { /// Reliably overwrites the given buffer with zeros, fn zeroize(&mut self); } macro_rules! integer_impl { ($typ:ty) => { impl Zeroize for $typ { /// Reliably overwrites the given number with zeros, /// by performing a volatile write followed by a memory barrier fn zeroize(&mut self) { unsafe { std::ptr::write_volatile(self as *mut $typ, <$typ>::default()); } std::sync::atomic::compiler_fence(std::sync::atomic::Ordering::SeqCst); } } }; } integer_impl!(u8); integer_impl!(u16); integer_impl!(u32); integer_impl!(u64); integer_impl!(u128); integer_impl!(usize); integer_impl!(i8); integer_impl!(i16); integer_impl!(i32); integer_impl!(i64); integer_impl!(i128); integer_impl!(isize); // Automatically impl Zeroize for u8 arrays impl Zeroize for [T; COUNT] { /// Reliably overwrites the given buffer with zeros, /// by performing a volatile write followed by a memory barrier fn zeroize(&mut self) { let mut dst = self.as_mut_ptr(); for _ in 0..self.len() { // SAFETY: // * Array allocated len elements continuously // * dst points always to a valid location unsafe { std::ptr::write_volatile(dst, T::default()); dst = dst.add(1); } } std::sync::atomic::compiler_fence(std::sync::atomic::Ordering::SeqCst); } } impl Zeroize for Vec { /// Reliably overwrites the given buffer with zeros, /// by overwriting the whole vector's capacity with zeros. fn zeroize(&mut self) { let mut dst = self.as_mut_ptr(); for _ in 0..self.capacity() { // SAFETY: // * Vec allocated at least capacity elements continuously // * dst points always to a valid location unsafe { std::ptr::write_volatile(dst, T::default()); dst = dst.add(1); } } std::sync::atomic::compiler_fence(std::sync::atomic::Ordering::SeqCst); } } impl Zeroize for String { fn zeroize(&mut self) { // SAFETY: The Vec zerorize function overwrites memory with the zero byte -> still valid UTF-8 unsafe { self.as_mut_vec().zeroize() }; } } /// Thin wrapper around an type implementing Zeroize. /// /// A `Confidential` represents a confidential value that must be securely overwritten during drop. /// Will never leak its wrapped value during [`Debug`] /// /// ```rust /// # use s390_pv_core::request::Confidential; /// fn foo(value: Confidential<[u8; 2]>) { /// println!("value: {value:?}"); /// } /// # fn main() { /// foo([1, 2].into()); /// // prints: /// // in debug builds: /// // value: Confidential([1, 2]) /// // in release builds: /// // value: Confidential(***) /// # } /// ``` #[derive(Clone, PartialEq, Eq, Default)] pub struct Confidential(C); impl Confidential { /// Convert a type into a self overwriting one. /// /// Prefer using [`Into`] pub fn new(v: C) -> Self { Self(v) } /// Get a reference to the contained value pub fn value(&self) -> &C { &self.0 } /// Get a mutable reference to the contained value /// /// NOTE that modifications to a mutable reference can trigger reallocation. /// e.g. a [`Vec`] might expand if more space needed. -> preallocate enough space /// or operate on slices. The old locations can and will **NOT** be zeroized. pub fn value_mut(&mut self) -> &mut C { &mut self.0 } } impl Confidential { /// Consume the [`Confidential`] into its contained type as a clone. /// /// This disables any cleanups for the result. pub fn into_inner(self) -> C { // The clone is required because drop is implemented (E0509) self.0.clone() } } impl Debug for Confidential { #[allow(unreachable_code)] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // do NOT leak secrets in production builds #[cfg(not(debug_assertions))] return write!(f, "Confidential(***)"); let mut b = f.debug_tuple("Confidential"); b.field(&self.0); b.finish() } } impl From for Confidential { fn from(v: C) -> Self { Self(v) } } impl Zeroize for Confidential { fn zeroize(&mut self) { self.0.zeroize(); } } impl Drop for Confidential { fn drop(&mut self) { self.0.zeroize(); } } impl TryFrom>> for Confidential<[u8; N]> { type Error = Error; fn try_from(value: Confidential>) -> Result { let len = value.0.len(); if len == N { Ok(Self::new( TryInto::<[u8; N]>::try_into(value.0.clone()).unwrap(), )) } else { Err(Error::LengthMismatch { expected: N, actual: len, }) } } } impl From> for Confidential> { fn from(value: Confidential<[u8; N]>) -> Self { Self::new(value.0.to_vec()) } } #[cfg(test)] mod test { use super::*; #[derive(Debug, Default, PartialEq, Eq)] struct DummyStruct([u32; 8]); #[test] fn array() { let mut conf = Confidential::new([17; 42]); assert_eq!(&[17; 42], conf.value()); conf.zeroize(); assert_eq!(&[0; 42], conf.value()); let mut conf2 = Confidential::new([DummyStruct([0x12u32; 8]), DummyStruct([0x24u32; 8])]); assert_eq!( &[DummyStruct([0x12u32; 8]), DummyStruct([0x24u32; 8])], conf2.value() ); conf2.zeroize(); assert_eq!( &[DummyStruct([0x0u32; 8]), DummyStruct([0x0u32; 8])], conf2.value() ); } #[test] fn vec() { let mut conf = Confidential::new(vec![17; 42]); conf.zeroize(); assert_eq!(&[0; 42], conf.value().as_slice()); let mut conf2 = Confidential::new(vec![DummyStruct([0x12u32; 8]), DummyStruct([0x24u32; 8])]); assert_eq!( &[DummyStruct([0x12u32; 8]), DummyStruct([0x24u32; 8])], conf2.value().as_slice() ); conf2.zeroize(); assert_eq!( &[DummyStruct([0x0u32; 8]), DummyStruct([0x0u32; 8])], conf2.value().as_slice() ); } #[test] fn string() { let mut conf = Confidential::new("test".to_string()); conf.zeroize(); assert_eq!(&[0; 4], conf.value().as_bytes()); } #[test] fn try_from_conf_vec_into_conf_array() { let _: Confidential<[u8; 0]> = Confidential::new(vec![]) .try_into() .expect("should not fail"); let data = vec![0x12u8; 100]; let arr: Confidential<[u8; 100]> = Confidential::new(data.clone()) .try_into() .expect("should not fail"); assert_eq!(arr.value(), data.as_slice()); let result: Result, Error> = Confidential::new(data.clone()).try_into(); assert!(matches!( result, Err(Error::LengthMismatch { expected: 101, actual: 100 }) )); } #[test] fn try_from_conf_array_into_conf_vec() { let _: Confidential> = Confidential::new([]).into(); let data = [0x12u8; 100]; let vec: Confidential> = Confidential::new(data).into(); assert_eq!(vec.value(), data.as_slice()); } } s390-tools-2.38.0/rust/pv_core/src/error.rs000066400000000000000000000074061502674226300203760ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023, 2024 use std::path::PathBuf; use crate::uv::SecretId; /// Result type for this crate pub type Result = std::result::Result; /// Error cases for this crate #[allow(missing_docs)] #[derive(thiserror::Error, Debug)] #[non_exhaustive] pub enum Error { #[error("Ultravisor: '{msg}' ({rc:#06x},{rrc:#06x})")] Uv { rc: u16, rrc: u16, msg: &'static str, }, #[error("{0}")] Specification(String), #[error("Cannot {ty} {ctx} at `{path}`")] FileIo { ty: FileIoErrorType, ctx: String, path: PathBuf, source: std::io::Error, }, #[error("Cannot {ty} `{path}`")] FileAccess { ty: FileAccessErrorType, path: PathBuf, source: std::io::Error, }, #[error("Cannot rename '{src}' to '{dst}'")] FileAccessRename { src: String, dst: String, source: std::io::Error, }, #[error("Cannot encode secrets (Too many secrets)")] ManySecrets, #[error("Cannot decode secret list")] InvSecretList(#[source] std::io::Error), #[error("Input does not contain an add-secret request")] NoAsrcb, #[error("Input add-secret request is larger than 8k")] AscrbLarge, #[error("Input contains unsupported user-data type: {0:#06x}")] UnsupportedUserData(u16), #[error("The input has not the correct format: {field} is too large. Maximal size {max_size}")] AttDataSizeLarge { field: &'static str, max_size: u32 }, #[error("The input has not the correct format: {field} is too small. Minimal size {min_size}")] AttDataSizeSmall { field: &'static str, min_size: u32 }, #[error("The attestation request has an unknown algorithm type (.0)")] BinArcbInvAlgorithm(u32), #[error("The attestation request does not specify a measurement size or measurement data.")] BinArcbNoMeasurement, #[error( "The secret with the ID {id} cannot be retrieved. The requested size is too large ({size})" )] InvalidRetrievableSecretType { id: SecretId, size: usize }, #[error("Unknown bind state '{0}'.")] UnknownBindState(String), #[error("Unknown association state '{0}'.")] UnknownAssocState(String), #[error( "APQN({card:02x},{domain:04x}) is associated with {actual} but it should be {desired}." )] WrongAssocState { card: u32, domain: u32, desired: u16, actual: u16, }, #[error("Timeout on {0}.")] Timeout(String), #[error( "CCA card {0:02x} cannot be used with Secure Execution, as this combination is unsupported" )] CcaSeIncompatible(u32), #[error("APQN({card:02x}{domain:04x}) is offline.")] ApOffline { card: u32, domain: u32 }, #[error("Failure parsing {subject} '{content}'.")] ParseError { subject: String, content: String }, // errors from other crates #[error(transparent)] Io(#[from] std::io::Error), #[error(transparent)] ParseInt(#[from] std::num::ParseIntError), #[error("Cannot decode hex string: Size {0} is not a multiple of two")] InvHexStringSize(usize), #[error("Cannot decode hex string")] InvHexStringChar { source: std::num::ParseIntError }, #[error("Expected size {expected}, actual {actual}")] LengthMismatch { expected: usize, actual: usize }, } /// Error cases for I/O operations #[derive(thiserror::Error, Debug)] #[non_exhaustive] #[allow(missing_docs)] pub enum FileIoErrorType { #[error("read")] Read, #[error("write")] Write, } /// Error cases for accessing files #[derive(thiserror::Error, Debug)] #[non_exhaustive] #[allow(missing_docs)] pub enum FileAccessErrorType { #[error("open")] Open, #[error("create")] Create, } s390-tools-2.38.0/rust/pv_core/src/lib.rs000066400000000000000000000065571502674226300200210ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023, 2024 #![doc = include_str!("../README.md")] mod apdevice; mod confidential; mod error; mod macros; mod utils; mod uvattest; mod uvdevice; mod uvsecret; pub use error::{Error, FileAccessErrorType, FileIoErrorType, Result}; /// Functionalities for reading attestation requests pub mod attest { pub use crate::uvattest::{AttestationMagic, AttestationMeasAlg}; } /// Miscellaneous functions and definitions pub mod misc { pub use crate::utils::pv_guest_bit_set; pub use crate::utils::{create_file, open_file}; pub use crate::utils::{decode_hex, encode_hex, parse_hex}; pub use crate::utils::{read, write}; pub use crate::utils::{read_exact_file, read_file, read_file_string, write_file}; pub use crate::utils::{to_u16, to_u32, try_parse_u128, try_parse_u64}; pub use crate::utils::{Flags, Lsb0Flags64, Msb0Flags64}; } /// Definitions and functions for interacting with the Ultravisor /// /// For detailed Information on how to send Ultravisor Commands see [`crate::uv::UvDevice`] and /// [`crate::uv::UvCmd`] pub mod uv { pub use crate::uvdevice::attest::AttestationCmd; pub use crate::uvdevice::retr_secret::RetrievableSecret; pub use crate::uvdevice::retr_secret::{AesSizes, AesXtsSizes, EcCurves, HmacShaSizes}; pub use crate::uvdevice::secret::{AddCmd, ListCmd, LockCmd, RetrieveCmd}; pub use crate::uvdevice::secret_list::{ListableSecretType, SecretEntry, SecretId, SecretList}; pub use crate::uvdevice::{ConfigUid, UvCmd, UvDevice, UvDeviceInfo, UvFlags, UvcSuccess}; } /// Functionalities to verify UV requests pub mod request { pub use crate::confidential::{Confidential, Zeroize}; /// Version number of the request in system endianness pub type RequestVersion = u32; /// Request magic value /// /// The first 8 byte of a request providing an identifier of the request type /// for programs pub type RequestMagic = [u8; 8]; /// A `MagicValue` is a byte pattern, that indicates if a byte slice contains the specified /// (binary) data. pub trait MagicValue { /// Magic value as byte array const MAGIC: [u8; N]; /// Test whether the given slice starts with the magic value. fn starts_with_magic(v: &[u8]) -> bool { if v.len() < Self::MAGIC.len() { return false; } v[..Self::MAGIC.len()] == Self::MAGIC } } } /// Functionalities for reading add-secret requests pub mod secret { pub use crate::uvsecret::AddSecretMagic; pub use crate::uvsecret::UserDataType; } /// Functionalities for the AP bus pub mod ap { pub use crate::apdevice::Apqn; pub use crate::apdevice::RE_QUEUE_DIR; pub use crate::apdevice::{get_apqn_bind_state, set_apqn_bind_state}; /// AP modes pub mod apqn_mode { pub use crate::apdevice::ApqnMode::{self, *}; } /// AP info for each state pub mod apqn_info { pub use crate::apdevice::ApqnInfo::{self, *}; pub use crate::apdevice::{ApqnInfoAccel, ApqnInfoCca, ApqnInfoEp11}; } /// AP bind states pub mod bind_state { pub use crate::apdevice::BindState::{self, *}; } /// AP association states pub mod assoc_state { pub use crate::apdevice::AssocState::{self, *}; } } // Internal definitions/ imports const PAGESIZE: usize = 0x1000; s390-tools-2.38.0/rust/pv_core/src/macros.rs000066400000000000000000000025171502674226300205270ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023, 2024 macro_rules! file_error { ($ty: tt, $ctx: expr, $path:expr, $src: expr) => { $crate::Error::FileIo { ty: $crate::FileIoErrorType::$ty, ctx: $ctx.to_string(), path: $path.as_ref().to_path_buf(), source: $src, } }; } pub(crate) use file_error; macro_rules! bail_spec { ($str: expr) => { return Err($crate::Error::Specification($str.to_string())) }; } pub(crate) use bail_spec; /// Asserts a constant expression evaluates to `true`. /// /// If the expression is not evaluated to `true` the compilation will fail. #[macro_export] macro_rules! static_assert { ($condition:expr) => { const _: () = core::assert!($condition); }; } /// Asserts that a type has a specific size. /// /// Useful to validate structs that are passed to C code. /// If the size has not the expected value the compilation will fail. /// /// # Example /// ```rust /// # use s390_pv_core::assert_size; /// # fn main() {} /// #[repr(C)] /// struct c_struct { /// v: u64, /// } /// assert_size!(c_struct, 8); /// // assert_size!(c_struct, 7);//won't compile /// ``` #[macro_export] macro_rules! assert_size { ($t:ty, $sz:expr ) => { $crate::static_assert!(::std::mem::size_of::<$t>() == $sz); }; } s390-tools-2.38.0/rust/pv_core/src/utils.rs000066400000000000000000000452621502674226300204070ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 use std::{ fs::File, io::{Read, Write}, path::Path, }; use zerocopy::{BigEndian, FromBytes, Immutable, IntoBytes, U64}; use crate::{ macros::{bail_spec, file_error}, Error, FileAccessErrorType, Result, }; /// Trait that describes bitflags, represented by `T`. pub trait Flags: From + for<'a> From<&'a T> { /// Set the specified bit to one. /// # Panics /// Panics if bit is >= 64 fn set_bit(&mut self, bit: u8); /// Set the specified bit to zero. /// # Panics /// Panics if bit is >= 64 fn unset_bit(&mut self, bit: u8); /// Test if the specified bit is set. /// # Panics /// Panics if bit is >= 64 fn is_set(&self, bit: u8) -> bool; } /// Bitflags in MSB0 ordering /// /// Wraps an u64 to set/get individual bits #[repr(C)] #[derive(Debug, Clone, Copy, Default, IntoBytes, FromBytes, Eq, PartialEq, Immutable)] pub struct Msb0Flags64(U64); impl Flags for Msb0Flags64 { #[track_caller] fn set_bit(&mut self, bit: u8) { assert!(bit < 64, "Flag bit set to greater than 63"); let mut v = self.0.get(); v |= 1 << (63 - bit); self.0.set(v) } #[track_caller] fn unset_bit(&mut self, bit: u8) { assert!(bit < 64, "Flag bit set to greater than 63"); let mut v = self.0.get(); v &= !(1 << (63 - bit)); self.0.set(v) } #[track_caller] fn is_set(&self, bit: u8) -> bool { assert!(bit < 64, "Flag bit set to greater than 63"); self.0.get() & (1 << (63 - bit)) > 0 } } impl From for Msb0Flags64 { fn from(value: u64) -> Self { Self(value.into()) } } impl From<&u64> for Msb0Flags64 { fn from(value: &u64) -> Self { (*value).into() } } impl From for u64 { fn from(value: Msb0Flags64) -> Self { value.0.into() } } /// Bitflags in LSB0 ordering /// /// Wraps an u64 to set/get individual bits #[repr(C)] #[derive(Debug, Clone, Copy, Default, IntoBytes, FromBytes, Immutable)] pub struct Lsb0Flags64(U64); impl Flags for Lsb0Flags64 { #[track_caller] fn set_bit(&mut self, bit: u8) { assert!(bit < 64, "Flag bit set to greater than 63"); let mut v = self.0.get(); v |= 1 << bit; self.0.set(v) } #[track_caller] fn unset_bit(&mut self, bit: u8) { assert!(bit < 64, "Flag bit set to greater than 63"); let mut v = self.0.get(); v &= !(1 << bit); self.0.set(v) } #[track_caller] fn is_set(&self, bit: u8) -> bool { assert!(bit < 64, "Flag bit set to greater than 63"); self.0.get() & (1 << bit) > 0 } } impl From for Lsb0Flags64 { fn from(value: u64) -> Self { Self(value.into()) } } impl From<&u64> for Lsb0Flags64 { fn from(value: &u64) -> Self { (*value).into() } } impl From for u64 { fn from(value: Lsb0Flags64) -> Self { value.0.into() } } /// Tries to convert a BE hex string into a 128 unsigned integer /// The hexstring must contain 32chars of hexdigits /// /// * `hex_str` - string to convert can be prepended with "0x" /// * `ctx` - Error context string in case of an error /// ```rust /// # use std::error::Error; /// # use s390_pv_core::misc::try_parse_u128; /// # fn main() -> Result<(), Box> { /// let hex = "11223344556677889900aabbccddeeff"; /// try_parse_u128(&hex, "The test")?; /// # Ok(()) /// # } /// ``` /// /// # Errors /// If `hex_string` is not a 32 byte hex string an Error appears pub fn try_parse_u128(hex_str: &str, ctx: &str) -> Result<[u8; 16]> { let hex_str = if hex_str.starts_with("0x") { hex_str.split_at(2).1 } else { hex_str }; if hex_str.len() != 32 { bail_spec!(format!( "{ctx} hexstring must be 32chars long to cover all 16 bytes" )); } decode_hex(hex_str)?.try_into().map_err(|_| { Error::Specification(format!( "{ctx} hexstring must be 32chars long to cover all 16 bytes" )) }) } /// Tries to convert a BE hex string into a 64 unsigned integer /// The hexstring must *NOT* contain 16 chars of hexdigits, but /// 16 chars at most. /// /// * `hex_str` - string to convert can be prepended with "0x" /// * `ctx` - Error context string in case of an error /// ```rust /// # use std::error::Error; /// # use s390_pv_core::misc::try_parse_u64; /// # fn main() -> Result<(), Box> { /// let hex = "1234567890abcdef"; /// try_parse_u64(&hex, "The test")?; /// # Ok(()) /// # } /// ``` /// /// # Errors /// If `hex_string` is not a 32 byte hex string an Error appears pub fn try_parse_u64(hex_str: &str, ctx: &str) -> Result { let hex_str = if hex_str.starts_with("0x") { hex_str.split_at(2).1 } else { hex_str }; if hex_str.len() > 16 { bail_spec!(format!( "{ctx} hexstring {hex_str} must be max 16 chars long" )); } Ok(u64::from_str_radix(hex_str, 16)?) } /// Open a file. /// /// Wraps [`File::open`] /// /// * `path` - Path to file /// /// # Errors /// /// This function will return errors according to [`File::open`]. pub fn open_file>(path: P) -> Result { File::open(&path).map_err(|e| Error::FileAccess { ty: FileAccessErrorType::Open, path: path.as_ref().to_path_buf(), source: e, }) } /// Create a file. /// /// Wraps [`File::create`] /// /// * `path` - Path to file /// /// # Errors /// /// This function will return errors according to [`File::create`]. pub fn create_file>(path: P) -> Result { File::create(&path).map_err(|e| Error::FileAccess { ty: FileAccessErrorType::Create, path: path.as_ref().to_path_buf(), source: e, }) } /// Read exactly COUNT bytes into a buffer and return it. /// /// * `path` - Path to file /// * `ctx` - Error context string in case of an error /// /// # Errors /// If this function encounters an "end of file" before completely filling /// the buffer, it returns an error. The contents of `buf` are unspecified in this case. /// /// If any other read error occurs, this function returns immediately. The /// contents of `buf` are unspecified in this case. /// /// If this function returns an error, it is unspecified how many bytes it /// has read, but it will never read more than would be necessary to /// completely fill the buffer. pub fn read_exact_file, const COUNT: usize>( path: P, ctx: &str, ) -> Result<[u8; COUNT]> { let mut f = File::open(&path).map_err(|e| Error::FileAccess { ty: FileAccessErrorType::Open, path: path.as_ref().to_path_buf(), source: e, })?; if f.metadata()?.len() as usize != COUNT { bail_spec!(format!("{ctx} must be exactly {COUNT} bytes long")); } let mut buf = [0; COUNT]; f.read_exact(&mut buf) .map_err(|e| file_error!(Read, ctx, path, e))?; Ok(buf) } /// Read content from a file as string and add context in case of an error /// /// * `path` - Path to file /// * `ctx` - Error context string in case of an error /// /// # Errors /// Passes through any kind of error `std::fs::read` produces pub fn read_file_string>(path: P, ctx: &str) -> Result { std::fs::read_to_string(&path).map_err(|e| file_error!(Read, ctx, path, e)) } /// Read content from a file and add context in case of an error /// /// * `path` - Path to file /// * `ctx` - Error context string in case of an error /// /// /// # Errors /// Passes through any kind of error `std::fs::read` produces pub fn read_file>(path: P, ctx: &str) -> Result> { std::fs::read(&path).map_err(|e| file_error!(Read, ctx, path, e)) } /// Reads all content from a [`std::io::Read`] and add context in case of an error /// /// * `path` - Path to file /// * `ctx` - Error context string in case of an error /// /// /// # Errors /// Passes through any kind of error `std::fs::read` produces pub fn read>(rd: &mut R, path: P, ctx: &str) -> Result> { let mut buf = vec![]; rd.read_to_end(&mut buf) .map_err(|e| file_error!(Read, ctx, path, e))?; Ok(buf) } /// write content to a file and add context in case of an error /// /// * `path` - Path to file /// * `ctx` - Error context string in case of an error /// /// /// # Errors /// Passes through any kind of error `std::fs::write` produces pub fn write_file, P: AsRef>(path: P, data: D, ctx: &str) -> Result<()> { std::fs::write(path.as_ref(), data.as_ref()).map_err(|e| file_error!(Write, ctx, path, e)) } /// Write content to a [`std::io::Write`] and add context in case of an error /// /// * `path` - Path to file /// * `ctx` - Error context string in case of an error /// /// /// # Errors /// Passes through any kind of error `std::fs::write` produces pub fn write, P: AsRef, W: Write>( wr: &mut W, data: D, path: P, ctx: &str, ) -> Result<()> { wr.write_all(data.as_ref()) .map_err(|e| file_error!(Write, ctx, path, e)) } macro_rules! usize_to_ui { ($(#[$attr:meta])* => $t: ident, $name:ident) => { ///Converts an [`usize`] to an [` $(#[$attr])* ///`] if possible pub fn $name(u: usize) -> Option<$t> { if u > $t::MAX as usize { None } else { Some(u as $t) } } } } usize_to_ui! { #[doc = r"u32"] => u32, to_u32} usize_to_ui! { #[doc = r"u16"] => u16, to_u16} /// Converts the u8 slice into (lowercase) hexstring pub fn encode_hex>(s: S) -> String { let slice = s.as_ref(); let string = String::with_capacity(2 * slice.len()); slice .iter() .fold(string, |acc, e| acc + &format!("{e:02x}")) } /// Converts the hexstring into a byte vector. /// /// # Errors /// /// Raises an error if a non-hex character was found or the length was not a /// multiple of two. pub fn decode_hex>(s: S) -> Result> { let hex = s.as_ref(); let hex_len = hex.len(); if hex_len % 2 != 0 { return Err(Error::InvHexStringSize(hex_len)); } (0..hex_len) .step_by(2) .map(|i| { u8::from_str_radix(&hex[i..i + 2], 16) .map_err(|err| Error::InvHexStringChar { source: err }) }) .collect() } /// Converts the hexstring into a byte vector. /// /// Stops if the end or until a non hex chat is found pub fn parse_hex(hex_str: &str) -> Vec { let mut hex_bytes = hex_str.as_bytes().iter().map_while(|b| match b { b'0'..=b'9' => Some(b - b'0'), b'a'..=b'f' => Some(b - b'a' + 10), b'A'..=b'F' => Some(b - b'A' + 10), _ => None, }); let mut bytes = Vec::new(); while let (Some(h), Some(l)) = (hex_bytes.next(), hex_bytes.next()) { bytes.push(h << 4 | l) } bytes } /// Report if the `prot_virt_guest` sysfs entry is one. /// /// If the entry does not exist returns false. /// /// for non-s390-architectures: /// Returns always false /// A non-s390 system cannot be a secure execution guest. #[allow(unreachable_code)] pub fn pv_guest_bit_set() -> bool { #[cfg(not(target_arch = "s390x"))] return false; // s390 branch let v = std::fs::read("/sys/firmware/uv/prot_virt_guest").unwrap_or_else(|_| vec![0]); let v: u8 = String::from_utf8_lossy(&v[..1]).parse().unwrap_or(0); v == 1 } #[cfg(test)] mod tests { use super::*; #[test] fn msb_flags() { let v = 17; let v_flag: Msb0Flags64 = v.into(); assert_eq!(v, v_flag.0.get()); let mut v: Msb0Flags64 = 4.into(); v.unset_bit(61); assert_eq!(v.0.get(), 0); v.set_bit(61); assert_eq!(4, v.0.get()); let mut v = Msb0Flags64::default(); v.set_bit(0); assert_eq!(&[0x80, 0, 0, 0, 0, 0, 0, 0], v.as_bytes()); v.set_bit(0); assert_eq!(&[0x80, 0, 0, 0, 0, 0, 0, 0], v.as_bytes()); v.set_bit(1); assert_eq!(&[0xc0, 0, 0, 0, 0, 0, 0, 0], v.as_bytes()); v.set_bit(2); assert_eq!(&[0xe0, 0, 0, 0, 0, 0, 0, 0], v.as_bytes()); v.set_bit(3); assert_eq!(&[0xf0, 0, 0, 0, 0, 0, 0, 0], v.as_bytes()); v.unset_bit(3); assert_eq!(&[0xe0, 0, 0, 0, 0, 0, 0, 0], v.as_bytes()); v.unset_bit(3); assert_eq!(&[0xe0, 0, 0, 0, 0, 0, 0, 0], v.as_bytes()); v.set_bit(16); assert_eq!(&[0xe0, 0, 0x80, 0, 0, 0, 0, 0], v.as_bytes()); } #[test] #[should_panic] fn msb_flags_set_panic() { Msb0Flags64::default().set_bit(64) } #[test] #[should_panic] fn msb_flags_unset_panic() { Msb0Flags64::default().unset_bit(64) } #[test] fn lsb_flags() { let v = 17; let v_flag: Lsb0Flags64 = v.into(); assert_eq!(v, v_flag.0.get()); let mut v: Lsb0Flags64 = 4.into(); v.unset_bit(2); assert_eq!(v.0.get(), 0); v.set_bit(2); assert_eq!(4, v.0.get()); let mut v = Lsb0Flags64::default(); v.set_bit(0); assert_eq!(&[0, 0, 0, 0, 0, 0, 0, 1], v.as_bytes()); v.set_bit(0); assert_eq!(&[0, 0, 0, 0, 0, 0, 0, 1], v.as_bytes()); v.set_bit(1); assert_eq!(&[0, 0, 0, 0, 0, 0, 0, 3], v.as_bytes()); v.set_bit(2); assert_eq!(&[0, 0, 0, 0, 0, 0, 0, 7], v.as_bytes()); v.set_bit(3); assert_eq!(&[0, 0, 0, 0, 0, 0, 0, 0xf], v.as_bytes()); v.unset_bit(3); assert_eq!(&[0, 0, 0, 0, 0, 0, 0, 7], v.as_bytes()); v.unset_bit(3); assert_eq!(&[0, 0, 0, 0, 0, 0, 0, 7], v.as_bytes()); v.set_bit(16); assert_eq!(&[0, 0, 0, 0, 0, 1, 0, 7], v.as_bytes()); } #[test] #[should_panic] fn lsb_flags_set_panic() { Lsb0Flags64::default().set_bit(64) } #[test] #[should_panic] fn lsb_flags_unset_panic() { Lsb0Flags64::default().unset_bit(64) } #[test] fn encode_hex() { let arr = [0x12, 0x34, 0x56, 0xac, 0xbe, 0xf0]; let exp = "123456acbef0"; assert_eq!(super::encode_hex(arr), exp); } #[test] fn parse_hex() { let s = "123456acbef0"; let exp = vec![0x12, 0x34, 0x56, 0xac, 0xbe, 0xf0]; assert_eq!(super::parse_hex(s), exp); let s = "00123456acbef0"; let exp = vec![0, 0x12, 0x34, 0x56, 0xac, 0xbe, 0xf0]; assert_eq!(super::parse_hex(s), exp); let s = "00123456acbef0ii90"; let exp = vec![0, 0x12, 0x34, 0x56, 0xac, 0xbe, 0xf0]; assert_eq!(super::parse_hex(s), exp); } #[test] fn decode_hex() { let s = "123456acbef0"; let exp = vec![0x12, 0x34, 0x56, 0xac, 0xbe, 0xf0]; assert_eq!(super::decode_hex(s).expect("should not fail"), exp); let s = "00123456acbef0"; let exp = vec![0, 0x12, 0x34, 0x56, 0xac, 0xbe, 0xf0]; assert_eq!(super::decode_hex(s).expect("should not fail"), exp); let s = "00123456acbef0"; let exp = vec![0, 0x12, 0x34, 0x56, 0xac, 0xbe, 0xf0]; assert_eq!(super::decode_hex(s).expect("should not fail"), exp); assert_eq!( super::decode_hex("c0ffee").expect("should not fail"), [0xc0, 0xff, 0xee] ); assert_eq!(super::decode_hex("c0").expect("should not fail"), [0xc0]); assert_eq!(super::decode_hex("").expect("should not fail"), []); assert!(matches!( super::decode_hex(" "), Err(Error::InvHexStringSize(_)) )); assert!(matches!( super::decode_hex("coffee"), Err(Error::InvHexStringChar { .. }) )); assert!(matches!( super::decode_hex(" c0a"), Err(Error::InvHexStringChar { .. }) )); assert!(matches!( super::decode_hex("c0 a"), Err(Error::InvHexStringChar { .. }) )); assert!(matches!( super::decode_hex("c0a"), Err(Error::InvHexStringSize(_)) )); } #[test] fn to_u32() { assert_eq!(Some(17), super::to_u32(17)); assert_eq!(Some(0), super::to_u32(0)); assert_eq!(Some(u32::MAX), super::to_u32(u32::MAX as usize)); assert_eq!(None, super::to_u32(u32::MAX as usize + 1)); assert_eq!(None, super::to_u32(usize::MAX)); } #[test] fn parse_u128() { assert!(matches!( try_parse_u128("123456", ""), Err(Error::Specification(_)) )); assert!(matches!( try_parse_u128("-1234", ""), Err(Error::Specification(_)) )); assert!(matches!( try_parse_u128("0011223344556677889900aabbccddeeff", ""), Err(Error::Specification(_)) )); assert!(matches!( try_parse_u128("dd11223344556677889900aabbccddeeff", ""), Err(Error::Specification(_)) )); assert!(matches!( try_parse_u128("-1223344556677889900aabbccddeeff", ""), Err(Error::InvHexStringChar { .. }) )); assert!(matches!( try_parse_u128("0x123456", ""), Err(Error::Specification(_)) )); assert!(matches!( try_parse_u128("0x123", ""), Err(Error::Specification(_)) )); assert!(matches!( try_parse_u128("0x0011223344556677889900aabbccddeeff", ""), Err(Error::Specification(_)) )); assert!(matches!( try_parse_u128("0xdd11223344556677889900aabbccddeeff", ""), Err(Error::Specification(_)) )); assert!(matches!( try_parse_u128("0x-1223344556677889900aabbccddeeff", ""), Err(Error::InvHexStringChar { .. }) )); assert_eq!( [ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff ], try_parse_u128("11223344556677889900aabbccddeeff", "").unwrap() ); assert_eq!( [ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff ], try_parse_u128("0x11223344556677889900aabbccddeeff", "").unwrap() ); assert_eq!( [ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff ], try_parse_u128("00112233445566778899aabbccddeeff", "").unwrap() ); assert_eq!( [ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff ], try_parse_u128("00112233445566778899aabbccddeeff", "").unwrap() ); } } s390-tools-2.38.0/rust/pv_core/src/uvattest.rs000066400000000000000000000027511502674226300211220ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use crate::{request::MagicValue, Error}; use zerocopy::U32; use zerocopy::{BigEndian, ByteOrder}; /// The magic value used to identify an attestation request /// /// The magic value is ASCII: /// ```rust /// # use s390_pv_core::attest::AttestationMagic; /// # use s390_pv_core::request::MagicValue; /// # fn main() { /// # let magic = & /// [0u8; 8] /// # ; /// # assert!(AttestationMagic::starts_with_magic(magic)); /// # } /// ``` #[derive(Debug)] pub struct AttestationMagic; impl MagicValue<8> for AttestationMagic { const MAGIC: [u8; 8] = [0; 8]; } /// Identifier for the used measurement algorithm #[repr(u32)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum AttestationMeasAlg { /// Use HMAC with SHA512 as measurement algorithm HmacSha512 = 1, } impl AttestationMeasAlg { /// Report the expected size for a given measurement algorithm pub const fn exp_size(&self) -> u32 { match self { Self::HmacSha512 => 64, } } } impl TryFrom> for AttestationMeasAlg { type Error = Error; fn try_from(value: U32) -> Result { if value.get() == Self::HmacSha512 as u32 { Ok(Self::HmacSha512) } else { Err(Error::BinArcbInvAlgorithm(value.get())) } } } impl From for U32 { fn from(value: AttestationMeasAlg) -> Self { (value as u32).into() } } s390-tools-2.38.0/rust/pv_core/src/uvdevice.rs000066400000000000000000000172241502674226300210560ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 #![allow(non_camel_case_types)] use crate::FileAccessErrorType; use crate::{Error, Result}; use log::debug; use std::{ convert::TryInto, ffi::c_ulong, fs::File, os::unix::prelude::{AsRawFd, RawFd}, }; #[cfg(not(test))] use ::libc::ioctl; #[cfg(test)] use test::mock_libc::ioctl; /// Contains the rust representation of asm/uvdevice.h /// from kernel version: 6.5 verify mod ffi; mod info; mod test; pub(crate) use ffi::uv_ioctl; pub mod attest; pub mod retr_secret; pub mod secret; pub mod secret_list; pub use info::UvDeviceInfo; /// User data for the attestation UVC pub type AttestationUserData = [u8; ffi::UVIO_ATT_USER_DATA_LEN]; /// Configuration Unique Id of the Secure Execution guest pub type ConfigUid = [u8; ffi::UVIO_ATT_UID_LEN]; /// Bitflags as used by the Ultravisor in MSB0 ordering /// /// Wraps an u64 to set/get individual bits pub type UvFlags = crate::misc::Msb0Flags64; /// Fire an ioctl. /// /// # Safety: /// Raw fd must point to an open file fn ioctl_raw(raw_fd: RawFd, cmd: c_ulong, cb: &mut IoctlCb) -> Result<()> { debug!("calling unsafe fn wrapper uv::ioctl_raw with {raw_fd:#x?}, {cmd:#x?}, {cb:?}"); let rc; // Get the raw pointer and do an ioctl. // // SAFETY: the passed pointer points to a valid memory region that // contains the expected C-struct. The struct outlives this function. unsafe { rc = ioctl(raw_fd, cmd, cb.as_ptr_mut()); } // NOTE io::Error handles all errnos ioctl uses let errno = std::io::Error::last_os_error(); debug!("ioctl resulted with {cb:?}"); match rc { 0 => Ok(()), _ => Err(errno.into()), } } /// Converts UV return codes into human readable error messages fn rc_fmt(rc: u16, rrc: u16, cmd: &mut C) -> &'static str { let s = match (rc, rrc) { (0x0000, _) => Some("invalid rc"), (0x0002, _) => Some("invalid UV command"), (0x0005, _) => Some("request has an invalid size"), (0x0030, _) => Some("home address space control bit has R-bit set to one. This may be due to a corrupted host or a guest kernel bug. STOP using this guest"), (0x0031, _) => Some("address translation exception. This may be due to a corrupted host or a guest kernel bug. STOP using this guest"), (0x0032, _) => Some("request contains virtual address translating to an invalid address. This may be due to a corrupted host or a guest kernel bug. STOP using this guest"), (UvDevice::RC_MORE_DATA, _) => unreachable!("This is no Error!!!!"), (UvDevice::RC_SUCCESS, _) => unreachable!("This is no Error!!!!"), _ => cmd.rc_fmt(rc, rrc), }; s.unwrap_or("unexpected error-code") } /// Ultravisor Command. /// /// Implementers provide information on the specific Ultravisor command metadata and content. /// API users do not need to interact directly with any functions provided by this trait and refer /// to the specialized access and tweaking functionalities of the specific command. pub trait UvCmd { /// The UV IOCTL number of the UV call const UV_IOCTL_NR: u8; /// Returns the uvdevice IOCTL command that his command uses. /// /// # Returns /// /// The IOCTL cmd for this `UvCmd` usually something like `uv_ioctl!(CMD_NR)` fn cmd(&self) -> u64 { uv_ioctl(Self::UV_IOCTL_NR) } /// Converts UV return codes into human readable error messages /// /// # Note for implementations /// /// No need to handle `0x0000, 0x0001, 0x0002, 0x0005, 0x0030, 0x0031, 0x0032, 0x0100` fn rc_fmt(&self, rc: u16, rrc: u16) -> Option<&'static str>; /// Returns data used by this command if available. fn data(&mut self) -> Option<&mut [u8]> { None } } /// [`UvDevice`] IOCTL control block. #[derive(Debug)] struct IoctlCb(ffi::uvio_ioctl_cb); impl IoctlCb { fn new(data: Option<&mut [u8]>) -> Result { let (data_raw, data_size) = match data { Some(data) => ( data.as_mut_ptr(), data.len() .try_into() .map_err(|_| Error::Specification("passed data too large".to_string()))?, ), None => (std::ptr::null_mut(), 0), }; Ok(Self(ffi::uvio_ioctl_cb { flags: 0, uv_rc: 0, uv_rrc: 0, argument_addr: data_raw as u64, argument_len: data_size, reserved14: [0; 44], })) } fn rc(&self) -> u16 { self.0.uv_rc } fn rrc(&self) -> u16 { self.0.uv_rrc } fn as_ptr_mut(&mut self) -> *mut ffi::uvio_ioctl_cb { &mut self.0 as *mut _ } } /// The Ultravisor has two codes that represent a successful execution. /// These are represented by this enum. #[repr(u16)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum UvcSuccess { /// Command executed successfully RC_SUCCESS = UvDevice::RC_SUCCESS, /// Command executed successfully, but there is more data available and the buffer was to small /// to hold it all. The returned data is still valid. RC_MORE_DATA = UvDevice::RC_MORE_DATA, } impl UvcSuccess { /// Returns true if there is more data available pub fn more_data(&self) -> bool { match self { Self::RC_SUCCESS => false, Self::RC_MORE_DATA => true, } } } /// The `UvDevice` is a (virtual) device on s390 machines to send Ultravisor commands(UVCs) from /// userspace. /// /// On s390 machines with Ultravisor enabled (Secure Execution guest & hosts) the device at /// `/dev/uv` will accept ioctls. /// /// # Example /// /// Use a implementation of [`UvCmd`] to send a specific Ultravisor command to the uvdevice to /// forward to Firmware. /// /// ```rust,no_run /// # use s390_pv_core::uv::UvDevice; /// # use s390_pv_core::uv::AddCmd; /// # use std::fs::File; /// # fn main() -> s390_pv_core::Result<()> { /// let mut file = File::open("request")?; /// let uv = UvDevice::open()?; /// let mut cmd = AddCmd::new(&mut file)?; /// uv.send_cmd(&mut cmd)?; /// # Ok(()) /// # } /// // do something with the result /// ``` #[derive(Debug)] pub struct UvDevice(File); impl UvDevice { const PATH: &'static str = "/dev/uv"; const RC_MORE_DATA: u16 = 0x0100; const RC_SUCCESS: u16 = 0x0001; /// Open the uvdevice located at `/dev/uv` /// /// # Errors /// /// This function will return an error if the device file cannot be opened. pub fn open() -> Result { Ok(Self( std::fs::OpenOptions::new() .read(true) .write(true) .open(Self::PATH) .map_err(|e| Error::FileAccess { ty: FileAccessErrorType::Open, path: (Self::PATH).into(), source: e, })?, )) } /// Send an Ultravisor Command via this uvdevice. /// /// This works by sending an IOCTL to the uvdevice. /// /// # Errors /// /// This function will return an error if the IOCTL fails or the Ultravisor does not report /// a success. /// /// # Returns /// /// [`UvcSuccess`] if the UVC executed successfully pub fn send_cmd(&self, cmd: &mut C) -> Result { let mut cb = IoctlCb::new(cmd.data())?; ioctl_raw(self.0.as_raw_fd(), cmd.cmd(), &mut cb)?; match (cb.rc(), cb.rrc()) { (Self::RC_SUCCESS, _) => Ok(UvcSuccess::RC_SUCCESS), (Self::RC_MORE_DATA, _) => Ok(UvcSuccess::RC_MORE_DATA), (rc, rrc) => Err(Error::Uv { rc, rrc, msg: rc_fmt(rc, rrc, cmd), }), } } } s390-tools-2.38.0/rust/pv_core/src/uvdevice/000077500000000000000000000000001502674226300205025ustar00rootroot00000000000000s390-tools-2.38.0/rust/pv_core/src/uvdevice/attest.rs000066400000000000000000000245351502674226300223650ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use super::{ffi, AttestationUserData, ConfigUid, UvCmd}; use crate::{Error, Result}; use std::ptr; use zerocopy::{FromZeros, IntoBytes}; /// _Retrieve Attestation Measurement_ UVC /// /// The Attestation Request has two input and three outputs. /// ARCB and user-data are inputs for the UV. /// Measurement, additional data, and the Configuration Unique ID are outputs generated by UV. /// /// The Attestation Request Control Block (ARCB) is a cryptographically verified /// and secured request to UV and user-Data is some plaintext data which is /// going to be included in the Attestation Measurement calculation. /// /// Measurement is a cryptographic measurement of the callers properties, /// optional data configured by the ARCB and the user-data. If specified by the /// ARCB, UV will add some additional Data to the measurement calculation. /// This additional data is then returned as well. /// /// If the Retrieve Attestation Measurement UV facility is not present, /// UV will return invalid command rc. /// /// # Example /// /// ```rust,no_run /// # use s390_pv_core::uv::UvDevice; /// # use s390_pv_core::uv::AttestationCmd; /// # fn main() -> s390_pv_core::Result<()> { /// let arcb = std::fs::read("arcb")?.into(); /// let user_data = vec![0, 1, 2, 3]; /// // Hard-coded example /// let mut cmd = AttestationCmd::new_request(arcb, Some(user_data), 64, 0)?; /// let uv = UvDevice::open()?; /// # uv.send_cmd(&mut cmd)?; /// # Ok(()) /// # } /// ``` #[derive(Debug)] pub struct AttestationCmd { // all sizes are guaranteed to fit in the exchange format/UV-Call at any time // attestation data, these must not changed by this tooling, this is an invariant of this // struct, so that the raw pointer stay valid through the lifetime of this struct. // no mutable references are ever passed from this struct arcb: Box<[u8]>, measurement: Vec, additional: Option>, // raw IOCTL struct uvio_attest: ffi::uvio_attest, } impl AttestationCmd { /// Maximum size for Additional-data pub const ADDITIONAL_MAX_SIZE: u32 = ffi::UVIO_ATT_ADDITIONAL_MAX_LEN as u32; /// Maximum size for Attestation Request Control Block pub const ARCB_MAX_SIZE: u32 = ffi::UVIO_ATT_ARCB_MAX_LEN as u32; /// Maximum size for Configuration UID pub const CUID_SIZE: u32 = ffi::UVIO_ATT_UID_LEN as u32; /// Maximum size for Measurement-data pub const MEASUREMENT_MAX_SIZE: u32 = ffi::UVIO_ATT_MEASUREMENT_MAX_LEN as u32; /// Maximum size for user-data pub const USER_MAX_SIZE: u32 = ffi::UVIO_ATT_USER_DATA_LEN as u32; fn verify_size(size: u32, min_size: u32, max_size: u32, field: &'static str) -> Result<()> { if size < min_size { return Err(Error::AttDataSizeSmall { field, min_size }); } if size > max_size { return Err(Error::AttDataSizeLarge { field, max_size }); } Ok(()) } fn verify_slice(val: &[u8], max_size: u32, field: &'static str) -> Result<()> { if val.len() > max_size as usize { Err(Error::AttDataSizeLarge { field, max_size }) } else { Ok(()) } } /// Creates a new [`AttestationCmd`] /// /// * `arcb` - binary attestation request /// * `user_data` - optional, up to 256 bytes of arbitrary data /// * `exp_measurement` - expected size the Attestation measurement requires /// * `exp_additional` - expected size of the additional data. /// /// Creates a new Retrieve Attestation Measurement UVC. pub fn new_request( arcb: Box<[u8]>, user_data: Option>, exp_measurement: u32, exp_additional: u32, ) -> Result { Self::verify_size( exp_measurement, 1, Self::MEASUREMENT_MAX_SIZE, "Expected measurement size", )?; Self::verify_size( exp_additional, 0, Self::ADDITIONAL_MAX_SIZE, "Expected additional data size", )?; Self::verify_slice(&arcb, Self::ARCB_MAX_SIZE, "Attestation request")?; if let Some(ref data) = user_data { Self::verify_slice(data, Self::USER_MAX_SIZE, "User data")?; } let (user_len, user_data) = match user_data { // enforced by tests before invariants Some(user) => (Some(user.len() as u16), { let mut user_data = AttestationUserData::new_zeroed(); user_data[0..user.len()].clone_from_slice(&user); Some(user_data) }), None => (None, None), }; let mut additional = match exp_additional { 0 => None, size => Some(vec![0u8; size as usize]), }; let mut measurement = vec![0u8; exp_measurement as usize]; let uvio_attest = unsafe { ffi::uvio_attest::new( &arcb, &mut measurement, additional.as_deref_mut(), user_data, user_len, ) }; Ok(Self { arcb, measurement, additional, uvio_attest, }) } /// Provides the additional data calculated by UV after a successful UVC /// /// Truncates the additional data to the correct length in place. /// If called before a successful attestation the data in this buffer is undefined. pub fn additional(&mut self) -> Option<&[u8]> { // truncate the add size to the UV reported size if let Some(ref mut a) = &mut self.additional { a.truncate(self.uvio_attest.add_data_len as usize) } self.additional.as_deref() } /// Copies the additional data calculated by UV after a successful UVC into a Vec /// /// Truncates the additional data to the correct length. /// If called before a successful attestation the data in this buffer is undefined. pub fn additional_owned(&self) -> Option> { let mut additional = self.additional.clone()?; additional.truncate(self.uvio_attest.add_data_len as usize); Some(additional) } /// Provides the Configuration Unique Identifier received from UV after a successful UVC /// /// If called before a successful attestation the data in this buffer is undefined. pub fn cuid(&self) -> &ConfigUid { &self.uvio_attest.config_uid } /// Provides the attestation measurement calculated by UV after a successful UVC /// /// If called before a successful attestation the data in this buffer is undefined. pub fn measurement(&self) -> &[u8] { &self.measurement } /// Returns a reference to the request of this [`AttestationCmd`]. pub fn arcb(&self) -> &[u8] { self.arcb.as_ref() } } impl UvCmd for AttestationCmd { const UV_IOCTL_NR: u8 = ffi::UVIO_IOCTL_ATT_NR; fn rc_fmt(&self, rc: u16, _rrc: u16) -> Option<&'static str> { match rc { // should not happen, uvdevice local value 0x0101 => Some("Invalid continuation token specified"), 0x010a => Some("Unsupported plaintext attestation flag set"), 0x010b => Some("Unsupported measurement algorithm specified."), 0x010c => Some("Unable to decrypt attestation request control block. Probably no valid host-key was provided"), 0x0106 => Some("Unsupported attestation request version"), // should not happen, protected by AttestationCmd constructors 0x0102 => Some("User data length is greater than 256"), // should not happen, uvdevice ensures this 0x0103 => Some("Access exception recognized when accessing the attestation request control block"), // should not happen, uvdevice ensures this 0x0104 => Some("Access exception recognized when accessing the measurement data area"), // should not happen, uvdevice ensures this 0x0105 => Some("Access exception recognized when accessing the additional data area"), // should not happen, ensured by Attestation Request builder 0x0107 => Some("Invalid attestation request length for the specified attestation request version"), // 0 case should not happen, ensured by Attestation Request builder 0x0108 => Some("Number of key slots is either equal to 0 or greater than the maximum number supported by the specified attestation request version"), // should not happen, ensured by Attestation Request builder 0x0109 => Some("Size of encrypted area does not match measurement length plus any optional items"), // should not happen, ensured by Attestation Request builder 0x010d => Some("Measurement data length is not large enough to store measurement"), // should not happen, ensured by Attestation Request builder 0x010e => Some("Additional data length not large enough to hold all requested additional data"), _ => None, } } fn data(&mut self) -> Option<&mut [u8]> { Some(self.uvio_attest.as_mut_bytes()) } } fn opt_to_mut_ptr_u64(opt: &mut Option<&mut [u8]>) -> u64 { (match opt { Some(v) => v.as_mut_ptr(), None => ptr::null_mut(), }) as u64 } impl ffi::uvio_attest { /// Create a new attestation IOCTL control block /// /// Happily converts slice lengths into u32/u16 without verifying. /// Therefore marked as unsafe. /// /// # SAFETY /// It is safe to call this function iff: /// - `arcb.len() < u32::MAX` /// - `additional.len() < u32::MAX` /// - `user_len() <= 256` /// - pointer fits into an u64 unsafe fn new( arcb: &[u8], measurement: &mut [u8], mut additional: Option<&mut [u8]>, user: Option, user_len: Option, ) -> Self { Self { arcb_addr: arcb.as_ptr() as u64, meas_addr: measurement.as_ptr() as u64, add_data_addr: opt_to_mut_ptr_u64(&mut additional), user_data: user.unwrap_or([0; 256]), config_uid: [0; 16], arcb_len: arcb.len() as u32, meas_len: measurement.len() as u32, add_data_len: additional.unwrap_or_default().len() as u32, user_data_len: user_len.unwrap_or_default(), reserved136: 0, } } } s390-tools-2.38.0/rust/pv_core/src/uvdevice/ffi.rs000066400000000000000000000111671502674226300216220ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 // This file is a rustified copy of linux/arch/s390/include/uapi/asm/uvdevice.h // There might be things that are not needed here but nontheless defined in that header. // Those two files should be in sync -> there might be unused/dead code. // // The `UVIO_IOCTL_*` and `UVIO_SUPP_*` macros #![allow(dead_code)] use std::mem::size_of; use crate::{assert_size, static_assert}; use zerocopy::{FromBytes, IntoBytes}; pub const UVIO_ATT_ARCB_MAX_LEN: usize = 0x100000; pub const UVIO_ATT_MEASUREMENT_MAX_LEN: usize = 0x8000; pub const UVIO_ATT_ADDITIONAL_MAX_LEN: usize = 0x8000; pub const UVIO_ADD_SECRET_MAX_LEN: usize = 0x100000; pub const UVIO_LIST_SECRETS_LEN: usize = 0x1000; pub const UVIO_RETR_SECRET_MAX_LEN: usize = 0x2000; // equal to ascii 'u' pub const UVIO_TYPE_UVC: u8 = 117u8; pub const UVIO_IOCTL_UVDEV_INFO_NR: u8 = 0; pub const UVIO_IOCTL_ATT_NR: u8 = 1; pub const UVIO_IOCTL_ADD_SECRET_NR: u8 = 2; pub const UVIO_IOCTL_LIST_SECRETS_NR: u8 = 3; pub const UVIO_IOCTL_LOCK_SECRETS_NR: u8 = 4; pub const UVIO_IOCTL_RETR_SECRET_NR: u8 = 5; /// Uvdevice IOCTL control block /// Programs can use this struct to communicate with the uvdevice via IOCTLs /// `argument_{addr,len}` specifies in/out data depending on the request /// /// `uv_rc` and `uv_rrc` are the response and reason response codes from the /// Ultravisor. /// /// `flags` is currently unused and to be set zero #[repr(C)] #[derive(Debug)] pub struct uvio_ioctl_cb { pub flags: u32, pub uv_rc: u16, pub uv_rrc: u16, pub argument_addr: u64, pub argument_len: u32, pub reserved14: [u8; 44usize], } assert_size!(uvio_ioctl_cb, 0x40); /// Information of supported functions by the uvdevice /// /// * `supp_uvio_cmds` - supported IOCTLs by this device /// * `supp_uv_cmds` - supported UVCs corresponding to the IOCTL /// /// UVIO request to get information about supported request types by this /// uvdevice and the Ultravisor. /// Everything is output. Bits are in LSB0 ordering. /// If the bit is set in both, `supp_uvio_cmds` and `supp_uv_cmds`, /// the uvdevice and the Ultravisor support that call. /// /// Note that bit 0 (`UVIO_IOCTL_UVDEV_INFO_NR`) is always zero for `supp_uv_cmds` /// as there is no corresponding UV-call. #[repr(C)] #[derive(Debug, Copy, Clone, IntoBytes, FromBytes)] pub struct uvio_uvdev_info { pub supp_uvio_cmds: u64, pub supp_uv_cmds: u64, } assert_size!(uvio_uvdev_info, 0x10); pub const UVIO_ATT_USER_DATA_LEN: usize = 0x100; pub const UVIO_ATT_UID_LEN: usize = 0x10; /// Request Attestation Measurement control block /// /// The Attestation Request has two input and two outputs. /// ARCB and user-data are inputs for the UV. /// Measurement and additional-data are outputs generated by UV. /// /// The Attestation Request Control Block (ARCB) is a cryptographically verified /// and secured request to UV and user-data is some plaintext data which is /// going to be included in the Attestation Measurement calculation. /// /// Measurement is a cryptographic measurement of the callers properties, /// optional data configured by the ARCB and the user data. If specified by the /// ARCB, UV will add some additional-data to the measurement calculation. /// This additional-data is then returned as well. /// /// If the Retrieve Attestation Measurement UV facility is not present, /// UV will return invalid command rc. /// /// All numbers are in big-endian! #[repr(C)] #[derive(Debug, IntoBytes, FromBytes)] pub struct uvio_attest { pub arcb_addr: u64, // in pub meas_addr: u64, // out pub add_data_addr: u64, // out pub user_data: [u8; UVIO_ATT_USER_DATA_LEN], // in pub config_uid: [u8; UVIO_ATT_UID_LEN], // out pub arcb_len: u32, pub meas_len: u32, pub add_data_len: u32, pub user_data_len: u16, pub reserved136: u16, } assert_size!(uvio_attest, 0x138); /// corresponds to the `UV_IOCTL` macro pub(crate) const fn uv_ioctl(nr: u8) -> u64 { iowr(UVIO_TYPE_UVC, nr, size_of::()) } static_assert!(uv_ioctl(UVIO_IOCTL_ATT_NR) == 0xc0407501); /// corresponds to the __IOWR macro const fn iowr(ty: u8, nr: u8, size: usize) -> u64 { // constants and calculation from linux: asm-generic/ioctl.h const _IOC_WRITE: u32 = 1; const _IOC_READ: u32 = 2; const _IOC_NRSHIFT: u32 = 0; const _IOC_TYPESHIFT: u32 = 8; const _IOC_SIZESHIFT: u32 = 16; const _IOC_DIRSHIFT: u32 = 30; ((_IOC_READ | _IOC_WRITE) as u64) << _IOC_DIRSHIFT | ((ty as u64) << _IOC_TYPESHIFT) | ((nr as u64) << _IOC_NRSHIFT) | ((size as u64) << _IOC_SIZESHIFT) } s390-tools-2.38.0/rust/pv_core/src/uvdevice/info.rs000066400000000000000000000102001502674226300217740ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 use super::ffi::{self, uvio_uvdev_info}; use crate::{ misc::{Flags, Lsb0Flags64}, uv::{UvCmd, UvDevice}, Result, }; use std::fmt::Display; use zerocopy::{FromZeros, IntoBytes}; /// Information of supported functions by the uvdevice /// /// * `supp_uvio_cmds` - supported IOCTLs by this device /// * `supp_uv_cmds` - supported UVCs corresponding to the IOCTL /// /// UVIO request to get information about supported request types by this /// uvdevice and the Ultravisor. /// Everything is output. /// If the bit is set in both, `supp_uvio_cmds` and `supp_uv_cmds`, /// the uvdevice and the Ultravisor support that call. /// /// Note that bit 0 is always zero for `supp_uv_cmds` /// as there is no corresponding Info UV-call. #[derive(Debug)] pub struct UvDeviceInfo { supp_uvio_cmds: Lsb0Flags64, supp_uv_cmds: Option, } impl UvDeviceInfo { /// Get information from the uvdevice. /// /// # Errors /// /// This function will return an error if the ioctl fails and the error code is not /// [`libc::ENOTTY`]. /// `ENOTTY` is most likely because older uvdevices does not support the info IOCTL. /// In that case one can safely assume that the device only supports the Attestation IOCTL. /// Therefore this is what this function returns IOCTL support for Attestation and _Data not /// available_ for the UV Attestation facility. /// To check if the Ultravisor supports the Attestation call check at /// `/sys/firmware/uv/query/facilities` and check for bit 28 (Msb0 ordering!) pub fn get(uv: &UvDevice) -> Result { let mut cmd = uvio_uvdev_info::new_zeroed(); match uv.send_cmd(&mut cmd) { Ok(_) => Ok(cmd.into()), Err(crate::Error::Io(e)) if e.raw_os_error() == Some(libc::ENOTTY) => { let mut supp_uvio_cmds = Lsb0Flags64::default(); supp_uvio_cmds.set_bit(ffi::UVIO_IOCTL_ATT_NR); Ok(Self { supp_uvio_cmds, supp_uv_cmds: None, }) } Err(e) => Err(e), } } } impl From for UvDeviceInfo { fn from(value: uvio_uvdev_info) -> Self { Self { supp_uvio_cmds: value.supp_uvio_cmds.into(), supp_uv_cmds: Some(value.supp_uv_cmds.into()), } } } impl UvCmd for uvio_uvdev_info { const UV_IOCTL_NR: u8 = ffi::UVIO_IOCTL_UVDEV_INFO_NR; fn data(&mut self) -> Option<&mut [u8]> { Some(self.as_mut_bytes()) } fn rc_fmt(&self, _: u16, _: u16) -> Option<&'static str> { None } } fn nr_as_string(nr: u8) -> Option<&'static str> { match nr { ffi::UVIO_IOCTL_UVDEV_INFO_NR => Some("Info"), ffi::UVIO_IOCTL_ATT_NR => Some("Attestation"), ffi::UVIO_IOCTL_ADD_SECRET_NR => Some("Add Secret"), ffi::UVIO_IOCTL_LIST_SECRETS_NR => Some("List Secrets"), ffi::UVIO_IOCTL_LOCK_SECRETS_NR => Some("Lock Secret Store"), _ => None, } } fn print_uvdevice_cmd(nr: u8, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match nr_as_string(nr) { Some(s) => write!(f, "{s}"), None => write!(f, "Unknown ({nr})"), } } fn parse_flags(uv_cmds: &Lsb0Flags64, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let supp_cmds: Vec<_> = (0u8..64) .filter(|v| -> bool { uv_cmds.is_set(*v) }) .enumerate() .collect(); let num_supp_cmds = supp_cmds.len(); if num_supp_cmds == 0 { println!("None"); return Ok(()); } for (n, cmd) in supp_cmds { print_uvdevice_cmd(cmd, f)?; if n != num_supp_cmds - 1 { write!(f, ", ")?; } } writeln!(f) } impl Display for UvDeviceInfo { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "uvdevice supports:")?; parse_flags(&self.supp_uvio_cmds, f)?; writeln!(f, "Ultravisor-calls available:")?; match &self.supp_uv_cmds { Some(cmds) => parse_flags(cmds, f), None => writeln!(f, "Data not available"), } } } s390-tools-2.38.0/rust/pv_core/src/uvdevice/retr_secret.rs000066400000000000000000000300511502674226300233700ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use crate::uv::{ListableSecretType, RetrieveCmd}; use serde::{Deserialize, Serialize, Serializer}; use std::fmt::Display; /// Allowed sizes for AES keys #[non_exhaustive] #[derive(PartialEq, Eq, Debug)] pub enum AesSizes { /// 128 bit key Bits128, /// 192 bit key Bits192, /// 256 bit key Bits256, } impl AesSizes { /// Construct the key-size from the bit-size. /// /// Returns [`None`] if the bit-size is not supported. pub fn from_bits(bits: u32) -> Option { match bits { 128 => Some(Self::Bits128), 192 => Some(Self::Bits192), 256 => Some(Self::Bits256), _ => None, } } /// Returns the bit-size for the key-type const fn bit_size(&self) -> u32 { match self { Self::Bits128 => 128, Self::Bits192 => 192, Self::Bits256 => 256, } } } impl Display for AesSizes { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.bit_size()) } } /// Allowed sizes for AES-XTS keys #[non_exhaustive] #[derive(PartialEq, Eq, Debug)] pub enum AesXtsSizes { /// Two AES 128 bit keys Bits128, /// Two AES 256 bit keys Bits256, } impl AesXtsSizes { /// Construct the key-size from the bit-size. /// /// It's a key containing two keys; bit-size is half the number of bits it has /// Returns [`None`] if the bit-size is not supported. pub fn from_bits(bits: u32) -> Option { match bits { 128 => Some(Self::Bits128), 256 => Some(Self::Bits256), _ => None, } } /// Returns the bit-size for the key-type /// /// It's a key containing two keys: bit-size is half the number of bits it has const fn bit_size(&self) -> u32 { match self { Self::Bits128 => 128, Self::Bits256 => 256, } } } impl Display for AesXtsSizes { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.bit_size()) } } /// Allowed sizes for HMAC-SHA keys #[non_exhaustive] #[derive(PartialEq, Eq, Debug)] pub enum HmacShaSizes { /// SHA 256 bit Sha256, /// SHA 512 bit Sha512, } impl HmacShaSizes { /// Construct the key-size from the sha-size. /// /// FW expects maximum resistance keys (double the SHA size). /// The `sha_size` is half of the number of bits in the key /// Returns [`None`] if the `sha_size` is not supported. pub fn from_sha_size(sha_size: u32) -> Option { match sha_size { 256 => Some(Self::Sha256), 512 => Some(Self::Sha512), _ => None, } } /// Returns the sha-size for the key-type /// /// FW expects maximum resistance keys (double the SHA size). /// The `sha_size` is half of the number of bits in the key const fn sha_size(&self) -> u32 { match self { Self::Sha256 => 256, Self::Sha512 => 512, } } } impl Display for HmacShaSizes { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.sha_size()) } } /// Allowed curves for EC private keys #[non_exhaustive] #[derive(PartialEq, Eq, Debug)] pub enum EcCurves { /// secp256r1 or prime256v1 curve Secp256R1, /// secp384p1 curve Secp384R1, /// secp521r1 curve Secp521R1, /// ed25519 curve Ed25519, /// ed448 curve Ed448, } impl EcCurves { /// Returns the expected key-byte-size for this curve. pub const fn exp_key_size(&self) -> usize { match self { Self::Secp256R1 => 32, Self::Secp384R1 => 48, Self::Secp521R1 => 80, Self::Ed25519 => 32, Self::Ed448 => 64, } } } // The names have to stay constant, otherwise the PEM contains invalid types impl Display for EcCurves { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Secp256R1 => write!(f, "SECP256R1"), Self::Secp384R1 => write!(f, "SECP384R1"), Self::Secp521R1 => write!(f, "SECP521R1"), Self::Ed25519 => write!(f, "ED25519"), Self::Ed448 => write!(f, "ED448"), } } } /// Retrievable Secret types #[non_exhaustive] #[derive(PartialEq, Eq, Debug)] pub enum RetrievableSecret { /// Plain-text secret PlainText, /// Protected AES key Aes(AesSizes), /// Protected AES-XTS key AesXts(AesXtsSizes), /// Protected HMAC-SHA key HmacSha(HmacShaSizes), /// Protected EC-private key Ec(EcCurves), } // The names have to stay constant, otherwise the PEM contains invalid/unknown types impl Display for RetrievableSecret { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // Alternate representation: Omit sizes/curves if f.alternate() { match self { Self::PlainText => write!(f, "PLAINTEXT"), Self::Aes(_) => write!(f, "AES-KEY"), Self::AesXts(_) => write!(f, "AES-XTS-KEY"), Self::HmacSha(_) => write!(f, "HMAC-SHA-KEY"), Self::Ec(_) => write!(f, "EC-PRIVATE-KEY"), } } else { match self { Self::PlainText => write!(f, "PLAINTEXT"), Self::Aes(s) => write!(f, "AES-{s}-KEY"), Self::AesXts(s) => write!(f, "AES-XTS-{s}-KEY"), Self::HmacSha(s) => write!(f, "HMAC-SHA-{s}-KEY"), Self::Ec(c) => write!(f, "EC-{c}-PRIVATE-KEY"), } } } } impl RetrievableSecret { /// Report expected input types pub fn expected(&self) -> String { match self { Self::PlainText => format!("less than {}", RetrieveCmd::MAX_SIZE), Self::Aes(_) => "128, 192, or 256".to_string(), Self::AesXts(_) => "128 or 256".to_string(), Self::HmacSha(_) => "256 or 512".to_string(), Self::Ec(_) => "secp256r1, secp384r1, secp521r1, ed25519, or ed448".to_string(), } } } impl From<&RetrievableSecret> for u16 { fn from(value: &RetrievableSecret) -> Self { match value { RetrievableSecret::PlainText => ListableSecretType::PLAINTEXT, RetrievableSecret::Aes(AesSizes::Bits128) => ListableSecretType::AES_128_KEY, RetrievableSecret::Aes(AesSizes::Bits192) => ListableSecretType::AES_192_KEY, RetrievableSecret::Aes(AesSizes::Bits256) => ListableSecretType::AES_256_KEY, RetrievableSecret::AesXts(AesXtsSizes::Bits128) => ListableSecretType::AES_128_XTS_KEY, RetrievableSecret::AesXts(AesXtsSizes::Bits256) => ListableSecretType::AES_256_XTS_KEY, RetrievableSecret::HmacSha(HmacShaSizes::Sha256) => { ListableSecretType::HMAC_SHA_256_KEY } RetrievableSecret::HmacSha(HmacShaSizes::Sha512) => { ListableSecretType::HMAC_SHA_512_KEY } RetrievableSecret::Ec(EcCurves::Secp256R1) => ListableSecretType::ECDSA_P256_KEY, RetrievableSecret::Ec(EcCurves::Secp384R1) => ListableSecretType::ECDSA_P384_KEY, RetrievableSecret::Ec(EcCurves::Secp521R1) => ListableSecretType::ECDSA_P521_KEY, RetrievableSecret::Ec(EcCurves::Ed25519) => ListableSecretType::ECDSA_ED25519_KEY, RetrievableSecret::Ec(EcCurves::Ed448) => ListableSecretType::ECDSA_ED448_KEY, } } } // serializes to: (String name) impl Serialize for RetrievableSecret { fn serialize(&self, serializer: S) -> Result where S: Serializer, { let id: u16 = self.into(); serializer.serialize_str(&format!("{id} ({self})")) } } /// deserializes from the secret type nb only impl<'de> Deserialize<'de> for RetrievableSecret { fn deserialize(de: D) -> Result where D: serde::Deserializer<'de>, { struct RetrSecretVisitor; impl serde::de::Visitor<'_> for RetrSecretVisitor { type Value = RetrievableSecret; fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { fmt.write_str( "a retrievable secret type: ` (String name)` number in [3,10]|[17,21]", ) } fn visit_str(self, s: &str) -> Result where E: serde::de::Error, { let (n, _) = s.split_once(' ').ok_or(serde::de::Error::invalid_value( serde::de::Unexpected::Str(s), &self, ))?; let id: u16 = n.parse().map_err(|_| { serde::de::Error::invalid_value(serde::de::Unexpected::Str(n), &self) })?; let listable: ListableSecretType = id.into(); match listable { ListableSecretType::Retrievable(r) => Ok(r), _ => Err(serde::de::Error::invalid_value( serde::de::Unexpected::Unsigned(id.into()), &self, )), } } } de.deserialize_str(RetrSecretVisitor) } } #[cfg(test)] mod test { use serde_test::{assert_tokens, Token}; use super::*; #[test] fn retr_serde_plain() { let retr = RetrievableSecret::PlainText; assert_tokens(&retr, &[Token::Str("3 (PLAINTEXT)")]); } #[test] fn retr_serde_aes() { let retr = RetrievableSecret::Aes(AesSizes::Bits192); assert_tokens(&retr, &[Token::Str("5 (AES-192-KEY)")]); } #[test] fn retr_serde_aes_xts() { let retr = RetrievableSecret::AesXts(AesXtsSizes::Bits128); assert_tokens(&retr, &[Token::Str("7 (AES-XTS-128-KEY)")]); } #[test] fn retr_serde_hmac() { let retr = RetrievableSecret::HmacSha(HmacShaSizes::Sha256); assert_tokens(&retr, &[Token::Str("9 (HMAC-SHA-256-KEY)")]); } #[test] fn retr_serde_es() { let retr = RetrievableSecret::Ec(EcCurves::Secp521R1); assert_tokens(&retr, &[Token::Str("19 (EC-SECP521R1-PRIVATE-KEY)")]); } // Ensure that the string representation of the retrievable types stay constant, or PEM will have // different, incompatible types #[test] fn stable_type_names() { assert_eq!("PLAINTEXT", RetrievableSecret::PlainText.to_string()); assert_eq!( "AES-128-KEY", RetrievableSecret::Aes(AesSizes::Bits128).to_string() ); assert_eq!( "AES-192-KEY", RetrievableSecret::Aes(AesSizes::Bits192).to_string() ); assert_eq!( "AES-256-KEY", RetrievableSecret::Aes(AesSizes::Bits256).to_string() ); assert_eq!( "AES-XTS-128-KEY", RetrievableSecret::AesXts(AesXtsSizes::Bits128).to_string() ); assert_eq!( "AES-XTS-256-KEY", RetrievableSecret::AesXts(AesXtsSizes::Bits256).to_string() ); assert_eq!( "HMAC-SHA-256-KEY", RetrievableSecret::HmacSha(HmacShaSizes::Sha256).to_string() ); assert_eq!( "HMAC-SHA-512-KEY", RetrievableSecret::HmacSha(HmacShaSizes::Sha512).to_string() ); assert_eq!( "EC-SECP256R1-PRIVATE-KEY", RetrievableSecret::Ec(EcCurves::Secp256R1).to_string() ); assert_eq!( "EC-SECP384R1-PRIVATE-KEY", RetrievableSecret::Ec(EcCurves::Secp384R1).to_string() ); assert_eq!( "EC-SECP521R1-PRIVATE-KEY", RetrievableSecret::Ec(EcCurves::Secp521R1).to_string() ); assert_eq!( "EC-ED25519-PRIVATE-KEY", RetrievableSecret::Ec(EcCurves::Ed25519).to_string() ); assert_eq!( "EC-ED448-PRIVATE-KEY", RetrievableSecret::Ec(EcCurves::Ed448).to_string() ); } } s390-tools-2.38.0/rust/pv_core/src/uvdevice/secret.rs000066400000000000000000000155231502674226300223430ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 use super::ffi; use crate::{ request::{Confidential, MagicValue}, uv::{SecretEntry, UvCmd}, uvsecret::AddSecretMagic, Error, Result, PAGESIZE, }; use log::debug; use std::{io::Read, mem::size_of_val}; use zerocopy::IntoBytes; /// _List Secrets_ Ultravisor command. /// /// The List Secrets Ultravisor call is used to list the /// secrets that are in the secret store for the current SE-guest. #[derive(Debug)] pub struct ListCmd(Vec); impl ListCmd { fn with_size(size: usize) -> Self { Self(vec![0; size]) } /// Create a new list secrets command with `pages` capacity. /// /// * `pages` - number pf pages to allocate for this IOCTL /// /// # Panic /// This function will trigger a panic if the allocation size is larger than [`usize::MAX`]. /// Very likely an OOM situation occurs way before this! pub fn with_pages(pages: usize) -> Self { Self::with_size(pages * PAGESIZE) } /// Create a new list secrets command with a one page capacity pub fn new() -> Self { Self::with_size(PAGESIZE) } } impl Default for ListCmd { fn default() -> Self { Self::new() } } impl UvCmd for ListCmd { const UV_IOCTL_NR: u8 = ffi::UVIO_IOCTL_LIST_SECRETS_NR; fn data(&mut self) -> Option<&mut [u8]> { Some(self.0.as_mut_slice()) } fn rc_fmt(&self, _rc: u16, _rrc: u16) -> Option<&'static str> { None } } /// _Add Secret_ Ultravisor command. /// /// The Add Secret Ultravisor-call is used to add a secret /// to the secret store for the current SE-guest. #[derive(Debug)] pub struct AddCmd(Vec); impl AddCmd { /// Create a new Add Secret command using the provided data. /// /// # Errors /// /// This function will return an error if the provided data does not start /// with the `AddSecretRequest` magic Value. pub fn new(bin_add_secret_req: &mut R) -> Result { let mut data = Vec::with_capacity(PAGESIZE); bin_add_secret_req.read_to_end(&mut data)?; if data.len() > ffi::UVIO_ADD_SECRET_MAX_LEN { return Err(Error::AscrbLarge); } if !AddSecretMagic::starts_with_magic(&data[..6]) { return Err(Error::NoAsrcb); } Ok(Self(data)) } } impl UvCmd for AddCmd { const UV_IOCTL_NR: u8 = ffi::UVIO_IOCTL_ADD_SECRET_NR; fn data(&mut self) -> Option<&mut [u8]> { Some(&mut self.0) } fn rc_fmt(&self, rc: u16, _rrc: u16) -> Option<&'static str> { match rc { 0x0101 => Some("not allowed to modify the secret store"), 0x0102 => Some("secret store locked"), 0x0103 => Some("access exception when accessing request control block"), 0x0104 => Some("unsupported add secret version"), 0x0105 => Some("invalid request size"), 0x0106 => Some("invalid number of host-keys"), 0x0107 => Some("unsupported flags specified"), 0x0108 => Some("unable to decrypt the request"), 0x0109 => Some("unsupported secret provided"), 0x010a => Some("invalid length for the specified secret"), 0x010b => Some("secret store full"), 0x010c => Some("unable to add secret"), 0x010d => Some("dump in progress, try again later"), _ => None, } } } /// _Lock Secret Store_ Ultravisor command. /// /// The Lock Secret Store Ultravisor-call is used to block /// all changes to the secret store. Upon successful /// completion of a Lock Secret Store Ultravisor-call, any /// request to modify the secret store will fail. #[derive(Debug)] pub struct LockCmd; impl UvCmd for LockCmd { const UV_IOCTL_NR: u8 = ffi::UVIO_IOCTL_LOCK_SECRETS_NR; fn rc_fmt(&self, rc: u16, _rrc: u16) -> Option<&'static str> { match rc { 0x0101 => Some("not allowed to modify the secret store"), 0x0102 => Some("secret store already locked"), _ => None, } } } /// Retrieve a secret value from UV store #[derive(Debug)] pub struct RetrieveCmd { entry: SecretEntry, key: Confidential>, } impl RetrieveCmd { /// Maximum size of a retrieved key (=2 pages) pub const MAX_SIZE: usize = ffi::UVIO_RETR_SECRET_MAX_LEN; /// Create a retrieve-secret UVC from a [`SecretEntry`]. /// /// This uses the index of the secret entry for the UVC. pub fn from_entry(entry: SecretEntry) -> Result { entry.try_into() } /// Transform a [`RetrieveCmd`] into a key-vector. /// /// Only makes sense to call after a successful UVC execution. pub fn into_key(self) -> Confidential> { self.key } /// Get the secret entry /// /// Get the secret entry that is used as metadata to retrieve the secret pub fn meta_data(&self) -> &SecretEntry { &self.entry } } impl TryFrom for RetrieveCmd { type Error = Error; fn try_from(entry: SecretEntry) -> Result { let len = entry.secret_size() as usize; // Next to impossible if the secret entry is a valid response from UV if len > Self::MAX_SIZE { return Err(Error::InvalidRetrievableSecretType { id: entry.secret_id().to_owned(), size: len, }); } // Ensure that an u16 fits into the buffer. let size = std::cmp::max(size_of_val(&entry.index()), len); debug!("Create a buf with {} elements", size); let mut buf = vec![0; size]; // The IOCTL expects the secret index in the first two bytes of the buffer. They will be // overwritten in the response entry.index_be().write_to_prefix(&mut buf).unwrap(); Ok(Self { entry, key: buf.into(), }) } } impl UvCmd for RetrieveCmd { const UV_IOCTL_NR: u8 = ffi::UVIO_IOCTL_RETR_SECRET_NR; fn rc_fmt(&self, rc: u16, _: u16) -> Option<&'static str> { match rc { // should not appear (TM), software creates request from a list item 0x0009 => Some("the allocated buffer is to small to store the secret"), // should not appear (TM), kernel allocates the memory 0x0102 => { Some("access exception recognized when accessing retrieved secret storage area") } // should not appear (TM), software creates request from a list item 0x010f => Some("the Secret Store is empty"), // should not appear (TM), software creates request from a list item 0x0110 => Some("the Secret Store does not contain a secret with the specified index"), 0x0111 => Some("the secret is not retrievable"), _ => None, } } fn data(&mut self) -> Option<&mut [u8]> { Some(self.key.value_mut()) } } s390-tools-2.38.0/rust/pv_core/src/uvdevice/secret_list.rs000066400000000000000000000617731502674226300234060ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use crate::{ assert_size, uv::{AesSizes, AesXtsSizes, EcCurves, HmacShaSizes, ListCmd, RetrievableSecret}, uvdevice::UvCmd, Error, Result, }; use serde::{Deserialize, Serialize, Serializer}; use std::{ cmp::min, ffi::CStr, fmt::{Debug, Display, LowerHex, UpperHex}, io::{Cursor, Read, Seek, Write}, mem::size_of, slice::Iter, vec::IntoIter, }; use zerocopy::{BigEndian, ByteOrder}; use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, U16, U32}; /// The 32 byte long ID of an UV secret /// /// (de)serializes itself in/from a hex-string #[repr(C)] #[derive(PartialEq, Eq, IntoBytes, FromBytes, Debug, Clone, Default, Immutable, KnownLayout)] pub struct SecretId([u8; Self::ID_SIZE]); assert_size!(SecretId, SecretId::ID_SIZE); impl SecretId { /// Size in bytes of the [`SecretId`] pub const ID_SIZE: usize = 32; /// Create a [`SecretId`] from a buffer. pub fn from(buf: [u8; Self::ID_SIZE]) -> Self { buf.into() } /// Create a Id from a string /// /// Uses the first 31 bytes from `name` as id /// Does not hash anything. Byte 32 is the NUL char pub fn from_string(name: &str) -> Self { let len = min(name.len(), Self::ID_SIZE - 1); let mut res = Self::default(); res.0[0..len].copy_from_slice(&name.as_bytes()[0..len]); res } /// Tries to represent the Id as printable-ASCII string pub fn as_ascii(&self) -> Option<&str> { if let Ok(t) = CStr::from_bytes_until_nul(&self.0) { if let Ok(t) = t.to_str() { if !t.is_empty() && t.chars() .all(|c| c.is_ascii_whitespace() | c.is_ascii_graphic()) && self.0[t.len()..].iter().all(|b| *b == 0) { return Some(t); } } }; None } } impl Serialize for SecretId { fn serialize(&self, ser: S) -> std::result::Result where S: Serializer, { // calls LowerHex at one point ser.serialize_str(&format!("{self:#x}")) } } impl<'de> Deserialize<'de> for SecretId { fn deserialize(de: D) -> std::result::Result where D: serde::Deserializer<'de>, { de_gsid(de).map(|id| id.into()) } } impl UpperHex for SecretId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if f.alternate() { write!(f, "0x")?; } for b in self.0 { write!(f, "{b:02X}")?; } Ok(()) } } impl LowerHex for SecretId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if f.alternate() { write!(f, "0x")?; } for b in self.0 { write!(f, "{b:02x}")?; } Ok(()) } } impl Display for SecretId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if let Some(s) = self.as_ascii() { write!(f, "{s} | ")?; } write!(f, "{self:#x}") } } impl From<[u8; Self::ID_SIZE]> for SecretId { fn from(value: [u8; Self::ID_SIZE]) -> Self { Self(value) } } impl AsRef<[u8]> for SecretId { fn as_ref(&self) -> &[u8] { &self.0 } } /// A secret in a [`SecretList`] #[repr(C)] #[derive(Debug, Clone, PartialEq, Eq, IntoBytes, FromBytes, Serialize, Immutable)] pub struct SecretEntry { #[serde(serialize_with = "ser_u16")] index: U16, #[serde(serialize_with = "ser_u16")] stype: U16, #[serde(serialize_with = "ser_u32")] len: U32, #[serde(skip)] res_8: u64, id: SecretId, } assert_size!(SecretEntry, SecretEntry::STRUCT_SIZE); impl SecretEntry { const STRUCT_SIZE: usize = 0x30; /// Create a new entry for a [`SecretList`]. /// /// The content of this entry will very likely not represent the status of the guest in the /// Ultravisor. Use of [`SecretList::decode`] in any non-test environments is encouraged. pub fn new(index: u16, stype: ListableSecretType, id: SecretId, secret_len: u32) -> Self { Self { index: index.into(), stype: U16::new(stype.into()), len: secret_len.into(), res_8: 0, id, } } /// Returns the index of this [`SecretEntry`]. pub fn index(&self) -> u16 { self.index.get() } /// Returns the index of this [`SecretEntry`] in BE. pub(crate) fn index_be(&self) -> &U16 { &self.index } /// Returns the secret type of this [`SecretEntry`] pub fn stype(&self) -> ListableSecretType { self.stype.get().into() } /// Returns a reference to the id of this [`SecretEntry`]. /// /// The slice is guaranteed to be 32 bytes long. /// ```rust /// # use s390_pv_core::uv::SecretEntry; /// # use zerocopy::FromZeros; /// # let secr = SecretEntry::new_zeroed(); /// # assert_eq!(secr.id().len(), 32); /// ``` pub fn id(&self) -> &[u8] { self.id.as_ref() } /// Get the id as [`SecretId`] reference pub(crate) fn secret_id(&self) -> &SecretId { &self.id } /// Returns the secret size of this [`SecretEntry`]. pub fn secret_size(&self) -> u32 { self.len.get() } } impl Display for SecretEntry { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let stype: ListableSecretType = self.stype.get().into(); writeln!(f, "{} {}:", self.index, stype)?; write!(f, " {}", self.id) } } #[repr(C)] #[derive( Debug, FromBytes, IntoBytes, Clone, PartialEq, Eq, Default, Serialize, Immutable, KnownLayout, )] struct SecretListHdr { #[serde(skip)] num_secrets_stored: U16, #[serde(serialize_with = "ser_u16")] total_num_secrets: U16, #[serde(skip)] next_secret_idx: U16, #[serde(skip)] reserved_06: u16, #[serde(skip)] reserved_08: u64, } impl SecretListHdr { fn new(num_secrets_stored: u16, total_num_secrets: u16, next_secret_idx: u16) -> Self { Self { num_secrets_stored: num_secrets_stored.into(), total_num_secrets: total_num_secrets.into(), next_secret_idx: next_secret_idx.into(), reserved_06: 0, reserved_08: 0, } } } assert_size!(SecretListHdr, 16); /// List of secrets used to parse the [`crate::uv::ListCmd`] result. /// /// The list should ONLY be created from an UV-Call result using either: /// - [`TryInto::try_into`] from [`ListCmd`] /// - [`SecretList::decode`] /// Any other ways can create invalid lists that do not represent the UV secret store. /// The list must not hold more than [`u32::MAX`] elements #[derive(Debug, PartialEq, Eq, Serialize, Default)] pub struct SecretList { #[serde(flatten)] hdr: SecretListHdr, secrets: Vec, } impl<'a> IntoIterator for &'a SecretList { type IntoIter = Iter<'a, SecretEntry>; type Item = &'a SecretEntry; fn into_iter(self) -> Self::IntoIter { self.iter() } } impl IntoIterator for SecretList { type IntoIter = IntoIter; type Item = SecretEntry; fn into_iter(self) -> Self::IntoIter { self.secrets.into_iter() } } impl FromIterator for SecretList { fn from_iter>(iter: T) -> Self { let secrets: Vec<_> = iter.into_iter().collect(); let total_num_secrets = secrets.len() as u16; Self::new(total_num_secrets, secrets) } } impl SecretList { /// Creates a new `SecretList`. /// /// The content of this list will very likely not represent the status of the guest in the /// Ultravisor. Use of [`SecretList::decode`] in any non-test environments is encuraged. pub fn new(total_num_secrets: u16, secrets: Vec) -> Self { Self::new_with_hdr( SecretListHdr::new(total_num_secrets, total_num_secrets, 0), secrets, ) } fn new_with_hdr(hdr: SecretListHdr, secrets: Vec) -> Self { Self { hdr, secrets } } /// Returns an iterator over the slice. /// /// The iterator yields all secret entries from start to end. pub fn iter(&self) -> Iter<'_, SecretEntry> { self.secrets.iter() } /// Returns the length of this [`SecretList`]. pub fn len(&self) -> usize { self.secrets.len() } /// Returns `true` if the [`SecretList`] contains no [`SecretEntry`]. pub fn is_empty(&self) -> bool { self.secrets.is_empty() } /// Reports the number of secrets stored in UV. /// /// This number may be not equal to the provided number of [`SecretEntry`] pub fn total_num_secrets(&self) -> usize { self.hdr.total_num_secrets.get() as usize } /// Find the first [`SecretEntry`] that has the provided [`SecretId`] pub fn find(&self, id: &SecretId) -> Option { self.iter().find(|e| e.id() == id.as_ref()).cloned() } /// Encodes the list in the same binary format the UV would do pub fn encode(&self, w: &mut T) -> Result<()> { let hdr = self.hdr.as_bytes(); w.write_all(hdr)?; for secret in &self.secrets { w.write_all(secret.as_bytes())?; } w.flush().map_err(Error::Io) } /// Decodes the list from the binary format of the UV into this internal representation pub fn decode(r: &mut R) -> std::io::Result { let mut buf = [0u8; size_of::()]; r.read_exact(&mut buf)?; let hdr = SecretListHdr::ref_from_bytes(&buf).unwrap(); let mut buf = [0u8; SecretEntry::STRUCT_SIZE]; let mut v = Vec::with_capacity(hdr.num_secrets_stored.get() as usize); for _ in 0..hdr.num_secrets_stored.get() { r.read_exact(&mut buf)?; // cannot fail. buffer has the same size as the secret entry let secr = SecretEntry::read_from_bytes(buf.as_slice()).unwrap(); v.push(secr); } Ok(Self { hdr: hdr.clone(), secrets: v, }) } } impl TryFrom for SecretList { type Error = Error; fn try_from(mut list: ListCmd) -> Result { Self::decode(&mut Cursor::new(list.data().unwrap())).map_err(Error::InvSecretList) } } impl Display for SecretList { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { writeln!(f, "Total number of secrets: {}", self.total_num_secrets())?; if !self.secrets.is_empty() { writeln!(f)?; } for s in &self.secrets { writeln!(f, "{s}")?; } Ok(()) } } fn ser_u32(v: &U32, ser: S) -> Result { ser.serialize_u32(v.get()) } fn ser_u16(v: &U16, ser: S) -> Result { ser.serialize_u16(v.get()) } /// Secret types that can appear in a [`SecretList`] #[non_exhaustive] #[derive(PartialEq, Eq, Debug)] pub enum ListableSecretType { /// Association Secret Association, /// Retrievable key Retrievable(RetrievableSecret), /// Invalid secret type, that should never appear in a list /// /// 0 is reserved /// 1 is Null secret, with no id and not list-able /// 21 is Update CCK secret, with no id and not list-able Invalid(u16), /// Unknown secret type Unknown(u16), } impl ListableSecretType { const RESERVED_0: u16 = 0x0000; /// UV secret-type id for a null secret pub const NULL: u16 = 0x0001; /// UV secret-type id for an association secret pub const ASSOCIATION: u16 = 0x0002; /// UV secret-type id for a plain text secret pub const PLAINTEXT: u16 = 0x0003; /// UV secret-type id for an aes-128-key secret pub const AES_128_KEY: u16 = 0x0004; /// UV secret-type id for an aes-192-key secret pub const AES_192_KEY: u16 = 0x0005; /// UV secret-type id for an aes-256-key secret pub const AES_256_KEY: u16 = 0x0006; /// UV secret-type id for an aes-xts-128-key secret pub const AES_128_XTS_KEY: u16 = 0x0007; /// UV secret-type id for an aes-xts-256-key secret pub const AES_256_XTS_KEY: u16 = 0x0008; /// UV secret-type id for an hmac-sha-256-key secret pub const HMAC_SHA_256_KEY: u16 = 0x0009; /// UV secret-type id for an hmac-sha-512-key secret pub const HMAC_SHA_512_KEY: u16 = 0x000a; // 0x000b - 0x0010 reserved /// UV secret-type id for an ecdsa-p256-private-key secret pub const ECDSA_P256_KEY: u16 = 0x0011; /// UV secret-type id for an ecdsa-p384-private-key secret pub const ECDSA_P384_KEY: u16 = 0x0012; /// UV secret-type id for an ecdsa-p521-private-key secret pub const ECDSA_P521_KEY: u16 = 0x0013; /// UV secret-type id for an ed25519-private-key secret pub const ECDSA_ED25519_KEY: u16 = 0x0014; /// UV secret-type id for an ed448-private-key secret pub const ECDSA_ED448_KEY: u16 = 0x0015; /// UV secret-type id for a new customer communication key pub const UPDATE_CCK: u16 = 0x0016; } impl Display for ListableSecretType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Association => write!(f, "Association"), Self::Invalid(n) => write!(f, "Invalid(0x{n:04x})"), Self::Unknown(n) => write!(f, "Unknown(0x{n:04x})"), Self::Retrievable(r) => write!(f, "{r}"), } } } impl From> for ListableSecretType { fn from(value: U16) -> Self { value.get().into() } } impl From for ListableSecretType { fn from(value: u16) -> Self { match value { Self::RESERVED_0 => Self::Invalid(Self::RESERVED_0), Self::NULL => Self::Invalid(Self::NULL), Self::ASSOCIATION => Self::Association, Self::PLAINTEXT => Self::Retrievable(RetrievableSecret::PlainText), Self::AES_128_KEY => Self::Retrievable(RetrievableSecret::Aes(AesSizes::Bits128)), Self::AES_192_KEY => Self::Retrievable(RetrievableSecret::Aes(AesSizes::Bits192)), Self::AES_256_KEY => Self::Retrievable(RetrievableSecret::Aes(AesSizes::Bits256)), Self::AES_128_XTS_KEY => { Self::Retrievable(RetrievableSecret::AesXts(AesXtsSizes::Bits128)) } Self::AES_256_XTS_KEY => { Self::Retrievable(RetrievableSecret::AesXts(AesXtsSizes::Bits256)) } Self::HMAC_SHA_256_KEY => { Self::Retrievable(RetrievableSecret::HmacSha(HmacShaSizes::Sha256)) } Self::HMAC_SHA_512_KEY => { Self::Retrievable(RetrievableSecret::HmacSha(HmacShaSizes::Sha512)) } Self::ECDSA_P256_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Secp256R1)), Self::ECDSA_P384_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Secp384R1)), Self::ECDSA_P521_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Secp521R1)), Self::ECDSA_ED25519_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Ed25519)), Self::ECDSA_ED448_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Ed448)), Self::UPDATE_CCK => Self::Invalid(Self::UPDATE_CCK), n => Self::Unknown(n), } } } impl From for U16 { fn from(value: ListableSecretType) -> Self { Self::new(value.into()) } } impl From for u16 { fn from(value: ListableSecretType) -> Self { match value { ListableSecretType::Association => ListableSecretType::ASSOCIATION, ListableSecretType::Invalid(n) | ListableSecretType::Unknown(n) => n, ListableSecretType::Retrievable(r) => (&r).into(), } } } fn de_gsid<'de, D>(de: D) -> Result<[u8; 32], D::Error> where D: serde::Deserializer<'de>, { struct FieldVisitor; impl serde::de::Visitor<'_> for FieldVisitor { type Value = [u8; SecretId::ID_SIZE]; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("a `32 bytes (=64 character) long hexstring` prepended with 0x") } fn visit_str(self, s: &str) -> Result where E: serde::de::Error, { if s.len() != SecretId::ID_SIZE * 2 + "0x".len() { return Err(serde::de::Error::invalid_length( s.len().saturating_sub("0x".len()), &self, )); } let nb = s.strip_prefix("0x").ok_or_else(|| { serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self) })?; crate::misc::decode_hex(nb) .map_err(|_| serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self))? .try_into() .map_err(|_| serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self)) } } de.deserialize_identifier(FieldVisitor) } #[cfg(test)] mod test { use std::io::{BufReader, BufWriter, Cursor}; use serde_test::{assert_ser_tokens, assert_tokens, Token}; use zerocopy::FromZeros; use super::*; #[test] fn dump_secret_entry() { const EXP: &[u8] = &[ 0x00, 0x01, 0x00, 0x02, // idx + type 0x00, 0x00, 0x00, 0x20, // len 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved // id 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let s = SecretEntry { index: 1.into(), stype: 2.into(), len: 32.into(), res_8: 0, id: SecretId::from([0; 32]), }; assert_eq!(s.as_bytes(), EXP); } #[test] fn secret_list_dec() { let buf = [ 0x00u8, 0x01, // num secr stored 0x01, 0x12, // total num secrets 0x01, 0x01, // next valid idx 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved // secret 0x00, 0x01, 0x00, 0x02, // idx + type 0x00, 0x00, 0x00, 0x20, // len 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved // id 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let exp = SecretList::new_with_hdr( SecretListHdr::new(0x001, 0x112, 0x101), vec![SecretEntry { index: 1.into(), stype: 2.into(), len: 32.into(), res_8: 0, id: SecretId::from([0; 32]), }], ); let mut br = BufReader::new(Cursor::new(buf)); let sl = SecretList::decode(&mut br).unwrap(); assert_eq!(sl, exp); } #[test] fn secret_list_enc() { const EXP: &[u8] = &[ 0x00, 0x01, // num secr stored 0x01, 0x12, // total num secrets 0x01, 0x01, // next valid idx 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved // secret 0x00, 0x01, 0x00, 0x02, // idx + type 0x00, 0x00, 0x00, 0x20, // len 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved // id 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let sl = SecretList::new_with_hdr( SecretListHdr::new(0x001, 0x112, 0x101), vec![SecretEntry { index: 1.into(), stype: 2.into(), len: 32.into(), res_8: 0, id: SecretId::from([0; 32]), }], ); let mut buf = [0u8; 0x40]; { let mut bw = BufWriter::new(&mut buf[..]); sl.encode(&mut bw).unwrap(); } println!("list: {sl:?}"); assert_eq!(buf, EXP); } #[test] fn secret_entry_ser() { let entry = SecretEntry::new_zeroed(); assert_ser_tokens( &entry, &[ Token::Struct { name: "SecretEntry", len: (4), }, Token::String("index"), Token::U16(0), Token::String("stype"), Token::U16(0), Token::String("len"), Token::U32(0), Token::String("id"), Token::String("0x0000000000000000000000000000000000000000000000000000000000000000"), Token::StructEnd, ], ) } #[test] fn secret_id_serde() { let id = SecretId::from([ 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, ]); assert_tokens( &id, &[Token::String( "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", )], ) } #[test] fn secret_list_ser() { let list = SecretList::new_with_hdr( SecretListHdr::new(0x001, 0x112, 0x101), vec![SecretEntry { index: 1.into(), stype: 2.into(), len: 32.into(), res_8: 0, id: SecretId::from([0; 32]), }], ); assert_ser_tokens( &list, &[ Token::Map { len: None }, Token::String("total_num_secrets"), Token::U16(0x112), Token::String("secrets"), Token::Seq { len: Some(1) }, Token::Struct { name: "SecretEntry", len: (4), }, Token::String("index"), Token::U16(1), Token::String("stype"), Token::U16(2), Token::String("len"), Token::U32(32), Token::String("id"), Token::String("0x0000000000000000000000000000000000000000000000000000000000000000"), Token::StructEnd, Token::SeqEnd, Token::MapEnd, ], ) } #[test] fn secret_id_display() { let text = "Fancy secret ID"; let id = SecretId::from_string(text); let exp = "Fancy secret ID | 0x46616e6379207365637265742049440000000000000000000000000000000000"; assert_eq!(id.to_string(), exp); } #[test] fn secret_id_long_name() { let text = "the most fanciest secret ID you ever seen in the time the universe exists"; let id = SecretId::from_string(text); let exp = "the most fanciest secret ID you | 0x746865206d6f73742066616e63696573742073656372657420494420796f7500"; assert_eq!(id.to_string(), exp); } #[test] fn secret_id_no_ascii_name() { let text = [0; 32]; let id = SecretId::from(text); let exp = "0x0000000000000000000000000000000000000000000000000000000000000000"; assert_eq!(id.to_string(), exp); } #[test] fn secret_id_no_ascii_name2() { let text = [ 0x25, 0x55, 3, 4, 50, 0, 6, 0, 8, 0, 0, 0, 0, 0, 0, 0, 90, 0, 0xa, 0, 0, 0, 0, 0xf, 0, 0, 0, 0, 0, 0, 0, 0, ]; let id = SecretId::from(text); assert_eq!(id.as_ascii(), None); } #[test] fn secret_id_no_ascii_name3() { let text = [ 0x25, 0x55, 0, 4, 50, 0, 6, 0, 8, 0, 0, 0, 0, 0, 0, 0, 90, 0, 0xa, 0, 0, 0, 0, 0xf, 0, 0, 0, 0, 0, 0, 0, 0, ]; let id = SecretId::from(text); assert_eq!(id.as_ascii(), None); } #[test] fn secret_id_hex() { let id_str = "Nice Test 123"; let id = SecretId::from_string(id_str); let s = format!("{id:#x}"); assert_eq!( s, "0x4e69636520546573742031323300000000000000000000000000000000000000" ); let s = format!("{id:x}"); assert_eq!( s, "4e69636520546573742031323300000000000000000000000000000000000000" ); let s = format!("{id:#X}"); assert_eq!( s, "0x4E69636520546573742031323300000000000000000000000000000000000000" ); let s = format!("{id:X}"); assert_eq!( s, "4E69636520546573742031323300000000000000000000000000000000000000" ); } } s390-tools-2.38.0/rust/pv_core/src/uvdevice/test.rs000066400000000000000000000132461502674226300220350ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 #![cfg(test)] use std::{ ffi::{c_int, c_ulong}, sync::{Mutex, MutexGuard}, }; use super::*; use lazy_static::lazy_static; lazy_static! { /// needed to serialize all tests as tests operate on static data required by the mock static ref TEST_LOCK: Mutex<()> = Mutex::new(()); /// exists to have a lazy static mod variable static ref IOCTL_MTX: Mutex = Mutex::new(IoctlCtx::new()); } fn get_lock(m: &'static Mutex) -> MutexGuard<'static, T> { match m.lock() { Ok(guard) => guard, Err(poisoned) => poisoned.into_inner(), } } struct IoctlCtx { modify: Box i32 + Send + Sync>, exp_cmd: c_ulong, called: bool, } impl IoctlCtx { pub fn exp_cmd(&mut self, cmd: c_ulong) -> &mut Self { self.exp_cmd = cmd; self } pub fn set_mdfy(&mut self, mdfy: F) -> &mut Self where F: FnMut(&mut ffi::uvio_ioctl_cb) -> c_int + 'static + Send + Sync, { self.modify = Box::new(mdfy); self } pub fn reset(&mut self) -> bool { let old = self.called; self.called = false; old } fn new() -> Self { Self { modify: Box::new(|_| -1), exp_cmd: 0, called: false, } } } pub mod mock_libc { use super::*; pub unsafe fn ioctl(_fd: c_int, cmd: c_ulong, data: *mut ffi::uvio_ioctl_cb) -> c_int { let mut ctx = get_lock(&IOCTL_MTX); assert!(!ctx.called, "IOCTL called more than once"); ctx.called = true; assert_eq!(cmd, ctx.exp_cmd, "IOCTL cmd mismatch"); let data_ref: &mut ffi::uvio_ioctl_cb = &mut *data; (ctx.modify)(data_ref) } } impl ffi::uvio_ioctl_cb { fn addr_eq(&self, exp: u64) -> &Self { assert_eq!( self.argument_addr, exp, "ioctl arg addr not eq: {} == {}", self.argument_addr, exp ); self } fn size_eq(&self, exp: u32) -> &Self { assert_eq!( self.argument_len, exp, "ioctl arg len not eq: {} == {}", self.argument_len, exp ); self } fn set_rc(&mut self, rc: u16) -> &mut Self { self.uv_rc = rc; self } fn set_rrc(&mut self, rrc: u16) -> &mut Self { self.uv_rrc = rrc; self } } const TEST_CMD: u64 = 17; struct TestCmd(Option>); impl UvCmd for TestCmd { const UV_IOCTL_NR: u8 = 42; fn cmd(&self) -> u64 { TEST_CMD } fn rc_fmt(&self, _rc: u16, _rrc: u16) -> Option<&'static str> { None } fn data(&mut self) -> Option<&mut [u8]> { match &mut self.0 { None => None, Some(d) => Some(d.as_mut_slice()), } } } impl UvDevice { /// Use this file as backing file for `uvdevice`. This is OK, as the ioctl is mocked and never touches the /// passed file fn test_dev() -> Self { Self(File::open(".").unwrap()) } } #[test] fn ioctl_fail() { let _m = get_lock(&TEST_LOCK); let mut mock_cmd = TestCmd(None); get_lock(&IOCTL_MTX).exp_cmd(TEST_CMD).set_mdfy(|_| -1); let uv = UvDevice::test_dev(); let res = uv.send_cmd(&mut mock_cmd); assert!(get_lock(&IOCTL_MTX).reset(), "IOCTL was never called"); assert!(matches!(res, Err(Error::Io(_)))); } #[test] fn ioctl_simpleo() { let _m = get_lock(&TEST_LOCK); let mut mock_cmd = TestCmd(None); get_lock(&IOCTL_MTX).exp_cmd(TEST_CMD).set_mdfy(|cb| { cb.set_rc(1).addr_eq(0).size_eq(0); 0 }); let uv = UvDevice::test_dev(); let res = uv.send_cmd(&mut mock_cmd); assert!(get_lock(&IOCTL_MTX).reset(), "IOCTL was never called"); assert!(res.is_ok()); } #[test] fn ioctl_simple_err() { let _m = get_lock(&TEST_LOCK); let mut mock_cmd = TestCmd(None); get_lock(&IOCTL_MTX).exp_cmd(TEST_CMD).set_mdfy(|cb| { cb.set_rc(17).set_rrc(3).addr_eq(0).size_eq(0); 0 }); let uv = UvDevice::test_dev(); let res = uv.send_cmd(&mut mock_cmd); assert!(get_lock(&IOCTL_MTX).reset(), "IOCTL was never called"); assert!(matches!(res, Err(Error::Uv{rc, rrc, ..}) if rc == 17 && rrc == 3 )); } #[test] fn ioctl_write_data() { let _m = get_lock(&TEST_LOCK); let cmd_data = vec![0u8; 32]; let cmd_data_len = cmd_data.len(); let data_addr = cmd_data.as_ptr() as u64; let mut mock_cmd = TestCmd(Some(cmd_data)); get_lock(&IOCTL_MTX).exp_cmd(TEST_CMD).set_mdfy(move |cb| { cb.set_rc(1).addr_eq(data_addr).size_eq(32); unsafe { ::libc::memset(cb.argument_addr as *mut ::libc::c_void, 0x42, cmd_data_len); } 0 }); let uv = UvDevice::test_dev(); let res = uv.send_cmd(&mut mock_cmd); assert!(get_lock(&IOCTL_MTX).reset(), "IOCTL was never called"); assert_eq!(res.unwrap(), UvcSuccess::RC_SUCCESS); } #[test] fn ioctl_read_data() { let _m = get_lock(&TEST_LOCK); let cmd_data = vec![42u8; 32]; let cmd_data_len = cmd_data.len(); let data_addr = cmd_data.as_ptr() as u64; let data_exp = cmd_data.clone(); let mut mock_cmd = TestCmd(Some(cmd_data)); get_lock(&IOCTL_MTX).exp_cmd(TEST_CMD).set_mdfy(move |cb| { cb.set_rc(1).addr_eq(data_addr).size_eq(32); unsafe { let data = std::slice::from_raw_parts(cb.argument_addr as *const u8, cmd_data_len); assert_eq!(data, data_exp); } 0 }); let uv = UvDevice::test_dev(); let res = uv.send_cmd(&mut mock_cmd); assert!(get_lock(&IOCTL_MTX).reset(), "IOCTL was never called"); assert_eq!(res.unwrap(), UvcSuccess::RC_SUCCESS); } s390-tools-2.38.0/rust/pv_core/src/uvsecret.rs000066400000000000000000000122451502674226300211020ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 use crate::assert_size; use crate::{ request::{MagicValue, RequestMagic}, Error, Result, }; use byteorder::ByteOrder; use std::{fmt::Display, mem::size_of}; use zerocopy::{BigEndian, Immutable, IntoBytes, U16}; /// The magic value used to identify an `AddSecretRequest`. /// /// The magic value is ASCII: /// ```rust /// # use s390_pv_core::secret::AddSecretMagic; /// # use s390_pv_core::request::MagicValue; /// # fn main() { /// # let magic = /// b"asrcbM" /// # ; /// # assert!(AddSecretMagic::starts_with_magic(magic)); /// # } /// ``` #[repr(C)] #[derive(Debug, Clone, Copy, PartialEq, Eq, IntoBytes, Immutable)] pub struct AddSecretMagic { magic: [u8; 6], // [0x61, 0x73, 0x72, 0x63, 0x62, 0x4D] kind: U16, } assert_size!(AddSecretMagic, 8); impl MagicValue<6> for AddSecretMagic { // "asrcbM" const MAGIC: [u8; 6] = [0x61, 0x73, 0x72, 0x63, 0x62, 0x4D]; } impl AddSecretMagic { /// Get the magic value. pub fn get(&self) -> RequestMagic { let mut res = RequestMagic::default(); debug_assert!(res.len() == size_of::()); // Panic: does not panic, buf is 8 bytes long self.write_to(&mut res).unwrap(); res } /// Try to convert from a byte slice. /// /// Returns [`None`] if the byte slice does not contain a valid magic value variant. pub fn try_from_bytes(bytes: &[u8]) -> Result { if !Self::starts_with_magic(bytes) || bytes.len() < size_of::() { return Err(Error::NoAsrcb); } // Panic: Will not panic, bytes is at least 8 elements long let kind = byteorder::BigEndian::read_u16(&bytes[6..8]); let kind = UserDataType::try_from(kind)?; Ok(Self::from(kind)) } /// Returns the [`UserDataType`] of this [`AddSecretMagic`]. pub fn kind(&self) -> UserDataType { // Panic: Will never panic. The value is checked during construction of // the object for being one of the enum values. self.kind.get().try_into().unwrap() } } /// Types of (non architectured) user data for an add-secret request #[repr(u16)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum UserDataType { /// Marker that the request does not contain any user data Null = 0x0000, /// Arbitrary user data (max 512 bytes) Unsigned = 0x0001, /// User data message signed with an EC key, (max 256 byte) SgnEcSECP521R1 = 0x0002, /// User data message signature with a RSA key of 2048 bit size, (max 256 byte) SgnRsa2048 = 0x0003, /// User data message signature with a RSA key of 3072 bit size, (max 128 byte) SgnRsa3072 = 0x0004, } impl UserDataType { /// Returns the maximum user-data size in bytes. pub fn max(&self) -> usize { match self { Self::Null => 0, Self::Unsigned => 512, Self::SgnEcSECP521R1 => 256, Self::SgnRsa2048 => 256, Self::SgnRsa3072 => 128, } } } impl Display for UserDataType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "{}", match self { Self::Null => "None", Self::Unsigned => "unsigned", Self::SgnEcSECP521R1 => "ECDSA signed", Self::SgnRsa2048 => "RSA 2048 signed", Self::SgnRsa3072 => "RSA 3072 signed", } ) } } impl TryFrom for UserDataType { type Error = Error; fn try_from(value: u16) -> std::result::Result { if value == Self::Null as u16 { Ok(Self::Null) } else if value == Self::Unsigned as u16 { Ok(Self::Unsigned) } else if value == Self::SgnEcSECP521R1 as u16 { Ok(Self::SgnEcSECP521R1) } else if value == Self::SgnRsa2048 as u16 { Ok(Self::SgnRsa2048) } else if value == Self::SgnRsa3072 as u16 { Ok(Self::SgnRsa3072) } else { Err(Error::UnsupportedUserData(value)) } } } impl From for AddSecretMagic { fn from(kind: UserDataType) -> Self { Self { magic: Self::MAGIC, kind: (kind as u16).into(), } } } #[cfg(test)] mod test { use crate::{ request::MagicValue, secret::{AddSecretMagic, UserDataType}, Error, }; #[test] fn convert_user_data() { assert!(matches!( UserDataType::try_from(5), Err(Error::UnsupportedUserData(5)) )); } #[test] fn magic_get() { let user_data = AddSecretMagic::from(UserDataType::SgnEcSECP521R1); assert_eq!( user_data.get(), [0x61, 0x73, 0x72, 0x63, 0x62, 0x4D, 0x00, 0x02] ); } #[test] fn magic_try_from() { let bin = [0x61, 0x73, 0x72, 0x63, 0x62, 0x4D, 0x00, 0x02]; let magic = AddSecretMagic::try_from_bytes(&bin).unwrap(); assert_eq!( magic, AddSecretMagic { magic: AddSecretMagic::MAGIC, kind: (UserDataType::SgnEcSECP521R1 as u16).into() } ); } } s390-tools-2.38.0/rust/pvapconfig/000077500000000000000000000000001502674226300166005ustar00rootroot00000000000000s390-tools-2.38.0/rust/pvapconfig/Cargo.toml000066400000000000000000000013541502674226300205330ustar00rootroot00000000000000[package] name = "pvapconfig" description = "A tool to configure the AP resources inside a SE guest based on UV secrets and an AP config file." authors = ["Harald Freudenberger "] version = "0.12.0" edition.workspace = true license.workspace = true rust-version.workspace = true [dependencies] clap = { version ="4.5", features = ["derive", "wrap_help"]} lazy_static = "1.5" openssl = { version = "0.10.70" } pv_core = { path = "../pv_core", package = "s390_pv_core"} rand = "0.9" regex = "1.11" serde = { version = "1.0.217", features = ["derive"] } serde_yaml = "0.9" utils = { path = "../utils" } [build-dependencies] clap = { version ="4.5", features = ["derive", "wrap_help"]} clap_complete = "4.5" lazy_static = "1.5" s390-tools-2.38.0/rust/pvapconfig/README.md000066400000000000000000000004531502674226300200610ustar00rootroot00000000000000 # pvapconfig ## Description **pvapconfig** is used to automatically set up the AP configuration within an IBM Secure Execution guest. s390-tools-2.38.0/rust/pvapconfig/build.rs000066400000000000000000000012211502674226300202410ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 // it under the terms of the MIT license. See LICENSE for details. use clap::{CommandFactory, ValueEnum}; use clap_complete::{generate_to, Shell}; use std::env; use std::io::Error; include!("src/cli.rs"); fn main() -> Result<(), Error> { let outdir = env::var_os("OUT_DIR").unwrap(); let crate_name = env!("CARGO_PKG_NAME"); let mut cmd = Cli::command(); for &shell in Shell::value_variants() { generate_to(shell, &mut cmd, crate_name, &outdir)?; } println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=src/cli.rs"); Ok(()) } s390-tools-2.38.0/rust/pvapconfig/man/000077500000000000000000000000001502674226300173535ustar00rootroot00000000000000s390-tools-2.38.0/rust/pvapconfig/man/pvapconfig.1000066400000000000000000000163601502674226300215770ustar00rootroot00000000000000.\" pvapconfig.1 .\" .\" Copyright 2023 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .\" use .\" groff -man -Tutf8 pvapconfig.1 .\" or .\" nroff -man pvapconfig.1 .\" to process this source .\" .TH PVAPCONFIG 1 "DEC 2023" "s390-tools" .SH NAME pvapconfig \- automatic configure APQNs within an SE KVM guest. .SH SYNOPSIS .TP 9 .B pvapconfig [OPTIONS] .SH DESCRIPTION pvapconfig is a tool for automatically configuring the APQNs within an Secure Execution KVM guest with AP pass-through support. Based on a given AP configuration it tries to find matching APQNs and binds and associates them with the given secret(s). Here is a description of pvapconfig's process: .TP 3 1. Check AP bus: Support for AP bus needs to be available and the AP bus needs to support APSB. APSB is only available within an KVM SE guest with AP pass-through support. .TP 3 2. Check Ultravisor: UV support needs to be available and the UV needs to support the .I list secrets feature. .TP 3 3. Read in and validate the AP configuration file. By default if not overwritten by the \-\-config option the AP configuration is read from .I /etc/pvapconfig.yaml and syntactically verified. See section CONFIGFILE for details about the syntax and semantic of the configuration file. .TP 3 4. Fetch the list of association secrets from the UV. Actually the index of the secret and the secret id for each entry is collected. The secret value is NOT fetched as it is NOT accessible but only usable within the UV firmware. .TP 3 5. Gather all APQNs available within this KVM SE guest. Collect information about each APQN like online states, crypto card serial numbers and master key verification patterns (MKVP). .TP 3 6. Go through all AP config entries. For each AP config entry try to find an APQN which is already configured (bound/associated) to satisfy this config entry. If such a match is found, the AP config entry is assumed to be fulfilled and marked as done. .TP 3 7. All remaining APQNs which do not already satisfy an AP config entry are now examined for their bind and association state and maybe reset to unbound state. .TP 3 8. Go through all AP config entries which are still not fulfilled. For each such AP config entry try to search for an APQN which would match to this entry and then prepare this APQN (bind, maybe associate). If successful, mark the AP config entry as done. .TP 3 9. Evaluation of the applied AP config entries. Applied means the AP config entry has been fulfilled either in step 6 or in step 8. With the strict option given ALL AP config entries need to apply otherwise an error message is printed and pvapconfig returns with exit failure. If the strict option is not given, it is enough to satisfy at least one AP config entry from the configuration and pvapconfig will return successfully. .SH OPTIONS .TP 8 .B \-c, \-\-config Use as the AP config file for pvapconfig. If pvapconfig is run without this option the default configuration file /etc/pvapconfig.yaml is used. .TP 8 .B \-h, \-\-help Print pvapconfig usage information and exit. .TP 8 .B \-n, \-\-dry\-run Do not bind, unbind or associate APQNs but only process the configuration and the available APQNs and secrets and simulate the bind, unbind or associate action on the chosen APQN. Use it together with the verbose option to see which actions pvapconfig would do if unleashed. .TP 8 .B \-s, \-\-strict All AP config entries need to be satisfied to have pvapconfig terminate with success. Without this option one applied AP config entry is enough to meet the expectations. .TP 8 .B \-\-unbind Unbind all available APQNs. .TP 8 .B \-v, \-\-verbose Print out informational messages about what pvapconfig is actually doing. .TP 8 .B \-V, \-\-version Print version information and exit. .SH CONFIGFILE The pvapconfig yaml configuration file consists of a list of AP config entries. Each entry may hold this information: .TP 2 - mode: AP queue mode information, required, either "EP11" or "Accel". .TP 2 - mkvp: AP queue Master Key Verification Pattern (MKVP), required for EP11, hex string optional prepended with 0x. The MKVP hex string value may hold either 16 bytes (32 hex characters) or 32 bytes (64 hex characters) but only the leftmost 16 bytes hold MKVP information and thus the rest is ignored. .TP 2 - serialnr: Crypto Card Serial Number, string, optional for EP11, ignored for Accel. As this is a real ASCII string uppercase and lowercase character(s) count different. .TP 2 - mingen: Card Minimal Generation, string "CEX4", "CEX5", "CEX6", "CEX7" or "CEX8" for Accelerator, string "CEC8" for EP11, optional. If given specifies the minimal accepted Crypto card generation. .TP 2 - secretid: Secret id, hex string with optional 0x prepended, required for EP11, ignored for Accel. Details see the following text. .TP 2 - name: ASCII string, optional, but see details below. .TP 2 - description: Description of this AP config entry, string, ignored, just for convenience for the reader or editor of the configuration. .PP The secret id uniquely identifies an association secret. However, it is a clumsy hex string of 64 characters which represent the readable sha256 value over the secret's name. So pvapconfig can use the name instead and calculate the secret id from the name. So the rule is: .TP 2 - If name and secretid is given, the secretid needs to match to the sha256 hash over the given name for this AP config entry. .TP 2 - If only name is given then the secretid is calculated with a sha256 hash over the given name. .TP 2 - If only the secretid is given, there is nothing more to do but verify that the value is a hex string with 64 characters. .SH LOCKING Pvapconfig needs to have a consistent view of the AP resources during lifetime. There must not run multiple instances of pvapconfig or any manipulations of the AP resources in parallel. To prevent the execution of multiple pvapconfig instances the lock file /run/lock/pvapconfig.lock is established. A second instance of pvapconfig will detect this lock file and terminated with an error message. If for any reason this file still exists as a leftover from a previous pvapconfig crash for example, it needs to get removed by hand. The lock file contains the process id of the pvapconfig process which created this file. .SH RETURN VALUE .TP 8 .B 0 - Successful termination. At least one AP config entry has been applied or at least one APQN has been found in a state matching to one AP config entry. If strict option is given, ALL AP config entries have been applied. An AP config entry is applied either by configuring the APQN accordingly or an APQN has been found which already fulfills the constrains. .RE .TP 8 .B 1 - Failure. Either some kind of failure happened during processing the configuration or the configuration could not get applied successful. In all cases pvapconfig prints out a message to standard error with details about the failure. Also pvapconfig does NOT reset the APQNs to the state found at the startup when failing to apply the configuration. .SH NOTES For more information and details see the IBM documentation about Confidential Computing "Introducing IBM Secure Execution for Linux" available at https://www.ibm.com/docs/. .SH SEE ALSO \fBpvsecret\fR(1), \fBlszcrypt\fR(8), \fBchzcrypt\fR(8) s390-tools-2.38.0/rust/pvapconfig/src/000077500000000000000000000000001502674226300173675ustar00rootroot00000000000000s390-tools-2.38.0/rust/pvapconfig/src/ap.rs000066400000000000000000000253751502674226300203510ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 // //! AP support functions for pvapconfig // use crate::helper::*; use pv_core::ap::*; use pv_core::misc::read_file_string; use std::path::Path; use std::slice::Iter; use std::thread; use std::time; const RE_CARD_DIR: &str = r"^card([[:xdigit:]]{2})$"; const PATH_SYS_BUS_AP: &str = "/sys/bus/ap"; const PATH_SYS_BUS_AP_FEATURES: &str = "/sys/bus/ap/features"; const PATH_SYS_BUS_AP_BINDINGS: &str = "/sys/bus/ap/bindings"; const PATH_SYS_DEVICES_AP: &str = "/sys/devices/ap"; const SYS_BUS_AP_BINDINGS_POLL_MS: u64 = 500; /// Check if AP bus support is available. /// Returns Result with Ok(()) or Err(failurestring). pub fn check_ap_bus_support() -> Result<(), String> { if !Path::new(PATH_SYS_BUS_AP).is_dir() { return Err(format!( "AP bus support missing (path {PATH_SYS_BUS_AP} is invalid)." )); } Ok(()) } /// Check if AP bus supports APSB. /// /// When APSB support is available returns Result /// with Ok(()) or otherwise Err(failurestring). pub fn ap_bus_has_apsb_support() -> Result<(), String> { let features = read_file_string(PATH_SYS_BUS_AP_FEATURES, "AP bus features").map_err(|e| e.to_string())?; match features.find("APSB") { Some(_) => Ok(()), None => Err("Missing AP bus feature APSB (SE AP pass-through not enabled ?).".to_string()), } } /// Wait for AP bus set up all it's devices. /// /// This function loops until the AP bus reports that /// - all AP queue devices have been constructed /// - and all AP device have been bound to a device driver. /// /// This may take some time and even loop forever if there /// is something wrong with the kernel modules setup. /// Returns true when AP bus bindings are complete, /// otherwise false and a message is printed. /// When AP bus binding complete is not immediately reached /// and this function needs to loop, about every 5 seconds /// a message is printed "Waiting for ...". pub fn wait_for_ap_bus_bindings_complete() -> bool { let mut counter = 0; loop { match read_file_string(PATH_SYS_BUS_AP_BINDINGS, "AP bus bindings") { Ok(s) => { if s.contains("complete") { return true; } } Err(err) => { eprintln!("{err}"); return false; } } thread::sleep(time::Duration::from_millis(SYS_BUS_AP_BINDINGS_POLL_MS)); counter += 1; if counter % 10 == 0 { println!("Waiting for AP bus bindings complete."); } } } /// Wrapper object around Vector of Apqns #[derive(Debug)] pub struct ApqnList(Vec); impl ApqnList { /// Create from APQN vector. #[cfg(test)] // only used in test code pub fn from_apqn_vec(apqns: Vec) -> Self { Self(apqns) } /// Converts to an APQN vector. #[cfg(test)] // only used in test code pub fn to_apqn_vec(&self) -> Vec { self.0.clone() } /// Iter over APQN list pub fn iter(&self) -> Iter<'_, Apqn> { self.0.iter() } /// Length of the APQN list pub fn len(&self) -> usize { self.0.len() } /// Check if APQN list is empty. pub fn is_empty(&self) -> bool { self.0.is_empty() } /// Scan AP bus devices in sysfs and construct the Apqnlist. /// /// The list is a vector of struct Apqn for each APQN found in sysfs /// that this struct can be created from. /// On success a vector of struct Apqn is returned. This list may be /// empty if there are no APQNs available or do not match to the conditions. /// On failure None is returned. pub fn gather_apqns() -> Option { let mut apqns: Vec = Vec::new(); let card_dirs = match sysfs_get_list_of_subdirs_matching_regex(PATH_SYS_DEVICES_AP, RE_CARD_DIR) { Ok(r) => r, Err(err) => { eprintln!( "Failure reading AP devices {} ({:?}).", PATH_SYS_DEVICES_AP, err ); return None; } }; for dir in card_dirs { let path = format!("{PATH_SYS_DEVICES_AP}/{dir}"); let queue_dirs = match sysfs_get_list_of_subdirs_matching_regex(&path, RE_QUEUE_DIR) { Ok(r) => r, Err(err) => { eprintln!( "Failure reading AP queue directories in {} ({:?}).", path, err ); return None; } }; for queue_dir in queue_dirs { let apqn: Apqn = match (&queue_dir as &str).try_into() { Ok(apqn) => apqn, Err(e) => { eprintln!("{e}"); continue; } }; // Warn about non-fatal errors if apqn.info.is_none() { eprintln!("Warning: Failure gathering info for APQN {queue_dir}"); } if let Some(apqn_info::Cca(ref cca_info)) = apqn.info { if cca_info.mkvp_aes.is_empty() { eprintln!("Warning: APQN {queue_dir} has no valid AES master key set."); } if cca_info.mkvp_apka.is_empty() { eprintln!("Warning: APQN {queue_dir} has no valid APKA master key set."); } } if let Some(apqn_info::Ep11(ref ep11_info)) = apqn.info { if ep11_info.mkvp.is_empty() { eprintln!("Warning: APQN {queue_dir} has no valid wrapping key set."); } } apqns.push(apqn); } } Some(Self(apqns)) } /// Sort this Apqnlist by card generation: /// newest generation first, older generations last. pub fn sort_by_gen(&mut self) { self.0.sort_unstable_by(|a, b| b.gen.cmp(&a.gen)); } /// Check MK restriction /// /// Within one card there must not exist 2 APQNs with same /// MK setup. This rule only applies to EP11 cards. /// Returns true if this check passed, /// otherwise false and a message is printed. pub fn check_mk_restriction(&self) -> bool { for a1 in self.0.iter() { for a2 in self.0.iter() { if a1.card == a2.card && a1.domain < a2.domain && a1.mode == apqn_mode::Ep11 && a1.info.is_some() && a2.info.is_some() { let i1 = match a1.info.as_ref().unwrap() { apqn_info::Ep11(i) => i, _ => continue, }; let i2 = match a2.info.as_ref().unwrap() { apqn_info::Ep11(i) => i, _ => continue, }; if i1.mkvp.is_empty() || i2.mkvp.is_empty() { continue; } if i1.mkvp == i2.mkvp { eprintln!("APQN {} and APQN {} have same MPVK", a1, a2); return false; } } } } true } } #[cfg(test)] mod tests { use super::*; // These tests assume, there is an AP bus available // Also for each APQN which is online, it is assumed // to have a valid master key set up (for Ep11 and CCA). #[test] fn test_check_ap_bus_support() { if Path::new(PATH_SYS_BUS_AP).is_dir() { assert!(check_ap_bus_support().is_ok()); } else { assert!(check_ap_bus_support().is_err()); } } #[test] fn test_check_ap_bus_apsb_support() { if Path::new(PATH_SYS_BUS_AP).is_dir() { // if we are inside a secure execution guest the // apsb check should succeed. Outside an SE guest // the check should fail. if pv_core::misc::pv_guest_bit_set() { assert!(ap_bus_has_apsb_support().is_ok()); } else { assert!(ap_bus_has_apsb_support().is_err()); } } else { assert!(ap_bus_has_apsb_support().is_err()); } } #[test] fn test_wait_for_ap_bus_bindings_complete() { let r = wait_for_ap_bus_bindings_complete(); if Path::new(PATH_SYS_BUS_AP).is_dir() { assert!(r); } else { assert!(!r); } } #[test] fn test_gather_apqns() { let r = ApqnList::gather_apqns(); if Path::new(PATH_SYS_BUS_AP).is_dir() { assert!(r.is_some()); // fail if no entries found let l = r.unwrap(); let v = l.to_apqn_vec(); for a in v { match a.mode { apqn_mode::Accel => { // fail if no ApqnInfo is attached assert!(a.info.is_some()); } apqn_mode::Ep11 => { // fail if no ApqnInfo is attached assert!(a.info.is_some()); let info = a.info.unwrap(); let i = match &info { apqn_info::Ep11(i) => i, _ => panic!("ApqnInfo attached onto Ep11 APQN is NOT ApqnInfoEp11 ?!?"), }; // fail if no serialnr assert!(!i.serialnr.is_empty()); // mkvp is either empty (no WK set) or has exact 32 characters assert!(i.mkvp.is_empty() || i.mkvp.len() == 32); } apqn_mode::Cca => { // fail if no ApqnInfo is attached assert!(a.info.is_some()); let info = a.info.unwrap(); let i = match &info { apqn_info::Cca(i) => i, _ => panic!("ApqnInfo attached onto Cca APQN is NOT ApqnInfoCca ?!?"), }; // fail if no serialnr assert!(!i.serialnr.is_empty()); // aes mkvp is either empty (no MK set) or exact 16 characters assert!(i.mkvp_aes.is_empty() || i.mkvp_aes.len() == 16); // apka mkvp is either empty (no MK set) or exact 16 characters assert!(i.mkvp_apka.is_empty() || i.mkvp_apka.len() == 16); } } } } else { assert!(r.is_none()); } } } s390-tools-2.38.0/rust/pvapconfig/src/cli.rs000066400000000000000000000034461502674226300205130ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 // // use clap::Parser; use lazy_static::lazy_static; /// The default pvapconfig config file pub const PATH_DEFAULT_CONFIG_FILE: &str = "/etc/pvapconfig.yaml"; /// Command line interface for pvapconfig #[derive(Parser, Clone)] pub struct Cli { /// Provide a custom config file (overwrites default /etc/pvapconfig.yaml). #[arg(short, long, value_name = "FILE")] pub config: Option, /// Dry run: display the actions but don't actually perform them on the APQNs. #[arg(short = 'n', long = "dry-run")] pub dryrun: bool, /// Enforce strict match: All config entries need to be fulfilled. /// /// By default it is enough to successfully apply at least one config entry. /// With the strict flag enabled, all config entries within a config file /// need to be applied successful. #[arg(long = "strict")] pub strict: bool, /// Unbind all available APQNs. #[arg(long, conflicts_with_all = ["config", "strict"])] pub unbind: bool, /// Provide more detailed output. #[arg(short, long)] pub verbose: bool, /// Print version information and exit. #[arg(short = 'V', long)] pub version: bool, } lazy_static! { pub static ref ARGS: Cli = Cli::parse(); } impl Cli { /// verbose returns true if the verbose command line option /// was given, otherwise false is returned. pub fn verbose(&self) -> bool { self.verbose } /// dryrun returns true if the dry-run command line option /// was given, otherwise false is returned. pub fn dryrun(&self) -> bool { self.dryrun } /// strict returns true if the strict flag was given, otherwise /// false is returned. pub fn strict(&self) -> bool { self.strict } } s390-tools-2.38.0/rust/pvapconfig/src/config.rs000066400000000000000000000323061502674226300212060ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 // //! Functions around handling the pvapconfig configuration file // use openssl::sha::sha256; use pv_core::misc::encode_hex; use regex::Regex; use serde::{Deserialize, Serialize}; use serde_yaml::{self}; use std::fs::File; use std::slice::Iter; pub const STR_MODE_EP11: &str = "ep11"; pub const STR_MODE_ACCEL: &str = "accel"; const RE_EP11_MKVP_32: &str = r"^(0x)?([[:xdigit:]]{32})$"; const RE_EP11_MKVP_64: &str = r"^(0x)?([[:xdigit:]]{64})$"; const RE_SERIALNR: &str = r"^(\S{16})$"; const RE_EP11_GEN: &str = r"^cex(8)$"; const RE_ACCEL_GEN: &str = r"^cex([4-8])$"; const RE_SECRETID: &str = r"^(0x)?([[:xdigit:]]{64})$"; #[derive(Debug, Serialize, Deserialize, Default, Clone)] #[serde(default, deny_unknown_fields)] pub struct ApConfigEntry { pub name: String, // name and description are unmodified from the config file pub description: String, // accel after validation ep11 after validation pub mode: String, // "accel" "ep11" pub mkvp: String, // empty 32 hex lowercase characters pub serialnr: String, // empty empty or 16 non-whitespace characters pub mingen: String, // empty or "cex4"..."cex8" empty or "cex8" pub secretid: String, // empty 64 hex lowercase characters } impl ApConfigEntry { fn validate_secretid(&mut self) -> Result<(), String> { // either secret id or name may be given if self.secretid.is_empty() && self.name.is_empty() { return Err("Neither secretid nor name given.".to_string()); } // if name is given, calculate sha256 digest for this name // test for the hash calculated here can be done with openssl: // echo -n "Hello" >in.bin; openssl dgst -sha256 -binary -out out.bin in.bin; hexdump -C // out.bin if self.name.is_empty() { return Ok(()); } let hash = sha256(self.name.as_bytes()); let hashstr = encode_hex(hash); // if there is a secretid given, this must match to the hash if !self.secretid.is_empty() { if self.secretid != hashstr { return Err("Mismatch between sha256(name) and secretid.".to_string()); } } else { self.secretid = hashstr; } Ok(()) } /// # Panics /// Panics if the compilation of a static regular expression fails. fn validate_ep11_entry(&mut self) -> Result<(), String> { // mkvp is required let mut mkvp = self.mkvp.trim().to_lowercase(); if mkvp.is_empty() { return Err("Mkvp value missing.".to_string()); } // either 64 hex or 32 hex if Regex::new(RE_EP11_MKVP_64).unwrap().is_match(&mkvp) { // need to cut away the last 32 hex characters mkvp = String::from(&mkvp[..mkvp.len() - 32]) } else if Regex::new(RE_EP11_MKVP_32).unwrap().is_match(&mkvp) { // nothing to do here } else { return Err(format!("Mkvp value '{}' is not valid.", &self.mkvp)); } self.mkvp = match mkvp.strip_prefix("0x") { Some(rest) => String::from(rest), None => mkvp, }; // serialnr is optional let serialnr = self.serialnr.trim().to_string(); if !serialnr.is_empty() && !Regex::new(RE_SERIALNR).unwrap().is_match(&serialnr) { return Err(format!("Serialnr value '{}' is not valid.", &self.serialnr)); } self.serialnr = serialnr; // mingen is optional, but if given only CEX8 is valid let mingen = self.mingen.trim().to_lowercase(); if !mingen.is_empty() && !Regex::new(RE_EP11_GEN).unwrap().is_match(&mingen) { return Err(format!("Mingen value '{}' is not valid.", &self.mingen)); } self.mingen = mingen; // secretid or name is required let secretid = self.secretid.trim().to_lowercase(); if !secretid.is_empty() && !Regex::new(RE_SECRETID).unwrap().is_match(&secretid) { return Err(format!("Secretid value '{}' is not valid.", &self.secretid)); } self.secretid = match secretid.strip_prefix("0x") { Some(rest) => String::from(rest), None => secretid, }; // name is optional, ignored here // description is optional, ignored here // but the secretid needs some more validation self.validate_secretid() } /// # Panics /// Panics if the compilation of a static regular expression fails. fn validate_accel_entry(&mut self) -> Result<(), String> { // mkvp is ignored self.mkvp.clear(); // serialnr is ignored self.serialnr.clear(); // mingen is optional, but if given must match to CEX4..CEX8 let mingen = self.mingen.trim().to_lowercase(); if !mingen.is_empty() && !Regex::new(RE_ACCEL_GEN).unwrap().is_match(&mingen) { return Err(format!("Mingen value '{}' is not valid.", &self.mingen)); } self.mingen = mingen; // secretid is ignored self.secretid.clear(); // name is optional, ignored here // description is optional, ignored here Ok(()) } fn validate(&mut self) -> Result<(), String> { // trim name self.name = self.name.trim().to_string(); // mode is always required let mode = self.mode.trim().to_lowercase(); match mode.as_str() { STR_MODE_EP11 => { self.mode = mode; self.validate_ep11_entry()?; } STR_MODE_ACCEL => { self.mode = mode; self.validate_accel_entry()?; } _ => return Err(format!("Unknown or invalid mode '{}'.", mode)), } Ok(()) } } /// Wrapper object around Vector of ApConfigEntry #[derive(Default)] pub struct ApConfigList(Vec); impl ApConfigList { #[cfg(test)] // only used in test code pub fn from_apconfigentry_vec(apconfigs: Vec) -> Self { Self(apconfigs) } pub fn iter(&self) -> Iter<'_, ApConfigEntry> { self.0.iter() } pub fn len(&self) -> usize { self.0.len() } pub fn is_empty(&self) -> bool { self.0.is_empty() } fn read_yaml_file(fname: &str) -> Result, String> { let file = match File::open(fname) { Ok(f) => f, Err(err) => { return Err(format!( "Failure to open AP config file {}: {:?}", fname, err )) } }; match serde_yaml::from_reader(file) { Ok(cfg) => Ok(cfg), Err(err) => Err(format!( "Failure parsing AP config file {}: {:?}", fname, err )), } } fn validate(config: &mut [ApConfigEntry]) -> Result<(), String> { for (i, entry) in config.iter_mut().enumerate() { let ename = if !entry.name.trim().is_empty() { format!("AP config entry {} '{}'", i, entry.name.trim()) } else { format!("AP config entry {}", i) }; if let Err(err) = &entry.validate() { return Err(format!("{}: {}", ename, err)); } } Ok(()) } /// Read in and validate the yaml configuration from a file. /// Returns a Result with Ok(ApConfigList) on success /// or an Err(errorstring) on failure. pub fn read_and_validate_yaml_file(fname: &str) -> Result { let mut apconfig = Self::read_yaml_file(fname)?; Self::validate(&mut apconfig)?; Ok(Self(apconfig)) } } #[cfg(test)] mod tests { use super::*; use std::env; use std::fs; use std::io::Write; const GOOD_CONFIGS: [&str; 8] = [ "# good test 1 - name: my Accelerator mode: AcCel mingen: Cex7\n", "# good test 2 - name: my Accelerator 2 description: Accelerator entry with description mode: Accel\n", "# good test 3 - name: my EP11 APQN 1 mode: Ep11 mkvp: 0xDB3C3B3C3F097DD55EC7EB0E7FDBCB93 serialnr: 93AADFK719460083 secretid: 0xBC9d46c052BC3574454C5715757274629a283767ed237922cfb8651c0e77320A\n", "# good test 4 - name: my EP11 APQN 2 mode: EP11 mkvp: 0xdb3c3b3c3f097dd55ec7eb0e7fdbcb93 serialnr: 93aaDHzu42082261 secretid: 0x2ca853f959fc5ce5f1888cb48dae39514a27bb66520ac85f6073a7f678d262c0\n", "# good test 5 - name: my EP11 APQN 3 mode: EP11 mkvp: 0xdb3c3b3c3f097dd55ec7eb0e7fdbcb93db3c3b3c3f097dd55ec7eb0e7fdbcb93 serialnr: 93aaDHzu42082261 secretid: 0xd146c9ae77cdff25fa87a5b3487587dc29a4e391b315c98570e8fa2e2ec91454\n", "# no name but secretid given - mode: EP11 mkvp: 0xdb3c3b3c3f097dd55ec7eb0e7fdbcb93 secretid: 0x0767668dd22f23fa675c4641e04bb4e991f443be4df13ce3896b8eeca59fcc10\n", "# no secretid but name given - mode: EP11 name: My-EP11-AP-config mkvp: 0xdb3c3b3c3f097dd55ec7eb0e7fdbcb93\n", "# secretid and name given - mode: EP11 name: My-EP11-AP-config mkvp: 0xdb3c3b3c3f097dd55ec7eb0e7fdbcb93 secretid: 0x0767668dd22f23fa675c4641e04bb4e991f443be4df13ce3896b8eeca59fcc10\n", ]; const BAD_CONFIGS: [&str; 12] = [ "# mode missing - name: bad test 1 mkvp: 0xdb3c3b3c3f097dd55ec7eb0e7fdbcb93 secretid: 0x0767668dd22f23fa675c4641e04bb4e991f443be4df13ce3896b8eeca59fcc10\n", "# invalid mode - name: bad test 2 mode: CCA mkvp: 0xdb3c3b3c3f097dd55ec7eb0e7fdbcb93 secretid: 0x0767668dd22f23fa675c4641e04bb4e991f443be4df13ce3896b8eeca59fcc10\n", "# Accelerator with wrong CEX3 - name: bad test 3 mode: Accel mingen: Cex3\n", "# Accelerator with wrong CEX9 - name: bad test 4 mode: Accel mingen: CEX9\n", "# EP11 with mkvp missing - name: bad test 5 mode: EP11 serialnr: 93AADHZU42082261\n", "# EP11 with non hex mkvp - name: bad test 6 mode: EP11 mkvp: 0xabcdefghijklmnopqqponmlkjihgfedcba serialnr: 93AADHZU42082261\n", "# EP11 with mkvp too big - name: bad test 7 mode: EP11 mkvp: 0xdb3c3b3c3f097dd55ec7eb0e7fdbcb93aa serialnr: 93AADHZU42082261\n", "# EP11 with mkvp too small - name: bad test 8 mode: EP11 mkvp: 0xdb3c3b3c3f097dd55ec7eb0e7fdbcb serialnr: 93AADHZU42082261\n", "# EP11 with invalid CEXx - name: bad test 9 mode: EP11 mingen: CEX7 mkvp: 0x00112233445566778899aabbccddeeff serialnr: 93AADHZU42082261\n", "# EP11 with invalid Serialnr - name: bad test 10 mode: EP11 mkvp: 0x00112233445566778899aabbccddeeff serialnr: 93AADHZU4208226\n", "# EP11 with invalid Serialnr - name: bad test 11 mode: EP11 mkvp: 0x00112233445566778899aabbccddeeff serialnr: 93AAD ZU42082261\n", "# EP11 with sha256(name) != secretid - name: bad test 12 mode: EP11 mkvp: 0x00112233445566778899aabbccddeeff serialnr: AABBCCDDEEFFGGHH secretid: 0x2ca853f959fc5ce5f1888cb48dae39514a27bb66520ac85f6073a7f678d262c0\n", ]; const BAD_DESERIALIZE: [&str; 2] = [ "/*\ntotal nonsense\n */\n", "# wrong/unknown field - name: de-serialize failure 1 type: EP11\n", ]; fn write_yaml_config_to_temp_file(content: &str) -> Result { let dir = env::temp_dir(); let rnd = rand::random::(); let fname = format!("{}/config-test-{}.yaml", dir.to_str().unwrap(), rnd); let mut f = match File::create(&fname) { Ok(f) => f, Err(_) => return Err(format!("Failure creating temp file '{fname}'.")), }; match f.write_all(content.as_bytes()) { Ok(_) => Ok(fname), Err(_) => { fs::remove_file(&fname).ok(); Err(format!("Failure writing to temp file '{fname}'.")) } } } #[test] fn test_good_yaml() { for yaml in GOOD_CONFIGS { let f = write_yaml_config_to_temp_file(yaml).unwrap(); let config = ApConfigList::read_and_validate_yaml_file(&f).unwrap(); assert!(!config.is_empty()); fs::remove_file(&f).ok(); } } #[test] fn test_bad_yaml() { for yaml in BAD_CONFIGS { let f = write_yaml_config_to_temp_file(yaml).unwrap(); let r = ApConfigList::read_and_validate_yaml_file(&f); assert!(r.is_err()); fs::remove_file(&f).ok(); } } #[test] fn test_invalid_deserizalize() { for yaml in BAD_DESERIALIZE { let f = write_yaml_config_to_temp_file(yaml).unwrap(); let r = ApConfigList::read_and_validate_yaml_file(&f); assert!(r.is_err()); fs::remove_file(&f).ok(); } } #[test] fn test_sha256() { assert!( encode_hex(sha256("Hello".as_bytes())) == "185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969" ); assert!( encode_hex(sha256("SECRET1".as_bytes())) == "03153249db7ce46b0330ffb1a760b59710531af08ec4d7f8424a6870fae49360" ); assert!( encode_hex(sha256("SECRET2".as_bytes())) == "258499e710e0bd3bb878d6bac7e478b30f3f3e72566989f638c4143d14f6c0b6" ); } } s390-tools-2.38.0/rust/pvapconfig/src/helper.rs000066400000000000000000000126311502674226300212170ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 // //! Collection of helper functions for pvapconfig // use regex::Regex; use std::error::Error; use std::fs; use std::fs::OpenOptions; use std::io::Write; use std::path::PathBuf; pub const PATH_PVAPCONFIG_LOCK: &str = "/run/lock/pvapconfig.lock"; /// For a given (sysfs) directory construct a list of all subdirs /// and give it back as a vector of strings. If there is no subdir, /// the vector is empty. pub fn sysfs_get_list_of_subdirs(dname: &str) -> Result, Box> { let mut v: Vec = Vec::new(); let entries = fs::read_dir(dname)?; for entry in entries.flatten() { let file_type = match entry.file_type() { Ok(ft) => ft, _ => continue, }; if !file_type.is_dir() { continue; } let fname = match entry.file_name().into_string() { Ok(s) => s, _ => continue, }; v.push(fname); } Ok(v) } /// For a given (sysfs) directory construct a list of all subdirs which /// match to the given regular expression and give the list back as a /// vector of strings. If there is no subdir, the vector is empty. pub fn sysfs_get_list_of_subdirs_matching_regex( dname: &str, regex: &str, ) -> Result, Box> { let mut v: Vec = Vec::new(); let re = Regex::new(regex)?; let entries = sysfs_get_list_of_subdirs(dname)?; for entry in entries { if re.is_match(&entry) { v.push(entry); } } Ok(v) } /// LockFile for inter-process locking /// /// Simple class for process locking for pvapconfig. /// The lock concept is simple: The existence of a file is used as the /// locking indicator. If the file exists something is locked, if it does /// not exist something is not locked. In the lock file the PID of the /// process created the file ("owning this file") is written in. /// With the ProcessLock object leaving scope the associated lock file /// is automatically deleted. It is assumed that the creation of a file /// is an atomic operation - that's true for most filesystems but may /// cause problems with network based filesystems. /// Example: /// ``` /// let lock = LockFile::lock("/var/lock/process.lock"); /// assert!(lock.is_ok()); /// let lock2 = LockFile::lock("/var/lock/process.lock"); /// assert!(lock2.is_err()); /// drop(lock); /// let lock3 = LockFile::lock("/var/lock/process.lock"); /// assert!(lock3.is_ok()); /// ``` #[derive(Debug)] pub struct LockFile { lockfile: PathBuf, } impl LockFile { /// Try to establish the lock file. /// Upon success the given file is fresh created and has the pid of this /// process written in. The function returns a new LockFile object /// which has implemented the Drop Trait. So with this object going out /// of scope the lock file is deleted. If establishing the lock file /// fails for any reason (for example the file already exists), the /// function fails with returning an Error string. This function does /// NOT panic if establishing the lock file fails for any reason. If /// the lock file could be esablished but writing in the PID fails, a /// warning is printed but the function continues with returning a /// LockFile object. pub fn try_lock(fname: &str) -> Result { let lockfile = PathBuf::from(fname); let mut file = match OpenOptions::new() .write(true) .create_new(true) .open(&lockfile) { Err(err) => { return Err(format!( "Failure trying to create lock file {fname}: {err:?}." )) } Ok(f) => f, }; let _ = file .write(format!("{}", std::process::id()).as_bytes()) .map_err(|err| { println!("Warning: could not write PID into lockfile {fname}: {err:?}.") }); Ok(Self { lockfile }) } } impl Drop for LockFile { fn drop(&mut self) { let _ = fs::remove_file(&self.lockfile).map_err(|err| { println!( "Warning: could not remove lockfile {}: {err:?}.", self.lockfile.display() ) }); } } #[cfg(test)] mod tests { use super::*; use utils::TemporaryDirectory; // Only very simple tests #[test] fn test_sysfs_get_list_of_subdirs() { let r = sysfs_get_list_of_subdirs("/proc/self"); assert!(r.is_ok()); let v = r.unwrap(); assert!(!v.is_empty()); } #[test] fn test_sysfs_get_list_of_subdirs_matching_regex() { let r = sysfs_get_list_of_subdirs_matching_regex("/proc/self", "fd.*"); assert!(r.is_ok()); let v = r.unwrap(); assert!(!v.is_empty()); for e in v { assert!(e.strip_prefix("fd").is_some()); } } #[test] fn test_lockfile() { let temp_dir = TemporaryDirectory::new().expect("creating a temporary directory should work"); let file_path = temp_dir.path().join("my.lock"); let file_path_str = file_path.to_str().expect("should work"); let r1 = LockFile::try_lock(file_path_str); assert!(r1.is_ok()); let r2 = LockFile::try_lock(file_path_str); assert!(r2.is_err()); drop(r1); let r3 = LockFile::try_lock(file_path_str); assert!(r3.is_ok()); } } s390-tools-2.38.0/rust/pvapconfig/src/main.rs000066400000000000000000000604251502674226300206700ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 // //! pvapconfig - Tool to automatically set up the AP configuration //! within an IBM Secure Execution guest. // mod ap; mod cli; mod config; mod helper; mod uv; use ap::ApqnList; use cli::ARGS; use config::{ApConfigEntry, ApConfigList}; use helper::{LockFile, PATH_PVAPCONFIG_LOCK}; use pv_core::ap::{self as pvap, Apqn}; use pv_core::misc::encode_hex; use pv_core::uv::{ListableSecretType, SecretList}; use std::process::ExitCode; use utils::print_version; /// Simple macro for /// if Cli::verbose() { /// print!(...); /// } macro_rules! info { ($($arg:tt)*) => {{ if ARGS.verbose() { print!($($arg)*); } }}; } /// Simple macro for the main function only /// Does a eprintln of the arguments and then /// return with exit failure. macro_rules! println_and_exit_failure { ($($arg:tt)*) => {{ eprintln!($($arg)*); return ExitCode::FAILURE; }}; } /// Simple macro for the main function only /// Check if given object has is_err() true and /// then eprintln the unwrapped error and /// returns with exit failure. macro_rules! on_error_print_and_exit { ($r:expr) => { if $r.is_err() { eprintln!("{}", $r.unwrap_err()); return ExitCode::FAILURE; } }; } fn main() -> ExitCode { // handle version option if cli::ARGS.version { print_version!("2023"); return ExitCode::SUCCESS; } // make sure only one pvapconfig instance is running let r = LockFile::try_lock(PATH_PVAPCONFIG_LOCK); on_error_print_and_exit!(r); let _lockfile = r.unwrap(); // AP bus check info!("Checking AP bus support and facilities...\n"); let r = ap::check_ap_bus_support(); on_error_print_and_exit!(r); let r = ap::ap_bus_has_apsb_support(); on_error_print_and_exit!(r); info!("AP bus support and facilities are ok.\n"); // UV check info!("Checking UV support and environment...\n"); if !pv_core::misc::pv_guest_bit_set() { println_and_exit_failure!("Failure: this is not a SE guest."); } let r = uv::has_list_secrets_facility(); on_error_print_and_exit!(r); info!("UV support and environment is ok.\n"); let mut apconfig: ApConfigList = Default::default(); if !cli::ARGS.unbind { // read configuration let configfile: &str = match &cli::ARGS.config { Some(f) => f, _ => cli::PATH_DEFAULT_CONFIG_FILE, }; info!( "Reading AP configuration entries from file '{}'...\n", configfile ); apconfig = match ApConfigList::read_and_validate_yaml_file(configfile) { Ok(apcfg) => apcfg, Err(err) => println_and_exit_failure!("{}", err), }; if apconfig.is_empty() { println!( "No AP configuration entries in config file '{}': Nothing to do.", configfile ); return ExitCode::SUCCESS; } info!("Found {} AP configuration entries.\n", apconfig.len()); }; // get list of secrets from UV let mut secrets = SecretList::new(0, Vec::new()); if !cli::ARGS.unbind { info!("Fetching list of secrets from UV...\n"); secrets = match uv::gather_secrets() { Err(e) => println_and_exit_failure!("{}", e), Ok(los) => los, }; info!("Fetched {} Secret entries from UV.\n", secrets.len()); } // Warning if no UV secrets given but AP config entries require it let non_accel_apc = apconfig .iter() .filter(|apc| apc.mode != config::STR_MODE_ACCEL) .count(); if !cli::ARGS.unbind && non_accel_apc > 0 && secrets.is_empty() { println!( "Warning: No UV Secrets given but at least one AP config entry requires a Secret." ); } info!("Waiting for AP bus bindings complete...\n"); if !ap::wait_for_ap_bus_bindings_complete() { return ExitCode::FAILURE; } info!("Fetching list of available APQNs...\n"); let mut apqns: ApqnList = match ApqnList::gather_apqns() { Some(l) => l, None => return ExitCode::FAILURE, }; if apqns.is_empty() { info!("List of available APQNs is empty: So there's nothing to do.\n"); return ExitCode::SUCCESS; } info!("Found {} APQNs.\n", apqns.len()); // check MK restriction if !apqns.check_mk_restriction() { return ExitCode::FAILURE; } // now the real work info!("Applying AP configuration...\n"); let n = match do_ap_config(&mut apqns, &secrets, &apconfig, false) { Err(e) => println_and_exit_failure!("{}", e), Ok(n) => n, }; if !cli::ARGS.unbind && n == 0 { println_and_exit_failure!( "None out of {} AP config entries could be applied.", apconfig.len() ); } else if ARGS.strict() && n != apconfig.len() { println_and_exit_failure!( "Strict flag given and only {} out of {} AP config entries have been applied.", n, apconfig.len() ); } if !cli::ARGS.unbind { info!( "Successfully applied {} out of {} AP config entries.\n", n, apconfig.len() ); } ExitCode::SUCCESS } /// The real worker function /// /// This is the real algorithm which is trying to apply the /// AP configuration read from the config file to the existing /// APQNs with the info from the list of secrets from the UV. /// Returns the nr of AP config entries which are fulfilled /// after the function ended. /// apqns needs to be mutable as the function does a resort /// but content stays the same. fn do_ap_config( apqns: &mut ApqnList, secrets: &SecretList, apconfig: &ApConfigList, fntest: bool, ) -> Result { let mut resolved_entries = 0; let mut apconfig_done = vec![false; apconfig.len()]; let mut apqn_done = vec![false; apqns.len()]; // Preparation: Sort APQNs by generation. // All the following steps iterate through the list // of APQNs. So by sorting the APQNs starting with // highest card generation down to the older card // generations we prefer newer card generations over // older card generations. apqns.sort_by_gen(); // Step 1: // Go through all AP config entries and try to find an APQN // which already matches to this entry. If such an APQN is // found mark the AP config entry as done, and mark the APQN // as used so that entry and APQN will get skipped over in // the next steps. for (ci, apc) in apconfig.iter().enumerate() { let cistr = if !apc.name.is_empty() { format!("#{} '{}'", ci + 1, apc.name) } else { format!("#{}", ci + 1) }; for (ai, apqn) in apqns.iter().enumerate() { if apqn_done[ai] { continue; } if !config_and_apqn_match(apc, apqn) { continue; } if fntest { continue; } match apqn.mode { pvap::apqn_mode::Accel => { // check bind state of this APQN let bind_state_ok = match apqn.bind_state() { Err(err) => { eprintln!("Warning: Failure reading APQN {apqn} bind state: {err}"); false } Ok(pvap::bind_state::Bound) => true, Ok(_) => false, }; if !bind_state_ok { continue; } // This APQN matches to the current AP config entry and is already bound. // So this AP config entry is satisfied: mark this config entry as done // and mark this APQN as used. info!("Accelerator APQN {apqn} already satisfies AP config entry {cistr}.\n"); apconfig_done[ci] = true; apqn_done[ai] = true; resolved_entries += 1; break; } pvap::apqn_mode::Ep11 => { // check association state of this APQN let (assoc_state_ok, assoc_idx) = match apqn.associate_state() { Err(err) => { eprintln!( "Warning: Failure reading APQN {apqn} associate state: {err}" ); (false, 0) } Ok(pvap::assoc_state::Associated(idx)) => (true, idx), Ok(_) => (false, 0), }; if !assoc_state_ok { continue; } // check association index let r = secrets.iter().find(|&se| { se.stype() == ListableSecretType::Association && se.id().len() == uv::AP_ASSOC_SECRET_ID_SIZE && se.index() == assoc_idx && encode_hex(se.id()) == apc.secretid }); if r.is_none() { continue; } // This APQN matches to the current AP config entry and is already // associated with the right secret id. So this AP config entry is // satisfied: mark this config entry as done and mark this APQN as used. info!("EP11 APQN {apqn} already satisfies AP config entry {cistr}.\n"); apconfig_done[ci] = true; apqn_done[ai] = true; resolved_entries += 1; break; } _ => { // (currently) unknown/unsupported APQN mode } } } } // Step 2: // All APQNs NOT marked as done are now examined for their bind // and association state and maybe reset to "unbound". for (ai, apqn) in apqns.iter().enumerate() { if apqn_done[ai] || fntest { continue; } match apqn.bind_state() { Err(err) => eprintln!("Warning: Failure reading APQN {apqn} bind state: {err}"), Ok(pvap::bind_state::Bound) => { info!("Unbind APQN {apqn} as this bind/associate does not match to any AP config entry.\n"); if !ARGS.dryrun() { if let Err(err) = apqn.set_bind_state(pvap::bind_state::Unbound) { return Err(format!("Failure unbinding APQN {apqn}: {err}")); } } } Ok(_) => {} }; } // Step 3: // Go through all remaining AP config entries and try to fulfill each // by searching for an APQN which would match to this config entry and // then prepare this APQN (bind, maybe associate). for (ci, apc) in apconfig.iter().enumerate() { let cistr = if !apc.name.is_empty() { format!("#{} '{}'", ci + 1, apc.name) } else { format!("#{}", ci + 1) }; if apconfig_done[ci] { continue; } for (ai, apqn) in apqns.iter().enumerate() { if apqn_done[ai] { continue; } if !config_and_apqn_match(apc, apqn) { continue; } match apqn.mode { pvap::apqn_mode::Accel => { // try to bind this accelerator APQN if ARGS.verbose() || fntest { println!("Bind APQN {apqn} to match to AP config entry {cistr}."); } if !(ARGS.dryrun() || fntest) { if let Err(err) = apqn.set_bind_state(pvap::bind_state::Bound) { // bind failed, unbind/reset this apqn, return with failure let _ = apqn.set_bind_state(pvap::bind_state::Unbound); return Err(format!("Failure binding APQN {apqn}: {err}")); } } apconfig_done[ci] = true; apqn_done[ai] = true; resolved_entries += 1; break; } pvap::apqn_mode::Ep11 => { // EP11 needs bind and associate, but before doing this let's // check out which secret index to use with the associate let se = match secrets.iter().find(|&se| { se.stype() == ListableSecretType::Association && se.id().len() == uv::AP_ASSOC_SECRET_ID_SIZE && encode_hex(se.id()) == apc.secretid }) { None => { eprintln!("Warning: Secret id '{}' from config entry {} not found in UV secrets list.", apc.secretid, cistr); break; } Some(se) => se, }; // try to bind if ARGS.verbose() || fntest { println!( "Bind APQN {apqn} to match to AP config entry {cistr} (step 1/2)." ); } if !(ARGS.dryrun() || fntest) { if let Err(err) = apqn.set_bind_state(pvap::bind_state::Bound) { // bind failed, unbind/reset this apqn, return with failure let _ = apqn.set_bind_state(pvap::bind_state::Unbound); return Err(format!("Failure binding APQN {}: {}", apqn, err)); } } // try to associate if ARGS.verbose() || fntest { println!( "Associate APQN {} with uv secrets index {} to match AP config entry {} (step 2/2).", apqn, se.index(), cistr ); } if !(ARGS.dryrun() || fntest) { let apas = pvap::assoc_state::Associated(se.index()); apqn.set_associate_state(apas) .map_err(|err| format!("Failure associating APQN {apqn}: {err}"))?; } apconfig_done[ci] = true; apqn_done[ai] = true; resolved_entries += 1; break; } _ => { // (currently) unknown/unsupported APQN mode } } } } Ok(resolved_entries) } /// # Panics /// Panics if mingen for an accelerator has not a number as the 4th character. /// Panics if mingen for an ep11 has not a number as the 4th character. /// Please note this can not happen, as mingen is already checked via RE /// during storing the value into mingen. fn config_and_apqn_match(apc: &ApConfigEntry, apqn: &Apqn) -> bool { if apc.mode == config::STR_MODE_ACCEL && apqn.mode == pvap::apqn_mode::Accel { // config and apqn are accelerators // maybe check mingen if !apc.mingen.is_empty() { let mingen = &apc.mingen[3..].parse::().unwrap(); if mingen < &apqn.gen { return false; } } return true; } else if apc.mode == config::STR_MODE_EP11 && apqn.mode == pvap::apqn_mode::Ep11 { // config and apqn are ep11 let info = match &apqn.info { Some(pvap::apqn_info::Ep11(i)) => i, _ => return false, }; // maybe check mingen if !apc.mingen.is_empty() { let mingen = &apc.mingen[3..].parse::().unwrap(); if mingen < &apqn.gen { return false; } } // maybe check serialnr if !apc.serialnr.is_empty() && apc.serialnr != info.serialnr { return false; } // check mkvp, currently an ep11 config entry must state an mkvp value // whereas an ep11 info from an APQN may have an empty mkvp value to // indicate that there is no WK set on this APQN. if apc.mkvp != info.mkvp { return false; } return true; } false } #[cfg(test)] mod tests { use super::*; use pv_core::{misc::decode_hex, uv::SecretEntry}; // This is more or less only a test for the do_ap_config() function // However, this is THE main functionality of the whole application. fn make_test_apqns() -> Vec { vec![ pvap::Apqn { name: String::from("10.0007"), card: 16, domain: 7, gen: 8, mode: pvap::apqn_mode::Accel, info: Option::Some(pvap::apqn_info::Accel(pvap::apqn_info::ApqnInfoAccel {})), }, pvap::Apqn { name: String::from("11.0008"), card: 17, domain: 8, gen: 8, mode: pvap::apqn_mode::Ep11, info: Option::Some(pvap::apqn_info::Ep11(pvap::apqn_info::ApqnInfoEp11 { serialnr: String::from("93AADFK719460083"), mkvp: String::from("db3c3b3c3f097dd55ec7eb0e7fdbcb93"), })), }, pvap::Apqn { name: String::from("12.0009"), card: 18, domain: 9, gen: 8, mode: pvap::apqn_mode::Ep11, info: Option::Some(pvap::apqn_info::Ep11(pvap::apqn_info::ApqnInfoEp11 { serialnr: String::from("93AADHZU42082261"), mkvp: String::from("4a27bb66520ac85f6073a7f678d262c0"), })), }, pvap::Apqn { name: String::from("12.000a"), card: 18, domain: 10, gen: 8, mode: pvap::apqn_mode::Ep11, info: Option::Some(pvap::apqn_info::Ep11(pvap::apqn_info::ApqnInfoEp11 { serialnr: String::from("93AADHZU42082261"), mkvp: String::from("383d2a9ab781f35343554c5b3d9337cd"), })), }, pvap::Apqn { name: String::from("13.000d"), card: 19, domain: 13, gen: 8, mode: pvap::apqn_mode::Ep11, info: Option::Some(pvap::apqn_info::Ep11(pvap::apqn_info::ApqnInfoEp11 { serialnr: String::from("87HU397G150TZGR"), mkvp: String::new(), })), }, pvap::Apqn { name: String::from("13.000f"), card: 19, domain: 15, gen: 8, mode: pvap::apqn_mode::Ep11, info: Option::None, }, ] } fn make_assoc_secretentry(idx: u16, hexidstr: &str) -> SecretEntry { let id = decode_hex(hexidstr).unwrap(); let idlen: u32 = id.len().try_into().unwrap(); let idarray: [u8; 32] = id.try_into().unwrap(); SecretEntry::new(idx, ListableSecretType::Association, idarray.into(), idlen) } fn make_test_secrets() -> Vec { vec![ make_assoc_secretentry( 33, "3333333333333333333333333333333333333333333333333333333333333333", ), make_assoc_secretentry( 13, "bc9d46c052bc3574454c5715757274629a283767ed237922cfb8651c0e77320a", ), make_assoc_secretentry( 44, "4444444444444444444444444444444444444444444444444444444444444444", ), make_assoc_secretentry( 15, "06cdbbac76a595b481110d108154bc05ebbf900a0f16e36a24045998934fb1e9", ), make_assoc_secretentry( 17, "6831af07f8c8e7309a3ace9f3b5554d34e3eaa4a27a08fdee469e367c3fa3e9e", ), ] } fn make_test_apconfigs() -> Vec { vec![ config::ApConfigEntry { name: String::from("test_1"), description: String::from("test_1"), mode: String::from("accel"), mkvp: String::from(""), serialnr: String::from(""), mingen: String::from("cex8"), secretid: String::from(""), }, config::ApConfigEntry { name: String::from("test_2"), description: String::from("test_2"), mode: String::from("ep11"), mkvp: String::from("db3c3b3c3f097dd55ec7eb0e7fdbcb93"), serialnr: String::from("93AADFK719460083"), mingen: String::from("cex8"), secretid: String::from( "bc9d46c052bc3574454c5715757274629a283767ed237922cfb8651c0e77320a", ), }, config::ApConfigEntry { name: String::from("test_3"), description: String::from("test_3"), mode: String::from("ep11"), mkvp: String::from("4a27bb66520ac85f6073a7f678d262c0"), serialnr: String::from(""), mingen: String::from("cex8"), secretid: String::from( "06cdbbac76a595b481110d108154bc05ebbf900a0f16e36a24045998934fb1e9", ), }, config::ApConfigEntry { name: String::from("test_4"), description: String::from("test_4"), mode: String::from("ep11"), mkvp: String::from("8be1eaf5c44e2fa8b18804551b604b1b"), serialnr: String::from(""), mingen: String::from("cex8"), secretid: String::from( "6831af07f8c8e7309a3ace9f3b5554d34e3eaa4a27a08fdee469e367c3fa3e9e", ), }, ] } #[test] fn test_do_ap_config_invocation_1() { let test_apqns = make_test_apqns(); let apqns: Vec = vec![test_apqns[0].clone()]; let secrets: Vec = Vec::new(); let secretlist = SecretList::new(secrets.len() as u16, secrets); let test_apconfigs = make_test_apconfigs(); let apconfig: Vec = vec![test_apconfigs[0].clone()]; let apcfglist = ApConfigList::from_apconfigentry_vec(apconfig); let mut apqnlist = ApqnList::from_apqn_vec(apqns); let r = do_ap_config(&mut apqnlist, &secretlist, &apcfglist, true); assert!(r.is_ok()); let n = r.unwrap(); assert!(n == 1); } #[test] fn test_do_ap_config_invocation_2() { let test_apqns = make_test_apqns(); let apqns: Vec = vec![test_apqns[1].clone()]; let mut secrets = make_test_secrets(); secrets.truncate(2); let secretlist = SecretList::new(secrets.len() as u16, secrets); let test_apconfigs = make_test_apconfigs(); let apconfig: Vec = vec![test_apconfigs[1].clone()]; let apcfglist = ApConfigList::from_apconfigentry_vec(apconfig); let mut apqnlist = ApqnList::from_apqn_vec(apqns); let r = do_ap_config(&mut apqnlist, &secretlist, &apcfglist, true); assert!(r.is_ok()); let n = r.unwrap(); assert!(n == 1); } #[test] fn test_do_ap_config_invocation_3() { let test_apqns = make_test_apqns(); let mut apqns: Vec = Vec::new(); for a in test_apqns.iter() { apqns.push(a.clone()); } apqns.reverse(); let secrets = make_test_secrets(); let secretlist = SecretList::new(secrets.len() as u16, secrets); let test_apconfigs = make_test_apconfigs(); let mut apconfig: Vec = Vec::new(); for c in test_apconfigs.iter() { apconfig.push(c.clone()); } let apcfglist = ApConfigList::from_apconfigentry_vec(apconfig); let mut apqnlist = ApqnList::from_apqn_vec(apqns); let r = do_ap_config(&mut apqnlist, &secretlist, &apcfglist, true); assert!(r.is_ok()); let n = r.unwrap(); assert!(n == 3, "n = {} != 3", n); } } s390-tools-2.38.0/rust/pvapconfig/src/uv.rs000066400000000000000000000062241502674226300203730ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 // //! UV related functions for pvapconfig // use pv_core::misc::read_file_string; use pv_core::uv::{ListCmd, SecretList, UvDevice, UvcSuccess}; use regex::Regex; /// The byte size of association secret of type 2 in struct SecretEntry pub const AP_ASSOC_SECRET_ID_SIZE: usize = 32; const PATH_SYS_FW_UV_FACILITIES: &str = "/sys/firmware/uv/query/facilities"; const RE_UV_FACILITIES: &str = r"^(0x)?([[:xdigit:]]+)"; const RE_UV_FAC_BIT_LIST_SECRETS: u32 = 30; /// Check UV facilities to offer the 'list secrets' call. /// Returns a Result with Ok(()) if the 'list secrets' feature /// is available, otherwise an Err(reasonstring) is returned where /// the string denotes a hint which can be displayed. /// # Panics /// Panics if the compilation of a static regular expression fails. /// Panics if RE_UV_FACILITIES does not match. pub fn has_list_secrets_facility() -> Result<(), String> { let facstr = read_file_string(PATH_SYS_FW_UV_FACILITIES, "UV facilities").map_err(|e| e.to_string())?; let re_uv_facilities = Regex::new(RE_UV_FACILITIES).unwrap(); if !re_uv_facilities.is_match(&facstr) { Err(format!("Failure parsing UV facilities entry '{facstr}'.")) } else { let caps = re_uv_facilities.captures(&facstr).unwrap(); let fachex = caps.get(2).unwrap().as_str(); let i: usize = RE_UV_FAC_BIT_LIST_SECRETS as usize / 4; if i >= fachex.len() { return Err(format!("Failure parsing UV facilities entry '{fachex}'.")); } let nibble = u32::from_str_radix(&fachex[i..i + 1], 16).unwrap(); const THEBIT: u32 = 1 << (3 - (RE_UV_FAC_BIT_LIST_SECRETS % 4)); if nibble & THEBIT == 0 { return Err("The 'list secret' feature is missing on this UV.".to_string()); } Ok(()) } } /// Fetch the list of secrets from the UV. /// Returns Err(errorstring) on error or /// Ok(SecretList) on success. /// The list may be empty if the UV doesn't have any secrets stored. pub fn gather_secrets() -> Result { let uv = match UvDevice::open() { Err(e) => return Err(format!("Failed to open UV device: {:?}.", e)), Ok(u) => u, }; let mut cmd = ListCmd::default(); match uv.send_cmd(&mut cmd).map_err(|e| format!("{e:?}"))? { UvcSuccess::RC_SUCCESS => (), UvcSuccess::RC_MORE_DATA => println!("Warning: There is more data available than expected"), }; cmd.try_into().map_err(|e| format!("{e:?}")) } #[cfg(test)] mod tests { use super::*; // As the name says: check for list secrets feature bit in UV facilities. #[test] fn test_has_list_secrets_facility() { let r = has_list_secrets_facility(); if pv_core::misc::pv_guest_bit_set() { assert!(r.is_ok()); } else { assert!(r.is_err()); } } // Simple invocation of the list_secrets function. Should not fail #[test] fn test_list_secrets() { let r = gather_secrets(); if pv_core::misc::pv_guest_bit_set() { assert!(r.is_ok()); } else { assert!(r.is_err()); } } } s390-tools-2.38.0/rust/pvattest/000077500000000000000000000000001502674226300163165ustar00rootroot00000000000000s390-tools-2.38.0/rust/pvattest/Cargo.toml000066400000000000000000000015331502674226300202500ustar00rootroot00000000000000[package] name = "pvattest" version = "0.12.0" edition.workspace = true license.workspace = true rust-version.workspace = true [lints] workspace = true [dependencies] anyhow = { version = "1.0.95", features = ["std"] } base64 = "0.22.1" byteorder = "1.5" clap = { version ="4.5", features = ["derive", "wrap_help"]} curl = "0.4.47" log = { version = "0.4.25", features = ["std", "release_max_level_debug"] } openssl = "0.10.70" serde = { version = "1.0.217", features = ["derive"]} serde_json = "1.0" serde_yaml = "0.9" zerocopy = { version="0.8", features = ["derive"] } pv = { path = "../pv", package = "s390_pv" } utils = { path = "../utils" } [build-dependencies] clap = { version ="4.5", features = ["derive", "wrap_help"]} clap_complete = "4.5" log = { version = "0.4", features = ["std", "release_max_level_debug"] } utils = { path = "../utils" } s390-tools-2.38.0/rust/pvattest/README.md000066400000000000000000000222721502674226300176020ustar00rootroot00000000000000 # pvattest ## Synopsis `pvattest [OPTIONS] ` ## Description create, perform, and verify attestation measurements Create, perform, and verify attestation measurements for IBM Secure Execution guest systems. ## Commands Overview - **create**

    Create an attestation measurement request
- **perform**
    Send the attestation request to the Ultravisor
- **verify**
    Verify an attestation response
- **check**
    Check if the attestation result matches defined policies
## Options `-v`, `--verbose`
    Provide more detailed output.
`-q`, `--quiet`
    Provide less output.
`--version`
    Print version information and exit.
`-h`, `--help`
    Print help (see a summary with '-h').
## pvattest create ### Synopsis `pvattest create [OPTIONS] --host-key-document --output --arpk <--no-verify|--cert >` ### Description Create an attestation measurement request. Create attestation measurement requests to attest an IBM Secure Execution guest. Only build attestation requests in a trusted environment such as your Workstation. To avoid compromising the attestation do not publish the attestation request protection key and shred it after verification. Every 'create' will generate a new, random protection key. ### Options `-k`, `--host-key-document `
    Use FILE as a host-key document. Can be specified multiple times and must be specified at least once.
`--no-verify`
    Disable the host-key document verification. Does not require the host-key documents to be valid. Do not use for a production request unless you verified the host-key document beforehand.
`-C`, `--cert `
    Use FILE as a certificate to verify the host-key or keys. The certificates are used to establish a chain of trust for the verification of the host-key documents. Specify this option twice to specify the IBM Z signing key and the intermediate CA certificate (signed by the root CA).
`--crl `
    Use FILE as a certificate revocation list (CRL). The list is used to check whether a certificate of the chain of trust is revoked. Specify this option multiple times to use multiple CRLs.
`--offline`
    Make no attempt to download CRLs.
`--root-ca `
    Use FILE as the root-CA certificate for the verification. If omitted, the system wide-root CAs installed on the system are used. Use this only if you trust the specified certificate.
`-o`, `--output `
    Write the generated request to FILE.
`-a`, `--arpk `
    Save the protection key as unencrypted GCM-AES256 key in FILE Do not publish this key, otherwise your attestation is compromised.
`--add-data `
    Specify additional data for the request. Additional data is provided by the Ultravisor and returned during the attestation request and is covered by the attestation measurement. Can be specified multiple times. Optional. Possible values: - **phkh-img**: Request the public host-key-hash of the key that decrypted the SE-image as additional-data. - **phkh-att**: Request the public host-key-hash of the key that decrypted the attestation request as additional-data. - **secret-store-hash**: Request a hash over all successful Add-secret requests and the lock state as additional-data. - **firmware-state**: Request the state of the firmware as additional-data.
`-h`, `--help`
    Print help (see a summary with '-h').
## pvattest perform ### Synopsis `pvattest perform [OPTIONS] [IN] [OUT]` ### Description Send the attestation request to the Ultravisor. Run a measurement of this system through ’/dev/uv’. This device must be accessible and the attestation Ultravisor facility must be present. The input must be an attestation request created with ’pvattest create’. Output will contain the original request and the response from the Ultravisor. ### Arguments ``
    Specify the request to be sent.
``
    Write the result to FILE.
### Options `-u`, `--user-data `
    Provide up to 256 bytes of user input User-data is arbitrary user-defined data appended to the Attestation measurement. It is verified during the Attestation measurement verification. May be any arbitrary data, as long as it is less or equal to 256 bytes
`-h`, `--help`
    Print help (see a summary with '-h').
## pvattest verify ### Synopsis `pvattest verify [OPTIONS] --input --hdr --arpk ` ### Description Verify an attestation response. Verify that a previously generated attestation measurement of an IBM Secure Execution guest is as expected. Only verify attestation requests in a trusted environment, such as your workstation. Input must contain the response as produced by ’pvattest perform’. The protection key must be the one that was used to create the request by ’pvattest create’. Shred the protection key after the verification. The header must be the IBM Secure Execution header of the image that was attested during ’pvattest perform’. The verify command solely verifies that the Attestation measurement is correct. It does not check for the content of additional data or user data. See `pvattest check` for policy checks after you verified the Attestation measurement. ### Options `-i`, `--input `
    Specify the attestation response to be verified.
`-o`, `--output `
    Specify the output for the verification result.
`--hdr `
    Specifies the header of the guest image. Can be an IBM Secure Execution image created by genprotimg or an extracted IBM Secure Execution header. The header must start at a page boundary.
`-a`, `--arpk `
    Use FILE as the protection key to decrypt the request Do not publish this key, otherwise your attestation is compromised. Delete this key after verification.
`--format `
    Define the output format. Default value: 'yaml' Possible values: - **yaml**: Use yaml format.
`-u`, `--user-data `
    Write the user data to the FILE if any. Writes the user data, if the response contains any, to FILE The user-data is part of the attestation measurement. If the user-data is written to FILE the user-data was part of the measurement and verified. Emits a warning if the response contains no user-data.
`-h`, `--help`
    Print help (see a summary with '-h').
## pvattest check ### Synopsis `pvattest check [OPTIONS] ` ### Description Check if the attestation result matches defined policies. After the attestation verification, check whether the attestation result complies with user-defined policies. ### Arguments ``
    Specify the attestation response to check whether the policies are validated.
``
    Specify the output file for the check result.
### Options `--format `
    Define the output format. Default value: 'yaml' Possible values: - **yaml**: Use yaml format.
`-k`, `--host-key-document `
    Use FILE to check for a host-key document. Verifies that the attestation response contains the host-key hash of one of the specified host keys. The check fails if none of the host-keys match the hash in the response. This parameter can be specified multiple times.
`--host-key-check `
    Define the host-key check policy By default, all host-key hashes are checked, and it is not considered a failure if a hash is missing from the attestation response. Use this policy switch to trigger a failure if no corresponding hash is found. Requires at least one host-key document. Possible values: - **att-key-hash**: Check the host-key used for the attestation request. - **boot-key-hash**: Check the host-key used to the boot the image.
`-u`, `--user-data `
    Check if the provided user data matches the data from the attestation response.
`--secret `
    Use FILE to include as successful Add-secret request. Checks if the Attestation response contains the hash of all specified add secret requests-tags. The hash is sensible to the order in which the secrets where added. This means that if the order of adding here different from the order the add-secret requests where sent to the UV this check will fail even though the same secrets are included in the UV secret store. Can be specified multiple times.
`--secret-store-locked `
    Check whether the guests secret store is locked or not. Compares the hash of the secret store state to the one calculated by this option and optionally specified add-secret-requests in the correct order. If the attestation response does not contain a secret store hash, this check fails. Required if add-secret-requests are specified.
`--firmware`
    Check whether the firmware is supported by IBM. Requires internet access.
`--firmware-verify-url `
    Specify the endpoint to use for firmware version verification. Use an endpoint you trust. Requires the --firmware option.
`-h`, `--help`
    Print help (see a summary with '-h').
s390-tools-2.38.0/rust/pvattest/build.rs000066400000000000000000000013371502674226300177670ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 // it under the terms of the MIT license. See LICENSE for details. #![allow(missing_docs)] use clap::CommandFactory; use clap_complete::{generate_to, Shell}; use std::env; use std::io::Error; include!("src/cli.rs"); fn main() -> Result<(), Error> { let outdir = env::var_os("OUT_DIR").unwrap(); let crate_name = env!("CARGO_PKG_NAME"); let mut cmd = CliOptions::command(); for &shell in Shell::value_variants() { generate_to(shell, &mut cmd, crate_name, &outdir)?; } println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=src/cli.rs"); println!("cargo:rerun-if-changed=../utils/src/cli.rs"); Ok(()) } s390-tools-2.38.0/rust/pvattest/man/000077500000000000000000000000001502674226300170715ustar00rootroot00000000000000s390-tools-2.38.0/rust/pvattest/man/pvattest-check.1000066400000000000000000000060411502674226300221010ustar00rootroot00000000000000.\" Copyright 2024, 2025 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH "PVATTEST-CHECK" "1" "2025-03-12" "s390-tools" "Attestation Manual" .nh .ad l .SH NAME pvattest-check \- Check if the attestation result matches defined policies .SH SYNOPSIS .nf .fam C pvattest check [OPTIONS] .fam C .fi .SH DESCRIPTION After the attestation verification, check whether the attestation result complies with user\-defined policies. .SH OPTIONS .PP .RS 4 Specify the attestation response to check whether the policies are validated. .RE .RE .PP .RS 4 Specify the output file for the check result. .RE .RE .PP \-\-format .RS 4 Define the output format. [default: 'yaml'] Possible values: .RS 4 \- \fByaml\fP: Use yaml format. .RE .RE .PP \-k, \-\-host\-key\-document .RS 4 Use FILE to check for a host\-key document. Verifies that the attestation response contains the host\-key hash of one of the specified host keys. The check fails if none of the host\-keys match the hash in the response. This parameter can be specified multiple times. .RE .RE .PP \-\-host\-key\-check .RS 4 Define the host\-key check policy By default, all host\-key hashes are checked, and it is not considered a failure if a hash is missing from the attestation response. Use this policy switch to trigger a failure if no corresponding hash is found. Requires at least one host\-key document. Possible values: .RS 4 \- \fBatt-key-hash\fP: Check the host-key used for the attestation request. \- \fBboot-key-hash\fP: Check the host-key used to the boot the image. .RE .RE .PP \-u, \-\-user\-data .RS 4 Check if the provided user data matches the data from the attestation response. .RE .RE .PP \-\-secret .RS 4 Use FILE to include as successful Add\-secret request. Checks if the Attestation response contains the hash of all specified add secret requests\-tags. The hash is sensible to the order in which the secrets where added. This means that if the order of adding here different from the order the add\-secret requests where sent to the UV this check will fail even though the same secrets are included in the UV secret store. Can be specified multiple times. .RE .RE .PP \-\-secret\-store\-locked .RS 4 Check whether the guests secret store is locked or not. Compares the hash of the secret store state to the one calculated by this option and optionally specified add\-secret\-requests in the correct order. If the attestation response does not contain a secret store hash, this check fails. Required if add\-secret\-requests are specified. .RE .RE .PP \-\-firmware .RS 4 Check whether the firmware is supported by IBM. Requires internet access. .RE .RE .PP \-\-firmware\-verify\-url .RS 4 Specify the endpoint to use for firmware version verification. Use an endpoint you trust. Requires the \-\-firmware option. .RE .RE .PP \-h, \-\-help .RS 4 Print help (see a summary with \fB\-h\fR). .RE .RE .SH "SEE ALSO" .sp \fBpvattest\fR(1) s390-tools-2.38.0/rust/pvattest/man/pvattest-create.1000066400000000000000000000100361502674226300222660ustar00rootroot00000000000000.\" Copyright 2024, 2025 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH "PVATTEST-CREATE" "1" "2025-03-12" "s390-tools" "Attestation Manual" .nh .ad l .SH NAME pvattest-create \- Create an attestation measurement request .SH SYNOPSIS .nf .fam C pvattest create [OPTIONS] --host-key-document --output --arpk <--no-verify|--cert > .fam C .fi .SH DESCRIPTION Create attestation measurement requests to attest an IBM Secure Execution guest. Only build attestation requests in a trusted environment such as your Workstation. To avoid compromising the attestation do not publish the attestation request protection key and shred it after verification. Every \fBcreate\fR will generate a new, random protection key. .SH OPTIONS .PP \-k, \-\-host\-key\-document .RS 4 Use FILE as a host\-key document. Can be specified multiple times and must be specified at least once. .RE .RE .PP \-\-no\-verify .RS 4 Disable the host\-key document verification. Does not require the host\-key documents to be valid. Do not use for a production request unless you verified the host\-key document beforehand. .RE .RE .PP \-C, \-\-cert .RS 4 Use FILE as a certificate to verify the host\-key or keys. The certificates are used to establish a chain of trust for the verification of the host\-key documents. Specify this option twice to specify the IBM Z signing key and the intermediate CA certificate (signed by the root CA). .RE .RE .PP \-\-crl .RS 4 Use FILE as a certificate revocation list (CRL). The list is used to check whether a certificate of the chain of trust is revoked. Specify this option multiple times to use multiple CRLs. .RE .RE .PP \-\-offline .RS 4 Make no attempt to download CRLs. .RE .RE .PP \-\-root\-ca .RS 4 Use FILE as the root\-CA certificate for the verification. If omitted, the system wide\-root CAs installed on the system are used. Use this only if you trust the specified certificate. .RE .RE .PP \-o, \-\-output .RS 4 Write the generated request to FILE. .RE .RE .PP \-a, \-\-arpk .RS 4 Save the protection key as unencrypted GCM\-AES256 key in FILE Do not publish this key, otherwise your attestation is compromised. .RE .RE .PP \-\-add\-data .RS 4 Specify additional data for the request. Additional data is provided by the Ultravisor and returned during the attestation request and is covered by the attestation measurement. Can be specified multiple times. Optional. Possible values: .RS 4 \- \fBphkh-img\fP: Request the public host-key-hash of the key that decrypted the SE-image as additional-data. \- \fBphkh-att\fP: Request the public host-key-hash of the key that decrypted the attestation request as additional-data. \- \fBsecret-store-hash\fP: Request a hash over all successful Add-secret requests and the lock state as additional-data. \- \fBfirmware-state\fP: Request the state of the firmware as additional-data. .RE .RE .PP \-h, \-\-help .RS 4 Print help (see a summary with \fB\-h\fR). .RE .RE .SH EXAMPLES Create an attestation request with the protection key 'arp.key', write the request to 'arcb.bin', and verify the host-key document using the CA-signed key 'DigiCertCA.crt' and the intermediate key 'IbmSigningKey.crt'. .PP .nf .fam C $ pvattest create \-k hkd.crt -\-\arpk arp.key \-o attreq.bin \-\-cert DigiCertCA.crt \-\-cert IbmSigningKey.crt .fam T .fi Create an attestation request with the protection key 'arp.key', write the request to 'arcb.bin', verify the host-key document using the CA-signed key 'DigiCertCA.crt' and the intermediate key 'IbmSigningKey.crt', and instead of downloading the certificate revocation list use certificate revocation lists 'DigiCertCA.crl', 'IbmSigningKey.crl', and 'rootCA.crl'. .PP .nf .fam C $ pvattest create \-k hkd.crt \-\-arpk arp.key \-o attreq.bin \-\-cert DigiCertCA.crt \-\-cert IbmSigningKey.crt \-\-offline \-\-crl DigiCertCA.crl \-\-crl IbmSigningKey.crl \-\-crl rootCA.crl .fam T .fi .SH "SEE ALSO" .sp \fBpvattest\fR(1) s390-tools-2.38.0/rust/pvattest/man/pvattest-perform.1000066400000000000000000000026731502674226300225050ustar00rootroot00000000000000.\" Copyright 2024, 2025 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH "PVATTEST-PERFORM" "1" "2025-03-12" "s390-tools" "Attestation Manual" .nh .ad l .SH NAME pvattest-perform \- Send the attestation request to the Ultravisor .SH SYNOPSIS .nf .fam C pvattest perform [OPTIONS] [IN] [OUT] .fam C .fi .SH DESCRIPTION Run a measurement of this system through ’/dev/uv’. This device must be accessible and the attestation Ultravisor facility must be present. The input must be an attestation request created with ’pvattest create’. Output will contain the original request and the response from the Ultravisor. .SH OPTIONS .PP .RS 4 Specify the request to be sent. .RE .RE .PP .RS 4 Write the result to FILE. .RE .RE .PP \-u, \-\-user\-data .RS 4 Provide up to 256 bytes of user input User\-data is arbitrary user\-defined data appended to the Attestation measurement. It is verified during the Attestation measurement verification. May be any arbitrary data, as long as it is less or equal to 256 bytes .RE .RE .PP \-h, \-\-help .RS 4 Print help (see a summary with \fB\-h\fR). .RE .RE .SH EXAMPLES Perform an attestation measurement with the attestation request 'attreq.bin' and write the output to 'attresp.bin'. .PP .nf .fam C $ pvattest perform attreq.bin attresp.bin .fam T .fi .SH "SEE ALSO" .sp \fBpvattest\fR(1) s390-tools-2.38.0/rust/pvattest/man/pvattest-verify.1000066400000000000000000000065351502674226300223400ustar00rootroot00000000000000.\" Copyright 2024, 2025 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH "PVATTEST-VERIFY" "1" "2025-03-12" "s390-tools" "Attestation Manual" .nh .ad l .SH NAME pvattest-verify \- Verify an attestation response .SH SYNOPSIS .nf .fam C pvattest verify [OPTIONS] --input --hdr --arpk .fam C .fi .SH DESCRIPTION Verify that a previously generated attestation measurement of an IBM Secure Execution guest is as expected. Only verify attestation requests in a trusted environment, such as your workstation. Input must contain the response as produced by ’pvattest perform’. The protection key must be the one that was used to create the request by ’pvattest create’. Shred the protection key after the verification. The header must be the IBM Secure Execution header of the image that was attested during ’pvattest perform’. The verify command solely verifies that the Attestation measurement is correct. It does not check for the content of additional data or user data. See `pvattest check` for policy checks after you verified the Attestation measurement. .SH OPTIONS .PP \-i, \-\-input .RS 4 Specify the attestation response to be verified. .RE .RE .PP \-o, \-\-output .RS 4 Specify the output for the verification result. .RE .RE .PP \-\-hdr .RS 4 Specifies the header of the guest image. Can be an IBM Secure Execution image created by genprotimg or an extracted IBM Secure Execution header. The header must start at a page boundary. .RE .RE .PP \-a, \-\-arpk .RS 4 Use FILE as the protection key to decrypt the request Do not publish this key, otherwise your attestation is compromised. Delete this key after verification. .RE .RE .PP \-\-format .RS 4 Define the output format. [default: 'yaml'] Possible values: .RS 4 \- \fByaml\fP: Use yaml format. .RE .RE .PP \-u, \-\-user\-data .RS 4 Write the user data to the FILE if any. Writes the user data, if the response contains any, to FILE The user\-data is part of the attestation measurement. If the user\-data is written to FILE the user\-data was part of the measurement and verified. Emits a warning if the response contains no user\-data. .RE .RE .PP \-h, \-\-help .RS 4 Print help (see a summary with \fB\-h\fR). .RE .RE .SH EXIT STATUS .TP 8 .B 0 - Attestation Verified Attesatation measurement verified successfully. Measured guest is in Secure Execution mode. .RE .TP 8 .B 1 - Program Error Something went wrong during the local calculation or receiving of the measurement value. Refer to the error message. .RE .TP 8 .B 2 - Attestation NOT Verified Attesation measurement calculation does not match the received value. Measured guest is very likely not in Secure Execution mode. .RE .SH EXAMPLES To verify a measurement in 'measurement.bin' with the protection key 'arp.kep' and SE-guest header 'se_guest.hdr'. .PP .nf .fam C $ pvattest verify --input attresp.bin --arpk arp.key --hdr se_guest.hdr .fam T .fi If the verification was successful the program exists with zero. If the verification failed it exists with 2 and prints the following to stderr: .PP .nf .fam C ERROR: Attestation measurement verification failed: Calculated and received attestation measurement are not the same. .fam T .fi .SH "SEE ALSO" .sp \fBpvattest\fR(1) s390-tools-2.38.0/rust/pvattest/man/pvattest.1000066400000000000000000000044241502674226300210310ustar00rootroot00000000000000.\" Copyright 2024, 2025 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH "PVATTEST" "1" "2025-03-12" "s390-tools" "Attestation Manual" .nh .ad l .SH NAME pvattest \- create, perform, and verify attestation measurements .SH SYNOPSIS .nf .fam C pvattest [OPTIONS] .fam C .fi .SH DESCRIPTION Create, perform, and verify attestation measurements for IBM Secure Execution guest systems. .SH "PVATTEST COMMANDS" .PP \fBpvattest-create(1)\fR .RS 4 Create an attestation measurement request .RE .PP \fBpvattest-perform(1)\fR .RS 4 Send the attestation request to the Ultravisor .RE .PP \fBpvattest-verify(1)\fR .RS 4 Verify an attestation response .RE .PP \fBpvattest-check(1)\fR .RS 4 Check if the attestation result matches defined policies .RE .SH OPTIONS .PP \-v, \-\-verbose .RS 4 Provide more detailed output. .RE .RE .PP \-q, \-\-quiet .RS 4 Provide less output. .RE .RE .PP \-\-version .RS 4 Print version information and exit. .RE .RE .PP \-h, \-\-help .RS 4 Print help (see a summary with \fB\-h\fR). .RE .RE .SH EXAMPLES For details refer to the man page of the command. .PP Create the request on a trusted system. .PP .nf .fam C trusted:~$ pvattest create \-k hkd.crt \-\-cert CA.crt \-\-cert ibmsk.crt \-\-arpk arp.key \-o attreq.bin .fam T .fi On the SE-guest, \fIperform\fP the attestation. .PP .nf .fam C seguest:~$ pvattest perform attreq.bin attresp.bin .fam T .fi On a trusted system, \fIverify\fP that the response is correct. Here, the protection key from the creation and the SE-guest’s header is used to \fIverify\fP the measurement. .PP .nf .fam C trusted:~$ pvattest verify \-i attresp.bin \-\-arpk arp.key \-\-hdr se_guest.hdr trusted:~$ echo $? 0 .fam T .fi If the measurements do not match \fBpvattest\fP exits with code 2 and emits an error message. The SE-guest attestation failed. .PP .nf .fam C trusted:~$ pvattest verify \-i wrongresp.bin \-\-arpk arp.key \-\-hdr se_guest.hdr ERROR: Attestation measurement verification failed: Calculated and received attestation measurement are not the same. trusted:~$ echo $? 2 .fam T .fi .SH "SEE ALSO" .sp \fBpvattest-create\fR(1) \fBpvattest-perform\fR(1) \fBpvattest-verify\fR(1) \fBpvattest-check\fR(1) s390-tools-2.38.0/rust/pvattest/src/000077500000000000000000000000001502674226300171055ustar00rootroot00000000000000s390-tools-2.38.0/rust/pvattest/src/additional.rs000066400000000000000000000033761502674226300215740ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use crate::exchange::ExchangeFormatResponse; use anyhow::Result; use pv::attest::{AdditionalData, AttestationFlags}; use serde::Serialize; use std::fmt::Display; use utils::HexSlice; #[derive(Serialize)] pub struct AttestationResult<'a> { pub cuid: HexSlice<'a>, #[serde(skip_serializing_if = "Option::is_none")] pub add: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub add_fields: Option>>, #[serde(skip_serializing_if = "Option::is_none")] pub user_data: Option>, } impl<'a> AttestationResult<'a> { pub fn from_exchange( resp: &'a ExchangeFormatResponse, flags: &AttestationFlags, ) -> Result { let add_fields = resp .additional() .map(|a| AdditionalData::from_slice_sized(a, flags)) .transpose()?; Ok(Self { cuid: resp.config_uid().into(), add: resp.additional().map(|a| a.into()), add_fields, user_data: resp.user().map(|u| u.into()), }) } } impl Display for AttestationResult<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { writeln!(f, "Config UID:")?; writeln!(f, "{:#}", self.cuid)?; if let Some(data) = &self.add { writeln!(f, "Additional-data:")?; writeln!(f, "{:#}", data)?; } if let Some(data) = &self.add_fields { writeln!(f, "Additional-data content:")?; writeln!(f, "{:#}", data)?; } if let Some(data) = &self.user_data { writeln!(f, "user-data:")?; writeln!(f, "{:#}", data)?; } Ok(()) } } s390-tools-2.38.0/rust/pvattest/src/cli.rs000066400000000000000000000304161502674226300202260ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::path::PathBuf; use clap::{Args, Parser, Subcommand, ValueEnum, ValueHint}; use utils::{CertificateOptions, DeprecatedVerbosityOptions}; /// create, perform, and verify attestation measurements /// /// Create, perform, and verify attestation measurements for IBM Secure Execution guest systems. #[derive(Parser, Debug)] pub struct CliOptions { #[clap(flatten)] pub verbosity: DeprecatedVerbosityOptions, /// Print version information and exit // Implemented for the help message only. Actual parsing happens in the // version command. #[arg(long)] pub version: bool, #[command(subcommand)] pub cmd: Command, } #[derive(Subcommand, Debug)] pub enum Command { /// Create an attestation measurement request. /// /// Create attestation measurement requests to attest an IBM Secure Execution guest. Only build /// attestation requests in a trusted environment such as your Workstation. To avoid /// compromising the attestation do not publish the attestation request protection key and /// shred it after verification. Every 'create' will generate a new, random protection key. Create(Box), /// Send the attestation request to the Ultravisor. /// /// Run a measurement of this system through ’/dev/uv’. This device must be accessible and the /// attestation Ultravisor facility must be present. The input must be an attestation request /// created with ’pvattest create’. Output will contain the original request and the response /// from the Ultravisor. Perform(PerformAttOpt), /// Verify an attestation response. /// /// Verify that a previously generated attestation measurement of an IBM Secure Execution guest /// is as expected. Only verify attestation requests in a trusted environment, such as your /// workstation. Input must contain the response as produced by ’pvattest perform’. The /// protection key must be the one that was used to create the request by ’pvattest create’. /// Shred the protection key after the verification. The header must be the IBM Secure /// Execution header of the image that was attested during ’pvattest perform’. The verify /// command solely verifies that the Attestation measurement is correct. It does not check for /// the content of additional data or user data. See `pvattest check` for policy checks after /// you verified the Attestation measurement. Verify(VerifyOpt), /// Check if the attestation result matches defined policies. /// /// After the attestation verification, check whether the attestation result complies with user-defined policies. Check(CheckOpt), /// Print version information and exit. #[command(aliases(["--version"]), hide(true))] Version, } #[derive(Args, Debug)] pub struct CreateAttOpt { #[command(flatten)] pub certificate_args: CertificateOptions, /// Write the generated request to FILE. #[arg(short, long, value_name = "FILE", value_hint = ValueHint::FilePath,)] pub output: String, /// Save the protection key as unencrypted GCM-AES256 key in FILE /// /// Do not publish this key, otherwise your attestation is compromised. #[arg(short, long, value_name = "FILE", value_hint = ValueHint::FilePath,)] pub arpk: String, /// Specify additional data for the request. /// /// Additional data is provided by the Ultravisor and returned during the attestation request /// and is covered by the attestation measurement. Can be specified multiple times. /// Optional. #[arg( long, value_name = "FLAGS", use_value_delimiter = true, value_delimiter = ',' )] pub add_data: Vec, } #[derive(Debug, ValueEnum, Clone, Copy)] pub enum AttAddFlags { /// Request the public host-key-hash of the key that decrypted the SE-image as additional-data. PhkhImg, /// Request the public host-key-hash of the key that decrypted the attestation request as /// additional-data. PhkhAtt, /// Request a hash over all successful Add-secret requests and the lock state as additional-data. SecretStoreHash, /// Request the state of the firmware as additional-data. FirmwareState, } // all members s390x only #[derive(Args, Debug)] pub struct PerformAttOpt { /// Specify the request to be sent. #[cfg(target_arch = "s390x")] #[arg(hide=true, short, long, value_name = "FILE", value_hint = ValueHint::FilePath,)] pub input: Option, /// Specify the request to be sent. #[cfg(target_arch = "s390x")] #[arg(value_name = "IN", value_hint = ValueHint::FilePath, required_unless_present("input"), conflicts_with("input"))] pub input_pos: Option, /// Write the result to FILE. #[cfg(target_arch = "s390x")] #[arg(hide=true, short, long, value_name = "FILE", value_hint = ValueHint::FilePath,)] pub output: Option, /// Write the result to FILE. #[arg(value_name = "OUT", value_hint = ValueHint::FilePath, required_unless_present("output"), conflicts_with("output"))] #[cfg(target_arch = "s390x")] pub output_pos: Option, /// Provide up to 256 bytes of user input /// /// User-data is arbitrary user-defined data appended to the Attestation measurement. /// It is verified during the Attestation measurement verification. /// May be any arbitrary data, as long as it is less or equal to 256 bytes #[arg(short, long, value_name = "File", value_hint = ValueHint::FilePath,)] pub user_data: Option, } #[cfg(target_arch = "s390x")] #[derive(Debug)] pub struct PerformAttOptComb<'a> { pub input: &'a str, pub output: &'a str, pub user_data: Option<&'a str>, } #[cfg(target_arch = "s390x")] impl<'a> From<&'a PerformAttOpt> for PerformAttOptComb<'a> { fn from(value: &'a PerformAttOpt) -> Self { let input = match (&value.input, &value.input_pos) { (None, Some(i)) => i, (Some(i), None) => i, (Some(_), Some(_)) => unreachable!(), (None, None) => unreachable!(), }; let output = match (&value.output, &value.output_pos) { (None, Some(o)) => o, (Some(o), None) => o, (Some(_), Some(_)) => unreachable!(), (None, None) => unreachable!(), }; let user_data = value.user_data.as_deref(); Self { input, output, user_data, } } } #[derive(Args, Debug)] pub struct VerifyOpt { /// Specify the attestation response to be verified. #[arg(short, long, value_name = "FILE", value_hint = ValueHint::FilePath,)] pub input: String, /// Specify the output for the verification result #[arg(short, long, value_name = "FILE", value_hint = ValueHint::FilePath,)] pub output: Option, /// Specifies the header of the guest image. /// /// Can be an IBM Secure Execution image created by genprotimg or an extracted IBM Secure /// Execution header. The header must start at a page boundary. #[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath)] pub hdr: String, /// Use FILE as the protection key to decrypt the request /// /// Do not publish this key, otherwise your attestation is compromised. /// Delete this key after verification. #[arg(short, long, value_name = "FILE", value_hint = ValueHint::FilePath,)] pub arpk: String, /// Define the output format. #[arg(long, value_enum, default_value_t)] pub format: OutputType, /// Write the user data to the FILE if any. /// /// Writes the user data, if the response contains any, to FILE /// The user-data is part of the attestation measurement. If the user-data is written to FILE /// the user-data was part of the measurement and verified. /// Emits a warning if the response contains no user-data. #[arg(long, short ,value_name = "FILE", value_hint = ValueHint::FilePath,)] pub user_data: Option, } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug, Default)] pub enum OutputType { /// Use yaml format. #[default] Yaml, } #[derive(Args, Debug)] pub struct CheckOpt { /// Specify the attestation response to check whether the policies are validated. #[arg(value_name = "IN", value_hint = ValueHint::FilePath,)] pub input: PathBuf, /// Specify the output file for the check result. #[arg(value_name = "OUT", value_hint = ValueHint::FilePath,)] pub output: PathBuf, /// Define the output format. #[arg(long, value_enum, default_value_t)] pub format: OutputType, /// Use FILE to check for a host-key document. /// /// Verifies that the attestation response contains the host-key hash of one of the specified /// host keys. The check fails if none of the host-keys match the hash in the response. This /// parameter can be specified multiple times. #[arg( short = 'k', long = "host-key-document", value_name = "FILE", value_hint = ValueHint::FilePath, use_value_delimiter = true, value_delimiter = ',', )] pub host_key_documents: Vec, /// Define the host-key check policy /// /// By default, all host-key hashes are checked, and it is not considered a failure if a hash /// is missing from the attestation response. Use this policy switch to trigger a failure if no /// corresponding hash is found. Requires at least one host-key document. #[arg( long = "host-key-check", requires("host_key_documents"), use_value_delimiter = true, value_delimiter = ',' )] pub host_key_checks: Vec, /// Check if the provided user data matches the data from the attestation response. #[arg(short, long, value_name = "FILE", value_hint = ValueHint::FilePath,)] pub user_data: Option, /// Use FILE to include as successful Add-secret request. /// /// Checks if the Attestation response contains the hash of all specified add secret /// requests-tags. The hash is sensible to the order in which the secrets where added. This /// means that if the order of adding here different from the order the add-secret requests /// where sent to the UV this check will fail even though the same secrets are included in the /// UV secret store. Can be specified multiple times. #[arg( long, value_name = "FILE", value_hint = ValueHint::FilePath, use_value_delimiter = true, value_delimiter = ',', requires("secret_store_locked"), )] pub secret: Vec, /// Check whether the guests secret store is locked or not. /// /// Compares the hash of the secret store state to the one calculated by this option and /// optionally specified add-secret-requests in the correct order. If the attestation response /// does not contain a secret store hash, this check fails. /// /// Required if add-secret-requests are specified. #[arg(long, value_name = "BOOL")] pub secret_store_locked: Option, /// Check whether the firmware is supported by IBM. /// /// Requires internet access. #[arg(long)] pub firmware: bool, /// Specify the endpoint to use for firmware version verification. /// /// Use an endpoint you trust. Requires the --firmware option. #[arg(long, requires("firmware"), value_name = "URL", value_hint = ValueHint::Url)] pub firmware_verify_url: Option, } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)] pub enum HostKeyCheckPolicy { /// Check the host-key used for the attestation request. /// /// The check is considered failed, if no given host-key matches the hash from the attestation /// host-key hash, or no attestation host-key has is in the attestation response. AttKeyHash, /// Check the host-key used to the boot the image. /// /// The check is considered failed, if no given host-key matches the hash from the boot /// host-key hash, or no boot host-key has is in the attestation response. BootKeyHash, } #[cfg(test)] mod test { #[test] fn verify_cli() { use clap::CommandFactory; super::CliOptions::command().debug_assert() } } s390-tools-2.38.0/rust/pvattest/src/cmd.rs000066400000000000000000000013621502674226300202200ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 // pub mod check; pub mod create; #[cfg(target_arch = "s390x")] pub mod perform; pub mod verify; pub use check::check; pub use create::create; pub use verify::verify; pub const CMD_FN: &[&str] = &["+create", "+verify"]; // s390 branch #[cfg(target_arch = "s390x")] mod uv_cmd { pub use super::perform::perform; pub const UV_CMD_FN: &[&str] = &["+perform"]; } // non s390-branch #[cfg(not(target_arch = "s390x"))] mod uv_cmd { use std::process::ExitCode; use anyhow::{bail, Result}; pub fn perform(_: &crate::cli::PerformAttOpt) -> Result { bail!("Command only available on s390x") } pub const UV_CMD_FN: &[&str] = &[]; } pub use uv_cmd::*; s390-tools-2.38.0/rust/pvattest/src/cmd/000077500000000000000000000000001502674226300176505ustar00rootroot00000000000000s390-tools-2.38.0/rust/pvattest/src/cmd/check.rs000066400000000000000000000106221502674226300212740ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 mod firmware; mod host_key; mod secret_store; use self::{ firmware::firmware_check, host_key::{host_key_check, HostKeyCheck}, secret_store::secret_store_check, secret_store::SecretStoreCheck, }; use crate::{additional::AttestationResult, cli::CheckOpt, exchange::ExchangeFormatResponse}; use anyhow::Result; use log::{debug, info, warn}; use pv::{ attest::AttestationRequest, misc::{create_file, open_file, read_file}, }; use serde::Serialize; use std::process::ExitCode; use utils::HexSlice; #[derive(Default, Debug)] enum CheckState { #[default] None, Data(T), Err(String), } impl CheckState { fn check(self, issues: &mut Vec) -> Option { match self { Self::None => None, Self::Data(d) => Some(d), Self::Err(e) => { issues.push(e.to_string()); warn!("✘ {e}"); None } } } } impl From> for CheckState { fn from(value: Option) -> Self { value.map_or(Self::None, Self::Data) } } /// Return a [`CheckState::Err`]` #[allow(unused_macro_rules)] macro_rules! bail_check { ($msg:literal) => { return Ok(CheckState::Err($msg.to_string())) }; ($err:expr) => { return Ok(CheckState::Err($err.to_string())) }; ($fmt:expr, $($arg:tt)*) => { return Ok(CheckState::Err(format!($fmt, $($arg)*))) }; } use bail_check; /// Check if the user-data matches with the user-data in the attestation response fn user_data_check<'a>( opt: &CheckOpt, att_res: &'a AttestationResult, ) -> Result>> { let user_data = match &opt.user_data { Some(file) => read_file(file, "user-data")?, None => return Ok(CheckState::None), }; if Some(HexSlice::from(&user_data)) != att_res.user_data { bail_check!( "The Provided user data does not match the user data from the attestation response." ); } info!("✓ Checked user-data"); Ok(att_res.user_data.clone().into()) } #[derive(Debug, Serialize, Default)] pub struct CheckResult<'a> { successful: bool, #[serde(skip_serializing_if = "Vec::is_empty")] issues: Vec, #[serde(skip_serializing_if = "HostKeyCheck::hide")] image_host_key: HostKeyCheck<'a>, #[serde(skip_serializing_if = "HostKeyCheck::hide")] attest_host_key: HostKeyCheck<'a>, #[serde(skip_serializing_if = "Option::is_none")] user_data: Option>, #[serde(skip_serializing_if = "Option::is_none")] secret_store: Option>, #[serde(skip_serializing_if = "Option::is_none")] valid_firmware: Option, } /// Perform the policy checks pub fn check(opt: &CheckOpt) -> Result { let mut input = open_file(&opt.input)?; let inp = ExchangeFormatResponse::read(&mut input)?; let auth = AttestationRequest::auth_bin(inp.arcb())?; let att_res = AttestationResult::from_exchange(&inp, auth.flags())?; let mut issues = vec![]; let image_host_key = host_key_check(opt, host_key::HkCheck::Image, &att_res)? .check(&mut issues) .unwrap(); let attest_host_key = host_key_check(opt, host_key::HkCheck::Attest, &att_res)? .check(&mut issues) .unwrap(); let user_data = user_data_check(opt, &att_res)?.check(&mut issues); let secret_store = secret_store_check(opt, &att_res)?.check(&mut issues); let firmware_check = firmware_check(opt, &att_res)?; let valid_firmware = match firmware_check { CheckState::None => None, CheckState::Data(_) => Some(true), CheckState::Err(_) => Some(false), }; firmware_check.check(&mut issues); let res = CheckResult { successful: issues.is_empty(), issues, image_host_key, attest_host_key, user_data, secret_store, valid_firmware, }; debug!("res {res:?}"); let output = create_file(&opt.output)?; serde_yaml::to_writer(output, &res)?; match res.successful { true => { warn!("✓ The Attestation response fulfills all policies"); Ok(ExitCode::SUCCESS) } false => { warn!("✘ The Attestation response does not fulfill all policies"); Ok(ExitCode::from(crate::EXIT_CODE_ATTESTATION_FAIL)) } } } s390-tools-2.38.0/rust/pvattest/src/cmd/check/000077500000000000000000000000001502674226300207255ustar00rootroot00000000000000s390-tools-2.38.0/rust/pvattest/src/cmd/check/firmware.rs000066400000000000000000000076641502674226300231240ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::{fmt::Display, time::Duration}; use anyhow::{bail, Result}; use base64::prelude::*; use curl::easy::{Easy2, Handler, List, WriteError}; use log::{debug, info, log_enabled}; use serde::{Deserialize, Serialize}; use super::{bail_check, CheckState}; use crate::{additional::AttestationResult, cli::CheckOpt}; const CHECK_DEFAULT_ENDP: &str = "https://www.ibm.com/support/resourcelink/api"; const VERIFY_API: &str = "firmware-attestation/verify/v1"; const TIMEOUT_MAX: Duration = Duration::from_secs(3); const USER_AGENT: &str = "s390-tools-pvattest"; const CONTENT_TYPE: &str = "Content-Type: application/json"; const CLIENT_ID: &str = "x-client-id: X"; #[derive(Debug, Serialize)] struct Request { version: String, payload: String, } impl Request { const VERSION_ONE: &'static str = "1.0"; fn new_v1(firmware_hash: &[u8]) -> Self { Self { version: Self::VERSION_ONE.to_string(), payload: BASE64_STANDARD.encode(firmware_hash), } } } #[allow(unused)] #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] struct Response { version: String, valid: bool, reference_id: String, #[serde(default)] reason: Option, } impl Display for Response { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "The firmware is {}in a valid state", if self.valid { "" } else { "not " } )?; self.reason.as_ref().map_or(Ok(()), |r| { write!(f, "\n Reason: {r}\n ReferenceId: {}", self.reference_id) }) } } #[derive(Debug)] struct Buf(Vec); impl Handler for Buf { fn write(&mut self, data: &[u8]) -> std::result::Result { self.0.extend_from_slice(data); Ok(data.len()) } } fn check>(fw_hash: &U, endp: &str) -> Result> { let req = serde_json::to_vec(&Request::new_v1(fw_hash.as_ref()))?; let url = format!("{endp}/{VERIFY_API}"); debug!("POST {url}"); let mut http_header = List::new(); http_header.append(CLIENT_ID)?; http_header.append(CONTENT_TYPE)?; let mut handle = Easy2::new(Buf(Vec::with_capacity(0x1000))); handle.buffer_size(102400)?; handle.url(&url)?; handle.post_fields_copy(&req)?; handle.http_headers(http_header)?; handle.useragent(USER_AGENT)?; handle.max_redirections(50)?; handle.post(true)?; handle.timeout(TIMEOUT_MAX)?; handle.follow_location(true)?; if log_enabled!(log::Level::Trace) { handle.verbose(true)?; } handle.perform()?; if handle.response_code()? != 200 { bail!( "The firmware verification server responded with http response status code '{}'", handle.response_code()? ); } let resp: Response = match serde_json::from_slice(&handle.get_ref().0) { Ok(res) => res, Err(e) => bail!( "Unexpected response from server: {} \n (\"{}\")", String::from_utf8(handle.get_ref().0.clone()) .unwrap_or_else(|_| "No UTF-8 message".to_string()), e ), }; debug!("Firmware check {resp:?}"); match resp.valid { true => info!("✓ {resp}"), false => bail_check!(&format!("{resp}")), } Ok(CheckState::Data(())) } pub fn firmware_check(opt: &CheckOpt, att_res: &AttestationResult) -> Result> { if !opt.firmware { return Ok(None.into()); } let endp = opt .firmware_verify_url .as_deref() .unwrap_or(CHECK_DEFAULT_ENDP); match att_res .add_fields .as_ref() .and_then(|add| add.firmware_state()) { Some(hash) => check(hash, endp), None => { bail_check!( "The Attestation response contains no firmware hash, but checking was enabled" ) } } } s390-tools-2.38.0/rust/pvattest/src/cmd/check/host_key.rs000066400000000000000000000120401502674226300231150ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use anyhow::Result; use log::{debug, info}; use pv::{ misc::{read_certs, read_file}, request::{openssl::DigestBytes, EcPubKeyCoord}, }; use serde::Serialize; use std::{fmt::Display, path::Path}; use utils::HexSlice; use super::CheckState; use crate::{ additional::AttestationResult, cli::{CheckOpt, HostKeyCheckPolicy}, }; #[derive(Debug, Clone, Copy)] pub enum HkCheck { Image, Attest, } impl Display for HkCheck { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "{} public host-key hash", match self { Self::Image => "image", Self::Attest => "attestation", } ) } } fn load_host_keys>(hkds: &[A]) -> Result> { let mut hkd_hash = Vec::with_capacity(hkds.len()); for hkd in hkds { let hkd = hkd.as_ref(); let hk = read_file(hkd, "host-key document")?; let certs = read_certs(&hk).map_err(|source| pv::Error::HkdNotPemOrDer { hkd: hkd.display().to_string(), source, })?; let ec_coord: EcPubKeyCoord = certs.first().unwrap().public_key()?.as_ref().try_into()?; hkd_hash.push((hkd, ec_coord.sha256()?)); } Ok(hkd_hash) } fn contains_phkh<'a>( hkd_hashes: &[(&'a Path, DigestBytes)], phkh: &HexSlice<'_>, mode: HkCheck, check_enforced: bool, ) -> CheckState> { let hk: Vec<_> = hkd_hashes .iter() .filter_map(|(path, hash)| match hash.as_ref() == phkh.as_ref() { true => Some(*path), false => None, }) .collect(); debug!("HK: {hk:?}"); match hk.len() { 0 => CheckState::Err(format!( "No given host-key document matches the given {mode}" )), 1 => CheckState::Data(HostKeyCheck::new(check_enforced, hk.first().copied())), _ => CheckState::Err(format!( "More than one host-key document matches the given {mode}" )), } } #[derive(Debug, Serialize, Default)] pub struct HostKeyCheck<'a> { check_enforced: bool, #[serde(skip_serializing_if = "Option::is_none")] hash: Option<&'a Path>, } impl<'a> HostKeyCheck<'a> { pub const fn new(check_enforced: bool, hash: Option<&'a Path>) -> Self { Self { check_enforced, hash, } } pub const fn hide(&self) -> bool { self.hash.is_none() && !self.check_enforced } } pub fn host_key_check<'a, 'b>( opt: &'a CheckOpt, kind: HkCheck, att_res: &'b AttestationResult<'b>, ) -> Result>> { if opt.host_key_documents.is_empty() { return Ok(CheckState::Data(HostKeyCheck::default())); } let check_enforced = opt.host_key_checks.contains(&match kind { HkCheck::Image => HostKeyCheckPolicy::BootKeyHash, HkCheck::Attest => HostKeyCheckPolicy::AttKeyHash, }); let hkd_hashes = load_host_keys(&opt.host_key_documents)?; let res = match att_res .add_fields .as_ref() .and_then(|add_fields| match kind { HkCheck::Image => add_fields.image_public_host_key_hash(), HkCheck::Attest => add_fields.attestation_public_host_key_hash(), }) { Some(phkh) => contains_phkh(&hkd_hashes, phkh, kind, check_enforced), None if check_enforced => CheckState::Err(format!( "The Attestation result does not contain an {}, but checking was enabled.", kind )), None => CheckState::Data(HostKeyCheck::default()), }; info!("✓ Check {kind}"); Ok(res) } #[cfg(test)] mod test { use std::path::PathBuf; use super::*; #[test] fn check_hash_neq() { let hostkey = [concat!(env!("CARGO_MANIFEST_DIR"), "/tests/assets/host.pem.crt").to_string()]; let hash = load_host_keys(&hostkey).unwrap(); let res = contains_phkh(&hash, &HexSlice::from(&[0; 32]), HkCheck::Image, true); assert!(matches!(res, CheckState::Err(_))); } #[test] fn check_hash_mul() { let hostkey = [ concat!(env!("CARGO_MANIFEST_DIR"), "/tests/assets/host.pem.crt").to_string(), concat!(env!("CARGO_MANIFEST_DIR"), "/tests/assets/host.pem.crt").to_string(), ]; let hash = load_host_keys(&hostkey).unwrap(); let res = contains_phkh(&hash, &HexSlice::from(&hash[0].1), HkCheck::Image, true); assert!(matches!(res, CheckState::Err(_))); } #[test] fn check_hash_eq() { let hostkey = [concat!(env!("CARGO_MANIFEST_DIR"), "/tests/assets/host.pem.crt").to_string()]; let hash = load_host_keys(&hostkey).unwrap(); let res = contains_phkh(&hash, &HexSlice::from(&hash[0].1), HkCheck::Image, true); assert!(matches!( res, CheckState::Data(s) if s.hash.unwrap() == PathBuf::from(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/assets/host.pem.crt")) )) } } s390-tools-2.38.0/rust/pvattest/src/cmd/check/secret_store.rs000066400000000000000000000117341502674226300240020ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::path::{Path, PathBuf}; use anyhow::Result; use log::info; use openssl::hash::DigestBytes; use openssl::hash::{hash, MessageDigest}; use pv::{misc::read_file, secret::AddSecretRequest}; use serde::Serialize; use super::{bail_check, CheckState}; use crate::{additional::AttestationResult, cli::CheckOpt}; #[derive(Debug, Serialize)] pub struct SecretStoreCheck<'a> { add_secret_requests: &'a [PathBuf], locked: bool, } const REQUEST_TAG_SIZE: usize = 16; fn secret_store_hash>(asrcbs: &[A], locked: bool) -> Result { let mut requests = Vec::with_capacity(asrcbs.len() * REQUEST_TAG_SIZE + 1); for asrcb in asrcbs { let asrcb = read_file(asrcb, "Add-secret request")?; let mut tag = AddSecretRequest::bin_tag(&asrcb)?; requests.append(&mut tag); } requests.push(locked as u8); Ok(hash(MessageDigest::sha512(), &requests)?) } pub fn secret_store_check<'a>( opt: &'a CheckOpt, att_res: &AttestationResult, ) -> Result>> { // The locked flag is the feature gate of this check let locked = match opt.secret_store_locked { None => return Ok(CheckState::None), Some(state) => state, }; let att_store_hash = match att_res .add_fields .as_ref() .and_then(|add| add.secret_store_hash()) { Some(h) => h, None => bail_check!( "The Attestation response contains no secret-store-hash, but checking was enabled" ), }; if secret_store_hash(&opt.secret, locked)?.as_ref() != att_store_hash.as_ref() { bail_check!("The calculated secret-store-hash does not match with the provided hash"); } info!("✓ Secret Store hash"); Ok(CheckState::Data(SecretStoreCheck { add_secret_requests: &opt.secret, locked, })) } #[cfg(test)] mod test { use super::secret_store_hash; const ASRCB_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/assets/asrcb"); #[test] fn hash() { let asrcbs = [ format!("{ASRCB_DIR}/assoc_derived_default_cuid_one"), format!("{ASRCB_DIR}/assoc_simple_default_cuid_one"), format!("{ASRCB_DIR}/null_none_default_cuid_one"), format!("{ASRCB_DIR}/null_none_default_ncuid_one"), format!("{ASRCB_DIR}/null_simple_default_cuid_one"), format!("{ASRCB_DIR}/assoc_none_default_cuid_one"), format!("{ASRCB_DIR}/null_derived_default_cuid_one"), format!("{ASRCB_DIR}/null_none_default_cuid_seven"), format!("{ASRCB_DIR}/null_none_dump_cuid_one"), ]; let hash = secret_store_hash(&asrcbs, true).unwrap(); let exp = [ 0xd0, 0x48, 0x70, 0x2b, 0x4a, 0x79, 0x47, 0x8b, 0x98, 0x5e, 0x92, 0xe7, 0xed, 0xff, 0x45, 0x3f, 0x63, 0xf2, 0x4, 0x4e, 0x7d, 0x72, 0xfa, 0xf1, 0x2e, 0xfd, 0x2e, 0xae, 0xa0, 0xcd, 0x5, 0x5, 0x55, 0x9e, 0xd6, 0x66, 0x5a, 0x6, 0xf6, 0xb4, 0xf9, 0xc6, 0xfc, 0xf9, 0xf2, 0x96, 0xe, 0x6c, 0xd3, 0xc3, 0xcc, 0x8c, 0xaf, 0xe5, 0x7a, 0xc8, 0x40, 0x6e, 0x61, 0x6b, 0xf9, 0x52, 0x95, 0x17, ]; assert_eq!(&exp, hash.as_ref()); let hash = secret_store_hash(&asrcbs, false).unwrap(); let exp = [ 0x51, 0xce, 0x62, 0xaf, 0x1f, 0x67, 0xb9, 0xe3, 0x25, 0x4b, 0x18, 0x4e, 0x33, 0xb2, 0xaa, 0xd3, 0x10, 0x7, 0x58, 0x1a, 0x39, 0xe9, 0x9c, 0xde, 0xb0, 0x29, 0x98, 0xa3, 0xb6, 0x7f, 0xf4, 0x56, 0xc4, 0x4a, 0x5, 0xee, 0x7d, 0x68, 0xe2, 0x4d, 0xfd, 0x43, 0x6f, 0x2b, 0xe4, 0xc1, 0xe9, 0xf5, 0xc1, 0x1, 0x64, 0x68, 0xda, 0x64, 0x1, 0x5e, 0x9f, 0x9f, 0xa3, 0x15, 0x6e, 0x11, 0xd, 0x6c, ]; assert_eq!(&exp, hash.as_ref()); } #[test] fn hash_empty() { let hash = secret_store_hash::<&str>(&[], true).unwrap(); let exp = [ 0x7b, 0x54, 0xb6, 0x68, 0x36, 0xc1, 0xfb, 0xdd, 0x13, 0xd2, 0x44, 0x1d, 0x9e, 0x14, 0x34, 0xdc, 0x62, 0xca, 0x67, 0x7f, 0xb6, 0x8f, 0x5f, 0xe6, 0x6a, 0x46, 0x4b, 0xaa, 0xde, 0xcd, 0xbd, 0x0, 0x57, 0x6f, 0x8d, 0x6b, 0x5a, 0xc3, 0xbc, 0xc8, 0x8, 0x44, 0xb7, 0xd5, 0xb, 0x1c, 0xc6, 0x60, 0x34, 0x44, 0xbb, 0xe7, 0xcf, 0xcf, 0x8f, 0xc0, 0xaa, 0x1e, 0xe3, 0xc6, 0x36, 0xd9, 0xe3, 0x39, ]; assert_eq!(&exp, hash.as_ref()); let hash = secret_store_hash::<&str>(&[], false).unwrap(); let exp = [ 0xb8, 0x24, 0x4d, 0x2, 0x89, 0x81, 0xd6, 0x93, 0xaf, 0x7b, 0x45, 0x6a, 0xf8, 0xef, 0xa4, 0xca, 0xd6, 0x3d, 0x28, 0x2e, 0x19, 0xff, 0x14, 0x94, 0x2c, 0x24, 0x6e, 0x50, 0xd9, 0x35, 0x1d, 0x22, 0x70, 0x4a, 0x80, 0x2a, 0x71, 0xc3, 0x58, 0xb, 0x63, 0x70, 0xde, 0x4c, 0xeb, 0x29, 0x3c, 0x32, 0x4a, 0x84, 0x23, 0x34, 0x25, 0x57, 0xd4, 0xe5, 0xc3, 0x84, 0x38, 0xf0, 0xe3, 0x69, 0x10, 0xee, ]; assert_eq!(&exp, hash.as_ref()); } } s390-tools-2.38.0/rust/pvattest/src/cmd/create.rs000066400000000000000000000042011502674226300214560ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use crate::{ cli::{AttAddFlags, CreateAttOpt}, exchange::{ExchangeFormatRequest, ExchangeFormatVersion}, }; use anyhow::{bail, Context, Result}; use log::{debug, warn}; use pv::{ attest::{AttestationFlags, AttestationMeasAlg, AttestationRequest, AttestationVersion}, misc::{create_file, write_file}, request::{ReqEncrCtx, Request, SymKey, SymKeyType}, }; use std::process::ExitCode; fn flags(cli_flags: &[AttAddFlags]) -> AttestationFlags { let mut att_flags = AttestationFlags::default(); for flag in cli_flags { match flag { AttAddFlags::PhkhImg => att_flags.set_image_phkh(), AttAddFlags::PhkhAtt => att_flags.set_attest_phkh(), AttAddFlags::SecretStoreHash => att_flags.set_secret_store_hash(), AttAddFlags::FirmwareState => att_flags.set_firmware_state(), } } att_flags } pub fn create(opt: &CreateAttOpt) -> Result { let att_version = AttestationVersion::One; let meas_alg = AttestationMeasAlg::HmacSha512; let mut arcb = AttestationRequest::new(att_version, meas_alg, flags(&opt.add_data))?; debug!("Generated Attestation request"); // Add host-key documents opt.certificate_args .get_verified_hkds("attestation request")? .into_iter() .for_each(|k| arcb.add_hostkey(k)); debug!("Added all host-keys"); let encr_ctx = ReqEncrCtx::random(SymKeyType::Aes256Gcm).context("Failed to generate random input")?; let ser_arcb = arcb.encrypt(&encr_ctx)?; warn!("Successfully generated the request"); let mut output = create_file(&opt.output)?; let exch_ctx = ExchangeFormatRequest::new( ser_arcb, meas_alg.exp_size(), arcb.flags().expected_additional_size(), )?; exch_ctx.write(&mut output, ExchangeFormatVersion::One)?; let arpk = match encr_ctx.prot_key() { SymKey::Aes256(k) => k, _ => bail!("Unexpected key type"), }; write_file( &opt.arpk, arpk.value(), "Attestation request Protection Key", )?; Ok(ExitCode::SUCCESS) } s390-tools-2.38.0/rust/pvattest/src/cmd/perform.rs000066400000000000000000000025001502674226300216650ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use crate::{ cli::PerformAttOptComb, exchange::{ExchangeFormatRequest, ExchangeFormatResponse, ExchangeFormatVersion}, }; use anyhow::Result; use pv::{ misc::{create_file, open_file, read_file}, uv::{AttestationCmd, UvDevice}, }; use std::process::ExitCode; pub fn perform<'a, P>(opt: P) -> Result where P: Into>, { let opt = opt.into(); let mut input = open_file(opt.input)?; let mut output = create_file(opt.output)?; let uvdevice = UvDevice::open()?; let ex_in = ExchangeFormatRequest::read(&mut input)?; let user_data = opt .user_data .map(|u| read_file(u, "user-data")) .transpose()?; let mut cmd = AttestationCmd::new_request( ex_in.arcb.clone().into(), user_data.clone(), ex_in.exp_measurement, ex_in.exp_additional, )?; uvdevice.send_cmd(&mut cmd)?; let measurement = cmd.measurement(); let additional = cmd.additional_owned(); let cuid = cmd.cuid(); let ex_out = ExchangeFormatResponse::new( ex_in.arcb, measurement.to_owned(), additional, user_data, cuid.to_owned(), )?; ex_out.write(&mut output, ExchangeFormatVersion::One)?; Ok(ExitCode::SUCCESS) } s390-tools-2.38.0/rust/pvattest/src/cmd/verify.rs000066400000000000000000000050171502674226300215250ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use anyhow::Result; use log::{debug, warn}; use pv::{ attest::{AttestationItems, AttestationMeasurement, AttestationRequest}, misc::{create_file, open_file, read_exact_file, write_file}, request::{openssl::pkey::PKey, BootHdrTags, Confidential, SymKey}, }; use std::process::ExitCode; use utils::HexSlice; use crate::{ additional::AttestationResult, cli::{OutputType, VerifyOpt}, exchange::ExchangeFormatResponse, EXIT_CODE_ATTESTATION_FAIL, }; pub fn verify(opt: &VerifyOpt) -> Result { let mut input = open_file(&opt.input)?; let mut img = open_file(&opt.hdr)?; let output = opt.output.as_ref().map(create_file).transpose()?; let arpk = SymKey::Aes256( read_exact_file(&opt.arpk, "Attestation request protection key").map(Confidential::new)?, ); let tags = BootHdrTags::from_se_image(&mut img)?; let exchange = ExchangeFormatResponse::read(&mut input)?; let (auth, conf) = AttestationRequest::decrypt_bin(exchange.arcb(), &arpk)?; let meas_key = PKey::hmac(conf.measurement_key())?; let items = AttestationItems::new( &tags, exchange.config_uid(), exchange.user(), conf.nonce().as_ref().map(|v| v.value()), exchange.additional(), ); let measurement = AttestationMeasurement::calculate(items, auth.mai(), &meas_key)?; let uv_meas = exchange.measurement(); if !measurement.eq_secure(uv_meas) { debug!("Measurement values:"); debug!("Recieved: {}", HexSlice::from(uv_meas)); debug!("Calculated: {}", HexSlice::from(measurement.as_ref())); warn!("Attestation measurement verification failed. Calculated and received attestation measurement are not equal."); return Ok(ExitCode::from(EXIT_CODE_ATTESTATION_FAIL)); } warn!("Attestation measurement verified"); // Error impossible CUID is present Attestation verified let pr_data = AttestationResult::from_exchange(&exchange, auth.flags())?; warn!("{pr_data}"); if let Some(mut output) = output { match opt.format { OutputType::Yaml => serde_yaml::to_writer(&mut output, &pr_data)?, }; } if let Some(user_data) = &opt.user_data { match exchange.user() { Some(data) => write_file(user_data, data, "user-data")?, None => { warn!("Location for `user-data` specified, but respose does not contain any user-data") } } }; Ok(ExitCode::SUCCESS) } s390-tools-2.38.0/rust/pvattest/src/exchange.rs000066400000000000000000000625011502674226300212410ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use anyhow::{anyhow, bail, Error, Result}; use pv::{assert_size, request::MagicValue, uv::AttestationCmd, uv::ConfigUid}; use std::{ io::{ErrorKind, Read, Seek, SeekFrom, Write}, mem::size_of, }; use zerocopy::ByteOrder; use zerocopy::{BigEndian, FromBytes, Immutable, IntoBytes, KnownLayout, U32, U64}; const INV_EXCHANGE_FMT_ERROR_TEXT: &str = "The input has not the correct format:"; #[repr(C)] #[derive(Debug, IntoBytes, PartialEq, Eq, Default, FromBytes, Immutable, KnownLayout)] struct Entry { size: U32, offset: U32, } assert_size!(Entry, 8); /// If size == 0 the offset is ignored. (entry does not exist) /// If offset >0 and <0x40 -> invalid format /// If offset == 0 and size > 0 no data saved, however the request will need this amount of memory /// to succeed. Only makes sense for measurement and additional data. This however, is not /// enforced. impl Entry { fn new(size: u32, offset: u32) -> Self { Self { size: size.into(), offset: offset.into(), } } /// # Panic /// /// panics if `val` is larger than `max_size` bytes fn from_slice(val: Option<&[u8]>, max_size: u32, offset: &mut u32) -> Self { val.map_or_else(Self::default, |val| { assert!(val.len() <= max_size as usize); let size = val.len() as u32; let res = Self::new(size, *offset); *offset += size; res }) } /// # Panic /// /// panics if `val` is larger than `max_size` bytes fn from_exp(val: Option) -> Self { val.map_or_else(Self::default, |val| Self::new(val, 0)) } fn from_none() -> Self { Self::default() } /// Reads data from stream if required fn read(&self, reader: &mut R) -> Result where R: Read + Seek, { match self { Self { size, .. } if size.get() == 0 => Ok(ExpOrData::None), Self { size, offset } if offset.get() == 0 => Ok(ExpOrData::Exp(size.get())), Self { size, offset } => { reader.seek(SeekFrom::Start(offset.get() as u64))?; let mut buf = vec![0; size.get() as usize]; reader.read_exact(&mut buf)?; Ok(ExpOrData::Data(buf)) } } } } #[repr(C)] #[derive(Debug, IntoBytes, FromBytes, Immutable, KnownLayout)] struct ExchangeFormatV1Hdr { magic: U64, version: U32, size: U32, reserved: U64, /// v1 specific arcb: Entry, measurement: Entry, additional: Entry, user: Entry, config_uid: Entry, } assert_size!(ExchangeFormatV1Hdr, 0x40); impl ExchangeFormatV1Hdr { fn new_request(arcb: &[u8], measurement: u32, additional: u32) -> Result { let mut offset: u32 = size_of::() as u32; let arcb_entry = Entry::from_slice(Some(arcb), AttestationCmd::ARCB_MAX_SIZE, &mut offset); let measurement_entry = Entry::from_exp(Some(measurement)); let exp_add = match additional { 0 => None, size => Some(size), }; // TODO min and max size check? let additional_entry = Entry::from_exp(exp_add); //, AttestationCmd::ADDITIONAL_MAX_SIZE, &mut offset); let user_entry = Entry::from_none(); let cuid_entry = Entry::from_none(); Ok(Self { magic: U64::from_bytes(ExchangeMagic::MAGIC), version: ExchangeFormatVersion::One.into(), size: offset.into(), reserved: 0.into(), arcb: arcb_entry, measurement: measurement_entry, additional: additional_entry, user: user_entry, config_uid: cuid_entry, }) } fn new_response( arcb: &[u8], measurement: &[u8], additional: Option<&[u8]>, user: Option<&[u8]>, config_uid: &[u8], ) -> Result { let mut offset: u32 = size_of::() as u32; let arcb_entry = Entry::from_slice(Some(arcb), AttestationCmd::ARCB_MAX_SIZE, &mut offset); let measurement_entry = Entry::from_slice( Some(measurement), AttestationCmd::MEASUREMENT_MAX_SIZE, &mut offset, ); let additional_entry = Entry::from_slice(additional, AttestationCmd::ADDITIONAL_MAX_SIZE, &mut offset); let user_entry = Entry::from_slice(user, AttestationCmd::USER_MAX_SIZE, &mut offset); let cuid_entry = Entry::from_slice(Some(config_uid), 0x10, &mut offset); Ok(Self { magic: U64::from_bytes(ExchangeMagic::MAGIC), version: ExchangeFormatVersion::One.into(), size: offset.into(), reserved: 0.into(), arcb: arcb_entry, measurement: measurement_entry, additional: additional_entry, user: user_entry, config_uid: cuid_entry, }) } } /// The magic value used to identify an [`ExchangeFormatRequest`] /// /// The magic value is ASCII: /// ```rust /// # use s390_pv_core::attest::ExchangeMagic; /// # use s390_pv_core::request::MagicValue; /// # fn main() { /// # let magic = /// b"pvattest" /// # ; /// # assert!(ExchangeMagic::starts_with_magic(magic)); /// # } /// ``` pub struct ExchangeMagic; impl MagicValue<8> for ExchangeMagic { const MAGIC: [u8; 8] = [0x70, 0x76, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74]; } /// Version identifier for an [`ExchangeFormatRequest`] #[repr(u32)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ExchangeFormatVersion { /// Version 1 (= 0x0100) One = 0x0100, } impl TryFrom> for ExchangeFormatVersion { type Error = Error; fn try_from(value: U32) -> Result { if value.get() == Self::One as u32 { Ok(Self::One) } else { bail!( "{INV_EXCHANGE_FMT_ERROR_TEXT} Unsupported version: ({})", value.get() ); } } } impl From for U32 { fn from(value: ExchangeFormatVersion) -> Self { (value as u32).into() } } /// A parsed exchange entry value /// /// An entry can be all zero(None), just a size (Exp) or a offset+size to some data (Data) #[derive(Debug, PartialEq, Eq)] pub(crate) enum ExpOrData { Exp(u32), Data(Vec), None, } impl ExpOrData { /// calculates the (expected or real) size fn size(&self) -> u32 { match self { Self::Exp(s) => *s, // size is max u32 large as read in before Self::Data(v) => v.len() as u32, Self::None => 0, } } /// Returns data if self is [`ExpOrData::Data`] /// /// Consumes itself fn data(self) -> Option> { match self { Self::Data(v) => Some(v), _ => None, } } } impl From> for ExpOrData { fn from(value: Option) -> Self { value.map_or(Self::None, Self::Exp) } } impl From<&ExpOrData> for Option { fn from(value: &ExpOrData) -> Self { match value { ExpOrData::Exp(v) => Some(*v), _ => None, } } } impl From for Option> { fn from(value: ExpOrData) -> Self { match value { ExpOrData::Exp(s) => Some(vec![0; s as usize]), ExpOrData::Data(d) => Some(d), ExpOrData::None => None, } } } /// The _exchange format_ is a simple file format to send labeled binary blobs between /// pvattest instances on different machines. #[derive(Debug, PartialEq, Eq)] pub struct ExchangeFormatRequest { // all sizes are guaranteed to fit in the exchange format/UV Call at any time // pub to allow deconstruction of this struct pub arcb: Vec, pub exp_measurement: u32, pub exp_additional: u32, } /// The _exchange format_ is a simple file format to send labeled binary blobs between /// pvattest instances on different machines. #[derive(Debug, PartialEq, Eq)] pub struct ExchangeFormatResponse { // all sizes are guaranteed to fit in the exchange format/UV Call at any time // pub to allow deconstruction of this struct pub arcb: Vec, pub measurement: Vec, pub additional: Option>, pub user: Option>, pub config_uid: ConfigUid, } impl ExchangeFormatRequest { /// Creates a new exchange context, with an attestation request, expected measurement and /// optional an additional data size. Useful for creating a attestation request. pub fn new(arcb: Vec, exp_measurement: u32, exp_additional: u32) -> Result { verify_size( exp_measurement, 1, AttestationCmd::MEASUREMENT_MAX_SIZE, "Expected measurement size", )?; verify_size( exp_additional, 0, AttestationCmd::ADDITIONAL_MAX_SIZE, "Expected additional data size", )?; verify_slice(&arcb, AttestationCmd::ARCB_MAX_SIZE, "Attestation request")?; Ok(Self { arcb, exp_measurement, exp_additional, }) } fn write_v1(&self, writer: &mut W) -> Result<()> where W: Write, { let hdr = ExchangeFormatV1Hdr::new_request( self.arcb.as_slice(), self.exp_measurement, self.exp_additional, )?; writer.write_all(hdr.as_bytes())?; writer.write_all(&self.arcb)?; Ok(()) } /// Serializes the encapsulated data into the provides stream in the provided format pub fn write(&self, writer: &mut W, version: ExchangeFormatVersion) -> Result<()> where W: Write, { match version { ExchangeFormatVersion::One => self.write_v1(writer), } } /// Reads and deserializes the exchange file in the provided stream /// /// # Errors /// /// Returns an error if the stream does not contain data in exchange format, CUID or user data /// do not fit, or any IO error that can appear during reading streams. pub fn read(reader: &mut R) -> Result where R: Read + Seek, { let mut buf = vec![0; size_of::()]; match reader.read_exact(&mut buf) { Ok(it) => it, // report hdr file to small for header Err(err) if err.kind() == ErrorKind::UnexpectedEof => { bail!("{INV_EXCHANGE_FMT_ERROR_TEXT} Invalid Header."); } Err(err) => return Err(err.into()), }; if !ExchangeMagic::starts_with_magic(&buf) { bail!("{INV_EXCHANGE_FMT_ERROR_TEXT} Does not start with the magic value.",); } let hdr = ExchangeFormatV1Hdr::ref_from_bytes(buf.as_slice()) .map_err(|_| anyhow!("{INV_EXCHANGE_FMT_ERROR_TEXT} Invalid Header."))?; match TryInto::::try_into(hdr.version)? { ExchangeFormatVersion::One => (), } if stream_len(reader)? < hdr.size.get() as u64 { bail!("{INV_EXCHANGE_FMT_ERROR_TEXT} File size too small"); } let arcb = hdr.arcb.read(reader)?.data().ok_or(anyhow!( "{INV_EXCHANGE_FMT_ERROR_TEXT} Contains no attestation request.", ))?; let measurement = hdr.measurement.read(reader)?.size(); let additional = hdr.additional.read(reader)?.size(); Self::new(arcb, measurement, additional) } } // Seek::stream_is unstable // not expose to API users // taken from rust std::io::seek; fn stream_len(seek: &mut S) -> Result where S: Seek, { let old_pos = seek.stream_position()?; let len = seek.seek(SeekFrom::End(0))?; // Avoid seeking a third time when we were already at the end of the // stream. The branch is usually way cheaper than a seek operation. if old_pos != len { seek.seek(SeekFrom::Start(old_pos))?; } Ok(len) } fn verify_size(size: u32, min_size: u32, max_size: u32, field: &'static str) -> Result<()> { if size < min_size { bail!("{INV_EXCHANGE_FMT_ERROR_TEXT} The {field} field is too small ({size})"); } if size > max_size { bail!("{INV_EXCHANGE_FMT_ERROR_TEXT} The {field} field is too large ({size})"); } Ok(()) } /// check that a slice has at max `max_size` amount of bytes fn verify_slice(val: &[u8], max_size: u32, field: &'static str) -> Result<()> { if val.len() > max_size as usize { bail!( "{INV_EXCHANGE_FMT_ERROR_TEXT} The {field} field is too large ({})", val.len() ); } Ok(()) } impl ExchangeFormatResponse { /// Creates a new exchange context, with an attestation request, measurement and /// cuid. pub fn new( arcb: Vec, measurement: Vec, additional: Option>, user: Option>, config_uid: ConfigUid, ) -> Result { // should not fail; Already checked during import. verify_slice( &arcb, AttestationCmd::ARCB_MAX_SIZE, "Attestation request data", )?; verify_slice( &measurement, AttestationCmd::MEASUREMENT_MAX_SIZE, "Attestation Measurement", )?; if let Some(additional) = &additional { verify_slice( additional, AttestationCmd::ADDITIONAL_MAX_SIZE, "Additional data", )?; } if let Some(user) = &user { verify_slice(user, AttestationCmd::USER_MAX_SIZE, "User data")?; } Ok(Self { arcb, measurement, additional, user, config_uid, }) } fn write_v1(&self, writer: &mut W) -> Result<()> where W: Write, { let hdr = ExchangeFormatV1Hdr::new_response( self.arcb.as_slice(), &self.measurement, self.additional.as_deref(), self.user.as_deref(), &self.config_uid, )?; writer.write_all(hdr.as_bytes())?; writer.write_all(&self.arcb)?; writer.write_all(&self.measurement)?; if let Some(data) = &self.additional { writer.write_all(data)?; } if let Some(data) = &self.user { writer.write_all(data)?; } writer.write_all(&self.config_uid)?; Ok(()) } /// Serializes the encapsulated data into the provides stream in the provided format pub fn write(&self, writer: &mut W, version: ExchangeFormatVersion) -> Result<()> where W: Write, { match version { ExchangeFormatVersion::One => self.write_v1(writer), } } /// Reads and deserializes the exchange file in the provided stream /// /// # Errors /// /// Returns an error if the stream does not contain data in exchange format, CUID or user data /// do not fit, or any IO error that can appear during reading streams. pub fn read(reader: &mut R) -> Result where R: Read + Seek, { let mut buf = vec![0; size_of::()]; match reader.read_exact(&mut buf) { Ok(it) => it, // report hdr file to small for header Err(err) if err.kind() == ErrorKind::UnexpectedEof => { bail!("{INV_EXCHANGE_FMT_ERROR_TEXT} Invalid Header."); } Err(err) => return Err(err.into()), }; if !ExchangeMagic::starts_with_magic(&buf) { bail!("{INV_EXCHANGE_FMT_ERROR_TEXT} Does not start with the magic value."); } let hdr = ExchangeFormatV1Hdr::ref_from_bytes(buf.as_slice()) .map_err(|_| anyhow!("{INV_EXCHANGE_FMT_ERROR_TEXT} Invalid Header."))?; match TryInto::::try_into(hdr.version)? { ExchangeFormatVersion::One => (), } if stream_len(reader)? < hdr.size.get() as u64 { bail!("{INV_EXCHANGE_FMT_ERROR_TEXT} File size too small"); } let arcb = hdr.arcb.read(reader)?.data().ok_or(anyhow!( "{INV_EXCHANGE_FMT_ERROR_TEXT} Contains no attestation request.", ))?; // TODO remove unwrap let measurement = hdr.measurement.read(reader)?.data().ok_or(anyhow!( "{INV_EXCHANGE_FMT_ERROR_TEXT} Contains no attestation response (Measurement missing).", ))?; let additional = hdr.additional.read(reader)?.data(); let user = hdr.user.read(reader)?.data(); let config_uid: ConfigUid = match hdr.config_uid.read(reader)?.data() { Some(v) => v.try_into().map_err(|_| { anyhow!( "{INV_EXCHANGE_FMT_ERROR_TEXT} Configuration UID has an invalid size. Expected size 16, is {}",hdr.config_uid.size.get() ) })?, None => bail!("{INV_EXCHANGE_FMT_ERROR_TEXT} Contains no attestation response (CUID missing).") , }; Self::new(arcb, measurement, additional, user, config_uid) } /// Returns the measurement of this [`ExchangeFormatRequest`]. pub fn measurement(&self) -> &[u8] { &self.measurement } /// Returns the additional data of this [`ExchangeFormatRequest`]. pub fn additional(&self) -> Option<&[u8]> { self.additional.as_deref() } /// Returns the user data of this [`ExchangeFormatRequest`]. pub fn user(&self) -> Option<&[u8]> { self.user.as_deref() } /// Returns the config UID of this [`ExchangeFormatRequest`]. /// /// # Error /// Returns an error if the [`ExchangeFormatRequest`] contains no CUID, pub const fn config_uid(&self) -> &ConfigUid { &self.config_uid } /// Returns a reference to the attestation request of this [`ExchangeFormatRequest`]. pub fn arcb(&self) -> &[u8] { self.arcb.as_ref() } } #[cfg(test)] mod test { use std::io::Cursor; use super::*; use pv::misc::read_file; #[test] fn exchange_from_slice() { let val = &[0; 17]; let mut offset = 18; let entry = Entry::from_slice(Some(val), 20, &mut offset); assert_eq!( entry, Entry { size: 17.into(), offset: 18.into(), } ); assert_eq!(offset, 18 + 17); } static ARCB: [u8; 16] = [0x11; 16]; static MEASUREMENT: [u8; 64] = [0x12; 64]; static ADDITIONAL: [u8; 32] = [0x13; 32]; static CUID: [u8; 16] = [0x14; 16]; static USER: [u8; 256] = [0x15; 256]; fn test_read_write_request( path: &'static str, arcb: Vec, measurement: usize, additional: usize, ) { // TODO as 32 checks let ctx_write = ExchangeFormatRequest::new(arcb, measurement as u32, additional as u32) .expect("exchange fmt creation"); // let mut out = create_file(path).unwrap(); let mut out = vec![]; ctx_write .write(&mut out, ExchangeFormatVersion::One) .unwrap(); let buf = read_file(path, "test read exchange").unwrap(); assert_eq!(out, buf); let ctx_read = ExchangeFormatRequest::read(&mut Cursor::new(&mut &buf)).unwrap(); assert_eq!(ctx_read, ctx_write); } fn test_read_write_response( path: &'static str, arcb: Vec, measurement: Vec, additional: Option>, user: Option>, cuid: ConfigUid, ) { let ctx_write = ExchangeFormatResponse::new(arcb, measurement, additional, user, cuid) .expect("exchange fmt creation"); // let mut out = create_file(path).unwrap(); let mut out = vec![]; ctx_write .write(&mut out, ExchangeFormatVersion::One) .unwrap(); let buf = read_file(path, "test read exchange").unwrap(); assert_eq!(out, buf); let ctx_read = ExchangeFormatResponse::read(&mut Cursor::new(&mut &buf)).unwrap(); assert_eq!(ctx_read, ctx_write); } #[test] fn full_req() { test_read_write_request( concat!( env!("CARGO_MANIFEST_DIR"), "/tests/assets/", "exp/exchange/full_req.bin" ), ARCB.to_vec(), MEASUREMENT.len(), ADDITIONAL.len(), ); } #[test] fn add_req() { test_read_write_request( concat!( env!("CARGO_MANIFEST_DIR"), "/tests/assets/", "exp/exchange/add_req.bin" ), ARCB.to_vec(), MEASUREMENT.len(), ADDITIONAL.len(), ); } #[test] fn invalid_req() { ExchangeFormatRequest::new(ARCB.to_vec(), 0, ADDITIONAL.len() as u32).unwrap_err(); } #[test] fn min_req() { test_read_write_request( concat!( env!("CARGO_MANIFEST_DIR"), "/tests/assets/", "exp/exchange/min_req.bin" ), ARCB.to_vec(), MEASUREMENT.len(), 0, ); } #[test] fn full_resp() { test_read_write_response( concat!( env!("CARGO_MANIFEST_DIR"), "/tests/assets/", "exp/exchange/full_resp.bin" ), ARCB.to_vec(), MEASUREMENT.to_vec(), ADDITIONAL.to_vec().into(), USER.to_vec().into(), CUID, ); } #[test] fn add_resp() { test_read_write_response( concat!( env!("CARGO_MANIFEST_DIR"), "/tests/assets/", "exp/exchange/add_resp.bin" ), ARCB.to_vec(), MEASUREMENT.to_vec(), ADDITIONAL.to_vec().into(), None, CUID, ); } #[test] fn user_resp() { test_read_write_response( concat!( env!("CARGO_MANIFEST_DIR"), "/tests/assets/", "exp/exchange/user_resp.bin" ), ARCB.to_vec(), MEASUREMENT.to_vec(), None, USER.to_vec().into(), CUID, ); } #[test] fn min_resp() { test_read_write_response( concat!( env!("CARGO_MANIFEST_DIR"), "/tests/assets/", "exp/exchange/min_resp.bin" ), ARCB.to_vec(), MEASUREMENT.to_vec(), None, None, CUID, ) } #[test] fn resp_no_cuid() { let buf = include_bytes!(concat!( env!("CARGO_MANIFEST_DIR"), "/tests/assets/", "exp/exchange/min_req.bin" )); let _ctx_read = ExchangeFormatResponse::read(&mut Cursor::new(&mut &buf)).unwrap_err(); } #[test] fn resp_inv_magic() { let mut buf = read_file( concat!( env!("CARGO_MANIFEST_DIR"), "/tests/assets/", "exp/exchange/min_req.bin" ), "test resp inv magic", ) .unwrap(); // tamper with the magic buf[0] = !buf[0]; let _ctx_read = ExchangeFormatResponse::read(&mut Cursor::new(&mut &buf)).unwrap_err(); } #[test] fn no_arcb() { let mut buf = read_file( concat!( env!("CARGO_MANIFEST_DIR"), "/tests/assets/", "exp/exchange/min_req.bin" ), "test resp inv magic", ) .unwrap(); // delete the arcb entry buf[0x18..0x20].copy_from_slice(&[0; 8]); let _ctx_read = ExchangeFormatRequest::read(&mut Cursor::new(&mut &buf)).unwrap_err(); } #[test] fn small() { let mut buf = read_file( concat!( env!("CARGO_MANIFEST_DIR"), "/tests/assets/", "exp/exchange/min_req.bin" ), "test resp inv magic", ) .unwrap(); buf.pop(); let _ctx_read = ExchangeFormatRequest::read(&mut Cursor::new(&mut &buf)).unwrap_err(); } #[test] fn hdr() { // buffer smaller than the header but containing the magic let buf = [ 0x70, 0x76, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x1, 0x2, 0x3, 0x4, ]; let _ctx_read = ExchangeFormatRequest::read(&mut Cursor::new(&mut &buf)).unwrap_err(); } #[test] fn version() { let mut buf = read_file( concat!( env!("CARGO_MANIFEST_DIR"), "/tests/assets/", "exp/exchange/min_req.bin" ), "test resp inv magic", ) .unwrap(); // tamper with the version buf[0x8] = 0xff; let _ctx_read = ExchangeFormatRequest::read(&mut Cursor::new(&mut &buf)).unwrap_err(); } #[test] fn cuid_size() { let mut buf = read_file( concat!( env!("CARGO_MANIFEST_DIR"), "/tests/assets/", "exp/exchange/min_resp.bin" ), "test resp inv magic", ) .unwrap(); // tamper with the cuid size buf[0x3b] = 0xf; let _ctx_read = ExchangeFormatResponse::read(&mut Cursor::new(&mut &buf)).unwrap_err(); } } s390-tools-2.38.0/rust/pvattest/src/main.rs000066400000000000000000000030021502674226300203720ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 #![allow(missing_docs)] mod additional; mod cli; mod cmd; mod exchange; use clap::{CommandFactory, Parser}; use cli::{CliOptions, Command}; use log::trace; use std::process::ExitCode; use utils::{print_cli_error, print_error, print_version, PvLogger}; use crate::cmd::{check, create, perform, verify, CMD_FN, UV_CMD_FN}; static LOGGER: PvLogger = PvLogger; const FEATURES: &[&[&str]] = &[CMD_FN, UV_CMD_FN]; const EXIT_CODE_ATTESTATION_FAIL: u8 = 2; const EXIT_CODE_LOGGER_FAIL: u8 = 3; fn main() -> ExitCode { let cli: CliOptions = match CliOptions::try_parse() { Ok(cli) => cli, Err(e) => return print_cli_error(e, CliOptions::command()), }; // set up logger/stderr let log_level = cli.verbosity.to_level_filter(); if let Err(e) = LOGGER.start(log_level) { // should(TM) never happen eprintln!("Logger error: {e:?}"); return EXIT_CODE_LOGGER_FAIL.into(); } trace!("Trace verbosity, may leak secrets to command-line"); trace!("Options {cli:?}"); let res = match &cli.cmd { Command::Create(opt) => create(opt), Command::Perform(opt) => perform(opt), Command::Verify(opt) => verify(opt), Command::Version => { print_version!("2024", log_level; FEATURES.concat()); Ok(ExitCode::SUCCESS) } Command::Check(opt) => check(opt), }; match res { Ok(c) => c, Err(e) => print_error(&e, log_level), } } s390-tools-2.38.0/rust/pvattest/tests/000077500000000000000000000000001502674226300174605ustar00rootroot00000000000000s390-tools-2.38.0/rust/pvattest/tests/assets/000077500000000000000000000000001502674226300207625ustar00rootroot00000000000000s390-tools-2.38.0/rust/pvattest/tests/assets/asrcb000077700000000000000000000000001502674226300275352../../../pv/tests/assets/exp/asrcbustar00rootroot00000000000000s390-tools-2.38.0/rust/pvattest/tests/assets/exp/000077500000000000000000000000001502674226300215565ustar00rootroot00000000000000s390-tools-2.38.0/rust/pvattest/tests/assets/exp/exchange/000077500000000000000000000000001502674226300233405ustar00rootroot00000000000000s390-tools-2.38.0/rust/pvattest/tests/assets/exp/exchange/add_req.bin000066400000000000000000000001201502674226300254220ustar00rootroot00000000000000pvattestP@@ s390-tools-2.38.0/rust/pvattest/tests/assets/exp/exchange/add_resp.bin000066400000000000000000000003001502674226300256040ustar00rootroot00000000000000pvattest@@P s390-tools-2.38.0/rust/pvattest/tests/assets/exp/exchange/full.bin000066400000000000000000000007001502674226300247710ustar00rootroot00000000000000pvattest@@P s390-tools-2.38.0/rust/pvattest/tests/assets/exp/exchange/full_req.bin000066400000000000000000000001201502674226300256340ustar00rootroot00000000000000pvattestP@@ s390-tools-2.38.0/rust/pvattest/tests/assets/exp/exchange/full_resp.bin000066400000000000000000000007001502674226300260220ustar00rootroot00000000000000pvattest@@P s390-tools-2.38.0/rust/pvattest/tests/assets/exp/exchange/min_req.bin000066400000000000000000000001201502674226300254550ustar00rootroot00000000000000pvattestP@@s390-tools-2.38.0/rust/pvattest/tests/assets/exp/exchange/min_resp.bin000066400000000000000000000002401502674226300256420ustar00rootroot00000000000000pvattest@@Ps390-tools-2.38.0/rust/pvattest/tests/assets/exp/exchange/user_req.bin000066400000000000000000000005201502674226300256540ustar00rootroot00000000000000pvattestP@@Ps390-tools-2.38.0/rust/pvattest/tests/assets/exp/exchange/user_resp.bin000066400000000000000000000006401502674226300260410ustar00rootroot00000000000000pvattest@@Ps390-tools-2.38.0/rust/pvattest/tests/assets/host.pem.crt000066400000000000000000000032501502674226300232310ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIEvDCCAqSgAwIBAgIUaAbjRvw8jn+JnUpX8FvywTRln98wDQYJKoZIhvcNAQEN BQAwgcwxCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y azEVMBMGA1UEBwwMUG91Z2hrZWVwc2llMScwJQYDVQQLDB5JQk0gWiBIb3N0IEtl eSBTaWduaW5nIFNlcnZpY2UwIBcNMjEwNjAxMDkxODA3WhgPMjI5NTAzMTcwOTE4 MDdaMIG2MQswCQYDVQQGEwJVUzE0MDIGA1UECgwrSW50ZXJuYXRpb25hbCBCdXNp bmVzcyBNYWNoaW5lcyBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrSW50ZXJuYXRpb25h bCBCdXNpbmVzcyBNYWNoaW5lcyBDb3Jwb3JhdGlvbjERMA8GA1UECAwITmV3IFlv cmsxDzANBgNVBAcMBkFybW9uazEXMBUGA1UECwwOSUJNIFogSG9zdCBLZXkwgZsw EAYHKoZIzj0CAQYFK4EEACMDgYYABAEm7h/+hliO9pRO+nuDpkzuvdAlcMaiO/B4 Fa7gDHP59/E5s86G9GDBr1dqGNeJeB0wX8FTxZI2YJPv+T9kJDxaJQGenakJOdGe SkkVjcdf4+65leLN2CzUMl0/csUu8jTZNsdFIL2vVMuY9ruT2se6tPdR4YhuoQRj ADli4nUOsj/wcqMwMC4wHAYDVR0fBBUwEzARoA+gDYYLaWJtLmNydC5wZW0wDgYD VR0PAQH/BAQDAgMIMA0GCSqGSIb3DQEBDQUAA4ICAQBZNR9MypBMNgT44FsKKQDG Cduozb/NbcQFVsY7EV5VaeOjMzYSpKYNHZhJekVT8FRzRNrqcsaIvJMixPyx773g GlYbE23Y6ktFvjwgbPrllBqsepoowCk04e3kt4yGYoo8MKQhYmSizcfIa8rXIjon 3INJHysh50hlWQgGScgOP1IK6vweKn+O9UcBxHuO9xm67NKmWVyEJkABB048eGFd P10becukiN7XjBacxGnT8haKkinB0hoj1AzXHORZYaG3JM8KF+G/tcBhJcxc9qJd XpGsvEIiHXg5JvaKmk4wX4y36O3koFc5fzm1fNEFt4k5OaKxI5jglmws/Tf7Q8qq QR2WBcQ3dfqToFeDL7PXH4bgLkiAz1PFZpJGqCxKwI1dFc57fhpwGqoVDynH36mE QSFMHGxSzigZYWCAlk/vYTDF10IuKwxaseuuaEXSfGfIl8Nmw/k5w4kBhk7i3vh7 QasnhCaDir13QjCZ/uPbXEc5RlgQ+fZVmG9HoZv9rnKlxlLzYYxHR5idKku1D789 bn2YsN4J9yxBGdSsG1qWD+pUbtn9vn7LbD5eXFmIiKEn3aDgyUBBFTdB24OgKRVn tbBNLGc+yMEBH5BCGVHM5Y7tHo4cgCCbmD+PStQ9w+0+WxBwYt1n7La2Sx22K7e8 FLwjv+dXJRmO54vY3laicQ== -----END CERTIFICATE----- s390-tools-2.38.0/rust/pvattest/tools/000077500000000000000000000000001502674226300174565ustar00rootroot00000000000000s390-tools-2.38.0/rust/pvattest/tools/Makefile000066400000000000000000000002401502674226300211120ustar00rootroot00000000000000include ../../common.mak install: $(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR) $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 pvextract-hdr $(DESTDIR)$(USRBINDIR) s390-tools-2.38.0/rust/pvattest/tools/pvattest-info000077500000000000000000000045731502674226300222200ustar00rootroot00000000000000#!/bin/bash # # pvattest-info - get additional information from an attestation measurement # # Sample: # ./pvattest-info attestresp.bin # # Copyright IBM Corp. 2022 # # s390-tools is free software; you can redistribute it and/or modify # it under the terms of the MIT license. See LICENSE for details. set -o pipefail set -o nounset set -e XDUMP='od -A x -t x2z -v --endian=big' usage() { cat <<-EOF Usage: $(basename "$0") FILE Prints config UID and additional data if available. EOF } function check_is_pvattest_binary() { local input="$1" local size local version size=$(wc -c <"$input") if [ "$size" -lt 64 ]; then echo "ERROR: Input file is too small." >&2 exit 1 fi ${XDUMP} --read-bytes 16 -- "${input}" 2>/dev/null | grep -q pvattest || { echo "ERROR: ${input} does not contain a pvattest binary output." >&2 && exit 1; } size=$(${XDUMP} --skip-bytes 12 --read-bytes 4 -- "${input}" 2>/dev/null | awk 'NR==1 {print "0x" $2 $3}') if [ $((size)) -lt 64 ]; then echo "ERROR: ${input} does not contain a pvattest binary output." >&2 exit 1 fi version=$(${XDUMP} --skip-bytes 8 --read-bytes 4 -- "$input" 2>/dev/null) echo "$version" | grep -q "0000 0100" || { echo -n "WARNING: unknown hdr version " >&2 && echo "$version" | awk '{print "0x" $2 $3}'>&2 ; } } function print_entry() { local file_off="$1" local text="$2" local input="$3" local size local off size=$(${XDUMP} --skip-bytes $((file_off)) --read-bytes 4 -- "${input}" 2>/dev/null | awk 'NR==1 {print "0x" $2 $3}') off=$(${XDUMP} --skip-bytes $((file_off + 4)) --read-bytes 4 -- "${input}" 2>/dev/null | awk 'NR==1 {print "0x" $2 $3}') if [[ $size != "0x00000000" ]] || [[ $off != "0x00000000" ]]; then echo "${text}:" od -A n -w$((size)) -t x8 --skip-bytes $((off)) --read-bytes $((size)) -- "${input}" 2>/dev/null |\ sed -e 's/\s//g' fi } function require_command() { local cmd="$1" command -v "$cmd" >/dev/null 2>&1 || \ { echo >&2 "ERROR: $cmd required but not installed."; exit 1; } } require_command awk require_command wc require_command od if [ $# -eq 0 ]; then echo "ERROR: Input not set. Use '$(basename "$0") [FILE]' to specify the Input file" >&2 exit 1 fi input="$1" [ -e "$input" ] || { echo "ERROR: File '$1' not found" >&2 && exit 1; } check_is_pvattest_binary "$input" print_entry 0x38 "Config UID" "$input" print_entry 0x28 "Additional Data" "$input" s390-tools-2.38.0/rust/pvattest/tools/pvextract-hdr000077500000000000000000000052531502674226300222040ustar00rootroot00000000000000#!/bin/bash # # pvextract_hdr - extract an IBM Secure Execution header from the Image # # Sample: # ./pvextract-hdr -o sehdr.bin se-image.bin # # Copyright IBM Corp. 2022 # # s390-tools is free software; you can redistribute it and/or modify # it under the terms of the MIT license. See LICENSE for details. set -o pipefail set -o nounset set -e XDUMP='od -A x -t x2z -v --endian=big' def_output='sehdr.bin' def_skip=0x14 def_len=0x4 usage() { cat <<-EOF Usage: $(basename "$0") [-o ${def_output}] [-s ${def_skip}] [-l ${def_len}] FILE Extract the header of the SE-image located in FILE. By default ${def_skip} pages will be skipped until starting to search for the header. By default the search will be stopped after ${def_len} pages. '${def_output}' is the default output file name. EOF } function check_file() { [ -e "$1" ] || { echo "ERROR: File '$1' not found" >&2 && exit 1; } } function check_hdr_ver() { local hdr_start="$1" local input="$2" ${XDUMP} --skip-bytes $((hdr_start + 8)) --read-bytes 4 -- "$input" 2>/dev/null | grep -q "000 0100" || { echo -n "WARNING: unknown hdr version " && ${XDUMP} --skip-bytes $((hdr_start + 8)) --read_bytes 4 -- "$input" 2>/dev/null | awk '{print "0x" $2 $3}'; } } function require_command() { local cmd="$1" command -v "$cmd" >/dev/null 2>&1 || \ { echo >&2 "ERROR: $cmd required but not installed."; exit 1; } } require_command od require_command awk require_command grep output=${def_output} parsed_skip=${def_skip} parsed_len=${def_len} # the last argument must be the input file input="${*: -1}" while getopts 'o:s:l:h' OPTION; do case "$OPTION" in o) output="$OPTARG" ;; s) parsed_skip="$OPTARG" ;; l) parsed_len="$OPTARG" ;; h) usage exit 0 ;; :) echo "ERROR: Must supply an argument to -$OPTARG." >&2 exit 1 ;; *) usage exit 1 ;; esac done #argument specify pages; convert to bytes skip=$((parsed_skip * 0x1000)) len=$((parsed_len * 0x1000)) if [ $# -eq 0 ]; then echo "ERROR: Input not set. Use '$(basename "$0") [FILE]' to specify the Input file" >&2 exit 1 fi check_file "$input" hdr_start=$(${XDUMP} --skip-bytes $((skip)) --read-bytes $((len)) -- "${input}" 2>/dev/null | grep IBMSecEx || { echo ERROR: "${input} does not contain an SE header." >&2 && exit 1; }) hdr_start=$(echo "${hdr_start}" | awk '{print "0x" $1}' | cut -c 1-10) echo "SE header found at offset ${hdr_start}" check_hdr_ver "$hdr_start" "$input" size=$(${XDUMP} --skip-bytes $((hdr_start + 12)) --read-bytes 4 -- "${input}" 2>/dev/null | awk 'NR==1 {print "0x" $2 $3}') dd if="${input}" of="${output}" bs=1 count=$((size)) skip=$((hdr_start)) status=none echo "SE header written to '${output}' ($((size)) bytes)" s390-tools-2.38.0/rust/pvimg/000077500000000000000000000000001502674226300155665ustar00rootroot00000000000000s390-tools-2.38.0/rust/pvimg/Cargo.toml000066400000000000000000000016671502674226300175300ustar00rootroot00000000000000[package] name = "pvimg" version = "0.12.0" edition.workspace = true license.workspace = true rust-version.workspace = true [lints] workspace = true [dependencies] anyhow = { version = "1.0.95", features = ["std"] } clap = { version ="4.5", features = ["derive", "wrap_help"]} deku = "0.18" deku_derive = "0.18" enum_dispatch = "0.3.13" log = { version = "0.4.25", features = ["std", "release_max_level_debug"] } openssl = "0.10.70" serde = { version = "1.0.217", features = ["derive"]} serde_json = "1.0" thiserror = "2.0.11" pv = { path = "../pv", package = "s390_pv" } utils = { path = "../utils" } [build-dependencies] anyhow = { version = "1.0.95", features = ["std"] } clap = { version ="4.5", features = ["derive", "wrap_help"]} clap_complete = "4.5" log = { version = "0.4.25", features = ["std", "release_max_level_debug"] } pv = { path = "../pv", package = "s390_pv" } utils = { path = "../utils" } [dev-dependencies] proptest = "1.6" s390-tools-2.38.0/rust/pvimg/README.md000066400000000000000000000114531502674226300170510ustar00rootroot00000000000000# pvimg `pvimg create` takes a kernel, key files, optionally an initrd image, optionally a file containing the kernel command line parameters, and generates a single, bootable image file. The generated image file consists of a concatenation of a plain text boot loader, the encrypted components for kernel, initrd, kernel command line, and the integrity-protected Secure Execution header, containing the metadata necessary for running the guest in protected mode. See [Memory Layout](#memory-layout) for details about the internal structure of the created image. It is possible to use the generated image as a kernel for zipl or for a direct kernel boot using QEMU. ## Getting started If all dependencies are met a simple `make` call in the source tree should be enough for building `pvimg`. ## Details The main idea of `pvimg create` is: 1. Generate all keys, IVs, and other information needed for the encryption of the components and the generation of the PV header 2. add stub stage3a (so we can calculate the memory addresses) 3. add components: prepare the components (alignment and encryption) and add them to the memory layout 4. build and add stage3b: generate the stage3b and add it to the memory layout 5. generate the Secure Execution header: generate the hashes (pld, ald, and tld) of the components and create the header and IPIB 6. parameterize the stub stage3a: uses the address of the IPIB and Secure Execution header 8. write the final image to the specified output path and generate the boot image metadata at address `0xc000`. The address `0xc000` is chosen as this is the `BSS` section of the stage3a loader and will therefore zeroed out as soon as the stage3a is executed and has therefore no leftovers in the memory. ### Boot Loader The boot loader consists of two parts: 1. stage3a boot loader (cleartext), this loader is responsible for the transition into the protected mode by doing `diag308` subcode 8 and 10 calls. 2. stage3b boot loader (encrypted), this loader is very similar to the normal zipl stage3 boot loader. It will be loaded by the Ultravisor after the successful transition into protected mode. Like the zipl stage3 boot loader it moves the kernel and patches in the values for initrd and kernel command line. The loaders have the following constraints: 1. It must be possible to place stage3a and stage3b at a location greater than 0x10000 because the zipl stage3 loader zeroes out everything at addresses lower than 0x10000 of the image. 2. As the stage3 loader of zipl assumes that the passed kernel image looks like a normal kernel image, the zipl stage3 loader modifies the content at the memory area 0x10400 - 0x10800, therefore we leave this area unused in our stage3a loader. 3. The default entry address used by the zipl stage3 loader is 0x10000 so we add a simple branch to 0x11000 at 0x10000 so the zipl stage3 loader can modify the area 0x10400 - 0x10800 without affecting the stage3a loader. #### Stage3b The `stage3b.bin` is linked at address 0x9000, therefore it will not work at another address. The relocation support for the stage3b loader, so that it can be loaded at addresses != 0x9000, is added in the loader with the name `stage3b_reloc.bin`. By default, if we're talking about stage3b we refer to `stage3b_reloc.bin.` ### Memory Layout The memory layout of the bootable file looks like: | Start | End | Use | |--------------------------|------------|-----------------------------------------------------------------------| | 0 | 0x7 | Short PSW, starting instruction at 0x11000 | | 0x0c000 | 0x0cfff | Image metadata, e.g. it includes the file offset of the SE-header | | 0x10000 | 0x10012 | Branch to 0x11000 | | 0x10013 | 0x10fff | Left intentionally unused | | 0x11000 | 0x12fff | Stage3a | | 0x14000 | 0x1[45]fff | SE-header used for the diag308 call (size can be either 1 or 2 pages) | | `NEXT_PAGE_ALIGNED_ADDR` | | Encrypted kernel | | `NEXT_PAGE_ALIGNED_ADDR` | | Encrypted kernel parameters | | `NEXT_PAGE_ALIGNED_ADDR` | | Encrypted initrd | | `NEXT_PAGE_ALIGNED_ADDR` | | Encrypted stage3b_reloc | | `NEXT_PAGE_ALIGNED_ADDR` | | IPIB used as argument for the diag308 call | s390-tools-2.38.0/rust/pvimg/boot/000077500000000000000000000000001502674226300165315ustar00rootroot00000000000000s390-tools-2.38.0/rust/pvimg/boot/.gitignore000066400000000000000000000000261502674226300205170ustar00rootroot00000000000000*.elf *.lds *.bin *.d s390-tools-2.38.0/rust/pvimg/boot/Makefile000066400000000000000000000057721502674226300202040ustar00rootroot00000000000000# Common definitions include ../../../common.mak FILES := stage3a.bin stage3b.bin stage3b_reloc.bin DEBUG_FILES := $(addsuffix .debug,$(FILES)) ifeq ($(HOST_ARCH),s390x) ZIPL_DIR := $(rootdir)/zipl ZIPL_BOOT_DIR := $(ZIPL_DIR)/boot PKGDATADIR := $(TOOLS_DATADIR)/pvimg INCLUDE_PATHS := $(ZIPL_BOOT_DIR) $(ZIPL_DIR)/include $(rootdir)/include INCLUDE_PARMS := $(addprefix -I,$(INCLUDE_PATHS)) ALL_CFLAGS := $(NO_PIE_CFLAGS) -Os -g \ $(INCLUDE_PARMS) \ -DENABLE_SCLP_ASCII=1 \ -DS390_TOOLS_RELEASE=$(S390_TOOLS_RELEASE) \ -fno-builtin -ffreestanding -fno-asynchronous-unwind-tables \ -fno-delete-null-pointer-checks -fno-stack-protector \ -fexec-charset=IBM1047 -m64 -mpacked-stack \ -mstack-size=4096 -mstack-guard=128 -msoft-float \ -Wall -Wformat-security -Wextra \ -Wno-array-bounds ZIPL_SRCS_C := libc.c ebcdic.c ebcdic_conv.c sclp.c ZIPL_SRCS_ASM := entry.S ZIPL_OBJS_C := $(ZIPL_SRCS_C:%.c=%.o) ZIPL_OBJS_ASM := $(ZIPL_SRCS_ASM:%.S=%.o) ZIPL_OBJS := $(ZIPL_OBJS_C) $(ZIPL_OBJS_ASM) # Prevent make from using some default rules... %: %.S %.o: %.S Makefile $(CC) $(ALL_CFLAGS) -c -o $@ $< %.o: %.c Makefile $(CC) $(ALL_CFLAGS) -c -o $@ $< # Dependencies for the .lds generation sources_lds_S = $(wildcard *.lds.S) dependencies_lds_S = $(sources_lds_S:%.lds.S=.%.lds.d) # Include all ".lds.d" dependency files for all make targets except for "clean" ifneq ($(MAKECMDGOALS),clean) -include $(dependencies_lds_S) endif %.lds: %.lds.S Makefile $(CPP) -Wp,-MD,.$@.d,-MT,$@ $(INCLUDE_PARMS) -P -C -o $@ $< # Special rules for zipl object files $(ZIPL_OBJS_C): %.o : $(ZIPL_BOOT_DIR)/%.c $(CC) $(ALL_CFLAGS) -c -o $@ $< $(ZIPL_OBJS_ASM): %.o : $(ZIPL_BOOT_DIR)/%.S $(CC) $(ALL_CFLAGS) -c -o $@ $< dependencies_zipl_c := $(ZIPL_SRCS_C:%.c=.%.o.d) $(dependencies_zipl_c): .%.o.d : $(ZIPL_BOOT_DIR)/%.c $(CC_SILENT) -MM $(ALL_CPPFLAGS) $(ALL_CFLAGS) $< > $@ ifneq ($(MAKECMDGOALS),clean) -include $(dependencies_zipl_c) endif stage3b_reloc.o: stage3b.bin stage3a.elf: head.o stage3a_init.o $(ZIPL_OBJS) stage3b.elf: head.o $(ZIPL_OBJS) .SECONDARY: $(FILES:.bin=.lds) %.elf: %.lds %.o $(LINK) $(NO_PIE_LDFLAGS) $(NO_WARN_RWX_SEGMENTS_LDFLAGS) -Wl,-T,$< -Wl,--build-id=none -m64 -static -nostdlib $(filter %.o, $^) -o $@ @chmod a-x $@ %.bin.debug: %.elf $(OBJCOPY) --only-keep-debug $< $@ @chmod a-x $@ %.bin: %.elf $(OBJCOPY) -O binary $< $@ @chmod a-x $@ install: stage3a.bin stage3b_reloc.bin $(INSTALL) -d -m 755 $(DESTDIR)$(PKGDATADIR) $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 stage3a.bin $(DESTDIR)$(PKGDATADIR) $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 stage3b_reloc.bin $(DESTDIR)$(PKGDATADIR) else # Don't generate the dependency files (see `common.mak` for the # `-include $(dependencies_c)` statement). .PHONY: $(dependencies_c) $(FILES) $(DEBUG_FILES): echo " SKIP $@ due to HOST_ARCH != s390x" install: echo " SKIP Bootloader installation due to HOST_ARCH != s390x" endif .DEFAULT_GOAL := all all: $(FILES) $(DEBUG_FILES) clean: rm -f -- *.o *.elf *.bin *.map .*.d *.lds *.debug .PHONY: all clean s390-tools-2.38.0/rust/pvimg/boot/common_memory_layout.h000066400000000000000000000011121502674226300231520ustar00rootroot00000000000000/* * Common memory layout for stage3a and stage3b bootloader. * * Copyright IBM Corp. 2020 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef COMMON_MEMORY_LAYOUT_H #define COMMON_MEMORY_LAYOUT_H #include "boot/loaders_layout.h" #define STACK_ADDRESS STAGE3_STACK_ADDRESS #define STACK_SIZE STAGE3_STACK_SIZE #define HEAP_ADDRESS STAGE3_HEAP_ADDRESS #define HEAP_SIZE STAGE3_HEAP_SIZE #ifndef __ASSEMBLER__ #endif /* __ASSEMBLER__ */ #endif /* COMMON_MEMORY_LAYOUT_H */ s390-tools-2.38.0/rust/pvimg/boot/head.S000066400000000000000000000014721502674226300175620ustar00rootroot00000000000000/* * Entry code for stage 3a and stage 3b boot loader * * Copyright IBM Corp. 2020 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include "common_memory_layout.h" #include "boot/s390.h" #include "boot/sigp.h" .section .text.start .globl _start _start: /* Might be called after a diag308 so better set * architecture and addressing mode */ lhi %r1, 1 sigp %r1, %r0, SIGP_SET_ARCHITECTURE sam64 /* Initialize stack */ basr %r13, 0 .Lbase: llgf %r15, .Lstack - .Lbase(%r13) brasl %r14, initialize .Lstack: .long STACK_ADDRESS + STACK_SIZE - STACK_FRAME_OVERHEAD .previous /* The code doesn't require an executable stack */ #if defined(__linux__) && defined(__ELF__) .section .note.GNU-stack,"",%progbits #endif s390-tools-2.38.0/rust/pvimg/boot/stage3a.c000066400000000000000000000131261502674226300202270ustar00rootroot00000000000000/* * Main program for stage3a bootloader * * Copyright IBM Corp. 2020 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include "libc.h" #include "stage3a.h" #include "lib/zt_common.h" #include "boot/error.h" #include "boot/s390.h" #include "boot/ipl.h" #include "sclp.h" /* * The following UV RC and RRC codes correspond to the errors * that occur often or may be fixeable directly by the user when * applying DIAG308 subcode 10 but configuration is unable to * enter the secure mode. Note that it is not an exhaustive list * of all possible UV RCs and RRCs. */ enum UV_RC { UNPACK_VERIFY_MISMATCH = 0x0102, SSC_HDR_VER_MISMATCH = 0x0104, SSC_UNSUPPORTED_PCF = 0x0106, SSC_HOSTKEY_HASH_ERR = 0x0108, SSC_UNSUPPORTED_SCF = 0x0109, SSC_HDR_CORRUPT = 0x010a, }; enum UV_RRC_UNPACK_VERIFY_MISMATCH { ALD_MISMATCH = 0x001A, PLD_MISMATCH = 0x001B, TLD_MISMATCH = 0x001C, NUM_ENC_PAGES_MISMATCH = 0x001D, }; enum UV_RRC_SSC_HDR_VER_MISMATCH { HDR_VER_MISMATCH = 0x0001, }; enum UV_RRC_SSC_UNSUPPORTED_PCF { UNSUPPORTED_PCF = 0x0030, }; enum UV_RRC_SSC_HOSTKEY_HASH_ERR { HOSTKEY_MISMATCH = 0x0005, INVAL_ECDH_KEY_HDR = 0x000B, BACKUP_HOSTKEY_MISMATCH = 0x0034, }; enum UV_RRC_SSC_UNSUPPORTED_SCF { UNSUPPORTED_SCF = 0x0000, }; enum UV_RRC_SSC_HDR_CORRUPT { HDR_LEN_MISMATCH = 0x0031, HDR_SIZE_ENC_INVAL = 0x0039, KEY_SLOT_EMPTY = 0x0040, }; static volatile struct stage3a_args __section(".loader_parms") loader_parms; static void print_error_message(enum UV_RC pv_rc, uint16_t pv_rrc) { switch (pv_rc) { case UNPACK_VERIFY_MISMATCH: switch ((enum UV_RRC_UNPACK_VERIFY_MISMATCH)pv_rrc) { case ALD_MISMATCH: printf("Address digest list (ALD) mismatch.\n"); break; case PLD_MISMATCH: printf("Page digest list (PLD) mismatch.\n"); break; case TLD_MISMATCH: printf("Tweak digest list (TLD) mismatch.\n"); break; case NUM_ENC_PAGES_MISMATCH: printf("Mismatch in number of encrypted pages.\n"); break; } break; case SSC_HDR_VER_MISMATCH: if (pv_rrc == HDR_VER_MISMATCH) printf("Mismatch in IBM Secure Execution image header version.\n"); break; case SSC_UNSUPPORTED_PCF: if (pv_rrc == UNSUPPORTED_PCF) printf("An unsupported plaintext control flag is set.\n"); break; case SSC_HOSTKEY_HASH_ERR: switch ((enum UV_RRC_SSC_HOSTKEY_HASH_ERR)pv_rrc) { case HOSTKEY_MISMATCH: printf("The host key hash of the IBM Secure Execution image "); printf("does not match the host key of the installed key bundle.\n"); break; case INVAL_ECDH_KEY_HDR: printf("The public customer ECDH key in the "); printf("IBM Secure Execution image is not valid.\n"); break; case BACKUP_HOSTKEY_MISMATCH: printf("The secondary host key hash of the "); printf("IBM Secure Execution image does not match the "); printf("host key of the installed key bundle.\n"); break; default: printf("Ensure that the image is "); printf("correctly encrypted for this host.\n"); } break; case SSC_UNSUPPORTED_SCF: if (pv_rrc == UNSUPPORTED_SCF) printf("An unsupported secret control flag is set.\n"); break; case SSC_HDR_CORRUPT: switch ((enum UV_RRC_SSC_HDR_CORRUPT)pv_rrc) { case HDR_LEN_MISMATCH: printf("Mismatch in IBM Secure Execution image header size.\n"); break; case HDR_SIZE_ENC_INVAL: printf("The size of the encrypted area in the "); printf("IBM Secure Execution image is invalid.\n"); break; case KEY_SLOT_EMPTY: printf("There are no host keys in"); printf("the IBM Secure Execution image.\n"); break; } break; } } char *get_cmd_name(uint16_t pv_cmd) { char *cmd_name; /* * QEMU returns command code IDs 2, 3 or 4 corresponding * to the UV commands (SSC, UNPACK or UNPACK VERIFY) when * DIAG 308 subcode is applied and the configuration is * unable to enter the secure mode. */ switch (pv_cmd) { case 2: cmd_name = "KVM_PV_SET_SEC_PARMS"; break; case 3: cmd_name = "KVM_PV_UNPACK"; break; case 4: cmd_name = "KVM_PV_VERIFY"; break; default: // should not reach here cmd_name = "UNKNOWN"; } return cmd_name; } void report_diag308_unpack_pv_error(uint64_t rc) { union { struct { uint16_t pv_cmd; uint16_t pv_rrc; uint16_t pv_rc; uint16_t diag_rc; }; uint64_t regs; } resp = { .regs = rc }; sclp_setup(SCLP_LINE_ASCII_INIT); print_error_message(resp.pv_rc, resp.pv_rrc); panic(EPV, "Protected boot failed: 0x%x, " "%s - RC: 0x%x, RRC:0x%x\n", resp.diag_rc, get_cmd_name(resp.pv_cmd), resp.pv_rc, resp.pv_rrc); } void __noreturn start(void) { volatile struct stage3a_args *args = &loader_parms; uint64_t rc; /* calculate the IPIB memory address */ struct ipl_parameter_block *ipib = (void *)((uint64_t)args + args->ipib_offs); /* Calculate the PV header memory address and set it and its * size in the IPIB. This allows the PV header to be position * independent. */ ipib->pv.pv_hdr_addr = (uint64_t)args + args->hdr_offs; ipib->pv.pv_hdr_size = args->hdr_size; /* set up ASCII and line-mode */ sclp_setup(SCLP_LINE_ASCII_INIT); /* test if Secure Execution Unpack facility is available */ stfle(S390_lowcore.stfle_fac_list, ARRAY_SIZE(S390_lowcore.stfle_fac_list)); rc = test_facility(UNPACK_FACILITY); if (rc == 0) panic(ENOPV, "Secure unpack facility is not available\n"); rc = diag308(DIAG308_SET_PV, ipib); if (rc != DIAG308_RC_OK) panic(EPV, "Protected boot setup has failed: 0x%x\n", rc); rc = diag308(DIAG308_UNPACK_PV, 0x0); if (rc != DIAG308_RC_OK) report_diag308_unpack_pv_error(rc); while (1) ; } void panic_notify(unsigned long UNUSED(rc)) { } s390-tools-2.38.0/rust/pvimg/boot/stage3a.h000066400000000000000000000014741502674226300202370ustar00rootroot00000000000000/* * Main program for stage3a bootloader. * * Copyright IBM Corp. 2020 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef STAGE3A_H #define STAGE3A_H #include "lib/zt_common.h" #include "boot/loaders_layout.h" #define STAGE3A_BSS_ADDRESS _AC(0xc000, UL) #define STAGE3A_BSS_SIZE _AC(0x1000, UL) #define STAGE3A_INIT_ENTRY IMAGE_ENTRY #define STAGE3A_ENTRY (STAGE3A_INIT_ENTRY + _AC(0x1000, UL)) #define STAGE3A_LOAD_ADDRESS IMAGE_LOAD_ADDRESS #ifndef __ASSEMBLER__ #include /* Must not have any padding */ struct stage3a_args { uint64_t hdr_offs; uint64_t hdr_size; uint64_t ipib_offs; }; STATIC_ASSERT(sizeof(struct stage3a_args) == 3 * 8) #endif /* __ASSEMBLER__ */ #endif /* STAGE3A_H */ s390-tools-2.38.0/rust/pvimg/boot/stage3a.lds.S000066400000000000000000000047621502674226300207760ustar00rootroot00000000000000/* * Memory layout for stage 3a * ========================== * * General memory layout * --------------------- * * 0x00000 - 0x01fff Lowcore * 0x02000 - 0x05fff Memory allocation (heap) * 0x0f000 - 0x0ffff Stack * 0x10000 - 0x10012 Jump to the "actual" stage3a code * 0x11000 - 0x12fff Stage3a code + arguments (offsets and lengths to the * actual data: IPIB and UV header) */ #include "stage3a.h" #include "common_memory_layout.h" OUTPUT_FORMAT("elf64-s390", "elf64-s390", "elf64-s390") OUTPUT_ARCH(s390:64-bit) ENTRY(_init) SECTIONS { . = HEAP_ADDRESS; __heap_start = .; .heap : { . = . + HEAP_SIZE; ASSERT(__heap_stop - __heap_start == HEAP_SIZE, "Heap section doesn't conform to the described memory layout"); } __heap_stop = .; . = STAGE3A_BSS_ADDRESS; __bss_start = .; .bss : { *(.bss .bss.*) . = ALIGN(4096); ASSERT(__bss_stop - __bss_start == STAGE3A_BSS_SIZE, "Stack section doesn't conform to the described memory layout"); } __bss_stop = .; . = STACK_ADDRESS; __stack_start = .; .stack : { . = . + STACK_SIZE; ASSERT(__stack_end - __stack_start == STACK_SIZE, "Stack section doesn't conform to the described memory layout"); } __stack_end = .; . = STAGE3A_INIT_ENTRY; __text_init_start = .; .text : { *(.text.init) __text_init_stop = ABSOLUTE(.); /* Text size of text_init must be smaller than 'PARMAREA - IMAGE_ENTRY', * otherwise the text data could be overwritten by the original zipl stage3 * boot loader */ ASSERT(__text_init_stop - __text_init_start < PARMAREA - IMAGE_ENTRY, "Text size must be smaller than 'PARMAREA - IMAGE_ENTRY'"); . = 0x1000; ASSERT(ABSOLUTE(.) == STAGE3A_ENTRY, "Text section doesn't conform to the described memory layout"); *(.text.start) *(.text .text.*) } .ex_table ALIGN(16) : { __ex_table_start = .; *(.ex_table) __ex_table_stop = .; } .rodata ALIGN(16) : { *(.rodata) *(.rodata*) } .data ALIGN(16) : { *(.data) . = ALIGN(16); /* The IPIB offset and the UV header offset and size will be * saved in 'loader_parms' */ __loader_parms_start = .; KEEP(*(.loader_parms)); __loader_parms_stop = .; ASSERT(__loader_parms_stop - __loader_parms_start == 3 * 8, "Data size must be equal to 'sizeof(struct stage3a_args)'"); ASSERT(ABSOLUTE(.) < 0x13000, "Data section doesn't conform to the described memory layout"); } /* Sections to be discarded */ /DISCARD/ : { *(.eh_frame) *(.interp) *(.note.GNU-stack) *(.note.package) } } s390-tools-2.38.0/rust/pvimg/boot/stage3a_init.S000066400000000000000000000015061502674226300212310ustar00rootroot00000000000000/* * Entry code for stage 3a boot loader * * Copyright IBM Corp. 2020 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include "stage3a.h" #include "boot/sigp.h" .section .text.init .globl _init _init: /* set architecture and switch to 64bit */ lhi %r1, 1 sigp %r1, %r0, SIGP_SET_ARCHITECTURE sam64 /* The original stage3 boot loader will try to store the * kernel command line and the address and size of the * ramdisk. Simply ignore this by starting at 0x11000. */ basr %r13, 0 .Lbase: llgf %r1, .Lstage3a_entry - .Lbase(%r13) br %r1 .Lstage3a_entry: .long STAGE3A_ENTRY .previous /* The code doesn't require an executable stack */ #if defined(__linux__) && defined(__ELF__) .section .note.GNU-stack,"",%progbits #endif s390-tools-2.38.0/rust/pvimg/boot/stage3b.c000066400000000000000000000044731502674226300202350ustar00rootroot00000000000000/* * Main program for stage3b bootloader * * Copyright IBM Corp. 2020 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include "libc.h" #include "stage3b.h" #include "lib/zt_common.h" #include "boot/psw.h" #include "boot/error.h" #include "boot/s390.h" #include "boot/linux_layout.h" #include "boot/loaders_layout.h" #include "sclp.h" static volatile struct stage3b_args __section(".loader_parms") loader_parms; static inline void __noreturn load_psw(struct psw_t psw) { asm volatile("lpswe %0" : : "Q"(psw) : "cc"); while (1) ; } static unsigned long get_kernel_cmdline_size(void) { unsigned long size = *(volatile unsigned long *)MAX_COMMAND_LINE_SIZE; if (size != 0) return size; return LEGACY_COMMAND_LINE_SIZE; } void __noreturn start(void) { volatile struct stage3b_args *args = &loader_parms; volatile struct memblob *kernel = &args->kernel; volatile struct memblob *cmdline = &args->cmdline; volatile struct memblob *initrd = &args->initrd; struct psw_t psw = args->psw; /* set up ASCII and line-mode */ sclp_setup(SCLP_LINE_ASCII_INIT); if (kernel->size < IMAGE_LOAD_ADDRESS) panic(EINTERNAL, "Invalid kernel\n"); /* move the kernel and cut the kernel header */ memmove((void *)IMAGE_LOAD_ADDRESS, (void *)(kernel->src + IMAGE_LOAD_ADDRESS), kernel->size - IMAGE_LOAD_ADDRESS); if (cmdline->size > get_kernel_cmdline_size()) panic(EINTERNAL, "Command line is too large\n"); if (cmdline->size > 0) { /* make sure the cmdline is a null-terminated string */ if (((char *)cmdline->src)[cmdline->size - 1] != '\0') panic(EINTERNAL, "Command line needs to be null-terminated\n"); /* move the kernel cmdline */ memmove((void *)COMMAND_LINE, (void *)cmdline->src, cmdline->size); } /* the initrd does not need to be moved */ if (initrd->size > 0) { /* copy initrd start address and size into new kernel space */ *(unsigned long long *)INITRD_START = initrd->src; *(unsigned long long *)INITRD_SIZE = initrd->size; } /* disable ASCII and line-mode */ sclp_setup(SCLP_DISABLE); /* use lpswe instead of diag308 as a I/O subsystem reset is not * needed as this was already done by the diag308 subcode 10 call * in stage3a */ load_psw(psw); } void panic_notify(unsigned long UNUSED(rc)) { } s390-tools-2.38.0/rust/pvimg/boot/stage3b.h000066400000000000000000000015751502674226300202420ustar00rootroot00000000000000/* * Main program for stage3b bootloader * * Copyright IBM Corp. 2020 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #ifndef STAGE3B_H #define STAGE3B_H #include "lib/zt_common.h" #include "boot/loaders_layout.h" #define STAGE3B_ENTRY STAGE3_ENTRY #define STAGE3B_LOAD_ADDRESS STAGE3B_ENTRY #ifndef __ASSEMBLER__ #include #include "boot/psw.h" /* Must not have any padding included */ struct memblob { uint64_t src; uint64_t size; }; STATIC_ASSERT(sizeof(struct memblob) == 2 * 8) /* Must not have any padding included */ struct stage3b_args { struct memblob kernel; struct memblob cmdline; struct memblob initrd; struct psw_t psw; }; STATIC_ASSERT(sizeof(struct stage3b_args) == 3 * sizeof(struct memblob) + 16) #endif /* __ASSEMBLER__ */ #endif /* STAGE3B_H */ s390-tools-2.38.0/rust/pvimg/boot/stage3b.lds.S000066400000000000000000000031221502674226300207640ustar00rootroot00000000000000/* * Memory layout for stage 3b * ========================== * * General memory layout * --------------------- * * 0x00000 - 0x01fff Lowcore * 0x02000 - 0x05fff Memory allocation (heap) * 0x0a000 - 0x0efff Stage3b code * 0x0f000 - 0x0ffff Stack */ #include "stage3b.h" #include "common_memory_layout.h" OUTPUT_FORMAT("elf64-s390", "elf64-s390", "elf64-s390") OUTPUT_ARCH(s390:64-bit) ENTRY(_start) SECTIONS { . = HEAP_ADDRESS; __heap_start = .; .heap : { . = . + HEAP_SIZE; ASSERT(__heap_stop - __heap_start == HEAP_SIZE, "Heap section doesn't conform to the described memory layout"); } __heap_stop = .; . = STAGE3B_ENTRY; .text : { *(.text.start) *(.text .text.*) } .ex_table ALIGN(16) : { __ex_table_start = .; *(.ex_table) __ex_table_stop = .; } .bss ALIGN(16) : { __bss_start = .; *(.bss) __bss_stop = .; } .rodata ALIGN(16) : { *(.rodata) *(.rodata*) } .data ALIGN(16) : { *(.data) . = ALIGN(16); __loader_parms_start = .; KEEP(*(.loader_parms)); __loader_parms_end = .; ASSERT(__loader_parms_end - __loader_parms_start == 3 * 16 + 16, "Data size must be equal to 'sizeof(struct stage3b_args)'"); } . = STACK_ADDRESS; __stack_start = .; .stack : { . = . + STACK_SIZE; ASSERT(__stack_end - __stack_start == STACK_SIZE, "Stack section doesn't conform to the described memory layout"); } __stack_end = .; ASSERT(. <= IMAGE_ENTRY, "stage3b size must be smaller than 0x10000 bytes") /* Sections to be discarded */ /DISCARD/ : { *(.eh_frame) *(.interp) *(.note.GNU-stack) *(.note.package) } } s390-tools-2.38.0/rust/pvimg/boot/stage3b_reloc.S000066400000000000000000000023771502674226300214020ustar00rootroot00000000000000/* * Relocator code for stage 3b boot loader * * Copyright IBM Corp. 2020 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include "stage3b.h" #include "boot/sigp.h" .macro MEMCPY dst,src,len lgr %r0, \dst lgr %r1, \len lgr %r2, \src lgr %r3, \len 20: mvcle %r0, %r2, 0 jo 20b .endm .org 0x0 .section .text.start .globl _start _start: /* Might be called after a diag308 so better set * architecture and addressing mode */ lhi %r1, 1 sigp %r1, %r0, SIGP_SET_ARCHITECTURE sam64 /* Location of stage3b in memory */ larl %r8, stage3b_start /* Destination for stage3b */ basr %r13, 0 .Lbase: llgf %r9, .Lstage3b_load_address - .Lbase(%r13) /* Size of stage3b */ lghi %r11, stage3b_end - stage3b_start /* Copy the stage3b loader to address STAGE3B_LOAD_ADDRESS */ MEMCPY %r9, %r8, %r11 /* Branch to STAGE3B_ENTRY */ llgf %r9, .Lstage3b_entry - .Lbase(%r13) br %r9 .Lstage3b_load_address: .long STAGE3B_LOAD_ADDRESS .Lstage3b_entry: .long STAGE3B_ENTRY stage3b_start: .incbin "stage3b.bin" stage3b_end: .previous /* The code doesn't require an executable stack */ #if defined(__linux__) && defined(__ELF__) .section .note.GNU-stack,"",%progbits #endif s390-tools-2.38.0/rust/pvimg/boot/stage3b_reloc.lds.S000066400000000000000000000004271502674226300221550ustar00rootroot00000000000000OUTPUT_FORMAT("elf64-s390", "elf64-s390", "elf64-s390") OUTPUT_ARCH(s390:64-bit) ENTRY(_start) SECTIONS { .text : { *(.text.start) *(.text .text.*) } /* Sections to be discarded */ /DISCARD/ : { *(.eh_frame) *(.interp) *(.note.GNU-stack) *(.note.package) } } s390-tools-2.38.0/rust/pvimg/build.rs000066400000000000000000000014621502674226300172360ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 // it under the terms of the MIT license. See LICENSE for details. #![allow(missing_docs)] use std::io::Error; use clap_complete::{generate_to, Shell}; include!("src/cli.rs"); fn main() -> Result<(), Error> { let outdir = env::var_os("OUT_DIR").unwrap(); let crate_name = env!("CARGO_PKG_NAME"); for &shell in Shell::value_variants() { for (name, mut cmd) in [ (crate_name, CliOptions::command()), ("genprotimg", GenprotimgCliOptions::command()), ] { generate_to(shell, &mut cmd, name, &outdir)?; } } println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=src/cli.rs"); println!("cargo:rerun-if-changed=../utils/src/cli.rs"); Ok(()) } s390-tools-2.38.0/rust/pvimg/examples/000077500000000000000000000000001502674226300174045ustar00rootroot00000000000000s390-tools-2.38.0/rust/pvimg/examples/create-sehdr/000077500000000000000000000000001502674226300217525ustar00rootroot00000000000000s390-tools-2.38.0/rust/pvimg/examples/create-sehdr/main.rs000066400000000000000000000170641502674226300232540ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 #![allow(missing_docs)] use std::{ fmt::Display, fs::{File, OpenOptions}, io::{BufReader, Read, Write}, path::PathBuf, str::FromStr, }; use anyhow::{anyhow, Context, Error}; use clap::{Parser, ValueHint}; use log::{info, warn}; use pv::{ misc::{decode_hex, open_file, read_certs, read_file, try_parse_u64}, request::SymKeyType, Error as PvError, Result, }; use pvimg::{ misc::PSW, secured_comp::{ComponentTrait, Layout, SecuredComponentBuilder}, uvdata::{BuilderTrait, SeHdrBuilder, SeHdrVersion}, }; use utils::{AtomicFile, AtomicFileOperation, HexSlice, PvLogger, VerbosityOptions}; /// Converts the hexstring into a byte vector. /// /// # Errors /// /// Raises an error if a non-hex character was found or the length was not a /// multiple of two. pub fn decode_hex_str>(s: S) -> Result> { let hex_str = s.as_ref(); let hex_value = if hex_str.starts_with("0x") { hex_str.split_at(2).1 } else { hex_str }; Ok(decode_hex(hex_value)?) } fn decode_u64_hex_str(s: &str) -> Result { Ok(try_parse_u64(s, "The")?) } impl FromStr for ComponentArg { type Err = Error; fn from_str(s: &str) -> Result { let parts: Vec<_> = s.split(',').collect(); if parts.len() != 3 { return Err(anyhow!("Invalid component format.")); } let path = parts[0].into(); let addr = try_parse_u64(parts[1], "Invalid address")?; let mut tweak = decode_hex_str(parts[2]).with_context(|| format!("Invalid tweak {}", parts[2]))?; if tweak.len() > SymKeyType::AES_256_XTS_TWEAK_LEN { return Err(anyhow!( "Invalid tweak because the length of {} is greater than the expected {}.", tweak.len(), SymKeyType::AES_256_XTS_TWEAK_LEN )); } tweak.resize(SymKeyType::AES_256_XTS_TWEAK_LEN, 0x0); Ok(Self { path, addr, tweak }) } } #[derive(Debug, Clone)] struct ComponentArg { path: PathBuf, addr: u64, tweak: Vec, } impl Display for ComponentArg { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "component\n\ Path ......: {:}\n\ Address ...: {:#0x}\n\ Tweak .....: {:#}", self.path.display(), self.addr, HexSlice::from(&self.tweak), ) } } /// Create a Secure Execution header. #[derive(Parser, Debug)] pub struct Args { /// Use FILE as the component, ADDR as the component address, and TWEAK as /// the component tweak. /// /// ADDR and TWEAK must be a hex-string. TWEAK is right padded with zero /// bytes if the given tweak is not large enough. Can be specified multiple /// times and must be used at least once. #[arg(short, long = "component", required = true, value_name = "FILE,ADDR,TWEAK", value_hint = ValueHint::FilePath)] components: Vec, /// Use FILE as a host key document. /// /// Can be specified multiple times and must be used at least once. #[arg(short = 'k', long = "host-key", required = true)] pub host_key_documents: Vec, /// Plain control flags. Must be a hex value. #[arg(long, default_value = "0x10000000")] pub pcf: String, /// Secret control flags. Must be a hex value. #[arg(long, default_value = "0x0")] pub scf: String, /// PSW address. Must be a hex value. #[arg(long, default_value = "0x10000", value_parser=decode_u64_hex_str)] pub psw_addr: u64, /// PSW mask. Must be a hex value. #[arg(long, default_value = "0x0000000180000000", value_parser=decode_u64_hex_str)] pub psw_mask: u64, /// Customer communication key (CCK) file path. #[arg(long)] pub cck: Option, /// Secure Execution header output location. #[arg(short, long)] pub output: PathBuf, #[clap(flatten)] pub verbosity: VerbosityOptions, } #[derive(Debug)] pub struct Comp { pub reader: BufReader, } impl Read for Comp { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { self.reader.read(buf) } } enum CompType { Dummy = 1, } impl ComponentTrait for Comp { fn secure_mode(&self) -> bool { true } fn kind(&self) -> CompType { CompType::Dummy } } static LOGGER: PvLogger = PvLogger; fn main() -> anyhow::Result<()> { let mut args = Args::parse(); let log_level = args.verbosity.to_level_filter(); LOGGER .start(log_level) .with_context(|| "Failed to set-up logger")?; info!("# Preparing components"); let mut layout = Layout::new(0x0, SecuredComponentBuilder::COMPONENT_ALIGNMENT_V1)?; // Don't store the prepared components anywhere as we're only interested in // the hashes. let mut writer = std::io::empty(); let mut secure_comp_builer = SecuredComponentBuilder::new_v1(false)?; // Sort components by address in ascending order args.components.sort_by(|a, b| a.addr.cmp(&b.addr)); for component_arg in args.components { info!("## Preparing {}", component_arg); let mut comp = Comp { reader: BufReader::new(open_file(&component_arg.path)?), }; let comp_addr = component_arg.addr; let _ = secure_comp_builer .prepare_and_insert_as_secure_component( &mut writer, &mut layout, &mut comp, comp_addr, component_arg.tweak, ) .with_context(|| { format!( "Failed to prepare component '{}'", component_arg.path.display() ) })?; } info!("\n# Creating Secure Execution Header"); let addr = args.psw_addr; let mask = args.psw_mask; let mut builder = SeHdrBuilder::new( SeHdrVersion::V1, PSW { addr, mask }, secure_comp_builer.finish()?, )?; let mut target_pub_keys = vec![]; for hkd_path in args.host_key_documents { info!( "Use the file '{}' as a host key document", hkd_path.display() ); let hkd_data = read_file(&hkd_path, "host key document")?; let certs = read_certs(&hkd_data)?; if certs.is_empty() { return Err(PvError::NoHkdInFile(hkd_path.display().to_string()).into()); } if certs.len() > 1 { warn!("The host key document in '{}' contains more than one certificate! All keys will be used.", hkd_path.display()); } for cert in &certs { target_pub_keys.push(cert.public_key()?); } } builder.add_hostkeys(&target_pub_keys)?; let pcf = try_parse_u64(&args.pcf, "pcf")?.into(); let scf = try_parse_u64(&args.scf, "scf")?.into(); info!( "PSW addr ............: {addr:#018x}\n\ PSW mask ............: {mask:#018x}\n\ PCF .................: {pcf}\n\ SCF .................: {scf}" ); builder.with_pcf(&pcf)?; builder.with_scf(&scf)?; if let Some(cck) = args.cck { info!("CCK ................: {}", cck.display()); builder .with_cck(read_file(&cck, "CCK")?.into()) .with_context(|| format!("Invalid CCK in '{}'", &cck.display()))?; } let mut output = AtomicFile::new(args.output, &mut OpenOptions::new())?; output.write_all(&builder.build()?.as_bytes()?)?; Ok(output.finish(AtomicFileOperation::Replace)?) } s390-tools-2.38.0/rust/pvimg/examples/tamper_pvimg/000077500000000000000000000000001502674226300220765ustar00rootroot00000000000000s390-tools-2.38.0/rust/pvimg/examples/tamper_pvimg/main.rs000066400000000000000000000120361502674226300233720ustar00rootroot00000000000000#![allow(missing_docs)] use anyhow::Context; use clap::{Parser, ValueEnum, ValueHint}; use log::info; use pv::{ misc::{open_file, parse_hex, read_file}, request::SymKey, }; use pvimg::{ error::Result, uvdata::{ KeyExchangeTrait, SeHdr, SeHdrBinV1, SeHdrData, SeHdrDataV1, SeHdrVersioned, UvDataPlainTrait, UvDataTrait, }, }; use std::{fs::File, io::Write, path::PathBuf}; use utils::{PvLogger, VerbosityOptions}; #[derive(Parser, Debug)] struct Cli { /// Use INPUT as the Secure Execution image. #[arg(short, long, value_name = "INPUT", value_hint = ValueHint::FilePath,)] infile: PathBuf, /// Use INPUT as the Secure Execution image. #[arg(short, long, value_name = "OUTPUT", value_hint = ValueHint::FilePath,)] outfile: PathBuf, /// Use the key in FILE to decrypt the Secure Execution header. /// It is the key that was specified with the command line option /// '--hdr-key' at the Secure Execution image creation. #[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath, alias = "key")] hdr_key: PathBuf, #[clap(flatten)] verbosity: VerbosityOptions, /// Hdr Value to tamper with #[arg(long, value_enum)] tamp: TampVal, } #[non_exhaustive] #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)] pub enum TampVal { /// ALD. AddressListDigest, /// PLD. PageListDigest, /// TLD. TweakListDigest, /// Change lenght of secure header HdrSize, /// customer ECDH key CustomerPublicKey, /// host key hash HostKey, /// Secret control flags SecretControlFlag, /// Plaintext control flags PlaintextControlFlag, /// Number of keyslots NumKeySlots, /// Size encrypted area SizeEncArea, /// Number of encrypted pages NumEncPages, } static LOGGER: PvLogger = PvLogger; fn main() -> anyhow::Result<()> { let opt = Cli::parse(); LOGGER .start(opt.verbosity.to_level_filter()) .with_context(|| "Failed to set-up logger")?; info!("Reading Secure Execution header {}", opt.infile.display()); let mut input = open_file(&opt.infile)?; SeHdr::seek_sehdr(&mut input, None)?; let hdr = SeHdr::try_from_io(&mut input)?; let mut hdr_encr_v1: SeHdrBinV1 = >::try_into(hdr.clone().data).expect("SE-header V1"); let mut decryption_required = false; let mut hdr_encr: SeHdr; // Tamper with parts of SE header that doesn't require decryption match &opt.tamp { TampVal::HdrSize => hdr_encr_v1.aad.sehs += 1, TampVal::HostKey => hdr_encr_v1.aad.keyslots[0].phkh[..32].copy_from_slice(&[0; 32]), TampVal::PlaintextControlFlag => { let hex_str = String::from_utf8( read_file("/sys/firmware/uv/query/supp_se_hdr_pcf", "input file").unwrap(), ) .expect("PCF support not found"); let hex = parse_hex(&hex_str); let mut hex_pad = [0u8; 8]; hex_pad[(8 - hex.len())..].copy_from_slice(&hex); let supported_pcf: u64 = u64::from_be_bytes(hex_pad); hdr_encr_v1.aad.pcf = 0xFFFF_FFFF_FFFF_D5FF - supported_pcf; } TampVal::NumKeySlots => hdr_encr_v1.aad.nks = 0, TampVal::SizeEncArea => hdr_encr_v1.aad.sea = 0, _ => decryption_required = true, } if decryption_required { let key = SymKey::try_from_data( hdr.key_type(), read_file(&opt.hdr_key, "Reading key")?.into(), )?; let mut hdr_plain = hdr.decrypt(&key)?; let mut hdr_v1: SeHdrDataV1 = >::try_into(hdr_plain.clone().data) .expect("SE-header V1"); // Tamper with the SE header data match &opt.tamp { TampVal::AddressListDigest => hdr_v1.aad.ald[..8].copy_from_slice(&[0; 8]), TampVal::PageListDigest => hdr_v1.aad.pld[..8].copy_from_slice(&[0; 8]), TampVal::TweakListDigest => hdr_v1.aad.tld[..8].copy_from_slice(&[0; 8]), TampVal::CustomerPublicKey => { hdr_v1.aad.cust_pub_key.coord[..160].copy_from_slice(&[0; 160]) } TampVal::SecretControlFlag => hdr_v1.data.value_mut().scf = 0xFFFF_FFFF_FFFF_FFFF, TampVal::NumEncPages => hdr_v1.aad.nep = 0, _ => {} } hdr_plain.data = hdr_v1.into(); hdr_encr = hdr_plain.encrypt(&key)?; } else { hdr_encr = hdr.clone(); hdr_encr.data = hdr_encr_v1.clone().into(); } std::fs::copy(&opt.infile, &opt.outfile)?; match tamper_image_file(&opt.outfile, hdr_encr) { Ok(_) => (), Err(err) => { std::fs::remove_file(&opt.outfile)?; panic!("Could not seek SE header: {}", err); } }; Ok(()) } fn tamper_image_file(outfile: &PathBuf, hdr_encr: SeHdr) -> Result<()> { let mut output = File::options().read(true).write(true).open(outfile)?; SeHdr::seek_sehdr(&mut output, None)?; output.write_all(&hdr_encr.as_bytes()?)?; Ok(()) } s390-tools-2.38.0/rust/pvimg/man/000077500000000000000000000000001502674226300163415ustar00rootroot00000000000000s390-tools-2.38.0/rust/pvimg/man/pvimg-create.1000066400000000000000000000175721502674226300210220ustar00rootroot00000000000000.\" Copyright 2024 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH "PVIMG-CREATE" "1" "2025-04-24" "s390-tools" "Pvimg Manual" .nh .ad l .SH NAME pvimg-create \- Create an IBM Secure Execution image .SH SYNOPSIS .nf .fam C pvimg create [OPTIONS] --kernel --output --host-key-document <--no-verify|--cert > .fam C .fi .SH DESCRIPTION .PP Use \fBpvimg\fR to generate a single bootable image file with encrypted and integrity-protected parts. The command requires a kernel image, a host-key document, certificates for the host-key document verification, and an output file name. Optionally, specify an initial RAM filesystem, and a file containing the kernel parameters. If the command should be run offline, use the \fB\-\-offline\fR option and specify the certificate revocation lists (CRLs) by using the \fB\-\-crl\fR option. Should special circumstances require it, you can optionally specify your own keys for the encryption by using the experimental options. For all certificates, CRLs, and host-key documents, both the PEM and DER input formats are supported. In the resulting image file, a plain text boot loader, the encrypted components for kernel, initial RAM disk, kernel parameters, and the encrypted and integrity-protected header are concatenated. The header contains metadata necessary for running the guest in protected mode. .PP Use this image file as a kernel image for zipl or for a direct kernel boot using QEMU. .SH OPTIONS .PP \-i, \-\-kernel, \-\-image .RS 4 Use the content of FILE as a raw binary Linux kernel. The Linux kernel must be a raw binary s390x Linux kernel. The ELF format is not supported. .RE .RE .PP \-r, \-\-ramdisk .RS 4 Use the content of FILE as the Linux initial RAM disk. .RE .RE .PP \-p, \-\-parmfile .RS 4 Use the content of FILE as the Linux kernel command line. The Linux kernel command line must be shorter than the maximum kernel command line size supported by the given Linux kernel. .RE .RE .PP \-o, \-\-output .RS 4 Write the generated Secure Execution boot image to FILE. .RE .RE .PP \-k, \-\-host\-key\-document .RS 4 Use FILE as a host\-key document. Can be specified multiple times and must be specified at least once. .RE .RE .PP \-\-no\-verify .RS 4 Disable the host\-key document verification. Does not require the host\-key documents to be valid. Do not use for a production request unless you verified the host\-key document beforehand. .RE .RE .PP \-C, \-\-cert .RS 4 Use FILE as a certificate to verify the host\-key or keys. The certificates are used to establish a chain of trust for the verification of the host\-key documents. Specify this option twice to specify the IBM Z signing key and the intermediate CA certificate (signed by the root CA). .RE .RE .PP \-\-crl .RS 4 Use FILE as a certificate revocation list (CRL). The list is used to check whether a certificate of the chain of trust is revoked. Specify this option multiple times to use multiple CRLs. .RE .RE .PP \-\-offline .RS 4 Make no attempt to download CRLs. .RE .RE .PP \-\-root\-ca .RS 4 Use FILE as the root\-CA certificate for the verification. If omitted, the system wide\-root CAs installed on the system are used. Use this only if you trust the specified certificate. .RE .RE .PP \-\-no\-component\-check .RS 4 Disable all input component checks. For example, for the Linux kernel, it tests if the given kernel looks like a raw binary s390x kernel. .RE .RE .PP \-\-overwrite .RS 4 Overwrite an existing Secure Execution boot image. .RE .RE .PP \-\-cck, \-\-comm\-key .RS 4 Use the content of FILE as the customer\-communication key (CCK). The file must contain exactly 32 bytes of data. .RE .RE .PP \-\-hdr\-key .RS 4 Use the content of FILE as the Secure Execution header protection key. The file must contain exactly 32 bytes of data. If the option is not specified, the Secure Execution header protection key is a randomly generated key. .RE .RE .PP \-\-enable\-dump .RS 4 Enable Secure Execution guest dump support. This option requires the \fB\-\-cck\fR or \fB\-\-enable\-cck\-update\fR option. .RE .RE .PP \-\-disable\-dump .RS 4 Disable Secure Execution guest dump support (default). .RE .RE .PP \-\-enable\-cck\-extension\-secret .RS 4 Add\-secret requests must provide an extension secret that matches the CCK\-derived extension secret. This option requires the \fB\-\-cck\fR option. .RE .RE .PP \-\-disable\-cck\-extension\-secret .RS 4 Add\-secret requests don't have to provide the CCK\-derived extension secret (default). .RE .RE .PP \-\-enable\-cck\-update .RS 4 Enable CCK update support. Requires z17 or up. This option cannot be used in conjunction with the \fB\-\-enable\-cck\-extension\-secret\fR option. .RE .RE .PP \-\-disable\-cck\-update .RS 4 Disable CCK update support (default). .RE .RE .PP \-\-enable\-pckmo .RS 4 Enable the support for the DEA, TDEA, AES, and ECC PCKMO key encryption functions (default). .RE .RE .PP \-\-disable\-pckmo .RS 4 Disable the support for the DEA, TDEA, AES, and ECC PCKMO key encryption functions. .RE .RE .PP \-\-enable\-pckmo\-hmac .RS 4 Enable the support for the HMAC PCKMO key encryption function. .RE .RE .PP \-\-disable\-pckmo\-hmac .RS 4 Disable the support for the HMAC PCKMO key encryption function (default). .RE .RE .PP \-\-enable\-backup\-keys .RS 4 Enable the support for backup target keys. .RE .RE .PP \-\-disable\-backup\-keys .RS 4 Disable the support for backup target keys (default). .RE .RE .PP \-\-enable\-image\-encryption .RS 4 Enable encryption of the image components (default). The image components are: the kernel, ramdisk, and kernel command line. .RE .RE .PP \-\-disable\-image\-encryption .RS 4 Disable encryption of the image components. The image components are: the kernel, ramdisk, and kernel command line. Use only if the components used do not contain any confidential content (for example, secrets like non\-public cryptographic keys). .RE .RE .PP \-h, \-\-help .RS 4 Print help (see a summary with \fB\-h\fR). .RE .RE .SH EXIT STATUS .TP 8 .B 0 \- Program finished successfully The command was executed successfully. .RE .TP 8 .B 1 \- Generic error Something went wrong during the operation. Refer to the error message. .RE .TP 8 .B 2 \- Usage error The command was used incorrectly, for example: unsupported command line flag, or wrong number of arguments. .RE .SH EXAMPLES These are examples of how to generate an IBM Secure Execution image in \fI\,/boot/secure\-linux\/\fR, using the kernel file \fI\,/boot/vmlinuz\/\fR, the initrd in \fI\,/boot/initrd.img\/\fR, the kernel parameters contained in \fI\,parmfile\/\fR, the intermediate CA in \fI\,DigiCertCA.crt\/\fR, the IBM Z signing key in \fI\,ibm\-z\-host\-key\-signing.crt\/\fR, and the host-key document in \fI\,host_key.crt\/\fR. An AES-256 GCM key is stored in \fI\,comm\-key\/\fR, which is used when creating a Secure Execution image with guest dump support enabled in the second example. Generate an IBM Secure Execution image: .PP .B pvimg create \-i \fI\,/boot/vmlinuz\/\fR \-r \fI\,/boot/initrd.img\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm\-z\-host\-key\-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/secure\-linux\/\fR Generate an IBM Secure Execution image with Secure Execution guest dump support: .PP .B pvimg create \-i \fI\,/boot/vmlinuz\/\fR \-r \fI\,/boot/initrd.img\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm\-z\-host\-key\-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/secure\-linux\/\fR \-\-enable\-dump \-\-cck \fI\,comm\-key\fR .SH NOTES .IP "1." 4 The \fBgenprotimg\fR(1) command is a symbolic link to the \fBpvimg-create\fR(1) command. .IP "2." 4 An ELF file cannot be used as a Linux kernel image. .IP "3." 4 Remember to re-run \fBzipl\fR after updating an IBM Secure Execution image. .SH "SEE ALSO" .sp \fBpvimg\fR(1) \fBzipl\fR(8) \fBqemu\fR(1) s390-tools-2.38.0/rust/pvimg/man/pvimg-info.1000066400000000000000000000025531502674226300205030ustar00rootroot00000000000000.\" Copyright 2024 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH "PVIMG-INFO" "1" "2024-12-19" "s390-tools" "Pvimg Manual" .nh .ad l .SH NAME pvimg-info \- Print information about the IBM Secure Execution image .SH SYNOPSIS .nf .fam C pvimg info [OPTIONS] --format .fam C .fi .SH DESCRIPTION Note that the API and output format is experimental and subject to change. .SH OPTIONS .PP .RS 4 Use INPUT as the Secure Execution image. .RE .RE .PP \-\-format .RS 4 The output format. Possible values: .RS 4 \- \fBjson\fP: JSON format. .RE .RE .PP \-\-hdr\-key .RS 4 Use the key in FILE to decrypt the Secure Execution header. It is the key that was specified with the command line option \fB\-\-hdr\-key\fR at the Secure Execution image creation. .RE .RE .PP \-h, \-\-help .RS 4 Print help (see a summary with \fB\-h\fR). .RE .RE .SH EXIT STATUS .TP 8 .B 0 \- Program finished successfully The command was executed successfully. .RE .TP 8 .B 1 \- Generic error Something went wrong during the operation. Refer to the error message. .RE .TP 8 .B 2 \- Usage error The command was used incorrectly, for example: unsupported command line flag, or wrong number of arguments. .RE .SH "SEE ALSO" .sp \fBpvimg\fR(1) \fBzipl\fR(8) \fBqemu\fR(1) s390-tools-2.38.0/rust/pvimg/man/pvimg-test.1000066400000000000000000000036111502674226300205230ustar00rootroot00000000000000.\" Copyright 2024 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH "PVIMG-TEST" "1" "2024-12-19" "s390-tools" "Pvimg Manual" .nh .ad l .SH NAME pvimg-test \- Test different aspects of an existing IBM Secure Execution image .SH SYNOPSIS .nf .fam C pvimg test <--host-key-document |--key-hashes[=]> .fam C .fi .SH DESCRIPTION Test different aspects of an existing IBM Secure Execution image .SH OPTIONS .PP .RS 4 Use INPUT as the Secure Execution image. .RE .RE .PP \-k, \-\-host\-key\-document .RS 4 Use FILE to check for a host key document. Verifies that the image contains the host key hash of one of the specified host keys. The check fails if none of the host keys match the hash in the image. This parameter can be specified multiple times. Mutually exclusive with \fB\-\-key\-hashes\fR. .RE .RE .PP \-\-key\-hashes[=] .RS 4 Use FILE to check for the host key hashes provided by the ultravisor. If no FILE is specified, FILE defaults to \fB/sys/firmware/uv/keys/all\fR. The default file is only available if the local system supports the Query Ultravisor Keys UVC. Verifies that the image contains the host key hash of one of the specified hashes in FILE. The check fails if none of the host keys match a hash in the response. Mutually exclusive with \fB\-\-host\-key\-document\fR. .RE .RE .PP \-h, \-\-help .RS 4 Print help (see a summary with \fB\-h\fR). .RE .RE .SH EXIT STATUS .TP 8 .B 0 \- Program finished successfully The command was executed successfully. .RE .TP 8 .B 1 \- Generic error Something went wrong during the operation. Refer to the error message. .RE .TP 8 .B 2 \- Usage error The command was used incorrectly, for example: unsupported command line flag, or wrong number of arguments. .RE .SH "SEE ALSO" .sp \fBpvimg\fR(1) \fBzipl\fR(8) \fBqemu\fR(1) s390-tools-2.38.0/rust/pvimg/man/pvimg.1000066400000000000000000000030371502674226300175500ustar00rootroot00000000000000.\" Copyright 2024 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH "PVIMG" "1" "2024-12-19" "s390-tools" "Pvimg Manual" .nh .ad l .SH NAME pvimg \- Create and inspect IBM Secure Execution images .SH SYNOPSIS .nf .fam C pvimg [OPTIONS] .fam C .fi .SH DESCRIPTION Use \fBpvimg\fP to create an IBM Secure Execution image, which can be loaded using zipl or QEMU. \fBpvimg\fP can also be used to inspect existing Secure Execution images. .SH "PVIMG COMMANDS" .PP \fBpvimg-create(1)\fR .RS 4 Create an IBM Secure Execution image .RE .PP \fBpvimg-info(1)\fR .RS 4 Print information about the IBM Secure Execution image .RE .PP \fBpvimg-test(1)\fR .RS 4 Test different aspects of an existing IBM Secure Execution image .RE .SH OPTIONS .PP \-v, \-\-verbose .RS 4 Provide more detailed output. .RE .RE .PP \-q, \-\-quiet .RS 4 Provide less output. .RE .RE .PP \-\-version .RS 4 Print version information and exit. .RE .RE .PP \-h, \-\-help .RS 4 Print help (see a summary with \fB\-h\fR). .RE .RE .SH EXIT STATUS .TP 8 .B 0 \- Program finished successfully The command was executed successfully. .RE .TP 8 .B 1 \- Generic error Something went wrong during the operation. Refer to the error message. .RE .TP 8 .B 2 \- Usage error The command was used incorrectly, for example: unsupported command line flag, or wrong number of arguments. .RE .SH "SEE ALSO" .sp \fBpvimg-create\fR(1) \fBpvimg-info\fR(1) \fBpvimg-test\fR(1) \fBzipl\fR(8) \fBqemu\fR(1) s390-tools-2.38.0/rust/pvimg/src/000077500000000000000000000000001502674226300163555ustar00rootroot00000000000000s390-tools-2.38.0/rust/pvimg/src/cli.rs000066400000000000000000001010241502674226300174700ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::{env, fmt::Display, path::PathBuf}; use clap::{ArgGroup, Args, Command, CommandFactory, Parser, ValueEnum, ValueHint}; use log::warn; use utils::{CertificateOptions, DeprecatedVerbosityOptions}; /// Create and inspect IBM Secure Execution images. /// /// Use pvimg to create an IBM Secure Execution image, which can be loaded using /// zipl or QEMU. pvimg can also be used to inspect existing Secure Execution /// images. #[derive(Parser, Debug)] #[command()] pub struct CliOptions { #[clap(flatten)] pub verbose: DeprecatedVerbosityOptions, /// Print version information and exit. // Implemented for the help message only. Actual parsing happens in the // version command. #[arg(long)] pub version: bool, #[command(subcommand)] pub cmd: SubCommands, } impl From for CliOptions { fn from(value: GenprotimgCliOptions) -> Self { Self { verbose: value.verbose, version: false, cmd: SubCommands::Create(value.args), } } } impl CliOptions { pub fn new_version_cmd_opts() -> Self { Self { verbose: DeprecatedVerbosityOptions::default(), version: true, cmd: SubCommands::Version, } } } /// Validates the given command line options. /// /// # Errors /// /// This function will return an error if an argument is missing. pub fn validate_cli(opts: &CliOptions) -> Result<(), clap::error::Error> { match &opts.cmd { SubCommands::Create(create_opts) => { if let Some(dir) = create_opts .experimental_args .x_bootloader_directory .as_ref() { warn!("Use bootloader directory: {}", dir.display()); } Ok(()) } _ => Ok(()), } } /// CLI Argument collection for handling input components. #[derive(Args, Debug)] #[cfg_attr(test, derive(Default))] pub struct ComponentPaths { /// Use the content of FILE as a raw binary Linux kernel. /// /// The Linux kernel must be a raw binary s390x Linux kernel. The ELF format /// is not supported. #[arg(short='i', long = "kernel", value_name = "FILE", value_hint = ValueHint::FilePath, visible_alias = "image")] pub kernel: PathBuf, /// Use the content of FILE as the Linux initial RAM disk. #[arg(short, long, value_name = "FILE", value_hint = ValueHint::FilePath)] pub ramdisk: Option, /// Use the content of FILE as the Linux kernel command line. /// /// The Linux kernel command line must be shorter than the maximum kernel /// command line size supported by the given Linux kernel. #[arg(short, long, value_name = "FILE", value_hint = ValueHint::FilePath)] pub parmfile: Option, } #[derive(Args, Debug)] #[cfg_attr(test, derive(Default))] #[command( group(ArgGroup::new("header-flags").multiple(true).conflicts_with_all(["x_pcf", "x_scf"])), group(ArgGroup::new("cck-available").multiple(true)))] pub struct CreateBootImageLegacyFlags { /// Enable Secure Execution guest dump support. This option requires the /// '--cck' or '--enable-cck-update' option. #[arg(long, action = clap::ArgAction::SetTrue, requires = "cck-available", group="header-flags")] pub enable_dump: Option, /// Disable Secure Execution guest dump support (default). #[arg(long, action = clap::ArgAction::SetTrue, conflicts_with="enable_dump", group="header-flags")] pub disable_dump: Option, /// Add-secret requests must provide an extension secret that matches the /// CCK-derived extension secret. This option requires the '--cck' /// option. #[arg(long, action = clap::ArgAction::SetTrue, requires="cck", group="header-flags")] pub enable_cck_extension_secret: Option, /// Add-secret requests don't have to provide the CCK-derived extension /// secret (default). #[arg(long, action = clap::ArgAction::SetTrue, conflicts_with="enable_cck_extension_secret", group="header-flags")] pub disable_cck_extension_secret: Option, /// Enable CCK update support. Requires z17 or up. This option cannot be /// used in conjunction with the '--enable-cck-extension-secret' option. #[arg(long, action = clap::ArgAction::SetTrue, conflicts_with="enable_cck_extension_secret", group="cck-available", group="header-flags")] pub enable_cck_update: Option, /// Disable CCK update support (default). #[arg(long, action = clap::ArgAction::SetTrue, conflicts_with="enable_cck_update", group="header-flags")] pub disable_cck_update: Option, /// Enable the support for the DEA, TDEA, AES, and ECC PCKMO key encryption /// functions (default). #[arg(long, action = clap::ArgAction::SetTrue, group="header-flags")] pub enable_pckmo: Option, /// Disable the support for the DEA, TDEA, AES, and ECC PCKMO key encryption /// functions. #[arg(long, action = clap::ArgAction::SetTrue, conflicts_with="enable_pckmo", group="header-flags")] pub disable_pckmo: Option, /// Enable the support for the HMAC PCKMO key encryption function. #[arg(long, action = clap::ArgAction::SetTrue, group="header-flags")] pub enable_pckmo_hmac: Option, /// Disable the support for the HMAC PCKMO key encryption function (default). #[arg(long, action = clap::ArgAction::SetTrue, conflicts_with="enable_pckmo_hmac", group="header-flags")] pub disable_pckmo_hmac: Option, /// Enable the support for backup target keys. #[arg(long, action = clap::ArgAction::SetTrue, group="header-flags")] pub enable_backup_keys: Option, /// Disable the support for backup target keys (default). #[arg(long, action = clap::ArgAction::SetTrue, conflicts_with="enable_backup_keys", group="header-flags")] pub disable_backup_keys: Option, /// Enable encryption of the image components (default). /// /// The image components are: the kernel, ramdisk, and kernel command line. #[arg(long, action = clap::ArgAction::SetTrue, group="header-flags")] pub enable_image_encryption: Option, /// Disable encryption of the image components. /// /// The image components are: the kernel, ramdisk, and kernel command line. /// Use only if the components used do not contain any confidential content /// (for example, secrets like non-public cryptographic keys). #[arg(long, action = clap::ArgAction::SetTrue, conflicts_with="enable_image_encryption", group="header-flags")] pub disable_image_encryption: Option, } #[non_exhaustive] #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)] pub enum OutputFormat { /// JSON format. Json, } impl Display for OutputFormat { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "{}", match self { Self::Json => "JSON", } ) } } #[derive(Args, Debug)] pub struct SeImgInputArgs { /// Use INPUT as the Secure Execution image. #[arg(value_name = "INPUT", value_hint = ValueHint::FilePath,)] pub path: PathBuf, } #[derive(Args, Debug)] pub struct InfoArgs { #[clap(flatten)] pub input: SeImgInputArgs, /// The output format #[arg(long, value_enum)] pub format: OutputFormat, /// Use the key in FILE to decrypt the Secure Execution header. /// /// It is the key that was specified with the command line option /// '--hdr-key' at the Secure Execution image creation. #[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath, alias = "key")] pub hdr_key: Option, } #[derive(Args, Debug)] #[command(group(ArgGroup::new("test-args").multiple(true).required(true)))] pub struct TestArgs { #[clap(flatten)] pub input: SeImgInputArgs, /// Use FILE to check for a host key document. /// /// Verifies that the image contains the host key hash of one of the /// specified host keys. The check fails if none of the host keys match the /// hash in the image. This parameter can be specified multiple times. /// Mutually exclusive with '--key-hashes'. #[arg( short = 'k', long = "host-key-document", value_name = "FILE", value_hint = ValueHint::FilePath, use_value_delimiter = true, value_delimiter = ',', group = "test-args", )] pub host_key_documents: Vec, /// Use FILE to check for the host key hashes provided by the ultravisor. If /// no FILE is specified, FILE defaults to '/sys/firmware/uv/keys/all'. /// /// The default file is only available if the local system supports the /// Query Ultravisor Keys UVC. Verifies that the image contains the host key /// hash of one of the specified hashes in FILE. The check fails if none of /// the host keys match a hash in the response. Mutually exclusive with /// '--host-key-document'. #[arg( long = "key-hashes", value_name = "FILE", value_hint = ValueHint::FilePath, num_args = 0..=1, require_equals = true, default_missing_value = "/sys/firmware/uv/keys/all", conflicts_with="host_key_documents", group = "test-args", )] pub key_hashes: Option, } /// Create an IBM Secure Execution image. /// /// Create a new IBM Secure Execution image. Only create these images in a /// trusted environment, such as your workstation. The 'genprotimg' command /// creates randomly generated keys to protect the image. The generated image /// can then be booted on an IBM Secure Execution system as a KVM guest. /// /// Note: The 'genprotimg' command is a symbolic link to the 'pvimg create' /// command. #[derive(Parser, Debug)] pub struct GenprotimgCliOptions { #[clap(flatten)] pub args: Box, #[clap(flatten)] pub verbose: DeprecatedVerbosityOptions, /// Print version information and exit. // Implemented for the help message only. Actual parsing happens in the // version command. #[arg(long, action = clap::ArgAction::SetTrue )] pub version: (), #[arg(long, action = clap::ArgAction::HelpLong, hide(true))] /// Print help (deprecated, use '--help' instead). help_all: (), #[arg(long, action = clap::ArgAction::HelpLong, hide(true))] /// Print help (deprecated, use '--help' instead). help_experimental: (), } impl GenprotimgCliOptions { pub fn command() -> Command { let cmd = ::command(); // Make sure that the correct binary is shown in the clap error // messages. cmd.bin_name("genprotimg") } pub fn own_parse() -> CliOptions { let args = env::args_os(); let args_len = args.len(); let version_count = args.filter(|value| value == "--version").count(); if version_count > 1 || version_count == 1 && (args_len != version_count + 1) { Self::command() .error( clap::error::ErrorKind::UnknownArgument, "unexpected argument", ) .exit() } if version_count == 1 { CliOptions::new_version_cmd_opts() } else { let genprotimg_opts = Self::parse(); genprotimg_opts.into() } } } #[derive(Parser, Debug)] #[cfg_attr(test, derive(Default))] pub struct CreateBootImageArgs { #[clap(flatten)] pub component_paths: ComponentPaths, /// Write the generated Secure Execution boot image to FILE. #[arg(short, long, value_name = "FILE", value_hint = ValueHint::FilePath,)] pub output: PathBuf, #[clap(flatten)] pub certificate_args: CertificateOptions, /// Disable all input component checks. /// /// For example, for the Linux kernel, it tests if the given kernel looks /// like a raw binary s390x kernel. #[arg(long)] pub no_component_check: bool, /// Overwrite an existing Secure Execution boot image. #[arg(long)] pub overwrite: bool, /// Use the content of FILE as the customer-communication key (CCK). /// /// The file must contain exactly 32 bytes of data. This option used to be /// called '--comm-key' in previous versions. #[arg( long, value_name = "FILE", group = "cck-available", visible_alias = "comm-key" )] pub cck: Option, /// Use the content of FILE as the Secure Execution header protection key. /// /// The file must contain exactly 32 bytes of data. If the option is not /// specified, the Secure Execution header protection key is a randomly /// generated key. #[arg(long, value_name = "FILE", alias = "x-header-key")] pub hdr_key: Option, #[clap(flatten)] pub legacy_flags: CreateBootImageLegacyFlags, #[clap(flatten)] pub experimental_args: CreateBootImageExperimentalArgs, } /// Experimental options #[derive(Args, Debug)] #[cfg_attr(test, derive(Default))] pub struct CreateBootImageExperimentalArgs { /// Manually set the directory used to load the Secure Execution bootloaders /// (stage3a and stage3b) (experimental option). // Hidden in user documentation. #[arg(long, value_name = "DIR", hide(true))] pub x_bootloader_directory: Option, /// Manually set the image components encryption key (experimental option). // Hidden in user documentation. #[arg(long, value_name = "FILE", hide(true))] pub x_comp_key: Option, /// Manually set the PSW address used for the Secure Execution header (experimental option). // Hidden in user documentation. #[arg(long, value_name = "ADDRESS", hide(true))] pub x_psw: Option, /// Manually set the plaintext control flags (experimental option). // No validity checks made. Hidden in user documentation. #[arg(long, value_name = "PCF", hide(true))] pub x_pcf: Option, /// Manually set the secret control flags (experimental option). // No validity checks made. Hidden in user documentation. #[arg(long, value_name = "SCF", hide(true))] pub x_scf: Option, } #[derive(Debug, clap::Subcommand)] pub enum SubCommands { /// Create an IBM Secure Execution image. /// /// Create a new IBM Secure Execution image. Only create these images in a /// trusted environment, such as your workstation. The 'pvimg create' /// command creates randomly generated keys to protect the image. The /// generated image can then be booted on an IBM Secure Execution system as /// a KVM guest. Create(Box), /// Print information about the IBM Secure Execution image. /// /// Note that the API and output format is experimental and subject to /// change. Info(InfoArgs), /// Test different aspects of an existing IBM Secure Execution image. Test(Box), /// Print version information and exit. #[command(aliases(["--version"]), hide(true))] Version, } #[allow(clippy::shadow_unrelated)] #[cfg(test)] mod test { use std::collections::BTreeMap; use super::*; #[derive(Hash, Eq, PartialEq, Debug, Clone)] struct CliOption { name: String, args: Vec, } impl CliOption { fn new, T: AsRef, P: AsRef<[S]>>(name: T, args: P) -> Self { let name = name.as_ref().to_owned(); let args = args .as_ref() .iter() .map(|v| v.as_ref().to_owned()) .collect(); Self { name, args } } } impl From for Vec { fn from(val: CliOption) -> Self { let CliOption { args, .. } = val; args } } fn flat_map_collect(map: BTreeMap) -> Vec { map.into_values().flat_map(|v| v.args).collect() } fn insert( mut map: BTreeMap, values: Vec, ) -> BTreeMap { for value in values { map.insert(value.name.to_owned(), value); } map } fn remove>( mut map: BTreeMap, key: S, ) -> BTreeMap { map.remove(key.as_ref()); map } #[test] #[rustfmt::skip] fn genprotimg_and_pvimg_create_args() { // Minimal valid create arguments using no-verify let mut mvcanv = BTreeMap::new(); mvcanv = insert(mvcanv, vec![CliOption::new("image", ["--image", "/dev/null"])]); mvcanv = insert(mvcanv, vec![CliOption::new("hkd", ["--host-key-document", "/dev/null"])]); mvcanv = insert(mvcanv, vec![CliOption::new("output", ["--output", "/dev/null"])]); mvcanv = insert(mvcanv, vec![CliOption::new("no-verify", ["--no-verify"])]); // Minimal valid create arguments using --cert let mut mvca = mvcanv.clone(); mvca.remove("no-verify"); mvca = insert(mvca, vec![CliOption::new("cert", ["--cert", "/dev/null"])]); let valid_create_args = [ flat_map_collect(mvcanv.clone()), flat_map_collect(insert(remove(mvcanv.clone(), "image"), vec![CliOption::new("kernel", ["--kernel", "/dev/kernel"])])), flat_map_collect(insert(mvcanv.clone(), vec![CliOption::new("root-ca", ["--root-ca", "/dev/null"])])), flat_map_collect(mvca.clone()), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("quiet", ["-q"])])), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("verbose", ["-vvv"])])), // Verify the old verbosity is still working. flat_map_collect(insert(mvca.clone(), vec![CliOption::new("verbose", ["-VVV"])])), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("offline", ["--offline"])])), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("ramdisk", ["--ramdisk", "/dev/null"])])), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("parmfile", ["--parmfile", "/dev/null"])])), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-dump", ["--enable-dump"]), CliOption::new("comm-key", ["--comm-key", "/dev/null"])])), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-dump", ["--enable-dump"]), CliOption::new("comm-key", ["--cck", "/dev/null"])])), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-dump", ["--enable-dump"]), CliOption::new("comm-key", ["--comm-key", "/dev/null"]), CliOption::new("enable-cck-update", ["--enable-cck-update"])])), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-pcf", ["--x-pcf", "0x0"]), CliOption::new("x-scf", ["--x-scf", "0x0"])])), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-psw", ["--x-psw", "0x0"])])), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("no-component-check", ["--no-component-check"])])), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-pckmo", ["--enable-pckmo"])])), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-pckmo-hmac", ["--enable-pckmo-hmac"])])), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-backup-keys", ["--enable-backup-keys"])])), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("disable-image-encryption", ["--disable-image-encryption"])])), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-image-encryption", ["--enable-image-encryption"])])), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-header-key", ["--x-header-key", "/dev/null"]),])), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-header-key", ["--hdr-key", "/dev/null"]),])), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-cck-update", ["--enable-cck-update"])])), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("disable-cck-update", ["--disable-cck-update"])])), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("multiple-cck", ["--disable-cck-update", "--cck", "/dev/null"])])), ]; let invalid_create_args = [ flat_map_collect(remove(mvcanv.clone(), "no-verify")), flat_map_collect(remove(mvcanv.clone(), "image")), flat_map_collect(remove(mvcanv.clone(), "hkd")), flat_map_collect(remove(mvcanv, "output")), // missing both `--cck' and `--enable-cck-update' flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-dump", ["--enable-dump"])])), // -v and -q cannot be combined flat_map_collect(insert(mvca.clone(), vec![ CliOption::new("verbose", ["-v"]), CliOption::new("quiet", ["-q"])])), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("image2", ["--image", "/dev/null"])])), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("output2", ["--output", "/dev/null"])])), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("ramdisk", ["--ramdisk", "/dev/null"]), CliOption::new("ramdisk2", ["--ramdisk", "/dev/null"]) ])), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("parmfile", ["--parmfile", "/dev/null"]), CliOption::new("parmfile2", ["--parmfile", "/dev/null"]) ])), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-pcf", ["--x-pcf", "0x0"]), CliOption::new("x-pcf2", ["--x-pcf", "0x0"])])), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-pckmo", ["--enable-pckmo"]), CliOption::new("disable-pckmo", ["--disable-pckmo"])])), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-image-encryption", ["--enable-image-encryption"]), CliOption::new("disable-image-encryption", ["--disable-image-encryption"])])), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-header-key", ["--hdr-key"]),])), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("extension", ["--enable-cck-extension-secret"]), CliOption::new("update", ["--enable-cck-update"])])), ]; let mut genprotimg_valid_args = vec![ // See workaround `parse_version` in `pvimg/main.rs`. // vec!["genprotimg", "--version"], ]; let mut pvimg_valid_args = vec![ vec!["pvimg", "--version"], vec!["pvimg", "version"], ]; // Test for invalid combinations let mut genprotimg_invalid_args = vec![ vec!["genprotimg"], ]; let mut pvimg_invalid_args = vec![ vec!["pvimg"], ]; // Test that `genprotimg` and `pvimg create` behave equally. for create_args in &valid_create_args { genprotimg_valid_args.push([["genprotimg"].to_vec(), Vec::from_iter(create_args.iter().map(String::as_str))].concat()); pvimg_valid_args.push([["pvimg", "create"].to_vec(), Vec::from_iter(create_args.iter().map(String::as_str))].concat()); } for invalid_create_args in &invalid_create_args { genprotimg_invalid_args.push([["genprotimg"].to_vec(), Vec::from_iter(invalid_create_args.iter().map(String::as_str))].concat()); pvimg_invalid_args.push([["pvimg", "create"].to_vec(), Vec::from_iter(invalid_create_args.iter().map(String::as_str))].concat()); } for arg in pvimg_valid_args { let res = CliOptions::try_parse_from(&arg); #[allow(clippy::use_debug, clippy::print_stdout)] if let Err(e) = &res { println!("arg: {arg:?}"); println!("{e}"); } assert!(res.is_ok()); } for arg in pvimg_invalid_args { let res = CliOptions::try_parse_from(&arg); assert!(res.is_err()); } for arg in genprotimg_valid_args { let res = GenprotimgCliOptions::try_parse_from(&arg); #[allow(clippy::use_debug, clippy::print_stdout)] if let Err(e) = &res { println!("arg: {arg:?}"); println!("{e}"); } assert!(res.is_ok()); } for arg in genprotimg_invalid_args { let res = GenprotimgCliOptions::try_parse_from(&arg); assert!(res.is_err()); } } #[test] fn pvimg_test_cli() { let args = BTreeMap::new(); let valid_test_args = [ flat_map_collect(insert( args.clone(), vec![ CliOption::new("host-key-hashes", ["--key-hashes"]), CliOption::new("image", ["/dev/null"]), ], )), flat_map_collect(insert( args.clone(), vec![ CliOption::new("host-key-hashes2", ["--key-hashes=/dev/null"]), CliOption::new("image", ["/dev/null"]), ], )), flat_map_collect(insert( args.clone(), vec![ CliOption::new("host-key-hashes2", ["--key-hashes=/dev/null"]), CliOption::new("image", ["/dev/null"]), // global works CliOption::new("quiet", ["-q"]), ], )), // separation between keyword and positional args works flat_map_collect(insert( args.clone(), vec![ CliOption::new("host-key-hashes2", ["--key-hashes=/dev/null"]), CliOption::new("image", ["--", "/dev/null"]), ], )), // Verify that the old verbosity is still working. flat_map_collect(insert( args.clone(), vec![ CliOption::new("host-key-hashes2", ["--key-hashes=/dev/null"]), CliOption::new("image", ["/dev/null"]), CliOption::new("verbose", ["-VVV"]), ], )), ]; let invalid_test_args = [ flat_map_collect(insert( args.clone(), vec![CliOption::new("image", ["/dev/null"])], )), // the argument '--key-hashes[=]' cannot be used with '--host-key-document // ' flat_map_collect(insert( args.clone(), vec![ CliOption::new("host-key-hashes2", ["--key-hashes=/dev/null"]), CliOption::new("host-key-document", ["--host-key-document", "/dev/null"]), CliOption::new("image", ["/dev/null"]), ], )), flat_map_collect(insert( args, vec![ CliOption::new("host-key-hashes2", ["--key-hashes", "/sys/null"]), CliOption::new("image", ["--", "/dev/null"]), ], )), ]; let mut pvimg_valid_args = vec![]; // Test for invalid combinations // Input is missing let mut pvimg_invalid_args = vec![vec!["pvimg", "test"]]; for create_args in &valid_test_args { pvimg_valid_args.push( [ ["pvimg", "test"].to_vec(), Vec::from_iter(create_args.iter().map(String::as_str)), ] .concat(), ); } for invalid_test_arg in &invalid_test_args { pvimg_invalid_args.push( [ ["pvimg", "test"].to_vec(), Vec::from_iter(invalid_test_arg.iter().map(String::as_str)), ] .concat(), ); } for arg in pvimg_valid_args { let res = CliOptions::try_parse_from(&arg); #[allow(clippy::use_debug, clippy::print_stdout)] if let Err(e) = &res { println!("arg: {arg:?}"); println!("{e}"); } assert!(res.is_ok()); } for arg in pvimg_invalid_args { let res = CliOptions::try_parse_from(&arg); assert!(res.is_err()); } } #[test] fn pvimg_info_cli() { let args = BTreeMap::new(); let valid_test_args = [ flat_map_collect(insert( args.clone(), vec![ CliOption::new("format", ["--format", "json"]), CliOption::new("image", ["/dev/null"]), ], )), flat_map_collect(insert( args.clone(), vec![ CliOption::new("format", ["--format=json"]), CliOption::new("image", ["/dev/null"]), ], )), flat_map_collect(insert( args.clone(), vec![ CliOption::new("hdr-key", ["--hdr-key", "/dev/null"]), CliOption::new("format", ["--format=json"]), CliOption::new("image", ["/dev/null"]), ], )), flat_map_collect(insert( args.clone(), vec![ CliOption::new("hdr-key", ["--key", "/dev/null"]), CliOption::new("format", ["--format=json"]), CliOption::new("image", ["/dev/null"]), ], )), // separation between keyword and positional args works flat_map_collect(insert( args.clone(), vec![ CliOption::new("format", ["--format=json"]), CliOption::new("image", ["--", "/dev/null"]), ], )), // Verify that the old verbosity is still working. flat_map_collect(insert( args.clone(), vec![ CliOption::new("format", ["--format=json"]), CliOption::new("image", ["/dev/null"]), CliOption::new("verbose", ["-VVV"]), ], )), ]; let invalid_test_args = [ // the argument '--key-hashes[=]' cannot be used with '--host-key-document // ' flat_map_collect(insert( args.clone(), vec![CliOption::new("image", ["/dev/null"])], )), // No default defined for --format flat_map_collect(insert( args, vec![ CliOption::new("format", ["--format"]), CliOption::new("image", ["--", "/dev/null"]), ], )), ]; let mut pvimg_valid_args = vec![]; // Test for invalid combinations // Input is missing let mut pvimg_invalid_args = vec![vec!["pvimg", "info"]]; for create_args in &valid_test_args { pvimg_valid_args.push( [ ["pvimg", "info"].to_vec(), Vec::from_iter(create_args.iter().map(String::as_str)), ] .concat(), ); } for invalid_test_arg in &invalid_test_args { pvimg_invalid_args.push( [ ["pvimg", "info"].to_vec(), Vec::from_iter(invalid_test_arg.iter().map(String::as_str)), ] .concat(), ); } for arg in pvimg_valid_args { let res = CliOptions::try_parse_from(&arg); #[allow(clippy::use_debug, clippy::print_stdout)] if let Err(e) = &res { println!("arg: {arg:?}"); println!("{e}"); } assert!(res.is_ok()); } for arg in pvimg_invalid_args { let res = CliOptions::try_parse_from(&arg); assert!(res.is_err()); } } #[test] fn verify_cli() { use clap::CommandFactory; CliOptions::command().debug_assert(); } } s390-tools-2.38.0/rust/pvimg/src/cmd.rs000066400000000000000000000004211502674226300174630ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 mod common; mod create; mod info; mod test; mod version; pub const CMD_FN: &[&str] = &["+create", "+test", "+info"]; pub use create::create; pub use info::info; pub use test::test; pub use version::version; s390-tools-2.38.0/rust/pvimg/src/cmd/000077500000000000000000000000001502674226300171205ustar00rootroot00000000000000s390-tools-2.38.0/rust/pvimg/src/cmd/common.rs000066400000000000000000000045411502674226300207620ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::path::{Path, PathBuf}; use anyhow::Result; use log::info; use pv::{misc::read_file, request::Confidential}; use crate::cli::CreateBootImageExperimentalArgs; #[macro_export] /// Makes it easier to macro_rules! log_println { ($($arg:tt)+) => { warn!($($arg)+) }; } pub struct UserProvidedKeys { pub(crate) cck: Option<(PathBuf, Confidential>)>, pub(crate) components_key: Option<(PathBuf, Confidential>)>, pub(crate) aead_key: Option<(PathBuf, Confidential>)>, } /// Reads all user provided keys. pub fn read_user_provided_keys( cck_path: Option<&Path>, hdr_key_path: Option<&Path>, experimental_args: &CreateBootImageExperimentalArgs, ) -> Result { let components_key = { match &experimental_args.x_comp_key { Some(key_path) => { info!( "Use file '{}' as the image components protection key", key_path.display() ); Some(( key_path.to_owned(), Confidential::new(read_file(key_path, "image components key")?), )) } None => None, } }; let aead_key = { match hdr_key_path { Some(key_path) => { info!( "Use file '{}' as the Secure Execution header protection", key_path.display() ); Some(( key_path.to_owned(), Confidential::new(read_file( key_path, "Secure Execution header protection key", )?), )) } None => None, } }; let cck = { match cck_path { Some(key_path) => { info!( "Use file '{}' as the customer communication key (CCK)", key_path.display() ); Some(( key_path.to_owned(), (Confidential::new(read_file(key_path, "customer communication key (CCK)")?)), )) } None => None, } }; Ok(UserProvidedKeys { cck, components_key, aead_key, }) } s390-tools-2.38.0/rust/pvimg/src/cmd/create.rs000066400000000000000000000211371502674226300207350ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::{fs::OpenOptions, io::BufReader}; use anyhow::{Context, Result}; use log::{debug, warn}; use pv::misc::{open_file, try_parse_u64}; use pvimg::{ error::OwnExitCode, secured_comp::ComponentTrait, uvdata::{ ControlFlagTrait, ControlFlagsTrait, FlagData, PcfV1, PlaintextControlFlagsV1, ScfV1, SeHdrDataV1, SecretControlFlagsV1, }, }; use utils::{AtomicFile, AtomicFileOperation}; use crate::{ cli::{ComponentPaths, CreateBootImageArgs}, cmd::common::read_user_provided_keys, se_img::{SeHdrArgs, SeImgBuilder}, se_img_comps::{ check_components, cmdline::Cmdline, kernel::S390Kernel, ramdisk::Ramdisk, Component, }, }; /// The returned vector is sorted by the occurrence in the memory layout: /// First the kernel, then the ramdisk and then the kernel cmdline. /// /// Keep this ordering in sync with the ordering of [`ComponentKind`]! fn components(component_args: &ComponentPaths) -> Result> { // IMPORTANT: Don't change the order of the components: kernel, ramdisk, and // then parmline! This is important since ALD, PLD and TLD is sorted by the // component address. let mut components: Vec = vec![S390Kernel::new(Box::new(BufReader::new(open_file(&component_args.kernel)?))).into()]; if let Some(path) = &component_args.ramdisk { components.push(Ramdisk::new(Box::new(BufReader::new(open_file(path)?))).into()); } if let Some(path) = &component_args.parmfile { components.push(Cmdline::new(Box::new(BufReader::new(open_file(path)?))).into()); } Ok(components) } fn parse_flags( args: &CreateBootImageArgs, ) -> Result<(PlaintextControlFlagsV1, SecretControlFlagsV1)> { let lf = &args.legacy_flags; macro_rules! flag_disabled { ($cli_flag:expr, $control_flags:expr) => { $cli_flag .filter(|x| *x) .and(Some(ControlFlagTrait::all_disabled($control_flags))) }; } macro_rules! flag_enabled { ($cli_flag:expr, $control_flags:expr) => { $cli_flag .filter(|x| *x) .and(Some(ControlFlagTrait::all_enabled($control_flags))) }; } let plaintext_flags: Vec> = [ flag_disabled!(lf.disable_dump, [PcfV1::AllowDumping]), flag_enabled!(lf.enable_dump, [PcfV1::AllowDumping]), flag_disabled!(lf.disable_pckmo, PlaintextControlFlagsV1::PCKMO), flag_enabled!(lf.enable_pckmo, PlaintextControlFlagsV1::PCKMO), flag_disabled!(lf.disable_pckmo_hmac, [PcfV1::PckmoHmac]), flag_enabled!(lf.enable_pckmo_hmac, [PcfV1::PckmoHmac]), flag_disabled!(lf.disable_backup_keys, [PcfV1::BackupTargetKeys]), flag_enabled!(lf.enable_backup_keys, [PcfV1::BackupTargetKeys]), flag_enabled!(lf.disable_image_encryption, [PcfV1::NoComponentEncryption]), flag_disabled!(lf.enable_image_encryption, [PcfV1::NoComponentEncryption]), ] .into_iter() .flatten() .flatten() .collect(); // This is ensured by Clap's `conflicts_with`. assert!(PlaintextControlFlagsV1::no_duplicates(&plaintext_flags)); let secret_flags: Vec> = [ flag_disabled!( lf.disable_cck_extension_secret, [ScfV1::CckExtensionSecretEnforcement] ), flag_enabled!( lf.enable_cck_extension_secret, [ScfV1::CckExtensionSecretEnforcement] ), flag_disabled!(lf.disable_cck_update, [ScfV1::CckUpdateAllowed]), flag_enabled!(lf.enable_cck_update, [ScfV1::CckUpdateAllowed]), ] .into_iter() .flatten() .flatten() .collect(); // This is ensured by Clap's `conflicts_with`. assert!(SecretControlFlagsV1::no_duplicates(&secret_flags)); let mut pcf: PlaintextControlFlagsV1 = match &args.experimental_args.x_pcf { Some(v) => try_parse_u64(v, "x-pcf")?.into(), None => PlaintextControlFlagsV1::default(), }; pcf.parse_flags(&plaintext_flags); debug!("Using plaintext flags: {pcf}"); let mut scf: SecretControlFlagsV1 = match &args.experimental_args.x_scf { Some(v) => try_parse_u64(v, "x-scf")?.into(), None => SecretControlFlagsV1::default(), }; scf.parse_flags(&secret_flags); debug!("Using secret flags: {scf}"); Ok((pcf, scf)) } /// Create a Secure Execution boot image pub fn create(opt: &CreateBootImageArgs) -> Result { // Verify host key documents first, because if they are not valid there is // no reason to continue. let verified_host_keys = opt .certificate_args .get_verified_hkds("Secure Execution image")?; let user_provided_keys = read_user_provided_keys( opt.cck.as_deref(), opt.hdr_key.as_deref(), &opt.experimental_args, )?; let (plaintext_flags, secret_flags) = parse_flags(opt)?; if plaintext_flags.is_set(PcfV1::NoComponentEncryption) { warn!("The components encryption is disabled, make sure that the components do not contain any confidential content."); } let mut components = components(&opt.component_paths)?; if opt.no_component_check { warn!("The component check is turned off!"); } else { check_components(&mut components)?; } // FIXME get rid of the legacy mode. But that's only possible as soon as all // available tools are updated. let expected_se_hdr_size = SeHdrDataV1::expected_size(verified_host_keys.len())?; let mut writer = AtomicFile::with_extension(&opt.output, "part", &mut OpenOptions::new())?; let mut seimg_ctx = SeImgBuilder::new_v1( &mut writer, plaintext_flags.is_unset(PcfV1::NoComponentEncryption), Some(expected_se_hdr_size), opt.experimental_args.x_bootloader_directory.as_ref(), )?; // Enable expert mode seimg_ctx.i_know_what_i_am_doing(); if let Some((path, key)) = user_provided_keys.components_key { seimg_ctx.set_components_key(key).with_context(|| { format!( "Failed to use '{}' as the image components key", path.display() ) })?; } let psw_addr: Option = match &opt.experimental_args.x_psw { Some(v) => try_parse_u64(v, "x-psw")?.into(), None => None, }; for mut component in components.into_iter() { seimg_ctx .prepare_and_append_as_secure_component(&mut component, None) .with_context(|| format!("Failed to prepare {} component", component.kind()))?; } let img_comps = seimg_ctx.finish(SeHdrArgs { keys: verified_host_keys.as_slice(), pcf: &plaintext_flags, scf: &secret_flags, cck: &user_provided_keys.cck, hdr_aead_key: &user_provided_keys.aead_key, psw_addr: &psw_addr, })?; debug!(""); debug!("----------------------------------------------------------------"); debug!("| {:^60} |", "Secure Execution image layout"); debug!("|--------------------------------------------------------------|"); debug!("| {:<23} | {:<34} |", "Component type", "Component address"); debug!("|-------------------------|------------------------------------|"); img_comps .iter() .for_each(|img_comp| debug!("{img_comp:<33}")); debug!("----------------------------------------------------------------"); // Rename the file `$OUTPUT.part` to `$OUTPUT` for achieving atomic file // creation. let op = match opt.overwrite { true => AtomicFileOperation::Replace, false => AtomicFileOperation::NoReplace, }; writer.finish(op)?; warn!("Successfully generated the Secure Execution image."); Ok(OwnExitCode::Success) } #[cfg(test)] mod test { use super::*; use crate::cli::CreateBootImageLegacyFlags; #[test] fn parse_flags() { let args = CreateBootImageArgs { legacy_flags: CreateBootImageLegacyFlags { enable_dump: Some(true), enable_cck_update: Some(true), ..Default::default() }, ..Default::default() }; let parsed_flags = super::parse_flags(&args).expect("Failed to parse flags {args:?}"); let mut exp_pcf = Vec::from(PlaintextControlFlagsV1::PCKMO); exp_pcf.push(PcfV1::AllowDumping); let pcf = PlaintextControlFlagsV1::from_flags(PcfV1::all_enabled(exp_pcf)); assert_eq!(parsed_flags.0, pcf); let exp_scf = vec![ScfV1::CckUpdateAllowed]; let scf = SecretControlFlagsV1::from_flags(ScfV1::all_enabled(exp_scf)); assert_eq!(parsed_flags.1, scf); } } s390-tools-2.38.0/rust/pvimg/src/cmd/info.rs000066400000000000000000000017421502674226300204250ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::io::Write; use anyhow::Result; use log::info; use pv::{ misc::{open_file, read_file}, request::SymKey, }; use pvimg::{ error::OwnExitCode, uvdata::{KeyExchangeTrait, SeHdr, UvDataTrait}, }; use crate::cli::InfoArgs; pub fn info(opt: &InfoArgs) -> Result { info!( "Reading Secure Execution header {}", opt.input.path.display() ); let mut input = open_file(&opt.input.path)?; let mut output = std::io::stdout(); SeHdr::seek_sehdr(&mut input, None)?; let hdr = SeHdr::try_from_io(input)?; if let Some(key_path) = &opt.hdr_key { let key = SymKey::try_from_data(hdr.key_type(), read_file(key_path, "Reading key")?.into())?; serde_json::to_writer_pretty(&mut output, &hdr.decrypt(&key)?)?; } else { serde_json::to_writer_pretty(&mut output, &hdr)?; } writeln!(output)?; Ok(OwnExitCode::Success) } s390-tools-2.38.0/rust/pvimg/src/cmd/test.rs000066400000000000000000000071371502674226300204550ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::path::{Path, PathBuf}; use anyhow::Result; use log::{info, warn}; use pv::{ misc::{open_file, read_certs, read_file}, FileAccessErrorType, PvCoreError, }; use pvimg::{ error::{Error, OwnExitCode, PvError}, uvdata::{KeyExchangeTrait, SeHdr, UvKeyHashesV1}, }; use utils::HexSlice; use crate::{cli::TestArgs, log_println}; /// Returns `Ok(true)` if at least one of the hashes is included. fn hdr_test_target_hashes(hdr: &SeHdr, key_hashes: &Path) -> Result { let file = open_file(key_hashes).map_err(|err| match err { PvCoreError::FileAccess { ref ty, ref path, ref source, } if matches!(ty, FileAccessErrorType::Open) && source.kind() == std::io::ErrorKind::NotFound && *path == PathBuf::from(UvKeyHashesV1::SYS_UV_KEYS_ALL) => { Error::UnavailableQueryUvKeyHashesSupport { source: err } } err => Error::PvCore(err), })?; let hashes = UvKeyHashesV1::read_from_io(file)?; let mut contains = hdr.contains_hash(&hashes.pchkh); if contains { log_println!( " ✓ Host key hash {:#} is included", HexSlice::from(&hashes.pchkh) ); } if hdr.contains_hash(&hashes.pbhkh) { log_println!( " ✓ Backup host key hash {:#} is included", HexSlice::from(&hashes.pbhkh) ); contains = true; }; for hash in hashes.res { if hdr.contains_hash(&hash) { log_println!(" ✓ Key hash {:#} is included", HexSlice::from(&hash)); contains = true; } } if !contains { warn!(" ✘ None of the key hashes is included"); } Ok(contains) } /// Returns `Ok(true)` if at least one of the given public key of the host key /// documents was used for the image creation or if no host key document was /// specified. fn hdr_test_hkd

(hdr: &SeHdr, host_key_documents: &[P]) -> Result where P: AsRef, { if host_key_documents.is_empty() { return Ok(true); } let mut result = false; for path in host_key_documents { let hkd_path = path.as_ref(); let hkd_data = read_file(hkd_path, "host key document")?; let certs = read_certs(&hkd_data)?; if certs.is_empty() { return Err(PvError::NoHkdInFile(hkd_path.display().to_string()).into()); } if certs.len() != 1 { warn!("The host key document in '{}' contains more than one certificate! Only the first certificate will be used.", hkd_path.display()); } // Panic: len is == 1 -> unwrap will succeed/not panic let cert = certs.first().unwrap(); if hdr.contains(cert.public_key()?)? { result = true; log_println!(" ✓ Host key document '{}' is included", hkd_path.display()); } else { log_println!( " ✘ Host key document '{}' is not included", hkd_path.display() ); } } Ok(result) } pub fn test(opt: &TestArgs) -> Result { info!("Testing a Secure Execution image"); let mut input = open_file(&opt.input.path)?; SeHdr::seek_sehdr(&mut input, None)?; let hdr = SeHdr::try_from_io(input)?; let mut success = hdr_test_hkd(&hdr, &opt.host_key_documents)?; if let Some(path) = &opt.key_hashes { success = hdr_test_target_hashes(&hdr, path)? && success; } Ok(if success { OwnExitCode::Success } else { OwnExitCode::GenericError }) } s390-tools-2.38.0/rust/pvimg/src/cmd/version.rs000066400000000000000000000006141502674226300211540ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use anyhow::Result; use log::LevelFilter; use pvimg::error::OwnExitCode; use utils::print_version; use crate::cmd; const FEATURES: &[&[&str]] = &[cmd::CMD_FN]; /// Print the version pub fn version(filter: LevelFilter) -> Result { print_version!("2024", filter; FEATURES.concat()); Ok(OwnExitCode::Success) } s390-tools-2.38.0/rust/pvimg/src/lib.rs000066400000000000000000000027531502674226300175000ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 //! # Library for Secure Execution headers //! //! This crate provides functionalities for creating and inspecting Secure //! Execution headers. It also provides support for preparing arbitrary //! components as secured components and calculating the PLD, ALD, and TLD of //! them. //! //! ## Secure Execution headers //! ### Creation //! //! [`uvdata::SeHdrBuilder`] //! //! ### Serialization and Deserialization //! //! [`uvdata::SeHdr`] //! //! ## Secured components //! //! [`secured_comp::SecuredComponentBuilder`] and //! [`secured_comp::SecuredComponent`]. #![allow(missing_docs)] mod pv_utils; pub mod misc { pub const PAGESIZE: usize = 4096; pub use crate::pv_utils::{ bytesize, round_up, serialize_to_bytes, ShortPsw, PSW, PSW_MASK_BA, PSW_MASK_EA, }; } pub mod uvdata { pub use crate::pv_utils::{ AeadPlainDataTrait, BuilderTrait, ComponentMetadataV1, ControlFlagTrait, ControlFlagsTrait, FlagData, KeyExchangeTrait, PcfV1, PlaintextControlFlagsV1, ScfV1, SeHdr, SeHdrAadV1, SeHdrBinV1, SeHdrBuilder, SeHdrData, SeHdrDataV1, SeHdrPlain, SeHdrVersion, SeHdrVersioned, SecretControlFlagsV1, UvDataPlainTrait, UvDataTrait, UvKeyHashesV1, }; } pub mod secured_comp { pub use crate::pv_utils::{ ComponentTrait, Interval, Layout, SecuredComponent, SecuredComponentBuilder, }; } pub mod error { pub use crate::pv_utils::{Error, OwnExitCode, PvError, Result}; } s390-tools-2.38.0/rust/pvimg/src/main.rs000066400000000000000000000040471502674226300176540ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 //! # pvimg //! //! `pvimg` is a command line utility to create and inspect IBM Secure //! Execution boot images. //! //! Use `pvimg` to create a IBM Secure Execution boot image file, which can //! be loaded using `zipl` or `QEMU`. The tool can also be used to inspect //! existing Secure Execution boot images. mod cli; mod cmd; mod se_img; mod se_img_comps; use std::{env, process::ExitCode}; use clap::{Command, CommandFactory, Parser}; use cli::{validate_cli, CliOptions, SubCommands}; use log::trace; use pvimg::error::OwnExitCode; use utils::{print_cli_error, print_error, PvLogger}; use crate::cli::GenprotimgCliOptions; static LOGGER: PvLogger = PvLogger; fn main() -> ExitCode { let exe = env::args_os().next().unwrap(); let (opts, cmd): (CliOptions, Command) = match exe.to_str() { // Test if the symlink executable 'genprotimg' was used. If so use the // `pvimg create` command directly. Some(val) if val.ends_with("genprotimg") => ( GenprotimgCliOptions::own_parse(), GenprotimgCliOptions::command(), ), _ => (CliOptions::parse(), CliOptions::command()), }; let verbosity = opts.verbose.to_level_filter(); if let Err(e) = LOGGER.start(verbosity) { unreachable!("Logger error: {e:?}"); } match validate_cli(&opts) { Ok(opts) => opts, Err(e) => { let _ = print_cli_error(e, cmd); return OwnExitCode::UsageError.into(); } }; // NOTE trace verbosity is disabled in release builds trace!("Trace verbosity, may leak secrets to command-line"); trace!("Options {opts:?}"); let res = match &opts.cmd { SubCommands::Create(opt) => cmd::create(opt), SubCommands::Info(opt) => cmd::info(opt), SubCommands::Test(opt) => cmd::test(opt), SubCommands::Version => cmd::version(verbosity), }; match res { Ok(own_exit_code) => own_exit_code.into(), Err(e) => print_error(&e, verbosity), } } s390-tools-2.38.0/rust/pvimg/src/pv_utils.rs000066400000000000000000000017021502674226300205700ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 mod error; mod layout; mod misc; mod psw; mod se_hdr; mod secured_comp; mod serializing; mod uv_keys; mod uvdata; mod uvdata_builder; pub use error::{Error, OwnExitCode, PvError, Result}; pub use layout::{Interval, Layout}; pub use misc::{round_up, try_copy_slice_to_array}; pub use psw::{ShortPsw, PSW, PSW_MASK_BA, PSW_MASK_EA}; pub use se_hdr::{ ComponentMetadataV1, ControlFlagTrait, ControlFlagsTrait, FlagData, PcfV1, PlaintextControlFlagsV1, ScfV1, SeHdr, SeHdrAadV1, SeHdrBinV1, SeHdrBuilder, SeHdrData, SeHdrDataV1, SeHdrPlain, SeHdrVersion, SeHdrVersioned, SecretControlFlagsV1, }; pub use secured_comp::{ComponentTrait, SecuredComponent, SecuredComponentBuilder}; pub use serializing::{bytesize, serialize_to_bytes}; pub use uv_keys::UvKeyHashesV1; pub use uvdata::{AeadPlainDataTrait, KeyExchangeTrait, UvDataPlainTrait, UvDataTrait}; pub use uvdata_builder::BuilderTrait; s390-tools-2.38.0/rust/pvimg/src/pv_utils/000077500000000000000000000000001502674226300202225ustar00rootroot00000000000000s390-tools-2.38.0/rust/pvimg/src/pv_utils/error.rs000066400000000000000000000113701502674226300217230ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 pub use pv::Error as PvError; pub use pv::PvCoreError; use utils::{impl_exitcodetrait, ExitCodeTrait}; /// Result type for this crate pub type Result = std::result::Result; /// Error cases for this crate #[derive(thiserror::Error, Debug)] #[non_exhaustive] pub enum Error { #[error("First image component was already prepared")] FirstComponentAlreadyPrepared, #[error( "Stage3b is already added so there is no possibility to add another secured component" )] ImgAlreadyFinalized, #[error("Invalid target key hash")] InvalidTargetKeyHash, #[error("Invalid UV key hashes")] InvalidUvKeyHashes, #[error("Invalid Secure Execution header")] InvalidSeHdr, #[error("Secure Execution header size {given} is larger than the maximum of {maximum} bytes")] InvalidSeHdrTooLarge { given: usize, maximum: usize }, #[error("Invalid component metadata.")] InvalidComponentMetadata, #[error("Invalid alignment {alignment} as it's larger than the chunk size {chunk_size}.")] InvalidAlignment { alignment: u64, chunk_size: usize }, #[error("Invalid interval: Start {start} is larger than {stop}")] InvalidInterval { start: u64, stop: u64 }, #[error( "The given tweak size {given} is smaller than the expected tweak size, which is {expected}" )] InvalidTweakSize { given: usize, expected: usize }, #[error("Invalid customer communication key (CCK)")] InvalidCCK { source: Box }, #[error("Invalid stage3a")] InvalidStage3a, #[error("Invalid stage3b")] InvalidStage3b, #[error("Interval overlaps: {0}")] IntervalOverlap(String), #[error("Image already finalized")] ImageAlreadyFinalized, #[error("Provided kernel cmdline is too large: {size} > {max_size}")] KernelCmdlineTooLarge { size: usize, max_size: usize }, #[error("Cannot convert to short PSW")] TryToShortPSWError, #[error("Address {addr:#0x} is not aligned to {alignment:#0x}")] UnalignedAddress { addr: u64, alignment: u64 }, #[error("Support for query UV host key hashes is not available")] UnavailableQueryUvKeyHashesSupport { source: PvCoreError }, #[error("ELF file found, but only raw binary kernels are supported.")] UnexpectedElfFile, #[error("Unexpected arithmetic overflow")] UnexpectedOverflow, #[error("Unexpected key type. Given {given}, expected {expected}")] UnexpectedKeyType { given: String, expected: String }, #[error("Unsupported message digest")] UnsupportMessageDigest, #[error("Unexpected arithmetic underflow")] UnexpectedUnderflow, #[error( "Address {addr:#0x} is smaller than the next possible address, which is {next_addr:#0x}" )] NonMonotonicallyIncreasing { addr: u64, next_addr: u64 }, #[error("No host key document provided")] NoHostkey, #[error("No s390x Linux kernel provided")] NoS390Kernel, #[error("Expert mode is not enabled")] NonExpertMode, #[error("Tweaks can be specified in expert mode only")] NonExpertModeTweakGiven, #[error("No plaintext control flag")] NoPlainTextControlFlag, #[error("No secret control flag")] NoSecretControlFlag, #[error("No Secure Execution header found.")] NoSeHdrFound, #[error("Address {addr} is already used")] NoUnusedAddr { addr: u64 }, #[error("Prepared component is too large for the given location: {output_size} > {max_output_size}")] PreparedComponentTooLarge { output_size: usize, max_output_size: usize, }, // Errors from other crates #[error(transparent)] Deku(#[from] deku::DekuError), #[error(transparent)] Crypto(#[from] openssl::error::ErrorStack), #[error(transparent)] Pv(#[from] PvError), #[error(transparent)] PvCore(#[from] PvCoreError), #[error(transparent)] Io(#[from] std::io::Error), #[error(transparent)] TryFromIntError(#[from] std::num::TryFromIntError), } impl_exitcodetrait!( #[repr(u8)] #[derive(Debug)] pub enum OwnExitCode { /// Program finished successfully /// /// The command was executed successfully. Success = 0, /// Generic error /// /// Something went wrong during the operation. Refer to the error /// message. GenericError = 1, /// Usage error /// /// The command was used incorrectly, for example: unsupported command /// line flag, or wrong number of arguments. UsageError = 2, // same exit code as used by `Clap` crate } ); impl From for std::process::ExitCode { fn from(value: OwnExitCode) -> Self { Self::from(value as u8) } } s390-tools-2.38.0/rust/pvimg/src/pv_utils/layout.rs000066400000000000000000000276461502674226300221240ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::{collections::BTreeSet, fmt::Display, rc::Rc}; use crate::{ misc::round_up, pv_utils::error::{Error, Result}, }; /// Represents a range from [start, stop) (inclusive start, exclusive stop) #[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)] pub struct Interval { pub start: u64, pub stop: u64, } impl Display for Interval { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { format!("start: {:#10x} stop: {:#10x}", self.start, self.stop - 1).fmt(f) } } impl Interval { /// Create a new [`Interval`]. /// /// # Errors /// /// This function will return an error if `stop` is not larger than `start`. const fn new(start: u64, stop: u64) -> Result { if stop <= start { return Err(Error::InvalidInterval { start, stop }); } Ok(Self { start, stop }) } /// Creates a new [`Interval`] with the start address `start` and the size /// of `size`. /// /// # Errors /// /// This function will return an error if `size == 0` or if there was an /// unexpected overflow. pub fn new_with_size(start: u64, size: u64) -> Result { Self::new( start, start.checked_add(size).ok_or(Error::UnexpectedOverflow)?, ) } const fn contains(&self, addr: u64) -> bool { addr >= self.start && addr < self.stop } /// Returns the size of this [`Interval`]. pub const fn size(&self) -> u64 { self.stop - self.start } } #[derive(Debug, PartialEq, Eq)] pub struct Layout { pub next_addr: u64, pub alignment: u64, chunks: BTreeSet>, } impl Layout { /// Creates a new [`Layout`]. /// /// # Errors /// /// This function will return an error if `start_addr` is not aligned. pub fn new(start_addr: u64, alignment: u64) -> Result { if start_addr != round_up(start_addr, alignment)? { return Err(Error::UnalignedAddress { addr: start_addr, alignment, }); } Ok(Self { next_addr: start_addr, alignment, chunks: BTreeSet::new(), }) } fn is_aligned(&self, addr: u64) -> Result { Ok(addr == round_up(addr, self.alignment)?) } fn overlaps(&self, b: &Interval) -> Option> { for a in self.chunks.iter() { if a.start < b.stop && b.start < a.stop { return Some(a.clone()); } } None } /// Returns the maximum chunk size at the given address `addr`. If there is /// no limit `None` is returned. /// /// # Errors /// /// This function will return an error if the address is in use already. pub fn max_size_of_chunk_at_addr(&self, addr: u64) -> Result> { if !self.is_aligned(addr)? { return Err(Error::UnalignedAddress { addr, alignment: self.alignment, }); } if addr >= self.next_addr { return Ok(None); } for chunk in &self.chunks { if chunk.contains(addr) { return Err(Error::NoUnusedAddr { addr }); } if chunk.start >= addr { let max_size = usize::try_from(chunk.start - addr).unwrap(); return Ok(Some(max_size)); } } Ok(None) } /// Insert an interval in the layout. /// /// # Errors /// /// This function will return an error if the given address was unaligned or /// the interval would overlap with an existing interval in the layout. pub fn insert_interval(&mut self, addr: u64, size: u64) -> Result> { let interval = Interval::new_with_size(addr, size)?; assert!(self.next_addr % self.alignment == 0); if interval.start != round_up(interval.start, self.alignment)? { return Err(Error::UnalignedAddress { addr: interval.start, alignment: self.alignment, }); } if let Some(overlapped) = self.overlaps(&interval) { let msg = format!("{} ... {}", overlapped, interval); return Err(Error::IntervalOverlap(msg)); } let interval = Rc::new(interval); self.chunks.insert(interval.clone()); let maybe_next_addr = interval .start .checked_add(round_up(size, self.alignment)?) .ok_or(Error::UnexpectedOverflow)?; if maybe_next_addr > self.next_addr { self.next_addr = maybe_next_addr; } assert!(self.next_addr % self.alignment == 0); Ok(interval) } /// Creates and appends this newly created interval with size `size` to the /// layout. Returns the created interval. /// /// # Errors /// /// This function will return an error if it was not possible to append the /// newly created interval. pub fn push(&mut self, size: u64) -> Result> { let addr = self.next_addr; self.insert_interval(addr, size) } } impl IntoIterator for Layout { type IntoIter = > as IntoIterator>::IntoIter; type Item = Rc; fn into_iter(self) -> Self::IntoIter { self.chunks.into_iter() } } #[allow(clippy::shadow_unrelated)] #[cfg(test)] mod tests { use std::{collections::BTreeSet, rc::Rc}; use proptest::{ prelude::{Just, Strategy}, prop_assert, prop_assert_eq, proptest, }; use crate::pv_utils::{Interval, Layout}; proptest! { #[test] fn interval_new( (a,b) in (0..u64::MAX).prop_flat_map(|a| (Just(a), 0..a)) ) { prop_assert!(b < a); Interval::new(b, a).expect("should not fail"); } #[test] fn interval_new_with_size( (start, size) in (0..u64::MAX).prop_flat_map(|a| (Just(a), 1..=u64::MAX - a)) ) { Interval::new_with_size(start, size).expect("should not fail"); } #[test] fn interval_contains( (start, size, c) in (0_u16..4_u16).prop_flat_map(|a| (Just(a), 1_u16..4_u16)).prop_flat_map(|(a,b)| (Just(a), Just(b), a..(a + b))) ) { let interval = Interval::new_with_size(start.into(), size.into()).expect("should not fail"); prop_assert!(interval.contains(c.into())); } #[test] fn interval_cmp( (start, size, start2) in (1..16_u64).prop_flat_map(|a| (Just(a), 1..16_u16)).prop_flat_map(|(a,b)| (Just(a), Just(b), 0..a)) ) { let interval = Interval::new_with_size(start, size.into()).expect("should not fail"); let interval2 = Interval::new_with_size(start2, size.into()).expect("should not fail"); let interval3 = Interval::new_with_size(start, >::into(size) + 1).expect("should not fail"); let interval4 = Interval::new_with_size(start, size.into()).expect("should not fail"); prop_assert!(interval > interval2); prop_assert!(interval != interval2); prop_assert!(interval < interval3); prop_assert!(interval == interval4); } #[test] fn interval_size((start, size) in (0..u64::MAX).prop_flat_map(|a| (Just(a), 1..=u64::MAX - a)) ) { let interval = Interval::new_with_size(start, size).expect("should not fail"); prop_assert_eq!(interval.size(), size); } } #[test] fn interval_overflow() { Interval::new_with_size(u64::MAX, 1).expect_err("should fail"); } #[test] fn memory_layout_test() { // Unaligned start address let layout = Layout::new(0x1_u64, 0x1000_u64); assert!(layout.is_err()); let mut layout = Layout::new(0x1000_u64, 0x1000_u64).unwrap(); assert_eq!( layout, Layout { next_addr: 0x1000, alignment: 0x1000, chunks: BTreeSet::new(), } ); layout.push(0x16).unwrap(); layout.push(0x1000).unwrap(); layout.push(0x1).unwrap(); let mut bin = BTreeSet::from([ Rc::new(Interval::new_with_size(0x1000, 0x16).expect("should not fail")), Rc::new(Interval::new_with_size(0x2000, 0x1000).expect("should not fail")), Rc::new(Interval::new_with_size(0x3000, 0x1).expect("should not fail")), ]); assert_eq!( layout, Layout { next_addr: 0x4000, alignment: 0x1000, chunks: bin.clone() } ); // Invalid chunk size assert!(layout.push(0x0).is_err()); // NonMonolithic address assert!(layout.insert_interval(0x0, 0x1001).is_err()); assert!(layout.insert_interval(0x0, 0x1000).is_ok()); assert!(layout.insert_interval(0x10, 0x1000).is_err()); bin.insert(Rc::new( Interval::new_with_size(0x0, 0x1000).expect("should not fail"), )); assert_eq!( layout, Layout { next_addr: 0x4000, alignment: 0x1000, chunks: bin.clone() } ); assert!(layout.insert_interval(0x3000, 0x400).is_err()); assert!(layout.insert_interval(0x4000, 0x400).is_ok()); bin.insert(Rc::new( Interval::new_with_size(0x4000, 0x400).expect("should not fail"), )); assert_eq!( layout, Layout { next_addr: 0x5000, alignment: 0x1000, chunks: bin } ); } #[test] fn test_max_interval_size_at_addr() { let mut layout = Layout::new(0x0_u64, 0x1000_u64).expect("should not fail"); assert_eq!( layout, Layout { next_addr: 0x0, alignment: 0x1000, chunks: BTreeSet::new(), } ); layout.push(0x16).unwrap(); layout.push(0x1000).unwrap(); layout.push(0x1).unwrap(); layout.push(0x0).expect_err("should fail"); let bin = BTreeSet::from([ Rc::new(Interval::new_with_size(0x0, 0x16).expect("should not fail")), Rc::new(Interval::new_with_size(0x1000, 0x1000).expect("should not fail")), Rc::new(Interval::new_with_size(0x2000, 0x1).expect("should not fail")), ]); assert_eq!( layout, Layout { next_addr: 0x3000, alignment: 0x1000, chunks: bin, } ); layout .max_size_of_chunk_at_addr(0x5) .expect_err("should fail"); layout .max_size_of_chunk_at_addr(0x2000) .expect_err("should fail"); layout .max_size_of_chunk_at_addr(0x2fff) .expect_err("should fail"); assert_eq!( layout .max_size_of_chunk_at_addr(0x3000) .expect("should not fail"), None ); layout.alignment = 1; assert_eq!( layout .max_size_of_chunk_at_addr(0x16) .expect("should not fail"), Some(0x1000 - 0x16) ); assert_eq!( layout .max_size_of_chunk_at_addr(0xfff) .expect("should not fail"), Some(0x1) ); layout .max_size_of_chunk_at_addr(0x1000) .expect_err("should not fail"); assert_eq!( layout .max_size_of_chunk_at_addr(0x2fff) .expect("should not fail"), None ); assert_eq!( layout .max_size_of_chunk_at_addr(0x3000) .expect("should not fail"), None ); } } s390-tools-2.38.0/rust/pvimg/src/pv_utils/misc.rs000066400000000000000000000036121502674226300215250ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use pv::PvCoreError; use crate::error::{Error, Result}; /// Rounds up the given `value` to a multiple of `multiple`. /// /// # Errors /// /// This function will return an error if there was an unexpected arithmetic /// overflow. pub fn round_up(value: u64, multiple: u64) -> Result { assert!(multiple >= 1); Ok((value .checked_add(multiple) .ok_or(Error::UnexpectedOverflow)? - 1) & !(multiple - 1)) } /// Try to copy a slice to an array. /// /// # Errors /// /// This function will return an error if the length of the slice is not equal /// to the length of the destination array. pub fn try_copy_slice_to_array( src: &[T], ) -> Result<[T; COUNT]> { if COUNT != src.len() { return Err(Error::PvCore(PvCoreError::LengthMismatch { expected: COUNT, actual: src.len(), })); } let mut result = [T::default(); COUNT]; result.copy_from_slice(src); Ok(result) } #[allow(clippy::shadow_unrelated)] #[cfg(test)] mod tests { use pv::PvCoreError; use crate::{ error::{Error, Result}, pv_utils::try_copy_slice_to_array, }; #[test] fn test_try_copy_slice_to_array() { let data = vec![]; let result: [u8; 0] = try_copy_slice_to_array(data.as_slice()).expect("should not fail"); assert_eq!(data, result); let data = vec![0x1_u8, 0x2_u8, 0x3_u8]; let result: [u8; 3] = try_copy_slice_to_array(data.as_slice()).expect("should not fail"); assert_eq!(data, result); let result: Result<[u8; 4]> = try_copy_slice_to_array(data.as_slice()); assert!(matches!( result, Err(Error::PvCore(PvCoreError::LengthMismatch { expected: 4, actual: 3 })) )); } } s390-tools-2.38.0/rust/pvimg/src/pv_utils/psw.rs000066400000000000000000000072151502674226300214060ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use deku::{ctx::Endian, DekuRead, DekuWrite}; use pv::request::Zeroize; use serde::Serialize; use super::serializing::ser_lower_hex; use crate::pv_utils::error::Error; pub const PSW32_ADDR_MASK: u64 = 0x000000007fffffff; pub const PSW_MASK_BA: u64 = 0x0000000080000000; pub const PSW_MASK_EA: u64 = 0x0000000100000000; pub const PSW_MASK_BIT_12: u64 = 0x08000000000000; #[derive(Default, Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize)] #[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")] pub struct PSW { #[serde(serialize_with = "ser_lower_hex")] pub mask: u64, #[serde(serialize_with = "ser_lower_hex")] pub addr: u64, } impl Zeroize for PSW { fn zeroize(&mut self) { self.mask.zeroize(); self.addr.zeroize(); } } #[derive(Debug, Default, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize)] #[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")] pub struct ShortPsw(u64); impl From for PSW { fn from(value: ShortPsw) -> Self { let mask = value.0 & !PSW32_ADDR_MASK & !PSW_MASK_BIT_12; let addr = value.0 & PSW32_ADDR_MASK; Self { mask, addr } } } impl TryFrom for ShortPsw { type Error = Error; fn try_from(value: PSW) -> Result { // test if PSW mask can be converted if value.mask & PSW32_ADDR_MASK != 0 { return Err(Error::TryToShortPSWError); } // test for bit 12 if value.mask & PSW_MASK_BIT_12 != 0 { return Err(Error::TryToShortPSWError); } // test if PSW addr can be converted if value.addr & !PSW32_ADDR_MASK != 0 { return Err(Error::TryToShortPSWError); } let mut short_psw = value.mask; // Set bit 12 to 1 short_psw |= PSW_MASK_BIT_12; short_psw |= value.addr; Ok(Self(short_psw)) } } #[cfg(test)] mod tests { use super::{ShortPsw, PSW}; use crate::pv_utils::{error::Result, psw::PSW_MASK_BIT_12}; #[test] fn test_from_psw_to_short_psw_ok() { let psw = PSW { mask: 0x180000000, addr: 0x11000, }; let short_psw_res: Result = psw.try_into(); assert!(short_psw_res.is_ok()); let short_psw = short_psw_res.unwrap(); assert_eq!(short_psw, ShortPsw(0x8000180011000)); } #[test] fn test_from_psw_to_short_psw_mask_bit12_is_set() { let psw = PSW { mask: PSW_MASK_BIT_12, addr: 0x11000, }; let short_psw_res: Result = psw.try_into(); assert!(short_psw_res.is_err()); } #[test] fn test_from_psw_to_short_psw_mask_too_large() { let psw = PSW { mask: 0x8000180011000, addr: 0x11000, }; let short_psw_res: Result = psw.try_into(); assert!(short_psw_res.is_err()); } #[test] fn test_from_psw_to_short_psw_addr_too_large() { let psw = PSW { mask: 0x180000000, addr: 0x8000180011000, }; let short_psw_res: Result = psw.try_into(); assert!(short_psw_res.is_err()); } #[test] fn test_from_psw_to_short_psw_and_vice_versa() { let psw = PSW { mask: 0x180000000, addr: 0x11000, }; let short_psw_res: Result = psw.clone().try_into(); assert!(short_psw_res.is_ok()); let short_psw = short_psw_res.unwrap(); let new_psw: PSW = short_psw.into(); assert_eq!(new_psw, psw); } } s390-tools-2.38.0/rust/pvimg/src/pv_utils/se_hdr.rs000066400000000000000000000007351502674226300220410ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 mod brb; mod builder; mod flags; mod hdr_v1; mod keys; pub use brb::{ ComponentMetadata, ComponentMetadataV1, SeHdr, SeHdrDataV1, SeHdrPlain, SeHdrVersion, }; pub use brb::{SeHdrBinV1, SeHdrData, SeHdrVersioned}; pub use builder::SeHdrBuilder; pub use flags::{ ControlFlagTrait, ControlFlagsTrait, FlagData, PcfV1, PlaintextControlFlagsV1, ScfV1, SecretControlFlagsV1, }; pub use hdr_v1::SeHdrAadV1; s390-tools-2.38.0/rust/pvimg/src/pv_utils/se_hdr/000077500000000000000000000000001502674226300214665ustar00rootroot00000000000000s390-tools-2.38.0/rust/pvimg/src/pv_utils/se_hdr/brb.rs000066400000000000000000000315451502674226300226110ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::{ io::{Read, Seek, SeekFrom}, mem::size_of, }; use deku::{ctx::Endian, prelude::*}; use enum_dispatch::enum_dispatch; use pv::{ request::{ openssl::pkey::{PKey, PKeyRef, Private, Public}, seek_se_hdr_start, Aes256XtsKey, Confidential, SymKey, SymKeyType, }, static_assert, }; use serde::Serialize; pub use super::hdr_v1::{SeHdrBinV1, SeHdrDataV1}; use super::{PlaintextControlFlagsV1, SecretControlFlagsV1}; use crate::{ misc::PAGESIZE, pv_utils::{ error::{Error, Result}, serializing::{ser_hex, serialize_to_bytes}, uvdata::{ AeadCipherTrait, AeadDataTrait, AeadPlainDataTrait, KeyExchangeTrait, UvDataPlainTrait, UvDataTrait, }, uvdata_builder::{AeadCipherBuilderTrait, KeyExchangeBuilderTrait}, PSW, }, }; #[repr(u32)] #[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq, Eq, DekuRead, DekuWrite, Serialize)] #[deku( endian = "endian", id_type = "u32", ctx = "endian: Endian", ctx_default = "Endian::Big" )] pub enum SeHdrVersion { /// Secure Execution header v1 V1 = 0x100, } #[repr(C)] #[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize)] #[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")] pub struct SeHdrCommon { #[serde(serialize_with = "ser_hex")] pub magic: [u8; 8], pub version: SeHdrVersion, } static_assert!(::std::mem::size_of::() == 12); #[repr(C)] #[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize)] #[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")] pub struct SeHdrCommonWithSize { pub magic: [u8; 8], pub version: SeHdrVersion, pub sehs: u32, } static_assert!(::std::mem::size_of::() == 16); impl SeHdrCommon { /// Magic value for a SE-header (FIXME as soon as `concat_bytes!(b"IBMSecEx`") is stable) pub(crate) const MAGIC: &'static [u8; 8] = &[73, 66, 77, 83, 101, 99, 69, 120]; pub(crate) const fn new(version: SeHdrVersion) -> Self { Self { magic: *Self::MAGIC, version, } } } #[derive(Clone, PartialEq, Eq, Debug, DekuRead, DekuWrite, Serialize)] #[deku(endian = "Endian::Big")] /// Secure Execution header structure pub struct SeHdr { /// Common Secure Execution header part #[serde(flatten)] pub common: SeHdrCommon, #[serde(flatten)] #[deku(ctx = "common.version")] pub data: SeHdrVersioned, } #[derive(Clone, PartialEq, Eq, Debug, DekuRead, DekuWrite, Serialize)] #[deku(endian = "Endian::Big")] /// Plain data Secure Execution header structure pub struct SeHdrPlain { #[serde(flatten)] pub common: SeHdrCommon, #[serde(flatten)] #[deku(ctx = "common.version")] pub data: SeHdrData, } #[enum_dispatch(AeadCipherTrait, AeadDataTrait, KeyExchangeTrait)] #[derive(Clone, PartialEq, Eq, Debug, DekuRead, DekuWrite, Serialize)] #[serde(untagged)] #[deku(ctx = "_endian: Endian, version: SeHdrVersion", id = "version")] pub enum SeHdrVersioned { #[deku(id = "SeHdrVersion::V1")] SeHdrBinV1(SeHdrBinV1), } #[enum_dispatch( AeadCipherTrait, AeadPlainDataTrait, KeyExchangeTrait, KeyExchangeBuilderTrait )] #[derive(Clone, PartialEq, Eq, Debug, DekuRead, DekuWrite, Serialize)] #[serde(untagged)] #[deku(ctx = "_endian: Endian, version: SeHdrVersion", id = "version")] pub enum SeHdrData { #[deku(id = "SeHdrVersion::V1")] SeHdrDataV1(SeHdrDataV1), } impl AeadCipherBuilderTrait for SeHdrData { fn set_iv(&mut self, iv: &[u8]) -> Result<()> { match self { Self::SeHdrDataV1(data) => data.set_iv(iv), } } } #[enum_dispatch(SeHdrData)] pub trait SeHdrPubBuilderTrait { // Payload related methods fn set_components(&mut self, meta: ComponentMetadata) -> Result<()>; fn set_pcf(&mut self, pcf: &PlaintextControlFlagsV1) -> Result<()>; } #[enum_dispatch(SeHdrData)] pub trait SeHdrConfBuilderTrait { fn generate_cck(&self) -> Result; fn set_cck(&mut self, cck: Confidential>) -> Result<()>; fn set_psw(&mut self, psw: &PSW); fn set_scf(&mut self, scf: &SecretControlFlagsV1) -> Result<()>; } #[enum_dispatch(SeHdr)] #[allow(unused)] pub trait SeHdrTrait: UvDataTrait {} #[enum_dispatch(SeHdr)] #[allow(unused)] pub trait SeHdrPlainTrait: UvDataPlainTrait {} impl AeadCipherTrait for SeHdr { fn aead_key_type(&self) -> SymKeyType { self.data.aead_key_type() } fn iv(&self) -> &[u8] { self.data.iv() } fn aead_tag_size(&self) -> usize { self.data.aead_tag_size() } } impl AeadDataTrait for SeHdr { fn aad(&self) -> Result> { Ok([serialize_to_bytes(&self.common)?, self.data.aad()?].concat()) } fn data(&self) -> Vec { self.data.data() } fn tag(&self) -> Vec { self.data.tag() } } impl KeyExchangeTrait for SeHdr { fn contains_hash>(&self, hash: H) -> bool { self.data.contains_hash(hash) } fn contains>>(&self, key: K) -> Result { self.data.contains(key) } fn cust_pub_key(&mut self) -> Result> { self.data.cust_pub_key() } fn key_type(&self) -> SymKeyType { self.aead_key_type() } } impl UvDataTrait for SeHdr { type P = SeHdrPlain; } impl SeHdr { /// Seek to the start of the next Secure Execution header. /// /// # Errors /// /// This function will return an error if no Secure Execution header was /// found or the IO operation has failed. pub fn seek_sehdr(reader: &mut R, addr: Option) -> Result<()> { if let Some(addr) = addr { reader.seek(SeekFrom::Start(addr))?; } if !seek_se_hdr_start(reader)? { return Err(Error::NoSeHdrFound); } Ok(()) } /// Serializes the [`SeHdr`] to a byte vector. /// /// # Errors /// /// This function will return an error if the Secure Execution header could /// not be serialized. pub fn as_bytes(&self) -> Result> { serialize_to_bytes(self) } /// Deserializes a Secure Execution header from an I/O stream. /// /// # Errors /// /// This function will return an error if no Secure Execution header could /// be read, e.g. because no Secure Execution header was found. pub fn try_from_io(mut reader: R) -> Result where R: Read, { let common_size = size_of::(); let mut data = vec![0_u8; common_size]; reader.read_exact(&mut data)?; let (_, common) = SeHdrCommonWithSize::from_bytes((&data, 0))?; if &common.magic != SeHdrCommon::MAGIC { return Err(Error::NoSeHdrFound); } let sehs = common.sehs.try_into()?; // DoS attack prevention if sehs > 1024 * PAGESIZE { return Err(Error::InvalidSeHdr); } if sehs <= common_size { return Err(Error::InvalidSeHdr); } data.resize(sehs, 0); reader.read_exact(&mut data[common_size..])?; Self::try_from_data(&data).map_err(|_| Error::InvalidSeHdr) } } impl AeadCipherBuilderTrait for SeHdrPlain { fn set_iv(&mut self, iv: &[u8]) -> Result<()> { self.data.set_iv(iv) } } impl KeyExchangeBuilderTrait for SeHdrPlain { fn add_keyslot( &mut self, hostkey: &PKeyRef, aead_key: &SymKey, priv_key: &PKeyRef, ) -> Result<()> { self.data.add_keyslot(hostkey, aead_key, priv_key) } fn clear_keyslots(&mut self) -> Result<()> { self.data.clear_keyslots() } fn generate_private_key(&self) -> Result> { self.data.generate_private_key() } fn set_cust_public_key(&mut self, key: &PKeyRef) -> Result<()> { self.data.set_cust_public_key(key) } } #[derive(Debug, Clone)] pub struct ComponentMetadataV1 { pub ald: [u8; 64], pub pld: [u8; 64], pub tld: [u8; 64], pub nep: u64, pub key: Aes256XtsKey, } /// The `enum_dispatch` macros needs at least one local trait to be implemented. #[allow(unused)] #[enum_dispatch] trait ComponentMetadataTrait {} #[non_exhaustive] #[enum_dispatch(ComponentMetadataTrait)] #[derive(Debug)] pub enum ComponentMetadata { ComponentMetadataV1(ComponentMetadataV1), } impl KeyExchangeTrait for SeHdrPlain { fn contains>>(&self, key: K) -> Result { self.data.contains(key) } fn cust_pub_key(&mut self) -> Result> { self.data.cust_pub_key() } fn key_type(&self) -> SymKeyType { self.data.key_type() } fn contains_hash>(&self, hash: H) -> bool { self.data.contains_hash(hash) } } impl UvDataPlainTrait for SeHdrPlain { type C = SeHdr; } impl AeadPlainDataTrait for SeHdrPlain { fn aad(&self) -> Result> { let data_aad = self.data.aad()?; Ok([serialize_to_bytes(&self.common)?, data_aad].concat()) } fn data(&self) -> Result>> { self.data.data() } fn tag(&self) -> Vec { self.data.tag() } } impl AeadCipherTrait for SeHdrPlain { fn aead_key_type(&self) -> SymKeyType { self.data.aead_key_type() } fn iv(&self) -> &[u8] { self.data.iv() } fn aead_tag_size(&self) -> usize { self.data.aead_tag_size() } } #[cfg(test)] mod tests { use std::io::Cursor; use super::SeHdr; use crate::error::Error; #[test] fn test_sehdr_try_from_io() { // Invalid SeHdr as `sehs` is set to 0 assert!(matches!( SeHdr::try_from_io(Cursor::new([ 73, 66, 77, 83, 101, 99, 69, 120, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 8 ])), Err(Error::InvalidSeHdr) )); // Invalid SeHdr as the `sehs` is too large. assert!(matches!( SeHdr::try_from_io(Cursor::new([ 73, 66, 77, 83, 101, 99, 69, 120, 0, 0, 1, 0, 0, 0, 1, 255, 65, 65, 65, 65, 67, 0, 65, 17, 65, 0, 65, 65, 65, 65, 65, 65, 91, 91, 180, 91, 91, 91, 91, 91, 91, 91, 91, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 241, 241, 241, 241, 241, 91, 91, 91, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 80, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 91, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 0, 0, 0, 0, 101, 99, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 65, 65, 65, 65, 67, 0, 65, 17, 65, 0, 65, 65, 65, 65, 65, 65, 91, 91, 180, 91, 91, 91, 91, 91, 91, 91, 91, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 241, 241, 241, 241, 241, 91, 91, 91, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 80, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 91, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 73, 66, 77, 83, 101, 99, 69, 120, 0, 112, 112, 0, 1, 0, 0, 0, 0, 101, 99, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 65, 65, 65, 65, 67, 0, 65, 17, 65, 0, 65, 65, 65, 65, 65, 65, 91, 91, 180, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 112, 112, 112, 112, 112, 73, 66, 77, 83, 101, 99, 69, 120, 0, 0, 1, 0, 0, 0, 0, 48, 53, 53, 53, 53, 53, 53, 53, 91, 91, 91, 241, 241, 46, 49, 49, 0, 49, 49, 0, 0, 112, 112, 112, 91, 0, 0, 0, 0, 9, 0, 49, 50, 22, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 91, 91, 91, 91, 91, 255, 251, 0, 0, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 0, 0, 91, 0, 0, 10, 91, 91, 91, 65, 65, 65, 65 ])), Err(Error::InvalidSeHdr) )); } } s390-tools-2.38.0/rust/pvimg/src/pv_utils/se_hdr/builder.rs000066400000000000000000000254571502674226300234770ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use pv::request::Confidential; use super::{hdr_v1::SeHdrDataV1, SeHdr}; use crate::pv_utils::{ error::{Error, Result}, se_hdr::{ brb::{SeHdrCommon, SeHdrConfBuilderTrait, SeHdrData, SeHdrPubBuilderTrait}, ComponentMetadata, SeHdrPlain, SeHdrVersion, }, uvdata::UvDataPlainTrait, uvdata_builder::{ AeadCipherBuilderTrait, BuilderTrait, KeyExchangeBuilderTrait, UvDataBuilder, }, PlaintextControlFlagsV1, SecretControlFlagsV1, PSW, }; /// `SeHdrBuilder` pub type SeHdrBuilder<'a> = UvDataBuilder<'a, SeHdrPlain>; impl SeHdrBuilder<'_> { pub fn new>( version: SeHdrVersion, psw: PSW, components_meta: M, ) -> Result { let (data, aead_key, priv_key) = match version { SeHdrVersion::V1 => { let mut data = SeHdrDataV1::new( psw, components_meta .into() .try_into() .map_err(|_| Error::InvalidComponentMetadata)?, )?; let aead_key = data.generate_aead_key()?; let priv_key = data.generate_private_key()?; data.set_cust_public_key(&priv_key)?; (SeHdrData::SeHdrDataV1(data), aead_key, priv_key) } }; let common = SeHdrCommon::new(version); let hdr = SeHdrPlain { common, data }; Ok(Self { plain_data: hdr, target_keys: Vec::new(), prot_key: aead_key, expert_mode: false, priv_key, }) } pub fn with_cck(&mut self, cck: Confidential>) -> Result<&mut Self> { self.plain_data .data .set_cck(cck) .map_err(|err| Error::InvalidCCK { source: Box::new(err), })?; Ok(self) } pub fn with_components>(&mut self, meta: M) -> Result<&mut Self> { self.plain_data.data.set_components(meta.into())?; Ok(self) } pub fn with_pcf(&mut self, flags: &PlaintextControlFlagsV1) -> Result<&mut Self> { self.plain_data.data.set_pcf(flags)?; Ok(self) } pub fn with_scf(&mut self, flags: &SecretControlFlagsV1) -> Result<&mut Self> { self.plain_data.data.set_scf(flags)?; Ok(self) } } impl BuilderTrait for SeHdrBuilder<'_> { type T = SeHdr; fn build(self) -> Result { // At least one target key must be set if self.target_keys.is_empty() { return Err(Error::NoHostkey); } self.plain_data.encrypt(&self.prot_key) } } #[cfg(test)] mod tests { use std::io::Cursor; use pv::{ request::{Confidential, SymKeyType, SHA_512_HASH_LEN}, test_utils::get_test_key_and_cert, }; use super::*; use crate::pv_utils::{ se_hdr::ComponentMetadataV1, uvdata::{AeadDataTrait, AeadPlainDataTrait}, UvDataTrait, }; #[test] fn builder_test() { use pv::test_utils::get_test_key_and_cert; let (cust_key, host_key) = get_test_key_and_cert(); let host_keys = [host_key.public_key().unwrap()]; let xts_key = Confidential::new([0x3; SymKeyType::AES_256_XTS_KEY_LEN]); let xts_key2 = Confidential::new([0x3; SymKeyType::AES_256_XTS_KEY_LEN]); let mut builder = SeHdrBuilder::new( SeHdrVersion::V1, PSW { addr: 1234, mask: 5678, }, ComponentMetadata::ComponentMetadataV1(ComponentMetadataV1 { ald: [0x1; SHA_512_HASH_LEN], pld: [0x2; SHA_512_HASH_LEN], tld: [0x3; SHA_512_HASH_LEN], nep: 1, key: xts_key, }), ) .expect("should not fail"); // builder.add_comp_data(addr, tweak, )?; builder .with_components(ComponentMetadataV1 { ald: [0x1; SHA_512_HASH_LEN], pld: [0x2; SHA_512_HASH_LEN], tld: [0x3; SHA_512_HASH_LEN], nep: 1, key: xts_key2, }) .expect("should not fail"); builder .with_priv_key(&cust_key) .expect_err("Error expected as expert mode is not enabled"); builder.expert_mode = true; builder.with_priv_key(&cust_key).expect("should not fail"); // Set CCK // Too large key builder .with_cck([49; SymKeyType::AES_256_GCM_KEY_LEN - 1].to_vec().into()) .expect_err("should fail"); // Too small key builder .with_cck([49; SymKeyType::AES_256_GCM_KEY_LEN + 1].to_vec().into()) .expect_err("should fail"); builder .with_cck([49; SymKeyType::AES_256_GCM_KEY_LEN].to_vec().into()) .expect("should not fail"); // Set protection key // Too large key builder .with_aead_key(Confidential::new([50; 33].into())) .expect_err("should fail"); // Too small key builder .with_aead_key(Confidential::new([50; 31].into())) .expect_err("should fail"); builder .with_aead_key(Confidential::new([50; 32].into())) .expect("should not fail"); // Set IV // Too large IV builder.with_iv(&[51; 13]).expect_err("should fail"); // Too small IV builder.with_iv(&[51; 11]).expect_err("should fail"); builder.with_iv(&[51; 12]).expect("should not fail"); builder.add_hostkeys(&host_keys).expect("should not fail"); let prot_key = builder.prot_key().clone(); let bin = builder.build().expect("wuhu"); assert_eq!(bin.common.version, SeHdrVersion::V1); assert_eq!(bin.as_bytes().expect("should not fail").len(), 640); assert_eq!( bin.as_bytes().expect("should not fail"), [ 73, 66, 77, 83, 101, 99, 69, 120, 0, 0, 1, 0, 0, 0, 2, 128, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, 93, 52, 249, 22, 82, 219, 69, 123, 11, 32, 156, 70, 164, 145, 164, 78, 226, 177, 110, 35, 194, 216, 218, 241, 22, 103, 138, 98, 242, 76, 227, 50, 197, 153, 95, 8, 69, 107, 102, 177, 109, 213, 90, 146, 197, 7, 241, 227, 26, 247, 140, 100, 168, 46, 122, 84, 27, 21, 19, 80, 21, 242, 2, 134, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 64, 128, 88, 167, 241, 165, 195, 80, 151, 83, 58, 2, 169, 56, 121, 231, 222, 103, 186, 40, 11, 206, 131, 101, 236, 148, 178, 185, 8, 245, 137, 195, 169, 152, 216, 190, 30, 99, 7, 215, 74, 224, 26, 220, 70, 130, 95, 246, 187, 111, 160, 92, 17, 71, 207, 226, 204, 244, 162, 79, 61, 131, 61, 218, 112, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 65, 92, 37, 165, 209, 156, 56, 30, 97, 151, 51, 225, 193, 183, 251, 216, 139, 221, 28, 49, 216, 130, 213, 173, 224, 75, 151, 4, 60, 80, 16, 240, 229, 82, 102, 228, 113, 137, 5, 64, 29, 48, 138, 18, 148, 179, 136, 59, 221, 205, 98, 76, 41, 121, 59, 220, 160, 12, 56, 212, 171, 77, 85, 253, 38, 196, 235, 112, 49, 183, 94, 171, 221, 120, 96, 65, 149, 102, 55, 59, 180, 25, 143, 227, 222, 144, 3, 77, 240, 4, 217, 205, 199, 175, 123, 1, 191, 76, 78, 99, 115, 131, 5, 160, 112, 142, 117, 125, 30, 239, 7, 51, 239, 66, 173, 61, 243, 199, 20, 71, 115, 107, 113, 139, 68, 200, 219, 233, 84, 220, 108, 242, 133, 71, 91, 154, 160, 171, 4, 32, 67, 90, 107, 216, 149, 141, 210, 20, 125, 4, 39, 73, 163, 75, 1, 148, 78, 245, 135, 76, 68, 42, 164, 174, 185, 216, 29, 60, 76, 28, 232, 191, 209, 218, 134, 184, 110, 154, 227, 144, 66, 213, 145, 93, 157, 150, 61, 80, 69, 238, 6, 190, 191, 202, 172, 221, 159, 190, 62, 253, 67, 162, 142, 245, 109, 23, 26, 102, 113, 101, 23, 64, 85, 249, 255, 250, 30, 28, 136, 135, 187, 109, 118, 222 ] ); let decrypted = bin.decrypt(&prot_key).expect("BUG"); assert_eq!(bin.common, decrypted.common); assert_eq!( bin.aad().expect("should not fail"), decrypted.aad().expect("should not fail") ); assert_ne!( &bin.data(), decrypted.data().expect("should not fail").value() ); let _decrypted_hdrv1: SeHdrDataV1 = decrypted.data.try_into().expect("BUG"); } #[test] fn chain_test() { let (_, host_key) = get_test_key_and_cert(); let host_keys = [host_key.public_key().unwrap()]; let xts_key = Confidential::new([0x3; SymKeyType::AES_256_XTS_KEY_LEN]); let meta = ComponentMetadataV1 { ald: [0x1; SHA_512_HASH_LEN], pld: [0x2; SHA_512_HASH_LEN], tld: [0x3; SHA_512_HASH_LEN], nep: 3, key: xts_key, }; let cck = Confidential::new([0x42; 32].to_vec()); let mut builder = SeHdrBuilder::new( SeHdrVersion::V1, PSW { addr: 1234, mask: 5678, }, meta, ) .expect("should not fail"); let prot_key = builder.prot_key().to_owned(); builder .add_hostkeys(&host_keys) .expect("should not fail") .with_cck(cck) .expect("should not fail"); let bin = builder.build().expect("should not fail"); let reader = Cursor::new(bin.as_bytes().expect("should not fail")); let hdr = SeHdr::try_from_io(reader).unwrap(); let hdr_plain = hdr.decrypt(&prot_key).unwrap(); assert_eq!(hdr_plain.common.version, SeHdrVersion::V1); assert_eq!(hdr_plain.common.version, hdr.common.version); let _hdr_data_v1: SeHdrDataV1 = hdr_plain.data.try_into().expect("should not fail"); } } s390-tools-2.38.0/rust/pvimg/src/pv_utils/se_hdr/components.rs000066400000000000000000000000001502674226300242070ustar00rootroot00000000000000s390-tools-2.38.0/rust/pvimg/src/pv_utils/se_hdr/flags.rs000066400000000000000000000221531502674226300231330ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::{fmt::Display, marker::PhantomData, mem::size_of}; use pv::misc::{Flags, Msb0Flags64}; pub trait ControlFlagTrait: std::fmt::Debug + std::hash::Hash + Copy + Eq + Ord { fn discriminant(&self) -> u8 { assert!(size_of::() == size_of::()); unsafe { *(self as *const Self as *const u8) } } fn enabled(self) -> FlagData { FlagData::new(self, FlagState::Enabled) } fn disabled(self) -> FlagData { FlagData::new(self, FlagState::Disabled) } fn all_enabled>(flags: F) -> Vec> { flags .as_ref() .iter() .map(|flag| (*flag).enabled()) .collect() } fn all_disabled>(flags: F) -> Vec> { flags .as_ref() .iter() .map(|flag| (*flag).disabled()) .collect() } } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] enum FlagState { Enabled, Disabled, } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] pub struct FlagData { value: T, state: FlagState, } impl FlagData { const fn new(value: T, state: FlagState) -> Self { Self { value, state } } } pub trait ControlFlagsTrait: Display { type T: ControlFlagTrait; fn from_flags]>>(flags: F) -> Self; fn parse_flags]>>(&mut self, flags: F); fn is_set(&self, flag: Self::T) -> bool; fn is_unset(&self, flag: Self::T) -> bool { !self.is_set(flag) } fn no_duplicates]>>(flags: F) -> bool { let mut flags_sorted = flags.as_ref().to_vec(); flags_sorted.sort_by_key(|data| data.value); flags_sorted.dedup_by_key(|data| data.value); flags_sorted.len() == flags.as_ref().len() } fn all_set>(&self, flags: F) -> bool { flags.as_ref().iter().all(|flag| self.is_set(*flag)) } fn all_unset>(&self, flags: F) -> bool { flags.as_ref().iter().all(|flag| self.is_unset(*flag)) } } /// Bitflags as used by the Secure Execution in MSB0 ordering /// /// Wraps an u64 to set/get individual bits #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct ControlFlags { flags: Msb0Flags64, t: PhantomData, } impl ControlFlags { fn new() -> Self { Self { flags: 0x0.into(), t: PhantomData {}, } } } impl From for ControlFlags { fn from(value: u64) -> Self { Self { flags: value.into(), t: PhantomData, } } } impl From<&ControlFlags> for u64 { fn from(value: &ControlFlags) -> Self { value.flags.into() } } impl From> for u64 { fn from(value: ControlFlags) -> Self { value.flags.into() } } impl ControlFlagsTrait for ControlFlags { type T = T; fn from_flags]>>(flags: F) -> Self { let mut ret = Self::new(); ret.parse_flags(flags); ret } fn parse_flags]>>(&mut self, flags: F) { flags.as_ref().iter().for_each(|v| match v.state { FlagState::Enabled => self.flags.set_bit(v.value.discriminant()), FlagState::Disabled => self.flags.unset_bit(v.value.discriminant()), }); } fn is_set(&self, flag: T) -> bool { self.flags.is_set(flag.discriminant()) } } impl Display for ControlFlags { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let value: u64 = self.flags.into(); write!(f, "{:#018x}", value) } } #[repr(u8)] #[non_exhaustive] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] pub enum PcfV1 { /// PV guest dump support. AllowDumping = 34, /// The components are not decrypted during the image unpack. NoComponentEncryption = 35, /// DEA/TDEA PCKMO encryption function are allowed. PckmoDeaTdea = 56, /// AES PCKMO encryption function are allowed. PckmoAes = 57, /// ECC PCKMO encryption function are allowed. PckmoEcc = 58, /// HMAC PCKMO encryption function are allowed. PckmoHmac = 59, /// Backup target keys can be used. BackupTargetKeys = 62, } pub type PlaintextControlFlagsV1 = ControlFlags; impl PlaintextControlFlagsV1 { pub const PCKMO: [PcfV1; 3] = [PcfV1::PckmoAes, PcfV1::PckmoDeaTdea, PcfV1::PckmoEcc]; } impl Default for PlaintextControlFlagsV1 { fn default() -> Self { Self::from_flags(PcfV1::all_enabled(PlaintextControlFlagsV1::PCKMO)) } } impl Display for PcfV1 { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "{}", match self { Self::AllowDumping => "allow dumping", Self::NoComponentEncryption => "no component encryption", Self::PckmoDeaTdea => "DEA and TDEA PCMKO", Self::PckmoAes => "AES", Self::PckmoEcc => "ECC PCKMO", Self::PckmoHmac => "HMAC PCKMO", Self::BackupTargetKeys => "backup target keys", } ) } } impl ControlFlagTrait for PcfV1 {} #[repr(u8)] #[non_exhaustive] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] pub enum ScfV1 { /// All add-secret requests must provide an extension secret CckExtensionSecretEnforcement = 1, /// Whether CCK can be updated CckUpdateAllowed = 2, } pub type SecretControlFlagsV1 = ControlFlags; impl ControlFlagTrait for ScfV1 {} impl Default for SecretControlFlagsV1 { fn default() -> Self { Self::from_flags(ScfV1::all_enabled([])) } } #[allow(clippy::shadow_unrelated)] #[cfg(test)] mod test { use super::{ControlFlagTrait, ControlFlagsTrait, PcfV1, PlaintextControlFlagsV1}; #[test] fn test_from_flags() { let flags = PlaintextControlFlagsV1::from_flags(&[]); assert_eq!(u64::from(flags), 0_u64); let flags = PlaintextControlFlagsV1::from_flags([PcfV1::AllowDumping.enabled()]); assert_eq!(u64::from(&flags), 536870912); assert!(flags.is_set(PcfV1::AllowDumping)); let flags = PlaintextControlFlagsV1::from_flags([ PcfV1::AllowDumping.enabled(), PcfV1::AllowDumping.disabled(), ]); assert_eq!(u64::from(flags), 0); let flags = PlaintextControlFlagsV1::from_flags([ PcfV1::AllowDumping.disabled(), PcfV1::AllowDumping.enabled(), ]); assert_eq!(u64::from(&flags), 536870912); let flags = PlaintextControlFlagsV1::from_flags([ PcfV1::AllowDumping.enabled(), PcfV1::BackupTargetKeys.enabled(), ]); assert_eq!(u64::from(&flags), 536870914); } #[test] fn test_all_set_unset() { let flags = PlaintextControlFlagsV1::from_flags([ PcfV1::AllowDumping.enabled(), PcfV1::BackupTargetKeys.enabled(), ]); assert!(flags.all_set([PcfV1::AllowDumping, PcfV1::BackupTargetKeys])); assert!(!flags.all_set([PcfV1::NoComponentEncryption, PcfV1::BackupTargetKeys])); assert!(!flags.all_unset([PcfV1::NoComponentEncryption, PcfV1::BackupTargetKeys])); assert!(flags.all_unset([PcfV1::NoComponentEncryption, PcfV1::PckmoHmac])); } #[test] fn test_display() { let flags = PlaintextControlFlagsV1::from_flags([PcfV1::NoComponentEncryption.enabled()]); assert_eq!("0x0000000010000000", format!("{}", flags)); let flags = PlaintextControlFlagsV1::from_flags([ PcfV1::AllowDumping.enabled(), PcfV1::BackupTargetKeys.enabled(), PcfV1::NoComponentEncryption.enabled(), PcfV1::PckmoAes.enabled(), PcfV1::PckmoDeaTdea.enabled(), PcfV1::PckmoEcc.enabled(), PcfV1::PckmoHmac.enabled(), ]); assert_eq!("0x00000000300000f2", format!("{}", flags)); } #[test] fn test_no_duplicates() { let flags: Vec<_> = [ PcfV1::all_disabled([PcfV1::PckmoAes, PcfV1::PckmoDeaTdea, PcfV1::PckmoEcc]), PcfV1::all_enabled([PcfV1::PckmoAes, PcfV1::PckmoDeaTdea, PcfV1::PckmoEcc]), ] .into_iter() .flatten() .collect(); assert!(!PlaintextControlFlagsV1::no_duplicates(flags)); let flags: Vec<_> = [ PcfV1::all_disabled([PcfV1::PckmoAes]), PcfV1::all_enabled([PcfV1::PckmoDeaTdea, PcfV1::PckmoEcc]), ] .into_iter() .flatten() .collect(); assert!(PlaintextControlFlagsV1::no_duplicates(flags)); let flags: Vec<_> = std::iter::once(PcfV1::all_disabled([PcfV1::PckmoAes, PcfV1::PckmoAes])) .flatten() .collect(); assert!(!PlaintextControlFlagsV1::no_duplicates(flags)); } } s390-tools-2.38.0/rust/pvimg/src/pv_utils/se_hdr/hdr_v1.rs000066400000000000000000000467121502674226300232310ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::mem::{size_of, size_of_val}; use deku::{ctx::Endian, prelude::*}; use openssl::{ nid::Nid, pkey::{PKeyRef, Public}, }; use pv::request::{ gen_ec_key, openssl::pkey::{PKey, Private}, random_array, Aes256XtsKey, Confidential, EcPubKeyCoord, Encrypt, Keyslot, SymKey, SymKeyType, Zeroize, SHA_512_HASH_LEN, }; use serde::{Serialize, Serializer}; use super::keys::phkh_v1; use crate::{ error::Error, misc::PAGESIZE, pv_utils::{ error::Result, se_hdr::{ brb::{ ComponentMetadata, ComponentMetadataV1, SeHdrCommon, SeHdrConfBuilderTrait, SeHdrPlainTrait, SeHdrPubBuilderTrait, SeHdrTrait, }, keys::{BinaryKeySlotV1, EcPubKeyCoordV1}, }, serializing::{ bytesize, bytesize_confidential, confidential_read_slice, confidential_write_slice, ser_hex, ser_hex_confidential, ser_lower_hex, serialize_to_bytes, }, try_copy_slice_to_array, uvdata::{ AeadCipherTrait, AeadDataTrait, AeadPlainDataTrait, KeyExchangeTrait, UvDataPlainTrait, UvDataTrait, }, uvdata_builder::{AeadCipherBuilderTrait, KeyExchangeBuilderTrait}, PlaintextControlFlagsV1, SecretControlFlagsV1, PSW, }, }; #[derive(Debug)] struct HdrSizesV1 { pub phs: u64, pub sea: u64, } #[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize)] #[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")] pub struct SeHdrAadV1 { #[deku(assert = "*sehs <= SeHdrDataV1::MAX_SIZE.try_into().unwrap()")] pub sehs: u32, #[serde(serialize_with = "ser_hex")] pub iv: [u8; SymKeyType::AES_256_GCM_IV_LEN], res1: u32, #[deku(assert = "*nks <= (*sehs).into()", update = "self.keyslots.len()")] pub nks: u64, #[deku(assert = "*sea <= (*sehs).into()")] pub sea: u64, pub nep: u64, #[serde(serialize_with = "ser_lower_hex")] pub pcf: u64, pub cust_pub_key: EcPubKeyCoordV1, #[serde(serialize_with = "ser_hex")] pub pld: [u8; SHA_512_HASH_LEN], #[serde(serialize_with = "ser_hex")] pub ald: [u8; SHA_512_HASH_LEN], #[serde(serialize_with = "ser_hex")] pub tld: [u8; SHA_512_HASH_LEN], #[deku(count = "nks")] pub keyslots: Vec, } impl SeHdrAadV1 { const KEY_TYPE: SymKeyType = SymKeyType::Aes256Gcm; } impl KeyExchangeTrait for SeHdrAadV1 { fn contains>>(&self, key: K) -> Result { let phkh = phkh_v1(key)?; Ok(self.contains_hash(phkh)) } fn cust_pub_key(&mut self) -> Result> { self.cust_pub_key.clone().try_into() } fn key_type(&self) -> SymKeyType { Self::KEY_TYPE } fn contains_hash>(&self, hash: H) -> bool { for slot in &self.keyslots { if hash.as_ref() != slot.phkh { continue; } return true; } false } } #[derive(PartialEq, Eq, Debug, Clone, DekuRead, DekuWrite, Serialize)] #[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")] pub struct SeHdrConfV1 { #[serde(serialize_with = "ser_hex_confidential")] #[deku( reader = "confidential_read_slice(deku::reader, endian)", writer = "confidential_write_slice(cck, deku::writer, endian)" )] cck: Confidential<[u8; 32]>, #[serde(serialize_with = "ser_hex_confidential")] #[deku( reader = "confidential_read_slice(deku::reader, endian)", writer = "confidential_write_slice(xts, deku::writer, endian)" )] xts: Aes256XtsKey, psw: PSW, #[serde(serialize_with = "ser_lower_hex")] pub scf: u64, #[deku(assert_eq = "0")] noi: u32, res2: u32, #[deku(count = "noi")] opt_items: Vec, } impl Zeroize for SeHdrConfV1 { fn zeroize(&mut self) { self.cck.zeroize(); self.xts.zeroize(); self.psw.zeroize(); self.scf.zeroize(); self.noi.zeroize(); self.res2.zeroize(); self.opt_items.zeroize(); } } #[derive(Default, PartialEq, Eq, Debug, Clone, DekuRead, DekuWrite, Serialize)] #[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")] pub struct SeHdrTagV1 { #[serde(serialize_with = "ser_hex")] tag: [u8; SymKeyType::AES_256_GCM_TAG_LEN], } fn ser_confidential_confv1( encrypted: &Confidential, ser: S, ) -> std::result::Result { encrypted.value().serialize(ser) } /// Secure Execution Header definition #[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize)] #[deku(endian = "big")] pub struct SeHdrDataV1 { #[serde(flatten)] pub aad: SeHdrAadV1, #[serde(flatten, serialize_with = "ser_confidential_confv1")] #[deku( reader = "confidential_read_sehdrconf_v1(deku::reader)", writer = "confidential_write_sehdrconf_v1(data, deku::writer)" )] pub data: Confidential, #[serde(flatten)] tag: SeHdrTagV1, } /// Reads from a `reader` and creates a confidential `SeHdrConfV1`. /// /// # Errors /// /// This function will return an error if there was an I/O error or the /// `SeHdrConfV1` could not be constructed. fn confidential_read_sehdrconf_v1( reader: &mut Reader, ) -> Result, DekuError> where R: std::io::Read + std::io::Seek, { Ok(Confidential::new(SeHdrConfV1::from_reader_with_ctx( reader, (), )?)) } /// Writes a `Confidential` into this `writer`. /// /// # Errors /// /// This function will return an error if there was an I/O error. fn confidential_write_sehdrconf_v1( value: &Confidential, writer: &mut Writer, ) -> Result<(), DekuError> where W: std::io::Write + std::io::Seek, { value.value().to_writer(writer, ()) } impl SeHdrDataV1 { const MAX_SIZE: usize = 2 * PAGESIZE; const PCF_DEFAULT: u64 = 0x0; const SCF_DEFAULT: u64 = 0x0; /// Creates a new `SeHdrDataV1`. It initializes the CCK and IV with random /// data. /// /// # Errors /// /// This function will return an error if there was not enough entropy to /// create the random data or another error has occurred. pub fn new(psw: PSW, components: ComponentMetadataV1) -> Result { // Safety: The CCK is also 32 bytes large. let cck = SymKey::random(SymKeyType::Aes256Gcm)?.try_into().unwrap(); let mut ret = Self { aad: SeHdrAadV1 { sehs: 0, pcf: Self::PCF_DEFAULT, ald: components.ald, pld: components.pld, tld: components.tld, nep: components.nep, sea: 0, iv: random_array()?, res1: 0, nks: 0, cust_pub_key: EcPubKeyCoordV1 { coord: [0_u8; 160] }, keyslots: vec![], }, data: SeHdrConfV1 { cck, scf: Self::SCF_DEFAULT, psw, xts: components.key, noi: 0, res2: 0, opt_items: vec![], } .into(), tag: SeHdrTagV1::default(), }; let hdr_size = ret.size()?; let phs = hdr_size.phs.try_into()?; if phs > Self::MAX_SIZE { return Err(Error::InvalidSeHdrTooLarge { given: phs, maximum: Self::MAX_SIZE, }); } ret.aad.sehs = phs.try_into()?; ret.aad.sea = hdr_size.sea; Ok(ret) } fn size(&self) -> Result { let sea = bytesize_confidential(&self.data)?; let mut phs = bytesize(&self.aad)? .checked_add(size_of::()) .ok_or(Error::UnexpectedOverflow)?; phs = phs .checked_add(bytesize(&self.tag)?) .ok_or(Error::UnexpectedOverflow)?; phs = phs.checked_add(sea).ok_or(Error::UnexpectedOverflow)?; Ok(HdrSizesV1 { sea: sea.try_into()?, phs: phs.try_into()?, }) } /// Return the expected size of an constructed `SeHdrDataV1` with `n` key /// slots. /// /// # Errors /// /// This function will return an error if there was an arithmetic overflow /// or. pub fn expected_size(nks: usize) -> Result { let cck = [0x0; 32].into(); let hdr = Self { aad: SeHdrAadV1 { sehs: 0, pcf: Self::PCF_DEFAULT, ald: [0x0; SHA_512_HASH_LEN], pld: [0x0; SHA_512_HASH_LEN], tld: [0x0; SHA_512_HASH_LEN], nep: 0, sea: 0, iv: [0x0_u8; SymKeyType::AES_256_GCM_IV_LEN], res1: 0, nks: 0, cust_pub_key: EcPubKeyCoordV1 { coord: [0_u8; 160] }, keyslots: vec![], }, data: SeHdrConfV1 { cck, scf: Self::SCF_DEFAULT, psw: PSW { mask: 0, addr: 0 }, xts: [0x0; SymKeyType::AES_256_XTS_KEY_LEN].into(), noi: 0, res2: 0, opt_items: vec![], } .into(), tag: SeHdrTagV1::default(), }; let hdr_size: usize = hdr.size()?.phs.try_into().unwrap(); hdr_size .checked_add( size_of::() .checked_mul(nks) .ok_or(Error::UnexpectedOverflow)?, ) .ok_or(Error::UnexpectedOverflow) } } impl UvDataPlainTrait for SeHdrDataV1 { type C = SeHdrBinV1; } impl SeHdrPlainTrait for SeHdrDataV1 {} impl KeyExchangeBuilderTrait for SeHdrDataV1 { fn add_keyslot( &mut self, hostkey: &PKeyRef, aead_key: &SymKey, priv_key: &PKeyRef, ) -> Result<()> { let keyslot = Keyslot::new(hostkey.to_owned()); let keyslot_bin = keyslot.encrypt(aead_key.value(), priv_key)?.try_into()?; let keyslot_bin_size = u32::try_from(size_of_val(&keyslot_bin)).unwrap(); self.aad.keyslots.push(keyslot_bin); self.aad.nks = self .aad .nks .checked_add(1) .ok_or(Error::UnexpectedOverflow)?; self.aad.sehs = self .aad .sehs .checked_add(keyslot_bin_size) .ok_or(Error::UnexpectedOverflow)?; Ok(()) } fn generate_private_key(&self) -> Result> { Ok(gen_ec_key(Nid::SECP521R1)?) } fn set_cust_public_key(&mut self, key: &PKeyRef) -> Result<()> { self.aad.cust_pub_key = TryInto::::try_into(key)?.into(); Ok(()) } fn clear_keyslots(&mut self) -> Result<()> { let old_nks: usize = self.aad.nks.try_into().unwrap(); let keyslot_bin_size = size_of::(); self.aad.keyslots.clear(); self.aad.nks = 0; self.aad.sehs -= u32::try_from( old_nks .checked_mul(keyslot_bin_size) .ok_or(Error::UnexpectedOverflow)?, ) .unwrap(); Ok(()) } } impl KeyExchangeTrait for SeHdrDataV1 { fn contains>>(&self, key: K) -> Result { self.aad.contains(key) } fn cust_pub_key(&mut self) -> Result> { self.aad.cust_pub_key() } fn key_type(&self) -> SymKeyType { self.aad.key_type() } fn contains_hash>(&self, hash: H) -> bool { self.aad.contains_hash(hash) } } impl SeHdrConfBuilderTrait for SeHdrDataV1 { fn set_psw(&mut self, psw: &PSW) { self.data.value_mut().psw = psw.clone(); } fn set_scf(&mut self, scf: &SecretControlFlagsV1) -> Result<()> { self.data.value_mut().scf = scf.into(); Ok(()) } fn set_cck(&mut self, cck: Confidential>) -> Result<()> { self.data.value_mut().cck = cck.try_into()?; Ok(()) } fn generate_cck(&self) -> Result { Ok(SymKey::random(SymKeyType::Aes256Gcm)?) } } impl SeHdrPubBuilderTrait for SeHdrDataV1 { fn set_pcf(&mut self, pcf: &PlaintextControlFlagsV1) -> Result<()> { self.aad.pcf = pcf.into(); Ok(()) } fn set_components(&mut self, meta: ComponentMetadata) -> Result<()> { let ComponentMetadataV1 { ald, pld, tld, nep, key, }: ComponentMetadataV1 = meta .try_into() .map_err(|_| Error::InvalidComponentMetadata)?; self.data.value_mut().xts = key; self.aad.ald = ald; self.aad.pld = pld; self.aad.tld = tld; self.aad.nep = nep; Ok(()) } } #[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize)] #[deku(endian = "big")] pub struct SeHdrBinV1 { #[serde(flatten)] pub aad: SeHdrAadV1, #[serde(serialize_with = "ser_hex")] #[deku(bytes_read = "aad.sea")] pub data: Vec, #[serde(flatten)] pub tag: SeHdrTagV1, } impl SeHdrBinV1 { pub fn new(d: &[u8]) -> Result { Self::try_from_data(d) } pub(crate) fn try_from_data(data: &[u8]) -> Result { let (_rest, val) = Self::from_bytes((data, 0))?; Ok(val) } } impl UvDataTrait for SeHdrBinV1 { type P = SeHdrDataV1; } impl SeHdrTrait for SeHdrBinV1 {} impl AeadCipherTrait for SeHdrBinV1 { fn aead_key_type(&self) -> SymKeyType { self.key_type() } fn iv(&self) -> &[u8] { &self.aad.iv } fn aead_tag_size(&self) -> usize { SymKeyType::AES_256_GCM_TAG_LEN } } impl AeadCipherBuilderTrait for SeHdrDataV1 { fn set_iv(&mut self, iv: &[u8]) -> Result<()> { self.aad.iv = try_copy_slice_to_array(iv)?; Ok(()) } } impl KeyExchangeTrait for SeHdrBinV1 { fn contains>>(&self, key: K) -> Result { self.aad.contains(key) } fn cust_pub_key(&mut self) -> Result> { self.aad.cust_pub_key() } fn key_type(&self) -> SymKeyType { self.aad.key_type() } fn contains_hash>(&self, hash: H) -> bool { self.aad.contains_hash(hash) } } impl AeadDataTrait for SeHdrBinV1 { fn aad(&self) -> Result> { serialize_to_bytes(&self.aad) } fn data(&self) -> Vec { self.data.to_owned() } fn tag(&self) -> Vec { serialize_to_bytes(&self.tag).unwrap() } } impl AeadPlainDataTrait for SeHdrDataV1 { fn aad(&self) -> Result> { serialize_to_bytes(&self.aad) } fn data(&self) -> Result>> { Ok(serialize_to_bytes(self.data.value())?.into()) } fn tag(&self) -> Vec { serialize_to_bytes(&self.tag).unwrap() } } impl AeadCipherTrait for SeHdrDataV1 { fn aead_key_type(&self) -> SymKeyType { self.aad.key_type() } fn iv(&self) -> &[u8] { &self.aad.iv } fn aead_tag_size(&self) -> usize { SymKeyType::AES_256_GCM_TAG_LEN } } #[cfg(test)] mod tests { use std::io::Cursor; use pv::test_utils::get_test_key_and_cert; use super::*; use crate::pv_utils::{BuilderTrait, SeHdr, SeHdrBuilder, SeHdrVersion}; #[test] fn iv_keys_auto_generation_test() { let (_, host_key) = get_test_key_and_cert(); let host_keys = [host_key.public_key().unwrap()]; let mut builder = SeHdrBuilder::new( SeHdrVersion::V1, PSW { addr: 1234, mask: 5678, }, ComponentMetadataV1 { ald: [0x1; SHA_512_HASH_LEN], pld: [0x2; SHA_512_HASH_LEN], tld: [0x3; SHA_512_HASH_LEN], nep: 1, key: Confidential::new([0x0_u8; SymKeyType::AES_256_XTS_KEY_LEN]), }, ) .expect("should not fail"); builder.add_hostkeys(&host_keys).expect("should not fail"); } #[test] fn chain_test() { let (_, host_key) = get_test_key_and_cert(); let host_keys = [host_key.public_key().unwrap()]; let xts_key = Confidential::new([0x3; SymKeyType::AES_256_XTS_KEY_LEN]); let meta = ComponentMetadataV1 { ald: [0x1; SHA_512_HASH_LEN], pld: [0x2; SHA_512_HASH_LEN], tld: [0x3; SHA_512_HASH_LEN], nep: 3, key: xts_key, }; let cck: Confidential> = [0x42; 32].to_vec().into(); let psw = PSW { addr: 1234, mask: 5678, }; let mut builder = SeHdrBuilder::new(SeHdrVersion::V1, psw.clone(), meta.clone()) .expect("should not fail"); builder .add_hostkeys(&host_keys) .expect("should not fail") .with_components(meta.clone()) .expect("should not fail") .with_cck(cck.clone()) .expect("should not fail"); let prot_key = builder.prot_key().to_owned(); let bin = builder.build().expect("should not fail"); let reader = Cursor::new(bin.as_bytes().expect("should not fail")); let hdr = SeHdr::try_from_io(reader).unwrap(); let hdr_plain = hdr.decrypt(&prot_key).unwrap(); assert_eq!(hdr_plain.common.version, SeHdrVersion::V1); let hdr_data_v1: SeHdrDataV1 = hdr_plain.data.try_into().expect("should not fail"); assert_eq!(meta.ald, hdr_data_v1.aad.ald); assert_eq!(meta.pld, hdr_data_v1.aad.pld); assert_eq!(meta.tld, hdr_data_v1.aad.tld); assert_eq!(psw, hdr_data_v1.data.value().psw); assert_eq!(cck.value(), hdr_data_v1.data.value().cck.value()); } #[test] fn max_size_sehdr_test() { const MAX_HOST_KEYS: usize = 95; let (_, host_key) = get_test_key_and_cert(); let pub_key = host_key.public_key().unwrap(); let host_keys_max: Vec<_> = (0..MAX_HOST_KEYS).map(|_| pub_key.clone()).collect(); let too_many_host_keys: Vec<_> = (0..MAX_HOST_KEYS + 1).map(|_| pub_key.clone()).collect(); let xts_key = Confidential::new([0x3; SymKeyType::AES_256_XTS_KEY_LEN]); let meta = ComponentMetadataV1 { ald: [0x1; SHA_512_HASH_LEN], pld: [0x2; SHA_512_HASH_LEN], tld: [0x3; SHA_512_HASH_LEN], nep: 3, key: xts_key, }; let psw = PSW { addr: 1234, mask: 5678, }; let mut builder = SeHdrBuilder::new(SeHdrVersion::V1, psw.clone(), meta.clone()) .expect("should not fail"); builder .add_hostkeys(&host_keys_max) .expect("should not fail") .with_components(meta.clone()) .expect("should not fail"); let bin = builder.build().expect("should not fail"); assert_eq!(bin.common.version, SeHdrVersion::V1); let hdr_v1: SeHdrBinV1 = bin.data.try_into().expect("should not fail"); assert_eq!(hdr_v1.aad.sehs, 8160); let mut builder = SeHdrBuilder::new(SeHdrVersion::V1, psw.clone(), meta.clone()) .expect("should not fail"); builder .add_hostkeys(&too_many_host_keys) .expect("should not fail") .with_components(meta) .expect("should not fail"); assert!(matches!(builder.build(), Err(Error::InvalidSeHdr))); } } s390-tools-2.38.0/rust/pvimg/src/pv_utils/se_hdr/keys.rs000066400000000000000000000057501502674226300230160ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::mem::size_of; use deku::{ctx::Endian, DekuRead, DekuWrite}; use openssl::{ hash::{hash, MessageDigest}, pkey::{PKey, PKeyRef, Public}, }; use pv::{request::EcPubKeyCoord, static_assert}; use serde::Serialize; use crate::{ error::{Error, Result}, pv_utils::{serializing::ser_hex, try_copy_slice_to_array}, }; /// Try to hash the public EC key. /// /// # Errors /// /// This function will return an error if OpenSSL could not hash the key. pub fn phkh_v1>>(key: T) -> Result<[u8; 32]> { let phk: EcPubKeyCoord = key.as_ref().try_into()?; let binding = hash(MessageDigest::sha256(), phk.as_ref())?; try_copy_slice_to_array(&binding) } #[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize)] #[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")] pub struct EcPubKeyCoordV1 { #[serde(serialize_with = "ser_hex")] pub coord: [u8; 160], } #[allow(clippy::fallible_impl_from)] impl From for EcPubKeyCoordV1 { fn from(value: EcPubKeyCoord) -> Self { // SAFETY: `EcPubKeyCoord` has the same struct definition as // `EcPubKeyCooardV1`. let coord = try_copy_slice_to_array(value.as_ref()).unwrap(); Self { coord } } } #[allow(clippy::fallible_impl_from)] impl From for EcPubKeyCoord { fn from(value: EcPubKeyCoordV1) -> Self { // SAFETY: `EcPubKeyCoord` has the same struct definition as // `EcPubKeyCooardV1`. let coord = try_copy_slice_to_array(&value.coord).unwrap(); // SAFETY: This call is safe because we do not expect, that // EcPubKeyCoord is always a valid EC pub key. unsafe { Self::from_data(coord) } } } impl TryFrom for PKey { type Error = Error; fn try_from(value: EcPubKeyCoordV1) -> Result { >::into(value) .try_into() .map_err(Error::Crypto) } } #[repr(C)] #[derive(Default, Debug, PartialEq, Eq, Clone, DekuRead, DekuWrite, Serialize)] #[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")] /// Binary key slot v1 pub struct BinaryKeySlotV1 { #[serde(serialize_with = "ser_hex")] /// Public host key hash pub phkh: [u8; 32], /// Wrapper key #[serde(serialize_with = "ser_hex")] pub wrpk: [u8; 32], /// Tag #[serde(serialize_with = "ser_hex")] pub kst: [u8; 16], } static_assert!(size_of::() == 80); impl TryFrom> for BinaryKeySlotV1 { type Error = Error; fn try_from(value: Vec) -> Result { let data: [u8; 80] = try_copy_slice_to_array(&value)?; let bin = Self { phkh: data[..32].try_into().unwrap(), wrpk: data[32..64].try_into().unwrap(), kst: data[64..].try_into().unwrap(), }; Ok(bin) } } s390-tools-2.38.0/rust/pvimg/src/pv_utils/secured_comp.rs000066400000000000000000000650101502674226300232420ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::{ fmt::{Debug, Formatter}, io::{Read, Write}, rc::Rc, }; use log::debug; use openssl::{ bn::BigNum, cipher::{Cipher, CipherRef}, cipher_ctx::{CipherCtx, CipherCtxRef}, hash::{Hasher, MessageDigest}, nid::Nid, }; use pv::request::{Confidential, SymKey, SymKeyType}; use super::{try_copy_slice_to_array, Layout}; use crate::pv_utils::{ error::{Error, PvError, Result}, se_hdr::{ComponentMetadata, ComponentMetadataV1}, Interval, }; #[allow(unused)] #[derive(Copy, Clone, Eq, PartialEq)] pub enum Mode { Encrypt, Decrypt, Padding, } fn update_ald_digest(hasher: &mut Hasher, interval: &Interval, chunk_size: usize) -> Result { let mut num_chunks = 0; for addr in (interval.start..interval.stop).step_by(chunk_size) { let addr_be_data = addr.to_be_bytes(); hasher.update(&addr_be_data)?; num_chunks += 1; } Ok(num_chunks) } pub struct PrepareSecuredComponentArgs<'a> { pub(crate) addr: u64, pub(crate) cipher: &'a CipherRef, pub(crate) mode: Mode, pub(crate) key: &'a [u8], pub(crate) iv: &'a [u8], pub(crate) chunk_size: usize, } pub struct MetadataArgs<'a> { pub(crate) content_hasher: Option<&'a mut Hasher>, pub(crate) tweak_hasher: Option<&'a mut Hasher>, pub(crate) address_hasher: Option<&'a mut Hasher>, pub(crate) num_chunks: Option<&'a mut usize>, pub(crate) max_component_size: Option, pub(crate) input_size: usize, pub(crate) padded_input_size: usize, pub(crate) output_size: usize, } /// This functions tries to read the exact number of bytes required to fill /// `buf`. /// /// # Errors /// /// If this function encounters an EOF before completely filling the buffer, it /// returns an error of the kind [`std::io::ErrorKind::UnexpectedEof`]. The /// contents of `buf` are unspecfied in this case. fn own_read_exact(reader: &mut R, mut buf: &mut [u8]) -> std::io::Result { let mut data_read = 0; while !buf.is_empty() { match reader.read(buf) { Ok(0) => break, Ok(n) => { buf = &mut buf[n..]; data_read += n; } Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => {} Err(e) => return Err(e), } } Ok(data_read) } /// This function is used for prepare a "secured component" used in the Secure Execution /// context. It adds padding if needed, encrypts the components and calculates /// the PLD and TLD. pub fn prepare_component( crypto_args: &PrepareSecuredComponentArgs, src: &mut R, dst: &mut W, mut opt_data: Option<&mut MetadataArgs>, ) -> Result<()> { let PrepareSecuredComponentArgs { addr, cipher, mode, key, iv, chunk_size, } = *crypto_args; let mut chunk_data = vec![0_u8; chunk_size]; let mut output_data = vec![0_u8; chunk_data.len()]; let mut chunks_count: usize = 0; let mut count; let mut tweak_num = BigNum::from_slice(iv)?; let mut ctx = if matches!(mode, Mode::Decrypt) || matches!(mode, Mode::Encrypt) { Some(CipherCtx::new()?) } else { None }; let init_func = match mode { // The value for Mode::Padding will never be actually used. Mode::Encrypt | Mode::Padding => CipherCtxRef::encrypt_init, Mode::Decrypt => CipherCtxRef::decrypt_init, }; if let Some(ref mut ctx) = &mut ctx { assert!(chunk_size % cipher.block_size() == 0, "Invalid chunk size"); init_func(ctx, Some(cipher), None, None)?; if key.len() != cipher.key_length() { debug!("Setting new key length: {}", key.len()); ctx.set_key_length(key.len())?; } if iv.len() != cipher.iv_length() { debug!("Setting new IV length: {}", iv.len()); ctx.set_iv_length(iv.len())?; } // Set key init_func(ctx, None, Some(key), None)?; }; loop { let new_tweak = tweak_num.to_vec_padded(iv.len().try_into()?)?; // Set a new tweak if let Some(ref mut ctx) = &mut ctx { init_func(ctx, None, None, Some(&new_tweak))?; } // Read input data let read_count = own_read_exact(src, &mut chunk_data)?; // EOF has been reached if read_count == 0 { // A chunk was read before and EOF was reached => it was not an // empty file and therefore break the loop. if chunks_count != 0 { break; } } let input_slice = &chunk_data[..]; // Encrypt if let Some(ref mut ctx) = ctx { count = ctx.cipher_update(input_slice, Some(&mut output_data))?; } else { output_data.copy_from_slice(input_slice); count = input_slice.len(); } // Write output data and check if it fits in the image layout let output_slice = &output_data[..count]; if let Some(ops) = opt_data.as_mut() { let output_size = ops .output_size .checked_add(output_slice.len()) .ok_or(Error::UnexpectedOverflow)?; if let Some(max_output_size) = ops.max_component_size { if output_size > max_output_size { return Err(Error::PreparedComponentTooLarge { output_size, max_output_size, }); } } ops.output_size = output_size; // Calculate input size ops.input_size = ops .input_size .checked_add(read_count) .ok_or(Error::UnexpectedOverflow)?; // Calculate padded input size ops.padded_input_size = ops .padded_input_size .checked_add(input_slice.len()) .ok_or(Error::UnexpectedOverflow)?; // Calculate PLD if let Some(ref mut hasher) = ops.content_hasher { hasher.update(output_slice)?; } // Calculate TLD if let Some(ref mut hasher) = ops.tweak_hasher { hasher.update(&new_tweak)?; } } dst.write_all(output_slice)?; chunks_count = chunks_count .checked_add(1) .ok_or(Error::UnexpectedOverflow)?; // Prepare for the next chunk: // * Calculate new tweak // * Reset chunk data to zeroes tweak_num.add_word(chunk_size.try_into()?)?; chunk_data.fill(0x0); } if let Some(ref mut ctx) = &mut ctx { count = ctx.cipher_final(&mut output_data)?; } else { count = 0; } let output_slice = &output_data[..count]; dst.write_all(output_slice)?; if let Some(ops) = opt_data.as_mut() { // Calculate output size let output_size = ops .output_size .checked_add(output_slice.len()) .ok_or(Error::UnexpectedOverflow)?; if let Some(max_output_size) = ops.max_component_size { if output_size > max_output_size { return Err(Error::PreparedComponentTooLarge { output_size, max_output_size, }); } } ops.output_size = output_size; // Calculate PLD if let Some(ref mut hasher) = ops.content_hasher { hasher.update(output_slice)?; } // Calculate ALD if let Some(ref mut hasher) = ops.address_hasher { update_ald_digest( hasher, &Interval::new_with_size(addr, output_size.try_into()?)?, chunk_size, )?; } // Update the total number of prepared chunks. if let Some(ref mut num_chunks) = ops.num_chunks { **num_chunks = num_chunks .checked_add(chunks_count) .ok_or(Error::UnexpectedOverflow)?; } } Ok(()) } /// A trait for dealing with (secured) components. pub trait ComponentTrait: Debug + Read { /// Returns if the component is used in secure mode. fn secure_mode(&self) -> bool; /// Returns the component type. fn kind(&self) -> T; } /// Struct for representing a secured component that is going to be unpacked by /// the Ultravisor. #[derive(Debug, PartialEq, Eq)] pub struct SecuredComponent { /// Source of the prepared (encrypted) component pub src: Rc, /// Size of the unprepared (unencrypted) component. pub original_size: usize, /// Tweak or IV used for the (de/en)cryption of the component. tweak_or_iv: Vec, } impl SecuredComponent { pub fn tweak(&self) -> &[u8] { self.tweak_or_iv.as_slice() } } /// A builder that is used to prepare a [`SecuredComponent`]. pub struct SecuredComponentBuilder { /// Expert mode, in example the secured components encryption key and be set /// manually. By default disabled. expert_mode: bool, /// Chunk size, currently only 4096 bytes is supported by the Ultravisor. chunk_size: usize, /// Determines whether a secured component needs to be encrypted. encrypt: bool, /// Determines which cipher will be used for the encryption. cipher: &'static CipherRef, /// Key used for the encryption of the components. comp_key: SymKey, // Cached values /// Number of chunks already prepared by this [`Self`]. num_chunks: usize, /// ALD hasher ald_hasher: Hasher, /// PLD hasher pld_hasher: Hasher, /// TLD hasher tld_hasher: Hasher, /// Finalized image? finalized: bool, } // Needs to be implemented manually as `CipherRef` and `Hasher` do not implement // [`Debug`]. impl Debug for SecuredComponentBuilder { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_struct("SecuredComponentBuilder") .field("chunk_size", &self.chunk_size) .field("cipher", &self.cipher.nid().long_name()?) .field("comp_key", &self.comp_key) .field("encrypt", &self.encrypt) .field("expert_mode", &self.expert_mode) .field("num_chunks", &self.num_chunks) .finish() } } impl SecuredComponentBuilder { /// Values used for the first (and current) Ultravisor implementation. const CHUNK_SIZE_V1: usize = 4096; const CIPHER_V1: SymKeyType = SymKeyType::Aes256Xts; pub const COMPONENT_ALIGNMENT_V1: u64 = 4096; const DIGEST_V1: Nid = Nid::SHA512; fn new( encrypt: bool, key_type: SymKeyType, digest_nid: Nid, chunk_size: usize, ) -> Result { let digest = MessageDigest::from_nid(digest_nid).ok_or(Error::UnsupportMessageDigest)?; let nid = key_type.into(); let cipher = Cipher::from_nid(nid).ok_or(PvError::UnsupportedCipher(nid))?; let key = SymKey::random(key_type)?; Ok(Self { expert_mode: false, comp_key: key, cipher, encrypt, num_chunks: 0, chunk_size, ald_hasher: Hasher::new(digest)?, pld_hasher: Hasher::new(digest)?, tld_hasher: Hasher::new(digest)?, finalized: false, }) } /// Creates a new [`Self`] that can be used for the preparation of V1 /// secured components. AES256-XTS is used for the components encryption, /// SHA-512 for the ALD, PLD and TLD. The components must be aligned and a /// multiple of 4096 bytes. /// /// # Errors /// /// This function will return an error if the cipher or digest algorithm is /// not supported or no random key could be generated. pub fn new_v1(encryption: bool) -> Result { Self::new( encryption, Self::CIPHER_V1, Self::DIGEST_V1, Self::CHUNK_SIZE_V1, ) } /// Activate the expert mode. For example, it's then allowed to change /// security related settings like setting the encryption keys manually. pub fn i_know_what_i_am_doing(&mut self) { self.expert_mode = true; } /// Sets the components key. This requires the expert mode to be active and /// there is also the restriction, that it cannot be changed after the first /// secured component was prepared. /// /// * `key_data` - Data that is used as the new components key. /// /// # Errors /// /// This function will return an error if: /// * the expert mode is not active (see [`Self::i_know_what_i_am_doing`]) /// * or a first component was already prepared or finalized /// * the key could not created by using `key_data`. pub fn set_components_key(&mut self, key_data: Confidential>) -> Result<()> { if !self.expert_mode { return Err(Error::NonExpertMode); } // We have already encrypted a component, therefore reject the new // components key. if self.num_chunks > 0 || self.finalized { return Err(Error::FirstComponentAlreadyPrepared); } self.comp_key = SymKey::try_from_data(self.comp_key.key_type(), key_data)?; Ok(()) } /// Prepare the given component and write it into the given writer and /// assume the given memory address. /// /// * `writer` - Write the prepared component into this writer. /// * `layout` - Memory layout where the prepared component is later used. /// * `component` - Component to be prepared as . /// * `addr` - Memory address where the prepared component later will later be located /// (important for the Secure Execution header). /// * `tweak` - Tweak used for the component encryption. /// /// # Errors /// /// This function will return an error if: /// * address is smaller than the expected next possible address. /// * the image was already finalized /// * the given tweak is invalid pub fn prepare_and_insert_as_secure_component>( &mut self, writer: &mut W, layout: &mut Layout, component: &mut T, addr: u64, tweak: Vec, ) -> Result { let next_possible_addr = layout.next_addr; if addr < next_possible_addr { return Err(Error::NonMonotonicallyIncreasing { addr, next_addr: next_possible_addr, }); } let alignment = layout.alignment; if (addr % alignment) != 0 { return Err(Error::UnalignedAddress { addr, alignment }); } if alignment > self.chunk_size.try_into().unwrap() { return Err(Error::InvalidAlignment { alignment, chunk_size: self.chunk_size, }); } let max_component_size = layout.max_size_of_chunk_at_addr(addr)?; let secured_comp = self.prepare_and_insert_as_secure_component_unchecked( writer, component, addr, max_component_size, tweak, )?; layout.insert_interval(secured_comp.src.start, secured_comp.src.size())?; Ok(secured_comp) } /// Prepare the given component and insert it at the given image address. /// /// * `writer` - Write the prepared component into this writer. /// * `component` - Component to be prepared. /// * `addr` - Address where the prepared component should be inserted. /// * `max_component_size`- Maximum possible size that the prepared component may have /// * `tweak` - Tweak used for the component encryption. /// /// # Errors /// /// This function will return an error if: /// * address is smaller than the expected next possible address. /// * the image was already finalized /// * the given tweak is invalid fn prepare_and_insert_as_secure_component_unchecked>( &mut self, writer: &mut W, component: &mut T, addr: u64, max_component_size: Option, tweak: Vec, ) -> Result { assert!(component.secure_mode()); assert_ne!(self.chunk_size, 0); if self.finalized { return Err(Error::ImageAlreadyFinalized); } let expected_tweak_len = self.cipher.iv_length(); if expected_tweak_len != tweak.len() { return Err(Error::InvalidTweakSize { given: tweak.len(), expected: expected_tweak_len, }); } let mode = if self.encrypt && component.secure_mode() { Mode::Encrypt } else { Mode::Padding }; let prepare_args = PrepareSecuredComponentArgs { addr, cipher: self.cipher, mode, key: self.comp_key.value(), iv: &tweak, chunk_size: self.chunk_size, }; let mut ops = MetadataArgs { content_hasher: Some(&mut self.pld_hasher), tweak_hasher: Some(&mut self.tld_hasher), address_hasher: Some(&mut self.ald_hasher), num_chunks: Some(&mut self.num_chunks), max_component_size, input_size: 0, padded_input_size: 0, output_size: 0, }; // Prepare the component and write the prepared data directly to the output prepare_component(&prepare_args, component, writer, Some(&mut ops))?; let original_size = ops.input_size; let prepared_size = ops.output_size.try_into()?; let src = Interval::new_with_size(addr, prepared_size)?; Ok(SecuredComponent { original_size, src: Rc::new(src), tweak_or_iv: tweak, }) } /// Prepare the given component and append the prepared component to the /// back of the image layout. /// /// # Errors /// /// This function will return an error if the image was already finalized or /// the given tweak is invalid. pub fn prepare_and_append_as_secure_component>( &mut self, writer: &mut W, layout: &mut Layout, component: &mut T, tweak: Vec, ) -> Result { let next_addr = layout.next_addr; self.prepare_and_insert_as_secure_component(writer, layout, component, next_addr, tweak) } /// Finalizes the image and returns the image metadata (the digests, number /// of chunks) and the key that was used for the components encryption. /// /// # Errors /// /// This function will return an error if the builder is already finalized /// or there was a problem in a cryptographic operation. pub fn finish(&mut self) -> Result { if self.finalized { return Err(Error::ImageAlreadyFinalized); } self.finalized = true; Ok(ComponentMetadata::ComponentMetadataV1( ComponentMetadataV1 { ald: try_copy_slice_to_array(self.ald_hasher.finish()?.as_ref())?, pld: try_copy_slice_to_array(self.pld_hasher.finish()?.as_ref())?, tld: try_copy_slice_to_array(self.tld_hasher.finish()?.as_ref())?, nep: self.num_chunks.try_into()?, key: try_copy_slice_to_array(self.comp_key.value())?.into(), }, )) } /// Returns if encryption is used. pub const fn encryption_enabled(&self) -> bool { self.encrypt } /// Returns the chunk size. pub const fn chunk_size(&self) -> usize { self.chunk_size } } #[allow(clippy::shadow_unrelated)] #[cfg(test)] mod tests { use std::{fmt::Debug, io::Cursor}; use pv::request::Aes256XtsKey; use super::*; #[test] fn prepare_aligned_component_test() { #[derive(Debug)] struct TestComp { reader: T, } impl ComponentTrait<()> for TestComp { fn secure_mode(&self) -> bool { true } fn kind(&self) {} } impl Read for TestComp { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { self.reader.read(buf) } } let start_addr = 0x10000; let encryption = true; let mut writer = Cursor::new(Vec::new()); let mut ctx = SecuredComponentBuilder::new_v1(encryption).expect("should work"); let mut key = vec![0x42; 32]; key.extend([0x43; 32]); ctx.i_know_what_i_am_doing(); ctx.set_components_key( Aes256XtsKey::new(<[u8; 64]>::try_from(key.as_slice()).unwrap()).into(), ) .unwrap(); let input_data1 = vec![0x1; 0x3400]; let input_data2 = vec![0x2; 0x3000]; let mut comp1 = TestComp { reader: Cursor::new(input_data1), }; let tweak1 = vec![ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x42, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ]; let img_comp_res = ctx.prepare_and_insert_as_secure_component_unchecked( &mut writer, &mut comp1, start_addr, None, tweak1, ); assert!(img_comp_res.is_ok()); assert_eq!(ctx.num_chunks, 4); let reader2 = Cursor::new(input_data2); let mut comp2 = TestComp { reader: reader2 }; let tweak2 = vec![ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x42, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ]; let img_comp_res = ctx.prepare_and_insert_as_secure_component_unchecked( &mut writer, &mut comp2, 0x20000, None, tweak2, ); assert!(img_comp_res.is_ok()); assert_eq!(ctx.num_chunks, 7); let metav1: ComponentMetadataV1 = ctx .finish() .expect("should not fail") .try_into() .expect("should not fail"); // Check ALD assert_eq!( metav1.ald, [ 195, 145, 222, 87, 39, 160, 130, 18, 234, 47, 234, 156, 55, 249, 207, 9, 11, 229, 31, 147, 198, 213, 33, 184, 144, 99, 50, 206, 114, 12, 95, 56, 173, 160, 231, 62, 105, 102, 62, 82, 17, 208, 21, 254, 244, 29, 198, 38, 6, 245, 19, 94, 97, 153, 4, 212, 244, 80, 171, 136, 159, 73, 202, 173 ], ); // Check PLD assert_eq!( metav1.pld, [ 162, 79, 243, 10, 138, 241, 41, 88, 136, 222, 223, 233, 54, 158, 181, 9, 41, 3, 9, 169, 1, 89, 235, 195, 44, 162, 106, 83, 249, 212, 54, 74, 120, 24, 87, 226, 89, 5, 135, 83, 108, 62, 118, 115, 85, 199, 183, 96, 63, 43, 12, 106, 64, 127, 22, 51, 13, 130, 18, 141, 9, 100, 250, 210 ] ); // Check TLD let digest = MessageDigest::sha512(); let mut hasher_new = Hasher::new(digest).expect("should work"); // Tweaks for comp1 hasher_new .update(&[0, 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0]) .expect("should work"); hasher_new .update(&[0, 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 16, 0]) .expect("should work"); hasher_new .update(&[0, 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 32, 0]) .expect("should work"); hasher_new .update(&[0, 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 48, 0]) .expect("should work"); // Tweaks for comp2 hasher_new .update(&[0, 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0]) .expect("should work"); hasher_new .update(&[0, 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 16, 0]) .expect("should work"); hasher_new .update(&[0, 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 32, 0]) .expect("should work"); let exp = hasher_new.finish().expect("should work"); assert_eq!(metav1.tld, *exp); assert_eq!( metav1.tld, [ 66, 79, 227, 207, 4, 166, 246, 74, 122, 239, 24, 92, 59, 78, 246, 7, 192, 228, 245, 75, 183, 225, 70, 32, 181, 116, 163, 211, 30, 239, 49, 199, 212, 98, 235, 4, 13, 69, 238, 105, 24, 230, 184, 9, 104, 186, 68, 84, 249, 226, 237, 194, 111, 105, 41, 237, 98, 77, 0, 85, 242, 53, 86, 89 ] ); } #[test] fn test_update_ald_digest() { let start = 0x10000; let stop = 0x13400; let digest = MessageDigest::sha512(); let mut hasher = Hasher::new(digest).expect("should work"); let mut hasher_new = Hasher::new(digest).expect("should work"); hasher_new .update(&0x10000_u64.to_be_bytes()) .expect("should work"); hasher_new .update(&0x11000_u64.to_be_bytes()) .expect("should work"); hasher_new .update(&0x12000_u64.to_be_bytes()) .expect("should work"); hasher_new .update(&0x13000_u64.to_be_bytes()) .expect("should work"); hasher_new .update(&0x20000_u64.to_be_bytes()) .expect("should work"); hasher_new .update(&0x21000_u64.to_be_bytes()) .expect("should work"); hasher_new .update(&0x22000_u64.to_be_bytes()) .expect("should work"); let exp = hasher_new.finish().expect("should work"); let chunks_count_res = update_ald_digest(&mut hasher, &Interval { start, stop }, 4096); assert!(chunks_count_res.is_ok()); assert_eq!(chunks_count_res.unwrap(), 4); let chunks_count_res = update_ald_digest( &mut hasher, &Interval { start: 0x20000, stop: 0x23000, }, 4096, ); assert!(chunks_count_res.is_ok()); assert_eq!(chunks_count_res.unwrap(), 3); let res_ret = hasher.finish(); assert!(res_ret.is_ok()); let res = res_ret.unwrap(); assert_eq!(&*exp, &*res,); assert_eq!( &*res, [ 195, 145, 222, 87, 39, 160, 130, 18, 234, 47, 234, 156, 55, 249, 207, 9, 11, 229, 31, 147, 198, 213, 33, 184, 144, 99, 50, 206, 114, 12, 95, 56, 173, 160, 231, 62, 105, 102, 62, 82, 17, 208, 21, 254, 244, 29, 198, 38, 6, 245, 19, 94, 97, 153, 4, 212, 244, 80, 171, 136, 159, 73, 202, 173 ] ); } } s390-tools-2.38.0/rust/pvimg/src/pv_utils/serializing.rs000066400000000000000000000075121502674226300231150ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::fmt::LowerHex; use deku::{ reader::Reader, writer::Writer, DekuContainerRead, DekuContainerWrite, DekuError, DekuReader, DekuWriter, }; use pv::request::{Confidential, Zeroize}; use serde::{Serialize, Serializer}; use utils::HexSlice; use crate::pv_utils::error::Result; pub fn ser_hex, S: Serializer>( data: A, ser: S, ) -> std::result::Result { HexSlice::from(data.as_ref()).serialize(ser) } pub fn ser_lower_hex( data: &B, ser: S, ) -> std::result::Result { format!("{:#018x}", data).serialize(ser) } pub fn ser_hex_confidential( data: &Confidential<[u8; COUNT]>, ser: S, ) -> std::result::Result { ser_hex(data.value(), ser) } /// Read a slice into a confidential array of type [`T`] and length [`N`]. /// /// # Errors /// /// This function will return an error if the result could not be constructed or /// if there was an I/O error. pub fn confidential_read_slice<'a, Ctx, T, const COUNT: usize, R>( reader: &mut Reader, ctx: Ctx, ) -> Result, DekuError> where Ctx: Copy, T: Default + DekuReader<'a, Ctx>, R: std::io::Read + std::io::Seek, { Ok(Confidential::new(<[T; COUNT]>::from_reader_with_ctx( reader, ctx, )?)) } /// Writes a confidential array into this writer. /// /// # Errors /// /// This function will return an error if there was an I/O error. pub fn confidential_write_slice( value: &Confidential<[T; COUNT]>, writer: &mut Writer, ctx: Ctx, ) -> Result<(), DekuError> where Ctx: Copy, T: Default + DekuWriter, W: std::io::Write + std::io::Seek, { value.value().to_writer(writer, ctx) } /// Serializes `value` to bytes. /// /// # Errors /// /// This function will return an error if the value could not be serialized. pub fn serialize_to_bytes(value: &T) -> Result> where T: DekuContainerWrite, { Ok(value.to_bytes()?) } /// Deserializes `value` to `T`. /// /// # Errors /// /// This function will return an error if the given value could not be /// deserialized. pub fn deserialize_from_bytes<'a, T>(value: &'a [u8]) -> Result where T: DekuContainerRead<'a>, { let ((_, rest), obj) = T::from_bytes((value, 0))?; assert_eq!(rest, 0); Ok(obj) } /// Returns the size (in bytes) of the serialized `value`. /// /// # Errors /// /// This function will return an error if the given value could not be /// serialized. pub fn bytesize(value: &T) -> Result where T: DekuContainerWrite, { let data = serialize_to_bytes(value)?; Ok(data.len()) } pub fn bytesize_confidential(value: &Confidential) -> Result where T: DekuContainerWrite + Zeroize, { let data = Confidential::new(serialize_to_bytes(value.value())?); Ok(data.value().len()) } #[cfg(test)] mod tests { use deku::{ctx::Endian, DekuContainerWrite, DekuRead, DekuWrite}; use pv::request::Confidential; use crate::pv_utils::serializing::{confidential_read_slice, confidential_write_slice}; #[test] fn read_and_write() { #[derive(DekuRead, DekuWrite)] #[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")] struct Test { #[deku( reader = "confidential_read_slice(deku::reader, endian)", writer = "confidential_write_slice(test, deku::writer, endian)" )] test: Confidential<[u32; 1]>, } const DATA: [u8; 4] = [15_u8, 1_u8, 2_u8, 3_u8]; let test = Test::try_from(DATA.as_ref()).unwrap(); assert_eq!(test.test.value()[0], 0x0f010203); assert_eq!(test.to_bytes().unwrap().as_slice(), &DATA); } } s390-tools-2.38.0/rust/pvimg/src/pv_utils/uv_keys.rs000066400000000000000000000113021502674226300222520ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::io::{BufRead, BufReader, Read}; use enum_dispatch::enum_dispatch; use pv::misc::decode_hex; use super::try_copy_slice_to_array; use crate::error::{Error, Result}; /// The `enum_dispatch` macros needs at least one local trait to be implemented. #[allow(unused)] #[enum_dispatch(UvKeyHashes)] trait UvKeyHashTrait: AsRef<[u8]> {} #[derive(Debug, PartialEq, Eq)] pub struct UvKeyHashV1([u8; 32]); #[non_exhaustive] #[enum_dispatch] #[derive(PartialEq, Eq, Debug)] pub enum UvKeyHash { UvKeyHashV1(UvKeyHashV1), } impl AsRef<[u8]> for UvKeyHash { fn as_ref(&self) -> &[u8] { match self { Self::UvKeyHashV1(hash) => hash.as_ref(), } } } impl UvKeyHashV1 { pub fn new>(data: T) -> Result { let array = try_copy_slice_to_array(data.as_ref())?; Ok(Self(array)) } } #[derive(Debug, PartialEq, Eq)] pub struct UvKeyHashesV1 { pub pchkh: UvKeyHashV1, pub pbhkh: UvKeyHashV1, pub res: [UvKeyHashV1; 13], } impl UvKeyHashV1 { pub const UV_KEY_HASH_NULL: Self = Self([0x0_u8; 32]); } impl AsRef<[u8]> for UvKeyHashV1 { fn as_ref(&self) -> &[u8] { self.0.as_ref() } } impl TryFrom<&str> for UvKeyHashV1 { type Error = Error; fn try_from(value: &str) -> Result { let bytes = decode_hex(value)?; if bytes.len() != 32 { return Err(Error::InvalidTargetKeyHash); } Ok(Self(bytes.try_into().unwrap())) } } impl TryFrom for UvKeyHashV1 { type Error = Error; fn try_from(value: String) -> Result { value.as_str().try_into() } } impl UvKeyHashesV1 { pub const SYS_UV_KEYS_ALL: &'static str = "/sys/firmware/uv/keys/all"; /// Reads a `UvKeyHashesV1` from an [`std::io::Read`]. /// /// # Errors /// /// This function will return an error if this functions encounters an I/O /// error, if a line could not be interpreted as `UvKeyHashV1` or if the /// count of hashes is less than 15. #[allow(clippy::similar_names)] pub fn read_from_io(reader: R) -> Result where R: Read, { let buf_reader = BufReader::new(reader); let lines: Vec = buf_reader .lines() .collect::, std::io::Error>>()?; let hashes: Vec = lines .into_iter() .map(UvKeyHashV1::try_from) .collect::, Error>>()?; let hashes_count = hashes.len(); if hashes_count < 15 { return Err(Error::InvalidUvKeyHashes); } let [pchkh, pbhkh, res @ ..]: [UvKeyHashV1; 15] = hashes.try_into().map_err(|_| Error::InvalidUvKeyHashes)?; Ok(Self { pchkh, pbhkh, res }) } } #[cfg(test)] mod tests { use std::io::Cursor; use pv::misc::decode_hex; use crate::{pv_utils::uv_keys::UvKeyHashV1, uvdata::UvKeyHashesV1}; #[test] fn from_reader() { let data = "0b729fd62241b339840d61b964a06bb6a1fd4976d9ebea2b4fb48d44de3a2461 8ec6bc2f77d5d6474b1417cf0a8c914f576245a5b9bb0eefacc7b821483ece7d 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 "; let result = UvKeyHashesV1::read_from_io(Cursor::new(data)).expect("should not fail"); assert_eq!( result, UvKeyHashesV1 { pchkh: UvKeyHashV1::new( decode_hex("0b729fd62241b339840d61b964a06bb6a1fd4976d9ebea2b4fb48d44de3a2461") .unwrap() ) .unwrap(), pbhkh: UvKeyHashV1::new( decode_hex("8ec6bc2f77d5d6474b1417cf0a8c914f576245a5b9bb0eefacc7b821483ece7d") .unwrap() ) .unwrap(), res: [UvKeyHashV1::UV_KEY_HASH_NULL; 13], } ); } } s390-tools-2.38.0/rust/pvimg/src/pv_utils/uvdata.rs000066400000000000000000000136761502674226300220710ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use enum_dispatch::enum_dispatch; use pv::request::{ decrypt_aead, derive_aes256_gcm_key, encrypt_aead, openssl::pkey::{PKey, PKeyRef, Private, Public}, Confidential, SymKey, SymKeyType, }; use super::se_hdr::{SeHdrBinV1, SeHdrData, SeHdrVersioned}; use crate::pv_utils::{ error::{Error, Result}, serializing::deserialize_from_bytes, }; /// Trait to be used for Authenticated Encryption with Associated Data (AEAD) /// data structures. #[enum_dispatch] pub trait AeadCipherTrait { /// Returns the AEAD key type used by the data structure. fn aead_key_type(&self) -> SymKeyType; /// Returns the AEAD tag size used by the data structure. fn aead_tag_size(&self) -> usize; /// Returns the initialization vector (IV) used for AEAD /// encryption/decryption. fn iv(&self) -> &[u8]; } /// Trait to be used for AEAD cipher data #[enum_dispatch] pub trait AeadDataTrait { /// Returns the authenticated associated data. fn aad(&self) -> Result>; /// Returns the encrypted data. fn data(&self) -> Vec; /// Returns the tag data. fn tag(&self) -> Vec; } /// Trait to be used for AEAD plaintext data #[enum_dispatch] pub trait AeadPlainDataTrait { /// Returns the authenticated associated data. fn aad(&self) -> Result>; /// Returns the unencrypted data. fn data(&self) -> Result>>; /// Returns the tag data. fn tag(&self) -> Vec; } /// Key exchange related methods #[enum_dispatch] pub trait KeyExchangeTrait { /// Checks if a public key was used. /// /// # Errors /// /// This function will return an error if the public key cannot be converted /// into a hash. fn contains>>(&self, key: K) -> Result; /// Checks if the hash of a public key was used. fn contains_hash>(&self, hash: H) -> bool; /// Returns the public key from the party (e.g. guest owner) that is stored /// in the data structed. /// /// # Errors /// /// This function will return an error if the public key cannot be /// reconstructed. fn cust_pub_key(&mut self) -> Result>; /// Returns the key type of the exchanged key. fn key_type(&self) -> SymKeyType; /// Derive the key. /// /// # Errors /// /// This function will return an error if there is no customer public key is /// available or the key derivations fails. fn derive_key>>(&mut self, other_priv_key: K) -> Result { match self.key_type() { SymKeyType::Aes256Gcm => Ok(derive_aes256_gcm_key( other_priv_key.as_ref(), self.cust_pub_key()?.as_ref(), )? .into()), _ => unreachable!("BUG"), } } } /// Trait to be used for plain UV data. #[enum_dispatch] pub trait UvDataPlainTrait: AeadPlainDataTrait + AeadCipherTrait + KeyExchangeTrait + Clone { /// Returned type by [`Self::encrypt`]. type C: UvDataTrait; /// Encrypt the plain data. /// /// # Errors /// /// This function will return an error if the passed `key` has the wrong /// key type or the encryption fails. fn encrypt(&self, key: &SymKey) -> Result where ::C: for<'a> deku::DekuContainerRead<'a>, { if key.key_type() != self.aead_key_type() { return Err(Error::UnexpectedKeyType { given: self.key_type().to_string(), expected: self.aead_key_type().to_string(), }); } let aad = self.aad().map_err(|err| match err { Error::Deku(_) => Error::InvalidSeHdr, err => err, })?; let unecrypted_data = self.data().map_err(|err| match err { Error::Deku(_) => Error::InvalidSeHdr, err => err, })?; let iv = self.iv(); let result = encrypt_aead(key, iv, &aad, unecrypted_data.value())?; Self::C::try_from_data(&result.into_buf()) } /// Parses and converts the data into an instance of [`Self`] if possible. /// /// # Errors /// /// This function will return an error if the data could not parsed or /// converted. fn try_from_data<'a>(data: &'a [u8]) -> Result where Self: deku::DekuContainerRead<'a> + Sized, { deserialize_from_bytes(data) } } /// Trait to be used for (cipher) UV data. #[enum_dispatch] pub trait UvDataTrait: AeadDataTrait + AeadCipherTrait + KeyExchangeTrait + Clone { /// Returned type by [`Self::decrypt`]. type P: UvDataPlainTrait; /// Decrypt the UV data. /// /// # Errors /// /// This function will return an error if the passed `key` has the wrong key /// type or the decryption fails. fn decrypt(&self, key: &SymKey) -> Result where ::P: for<'a> deku::DekuContainerRead<'a>, { if key.key_type() != self.aead_key_type() { return Err(Error::UnexpectedKeyType { given: key.key_type().to_string(), expected: self.aead_key_type().to_string(), }); } let tag_size = self.aead_tag_size(); let aad = self.aad()?; let unecrypted_data = self.data(); let iv = self.iv(); let tag = self.tag(); assert_eq!(tag.len(), tag_size); let result = decrypt_aead(key, iv, &aad, &unecrypted_data, &tag)?; Self::P::try_from_data(result.into_buf().value()) } /// Parses and converts the data into an instance of [`Self`] if possible. /// /// # Errors /// /// This function will return an error if the data could not parsed or /// converted. fn try_from_data<'a>(data: &'a [u8]) -> Result where Self: deku::DekuContainerRead<'a> + Sized, { deserialize_from_bytes(data) } } s390-tools-2.38.0/rust/pvimg/src/pv_utils/uvdata_builder.rs000066400000000000000000000077531502674226300235760ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use enum_dispatch::enum_dispatch; use openssl::pkey::{PKey, PKeyRef, Private, Public}; use pv::request::{Confidential, SymKey}; use super::Error; use crate::pv_utils::{ error::Result, se_hdr::SeHdrData, uvdata::{AeadCipherTrait, UvDataPlainTrait}, }; #[enum_dispatch] pub trait AeadCipherBuilderTrait: AeadCipherTrait { fn set_iv(&mut self, iv: &[u8]) -> Result<()>; fn generate_aead_key(&self) -> Result { Ok(SymKey::random(self.aead_key_type())?) } } /// Key exchange related methods #[enum_dispatch] pub trait KeyExchangeBuilderTrait { fn add_keyslot( &mut self, hostkey: &PKeyRef, aead_key: &SymKey, priv_key: &PKeyRef, ) -> Result<()>; fn clear_keyslots(&mut self) -> Result<()>; fn generate_private_key(&self) -> Result>; fn set_cust_public_key(&mut self, key: &PKeyRef) -> Result<()>; } pub struct UvDataBuilder< 'a, T: KeyExchangeBuilderTrait + AeadCipherBuilderTrait, K = PKeyRef, P = PKey, > { pub(crate) expert_mode: bool, pub(crate) prot_key: SymKey, pub(crate) priv_key: P, pub(crate) target_keys: Vec<&'a K>, pub(crate) plain_data: T, } impl UvDataBuilder<'_, T, K, P> { /// Enable expert mode - this is required for specifying PSW, etc. pub fn i_know_what_i_am_doing(&mut self) { self.expert_mode = true; } } impl std::fmt::Debug for UvDataBuilder<'_, T> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("UvDataBuilder") .field("expert_mode", &self.expert_mode) .field("prot_key", &self.prot_key) .field("plain_data", &self.plain_data) .finish() } } impl<'a, T: KeyExchangeBuilderTrait + AeadCipherBuilderTrait> UvDataBuilder<'a, T> { pub fn add_hostkeys>>( &mut self, hostkeys: &'a [P], ) -> Result<&mut Self> { for hk in hostkeys { self.plain_data .add_keyslot(hk.as_ref(), &self.prot_key, &self.priv_key)?; self.target_keys.push(hk.as_ref()); } Ok(self) } pub fn with_iv(&mut self, iv: &[u8]) -> Result<&mut Self> { if !self.expert_mode { return Err(Error::NonExpertMode); } self.plain_data.set_iv(iv)?; Ok(self) } fn update_target_key_slots(&mut self) -> Result<()> { self.plain_data.clear_keyslots()?; for hk in &self.target_keys { self.plain_data .add_keyslot(hk, &self.prot_key, &self.priv_key)?; } Ok(()) } pub fn with_aead_key(&mut self, data: Confidential>) -> Result<&mut Self> { if !self.expert_mode { return Err(Error::NonExpertMode); } let key = SymKey::try_from_data(self.plain_data.aead_key_type(), data)?; self.prot_key = key; self.update_target_key_slots()?; Ok(self) } pub fn with_priv_key(&mut self, priv_key: &PKeyRef) -> Result<&mut Self> { if !self.expert_mode { return Err(Error::NonExpertMode); } self.plain_data.set_cust_public_key(priv_key)?; self.priv_key = priv_key.to_owned(); self.update_target_key_slots()?; Ok(self) } pub const fn prot_key(&self) -> &SymKey { &self.prot_key } pub fn priv_key(&self) -> &PKeyRef { self.priv_key.as_ref() } } /// A trait for the builder pattern. pub trait BuilderTrait { /// Data structure to construct type T; /// Builds the type [`Self::T`]. /// /// # Errors /// /// This function will return an error if the data structure could not be /// build. fn build(self) -> Result; } s390-tools-2.38.0/rust/pvimg/src/se_img.rs000066400000000000000000000432101502674226300201660ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::{ fmt::Display, io::{Cursor, Seek, SeekFrom, Write}, path::PathBuf, rc::Rc, }; use anyhow::{anyhow, Context, Result}; use deku::DekuContainerRead; use log::debug; use openssl::pkey::{PKey, Public}; use pv::{misc::read_file, request::Confidential}; use pvimg::{ error::Error, misc::{round_up, serialize_to_bytes, ShortPsw, PSW, PSW_MASK_BA, PSW_MASK_EA}, secured_comp::{ComponentTrait, Interval, Layout, SecuredComponent, SecuredComponentBuilder}, uvdata::{ BuilderTrait, PlaintextControlFlagsV1, SeHdrBuilder, SeHdrVersion, SecretControlFlagsV1, }, }; use crate::se_img_comps::{ create_ipib, ipib::Ipib, kernel::S390Kernel, metadata::ImgMetaData, render_stage3a, render_stage3b, sehdr::SeHdrComp, shortpsw::ShortPSWComp, stage3a_path, stage3b_path, CompTweakV1, Component, ComponentKind, STAGE3A_ENTRY, STAGE3A_INIT_ENTRY, STAGE3A_LOAD_ADDRESS, }; pub struct SeHdrArgs<'a> { pub keys: &'a [PKey], pub pcf: &'a PlaintextControlFlagsV1, pub scf: &'a SecretControlFlagsV1, pub cck: &'a Option<(PathBuf, Confidential>)>, pub hdr_aead_key: &'a Option<(PathBuf, Confidential>)>, pub psw_addr: &'a Option, } #[derive(Debug, PartialEq, Eq)] pub struct ImgComponent { kind: ComponentKind, pub(crate) src: Rc, pub(crate) secure_mode: Option, } impl ImgComponent { pub fn kind(&self) -> ComponentKind { self.kind.clone() } } impl Ord for ImgComponent { fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.src.cmp(&other.src) } } impl PartialOrd for ImgComponent { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Display for ImgComponent { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "| {:23} | ", self.kind.to_string())?; self.src.to_string().fmt(f)?; write!(f, " |") } } pub struct SeImgBuilder { /// Expert mode (components encryption key and Secure Execution header /// protection key can be set). By default disabled. expert_mode: bool, writer: W, layout: Layout, comps: Vec>, builder: SecuredComponentBuilder, stage3a: Vec, stage3b: Vec, /// The legacy Secure Execution header address (directly after stage3a) legacy_se_hdr_addr: Option, finalized: bool, } impl SeImgBuilder { const COMPONENT_ALIGNMENT_V1: u64 = SecuredComponentBuilder::COMPONENT_ALIGNMENT_V1; const DEFAULT_INITIAL_PSW_MASK: u64 = PSW_MASK_BA | PSW_MASK_EA; /// Create a Secure Execution boot image builder #[allow(clippy::similar_names)] pub(crate) fn new_v1( mut writer: W, encryption: bool, legacy_expected_se_hdr_size: Option, bootloader_dir: Option<&PathBuf>, ) -> Result { let stage3a = read_file(stage3a_path(bootloader_dir), "stage3a")?; let stage3b = read_file(stage3b_path(bootloader_dir), "stage3b")?; let mut legacy_se_hdr_addr = None; // Reserve memory space for the stage3a loader that will be written // later. let mut next_comp_addr: u64 = round_up( STAGE3A_LOAD_ADDRESS .checked_add(stage3a.len().try_into()?) .ok_or(Error::UnexpectedOverflow)?, Self::COMPONENT_ALIGNMENT_V1, )?; // Reserve memory space for the Secure Execution header in case of // legacy mode. if let Some(expected_se_hdr_size) = legacy_expected_se_hdr_size { // Place the Secure Execution header next to the stage3a and use as // the minimum address 0x14000. 0x14000 is used as the starting // point for searching the Secure Execution header in the // `pvextract-hdr` utility and we can therefore not use e.g. 0x13000 // even if it would be possible in regard to the memory layout. const PV_EXTRACT_SE_HDR_SEARCH_ADDR: u64 = 0x14000; let se_hdr_addr = std::cmp::max(next_comp_addr, PV_EXTRACT_SE_HDR_SEARCH_ADDR); next_comp_addr = round_up( se_hdr_addr .checked_add(expected_se_hdr_size.try_into()?) .ok_or(Error::UnexpectedOverflow)?, Self::COMPONENT_ALIGNMENT_V1, )?; legacy_se_hdr_addr = Some(se_hdr_addr); } if next_comp_addr % Self::COMPONENT_ALIGNMENT_V1 != 0 { return Err(Error::UnalignedAddress { addr: next_comp_addr, alignment: Self::COMPONENT_ALIGNMENT_V1, } .into()); } // Secure Execution expects, that the component addresses are aligned to // 4096. let layout = Layout::new(next_comp_addr, Self::COMPONENT_ALIGNMENT_V1)?; let builder = SecuredComponentBuilder::new_v1(encryption)?; // The layout of the boot image matches with the memory layout as it // it's loaded at location 0x0. Therefore let's seek to the // `next_comp_addr`. writer.seek(SeekFrom::Start(next_comp_addr))?; Ok(Self { layout, expert_mode: false, comps: vec![], writer, builder, legacy_se_hdr_addr, stage3a, stage3b, finalized: false, }) } /// Enable expert mode - this is required for specifying component tweaks by /// hand etc... pub(crate) fn i_know_what_i_am_doing(&mut self) { self.builder.i_know_what_i_am_doing(); self.expert_mode = true; } /// Prepare the given component as secured component, append it to the layout /// and write it to the output. /// /// # Errors /// /// This function will return an error if: /// + stage3b has already been added /// + problem with the preparation of the secured component /// + serialization problem of the component tweak (very unlikely) /// + a tweak was given, but the expert mode not enabled pub(crate) fn prepare_and_append_as_secure_component( &mut self, component: &mut T, tweak: Option>, ) -> Result> where T: ComponentTrait, { if self.finalized { return Err(Error::ImgAlreadyFinalized.into()); } if !component.secure_mode() { unreachable!("Bug") } if tweak.is_some() && !self.expert_mode { return Err(Error::NonExpertModeTweakGiven.into()); } debug!("Preparing {} as secured component", component.kind()); let tweak = tweak.unwrap_or(serialize_to_bytes(&CompTweakV1::new(component.kind())?)?); // No reason to seek as there are no holes between components (addr // alignment == alignment of the component size). If that changes we have to seek beforehand // to `self.layout.next_addr` self.writer.seek(SeekFrom::Start(self.layout. // next_addr))?; let secured_comp = self.builder.prepare_and_append_as_secure_component( &mut self.writer, &mut self.layout, component, tweak, )?; let img_comp = Rc::new(ImgComponent { kind: component.kind(), src: secured_comp.src.clone(), secure_mode: Some(secured_comp), }); self.comps.push(img_comp.clone()); Ok(img_comp) } /// Insert and write the given non-secured component at the given address. fn insert_nonsecure_component>( &mut self, component: &mut T, addr: u64, ) -> Result> { // FIXME Guarantee this during compile time using a "SecureMode" trait. if component.secure_mode() { unreachable!("Programming bug!") }; let max_component_size = self.layout.max_size_of_chunk_at_addr(addr)?; let mut buf = vec![0_u8; self.builder.chunk_size()]; let mut total_written_count: usize = 0; assert_ne!(buf.len(), 0); self.writer.seek(SeekFrom::Start(addr))?; loop { let read_count = component.read(&mut buf)?; // The end of file has reached as it's guaranteed that the buffer // [`buf`] has a length != 0. See // https://doc.rust-lang.org/std/io/trait.Read.html#tymethod.read if read_count == 0 { break; } if let Some(max_component_size) = max_component_size { if total_written_count .checked_add(read_count) .ok_or(Error::UnexpectedOverflow)? > max_component_size { return Err(anyhow!( "BUG: Component is too large for this location in the image: {} > {}", total_written_count + read_count, max_component_size )); } } self.writer.write_all(&buf[0..read_count])?; total_written_count = total_written_count .checked_add(read_count) .ok_or(Error::UnexpectedOverflow)?; } let src = self .layout .insert_interval(addr, total_written_count.try_into()?)?; let img_comp = Rc::new(ImgComponent { src, kind: component.kind(), secure_mode: None, }); match self.comps.binary_search(&img_comp) { Ok(_pos) => { return Err(anyhow!( "BUG: There is already another component at this location" )) } Err(pos) => self.comps.insert(pos, img_comp.clone()), } Ok(img_comp) } fn append_component>( &mut self, component: &mut T, ) -> Result> { let next_addr = self.layout.next_addr; self.insert_nonsecure_component(component, next_addr) } /// Prepare IPIB and write it to file fn add_ipib(&mut self, sehdr_src: &Interval) -> Result> { let img_comps_tweak_and_src: Result> = self .comps .iter() .filter(|comp| comp.secure_mode.is_some()) .map(|comp| { // Safety: We checked in the filter for `comp.secure_mode.is_some()`. let secure_mode_data = comp.secure_mode.as_ref().unwrap(); let src = &comp.src; let (_, tweak) = CompTweakV1::from_bytes((secure_mode_data.tweak(), 0))?; Ok((tweak.pref, src.clone())) }) .collect(); let ipib = create_ipib(sehdr_src, img_comps_tweak_and_src?)?; let mut ipib_comp = Ipib::new(Box::new(Cursor::new(serialize_to_bytes(&ipib)?))); self.append_component(&mut ipib_comp) } /// Prepare Secure Execution header and write it to the output fn add_sehdr(&mut self, stage3b_entry: u64, sehdr_args: SeHdrArgs) -> Result> { let meta = self.builder.finish()?; let mut se_hdr_builder = SeHdrBuilder::new( SeHdrVersion::V1, PSW { addr: sehdr_args.psw_addr.unwrap_or(stage3b_entry), mask: Self::DEFAULT_INITIAL_PSW_MASK, }, meta, )?; se_hdr_builder .add_hostkeys(sehdr_args.keys)? .with_pcf(sehdr_args.pcf)? .with_scf(sehdr_args.scf)?; if self.expert_mode { se_hdr_builder.i_know_what_i_am_doing(); } if let Some((path, cck)) = &sehdr_args.cck { se_hdr_builder .with_cck(cck.clone()) .with_context(|| format!("Failed to use '{}' as the CCK", path.display()))?; } if let Some((path, prot_key)) = sehdr_args.hdr_aead_key { se_hdr_builder .with_aead_key(prot_key.clone()) .with_context(|| { format!( "Failed to use '{}' as the Secure Execution header protection key", path.display() ) })?; } let se_hdr_bin = se_hdr_builder.build()?; let mut comp: Component = SeHdrComp::new(Box::new(Cursor::new(se_hdr_bin.as_bytes()?))).into(); if let Some(se_hdr_addr) = self.legacy_se_hdr_addr { self.insert_nonsecure_component(&mut comp, se_hdr_addr) } else { self.append_component(&mut comp) } } /// Finish the Secure Execution image - e.g. create Stage3a, Stage3b, Secure /// Execution header and so on. #[allow(clippy::similar_names)] pub fn finish(mut self, sehdr_args: SeHdrArgs) -> Result>> { if (sehdr_args.hdr_aead_key.is_some() || sehdr_args.psw_addr.is_some()) && !self.expert_mode { return Err(Error::NonExpertMode.into()); } // Create stage3b and write it to the output file let psw = PSW { addr: S390Kernel::KERNEL_ENTRY, mask: Self::DEFAULT_INITIAL_PSW_MASK, }; let stage3b_img_comp = self .add_stage3b(psw) .context("Failed to prepare stage3b component")?; // Create Secure Execution header and write it to the output file let sehdr_img_comp = self .add_sehdr(stage3b_img_comp.src.start, sehdr_args) .context("Failed to prepare Secure Execution header")?; // Create and write IPIB to the output file let ipib_img_comp = self .add_ipib(&sehdr_img_comp.src) .context("Failed to prepare IPIB")?; // Create and write stage3a to the output file let stage3a_img_comp = self .add_stage3a(&sehdr_img_comp.src, &ipib_img_comp.src) .context("Failed to prepare Stage3a")?; assert_eq!(stage3a_img_comp.src.start, STAGE3A_INIT_ENTRY); assert_eq!(stage3a_img_comp.src.start + 0x1000, STAGE3A_ENTRY); // Create and write short PSW at the beginning of the file let _short_psw_img_comp = self.add_short_psw( stage3a_img_comp .src .start .checked_add(0x1000) .ok_or(Error::UnexpectedOverflow)?, )?; // Create and write Secure Execution boot image meta data right after the short PSW let _metadata_img_comp = self.add_metadata(ipib_img_comp.src.start, sehdr_img_comp.src.start)?; Ok(self.comps) } /// Prepare stage3a and write it to file fn add_stage3a( &mut self, se_hdr_src: &Interval, ipib_src: &Interval, ) -> Result> { let stage3a_load_addr = STAGE3A_LOAD_ADDRESS; let mut stage3a_comp = render_stage3a( self.stage3a.clone(), stage3a_load_addr, se_hdr_src, ipib_src, )?; self.insert_nonsecure_component(&mut stage3a_comp, stage3a_load_addr) } /// Prepare short PSW and write it to file fn add_short_psw(&mut self, stage3a_entry: u64) -> Result> { let short_psw: ShortPsw = PSW { addr: stage3a_entry, mask: Self::DEFAULT_INITIAL_PSW_MASK, } .try_into()?; let mut short_psw_comp = ShortPSWComp::new(Box::new(Cursor::new(serialize_to_bytes(&short_psw)?))); self.insert_nonsecure_component(&mut short_psw_comp, ShortPSWComp::OFFSET) } /// Prepare Secure Execution image metadata and write it to the file fn add_metadata(&mut self, ipib_off: u64, hdr_off: u64) -> Result> { let mut metadata_comp = ImgMetaData::new(ipib_off, hdr_off)?; let metadata_img_comp = self.insert_nonsecure_component(&mut metadata_comp, ImgMetaData::OFFSET)?; if metadata_img_comp.src.size() > ImgMetaData::MAX_SIZE { unreachable!("The metadata should never be larger than the BSS size of stage3a"); } Ok(metadata_img_comp) } /// Prepare stage3b and write it to file fn add_stage3b(&mut self, psw: PSW) -> Result> { // Prepare stage3b - for this we must prepare the arguments for it. Since we // have the memory layout for the movable components (kernel, cmdline, and // initrd) we can do this now. let mut stage3b_comp = render_stage3b(self.stage3b.clone(), psw, &self.comps)?; let result = self.prepare_and_append_as_secure_component(&mut stage3b_comp, None); // No other "regular components can be added now self.finalized = true; result } pub(crate) fn set_components_key( &mut self, key_data: Confidential>, ) -> pvimg::error::Result<()> { self.builder.set_components_key(key_data) } } #[cfg(test)] mod tests { use std::io::Cursor; use super::SeImgBuilder; use crate::{se_img::stage3a_path, se_img_comps::stage3b_path}; #[test] fn test_comp_ctx_new() { // If the bootloader does not exist, we cannot test. if !stage3a_path(None).exists() || !stage3b_path(None).exists() { return; } let encryption = true; let mut writer = Cursor::new(Vec::new()); let ctx_res = SeImgBuilder::new_v1(&mut writer, encryption, None, None); assert!(ctx_res.is_ok()); let ctx = ctx_res.unwrap(); assert_eq!(ctx.layout.next_addr, 0x13000); assert!(ctx.builder.encryption_enabled()); assert_eq!(ctx.comps, vec![]); } } s390-tools-2.38.0/rust/pvimg/src/se_img_comps.rs000066400000000000000000000263741502674226300214030ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::{ fmt::{Debug, Display}, io::{Read, Seek, SeekFrom}, }; use anyhow::Context; use deku::{ctx::Endian, DekuRead, DekuWrite}; use enum_dispatch::enum_dispatch; use pv::request::random_array; use pvimg::{error::Result, secured_comp::ComponentTrait}; use self::{ cmdline::Cmdline, kernel::S390Kernel, metadata::ImgMetaData, ramdisk::Ramdisk, sehdr::SeHdrComp, shortpsw::ShortPSWComp, stage3a::Stage3a, stage3b::Stage3b, }; pub use crate::se_img_comps::bootloader::{ create_ipib, render_stage3a, render_stage3b, stage3a_path, stage3b_path, STAGE3A_ENTRY, STAGE3A_INIT_ENTRY, STAGE3A_LOAD_ADDRESS, }; use crate::se_img_comps::ipib::Ipib; mod bootloader; pub mod cmdline; pub mod ipib; pub mod kernel; pub mod metadata; pub mod ramdisk; pub mod sehdr; pub mod shortpsw; pub mod stage3a; pub mod stage3b; /// A trait for checking a component. #[enum_dispatch] trait ComponentCheckTrait: ComponentTrait { /// Check the component /// /// Note: The implementer does not have to care about resetting the file position /// as this is done by [`ComponentCheckCtx`]. fn check(&mut self, ctx: &ComponentCheckCtx) -> Result<()>; /// Initialize [`ComponentCheckCtx`], e.g. it reads what max kernel command /// line is supported by the given Linux kernel. /// /// Note: The implementer does not have to care about resetting the file /// position as this is done by the [`ComponentCheckCtx`] fn init_ctx(&mut self, ctx: &mut ComponentCheckCtx) -> Result<()>; } #[derive(Debug)] struct ComponentCheckCtx { max_kernel_cmdline_size: usize, } impl Default for ComponentCheckCtx { fn default() -> Self { Self { max_kernel_cmdline_size: S390Kernel::LEGACY_MAX_COMMAND_LINE_SIZE, } } } impl ComponentCheckCtx { fn new() -> Self { Default::default() } // Initialize component context. fn init(&mut self, component: &mut Component) -> Result<()> { let old_pos = component.stream_position()?; let result = component.init_ctx(self); component.seek(SeekFrom::Start(old_pos))?; result } // Check component. fn check_comp(&self, component: &mut Component) -> Result<()> { let old_pos = component.stream_position()?; let result = component.check(self); component.seek(SeekFrom::Start(old_pos))?; result } } /// Check the given components. /// /// The original stream position of the components remains as it was before /// calling this function. /// /// # Errors /// /// This function will return an error if there was an IO error or the component /// check has failed. pub fn check_components(components: &mut [Component]) -> Result<(), anyhow::Error> { let mut components_ctx = ComponentCheckCtx::new(); for component in components.iter_mut() { components_ctx .init(component) .with_context(|| format!("Check for {} component has failed", component.kind()))?; } for component in components.iter_mut() { components_ctx .check_comp(component) .with_context(|| format!("Check for {} component has failed", component.kind()))?; } Ok(()) } #[non_exhaustive] #[derive(Debug)] #[enum_dispatch(ComponentCheckTrait)] pub enum Component { ShortPSW(ShortPSWComp), ImgMetaData(ImgMetaData), Stage3a(Stage3a), Kernel(S390Kernel), Ramdisk(Ramdisk), Cmdline(Cmdline), Stage3b(Stage3b), SeHdr(SeHdrComp), Ipib(Ipib), } // No `enum_dispatch` can be used since the trait is implemented in another // crate. impl Seek for Component { fn seek(&mut self, pos: SeekFrom) -> std::io::Result { match self { Self::ShortPSW(obj) => obj.seek(pos), Self::Stage3a(obj) => obj.seek(pos), Self::Kernel(obj) => obj.seek(pos), Self::Ramdisk(obj) => obj.seek(pos), Self::Cmdline(obj) => obj.seek(pos), Self::Stage3b(obj) => obj.seek(pos), Self::SeHdr(obj) => obj.seek(pos), Self::Ipib(obj) => obj.seek(pos), Self::ImgMetaData(obj) => obj.seek(pos), } } } // No `enum_dispatch` can be used since the trait is implemented in another // crate. impl Read for Component { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { match self { Self::ShortPSW(obj) => obj.read(buf), Self::Stage3a(obj) => obj.read(buf), Self::Kernel(obj) => obj.read(buf), Self::Ramdisk(obj) => obj.read(buf), Self::Cmdline(obj) => obj.read(buf), Self::Stage3b(obj) => obj.read(buf), Self::SeHdr(obj) => obj.read(buf), Self::Ipib(obj) => obj.read(buf), Self::ImgMetaData(obj) => obj.read(buf), } } } // No `enum_dispatch` can be used since the trait is implemented in another // crate. impl ComponentTrait for Component { fn secure_mode(&self) -> bool { match self { Self::ShortPSW(obj) => obj.secure_mode(), Self::Stage3a(obj) => obj.secure_mode(), Self::Kernel(obj) => obj.secure_mode(), Self::Ramdisk(obj) => obj.secure_mode(), Self::Cmdline(obj) => obj.secure_mode(), Self::Stage3b(obj) => obj.secure_mode(), Self::SeHdr(obj) => obj.secure_mode(), Self::Ipib(obj) => obj.secure_mode(), Self::ImgMetaData(obj) => obj.secure_mode(), } } fn kind(&self) -> ComponentKind { match self { Self::ShortPSW(obj) => obj.kind(), Self::Stage3a(obj) => obj.kind(), Self::Kernel(obj) => obj.kind(), Self::Ramdisk(obj) => obj.kind(), Self::Cmdline(obj) => obj.kind(), Self::Stage3b(obj) => obj.kind(), Self::SeHdr(obj) => obj.kind(), Self::Ipib(obj) => obj.kind(), Self::ImgMetaData(obj) => obj.kind(), } } } // Trick to be able to pass it as `&dyn ReadSeekDebug` pub trait ReadSeekDebug: Read + Seek + Debug {} impl ReadSeekDebug for T {} #[derive(Debug)] pub struct CompReader { reader: Box, } impl CompReader { pub fn new(reader: Box) -> Self { Self { reader } } } impl Read for CompReader { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { self.reader.read(buf) } } impl Seek for CompReader { fn seek(&mut self, pos: SeekFrom) -> std::io::Result { self.reader.seek(pos) } } /// The order of enum variants implicitly defines the order of the secured /// components within the Secure Execution image! #[repr(u16)] #[derive(Debug, Clone, PartialEq, PartialOrd, Eq)] pub enum ComponentKind { ShortPSW = 10, ImgMetaData = 20, Stage3a = 30, Kernel = 40, Ramdisk = 50, Cmdline = 60, Stage3b = 70, SeHdr = 80, Ipib = 90, } impl ComponentKind { pub fn tweak_prefix(&self) -> u16 { self.clone() as u16 } pub fn from_tweak_prefix(value: u16) -> Self { // Safety: `value` must correspond to a discriminant value of `Self` unsafe { std::mem::transmute(value) } } } impl Display for ComponentKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Kernel => write!(f, "Linux kernel"), Self::Ramdisk => write!(f, "ramdisk"), Self::Cmdline => write!(f, "kernel cmdline"), Self::Stage3a => write!(f, "stage3a"), Self::Stage3b => write!(f, "stage3b"), Self::SeHdr => write!(f, "Secure Execution header"), Self::Ipib => write!(f, "IPIB"), Self::ShortPSW => write!(f, "short PSW"), Self::ImgMetaData => write!(f, "Image metadata"), } } } #[derive(Debug, Default, PartialEq, Eq, Clone, DekuRead, DekuWrite)] #[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")] pub struct CompTweakPrefV1 { pub comp_prefix: u16, pub rand: [u8; 6], } impl CompTweakPrefV1 { fn to_u64(&self) -> u64 { let mut bytes_be = self.comp_prefix.to_be_bytes().to_vec(); bytes_be.extend_from_slice(self.rand.as_slice()); assert_eq!(bytes_be.len(), 8); // Safety: `bytes_be ` is guaranteed to be 8 bytes long. u64::from_be_bytes(bytes_be.try_into().unwrap()) } } #[derive(Debug, Default, PartialEq, Eq, Clone, DekuRead, DekuWrite)] #[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")] pub struct CompTweakV1 { pub pref: CompTweakPrefV1, pub pg_idx: u64, } impl CompTweakV1 { pub fn new(kind: ComponentKind) -> Result { let pref = CompTweakPrefV1 { comp_prefix: kind.tweak_prefix(), rand: random_array()?, }; Ok(Self { pref, pg_idx: 0 }) } pub const fn comp_prefix(&self) -> u16 { self.pref.comp_prefix } } #[allow(clippy::shadow_unrelated)] #[cfg(test)] mod tests { use deku::{DekuContainerRead, DekuContainerWrite}; use proptest::{ prelude::{Just, Strategy}, prop_assert_eq, prop_oneof, proptest, }; use super::{ComponentCheckCtx, ComponentKind}; use crate::se_img_comps::{check_components, kernel::S390Kernel, CompTweakPrefV1, CompTweakV1}; fn component_kind_strategy() -> impl Strategy { prop_oneof![ Just(ComponentKind::ShortPSW), Just(ComponentKind::ImgMetaData), Just(ComponentKind::Stage3a), Just(ComponentKind::Kernel), Just(ComponentKind::Ramdisk), Just(ComponentKind::Cmdline), Just(ComponentKind::Stage3b), Just(ComponentKind::SeHdr), Just(ComponentKind::Ipib), ] } proptest! { #[test] fn tweak_prefix_back_to_original(kind in component_kind_strategy()) { let prefix = kind.tweak_prefix(); prop_assert_eq!(kind, ComponentKind::from_tweak_prefix(prefix)); } } #[test] fn compctx() { let ctx = ComponentCheckCtx::new(); assert_eq!( ctx.max_kernel_cmdline_size, S390Kernel::LEGACY_MAX_COMMAND_LINE_SIZE ); } #[test] fn test_check_components() { check_components(&mut []).unwrap(); } #[test] fn comptweak_v1() { let tweak = CompTweakV1 { pref: CompTweakPrefV1 { comp_prefix: 3, rand: [157, 239, 44, 103, 219, 118], }, pg_idx: 0, }; let bytes = [0, 3, 157, 239, 44, 103, 219, 118, 0, 0, 0, 0, 0, 0, 0, 0]; assert_eq!(tweak.pref.to_u64(), 1018075497880438); assert_eq!(tweak.to_bytes().unwrap(), bytes,); assert_eq!(CompTweakV1::from_bytes((&bytes, 0)).unwrap().1, tweak); let tweak = CompTweakV1 { pref: CompTweakPrefV1 { comp_prefix: 0, rand: [0; 6], }, pg_idx: 0, }; let bytes = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; assert_eq!(tweak.pref.to_u64(), 0); assert_eq!(tweak.to_bytes().unwrap(), bytes,); assert_eq!(CompTweakV1::from_bytes((&bytes, 0)).unwrap().1, tweak); } } s390-tools-2.38.0/rust/pvimg/src/se_img_comps/000077500000000000000000000000001502674226300210215ustar00rootroot00000000000000s390-tools-2.38.0/rust/pvimg/src/se_img_comps/bootloader.rs000066400000000000000000000153671502674226300235350ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::{io::Cursor, path::PathBuf, rc::Rc}; pub mod ipl; mod stage3a_defs; mod stage3b_defs; use ipl::IPL_PARM_BLOCK_PV_VERSION; use log::trace; use pvimg::{ error::{Error, Result}, misc::{serialize_to_bytes, PSW}, secured_comp::Interval, }; pub use self::stage3a_defs::{ STAGE3A_BSS_ADDRESS, STAGE3A_BSS_SIZE, STAGE3A_ENTRY, STAGE3A_INIT_ENTRY, STAGE3A_LOAD_ADDRESS, }; use self::{ ipl::{ ipl_parameter_block, ipl_pb0_pv, ipl_pb0_pv_comp, ipl_pbt_IPL_PBT_PV, ipl_pl_hdr, IPL_PARM_BLOCK_VERSION, }, stage3b_defs::{memblob, stage3b_args}, }; use super::CompTweakPrefV1; use crate::{ se_img::ImgComponent, se_img_comps::{ bootloader::stage3a_defs::stage3a_args, stage3a::Stage3a, stage3b::Stage3b, ComponentKind, }, }; /// Get the `PVIMG_PKGDATADIR` used for `pvimg` /// /// Provides the package data directory for `pvimg`. /// For release builds this requires the environment variable /// `PVIMG_PKGDATADIR` to be present at compile time. /// For debug builds this value defaults to `CARGO_MANIFEST_DIR/boot/` /// if that variable is not present. /// Should only be used by binary targets!! /// /// Collapses to a compile time constant, that is likely to be inlined by the /// compiler in release builds. macro_rules! pvimg_pkg_data { () => {{ #[cfg(debug_assertions)] match option_env!("PVIMG_PKGDATADIR") { Some(data) => data, None => concat!(env!("CARGO_MANIFEST_DIR"), "/boot/"), } #[cfg(not(debug_assertions))] env!("PVIMG_PKGDATADIR", "env 'PVIMG_PKGDATADIR' must be set for release builds. Trigger build using the s390-tools build system or export the variable yourself") }}; } fn bootloader_dir(path: Option<&PathBuf>) -> PathBuf { path.map_or_else(|| PathBuf::from(pvimg_pkg_data!()), |v| v.to_owned()) } /// Returns the path to `stage3a.bin`. pub fn stage3a_path(dir: Option<&PathBuf>) -> PathBuf { bootloader_dir(dir).join("stage3a.bin") } /// Returns the path to `stage3b_reloc.bin`. pub fn stage3b_path(dir: Option<&PathBuf>) -> PathBuf { bootloader_dir(dir).join("stage3b_reloc.bin") } /// Render stage3b "template" pub fn render_stage3a( mut stage3a: Vec, stage3a_addr: u64, se_hdr_src: &Interval, ipib_src: &Interval, ) -> Result { let stage3a_size = stage3a.len(); let stage3a_size_u64: u64 = stage3a_size.try_into()?; if stage3a_size <= 24 { return Err(Error::InvalidStage3a); } let stage3a_data_addr = stage3a_addr .checked_add(stage3a_size_u64) .ok_or(Error::UnexpectedOverflow)? - 24; assert!( se_hdr_src.start > stage3a_addr .checked_add(stage3a_size_u64) .ok_or(Error::UnexpectedOverflow)? ); // IMPORTANT: Secure Execution header must be located AFTER the stage3a // loader. let hdr_offs = se_hdr_src .start .checked_sub(stage3a_data_addr) .ok_or(Error::UnexpectedUnderflow)?; assert!( ipib_src.start > stage3a_addr .checked_add(stage3a_size_u64) .ok_or(Error::UnexpectedOverflow)? ); // IMPORTANT: IPIB must be located AFTER the stage3a loader. let ipib_offs = ipib_src .start .checked_sub(stage3a_data_addr) .ok_or(Error::UnexpectedUnderflow)?; let args = stage3a_args { hdr_offs, hdr_size: se_hdr_src.size(), ipib_offs, }; trace!("stage3a arguments: {args:#x?}"); let stage3a_args_bin = serialize_to_bytes(&args)?; assert_eq!(stage3a_args_bin.len(), 24); // Insert the stage3a arguments assert!(stage3a_size > stage3a_args_bin.len()); stage3a.splice(stage3a_size - stage3a_args_bin.len().., stage3a_args_bin); Ok(Stage3a::new(Box::new(Cursor::new(stage3a)))) } /// Render stage3b "template" pub fn render_stage3b( mut stage3b: Vec, psw: PSW, prepared_comps: &[Rc], ) -> Result { let mut args = stage3b_args { psw, ..Default::default() }; prepared_comps .iter() .filter(|comp| comp.secure_mode.is_some() && comp.kind() != ComponentKind::Stage3b) .map(|comp| { // Safety: Safe because of the filtering. let secure_mode_data = comp.secure_mode.as_ref().unwrap(); let src = comp.src.start; let size = secure_mode_data.original_size.try_into()?; match comp.kind() { ComponentKind::Cmdline => args.cmdline = memblob { src, size }, ComponentKind::Kernel => args.kernel = memblob { src, size }, ComponentKind::Ramdisk => args.initrd = memblob { src, size }, ComponentKind::Stage3a | ComponentKind::Ipib | ComponentKind::SeHdr | ComponentKind::ShortPSW | ComponentKind::ImgMetaData | ComponentKind::Stage3b => unreachable!(), } Ok(()) }) .collect::>>()?; if prepared_comps.len() > 3 { // That would mean there is a bug somewhere. unreachable!() } trace!("stage3b arguments: {args:#x?}"); let stage3b_args_bin = serialize_to_bytes(&args)?; assert_eq!(stage3b_args_bin.len(), 64); let stage3b_len = stage3b.len(); let stage3b_args_bin_len = stage3b_args_bin.len(); // Insert the stage3b arguments if stage3b_len <= stage3b_args_bin_len { return Err(Error::InvalidStage3b); } let stage3b_parms_off = stage3b_len - stage3b_args_bin_len; stage3b.splice(stage3b_parms_off.., stage3b_args_bin); Ok(Stage3b::new(Box::new(Cursor::new(stage3b)))) } pub fn create_ipib( hdr: &Interval, img_comps: Vec<(CompTweakPrefV1, Rc)>, ) -> Result { let mut components = vec![]; for (tweak_pref, src) in img_comps { components.push(ipl_pb0_pv_comp { tweak_pref: tweak_pref.to_u64(), addr: src.start, len: src.size(), }); } let comps_len = components.len(); let ipip_len = ipl_parameter_block::size(comps_len)?.try_into()?; let ipip_pv_len = ipl_pb0_pv::size(comps_len)?.try_into()?; let ipib = ipl_parameter_block { hdr: ipl_pl_hdr { len: ipip_len, flags: 0, version: IPL_PARM_BLOCK_VERSION, ..Default::default() }, pv: ipl_pb0_pv { len: ipip_pv_len, pbt: ipl_pbt_IPL_PBT_PV, version: IPL_PARM_BLOCK_PV_VERSION, num_comp: comps_len.try_into()?, pv_hdr_addr: hdr.start, pv_hdr_size: hdr.size(), components, ..Default::default() }, }; Ok(ipib) } s390-tools-2.38.0/rust/pvimg/src/se_img_comps/bootloader/000077500000000000000000000000001502674226300231535ustar00rootroot00000000000000s390-tools-2.38.0/rust/pvimg/src/se_img_comps/bootloader/ipl.rs000066400000000000000000000065011502674226300243070ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 // Based on the output of rust-bindgen 0.69.1 #![allow(nonstandard_style, unused)] use deku::{ctx::Endian, prelude::*}; use pvimg::{error::Result, misc::bytesize}; pub const IPL_FLAG_SECURE: u32 = 64; pub const IPL_RB_COMPONENT_FLAG_SIGNED: u32 = 128; pub const IPL_RB_COMPONENT_FLAG_VERIFIED: u32 = 64; pub const IPL_MAX_SUPPORTED_VERSION: u32 = 0; pub const IPL_PARM_BLOCK_VERSION: u8 = 1; pub const IPL_PARM_BLOCK_PV_VERSION: u8 = 1; pub const ipl_pbt_IPL_PBT_FCP: ipl_pbt = 0; pub const ipl_pbt_IPL_PBT_SCP_DATA: ipl_pbt = 1; pub const ipl_pbt_IPL_PBT_CCW: ipl_pbt = 2; pub const ipl_pbt_IPL_PBT_ECKD: ipl_pbt = 3; pub const ipl_pbt_IPL_PBT_NVME: ipl_pbt = 4; pub const ipl_pbt_IPL_PBT_PV: ipl_pbt = 5; pub type ipl_pbt = u8; #[repr(C)] #[derive(Debug, Default, Clone, DekuRead, DekuWrite)] #[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")] pub struct ipl_pl_hdr { pub len: u32, pub flags: u8, pub reserved1: [u8; 2_usize], pub version: u8, } #[repr(C)] #[derive(Debug, Default, Clone, DekuRead, DekuWrite)] #[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")] pub struct ipl_pb0_pv_comp { pub tweak_pref: u64, pub addr: u64, pub len: u64, } #[repr(C)] #[derive(Debug, Clone, DekuRead, DekuWrite)] #[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")] pub struct ipl_pb0_pv { pub len: u32, pub pbt: u8, pub reserved1: [u8; 3_usize], pub loadparm: [u8; 8_usize], pub reserved2: [u8; 84_usize], pub reserved3: [u8; 3_usize], pub version: u8, pub reserved4: [u8; 4_usize], pub num_comp: u32, pub pv_hdr_addr: u64, pub pv_hdr_size: u64, #[deku(count = "num_comp")] pub components: Vec, } impl Default for ipl_pb0_pv { fn default() -> Self { Self { len: Default::default(), pbt: Default::default(), reserved1: Default::default(), loadparm: Default::default(), reserved2: [0; 84], reserved3: Default::default(), version: Default::default(), reserved4: Default::default(), num_comp: Default::default(), pv_hdr_addr: Default::default(), pv_hdr_size: Default::default(), components: Default::default(), } } } #[repr(C)] #[derive(Debug, Default, Clone, DekuRead, DekuWrite)] #[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")] pub struct ipl_parameter_block { pub hdr: ipl_pl_hdr, pub pv: ipl_pb0_pv, } use std::iter; impl ipl_parameter_block { pub fn size(num_comp: usize) -> Result { let comps = iter::repeat(ipl_pb0_pv_comp::default()) .take(num_comp) .collect(); let ipib = Self { pv: ipl_pb0_pv { components: comps, ..Default::default() }, ..Default::default() }; bytesize(&ipib) } } impl ipl_pb0_pv { pub fn size(num_comp: usize) -> Result { let comp = ipl_pb0_pv_comp::default(); let comps = iter::repeat(comp).take(num_comp).collect(); let ipl = Self { components: comps, ..Default::default() }; bytesize(&ipl) } } s390-tools-2.38.0/rust/pvimg/src/se_img_comps/bootloader/stage3a_defs.rs000066400000000000000000000036771502674226300260660ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 // Based on the output of rust-bindgen 0.69.1 #![allow(nonstandard_style)] use deku::{ctx::Endian, prelude::*}; pub const IMAGE_ENTRY: u64 = 0x10000; pub const STAGE3A_INIT_ENTRY: u64 = IMAGE_ENTRY; pub const STAGE3A_ENTRY: u64 = STAGE3A_INIT_ENTRY + 0x1000; pub const STAGE3A_LOAD_ADDRESS: u64 = STAGE3A_INIT_ENTRY; pub const STAGE3A_BSS_ADDRESS: u64 = 0xc000; pub const STAGE3A_BSS_SIZE: u64 = 0x1000; #[derive(Debug, Default, Clone, DekuRead, DekuWrite)] #[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")] pub struct stage3a_args { pub hdr_offs: u64, pub hdr_size: u64, pub ipib_offs: u64, } #[test] fn bindgen_test_layout_stage3a_args() { const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 24_usize, concat!("Size of: ", stringify!(stage3a_args)) ); assert_eq!( ::std::mem::align_of::(), 8_usize, concat!("Alignment of ", stringify!(stage3a_args)) ); assert_eq!( unsafe { ::std::ptr::addr_of!((*ptr).hdr_offs) as usize - ptr as usize }, 0_usize, concat!( "Offset of field: ", stringify!(stage3a_args), "::", stringify!(hdr_offs) ) ); assert_eq!( unsafe { ::std::ptr::addr_of!((*ptr).hdr_size) as usize - ptr as usize }, 8_usize, concat!( "Offset of field: ", stringify!(stage3a_args), "::", stringify!(hdr_size) ) ); assert_eq!( unsafe { ::std::ptr::addr_of!((*ptr).ipib_offs) as usize - ptr as usize }, 16_usize, concat!( "Offset of field: ", stringify!(stage3a_args), "::", stringify!(ipib_offs) ) ); } s390-tools-2.38.0/rust/pvimg/src/se_img_comps/bootloader/stage3b_defs.rs000066400000000000000000000061411502674226300260540ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 // Based on the output of rust-bindgen 0.69.1 #![allow(non_camel_case_types, non_snake_case, nonstandard_style)] use deku::{ctx::Endian, prelude::*}; use pvimg::misc::PSW; #[derive(Debug, Default, Clone, DekuRead, DekuWrite)] #[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")] pub struct memblob { pub src: u64, pub size: u64, } #[test] fn bindgen_test_layout_memblob() { const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 16_usize, concat!("Size of: ", stringify!(memblob)) ); assert_eq!( ::std::mem::align_of::(), 8_usize, concat!("Alignment of ", stringify!(memblob)) ); assert_eq!( unsafe { ::std::ptr::addr_of!((*ptr).src) as usize - ptr as usize }, 0_usize, concat!( "Offset of field: ", stringify!(memblob), "::", stringify!(src) ) ); assert_eq!( unsafe { ::std::ptr::addr_of!((*ptr).size) as usize - ptr as usize }, 8_usize, concat!( "Offset of field: ", stringify!(memblob), "::", stringify!(size) ) ); } #[derive(Debug, Default, Clone, DekuRead, DekuWrite)] #[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")] pub struct stage3b_args { pub kernel: memblob, pub cmdline: memblob, pub initrd: memblob, pub psw: PSW, } #[test] fn bindgen_test_layout_stage3b_args() { const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); let ptr = UNINIT.as_ptr(); assert_eq!( ::std::mem::size_of::(), 64_usize, concat!("Size of: ", stringify!(stage3b_args)) ); assert_eq!( ::std::mem::align_of::(), 8_usize, concat!("Alignment of ", stringify!(stage3b_args)) ); assert_eq!( unsafe { ::std::ptr::addr_of!((*ptr).kernel) as usize - ptr as usize }, 0_usize, concat!( "Offset of field: ", stringify!(stage3b_args), "::", stringify!(kernel) ) ); assert_eq!( unsafe { ::std::ptr::addr_of!((*ptr).cmdline) as usize - ptr as usize }, 16_usize, concat!( "Offset of field: ", stringify!(stage3b_args), "::", stringify!(cmdline) ) ); assert_eq!( unsafe { ::std::ptr::addr_of!((*ptr).initrd) as usize - ptr as usize }, 32_usize, concat!( "Offset of field: ", stringify!(stage3b_args), "::", stringify!(initrd) ) ); assert_eq!( unsafe { ::std::ptr::addr_of!((*ptr).psw) as usize - ptr as usize }, 48_usize, concat!( "Offset of field: ", stringify!(stage3b_args), "::", stringify!(psw) ) ); } s390-tools-2.38.0/rust/pvimg/src/se_img_comps/cmdline.rs000066400000000000000000000042721502674226300230070ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::io::{Read, Seek, SeekFrom}; use pvimg::error::{Error, Result}; use super::{ CompReader, ComponentCheckCtx, ComponentCheckTrait, ComponentKind, ComponentTrait, ReadSeekDebug, }; #[derive(Debug)] pub struct Cmdline { comp: CompReader, last_value: Option, } impl Cmdline { pub fn new(reader: Box) -> Self { Self { comp: CompReader { reader }, last_value: None, } } } impl Read for Cmdline { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { // Make sure that the kernel cmdline always is C NUL-terminated. let size = self.comp.read(buf)?; // Store last value if size > 0 { self.last_value = Some(buf[size - 1]); return Ok(size); } if buf.is_empty() { return Ok(size); } // EOF has been reached, check for NUL-Terminator assert!(size == 0); // Was the last value a NUL-Terminator? if self.last_value.is_some_and(|x| x == b'\0') { return Ok(size); } // Store a NUL-Terminator in buf so the next `read(...)` call will stop. buf[0] = b'\0'; self.last_value = Some(buf[0]); Ok(1) } } impl Seek for Cmdline { fn seek(&mut self, pos: SeekFrom) -> std::io::Result { // Invalidate last value after seeking self.last_value = None; self.comp.seek(pos) } } impl ComponentCheckTrait for Cmdline { fn check(&mut self, ctx: &ComponentCheckCtx) -> Result<()> { let mut buf = vec![]; let size = self.read_to_end(&mut buf)?; if size > ctx.max_kernel_cmdline_size { return Err(Error::KernelCmdlineTooLarge { size, max_size: ctx.max_kernel_cmdline_size, }); } Ok(()) } fn init_ctx(&mut self, _ctx: &mut ComponentCheckCtx) -> Result<()> { Ok(()) } } impl ComponentTrait for Cmdline { fn kind(&self) -> ComponentKind { ComponentKind::Cmdline } fn secure_mode(&self) -> bool { true } } s390-tools-2.38.0/rust/pvimg/src/se_img_comps/ipib.rs000066400000000000000000000020641502674226300223140ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::io::{Read, Seek}; use pvimg::error::Result; use pvimg::secured_comp::ComponentTrait; use super::ComponentKind; use super::{CompReader, ComponentCheckCtx, ComponentCheckTrait, ReadSeekDebug}; #[derive(Debug)] pub struct Ipib(CompReader); impl Ipib { pub fn new(reader: Box) -> Self { Self(CompReader { reader }) } } impl Read for Ipib { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { self.0.read(buf) } } impl Seek for Ipib { fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result { self.0.seek(pos) } } impl ComponentCheckTrait for Ipib { fn check(&mut self, _ctx: &ComponentCheckCtx) -> Result<()> { Ok(()) } fn init_ctx(&mut self, _ctx: &mut ComponentCheckCtx) -> Result<()> { Ok(()) } } impl ComponentTrait for Ipib { fn secure_mode(&self) -> bool { false } fn kind(&self) -> ComponentKind { ComponentKind::Ipib } } s390-tools-2.38.0/rust/pvimg/src/se_img_comps/kernel.rs000066400000000000000000000056471502674226300226630ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::io::{Read, Seek, SeekFrom}; use pvimg::error::{Error, Result}; use super::{ CompReader, ComponentCheckCtx, ComponentCheckTrait, ComponentKind, ComponentTrait, ReadSeekDebug, }; #[derive(Debug)] pub struct S390Kernel(CompReader); impl S390Kernel { const ELF_MAGIC: [u8; Self::ELF_MAGIC_SIZE] = [0x7f, 0x45, 0x4c, 0x46]; const ELF_MAGIC_OFF: u64 = 0x0; const ELF_MAGIC_SIZE: usize = 4; const KERNEL_COMMAND_LINE_SIZE_ADDR: u64 = 0x10430; const KERNEL_COMMAND_LINE_SIZE_LEN: usize = 8; pub const KERNEL_ENTRY: u64 = 0x10000; pub const LEGACY_MAX_COMMAND_LINE_SIZE: usize = 896; const S390EP: [u8; Self::S390EP_SIZE] = [0x53, 0x33, 0x39, 0x30, 0x45, 0x50]; // Location of "S390EP" in a Linux binary (see arch/s390/boot/head.S) const S390EP_OFFS: u64 = 0x10008; const S390EP_SIZE: usize = 6; pub fn new(reader: Box) -> Self { Self(CompReader { reader }) } fn is_elf_file(&mut self) -> Result { self.seek(SeekFrom::Start(Self::ELF_MAGIC_OFF))?; let mut buf = [0x0_u8; Self::ELF_MAGIC_SIZE]; self.read_exact(&mut buf)?; Ok(buf == Self::ELF_MAGIC) } fn is_s390x_kernel(&mut self) -> Result { self.seek(SeekFrom::Start(Self::S390EP_OFFS))?; let mut buf = [0_u8; Self::S390EP_SIZE]; self.read_exact(&mut buf)?; Ok(buf == Self::S390EP) } fn read_max_kernel_cmdline_size(&mut self) -> Result { self.seek(SeekFrom::Start(Self::KERNEL_COMMAND_LINE_SIZE_ADDR))?; let mut buf = [0x0_u8; Self::KERNEL_COMMAND_LINE_SIZE_LEN]; self.read_exact(&mut buf).map_err(|e| match e.kind() { std::io::ErrorKind::UnexpectedEof => Error::NoS390Kernel, _ => e.into(), })?; let mut max_size = u64::from_be_bytes(buf).try_into()?; if max_size == 0 { max_size = Self::LEGACY_MAX_COMMAND_LINE_SIZE; } Ok(max_size) } } impl Read for S390Kernel { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { self.0.read(buf) } } impl Seek for S390Kernel { fn seek(&mut self, pos: SeekFrom) -> std::io::Result { self.0.seek(pos) } } impl ComponentCheckTrait for S390Kernel { fn check(&mut self, _ctx: &ComponentCheckCtx) -> Result<()> { if self.is_elf_file()? { return Err(Error::UnexpectedElfFile); } if !self.is_s390x_kernel()? { return Err(Error::NoS390Kernel); } Ok(()) } fn init_ctx(&mut self, ctx: &mut ComponentCheckCtx) -> Result<()> { ctx.max_kernel_cmdline_size = self.read_max_kernel_cmdline_size()?; Ok(()) } } impl ComponentTrait for S390Kernel { fn secure_mode(&self) -> bool { true } fn kind(&self) -> ComponentKind { ComponentKind::Kernel } } s390-tools-2.38.0/rust/pvimg/src/se_img_comps/metadata.rs000066400000000000000000000027321502674226300231530ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::io::{Cursor, Read, Seek}; use pv::{request::SeImgMetaData, static_assert}; use pvimg::error::Result; use super::{ bootloader::{STAGE3A_BSS_ADDRESS, STAGE3A_BSS_SIZE}, CompReader, ComponentCheckCtx, ComponentCheckTrait, ComponentKind, ComponentTrait, }; #[derive(Debug)] pub struct ImgMetaData(CompReader); static_assert!(ImgMetaData::OFFSET == SeImgMetaData::OFFSET); impl ImgMetaData { pub const MAX_SIZE: u64 = STAGE3A_BSS_SIZE; pub const OFFSET: u64 = STAGE3A_BSS_ADDRESS; pub fn new(ipib_off: u64, hdr_off: u64) -> Result { let data = SeImgMetaData::new_v1(hdr_off, ipib_off); let reader = Box::new(Cursor::new(data.as_bytes().to_owned())); Ok(Self(CompReader { reader })) } } impl Read for ImgMetaData { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { self.0.read(buf) } } impl Seek for ImgMetaData { fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result { self.0.seek(pos) } } impl ComponentCheckTrait for ImgMetaData { fn check(&mut self, _ctx: &ComponentCheckCtx) -> Result<()> { Ok(()) } fn init_ctx(&mut self, _ctx: &mut ComponentCheckCtx) -> Result<()> { Ok(()) } } impl ComponentTrait for ImgMetaData { fn kind(&self) -> ComponentKind { ComponentKind::ImgMetaData } fn secure_mode(&self) -> bool { false } } s390-tools-2.38.0/rust/pvimg/src/se_img_comps/ramdisk.rs000066400000000000000000000020571502674226300230250ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::io::{Read, Seek}; use pvimg::error::Result; use super::ComponentKind; use super::{CompReader, ComponentCheckCtx, ComponentCheckTrait, ComponentTrait, ReadSeekDebug}; #[derive(Debug)] pub struct Ramdisk(CompReader); impl Ramdisk { pub fn new(reader: Box) -> Self { Self(CompReader { reader }) } } impl Read for Ramdisk { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { self.0.read(buf) } } impl Seek for Ramdisk { fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result { self.0.seek(pos) } } impl ComponentCheckTrait for Ramdisk { fn check(&mut self, _ctx: &ComponentCheckCtx) -> Result<()> { Ok(()) } fn init_ctx(&mut self, _ctx: &mut ComponentCheckCtx) -> Result<()> { Ok(()) } } impl ComponentTrait for Ramdisk { fn kind(&self) -> ComponentKind { ComponentKind::Ramdisk } fn secure_mode(&self) -> bool { true } } s390-tools-2.38.0/rust/pvimg/src/se_img_comps/sehdr.rs000066400000000000000000000021271502674226300224760ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::io::{Read, Seek}; use pvimg::error::Result; use pvimg::secured_comp::ComponentTrait; use super::ComponentKind; use super::{CompReader, ComponentCheckCtx, ComponentCheckTrait, ReadSeekDebug}; #[derive(Debug)] pub struct SeHdrComp(pub CompReader); impl SeHdrComp { pub fn new(reader: Box) -> Self { Self(CompReader { reader }) } } impl Seek for SeHdrComp { fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result { self.0.seek(pos) } } impl Read for SeHdrComp { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { self.0.read(buf) } } impl ComponentCheckTrait for SeHdrComp { fn check(&mut self, _ctx: &ComponentCheckCtx) -> Result<()> { Ok(()) } fn init_ctx(&mut self, _ctx: &mut ComponentCheckCtx) -> Result<()> { Ok(()) } } impl ComponentTrait for SeHdrComp { fn kind(&self) -> ComponentKind { ComponentKind::SeHdr } fn secure_mode(&self) -> bool { false } } s390-tools-2.38.0/rust/pvimg/src/se_img_comps/shortpsw.rs000066400000000000000000000022671502674226300232670ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::io::{Read, Seek}; use pvimg::error::Result; use pvimg::secured_comp::ComponentTrait; use super::ComponentKind; use super::{CompReader, ComponentCheckCtx, ComponentCheckTrait, ReadSeekDebug}; #[derive(Debug)] pub struct ShortPSWComp(CompReader); impl ShortPSWComp { /// Offset in the Secure Execution image pub const OFFSET: u64 = 0x0; pub fn new(reader: Box) -> Self { Self(CompReader { reader }) } } impl Read for ShortPSWComp { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { self.0.read(buf) } } impl Seek for ShortPSWComp { fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result { self.0.seek(pos) } } impl ComponentCheckTrait for ShortPSWComp { fn check(&mut self, _ctx: &ComponentCheckCtx) -> Result<()> { Ok(()) } fn init_ctx(&mut self, _ctx: &mut ComponentCheckCtx) -> Result<()> { Ok(()) } } impl ComponentTrait for ShortPSWComp { fn kind(&self) -> ComponentKind { ComponentKind::ShortPSW } fn secure_mode(&self) -> bool { false } } s390-tools-2.38.0/rust/pvimg/src/se_img_comps/stage3a.rs000066400000000000000000000021101502674226300227100ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::io::{Read, Seek}; use pvimg::error::Result; use pvimg::secured_comp::ComponentTrait; use super::ComponentKind; use super::{CompReader, ComponentCheckCtx, ComponentCheckTrait, ReadSeekDebug}; #[derive(Debug)] pub struct Stage3a(CompReader); impl Stage3a { pub fn new(reader: Box) -> Self { Self(CompReader { reader }) } } impl Read for Stage3a { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { self.0.read(buf) } } impl Seek for Stage3a { fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result { self.0.seek(pos) } } impl ComponentCheckTrait for Stage3a { fn check(&mut self, _ctx: &ComponentCheckCtx) -> Result<()> { Ok(()) } fn init_ctx(&mut self, _ctx: &mut ComponentCheckCtx) -> Result<()> { Ok(()) } } impl ComponentTrait for Stage3a { fn kind(&self) -> ComponentKind { ComponentKind::Stage3a } fn secure_mode(&self) -> bool { false } } s390-tools-2.38.0/rust/pvimg/src/se_img_comps/stage3b.rs000066400000000000000000000021121502674226300227130ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::io::{Read, Seek}; use pvimg::error::Result; use pvimg::secured_comp::ComponentTrait; use super::ComponentKind; use super::{CompReader, ComponentCheckCtx, ComponentCheckTrait, ReadSeekDebug}; #[derive(Debug)] pub struct Stage3b(CompReader); impl Stage3b { pub fn new(reader: Box) -> Self { Self(CompReader::new(reader)) } } impl Read for Stage3b { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { self.0.read(buf) } } impl Seek for Stage3b { fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result { self.0.seek(pos) } } impl ComponentCheckTrait for Stage3b { fn check(&mut self, _ctx: &ComponentCheckCtx) -> Result<()> { Ok(()) } fn init_ctx(&mut self, _ctx: &mut ComponentCheckCtx) -> Result<()> { Ok(()) } } impl ComponentTrait for Stage3b { fn kind(&self) -> ComponentKind { ComponentKind::Stage3b } fn secure_mode(&self) -> bool { true } } s390-tools-2.38.0/rust/pvsecret/000077500000000000000000000000001502674226300162775ustar00rootroot00000000000000s390-tools-2.38.0/rust/pvsecret/.gitignore000066400000000000000000000000141502674226300202620ustar00rootroot00000000000000!Cargo.lock s390-tools-2.38.0/rust/pvsecret/Cargo.toml000066400000000000000000000012261502674226300202300ustar00rootroot00000000000000[package] name = "pvsecret" version = "0.12.0" edition.workspace = true license.workspace = true rust-version.workspace = true [lints] workspace = true [dependencies] anyhow = { version = "1.0.95", features = ["std"] } clap = { version ="4.5", features = ["derive", "wrap_help"]} log = { version = "0.4.25", features = ["std", "release_max_level_debug"] } serde_yaml = "0.9" pv = { path = "../pv" , package = "s390_pv" } utils = { path = "../utils"} [build-dependencies] clap = { version ="4.5", features = ["derive", "wrap_help"]} clap_complete = "4.5" log = { version = "0.4", features = ["std", "release_max_level_debug"] } utils = { path = "../utils" } s390-tools-2.38.0/rust/pvsecret/README.md000066400000000000000000000373341502674226300175700ustar00rootroot00000000000000 # pvsecret ## Synopsis `pvsecret [OPTIONS] ` ## Description Use **pvsecret** to manage secrets for IBM Secure Execution guests. **pvsecret** can **create** add-secret requests on any architecture. On s390x systems, use **pvsecret** to **add** the secrets to the ultravisor secret store, **list** all secrets in the secret store, or **lock** the secret store to prevent any modifications in the future. The ultravisor secret store stores secrets for the IBM Secure Execution guest. The secret store is cleared on guest reboot. Create requests only on trusted systems that are not the IBM Secure Execution guest where you want to inject the secrets. This approach prevents the secrets from being in cleartext on the guest. For extra safety, do an attestation with **pvattest** of your guest beforehand, and include the configuration UID in the secret request using **--cuid**. Refer to **pvsecret-add** for more information. For all certificates, revocation lists, and host-key documents, both the PEM and DER input formats are supported. ## Commands Overview - **create**

    Create a new add-secret request
- **add**
    Submit an add-secret request to the Ultravisor (s390x only)
- **lock**
    Lock the secret-store (s390x only)
- **list**
    List all ultravisor secrets (s390x only)
- **verify**
    Verify that an add-secret request is sane
- **retrieve**
    Retrieve a secret from the UV secret store (s390x only)
## Options `-v`, `--verbose`
    Provide more detailed output.
`-q`, `--quiet`
    Provide less output.
`--version`
    Print version information and exit.
`-h`, `--help`
    Print help (see a summary with '-h').
## pvsecret create ### Synopsis `pvsecret create [OPTIONS] --host-key-document --hdr --output <--no-verify|--cert > ` ### Description Create add-secret requests for IBM Secure Execution guests. Only create these requests in a trusted environment, such as your workstation. The **pvattest create** command creates a randomly generated key to protect the request. The generated requests can then be added on an IBM Secure Execution guest using **pvsecret add**. The guest can then use the secrets with the use case depending on the secret type. Such a request is bound to a specific IBM Secure Execution image specified with **--hdr**. Optionally, the request can be bound to a specific instance when bound to the Configuration Unique ID from **pvattest** using **--cuid** ### Commands Overview - **meta**
    Create a meta secret
- **association**
    Create an association secret
- **retrievable**
    Create a retrievable secret
### Options `-k`, `--host-key-document `
    Use FILE as a host-key document. Can be specified multiple times and must be specified at least once.
`--no-verify`
    Disable the host-key document verification. Does not require the host-key documents to be valid. Do not use for a production request unless you verified the host-key document beforehand.
`-C`, `--cert `
    Use FILE as a certificate to verify the host-key or keys. The certificates are used to establish a chain of trust for the verification of the host-key documents. Specify this option twice to specify the IBM Z signing key and the intermediate CA certificate (signed by the root CA).
`--crl `
    Use FILE as a certificate revocation list (CRL). The list is used to check whether a certificate of the chain of trust is revoked. Specify this option multiple times to use multiple CRLs.
`--offline`
    Make no attempt to download CRLs.
`--root-ca `
    Use FILE as the root-CA certificate for the verification. If omitted, the system wide-root CAs installed on the system are used. Use this only if you trust the specified certificate.
`--hdr `
    Specifies the header of the guest image. Can be an IBM Secure Execution image created by 'pvimg/genprotimg' or an extracted IBM Secure Execution header.
`-f`, `--force`
    Force the generation of add-secret requests on IBM Secure Execution guests. If the program detects that it is running on an IBM Secure Execution guest, it denies the generation of add-secret requests. The force flag overwrites this behavior.
`-o`, `--output `
    Write the generated request to FILE.
`--extension-secret `
    Use the content of FILE as an extension secret. The file must be exactly 32 bytes long. If this request is the first, all subsequent requests must have the same extension secret. Only makes sense if bit 1 of the secret control flags of the IBM Secure Execution header is 0. Otherwise the ultravisor rejects the request.
`--cck `
    Use the content of FILE as the customer-communication key (CCK) to derive the extension secret. The file must contain exactly 32 bytes of data. If the target guest was started with bit 1 of the secret control flag set, the ultravisor also derives the secret from the CCK. Otherwise, the ultravisor interprets the extension secret as a normal one. This still works if you use the same CCK for all requests.
`--cuid-hex `
    Use HEXSTRING as the Configuration Unique ID. Must be a hex 128-bit unsigned big endian number string. Leading zeros must be provided. If specified, the value must match with the Config-UID from the attestation result of that guest. If not specified, the CUID will be ignored by the ultravisor during the verification of the request.
`--cuid `
    Use the content of FILE as the Configuration Unique ID. The file must contain exactly 128 bit of data or a yaml with a `cuid` entry. If specified, the value must match the Config-UID from the attestation result of that guest. If not specified, the CUID will be ignored by the Ultravisor during the verification of the request.
`--flags `
    Flags for the add-secret request. Possible values: - **disable-dump**: Disables host-initiated dumping for the target guest instance.
`--user-data `
    Use the content of FILE as user-data. Passes user data defined in FILE through the add-secret request to the ultravisor. The user data can be up to 512 bytes of arbitrary data, and the maximum size depends on the size of the user-signing key: - No key: user data can be 512 bytes. - EC(secp521r1) or RSA 2048 keys: user data can be 256 bytes. - RSA 3072 key: user data can be 128 bytes. The firmware ignores this data, but the request tag protects the user-data. Optional. No user-data by default.
`--user-sign-key `
    Use the content of FILE as user signing key. Adds a signature calculated from the key in FILE to the add-secret request. The file must be in DER or PEM format containing a private key. Supported are RSA 2048 & 3072-bit and EC(secp521r1) keys. The firmware ignores the content, but the request tag protects the signature. The user-signing key signs the request. The location of the signature is filled with zeros during the signature calculation. The request tag also secures the signature. See man pvsecret verify for more details. Optional. No signature by default.
`--use-name`
    Do not hash the name, use it directly as secret ID. Ignored for meta-secrets.
`-h`, `--help`
    Print help (see a summary with '-h').
### pvsecret create meta #### Synopsis `pvsecret create meta` #### Description Create a meta secret. Use a meta secret to carry flags to the ultravisor without having to provide an actual secret value. Meta secrets do not appear in the list of secrets. ### pvsecret create association #### Synopsis `pvsecret create association [OPTIONS] ` #### Description Create an association secret. Use an association secret to connect a trusted I/O device to a guest. The 'pvapconfig' tool provides more information about association secrets. #### Arguments ``
    String that identifies the new secret. The actual secret is set with '--input-secret'. The name is saved in `NAME.yaml` with white-spaces mapped to `_`.
#### Options `--stdout`
    Print the hashed name to stdout. The hashed name is not written to `NAME.yaml`
`--input-secret `
    Path from which to read the plaintext secret. Uses a random secret if not specified.
`--output-secret `
    Save the generated secret as plaintext in SECRET-FILE. The generated secret can be used to generate add-secret requests for a different guest with the same secret using '--input-secret'. Destroy the secret when it is not used anymore.
`-h`, `--help`
    Print help (see a summary with '-h').
### pvsecret create retrievable #### Synopsis `pvsecret create retrievable [OPTIONS] --secret --type ` `pvsecret create retr [OPTIONS] --secret --type ` #### Description Create a retrievable secret. A retrievable secret is stored in the per-guest storage of the Ultravisor. A SE-guest can retrieve the secret at runtime and use it. All retrievable secrets, but the plaintext secret, are retrieved as wrapped/protected key objects and only usable inside the current, running SE-guest instance. #### Arguments ``
    String that identifies the new secret. The actual secret is set with '--secret'. The name is saved in `NAME.yaml` with white-spaces mapped to `_`.
#### Options `--stdout`
    Print the hashed name to stdout. The hashed name is not written to `NAME.yaml`
`--secret `
    Use SECRET-FILE as retrievable secret.
`--type `
    Specify the secret type. Limitations to the input data apply depending on the secret type. Possible values: - **plain**: A plaintext secret. Can be any file up to 8190 bytes long. - **aes**: An AES key. Must be a plain byte file 128, 192, or 256 bit long. - **aes-xts**: An AES-XTS key. Must be a plain byte file 512, or 1024 bit long. - **hmac-sha**: A HMAC-SHA key. Must be a plain byte file 512, or 1024 bit long. Special care is required when creating HMAC-SHA keys. For more Information refer to the DESCRIPTION section of the man file. - **ec**: An elliptic curve private key. Must be a PEM or DER file.
`-h`, `--help`
    Print help (see a summary with '-h').
## pvsecret add ### Synopsis `pvsecret add ` ### Description Submit an add-secret request to the Ultravisor (s390x only). Perform an add-secret request using a previously generated add-secret request. Only available on s390x. ### Arguments ``
    Specify the request to be sent.
## pvsecret lock ### Synopsis `pvsecret lock` ### Description Lock the secret-store (s390x only). Lock the secret store (s390x only). After this command executed successfully, all subsequent add-secret requests will fail. Only available on s390x. ## pvsecret list ### Synopsis `pvsecret list [OPTIONS] [FILE]` ### Description List all ultravisor secrets (s390x only). Lists the IDs of all non-null secrets currently stored in the ultravisor for the currently running IBM Secure Execution guest. Only available on s390x. ### Arguments ``
    Store the result in FILE. Default value: '-'
### Options `--format `
    Define the output format of the list. Default value: 'human' Possible values: - **human**: Human-focused, non-parsable output format. - **yaml**: Use yaml format. - **bin**: Use the format the ultravisor uses to pass the list.
`-h`, `--help`
    Print help (see a summary with '-h').
## pvsecret verify ### Synopsis `pvsecret verify [OPTIONS] ` ### Description Verifies that the given request is an Add-Secret request by testing for some values to be present. If the request contains signed user-data, the signature is verified with the provided key. Outputs the arbitrary user-data. All data in the request is in big endian. `verify` checks the following: - The first 6 bytes of the request are equal to: `B6173 7263 624d | asrcbM` - The sizes in the request header are sane and do not point out of the file - The request version is supported by the binary - If user-data contains a signature, verify the signature using a public key The content of bytes 6&7 of the request define which kind of user-data the request contains. - **0x0000** `no user-data (512 bytes zero)` - **0x0001** `512 bytes user-data` - **0x0002** `265 bytes user-data| 139 bytes ecdsa signature | 5 bytes reserved | 2 bytes signature size | ...` - **0x0003** `256 bytes user-data | 256 bytes rsa2048 signature` - **0x0004** `128 bytes user-data | 384 bytes rsa3072 signature` The actual user-data may be less than the capacity. If less data was provided during `create` zeros are appended. For type 2-4 The signature is calculated as follows: 1) The request is generated with the user-data in place and zeros for the signature data. 2) The signature is calculated for the request. The signature signs the authenticated data and the encrypted data, but not the request tag. I.e. the signature signs the whole request but the last 16 bytes a,d with the signature bytes set to zero. 3) The signature is inserted to its location in the request. 4) The request GCM tag is calculated. The verification process works as follows: 1) copy the signature to a buffer 2) overwrite the signature with zeros 3) verify the signature of the request but the last 16 bytes ### Arguments ``
    Specify the request to be checked.
### Options `--user-cert `
    Certificate containing a public key used to verify the user data signature. Specifies a public key used to verify the user-data signature. The file must be a X509 certificate in DSA or PEM format. The certificate must hold the public EC, RSA 2048, or RSA 3072 key corresponding to the private user-key used during `create`. No chain of trust is established. Ensuring that the certificate can be trusted is the responsibility of the user. The EC key must use the NIST/SECG curve over a 521 bit prime field (secp521r1).
`-o`, `--output `
    Store the result in FILE If the request contained abirtary user-data the output contains this user-data with padded zeros if available. Default value: '-'
`-h`, `--help`
    Print help (see a summary with '-h').
## pvsecret retrieve ### Synopsis `pvsecret retrieve [OPTIONS] ` `pvsecret retr [OPTIONS] ` ### Description Retrieve a secret from the UV secret store (s390x only) ### Arguments ``
    Specify the secret ID to be retrieved. Input type depends on '--inform'. If `yaml` (default) is specified, it must be a yaml created by the create subcommand of this tool. If `hex` is specified, it must be a 32 byte handle encodes in hexadecimal. Leading zeros are required. If there are multiple secrets in the store with the same Id there are no guarantees on which specific secret is retrieved. Use --inform=idx to make sure a specific secret is retrieved.
### Options `-o`, `--output `
    Specify the output path to place the secret value. Default value: '-'
`--inform `
    Define input type for the Secret ID. Default value: 'yaml' Possible values: - **yaml**: Use a yaml file. - **hex**: Use a hex string. - **name**: Use a name-string. Will hash it if no secret with the name found. - **idx**: Use the secret-index (base 10) instead of the secret-ID.
`--outform `
    Define the output format for the retrieved secret. Default value: 'pem' Possible values: - **pem**: Write the secret as PEM. - **bin**: Write the secret in binary.
`-h`, `--help`
    Print help (see a summary with '-h').
s390-tools-2.38.0/rust/pvsecret/build.rs000066400000000000000000000013051502674226300177430ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 // it under the terms of the MIT license. See LICENSE for details. #![allow(missing_docs)] use clap_complete::{generate_to, Shell}; use std::env; use std::io::Error; include!("src/cli.rs"); fn main() -> Result<(), Error> { let outdir = env::var_os("OUT_DIR").unwrap(); let crate_name = env!("CARGO_PKG_NAME"); let mut cmd = CliOptions::command(); for &shell in Shell::value_variants() { generate_to(shell, &mut cmd, crate_name, &outdir)?; } println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=src/cli.rs"); println!("cargo:rerun-if-changed=../utils/src/cli.rs"); Ok(()) } s390-tools-2.38.0/rust/pvsecret/man/000077500000000000000000000000001502674226300170525ustar00rootroot00000000000000s390-tools-2.38.0/rust/pvsecret/man/pvsecret-add.1000066400000000000000000000012761502674226300215230ustar00rootroot00000000000000.\" Copyright 2023, 2024 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH "PVSECRET-ADD" "1" "2024-12-19" "s390-tools" "UV-Secret Manual" .nh .ad l .SH NAME pvsecret-add \- Submit an add-secret request to the Ultravisor (s390x only) .SH SYNOPSIS .nf .fam C pvsecret add .fam C .fi .SH DESCRIPTION Perform an add\-secret request using a previously generated add\-secret request. Only available on s390x. .SH OPTIONS .PP .RS 4 Specify the request to be sent. .RE .RE .PP \-h, \-\-help .RS 4 Print help (see a summary with \fB\-h\fR). .RE .RE .SH "SEE ALSO" .sp \fBpvsecret\fR(1) s390-tools-2.38.0/rust/pvsecret/man/pvsecret-create-association.1000066400000000000000000000026651502674226300245530ustar00rootroot00000000000000.\" Copyright 2023, 2024 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH "PVSECRET-CREATE-ASSOCIATION" "1" "2024-12-19" "s390-tools" "UV-Secret Manual" .nh .ad l .SH NAME pvsecret-create-association \- Create an association secret .SH SYNOPSIS .nf .fam C pvsecret create association [OPTIONS] .fam C .fi .SH DESCRIPTION Use an association secret to connect a trusted I/O device to a guest. The \fBpvapconfig\fR tool provides more information about association secrets. .SH OPTIONS .PP .RS 4 String that identifies the new secret. The actual secret is set with \fB\-\-input\-secret\fR. The name is saved in `NAME.yaml` with white\-spaces mapped to `_`. .RE .RE .PP \-\-stdout .RS 4 Print the hashed name to stdout. The hashed name is not written to `NAME.yaml` .RE .RE .PP \-\-input\-secret .RS 4 Path from which to read the plaintext secret. Uses a random secret if not specified. .RE .RE .PP \-\-output\-secret .RS 4 Save the generated secret as plaintext in SECRET\-FILE. The generated secret can be used to generate add\-secret requests for a different guest with the same secret using \fB\-\-input\-secret\fR. Destroy the secret when it is not used anymore. .RE .RE .PP \-h, \-\-help .RS 4 Print help (see a summary with \fB\-h\fR). .RE .RE .SH "SEE ALSO" .sp \fBpvsecret\fR(1) \fBpvsecret-create\fR(1) s390-tools-2.38.0/rust/pvsecret/man/pvsecret-create-meta.1000066400000000000000000000011351502674226300231540ustar00rootroot00000000000000.\" Copyright 2023, 2024 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH "PVSECRET-CREATE-META" "1" "2024-12-19" "s390-tools" "UV-Secret Manual" .nh .ad l .SH NAME pvsecret-create-meta \- Create a meta secret .SH SYNOPSIS .nf .fam C pvsecret create meta .fam C .fi .SH DESCRIPTION Use a meta secret to carry flags to the ultravisor without having to provide an actual secret value. Meta secrets do not appear in the list of secrets. .SH "SEE ALSO" .sp \fBpvsecret\fR(1) \fBpvsecret-create\fR(1) s390-tools-2.38.0/rust/pvsecret/man/pvsecret-create-retrievable.1000066400000000000000000000076751502674226300245510ustar00rootroot00000000000000.\" Copyright 2024, 2025 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH "PVSECRET-CREATE-RETRIEVABLE" "1" "2025-02-28" "s390-tools" "UV-Secret Manual" .nh .ad l .SH NAME pvsecret-create-retrievable \- Create a retrievable secret .SH SYNOPSIS .nf .fam C pvsecret create retrievable [OPTIONS] --secret --type pvsecret create retr [OPTIONS] --secret --type .fam C .fi .SH DESCRIPTION A retrievable secret is stored in the per\-guest storage of the Ultravisor. A SE\-guest can retrieve the secret at runtime and use it. All retrievable secrets, but the plaintext secret, are retrieved as wrapped/protected key objects and only usable inside the current, running SE\-guest instance. The input file may contain up to 8190 bytes for the plaintext secret. For the symmetric keys (AES, AES\-XTS, HMAC) the file must contain a byte pattern for the key with the key\-size as file size. For the EC private keys the file must be either in PEM or DER format and contain an \fBEC PRIVATE KEY\fP with one of the following curves: secp256r1, secp384r1, secp521r1, ed25519, or ed448. .PP \fBHMAC\-SHA preprocessing\fP .RS 2 The \fBHMAC\-SHA\fP key supplied in the plain bytes file is the key \fBK_0\fP as of \fBFIPS\-198\-1\fP, i.e. the key \fBK\fP after any necessary pre\-processing. The pre\-processing must be performed by the user prior to creating the retrievable secret. .PP Pre\-processing means that if the key \fBK\fP is shorter than the block size of the to\-be\-used HMAC digest, then the key must be padded with binary zeros to the right up to the block size. The block size of SHA\-224 and SHA\-256 is 512 bits (64 bytes) and the bock size of SHA\-384 and SHA\-512 is 1024 bits (128 bytes). Such padding can for example be achieved by using the \fBtruncate\fP command with the desired size in bytes, e.g. \fB'truncate \-\-size 64 '\fP for creating a \fBK_0\fP key for HMAC\-SHA\-224 and HMAC\-SHA\-256. .PP In case key \fBK\fP is longer than the block size of the to\-be\-used HMAC digest, then key \fBK\fP must first be hashed with the to\-be\-used HMAC digest, and the result must then be padded with binary zeros to the right up to the block size of the digest. This can be achieved by using the following OpenSSL command followed by the \fBtruncate\fP command: \fB'openssl sha256 \-binary \-out '\fP and then \fB'truncate \-\-size 64 '\fP for creating a \fBK_0\fP key for HMAC\-SHA\-256. .PP \fBATTENTION:\fP The digest used for hashing the key \fBK\fP must be the exact same as the later to\-be\-used HMAC digest! If the pre\-processing and the HMAC calculation use different digests, then a wrong MAC is calculated! .RE .SH OPTIONS .PP .RS 4 String that identifies the new secret. The actual secret is set with \fB\-\-secret\fR. The name is saved in `NAME.yaml` with white\-spaces mapped to `_`. .RE .RE .PP \-\-stdout .RS 4 Print the hashed name to stdout. The hashed name is not written to `NAME.yaml` .RE .RE .PP \-\-secret .RS 4 Use SECRET\-FILE as retrievable secret. .RE .RE .PP \-\-type .RS 4 Specify the secret type. Limitations to the input data apply depending on the secret type. Possible values: .RS 4 \- \fBplain\fP: A plaintext secret. Can be any file up to 8190 bytes long. \- \fBaes\fP: An AES key. Must be a plain byte file 128, 192, or 256 bit long. \- \fBaes-xts\fP: An AES-XTS key. Must be a plain byte file 256, or 512 bit long. \- \fBhmac-sha\fP: A HMAC-SHA key. Must be a plain byte file 512, or 1024 bit long. Special care is required when creating HMAC-SHA keys. For more Information refer to the DESCRIPTION section of the man file. \- \fBec\fP: An elliptic curve private key. Must be a PEM or DER file. .RE .RE .PP \-h, \-\-help .RS 4 Print help (see a summary with \fB\-h\fR). .RE .RE .SH "SEE ALSO" .sp \fBpvsecret\fR(1) \fBpvsecret-create\fR(1) s390-tools-2.38.0/rust/pvsecret/man/pvsecret-create-update-cck.1000066400000000000000000000013141502674226300242450ustar00rootroot00000000000000.\" Copyright 2025 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH "PVSECRET-CREATE-UPDATE-CCK" "1" "2025-02-19" "s390-tools" "UV-Secret Manual" .nh .ad l .SH NAME pvsecret-create-update-cck \- Update customer communication key. .SH SYNOPSIS .nf .fam C pvsecret create update-cck [OPTIONS] \-\-secret .fam C .fi .SH DESCRIPTION Insert a customer communication key into a guest. .SH OPTIONS .PP \-\-secret .RS 4 Use CCK\-FILE as new CCK .RE .RE .PP \-h, \-\-help .RS 4 Print help (see a summary with \fB\-h\fR). .RE .RE .SH "SEE ALSO" .sp \fBpvsecret\fR(1) \fBpvsecret-create\fR(1) s390-tools-2.38.0/rust/pvsecret/man/pvsecret-create.1000066400000000000000000000145171502674226300222400ustar00rootroot00000000000000.\" Copyright 2023, 2024 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH "PVSECRET-CREATE" "1" "2025-04-25" "s390-tools" "UV-Secret Manual" .nh .ad l .SH NAME pvsecret-create \- Create a new add-secret request .SH SYNOPSIS .nf .fam C pvsecret create [OPTIONS] --host-key-document --hdr --output <--no-verify|--cert > .fam C .fi .SH DESCRIPTION Create add-secret requests for IBM Secure Execution guests. Only create these requests in a trusted environment, such as your workstation. The \fBpvattest create\fR command creates a randomly generated key to protect the request. The generated requests can then be added on an IBM Secure Execution guest using \fBpvsecret add\fR. The guest can then use the secrets with the use case depending on the secret type. Such a request is bound to a specific IBM Secure Execution image specified with \fB--hdr\fR. Optionally, the request can be bound to a specific instance when bound to the Configuration Unique ID from \fBpvattest\fR using \fB--cuid\fR .SH "PVSECRET CREATE COMMANDS" .PP \fBpvsecret create-meta(1)\fR .RS 4 Create a meta secret .RE .PP \fBpvsecret create-association(1)\fR .RS 4 Create an association secret .RE .PP \fBpvsecret create-retrievable(1)\fR .RS 4 Create a retrievable secret .RE \fBpvsecret create-update-cck(1)\fR .RS 4 Update customer communication key .RE .SH OPTIONS .PP \-k, \-\-host\-key\-document .RS 4 Use FILE as a host\-key document. Can be specified multiple times and must be specified at least once. .RE .RE .PP \-\-no\-verify .RS 4 Disable the host\-key document verification. Does not require the host\-key documents to be valid. Do not use for a production request unless you verified the host\-key document beforehand. .RE .RE .PP \-C, \-\-cert .RS 4 Use FILE as a certificate to verify the host\-key or keys. The certificates are used to establish a chain of trust for the verification of the host\-key documents. Specify this option twice to specify the IBM Z signing key and the intermediate CA certificate (signed by the root CA). .RE .RE .PP \-\-crl .RS 4 Use FILE as a certificate revocation list (CRL). The list is used to check whether a certificate of the chain of trust is revoked. Specify this option multiple times to use multiple CRLs. .RE .RE .PP \-\-offline .RS 4 Make no attempt to download CRLs. .RE .RE .PP \-\-root\-ca .RS 4 Use FILE as the root\-CA certificate for the verification. If omitted, the system wide\-root CAs installed on the system are used. Use this only if you trust the specified certificate. .RE .RE .PP \-\-hdr .RS 4 Specifies the header of the guest image. Can be an IBM Secure Execution image created by \fBpvimg/genprotimg\fR or an extracted IBM Secure Execution header. .RE .RE .PP \-f, \-\-force .RS 4 Force the generation of add\-secret requests on IBM Secure Execution guests. If the program detects that it is running on an IBM Secure Execution guest, it denies the generation of add\-secret requests. The force flag overwrites this behavior. .RE .RE .PP \-o, \-\-output .RS 4 Write the generated request to FILE. .RE .RE .PP \-\-extension\-secret .RS 4 Use the content of FILE as an extension secret. The file must be exactly 32 bytes long. If this request is the first, all subsequent requests must have the same extension secret. Only makes sense if bit 1 of the secret control flags of the IBM Secure Execution header is 0. Otherwise the ultravisor rejects the request. .RE .RE .PP \-\-cck .RS 4 Use the content of FILE as the customer\-communication key (CCK) to derive the extension secret. The file must contain exactly 32 bytes of data. If the target guest was started with bit 1 of the secret control flag set, the ultravisor also derives the secret from the CCK. Otherwise, the ultravisor interprets the extension secret as a normal one. This still works if you use the same CCK for all requests. .RE .RE .PP \-\-cuid\-hex .RS 4 Use HEXSTRING as the Configuration Unique ID. Must be a hex 128\-bit unsigned big endian number string. Leading zeros must be provided. If specified, the value must match with the Config\-UID from the attestation result of that guest. If not specified, the CUID will be ignored by the ultravisor during the verification of the request. .RE .RE .PP \-\-cuid .RS 4 Use the content of FILE as the Configuration Unique ID. The file must contain exactly 128 bit of data or a yaml with a `cuid` entry. If specified, the value must match the Config\-UID from the attestation result of that guest. If not specified, the CUID will be ignored by the Ultravisor during the verification of the request. .RE .RE .PP \-\-flags .RS 4 Flags for the add\-secret request. Possible values: .RS 4 \- \fBdisable-dump\fP: Disables host-initiated dumping for the target guest instance. .RE .RE .PP \-\-user\-data .RS 4 Use the content of FILE as user\-data. Passes user data defined in FILE through the add\-secret request to the ultravisor. The user data can be up to 512 bytes of arbitrary data, and the maximum size depends on the size of the user\-signing key: \- No key: user data can be 512 bytes. \- EC(secp521r1) or RSA 2048 keys: user data can be 256 bytes. \- RSA 3072 key: user data can be 128 bytes. The firmware ignores this data, but the request tag protects the user\-data. Optional. No user\-data by default. .RE .RE .PP \-\-user\-sign\-key .RS 4 Use the content of FILE as user signing key. Adds a signature calculated from the key in FILE to the add\-secret request. The file must be in DER or PEM format containing a private key. Supported are RSA 2048 & 3072\-bit and EC(secp521r1) keys. The firmware ignores the content, but the request tag protects the signature. The user\-signing key signs the request. The location of the signature is filled with zeros during the signature calculation. The request tag also secures the signature. See man pvsecret verify for more details. Optional. No signature by default. .RE .RE .PP \-\-use\-name .RS 4 Do not hash the name, use it directly as secret ID. Ignored for meta\-secrets. .RE .RE .PP \-h, \-\-help .RS 4 Print help (see a summary with \fB\-h\fR). .RE .RE .SH "SEE ALSO" .sp \fBpvsecret\fR(1) \fBpvsecret-create-meta\fR(1) \fBpvsecret-create-association\fR(1) \fBpvsecret-create-retrievable\fR(1) s390-tools-2.38.0/rust/pvsecret/man/pvsecret-list.1000066400000000000000000000020101502674226300217310ustar00rootroot00000000000000.\" Copyright 2023, 2024 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH "PVSECRET-LIST" "1" "2024-12-19" "s390-tools" "UV-Secret Manual" .nh .ad l .SH NAME pvsecret-list \- List all ultravisor secrets (s390x only) .SH SYNOPSIS .nf .fam C pvsecret list [OPTIONS] [FILE] .fam C .fi .SH DESCRIPTION Lists the IDs of all non\-null secrets currently stored in the ultravisor for the currently running IBM Secure Execution guest. Only available on s390x. .SH OPTIONS .PP .RS 4 Store the result in FILE. [default: '-'] .RE .RE .PP \-\-format .RS 4 Define the output format of the list. [default: 'human'] Possible values: .RS 4 \- \fBhuman\fP: Human-focused, non-parsable output format. \- \fByaml\fP: Use yaml format. \- \fBbin\fP: Use the format the ultravisor uses to pass the list. .RE .RE .PP \-h, \-\-help .RS 4 Print help (see a summary with \fB\-h\fR). .RE .RE .SH "SEE ALSO" .sp \fBpvsecret\fR(1) s390-tools-2.38.0/rust/pvsecret/man/pvsecret-lock.1000066400000000000000000000010651502674226300217170ustar00rootroot00000000000000.\" Copyright 2024 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH "PVSECRET-LOCK" "1" "2024-12-19" "s390-tools" "UV-Secret Manual" .nh .ad l .SH NAME pvsecret-lock \- Lock the secret-store (s390x only) .SH SYNOPSIS .nf .fam C pvsecret lock .fam C .fi .SH DESCRIPTION Lock the secret store (s390x only). After this command executed successfully, all subsequent add\-secret requests will fail. Only available on s390x. .SH "SEE ALSO" .sp \fBpvsecret\fR(1) s390-tools-2.38.0/rust/pvsecret/man/pvsecret-retrieve.1000066400000000000000000000040661502674226300226200ustar00rootroot00000000000000.\" Copyright 2024, 2025 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH "PVSECRET-RETRIEVE" "1" "2025-04-17" "s390-tools" "UV-Secret Manual" .nh .ad l .SH NAME pvsecret-retrieve \- Retrieve a secret from the UV secret store (s390x only) .SH SYNOPSIS .nf .fam C pvsecret retrieve [OPTIONS] pvsecret retr [OPTIONS] .fam C .fi .SH DESCRIPTION Retrieve a secret from the UV secret store (s390x only). Retrieves a retrievable secret from the UV\-storage of the guest by its ID. The ID may be provided as yaml file or as 32 byte hex\-string. The secret is written as PEM file. For Plaintext secret \fBPLAINTEXT SECRET\fP is used as PEM name and for protected keys the PEM name \fBIBM PROTECTED KEY\fP is used. .SH OPTIONS .PP .RS 4 Specify the secret ID to be retrieved. Input type depends on \fB\-\-inform\fR. If `yaml` (default) is specified, it must be a yaml created by the create subcommand of this tool. If `hex` is specified, it must be a 32 byte handle encodes in hexadecimal. Leading zeros are required. If there are multiple secrets in the store with the same Id there are no guarantees on which specific secret is retrieved. Use \-\-inform=idx to make sure a specific secret is retrieved. .RE .RE .PP \-o, \-\-output .RS 4 Specify the output path to place the secret value. [default: '-'] .RE .RE .PP \-\-inform .RS 4 Define input type for the Secret ID. [default: 'yaml'] Possible values: .RS 4 \- \fByaml\fP: Use a yaml file. \- \fBhex\fP: Use a hex string. \- \fBname\fP: Use a name-string. Will hash it if no secret with the name found. \- \fBidx\fP: Use the secret-index (base 10) instead of the secret-ID. .RE .RE .PP \-\-outform .RS 4 Define the output format for the retrieved secret. [default: 'pem'] Possible values: .RS 4 \- \fBpem\fP: Write the secret as PEM. \- \fBbin\fP: Write the secret in binary. .RE .RE .PP \-h, \-\-help .RS 4 Print help (see a summary with \fB\-h\fR). .RE .RE .SH "SEE ALSO" .sp \fBpvsecret\fR(1) s390-tools-2.38.0/rust/pvsecret/man/pvsecret-verify.1000066400000000000000000000105711502674226300222750ustar00rootroot00000000000000.\" Copyright 2023, 2024 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH "PVSECRET-VERIFY" "1" "2024-12-19" "s390-tools" "UV-Secret Manual" .nh .ad l .SH NAME pvsecret-verify \- Verify that an add-secret request is sane .SH SYNOPSIS .nf .fam C pvsecret verify [OPTIONS] .fam C .fi .SH DESCRIPTION .PP Verifies that the given request is an Add-Secret request by testing for some values to be present. If the request contains signed user-data, the signature is verified with the provided key. Outputs the arbitrary user-data. All data in the request is in big endian. .PP \fIverify\fP checks the following: .RS .IP \[bu] 2 The first 6 bytes of the request are equal to: \fB6173 7263 624d | asrcbM\fP .IP \[bu] 2 The sizes in the request header are sane and do not point out of the file .IP \[bu] 2 The request version is supported by the binary .IP \[bu] 2 If user-data contains a signature, verify the signature using a public key .RE .PP The content of bytes 6&7 of the request define which kind of user-data the request contains. .IP \fB0x0000\fP 8 no user-data (512 bytes zero) .IP \fB0x0001\fP 8 512 bytes user-data .IP \fB0x0002\fP 8 265 bytes user-data| 139 bytes ecdsa signature | 5 bytes reserved | 2 bytes signature size | ... .IP \fB0x0003\fP 8 256 bytes user-data | 256 bytes rsa2048 signature .IP \fB0x0004\fP 8 128 bytes user-data | 384 bytes rsa3072 signature .PP The actual user-data may be less than the capacity. If less data was provided during \fIcreate\fP zeros are appended. . For type 2-4 The signature is calculated as follows: .RS .IP "1." 3 The request is generated with the user-data in place and zeros for the signature data. .IP "2." 3 The signature is calculated for the request. The signature signs the authenticated data and the encrypted data, but not the request tag. I.e. the signature signs the whole request but the last 16 bytes and with the signature bytes set to zero. .IP "3." 3 The signature is inserted to its location in the request. .IP "4." 3 The request GCM tag is calculated. .PP .RE The verification process works as follows: .RS .IP "1." 3 copy the signature to a buffer .IP "2." 3 overwrite the signature with zeros .IP "3." 3 verify the signature of the request but the last 16 bytes .RE .SH OPTIONS .PP .RS 4 Specify the request to be checked. .RE .RE .PP \-\-user\-cert .RS 4 Certificate containing a public key used to verify the user data signature. Specifies a public key used to verify the user\-data signature. The file must be a X509 certificate in DSA or PEM format. The certificate must hold the public EC, RSA 2048, or RSA 3072 key corresponding to the private user\-key used during `create`. No chain of trust is established. Ensuring that the certificate can be trusted is the responsibility of the user. The EC key must use the NIST/SECG curve over a 521 bit prime field (secp521r1). .RE .RE .PP \-o, \-\-output .RS 4 Store the result in FILE If the request contained abirtary user\-data the output contains this user\-data with padded zeros if available. [default: '-'] .RE .RE .PP \-h, \-\-help .RS 4 Print help (see a summary with \fB\-h\fR). .RE .RE .SH EXAMPLES .PP Create the add-secret request on a trusted system with signed user data similar to the example for \fBpvsecret\fR. Let's assume there are three more files present. \fIuser_data\fR contains ascii "some example user-data", a private user-signing key e.g. rsa3072 \fIusr_sgn_key.priv.pem\fR, and a certificate containing the corresponding public key to the private rsa3072 key \fIuser_cert.pem\fR. .PP .RS .IP trusted:~$ 12 pvsecret create \-k hkd.crt \-\-cert CA.crt \-\-cert ibmsk.crt \-\-hdr pvimage \-o addsecreq.bin \-\-user\-data user_data \-\-user\-sign\-key usr_sgn_key.priv.pem association EXAMPLE .RE .RS Successfully generated the request .br Successfully wrote association info to 'EXAMPLE.yaml' .RE For example, on the SE-guest, perform \fIverify\fP on the request to verify the user-signature and the saneness of the request. On success, The user-data is printed to stdout (if \fI\-\-output\fP was not specified) and \fBSuccesfully verified the request.\fR is printed to stderr. .PP .RS .IP seguest:~$ 12 pvsecret verify \-\-user\-cert user_cert.pem \-o addsecreq.bin .RE .RS some example user-data .br Successfully verified the request .RE .SH "SEE ALSO" .sp \fBpvsecret\fR(1) s390-tools-2.38.0/rust/pvsecret/man/pvsecret.1000066400000000000000000000066741502674226300210040ustar00rootroot00000000000000.\" Copyright 2023, 2024 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" .TH "PVSECRET" "1" "2024-12-19" "s390-tools" "UV-Secret Manual" .nh .ad l .SH NAME pvsecret \- Manage secrets for IBM Secure Execution guests .SH SYNOPSIS .nf .fam C pvsecret [OPTIONS] .fam C .fi .SH DESCRIPTION Use \fBpvsecret\fR to manage secrets for IBM Secure Execution guests. \fBpvsecret\fR can \fIcreate\fR add-secret requests on any architecture. On s390x systems, use \fBpvsecret\fR to \fIadd\fR the secrets to the ultravisor secret store, \fIlist\fR all secrets in the secret store, or \fIlock\fR the secret store to prevent any modifications in the future. The ultravisor secret store stores secrets for the IBM Secure Execution guest. The secret store is cleared on guest reboot. Create requests only on trusted systems that are not the IBM Secure Execution guest where you want to inject the secrets. This approach prevents the secrets from being in cleartext on the guest. For extra safety, do an attestation with \fBpvattest\fR of your guest beforehand, and include the configuration UID in the secret request using \fB--cuid\fR. Refer to \fBpvsecret-add\fR(1) for more information. For all certificates, revocation lists, and host-key documents, both the PEM and DER input formats are supported. .SH "PVSECRET COMMANDS" .PP \fBpvsecret-create(1)\fR .RS 4 Create a new add-secret request .RE .PP \fBpvsecret-add(1)\fR .RS 4 Submit an add-secret request to the Ultravisor (s390x only) .RE .PP \fBpvsecret-lock(1)\fR .RS 4 Lock the secret-store (s390x only) .RE .PP \fBpvsecret-list(1)\fR .RS 4 List all ultravisor secrets (s390x only) .RE .PP \fBpvsecret-verify(1)\fR .RS 4 Verify that an add-secret request is sane .RE .PP \fBpvsecret-retrieve(1)\fR .RS 4 Retrieve a secret from the UV secret store (s390x only) .RE .SH OPTIONS .PP \-v, \-\-verbose .RS 4 Provide more detailed output. .RE .RE .PP \-q, \-\-quiet .RS 4 Provide less output. .RE .RE .PP \-\-version .RS 4 Print version information and exit. .RE .RE .PP \-h, \-\-help .RS 4 Print help (see a summary with \fB\-h\fR). .RE .RE .SH EXAMPLES .PP Create the add-secret request on a trusted system. The program generates two files. \fBaddsecreq.bin\fR contains the add-secret request. \fBEXAMPLE.yaml\fR contains the non-confidential information about the generated secret. It contains name and id of the secret. .PP .nf .fam C trusted:~$ pvsecret create \-k hkd.crt \-\-cert CA.crt \-\-cert ibmsk.crt \-\-hdr pvimage \-o addsecreq.bin association EXAMPLE Successfully generated the request Successfully wrote association info to 'EXAMPLE.yaml' .fam T .fi On the SE-guest, \fIadd\fP the secret from request to the secret store. .PP .nf .fam C seguest:~$ pvsecret add addsecreq.bin Successfully added the secret .fam T .fi On the SE-guest, \fIlist\fP the secrets currently stored. .PP .nf .fam C seguest:~$ pvsecret list Total number of secrets: 1 0 Association: 94ee059335e587e501cc4bf90613e0814f00a7b08bc7c648fd865a2af6a22cc2 .fam T .fi On the SE-guest, \fIlock\fP the secret store. .PP .nf .fam C seguest:~$ pvsecret lock Successfully locked secret store seguest:~$ pvsecret add addsecreq.bin error: Ultravisor: 'secret store locked' (0x0102) .fam T .fi .SH "SEE ALSO" .sp \fBpvsecret-create\fR(1) \fBpvsecret-add\fR(1) \fBpvsecret-lock\fR(1) \fBpvsecret-list\fR(1) \fBpvsecret-verify\fR(1) \fBpvsecret-retrieve\fR(1) s390-tools-2.38.0/rust/pvsecret/src/000077500000000000000000000000001502674226300170665ustar00rootroot00000000000000s390-tools-2.38.0/rust/pvsecret/src/cli.rs000066400000000000000000000521461502674226300202130ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023, 2024 use std::fmt::Display; use clap::error::ErrorKind::ValueValidation; use clap::{ArgGroup, Args, CommandFactory, Parser, Subcommand, ValueEnum, ValueHint}; use utils::{CertificateOptions, DeprecatedVerbosityOptions, STDOUT}; /// Manage secrets for IBM Secure Execution guests. /// /// Use to create and send add-secret requests, list the added secrets and lock the Secret Store. #[derive(Parser, Debug)] pub struct CliOptions { #[clap(flatten)] pub verbosity: DeprecatedVerbosityOptions, /// Print version information and exit. // Implemented for the help message only. Actual parsing happens in the // version command. #[arg(long)] pub version: bool, #[command(subcommand)] pub cmd: Command, } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)] pub enum CreateSecretFlags { /// Disables host-initiated dumping for the target guest instance. DisableDump, } #[derive(Args, Debug)] #[command(group(ArgGroup::new("as-ext").args(["cck", "extension_secret"])),)] pub struct CreateSecretOpt { #[command(flatten)] pub certificate_args: CertificateOptions, /// Specifies the header of the guest image. /// /// Can be an IBM Secure Execution image created by 'pvimg/genprotimg' or an /// extracted IBM Secure Execution header. #[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath)] pub hdr: String, /// Force the generation of add-secret requests on IBM Secure Execution guests. /// /// If the program detects that it is running on an IBM Secure Execution guest, it denies the /// generation of add-secret requests. The force flag overwrites this behavior. #[arg(short, long)] pub force: bool, /// Write the generated request to FILE. #[arg(short, long, value_name = "FILE", value_hint = ValueHint::FilePath,)] pub output: String, /// Use the content of FILE as an extension secret. /// /// The file must be exactly 32 bytes long. If this request is the first, all subsequent /// requests must have the same extension secret. Only makes sense if bit 1 of the secret /// control flags of the IBM Secure Execution header is /// 0. Otherwise the ultravisor rejects the request. #[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath,)] pub extension_secret: Option, /// Use the content of FILE as the customer-communication key (CCK) to derive the extension /// secret. /// /// The file must contain exactly 32 bytes of data. If the target guest was started /// with bit 1 of the secret control flag set, the ultravisor also derives the secret from the /// CCK. Otherwise, the ultravisor interprets the extension secret as a normal one. This still /// works if you use the same CCK for all requests. #[arg(long, value_name = "FILE")] pub cck: Option, /// Use HEXSTRING as the Configuration Unique ID. /// /// Must be a hex 128-bit unsigned big endian number string. Leading zeros must be provided. If /// specified, the value must match with the Config-UID from the attestation result of that /// guest. If not specified, the CUID will be ignored by the ultravisor during the /// verification of the request. #[arg(long, value_name = "HEXSTRING")] pub cuid_hex: Option, /// Use the content of FILE as the Configuration Unique ID. /// /// The file must contain exactly 128 bit of data or a yaml with a `cuid` entry. /// If specified, the value must match the Config-UID from the attestation result of that /// guest. If not specified, the CUID will be ignored by the Ultravisor during the verification /// of the request. #[arg(long, value_name = "FILE", conflicts_with("cuid_hex"), value_hint = ValueHint::FilePath,)] pub cuid: Option, #[command(subcommand)] pub secret: AddSecretType, // FLAGS // each flag must conflict with `flags` // `flags` is hidden in the help menu /// Manually set the add-secret request flags. /// /// No validity checks made. Hidden in user documentation. #[arg(long, hide(true))] pub pcf: Option, /// Flags for the add-secret request. #[arg( long, conflicts_with("pcf"), value_enum, value_parser, use_value_delimiter = true, value_delimiter = ',' )] pub flags: Vec, /// Use the content of FILE as user-data. /// /// Passes user data defined in FILE through the add-secret request to the ultravisor. The /// user data can be up to 512 bytes of arbitrary data, and the maximum size depends on the /// size of the user-signing key: /// - No key: user data can be 512 bytes. /// - EC(secp521r1) or RSA 2048 keys: user data can be 256 bytes. /// - RSA 3072 key: user data can be 128 bytes. /// /// The firmware ignores this data, but the request tag protects the user-data. Optional. No /// user-data by default. #[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath,)] pub user_data: Option, /// Use the content of FILE as user signing key. /// /// Adds a signature calculated from the key in FILE to the add-secret request. The /// file must be in DER or PEM format containing a private key. Supported are RSA 2048 & /// 3072-bit and EC(secp521r1) keys. The firmware ignores the content, but the request tag /// protects the signature. The user-signing key signs the request. The location of the /// signature is filled with zeros during the signature calculation. The request tag also /// secures the signature. See man pvsecret verify for more details. Optional. No signature /// by default. #[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath,)] pub user_sign_key: Option, /// Do not hash the name, use it directly as secret ID. /// /// Ignored for meta-secrets. #[arg(long)] pub use_name: bool, } #[derive(Subcommand, Debug)] pub enum AddSecretType { /// Create a meta secret. /// /// Use a meta secret to carry flags to the ultravisor without having to provide an actual /// secret value. Meta secrets do not appear in the list of secrets. Meta, /// Create an association secret. /// /// Use an association secret to connect a trusted I/O device to a guest. The 'pvapconfig' tool /// provides more information about association secrets. Association { /// String that identifies the new secret. /// /// The actual secret is set with '--input-secret'. The name is saved in `NAME.yaml` with /// white-spaces mapped to `_`. name: String, /// Print the hashed name to stdout. /// /// The hashed name is not written to `NAME.yaml` #[arg(long)] stdout: bool, /// Path from which to read the plaintext secret. Uses a random secret if not specified. #[arg(long, value_name = "SECRET-FILE", value_hint = ValueHint::FilePath, conflicts_with("output_secret"))] input_secret: Option, /// Save the generated secret as plaintext in SECRET-FILE. /// /// The generated secret can be used to generate add-secret requests for a different guest /// with the same secret using '--input-secret'. Destroy the secret when it is not used /// anymore. #[arg(long, value_name = "SECRET-FILE", value_hint = ValueHint::FilePath,)] output_secret: Option, }, /// Create a retrievable secret. /// /// A retrievable secret is stored in the per-guest storage of the Ultravisor. A SE-guest can /// retrieve the secret at runtime and use it. All retrievable secrets, but the plaintext /// secret, are retrieved as wrapped/protected key objects and only usable inside the current, /// running SE-guest instance. #[command(visible_alias = "retr")] Retrievable { /// String that identifies the new secret. /// /// The actual secret is set with '--secret'. The name is saved in `NAME.yaml` with /// white-spaces mapped to `_`. name: String, /// Print the hashed name to stdout. /// /// The hashed name is not written to `NAME.yaml` #[arg(long)] stdout: bool, /// Use SECRET-FILE as retrievable secret #[arg(long, value_name = "SECRET-FILE", value_hint = ValueHint::FilePath)] secret: String, /// Specify the secret type. /// /// Limitations to the input data apply depending on the secret type. #[arg(long = "type", value_name = "TYPE")] kind: RetrieveableSecretInpKind, }, /// Update customer communication key. /// /// Insert a customer communication key into a guest. #[command(visible_alias = "cck")] UpdateCck { /// Use CCK-FILE as new CCK. #[arg(long, value_name = "CCK-FILE", value_hint = ValueHint::FilePath)] secret: String, }, } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)] pub enum RetrieveableSecretInpKind { /// A plaintext secret. /// Can be any file up to 8190 bytes long Plain, /// An AES key. /// Must be a plain byte file 128, 192, or 256 bit long. Aes, /// An AES-XTS key. /// Must be a plain byte file 256, or 512 bit long. AesXts, /// A HMAC-SHA key. /// Must be a plain byte file 512, or 1024 bit long. Special care is required when creating /// HMAC-SHA keys. For more Information refer to the DESCRIPTION section of the man file. HmacSha, /// An elliptic curve private key. /// Must be a PEM or DER file. Ec, } impl Display for RetrieveableSecretInpKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "{}", match self { Self::Plain => "PLAINTEXT", Self::Aes => "AES KEY", Self::AesXts => "AES-XTS KEY", Self::HmacSha => "HMAC-SHA KEY", Self::Ec => "EC PRIVATE KEY", } ) } } // all members s390x only #[derive(Args, Debug)] pub struct AddSecretOpt { /// Specify the request to be sent. #[arg(value_name = "FILE", value_hint = ValueHint::FilePath,)] #[cfg(target_arch = "s390x")] pub input: String, /// Force the addition of add-secret requests. /// /// Add an add-secret request even if there is already a secret with the same ID in the secret /// store. #[arg(short, long)] pub force: bool, } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug, Default)] #[cfg(target_arch = "s390x")] pub enum ListSecretOutputType { /// Human-focused, non-parsable output format #[default] Human, /// Use yaml format. Yaml, /// Use the format the ultravisor uses to pass the list. Bin, } // all members s390x only #[derive(Args, Debug)] pub struct ListSecretOpt { /// Store the result in FILE #[arg(value_name = "FILE", default_value = STDOUT, value_hint = ValueHint::FilePath,)] #[cfg(target_arch = "s390x")] pub output: String, /// Define the output format of the list. #[arg(long, value_enum, default_value_t)] #[cfg(target_arch = "s390x")] pub format: ListSecretOutputType, } #[derive(Args, Debug)] pub struct VerifyOpt { /// Specify the request to be checked. #[arg(value_name = "FILE", value_hint = ValueHint::FilePath,)] pub input: String, /// Certificate containing a public key used to verify the user data signature. /// /// Specifies a public key used to verify the user-data signature. The file must be a X509 /// certificate in DSA or PEM format. The certificate must hold the public EC, RSA 2048, or RSA /// 3072 key corresponding to the private user-key used during `create`. No chain of trust is /// established. Ensuring that the certificate can be trusted is the responsibility of the /// user. The EC key must use the NIST/SECG curve over a 521 bit prime field (secp521r1). #[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath,)] pub user_cert: Option, /// Store the result in FILE /// /// If the request contained abirtary user-data the output contains this user-data with padded /// zeros if available. #[arg(short, long, value_name = "FILE", default_value = STDOUT, value_hint = ValueHint::FilePath,)] pub output: String, } // all members s390x only #[derive(Args, Debug)] pub struct RetrSecretOptions { /// Specify the secret ID to be retrieved. /// /// Input type depends on '--inform'. If `yaml` (default) is specified, it must be a yaml /// created by the create subcommand of this tool. If `hex` is specified, it must be a 32 byte /// handle encodes in hexadecimal. Leading zeros are required. If there are multiple secrets in /// the store with the same Id there are no guarantees on which specific secret is retrieved. /// Use --inform=idx to make sure a specific secret is retrieved. #[cfg(target_arch = "s390x")] #[arg(value_name = "ID", value_hint = ValueHint::FilePath)] pub input: String, /// Specify the output path to place the secret value #[cfg(target_arch = "s390x")] #[arg(short, long, value_name = "FILE", default_value = STDOUT, value_hint = ValueHint::FilePath)] pub output: String, /// Define input type for the Secret ID #[cfg(target_arch = "s390x")] #[arg(long, value_enum, default_value_t)] pub inform: RetrInpFmt, /// Define the output format for the retrieved secret #[cfg(target_arch = "s390x")] #[arg(long, value_enum, default_value_t)] pub outform: RetrOutFmt, } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug, Default)] pub enum RetrInpFmt { /// Use a yaml file #[default] Yaml, /// Use a hex string. Hex, /// Use a name-string. Will hash it if no secret with the name found. Name, /// Use the secret-index (base 10) instead of the secret-ID Idx, } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug, Default)] pub enum RetrOutFmt { /// Write the secret as PEM. /// /// File starts with `-----BEGIN IBM PROTECTED KEY----` and `-----BEGIN /// PLAINTEXT SECRET-----` for plaintext secrets it contains one header /// line with the type information and the base64 protected key #[default] Pem, /// Write the secret in binary. Bin, } #[derive(Subcommand, Debug)] pub enum Command { /// Create a new add-secret request. /// /// Create add-secret requests for IBM Secure Execution guests. Only create these requests in a /// trusted environment, such as your workstation. The 'pvattest create' command creates a /// randomly generated key to protect the request. The generated requests can then be added on /// an IBM Secure Execution guest using 'pvsecret add'. The guest can then use the secrets with /// the use case depending on the secret type. Create(Box), /// Submit an add-secret request to the Ultravisor (s390x only). /// /// Perform an add-secret request using a previously generated add-secret request. Only /// available on s390x. Add(AddSecretOpt), /// Lock the secret-store (s390x only). /// /// Lock the secret store (s390x only). After this command executed successfully, all /// subsequent add-secret requests will fail. Only available on s390x. Lock, /// List all ultravisor secrets (s390x only). /// /// Lists the IDs of all non-null secrets currently stored in the ultravisor for the currently /// running IBM Secure Execution guest. Only available on s390x. List(ListSecretOpt), /// Verify that an add-secret request is sane. /// /// Verifies that the given request is an add-secret request by testing for some values to be /// present. If the request contains signed user-data, the signature is verified with the /// provided key. Outputs the arbitrary user-data. Verify(VerifyOpt), /// Retrieve a secret from the UV secret store (s390x only). #[command(visible_alias = "retr")] Retrieve(RetrSecretOptions), /// Print version information and exit. #[command(aliases(["--version"]), hide(true))] Version, } /// Additional checks to assure, option integrity pub fn validate_cli(cli: &CliOptions) -> Result<(), clap::Error> { if let Command::Create(opt) = &cli.cmd { if let AddSecretType::Association { name, stdout, input_secret: _, output_secret: secret_out, } = &opt.secret { if *stdout { return Ok(()); } if secret_out == &Some(format!("{name}.yaml")) { return Err(CliOptions::command().error( ValueValidation, format!("Secret output file and the secret name '{name}.yaml' are the same."), )); } if format!("{name}.yaml") == opt.output { return Err(CliOptions::command().error( ValueValidation, format!( "output file and the secret name '{}' are the same.", &opt.output ), )); } } } Ok(()) } #[cfg(test)] mod test { use super::*; #[test] #[rustfmt::skip] fn cli_args() { //Verify only that some arguments are optional, we do not want to test clap, only the //configuration let valid_args = [ vec!["pvsecret", "lock"], vec!["pvsecret", "version"], vec!["pvsecret", "list"], #[cfg(target_arch = "s390x")] vec!["pvsecret", "add", "abc"], #[cfg(not(target_arch = "s390x"))] vec!["pvsecret", "add"], vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--no-verify", "meta"], vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--no-verify", "association", "name" ], vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--no-verify", "update-cck", "--secret", "abc"], // verify that arguments stay backwards compatible vec!["pvsecret", "create", "-k", "abc,cdef", "--hdr", "abc", "-o", "abc", "-C", "uuu,ggg", "--crl", "yyy,hhh", "--root-ca", "tttt", "--extension-secret", "fff", "--cuid", "cuid", "--flags", "disable-dump", "meta"], vec!["pvsecret", "create", "--host-key-document", "abc", "-k", "y", "--hdr", "abc", "-o", "abc", "--cert", "uuu", "--crl", "yyy", "--root-ca", "tttt", "--cck", "cck", "--cuid-hex", "0x11223344556677889900aabbccddeeff", "--pcf", "0x123", "association", "name", "--stdout", "--output-secret", "secret"], vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--no-verify", "association", "name", "--output-secret", "secret"], #[cfg(target_arch = "s390x")] vec!["pvsecret", "list", "--format", "human"], #[cfg(target_arch = "s390x")] vec!["pvsecret", "list", "--format", "yaml"], #[cfg(target_arch = "s390x")] vec!["pvsecret", "list", "--format", "bin"], ]; // Test for the minimal amount of flags to yield an invalid combination let invalid_args = [ vec!["pvsecret"], vec!["pvsecret", "list", "--yaml", "--bin"], vec!["pvsecret", "create", "--hdr", "abc", "-o", "abc", "--no-verify" ,"null"], vec!["pvsecret", "create", "-k", "abc", "-o", "abc", "--no-verify", "null"], vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "--no-verify", "null"], vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "null"], vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--cck", "abc", "--extension_secret", "abc", "--no-verify", "null"], vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--no-verify", "--flags", "disable-dump", "--pcf", "0", "null"], vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--no-verify", "--cuid", "abc", "--cuid_hex", "9", "null"], vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--no-verify", "association"], vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--no-verify", "association", "name", "--output-secret", "secret", "--input-secret", "secret"], vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--no-verify", "update-cck"], ]; for arg in valid_args { let res = CliOptions::try_parse_from(&arg); if let Err(e) = &res { println!("arg: {arg:?}"); println!("{e}"); } assert!(res.is_ok()); } for arg in invalid_args { let res = CliOptions::try_parse_from(&arg); assert!(res.is_err()); } } #[test] fn verify_cli() { use clap::CommandFactory; CliOptions::command().debug_assert() } } s390-tools-2.38.0/rust/pvsecret/src/cmd.rs000066400000000000000000000022621502674226300202010ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 mod create; pub use create::create; mod verify; pub use verify::verify; pub const CMD_FN: &[&str] = &["+create", "+verify"]; #[cfg(target_arch = "s390x")] mod add; #[cfg(target_arch = "s390x")] mod list; #[cfg(target_arch = "s390x")] mod lock; #[cfg(target_arch = "s390x")] mod retr; // Commands (directly) related to UVCs are only available on s389x #[cfg(target_arch = "s390x")] mod uv_cmd { pub use super::*; pub use add::add; pub use list::list; pub use lock::lock; pub use retr::retr; pub const UV_CMD_FN: &[&str] = &["+add", "+lock", "+list"]; } #[cfg(not(target_arch = "s390x"))] mod uv_cmd { use crate::cli::{AddSecretOpt, ListSecretOpt, RetrSecretOptions}; use anyhow::{bail, Result}; macro_rules! not_supp { ($name: ident $( ,$opt: ty )?) => { pub fn $name($(_: &$opt)?) -> Result<()> { bail!("Command only available on s390x") } }; } not_supp!(add, AddSecretOpt); not_supp!(list, ListSecretOpt); not_supp!(retr, RetrSecretOptions); not_supp!(lock); pub const UV_CMD_FN: &[&str] = &[]; } pub use uv_cmd::*; s390-tools-2.38.0/rust/pvsecret/src/cmd/000077500000000000000000000000001502674226300176315ustar00rootroot00000000000000s390-tools-2.38.0/rust/pvsecret/src/cmd/add.rs000066400000000000000000000021061502674226300207260ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 use crate::{cli::AddSecretOpt, cmd::list::list_uvc}; use anyhow::{bail, Context, Result}; use log::warn; use pv::{ secret::AddSecretRequest, uv::{AddCmd, UvCmd, UvDevice}, }; use utils::get_reader_from_cli_file_arg; /// Do an Add Secret UVC pub fn add(opt: &AddSecretOpt) -> Result<()> { let uv = UvDevice::open()?; let mut rd_in = get_reader_from_cli_file_arg(&opt.input)?; let mut cmd = AddCmd::new(&mut rd_in).context(format!("Processing input file {}", opt.input))?; if let Some(id) = AddSecretRequest::bin_id(cmd.data().unwrap())? { if list_uvc(&uv)?.iter().any(|e| e.id() == id.as_ref()) { warn!("There is already a secret in the secret store with that id."); match opt.force { true => warn!("'--force' specified: Adding the secret anyways."), false => bail!("Unable to add the secret due to duplicated IDs"), } } } uv.send_cmd(&mut cmd)?; warn!("Successfully added the secret"); Ok(()) } s390-tools-2.38.0/rust/pvsecret/src/cmd/create.rs000066400000000000000000000236111502674226300214450ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023, 2024 use std::path::Path; use anyhow::{anyhow, bail, Context, Error, Result}; use log::{debug, info, trace, warn}; use pv::{ misc::{ decode_hex, open_file, pv_guest_bit_set, read_exact_file, read_file, try_parse_u128, try_parse_u64, write, }, request::{ openssl::pkey::{PKey, Private}, BootHdrTags, ReqEncrCtx, Request, SymKeyType, }, secret::{AddSecretFlags, AddSecretRequest, AddSecretVersion, ExtSecret, GuestSecret}, uv::ConfigUid, }; use serde_yaml::Value; use utils::get_writer_from_cli_file_arg; use crate::cli::{AddSecretType, CreateSecretFlags, CreateSecretOpt, RetrieveableSecretInpKind}; fn write_out(path: &P, data: D, ctx: &str) -> pv::Result<()> where P: AsRef, D: AsRef<[u8]>, { let mut wr = get_writer_from_cli_file_arg(path.as_ref())?; write(&mut wr, data, path, ctx)?; Ok(()) } fn retrievable(name: &str, secret: &str, kind: &RetrieveableSecretInpKind) -> Result { let secret_data = read_file(secret, &format!("retrievable {kind}"))?.into(); match kind { RetrieveableSecretInpKind::Plain => GuestSecret::plaintext(name, secret_data), RetrieveableSecretInpKind::Aes => GuestSecret::aes(name, secret_data), RetrieveableSecretInpKind::AesXts => GuestSecret::aes_xts(name, secret_data), RetrieveableSecretInpKind::HmacSha => GuestSecret::hmac_sha(name, secret_data), RetrieveableSecretInpKind::Ec => GuestSecret::ec( name, read_private_key(secret_data.value()) .with_context(|| format!("Cannot read {secret} as {kind} from PEM or DER"))?, ), } .map_err(Error::from) } /// Prepare an add-secret request pub fn create(opt: &CreateSecretOpt) -> Result<()> { if pv_guest_bit_set() { warn!("The system seems to be a Secure Execution guest"); if !opt.force { bail!("Do NOT generate Add-secret requests on a machine where you want to use the secret! Overwrite with '-f'"); } else { warn!("WARNING: Enforcing of generating a request on a Secure Execution guest") } } let mut asrcb = build_asrcb(opt)?; debug!("Generated Add-secret request"); // Add host-key documents opt.certificate_args .get_verified_hkds("secret")? .into_iter() .for_each(|k| asrcb.add_hostkey(k)); debug!("Added all host-keys"); // build + encrypt the request let rq = ReqEncrCtx::random(SymKeyType::Aes256Gcm).context("Failed to generate random input")?; let ser_asrbc = asrcb.encrypt(&rq)?; warn!("Successfully generated the request"); write_out(&opt.output, ser_asrbc, "add-secret request")?; info!("Successfully wrote the request to '{}'", &opt.output); write_secret(&opt.secret, asrcb.guest_secret(), &opt.output) } /// Read+parse the first key from the buffer. fn read_private_key(buf: &[u8]) -> Result> { PKey::private_key_from_der(buf) .or_else(|_| PKey::private_key_from_pem(buf)) .map_err(Error::new) } /// Set-up the `add-secret request` from command-line arguments fn build_asrcb(opt: &CreateSecretOpt) -> Result { debug!("Build add-secret request"); let mut secret = match &opt.secret { AddSecretType::Meta => GuestSecret::Null, AddSecretType::Association { name, input_secret: Some(p), .. } => GuestSecret::association(name, read_exact_file(p, "Association secret")?)?, AddSecretType::Association { name, input_secret: None, .. } => GuestSecret::association(name, None)?, AddSecretType::Retrievable { name, secret, kind, .. } => retrievable(name, secret, kind)?, AddSecretType::UpdateCck { secret } => { GuestSecret::update_cck(read_exact_file(secret, "CCK file")?) } }; trace!("AddSecret: {secret:x?}"); opt.use_name.then(|| secret.no_hash_name()); let mut flags = match &opt.pcf { Some(v) => (&try_parse_u64(v, "pcf")?).into(), None => AddSecretFlags::default(), }; opt.flags.iter().for_each(|v| match v { CreateSecretFlags::DisableDump => flags.set_disable_dump(), }); debug!("FLAGS: {flags:x?}"); let mut se_hdr = open_file(&opt.hdr)?; let mut asrcb = AddSecretRequest::new( AddSecretVersion::One, secret, BootHdrTags::from_se_image(&mut se_hdr) .with_context(|| format!("Provided SE-header in '{}' is malformed", &opt.hdr))?, flags, ); // Set CUID read_cuid(&mut asrcb, opt)?; // Set extension secret if let Some(path) = &opt.extension_secret { asrcb.set_ext_secret(ExtSecret::Simple( read_exact_file(path, "extension secret")?.into(), ))?; } else if let Some(path) = &opt.cck { asrcb.set_ext_secret(ExtSecret::Derived(read_exact_file(path, "CCK")?.into()))?; } // add user data let user_data = opt .user_data .as_ref() .map(|p| read_file(p, "user-data")) .transpose()?; if user_data.as_ref().is_some_and(|data| data.is_empty()) { warn!("Added empty user-data file."); } let user_key = opt .user_sign_key .as_ref() .map(|p| read_file(p, "User-signing key")) .transpose()? .map(|buf| { read_private_key(&buf).context("Cannot read {secret} as private key from PEM or DER") }) .transpose()?; if user_data.is_some() || user_key.is_some() { asrcb.set_user_data(user_data.unwrap_or_default(), user_key)?; } Ok(asrcb) } // Try to extract a Config-UId from a yaml structure // The cuid field can be embedded in an abritray amount of Mappings // The function takes the first cuid it founds (width search). fn try_from_val(val: Value) -> Result { fn get_cuid_from_mapping(val: &Value, depth: u8) -> Option { if depth >= 8 { return None; } match val { Value::Mapping(m) if m.contains_key("cuid") => { return m.get("cuid").and_then(|v| v.as_str()).map(|s| s.to_owned()) } Value::Mapping(m) => { for (_, v) in m { if let Some(v) = get_cuid_from_mapping(v, depth + 1) { return Some(v); } } } _ => return None, }; None } let cuid = match &val { Value::String(s) => Some(s.clone()), Value::Mapping(_) => get_cuid_from_mapping(&val, 0), _ => None, } .ok_or(anyhow!("No 'cuid' entry found"))?; let cuid = cuid .strip_prefix("0x") .ok_or(anyhow!("CUID value starts not with 0x".to_string()))? .to_owned(); if cuid.len() != ::std::mem::size_of::() * 2 { return Err(anyhow!(format!("len invalid ({})", cuid.len()))); } let cuid: ConfigUid = decode_hex(&cuid)? .try_into() .map_err(|_| anyhow!("Cannot parse hex number".to_string()))?; Ok(cuid) } fn read_cuid(asrcb: &mut AddSecretRequest, opt: &CreateSecretOpt) -> Result<()> { if let Some(path) = &opt.cuid { let cuid = match read_exact_file(path, "The CUID-file") { Ok(v) => v, Err(_) => { let buf = read_file(path, "The CUID-file")?; let val: Value = serde_yaml::from_slice(&buf).context( "The CUID-file does not contain a 128bit value or a yaml with a 'cuid' field", )?; try_from_val(val)? } }; asrcb.set_cuid(cuid); } else if let Some(v) = &opt.cuid_hex { asrcb.set_cuid(try_parse_u128(v, "CUID")?); } Ok(()) } // Write non confidential data (=name+id) to a yaml stdout fn write_yaml>( name: &str, guest_secret: &GuestSecret, stdout: &bool, outp_path: P, ) -> Result<()> { debug!("Non-confidential secret information: {guest_secret:x?}"); let secret_info = serde_yaml::to_string(guest_secret)?; if stdout.to_owned() { println!("{secret_info}"); return Ok(()); } let gen_name: String = name .chars() .map(|c| if c.is_whitespace() { '_' } else { c }) .collect(); let mut yaml_path = outp_path .as_ref() .parent() .with_context(|| format!("Cannot open directory of {:?}", outp_path.as_ref()))? .to_owned(); yaml_path.push(gen_name); yaml_path.set_extension("yaml"); write_out(&yaml_path, secret_info, "secret information")?; warn!( "Successfully wrote secret info to '{}'", yaml_path.display().to_string() ); Ok(()) } /// Write the generated secret (if any) to the specified output stream fn write_secret>( secret: &AddSecretType, guest_secret: &GuestSecret, outp_path: P, ) -> Result<()> { match secret { AddSecretType::Association { name, stdout, output_secret, .. } => { write_yaml(name, guest_secret, stdout, outp_path)?; if let Some(path) = output_secret { write_out(path, guest_secret.confidential(), "Association secret")? } } AddSecretType::Retrievable { name, stdout, .. } => { write_yaml(name, guest_secret, stdout, outp_path)? } _ => (), }; Ok(()) } #[cfg(test)] mod test { #[test] fn read_private_key() { let key = include_bytes!("../../../pv/tests/assets/keys/rsa3072key.pem"); let key = super::read_private_key(key).unwrap(); assert_eq!(key.rsa().unwrap().size(), 384); } #[test] fn read_private_key_fail() { let key = include_bytes!("create.rs"); let key = super::read_private_key(key); assert!(key.is_err()); } } s390-tools-2.38.0/rust/pvsecret/src/cmd/list.rs000066400000000000000000000035501502674226300211550ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 use std::io::ErrorKind; use crate::cli::{ListSecretOpt, ListSecretOutputType}; use anyhow::{Context, Error, Result}; use log::{info, warn}; use pv::uv::{ListCmd, SecretList, UvDevice}; use utils::{get_writer_from_cli_file_arg, STDOUT}; const SECRET_LIST_BUF_SIZE: usize = 4; /// Do a List Secrets UVC pub fn list_uvc(uv: &UvDevice) -> Result { let mut cmd = ListCmd::with_pages(SECRET_LIST_BUF_SIZE); let more_data = match uv.send_cmd(&mut cmd) { Ok(v) => Ok(v), Err(pv::PvCoreError::Io(e)) if e.kind() == ErrorKind::InvalidInput => { info!("Uvdevice does not suport longer list. Fallback to one page list."); cmd = ListCmd::default(); uv.send_cmd(&mut cmd) } Err(e) => Err(e), }? .more_data(); if more_data { warn!("The secret list contains more data but the uvdevice cannot show all."); } cmd.try_into().map_err(Error::new) } /// Do a List Secrets UVC and output the list in the requested format pub fn list(opt: &ListSecretOpt) -> Result<()> { let uv = UvDevice::open()?; let secret_list = list_uvc(&uv)?; let mut wr_out = get_writer_from_cli_file_arg(&opt.output)?; match &opt.format { ListSecretOutputType::Human => { write!(wr_out, "{secret_list}").context("Cannot generate output")? } ListSecretOutputType::Yaml => write!(wr_out, "{}", serde_yaml::to_string(&secret_list)?) .context("Cannot generate yaml output")?, ListSecretOutputType::Bin => secret_list .encode(&mut wr_out) .context("Cannot encode secret list")?, } wr_out.flush()?; if opt.output != STDOUT { warn!( "Successfully wrote the list of secrets to '{}'", &opt.output ); } Ok(()) } s390-tools-2.38.0/rust/pvsecret/src/cmd/lock.rs000066400000000000000000000004551502674226300211330ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 use anyhow::Result; use log::warn; use pv::uv::{LockCmd, UvDevice}; /// Do a Lock Secret Store UVC pub fn lock() -> Result<()> { UvDevice::open()?.send_cmd(&mut LockCmd)?; warn!("Successfully locked secret store"); Ok(()) } s390-tools-2.38.0/rust/pvsecret/src/cmd/retr.rs000066400000000000000000000072351502674226300211620ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::{collections::VecDeque, fmt::Display}; use anyhow::{anyhow, bail, Context, Result}; use log::{debug, info, warn}; use pv::{ misc::open_file, misc::write, secret::{GuestSecret, RetrievedSecret}, uv::{RetrieveCmd, SecretEntry, SecretId, SecretList, UvDevice}, }; use utils::get_writer_from_cli_file_arg; use super::list::list_uvc; use crate::cli::{RetrInpFmt, RetrOutFmt, RetrSecretOptions}; enum Value { Id(SecretId), Idx(u16), } impl Display for Value { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Value::Id(id) => write!(f, "ID {id}"), Value::Idx(idx) => write!(f, "index {idx}"), } } } impl TryFrom<&RetrSecretOptions> for Value { type Error = anyhow::Error; fn try_from(opt: &RetrSecretOptions) -> Result { match opt.inform { RetrInpFmt::Yaml => match serde_yaml::from_reader(&mut open_file(&opt.input)?)? { GuestSecret::Retrievable { id, .. } => Ok(Self::Id(id)), gs => bail!("The file contains a {gs}-secret, which is not retrievable."), }, RetrInpFmt::Hex => serde_yaml::from_str(&opt.input) .context("Cannot parse SecretId information") .map(Self::Id), RetrInpFmt::Name => Ok(Self::Id(SecretId::from_string(&opt.input))), RetrInpFmt::Idx => opt .input .parse() .context("Invalid index value") .map(Self::Idx), } } } fn find_secret_by_id(secrets: &SecretList, id: &SecretId) -> Option { let mut secrets: VecDeque<_> = secrets .into_iter() .filter(|s| s.id() == id.as_ref()) .collect(); let secret = secrets.pop_front(); if !secrets.is_empty() { warn!( "There are multiple secrets in the secret store with that id. Indices: {}", secrets .iter() .fold(format!("{}", secret.unwrap().index()), |acc, e| { format!("{acc}, {}", e.index()) }) ); } secret.cloned() } fn retrieve(value: Value) -> Result { let uv = UvDevice::open()?; let secrets = list_uvc(&uv)?; let entry = match &value { Value::Id(id) => { match find_secret_by_id(&secrets, id) { Some(s) => Some(s), // hash it + try again if it is ASCII-representable None => match id.as_ascii() { Some(s) => find_secret_by_id(&secrets, &GuestSecret::name_to_id(s)?), None => None, }, } } Value::Idx(idx) => secrets.into_iter().find(|e| &e.index() == idx), } .ok_or(anyhow!( "The UV secret-store has no secret with the {value}" ))?; info!("Try to retrieve secret at index: {}", entry.index()); debug!("Try to retrieve: {entry:?}"); let mut uv_cmd = RetrieveCmd::from_entry(entry)?; uv.send_cmd(&mut uv_cmd)?; Ok(RetrievedSecret::from_cmd(uv_cmd)) } pub fn retr(opt: &RetrSecretOptions) -> Result<()> { let mut output = get_writer_from_cli_file_arg(&opt.output)?; let retr_secret = retrieve(opt.try_into()?) .context("Could not retrieve the secret from the UV secret store.")?; let out_data = match opt.outform { RetrOutFmt::Bin => retr_secret.into_bytes(), RetrOutFmt::Pem => retr_secret.to_pem()?.into_bytes(), }; write( &mut output, out_data.value(), &opt.output, "IBM Protected Key", )?; Ok(()) } s390-tools-2.38.0/rust/pvsecret/src/cmd/verify.rs000066400000000000000000000030401502674226300215000ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use crate::cli::VerifyOpt; use anyhow::{anyhow, Context, Result}; use log::warn; use pv::misc::{read_certs, read_file}; use pv::{ request::openssl::pkey::{PKey, Public}, secret::verify_asrcb_and_get_user_data, }; use utils::{get_reader_from_cli_file_arg, get_writer_from_cli_file_arg}; /// read the content of a DER or PEM x509 and return the public key fn read_sgn_key(path: &str) -> Result> { read_certs(read_file(path, "user-signing key")?)? .first() .ok_or(anyhow!("File does not contain a X509 certificate"))? .public_key() .map_err(anyhow::Error::new) } pub fn verify(opt: &VerifyOpt) -> Result<()> { let mut rd_in = get_reader_from_cli_file_arg(&opt.input)?; let mut data_in = Vec::with_capacity(0x1000); rd_in .read_to_end(&mut data_in) .with_context(|| format!("Cannot read input file {}", opt.input))?; let verify_cert = opt .user_cert .as_ref() .map(|p| read_sgn_key(p)) .transpose() .context("Cannot read user-verification certificate.")?; let user_data = verify_asrcb_and_get_user_data(data_in, verify_cert) .context("Could not verify the the Add-secret request")?; if let Some(user_data) = user_data { get_writer_from_cli_file_arg(&opt.output)? .write_all(&user_data) .with_context(|| format!("Cannot write user data to {}", opt.output))?; } warn!("Successfully verified the request."); Ok(()) } s390-tools-2.38.0/rust/pvsecret/src/main.rs000066400000000000000000000032571502674226300203670ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023, 2024 #![allow(missing_docs)] mod cli; mod cmd; use clap::{CommandFactory, Parser}; use cli::{validate_cli, CliOptions, Command}; use log::trace; use std::process::ExitCode; use utils::{print_cli_error, print_error, print_version, PvLogger}; static LOGGER: PvLogger = PvLogger; static EXIT_LOGGER: u8 = 3; const FEATURES: &[&[&str]] = &[cmd::CMD_FN, cmd::UV_CMD_FN]; fn main() -> ExitCode { let cli: CliOptions = match CliOptions::try_parse() { Ok(cli) => match validate_cli(&cli) { Ok(_) => cli, Err(e) => return print_cli_error(e, CliOptions::command()), }, Err(e) => return print_cli_error(e, CliOptions::command()), }; // set up logger/std(out,err) let log_level = cli.verbosity.to_level_filter(); if let Err(e) = LOGGER.start(log_level) { // should(TM) never happen eprintln!("Logger error: {e:?}"); return EXIT_LOGGER.into(); } // NOTE trace verbosity is disabled in release builds trace!("Trace verbosity, may leak secrets to command-line"); trace!("Options {cli:?}"); // perform the command selected by the user let res = match &cli.cmd { Command::Add(opt) => cmd::add(opt), Command::List(opt) => cmd::list(opt), Command::Lock => cmd::lock(), Command::Create(opt) => cmd::create(opt), Command::Version => Ok(print_version!("2024", log_level; FEATURES.concat())), Command::Verify(opt) => cmd::verify(opt), Command::Retrieve(opt) => cmd::retr(opt), }; match res { Ok(_) => ExitCode::SUCCESS, Err(e) => print_error(&e, log_level), } } s390-tools-2.38.0/rust/utils/000077500000000000000000000000001502674226300156045ustar00rootroot00000000000000s390-tools-2.38.0/rust/utils/Cargo.toml000066400000000000000000000005251502674226300175360ustar00rootroot00000000000000[package] name = "utils" version = "0.12.0" edition.workspace = true license.workspace = true [dependencies] clap = { version ="4.5", features = ["derive", "wrap_help"] } libc = "0.2.169" log = { version = "0.4.25", features = ["std", "release_max_level_debug"] } pv = { path = "../pv", package = "s390_pv" } serde = { version = "1.0.217"} s390-tools-2.38.0/rust/utils/src/000077500000000000000000000000001502674226300163735ustar00rootroot00000000000000s390-tools-2.38.0/rust/utils/src/cli.rs000066400000000000000000000233041502674226300175120ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023, 2024 use clap::{ArgAction, ArgGroup, Args, Command, ValueHint}; use log::{info, warn, LevelFilter}; use pv::misc::read_file; use pv::{ misc::{create_file, open_file, read_certs}, request::{ openssl::pkey::{PKey, Public}, HkdVerifier, }, Error, Result, }; use std::io::{Read, Write}; use std::path::{Path, PathBuf}; use std::process::ExitCode; /// CLI Argument collection for handling host-keys, IBM signing keys, and certificates. #[derive(Args, Debug, Clone, PartialEq, Eq, Default)] #[command( group(ArgGroup::new("pv_verify").required(true).args(["no_verify", "certs"])), )] pub struct CertificateOptions { /// Use FILE as a host-key document. /// /// Can be specified multiple times and must be specified at least once. #[arg( short = 'k', long = "host-key-document", value_name = "FILE", required = true, value_hint = ValueHint::FilePath, use_value_delimiter = true, value_delimiter = ',', )] pub host_key_documents: Vec, /// Disable the host-key document verification. /// /// Does not require the host-key documents to be valid. /// Do not use for a production request unless you verified the host-key document beforehand. #[arg(long)] pub no_verify: bool, /// Use FILE as a certificate to verify the host-key or keys. /// /// The certificates are used to establish a chain of trust for the verification /// of the host-key documents. Specify this option twice to specify the IBM Z signing key and /// the intermediate CA certificate (signed by the root CA). #[arg( short= 'C', long = "cert", value_name = "FILE", alias("crt"), value_hint = ValueHint::FilePath, use_value_delimiter = true, value_delimiter = ',', )] pub certs: Vec, /// Use FILE as a certificate revocation list (CRL). /// /// The list is used to check whether a certificate of the chain of /// trust is revoked. Specify this option multiple times to use multiple CRLs. #[arg( long = "crl", requires("certs"), value_name = "FILE", value_hint = ValueHint::FilePath, use_value_delimiter = true, value_delimiter = ',', )] pub crls: Vec, /// Make no attempt to download CRLs. #[arg(long, requires("certs"))] pub offline: bool, /// Use FILE as the root-CA certificate for the verification. /// /// If omitted, the system wide-root CAs installed on the system are used. /// Use this only if you trust the specified certificate. #[arg(long, requires("certs"))] pub root_ca: Option, } impl CertificateOptions { /// Returns the verifier of this [`CertificateOptions`] based on the given CLI options. /// /// - `protectee`: what you want to create. e.g. add-secret request or SE-image /// /// # Errors /// /// This function will return an error if [`crate::request::HkdVerifier`] cannot be created. fn verifier(&self, protectee: &'static str) -> Result> { use pv::request::{CertVerifier, NoVerifyHkd}; match self.no_verify { true => { log::warn!( "Host-key document verification is disabled. The {protectee} may not be protected." ); Ok(Box::new(NoVerifyHkd)) } false => Ok(Box::new(CertVerifier::new( &self.certs, &self.crls, self.root_ca.as_ref(), self.offline, )?)), } } /// Read the host-keys specified and verifies them if required /// /// - `protectee`: what you want to create. e.g. add-secret request or SE-image /// /// # Error /// Returns an error if something went wrong during parsing the HKDs, the verification chain /// could not built, or when the verification /// failed. pub fn get_verified_hkds(&self, protectee: &'static str) -> Result>> { let hkds = &self.host_key_documents; let verifier = self.verifier(protectee)?; let mut res = Vec::with_capacity(hkds.len()); for hkd in hkds { let hk = read_file(hkd, "host-key document")?; let certs = read_certs(&hk).map_err(|source| Error::HkdNotPemOrDer { hkd: hkd.display().to_string(), source, })?; if certs.is_empty() { return Err(Error::NoHkdInFile(hkd.display().to_string())); } if certs.len() != 1 { warn!( "The host-key document in '{}' contains more than one certificate!", hkd.display() ) } // Panic: len is == 1 -> unwrap will succeed/not panic let c = certs.first().unwrap(); verifier.verify(c)?; res.push(c.public_key()?); info!("Use host-key document at '{}'", hkd.display()); } Ok(res) } } /// stdout pub const STDOUT: &str = "-"; /// stdin pub const STDIN: &str = "-"; /// Converts an argument value into a Writer. pub fn get_writer_from_cli_file_arg>(path: P) -> Result> { if path.as_ref() == Path::new(STDOUT) { Ok(Box::new(std::io::stdout())) } else { Ok(Box::new(create_file(path)?)) } } /// Converts an argument value into a Reader. pub fn get_reader_from_cli_file_arg>(path: P) -> Result> { if path.as_ref() == Path::new(STDIN) { Ok(Box::new(std::io::stdin())) } else { Ok(Box::new(open_file(path)?)) } } /// Print an error that occurred during CLI parsing pub fn print_cli_error(e: clap::Error, mut cmd: Command) -> ExitCode { let ret = if e.use_stderr() { ExitCode::FAILURE } else { ExitCode::SUCCESS }; // Ignore any errors during printing of the error let _ = e.format(&mut cmd).print(); ret } /// Print an error to stderr pub fn print_error(e: &E, verbosity: LevelFilter) -> ExitCode where // Error trait is not required, but here to limit the usage to errors E: AsRef + std::fmt::Debug + std::fmt::Display, { if verbosity > LevelFilter::Warn { // Debug formatter also prints the whole error stack // So only print it when on verbose eprintln!("error: {e:?}") } else { eprintln!("error: {e}") }; ExitCode::FAILURE } #[derive(Args, Debug, Clone, Default)] pub struct VerbosityOptions { #[arg( long, short = 'v', action = ArgAction::Count, global = true, display_order = 999, )] /// Provide more detailed output. verbose: u8, #[arg( long, short = 'q', action = ArgAction::Count, global = true, conflicts_with = "verbose", display_order = 999, )] /// Provide less output. quiet: u8, } const fn to_level_filter(v: u8) -> LevelFilter { match v { 0 => LevelFilter::Off, 1 => LevelFilter::Error, 2 => LevelFilter::Warn, 3 => LevelFilter::Info, 4 => LevelFilter::Debug, 5.. => LevelFilter::Trace, } } impl VerbosityOptions { fn verbosity(&self) -> u8 { (LevelFilter::Warn as i16 + self.verbose as i16 - self.quiet as i16) .clamp(u8::MIN.into(), u8::MAX.into()) as u8 } pub fn to_level_filter(&self) -> LevelFilter { to_level_filter(self.verbosity()) } } #[derive(Args, Debug, Clone, Default)] pub struct DeprecatedVerbosityOptions { #[clap(flatten)] verbosity: VerbosityOptions, #[arg( short = 'V', action = ArgAction::Count, global = true, hide = true, )] /// Provide more detailed output. deprecated_verbose: u8, } impl DeprecatedVerbosityOptions { pub fn to_level_filter(&self) -> LevelFilter { if self.deprecated_verbose > 0 { // Use eprintln as the logger is most likely not yet initialized. eprintln!("WARNING: Use of deprecated flag '-V'. Use '-v' or '--verbose' instead.") } to_level_filter( self.verbosity .verbosity() .saturating_add(self.deprecated_verbose), ) } } #[cfg(test)] mod test { use clap::Parser; use super::*; #[test] #[rustfmt::skip] fn cli_args() { //Verify only that some arguments are optional, we do not want to test clap, only the //configuration let valid_args = [vec!["pgr", "-k", "hkd.crt", "--no-verify"], vec!["pgr", "-k", "hkd.crt", "--crt", "abc.crt"]]; // Test for the minimal amount of flags to yield an invalid combination let invalid_args = [ vec!["pgr", "-k", "hkd.crt"], vec!["pgr", "--no-verify", "--crt", "abc.crt"], vec!["pgr", "--no-verify", "--crt", "abc.crt", "--offline"], vec!["pgr", "--no-verify", "--crt", "abc.crt", "--crl", "abc.crl"], vec!["pgr", "--no-verify", "--crt", "abc.crt", "--root-ca", "root.crt"], vec!["pgr", "--offline"], vec!["pgr", "--crl", "abc.crl"], vec!["pgr", "--root-ca", "root.crt"], ]; #[derive(Parser, Debug)] struct TestParser { #[command(flatten)] pub verify_args: CertificateOptions, } for arg in valid_args { let res = TestParser::try_parse_from(&arg); assert!(res.is_ok()); } for arg in invalid_args { let res = TestParser::try_parse_from(&arg); assert!(res.is_err()); } } } s390-tools-2.38.0/rust/utils/src/exit_code.rs000066400000000000000000000124171502674226300207110ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct ExitCodeVariantDoc { pub name: String, pub value: String, pub doc: String, } impl ExitCodeVariantDoc { pub fn new(name: N, value: V, doc: D) -> Self where N: AsRef, V: AsRef, D: AsRef, { Self { name: name.as_ref().to_string(), value: value.as_ref().to_string(), doc: doc.as_ref().to_string(), } } } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct ExitCodeDoc { pub doc: Option, pub variants: Vec, } pub trait ExitCodeTrait { fn exit_code_doc() -> ExitCodeDoc; } pub fn docstring(attr: &str) -> Option { if !attr.starts_with("doc = r\"") { return None; } let mut doc = attr .strip_prefix("doc = r\"") .unwrap() .strip_suffix("\"") .unwrap() .to_string(); if doc.starts_with(" ") { doc = doc.strip_prefix(" ").unwrap().to_string(); } Some(doc) } #[macro_export] macro_rules! impl_exitcodetrait { ($(#[$attr:meta])* $vis:vis enum $name:ident $(<$($gen:ident),*>)? { $( $(#[$variantattr:meta])+ $variant:ident = $tvalue:literal ),* $(,)? } ) => { $(#[$attr])* $vis enum $name $(<$($gen),*>)? { $( $(#[$variantattr])+ $variant = $tvalue ),* } impl ExitCodeTrait for $(<$($gen),*>)? $name $(<$($gen),*>)? { fn exit_code_doc() -> $crate::ExitCodeDoc { let enum_doc_vec = [$(stringify!($attr)),*].into_iter().map($crate::docstring).filter_map(std::convert::identity).collect::>(); let enum_doc = (!enum_doc_vec.is_empty()).then_some(enum_doc_vec.join("\n")); let mut variants = vec![]; $( let name = stringify!($variant).to_string(); let value = stringify!($tvalue).to_string(); let docs: Vec<_> = [$(stringify!($variantattr)),+].into_iter().map($crate::docstring).filter_map(std::convert::identity).collect(); assert!(!docs.is_empty(), "Please add a docstring to enum variant '{name}' of '{}'", stringify!($name)); variants.push($crate::ExitCodeVariantDoc { name, value, doc: docs.join("\n")}); )* $crate::ExitCodeDoc { doc: enum_doc, variants, } } } }; } #[cfg(test)] mod tests { use crate::{exit_code::ExitCodeDoc, ExitCodeTrait, ExitCodeVariantDoc}; #[test] fn test_impl_exitcodetrait_with_doc() { impl_exitcodetrait!( /// Program exit codes /// /// Multiline. #[repr(u8)] #[allow(unused)] #[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord)] pub enum OwnExitCode { /// Program finished successfully /// /// Long description. Success = 0, /// Generic error #[default] GenericError = 1, /// Usage error UsageError = 2, // same exit code as used by `Clap` crate } ); assert_eq!( OwnExitCode::exit_code_doc(), ExitCodeDoc { doc: Some("Program exit codes\n\nMultiline.".to_string()), variants: vec![ ExitCodeVariantDoc::new( "Success", "0", "Program finished successfully\n\nLong description." ), ExitCodeVariantDoc::new("GenericError", "1", "Generic error"), ExitCodeVariantDoc::new("UsageError", "2", "Usage error") ] } ); assert_eq!(OwnExitCode::default(), OwnExitCode::GenericError); } #[test] fn test_impl_exitcodetrait_without_doc() { impl_exitcodetrait!( #[repr(u8)] #[allow(unused)] #[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord)] pub enum OwnExitCode { /// Program finished successfully /// /// Long description. Success = 0, /// Generic error #[default] GenericError = 1, /// Usage error UsageError = 2, // same exit code as used by `Clap` crate } ); assert_eq!( OwnExitCode::exit_code_doc(), ExitCodeDoc { doc: None, variants: vec![ ExitCodeVariantDoc::new( "Success", "0", "Program finished successfully\n\nLong description." ), ExitCodeVariantDoc::new("GenericError", "1", "Generic error"), ExitCodeVariantDoc::new("UsageError", "2", "Usage error") ] } ); assert_eq!(OwnExitCode::default(), OwnExitCode::GenericError); } } s390-tools-2.38.0/rust/utils/src/file.rs000066400000000000000000000221541502674226300176640ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::{ ffi::{CString, OsStr}, fs::{File, OpenOptions}, io::{self, Seek, SeekFrom, Write}, os::unix::{ffi::OsStrExt, fs::OpenOptionsExt}, path::Path, }; use pv::{Error, FileAccessErrorType, PvCoreError, Result}; /// Rust wrapper for `libc::renameat2` fn renameat2, Q: AsRef>(oldpath: P, newpath: Q, flags: u32) -> io::Result<()> { let oldpath_cstr = CString::new(oldpath.as_ref().as_os_str().as_bytes())?; let oldpath_raw = oldpath_cstr.into_raw(); let newpath_cstr = CString::new(newpath.as_ref().as_os_str().as_bytes())?; let newpath_raw = newpath_cstr.into_raw(); unsafe { // SAFETY: oldpath_raw and newpath_raw are valid CStrings because they were // generated by the `CString::new` function. let ret = libc::renameat2( libc::AT_FDCWD, oldpath_raw, libc::AT_FDCWD, newpath_raw, flags, ); // SAFETY: libc::renameat2 does not modify `newpath_raw` and is // therefore still valid. let _ = CString::from_raw(newpath_raw); // SAFETY: libc::renameat2 does not modify `oldpath_raw` and is // therefore still valid. let _ = CString::from_raw(oldpath_raw); if ret == -1 { return Err(io::Error::last_os_error()); } Ok(()) } } /// This type helps to perform atomic operations by writing to a temporary file /// and renaming it to the actual filename when the [`AtomicFile::finish`] /// function is called. If the [`AtomicFile::finish`] function is never called, /// the temporary file is automatically removed when it goes out of scope. It /// utilizes the `renameat2` libc function and its semantics. #[derive(Debug)] pub struct AtomicFile { /// Do not change the order! See comment in [`TempPath::drop`]. file: F, path: TempPath, } impl AsRef for AtomicFile { fn as_ref(&self) -> &F { &self.file } } impl AsMut for AtomicFile { fn as_mut(&mut self) -> &mut F { &mut self.file } } /// Enum used for more verbosity. #[derive(Debug)] pub enum AtomicFileOperation { /// Replace existing file Replace, /// Do not replace existing file NoReplace, } impl AtomicFile {} impl AtomicFile { /// Creates a new [`AtomicFile`] at the given `output` path using /// `open_options`. /// /// # Errors /// /// This function will return an error if the temporary file could not be /// created. /// /// # Example /// /// ``` /// # use utils::AtomicFile; /// /// let file = AtomicFile::new("test", &mut std::fs::OpenOptions::new()).unwrap(); /// ``` pub fn new>(output: P, open_options: &mut OpenOptions) -> Result { Self::with_extension(output, "part", open_options) } /// Creates a new [`AtomicFile`] at the given `output` path using /// `open_options` and `suffix` as the suffix for the temporary file. /// /// # Errors /// /// This function will return an error if the temporary file could not be /// created. /// /// # Example /// /// ``` /// # use utils::AtomicFile; /// /// let file = AtomicFile::with_extension("test", ".incomplete", &mut std::fs::OpenOptions::new()).unwrap(); /// ``` pub fn with_extension, S: AsRef>( output: P, tmp_suffix: S, open_options: &mut OpenOptions, ) -> Result { let path = TempPath::new(output, tmp_suffix); open_options .read(true) .write(true) .create_new(true) .mode(0o600); match open_options.open(&path.temp_path) { Ok(file) => Ok(Self { path, file }), Err(err) => { let path_copy = path.temp_path.to_path_buf(); path.forget(); Err(Error::PvCore(PvCoreError::FileAccess { ty: FileAccessErrorType::Create, path: path_copy, source: err, })) } } } /// Renames the file to the actual file name. If `overwrite` is set to /// [`AtomicFileOperation::Replace`] the file is overwritten. /// /// # Errors /// /// This function will return an error if the rename operation fails. pub fn finish(mut self, overwrite: AtomicFileOperation) -> Result<()> { self.file.flush()?; self.file.sync_all()?; drop(self.file); self.path.persist(overwrite) } /// Discard all changes and delete the temporary file. /// /// # Errors /// /// This function will return an error if the temporary file could not be /// deleted. pub fn discard(mut self) -> io::Result<()> { self.file.flush()?; self.file.sync_all()?; drop(self.file); self.path.remove_file() } } impl Seek for AtomicFile { /// Seek to the offset (in bytes) in the underlying file. fn seek(&mut self, pos: SeekFrom) -> io::Result { self.as_mut().seek(pos) } } impl Write for AtomicFile { fn write(&mut self, buf: &[u8]) -> io::Result { self.as_mut().write(buf) } fn flush(&mut self) -> io::Result<()> { self.as_mut().flush() } } #[derive(Debug)] struct TempPath { temp_path: Box, path: Box, } impl TempPath { fn new, S: AsRef>(path: P, extension: S) -> Self { let mut temp_path = path.as_ref().to_path_buf(); match temp_path.extension() { Some(ext) => { let mut ext = ext.to_os_string(); ext.push("."); ext.push(extension.as_ref()); temp_path.set_extension(ext) } None => temp_path.set_extension(extension.as_ref()), }; Self { temp_path: temp_path.into(), path: path.as_ref().into(), } } fn forget(self) { // Make sure that [`Self::drop`] is not called. Self { .. } = self; } /// Removes the temporary file pub fn remove_file(self) -> io::Result<()> { let result = std::fs::remove_file(&self.temp_path); self.forget(); result } fn persist(self, operation: AtomicFileOperation) -> Result<()> { let options = match operation { AtomicFileOperation::Replace => 0, AtomicFileOperation::NoReplace => libc::RENAME_NOREPLACE, }; renameat2(&self.temp_path, &self.path, options).map_err(|e| { PvCoreError::FileAccessRename { src: self.temp_path.as_ref().to_str().unwrap().to_string(), dst: self.path.as_ref().to_str().unwrap().to_string(), source: e, } })?; self.forget(); Ok(()) } } impl Drop for TempPath { // When this is called while dropping an [`AtomicFile`], // the field [`AtomicFile::file`] is already dropped. fn drop(&mut self) { let _ = std::fs::remove_file(&self.temp_path); } } #[cfg(test)] mod tests { use std::io::Write; use super::{AtomicFile, AtomicFileOperation}; use crate::TemporaryDirectory; #[test] fn atomicfile_basic_functionality() { let tmp_dir = TemporaryDirectory::new().expect("should work"); let tmp_file = tmp_dir.path().join("atomic_basic"); let data = b"test_data"; assert!(!tmp_file.exists()); let mut writer = AtomicFile::with_extension(&tmp_file, "basic", &mut std::fs::OpenOptions::new()) .unwrap(); writer.write_all(data).unwrap(); writer.finish(AtomicFileOperation::Replace).unwrap(); assert!(tmp_file.exists()); let writer = AtomicFile::with_extension(&tmp_file, "basic", &mut std::fs::OpenOptions::new()) .unwrap(); writer.finish(AtomicFileOperation::NoReplace).expect_err( "Creating the file is expected to fail", ); std::fs::remove_file(tmp_file).expect("should not fail"); } #[test] fn atomicfile_discard() { let tmp_dir = TemporaryDirectory::new().expect("should work"); let tmp_file = tmp_dir.path().join("atomic_close"); let data = b"test_data"; assert!(!tmp_file.exists()); let mut writer = AtomicFile::with_extension(&tmp_file, "close", &mut std::fs::OpenOptions::new()) .unwrap(); writer.write_all(data).unwrap(); writer.discard().expect("bla"); assert!(!tmp_file.exists()); } #[test] fn atomicfile_drop() { let tmp_dir = TemporaryDirectory::new().expect("should work"); let tmp_file = tmp_dir.path().join("atomic_close"); let data = b"test_data"; assert!(!tmp_file.exists()); let mut writer = AtomicFile::with_extension(&tmp_file, "close", &mut std::fs::OpenOptions::new()) .unwrap(); writer.write_all(data).unwrap(); drop(writer); assert!(!tmp_file.exists()); } } s390-tools-2.38.0/rust/utils/src/hexslice.rs000066400000000000000000000042141502674226300205460ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use serde::Serialize; use std::fmt::{Display, Formatter}; /// Displays/Serializes an u8-slice into a Hex-string /// /// Thin wrapper around an u8-slice. #[derive(Debug, Default, PartialEq, Eq, Clone)] pub struct HexSlice<'a>(&'a [u8]); impl<'a> HexSlice<'a> { /// Creates a [`HexSlice`] from the given value. pub fn from(s: &'a T) -> Self where T: ?Sized + AsRef<[u8]> + 'a, { s.into() } } impl<'a, T> From<&'a T> for HexSlice<'a> where T: ?Sized + AsRef<[u8]> + 'a, { fn from(value: &'a T) -> Self { Self(value.as_ref()) } } impl Serialize for HexSlice<'_> { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { serializer.serialize_str(&format!("{self:#}")) } } impl Display for HexSlice<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { if f.alternate() && !f.sign_minus() { write!(f, "0x")?; } for byte in self.0 { if f.sign_minus() && f.alternate() { write!(f, "0x")?; } write!(f, "{:0>2x}", byte)?; if f.sign_minus() { write!(f, " ")?; } } Ok(()) } } impl AsRef<[u8]> for HexSlice<'_> { fn as_ref(&self) -> &[u8] { self.0 } } #[cfg(test)] mod test { use super::*; #[test] fn display_minus() { let hex = HexSlice::from(&[0; 8]); let exp = "00 00 00 00 00 00 00 00 "; assert_eq!(exp, format!("{hex:-}")); } #[test] fn display() { let hex = HexSlice::from(&[0; 8]); let exp = "0000000000000000"; assert_eq!(exp, format!("{hex}")); } #[test] fn display_alternate() { let hex = HexSlice::from(&[0; 8]); let exp = "0x0000000000000000"; assert_eq!(exp, format!("{hex:#}")); } #[test] fn display_minus_alternate() { let hex = HexSlice::from(&[0; 8]); let exp = "0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 "; assert_eq!(exp, format!("{hex:-#}")); } } s390-tools-2.38.0/rust/utils/src/lib.rs000066400000000000000000000066531502674226300175210ustar00rootroot00000000000000// SPDX-License-Identifier: MIT //! Utils for s390-tools written in rust. //! Not intened to be used outside of s390-tools. //! //! Copyright IBM Corp. 2023, 2024 mod cli; mod exit_code; mod file; mod hexslice; mod log; mod tmpfile; pub use ::log::LevelFilter; pub use crate::{ cli::{ get_reader_from_cli_file_arg, get_writer_from_cli_file_arg, print_cli_error, print_error, CertificateOptions, DeprecatedVerbosityOptions, VerbosityOptions, STDIN, STDOUT, }, exit_code::{docstring, ExitCodeDoc, ExitCodeTrait, ExitCodeVariantDoc}, file::{AtomicFile, AtomicFileOperation}, hexslice::HexSlice, log::PvLogger, tmpfile::TemporaryDirectory, }; /// Get the s390-tools release string /// /// Provides the s390-tools release string. /// For release builds this reqquires the environment variable /// `S390_TOOLS_RELEASE` to be present at compile time. /// For debug builds this value defaults to `DEBUG_BUILD` /// if that variable is not present. /// Should only be used by binary targets!! /// /// Collapses to a compile time constant, that is likely to be inlined /// by the compiler in release builds. #[macro_export] macro_rules! release_string { () => {{ #[cfg(debug_assertions)] match option_env!("S390_TOOLS_RELEASE") { Some(ver) => ver, None => "DEBUG BUILD", } #[cfg(not(debug_assertions))] env!("S390_TOOLS_RELEASE", "env 'S390_TOOLS_RELEASE' must be set for release builds. Trigger build using the s390-tools build system or export the variable yourself") }}; } #[macro_export] macro_rules! __print_version { ($year: expr, $verbosity: expr $( ,$feat: expr)?) => {{ println!( "{} version {}\nCopyright IBM Corp. {}", env!("CARGO_PKG_NAME"), $crate::release_string!(), $year, ); if $verbosity > $crate::LevelFilter::Warn { $($feat.iter().for_each(|f| print!("{f} ")); println!("(compiled)");)? } if $verbosity > $crate::LevelFilter::Info { println!( "\n{}-crate {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"), ); } }}; } #[macro_export] /// Print the version to stdout /// /// `verbosity` (optional): `LogLevel` /// `feat` (optional): list of features /// `rel_str`: a string containig the release name macro_rules! print_version { ($year: expr $( ;$feat: expr)?) => { $crate::__print_version!($year, $crate::LevelFilter::Warn $(, $feat)*) }; ($year: expr, $verbosity: expr $( ;$feat: expr)?) => { $crate::__print_version!($year, $verbosity $(, $feat)*) }; } /// Asserts a constant expression evaluates to `true`. /// /// If the expression is not evaluated to `true` the compilation will fail. #[macro_export] macro_rules! static_assert { ($condition:expr) => { const _: () = core::assert!($condition); }; } /// Asserts that a type has a specific size. /// /// Useful to validate structs that are passed to C code. /// If the size has not the expected value the compilation will fail. /// /// # Example /// ```rust /// # use utils::assert_size; /// # fn main() {} /// #[repr(C)] /// struct c_struct { /// v: u64, /// } /// assert_size!(c_struct, 8); /// // assert_size!(c_struct, 7);//won't compile /// ``` #[macro_export] macro_rules! assert_size { ($t:ty, $sz:expr ) => { $crate::static_assert!(::std::mem::size_of::<$t>() == $sz); }; } s390-tools-2.38.0/rust/utils/src/log.rs000066400000000000000000000020301502674226300175150ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2023 use log::{self, Level, LevelFilter, Log, Metadata, Record}; /// A simple Logger that prints to stderr if the verbosity level is high enough. /// Prints log-level for Debug+Trace #[derive(Clone, Default, Debug)] pub struct PvLogger; impl PvLogger { /// Set self as the logger for this application. /// /// # Errors /// /// An error is returned if a logger has already been set. pub fn start(&'static self, filter: LevelFilter) -> Result<(), log::SetLoggerError> { log::set_logger(self).map(|()| log::set_max_level(filter)) } } impl Log for PvLogger { fn enabled(&self, _metadata: &Metadata) -> bool { true } fn log(&self, record: &Record) { if self.enabled(record.metadata()) { if record.level() > Level::Info { eprintln!("{}: {}", record.level(), record.args()); } else { eprintln!("{}", record.args()); } } } fn flush(&self) {} } s390-tools-2.38.0/rust/utils/src/tmpfile.rs000066400000000000000000000144571502674226300204140ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::{ ffi::{CString, OsStr}, os::unix::prelude::OsStrExt, path::{Path, PathBuf}, }; /// Rust wrapper for `libc::mkdtemp` fn mkdtemp>(template: P) -> Result { let template_cstr = CString::new(template.as_ref().as_os_str().as_bytes())?; let template_raw = template_cstr.into_raw(); unsafe { // SAFETY: template_raw is a valid CString because it was generated by // the `CString::new`. let ret = libc::mkdtemp(template_raw); // SAFETY: `template_raw` is still a valid CString because it was // generated by `CString::new` and modified by `libc::mkdtemp`. let path_cstr = std::ffi::CString::from_raw(template_raw); if ret.is_null() { drop(path_cstr); Err(std::io::Error::last_os_error()) } else { let path = OsStr::from_bytes(path_cstr.as_bytes()); let path = std::path::PathBuf::from(path); Ok(path) } } } /// This type creates a temporary directory that is automatically removed when /// it goes out of scope. It utilizes the `mkdtemp` function and its semantics, /// with the addition of automatically including the template characters /// `XXXXXX`. #[derive(PartialEq, Eq, Debug)] pub struct TemporaryDirectory { path: Box, } impl TemporaryDirectory { /// Creates a temporary directory in the current working directory using /// 'tmp.' as directory prefix. /// /// # Errors /// /// This function will return an error if the temporary directory could not /// be created. /// /// # Example /// /// ``` /// # use utils::TemporaryDirectory; /// let temp = TemporaryDirectory::new().unwrap(); /// ``` pub fn new() -> Result { Self::with_prefix("tmp.") } /// Creates a temporary directory in the current working directory using /// `prefix` as directory prefix. /// /// # Errors /// /// This function will return an error if the temporary directory could not /// created. /// /// # Example /// /// ``` /// # use utils::TemporaryDirectory; /// let temp = TemporaryDirectory::with_prefix("test").unwrap(); /// ``` pub fn with_prefix>(prefix: P) -> Result { let mut template = prefix.as_ref().to_owned(); let template_os_string = template.as_mut_os_string(); template_os_string.push("XXXXXX"); let temp_dir = mkdtemp(template_os_string)?; Ok(Self { path: temp_dir.into_boxed_path(), }) } /// Returns a reference to the path of the created temporary directory. pub fn path(&self) -> &Path { self.path.as_ref() } /// Takes ownership and releases the memory and makes sure no destructor is /// called and therefore the temporary directory will not be removed. fn forget(mut self) { self.path = PathBuf::new().into_boxed_path(); std::mem::forget(self); } /// Removes the created temporary directory and it's contents. /// /// # Errors /// /// This function will return an error if the temporary directory could not /// removed. pub fn close(self) -> std::io::Result<()> { let ret = std::fs::remove_dir_all(&self.path); self.forget(); ret } } impl AsRef for TemporaryDirectory { fn as_ref(&self) -> &Path { self.path() } } impl Drop for TemporaryDirectory { fn drop(&mut self) { let _ = std::fs::remove_dir_all(&self.path); } } #[cfg(test)] mod tests { use super::{mkdtemp, TemporaryDirectory}; #[test] fn mkdtemp_test() { let template_inv_not_last_characters = "XXXXXXyay"; let template_inv_too_less_x = "yayXXXXX"; let template_inv_path_does_not_exist = "../NA-yay/XXXXXX"; let template = "yayXXXXXX"; let _err = mkdtemp(template_inv_not_last_characters).expect_err("invalid template"); let _err = mkdtemp(template_inv_too_less_x).expect_err("invalid template"); let _err = mkdtemp(template_inv_path_does_not_exist).expect_err("path does not exist template"); let path = mkdtemp(template).expect("mkdtemp should work"); assert!(path.exists()); assert!(path.as_os_str().to_str().expect("works").starts_with("yay")); std::fs::remove_dir(path).unwrap(); } #[test] fn temporary_directory_resides_in_cwd() { let temp_dir = TemporaryDirectory::new().expect("should work"); let path = temp_dir.path().to_owned(); let cwd = std::env::current_dir().unwrap(); assert_eq!(path.canonicalize().unwrap().parent().unwrap(), cwd); } #[test] fn temporary_directory_close_test() { let temp_dir = TemporaryDirectory::new().expect("should work"); let path = temp_dir.path().to_owned(); assert!(path.exists()); // Test that close removes the directory temp_dir.close().unwrap(); assert!(!path.exists()); } #[test] fn temporary_directory_drop_test() { let temp_dir = TemporaryDirectory::new().expect("should work"); let path = temp_dir.path().to_owned(); assert!(path.exists()); // Test that the destructor removes the directory drop(temp_dir); assert!(!path.exists()); } #[test] fn temporary_directory_prefix_test() { let prefix = "yay"; let temp_dir = TemporaryDirectory::with_prefix(prefix).expect("should work"); let path = temp_dir.path().to_owned(); assert!(path.exists()); assert!(path .as_os_str() .to_str() .expect("works") .starts_with(prefix)); } #[test] fn temporary_directory_empty_prefix_test() { let temp_dir = TemporaryDirectory::with_prefix("").expect("should work"); let path = temp_dir.path().to_owned(); assert!(path.exists()); // Path consists only of the rendered template. assert_eq!(path.as_os_str().len(), "XXXXXX".len()); } #[test] fn temporary_directory_as_ref_test() { let temp_dir = TemporaryDirectory::new().expect("should work"); assert_eq!(temp_dir.path(), temp_dir.as_ref()); } } s390-tools-2.38.0/scripts/000077500000000000000000000000001502674226300151365ustar00rootroot00000000000000s390-tools-2.38.0/scripts/.gitignore000066400000000000000000000000151502674226300171220ustar00rootroot00000000000000/.zfcpdbf.ct s390-tools-2.38.0/scripts/Makefile000066400000000000000000000025141502674226300166000ustar00rootroot00000000000000include ../common.mak SCRIPTS = dbginfo.sh zfcpdbf zipl-switch-to-blscfg sclpdbf # Helper scripts controlled by corresponding systemd services SD_HELPER_SCRIPTS = cpictl dumpconf MAN_PAGES = dbginfo.sh.8 zfcpdbf.8 zipl-switch-to-blscfg.8 all: install: $(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8 @for i in $(SCRIPTS); \ do \ cat $$i | \ sed -e 's/%S390_TOOLS_VERSION%/$(S390_TOOLS_RELEASE)/' \ >$(DESTDIR)$(BINDIR)/$$i; \ chown $(OWNER):$(GROUP) $(DESTDIR)$(BINDIR)/$$i; \ chmod 755 $(DESTDIR)$(BINDIR)/$$i; \ done @for i in $(SD_HELPER_SCRIPTS); \ do \ cat $$i | \ sed -e 's/%S390_TOOLS_VERSION%/$(S390_TOOLS_RELEASE)/' \ >$(DESTDIR)$(TOOLS_LIBDIR)/$$i; \ chown $(OWNER):$(GROUP) $(DESTDIR)$(TOOLS_LIBDIR)/$$i; \ chmod 755 $(DESTDIR)$(TOOLS_LIBDIR)/$$i; \ done @for i in $(MAN_PAGES); \ do \ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 $$i \ $(DESTDIR)$(MANDIR)/man8; \ done clean: .zfcpdbf.ct: zfcpdbf $(PERLC) $(ALL_PERLCFLAGS) zfcpdbf 2>$(@); \ if [ "$${?}" -ne 0 ]; then \ cat "$(@)" >&2 && rm "$(@)"; \ elif [ "$$(cat '$(@)' | wc -l)" -gt 1 ]; then \ cat "$(@)" >&2; \ elif [ " $(V)" = " 1" ]; then \ cat "$(@)"; \ fi .PHONY: .zfcpdbf.ct-clean .zfcpdbf.ct-clean: rm -f .zfcpdbf.ct all: .zfcpdbf.ct clean: .zfcpdbf.ct-clean .PHONY: all install clean s390-tools-2.38.0/scripts/check_hostkeydoc000077500000000000000000000223221502674226300203760ustar00rootroot00000000000000#!/bin/sh # # check_hostkeydoc - Verify an IBM Secure Execution host key document # # Sample script to verify that a host key document is genuine by # verifying the issuer, the validity date and the signature. # Optionally verify the full trust chain using a CA certificate. # # Sample invocation: # # ./check_hostkeydoc HKD1234.crt ibm-z-host-key-signing.crt -c DigiCertCA.crt -r ibm-z-host-key.crl # # Copyright IBM Corp. 2020 # # s390-tools is free software; you can redistribute it and/or modify # it under the terms of the MIT license. See LICENSE for details. # Allocate temporary files ISSUER_PUBKEY_FILE=$(mktemp) SIGNATURE_FILE=$(mktemp) BODY_FILE=$(mktemp) ISSUER_DN_FILE=$(mktemp) SUBJECT_DN_FILE=$(mktemp) DEF_ISSUER_ARMONK_DN_FILE=$(mktemp) DEF_ISSUER_POUGHKEEPSIE_DN_FILE=$(mktemp) CANONICAL_ISSUER_DN_FILE=$(mktemp) CRL_SERIAL_FILE=$(mktemp) CRL_ISSUER_FILE=$(mktemp) # Cleanup on exit cleanup() { rm -f "$ISSUER_PUBKEY_FILE" "$SIGNATURE_FILE" "$BODY_FILE" \ "$ISSUER_DN_FILE" "$SUBJECT_DN_FILE" "$DEF_ISSUER_ARMONK_DN_FILE" "$DEF_ISSUER_POUGHKEEPSIE_DN_FILE" \ "$CANONICAL_ISSUER_DN_FILE" "$CRL_SERIAL_FILE" "$CRL_ISSUER_FILE" } trap cleanup EXIT # Enhanced error checking for bash if [ -n "${BASH}" ]; then # shellcheck disable=SC3040 set -o posix # shellcheck disable=SC3040 set -o pipefail # shellcheck disable=SC3040 set -o nounset fi set -e # Usage usage() { cat <<-EOF Usage: $(basename "$1") [-d] [-c CA-cert] [-r CRL] host-key-doc signing-key-cert Verify an IBM Secure Execution host key document against a signing key. Use for resolving issues only. Use the built-in functions from pvimg, pvsecret, or pvattest directly to verify the host-key documents. This script should only be used as a last resort for when the distribution provided binaries have unresolved issues regarding the host-key verification. In that case ensure the latest version of this script is used. Find the latest version here https://github.com/ibm-s390-linux/s390-tools Options: -d disable default issuer check of host-key-doc -c CA-cert trusted CA certificate -r CRL list of revoked host-key-docs Note that in order to have the full trust chain verified it is necessary to provide the issuing CA's certificate. The default issuer check may be disabled if a non-default signing key certificate needs to be verified against the CA certificate. EOF } curl_crl_by_crt() { CRT="$1" OFFSET=$2 CRL_URL="$(openssl x509 -noout -text -in "$CRT" \ |grep 'URI:http.*\.crl' | sed -n "$OFFSET"p | xargs)" # no CRL at this offset if [ -z "$CRL_URL" ]; then return 5 fi curl --silent --output "${CRL_ISSUER_FILE}" -- "${CRL_URL#URI:}" } check_verify_chain() { # Verify certificate chain in case a CA certificate file/bundle # was specified on the command line. if [ -z "$2" ]; then cat >&2 <<-EOF !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! No CA certificate specified! Skipping trust chain verification. Make sure that '$1' is a valid certificate. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! EOF else openssl verify -crl_download -crl_check "$2" || exit 1 if ! stderr=$(openssl verify -crl_download -crl_check -untrusted "$2" "$1" 3>&2 2>&1 1>&3 3>&-); then if ! printf '%s' "${stderr}" | grep -q 'max resp len exceeded'; then printf '%s\n' "${stderr}" exit 1 fi # Turn off exit-on-error locally to be able to retry on invalid URIs set +e off=0; # Search for a CRL in the CRT. If the first link does not work try # again with the next one until either no more URIS are available or a # CRL was found. while [ $off -le 10 ]; do i=$(( i + 1 )) CRL=curl_crl_by_crt "$3" $i SUCCESS=$? # No more CRLS available if [ $SUCCESS -eq 5 ]; then exit 1 fi # URI was invalid/not reachable if [ $SUCCESS -ne 0 ]; then continue; fi # Found one break done openssl verify -CRLfile "${CRL_ISSUER_FILE}" -crl_check -untrusted "$2" "$1" || exit 1 fi set -e fi } extract_pubkey() { openssl x509 -in "$1" -pubkey -noout >"$2" } extract_signature() { # Assuming that the last field is the signature SIGOFFSET=$(openssl asn1parse -in "$1" | tail -1 | cut -d : -f 1) openssl asn1parse -in "$1" -out "$2" -strparse "$SIGOFFSET" -noout } extract_body() { # Assuming that the first field is the full cert body SIGOFFSET=$(openssl asn1parse -in "$1" | head -2 | tail -1 | cut -d : -f 1) openssl asn1parse -in "$1" -out "$2" -strparse "$SIGOFFSET" -noout } verify_signature() { # Assuming that the signature algorithm is SHA512 with RSA openssl sha512 -verify "$1" -signature "$2" "$3" } canonical_dn() { OBJTYPE=$1 OBJ=$2 DNTYPE=$3 OUTPUT=$4 openssl "$OBJTYPE" -in "$OBJ" -"$DNTYPE" -noout -nameopt multiline | LC_ALL=C sort | grep -v "$DNTYPE"= >"$OUTPUT" } default_issuer_armonk() { cat <<-EOF commonName = International Business Machines Corporation countryName = US localityName = Armonk organizationName = International Business Machines Corporation organizationalUnitName = Key Signing Service stateOrProvinceName = New York EOF } default_issuer_pougkeepsie() { cat <<-EOF commonName = International Business Machines Corporation countryName = US localityName = Poughkeepsie organizationName = International Business Machines Corporation organizationalUnitName = Key Signing Service stateOrProvinceName = New York EOF } # As organizationalUnitName can have an arbitrary prefix but must # end with "Key Signing Service" let's normalize the OU name by # stripping off the prefix verify_default_issuer() { default_issuer_pougkeepsie >"$DEF_ISSUER_POUGHKEEPSIE_DN_FILE" default_issuer_armonk >"$DEF_ISSUER_ARMONK_DN_FILE" sed "s/\(^[ ]*organizationalUnitName[ ]*=[ ]*\).*\(Key Signing Service$\)/\1\2/" \ "$ISSUER_DN_FILE" >"$CANONICAL_ISSUER_DN_FILE" if ! { diff "$CANONICAL_ISSUER_DN_FILE" "$DEF_ISSUER_POUGHKEEPSIE_DN_FILE" || diff "$CANONICAL_ISSUER_DN_FILE" "$DEF_ISSUER_ARMONK_DN_FILE" } >/dev/null 2>&1; then echo Incorrect default issuer >&2 && exit 1 fi } verify_issuer_files() { if [ "$1" -eq 1 ]; then verify_default_issuer fi } cert_time() { DATE=$(openssl x509 -in "$1" -"$2" -noout | sed "s/^.*=//") date -d "$DATE" +%s } crl_time() { DATE=$(openssl crl -in "$1" -"$2" -noout | sed "s/^.*=//") date -d "$DATE" +%s } verify_dates() { START="$1" END="$2" MSG="${3:-Certificate}" NOW=$(date +%s) if [ "$START" -le "$NOW" ] && [ "$NOW" -le "$END" ]; then echo "${MSG} dates are OK" else echo "${MSG} date verification failed" >&2 && exit 1 fi } crl_serials() { openssl crl -in "$1" -text -noout | grep "Serial Number" >"$CRL_SERIAL_FILE" } check_serial() { CERT_SERIAL=$(openssl x509 -in "$1" -noout -serial | cut -d = -f 2) grep -q "$CERT_SERIAL" "$CRL_SERIAL_FILE" } check_file() { [ -e "$1" ] || (echo "File '$1' not found" >&2 && exit 1) } # check args CRL_FILE= CA_FILE= CHECK_DEFAULT_ISSUER=1 while getopts 'dr:c:h' opt; do case $opt in d) CHECK_DEFAULT_ISSUER=0 ;; r) CRL_FILE=$OPTARG ;; c) CA_FILE=$OPTARG ;; h) usage "$0" exit 0 ;; ?) usage "$0" exit 1 ;; esac done shift "$((OPTIND - 1))" if [ $# -ne 2 ]; then usage "$0" >&2 exit 1 fi HKD_FILE=$1 HKSK_FILE=$2 printf "DEPRECATED SCRIPT. Use pvimg, pvattest, or pvsecret directly.\n" printf "This script is intended for resolving issues only.\n" printf "This script may be inaccessible in the future.\n" # Check whether all specified files exist check_file "$HKD_FILE" check_file "$HKSK_FILE" # CA and CRL are optional arguments [ -n "$CA_FILE" ] && check_file "$CA_FILE" [ -n "$CRL_FILE" ] && check_file "$CRL_FILE" # Check trust chain check_verify_chain "$HKSK_FILE" "$CA_FILE" "$HKSK_FILE" # Verify host key document signature printf "Checking host key document signature: " extract_pubkey "$HKSK_FILE" "$ISSUER_PUBKEY_FILE" && extract_signature "$HKD_FILE" "$SIGNATURE_FILE" && extract_body "$HKD_FILE" "$BODY_FILE" && verify_signature "$ISSUER_PUBKEY_FILE" "$SIGNATURE_FILE" "$BODY_FILE" || exit 1 # Verify the issuer canonical_dn x509 "$HKD_FILE" issuer "$ISSUER_DN_FILE" canonical_dn x509 "$HKSK_FILE" subject "$SUBJECT_DN_FILE" verify_issuer_files $CHECK_DEFAULT_ISSUER # Verify dates verify_dates "$(cert_time "$HKD_FILE" startdate)" "$(cert_time "$HKD_FILE" enddate)" # Check CRL if specified if [ -n "$CRL_FILE" ]; then printf "Checking CRL signature: " extract_signature "$CRL_FILE" "$SIGNATURE_FILE" && extract_body "$CRL_FILE" "$BODY_FILE" && verify_signature "$ISSUER_PUBKEY_FILE" "$SIGNATURE_FILE" "$BODY_FILE" || exit 1 printf "CRL " canonical_dn crl "$CRL_FILE" issuer "$ISSUER_DN_FILE" canonical_dn x509 "$HKSK_FILE" subject "$SUBJECT_DN_FILE" verify_issuer_files $CHECK_DEFAULT_ISSUER verify_dates "$(crl_time "$CRL_FILE" lastupdate)" "$(crl_time "$CRL_FILE" nextupdate)" 'CRL' crl_serials "$CRL_FILE" check_serial "$HKD_FILE" && echo "Certificate is revoked, do not use it anymore!" >&2 && exit 1 fi # We made it echo All checks requested for \'"$HKD_FILE"\' were successful s390-tools-2.38.0/scripts/cpictl000077500000000000000000000300011502674226300163340ustar00rootroot00000000000000#!/bin/bash # # cpictl - Configure the Control-Program-Information (CPI) settings # # This is an internal helper script that is used by the "cpi.service" # systemd unit and the "90-cpi.rules" udev rule. # # The bash shell is really needed. Other shells have different ideas of how # bitwise operators work. # # Copyright 2017 IBM Corp. # # s390-tools is free software; you can redistribute it and/or modify # it under the terms of the MIT license. See LICENSE for details. # readonly CPI_LOCK="/var/lock/cpictl.lock" readonly PRG="${0##*/}" readonly SYSTEM_LEVEL_PATH="/sys/firmware/cpi/system_level" readonly SYSTEM_TYPE_PATH="/sys/firmware/cpi/system_type" readonly SYSTEM_NAME_PATH="/sys/firmware/cpi/system_name" readonly SYSPLEX_NAME_PATH="/sys/firmware/cpi/sysplex_name" readonly CPI_SET="/sys/firmware/cpi/set" # Location of os-release file - can be specified externally for testing purpose readonly OS_RELEASE=${CPI_OS_RELEASE:-"/etc/os-release"} declare LEVEL declare TYPE declare NAME declare SYSPLEX declare -i DRYRUN=0 # Exit codes readonly EXIT_SUCCESS=0 readonly EXIT_FAILURE=1 readonly EXIT_ARG_TOO_LONG=3 readonly EXIT_INVALID_CHARS=4 readonly EXIT_INVALID_ARGS=5 # Distro-IDs as supported by SE/HMC firmware readonly DISTRO_GENERIC=0 readonly DISTRO_RHEL=1 readonly DISTRO_SLES=2 readonly DISTRO_UBUNTU=3 readonly DISTRO_FEDORA=4 readonly DISTRO_OPENSUSE=5 readonly DISTRO_DEBIAN=6 readonly DISTRO_RHCOS=7 print_help_and_exit() { cat < or [[[flags:]distro_id:distro_version:]kernel_version] -N, --name SYSTEM Set and commit the system name to SYSTEM -S, --sysplex SYSPLEX Set and commit the sysplex name to SYSPLEX -T, --type TYPE Set and commit OS type to TYPE -v, --version Print version information, then exit --commit Ignore all other options and commit any uncommitted values --dry-run Do not actually set or commit anything, but show what would be done --show Ignore all other options and show current (possibly uncommitted) values Environment variables used for the --defaults option: CPI_SYSTEM_TYPE, CPI_SYSTEM_LEVEL, CPI_SYSTEM_NAME, CPI_SYSPLEX_NAME Available bits for the --set-bit option: kvm: Indicate that system is a KVM host The maximum length for system type, system name, and sysplex name is 8 characters. The allowed characters are: [A-Z0-9 @#$] This tool should be called as root. EndHelp exit $EXIT_SUCCESS } print_version_and_exit() { cat <&2 exit $EXIT_FAILURE } fail_with() { echo "$1" >&2 echo "Try '$PRG --help' for more information." >&2 exit ${2:-$EXIT_FAILURE} } cpi_commit() { echo 1 > "$CPI_SET" 2> /dev/null } do_length_check() { [ ${#1} -gt 8 ] && fail_with "$PRG: Specified $2 too long. The maximum length is 8 characters." $EXIT_ARG_TOO_LONG } do_character_check() { echo "$1" | grep -q -E '^[a-zA-Z0-9@#$ ]*$' || fail_with "$PRG: Invalid characters in $2. Valid characters are: A-Z0-9 @#$" $EXIT_INVALID_CHARS } cpi_set_bit() { LEVEL=$(printf '0x%x' $((LEVEL | (1 << (63 - $1)) )) ) } # # split_version - Split generic version string into array of sub-versions # # @version: Version string # @delim: Characters that delimit sub-versions in version string # @num: Number of sub-versions # # Print @num sub-versions of @version where each sub-version is delimited by # any of the characters in @delim. Print 0 in place of non-decimal or missing # sub-versions. # # Examples: # version=10 delim=. num=3 => 10 0 0 # version=4.8 delim=. num=2 => 4 8 # split_version() { local version="$1" delim="$2" num="$3" local list i subver IFS="$delim" read -r -a list <<< "$version" for (( i=0; i 2 4 0 13 # version=3.0.93_3.0.101-0.8.2_0.8.1 num=6 => 3 0 93 3 0 101 # version=4.12.14-lp150.11.4 num=5 => 4 12 14 0 11 # split_kver() { local version="$1" num="$2" local main extra # Separate extra version to handle short main version (e.g. 2.4-13) IFS="-_" read -r main extra <<< "$version" split_version "$main" "." $(( num > 3 ? 3 : num )) [[ "$num" -gt 3 ]] && split_version "$extra" ".-_" $(( num - 3 )) } # # bytes_to_word - Convert byte array to hexadecimal word # # @bytes: List of numbers representing byte values # # Print a big-endian hexadecimal representation of the word that results from # combining the specified byte values. # bytes_to_word() { printf "0x" printf "%02x" "$@" } # # get_system_level - Print system level word for specified distribution version # # @distro: Distro ID (ID value from /etc/os-release) # @ver_str: Distro version string (VERSION_ID from /etc/os-release) # @kver_str: Kernel version string (output of 'uname -r') # @flags: Optional statistics flags # # Print a 64 bit hexadecimal system level in a format as understood by firmware. # # The format is 0xabccddeeeeffgghh, where # - a=statistics flags # - b=distro id # - c=distro major version # - d=distro minor version(s) # - e=kernel sublevel 2 # - f=kernel version # - g=kernel patchlevel # - h=kernel sublevel 1 # get_system_level() { local distro="$1" ver_str="$2" kver_str="$3" flags="${4:-0}" local distro_id d_major d_minor d_minor2 local k_ver k_patchlvl k_sublvl k_sublvl2 bytes=() # Extract list of sub-version numbers from version strings read -r d_major d_minor d_minor2 <<< "$(split_version "$ver_str" "._-" 3)" read -r k_ver k_patchlvl k_sublvl k_sublvl2 <<< "$(split_kver "$kver_str" 4)" # Handle excessive sublevel numbers consistently if [[ "$k_sublvl" -gt 255 ]] ; then k_sublvl=0 fi if [[ "$k_sublvl2" -gt 65535 ]] ; then k_sublvl2=0 fi # Apply distro-specific logic case "$distro" in "rhel") distro_id=$DISTRO_RHEL ;; "sles") distro_id=$DISTRO_SLES ;; "ubuntu") distro_id=$DISTRO_UBUNTU # Encode minor and update version numbers in minor field (( d_minor=((d_minor & 0xf) * 0x10) + (d_minor2 & 0xf) )) ;; "fedora") distro_id=$DISTRO_FEDORA ;; "opensuse-leap") distro_id=$DISTRO_OPENSUSE ;; "debian") distro_id=$DISTRO_DEBIAN ;; "rhcos") distro_id=$DISTRO_RHCOS ;; *) distro_id=$DISTRO_GENERIC # Reset unsupported fields d_major=0 d_minor=0 k_sublvl2=0 ;; esac # Assemble byte data (( bytes[0] = (flags & 0xf) * 0x10 + distro_id )) (( bytes[1] = d_major )) (( bytes[2] = d_minor )) (( bytes[3] = (k_sublvl2 / 256) & 0xff )) (( bytes[4] = k_sublvl2 & 0xff )) (( bytes[5] = k_ver )) (( bytes[6] = k_patchlvl )) (( bytes[7] = k_sublvl )) # Print as single hex word bytes_to_word "${bytes[@]}" } get_distro() { local line ID="linux" VERSION_ID="0" VERSION="" update [[ ! -e "$OS_RELEASE" ]] && return # Only import required variables while read -r line ; do if [[ "$line" =~ ^ID= ]] || [[ "$line" =~ ^VERSION_ID= ]] || [[ "$line" =~ ^VERSION= ]] ; then eval "$line" fi done <"$OS_RELEASE" if [[ "$ID" == "ubuntu" ]] ; then # Extract update version number only found in VERSION, e.g. # VERSION_ID="18.04" VERSION="18.04.5 LTS" update="${VERSION/*$VERSION_ID/}" update="${update%% *}" VERSION_ID="$VERSION_ID$update" fi echo "$ID:$VERSION_ID" } cpi_set_oslevel() { local level="${1:-}" local flags list distro_id distro_ver kver id ver if [[ "$level" =~ ^0x ]] && ! [[ "$level" =~ : ]] ; then # Format: level=0x printf -v LEVEL "0x%016x" "$level" 2>/dev/null || fail_with "$PRG: Invalid hexadecimal number in $level" \ $EXIT_INVALID_CHARS return fi # Format: level=[[[flags:]distro_id:distro_ver:]kver] IFS=":" read -r -a list <<< "$level:" kver="${list[*]: -1: 1}" distro_ver="${list[*]: -2: 1}" distro_id="${list[*]: -3: 1}" flags="${list[*]: -4: 1}" if [[ -z "$kver" ]] ; then # Use version of currently running kernel kver="$(uname -r)" fi if [[ -z "$distro_ver" ]] || [[ -z "$distro_id" ]] ; then # Use distro ID and version from os-release file IFS=":" read -r id ver <<< "$(get_distro)" distro_id=${distro_id:-$id} distro_ver=${distro_ver:-$ver} fi if [[ -z "$flags" ]] ; then # Keep statistics flags from current system level flags=$(( (LEVEL >> 60) & 0xf )) fi LEVEL=$(get_system_level "$distro_id" "$distro_ver" "$kver" "$flags") } cpi_set_type() { TYPE="$1" do_length_check "$TYPE" "system type" do_character_check "$TYPE" "system type" } cpi_set_sysplex() { SYSPLEX="$1" do_length_check "$SYSPLEX" "sysplex name" do_character_check "$SYSPLEX" "sysplex name" } cpi_set_name() { NAME="$1" do_length_check "$NAME" "system name" do_character_check "$NAME" "system name" } # cpictl starts here if [ $# -le 0 ]; then echo "$PRG: No parameters specified" print_parse_error_and_exit fi opts=$(getopt -o b:ehL:N:S:T:v -l set-bit:,environment,help,level:,name:,sysplex:,type:,commit,dry-run,show,version -n $PRG -- "$@") if [ $? -ne 0 ]; then print_parse_error_and_exit fi # This guarantees that only one instance will be running, and will serialize # the execution of multiple instances [ -e "$CPI_LOCK" -a ! -w "$CPI_LOCK" ] && fail_with "$PRG: Cannot access lock file: $CPI_LOCK" [ ! -w "${CPI_LOCK%/*}" ] && fail_with "$PRG: Cannot access lock file: $CPI_LOCK" exec 9<> "$CPI_LOCK" flock -x 9 # Get current values from sys/firmware read LEVEL < "$SYSTEM_LEVEL_PATH" read TYPE < "$SYSTEM_TYPE_PATH" read NAME < "$SYSTEM_NAME_PATH" read SYSPLEX < "$SYSPLEX_NAME_PATH" # Parse command line options: Use eval to remove getopt quotes eval set -- $opts while [ -n $1 ]; do case "$1" in --help|-h) print_help_and_exit ;; --version|-v) print_version_and_exit ;; -b|--set-bit) case "$2" in kvm) cpi_set_bit 0 ;; *) fail_with "$PRG: Unknown bit \"$2\" for the $1 option" ;; esac shift 2 ;; -L|--level) cpi_set_oslevel "$2" shift 2 ;; -e|--environment) cpi_set_type "$CPI_SYSTEM_TYPE" cpi_set_name "$CPI_SYSTEM_NAME" cpi_set_oslevel "$CPI_SYSTEM_LEVEL" cpi_set_sysplex "$CPI_SYSPLEX_NAME" shift ;; -T|--type) cpi_set_type "$2" shift 2 ;; -S|--sysplex) cpi_set_sysplex "$2" shift 2 ;; -N|--name) cpi_set_name "$2" shift 2 ;; --show) cpi_show exit $EXIT_SUCCESS ;; --commit) cpi_commit exit $EXIT_SUCCESS ;; --dry-run) DRYRUN=1 shift ;; --) shift break ;; *) break; ;; esac done # Unparsed options are not supported if [ $# -ne 0 ]; then fail_with "$PRG: Invalid command-line option: $*" $EXIT_INVALID_ARGS fi # Print settings for --dry-run or commit them to sysfs otherwise if [ $DRYRUN -eq 1 ]; then cat <<-EndDryrun System type: $TYPE System level: $LEVEL System name: $NAME Sysplex name: $SYSPLEX EndDryrun else echo "$LEVEL" > "$SYSTEM_LEVEL_PATH" echo "$TYPE" > "$SYSTEM_TYPE_PATH" echo "$NAME" > "$SYSTEM_NAME_PATH" echo "$SYSPLEX" > "$SYSPLEX_NAME_PATH" cpi_commit fi exit $EXIT_SUCCESS s390-tools-2.38.0/scripts/dbginfo.sh000077500000000000000000001306741502674226300171200ustar00rootroot00000000000000#!/bin/sh # # dbginfo.sh - Tool to collect runtime, configuration, and trace information # # Copyright IBM Corp. 2002, 2025 # # s390-tools is free software; you can redistribute it and/or modify # it under the terms of the MIT license. See LICENSE for details. # # Switching to neutral locale LC_ALL=C export LC_ALL ######################################## # Global used variables readonly SCRIPTNAME="${0##*/}" # general name of this script readonly FULLPATHSCRIPT="$(readlink -f "${0}")" # readonly DATETIME="$(date +%Y-%m-%d-%H-%M-%S 2>/dev/null)" readonly DOCKER=$(if type docker >/dev/null 2>&1; then echo "YES"; else echo "NO"; fi) readonly DUMP2TAR_OK=$(if type dump2tar >/dev/null 2>&1; then echo "YES"; else echo "NO"; fi) readonly HW="$(uname -m 2>/dev/null)" readonly IFS_ORI="${IFS}" # retrieve and split kernel version readonly KERNEL_BASE="$(uname -r 2>/dev/null)" readonly KERNEL_VERSION=$(echo ${KERNEL_BASE} | cut -d'.' -f1 ) readonly KERNEL_MAJOR_REVISION=$(echo ${KERNEL_BASE} | cut -d'.' -f2 ) readonly KERNEL_MINOR_REVISION=$(echo ${KERNEL_BASE} | cut -d'.' -f3 | sed 's/[^0-9].*//g') readonly KERNEL_INFO=${KERNEL_VERSION}.${KERNEL_MAJOR_REVISION}.${KERNEL_MINOR_REVISION} readonly KUBERNETES=$(if type kubectl >/dev/null 2>&1; then echo "YES"; else echo "NO"; fi) readonly KUBERNETES_LOG_LINES=50 readonly KVM=$(if type virsh >/dev/null 2>&1; then echo "YES"; else echo "NO"; fi) # The file to indicate that another instance of the script is already running readonly LOCKFILE="/tmp/${SCRIPTNAME}.lock" # check limits for logfiles like /var/log/messages readonly LOG_FILE_SIZE_CHECK=50 # max logfile size in MB readonly LOG_FILE_AGE_CHECK=7 # age in days to include for size checking # Mount point of the debug file system readonly MOUNT_POINT_DEBUGFS="/sys/kernel/debug" # Network devices readonly NETWORK_DEVS=$(cd /sys/class/net; ls -d */ 2>/dev/null | sed 's/\///g') # distro info readonly OSPRETTY="$(cat /etc/os* 2>/dev/null | grep -m1 PRETTY_NAME | sed 's/\"//g')" readonly OS_NAME="${OSPRETTY##*=}" readonly PODMAN=$(if type podman >/dev/null 2>&1; then echo "YES"; else echo "NO"; fi) # The processor ID for the first processor readonly PROCESSORID="$(grep -E ".*processor 0:.*" /proc/cpuinfo | \ sed 's/.*identification[[:space:]]*\=[[:space:]]*\([[:alnum:]]*\).*/\1/g')" readonly PROCESSORVERSION="$(grep -E ".*processor 0:.*" /proc/cpuinfo | \ sed 's/.*version[[:space:]]*\=[[:space:]]*\([[:alnum:]]*\).*/\1/g')" if test "x${PROCESSORVERSION}" = "xFF" || test "x${PROCESSORVERSION}" = "xff"; then RUNTIME_ENVIRONMENT=$(grep -E "VM00.*Control Program.*" /proc/sysinfo | \ sed 's/.*:[[:space:]]*\([[:graph:]]*\).*/\1/g') else RUNTIME_ENVIRONMENT="LPAR" fi # check for Dynamic Partition Mode readonly DPM_UUID=$(grep "LPAR UUID" /proc/sysinfo | \ sed 's/.*:[[:space:]]*\([[:graph:]]*\).*/\1/g') if test "${#DPM_UUID}" -eq 0; then DPM_MODE="NO" else DPM_MODE="YES" fi readonly SYSTEMHOSTNAME="$(hostname -s 2>/dev/null)" # hostname of system being analysed readonly TERMINAL="$(tty 2>/dev/null)" # timeout seconds TOS / kill timeout TOKS readonly TOS=15 readonly TOKS=30 readonly TIMEOUT=$(if type timeout >/dev/null 2>&1; then echo "YES"; else echo "NO"; fi) readonly TIMEOUT_OK=$(if test "x${TIMEOUT}" = "xYES"; then if test $(timeout -k ${TOKS} ${TOS} uname 2>/dev/null) = $(uname); then echo "YES"; else echo "WRONG_VERSION"; fi else echo "NO"; fi) readonly ZDEV_CONF=$(lszdev --configured 2>/dev/null | wc -l) readonly ZDEV_OFF=$(lszdev --offline 2>/dev/null | wc -l) readonly ZDEV_ONL=$(lszdev --online 2>/dev/null | wc -l) paramWORKDIR_BASE="/tmp" # initial default path ######################################## # print dbginfo.sh version info print_version() { cat </dev/null | wc -l) if [ ${counter} -eq 1 ]; then echo " CHECK - ${logfile} may miss a rotation" else echo " OK - ${logfile}* may have a rotation "\ "in place: ${counter} files" fi done fi } ######################################## # print basic info and online checks print_check() { print_version cat </dev/null | wc -l) zdevice onl/conf/offl = ${ZDEV_ONL} / ${ZDEV_CONF} / ${ZDEV_OFF} Log file check =$(logfile_checker "/var/log*") Working directory = $(ls -d ${paramWORKDIR_BASE} 2>&1 && df -k ${paramWORKDIR_BASE}) $(ls -ltr ${paramWORKDIR_BASE}/DBGINFO*tgz 2>/dev/null | tail -2) $(ls ${LOCKFILE} 2>/dev/null && echo " Warning: dbginfo running since: $(cat ${LOCKFILE})") Tool/command dependency check successful: timeout = ${TIMEOUT_OK} dump2tar = ${DUMP2TAR_OK} This is a console output only - no data was saved using option -c ! EOF } ####################################### # Parsing the command line and pre checks while [ ${#} -gt 0 ]; do case ${1} in --help|-h) print_usage exit 0 ;; --version|-v) print_version exit 0 ;; --directory|-d) paramWORKDIR_BASE=${2} if test -z "${paramWORKDIR_BASE}"; then echo "${SCRIPTNAME}: Error: No directory specified for data collection!" echo exit 1 elif test ! -d "${paramWORKDIR_BASE}"; then echo "${SCRIPTNAME}: Error: The specified directory does not exist!" echo exit 1 else # jump to next param shift fi ;; --check|-c) print_check exit 0 ;; -*|--*|*) echo echo "${SCRIPTNAME}: invalid option \"${1}\"" echo "Try '${SCRIPTNAME} --help' for more information" echo exit 1 ;; esac # next parameter, if already last the final shift will do termination shift done # finally verification to run as root if test "$(/usr/bin/id -u 2>/dev/null)" -ne 0; then echo "${SCRIPTNAME}: Error: You must be user root to run data collection \"${SCRIPTNAME}\"!" echo "${SCRIPTNAME}: Hint: for base system information you may run \"${SCRIPTNAME} -c\"" exit 1 fi ######################################### # The base working directory and derieved path info readonly WORKDIR_BASE="$(echo "${paramWORKDIR_BASE}" | sed -e 's#/$##')/" # The current working directory for the actual script execution if test -z "${PROCESSORID}"; then readonly WORKDIR_CURRENT="DBGINFO-${DATETIME}-${SYSTEMHOSTNAME:-localhost}" else readonly WORKDIR_CURRENT="DBGINFO-${DATETIME}-${SYSTEMHOSTNAME:-localhost}-${PROCESSORID}" fi # The current path where the collected information is put together readonly WORKPATH="${WORKDIR_BASE}${WORKDIR_CURRENT}/" # The current TAR archive that finally includes all collected information readonly WORKARCHIVE="${WORKDIR_BASE}${WORKDIR_CURRENT}.tgz" # The log file of activities from this script execution readonly LOGFILE="${WORKPATH}dbginfo.log" # File names for output files per section (duplicates are ok) readonly OUTPUT_FILE_BRIDGE="${WORKPATH}network.out" readonly OUTPUT_FILE_CMD="${WORKPATH}runtime.out" readonly OUTPUT_FILE_COREDUMPCTL="${WORKPATH}coredump.out" # separate file needed readonly OUTPUT_FILE_DASD="${WORKPATH}dasd.out" readonly OUTPUT_FILE_DOCKER="${WORKPATH}docker_runtime.out" readonly OUTPUT_FILE_ETHTOOL="${WORKPATH}network.out" readonly OUTPUT_FILE_HYPTOP="${WORKPATH}runtime.out" readonly OUTPUT_FILE_JOURNALCTL="${WORKPATH}journalctl.out" readonly OUTPUT_FILE_KVM="${WORKPATH}kvm_runtime.out" readonly OUTPUT_FILE_LSOF="${WORKPATH}open_files.out" readonly OUTPUT_FILE_NETWORK="${WORKPATH}network.out" readonly OUTPUT_FILE_NVME="${WORKPATH}runtime.out" readonly OUTPUT_FILE_OVS="${WORKPATH}network.out" readonly OUTPUT_FILE_PODMAN="${WORKPATH}podman_runtime.out" readonly OUTPUT_FILE_ISW="${WORKPATH}installed_sw.out" readonly OUTPUT_FILE_TC="${WORKPATH}network.out" readonly OUTPUT_FILE_VMCMD="${WORKPATH}zvm_runtime.out" readonly OUTPUT_FILE_UDEVDB="${WORKPATH}udevdb.out" # Base file names for different output files - no extension ! readonly OUTPUT_FILE_KUBERNETES="${WORKPATH}kubernetes" readonly OUTPUT_FILE_OSAOAT="${WORKPATH}network" readonly OUTPUT_FILE_SYSFS="${WORKPATH}sysfs" # define order of collection steps # - state/debug files are collected first, to avoid overwriting by command execution ALL_STEPS="\ collect_sysfs\ collect_procfs\ collect_configfiles\ collect_initrd_configfiles\ collect_cmdsout\ collect_hyptop\ collect_vmcmdsout\ collect_network\ collect_osaoat\ collect_ethtool\ collect_tc\ collect_bridge\ collect_ovs\ collect_kvm\ collect_container\ collect_nvme\ collect_dasd\ collect_logfiles\ post_processing\ create_package\ environment_cleanup\ " # The amount of steps running the whole collections, without last cleanup readonly COLLECTION_COUNT=`expr $(echo ${ALL_STEPS} | wc -w) - 1` ######################################## # Collection of proc fs entries PROCFILES="\ /proc/buddyinfo\ /proc/cio_ignore\ /proc/cmdline\ /proc/cpuinfo\ /proc/crypto\ /proc/dasd/devices\ /proc/dasd/statistics\ /proc/devices\ /proc/diskstats\ /proc/interrupts\ /proc/iomem\ /proc/kallsyms\ /proc/mdstat\ /proc/meminfo\ /proc/misc\ /proc/modules\ /proc/mounts\ /proc/net/vlan\ /proc/net/bonding\ /proc/net/sockstat\ /proc/net/sockstat6\ /proc/net/softnet_stat\ /proc/partitions\ /proc/qeth\ /proc/qeth_perf\ /proc/qeth_ipa_takeover\ /proc/sched_debug\ /proc/schedstat\ /proc/service_levels\ /proc/slabinfo\ /proc/softirqs\ /proc/stat\ /proc/swaps\ /proc/sys/kernel\ /proc/sys/vm\ /proc/sysinfo\ /proc/version\ /proc/zoneinfo\ " # Adding files to PROCFILES in case scsi devices are available if test -e /proc/scsi; then PROCFILES="${PROCFILES}\ $(find /proc/scsi -type f -perm /444 2>/dev/null)\ " fi ######################################## LOGFILES="\ /root/anaconda-ks.cfg\ /root/original-ks.cfg\ /run/containerd/events.log\ /run/docker/libcontainerd/containerd/events.log\ /run/udev/chreiplzfcpmp-[0-9][0-9][a-z][a-z][a-z][a-z]-*\ /var/log/anaconda\ /var/log/anaconda.*\ /var/log/audit\ /var/log/boot*\ /var/log/crash/*/vmcore-dmesg.txt\ /var/log/cron*\ /var/log/dmesg*\ /var/log/dnf.*\ /var/log/dracut.log*\ /var/log/IBMtape.trace\ /var/log/IBMtape.errorlog\ /var/log/kdump.log\ /var/log/libvirt\ /var/log/lin_tape.trace\ /var/log/lin_tape.errorlog\ /var/log/messages*\ /var/log/openvswitch/ovs-vswitchd.log\ /var/log/openvswitch/ovsdb-server.log\ /var/log/pbl.log\ /var/log/sa\ /var/log/syslog*\ /var/log/yum.log\ /var/log/YaST2\ /var/log/zypp*\ " ######################################## CONFIGFILES="\ /boot/grub2/grub.cfg\ /boot/loader/entries/*.conf\ /boot/zipl/active_devices.txt\ /boot/zipl/config\ /conf/\ /etc/*.conf\ /etc/*release\ /etc/anacrontab\ /etc/apparmor.d\ /etc/audit\ /etc/auto.*\ /etc/cmdline\ /etc/cmdline.d\ /etc/conf.d\ /etc/containers\ /etc/cron.*\ /etc/crontab\ /etc/crypttab\ /etc/dasd.*\ /etc/default\ /etc/depmod.d\ /etc/dnf/dnf.conf\ /etc/docker\ /etc/dracut.conf.d\ /etc/exports\ /etc/fstab\ /etc/groups\ /etc/grub.d\ /etc/hosts*\ /etc/iscsi\ /etc/inittab\ /etc/kdump\ /etc/libvirt\ /etc/logrotate.d\ /etc/lvm\ /etc/modprobe.conf*\ /etc/modprobe.d\ /etc/modules-load.d/*.conf\ /etc/mtab\ /etc/multipath\ /etc/network\ /etc/networks\ /etc/opencryptoki/*.conf\ /etc/pam.d\ /etc/profile\ /etc/profile.d\ /etc/pki/tls/openssl.cnf\ /etc/rc.d\ /etc/rc.local\ /etc/resolv.*\ /etc/rsyslog.d\ /etc/selinux\ /etc/ssl/openssl.conf\ /etc/ssl/openssl.cnf\ /etc/sysconfig\ /etc/sysctl.d\ /etc/syslog*\ /etc/systemd\ /etc/udev*\ /etc/xinet.d\ /etc/zfcp.*\ /lib/modprobe.d\ $(find /lib/modules -name modules.dep 2>/dev/null)\ /lib/systemd/system/docker.service\ /lib/udev/rules.d\ /usr/lib/modprobe.d\ /usr/lib/modules-load.d/*.conf\ /usr/lib/systemd/system/\ /usr/local/lib/modprobe.d\ /usr/local/lib/modules-load.d/*.conf\ /run/modprobe.d\ /run/modules-load.d/*.conf\ /run/udev/chreiplzfcpmp-ipl-volume-id\ /run/udev/rules.d\ /run/zdev_id.env\ /run/zdev.*\ " ######################################## # system state commands (non alphabetical order here) CMDS="uname -a\ :uptime\ :timedatectl\ :chronyc sources\ :chronyc sourcestats\ :chronyc ntpdata\ :chronyc tracking\ :zhypinfo\ :last\ :lscpu -ae\ :lscpu -ye\ :lscpumf -i\ :lsmem\ :lsmod\ :lsshut\ :runlevel\ :sysctl -a\ :systemctl --all --no-pager show\ :systemctl --all --no-pager list-units\ :systemctl --all --no-pager list-unit-files\ :systemd-delta\ :ulimit -a\ :env --null | sort --zero-terminated | tr '\000' '\n'\ :find /boot -print0 | sort -z | xargs -0 -n 10 ls -ld\ :find /var/crash -print0 | sort -z | xargs -0 -n 10 ls -ld\ :df -h\ :df -i\ :mount\ " # Z device subsystem commands (first commands in non alphabetical order) CMDS="${CMDS}\ :lschp\ :lscss --vpm\ :lszdev\ :find /dev -print0 | sort -z | xargs -0 -n 10 ls -ld\ :lspci -t\ :lspci -vvv\ :lstape\ :smc_dbg\ " # block device and other scsi device commands (in non alphabetical order) # command groups itself have an intentional topical grouping and order CMDS="${CMDS}\ :lsblk\ :lsblk -o +FSTYPE,UUID\ :lsdasd\ :lsdasd -u\ :pvpath -qa\ :dmsetup ls\ :dmsetup ls --tree\ :dmsetup table\ :dmsetup table --target multipath\ :dmsetup status\ :multipathd -k'show config'\ :multipathd -k'show config local'\ :multipathd -k'show maps'\ :multipathd -k'show topo'\ :multipathd -k'show paths'\ :multipathd -k'show paths format %w|%a|%r|%L|%p|%i|%d|%D|%t|%T|%o|%0|%C' | sort\ :multipathd -k'show maps stats'\ :multipathd -k'show maps status'\ :multipathd -k'show status'\ :multipathd -k'show daemon'\ :multipathd -k'show blacklist'\ :multipathd -k'show devices'\ :multipath -v6 -ll\ :multipath -d\ :multipath -t\ :lszfcp\ :lszfcp -D\ :lszfcp -V\ :ziorep_config -ADM\ :lsscsi\ :fdisk -l\ :lvmconfig\ :lvdisplay -m\ :vgs -v\ :pvs -v\ :lvs -v\ " # crypto specific commands CMDS="${CMDS}\ :cpacfinfo # for MSA 13 info\ :cpacfinfo -m -f -a -n\ :ep11info -D\ :ep11info -H\ :ep11info -M\ :icainfo\ :icastats --all\ :ivp.e # IBM CCA package install check\ :ls -al /usr/lib64/opencryptoki/stdll\ :lszcrypt -VV\ :pkcsconf -i\ :pkcsconf -s\ :pkcsconf -l\ :pkcsconf -t\ :pkcsconf -m\ :systemctl status pkcsslotd\ " # product / distro specific commands CMDS="${CMDS}\ :java -version\ :qemu-ga -V\ :SPident # SLES service package\ " # commands with long output at the end CMDS="${CMDS}\ :cat /root/.bash_history\ :dmesg -s 1048576\ :ps -emo pid,tid,nlwp,policy,user,tname,ni,pri,psr,sgi_p,stat,wchan,start_time,time,pcpu,pmem,vsize,size,rss,share,command\ :ps -eHo pid,tid,nlwp,policy,user,tname,ni,pri,psr,sgi_p,stat,wchan,start_time,time,pcpu,pmem,vsize,size,rss,share,command\ :ps axX\ " # commands with separate output file CMDS="${CMDS}\ :apt list >> '${OUTPUT_FILE_ISW}'\ :snap list --all >> '${OUTPUT_FILE_ISW}'\ :coredumpctl && coredumpctl info -o ${OUTPUT_FILE_COREDUMPCTL}\ :journalctl --all --no-pager --lines=100000 --output=short-precise\ >> '${OUTPUT_FILE_JOURNALCTL}'\ :lsof >> '${OUTPUT_FILE_LSOF}'\ :rpm -qa | sort >> '${OUTPUT_FILE_ISW}'\ :udevadm info --export-db >> '${OUTPUT_FILE_UDEVDB}'\ " ######################################## # network comamnds for separate output file with ip -br a as header command NETWORK_CMDS="ip -br a\ :firewall-cmd --list-all\ :ifconfig -a\ :ip a sh\ :ip -s -s link\ :ip link show\ :ip neigh list\ :ip ntable\ :ip route list\ :ip route list table all\ :ip rule list\ :ipcs -a\ :iptables -L # about to be replaced by nftables\ :lsqeth\ :netstat -pantu\ :netstat -s\ :nft list tables # is replacing iptables\ :nm-tool\ :nstat -az\ :openssl engine # deprecated in OpenSSL 3.n\ :openssl list --providers 2>/dev/null # redirect help on older systems\ :opticsmon --module-info\ :route -n\ " ######################################## DOCKER_CMDS="docker version\ :docker info\ :docker images\ :docker network ls\ :docker ps -a\ :docker stats --no-stream\ :systemctl status docker.service\ " ######################################## PODMAN_CMDS="podman version\ :podman info\ :podman images\ :podman network ls\ :podman ps -a\ :podman stats --no-stream\ :podman pod stats -a --no-stream\ :ls -l /var/lib/containers/storage/overlay/* # list of files is sufficent\ :ls -l /var/log/crio/pods/*/*\ " ######################################## KUBERNETES_CMDS="kubectl version\ :oc version # get also OCP version if installed\ :kubectl top pod\ :kubectl top node\ :kubectl describe pod -A\ :kubectl describe node -A\ :kubectl describe endpoints\ :ls -l /var/log/containers # list of logs is sufficent\ :kubectl cluster-info dump >>${OUTPUT_FILE_KUBERNETES}.dmp\ " ######################################## VM_CMDS="q userid\ :q users\ :q privclass\ :q cplevel\ :q cpservice\ :q cpprot user\ :q specex\ :q ssi\ :q cpus\ :q srm\ :q vtod\ :q time full\ :q timezone\ :q loaddev\ :q v osa\ :q v dasd\ :q v crypto\ :q v fcp\ :q v pav\ :q v sw\ :q v st\ :q v nic\ :q st\ :q xstore\ :q xstore user system\ :q sxspages\ :q vmlan\ :q vswitch\ :q vswitch details\ :q vswitch access\ :q vswitch active\ :q vswitch accesslist\ :q vswitch promiscuous\ :q vswitch controller\ :q port group all active details\ :q set\ :q comm\ :q controller all\ :q fcp\ :q frames\ :q lan\ :q lan all details\ :q lan all access\ :q memassist\ :q nic\ :q pav\ :q proc\ :q proc topology\ :q mt\ :q qioass\ :q spaces\ :q swch all\ :q trace\ :q mdcache\ :q alloc page\ :q alloc spool\ :q dump\ :q dumpdev\ :q reorder VMUSERID\ :q quickdsp VMUSERID\ :q pcifunction\ :q vmrelocate\ :q syscontrol\ :ind load\ :ind sp\ :ind user\ " ############################################################################### KVM_CMDS="virsh version\ :virsh nodeinfo\ :virsh nodememstats\ :virsh nodecpustats\ :virsh list --all\ :virsh iface-list\ :virsh net-list\ :virsh nwfilter-list\ :virsh nodedev-list --tree\ :virsh pool-list\ :virt-host-validate\ " ######################################## collect_cmdsout() { local cmd pr_collect_output "command" IFS=: for cmd in ${CMDS}; do IFS=${IFS_ORI} call_run_command "${cmd}" "${OUTPUT_FILE_CMD}" done IFS="${IFS_ORI}" } ######################################## collect_network() { local cmd pr_collect_output "network" IFS=: for cmd in ${NETWORK_CMDS}; do IFS=${IFS_ORI} call_run_command "${cmd}" "${OUTPUT_FILE_NETWORK}" done IFS="${IFS_ORI}" } ######################################## collect_vmcmdsout() { local vm_command local cp_command local vm_cmds local vm_userid local module_loaded=1 local cp_buffer_size local rc_buffer_check if echo "${RUNTIME_ENVIRONMENT}" | grep -qi "z/VM" >/dev/null 2>&1; then pr_collect_output "z/VM" if type vmcp >/dev/null; then cp_command="vmcp" if ! lsmod 2>/dev/null | grep -q vmcp && modinfo vmcp >/dev/null 2>&1; then modprobe vmcp && module_loaded=0 && sleep 2 fi elif type hcp >/dev/null; then cp_command="hcp" if ! lsmod 2>/dev/null | grep -q cpint; then modprobe cpint && module_loaded=0 && sleep 2 fi else pr_log_stdout "${SCRIPTNAME}: Warning: No program to communicate to z/VM CP" pr_skip "z/VM: vmcp not available" return 1 fi vm_userid=$(${cp_command} q userid 2>/dev/null | sed -ne 's/^\([^[:space:]]*\).*$/\1/p') vm_cmds=$(echo "${VM_CMDS}" | sed "s/VMUSERID/${vm_userid}/g") IFS=: for vm_command in ${vm_cmds}; do IFS="${IFS_ORI}" # initialize buffer values for automatic resizing cp_buffer_size=2 rc_buffer_check=2 while test ${rc_buffer_check} -eq 2 && test ${cp_buffer_size} -lt 1024; do cp_buffer_size=$(( cp_buffer_size * 2 )) eval ${cp_command} -b ${cp_buffer_size}k "${vm_command}" \ >/dev/null 2>&1 rc_buffer_check=$? done call_run_command "${cp_command} -b ${cp_buffer_size}k ${vm_command}" \ "${OUTPUT_FILE_VMCMD}" IFS=: done IFS="${IFS_ORI}" if test ${module_loaded} -eq 0 && test "x${cp_command}" = "xhcp"; then rmmod cpint elif test ${module_loaded} -eq 0 && test "x${cp_command}" = "xvmcp"; then rmmod vmcp fi else pr_skip "z/VM: no z/VM environment" fi } ######################################## collect_hyptop() { local param local delay=1 # seconds local iter=5 local sec=`expr ${delay} \\* ${iter}` case ${RUNTIME_ENVIRONMENT} in "z/VM") param="\#,c,m,C:s,M:s,o" # z/VM guest fields ;; "LPAR") param="\#,T,c,e,m,C:s,E:s,M:s,o" # all LPAR fields ;; *) # KVM guest pr_skip "hyptop: not available for ${RUNTIME_ENVIRONMENT}" return 1 ;; esac pr_collect_output "hyptop for ${RUNTIME_ENVIRONMENT} - ${sec}s" call_run_command "hyptop -b -d ${delay} -n ${iter} -f ${param} -S c" "${OUTPUT_FILE_HYPTOP}" } ######################################## collect_procfs() { local file_name pr_collect "procfs" for file_name in ${PROCFILES}; do call_collect_file "${file_name}" done } ######################################## collect_sysfs() { local debugfs_mounted=0 local dir_name local file_name local rc=0 pr_collect "sysfs" if ! grep -qE "${MOUNT_POINT_DEBUGFS}.*debugfs" /proc/mounts 2>/dev/null; then if mount -t debugfs debugfs "${MOUNT_POINT_DEBUGFS}" >/dev/null 2>&1; then sleep 2 debugfs_mounted=1 else pr_log_stdout "${SCRIPTNAME}: Warning: Unable to mount debugfs \ at \"${MOUNT_POINT_DEBUGFS}\"" fi fi # Collect sysfs files using multiple threads (-J 1) while excluding # files known to block on read (-x). Stop reading a file that takes # more than 5 seconds (-T 5) such as an active ftrace buffer. # error messages are not written to the log if test "x${DUMP2TAR_OK}" = "xYES"; then dump2tar /sys -z -o "${OUTPUT_FILE_SYSFS}.tgz" \ -x '*/tracing/trace_pipe*' \ -x '*/page_idle/bitmap*' \ -x '*/tracing/per_cpu/*' \ -x '*/tracing/free_buffer' \ -x '*/devices/css[0-9]*/[0-9]*.*.*/[0-9]*.*.*/host[0-9]*/rport-[0-9]*:*-*/target[0-9]*:*:*/[0-9]*:*:*:*/block/sd*/mq/0/cpu[0-9]*' \ --ignore-failed-read -J 1 -T 5 2>>${OUTPUT_FILE_SYSFS}.err rc=$? else echo "${SCRIPTNAME}: Warning: dump2tar is unavailable" rc=255 fi if [ ${rc} -eq 0 ] ; then echo " all failed entries are logged to ${OUTPUT_FILE_SYSFS}.err" else if [ $rc -ne 255 ]; then echo "${SCRIPTNAME}: Warning: dump2tar failed with $rc" fi pr_log_stdout " Warning: falling back to slow path" call_run_command "find /sys -print0 | sort -z \ | xargs -0 -n 10 ls -ld" "${OUTPUT_FILE_SYSFS}.out" find /sys -noleaf -type d 2>/dev/null | while IFS= read -r dir_name; do mkdir -p "${WORKPATH}${dir_name}" done find /sys -noleaf -type f -perm /444 \ -a -not \( -path '*tracing*' -a -name "trace_pipe*" \) \ -a -not \( -path '*page_idle*' -a -name 'bitmap*' \) \ -a -not -path '*tracing/per_cpu*' \ -a -not -path '*/tracing/free_buffer' \ -a -not -path '*/devices/css[0-9]*/[0-9]*.*.*/[0-9]*.*.*/host[0-9]*/rport-[0-9]*:*-*/target[0-9]*:*:*/[0-9]*:*:*:*/block/sd*/mq/0/cpu[0-9]*' \ 2>/dev/null | while IFS= read -r file_name; do echo " ${file_name}" if ! dd if="${file_name}" status=noxfer iflag=nonblock \ of="${WORKPATH}${file_name}" >/dev/null 2>&1; then echo "${SCRIPTNAME}: Warning: failed to copy \"${file_name}\"" fi done fi if test ${debugfs_mounted} -eq 1; then umount "${MOUNT_POINT_DEBUGFS}" fi } ######################################## collect_logfiles() { local file_name pr_collect "log files" for file_name in ${LOGFILES}; do call_collect_file "${file_name}" done pr_log_stdout "$(logfile_checker "/var/log*")" } ######################################## collect_configfiles() { local file_name pr_collect "config files" for file_name in ${CONFIGFILES}; do call_collect_file "${file_name}" done } ######################################## collect_initrd_configfiles() { local file_name local rd rdbase rdreldir rdextract mypopd noglob mycat patterns="" pr_collect "initrd config files" cd "${WORKPATH}" || return mypopd="$OLDPWD" # (pushd alternative) # prevent expansion of glob special characters such as '*' noglob=$(set +o | grep -F noglob) set -o noglob # prepare relative configfiles paths for file_name in ${CONFIGFILES}; do # strip leading slash to make path relative as in initrd file_name=${file_name#/} # in contrast to call_collect_file(), cpio extract with patterns # does not recurse, but cpio wildcards match slash, so add suffix; # otherwise e.g. /lib/udev/rules.d or /etc/multipath only extracts # just that directory without any of its content file_name="${file_name}*" patterns="$patterns ${file_name}" done patterns="$patterns squash-root.img" $noglob # iterate over initrds for rd in /boot/initr* /boot/zipl/initr* /var/lib/kdump/initr*; do [ -f "$rd" ] || continue # (nullglob alternative) rdbase=${rd##*/} # basename rdreldir=${rd%/*} # dirname rdreldir=${rdreldir#/} # strip leading slash to make path relative mkdir -p "${rdreldir}" cd "${rdreldir}" || break # (pushd alternative) if type lsinitrd >/dev/null 2>&1; then lsinitrd "${rd}" > "${rdbase}.lsinitrd" fi if type lsinitramfs >/dev/null 2>&1; then lsinitramfs -l "${rd}" > "${rdbase}.lsinitramfs" fi cd - || break # change to $OLDPWD (popd alternative) rdextract=${rd#/} mkdir -p "${rdextract}" cd "${rdextract}" || break # (pushd alternative) case $(file "${rd}") in *cpio*) mycat="cat" ;; *gzip*) mycat="zcat" ;; *XZ*) mycat="xzcat" ;; *Zstandard*) mycat="zstdcat" ;; *bzip2*) mycat="bzcat" ;; *LZMA*) mycat="lzcat" ;; *) # change to $OLDPWD (popd alternative) cd - || { cd "$mypopd" || return; return; } continue ;; esac # prevent expansion of glob special characters such as '*' noglob=$(set +o | grep -F noglob) set -o noglob # we expect no early-initrd stuff on s390 so extract directly. # globbing is turned off and we want word split on $patterns: # shellcheck disable=SC2086 "$mycat" "${rd}" | cpio --extract --no-absolute-filenames \ --make-directories \ --preserve-modification-time $patterns $noglob if [ ! -f "squash-root.img" ]; then cd - || break # change to $OLDPWD (popd alternative) continue fi # prevent expansion of glob special characters such as '*' noglob=$(set +o | grep -F noglob) set -o noglob # globbing is turned off and we want word split on $patterns: # shellcheck disable=SC2086 unsquashfs -no-progress -dest "squashfs-root" "squash-root.img" \ $patterns $noglob rm -f "squash-root.img" cd - || break # change to $OLDPWD (popd alternative) done cd "$mypopd" || return } ######################################## collect_osaoat() { local osa_devices local osa_device if type qethqoat >/dev/null; then osa_devices=$(lsqeth 2>/dev/null | grep "Device name" \ | sed 's/D.*:[[:space:]]*\([^[:space:]]*\)[[:space:]]\+/\1/g' \ | sed 's/[()]//g' ) if test -n "${osa_devices}"; then pr_collect_output "osa oat" for osa_device in ${osa_devices}; do call_run_command "qethqoat ${osa_device}" \ "${OUTPUT_FILE_OSAOAT}.out" && call_run_command "qethqoat -r ${osa_device}" \ "${OUTPUT_FILE_OSAOAT}_${osa_device}.raw" done else pr_skip "osa oat: no devices" fi else pr_skip "osa oat: qethqoat not available" fi } ######################################## collect_ethtool() { local network_device if type ethtool >/dev/null; then if test -n "${NETWORK_DEVS}"; then pr_collect_output "ethtool" for network_device in ${NETWORK_DEVS}; do call_run_command "ethtool ${network_device}" \ "${OUTPUT_FILE_ETHTOOL}" call_run_command "ethtool -k ${network_device}" \ "${OUTPUT_FILE_ETHTOOL}" call_run_command "ethtool -a ${network_device}" \ "${OUTPUT_FILE_ETHTOOL}" call_run_command "ethtool -c ${network_device}" \ "${OUTPUT_FILE_ETHTOOL}" call_run_command "ethtool --per-queue ${network_device} \ --show-coalesce" "${OUTPUT_FILE_ETHTOOL}" call_run_command "ethtool -g ${network_device}" \ "${OUTPUT_FILE_ETHTOOL}" call_run_command "ethtool -i ${network_device}" \ "${OUTPUT_FILE_ETHTOOL}" call_run_command "ethtool -l ${network_device}" \ "${OUTPUT_FILE_ETHTOOL}" call_run_command "ethtool -P ${network_device}" \ "${OUTPUT_FILE_ETHTOOL}" call_run_command "ethtool -S ${network_device}" \ "${OUTPUT_FILE_ETHTOOL}" call_run_command "ethtool -T ${network_device}" \ "${OUTPUT_FILE_ETHTOOL}" call_run_command "ethtool --module-info ${network_device}" \ "${OUTPUT_FILE_ETHTOOL}" done else pr_skip "ethtool: no devices" fi else pr_skip "ethtool: not available" fi } ######################################## collect_tc() { local network_device if type tc >/dev/null; then if test -n "${NETWORK_DEVS}"; then pr_collect_output "Traffic Control" for network_device in ${NETWORK_DEVS}; do call_run_command "tc -s qdisc show dev ${network_device}" \ "${OUTPUT_FILE_TC}" done else pr_skip "Traffic Control: no devices" fi else pr_skip "Traffic Control: tc not available" fi } ######################################## collect_bridge() { local network_device if type bridge >/dev/null; then if test -n "${NETWORK_DEVS}"; then pr_collect_output "bridge" for network_device in ${NETWORK_DEVS}; do call_run_command "bridge -d link show dev ${network_device}" \ "${OUTPUT_FILE_BRIDGE}" call_run_command "bridge -s fdb show dev ${network_device}" \ "${OUTPUT_FILE_BRIDGE}" call_run_command "bridge -d mdb show dev ${network_device}" \ "${OUTPUT_FILE_BRIDGE}" done else pr_skip "bridge: no devices" fi else pr_skip "bridge: not available" fi } ######################################## # OpenVSwitch collect_ovs() { local ovscmd local bridge local ovscmds local ovsbrcmd local ovsbrcmds ovscmds="ovs-dpctl -s show\ :ovs-vsctl -t 5 show\ :ovsdb-client dump\ " if type ovs-vsctl >/dev/null; then pr_collect_output "OpenVSwitch" IFS=: for ovscmd in ${ovscmds}; do IFS=${IFS_ORI} call_run_command "${ovscmd}" "${OUTPUT_FILE_OVS}" done IFS="${IFS_ORI}" for bridge in ${ovs-vsctl list-br}; do ovsbrcmds="ovs-ofctl show ${bridge}\ :ovs-ofctl dump-flows ${bridge}\ :ovs-appctl fdb/show ${bridge}\ " IFS=: for ovsbrcmd in ${ovsbrcmds}; do IFS=${IFS_ORI} call_run_command "${ovsbrcmd}" "${OUTPUT_FILE_OVS}" done IFS="${IFS_ORI}" done else pr_skip "OpenVSwitch: ovs-vsctl not available" fi } ######################################## collect_container() { local container_list local item # check if container environment exists if test "x${DOCKER}" = "xYES" || test "x${KUBERNETES}" = "xYES" || test "x${PODMAN}" = "xYES"; then pr_collect_output "container host" else pr_skip "container host: not found" fi # for docker command exists if test "x${DOCKER}" = "xYES"; then pr_syslog_stdout " docker ..." IFS=: for item in ${DOCKER_CMDS}; do IFS=${IFS_ORI} call_run_command "${item}" "${OUTPUT_FILE_DOCKER}" done IFS="${IFS_ORI}" fi # for podman command exists if test "x${PODMAN}" = "xYES"; then pr_syslog_stdout " podman ..." IFS=: for item in ${PODMAN_CMDS}; do IFS=${IFS_ORI} call_run_command "${item}" "${OUTPUT_FILE_PODMAN}" done IFS="${IFS_ORI}" fi # for kubectl command exists if test "x${KUBERNETES}" = "xYES"; then pr_syslog_stdout " Kubernetes ..." container_list=$(kubectl top pod | grep -v "MEMORY(bytes)" | cut -d' ' -f1) IFS=: for item in ${KUBERNETES_CMDS}; do IFS=${IFS_ORI} call_run_command "${item}" "${OUTPUT_FILE_KUBERNETES}.out" done IFS="${IFS_ORI}" if test -n "${container_list}"; then for item in ${container_list}; do call_run_command "kubectl logs --tail=${KUBERNETES_LOG_LINES} ${item}"\ "${OUTPUT_FILE_KUBERNETES}.log" done fi fi } ######################################## collect_nvme() { local device if type nvme >/dev/null; then pr_collect_output "NVME storage" call_run_command "nvme list" "${OUTPUT_FILE_NVME}" for device in /dev/nvme[0-9]*; do if [ -c $device ]; then call_run_command "smartctl -x $device" "${OUTPUT_FILE_NVME}" call_run_command "nvme fw-log $device" "${OUTPUT_FILE_NVME}" call_run_command "nvme smart-log $device" "${OUTPUT_FILE_NVME}" call_run_command "nvme error-log $device" "${OUTPUT_FILE_NVME}" fi done else pr_skip "nvme: not available" fi } ######################################## collect_dasd() { local device if type dasdview >/dev/null; then pr_collect_output "DASD storage" call_run_command "lsdasd" "${OUTPUT_FILE_DASD}" # duplicate as file header for device in /dev/dasd*; do if [ -b $device ]; then call_run_command "dasdview -i $device" "${OUTPUT_FILE_DASD}" call_run_command "dasdview -t info $device" "${OUTPUT_FILE_DASD}" else echo "$device is no block device" >> ${OUTPUT_FILE_DASD} fi done else pr_skip "dasdview: not available" fi } ######################################## collect_kvm() { local cmd local domain_list local domain # check if KVM virsh command exists if [ "x${KVM}" = "xYES" ]; then pr_collect_output "KVM" IFS=: for cmd in ${KVM_CMDS}; do IFS=${IFS_ORI} call_run_command "${cmd}" "${OUTPUT_FILE_KVM}" done IFS="${IFS_ORI}" # domain/guest specific commands domain_list=$(virsh list --all --name) if test -n "${domain_list}"; then for domain in ${domain_list}; do call_run_command "virsh dominfo ${domain}" "${OUTPUT_FILE_KVM}" call_run_command "virsh domblklist ${domain}" "${OUTPUT_FILE_KVM}" call_run_command "virsh domstats ${domain}" "${OUTPUT_FILE_KVM}" done else echo "no KVM domains found" | tee -a ${OUTPUT_FILE_KVM} fi else pr_skip "KVM: no virsh command" fi } ######################################## post_processing() { local file_mtime local file_mtime_epoche local tmp_file local file_name local base_dir local dir_list pr_syslog_stdout ${step_num} "Postprocessing" # wipe possible passwords dir_list="${WORKPATH} \ ${WORKPATH}etc/ssl/ \ ${WORKPATH}etc/libvirt/" for base_dir in ${dir_list}; do find "${base_dir}" -maxdepth 2 -name "*xml" -o -name "*conf" -o -name "*cnf" \ 2>/dev/null | while read -r file_name; do file_mtime_epoche=$(stat --format=%Y "${file_name}") file_mtime=$(date +%Y%m%d%H%M.%S --date="@${file_mtime_epoche}") tmp_file=${file_name}.$$ echo " clean pw: ${file_name}" if ! sed "s/\(.*[Pp]assw.*=\).*/\1********/g" "${file_name}" > "${tmp_file}"; then echo "${SCRIPTNAME}: Warning: Postprocessing failed on ${file_name}" fi mv "${tmp_file}" "${file_name}" touch --time=mtime -t "${file_mtime}" "${file_name}" done done # compressing data folder to avoid full unpack for any DBGINFO base_dir="${WORKPATH}proc/" search4="kallsyms" find "${base_dir}" -name ${search4} 2>/dev/null | while read -r file_name; do tmp_file=${file_name}-${KERNEL_BASE}.tgz echo " compress: ${file_name}" if ! tar -czf "${tmp_file}" -C "${base_dir}" "${search4}"; then echo "${SCRIPTNAME}: Warning: Postprocessing failed on ${file_name}" echo else rm -f "${file_name}" fi done echo "log work directory state after post processing:" echo "du summary: $(du -ks ${WORKPATH})" df -k ${WORKPATH} } ######################################## # Be aware that this output must be # redirected into a separate logfile call_run_command() { local rc=0 local cmd="${1}" local logfile="${2}" # extract the raw_command and set cmd_type, as some shell might split into # several lines restrict the cmd echo to the first line only local raw_cmd=$(echo "${cmd}" | head -1 | sed -ne 's/^\([^[:space:]]*\).*$/\1/p') local cmd_type=$(type ${raw_cmd} | cut -d' ' -sf4,5) echo "#######################################################" >> "${logfile}" echo "${USER}@${SYSTEMHOSTNAME:-localhost}> ${cmd}" >> "${logfile}" # check calling command type if [ "X${cmd_type}" = "Xshell builtin" ]; then # command is a builtin (no use of timeout possible) eval "${cmd}" >> ${logfile} 2>&1 rc=$? elif [ "X${cmd_type}" != "Xnot found" ]; then if [ "x${TIMEOUT_OK}" = "xYES" ]; then eval timeout -k ${TOKS} ${TOS} "${cmd}" >> ${logfile} 2>&1 rc=$? else # fall back - call all existing commands without timeout eval "${cmd}" >> ${logfile} 2>&1 rc=$? fi else echo "${SCRIPTNAME}: Warning: Command \"${raw_cmd}\" not available" >> "${logfile}" echo >> "${logfile}" return 1 fi # log a warning on rc not 0 and define return if [ ${rc} ]; then echo >> "${logfile}" return 0 else echo "${SCRIPTNAME}: Warning: Command \"${cmd}\" failed" >> "${logfile}" echo >> "${logfile}" return 1 fi } ######################################## call_collect_file() { local file_name="${1}" local directory_name=$(dirname "${file_name}" 2>/dev/null) echo " ${file_name}" if test ! -e "${WORKPATH}${directory_name}"; then mkdir -p "${WORKPATH}${directory_name}" 2>&1 fi if ! cp -r --preserve=mode,timestamps -dL --parents "${file_name}" "${WORKPATH}" 2>&1; then return 1 else return 0 fi } ######################################## # print that an instance is already running print_alreadyrunning() { print_version cat < "${LOCKFILE}" fi if ! mkdir "${WORKPATH}" 2>/dev/null; then echo "${SCRIPTNAME}: Error: Target directory \"${WORKPATH}\" already exists or" echo " \"${WORKDIR_BASE}\" does not exist!" exit 1 fi chmod 0700 "${WORKPATH}" } ######################################## # create gzip-ped tar file create_package() { local rc_tar pr_syslog_stdout ${step_num} "Finalizing: Creating archive with collected data" # get a copy of the script used cp -p "${FULLPATHSCRIPT}" "${WORKPATH}" # create the archive cd "${WORKDIR_BASE}" touch "${WORKARCHIVE}" chmod 0600 "${WORKARCHIVE}" tar -czf "${WORKARCHIVE}" "${WORKDIR_CURRENT}" rc_tar=$? if [ $rc_tar -eq 0 ]; then chmod 0600 "${WORKARCHIVE}" pr_stdout " " pr_stdout "Collected data was saved to:" pr_stdout " >> ${WORKARCHIVE} <<" pr_stdout " " pr_stdout "Please review all collected data before sending to your" \ "service organization." pr_stdout " " elif [ $rc_tar -eq 127 ]; then pr_stdout " " pr_stdout "${SCRIPTNAME}: Error: tar command is not available!" pr_stdout " Please install the corresponding package!" else pr_stdout " " pr_stdout "${SCRIPTNAME}: Error: Collection of data failed!" pr_stdout " The creation of \"${WORKARCHIVE}\" was not successful." pr_stdout " Please check the directory \"${WORKDIR_BASE}\"" pr_stdout " to provide enough free available space." fi } ######################################## # Cleaning up the prepared/collected information environment_cleanup() { if ! rm -rf "${WORKPATH}" 2>/dev/null; then pr_stdout " " pr_stdout "${SCRIPTNAME}: Warning: Deletion of \"${WORKPATH}\" failed" pr_stdout " Please remove the directory manually" pr_stdout " " fi if ! rm -f "${LOCKFILE}" 2>/dev/null; then pr_stdout " " pr_stdout "${SCRIPTNAME}: Warning: Deletion of \"${LOCKFILE}\" failed" pr_stdout " Please remove the file manually" pr_stdout " " fi } ######################################## # Function to perform a cleanup in case of a received signal emergency_exit() { pr_stdout " " pr_stdout "${SCRIPTNAME}: Info: Data collection has been interrupted" pr_stdout " Cleanup of temporary collected data" environment_cleanup pr_stdout "${SCRIPTNAME}: Info: Emergency exit processed" pr_stdout " " logger -t "${SCRIPTNAME}" "Data collection interrupted" exit } ######################################## # Function to print to stdout when rediretion is active pr_stdout() { echo "${@}" >&8 } ######################################## # Function to print to stdout and into log file when rediretion is active pr_log_stdout() { echo "$@" echo "$@" >&8 } ######################################## # Function to print to stdout and into log file when rediretion is active pr_syslog_stdout() { echo "$@" >&8 echo echo "$(date +%H:%M:%S.%N) - " "$@" logger -t "${SCRIPTNAME}" "$@" } ######################################## # print "Collecting ... output" pr_collect_output() { pr_syslog_stdout ${step_num} "Collecting" $1 "output" } ######################################## # print "Collecting ..." like fs pr_collect() { pr_syslog_stdout ${step_num} "Collecting" "$@" } ######################################## # print "Skipping ..." info with reason pr_skip() { pr_syslog_stdout ${step_num} "Skip" "$@" } ############################################################################### # Running the script (main) environment_setup print_version # saving stdout/stderr and redirecting stdout/stderr into log file exec 8>&1 9>&2 >"${LOGFILE}" 2>&1 # run print_version again to be logged in dbginfo.log print_version # trap on SIGHUP=1 SIGINT=2 SIGTERM=15 trap emergency_exit SIGHUP SIGINT SIGTERM pr_log_stdout "" pr_log_stdout "Hardware platform = ${HW}" pr_log_stdout "Runtime environment = ${RUNTIME_ENVIRONMENT} - DPM: ${DPM_MODE}" pr_log_stdout "Kernel version = ${KERNEL_INFO} (${KERNEL_BASE})" pr_log_stdout "OS version / distro = ${OS_NAME}" pr_log_stdout "Date and time of info = ${DATETIME}" pr_log_stdout "" logger -t "${SCRIPTNAME}" "Starting data collection" # step counter current_step=1 # run all collection steps for step in ${ALL_STEPS}; do # generate step numbering step_num="${current_step} of ${COLLECTION_COUNT}: " # calling step procedure ${step} current_step=`expr ${current_step} + 1` done logger -t "${SCRIPTNAME}" "Data collection completed" exec 1>&8 2>&9 8>&- 9>&- #EOF s390-tools-2.38.0/scripts/dbginfo.sh.8000066400000000000000000000064201502674226300172520ustar00rootroot00000000000000.TH DBGINFO.SH 8 "07 2024" "s390-tools" .SH NAME dbginfo.sh \- collect runtime, configuration and trace information for debugging Linux on IBM Z .SH SYNOPSIS .br \fBdbginfo.sh\fP [OPTIONS] .br \fBdbginfo.sh\fP {\-h|\-c|\-v} .SH DESCRIPTION This script collects runtime, configuration and trace information that can be used to debug a Linux on IBM Z instance. For Linux on z/VM, the script also traces information about the z/VM system. Virtualization platform data is collected on a host serving this. The debug information is written to a file //DBGINFO\-\-