mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Compare commits
87 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
997343f841 | ||
|
|
6a767408b3 | ||
|
|
3aa5c38714 | ||
|
|
c04a0919f6 | ||
|
|
c803cb925e | ||
|
|
1741ecff96 | ||
|
|
20de1fce2a | ||
|
|
a5af5bcf70 | ||
|
|
7730f2489f | ||
|
|
6dbc5646f9 | ||
|
|
f70991ab1e | ||
|
|
e9ee658492 | ||
|
|
35d5f41232 | ||
|
|
260a0a2428 | ||
|
|
b444e71ee3 | ||
|
|
31d576a595 | ||
|
|
68309ccb7f | ||
|
|
755ea88d5d | ||
|
|
af99efaab2 | ||
|
|
3b55ca085e | ||
|
|
9fdfd1a6dc | ||
|
|
c459ec08c6 | ||
|
|
eb63434f44 | ||
|
|
0eae712cc2 | ||
|
|
e7e9f137c1 | ||
|
|
364cb9d869 | ||
|
|
a64e9cab67 | ||
|
|
74cb6ee40e | ||
|
|
8c99c3878e | ||
|
|
f302f5734b | ||
|
|
3485192791 | ||
|
|
715da84030 | ||
|
|
6e53be736e | ||
|
|
88bf638487 | ||
|
|
182892da12 | ||
|
|
f9e07c3916 | ||
|
|
3aaf3c067e | ||
|
|
801e404160 | ||
|
|
280b8509d1 | ||
|
|
0f56416d82 | ||
|
|
c88e0276c1 | ||
|
|
5b7f08624b | ||
|
|
b94d5e8f87 | ||
|
|
a4f171d4ba | ||
|
|
93f8d093ce | ||
|
|
7e68d7f61a | ||
|
|
7755d35995 | ||
|
|
211431abc2 | ||
|
|
0ae29148f0 | ||
|
|
43cf7873be | ||
|
|
d8cea21e1e | ||
|
|
7093a70b51 | ||
|
|
06984cd9ee | ||
|
|
3a05ab769d | ||
|
|
bcbc69c77f | ||
|
|
b385b8cc38 | ||
|
|
2b282bdacd | ||
|
|
bd2610d275 | ||
|
|
5cf2cefa94 | ||
|
|
984e008127 | ||
|
|
fcfee1aa84 | ||
|
|
b401ad0da0 | ||
|
|
50f909db8f | ||
|
|
d0046257b6 | ||
|
|
daad3bf0e7 | ||
|
|
6537f711a6 | ||
|
|
7c75df9e96 | ||
|
|
376ddfbd22 | ||
|
|
1afa6efb26 | ||
|
|
69c89416b0 | ||
|
|
81e0d02d90 | ||
|
|
d41d968792 | ||
|
|
c81ca8f01b | ||
|
|
73ab25f419 | ||
|
|
6fc1ed1a67 | ||
|
|
1c3547d205 | ||
|
|
b6bb87f377 | ||
|
|
bd0bb9dd5c | ||
|
|
075f7f7186 | ||
|
|
5af1e8cc69 | ||
|
|
145c21a9ca | ||
|
|
1bd8ee4b79 | ||
|
|
4f0dfae97e | ||
|
|
3d6bb988c2 | ||
|
|
8f83002e37 | ||
|
|
d92f419836 | ||
|
|
8ff8b40182 |
52
.gitignore
vendored
52
.gitignore
vendored
@@ -25,7 +25,7 @@ compile_commands.json
|
||||
#
|
||||
**/.detect-openssl.dep.c
|
||||
*.debug
|
||||
*/autocompletion_generator_host
|
||||
**/autocompletion_generator_host
|
||||
ap_tools/ap-check
|
||||
cmsfs-fuse/cmsfs-fuse
|
||||
cpacfstats/cpacfstats
|
||||
@@ -50,12 +50,22 @@ dasdfmt/dasdfmt
|
||||
dasdfmt/_dasdfmt
|
||||
dasdfmt/dasdfmt.bash
|
||||
dasdinfo/dasdinfo
|
||||
dasdinfo/_dasdinfo
|
||||
dasdinfo/dasdinfo.bash
|
||||
dasdview/dasdview
|
||||
dasdview/_dasdview
|
||||
dasdview/dasdview.bash
|
||||
dump2tar/src/dump2tar
|
||||
dump2tar/src/_dump2tar
|
||||
dump2tar/src/dump2tar.bash
|
||||
fdasd/fdasd
|
||||
fdasd/_fdasd
|
||||
fdasd/fdasd.bash
|
||||
hmcdrvfs/hmcdrvfs
|
||||
hsavmcore/check-dep-fuse
|
||||
hsavmcore/hsavmcore
|
||||
hsavmcore/_hsavmcore
|
||||
hsavmcore/hsavmcore.bash
|
||||
hyptop/hyptop
|
||||
ip_watcher/xcec-bridge
|
||||
ipl_tools/chreipl
|
||||
@@ -88,34 +98,58 @@ libutil/*_example
|
||||
libvmcp/vmcp_example
|
||||
libzds/libzds.a
|
||||
lsstp/lsstp
|
||||
lsstp/_lsstp
|
||||
lsstp/lsstp.bash
|
||||
mon_tools/mon_fsstatd
|
||||
mon_tools/mon_procd
|
||||
opticsmon/opticsmon
|
||||
opticsmon/_opticsmon
|
||||
opticsmon/opticsmon.bash
|
||||
osasnmpd/osasnmpd
|
||||
qetharp/qetharp
|
||||
qethqoat/qethqoat
|
||||
systemd/cpacfstatsd.service
|
||||
systemd/iucvtty-login@.service
|
||||
systemd/ttyrun-getty@.service
|
||||
tape390/tape390_crypt
|
||||
tape390/tape390_display
|
||||
tunedasd/src/tunedasd
|
||||
tunedasd/src/_tunedasd
|
||||
tunedasd/src/tunedasd.bash
|
||||
vmcp/vmcp
|
||||
vmcp/_vmcp
|
||||
vmcp/vmcp.bash
|
||||
vmur/vmur
|
||||
zconf/chp/chchp
|
||||
zconf/chp/_chchp
|
||||
zconf/chp/chchp.bash
|
||||
zconf/chp/lschp
|
||||
zconf/chp/_lschp
|
||||
zconf/chp/lschp.bash
|
||||
zconf/chp/chpstat/chpstat
|
||||
zconf/chp/chpstat/_chpstat
|
||||
zconf/chp/chpstat/chpstat.bash
|
||||
zconf/css/lscss
|
||||
zconf/css/_lscss
|
||||
zconf/css/lscss.bash
|
||||
zconf/qeth/lsqeth
|
||||
zconf/qeth/_lsqeth
|
||||
zconf/qeth/lsqeth.bash
|
||||
zconf/scm/lsscm
|
||||
zconf/scm/_lsscm
|
||||
zconf/scm/lsscm.bash
|
||||
zconf/zcrypt/chzcrypt
|
||||
zconf/zcrypt/_chzcrypt
|
||||
zconf/zcrypt/chzcrypt.bash
|
||||
zconf/zcrypt/lszcrypt
|
||||
zconf/zcrypt/_lszcrypt
|
||||
zconf/zcrypt/lszcrypt.bash
|
||||
zconf/zcrypt/zcryptctl
|
||||
zconf/zcrypt/zcryptstats
|
||||
zconf/zcrypt/_zcryptstats
|
||||
zconf/zcrypt/zcryptstats.bash
|
||||
zdev/src/chzdev
|
||||
zdev/src/chzdev_usage.c
|
||||
zdev/src/chzdev_usage.h
|
||||
zdev/src/lszdev
|
||||
zdev/src/lszdev_usage.c
|
||||
zdev/src/lszdev_usage.h
|
||||
zdev/src/zdev_id
|
||||
zdsfs/zdsfs
|
||||
zdump/.check_dep_fuse
|
||||
@@ -139,6 +173,8 @@ zipl/src/chreipl_helper.device-mapper
|
||||
zipl/src/chreipl_helper.md
|
||||
zipl/src/zipl
|
||||
zipl/src/zipl-editenv
|
||||
zipl/src/_zipl-editenv
|
||||
zipl/src/zipl-editenv.bash
|
||||
zipl/src/zipl_helper.device-mapper
|
||||
zipl/src/zipl_helper.md
|
||||
zkey/check-dep-zkey
|
||||
@@ -151,5 +187,11 @@ zkey/kmip/zkey-kmip.so
|
||||
zkey/zkey
|
||||
zkey/zkey-cryptsetup
|
||||
zmemtopo/zmemtopo
|
||||
zmemtopo/_zmemtopo
|
||||
zmemtopo/zmemtopo.bash
|
||||
zpcictl/zpcictl
|
||||
zpcictl/_zpcictl
|
||||
zpcictl/zpcictl.bash
|
||||
zpwr/zpwr
|
||||
zpwr/_zpwr
|
||||
zpwr/zpwr.bash
|
||||
|
||||
@@ -17,6 +17,7 @@ List of all individuals having contributed content to s390-tools
|
||||
- Bjoern Walk
|
||||
- Brian C. Lane
|
||||
- Carsten Otte
|
||||
- Chinmaya Kajagar
|
||||
- Christian Borntraeger
|
||||
- Christian Ehrhardt
|
||||
- Christof Schmitt
|
||||
|
||||
31
CHANGELOG.md
31
CHANGELOG.md
@@ -1,6 +1,37 @@
|
||||
Release history for s390-tools (MIT version)
|
||||
--------------------------------------------
|
||||
|
||||
* __v2.42.0 (2026-04-30)__
|
||||
|
||||
For Linux kernel version: 7.0
|
||||
|
||||
Add new tools / libraries:
|
||||
- Enable zsh and bash autocompletion for various tools
|
||||
- pvebc: Resolve ASR integrity structure for EBC
|
||||
- pvics: Generate SEL guests from base images
|
||||
|
||||
Remove:
|
||||
- tape390_display and tape390_crypt removed due to long gone hardware support
|
||||
- znetcontrolunits: Remove znetcontrolunits library
|
||||
|
||||
Changes of existing tools:
|
||||
- cpumf/pai: Install SIGINT/SIGTERM handler for graceful termination
|
||||
- dbginfo.sh: Add command zmemtopo
|
||||
- libutil/util_fmt: Add support for JSON Lines text format
|
||||
- lstape: Remove 3480 and 3590 tape support
|
||||
- lsznet: Remove support for lcs device type
|
||||
- pvsecret: Add support for ASR integrity structure for EBC
|
||||
- zfcpdbf: Print plogi and prli within open port response as payload
|
||||
- zfcpdbf: Trace all fsf status read buffer fields under HBA
|
||||
- zipl/boot: Add secure boot option to the dump programm
|
||||
- zkey, libekmfweb, libseckey, libkmipclient: Adjust for OpenSSL v4.0.0 API
|
||||
changes and deprecations
|
||||
|
||||
Bug Fixes:
|
||||
- hyptop/opts: Fix long command line option abbreviations
|
||||
- libutil/util_autocomp: Fix default file completion
|
||||
- zipl/boot: Fix stage3 secure boot trailer placement
|
||||
|
||||
* __v2.41.0 (2026-02-16)__
|
||||
|
||||
For Linux kernel version: 6.19
|
||||
|
||||
2
Makefile
2
Makefile
@@ -11,7 +11,7 @@ BASELIB_DIRS = libutil libseckey
|
||||
LIB_DIRS = libvtoc libzds libdasd libccw libvmcp libekmfweb \
|
||||
libkmipclient libcpumf libap libpv libzpci
|
||||
TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \
|
||||
tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf \
|
||||
osasnmpd qetharp ip_watcher qethconf scripts zconf \
|
||||
vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \
|
||||
ziomon iucvterm hyptop cmsfs-fuse qethqoat zfcpdump zdsfs cpumf \
|
||||
systemd hmcdrvfs cpacfstats zdev dump2tar zkey netboot etc zpcictl \
|
||||
|
||||
13
README.md
13
README.md
@@ -30,6 +30,8 @@ Package contents
|
||||
Manage secrets for IBM Secure Execution guests
|
||||
- pvimg:
|
||||
Create and inspect IBM Secure Execution images
|
||||
- pvebc:
|
||||
Verify a secret structure for IBM Secure Execution for Linux.
|
||||
|
||||
* dasdfmt:
|
||||
Low-level format ECKD DASDs with the classical Linux disk layout or the new
|
||||
@@ -50,6 +52,10 @@ Package contents
|
||||
Create an IBM Secure Execution (protected virtualization) image. The
|
||||
genprotimg command is a symbolic link to the `pvimg create` command.
|
||||
|
||||
* pvics:
|
||||
Convert a qcow2 image to a qcow2 image ready to boot as IBM Secure Execution
|
||||
for Linux guest.
|
||||
|
||||
* udev rules:
|
||||
- 59-dasd.rules: rules for unique DASD device nodes created in /dev/disk/.
|
||||
- 57-osasnmpd.rules: udev rules for osasnmpd.
|
||||
@@ -79,13 +85,6 @@ Package contents
|
||||
* qetharp:
|
||||
Read and flush the ARP cache on OSA Express network cards.
|
||||
|
||||
* tape390_display:
|
||||
Display information on the message display facility of a s390 tape
|
||||
device.
|
||||
|
||||
* tape390_crypt:
|
||||
Control and query crypto settings for 3592 tape devices.
|
||||
|
||||
* osasnmpd:
|
||||
NET-SNMP subagent implementing MIBs provided by OSA-Express
|
||||
features Fast Ethernet, Gigabit Ethernet, 10 Gigabit Ethernet.
|
||||
|
||||
@@ -32,7 +32,7 @@ endif
|
||||
# The variable "DISTRELEASE" should be overwritten in rpm spec files with:
|
||||
# "make DISTRELEASE=%{release}" and "make install DISTRELEASE=%{release}"
|
||||
VERSION := 2
|
||||
RELEASE := 41
|
||||
RELEASE := 42
|
||||
PATCHLEVEL := 0
|
||||
DISTRELEASE := build-$(shell date +%Y%m%d)
|
||||
S390_TOOLS_RELEASE := $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE)
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
* Autocompletion generation - for cpumf family of tools
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright IBM Corp. 2025
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
* Copyright IBM Corp.
|
||||
*/
|
||||
|
||||
#include "lib/util_autocomp.h"
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/*
|
||||
* Command line utilities - for chcpumf
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright IBM Corp. 2025
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
* Copyright IBM Corp.
|
||||
*/
|
||||
|
||||
#ifndef CHCPUMF_CLI_H
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/*
|
||||
* Command line utilities - for lscpumf
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright IBM Corp. 2025
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
* Copyright IBM Corp.
|
||||
*/
|
||||
|
||||
#ifndef LSCPUMF_CLI_H
|
||||
|
||||
@@ -700,7 +700,7 @@ static int do_it(char *s)
|
||||
|
||||
if (output_format == FMT_CSV)
|
||||
flags |= FMT_NOMETA;
|
||||
if (output_format == FMT_JSON || output_format == FMT_JSONSEQ)
|
||||
if (util_fmt_is_json(output_format))
|
||||
flags |= FMT_HANDLEINT;
|
||||
if (quote_all)
|
||||
flags |= FMT_QUOTEALL;
|
||||
@@ -708,7 +708,7 @@ static int do_it(char *s)
|
||||
mk_labels();
|
||||
util_fmt_init(stdout, output_format, flags, 1);
|
||||
util_fmt_obj_start(FMT_DEFAULT, "lshwc");
|
||||
if (output_format == FMT_JSON || output_format == FMT_JSONSEQ) {
|
||||
if (util_fmt_is_json(output_format)) {
|
||||
util_fmt_obj_start(FMT_ROW, "cpumcf info");
|
||||
util_fmt_pair(FMT_PERSIST, "counter first", "%d", cfvn);
|
||||
util_fmt_pair(FMT_PERSIST, "counter second", "%d", csvn);
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/*
|
||||
* Command line utilities - for lshwc
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright IBM Corp. 2025
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
* Copyright IBM Corp.
|
||||
*/
|
||||
|
||||
#ifndef LSHWC_CLI_H
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/*
|
||||
* Command line utilities - for lspai
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright IBM Corp. 2025
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
* Copyright IBM Corp.
|
||||
*/
|
||||
|
||||
#ifndef LSPAI_CLI_H
|
||||
|
||||
@@ -138,7 +138,7 @@ Apply quoting to every output element, regardless of content or format.
|
||||
.TP
|
||||
.BR \-f ", " \-\-format \fI\ FORMAT\fP
|
||||
Retrieve output in one of the following formats:
|
||||
JSON, CSV, JSON-SEQ or PAIRS.
|
||||
JSON, CSV, JSON-SEQ, JSONL or PAIRS.
|
||||
If no format is specified, the output defaults to CSV.
|
||||
.
|
||||
.SS JSON Output Structure
|
||||
|
||||
@@ -136,7 +136,7 @@ Default sort order is PAI counter name.
|
||||
.TP
|
||||
.BI \-\-format "\ FORMAT"
|
||||
Retrieve output in one of the following formats:
|
||||
JSON, csv, json-seq or pairs.
|
||||
JSON, csv, json-seq, jsonl or pairs.
|
||||
If no format is specified,
|
||||
the output defaults to a human-readable format.
|
||||
.
|
||||
|
||||
17
cpumf/pai.c
17
cpumf/pai.c
@@ -14,6 +14,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
@@ -47,6 +48,8 @@
|
||||
#define S390_EVT_PAI_CRYPTO 0x1000
|
||||
#define S390_EVT_PAI_NNPA 0x1800
|
||||
|
||||
/* SIGINT or SIGTERM signal received */
|
||||
static volatile unsigned int sigterm;
|
||||
/* Default values for select() timeout: 1 second */
|
||||
static unsigned long read_interval = 1000;
|
||||
/* Size of mapped perf event ring buffer in 4KB pages.
|
||||
@@ -350,6 +353,10 @@ static int collect(unsigned long cnt)
|
||||
if (FD_ISSET(i, &r_fds))
|
||||
readmap(i);
|
||||
}
|
||||
} else if (errno == EINTR && sigterm) {
|
||||
/* Interrupt by signal SIGINT/SIGTERM, one more iteration */
|
||||
cnt = 2;
|
||||
rc = 0;
|
||||
}
|
||||
} while (rc != -1 && --cnt > 0);
|
||||
return rc;
|
||||
@@ -983,6 +990,12 @@ static void setprio(const char *prio)
|
||||
err(EXIT_FAILURE, "Could not set realtime priority");
|
||||
}
|
||||
|
||||
static void sig_handler(int no)
|
||||
{
|
||||
if (no == SIGINT || no == SIGTERM)
|
||||
sigterm = 1;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
bool crypto_record = false, report = false;
|
||||
@@ -1070,6 +1083,10 @@ int main(int argc, char **argv)
|
||||
errx(EXIT_FAILURE, "Invalid argument for runtime");
|
||||
}
|
||||
|
||||
if (signal(SIGTERM, sig_handler) == SIG_ERR ||
|
||||
signal(SIGINT, sig_handler) == SIG_ERR)
|
||||
errx(EXIT_FAILURE, "Failed to set signal handler");
|
||||
|
||||
ev_install(group);
|
||||
ev_enable();
|
||||
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/*
|
||||
* Command line utilities - for pai
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright IBM Corp. 2025
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
* Copyright IBM Corp.
|
||||
*/
|
||||
|
||||
#ifndef PAI_CLI_H
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
* Autocompletion generation - for dasdfmt tool
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright IBM Corp. 2025
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
* Copyright IBM Corp.
|
||||
*/
|
||||
|
||||
#include "lib/util_autocomp.h"
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/*
|
||||
* Command line utilities - for dasdfmt
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright IBM Corp. 2025
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
* Copyright IBM Corp.
|
||||
*/
|
||||
|
||||
#ifndef DASDFMT_CLI_H
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
include ../common.mak
|
||||
|
||||
zsh-completions = _dasdinfo
|
||||
bash-completions = dasdinfo.bash
|
||||
|
||||
include ../common_autocomp.mak
|
||||
|
||||
libs = $(rootdir)/libutil/libutil.a \
|
||||
$(rootdir)/libdasd/libdasd.a
|
||||
|
||||
|
||||
16
dasdinfo/autocompletion_generator_host.c
Normal file
16
dasdinfo/autocompletion_generator_host.c
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright IBM Corp.
|
||||
*/
|
||||
|
||||
#include "lib/util_autocomp.h"
|
||||
|
||||
#include "dasdinfo_cli.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
generate_autocomp(opt_vec, "dasdinfo");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -30,6 +30,8 @@
|
||||
#include "lib/util_path.h"
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
#include "dasdinfo_cli.h"
|
||||
|
||||
#define RD_BUFFER_SIZE 80
|
||||
#define TEMP_DEV_MAX_RETRIES 1000
|
||||
|
||||
@@ -50,49 +52,6 @@ static const struct util_prg prg = {
|
||||
}
|
||||
};
|
||||
|
||||
static struct util_opt opt_vec[] = {
|
||||
UTIL_OPT_SECTION("DEVICE"),
|
||||
{
|
||||
.option = { "block", required_argument, NULL, 'b' },
|
||||
.argument = "BLOCKDEV",
|
||||
.desc = "Block device name, e.g. dasdb",
|
||||
},
|
||||
{
|
||||
.option = { "devnode", required_argument, NULL, 'd' },
|
||||
.argument = "DEVNODE",
|
||||
.desc = "Device node, e.g. /dev/dasda",
|
||||
},
|
||||
{
|
||||
.option = { "busid", required_argument, NULL, 'i' },
|
||||
.argument = "BUSID",
|
||||
.desc = "Bus ID, e.g. 0.0.e910",
|
||||
},
|
||||
UTIL_OPT_SECTION("OPTIONS"),
|
||||
{
|
||||
.option = { "label", no_argument, NULL, 'l' },
|
||||
.desc = "Print DASD volume label (volser)",
|
||||
},
|
||||
{
|
||||
.option = { "uid", no_argument, NULL, 'u' },
|
||||
.desc = "Print DASD uid (without z/VM minidisk token)",
|
||||
},
|
||||
{
|
||||
.option = { "extended-uid", no_argument, NULL, 'x' },
|
||||
.desc = "Print DASD uid (including z/VM minidisk token)",
|
||||
},
|
||||
{
|
||||
.option = { "all", no_argument, NULL, 'a' },
|
||||
.desc = "Same as -u -x -l",
|
||||
},
|
||||
{
|
||||
.option = { "export", no_argument, NULL, 'e' },
|
||||
.desc = "Export ID_BUS, ID_TYPE, ID_SERIAL for use in udev",
|
||||
},
|
||||
UTIL_OPT_HELP,
|
||||
UTIL_OPT_VERSION,
|
||||
UTIL_OPT_END
|
||||
};
|
||||
|
||||
/* needed because ftw can not pass arbitrary arguments */
|
||||
static char *searchbusid;
|
||||
static char *busiddir;
|
||||
@@ -101,9 +60,9 @@ struct volume_label {
|
||||
char volkey[4];
|
||||
char vollbl[4];
|
||||
char volid[6];
|
||||
} __attribute__ ((packed));
|
||||
} __packed;
|
||||
|
||||
static char EBCtoASC[256] = {
|
||||
static char ebc_to_asc[256] = {
|
||||
/* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */
|
||||
0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F,
|
||||
/* 0x08 -GE -SPS -RPT VT FF CR SO SI */
|
||||
@@ -175,7 +134,7 @@ static char *dinfo_ebcdic_dec(char *source, char *target, int l)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < l; i++)
|
||||
target[i] = EBCtoASC[(unsigned char)(source[i])];
|
||||
target[i] = ebc_to_asc[(unsigned char)(source[i])];
|
||||
|
||||
return target;
|
||||
}
|
||||
@@ -261,7 +220,7 @@ static void *dinfo_malloc(size_t size)
|
||||
void *result;
|
||||
|
||||
result = malloc(size);
|
||||
if (result == NULL)
|
||||
if (!result)
|
||||
warnx("Could not allocate %lu bytes of memory", size);
|
||||
|
||||
return result;
|
||||
@@ -274,7 +233,7 @@ static char *dinfo_make_path(char *dirname, char *filename)
|
||||
|
||||
len = strlen(dirname) + strlen(filename) + 2;
|
||||
result = (char *)dinfo_malloc(len);
|
||||
if (result == NULL)
|
||||
if (!result)
|
||||
return NULL;
|
||||
sprintf(result, "%s/%s", dirname, filename);
|
||||
return result;
|
||||
@@ -296,12 +255,12 @@ static int dinfo_create_devnode(dev_t dev, char **devno)
|
||||
|
||||
/* Try several locations for the temporary device node. */
|
||||
for (path = 0; path < ARRAY_SIZE(pathname); path++) {
|
||||
if (pathname[path] == NULL)
|
||||
if (!pathname[path])
|
||||
continue;
|
||||
for (retry = 0; retry < TEMP_DEV_MAX_RETRIES; retry++) {
|
||||
snprintf(filename, sizeof(filename), "dasdinfo%04d", retry);
|
||||
result = dinfo_make_path(pathname[path], filename);
|
||||
if (result == NULL)
|
||||
if (!result)
|
||||
return -1;
|
||||
rc = mknod(result, mode, dev);
|
||||
if (rc == 0) {
|
||||
@@ -342,7 +301,7 @@ static int dinfo_extract_dev(dev_t *dev, char *str)
|
||||
memset(tmp, 0, RD_BUFFER_SIZE);
|
||||
util_strlcpy(tmp, str, RD_BUFFER_SIZE);
|
||||
p = strchr(tmp, ':');
|
||||
if (p == NULL) {
|
||||
if (!p) {
|
||||
warnx("Error: unable to extract major/minor");
|
||||
return -1;
|
||||
}
|
||||
@@ -401,14 +360,14 @@ dinfo_is_busiddir(const char *fpath, const struct stat *UNUSED(sb),
|
||||
return -1;
|
||||
linkdir = util_readlink(tempdir);
|
||||
free(tempdir);
|
||||
if (strstr(linkdir, "dasd") == NULL) {
|
||||
if (!strstr(linkdir, "dasd")) {
|
||||
free(linkdir);
|
||||
return FTW_CONTINUE;
|
||||
}
|
||||
free(linkdir);
|
||||
free(busiddir);
|
||||
busiddir = strdup(fpath);
|
||||
if (busiddir == NULL)
|
||||
if (!busiddir)
|
||||
return -1;
|
||||
return FTW_STOP;
|
||||
}
|
||||
@@ -421,7 +380,7 @@ dinfo_find_entry(const char *dir, const char *searchstring,
|
||||
struct dirent *dir_entry = NULL;
|
||||
|
||||
directory = opendir(dir);
|
||||
if (directory == NULL)
|
||||
if (!directory)
|
||||
return -1;
|
||||
while ((dir_entry = readdir(directory)) != NULL) {
|
||||
/* compare if the found entry has exactly the same name and type
|
||||
@@ -431,7 +390,7 @@ dinfo_find_entry(const char *dir, const char *searchstring,
|
||||
strlen(searchstring)) == 0) &&
|
||||
(dir_entry->d_type & type)) {
|
||||
*result = strdup(dir_entry->d_name);
|
||||
if (*result == NULL)
|
||||
if (!*result)
|
||||
goto out;
|
||||
closedir(directory);
|
||||
return 0; /* found */
|
||||
@@ -477,7 +436,7 @@ dinfo_get_blockdev_from_busid(char *busid, char **blkdev)
|
||||
if (rc != 0)
|
||||
goto out2;
|
||||
*blkdev = strdup(strchr(result, ':') + 1);
|
||||
if (*blkdev == NULL)
|
||||
if (!*blkdev)
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
@@ -510,7 +469,7 @@ static int dinfo_get_uid_from_devnode(char **uidfile, char *devnode)
|
||||
|
||||
path = util_path_sysfs("block/");
|
||||
directory = opendir(path);
|
||||
if (directory == NULL) {
|
||||
if (!directory) {
|
||||
warnx("Error: could not open directory %s", path);
|
||||
free(path);
|
||||
return -1;
|
||||
@@ -550,8 +509,6 @@ static int dinfo_get_uid_from_devnode(char **uidfile, char *devnode)
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct utsname uname_buf;
|
||||
int version, release;
|
||||
char *uidfile = NULL;
|
||||
char *device = NULL;
|
||||
char *readbuf = NULL;
|
||||
@@ -611,35 +568,19 @@ int main(int argc, char *argv[])
|
||||
util_prg_print_version();
|
||||
exit(EXIT_SUCCESS);
|
||||
default:
|
||||
fprintf(stderr, "Try 'dasdinfo --help' for more "
|
||||
"information.\n");
|
||||
fprintf(stderr, "Try 'dasdinfo --help' for more information.\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
uname(&uname_buf);
|
||||
sscanf(uname_buf.release, "%d.%d", &version, &release);
|
||||
if (strcmp(uname_buf.sysname, "Linux") ||
|
||||
version < 2 || (version == 2 && release < 6)) {
|
||||
warnx("%s %d.%d is not supported", uname_buf.sysname,
|
||||
version, release);
|
||||
exit(1);
|
||||
}
|
||||
if (!busid && !blockdev && !devnode)
|
||||
errx(EXIT_FAILURE, "Error: please specify a device using either -b, -i or -d");
|
||||
|
||||
if (!busid && !blockdev && !devnode) {
|
||||
warnx("Error: please specify a device using either -b, -i or -d");
|
||||
exit(1);
|
||||
}
|
||||
if ((busid && blockdev) || (busid && devnode) || (blockdev && devnode))
|
||||
errx(EXIT_FAILURE, "Error: please specify device only once, either -b, -i or -d");
|
||||
|
||||
if ((busid && blockdev) || (busid && devnode) || (blockdev && devnode)) {
|
||||
warnx("Error: please specify device only once, either -b, -i or -d");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!print_uid && !print_extended_uid && !print_vlabel) {
|
||||
warnx("Error: no action specified (e.g. -u)");
|
||||
exit(1);
|
||||
}
|
||||
if (!print_uid && !print_extended_uid && !print_vlabel)
|
||||
errx(EXIT_FAILURE, "Error: no action specified (e.g. -u)");
|
||||
|
||||
readbuf = dinfo_malloc(RD_BUFFER_SIZE);
|
||||
if (!readbuf)
|
||||
|
||||
55
dasdinfo/dasdinfo_cli.h
Normal file
55
dasdinfo/dasdinfo_cli.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright IBM Corp.
|
||||
*/
|
||||
|
||||
#ifndef DASDINFO_CLI_H
|
||||
#define DASDINFO_CLI_H
|
||||
|
||||
#include "lib/util_opt.h"
|
||||
|
||||
static struct util_opt opt_vec[] = {
|
||||
UTIL_OPT_SECTION("DEVICE"),
|
||||
{
|
||||
.option = { "block", required_argument, NULL, 'b' },
|
||||
.argument = "BLOCKDEV",
|
||||
.desc = "Block device name, e.g. dasdb",
|
||||
},
|
||||
{
|
||||
.option = { "devnode", required_argument, NULL, 'd' },
|
||||
.argument = "DEVNODE",
|
||||
.desc = "Device node, e.g. /dev/dasda",
|
||||
},
|
||||
{
|
||||
.option = { "busid", required_argument, NULL, 'i' },
|
||||
.argument = "BUSID",
|
||||
.desc = "Bus ID, e.g. 0.0.e910",
|
||||
},
|
||||
UTIL_OPT_SECTION("OPTIONS"),
|
||||
{
|
||||
.option = { "label", no_argument, NULL, 'l' },
|
||||
.desc = "Print DASD volume label (volser)",
|
||||
},
|
||||
{
|
||||
.option = { "uid", no_argument, NULL, 'u' },
|
||||
.desc = "Print DASD uid (without z/VM minidisk token)",
|
||||
},
|
||||
{
|
||||
.option = { "extended-uid", no_argument, NULL, 'x' },
|
||||
.desc = "Print DASD uid (including z/VM minidisk token)",
|
||||
},
|
||||
{
|
||||
.option = { "all", no_argument, NULL, 'a' },
|
||||
.desc = "Same as -u -x -l",
|
||||
},
|
||||
{
|
||||
.option = { "export", no_argument, NULL, 'e' },
|
||||
.desc = "Export ID_BUS, ID_TYPE, ID_SERIAL for use in udev",
|
||||
},
|
||||
UTIL_OPT_HELP,
|
||||
UTIL_OPT_VERSION,
|
||||
UTIL_OPT_END
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,5 +1,10 @@
|
||||
include ../common.mak
|
||||
|
||||
zsh-completions = _dasdview
|
||||
bash-completions = dasdview.bash
|
||||
|
||||
include ../common_autocomp.mak
|
||||
|
||||
libs = $(rootdir)/libdasd/libdasd.a \
|
||||
$(rootdir)/libzds/libzds.a \
|
||||
$(rootdir)/libvtoc/libvtoc.a \
|
||||
|
||||
16
dasdview/autocompletion_generator_host.c
Normal file
16
dasdview/autocompletion_generator_host.c
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright IBM Corp.
|
||||
*/
|
||||
|
||||
#include "lib/util_autocomp.h"
|
||||
|
||||
#include "dasdview_cli.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
generate_autocomp(opt_vec, "dasdview");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
#include "dasdview.h"
|
||||
#include "dasdview_cli.h"
|
||||
|
||||
/* Characters per line */
|
||||
#define DASDVIEW_CPL 16
|
||||
@@ -56,61 +57,6 @@ static const struct util_prg prg = {
|
||||
}
|
||||
};
|
||||
|
||||
static struct util_opt opt_vec[] = {
|
||||
UTIL_OPT_SECTION("DUMP OPTIONS"),
|
||||
{
|
||||
.option = { NULL, no_argument, NULL, '1' },
|
||||
.desc = "Show DASD content in short Hex/EBCDIC/ASCII format",
|
||||
.flags = UTIL_OPT_FLAG_NOLONG,
|
||||
},
|
||||
{
|
||||
.option = { NULL, no_argument, NULL, '2' },
|
||||
.desc = "Show DASD content in detailed Hex/EBCDIC/ASCII format",
|
||||
.flags = UTIL_OPT_FLAG_NOLONG,
|
||||
},
|
||||
{
|
||||
.option = { "begin", required_argument, NULL, 'b' },
|
||||
.argument = "BEGIN",
|
||||
.desc = "Specify start of dump in kilobytes (suffix k), "
|
||||
"megabytes (m), blocks (b), tracks (t), or cylinders (c)",
|
||||
},
|
||||
{
|
||||
.option = { "size", required_argument, NULL, 's' },
|
||||
.argument = "SIZE",
|
||||
.desc = "Specify size of dump in kilobytes (suffix k), "
|
||||
"megabytes (m), blocks (b), tracks (t), or cylinders (c)",
|
||||
},
|
||||
UTIL_OPT_SECTION("MISC"),
|
||||
{
|
||||
.option = { "characteristic", no_argument, NULL, 'c' },
|
||||
.desc = "Print the characteristics of a device",
|
||||
},
|
||||
{
|
||||
.option = { "info", no_argument, NULL, 'i' },
|
||||
.desc = "Print general DASD information and geometry",
|
||||
},
|
||||
{
|
||||
.option = { "volser", no_argument, NULL, 'j' },
|
||||
.desc = "Print the volume serial number",
|
||||
},
|
||||
{
|
||||
.option = { "label", no_argument, NULL, 'l' },
|
||||
.desc = "Print information about the volume label",
|
||||
},
|
||||
{
|
||||
.option = { "vtoc", required_argument, NULL, 't' },
|
||||
.argument = "SPEC",
|
||||
.desc = "Print the table of content (VTOC)",
|
||||
},
|
||||
{
|
||||
.option = { "extended", no_argument, NULL, 'x' },
|
||||
.desc = "Print extended DASD information",
|
||||
},
|
||||
UTIL_OPT_HELP,
|
||||
UTIL_OPT_VERSION,
|
||||
UTIL_OPT_END
|
||||
};
|
||||
|
||||
/*
|
||||
* Generate and print an error message based on the formatted
|
||||
* text string FMT and a variable amount of extra arguments.
|
||||
|
||||
67
dasdview/dasdview_cli.h
Normal file
67
dasdview/dasdview_cli.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright IBM Corp.
|
||||
*/
|
||||
|
||||
#ifndef DASDVIEW_CLI_H
|
||||
#define DASDVIEW_CLI_H
|
||||
|
||||
#include "lib/util_opt.h"
|
||||
|
||||
static struct util_opt opt_vec[] = {
|
||||
UTIL_OPT_SECTION("DUMP OPTIONS"),
|
||||
{
|
||||
.option = { NULL, no_argument, NULL, '1' },
|
||||
.desc = "Show DASD content in short Hex/EBCDIC/ASCII format",
|
||||
.flags = UTIL_OPT_FLAG_NOLONG,
|
||||
},
|
||||
{
|
||||
.option = { NULL, no_argument, NULL, '2' },
|
||||
.desc = "Show DASD content in detailed Hex/EBCDIC/ASCII format",
|
||||
.flags = UTIL_OPT_FLAG_NOLONG,
|
||||
},
|
||||
{
|
||||
.option = { "begin", required_argument, NULL, 'b' },
|
||||
.argument = "BEGIN",
|
||||
.desc = "Specify start of dump in kilobytes (suffix k), "
|
||||
"megabytes (m), blocks (b), tracks (t), or cylinders (c)",
|
||||
},
|
||||
{
|
||||
.option = { "size", required_argument, NULL, 's' },
|
||||
.argument = "SIZE",
|
||||
.desc = "Specify size of dump in kilobytes (suffix k), "
|
||||
"megabytes (m), blocks (b), tracks (t), or cylinders (c)",
|
||||
},
|
||||
UTIL_OPT_SECTION("MISC"),
|
||||
{
|
||||
.option = { "characteristic", no_argument, NULL, 'c' },
|
||||
.desc = "Print the characteristics of a device",
|
||||
},
|
||||
{
|
||||
.option = { "info", no_argument, NULL, 'i' },
|
||||
.desc = "Print general DASD information and geometry",
|
||||
},
|
||||
{
|
||||
.option = { "volser", no_argument, NULL, 'j' },
|
||||
.desc = "Print the volume serial number",
|
||||
},
|
||||
{
|
||||
.option = { "label", no_argument, NULL, 'l' },
|
||||
.desc = "Print information about the volume label",
|
||||
},
|
||||
{
|
||||
.option = { "vtoc", required_argument, NULL, 't' },
|
||||
.argument = "SPEC",
|
||||
.desc = "Print the table of content (VTOC)",
|
||||
},
|
||||
{
|
||||
.option = { "extended", no_argument, NULL, 'x' },
|
||||
.desc = "Print extended DASD information",
|
||||
},
|
||||
UTIL_OPT_HELP,
|
||||
UTIL_OPT_VERSION,
|
||||
UTIL_OPT_END
|
||||
};
|
||||
|
||||
#endif
|
||||
136
dump2tar/include/dump2tar_cli.h
Normal file
136
dump2tar/include/dump2tar_cli.h
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright IBM Corp.
|
||||
*/
|
||||
|
||||
#ifndef DUMP2TAR_CLI_H
|
||||
#define DUMP2TAR_CLI_H
|
||||
|
||||
#include "lib/util_opt.h"
|
||||
|
||||
#define OPT_NOSHORT_BASE 256
|
||||
|
||||
#define OPT_DEREFERENCE (OPT_NOSHORT_BASE + 0)
|
||||
#define OPT_NORECURSION (OPT_NOSHORT_BASE + 1)
|
||||
#define OPT_EXCLUDETYPE (OPT_NOSHORT_BASE + 2)
|
||||
|
||||
/* Definition of command line options */
|
||||
static struct util_opt dump2tar_opts[] = {
|
||||
UTIL_OPT_SECTION("OUTPUT OPTIONS"),
|
||||
{
|
||||
.option = { "output-file", required_argument, NULL, 'o' },
|
||||
.argument = "FILE",
|
||||
.desc = "Write archive to FILE (default: standard output)",
|
||||
},
|
||||
#ifdef HAVE_ZLIB
|
||||
{
|
||||
.option = { "gzip", no_argument, NULL, 'z' },
|
||||
.desc = "Write a gzip compressed archive",
|
||||
},
|
||||
#endif /* HAVE_ZLIB */
|
||||
{
|
||||
.option = { "max-size", required_argument, NULL, 'm' },
|
||||
.argument = "N",
|
||||
.desc = "Stop adding files when archive size exceeds N bytes",
|
||||
},
|
||||
{
|
||||
.option = { "timeout", required_argument, NULL, 't' },
|
||||
.argument = "SEC",
|
||||
.desc = "Stop adding files after SEC seconds",
|
||||
},
|
||||
{
|
||||
.option = { "no-eof", no_argument, NULL, 131 },
|
||||
.desc = "Do not write an end-of-file marker",
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
{
|
||||
.option = { "add-cmd-status", no_argument, NULL, 132 },
|
||||
.desc = "Add status of commands as separate file",
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
{
|
||||
.option = { "append", no_argument, NULL, 133 },
|
||||
.desc = "Append output to end of file",
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
|
||||
UTIL_OPT_SECTION("INPUT OPTIONS"),
|
||||
{
|
||||
.option = { "files-from", required_argument, NULL, 'F' },
|
||||
.argument = "FILE",
|
||||
.desc = "Read filenames from FILE (- for standard input)",
|
||||
},
|
||||
{
|
||||
.option = { "ignore-failed-read", no_argument, NULL, 'i' },
|
||||
.desc = "Continue after read errors",
|
||||
},
|
||||
{
|
||||
.option = { "buffer-size", required_argument, NULL, 'b' },
|
||||
.argument = "N",
|
||||
.desc = "Read data in chunks of N byte (default: 16384)",
|
||||
},
|
||||
{
|
||||
.option = { "file-timeout", required_argument, NULL, 'T' },
|
||||
.desc = "Stop reading file after SEC seconds",
|
||||
.argument = "SEC",
|
||||
},
|
||||
{
|
||||
.option = { "file-max-size", required_argument, NULL, 'M' },
|
||||
.argument = "N",
|
||||
.desc = "Stop reading file after N bytes",
|
||||
},
|
||||
{
|
||||
.option = { "jobs", required_argument, NULL, 'j' },
|
||||
.argument = "N",
|
||||
.desc = "Read N files in parallel (default: 1)",
|
||||
},
|
||||
{
|
||||
.option = { "jobs-per-cpu", required_argument, NULL, 'J' },
|
||||
.argument = "N",
|
||||
.desc = "Read N files per CPU in parallel",
|
||||
},
|
||||
{
|
||||
.option = { "exclude", required_argument, NULL, 'x' },
|
||||
.argument = "PATTERN",
|
||||
.desc = "Don't add files matching PATTERN",
|
||||
},
|
||||
{
|
||||
.option = { "exclude-from", required_argument, NULL, 'X' },
|
||||
.argument = "FILE",
|
||||
.desc = "Don't add files matching patterns in FILE",
|
||||
},
|
||||
{
|
||||
.option = { "exclude-type", required_argument, NULL,
|
||||
OPT_EXCLUDETYPE },
|
||||
.argument = "TYPE",
|
||||
.desc = "Don't add files of specified TYPE (one of: fdcbpls)",
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
{
|
||||
.option = { "dereference", no_argument, NULL, OPT_DEREFERENCE },
|
||||
.desc = "Add link targets instead of links",
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
{
|
||||
.option = { "no-recursion", no_argument, NULL,
|
||||
OPT_NORECURSION },
|
||||
.desc = "Don't add files from sub-directories",
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
|
||||
UTIL_OPT_SECTION("MISC OPTIONS"),
|
||||
UTIL_OPT_HELP,
|
||||
UTIL_OPT_VERSION,
|
||||
{
|
||||
.option = { "verbose", no_argument, NULL, 'V' },
|
||||
.desc = "Print additional informational output",
|
||||
},
|
||||
{
|
||||
.option = { "quiet", no_argument, NULL, 'q' },
|
||||
.desc = "Suppress printing of informational output",
|
||||
},
|
||||
UTIL_OPT_END,
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,11 @@
|
||||
# Common definitions
|
||||
include ../../common.mak
|
||||
|
||||
zsh-completions = _dump2tar
|
||||
bash-completions = dump2tar.bash
|
||||
|
||||
include ../../common_autocomp.mak
|
||||
|
||||
ALL_CPPFLAGS += -I../include -Wno-unused-parameter
|
||||
LDLIBS += -lpthread -lrt
|
||||
ifneq ($(HAVE_ZLIB),0)
|
||||
|
||||
16
dump2tar/src/autocompletion_generator_host.c
Normal file
16
dump2tar/src/autocompletion_generator_host.c
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright IBM Corp.
|
||||
*/
|
||||
|
||||
#include "lib/util_autocomp.h"
|
||||
|
||||
#include "../include/dump2tar_cli.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
generate_autocomp(dump2tar_opts, "dump2tar");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "lib/util_prg.h"
|
||||
|
||||
#include "dump.h"
|
||||
#include "dump2tar_cli.h"
|
||||
#include "global.h"
|
||||
#include "idcache.h"
|
||||
#include "misc.h"
|
||||
@@ -28,12 +29,6 @@
|
||||
|
||||
#define MIN_BUFFER_SIZE 4096
|
||||
|
||||
#define OPT_NOSHORT_BASE 256
|
||||
|
||||
#define OPT_DEREFERENCE (OPT_NOSHORT_BASE + 0)
|
||||
#define OPT_NORECURSION (OPT_NOSHORT_BASE + 1)
|
||||
#define OPT_EXCLUDETYPE (OPT_NOSHORT_BASE + 2)
|
||||
|
||||
/* Program description */
|
||||
static const struct util_prg dump2tar_prg = {
|
||||
.desc = "Use dump2tar to create a tar archive from the contents "
|
||||
@@ -52,124 +47,6 @@ static const struct util_prg dump2tar_prg = {
|
||||
},
|
||||
};
|
||||
|
||||
/* Definition of command line options */
|
||||
static struct util_opt dump2tar_opts[] = {
|
||||
UTIL_OPT_SECTION("OUTPUT OPTIONS"),
|
||||
{
|
||||
.option = { "output-file", required_argument, NULL, 'o' },
|
||||
.argument = "FILE",
|
||||
.desc = "Write archive to FILE (default: standard output)",
|
||||
},
|
||||
#ifdef HAVE_ZLIB
|
||||
{
|
||||
.option = { "gzip", no_argument, NULL, 'z' },
|
||||
.desc = "Write a gzip compressed archive",
|
||||
},
|
||||
#endif /* HAVE_ZLIB */
|
||||
{
|
||||
.option = { "max-size", required_argument, NULL, 'm' },
|
||||
.argument = "N",
|
||||
.desc = "Stop adding files when archive size exceeds N bytes",
|
||||
},
|
||||
{
|
||||
.option = { "timeout", required_argument, NULL, 't' },
|
||||
.argument = "SEC",
|
||||
.desc = "Stop adding files after SEC seconds",
|
||||
},
|
||||
{
|
||||
.option = { "no-eof", no_argument, NULL, 131 },
|
||||
.desc = "Do not write an end-of-file marker",
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
{
|
||||
.option = { "add-cmd-status", no_argument, NULL, 132 },
|
||||
.desc = "Add status of commands as separate file",
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
{
|
||||
.option = { "append", no_argument, NULL, 133 },
|
||||
.desc = "Append output to end of file",
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
|
||||
UTIL_OPT_SECTION("INPUT OPTIONS"),
|
||||
{
|
||||
.option = { "files-from", required_argument, NULL, 'F' },
|
||||
.argument = "FILE",
|
||||
.desc = "Read filenames from FILE (- for standard input)",
|
||||
},
|
||||
{
|
||||
.option = { "ignore-failed-read", no_argument, NULL, 'i' },
|
||||
.desc = "Continue after read errors",
|
||||
},
|
||||
{
|
||||
.option = { "buffer-size", required_argument, NULL, 'b' },
|
||||
.argument = "N",
|
||||
.desc = "Read data in chunks of N byte (default: 16384)",
|
||||
},
|
||||
{
|
||||
.option = { "file-timeout", required_argument, NULL, 'T' },
|
||||
.desc = "Stop reading file after SEC seconds",
|
||||
.argument = "SEC",
|
||||
},
|
||||
{
|
||||
.option = { "file-max-size", required_argument, NULL, 'M' },
|
||||
.argument = "N",
|
||||
.desc = "Stop reading file after N bytes",
|
||||
},
|
||||
{
|
||||
.option = { "jobs", required_argument, NULL, 'j' },
|
||||
.argument = "N",
|
||||
.desc = "Read N files in parallel (default: 1)",
|
||||
},
|
||||
{
|
||||
.option = { "jobs-per-cpu", required_argument, NULL, 'J' },
|
||||
.argument = "N",
|
||||
.desc = "Read N files per CPU in parallel",
|
||||
},
|
||||
{
|
||||
.option = { "exclude", required_argument, NULL, 'x' },
|
||||
.argument = "PATTERN",
|
||||
.desc = "Don't add files matching PATTERN",
|
||||
},
|
||||
{
|
||||
.option = { "exclude-from", required_argument, NULL, 'X' },
|
||||
.argument = "FILE",
|
||||
.desc = "Don't add files matching patterns in FILE",
|
||||
},
|
||||
{
|
||||
.option = { "exclude-type", required_argument, NULL,
|
||||
OPT_EXCLUDETYPE },
|
||||
.argument = "TYPE",
|
||||
.desc = "Don't add files of specified TYPE (one of: fdcbpls)",
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
{
|
||||
.option = { "dereference", no_argument, NULL, OPT_DEREFERENCE },
|
||||
.desc = "Add link targets instead of links",
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
{
|
||||
.option = { "no-recursion", no_argument, NULL,
|
||||
OPT_NORECURSION },
|
||||
.desc = "Don't add files from sub-directories",
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
|
||||
UTIL_OPT_SECTION("MISC OPTIONS"),
|
||||
UTIL_OPT_HELP,
|
||||
UTIL_OPT_VERSION,
|
||||
{
|
||||
.option = { "verbose", no_argument, NULL, 'V' },
|
||||
.desc = "Print additional informational output",
|
||||
},
|
||||
{
|
||||
.option = { "quiet", no_argument, NULL, 'q' },
|
||||
.desc = "Suppress printing of informational output",
|
||||
},
|
||||
UTIL_OPT_END,
|
||||
};
|
||||
|
||||
/* Split buffer size specification in @arg into two numbers to be stored in
|
||||
* @from_ptr and @to_ptr. Return %EXIT_OK on success. */
|
||||
static int parse_buffer_size(char *arg, size_t *from_ptr, size_t *to_ptr)
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
include ../common.mak
|
||||
|
||||
zsh-completions = _fdasd
|
||||
bash-completions = fdasd.bash
|
||||
|
||||
include ../common_autocomp.mak
|
||||
|
||||
libs = $(rootdir)/libvtoc/libvtoc.a \
|
||||
$(rootdir)/libzds/libzds.a \
|
||||
$(rootdir)/libdasd/libdasd.a \
|
||||
|
||||
16
fdasd/autocompletion_generator_host.c
Normal file
16
fdasd/autocompletion_generator_host.c
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright IBM Corp.
|
||||
*/
|
||||
|
||||
#include "lib/util_autocomp.h"
|
||||
|
||||
#include "fdasd_cli.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
generate_autocomp(opt_vec, "fdasd");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
#include "fdasd.h"
|
||||
#include "fdasd_cli.h"
|
||||
|
||||
/* global variables */
|
||||
static struct hd_geometry geo;
|
||||
@@ -138,58 +139,6 @@ static const struct util_prg prg = {
|
||||
}
|
||||
};
|
||||
|
||||
static struct util_opt opt_vec[] = {
|
||||
UTIL_OPT_SECTION("NON-INTERACTIVE MODE"),
|
||||
{
|
||||
.option = { "auto", no_argument, NULL, 'a' },
|
||||
.desc = "Create a single partition spanning the entire disk",
|
||||
},
|
||||
{
|
||||
.option = { "config", required_argument, NULL, 'c' },
|
||||
.argument = "FILE",
|
||||
.desc = "Create partitions(s) based on content of FILE",
|
||||
},
|
||||
{
|
||||
.option = { "keep_volser", no_argument, NULL, 'k' },
|
||||
.desc = "Do not change the current volume serial",
|
||||
},
|
||||
{
|
||||
.option = { "label", required_argument, NULL, 'l' },
|
||||
.argument = "VOLSER",
|
||||
.desc = "Set the volume serial to VOLSER",
|
||||
},
|
||||
UTIL_OPT_SECTION("MISC"),
|
||||
{
|
||||
.option = { "check_host_count", no_argument, NULL, 'C' },
|
||||
.desc = "Check if device is in use by other hosts",
|
||||
},
|
||||
{
|
||||
.option = { "force", optional_argument, NULL, 'f' },
|
||||
.argument = "TYPE,SIZE",
|
||||
.desc = "Force fdasd to work on non DASD devices with assumed "
|
||||
"TYPE (3390, 3380, or 9345) and blocksize SIZE",
|
||||
},
|
||||
{
|
||||
.option = { "volser", no_argument, NULL, 'i' },
|
||||
.desc = "Print volume serial",
|
||||
},
|
||||
{
|
||||
.option = { "table", no_argument, NULL, 'p' },
|
||||
.desc = "Print partition table",
|
||||
},
|
||||
{
|
||||
.option = { "verbose", no_argument, NULL, 'r' },
|
||||
.desc = "Provide more verbose output",
|
||||
},
|
||||
{
|
||||
.option = { "silent", no_argument, NULL, 's' },
|
||||
.desc = "Suppress messages",
|
||||
},
|
||||
UTIL_OPT_HELP,
|
||||
UTIL_OPT_VERSION,
|
||||
UTIL_OPT_END
|
||||
};
|
||||
|
||||
static int getpos(fdasd_anchor_t *anc, int dsn)
|
||||
{
|
||||
return anc->partno[dsn];
|
||||
|
||||
64
fdasd/fdasd_cli.h
Normal file
64
fdasd/fdasd_cli.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright IBM Corp.
|
||||
*/
|
||||
|
||||
#ifndef FDASD_CLI_H
|
||||
#define FDASD_CLI_H
|
||||
|
||||
#include "lib/util_opt.h"
|
||||
|
||||
static struct util_opt opt_vec[] = {
|
||||
UTIL_OPT_SECTION("NON-INTERACTIVE MODE"),
|
||||
{
|
||||
.option = { "auto", no_argument, NULL, 'a' },
|
||||
.desc = "Create a single partition spanning the entire disk",
|
||||
},
|
||||
{
|
||||
.option = { "config", required_argument, NULL, 'c' },
|
||||
.argument = "FILE",
|
||||
.desc = "Create partitions(s) based on content of FILE",
|
||||
},
|
||||
{
|
||||
.option = { "keep_volser", no_argument, NULL, 'k' },
|
||||
.desc = "Do not change the current volume serial",
|
||||
},
|
||||
{
|
||||
.option = { "label", required_argument, NULL, 'l' },
|
||||
.argument = "VOLSER",
|
||||
.desc = "Set the volume serial to VOLSER",
|
||||
},
|
||||
UTIL_OPT_SECTION("MISC"),
|
||||
{
|
||||
.option = { "check_host_count", no_argument, NULL, 'C' },
|
||||
.desc = "Check if device is in use by other hosts",
|
||||
},
|
||||
{
|
||||
.option = { "force", optional_argument, NULL, 'f' },
|
||||
.argument = "TYPE,SIZE",
|
||||
.desc = "Force fdasd to work on non DASD devices with assumed "
|
||||
"TYPE (3390, 3380, or 9345) and blocksize SIZE",
|
||||
},
|
||||
{
|
||||
.option = { "volser", no_argument, NULL, 'i' },
|
||||
.desc = "Print volume serial",
|
||||
},
|
||||
{
|
||||
.option = { "table", no_argument, NULL, 'p' },
|
||||
.desc = "Print partition table",
|
||||
},
|
||||
{
|
||||
.option = { "verbose", no_argument, NULL, 'r' },
|
||||
.desc = "Provide more verbose output",
|
||||
},
|
||||
{
|
||||
.option = { "silent", no_argument, NULL, 's' },
|
||||
.desc = "Suppress messages",
|
||||
},
|
||||
UTIL_OPT_HELP,
|
||||
UTIL_OPT_VERSION,
|
||||
UTIL_OPT_END
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -7,6 +7,11 @@
|
||||
|
||||
include ../common.mak
|
||||
|
||||
zsh-completions = _hsavmcore
|
||||
bash-completions = hsavmcore.bash
|
||||
|
||||
include ../common_autocomp.mak
|
||||
|
||||
ALL_CPPFLAGS += -D_FILE_OFFSET_BITS=64
|
||||
|
||||
ifeq (${HAVE_FUSE},0)
|
||||
@@ -41,7 +46,7 @@ endif
|
||||
ALL_CFLAGS += $(FUSE_CFLAGS) $(SYSTEMD_CFLAGS)
|
||||
LDLIBS += $(FUSE_LDLIBS) $(SYSTEMD_LDLIBS) -lpthread
|
||||
|
||||
sources := $(wildcard *.c)
|
||||
sources := $(filter-out %_host.c, $(wildcard *.c))
|
||||
objects := $(patsubst %.c,%.o,$(sources))
|
||||
|
||||
libs = $(rootdir)/libutil/libutil.a
|
||||
|
||||
16
hsavmcore/autocompletion_generator_host.c
Normal file
16
hsavmcore/autocompletion_generator_host.c
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright IBM Corp.
|
||||
*/
|
||||
|
||||
#include "lib/util_autocomp.h"
|
||||
|
||||
#include "hsavmcore_cli.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
generate_autocomp(opt_vec, "hsavmcore");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "lib/util_log.h"
|
||||
|
||||
#include "cmdline_options.h"
|
||||
#include "hsavmcore_cli.h"
|
||||
|
||||
static const struct util_prg prg = {
|
||||
.desc = "hsavmcore is designed to make the dump process with kdump more "
|
||||
@@ -32,91 +33,6 @@ static const struct util_prg prg = {
|
||||
}
|
||||
};
|
||||
|
||||
static struct util_opt opt_vec[] = {
|
||||
UTIL_OPT_SECTION("CONFIGURATION"),
|
||||
{
|
||||
.option = { "config", required_argument, NULL, 'c' },
|
||||
.argument = "CONFIGFILE",
|
||||
.desc = "Path to the configuration file.\n"
|
||||
"Default: no configuration file is used",
|
||||
},
|
||||
{
|
||||
.option = { "vmcore", required_argument, NULL, 'C' },
|
||||
.argument = "VMCOREFILE",
|
||||
.desc = "Path to the vmcore file.\n"
|
||||
"Default: " PROC_VMCORE,
|
||||
},
|
||||
{
|
||||
.option = { "hsa", required_argument, NULL, 'H' },
|
||||
.argument = "ZCOREHSAFILE",
|
||||
.desc = "Path to the zcore HSA file.\n"
|
||||
"Default: " ZCORE_HSA,
|
||||
},
|
||||
{
|
||||
.option = { "workdir", required_argument, NULL, 'W' },
|
||||
.argument = "WORKDIR",
|
||||
.desc = "Path to the work directory where temporary files can be "
|
||||
"stored.\nDefault: " WORKDIR,
|
||||
},
|
||||
{
|
||||
.option = { "bmvmcore", required_argument, NULL, 'B' },
|
||||
.argument = "VMCOREFILE",
|
||||
.desc = "Path to the target of the bind mount for the vmcore "
|
||||
"replacement.\nDefault: " PROC_VMCORE,
|
||||
},
|
||||
{
|
||||
.option = { "swap", required_argument, NULL, 'S' },
|
||||
.argument = "PATH",
|
||||
.desc = "Path to a swap device or file. The specified swap "
|
||||
"device or file must exist and have the proper swap "
|
||||
"format.\nDefault: no swap device or file is activated",
|
||||
},
|
||||
{
|
||||
.option = { "hsasize", required_argument, NULL, 'T' },
|
||||
.argument = "HSASIZE",
|
||||
.desc = "HSA size in bytes.\n"
|
||||
"Default: -1 (read from the zcore HSA file)",
|
||||
},
|
||||
{
|
||||
.option = { "dbgfsmnt", no_argument, NULL, 'D' },
|
||||
.desc = "Mount the debug file system.\n"
|
||||
"Default: the debug file system is not mounted",
|
||||
},
|
||||
{
|
||||
.option = { "hsamem", no_argument, NULL, 'F' },
|
||||
.desc = "Cache the HSA memory in regular memory.\n"
|
||||
"Default: the HSA memory is cached as a file within "
|
||||
"WORKDIR",
|
||||
},
|
||||
{
|
||||
.option = { "norelhsa", no_argument, NULL, 'R' },
|
||||
.desc = "Do NOT release the HSA memory after caching.\n"
|
||||
"Default: the HSA memory is released",
|
||||
},
|
||||
{
|
||||
.option = { "nobindmnt", no_argument, NULL, 'N' },
|
||||
.desc = "Do NOT replace the system's vmcore.\n"
|
||||
"Default: the system's vmcore is replaced",
|
||||
},
|
||||
UTIL_OPT_SECTION("LOGGING"),
|
||||
{
|
||||
.option = { "verbose", no_argument, NULL, 'V' },
|
||||
.desc = "Print verbose messages to stdout. Repeat this option "
|
||||
"for increased verbosity from just error messages to "
|
||||
"also include warning, information, debug, and trace "
|
||||
"messages. This option is intended for debugging",
|
||||
},
|
||||
{
|
||||
.option = { "fusedbg", no_argument, NULL, 'G' },
|
||||
.desc = "Enable FUSE debugging.\n"
|
||||
"Default: FUSE debugging is disabled",
|
||||
},
|
||||
UTIL_OPT_SECTION("GENERAL OPTIONS"),
|
||||
UTIL_OPT_HELP,
|
||||
UTIL_OPT_VERSION,
|
||||
UTIL_OPT_END
|
||||
};
|
||||
|
||||
void parse_cmdline_options(int argc, char *argv[], struct config *config)
|
||||
{
|
||||
int opt, ret;
|
||||
|
||||
99
hsavmcore/hsavmcore_cli.h
Normal file
99
hsavmcore/hsavmcore_cli.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright IBM Corp.
|
||||
*/
|
||||
|
||||
#ifndef HSAVMCORE_CLI_H
|
||||
#define HSAVMCORE_CLI_H
|
||||
|
||||
#include "lib/util_opt.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
static struct util_opt opt_vec[] = {
|
||||
UTIL_OPT_SECTION("CONFIGURATION"),
|
||||
{
|
||||
.option = { "config", required_argument, NULL, 'c' },
|
||||
.argument = "CONFIGFILE",
|
||||
.desc = "Path to the configuration file.\n"
|
||||
"Default: no configuration file is used",
|
||||
},
|
||||
{
|
||||
.option = { "vmcore", required_argument, NULL, 'C' },
|
||||
.argument = "VMCOREFILE",
|
||||
.desc = "Path to the vmcore file.\n"
|
||||
"Default: " PROC_VMCORE,
|
||||
},
|
||||
{
|
||||
.option = { "hsa", required_argument, NULL, 'H' },
|
||||
.argument = "ZCOREHSAFILE",
|
||||
.desc = "Path to the zcore HSA file.\n"
|
||||
"Default: " ZCORE_HSA,
|
||||
},
|
||||
{
|
||||
.option = { "workdir", required_argument, NULL, 'W' },
|
||||
.argument = "WORKDIR",
|
||||
.desc = "Path to the work directory where temporary files can be "
|
||||
"stored.\nDefault: " WORKDIR,
|
||||
},
|
||||
{
|
||||
.option = { "bmvmcore", required_argument, NULL, 'B' },
|
||||
.argument = "VMCOREFILE",
|
||||
.desc = "Path to the target of the bind mount for the vmcore "
|
||||
"replacement.\nDefault: " PROC_VMCORE,
|
||||
},
|
||||
{
|
||||
.option = { "swap", required_argument, NULL, 'S' },
|
||||
.argument = "PATH",
|
||||
.desc = "Path to a swap device or file. The specified swap "
|
||||
"device or file must exist and have the proper swap "
|
||||
"format.\nDefault: no swap device or file is activated",
|
||||
},
|
||||
{
|
||||
.option = { "hsasize", required_argument, NULL, 'T' },
|
||||
.argument = "HSASIZE",
|
||||
.desc = "HSA size in bytes.\n"
|
||||
"Default: -1 (read from the zcore HSA file)",
|
||||
},
|
||||
{
|
||||
.option = { "dbgfsmnt", no_argument, NULL, 'D' },
|
||||
.desc = "Mount the debug file system.\n"
|
||||
"Default: the debug file system is not mounted",
|
||||
},
|
||||
{
|
||||
.option = { "hsamem", no_argument, NULL, 'F' },
|
||||
.desc = "Cache the HSA memory in regular memory.\n"
|
||||
"Default: the HSA memory is cached as a file within "
|
||||
"WORKDIR",
|
||||
},
|
||||
{
|
||||
.option = { "norelhsa", no_argument, NULL, 'R' },
|
||||
.desc = "Do NOT release the HSA memory after caching.\n"
|
||||
"Default: the HSA memory is released",
|
||||
},
|
||||
{
|
||||
.option = { "nobindmnt", no_argument, NULL, 'N' },
|
||||
.desc = "Do NOT replace the system's vmcore.\n"
|
||||
"Default: the system's vmcore is replaced",
|
||||
},
|
||||
UTIL_OPT_SECTION("LOGGING"),
|
||||
{
|
||||
.option = { "verbose", no_argument, NULL, 'V' },
|
||||
.desc = "Print verbose messages to stdout. Repeat this option "
|
||||
"for increased verbosity from just error messages to "
|
||||
"also include warning, information, debug, and trace "
|
||||
"messages. This option is intended for debugging",
|
||||
},
|
||||
{
|
||||
.option = { "fusedbg", no_argument, NULL, 'G' },
|
||||
.desc = "Enable FUSE debugging.\n"
|
||||
"Default: FUSE debugging is disabled",
|
||||
},
|
||||
UTIL_OPT_SECTION("GENERAL OPTIONS"),
|
||||
UTIL_OPT_HELP,
|
||||
UTIL_OPT_VERSION,
|
||||
UTIL_OPT_END
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -96,6 +96,17 @@ with an ASCII Record Separator character (0x1e) and suffixed with an ASCII Line
|
||||
Feed character (0x0a) in accordance with RFC7464.
|
||||
.BR
|
||||
|
||||
See section "OUTPUT FORMAT" for more details.
|
||||
.BR
|
||||
.PP
|
||||
.IP \(bu 3
|
||||
.B jsonl:
|
||||
Line-delimited JSON data structures
|
||||
|
||||
Data for each iteration is formatted as a separate JSON data structure
|
||||
separated by ASCII Line Feed character (0x0a, LF).
|
||||
.BR
|
||||
|
||||
See section "OUTPUT FORMAT" for more details.
|
||||
.BR
|
||||
.PP
|
||||
@@ -458,6 +469,14 @@ Subsequent objects each represent performance data for one iteration
|
||||
.br
|
||||
.PP
|
||||
|
||||
.SS jsonl
|
||||
|
||||
The jsonl output format is a data streaming variation of the JSON output format
|
||||
described above with the same properties as the json\-seq output with the
|
||||
difference that JSON data is separated only by an ASCII Line Feed character
|
||||
(0x0a, LF).
|
||||
|
||||
|
||||
.SH EXAMPLES
|
||||
To start hyptop with the "sys_list" window in interactive mode, enter:
|
||||
.br
|
||||
|
||||
@@ -233,7 +233,7 @@ static void l_fmt_init(void)
|
||||
flags |= FMT_QUOTEALL;
|
||||
if (g.o.format == FMT_CSV || g.o.format_all)
|
||||
flags |= FMT_KEEPINVAL;
|
||||
if (g.o.format == FMT_JSON || g.o.format == FMT_JSONSEQ)
|
||||
if (util_fmt_is_json(g.o.format))
|
||||
flags |= FMT_HANDLEINT;
|
||||
util_fmt_init(stdout, g.o.format, flags, 1);
|
||||
}
|
||||
|
||||
@@ -54,14 +54,6 @@ static char HELP_TEXT[] =
|
||||
#define OPT_FORMAT 256 /* --format */
|
||||
#define OPT_FORMAT_ALL 261 /* --all*/
|
||||
|
||||
/*
|
||||
* Options with underscore to keep compatibility
|
||||
*/
|
||||
#define OPT_BATCH_MODE 257 /* --batch_mode */
|
||||
#define OPT_SORT_FIELD 258 /* --sort | --sort_field */
|
||||
#define OPT_CPU_TYPES 259 /* --cpu_types */
|
||||
#define OPT_SMT_FACTOR 260 /* --smt_factor */
|
||||
|
||||
/*
|
||||
* Initialize default settings
|
||||
*/
|
||||
@@ -224,7 +216,7 @@ static void l_fields_set(char *str)
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the "--sort_field" option
|
||||
* Set the "--sort" option
|
||||
*/
|
||||
static void l_sort_field_set(char *str)
|
||||
{
|
||||
@@ -362,19 +354,18 @@ void opts_parse(int argc, char *argv[])
|
||||
{ "version", no_argument, NULL, 'v'},
|
||||
{ "help", no_argument, NULL, 'h'},
|
||||
{ "batch-mode", no_argument, NULL, 'b'},
|
||||
{ "batch_mode", no_argument, NULL, OPT_BATCH_MODE},
|
||||
{ "batch_mode", no_argument, NULL, 'b'},
|
||||
{ "all", no_argument, NULL, OPT_FORMAT_ALL },
|
||||
{ "delay", required_argument, NULL, 'd'},
|
||||
{ "smt-factor", required_argument, NULL, 'm'},
|
||||
{ "smt_factor", required_argument, NULL, OPT_SMT_FACTOR},
|
||||
{ "smt_factor", required_argument, NULL, 'm'},
|
||||
{ "window", required_argument, NULL, 'w'},
|
||||
{ "sys", required_argument, NULL, 's'},
|
||||
{ "iterations", required_argument, NULL, 'n'},
|
||||
{ "fields", required_argument, NULL, 'f'},
|
||||
{ "sort-field", required_argument, NULL, 'S'},
|
||||
{ "sort_field", required_argument, NULL, OPT_SORT_FIELD},
|
||||
{ "sort", required_argument, NULL, 'S'},
|
||||
{ "cpu-types", required_argument, NULL, 't'},
|
||||
{ "cpu_types", required_argument, NULL, OPT_CPU_TYPES},
|
||||
{ "cpu_types", required_argument, NULL, 't'},
|
||||
{ "format", required_argument, NULL, OPT_FORMAT },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
@@ -393,14 +384,12 @@ void opts_parse(int argc, char *argv[])
|
||||
case 'h':
|
||||
l_usage();
|
||||
hyptop_exit(0);
|
||||
case OPT_BATCH_MODE:
|
||||
case 'b':
|
||||
l_batch_mode_set();
|
||||
break;
|
||||
case 'd':
|
||||
l_delay_set(optarg);
|
||||
break;
|
||||
case OPT_SMT_FACTOR:
|
||||
case 'm':
|
||||
l_factor_set(optarg);
|
||||
break;
|
||||
@@ -413,14 +402,12 @@ void opts_parse(int argc, char *argv[])
|
||||
case 'n':
|
||||
l_iterations_set(optarg);
|
||||
break;
|
||||
case OPT_CPU_TYPES:
|
||||
case 't':
|
||||
l_cpu_types_set(optarg);
|
||||
break;
|
||||
case 'f':
|
||||
l_fields_set(optarg);
|
||||
break;
|
||||
case OPT_SORT_FIELD:
|
||||
case 'S':
|
||||
l_sort_field_set(optarg);
|
||||
break;
|
||||
|
||||
@@ -1037,7 +1037,7 @@ void table_fmt_start(void)
|
||||
{
|
||||
if (!g.o.format_specified)
|
||||
return;
|
||||
if (g.o.format != FMT_JSONSEQ)
|
||||
if (!util_fmt_is_json_stream(g.o.format))
|
||||
util_fmt_obj_start(FMT_LIST, "hyptop");
|
||||
}
|
||||
|
||||
@@ -1045,7 +1045,7 @@ void table_fmt_end(void)
|
||||
{
|
||||
if (!g.o.format_specified)
|
||||
return;
|
||||
if (g.o.format != FMT_JSONSEQ)
|
||||
if (!util_fmt_is_json_stream(g.o.format))
|
||||
util_fmt_obj_end(); /* hyptop[] */
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/*
|
||||
* autocomp - command line autocompletion
|
||||
*
|
||||
* Generating autocompletion scripts for bash and zsh
|
||||
* based on util_opt struct
|
||||
*
|
||||
* Copyright IBM Corp. 2025
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright IBM Corp.
|
||||
*/
|
||||
|
||||
#ifndef LIB_UTIL_AUTOCOMP_H
|
||||
|
||||
@@ -45,12 +45,13 @@
|
||||
#define FMT_DEFAULT 0
|
||||
|
||||
/* Names of supported output format types. */
|
||||
#define FMT_TYPE_NAMES "json json-seq pairs csv"
|
||||
#define FMT_TYPE_NAMES "json json-seq jsonl pairs csv"
|
||||
|
||||
/**
|
||||
* enum util_fmt_t - Output format types.
|
||||
* @FMT_JSON: JavaScript Object Notation output data structure
|
||||
* @FMT_JSONSEQ: Sequence of JSON data structures according to RFC7464
|
||||
* @FMT_JSONL: Line-delimited JSON
|
||||
* @FMT_PAIRS: Textual key=value pairs
|
||||
* @FMT_CSV: Comma-separated-values output
|
||||
*
|
||||
@@ -59,6 +60,7 @@
|
||||
enum util_fmt_t {
|
||||
FMT_JSON,
|
||||
FMT_JSONSEQ,
|
||||
FMT_JSONL,
|
||||
FMT_PAIRS,
|
||||
FMT_CSV,
|
||||
};
|
||||
@@ -232,4 +234,23 @@ void util_fmt_obj_end(void);
|
||||
*/
|
||||
void util_fmt_pair(unsigned int mflags, const char *key, const char *fmt, ...);
|
||||
|
||||
/**
|
||||
* util_fmt_is_json() - Determine whether format is JSON.
|
||||
* @type: Format type identifier.
|
||||
*
|
||||
* Return: %true if type is JSON, %false otherwise.
|
||||
*/
|
||||
bool util_fmt_is_json(enum util_fmt_t type);
|
||||
|
||||
/**
|
||||
* util_fmt_is_json_stream() - Determine whether format is JSON stream.
|
||||
* @type: Format type identifier.
|
||||
*
|
||||
* Determine whether a given format @type represents a JSON streaming format
|
||||
* such as json-seq (@FMT_JSONSEQ) or jsonl (@FMT_JSONL).
|
||||
*
|
||||
* Return: %true if type is either @FMT_JSONSEQ or @FMT_JSONL, %false otherwise.
|
||||
*/
|
||||
bool util_fmt_is_json_stream(enum util_fmt_t type);
|
||||
|
||||
#endif /* LIB_UTIL_FMT_H */
|
||||
|
||||
@@ -2172,7 +2172,7 @@ int write_public_key(const char *pem_filename, EVP_PKEY *pkey)
|
||||
*/
|
||||
static bool is_duplicate_name_entry(X509_NAME *name, X509_NAME_ENTRY *entry)
|
||||
{
|
||||
X509_NAME_ENTRY *ne;
|
||||
const X509_NAME_ENTRY *ne;
|
||||
int count, i;
|
||||
|
||||
count = X509_NAME_entry_count(name);
|
||||
|
||||
@@ -378,8 +378,14 @@ int kmip_connection_tls_init(struct kmip_connection *conn, bool debug)
|
||||
if (conn->config.tls_verify_host) {
|
||||
SSL_set_hostflags(conn->plain_tls.ssl,
|
||||
X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
|
||||
#if OPENSSL_VERSION_PREREQ(4, 0)
|
||||
if (SSL_set1_ipaddr(conn->plain_tls.ssl, hostname) != 1 &&
|
||||
SSL_set1_dnsname(conn->plain_tls.ssl, hostname) != 1) {
|
||||
kmip_debug(debug, "SSL_set1_ipaddr/dnsname failed");
|
||||
#else
|
||||
if (SSL_set1_host(conn->plain_tls.ssl, hostname) != 1) {
|
||||
kmip_debug(debug, "SSL_set1_host failed");
|
||||
#endif
|
||||
if (debug)
|
||||
ERR_print_errors_fp(stderr);
|
||||
rc = -EIO;
|
||||
|
||||
@@ -395,7 +395,7 @@ const struct sk_digest_info *SK_UTIL_get_digest_info(int digest_nid)
|
||||
static bool SK_UTILS_is_duplicate_name_entry(const X509_NAME *name,
|
||||
const X509_NAME_ENTRY *entry)
|
||||
{
|
||||
X509_NAME_ENTRY *ne;
|
||||
const X509_NAME_ENTRY *ne;
|
||||
int count, i;
|
||||
|
||||
count = X509_NAME_entry_count(name);
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
* autocomp - command line autocompletion
|
||||
*
|
||||
* Generating autocompletion scripts for bash and zsh
|
||||
* based on util_opt struct
|
||||
*
|
||||
* Copyright IBM Corp. 2025
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright IBM Corp.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
@@ -22,18 +14,19 @@
|
||||
#include "lib/util_autocomp.h"
|
||||
#include "lib/util_opt.h"
|
||||
|
||||
static const char *bash_script_part1 = "() {\n\n\
|
||||
\tlocal current_word previous_word options_array\n\n\
|
||||
static const char *bash_script_part1 = "() {\n\
|
||||
\tlocal current_word options_array\n\
|
||||
\tCOMPREPLY=()\n\n\
|
||||
\tcurrent_word=\"${COMP_WORDS[COMP_CWORD]}\"\n\n\
|
||||
\tprevious_word=\"${COMP_WORDS[COMP_CWORD-1]}\"\n\n\
|
||||
\tcurrent_word=\"${COMP_WORDS[COMP_CWORD]}\"\n\
|
||||
\toptions_array=\"";
|
||||
|
||||
static const char *bash_script_part2 = "\tif [[ ${current_word} == -* || ${COMP_CWORD} -eq 1 ]] ; then\n\n\
|
||||
\t\tCOMPREPLY=( $(compgen -W \"${options_array}\" -- ${current_word} ) )\n\n\
|
||||
\t\treturn 0\n\n\
|
||||
\tfi\n\n\
|
||||
}\n\n\
|
||||
static const char *bash_script_part2 = "\tif [[ ${current_word} == -* ]] ; then\n\
|
||||
\t\tmapfile -t \"COMPREPLY\" < <(compgen -W \"${options_array}\" -- \"$current_word\")\n\
|
||||
\telse\n\
|
||||
\t\tcompopt -o bashdefault -o default\n\
|
||||
\tfi\n\
|
||||
\treturn 0\n\
|
||||
}\n\
|
||||
complete -F ";
|
||||
|
||||
static char *format_name(const char *fmt, char *tool_name)
|
||||
@@ -63,7 +56,7 @@ static int init_scriptfile(char *file_path)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = open(file_path, O_CREAT | O_WRONLY, 0644);
|
||||
fd = open(file_path, O_CREAT | O_WRONLY | O_TRUNC, 0644);
|
||||
if (fd < 0)
|
||||
return -EIO;
|
||||
return fd;
|
||||
@@ -86,7 +79,7 @@ static int start_bash_scriptfile(int fd, char *func_name)
|
||||
|
||||
static int start_zsh_scriptfile(int fd, char *func_name, char *tool_name)
|
||||
{
|
||||
const char *part3 = " {\n\n\t_arguments -C \\\n";
|
||||
const char *part3 = " {\n\n\t_arguments -C -A \"*\" \\\n";
|
||||
const char *part2 = "\n\nfunction ";
|
||||
const char *part1 = "#compdef ";
|
||||
int len, ret = 0;
|
||||
@@ -127,6 +120,7 @@ static int write_bash_command_options(struct util_opt *opt_vec, int fd)
|
||||
|
||||
static int write_zsh_command_options(struct util_opt *opt_vec, int fd)
|
||||
{
|
||||
const char *end = "\t\t\"*:files:_files\"\n}\n";
|
||||
const char *name, *desc;
|
||||
char *str;
|
||||
int len;
|
||||
@@ -145,8 +139,14 @@ static int write_zsh_command_options(struct util_opt *opt_vec, int fd)
|
||||
free(str);
|
||||
}
|
||||
}
|
||||
if (write(fd, "\n}\n", 3) != 3)
|
||||
len = asprintf(&str, "%s", end);
|
||||
if (len == -1)
|
||||
return -EIO;
|
||||
if (write(fd, str, len) != len) {
|
||||
free(str);
|
||||
return -EIO;
|
||||
}
|
||||
free(str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -169,35 +169,6 @@ static int finish_bash_scriptfile(char *tool_name, int fd, char *func_name)
|
||||
* Adds tab completion in bash for a command.
|
||||
* Works by generating an autocompletion
|
||||
* script file at '/usr/share/bash-completion/completions'.
|
||||
*
|
||||
* The full script will be as follows, supposing the tool name is
|
||||
* 'example' and it only has the options '--help' and
|
||||
* '--version':
|
||||
*
|
||||
* _example() {
|
||||
*
|
||||
* local current_word previous_word options_array
|
||||
*
|
||||
* COMPREPLY=()
|
||||
*
|
||||
* current_word="${COMP_WORDS[COMP_CWORD]}"
|
||||
*
|
||||
* previous_word="${COMP_WORDS[COMP_CWORD-1]}"
|
||||
*
|
||||
* options_array="--version --help"
|
||||
*
|
||||
* if [[ ${current_word} == -* || ${COMP_CWORD} -eq 1 ]] ; then
|
||||
*
|
||||
* COMPREPLY=( $(compgen -W "${options_array}" -- ${current_word} ) )
|
||||
*
|
||||
* return 0
|
||||
*
|
||||
* fi
|
||||
*
|
||||
* }
|
||||
*
|
||||
* complete -F _example example
|
||||
*
|
||||
*/
|
||||
static void generate_bash_autocomp(struct util_opt *opt_vec, char *tool_name)
|
||||
{
|
||||
@@ -244,22 +215,6 @@ end:
|
||||
* Adds tab completion in zsh for a command.
|
||||
* Works by generating an autocompletion
|
||||
* script file at '/usr/share/zsh/site-functions'.
|
||||
*
|
||||
* The full script will be as follows, supposing the tool name is
|
||||
* 'example' and it only has the options '--help', -h and
|
||||
* '--version' (the descriptions, as well as the flags are
|
||||
* taken from a util_opt struct):
|
||||
*
|
||||
* #compdef example_completion
|
||||
*
|
||||
* function _example_completion {
|
||||
*
|
||||
* _arguments -C \
|
||||
* "-h[Show help information]" \
|
||||
* "--help[Show help but long format]" \
|
||||
* "--version[Show version]"
|
||||
* }
|
||||
*
|
||||
*/
|
||||
static void generate_zsh_autocomp(struct util_opt *opt_vec, char *tool_name)
|
||||
{
|
||||
|
||||
@@ -91,6 +91,7 @@ static const struct {
|
||||
} formats[] = {
|
||||
{ "json", FMT_JSON },
|
||||
{ "json-seq", FMT_JSONSEQ },
|
||||
{ "jsonl", FMT_JSONL },
|
||||
{ "pairs", FMT_PAIRS },
|
||||
{ "csv", FMT_CSV },
|
||||
};
|
||||
@@ -111,6 +112,29 @@ bool util_fmt_name_to_type(const char *name, enum util_fmt_t *type)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool util_fmt_is_json(enum util_fmt_t type)
|
||||
{
|
||||
switch (type) {
|
||||
case FMT_JSON:
|
||||
case FMT_JSONSEQ:
|
||||
case FMT_JSONL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool util_fmt_is_json_stream(enum util_fmt_t type)
|
||||
{
|
||||
switch (type) {
|
||||
case FMT_JSONSEQ:
|
||||
case FMT_JSONL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void safe_write(const char *str)
|
||||
{
|
||||
size_t done, todo;
|
||||
@@ -129,7 +153,7 @@ static void _indent(unsigned int off, bool safe)
|
||||
{
|
||||
unsigned int num, i;
|
||||
|
||||
if (f.type == FMT_JSONSEQ)
|
||||
if (util_fmt_is_json_stream(f.type))
|
||||
return;
|
||||
num = f.ind_base + off;
|
||||
if (f.type == FMT_JSON && f.lvl > 0)
|
||||
@@ -408,8 +432,8 @@ static void emit_meta_object(void)
|
||||
util_fmt_pair(quoted, "time", "%s", date);
|
||||
_util_fmt_obj_end();
|
||||
|
||||
if (f.type == FMT_JSONSEQ) {
|
||||
/* Tool meta-data is a separate object for JSONSEQ. */
|
||||
if (util_fmt_is_json_stream(f.type)) {
|
||||
/* Tool meta-data is a separate object for JSON streams. */
|
||||
util_fmt_obj_end();
|
||||
}
|
||||
}
|
||||
@@ -478,7 +502,7 @@ void util_fmt_obj_end(void)
|
||||
{
|
||||
_util_fmt_obj_end();
|
||||
|
||||
if (f.lvl == 1 && f.meta_done && f.type != FMT_JSONSEQ) {
|
||||
if (f.lvl == 1 && f.meta_done && !util_fmt_is_json_stream(f.type)) {
|
||||
/* Emit closure for top-level meta-container object. */
|
||||
util_fmt_obj_end();
|
||||
}
|
||||
@@ -737,7 +761,7 @@ void util_fmt_init(FILE *fd, enum util_fmt_t type, unsigned int flags,
|
||||
f.do_warn = (flags & FMT_WARN);
|
||||
f.handle_int = (flags & FMT_HANDLEINT);
|
||||
f.api_level = api_level;
|
||||
if (type == FMT_JSONSEQ)
|
||||
if (util_fmt_is_json_stream(type))
|
||||
f.nl = "";
|
||||
else
|
||||
f.nl = "\n";
|
||||
@@ -750,6 +774,7 @@ void util_fmt_init(FILE *fd, enum util_fmt_t type, unsigned int flags,
|
||||
break;
|
||||
case FMT_JSON:
|
||||
case FMT_JSONSEQ:
|
||||
case FMT_JSONL:
|
||||
f.obj_start = &json_obj_start;
|
||||
f.obj_end = &json_obj_end;
|
||||
f.map = &json_map;
|
||||
|
||||
@@ -193,6 +193,9 @@ int main(int UNUSED(argc), char *UNUSED(argv[]))
|
||||
announce("JSON formatted as sequence");
|
||||
simple_example(FMT_JSONSEQ, FMT_DEFAULT);
|
||||
|
||||
announce("JSON Lines format");
|
||||
simple_example(FMT_JSONL, FMT_DEFAULT);
|
||||
|
||||
announce("Pairs output");
|
||||
simple_example(FMT_PAIRS, FMT_KEEPINVAL);
|
||||
|
||||
@@ -232,6 +235,9 @@ int main(int UNUSED(argc), char *UNUSED(argv[]))
|
||||
announce("JSON sequence output with meta-data");
|
||||
meta_example(FMT_JSONSEQ);
|
||||
|
||||
announce("JSON Lines output with meta-data");
|
||||
meta_example(FMT_JSONL);
|
||||
|
||||
announce("Pairs output with meta-data");
|
||||
meta_example(FMT_PAIRS);
|
||||
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
include ../common.mak
|
||||
|
||||
zsh-completions = _lsstp
|
||||
bash-completions = lsstp.bash
|
||||
|
||||
include ../common_autocomp.mak
|
||||
|
||||
libs = $(rootdir)/libutil/libutil.a
|
||||
all: lsstp
|
||||
|
||||
|
||||
16
lsstp/autocompletion_generator_host.c
Normal file
16
lsstp/autocompletion_generator_host.c
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright IBM Corp.
|
||||
*/
|
||||
|
||||
#include "lib/util_autocomp.h"
|
||||
|
||||
#include "lsstp_cli.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
generate_autocomp(opt_vec, "lsstp");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -22,6 +22,8 @@
|
||||
#include "lib/util_prg.h"
|
||||
#include "lib/util_path.h"
|
||||
|
||||
#include "lsstp_cli.h"
|
||||
|
||||
static const struct util_prg prg = {
|
||||
.desc = "Display STP system information",
|
||||
.args = "",
|
||||
@@ -34,12 +36,6 @@ static const struct util_prg prg = {
|
||||
}
|
||||
};
|
||||
|
||||
static struct util_opt opt_vec[] = {
|
||||
UTIL_OPT_HELP,
|
||||
UTIL_OPT_VERSION,
|
||||
UTIL_OPT_END
|
||||
};
|
||||
|
||||
struct stp_parms {
|
||||
uint64_t ctn_id;
|
||||
unsigned int online;
|
||||
|
||||
18
lsstp/lsstp_cli.h
Normal file
18
lsstp/lsstp_cli.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright IBM Corp.
|
||||
*/
|
||||
|
||||
#ifndef LSSTP_CLI_H
|
||||
#define LSSTP_CLI_H
|
||||
|
||||
#include "lib/util_opt.h"
|
||||
|
||||
static struct util_opt opt_vec[] = {
|
||||
UTIL_OPT_HELP,
|
||||
UTIL_OPT_VERSION,
|
||||
UTIL_OPT_END
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -2,6 +2,11 @@ include ../common.mak
|
||||
|
||||
TESTS := tests/
|
||||
|
||||
zsh-completions = _opticsmon
|
||||
bash-completions = opticsmon.bash
|
||||
|
||||
include ../common_autocomp.mak
|
||||
|
||||
libs =$(rootdir)/libzpci/libzpci.a $(rootdir)/libutil/libutil.a
|
||||
|
||||
ifneq (${HAVE_OPENSSL},0)
|
||||
|
||||
16
opticsmon/autocompletion_generator_host.c
Normal file
16
opticsmon/autocompletion_generator_host.c
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright IBM Corp.
|
||||
*/
|
||||
|
||||
#include "lib/util_autocomp.h"
|
||||
|
||||
#include "opticsmon_cli.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
generate_autocomp(opt_vec, "opticsmon");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -45,7 +45,7 @@ opticsmon - Monitor optical modules for directly attached PCI based NICs
|
||||
Use
|
||||
.B opticsmon
|
||||
to monitor the health of the optical modules of directly attached PCI based
|
||||
NICs. When executed without the \fB--daemon\fR option it will collect optical
|
||||
NICs. When executed without the \fB--monitor\fR option it will collect optical
|
||||
module data from all available PCI network interface physical functions and
|
||||
print a summary in JSON format. Add the \fB--send-report\fR option to report
|
||||
this data to the support element.
|
||||
@@ -55,7 +55,7 @@ this data to the support element.
|
||||
.
|
||||
.SH OPTIONS
|
||||
.SS Operation Options
|
||||
.OD daemon "d"
|
||||
.OD monitor "m"
|
||||
Run continuously and report on link state changes and periodically
|
||||
.PP
|
||||
.
|
||||
|
||||
@@ -27,10 +27,11 @@
|
||||
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include "optics_info.h"
|
||||
#include "optics_sclp.h"
|
||||
#include "ethtool.h"
|
||||
#include "link_mon.h"
|
||||
#include "optics_info.h"
|
||||
#include "optics_sclp.h"
|
||||
#include "opticsmon_cli.h"
|
||||
|
||||
#define API_LEVEL 1
|
||||
|
||||
@@ -61,44 +62,6 @@ static const struct util_prg prg = {
|
||||
UTIL_PRG_COPYRIGHT_END }
|
||||
};
|
||||
|
||||
#define OPT_DUMP 128
|
||||
|
||||
static struct util_opt opt_vec[] = {
|
||||
UTIL_OPT_SECTION("OPERATION OPTIONS"),
|
||||
{
|
||||
.option = { "monitor", no_argument, NULL, 'm' },
|
||||
.desc = "Run continuously and report on link state changes "
|
||||
"collecting optics health data when a change is detected",
|
||||
},
|
||||
{
|
||||
.option = { "send-report", no_argument, NULL, 'r' },
|
||||
.desc = "Report the optics health data to the Support Element",
|
||||
},
|
||||
{
|
||||
.option = { "quiet", no_argument, NULL, 'q' },
|
||||
.desc = "Be quiet and don't print optics health summary",
|
||||
},
|
||||
{
|
||||
.option = { "module-info", no_argument, NULL, OPT_DUMP },
|
||||
.desc = "Include a base64 encoded binary dump of the module's "
|
||||
"SFF-8636/8472/8024 standard data for each netdev. "
|
||||
"This matches \"ethtool --module-info <netdev> raw on\"",
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
UTIL_OPT_SECTION("OPTIONS WITH ARGUMENTS"),
|
||||
{
|
||||
.option = { "interval", required_argument, NULL, 'i' },
|
||||
.argument = "seconds",
|
||||
.desc = "Interval in seconds at which to collect monitoring data "
|
||||
"in the absence of link state changes. A value larger than "
|
||||
"24 hours (86400 seconds) is clamped down to 24 hours.",
|
||||
},
|
||||
UTIL_OPT_SECTION("GENERAL OPTIONS"),
|
||||
UTIL_OPT_HELP,
|
||||
UTIL_OPT_VERSION,
|
||||
UTIL_OPT_END
|
||||
};
|
||||
|
||||
static void parse_cmdline(int argc, char *argv[], struct options *opts)
|
||||
{
|
||||
uint32_t seconds;
|
||||
|
||||
50
opticsmon/opticsmon_cli.h
Normal file
50
opticsmon/opticsmon_cli.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright IBM Corp.
|
||||
*/
|
||||
|
||||
#ifndef OPTICSMON_CLI_H
|
||||
#define OPTICSMON_CLI_H
|
||||
|
||||
#include "lib/util_opt.h"
|
||||
|
||||
#define OPT_DUMP 128
|
||||
|
||||
static struct util_opt opt_vec[] = {
|
||||
UTIL_OPT_SECTION("OPERATION OPTIONS"),
|
||||
{
|
||||
.option = { "monitor", no_argument, NULL, 'm' },
|
||||
.desc = "Run continuously and report on link state changes "
|
||||
"collecting optics health data when a change is detected",
|
||||
},
|
||||
{
|
||||
.option = { "send-report", no_argument, NULL, 'r' },
|
||||
.desc = "Report the optics health data to the Support Element",
|
||||
},
|
||||
{
|
||||
.option = { "quiet", no_argument, NULL, 'q' },
|
||||
.desc = "Be quiet and don't print optics health summary",
|
||||
},
|
||||
{
|
||||
.option = { "module-info", no_argument, NULL, OPT_DUMP },
|
||||
.desc = "Include a base64 encoded binary dump of the module's "
|
||||
"SFF-8636/8472/8024 standard data for each netdev. "
|
||||
"This matches 'ethtool --module-info <netdev> raw on'",
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
UTIL_OPT_SECTION("OPTIONS WITH ARGUMENTS"),
|
||||
{
|
||||
.option = { "interval", required_argument, NULL, 'i' },
|
||||
.argument = "seconds",
|
||||
.desc = "Interval in seconds at which to collect monitoring data "
|
||||
"in the absence of link state changes. A value larger than "
|
||||
"24 hours (86400 seconds) is clamped down to 24 hours.",
|
||||
},
|
||||
UTIL_OPT_SECTION("GENERAL OPTIONS"),
|
||||
UTIL_OPT_HELP,
|
||||
UTIL_OPT_VERSION,
|
||||
UTIL_OPT_END
|
||||
};
|
||||
|
||||
#endif
|
||||
105
rust/Cargo.lock
generated
105
rust/Cargo.lock
generated
@@ -62,9 +62,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.95"
|
||||
version = "1.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
|
||||
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
@@ -111,6 +111,15 @@ dependencies = [
|
||||
"wyz",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
@@ -212,6 +221,25 @@ dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curl"
|
||||
version = "0.4.49"
|
||||
@@ -302,6 +330,16 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enum_dispatch"
|
||||
version = "0.3.13"
|
||||
@@ -363,6 +401,16 @@ version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.16"
|
||||
@@ -404,6 +452,12 @@ version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
@@ -641,6 +695,20 @@ dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pvebc"
|
||||
version = "0.12.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"clap_complete",
|
||||
"hex",
|
||||
"s390_pv_core",
|
||||
"sha2",
|
||||
"utils",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pvimg"
|
||||
version = "0.12.0"
|
||||
@@ -687,6 +755,7 @@ dependencies = [
|
||||
"s390_pv",
|
||||
"serde_yaml",
|
||||
"utils",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -891,6 +960,7 @@ dependencies = [
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_test",
|
||||
"sha2",
|
||||
"thiserror",
|
||||
"zerocopy",
|
||||
]
|
||||
@@ -969,6 +1039,17 @@ dependencies = [
|
||||
"unsafe-libyaml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
@@ -1069,6 +1150,12 @@ dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
|
||||
|
||||
[[package]]
|
||||
name = "unarray"
|
||||
version = "0.1.4"
|
||||
@@ -1112,6 +1199,12 @@ version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "wait-timeout"
|
||||
version = "0.2.0"
|
||||
@@ -1238,18 +1331,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.25"
|
||||
version = "0.8.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
|
||||
checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.25"
|
||||
version = "0.8.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
|
||||
checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
@@ -5,6 +5,7 @@ members = [
|
||||
"pv_core",
|
||||
"pvapconfig",
|
||||
"pvattest",
|
||||
"pvebc",
|
||||
"pvimg",
|
||||
"pvinfo",
|
||||
"pvsecret",
|
||||
|
||||
@@ -10,8 +10,10 @@ CARGO_TARGETS :=
|
||||
PV_TARGETS :=
|
||||
CARGO_TEST_TARGETS :=
|
||||
|
||||
SEL_EBC_MODDIR := 95sel-ebc
|
||||
|
||||
ifneq (${HAVE_CARGO},0)
|
||||
CARGO_TARGETS :=
|
||||
CARGO_TARGETS := pvebc
|
||||
|
||||
BUILD_TARGETS = $(CARGO_TARGETS)
|
||||
INSTALL_TARGETS := install-rust-tools install-man install-shell-completions
|
||||
@@ -29,6 +31,7 @@ ifneq (${HAVE_LIBCURL},0)
|
||||
|
||||
ifeq ($(HOST_ARCH),s390x)
|
||||
PV_TARGETS += pvapconfig pvinfo
|
||||
INSTALL_TARGETS += install-ebc-dracut-module
|
||||
else
|
||||
BUILD_TARGETS += skip-pvapconfig skip-pvinfo
|
||||
endif #HOSTARCH
|
||||
@@ -104,6 +107,23 @@ endif # CARGO
|
||||
|
||||
rust-test: $(CARGO_TEST_TARGETS)
|
||||
|
||||
install-ebc-dracut-module:
|
||||
ifneq ($(HAVE_DRACUT),0)
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(DRACUTMODDIR)/
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(DRACUTMODDIR)/$(SEL_EBC_MODDIR)
|
||||
$(INSTALL) -m 755 pvebc/$(SEL_EBC_MODDIR)/module-setup.sh \
|
||||
pvebc/$(SEL_EBC_MODDIR)/override-crypttab.sh \
|
||||
pvebc/$(SEL_EBC_MODDIR)/pvebc-wrapper.sh \
|
||||
$(DESTDIR)$(DRACUTMODDIR)/$(SEL_EBC_MODDIR)
|
||||
$(INSTALL) -m 644 pvebc/$(SEL_EBC_MODDIR)/boot.mount \
|
||||
pvebc/$(SEL_EBC_MODDIR)/sel-ebc-override-crypttab.service \
|
||||
pvebc/$(SEL_EBC_MODDIR)/sel-ebc-paes-enforce.service \
|
||||
pvebc/$(SEL_EBC_MODDIR)/sel-ebc-pvebc.service \
|
||||
pvebc/$(SEL_EBC_MODDIR)/sel-ebc.crypttab \
|
||||
pvebc/$(SEL_EBC_MODDIR)/sel-ebc.target \
|
||||
$(DESTDIR)$(DRACUTMODDIR)/$(SEL_EBC_MODDIR)
|
||||
endif
|
||||
|
||||
install-rust-tools: $(BUILD_TARGETS)
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR)
|
||||
$(foreach target,$(CARGO_TARGETS),\
|
||||
|
||||
@@ -104,7 +104,14 @@ pub mod request {
|
||||
|
||||
/// Reexports some useful OpenSSL symbols
|
||||
pub mod openssl {
|
||||
pub use openssl::{error::ErrorStack, hash::DigestBytes, nid::Nid, pkey, x509};
|
||||
pub use openssl::{
|
||||
error::ErrorStack,
|
||||
hash::DigestBytes,
|
||||
nid::Nid,
|
||||
pkey,
|
||||
sha::{Sha256, Sha512},
|
||||
x509,
|
||||
};
|
||||
// rust-OpenSSL does not define these NIDs
|
||||
#[allow(missing_docs)]
|
||||
pub const NID_ED25519: Nid = Nid::from_raw(openssl_sys::NID_ED25519);
|
||||
@@ -113,6 +120,8 @@ pub mod request {
|
||||
}
|
||||
|
||||
pub use pv_core::request::*;
|
||||
|
||||
pub use pv_core::PolicyReference;
|
||||
}
|
||||
|
||||
/// Functionalities for creating add-secret requests
|
||||
|
||||
@@ -248,7 +248,7 @@ fn check_key_format(kind: UserDataType, key: &PKeyRef<Public>) -> Result<()> {
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Extracrted user-data if available
|
||||
/// Extracted user-data if available
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
|
||||
@@ -23,6 +23,7 @@ zerocopy = {version = "0.8", features = ["derive"]}
|
||||
serde = { version = "1.0.217", features = ["derive"]}
|
||||
byteorder = "1.5"
|
||||
regex = "1.10"
|
||||
sha2 = "0.10.9"
|
||||
|
||||
[dev-dependencies]
|
||||
serde_test = "1.0.177"
|
||||
|
||||
@@ -6,6 +6,7 @@ mod apdevice;
|
||||
mod confidential;
|
||||
mod error;
|
||||
mod macros;
|
||||
mod policy;
|
||||
mod utils;
|
||||
mod uvattest;
|
||||
mod uvdevice;
|
||||
@@ -13,6 +14,13 @@ mod uvsecret;
|
||||
|
||||
pub use error::{Error, FileAccessErrorType, FileIoErrorType, Result};
|
||||
|
||||
/// Early Boot Customization (EBC) utilities.
|
||||
///
|
||||
/// This module provides types and functions for working with Early Boot
|
||||
/// Customization. The integrity and completeness of ASRs are ensured through
|
||||
/// cryptographically protected table of contents files.
|
||||
pub use policy::PolicyReference;
|
||||
|
||||
/// Functionalities for reading attestation requests
|
||||
pub mod attest {
|
||||
pub use crate::uvattest::{AttestationMagic, AttestationMeasAlg};
|
||||
|
||||
112
rust/pv_core/src/policy.rs
Normal file
112
rust/pv_core/src/policy.rs
Normal file
@@ -0,0 +1,112 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp.
|
||||
|
||||
use crate::misc::encode_hex;
|
||||
use crate::utils::open_file;
|
||||
use crate::{Error, Result};
|
||||
use std::{
|
||||
fmt::{Display, Formatter, Result as Resfmt},
|
||||
fs::File,
|
||||
os::unix::ffi::OsStrExt,
|
||||
path::{Path, PathBuf},
|
||||
str::from_utf8,
|
||||
};
|
||||
use zerocopy::{FromBytes, Immutable, IntoBytes};
|
||||
|
||||
const HASH_LEN: usize = 32;
|
||||
// UserDataType::Unsigned.max() returns 512
|
||||
const USER_DATA_MAX_SIZE: usize = 512;
|
||||
|
||||
/// A reference to a policy file containing its SHA-256 hash and file path.
|
||||
///
|
||||
/// This structure is used in Early Boot Customization (EBC) to store
|
||||
/// a reference to a policy file. It contains the SHA-256 hash of the policy
|
||||
/// file content and the file path as a fixed-size byte array.
|
||||
///
|
||||
/// The total size is constrained by `USER_DATA_MAX_SIZE` (512 bytes), with
|
||||
/// 32 bytes allocated for the hash and the remaining bytes for the file path.
|
||||
#[derive(Debug, FromBytes, IntoBytes, Immutable, Copy, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct PolicyReference {
|
||||
/// SHA-256 hash of the policy file content (32 bytes)
|
||||
pub hash: [u8; HASH_LEN],
|
||||
/// File path stored as a null-terminated byte array
|
||||
pub name: [u8; USER_DATA_MAX_SIZE - HASH_LEN],
|
||||
}
|
||||
|
||||
impl PolicyReference {
|
||||
/// Creates a new `PolicyReference` from a file path.
|
||||
///
|
||||
/// Opens the file, reads its content, and computes the SHA-256 hash.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `src` - The path to the policy file
|
||||
/// * `sha256` - A function that computes the SHA-256 hash of the content
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns a `PolicyReference` containing the SHA-256 hash and the file path,
|
||||
/// or an error if the file cannot be opened or the hash computation fails.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// The file path is truncated if it exceeds the available space in the `name` field.
|
||||
pub fn new<P, H>(src: P, sha256: H) -> Result<Self>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
H: Fn(File) -> Result<Vec<u8>>,
|
||||
{
|
||||
let mut ret = Self {
|
||||
hash: [0; HASH_LEN],
|
||||
name: [0; USER_DATA_MAX_SIZE - HASH_LEN],
|
||||
};
|
||||
|
||||
let file = open_file(src.as_ref())?;
|
||||
ret.hash.copy_from_slice(sha256(file)?.as_bytes());
|
||||
let strbytes = src.as_ref().as_os_str().as_bytes();
|
||||
let nbytes = strbytes.len().min(ret.name.len());
|
||||
ret.name[..nbytes].copy_from_slice(&strbytes[..nbytes]);
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
/// Converts the stored file path back to a `PathBuf`.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns the file path as a `PathBuf`, or an error if the stored name
|
||||
/// is not valid UTF-8.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// * `Error::ParseError` - If the name contains invalid UTF-8
|
||||
pub fn to_path(&self) -> Result<PathBuf> {
|
||||
// Extract bytes until the first null byte (null-terminated string)
|
||||
let name_bytes: Vec<u8> = self
|
||||
.name
|
||||
.iter()
|
||||
.copied()
|
||||
.take_while(|&byte| byte != 0)
|
||||
.collect();
|
||||
|
||||
let rust_string = String::from_utf8(name_bytes).map_err(|e| Error::ParseError {
|
||||
subject: "PolicyReference name".to_string(),
|
||||
content: format!("Invalid UTF-8 in name: {}", e),
|
||||
})?;
|
||||
|
||||
Ok(Path::new(&rust_string).to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for PolicyReference {
|
||||
fn fmt(&self, f: &mut Formatter) -> Resfmt {
|
||||
write!(
|
||||
f,
|
||||
"{} {}",
|
||||
encode_hex(self.hash),
|
||||
from_utf8(&self.name).expect("unable to convert name")
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -35,12 +35,12 @@ pub enum Command {
|
||||
/// shred it after verification. Every 'create' will generate a new, random protection key.
|
||||
Create(Box<CreateAttOpt>),
|
||||
|
||||
/// Send the attestation request to the Ultravisor.
|
||||
/// Send the attestation request to the Ultravisor (s390x only.)
|
||||
///
|
||||
/// Run a measurement of this system through ’/dev/uv’. This device must be accessible and the
|
||||
/// attestation Ultravisor facility must be present. The input must be an attestation request
|
||||
/// created with ’pvattest create’. Output will contain the original request and the response
|
||||
/// from the Ultravisor.
|
||||
/// from the Ultravisor. Only available on s390x.
|
||||
Perform(PerformAttOpt),
|
||||
|
||||
/// Verify an attestation response.
|
||||
@@ -110,27 +110,22 @@ pub enum AttAddFlags {
|
||||
FirmwareState,
|
||||
}
|
||||
|
||||
// all members s390x only
|
||||
#[derive(Args, Debug)]
|
||||
pub struct PerformAttOpt {
|
||||
/// Specify the request to be sent.
|
||||
#[cfg(target_arch = "s390x")]
|
||||
#[arg(hide=true, short, long, value_name = "FILE", value_hint = ValueHint::FilePath,)]
|
||||
pub input: Option<String>,
|
||||
|
||||
/// Specify the request to be sent.
|
||||
#[cfg(target_arch = "s390x")]
|
||||
#[arg(value_name = "IN", value_hint = ValueHint::FilePath, required_unless_present("input"), conflicts_with("input"))]
|
||||
pub input_pos: Option<String>,
|
||||
|
||||
/// Write the result to FILE.
|
||||
#[cfg(target_arch = "s390x")]
|
||||
#[arg(hide=true, short, long, value_name = "FILE", value_hint = ValueHint::FilePath,)]
|
||||
pub output: Option<String>,
|
||||
|
||||
/// Write the result to FILE.
|
||||
#[arg(value_name = "OUT", value_hint = ValueHint::FilePath, required_unless_present("output"), conflicts_with("output"))]
|
||||
#[cfg(target_arch = "s390x")]
|
||||
pub output_pos: Option<String>,
|
||||
|
||||
/// Provide up to 256 bytes of user input
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
#![allow(unused)]
|
||||
use anyhow::{anyhow, bail, Error, Result};
|
||||
use pv::{assert_size, request::MagicValue, uv::AttestationCmd, uv::ConfigUid};
|
||||
use std::{
|
||||
|
||||
28
rust/pvebc/95sel-ebc/boot.mount
Normal file
28
rust/pvebc/95sel-ebc/boot.mount
Normal file
@@ -0,0 +1,28 @@
|
||||
[Unit]
|
||||
Description=Mount /boot early in initramfs
|
||||
|
||||
# Initramfs requirement
|
||||
DefaultDependencies=no
|
||||
# Make absolutely sure this only runs in initramfs (and not post-pivot if the
|
||||
# unit ever appears there)
|
||||
ConditionPathExists=/etc/initrd-release
|
||||
ConditionKernelCommandLine=root
|
||||
|
||||
# we use /dev/disk/by-label because it identifies the boot partition system
|
||||
# independently IF set up correctly
|
||||
Requires=dev-disk-by\x2dlabel-boot.device
|
||||
|
||||
# Ordering dependencies
|
||||
After=dev-disk-by\x2dlabel-boot.device
|
||||
Before=sel-ebc-pvebc.service
|
||||
|
||||
[Mount]
|
||||
# system independent identification of boot partition requires that the label
|
||||
# boot is set for the boot partition
|
||||
What=/dev/disk/by-label/boot
|
||||
Where=/boot
|
||||
Type=auto
|
||||
Options=defaults
|
||||
|
||||
[Install]
|
||||
WantedBy=sel-ebc-pvebc.service
|
||||
76
rust/pvebc/95sel-ebc/module-setup.sh
Normal file
76
rust/pvebc/95sel-ebc/module-setup.sh
Normal file
@@ -0,0 +1,76 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Copyright IBM Corp.
|
||||
|
||||
|
||||
# Called by dracut
|
||||
check() {
|
||||
# always include
|
||||
return 0
|
||||
}
|
||||
|
||||
# Called by dracut
|
||||
depends() {
|
||||
# We need systemd in the initramfs
|
||||
echo systemd
|
||||
echo systemd-udevd
|
||||
echo crypt
|
||||
echo dm
|
||||
return 0
|
||||
}
|
||||
|
||||
# Called by dracut
|
||||
installkernel() {
|
||||
# kernel modules needed for opening an encrypted rfs
|
||||
instmods -c uvdevice
|
||||
instmods -c paes_s390
|
||||
instmods -c pkey_uv
|
||||
instmods -c pkey_pckmo
|
||||
instmods -c pkey
|
||||
}
|
||||
|
||||
# Called by dracut
|
||||
install() {
|
||||
# shellcheck disable=SC2154
|
||||
# moddir, systemdsystemunitdir, and initdir are provided by dracut
|
||||
# Copy the units into the initramfs' systemd unit dir
|
||||
inst_simple "$moddir/sel-ebc.target" \
|
||||
"$systemdsystemunitdir/sel-ebc.target"
|
||||
inst_simple "$moddir/sel-ebc-pvebc.service" \
|
||||
"$systemdsystemunitdir/sel-ebc-pvebc.service"
|
||||
inst_simple "$moddir/sel-ebc-paes-enforce.service" \
|
||||
"$systemdsystemunitdir/sel-ebc-paes-enforce.service"
|
||||
inst_simple "$moddir/sel-ebc-override-crypttab.service" \
|
||||
"$systemdsystemunitdir/sel-ebc-override-crypttab.service"
|
||||
inst_simple "$moddir/boot.mount" \
|
||||
"$systemdsystemunitdir/boot.mount"
|
||||
|
||||
# already exisitng unit we depend on for kernel modules
|
||||
inst_simple /usr/lib/systemd/system/systemd-modules-load.service \
|
||||
"$systemdsystemunitdir/systemd-modules-load.service"
|
||||
|
||||
# wrapper for sel-ebc.service
|
||||
inst_simple "$moddir/pvebc-wrapper.sh" \
|
||||
"/etc/sel-ebc/pvebc-wrapper.sh"
|
||||
|
||||
# override crypttab
|
||||
inst_simple "$moddir/override-crypttab.sh" \
|
||||
"/etc/sel-ebc/override-crypttab.sh"
|
||||
|
||||
# copy main application
|
||||
inst_binary "/usr/bin/pvebc"
|
||||
inst_binary "/usr/bin/pvsecret"
|
||||
|
||||
inst_simple "$moddir/sel-ebc.crypttab" "/etc/sel-ebc/crypttab"
|
||||
|
||||
# Create the enablement symlinks in the image using host systemctl:
|
||||
# shellcheck disable=SC2154
|
||||
inst_dir "$initdir/etc/systemd/system"
|
||||
systemctl --root "$initdir" --no-reload --quiet enable sel-ebc.target
|
||||
systemctl --root "$initdir" --no-reload --quiet enable sel-ebc-pvebc.service
|
||||
systemctl --root "$initdir" --no-reload --quiet enable sel-ebc-override-crypttab.service
|
||||
systemctl --root "$initdir" --no-reload --quiet enable sel-ebc-paes-enforce.service
|
||||
systemctl --root "$initdir" --no-reload --quiet enable systemd-modules-load.service
|
||||
systemctl --root "$initdir" --no-reload --quiet enable boot.mount
|
||||
}
|
||||
20
rust/pvebc/95sel-ebc/override-crypttab.sh
Normal file
20
rust/pvebc/95sel-ebc/override-crypttab.sh
Normal file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Copyright IBM Corp.
|
||||
|
||||
IBM_RSRC_DIR="/etc/sel-ebc"
|
||||
|
||||
if [[ ! -f "${IBM_RSRC_DIR}/crypttab" ]]; then
|
||||
echo "Error: source file $IBM_RSRC_DIR/crypttab does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Unconditionally override /etc/crypttab to ensure correct EBC configuration
|
||||
cp "${IBM_RSRC_DIR}/crypttab" "/etc/crypttab"
|
||||
|
||||
systemctl daemon-reload
|
||||
|
||||
systemctl restart systemd-cryptsetup@cryptroot_mapper.service
|
||||
|
||||
exit 0
|
||||
63
rust/pvebc/95sel-ebc/pvebc-wrapper.sh
Normal file
63
rust/pvebc/95sel-ebc/pvebc-wrapper.sh
Normal file
@@ -0,0 +1,63 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Copyright IBM Corp.
|
||||
|
||||
SYSFS=/sys/firmware/uv/prot_virt_guest
|
||||
SICS=/boot/sics
|
||||
EBC_TMPFS=/run/sel-ebc
|
||||
TOC=toc.asr
|
||||
ASR_NAME=luks-rfs-passphrase
|
||||
|
||||
# Early exit for non SEL guests
|
||||
if [[ ! -e $SYSFS ]]; then
|
||||
echo "Not running in a SEL guest."
|
||||
exit 1
|
||||
fi
|
||||
if [[ $(cat $SYSFS) -ne 1 ]]; then
|
||||
echo "Not running in a SEL guest."
|
||||
exit 1
|
||||
fi
|
||||
echo "Running in SEL guest."
|
||||
|
||||
# Copy EBC resources from /boot/sics to tmpfs for security
|
||||
# This protects against host injection attacks by moving resources to UV-protected RAM
|
||||
echo "Copying EBC resources from $SICS to $EBC_TMPFS"
|
||||
if ! mkdir -p "$EBC_TMPFS"; then
|
||||
echo "Failed to create $EBC_TMPFS"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Copy only .asr and .pol files
|
||||
for file in "$SICS"/*.asr "$SICS"/*.pol; do
|
||||
if [[ -f "$file" && ! -L "$file" ]]; then
|
||||
cp "$file" "$EBC_TMPFS/" || {
|
||||
echo "Failed to copy $file to $EBC_TMPFS"
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
done
|
||||
|
||||
# Verify toc.asr was copied
|
||||
if [[ ! -f "$EBC_TMPFS/$TOC" ]]; then
|
||||
echo "Error: $EBC_TMPFS/$TOC does not exist after copy"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# execute the actual tool with the copied toc.asr
|
||||
pvebc --toc "$EBC_TMPFS/$TOC"
|
||||
rc=$?
|
||||
if [[ $rc -ne 0 ]]; then
|
||||
exit $rc
|
||||
fi
|
||||
|
||||
# Retrieve and check for dummy LUKS passphrase
|
||||
pvsecret retrieve --inform name -o "$EBC_TMPFS/$ASR_NAME" --outform bin "$ASR_NAME"
|
||||
|
||||
if [[ ! -f "$EBC_TMPFS/$ASR_NAME" ]]; then
|
||||
echo "$EBC_TMPFS/$ASR_NAME does not exist"
|
||||
fi
|
||||
|
||||
chmod 400 "$EBC_TMPFS/$ASR_NAME"
|
||||
|
||||
exit 0
|
||||
37
rust/pvebc/95sel-ebc/sel-ebc-override-crypttab.service
Normal file
37
rust/pvebc/95sel-ebc/sel-ebc-override-crypttab.service
Normal file
@@ -0,0 +1,37 @@
|
||||
[Unit]
|
||||
Description=Override crypttab
|
||||
|
||||
# boot partition contains SICS
|
||||
# Loading of kernel modules is required which are needed for protected keys
|
||||
Requires=systemd-modules-load.service
|
||||
Requires=boot.mount
|
||||
|
||||
# Ensure this runs before the handoff to the real root, if that's required:
|
||||
After=boot.mount
|
||||
After=systemd-modules-load.service
|
||||
Before=cryptsetup-pre.target
|
||||
Before=cryptsetup.target
|
||||
Before=systemd-cryptsetup@.service
|
||||
Before=initrd-root-device.target
|
||||
After=sel-ebc-pvebc.service
|
||||
|
||||
# Initramfs requirement
|
||||
DefaultDependencies=no
|
||||
# Make absolutely sure this only runs in initramfs
|
||||
ConditionPathExists=/etc/initrd-release
|
||||
ConditionKernelCommandLine=rd.sel-ebc
|
||||
ConditionKernelCommandLine=root
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/bin/bash /etc/sel-ebc/override-crypttab.sh
|
||||
RemainAfterExit=yes
|
||||
# If pvebc fails immediately abort boot
|
||||
FailureAction=poweroff-immediate
|
||||
# boot partition is unencrypted and contains SICS so we can get logs out this way
|
||||
# logs do not leek any sensitive information
|
||||
StandardOutput=file:/boot/sics/log
|
||||
StandardError=file:/boot/sics/log
|
||||
|
||||
[Install]
|
||||
RequiredBy=sel-ebc.target
|
||||
27
rust/pvebc/95sel-ebc/sel-ebc-paes-enforce.service
Normal file
27
rust/pvebc/95sel-ebc/sel-ebc-paes-enforce.service
Normal file
@@ -0,0 +1,27 @@
|
||||
[Unit]
|
||||
Description=Enforce PAES encrypted root fs for SEL guests
|
||||
|
||||
# Ensure this runs before the handoff to the real root, if that's required:
|
||||
After=cryptsetup.target
|
||||
After=initrd-root-device.target
|
||||
Before=sysroot.mount
|
||||
|
||||
# Initramfs requirement
|
||||
DefaultDependencies=no
|
||||
# Make absolutely sure this only runs in initramfs
|
||||
ConditionPathExists=/etc/initrd-release
|
||||
ConditionKernelCommandLine=rd.sel-ebc
|
||||
ConditionKernelCommandLine=root
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=bash -c 'dmsetup table /dev/disk/by-label/root | grep "paes-xts-plain64"'
|
||||
FailureAction=poweroff-immediate
|
||||
RemainAfterExit=yes
|
||||
|
||||
# logs do not leek any sensitive information
|
||||
StandardOutput=file:/boot/sics/log
|
||||
StandardError=file:/boot/sics/log
|
||||
|
||||
[Install]
|
||||
RequiredBy=sel-ebc.target
|
||||
36
rust/pvebc/95sel-ebc/sel-ebc-pvebc.service
Normal file
36
rust/pvebc/95sel-ebc/sel-ebc-pvebc.service
Normal file
@@ -0,0 +1,36 @@
|
||||
[Unit]
|
||||
Description=Run pvebc during early boot to process SICS
|
||||
|
||||
# boot partition contains SICS
|
||||
# Loading of kernel modules is required which are needed for protected keys
|
||||
Requires=systemd-modules-load.service
|
||||
Wants=boot.mount
|
||||
|
||||
# Ensure this runs before the handoff to the real root, if that's required:
|
||||
Before=initrd-root-device.target
|
||||
Before=cryptsetup-pre.target
|
||||
Before=cryptsetup.target
|
||||
After=boot.mount
|
||||
After=systemd-modules-load.service
|
||||
|
||||
# Initramfs requirement
|
||||
DefaultDependencies=no
|
||||
# Make absolutely sure this only runs in initramfs
|
||||
ConditionPathExists=/etc/initrd-release
|
||||
AssertPathIsDirectory=/boot/sics
|
||||
ConditionKernelCommandLine=rd.sel-ebc
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
# execute pvebc
|
||||
ExecStart=/bin/bash /etc/sel-ebc/pvebc-wrapper.sh
|
||||
RemainAfterExit=yes
|
||||
# If pvebc fails immediately abort boot
|
||||
FailureAction=poweroff-immediate
|
||||
# boot partition is unencrypted and contains SICS so we can get logs out this way
|
||||
# logs do not leek any sensitive information
|
||||
StandardOutput=file:/boot/sics/log
|
||||
StandardError=file:/boot/sics/log
|
||||
|
||||
[Install]
|
||||
RequiredBy=sel-ebc.target
|
||||
1
rust/pvebc/95sel-ebc/sel-ebc.crypttab
Normal file
1
rust/pvebc/95sel-ebc/sel-ebc.crypttab
Normal file
@@ -0,0 +1 @@
|
||||
cryptroot_mapper /dev/disk/by-label/cryptroot /run/sel-ebc/luks-rfs-passphrase
|
||||
27
rust/pvebc/95sel-ebc/sel-ebc.target
Normal file
27
rust/pvebc/95sel-ebc/sel-ebc.target
Normal file
@@ -0,0 +1,27 @@
|
||||
[Unit]
|
||||
Description=Target unit for IBM Secure Execution for Linux early boot customization
|
||||
|
||||
# enable for use in initramfs
|
||||
DefaultDependencies=no
|
||||
|
||||
# if not met start the target without starting its dependencies
|
||||
ConditionPathExists=/etc/initrd-release
|
||||
ConditionKernelCommandLine=rd.sel-ebc
|
||||
|
||||
# if not met the target is considered failed
|
||||
# AssertVirtualization=kvm
|
||||
|
||||
# if target fails abort immediately
|
||||
FailureAction=poweroff-immediate
|
||||
|
||||
Requires=sel-ebc-pvebc.service
|
||||
Requires=sel-ebc-override-crypttab.service
|
||||
Requires=sel-ebc-paes-enforce.service
|
||||
|
||||
# ordering dependencies
|
||||
After=systemd-modules-load.service
|
||||
Before=initrd-root-device.target
|
||||
|
||||
[Install]
|
||||
# most basic hook to start target by default
|
||||
WantedBy=initrd.target
|
||||
22
rust/pvebc/Cargo.toml
Normal file
22
rust/pvebc/Cargo.toml
Normal file
@@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "pvebc"
|
||||
version = "0.12.0"
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
sha2 = "0.10"
|
||||
hex = "0.4"
|
||||
clap = { version = "4.5", features = ["derive"] }
|
||||
pv_core = { path = "../pv_core" , package = "s390_pv_core" }
|
||||
utils = { path = "../utils" }
|
||||
anyhow = { version = "1.0.95", features = ["std"] }
|
||||
zerocopy = { version = "0.8.27", features = ["derive"] }
|
||||
|
||||
[build-dependencies]
|
||||
clap = { version ="4.5", features = ["derive"]}
|
||||
clap_complete = "4.5"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
165
rust/pvebc/man/pvebc.1
Normal file
165
rust/pvebc/man/pvebc.1
Normal file
@@ -0,0 +1,165 @@
|
||||
.\" Copyright IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
|
||||
.TH "PVEBC" "1" "2026-03-24" "s390-tools" "EBC Management Manual"
|
||||
.nh
|
||||
.ad l
|
||||
.SH NAME
|
||||
pvebc \- Protected Virtualization Early Boot Customization Tool
|
||||
.SH SYNOPSIS
|
||||
.nf
|
||||
.fam C
|
||||
pvebc [OPTIONS] \-\-toc <FILE>
|
||||
.fam C
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
Use the \fBpvebc\fR tool to process add-secret request files (\fBtoc.asr\fR)
|
||||
that define the root of early boot customization (EBC) resources for
|
||||
IBM Secure Execution for Linux (SEL) guests. The tool validates the
|
||||
integrity and completeness of add-secret request (ASR) files using
|
||||
cryptographic verification to prevent tampering during transmission
|
||||
over unsecured channels.
|
||||
|
||||
The tool operates on a structure consisting of three types of files:
|
||||
.RS 4
|
||||
\- \fBtoc.asr\fP: The root add\-secret request file that references the table of contents (TOC policy).
|
||||
|
||||
\- \fBtoc.pol\fP: The table of contents policy file that contains a list of AES\-GCM authentication tags (MAC tags)
|
||||
|
||||
\- \fBASR files\fP: Individual add-secret request files to be added to the guest
|
||||
|
||||
.RE
|
||||
The integrity protection mechanism works as follows:
|
||||
.RS 4
|
||||
.IP "1." 3
|
||||
Each add-secret request file includes an AES\-GCM authentication tag (last 16 bytes), which provides integrity
|
||||
protection for the file.
|
||||
.IP "2." 3
|
||||
The toc.pol file lists the expected message authentication code (MAC) tags of all add-secret request files.
|
||||
.IP "3." 3
|
||||
The toc.asr file contains a cryptographically protected reference to toc.pol, consisting
|
||||
of the relative file path and a SHA\-512 hash of the policy file.
|
||||
.IP "4." 3
|
||||
The AES\-GCM authentication tag of toc.asr protects the integrity of this reference.
|
||||
.RE
|
||||
|
||||
This structure prevents attackers from:
|
||||
.RS 4
|
||||
\- Removing add-secret request files from the set
|
||||
|
||||
\- Inserting unauthorized add-secret request files
|
||||
|
||||
\- Modifying existing add-secret request files
|
||||
|
||||
\- Altering the policy file
|
||||
|
||||
.RE
|
||||
\fBpvebc\fR validates the entire chain of trust by:
|
||||
.RS 4
|
||||
\- Verifying that the SHA\-512 hash of toc.pol matches the reference stored in toc.asr
|
||||
|
||||
\- Checking that all MAC tags listed in toc.pol correspond to existing add-secret request files
|
||||
|
||||
\- Ensuring that all add-secret request files are present and unmodified
|
||||
|
||||
.RE
|
||||
The tool can run in one of two modes:
|
||||
.RS 4
|
||||
\- \fBNormal mode\fP: Validates add\-secret requests and adds the defined secrets to the SEL guest (z/Architecture only)
|
||||
|
||||
\- \fBDry\-run mode\fP: Validates the request structure without adding secrets; useful for verification on non\-SEL systems
|
||||
|
||||
.RE
|
||||
.SH OPTIONS
|
||||
.PP
|
||||
\-t, \-\-toc <FILE>
|
||||
.RS 4
|
||||
Specify the table\-of\-contents add\-secret request file toc.asr, which serves as the root of the EBC
|
||||
resources. This file must contain a reference to toc.pol in its user data field,
|
||||
as generated by \fBpvsecret\fR with the \fB\-\-policy\fR option. The toc.asr
|
||||
file cryptographically links to the policy file by using a relative path and
|
||||
SHA\-512 hash, ensuring the integrity of the complete EBC structure.
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
\-\-dry\-run
|
||||
.RS 4
|
||||
Validate the EBC structure without adding the add\-secret requests to the
|
||||
ultravisor. Use this option to verify the integrity of the generated
|
||||
policy file and the links between add\-secret request files and the policy.
|
||||
This mode can be run on non\-SEL guests to validate the
|
||||
structure before deployment. When \fB\-\-dry\-run\fR is specified, the tool
|
||||
performs all cryptographic verifications but skips the actual addition of
|
||||
secrets to the ultravisor secret store.
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
\-\-version
|
||||
.RS 4
|
||||
Print version information and exit.
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
\-h, \-\-help
|
||||
.RS 4
|
||||
Print help information.
|
||||
.RE
|
||||
.RE
|
||||
|
||||
.SH FILES
|
||||
.PP
|
||||
The \fBpvebc\fR tool operates on the following file types (actual names may
|
||||
differ):
|
||||
.PP
|
||||
\fBtoc.asr\fR
|
||||
.RS 4
|
||||
The root add\-secret request file that contains a reference to the table\-of\-content \fBtoc.pol\fR in its
|
||||
user data field. This file is generated by using \fBpvsecret create\fR with the
|
||||
\fB\-\-policy\fR option.
|
||||
.RE
|
||||
.PP
|
||||
\fBtoc.pol\fR
|
||||
.RS 4
|
||||
The policy file containing a newline\-separated list of AES\-GCM authentication
|
||||
tags (MAC tags) in hexadecimal format. Each entry corresponds to one
|
||||
add\-secret request file.
|
||||
This file is generated by using \fBpvsecret create\fR with the
|
||||
\fB\-\-toc\-policy\fR option.
|
||||
.RE
|
||||
.PP
|
||||
\fB*.asr\fR
|
||||
.RS 4
|
||||
Individual add\-secret request files that contain the actual secrets to be added to
|
||||
the SEL guest. Each add\-secret request file includes an AES\-GCM authentication tag (the last 16 bytes), which
|
||||
provides integrity protection.
|
||||
.RE
|
||||
|
||||
.SH EXIT STATUS
|
||||
.PP
|
||||
\fBpvebc\fR returns the following exit codes:
|
||||
.PP
|
||||
\fB0\fR
|
||||
.RS 4
|
||||
Success. All validations passed and secrets were added (if not in dry\-run mode).
|
||||
.RE
|
||||
.PP
|
||||
\fB1\fR
|
||||
.RS 4
|
||||
Failure. An error occurred during validation or secret addition. Error details
|
||||
are printed to stderr.
|
||||
.RE
|
||||
|
||||
.SH NOTES
|
||||
.PP
|
||||
\- All file paths in a policy reference must be relative to the directory
|
||||
that contains the referencing file.
|
||||
.PP
|
||||
\- On z/Architecture systems, the tool requires access to the ultravisor device
|
||||
(\fB/dev/uv\fR) to add secrets.
|
||||
.PP
|
||||
|
||||
.SH "SEE ALSO"
|
||||
.sp
|
||||
\fBpvsecret\fR(1), \fBpvsecret\-create\fR(1), \fBpvsecret\-add\fR(1)
|
||||
39
rust/pvebc/src/cli.rs
Normal file
39
rust/pvebc/src/cli.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp.
|
||||
|
||||
use std::{path::PathBuf, sync::OnceLock};
|
||||
|
||||
use clap::{ArgAction, Parser, ValueHint};
|
||||
|
||||
static VERSION: OnceLock<String> = OnceLock::new();
|
||||
|
||||
/// The pvebc command processes an AddSecretRequest file (toc.asr) that defines the root of EBC
|
||||
/// resources. It validates references to the associated toc.pol policy and manages their addition,
|
||||
/// with options for verification and dry-run execution.
|
||||
#[derive(Parser)]
|
||||
#[command(long_version=ver(), disable_version_flag(true))]
|
||||
pub struct Cli {
|
||||
/// Print version information and exit.
|
||||
#[arg(long, action=ArgAction::Version)]
|
||||
version: (),
|
||||
|
||||
/// Specifies the toc.asr which is the root of the EBC resources
|
||||
///
|
||||
/// Specify the add-secret request file toc.asr that serves as the root of the EBC resources.
|
||||
/// Its user data must contain a reference to toc.pol as generated by pvsecret with the --policy
|
||||
/// option.
|
||||
#[arg(short, long, value_name = "FILE", value_hint = ValueHint::FilePath)]
|
||||
pub toc: PathBuf,
|
||||
|
||||
/// Prevents adding of the AddSecretRequest
|
||||
///
|
||||
/// Do not add the AddSecretRequest. Use this option to validate a generated policy. It can be
|
||||
/// run on a non-SEL guest to verify ASR-to-policy links and the associated policy toc.pol.
|
||||
#[arg(long)]
|
||||
pub dry_run: bool,
|
||||
}
|
||||
|
||||
fn ver() -> &'static str {
|
||||
VERSION.get_or_init(|| utils::tools_version_fmt!(2026))
|
||||
}
|
||||
54
rust/pvebc/src/ebc_utils.rs
Normal file
54
rust/pvebc/src/ebc_utils.rs
Normal file
@@ -0,0 +1,54 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp.
|
||||
|
||||
use anyhow::{bail, Context, Result};
|
||||
use pv_core::misc::open_file;
|
||||
use std::{io::Read, path::Path};
|
||||
|
||||
/// Length of the MAC tag in bytes (last 16 bytes of AddSecretRequest files)
|
||||
pub const MAC_TAG_LEN: usize = 16;
|
||||
|
||||
/// Opens a file and returns a boxed reader
|
||||
pub fn get_reader_from_filepath<P: AsRef<Path>>(filepath: P) -> Result<Box<dyn Read>> {
|
||||
Ok(Box::new(open_file(filepath)?))
|
||||
}
|
||||
|
||||
/// Get reader from &Path with additional context on error
|
||||
pub fn get_reader(filepath: &Path) -> Result<Box<dyn Read>> {
|
||||
get_reader_from_filepath(filepath)
|
||||
.with_context(|| format!("unable to get reader from {:?}", filepath))
|
||||
}
|
||||
|
||||
/// Read all data from a reader into a Vec<u8>
|
||||
pub fn get_data(rd_in: &mut Box<dyn Read>) -> Result<Vec<u8>> {
|
||||
let mut data_in = Vec::new();
|
||||
rd_in
|
||||
.read_to_end(&mut data_in)
|
||||
.context("Cannot read input file")?;
|
||||
|
||||
Ok(data_in)
|
||||
}
|
||||
|
||||
/// Extract the MAC tag (last 16 bytes) from an AddSecretRequest file
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if:
|
||||
/// - The file cannot be read
|
||||
/// - The file is smaller than MAC_TAG_LEN bytes
|
||||
pub fn get_mac_tag(filepath: &Path) -> Result<Vec<u8>> {
|
||||
let mut rd_in = get_reader(filepath)?;
|
||||
let data_in = get_data(&mut rd_in)?;
|
||||
|
||||
if data_in.len() < MAC_TAG_LEN {
|
||||
bail!(
|
||||
"File {:?} too small to contain MAC tag (expected at least {} bytes, got {})",
|
||||
filepath,
|
||||
MAC_TAG_LEN,
|
||||
data_in.len()
|
||||
);
|
||||
}
|
||||
|
||||
Ok(data_in[data_in.len() - MAC_TAG_LEN..].to_vec())
|
||||
}
|
||||
369
rust/pvebc/src/main.rs
Normal file
369
rust/pvebc/src/main.rs
Normal file
@@ -0,0 +1,369 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp.
|
||||
|
||||
//! # pvebc - Protected Virtualization Early Boot Customization Tool
|
||||
//!
|
||||
//! This tool processes AddSecretRequest files (toc.asr) that define the root of EBC
|
||||
//! (Early Boot Customization) resources. It validates references to associated
|
||||
//! toc.pol policies and manages their addition to the system.
|
||||
//!
|
||||
//! ## Features
|
||||
//!
|
||||
//! - Validates AddSecretRequest files and their associated policies
|
||||
//! - Verifies cryptographic hashes of policy files using SHA-256
|
||||
//! - Supports dry-run mode for validation without system changes
|
||||
//! - MAC tag validation for AddSecretRequest files
|
||||
|
||||
#![allow(missing_docs)]
|
||||
|
||||
mod cli;
|
||||
mod ebc_utils;
|
||||
|
||||
#[cfg(target_arch = "s390x")]
|
||||
use pv_core::uv::{AddCmd, UvDevice};
|
||||
#[cfg(target_arch = "s390x")]
|
||||
use utils::get_reader_from_cli_file_arg;
|
||||
|
||||
use anyhow::{bail, Context, Error, Result};
|
||||
use clap::Parser;
|
||||
use pv_core::{
|
||||
misc::{decode_hex, encode_hex},
|
||||
PolicyReference,
|
||||
};
|
||||
use std::{
|
||||
fs::{self, File},
|
||||
io::{BufRead, Read},
|
||||
path::{Path, PathBuf},
|
||||
process::ExitCode,
|
||||
str::from_utf8,
|
||||
};
|
||||
// Don't use openssl here because this tool is intended to run in the initramfs
|
||||
// phase of the boot and there we don't want to dynamically link against a C lib
|
||||
use sha2::{self, Digest};
|
||||
use zerocopy::TryFromBytes;
|
||||
|
||||
use crate::cli::Cli;
|
||||
use crate::ebc_utils::{get_data, get_mac_tag, get_reader};
|
||||
|
||||
/// Offset in bytes where user data starts in an ASRCB v1 structure
|
||||
const V1_USER_DATA_OFFS: usize = 536;
|
||||
/// Size in bytes of the user data field in an ASRCB
|
||||
const USER_DATA_SIZE: usize = 512;
|
||||
|
||||
/// Validates and resolves a policy name relative to a base directory
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns an error if path traversal is detected or the resolved path
|
||||
/// is outside the base directory
|
||||
fn validate_and_resolve_policy_path(base: &Path, name: &str) -> Result<PathBuf> {
|
||||
// Validate path to prevent directory traversal
|
||||
if name.contains("..") || name.starts_with('/') {
|
||||
bail!("Invalid policy name: path traversal detected in '{}'", name);
|
||||
}
|
||||
|
||||
let resolved = base.join(name);
|
||||
|
||||
// Ensure the resulting path is within the expected directory
|
||||
if !resolved.starts_with(base) {
|
||||
bail!(
|
||||
"Policy path '{}' is outside the expected directory",
|
||||
resolved.display()
|
||||
);
|
||||
}
|
||||
|
||||
Ok(resolved)
|
||||
}
|
||||
|
||||
/// Extracts and validates the policy name from a PolicyReference
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns an error if the policy name contains invalid UTF-8 or is empty
|
||||
fn extract_policy_name(policy_ref: &PolicyReference) -> Result<String> {
|
||||
let name = from_utf8(&policy_ref.name)
|
||||
.context("Policy name contains invalid UTF-8")?
|
||||
.trim_matches('\0')
|
||||
.to_string();
|
||||
|
||||
if name.is_empty() {
|
||||
bail!("Policy name is empty");
|
||||
}
|
||||
|
||||
Ok(name)
|
||||
}
|
||||
|
||||
/// Computes the SHA-256 hash of data from a reader.
|
||||
///
|
||||
/// Reads data from the provided reader in 4096-byte chunks and computes
|
||||
/// the SHA-256 hash of the entire content.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `r` - A reader providing the data to hash
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns a `Vec<u8>` containing the 32-byte SHA-256 hash, or an error
|
||||
/// if reading fails.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if reading from the reader fails.
|
||||
pub fn sha256_hash<R: Read>(mut r: R) -> Result<Vec<u8>, Error> {
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
let mut buf: [u8; 4096] = [0; 4096];
|
||||
|
||||
loop {
|
||||
let read = r.read(&mut buf)?;
|
||||
if read == 0 {
|
||||
break;
|
||||
}
|
||||
hasher.update(&buf[..read]);
|
||||
}
|
||||
|
||||
Ok(hasher.finalize().to_vec())
|
||||
}
|
||||
|
||||
/// Extract the user data from an ASRCB
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the ASRCB is too small to contain user data
|
||||
fn get_user_data(asrcb: &[u8]) -> Result<Option<Vec<u8>>> {
|
||||
if asrcb.len() < V1_USER_DATA_OFFS + USER_DATA_SIZE {
|
||||
bail!(
|
||||
"ASRCB too small (expected at least {} bytes, got {})",
|
||||
V1_USER_DATA_OFFS + USER_DATA_SIZE,
|
||||
asrcb.len()
|
||||
);
|
||||
}
|
||||
|
||||
let user_data = &asrcb[V1_USER_DATA_OFFS..V1_USER_DATA_OFFS + USER_DATA_SIZE];
|
||||
Ok(Some(user_data.to_vec()))
|
||||
}
|
||||
|
||||
/// Get user data from AddSecretRequest
|
||||
fn verify_user_data(filepath: &Path) -> Result<Option<Vec<u8>>> {
|
||||
let mut rd_in = get_reader(filepath)?;
|
||||
let data_in = get_data(&mut rd_in)?;
|
||||
|
||||
get_user_data(&data_in).context("Could not verify the Add-secret request")
|
||||
}
|
||||
|
||||
/// Adds the given AddSecretRequest (if dryrun == false) and parses the contained user data
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// On Success returns the PolicyReference parsed from the given AddSecretRequest
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// returns an error if
|
||||
/// - unable to open UvDevice
|
||||
/// - reader from path fails
|
||||
/// - adding of ASR fails
|
||||
/// - verify_user_data fails
|
||||
/// - unable to convert user data to PolicyReference
|
||||
fn asr_to_pol_ref(filepath: &Path, dryrun: bool) -> Result<Option<PolicyReference>> {
|
||||
print!("Add-Secret-Request: \"{}\"", filepath.display());
|
||||
if !dryrun {
|
||||
#[cfg(target_arch = "s390x")]
|
||||
{
|
||||
let uv = UvDevice::open()?;
|
||||
let mut rd_in = get_reader_from_cli_file_arg(filepath)?;
|
||||
let mut cmd = AddCmd::new(&mut rd_in)
|
||||
.context(format!("Processing input file {:?}", filepath.to_str()))?;
|
||||
uv.send_cmd(&mut cmd)?;
|
||||
println!();
|
||||
}
|
||||
#[cfg(not(target_arch = "s390x"))]
|
||||
{
|
||||
println!(" (skip adding: not running on s390x architecture)");
|
||||
}
|
||||
} else {
|
||||
println!(" (skip adding: dry-run mode)");
|
||||
}
|
||||
|
||||
Ok(match verify_user_data(filepath)? {
|
||||
Some(ud) => {
|
||||
let ret = PolicyReference::try_read_from_bytes(&ud).map_err(|e| {
|
||||
anyhow::anyhow!("Failed to parse PolicyReference from user data: {:?}", e)
|
||||
})?;
|
||||
println!(" Reference: {}", ret);
|
||||
Some(ret)
|
||||
}
|
||||
None => None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Read the mac tag list of the toc policy, parse and verify every tag
|
||||
fn execute_toc_pol(path: &Path, toc_pol_ref: PolicyReference) -> Result<Vec<PathBuf>> {
|
||||
let name = extract_policy_name(&toc_pol_ref)?;
|
||||
let toc_path = validate_and_resolve_policy_path(path, &name)?;
|
||||
let mut ret: Vec<PathBuf> = Vec::new();
|
||||
let mut rd_in = get_reader(&toc_path)?;
|
||||
let data_in = get_data(&mut rd_in)?;
|
||||
|
||||
let macs = data_in.lines();
|
||||
let base = get_base_dir(&toc_path);
|
||||
|
||||
println!("Mac tags in {:?}:", toc_path);
|
||||
for mac in macs {
|
||||
let mut mac_tag = Vec::new();
|
||||
let mac_tag_ref = match mac {
|
||||
Ok(s) => s,
|
||||
_ => continue,
|
||||
};
|
||||
print!(" {}", mac_tag_ref);
|
||||
|
||||
let entries = fs::read_dir(base)?;
|
||||
for entry in entries {
|
||||
let path = entry?;
|
||||
let filepath = path.path();
|
||||
let ext = match filepath.extension() {
|
||||
Some(e) => e,
|
||||
None => continue,
|
||||
};
|
||||
if ext == "asr" {
|
||||
mac_tag = get_mac_tag(filepath.as_path())?;
|
||||
|
||||
if mac_tag == decode_hex(&mac_tag_ref)? {
|
||||
println!(" -> {}", filepath.display());
|
||||
ret.push(filepath.clone());
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if mac_tag != decode_hex(&mac_tag_ref)? {
|
||||
bail!(
|
||||
"No ASR with mac tag \"{}\" found in {:?}",
|
||||
mac_tag_ref,
|
||||
base
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
println!();
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
/// Checks whether the given hash in polref matches the actual hash of the referenced file
|
||||
fn verify_policy(basename: &Path, polref: PolicyReference) -> Result<()> {
|
||||
let name = extract_policy_name(&polref)?;
|
||||
let filepath = validate_and_resolve_policy_path(basename, &name)?;
|
||||
println!("Verify \"{}\"", filepath.display());
|
||||
|
||||
println!(" Referenced: {}", encode_hex(polref.hash));
|
||||
let f = File::open(filepath.as_path())?;
|
||||
|
||||
let check_hash = sha256_hash(f)?;
|
||||
println!(" Calculated: {}", encode_hex(&check_hash));
|
||||
|
||||
if check_hash != polref.hash {
|
||||
bail!(
|
||||
"{:?} ({}) does not match expected hash ({})",
|
||||
filepath,
|
||||
encode_hex(&check_hash),
|
||||
encode_hex(polref.hash)
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// For every AddSecretRequest find the corresponding policy
|
||||
fn execute_asrs(asrs: Vec<PathBuf>, dryrun: bool) -> Result<()> {
|
||||
// loop over ASRs
|
||||
for asr in asrs {
|
||||
// get the referenced policy
|
||||
let pol_ref = match asr_to_pol_ref(asr.as_path(), dryrun)? {
|
||||
Some(pr) => pr,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
if pol_ref.hash.iter().all(|&b| b == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let base = get_base_dir(asr.as_path());
|
||||
let name = extract_policy_name(&pol_ref)?;
|
||||
let _pol_path = validate_and_resolve_policy_path(base, &name)?;
|
||||
|
||||
// verify the integrity of the referenced policy
|
||||
verify_policy(base, pol_ref)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the parent directory of the given file
|
||||
fn get_base_dir(filepath: &Path) -> &Path {
|
||||
match filepath.parent() {
|
||||
Some(p) => p,
|
||||
None => Path::new(""),
|
||||
}
|
||||
}
|
||||
|
||||
/// Print a given error and return the failure exit code
|
||||
fn error_to_exit_code(err: Error) -> ExitCode {
|
||||
eprintln!("Error: {}", err);
|
||||
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
|
||||
/// main function
|
||||
pub fn main() -> ExitCode {
|
||||
let opt: Cli = Cli::parse();
|
||||
|
||||
let dryrun = opt.dry_run;
|
||||
|
||||
if dryrun {
|
||||
println!("Dry-run mode detected, skipping secret addition");
|
||||
println!();
|
||||
}
|
||||
|
||||
// get toc asr filepath - wrapper script has already copied files to tmpfs
|
||||
let toc_asr = &opt.toc;
|
||||
let basename = match toc_asr.parent() {
|
||||
Some(p) => p,
|
||||
None => {
|
||||
return error_to_exit_code(Error::msg("Unable to get directory from specified path"))
|
||||
}
|
||||
};
|
||||
|
||||
// get the PolicyReference from toc.asr to toc.pol
|
||||
let toc_pol = match asr_to_pol_ref(toc_asr, dryrun) {
|
||||
Ok(o) => match o {
|
||||
Some(r) => r,
|
||||
None => {
|
||||
return error_to_exit_code(Error::msg(
|
||||
"There is no linked policy in the supplied ASR",
|
||||
))
|
||||
}
|
||||
},
|
||||
Err(e) => return error_to_exit_code(e),
|
||||
};
|
||||
|
||||
// verify the integrity on the referenced toc.pol
|
||||
if let Err(e) = verify_policy(basename, toc_pol) {
|
||||
return error_to_exit_code(e);
|
||||
}
|
||||
|
||||
println!();
|
||||
|
||||
// get and verify ASRs from mac list of toc policy
|
||||
let asr_list = match execute_toc_pol(basename, toc_pol) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return error_to_exit_code(e),
|
||||
};
|
||||
|
||||
// execute ASRs
|
||||
if let Err(e) = execute_asrs(asr_list, dryrun) {
|
||||
return error_to_exit_code(e);
|
||||
}
|
||||
|
||||
ExitCode::SUCCESS
|
||||
}
|
||||
@@ -28,7 +28,7 @@ use std::{fmt::Display, fmt::LowerHex, marker::PhantomData, mem::size_of};
|
||||
|
||||
use pv::misc::{Flags, Msb0Flags64};
|
||||
|
||||
pub trait IntoEnumIterator {
|
||||
pub trait IntoEnumIterator: Sized {
|
||||
/// Returns an iterator over all variants of this enum.
|
||||
fn iter() -> impl Iterator<Item = Self>;
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ pub mod serde_base64 {
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
BASE64_STANDARD.decode(&s).map_err(serde::de::Error::custom)
|
||||
BASE64_STANDARD.decode(s).map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ pub mod serde_base64_array {
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
let decoded = BASE64_STANDARD
|
||||
.decode(&s)
|
||||
.decode(s)
|
||||
.map_err(serde::de::Error::custom)?;
|
||||
try_copy_slice_to_array(&decoded).map_err(serde::de::Error::custom)
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ anyhow = { version = "1.0.95", features = ["std"] }
|
||||
clap = { version ="4.5", features = ["derive", "wrap_help"]}
|
||||
log = { version = "0.4.25", features = ["std", "release_max_level_debug"] }
|
||||
serde_yaml = "0.9"
|
||||
zerocopy = "0.8"
|
||||
|
||||
pv = { path = "../pv" , package = "s390_pv" }
|
||||
utils = { path = "../utils"}
|
||||
|
||||
@@ -194,6 +194,28 @@ Optional. No user\-data by default.
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
\-\-policy <FILE>
|
||||
.RS 4
|
||||
Links an add\-secret request to a policy file.
|
||||
This option embeds a reference to a policy in the add\-secret request user data field. The
|
||||
reference includes the relative file path and the SHA-512 hash of the
|
||||
policy file, enabling verification of the policy file’s integrity.
|
||||
This option conflicts with \fB\-\-user\-data\fR, because both options use the
|
||||
same user data field in the add\-secret request structure.
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
\-\-toc\-policy <FILE>
|
||||
.RS 4
|
||||
Adds the AES\-GCM authentication tag to a table-of-contents (TOC) policy file.
|
||||
This option appends the AES\-GCM authentication tag to the specified TOC policy
|
||||
file. This allows the TOC policy to maintain a list of all add\-secret request MAC tags for
|
||||
completeness verification during boot. During verification, the TOC checks the
|
||||
AES\-GCM tags against this list to ensure that all expected add\-secret request are present and
|
||||
unmodified.
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
\-\-user\-sign\-key <FILE>
|
||||
.RS 4
|
||||
Use the content of FILE as user signing key. Adds a signature calculated from
|
||||
|
||||
@@ -130,6 +130,30 @@ pub struct CreateSecretOpt {
|
||||
#[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath,)]
|
||||
pub user_data: Option<String>,
|
||||
|
||||
/// Links an Add‑Secret-Request (ASR) to a policy file.
|
||||
///
|
||||
/// This option embeds a PolicyReference in the ASR user data field. The PolicyReference
|
||||
/// includes the relative file path and the SHA‑512 hash of the policy file, allowing the
|
||||
/// policy’s integrity to be verified.
|
||||
///
|
||||
/// This option conflicts with --user-data, because both options use the same user data field in
|
||||
/// the ASR structure.
|
||||
#[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath, conflicts_with("user_data"))]
|
||||
pub policy: Option<String>,
|
||||
|
||||
/// Adds the AES‑GCM authentication tag to a TOC policy file.
|
||||
///
|
||||
/// This option appends the AES‑GCM authentication tag to the specified TOC policy file. This
|
||||
/// allows the TOC policy to maintain a list of all ASR MAC tags for completeness verification
|
||||
/// during boot. During verification, the TOC checks the MAC tags against this list to ensure
|
||||
/// that all expected ASRs are present and unmodified.
|
||||
#[arg(
|
||||
long = "toc-policy",
|
||||
value_name = "FILE",
|
||||
value_hint = ValueHint::FilePath,
|
||||
)]
|
||||
pub tocpolicy: Option<String>,
|
||||
|
||||
/// Use the content of FILE as user signing key.
|
||||
///
|
||||
/// Adds a signature calculated from the key in FILE to the add-secret request. The
|
||||
@@ -265,12 +289,10 @@ impl Display for RetrieveableSecretInpKind {
|
||||
}
|
||||
}
|
||||
|
||||
// all members s390x only
|
||||
#[derive(Args, Debug)]
|
||||
pub struct AddSecretOpt {
|
||||
/// Specify the request to be sent.
|
||||
#[arg(value_name = "FILE", value_hint = ValueHint::FilePath,)]
|
||||
#[cfg(target_arch = "s390x")]
|
||||
pub input: String,
|
||||
|
||||
/// Force the addition of add-secret requests.
|
||||
@@ -282,7 +304,6 @@ pub struct AddSecretOpt {
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug, Default)]
|
||||
#[cfg(target_arch = "s390x")]
|
||||
pub enum ListSecretOutputType {
|
||||
/// Human-focused, non-parsable output format
|
||||
#[default]
|
||||
@@ -293,17 +314,14 @@ pub enum ListSecretOutputType {
|
||||
Bin,
|
||||
}
|
||||
|
||||
// all members s390x only
|
||||
#[derive(Args, Debug)]
|
||||
pub struct ListSecretOpt {
|
||||
/// Store the result in FILE
|
||||
#[arg(value_name = "FILE", default_value = STDOUT, value_hint = ValueHint::FilePath,)]
|
||||
#[cfg(target_arch = "s390x")]
|
||||
pub output: String,
|
||||
|
||||
/// Define the output format of the list.
|
||||
#[arg(long, value_enum, default_value_t)]
|
||||
#[cfg(target_arch = "s390x")]
|
||||
pub format: ListSecretOutputType,
|
||||
}
|
||||
|
||||
@@ -331,7 +349,6 @@ pub struct VerifyOpt {
|
||||
pub output: String,
|
||||
}
|
||||
|
||||
// all members s390x only
|
||||
#[derive(Args, Debug)]
|
||||
pub struct RetrSecretOptions {
|
||||
/// Specify the secret ID to be retrieved.
|
||||
@@ -341,22 +358,18 @@ pub struct RetrSecretOptions {
|
||||
/// handle encodes in hexadecimal. Leading zeros are required. If there are multiple secrets in
|
||||
/// the store with the same Id there are no guarantees on which specific secret is retrieved.
|
||||
/// Use --inform=idx to make sure a specific secret is retrieved.
|
||||
#[cfg(target_arch = "s390x")]
|
||||
#[arg(value_name = "ID", value_hint = ValueHint::FilePath)]
|
||||
pub input: String,
|
||||
|
||||
/// Specify the output path to place the secret value
|
||||
#[cfg(target_arch = "s390x")]
|
||||
#[arg(short, long, value_name = "FILE", default_value = STDOUT, value_hint = ValueHint::FilePath)]
|
||||
pub output: String,
|
||||
|
||||
/// Define input type for the Secret ID
|
||||
#[cfg(target_arch = "s390x")]
|
||||
#[arg(long, value_enum, default_value_t)]
|
||||
pub inform: RetrInpFmt,
|
||||
|
||||
/// Define the output format for the retrieved secret
|
||||
#[cfg(target_arch = "s390x")]
|
||||
#[arg(long, value_enum, default_value_t)]
|
||||
pub outform: RetrOutFmt,
|
||||
}
|
||||
@@ -478,10 +491,7 @@ mod test {
|
||||
vec!["pvsecret", "lock"],
|
||||
vec!["pvsecret", "version"],
|
||||
vec!["pvsecret", "list"],
|
||||
#[cfg(target_arch = "s390x")]
|
||||
vec!["pvsecret", "add", "abc"],
|
||||
#[cfg(not(target_arch = "s390x"))]
|
||||
vec!["pvsecret", "add"],
|
||||
vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--no-verify", "meta"],
|
||||
vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--no-verify", "association", "name" ],
|
||||
vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--no-verify", "update-cck", "--secret", "abc"],
|
||||
@@ -492,11 +502,8 @@ mod test {
|
||||
"--root-ca", "tttt", "--cck", "cck", "--cuid-hex", "0x11223344556677889900aabbccddeeff", "--pcf", "0x123", "association", "name", "--stdout",
|
||||
"--output-secret", "secret"],
|
||||
vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--no-verify", "association", "name", "--output-secret", "secret"],
|
||||
#[cfg(target_arch = "s390x")]
|
||||
vec!["pvsecret", "list", "--format", "human"],
|
||||
#[cfg(target_arch = "s390x")]
|
||||
vec!["pvsecret", "list", "--format", "yaml"],
|
||||
#[cfg(target_arch = "s390x")]
|
||||
vec!["pvsecret", "list", "--format", "bin"],
|
||||
];
|
||||
// Test for the minimal amount of flags to yield an invalid combination
|
||||
|
||||
@@ -2,24 +2,30 @@
|
||||
//
|
||||
// Copyright IBM Corp. 2023, 2024
|
||||
|
||||
use std::path::Path;
|
||||
use std::{
|
||||
fs::OpenOptions,
|
||||
io::{Read, Write},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, bail, Context, Error, Result};
|
||||
use log::{debug, info, trace, warn};
|
||||
use pv::request::openssl;
|
||||
use pv::{
|
||||
misc::{
|
||||
decode_hex, open_file, pv_guest_bit_set, read_exact_file, read_file, try_parse_u128,
|
||||
try_parse_u64, write,
|
||||
decode_hex, encode_hex, open_file, pv_guest_bit_set, read_exact_file, read_file,
|
||||
try_parse_u128, try_parse_u64, write,
|
||||
},
|
||||
request::{
|
||||
openssl::pkey::{PKey, Private},
|
||||
BootHdrTags, ReqEncrCtx, Request, SymKeyType,
|
||||
BootHdrTags, PolicyReference, ReqEncrCtx, Request, SymKeyType,
|
||||
},
|
||||
secret::{AddSecretFlags, AddSecretRequest, AddSecretVersion, ExtSecret, GuestSecret},
|
||||
uv::ConfigUid,
|
||||
};
|
||||
use serde_yaml::Value;
|
||||
use utils::get_writer_from_cli_file_arg;
|
||||
use zerocopy::IntoBytes;
|
||||
|
||||
use crate::cli::{AddSecretType, CreateSecretFlags, CreateSecretOpt, RetrieveableSecretInpKind};
|
||||
|
||||
@@ -33,6 +39,38 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Computes the SHA-256 hash of data from a reader.
|
||||
///
|
||||
/// Reads data from the provided reader in 4096-byte chunks and computes
|
||||
/// the SHA-256 hash of the entire content.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `r` - A reader providing the data to hash
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns a `Vec<u8>` containing the 32-byte SHA-256 hash, or an error
|
||||
/// if reading fails.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if reading from the reader fails.
|
||||
pub fn sha256_hash<R: Read>(mut r: R) -> Result<Vec<u8>, pv::PvCoreError> {
|
||||
let mut hasher = openssl::Sha256::new();
|
||||
let mut buf: [u8; 4096] = [0; 4096];
|
||||
|
||||
loop {
|
||||
let read = r.read(&mut buf)?;
|
||||
if read == 0 {
|
||||
break;
|
||||
}
|
||||
hasher.update(&buf[..read]);
|
||||
}
|
||||
|
||||
Ok(hasher.finish().to_vec())
|
||||
}
|
||||
|
||||
fn retrievable(name: &str, secret: &str, kind: &RetrieveableSecretInpKind) -> Result<GuestSecret> {
|
||||
let secret_data = read_file(secret, &format!("retrievable {kind}"))?.into();
|
||||
|
||||
@@ -76,6 +114,14 @@ pub fn create(opt: &CreateSecretOpt) -> Result<()> {
|
||||
let rq =
|
||||
ReqEncrCtx::random(SymKeyType::Aes256Gcm).context("Failed to generate random input")?;
|
||||
let ser_asrbc = asrcb.encrypt(&rq)?;
|
||||
|
||||
if let Some(path) = &opt.tocpolicy {
|
||||
let mac_tag = encode_hex(&ser_asrbc[(ser_asrbc.len() - 16)..]);
|
||||
let mut file = OpenOptions::new().create(true).append(true).open(path)?;
|
||||
|
||||
writeln!(file, "{mac_tag}")?;
|
||||
}
|
||||
|
||||
warn!("Successfully generated the request");
|
||||
write_out(&opt.output, ser_asrbc, "add-secret request")?;
|
||||
info!("Successfully wrote the request to '{}'", &opt.output);
|
||||
@@ -157,6 +203,17 @@ fn build_asrcb(opt: &CreateSecretOpt) -> Result<AddSecretRequest> {
|
||||
warn!("Added empty user-data file.");
|
||||
}
|
||||
|
||||
let supplied_ref = opt
|
||||
.policy
|
||||
.as_ref()
|
||||
.map(|s| -> Result<PolicyReference> {
|
||||
let p = Path::new(s);
|
||||
let reference = PolicyReference::new(p, sha256_hash)?;
|
||||
println!("{}", encode_hex(reference.hash));
|
||||
Ok(reference)
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let user_key = opt
|
||||
.user_sign_key
|
||||
.as_ref()
|
||||
@@ -169,6 +226,8 @@ fn build_asrcb(opt: &CreateSecretOpt) -> Result<AddSecretRequest> {
|
||||
|
||||
if user_data.is_some() || user_key.is_some() {
|
||||
asrcb.set_user_data(user_data.unwrap_or_default(), user_key)?;
|
||||
} else if let Some(ref_val) = supplied_ref {
|
||||
asrcb.set_user_data(ref_val.as_bytes(), None)?;
|
||||
}
|
||||
Ok(asrcb)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ use utils::{print_cli_error, print_error, print_version, PvLogger};
|
||||
|
||||
static LOGGER: PvLogger = PvLogger;
|
||||
static EXIT_LOGGER: u8 = 3;
|
||||
const FEATURES: &[&[&str]] = &[cmd::CMD_FN, cmd::UV_CMD_FN];
|
||||
const FEATURES: &[&[&str]] = &[cmd::CMD_FN, cmd::UV_CMD_FN, &["+ebc"]];
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let cli: CliOptions = match CliOptions::try_parse() {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
include ../common.mak
|
||||
|
||||
SCRIPTS = dbginfo.sh zfcpdbf zipl-switch-to-blscfg sclpdbf
|
||||
SCRIPTS = dbginfo.sh zfcpdbf zipl-switch-to-blscfg sclpdbf pvics
|
||||
# Helper scripts controlled by corresponding systemd services
|
||||
SD_HELPER_SCRIPTS = cpictl dumpconf
|
||||
MAN_PAGES = dbginfo.sh.8 zfcpdbf.8 zipl-switch-to-blscfg.8
|
||||
MAN_PAGES = dbginfo.sh.8 zfcpdbf.8 zipl-switch-to-blscfg.8 pvics.8
|
||||
FILE_MAN_PAGES = pvics.yaml.5
|
||||
|
||||
all:
|
||||
|
||||
@@ -33,6 +34,12 @@ install:
|
||||
$(DESTDIR)$(MANDIR)/man8; \
|
||||
done
|
||||
|
||||
@for i in $(FILE_MAN_PAGES); \
|
||||
do \
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 $$i \
|
||||
$(DESTDIR)$(MANDIR)/man5; \
|
||||
done
|
||||
|
||||
clean:
|
||||
|
||||
.zfcpdbf.ct: zfcpdbf
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# dbginfo.sh - Tool to collect runtime, configuration, and trace information
|
||||
#
|
||||
# Copyright IBM Corp. 2002, 2025
|
||||
# Copyright IBM Corp. 2002, 2026
|
||||
#
|
||||
# s390-tools is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -14,7 +14,7 @@ export LC_ALL
|
||||
|
||||
########################################
|
||||
# Global used variables
|
||||
readonly SCRIPTNAME="${0##*/}" # general name of this script
|
||||
readonly SCRIPTNAME="${0##*/}" # current name of this script
|
||||
readonly FULLPATHSCRIPT="$(readlink -f "${0}")"
|
||||
#
|
||||
readonly DATETIME="$(date +%Y-%m-%d-%H-%M-%S 2>/dev/null)"
|
||||
@@ -84,7 +84,7 @@ paramWORKDIR_BASE="/tmp" # initial default path
|
||||
print_version() {
|
||||
cat <<EOF
|
||||
${SCRIPTNAME}: Debug information script version %S390_TOOLS_VERSION%
|
||||
Copyright IBM Corp. 2002, 2025
|
||||
Copyright IBM Corp. 2002, 2026
|
||||
EOF
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ EOF
|
||||
}
|
||||
|
||||
########################################
|
||||
# check for oversize logfiles and missing rotation
|
||||
# check for oversized logfiles and missing rotation
|
||||
logfile_checker() {
|
||||
local counter
|
||||
local logfile
|
||||
@@ -485,6 +485,7 @@ CMDS="uname -a\
|
||||
:lscpu -ye\
|
||||
:lscpumf -i\
|
||||
:lsmem\
|
||||
:zmemtopo -t\
|
||||
:lsmod\
|
||||
:lsshut\
|
||||
:lsstp\
|
||||
|
||||
1579
scripts/pvics
Executable file
1579
scripts/pvics
Executable file
File diff suppressed because it is too large
Load Diff
340
scripts/pvics.8
Normal file
340
scripts/pvics.8
Normal file
@@ -0,0 +1,340 @@
|
||||
.\" Copyright IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
.TH PVICS 8 "April 2026" "s390-tools"
|
||||
|
||||
.SH NAME
|
||||
pvics \- Convert qcow2 KVM guest images to EBC-ready SEL images
|
||||
|
||||
.SH SYNOPSIS
|
||||
.B pvics
|
||||
.I ACTION
|
||||
.RB [ \-h | \-\-help ]
|
||||
.RB [ \-c | \-\-config
|
||||
.IR CONFIG_FILE ]
|
||||
.RB [ \-i | \-\-image
|
||||
.IR BASE_IMAGE ]
|
||||
|
||||
.SH DESCRIPTION
|
||||
Use the \fBpvics\fR tool to convert existing QEMU Copy/-On/-Write version 2(qcow2) KVM
|
||||
guest images into images that are ready for Early Boot Customization (EBC) and Secure Execution for Linux (SEL).
|
||||
The tool encrypts the root file system and prepares all resources required for EBC.
|
||||
|
||||
The tool operates on a copy of the original base image and performs operations
|
||||
in three main phases:
|
||||
.IP \(bu 2
|
||||
Retrieving information about a given base image
|
||||
.IP \(bu 2
|
||||
Converting a base image according to a configuration
|
||||
.IP \(bu 2
|
||||
Encrypting the root file system and preparing EBC resources
|
||||
|
||||
All operations preserve the original base image. Logs are written to a
|
||||
temporary file, with the filename logged as the first message during runtime.
|
||||
Temporary artifacts are automatically cleaned up on completion or failure.
|
||||
|
||||
.SH ACTIONS
|
||||
.TP
|
||||
.B list
|
||||
Retrieve information about a given base image. This action displays available
|
||||
boot loader entries from \fB/boot/loader/entries\fR, along with kernel and
|
||||
initramfs hashes and the kernel command line. This is useful for:
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
Determining valid values for the \fBboot-loader-entry\fR configuration option
|
||||
.IP \(bu 2
|
||||
Comparing components between base and converted images
|
||||
.IP \(bu 2
|
||||
Verifying image contents before conversion
|
||||
.RE
|
||||
|
||||
.TP
|
||||
.B convert
|
||||
Convert a base image according to the configuration file. This action:
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
Fetches kernel, initramfs, and kernel command line from the specified boot
|
||||
loader entry
|
||||
.IP \(bu 2
|
||||
Builds a SEL image using \fBpvimg\fR(1)
|
||||
.IP \(bu 2
|
||||
Updates \fB/boot/bootmap\fR to boot into the SEL image
|
||||
.RE
|
||||
.IP
|
||||
The resulting image will be a SEL guest image. It requires encryption to be
|
||||
fully EBC-ready (unless \fBno-ebc\fR is enabled in the configuration).
|
||||
|
||||
.TP
|
||||
.B encrypt
|
||||
Encrypt the root file system and prepare EBC resources. This action:
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
Generates secure defaults (CCK, extension secret, LUKS keys) if not provided
|
||||
.IP \(bu 2
|
||||
Encrypts the root file system using LUKS with PAES
|
||||
.IP \(bu 2
|
||||
Populates \fB/boot/sics/\fR with add/-secret requests
|
||||
.IP \(bu 2
|
||||
Creates \fBtoc.pol\fR and \fBtoc.asr\fR for integrity protection
|
||||
.RE
|
||||
.IP
|
||||
This action requires EBC. The guest can boot only by using PAES to open the root file system.
|
||||
|
||||
.TP
|
||||
.B full
|
||||
Perform both \fBconvert\fR and \fBencrypt\fR actions in sequence. This is
|
||||
equivalent to running \fBconvert\fR followed by \fBencrypt\fR, but in a single
|
||||
invocation.
|
||||
|
||||
.SH ARGUMENTS
|
||||
.TP
|
||||
.I ACTION
|
||||
The action to perform: \fBlist\fR, \fBconvert\fR, \fBencrypt\fR, or \fBfull\fR.
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BR \-h ", " \-\-help
|
||||
Display help message and exit.
|
||||
|
||||
.TP
|
||||
.BR \-c ", " \-\-config " " \fICONFIG_FILE\fR
|
||||
Path to the YAML configuration file. See \fBpvics.yaml\fR(5) for the
|
||||
configuration file format and options.
|
||||
.br
|
||||
Required for \fBconvert\fR, \fBencrypt\fR, and \fBfull\fR actions.
|
||||
.br
|
||||
Not required for the \fBlist\fR action.
|
||||
|
||||
.TP
|
||||
.BR \-i ", " \-\-image " " \fIBASE_IMAGE\fR
|
||||
Path to the base qcow2 image file to process. Required for all actions.
|
||||
|
||||
.SH CONVERSION PROCESS
|
||||
The conversion process consists of three phases:
|
||||
|
||||
.SS Component Fetching
|
||||
The tool retrieves the kernel, initramfs, and kernel command line from the
|
||||
boot loader entry specified in the configuration file. The components are
|
||||
validated to ensure they meet SEL EBC requirements.
|
||||
|
||||
The tool prepends \fBrd.sel-ebc\fR to the kernel command line to trigger the
|
||||
SEL EBC dracut module (unless \fBno-ebc\fR is enabled) and appends any
|
||||
user-provided kernel parameters from the configuration.
|
||||
|
||||
.SS SEL Image Build
|
||||
The SEL image is built using \fBpvimg\fR(1) with the fetched components.
|
||||
Additional options may be specified in the configuration file via
|
||||
\fB.convert.pvimg-create-options\fR.
|
||||
|
||||
.SS Bootmap Update
|
||||
The tool updates the bootmap to boot into the new SEL image:
|
||||
.IP \(bu 2
|
||||
Existing boot loader entries in \fB/boot/loader/entries/*.conf\fR are renamed
|
||||
to \fB*.conf.old\fR
|
||||
.IP \(bu 2
|
||||
A new entry \fBsel-ebc.conf\fR is created that points to \fB/boot/sel-ebc.img\fR
|
||||
.IP \(bu 2
|
||||
\fBzipl\fR(8) is run to update the bootmap.
|
||||
|
||||
The guest is temporarily started to update the bootmap using libvirt on z/Architecture
|
||||
or qemu on non-z/Architecture architectures. Failures are logged to
|
||||
\fB/var/log/sel-ebc-zipl.log\fR.
|
||||
|
||||
.SH ENCRYPTION PROCESS
|
||||
The encryption process consists of three phases:
|
||||
|
||||
.SS Secure Default Generation
|
||||
If not provided in the configuration, the following are generated from
|
||||
\fB/dev/random\fR:
|
||||
.IP \(bu 2
|
||||
Customer communication key (CCK)
|
||||
.IP \(bu 2
|
||||
Root file system LUKS encryption key
|
||||
.IP \(bu 2
|
||||
Extension secret for add/-secret requests
|
||||
.IP \(bu 2
|
||||
LUKS passphrase
|
||||
|
||||
.SS Root File System Encryption
|
||||
The root file system is encrypted using the following steps:
|
||||
.IP 1. 3
|
||||
Resize the qcow2 image to accommodate the LUKS header
|
||||
.IP 2. 3
|
||||
Resize the root partition (but not the file system) to fit the LUKS header
|
||||
.IP 3. 3
|
||||
Encrypt the root file system using LUKS
|
||||
.IP 4. 3
|
||||
Reformat the LUKS header from AES to PAES (Protected AES)
|
||||
|
||||
.SS AES to PAES Conversion
|
||||
The LUKS header is converted from standard AES encryption to protected AES (PAES).
|
||||
With PAES, encryption secrets are stored in the ultravisor secret store rather than directly in the LUKS header.
|
||||
|
||||
.SS SICS Population
|
||||
The SEL Image Customization Source directory (\fB/boot/sics\fR) is populated
|
||||
with the following secrets as add/-secret requests:
|
||||
.IP \(bu 2
|
||||
Customer communication key (CCK)
|
||||
.IP \(bu 2
|
||||
LUKS encryption key
|
||||
.IP \(bu 2
|
||||
LUKS passphrase
|
||||
.IP \(bu 2
|
||||
Any additional add/-secret requests specified in the configuration
|
||||
|
||||
All automatically built add/-secret requests are added to \fBtoc.pol\fR, which verifies the
|
||||
completeness of \fB/boot/sics\fR. User-supplied add/-secret requests from the configuration
|
||||
are also added. Finally, \fBtoc.asr\fR is built as a meta secret that links
|
||||
to \fBtoc.pol\fR for integrity protection.
|
||||
|
||||
.SH BUILDING SEL IMAGES WITHOUT EBC
|
||||
While the primary purpose of \fBpvics\fR is to create EBC-ready SEL images with
|
||||
encrypted root file systems, it can also build SEL images without EBC
|
||||
functionality using the \fBno-ebc\fR configuration option.
|
||||
|
||||
When \fBno-ebc: true\fR is set in the configuration:
|
||||
.IP \(bu 2
|
||||
The \fBconvert\fR action builds a SEL image without EBC-specific flags
|
||||
.IP \(bu 2
|
||||
The \fBencrypt\fR action is completely skipped
|
||||
.IP \(bu 2
|
||||
The \fBfull\fR action becomes equivalent to \fBconvert\fR only
|
||||
.IP \(bu 2
|
||||
The \fBrd.sel-ebc\fR kernel parameter is not added
|
||||
.IP \(bu 2
|
||||
\fB--enable-cck-update\fR and \fB--disable-image-encryption\fR flags are not
|
||||
passed to \fBpvimg\fR
|
||||
|
||||
This mode is useful for:
|
||||
.IP \(bu 2
|
||||
Single-party image creation where the same entity performs conversion,
|
||||
customization, and encryption
|
||||
.IP \(bu 2
|
||||
Testing SEL guest functionality without EBC complexity
|
||||
.IP \(bu 2
|
||||
Development and debugging scenarios
|
||||
.IP \(bu 2
|
||||
Simplified SEL image generation workflows
|
||||
|
||||
.SH OUTPUT FILES
|
||||
All output files are written to the directory specified by the \fBout\fR
|
||||
configuration option:
|
||||
|
||||
.TP
|
||||
\fB<out>/image.qcow2\fR
|
||||
The converted SEL image (after \fBconvert\fR or \fBfull\fR action)
|
||||
|
||||
.TP
|
||||
\fB<out>/cck.key\fR
|
||||
Customer communication key (generated if not provided)
|
||||
|
||||
.TP
|
||||
\fB<out>/extension.secret\fR
|
||||
Extension secret for add/-secret requests (generated if not provided)
|
||||
|
||||
.TP
|
||||
\fB<out>/rfs.key\fR
|
||||
Root file system LUKS encryption key (generated if not provided)
|
||||
|
||||
.TP
|
||||
\fB<out>/passphrase\fR
|
||||
LUKS passphrase (generated if not provided)
|
||||
|
||||
.TP
|
||||
\fB<out>/*.asr\fR
|
||||
Generated add-secret request files
|
||||
|
||||
.SH TEMPORARY FILES
|
||||
.TP
|
||||
\fB/tmp/tmp.*\fR
|
||||
Log file (filename logged at startup)
|
||||
|
||||
.SH EXIT STATUS
|
||||
.TP
|
||||
.B 0
|
||||
Success
|
||||
.TP
|
||||
.B 1
|
||||
General error (invalid arguments, missing files, operation failure)
|
||||
|
||||
.SH EXAMPLES
|
||||
.SS List Boot Loader Entries
|
||||
.nf
|
||||
pvics list \-\-image /path/to/base-image.qcow2
|
||||
.fi
|
||||
|
||||
.SS Convert Image Only
|
||||
.nf
|
||||
pvics convert \-\-config /path/to/config.yaml \-\-image /path/to/base-image.qcow2
|
||||
.fi
|
||||
|
||||
.SS Encrypt Image Only
|
||||
.nf
|
||||
pvics encrypt \-\-config /path/to/config.yaml \-\-image /path/to/converted-image.qcow2
|
||||
.fi
|
||||
|
||||
.SS Full Conversion and Encryption
|
||||
.nf
|
||||
pvics full \-\-config /path/to/config.yaml \-\-image /path/to/base-image.qcow2
|
||||
.fi
|
||||
|
||||
.SS Build SEL Image Without EBC
|
||||
.nf
|
||||
# config.yaml contains: no-ebc: true
|
||||
pvics convert \-\-config /path/to/config.yaml \-\-image /path/to/base-image.qcow2
|
||||
.fi
|
||||
|
||||
.SH FILES
|
||||
.TP
|
||||
\fB/boot/loader/entries/*.conf\fR
|
||||
Boot loader entry files in the base image
|
||||
|
||||
.TP
|
||||
\fB/boot/sel-ebc.img\fR
|
||||
The resulting SEL image file in the guest
|
||||
|
||||
.TP
|
||||
\fB/boot/sics/\fR
|
||||
SEL image-customization source directory that contains EBC resources
|
||||
|
||||
.TP
|
||||
\fB/boot/sics/toc.pol\fR
|
||||
Table-of-contents policy file that lists all add/-secret requests
|
||||
|
||||
.TP
|
||||
\fB/boot/sics/toc.asr\fR
|
||||
Meta secret for integrity protection of toc.pol
|
||||
|
||||
.TP
|
||||
\fB/var/log/sel-ebc-zipl.log\fR
|
||||
Log file for zipl bootmap update operations
|
||||
|
||||
.SH NOTES
|
||||
.IP \(bu 2
|
||||
All operations are performed on a copy of the original base image.
|
||||
.IP \(bu 2
|
||||
The tool requires root privileges for file-system operations.
|
||||
.IP \(bu 2
|
||||
The LUKS device is named \fBcryptroot\fR.
|
||||
.IP \(bu 2
|
||||
The temporary working directory is created at \fB/opt/sel-<UUID>\fR.
|
||||
.IP \(bu 2
|
||||
The \fBno-verify\fR option should not be used in production environments.
|
||||
.IP \(bu 2
|
||||
Generated secrets are written to the output directory and should be secured
|
||||
appropriately.
|
||||
|
||||
.SH SEE ALSO
|
||||
.BR pvics.yaml (5),
|
||||
.BR pvsecret (1),
|
||||
.BR pvimg (1),
|
||||
.BR pvebc (8),
|
||||
.BR zipl (8),
|
||||
.BR cryptsetup (8),
|
||||
.BR lsinitrd (1)
|
||||
.PP
|
||||
Linux on IBM Z and IBM LinuxONE: Secure Execution for Linux documentation
|
||||
|
||||
.SH AUTHOR
|
||||
IBM Corporation
|
||||
106
scripts/pvics.yaml
Normal file
106
scripts/pvics.yaml
Normal file
@@ -0,0 +1,106 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Copyright IBM Corp.
|
||||
|
||||
|
||||
# OPTIONAL (default: false)
|
||||
no-verify: false
|
||||
# This option controls whether the --no-verify flag is used with the pv commands.
|
||||
|
||||
# OPTIONAL (default: false)
|
||||
no-ebc: false
|
||||
# This option controls whether to trigger the SEL EBC related systemd units.
|
||||
# It can be used to create a SEL guest image from an existing qcow2 image without utilizing SEL EBC.
|
||||
# Setting this to true will prevent the encrypt action from running.
|
||||
# In this case, the full action equals the convert action.
|
||||
# Setting this to true will prevent the default pvimg options (see below) from being added and instead
|
||||
# only add the user provided ones in .convert.pvimg-options.
|
||||
|
||||
# REQUIRED
|
||||
out: data/output
|
||||
# This specifies the local directory used for output.
|
||||
|
||||
# REQUIRED
|
||||
hkds:
|
||||
- data/*.hkd
|
||||
# These are the paths to all relevant Host-Key-Documents.
|
||||
# The paths may contain wildcard patterns.
|
||||
|
||||
# REQUIRED if .no-verify is false
|
||||
# UNUSED if .no-verify is true
|
||||
# This section contains all relevant files for verification of the certificate chain.
|
||||
# See man pvsecret for more detailed information about any of the keys in this section.
|
||||
certificate-chain:
|
||||
# The paths may contain wildcard patterns.
|
||||
certs:
|
||||
- data/*.cert
|
||||
|
||||
# The paths may contain wildcard patterns.
|
||||
crls:
|
||||
- data/*.crl
|
||||
|
||||
# This is the pvsecret create offline option.
|
||||
offline: true
|
||||
|
||||
# This is the root CA of the given certificates.
|
||||
root-ca: data/root.ca
|
||||
|
||||
# This section contains relevant information for the first phase: conversion.
|
||||
convert:
|
||||
# REQUIRED
|
||||
boot-loader-entry: boot_loader_entry_title
|
||||
# This is the boot loader entry name to be used for kernel, initramfs and kernel parameter.
|
||||
# You can list available boot loader entries with the list action.
|
||||
|
||||
# OPTIONAL
|
||||
sel-kernel-parameter: swiotlb=524288
|
||||
# These are additional kernel parameters for the resulting SEL image.
|
||||
# The parameter rd.sel-ebc will always be added.
|
||||
|
||||
# OPTIONAL (default: --enable-pckmo-hmac)
|
||||
pvimg-create-options: --enable-pckmo-hmac
|
||||
# These are additional pvimg options for the resulting SEL image.
|
||||
# The options --disable-image-encryption and --enable-update-cck are always used except when .no-ebc is true.
|
||||
|
||||
# This section contains relevant information for the second phase: encryption.
|
||||
encrypt:
|
||||
# OPTIONAL
|
||||
cck: data/cck.key
|
||||
# This is the local path to the CCK.
|
||||
# If none is supplied, one will be generated and written to .out/cck.key.
|
||||
|
||||
# OPTIONAL
|
||||
extension-secret: data/extension.secret
|
||||
# This is the local path to the extension secret for pvsecret create.
|
||||
# This is REQUIRED if .encrypt.add-secret-requests is used.
|
||||
# In this case, the extension secret must be the one from the supplied ASRs.
|
||||
# If none is supplied, one will be generated and written to .out/extension.secret.
|
||||
|
||||
# OPTIONAL
|
||||
luks-key: data/rfs.key
|
||||
# This is the local path to the LUKS encryption key.
|
||||
# If none is supplied, one will be generated and written to .out/rfs.key.
|
||||
|
||||
# OPTIONAL
|
||||
luks-passphrase: data/passphrase
|
||||
# This is the local path to a file containing the LUKS passphrase.
|
||||
# If none is supplied, one will be generated and written to .out/passphrase.
|
||||
|
||||
# OPTIONAL (default: rfs-luks-key)
|
||||
luks-key-asr-name: aes-xts-segment-key
|
||||
# This is the name of the ASR containing the LUKS encryption key.
|
||||
|
||||
# OPTIONAL (default: depending on .encrypt.luks-key)
|
||||
luks-key-size: 512
|
||||
# This is the key size of the LUKS encryption key (256 or 512).
|
||||
# If .encrypt.luks-key is supplied, this will default to a size depending on the key.
|
||||
# Otherwise, this will default to 512 and generate a matching key.
|
||||
# If this is supplied but no key is supplied, a key of the requested size will be generated.
|
||||
|
||||
# OPTIONAL
|
||||
add-secret-requests:
|
||||
- data/*.asr
|
||||
# These are additional secrets to be added during boot.
|
||||
# Using this will make .encrypt.extension-secret a required argument.
|
||||
# The supplied extension secret and the extension secret of supplied ASRs must be the same.
|
||||
# The paths may contain wildcard patterns.
|
||||
321
scripts/pvics.yaml.5
Normal file
321
scripts/pvics.yaml.5
Normal file
@@ -0,0 +1,321 @@
|
||||
.\" Copyright IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
.TH PVICS.YAML 5 "April 2026" "s390-tools"
|
||||
|
||||
.SH NAME
|
||||
pvics.yaml \- Configuration file for pvics SEL EBC image conversion tool
|
||||
|
||||
.SH DESCRIPTION
|
||||
The \fBpvics.yaml\fR file is a YAML-formatted configuration file used by the
|
||||
\fBpvics\fR tool to control the conversion of existing qcow2 KVM guest images
|
||||
to EBC-ready Secure Execution for Linux (SEL) images. The configuration file
|
||||
specifies parameters for image conversion, root file system encryption, and
|
||||
early boot customization (EBC) resource preparation.
|
||||
|
||||
See \fBpvics\fR(8) for detailed information about the tool's actions and the
|
||||
conversion process.
|
||||
|
||||
.SH FILE FORMAT
|
||||
The configuration file uses YAML syntax with the following top-level sections:
|
||||
.TP
|
||||
\fBGlobal Options\fR
|
||||
General settings that apply to all actions
|
||||
.TP
|
||||
\fBCertificate Chain\fR
|
||||
Certificate verification configuration
|
||||
.TP
|
||||
\fBConversion Options\fR
|
||||
Settings specific to the \fBconvert\fR action
|
||||
.TP
|
||||
\fBEncryption Options\fR
|
||||
Settings specific to the \fBencrypt\fR action
|
||||
|
||||
.SH GLOBAL OPTIONS
|
||||
.TP
|
||||
\fBno-verify:\fR \fIboolean\fR
|
||||
Disables certificate chain verification for pv commands when set to true.
|
||||
Default: \fBfalse\fR.
|
||||
.br
|
||||
\fBWARNING:\fR Do not disable certificate chain verification in production environments.
|
||||
|
||||
.TP
|
||||
\fBno-ebc:\fR \fIboolean\fR
|
||||
Controls whether EBC functionality is used. When set to
|
||||
\fBtrue\fR, a SEL guest image is created without using EBC.
|
||||
Default: \fBfalse\fR.
|
||||
.br
|
||||
When enabled:
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
The \fBencrypt\fR action is skipped
|
||||
.IP \(bu 2
|
||||
The \fBfull\fR action behaves the same as the \fBconvert\fR action
|
||||
.IP \(bu 2
|
||||
Default \fBpvimg\fR options are not added; only user-provided options specified in
|
||||
\fB.convert.pvimg-create-options\fR are used
|
||||
.IP \(bu 2
|
||||
The \fBrd.sel-ebc\fR kernel parameter is not added
|
||||
.RE
|
||||
|
||||
.TP
|
||||
\fBout:\fR \fIpath\fR
|
||||
\fB(REQUIRED)\fR Local directory path used for output files. All generated
|
||||
files (converted images, keys, ASRs) will be written to this directory.
|
||||
|
||||
.TP
|
||||
\fBhkds:\fR \fIlist\fR
|
||||
\fB(REQUIRED)\fR List of paths to host-key-documents (HKDs). Paths may contain
|
||||
wildcard patterns, for example \fBdata/*.hkd\fR.
|
||||
.br
|
||||
Example:
|
||||
.RS
|
||||
.nf
|
||||
hkds:
|
||||
- data/*.hkd
|
||||
- /path/to/specific.hkd
|
||||
.fi
|
||||
.RE
|
||||
|
||||
.SH CERTIFICATE CHAIN
|
||||
The \fBcertificate-chain\fR section contains all relevant files for verification
|
||||
of the certificate chain. This section is \fBREQUIRED\fR if \fBno-verify\fR is
|
||||
\fBfalse\fR, and \fBUNUSED\fR if \fBno-verify\fR is \fBtrue\fR.
|
||||
|
||||
See \fBpvsecret\fR(1) for more detailed information about certificate chain
|
||||
verification.
|
||||
|
||||
.TP
|
||||
\fBcertificate-chain.certs:\fR \fIlist\fR
|
||||
List of paths to certificate files. Paths may contain wildcard patterns.
|
||||
.br
|
||||
Example:
|
||||
.RS
|
||||
.nf
|
||||
certs:
|
||||
- data/*.cert
|
||||
.fi
|
||||
.RE
|
||||
|
||||
.TP
|
||||
\fBcertificate-chain.crls:\fR \fIlist\fR
|
||||
List of paths to certificate revocation list (CRL) files. Paths may contain
|
||||
wildcard patterns.
|
||||
.br
|
||||
Example:
|
||||
.RS
|
||||
.nf
|
||||
crls:
|
||||
- data/*.crl
|
||||
.fi
|
||||
.RE
|
||||
|
||||
.TP
|
||||
\fBcertificate-chain.offline:\fR \fIboolean\fR
|
||||
Enables offline mode for the \fBpvsecret create\fR command. When \fBtrue\fR, no
|
||||
network access is attempted for certificate verification.
|
||||
|
||||
.TP
|
||||
\fBcertificate-chain.root-ca:\fR \fIpath\fR
|
||||
Path to the root Certificate Authority (CA) file for the certificate chain.
|
||||
|
||||
.SH CONVERSION OPTIONS
|
||||
The \fBconvert\fR section contains configuration options for the image
|
||||
conversion phase. These options are used only when running the \fBconvert\fR or
|
||||
\fBfull\fR actions and have no effect on other actions.
|
||||
|
||||
.TP
|
||||
\fBconvert.boot-loader-entry:\fR \fIstring\fR
|
||||
\fB(REQUIRED)\fR Title of an existing boot loader entry from the base image.
|
||||
This entry specifies which kernel, initramfs, and kernel command line to use
|
||||
for the SEL image.
|
||||
.br
|
||||
Use the \fBlist\fR action to display available boot loader entries in the
|
||||
base image.
|
||||
|
||||
.TP
|
||||
\fBconvert.sel-kernel-parameter:\fR \fIstring\fR
|
||||
\fB(OPTIONAL)\fR Additional kernel parameters to append to the kernel command
|
||||
line of the resulting SEL image. The \fBrd.sel-ebc\fR parameter is always
|
||||
prepended automatically (unless \fBno-ebc\fR is \fBtrue\fR).
|
||||
.br
|
||||
Example:
|
||||
.RS
|
||||
.nf
|
||||
sel-kernel-parameter: swiotlb=524288
|
||||
.fi
|
||||
.RE
|
||||
|
||||
.TP
|
||||
\fBconvert.pvimg-create-options:\fR \fIstring\fR
|
||||
\fB(OPTIONAL)\fR Additional options to pass to the \fBpvimg\fR command during
|
||||
SEL image creation.
|
||||
.br
|
||||
See \fBpvimg\fR(1) for available options.
|
||||
|
||||
.SH ENCRYPTION OPTIONS
|
||||
The \fBencrypt\fR section contains configuration options specific to the root filesystem
|
||||
encryption and EBC resource preparation phase. These options are only used when running
|
||||
the \fBencrypt\fR or \fBfull\fR actions and are ignored when \fBno-ebc\fR
|
||||
is \fBtrue\fR.
|
||||
|
||||
.TP
|
||||
\fBencrypt.cck:\fR \fIpath\fR
|
||||
\fB(OPTIONAL)\fR Path to the customer communication key (CCK) file. If not
|
||||
supplied, a CCK will be generated from \fB/dev/random\fR and written to
|
||||
\fB<out>/cck.key\fR.
|
||||
|
||||
.TP
|
||||
\fBencrypt.extension-secret:\fR \fIpath\fR
|
||||
\fB(OPTIONAL)\fR Path to the extension secret file used for \fBpvsecret create\fR
|
||||
commands. If not supplied, an extension secret will be generated from
|
||||
\fB/dev/random\fR and written to \fB<out>/extension.secret\fR.
|
||||
.br
|
||||
\fBREQUIRED\fR if \fBencrypt.add-secret-requests\fR is used. In this case, the
|
||||
extension secret must match the one used in the supplied add/-secret requests.
|
||||
|
||||
.TP
|
||||
\fBencrypt.luks-key:\fR \fIpath\fR
|
||||
\fB(OPTIONAL)\fR Path to the LUKS encryption key file for the root filesystem.
|
||||
If not supplied, a key will be generated from \fB/dev/random\fR and written to
|
||||
\fB<out>/rfs.key\fR.
|
||||
|
||||
.TP
|
||||
\fBencrypt.luks-passphrase:\fR \fIpath\fR
|
||||
\fB(OPTIONAL)\fR Path to a file that contains the LUKS passphrase. If not supplied,
|
||||
a passphrase will be generated from \fB/dev/random\fR and written to
|
||||
\fB<out>/passphrase\fR.
|
||||
|
||||
.TP
|
||||
\fBencrypt.luks-key-asr-name:\fR \fIstring\fR
|
||||
\fB(OPTIONAL)\fR Name of the add-secret request that contains the LUKS
|
||||
encryption key. Default: \fBrfs-luks-key\fR.
|
||||
|
||||
.TP
|
||||
\fBencrypt.luks-key-size:\fR \fIinteger\fR
|
||||
\fB(OPTIONAL)\fR Key size in bits for the LUKS encryption key. Valid values:
|
||||
\fB256\fR or \fB512\fR. If not specified, the size is determined from the
|
||||
supplied key file or a default size is used for generated keys.
|
||||
|
||||
.TP
|
||||
\fBencrypt.add-secret-requests:\fR \fIlist\fR
|
||||
\fB(OPTIONAL)\fR List of paths to additional add/-secret request files to
|
||||
be added during boot. Paths may contain wildcard patterns.
|
||||
.br
|
||||
When using this option:
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
\fBencrypt.extension-secret\fR is \fBREQUIRED\fR
|
||||
.IP \(bu 2
|
||||
The supplied extension secret must match the extension secret used in all
|
||||
supplied add/-secret requests
|
||||
.RE
|
||||
.br
|
||||
Example:
|
||||
.RS
|
||||
.nf
|
||||
add-secret-requests:
|
||||
- data/*.asr
|
||||
- /path/to/custom.asr
|
||||
.fi
|
||||
.RE
|
||||
|
||||
.SH EXAMPLES
|
||||
.SS Minimal Configuration for EBC
|
||||
.nf
|
||||
out: /path/to/output
|
||||
hkds:
|
||||
- /path/to/*.hkd
|
||||
certificate-chain:
|
||||
certs:
|
||||
- /path/to/*.cert
|
||||
crls:
|
||||
- /path/to/*.crl
|
||||
offline: true
|
||||
root-ca: /path/to/root.ca
|
||||
convert:
|
||||
boot-loader-entry: "My Boot Entry"
|
||||
.fi
|
||||
|
||||
.SS Configuration with Custom Encryption Keys
|
||||
.nf
|
||||
out: /path/to/output
|
||||
hkds:
|
||||
- /path/to/*.hkd
|
||||
certificate-chain:
|
||||
certs:
|
||||
- /path/to/*.cert
|
||||
crls:
|
||||
- /path/to/*.crl
|
||||
offline: true
|
||||
root-ca: /path/to/root.ca
|
||||
convert:
|
||||
boot-loader-entry: "My Boot Entry"
|
||||
sel-kernel-parameter: swiotlb=524288
|
||||
pvimg-create-options: --enable-pckmo-hmac
|
||||
encrypt:
|
||||
cck: /path/to/cck.key
|
||||
luks-key: /path/to/rfs.key
|
||||
luks-passphrase: /path/to/passphrase
|
||||
luks-key-size: 512
|
||||
.fi
|
||||
|
||||
.SS Configuration with Additional ASRs
|
||||
.nf
|
||||
out: /path/to/output
|
||||
hkds:
|
||||
- /path/to/*.hkd
|
||||
certificate-chain:
|
||||
certs:
|
||||
- /path/to/*.cert
|
||||
crls:
|
||||
- /path/to/*.crl
|
||||
offline: true
|
||||
root-ca: /path/to/root.ca
|
||||
convert:
|
||||
boot-loader-entry: "My Boot Entry"
|
||||
encrypt:
|
||||
extension-secret: /path/to/extension.secret
|
||||
add-secret-requests:
|
||||
- /path/to/*.asr
|
||||
.fi
|
||||
|
||||
.SS SEL Image Without EBC
|
||||
.nf
|
||||
no-ebc: true
|
||||
out: /path/to/output
|
||||
hkds:
|
||||
- /path/to/*.hkd
|
||||
convert:
|
||||
boot-loader-entry: "My Boot Entry"
|
||||
.fi
|
||||
|
||||
.SH FILES
|
||||
.TP
|
||||
\fB/boot/loader/entries/*.conf\fR
|
||||
Boot loader entry files in the base image
|
||||
.TP
|
||||
\fB/boot/sel-ebc.img\fR
|
||||
The resulting SEL image file
|
||||
.TP
|
||||
\fB/boot/sics/\fR
|
||||
SEL Image customization source directory containing EBC resources
|
||||
.TP
|
||||
\fB/boot/sics/toc.pol\fR
|
||||
Table of contents policy file listing all add/-secret requests
|
||||
.TP
|
||||
\fB/boot/sics/toc.asr\fR
|
||||
Meta-secret for integrity protection of toc.pol
|
||||
.TP
|
||||
\fB/var/log/sel-ebc-zipl.log\fR
|
||||
Log file for zipl bootmap update operations
|
||||
|
||||
.SH SEE ALSO
|
||||
.BR pvics (8),
|
||||
.BR pvsecret (1),
|
||||
.BR pvimg (1),
|
||||
.BR zipl (8),
|
||||
.BR cryptsetup (8)
|
||||
.PP
|
||||
Linux on IBM Z and IBM LinuxONE: Secure Execution for Linux documentation
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# zfcpdbf - Tool to interpret the information from logging/tracing sources
|
||||
#
|
||||
# Copyright IBM Corp. 2010, 2020
|
||||
# Copyright IBM Corp. 2010, 2026
|
||||
#
|
||||
# s390-tools is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
@@ -102,6 +102,7 @@ use POSIX qw(strftime floor difftime mktime);
|
||||
use File::Spec::Functions qw/catfile catdir rel2abs/;
|
||||
use File::Basename;
|
||||
use Getopt::Long;
|
||||
use List::Util qw(min);
|
||||
|
||||
use constant TOD_UNIX_EPOCH => 0x7d91048bca000000;
|
||||
use constant STD_DEBUG_DIR => "/sys/kernel/debug/s390dbf/";
|
||||
@@ -595,6 +596,9 @@ sub _print_hba_id1
|
||||
my $rec_received = shift();
|
||||
my $rec_issued;
|
||||
my $pay_rec = $PAYLOAD_RECORDS{$fsf_req_id}{"fsf_res"};
|
||||
my $plogi_length = hex(substr($rec, 112, 8));
|
||||
my $prli_length = hex(substr($rec, 120, 8));
|
||||
my $els_rec = $PAYLOAD_RECORDS{$fsf_req_id}{"fsf_els"};
|
||||
|
||||
$rec_issued = stck_to_timeval(substr($rec, 0, 16));
|
||||
|
||||
@@ -627,6 +631,19 @@ sub _print_hba_id1
|
||||
print "QTCB log length: ", $payload_length, "\n" if ($payload_length);
|
||||
print_payload($payload_length, $pay_rec, "QTCB log info")
|
||||
if ($payload_length);
|
||||
|
||||
if ($plogi_length) {
|
||||
print "PLOGI length : ", $plogi_length, "\n";
|
||||
$plogi_length = min($plogi_length, 256);
|
||||
}
|
||||
if ($prli_length) {
|
||||
print "PRLI length : ", $prli_length, "\n";
|
||||
$prli_length = min($prli_length, 256 - $plogi_length);
|
||||
}
|
||||
if ($plogi_length || $prli_length) {
|
||||
print_payload($plogi_length + $prli_length, $els_rec,
|
||||
"PLOGI/PRLIinfo");
|
||||
}
|
||||
}
|
||||
|
||||
sub _print_hba_id2
|
||||
@@ -641,8 +658,15 @@ sub _print_hba_id2
|
||||
print "SRB D_ID : 0x", substr($rec, 16, 8), "\n";
|
||||
print "SRB LUN : 0x", substr($rec, 24, 16), "\n";
|
||||
print "SRB q-design. : 0x", substr($rec, 40, 16), "\n";
|
||||
print "SRB length : 0x", substr($rec, 56, 8), "\n";
|
||||
print "SRB res1 : 0x", substr($rec, 64, 8), "\n";
|
||||
print "SRB res2 : 0x", substr($rec, 72, 2), "\n";
|
||||
print "SRB class : 0x", substr($rec, 74, 8), "\n";
|
||||
print "SRB res3 : 0x", substr($rec, 82, 2), "\n";
|
||||
print "SRB S_ID : 0x", substr($rec, 84, 8), "\n";
|
||||
print "SRB res4 : ", payload_format(substr($rec, 92, 40));
|
||||
|
||||
print "SRB length : ", $payload_length, "\n" if ($payload_length);
|
||||
print "SRB pay length : ", $payload_length, "\n" if ($payload_length);
|
||||
print_payload($payload_length, $pay_rec, "SRB info")
|
||||
if ($payload_length);
|
||||
}
|
||||
@@ -714,6 +738,19 @@ sub _print_hba_id5
|
||||
print "FCES new : 0x", substr($rec, 56, 8), "\n";
|
||||
}
|
||||
|
||||
sub _print_hba_id6
|
||||
{
|
||||
my $fsf_req_id = shift();
|
||||
my $rec = shift();
|
||||
my $payload_length = shift();
|
||||
my $rec_received = shift();
|
||||
my $rec_issued;
|
||||
|
||||
print "WWPN : 0x", substr($rec, 0, 16), "\n";
|
||||
print "LUN : 0x", substr($rec, 16, 16), "\n";
|
||||
print "Return Value : 0x", substr($rec, 32, 8), "\n";
|
||||
}
|
||||
|
||||
sub print_deferr_common
|
||||
{
|
||||
my $rec = shift();
|
||||
@@ -916,6 +953,7 @@ sub assign_callback_subs
|
||||
$print_hba_id[2] = \&_print_hba_id2;
|
||||
$print_hba_id[3] = \&_print_hba_id3;
|
||||
$print_hba_id[5] = \&_print_hba_id5;
|
||||
$print_hba_id[6] = \&_print_hba_id6;
|
||||
|
||||
$print_rec_id[1] = \&_print_rec_id1;
|
||||
$print_rec_id[2] = \&_print_rec_id2;
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
include ../common.mak
|
||||
|
||||
ALL_CPPFLAGS += -D_FILE_OFFSET_BITS=64
|
||||
|
||||
all: tape390_display tape390_crypt
|
||||
|
||||
tape390_display: tape390_display.o tape390_common.o
|
||||
|
||||
tape390_crypt: tape390_crypt.o tape390_common.o
|
||||
|
||||
install: all
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 tape390_display \
|
||||
$(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 tape390_display.8 \
|
||||
$(DESTDIR)$(MANDIR)/man8
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 tape390_crypt \
|
||||
$(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 tape390_crypt.8 \
|
||||
$(DESTDIR)$(MANDIR)/man8
|
||||
|
||||
clean:
|
||||
rm -f *.o *~ tape390_display tape390_crypt core
|
||||
|
||||
.PHONY: all install clean
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* tape_390 - Common functions
|
||||
*
|
||||
* Copyright IBM Corp. 2006, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysmacros.h>
|
||||
|
||||
#include "tape390_common.h"
|
||||
|
||||
#define PROC_DEVICES_FILE "/proc/devices"
|
||||
#define PROC_DEVICES_FILE_WIDTH 100
|
||||
|
||||
char *prog_name; /* Name of tool */
|
||||
|
||||
/*
|
||||
* Set name of tool
|
||||
*/
|
||||
void set_prog_name(char *name)
|
||||
{
|
||||
prog_name = name;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether specified device node is tape device
|
||||
*/
|
||||
int is_not_tape(char *device)
|
||||
{
|
||||
FILE* fh;
|
||||
char line[PROC_DEVICES_FILE_WIDTH];
|
||||
char last_line[PROC_DEVICES_FILE_WIDTH];
|
||||
int found = 0;
|
||||
struct stat stat_struct;
|
||||
|
||||
if (stat(device, &stat_struct)) {
|
||||
ERRMSG("%s: Unable to get device status for "
|
||||
"'%s'. \n", prog_name, device);
|
||||
perror("");
|
||||
return 1;
|
||||
}
|
||||
fh = fopen(PROC_DEVICES_FILE,"r");
|
||||
if (!fh) {
|
||||
ERRMSG("%s: WARNING: Cannot check for tape in file "
|
||||
PROC_DEVICES_FILE ".\n", prog_name);
|
||||
perror("");
|
||||
return(0); /* check not possible, just continue */
|
||||
}
|
||||
while (!found && (fscanf(fh, "%s", line) != EOF)) {
|
||||
if (strcmp(line, "tape") == 0)
|
||||
found = 1;
|
||||
else
|
||||
strcpy(last_line, line);
|
||||
}
|
||||
fclose(fh);
|
||||
if (found && (major(stat_struct.st_rdev) ==
|
||||
(unsigned int) atoi(last_line)))
|
||||
return (0);
|
||||
else {
|
||||
ERRMSG("%s: '%s' is not a tape device. \n", prog_name, device);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Open the tape device
|
||||
*/
|
||||
int open_tape(char *device)
|
||||
{
|
||||
int fd;
|
||||
fd = open(device,O_RDONLY);
|
||||
if (fd < 0) {
|
||||
ERRMSG("%s: Cannot open device %s.\n",
|
||||
prog_name,device);
|
||||
perror("");
|
||||
exit(EXIT_MISUSE);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
* tape_390 - Common functions
|
||||
*
|
||||
* Copyright IBM Corp. 2006, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef _TAPE390_COMMON_H
|
||||
#define _TAPE390_COMMON_H
|
||||
|
||||
#define ERRMSG(x...) {fflush(stdout);fprintf(stderr,x);}
|
||||
#define ERRMSG_EXIT(ec,x...) do {fflush(stdout);fprintf(stderr,x);exit(ec);} while(0)
|
||||
#define EXIT_MISUSE 1
|
||||
|
||||
extern int is_not_tape(char *);
|
||||
extern int open_tape(char *);
|
||||
extern void set_prog_name(char *);
|
||||
extern char *prog_name;
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user