mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Compare commits
116 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f75c923627 | ||
|
|
bca8fa3f9c | ||
|
|
d713ea6389 | ||
|
|
f9fbe79e4f | ||
|
|
3cc0b4cdea | ||
|
|
eef9f622c0 | ||
|
|
889293e557 | ||
|
|
0c5b9f5d11 | ||
|
|
02af069d48 | ||
|
|
5e0056db8d | ||
|
|
4b5937f142 | ||
|
|
8d8d5e9746 | ||
|
|
5bdabe3e06 | ||
|
|
8a1db94d37 | ||
|
|
1911cba130 | ||
|
|
f4ed0b0ec6 | ||
|
|
6e42c527d7 | ||
|
|
f7d2339c6a | ||
|
|
cf5f373142 | ||
|
|
e09c675b70 | ||
|
|
6bd93f475c | ||
|
|
3f12bdf4c7 | ||
|
|
3aa7f6e298 | ||
|
|
4e28047eb4 | ||
|
|
f654b971d0 | ||
|
|
e742d1c9ae | ||
|
|
803b87e324 | ||
|
|
5107a7be16 | ||
|
|
1178291686 | ||
|
|
c8d1aa55b4 | ||
|
|
25a70ac5a8 | ||
|
|
6235a51d7c | ||
|
|
e1aec24e84 | ||
|
|
d9a6fb3ccf | ||
|
|
a47dd4570a | ||
|
|
8fa1b5a00b | ||
|
|
e537ab902e | ||
|
|
f0fb4a180a | ||
|
|
f957d895cd | ||
|
|
73c3a9e684 | ||
|
|
2ee1387f34 | ||
|
|
10cefb3337 | ||
|
|
7b369318dd | ||
|
|
cf2fb296f0 | ||
|
|
6cbd81f997 | ||
|
|
2025cf751f | ||
|
|
5a052103ae | ||
|
|
2ced5853b4 | ||
|
|
13edbb7d5e | ||
|
|
1643af4578 | ||
|
|
c7ac1e63bd | ||
|
|
fa3dd0ec20 | ||
|
|
34146c3f92 | ||
|
|
298ede17f5 | ||
|
|
d66ace8121 | ||
|
|
2e89722ef0 | ||
|
|
c8ad5f57d0 | ||
|
|
d2317943c0 | ||
|
|
198c9fc052 | ||
|
|
b3551f50a8 | ||
|
|
4ff360e621 | ||
|
|
87e8fa18cf | ||
|
|
4bd135c3c7 | ||
|
|
f69350e8bd | ||
|
|
5768d55a08 | ||
|
|
538264a8cd | ||
|
|
7f09f0e4ad | ||
|
|
e36084d7f2 | ||
|
|
bd18166a21 | ||
|
|
a6f2c38891 | ||
|
|
029a3f8f52 | ||
|
|
dc87aef335 | ||
|
|
d712806b39 | ||
|
|
2734724432 | ||
|
|
6176509076 | ||
|
|
1780efae07 | ||
|
|
922e8cbb2f | ||
|
|
eef0dd4fa4 | ||
|
|
f93f437f8b | ||
|
|
4e6d43e2ac | ||
|
|
922d29ee49 | ||
|
|
25475bdf42 | ||
|
|
09c413b1be | ||
|
|
8944c2f60d | ||
|
|
ff3fe4bf43 | ||
|
|
5fe2e19277 | ||
|
|
b8cf861aea | ||
|
|
6ca3576dbf | ||
|
|
7ea425db5e | ||
|
|
330ddb7adb | ||
|
|
d69b9fab4a | ||
|
|
4e02dc8751 | ||
|
|
0c8be405ce | ||
|
|
24677dc561 | ||
|
|
7e7c38755d | ||
|
|
229cd6d194 | ||
|
|
776e5d41d7 | ||
|
|
8d7e8a87b5 | ||
|
|
e24d8c936c | ||
|
|
39b6c4a273 | ||
|
|
da9b96fb12 | ||
|
|
7d855acd07 | ||
|
|
f961a2bf90 | ||
|
|
9f93af614f | ||
|
|
d529ba07d3 | ||
|
|
0fffb05d01 | ||
|
|
92b8409207 | ||
|
|
bc3134ba5a | ||
|
|
c7f55a182f | ||
|
|
5078be45b8 | ||
|
|
2418ea93fc | ||
|
|
13cac5de44 | ||
|
|
b6687925cc | ||
|
|
7ee51182bf | ||
|
|
ec3e5b4a75 | ||
|
|
0b5aac3d79 |
12
.checkpatch.conf
Normal file
12
.checkpatch.conf
Normal file
@@ -0,0 +1,12 @@
|
||||
--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
|
||||
134
.clang-format
Normal file
134
.clang-format
Normal file
@@ -0,0 +1,134 @@
|
||||
# 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"
|
||||
5
.codespellrc
Normal file
5
.codespellrc
Normal file
@@ -0,0 +1,5 @@
|
||||
[codespell]
|
||||
ignore-words-list = parm,parms
|
||||
skip = ''
|
||||
count = ''
|
||||
quiet-level = 3
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -95,6 +95,9 @@ zdev/src/chzdev_usage.c
|
||||
zdev/src/lszdev
|
||||
zdev/src/lszdev_usage.c
|
||||
zdsfs/zdsfs
|
||||
zdump/.check_dep_fuse
|
||||
zdump/.check_dep_zgetdump
|
||||
zdump/.detect_openssl.dep.c
|
||||
zdump/zgetdump
|
||||
zfcpdump/10-zfcpdump.install
|
||||
zfcpdump/cpioinit
|
||||
|
||||
@@ -21,6 +21,7 @@ List of all individuals having contributed content to s390-tools
|
||||
- Colin Walters
|
||||
- Dan Horak
|
||||
- Dan Horák
|
||||
- Daniel S. Haischt
|
||||
- Despina Papadopoulou
|
||||
- Dimitri John Ledkov
|
||||
- Eberhard Pasch
|
||||
|
||||
38
CHANGELOG.md
38
CHANGELOG.md
@@ -1,6 +1,43 @@
|
||||
Release history for s390-tools (MIT version)
|
||||
--------------------------------------------
|
||||
|
||||
* __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
|
||||
@@ -24,7 +61,6 @@ Release history for s390-tools (MIT version)
|
||||
- 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
|
||||
|
||||
@@ -4,8 +4,10 @@ Coding 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.
|
||||
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
|
||||
@@ -17,6 +19,39 @@ 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
|
||||
----------------------
|
||||
|
||||
|
||||
22
README.md
22
README.md
@@ -60,6 +60,7 @@ Package contents
|
||||
|
||||
* 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.
|
||||
@@ -309,16 +310,15 @@ build options:
|
||||
| net-snmp | `HAVE_SNMP` | osasnmpd |
|
||||
| glibc-static | `HAVE_LIBC_STATIC` | zfcpdump |
|
||||
| openssl | `HAVE_OPENSSL` | genprotimg, zkey, libekmfweb, |
|
||||
| | | libkmipclient, pvattest |
|
||||
| | | libkmipclient, pvattest, zgetdump |
|
||||
| cryptsetup | `HAVE_CRYPTSETUP2` | zkey-cryptsetup |
|
||||
| json-c | `HAVE_JSONC` | zkey-cryptsetup, libekmfweb, |
|
||||
| | | libkmipclient |
|
||||
| glib2 | `HAVE_GLIB2` | genprotimg, pvattest |
|
||||
| glib2 | `HAVE_GLIB2` | genprotimg, pvattest, zgetdump |
|
||||
| libcurl | `HAVE_LIBCURL` | genprotimg, libekmfweb, libkmipclient,|
|
||||
| | | pvattest |
|
||||
| libxml2 | `HAVE_LIBXML2` | libkmipclient |
|
||||
| systemd | `HAVE_SYSTEMD` | hsavmcore |
|
||||
| liblockfile | `HAVE_LOCKFILE` | ap-check |
|
||||
| libudev | `HAVE_LIBUDEV` | cpacfstatsd |
|
||||
|
||||
This table lists additional build or install options:
|
||||
@@ -376,6 +376,13 @@ the different tools are provided:
|
||||
- Packages: blktrace, multipath-tools, sg3-utils
|
||||
- Tools: rsync, tar, lsscsi
|
||||
|
||||
* 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
|
||||
@@ -505,8 +512,7 @@ the different tools are provided:
|
||||
GNU awk for the build process.
|
||||
|
||||
* ap-check:
|
||||
For building the ap-check mdevctl callout utility you need liblockfile
|
||||
version 1.14 or newer installed (liblockfile-devel.rpm). Also required is
|
||||
json-c version 0.13 or newer (json-c-devel.rpm).
|
||||
Tip: you may skip ap-check build by adding `HAVE_LOCKFILE=0` or `HAVE_JSONC=0`
|
||||
to the make invocation.
|
||||
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.
|
||||
|
||||
@@ -1,27 +1,23 @@
|
||||
include ../common.mak
|
||||
|
||||
MDEVCTL_DIR = /etc/mdevctl.d/
|
||||
MDEVCTL_SCRIPTS = /etc/mdevctl.d/scripts.d/
|
||||
MDEVCTL_CALLOUTS = /etc/mdevctl.d/scripts.d/callouts/
|
||||
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_LOCKFILE},0)
|
||||
all:
|
||||
$(SKIP) HAVE_LOCKFILE=0
|
||||
|
||||
install:
|
||||
$(SKIP) HAVE_LOCKFILE=0
|
||||
|
||||
else ifeq (${HAVE_JSONC},0)
|
||||
ifeq (${HAVE_JSONC},0)
|
||||
all:
|
||||
$(SKIP) HAVE_JSONC=0
|
||||
|
||||
install:
|
||||
$(SKIP) HAVE_JSONC=0
|
||||
else
|
||||
LDLIBS += -llockfile -ljson-c
|
||||
LDLIBS += -ljson-c
|
||||
|
||||
all: ap-check
|
||||
|
||||
@@ -39,6 +35,17 @@ install: all
|
||||
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:
|
||||
|
||||
@@ -133,7 +133,7 @@ static void ap_check_cleanup(struct ap_check_anchor *anc)
|
||||
if (anc->dev)
|
||||
vfio_ap_device_free(anc->dev);
|
||||
if (anc->cleanup_lock)
|
||||
ap_release_lock();
|
||||
ap_release_lock_callout();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -685,6 +685,14 @@ static int ap_check_handle_start(struct ap_check_anchor *anc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* 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");
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Check against all other active vfio-ap devices */
|
||||
rc = check_other_mdevs_sysfs(anc);
|
||||
/* Check against the system sysfs values for apmask/aqmask */
|
||||
@@ -767,7 +775,7 @@ out:
|
||||
*/
|
||||
static int ap_check_handle_post(void)
|
||||
{
|
||||
return ap_release_lock();
|
||||
return ap_release_lock_callout();
|
||||
}
|
||||
|
||||
/* For the specified device, print the attributes to stdout in JSON format */
|
||||
|
||||
15
ap_tools/ap-check.sh
Normal file
15
ap_tools/ap-check.sh
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/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 "$@"
|
||||
10
common.mak
10
common.mak
@@ -5,7 +5,7 @@ COMMON_INCLUDED = true
|
||||
# The variable "DISTRELEASE" should be overwritten in rpm spec files with:
|
||||
# "make DISTRELEASE=%{release}" and "make install DISTRELEASE=%{release}"
|
||||
VERSION = 2
|
||||
RELEASE = 23
|
||||
RELEASE = 25
|
||||
PATCHLEVEL = 0
|
||||
DISTRELEASE = build-$(shell date +%Y%m%d)
|
||||
S390_TOOLS_RELEASE = $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE)
|
||||
@@ -60,8 +60,10 @@ endef
|
||||
|
||||
$(eval $(call cmd_define, AS," AS ",$(CROSS_COMPILE)as))
|
||||
$(eval $(call cmd_define, CC," CC ",$(CROSS_COMPILE)gcc))
|
||||
$(eval $(call cmd_define, LINK," LINK ",$(CC)))
|
||||
$(eval $(call cmd_define, HOSTCC," HOSTCC ",gcc))
|
||||
$(eval $(call cmd_define, CXX," CXX ",$(CROSS_COMPILE)g++))
|
||||
$(eval $(call cmd_define, LINKXX," LINKXX ",$(CXX)))
|
||||
$(eval $(call cmd_define, CPP," CPP ",$(CROSS_COMPILE)gcc -E))
|
||||
$(eval $(call cmd_define, AR," AR ",$(CROSS_COMPILE)ar))
|
||||
$(eval $(call cmd_define, NM," NM ",$(CROSS_COMPILE)nm))
|
||||
@@ -81,8 +83,6 @@ CHECKTOOL = $(call echocmd," CHECK ",/$@)$(CHECK_SILENT)
|
||||
SKIP = echo " SKIP $(call reldir) due to"
|
||||
|
||||
INSTALL = install
|
||||
LINK = $(CC)
|
||||
LINKXX = $(CXX)
|
||||
CP = cp
|
||||
ifneq ("${V}","1")
|
||||
MAKEFLAGS += --quiet
|
||||
@@ -120,7 +120,7 @@ endif
|
||||
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 -xc - ) >/dev/null 2>&1 && echo -n yes)
|
||||
$2 -o /dev/null -x c - ) >/dev/null 2>&1 && echo -n yes)
|
||||
endef
|
||||
|
||||
#
|
||||
@@ -133,7 +133,7 @@ endef
|
||||
# $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 -xc - ) > /dev/null 2>&1; \
|
||||
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; \
|
||||
|
||||
@@ -356,6 +356,8 @@ static void line(char *header)
|
||||
putchar(',');
|
||||
printf("%ld", ctrname[i].total);
|
||||
comma = true;
|
||||
ctrname[i].total = 0;
|
||||
ctrname[i].hitcnt = false;
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
@@ -446,7 +448,7 @@ static bool add_countervalue(size_t idx, unsigned int cpu, unsigned long value)
|
||||
ctrname[idx].ccv = calloc(max_possible_cpus,
|
||||
sizeof(unsigned long));
|
||||
if (ctrname[idx].ccv)
|
||||
ctrname[idx].ccv[cpu] += value;
|
||||
ctrname[idx].ccv[cpu] = value;
|
||||
ctrname[idx].total += value;
|
||||
ctrname[idx].hitcnt = true;
|
||||
return true;
|
||||
@@ -458,10 +460,8 @@ static int test_read(struct s390_hwctr_read *read)
|
||||
size_t offset = 0;
|
||||
|
||||
/* Clear previous hit counters */
|
||||
for (unsigned int i = 0; i < max_possible_cpus; ++i) {
|
||||
check[i].sets_hit = 0;
|
||||
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) {
|
||||
|
||||
@@ -426,7 +426,7 @@ static const char *evt_selector(struct perf_event_attr *pa)
|
||||
static void evt_show(__u64 evtnum, const char *evtsel, struct pai_event_out *ev)
|
||||
{
|
||||
if (summary) {
|
||||
if (ev->type == PERF_RECORD_SAMPLE)
|
||||
if (ev->type == PERF_RECORD_SAMPLE && ev->raw)
|
||||
evtraw_show(evtnum, ev->raw);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
#define GETTEXT_PACKAGE "genprotimg"
|
||||
#include <glib.h>
|
||||
#include <glib/gi18n-lib.h>
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
#include "boot/linux_layout.h"
|
||||
#include "boot/s390.h"
|
||||
|
||||
@@ -57,11 +57,41 @@ struct linear_blockptr {
|
||||
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
|
||||
*/
|
||||
struct eckd_blockptr {
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
/*
|
||||
* Block pointers format identified as LEGACY_BLKPTR_FORMAT_ID.
|
||||
*/
|
||||
struct eckd_blockptr_legacy {
|
||||
uint16_t cyl;
|
||||
uint16_t head;
|
||||
uint8_t sec;
|
||||
@@ -70,10 +100,22 @@ struct eckd_blockptr {
|
||||
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_EXECUTE = 0x01,
|
||||
COMPONENT_LOAD = 0x02,
|
||||
COMPONENT_SIGNATURE = 0x03
|
||||
COMPONENT_TYPE_EXECUTE = 0x01,
|
||||
COMPONENT_TYPE_LOAD = 0x02,
|
||||
COMPONENT_TYPE_SIGNATURE = 0x03
|
||||
} component_type;
|
||||
|
||||
typedef enum {
|
||||
@@ -117,7 +159,8 @@ struct scsi_dump_sb {
|
||||
uint64_t csum_offset;
|
||||
uint64_t csum_size;
|
||||
uint64_t csum;
|
||||
} __packed;
|
||||
};
|
||||
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 */
|
||||
@@ -153,6 +196,7 @@ struct boot_info_bp_dump {
|
||||
|
||||
struct boot_info_bp_ipl {
|
||||
union {
|
||||
struct eckd_blockptr_legacy eckd_legacy;
|
||||
struct eckd_blockptr eckd;
|
||||
struct linear_blockptr lin;
|
||||
} bm_ptr;
|
||||
|
||||
@@ -90,5 +90,6 @@ void ap_list_remove_all(struct util_list *list);
|
||||
int ap_get_lock(void);
|
||||
int ap_get_lock_callout(void);
|
||||
int ap_release_lock(void);
|
||||
int ap_release_lock_callout(void);
|
||||
|
||||
#endif /* LIB_AP_H */
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
#include <stdio.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
/* A bus id of a DASD is 8 characters long. E.g. 0.0.4711 */
|
||||
#define DASD_BUS_ID_SIZE 9
|
||||
/* 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 */
|
||||
@@ -187,6 +187,68 @@ typedef struct format_check_t {
|
||||
#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 {
|
||||
@@ -207,27 +269,29 @@ struct hd_geometry {
|
||||
#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)
|
||||
|
||||
/********************************************************************************
|
||||
* SECTION: Further IOCTL Definitions (see fs.h and hdreq.h)
|
||||
*******************************************************************************/
|
||||
/* get read-only status (0 = read_write) */
|
||||
#define BLKROGET _IO(0x12, 94)
|
||||
/* re-read partition table */
|
||||
#define BLKRRPART _IO(0x12, 95)
|
||||
/* get block device sector size */
|
||||
#define BLKSSZGET _IO(0x12, 104)
|
||||
/* return device size in bytes (u64 *arg) */
|
||||
#define BLKGETSIZE64 _IOR(0x12, 114, size_t)
|
||||
|
||||
#ifndef __linux__ /* from <linux/hdreg.h> */
|
||||
#define HDIO_GETGEO 0x0301
|
||||
#endif
|
||||
@@ -245,5 +309,12 @@ 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 */
|
||||
|
||||
26
include/lib/util_lockfile.h
Normal file
26
include/lib/util_lockfile.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* @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_parent_lock(char *lockfile, int retries);
|
||||
|
||||
int util_lockfile_release(char *lockfile);
|
||||
int util_lockfile_parent_release(char *lockfile);
|
||||
|
||||
#endif /** LIB_UTIL_LOCKFILE_H @} */
|
||||
@@ -97,6 +97,24 @@ typedef struct volume_label
|
||||
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 */
|
||||
|
||||
@@ -32,6 +32,10 @@
|
||||
|
||||
#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))
|
||||
@@ -50,6 +54,10 @@
|
||||
#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)
|
||||
@@ -62,6 +70,7 @@
|
||||
#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.
|
||||
|
||||
@@ -68,6 +68,7 @@ typedef enum {
|
||||
PV_CERT_ERROR_MALFORMED_ROOT_CA,
|
||||
PV_CERT_ERROR_NO_CRL,
|
||||
PV_CERT_ERROR_NO_CRLDP,
|
||||
PV_CERT_ERROR_NO_IBM_Z_SIGNING_KEY,
|
||||
PV_CERT_ERROR_NO_ISSUER_IBM_Z_FOUND,
|
||||
PV_CERT_ERROR_NO_PUBLIC_KEY,
|
||||
PV_CERT_ERROR_READ_CERTIFICATE,
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
#include "libpv/glib-helper.h"
|
||||
|
||||
#include <glib/gi18n-lib.h>
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
#include "libpv/openssl-compat.h"
|
||||
#include "libpv/macros.h"
|
||||
|
||||
@@ -42,11 +42,14 @@ enum PvCryptoMode {
|
||||
PV_DECRYPT,
|
||||
};
|
||||
|
||||
/** pv_get_openssl_error:
|
||||
/** pv_get_openssl_errors:
|
||||
*
|
||||
* Returns: (transfer full): String representing the error.
|
||||
* Returns the last OpenSSL error messages.
|
||||
* Caller is responsible to free the returned value.
|
||||
*
|
||||
* Returns: String representing the error.
|
||||
*/
|
||||
const char *pv_get_openssl_error(void);
|
||||
char *pv_get_openssl_errors(void);
|
||||
|
||||
/**
|
||||
* pv_BIO_reset:
|
||||
@@ -181,7 +184,6 @@ typedef enum {
|
||||
PV_CRYPTO_ERROR_KEYGENERATION,
|
||||
PV_CRYPTO_ERROR_RANDOMIZATION,
|
||||
PV_CRYPTO_ERROR_READ_FILE,
|
||||
PV_CRYPTO_ERROR_NO_IBM_Z_SIGNING_KEY,
|
||||
PV_CRYPTO_ERROR_NO_MATCH_TAG,
|
||||
} PvCryptoErrors;
|
||||
|
||||
|
||||
@@ -102,9 +102,12 @@ FILE *pv_file_open(const char *filename, const char *mode, GError **error);
|
||||
|
||||
/** pv_gbytes_memcpy:
|
||||
*
|
||||
* memcpy with size check.
|
||||
* 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);
|
||||
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 {
|
||||
|
||||
@@ -10,7 +10,12 @@
|
||||
#ifndef LIBPV_MACROS_H
|
||||
#define LIBPV_MACROS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define PV_NONNULL(...)
|
||||
#define DO_PRAGMA(x) _Pragma(#x)
|
||||
|
||||
/* Most significant bit */
|
||||
#define PV_MSB(idx) ((uint64_t)1 << (63 - (idx)))
|
||||
|
||||
#endif /* LIBPV_MACROS_H */
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "boot/s390.h"
|
||||
#include "libpv/crypto.h"
|
||||
#include "libpv/macros.h"
|
||||
|
||||
/* Magic number which is used to identify the file containing the PV
|
||||
* header
|
||||
@@ -22,20 +23,17 @@
|
||||
#define PV_MAGIC_NUMBER 0x49424d5365634578ULL
|
||||
#define PV_VERSION_1 0x00000100U
|
||||
|
||||
/* Internal helper macro */
|
||||
#define __PV_BIT(nr) (1ULL << (63 - (nr)))
|
||||
|
||||
/* Plaintext control flags */
|
||||
/* dumping of the configuration is allowed */
|
||||
#define PV_PCF_ALLOW_DUMPING __PV_BIT(34)
|
||||
#define PV_PCF_ALLOW_DUMPING PV_MSB(34)
|
||||
/* prevent Ultravisor decryption during unpack operation */
|
||||
#define PV_PCF_NO_DECRYPTION __PV_BIT(35)
|
||||
#define PV_PCF_NO_DECRYPTION PV_MSB(35)
|
||||
/* PCKMO encrypt-DEA/TDEA-key functions allowed */
|
||||
#define PV_PCF_PCKMO_DEA_TDEA __PV_BIT(56)
|
||||
#define PV_PCF_PCKMO_DEA_TDEA PV_MSB(56)
|
||||
/* PCKMO encrypt-AES-key functions allowed */
|
||||
#define PV_PCF_PCKMO_AES __PV_BIT(57)
|
||||
#define PV_PCF_PCKMO_AES PV_MSB(57)
|
||||
/* PCKMO encrypt-ECC-key functions allowed */
|
||||
#define PV_PCF_PCKM_ECC __PV_BIT(58)
|
||||
#define PV_PCF_PCKM_ECC PV_MSB(58)
|
||||
|
||||
/* maxima for the PV version 1 */
|
||||
#define PV_V1_IPIB_MAX_SIZE PAGE_SIZE
|
||||
|
||||
@@ -75,6 +75,7 @@ extern 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)
|
||||
|
||||
extern void nvme_fid_get(const char *device, char *fid);
|
||||
|
||||
@@ -18,15 +18,31 @@
|
||||
#include "lib/util_file.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 path[PATH_MAX], buf[FID_MAX_LEN];
|
||||
char nvme_dev[NVME_DEV_MAX_LEN];
|
||||
|
||||
snprintf(path, PATH_MAX, "/sys/block/%s/device/device/function_id",
|
||||
device);
|
||||
/*
|
||||
* An NVMe may present multiple namespaces and thus block devices, even
|
||||
* before partitioning, so we need the nvme<NUM> 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);
|
||||
|
||||
snprintf(path, PATH_MAX, "/sys/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);
|
||||
|
||||
|
||||
@@ -2,17 +2,6 @@ include ../common.mak
|
||||
|
||||
lib = libap.a
|
||||
|
||||
check-dep-lock:
|
||||
touch check-dep-lock
|
||||
ifneq (${HAVE_LOCKFILE},0)
|
||||
$(call check_dep, \
|
||||
"libap", \
|
||||
"lockfile.h", \
|
||||
"liblockfile-devel", \
|
||||
"HAVE_LOCKFILE=0")
|
||||
ALL_CPPFLAGS += -DHAVE_LOCKFILE
|
||||
endif
|
||||
|
||||
check-dep-json:
|
||||
touch check-dep-json
|
||||
ifneq (${HAVE_JSONC},0)
|
||||
@@ -29,7 +18,7 @@ objects = ap.o
|
||||
|
||||
$(lib): $(objects)
|
||||
|
||||
$(objects): check-dep-lock check-dep-json
|
||||
$(objects): check-dep-json
|
||||
|
||||
install: all
|
||||
|
||||
|
||||
45
libap/ap.c
45
libap/ap.c
@@ -22,13 +22,10 @@
|
||||
#include <json-c/json.h>
|
||||
#endif /* HAVE_JSONC */
|
||||
|
||||
#ifdef HAVE_LOCKFILE
|
||||
#include <lockfile.h>
|
||||
#endif /* HAVE_LOCKFILE */
|
||||
|
||||
#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"
|
||||
@@ -701,33 +698,28 @@ void ap_list_remove_all(struct util_list *list)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_LOCKFILE
|
||||
static int ap_lockfile_create(int flags)
|
||||
{
|
||||
return lockfile_create(AP_LOCKFILE, AP_LOCK_RETRIES, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire the ap config lock using this process PID (L_PID)
|
||||
* Acquire the ap config lock using this Process ID
|
||||
*
|
||||
* @retval 0 Lock acquired on behalf of this process
|
||||
*
|
||||
* @retval 0 Lock successfully acquired on behalf of L_PID
|
||||
* @retval != 0 Error, lock was not obtained
|
||||
*/
|
||||
int ap_get_lock(void)
|
||||
{
|
||||
return ap_lockfile_create(L_PID);
|
||||
return util_lockfile_lock(AP_LOCKFILE, AP_LOCK_RETRIES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire the ap config lock using the parent process PID (L_PPID) -- intended
|
||||
* for use by the mdevctl callout ap-check utility
|
||||
* Acquire the ap config lock using the Parent Process ID -- intended for use
|
||||
* by the mdevctl callout ap-check utility
|
||||
*
|
||||
* @retval 0 Lock successfully acquired on behalf of L_PPID
|
||||
* @retval 0 Lock acquired on behalf of parent process
|
||||
* @retval != 0 Error, lock was not obtained
|
||||
*/
|
||||
int ap_get_lock_callout(void)
|
||||
{
|
||||
return ap_lockfile_create(L_PPID);
|
||||
return util_lockfile_parent_lock(AP_LOCKFILE, AP_LOCK_RETRIES);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -738,23 +730,10 @@ int ap_get_lock_callout(void)
|
||||
*/
|
||||
int ap_release_lock(void)
|
||||
{
|
||||
return lockfile_remove(AP_LOCKFILE);
|
||||
}
|
||||
#else
|
||||
/* If no liblockfile, actions are performed without acquiring the file lock */
|
||||
int ap_get_lock(void)
|
||||
{
|
||||
return 0;
|
||||
return util_lockfile_release(AP_LOCKFILE);
|
||||
}
|
||||
|
||||
int ap_get_lock_callout(void)
|
||||
int ap_release_lock_callout(void)
|
||||
{
|
||||
return 0;
|
||||
return util_lockfile_parent_release(AP_LOCKFILE);
|
||||
}
|
||||
|
||||
int ap_release_lock(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* HAVE_LOCKFILE */
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/mount.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lib/dasd_base.h"
|
||||
|
||||
@@ -27,11 +29,13 @@
|
||||
|
||||
#define RUN_IOCTL(fd, req, argp) \
|
||||
do { \
|
||||
if (ioctl(fd, req, argp) != 0) { \
|
||||
int err = errno; \
|
||||
if (err != EBADF) \
|
||||
int rc = ioctl(fd, req, argp); \
|
||||
if (rc != 0) { \
|
||||
if (rc == -1) \
|
||||
rc = errno; \
|
||||
if (rc != EBADF) \
|
||||
dasd_close_device(fd); \
|
||||
return err; \
|
||||
return rc; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
@@ -321,3 +325,159 @@ int dasd_disk_release(const char *device)
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
16
libpv/cert.c
16
libpv/cert.c
@@ -290,7 +290,9 @@ static char *pv_X509_NAME_oneline(const X509_NAME *name)
|
||||
long len;
|
||||
|
||||
if (X509_NAME_print_ex(key_bio, name, 0, XN_FLAG_RFC2253) == -1) {
|
||||
g_warning(_("Cannot receive X509-NAME from CRL: %s"), pv_get_openssl_error());
|
||||
g_autofree char *openssl_err_msg = pv_get_openssl_errors();
|
||||
|
||||
g_warning(_("Cannot receive X509-NAME from CRL: %s"), openssl_err_msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -790,7 +792,7 @@ int pv_store_set_verify_param(X509_STORE *store, GError **error)
|
||||
return 0;
|
||||
|
||||
error:
|
||||
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_INTERNAL,
|
||||
_("X509 store initialization failed"));
|
||||
return -1;
|
||||
}
|
||||
@@ -1375,14 +1377,14 @@ int pv_check_chain_parameters(const STACK_OF_X509 *chain, GError **error)
|
||||
|
||||
ca_x509_subject = X509_get_subject_name(ca);
|
||||
if (!ca_x509_subject) {
|
||||
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_INTERNAL,
|
||||
_("subject of the root CA cannot be retrieved"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
ca_subject = pv_X509_NAME_oneline(ca_x509_subject);
|
||||
if (!ca_subject) {
|
||||
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_INTERNAL,
|
||||
_("subject name of the root CA cannot be retrieved"));
|
||||
return -1;
|
||||
}
|
||||
@@ -1476,11 +1478,11 @@ static STACK_OF_X509 *get_ibm_signing_certs(STACK_OF_X509 *certs, GError **error
|
||||
ibm_signing_certs = pv_remove_ibm_signing_certs(certs);
|
||||
ibm_signing_certs_count = sk_X509_num(ibm_signing_certs);
|
||||
if (ibm_signing_certs_count < 1) {
|
||||
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_IBM_Z_SIGNING_KEY,
|
||||
g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_NO_IBM_Z_SIGNING_KEY,
|
||||
_("Specify at least one IBM Z signing key"));
|
||||
return NULL;
|
||||
} else if (ibm_signing_certs_count > 1) {
|
||||
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_IBM_Z_SIGNING_KEY,
|
||||
g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_NO_IBM_Z_SIGNING_KEY,
|
||||
_("Specify only one IBM Z signing key"));
|
||||
return NULL;
|
||||
}
|
||||
@@ -1511,7 +1513,7 @@ static gboolean download_crls(X509_STORE *trusted, PvCertWithPathList *host_key_
|
||||
X509_CRL *crl = sk_X509_CRL_value(downloaded_ibm_signing_crls, i);
|
||||
|
||||
if (X509_STORE_add_crl(trusted, crl) != 1) {
|
||||
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_INTERNAL,
|
||||
_("failed to load CRL"));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -19,18 +19,18 @@
|
||||
#include "libpv/glib-helper.h"
|
||||
#include "libpv/hash.h"
|
||||
|
||||
const char *pv_get_openssl_error(void)
|
||||
char *pv_get_openssl_errors(void)
|
||||
{
|
||||
const char *ret;
|
||||
BIO *bio;
|
||||
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)
|
||||
ret = "Cannot receive OpenSSL error message.";
|
||||
if (len <= 0 || !buf)
|
||||
ret = g_strdup("Cannot receive OpenSSL error message.");
|
||||
else
|
||||
ret = g_strndup(buf, (size_t)len);
|
||||
BIO_free(bio);
|
||||
@@ -276,7 +276,7 @@ int64_t pv_gcm_decrypt(GBytes *cipher, GBytes *aad, GBytes *tag, const PvCipherP
|
||||
}
|
||||
|
||||
GBytes *pv_hkdf_extract_and_expand(size_t derived_key_len, GBytes *key, GBytes *salt, GBytes *info,
|
||||
const EVP_MD *md, G_GNUC_UNUSED GError **error)
|
||||
const EVP_MD *md, GError **error)
|
||||
{
|
||||
const unsigned char *salt_data, *key_data, *info_data;
|
||||
g_autoptr(EVP_PKEY_CTX) ctx = NULL;
|
||||
@@ -354,7 +354,6 @@ GBytes *pv_hkdf_extract_and_expand(size_t derived_key_len, GBytes *key, GBytes *
|
||||
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");
|
||||
printf("%s\n", pv_get_openssl_error());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -168,12 +168,14 @@ GBytes *pv_file_get_content_as_secure_bytes(const char *filename)
|
||||
return pv_sec_gbytes_new_take(g_steal_pointer(&data), data_size);
|
||||
}
|
||||
|
||||
void *pv_gbytes_memcpy(void *dst, size_t dst_size, GBytes *src)
|
||||
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);
|
||||
}
|
||||
|
||||
12
libpv/hash.c
12
libpv/hash.c
@@ -97,8 +97,10 @@ HMAC_CTX *pv_hmac_ctx_new(GBytes *key, const EVP_MD *md, GError **error)
|
||||
key_data = g_bytes_get_data(key, &key_size);
|
||||
|
||||
if (HMAC_Init_ex(ctx, key_data, (int)key_size, md, NULL) != 1) {
|
||||
g_autofree char *openssl_err_msg = pv_get_openssl_errors();
|
||||
|
||||
g_set_error(error, PV_HASH_ERROR, PV_HASH_ERROR_INTERNAL,
|
||||
"unable to create HMAC context: %s", pv_get_openssl_error());
|
||||
"unable to create HMAC context: %s", openssl_err_msg);
|
||||
return NULL;
|
||||
}
|
||||
return g_steal_pointer(&ctx);
|
||||
@@ -110,8 +112,10 @@ int pv_hmac_ctx_update_raw(HMAC_CTX *ctx, const void *buf, size_t size, GError *
|
||||
return 0;
|
||||
|
||||
if (HMAC_Update(ctx, buf, size) != 1) {
|
||||
g_autofree char *openssl_err_msg = pv_get_openssl_errors();
|
||||
|
||||
g_set_error(error, PV_HASH_ERROR, PV_HASH_ERROR_INTERNAL,
|
||||
"unable to add data to HMAC context: %s", pv_get_openssl_error());
|
||||
"unable to add data to HMAC context: %s", openssl_err_msg);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
@@ -139,8 +143,10 @@ GBytes *pv_hamc_ctx_finalize(HMAC_CTX *ctx, GError **error)
|
||||
hmac = g_malloc0((unsigned int)md_size);
|
||||
|
||||
if (HMAC_Final(ctx, hmac, &hmac_size) != 1) {
|
||||
g_autofree char *openssl_err_msg = pv_get_openssl_errors();
|
||||
|
||||
g_set_error(error, PV_HASH_ERROR, PV_HASH_ERROR_INTERNAL,
|
||||
"unable to calculate HMAC: %s", pv_get_openssl_error());
|
||||
"unable to calculate HMAC: %s", openssl_err_msg);
|
||||
return NULL;
|
||||
}
|
||||
return g_bytes_new_take(g_steal_pointer(&hmac), hmac_size);
|
||||
|
||||
@@ -107,13 +107,12 @@ const char *util_arch_machine_type_to_str(int type)
|
||||
*/
|
||||
unsigned long util_arch_hsa_maxsize(void)
|
||||
{
|
||||
unsigned long size = HSA_SIZE_32M;
|
||||
int type;
|
||||
|
||||
type = util_arch_machine_type();
|
||||
if (type != UTIL_ARCH_MACHINE_TYPE_UNKNOWN &&
|
||||
type >= UTIL_ARCH_MACHINE_TYPE_Z15)
|
||||
size = HSA_SIZE_512M;
|
||||
|
||||
return size;
|
||||
switch (util_arch_machine_type()) {
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z15:
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z15_T02:
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z16:
|
||||
return HSA_SIZE_512M;
|
||||
default:
|
||||
return HSA_SIZE_32M;
|
||||
}
|
||||
}
|
||||
|
||||
301
libutil/util_lockfile.c
Normal file
301
libutil/util_lockfile.c
Normal file
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
* 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 <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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 WAITINC 5 /* Additional time to wait each retry */
|
||||
#define MAXWAIT 60 /* 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] 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)
|
||||
{
|
||||
int fd, plen, len, rc = 0, snooze = 0;
|
||||
unsigned int tries = retries + 1;
|
||||
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;
|
||||
sleep(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());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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());
|
||||
}
|
||||
232
libutil/util_lockfile_example.c
Normal file
232
libutil/util_lockfile_example.c
Normal file
@@ -0,0 +1,232 @@
|
||||
/**
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#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]
|
||||
@@ -1419,7 +1419,6 @@ int lzds_dasd_read_rawvtoc(struct dasd *dasd, struct raw_vtoc *rawvtoc)
|
||||
|
||||
volume_label_t *vlabel = NULL;
|
||||
char *trackdata = NULL;
|
||||
char vol1[] = {0xe5, 0xd6, 0xd3, 0xf1, 0x00}; /* "VOL1" in EBCDIC */
|
||||
|
||||
errorlog_clear(dasd->log);
|
||||
|
||||
@@ -1431,8 +1430,7 @@ int lzds_dasd_read_rawvtoc(struct dasd *dasd, struct raw_vtoc *rawvtoc)
|
||||
goto cleanup;
|
||||
}
|
||||
/* verify that we have a proper VOL1 label */
|
||||
if (strncmp(vlabel->volkey, vol1, 4) ||
|
||||
strncmp(vlabel->vollbl, vol1, 4)) {
|
||||
if (!is_vol1(vlabel->volkey) || !is_vol1(vlabel->vollbl)) {
|
||||
rc = EINVAL;
|
||||
errorlog_add_message(
|
||||
&dasd->log, NULL, rc,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
include ../common.mak
|
||||
|
||||
ALL_CFLAGS += -Wno-address-of-packed-member
|
||||
|
||||
all: mon_fsstatd mon_procd
|
||||
|
||||
mon_fsstatd: mon_fsstatd.o
|
||||
|
||||
@@ -24,7 +24,7 @@ Prints usage information, then exits.
|
||||
.B
|
||||
\fB-k\fP, \fB--host-key-document\fP=\fBFILE\fP
|
||||
Specify one or more host key documents. At least one is required.
|
||||
Specify this option multiple times to enable the image to run on more than one host.
|
||||
Specify this option multiple times to create an attestation request control block that is usable on multiple hosts.
|
||||
.TP
|
||||
.B
|
||||
\fB-C\fP, \fB--cert\fP=\fBFILE\fP
|
||||
|
||||
@@ -171,9 +171,9 @@ int arcb_v1_add_key_slot(arcb_v1_t *arcb, EVP_PKEY *evp_host, GError **error)
|
||||
g_assert(g_bytes_get_size(phkh) == sizeof(key_slot->phkh));
|
||||
|
||||
key_slot = g_malloc0(sizeof(*key_slot));
|
||||
pv_gbytes_memcpy(key_slot->warpk, sizeof(key_slot->warpk), warpk);
|
||||
pv_gbytes_memcpy(key_slot->kst, sizeof(key_slot->warpk), tag);
|
||||
pv_gbytes_memcpy(key_slot->phkh, sizeof(key_slot->warpk), phkh);
|
||||
pv_gbytes_memcpy(key_slot->warpk, sizeof(key_slot->warpk), warpk, NULL);
|
||||
pv_gbytes_memcpy(key_slot->kst, sizeof(key_slot->warpk), tag, NULL);
|
||||
pv_gbytes_memcpy(key_slot->phkh, sizeof(key_slot->warpk), phkh, NULL);
|
||||
|
||||
arcb->host_key_slots = g_slist_prepend(arcb->host_key_slots, g_steal_pointer(&key_slot));
|
||||
return 0;
|
||||
@@ -246,7 +246,7 @@ GBytes *arcb_v1_serialize(const arcb_v1_t *arcb, GError **error)
|
||||
/* copy plain data to contiguous memory */
|
||||
hdr.arl = GUINT32_TO_BE((uint32_t)att_req_len);
|
||||
|
||||
pv_gbytes_memcpy(hdr.iv, ARCB_V1_IV_SIZE, arcb->iv);
|
||||
pv_gbytes_memcpy(hdr.iv, ARCB_V1_IV_SIZE, arcb->iv, NULL);
|
||||
hdr.nks = (uint8_t)nks;
|
||||
hdr.sea = GUINT32_TO_BE((uint32_t)sea);
|
||||
ecdh_cpk = pv_evp_pkey_to_ecdh_pub_key(arcb->evp_cust_pub_key, error);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
#include "libpv/crypto.h"
|
||||
#include "libpv/macros.h"
|
||||
|
||||
#include "types.h"
|
||||
|
||||
@@ -28,18 +29,17 @@
|
||||
#define ARCB_V1_IV_SIZE 12
|
||||
#define ARCB_V1_PHKH_SIZE 32
|
||||
|
||||
#define BIT(bit) ((uint64_t)1 << (63 - (bit)))
|
||||
/* Optional nonce in ARCB */
|
||||
#define ARCB_V1_PAF_NONCE BIT(1)
|
||||
#define ARCB_V1_PAF_NONCE PV_MSB(1)
|
||||
/* Public host key hash used to unseal SE header added to additional data to be measured */
|
||||
#define ARCB_V1_PAF_AAD_PHKH_HEADER BIT(2)
|
||||
#define ARCB_V1_PAF_AAD_PHKH_HEADER PV_MSB(2)
|
||||
/* Public host key hash used to unseal this attestation added to additional data to be measured */
|
||||
#define ARCB_V1_PAF_AAD_PHKH_ATTEST BIT(3)
|
||||
#define ARCB_V1_PAF_AAD_PHKH_ATTEST PV_MSB(3)
|
||||
/* Temporary backup-host-key use allowed */
|
||||
#define ARCB_V1_PAF_TMP_BACKUP_ALLOWED BIT(62)
|
||||
#define ARCB_V1_PAF_TMP_BACKUP_ALLOWED PV_MSB(62)
|
||||
|
||||
/* Global not-host-specific key allowed */
|
||||
#define ARCB_V1_PAF_GLOBAL_NHS_KEY_ALLOWED BIT(63)
|
||||
#define ARCB_V1_PAF_GLOBAL_NHS_KEY_ALLOWED PV_MSB(63)
|
||||
|
||||
#define ARCB_V1_PAF_ALL \
|
||||
(ARCB_V1_PAF_NONCE | ARCB_V1_PAF_AAD_PHKH_HEADER | ARCB_V1_PAF_AAD_PHKH_ATTEST | \
|
||||
|
||||
@@ -130,7 +130,7 @@ void att_add_uid(att_meas_ctx_t *meas_ctx, GBytes *config_uid)
|
||||
pv_wrapped_g_assert(config_uid);
|
||||
|
||||
g_assert(g_bytes_get_size(config_uid) == ATT_CONFIG_UID_SIZE);
|
||||
pv_gbytes_memcpy(meas_ctx->config_uid, ATT_CONFIG_UID_SIZE, config_uid);
|
||||
pv_gbytes_memcpy(meas_ctx->config_uid, ATT_CONFIG_UID_SIZE, config_uid, NULL);
|
||||
}
|
||||
|
||||
gboolean att_verify_measurement(const GBytes *calculated_measurement,
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
/* Must be included before any other header */
|
||||
#include "config.h"
|
||||
|
||||
#include <glib/gi18n-lib.h>
|
||||
#include <glib/gi18n.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "libpv/glib-helper.h"
|
||||
|
||||
@@ -191,7 +191,7 @@ exchange_format_ctx_t *exchange_ctx_from_file(const char *filename, GError **err
|
||||
}
|
||||
ctx->req_meas_size = GUINT32_FROM_BE(hdr_v1->measurement.size);
|
||||
ctx->req_add_size = GUINT32_FROM_BE(hdr_v1->additional_data.size);
|
||||
ctx->version = GUINT32_TO_BE(hdr->version);
|
||||
ctx->version = GUINT32_FROM_BE(hdr->version);
|
||||
|
||||
return g_steal_pointer(&ctx);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
/* Must be included before any other header */
|
||||
#include "config.h"
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
#ifdef PVATTEST_COMPILE_PERFORM
|
||||
#include <stdio.h>
|
||||
@@ -42,26 +43,29 @@ uvio_attest_t *build_attestation_v1_ioctl(GBytes *serialized_arcb, GBytes *user_
|
||||
uvio_attest = g_malloc0(sizeof(*uvio_attest));
|
||||
uvio_attest->arcb_addr = PTR_TO_U64(g_steal_pointer(&arcb));
|
||||
g_assert_cmpuint(arcb_size, <, UINT32_MAX);
|
||||
uvio_attest->arcb_len = GUINT32_TO_BE((uint32_t)arcb_size);
|
||||
uvio_attest->arcb_len = (uint32_t)arcb_size;
|
||||
/* transferred the local ownership of the arcb from this function to uvio_attest; nullify pointer */
|
||||
g_steal_pointer(&serialized_arcb);
|
||||
|
||||
if (user_data) {
|
||||
if (g_bytes_get_size(user_data) > sizeof(uvio_attest->user_data)) {
|
||||
size_t copied_data_size;
|
||||
|
||||
if (pv_gbytes_memcpy(uvio_attest->user_data, sizeof(uvio_attest->user_data),
|
||||
user_data, &copied_data_size) == NULL) {
|
||||
g_set_error(error, ATT_ERROR, ATT_ERR_INVALID_USER_DATA,
|
||||
_("User data larger than %li bytes"),
|
||||
sizeof(uvio_attest->user_data));
|
||||
_("User data %li bytes is larger than %li bytes"),
|
||||
g_bytes_get_size(user_data), sizeof(uvio_attest->user_data));
|
||||
return NULL;
|
||||
}
|
||||
uvio_attest->user_data_len = GUINT16_TO_BE((uint16_t)g_bytes_get_size(user_data));
|
||||
pv_gbytes_memcpy(uvio_attest->user_data, uvio_attest->user_data_len, user_data);
|
||||
STATIC_ASSERT(sizeof(uvio_attest->user_data) <= UINT16_MAX);
|
||||
uvio_attest->user_data_len = (uint16_t)copied_data_size;
|
||||
}
|
||||
|
||||
uvio_attest->meas_len = GUINT32_TO_BE(measurement_size);
|
||||
uvio_attest->meas_addr = PTR_TO_U64(g_malloc0(uvio_attest->meas_len));
|
||||
uvio_attest->meas_addr = PTR_TO_U64(g_malloc0(measurement_size));
|
||||
uvio_attest->meas_len = measurement_size;
|
||||
|
||||
uvio_attest->add_data_len = GUINT32_TO_BE(add_data_size);
|
||||
uvio_attest->add_data_addr = PTR_TO_U64(g_malloc0(uvio_attest->add_data_len));
|
||||
uvio_attest->add_data_addr = PTR_TO_U64(g_malloc0(add_data_size));
|
||||
uvio_attest->add_data_len = add_data_size;
|
||||
|
||||
return g_steal_pointer(&uvio_attest);
|
||||
}
|
||||
@@ -83,7 +87,7 @@ GBytes *uvio_get_measurement(const uvio_attest_t *attest)
|
||||
|
||||
if (attest->meas_addr == (__u64)0)
|
||||
return NULL;
|
||||
return g_bytes_new(U64_TO_PTR(attest->meas_addr), GUINT32_FROM_BE(attest->meas_len));
|
||||
return g_bytes_new(U64_TO_PTR(attest->meas_addr), attest->meas_len);
|
||||
}
|
||||
|
||||
GBytes *uvio_get_additional_data(const uvio_attest_t *attest)
|
||||
@@ -92,8 +96,7 @@ GBytes *uvio_get_additional_data(const uvio_attest_t *attest)
|
||||
|
||||
if (attest->add_data_addr == (__u64)0)
|
||||
return NULL;
|
||||
return g_bytes_new(U64_TO_PTR(attest->add_data_addr),
|
||||
GUINT32_FROM_BE(attest->add_data_len));
|
||||
return g_bytes_new(U64_TO_PTR(attest->add_data_addr), attest->add_data_len);
|
||||
}
|
||||
|
||||
GBytes *uvio_get_config_uid(const uvio_attest_t *attest)
|
||||
@@ -127,7 +130,7 @@ uint16_t uvio_ioctl(const int uv_fd, const unsigned int cmd, const uint32_t flag
|
||||
g_set_error(error, UVIO_ERROR, UVIO_ERR_UV_NOT_OK,
|
||||
_("Ultravisor call returned '%#x' (%s)"), uv_ioctl->uv_rc,
|
||||
uvio_uv_rc_to_str(uv_ioctl->uv_rc));
|
||||
return GUINT16_FROM_BE(uv_ioctl->uv_rc);
|
||||
return uv_ioctl->uv_rc;
|
||||
}
|
||||
|
||||
uint16_t uvio_ioctl_attest(const int uv_fd, uvio_attest_t *attest, GError **error)
|
||||
|
||||
@@ -14,6 +14,8 @@ set -o pipefail
|
||||
set -o nounset
|
||||
set -e
|
||||
|
||||
XDUMP='od -A x -t x2z -v'
|
||||
|
||||
usage() {
|
||||
cat <<-EOF
|
||||
Usage: $(basename "$0") FILE
|
||||
@@ -33,16 +35,16 @@ function check_is_pvattest_binary() {
|
||||
exit 1
|
||||
fi
|
||||
|
||||
xxd -l 16 "${input}" | grep -q pvattest ||
|
||||
${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=$(xxd -s 12 -l 4 "${input}" | awk 'NR==1 {print "0x" $2 $3}')
|
||||
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=$(xxd -s 8 -l 4 "$input")
|
||||
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 ; }
|
||||
@@ -55,24 +57,28 @@ function print_entry() {
|
||||
local size
|
||||
local off
|
||||
|
||||
size=$(xxd -s $((file_off)) -l 4 "${input}" | awk 'NR==1 {print "0x" $2 $3}')
|
||||
off=$(xxd -s $((file_off + 4)) -l 4 "${input}" | awk 'NR==1 {print "0x" $2 $3}')
|
||||
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}:"
|
||||
xxd -s $((off)) -l $((size)) -p "${input}"
|
||||
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; }
|
||||
command -v "$cmd" >/dev/null 2>&1 || \
|
||||
{ echo >&2 "ERROR: $cmd required but not installed."; exit 1; }
|
||||
}
|
||||
|
||||
require_command xxd
|
||||
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
|
||||
|
||||
@@ -14,7 +14,9 @@ set -o pipefail
|
||||
set -o nounset
|
||||
set -e
|
||||
|
||||
def_output="sehdr.bin"
|
||||
XDUMP='od -A x -t x2z -v'
|
||||
|
||||
def_output='sehdr.bin'
|
||||
def_skip=0x14
|
||||
def_len=0x4
|
||||
|
||||
@@ -37,11 +39,22 @@ function check_file() {
|
||||
function check_hdr_ver() {
|
||||
local hdr_start="$1"
|
||||
local input="$2"
|
||||
xxd -s $((hdr_start + 8)) -l 4 "$input" | grep -q "000 0100" ||
|
||||
${XDUMP} --skip-bytes $((hdr_start + 8)) --read-bytes 4 -- "$input" 2>/dev/null | grep -q "000 0100" ||
|
||||
{ echo -n "WARNING: unknown hdr version " &&
|
||||
xxd -s $((hdr_start + 8)) -l 4 "$input" | awk '{print "0x" $2 $3}'; }
|
||||
${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}
|
||||
@@ -77,13 +90,15 @@ if [ $# -eq 0 ]; then
|
||||
fi
|
||||
|
||||
check_file "$input"
|
||||
hdr_start=$(xxd -s $((skip)) -l $((len)) "${input}" | grep IBMSecEx || { echo ERROR: "${input} does not contain an SE header." >&2 && exit 1; })
|
||||
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=$(xxd -s $((hdr_start + 12)) -l 4 "${input}" | awk 'NR==1 {print "0x" $2 $3}')
|
||||
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)"
|
||||
|
||||
@@ -17,7 +17,8 @@ export LC_ALL
|
||||
readonly SCRIPTNAME="${0##*/}" # general name of this script
|
||||
#
|
||||
readonly DATETIME="$(date +%Y-%m-%d-%H-%M-%S 2>/dev/null)"
|
||||
readonly DOCKER=$(if type -t docker >/dev/null; then echo "YES"; else echo "NO"; fi)
|
||||
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 -i 2>/dev/null)"
|
||||
# retrieve and split kernel version
|
||||
readonly KERNEL_BASE="$(uname -r 2>/dev/null)"
|
||||
@@ -25,7 +26,9 @@ 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 KVM=$(if type -t virsh >/dev/null; then echo "YES"; else echo "NO"; fi)
|
||||
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
|
||||
@@ -43,14 +46,20 @@ 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')
|
||||
sed 's/.*:[[:space:]]*\([[:graph:]]*\).*/\1/g')
|
||||
else
|
||||
RUNTIME_ENVIRONMENT="LPAR"
|
||||
fi
|
||||
readonly SYSTEMHOSTNAME="$(hostname -s 2>/dev/null)" # hostname of system being analysed
|
||||
readonly TERMINAL="$(tty 2>/dev/null)"
|
||||
# The processor version for the first processor and resulting vitrtualization RUNTIME
|
||||
readonly TOS=15 # timeout seconds for command execution
|
||||
# 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)
|
||||
@@ -79,7 +88,7 @@ This script collects runtime, configuration and trace information on
|
||||
a Linux on IBM Z installation for debugging purposes.
|
||||
|
||||
It also traces information about z/VM if the Linux runs under z/VM.
|
||||
KVM or DOCKER data ist collected on a host serving this.
|
||||
Virtualization platform data is collected on a host serving this.
|
||||
|
||||
Default location for data collection and final tar file is "/tmp/".
|
||||
The collected information is written to a TAR archive named
|
||||
@@ -96,7 +105,7 @@ Options:
|
||||
stores the temporary data and the final archive.
|
||||
-h|--help print this help
|
||||
-v|--version print version information
|
||||
-c|--check online quick check (no data collection)
|
||||
-c|--check online base system information (no data collection)
|
||||
|
||||
Please report bugs to: linux390@de.ibm.com
|
||||
|
||||
@@ -150,7 +159,7 @@ $(cat /proc/sysinfo | grep 'Name')
|
||||
Kernel version = ${KERNEL_INFO}
|
||||
OS version / distro = ${OS_NAME}
|
||||
KVM host = ${KVM}
|
||||
DOCKER host = ${DOCKER}
|
||||
container host Kubernetes: ${KUBERNETES} - docker: ${DOCKER}
|
||||
|
||||
Current user = $(whoami) (must be root for data collection)
|
||||
Date and time = $(date)
|
||||
@@ -162,6 +171,9 @@ 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 !
|
||||
|
||||
@@ -213,7 +225,8 @@ 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 \"${SCRIPTNAME}\"!"
|
||||
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
|
||||
|
||||
@@ -249,6 +262,7 @@ readonly OUTPUT_FILE_ISW="${WORKPATH}installed_sw.out"
|
||||
readonly OUTPUT_FILE_TC="${WORKPATH}network.out"
|
||||
readonly OUTPUT_FILE_VMCMD="${WORKPATH}zvm_runtime.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"
|
||||
|
||||
@@ -268,7 +282,7 @@ ALL_STEPS="\
|
||||
collect_bridge\
|
||||
collect_ovs\
|
||||
collect_kvm\
|
||||
collect_docker\
|
||||
collect_container\
|
||||
collect_nvme\
|
||||
collect_logfiles\
|
||||
post_processing\
|
||||
@@ -329,14 +343,18 @@ 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/cron*\
|
||||
/var/log/dmesg*\
|
||||
/var/log/dnf.*\
|
||||
/var/log/dracut.log*\
|
||||
/var/log/IBMtape.trace\
|
||||
/var/log/IBMtape.errorlog\
|
||||
@@ -346,9 +364,12 @@ LOGFILES="\
|
||||
/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*\
|
||||
"
|
||||
|
||||
########################################
|
||||
@@ -368,6 +389,7 @@ CONFIGFILES="\
|
||||
/etc/crypttab\
|
||||
/etc/default\
|
||||
/etc/depmod.d\
|
||||
/etc/dnf/dnf.conf\
|
||||
/etc/docker\
|
||||
/etc/dracut.conf.d\
|
||||
/etc/exports\
|
||||
@@ -468,6 +490,7 @@ CMDS="${CMDS}\
|
||||
: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'\
|
||||
@@ -557,6 +580,18 @@ DOCKER_CMDS="docker version\
|
||||
:systemctl status docker.service\
|
||||
"
|
||||
|
||||
########################################
|
||||
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\
|
||||
@@ -676,18 +711,18 @@ collect_vmcmdsout() {
|
||||
local vm_userid
|
||||
local module_loaded=1
|
||||
local ifs_orig="${IFS}"
|
||||
local cp_buffer_size=2
|
||||
local rc_buffer_size=2
|
||||
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 -t vmcp >/dev/null; then
|
||||
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 -t hcp >/dev/null; then
|
||||
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
|
||||
@@ -703,11 +738,14 @@ collect_vmcmdsout() {
|
||||
IFS=:
|
||||
for vm_command in ${vm_cmds}; do
|
||||
IFS="${ifs_orig}"
|
||||
while test ${rc_buffer_size} -eq 2 && test ${cp_buffer_size} -lt 1024; do
|
||||
# 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_size=$?
|
||||
rc_buffer_check=$?
|
||||
done
|
||||
call_run_command "${cp_command} -b ${cp_buffer_size}k ${vm_command}" \
|
||||
"${OUTPUT_FILE_VMCMD}"
|
||||
@@ -781,7 +819,7 @@ collect_sysfs() {
|
||||
# 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 type -t dump2tar >/dev/null; then
|
||||
if test "x${DUMP2TAR_OK}" = "xYES"; then
|
||||
dump2tar /sys -z -o "${OUTPUT_FILE_SYSFS}.tgz" \
|
||||
-x '*/tracing/trace_pipe*' \
|
||||
-x '*/page_idle/bitmap*' \
|
||||
@@ -859,7 +897,7 @@ collect_osaoat() {
|
||||
|
||||
network_devices=$(lsqeth 2>/dev/null | grep "Device name" \
|
||||
| sed 's/D.*:[[:space:]]*\([^[:space:]]*\)[[:space:]]\+/\1/g')
|
||||
if type -t qethqoat >/dev/null; then
|
||||
if type qethqoat >/dev/null; then
|
||||
if test -n "${network_devices}"; then
|
||||
pr_collect_output "osa oat"
|
||||
for network_device in ${network_devices}; do
|
||||
@@ -882,7 +920,7 @@ collect_ethtool() {
|
||||
local network_device
|
||||
|
||||
network_devices=$(ls /sys/class/net 2>/dev/null)
|
||||
if type -t ethtool >/dev/null; then
|
||||
if type ethtool >/dev/null; then
|
||||
if test -n "${network_devices}"; then
|
||||
pr_collect_output "ethtool"
|
||||
for network_device in ${network_devices}; do
|
||||
@@ -923,7 +961,7 @@ collect_tc() {
|
||||
local network_device
|
||||
|
||||
network_devices=$(ls /sys/class/net 2>/dev/null)
|
||||
if type -t tc >/dev/null; then
|
||||
if type tc >/dev/null; then
|
||||
if test -n "${network_devices}"; then
|
||||
pr_collect_output "Trafic Control"
|
||||
for network_device in ${network_devices}; do
|
||||
@@ -944,7 +982,7 @@ collect_bridge() {
|
||||
local network_device
|
||||
|
||||
network_devices=$(ls /sys/class/net 2>/dev/null)
|
||||
if type -t bridge >/dev/null; then
|
||||
if type bridge >/dev/null; then
|
||||
if test -n "${network_devices}"; then
|
||||
pr_collect_output "bridge"
|
||||
for network_device in ${network_devices}; do
|
||||
@@ -976,7 +1014,7 @@ collect_ovs() {
|
||||
:ovs-vsctl -t 5 show\
|
||||
:ovsdb-client dump\
|
||||
"
|
||||
if type -t ovs-vsctl >/dev/null; then
|
||||
if type ovs-vsctl >/dev/null; then
|
||||
pr_collect_output "OpenVSwitch"
|
||||
IFS=:
|
||||
for ovscmd in ${ovscmds}; do
|
||||
@@ -1001,14 +1039,21 @@ collect_ovs() {
|
||||
}
|
||||
|
||||
########################################
|
||||
collect_docker() {
|
||||
collect_container() {
|
||||
local container_list
|
||||
local network_list
|
||||
local item
|
||||
|
||||
# check if docker command exists
|
||||
# check if container environment exists
|
||||
if test "x${DOCKER}" = "xYES" || test "x${KUBERNETES}" = "xYES"; then
|
||||
pr_collect_output "container host"
|
||||
else
|
||||
pr_skip "container host: not found"
|
||||
fi
|
||||
|
||||
# for docker command exists
|
||||
if [ "x${DOCKER}" = "xYES" ]; then
|
||||
pr_collect_output "docker"
|
||||
pr_syslog_stdout " docker ..."
|
||||
container_list=$(docker ps -qa)
|
||||
network_list=$(docker network ls -q)
|
||||
ifs_orig="${IFS}"
|
||||
@@ -1030,8 +1075,25 @@ collect_docker() {
|
||||
"${OUTPUT_FILE_DOCKER}"
|
||||
done
|
||||
fi
|
||||
else
|
||||
pr_skip "docker: not available"
|
||||
fi
|
||||
|
||||
# for kubectl command exists
|
||||
if [ "x${KUBERNETES}" = "xYES" ]; then
|
||||
pr_syslog_stdout " Kubernetes ..."
|
||||
container_list=$(kubectl top pod | grep -v "MEMORY(bytes)" | cut -d' ' -f1)
|
||||
|
||||
ifs_orig="${IFS}"
|
||||
IFS=:
|
||||
for item in ${KUBERNETES_CMDS}; do
|
||||
IFS=${ifs_orig} call_run_command "${item}" "${OUTPUT_FILE_KUBERNETES}.out"
|
||||
done
|
||||
IFS="${ifs_orig}"
|
||||
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
|
||||
}
|
||||
|
||||
@@ -1039,7 +1101,7 @@ collect_docker() {
|
||||
collect_nvme() {
|
||||
local device
|
||||
|
||||
if type -t nvme >/dev/null; then
|
||||
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
|
||||
@@ -1080,7 +1142,7 @@ collect_kvm() {
|
||||
call_run_command "virsh domstats ${domain}" "${OUTPUT_FILE_KVM}"
|
||||
done
|
||||
else
|
||||
echo "no KVM doamins found" | tee -a ${OUTPUT_FILE_KVM}
|
||||
echo "no KVM domains found" | tee -a ${OUTPUT_FILE_KVM}
|
||||
fi
|
||||
else
|
||||
pr_skip "KVM: no virsh command"
|
||||
@@ -1130,6 +1192,10 @@ post_processing() {
|
||||
rm -f "${file_name}"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "log work directory state after post processing:"
|
||||
echo "du summary: $(du -ks ${WORKPATH})"
|
||||
df -k ${WORKPATH}
|
||||
}
|
||||
|
||||
########################################
|
||||
@@ -1139,23 +1205,23 @@ call_run_command() {
|
||||
local rc=0
|
||||
local cmd="${1}"
|
||||
local logfile="${2}"
|
||||
# extract the raw_command and set cmd_type
|
||||
local raw_cmd=$(echo "${cmd}" | sed -ne 's/^\([^[:space:]]*\).*$/\1/p')
|
||||
local cmd_type=$(type -t ${raw_cmd})
|
||||
# timeout_ok - like a boolean - is not empty if command exists
|
||||
local timeout_ok=$(type -t timeout)
|
||||
# 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}" = "Xbuiltin" ]; then
|
||||
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}" != "X" ]; then
|
||||
if [ "X${timeout_ok}" = "Xfile" ]; then
|
||||
eval timeout -k ${TOS} ${TOS} "${cmd}" >> ${logfile} 2>&1
|
||||
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
|
||||
@@ -1347,6 +1413,9 @@ 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
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.TH DBGINFO.SH 8 "May 2022" "s390-tools"
|
||||
.TH DBGINFO.SH 8 "Sep 2022" "s390-tools"
|
||||
|
||||
.SH NAME
|
||||
dbginfo.sh \- collect runtime, configuration and trace information
|
||||
@@ -8,13 +8,13 @@ for debugging Linux on IBM Z
|
||||
.br
|
||||
\fBdbginfo.sh\fP [OPTIONS]
|
||||
.br
|
||||
\fBdbginfo.sh\fP {\-h|\-v}
|
||||
\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.
|
||||
KVM or DOCKER data ist collected on a host serving this.
|
||||
Virtualization platform data is collected on a host serving this.
|
||||
|
||||
The debug information is written to a file
|
||||
/<DIRECTORY>/DBGINFO\-<date>\-<time>\-<hostname>\-<processorid>.tgz
|
||||
@@ -59,7 +59,7 @@ Sample invocation:
|
||||
.br
|
||||
dbginfo.sh: Debug information script version %S390_TOOLS_VERSION%
|
||||
.br
|
||||
Copyright IBM Corp. 2002, 2021
|
||||
Copyright IBM Corp. 2002, 2022
|
||||
.PP
|
||||
Hardware platform = s390x
|
||||
.br
|
||||
@@ -97,8 +97,10 @@ Date and time of info = 2021-08-26-15-27-58
|
||||
.PP
|
||||
13 of 18: Skip KVM: no virsh command
|
||||
.PP
|
||||
14 of 18: Collecting docker output
|
||||
14 of 18: Collecting container host output
|
||||
.PP
|
||||
Kubernetes ...
|
||||
.br
|
||||
15 of 18: Collecting NVME storage output
|
||||
.PP
|
||||
16 of 18: Collecting log files
|
||||
|
||||
@@ -31,6 +31,7 @@ int disk_query_reserve_status(char* device);
|
||||
int disk_profile (char* device, char* prof_item);
|
||||
int disk_reset_prof(char *device);
|
||||
int disk_reset_chpid(char *device, char *chpid);
|
||||
int disk_copy_swap(char *device, char *copy_pair);
|
||||
|
||||
#endif /* not DISK_H */
|
||||
|
||||
|
||||
@@ -174,6 +174,14 @@ Reset all channel paths of the selected device. The channel paths
|
||||
might be suspended due to high IFCC error rates or a High Performance
|
||||
FICON failure. Use this option to resume considering all defined
|
||||
channel paths for I/O.
|
||||
.TP
|
||||
.BR "\-s" " or " "\-\-copy\-pair\-swap <copy_pair>"
|
||||
Swap the copy roles of the specified copy pair. The <copy_pair> has to
|
||||
be a comma separated pair of \fBPRIMARY,SECONDARY\fR where
|
||||
\fBPRIMARY\fR is the old primary device that will become a secondary
|
||||
automatically. The old \fBSECONDARY\fR device will become the new
|
||||
primary device. Both devices have to be online for this operation to
|
||||
succeed.
|
||||
.\"
|
||||
.\".TP
|
||||
.\".BR "\-o" " or " "\-\-online"
|
||||
@@ -197,6 +205,12 @@ channel paths for I/O.
|
||||
.br
|
||||
|
||||
tunedasd -p 45 /dev/dasdc
|
||||
|
||||
.br
|
||||
4. Scenario: Swap copy pair 0.0.9700 and 0.0.9740
|
||||
.br
|
||||
|
||||
tunedasd -s 0.0.9700,0.0.9740 /dev/dasdc
|
||||
.br
|
||||
.SH "SEE ALSO"
|
||||
.BR dasdview (8),
|
||||
|
||||
@@ -19,98 +19,13 @@
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lib/dasd_base.h"
|
||||
#include "lib/dasd_sys.h"
|
||||
#include "lib/util_libc.h"
|
||||
|
||||
#include "disk.h"
|
||||
#include "tunedasd.h"
|
||||
|
||||
#define BUS_ID_SIZE 30
|
||||
|
||||
/*
|
||||
* DASD DEFINITIONS (copied from dasd.h)
|
||||
*/
|
||||
|
||||
#define DASD_IOCTL_LETTER 'D'
|
||||
|
||||
/*
|
||||
* 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));
|
||||
|
||||
|
||||
/*
|
||||
* DASD-IOCTLs (copied from dasd.h)
|
||||
*/
|
||||
/* Issue a reserve/release command, rsp. */
|
||||
#define BIODASDRSRV _IO (DASD_IOCTL_LETTER,2) /* reserve */
|
||||
#define BIODASDRLSE _IO (DASD_IOCTL_LETTER,3) /* release */
|
||||
#define BIODASDSLCK _IO (DASD_IOCTL_LETTER,4) /* steal lock */
|
||||
/* 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 Attributes (cache operations) */
|
||||
#define BIODASDGATTR _IOR(DASD_IOCTL_LETTER,5,attrib_data_t)
|
||||
|
||||
/* Set Attributes (cache operations) */
|
||||
#define BIODASDSATTR _IOW (DASD_IOCTL_LETTER,2,attrib_data_t)
|
||||
|
||||
/* Get Sense Path Group ID (SNID) data */
|
||||
#define BIODASDSNID _IOWR(DASD_IOCTL_LETTER, 1, struct dasd_snid_ioctl_data)
|
||||
|
||||
|
||||
/* id definition for profile items */
|
||||
enum prof_id {
|
||||
prof_reqs = 0,
|
||||
@@ -241,48 +156,33 @@ check_prof_item (char* prof_item)
|
||||
* 'cache' is the caching mode (see ESS docu for more info) and 'no_cyl'
|
||||
* the number of cylinders to be cached.
|
||||
*/
|
||||
int
|
||||
disk_get_cache (char* device)
|
||||
int disk_get_cache(char *device)
|
||||
{
|
||||
int fd;
|
||||
attrib_data_t attrib_data;
|
||||
|
||||
/* Open device file */
|
||||
fd = open (device, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
error_print ("<%s> - %s", device, strerror (errno));
|
||||
return -1;
|
||||
}
|
||||
int rc;
|
||||
|
||||
/* Get the given caching attributes */
|
||||
if (ioctl (fd, BIODASDGATTR, &attrib_data)) {
|
||||
error_print ("Could not get cache attributes for device <%s>",
|
||||
device);
|
||||
close (fd);
|
||||
return -1;
|
||||
}
|
||||
rc = dasd_get_cache(device, &attrib_data);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
printf ("%s (%i cyl)\n",
|
||||
get_cache_name(attrib_data.operation),
|
||||
attrib_data.nr_cyl);
|
||||
|
||||
close (fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Set the caching algorithm used for the channel programs of this device.
|
||||
* 'cache' is the caching mode (see ESS docu for more info) and 'no_cyl'
|
||||
* the number of cylinders to be cached.
|
||||
*/
|
||||
int
|
||||
disk_set_cache (char* device, char* cache, char* no_cyl)
|
||||
int disk_set_cache(char *device, char *cache, char *no_cyl)
|
||||
{
|
||||
int fd;
|
||||
attrib_data_t attrib_data;
|
||||
|
||||
/* get caching mode and # cylinders */
|
||||
int rc;
|
||||
|
||||
/* get caching mode and # cylinders */
|
||||
attrib_data.operation = check_cache (cache);
|
||||
attrib_data.nr_cyl = check_no_cyl (no_cyl);
|
||||
|
||||
@@ -292,52 +192,34 @@ disk_set_cache (char* device, char* cache, char* no_cyl)
|
||||
attrib_data.nr_cyl);
|
||||
}
|
||||
|
||||
/* Open device file */
|
||||
fd = open (device, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
error_print ("<%s> - %s", device, strerror (errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set the given caching attributes */
|
||||
printf ("Setting cache mode for device <%s>...\n", device);
|
||||
if (ioctl (fd, BIODASDSATTR, &attrib_data)) {
|
||||
error_print ("Could not set caching for device <%s>", device);
|
||||
close (fd);
|
||||
rc = dasd_set_cache(device, &attrib_data);
|
||||
if (rc) {
|
||||
error_print("Could not set caching for device <%s>", device);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf ("Done.\n");
|
||||
close (fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Reserve the device.
|
||||
*/
|
||||
int
|
||||
disk_reserve (char* device)
|
||||
int disk_reserve(char *device)
|
||||
{
|
||||
int fd;
|
||||
|
||||
/* Open device file */
|
||||
fd = open (device, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
error_print ("<%s> - %s", device, strerror (errno));
|
||||
return -1;
|
||||
}
|
||||
int rc;
|
||||
|
||||
/* Reserve device */
|
||||
printf ("Reserving device <%s>...\n", device);
|
||||
if (ioctl (fd, BIODASDRSRV)) {
|
||||
error_print ("Could not reserve device <%s>", device);
|
||||
close (fd);
|
||||
rc = dasd_disk_reserve(device);
|
||||
if (rc) {
|
||||
error_print("Could not reserve device <%s>", device);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf ("Done.\n");
|
||||
close (fd);
|
||||
printf("Done.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -345,28 +227,18 @@ disk_reserve (char* device)
|
||||
/*
|
||||
* Release the device.
|
||||
*/
|
||||
int
|
||||
disk_release (char* device)
|
||||
int disk_release(char *device)
|
||||
{
|
||||
int fd;
|
||||
int rc;
|
||||
|
||||
/* Open device file */
|
||||
fd = open (device, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
error_print ("<%s> - %s", device, strerror (errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Release device */
|
||||
printf ("Releasing device <%s>...\n", device);
|
||||
if (ioctl (fd, BIODASDRLSE)) {
|
||||
error_print ("Could not release device <%s>", device);
|
||||
close (fd);
|
||||
rc = dasd_disk_release(device);
|
||||
if (rc) {
|
||||
error_print("Could not release device <%s>", device);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf ("Done.\n");
|
||||
close (fd);
|
||||
printf("Done.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -376,29 +248,19 @@ disk_release (char* device)
|
||||
* This means to reserve the device even if it was already reserved.
|
||||
* The current reserve is broken (steal lock).
|
||||
*/
|
||||
int
|
||||
disk_slock (char* device)
|
||||
int disk_slock(char *device)
|
||||
{
|
||||
int fd;
|
||||
|
||||
/* Open device file */
|
||||
fd = open (device, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
error_print ("<%s> - %s", device, strerror (errno));
|
||||
return -1;
|
||||
}
|
||||
int rc;
|
||||
|
||||
/* Unconditional reserve device */
|
||||
printf ("Unconditional reserving device <%s>...\n", device);
|
||||
if (ioctl (fd, BIODASDSLCK)) {
|
||||
error_print ("Could not unconditional reserve device <%s>",
|
||||
device);
|
||||
close (fd);
|
||||
rc = dasd_slock(device);
|
||||
if (rc) {
|
||||
error_print("Could not unconditional reserve device <%s>", device);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf ("Done.\n");
|
||||
close (fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -407,27 +269,16 @@ disk_slock (char* device)
|
||||
* Uses the Sense Path Group ID (SNID) ioctl to find out if
|
||||
* a device is reserved to it's path group.
|
||||
*/
|
||||
int
|
||||
disk_query_reserve_status(char* device)
|
||||
int disk_query_reserve_status(char *device)
|
||||
{
|
||||
int fd;
|
||||
struct dasd_snid_ioctl_data snid;
|
||||
int rc;
|
||||
|
||||
/* Open device file */
|
||||
fd = open (device, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
error_print ("<%s> - %s", device, strerror (errno));
|
||||
rc = dasd_query_reserve(device);
|
||||
if (rc < 0) {
|
||||
error_print("Could not read reserve status for device <%s>", device);
|
||||
return -1;
|
||||
}
|
||||
snid.path_mask = 0;
|
||||
/* Release device */
|
||||
if (ioctl(fd, BIODASDSNID, &snid)) {
|
||||
error_print("Could not read reserve status"
|
||||
" for device <%s>", device);
|
||||
close (fd);
|
||||
return -1;
|
||||
}
|
||||
switch (snid.data.path_state.reserve) {
|
||||
switch (rc) {
|
||||
case 0:
|
||||
printf("none\n");
|
||||
break;
|
||||
@@ -441,7 +292,7 @@ disk_query_reserve_status(char* device)
|
||||
printf("reserved\n");
|
||||
break;
|
||||
}
|
||||
close (fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -615,21 +466,14 @@ static int disk_profile_item(dasd_profile_info_t dasd_profile_info,
|
||||
/*
|
||||
* Get and print the profiling info of the device.
|
||||
*/
|
||||
int
|
||||
disk_profile (char* device, char* prof_item)
|
||||
int disk_profile(char *device, char *prof_item)
|
||||
{
|
||||
int fd, rc;
|
||||
dasd_profile_info_t dasd_profile_info;
|
||||
|
||||
/* Open device file */
|
||||
fd = open (device, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
error_print ("<%s> - %s", device, strerror (errno));
|
||||
return -1;
|
||||
}
|
||||
int rc;
|
||||
|
||||
/* Get the profile info */
|
||||
if (ioctl (fd, BIODASDPRRD, &dasd_profile_info)) {
|
||||
rc = dasd_profile(device, &dasd_profile_info);
|
||||
if (rc) {
|
||||
switch (errno) {
|
||||
case EIO: /* profiling is not active */
|
||||
error_print ("Profiling (on device <%s>) is not "
|
||||
@@ -639,7 +483,6 @@ disk_profile (char* device, char* prof_item)
|
||||
error_print ("Could not get profile info for device "
|
||||
"<%s>.", device);
|
||||
}
|
||||
close (fd);
|
||||
return -1;
|
||||
}
|
||||
/* Check for profile item or summary */
|
||||
@@ -648,38 +491,26 @@ disk_profile (char* device, char* prof_item)
|
||||
} else {
|
||||
rc = disk_profile_item (dasd_profile_info, prof_item);
|
||||
}
|
||||
|
||||
close (fd);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Reset the profiling counters of the device.
|
||||
*/
|
||||
int
|
||||
disk_reset_prof (char* device)
|
||||
int disk_reset_prof(char *device)
|
||||
{
|
||||
int fd;
|
||||
|
||||
/* Open device file */
|
||||
fd = open (device, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
error_print ("<%s> - %s", device, strerror (errno));
|
||||
return -1;
|
||||
}
|
||||
int rc;
|
||||
|
||||
/* reset profile info */
|
||||
printf ("Resetting profile info for device <%s>...\n", device);
|
||||
if (ioctl (fd, BIODASDPRRST)) {
|
||||
error_print ("Could not reset profile info for device <%s>",
|
||||
device);
|
||||
close (fd);
|
||||
rc = dasd_reset_profile(device);
|
||||
if (rc) {
|
||||
error_print("Could not reset profile info for device <%s>", device);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf ("Done.\n");
|
||||
close (fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -717,3 +548,50 @@ int disk_reset_chpid(char *device, char *chpid)
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int disk_copy_swap(char *device, char *copy_pair)
|
||||
{
|
||||
struct dasd_copypair_swap_data data = { 0 };
|
||||
char *primary, *secondary;
|
||||
int rc = 0;
|
||||
|
||||
primary = strtok(copy_pair, ",");
|
||||
secondary = strtok(NULL, ",");
|
||||
|
||||
if (!primary || !secondary) {
|
||||
error_print("%s: Error parsing Copy Pair %s", device, copy_pair);
|
||||
return -1;
|
||||
}
|
||||
|
||||
util_strlcpy(data.primary, primary, DASD_BUS_ID_SIZE);
|
||||
util_strlcpy(data.secondary, secondary, DASD_BUS_ID_SIZE);
|
||||
|
||||
printf("Swapping copy pair %s %s on device <%s>...\n", primary, secondary, device);
|
||||
rc = dasd_copy_swap(device, &data);
|
||||
|
||||
switch (rc) {
|
||||
case 0:
|
||||
printf("Done.\n");
|
||||
return 0;
|
||||
case 1:
|
||||
error_print("Swap data invalid");
|
||||
break;
|
||||
case 2:
|
||||
error_print("No active device found");
|
||||
break;
|
||||
case 3:
|
||||
error_print("Wrong primary device specified");
|
||||
break;
|
||||
case 4:
|
||||
error_print("Secondary device not found");
|
||||
break;
|
||||
case 5:
|
||||
error_print("Swap already running");
|
||||
break;
|
||||
default:
|
||||
error_print("Swap not successful rc %d", rc);
|
||||
break;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -119,12 +119,17 @@ static struct util_opt opt_vec[] = {
|
||||
.desc = "Reset all channel paths of a device",
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
{
|
||||
.option = { "copy-pair-swap", required_argument, NULL, 's' },
|
||||
.argument = "COPY_PAIR",
|
||||
.desc = "Swap a specified, comma separated copy pair.",
|
||||
},
|
||||
UTIL_OPT_HELP,
|
||||
UTIL_OPT_VERSION,
|
||||
UTIL_OPT_END
|
||||
};
|
||||
|
||||
#define CMD_KEYWORD_NUM 16
|
||||
#define CMD_KEYWORD_NUM 17
|
||||
#define DEVICES_NUM 256
|
||||
|
||||
enum cmd_keyword_id {
|
||||
@@ -144,6 +149,7 @@ enum cmd_keyword_id {
|
||||
cmd_keyword_path_all,
|
||||
cmd_keyword_enable_stats,
|
||||
cmd_keyword_disable_stats,
|
||||
cmd_keyword_copy_swap,
|
||||
};
|
||||
|
||||
|
||||
@@ -167,7 +173,8 @@ static const struct {
|
||||
{ "path_reset", cmd_keyword_path },
|
||||
{ "path_reset_all", cmd_keyword_path_all },
|
||||
{ "enable-stats", cmd_keyword_enable_stats },
|
||||
{ "disable-stats", cmd_keyword_disable_stats }
|
||||
{ "disable-stats", cmd_keyword_disable_stats },
|
||||
{ "copy-swap", cmd_keyword_copy_swap },
|
||||
};
|
||||
|
||||
|
||||
@@ -180,26 +187,27 @@ enum cmd_key_state {
|
||||
|
||||
/* Determines which combination of keywords are valid */
|
||||
static enum cmd_key_state cmd_key_table[CMD_KEYWORD_NUM][CMD_KEYWORD_NUM] = {
|
||||
/* help vers get_ cach no_c rese rele sloc prof prof rese quer path path
|
||||
* ion cach e yl rve ase k ile _ite t_pr y_re _all
|
||||
* e m of serv
|
||||
/* help vers get_ cach no_c rese rele sloc prof prof rese quer path path enab disa copy
|
||||
* ion cach e yl rve ase k ile _ite t_pr y_re _all le-s ble- -swa
|
||||
* e m of serv tats stat p
|
||||
*/
|
||||
/* help */ { req, opt, opt, opt, opt, opt, opt, opt, opt, opt, opt, inv, inv, inv, inv, inv },
|
||||
/* version */ { inv, req, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv },
|
||||
/* get_cache */ { opt, opt, req, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv },
|
||||
/* cache */ { opt, opt, inv, req, opt, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv },
|
||||
/* no_cyl */ { opt, opt, inv, req, req, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv },
|
||||
/* reserve */ { opt, opt, inv, inv, inv, req, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv },
|
||||
/* release */ { opt, opt, inv, inv, inv, inv, req, inv, inv, inv, inv, inv, inv, inv, inv, inv },
|
||||
/* slock */ { opt, opt, inv, inv, inv, inv, inv, req, inv, inv, inv, inv, inv, inv, inv, inv },
|
||||
/* profile */ { opt, opt, inv, inv, inv, inv, inv, inv, req, opt, inv, inv, inv, inv, inv, inv },
|
||||
/* prof_item */ { opt, opt, inv, inv, inv, inv, inv, inv, req, req, inv, inv, inv, inv, inv, inv },
|
||||
/* reset_prof */ { opt, opt, inv, inv, inv, inv, inv, inv, inv, inv, req, inv, inv, inv, inv, inv },
|
||||
/* query_reserve */ { inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req, inv, inv, inv, inv },
|
||||
/* path */ { inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req, inv, inv, inv },
|
||||
/* path_all */ { inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req, inv, inv },
|
||||
/* enable-stats */ { inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req, inv },
|
||||
/* disable-stats */ { inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req },
|
||||
/* help */ { req, opt, opt, opt, opt, opt, opt, opt, opt, opt, opt, inv, inv, inv, inv, inv, inv },
|
||||
/* version */ { inv, req, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv },
|
||||
/* get_cache */ { opt, opt, req, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv },
|
||||
/* cache */ { opt, opt, inv, req, opt, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv },
|
||||
/* no_cyl */ { opt, opt, inv, req, req, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv },
|
||||
/* reserve */ { opt, opt, inv, inv, inv, req, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv },
|
||||
/* release */ { opt, opt, inv, inv, inv, inv, req, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv },
|
||||
/* slock */ { opt, opt, inv, inv, inv, inv, inv, req, inv, inv, inv, inv, inv, inv, inv, inv, inv },
|
||||
/* profile */ { opt, opt, inv, inv, inv, inv, inv, inv, req, opt, inv, inv, inv, inv, inv, inv, inv },
|
||||
/* prof_item */ { opt, opt, inv, inv, inv, inv, inv, inv, req, req, inv, inv, inv, inv, inv, inv, inv },
|
||||
/* reset_prof */ { opt, opt, inv, inv, inv, inv, inv, inv, inv, inv, req, inv, inv, inv, inv, inv, inv },
|
||||
/* query_reserve */ { inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req, inv, inv, inv, inv, inv },
|
||||
/* path */ { inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req, inv, inv, inv, inv },
|
||||
/* path_all */ { inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req, inv, inv, inv },
|
||||
/* enable-stats */ { inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req, inv, inv },
|
||||
/* disable-stats */ { inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req, inv },
|
||||
/* copy-swap */ { inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req },
|
||||
};
|
||||
|
||||
struct parameter {
|
||||
@@ -439,7 +447,9 @@ static int get_command_line(int argc, char *argv[], struct command_line *line)
|
||||
rc = store_option (&cmdline, cmd_keyword_query_reserve,
|
||||
optarg);
|
||||
break;
|
||||
|
||||
case 's':
|
||||
rc = store_option(&cmdline, cmd_keyword_copy_swap, optarg);
|
||||
break;
|
||||
case -1:
|
||||
/* End of options string - start of devices list */
|
||||
cmdline.device_id = optind;
|
||||
@@ -508,6 +518,9 @@ static int do_command(char *device, struct command_line cmdline)
|
||||
case cmd_keyword_path_all:
|
||||
rc = disk_reset_chpid(device, NULL);
|
||||
break;
|
||||
case cmd_keyword_copy_swap:
|
||||
rc = disk_copy_swap(device, cmdline.parm[cmd_keyword_copy_swap].data);
|
||||
break;
|
||||
default:
|
||||
error_print ("Unknown command '%s' specified",
|
||||
get_keyword_name (i));
|
||||
|
||||
158
zconf/lsdasd
158
zconf/lsdasd
@@ -35,6 +35,8 @@ function PrintUsage() {
|
||||
Print extended information about DASDs.
|
||||
-H|--host-access-list
|
||||
Print information about hosts accessing DASDs.
|
||||
-P|--copy-pairs
|
||||
Print information about copy pairs.
|
||||
-v|--verbose
|
||||
For compatibility/future use. Currently ignored.
|
||||
--version
|
||||
@@ -146,6 +148,62 @@ function findDASDDebugfsDirectorie() {
|
||||
fi
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# The blockpath can usually be found over the DEVPATH
|
||||
# In case of a swapped Copy Pair device the blockdev is likely associated
|
||||
# with one of the secondary devices
|
||||
# this function checks if the blockdev is associated with the primary device
|
||||
# and if not search for it in the secondary device list
|
||||
#------------------------------------------------------------------------------
|
||||
function setCorrectBlockPath() {
|
||||
local DRIVERECKD="$SYSFSDIR/bus/ccw/drivers/dasd-eckd/"
|
||||
local DRIVERFBA="$SYSFSDIR/bus/ccw/drivers/dasd-fba/"
|
||||
local SEARCHDIRS=
|
||||
local SEARCHLIST=
|
||||
|
||||
if [[ "$COPYROLE" == "none" ]] || [[ -d "$DEVPATH/block" ]]; then
|
||||
BLOCKPATH="$DEVPATH"/block/dasd*
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ -d "$DRIVERECKD" ]]; then
|
||||
SEARCHDIRS="$DRIVERECKD"
|
||||
fi
|
||||
if [[ -d "$DRIVERFBA" ]]; then
|
||||
SEARCHDIRS="$SEARCHDIRS $DRIVERFBA"
|
||||
fi
|
||||
|
||||
SEARCHLIST=$(echo $SECONDARY_LIST | sed 's/,/ /g')
|
||||
for SEARCHDEV in $SEARCHLIST
|
||||
do
|
||||
if [[ -n "$SEARCHDIRS" ]]; then
|
||||
PRIM_DEVPATH=$(find $SEARCHDIRS -type l -name $SEARCHDEV \
|
||||
-printf "%h/%l\n" 2> /dev/null)
|
||||
if [[ -d "$PRIM_DEVPATH/block" ]]; then
|
||||
break
|
||||
fi
|
||||
else
|
||||
# The above paths may become invalid in the future, so we keep the
|
||||
# following query as backup:
|
||||
PRIM_DEVPATH=$(find "$SYSFSDIR/devices" -type l -name $SEARCHDEV \
|
||||
"driver" -lname "*/dasd*" -printf "%h\n" \
|
||||
2> /dev/null)
|
||||
fi
|
||||
done
|
||||
|
||||
BLOCKPATH="$PRIM_DEVPATH"/block/dasd*
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# gather Copy Pair related data
|
||||
#------------------------------------------------------------------------------
|
||||
function gatherCopyPairData() {
|
||||
read COPYPAIR 2> /dev/null < $DEVPATH/copy_pair
|
||||
PRIMARY_UID=`echo $COPYPAIR | cut -d " " -f1 | cut -d "," -f1`
|
||||
COPYPAIR=`echo "$COPYPAIR" | sed 's/ /,/g' | sed 's/^,//' `
|
||||
SECONDARY_LIST=`echo ${COPYPAIR//$PRIMARY_UID","/} `
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# gather device data and call appropriate output function
|
||||
#------------------------------------------------------------------------------
|
||||
@@ -165,16 +223,18 @@ function gatherDeviceData() {
|
||||
read READONLY 2> /dev/null < $DEVPATH/readonly || continue
|
||||
read DISCIPLINE 2> /dev/null < $DEVPATH/discipline || continue
|
||||
read ESE 2> /dev/null < $DEVPATH/ese
|
||||
read COPYROLE 2> /dev/null < $DEVPATH/copy_role
|
||||
|
||||
if [[ "$COPYROLE" != "none" ]]; then
|
||||
gatherCopyPairData
|
||||
fi
|
||||
# Block device specific information is only available for
|
||||
# devices that are online and not a PAV alias
|
||||
if [[ ! "$ONLINE" == 0 ]] && [[ ! "$ALIAS" == 1 ]]; then
|
||||
#find device Path to the block device
|
||||
if [[ -d "$DEVPATH/block" ]]; then
|
||||
set - "$DEVPATH"/block/dasd*
|
||||
else
|
||||
set - "$DEVPATH"/block:dasd*
|
||||
fi
|
||||
if [[ ! "$ONLINE" == 0 ]] && [[ ! "$ALIAS" == 1 ]] &&
|
||||
[[ ! "$COPYROLE" == "secondary" ]]; then
|
||||
#find device Path to the block device
|
||||
setCorrectBlockPath
|
||||
set - $BLOCKPATH
|
||||
MAJMIN=
|
||||
MAJOR=
|
||||
MINOR=
|
||||
@@ -194,10 +254,12 @@ function gatherDeviceData() {
|
||||
fi
|
||||
fi
|
||||
else
|
||||
# BLOCKNAME for offline and alias devices will not be
|
||||
# printed, it's just a key for sorting
|
||||
# BLOCKNAME for offline, alias and secondary devices
|
||||
# will not be printed, it's just a key for sorting
|
||||
if [[ "$ONLINE" == 0 ]]; then
|
||||
BLOCKNAME=""
|
||||
elif [[ "$COPYROLE" == "secondary" ]]; then
|
||||
BLOCKNAME="b"
|
||||
else
|
||||
BLOCKNAME="a"
|
||||
fi
|
||||
@@ -232,6 +294,8 @@ function gatherDeviceData() {
|
||||
uid
|
||||
elif [[ "$OUTPUT" == "host" ]]; then
|
||||
host
|
||||
elif [[ "$OUTPUT" == "copy" ]]; then
|
||||
copy
|
||||
else
|
||||
newoutput
|
||||
fi
|
||||
@@ -261,6 +325,15 @@ function newoutput()
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "$COPYROLE" == "secondary" ]]; then
|
||||
printf "%s:%s:%-8s secondary %-8s %13s\n" \
|
||||
"$SORTKEYLEN" "$SORTKEY" \
|
||||
"$BUSID" \
|
||||
"$PRIMARY_BLOCKNAME" \
|
||||
"$DISCIPLINE"
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "$READONLY" == 0 ]]; then
|
||||
ROSTRING=""
|
||||
else
|
||||
@@ -519,10 +592,14 @@ function extended()
|
||||
"${HPF_PATHS[@]}" \
|
||||
"${IFCC_PATHS[@]}" ;
|
||||
return
|
||||
elif [[ "$ALIAS" == 1 ]]; then
|
||||
if [[ "$BASEONLY" == "false" ]]; then
|
||||
ACTIVE="alias"
|
||||
printf "%s:%s:%s# status:\t\t\t\t%s# type: \t\t\t\t%s# use_diag:\t\t\t\t%s# readonly:\t\t\t\t%s# eer_enabled:\t\t\t\t%s# erplog:\t\t\t\t%s# hpf:\t\t\t\t\t%s # uid: \t\t\t\t%s# fc_security: \t\t\t\t%s# paths_installed: \t\t\t%s %s %s %s %s %s %s %s# paths_in_use: \t\t\t%s %s %s %s %s %s %s %s# paths_non_preferred: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_cabling: \t\t%s %s %s %s %s %s %s %s# paths_cuir_quiesced: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_hpf_characteristics: \t%s %s %s %s %s %s %s %s# paths_error_threshold_exceeded: \t%s %s %s %s %s %s %s %s#\n" \
|
||||
elif [[ "$ALIAS" == 1 ]] || [[ "$COPYROLE" == "secondary" ]]; then
|
||||
if [[ "$BASEONLY" == "false" ]] || [[ "$COPYROLE" == "secondary" ]]; then
|
||||
if [[ "$COPYROLE" == "secondary" ]]; then
|
||||
ACTIVE="secondary"
|
||||
else
|
||||
ACTIVE="alias"
|
||||
fi
|
||||
printf "%s:%s:%s# status:\t\t\t\t%s# type: \t\t\t\t%s# use_diag:\t\t\t\t%s# readonly:\t\t\t\t%s# eer_enabled:\t\t\t\t%s# erplog:\t\t\t\t%s# hpf:\t\t\t\t\t%s # uid: \t\t\t\t%s# fc_security: \t\t\t\t%s# paths_installed: \t\t\t%s %s %s %s %s %s %s %s# paths_in_use: \t\t\t%s %s %s %s %s %s %s %s# paths_non_preferred: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_cabling: \t\t%s %s %s %s %s %s %s %s# paths_cuir_quiesced: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_hpf_characteristics: \t%s %s %s %s %s %s %s %s# paths_error_threshold_exceeded: \t%s %s %s %s %s %s %s %s# copy_pairs:\t\t\t\t%s#\n" \
|
||||
"$SORTKEYLEN" "$SORTKEY" \
|
||||
"$BUSID" \
|
||||
"$ACTIVE" \
|
||||
@@ -540,7 +617,8 @@ function extended()
|
||||
"${CABLE_PATHS[@]}" \
|
||||
"${CUIR_PATHS[@]}" \
|
||||
"${HPF_PATHS[@]}" \
|
||||
"${IFCC_PATHS[@]}" ;
|
||||
"${IFCC_PATHS[@]}" \
|
||||
"${COPYPAIR}" ;
|
||||
fi
|
||||
return
|
||||
elif [[ -z "$BLOCKNAME" ]] || [[ -z "$SIZE" ]]; then
|
||||
@@ -565,7 +643,7 @@ function extended()
|
||||
DISCIPLINE="${DISCIPLINE} (ESE)"
|
||||
fi
|
||||
|
||||
printf "%s:%s:%s/%s/%s%s%s# status:\t\t\t\t%s# type: \t\t\t\t%s# blksz:\t\t\t\t%s# size: \t\t\t\t%s# blocks:\t\t\t\t%s# extent_size:\t\t\t\t%s# logical_capacity:\t\t\t%s# space_allocated:\t\t\t%s# use_diag:\t\t\t\t%s# readonly:\t\t\t\t%s# eer_enabled:\t\t\t\t%s# erplog:\t\t\t\t%s# hpf:\t\t\t\t\t%s# uid: \t\t\t\t%s# fc_security: \t\t\t\t%s# paths_installed: \t\t\t%s %s %s %s %s %s %s %s# paths_in_use: \t\t\t%s %s %s %s %s %s %s %s# paths_non_preferred: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_cabling: \t\t%s %s %s %s %s %s %s %s# paths_cuir_quiesced: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_hpf_characteristics: \t%s %s %s %s %s %s %s %s# paths_error_threshold_exceeded: \t%s %s %s %s %s %s %s %s#\n" \
|
||||
printf "%s:%s:%s/%s/%s%s%s# status:\t\t\t\t%s# type: \t\t\t\t%s# blksz:\t\t\t\t%s# size: \t\t\t\t%s# blocks:\t\t\t\t%s# extent_size:\t\t\t\t%s# logical_capacity:\t\t\t%s# space_allocated:\t\t\t%s# use_diag:\t\t\t\t%s# readonly:\t\t\t\t%s# eer_enabled:\t\t\t\t%s# erplog:\t\t\t\t%s# hpf:\t\t\t\t\t%s# uid: \t\t\t\t%s# fc_security: \t\t\t\t%s# paths_installed: \t\t\t%s %s %s %s %s %s %s %s# paths_in_use: \t\t\t%s %s %s %s %s %s %s %s# paths_non_preferred: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_cabling: \t\t%s %s %s %s %s %s %s %s# paths_cuir_quiesced: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_hpf_characteristics: \t%s %s %s %s %s %s %s %s# paths_error_threshold_exceeded: \t%s %s %s %s %s %s %s %s# copy_pairs:\t\t\t\t%s#\n" \
|
||||
"$SORTKEYLEN" "$SORTKEY" \
|
||||
"$BUSID" \
|
||||
"$BLOCKNAME" \
|
||||
@@ -593,7 +671,8 @@ function extended()
|
||||
"${CABLE_PATHS[@]}" \
|
||||
"${CUIR_PATHS[@]}" \
|
||||
"${HPF_PATHS[@]}" \
|
||||
"${IFCC_PATHS[@]}" ;
|
||||
"${IFCC_PATHS[@]}" \
|
||||
"${COPYPAIR}" ;
|
||||
}
|
||||
|
||||
function host()
|
||||
@@ -694,6 +773,41 @@ printf "\n";
|
||||
rm -f $temp
|
||||
}
|
||||
|
||||
# pad sortkey with given number zeroes
|
||||
function padSortKey()
|
||||
{
|
||||
local LEN=$1
|
||||
for (( i=0; i<$LEN; i++ ))
|
||||
do
|
||||
printf -v SORTKEY "%s0" $SORTKEY
|
||||
done
|
||||
}
|
||||
|
||||
function copy()
|
||||
{
|
||||
if [[ "$COPYROLE" == "none" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
SORTKEYLEN=$((${#PRIMARY_UID}+${#DEV_UID}))
|
||||
if [[ "$COPYROLE" == "secondary" ]]; then
|
||||
SORTKEY=$PRIMARY_UID$DEV_UID
|
||||
PAIREDDEVICES=$PRIMARY_UID
|
||||
BLOCKNAME=""
|
||||
else
|
||||
SORTKEY=$PRIMARY_UID
|
||||
padSortKey ${#DEV_UID}
|
||||
PAIREDDEVICES=$SECONDARY_LIST
|
||||
fi
|
||||
|
||||
printf "%s:%s:%-8s %-10s %-8s %-8s %s \n" \
|
||||
"$SORTKEYLEN" "$SORTKEY" \
|
||||
"$BUSID" \
|
||||
"$COPYROLE" \
|
||||
"$BLOCKNAME" \
|
||||
"$PAIREDDEVICES";
|
||||
}
|
||||
|
||||
function uid()
|
||||
{
|
||||
#-------------------------------------------#
|
||||
@@ -710,7 +824,11 @@ function uid()
|
||||
fi
|
||||
fi
|
||||
|
||||
printf "%s:%s:%-8s %-8s %s\n" \
|
||||
if [[ "$COPYROLE" == "secondary" ]]; then
|
||||
BLOCKNAME="secondary"
|
||||
fi
|
||||
|
||||
printf "%s:%s:%-8s %-10s %s\n" \
|
||||
"$SORTKEYLEN" "$SORTKEY" \
|
||||
"$BUSID" \
|
||||
"$BLOCKNAME" \
|
||||
@@ -756,6 +874,9 @@ while [ $# -gt 0 ]; do
|
||||
--host-access-list|-H)
|
||||
OUTPUT="host"
|
||||
;;
|
||||
--copy-pairs|-P)
|
||||
OUTPUT="copy"
|
||||
;;
|
||||
--version)
|
||||
PrintVersion
|
||||
exit 0
|
||||
@@ -810,6 +931,9 @@ if [[ "$PRINTUID" == "true" ]] && [[ "$OUTPUT" != "old" ]]; then
|
||||
elif [[ "$OUTPUT" == "new" ]]; then
|
||||
printf "Bus-ID Status Name Device Type BlkSz Size Blocks\n"
|
||||
printf "================================================================================\n"
|
||||
elif [[ "$OUTPUT" == "copy" ]]; then
|
||||
printf "Bus-ID Role Name Paired devices\n";
|
||||
printf "================================================================================\n";
|
||||
elif [[ "$OUTPUT" == "extended" ]]; then
|
||||
PROCESSING=" $PROCESSING | sed 's/#/\n/g' "
|
||||
fi
|
||||
|
||||
@@ -872,7 +872,7 @@ static void show_devices_argv(char *argv[])
|
||||
show_device(rec, card);
|
||||
free(card);
|
||||
}
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
if (sscanf(argv[i]+1, "%x", &dom) == 1) {
|
||||
/* list specific domains of all adapters */
|
||||
@@ -897,7 +897,7 @@ static void show_devices_argv(char *argv[])
|
||||
}
|
||||
free(path);
|
||||
}
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
printf("Invalid adpater id!\n");
|
||||
}
|
||||
|
||||
@@ -55,9 +55,22 @@ install() {
|
||||
chzdev --export - --persistent --by-attrib "zdev:early=1" --quiet \
|
||||
--type 2>/dev/null >> "$_tempfile"
|
||||
|
||||
# Obtain early device configuration for site-specific settings
|
||||
for (( site=0; site<10; site++ ))
|
||||
do
|
||||
chzdev --export - --persistent --by-attrib "zdev:early=1" --site $site \
|
||||
--quiet 2>/dev/null >> "$_tempfile"
|
||||
done
|
||||
|
||||
# Apply via --import to prevent other devices from being configured
|
||||
chzdev --import "$_tempfile" --persistent --base "/etc=$initdir/etc" \
|
||||
--yes --quiet --no-root-update --force >/dev/null
|
||||
# Apply site-specific configurations via --import
|
||||
for (( site=0; site<10; site++ ))
|
||||
do
|
||||
chzdev --import "$_tempfile" --persistent --base "/etc=$initdir/etc" \
|
||||
--site $site --yes --quiet --no-root-update --force >/dev/null
|
||||
done
|
||||
|
||||
rm -f "$_tempfile"
|
||||
|
||||
|
||||
@@ -19,6 +19,20 @@
|
||||
struct subtype;
|
||||
struct setting_list;
|
||||
|
||||
/**
|
||||
* Currently zdev supports 10 sites. Which means, zdev support 10 different
|
||||
* set of attributes which are specific to each site. When the user does
|
||||
* not provide any site information, the common set will be used which is
|
||||
* not specific to any site. So, total we have 11 persistent attribute sets
|
||||
* Where,
|
||||
* 0- 9: Site specific attributes
|
||||
* 10: Common attributes which do not belong to any sites
|
||||
*/
|
||||
|
||||
#define NUM_SITES 11
|
||||
#define NUM_USER_SITES (NUM_SITES - 1)
|
||||
#define SITE_FALLBACK NUM_USER_SITES
|
||||
|
||||
/**
|
||||
* struct device_state - Represent the state of a device in a configuration
|
||||
* @settings: List of attribute settings
|
||||
@@ -61,6 +75,7 @@ struct device {
|
||||
struct device_state active;
|
||||
struct device_state persistent;
|
||||
struct device_state autoconf;
|
||||
struct device_state site_specific[NUM_SITES];
|
||||
|
||||
unsigned int processed:1;
|
||||
};
|
||||
@@ -97,8 +112,11 @@ struct device *device_list_find(struct device_list *, const char *,
|
||||
struct device *);
|
||||
void device_list_print(struct device_list *, int);
|
||||
struct setting_list *device_get_setting_list(struct device *dev,
|
||||
config_t config);
|
||||
config_t config,
|
||||
int site_id);
|
||||
|
||||
config_t device_get_config(struct device *dev);
|
||||
char *device_get_sites(struct device *dev);
|
||||
bool is_dev_pers(struct device *dev);
|
||||
|
||||
#endif /* DEVICE_H */
|
||||
|
||||
@@ -38,6 +38,7 @@ extern struct devtype *devtypes[];
|
||||
* devtypes with empty title will not be shown with --list-types.
|
||||
* @devname: Short name for devices of this device type
|
||||
* @modules: (Optional) Array of kernel modules required for this devtype
|
||||
* @site_support: Support for site-specific configuration for the device-type
|
||||
* @subtypes: Array of subtypes
|
||||
* @type_attribs: Array of device type attributes (may point to empty array)
|
||||
* @unknown_type_attribs: Allow specification of unknown device type attributes
|
||||
@@ -59,6 +60,7 @@ struct devtype {
|
||||
const char title[DEVTYPE_TITLE_LEN + 1];
|
||||
const char *devname;
|
||||
const char **modules;
|
||||
unsigned int site_support:1;
|
||||
|
||||
struct subtype **subtypes;
|
||||
struct attrib **type_attribs;
|
||||
|
||||
@@ -103,6 +103,8 @@ extern int delayed_warnings;
|
||||
extern unsigned long longrun_total;
|
||||
extern unsigned long longrun_current;
|
||||
|
||||
extern int global_site_id;
|
||||
|
||||
void misc_exit(void);
|
||||
void indent(unsigned int, const char *, ...);
|
||||
void error(const char *, ...);
|
||||
@@ -239,6 +241,7 @@ void longrun_stop(void);
|
||||
char *skip_comp(char *);
|
||||
void byte_swap(uint8_t *, unsigned int *, unsigned);
|
||||
bool valid_hex(const char *);
|
||||
bool is_valid_site(const char *);
|
||||
void debug_init(int, char **);
|
||||
|
||||
#endif /* MISC_H */
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#define MODPROBE_PREFIX "s390x"
|
||||
#define UDEV_PREFIX "41"
|
||||
#define UDEV_SUFFIX ".rules"
|
||||
#define ZDEV_PREFIX "40"
|
||||
|
||||
#define PATH_MODPROBE_CONF "/etc/modprobe.d"
|
||||
#define PATH_MODPROBE "/sbin/modprobe"
|
||||
@@ -65,6 +66,7 @@ char *path_get_zfcp_lun_dev(struct zfcp_lun_devid *);
|
||||
char *path_get_zfcp_port_dev(struct zfcp_lun_devid *);
|
||||
char *path_get_scsi_hctl_dev(const char *);
|
||||
char *path_get_bus_attr(const char *, const char *);
|
||||
char *path_get_zdev_rule(const char *name);
|
||||
|
||||
exit_code_t path_for_each(const char *,
|
||||
exit_code_t (*callback)(const char *, const char *,
|
||||
|
||||
@@ -12,6 +12,6 @@
|
||||
|
||||
#include "exit_code.h"
|
||||
|
||||
exit_code_t initrd_check(bool all_pers);
|
||||
exit_code_t initrd_check(bool all_pers, int site_validate);
|
||||
|
||||
#endif /* ROOT_H */
|
||||
|
||||
@@ -66,7 +66,7 @@ typedef char *(*table_value_cb_t)(void *item, int id, const char *heading,
|
||||
struct column *table_get_column(struct column *, const char *);
|
||||
exit_code_t table_print(struct column *, table_value_cb_t, void *,
|
||||
struct util_list *, struct util_list *, int, int, int,
|
||||
int);
|
||||
int, int);
|
||||
exit_code_t table_check_columns(struct column *, struct util_list *);
|
||||
void table_print_columns(struct column *, struct util_list *, int, int);
|
||||
void table_set_default(struct column *, int, int);
|
||||
|
||||
@@ -23,7 +23,7 @@ struct table_attrib {
|
||||
};
|
||||
|
||||
struct table_attrib *table_attrib_new(struct subtype *, struct attrib *);
|
||||
void table_attribs_show(struct util_list *, int, int, struct devtype *);
|
||||
void table_attribs_show(struct util_list *, int, int, int, struct devtype *);
|
||||
void table_attribs_show_details(struct util_list *, struct devtype *);
|
||||
|
||||
#endif /* TABLE_ATTRIBS_H */
|
||||
|
||||
@@ -19,6 +19,9 @@ struct setting_list;
|
||||
extern int udev_need_settle;
|
||||
extern int udev_no_settle;
|
||||
|
||||
#define SITE_BLOCK_START "# site_start"
|
||||
#define SITE_BLOCK_END "# site_end"
|
||||
|
||||
bool udev_file_is_empty(struct util_udev_file *file);
|
||||
|
||||
void udev_get_device_ids(const char *type, struct util_list *list,
|
||||
@@ -31,4 +34,7 @@ void udev_add_internal_from_entry(struct setting_list *list,
|
||||
struct util_udev_entry_node *entry,
|
||||
struct attrib **attribs);
|
||||
|
||||
exit_code_t udev_write_site_rule(void);
|
||||
bool is_legacy_rule(struct util_udev_file *file);
|
||||
|
||||
#endif /* UDEV_H */
|
||||
|
||||
15
zdev/include/zdev_id.h
Normal file
15
zdev/include/zdev_id.h
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 ZDEV_ID_H
|
||||
#define ZDEV_ID_H
|
||||
|
||||
#define ZDEV_SITE_ID_FILE "/run/zdev_id.env"
|
||||
|
||||
#endif /* ZDEV_ID_H */
|
||||
@@ -82,6 +82,42 @@ when a device driver is loaded.
|
||||
.PP
|
||||
By default chzdev applies changes to both the active and the persistent
|
||||
configuration.
|
||||
.
|
||||
.SS "Site-specific configurations"
|
||||
In some high-availability setups, root and boot disks of a Linux installation are
|
||||
copied to remote sites that can take over execution of a Linux workload in case of
|
||||
an outage of the original site. A
|
||||
.B site
|
||||
in this context is an execution environment such as an
|
||||
.B LPAR or
|
||||
.B z/VM guest.
|
||||
Each site may provide a different set of devices, or require different parameters to be
|
||||
applied per device.
|
||||
|
||||
chzdev supports site-specific device configuration for up to
|
||||
.B 10
|
||||
sites. Each site is identified by a number in the range 0 to 9. You can provide a separate
|
||||
set of persistent device configuration for each site. The IPL Load Parameter value specified
|
||||
during boot determines the currently active site. Only the device configuration of the active
|
||||
site is applied during boot and when new devices become available. A common configuration can
|
||||
be provided that is applied when no site-specific configuration is available for a device in
|
||||
the active site.
|
||||
|
||||
You can use the
|
||||
.B --site
|
||||
parameter to configure devices for a specific site only. Configuration actions without a site
|
||||
parameter apply to the common configuration.
|
||||
|
||||
Linux supports the following IPL Load Parameter values for selecting the active site:
|
||||
|
||||
.B S<n>
|
||||
Site with number <n> becomes the active site. Example: "S2" specifies site 2 as active site.
|
||||
|
||||
.B SS
|
||||
The Subchannel-Set ID (SSID) of the IPL device specifies the number of the active site.
|
||||
|
||||
If no site is specified in the Load Parameter, only the common configuration is applied.
|
||||
|
||||
.PP
|
||||
.
|
||||
.SS "Tool actions"
|
||||
@@ -392,6 +428,10 @@ Removes configuration files and settings associated with the
|
||||
selected device.
|
||||
.PP
|
||||
.
|
||||
.OD disable-all "D" ""
|
||||
Disable device in all configurations.
|
||||
.PP
|
||||
.
|
||||
.OD enable "e" ""
|
||||
Enable device.
|
||||
|
||||
@@ -583,6 +623,34 @@ Apply changes to persistent configuration only.
|
||||
|
||||
The persistent configuration is put into effect when the system boots, or
|
||||
when a device becomes available, or when a device driver is loaded.
|
||||
.PP
|
||||
.
|
||||
.OD site "s" "SITE-ID"
|
||||
Apply the persistent configuration to the specified site only.
|
||||
|
||||
This option can be used to configure the device in such a way that, the
|
||||
same configuration can be used on multiple different sites with different
|
||||
device hardwares, without any modification. With the site information, we
|
||||
can add different configurations which are specific to different sites. Where
|
||||
each site represents different systems or different hardware devices which
|
||||
are even located in different locations.
|
||||
For example, having
|
||||
the configuration of main-site or failover-site in the same place, but these
|
||||
configurations used only on the respective sites. Currently a user can define
|
||||
10 different configurations for 10 different sites with
|
||||
.I SITE-ID
|
||||
0 to 9.
|
||||
|
||||
The configuration settings is put into effect only on the system with the
|
||||
site number which is mentioned in
|
||||
.I SITE-ID.
|
||||
|
||||
This parameter is only effective only for persistent configurations and for
|
||||
dasd device-types.
|
||||
|
||||
.B Example:
|
||||
.CL chzdev -ep 0.0.f001 --site 3
|
||||
|
||||
.PP
|
||||
.
|
||||
.OD quiet "q" ""
|
||||
|
||||
@@ -74,9 +74,43 @@ of the currently running system, the persistent configuration stored in
|
||||
configuration files, and the auto-configuration that is provided by some
|
||||
machine types to automatically enable I/O devices.
|
||||
.PP
|
||||
Persistent configuration data is of two different types. Site-specific configuration
|
||||
and common-configuration. Common configuration refers to the persistent configuration
|
||||
which is not bound to any site. In many use cases, this configuration is being
|
||||
used as a fallback configuration.
|
||||
Refer the
|
||||
.B site
|
||||
section below to learn more about site-specific configuration.
|
||||
.PP
|
||||
By default lszdev displays information from both the active and the persistent
|
||||
configuration, and, if available, from the auto-configuration.
|
||||
.PP
|
||||
.SS Output Format
|
||||
In list format, lszdev uses values with the following meaning for the persistent colums (PERS):
|
||||
|
||||
.B yes
|
||||
.CL A persistent, non-site specific configuration exists; No site specific configurations available.
|
||||
|
||||
.B no
|
||||
.CL No persistent configurations is available.
|
||||
|
||||
.B auto
|
||||
.CL Machine-provided auto-configuration is available.
|
||||
|
||||
.B s<n>
|
||||
.CL A site-specific configuration is available for site-id n.
|
||||
|
||||
.B s+
|
||||
.CL Site specific configurations are available for multiple sites.
|
||||
|
||||
.B Example
|
||||
|
||||
.CL TYPE ID ON PERS NAMES
|
||||
.CL dasd-eckd 0.0.f001 no s+ dasda
|
||||
.CL dasd-eckd 0.0.f002 no s4 dasdb
|
||||
.CL dasd-eckd 0.0.f003 no no
|
||||
.CL qeth 0.0.1200:0.0.1201:0.0.1202 no yes encbdf0
|
||||
|
||||
.
|
||||
.
|
||||
.SH TYPE
|
||||
@@ -123,6 +157,7 @@ comma (,).
|
||||
.PP
|
||||
.
|
||||
.
|
||||
|
||||
.SH SELECTION
|
||||
In addition to specifying devices by their ID, you can also select devices by
|
||||
their state or by the Linux functions they provide.
|
||||
@@ -292,6 +327,7 @@ Display detailed information.
|
||||
Displays detailed information about the configuration of the selected device
|
||||
or device type. Specifying \-\-info twice will display additional device
|
||||
information.
|
||||
|
||||
.PP
|
||||
.
|
||||
.OD list-columns "l" ""
|
||||
@@ -387,11 +423,41 @@ with the name of the corresponding column. Values are enclosed in double
|
||||
quotation marks. Any quotation marks or slashes in the value string are
|
||||
escaped by placing a slash in front of it.
|
||||
.PP
|
||||
|
||||
.OD shell "" ""
|
||||
Produces KEY="VALUE" format usable as shell variables.
|
||||
|
||||
You can use this option together with the --pairs option to generate
|
||||
output that is suitable to be sourced in a shell environment. This
|
||||
option generates similar output as it would be provided by the --pairs
|
||||
option but it would take care of replacing any character of a column
|
||||
name that would not be appropriate to be used as part of a shell
|
||||
variable name by an underscore.
|
||||
.PP
|
||||
.
|
||||
.OD persistent "p" ""
|
||||
List information from the persistent configuration only.
|
||||
|
||||
Restricts output to information obtained from configuration files.
|
||||
.PP
|
||||
.
|
||||
.OD site "s" "SITE-ID"
|
||||
List the persistent configuration from the specified site only.
|
||||
|
||||
The resulted persistent configuration details are filtered based on the
|
||||
.ID SITE-ID
|
||||
value. The site specific information can be found in a basic lszdev output.
|
||||
For example, the PERS column may show "s+" which indicates that the device
|
||||
has multiple configurations for multiple sites, and "sn" where 'n' is an
|
||||
number between 0 to 9, which indicates that the persistent configuration
|
||||
for the device is available only for site 'n'.
|
||||
|
||||
.B Example:
|
||||
|
||||
.CL lszdev --site 3
|
||||
.CL lszdev -i --site 2
|
||||
.CL lszdev f001 --site 9
|
||||
|
||||
.PP
|
||||
.
|
||||
.OD quiet "q" ""
|
||||
|
||||
@@ -81,9 +81,6 @@ lszdev_objects += generic_ccw.o
|
||||
|
||||
all: chzdev lszdev zdev_id
|
||||
|
||||
ifneq (${HAVE_LOCKFILE}, 0)
|
||||
LDLIBS += -llockfile
|
||||
endif
|
||||
ifneq (${HAVE_JSONC}, 0)
|
||||
LDLIBS += -ljson-c
|
||||
endif
|
||||
|
||||
@@ -1593,7 +1593,7 @@ static exit_code_t ccw_st_deconfigure_active(struct subtype *st,
|
||||
static exit_code_t ccw_st_deconfigure_persistent(struct subtype *st,
|
||||
struct device *dev)
|
||||
{
|
||||
return udev_remove_rule(st->name, dev->id, false);
|
||||
return ccw_st_configure_persistent(st, dev);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "lib/util_path.h"
|
||||
#include "lib/zt_common.h"
|
||||
@@ -44,6 +45,12 @@
|
||||
#include "udev.h"
|
||||
#include "zfcp_lun.h"
|
||||
|
||||
/* current site-id in action. By default, operations are always on fallback
|
||||
* site; We need the current site-id as global variable to avoid excessive
|
||||
* diff of simple function parameter modifications to pass this value.
|
||||
*/
|
||||
int global_site_id = SITE_FALLBACK;
|
||||
|
||||
/* Main program action. */
|
||||
typedef enum {
|
||||
ACT_CONFIGURE,
|
||||
@@ -73,6 +80,7 @@ struct options {
|
||||
/* Actions */
|
||||
unsigned int enable:1;
|
||||
unsigned int deconfigure:1;
|
||||
unsigned int deconfigure_all:1;
|
||||
unsigned int list_attribs:1;
|
||||
unsigned int list_types:1;
|
||||
unsigned int help_attribs:1;
|
||||
@@ -97,6 +105,7 @@ struct options {
|
||||
unsigned int verbose:1;
|
||||
unsigned int quiet:1;
|
||||
unsigned int no_settle:1;
|
||||
unsigned int site_id;
|
||||
};
|
||||
|
||||
/* Makefile converts chzdev_usage.txt into C file which we include here. */
|
||||
@@ -119,6 +128,7 @@ enum {
|
||||
OPT_TYPE = 't',
|
||||
OPT_ENABLE = 'e',
|
||||
OPT_DECONFIGURE = 'd',
|
||||
OPT_DECONFIGURE_ALL = 'D',
|
||||
OPT_LIST_ATTRIBS = 'l',
|
||||
OPT_HELP_ATTRIBS = 'H',
|
||||
OPT_LIST_TYPES = 'L',
|
||||
@@ -140,43 +150,52 @@ enum {
|
||||
OPT_QUIET = 'q',
|
||||
OPT_NO_SETTLE = (OPT_ANONYMOUS_BASE+__COUNTER__),
|
||||
OPT_AUTO_CONF = (OPT_ANONYMOUS_BASE+__COUNTER__),
|
||||
OPT_SITE = 's',
|
||||
};
|
||||
|
||||
static struct opts_conflict conflict_list[] = {
|
||||
OPTS_CONFLICT(OPT_ENABLE,
|
||||
OPT_DECONFIGURE, OPT_LIST_ATTRIBS, OPT_LIST_TYPES,
|
||||
OPT_EXPORT, OPT_IMPORT, OPT_APPLY, OPT_TYPE, 0),
|
||||
OPT_EXPORT, OPT_IMPORT, OPT_APPLY, OPT_TYPE,
|
||||
OPT_DECONFIGURE_ALL, 0),
|
||||
OPTS_CONFLICT(OPT_DECONFIGURE,
|
||||
OPT_LIST_ATTRIBS, OPT_HELP_ATTRIBS, OPT_LIST_TYPES,
|
||||
OPT_EXPORT, OPT_IMPORT, OPT_APPLY, OPT_REMOVE,
|
||||
OPT_REMOVE_ALL, OPT_TYPE, 0),
|
||||
OPT_REMOVE_ALL, OPT_TYPE,
|
||||
OPT_DECONFIGURE_ALL, 0),
|
||||
OPTS_CONFLICT(OPT_LIST_ATTRIBS,
|
||||
OPT_DECONFIGURE, OPT_HELP_ATTRIBS, OPT_LIST_TYPES,
|
||||
OPT_EXPORT, OPT_IMPORT, OPT_APPLY, OPT_REMOVE,
|
||||
OPT_REMOVE_ALL, OPT_CONFIGURED, OPT_EXISTING, OPT_ONLINE,
|
||||
OPT_OFFLINE, OPT_BY_PATH, OPT_BY_NODE, OPT_BY_INTERFACE,
|
||||
OPT_BY_ATTRIB, OPT_ACTIVE, OPT_PERSISTENT, OPT_FAILED, 0),
|
||||
OPT_BY_ATTRIB, OPT_ACTIVE, OPT_PERSISTENT, OPT_FAILED,
|
||||
OPT_DECONFIGURE_ALL, 0),
|
||||
OPTS_CONFLICT(OPT_LIST_TYPES,
|
||||
OPT_DECONFIGURE, OPT_LIST_ATTRIBS, OPT_HELP_ATTRIBS,
|
||||
OPT_EXPORT, OPT_IMPORT, OPT_APPLY, OPT_REMOVE,
|
||||
OPT_REMOVE_ALL, OPT_CONFIGURED, OPT_EXISTING, OPT_ONLINE,
|
||||
OPT_OFFLINE, OPT_BY_PATH, OPT_BY_NODE, OPT_BY_INTERFACE,
|
||||
OPT_BY_ATTRIB, OPT_ACTIVE, OPT_PERSISTENT, OPT_FAILED, 0),
|
||||
OPT_BY_ATTRIB, OPT_ACTIVE, OPT_PERSISTENT, OPT_FAILED,
|
||||
OPT_DECONFIGURE_ALL, 0),
|
||||
OPTS_CONFLICT(OPT_EXPORT,
|
||||
OPT_DECONFIGURE, OPT_LIST_ATTRIBS, OPT_HELP_ATTRIBS,
|
||||
OPT_LIST_TYPES, OPT_IMPORT, OPT_APPLY, OPT_REMOVE,
|
||||
OPT_REMOVE_ALL, 0),
|
||||
OPT_REMOVE_ALL, OPT_DECONFIGURE_ALL, 0),
|
||||
OPTS_CONFLICT(OPT_IMPORT,
|
||||
OPT_DECONFIGURE, OPT_LIST_ATTRIBS, OPT_HELP_ATTRIBS,
|
||||
OPT_LIST_TYPES, OPT_EXPORT, OPT_APPLY, OPT_REMOVE,
|
||||
OPT_REMOVE_ALL, OPT_CONFIGURED, OPT_EXISTING, OPT_BY_PATH,
|
||||
OPT_BY_ATTRIB, OPT_BY_NODE, OPT_BY_INTERFACE, 0),
|
||||
OPT_BY_ATTRIB, OPT_BY_NODE, OPT_BY_INTERFACE,
|
||||
OPT_DECONFIGURE_ALL, 0),
|
||||
OPTS_CONFLICT(OPT_APPLY,
|
||||
OPT_DECONFIGURE, OPT_LIST_ATTRIBS, OPT_HELP_ATTRIBS,
|
||||
OPT_LIST_TYPES, OPT_EXPORT, OPT_IMPORT, OPT_REMOVE,
|
||||
OPT_REMOVE_ALL, OPT_ACTIVE, 0),
|
||||
OPT_REMOVE_ALL, OPT_ACTIVE,
|
||||
OPT_DECONFIGURE_ALL, 0),
|
||||
OPTS_CONFLICT(OPT_ONLINE,
|
||||
OPT_OFFLINE),
|
||||
OPTS_CONFLICT(OPT_SITE,
|
||||
OPT_TYPE, 0),
|
||||
OPTS_CONFLICT(OPT_QUIET,
|
||||
OPT_VERBOSE),
|
||||
OPTS_CONFLICT(OPT_AUTO_CONF,
|
||||
@@ -202,6 +221,7 @@ static const struct option opt_list[] = {
|
||||
/* Actions. */
|
||||
{ "enable", no_argument, NULL, OPT_ENABLE },
|
||||
{ "disable", no_argument, NULL, OPT_DECONFIGURE },
|
||||
{ "disable-all", no_argument, NULL, OPT_DECONFIGURE_ALL },
|
||||
{ "list-attributes", no_argument, NULL, OPT_LIST_ATTRIBS },
|
||||
{ "help-attribute", no_argument, NULL, OPT_HELP_ATTRIBS },
|
||||
{ "list-types", no_argument, NULL, OPT_LIST_TYPES },
|
||||
@@ -225,11 +245,12 @@ static const struct option opt_list[] = {
|
||||
{ "verbose", no_argument, NULL, OPT_VERBOSE },
|
||||
{ "quiet", no_argument, NULL, OPT_QUIET },
|
||||
{ "no-settle", no_argument, NULL, OPT_NO_SETTLE },
|
||||
{ "site", required_argument, NULL, OPT_SITE },
|
||||
{ NULL, no_argument, NULL, 0 },
|
||||
};
|
||||
|
||||
/* Command line abbreviations. */
|
||||
static const char opt_str[] = ":edlHLapr:RfyhvVqt";
|
||||
static const char opt_str[] = ":edlHLapr:RfyhvVqts:D";
|
||||
|
||||
/* Count of persistently modified devices. */
|
||||
static int pers_mod_devs;
|
||||
@@ -246,6 +267,8 @@ static void init_options(struct options *opts)
|
||||
opts->settings = strlist_new();
|
||||
opts->remove = strlist_new();
|
||||
opts->base = strlist_new();
|
||||
/* Default operations are on fallback site*/
|
||||
opts->site_id = SITE_FALLBACK;
|
||||
}
|
||||
|
||||
/* Release memory used in options data structure. */
|
||||
@@ -838,6 +861,12 @@ static exit_code_t parse_options(struct options *opts, int argc, char *argv[])
|
||||
opts->deconfigure = 1;
|
||||
break;
|
||||
|
||||
case OPT_DECONFIGURE_ALL:
|
||||
/* --disable all the settings*/
|
||||
opts->deconfigure = 1;
|
||||
opts->deconfigure_all = 1;
|
||||
break;
|
||||
|
||||
case OPT_LIST_ATTRIBS:
|
||||
/* --list-attributes */
|
||||
opts->list_attribs = 1;
|
||||
@@ -955,6 +984,28 @@ static exit_code_t parse_options(struct options *opts, int argc, char *argv[])
|
||||
opts->no_settle = 1;
|
||||
break;
|
||||
|
||||
case OPT_SITE:
|
||||
/* --site */
|
||||
/* 1. User can specify only site-ids from 0 to 9
|
||||
* 2. only one --site parameter is accepted
|
||||
*/
|
||||
|
||||
if (!is_valid_site(optarg)) {
|
||||
syntax("Unsupported site ID\n");
|
||||
return EXIT_USAGE_ERROR;
|
||||
}
|
||||
|
||||
if (opts->site_id != SITE_FALLBACK) {
|
||||
syntax("Cannot specify '--site' multiple "
|
||||
"times\n");
|
||||
return EXIT_USAGE_ERROR;
|
||||
}
|
||||
|
||||
opts->site_id = atoi(optarg);
|
||||
global_site_id = opts->site_id;
|
||||
opts->persistent = 1;
|
||||
break;
|
||||
|
||||
case ':':
|
||||
/* Missing option argument. */
|
||||
syntax("Option '%s' requires an argument\n",
|
||||
@@ -987,7 +1038,9 @@ static exit_code_t parse_options(struct options *opts, int argc, char *argv[])
|
||||
goto out;
|
||||
|
||||
/* Determine configuration set. */
|
||||
if (!opts->active && !opts->persistent && !opts->auto_conf) {
|
||||
if (opts->deconfigure_all) {
|
||||
opts->config = config_all;
|
||||
} else if (!opts->active && !opts->persistent && !opts->auto_conf) {
|
||||
/* Default configuration targets are active + persistent */
|
||||
opts->config = config_active | config_persistent;
|
||||
} else {
|
||||
@@ -1794,6 +1847,24 @@ static void unblacklist_ranges(struct selected_dev_node *sel,
|
||||
*param_ptr = param;
|
||||
}
|
||||
|
||||
/* Currently the site-specific configurations are supported only
|
||||
* for some device types. Make sure that user get an error message when
|
||||
* try to configure other device-types with site parameter.
|
||||
*/
|
||||
static exit_code_t is_site_supported(struct util_list *selected)
|
||||
{
|
||||
struct selected_dev_node *sel;
|
||||
|
||||
util_list_iterate(selected, sel) {
|
||||
if (sel->dt && !sel->dt->site_support) {
|
||||
error("Site specific configuration is not supported"
|
||||
" for %s devices\n", sel->dt->devname);
|
||||
return EXIT_USAGE_ERROR;
|
||||
}
|
||||
}
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
/* Handle device configuration. */
|
||||
static exit_code_t configure_devices(struct options *opts, int specified,
|
||||
int *found_ptr)
|
||||
@@ -1827,6 +1898,9 @@ static exit_code_t configure_devices(struct options *opts, int specified,
|
||||
error("No device was selected!\n");
|
||||
rc = EXIT_EMPTY_SELECTION;
|
||||
goto out;
|
||||
} else if (opts->site_id != SITE_FALLBACK) {
|
||||
if (is_site_supported(selected))
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Work on selected devices. */
|
||||
@@ -1913,6 +1987,18 @@ static exit_code_t check_in_use(struct device *dev)
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
static bool is_site_modified(struct device *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_USER_SITES; i++) {
|
||||
if (dev->site_specific[i].modified)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Deconfigure specified device. */
|
||||
static exit_code_t deconfigure_one_device(struct subtype *st, const char *id,
|
||||
struct options *opts, int try,
|
||||
@@ -1922,6 +2008,7 @@ static exit_code_t deconfigure_one_device(struct subtype *st, const char *id,
|
||||
exit_code_t rc;
|
||||
struct device *dev;
|
||||
config_t config, read_config;
|
||||
int i, site_available = 0;
|
||||
|
||||
/* Reset processed flag. */
|
||||
if (proc_ptr)
|
||||
@@ -1954,7 +2041,7 @@ static exit_code_t deconfigure_one_device(struct subtype *st, const char *id,
|
||||
dev->processed = 1;
|
||||
|
||||
if (try && !(dev->active.exists || dev->active.definable ||
|
||||
dev->persistent.exists || dev->autoconf.exists)) {
|
||||
is_dev_pers(dev) || dev->autoconf.exists)) {
|
||||
/* Attempt to deconfigure this device will fail. */
|
||||
return EXIT_DEVICE_NOT_FOUND;
|
||||
}
|
||||
@@ -1964,7 +2051,7 @@ static exit_code_t deconfigure_one_device(struct subtype *st, const char *id,
|
||||
* dev. */
|
||||
if (SCOPE_ACTIVE(config) &&
|
||||
!(dev->active.exists || dev->active.definable)) {
|
||||
if (!((SCOPE_PERSISTENT(config) && dev->persistent.exists) ||
|
||||
if (!((SCOPE_PERSISTENT(config) && is_dev_pers(dev)) ||
|
||||
(SCOPE_AUTOCONF(config) && dev->autoconf.exists)))
|
||||
return EXIT_DEVICE_NOT_FOUND;
|
||||
}
|
||||
@@ -1985,16 +2072,48 @@ static exit_code_t deconfigure_one_device(struct subtype *st, const char *id,
|
||||
} else if (subtype_online_get(st, dev, config_active) == 1)
|
||||
dev->active.modified = 1;
|
||||
}
|
||||
if (SCOPE_PERSISTENT(config) && dev->persistent.exists) {
|
||||
dev->persistent.deconfigured = 1;
|
||||
dev->persistent.modified = 1;
|
||||
if (SCOPE_PERSISTENT(config)) {
|
||||
if (dev->persistent.exists) {
|
||||
dev->persistent.deconfigured = 1;
|
||||
dev->persistent.modified = 1;
|
||||
} else if (is_dev_pers(dev) && global_site_id != SITE_FALLBACK) {
|
||||
dev->persistent.deconfigured = 1;
|
||||
dev->persistent.modified = 1;
|
||||
}
|
||||
}
|
||||
if (SCOPE_AUTOCONF(config) && dev->autoconf.exists) {
|
||||
dev->autoconf.deconfigured = 1;
|
||||
dev->autoconf.modified = 1;
|
||||
}
|
||||
|
||||
/* In case of deconfigure_all command, make sure that site specific
|
||||
* settings are also removed.
|
||||
*/
|
||||
if (opts->deconfigure_all) {
|
||||
for (i = 0; i < NUM_USER_SITES; i++) {
|
||||
if (dev->site_specific[i].exists) {
|
||||
site_available++;
|
||||
/* To remove site-specific settings, simply
|
||||
* make the deconfigured = 1
|
||||
*/
|
||||
dev->site_specific[i].deconfigured = 1;
|
||||
dev->site_specific[i].modified = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (site_available) {
|
||||
/* set persistent.exists = 1 to let the
|
||||
* drivers know that we need to modify the
|
||||
* udev-rule
|
||||
*/
|
||||
dev->persistent.exists = 1;
|
||||
dev->persistent.modified = 1;
|
||||
dev->persistent.deconfigured = 1;
|
||||
}
|
||||
|
||||
if (!dev->active.modified && !dev->persistent.modified &&
|
||||
!dev->autoconf.modified)
|
||||
!dev->autoconf.modified && !is_site_modified(dev))
|
||||
return EXIT_OK;
|
||||
|
||||
/* Pre-write check. */
|
||||
@@ -2036,6 +2155,9 @@ static exit_code_t deconfigure_devices(struct options *opts)
|
||||
error("No device was selected!\n");
|
||||
rc = EXIT_EMPTY_SELECTION;
|
||||
goto out;
|
||||
} else if (opts->site_id != SITE_FALLBACK) {
|
||||
if (is_site_supported(selected))
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Work on selected devices. */
|
||||
@@ -2410,7 +2532,7 @@ static exit_code_t do_list_attribs(struct options *opts)
|
||||
remove_duplicate_attribs(attribs, devtype_count_subtypes(dt));
|
||||
|
||||
if (attribs) {
|
||||
table_attribs_show(attribs, 1, 0, dt);
|
||||
table_attribs_show(attribs, 1, 0, 0, dt);
|
||||
ptrlist_free(attribs, 1);
|
||||
} else
|
||||
rc = EXIT_ATTRIB_NOT_FOUND;
|
||||
@@ -2558,8 +2680,13 @@ static void action_note(const char *msg, config_t config)
|
||||
{
|
||||
if (config == config_active)
|
||||
info("%s the active configuration only\n", msg);
|
||||
else if (config == config_persistent)
|
||||
info("%s the persistent configuration only\n", msg);
|
||||
else if (config == config_persistent) {
|
||||
if (global_site_id == SITE_FALLBACK)
|
||||
info("%s the persistent configuration only\n", msg);
|
||||
else
|
||||
info("%s the site %d configuration only\n", msg,
|
||||
global_site_id);
|
||||
}
|
||||
else if (config == config_autoconf)
|
||||
info("%s the auto-configuration only\n", msg);
|
||||
}
|
||||
@@ -3034,7 +3161,7 @@ int main(int argc, char *argv[])
|
||||
!dryrun) {
|
||||
/* If the root device/device type or early devices have been
|
||||
* modified, additional work might be necessary. */
|
||||
rc = initrd_check(ZDEV_ALWAYS_UPDATE_INITRD);
|
||||
rc = initrd_check(ZDEV_ALWAYS_UPDATE_INITRD, opts.deconfigure_all);
|
||||
if (rc && !drc)
|
||||
drc = rc;
|
||||
}
|
||||
|
||||
@@ -33,7 +33,8 @@ SETTINGS
|
||||
|
||||
ACTIONS
|
||||
-e, --enable Enable device
|
||||
-d, --disable Disable device
|
||||
-d, --disable Disable specified setting of the device
|
||||
-D, --disable-all Disable all the settings of the device
|
||||
-l, --list-attributes List attributes
|
||||
-L, --list-types List supported device types
|
||||
-H, --help-attribute Show detailed help on specified attribute
|
||||
@@ -56,5 +57,6 @@ OPTIONS
|
||||
--base PATH Use PATH as base for accessing files
|
||||
--no-settle Do not wait for udev to settle
|
||||
--auto-conf Apply changes to auto-configuration only
|
||||
-s, --site ID Apply changes to the specified site only
|
||||
-V, --verbose Print additional run-time information
|
||||
-q, --quiet Print only minimal run-time information
|
||||
|
||||
@@ -137,6 +137,21 @@ static struct attrib dasd_attr_readonly = {
|
||||
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)),
|
||||
};
|
||||
|
||||
static struct attrib dasd_attr_copy_pair = {
|
||||
.name = "copy_pair",
|
||||
.title = "Modify copy-pair relations",
|
||||
.desc = "Make a copy-pair relation for this device known to the DASD "
|
||||
"driver.\n"
|
||||
"A copy-pair is a comma-separated pair of device bus-IDs\n"
|
||||
"<primary>,<secondary>.\n"
|
||||
"Example: 0.0.1000,0.0.2000\n"
|
||||
"Up to 4 copy-pairs are accepted by the DASD driver for each "
|
||||
"device.\n",
|
||||
.unstable = 1,
|
||||
.multi = 1,
|
||||
.activerem = 1,
|
||||
};
|
||||
|
||||
static struct attrib dasd_attr_erplog = {
|
||||
.name = "erplog",
|
||||
.title = "Enable logging of Error Recovery Processing",
|
||||
@@ -600,6 +615,81 @@ persistent:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Remove all entries from the copy_pair attribute by writing 'clear' to it. */
|
||||
static void dasd_clear_copy_pair(struct setting *s)
|
||||
{
|
||||
free(s->value);
|
||||
s->value = misc_strdup("clear");
|
||||
strlist_free(s->values);
|
||||
s->values = strlist_new();
|
||||
strlist_add(s->values, "clear");
|
||||
s->removed = 0;
|
||||
s->modified = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy-pair values may contain multiple values in one line
|
||||
* those need to be split up and put in multiple values entries
|
||||
* delimiter is " " and "\n" depending on the source
|
||||
* values read from sysfs have " " as delimiter and values from
|
||||
* zdev have "\n"
|
||||
*/
|
||||
static void dasd_split_copy_pair(struct setting *s)
|
||||
{
|
||||
struct util_list *new = NULL;
|
||||
struct strlist_node *node;
|
||||
|
||||
new = strlist_new();
|
||||
util_list_iterate(s->values, node)
|
||||
strlist_add_multi(new, node->str, "\n ", 0);
|
||||
|
||||
strlist_free(s->values);
|
||||
s->values = new;
|
||||
}
|
||||
|
||||
/*
|
||||
* For persistent configurations one value per values[] entry is required
|
||||
* to achieve a correct multi-line copy-pair statement in the resulting
|
||||
* udev-rule.
|
||||
*/
|
||||
static exit_code_t dasd_st_configure_persistent(struct subtype *st,
|
||||
struct device *dev)
|
||||
{
|
||||
struct setting *s;
|
||||
|
||||
util_list_iterate(&dev->persistent.settings->list, s) {
|
||||
if (strcmp(s->name, "copy_pair") == 0)
|
||||
dasd_split_copy_pair(s);
|
||||
}
|
||||
|
||||
return st->super->configure_persistent(st, dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* For active configurations one value per values[] entry is required to
|
||||
* correctly operate the sysfs attribute.
|
||||
*
|
||||
* In case of removal the string "clear" has to be written to the
|
||||
* value and values[] entry.
|
||||
*/
|
||||
static exit_code_t dasd_st_configure_active(struct subtype *st,
|
||||
struct device *dev)
|
||||
{
|
||||
struct setting *s;
|
||||
|
||||
util_list_iterate(&dev->active.settings->list, s) {
|
||||
if (strcmp(s->name, "copy_pair") != 0)
|
||||
continue;
|
||||
|
||||
if (s->removed)
|
||||
dasd_clear_copy_pair(s);
|
||||
else
|
||||
dasd_split_copy_pair(s);
|
||||
}
|
||||
|
||||
return st->super->configure_active(st, dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* DASD device sub-types.
|
||||
*/
|
||||
@@ -634,10 +724,13 @@ struct subtype dasd_subtype_eckd = {
|
||||
&dasd_attr_last_known_reservation_state,
|
||||
&dasd_attr_safe_offline,
|
||||
&dasd_attr_fc_security,
|
||||
&dasd_attr_copy_pair,
|
||||
&internal_attr_early,
|
||||
),
|
||||
.unknown_dev_attribs = 1,
|
||||
|
||||
.configure_active = &dasd_st_configure_active,
|
||||
.configure_persistent = &dasd_st_configure_persistent,
|
||||
.check_pre_configure = &dasd_st_check_pre_configure,
|
||||
.add_modules = &dasd_st_add_modules,
|
||||
};
|
||||
@@ -688,6 +781,7 @@ struct devtype dasd_devtype = {
|
||||
"(DASDs)",
|
||||
.devname = "DASD",
|
||||
.modules = STRING_ARRAY(DASD_MOD_NAME),
|
||||
.site_support = 1,
|
||||
|
||||
.subtypes = SUBTYPE_ARRAY(
|
||||
&dasd_subtype_eckd,
|
||||
|
||||
@@ -29,6 +29,7 @@ struct device *device_new(struct subtype *st, const char *id)
|
||||
{
|
||||
struct device *dev;
|
||||
struct namespace *ns = st->namespace;
|
||||
int i;
|
||||
|
||||
dev = misc_malloc(sizeof(struct device));
|
||||
dev->subtype = st;
|
||||
@@ -44,12 +45,17 @@ struct device *device_new(struct subtype *st, const char *id)
|
||||
dev->persistent.settings = setting_list_new();
|
||||
dev->autoconf.settings = setting_list_new();
|
||||
|
||||
for (i = 0; i < NUM_SITES; i++)
|
||||
dev->site_specific[i].settings = setting_list_new();
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
/* Release all resources associated with the specified device. */
|
||||
void device_free(struct device *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!dev)
|
||||
return;
|
||||
free(dev->id);
|
||||
@@ -57,12 +63,18 @@ void device_free(struct device *dev)
|
||||
setting_list_free(dev->active.settings);
|
||||
setting_list_free(dev->persistent.settings);
|
||||
setting_list_free(dev->autoconf.settings);
|
||||
|
||||
for (i = 0; i < NUM_SITES; i++)
|
||||
setting_list_free(dev->site_specific[i].settings);
|
||||
|
||||
free(dev);
|
||||
}
|
||||
|
||||
/* Used for debugging. */
|
||||
void device_print(struct device *dev, int level)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("%*sdevice at %p:\n", level, "", (void *) dev);
|
||||
if (!dev)
|
||||
return;
|
||||
@@ -96,6 +108,36 @@ void device_print(struct device *dev, int level)
|
||||
setting_list_print(dev->autoconf.settings, level + 8);
|
||||
else
|
||||
printf("%*s<none>\n", level + 8, "");
|
||||
|
||||
printf("%*spersistent-site-specific:\n", level + 4, "");
|
||||
for (i = 0; i < NUM_SITES; i++) {
|
||||
printf("%*sexists=%d mod=%d deconf=%d\n",
|
||||
level + 8, "", dev->site_specific[i].exists,
|
||||
dev->site_specific[i].modified,
|
||||
dev->site_specific[i].deconfigured);
|
||||
if (dev->site_specific[i].settings)
|
||||
setting_list_print(dev->site_specific[i].settings, level + 8);
|
||||
else
|
||||
printf("%*s<none>\n", level + 8, "");
|
||||
}
|
||||
}
|
||||
|
||||
char *device_get_sites(struct device *dev)
|
||||
{
|
||||
struct util_list *names;
|
||||
char *str;
|
||||
int i;
|
||||
|
||||
names = strlist_new();
|
||||
for (i = 0; i < NUM_USER_SITES; i++) {
|
||||
if (dev->site_specific[i].exists)
|
||||
strlist_add(names, "%d", i);
|
||||
}
|
||||
|
||||
str = strlist_flatten(names, ",");
|
||||
strlist_free(names);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static const void *device_hash_get_id(void *dev_ptr)
|
||||
@@ -627,10 +669,16 @@ exit_code_t device_check_settings(struct device *dev, config_t config,
|
||||
}
|
||||
|
||||
struct setting_list *device_get_setting_list(struct device *dev,
|
||||
config_t config)
|
||||
config_t config,
|
||||
int site_id)
|
||||
{
|
||||
struct setting_list *settings = NULL;
|
||||
|
||||
if (site_id < SITE_FALLBACK) {
|
||||
settings = dev->site_specific[site_id].settings;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (config == config_active)
|
||||
settings = dev->active.settings;
|
||||
else if (config == config_persistent)
|
||||
@@ -638,6 +686,7 @@ struct setting_list *device_get_setting_list(struct device *dev,
|
||||
else if (config == config_autoconf)
|
||||
settings = dev->autoconf.settings;
|
||||
|
||||
out:
|
||||
return settings;
|
||||
}
|
||||
|
||||
@@ -657,3 +706,21 @@ config_t device_get_config(struct device *dev)
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/* Determine if persistent configuration available for any of the sites*/
|
||||
bool is_dev_pers(struct device *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (global_site_id != SITE_FALLBACK &&
|
||||
!dev->site_specific[global_site_id].exists)
|
||||
return false;
|
||||
else if (dev->persistent.exists)
|
||||
return true;
|
||||
|
||||
for (i = 0; i < NUM_SITES; i++)
|
||||
if (dev->site_specific[i].exists)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include "setting.h"
|
||||
#include "subtype.h"
|
||||
|
||||
static int import_site_id = SITE_FALLBACK;
|
||||
|
||||
struct export_header {
|
||||
export_t type;
|
||||
config_t config;
|
||||
@@ -28,13 +30,28 @@ struct export_header {
|
||||
char *id;
|
||||
};
|
||||
|
||||
static bool header_str_to_config(const char *config_str, config_t *config)
|
||||
{
|
||||
int site;
|
||||
|
||||
if (strstr(config_str, "site")) {
|
||||
if (sscanf(config_str, "site%d", &site) == 1) {
|
||||
import_site_id = site;
|
||||
*config = config_persistent;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
import_site_id = SITE_FALLBACK;
|
||||
return str_to_config(config_str, config);
|
||||
}
|
||||
|
||||
static struct export_header *header_new(const char *config_str,
|
||||
const char *type, const char *id)
|
||||
{
|
||||
config_t config;
|
||||
struct export_header *hdr;
|
||||
|
||||
if (!str_to_config(config_str, &config))
|
||||
if (!header_str_to_config(config_str, &config))
|
||||
return NULL;
|
||||
hdr = misc_malloc(sizeof(struct export_header));
|
||||
if (!id)
|
||||
@@ -130,6 +147,14 @@ static void export_write_comment(FILE *fd)
|
||||
fprintf(fd, "# Generated by chzdev\n");
|
||||
}
|
||||
|
||||
static const char *site_specific_header(config_t config)
|
||||
{
|
||||
if (global_site_id != SITE_FALLBACK)
|
||||
return misc_asprintf("site%d", global_site_id);
|
||||
else
|
||||
return config_to_str(config);
|
||||
}
|
||||
|
||||
/* Write an export file header. */
|
||||
static void write_header(FILE *fd, config_t config, const char *type,
|
||||
const char *id, int *first_ptr)
|
||||
@@ -143,9 +168,9 @@ static void write_header(FILE *fd, config_t config, const char *type,
|
||||
}
|
||||
|
||||
if (id)
|
||||
fprintf(fd, "[%s %s %s]\n", config_to_str(config), type, id);
|
||||
fprintf(fd, "[%s %s %s]\n", site_specific_header(config), type, id);
|
||||
else
|
||||
fprintf(fd, "[%s %s]\n", config_to_str(config), type);
|
||||
fprintf(fd, "[%s %s]\n", site_specific_header(config), type);
|
||||
}
|
||||
|
||||
static int count_exportable(struct device *dev, config_t config)
|
||||
@@ -154,7 +179,7 @@ static int count_exportable(struct device *dev, config_t config)
|
||||
struct setting *s;
|
||||
int count;
|
||||
|
||||
list = device_get_setting_list(dev, config);
|
||||
list = device_get_setting_list(dev, config, global_site_id);
|
||||
if (!list)
|
||||
return 0;
|
||||
count = 0;
|
||||
@@ -408,7 +433,11 @@ static exit_code_t handle_header(const char *filename, int lineno,
|
||||
dev->active.exists = 1;
|
||||
}
|
||||
if (SCOPE_PERSISTENT(hdr->config)) {
|
||||
setting_list_clear(dev->persistent.settings);
|
||||
if (import_site_id != SITE_FALLBACK)
|
||||
setting_list_clear(dev->site_specific[import_site_id].settings);
|
||||
else
|
||||
setting_list_clear(dev->persistent.settings);
|
||||
|
||||
dev->persistent.exists = 1;
|
||||
}
|
||||
if (SCOPE_AUTOCONF(hdr->config)) {
|
||||
@@ -462,7 +491,7 @@ static exit_code_t handle_setting(const char *filename, int lineno,
|
||||
} else if (dev) {
|
||||
/* We're inside a device section. */
|
||||
attribs = dev->subtype->dev_attribs;
|
||||
list = device_get_setting_list(dev, config);
|
||||
list = device_get_setting_list(dev, config, SITE_FALLBACK);
|
||||
} else
|
||||
return EXIT_OK;
|
||||
|
||||
@@ -502,7 +531,8 @@ static bool empty_device(struct device *dev)
|
||||
return false;
|
||||
if (util_list_is_empty(&dev->active.settings->list) &&
|
||||
util_list_is_empty(&dev->persistent.settings->list) &&
|
||||
util_list_is_empty(&dev->autoconf.settings->list))
|
||||
util_list_is_empty(&dev->autoconf.settings->list) &&
|
||||
import_site_id == SITE_FALLBACK)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@@ -574,8 +604,9 @@ exit_code_t export_read(FILE *fd, const char *filename,
|
||||
ptrlist_add(objects, object_new(export_devtype,
|
||||
dt));
|
||||
} else if (dev) {
|
||||
ptrlist_add(objects, object_new(export_device,
|
||||
dev));
|
||||
if (global_site_id == import_site_id)
|
||||
ptrlist_add(objects, object_new(export_device,
|
||||
dev));
|
||||
}
|
||||
} else if (parse_setting(l, &key, &value)) {
|
||||
/* Found <key>=<value>. */
|
||||
@@ -584,8 +615,10 @@ exit_code_t export_read(FILE *fd, const char *filename,
|
||||
"valid section\n");
|
||||
rc = EXIT_FORMAT_ERROR;
|
||||
} else {
|
||||
rc = handle_setting(filename, lineno, key,
|
||||
value, dt, dev, config);
|
||||
if (global_site_id == import_site_id ||
|
||||
import_site_id == SITE_FALLBACK)
|
||||
rc = handle_setting(filename, lineno, key,
|
||||
value, dt, dev, config);
|
||||
}
|
||||
free(key);
|
||||
free(value);
|
||||
|
||||
@@ -348,7 +348,7 @@ static void _add_setting(const char *filename, struct device *dev,
|
||||
struct attrib *a;
|
||||
struct setting_list *list;
|
||||
|
||||
list = device_get_setting_list(dev, config);
|
||||
list = device_get_setting_list(dev, config, SITE_FALLBACK);
|
||||
a = attrib_find(dev->subtype->dev_attribs, key);
|
||||
if (!a) {
|
||||
warnx("%s: Applying unknown device setting %s=%s", filename,
|
||||
|
||||
@@ -32,6 +32,12 @@
|
||||
#include "table.h"
|
||||
#include "table_types.h"
|
||||
|
||||
/* current site-id in action. By default, operations are always on fallback
|
||||
* site; We need the current site-id as global variable to avoid excessive
|
||||
* diff of simple function parameter modifications to pass this value.
|
||||
*/
|
||||
int global_site_id = SITE_FALLBACK;
|
||||
|
||||
/* Main program action. */
|
||||
typedef enum {
|
||||
ACT_LIST,
|
||||
@@ -64,8 +70,10 @@ struct options {
|
||||
unsigned int no_headings:1;
|
||||
struct util_list *base; /* List of struct strlist_node */
|
||||
unsigned int pairs:1;
|
||||
unsigned int shell:1;
|
||||
unsigned int verbose:1;
|
||||
unsigned int quiet:1;
|
||||
unsigned int site_id;
|
||||
};
|
||||
|
||||
/* Makefile converts lszdev_usage.txt into C file which we include here. */
|
||||
@@ -99,7 +107,9 @@ enum {
|
||||
OPT_VERBOSE = 'V',
|
||||
OPT_QUIET = 'q',
|
||||
OPT_PAIRS = 'P',
|
||||
OPT_SHELL = (OPT_ANONYMOUS_BASE+__COUNTER__),
|
||||
OPT_AUTO_CONF = (OPT_ANONYMOUS_BASE+__COUNTER__),
|
||||
OPT_SITE = 's',
|
||||
};
|
||||
|
||||
static struct opts_conflict conflict_list[] = {
|
||||
@@ -120,6 +130,8 @@ static struct opts_conflict conflict_list[] = {
|
||||
OPT_CONFIGURED, OPT_EXISTING, OPT_ONLINE, OPT_OFFLINE,
|
||||
OPT_BY_PATH, OPT_BY_NODE, OPT_BY_INTERFACE, OPT_FAILED,
|
||||
0),
|
||||
OPTS_CONFLICT(OPT_SITE,
|
||||
OPT_TYPE),
|
||||
OPTS_CONFLICT(OPT_ONLINE,
|
||||
OPT_OFFLINE),
|
||||
OPTS_CONFLICT(OPT_QUIET,
|
||||
@@ -157,13 +169,15 @@ static const struct option opt_list[] = {
|
||||
{ "no-headings", no_argument, NULL, OPT_NO_HEADINGS },
|
||||
{ "base", required_argument, NULL, OPT_BASE },
|
||||
{ "pairs", no_argument, NULL, OPT_PAIRS },
|
||||
{ "shell", no_argument, NULL, OPT_SHELL },
|
||||
{ "verbose", no_argument, NULL, OPT_VERBOSE },
|
||||
{ "quiet", no_argument, NULL, OPT_QUIET },
|
||||
{ "site", required_argument, NULL, OPT_SITE },
|
||||
{ NULL, no_argument, NULL, 0 },
|
||||
};
|
||||
|
||||
/* Command line abbreviations. */
|
||||
static const char opt_str[] = ":tilLhvapPc:nVq";
|
||||
static const char opt_str[] = ":tilLhvapPc:nVqs:";
|
||||
|
||||
/* Initialize options data structure. */
|
||||
static void init_options(struct options *opts)
|
||||
@@ -172,6 +186,8 @@ static void init_options(struct options *opts)
|
||||
opts->select = select_opts_new();
|
||||
opts->columns = strlist_new();
|
||||
opts->base = strlist_new();
|
||||
/* Default operations are on fallback site*/
|
||||
opts->site_id = SITE_FALLBACK;
|
||||
}
|
||||
|
||||
/* Release memory used in options data structure. */
|
||||
@@ -212,6 +228,41 @@ static action_t get_action(struct options *opts)
|
||||
return ACT_LIST;
|
||||
}
|
||||
|
||||
/* Determine the site information from the device */
|
||||
static char *get_site_from_pers(struct device *dev)
|
||||
{
|
||||
int site, i, num = 0;
|
||||
|
||||
for (i = 0; i < NUM_SITES; i++) {
|
||||
if (dev->site_specific[i].exists) {
|
||||
site = i;
|
||||
num++;
|
||||
if (site == global_site_id && site != SITE_FALLBACK)
|
||||
return misc_asprintf("s%d", site);
|
||||
}
|
||||
}
|
||||
|
||||
/* Incase of devices which do not have --site support, all the
|
||||
* read configurations will be in the dev->persistent and not
|
||||
* in dev->site_specific.
|
||||
*/
|
||||
if (num == 0 && dev->persistent.exists == 1 &&
|
||||
global_site_id == SITE_FALLBACK)
|
||||
return misc_asprintf("yes");
|
||||
|
||||
/* Found more site-specific information ? return s+ */
|
||||
if (num > 1)
|
||||
return misc_asprintf("s+");
|
||||
|
||||
if (num == 1)
|
||||
if (site == SITE_FALLBACK)
|
||||
return misc_asprintf("yes");
|
||||
else
|
||||
return misc_asprintf("s%d", site);
|
||||
else
|
||||
return misc_asprintf("no");
|
||||
}
|
||||
|
||||
/* Check options data structure for syntax errors. */
|
||||
static exit_code_t check_options(struct options *opts,
|
||||
int specified[OPTS_MAX + 1], int op)
|
||||
@@ -651,6 +702,11 @@ static exit_code_t parse_options(struct options *opts, int argc, char *argv[])
|
||||
opts->pairs = 1;
|
||||
break;
|
||||
|
||||
case OPT_SHELL:
|
||||
/* --shell */
|
||||
opts->shell = 1;
|
||||
break;
|
||||
|
||||
case OPT_VERBOSE:
|
||||
/* --verbose */
|
||||
opts->verbose = 1;
|
||||
@@ -661,6 +717,27 @@ static exit_code_t parse_options(struct options *opts, int argc, char *argv[])
|
||||
opts->quiet = 1;
|
||||
break;
|
||||
|
||||
case OPT_SITE:
|
||||
/* --site */
|
||||
/* User can specify only site-ids from 0 to 9 */
|
||||
if (!is_valid_site(optarg)) {
|
||||
syntax("Unsupported site ID\n");
|
||||
return EXIT_USAGE_ERROR;
|
||||
}
|
||||
|
||||
if (opts->site_id != SITE_FALLBACK) {
|
||||
syntax("Cannot specify '--site' multiple "
|
||||
"times\n");
|
||||
return EXIT_USAGE_ERROR;
|
||||
}
|
||||
|
||||
/* site information is a persistent configuration.
|
||||
* set opts->persistent here.
|
||||
*/
|
||||
opts->persistent = 1;
|
||||
opts->site_id = atoi(optarg);
|
||||
break;
|
||||
|
||||
case ':':
|
||||
/* Missing option argument. */
|
||||
syntax("Option '%s' requires an argument\n",
|
||||
@@ -692,6 +769,13 @@ static exit_code_t parse_options(struct options *opts, int argc, char *argv[])
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
/* check whether --pairs and --columns is used */
|
||||
if (opts->shell == 1 && (opts->pairs != 1 || util_list_is_empty(opts->columns))) {
|
||||
syntax("'--shell' must be used together with "
|
||||
"'--pairs' and '--columns'\n");
|
||||
return EXIT_USAGE_ERROR;
|
||||
}
|
||||
|
||||
/* Determine configuration set. */
|
||||
if (!opts->active && !opts->persistent && !opts->auto_conf) {
|
||||
/* Default display targets are active + persistent - note that
|
||||
@@ -718,7 +802,8 @@ static exit_code_t parse_options(struct options *opts, int argc, char *argv[])
|
||||
opts->select->all = 1;
|
||||
else if (opts->config == config_active)
|
||||
opts->select->existing = 1;
|
||||
else if (opts->config == config_persistent)
|
||||
else if (opts->config == config_persistent ||
|
||||
opts->site_id != SITE_FALLBACK)
|
||||
opts->select->configured = 1;
|
||||
}
|
||||
break;
|
||||
@@ -782,34 +867,53 @@ static struct column *dev_table = COLUMN_ARRAY(
|
||||
);
|
||||
|
||||
static char *merge_str(const char *act, const char *pers, const char *ac,
|
||||
config_t config)
|
||||
config_t config, char **site, int max_sites)
|
||||
{
|
||||
char *str;
|
||||
size_t len;
|
||||
int i;
|
||||
struct util_list *str_list;
|
||||
|
||||
str_list = strlist_new();
|
||||
|
||||
act = act ? act : "-";
|
||||
pers = pers ? pers : "-";
|
||||
ac = ac ? ac : "-";
|
||||
|
||||
if (strcmp(act, pers) == 0 && strcmp(act, ac) == 0)
|
||||
if (strcmp(act, pers) == 0 && strcmp(act, ac) == 0 &&
|
||||
max_sites == 1)
|
||||
return misc_strdup(act);
|
||||
|
||||
len = strlen(act) + strlen(pers) + strlen(ac) + /* 3 * / + NUL */ 4;
|
||||
str = misc_malloc(len);
|
||||
|
||||
if (SCOPE_ACTIVE(config))
|
||||
strcat(str, act);
|
||||
strlist_add(str_list, act);
|
||||
if (SCOPE_PERSISTENT(config)) {
|
||||
if (*str)
|
||||
strcat(str, "/");
|
||||
strcat(str, pers);
|
||||
if (global_site_id == SITE_FALLBACK) {
|
||||
strlist_add(str_list, pers);
|
||||
} else {
|
||||
strlist_add(str_list, site[global_site_id]);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (SCOPE_AUTOCONF(config)) {
|
||||
if (*str)
|
||||
strcat(str, "/");
|
||||
strcat(str, ac);
|
||||
strlist_add(str_list, ac);
|
||||
}
|
||||
|
||||
/* Do not show site-specific information when user specifically
|
||||
* asked for persistent configuration setting.
|
||||
*/
|
||||
if (SCOPE_PERSISTENT(config) && config != config_persistent) {
|
||||
for (i = 0; i <= max_sites; i++) {
|
||||
if (site[i])
|
||||
strlist_add(str_list, site[i]);
|
||||
else
|
||||
if (str_list)
|
||||
strlist_add(str_list, "-");
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
str = strlist_flatten(str_list, "/");
|
||||
strlist_free(str_list);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
@@ -848,12 +952,13 @@ static char *dev_table_get_modules(struct device *dev)
|
||||
return str;
|
||||
}
|
||||
|
||||
static char *get_attr(struct device *dev, const char *name, config_t config)
|
||||
static char *get_attr(struct device *dev, const char *name, config_t config,
|
||||
int site_id)
|
||||
{
|
||||
struct setting_list *list;
|
||||
struct setting *s;
|
||||
|
||||
list = device_get_setting_list(dev, config);
|
||||
list = device_get_setting_list(dev, config, site_id);
|
||||
if (!list)
|
||||
return NULL;
|
||||
s = setting_list_find(list, name);
|
||||
@@ -868,30 +973,61 @@ static char *get_attr(struct device *dev, const char *name, config_t config)
|
||||
return misc_strdup(s->value);
|
||||
}
|
||||
|
||||
/* When lszdev --info, make sure that the output contains only the
|
||||
* valid site information. Check if the site is valid and then add
|
||||
* the new SITEn column. For SITE_FALLBACK, the PERSISTENT column will
|
||||
* be used.
|
||||
*/
|
||||
static bool is_site_show(int site_id, int current_site)
|
||||
{
|
||||
if (site_id == SITE_FALLBACK || site_id == current_site)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return string for attribute with specified @name for device @dev. */
|
||||
static char *dev_table_get_attr(struct device *dev, const char *attr,
|
||||
config_t config)
|
||||
{
|
||||
char *act = NULL, *pers = NULL, *ac = NULL, *str;
|
||||
const char *name;
|
||||
char *site[NUM_USER_SITES];
|
||||
int i;
|
||||
static int max_sites;
|
||||
|
||||
name = strchr(attr, ':');
|
||||
if (!name)
|
||||
return NULL;
|
||||
name++;
|
||||
|
||||
/* To get the default settings on any configuration, make sure that
|
||||
* the site_id is specified as SITE_FALLBACK. Any value of site_id
|
||||
* less than SITE_FALLBACK will endup providing site-specific attribute
|
||||
* settings.
|
||||
*/
|
||||
if (SCOPE_ACTIVE(config))
|
||||
act = get_attr(dev, name, config_active);
|
||||
if (SCOPE_PERSISTENT(config))
|
||||
pers = get_attr(dev, name, config_persistent);
|
||||
act = get_attr(dev, name, config_active, SITE_FALLBACK);
|
||||
if (SCOPE_PERSISTENT(config)) {
|
||||
pers = get_attr(dev, name, config_persistent, SITE_FALLBACK);
|
||||
for (i = 0; i < NUM_USER_SITES; i++)
|
||||
site[i] = get_attr(dev, name, config, i);
|
||||
}
|
||||
if (SCOPE_AUTOCONF(config))
|
||||
ac = get_attr(dev, name, config_autoconf);
|
||||
ac = get_attr(dev, name, config_autoconf, SITE_FALLBACK);
|
||||
|
||||
str = merge_str(act, pers, ac, config);
|
||||
for (i = 0; i < NUM_USER_SITES; i++)
|
||||
if (dev->site_specific[i].exists)
|
||||
max_sites = (max_sites < i) ? i : max_sites;
|
||||
|
||||
str = merge_str(act, pers, ac, config, site, max_sites);
|
||||
free(act);
|
||||
free(pers);
|
||||
free(ac);
|
||||
|
||||
if (SCOPE_PERSISTENT(config))
|
||||
for (i = 0; i < NUM_USER_SITES; i++)
|
||||
free(site[i]);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
@@ -938,9 +1074,9 @@ static char *dev_table_get_value(void *item, int id, const char *heading,
|
||||
return misc_strdup(YESNO(dev->active.exists ||
|
||||
dev->active.definable));
|
||||
case dev_pers:
|
||||
if (!dev->persistent.exists && dev->autoconf.exists)
|
||||
if (!is_dev_pers(dev) && dev->autoconf.exists)
|
||||
return misc_strdup("auto");
|
||||
return misc_strdup(YESNO(dev->persistent.exists));
|
||||
return get_site_from_pers(dev);
|
||||
case dev_auto:
|
||||
return misc_strdup(YESNO(dev->autoconf.exists));
|
||||
case dev_online:
|
||||
@@ -1032,11 +1168,17 @@ static struct util_list *dev_table_build(struct options *opts,
|
||||
|
||||
/* Only process existing devices. */
|
||||
active = dev->active.exists || dev->active.definable;
|
||||
persistent = dev->persistent.exists;
|
||||
persistent = (int)is_dev_pers(dev);
|
||||
autoconf = dev->autoconf.exists;
|
||||
if (!active && !persistent && !autoconf)
|
||||
continue;
|
||||
|
||||
/* with --site option, we consider only the persistent
|
||||
* configurations available on the specified site-id.
|
||||
* ignore active and autoconf here
|
||||
*/
|
||||
if (!persistent && global_site_id != SITE_FALLBACK)
|
||||
continue;
|
||||
ptrlist_add(devices, dev);
|
||||
}
|
||||
|
||||
@@ -1082,7 +1224,7 @@ static exit_code_t do_list_devices(struct options *opts)
|
||||
/* Display table. */
|
||||
rc = table_print(dev_table, dev_table_get_value, opts, items,
|
||||
opts->columns, !opts->no_headings, opts->pairs, 0,
|
||||
util_list_is_empty(opts->columns));
|
||||
util_list_is_empty(opts->columns), opts->shell);
|
||||
ptrlist_free(items, 0);
|
||||
|
||||
return rc;
|
||||
@@ -1107,6 +1249,16 @@ enum settings_table_id {
|
||||
settings_active,
|
||||
settings_persistent,
|
||||
settings_autoconf,
|
||||
settings_site0,
|
||||
settings_site1,
|
||||
settings_site2,
|
||||
settings_site3,
|
||||
settings_site4,
|
||||
settings_site5,
|
||||
settings_site6,
|
||||
settings_site7,
|
||||
settings_site8,
|
||||
settings_site9,
|
||||
};
|
||||
|
||||
static struct column *settings_table = COLUMN_ARRAY(
|
||||
@@ -1114,17 +1266,29 @@ static struct column *settings_table = COLUMN_ARRAY(
|
||||
COLUMN("READONLY", align_left, settings_readonly, 1, ""),
|
||||
COLUMN("ACTIVE", align_left, settings_active, 1, ""),
|
||||
COLUMN("PERSISTENT", align_left, settings_persistent, 1, ""),
|
||||
COLUMN("AUTOCONF", align_left, settings_autoconf, 0, "")
|
||||
COLUMN("AUTOCONF", align_left, settings_autoconf, 0, ""),
|
||||
COLUMN("SITE0", align_left, settings_site0, 0, ""),
|
||||
COLUMN("SITE1", align_left, settings_site1, 0, ""),
|
||||
COLUMN("SITE2", align_left, settings_site2, 0, ""),
|
||||
COLUMN("SITE3", align_left, settings_site3, 0, ""),
|
||||
COLUMN("SITE4", align_left, settings_site4, 0, ""),
|
||||
COLUMN("SITE5", align_left, settings_site5, 0, ""),
|
||||
COLUMN("SITE6", align_left, settings_site6, 0, ""),
|
||||
COLUMN("SITE7", align_left, settings_site7, 0, ""),
|
||||
COLUMN("SITE8", align_left, settings_site8, 0, ""),
|
||||
COLUMN("SITE9", align_left, settings_site9, 0, "")
|
||||
);
|
||||
|
||||
static struct util_list *settings_table_build(struct setting_list *active,
|
||||
struct setting_list *persistent,
|
||||
struct setting_list *autoconf,
|
||||
struct device *dev,
|
||||
bool readonly)
|
||||
{
|
||||
struct util_list *names, *items;
|
||||
struct setting *s;
|
||||
struct strlist_node *str;
|
||||
int i;
|
||||
|
||||
/* Get a list of all configured attributes. */
|
||||
names = strlist_new();
|
||||
@@ -1139,6 +1303,11 @@ static struct util_list *settings_table_build(struct setting_list *active,
|
||||
if (persistent && !readonly) {
|
||||
util_list_iterate(&persistent->list, s)
|
||||
strlist_add(names, s->name);
|
||||
if (dev) {
|
||||
for (i = 0; i < NUM_SITES; i++)
|
||||
util_list_iterate(&dev->site_specific[i].settings->list, s)
|
||||
strlist_add(names, s->name);
|
||||
}
|
||||
}
|
||||
if (autoconf && !readonly) {
|
||||
util_list_iterate(&autoconf->list, s)
|
||||
@@ -1159,6 +1328,7 @@ struct settings_table_data {
|
||||
struct setting_list *active;
|
||||
struct setting_list *persistent;
|
||||
struct setting_list *autoconf;
|
||||
struct setting_list *sites[NUM_SITES];
|
||||
int pairs;
|
||||
bool readonly;
|
||||
};
|
||||
@@ -1184,6 +1354,37 @@ static char *settings_table_get_value(void *item, int id, const char *heading,
|
||||
case settings_autoconf:
|
||||
list = stdata->autoconf;
|
||||
break;
|
||||
case settings_site0:
|
||||
list = stdata->sites[0];
|
||||
break;
|
||||
case settings_site1:
|
||||
list = stdata->sites[1];
|
||||
break;
|
||||
case settings_site2:
|
||||
list = stdata->sites[2];
|
||||
break;
|
||||
case settings_site3:
|
||||
list = stdata->sites[3];
|
||||
break;
|
||||
case settings_site4:
|
||||
list = stdata->sites[4];
|
||||
break;
|
||||
case settings_site5:
|
||||
list = stdata->sites[5];
|
||||
break;
|
||||
case settings_site6:
|
||||
list = stdata->sites[6];
|
||||
break;
|
||||
case settings_site7:
|
||||
list = stdata->sites[7];
|
||||
break;
|
||||
case settings_site8:
|
||||
list = stdata->sites[8];
|
||||
break;
|
||||
case settings_site9:
|
||||
list = stdata->sites[9];
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1208,16 +1409,18 @@ static char *settings_table_get_value(void *item, int id, const char *heading,
|
||||
static void settings_table_print(struct setting_list *active,
|
||||
struct setting_list *persistent,
|
||||
struct setting_list *autoconf,
|
||||
struct device *dev,
|
||||
struct options *opts, int ind, bool readonly,
|
||||
bool neednl)
|
||||
{
|
||||
struct util_list *items;
|
||||
struct settings_table_data data;
|
||||
int i;
|
||||
|
||||
items = settings_table_build(
|
||||
SCOPE_ACTIVE(opts->config) ? active : NULL,
|
||||
SCOPE_PERSISTENT(opts->config) ? persistent : NULL,
|
||||
autoconf, readonly);
|
||||
autoconf, dev, readonly);
|
||||
if (util_list_is_empty(items)) {
|
||||
if (!opts->pairs && !readonly)
|
||||
indent(ind, "%sNo settings found\n",
|
||||
@@ -1232,16 +1435,30 @@ static void settings_table_print(struct setting_list *active,
|
||||
table_set_default(settings_table, settings_active,
|
||||
SCOPE_ACTIVE(opts->config));
|
||||
table_set_default(settings_table, settings_persistent,
|
||||
is_site_show(opts->site_id, SITE_FALLBACK) &&
|
||||
SCOPE_PERSISTENT(opts->config) && !readonly ? 1 : 0);
|
||||
table_set_default(settings_table, settings_autoconf,
|
||||
(SCOPE_AUTOCONF(opts->config) || autoconf) &&
|
||||
!readonly ? 1 : 0);
|
||||
if (dev) {
|
||||
for (i = 0; i < NUM_USER_SITES; i++)
|
||||
table_set_default(settings_table, settings_site0 + i,
|
||||
SCOPE_PERSISTENT(opts->config) &&
|
||||
is_site_show(opts->site_id, i) &&
|
||||
dev->site_specific[i].exists &&
|
||||
!readonly ? 1 : 0);
|
||||
|
||||
for (i = 0; i < NUM_SITES; i++)
|
||||
data.sites[i] = dev->site_specific[i].settings;
|
||||
}
|
||||
|
||||
data.active = active;
|
||||
data.persistent = persistent;
|
||||
data.autoconf = autoconf;
|
||||
data.pairs = opts->pairs;
|
||||
|
||||
table_print(settings_table, settings_table_get_value, &data, items,
|
||||
NULL, 1, opts->pairs, ind, 0);
|
||||
NULL, 1, opts->pairs, ind, 0, opts->shell);
|
||||
|
||||
out:
|
||||
ptrlist_free(items, 1);
|
||||
@@ -1320,7 +1537,7 @@ static exit_code_t do_info_type(struct options *opts)
|
||||
}
|
||||
} else {
|
||||
settings_table_print(dt->active_settings,
|
||||
dt->persistent_settings, NULL, opts,
|
||||
dt->persistent_settings, NULL, NULL, opts,
|
||||
INFO_INDENT, false, false);
|
||||
}
|
||||
|
||||
@@ -1346,7 +1563,7 @@ static void do_list_columns(struct options *opts)
|
||||
static void do_info_one_device(struct device *dev, struct options *opts)
|
||||
{
|
||||
struct subtype *st = dev->subtype;
|
||||
char *names, *bdevs, *cdevs, *ndevs, *modules, *online, *path, *key;
|
||||
char *names, *bdevs, *cdevs, *ndevs, *modules, *online, *path, *key, *sites;
|
||||
const char *id, *type, *exists, *persist, *ac, *prefix;
|
||||
struct util_list *resources, *errors;
|
||||
struct strlist_node *s;
|
||||
@@ -1367,6 +1584,7 @@ static void do_info_one_device(struct device *dev, struct options *opts)
|
||||
exists = YESNO(dev->active.exists || dev->active.definable);
|
||||
persist = YESNO(dev->persistent.exists);
|
||||
ac = YESNO(dev->autoconf.exists);
|
||||
sites = device_get_sites(dev);
|
||||
|
||||
/* Print information. */
|
||||
if (opts->pairs) {
|
||||
@@ -1384,6 +1602,9 @@ static void do_info_one_device(struct device *dev, struct options *opts)
|
||||
print_pair("ONLINE", online);
|
||||
print_pair("EXISTS", exists);
|
||||
print_pair("PERSISTENT", persist);
|
||||
if (*sites)
|
||||
print_pair("SITES", sites);
|
||||
|
||||
if (SCOPE_AUTOCONF(opts->config) || dev->autoconf.exists)
|
||||
print_pair("AUTOCONF", ac);
|
||||
if (errors) {
|
||||
@@ -1413,6 +1634,9 @@ static void do_info_one_device(struct device *dev, struct options *opts)
|
||||
print_info("Persistent", persist);
|
||||
if (SCOPE_AUTOCONF(opts->config) || dev->autoconf.exists)
|
||||
print_info("Auto-configured", ac);
|
||||
if (*sites)
|
||||
print_info("Sites", sites);
|
||||
|
||||
if (errors) {
|
||||
first = true;
|
||||
util_list_iterate(errors, s) {
|
||||
@@ -1453,17 +1677,17 @@ static void do_info_one_device(struct device *dev, struct options *opts)
|
||||
}
|
||||
|
||||
settings_table_print(dev->active.exists ? dev->active.settings : NULL,
|
||||
dev->persistent.exists ? dev->persistent.settings :
|
||||
is_dev_pers(dev) ? dev->persistent.settings :
|
||||
NULL,
|
||||
dev->autoconf.exists ? dev->autoconf.settings :
|
||||
NULL,
|
||||
NULL, dev,
|
||||
opts, INFO_INDENT, false, !opts->pairs);
|
||||
|
||||
settings_table_print(dev->active.exists ? dev->active.settings : NULL,
|
||||
dev->persistent.exists ? dev->persistent.settings :
|
||||
is_dev_pers(dev) ? dev->persistent.settings :
|
||||
NULL,
|
||||
dev->autoconf.exists ? dev->autoconf.settings :
|
||||
NULL,
|
||||
NULL, dev,
|
||||
opts, INFO_INDENT, true, !opts->pairs);
|
||||
|
||||
strlist_free(errors);
|
||||
@@ -1474,6 +1698,7 @@ static void do_info_one_device(struct device *dev, struct options *opts)
|
||||
free(cdevs);
|
||||
free(bdevs);
|
||||
free(names);
|
||||
free(sites);
|
||||
}
|
||||
|
||||
/* Perform --info on devices. */
|
||||
@@ -1518,10 +1743,18 @@ static exit_code_t do_info_devices(struct options *opts)
|
||||
|
||||
/* Only process existing devices. */
|
||||
active = dev->active.exists || dev->active.definable;
|
||||
persistent = dev->persistent.exists;
|
||||
persistent = (int)is_dev_pers(dev);
|
||||
autoconf = dev->autoconf.exists;
|
||||
if (!active && !persistent && !autoconf)
|
||||
continue;
|
||||
|
||||
/* with --site option, we consider only the persistent
|
||||
* configurations available on the specified site-id.
|
||||
* ignore active and autoconf here
|
||||
*/
|
||||
if (!persistent && global_site_id != SITE_FALLBACK)
|
||||
continue;
|
||||
|
||||
if (found > 0)
|
||||
printf("\n");
|
||||
do_info_one_device(dev, opts);
|
||||
@@ -1558,6 +1791,7 @@ int main(int argc, char *argv[])
|
||||
/* Set globals. */
|
||||
verbose = opts.verbose;
|
||||
quiet = opts.quiet;
|
||||
global_site_id = opts.site_id;
|
||||
path_set_base(opts.base);
|
||||
if (opts.pairs)
|
||||
set_stdout_data();
|
||||
|
||||
@@ -41,6 +41,8 @@ OPTIONS
|
||||
-n, --no-headings Do not print column headings
|
||||
--base PATH Use PATH as base for accessing files
|
||||
--pairs Produce output in KEY="VALUE" format
|
||||
--shell Produces KEY="VALUE" format usable as shell variables
|
||||
--auto-conf Only show auto-configuration data
|
||||
-s, --site ID Only show data configured for the specified site
|
||||
-V, --verbose Print additional run-time information
|
||||
-q, --quiet Print only minimal run-time information
|
||||
|
||||
@@ -1695,3 +1695,20 @@ char *misc_strrstr(const char *haystack, const char *needle)
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Check if the site mentioned is valid */
|
||||
bool is_valid_site(const char *str)
|
||||
{
|
||||
char *ptr = NULL;
|
||||
long site_id;
|
||||
|
||||
site_id = strtol(str, &ptr, 10);
|
||||
|
||||
if (*ptr)
|
||||
return false;
|
||||
|
||||
if (site_id < 0 || site_id >= NUM_USER_SITES)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -404,3 +404,13 @@ char *path_get_bus_attr(const char *bus, const char *attr)
|
||||
{
|
||||
return path_get("/sys/bus/%s/%s", bus, attr);
|
||||
}
|
||||
|
||||
/* Return path will be with ZDEV_PREFIX oon PATH_UDEV_RULES */
|
||||
char *path_get_zdev_rule(const char *name)
|
||||
{
|
||||
const char *path = PATH_UDEV_RULES;
|
||||
|
||||
/* Create file. */
|
||||
return path_get("%s/%s-%s%s", path,
|
||||
ZDEV_PREFIX, name, UDEV_SUFFIX);
|
||||
}
|
||||
|
||||
@@ -83,23 +83,58 @@ static bool is_zdev_early_0(struct selected_dev_node *sel)
|
||||
{
|
||||
struct setting *s;
|
||||
struct device *dev;
|
||||
int i;
|
||||
|
||||
dev = device_list_find(sel->st->devices, sel->id, NULL);
|
||||
if (!dev)
|
||||
return false;
|
||||
s = setting_list_find(dev->persistent.settings,
|
||||
internal_attr_early.name);
|
||||
if (!s)
|
||||
|
||||
for (i = 0; i < NUM_SITES; i++) {
|
||||
s = setting_list_find(dev->site_specific[i].settings,
|
||||
internal_attr_early.name);
|
||||
if (!s)
|
||||
return false;
|
||||
if (s->specified && strcmp(s->value, "0") == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* When site-specific settings has zdev:early configured, it is not
|
||||
* detected with the --by-attr functionality of select_devices. Instead
|
||||
* we will have to iterate through each configuration and check if there
|
||||
* is a zdev:early set in the device. This is usually applicable only in
|
||||
* the case of deconfigure-all command, because, for all other commands
|
||||
* we copy the settings-in-action to the persistent settings and do the
|
||||
* processing there.
|
||||
*/
|
||||
static bool is_zdev_early_site(struct selected_dev_node *sel)
|
||||
{
|
||||
struct setting *s;
|
||||
struct device *dev;
|
||||
int i;
|
||||
|
||||
if (global_site_id != SITE_FALLBACK)
|
||||
return false;
|
||||
if (s->specified && strcmp(s->value, "0") == 0)
|
||||
return true;
|
||||
|
||||
dev = device_list_find(sel->st->devices, sel->id, NULL);
|
||||
if (!dev)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < NUM_SITES; i++) {
|
||||
s = setting_list_find(dev->site_specific[i].settings,
|
||||
internal_attr_early.name);
|
||||
if (s)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Determine if initial RAM-disk needs updating. If so, run the corresponding
|
||||
* scripts if available. */
|
||||
exit_code_t initrd_check(bool all_pers)
|
||||
exit_code_t initrd_check(bool all_pers, int site_validate)
|
||||
{
|
||||
struct util_list *selected, *params, *mod = strlist_new();
|
||||
struct selected_dev_node *sel;
|
||||
@@ -142,7 +177,12 @@ exit_code_t initrd_check(bool all_pers)
|
||||
}
|
||||
/* Finally add devices with zdev:early=1. */
|
||||
select = select_opts_new();
|
||||
strlist_add(&select->by_attr, "%s=1", INTERNAL_ATTR_EARLY);
|
||||
/* If we need information from multiple site, we cannot use the by-attr
|
||||
* functionality of select_devices function. Instead we have to iterate
|
||||
* through individual devices and site settings to find the key.
|
||||
*/
|
||||
if (!site_validate)
|
||||
strlist_add(&select->by_attr, "%s=1", INTERNAL_ATTR_EARLY);
|
||||
select_devices(select, selected, 1, 0, 0,
|
||||
config_active | config_persistent, scope_mandatory,
|
||||
err_ignore);
|
||||
@@ -162,7 +202,12 @@ check_mod:
|
||||
/* Check devices. */
|
||||
dev = device_list_find(sel->st->devices, sel->id, NULL);
|
||||
if (dev && dev->persistent.exists &&
|
||||
(device_needs_writing(dev, config_persistent) || force)) {
|
||||
(device_needs_writing(dev, config_persistent) || force) &&
|
||||
!site_validate) {
|
||||
strlist_add(mod, "%s %s", dev->subtype->devname,
|
||||
dev->id);
|
||||
} else if (site_validate && is_zdev_early_site(sel) &&
|
||||
device_needs_writing(dev, config_persistent)) {
|
||||
strlist_add(mod, "%s %s", dev->subtype->devname,
|
||||
dev->id);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -116,6 +117,40 @@ static struct cell *cells_get_default(struct column *columns)
|
||||
return cells;
|
||||
}
|
||||
|
||||
bool is_shell_char(const char c)
|
||||
{
|
||||
/* check whether character is alphabetic */
|
||||
if (tolower(c) >= 'a' && tolower(c) <= 'z')
|
||||
return true;
|
||||
if (isdigit(c) || c == '_')
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* replaces bad characters that won't work if using them
|
||||
* as part of variable names in a shell environment
|
||||
*/
|
||||
char *replace_bad_chars(const char *value)
|
||||
{
|
||||
int i;
|
||||
char *copy, c;
|
||||
|
||||
/* remove bad characters and replace them by an underscore */
|
||||
copy = misc_strdup(value);
|
||||
for (i = 0; (c = value[i]); i++) {
|
||||
/*
|
||||
* check whether character is alphabetic,
|
||||
* first character of an env var can't be a digit
|
||||
*/
|
||||
if (!is_shell_char(c) || (i == 0 && isdigit(c)))
|
||||
c = '_';
|
||||
/* copy character into string copy */
|
||||
copy[i] = c;
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
/* Return a newly allocated array of struct cells containing a struct cell for
|
||||
* each column definition whose name was specified in strlist @names. */
|
||||
static struct cell *cells_get(struct column *columns, struct util_list *names)
|
||||
@@ -320,17 +355,23 @@ do_print:
|
||||
}
|
||||
|
||||
/* Print all cells in a row in pairs format. */
|
||||
static void print_row_pairs(struct cell *cells)
|
||||
static void print_row_pairs(struct cell *cells, int shell)
|
||||
{
|
||||
int i;
|
||||
struct cell *c;
|
||||
char *val;
|
||||
char *heading;
|
||||
|
||||
for (i = 0; cells[i].heading; i++) {
|
||||
c = &cells[i];
|
||||
val = quote_str(c->value ? c->value : "", 1);
|
||||
printf("%s%s=%s", i > 0 ? " " : "", c->heading, val);
|
||||
if (shell)
|
||||
heading = replace_bad_chars(c->heading);
|
||||
else
|
||||
heading = misc_strdup(c->heading);
|
||||
printf("%s%s=%s", i > 0 ? " " : "", heading, val);
|
||||
free(val);
|
||||
free(heading);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
@@ -342,7 +383,7 @@ static void print_row_pairs(struct cell *cells)
|
||||
exit_code_t table_print(struct column *columns, table_value_cb_t get_value_cb,
|
||||
void *data, struct util_list *items,
|
||||
struct util_list *names, int heading, int pairs,
|
||||
int indent, int wrap)
|
||||
int indent, int wrap, int shell)
|
||||
{
|
||||
struct cell *cells;
|
||||
struct ptrlist_node *p;
|
||||
@@ -372,7 +413,7 @@ exit_code_t table_print(struct column *columns, table_value_cb_t get_value_cb,
|
||||
util_list_iterate(items, p) {
|
||||
cells_get_values(cells, p->ptr, get_value_cb, data);
|
||||
if (pairs)
|
||||
print_row_pairs(cells);
|
||||
print_row_pairs(cells, shell);
|
||||
else
|
||||
print_row(cells, space, indent, wrap);
|
||||
}
|
||||
@@ -434,7 +475,7 @@ void table_print_columns(struct column *columns, struct util_list *names,
|
||||
for (i = 0; columns[i].name; i++)
|
||||
ptrlist_add(items, &columns[i]);
|
||||
table_print(columns_table, columns_table_get_value, NULL, items, names,
|
||||
heading, pairs, 0, 0);
|
||||
heading, pairs, 0, 0, 0);
|
||||
ptrlist_free(items, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ void print_type(struct devtype *dt, struct subtype *st, bool multiple)
|
||||
|
||||
/* Display table of attributes. */
|
||||
void table_attribs_show(struct util_list *attribs, int headings, int pairs,
|
||||
struct devtype *dt)
|
||||
int shell, struct devtype *dt)
|
||||
{
|
||||
struct util_list *subtypes, *subattribs;
|
||||
struct ptrlist_node *p;
|
||||
@@ -145,7 +145,7 @@ void table_attribs_show(struct util_list *attribs, int headings, int pairs,
|
||||
print_type(dt, st, multiple);
|
||||
|
||||
table_print(table_attribs, table_attribs_get_value, st,
|
||||
subattribs, NULL, headings, pairs, indent, 0);
|
||||
subattribs, NULL, headings, pairs, shell, indent, 0);
|
||||
|
||||
ptrlist_free(subattribs, 0);
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ exit_code_t table_types_show(struct util_list *columns, int headings, int pairs)
|
||||
|
||||
items = table_types_build();
|
||||
rc = table_print(table_types, table_types_get_value, NULL,
|
||||
items, columns, headings, pairs, 0, 0);
|
||||
items, columns, headings, pairs, 0, 0, 0);
|
||||
ptrlist_free(items, 1);
|
||||
|
||||
return rc;
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "path.h"
|
||||
#include "setting.h"
|
||||
#include "udev.h"
|
||||
#include "zdev_id.h"
|
||||
|
||||
int udev_need_settle = 0;
|
||||
int udev_no_settle;
|
||||
@@ -137,3 +138,61 @@ void udev_add_internal_from_entry(struct setting_list *list,
|
||||
out:
|
||||
free(copy);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write the UDEV rule that determines the active SITE_ID
|
||||
*/
|
||||
exit_code_t udev_write_site_rule(void)
|
||||
{
|
||||
char *path, *name = "zdev-id";
|
||||
exit_code_t rc = EXIT_OK;
|
||||
FILE *fd;
|
||||
|
||||
/* Create file. */
|
||||
path = path_get_zdev_rule(name);
|
||||
|
||||
if (!util_path_exists(path)) {
|
||||
rc = path_create(path);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
fd = misc_fopen(path, "w");
|
||||
if (!fd) {
|
||||
error("Could not write to file %s: %s\n", path,
|
||||
strerror(errno));
|
||||
rc = EXIT_RUNTIME_ERROR;
|
||||
goto out;
|
||||
}
|
||||
fprintf(fd, "# Generated by chzdev\n");
|
||||
fprintf(fd, "\n");
|
||||
/* We have to make sure that we are not trying to determine the SITE_ID
|
||||
* for all the corresponding uevent. Check if the SITE_ID is already
|
||||
* determined by reading the file /run/zdev_site_id; and if it is not
|
||||
* available, execute the zdev_id program and determine the SITE_ID and
|
||||
* create the new zdev_site_id file for next run.
|
||||
*/
|
||||
fprintf(fd, "TEST!=\"%s\", IMPORT{program}="
|
||||
"\"%s/zdev_id\",GOTO=\"end\"\n", ZDEV_SITE_ID_FILE,
|
||||
TOOLS_LIBDIR);
|
||||
fprintf(fd, "IMPORT{file}=\"%s\"\n", ZDEV_SITE_ID_FILE);
|
||||
fprintf(fd, "LABEL=\"end\"\n");
|
||||
|
||||
if (misc_fclose(fd))
|
||||
warn("Could not close file %s: %s\n", path, strerror(errno));
|
||||
|
||||
out:
|
||||
free(path);
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool is_legacy_rule(struct util_udev_file *file)
|
||||
{
|
||||
struct util_udev_line_node *line;
|
||||
|
||||
util_list_iterate(&file->lines, line)
|
||||
if (starts_with(line->line, SITE_BLOCK_START))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#include "udev.h"
|
||||
#include "udev_ccw.h"
|
||||
|
||||
#define SITE_FALLBACK_SUFFIX "fb"
|
||||
|
||||
/* Check if a udev rule for the specified ccw device exists. */
|
||||
bool udev_ccw_exists(const char *type, const char *id, bool autoconf)
|
||||
{
|
||||
@@ -79,10 +81,68 @@ out:
|
||||
free(copy);
|
||||
}
|
||||
|
||||
static void get_site_from_line(char *line, int *site, bool *in_site)
|
||||
{
|
||||
char site_str[2];
|
||||
|
||||
if (starts_with(line, SITE_BLOCK_START)) {
|
||||
memcpy(site_str, line + sizeof(SITE_BLOCK_START), 2);
|
||||
if (strcmp(site_str, "fb") != 0)
|
||||
*site = atoi(site_str);
|
||||
else
|
||||
*site = SITE_FALLBACK;
|
||||
*in_site = true;
|
||||
} else if (starts_with(line, SITE_BLOCK_END)) {
|
||||
memcpy(site_str, line + sizeof(SITE_BLOCK_END), 2);
|
||||
if (strcmp(site_str, "fb") != 0)
|
||||
*site = atoi(site_str);
|
||||
else
|
||||
*site = SITE_FALLBACK;
|
||||
*in_site = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void dev_state_copy(struct device_state *dst, struct device_state *src)
|
||||
{
|
||||
setting_list_free(dst->settings);
|
||||
dst->settings = setting_list_copy(src->settings);
|
||||
dst->exists = src->exists;
|
||||
dst->modified = src->modified;
|
||||
dst->deconfigured = src->deconfigured;
|
||||
dst->blacklisted = src->blacklisted;
|
||||
dst->definable = src->definable;
|
||||
}
|
||||
|
||||
/* Extract per-site CCW device settings from a CCW device udev rule file. */
|
||||
static void _udev_file_get_settings_new(struct util_udev_file *file,
|
||||
struct attrib **attribs,
|
||||
struct device *dev)
|
||||
{
|
||||
struct util_udev_line_node *line;
|
||||
struct util_udev_entry_node *entry;
|
||||
int site_id = SITE_FALLBACK;
|
||||
struct setting_list *list;
|
||||
bool in_site = false;
|
||||
|
||||
util_list_iterate(&file->lines, line) {
|
||||
get_site_from_line(line->line, &site_id, &in_site);
|
||||
if (!in_site || site_id >= NUM_SITES || site_id < 0)
|
||||
continue;
|
||||
entry = util_list_start(&line->entries);
|
||||
if (!entry)
|
||||
continue;
|
||||
list = dev->site_specific[site_id].settings;
|
||||
add_setting_from_entry(list, entry, attribs);
|
||||
dev->site_specific[site_id].exists = 1;
|
||||
if (site_id == global_site_id)
|
||||
dev_state_copy(&dev->persistent, &dev->site_specific[site_id]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Extract CCW device settings from a CCW device udev rule file. */
|
||||
static void udev_file_get_settings(struct util_udev_file *file,
|
||||
struct attrib **attribs,
|
||||
struct setting_list *list)
|
||||
static void _udev_file_get_settings_legacy(struct util_udev_file *file,
|
||||
struct attrib **attribs,
|
||||
struct setting_list *list)
|
||||
{
|
||||
struct util_udev_line_node *line;
|
||||
struct util_udev_entry_node *entry;
|
||||
@@ -95,6 +155,22 @@ static void udev_file_get_settings(struct util_udev_file *file,
|
||||
}
|
||||
}
|
||||
|
||||
static void udev_file_get_settings(struct util_udev_file *file,
|
||||
struct attrib **attribs,
|
||||
struct device *dev,
|
||||
bool autoconf)
|
||||
{
|
||||
struct device_state *state = autoconf ? &dev->autoconf :
|
||||
&dev->persistent;
|
||||
|
||||
if (is_legacy_rule(file) || autoconf) {
|
||||
_udev_file_get_settings_legacy(file, attribs, state->settings);
|
||||
state->exists = 1;
|
||||
} else {
|
||||
_udev_file_get_settings_new(file, attribs, dev);
|
||||
}
|
||||
}
|
||||
|
||||
/* Read the persistent configuration of a CCW device from a udev rule. */
|
||||
exit_code_t udev_ccw_read_device(struct device *dev, bool autoconf)
|
||||
{
|
||||
@@ -113,8 +189,7 @@ exit_code_t udev_ccw_read_device(struct device *dev, bool autoconf)
|
||||
warn_once("Warning: Invalid udev rule: %s\n", path);
|
||||
state->exists = 0;
|
||||
} else {
|
||||
udev_file_get_settings(file, st->dev_attribs, state->settings);
|
||||
state->exists = 1;
|
||||
udev_file_get_settings(file, st->dev_attribs, dev, autoconf);
|
||||
}
|
||||
util_udev_free_file(file);
|
||||
|
||||
@@ -141,8 +216,38 @@ static char *get_label_id(const char *prefix, const char *type,
|
||||
return id;
|
||||
}
|
||||
|
||||
/* Write the persistent configuration of a CCW device to a udev rule. */
|
||||
exit_code_t udev_ccw_write_device(struct device *dev, bool autoconf)
|
||||
static void write_attr_to_file(FILE *fd, struct device_state *state, const char *id)
|
||||
{
|
||||
struct ptrlist_node *p;
|
||||
struct setting *s;
|
||||
struct util_list *list = NULL;
|
||||
struct strlist_node *str;
|
||||
|
||||
/* Apply attributes in correct order. */
|
||||
list = setting_list_get_sorted(state->settings);
|
||||
|
||||
util_list_iterate(list, p) {
|
||||
s = p->ptr;
|
||||
if (s->removed)
|
||||
continue;
|
||||
if (s->values) {
|
||||
util_list_iterate(s->values, str) {
|
||||
fprintf(fd, "ATTR{[ccw/%s]%s}=\"%s\"\n",
|
||||
id, s->name, str->str);
|
||||
}
|
||||
} else if ((s->attrib && s->attrib->internal) ||
|
||||
internal_by_name(s->name)) {
|
||||
fprintf(fd, "ENV{zdev_%s}=\"%s\"\n",
|
||||
internal_get_name(s->name), s->value);
|
||||
} else {
|
||||
fprintf(fd, "ATTR{[ccw/%s]%s}=\"%s\"\n", id, s->name,
|
||||
s->value);
|
||||
}
|
||||
}
|
||||
ptrlist_free(list, 0);
|
||||
}
|
||||
|
||||
static exit_code_t udev_ccw_write_device_legacy(struct device *dev, bool autoconf)
|
||||
{
|
||||
struct subtype *st = dev->subtype;
|
||||
struct ccw_subtype_data *data = st->data;
|
||||
@@ -150,22 +255,16 @@ exit_code_t udev_ccw_write_device(struct device *dev, bool autoconf)
|
||||
struct device_state *state = autoconf ? &dev->autoconf :
|
||||
&dev->persistent;
|
||||
char *path, *cfg_label = NULL, *end_label = NULL;
|
||||
struct util_list *list;
|
||||
struct ptrlist_node *p;
|
||||
struct setting *s;
|
||||
exit_code_t rc = EXIT_OK;
|
||||
FILE *fd;
|
||||
|
||||
if (!state->exists)
|
||||
return udev_remove_rule(type, id, autoconf);
|
||||
return udev_remove_rule(type, id, true);
|
||||
|
||||
cfg_label = get_label_id("cfg", type, id);
|
||||
end_label = get_label_id("end", type, id);
|
||||
|
||||
/* Apply attributes in correct order. */
|
||||
list = setting_list_get_sorted(state->settings);
|
||||
|
||||
path = path_get_udev_rule(type, id, autoconf);
|
||||
path = path_get_udev_rule(type, id, true);
|
||||
debug("Writing %s udev rule file %s\n", type, path);
|
||||
if (!util_path_exists(path)) {
|
||||
rc = path_create(path);
|
||||
@@ -196,22 +295,8 @@ exit_code_t udev_ccw_write_device(struct device *dev, bool autoconf)
|
||||
}
|
||||
fprintf(fd, "GOTO=\"%s\"\n", end_label);
|
||||
fprintf(fd, "\n");
|
||||
fprintf(fd, "LABEL=\"%s\"\n", cfg_label);
|
||||
|
||||
/* Write settings. */
|
||||
util_list_iterate(list, p) {
|
||||
s = p->ptr;
|
||||
if (s->removed)
|
||||
continue;
|
||||
if ((s->attrib && s->attrib->internal) ||
|
||||
internal_by_name(s->name)) {
|
||||
fprintf(fd, "ENV{zdev_%s}=\"%s\"\n",
|
||||
internal_get_name(s->name), s->value);
|
||||
} else {
|
||||
fprintf(fd, "ATTR{[ccw/%s]%s}=\"%s\"\n", id, s->name,
|
||||
s->value);
|
||||
}
|
||||
}
|
||||
write_attr_to_file(fd, state, id);
|
||||
|
||||
/* Write udev rule epilog. */
|
||||
fprintf(fd, "\n");
|
||||
@@ -221,7 +306,6 @@ exit_code_t udev_ccw_write_device(struct device *dev, bool autoconf)
|
||||
warn("Could not close file %s: %s\n", path, strerror(errno));
|
||||
|
||||
out:
|
||||
ptrlist_free(list, 0);
|
||||
free(end_label);
|
||||
free(cfg_label);
|
||||
free(path);
|
||||
@@ -229,6 +313,150 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Write the persistent configuration of a CCW device to a udev rule. */
|
||||
static exit_code_t udev_ccw_write_device_new(struct device *dev)
|
||||
{
|
||||
struct subtype *st = dev->subtype;
|
||||
struct ccw_subtype_data *data = st->data;
|
||||
const char *type = st->name, *drv = data->any_driver ? "*" : data->ccwdrv, *id = dev->id;
|
||||
char *path, *cfg_label = NULL, *end_label = NULL;
|
||||
exit_code_t rc = EXIT_OK;
|
||||
FILE *fd;
|
||||
int i, configured = 0;
|
||||
|
||||
/*
|
||||
* Remove the previous file; The new udev rule will be created based on the
|
||||
* current configuration settings
|
||||
*/
|
||||
udev_remove_rule(type, id, 0);
|
||||
|
||||
/*
|
||||
* Copy the dev->persistent to the site_specific array; While writing the new
|
||||
* udev-rule we consider the site_specific array only
|
||||
*/
|
||||
|
||||
dev_state_copy(&dev->site_specific[global_site_id], &dev->persistent);
|
||||
for (i = 0; i < NUM_SITES; i++) {
|
||||
configured += dev->site_specific[i].exists;
|
||||
configured -= dev->site_specific[i].deconfigured;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there is no configuration settings available, do not create the new
|
||||
* udev-rule file; exit here
|
||||
*/
|
||||
if (!configured)
|
||||
return rc;
|
||||
|
||||
cfg_label = get_label_id("cfg", type, id);
|
||||
end_label = get_label_id("end", type, id);
|
||||
|
||||
path = path_get_udev_rule(type, id, false);
|
||||
debug("Writing %s udev rule file %s\n", type, path);
|
||||
if (!util_path_exists(path)) {
|
||||
rc = path_create(path);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
fd = misc_fopen(path, "w");
|
||||
if (!fd) {
|
||||
error("Could not write to file %s: %s\n", path,
|
||||
strerror(errno));
|
||||
rc = EXIT_RUNTIME_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Write udev rule prolog. */
|
||||
fprintf(fd, "# Generated by chzdev\n");
|
||||
|
||||
if (drv) {
|
||||
fprintf(fd, "ACTION==\"add\", SUBSYSTEM==\"ccw\", "
|
||||
"KERNEL==\"%s\", DRIVER==\"%s\", GOTO=\"%s\"\n", id,
|
||||
drv, cfg_label);
|
||||
fprintf(fd, "ACTION==\"add\", SUBSYSTEM==\"drivers\", "
|
||||
"KERNEL==\"%s\", TEST==\"[ccw/%s]\", "
|
||||
"GOTO=\"%s\"\n", drv, id, cfg_label);
|
||||
} else {
|
||||
fprintf(fd, "ACTION==\"add\", SUBSYSTEM==\"ccw\", "
|
||||
"KERNEL==\"%s\", GOTO=\"%s\"\n", id, cfg_label);
|
||||
}
|
||||
fprintf(fd, "GOTO=\"%s\"\n", end_label);
|
||||
fprintf(fd, "\n");
|
||||
|
||||
fprintf(fd, "LABEL=\"%s\"\n", cfg_label);
|
||||
/* site comparison block */
|
||||
for (i = 0; i < NUM_USER_SITES; i++) {
|
||||
if (dev->site_specific[i].exists &&
|
||||
!dev->site_specific[i].deconfigured) {
|
||||
fprintf(fd, "ENV{ZDEV_SITE_ID}==\"%d\",GOTO=\"%s_site%d\"\n", i,
|
||||
cfg_label, i);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have a generic configuration available, then use that setting as a
|
||||
* fail-over incase of no site-id information in LOADPARM
|
||||
*/
|
||||
if (dev->site_specific[SITE_FALLBACK].exists &&
|
||||
!dev->site_specific[SITE_FALLBACK].deconfigured)
|
||||
fprintf(fd, "GOTO=\"%s_site_fb\"\n", cfg_label);
|
||||
else
|
||||
fprintf(fd, "GOTO=\"%s\"\n", end_label);
|
||||
fprintf(fd, "\n");
|
||||
|
||||
/* Write the site blocks for all the available configurations */
|
||||
for (i = 0; i < NUM_USER_SITES; i++) {
|
||||
if (dev->site_specific[i].exists &&
|
||||
!dev->site_specific[i].deconfigured) {
|
||||
fprintf(fd, "# site_start_%d\n", i);
|
||||
fprintf(fd, "LABEL=\"%s_site%d\"\n", cfg_label, i);
|
||||
|
||||
write_attr_to_file(fd, &dev->site_specific[i], id);
|
||||
/* Write udev rule epilog. */
|
||||
fprintf(fd, "GOTO=\"%s\"\n", end_label);
|
||||
fprintf(fd, "# site_end_%d\n", i);
|
||||
fprintf(fd, "\n");
|
||||
}
|
||||
}
|
||||
if (dev->site_specific[SITE_FALLBACK].exists &&
|
||||
!dev->site_specific[SITE_FALLBACK].deconfigured) {
|
||||
fprintf(fd, "# site_start_fb\n");
|
||||
fprintf(fd, "LABEL=\"%s_site_fb\"\n", cfg_label);
|
||||
|
||||
write_attr_to_file(fd, &dev->site_specific[i], id);
|
||||
/* Write udev rule epilog. */
|
||||
fprintf(fd, "GOTO=\"%s\"\n", end_label);
|
||||
fprintf(fd, "# site_end_fb\n");
|
||||
fprintf(fd, "\n");
|
||||
}
|
||||
|
||||
/* Write udev rule epilog. */
|
||||
fprintf(fd, "\n");
|
||||
fprintf(fd, "LABEL=\"%s\"\n", end_label);
|
||||
|
||||
if (misc_fclose(fd))
|
||||
warn("Could not close file %s: %s\n", path, strerror(errno));
|
||||
|
||||
rc = udev_write_site_rule();
|
||||
if (rc)
|
||||
goto out;
|
||||
out:
|
||||
free(end_label);
|
||||
free(cfg_label);
|
||||
free(path);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
exit_code_t udev_ccw_write_device(struct device *dev, bool autoconf)
|
||||
{
|
||||
if (autoconf)
|
||||
return udev_ccw_write_device_legacy(dev, autoconf);
|
||||
else
|
||||
return udev_ccw_write_device_new(dev);
|
||||
}
|
||||
|
||||
#define MARKER "echo free "
|
||||
|
||||
static char *read_cio_ignore(const char *path)
|
||||
@@ -300,6 +528,7 @@ exit_code_t udev_ccw_write_cio_ignore(const char *id_list, bool autoconf)
|
||||
|
||||
/* Write udev rule. */
|
||||
fprintf(fd, "# Generated by chzdev\n");
|
||||
|
||||
fprintf(fd, "ACTION==\"add\", SUBSYSTEM==\"subsystem\", "
|
||||
"KERNEL==\"ccw\", RUN{program}+=\"/bin/sh -c "
|
||||
"'echo free %s > /proc/cio_ignore'\"\n", id_list);
|
||||
|
||||
@@ -11,9 +11,17 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "zdev_id.h"
|
||||
|
||||
#define SYSINFO "/proc/sysinfo"
|
||||
#define CMDLINE "/proc/cmdline"
|
||||
#define LOADPARM "/sys/firmware/ipl/loadparm"
|
||||
#define IPL_DEV_ID "/sys/firmware/ipl/device"
|
||||
#define IPL_DEV_TYPE "/sys/firmware/ipl/ipl_type"
|
||||
#define SITE_FALLBACK 10
|
||||
|
||||
#define WHITESPACE " \t\n"
|
||||
|
||||
static void array_add(char ***array_p, int *num_p, const char *str)
|
||||
@@ -140,15 +148,139 @@ out:
|
||||
printf("ZDEV_NO_AUTO=%d\n", no_auto);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the erraneous loadparm provides a site_id which is not valid,
|
||||
* default it to the common-site id.
|
||||
*/
|
||||
static int validate_site(int site_id)
|
||||
{
|
||||
if (site_id < SITE_FALLBACK)
|
||||
return site_id;
|
||||
return SITE_FALLBACK;
|
||||
}
|
||||
|
||||
/* get the ipl device and extract the SSID */
|
||||
static int get_site_id(void)
|
||||
{
|
||||
FILE *fd;
|
||||
char *line;
|
||||
size_t n;
|
||||
int site_id = SITE_FALLBACK, ssid;
|
||||
|
||||
fd = fopen(IPL_DEV_ID, "r");
|
||||
if (!fd)
|
||||
goto fail_safe;
|
||||
|
||||
if (getline(&line, &n, fd) == -1)
|
||||
goto out;
|
||||
|
||||
if (sscanf(line, "%*d.%d.%*d", &ssid) == 1)
|
||||
site_id = validate_site(ssid);
|
||||
|
||||
out:
|
||||
free(line);
|
||||
fclose(fd);
|
||||
fail_safe:
|
||||
return site_id;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the ipl type and see if it belongs to ccw or zfcp, if different,
|
||||
* default it to common-site.
|
||||
*/
|
||||
static int read_ssid(void)
|
||||
{
|
||||
FILE *fd;
|
||||
char *line = NULL;
|
||||
size_t n;
|
||||
int site_id = SITE_FALLBACK;
|
||||
|
||||
fd = fopen(IPL_DEV_TYPE, "r");
|
||||
if (!fd)
|
||||
goto out;
|
||||
|
||||
if (getline(&line, &n, fd) != -1) {
|
||||
if (strcmp(line, "ccw") == 0 || strcmp(line, "zfcp"))
|
||||
site_id = get_site_id();
|
||||
}
|
||||
|
||||
free(line);
|
||||
fclose(fd);
|
||||
out:
|
||||
return site_id;
|
||||
}
|
||||
|
||||
static void write_zdev_site_id(int site_id)
|
||||
{
|
||||
FILE *fd;
|
||||
|
||||
fd = fopen(ZDEV_SITE_ID_FILE, "w");
|
||||
if (!fd)
|
||||
err(1, "Could not write to zdev_site_id file");
|
||||
if (site_id == SITE_FALLBACK)
|
||||
fprintf(fd, "ZDEV_SITE_ID=\n");
|
||||
else
|
||||
fprintf(fd, "ZDEV_SITE_ID=%d\n", site_id);
|
||||
|
||||
fclose(fd);
|
||||
}
|
||||
|
||||
/* Read the loadparm and extract the current site_id.
|
||||
* loadparm can contains either Sn or "Ss". Sn indicate the site_id, where
|
||||
* 'n' is the integer which could be one of the valid site_ids from 0 to 9.
|
||||
* When loadparm value is "Ss", zdev_id extracts the site_id from the SSID
|
||||
* of the current ipl device. For ccw and zfcp devices, the current ipl
|
||||
* device-id can be found at /sys/firmware/ipl/device.
|
||||
* For all other invalid cases, set ZDEV_SITE_ID to NULL.
|
||||
*/
|
||||
|
||||
static void process_loadparm(const char *filename)
|
||||
{
|
||||
size_t n;
|
||||
FILE *fd;
|
||||
char *line = NULL, *substr;
|
||||
int site_id = SITE_FALLBACK;
|
||||
|
||||
fd = fopen(filename, "r");
|
||||
if (!fd)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* We expect the value here to be either SX, where X is the site-id
|
||||
* or SS,in which case, the site-id will be derived from the SSID
|
||||
*/
|
||||
if ((getline(&line, &n, fd)) != -1) {
|
||||
substr = strchr(line, 'S');
|
||||
if (!substr)
|
||||
goto out;
|
||||
|
||||
if (isdigit(substr[1]))
|
||||
site_id = validate_site(atoi(&substr[1]));
|
||||
else if (substr[1] == 'S')
|
||||
site_id = read_ssid();
|
||||
}
|
||||
|
||||
free(line);
|
||||
fclose(fd);
|
||||
out:
|
||||
write_zdev_site_id(site_id);
|
||||
if (site_id == SITE_FALLBACK)
|
||||
printf("ZDEV_SITE_ID=\n");
|
||||
else
|
||||
printf("ZDEV_SITE_ID=%d\n", site_id);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char *sysinfo, *cmdline;
|
||||
char *sysinfo, *cmdline, *loadparm;
|
||||
|
||||
sysinfo = argc < 2 ? SYSINFO : argv[1];
|
||||
cmdline = argc < 3 ? CMDLINE : argv[2];
|
||||
loadparm = argc < 4 ? LOADPARM : argv[3];
|
||||
|
||||
process_sysinfo(sysinfo);
|
||||
process_cmdline(cmdline);
|
||||
process_loadparm(loadparm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
113
zdump/Makefile
113
zdump/Makefile
@@ -7,44 +7,66 @@ ALL_CFLAGS += -Wno-address-of-packed-member
|
||||
#
|
||||
ifeq (${HAVE_FUSE},0)
|
||||
|
||||
check_dep_fuse:
|
||||
|
||||
.check_dep_fuse:
|
||||
touch $@
|
||||
else
|
||||
|
||||
check_dep_fuse:
|
||||
.check_dep_fuse:
|
||||
$(call check_dep, \
|
||||
"zgetdump mount support", \
|
||||
"fuse.h", \
|
||||
"fuse3-devel or libfuse3-dev", \
|
||||
"HAVE_FUSE=0", \
|
||||
"-DFUSE_USE_VERSION=30")
|
||||
touch $@
|
||||
endif
|
||||
|
||||
#
|
||||
# HAVE_ZLIB: Allow skip zgetdump build, when no zlib-devel is available
|
||||
#
|
||||
ifeq (${HAVE_ZLIB},0)
|
||||
.detect_openssl.dep.c:
|
||||
echo "#include <openssl/evp.h>" > $@
|
||||
echo "#if OPENSSL_VERSION_NUMBER < 0x10100000L" >> $@
|
||||
echo " #error At least OpenSSL version 1.1.0 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 "}" >> $@
|
||||
|
||||
all:
|
||||
$(SKIP) HAVE_ZLIB=0
|
||||
|
||||
install:
|
||||
$(SKIP) HAVE_ZLIB=0
|
||||
|
||||
else
|
||||
|
||||
check_dep_zlib:
|
||||
.check_dep_zgetdump: .detect_openssl.dep.c .check_dep_fuse
|
||||
$(call check_dep, \
|
||||
"zgetdump", \
|
||||
"zlib.h", \
|
||||
"zlib-devel or libz-dev", \
|
||||
"HAVE_ZLIB=0")
|
||||
"zgetdump", \
|
||||
"zlib.h", \
|
||||
"zlib-devel or libz-dev", \
|
||||
"HAVE_ZLIB=0")
|
||||
$(call check_dep, \
|
||||
"zgetdump", \
|
||||
"glib.h", \
|
||||
"glib2-devel / libglib2.0-dev", \
|
||||
"HAVE_GLIB2=0")
|
||||
$(call check_dep, \
|
||||
"zgetdump", \
|
||||
$<, \
|
||||
"openssl-devel / libssl-dev version >= 1.1.0", \
|
||||
"HAVE_OPENSSL=0", \
|
||||
"-I.")
|
||||
touch $@
|
||||
|
||||
all: check_dep_fuse check_dep_zlib zgetdump
|
||||
#
|
||||
# Allow skip of zgetdump build, when no zlib-devel, openssl-devel, glib2-devel is available
|
||||
#
|
||||
BUILD_TARGETS :=skip-zgetdump
|
||||
INSTALL_TARGETS := skip-zgetdump
|
||||
ifneq (${HAVE_ZLIB},0)
|
||||
ifneq (${HAVE_OPENSSL},0)
|
||||
ifneq (${HAVE_GLIB2},0)
|
||||
BUILD_TARGETS := zgetdump
|
||||
INSTALL_TARGETS := install-zgetdump
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
OBJECTS = zgetdump.o opts.o zg.o zg_error.o zg_print.o \
|
||||
dfi.o dfi_mem_chunk.o dfi_vmcoreinfo.o \
|
||||
dfi_lkcd.o dfi_elf.o \
|
||||
dfi_lkcd.o dfi_elf.o dfi_elf_common.o dfi_pv_elf.o \
|
||||
dfi_s390.o dfi_s390_ext.o\
|
||||
dfi_s390mv.o dfi_s390mv_ext.o \
|
||||
dfi_s390tape.o dfi_kdump.o \
|
||||
@@ -54,7 +76,27 @@ OBJECTS = zgetdump.o opts.o zg.o zg_error.o zg_print.o \
|
||||
dt.o dt_s390sv.o dt_s390sv_ext.o \
|
||||
dt_s390mv.o dt_s390mv_ext.o \
|
||||
dt_scsi.o output.o \
|
||||
ngdump.o dt_ngdump.o dfi_ngdump.o
|
||||
ngdump.o dt_ngdump.o dfi_ngdump.o \
|
||||
pv_utils.o
|
||||
|
||||
ifneq ($(shell sh -c 'command -v pkg-config'),)
|
||||
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)
|
||||
else
|
||||
GLIB2_CFLAGS := -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include
|
||||
GLIB2_LIBS := -lglib-2.0
|
||||
LIBCRYPTO_CFLAGS :=
|
||||
LIBCRYPTO_LIBS := -lcrypto
|
||||
endif
|
||||
|
||||
LIBPV = $(rootdir)/libpv/libpv.a
|
||||
LIBPV_CFLAGS := -DOPENSSL_API_COMPAT=0x10100000L \
|
||||
$(GLIB2_CFLAGS) \
|
||||
$(LIBCRYPTO_CFLAGS) \
|
||||
$(NULL)
|
||||
LIBPV_LIBS := $(GLIB2_LIBS) $(LIBCRYPTO_LIBS)
|
||||
|
||||
ifeq ("$(HAVE_FUSE)","0")
|
||||
FUSE_CFLAGS = -DHAVE_FUSE=0 -D_FILE_OFFSET_BITS=64
|
||||
@@ -66,23 +108,30 @@ else
|
||||
FUSE_CFLAGS = -DHAVE_FUSE=1 -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse3
|
||||
FUSE_LDLIBS = -lfuse3
|
||||
endif
|
||||
LDLIBS += -lz $(FUSE_LDLIBS)
|
||||
ALL_CFLAGS += $(FUSE_CFLAGS)
|
||||
LDLIBS += -lz $(FUSE_LDLIBS) $(LIBPV_LIBS)
|
||||
ALL_CFLAGS += $(FUSE_CFLAGS) $(LIBPV_CFLAGS)
|
||||
|
||||
ifneq ("$(HAVE_FUSE)","0")
|
||||
OBJECTS += zfuse.o
|
||||
endif
|
||||
|
||||
libs = $(rootdir)/libutil/libutil.a
|
||||
zgetdump: $(OBJECTS) $(libs)
|
||||
libs = $(rootdir)/libutil/libutil.a $(LIBPV)
|
||||
|
||||
install: all
|
||||
all: $(BUILD_TARGETS)
|
||||
|
||||
zgetdump: .check_dep_zgetdump $(OBJECTS) $(libs)
|
||||
|
||||
skip-zgetdump:
|
||||
echo " SKIP zgetdump due to unresolved dependencies"
|
||||
|
||||
install-zgetdump: zgetdump
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man8 $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -m 755 zgetdump $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -m 644 zgetdump.8 $(DESTDIR)$(MANDIR)/man8
|
||||
|
||||
clean:
|
||||
rm -f *.o *~ zgetdump core.*
|
||||
endif
|
||||
install: $(INSTALL_TARGETS)
|
||||
|
||||
.PHONY: all install clean check_dep_fuse check_dep_zlib
|
||||
clean:
|
||||
rm -f -- *.o *~ zgetdump core.* .detect_openssl.dep.c .check_dep_zgetdump .check_dep_fuse
|
||||
|
||||
.PHONY: all install clean skip-zgetdump install-zgetdump
|
||||
|
||||
269
zdump/df_elf.c
269
zdump/df_elf.c
@@ -7,27 +7,23 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "lib/util_libc.h"
|
||||
#include "lib/util_log.h"
|
||||
|
||||
#include "df_elf.h"
|
||||
|
||||
#define NOTE_NAME_CORE "CORE"
|
||||
#define NOTE_NAME_LINUX "LINUX"
|
||||
#define NOTE_NAME_VMCOREINFO "VMCOREINFO"
|
||||
|
||||
/*
|
||||
* Initialize ELF header
|
||||
*/
|
||||
void *ehdr_init(Elf64_Ehdr *ehdr, Elf64_Half phnum)
|
||||
{
|
||||
memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
|
||||
ehdr->e_ident[EI_CLASS] = ELFCLASS64;
|
||||
ehdr->e_ident[EI_DATA] = ELFDATA2MSB;
|
||||
ehdr->e_ident[EI_VERSION] = EV_CURRENT;
|
||||
ehdr->e_ident[EI_VERSION] = ELF_VERSION_1;
|
||||
ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV;
|
||||
ehdr->e_ident[EI_ABIVERSION] = 0;
|
||||
memset(ehdr->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
|
||||
ehdr->e_type = ET_CORE;
|
||||
ehdr->e_machine = EM_S390;
|
||||
ehdr->e_version = EV_CURRENT;
|
||||
ehdr->e_version = ELF_VERSION_1;
|
||||
ehdr->e_entry = 0;
|
||||
ehdr->e_phoff = sizeof(Elf64_Ehdr);
|
||||
ehdr->e_shoff = 0;
|
||||
@@ -41,9 +37,197 @@ void *ehdr_init(Elf64_Ehdr *ehdr, Elf64_Half phnum)
|
||||
return ehdr + 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize ELF note
|
||||
*/
|
||||
bool ehdr_is_elf_object(const Elf64_Ehdr *ehdr)
|
||||
{
|
||||
return (ehdr->e_version == ELF_VERSION_1) && (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) == 0);
|
||||
}
|
||||
|
||||
bool ehdr_is_vmcore(const Elf64_Ehdr *ehdr)
|
||||
{
|
||||
return ehdr->e_type == ET_CORE;
|
||||
}
|
||||
|
||||
bool ehdr_is_s390x(const Elf64_Ehdr *ehdr)
|
||||
{
|
||||
return ehdr->e_machine == EM_S390 && ehdr->e_ident[EI_DATA] == ELFDATA2MSB &&
|
||||
ehdr->e_ident[EI_CLASS] == ELFCLASS64;
|
||||
}
|
||||
|
||||
int ehdr_check_s390x(const Elf64_Ehdr *ehdr)
|
||||
{
|
||||
if (!ehdr_is_elf_object(ehdr) || !ehdr_is_vmcore(ehdr))
|
||||
return -ENODEV;
|
||||
if (!ehdr_is_s390x(ehdr))
|
||||
ERR_EXIT("Only s390x (64 bit) core dump files are supported");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Elf64_Ehdr *read_elf_hdr(const struct zg_fh *fh)
|
||||
{
|
||||
Elf64_Ehdr *ehdr;
|
||||
const size_t ehdr_size = sizeof(*ehdr);
|
||||
|
||||
if (zg_size(fh) < ehdr_size)
|
||||
return NULL;
|
||||
|
||||
ehdr = util_malloc(ehdr_size);
|
||||
zg_read(fh, ehdr, ehdr_size, ZG_CHECK);
|
||||
return ehdr;
|
||||
}
|
||||
|
||||
Elf64_Phdr *read_elf_phdrs(const struct zg_fh *fh, const Elf64_Ehdr *ehdr, unsigned int *phdr_count)
|
||||
{
|
||||
const Elf64_Half phnum = ehdr->e_phnum;
|
||||
size_t phdrs_size;
|
||||
Elf64_Phdr *phdrs;
|
||||
|
||||
if (!phnum) {
|
||||
*phdr_count = phnum;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ehdr->e_phoff > OFF_T_MAX)
|
||||
ERR_EXIT("Unsupported offset");
|
||||
|
||||
/* Cannot wraparound since `Elf64_Half`` is `uint16_t` */
|
||||
phdrs_size = sizeof(*phdrs) * phnum;
|
||||
phdrs = util_malloc(phdrs_size);
|
||||
zg_seek(fh, (off_t)ehdr->e_phoff, ZG_CHECK);
|
||||
zg_read(fh, phdrs, phdrs_size, ZG_CHECK);
|
||||
*phdr_count = phnum;
|
||||
return phdrs;
|
||||
}
|
||||
|
||||
Elf64_Shdr *read_elf_shdrs(const struct zg_fh *fh, const Elf64_Ehdr *ehdr, unsigned int *shdr_count)
|
||||
{
|
||||
const Elf64_Half shnum = ehdr->e_shnum;
|
||||
size_t shdrs_size;
|
||||
Elf64_Shdr *shdrs;
|
||||
|
||||
if (!shnum) {
|
||||
*shdr_count = shnum;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ehdr->e_shoff > OFF_T_MAX)
|
||||
ERR_EXIT("Unsupported offset");
|
||||
|
||||
/* Cannot wraparound since `Elf64_Half`` is `uint16_t` */
|
||||
shdrs_size = sizeof(*shdrs) * shnum;
|
||||
shdrs = util_malloc(shdrs_size);
|
||||
zg_seek(fh, (off_t)ehdr->e_shoff, ZG_CHECK);
|
||||
zg_read(fh, shdrs, shdrs_size, ZG_CHECK);
|
||||
*shdr_count = shnum;
|
||||
return shdrs;
|
||||
}
|
||||
|
||||
unsigned char *read_elf_section_data(const struct zg_fh *fh, const Elf64_Shdr *shdr, size_t *size,
|
||||
const size_t max_size)
|
||||
{
|
||||
const size_t sh_size = shdr->sh_size;
|
||||
unsigned char *ret;
|
||||
|
||||
if (!sh_size) {
|
||||
*size = sh_size;
|
||||
return NULL;
|
||||
}
|
||||
if (shdr->sh_offset > OFF_T_MAX)
|
||||
ERR_EXIT("Unsupported offset");
|
||||
|
||||
if (sh_size > max_size)
|
||||
ERR_EXIT("Unsupported section size: %#lx > %#lx", sh_size, max_size);
|
||||
|
||||
ret = util_malloc(sh_size);
|
||||
zg_seek(fh, (off_t)shdr->sh_offset, ZG_CHECK);
|
||||
zg_read(fh, ret, sh_size, ZG_CHECK);
|
||||
*size = sh_size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *read_elf_shstrtab(const struct zg_fh *fh, const Elf64_Ehdr *ehdr, const Elf64_Shdr *shdrs,
|
||||
const unsigned int shnum, size_t *shstrtab_size,
|
||||
const size_t max_shstrtab_size)
|
||||
{
|
||||
const size_t shstrndx = ehdr->e_shstrndx;
|
||||
Elf64_Xword tmp_shstrtab_size;
|
||||
Elf64_Off shstrndx_off;
|
||||
char *shstrtab;
|
||||
|
||||
/* file has no section name string table */
|
||||
if (shstrndx == SHN_UNDEF) {
|
||||
*shstrtab_size = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Invalid ELF file */
|
||||
if (shstrndx >= shnum) {
|
||||
util_log_print(UTIL_LOG_ERROR, "Invalid ELF file: invalid string table index\n");
|
||||
*shstrtab_size = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
shstrndx_off = shdrs[shstrndx].sh_offset;
|
||||
if (shstrndx_off > OFF_T_MAX)
|
||||
ERR_EXIT("Unsupported offset");
|
||||
|
||||
tmp_shstrtab_size = shdrs[shstrndx].sh_size;
|
||||
if (tmp_shstrtab_size > max_shstrtab_size)
|
||||
ERR_EXIT("Unsupported shstrtab size: %#lx > %#lx", tmp_shstrtab_size,
|
||||
max_shstrtab_size);
|
||||
|
||||
shstrtab = util_malloc(tmp_shstrtab_size);
|
||||
zg_seek(fh, (off_t)shstrndx_off, ZG_CHECK);
|
||||
zg_read(fh, shstrtab, tmp_shstrtab_size, ZG_CHECK);
|
||||
*shstrtab_size = tmp_shstrtab_size;
|
||||
return shstrtab;
|
||||
}
|
||||
|
||||
const Elf64_Shdr *find_elf_shdr_by_name(const Elf64_Shdr *shdrs, const unsigned int shnum,
|
||||
const char *const shstrtab, const size_t shstrtab_size,
|
||||
const char *name)
|
||||
{
|
||||
const Elf64_Shdr *shdr = shdrs;
|
||||
unsigned int idx;
|
||||
|
||||
for (idx = 0; idx < shnum; idx++, shdr++) {
|
||||
/* Invalid ELF file */
|
||||
if (shdr->sh_name >= shstrtab_size) {
|
||||
util_log_print(UTIL_LOG_ERROR,
|
||||
"Invalid ELF file: section name is too large\n");
|
||||
return NULL;
|
||||
}
|
||||
if (strcmp(name, &shstrtab[shdr->sh_name]) == 0)
|
||||
return shdr;
|
||||
}
|
||||
/* Not found */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int nt_read(const struct zg_fh *fh, const Elf64_Nhdr *note, void *buf, size_t buf_len)
|
||||
{
|
||||
ssize_t nread;
|
||||
|
||||
/* We cannot read more than the current note provides */
|
||||
if (note->n_descsz < buf_len)
|
||||
return -EINVAL;
|
||||
/* Skip note's name and position file at note's descriptor */
|
||||
zg_seek_cur(fh, ELF_NOTE_ROUNDUP(note->n_namesz), ZG_CHECK);
|
||||
/* Read note's descriptor */
|
||||
nread = zg_read(fh, buf, buf_len, ZG_CHECK_ERR);
|
||||
if (nread < 0 || (size_t)nread != buf_len)
|
||||
return -EINVAL;
|
||||
/* Skip the rest of note's descriptor until the next note */
|
||||
zg_seek_cur(fh, ELF_NOTE_ROUNDUP(note->n_descsz) - buf_len, ZG_CHECK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nt_skip(const struct zg_fh *fh, const Elf64_Nhdr *note)
|
||||
{
|
||||
/* Skip note's name + descriptor and position file at the next note */
|
||||
zg_seek_cur(fh, ELF_NOTE_ROUNDUP(note->n_namesz) + ELF_NOTE_ROUNDUP(note->n_descsz),
|
||||
ZG_CHECK);
|
||||
}
|
||||
|
||||
void *nt_init(void *buf, Elf64_Word type, const void *desc, int d_len,
|
||||
const char *name)
|
||||
{
|
||||
@@ -57,17 +241,14 @@ void *nt_init(void *buf, Elf64_Word type, const void *desc, int d_len,
|
||||
len = sizeof(Elf64_Nhdr);
|
||||
|
||||
memcpy(buf + len, name, note->n_namesz);
|
||||
len = ROUNDUP(len + note->n_namesz, 4);
|
||||
len = ELF_NOTE_ROUNDUP(len + note->n_namesz);
|
||||
|
||||
memcpy(buf + len, desc, note->n_descsz);
|
||||
len = ROUNDUP(len + note->n_descsz, 4);
|
||||
len = ELF_NOTE_ROUNDUP(len + note->n_descsz);
|
||||
|
||||
return PTR_ADD(buf, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize prstatus note
|
||||
*/
|
||||
void *nt_prstatus(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
struct nt_prstatus_64 nt_prstatus;
|
||||
@@ -84,9 +265,6 @@ void *nt_prstatus(void *ptr, const struct dfi_cpu *cpu)
|
||||
NOTE_NAME_CORE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize fpregset (floating point) note
|
||||
*/
|
||||
void *nt_fpregset(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
struct nt_fpregset_64 nt_fpregset;
|
||||
@@ -99,72 +277,53 @@ void *nt_fpregset(void *ptr, const struct dfi_cpu *cpu)
|
||||
NOTE_NAME_CORE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize timer note
|
||||
*/
|
||||
void *nt_s390_timer(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_TIMER, &cpu->timer, sizeof(cpu->timer),
|
||||
NOTE_NAME_LINUX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize TOD clock comparator note
|
||||
*/
|
||||
void *nt_s390_tod_cmp(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_TODCMP, &cpu->todcmp,
|
||||
sizeof(cpu->todcmp), NOTE_NAME_LINUX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize TOD programmable register note
|
||||
*/
|
||||
void *nt_s390_tod_preg(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_TODPREG, &cpu->todpreg,
|
||||
sizeof(cpu->todpreg), NOTE_NAME_LINUX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize control register note
|
||||
*/
|
||||
void *nt_s390_ctrs(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_CTRS, &cpu->ctrs, sizeof(cpu->ctrs),
|
||||
NOTE_NAME_LINUX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize prefix register note
|
||||
*/
|
||||
void *nt_s390_prefix(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_PREFIX, &cpu->prefix,
|
||||
sizeof(cpu->prefix), NOTE_NAME_LINUX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize vxrs_low register note
|
||||
*/
|
||||
void *nt_s390_vxrs_low(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_VXRS_LOW, &cpu->vxrs_low,
|
||||
sizeof(cpu->vxrs_low), NOTE_NAME_LINUX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize vxrs_high register note
|
||||
*/
|
||||
void *nt_s390_vxrs_high(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_VXRS_HIGH, &cpu->vxrs_high,
|
||||
sizeof(cpu->vxrs_high), NOTE_NAME_LINUX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize prpsinfo note
|
||||
*/
|
||||
void *nt_s390_gs_cb(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_GS_CB, &cpu->gscb, sizeof(cpu->gscb), NOTE_NAME_LINUX);
|
||||
}
|
||||
|
||||
void *nt_prpsinfo(void *ptr)
|
||||
{
|
||||
struct nt_prpsinfo_64 prpsinfo;
|
||||
@@ -179,9 +338,6 @@ void *nt_prpsinfo(void *ptr)
|
||||
NOTE_NAME_CORE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize vmcoreinfo note
|
||||
*/
|
||||
void *nt_vmcoreinfo(void *ptr, const char *vmcoreinfo)
|
||||
{
|
||||
if (!vmcoreinfo)
|
||||
@@ -189,3 +345,22 @@ void *nt_vmcoreinfo(void *ptr, const char *vmcoreinfo)
|
||||
return nt_init(ptr, 0, vmcoreinfo, strlen(vmcoreinfo),
|
||||
NOTE_NAME_VMCOREINFO);
|
||||
}
|
||||
|
||||
/* Keep in sync with `struct dfi_cpu` */
|
||||
size_t get_max_note_size_per_cpu(void)
|
||||
{
|
||||
size_t size = 0;
|
||||
|
||||
size += ELF64_NOTE_SIZE(NOTE_NAME_CORE, sizeof(struct nt_prstatus_64));
|
||||
size += ELF64_NOTE_SIZE(NOTE_NAME_CORE, sizeof(struct nt_fpregset_64));
|
||||
size += ELF64_NOTE_SIZE(NOTE_NAME_LINUX, sizeof_field(struct dfi_cpu, timer));
|
||||
size += ELF64_NOTE_SIZE(NOTE_NAME_LINUX, sizeof_field(struct dfi_cpu, todcmp));
|
||||
size += ELF64_NOTE_SIZE(NOTE_NAME_LINUX, sizeof_field(struct dfi_cpu, todpreg));
|
||||
size += ELF64_NOTE_SIZE(NOTE_NAME_LINUX, sizeof_field(struct dfi_cpu, ctrs));
|
||||
size += ELF64_NOTE_SIZE(NOTE_NAME_LINUX, sizeof_field(struct dfi_cpu, prefix));
|
||||
size += ELF64_NOTE_SIZE(NOTE_NAME_LINUX, sizeof_field(struct dfi_cpu, vxrs_low));
|
||||
size += ELF64_NOTE_SIZE(NOTE_NAME_LINUX, sizeof_field(struct dfi_cpu, vxrs_high));
|
||||
size += ELF64_NOTE_SIZE(NOTE_NAME_LINUX, sizeof_field(struct dfi_cpu, gscb));
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
160
zdump/df_elf.h
160
zdump/df_elf.h
@@ -14,12 +14,17 @@
|
||||
|
||||
#include <elf.h>
|
||||
#include <linux/types.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
#include "zg.h"
|
||||
#include "dfi.h"
|
||||
|
||||
#ifndef ELF_VERSION_1
|
||||
#define ELF_VERSION_1 0x1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* S390 CPU timer note (u64)
|
||||
*/
|
||||
@@ -69,6 +74,29 @@
|
||||
#define NT_S390_VXRS_HIGH 0x30a
|
||||
#endif
|
||||
|
||||
/*
|
||||
* S390 guarded-storage registers note (4 * u64)
|
||||
*/
|
||||
#ifndef NT_S390_GS_CB
|
||||
#define NT_S390_GS_CB 0x30b
|
||||
#endif
|
||||
|
||||
/*
|
||||
* S390 confidential CPU data from secure guest.
|
||||
*/
|
||||
#ifndef NT_S390_PV_CPU_DATA
|
||||
#define NT_S390_PV_CPU_DATA 0x30e
|
||||
#endif
|
||||
|
||||
#define NOTE_NAME_CORE "CORE"
|
||||
#define NOTE_NAME_LINUX "LINUX"
|
||||
#define NOTE_NAME_VMCOREINFO "VMCOREINFO"
|
||||
|
||||
#define ELF_NOTE_ROUNDUP(size) ROUNDUP(size, 4)
|
||||
#define ELF_NOTE_NAME_SIZE(name) ELF_NOTE_ROUNDUP(name ? strlen(name) + 1 : 0)
|
||||
#define ELF64_NOTE_SIZE(name, desc_size) \
|
||||
(sizeof(Elf64_Nhdr) + ELF_NOTE_NAME_SIZE(name) + ELF_NOTE_ROUNDUP(desc_size))
|
||||
|
||||
/*
|
||||
* prstatus ELF Note
|
||||
*/
|
||||
@@ -116,20 +144,152 @@ static inline void df_elf_ensure_s390x(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize ELF header
|
||||
*/
|
||||
void *ehdr_init(Elf64_Ehdr *ehdr, Elf64_Half phnum);
|
||||
|
||||
/*
|
||||
* Checks whether @ehdr is a ELF object
|
||||
*/
|
||||
bool ehdr_is_elf_object(const Elf64_Ehdr *ehdr);
|
||||
|
||||
/*
|
||||
* Checks whether @ehdr is a vmcore
|
||||
*/
|
||||
bool ehdr_is_vmcore(const Elf64_Ehdr *ehdr);
|
||||
|
||||
/*
|
||||
* Checks whether @ehdr is a s390x header
|
||||
*/
|
||||
bool ehdr_is_s390x(const Elf64_Ehdr *ehdr);
|
||||
|
||||
/*
|
||||
* Verify that given header is a s390x ELF header
|
||||
*/
|
||||
int ehdr_check_s390x(const Elf64_Ehdr *ehdr);
|
||||
|
||||
/*
|
||||
* Read ELF header at current offset
|
||||
*/
|
||||
Elf64_Ehdr *read_elf_hdr(const struct zg_fh *fh);
|
||||
|
||||
/*
|
||||
* Read ELF program headers
|
||||
*
|
||||
* To read the program headers the offset of @fh is changed.
|
||||
*/
|
||||
Elf64_Phdr *read_elf_phdrs(const struct zg_fh *fh, const Elf64_Ehdr *ehdr,
|
||||
unsigned int *phdr_count);
|
||||
|
||||
/*
|
||||
* Read ELF section headers
|
||||
*
|
||||
* To read the section headers the offset of @fh is changed.
|
||||
*/
|
||||
Elf64_Shdr *read_elf_shdrs(const struct zg_fh *fh, const Elf64_Ehdr *ehdr,
|
||||
unsigned int *shdr_count);
|
||||
|
||||
/*
|
||||
* Read ELF section content
|
||||
*
|
||||
* To read the section content the offset of @fh is changed.
|
||||
*/
|
||||
unsigned char *read_elf_section_data(const struct zg_fh *fh, const Elf64_Shdr *shdr, size_t *size,
|
||||
const size_t max_size);
|
||||
|
||||
/*
|
||||
* Read ELF section header string table
|
||||
*
|
||||
* To read the section header string table the offset of @fh is changed.
|
||||
*/
|
||||
char *read_elf_shstrtab(const struct zg_fh *fh, const Elf64_Ehdr *ehdr, const Elf64_Shdr *shdrs,
|
||||
const unsigned int shnum, size_t *shstrtab_size,
|
||||
const size_t max_shstrtab_size);
|
||||
|
||||
/*
|
||||
* Find ELF section header by section name
|
||||
*/
|
||||
const Elf64_Shdr *find_elf_shdr_by_name(const Elf64_Shdr *shdrs, const unsigned int shnum,
|
||||
const char *shstrtab, const size_t shstrtab_size,
|
||||
const char *name);
|
||||
|
||||
/*
|
||||
* Read note and store the note description in @buf
|
||||
*/
|
||||
int nt_read(const struct zg_fh *fh, const Elf64_Nhdr *note, void *buf, size_t buf_len);
|
||||
|
||||
/*
|
||||
* Skip note
|
||||
*/
|
||||
void nt_skip(const struct zg_fh *fh, const Elf64_Nhdr *note);
|
||||
|
||||
/*
|
||||
* Initialize ELF note
|
||||
*/
|
||||
void *nt_init(void *buf, Elf64_Word type, const void *desc, int d_len,
|
||||
const char *name);
|
||||
|
||||
/*
|
||||
* Initialize prstatus note
|
||||
*/
|
||||
void *nt_prstatus(void *ptr, const struct dfi_cpu *cpu);
|
||||
|
||||
/*
|
||||
* Initialize fpregset (floating point) note
|
||||
*/
|
||||
void *nt_fpregset(void *ptr, const struct dfi_cpu *cpu);
|
||||
|
||||
/*
|
||||
* Initialize timer note
|
||||
*/
|
||||
void *nt_s390_timer(void *ptr, const struct dfi_cpu *cpu);
|
||||
|
||||
/*
|
||||
* Initialize TOD clock comparator note
|
||||
*/
|
||||
void *nt_s390_tod_cmp(void *ptr, const struct dfi_cpu *cpu);
|
||||
|
||||
/*
|
||||
* Initialize TOD programmable register note
|
||||
*/
|
||||
void *nt_s390_tod_preg(void *ptr, const struct dfi_cpu *cpu);
|
||||
|
||||
/*
|
||||
* Initialize control register note
|
||||
*/
|
||||
void *nt_s390_ctrs(void *ptr, const struct dfi_cpu *cpu);
|
||||
|
||||
/*
|
||||
* Initialize prefix register note
|
||||
*/
|
||||
void *nt_s390_prefix(void *ptr, const struct dfi_cpu *cpu);
|
||||
|
||||
/*
|
||||
* Initialize vxrs_low register note
|
||||
*/
|
||||
void *nt_s390_vxrs_low(void *ptr, const struct dfi_cpu *cpu);
|
||||
|
||||
/*
|
||||
* Initialize vxrs_high register note
|
||||
*/
|
||||
void *nt_s390_vxrs_high(void *ptr, const struct dfi_cpu *cpu);
|
||||
|
||||
/*
|
||||
* Initialize guarded-storage registers note
|
||||
*/
|
||||
void *nt_s390_gs_cb(void *ptr, const struct dfi_cpu *cpu);
|
||||
|
||||
/*
|
||||
* Initialize prpsinfo note
|
||||
*/
|
||||
void *nt_prpsinfo(void *ptr);
|
||||
|
||||
/*
|
||||
* Initialize vmcoreinfo note
|
||||
*/
|
||||
void *nt_vmcoreinfo(void *ptr, const char *vmcoreinfo);
|
||||
|
||||
size_t get_max_note_size_per_cpu(void);
|
||||
|
||||
#endif /* DF_ELF_H */
|
||||
|
||||
10
zdump/dfi.c
10
zdump/dfi.c
@@ -26,6 +26,7 @@
|
||||
* DFI vector - ensure that tape is the first in the list and devmem the second!
|
||||
*/
|
||||
static struct dfi *dfi_vec[] = {
|
||||
/* clang-format off */
|
||||
&dfi_s390tape,
|
||||
&dfi_devmem,
|
||||
&dfi_s390mv_ext,
|
||||
@@ -33,11 +34,13 @@ static struct dfi *dfi_vec[] = {
|
||||
&dfi_s390_ext,
|
||||
&dfi_s390,
|
||||
&dfi_lkcd,
|
||||
&dfi_pv_elf,
|
||||
&dfi_elf,
|
||||
&dfi_kdump,
|
||||
&dfi_kdump_flat,
|
||||
&dfi_ngdump,
|
||||
NULL,
|
||||
/* clang-format on */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -168,6 +171,11 @@ struct dfi_cpu *dfi_cpu_alloc(void)
|
||||
return zg_alloc(sizeof(struct dfi_cpu));
|
||||
}
|
||||
|
||||
void dfi_cpu_free(struct dfi_cpu *cpu)
|
||||
{
|
||||
zg_free(cpu);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add DFI CPU
|
||||
*/
|
||||
@@ -780,7 +788,7 @@ int dfi_init(void)
|
||||
}
|
||||
util_log_print(UTIL_LOG_DEBUG, "DFI %s returned with rc %d\n",
|
||||
dfi->name, rc);
|
||||
if (rc == 0 || rc == -EINVAL)
|
||||
if (rc == 0 || rc == -EINVAL || rc == -ENOKEY)
|
||||
return rc;
|
||||
zg_close(g.fh);
|
||||
i++;
|
||||
|
||||
14
zdump/dfi.h
14
zdump/dfi.h
@@ -99,6 +99,15 @@ struct dfi_cpu {
|
||||
u32 todpreg;
|
||||
u64 vxrs_low[16];
|
||||
struct dfi_vxrs vxrs_high[16];
|
||||
union {
|
||||
u64 gscb[4];
|
||||
struct {
|
||||
u64 reserved;
|
||||
u64 gsd;
|
||||
u64 gssm;
|
||||
u64 gs_epl_a;
|
||||
};
|
||||
};
|
||||
u16 cpu_id;
|
||||
};
|
||||
|
||||
@@ -127,7 +136,8 @@ enum dfi_cpu_content {
|
||||
DFI_CPU_CONTENT_ALL, /* Complete register information available */
|
||||
};
|
||||
|
||||
#define DFI_CPU_CONTENT_FAC_VX 0x00000001
|
||||
#define DFI_CPU_CONTENT_FAC_VX 0x00000001
|
||||
#define DFI_CPU_CONTENT_FAC_GS 0x00000002
|
||||
int dfi_cpu_content_fac_check(int flags);
|
||||
void dfi_cpu_content_fac_add(int flags);
|
||||
|
||||
@@ -137,6 +147,7 @@ void dfi_cpu_content_fac_add(int flags);
|
||||
struct util_list *dfi_cpu_list(void);
|
||||
void dfi_cpu_info_init(enum dfi_cpu_content content);
|
||||
struct dfi_cpu *dfi_cpu_alloc(void);
|
||||
void dfi_cpu_free(struct dfi_cpu *cpu);
|
||||
struct dfi_cpu *dfi_cpu(unsigned int cpu_nr);
|
||||
void dfi_cpu_add(struct dfi_cpu *cpu);
|
||||
unsigned int dfi_cpu_cnt(void);
|
||||
@@ -238,6 +249,7 @@ extern struct dfi dfi_s390mv_ext;
|
||||
extern struct dfi dfi_s390;
|
||||
extern struct dfi dfi_s390_ext;
|
||||
extern struct dfi dfi_lkcd;
|
||||
extern struct dfi dfi_pv_elf;
|
||||
extern struct dfi dfi_elf;
|
||||
extern struct dfi dfi_kdump;
|
||||
extern struct dfi dfi_kdump_flat;
|
||||
|
||||
235
zdump/dfi_elf.c
235
zdump/dfi_elf.c
@@ -24,6 +24,7 @@
|
||||
#include "df_elf.h"
|
||||
#include "dfi.h"
|
||||
#include "dfi_mem_chunk.h"
|
||||
#include "dfi_elf_common.h"
|
||||
|
||||
/*
|
||||
* Read memory for given memory chunk
|
||||
@@ -37,77 +38,6 @@ static void dfi_elf_mem_chunk_read_fn(struct dfi_mem_chunk *mem_chunk, u64 off,
|
||||
zg_read(g.fh, buf, cnt, ZG_CHECK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add load (memory chunk) to DFI dump
|
||||
*/
|
||||
static int pt_load_add(Elf64_Phdr *phdr)
|
||||
{
|
||||
u64 *off_ptr;
|
||||
|
||||
util_log_print(UTIL_LOG_DEBUG,
|
||||
"DFI ELF p_paddr 0x%016lx p_vaddr 0x%016lx p_offset 0x%016lx p_filesz 0x%016lx p_memsz 0x%016lx\n",
|
||||
phdr->p_paddr, phdr->p_vaddr, phdr->p_offset,
|
||||
phdr->p_filesz, phdr->p_memsz);
|
||||
|
||||
if (phdr->p_paddr != phdr->p_vaddr) {
|
||||
phdr->p_paddr = phdr->p_vaddr;
|
||||
STDERR("Dump file \"%s\" is a user space core dump\n",
|
||||
g.opts.device);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (phdr->p_memsz == 0)
|
||||
return -EINVAL;
|
||||
if (phdr->p_offset + phdr->p_filesz > zg_size(g.fh))
|
||||
return -EINVAL;
|
||||
if (phdr->p_filesz > phdr->p_memsz)
|
||||
return -EINVAL;
|
||||
if (phdr->p_filesz > 0) {
|
||||
off_ptr = zg_alloc(sizeof(*off_ptr));
|
||||
*off_ptr = phdr->p_offset;
|
||||
dfi_mem_chunk_add(phdr->p_paddr, phdr->p_filesz, off_ptr,
|
||||
dfi_elf_mem_chunk_read_fn, zg_free);
|
||||
}
|
||||
if (phdr->p_memsz - phdr->p_filesz > 0) {
|
||||
/* Add zero memory chunk */
|
||||
dfi_mem_chunk_add(phdr->p_paddr + phdr->p_filesz,
|
||||
phdr->p_memsz - phdr->p_filesz, NULL,
|
||||
dfi_mem_chunk_read_zero, NULL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read note
|
||||
*/
|
||||
static int nt_read(const Elf64_Nhdr *note, void *buf, size_t buf_len)
|
||||
{
|
||||
ssize_t nread;
|
||||
|
||||
/* We cannot read more than the current note provides */
|
||||
if (note->n_descsz < buf_len)
|
||||
return -EINVAL;
|
||||
/* Skip note's name and position file at note's descriptor */
|
||||
zg_seek_cur(g.fh, ROUNDUP(note->n_namesz, 4), ZG_CHECK);
|
||||
/* Read note's descriptor */
|
||||
nread = zg_read(g.fh, buf, buf_len, ZG_CHECK_ERR);
|
||||
if (nread < 0 || (size_t)nread != buf_len)
|
||||
return -EINVAL;
|
||||
/* Skip the rest of note's descriptor until the next note */
|
||||
zg_seek_cur(g.fh, ROUNDUP(note->n_descsz, 4) - buf_len, ZG_CHECK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Skip note
|
||||
*/
|
||||
static void nt_skip(const Elf64_Nhdr *note)
|
||||
{
|
||||
/* Skip note's name + descriptor and position file at the next note */
|
||||
zg_seek_cur(g.fh,
|
||||
ROUNDUP(note->n_namesz, 4) + ROUNDUP(note->n_descsz, 4),
|
||||
ZG_CHECK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure that CPU is already defined by prstatus note
|
||||
*/
|
||||
@@ -121,12 +51,12 @@ static void check_cpu(struct dfi_cpu *cpu, const char *note_str)
|
||||
/*
|
||||
* Read prstatus note and return new DFI CPU
|
||||
*/
|
||||
static struct dfi_cpu *nt_prstatus_read(Elf64_Nhdr *note)
|
||||
static struct dfi_cpu *nt_prstatus_read(const struct zg_fh *fh, const Elf64_Nhdr *note)
|
||||
{
|
||||
struct dfi_cpu *cpu = dfi_cpu_alloc();
|
||||
struct nt_prstatus_64 nt_prstatus;
|
||||
|
||||
if (nt_read(note, &nt_prstatus, sizeof(nt_prstatus)))
|
||||
if (nt_read(fh, note, &nt_prstatus, sizeof(nt_prstatus)))
|
||||
return NULL;
|
||||
|
||||
memcpy(cpu->gprs, &nt_prstatus.gprs, sizeof(cpu->gprs));
|
||||
@@ -140,12 +70,12 @@ static struct dfi_cpu *nt_prstatus_read(Elf64_Nhdr *note)
|
||||
/*
|
||||
* Read fpregset note
|
||||
*/
|
||||
static int nt_fpregset_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
||||
static int nt_fpregset_read(const struct zg_fh *fh, struct dfi_cpu *cpu, const Elf64_Nhdr *note)
|
||||
{
|
||||
struct nt_fpregset_64 nt_fpregset;
|
||||
|
||||
check_cpu(cpu, "FPREGSET");
|
||||
if (nt_read(note, &nt_fpregset, sizeof(nt_fpregset)))
|
||||
if (nt_read(fh, note, &nt_fpregset, sizeof(nt_fpregset)))
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(&cpu->fpc, &nt_fpregset.fpc, sizeof(cpu->fpc));
|
||||
@@ -156,195 +86,208 @@ static int nt_fpregset_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
||||
/*
|
||||
* Read s390 timer note
|
||||
*/
|
||||
static int nt_s390_timer_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
||||
static int nt_s390_timer_read(const struct zg_fh *fh, struct dfi_cpu *cpu, const Elf64_Nhdr *note)
|
||||
{
|
||||
check_cpu(cpu, "S390_TIMER");
|
||||
return nt_read(note, &cpu->timer, sizeof(cpu->timer));
|
||||
return nt_read(fh, note, &cpu->timer, sizeof(cpu->timer));
|
||||
}
|
||||
|
||||
/*
|
||||
* Read s390 todcmp note
|
||||
*/
|
||||
static int nt_s390_todcmp_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
||||
static int nt_s390_todcmp_read(const struct zg_fh *fh, struct dfi_cpu *cpu, const Elf64_Nhdr *note)
|
||||
{
|
||||
check_cpu(cpu, "S390_TODCMP");
|
||||
return nt_read(note, &cpu->todcmp, sizeof(cpu->todcmp));
|
||||
return nt_read(fh, note, &cpu->todcmp, sizeof(cpu->todcmp));
|
||||
}
|
||||
|
||||
/*
|
||||
* Read s390 todpreg note
|
||||
*/
|
||||
static int nt_s390_todpreg_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
||||
static int nt_s390_todpreg_read(const struct zg_fh *fh, struct dfi_cpu *cpu, const Elf64_Nhdr *note)
|
||||
{
|
||||
check_cpu(cpu, "S390_TODPREG");
|
||||
return nt_read(note, &cpu->todpreg, sizeof(cpu->todpreg));
|
||||
return nt_read(fh, note, &cpu->todpreg, sizeof(cpu->todpreg));
|
||||
}
|
||||
|
||||
/*
|
||||
* Read s390 ctrs note
|
||||
*/
|
||||
static int nt_s390_ctrs_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
||||
static int nt_s390_ctrs_read(const struct zg_fh *fh, struct dfi_cpu *cpu, const Elf64_Nhdr *note)
|
||||
{
|
||||
check_cpu(cpu, "S390_CTRS");
|
||||
return nt_read(note, &cpu->ctrs, sizeof(cpu->ctrs));
|
||||
return nt_read(fh, note, &cpu->ctrs, sizeof(cpu->ctrs));
|
||||
}
|
||||
|
||||
/*
|
||||
* Read s390 prefix note
|
||||
*/
|
||||
static int nt_s390_prefix_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
||||
static int nt_s390_prefix_read(const struct zg_fh *fh, struct dfi_cpu *cpu, const Elf64_Nhdr *note)
|
||||
{
|
||||
check_cpu(cpu, "S390_PREFIX");
|
||||
return nt_read(note, &cpu->prefix, sizeof(cpu->prefix));
|
||||
return nt_read(fh, note, &cpu->prefix, sizeof(cpu->prefix));
|
||||
}
|
||||
|
||||
/*
|
||||
* Read s390 vxrs_low note
|
||||
*/
|
||||
static int nt_s390_vxrs_low_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
||||
static int nt_s390_vxrs_low_read(const struct zg_fh *fh, struct dfi_cpu *cpu,
|
||||
const Elf64_Nhdr *note)
|
||||
{
|
||||
check_cpu(cpu, "S390_VXRS_LOW");
|
||||
return nt_read(note, &cpu->vxrs_low, sizeof(cpu->vxrs_low));
|
||||
return nt_read(fh, note, &cpu->vxrs_low, sizeof(cpu->vxrs_low));
|
||||
}
|
||||
|
||||
/*
|
||||
* Read s390 vxrs_high note
|
||||
*/
|
||||
static int nt_s390_vxrs_high_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
||||
static int nt_s390_vxrs_high_read(const struct zg_fh *fh, struct dfi_cpu *cpu,
|
||||
const Elf64_Nhdr *note)
|
||||
{
|
||||
check_cpu(cpu, "S390_VXRS_HIGH");
|
||||
return nt_read(note, &cpu->vxrs_high, sizeof(cpu->vxrs_high));
|
||||
return nt_read(fh, note, &cpu->vxrs_high, sizeof(cpu->vxrs_high));
|
||||
}
|
||||
|
||||
/*
|
||||
* Read s390 gs_cb note
|
||||
*/
|
||||
static int nt_s390_gs_cb_read(const struct zg_fh *fh, struct dfi_cpu *cpu, const Elf64_Nhdr *note)
|
||||
{
|
||||
check_cpu(cpu, "S390_GSCB");
|
||||
return nt_read(fh, note, &cpu->gscb, sizeof(cpu->gscb));
|
||||
}
|
||||
|
||||
/*
|
||||
* Add all notes for notes phdr
|
||||
*/
|
||||
static int pt_notes_add(Elf64_Phdr *phdr)
|
||||
static int pt_notes_add(const Elf64_Phdr *phdr)
|
||||
{
|
||||
struct dfi_cpu *cpu_current = NULL;
|
||||
u64 notes_start_off;
|
||||
Elf64_Nhdr note;
|
||||
const struct zg_fh *fh = g.fh;
|
||||
int rc;
|
||||
|
||||
zg_seek(g.fh, phdr->p_offset, ZG_CHECK);
|
||||
notes_start_off = zg_tell(g.fh, ZG_CHECK);
|
||||
while (zg_tell(g.fh, ZG_CHECK) - notes_start_off < phdr->p_filesz) {
|
||||
rc = zg_read(g.fh, ¬e, sizeof(note), ZG_CHECK_ERR);
|
||||
zg_seek(fh, phdr->p_offset, ZG_CHECK);
|
||||
while (zg_tell(fh, ZG_CHECK) - phdr->p_offset < phdr->p_filesz) {
|
||||
Elf64_Nhdr note;
|
||||
|
||||
rc = zg_read(fh, ¬e, sizeof(note), ZG_CHECK_ERR);
|
||||
if (rc != sizeof(note))
|
||||
return -EINVAL;
|
||||
util_log_print(UTIL_LOG_DEBUG, "DFI ELF n_type 0x%x\n",
|
||||
note.n_type);
|
||||
switch (note.n_type) {
|
||||
case NT_PRSTATUS:
|
||||
cpu_current = nt_prstatus_read(¬e);
|
||||
cpu_current = nt_prstatus_read(fh, ¬e);
|
||||
if (!cpu_current)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case NT_FPREGSET:
|
||||
if (nt_fpregset_read(cpu_current, ¬e))
|
||||
if (nt_fpregset_read(fh, cpu_current, ¬e))
|
||||
return -EINVAL;
|
||||
break;
|
||||
case NT_S390_TIMER:
|
||||
if (nt_s390_timer_read(cpu_current, ¬e))
|
||||
if (nt_s390_timer_read(fh, cpu_current, ¬e))
|
||||
return -EINVAL;
|
||||
break;
|
||||
case NT_S390_TODCMP:
|
||||
if (nt_s390_todcmp_read(cpu_current, ¬e))
|
||||
if (nt_s390_todcmp_read(fh, cpu_current, ¬e))
|
||||
return -EINVAL;
|
||||
break;
|
||||
case NT_S390_TODPREG:
|
||||
if (nt_s390_todpreg_read(cpu_current, ¬e))
|
||||
if (nt_s390_todpreg_read(fh, cpu_current, ¬e))
|
||||
return -EINVAL;
|
||||
break;
|
||||
case NT_S390_CTRS:
|
||||
if (nt_s390_ctrs_read(cpu_current, ¬e))
|
||||
if (nt_s390_ctrs_read(fh, cpu_current, ¬e))
|
||||
return -EINVAL;
|
||||
break;
|
||||
case NT_S390_PREFIX:
|
||||
if (nt_s390_prefix_read(cpu_current, ¬e))
|
||||
if (nt_s390_prefix_read(fh, cpu_current, ¬e))
|
||||
return -EINVAL;
|
||||
break;
|
||||
case NT_S390_VXRS_LOW:
|
||||
if (nt_s390_vxrs_low_read(cpu_current, ¬e))
|
||||
if (nt_s390_vxrs_low_read(fh, cpu_current, ¬e))
|
||||
return -EINVAL;
|
||||
dfi_cpu_content_fac_add(DFI_CPU_CONTENT_FAC_VX);
|
||||
break;
|
||||
case NT_S390_VXRS_HIGH:
|
||||
if (nt_s390_vxrs_high_read(cpu_current, ¬e))
|
||||
if (nt_s390_vxrs_high_read(fh, cpu_current, ¬e))
|
||||
return -EINVAL;
|
||||
dfi_cpu_content_fac_add(DFI_CPU_CONTENT_FAC_VX);
|
||||
break;
|
||||
case NT_S390_GS_CB:
|
||||
if (nt_s390_gs_cb_read(fh, cpu_current, ¬e))
|
||||
return -EINVAL;
|
||||
dfi_cpu_content_fac_add(DFI_CPU_CONTENT_FAC_GS);
|
||||
break;
|
||||
default:
|
||||
nt_skip(¬e);
|
||||
nt_skip(fh, ¬e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read ELF header
|
||||
*/
|
||||
static int read_elf_hdr(Elf64_Ehdr *ehdr)
|
||||
{
|
||||
if (zg_size(g.fh) < sizeof(*ehdr))
|
||||
return -ENODEV;
|
||||
zg_read(g.fh, ehdr, sizeof(*ehdr), ZG_CHECK);
|
||||
if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0)
|
||||
return -ENODEV;
|
||||
if (ehdr->e_type != ET_CORE)
|
||||
return -ENODEV;
|
||||
if (ehdr->e_machine != EM_S390 || ehdr->e_ident[EI_CLASS] != ELFCLASS64)
|
||||
ERR_EXIT("Only s390x (64 bit) core dump files are supported");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize ELF input dump format
|
||||
*/
|
||||
static int dfi_elf_init(void)
|
||||
{
|
||||
Elf64_Ehdr ehdr;
|
||||
Elf64_Phdr *phdr;
|
||||
int i;
|
||||
unsigned int phnum, i;
|
||||
Elf64_Phdr *phdrs;
|
||||
Elf64_Ehdr *ehdr;
|
||||
int rc = -ENODEV;
|
||||
|
||||
util_log_print(UTIL_LOG_DEBUG, "DFI ELF initialization\n");
|
||||
|
||||
if (read_elf_hdr(&ehdr) != 0)
|
||||
ehdr = read_elf_hdr(g.fh);
|
||||
if (!ehdr)
|
||||
return -ENODEV;
|
||||
|
||||
if (ehdr_check_s390x(ehdr) < 0)
|
||||
goto free_ehdr;
|
||||
|
||||
df_elf_ensure_s390x();
|
||||
dfi_arch_set(DFI_ARCH_64);
|
||||
dfi_cpu_info_init(DFI_CPU_CONTENT_ALL);
|
||||
|
||||
phdr = util_malloc(sizeof(*phdr) * ehdr.e_phnum);
|
||||
zg_seek(g.fh, ehdr.e_phoff, ZG_CHECK);
|
||||
zg_read(g.fh, phdr, sizeof(*phdr) * ehdr.e_phnum, ZG_CHECK);
|
||||
util_log_print(UTIL_LOG_DEBUG, "DFI ELF e_phnum %u\n", ehdr.e_phnum);
|
||||
for (i = 0; i < ehdr.e_phnum; i++) {
|
||||
util_log_print(UTIL_LOG_DEBUG, "DFI ELF p_type[%d] 0x%lx\n",
|
||||
i, phdr[i].p_type);
|
||||
switch (phdr[i].p_type) {
|
||||
case PT_LOAD:
|
||||
if (pt_load_add(&phdr[i])) {
|
||||
free(phdr);
|
||||
return -EINVAL;
|
||||
phdrs = read_elf_phdrs(g.fh, ehdr, &phnum);
|
||||
util_log_print(UTIL_LOG_DEBUG, "DFI ELF e_phnum %u\n", phnum);
|
||||
for (i = 0; i < phnum; i++) {
|
||||
const Elf64_Phdr *phdr = &phdrs[i];
|
||||
|
||||
util_log_print(UTIL_LOG_DEBUG, "DFI ELF p_type[%d] 0x%lx\n", i, phdr->p_type);
|
||||
switch (phdr->p_type) {
|
||||
case PT_LOAD: {
|
||||
u64 *off_ptr = zg_alloc(sizeof(*off_ptr));
|
||||
*off_ptr = phdr->p_offset;
|
||||
|
||||
if (pt_load_add(g.fh, phdr, (void **)&off_ptr, dfi_elf_mem_chunk_read_fn,
|
||||
free) < 0) {
|
||||
free(off_ptr);
|
||||
rc = -EINVAL;
|
||||
goto free_phdrs;
|
||||
}
|
||||
break;
|
||||
} break;
|
||||
case PT_NOTE:
|
||||
if (pt_notes_add(&phdr[i])) {
|
||||
free(phdr);
|
||||
return -EINVAL;
|
||||
if (pt_notes_add(phdr)) {
|
||||
rc = -EINVAL;
|
||||
goto free_phdrs;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(phdr);
|
||||
|
||||
dfi_attr_version_set(ehdr.e_ident[EI_VERSION]);
|
||||
return 0;
|
||||
dfi_attr_version_set(ehdr->e_ident[EI_VERSION]);
|
||||
rc = 0;
|
||||
|
||||
free_phdrs:
|
||||
free(phdrs);
|
||||
free_ehdr:
|
||||
free(ehdr);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
61
zdump/dfi_elf_common.c
Normal file
61
zdump/dfi_elf_common.c
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* Common ELF core dump input format definitions
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 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 "dfi_elf_common.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "lib/util_log.h"
|
||||
|
||||
int pt_load_add(const struct zg_fh *fh, const Elf64_Phdr *phdr, void **data,
|
||||
dfi_mem_chunk_read_fn read_fn, dfi_mem_chunk_free_fn free_fn)
|
||||
{
|
||||
assert(fh);
|
||||
assert(phdr);
|
||||
assert(data);
|
||||
assert(read_fn);
|
||||
assert(free_fn);
|
||||
|
||||
util_log_print(
|
||||
UTIL_LOG_DEBUG,
|
||||
"%s p_paddr 0x%016lx p_vaddr 0x%016lx p_offset 0x%016lx p_filesz 0x%016lx p_memsz 0x%016lx\n",
|
||||
__func__, phdr->p_paddr, phdr->p_vaddr, phdr->p_offset, phdr->p_filesz,
|
||||
phdr->p_memsz);
|
||||
|
||||
if (phdr->p_paddr != phdr->p_vaddr) {
|
||||
STDERR("Dump file \"%s\" is a user space core dump\n", fh->path);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (phdr->p_memsz == 0)
|
||||
return -EINVAL;
|
||||
if (phdr->p_offset + phdr->p_filesz > zg_size(fh))
|
||||
return -EINVAL;
|
||||
if (phdr->p_filesz > phdr->p_memsz)
|
||||
return -EINVAL;
|
||||
/* check for wrap-around */
|
||||
if (phdr->p_paddr + phdr->p_filesz < phdr->p_paddr)
|
||||
return -EINVAL;
|
||||
|
||||
if (phdr->p_filesz > 0) {
|
||||
dfi_mem_chunk_add(phdr->p_paddr, phdr->p_filesz, *data, read_fn, free_fn);
|
||||
*data = NULL;
|
||||
} else {
|
||||
/* Free @data directly as it's not used */
|
||||
free_fn(*data);
|
||||
*data = NULL;
|
||||
}
|
||||
if (phdr->p_memsz - phdr->p_filesz > 0) {
|
||||
/* Add zero memory chunk */
|
||||
dfi_mem_chunk_add(phdr->p_paddr + phdr->p_filesz, phdr->p_memsz - phdr->p_filesz,
|
||||
NULL, dfi_mem_chunk_read_zero, NULL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
36
zdump/dfi_elf_common.h
Normal file
36
zdump/dfi_elf_common.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* Common ELF core dump input format definitions
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 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 DFI_ELF_COMMON_H
|
||||
#define DFI_ELF_COMMON_H
|
||||
|
||||
#include <elf.h>
|
||||
|
||||
#include "dfi_mem_chunk.h"
|
||||
#include "zg.h"
|
||||
|
||||
/**
|
||||
* pt_load_add:
|
||||
* @fh: (not nullable): open input file
|
||||
* @phdr: (not nullable): program ELF header of the load segment to add
|
||||
* @data: (not nullable): arbitrary pointer passed to the read callback @read_fn
|
||||
* @read_fn: (not nullable): callback used for reading the data of a memory chunk
|
||||
* @free_fn: (not nullable): function called to cleanup @data
|
||||
*
|
||||
* Add load (memory chunk) to DFI dump. After a successful call, @data belongs
|
||||
* to the mem_chunk structure created and the @data pointer is set to %NULL.
|
||||
*
|
||||
* Returns: %0 on success, returns < 0 in case of an error
|
||||
*/
|
||||
int pt_load_add(const struct zg_fh *fh, const Elf64_Phdr *phdr, void **data,
|
||||
dfi_mem_chunk_read_fn read_fn, dfi_mem_chunk_free_fn free_fn);
|
||||
|
||||
#endif /* DFI_ELF_COMMON_H */
|
||||
347
zdump/dfi_pv_elf.c
Normal file
347
zdump/dfi_pv_elf.c
Normal file
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* PV ELF core dump input format
|
||||
*
|
||||
* 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 <assert.h>
|
||||
#include <elf.h>
|
||||
#include <error.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <elf.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/crypto.h>
|
||||
|
||||
#include "lib/util_log.h"
|
||||
#include "libpv/glib-helper.h"
|
||||
|
||||
#include "pv_defs.h"
|
||||
#include "pv_utils.h"
|
||||
#include "zgetdump.h"
|
||||
#include "zg.h"
|
||||
#include "df_elf.h"
|
||||
#include "dfi.h"
|
||||
#include "dfi_mem_chunk.h"
|
||||
#include "dfi_elf_common.h"
|
||||
#include "opts.h"
|
||||
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(Elf64_Phdr, free);
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(Elf64_Shdr, free);
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(Elf64_Ehdr, free);
|
||||
|
||||
/**
|
||||
* read_elf_section_data_as_gbytes:
|
||||
* @fh: (not nullable): open input file
|
||||
* @shdr: (not nullable): section header of the section to read
|
||||
* @max_size: maximum section data size in bytes
|
||||
*
|
||||
* Try to read section data and return it as #GBytes.
|
||||
*
|
||||
* Returns: #GBytes on success, %NULL if an error occurred
|
||||
*/
|
||||
static GBytes *read_elf_section_data_as_gbytes(const struct zg_fh *fh, const Elf64_Shdr *shdr,
|
||||
const size_t max_size)
|
||||
{
|
||||
unsigned char *data;
|
||||
size_t size;
|
||||
|
||||
data = read_elf_section_data(fh, shdr, &size, max_size);
|
||||
if (!data)
|
||||
return NULL;
|
||||
return g_bytes_new_with_free_func(data, size, free, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* dfi_pv_elf_mem_chunk_read_fn:
|
||||
* @chunk: (not nullable): memory chunk to read from
|
||||
* @chunk_off: offset in the memory chunk to read from
|
||||
* @dst: (not nullable): destination buffer
|
||||
* @size: size in bytes to read
|
||||
*
|
||||
* Memory chunk callback that attempts to read and decrypt up to @size
|
||||
* bytes encrypted memory from @chunk.
|
||||
*/
|
||||
static void dfi_pv_elf_mem_chunk_read_fn(struct dfi_mem_chunk *chunk, u64 chunk_off, void *dst,
|
||||
u64 size)
|
||||
{
|
||||
const pv_elf_ctx_t *ctx = chunk->data;
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
g_assert_nonnull(ctx);
|
||||
|
||||
if (pv_elf_read(ctx, chunk_off, dst, size, &error) < 0)
|
||||
ERR_EXIT(_("Reading encrypted memory failed:" ERR_NEWLINE "%s"), error->message);
|
||||
}
|
||||
|
||||
/**
|
||||
* nt_s390_pv_cpu_data_read:
|
||||
* @fh: (not nullable): open input ELF file
|
||||
* @note_hdr: (not nullable): note header of the note to read
|
||||
* @version: expected PV CPU dump version
|
||||
* @dump_key: (not nullable): key to be used to decrypt data stored in the note
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Read and decrypt PV CPU data of the note using @dump_key.
|
||||
*
|
||||
* Returns: #dfi_cpu_t struct on success, %NULL if an error occurred.
|
||||
*/
|
||||
static dfi_cpu_t *nt_s390_pv_cpu_data_read(const struct zg_fh *fh, const Elf64_Nhdr *note_hdr,
|
||||
const unsigned int version, GBytes *dump_key,
|
||||
GError **error)
|
||||
{
|
||||
const size_t note_descsz = note_hdr->n_descsz;
|
||||
g_autofree uint8_t *note_data = NULL;
|
||||
g_autoptr(GBytes) note = NULL;
|
||||
|
||||
g_assert(dump_key);
|
||||
|
||||
if (note_descsz > PV_MAX_NT_S390_PV_CPU_DATA_SIZE) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_CORRUPTED_NOTE,
|
||||
_("Unable to read confidential CPU data. Dump probably corrupted."));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
note_data = g_malloc(note_descsz);
|
||||
if (nt_read(fh, note_hdr, note_data, note_descsz) < 0) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_CORRUPTED_NOTE,
|
||||
_("Unable to read confidential CPU data. Dump probably corrupted."));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
note = g_bytes_new_take(g_steal_pointer(¬e_data), note_descsz);
|
||||
return pv_decrypt_cpu_note_data(version, note, dump_key, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* pt_notes_add:
|
||||
* @fh: (not nullable): open input file
|
||||
* @phdr: (not nullable): program header of the note segment to add
|
||||
* @version: expected PV CPU dump version
|
||||
* @dump_key: (not nullable): key to be used to decrypt data stored in the notes
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Read all ELF notes for @phdr.
|
||||
*
|
||||
* Returns: %0 on success, -EINVAL if an error occurred.
|
||||
*/
|
||||
static int pt_notes_add(const struct zg_fh *fh, const Elf64_Phdr *phdr, const unsigned int version,
|
||||
GBytes *dump_key, GError **error)
|
||||
{
|
||||
if (phdr->p_offset > OFF_T_MAX) {
|
||||
g_set_error(
|
||||
error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_CORRUPTED_NOTE,
|
||||
_("Confidential CPU data has too large offset. Dump probably corrupted."));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
zg_seek(fh, (off_t)phdr->p_offset, ZG_CHECK);
|
||||
/* Cast to `size_t` is safe because we're using `ZG_CHECK` here. This
|
||||
* means an error in `zg_tell` leads to an exit and therefore `zg_tell`
|
||||
* cannot return an negative value.
|
||||
*/
|
||||
while ((size_t)zg_tell(fh, ZG_CHECK) - phdr->p_offset < phdr->p_filesz) {
|
||||
g_autoptr(dfi_cpu_t) cpu_current = NULL;
|
||||
Elf64_Nhdr note;
|
||||
ssize_t rc;
|
||||
|
||||
rc = zg_read(fh, ¬e, sizeof(note), ZG_CHECK_ERR);
|
||||
if (rc != sizeof(note)) {
|
||||
g_set_error(
|
||||
error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_CORRUPTED_NOTE,
|
||||
_("Confidential CPU data could not be read. Dump probably corrupted."));
|
||||
return -EINVAL;
|
||||
}
|
||||
switch (note.n_type) {
|
||||
case NT_S390_PV_CPU_DATA:
|
||||
cpu_current = nt_s390_pv_cpu_data_read(fh, ¬e, version, dump_key, error);
|
||||
if (!cpu_current)
|
||||
return -EINVAL;
|
||||
dfi_cpu_add(g_steal_pointer(&cpu_current));
|
||||
break;
|
||||
default:
|
||||
util_log_print(UTIL_LOG_WARN, _("Unknown ELF-Note %#x\n"), note.n_type);
|
||||
__attribute__((fallthrough));
|
||||
case NT_PRSTATUS:
|
||||
case NT_FPREGSET:
|
||||
case NT_S390_TIMER:
|
||||
case NT_S390_TODCMP:
|
||||
case NT_S390_TODPREG:
|
||||
case NT_S390_CTRS:
|
||||
case NT_S390_PREFIX:
|
||||
case NT_S390_VXRS_LOW:
|
||||
case NT_S390_VXRS_HIGH:
|
||||
case NT_S390_GS_CB:
|
||||
/* In case of PV ignore all these note types */
|
||||
nt_skip(fh, ¬e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dfi_pv_elf_init:
|
||||
*
|
||||
* Initialize Protected Virtualization ELF input dump format for @g.fh.
|
||||
*
|
||||
* Returns: %0 on success, -ENODEV in case it's not a PV ELF dump and
|
||||
* -EINVAL in case of an error.
|
||||
*/
|
||||
static int dfi_pv_elf_init(void)
|
||||
{
|
||||
const Elf64_Shdr *completion_shdr = NULL, *storage_state_shdr = NULL;
|
||||
g_autoptr(GBytes) key = NULL, dump_key = NULL, completion_sdata = NULL;
|
||||
g_autoptr(storage_state_mmap_t) storage_state_data = NULL;
|
||||
const pv_dump_completion_v1_t *cpl_conf_v1_decr = NULL;
|
||||
g_autoptr(pv_dump_completion_t) cpl_conf_decr = NULL;
|
||||
const char *key_path = g.opts.key_path;
|
||||
g_autoptr(Elf64_Phdr) phdrs = NULL;
|
||||
g_autoptr(Elf64_Shdr) shdrs = NULL;
|
||||
g_autoptr(Elf64_Ehdr) ehdr = NULL;
|
||||
g_autofree char *shstrtab = NULL;
|
||||
g_autoptr(GError) error = NULL;
|
||||
unsigned int cpu_dump_version;
|
||||
const struct zg_fh *fh = g.fh;
|
||||
unsigned int shnum, phnum;
|
||||
bool load_found = false;
|
||||
size_t shstrtab_size;
|
||||
unsigned int i;
|
||||
|
||||
util_log_print(UTIL_LOG_DEBUG, _("DFI %s initialization\n"), dfi_pv_elf.name);
|
||||
ehdr = read_elf_hdr(fh);
|
||||
if (!ehdr)
|
||||
return -ENODEV;
|
||||
|
||||
if (ehdr_check_s390x(ehdr) < 0)
|
||||
return -ENODEV;
|
||||
|
||||
shdrs = read_elf_shdrs(fh, ehdr, &shnum);
|
||||
if (!shdrs)
|
||||
return -ENODEV;
|
||||
|
||||
shstrtab = read_elf_shstrtab(fh, ehdr, shdrs, shnum, &shstrtab_size, PV_MAX_SHSTRTAB_SIZE);
|
||||
if (!shstrtab)
|
||||
return -ENODEV;
|
||||
|
||||
if (!pv_is_pv_elf(shdrs, shnum, shstrtab, shstrtab_size))
|
||||
return -ENODEV;
|
||||
|
||||
df_elf_ensure_s390x();
|
||||
dfi_arch_set(DFI_ARCH_64);
|
||||
dfi_cpu_info_init(DFI_CPU_CONTENT_ALL);
|
||||
|
||||
/* Try to read the customer communication key */
|
||||
if (!key_path)
|
||||
return -ENOKEY;
|
||||
util_log_print(UTIL_LOG_DEBUG, _("Key path: %s\n"), key_path);
|
||||
key = pv_file_get_content_as_secure_bytes(key_path);
|
||||
if (!key)
|
||||
return -ENOKEY;
|
||||
|
||||
/* Find PV completion configuration data and storage state data sections */
|
||||
completion_shdr = find_elf_shdr_by_name(shdrs, shnum, shstrtab, shstrtab_size,
|
||||
PV_ELF_SECTION_NAME_COMPL);
|
||||
if (!completion_shdr)
|
||||
return -EINVAL;
|
||||
|
||||
storage_state_shdr = find_elf_shdr_by_name(shdrs, shnum, shstrtab, shstrtab_size,
|
||||
PV_ELF_SECTION_NAME_TWEAKS);
|
||||
if (!storage_state_shdr)
|
||||
return -EINVAL;
|
||||
|
||||
/* Read the PV completion configuration section data */
|
||||
completion_sdata =
|
||||
read_elf_section_data_as_gbytes(fh, completion_shdr, PV_MAX_COMPL_DATA_SIZE);
|
||||
if (!completion_sdata)
|
||||
return -EINVAL;
|
||||
|
||||
/* Derive and store the dump key in `@dump_key`. This key is used to AES-GCM decrypt the
|
||||
* completion configuration data and store it in `@cpl_conf_decr`. In addition, mmap the
|
||||
* configuration storage state area and use the `tweak_nonce`.
|
||||
*/
|
||||
if (pv_process_section_data(fh->fh, fh->sb.st_size, completion_sdata,
|
||||
storage_state_shdr->sh_offset, storage_state_shdr->sh_size, key,
|
||||
&cpl_conf_decr, &dump_key, &storage_state_data, &error) < 0) {
|
||||
ERR(_("Unable to read decryption information:" ERR_NEWLINE "%s."), error->message);
|
||||
return -EINVAL;
|
||||
}
|
||||
g_assert_nonnull(cpl_conf_decr);
|
||||
g_assert_cmpuint(cpl_conf_decr->version, ==, PV_COMPL_DATA_VERSION_1);
|
||||
|
||||
cpl_conf_v1_decr = (pv_dump_completion_v1_t *)cpl_conf_decr;
|
||||
cpu_dump_version = PV_SEC_CPU_DATA_VERSION_1;
|
||||
phdrs = read_elf_phdrs(fh, ehdr, &phnum);
|
||||
util_log_print(UTIL_LOG_DEBUG, _("DFI %s e_phnum %u\n"), dfi_pv_elf.name, phnum);
|
||||
for (i = 0; i < phnum; i++) {
|
||||
const Elf64_Phdr *phdr = &phdrs[i];
|
||||
|
||||
util_log_print(UTIL_LOG_DEBUG, _("DFI %s p_type[%d] 0x%lx\n"), dfi_pv_elf.name, i,
|
||||
phdr->p_type);
|
||||
switch (phdr->p_type) {
|
||||
case PT_LOAD: {
|
||||
/* Initialize callback data for the `dfi_pv_elf_mem_chunk_read_fn` function
|
||||
*/
|
||||
g_autoptr(pv_elf_ctx_t) elf_ctx =
|
||||
pv_elf_ctx_new(fh->fh, &cpl_conf_v1_decr->data.confidential_area,
|
||||
storage_state_data, phdr->p_offset, &error);
|
||||
if (!elf_ctx) {
|
||||
ERR(_("Reading dump failed:" ERR_NEWLINE "%s"), error->message);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (load_found) {
|
||||
ERR(_("Reading dump failed:" ERR_NEWLINE
|
||||
"Multiple PT_LOAD segments are not supported."));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (pt_load_add(fh, phdr, (void **)&elf_ctx, dfi_pv_elf_mem_chunk_read_fn,
|
||||
(dfi_mem_chunk_free_fn)pv_elf_ctx_free) < 0)
|
||||
return -EINVAL;
|
||||
load_found = true;
|
||||
break;
|
||||
}
|
||||
case PT_NOTE:
|
||||
/* Add CPU information (decrypts the CPU data) */
|
||||
if (pt_notes_add(fh, phdr, cpu_dump_version, dump_key, &error)) {
|
||||
ERR(_("Reading dump failed:" ERR_NEWLINE "%s"), error->message);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
util_log_print(UTIL_LOG_WARN, _("Unknown ELF-PHDR type %#x\n"),
|
||||
phdr->p_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
dfi_attr_version_set(cpl_conf_decr->version);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dfi_pv_elf_cleanup(void)
|
||||
{
|
||||
/* nothing to do here */
|
||||
}
|
||||
|
||||
/*
|
||||
* PV ELF DFI operations
|
||||
*/
|
||||
struct dfi dfi_pv_elf = {
|
||||
.exit = dfi_pv_elf_cleanup,
|
||||
.feat_bits = DFI_FEAT_COPY | DFI_FEAT_SEEK,
|
||||
.init = dfi_pv_elf_init,
|
||||
.name = "ELF (protected virtualization dump)",
|
||||
};
|
||||
@@ -132,7 +132,7 @@ void dfi_vmcoreinfo_init(void)
|
||||
/*
|
||||
* Return vmcoreinfo data
|
||||
*/
|
||||
char *dfi_vmcoreinfo_get(void)
|
||||
const char *dfi_vmcoreinfo_get(void)
|
||||
{
|
||||
return l.vmcoreinfo;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#define DFI_VMCOREINFO_H
|
||||
|
||||
void dfi_vmcoreinfo_init(void);
|
||||
char *dfi_vmcoreinfo_get(void);
|
||||
const char *dfi_vmcoreinfo_get(void);
|
||||
int dfi_vmcoreinfo_tag(char *str, int len, const char *sym);
|
||||
int dfi_vmcoreinfo_symbol(unsigned long *val, const char *sym);
|
||||
int dfi_vmcoreinfo_offset(unsigned long *offs, const char *sym);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user