Files
s390-tools/zconf/chp/chchp.c
Michael Holzheu b627b8d8e1 Initial s390-tools-2.0.0 import
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>
2017-08-21 10:55:40 +02:00

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;
}