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. 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>
649 lines
18 KiB
C
649 lines
18 KiB
C
/**
|
|
* lsqeth - command of s390-tools
|
|
*
|
|
* List qeth-based network devices with their attributes
|
|
*
|
|
* Copyright IBM Corp. 2004, 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 <linux/ethtool.h>
|
|
#include <linux/sockios.h>
|
|
#include <net/if.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <argz.h>
|
|
#include <dirent.h>
|
|
#include <fcntl.h>
|
|
#include <libgen.h>
|
|
#include <limits.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "lib/util_base.h"
|
|
#include "lib/util_file.h"
|
|
#include "lib/util_opt.h"
|
|
#include "lib/util_libc.h"
|
|
#include "lib/util_panic.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 "lib/vmcp.h"
|
|
|
|
#include "lsqeth_cli.h"
|
|
#include "misc.h"
|
|
|
|
#define ID_FORMAT "^[[:xdigit:]]{1,2}[.][[:xdigit:]][.][[:xdigit:]]{4}$"
|
|
#define MAX_ID_LENGTH 10
|
|
#define PAGE_SIZE 4096
|
|
|
|
/*
|
|
* Private data
|
|
*/
|
|
static struct lsqeth_cmd_flags {
|
|
bool proc_format;
|
|
} cmd;
|
|
|
|
/*
|
|
* Program configuration
|
|
*/
|
|
const struct util_prg prg = {
|
|
.desc = "List all qeth-based network devices with their corresponding settings.\n"
|
|
"\nINTERFACE"
|
|
"\n List only attributes of specified interface",
|
|
.args = "[INTERFACE]",
|
|
.copyright_vec = {
|
|
{
|
|
.owner = "IBM Corp.",
|
|
.pub_first = 2017,
|
|
.pub_last = 2017,
|
|
},
|
|
UTIL_PRG_COPYRIGHT_END
|
|
}
|
|
};
|
|
|
|
/*
|
|
* Exchange content of field value via simple translation map
|
|
*
|
|
* If the attribute is required for translation (attribute name is present in
|
|
* tr_attr_list[]) then the value of the attribute (if matches the dictionary
|
|
* translation key) is exchanged by the target value of the translation
|
|
* dictionary (sys_to_proc_dict[][2]).
|
|
*
|
|
*/
|
|
static void tr_to_proc_format(char *val, const char *attr_name)
|
|
{
|
|
static const char *tr_attr_list[] = {
|
|
"checksumming",
|
|
"priority_queueing",
|
|
"route4",
|
|
"route6"
|
|
};
|
|
static const char *sys_to_proc_dict[][2] = {
|
|
{"sw checksumming", "sw"},
|
|
{"hw checksumming", "hw"},
|
|
{"no checksumming", "no"},
|
|
{"always queue 0", "always_q_0"},
|
|
{"always queue 1", "always_q_1"},
|
|
{"always queue 2", "always_q_2"},
|
|
{"always queue 3", "always_q_3"},
|
|
{"by precedence", "by_prec."},
|
|
{"by type of service", "by_ToS"},
|
|
{"by skb-priority", "by_skb"},
|
|
{"by VLAN headers", "by_vlan"},
|
|
{"primary router", "pri"},
|
|
{"secondary router", "sec"},
|
|
{"primary connector+", "p+c"},
|
|
{"primary connector", "p.c"},
|
|
{"secondary connector+", "s+c"},
|
|
{"secondary connector", "s.c"},
|
|
{"multicast router+", "mc+"},
|
|
{"multicast router", "mc"},
|
|
{NULL, NULL}
|
|
};
|
|
unsigned int i = 0;
|
|
|
|
if (misc_str_in_list(attr_name, tr_attr_list,
|
|
ARRAY_SIZE(tr_attr_list))) {
|
|
while (sys_to_proc_dict[i][0]) {
|
|
if (strcmp(val, sys_to_proc_dict[i][0]) == 0) {
|
|
strcpy(val, sys_to_proc_dict[i][1]);
|
|
return;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Read IPv4 and IPv6 addresses from related sysfs entries for ipa/parp/vipa and
|
|
* store each value at the end of *argz array. Return the number of entries stored.
|
|
*/
|
|
static int get_qethconf(const char *name, const char *if_name, char **argz,
|
|
size_t *argz_len)
|
|
{
|
|
char *path;
|
|
int count;
|
|
|
|
path = util_path_sysfs("class/net/%s/device/%s/add4", if_name, name);
|
|
count = misc_argz_add_from_file(argz, argz_len, path);
|
|
free(path);
|
|
path = util_path_sysfs("class/net/%s/device/%s/add6", if_name, name);
|
|
count += misc_argz_add_from_file(argz, argz_len, path);
|
|
free(path);
|
|
return count;
|
|
}
|
|
|
|
/*
|
|
* Get checksumming information for specified interface
|
|
*/
|
|
static void ethtool_checksumming(char *buf, const char *if_name)
|
|
{
|
|
struct ethtool_value val;
|
|
struct ifreq ifr;
|
|
int fd, rc;
|
|
|
|
fd = socket(AF_INET, SOCK_DGRAM, 0);
|
|
if (fd < 0)
|
|
errx(EXIT_FAILURE, "Internal error: cannot get SOCK_DGRAM socket");
|
|
util_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ);
|
|
val.cmd = ETHTOOL_GRXCSUM;
|
|
ifr.ifr_data = (void *)&val;
|
|
rc = ioctl(fd, SIOCETHTOOL, &ifr);
|
|
close(fd);
|
|
if (!rc && val.data)
|
|
strcpy(buf, "hw");
|
|
else
|
|
strcpy(buf, "sw");
|
|
}
|
|
|
|
/*
|
|
* Call cp command and return the resulting lines as argz vector
|
|
*
|
|
* @param[in,out] argz argz vector allocated by argz_create
|
|
* so the caller should free the allocated memory
|
|
* @param[in,out] argz_len argz length
|
|
* @param[in] fmt Format string specifying the cp command
|
|
*/
|
|
static void exec_cp(char **argz, size_t *argz_len, const char *fmt, ...)
|
|
{
|
|
struct vmcp_parm cp;
|
|
char *command;
|
|
va_list ap;
|
|
int rc;
|
|
|
|
UTIL_VASPRINTF(&command, fmt, ap);
|
|
cp.cpcmd = command;
|
|
cp.do_upper = false;
|
|
cp.buffer_size = VMCP_DEFAULT_BUFSZ;
|
|
rc = vmcp(&cp);
|
|
free(command);
|
|
|
|
switch (rc) {
|
|
case VMCP_ERR_OPEN:
|
|
errx(EXIT_FAILURE, "Could not open device %s", VMCP_DEVICE_NODE);
|
|
case VMCP_ERR_SETBUF:
|
|
errx(EXIT_FAILURE, "Could not set buffer size");
|
|
case VMCP_ERR_GETCODE:
|
|
errx(EXIT_FAILURE, "Could not query return code");
|
|
case VMCP_ERR_GETSIZE:
|
|
errx(EXIT_FAILURE, "Could not query response size");
|
|
case VMCP_ERR_WRITE:
|
|
errx(EXIT_FAILURE, "Could not issue CP command");
|
|
case VMCP_ERR_READ:
|
|
errx(EXIT_FAILURE, "Could not read CP response");
|
|
case VMCP_ERR_TOOSMALL:
|
|
break; /* Truncated CP response treated as good */
|
|
}
|
|
rc = argz_create_sep(cp.response, '\n', argz, argz_len);
|
|
if (rc)
|
|
errx(EXIT_FAILURE, "Memory allocation error for CP response processing");
|
|
free(cp.response);
|
|
}
|
|
|
|
/*
|
|
* Return card_type attribute value based on 'vmcp q v nic' output
|
|
*/
|
|
static char *print_LAN(const char *cdev0)
|
|
{
|
|
char *tok_vec[5], *tk, *result, *retstr, *devno, *entry;
|
|
char *tail = "not coupled";
|
|
size_t argz_len = 0;
|
|
char *argz = NULL;
|
|
unsigned int i;
|
|
|
|
devno = strrchr(cdev0, '.') + 1;
|
|
exec_cp(&argz, &argz_len, "QUERY VIRTUAL NIC %s", devno);
|
|
/* Process CP command output in argz format */
|
|
if (argz_count(argz, argz_len) < 2)
|
|
errx(EXIT_FAILURE, "Internal error: Unexpected result of 'vmcp q v nic' command");
|
|
/* Tokenize second line */
|
|
entry = argz_next(argz, argz_len, argz);
|
|
memset(tok_vec, 0, sizeof(tok_vec));
|
|
tk = strtok(entry, " \t");
|
|
for (i = 0; i < ARRAY_SIZE(tok_vec) && tk; i++) {
|
|
tok_vec[i] = tk;
|
|
tk = strtok(NULL, " \t");
|
|
}
|
|
util_asprintf(&result, "%s %s %s", tok_vec[2], tok_vec[3], tok_vec[4]);
|
|
/* Tokenize first line */
|
|
entry = argz;
|
|
memset(tok_vec, 0, sizeof(tok_vec));
|
|
tk = strtok(entry, " \t");
|
|
for (i = 0; i < ARRAY_SIZE(tok_vec) && tk; i++) {
|
|
tok_vec[i] = tk;
|
|
tk = strtok(NULL, " \t");
|
|
}
|
|
if (strncmp(result, "LAN", 3) == 0) {
|
|
if (strncmp(result + 5, "* Internal", 11) != 0)
|
|
tail = result + 5;
|
|
util_asprintf(&retstr, "GuestLAN: %s (%s %s)",
|
|
tail, tok_vec[2], tok_vec[3]);
|
|
} else {
|
|
util_asprintf(&retstr, "%s (%s %s)", result, tok_vec[2],
|
|
tok_vec[3]);
|
|
}
|
|
free(result);
|
|
free(argz);
|
|
return retstr;
|
|
}
|
|
|
|
/*
|
|
* Update card_type field for Virtual NIC devices
|
|
*/
|
|
static void update_card_type(struct util_rec *rec)
|
|
{
|
|
const char *card_type, *cdev0;
|
|
char *upd_card_type;
|
|
|
|
if (!util_path_is_readable("/dev/vmcp"))
|
|
return;
|
|
card_type = util_rec_get(rec, "card_type");
|
|
if (!card_type)
|
|
return;
|
|
if (strncmp(card_type, "GuestLAN", 8) == 0 ||
|
|
strncmp(card_type, "Virt.NIC", 8) == 0) {
|
|
cdev0 = util_rec_get(rec, "cdev0");
|
|
if (!cdev0)
|
|
return;
|
|
upd_card_type = print_LAN(cdev0);
|
|
util_rec_set(rec, "card_type", upd_card_type);
|
|
free(upd_card_type);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Process regular sysfs attribute and save it to the record.
|
|
*/
|
|
static void process_sysfs_attribute(struct util_rec *rec, const char *path,
|
|
const char *attr_name, const char *if_name)
|
|
{
|
|
char buf[PAGE_SIZE];
|
|
char *link = NULL;
|
|
|
|
/* Process cdev attributes (for normal output format only) */
|
|
if (strncmp(attr_name, "cdev", 4) == 0 && !cmd.proc_format) {
|
|
link = misc_link_target("%s/%s", path, attr_name);
|
|
/* If no simlink found, cdev* record field will not be set */
|
|
if (link) {
|
|
util_rec_set(rec, attr_name, basename(link));
|
|
free(link);
|
|
}
|
|
}
|
|
/* Process other sysfs attributes */
|
|
if (util_file_read_line(buf, sizeof(buf), "%s/%s",
|
|
path, attr_name) == 0 &&
|
|
strlen(buf) != 0 &&
|
|
strcmp(buf, "n/a") != 0) {
|
|
/* Translate attribute values to proc-format if required */
|
|
if (cmd.proc_format)
|
|
tr_to_proc_format(buf, attr_name);
|
|
/* Hex notation for 'chpid' attribute in proc format */
|
|
if (strcmp(attr_name, "chpid") == 0 &&
|
|
cmd.proc_format)
|
|
util_rec_set(rec, attr_name, "x%s", buf);
|
|
else
|
|
util_rec_set(rec, attr_name, buf);
|
|
} else {
|
|
/* Special case for 'checksumming' and 'route6' */
|
|
if (strcmp(attr_name, "checksumming") == 0) {
|
|
if (cmd.proc_format) {
|
|
ethtool_checksumming(buf, if_name);
|
|
util_rec_set(rec, attr_name, buf);
|
|
}
|
|
} else if (strcmp(attr_name, "route6") == 0 &&
|
|
strcmp(buf, "n/a") == 0) {
|
|
util_rec_set(rec, attr_name, "no");
|
|
} else {
|
|
/* Set 'n/a' default value for proc format*/
|
|
if (cmd.proc_format)
|
|
util_rec_set(rec, attr_name, "n/a");
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set ipa/parp/vipa attributes for normal format output.
|
|
*/
|
|
static void set_ipa_vipa_parp(struct util_rec *rec, const char *attr_name,
|
|
const char *path, const char *if_name)
|
|
{
|
|
size_t argz_len = 0;
|
|
char *argz = NULL;
|
|
|
|
if (strcmp(attr_name, "ipa") == 0) {
|
|
if (!util_path_is_dir("%s/ipa_takeover", path))
|
|
return;
|
|
if (get_qethconf("ipa_takeover", if_name, &argz, &argz_len))
|
|
util_rec_set_argz(rec, attr_name, argz, argz_len);
|
|
} else if (strcmp(attr_name, "parp") == 0) {
|
|
if (!util_path_is_dir("%s/rxip", path))
|
|
return;
|
|
if (get_qethconf("rxip", if_name, &argz, &argz_len))
|
|
util_rec_set_argz(rec, attr_name, argz, argz_len);
|
|
} else {
|
|
if (!util_path_is_dir("%s/vipa", path))
|
|
return;
|
|
if (get_qethconf("vipa", if_name, &argz, &argz_len))
|
|
util_rec_set_argz(rec, attr_name, argz, argz_len);
|
|
}
|
|
free(argz);
|
|
}
|
|
|
|
/*
|
|
* Set devices attribute for proc format output: '<cdev0>/<cdev1>/<cdev2>'
|
|
*/
|
|
static void set_devices_fld(struct util_rec *rec, const char *path)
|
|
{
|
|
char buf[3*MAX_ID_LENGTH + 3] = "";
|
|
char *link;
|
|
int i;
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
link = misc_link_target("%s/cdev%d", path, i);
|
|
/* File name is a link */
|
|
if (i == 2 && link) {
|
|
strcat(buf, basename(link));
|
|
} else {
|
|
if (link)
|
|
strcat(strcat(buf, basename(link)), "/");
|
|
else
|
|
strcat(buf, "/");
|
|
}
|
|
free(link);
|
|
}
|
|
util_rec_set(rec, "devices", buf);
|
|
}
|
|
|
|
/*
|
|
* Check if the attribute should be skipped for layer2 device
|
|
*/
|
|
static bool not_layer2_attr(const char *attr_name)
|
|
{
|
|
/* Layer3 specific attributes */
|
|
static const char *layer3_vec[] = {
|
|
"route4",
|
|
"route6",
|
|
"large_send",
|
|
"fake_ll",
|
|
"fake_broadcast",
|
|
"checksumming",
|
|
"hsuid",
|
|
"sniffer"
|
|
};
|
|
|
|
return misc_str_in_list(attr_name, layer3_vec, ARRAY_SIZE(layer3_vec));
|
|
}
|
|
|
|
/*
|
|
* Collect and print attributes for qeth-based device in specified format
|
|
*/
|
|
static void print_device(struct util_rec *rec, const char *device_id)
|
|
{
|
|
char *path, *path_net, *if_name;
|
|
unsigned long int layer2 = 0;
|
|
struct util_rec_fld *fld;
|
|
const char *attr_name;
|
|
char buf[PAGE_SIZE];
|
|
|
|
path = util_path_sysfs("bus/ccwgroup/drivers/qeth/%s", device_id);
|
|
/* Process if_name attribute */
|
|
if (util_file_read_line(buf, sizeof(buf), "%s/if_name", path) == 0)
|
|
if_name = util_strdup(buf);
|
|
else
|
|
if_name = util_strdup("");
|
|
util_rec_set(rec, "if_name", if_name);
|
|
|
|
/* Read layer2 attribute */
|
|
path_net = util_path_sysfs("class/net");
|
|
util_file_read_ul(&layer2, 10, "%s/%s/device/layer2",
|
|
path_net, if_name);
|
|
free(path_net);
|
|
/* Iterate over each rec field */
|
|
util_rec_iterate(rec, fld) {
|
|
attr_name = util_rec_fld_get_key(fld);
|
|
/* Skip layer3 attributes for layer2 device in normal format output */
|
|
if (layer2 == 1 &&
|
|
!cmd.proc_format &&
|
|
not_layer2_attr(attr_name))
|
|
continue;
|
|
/* Skip if_name attribute(already processed) */
|
|
if (strcmp(attr_name, "if_name") == 0)
|
|
continue;
|
|
/* Process 'devices' field for proc format output */
|
|
if (strcmp(attr_name, "devices") == 0 && cmd.proc_format) {
|
|
set_devices_fld(rec, path);
|
|
continue;
|
|
}
|
|
/* Process ipa/parp/vipa attributes */
|
|
if (strcmp(attr_name, "ipa") == 0 ||
|
|
strcmp(attr_name, "vipa") == 0 ||
|
|
strcmp(attr_name, "parp") == 0) {
|
|
set_ipa_vipa_parp(rec, attr_name, path, if_name);
|
|
continue;
|
|
}
|
|
/* Process other sysfs attributes */
|
|
process_sysfs_attribute(rec, path, attr_name, if_name);
|
|
}
|
|
free(if_name);
|
|
free(path);
|
|
/* Print the record */
|
|
if (!cmd.proc_format) {
|
|
/* Check if card_type attribute needs to be modified */
|
|
update_card_type(rec);
|
|
/* Print record header for each device in normal format output */
|
|
util_rec_print_hdr(rec);
|
|
}
|
|
util_rec_print(rec);
|
|
}
|
|
|
|
/*
|
|
* Setup record with the fields needed for output in wide form
|
|
*/
|
|
static void setup_rec_wide(struct util_rec *rec)
|
|
{
|
|
util_rec_def(rec, "devices", UTIL_REC_ALIGN_LEFT, 26, "devices");
|
|
util_rec_def(rec, "chpid", UTIL_REC_ALIGN_LEFT, 5, "CHPID");
|
|
util_rec_def(rec, "if_name", UTIL_REC_ALIGN_LEFT, 16, "interface");
|
|
util_rec_def(rec, "card_type", UTIL_REC_ALIGN_LEFT, 14, "cardtype");
|
|
util_rec_def(rec, "portno", UTIL_REC_ALIGN_LEFT, 4, "port");
|
|
util_rec_def(rec, "checksumming", UTIL_REC_ALIGN_LEFT, 6, "chksum");
|
|
util_rec_def(rec, "priority_queueing", UTIL_REC_ALIGN_LEFT, 10,
|
|
"prio-q'ing");
|
|
util_rec_def(rec, "route4", UTIL_REC_ALIGN_LEFT, 4, "rtr4");
|
|
util_rec_def(rec, "route6", UTIL_REC_ALIGN_LEFT, 4, "rtr6");
|
|
util_rec_def(rec, "layer2", UTIL_REC_ALIGN_LEFT, 5, "lay'2");
|
|
util_rec_def(rec, "buffer_count", UTIL_REC_ALIGN_LEFT, 5, "cnt");
|
|
}
|
|
|
|
/*
|
|
* Setup record for output in long form
|
|
*/
|
|
static void setup_rec_long(struct util_rec *rec)
|
|
{
|
|
util_rec_def(rec, "if_name", UTIL_REC_ALIGN_LEFT, 0, "Device name");
|
|
util_rec_def(rec, "card_type", UTIL_REC_ALIGN_LEFT, 0, "card_type");
|
|
util_rec_def(rec, "cdev0", UTIL_REC_ALIGN_LEFT, 0, "cdev0");
|
|
util_rec_def(rec, "cdev1", UTIL_REC_ALIGN_LEFT, 0, "cdev1");
|
|
util_rec_def(rec, "cdev2", UTIL_REC_ALIGN_LEFT, 0, "cdev2");
|
|
util_rec_def(rec, "chpid", UTIL_REC_ALIGN_LEFT, 0, "chpid");
|
|
util_rec_def(rec, "online", UTIL_REC_ALIGN_LEFT, 0, "online");
|
|
util_rec_def(rec, "portname", UTIL_REC_ALIGN_LEFT, 0, "portname");
|
|
util_rec_def(rec, "portno", UTIL_REC_ALIGN_LEFT, 0, "portno");
|
|
util_rec_def(rec, "route4", UTIL_REC_ALIGN_LEFT, 0, "route4");
|
|
util_rec_def(rec, "route6", UTIL_REC_ALIGN_LEFT, 0, "route6");
|
|
util_rec_def(rec, "checksumming", UTIL_REC_ALIGN_LEFT, 0,
|
|
"checksumming");
|
|
util_rec_def(rec, "state", UTIL_REC_ALIGN_LEFT, 0, "state");
|
|
util_rec_def(rec, "priority_queueing", UTIL_REC_ALIGN_LEFT, 0,
|
|
"priority_queueing");
|
|
util_rec_def(rec, "detach_state", UTIL_REC_ALIGN_LEFT, 0,
|
|
"detach_state");
|
|
util_rec_def(rec, "fake_ll", UTIL_REC_ALIGN_LEFT, 0, "fake_ll");
|
|
util_rec_def(rec, "fake_broadcast", UTIL_REC_ALIGN_LEFT, 0,
|
|
"fake_broadcast");
|
|
util_rec_def(rec, "buffer_count", UTIL_REC_ALIGN_LEFT, 0,
|
|
"buffer_count");
|
|
util_rec_def(rec, "add_hhlen", UTIL_REC_ALIGN_LEFT, 0, "add_hhlen");
|
|
util_rec_def(rec, "layer2", UTIL_REC_ALIGN_LEFT, 0, "layer2");
|
|
util_rec_def(rec, "large_send", UTIL_REC_ALIGN_LEFT, 0, "large_send");
|
|
util_rec_def(rec, "isolation", UTIL_REC_ALIGN_LEFT, 0, "isolation");
|
|
util_rec_def(rec, "hsuid", UTIL_REC_ALIGN_LEFT, 0, "hsuid");
|
|
util_rec_def(rec, "sniffer", UTIL_REC_ALIGN_LEFT, 0, "sniffer");
|
|
util_rec_def(rec, "bridge_role", UTIL_REC_ALIGN_LEFT, 0,
|
|
"bridge_role");
|
|
util_rec_def(rec, "bridge_state", UTIL_REC_ALIGN_LEFT, 0,
|
|
"bridge_state");
|
|
util_rec_def(rec, "bridge_hostnotify", UTIL_REC_ALIGN_LEFT, 0,
|
|
"bridge_hostnotify");
|
|
util_rec_def(rec, "bridge_reflect_promisc", UTIL_REC_ALIGN_LEFT, 0,
|
|
"bridge_reflect_promisc");
|
|
util_rec_def(rec, "switch_attrs", UTIL_REC_ALIGN_LEFT, 0,
|
|
"switch_attrs");
|
|
util_rec_def(rec, "ipa", UTIL_REC_ALIGN_LEFT, 0, "ipa");
|
|
util_rec_def(rec, "vipa", UTIL_REC_ALIGN_LEFT, 0, "vipa");
|
|
util_rec_def(rec, "parp", UTIL_REC_ALIGN_LEFT, 0, "parp");
|
|
util_rec_def(rec, "vnicc/bridge_invisible", UTIL_REC_ALIGN_LEFT, 0,
|
|
"vnicc/bridge_invisible");
|
|
util_rec_def(rec, "vnicc/flooding", UTIL_REC_ALIGN_LEFT, 0,
|
|
"vnicc/flooding");
|
|
util_rec_def(rec, "vnicc/learning", UTIL_REC_ALIGN_LEFT, 0,
|
|
"vnicc/learning");
|
|
util_rec_def(rec, "vnicc/learning_timeout", UTIL_REC_ALIGN_LEFT, 0,
|
|
"vnicc/learning_timeout");
|
|
util_rec_def(rec, "vnicc/mcast_flooding", UTIL_REC_ALIGN_LEFT, 0,
|
|
"vnicc/mcast_flooding");
|
|
util_rec_def(rec, "vnicc/rx_bcast", UTIL_REC_ALIGN_LEFT, 0,
|
|
"vnicc/rx_bcast");
|
|
util_rec_def(rec, "vnicc/takeover_learning", UTIL_REC_ALIGN_LEFT, 0,
|
|
"vnicc/takeover_learning");
|
|
util_rec_def(rec, "vnicc/takeover_setvmac", UTIL_REC_ALIGN_LEFT, 0,
|
|
"vnicc/takeover_setvmac");
|
|
}
|
|
|
|
/*
|
|
* Setup the record according to the desired output format
|
|
*/
|
|
static struct util_rec *setup_rec()
|
|
{
|
|
struct util_rec *rec;
|
|
|
|
if (cmd.proc_format) {
|
|
rec = util_rec_new_wide("-");
|
|
setup_rec_wide(rec);
|
|
} else {
|
|
rec = util_rec_new_long("-", ":", "if_name", 32, 40);
|
|
setup_rec_long(rec);
|
|
}
|
|
return rec;
|
|
}
|
|
|
|
/*
|
|
* Entry point
|
|
*/
|
|
int main(int argc, char *argv[])
|
|
{
|
|
char device[MAX_ID_LENGTH];
|
|
struct dirent **de_vec;
|
|
struct util_rec *rec;
|
|
int i, c = 0, count;
|
|
char *path, *link;
|
|
|
|
util_prg_init(&prg);
|
|
util_opt_init(opt_vec, NULL);
|
|
|
|
while (c != -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 'p':
|
|
cmd.proc_format = true;
|
|
continue;
|
|
default:
|
|
util_opt_print_parse_error(c, argv);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
/* Error if more than 1 argument specified */
|
|
if (argc > optind + 1) {
|
|
warnx("Too many arguments");
|
|
util_prg_print_parse_error();
|
|
return EXIT_FAILURE;
|
|
}
|
|
/* Create a proper record structure */
|
|
rec = setup_rec();
|
|
/* For proc output format print record header only once */
|
|
if (cmd.proc_format)
|
|
util_rec_print_hdr(rec);
|
|
if (argc > optind) {
|
|
/* Single argument specified (interface name), get related device_id */
|
|
path = util_path_sysfs("class/net/");
|
|
link = misc_link_target("%s/%s/device/cdev0",
|
|
path, argv[optind]);
|
|
free(path);
|
|
if (link) {
|
|
/* Interface present */
|
|
snprintf(device, sizeof(device), "%s", basename(link));
|
|
free(link);
|
|
print_device(rec, device);
|
|
free(rec);
|
|
} else {
|
|
errx(EXIT_FAILURE, "No such device: %s", argv[optind]);
|
|
}
|
|
} else {
|
|
/* No optional arguments specified, process all available devices */
|
|
path = util_path_sysfs("bus/ccwgroup/drivers/qeth/");
|
|
count = util_scandir(&de_vec, alphasort, path, "%s",
|
|
ID_FORMAT);
|
|
free(path);
|
|
for (i = 0; i < count; i++) {
|
|
/* Check if a symbolic link */
|
|
if (de_vec[i]->d_type != DT_LNK)
|
|
continue;
|
|
if (i > 0)
|
|
rec = setup_rec();
|
|
print_device(rec, de_vec[i]->d_name);
|
|
free(rec);
|
|
}
|
|
util_scandir_free(de_vec, count);
|
|
}
|
|
return EXIT_SUCCESS;
|
|
}
|