Files
s390-tools/zconf/zcrypt/chzcrypt.c
Szabina Korbai a5af5bcf70 zcrypt: Implement zsh and bash autocompletion
Add generation of shell autocompletion scripts
to chzcrypt, lszcrypt and zcryptstats.

Acked-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Szabina Korbai <szkorbai@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-04-30 08:51:04 +02:00

696 lines
20 KiB
C

/*
* chzcrypt - Tool to modify zcrypt configuration
*
* Copyright IBM Corp. 2008, 2023
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <argz.h>
#include <err.h>
#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "lib/util_base.h"
#include "lib/util_file.h"
#include "lib/util_libc.h"
#include "lib/util_opt.h"
#include "lib/util_panic.h"
#include "lib/util_path.h"
#include "lib/util_prg.h"
#include "lib/util_scandir.h"
#include "lib/zt_common.h"
#include "chzcrypt_cli.h"
#include "misc.h"
/* max seconds the se-association command will wait for completion */
#define MAX_ASSOC_POLL_TIME_IN_S 30
/* max seconds the se-unbind command will wait for unbind complete */
#define MAX_UNBIND_POLL_TIME_IN_S 30
/*
* Private data
*/
static struct chzcrypt_l {
int verbose;
} l;
/*
* Program configuration
*/
static const struct util_prg prg = {
.desc = "Modify zcrypt configuration.",
.args = "[DEVICE_IDS]",
.copyright_vec = {
{
.owner = "IBM Corp.",
.pub_first = 2008,
.pub_last = 2023,
},
UTIL_PRG_COPYRIGHT_END
}
};
/*
* Print if verbose is set
*/
#define verbose(x...) \
do { \
if (!l.verbose) \
break; \
printf(x); \
} while (0)
/*
* Set poll settings
*/
static void poll_thread_set(const char *mode_str)
{
long mode, mode_read = -1;
char *attr;
sscanf(mode_str, "%ld", &mode);
if (mode == 1)
verbose("Enabling poll thread.\n");
else
verbose("Disabling poll thread.\n");
attr = util_path_sysfs("bus/ap/poll_thread");
if (!util_path_is_writable(attr))
errx(EXIT_FAILURE, "Error - can't write to %s.\n Wrong permissions"
" or wrong tools version.", attr);
util_file_write_l(mode, 10, attr);
util_file_read_l(&mode_read, 10, attr);
if (mode != mode_read)
errx(EXIT_FAILURE, "Error - unable to change poll thread setting.");
free(attr);
}
/*
* Set timer
*/
static void config_time_set(const char *timeout_str)
{
long timeout, timeout_read;
char *attr;
if (sscanf(timeout_str, "%ld", &timeout) != 1) {
errx(EXIT_FAILURE, "Error - invalid configuration timeout '%s'.", timeout_str);
}
attr = util_path_sysfs("bus/ap/config_time");
verbose("Setting configuration timer to %ld seconds.\n", timeout);
if (!util_path_is_writable(attr))
errx(EXIT_FAILURE, "Error - can't write to %s.\n Wrong permissions"
" or wrong tools version.", attr);
util_file_write_l(timeout, 10, attr);
util_file_read_l(&timeout_read, 10, attr);
if (timeout != timeout_read)
errx(EXIT_FAILURE, "Error - unable to change configuration timer setting.");
free(attr);
}
/*
* Set poll timeout
*/
static void poll_timeout_set(const char *poll_timeout_str)
{
long poll_timeout, poll_timeout_read;
char *attr;
if (sscanf(poll_timeout_str, "%ld", &poll_timeout) != 1)
errx(EXIT_FAILURE, "Error - invalid poll timeout '%s'.", poll_timeout_str);
attr = util_path_sysfs("bus/ap/poll_timeout");
verbose("Setting poll timeout to %ld seconds.\n", poll_timeout);
if (!util_path_is_writable(attr))
errx(EXIT_FAILURE, "Error - can't write to %s.\n Wrong permissions"
" or wrong tools version.", attr);
util_file_write_l(poll_timeout, 10, attr);
util_file_read_l(&poll_timeout_read, 10, attr);
if (poll_timeout != poll_timeout_read)
errx(EXIT_FAILURE, "Error - unable to change poll timeout setting.");
free(attr);
}
/*
* Set default domain
*/
static void default_domain_set(const char *default_domain_str)
{
long max_dom, default_domain, default_domain_read;
char *attr, *ap_max_domain_id, *ap;
char use_dom_mask[80];
/* check if ap driver is available */
ap = util_path_sysfs("bus/ap");
if (!util_path_is_dir(ap))
errx(EXIT_FAILURE, "Crypto device driver not available.");
/* read usage domain mask */
util_file_read_line(use_dom_mask, sizeof(use_dom_mask),
"%s/ap_usage_domain_mask", ap);
sscanf(default_domain_str, "%li", &default_domain);
ap_max_domain_id = util_path_sysfs("bus/ap/ap_max_domain_id");
util_file_read_l(&max_dom, 10, ap_max_domain_id);
if (default_domain < 0 || default_domain > max_dom)
errx(EXIT_FAILURE, "Error - invalid default domain '%s'.", default_domain_str);
attr = util_path_sysfs("bus/ap/ap_domain");
if (!util_path_is_writable(attr))
errx(EXIT_FAILURE, "Error - can't write to %s.\n Wrong permissions"
" or wrong tools version.", attr);
verbose("Setting default domain to %ld.\n", default_domain);
util_file_write_l(default_domain, 10, attr);
util_file_read_l(&default_domain_read, 10, attr);
if (default_domain != default_domain_read)
errx(EXIT_FAILURE, "Error - unable to change default domain.");
/* give a warning, when the fresh set default domain is unavailable */
if (default_domain < 0 || check_mask_bit(use_dom_mask, default_domain) < 1)
printf("Warning: Default domain 0x%02lx is not available.\n", default_domain);
free(ap_max_domain_id);
free(attr);
free(ap);
}
static void set_online(const char *online, const char *online_text,
char *dev_list, size_t len)
{
long value;
int id, dom;
char *dev, *dev_path;
char device[256], online_read[32];
for (dev = dev_list; dev != NULL; dev = argz_next(dev_list, len, dev)) {
if (strncmp(dev, "card", 4) == 0) {
/* dev == "card2" */
if (sscanf(dev, "card%02x", &id) != 1)
errx(EXIT_FAILURE, "Error - unable to parse '%s'.", dev);
sprintf(device, "card%02x", id);
} else if (strncmp(dev, "0x", 2) == 0) {
/* dev == "0x.." */
if (sscanf(dev, "0x%02x", &id) != 1)
errx(EXIT_FAILURE, "Error - unable to parse '%s'.", dev);
sprintf(device, "card%02x", id);
} else if (misc_regex_match(dev, "^[0-9a-fA-F]+$")) {
/* dev == "2" */
if (sscanf(dev, "%02x", &id) != 1)
errx(EXIT_FAILURE, "Error - unable to parse '%s'.", dev);
sprintf(device, "card%02x", id);
} else {
/* Form: 01.0003 ? */
if (sscanf(dev, "%02x.%04x", &id, &dom) != 2)
errx(EXIT_FAILURE,
"Error - cryptographic device %s malformed.", dev);
sprintf(device, "card%02x/%02x.%04x", id, id, dom);
}
dev_path = util_path_sysfs("bus/ap/devices/%s", device);
if (!util_path_is_dir(dev_path))
errx(EXIT_FAILURE,
"Error - cryptographic device %s does not exist.", device);
if (!util_path_is_writable("%s/online", dev_path))
errx(EXIT_FAILURE, "Error - can't write to %s/online.\n"
" Wrong permissions or wrong tools version.", dev_path);
if (*online == '1' && util_path_is_readable("%s/config", dev_path)) {
util_file_read_l(&value, 10, "%s/config", dev_path);
if (value <= 0) {
warnx("Warning - device %s is deconfigured,"
" can't set to online.\n", dev);
goto next;
}
}
verbose("Setting cryptographic device %s %s\n", device, online_text);
util_file_write_s(online, "%s/online", dev_path);
util_file_read_line(online_read, sizeof(online_read), "%s/online", dev_path);
if (strcmp(online, online_read) != 0)
errx(EXIT_FAILURE, "Error - unable to set cryptographic device %s %s.",
device, online_text);
next:
free(dev_path);
}
}
static void set_config(const char *config, const char *config_text,
char *dev_list, size_t len)
{
int id;
char *dev, *dev_path;
char device[256], config_read[32];
for (dev = dev_list; dev != NULL; dev = argz_next(dev_list, len, dev)) {
if (strncmp(dev, "card", 4) == 0) {
/* dev == "card2" */
if (sscanf(dev, "card%02x", &id) != 1)
errx(EXIT_FAILURE, "Error - unable to parse '%s'.", dev);
sprintf(device, "card%02x", id);
} else if (strncmp(dev, "0x", 2) == 0) {
/* dev == "0x.." */
if (sscanf(dev, "0x%02x", &id) != 1)
errx(EXIT_FAILURE, "Error - unable to parse '%s'.", dev);
sprintf(device, "card%02x", id);
} else if (misc_regex_match(dev, "^[0-9a-fA-F]+$")) {
/* dev == "2" */
if (sscanf(dev, "%02x", &id) != 1)
errx(EXIT_FAILURE, "Error - unable to parse '%s'.", dev);
sprintf(device, "card%02x", id);
} else {
errx(EXIT_FAILURE, "Error - invalid device %s\n"
" Config on/off is only valid for card devices.", dev);
}
dev_path = util_path_sysfs("bus/ap/devices/%s", device);
if (!util_path_is_dir(dev_path))
errx(EXIT_FAILURE,
"Error - cryptographic device %s does not exist.", device);
if (!util_path_is_readable("%s/config", dev_path))
errx(EXIT_FAILURE, "Error - can't read %s/config.\n"
"File may not exist due to an older zcrypt device driver.", dev_path);
util_file_read_line(config_read, sizeof(config_read), "%s/config", dev_path);
if (strcmp(config, config_read) == 0) {
warnx("Warning - device %s is already %s.", device, config_text);
goto next;
}
if (!util_path_is_writable("%s/config", dev_path))
errx(EXIT_FAILURE, "Error - can't write to %s/config.\n"
"Wrong permissions or wrong tools version.", dev_path);
verbose("Setting cryptographic device %s %s\n", device, config_text);
util_file_write_s(config, "%s/config", dev_path);
util_file_read_line(config_read, sizeof(config_read), "%s/config", dev_path);
if (strcmp(config, config_read) != 0)
errx(EXIT_FAILURE, "Error - unable to set cryptographic device %s %s.",
device, config_text);
next:
free(dev_path);
}
}
static void se_assoc(const char *assoc_idx, const char *dev)
{
int i, idx, rc, ap, dom, loop;
char *dev_path, *attr;
char buf[256];
if (!ap_bus_has_SB_support())
errx(EXIT_FAILURE, "Error - AP bus: SE bind support is not available.");
if (sscanf(dev, "%02x.%04x", &ap, &dom) != 2)
errx(EXIT_FAILURE, "Error - Can't parse queue device '%s' as xy.abcd.",
dev);
dev_path = util_path_sysfs("bus/ap/devices/card%02x/%02x.%04x",
ap, ap, dom);
if (!util_path_is_dir(dev_path))
errx(EXIT_FAILURE, "Error - Queue device %s does not exist.",
dev);
if (sscanf(assoc_idx, "%i", &idx) != 1)
errx(EXIT_FAILURE, "Error - Can't parse association index '%s' as number.",
assoc_idx);
if (idx < 0 || idx > 0xFFFF)
errx(EXIT_FAILURE, "Error - Association index needs to be in range [0...%d].",
0xffff);
attr = util_path_sysfs("bus/ap/devices/card%02x/%02x.%04x/se_associate",
ap, ap, dom);
if (!util_path_is_writable(attr))
errx(EXIT_FAILURE, "Error - Can't write to %s (errno '%s').",
attr, strerror(errno));
/* read se_associate attribute and check for 'unassociated' */
rc = util_file_read_line(buf, sizeof(buf), attr);
if (rc)
errx(EXIT_FAILURE, "Error - Failure reading from %s (errno '%s').",
attr, strerror(errno));
if (strcmp(buf, "unassociated"))
errx(EXIT_FAILURE,
"Error - Queue device %s is NOT in 'unassociated' state (state '%s' found).",
dev, buf);
/* write assocition index to the se_associate attribute */
rc = util_file_write_l(idx, 10, attr);
if (rc)
errx(EXIT_FAILURE, "Error - Failure writing to %s (errno '%s').",
attr, strerror(errno));
/* loop up to MAX_ASSOC_POLL_TIME_IN_S seconds for completion */
for (loop = 0; loop < 2 * MAX_ASSOC_POLL_TIME_IN_S; usleep(500000), loop++) {
rc = util_file_read_line(buf, sizeof(buf), attr);
if (rc)
errx(EXIT_FAILURE, "Error - Failure reading from %s (errno '%s').",
attr, strerror(errno));
if (!strncmp(buf, "associated", strlen("associated")))
break;
if (!strcmp(buf, "unassociated"))
errx(EXIT_FAILURE,
"Error - Failure associating queue device %s (state '%s' found).",
dev, buf);
}
if (loop >= 2 * MAX_ASSOC_POLL_TIME_IN_S)
errx(EXIT_FAILURE,
"Error - Failure associating queue device %s (timeout after %d s).",
dev, MAX_ASSOC_POLL_TIME_IN_S);
if (sscanf(buf, "associated %d", &i) != 1 || idx != i)
errx(EXIT_FAILURE,
"Error - Failure associating queue device %s (state '%s' found).",
dev, buf);
verbose("Queue device %s successful associated with index %d.\n",
dev, idx);
free(dev_path);
free(attr);
}
static void se_bind(const char *dev)
{
char *dev_path, *attr;
int rc, ap, dom;
char buf[256];
if (!ap_bus_has_SB_support())
errx(EXIT_FAILURE, "Error - AP bus: SE bind support is not available.");
if (sscanf(dev, "%02x.%04x", &ap, &dom) != 2)
errx(EXIT_FAILURE, "Error - Can't parse queue device '%s' as xy.abcd.",
dev);
dev_path = util_path_sysfs("bus/ap/devices/card%02x/%02x.%04x",
ap, ap, dom);
if (!util_path_is_dir(dev_path))
errx(EXIT_FAILURE, "Error - Queue device %s does not exist.",
dev);
attr = util_path_sysfs("bus/ap/devices/card%02x/%02x.%04x/se_bind",
ap, ap, dom);
if (!util_path_is_writable(attr))
errx(EXIT_FAILURE, "Error - Can't write to %s (errno '%s').",
attr, strerror(errno));
/* read se_bind attribute and check for 'unboud' */
rc = util_file_read_line(buf, sizeof(buf), attr);
if (rc)
errx(EXIT_FAILURE, "Error - Failure reading from %s (errno '%s').",
attr, strerror(errno));
if (strcmp(buf, "unbound"))
errx(EXIT_FAILURE,
"Error - Queue device %s is NOT in 'unbound' state (state '%s' found).",
dev, buf);
/* write se_bind attribute, check for 'bound' afterwards */
rc = util_file_write_l(1, 10, attr);
if (rc)
errx(EXIT_FAILURE, "Error - Failure writing to %s (errno '%s').",
attr, strerror(errno));
rc = util_file_read_line(buf, sizeof(buf), attr);
if (rc)
errx(EXIT_FAILURE, "Error - Failure reading from %s (errno '%s').",
attr, strerror(errno));
if (strcmp(buf, "bound"))
errx(EXIT_FAILURE, "Error - Failure binding queue device %s (state '%s' found).",
dev, buf);
verbose("Queue device %s successful bound.\n", dev);
free(dev_path);
free(attr);
}
static void se_unbind(const char *dev)
{
int rc, ap, dom, loop;
char *dev_path, *attr;
char buf[256];
if (!ap_bus_has_SB_support())
errx(EXIT_FAILURE, "Error - AP bus: SE bind support is not available.");
if (sscanf(dev, "%02x.%04x", &ap, &dom) != 2)
errx(EXIT_FAILURE, "Error - Can't parse queue device '%s' as xy.abcd.",
dev);
dev_path = util_path_sysfs("bus/ap/devices/card%02x/%02x.%04x",
ap, ap, dom);
if (!util_path_is_dir(dev_path))
errx(EXIT_FAILURE, "Error - Queue device %s does not exist.",
dev);
attr = util_path_sysfs("bus/ap/devices/card%02x/%02x.%04x/se_bind",
ap, ap, dom);
if (!util_path_is_writable(attr))
errx(EXIT_FAILURE, "Error - Can't write to %s (errno '%s').",
attr, strerror(errno));
/* write se_bind attribute */
rc = util_file_write_l(0, 10, attr);
if (rc)
errx(EXIT_FAILURE, "Error - Failure writing to %s (errno '%s').",
attr, strerror(errno));
/* loop up to MAX_UNBIND_POLL_TIME_IN_S seconds for completion */
for (loop = 0; loop < 2 * MAX_UNBIND_POLL_TIME_IN_S; usleep(500000), loop++) {
rc = util_file_read_line(buf, sizeof(buf), attr);
if (rc)
errx(EXIT_FAILURE, "Error - Failure reading from %s (errno '%s').",
attr, strerror(errno));
if (!strcmp(buf, "unbound"))
break;
}
if (loop >= 2 * MAX_UNBIND_POLL_TIME_IN_S)
errx(EXIT_FAILURE,
"Error - Failure unbinding queue device %s (timeout after %d s).",
dev, MAX_UNBIND_POLL_TIME_IN_S);
verbose("Queue device %s successful unbound.\n", dev);
free(dev_path);
free(attr);
}
/*
* Print invalid commandline error message and then exit with error code
*/
#define invalid_cmdline_exit(x...) \
do { \
fprintf(stderr, "%s: ", program_invocation_short_name); \
fprintf(stderr, x); \
util_prg_print_parse_error(); \
exit(EXIT_FAILURE); \
} while (0)
/*
* Get device list from sysfs
*/
static void dev_list_all(char **argz, size_t *len)
{
struct dirent **de_vec;
int count, i;
char *path;
path = util_path_sysfs("bus/ap/devices/");
count = util_scandir(&de_vec, NULL, path, "card.*");
if (count < 0)
errx(EXIT_FAILURE, "Error - Could not read directory %s.", path);
*argz = NULL;
*len = 0;
for (i = 0; i < count; i++)
util_assert(argz_add(argz, len, de_vec[i]->d_name) == 0,
"Out of memory\n");
util_scandir_free(de_vec, count);
free(path);
}
/*
* Get device list from commandline
*/
static void dev_list_argv(char **argz, size_t *len, char * const argv[])
{
if (argv[0] == NULL)
errx(EXIT_FAILURE, "Need to specify at least one device ID.");
util_assert(argz_create(argv, argz, len) == 0, "Out of memory\n");
}
/*
* Describe adapter ids
*/
static void print_adapter_id_help(void)
{
printf("\n");
printf("DEVICE_IDS\n");
printf(" List of cryptographic device ids separated by blanks which will be set\n");
printf(" online/offline. Must be used in conjunction with the enable or disable option.\n");
printf(" DEVICE_ID could either be card device id ('<card-id>') or queue device id\n");
printf(" '<card-id>.<domain-id>').\n\n");
printf("QUEUE_DEVICE:\n");
printf(" An APQN queue device given as xy.abcd as it is listed by lszcrypt -V.\n\n");
printf("EXAMPLE:\n");
printf(" Disable the cryptographic device with card id '02' (inclusive all queues).\n");
printf(" #>chzcrypt -d 02\n");
printf(" \n");
printf(" Enable the cryptographic devices with card id '03' and domain id '0005'.\n");
printf(" #>chzcrypt -e 03.0005\n");
printf(" \n");
}
/*
* Parse options and execute the command
*/
int main(int argc, char *argv[])
{
const char *default_domain = NULL, *config = NULL, *config_text = NULL;
const char *online = NULL, *online_text = NULL, *poll_thread = NULL;
const char *config_time = NULL, *poll_timeout = NULL;
const char *queue_device = NULL, *assoc_idx = NULL;
int c, i, j, action = 0;
char *path, *dev_list;
bool all = false;
size_t len;
for (i=0; i < argc; i++)
for (j=2; j < (int) strlen(argv[i]); j++)
if (argv[i][j] == '_')
argv[i][j] = '-';
util_prg_init(&prg);
util_opt_init(chzcrypt_opt_vec, NULL);
while (1) {
c = util_opt_getopt_long(argc, argv);
if (c == -1)
break;
switch (c) {
case 'e':
action = c;
online = "1";
online_text = "online";
break;
case 'd':
action = c;
online = "0";
online_text = "offline";
break;
case 'a':
all = true;
break;
case 'p':
action = c;
poll_thread = "1";
break;
case 'n':
action = c;
poll_thread = "0";
break;
case 'c':
action = c;
config_time = optarg;
break;
case 't':
action = c;
poll_timeout = optarg;
break;
case 'q':
action = c;
default_domain = optarg;
break;
case 'V':
l.verbose = true;
break;
case 'h':
util_prg_print_help();
util_opt_print_help();
print_adapter_id_help();
return EXIT_SUCCESS;
case 'v':
util_prg_print_version();
return EXIT_SUCCESS;
case OPT_CONFIG_ON:
action = c;
config = "1";
config_text = "config on";
break;
case OPT_CONFIG_OFF:
action = c;
config = "0";
config_text = "config off";
break;
case OPT_SE_ASSOC:
action = c;
assoc_idx = optarg;
break;
case OPT_SE_BIND:
action = c;
break;
case OPT_SE_UNBIND:
action = c;
break;
default:
util_opt_print_parse_error(c, argv);
return EXIT_FAILURE;
}
}
if (!action)
invalid_cmdline_exit("Error - missing argument.\n");
path = util_path_sysfs("bus/ap");
if (!util_path_is_dir(path))
errx(EXIT_FAILURE, "Crypto device driver not available.");
free(path);
if (poll_thread) {
poll_thread_set(poll_thread);
return EXIT_SUCCESS;
}
if (config_time) {
config_time_set(config_time);
return EXIT_SUCCESS;
}
if (poll_timeout) {
poll_timeout_set(poll_timeout);
return EXIT_SUCCESS;
}
if (default_domain) {
default_domain_set(default_domain);
return EXIT_SUCCESS;
}
if (action == OPT_SE_ASSOC) {
if (optind >= argc)
errx(EXIT_FAILURE,
"Error - The --se-associate needs a queue device given.");
queue_device = argv[optind];
se_assoc(assoc_idx, queue_device);
return EXIT_SUCCESS;
}
if (action == OPT_SE_BIND) {
if (optind >= argc)
errx(EXIT_FAILURE,
"Error - The --se-bind needs a queue device given.");
queue_device = argv[optind];
se_bind(queue_device);
return EXIT_SUCCESS;
}
if (action == OPT_SE_UNBIND) {
if (optind >= argc)
errx(EXIT_FAILURE,
"Error - The --se-unbind needs a queue device given.");
queue_device = argv[optind];
se_unbind(queue_device);
return EXIT_SUCCESS;
}
if (all)
dev_list_all(&dev_list, &len);
else
dev_list_argv(&dev_list, &len, &argv[optind]);
if ((online || config) && len == 0)
errx(EXIT_FAILURE, "Error - missing cryptographic device id(s).");
if (online)
set_online(online, online_text, dev_list, len);
else if (config)
set_config(config, config_text, dev_list, len);
return EXIT_SUCCESS;
}