mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Add generation of shell autocompletion scripts to chchp and lschp. 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>
359 lines
9.0 KiB
C
359 lines
9.0 KiB
C
/*
|
|
* lschp - List information about available channel-paths
|
|
*
|
|
* Provide main function and command line parsing.
|
|
*
|
|
* Copyright IBM Corp. 2016, 2019
|
|
*
|
|
* 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 <stdlib.h>
|
|
#include <ctype.h>
|
|
|
|
#include "lib/util_base.h"
|
|
#include "lib/util_file.h"
|
|
#include "lib/util_fmt.h"
|
|
#include "lib/util_opt.h"
|
|
#include "lib/util_path.h"
|
|
#include "lib/util_prg.h"
|
|
#include "lib/util_rec.h"
|
|
#include "lib/util_scandir.h"
|
|
#include "lib/zt_common.h"
|
|
|
|
#include "lschp_cli.h"
|
|
|
|
#define CHP_SHARED "shared"
|
|
#define CHP_STATE "vary"
|
|
#define CHP_PCHID "pchid"
|
|
#define CHP_TYPE "type"
|
|
#define CHP_CFG "cfg"
|
|
#define CHP_CMG "cmg"
|
|
#define CHP_ID "chpid"
|
|
|
|
static enum util_fmt_t fmt;
|
|
static bool fmt_specified;
|
|
|
|
/*
|
|
* Program configuration
|
|
*/
|
|
static const struct util_prg prg = {
|
|
.desc = "List information about available channel-paths.",
|
|
.copyright_vec = {
|
|
{
|
|
.owner = "IBM Corp.",
|
|
.pub_first = 2016,
|
|
.pub_last = 2019,
|
|
},
|
|
UTIL_PRG_COPYRIGHT_END
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Set the "--format" option
|
|
*
|
|
* @param[in] str Format for structured output
|
|
*/
|
|
static void fmt_set(const char *str)
|
|
{
|
|
if (!util_fmt_name_to_type(str, &fmt)) {
|
|
errx(EXIT_FAILURE, "Unknown format '%s', supported formats: %s\n", str,
|
|
FMT_TYPE_NAMES);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Print buffer structure as formatted
|
|
*
|
|
* @param[in] rec The buffer structure, where results are written to
|
|
*/
|
|
static void fmt_rec_print_formatted(struct util_rec *rec)
|
|
{
|
|
const char *str_val, *key;
|
|
struct util_rec_fld *fld;
|
|
bool is_chid_external;
|
|
int ival = -1;
|
|
|
|
util_fmt_obj_start(FMT_ROW, "entry");
|
|
util_list_iterate(__util_rec_get_list(rec), fld) {
|
|
key = util_rec_fld_get_key(fld);
|
|
str_val = util_rec_get(rec, key);
|
|
if (!strcmp(str_val, "-")) {
|
|
util_fmt_pair(FMT_QUOTE | FMT_INVAL, key, "");
|
|
} else if (!strcmp(key, CHP_PCHID)) {
|
|
is_chid_external = (str_val[0] != '(');
|
|
sscanf(str_val, is_chid_external ? "%x" : "(%x)", &ival);
|
|
util_fmt_pair(FMT_DEFAULT, "chid", "%d", ival);
|
|
util_fmt_pair(FMT_DEFAULT, "chid_external", "%s", is_chid_external ?
|
|
"true" : "false");
|
|
} else if (!strcmp(key, CHP_ID)) {
|
|
util_fmt_pair(FMT_DEFAULT | FMT_QUOTE, key, "%s", str_val);
|
|
} else if (!strcmp(key, CHP_TYPE)) {
|
|
sscanf(str_val, "%x", &ival);
|
|
util_fmt_pair(FMT_DEFAULT, key, "%d", ival);
|
|
} else {
|
|
ival = atoi(str_val);
|
|
util_fmt_pair(FMT_DEFAULT, key, "%d", ival);
|
|
}
|
|
}
|
|
util_fmt_obj_end(); /* entry */
|
|
}
|
|
|
|
static void fmt_init(void)
|
|
{
|
|
unsigned int flags = FMT_HANDLEINT | FMT_KEEPINVAL;
|
|
|
|
if (!fmt_specified)
|
|
return;
|
|
if (fmt == FMT_CSV)
|
|
flags |= FMT_NOMETA | FMT_QUOTEALL;
|
|
else
|
|
flags |= FMT_DEFAULT;
|
|
util_fmt_init(stdout, fmt, flags, 1);
|
|
}
|
|
|
|
static void fmt_end(void)
|
|
{
|
|
if (!fmt_specified)
|
|
return;
|
|
util_fmt_exit();
|
|
}
|
|
|
|
/**
|
|
* Read all attributes of a desired directory
|
|
*
|
|
* Which attributes are to be investigated is defined in the
|
|
* fallowing function body.
|
|
*
|
|
* @param[in] dir Path of the desired directory
|
|
* @param[in] css_id ID for device identification
|
|
* @param[in] rec The buffer structure, where results are written to
|
|
* @param[in] chp If set: CHPID to filter by
|
|
*/
|
|
static void print_chpid(const char *chp_dir, unsigned int css_id,
|
|
struct util_rec *rec, char *chp)
|
|
{
|
|
unsigned int css_id_tmp, chp_id;
|
|
bool chid_external;
|
|
char *path, buf[8];
|
|
|
|
/* Get CHPID ID */
|
|
if (sscanf(chp_dir, "chp%x.%x", &css_id_tmp, &chp_id) != 2)
|
|
err(EXIT_FAILURE, "Invalid directory: %s", chp_dir);
|
|
if (css_id != css_id_tmp)
|
|
errx(EXIT_FAILURE, "Inconsistent css ids");
|
|
path = util_path_sysfs("devices/css%x/%s", css_id, chp_dir);
|
|
|
|
/* chpid */
|
|
util_rec_set(rec, CHP_ID, "%x.%02x", css_id, chp_id);
|
|
|
|
/* vary */
|
|
util_file_read_line(buf, sizeof(buf), "%s/status", path);
|
|
if (strcmp(buf, "online") == 0)
|
|
util_rec_set(rec, CHP_STATE, "1");
|
|
else if (strcmp(buf, "offline") == 0)
|
|
util_rec_set(rec, CHP_STATE, "0");
|
|
else
|
|
util_rec_set(rec, CHP_STATE, "-");
|
|
|
|
/* configure */
|
|
if (util_file_read_line(buf, sizeof(buf), "%s/configure", path))
|
|
util_rec_set(rec, CHP_CFG, "-");
|
|
else
|
|
util_rec_set(rec, CHP_CFG, buf);
|
|
|
|
/* type */
|
|
if (util_file_read_line(buf, sizeof(buf), "%s/type", path))
|
|
util_rec_set(rec, CHP_TYPE, "%s", "-");
|
|
else
|
|
util_rec_set(rec, CHP_TYPE, "%02lx", strtoul(buf, NULL, 16));
|
|
|
|
/* cmg */
|
|
util_file_read_line(buf, sizeof(buf), "%s/cmg", path);
|
|
if ((strcmp(buf, "unknown") == 0) || (strlen(buf) == 0))
|
|
util_rec_set(rec, CHP_CMG, "%s", "-");
|
|
else
|
|
util_rec_set(rec, CHP_CMG, "%-3lx", strtoul(buf, NULL, 0));
|
|
|
|
/* shared */
|
|
util_file_read_line(buf, sizeof(buf), "%s/shared", path);
|
|
if ((strcmp(buf, "unknown") == 0) || (strlen(buf) == 0))
|
|
util_rec_set(rec, CHP_SHARED, "%s", "-");
|
|
else
|
|
util_rec_set(rec, CHP_SHARED, "%-6lx", strtoul(buf, NULL, 0));
|
|
|
|
/* chid */
|
|
util_file_read_line(buf, sizeof(buf), "%s/chid_external", path);
|
|
if (strcmp(buf, "1") == 0)
|
|
chid_external = true;
|
|
else
|
|
chid_external = false;
|
|
util_file_read_line(buf, sizeof(buf), "%s/chid", path);
|
|
if (strlen(buf) != 0) {
|
|
if (chid_external)
|
|
util_rec_set(rec, CHP_PCHID, " %4s ", buf);
|
|
else
|
|
util_rec_set(rec, CHP_PCHID, "(%4s)", buf);
|
|
} else {
|
|
util_rec_set(rec, CHP_PCHID, "%s", "-");
|
|
}
|
|
if (!strlen(chp) || strcmp(util_rec_get(rec, CHP_ID), chp) == 0) {
|
|
if (fmt_specified)
|
|
fmt_rec_print_formatted(rec);
|
|
else
|
|
util_rec_print(rec);
|
|
}
|
|
free(path);
|
|
}
|
|
|
|
/**
|
|
* Compare two dirents numerically (chp0.ff <-> chp0.fe)
|
|
*
|
|
* @param[in] de1 First directory entry
|
|
* @param[in] de2 Second directory entry
|
|
* @retval -1 de1 is lower
|
|
* 0 de1 equals de2
|
|
* 1 de2 is lower
|
|
*/
|
|
static int chpsort(const struct dirent **de1, const struct dirent **de2)
|
|
{
|
|
unsigned long val1 = strtoul(&(*de1)->d_name[5], NULL, 16);
|
|
unsigned long val2 = strtoul(&(*de2)->d_name[5], NULL, 16);
|
|
|
|
if (val1 < val2)
|
|
return -1;
|
|
if (val1 == val2)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Show all channel paths for a given directory in sysfs
|
|
*
|
|
* @param[in] css_dir The desired directory
|
|
* @param[in] rec The buffer structure, where results are written to
|
|
* @param[in] chp If set: CHPID to filter by
|
|
*/
|
|
static void print_css(const char *css_dir, struct util_rec *rec, char *chp)
|
|
{
|
|
struct dirent **de_vec;
|
|
unsigned int css_id;
|
|
int i, count;
|
|
char *path;
|
|
|
|
if (sscanf(css_dir, "css%x", &css_id) != 1)
|
|
err(EXIT_FAILURE, "Invalid directory: %s", css_dir);
|
|
|
|
path = util_path_sysfs("devices/css%d", css_id);
|
|
count = util_scandir(&de_vec, chpsort, path, "chp%x.*", css_id);
|
|
for (i = 0; i < count; i++)
|
|
print_chpid(de_vec[i]->d_name, css_id, rec, chp);
|
|
util_scandir_free(de_vec, count);
|
|
free(path);
|
|
}
|
|
|
|
/*
|
|
* Print chpid table
|
|
*
|
|
* @param[in] chp If set: CHPID to filter by
|
|
*/
|
|
static void cmd_lschp(char *chp)
|
|
{
|
|
struct dirent **de_vec;
|
|
struct util_rec *rec;
|
|
int i, count;
|
|
char *path;
|
|
|
|
fmt_init();
|
|
rec = util_rec_new_wide("=");
|
|
util_rec_def(rec, CHP_ID, UTIL_REC_ALIGN_LEFT, 6, "CHPID");
|
|
util_rec_def(rec, CHP_STATE, UTIL_REC_ALIGN_LEFT, 5, "Vary");
|
|
util_rec_def(rec, CHP_CFG, UTIL_REC_ALIGN_LEFT, 5, "Cfg.");
|
|
util_rec_def(rec, CHP_TYPE, UTIL_REC_ALIGN_LEFT, 5, "Type");
|
|
util_rec_def(rec, CHP_CMG, UTIL_REC_ALIGN_LEFT, 4, "Cmg");
|
|
util_rec_def(rec, CHP_SHARED, UTIL_REC_ALIGN_LEFT, 6, "Shared");
|
|
util_rec_def(rec, CHP_PCHID, UTIL_REC_ALIGN_LEFT, 6, " PCHID");
|
|
|
|
if (!fmt_specified)
|
|
util_rec_print_hdr(rec);
|
|
/*
|
|
* Iterate over each "/sys/devices/css.*"
|
|
*/
|
|
path = util_path_sysfs("devices");
|
|
count = util_scandir(&de_vec, alphasort, path, "^css[[:xdigit:]]{1,2}$");
|
|
if (fmt_specified)
|
|
util_fmt_obj_start(FMT_LIST, "channel_paths");
|
|
for (i = 0; i < count; i++)
|
|
print_css(de_vec[i]->d_name, rec, chp);
|
|
if (fmt_specified)
|
|
util_fmt_obj_end(); /* channel_paths */
|
|
util_ptr_vec_free((void **) de_vec, count);
|
|
free(path);
|
|
util_rec_free(rec);
|
|
fmt_end();
|
|
}
|
|
|
|
#define CHP_LEN 4
|
|
/*
|
|
* Parse options and execute the command
|
|
*/
|
|
int main(int argc, char *argv[])
|
|
{
|
|
char chp[CHP_LEN + 1] = "";
|
|
int c;
|
|
|
|
util_prg_init(&prg);
|
|
util_opt_init(lschp_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 OPT_FORMAT:
|
|
fmt_specified = true;
|
|
fmt_set(argv[optind - 1]);
|
|
break;
|
|
default:
|
|
util_opt_print_parse_error(c, argv);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
if (argc > optind + 1)
|
|
errx(EXIT_FAILURE, "Too many arguments specified");
|
|
if (argc == optind + 1) {
|
|
/* we take a single argument only */
|
|
switch (strlen(argv[optind])) {
|
|
case 1:
|
|
sprintf(chp, "0.0%s", argv[optind]);
|
|
break;
|
|
case 2:
|
|
sprintf(chp, "0.%s", argv[optind]);
|
|
break;
|
|
case CHP_LEN:
|
|
strcpy(chp, argv[optind]);
|
|
break;
|
|
default:
|
|
errx(EXIT_FAILURE, "%s is not a valid channel-path ID",
|
|
argv[optind]);
|
|
}
|
|
if (!isdigit(chp[0]) || chp[1] != '.' || !isxdigit(chp[2]) ||
|
|
!isxdigit(chp[3]))
|
|
errx(EXIT_FAILURE, "%s is not a valid channel-path ID",
|
|
chp);
|
|
}
|
|
cmd_lschp(chp);
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|