mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
This commit is based on the s390-tools-1.39.0 version. Changes on top of s390-tools-1.39.0: - Add MIT license to all source files - Add LICENSE file - Transform REAMDE to README.md (markdown) - Add AUTHORS.md file - Add CONTRIBUTING.md file - Move changelog from README to CHANGELOG.md file Reviewed-by: Stefan Haberland <sth@linux.vnet.ibm.com> Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
443 lines
9.4 KiB
C
443 lines
9.4 KiB
C
/*
|
|
* chchp - Tool to modify channel-path state
|
|
*
|
|
* Provide main function and command line parsing.
|
|
*
|
|
* Copyright IBM Corp. 2016, 2017
|
|
*
|
|
* s390-tools is free software; you can redistribute it and/or modify
|
|
* it under the terms of the MIT license. See LICENSE for details.
|
|
*/
|
|
|
|
#include <err.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/zt_common.h"
|
|
|
|
#define CIO_SETTLE "/proc/cio_settle"
|
|
#define MAX_CHPID_CSS 255
|
|
#define MAX_CHPID_ID 255
|
|
|
|
/*
|
|
* Private data
|
|
*/
|
|
struct chchp_l {
|
|
struct {
|
|
enum cmd_code {
|
|
CMD_NONE,
|
|
CMD_ATTRIBUTE,
|
|
CMD_VARY,
|
|
CMD_CONFIGURE,
|
|
} code;
|
|
const char *value;
|
|
} cmd;
|
|
} l;
|
|
|
|
struct chchp_l *chchp_l = &l;
|
|
|
|
/*
|
|
* Program configuration
|
|
*/
|
|
const struct util_prg prg = {
|
|
.desc =
|
|
"Modify the state of channel-path CHPID. CHPID can be a single, hexadecimal\n"
|
|
"channel-path identifier, a comma-separated list or a range of identifiers.",
|
|
.copyright_vec = {
|
|
{
|
|
.owner = "IBM Corp.",
|
|
.pub_first = 2016,
|
|
.pub_last = 2017,
|
|
},
|
|
UTIL_PRG_COPYRIGHT_END
|
|
}
|
|
};
|
|
|
|
/*
|
|
* Configuration of command line options
|
|
*/
|
|
static struct util_opt opt_vec[] = {
|
|
{
|
|
.option = { "vary", required_argument, NULL, 'v'},
|
|
.argument = "VALUE",
|
|
.desc = "Logically vary channel-path to VALUE (1=on, 0=off)",
|
|
},
|
|
{
|
|
.option = { "configure", required_argument, NULL, 'c'},
|
|
.argument = "VALUE",
|
|
.desc = "Configure channel-path to VALUE (1=on, 0=standby)",
|
|
},
|
|
{
|
|
.option = { "attribute", required_argument, NULL, 'a'},
|
|
.argument = "KEY=VALUE",
|
|
.desc = "Set channel-path attribute KEY to VALUE",
|
|
},
|
|
UTIL_OPT_HELP,
|
|
{
|
|
.option = { "version", 0, NULL, 'V'},
|
|
.desc = "Print version information, then exit",
|
|
},
|
|
UTIL_OPT_END
|
|
};
|
|
|
|
/*
|
|
* Write and check attribute value
|
|
*/
|
|
static void write_value(const char *dir, const char *key, const char *val)
|
|
{
|
|
char val2[256];
|
|
|
|
if (!util_path_is_reg_file("%s/%s", dir, key)) {
|
|
printf("failed - no such attribute\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if (!util_path_is_writable("%s/%s", dir, key)) {
|
|
printf("failed - attribute not writable\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if (util_file_write_s(val, "%s/%s", dir, key)) {
|
|
printf("failed - write failed\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if (util_file_read_line(val2, sizeof(val2), "%s/%s", dir, key)) {
|
|
printf("failed - could not determine new attribute value\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
/*
|
|
* Skip value comparison for 'status' attribute because input
|
|
* can be specified in different ways.
|
|
*/
|
|
if (strcmp(key, "status") != 0) {
|
|
if (strcmp(val, val2) != 0) {
|
|
printf("failed - attribute value not as expected\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
printf("done.\n");
|
|
}
|
|
|
|
/*
|
|
* Configure channel-path
|
|
*/
|
|
static void configure(int css, int num, const char *dir, const char *val)
|
|
{
|
|
const char *op;
|
|
|
|
if (strcmp(val, "0") == 0)
|
|
op = "standby";
|
|
else
|
|
op = "online";
|
|
|
|
printf("Configure %s %x.%02x... ", op, css, num);
|
|
write_value(dir, "configure", val);
|
|
}
|
|
|
|
/*
|
|
* Vary channel-path
|
|
*/
|
|
static void vary(int css, int num, const char *dir, const char *val)
|
|
{
|
|
const char *op;
|
|
|
|
if (strcmp(val, "0") == 0)
|
|
op = "offline";
|
|
else
|
|
op = "online";
|
|
|
|
printf("Vary %s %x.%02x... ", op, css, num);
|
|
write_value(dir, "status", val);
|
|
}
|
|
|
|
/*
|
|
* Get key and value from "KEY=VALUE"
|
|
*/
|
|
static int get_key_value(char **key, char **val, const char *key_val)
|
|
{
|
|
char *ptr;
|
|
|
|
if (!strchr(key_val, '='))
|
|
return -1;
|
|
*key = util_strdup(key_val);
|
|
ptr = strchr(*key, '=');
|
|
*ptr = '\0';
|
|
ptr += 1;
|
|
*val = util_strdup(ptr);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Modify channel-path attribute
|
|
*/
|
|
static void attribute(int css, int num, const char *dir, const char *key_val)
|
|
{
|
|
char *key, *val;
|
|
|
|
if (get_key_value(&key, &val, key_val))
|
|
return;
|
|
printf("Attribute %s=%s %x.%02x... ", key, val, css, num);
|
|
write_value(dir, key, val);
|
|
free(key);
|
|
free(val);
|
|
}
|
|
|
|
/*
|
|
* Make sure only one command is specified and argument was specified correctly
|
|
*/
|
|
static void check_and_set_command(enum cmd_code code, const char *value)
|
|
{
|
|
char *key, *val;
|
|
int rc;
|
|
|
|
if (l.cmd.code != CMD_NONE) {
|
|
errx(EXIT_FAILURE, "Only one of --vary, --configure or "
|
|
"--attribute allowed");
|
|
}
|
|
switch (code) {
|
|
case CMD_ATTRIBUTE:
|
|
rc = get_key_value(&key, &val, value);
|
|
if (rc || strlen(key) == 0 || strlen(val) == 0)
|
|
errx(EXIT_FAILURE, "--attribute requires an argument");
|
|
free(key);
|
|
free(val);
|
|
break;
|
|
case CMD_VARY:
|
|
if (strcmp(value, "0") == 0 || strcmp(value, "1") == 0)
|
|
break;
|
|
errx(EXIT_FAILURE, "Invalid value for --vary (only 0 or 1 "
|
|
"allowed)");
|
|
case CMD_CONFIGURE:
|
|
if (strcmp(value, "0") == 0 || strcmp(value, "1") == 0)
|
|
break;
|
|
errx(EXIT_FAILURE, "Invalid value for --configure (only 0 or 1 "
|
|
"allowed)");
|
|
default:
|
|
break;
|
|
}
|
|
l.cmd.code = code;
|
|
l.cmd.value = value;
|
|
}
|
|
|
|
/*
|
|
* Get channel-path directory
|
|
*/
|
|
static char *get_chp_dir(int css, int id)
|
|
{
|
|
struct stat sb;
|
|
char *path;
|
|
|
|
path = util_path_sysfs("devices/css%x/chp%x.%x", css, css, id);
|
|
if ((stat(path, &sb) == 0) && (sb.st_mode == S_IFDIR))
|
|
return path;
|
|
free(path);
|
|
return util_path_sysfs("devices/css%x/chp%x.%02x", css, css, id);
|
|
}
|
|
|
|
/*
|
|
* Extract css id from channel-path id string
|
|
*/
|
|
static int get_chpid_css(const char *chpid)
|
|
{
|
|
int id, css_id;
|
|
|
|
if (strchr(chpid, '.') == NULL) {
|
|
css_id = 0;
|
|
} else {
|
|
if (sscanf(chpid, "%x.%x", &css_id, &id) != 2) {
|
|
errx(EXIT_FAILURE, "Invalid channel-path identifier "
|
|
"'%s'", chpid);
|
|
}
|
|
if (css_id < 0 || css_id > MAX_CHPID_CSS) {
|
|
errx(EXIT_FAILURE, "Invalid channel-path identifier "
|
|
"'%s'", chpid);
|
|
}
|
|
}
|
|
return css_id;
|
|
}
|
|
|
|
/*
|
|
* Extract id from channel-path id string
|
|
*/
|
|
static int get_chpid_id(const char *chpid)
|
|
{
|
|
int id, css_id;
|
|
|
|
if (strchr(chpid, '.') == NULL) {
|
|
if (sscanf(chpid, "%x", &id) != 1) {
|
|
errx(EXIT_FAILURE, "Invalid channel-path identifier "
|
|
"'%s'", chpid);
|
|
}
|
|
} else if (sscanf(chpid, "%x.%x", &css_id, &id) != 2) {
|
|
errx(EXIT_FAILURE, "Invalid channel-path identifier '%s'",
|
|
chpid);
|
|
}
|
|
if (id < 0 || id > MAX_CHPID_ID) {
|
|
errx(EXIT_FAILURE, "Invalid channel-path identifier '%s'",
|
|
chpid);
|
|
}
|
|
return id;
|
|
}
|
|
|
|
/*
|
|
* Perform command specified by COMMAND and VALUE
|
|
*/
|
|
static void perform_command(int css, int id)
|
|
{
|
|
struct stat sb;
|
|
char *path;
|
|
|
|
path = get_chp_dir(css, id);
|
|
if ((stat(path, &sb) != 0) || ((sb.st_mode & S_IFMT) != S_IFDIR)) {
|
|
printf("Skipping unknown channel-path %x.%02x\n", css, id);
|
|
goto out_free_path;
|
|
}
|
|
switch (l.cmd.code) {
|
|
case CMD_VARY:
|
|
vary(css, id, path, l.cmd.value);
|
|
break;
|
|
case CMD_CONFIGURE:
|
|
configure(css, id, path, l.cmd.value);
|
|
break;
|
|
case CMD_ATTRIBUTE:
|
|
attribute(css, id, path, l.cmd.value);
|
|
break;
|
|
default:
|
|
util_panic("Invalid cmd: %d\n", l.cmd.code);
|
|
}
|
|
out_free_path:
|
|
free(path);
|
|
}
|
|
|
|
/*
|
|
* Calculate iterator steps for chpid loop
|
|
*/
|
|
static int get_iterator_step(int css1, int id1, int css2, int id2)
|
|
{
|
|
if (css1 == css2) {
|
|
if (id1 < id2)
|
|
return 1;
|
|
else
|
|
return -1;
|
|
} else if (css1 < css2) {
|
|
return 1;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Execute command on all chpids: from - to
|
|
*/
|
|
static void loop_chpids(int css1, int id1, int css2, int id2)
|
|
{
|
|
int step = get_iterator_step(css1, id1, css2, id2);
|
|
|
|
while (1) {
|
|
/* Perform function */
|
|
perform_command(css1, id1);
|
|
/* Check for loop end */
|
|
if ((css1 == css2) && (id1 == id2))
|
|
break;
|
|
/* Advance iterator */
|
|
id1 = id1 + step;
|
|
if (id1 < 0) {
|
|
css1 -= 1;
|
|
id1 = 255;
|
|
} else if (id2 > 255) {
|
|
css1 += 1;
|
|
id1 = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Parse options and execute the command
|
|
*/
|
|
int main(int argc, char *argv[])
|
|
{
|
|
char *chpid_from, *chpid_to, *chpid_list = NULL;
|
|
int from_css, from_id, to_css, to_id;
|
|
struct stat sb;
|
|
int c, i;
|
|
|
|
util_prg_init(&prg);
|
|
util_opt_init(opt_vec, NULL);
|
|
while (1) {
|
|
c = util_opt_getopt_long(argc, argv);
|
|
if (c == -1)
|
|
break;
|
|
switch (c) {
|
|
case 'h':
|
|
util_prg_print_help();
|
|
util_opt_print_help();
|
|
return EXIT_SUCCESS;
|
|
case 'V':
|
|
util_prg_print_version();
|
|
return EXIT_SUCCESS;
|
|
case 'v':
|
|
check_and_set_command(CMD_VARY, optarg);
|
|
break;
|
|
case 'c':
|
|
check_and_set_command(CMD_CONFIGURE, optarg);
|
|
break;
|
|
case 'a':
|
|
check_and_set_command(CMD_ATTRIBUTE, optarg);
|
|
break;
|
|
default:
|
|
util_opt_print_parse_error(c, argv);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
if (l.cmd.code == CMD_NONE) {
|
|
errx(EXIT_FAILURE, "One of --vary, --configure or --attribute "
|
|
"required");
|
|
}
|
|
if (optind == argc) {
|
|
errx(EXIT_FAILURE, "Need to specify at least one channel-path "
|
|
"ID");
|
|
}
|
|
|
|
/* Append each argument with comma to distinguish blank-separators */
|
|
for (i = optind; i < argc; i++) {
|
|
if (i > optind)
|
|
chpid_list = util_strcat_realloc(chpid_list, ",");
|
|
chpid_list = util_strcat_realloc(chpid_list, argv[i]);
|
|
}
|
|
|
|
/* Loop over comma-separated list */
|
|
chpid_from = strtok(chpid_list, ",");
|
|
|
|
while (chpid_from != NULL) {
|
|
chpid_to = strchr(chpid_from, '-');
|
|
if (chpid_to == NULL)
|
|
chpid_to = chpid_from;
|
|
else
|
|
*chpid_to++ = '\0';
|
|
if (*chpid_to == '\0') {
|
|
errx(EXIT_FAILURE, "Invalid channel-path identifier "
|
|
"range %s", chpid_from);
|
|
}
|
|
from_css = get_chpid_css(chpid_from);
|
|
from_id = get_chpid_id(chpid_from);
|
|
to_css = get_chpid_css(chpid_to);
|
|
to_id = get_chpid_id(chpid_to);
|
|
loop_chpids(from_css, from_id, to_css, to_id);
|
|
chpid_from = strtok(NULL, ",");
|
|
}
|
|
|
|
/* Do CIO settle */
|
|
if (stat(CIO_SETTLE, &sb) != 0)
|
|
util_file_write_s("1", CIO_SETTLE);
|
|
free(chpid_list);
|
|
return EXIT_SUCCESS;
|
|
}
|