mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
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>
This commit is contained in:
13
zdev/Makefile
Normal file
13
zdev/Makefile
Normal file
@@ -0,0 +1,13 @@
|
||||
# Common definitions
|
||||
include ../common.mak
|
||||
|
||||
all:
|
||||
$(MAKE) -C src
|
||||
|
||||
install: all
|
||||
$(MAKE) -C src install
|
||||
$(MAKE) -C man install
|
||||
$(MAKE) -C dracut install
|
||||
|
||||
clean:
|
||||
$(MAKE) -C src clean
|
||||
56
zdev/dracut/95zdev/module-setup.sh
Normal file
56
zdev/dracut/95zdev/module-setup.sh
Normal file
@@ -0,0 +1,56 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#
|
||||
# 95zdev/module_setup.sh
|
||||
# This module installs configuration files (udev rules and modprobe.conf
|
||||
# files) required to enable the root device on s390. It will only work when
|
||||
# the root device was configured using the chzdev tool.
|
||||
#
|
||||
|
||||
check() {
|
||||
local _arch=$(uname -m)
|
||||
|
||||
# Ensure that we're running on s390
|
||||
[ "$_arch" = "s390" -o "$_arch" = "s390x" ] || return 1
|
||||
|
||||
# Ensure that required tools are available
|
||||
require_binaries chzdev lszdev || return 1
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
depends() {
|
||||
return 0
|
||||
}
|
||||
|
||||
installkernel() {
|
||||
local _modules=$(lszdev --by-path / --columns MODULES --no-headings)
|
||||
|
||||
[ ! -z "$_modules" ] && instmods $_modules
|
||||
}
|
||||
|
||||
install() {
|
||||
local _tempfile=$(mktemp dracut-zdev.XXXX)
|
||||
|
||||
if chzdev --export - --persistent --by-path / >/dev/null 2>&1 ; then
|
||||
# Use persistent configuration
|
||||
chzdev --export "$_tempfile" --persistent --by-path / --quiet --type
|
||||
else
|
||||
# Use active configuration
|
||||
chzdev --export "$_tempfile" --active --by-path / --quiet --type
|
||||
sed -i -e 's/active/persistent/g' "$_tempfile"
|
||||
fi
|
||||
|
||||
# Apply via --import to prevent other devices from being configured
|
||||
chzdev --import "$_tempfile" --persistent --base "/etc=$initdir/etc" \
|
||||
--yes --quiet --no-root-update --force >/dev/null
|
||||
|
||||
rm -f "$_tempfile"
|
||||
|
||||
return 0
|
||||
}
|
||||
21
zdev/dracut/Makefile
Normal file
21
zdev/dracut/Makefile
Normal file
@@ -0,0 +1,21 @@
|
||||
# Common definitions
|
||||
include ../../common.mak
|
||||
|
||||
MODDIR := /usr/lib/dracut/modules.d/
|
||||
ZDEVDIR := 95zdev
|
||||
|
||||
# HAVE_DRACUT
|
||||
#
|
||||
# This install time parameter determines whether the zdev dracut module is
|
||||
# installed (HAVE_DRACUT=1) or not (default). When installed, the module
|
||||
# performs the following functions when dracut is run:
|
||||
#
|
||||
# - copy the persistent root device configuration to the initial ram disk
|
||||
#
|
||||
ifeq ($(HAVE_DRACUT),1)
|
||||
install:
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(MODDIR)
|
||||
$(INSTALL) -m 755 -d $(DESTDIR)$(MODDIR)/$(ZDEVDIR)
|
||||
$(INSTALL) -m 755 $(ZDEVDIR)/module-setup.sh \
|
||||
$(DESTDIR)$(MODDIR)/$(ZDEVDIR)/
|
||||
endif
|
||||
238
zdev/include/attrib.h
Normal file
238
zdev/include/attrib.h
Normal file
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef ATTRIB_H
|
||||
#define ATTRIB_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "misc.h"
|
||||
|
||||
struct setting;
|
||||
|
||||
/**
|
||||
* enum val_type_t - Enumeration of acceptable value definition types
|
||||
* @VAL_NUM: A single integer number
|
||||
* @VAL_NUM_GE: Any integer number with a lower limit
|
||||
* @VAL_RANGE: A range of integer numbers
|
||||
* @VAL_STRING: A single text string
|
||||
* @VAL_FUNC: A function that checks the input
|
||||
* @VAL_NONE: Value to indicate the end of a struct value_accept list
|
||||
*/
|
||||
typedef enum {
|
||||
VAL_NONE = 0,
|
||||
VAL_NUM,
|
||||
VAL_NUM_GE,
|
||||
VAL_RANGE,
|
||||
VAL_STRING,
|
||||
VAL_FUNC,
|
||||
/* Array delimiter. */
|
||||
} val_type_t;
|
||||
|
||||
/**
|
||||
* struct notation - Define acceptable notations for a number
|
||||
* dec: Decimal numbers are acceptable
|
||||
* hex: Hexadecimal numbers are acceptable
|
||||
* oct: Octal numbers are acceptable
|
||||
*/
|
||||
struct notation {
|
||||
unsigned int dec:1;
|
||||
unsigned int hex:1;
|
||||
unsigned int oct:1;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct val_number - Define parameters for an acceptable integer
|
||||
* val: Acceptable value
|
||||
* notation: Acceptable notations in which the number can be presented
|
||||
*/
|
||||
struct val_number {
|
||||
long long val;
|
||||
struct notation notation;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct val_range - Define parameters for acceptable integers within a range
|
||||
* from: Minimum acceptable value
|
||||
* to: Maximum acceptable value
|
||||
* notation: Acceptable notations in which the number can be presented
|
||||
*/
|
||||
struct val_range {
|
||||
long long from;
|
||||
long long to;
|
||||
struct notation notation;
|
||||
};
|
||||
|
||||
struct attrib;
|
||||
|
||||
/**
|
||||
* union accept_def_content - Content of an attribute value definition
|
||||
* @number: Define an acceptable integer or lower limit (VAL_NUM+VAL_NUM_GE)
|
||||
* @range: Define a range of acceptable integers (VAL_RANGE)
|
||||
* @string: Define an acceptable string (VAL_STRING)
|
||||
* @func: Define a function that checks for acceptable values (VAL_FUNC)
|
||||
*/
|
||||
union accept_def_content {
|
||||
struct val_number number;
|
||||
struct val_range range;
|
||||
const char *string;
|
||||
int (*func)(struct attrib *, const char *);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct accept_def - Define an acceptable attribute value
|
||||
* @type: Basic value type
|
||||
*/
|
||||
struct accept_def {
|
||||
val_type_t type;
|
||||
union accept_def_content content;
|
||||
};
|
||||
|
||||
/* Helpers to more easily define acceptable attribute values. */
|
||||
|
||||
/* Accept decimal integer value equal to NUM. */
|
||||
#define ACCEPT_NUM(num) \
|
||||
{ \
|
||||
.type = VAL_NUM, \
|
||||
.content = { \
|
||||
.number = { \
|
||||
.val = (num), \
|
||||
.notation = { \
|
||||
.dec = 1 \
|
||||
}, \
|
||||
}, \
|
||||
} \
|
||||
}
|
||||
|
||||
/* Accept decimal integer values greater than or equal to NUM. */
|
||||
#define ACCEPT_NUM_GE(num) \
|
||||
{ \
|
||||
.type = VAL_NUM_GE, \
|
||||
.content = { \
|
||||
.number = { \
|
||||
.val = (num), \
|
||||
.notation = { \
|
||||
.dec = 1 \
|
||||
}, \
|
||||
}, \
|
||||
} \
|
||||
}
|
||||
|
||||
/* Accept decimal integer values from START to END. */
|
||||
#define ACCEPT_RANGE(start, end) \
|
||||
{ \
|
||||
.type = VAL_RANGE, \
|
||||
.content = { \
|
||||
.range = { \
|
||||
.from = (start), \
|
||||
.to = (end), \
|
||||
.notation = { \
|
||||
.dec = 1 \
|
||||
}, \
|
||||
}, \
|
||||
} \
|
||||
}
|
||||
|
||||
/* Accept string value STR. */
|
||||
#define ACCEPT_STR(str) \
|
||||
{ \
|
||||
.type = VAL_STRING, \
|
||||
.content = { \
|
||||
.string = (str) \
|
||||
} \
|
||||
}
|
||||
|
||||
/* Start a list of ACCEPT_* macros and terminate with VAL_NONE element. */
|
||||
#define ACCEPT_ARRAY(...) \
|
||||
((struct accept_def[]) { __VA_ARGS__, { .type = VAL_NONE } })
|
||||
|
||||
#define ATTRIB_TITLE_LEN 50
|
||||
|
||||
#define ATTRIB_ARRAY(...) \
|
||||
((struct attrib *[]) { __VA_ARGS__ NULL })
|
||||
|
||||
/**
|
||||
* struct value_map - Define a value mapping for an attribute
|
||||
* @from: Value which should be replaced when read in the active config
|
||||
* @to: Target value for mapping
|
||||
*/
|
||||
struct value_map {
|
||||
const char *from;
|
||||
const char *to;
|
||||
};
|
||||
|
||||
#define VALUE_MAP(a, b) { .from = (a), .to = (b) }
|
||||
#define VALUE_MAP_ARRAY(...) \
|
||||
((struct value_map []) { __VA_ARGS__, { .from = NULL, .to = NULL }, })
|
||||
|
||||
/**
|
||||
* struct attrib - Define an attribute
|
||||
* @name: Attribute name
|
||||
* @title: Short, one-line attribute description (max 50 characters)
|
||||
* @desc: Detailed, multi-line attribute description
|
||||
* @multi: This attribute accepts multiple values
|
||||
* @activeonly: This attribute should only be changed in the active config
|
||||
* @unstable: The value read is not the last value written
|
||||
* @writeonly: This attribute cannot be read from
|
||||
* @rewrite: Writing the same value multiple times has side-effects
|
||||
* @mandatory: This attribute cannot be removed from a configured device
|
||||
* @newline: There must be a newline when writing to this attribute
|
||||
* @activerem: This attribute can be removed in the active configuration
|
||||
* @defunset: There is no difference between not set and set to default value
|
||||
* in the persistent configuration
|
||||
* @nounload: (Device type attributes only) This attribute can be set while
|
||||
* the corresponding kernel module remains loaded.
|
||||
* @order: A number indicating the order in which to apply attribute
|
||||
* @order_cmp: A function determining if a setting for this attribute should
|
||||
* be applied before (-1) or after (1) another setting, or
|
||||
* if there are no ordering requirements (0).
|
||||
* @check: Check if settings for this attribute are compatible with
|
||||
* settings of another attribute.
|
||||
* @defval: Default attribue value (optional)
|
||||
* @accept: List of acceptable settings (optional)
|
||||
* @map: List of values read in the active config that should be replaced
|
||||
* @st_data: Subtype specific attribute data (optional)
|
||||
*/
|
||||
struct attrib {
|
||||
const char *name;
|
||||
const char title[ATTRIB_TITLE_LEN + 1];
|
||||
const char *desc;
|
||||
|
||||
/* Flags */
|
||||
unsigned int multi :1;
|
||||
unsigned int activeonly :1;
|
||||
unsigned int unstable :1;
|
||||
unsigned int writeonly :1;
|
||||
unsigned int rewrite :1;
|
||||
unsigned int mandatory :1;
|
||||
unsigned int newline :1;
|
||||
unsigned int activerem :1;
|
||||
unsigned int defunset :1;
|
||||
unsigned int nounload :1;
|
||||
|
||||
/* Optional */
|
||||
int order;
|
||||
int (*order_cmp)(struct setting *, struct setting *);
|
||||
bool (*check)(struct setting *, struct setting *, config_t);
|
||||
const char *defval;
|
||||
struct accept_def *accept;
|
||||
struct value_map *map;
|
||||
void *st_data;
|
||||
};
|
||||
|
||||
bool attrib_check_value(struct attrib *, const char *);
|
||||
void attrib_print_acceptable(struct attrib *, int);
|
||||
bool attrib_match_default(struct attrib *, const char *);
|
||||
struct attrib *attrib_find(struct attrib **, const char *);
|
||||
const char *attrib_map_value(struct attrib *, const char *);
|
||||
bool attrib_match_prefix(const char *, const char *);
|
||||
const char *attrib_rem_prefix(const char *, const char *);
|
||||
|
||||
#endif /* ATTRIB_H */
|
||||
24
zdev/include/blkinfo.h
Normal file
24
zdev/include/blkinfo.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BLKINFO_H
|
||||
#define BLKINFO_H
|
||||
|
||||
struct util_list;
|
||||
struct devnode;
|
||||
|
||||
void blkinfo_exit(void);
|
||||
|
||||
struct util_list *blkinfo_get_ancestor_devnodes(struct devnode *);
|
||||
struct util_list *blkinfo_get_same_uuid_devnodes(struct devnode *);
|
||||
struct devnode *blkinfo_get_devnode_by_path(const char *);
|
||||
void blkinfo_add_mountpoints(struct util_list *);
|
||||
void blkinfo_add_swap_devnodes(struct util_list *);
|
||||
|
||||
#endif /* BLKINFO_H */
|
||||
123
zdev/include/ccw.h
Normal file
123
zdev/include/ccw.h
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef CCW_H
|
||||
#define CCW_H
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "lib/ccw.h"
|
||||
|
||||
#include "devtype.h"
|
||||
#include "exit_code.h"
|
||||
#include "misc.h"
|
||||
#include "setting.h"
|
||||
#include "subtype.h"
|
||||
|
||||
#define CCW_BUS_NAME "ccw"
|
||||
#define CCW_CHPID_NUM 8
|
||||
#define CCW_CHPID_MASK(i) (1 << (CCW_CHPID_NUM - (i) - 1))
|
||||
#define CCW_CHPID_MAX 255
|
||||
|
||||
struct attrib;
|
||||
struct namespace;
|
||||
struct subtype;
|
||||
|
||||
/**
|
||||
* ccw_chpid - Channel-Path ID
|
||||
* @cssid: Channel Subsystem ID
|
||||
* @id: CHPID number
|
||||
*/
|
||||
struct ccw_chpid {
|
||||
uint8_t cssid;
|
||||
uint8_t id;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/**
|
||||
* ccw_devinfo - CCW device information
|
||||
* @devid: CCW device ID
|
||||
* @chpids: CHPID list
|
||||
* @pim: Path-Installed-Mask
|
||||
* @exists: Non-zero if CCW device exists
|
||||
*/
|
||||
struct ccw_devinfo {
|
||||
struct ccw_devid devid;
|
||||
struct ccw_chpid chpids[CCW_CHPID_NUM];
|
||||
unsigned int cutype:16;
|
||||
unsigned int devtype:16;
|
||||
unsigned int cumodel:8;
|
||||
unsigned int devmodel:8;
|
||||
unsigned int pim:8;
|
||||
unsigned int exists:1;
|
||||
unsigned int grouped:1;
|
||||
};
|
||||
|
||||
/**
|
||||
* ccw_subtype_data - CCW subtype specific information
|
||||
* @ccwdrv: The name of the CCW device driver for this subtype
|
||||
* @mod: The name of the main kernel module for this subtype
|
||||
*/
|
||||
struct ccw_subtype_data {
|
||||
const char *ccwdrv;
|
||||
const char *mod;
|
||||
};
|
||||
|
||||
extern struct attrib ccw_attr_online;
|
||||
extern struct attrib ccw_attr_online_force;
|
||||
extern struct attrib ccw_attr_cmb_enable;
|
||||
|
||||
/* CCW device ID namespace. */
|
||||
extern struct namespace ccw_namespace;
|
||||
|
||||
/* CCW device subtype. */
|
||||
extern struct subtype ccw_subtype;
|
||||
|
||||
/* Attribute related. */
|
||||
int ccw_online_only_order_cmp(struct setting *, struct setting *);
|
||||
bool ccw_online_only_check(struct setting *, struct setting *, config_t);
|
||||
int ccw_offline_only_order_cmp(struct setting *, struct setting *);
|
||||
bool ccw_offline_only_check(struct setting *, struct setting *, config_t);
|
||||
|
||||
/* Misc. */
|
||||
void ccw_exit(void);
|
||||
void cio_settle(int);
|
||||
bool ccw_exists(const char *, const char *, const char *);
|
||||
void ccw_get_ids(const char *, const char *, struct util_list *);
|
||||
char *ccw_get_driver(struct ccw_devid *);
|
||||
exit_code_t ccw_unbind_device(struct ccw_devid *);
|
||||
exit_code_t ccw_bind_device(struct ccw_devid *, const char *);
|
||||
|
||||
/* ID handling. */
|
||||
exit_code_t ccw_parse_devid(struct ccw_devid *, const char *, err_t);
|
||||
bool ccw_parse_devid_simple(struct ccw_devid *, const char *);
|
||||
bool ccw_is_id_similar(const char *);
|
||||
char *ccw_normalize_id(const char *);
|
||||
char *ccw_devid_to_str(struct ccw_devid *);
|
||||
int ccw_cmp_devids(struct ccw_devid *, struct ccw_devid *);
|
||||
int ccw_devid_distance(struct ccw_devid *, struct ccw_devid *);
|
||||
bool ccw_devid_in_range(struct ccw_devid *, struct ccw_devid *,
|
||||
struct ccw_devid *);
|
||||
struct ccw_devid *ccw_copy_devid(struct ccw_devid *);
|
||||
|
||||
/* Blacklist handling. */
|
||||
bool ccw_is_blacklist_active(void);
|
||||
bool ccw_is_id_blacklisted(const char *);
|
||||
bool ccw_is_id_range_blacklisted(const char *);
|
||||
void ccw_unblacklist_id(const char *);
|
||||
void ccw_unblacklist_id_range(const char *);
|
||||
exit_code_t ccw_blacklist_persist(void);
|
||||
|
||||
/* CCW device information handling. */
|
||||
void ccw_devinfo_print(struct ccw_devinfo *, int);
|
||||
struct ccw_devinfo *ccw_devinfo_get(struct ccw_devid *, int);
|
||||
int ccw_devinfo_chpids_cmp(struct ccw_devinfo *, struct ccw_devinfo *);
|
||||
int ccw_devinfo_cutype_cmp(struct ccw_devinfo *, struct ccw_devinfo *);
|
||||
int ccw_devinfo_devtype_cmp(struct ccw_devinfo *, struct ccw_devinfo *);
|
||||
|
||||
#endif /* CCW_H */
|
||||
67
zdev/include/ccwgroup.h
Normal file
67
zdev/include/ccwgroup.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef CCWGROUP_H
|
||||
#define CCWGROUP_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "ccw.h"
|
||||
#include "exit_code.h"
|
||||
#include "misc.h"
|
||||
|
||||
#define CCWGROUP_BUS "ccwgroup"
|
||||
#define CCWGROUP_MAX_DEVIDS 3
|
||||
|
||||
struct ns_range_iterator;
|
||||
struct subtype;
|
||||
struct attrib;
|
||||
|
||||
struct ccwgroup_devid {
|
||||
struct ccw_devid devid[CCWGROUP_MAX_DEVIDS];
|
||||
unsigned int num;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/**
|
||||
* ccwgroup_subtype_data - CCWGROUP subtype specific information
|
||||
* @ccwgroupdrv: The name of the CCWGROUP device driver for this subtype
|
||||
* @ccwdrv: The name of the CCW device driver for this subtype
|
||||
* @rootdrv: The name used by the driver in root_device_register.
|
||||
* @mod: The name of the main kernel module for this subtype
|
||||
* @num_devs: Number of CCW devices that are combined to form one device
|
||||
*/
|
||||
struct ccwgroup_subtype_data {
|
||||
const char *ccwgroupdrv;
|
||||
const char *ccwdrv;
|
||||
const char *rootdrv;
|
||||
const char *mod;
|
||||
unsigned int num_devs;
|
||||
};
|
||||
|
||||
extern struct subtype ccwgroup_subtype;
|
||||
|
||||
extern struct attrib ccwgroup_attr_online;
|
||||
|
||||
/* ID handling. */
|
||||
exit_code_t ccwgroup_parse_devid(struct ccwgroup_devid *, const char *, err_t);
|
||||
bool ccwgroup_parse_devid_simple(struct ccwgroup_devid *, const char *);
|
||||
bool ccwgroup_is_id_similar(const char *);
|
||||
char *ccwgroup_devid_to_str(struct ccwgroup_devid *);
|
||||
int ccwgroup_cmp_ids(const char *, const char *);
|
||||
int ccwgroup_cmp_parsed_ids(const void *, const void *);
|
||||
int ccwgroup_qsort_cmp(const void *, const void *);
|
||||
struct ccwgroup_devid *ccwgroup_copy_devid(struct ccwgroup_devid *);
|
||||
char *ccwgroup_get_partial_id(const char *);
|
||||
bool ccwgroup_is_id_in_range(const char *id, const char *range);
|
||||
|
||||
/* Namespace helpers. */
|
||||
void ccwgroup_range_next(struct ns_range_iterator *);
|
||||
bool ccwgroup_compatible_namespace(struct namespace *);
|
||||
|
||||
#endif /* CCWGROUP_H */
|
||||
31
zdev/include/ctc.h
Normal file
31
zdev/include/ctc.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef CTC_H
|
||||
#define CTC_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#define CTC_MOD_NAME "ctcm"
|
||||
#define CTC_CCWGROUPDRV_NAME "ctcm"
|
||||
#define CTC_CCWDRV_NAME "ctcm"
|
||||
#define CTC_ROOTDRV_NAME "ctcm"
|
||||
#define CTC_NUM_DEVS 2
|
||||
|
||||
struct devtype;
|
||||
struct namespace;
|
||||
struct ccw_devid;
|
||||
|
||||
extern struct devtype ctc_devtype;
|
||||
extern struct namespace ctc_namespace;
|
||||
|
||||
void ctc_exit(void);
|
||||
bool ctc_confirm(struct ccw_devid *);
|
||||
|
||||
#endif /* CTC_H */
|
||||
25
zdev/include/ctc_auto.h
Normal file
25
zdev/include/ctc_auto.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef CTC_AUTO_H
|
||||
#define CTC_AUTO_H
|
||||
|
||||
#include "exit_code.h"
|
||||
#include "misc.h"
|
||||
|
||||
struct util_list;
|
||||
struct ccwgroup_devid;
|
||||
struct ccw_devid;
|
||||
|
||||
void ctc_auto_add_ids(struct util_list *);
|
||||
exit_code_t ctc_auto_get_devid(struct ccwgroup_devid *, struct ccw_devid *,
|
||||
err_t);
|
||||
exit_code_t ctc_auto_is_possible(struct ccwgroup_devid *, err_t);
|
||||
|
||||
#endif /* CTC_AUTO_H */
|
||||
17
zdev/include/dasd.h
Normal file
17
zdev/include/dasd.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef DASD_H
|
||||
#define DASD_H
|
||||
|
||||
struct devtype;
|
||||
|
||||
extern struct devtype dasd_devtype;
|
||||
|
||||
#endif /* DASD_H */
|
||||
98
zdev/include/device.h
Normal file
98
zdev/include/device.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef DEVICE_H
|
||||
#define DEVICE_H
|
||||
|
||||
#include "lib/util_list.h"
|
||||
|
||||
#include "exit_code.h"
|
||||
#include "hash.h"
|
||||
#include "misc.h"
|
||||
|
||||
struct subtype;
|
||||
struct setting_list;
|
||||
|
||||
/**
|
||||
* struct device_state - Represent the state of a device in a configuration
|
||||
* @settings: List of attribute settings
|
||||
* @exists: Device exists
|
||||
* @modified: Device state has been modified
|
||||
* @deconfigured: Device has been deconfigured
|
||||
* @definable: Device can be defined
|
||||
* @blacklisted: Device is on the blacklist
|
||||
*/
|
||||
struct device_state {
|
||||
struct setting_list *settings;
|
||||
unsigned int exists:1;
|
||||
unsigned int modified:1;
|
||||
unsigned int deconfigured:1;
|
||||
unsigned int definable:1;
|
||||
unsigned int blacklisted:1;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct device - Represent a device configuration
|
||||
* @subtype: Pointer to device subtype definition
|
||||
* @id: Textual device ID
|
||||
* @devid: Parsed device ID
|
||||
* @node: Node for adding this device to a list
|
||||
* @active: Device state in the active configuration
|
||||
* @persistent: Device state in the persistent configuration
|
||||
* @errors: A strlist of error and warning messages issued for the device
|
||||
* @processed: Device has been processed
|
||||
*/
|
||||
struct device {
|
||||
/* Static data */
|
||||
struct subtype *subtype;
|
||||
char *id;
|
||||
void *devid;
|
||||
|
||||
/* Dynamic data */
|
||||
struct util_list_node node;
|
||||
|
||||
struct device_state active;
|
||||
struct device_state persistent;
|
||||
|
||||
unsigned int processed:1;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct device_list - A list of devices
|
||||
* @hash: Hash for maintaining a list of devices
|
||||
* @modified: Indication of whether this list was modified
|
||||
*/
|
||||
struct device_list {
|
||||
struct hash hash;
|
||||
unsigned int modified:1;
|
||||
};
|
||||
|
||||
struct device *device_new(struct subtype *, const char *);
|
||||
void device_free(struct device *);
|
||||
void device_reset(struct device *, config_t);
|
||||
void device_print(struct device *, int);
|
||||
|
||||
bool device_needs_writing(struct device *, config_t);
|
||||
exit_code_t device_apply_strlist(struct device *, config_t, struct util_list *);
|
||||
exit_code_t device_apply_settings(struct device *, config_t,
|
||||
struct util_list *);
|
||||
void device_add_modules(struct util_list *, struct device *);
|
||||
char *device_read_active_attrib(struct device *, const char *);
|
||||
void device_read_active_settings(struct device *, read_scope_t);
|
||||
exit_code_t device_write_active_settings(struct device *);
|
||||
exit_code_t device_check_settings(struct device *, config_t, err_t);
|
||||
|
||||
struct device_list *device_list_new(struct subtype *);
|
||||
void device_list_free(struct device_list *);
|
||||
void device_list_add(struct device_list *, struct device *);
|
||||
struct device *device_list_find(struct device_list *, const char *,
|
||||
struct device *);
|
||||
void device_list_print(struct device_list *, int);
|
||||
|
||||
#endif /* DEVICE_H */
|
||||
46
zdev/include/devnode.h
Normal file
46
zdev/include/devnode.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef DEVNODE_H
|
||||
#define DEVNODE_H
|
||||
|
||||
#include "misc.h"
|
||||
|
||||
struct util_list;
|
||||
|
||||
typedef enum {
|
||||
BLOCKDEV,
|
||||
CHARDEV,
|
||||
NETDEV,
|
||||
} devnode_t;
|
||||
|
||||
struct devnode {
|
||||
devnode_t type;
|
||||
|
||||
unsigned int major;
|
||||
unsigned int minor;
|
||||
|
||||
char name[];
|
||||
};
|
||||
|
||||
struct devnode *devnode_new(devnode_t, unsigned int, unsigned int,
|
||||
const char *);
|
||||
struct devnode *devnode_copy(struct devnode *);
|
||||
void devnode_print(struct devnode *, int);
|
||||
|
||||
struct devnode *devnode_from_node(const char *, err_t);
|
||||
struct devnode *devnode_from_devfile(const char *, const char *, devnode_t);
|
||||
struct devnode *devnode_from_path(const char *path);
|
||||
|
||||
int devnode_cmp(struct devnode *, struct devnode *);
|
||||
int devnode_add_block_from_sysfs(struct util_list *, const char *);
|
||||
int devnode_add_net_from_sysfs(struct util_list *, const char *);
|
||||
char *devnode_readlink(struct devnode *);
|
||||
|
||||
#endif /* DEVNODE_H */
|
||||
110
zdev/include/devtype.h
Normal file
110
zdev/include/devtype.h
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef DEVTYPE_H
|
||||
#define DEVTYPE_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "exit_code.h"
|
||||
#include "misc.h"
|
||||
|
||||
#define DEVTYPE_TITLE_LEN 60
|
||||
|
||||
struct devtype;
|
||||
struct device;
|
||||
struct devnode;
|
||||
struct namespace;
|
||||
struct selected_dev_node;
|
||||
struct subtype;
|
||||
struct util_list;
|
||||
|
||||
/* NULL-terminated list of device types. */
|
||||
extern struct devtype *devtypes[];
|
||||
|
||||
/* Define a NULL-terminated list of struct subtypes */
|
||||
#define SUBTYPE_ARRAY(...) ((struct subtype *[]) { __VA_ARGS__ NULL })
|
||||
|
||||
/**
|
||||
* struct devtype - Definition of a device type
|
||||
* @name: Short name of this device type
|
||||
* @title: Short description of this device type (max. 60 characters).
|
||||
* devtypes with empty title will not be shown with --list-types.
|
||||
* @devname: Short name for devices of this device type
|
||||
* @modules: (Optional) Array of kernel modules required for this devtype
|
||||
* @subtypes: Array of subtypes
|
||||
* @type_attribs: Array of device type attributes (may point to empty array)
|
||||
* @unknown_type_attribs: Allow specification of unknown device type attributes
|
||||
* @processed: Set if device type has already been processed
|
||||
*
|
||||
* @active_settings: Device type settings from the active configuration
|
||||
* @persistent_settings: Device type settings from the persistent configuration
|
||||
* @active_exists: Devtype configuration exists in active configuration
|
||||
* @persistent_exists: Devtype configuration exists in persistent configuration
|
||||
*
|
||||
* @init: Initialize device type
|
||||
* @exit: Release dynamically allocated resources associated with this type
|
||||
* @read_settings: Retrieve device type settings from specified configuration
|
||||
* @write_settings: Apply device type settings to specified configuration
|
||||
*/
|
||||
struct devtype {
|
||||
/* Static data */
|
||||
const char *name;
|
||||
const char title[DEVTYPE_TITLE_LEN + 1];
|
||||
const char *devname;
|
||||
const char **modules;
|
||||
|
||||
struct subtype **subtypes;
|
||||
struct attrib **type_attribs;
|
||||
unsigned int unknown_type_attribs:1;
|
||||
|
||||
/* Dynamic data */
|
||||
struct setting_list *active_settings;
|
||||
struct setting_list *persistent_settings;
|
||||
unsigned int active_exists:1;
|
||||
unsigned int persistent_exists:1;
|
||||
unsigned int processed:1;
|
||||
|
||||
/* Methods */
|
||||
void (*init)(struct devtype *);
|
||||
void (*exit)(struct devtype *);
|
||||
|
||||
/* Devtype settings. */
|
||||
exit_code_t (*read_settings)(struct devtype *, config_t);
|
||||
exit_code_t (*write_settings)(struct devtype *, config_t);
|
||||
};
|
||||
|
||||
void devtypes_init(void);
|
||||
void devtypes_exit(void);
|
||||
|
||||
void devtype_print(struct devtype *, int);
|
||||
|
||||
struct devtype *devtype_find(const char *);
|
||||
struct attrib *devtype_find_type_attrib(struct devtype *, const char *);
|
||||
struct attrib *devtype_find_dev_attrib(struct devtype *, const char *);
|
||||
|
||||
exit_code_t devtype_apply_settings(struct devtype *, config_t,
|
||||
struct util_list *);
|
||||
exit_code_t devtype_apply_strlist(struct devtype *, config_t,
|
||||
struct util_list *);
|
||||
|
||||
bool devtype_is_id_valid(struct devtype *, const char *);
|
||||
bool devtype_is_id_range_valid(struct devtype *, const char *);
|
||||
bool devtype_needs_writing(struct devtype *, config_t);
|
||||
|
||||
void devtype_add_modules(struct util_list *, struct devtype *, int);
|
||||
bool devtype_is_module_loaded(struct devtype *);
|
||||
int devtype_count_namespaces(struct devtype *);
|
||||
int devtype_count_subtypes(struct devtype *);
|
||||
|
||||
struct namespace *devtype_most_similar_namespace(struct devtype *,
|
||||
struct subtype *,
|
||||
const char *id);
|
||||
|
||||
#endif /* DEVTYPE_H */
|
||||
62
zdev/include/exit_code.h
Normal file
62
zdev/include/exit_code.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef EXIT_CODE_H
|
||||
#define EXIT_CODE_H
|
||||
|
||||
/* Program exit codes. */
|
||||
typedef enum {
|
||||
EXIT_OK = 0, /* Program finished successfully */
|
||||
|
||||
/* Usage related */
|
||||
EXIT_USAGE_ERROR = 1, /* Usage error */
|
||||
EXIT_UNKNOWN_DEVTYPE = 2, /* Unknown device type specified */
|
||||
EXIT_DEVICE_NOT_FOUND = 3, /* Device not found */
|
||||
EXIT_ATTRIB_NOT_FOUND = 4, /* Attribute not found */
|
||||
EXIT_INVALID_DEVTYPE = 5, /* Invalid device type specified */
|
||||
EXIT_INVALID_SETTING = 6, /* Invalid attribute value specified */
|
||||
EXIT_SETTING_NOT_FOUND = 7, /* Setting not found */
|
||||
EXIT_EMPTY_SELECTION = 8, /* Empty selection */
|
||||
EXIT_INVALID_CONFIG = 9, /* Invalid configuration */
|
||||
EXIT_INVALID_ID = 10, /* Invalid device ID specified */
|
||||
EXIT_INCOMPLETE_ID = 11, /* Incomplete device ID specified */
|
||||
EXIT_NO_DATA = 12, /* Configuration data not found */
|
||||
EXIT_UNKNOWN_COLUMN = 13, /* Unknown column specified */
|
||||
EXIT_INCOMPLETE_TYPE = 14, /* None or incomplete type specified */
|
||||
|
||||
/* Run-time related */
|
||||
EXIT_RUNTIME_ERROR = 15, /* A run-time error occurred */
|
||||
EXIT_ABORTED = 16, /* Operation aborted on user request */
|
||||
EXIT_SETTING_FAILED = 17, /* Error while applying setting */
|
||||
EXIT_FORMAT_ERROR = 18, /* File format error */
|
||||
EXIT_MOD_BUSY = 19, /* Module is in use */
|
||||
EXIT_MOD_UNLOAD_FAILED = 20, /* Module could not be unloaded */
|
||||
EXIT_MOD_LOAD_FAILED = 21, /* Module could not be loaded */
|
||||
EXIT_OUT_OF_MEMORY = 22, /* Not enough available memory */
|
||||
|
||||
/* zfcp related */
|
||||
EXIT_ZFCP_FCP_NOT_FOUND = 23, /* FCP device not found */
|
||||
EXIT_ZFCP_INVALID_WWPN = 24, /* Invalid WWPN specified */
|
||||
EXIT_ZFCP_WWPN_NOT_FOUND = 25, /* WWPN not found */
|
||||
EXIT_ZFCP_INVALID_LUN = 26, /* Invalid LUN specified */
|
||||
EXIT_ZFCP_SCSI_NOT_FOUND = 27, /* SCSI device not found */
|
||||
|
||||
/* ccwgroup related */
|
||||
EXIT_GROUP_NOT_FOUND = 28, /* CCW device not found */
|
||||
EXIT_GROUP_INVALID = 29, /* CCW devices are not a valid group */
|
||||
EXIT_GROUP_ALREADY = 30, /* CCW device already grouped */
|
||||
EXIT_GROUP_FAILED = 31, /* CCW group device grouping failed */
|
||||
EXIT_UNGROUP_FAILED = 32, /* CCW group device ungrouping failed */
|
||||
|
||||
EXIT_INTERNAL_ERROR = 99, /* An internal error occurred */
|
||||
} exit_code_t;
|
||||
|
||||
const char *exit_code_to_str(exit_code_t);
|
||||
|
||||
#endif /* EXIT_CODE_H */
|
||||
38
zdev/include/export.h
Normal file
38
zdev/include/export.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef EXPORT_H
|
||||
#define EXPORT_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "exit_code.h"
|
||||
#include "misc.h"
|
||||
|
||||
struct device;
|
||||
struct devtype;
|
||||
|
||||
typedef enum {
|
||||
export_device,
|
||||
export_devtype,
|
||||
} export_t;
|
||||
|
||||
struct export_object {
|
||||
export_t type;
|
||||
union {
|
||||
struct devtype *dt;
|
||||
struct device *dev;
|
||||
} ptr;
|
||||
};
|
||||
|
||||
exit_code_t export_write_device(FILE *, struct device *, config_t, int *);
|
||||
exit_code_t export_write_devtype(FILE *, struct devtype *, config_t, int *);
|
||||
exit_code_t export_read(FILE *, const char *, struct util_list *);
|
||||
|
||||
#endif /* EXPORT_H */
|
||||
17
zdev/include/findmnt.h
Normal file
17
zdev/include/findmnt.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef FINDMNT_H
|
||||
#define FINDMNT_H
|
||||
|
||||
struct util_list;
|
||||
|
||||
struct util_list *findmnt_get_devnodes_by_path(const char *path);
|
||||
|
||||
#endif /* FINDMNT_H */
|
||||
17
zdev/include/generic_ccw.h
Normal file
17
zdev/include/generic_ccw.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef GENERIC_CCW_H
|
||||
#define GENERIC_CCW_H
|
||||
|
||||
struct devtype;
|
||||
|
||||
extern struct devtype generic_ccw_devtype;
|
||||
|
||||
#endif /* GENERIC_CCW_H */
|
||||
65
zdev/include/hash.h
Normal file
65
zdev/include/hash.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef HASH_H
|
||||
#define HASH_H
|
||||
|
||||
#include "lib/util_list.h"
|
||||
|
||||
/* A hash is a list of entries. Each entry provides an ID. A hashing function
|
||||
* maps the ID to an integer bucket number. When adding entries to a hash, the
|
||||
* entry is stored in a list associated with this bucket number. This approach
|
||||
* can greatly reduce the effort needed to find an entry in a hash by ID. */
|
||||
|
||||
/* Return the ID of an entry. */
|
||||
typedef const void *(*hash_id_fn_t)(void *);
|
||||
|
||||
/* Check if two IDs are identical. */
|
||||
typedef int (*hash_cmp_fn_t)(const void *, const void *);
|
||||
|
||||
/* Return the bucket number for an ID. */
|
||||
typedef int (*hash_fn_t)(const void *);
|
||||
|
||||
/**
|
||||
* hash - A hash supported list
|
||||
* @list: Sequential list of entries
|
||||
* @buckets: Number of hash buckets
|
||||
* @get_id: Return the ID of an entry
|
||||
* @cmp_id: Return 0 if two IDs are identical
|
||||
* @get_hash: Function that returns a hash index for an ID
|
||||
* @hash: Lists per hash index
|
||||
*/
|
||||
struct hash {
|
||||
struct util_list list;
|
||||
int buckets;
|
||||
hash_id_fn_t get_id;
|
||||
hash_cmp_fn_t cmp_id;
|
||||
hash_fn_t get_hash;
|
||||
struct util_list **hash;
|
||||
};
|
||||
|
||||
#define hash_init(hash, buckets, get_id, cmp_id, get_hash, type, member) \
|
||||
_hash_init((hash), (buckets), (get_id), (cmp_id), (get_hash), \
|
||||
offsetof(type, member))
|
||||
#define hash_new(buckets, get_id, cmp_id, get_hash, type, member) \
|
||||
_hash_new((buckets), (get_id), (cmp_id), (get_hash), \
|
||||
offsetof(type, member))
|
||||
|
||||
void _hash_init(struct hash *hash, int buckets, hash_id_fn_t get_id,
|
||||
hash_cmp_fn_t cmp_id, hash_fn_t get_hash, unsigned long offset);
|
||||
struct hash *_hash_new(int buckets, hash_id_fn_t get_id, hash_cmp_fn_t cmp_id,
|
||||
hash_fn_t get_hash, unsigned long offset);
|
||||
void hash_clear(struct hash *hash, void (*free_fn)(void *));
|
||||
void hash_free(struct hash *hash, void (*free_fn)(void *));
|
||||
void hash_add(struct hash *hash, void *entry);
|
||||
void hash_remove(struct hash *hash, void *entry);
|
||||
void *hash_find_by_id(struct hash *hash, const void *id);
|
||||
void hash_print(struct hash *hash, int ind);
|
||||
|
||||
#endif /* HASH_H */
|
||||
19
zdev/include/inuse.h
Normal file
19
zdev/include/inuse.h
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef INUSE_H
|
||||
#define INUSE_H
|
||||
|
||||
struct util_list;
|
||||
struct device;
|
||||
|
||||
void inuse_exit(void);
|
||||
struct util_list *inuse_get_resources(struct device *dev);
|
||||
|
||||
#endif /* INUSE_H */
|
||||
17
zdev/include/iscsi.h
Normal file
17
zdev/include/iscsi.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef ISCSI_H
|
||||
#define ISCSI_H
|
||||
|
||||
struct devnode;
|
||||
|
||||
struct devnode *iscsi_get_net_devnode(struct devnode *);
|
||||
|
||||
#endif /* ISCSI_H */
|
||||
25
zdev/include/lcs.h
Normal file
25
zdev/include/lcs.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef LCS_H
|
||||
#define LCS_H
|
||||
|
||||
#define LCS_MOD_NAME "lcs"
|
||||
#define LCS_CCWGROUPDRV_NAME "lcs"
|
||||
#define LCS_CCWDRV_NAME "lcs"
|
||||
#define LCS_ROOTDRV_NAME "lcs"
|
||||
#define LCS_NUM_DEVS 2
|
||||
|
||||
struct devtype;
|
||||
struct namespace;
|
||||
|
||||
extern struct devtype lcs_devtype;
|
||||
extern struct namespace lcs_namespace;
|
||||
|
||||
#endif /* LCS_H */
|
||||
24
zdev/include/lcs_auto.h
Normal file
24
zdev/include/lcs_auto.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef LCS_AUTO_H
|
||||
#define LCS_AUTO_H
|
||||
|
||||
#include "misc.h"
|
||||
|
||||
struct util_list;
|
||||
struct ccwgroup_devid;
|
||||
struct ccw_devid;
|
||||
|
||||
void lcs_auto_add_ids(struct util_list *);
|
||||
exit_code_t lcs_auto_get_devid(struct ccwgroup_devid *, struct ccw_devid *,
|
||||
err_t);
|
||||
exit_code_t lcs_auto_is_possible(struct ccwgroup_devid *, err_t);
|
||||
|
||||
#endif /* LCS_AUTO_H */
|
||||
233
zdev/include/misc.h
Normal file
233
zdev/include/misc.h
Normal file
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef MISC_H
|
||||
#define MISC_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "lib/util_list.h"
|
||||
#include "exit_code.h"
|
||||
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
#define SCOPE_ACTIVE(x) (((x) == config_active) || ((x) == config_all))
|
||||
#define SCOPE_PERSISTENT(x) (((x) == config_persistent) || \
|
||||
((x) == config_all))
|
||||
|
||||
#define DELAY_INDENT 4
|
||||
|
||||
#define RANGE_LIMIT 256
|
||||
|
||||
/* Define a NULL-terminated list of strings. */
|
||||
#define STRING_ARRAY(...) ((const char *[]) { __VA_ARGS__, NULL })
|
||||
|
||||
#define YESNO(x) ((x) ? "yes" : "no")
|
||||
|
||||
#define EVEN(x) (((x) & 1) == 0)
|
||||
|
||||
/* Enumeration of configuration sets. */
|
||||
typedef enum {
|
||||
config_active,
|
||||
config_persistent,
|
||||
config_all,
|
||||
} config_t;
|
||||
|
||||
typedef enum {
|
||||
err_ignore,
|
||||
err_print,
|
||||
err_delayed_print,
|
||||
err_delayed_forceable,
|
||||
} err_t;
|
||||
|
||||
/**
|
||||
* read_scope_t - Define the scope of device attributes to read
|
||||
* @scope_mandatory: Read only mandatory attribute settings.
|
||||
* @scope_known: Read settings of all known attributes.
|
||||
* @scope_all: Read settings of all attributes.
|
||||
*/
|
||||
typedef enum {
|
||||
scope_mandatory,
|
||||
scope_known,
|
||||
scope_all,
|
||||
} read_scope_t;
|
||||
|
||||
/* A string that can be added to a struct util_list. */
|
||||
struct strlist_node {
|
||||
struct util_list_node node;
|
||||
char str[];
|
||||
};
|
||||
|
||||
/* A pointer that can be added to a struct util_list. */
|
||||
struct ptrlist_node {
|
||||
struct util_list_node node;
|
||||
void *ptr;
|
||||
};
|
||||
|
||||
/* A dry-run file buffer. */
|
||||
struct dryrun_file {
|
||||
char *filename;
|
||||
FILE *file;
|
||||
char *buffer;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
extern const char *toolname;
|
||||
extern int verbose;
|
||||
extern int debug_enabled;
|
||||
extern int quiet;
|
||||
extern int force;
|
||||
extern int yes;
|
||||
extern int dryrun;
|
||||
extern int found_forceable;
|
||||
extern int stdout_data;
|
||||
extern int delayed_errors;
|
||||
extern int delayed_warnings;
|
||||
|
||||
extern unsigned long longrun_total;
|
||||
extern unsigned long longrun_current;
|
||||
|
||||
void misc_exit(void);
|
||||
void indent(unsigned int, const char *, ...);
|
||||
void error(const char *, ...);
|
||||
#define err_t_print(err, ...) \
|
||||
do { \
|
||||
switch (err) { \
|
||||
case err_print: \
|
||||
error(__VA_ARGS__); \
|
||||
break; \
|
||||
case err_delayed_print: \
|
||||
delayed_err(__VA_ARGS__); \
|
||||
break; \
|
||||
case err_delayed_forceable: \
|
||||
delayed_forceable(__VA_ARGS__); \
|
||||
break; \
|
||||
default: \
|
||||
break; \
|
||||
} \
|
||||
} while (0)
|
||||
void __attribute__((noreturn)) fatal(exit_code_t rc, const char *format, ...);
|
||||
#define internal(...) \
|
||||
fatal(EXIT_INTERNAL_ERROR, \
|
||||
"An internal error occurred in %s:%d: %s\n", __FILE__, __LINE__, \
|
||||
__VA_ARGS__)
|
||||
void forceable(const char *, ...);
|
||||
void forceable_warn(const char *, ...);
|
||||
void syntax(const char *, ...);
|
||||
void _warn(const char *, ...);
|
||||
#define warn(...) do { \
|
||||
if (debug_enabled) \
|
||||
fprintf(stderr, "%s:%d: ", __FILE__, __LINE__); \
|
||||
_warn(__VA_ARGS__); \
|
||||
} while (0)
|
||||
#define info(...) do { if (!quiet) fprintf(stdout_data ? stderr : stdout, \
|
||||
__VA_ARGS__); } while (0)
|
||||
#define verb(...) do { if (verbose) fprintf(stdout_data ? stderr : stdout, \
|
||||
__VA_ARGS__); } while (0)
|
||||
#define debug(...) do { \
|
||||
if (debug_enabled) \
|
||||
fprintf(stderr, "DEBUG: " __VA_ARGS__); \
|
||||
if (debug_enabled > 1) \
|
||||
print_trace(); \
|
||||
} while (0)
|
||||
void oom(void);
|
||||
|
||||
void delayed_err(const char *, ...);
|
||||
void delayed_warn(const char *, ...);
|
||||
void delayed_info(const char *, ...);
|
||||
void delayed_forceable(const char *, ...);
|
||||
void delayed_forceable_warn(const char *, ...);
|
||||
void delayed_print(int);
|
||||
void delayed_clear(void);
|
||||
bool delayed_messages_available(void);
|
||||
|
||||
bool confirm(const char *format, ...);
|
||||
void set_stdout_data(void);
|
||||
char *misc_strdup(const char *);
|
||||
void *misc_malloc(size_t);
|
||||
char *misc_asprintf(const char *, ...);
|
||||
int misc_system(err_t, const char *, ...);
|
||||
bool misc_read_dir(const char *, struct util_list *,
|
||||
bool (*)(const char *, void *), void *);
|
||||
bool path_exists(const char *);
|
||||
bool file_exists(const char *);
|
||||
bool file_writable(const char *);
|
||||
bool dir_exists(const char *);
|
||||
bool file_is_devnode(const char *);
|
||||
exit_code_t remove_file(const char *);
|
||||
char *misc_read_text_file(const char *, int, err_t);
|
||||
char *misc_read_cmd_output(const char *, int, err_t);
|
||||
char *config_read_cmd_output(const char *, int, err_t);
|
||||
exit_code_t misc_write_text_file(const char *, const char *, err_t);
|
||||
exit_code_t misc_write_text_file_retry(const char *, const char *, err_t);
|
||||
exit_code_t misc_mktemp(char **, int *);
|
||||
char *misc_readlink(const char *path);
|
||||
config_t get_config(int, int);
|
||||
bool is_zvm(void);
|
||||
bool is_terminal(void);
|
||||
const char *config_to_str(config_t);
|
||||
bool str_to_config(const char *, config_t *);
|
||||
char *quote_str(const char *, int);
|
||||
char *unquote_str(const char *);
|
||||
char *shrink_str(const char *);
|
||||
|
||||
struct util_list *strlist_new(void);
|
||||
void strlist_free(struct util_list *);
|
||||
struct util_list *strlist_copy(struct util_list *);
|
||||
void strlist_add(struct util_list *, const char *, ...);
|
||||
bool strlist_add_unique(struct util_list *, const char *, ...);
|
||||
void strlist_add_multi(struct util_list *, const char *, const char *, int);
|
||||
struct strlist_node *strlist_find(struct util_list *, const char *);
|
||||
char *strlist_flatten(struct util_list *, const char *);
|
||||
void strlist_sort_unique(struct util_list *,
|
||||
int (*)(const void *, const void *));
|
||||
int str_cmp(const void *, const void *);
|
||||
|
||||
struct util_list *ptrlist_new(void);
|
||||
void ptrlist_free(struct util_list *, int);
|
||||
void ptrlist_add(struct util_list *, void *);
|
||||
void ptrlist_add_after(struct util_list *, struct ptrlist_node *, void *);
|
||||
void ptrlist_add_before(struct util_list *, struct ptrlist_node *, void *);
|
||||
void ptrlist_add_unique(struct util_list *, void *);
|
||||
void ptrlist_remove(struct util_list *, void *);
|
||||
void ptrlist_move(struct util_list *, struct util_list *,
|
||||
struct ptrlist_node *);
|
||||
|
||||
struct dryrun_file *dryrun_open(const char *, FILE **);
|
||||
void dryrun_close(struct dryrun_file *);
|
||||
void dryrun_active(const char *, ...);
|
||||
void dryrun_persistent(const char *, ...);
|
||||
|
||||
bool starts_with(const char *, const char *);
|
||||
bool starts_with_nocase(const char *, const char *);
|
||||
bool ends_with(const char *, const char *);
|
||||
|
||||
void line_split(const char *, int *, char ***);
|
||||
void line_free(int, char **);
|
||||
|
||||
void print_trace(void);
|
||||
|
||||
int get_columns(void);
|
||||
|
||||
FILE *misc_fopen(const char *, const char *);
|
||||
FILE *misc_popen(const char *, const char *);
|
||||
int misc_fclose(FILE *);
|
||||
int misc_pclose(FILE *);
|
||||
|
||||
void longrun_start(const char *, int);
|
||||
void longrun_stop(void);
|
||||
|
||||
char *skip_comp(char *);
|
||||
void byte_swap(uint8_t *, unsigned int *, unsigned);
|
||||
bool valid_hex(const char *);
|
||||
void debug_init(int, char **);
|
||||
|
||||
#endif /* MISC_H */
|
||||
24
zdev/include/modprobe.h
Normal file
24
zdev/include/modprobe.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef MODPROBE_H
|
||||
#define MODPROBE_H
|
||||
|
||||
#include "exit_code.h"
|
||||
#include "misc.h"
|
||||
|
||||
struct attrib;
|
||||
struct setting_list;
|
||||
|
||||
exit_code_t modprobe_read_settings(const char *, const char *,
|
||||
struct attrib **, struct setting_list **);
|
||||
exit_code_t modprobe_write_settings(const char *, const char *,
|
||||
struct setting_list *);
|
||||
|
||||
#endif /* MODPROBE_H */
|
||||
31
zdev/include/module.h
Normal file
31
zdev/include/module.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef MODULE_H
|
||||
#define MODULE_H
|
||||
|
||||
#include "exit_code.h"
|
||||
#include "misc.h"
|
||||
|
||||
struct setting_list;
|
||||
struct attrib;
|
||||
|
||||
void module_exit(void);
|
||||
|
||||
void module_load_suppress(int);
|
||||
|
||||
bool module_loaded(const char *);
|
||||
exit_code_t module_load(const char *, const char **, struct setting_list *,
|
||||
err_t);
|
||||
exit_code_t module_get_params(const char *, struct attrib **,
|
||||
struct setting_list **);
|
||||
bool module_set_params(const char *, struct setting_list *);
|
||||
void module_try_load_once(const char *, const char *);
|
||||
|
||||
#endif /* MODULE_H */
|
||||
129
zdev/include/namespace.h
Normal file
129
zdev/include/namespace.h
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef NAMESPACE_H
|
||||
#define NAMESPACE_H
|
||||
|
||||
#include "exit_code.h"
|
||||
#include "misc.h"
|
||||
|
||||
struct ns_range_iterator {
|
||||
void *devid;
|
||||
void *devid_last;
|
||||
char *id;
|
||||
};
|
||||
|
||||
/**
|
||||
* ns_range_for_each - Loop over all valid IDs in a range
|
||||
* @ns: The namespace for the IDs
|
||||
* @range: The range in text form
|
||||
* @it: The range iterator object. This must be allocated using
|
||||
* ns_range_iterator_new(). After the loop it must be freed using
|
||||
* ns_range_iterator_free(). During the loop, the ID field of the
|
||||
* iterator object contains the current ID as a string.
|
||||
*/
|
||||
#define ns_range_for_each(ns, range, it) \
|
||||
for ((ns)->range_start((it), (range)); (it)->id; \
|
||||
(ns)->range_next((it)))
|
||||
|
||||
struct namespace;
|
||||
struct subtype;
|
||||
|
||||
/* NULL-terminated list of namespaces. */
|
||||
#define NUM_NAMESPACES 5
|
||||
extern struct namespace *namespaces[NUM_NAMESPACES + 1];
|
||||
|
||||
/* struct namespace - Definition of a device ID namespace
|
||||
* @devname: Short name for devices of this namespace
|
||||
* @is_id_valid: Check if string is a valid device ID and print corresponding
|
||||
* error messages if requested.
|
||||
* @is_id_similar: Optional: Check if an ID looks similar to a namespace ID.
|
||||
* ID doesn't have to be a valid ID.
|
||||
* @cmp_ids: Compare two device IDs (same return codes as strcmp)
|
||||
* @normalize_id: Return a newly allocated string containing a normalized ID
|
||||
* @parse_id: Return a newly allocated parsed device ID object. Display
|
||||
* warnings for invalid IDs if specified.
|
||||
* @cmp_parsed_ids: Compare two parsed device IDs (same return codes as strcmp)
|
||||
* @qsort_cmp: Compare two device IDs for strlist_sort_unique
|
||||
*
|
||||
* @hash_buckets: Optional: Number of hash buckets to use for device ID hashes
|
||||
* @hash_parsed_id: Optional: Return hash bucket for specified ID in parsed
|
||||
* format
|
||||
*
|
||||
* @is_id_range_valid: Check if string is a valid range of device IDs
|
||||
* @num_ids_in_range: Return the number of device IDs in the specified range
|
||||
* @is_id_in_range: Check if the specified ID is within the range
|
||||
* @range_start: If the specified range is a valid range in this namespace
|
||||
* allocate and return a corresponding range iterator object,
|
||||
* otherwise return NULL.
|
||||
* @range_next: Move the range iterator to the next device ID in range. If
|
||||
* there is no next device ID in range, release the range iterator
|
||||
* object and return false.
|
||||
*
|
||||
* @is_blacklist_active: Optional callback: Check if a blacklist is active.
|
||||
* @is_id_blacklisted: Optional callback: Check if specified ID is on the
|
||||
* blacklist.
|
||||
* @is_id_range_blacklisted: Optional callback: Check if at least one ID of
|
||||
* the specified ID range is on the blacklist.
|
||||
* @unblacklist_id: Optional callback: Remove specified ID from blacklist.
|
||||
* @unblacklist_id_range: Optional callback: Remove specified ID range from
|
||||
* blacklist.
|
||||
* @blacklist_persist: Optional callback: Persistently remove all configured
|
||||
* devices of this namespace from blacklist.
|
||||
*/
|
||||
struct namespace {
|
||||
const char * devname;
|
||||
|
||||
/* IDs. */
|
||||
exit_code_t (*is_id_valid)(const char *, err_t);
|
||||
bool (*is_id_similar)(const char *);
|
||||
int (*cmp_ids)(const char *, const char *);
|
||||
char * (*normalize_id)(const char *);
|
||||
void * (*parse_id)(const char *, err_t);
|
||||
int (*cmp_parsed_ids)(const void *, const void *);
|
||||
int (*qsort_cmp)(const void *, const void *);
|
||||
|
||||
/* ID Hash. */
|
||||
int hash_buckets;
|
||||
int (*hash_parsed_id)(const void *);
|
||||
|
||||
/* Ranges. */
|
||||
exit_code_t (*is_id_range_valid)(const char *, err_t);
|
||||
unsigned long (*num_ids_in_range)(const char *);
|
||||
bool (*is_id_in_range)(const char *, const char *);
|
||||
|
||||
void (*range_start)(struct ns_range_iterator *,
|
||||
const char *);
|
||||
void (*range_next)(struct ns_range_iterator *);
|
||||
|
||||
/* Blacklist. */
|
||||
bool (*is_blacklist_active)(void);
|
||||
bool (*is_id_blacklisted)(const char *);
|
||||
bool (*is_id_range_blacklisted)(const char *);
|
||||
void (*unblacklist_id)(const char *);
|
||||
void (*unblacklist_id_range)(const char *);
|
||||
exit_code_t (*blacklist_persist)(void);
|
||||
};
|
||||
|
||||
exit_code_t namespace_exit(void);
|
||||
void namespace_set_modified(struct namespace *);
|
||||
|
||||
int namespaces_index(struct namespace *);
|
||||
bool namespaces_is_id_valid(const char *);
|
||||
bool namespaces_is_id_range_valid(const char *);
|
||||
bool namespaces_device_exists(struct namespace *, const char *,
|
||||
config_t, struct subtype **);
|
||||
|
||||
struct ns_range_iterator *ns_range_iterator_new(void);
|
||||
void ns_range_iterator_free(struct ns_range_iterator *);
|
||||
|
||||
bool ns_is_id_valid(struct namespace *, const char *);
|
||||
bool ns_is_id_range_valid(struct namespace *, const char *);
|
||||
|
||||
#endif /* NAMESPACE_H */
|
||||
22
zdev/include/net.h
Normal file
22
zdev/include/net.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef NET_H
|
||||
#define NET_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
struct util_list;
|
||||
struct devnode;
|
||||
|
||||
bool net_add_linked_devnodes(struct util_list *, struct devnode *);
|
||||
bool net_add_vlan_base(struct util_list *, struct devnode *);
|
||||
bool net_add_bonding_base(struct util_list *, struct devnode *);
|
||||
|
||||
#endif /* NET_H */
|
||||
42
zdev/include/nic.h
Normal file
42
zdev/include/nic.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef NIC_H
|
||||
#define NIC_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
enum nic_type {
|
||||
nic_qdio,
|
||||
nic_hipers,
|
||||
nic_iedn,
|
||||
nic_inmn,
|
||||
};
|
||||
|
||||
enum nic_target {
|
||||
nic_vswitch,
|
||||
nic_lan,
|
||||
};
|
||||
|
||||
#define NIC_OWNER_LEN 9
|
||||
#define NIC_NAME_LEN 9
|
||||
|
||||
struct nic_data {
|
||||
enum nic_type type;
|
||||
enum nic_target target;
|
||||
char owner[NIC_OWNER_LEN];
|
||||
char name[NIC_NAME_LEN];
|
||||
};
|
||||
|
||||
bool nic_data_get(const char *, struct nic_data *);
|
||||
void nic_data_print(struct nic_data *, int);
|
||||
bool nic_vswitch_get_layer2(const char *, int *);
|
||||
bool nic_lan_get_layer2(const char *, const char *, int *);
|
||||
|
||||
#endif /* NIC_H */
|
||||
29
zdev/include/opts.h
Normal file
29
zdev/include/opts.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef OPTS_H
|
||||
#define OPTS_H
|
||||
|
||||
#include "exit_code.h"
|
||||
|
||||
#define OPTS_MAX 255
|
||||
#define OPTS_CONFLICT(x, ...) { .op = (x), \
|
||||
.conflicts = ((int []) { __VA_ARGS__, 0 })}
|
||||
|
||||
struct opts_conflict {
|
||||
int op;
|
||||
int *conflicts;
|
||||
};
|
||||
|
||||
struct option;
|
||||
|
||||
exit_code_t opts_check_conflict(int, int[OPTS_MAX + 1], struct opts_conflict *,
|
||||
const struct option *);
|
||||
|
||||
#endif /* OPTS_H */
|
||||
71
zdev/include/path.h
Normal file
71
zdev/include/path.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef PATH_H
|
||||
#define PATH_H PATH_H
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
#include "exit_code.h"
|
||||
#include "misc.h"
|
||||
|
||||
#define MODPROBE_PREFIX "s390x"
|
||||
#define UDEV_PREFIX "41"
|
||||
#define UDEV_SUFFIX ".rules"
|
||||
|
||||
#define PATH_MODPROBE_CONF "/etc/modprobe.d"
|
||||
#define PATH_MODPROBE "/usr/sbin/modprobe"
|
||||
#define PATH_CCW_BUS "/sys/bus/ccw"
|
||||
#define PATH_CCWGROUP_BUS "/sys/bus/ccwgroup"
|
||||
#define PATH_UDEV_RULES "/etc/udev/rules.d"
|
||||
#define PATH_PROC "/proc"
|
||||
|
||||
#define PATH_UDEVADM "udevadm"
|
||||
#define PATH_VMCP "/usr/sbin/vmcp"
|
||||
#define PATH_IP "ip"
|
||||
|
||||
#define PATH_ROOT "/"
|
||||
#define PATH_ROOT_SCRIPT TOOLS_LIBDIR "/zdev-root-update"
|
||||
|
||||
struct devtype;
|
||||
struct zfcp_lun_devid;
|
||||
struct util_list;
|
||||
|
||||
void path_exit(void);
|
||||
void path_set_base(struct util_list *);
|
||||
|
||||
exit_code_t path_create(const char *);
|
||||
char *path_get(const char *, ...);
|
||||
char *path_get_modprobe_conf(struct devtype *);
|
||||
char *path_get_sys_module(const char *);
|
||||
char *path_get_sys_module_param(const char *, const char *);
|
||||
char *path_get_sys_block_dev(const char *);
|
||||
char *path_get_sys_dev_block(unsigned int, unsigned int);
|
||||
char *path_get_sys_dev_char(unsigned int, unsigned int);
|
||||
char *path_get_sys_dev_char_devices(void);
|
||||
char *path_get_sys_class(const char *, const char *);
|
||||
char *path_get_modprobe(void);
|
||||
char *path_get_ccw_device(const char *, const char *);
|
||||
char *path_get_ccw_devices(const char *);
|
||||
char *path_get_ccwgroup_device(const char *, const char *);
|
||||
char *path_get_ccwgroup_devices(const char *);
|
||||
char *path_get_udev_rule(const char *, const char *);
|
||||
char *path_get_udev_rules(void);
|
||||
char *path_get_proc(const char *);
|
||||
char *path_get_sys_bus_dev(const char *, const char *);
|
||||
char *path_get_sys_bus_drv(const char *, const char *);
|
||||
char *path_get_zfcp_lun_dev(struct zfcp_lun_devid *);
|
||||
char *path_get_zfcp_port_dev(struct zfcp_lun_devid *);
|
||||
char *path_get_scsi_hctl_dev(const char *);
|
||||
|
||||
exit_code_t path_for_each(const char *,
|
||||
exit_code_t (*callback)(const char *, const char *,
|
||||
void *), void *);
|
||||
|
||||
#endif /* PATH_H */
|
||||
25
zdev/include/qeth.h
Normal file
25
zdev/include/qeth.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef QETH_H
|
||||
#define QETH_H
|
||||
|
||||
#define QETH_MOD_NAME "qeth"
|
||||
#define QETH_CCWGROUPDRV_NAME "qeth"
|
||||
#define QETH_CCWDRV_NAME "qeth"
|
||||
#define QETH_ROOTDRV_NAME "qeth"
|
||||
#define QETH_NUM_DEVS 3
|
||||
|
||||
struct devtype;
|
||||
struct namespace;
|
||||
|
||||
extern struct devtype qeth_devtype;
|
||||
extern struct namespace qeth_namespace;
|
||||
|
||||
#endif /* QETH_H */
|
||||
24
zdev/include/qeth_auto.h
Normal file
24
zdev/include/qeth_auto.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef QETH_AUTO_H
|
||||
#define QETH_AUTO_H
|
||||
|
||||
#include "misc.h"
|
||||
|
||||
struct util_list;
|
||||
struct ccwgroup_devid;
|
||||
struct ccw_devid;
|
||||
|
||||
void qeth_auto_add_ids(struct util_list *);
|
||||
exit_code_t qeth_auto_get_devid(struct ccwgroup_devid *, struct ccw_devid *,
|
||||
err_t);
|
||||
exit_code_t qeth_auto_is_possible(struct ccwgroup_devid *, err_t);
|
||||
|
||||
#endif /* QETH_AUTO_H */
|
||||
17
zdev/include/root.h
Normal file
17
zdev/include/root.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef ROOT_H
|
||||
#define ROOT_H
|
||||
|
||||
#include "exit_code.h"
|
||||
|
||||
exit_code_t root_check(void);
|
||||
|
||||
#endif /* ROOT_H */
|
||||
32
zdev/include/scsi.h
Normal file
32
zdev/include/scsi.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef SCSI_H
|
||||
#define SCSI_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct zfcp_lun_devid;
|
||||
struct util_list;
|
||||
|
||||
/* HCTL = Host:Channel:Target:LUN tuple for identifying a SCSI device. */
|
||||
|
||||
void scsi_exit(void);
|
||||
void scsi_reread(void);
|
||||
uint64_t scsi_lun_to_fcp_lun(uint64_t);
|
||||
uint64_t scsi_lun_from_fcp_lun(uint64_t);
|
||||
char *scsi_hctl_to_zfcp_lun_id(const char *);
|
||||
char *scsi_hctl_from_zfcp_lun_devid(struct zfcp_lun_devid *);
|
||||
char *scsi_hctl_from_zfcp_lun_id(const char *);
|
||||
bool scsi_hctl_exists(const char *);
|
||||
void scsi_hctl_add_zfcp_lun_ids(struct util_list *);
|
||||
char *scsi_hctl_from_devpath(const char *);
|
||||
|
||||
#endif /* SCSI_H */
|
||||
88
zdev/include/select.h
Normal file
88
zdev/include/select.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef SELECT_H
|
||||
#define SELECT_H
|
||||
|
||||
#include "lib/util_list.h"
|
||||
|
||||
#include "exit_code.h"
|
||||
#include "misc.h"
|
||||
|
||||
struct device;
|
||||
struct devtype;
|
||||
struct subtype;
|
||||
struct devnode;
|
||||
|
||||
struct select_opts {
|
||||
struct devtype *devtype;
|
||||
struct subtype *subtype;
|
||||
unsigned int all:1;
|
||||
unsigned int configured:1;
|
||||
unsigned int existing:1;
|
||||
unsigned int online:1;
|
||||
unsigned int offline:1;
|
||||
unsigned int failed:1;
|
||||
struct util_list devids; /* List of struct strlist_node */
|
||||
struct util_list by_path; /* List of struct strlist_node */
|
||||
struct util_list by_node; /* List of struct strlist_node */
|
||||
struct util_list by_if; /* List of struct strlist_node */
|
||||
struct util_list by_attr; /* List of struct strlist_node */
|
||||
};
|
||||
|
||||
/** selected_dev_node - Represents a selected device ID
|
||||
* dt: Devtype of selected device
|
||||
* st: Subtype of selected device
|
||||
* id: ID if selected device
|
||||
* param: Command line parameter that resulted in device selection
|
||||
* rc: Per-device selection exit code (%EXIT_OK if all went well)
|
||||
*/
|
||||
struct selected_dev_node {
|
||||
struct util_list_node node;
|
||||
struct devtype *dt;
|
||||
struct subtype *st;
|
||||
char *id;
|
||||
char *param;
|
||||
exit_code_t rc;
|
||||
};
|
||||
|
||||
struct select_opts *select_opts_new(void);
|
||||
void select_opts_free(struct select_opts *);
|
||||
bool select_opts_dev_specified(struct select_opts *);
|
||||
|
||||
struct util_list *selected_dev_list_new(void);
|
||||
void selected_dev_free(struct selected_dev_node *);
|
||||
void selected_dev_list_free(struct util_list *);
|
||||
struct selected_dev_node *selected_dev_list_add(struct util_list *,
|
||||
struct devtype *,
|
||||
struct subtype *, const char *,
|
||||
const char *, exit_code_t);
|
||||
void selected_dev_print(struct selected_dev_node *, int);
|
||||
|
||||
exit_code_t select_devices(struct select_opts *, struct util_list *, int, int,
|
||||
int, config_t, read_scope_t, err_t);
|
||||
|
||||
exit_code_t select_by_devnode(struct select_opts *, struct util_list *,
|
||||
config_t, read_scope_t, struct devtype *,
|
||||
struct subtype *, struct devnode *, const char *,
|
||||
err_t);
|
||||
exit_code_t select_by_node(struct select_opts *, struct util_list *,
|
||||
config_t, read_scope_t, struct devtype *,
|
||||
struct subtype *, const char *, err_t err);
|
||||
exit_code_t select_by_path(struct select_opts *, struct util_list *, config_t,
|
||||
read_scope_t, struct devtype *, struct subtype *,
|
||||
const char *, err_t);
|
||||
exit_code_t select_by_interface(struct select_opts *, struct util_list *,
|
||||
config_t, read_scope_t, struct devtype *,
|
||||
struct subtype *, const char *, err_t);
|
||||
|
||||
bool select_match_state(struct device *, struct select_opts *);
|
||||
void selected_dev_list_print(struct util_list *, int);
|
||||
|
||||
#endif /* SELECT_H */
|
||||
100
zdev/include/setting.h
Normal file
100
zdev/include/setting.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef SETTING_H
|
||||
#define SETTING_H
|
||||
|
||||
#include "lib/util_list.h"
|
||||
|
||||
#include "exit_code.h"
|
||||
#include "misc.h"
|
||||
|
||||
struct attrib;
|
||||
|
||||
/**
|
||||
* struct setting - An attribute setting
|
||||
* @node: List node for adding to list
|
||||
* @attrib: Attribute definition - optional for unknown attribute settings
|
||||
* @name: Attribute name
|
||||
* @value: Attribute value
|
||||
* @values: List of values for multi-value attributes
|
||||
* @actual_value: Current attribute value
|
||||
* @actual_values: List of current values for multi-value attributes
|
||||
* @modified: Setting was modified or is new
|
||||
* @specified: User specified a value for this setting
|
||||
* @derived: The actual value was not read from the configuration
|
||||
* @removed: Setting was removed
|
||||
* @readonly: Setting cannot be written to
|
||||
*/
|
||||
struct setting {
|
||||
struct util_list_node node;
|
||||
struct attrib *attrib;
|
||||
char *name;
|
||||
char *value;
|
||||
struct util_list *values;
|
||||
char *actual_value;
|
||||
struct util_list *actual_values;
|
||||
unsigned int modified:1;
|
||||
unsigned int specified:1;
|
||||
unsigned int removed:1;
|
||||
unsigned int derived:1;
|
||||
unsigned int readonly:1;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct setting_list - A list of attribute settings
|
||||
* @list: List for maintaining settings
|
||||
* @modified: Indication of whether this list was modified
|
||||
*/
|
||||
struct setting_list {
|
||||
struct util_list list;
|
||||
unsigned int modified:1;
|
||||
};
|
||||
|
||||
struct setting *setting_new(struct attrib *, const char *, const char *);
|
||||
struct setting *setting_copy(const struct setting *);
|
||||
bool setting_is_set(struct setting *);
|
||||
exit_code_t setting_write(const char *, struct setting *s);
|
||||
void setting_print(struct setting *, int);
|
||||
|
||||
struct setting_list *setting_list_new(void);
|
||||
void setting_list_free(struct setting_list *);
|
||||
void setting_list_clear(struct setting_list *);
|
||||
void setting_list_add(struct setting_list *, struct setting *);
|
||||
struct setting *setting_list_find(struct setting_list *, const char *);
|
||||
void setting_list_get_bool_state(struct setting_list *, const char *, int *,
|
||||
int *);
|
||||
struct setting *setting_list_apply(struct setting_list *, struct attrib *,
|
||||
const char *, const char *);
|
||||
struct setting *setting_list_apply_specified(struct setting_list *,
|
||||
struct attrib *, const char *,
|
||||
const char *);
|
||||
|
||||
struct setting *setting_list_apply_actual(struct setting_list *,
|
||||
struct attrib *, const char *,
|
||||
const char *);
|
||||
|
||||
bool setting_list_modified(struct setting_list *);
|
||||
char *setting_list_flatten(struct setting_list *);
|
||||
void setting_list_print(struct setting_list *, int);
|
||||
struct util_list *setting_list_get_sorted(struct setting_list *);
|
||||
bool setting_list_check_conflict(struct setting_list *, config_t, err_t);
|
||||
|
||||
void setting_list_apply_defaults(struct setting_list *, struct attrib **, bool);
|
||||
void setting_list_merge(struct setting_list *, struct setting_list *, bool,
|
||||
bool);
|
||||
struct setting_list *setting_list_copy(struct setting_list *);
|
||||
void setting_list_map_values(struct setting_list *);
|
||||
void setting_list_mark_default_derived(struct setting_list *);
|
||||
int setting_list_count_set(struct setting_list *);
|
||||
void setting_list_remove_derived(struct setting_list *);
|
||||
char *setting_get_changes(struct setting_list *, struct setting_list *);
|
||||
bool setting_match_value(struct setting *, const char *);
|
||||
|
||||
#endif /* SETTING_H */
|
||||
297
zdev/include/subtype.h
Normal file
297
zdev/include/subtype.h
Normal file
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef SUBTYPE_H
|
||||
#define SUBTYPE_H
|
||||
|
||||
#include "exit_code.h"
|
||||
#include "misc.h"
|
||||
|
||||
#define SUBTYPE_TITLE_LEN 60
|
||||
|
||||
struct devtype;
|
||||
struct device;
|
||||
struct devnode;
|
||||
struct namespace;
|
||||
struct selected_dev_node;
|
||||
struct subtype;
|
||||
struct util_list;
|
||||
|
||||
extern struct subtype subtype_base;
|
||||
|
||||
/**
|
||||
* subtype_cb_t - Callback for subtype.for_each_id()
|
||||
* @subtype: Subtype
|
||||
* @id: Device ID
|
||||
* @config: Configuration specified to subtype.for_each_id()
|
||||
* @data: Private data pointer passed to subtype.for_each_id()
|
||||
*
|
||||
* This callback is called once for each device ID of a device that exists
|
||||
* in any of the configuration sets specified to subtype.for_each_id().
|
||||
* If the callback returns a value other than EXIT_OK, the loop is aborted
|
||||
* and the exit code is returned to the caller of subtype.for_each_id().
|
||||
*/
|
||||
typedef exit_code_t (*subtype_cb_t)(struct subtype *st, const char *id,
|
||||
config_t config, void *data);
|
||||
|
||||
/**
|
||||
* struct subtype - Definition of a sub-type
|
||||
* @super: Use methods of the super subtype if this subtype does not implement
|
||||
* a method.
|
||||
* @devtype: Pointer to device type
|
||||
* @name: Short name of this sub-type. Must be unique within this devtype.
|
||||
* May be the same as the devtype name.
|
||||
* @title: Short description of this sub-type (max. 60 characters)
|
||||
* @devname: Short name for devices of this sub-type
|
||||
* @modules: (Optional) Array of kernel modules required for this subtype
|
||||
* @namespace: Namespace for devices of this device type
|
||||
* @data: Arbitrary data used by subtype methods
|
||||
* @dev_attribs: Array of device attributes
|
||||
* @prefix: Array of device attribut prefixes that are searched for
|
||||
* attributes not listed in @dev_attribs. Trailing slashes must not be
|
||||
* included.
|
||||
* @unknown_dev_attribs: Allow specification of unknown device attributes
|
||||
* @support_definable: Allow definition of devices
|
||||
* @generic: This is a generic subtype that is intended as a fallback only
|
||||
*
|
||||
* @devices: Devices of this subtype
|
||||
*
|
||||
* @init: Initialize subtype
|
||||
* @exit: Release dynamically allocated resources associated with this type
|
||||
*
|
||||
* @exists_active: Check if device exists in active configuration
|
||||
* @exists_persistent: Check if device exists in persistent configuration
|
||||
*
|
||||
* @add_active_ids: Add IDs of all devices existing in active configuration to
|
||||
* specified strlist
|
||||
* @add_persistent_ids: Add IDs of all devices existing in persistent
|
||||
* configuration to specified strlist
|
||||
*
|
||||
* @read_active: Read device configuration from active configuration
|
||||
* @read_persistent: Read device configuration from persistent configuration
|
||||
*
|
||||
* @configure_active: Apply configuration to active configuration
|
||||
* @configure_persistent: Apply configuration to persistent configuration
|
||||
*
|
||||
* @check_pre_write: Optional: Determine if the given configuration is valid
|
||||
* for the specified device. If not, emit warning messages
|
||||
* and return an exit code other than EXIT_OK. This function
|
||||
* is called before writing the device configuration.
|
||||
* @check_post_write: Optional: Determine if the given configuration is valid
|
||||
* for the specified device. If not, emit warning messages
|
||||
* and return an exit code other than EXIT_OK. This function
|
||||
* is called after writing the device configuration.
|
||||
*
|
||||
* @online_set: Optional: Set the online state of a device
|
||||
* @online_get: Optional: Retrieve device online state (0=offline, 1=online,
|
||||
* -1=not set)
|
||||
* @online_specified: Optional: Check if online setting was specified
|
||||
*
|
||||
* @add_errors: Optional: Add textual summaries of known error conditions
|
||||
* found for the specified device to the strlist.
|
||||
*
|
||||
* @add_devnodes: Optional: Add struct devnodes to specified ptrlist for each
|
||||
* Linux device node or network interface that is provided by
|
||||
* the device with the specified ID.
|
||||
* @resolve_devnode: Optional: Attempt to find the device ID of the device
|
||||
* providing the specified devnode. The result is a newly
|
||||
* allocated device ID string or %NULL.
|
||||
* @add_prereqs: Optional: For devices that require the configuration of
|
||||
* other devices first, add corresponding selected_dev_nodes
|
||||
* to list.
|
||||
* @add_modules: Optional: Add list of kernel module names required by
|
||||
* device to strlist.
|
||||
* @remove_combined: Optional: For devices that combine multiple devices into
|
||||
* one "group" device, remove the IDs of devices that are
|
||||
* combined in the specified group device from the
|
||||
* selected_dev_nodes list.
|
||||
* @get_active_attrib_path: Optional: Return the absolute path to the specified
|
||||
* device attribute or device attribute prefix.
|
||||
* @get_active_attrib: Optional: Return the value of the specified device
|
||||
* attribute.
|
||||
*
|
||||
* @is_definable: Optional: Check if device can be defined
|
||||
* @detect_definable: Optional: Detect configuration of definable device
|
||||
* @device_define: Optional: Define a device (e.g. group CCWGROUP device)
|
||||
* @device_undefine: Optional: Undefine a device
|
||||
* @add_definable_ids: Optional: Add IDs of all devices that can be defined to
|
||||
* specified strlist
|
||||
*/
|
||||
struct subtype {
|
||||
/* Static data. */
|
||||
struct subtype *super;
|
||||
|
||||
struct devtype *devtype;
|
||||
const char *name;
|
||||
const char title[SUBTYPE_TITLE_LEN + 1];
|
||||
const char *devname;
|
||||
const char **modules;
|
||||
struct namespace *namespace;
|
||||
void *data;
|
||||
|
||||
struct attrib **dev_attribs;
|
||||
const char **prefixes;
|
||||
unsigned int unknown_dev_attribs:1;
|
||||
unsigned int support_definable:1;
|
||||
unsigned int generic:1;
|
||||
|
||||
/* Dynamic data. */
|
||||
struct device_list *devices;
|
||||
|
||||
/* Methods */
|
||||
void (*init)(struct subtype *);
|
||||
void (*exit)(struct subtype *);
|
||||
|
||||
bool (*exists_active)(struct subtype *, const char *);
|
||||
bool (*exists_persistent)(struct subtype *, const char *);
|
||||
|
||||
void (*add_active_ids)(struct subtype *, struct util_list *);
|
||||
void (*add_persistent_ids)(struct subtype *,
|
||||
struct util_list *);
|
||||
|
||||
exit_code_t (*read_active)(struct subtype *, struct device *,
|
||||
read_scope_t);
|
||||
exit_code_t (*read_persistent)(struct subtype *, struct device *,
|
||||
read_scope_t);
|
||||
|
||||
exit_code_t (*configure_active)(struct subtype *, struct device *);
|
||||
exit_code_t (*configure_persistent)(struct subtype *,
|
||||
struct device *);
|
||||
|
||||
exit_code_t (*deconfigure_active)(struct subtype *,
|
||||
struct device *);
|
||||
exit_code_t (*deconfigure_persistent)(struct subtype *,
|
||||
struct device *);
|
||||
|
||||
exit_code_t (*check_pre_configure)(struct subtype *,
|
||||
struct device *, int, config_t);
|
||||
exit_code_t (*check_post_configure)(struct subtype *,
|
||||
struct device *, int, config_t);
|
||||
|
||||
void (*online_set)(struct subtype *, struct device *, int,
|
||||
config_t);
|
||||
int (*online_get)(struct subtype *, struct device *,
|
||||
config_t);
|
||||
bool (*online_specified)(struct subtype *, struct device *,
|
||||
config_t);
|
||||
|
||||
void (*add_errors)(struct subtype *, const char *,
|
||||
struct util_list *);
|
||||
|
||||
void (*add_devnodes)(struct subtype *, const char *,
|
||||
struct util_list *);
|
||||
char *(*resolve_devnode)(struct subtype *, struct devnode *);
|
||||
void (*add_prereqs)(struct subtype *, const char *,
|
||||
struct util_list *);
|
||||
void (*add_modules)(struct subtype *, struct device *,
|
||||
struct util_list *);
|
||||
void (*rem_combined)(struct subtype *, struct device *,
|
||||
struct selected_dev_node *,
|
||||
struct util_list *);
|
||||
char *(*get_active_attrib_path)(struct subtype *,
|
||||
struct device *,
|
||||
const char *);
|
||||
char *(*get_active_attrib)(struct subtype *, struct device *,
|
||||
const char *);
|
||||
|
||||
/* Optional methods for definable devices. */
|
||||
exit_code_t (*is_definable)(struct subtype *, const char *, err_t);
|
||||
exit_code_t (*detect_definable)(struct subtype *,
|
||||
struct device *);
|
||||
exit_code_t (*device_define)(struct subtype *, struct device *);
|
||||
exit_code_t (*device_undefine)(struct subtype *, struct device *);
|
||||
void (*add_definable_ids)(struct subtype *,
|
||||
struct util_list *);
|
||||
};
|
||||
|
||||
/* Subtype method accessor functions. */
|
||||
|
||||
void subtype_init(struct subtype *);
|
||||
void subtype_exit(struct subtype *);
|
||||
|
||||
bool subtype_device_exists_active(struct subtype *, const char *);
|
||||
bool subtype_device_exists_persistent(struct subtype *, const char *);
|
||||
|
||||
void subtype_add_active_ids(struct subtype *, struct util_list *);
|
||||
void subtype_add_persistent_ids(struct subtype *, struct util_list *);
|
||||
|
||||
exit_code_t subtype_device_read_active(struct subtype *, struct device *,
|
||||
read_scope_t);
|
||||
exit_code_t subtype_device_read_persistent(struct subtype *, struct device *,
|
||||
read_scope_t);
|
||||
|
||||
exit_code_t subtype_device_configure_active(struct subtype *, struct device *);
|
||||
exit_code_t subtype_device_configure_persistent(struct subtype *,
|
||||
struct device *);
|
||||
|
||||
exit_code_t subtype_device_deconfigure_active(struct subtype *,
|
||||
struct device *);
|
||||
exit_code_t subtype_device_deconfigure_persistent(struct subtype *st,
|
||||
struct device *);
|
||||
|
||||
exit_code_t subtype_check_pre_configure(struct subtype *, struct device *, int,
|
||||
config_t);
|
||||
exit_code_t subtype_check_post_configure(struct subtype *, struct device *, int,
|
||||
config_t);
|
||||
|
||||
void subtype_online_set(struct subtype *, struct device *, int, config_t);
|
||||
int subtype_online_get(struct subtype *, struct device *, config_t);
|
||||
bool subtype_online_specified(struct subtype *, struct device *, config_t);
|
||||
|
||||
void subtype_add_errors(struct subtype *, const char *, struct util_list *);
|
||||
struct util_list *subtype_get_errors(struct subtype *st, const char *);
|
||||
|
||||
void subtype_add_modules(struct subtype *, struct device *, struct util_list *);
|
||||
void subtype_add_devnodes(struct subtype *, const char *, struct util_list *);
|
||||
char *subtype_resolve_devnode(struct subtype *, struct devnode *);
|
||||
char *subtype_get_devnodes_str(struct subtype *, const char *, int, int, int,
|
||||
int);
|
||||
void subtype_add_prereqs(struct subtype *, const char *, struct util_list *);
|
||||
void subtype_rem_combined(struct subtype *, struct device *,
|
||||
struct selected_dev_node *, struct util_list *);
|
||||
char *subtype_get_active_attrib_path(struct subtype *, struct device *,
|
||||
const char *);
|
||||
char *subtype_get_active_attrib(struct subtype *, struct device *,
|
||||
const char *);
|
||||
|
||||
exit_code_t subtype_device_is_definable(struct subtype *, const char *, err_t);
|
||||
exit_code_t subtype_detect_definable(struct subtype *, struct device *);
|
||||
exit_code_t subtype_device_define(struct subtype *, struct device *);
|
||||
exit_code_t subtype_device_undefine(struct subtype *, struct device *);
|
||||
void subtype_add_definable_ids(struct subtype *, struct util_list *);
|
||||
|
||||
/* Subtype helper functions. */
|
||||
bool subtype_device_exists(struct subtype *st, const char *id,
|
||||
config_t config);
|
||||
exit_code_t subtype_for_each_id(struct subtype *st, config_t config,
|
||||
subtype_cb_t cb, void *data);
|
||||
struct util_list *subtype_get_devnodes(struct subtype *st, const char *id);
|
||||
exit_code_t subtype_read_device(struct subtype *st, const char *id,
|
||||
config_t config, read_scope_t,
|
||||
struct device **dev_ptr);
|
||||
exit_code_t subtype_reread_device(struct subtype *st, const char *id,
|
||||
config_t config, read_scope_t,
|
||||
struct device **dev_ptr);
|
||||
exit_code_t subtype_write_device(struct subtype *st, struct device *dev,
|
||||
config_t config);
|
||||
|
||||
/* Generic helper functions. */
|
||||
struct subtype *subtype_find(const char *name);
|
||||
struct attrib *subtype_find_dev_attrib(struct subtype *st, const char *str);
|
||||
exit_code_t subtype_read_all_devices(struct subtype *st, config_t config,
|
||||
read_scope_t);
|
||||
bool subtypes_find_by_devnode(struct devnode *devnode,
|
||||
struct subtype **st_ptr, char **id_ptr);
|
||||
void subtype_devices_print_all(void);
|
||||
void subtype_add_static_modules(struct util_list *, struct subtype *);
|
||||
unsigned long subtype_count_ids(struct subtype *, config_t);
|
||||
|
||||
void subtype_print(struct subtype *st, int indent);
|
||||
|
||||
#endif /* SUBTYPE_H */
|
||||
74
zdev/include/table.h
Normal file
74
zdev/include/table.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef TABLE_H
|
||||
#define TABLE_H
|
||||
|
||||
#include "exit_code.h"
|
||||
|
||||
#define COLUMN(a, b, c, d, e) \
|
||||
{ \
|
||||
.name = (a), \
|
||||
.align = (b), \
|
||||
.id = (c), \
|
||||
.def = (d), \
|
||||
.desc = (e), \
|
||||
}
|
||||
|
||||
#define COLUMN_ARRAY(...) \
|
||||
((struct column[]) { __VA_ARGS__, { .name = NULL } })
|
||||
|
||||
/* Alignment of table cell text. */
|
||||
typedef enum {
|
||||
align_right,
|
||||
align_left,
|
||||
} align_t;
|
||||
|
||||
/**
|
||||
* column - Definition of a table column
|
||||
* @name: Name of column. A name ending with ':' indicates that it can be
|
||||
* followed by arbitrary text (e.g. ATTR:online)
|
||||
* @desc: Description of column
|
||||
* @align: Alignment of column values.
|
||||
* @id: ID of column. This value is passed to get_value_cb_t
|
||||
* @def: If set, include column in default table
|
||||
*/
|
||||
struct column {
|
||||
const char *name;
|
||||
const char *desc;
|
||||
align_t align;
|
||||
int id;
|
||||
unsigned int def:1;
|
||||
};
|
||||
|
||||
struct util_list;
|
||||
|
||||
/**
|
||||
* table_value_cb_t - Retrieve table cell value
|
||||
* @item: Item for which a cell value should be retrieved
|
||||
* @id: Column ID of the column to retrieve for the item
|
||||
* @heading: The full heading of the column
|
||||
* @data: Arbitrary data pointer passed to table_print()
|
||||
*
|
||||
* This callback function should retrieve the specified table cell value
|
||||
* and return it as a newly allocated string to the caller. It may return
|
||||
* %NULL if no value of the specified type is defined for the specified item.
|
||||
*/
|
||||
typedef char *(*table_value_cb_t)(void *item, int id, const char *heading,
|
||||
void *data);
|
||||
|
||||
struct column *table_get_column(struct column *, const char *);
|
||||
exit_code_t table_print(struct column *, table_value_cb_t, void *,
|
||||
struct util_list *, struct util_list *, int, int, int,
|
||||
int);
|
||||
exit_code_t table_check_columns(struct column *, struct util_list *);
|
||||
void table_print_columns(struct column *, struct util_list *, int, int);
|
||||
void table_set_default(struct column *, int, int);
|
||||
|
||||
#endif /* TABLE_H */
|
||||
29
zdev/include/table_attribs.h
Normal file
29
zdev/include/table_attribs.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef TABLE_ATTRIBS_H
|
||||
#define TABLE_ATTRIBS_H
|
||||
|
||||
#include "misc.h"
|
||||
|
||||
struct util_list;
|
||||
struct devtype;
|
||||
struct subtype;
|
||||
struct attrib;
|
||||
|
||||
struct table_attrib {
|
||||
struct subtype *st;
|
||||
struct attrib *attrib;
|
||||
};
|
||||
|
||||
struct table_attrib *table_attrib_new(struct subtype *, struct attrib *);
|
||||
void table_attribs_show(struct util_list *, int, int, struct devtype *);
|
||||
void table_attribs_show_details(struct util_list *, struct devtype *);
|
||||
|
||||
#endif /* TABLE_ATTRIBS_H */
|
||||
19
zdev/include/table_types.h
Normal file
19
zdev/include/table_types.h
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef TABLE_TYPES_H
|
||||
#define TABLE_TYPES_H
|
||||
|
||||
#include "misc.h"
|
||||
|
||||
struct util_list;
|
||||
|
||||
exit_code_t table_types_show(struct util_list *, int, int);
|
||||
|
||||
#endif /* TABLE_TYPES_H */
|
||||
47
zdev/include/udev.h
Normal file
47
zdev/include/udev.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef UDEV_H
|
||||
#define UDEV_H
|
||||
|
||||
#include "lib/util_list.h"
|
||||
#include "exit_code.h"
|
||||
|
||||
extern int udev_need_settle;
|
||||
|
||||
/* Single key-operator-value entry in a udev rule line.*/
|
||||
struct udev_entry_node {
|
||||
struct util_list_node node;
|
||||
char *key;
|
||||
char *op;
|
||||
char *value;
|
||||
};
|
||||
|
||||
/* Single udev line in a udev rule file. */
|
||||
struct udev_line_node {
|
||||
struct util_list_node node;
|
||||
struct util_list entries;
|
||||
char *line;
|
||||
};
|
||||
|
||||
/* Udev rule file. */
|
||||
struct udev_file {
|
||||
struct util_list lines;
|
||||
};
|
||||
|
||||
exit_code_t udev_read_file(const char *, struct udev_file **);
|
||||
void udev_free_file(struct udev_file *);
|
||||
void udev_file_print(struct udev_file *);
|
||||
|
||||
void udev_get_device_ids(const char *, struct util_list *);
|
||||
exit_code_t udev_remove_rule(const char *, const char *);
|
||||
|
||||
void udev_settle(void);
|
||||
|
||||
#endif /* UDEV_H */
|
||||
23
zdev/include/udev_ccw.h
Normal file
23
zdev/include/udev_ccw.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef UDEV_CCW_H
|
||||
#define UDEV_CCW_H
|
||||
|
||||
#include "exit_code.h"
|
||||
#include "misc.h"
|
||||
|
||||
struct device;
|
||||
|
||||
bool udev_ccw_exists(const char *, const char *);
|
||||
exit_code_t udev_ccw_read_device(struct device *);
|
||||
exit_code_t udev_ccw_write_device(struct device *);
|
||||
exit_code_t udev_ccw_write_cio_ignore(const char *);
|
||||
|
||||
#endif /* UDEV_CCW_H */
|
||||
25
zdev/include/udev_ccwgroup.h
Normal file
25
zdev/include/udev_ccwgroup.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef UDEV_CCWGROUP_H
|
||||
#define UDEV_CCWGROUP_H
|
||||
|
||||
#include "exit_code.h"
|
||||
#include "misc.h"
|
||||
|
||||
struct device;
|
||||
struct util_list;
|
||||
|
||||
bool udev_ccwgroup_exists(const char *, const char *);
|
||||
exit_code_t udev_ccwgroup_read_device(struct device *);
|
||||
exit_code_t udev_ccwgroup_write_device(struct device *);
|
||||
void udev_ccwgroup_add_device_ids(const char *, struct util_list *);
|
||||
exit_code_t udev_ccwgroup_remove_rule(const char *, const char *);
|
||||
|
||||
#endif /* UDEV_CCWGROUP_H */
|
||||
24
zdev/include/udev_zfcp_lun.h
Normal file
24
zdev/include/udev_zfcp_lun.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef UDEV_ZFCP_LUN_H
|
||||
#define UDEV_ZFCP_LUN_H
|
||||
|
||||
#include "exit_code.h"
|
||||
#include "misc.h"
|
||||
|
||||
struct device;
|
||||
|
||||
void udev_zfcp_lun_add_device_ids(struct util_list *);
|
||||
bool udev_zfcp_lun_exists(const char *);
|
||||
exit_code_t udev_zfcp_lun_read_device(struct device *);
|
||||
exit_code_t udev_zfcp_lun_write_device(struct device *);
|
||||
exit_code_t udev_zfcp_lun_remove_rule(const char *);
|
||||
|
||||
#endif /* UDEV_ZFCP_LUN_H */
|
||||
25
zdev/include/zfcp.h
Normal file
25
zdev/include/zfcp.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef ZFCP_H
|
||||
#define ZFCP_H
|
||||
|
||||
#include "exit_code.h"
|
||||
#include "misc.h"
|
||||
|
||||
#define ZFCP_MOD_NAME "zfcp"
|
||||
#define ZFCP_CCWDRV_NAME "zfcp"
|
||||
|
||||
struct devtype;
|
||||
|
||||
extern struct devtype zfcp_devtype;
|
||||
|
||||
exit_code_t zfcp_check_allow_lun_scan(int *, config_t);
|
||||
|
||||
#endif /* ZFCP_H */
|
||||
21
zdev/include/zfcp_host.h
Normal file
21
zdev/include/zfcp_host.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef ZFCP_HOST_H
|
||||
#define ZFCP_HOST_H
|
||||
|
||||
#include "exit_code.h"
|
||||
|
||||
struct subtype;
|
||||
|
||||
extern struct subtype zfcp_host_subtype;
|
||||
|
||||
exit_code_t zfcp_host_check_npiv(const char *, int *);
|
||||
|
||||
#endif /* ZFCP_HOST_H */
|
||||
35
zdev/include/zfcp_lun.h
Normal file
35
zdev/include/zfcp_lun.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef ZFCP_LUN_H
|
||||
#define ZFCP_LUN_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "ccw.h"
|
||||
|
||||
#define ZFCP_LUN_NAME "zfcp-lun"
|
||||
#define SCSI_ATTR_PREFIX "scsi_dev"
|
||||
|
||||
struct subtype;
|
||||
struct namespace;
|
||||
|
||||
extern struct subtype zfcp_lun_subtype;
|
||||
extern struct namespace zfcp_lun_namespace;
|
||||
|
||||
struct zfcp_lun_devid {
|
||||
struct ccw_devid fcp_dev;
|
||||
uint64_t wwpn;
|
||||
uint64_t lun;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
exit_code_t zfcp_lun_parse_devid(struct zfcp_lun_devid *, const char *, err_t);
|
||||
int zfcp_lun_cmp_devids(struct zfcp_lun_devid *, struct zfcp_lun_devid *);
|
||||
char *zfcp_lun_devid_to_str(struct zfcp_lun_devid *);
|
||||
|
||||
#endif /* ZFCP_LUN_H */
|
||||
13
zdev/man/Makefile
Normal file
13
zdev/man/Makefile
Normal file
@@ -0,0 +1,13 @@
|
||||
# Common definitions
|
||||
include ../../common.mak
|
||||
|
||||
all:
|
||||
|
||||
install:
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man8
|
||||
$(INSTALL) -m 644 -c chzdev.8 $(DESTDIR)$(MANDIR)/man8
|
||||
$(INSTALL) -m 644 -c lszdev.8 $(DESTDIR)$(MANDIR)/man8
|
||||
|
||||
clean:
|
||||
|
||||
.PHONY: all clean
|
||||
791
zdev/man/chzdev.8
Normal file
791
zdev/man/chzdev.8
Normal file
@@ -0,0 +1,791 @@
|
||||
.\" Copyright 2017 IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
.\" Macro for inserting an option description prologue.
|
||||
.\" .OD <long> [<short>] [args]
|
||||
.de OD
|
||||
. ds args "
|
||||
. if !'\\$3'' .as args \fI\\$3\fP
|
||||
. if !'\\$4'' .as args \\$4
|
||||
. if !'\\$5'' .as args \fI\\$5\fP
|
||||
. if !'\\$6'' .as args \\$6
|
||||
. if !'\\$7'' .as args \fI\\$7\fP
|
||||
. PD 0
|
||||
. if !'\\$2'' .IP "\fB\-\\$2\fP \\*[args]" 4
|
||||
. if !'\\$1'' .IP "\fB\-\-\\$1\fP \\*[args]" 4
|
||||
. PD
|
||||
..
|
||||
.\" Macro for inserting code line.
|
||||
.\" .CL <text>
|
||||
.de CL
|
||||
. ds pfont \fP
|
||||
. nh
|
||||
. na
|
||||
. ft CW
|
||||
\\$*
|
||||
. ft \\*[pfont]
|
||||
. ad
|
||||
. hy
|
||||
. br
|
||||
..
|
||||
.\" Macro for inserting a man page reference.
|
||||
.\" .MP man-page section [suffix]
|
||||
.de MP
|
||||
. nh
|
||||
. na
|
||||
. BR \\$1 (\\$2)\\$3
|
||||
. ad
|
||||
. hy
|
||||
..
|
||||
.
|
||||
.TH chzdev 8 "Jan 2016" s390-tools chzdev
|
||||
.
|
||||
.SH NAME
|
||||
chzdev - Configure z Systems specific devices
|
||||
.
|
||||
.
|
||||
.SH SYNOPSIS
|
||||
.B "chzdev "
|
||||
.I "TYPE "
|
||||
.I "DEVICE "
|
||||
[
|
||||
.I "SELECTION "
|
||||
.I "SETTINGS "
|
||||
.I "ACTIONS "
|
||||
.I "OPTIONS "
|
||||
]
|
||||
.
|
||||
.
|
||||
.SH DESCRIPTION
|
||||
.B chzdev
|
||||
is a tool for configuring devices and device drivers which are specific to the
|
||||
IBM z Systems platform. Supported device types include storage devices (DASD
|
||||
and zFCP) and networking devices (QETH, CTC and LCS).
|
||||
.PP
|
||||
.
|
||||
.SS "Configurations"
|
||||
You can apply configuration changes to the active configuration of the
|
||||
currently running system, or to the persistent configuration stored in
|
||||
configuration files:
|
||||
.IP \(bu 3
|
||||
Changes to the
|
||||
.B "active configuration"
|
||||
are effective immediately. They are lost on reboot, or when a device becomes
|
||||
unavailable, or when a device driver is unloaded.
|
||||
.PP
|
||||
.IP \(bu 3
|
||||
Changes to the
|
||||
.B "persistent configuration"
|
||||
are applied when the system boots, or when a device becomes available, or
|
||||
when a device driver is loaded.
|
||||
.PP
|
||||
By default chzdev applies changes to both the active and the persistent
|
||||
configuration.
|
||||
.PP
|
||||
.
|
||||
.SS "Tool actions"
|
||||
chzdev supports multiple
|
||||
.B tool actions
|
||||
such as
|
||||
.IP \(bu 3
|
||||
Enable a device.
|
||||
.PP
|
||||
.IP \(bu 3
|
||||
Disable a device.
|
||||
.PP
|
||||
.IP \(bu 3
|
||||
Export and import configuration data to and from a file.
|
||||
.PP
|
||||
.IP \(bu 3
|
||||
Display a list of available device types and attributes.
|
||||
.PP
|
||||
You can trigger these actions by specifying one of the command line options
|
||||
listed in the ACTIONS section.
|
||||
.PP
|
||||
.
|
||||
.
|
||||
.SH TYPE
|
||||
chzdev uses
|
||||
.B "device type"
|
||||
names to distinguish devices by their respective type.
|
||||
A device type typically corresponds to a device driver, though there are cases
|
||||
where multiple device types are provided for the same driver (for example both
|
||||
device types "dasd-eckd" and "dasd-fba" are related to the DASD device driver).
|
||||
.PP
|
||||
You can use the
|
||||
.B TYPE
|
||||
positional argument of the chzdev tool to restrict the scope of a tool action
|
||||
to the specified device type:
|
||||
.IP \(bu 3
|
||||
Specify a device type and optionally a device ID to only work on
|
||||
devices with matching type and ID
|
||||
.PP
|
||||
.IP \(bu 3
|
||||
Specify a device type together with option
|
||||
.nh
|
||||
\-\-type
|
||||
.hy
|
||||
to manage the configuration of the device type itself
|
||||
.PP
|
||||
.B Note:
|
||||
Specify the full device type when configuring a device by
|
||||
ID. Otherwise the same device ID might accidentally match other devices of a
|
||||
different type.
|
||||
.PP
|
||||
To get a list of supported device types, use the
|
||||
.nh
|
||||
\-\-list\-types
|
||||
.hy
|
||||
action.
|
||||
.PP
|
||||
.
|
||||
.
|
||||
.SH DEVICE
|
||||
Use the
|
||||
.B DEVICE
|
||||
positional argument of chzdev to select a single device or a range of devices
|
||||
by device ID as target for a tool action. To select a range of devices, specify
|
||||
the ID of the first and the last device in the range separated by a hyphen (-).
|
||||
Separate multiple IDs or ranges with a comma (,).
|
||||
.PP
|
||||
.B Example:
|
||||
.CL chzdev dasd 1000,2000-2010 -e
|
||||
.PP
|
||||
.
|
||||
.
|
||||
.SH SELECTION
|
||||
In addition to specifying devices by their ID you can also select devices by
|
||||
their state or by the Linux functions they provide.
|
||||
.PP
|
||||
chzdev supports the following device states:
|
||||
.PP
|
||||
.TP 12
|
||||
.B online
|
||||
The device is enabled in the active configuation and set up to provide a Linux
|
||||
function such as a block device like /dev/dasda, or a networking interface like
|
||||
eth0.
|
||||
.PP
|
||||
.TP 12
|
||||
.B offline
|
||||
The device is not enabled in the active configuration.
|
||||
.PP
|
||||
.TP 12
|
||||
.B existing
|
||||
The device is present in the active configuration.
|
||||
.PP
|
||||
.TP 12
|
||||
.B configured
|
||||
There is a persistent configuration for this device.
|
||||
.PP
|
||||
.TP 12
|
||||
.B failed
|
||||
The driver of a device detected errors that may cause the device to not function
|
||||
properly.
|
||||
.PP
|
||||
.
|
||||
Selection works in two stages:
|
||||
.PP
|
||||
.IP 1. 3
|
||||
All devices specified by ID or by options
|
||||
.nh
|
||||
\-\-all, \-\-by\-interface, \-\-by\-node or \-\-by\-path
|
||||
.hy
|
||||
are considered for selection.
|
||||
.PP
|
||||
.IP 2. 3
|
||||
Of the considered devices, only those matching the specified state options
|
||||
.nh
|
||||
\-\-online, \-\-offline, \-\-existing, \-\-configured and \-\-failed
|
||||
.hy
|
||||
are selected.
|
||||
.PP
|
||||
.
|
||||
.
|
||||
.SS "Selection options"
|
||||
.OD all "" ""
|
||||
Select all existing and configured devices.
|
||||
.PP
|
||||
.
|
||||
.OD by-attrib "" "KEY" "=" "VALUE"
|
||||
.OD by-attrib "" "KEY" "!=" "VALUE"
|
||||
|
||||
Select devices with specified attribute value.
|
||||
|
||||
When specified as
|
||||
.IR KEY = VALUE ,
|
||||
selects all devices that provide an attribute named
|
||||
.I KEY
|
||||
with a value of
|
||||
.IR VALUE .
|
||||
When specified as
|
||||
.IR KEY != VALUE ,
|
||||
selects all devices that
|
||||
.B don't
|
||||
provide an attribute named
|
||||
.I KEY
|
||||
with a value of
|
||||
.IR VALUE .
|
||||
.PP
|
||||
.
|
||||
.OD by-interface "" "NAME"
|
||||
Select device providing network interface, e.g. eth0.
|
||||
|
||||
The
|
||||
.I NAME
|
||||
parameter must be the name of an existing networking interface.
|
||||
.PP
|
||||
.
|
||||
.OD by-node "" "NODE"
|
||||
Select device providing device node, e.g. /dev/sda.
|
||||
|
||||
The
|
||||
.I NODE
|
||||
parameter must be the path to a block device or character device special file.
|
||||
|
||||
.B Note:
|
||||
If
|
||||
.I NODE
|
||||
is the device node for a logical device (such as a device mapper device),
|
||||
chzdev will try to resolve the corresponding physical device nodes. The
|
||||
.MP lsblk 8
|
||||
tool must be available for this resolution to work.
|
||||
.PP
|
||||
.
|
||||
.OD by-path "" "PATH"
|
||||
Select device providing file system path, e.g. /usr.
|
||||
|
||||
The
|
||||
.I PATH
|
||||
parameter can be the mount point of a mounted file system, or a path
|
||||
on that file system.
|
||||
|
||||
.B Note:
|
||||
If the file system that provides
|
||||
.I PATH
|
||||
is stored on multiple physical devices or on a subvolume (such as supported by
|
||||
btrfs) chzdev will try to resolve the corresponding physical device nodes. The
|
||||
.MP lsblk 8
|
||||
and
|
||||
.MP findmnt 8
|
||||
tools must be available and in the case of multiple physical devices the file
|
||||
system must provide a valid UUID for this resolution to work.
|
||||
.PP
|
||||
.
|
||||
.OD configured "" ""
|
||||
Select all devices for which a persistent configuration exists.
|
||||
.PP
|
||||
.
|
||||
.OD existing "" ""
|
||||
Select all devices that are present in the active configuration.
|
||||
.PP
|
||||
.
|
||||
.OD online "" ""
|
||||
Select devices that are online.
|
||||
|
||||
An online device is a device in the active configuration that is set up
|
||||
to provide its associated Linux function (such as a block device or networking
|
||||
interface).
|
||||
.PP
|
||||
.
|
||||
.OD offline "" ""
|
||||
Select devices that are offline.
|
||||
|
||||
|
||||
An offline device is a device in the active configuration that is
|
||||
.B not
|
||||
set up to provide its associated Linux function.
|
||||
.PP
|
||||
.
|
||||
.OD failed "" ""
|
||||
Select devices for which errors were detected.
|
||||
|
||||
A failed device is a device for which its driver detected an error condition
|
||||
that may cause the device to not correctly perform its function. You can use
|
||||
the \-\-info option of the lszdev tool to get more details on the detected
|
||||
errors.
|
||||
|
||||
.B Example:
|
||||
.CL lszdev \-\-failed \-\-info
|
||||
.PP
|
||||
.
|
||||
.
|
||||
.SH SETTINGS
|
||||
A
|
||||
.B setting
|
||||
is a positional argument of chzdev in the form KEY=VALUE. It can be used to
|
||||
configure a specific named attribute (KEY) of a device or device type with
|
||||
the provided VALUE.
|
||||
.PP
|
||||
.B Example:
|
||||
.CL chzdev dasd 1000 use_diag=1
|
||||
.PP
|
||||
You can use the
|
||||
.nh
|
||||
\-\-list\-attributes
|
||||
.hy
|
||||
option to display a list of available
|
||||
attributes and
|
||||
.nh
|
||||
\-\-help\-attribute
|
||||
.hy
|
||||
to get more detailed information about
|
||||
a specific attribute.
|
||||
.PP
|
||||
.
|
||||
.
|
||||
.SH ACTIONS
|
||||
You can use one of the action options listed below to specify the
|
||||
.B main tool action
|
||||
that chzdev should perform. Alternatively you can specify a setting
|
||||
or one of \-\-remove or \-\-remove\-all to modify device and device type
|
||||
settings.
|
||||
.PP
|
||||
.
|
||||
.
|
||||
.SS "Action options"
|
||||
.OD apply "" ""
|
||||
Apply persistent settings to active configuration.
|
||||
|
||||
Applies the persistent configuration of all selected devices and
|
||||
device types to the active configuration.
|
||||
.PP
|
||||
.
|
||||
.OD disable "d" ""
|
||||
Disable device.
|
||||
|
||||
.B Active configuration:
|
||||
Disables the selected devices by reverting the configuration steps necessary
|
||||
to enable a device.
|
||||
|
||||
.B Persistent configuration:
|
||||
Removes configuration files and settings associated with the
|
||||
selected device.
|
||||
.PP
|
||||
.
|
||||
.OD enable "e" ""
|
||||
Enable device.
|
||||
|
||||
The enable action instructs chzdev to perform any steps necessary for the
|
||||
selected devices to provide their corresponding Linux function (such as a block
|
||||
device or networking interface). Examples for such steps include setting a CCW
|
||||
device online, removing a CCW device from the CIO blacklist, and creating a CCW
|
||||
group device.
|
||||
|
||||
.B Active configuration:
|
||||
Performs all initial setup steps required for a device to provide its Linux
|
||||
function.
|
||||
|
||||
.B Persistent configuration:
|
||||
Creates configuration files and settings associated with the selected devices.
|
||||
.PP
|
||||
.
|
||||
.OD export "" "FILENAME" "|-"
|
||||
Export configuration data to a text file.
|
||||
|
||||
Writes configuration data for all selected devices and device types to
|
||||
.IR FILENAME .
|
||||
If a single hyphen ("-") is specified instead of an actual file name, data is
|
||||
written to the standard output stream.
|
||||
|
||||
The output format of this action is suitable for use with the
|
||||
.nh
|
||||
\-\-import
|
||||
.hy
|
||||
action of chzdev.
|
||||
.PP
|
||||
.
|
||||
.OD help "h" ""
|
||||
Print usage information, then exit.
|
||||
.PP
|
||||
.
|
||||
.OD help-attribute "H" ""
|
||||
Show detailed help on specified attribute.
|
||||
|
||||
Displays detailed information about the attributes specified as positional
|
||||
command line arguments. Information displayed includes a description of
|
||||
the attribute function and optionally a list of accepted values and the
|
||||
default value.
|
||||
.PP
|
||||
.
|
||||
.OD import "" "FILENAME" "|-"
|
||||
Import configuration data from a text file.
|
||||
|
||||
Reads configuration data from FILENAME and applies it. If a single hyphen ("-")
|
||||
is specified as FILENAME data is read from the standard input stream. The
|
||||
input format of the data read must be the same format as produced by the
|
||||
chzdev \-\-export action.
|
||||
|
||||
By default all configuration data that is read is also applied. To reduce the
|
||||
scope of imported configuration data, you can select specific devices, a device
|
||||
type, or define whether only data for the active or persistent configuration
|
||||
should be imported.
|
||||
.PP
|
||||
.
|
||||
.OD list-attributes "l" ""
|
||||
List attributes.
|
||||
|
||||
Lists all supported device or device type attributes, including a short
|
||||
description. You can use the
|
||||
.nh
|
||||
\-\-help\-attribute
|
||||
.hy
|
||||
action to get more detailed information about an attribute.
|
||||
.PP
|
||||
.
|
||||
.OD list-types "L" ""
|
||||
List supported device types.
|
||||
|
||||
Lists the name and a short description for all device types supported by
|
||||
chzdev.
|
||||
.PP
|
||||
.
|
||||
.OD version "v" ""
|
||||
Print version information, then exit.
|
||||
.PP
|
||||
.
|
||||
.
|
||||
.SH OPTIONS
|
||||
.OD active "a" ""
|
||||
Apply changes to the active configuration only.
|
||||
|
||||
Restricts configuration changes to the active configuration. As a result, the
|
||||
persistent configuration is not changed unless
|
||||
.nh
|
||||
\-\-persistent
|
||||
.hy
|
||||
was also specified.
|
||||
|
||||
.B Note:
|
||||
Changes to the active configuration are effective immediately. They are lost
|
||||
on reboot, or when a device becomes unavailable, or when a device driver is
|
||||
unloaded.
|
||||
.PP
|
||||
.
|
||||
.OD base "" "PATH" | "KEY" = "VALUE"
|
||||
Change file system paths used to access files.
|
||||
|
||||
If
|
||||
.I PATH
|
||||
is specified without an equal sign (=), it is used as base path for accessing
|
||||
files in the active and persistent configuration.
|
||||
|
||||
If the specified parameter is in KEY=VALUE format, only those paths
|
||||
that begin with
|
||||
.I KEY
|
||||
are modified.
|
||||
For these paths, the initial
|
||||
.I KEY
|
||||
portion is replaced with
|
||||
.IR VALUE .
|
||||
|
||||
.B Example:
|
||||
.CL chzdev -dasd-eckd 1000 -e -p --base /etc=/mnt/etc
|
||||
.PP
|
||||
.
|
||||
.OD dry-run "" ""
|
||||
Print output without performing configuration actions.
|
||||
|
||||
Performs action processing and displays tool output without changing the
|
||||
configuration of any devices or device types. When combined with
|
||||
.nh
|
||||
\-\-verbose,
|
||||
.hy
|
||||
details about skipped configuration steps are displayed.
|
||||
.PP
|
||||
.
|
||||
.OD force "f" ""
|
||||
Override safety checks.
|
||||
|
||||
Overrides safety checks and confirmation questions. These checks include:
|
||||
.RS 8
|
||||
.IP \(bu 3
|
||||
Selection of more than 256 devices
|
||||
.PP
|
||||
.IP \(bu 3
|
||||
Configuring unknown attributes
|
||||
.PP
|
||||
.IP \(bu 3
|
||||
Combining apparently inconsistent settings
|
||||
.PP
|
||||
.RE
|
||||
.
|
||||
.OD no-root-update "" ""
|
||||
Skip root device update.
|
||||
|
||||
Skips any additional steps that are required to make changes to the root
|
||||
device configuration persistent. Typically such steps include rebuilding the
|
||||
initial RAM disk, or modifying the kernel command line.
|
||||
.PP
|
||||
.
|
||||
.OD persistent "p" ""
|
||||
Apply changes to persistent configuration only.
|
||||
|
||||
The persistent configuration is put into effect when the system boots, or
|
||||
when a device becomes available, or when a device driver is loaded.
|
||||
.PP
|
||||
.
|
||||
.OD quiet "q" ""
|
||||
Print only minimal run-time information.
|
||||
.PP
|
||||
.
|
||||
.OD remove "r" "ATTRIB"
|
||||
Remove setting for attribute
|
||||
.IR ATTRIB .
|
||||
|
||||
.B Persistent configuration:
|
||||
Removes any setting for the specified attribute. As a result, the attribute will
|
||||
keep its initial value when the device or device type is configured.
|
||||
|
||||
.B Active configuration:
|
||||
For attributes that maintain a list of values written to them, clears all values
|
||||
for that list.
|
||||
|
||||
.B Note:
|
||||
Some attributes are mandatory and cannot be removed.
|
||||
.PP
|
||||
.
|
||||
.OD remove-all "R" ""
|
||||
Remove settings for all attributes.
|
||||
|
||||
.B Persistent configuration:
|
||||
Removes all non-mandatory attribute settings. As a result, these attributes will
|
||||
keep their initial value when the device or device type is configured.
|
||||
|
||||
.B Active configuration:
|
||||
For all attributes that maintain a list of values written to them, clears all
|
||||
values for that list.
|
||||
|
||||
.B Note:
|
||||
Some attributes are mandatory and cannot be removed.
|
||||
.PP
|
||||
.
|
||||
.OD type "t" ""
|
||||
Select device type as target for actions.
|
||||
|
||||
Selects a device type as target for a configuration or query action.
|
||||
.PP
|
||||
.
|
||||
.OD verbose "V" ""
|
||||
Print additional run-time information.
|
||||
.PP
|
||||
.
|
||||
.OD yes "y" ""
|
||||
Answer all confirmation questions with 'yes'.
|
||||
.PP
|
||||
.
|
||||
.
|
||||
.SH "EXAMPLES"
|
||||
.B Enable DASD 0.0.8000 and create persistent configuration:
|
||||
.RS 4
|
||||
.CL chzdev \-e dasd 8000
|
||||
.RE
|
||||
.PP
|
||||
.
|
||||
.B Enable zFCP LUN and create persistent configuration:
|
||||
.RS 4
|
||||
.CL chzdev \-e zfcp-lun 0.0.1724:0x50050763070ba5e2:0x4006404e00000000
|
||||
.RE
|
||||
.PP
|
||||
.
|
||||
.B Enable QETH device without creating a persistent configuration:
|
||||
.RS 4
|
||||
.CL chzdev \-e \-a qeth 0.0.a000:0.0.a001:0.0.a002
|
||||
.RE
|
||||
.PP
|
||||
.
|
||||
.B Get help on QETH device attribute:
|
||||
.RS 4
|
||||
.CL chzdev qeth \-\-help\-attribute layer2
|
||||
.RE
|
||||
.PP
|
||||
.
|
||||
.B Change dasd device type parameter:
|
||||
.RS 4
|
||||
.CL chzdev dasd \-\-type eer_pages=14
|
||||
.RE
|
||||
.PP
|
||||
.
|
||||
.B Remove persistent use_diag setting of dasd 0.0.8000:
|
||||
.RS 4
|
||||
.CL chzdev dasd 8000 \-\-remove use_diag --persistent
|
||||
.RE
|
||||
.PP
|
||||
.
|
||||
.B Export configuration data for all zFCP LUNs to file.
|
||||
.RS 4
|
||||
.CL chzdev zfcp\-lun \-\-all \-\-export config.txt
|
||||
.RE
|
||||
.PP
|
||||
.
|
||||
.B Persistently configure root device:
|
||||
.RS 4
|
||||
.CL chzdev \-\-by\-path / \-\-persistent
|
||||
.RE
|
||||
.PP
|
||||
.
|
||||
.B Disable device that provides networking interface eth0:
|
||||
.RS 4
|
||||
.CL chzdev \-\-by\-interface eth0 \-\-active
|
||||
.RE
|
||||
.PP
|
||||
.
|
||||
.
|
||||
.SH "EXIT CODES"
|
||||
.TP
|
||||
.B 0
|
||||
Program finished successfully
|
||||
.PP
|
||||
.TP
|
||||
.B 1
|
||||
Usage error
|
||||
.PP
|
||||
.TP
|
||||
.B 2
|
||||
Unknown device type specified
|
||||
.PP
|
||||
.TP
|
||||
.B 3
|
||||
Device not found
|
||||
.PP
|
||||
.TP
|
||||
.B 4
|
||||
Unknown attribute specified
|
||||
.PP
|
||||
.TP
|
||||
.B 5
|
||||
Invalid device type specified
|
||||
.PP
|
||||
.TP
|
||||
.B 6
|
||||
Invalid attribute value specified
|
||||
.PP
|
||||
.TP
|
||||
.B 7
|
||||
Setting not found
|
||||
.PP
|
||||
.TP
|
||||
.B 8
|
||||
Empty selection
|
||||
.PP
|
||||
.TP
|
||||
.B 9
|
||||
Invalid configuration
|
||||
.PP
|
||||
.TP
|
||||
.B 10
|
||||
Invalid device ID specified
|
||||
.PP
|
||||
.TP
|
||||
.B 11
|
||||
Incomplete device ID specified
|
||||
.PP
|
||||
.TP
|
||||
.B 12
|
||||
Configuration data not found
|
||||
.PP
|
||||
.TP
|
||||
.B 13
|
||||
Unknown column specified
|
||||
.PP
|
||||
.TP
|
||||
.B 14
|
||||
None or incomplete type specified
|
||||
.PP
|
||||
.TP
|
||||
.B 15
|
||||
A run-time error occurred
|
||||
.PP
|
||||
.TP
|
||||
.B 16
|
||||
Operation aborted on user request
|
||||
.PP
|
||||
.TP
|
||||
.B 17
|
||||
Error while applying setting
|
||||
.PP
|
||||
.TP
|
||||
.B 18
|
||||
File format error
|
||||
.PP
|
||||
.TP
|
||||
.B 19
|
||||
Kernel module is in use
|
||||
.PP
|
||||
.TP
|
||||
.B 20
|
||||
Kernel module could not be unloaded
|
||||
.PP
|
||||
.TP
|
||||
.B 21
|
||||
Kernel module could not be loaded
|
||||
.PP
|
||||
.TP
|
||||
.B 22
|
||||
Not enough available memory
|
||||
.PP
|
||||
.TP
|
||||
.B 23
|
||||
FCP device not found
|
||||
.PP
|
||||
.TP
|
||||
.B 24
|
||||
Invalid WWPN specified
|
||||
.PP
|
||||
.TP
|
||||
.B 25
|
||||
WWPN not found
|
||||
.PP
|
||||
.TP
|
||||
.B 26
|
||||
Invalid LUN specified
|
||||
.PP
|
||||
.TP
|
||||
.B 27
|
||||
SCSI device not found
|
||||
.PP
|
||||
.TP
|
||||
.B 28
|
||||
CCW group device: CCW device not found
|
||||
.PP
|
||||
.TP
|
||||
.B 29
|
||||
CCW group device: CCW devices are not a valid group
|
||||
.PP
|
||||
.TP
|
||||
.B 30
|
||||
CCW group device: CCW device already grouped
|
||||
.PP
|
||||
.TP
|
||||
.B 31
|
||||
CCW group device: Grouping failed
|
||||
.PP
|
||||
.TP
|
||||
.B 32
|
||||
CCW group device: Ungrouping failed
|
||||
.PP
|
||||
.TP
|
||||
.B 99
|
||||
An internal error occurred
|
||||
.PP
|
||||
.
|
||||
.
|
||||
.SH FILES
|
||||
.TP
|
||||
/etc/udev/rules.d/
|
||||
chzdev creates udev rules to store the persistent configuration of devices
|
||||
in this directory. File names start with "41-".
|
||||
.TP
|
||||
/etc/modprobe.d/
|
||||
chzdev creates modprobe configuration files to store the persistent
|
||||
configuration of certain device types in this directory. File names start
|
||||
with "s390x-".
|
||||
.
|
||||
.
|
||||
.SH "SEE ALSO"
|
||||
.MP lszdev 8 ,
|
||||
.MP lscss 8 ,
|
||||
.MP lsdasd 8 ,
|
||||
.MP lszfcp 8 ,
|
||||
.MP lsqeth 8 ,
|
||||
.MP lsblk 8 .
|
||||
441
zdev/man/lszdev.8
Normal file
441
zdev/man/lszdev.8
Normal file
@@ -0,0 +1,441 @@
|
||||
.\" Copyright 2017 IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
.\" Macro for inserting an option description prologue.
|
||||
.\" .OD <long> [<short>] [args]
|
||||
.de OD
|
||||
. ds args "
|
||||
. if !'\\$3'' .as args \fI\\$3\fP
|
||||
. if !'\\$4'' .as args \\$4
|
||||
. if !'\\$5'' .as args \fI\\$5\fP
|
||||
. if !'\\$6'' .as args \\$6
|
||||
. if !'\\$7'' .as args \fI\\$7\fP
|
||||
. PD 0
|
||||
. if !'\\$2'' .IP "\fB\-\\$2\fP \\*[args]" 4
|
||||
. if !'\\$1'' .IP "\fB\-\-\\$1\fP \\*[args]" 4
|
||||
. PD
|
||||
..
|
||||
.\" Macro for inserting code line.
|
||||
.\" .CL <text>
|
||||
.de CL
|
||||
. ds pfont \fP
|
||||
. nh
|
||||
. na
|
||||
. ft CW
|
||||
\\$*
|
||||
. ft \\*[pfont]
|
||||
. ad
|
||||
. hy
|
||||
. br
|
||||
..
|
||||
.\" Macro for inserting a man page reference.
|
||||
.\" .MP man-page section [suffix]
|
||||
.de MP
|
||||
. nh
|
||||
. na
|
||||
. BR \\$1 (\\$2)\\$3
|
||||
. ad
|
||||
. hy
|
||||
..
|
||||
.
|
||||
.TH lszdev 8 "Jan 2016" s390-tools lszdev
|
||||
.
|
||||
.SH NAME
|
||||
lszdev - Display configuration of z Systems specific devices
|
||||
.
|
||||
.
|
||||
.SH SYNOPSIS
|
||||
.B "lszdev "
|
||||
[
|
||||
.I "TYPE "
|
||||
.I "DEVICE "
|
||||
.I "SELECTION "
|
||||
.I "ACTIONS "
|
||||
.I "OPTIONS "
|
||||
]
|
||||
.
|
||||
.
|
||||
.SH DESCRIPTION
|
||||
.B lszdev
|
||||
displays the configuration of devices and device drivers that
|
||||
are specific to IBM z Systems. Supported device types include
|
||||
storage devices (DASD and zFCP) and networking devices (QETH, CTC and LCS).
|
||||
.PP
|
||||
.
|
||||
.PP
|
||||
lszdev can produce output in either list format or detailed format.
|
||||
With no further options specified, the output format defaults to list format.
|
||||
.
|
||||
.
|
||||
.SS "Configurations"
|
||||
There are two sources for configuration information: the active configuration
|
||||
of the currently running system, and the persistent configuration stored in
|
||||
configuration files.
|
||||
.PP
|
||||
By default lszdev displays information from both the active and the persistent
|
||||
configuration.
|
||||
.PP
|
||||
.
|
||||
.
|
||||
.SH TYPE
|
||||
lszdev uses
|
||||
.B "device type"
|
||||
names to distinguish devices by their respective type.
|
||||
A device type typically corresponds to a device driver, though there are cases
|
||||
where multiple device types are provided for the same driver (for example both
|
||||
device types "dasd-eckd" and "dasd-fba" are related to the DASD device driver).
|
||||
.PP
|
||||
You can use the
|
||||
.B TYPE
|
||||
positional argument to restrict output to the specified device type:
|
||||
.IP \(bu 3
|
||||
Specify a device type and optionally a device ID to only display data for
|
||||
devices with matching type and ID
|
||||
.PP
|
||||
.IP \(bu 3
|
||||
Specify a device type together with the
|
||||
.nh
|
||||
\-\-type
|
||||
.hy
|
||||
option to display the configuration of the device type itself
|
||||
.PP
|
||||
To get a list of supported device types, use the
|
||||
.nh
|
||||
\-\-list\-types
|
||||
.hy
|
||||
action.
|
||||
.PP
|
||||
.
|
||||
.
|
||||
.SH DEVICE
|
||||
You can use the
|
||||
.B DEVICE
|
||||
positional argument to select a single device or a range of devices by device
|
||||
ID. To select a range of devices, specify
|
||||
the ID of the first and the last device in the range separated by a hyphen (-).
|
||||
Multiple IDs or ranges can be specified by separating ID specifications with a
|
||||
comma (,).
|
||||
.PP
|
||||
.B Example:
|
||||
.CL lszdev dasd 1000,2000-2010
|
||||
.PP
|
||||
.
|
||||
.
|
||||
.SH SELECTION
|
||||
In addition to specifying devices by their ID, you can also select devices by
|
||||
their state or by the Linux functions they provide.
|
||||
.PP
|
||||
lszdev supports the following device states:
|
||||
.PP
|
||||
.TP 12
|
||||
.B online
|
||||
The device is enabled and set up to provide a Linux function such as a
|
||||
block device like /dev/dasda, or a networking interface like eth0.
|
||||
.PP
|
||||
.TP 12
|
||||
.B offline
|
||||
The device is not enabled.
|
||||
.PP
|
||||
.TP 12
|
||||
.B existing
|
||||
The device is present in the active configuration.
|
||||
.PP
|
||||
.TP 12
|
||||
.B configured
|
||||
There is a persistent configuration for this device.
|
||||
.PP
|
||||
.TP 12
|
||||
.B failed
|
||||
The driver of a device detected errors that may cause the device to not function
|
||||
properly.
|
||||
.PP
|
||||
.
|
||||
Selection works in two stages:
|
||||
.PP
|
||||
.IP 1. 3
|
||||
All devices specified by ID or by options
|
||||
.nh
|
||||
\-\-all, \-\-by\-interface, \-\-by\-node or \-\-by\-path
|
||||
.hy
|
||||
are considered for selection.
|
||||
.PP
|
||||
.IP 2. 3
|
||||
Of the considered devices, only those matching the specified state options
|
||||
.nh
|
||||
\-\-online, \-\-offline, \-\-existing, \-\-configured and \-\-failed
|
||||
.hy
|
||||
are selected.
|
||||
.PP
|
||||
.
|
||||
.
|
||||
.SS "Selection options"
|
||||
.OD all "" ""
|
||||
Select all existing and configured devices.
|
||||
|
||||
This is the default when no other options are specified.
|
||||
.PP
|
||||
.
|
||||
.OD by-attrib "" "KEY" "=" "VALUE"
|
||||
.OD by-attrib "" "KEY" "!=" "VALUE"
|
||||
|
||||
Select devices with specified attribute value.
|
||||
|
||||
When specified as
|
||||
.IR KEY = VALUE ,
|
||||
selects all devices that provide an attribute named
|
||||
.I KEY
|
||||
with a value of
|
||||
.IR VALUE .
|
||||
When specified as
|
||||
.IR KEY != VALUE ,
|
||||
selects all devices that
|
||||
.B don't
|
||||
provide an attribute named
|
||||
.I KEY
|
||||
with a value of
|
||||
.IR VALUE .
|
||||
.PP
|
||||
.
|
||||
.OD by-interface "" "NAME"
|
||||
Select device providing network interface, e.g. eth0.
|
||||
|
||||
The
|
||||
.I NAME
|
||||
parameter must be the name of an existing networking interface.
|
||||
.PP
|
||||
.
|
||||
.OD by-node "" "NODE"
|
||||
Select device providing device node, e.g. /dev/sda.
|
||||
|
||||
The
|
||||
.I NODE
|
||||
parameter must be the path to a block device or character device special file.
|
||||
|
||||
.B Note:
|
||||
If
|
||||
.I NODE
|
||||
is the device node for a logical device (such as a device mapper device),
|
||||
lszdev will try to resolve the corresponding physical device nodes. The
|
||||
.MP lsblk 8
|
||||
tool must be available for this resolution to work.
|
||||
.PP
|
||||
.
|
||||
.OD by-path "" "PATH"
|
||||
Select device providing file system path, e.g. /usr.
|
||||
|
||||
The
|
||||
.I PATH
|
||||
parameter can be the mount point of a mounted file system, or a path
|
||||
on that file system.
|
||||
|
||||
.B Note:
|
||||
If the file system that provides
|
||||
.I PATH
|
||||
is stored on multiple physical devices or on a subvolume (such as supported by
|
||||
btrfs) lszdev will try to resolve the corresponding physical device nodes. The
|
||||
.MP lsblk 8
|
||||
and
|
||||
.MP findmnt 8
|
||||
tools must be available and in the case of multiple physical devices the file
|
||||
system must provide a valid UUID for this resolution to work.
|
||||
.PP
|
||||
.
|
||||
.OD configured "" ""
|
||||
Select all devices for which a persistent configuration exists.
|
||||
.PP
|
||||
.
|
||||
.OD existing "" ""
|
||||
Select all devices that are present in the active configuration.
|
||||
.PP
|
||||
.
|
||||
.OD online "" ""
|
||||
Select devices that are online.
|
||||
|
||||
An online device is a device in the active configuration that is set up
|
||||
to provide its associated Linux function (such as a block device or networking
|
||||
interface).
|
||||
.PP
|
||||
.
|
||||
.OD offline "" ""
|
||||
Select devices that are offline.
|
||||
|
||||
|
||||
An offline device is a device in the active configuration that is
|
||||
.B not
|
||||
set up to provide its associated Linux function.
|
||||
.PP
|
||||
.
|
||||
.OD failed "" ""
|
||||
Select devices for which errors were detected.
|
||||
|
||||
A failed device is a device for which its driver detected an error condition
|
||||
that may cause the device to not correctly perform its function. You can use
|
||||
the \-\-info option of the lszdev tool to get more details on the detected
|
||||
errors.
|
||||
|
||||
.B Example:
|
||||
.CL lszdev \-\-failed \-\-info
|
||||
.PP
|
||||
|
||||
.
|
||||
.
|
||||
.SH ACTIONS
|
||||
.OD help "h" ""
|
||||
Print usage information, then exit.
|
||||
.PP
|
||||
.
|
||||
.OD info "i" ""
|
||||
Display detailed information.
|
||||
|
||||
Displays detailed information about the configuration of the selected device
|
||||
or device type. Specifying \-\-info twice will display additional device
|
||||
information.
|
||||
.PP
|
||||
.
|
||||
.OD list-columns "l" ""
|
||||
List available output columns.
|
||||
|
||||
Lists all available columns for use with option \-\-columns.
|
||||
.PP
|
||||
.
|
||||
.OD list-types "L" ""
|
||||
List supported device types.
|
||||
|
||||
Lists the name and a short description for all device types supported by
|
||||
lszdev.
|
||||
.PP
|
||||
.
|
||||
.OD version "v" ""
|
||||
Print version information, then exit.
|
||||
.PP
|
||||
.
|
||||
.
|
||||
.SH OPTIONS
|
||||
.OD active "a" ""
|
||||
List information from the active configuration only.
|
||||
|
||||
Restricts output to information obtained from the active configuration, that
|
||||
is information from the running system.
|
||||
.PP
|
||||
.
|
||||
.OD base "" "PATH" | "KEY" = "VALUE"
|
||||
Change file system paths used to access files.
|
||||
|
||||
If
|
||||
.I PATH
|
||||
is specified without an equal sign (=), it is used as base path for accessing
|
||||
files in the active and persistent configuration.
|
||||
|
||||
If the specified parameter is in KEY=VALUE format, only those paths
|
||||
that begin with
|
||||
.I KEY
|
||||
are modified.
|
||||
For these paths, the initial
|
||||
.I KEY
|
||||
portion is replaced with
|
||||
.IR VALUE .
|
||||
|
||||
.B Example:
|
||||
.CL lszdev --persistent --base /etc=/mnt/etc
|
||||
.PP
|
||||
.
|
||||
.OD columns "c" "COLUMNS"
|
||||
Specify comma-separated list of columns to display.
|
||||
|
||||
You can change the columns shown in list output format by specifying column
|
||||
names as comma-separated list via the \-\-columns option. Note that \-\-columns
|
||||
cannot be specified together with \-\-info.
|
||||
|
||||
.B Example:
|
||||
.CL lszdev \-\-columns TYPE,ID
|
||||
|
||||
To get a list of supported column names, use the
|
||||
.nh
|
||||
\-\-list\-columns
|
||||
.hy
|
||||
action.
|
||||
.PP
|
||||
.
|
||||
.OD no-headings "n" ""
|
||||
Do not print column headings.
|
||||
|
||||
When displaying output in list format, this option can be specified to
|
||||
suppress the output of a heading row.
|
||||
.PP
|
||||
.
|
||||
.OD pairs "" ""
|
||||
Produce output in KEY="VALUE" format.
|
||||
|
||||
You can use this option to generate output in a format more suitable for
|
||||
processing by other programs. In this format, column values are prefixed
|
||||
with the name of the corresponding column. Values are enclosed in double
|
||||
quotation marks. Any quotation marks or slashes in the value string are
|
||||
escaped by placing a slash in front of it.
|
||||
.PP
|
||||
.
|
||||
.OD persistent "p" ""
|
||||
List information from the persistent configuration only.
|
||||
|
||||
Restricts output to information obtained from configuration files.
|
||||
.PP
|
||||
.
|
||||
.OD quiet "q" ""
|
||||
Print only minimal run-time information.
|
||||
.PP
|
||||
.
|
||||
.OD type "t" ""
|
||||
List information about device type.
|
||||
|
||||
Use this option to display configuration information of a device type instead
|
||||
of a device.
|
||||
.PP
|
||||
.
|
||||
.OD verbose "V" ""
|
||||
Print additional run-time information.
|
||||
.PP
|
||||
.
|
||||
.
|
||||
.SH EXAMPLES
|
||||
.B Display a list of all devices:
|
||||
.RS 4
|
||||
.CL lszdev
|
||||
.RE
|
||||
.PP
|
||||
.
|
||||
.B Return type and ID of root device in machine-readable format:
|
||||
.RS 4
|
||||
.CL lszdev \-\-columns TYPE,ID \-\-by\-path /
|
||||
.RE
|
||||
.PP
|
||||
.
|
||||
.B Display DASD driver settings:
|
||||
.RS 4
|
||||
.CL lszdev \-\-type dasd
|
||||
.RE
|
||||
.PP
|
||||
.
|
||||
.SH "EXIT CODES"
|
||||
lszdev returns a subset of the exit codes returned by the chzdev tool. See
|
||||
.MP chzdev 8
|
||||
for the detailed list.
|
||||
.
|
||||
.
|
||||
.SH FILES
|
||||
.TP
|
||||
/etc/udev/rules.d/
|
||||
lszdev reads udev rules representing the persistent configuration of devices
|
||||
from this directory. File names start with "41-".
|
||||
.TP
|
||||
/etc/modprobe.d/
|
||||
chzdev reads modprobe configuration files representing the persistent
|
||||
configuration of certain device types from this directory. File names start
|
||||
with "s390x-".
|
||||
.
|
||||
.
|
||||
.SH "SEE ALSO"
|
||||
.MP chzdev 8 ,
|
||||
.MP lsdasd 8 ,
|
||||
.MP lszfcp 8 ,
|
||||
.MP lsqeth 8 ,
|
||||
.MP lsblk 8 .
|
||||
100
zdev/src/Makefile
Normal file
100
zdev/src/Makefile
Normal file
@@ -0,0 +1,100 @@
|
||||
# Common definitions
|
||||
include ../../common.mak
|
||||
|
||||
ALL_CPPFLAGS += -I ../include -std=gnu99 -Wno-unused-parameter \
|
||||
-Wno-missing-field-initializers
|
||||
|
||||
# Core
|
||||
chzdev_objects += attrib.o chzdev.o device.o devnode.o devtype.o exit_code.o \
|
||||
export.o hash.o inuse.o misc.o namespace.o opts.o path.o \
|
||||
root.o select.o setting.o subtype.o table.o table_attribs.o \
|
||||
table_types.o net.o
|
||||
|
||||
# Devtype Helpers
|
||||
chzdev_objects += blkinfo.o ccw.o ccwgroup.o findmnt.o modprobe.o module.o \
|
||||
udev.o udev_ccw.o udev_ccwgroup.o iscsi.o
|
||||
|
||||
# DASD devtype
|
||||
chzdev_objects += dasd.o
|
||||
|
||||
# FCP devtype
|
||||
chzdev_objects += zfcp.o zfcp_host.o
|
||||
chzdev_objects += scsi.o udev_zfcp_lun.o zfcp_lun.o
|
||||
|
||||
# QETH devtype
|
||||
chzdev_objects += nic.o qeth.o qeth_auto.o
|
||||
|
||||
# CTC devtype
|
||||
chzdev_objects += ctc.o ctc_auto.o
|
||||
|
||||
# LCS devtype
|
||||
chzdev_objects += lcs.o lcs_auto.o
|
||||
|
||||
# Generic CCW devtype
|
||||
chzdev_objects += generic_ccw.o
|
||||
|
||||
|
||||
# Core
|
||||
lszdev_objects += attrib.o lszdev.o device.o devnode.o devtype.o exit_code.o \
|
||||
export.o hash.o inuse.o misc.o namespace.o opts.o path.o \
|
||||
root.o select.o setting.o subtype.o table.o table_types.o \
|
||||
net.o
|
||||
|
||||
# Devtype Helpers
|
||||
lszdev_objects += blkinfo.o ccw.o ccwgroup.o findmnt.o modprobe.o module.o \
|
||||
udev.o udev_ccw.o udev_ccwgroup.o iscsi.o
|
||||
|
||||
# DASD devtype
|
||||
lszdev_objects += dasd.o
|
||||
|
||||
# FCP devtype
|
||||
lszdev_objects += zfcp.o zfcp_host.o
|
||||
lszdev_objects += scsi.o udev_zfcp_lun.o zfcp_lun.o
|
||||
|
||||
# QETH devtype
|
||||
lszdev_objects += nic.o qeth.o qeth_auto.o
|
||||
|
||||
# CTC devtype
|
||||
lszdev_objects += ctc.o ctc_auto.o
|
||||
|
||||
# LCS devtype
|
||||
lszdev_objects += lcs.o lcs_auto.o
|
||||
|
||||
# Generic CCW devtype
|
||||
lszdev_objects += generic_ccw.o
|
||||
|
||||
all: chzdev lszdev
|
||||
|
||||
chzdev_usage.c: chzdev_usage.txt
|
||||
$(SED) $< >$@ -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/^/"/g' \
|
||||
-e 's/$$/\\n"/g'
|
||||
|
||||
.chzdev.o.d: chzdev_usage.c
|
||||
|
||||
lszdev_usage.c: lszdev_usage.txt
|
||||
$(SED) $< >$@ -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/^/"/g' \
|
||||
-e 's/$$/\\n"/g'
|
||||
|
||||
.lszdev.o.d: lszdev_usage.c
|
||||
|
||||
chzdev.o: chzdev_usage.c
|
||||
lszdev.o: lszdev_usage.c
|
||||
|
||||
libs = $(rootdir)/libutil/libutil.a
|
||||
|
||||
chzdev: $(chzdev_objects) $(libs)
|
||||
lszdev: $(lszdev_objects) $(libs)
|
||||
|
||||
install: chzdev
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -c chzdev $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -c lszdev $(DESTDIR)$(BINDIR)
|
||||
ifeq ($(HAVE_DRACUT),1)
|
||||
$(INSTALL) -m 755 zdev-root-update.dracut \
|
||||
$(DESTDIR)$(TOOLS_LIBDIR)/zdev-root-update
|
||||
endif
|
||||
|
||||
clean:
|
||||
@rm -f *.o chzdev lszdev chzdev_usage.c lszdev_usage.c gmon.out *.gcda *.gcno
|
||||
|
||||
.PHONY: all install clean
|
||||
281
zdev/src/attrib.c
Normal file
281
zdev/src/attrib.c
Normal file
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <ctype.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "attrib.h"
|
||||
#include "misc.h"
|
||||
|
||||
/* Parse the string in VAL as a number of notation N. If NUM is non-null
|
||||
* store the resulting number there and return true. Return false
|
||||
* in case of parse error. */
|
||||
static bool parse_num(struct notation *n, const char *val, long long *num)
|
||||
{
|
||||
int i;
|
||||
long long r;
|
||||
char d;
|
||||
|
||||
/* Check for octal number first in case of leading 0. */
|
||||
for (i = 0; val[i]; i++)
|
||||
if (isspace(val[i]))
|
||||
return false;
|
||||
if (n->oct && (strlen(val) > 1) && (val[0] == '0') && isdigit(val[1])) {
|
||||
if (sscanf(val, "%llo %c", (unsigned long long *) &r, &d) == 1)
|
||||
goto ok;
|
||||
}
|
||||
if (n->dec) {
|
||||
if (sscanf(val, "%lld %c", &r, &d) == 1)
|
||||
goto ok;
|
||||
}
|
||||
if (n->hex) {
|
||||
if (sscanf(val, "%llx %c", (unsigned long long *) &r, &d) == 1)
|
||||
goto ok;
|
||||
}
|
||||
if (n->oct) {
|
||||
if (sscanf(val, "%llo %c", (unsigned long long *) &r, &d) == 1)
|
||||
goto ok;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
ok:
|
||||
if (num)
|
||||
*num = r;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Check if the specified value is a number in the acceptable notations
|
||||
* which is equal to n->val. */
|
||||
static bool check_num(struct val_number *n, const char *val)
|
||||
{
|
||||
long long num;
|
||||
|
||||
if (!parse_num(&n->notation, val, &num))
|
||||
return false;
|
||||
if (num != n->val)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Check if the specified value is a number in the acceptable notations
|
||||
* which is greater or equal than n->val. */
|
||||
static bool check_num_ge(struct val_number *n, const char *val)
|
||||
{
|
||||
long long num;
|
||||
|
||||
if (!parse_num(&n->notation, val, &num))
|
||||
return false;
|
||||
if (num < n->val)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Check if the specified value is a number in the acceptable notations
|
||||
* which is between n->from and n->to (from and to inclusively). */
|
||||
static bool check_range(struct val_range *r, const char *val)
|
||||
{
|
||||
long long num;
|
||||
|
||||
if (!parse_num(&r->notation, val, &num))
|
||||
return false;
|
||||
if (num < r->from || num > r->to)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Check if value is acceptable for attribute. */
|
||||
bool attrib_check_value(struct attrib *attrib, const char *val)
|
||||
{
|
||||
struct accept_def *a;
|
||||
int i;
|
||||
|
||||
if (!attrib->accept)
|
||||
return true;
|
||||
|
||||
for (i = 0; attrib->accept[i].type != VAL_NONE; i++) {
|
||||
a = &attrib->accept[i];
|
||||
|
||||
switch (a->type) {
|
||||
case VAL_NUM:
|
||||
if (check_num(&a->content.number, val))
|
||||
return true;
|
||||
break;
|
||||
case VAL_NUM_GE:
|
||||
if (check_num_ge(&a->content.number, val))
|
||||
return true;
|
||||
break;
|
||||
case VAL_RANGE:
|
||||
if (check_range(&a->content.range, val))
|
||||
return true;
|
||||
break;
|
||||
case VAL_STRING:
|
||||
if (strcmp(a->content.string, val) == 0)
|
||||
return true;
|
||||
break;
|
||||
case VAL_FUNC:
|
||||
if (a->content.func(attrib, val))
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char *notation_to_str(struct notation *n)
|
||||
{
|
||||
if (n->dec && n->hex && n->oct)
|
||||
return " in decimal, hexadecimal or octal notation";
|
||||
if (n->dec && n->hex)
|
||||
return " in decimal or hexadecimal notation";
|
||||
if (n->dec && n->oct)
|
||||
return " in decimal or octal notation";
|
||||
if (n->hex && n->oct)
|
||||
return " in hexadecimal or octal notation";
|
||||
if (n->dec)
|
||||
return "";
|
||||
if (n->hex)
|
||||
return " in hexadecimal notation";
|
||||
if (n->oct)
|
||||
return " in octal notation";
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
#define pr_val(x, ...) do { if ((x) < 0) \
|
||||
delayed_info(__VA_ARGS__); \
|
||||
else \
|
||||
indent((x), __VA_ARGS__); } while (0)
|
||||
|
||||
/* List acceptable values for the specified attributes. If @ind is a negative
|
||||
* number, messages are printed using delayed_info(), otherwise messages
|
||||
* are indented by the corresponding number of blank characters. */
|
||||
void attrib_print_acceptable(struct attrib *attrib, int ind)
|
||||
{
|
||||
struct accept_def *a;
|
||||
int i;
|
||||
|
||||
if (!attrib->accept) {
|
||||
pr_val(ind, "All values are accepted\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; attrib->accept[i].type != VAL_NONE; i++) {
|
||||
a = &attrib->accept[i];
|
||||
|
||||
switch (a->type) {
|
||||
case VAL_NUM:
|
||||
pr_val(ind, "- Integer %lld%s\n",
|
||||
a->content.number.val,
|
||||
notation_to_str(&a->content.number.notation));
|
||||
break;
|
||||
case VAL_NUM_GE:
|
||||
pr_val(ind, "- Integers greater or equal to %lld%s\n",
|
||||
a->content.number.val,
|
||||
notation_to_str(&a->content.number.notation));
|
||||
break;
|
||||
case VAL_RANGE:
|
||||
pr_val(ind, "- Integers in the range %lld - %lld%s\n",
|
||||
a->content.range.from, a->content.range.to,
|
||||
notation_to_str(&a->content.range.notation));
|
||||
break;
|
||||
case VAL_STRING:
|
||||
pr_val(ind, "- Text string '%s'\n", a->content.string);
|
||||
break;
|
||||
case VAL_FUNC:
|
||||
pr_val(ind, "- Other value checked dynamically\n");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if provided value matches default value. */
|
||||
bool attrib_match_default(struct attrib *attrib, const char *val)
|
||||
{
|
||||
size_t l1, l2;
|
||||
|
||||
if (!attrib->defval)
|
||||
return false;
|
||||
/* Exact match. */
|
||||
if (strcmp(attrib->defval, val) == 0)
|
||||
return true;
|
||||
/* Match with new-line at the end. */
|
||||
l1 = strlen(attrib->defval);
|
||||
l2 = strlen(val);
|
||||
if (l2 == (l1 + 1) && val[l2 - 1] == '\n' &&
|
||||
strncmp(attrib->defval, val, l1) == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Find an attribute by name in a NULL-terminated array of attributes. */
|
||||
struct attrib *attrib_find(struct attrib **attribs, const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; attribs[i]; i++) {
|
||||
if (strcmp(attribs[i]->name, name) == 0)
|
||||
return attribs[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Return a replacement for @value read from attribute @attrib or NULL if
|
||||
* there is no replacement. */
|
||||
const char *attrib_map_value(struct attrib *attrib, const char *value)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!attrib->map)
|
||||
return NULL;
|
||||
for (i = 0; attrib->map[i].from; i++) {
|
||||
if (strcmp(value, attrib->map[i].from) == 0)
|
||||
return attrib->map[i].to;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Determine if attribute name @name starts with attribute name prefix
|
||||
* @prefix. */
|
||||
bool attrib_match_prefix(const char *name, const char *prefix)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
len = strlen(prefix);
|
||||
if (strncmp(name, prefix, len) == 0 && name[len] == '/')
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return attribute name @name without prefix @prefix. */
|
||||
const char *attrib_rem_prefix(const char *name, const char *prefix)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
len = strlen(prefix);
|
||||
if (strncmp(name, prefix, len) == 0 && name[len] == '/')
|
||||
name += len + 1;
|
||||
|
||||
return name;
|
||||
}
|
||||
442
zdev/src/blkinfo.c
Normal file
442
zdev/src/blkinfo.c
Normal file
@@ -0,0 +1,442 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "blkinfo.h"
|
||||
#include "devnode.h"
|
||||
#include "misc.h"
|
||||
|
||||
#define LSBLK_CMDLINE "lsblk -P -o NAME,MAJ:MIN,FSTYPE,UUID,MOUNTPOINT,PKNAME 2>/dev/null"
|
||||
|
||||
struct blkinfo {
|
||||
struct devnode *devnode;
|
||||
char *fstype;
|
||||
char *uuid;
|
||||
char *mountpoint;
|
||||
char *parent;
|
||||
};
|
||||
|
||||
static struct util_list *cached_blkinfos;
|
||||
|
||||
static struct blkinfo *blkinfo_new(const char *name, const char *majmin,
|
||||
const char *fstype, const char *uuid,
|
||||
const char *mountpoint, const char *parent)
|
||||
{
|
||||
struct blkinfo *blkinfo;
|
||||
unsigned int major, minor;
|
||||
|
||||
blkinfo = misc_malloc(sizeof(struct blkinfo));
|
||||
if (name && majmin) {
|
||||
if (sscanf(majmin, "%u:%u", &major, &minor) == 2) {
|
||||
blkinfo->devnode = devnode_new(BLOCKDEV, major, minor,
|
||||
name);
|
||||
}
|
||||
}
|
||||
if (fstype && *fstype)
|
||||
blkinfo->fstype = misc_strdup(fstype);
|
||||
if (uuid && *uuid)
|
||||
blkinfo->uuid = misc_strdup(uuid);
|
||||
if (mountpoint && *mountpoint)
|
||||
blkinfo->mountpoint = misc_strdup(mountpoint);
|
||||
if (parent && *parent)
|
||||
blkinfo->parent = misc_strdup(parent);
|
||||
|
||||
return blkinfo;
|
||||
}
|
||||
|
||||
static void blkinfo_free(struct blkinfo *blkinfo)
|
||||
{
|
||||
if (!blkinfo)
|
||||
return;
|
||||
free(blkinfo->devnode);
|
||||
free(blkinfo->fstype);
|
||||
free(blkinfo->uuid);
|
||||
free(blkinfo->mountpoint);
|
||||
free(blkinfo->parent);
|
||||
free(blkinfo);
|
||||
}
|
||||
|
||||
/* Used for debugging. */
|
||||
void blkinfo_print(struct blkinfo *blkinfo, int level)
|
||||
{
|
||||
printf("%*sblkinfo at %p\n", level, "", (void *) blkinfo);
|
||||
level += 2;
|
||||
if (blkinfo->devnode)
|
||||
devnode_print(blkinfo->devnode, level);
|
||||
if (blkinfo->fstype)
|
||||
printf("%*sfstype=%s\n", level, "", blkinfo->fstype);
|
||||
if (blkinfo->uuid)
|
||||
printf("%*suuid=%s\n", level, "", blkinfo->uuid);
|
||||
if (blkinfo->mountpoint)
|
||||
printf("%*smountpoint=%s\n", level, "", blkinfo->mountpoint);
|
||||
if (blkinfo->parent)
|
||||
printf("%*sparent=%s\n", level, "", blkinfo->parent);
|
||||
}
|
||||
|
||||
static char *isolate_keyword(char **line_ptr, const char *keyword)
|
||||
{
|
||||
char *start, *end;
|
||||
|
||||
start = strstr(*line_ptr, keyword);
|
||||
if (!start)
|
||||
return NULL;
|
||||
start += strlen(keyword);
|
||||
end = start;
|
||||
while (*end && *end != '"')
|
||||
end++;
|
||||
if (*end) {
|
||||
*end = 0;
|
||||
*line_ptr = end + 1;
|
||||
} else
|
||||
*line_ptr = end;
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
static struct blkinfo *blkinfo_from_line(char *line)
|
||||
{
|
||||
char *name, *majmin, *fstype, *uuid, *mountpoint, *parent;
|
||||
|
||||
name = isolate_keyword(&line, "NAME=\"");
|
||||
majmin = isolate_keyword(&line, "MAJ:MIN=\"");
|
||||
fstype = isolate_keyword(&line, "FSTYPE=\"");
|
||||
uuid = isolate_keyword(&line, "UUID=\"");
|
||||
mountpoint = isolate_keyword(&line, "MOUNTPOINT=\"");
|
||||
parent = isolate_keyword(&line, "PKNAME=\"");
|
||||
|
||||
return blkinfo_new(name, majmin, fstype, uuid, mountpoint, parent);
|
||||
}
|
||||
|
||||
static struct util_list *blkinfos_read(void)
|
||||
{
|
||||
char *output, *curr, *next;
|
||||
struct util_list *blkinfos;
|
||||
struct blkinfo *blkinfo;
|
||||
|
||||
if (cached_blkinfos)
|
||||
return cached_blkinfos;
|
||||
|
||||
output = misc_read_cmd_output(LSBLK_CMDLINE, 0, 1);
|
||||
if (!output)
|
||||
return NULL;
|
||||
|
||||
blkinfos = ptrlist_new();
|
||||
|
||||
/* Iterate over each line. */
|
||||
next = output;
|
||||
while ((curr = strsep(&next, "\n"))) {
|
||||
blkinfo = blkinfo_from_line(curr);
|
||||
if (blkinfo)
|
||||
ptrlist_add(blkinfos, blkinfo);
|
||||
}
|
||||
|
||||
free(output);
|
||||
|
||||
cached_blkinfos = blkinfos;
|
||||
|
||||
return blkinfos;
|
||||
}
|
||||
|
||||
static void blkinfos_free(struct util_list *blkinfos)
|
||||
{
|
||||
struct ptrlist_node *p, *n;
|
||||
|
||||
if (!blkinfos)
|
||||
return;
|
||||
|
||||
util_list_iterate_safe(blkinfos, p, n) {
|
||||
util_list_remove(blkinfos, p);
|
||||
blkinfo_free(p->ptr);
|
||||
free(p);
|
||||
}
|
||||
free(blkinfos);
|
||||
}
|
||||
|
||||
/* Used for debugging. */
|
||||
void blkinfos_print(struct util_list *blkinfos, int level)
|
||||
{
|
||||
struct ptrlist_node *p;
|
||||
|
||||
printf("%*sblkinfos at %p\n", level, "", (void *) blkinfos);
|
||||
if (!blkinfos)
|
||||
return;
|
||||
level += 2;
|
||||
util_list_iterate(blkinfos, p)
|
||||
blkinfo_print(p->ptr, level);
|
||||
}
|
||||
|
||||
/* Find a blkinfo for the specified devnode or NULL of none was found. */
|
||||
static struct blkinfo *blkinfo_get_by_devnode(struct devnode *devnode)
|
||||
{
|
||||
struct util_list *blkinfos;
|
||||
struct ptrlist_node *p;
|
||||
struct blkinfo *b;
|
||||
|
||||
blkinfos = blkinfos_read();
|
||||
if (!blkinfos)
|
||||
return NULL;
|
||||
|
||||
/* Get UUID for the specified devnode. */
|
||||
util_list_iterate(blkinfos, p) {
|
||||
b = p->ptr;
|
||||
if (b->devnode && devnode_cmp(b->devnode, devnode) == 0)
|
||||
return b;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Find a blkinfo for the specified device name or NULL if none was found. */
|
||||
static struct blkinfo *blkinfo_get_by_name(const char *name)
|
||||
{
|
||||
struct util_list *blkinfos;
|
||||
struct ptrlist_node *p;
|
||||
struct blkinfo *b;
|
||||
|
||||
blkinfos = blkinfos_read();
|
||||
if (!blkinfos)
|
||||
return NULL;
|
||||
|
||||
/* Get UUID for the specified devnode. */
|
||||
util_list_iterate(blkinfos, p) {
|
||||
b = p->ptr;
|
||||
if (b->devnode && strcmp(b->devnode->name, name) == 0)
|
||||
return b;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Find a blkinfo for the specified major+minor or NULL if none was found. */
|
||||
static struct blkinfo *blkinfo_get_by_majmin(unsigned int major,
|
||||
unsigned int minor)
|
||||
{
|
||||
struct util_list *blkinfos;
|
||||
struct ptrlist_node *p;
|
||||
struct blkinfo *b;
|
||||
|
||||
blkinfos = blkinfos_read();
|
||||
if (!blkinfos)
|
||||
return NULL;
|
||||
|
||||
/* Get UUID for the specified devnode. */
|
||||
util_list_iterate(blkinfos, p) {
|
||||
b = p->ptr;
|
||||
if (b->devnode && b->devnode->major == major &&
|
||||
b->devnode->minor == minor)
|
||||
return b;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Return a newly allocated ptrlist of devnodes of block devices which are
|
||||
* ancestors of the specified block device or NULL if no ancestors are found.
|
||||
* An ancestor is a block device which is the parent of another block device
|
||||
* according to lsblk data. */
|
||||
struct util_list *blkinfo_get_ancestor_devnodes(struct devnode *devnode)
|
||||
{
|
||||
struct util_list *blkinfos;
|
||||
struct util_list *todos, *result;
|
||||
struct util_list *done;
|
||||
struct devnode *curr, *p_devnode;
|
||||
struct blkinfo *blkinfo, *parent;
|
||||
struct ptrlist_node *p_curr, *p;
|
||||
int num_parents;
|
||||
|
||||
blkinfos = blkinfos_read();
|
||||
if (!blkinfos)
|
||||
return NULL;
|
||||
|
||||
todos = ptrlist_new();
|
||||
result = ptrlist_new();
|
||||
done = strlist_new();
|
||||
|
||||
/* Repeat the resolution process until only devices with no parent
|
||||
* remain. */
|
||||
ptrlist_add(todos, devnode_copy(devnode));
|
||||
while ((p_curr = util_list_start(todos))) {
|
||||
util_list_remove(todos, p_curr);
|
||||
curr = p_curr->ptr;
|
||||
free(p_curr);
|
||||
|
||||
/* Check for parents. */
|
||||
num_parents = 0;
|
||||
util_list_iterate(blkinfos, p) {
|
||||
blkinfo = p->ptr;
|
||||
if (!blkinfo->devnode || !blkinfo->parent)
|
||||
continue;
|
||||
if (devnode_cmp(blkinfo->devnode, curr) != 0)
|
||||
continue;
|
||||
parent = blkinfo_get_by_name(blkinfo->parent);
|
||||
if (parent && parent->devnode)
|
||||
goto add;
|
||||
|
||||
/* Try to resolve name to major:minor. */
|
||||
p_devnode = devnode_from_devfile(NULL, blkinfo->parent,
|
||||
BLOCKDEV);
|
||||
if (!p_devnode)
|
||||
continue;
|
||||
parent = blkinfo_get_by_majmin(p_devnode->major,
|
||||
p_devnode->minor);
|
||||
free(p_devnode);
|
||||
if (!parent || !parent->devnode)
|
||||
continue;
|
||||
|
||||
add:
|
||||
ptrlist_add(todos, devnode_copy(parent->devnode));
|
||||
num_parents++;
|
||||
}
|
||||
|
||||
if (num_parents == 0 && devnode_cmp(curr, devnode) != 0 &&
|
||||
!strlist_find(done, curr->name)) {
|
||||
/* Add device with no parent to results (except for
|
||||
* the initially specified device). */
|
||||
ptrlist_add(result, curr);
|
||||
strlist_add(done, curr->name);
|
||||
} else
|
||||
free(curr);
|
||||
}
|
||||
ptrlist_free(todos, 0);
|
||||
strlist_free(done);
|
||||
|
||||
/* Check if any ancestors were found. */
|
||||
if (util_list_is_empty(result)) {
|
||||
ptrlist_free(result, 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Return a newly allocated ptrlist of devnodes of block devices which have
|
||||
* the same file system UUID and type or NULL if no such devices are found. */
|
||||
struct util_list *blkinfo_get_same_uuid_devnodes(struct devnode *devnode)
|
||||
{
|
||||
struct util_list *blkinfos;
|
||||
struct util_list *devnodes;
|
||||
struct ptrlist_node *p;
|
||||
struct blkinfo *b;
|
||||
char *uuid;
|
||||
|
||||
blkinfos = blkinfos_read();
|
||||
if (!blkinfos)
|
||||
return NULL;
|
||||
|
||||
b = blkinfo_get_by_devnode(devnode);
|
||||
if (!b)
|
||||
return NULL;
|
||||
|
||||
uuid = b->uuid;
|
||||
if (!uuid)
|
||||
return NULL;
|
||||
|
||||
/* Get all devnodes of devices with the same UUID. */
|
||||
devnodes = ptrlist_new();
|
||||
util_list_iterate(blkinfos, p) {
|
||||
b = p->ptr;
|
||||
if (!b->devnode || !b->uuid)
|
||||
continue;
|
||||
if (strcmp(b->uuid, uuid) != 0)
|
||||
continue;
|
||||
if (devnode_cmp(b->devnode, devnode) == 0)
|
||||
continue;
|
||||
ptrlist_add(devnodes, devnode_copy(b->devnode));
|
||||
}
|
||||
|
||||
if (util_list_is_empty(devnodes)) {
|
||||
strlist_free(devnodes);
|
||||
devnodes = NULL;
|
||||
}
|
||||
|
||||
return devnodes;
|
||||
}
|
||||
|
||||
/* Check mountpoints in blkinfo for an entry that provides path. */
|
||||
struct devnode *blkinfo_get_devnode_by_path(const char *path)
|
||||
{
|
||||
struct util_list *blkinfos;
|
||||
struct ptrlist_node *p;
|
||||
struct blkinfo *b, *match = NULL;
|
||||
size_t blen, mlen = 0;
|
||||
|
||||
blkinfos = blkinfos_read();
|
||||
if (!blkinfos)
|
||||
return NULL;
|
||||
|
||||
util_list_iterate(blkinfos, p) {
|
||||
b = p->ptr;
|
||||
if (!b->devnode || !b->mountpoint)
|
||||
continue;
|
||||
blen = strlen(b->mountpoint);
|
||||
|
||||
/* path: /path/to/file
|
||||
* mountpoint: /path */
|
||||
if (strncmp(path, b->mountpoint, blen) != 0)
|
||||
continue;
|
||||
if (path[blen] && path[blen] != '/')
|
||||
continue;
|
||||
|
||||
/* Ensure longest match. */
|
||||
if (!match || blen > mlen) {
|
||||
match = b;
|
||||
mlen = blen;
|
||||
}
|
||||
}
|
||||
|
||||
return match ? devnode_copy(match->devnode) : NULL;
|
||||
}
|
||||
|
||||
/* Add list of mountpoints for mounted file systems to strlist @list. */
|
||||
void blkinfo_add_mountpoints(struct util_list *list)
|
||||
{
|
||||
struct util_list *blkinfos;
|
||||
struct ptrlist_node *p;
|
||||
struct blkinfo *b;
|
||||
|
||||
blkinfos = blkinfos_read();
|
||||
if (!blkinfos)
|
||||
return;
|
||||
|
||||
util_list_iterate(blkinfos, p) {
|
||||
b = p->ptr;
|
||||
if (!b->devnode || !b->mountpoint || *b->mountpoint != '/')
|
||||
continue;
|
||||
strlist_add_unique(list, b->mountpoint);
|
||||
}
|
||||
}
|
||||
|
||||
/* Add list of newly allocated devnodes for active swap devices to ptrlist
|
||||
* @list. */
|
||||
void blkinfo_add_swap_devnodes(struct util_list *list)
|
||||
{
|
||||
struct util_list *blkinfos;
|
||||
struct ptrlist_node *p;
|
||||
struct blkinfo *b;
|
||||
|
||||
blkinfos = blkinfos_read();
|
||||
if (!blkinfos)
|
||||
return;
|
||||
|
||||
util_list_iterate(blkinfos, p) {
|
||||
b = p->ptr;
|
||||
if (!b->devnode || !b->mountpoint ||
|
||||
strcmp(b->mountpoint, "[SWAP]") != 0)
|
||||
continue;
|
||||
ptrlist_add(list, devnode_copy(b->devnode));
|
||||
}
|
||||
}
|
||||
|
||||
void blkinfo_exit(void)
|
||||
{
|
||||
blkinfos_free(cached_blkinfos);
|
||||
}
|
||||
1803
zdev/src/ccw.c
Normal file
1803
zdev/src/ccw.c
Normal file
File diff suppressed because it is too large
Load Diff
1214
zdev/src/ccwgroup.c
Normal file
1214
zdev/src/ccwgroup.c
Normal file
File diff suppressed because it is too large
Load Diff
2980
zdev/src/chzdev.c
Normal file
2980
zdev/src/chzdev.c
Normal file
File diff suppressed because it is too large
Load Diff
58
zdev/src/chzdev_usage.txt
Normal file
58
zdev/src/chzdev_usage.txt
Normal file
@@ -0,0 +1,58 @@
|
||||
Usage: chzdev TYPE DEVICE [SELECTION] [SETTINGS] [ACTION] [OPTIONS]
|
||||
|
||||
Use chzdev to manage the configuration of z Systems specific devices in either:
|
||||
- active configuration (running system), or
|
||||
- persistent configuration (configuration files)
|
||||
|
||||
Actions apply to both configurations unless specified otherwise.
|
||||
|
||||
TYPE
|
||||
Device type to which this command applies. Use --list-types to display
|
||||
supported types.
|
||||
|
||||
DEVICE
|
||||
ID Select single device by ID, e.g. 0.0.1234
|
||||
FROM-TO Select range of devices between FROM and TO
|
||||
DEV1,DEV2,... Select list of devices or device ranges
|
||||
|
||||
SELECTION
|
||||
--all Select all existing and configured devices
|
||||
--configured Select devices with a persistent configuration
|
||||
--existing Select devices found in the active configuration
|
||||
--online/--offline Select devices that are online/offline
|
||||
--failed Select devices that are not functioning correctly
|
||||
--by-path PATH Select device providing file system path, e.g. /usr
|
||||
--by-node NODE Select device providing device node, e.g. /dev/sda
|
||||
--by-interface NAME Select device providing network interface, e.g. eth0
|
||||
--by-attrib KEY=VALUE Select devices with specified attribute value
|
||||
|
||||
SETTINGS
|
||||
Specifications in the form ATTRIB=VALUE used to modify device or device type
|
||||
settings. Use --list-attributes (also with --type) to list available
|
||||
attributes.
|
||||
|
||||
ACTIONS
|
||||
-e, --enable Enable device
|
||||
-d, --disable Disable device
|
||||
-l, --list-attributes List attributes
|
||||
-L, --list-types List supported device types
|
||||
-H, --help-attribute Show detailed help on specified attribute
|
||||
--export FILENAME Export configuration data to file
|
||||
--import FILENAME Import configuration data from file
|
||||
--apply Apply persistent settings to active configuration
|
||||
-h, --help Print usage information, then exit
|
||||
-v, --version Print version information, then exit
|
||||
|
||||
OPTIONS
|
||||
-a, --active Apply changes to active configuration only
|
||||
-p, --persistent Apply changes to persistent configuration only
|
||||
-t, --type Apply changes to device type
|
||||
-r, --remove ATTRIB Remove setting for specified attribute
|
||||
-R, --remove-all Remove settings for all attributes
|
||||
-f, --force Override safety checks
|
||||
-y, --yes Answer all confirmation questions with 'yes'
|
||||
--no-root-update Skip root device update
|
||||
--dry-run Display changes without applying
|
||||
--base PATH Use PATH as base for accessing files
|
||||
-V, --verbose Print additional run-time information
|
||||
-q, --quiet Print only minimal run-time information
|
||||
570
zdev/src/ctc.c
Normal file
570
zdev/src/ctc.c
Normal file
@@ -0,0 +1,570 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "attrib.h"
|
||||
#include "ccw.h"
|
||||
#include "ccwgroup.h"
|
||||
#include "ctc.h"
|
||||
#include "ctc_auto.h"
|
||||
#include "device.h"
|
||||
#include "devtype.h"
|
||||
#include "misc.h"
|
||||
#include "namespace.h"
|
||||
#include "path.h"
|
||||
#include "setting.h"
|
||||
|
||||
#define DEVNAME "CTC device"
|
||||
|
||||
/*
|
||||
* CTC device ID namespace methods.
|
||||
*/
|
||||
static exit_code_t ctc_parse_devid(struct ccwgroup_devid *devid_ptr,
|
||||
const char *id, err_t err)
|
||||
{
|
||||
struct ccwgroup_devid devid;
|
||||
const char *reason = NULL;
|
||||
exit_code_t rc;
|
||||
|
||||
rc = ccwgroup_parse_devid(&devid, id, err);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (devid.num > CTC_NUM_DEVS) {
|
||||
reason = "Too many CCW device IDs specified";
|
||||
rc = EXIT_INVALID_ID;
|
||||
} else if (devid_ptr)
|
||||
*devid_ptr = devid;
|
||||
|
||||
if (reason) {
|
||||
err_t_print(err, "Error in %s ID format: %s: %s\n", DEVNAME,
|
||||
reason, id);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static exit_code_t ctc_parse_devid_range(struct ccwgroup_devid *from_ptr,
|
||||
struct ccwgroup_devid *to_ptr,
|
||||
const char *range, err_t err)
|
||||
{
|
||||
char *from_str, *to_str;
|
||||
struct ccwgroup_devid from, to;
|
||||
exit_code_t rc;
|
||||
const char *reason = NULL;
|
||||
|
||||
/* Split range. */
|
||||
from_str = misc_strdup(range);
|
||||
to_str = strchr(from_str, '-');
|
||||
if (!to_str) {
|
||||
rc = EXIT_INVALID_ID;
|
||||
reason = "Missing hyphen";
|
||||
goto out;
|
||||
}
|
||||
*to_str = 0;
|
||||
to_str++;
|
||||
|
||||
/* Parse range start end end ID. */
|
||||
rc = ctc_parse_devid(&from, from_str, err);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
rc = ctc_parse_devid(&to, to_str, err);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
/* Only allow ranges on CCWGROUP devices specified as single ID. */
|
||||
if (from.num != 1 || to.num != 1) {
|
||||
rc = EXIT_INVALID_ID;
|
||||
reason = "Ranges only supported on single CCW device IDs";
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = EXIT_OK;
|
||||
if (from_ptr)
|
||||
*from_ptr = from;
|
||||
if (to_ptr)
|
||||
*to_ptr = to;
|
||||
|
||||
out:
|
||||
free(from_str);
|
||||
|
||||
if (reason) {
|
||||
err_t_print(err, "Error in %s ID range format: %s: %s\n",
|
||||
DEVNAME, reason, range);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static bool ctc_parse_devid_range_simple(struct ccwgroup_devid *from,
|
||||
struct ccwgroup_devid *to,
|
||||
const char *range)
|
||||
{
|
||||
if (ctc_parse_devid_range(from, to, range, err_ignore) == EXIT_OK)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static exit_code_t ctc_ns_is_id_valid(const char *id, err_t err)
|
||||
{
|
||||
return ctc_parse_devid(NULL, id, err);
|
||||
}
|
||||
|
||||
static char *ctc_ns_normalize_id(const char *id)
|
||||
{
|
||||
struct ccwgroup_devid devid;
|
||||
|
||||
if (ctc_parse_devid(&devid, id, err_ignore) != EXIT_OK)
|
||||
return NULL;
|
||||
|
||||
return ccwgroup_devid_to_str(&devid);
|
||||
}
|
||||
|
||||
static void *ctc_ns_parse_id(const char *id, err_t err)
|
||||
{
|
||||
struct ccwgroup_devid *devid;
|
||||
|
||||
devid = misc_malloc(sizeof(struct ccwgroup_devid));
|
||||
if (ctc_parse_devid(devid, id, err) != EXIT_OK) {
|
||||
free(devid);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return devid;
|
||||
}
|
||||
|
||||
static exit_code_t ctc_ns_is_id_range_valid(const char *range, err_t err)
|
||||
{
|
||||
return ctc_parse_devid_range(NULL, NULL, range, err);
|
||||
}
|
||||
|
||||
static unsigned long ctc_ns_num_ids_in_range(const char *range)
|
||||
{
|
||||
struct ccwgroup_devid f, t;
|
||||
|
||||
if (!ctc_parse_devid_range_simple(&f, &t, range))
|
||||
return 0;
|
||||
|
||||
if (f.devid[0].cssid != t.devid[0].cssid ||
|
||||
f.devid[0].ssid != t.devid[0].ssid)
|
||||
return 0;
|
||||
|
||||
if (f.devid[0].devno > t.devid[0].devno)
|
||||
return 0;
|
||||
|
||||
return t.devid[0].devno - f.devid[0].devno + 1;
|
||||
}
|
||||
|
||||
static void ctc_ns_range_start(struct ns_range_iterator *it, const char *range)
|
||||
{
|
||||
struct ccwgroup_devid from, to;
|
||||
|
||||
if (!ctc_parse_devid_range_simple(&from, &to, range)) {
|
||||
memset(it, 0, sizeof(struct ns_range_iterator));
|
||||
return;
|
||||
}
|
||||
|
||||
it->devid = ccwgroup_copy_devid(&from);
|
||||
it->devid_last = ccwgroup_copy_devid(&to);
|
||||
it->id = ccwgroup_devid_to_str(it->devid);
|
||||
}
|
||||
|
||||
static bool ctc_ns_is_id_blacklisted(const char *id)
|
||||
{
|
||||
struct ccwgroup_devid devid;
|
||||
char *ccw_id;
|
||||
unsigned int i;
|
||||
bool result = false;
|
||||
|
||||
if (ctc_parse_devid(&devid, id, err_ignore) != EXIT_OK)
|
||||
return false;
|
||||
for (i = 0; i < devid.num; i++) {
|
||||
ccw_id = ccw_devid_to_str(&devid.devid[i]);
|
||||
result = ccw_is_id_blacklisted(ccw_id);
|
||||
free(ccw_id);
|
||||
if (result)
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void ctc_ns_unblacklist_id(const char *id)
|
||||
{
|
||||
struct ccwgroup_devid devid;
|
||||
char *ccw_id;
|
||||
unsigned int i;
|
||||
|
||||
if (ctc_parse_devid(&devid, id, err_ignore) != EXIT_OK)
|
||||
return;
|
||||
for (i = 0; i < devid.num; i++) {
|
||||
ccw_id = ccw_devid_to_str(&devid.devid[i]);
|
||||
if (ccw_is_id_blacklisted(ccw_id))
|
||||
ccw_unblacklist_id(ccw_id);
|
||||
free(ccw_id);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* CTC device ID namespace.
|
||||
*/
|
||||
|
||||
struct namespace ctc_namespace = {
|
||||
.devname = DEVNAME,
|
||||
.is_id_valid = ctc_ns_is_id_valid,
|
||||
.is_id_similar = ccwgroup_is_id_similar,
|
||||
.cmp_ids = ccwgroup_cmp_ids,
|
||||
.normalize_id = ctc_ns_normalize_id,
|
||||
.parse_id = ctc_ns_parse_id,
|
||||
.cmp_parsed_ids = ccwgroup_cmp_parsed_ids,
|
||||
.qsort_cmp = ccwgroup_qsort_cmp,
|
||||
.is_id_range_valid = ctc_ns_is_id_range_valid,
|
||||
.num_ids_in_range = ctc_ns_num_ids_in_range,
|
||||
.is_id_in_range = ccwgroup_is_id_in_range,
|
||||
.range_start = ctc_ns_range_start,
|
||||
.range_next = ccwgroup_range_next,
|
||||
|
||||
/* Blacklist handling. */
|
||||
.is_blacklist_active = ccw_is_blacklist_active,
|
||||
.is_id_blacklisted = ctc_ns_is_id_blacklisted,
|
||||
.is_id_range_blacklisted = ccw_is_id_range_blacklisted,
|
||||
.unblacklist_id = ctc_ns_unblacklist_id,
|
||||
.unblacklist_id_range = ccw_unblacklist_id_range,
|
||||
.blacklist_persist = ccw_blacklist_persist,
|
||||
};
|
||||
|
||||
/*
|
||||
* CTC device attributes.
|
||||
*/
|
||||
|
||||
static struct attrib ctc_attr_buffer = {
|
||||
.name = "buffer",
|
||||
.title = "Control maximum buffer size",
|
||||
.desc =
|
||||
"Specify the maximum buffer size used for a CTC interface. The value\n"
|
||||
"must be in the range of <minimum MTU + header size> to\n"
|
||||
"<maximum MTU + header size> where a header is typically 8 bytes\n"
|
||||
"long. When this attribute is set, the MTU size of the interface\n"
|
||||
"is also set accordingly.\n",
|
||||
.defval = "32768",
|
||||
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(584, 65535)),
|
||||
.order_cmp = ccw_online_only_order_cmp,
|
||||
.check = ccw_online_only_check,
|
||||
};
|
||||
|
||||
static struct attrib ctc_attr_protocol = {
|
||||
.name = "protocol",
|
||||
.title = "Specify CTC interface protocol",
|
||||
.desc =
|
||||
"Specify the protocol to use for a CTC interface. The correct\n"
|
||||
"protocol depends on the CTC connection peer:\n"
|
||||
" 0: Non-z/OS peer such as a z/VM TCP service machine\n"
|
||||
" 1: Linux peer\n"
|
||||
" 3: z/OS peer\n"
|
||||
" 4: MPC connection to VTAM\n",
|
||||
.defval = "0",
|
||||
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1), ACCEPT_RANGE(3, 4)),
|
||||
.order_cmp = ccw_offline_only_order_cmp,
|
||||
.check = ccw_offline_only_check,
|
||||
};
|
||||
|
||||
/*
|
||||
* CTC subtype methods.
|
||||
*/
|
||||
|
||||
/* Check if user attempts to modify an attribute of an online CTC device
|
||||
* which can only be set while device is offline. */
|
||||
static exit_code_t check_online_conflict(struct device *dev, config_t config)
|
||||
{
|
||||
struct attrib *attribs[] = {
|
||||
&ctc_attr_protocol,
|
||||
};
|
||||
struct setting *online, *s;
|
||||
unsigned int i;
|
||||
exit_code_t rc;
|
||||
|
||||
/* All is well if:
|
||||
* 1. We're not configuring the active configuration
|
||||
* 2. online->actual == 0 or we don't know the actual value
|
||||
* 3. online->modified && online->value == 0*/
|
||||
if (!SCOPE_ACTIVE(config) || dev->active.deconfigured)
|
||||
return EXIT_OK;
|
||||
online = setting_list_find(dev->active.settings, ccw_attr_online.name);
|
||||
if (!online)
|
||||
return EXIT_OK;
|
||||
if (!online->actual_value || atoi(online->actual_value) == 0)
|
||||
return EXIT_OK;
|
||||
if (online->modified && atoi(online->value) == 0)
|
||||
return EXIT_OK;
|
||||
|
||||
rc = EXIT_OK;
|
||||
for (i = 0; i < ARRAY_SIZE(attribs); i++) {
|
||||
s = setting_list_find(dev->active.settings, attribs[i]->name);
|
||||
if (!s || !s->modified)
|
||||
continue;
|
||||
delayed_warn("Attribute '%s' can only be changed while device "
|
||||
"is offline\n", s->name);
|
||||
rc = EXIT_INVALID_CONFIG;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static exit_code_t ctc_st_check_pre_configure(struct subtype *st,
|
||||
struct device *dev,
|
||||
int prereq, config_t config)
|
||||
{
|
||||
exit_code_t rc;
|
||||
|
||||
rc = check_online_conflict(dev, config);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
static exit_code_t ctc_st_is_definable(struct subtype *st, const char *id,
|
||||
err_t err)
|
||||
{
|
||||
struct ccwgroup_subtype_data *data = st->data;
|
||||
struct ccwgroup_devid devid;
|
||||
exit_code_t rc;
|
||||
|
||||
rc = ccwgroup_parse_devid(&devid, id, err);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (subtype_device_exists_active(st, id))
|
||||
return EXIT_OK;
|
||||
|
||||
if (devid.num == data->num_devs)
|
||||
return ctc_auto_is_possible(&devid, err);
|
||||
|
||||
if (devid.num == 1)
|
||||
return ctc_auto_get_devid(NULL, &devid.devid[0], err);
|
||||
|
||||
err_t_print(err, "Invalid number of CCW device IDs\n");
|
||||
|
||||
return EXIT_INVALID_ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* device_detect_definable - Detect configuration of definable device
|
||||
* @st: Device subtype
|
||||
* @dev: Device
|
||||
*
|
||||
* Detect the full ID and default parameters for non-existing but definable
|
||||
* device @dev and update active.definable. Return %EXIT_OK on success, or an
|
||||
* error code otherwise.
|
||||
*/
|
||||
static exit_code_t ctc_st_detect_definable(struct subtype *st,
|
||||
struct device *dev)
|
||||
{
|
||||
struct ccwgroup_devid *devid;
|
||||
exit_code_t rc;
|
||||
|
||||
devid = dev->devid;
|
||||
if (devid->num == 1) {
|
||||
/* Detect possible group for this device. */
|
||||
rc = ctc_auto_get_devid(devid, &devid->devid[0],
|
||||
err_delayed_print);
|
||||
if (rc) {
|
||||
error("Auto-detection failed for %s %s\n"
|
||||
"Please be sure to specify full CCWGROUP ID!\n",
|
||||
st->devname, dev->id);
|
||||
return rc;
|
||||
}
|
||||
free(dev->id);
|
||||
dev->id = ccwgroup_devid_to_str(dev->devid);
|
||||
}
|
||||
|
||||
dev->active.definable = 1;
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
static void ctc_st_add_definable_ids(struct subtype *st, struct util_list *ids)
|
||||
{
|
||||
ctc_auto_add_ids(ids);
|
||||
}
|
||||
|
||||
/*
|
||||
* CTC subtype.
|
||||
*/
|
||||
|
||||
static struct ccwgroup_subtype_data ctc_data = {
|
||||
.ccwgroupdrv = CTC_CCWGROUPDRV_NAME,
|
||||
.ccwdrv = CTC_CCWDRV_NAME,
|
||||
.rootdrv = CTC_ROOTDRV_NAME,
|
||||
.mod = CTC_MOD_NAME,
|
||||
.num_devs = CTC_NUM_DEVS,
|
||||
};
|
||||
|
||||
static struct subtype ctc_subtype = {
|
||||
.super = &ccwgroup_subtype,
|
||||
.devtype = &ctc_devtype,
|
||||
.name = "ctc",
|
||||
.title = "Channel-To-Channel (CTC) and CTC-MPC network "
|
||||
"devices",
|
||||
.devname = DEVNAME,
|
||||
.modules = STRING_ARRAY(CTC_MOD_NAME),
|
||||
.namespace = &ctc_namespace,
|
||||
.data = &ctc_data,
|
||||
|
||||
.dev_attribs = ATTRIB_ARRAY(
|
||||
&ccw_attr_online,
|
||||
&ctc_attr_buffer,
|
||||
&ctc_attr_protocol,
|
||||
),
|
||||
.unknown_dev_attribs = 1,
|
||||
.support_definable = 1,
|
||||
|
||||
.check_pre_configure = &ctc_st_check_pre_configure,
|
||||
|
||||
.is_definable = &ctc_st_is_definable,
|
||||
.detect_definable = &ctc_st_detect_definable,
|
||||
.add_definable_ids = &ctc_st_add_definable_ids,
|
||||
};
|
||||
|
||||
/*
|
||||
* CTC devtype methods.
|
||||
*/
|
||||
|
||||
/* Clean up all resources used by devtype object. */
|
||||
static void ctc_devtype_exit(struct devtype *dt)
|
||||
{
|
||||
setting_list_free(dt->active_settings);
|
||||
setting_list_free(dt->persistent_settings);
|
||||
}
|
||||
|
||||
static exit_code_t ctc_devtype_read_settings(struct devtype *dt,
|
||||
config_t config)
|
||||
{
|
||||
/* No kernel or module parameters exist for the ctc device driver,
|
||||
* but at least determine module loaded state. */
|
||||
dt->active_settings = setting_list_new();
|
||||
dt->persistent_settings = setting_list_new();
|
||||
|
||||
if (SCOPE_ACTIVE(config))
|
||||
dt->active_exists = devtype_is_module_loaded(dt);
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
static exit_code_t ctc_devtype_write_settings(struct devtype *dt,
|
||||
config_t config)
|
||||
{
|
||||
/* No kernel or module parameters exist for the ctc device driver. */
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* CTC devtype.
|
||||
*/
|
||||
|
||||
struct devtype ctc_devtype = {
|
||||
.name = "ctc",
|
||||
.title = "", /* Only use subtypes. */
|
||||
.devname = "CTC",
|
||||
|
||||
.subtypes = SUBTYPE_ARRAY(
|
||||
&ctc_subtype,
|
||||
),
|
||||
|
||||
.type_attribs = ATTRIB_ARRAY(
|
||||
),
|
||||
|
||||
.exit = &ctc_devtype_exit,
|
||||
|
||||
.read_settings = &ctc_devtype_read_settings,
|
||||
.write_settings = &ctc_devtype_write_settings,
|
||||
};
|
||||
|
||||
/*
|
||||
* Helper functions.
|
||||
*/
|
||||
|
||||
static struct util_list *ctc_list;
|
||||
|
||||
static void add_query(struct util_list *list, const char *cmd)
|
||||
{
|
||||
char *vmcp, *curr, *next, **argv;
|
||||
int argc;
|
||||
struct ccw_devid devid;
|
||||
|
||||
/* Query real CTC adapters. */
|
||||
vmcp = misc_read_cmd_output(cmd, 0, 1);
|
||||
if (!vmcp)
|
||||
return;
|
||||
|
||||
next = vmcp;
|
||||
while ((curr = strsep(&next, "\n"))) {
|
||||
line_split(curr, &argc, &argv);
|
||||
if (argc >= 2 && ccw_parse_devid_simple(&devid, argv[1]))
|
||||
ptrlist_add(list, ccw_copy_devid(&devid));
|
||||
line_free(argc, argv);
|
||||
}
|
||||
|
||||
free(vmcp);
|
||||
}
|
||||
|
||||
/* Perform a CP QUERY CTCA ALL command and return a ptrlist of CCW device IDs
|
||||
* for all found CTC devices. */
|
||||
static struct util_list *query_ctc(void)
|
||||
{
|
||||
struct util_list *list;
|
||||
char *cmd;
|
||||
|
||||
list = ptrlist_new();
|
||||
|
||||
/* Query real CTC adapters. */
|
||||
cmd = misc_asprintf("%s query ctca all 2>/dev/null", PATH_VMCP);
|
||||
add_query(list, cmd);
|
||||
free(cmd);
|
||||
|
||||
/* Query virtual CTC adapters. */
|
||||
cmd = misc_asprintf("%s query virtual ctca 2>/dev/null", PATH_VMCP);
|
||||
add_query(list, cmd);
|
||||
free(cmd);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/* Release all allocated resources. */
|
||||
void ctc_exit(void)
|
||||
{
|
||||
ptrlist_free(ctc_list, 1);
|
||||
}
|
||||
|
||||
/* Try to confirm that the specified CCW device ID refers to CTC device.
|
||||
* Return %true it is a CTC device, %false if it cannot be confirmed. */
|
||||
bool ctc_confirm(struct ccw_devid *devid)
|
||||
{
|
||||
struct ptrlist_node *p;
|
||||
struct ccw_devid *d;
|
||||
|
||||
if (devid->cssid != 0 || devid->ssid != 0 || !is_zvm())
|
||||
return false;
|
||||
|
||||
if (!ctc_list)
|
||||
ctc_list = query_ctc();
|
||||
|
||||
util_list_iterate(ctc_list, p) {
|
||||
d = p->ptr;
|
||||
if (ccw_cmp_devids(devid, d) == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
322
zdev/src/ctc_auto.c
Normal file
322
zdev/src/ctc_auto.c
Normal file
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <string.h>
|
||||
|
||||
#include "ccw.h"
|
||||
#include "ccwgroup.h"
|
||||
#include "ctc.h"
|
||||
#include "ctc_auto.h"
|
||||
#include "device.h"
|
||||
#include "lcs.h"
|
||||
#include "module.h"
|
||||
#include "path.h"
|
||||
|
||||
struct cutype {
|
||||
unsigned int cutype:16;
|
||||
unsigned int cumodel:8;
|
||||
};
|
||||
|
||||
static struct cutype ctc_cutypes[] = {
|
||||
{ .cutype = 0x3088, .cumodel = 0x08, },
|
||||
{ .cutype = 0x3088, .cumodel = 0x1e, },
|
||||
{ .cutype = 0x3088, .cumodel = 0x1f, },
|
||||
};
|
||||
|
||||
/*
|
||||
* CTC autodetection
|
||||
*
|
||||
* A CTC device must be grouped before it can be used. The following
|
||||
* rules apply to grouping:
|
||||
*
|
||||
* 1. A CTC device can be grouped from 2 CCW devices
|
||||
* a) Read device
|
||||
* b) Write device
|
||||
* 2. All CCW devices must be bound to the CTC CCW device driver. Note that
|
||||
* due to an overlap in CU-Types, CCW device could also be bound to
|
||||
* the LCS device driver.
|
||||
* 3. The subchannel of all CCW devices must be defined with the same CHPID
|
||||
* 4. None of the CCW devices is part of an existing CCWGROUP device
|
||||
*/
|
||||
|
||||
/* Compare by: 1. CHPID, 2. CUTYPE, 3. DEVTYPE, 4. CCW device ID
|
||||
* -1 = a < b 1 = a > b 0 = a == b. */
|
||||
static int info_cmp(void *a, void *b, void *data)
|
||||
{
|
||||
struct ptrlist_node *pa = a, *pb = b;
|
||||
struct ccw_devinfo *ia = pa->ptr, *ib = pb->ptr;
|
||||
int r;
|
||||
|
||||
r = ccw_devinfo_chpids_cmp(ia, ib);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
r = ccw_devinfo_cutype_cmp(ia, ib);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
r = ccw_devinfo_devtype_cmp(ia, ib);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return ccw_cmp_devids(&ia->devid, &ib->devid);
|
||||
}
|
||||
|
||||
static bool is_compatible(struct ccw_devinfo *a, struct ccw_devinfo *b)
|
||||
{
|
||||
if (ccw_devinfo_chpids_cmp(a, b) == 0 &&
|
||||
ccw_devinfo_cutype_cmp(a, b) == 0 &&
|
||||
ccw_devinfo_devtype_cmp(a, b) == 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool is_ctc(struct ccw_devinfo *info)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ctc_cutypes); i++) {
|
||||
if (info->cutype == ctc_cutypes[i].cutype &&
|
||||
info->cumodel == ctc_cutypes[i].cumodel)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Add device info for all CTC CCW devices to ptrlist in data. */
|
||||
static exit_code_t add_cb(const char *path, const char *filename, void *data)
|
||||
{
|
||||
struct ccw_devid devid;
|
||||
struct util_list *infos = data;
|
||||
struct ccw_devinfo *devinfo;
|
||||
|
||||
if (!strchr(filename, '.'))
|
||||
return EXIT_OK;
|
||||
if (ccw_parse_devid(&devid, filename, err_ignore) != EXIT_OK)
|
||||
return EXIT_OK;
|
||||
devinfo = ccw_devinfo_get(&devid, 0);
|
||||
if (devinfo->exists && !devinfo->grouped && is_ctc(devinfo))
|
||||
ptrlist_add(infos, devinfo);
|
||||
else
|
||||
free(devinfo);
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
/* Return a sorted ptrlist of struct ccw_devinfos for all CCW devices
|
||||
* bound to the ctc or lcs CCW device driver with matching CUTYPE.
|
||||
* The result must be freed using ptrlist_free(,1); */
|
||||
static struct util_list *read_sorted_ctc_devinfos(void)
|
||||
{
|
||||
struct util_list *infos;
|
||||
char *path;
|
||||
|
||||
/* Get CHPID information for all devices handled by the CTC driver. */
|
||||
infos = ptrlist_new();
|
||||
|
||||
/* Add CCW devices bound to the CTC CCW device driver. */
|
||||
module_try_load_once(CTC_MOD_NAME, NULL);
|
||||
path = path_get_sys_bus_drv(CCW_BUS_NAME, CTC_CCWDRV_NAME);
|
||||
if (dir_exists(path))
|
||||
path_for_each(path, add_cb, infos);
|
||||
free(path);
|
||||
|
||||
/* Add CCW devices bound to the LCS CCW device driver. */
|
||||
path = path_get_sys_bus_drv(CCW_BUS_NAME, LCS_CCWDRV_NAME);
|
||||
if (dir_exists(path))
|
||||
path_for_each(path, add_cb, infos);
|
||||
free(path);
|
||||
|
||||
/* For each CHPID: Find groups. Add to result list. */
|
||||
util_list_sort(infos, info_cmp, NULL);
|
||||
|
||||
return infos;
|
||||
}
|
||||
|
||||
static void add_ccwgroup_devid(struct util_list *devids, struct ccw_devid *read,
|
||||
struct ccw_devid *write)
|
||||
{
|
||||
struct ccwgroup_devid devid;
|
||||
|
||||
devid.devid[0] = *read;
|
||||
devid.devid[1] = *write;
|
||||
devid.num = CTC_NUM_DEVS;
|
||||
|
||||
ptrlist_add(devids, ccwgroup_copy_devid(&devid));
|
||||
}
|
||||
|
||||
static void add_groupable_devids(struct util_list *devids,
|
||||
struct util_list *infos)
|
||||
{
|
||||
struct ptrlist_node *curr, *next;
|
||||
struct ccw_devinfo *r, *w;
|
||||
|
||||
/* For each CHPID: Find groups. Add to result list. */
|
||||
curr = util_list_start(infos);
|
||||
while (curr) {
|
||||
next = util_list_next(infos, curr);
|
||||
if (!next)
|
||||
break;
|
||||
r = curr->ptr;
|
||||
w = next->ptr;
|
||||
if (is_compatible(r, w)) {
|
||||
add_ccwgroup_devid(devids, &r->devid, &w->devid);
|
||||
curr = util_list_next(infos, next);
|
||||
} else
|
||||
curr = next;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add CCWGROUP IDs of ctc devices that can be grouped to strlist @ids. */
|
||||
void ctc_auto_add_ids(struct util_list *ids)
|
||||
{
|
||||
struct util_list *infos, *devids;
|
||||
struct ptrlist_node *p;
|
||||
char *id;
|
||||
|
||||
infos = read_sorted_ctc_devinfos();
|
||||
devids = ptrlist_new();
|
||||
add_groupable_devids(devids, infos);
|
||||
|
||||
util_list_iterate(devids, p) {
|
||||
id = ccwgroup_devid_to_str(p->ptr);
|
||||
strlist_add(ids, id);
|
||||
free(id);
|
||||
}
|
||||
|
||||
ptrlist_free(devids, 1);
|
||||
ptrlist_free(infos, 1);
|
||||
}
|
||||
|
||||
exit_code_t ctc_auto_get_devid(struct ccwgroup_devid *devid_ptr,
|
||||
struct ccw_devid *ccw_devid, err_t err)
|
||||
{
|
||||
struct util_list *devids, *infos;
|
||||
struct ptrlist_node *p, *read, *write;
|
||||
struct ccw_devinfo *r, *w;
|
||||
struct ccwgroup_devid *devid;
|
||||
exit_code_t rc;
|
||||
|
||||
infos = read_sorted_ctc_devinfos();
|
||||
|
||||
/* Try to find an ID from the canonical auto-generated list. */
|
||||
devids = ptrlist_new();
|
||||
add_groupable_devids(devids, infos);
|
||||
|
||||
util_list_iterate(devids, p) {
|
||||
devid = p->ptr;
|
||||
if (ccw_cmp_devids(ccw_devid, &devid->devid[0]) != 0)
|
||||
continue;
|
||||
rc = EXIT_OK;
|
||||
if (devid_ptr)
|
||||
*devid_ptr = *devid;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Try to create a CCWGROUP ID with the specified ID as read device. */
|
||||
|
||||
/* Get CCW device info for read device. */
|
||||
util_list_iterate(infos, read) {
|
||||
r = read->ptr;
|
||||
if (ccw_cmp_devids(&r->devid, ccw_devid) == 0)
|
||||
break;
|
||||
}
|
||||
if (!read) {
|
||||
err_t_print(err, "Read CCW device not found\n");
|
||||
rc = EXIT_GROUP_NOT_FOUND;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get CCW device ID for write device. */
|
||||
write = util_list_next(infos, read);
|
||||
if (!write) {
|
||||
err_t_print(err, "Write CCW device not found\n");
|
||||
rc = EXIT_GROUP_NOT_FOUND;
|
||||
goto out;
|
||||
}
|
||||
w = write->ptr;
|
||||
if (!is_compatible(r, w)) {
|
||||
err_t_print(err, "No compatible write CCW device found\n");
|
||||
rc = EXIT_GROUP_INVALID;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = EXIT_OK;
|
||||
if (devid_ptr) {
|
||||
devid_ptr->devid[0] = r->devid;
|
||||
devid_ptr->devid[1] = w->devid;
|
||||
devid_ptr->num = CTC_NUM_DEVS;
|
||||
}
|
||||
|
||||
out:
|
||||
ptrlist_free(devids, 1);
|
||||
ptrlist_free(infos, 1);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
exit_code_t ctc_auto_is_possible(struct ccwgroup_devid *devid, err_t err)
|
||||
{
|
||||
struct ccw_devinfo *info[CTC_NUM_DEVS];
|
||||
unsigned int i;
|
||||
char *ccwid;
|
||||
const char *msg;
|
||||
exit_code_t rc;
|
||||
|
||||
if (devid->num < CTC_NUM_DEVS) {
|
||||
err_t_print(err, "Not enough CCW device IDs in CTC device "
|
||||
"ID\n");
|
||||
return EXIT_INCOMPLETE_ID;
|
||||
}
|
||||
if (devid->num > CTC_NUM_DEVS) {
|
||||
err_t_print(err, "CTC device ID contains too many CCW device "
|
||||
"IDs\n");
|
||||
return EXIT_INVALID_ID;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(info); i++)
|
||||
info[i] = ccw_devinfo_get(&devid->devid[i], 1);
|
||||
|
||||
rc = EXIT_OK;
|
||||
msg = NULL;
|
||||
for (i = 0; i < ARRAY_SIZE(info); i++) {
|
||||
if (!info[i]->exists) {
|
||||
msg = "CCW device %s does not exist\n";
|
||||
rc = EXIT_GROUP_NOT_FOUND;
|
||||
} else if (info[i]->grouped) {
|
||||
msg = "CCW device %s is in another group device\n";
|
||||
rc = EXIT_GROUP_ALREADY;
|
||||
} else if (i > 0 &&
|
||||
ccw_devinfo_chpids_cmp(info[i - 1], info[i]) != 0) {
|
||||
msg = "CCW device %s is not on the same CHPID\n";
|
||||
rc = EXIT_GROUP_INVALID;
|
||||
} else if (i > 0 &&
|
||||
ccw_devinfo_cutype_cmp(info[i - 1], info[i]) != 0) {
|
||||
msg = "CUTYPE of CCW device %s differs\n";
|
||||
rc = EXIT_GROUP_INVALID;
|
||||
} else if (i > 0 &&
|
||||
ccw_devinfo_devtype_cmp(info[i - 1], info[i]) != 0) {
|
||||
msg = "DEVTYPE of CCW device %s differs\n";
|
||||
rc = EXIT_GROUP_INVALID;
|
||||
}
|
||||
if (!msg)
|
||||
continue;
|
||||
ccwid = ccw_devid_to_str(&devid->devid[i]);
|
||||
err_t_print(err, msg, ccwid);
|
||||
free(ccwid);
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(info); i++)
|
||||
free(info[i]);
|
||||
|
||||
return rc;
|
||||
}
|
||||
687
zdev/src/dasd.c
Normal file
687
zdev/src/dasd.c
Normal file
@@ -0,0 +1,687 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "attrib.h"
|
||||
#include "ccw.h"
|
||||
#include "dasd.h"
|
||||
#include "device.h"
|
||||
#include "devtype.h"
|
||||
#include "misc.h"
|
||||
#include "modprobe.h"
|
||||
#include "module.h"
|
||||
#include "path.h"
|
||||
#include "setting.h"
|
||||
#include "udev.h"
|
||||
|
||||
#define DASD_MOD_NAME "dasd_mod"
|
||||
#define DASD_ECKD_MOD_NAME "dasd_eckd_mod"
|
||||
#define DASD_FBA_MOD_NAME "dasd_fba_mod"
|
||||
#define DASD_DIAG_MOD_NAME "dasd_diag_mod"
|
||||
|
||||
/* Subtype specific data. */
|
||||
struct dasd_subtype_data {
|
||||
const char *ccwdrv;
|
||||
const char *module;
|
||||
};
|
||||
|
||||
/* List of dependent modules. */
|
||||
static const char *dasd_mods[] = {
|
||||
DASD_ECKD_MOD_NAME,
|
||||
DASD_FBA_MOD_NAME,
|
||||
DASD_DIAG_MOD_NAME,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/*
|
||||
* DASD type attributes.
|
||||
*/
|
||||
|
||||
static struct attrib dasd_tattr_autodetect = {
|
||||
.name = "autodetect",
|
||||
.title = "Enable automatic DASD activation",
|
||||
.desc =
|
||||
"Control whether the DASD driver automatically enables all detected\n"
|
||||
"DASDs:\n"
|
||||
" 0: Only configured DASDs are enabled\n"
|
||||
" 1: All detected DASDs are automatically enabled\n",
|
||||
.defval = "0",
|
||||
.activerem = 1,
|
||||
.defunset = 1,
|
||||
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)),
|
||||
};
|
||||
|
||||
static struct attrib dasd_tattr_probeonly = {
|
||||
.name = "probeonly",
|
||||
.title = "Inhibit access to DASD device nodes",
|
||||
.desc =
|
||||
"Control whether the DASD driver allows access to DASD devices:\n"
|
||||
" 0: All enabled DASDs can be accessed normally\n"
|
||||
" 1: Reject any attempt to open DASD devices\n",
|
||||
.defval = "0",
|
||||
.activerem = 1,
|
||||
.defunset = 1,
|
||||
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)),
|
||||
};
|
||||
|
||||
static struct attrib dasd_tattr_nopav = {
|
||||
.name = "nopav",
|
||||
.title = "Deactivate the Parallel Access Volume feature",
|
||||
.desc = "Control the use of the Parallel Access Volume (PAV) feature:\n"
|
||||
" 0: PAV is used when supported by the storage system\n"
|
||||
" 1: The use of PAV is suppressed when running in LPAR\n",
|
||||
.defval = "0",
|
||||
.activerem = 1,
|
||||
.defunset = 1,
|
||||
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)),
|
||||
};
|
||||
|
||||
static struct attrib dasd_tattr_nofcx = {
|
||||
.name = "nofcx",
|
||||
.title = "Deactivate the High Performance FICON feature",
|
||||
.desc = "Control the use of the High Performance FICON (HPF) feature:\n"
|
||||
" 0: HPF is used when supported by the storage hardware\n"
|
||||
" 1: HPF is not used\n",
|
||||
.defval = "0",
|
||||
.activerem = 1,
|
||||
.defunset = 1,
|
||||
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)),
|
||||
};
|
||||
|
||||
static struct attrib dasd_tattr_eer_pages = {
|
||||
.name = "eer_pages",
|
||||
.title = "Modify buffer size for error records",
|
||||
.desc =
|
||||
"Control the number of 4 KB pages that are used for internal "
|
||||
"buffering\n"
|
||||
"of error records generated by the Extended Error Reporting (EER)\n"
|
||||
"feature.\n",
|
||||
.defval = "5",
|
||||
.activerem = 1,
|
||||
.nounload = 1,
|
||||
.accept = ACCEPT_ARRAY(ACCEPT_NUM_GE(1)),
|
||||
};
|
||||
|
||||
/*
|
||||
* DASD device attributes.
|
||||
*/
|
||||
|
||||
static struct attrib dasd_attr_failfast = {
|
||||
.name = "failfast",
|
||||
.title = "Modify error recovery in no-path scenario",
|
||||
.desc =
|
||||
"Control I/O handling when all paths to a DASD have been lost:\n"
|
||||
" 0: Queue I/O until path becomes available\n"
|
||||
" 1: Fail I/O request immediately\n",
|
||||
.defval = "0",
|
||||
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)),
|
||||
};
|
||||
|
||||
static struct attrib dasd_attr_readonly = {
|
||||
.name = "readonly",
|
||||
.title = "Inhibit write access to DASD",
|
||||
.desc = "Control the DASD driver read-only setting for a DASD:\n"
|
||||
" 0: Allow writing to the device\n"
|
||||
" 1: Deny writing to the device\n",
|
||||
.defval = "0",
|
||||
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)),
|
||||
};
|
||||
|
||||
static struct attrib dasd_attr_erplog = {
|
||||
.name = "erplog",
|
||||
.title = "Enable logging of Error Recovery Processing",
|
||||
.desc = "Control logging of Error Recovery Processing (ERP), such as\n"
|
||||
"failing channel programs:\n"
|
||||
" 0: ERP logging is disabled\n"
|
||||
" 1: ERP logging is enabled\n",
|
||||
.defval = "0",
|
||||
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)),
|
||||
};
|
||||
|
||||
static int dasd_raw_diag_order_cmp(struct setting *, struct setting *);
|
||||
static bool dasd_raw_diag_check(struct setting *, struct setting *, config_t);
|
||||
|
||||
static struct attrib dasd_attr_use_diag = {
|
||||
.name = "use_diag",
|
||||
.title = "Activate z/VM hypervisor assisted I/O processing",
|
||||
.desc =
|
||||
"Control I/O access mode for a DASD:\n"
|
||||
" 0: I/O is performed using standard channel programs\n"
|
||||
" 1: I/O is performed using the z/VM DIAGNOSE X'250' interface\n"
|
||||
"\n"
|
||||
"The DIAGNOSE X'250' access mode works only when:\n"
|
||||
" - Running Linux as z/VM guest\n"
|
||||
" - Using devices formatted with consistent block sizes, such as\n"
|
||||
" ECKD DASDs with LDL or CMS format, or FBA devices\n",
|
||||
.order_cmp = dasd_raw_diag_order_cmp,
|
||||
.check = dasd_raw_diag_check,
|
||||
.defval = "0",
|
||||
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)),
|
||||
};
|
||||
|
||||
static struct attrib dasd_attr_raw_track_access = {
|
||||
.name = "raw_track_access",
|
||||
.title = "Enable access to ECKD track metadata",
|
||||
.desc =
|
||||
"Control whether the DASD driver provides access to full ECKD tracks,\n"
|
||||
"including ECKD-specific meta-data:\n"
|
||||
" 0: Only access the data portion of ECKD tracks\n"
|
||||
" 1: Access the full track\n",
|
||||
.order_cmp = dasd_raw_diag_order_cmp,
|
||||
.check = dasd_raw_diag_check,
|
||||
.defval = "0",
|
||||
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)),
|
||||
};
|
||||
|
||||
/* Ensure ordering if both use_diag and raw_track_access are modified. */
|
||||
static int dasd_raw_diag_order_cmp(struct setting *a, struct setting *b)
|
||||
{
|
||||
int a_val, b_val;
|
||||
|
||||
if ((a->attrib == &dasd_attr_use_diag &&
|
||||
b->attrib == &dasd_attr_raw_track_access) ||
|
||||
(a->attrib == &dasd_attr_raw_track_access &&
|
||||
b->attrib == &dasd_attr_use_diag)) {
|
||||
/* Define order to ensure that =0 is done before =1. */
|
||||
a_val = atoi(a->value);
|
||||
b_val = atoi(b->value);
|
||||
|
||||
if (a_val == 1 && b_val == 0)
|
||||
return 1;
|
||||
if (a_val == 0 && b_val == 1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ccw_offline_only_order_cmp(a, b);
|
||||
}
|
||||
|
||||
/* Conflict if both use_diag and raw_track_access are set. */
|
||||
static bool dasd_raw_diag_check(struct setting *a, struct setting *b,
|
||||
config_t config)
|
||||
{
|
||||
if ((a->attrib == &dasd_attr_use_diag &&
|
||||
b->attrib == &dasd_attr_raw_track_access) ||
|
||||
(a->attrib == &dasd_attr_raw_track_access &&
|
||||
b->attrib == &dasd_attr_use_diag)) {
|
||||
if (atoi(a->value) == 1 && atoi(b->value) == 1)
|
||||
return false;
|
||||
}
|
||||
|
||||
return ccw_offline_only_check(a, b, config);
|
||||
}
|
||||
|
||||
static struct attrib dasd_attr_eer_enabled = {
|
||||
.name = "eer_enabled",
|
||||
.title = "Enable Extended Error Reporting",
|
||||
.desc =
|
||||
"Control the Extended Error Reporting (EER) feature for a DASD:\n"
|
||||
" 0: EER is disabled\n"
|
||||
" 1: EER is enabled\n",
|
||||
.order_cmp = ccw_online_only_order_cmp,
|
||||
.check = ccw_online_only_check,
|
||||
.defval = "0",
|
||||
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)),
|
||||
};
|
||||
|
||||
static struct attrib dasd_attr_expires = {
|
||||
.name = "expires",
|
||||
.title = "Modify I/O operation timeout",
|
||||
.desc =
|
||||
"Specify the time in seconds that the DASD driver waits for the\n"
|
||||
"completion of a single I/O operation before considering that I/O\n"
|
||||
"operation to have failed.\n"
|
||||
"\n"
|
||||
"The default value depends on the DASD type.\n",
|
||||
.order_cmp = ccw_online_only_order_cmp,
|
||||
.check = ccw_online_only_check,
|
||||
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(1, 40000000)),
|
||||
};
|
||||
|
||||
static struct attrib dasd_attr_retries = {
|
||||
.name = "retries",
|
||||
.title = "Modify I/O operation retry counter",
|
||||
.desc =
|
||||
"Specify the number of times that a failed I/O should be retried\n"
|
||||
"before reporting it as failed.\n"
|
||||
"\n"
|
||||
"The default value is dependent on the DASD type.\n",
|
||||
.order_cmp = ccw_online_only_order_cmp,
|
||||
.check = ccw_online_only_check,
|
||||
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 32768)),
|
||||
};
|
||||
|
||||
static struct attrib dasd_attr_timeout = {
|
||||
.name = "timeout",
|
||||
.title = "Modify I/O request timeout",
|
||||
.desc =
|
||||
"Specify the total time in seconds that the Linux block device layer\n"
|
||||
"waits for the completion of an I/O request issued to the DASD driver\n"
|
||||
"before considering that I/O to have failed. Specify 0 to deactivate\n"
|
||||
"timeout handling.\n",
|
||||
.order_cmp = ccw_online_only_order_cmp,
|
||||
.check = ccw_online_only_check,
|
||||
.defval = "0",
|
||||
.accept = ACCEPT_ARRAY(ACCEPT_NUM_GE(0)),
|
||||
};
|
||||
|
||||
static struct attrib dasd_attr_reservation_policy = {
|
||||
.name = "reservation_policy",
|
||||
.title = "Modify lost device reservation behavior",
|
||||
.desc =
|
||||
"Control the DASD driver behavior if an existing DASD reservation of\n"
|
||||
"this system for a DASD is lost:\n"
|
||||
" ignore: I/O operations are blocked until the external reservation\n"
|
||||
" is released (default)\n"
|
||||
" fail: All I/O operations are considered to have failed\n",
|
||||
.defval = "ignore",
|
||||
.accept = ACCEPT_ARRAY(ACCEPT_STR("ignore"), ACCEPT_STR("fail")),
|
||||
};
|
||||
|
||||
static struct attrib dasd_attr_last_known_reservation_state = {
|
||||
.name = "last_known_reservation_state",
|
||||
.title = "Display and reset driver device reservation view",
|
||||
.desc =
|
||||
"Display the DASD driver's view of device reservations held by this\n"
|
||||
"system:\n"
|
||||
" none: No reservation held or no information available\n"
|
||||
" reserved: Device is assumed to be reserved by this system\n"
|
||||
" lost: A reservation held by this system was lost\n"
|
||||
"\n"
|
||||
"To reset the reservation state from 'lost' to 'none', set this\n"
|
||||
"attribute to 'reset'.\n",
|
||||
.unstable = 1,
|
||||
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)),
|
||||
};
|
||||
|
||||
static struct attrib dasd_attr_safe_offline = {
|
||||
.name = "safe_offline",
|
||||
.title = "Deactivate DASD after processing pending I/Os",
|
||||
.desc = "Write an arbitrary value to this attribute to attempt to set\n"
|
||||
"the DASD offline after all outstanding I/O requests have\n"
|
||||
"been processed.\n",
|
||||
.activeonly = 1,
|
||||
.writeonly = 1,
|
||||
};
|
||||
|
||||
/*
|
||||
* DASD subtype methods.
|
||||
*/
|
||||
|
||||
/* Check if use_diag setting can be correctly applied. */
|
||||
static exit_code_t check_use_diag(struct device *dev, config_t config)
|
||||
{
|
||||
struct setting *u;
|
||||
int zvm = is_zvm();
|
||||
|
||||
if (SCOPE_ACTIVE(config)) {
|
||||
u = setting_list_find(dev->active.settings,
|
||||
dasd_attr_use_diag.name);
|
||||
if (u && u->modified) {
|
||||
if (u->value && atoi(u->value) == 1 && !zvm) {
|
||||
delayed_warn("Cannot set 'use_diag=1' on "
|
||||
"non-z/VM system\n");
|
||||
return EXIT_INVALID_CONFIG;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (SCOPE_PERSISTENT(config)) {
|
||||
u = setting_list_find(dev->persistent.settings,
|
||||
dasd_attr_use_diag.name);
|
||||
if (u && u->modified) {
|
||||
if (u->value && atoi(u->value) == 1 && !zvm) {
|
||||
delayed_warn("Cannot set 'use_diag=1' on "
|
||||
"non-z/VM system\n");
|
||||
return EXIT_INVALID_CONFIG;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
static exit_code_t dasd_st_check_pre_configure(struct subtype *st,
|
||||
struct device *dev,
|
||||
int prereq, config_t config)
|
||||
{
|
||||
exit_code_t rc;
|
||||
|
||||
if (dev->active.deconfigured)
|
||||
return EXIT_OK;
|
||||
rc = check_use_diag(dev, config);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
static void dasd_st_add_modules(struct subtype *st, struct device *dev,
|
||||
struct util_list *modules)
|
||||
{
|
||||
int changed, aset, pset;
|
||||
|
||||
/* Add main module. */
|
||||
st->super->add_modules(st, dev, modules);
|
||||
|
||||
/* Add dasd_diag_mod if use_diag is set. */
|
||||
setting_list_get_bool_state(dev->active.settings,
|
||||
dasd_attr_use_diag.name, &changed, &aset);
|
||||
setting_list_get_bool_state(dev->persistent.settings,
|
||||
dasd_attr_use_diag.name, &changed, &pset);
|
||||
|
||||
if (aset || pset)
|
||||
strlist_add_unique(modules, DASD_DIAG_MOD_NAME);
|
||||
}
|
||||
|
||||
/*
|
||||
* DASD methods.
|
||||
*/
|
||||
|
||||
/* Clean up all resources used by devtype object. */
|
||||
static void dasd_devtype_exit(struct devtype *dt)
|
||||
{
|
||||
setting_list_free(dt->active_settings);
|
||||
setting_list_free(dt->persistent_settings);
|
||||
}
|
||||
|
||||
/* Split a dasd= module parameter string into a setting_list, based on
|
||||
* known attributes encoded in dasd= parameters. */
|
||||
static void split_dasd(struct devtype *dt, const char *str,
|
||||
struct setting_list *list)
|
||||
{
|
||||
char *copy, *curr, *next, *dasd_str;
|
||||
struct attrib *a;
|
||||
struct util_list *dasd;
|
||||
|
||||
dasd = strlist_new();
|
||||
copy = misc_strdup(str);
|
||||
next = copy;
|
||||
while ((curr = strsep(&next, ","))) {
|
||||
if (strcmp(curr, "(null)") == 0)
|
||||
continue;
|
||||
if (*curr == 0) {
|
||||
/* Handle dasd="" */
|
||||
continue;
|
||||
}
|
||||
/* Check for known attributes. */
|
||||
a = attrib_find(dt->type_attribs, curr);
|
||||
if (a) {
|
||||
/* Currently only boolean attributes implemented. */
|
||||
setting_list_apply_actual(list, a, curr, "1");
|
||||
continue;
|
||||
}
|
||||
/* An unknown setting or device spec - leave in dasd=. */
|
||||
strlist_add(dasd, "%s", curr);
|
||||
}
|
||||
free(copy);
|
||||
|
||||
/* Add dasd= setting if necessary. */
|
||||
if (!util_list_is_empty(dasd)) {
|
||||
dasd_str = strlist_flatten(dasd, ",");
|
||||
setting_list_apply_actual(list, NULL, "dasd", dasd_str);
|
||||
free(dasd_str);
|
||||
}
|
||||
strlist_free(dasd);
|
||||
}
|
||||
|
||||
/* Convert a module parameter list into a devtype settings list. */
|
||||
static void convert_params_to_settings(struct devtype *dt,
|
||||
struct setting_list *from,
|
||||
struct setting_list *to)
|
||||
{
|
||||
struct setting *s;
|
||||
|
||||
util_list_iterate(&from->list, s) {
|
||||
if (strcmp(s->name, "dasd") == 0) {
|
||||
/* Special handling: split known attribs out. */
|
||||
split_dasd(dt, s->value, to);
|
||||
continue;
|
||||
}
|
||||
/* Just copy remaining attributes. */
|
||||
setting_list_apply_actual(to, s->attrib, s->name, s->value);
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert a devtype settings list to a module parameter list. */
|
||||
static void convert_settings_to_params(struct devtype *dt,
|
||||
struct setting_list *from,
|
||||
struct setting_list **to)
|
||||
{
|
||||
struct setting *s_from, *s_to;
|
||||
struct util_list *dasd;
|
||||
int dasd_mod;
|
||||
char *flat;
|
||||
|
||||
*to = setting_list_new();
|
||||
dasd = strlist_new();
|
||||
dasd_mod = 0;
|
||||
|
||||
util_list_iterate(&from->list, s_from) {
|
||||
if (s_from->removed)
|
||||
continue;
|
||||
if (s_from->derived && !s_from->modified)
|
||||
continue;
|
||||
if (strcmp(s_from->name, "eer_pages") == 0) {
|
||||
/* Copy stand-alone parameter. */
|
||||
setting_list_add(*to, setting_copy(s_from));
|
||||
continue;
|
||||
}
|
||||
/* All other settings need to go into dasd=. */
|
||||
if (strcmp(s_from->name, "dasd") == 0) {
|
||||
/* Just add explicit dasd= content. */
|
||||
strlist_add(dasd, "%s", s_from->value);
|
||||
} else if (strcmp(s_from->value, "0") == 0) {
|
||||
/* A flag in dasd= counts as set, so skip the unset
|
||||
* ones. */
|
||||
continue;
|
||||
} else {
|
||||
/* Just add the name for set attributes. */
|
||||
strlist_add(dasd, "%s", s_from->name);
|
||||
dasd_mod += s_from->modified;
|
||||
}
|
||||
}
|
||||
|
||||
flat = strlist_flatten(dasd, ",");
|
||||
s_to = setting_new(NULL, "dasd", flat);
|
||||
free(flat);
|
||||
if (dasd_mod > 0)
|
||||
s_to->modified = 1;
|
||||
if (util_list_is_empty(dasd))
|
||||
s_to->removed = 1;
|
||||
setting_list_add(*to, s_to);
|
||||
strlist_free(dasd);
|
||||
}
|
||||
|
||||
static exit_code_t dasd_devtype_read_settings(struct devtype *dt,
|
||||
config_t config)
|
||||
{
|
||||
struct setting_list *list;
|
||||
char *path;
|
||||
exit_code_t rc = EXIT_OK;
|
||||
|
||||
if (SCOPE_ACTIVE(config) && !dt->active_settings) {
|
||||
dt->active_exists = 0;
|
||||
rc = module_get_params(DASD_MOD_NAME, dt->type_attribs, &list);
|
||||
if (rc)
|
||||
return rc;
|
||||
dt->active_settings = setting_list_new();
|
||||
if (list) {
|
||||
convert_params_to_settings(dt, list,
|
||||
dt->active_settings);
|
||||
setting_list_mark_default_derived(dt->active_settings);
|
||||
setting_list_apply_defaults(dt->active_settings,
|
||||
dt->type_attribs, false);
|
||||
setting_list_free(list);
|
||||
dt->active_exists = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (SCOPE_PERSISTENT(config) && !dt->persistent_settings) {
|
||||
dt->persistent_exists = 0;
|
||||
path = path_get_modprobe_conf(dt);
|
||||
rc = modprobe_read_settings(path, DASD_MOD_NAME,
|
||||
dt->type_attribs, &list);
|
||||
free(path);
|
||||
if (rc)
|
||||
return rc;
|
||||
dt->persistent_settings = setting_list_new();
|
||||
if (list) {
|
||||
convert_params_to_settings(dt, list,
|
||||
dt->persistent_settings);
|
||||
setting_list_apply_defaults(dt->persistent_settings,
|
||||
dt->type_attribs, false);
|
||||
setting_list_free(list);
|
||||
dt->persistent_exists = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static exit_code_t dasd_devtype_write_settings(struct devtype *dt,
|
||||
config_t config)
|
||||
{
|
||||
struct setting_list *list;
|
||||
char *path;
|
||||
exit_code_t rc = EXIT_OK;
|
||||
|
||||
if (SCOPE_ACTIVE(config) && dt->active_settings) {
|
||||
/* Try setting parameters directly via Sysfs. */
|
||||
if (module_set_params(DASD_MOD_NAME, dt->active_settings))
|
||||
goto persistent;
|
||||
|
||||
convert_settings_to_params(dt, dt->active_settings, &list);
|
||||
rc = module_load(DASD_MOD_NAME, dasd_mods, list,
|
||||
err_delayed_print);
|
||||
setting_list_free(list);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
persistent:
|
||||
if (SCOPE_PERSISTENT(config) && dt->persistent_settings) {
|
||||
path = path_get_modprobe_conf(dt);
|
||||
if (!rc) {
|
||||
convert_settings_to_params(dt, dt->persistent_settings,
|
||||
&list);
|
||||
rc = modprobe_write_settings(path, DASD_MOD_NAME, list);
|
||||
setting_list_free(list);
|
||||
}
|
||||
free(path);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* DASD device sub-types.
|
||||
*/
|
||||
static struct ccw_subtype_data dasd_eckd_data = {
|
||||
.ccwdrv = "dasd-eckd",
|
||||
.mod = "dasd_eckd_mod",
|
||||
};
|
||||
|
||||
static struct subtype dasd_subtype_eckd = {
|
||||
.super = &ccw_subtype,
|
||||
.devtype = &dasd_devtype,
|
||||
.name = "dasd-eckd",
|
||||
.title = "Enhanced Count Key Data (ECKD) DASDs",
|
||||
.devname = "ECKD DASD",
|
||||
.modules = STRING_ARRAY(DASD_ECKD_MOD_NAME),
|
||||
.namespace = &ccw_namespace,
|
||||
.data = &dasd_eckd_data,
|
||||
|
||||
.dev_attribs = ATTRIB_ARRAY(
|
||||
&ccw_attr_online_force,
|
||||
&ccw_attr_cmb_enable,
|
||||
&dasd_attr_failfast,
|
||||
&dasd_attr_readonly,
|
||||
&dasd_attr_erplog,
|
||||
&dasd_attr_use_diag,
|
||||
&dasd_attr_raw_track_access,
|
||||
&dasd_attr_eer_enabled,
|
||||
&dasd_attr_expires,
|
||||
&dasd_attr_retries,
|
||||
&dasd_attr_timeout,
|
||||
&dasd_attr_reservation_policy,
|
||||
&dasd_attr_last_known_reservation_state,
|
||||
&dasd_attr_safe_offline,
|
||||
),
|
||||
.unknown_dev_attribs = 1,
|
||||
|
||||
.check_pre_configure = &dasd_st_check_pre_configure,
|
||||
.add_modules = &dasd_st_add_modules,
|
||||
};
|
||||
|
||||
static struct ccw_subtype_data dasd_fba_data = {
|
||||
.ccwdrv = "dasd-fba",
|
||||
.mod = "dasd_fba_mod",
|
||||
};
|
||||
|
||||
static struct subtype dasd_subtype_fba = {
|
||||
.super = &ccw_subtype,
|
||||
.devtype = &dasd_devtype,
|
||||
.name = "dasd-fba",
|
||||
.title = "Fixed Block Architecture (FBA) DASDs",
|
||||
.devname = "FBA DASD",
|
||||
.modules = STRING_ARRAY(DASD_FBA_MOD_NAME),
|
||||
.namespace = &ccw_namespace,
|
||||
.data = &dasd_fba_data,
|
||||
|
||||
.dev_attribs = ATTRIB_ARRAY(
|
||||
&ccw_attr_online_force,
|
||||
&ccw_attr_cmb_enable,
|
||||
&dasd_attr_failfast,
|
||||
&dasd_attr_readonly,
|
||||
&dasd_attr_erplog,
|
||||
&dasd_attr_use_diag,
|
||||
&dasd_attr_expires,
|
||||
&dasd_attr_retries,
|
||||
&dasd_attr_timeout,
|
||||
&dasd_attr_reservation_policy,
|
||||
&dasd_attr_last_known_reservation_state,
|
||||
&dasd_attr_safe_offline,
|
||||
),
|
||||
.unknown_dev_attribs = 1,
|
||||
|
||||
.check_pre_configure = &dasd_st_check_pre_configure,
|
||||
.add_modules = &dasd_st_add_modules,
|
||||
};
|
||||
|
||||
/*
|
||||
* DASD device type.
|
||||
*/
|
||||
|
||||
struct devtype dasd_devtype = {
|
||||
.name = "dasd",
|
||||
.title = "FICON-attached Direct Access Storage Devices "
|
||||
"(DASDs)",
|
||||
.devname = "DASD",
|
||||
.modules = STRING_ARRAY(DASD_MOD_NAME),
|
||||
|
||||
.subtypes = SUBTYPE_ARRAY(
|
||||
&dasd_subtype_eckd,
|
||||
&dasd_subtype_fba,
|
||||
),
|
||||
|
||||
.type_attribs = ATTRIB_ARRAY(
|
||||
&dasd_tattr_autodetect,
|
||||
&dasd_tattr_probeonly,
|
||||
&dasd_tattr_nopav,
|
||||
&dasd_tattr_nofcx,
|
||||
&dasd_tattr_eer_pages,
|
||||
),
|
||||
|
||||
.exit = &dasd_devtype_exit,
|
||||
|
||||
.read_settings = &dasd_devtype_read_settings,
|
||||
.write_settings = &dasd_devtype_write_settings,
|
||||
};
|
||||
569
zdev/src/device.c
Normal file
569
zdev/src/device.c
Normal file
@@ -0,0 +1,569 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "attrib.h"
|
||||
#include "device.h"
|
||||
#include "devtype.h"
|
||||
#include "misc.h"
|
||||
#include "namespace.h"
|
||||
#include "setting.h"
|
||||
#include "subtype.h"
|
||||
#include "udev.h"
|
||||
|
||||
/* Create and initialize a new device. */
|
||||
struct device *device_new(struct subtype *st, const char *id)
|
||||
{
|
||||
struct device *dev;
|
||||
struct namespace *ns = st->namespace;
|
||||
|
||||
dev = misc_malloc(sizeof(struct device));
|
||||
dev->subtype = st;
|
||||
|
||||
dev->id = ns->normalize_id(id);
|
||||
dev->devid = ns->parse_id(id, 0);
|
||||
if (!dev->id || !dev->devid) {
|
||||
device_free(dev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dev->active.settings = setting_list_new();
|
||||
dev->persistent.settings = setting_list_new();
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
/* Release all resources associated with the specified device. */
|
||||
void device_free(struct device *dev)
|
||||
{
|
||||
if (!dev)
|
||||
return;
|
||||
free(dev->id);
|
||||
free(dev->devid);
|
||||
setting_list_free(dev->active.settings);
|
||||
setting_list_free(dev->persistent.settings);
|
||||
free(dev);
|
||||
}
|
||||
|
||||
/* Used for debugging. */
|
||||
void device_print(struct device *dev, int level)
|
||||
{
|
||||
printf("%*sdevice at %p:\n", level, "", (void *) dev);
|
||||
if (!dev)
|
||||
return;
|
||||
printf("%*stype=%s id=%s devid=%p proc=%d\n", level + 4, "",
|
||||
dev->subtype->name, dev->id, dev->devid, dev->processed);
|
||||
|
||||
printf("%*sactive:\n", level + 4, "");
|
||||
printf("%*sexists=%d mod=%d deconf=%d def=%d blacklisted=%d\n",
|
||||
level + 8, "", dev->active.exists, dev->active.modified,
|
||||
dev->active.deconfigured, dev->active.definable,
|
||||
dev->active.blacklisted);
|
||||
if (dev->active.settings)
|
||||
setting_list_print(dev->active.settings, level + 8);
|
||||
else
|
||||
printf("%*s<none>\n", level + 8, "");
|
||||
|
||||
printf("%*spersistent:\n", level + 4, "");
|
||||
printf("%*sexists=%d mod=%d deconf=%d\n",
|
||||
level + 8, "", dev->persistent.exists, dev->persistent.modified,
|
||||
dev->persistent.deconfigured);
|
||||
if (dev->persistent.settings)
|
||||
setting_list_print(dev->persistent.settings, level + 8);
|
||||
else
|
||||
printf("%*s<none>\n", level + 8, "");
|
||||
}
|
||||
|
||||
static const void *device_hash_get_id(void *dev_ptr)
|
||||
{
|
||||
struct device *dev = dev_ptr;
|
||||
|
||||
return dev->devid;
|
||||
}
|
||||
|
||||
/* Create and initialize a new device_list. */
|
||||
struct device_list *device_list_new(struct subtype *st)
|
||||
{
|
||||
struct device_list *list;
|
||||
struct namespace *ns = st->namespace;
|
||||
|
||||
list = misc_malloc(sizeof(struct device_list));
|
||||
hash_init(&list->hash, ns->hash_buckets, device_hash_get_id,
|
||||
ns->cmp_parsed_ids, ns->hash_parsed_id,
|
||||
struct device, node);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/* Release resources used by list and enlisted devices. */
|
||||
void device_list_free(struct device_list *list)
|
||||
{
|
||||
if (!list)
|
||||
return;
|
||||
hash_clear(&list->hash, (void (*)(void *)) device_free);
|
||||
free(list);
|
||||
}
|
||||
|
||||
/* Add a new element to a list and mark the list as modified. */
|
||||
void device_list_add(struct device_list *list, struct device *device)
|
||||
{
|
||||
hash_add(&list->hash, device);
|
||||
list->modified = 1;
|
||||
}
|
||||
|
||||
/* Find an element in the list. */
|
||||
struct device *device_list_find(struct device_list *list, const char *id,
|
||||
struct device *start)
|
||||
{
|
||||
struct device *dev;
|
||||
struct namespace *ns;
|
||||
void *devid;
|
||||
|
||||
if (!list)
|
||||
return NULL;
|
||||
|
||||
dev = start ? start : util_list_start(&list->hash.list);
|
||||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
ns = dev->subtype->namespace;
|
||||
devid = ns->parse_id(id, err_ignore);
|
||||
if (!devid)
|
||||
goto out;
|
||||
|
||||
/* Try to find using hashed ID data. */
|
||||
if (!start && list->hash.get_hash) {
|
||||
dev = hash_find_by_id(&list->hash, devid);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Find the slow way. */
|
||||
while (dev) {
|
||||
if (ns->cmp_parsed_ids(dev->devid, devid) == 0)
|
||||
goto out;
|
||||
dev = util_list_next(&list->hash.list, dev);
|
||||
}
|
||||
|
||||
out:
|
||||
free(devid);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
/* Used for debugging. */
|
||||
void device_list_print(struct device_list *list, int level)
|
||||
{
|
||||
struct device *dev;
|
||||
|
||||
printf("%*sdevice list at %p:\n", level, "", (void *) list);
|
||||
util_list_iterate(&list->hash.list, dev)
|
||||
device_print(dev, level + 4);
|
||||
}
|
||||
|
||||
/* Check if a device configuration needs to be written. */
|
||||
bool device_needs_writing(struct device *dev, config_t config)
|
||||
{
|
||||
if (SCOPE_ACTIVE(config) &&
|
||||
(dev->active.modified || dev->active.deconfigured ||
|
||||
setting_list_modified(dev->active.settings)))
|
||||
return true;
|
||||
if (SCOPE_PERSISTENT(config) &&
|
||||
(dev->persistent.modified || dev->persistent.deconfigured ||
|
||||
setting_list_modified(dev->persistent.settings)))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static exit_code_t apply_setting(struct device *dev, config_t config,
|
||||
const char *key, const char *value,
|
||||
struct util_list *processed)
|
||||
{
|
||||
struct subtype *st = dev->subtype;
|
||||
struct attrib *a;
|
||||
struct setting *s;
|
||||
bool warn_readonly = false;
|
||||
|
||||
/* Check for known attribute. */
|
||||
a = subtype_find_dev_attrib(st, key);
|
||||
if (a) {
|
||||
/* Check for acceptable value of known attribute. */
|
||||
if (!force && !attrib_check_value(a, value))
|
||||
goto err_invalid_forceable;
|
||||
/* Check for activeonly. */
|
||||
if (!force && SCOPE_PERSISTENT(config) && a->activeonly)
|
||||
goto err_activeonly_forceable;
|
||||
/* Check for multiple values. */
|
||||
if (!force && !a->multi && strlist_find(processed, key))
|
||||
goto err_multi_forceable;
|
||||
} else {
|
||||
/* Handle unknown attribute. */
|
||||
if (!st->unknown_dev_attribs)
|
||||
goto err_unknown;
|
||||
if (!force)
|
||||
goto err_unknown_forceable;
|
||||
}
|
||||
|
||||
strlist_add(processed, "%s", key);
|
||||
|
||||
/* Apply to active config. */
|
||||
if (SCOPE_ACTIVE(config)) {
|
||||
s = setting_list_apply_specified(dev->active.settings, a,
|
||||
key, value);
|
||||
if (s->readonly)
|
||||
warn_readonly = true;
|
||||
}
|
||||
|
||||
/* Apply to persistent config. */
|
||||
if (SCOPE_PERSISTENT(config)) {
|
||||
setting_list_apply_specified(dev->persistent.settings,
|
||||
a, key, value);
|
||||
}
|
||||
|
||||
/* Additional warning when trying to persist read-only setting. */
|
||||
if (config == config_persistent) {
|
||||
s = setting_list_find(dev->active.settings, key);
|
||||
if (s && s->readonly)
|
||||
warn_readonly = true;
|
||||
}
|
||||
|
||||
if (warn_readonly)
|
||||
delayed_warn("Modifying read-only attribute: %s\n", key);
|
||||
|
||||
return EXIT_OK;
|
||||
|
||||
err_invalid_forceable:
|
||||
delayed_forceable("Invalid value for %s attribute: %s=%s\n",
|
||||
dev->subtype->name, key, value);
|
||||
delayed_info("Acceptable values:\n");
|
||||
attrib_print_acceptable(a, -1);
|
||||
delayed_info("Use '%s %s --help-attribute %s' for more "
|
||||
"information\n", toolname, dev->subtype->name, key);
|
||||
return EXIT_INVALID_SETTING;
|
||||
|
||||
err_multi_forceable:
|
||||
delayed_forceable("Cannot specify multiple values for attribute '%s'\n",
|
||||
key);
|
||||
return EXIT_INVALID_SETTING;
|
||||
|
||||
err_unknown:
|
||||
delayed_err("Unknown %s attribute specified: %s\n", st->devname, key);
|
||||
return EXIT_ATTRIB_NOT_FOUND;
|
||||
|
||||
err_unknown_forceable:
|
||||
delayed_forceable("Unknown %s attribute specified: %s\n",
|
||||
st->devname, key);
|
||||
return EXIT_ATTRIB_NOT_FOUND;
|
||||
|
||||
err_activeonly_forceable:
|
||||
delayed_forceable("Attribute '%s' should only be changed in the active "
|
||||
"config\n", a->name);
|
||||
return EXIT_INVALID_SETTING;
|
||||
}
|
||||
|
||||
/* Apply device settings from strlist to device. */
|
||||
exit_code_t device_apply_strlist(struct device *dev, config_t config,
|
||||
struct util_list *settings)
|
||||
{
|
||||
struct util_list *processed;
|
||||
struct strlist_node *s;
|
||||
exit_code_t rc;
|
||||
char *key, *value;
|
||||
|
||||
/* Apply settings. */
|
||||
processed = strlist_new();
|
||||
rc = EXIT_OK;
|
||||
util_list_iterate(settings, s) {
|
||||
key = misc_strdup(s->str);
|
||||
value = strchr(key, '=');
|
||||
*value = 0;
|
||||
value++;
|
||||
|
||||
rc = apply_setting(dev, config, key, value, processed);
|
||||
free(key);
|
||||
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
|
||||
strlist_free(processed);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Apply device settings from setting_list to device. */
|
||||
exit_code_t device_apply_settings(struct device *dev, config_t config,
|
||||
struct util_list *settings)
|
||||
{
|
||||
struct util_list *processed;
|
||||
struct setting *s;
|
||||
exit_code_t rc;
|
||||
|
||||
/* Apply settings. */
|
||||
processed = strlist_new();
|
||||
rc = EXIT_OK;
|
||||
util_list_iterate(settings, s) {
|
||||
rc = apply_setting(dev, config, s->name, s->value, processed);
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
|
||||
strlist_free(processed);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void reset_device_state(struct device_state *state)
|
||||
{
|
||||
setting_list_free(state->settings);
|
||||
state->settings = setting_list_new();
|
||||
state->exists = 0;
|
||||
state->modified = 0;
|
||||
state->deconfigured = 0;
|
||||
state->definable = 0;
|
||||
state->blacklisted = 0;
|
||||
}
|
||||
|
||||
void device_reset(struct device *dev, config_t config)
|
||||
{
|
||||
if (SCOPE_ACTIVE(config))
|
||||
reset_device_state(&dev->active);
|
||||
if (SCOPE_PERSISTENT(config))
|
||||
reset_device_state(&dev->persistent);
|
||||
dev->processed = 0;
|
||||
}
|
||||
|
||||
void device_add_modules(struct util_list *modules, struct device *dev)
|
||||
{
|
||||
struct subtype *st = dev->subtype;
|
||||
|
||||
/* Add dynamic module info. */
|
||||
subtype_add_modules(st, dev, modules);
|
||||
|
||||
/* Add static subtype module info. */
|
||||
subtype_add_static_modules(modules, st);
|
||||
|
||||
/* Add static devtype module info. */
|
||||
devtype_add_modules(modules, st->devtype, 0);
|
||||
}
|
||||
|
||||
char *device_read_active_attrib(struct device *dev, const char *name)
|
||||
{
|
||||
struct subtype *st = dev->subtype;
|
||||
char *path, *value, *link;
|
||||
|
||||
/* Try direct approach. */
|
||||
value = subtype_get_active_attrib(st, dev, name);
|
||||
if (value)
|
||||
return value;
|
||||
|
||||
/* Try reading from path. */
|
||||
path = subtype_get_active_attrib_path(st, dev, name);
|
||||
if (!path)
|
||||
return NULL;
|
||||
value = misc_read_text_file(path, 1, err_ignore);
|
||||
if (!value) {
|
||||
/* Symbolic links count as read-only attributes. */
|
||||
link = misc_readlink(path);
|
||||
if (link) {
|
||||
value = misc_strdup(basename(link));
|
||||
free(link);
|
||||
}
|
||||
}
|
||||
free(path);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/* Return a newly allocated strlist of attribute names for @dev based on
|
||||
* @scope. */
|
||||
static struct util_list *get_attrib_names(struct device *dev,
|
||||
read_scope_t scope)
|
||||
{
|
||||
struct subtype *st = dev->subtype;
|
||||
struct util_list *names, *files;
|
||||
struct strlist_node *s;
|
||||
int i;
|
||||
struct attrib *a;
|
||||
const char *prefix;
|
||||
char *path;
|
||||
|
||||
names = strlist_new();
|
||||
|
||||
/* Start with known and mandatory attributes. */
|
||||
for (i = 0; (a = st->dev_attribs[i]); i++) {
|
||||
if (scope != scope_mandatory || a->mandatory)
|
||||
strlist_add(names, a->name);
|
||||
}
|
||||
if (scope != scope_all)
|
||||
goto out;
|
||||
|
||||
/* Add attributes based on readable files in any of the prefix
|
||||
* directories. */
|
||||
prefix = "";
|
||||
i = 0;
|
||||
do {
|
||||
path = subtype_get_active_attrib_path(st, dev, prefix);
|
||||
if (!path)
|
||||
continue;
|
||||
|
||||
/* Add attribute name for each file in path. */
|
||||
files = strlist_new();
|
||||
misc_read_dir(path, files, NULL, NULL);
|
||||
util_list_iterate(files, s) {
|
||||
if (*prefix) {
|
||||
strlist_add_unique(names, "%s/%s", prefix,
|
||||
s->str);
|
||||
} else
|
||||
strlist_add_unique(names, s->str);
|
||||
}
|
||||
strlist_free(files);
|
||||
|
||||
free(path);
|
||||
} while (st->prefixes && (prefix = st->prefixes[i++]));
|
||||
|
||||
out:
|
||||
return names;
|
||||
}
|
||||
|
||||
/* Read settings according to @scope for device @dev from active
|
||||
* configuration and add them to dev->active.settings. */
|
||||
void device_read_active_settings(struct device *dev, read_scope_t scope)
|
||||
{
|
||||
struct subtype *st = dev->subtype;
|
||||
struct util_list *names;
|
||||
struct strlist_node *str;
|
||||
char *path, *name, *value, *link;
|
||||
struct attrib *a;
|
||||
struct setting *s;
|
||||
|
||||
/* Expand scope. */
|
||||
names = get_attrib_names(dev, scope);
|
||||
|
||||
util_list_iterate(names, str) {
|
||||
name = str->str;
|
||||
|
||||
/* Don't add uevent attribute. */
|
||||
if (strcmp(name, "uevent") == 0 || ends_with(name, "/uevent"))
|
||||
continue;
|
||||
|
||||
/* Determine full attribute path. */
|
||||
path = subtype_get_active_attrib_path(st, dev, name);
|
||||
if (!path)
|
||||
continue;
|
||||
|
||||
/* Get attribute value. */
|
||||
link = NULL;
|
||||
value = misc_read_text_file(path, 1, err_ignore);
|
||||
if (!value) {
|
||||
if (scope != scope_all)
|
||||
goto next;
|
||||
|
||||
/* Register symbolic links as readonly attributes. */
|
||||
link = misc_readlink(path);
|
||||
if (!link)
|
||||
goto next;
|
||||
|
||||
value = basename(link);
|
||||
}
|
||||
|
||||
/* Apply setting to list. */
|
||||
a = attrib_find(st->dev_attribs, name);
|
||||
s = setting_list_apply_actual(dev->active.settings, a, name,
|
||||
value);
|
||||
if (link || (scope == scope_all && !file_writable(path)))
|
||||
s->readonly = 1;
|
||||
if (link)
|
||||
free(link);
|
||||
else
|
||||
free(value);
|
||||
next:
|
||||
free(path);
|
||||
}
|
||||
|
||||
strlist_free(names);
|
||||
}
|
||||
|
||||
/* Apply modified settings in @dev->active.settings to active configuration.
|
||||
* Abort on first error. This requires that @dev defines
|
||||
* get_active_attrib_path. */
|
||||
exit_code_t device_write_active_settings(struct device *dev)
|
||||
{
|
||||
struct subtype *st = dev->subtype;
|
||||
struct util_list *list;
|
||||
struct ptrlist_node *p;
|
||||
struct setting *s;
|
||||
char *path;
|
||||
exit_code_t rc = EXIT_OK;
|
||||
|
||||
/* Get order of applying attributes. */
|
||||
list = setting_list_get_sorted(dev->active.settings);
|
||||
|
||||
/* Apply settings in order. */
|
||||
util_list_iterate(list, p) {
|
||||
s = p->ptr;
|
||||
if (!s->modified || s->removed)
|
||||
continue;
|
||||
|
||||
path = subtype_get_active_attrib_path(st, dev, s->name);
|
||||
if (!path) {
|
||||
delayed_err("Could not determine path for attribute "
|
||||
"'%s'\n", s->name);
|
||||
rc = EXIT_SETTING_NOT_FOUND;
|
||||
break;
|
||||
}
|
||||
rc = setting_write(path, s);
|
||||
free(path);
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
|
||||
ptrlist_free(list, 0);
|
||||
|
||||
/* Changing device configuration could generate uevents. */
|
||||
udev_need_settle = 1;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Check if there are any conflicts in the settings list of @dev for the
|
||||
* specified configuration. */
|
||||
exit_code_t device_check_settings(struct device *dev, config_t config,
|
||||
err_t err)
|
||||
{
|
||||
struct setting *s;
|
||||
|
||||
if (SCOPE_ACTIVE(config)) {
|
||||
/* Ensure that actual values are available or we might
|
||||
* report false positives. */
|
||||
util_list_iterate(&dev->active.settings->list, s) {
|
||||
if (s->actual_value || s->removed ||
|
||||
(!s->modified && !s->specified))
|
||||
continue;
|
||||
s->actual_value =
|
||||
device_read_active_attrib(dev, s->name);
|
||||
}
|
||||
|
||||
if (!setting_list_check_conflict(dev->active.settings,
|
||||
config_active,
|
||||
err))
|
||||
return EXIT_INVALID_CONFIG;
|
||||
}
|
||||
if (SCOPE_PERSISTENT(config)) {
|
||||
if (!setting_list_check_conflict(dev->persistent.settings,
|
||||
config_persistent,
|
||||
err))
|
||||
return EXIT_INVALID_CONFIG;
|
||||
}
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
316
zdev/src/devnode.c
Normal file
316
zdev/src/devnode.c
Normal file
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "devnode.h"
|
||||
#include "misc.h"
|
||||
#include "path.h"
|
||||
|
||||
/* Create a newly allocated devnode object from the specified data. */
|
||||
struct devnode *devnode_new(devnode_t type, unsigned int major,
|
||||
unsigned int minor, const char *name)
|
||||
{
|
||||
struct devnode *devnode;
|
||||
size_t len;
|
||||
|
||||
len = strlen(name);
|
||||
devnode = misc_malloc(sizeof(struct devnode) + len + 1);
|
||||
devnode->type = type;
|
||||
devnode->major = major;
|
||||
devnode->minor = minor;
|
||||
memcpy(devnode->name, name, len);
|
||||
|
||||
return devnode;
|
||||
}
|
||||
|
||||
struct devnode *devnode_copy(struct devnode *d)
|
||||
{
|
||||
return devnode_new(d->type, d->major, d->minor, d->name);
|
||||
}
|
||||
|
||||
/* Used for debugging. */
|
||||
void devnode_print(struct devnode *devnode, int level)
|
||||
{
|
||||
printf("%*sdevnode at %p\n", level, "", (void *) devnode);
|
||||
level += 2;
|
||||
printf("%*stype=%d\n", level, "", devnode->type);
|
||||
printf("%*smajor=%d\n", level, "", devnode->major);
|
||||
printf("%*sminor=%d\n", level, "", devnode->minor);
|
||||
printf("%*sname=%s\n", level, "", devnode->name);
|
||||
}
|
||||
|
||||
/* Create a newly allocated devnode object from a device special file. */
|
||||
struct devnode *devnode_from_node(const char *path, err_t err)
|
||||
{
|
||||
struct stat s;
|
||||
const char *name;
|
||||
|
||||
if (stat(path, &s)) {
|
||||
err_t_print(err, "Could not get information for %s: %s\n",
|
||||
path, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
if (!S_ISBLK(s.st_mode) && !S_ISCHR(s.st_mode)) {
|
||||
err_t_print(err, "File is not a device special file: %s\n",
|
||||
path);
|
||||
return NULL;
|
||||
}
|
||||
name = strrchr(path, '/');
|
||||
if (name)
|
||||
name++;
|
||||
else
|
||||
name = path;
|
||||
|
||||
return devnode_new(S_ISBLK(s.st_mode) ? BLOCKDEV : CHARDEV,
|
||||
major(s.st_rdev), minor(s.st_rdev), name);
|
||||
}
|
||||
|
||||
/* Create a newly allocated devnode object from a sysfs dev file containing
|
||||
* major:minor data in text form. */
|
||||
struct devnode *devnode_from_devfile(const char *path, const char *name,
|
||||
devnode_t type)
|
||||
{
|
||||
char *dev, *syspath = NULL;
|
||||
struct devnode *devnode = NULL;
|
||||
unsigned int major, minor;
|
||||
|
||||
if (!path) {
|
||||
syspath = path_get_sys_block_dev(name);
|
||||
path = syspath;
|
||||
}
|
||||
|
||||
dev = misc_read_text_file(path, 1, err_ignore);
|
||||
if (!dev)
|
||||
goto out;
|
||||
|
||||
if (sscanf(dev, "%u:%u", &major, &minor) == 2)
|
||||
devnode = devnode_new(type, major, minor, name);
|
||||
|
||||
out:
|
||||
free(dev);
|
||||
free(syspath);
|
||||
|
||||
return devnode;
|
||||
}
|
||||
|
||||
/* Create a newly allocated devnode object from a major:minor combination. */
|
||||
static struct devnode *devnode_from_majmin(devnode_t type, unsigned int major,
|
||||
unsigned int minor)
|
||||
{
|
||||
char *path, *link, *name;
|
||||
struct devnode *devnode = NULL;
|
||||
|
||||
switch (type) {
|
||||
case BLOCKDEV:
|
||||
path = path_get_sys_dev_block(major, minor);
|
||||
break;
|
||||
case CHARDEV:
|
||||
path = path_get_sys_dev_char(major, minor);
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
link = misc_readlink(path);
|
||||
if (!link)
|
||||
goto out;
|
||||
|
||||
name = strrchr(link, '/');
|
||||
if (name)
|
||||
name++;
|
||||
else
|
||||
name = link;
|
||||
|
||||
devnode = devnode_new(type, major, minor, name);
|
||||
|
||||
out:
|
||||
free(link);
|
||||
free(path);
|
||||
|
||||
return devnode;
|
||||
}
|
||||
|
||||
/* Return a devnode that represents the device on which path is located. */
|
||||
struct devnode *devnode_from_path(const char *path)
|
||||
{
|
||||
struct stat s;
|
||||
|
||||
if (stat(path, &s))
|
||||
return NULL;
|
||||
|
||||
return devnode_from_majmin(BLOCKDEV, major(s.st_dev), minor(s.st_dev));
|
||||
}
|
||||
|
||||
/* Compare two devnodes by type, major and minor. Return:
|
||||
* -1: a < b
|
||||
* 1: a > b
|
||||
* 0: a == b
|
||||
**/
|
||||
int devnode_cmp(struct devnode *a, struct devnode *b)
|
||||
{
|
||||
if (a->type < b->type)
|
||||
return -1;
|
||||
if (a->type > b->type)
|
||||
return 1;
|
||||
if (a->type == BLOCKDEV || a->type == CHARDEV) {
|
||||
/* Block and character devices are compared by maj:min only. */
|
||||
if (a->major < b->major)
|
||||
return -1;
|
||||
if (a->major > b->major)
|
||||
return 1;
|
||||
if (a->minor < b->minor)
|
||||
return -1;
|
||||
if (a->minor > b->minor)
|
||||
return 1;
|
||||
} else {
|
||||
/* Network interfaces are compared by name only. */
|
||||
return strcmp(a->name, b->name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct add_cb_data {
|
||||
struct util_list *list;
|
||||
int num;
|
||||
const char *prefix;
|
||||
};
|
||||
|
||||
static exit_code_t add_part_cb(const char *path, const char *filename,
|
||||
void *data)
|
||||
{
|
||||
struct add_cb_data *cb_data = data;
|
||||
struct devnode *node;
|
||||
char *devpath;
|
||||
|
||||
if (!starts_with(filename, cb_data->prefix))
|
||||
return EXIT_OK;
|
||||
|
||||
devpath = misc_asprintf("%s/dev", path);
|
||||
node = devnode_from_devfile(devpath, filename, BLOCKDEV);
|
||||
free(devpath);
|
||||
if (node) {
|
||||
ptrlist_add(cb_data->list, node);
|
||||
cb_data->num++;
|
||||
}
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
/* Add block device names to ptrlist found in @DATA. Note: the first entry
|
||||
* is always the main block device - when available, partitions will be
|
||||
* reported second. */
|
||||
static exit_code_t add_block_cb(const char *path, const char *filename,
|
||||
void *data)
|
||||
{
|
||||
struct add_cb_data *cb_data = data;
|
||||
struct devnode *node;
|
||||
char *devpath;
|
||||
|
||||
/* Add main node. */
|
||||
devpath = misc_asprintf("%s/dev", path);
|
||||
node = devnode_from_devfile(devpath, filename, BLOCKDEV);
|
||||
free(devpath);
|
||||
if (!node)
|
||||
return EXIT_OK;
|
||||
ptrlist_add(cb_data->list, node);
|
||||
cb_data->num++;
|
||||
|
||||
/* Add additional nodes. */
|
||||
cb_data->prefix = filename;
|
||||
if (dir_exists(path))
|
||||
path_for_each(path, add_part_cb, cb_data);
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
/* Add devnode objects to ptrlist LIST. Each devnode object represents one
|
||||
* block device node that is provided by the device found at sysfs path PATH.
|
||||
* Return the number of added objects. */
|
||||
int devnode_add_block_from_sysfs(struct util_list *list, const char *path)
|
||||
{
|
||||
struct add_cb_data cb_data;
|
||||
char *blkpath;
|
||||
|
||||
cb_data.list = list;
|
||||
cb_data.num = 0;
|
||||
cb_data.prefix = NULL;
|
||||
|
||||
blkpath = misc_asprintf("%s/block", path);
|
||||
if (dir_exists(blkpath))
|
||||
path_for_each(blkpath, add_block_cb, &cb_data);
|
||||
free(blkpath);
|
||||
|
||||
return cb_data.num;
|
||||
}
|
||||
|
||||
static exit_code_t add_net_cb(const char *path, const char *filename,
|
||||
void *data)
|
||||
{
|
||||
struct add_cb_data *cb_data = data;
|
||||
struct devnode *node;
|
||||
|
||||
/* Add main node. */
|
||||
node = devnode_new(NETDEV, 0, 0, filename);
|
||||
ptrlist_add(cb_data->list, node);
|
||||
cb_data->num++;
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
/* Add devnode objects to ptrlist LIST. Each devnode object represents one
|
||||
* network interface that is provided by the device found at sysfs path PATH.
|
||||
* Return the number of added objects. */
|
||||
int devnode_add_net_from_sysfs(struct util_list *list, const char *path)
|
||||
{
|
||||
struct add_cb_data cb_data;
|
||||
char *netpath;
|
||||
|
||||
cb_data.list = list;
|
||||
cb_data.num = 0;
|
||||
cb_data.prefix = NULL;
|
||||
|
||||
netpath = misc_asprintf("%s/net", path);
|
||||
if (dir_exists(netpath))
|
||||
path_for_each(netpath, add_net_cb, &cb_data);
|
||||
free(netpath);
|
||||
|
||||
return cb_data.num;
|
||||
}
|
||||
|
||||
/* Return the contents of the link in /sys/dev/ for @devnode or %NULL if the
|
||||
* link could not be read. */
|
||||
char *devnode_readlink(struct devnode *devnode)
|
||||
{
|
||||
char *path, *link;
|
||||
|
||||
switch (devnode->type) {
|
||||
case BLOCKDEV:
|
||||
path = path_get_sys_dev_block(devnode->major, devnode->minor);
|
||||
break;
|
||||
case CHARDEV:
|
||||
path = path_get_sys_dev_char(devnode->major, devnode->minor);
|
||||
break;
|
||||
case NETDEV:
|
||||
path = path_get_sys_class("net", devnode->name);
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
link = misc_readlink(path);
|
||||
free(path);
|
||||
|
||||
return link;
|
||||
}
|
||||
412
zdev/src/devtype.c
Normal file
412
zdev/src/devtype.c
Normal file
@@ -0,0 +1,412 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
|
||||
#include "attrib.h"
|
||||
#include "ctc.h"
|
||||
#include "dasd.h"
|
||||
#include "device.h"
|
||||
#include "devnode.h"
|
||||
#include "devtype.h"
|
||||
#include "generic_ccw.h"
|
||||
#include "lcs.h"
|
||||
#include "misc.h"
|
||||
#include "module.h"
|
||||
#include "namespace.h"
|
||||
#include "qeth.h"
|
||||
#include "select.h"
|
||||
#include "setting.h"
|
||||
#include "subtype.h"
|
||||
#include "zfcp.h"
|
||||
|
||||
/* Array of pointers to known device types. */
|
||||
struct devtype *devtypes[] = {
|
||||
&dasd_devtype,
|
||||
&zfcp_devtype,
|
||||
&qeth_devtype,
|
||||
&ctc_devtype,
|
||||
&lcs_devtype,
|
||||
&generic_ccw_devtype, /* Generic types should come last. */
|
||||
NULL
|
||||
};
|
||||
|
||||
/* Call the init() function of each registered devtype and subtype. */
|
||||
void devtypes_init(void)
|
||||
{
|
||||
struct devtype *dt;
|
||||
struct subtype *st;
|
||||
int i, j;
|
||||
|
||||
for (i = 0; (dt = devtypes[i]); i++) {
|
||||
if (dt->init)
|
||||
dt->init(dt);
|
||||
for (j = 0; (st = dt->subtypes[j]); j++)
|
||||
subtype_init(st);
|
||||
}
|
||||
}
|
||||
|
||||
/* Call the exit() function of each registered devtype and subtype. */
|
||||
void devtypes_exit(void)
|
||||
{
|
||||
struct devtype *dt;
|
||||
struct subtype *st;
|
||||
int i, j;
|
||||
|
||||
for (i = 0; (dt = devtypes[i]); i++) {
|
||||
if (dt->exit)
|
||||
dt->exit(dt);
|
||||
for (j = 0; (st = dt->subtypes[j]); j++)
|
||||
subtype_exit(st);
|
||||
}
|
||||
}
|
||||
|
||||
/* Return struct devtype associated with NAME or NULL if type could not
|
||||
* be found. */
|
||||
struct devtype *devtype_find(const char *name)
|
||||
{
|
||||
int i;
|
||||
struct devtype *dt;
|
||||
|
||||
for (i = 0; (dt = devtypes[i]); i++) {
|
||||
if (strcasecmp(dt->name, name) == 0)
|
||||
return dt;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Search for a device type attribute named STR. */
|
||||
struct attrib *devtype_find_type_attrib(struct devtype *devtype,
|
||||
const char *str)
|
||||
{
|
||||
struct attrib *a;
|
||||
int i;
|
||||
|
||||
for (i = 0; (a = devtype->type_attribs[i]); i++) {
|
||||
if (strcmp(str, a->name) == 0)
|
||||
return a;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Search for a device attribute named STR in any subtype of DT. */
|
||||
struct attrib *devtype_find_dev_attrib(struct devtype *dt, const char *str)
|
||||
{
|
||||
struct attrib *a;
|
||||
struct subtype *st;
|
||||
int i, j;
|
||||
|
||||
for (i = 0; (st = dt->subtypes[i]); i++) {
|
||||
for (j = 0; (a = st->dev_attribs[j]); j++) {
|
||||
if (strcmp(str, a->name) == 0)
|
||||
return a;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Apply device type settings string list to settings list. */
|
||||
static exit_code_t apply_setting(struct devtype *dt, config_t config,
|
||||
const char *key, const char *value,
|
||||
struct util_list *processed)
|
||||
{
|
||||
struct attrib *a;
|
||||
|
||||
/* Check for known attribute. */
|
||||
a = devtype_find_type_attrib(dt, key);
|
||||
if (a) {
|
||||
/* Check for acceptable value of known attribute. */
|
||||
if (!force && !attrib_check_value(a, value))
|
||||
goto err_invalid_forceable;
|
||||
/* Check for activeonly. */
|
||||
if (!force && SCOPE_PERSISTENT(config) && a->activeonly)
|
||||
goto err_activeonly_forceable;
|
||||
/* Check for multiple values. */
|
||||
if (!force && !a->multi && strlist_find(processed, key))
|
||||
goto err_multi_forceable;
|
||||
} else {
|
||||
/* Handle unknown attribute. */
|
||||
if (!dt->unknown_type_attribs)
|
||||
goto err_unknown;
|
||||
if (!force)
|
||||
goto err_unknown_forceable;
|
||||
}
|
||||
|
||||
strlist_add(processed, "%s", key);
|
||||
|
||||
/* Apply to active config. */
|
||||
if (SCOPE_ACTIVE(config)) {
|
||||
setting_list_apply_specified(dt->active_settings, a, key,
|
||||
value);
|
||||
}
|
||||
|
||||
/* Apply to persistent config. */
|
||||
if (SCOPE_PERSISTENT(config)) {
|
||||
setting_list_apply_specified(dt->persistent_settings, a, key,
|
||||
value);
|
||||
}
|
||||
|
||||
return EXIT_OK;
|
||||
|
||||
err_invalid_forceable:
|
||||
delayed_forceable("Invalid value for attribute '%s': %s\n", key, value);
|
||||
return EXIT_INVALID_SETTING;
|
||||
|
||||
err_multi_forceable:
|
||||
delayed_forceable("Cannot specify multiple values for attribute '%s'\n",
|
||||
key);
|
||||
return EXIT_INVALID_SETTING;
|
||||
|
||||
err_unknown:
|
||||
delayed_err("Unknown device type attribute specified: %s\n", key);
|
||||
return EXIT_ATTRIB_NOT_FOUND;
|
||||
|
||||
err_unknown_forceable:
|
||||
delayed_forceable("Unknown device type attribute specified: %s\n", key);
|
||||
return EXIT_ATTRIB_NOT_FOUND;
|
||||
|
||||
err_activeonly_forceable:
|
||||
delayed_forceable("Device type attribute should only be changed in the "
|
||||
"active configuration: %s\n", a->name);
|
||||
return EXIT_INVALID_SETTING;
|
||||
}
|
||||
|
||||
/* Apply device type settings from strlist to devtype. */
|
||||
exit_code_t devtype_apply_strlist(struct devtype *dt, config_t config,
|
||||
struct util_list *settings)
|
||||
{
|
||||
struct util_list *processed;
|
||||
struct strlist_node *s;
|
||||
exit_code_t rc;
|
||||
char *key, *value;
|
||||
|
||||
/* Apply settings. */
|
||||
processed = strlist_new();
|
||||
rc = EXIT_OK;
|
||||
util_list_iterate(settings, s) {
|
||||
key = misc_strdup(s->str);
|
||||
value = strchr(key, '=');
|
||||
*value = 0;
|
||||
value++;
|
||||
|
||||
rc = apply_setting(dt, config, key, value, processed);
|
||||
free(key);
|
||||
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
|
||||
strlist_free(processed);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Apply device type settings from setting_list to devtype. */
|
||||
exit_code_t devtype_apply_settings(struct devtype *dt, config_t config,
|
||||
struct util_list *settings)
|
||||
{
|
||||
struct util_list *processed;
|
||||
struct setting *s;
|
||||
exit_code_t rc;
|
||||
|
||||
/* Apply settings. */
|
||||
processed = strlist_new();
|
||||
rc = EXIT_OK;
|
||||
util_list_iterate(settings, s) {
|
||||
rc = apply_setting(dt, config, s->name, s->value, processed);
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
|
||||
strlist_free(processed);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Check if a device ID is valid for a subtype of the specified devtype. */
|
||||
bool devtype_is_id_valid(struct devtype *dt, const char *id)
|
||||
{
|
||||
int i;
|
||||
struct subtype *st;
|
||||
|
||||
for (i = 0; (st = dt->subtypes[i]); i++) {
|
||||
if (ns_is_id_valid(st->namespace, id))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check if a device ID range is valid for a subtype of the specified
|
||||
* devtype. */
|
||||
bool devtype_is_id_range_valid(struct devtype *dt, const char *id)
|
||||
{
|
||||
int i;
|
||||
struct subtype *st;
|
||||
|
||||
for (i = 0; (st = dt->subtypes[i]); i++) {
|
||||
if (st->namespace->is_id_range_valid(id, err_ignore) == EXIT_OK)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check if a device type configuration needs to be written. */
|
||||
bool devtype_needs_writing(struct devtype *dt, config_t config)
|
||||
{
|
||||
if (SCOPE_ACTIVE(config) && dt->active_settings &&
|
||||
setting_list_modified(dt->active_settings))
|
||||
return true;
|
||||
if (SCOPE_PERSISTENT(config) && dt->persistent_settings &&
|
||||
setting_list_modified(dt->persistent_settings))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void devtype_print(struct devtype *dt, int indent)
|
||||
{
|
||||
int i;
|
||||
struct subtype *st;
|
||||
|
||||
printf("%*sdevtype at %p\n", indent, "", (void *) dt);
|
||||
if (!dt)
|
||||
return;
|
||||
indent += 2;
|
||||
printf("%*sname=%s proc=%d\n", indent, "", dt->name, dt->processed);
|
||||
printf("%*sactive_settings exists=%d:\n", indent, "",
|
||||
dt->active_exists);
|
||||
setting_list_print(dt->active_settings, indent + 2);
|
||||
printf("%*spersistent_settings exists=%d:\n", indent, "",
|
||||
dt->persistent_exists);
|
||||
setting_list_print(dt->persistent_settings, indent + 2);
|
||||
printf("%*ssubtypes:\n", indent, "");
|
||||
for (i = 0; (st = dt->subtypes[i]); i++)
|
||||
subtype_print(st, indent + 2);
|
||||
}
|
||||
|
||||
/* Add name of all modules required by devtype @dt to strlist @modules.
|
||||
* If @subtypes is set, also add modules required by all subtypes. */
|
||||
void devtype_add_modules(struct util_list *modules, struct devtype *dt,
|
||||
int subtypes)
|
||||
{
|
||||
const char **mod = dt->modules;
|
||||
int i;
|
||||
struct subtype *st;
|
||||
|
||||
if (!mod)
|
||||
goto out;
|
||||
|
||||
for (i = 0; mod[i]; i++)
|
||||
strlist_add_unique(modules, "%s", mod[i]);
|
||||
|
||||
out:
|
||||
if (subtypes) {
|
||||
for (i = 0; (st = dt->subtypes[i]); i++)
|
||||
subtype_add_static_modules(modules, st);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if any of the kernel modules used by devtype @dt is loaded. */
|
||||
bool devtype_is_module_loaded(struct devtype *dt)
|
||||
{
|
||||
struct util_list *modules;
|
||||
struct strlist_node *s;
|
||||
bool result = false;
|
||||
|
||||
modules = strlist_new();
|
||||
devtype_add_modules(modules, dt, 1);
|
||||
util_list_iterate(modules, s) {
|
||||
if (module_loaded(s->str)) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
strlist_free(modules);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Return the number of different namespaces found in subtypes of @dt. */
|
||||
int devtype_count_namespaces(struct devtype *dt)
|
||||
{
|
||||
int found[NUM_NAMESPACES];
|
||||
int i, j, num;
|
||||
struct subtype *st;
|
||||
|
||||
/* Reset array. */
|
||||
for (i = 0; i < NUM_NAMESPACES; i++)
|
||||
found[i] = 0;
|
||||
|
||||
/* Set array entries for namespaces found. */
|
||||
for (i = 0; (st = dt->subtypes[i]); i++) {
|
||||
j = namespaces_index(st->namespace);
|
||||
if (j < 0)
|
||||
continue;
|
||||
found[j] = 1;
|
||||
}
|
||||
|
||||
/* Count namespaces. */
|
||||
num = 0;
|
||||
for (i = 0; i < NUM_NAMESPACES; i++) {
|
||||
if (found[i])
|
||||
num++;
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
/* Return the number of subtypes defined for devtype @dt. */
|
||||
int devtype_count_subtypes(struct devtype *dt)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; dt->subtypes[i]; i++) ;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/* Try to find namespace in which @ID is could be an ID. Return %NULL if no
|
||||
* namespace or more than one namespace was found. */
|
||||
struct namespace *devtype_most_similar_namespace(struct devtype *only_dt,
|
||||
struct subtype *only_st,
|
||||
const char *id)
|
||||
{
|
||||
struct namespace *match = NULL;
|
||||
struct devtype *dt;
|
||||
struct subtype *st;
|
||||
int i, j;
|
||||
|
||||
for (i = 0; (dt = devtypes[i]); i++) {
|
||||
if (only_dt && dt != only_dt)
|
||||
continue;
|
||||
for (j = 0; (st = dt->subtypes[j]); j++) {
|
||||
if (only_st && st != only_st)
|
||||
continue;
|
||||
|
||||
if (ns_is_id_valid(st->namespace, id))
|
||||
goto found;
|
||||
if (!st->namespace->is_id_similar ||
|
||||
!st->namespace->is_id_similar(id))
|
||||
continue;
|
||||
found:
|
||||
if (match && match != st->namespace) {
|
||||
/* Multiple matches. */
|
||||
return NULL;
|
||||
}
|
||||
match = st->namespace;
|
||||
}
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
89
zdev/src/exit_code.c
Normal file
89
zdev/src/exit_code.c
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 "exit_code.h"
|
||||
|
||||
/* Textual representation of program exit codes. */
|
||||
const char *exit_code_to_str(exit_code_t rc)
|
||||
{
|
||||
switch (rc) {
|
||||
case EXIT_OK:
|
||||
return "Program finished successfully";
|
||||
case EXIT_USAGE_ERROR:
|
||||
return "Usage error";
|
||||
case EXIT_UNKNOWN_DEVTYPE:
|
||||
return "Unknown device type specified";
|
||||
case EXIT_DEVICE_NOT_FOUND:
|
||||
return "Device not found";
|
||||
case EXIT_ATTRIB_NOT_FOUND:
|
||||
return "Unknown attribute specified";
|
||||
case EXIT_INVALID_DEVTYPE:
|
||||
return "Invalid device type specified";
|
||||
case EXIT_INVALID_SETTING:
|
||||
return "Invalid attribute value specified";
|
||||
case EXIT_SETTING_NOT_FOUND:
|
||||
return "Setting not found";
|
||||
case EXIT_EMPTY_SELECTION:
|
||||
return "Empty selection";
|
||||
case EXIT_INVALID_CONFIG:
|
||||
return "Invalid configuration";
|
||||
case EXIT_INVALID_ID:
|
||||
return "Invalid device ID specified";
|
||||
case EXIT_INCOMPLETE_ID:
|
||||
return "Incomplete device ID specified";
|
||||
case EXIT_NO_DATA:
|
||||
return "Configuration data not found";
|
||||
case EXIT_UNKNOWN_COLUMN:
|
||||
return "Unknown column specified";
|
||||
case EXIT_INCOMPLETE_TYPE:
|
||||
return "None or incomplete type specified";
|
||||
case EXIT_RUNTIME_ERROR:
|
||||
return "A run-time error occurred";
|
||||
case EXIT_ABORTED:
|
||||
return "Operation aborted on user request";
|
||||
case EXIT_SETTING_FAILED:
|
||||
return "Error while applying setting";
|
||||
case EXIT_FORMAT_ERROR:
|
||||
return "File format error";
|
||||
case EXIT_MOD_BUSY:
|
||||
return "Kernel module is in use";
|
||||
case EXIT_MOD_UNLOAD_FAILED:
|
||||
return "Kernel module could not be unloaded";
|
||||
case EXIT_MOD_LOAD_FAILED:
|
||||
return "Kernel module could not be loaded";
|
||||
case EXIT_OUT_OF_MEMORY:
|
||||
return "Not enough available memory";
|
||||
case EXIT_ZFCP_FCP_NOT_FOUND:
|
||||
return "FCP device not found";
|
||||
case EXIT_ZFCP_INVALID_WWPN:
|
||||
return "Invalid WWPN specified";
|
||||
case EXIT_ZFCP_WWPN_NOT_FOUND:
|
||||
return "WWPN not found";
|
||||
case EXIT_ZFCP_INVALID_LUN:
|
||||
return "Invalid LUN specified";
|
||||
case EXIT_ZFCP_SCSI_NOT_FOUND:
|
||||
return "SCSI device not found";
|
||||
case EXIT_GROUP_NOT_FOUND:
|
||||
return "CCW group device: CCW device not found";
|
||||
case EXIT_GROUP_INVALID:
|
||||
return "CCW group device: CCW devices are not a valid group";
|
||||
case EXIT_GROUP_ALREADY:
|
||||
return "CCW group device: CCW device already grouped";
|
||||
case EXIT_GROUP_FAILED:
|
||||
return "CCW group device: Grouping failed";
|
||||
case EXIT_UNGROUP_FAILED:
|
||||
return "CCW group device: Ungrouping failed";
|
||||
case EXIT_INTERNAL_ERROR:
|
||||
return "An internal error occurred";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return "An unknown error occurred";
|
||||
}
|
||||
574
zdev/src/export.c
Normal file
574
zdev/src/export.c
Normal file
@@ -0,0 +1,574 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <ctype.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#include "attrib.h"
|
||||
#include "device.h"
|
||||
#include "devtype.h"
|
||||
#include "export.h"
|
||||
#include "setting.h"
|
||||
#include "subtype.h"
|
||||
|
||||
struct export_header {
|
||||
export_t type;
|
||||
config_t config;
|
||||
char *type_id;
|
||||
char *id;
|
||||
};
|
||||
|
||||
static struct export_header *header_new(const char *config_str,
|
||||
const char *type, const char *id)
|
||||
{
|
||||
config_t config;
|
||||
struct export_header *hdr;
|
||||
|
||||
if (!str_to_config(config_str, &config))
|
||||
return NULL;
|
||||
hdr = misc_malloc(sizeof(struct export_header));
|
||||
if (!id)
|
||||
hdr->type = export_devtype;
|
||||
else
|
||||
hdr->type = export_device;
|
||||
hdr->config = config;
|
||||
hdr->type_id = misc_strdup(type);
|
||||
if (id)
|
||||
hdr->id = misc_strdup(id);
|
||||
|
||||
return hdr;
|
||||
}
|
||||
|
||||
static void header_free(struct export_header *hdr)
|
||||
{
|
||||
free(hdr->type_id);
|
||||
free(hdr->id);
|
||||
free(hdr);
|
||||
}
|
||||
|
||||
static void write_key_val(FILE *fd, const char *name, const char *value)
|
||||
{
|
||||
char *str;
|
||||
|
||||
str = quote_str(value, 0);
|
||||
fprintf(fd, "%s=%s\n", name, str);
|
||||
free(str);
|
||||
}
|
||||
|
||||
static bool is_exportable(struct setting *s, config_t config)
|
||||
{
|
||||
struct attrib *a = s->attrib;
|
||||
|
||||
if (!a)
|
||||
return true;
|
||||
if (a->mandatory)
|
||||
return true;
|
||||
if (SCOPE_ACTIVE(config)) {
|
||||
if (a->unstable && !a->map) {
|
||||
/* Skip values that cannot be determined. */
|
||||
return false;
|
||||
}
|
||||
if (!attrib_match_default(s->attrib, s->value)) {
|
||||
/* All non-default values should be exported. */
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (SCOPE_PERSISTENT(config)) {
|
||||
if (setting_is_set(s))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void write_settings(FILE *fd, struct setting_list *settings,
|
||||
config_t config)
|
||||
{
|
||||
struct setting *s;
|
||||
struct strlist_node *v;
|
||||
|
||||
if (!settings)
|
||||
return;
|
||||
util_list_iterate(&settings->list, s) {
|
||||
if (!is_exportable(s, config))
|
||||
continue;
|
||||
if (s->values) {
|
||||
/* Multiple values. */
|
||||
util_list_iterate(s->values, v)
|
||||
write_key_val(fd, s->name, v->str);
|
||||
} else {
|
||||
/* Single value. */
|
||||
write_key_val(fd, s->name, s->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Write an initial comment. */
|
||||
static void export_write_comment(FILE *fd)
|
||||
{
|
||||
struct utsname uts;
|
||||
|
||||
if (uname(&uts) == 0)
|
||||
fprintf(fd, "# Generated by chzdev on %s\n", uts.nodename);
|
||||
else
|
||||
fprintf(fd, "# Generated by chzdev\n");
|
||||
}
|
||||
|
||||
/* Write an export file header. */
|
||||
static void write_header(FILE *fd, config_t config, const char *type,
|
||||
const char *id, int *first_ptr)
|
||||
{
|
||||
if (first_ptr) {
|
||||
if (*first_ptr) {
|
||||
export_write_comment(fd);
|
||||
*first_ptr = 0;
|
||||
} else
|
||||
fprintf(fd, "\n");
|
||||
}
|
||||
|
||||
if (id)
|
||||
fprintf(fd, "[%s %s %s]\n", config_to_str(config), type, id);
|
||||
else
|
||||
fprintf(fd, "[%s %s]\n", config_to_str(config), type);
|
||||
}
|
||||
|
||||
static int count_exportable(struct device *dev, config_t config)
|
||||
{
|
||||
struct setting_list *list;
|
||||
struct setting *s;
|
||||
int count;
|
||||
|
||||
if (config == config_active)
|
||||
list = dev->active.settings;
|
||||
else
|
||||
list = dev->persistent.settings;
|
||||
count = 0;
|
||||
|
||||
util_list_iterate(&list->list, s) {
|
||||
if (is_exportable(s, config))
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Write settings for device @dev in the specified @config to @fd. */
|
||||
exit_code_t export_write_device(FILE *fd, struct device *dev, config_t config,
|
||||
int *first_ptr)
|
||||
{
|
||||
exit_code_t rc;
|
||||
struct setting_list *settings;
|
||||
|
||||
if (config == config_all) {
|
||||
rc = export_write_device(fd, dev, config_active, first_ptr);
|
||||
if (rc)
|
||||
return rc;
|
||||
return export_write_device(fd, dev, config_persistent,
|
||||
first_ptr);
|
||||
}
|
||||
|
||||
if (config == config_active) {
|
||||
if (!dev->active.exists)
|
||||
return EXIT_OK;
|
||||
/* No need to export device with no settings. */
|
||||
if (count_exportable(dev, config_active) == 0 &&
|
||||
!dev->subtype->support_definable)
|
||||
return EXIT_OK;
|
||||
settings = dev->active.settings;
|
||||
} else {
|
||||
if (!dev->persistent.exists)
|
||||
return EXIT_OK;
|
||||
settings = dev->persistent.settings;
|
||||
}
|
||||
|
||||
write_header(fd, config, dev->subtype->name, dev->id, first_ptr);
|
||||
write_settings(fd, settings, config);
|
||||
|
||||
return ferror(fd) ? EXIT_RUNTIME_ERROR : EXIT_OK;
|
||||
}
|
||||
|
||||
/* Write settings for device type @dt in the specified @config to @fd. */
|
||||
exit_code_t export_write_devtype(FILE *fd, struct devtype *dt, config_t config,
|
||||
int *first_ptr)
|
||||
{
|
||||
exit_code_t rc;
|
||||
struct setting_list *settings;
|
||||
|
||||
if (config == config_all) {
|
||||
rc = export_write_devtype(fd, dt, config_active, first_ptr);
|
||||
if (rc)
|
||||
return rc;
|
||||
return export_write_devtype(fd, dt, config_persistent,
|
||||
first_ptr);
|
||||
}
|
||||
|
||||
if (config == config_active)
|
||||
settings = dt->active_settings;
|
||||
else
|
||||
settings = dt->persistent_settings;
|
||||
|
||||
if (!settings || setting_list_count_set(settings) == 0)
|
||||
return EXIT_OK;
|
||||
|
||||
write_header(fd, config, dt->name, NULL, first_ptr);
|
||||
write_settings(fd, settings, config);
|
||||
|
||||
return ferror(fd) ? EXIT_RUNTIME_ERROR : EXIT_OK;
|
||||
}
|
||||
|
||||
static bool parse_header(const char *line, struct export_header **header_ptr)
|
||||
{
|
||||
char *copy;
|
||||
size_t end;
|
||||
int argc;
|
||||
char **argv;
|
||||
struct export_header *header;
|
||||
|
||||
if (*line != '[')
|
||||
return false;
|
||||
end = strlen(line) - 1;
|
||||
if (line[end] != ']')
|
||||
return false;
|
||||
|
||||
copy = misc_strdup(line);
|
||||
copy[end] = 0;
|
||||
line_split(©[1], &argc, &argv);
|
||||
free(copy);
|
||||
|
||||
if (argc == 2)
|
||||
header = header_new(argv[0], argv[1], NULL);
|
||||
else if (argc == 3)
|
||||
header = header_new(argv[0], argv[1], argv[2]);
|
||||
else
|
||||
header = NULL;
|
||||
|
||||
line_free(argc, argv);
|
||||
|
||||
if (!header)
|
||||
return false;
|
||||
|
||||
*header_ptr = header;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_setting(const char *line, char **key_ptr, char **val_ptr)
|
||||
{
|
||||
char *copy, *value;
|
||||
|
||||
copy = misc_strdup(line);
|
||||
value = strchr(copy, '=');
|
||||
if (!value) {
|
||||
free(copy);
|
||||
return false;
|
||||
}
|
||||
|
||||
*value = 0;
|
||||
value++;
|
||||
*key_ptr = shrink_str(copy);
|
||||
*val_ptr = unquote_str(value);
|
||||
free(copy);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct setting_list *dev_get_setting_list(struct device *dev,
|
||||
config_t config)
|
||||
{
|
||||
struct setting_list *settings = NULL;
|
||||
|
||||
if (config == config_active)
|
||||
settings = dev->active.settings;
|
||||
else
|
||||
settings = dev->persistent.settings;
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
static struct setting_list *dt_get_setting_list(struct devtype *dt,
|
||||
config_t config)
|
||||
{
|
||||
struct setting_list *settings = NULL;
|
||||
|
||||
if (config == config_active)
|
||||
settings = dt->active_settings;
|
||||
else
|
||||
settings = dt->persistent_settings;
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
/* Display a warning related to a line in an export data file. */
|
||||
static void fwarn(const char *filename, int lineno, const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, format);
|
||||
fprintf(stderr, "%s:%d: ", filename, lineno);
|
||||
vfprintf(stderr, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static exit_code_t handle_header(const char *filename, int lineno,
|
||||
struct export_header *hdr,
|
||||
struct devtype **dt_ptr,
|
||||
struct device **dev_ptr)
|
||||
{
|
||||
struct devtype *dt = NULL;
|
||||
struct device *dev = NULL;
|
||||
struct subtype *st;
|
||||
exit_code_t rc = EXIT_OK;
|
||||
|
||||
if (hdr->type == export_devtype) {
|
||||
/* Section header indicates device type settings. */
|
||||
dt = devtype_find(hdr->type_id);
|
||||
if (!dt) {
|
||||
fwarn(filename, lineno, "Unknown device type '%s' in "
|
||||
"section header\n", hdr->type_id);
|
||||
rc = EXIT_FORMAT_ERROR;
|
||||
goto out;
|
||||
}
|
||||
/* Prepare device type for new settings. */
|
||||
if (SCOPE_ACTIVE(hdr->config)) {
|
||||
setting_list_free(dt->active_settings);
|
||||
dt->active_settings = setting_list_new();
|
||||
}
|
||||
if (SCOPE_PERSISTENT(hdr->config)) {
|
||||
setting_list_free(dt->persistent_settings);
|
||||
dt->persistent_settings = setting_list_new();
|
||||
}
|
||||
} else if (hdr->type == export_device) {
|
||||
/* Section header indicates device settings. */
|
||||
st = subtype_find(hdr->type_id);
|
||||
if (!st) {
|
||||
if (devtype_find(hdr->type_id)) {
|
||||
fwarn(filename, lineno, "Ambiguous device "
|
||||
"type '%s' in section header\n",
|
||||
hdr->type_id);
|
||||
} else {
|
||||
fwarn(filename, lineno, "Unknown device type "
|
||||
"'%s' in section header\n", hdr->type_id);
|
||||
}
|
||||
rc = EXIT_FORMAT_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!st->devices)
|
||||
st->devices = device_list_new(st);
|
||||
|
||||
dev = device_list_find(st->devices, hdr->id, NULL);
|
||||
if (!dev) {
|
||||
/* Register a new device. */
|
||||
dev = device_new(st, hdr->id);
|
||||
if (!dev) {
|
||||
fwarn(filename, lineno, "Unknown device ID "
|
||||
"format '%s' in section header\n",
|
||||
hdr->id);
|
||||
rc = EXIT_FORMAT_ERROR;
|
||||
goto out;
|
||||
}
|
||||
device_list_add(st->devices, dev);
|
||||
}
|
||||
|
||||
/* Prepare device for new settings. */
|
||||
if (SCOPE_ACTIVE(hdr->config)) {
|
||||
setting_list_clear(dev->active.settings);
|
||||
if (dev->subtype->support_definable)
|
||||
dev->active.definable = 1;
|
||||
else
|
||||
dev->active.exists = 1;
|
||||
}
|
||||
if (SCOPE_PERSISTENT(hdr->config)) {
|
||||
setting_list_clear(dev->persistent.settings);
|
||||
dev->persistent.exists = 1;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
*dt_ptr = dt;
|
||||
*dev_ptr = dev;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static exit_code_t handle_setting(const char *filename, int lineno,
|
||||
const char *key, const char *value,
|
||||
struct devtype *dt, struct device *dev,
|
||||
config_t config)
|
||||
{
|
||||
exit_code_t rc;
|
||||
struct attrib **attribs, *a;
|
||||
struct setting_list *list;
|
||||
|
||||
if (config == config_all) {
|
||||
rc = handle_setting(filename, lineno, key, value, dt, dev,
|
||||
config_active);
|
||||
if (rc)
|
||||
return rc;
|
||||
return handle_setting(filename, lineno, key, value, dt, dev,
|
||||
config_persistent);
|
||||
}
|
||||
|
||||
if (dt) {
|
||||
/* We're inside a device type section. */
|
||||
attribs = dt->type_attribs;
|
||||
list = dt_get_setting_list(dt, config);
|
||||
} else if (dev) {
|
||||
/* We're inside a device section. */
|
||||
attribs = dev->subtype->dev_attribs;
|
||||
list = dev_get_setting_list(dev, config);
|
||||
} else
|
||||
return EXIT_OK;
|
||||
|
||||
a = attrib_find(attribs, key);
|
||||
if (a)
|
||||
setting_list_apply(list, a, a->name, value);
|
||||
else if (dt) {
|
||||
fwarn(filename, lineno, "Skipping unknown device type "
|
||||
"setting %s=%s\n", key, value);
|
||||
} else {
|
||||
fwarn(filename, lineno, "Skipping unknown device setting "
|
||||
"%s=%s\n", key, value);
|
||||
}
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
static struct export_object *object_new(export_t type, void *ptr)
|
||||
{
|
||||
struct export_object *obj;
|
||||
|
||||
obj = misc_malloc(sizeof(struct export_object));
|
||||
obj->type = type;
|
||||
if (type == export_devtype)
|
||||
obj->ptr.dt = ptr;
|
||||
else
|
||||
obj->ptr.dev = ptr;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
static bool empty_device(struct device *dev)
|
||||
{
|
||||
/* Empty device settings are ok for subtypes supporting definable
|
||||
* devices because there the device ID is information enough. */
|
||||
if (dev->subtype->support_definable)
|
||||
return false;
|
||||
if (util_list_is_empty(&dev->active.settings->list) &&
|
||||
util_list_is_empty(&dev->persistent.settings->list))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool empty_devtype(struct devtype *dt)
|
||||
{
|
||||
if ((!dt->active_settings ||
|
||||
util_list_is_empty(&dt->active_settings->list)) &&
|
||||
(!dt->persistent_settings ||
|
||||
util_list_is_empty(&dt->persistent_settings->list)))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool check_section(const char *filename, int lineno,
|
||||
struct devtype *dt, struct device *dev)
|
||||
{
|
||||
|
||||
if (dt && empty_devtype(dt)) {
|
||||
fwarn(filename, lineno, "Empty device type section\n");
|
||||
return false;
|
||||
}
|
||||
if (dev && empty_device(dev)) {
|
||||
fwarn(filename, lineno, "Empty device section\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Read configuration objects from @fd. Add pointer to newly allocated
|
||||
* struct export_objects to ptrlist @objects. */
|
||||
exit_code_t export_read(FILE *fd, const char *filename,
|
||||
struct util_list *objects)
|
||||
{
|
||||
char *line, *l, *key, *value;
|
||||
struct export_header *hdr;
|
||||
size_t end;
|
||||
exit_code_t rc = EXIT_OK;
|
||||
struct devtype *dt;
|
||||
int lineno;
|
||||
config_t config = config_all;
|
||||
struct device *dev;
|
||||
|
||||
lineno = 0;
|
||||
line = NULL;
|
||||
dt = NULL;
|
||||
dev = NULL;
|
||||
while (rc == EXIT_OK && getline(&line, &end, fd) > 0) {
|
||||
lineno++;
|
||||
|
||||
l = shrink_str(line);
|
||||
if (!*l || *l == '#') {
|
||||
free(l);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parse_header(l, &hdr)) {
|
||||
if (!check_section(filename, lineno, dt, dev)) {
|
||||
header_free(hdr);
|
||||
rc = EXIT_FORMAT_ERROR;
|
||||
goto err;
|
||||
}
|
||||
/* Found [<config> <subtype> <devid>] or
|
||||
* [<config> <devtype>]. */
|
||||
rc = handle_header(filename, lineno, hdr, &dt, &dev);
|
||||
config = hdr->config;
|
||||
header_free(hdr);
|
||||
if (dt) {
|
||||
ptrlist_add(objects, object_new(export_devtype,
|
||||
dt));
|
||||
} else if (dev) {
|
||||
ptrlist_add(objects, object_new(export_device,
|
||||
dev));
|
||||
}
|
||||
} else if (parse_setting(l, &key, &value)) {
|
||||
/* Found <key>=<value>. */
|
||||
if (!dt && !dev) {
|
||||
fwarn(filename, lineno, "Setting outside of "
|
||||
"valid section\n");
|
||||
rc = EXIT_FORMAT_ERROR;
|
||||
} else {
|
||||
rc = handle_setting(filename, lineno, key,
|
||||
value, dt, dev, config);
|
||||
}
|
||||
free(key);
|
||||
free(value);
|
||||
} else {
|
||||
warn("%s:%d: Unrecognized line: %s\n", filename,
|
||||
lineno, l);
|
||||
rc = EXIT_FORMAT_ERROR;
|
||||
}
|
||||
err:
|
||||
free(l);
|
||||
}
|
||||
free(line);
|
||||
|
||||
if (rc == EXIT_OK) {
|
||||
if (!check_section(filename, lineno, dt, dev))
|
||||
rc = EXIT_FORMAT_ERROR;
|
||||
}
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return ferror(fd) ? EXIT_RUNTIME_ERROR : EXIT_OK;
|
||||
}
|
||||
76
zdev/src/findmnt.c
Normal file
76
zdev/src/findmnt.c
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "devnode.h"
|
||||
#include "findmnt.h"
|
||||
#include "misc.h"
|
||||
|
||||
#define FINDMNT_CMDLINE "findmnt -n -T %s -o SOURCE 2>/dev/null"
|
||||
|
||||
static struct devnode *devnode_from_line(const char *line)
|
||||
{
|
||||
char *copy, *end;
|
||||
struct devnode *devnode;
|
||||
|
||||
copy = misc_strdup(line);
|
||||
|
||||
/* Could be /dev/sda[/subvolname] */
|
||||
end = strchr(copy, '[');
|
||||
if (end)
|
||||
*end = 0;
|
||||
|
||||
devnode = devnode_from_node(copy, err_ignore);
|
||||
|
||||
free(copy);
|
||||
|
||||
return devnode;
|
||||
}
|
||||
|
||||
/* Return a newly allocated ptrlist containing all devnodes that provide
|
||||
* the file system on which @path is located. Return %NULL if no devnodes
|
||||
* were found. We need this in addition to lsblk output because lsblk doesn't
|
||||
* work with btrfs subvolumes. */
|
||||
struct util_list *findmnt_get_devnodes_by_path(const char *path)
|
||||
{
|
||||
struct util_list *devnodes;
|
||||
char *quoted_path, *cmd, *output, *next, *curr;
|
||||
struct devnode *devnode;
|
||||
|
||||
devnodes = ptrlist_new();
|
||||
quoted_path = quote_str(path, 1);
|
||||
cmd = misc_asprintf(FINDMNT_CMDLINE, quoted_path);
|
||||
|
||||
output = misc_read_cmd_output(cmd, 0, err_ignore);
|
||||
if (!output)
|
||||
goto out;
|
||||
|
||||
/* Iterate over each line. */
|
||||
next = output;
|
||||
while ((curr = strsep(&next, "\n"))) {
|
||||
devnode = devnode_from_line(curr);
|
||||
if (devnode)
|
||||
ptrlist_add(devnodes, devnode);
|
||||
}
|
||||
|
||||
out:
|
||||
free(output);
|
||||
free(cmd);
|
||||
free(quoted_path);
|
||||
|
||||
/* Make empty list detection easier for calling function. */
|
||||
if (util_list_is_empty(devnodes)) {
|
||||
ptrlist_free(devnodes, 1);
|
||||
devnodes = NULL;
|
||||
}
|
||||
|
||||
return devnodes;
|
||||
}
|
||||
238
zdev/src/generic_ccw.c
Normal file
238
zdev/src/generic_ccw.c
Normal file
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <string.h>
|
||||
|
||||
#include "attrib.h"
|
||||
#include "ccw.h"
|
||||
#include "ccwgroup.h"
|
||||
#include "devnode.h"
|
||||
#include "devtype.h"
|
||||
#include "generic_ccw.h"
|
||||
#include "namespace.h"
|
||||
#include "path.h"
|
||||
#include "subtype.h"
|
||||
|
||||
/*
|
||||
* Generic CCW device sub-type.
|
||||
*/
|
||||
|
||||
static struct ccw_subtype_data generic_ccw_data = {
|
||||
.ccwdrv = NULL,
|
||||
.mod = NULL,
|
||||
};
|
||||
|
||||
/* Check if there is a non-generic subtype in the CCW namespace that uses the
|
||||
* specified CCW device driver. */
|
||||
static bool match_non_generic(const char *drv)
|
||||
{
|
||||
int i, j;
|
||||
struct devtype *dt;
|
||||
struct subtype *st;
|
||||
struct ccw_subtype_data *ccwdata;
|
||||
struct ccwgroup_subtype_data *ccwgroupdata;
|
||||
const char *ccwdrv;
|
||||
|
||||
for (i = 0; (dt = devtypes[i]); i++) {
|
||||
for (j = 0; (st = dt->subtypes[j]); j++) {
|
||||
if (st->generic || !st->data)
|
||||
continue;
|
||||
if (st->namespace == &ccw_namespace) {
|
||||
ccwdata = st->data;
|
||||
ccwdrv = ccwdata->ccwdrv;
|
||||
} else if (st->namespace->cmp_parsed_ids ==
|
||||
&ccwgroup_cmp_parsed_ids) {
|
||||
ccwgroupdata = st->data;
|
||||
ccwdrv = ccwgroupdata->ccwdrv;
|
||||
} else
|
||||
continue;
|
||||
if (!ccwdrv)
|
||||
continue;
|
||||
if (strcmp(drv, ccwdrv) == 0)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Determine if the specified device exists in the CCW namespace and is not
|
||||
* handled by another subtype. */
|
||||
static bool generic_exists(struct subtype *st, const char *id, int fast)
|
||||
{
|
||||
struct ccw_devid devid;
|
||||
char *drv = NULL;
|
||||
bool result = false;
|
||||
|
||||
if (ccw_parse_devid(&devid, id, err_ignore) != EXIT_OK)
|
||||
goto out;
|
||||
if (!fast && !ccw_exists(NULL, NULL, id))
|
||||
goto out;
|
||||
drv = ccw_get_driver(&devid);
|
||||
if (!drv) {
|
||||
/* Account devices with no driver to generic-ccw to make them
|
||||
* visible. */
|
||||
result = true;
|
||||
goto out;
|
||||
}
|
||||
result = !match_non_generic(drv);
|
||||
|
||||
out:
|
||||
free(drv);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool generic_ccw_st_exists_active(struct subtype *st, const char *id)
|
||||
{
|
||||
return generic_exists(st, id, 0);
|
||||
}
|
||||
|
||||
static bool get_ids_cb(const char *file, void *data)
|
||||
{
|
||||
/* We use fast=1 here to prevent another stat() syscall per device
|
||||
* when we already know that the directory exists. */
|
||||
return generic_exists(data, file, 1);
|
||||
}
|
||||
|
||||
/* Add the IDs of all CCW devices existing in the active configuration which
|
||||
* are not handled by other subtypes. */
|
||||
static void generic_ccw_st_add_active_ids(struct subtype *st,
|
||||
struct util_list *ids)
|
||||
{
|
||||
char *path;
|
||||
|
||||
cio_settle(0);
|
||||
path = path_get_ccw_devices(NULL);
|
||||
misc_read_dir(path, ids, get_ids_cb, st);
|
||||
free(path);
|
||||
}
|
||||
|
||||
struct add_cb_data {
|
||||
struct util_list *devnodes;
|
||||
const char *id;
|
||||
};
|
||||
|
||||
static exit_code_t add_cb(const char *abs_path, const char *rel_path,
|
||||
void *data)
|
||||
{
|
||||
struct add_cb_data *cb_data = data;
|
||||
char *link, *target, *name;
|
||||
static const char *prefix[] = { "vmrdr-", "vmpun-", "vmprt-" };
|
||||
unsigned int i, major, minor;
|
||||
struct devnode *devnode;
|
||||
|
||||
link = misc_readlink(abs_path);
|
||||
if (!link)
|
||||
return EXIT_OK;
|
||||
|
||||
target = basename(link);
|
||||
for (i = 0; i < ARRAY_SIZE(prefix); i++) {
|
||||
name = misc_asprintf("%s%s", prefix[i], cb_data->id);
|
||||
if (strcmp(target, name) != 0)
|
||||
goto next;
|
||||
if (sscanf(rel_path, "%u:%u", &major, &minor) != 2)
|
||||
goto next;
|
||||
devnode = devnode_new(CHARDEV, major, minor, target);
|
||||
ptrlist_add(cb_data->devnodes, devnode);
|
||||
next:
|
||||
free(name);
|
||||
}
|
||||
free(link);
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
/* Add struct devnodes to ptrlist @devnodes for each Linux device that is
|
||||
* provided by the CCW device with the specified ID. Since this is a generic
|
||||
* CCW driver covering multiple device drivers, this is somewhat of trial
|
||||
* and error work. */
|
||||
static void generic_ccw_st_add_devnodes(struct subtype *st, const char *id,
|
||||
struct util_list *devnodes)
|
||||
{
|
||||
struct add_cb_data cb_data;
|
||||
char *path;
|
||||
|
||||
cb_data.devnodes = devnodes;
|
||||
cb_data.id = id;
|
||||
path = path_get_sys_dev_char_devices();
|
||||
if (dir_exists(path))
|
||||
path_for_each(path, add_cb, &cb_data);
|
||||
free(path);
|
||||
}
|
||||
|
||||
static struct subtype generic_ccw_subtype = {
|
||||
.super = &ccw_subtype,
|
||||
.devtype = &generic_ccw_devtype,
|
||||
.name = "generic-ccw",
|
||||
.title = "Generic Channel-Command-Word (CCW) devices",
|
||||
.devname = "Generic CCW device",
|
||||
.modules = NULL,
|
||||
.namespace = &ccw_namespace,
|
||||
.data = &generic_ccw_data,
|
||||
|
||||
.dev_attribs = ATTRIB_ARRAY(
|
||||
&ccw_attr_online,
|
||||
&ccw_attr_cmb_enable,
|
||||
),
|
||||
.unknown_dev_attribs = 1,
|
||||
.generic = 1,
|
||||
|
||||
.exists_active = &generic_ccw_st_exists_active,
|
||||
.add_active_ids = &generic_ccw_st_add_active_ids,
|
||||
.add_devnodes = &generic_ccw_st_add_devnodes,
|
||||
};
|
||||
|
||||
/*
|
||||
* Generic CCW device type methods.
|
||||
*/
|
||||
|
||||
/* Clean up all resources used by devtype object. */
|
||||
static void generic_ccw_devtype_exit(struct devtype *dt)
|
||||
{
|
||||
setting_list_free(dt->active_settings);
|
||||
setting_list_free(dt->persistent_settings);
|
||||
}
|
||||
|
||||
static exit_code_t generic_ccw_devtype_read_settings(struct devtype *dt,
|
||||
config_t config)
|
||||
{
|
||||
dt->active_settings = setting_list_new();
|
||||
dt->persistent_settings = setting_list_new();
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
static exit_code_t generic_ccw_devtype_write_settings(struct devtype *dt,
|
||||
config_t config)
|
||||
{
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generic CCW device type.
|
||||
*/
|
||||
|
||||
struct devtype generic_ccw_devtype = {
|
||||
.name = "generic-ccw",
|
||||
.title = "", /* Only use subtypes. */
|
||||
.devname = "Generic CCW device",
|
||||
.modules = NULL,
|
||||
|
||||
.subtypes = SUBTYPE_ARRAY(
|
||||
&generic_ccw_subtype,
|
||||
),
|
||||
|
||||
.type_attribs = ATTRIB_ARRAY(),
|
||||
|
||||
.exit = &generic_ccw_devtype_exit,
|
||||
|
||||
.read_settings = &generic_ccw_devtype_read_settings,
|
||||
.write_settings = &generic_ccw_devtype_write_settings,
|
||||
};
|
||||
128
zdev/src/hash.c
Normal file
128
zdev/src/hash.c
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <string.h>
|
||||
|
||||
#include "hash.h"
|
||||
#include "misc.h"
|
||||
|
||||
/* Initialize hash. */
|
||||
void _hash_init(struct hash *hash, int buckets, hash_id_fn_t get_id,
|
||||
hash_cmp_fn_t cmp_id, hash_fn_t get_hash, unsigned long offset)
|
||||
{
|
||||
int i;
|
||||
|
||||
memset(hash, 0, sizeof(struct hash));
|
||||
util_list_init_offset(&hash->list, offset);
|
||||
hash->buckets = buckets;
|
||||
hash->get_id = get_id;
|
||||
hash->cmp_id = cmp_id;
|
||||
hash->get_hash = get_hash;
|
||||
hash->hash = misc_malloc(sizeof(struct util_list) * buckets);
|
||||
for (i = 0; i < buckets; i++)
|
||||
hash->hash[i] = ptrlist_new();
|
||||
}
|
||||
|
||||
/* Return a newly allocated hash. */
|
||||
struct hash *_hash_new(int buckets, hash_id_fn_t get_id, hash_cmp_fn_t cmp_id,
|
||||
hash_fn_t get_hash, unsigned long offset)
|
||||
{
|
||||
struct hash *hash;
|
||||
|
||||
hash = misc_malloc(sizeof(struct hash));
|
||||
_hash_init(hash, buckets, get_id, cmp_id, get_hash, offset);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
/* Release all resources associated with @hash, excluding @hash. If @free_fn
|
||||
* is specified, this function is called to release all resources associated
|
||||
* with each entry. */
|
||||
void hash_clear(struct hash *hash, void (*free_fn)(void *))
|
||||
{
|
||||
void *c, *n;
|
||||
int i;
|
||||
|
||||
util_list_iterate_safe(&hash->list, c, n) {
|
||||
util_list_remove(&hash->list, c);
|
||||
if (free_fn)
|
||||
free_fn(c);
|
||||
}
|
||||
|
||||
for (i = 0; i < hash->buckets; i++)
|
||||
ptrlist_free(hash->hash[i], 0);
|
||||
free(hash->hash);
|
||||
}
|
||||
|
||||
/* Release all resources associated with @hash. If @free_fn is specified,
|
||||
* this function is called to release all resources associated with each
|
||||
* entry. */
|
||||
void hash_free(struct hash *hash, void (*free_fn)(void *))
|
||||
{
|
||||
hash_clear(hash, free_fn);
|
||||
free(hash);
|
||||
}
|
||||
|
||||
/* Add a new entry to the hash. */
|
||||
void hash_add(struct hash *hash, void *entry)
|
||||
{
|
||||
int bucket;
|
||||
|
||||
util_list_add_tail(&hash->list, entry);
|
||||
if (hash->buckets > 0) {
|
||||
bucket = hash->get_hash(hash->get_id(entry));
|
||||
ptrlist_add(hash->hash[bucket], entry);
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove an entry from the hash. */
|
||||
void hash_remove(struct hash *hash, void *entry)
|
||||
{
|
||||
int bucket;
|
||||
|
||||
util_list_remove(&hash->list, entry);
|
||||
if (hash->buckets > 0) {
|
||||
bucket = hash->get_hash(hash->get_id(entry));
|
||||
ptrlist_remove(hash->hash[bucket], entry);
|
||||
}
|
||||
}
|
||||
|
||||
void hash_print(struct hash *hash, int ind)
|
||||
{
|
||||
int i;
|
||||
|
||||
indent(ind, "hash at %p\n", hash);
|
||||
ind += 2;
|
||||
indent(ind, "buckets=%d\n", hash->buckets);
|
||||
indent(ind, "get_id=%p\n", hash->get_id);
|
||||
indent(ind, "cmp_id=%p\n", hash->cmp_id);
|
||||
indent(ind, "get_hash=%p\n", hash->get_hash);
|
||||
|
||||
for (i = 0; i < hash->buckets; i++) {
|
||||
indent(ind, "bucket[%d]: len=%d\n", i,
|
||||
util_list_len(hash->hash[i]));
|
||||
}
|
||||
}
|
||||
|
||||
/* Find entry by ID. @cmp_fn compares two IDs and returns 0 when IDs match. */
|
||||
void *hash_find_by_id(struct hash *hash, const void *id)
|
||||
{
|
||||
int bucket;
|
||||
struct ptrlist_node *p;
|
||||
|
||||
if (hash->buckets > 0) {
|
||||
bucket = hash->get_hash(id);
|
||||
util_list_iterate(hash->hash[bucket], p) {
|
||||
if (hash->cmp_id(hash->get_id(p->ptr), id) == 0)
|
||||
return p->ptr;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
385
zdev/src/inuse.c
Normal file
385
zdev/src/inuse.c
Normal file
@@ -0,0 +1,385 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <mntent.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "blkinfo.h"
|
||||
#include "device.h"
|
||||
#include "devnode.h"
|
||||
#include "inuse.h"
|
||||
#include "misc.h"
|
||||
#include "path.h"
|
||||
#include "select.h"
|
||||
#include "subtype.h"
|
||||
|
||||
/* struct resource - A resource that is currently in use
|
||||
* @st: Subtype of device providing resource
|
||||
* @id: ID of device providing resource
|
||||
* @name: Name of the resource, e.g. the mount point. */
|
||||
struct resource {
|
||||
struct subtype *st;
|
||||
char *id;
|
||||
char *name;
|
||||
};
|
||||
|
||||
/* ptrlist of struct resources. */
|
||||
static struct util_list *resources;
|
||||
|
||||
/* Return a newly allocated struct resource. */
|
||||
static struct resource *resource_new(struct subtype *st, const char *id,
|
||||
const char *name)
|
||||
{
|
||||
struct resource *res;
|
||||
|
||||
res = misc_malloc(sizeof(struct resource));
|
||||
res->st = st;
|
||||
res->id = misc_strdup(id);
|
||||
res->name = misc_strdup(name);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Release all resources associated with @res. */
|
||||
static void resource_free(struct resource *res)
|
||||
{
|
||||
if (!res)
|
||||
return;
|
||||
free(res->id);
|
||||
free(res->name);
|
||||
free(res);
|
||||
}
|
||||
|
||||
/* Release all allocated resources. */
|
||||
void inuse_exit(void)
|
||||
{
|
||||
struct ptrlist_node *p, *n;
|
||||
struct resource *res;
|
||||
|
||||
if (!resources)
|
||||
return;
|
||||
util_list_iterate_safe(resources, p, n) {
|
||||
util_list_remove(resources, p);
|
||||
res = p->ptr;
|
||||
resource_free(res);
|
||||
free(p);
|
||||
}
|
||||
free(resources);
|
||||
}
|
||||
|
||||
/* Return newly allocated strlist containing names of mountpoints for all
|
||||
* mounted file systems. */
|
||||
static struct util_list *get_mountpoints(void)
|
||||
{
|
||||
struct util_list *list;
|
||||
char *path;
|
||||
FILE *fd;
|
||||
struct mntent *m;
|
||||
|
||||
list = strlist_new();
|
||||
|
||||
/* Try lsblk output first because it doesn't contain non-blockdevice
|
||||
* file systems. */
|
||||
blkinfo_add_mountpoints(list);
|
||||
|
||||
/* Use /proc/mounts as well to get btrfs subvolume mounts. */
|
||||
path = path_get_proc("mounts");
|
||||
fd = setmntent(path, "r");
|
||||
if (!fd)
|
||||
goto out;
|
||||
|
||||
while ((m = getmntent(fd))) {
|
||||
if (!m->mnt_dir || !m->mnt_fsname || !*m->mnt_dir ||
|
||||
*m->mnt_fsname != '/')
|
||||
continue;
|
||||
strlist_add_unique(list, m->mnt_dir);
|
||||
}
|
||||
|
||||
endmntent(fd);
|
||||
|
||||
out:
|
||||
free(path);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static void add_mounts(struct util_list *list)
|
||||
{
|
||||
struct util_list *mountpoints;
|
||||
struct strlist_node *mp;
|
||||
struct resource *res;
|
||||
struct util_list *selected;
|
||||
struct selected_dev_node *sel;
|
||||
char *r;
|
||||
|
||||
mountpoints = get_mountpoints();
|
||||
|
||||
util_list_iterate(mountpoints, mp) {
|
||||
selected = selected_dev_list_new();
|
||||
|
||||
/* Determine list of devices providing mountpoint. */
|
||||
if (select_by_path(NULL, selected, config_active, scope_mandatory,
|
||||
NULL, NULL, mp->str, err_ignore) != EXIT_OK)
|
||||
goto next;
|
||||
|
||||
/* Process list. */
|
||||
util_list_iterate(selected, sel) {
|
||||
if (sel->rc != EXIT_OK || !sel->st || !sel->id)
|
||||
continue;
|
||||
|
||||
r = misc_asprintf("Mount point %s", mp->str);
|
||||
res = resource_new(sel->st, sel->id, r);
|
||||
free(r);
|
||||
ptrlist_add(list, res);
|
||||
|
||||
/* Expand list to also contain prereq-devices. */
|
||||
subtype_add_prereqs(sel->st, sel->id, selected);
|
||||
}
|
||||
|
||||
next:
|
||||
selected_dev_list_free(selected);
|
||||
}
|
||||
|
||||
strlist_free(mountpoints);
|
||||
}
|
||||
|
||||
/* Return newly allocated ptrlist containing devnodes of active swap devices. */
|
||||
static struct util_list *get_swapdevs(void)
|
||||
{
|
||||
struct util_list *list;
|
||||
char *path, *text, *curr, *next, **argv;
|
||||
struct devnode *devnode;
|
||||
int argc;
|
||||
|
||||
list = ptrlist_new();
|
||||
|
||||
/* Try lsblk output first because it doesn't contain non-blockdevice
|
||||
* file systems. */
|
||||
blkinfo_add_swap_devnodes(list);
|
||||
if (!util_list_is_empty(list))
|
||||
return list;
|
||||
|
||||
/* Fall back to /proc/swaps. */
|
||||
path = path_get_proc("swaps");
|
||||
text = misc_read_text_file(path, 0, err_ignore);
|
||||
free(path);
|
||||
if (!text)
|
||||
goto out;
|
||||
|
||||
next = text;
|
||||
while ((curr = strsep(&next, "\n"))) {
|
||||
line_split(curr, &argc, &argv);
|
||||
if (argc > 0 && *(argv[0]) == '/') {
|
||||
devnode = devnode_from_node(argv[0], err_ignore);
|
||||
if (devnode)
|
||||
ptrlist_add(list, devnode);
|
||||
}
|
||||
line_free(argc, argv);
|
||||
}
|
||||
|
||||
|
||||
out:
|
||||
free(text);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static void add_swap(struct util_list *list)
|
||||
{
|
||||
struct util_list *swapdevs;
|
||||
struct ptrlist_node *swap;
|
||||
struct devnode *devnode;
|
||||
struct resource *res;
|
||||
struct util_list *selected;
|
||||
struct selected_dev_node *sel;
|
||||
char *r;
|
||||
|
||||
swapdevs = get_swapdevs();
|
||||
|
||||
util_list_iterate(swapdevs, swap) {
|
||||
devnode = swap->ptr;
|
||||
selected = selected_dev_list_new();
|
||||
|
||||
/* Determine list of devices providing mountpoint. */
|
||||
if (select_by_devnode(NULL, selected, config_active,
|
||||
scope_mandatory, NULL, NULL, devnode,
|
||||
NULL, err_ignore) != EXIT_OK)
|
||||
goto next;
|
||||
|
||||
/* Process list. */
|
||||
util_list_iterate(selected, sel) {
|
||||
if (sel->rc != EXIT_OK || !sel->st || !sel->id)
|
||||
continue;
|
||||
|
||||
r = misc_asprintf("Swap device %s", devnode->name);
|
||||
res = resource_new(sel->st, sel->id, r);
|
||||
free(r);
|
||||
ptrlist_add(list, res);
|
||||
|
||||
/* Expand list to also contain prereq-devices. */
|
||||
subtype_add_prereqs(sel->st, sel->id, selected);
|
||||
}
|
||||
|
||||
next:
|
||||
selected_dev_list_free(selected);
|
||||
}
|
||||
|
||||
ptrlist_free(swapdevs, 1);
|
||||
}
|
||||
|
||||
|
||||
/* Determine IPv4 and IPv6 addresses of networking interface @name. */
|
||||
static void add_ip_addresses(struct util_list *addrs, const char *name)
|
||||
{
|
||||
char *cmd, *text, *next, *curr, **argv;
|
||||
int argc;
|
||||
|
||||
cmd = misc_asprintf("%s address show %s 2>/dev/null", PATH_IP, name);
|
||||
text = misc_read_cmd_output(cmd, 0, err_ignore);
|
||||
if (!text)
|
||||
goto out;
|
||||
|
||||
next = text;
|
||||
while ((curr = strsep(&next, "\n"))) {
|
||||
line_split(curr, &argc, &argv);
|
||||
if (argc < 2)
|
||||
goto next;
|
||||
if (strcmp(argv[0], "inet") == 0)
|
||||
strlist_add(addrs, "IPv4 address %s", argv[1]);
|
||||
else if (strcmp(argv[0], "inet6") == 0)
|
||||
strlist_add(addrs, "IPv6 address %s", argv[1]);
|
||||
next:
|
||||
line_free(argc, argv);
|
||||
|
||||
}
|
||||
|
||||
out:
|
||||
free(text);
|
||||
free(cmd);
|
||||
}
|
||||
|
||||
/* Add struct resources to @list for each device providing an interface with
|
||||
* IP address. */
|
||||
static void add_network(struct util_list *list)
|
||||
{
|
||||
char *path;
|
||||
struct util_list *interfaces, *addrs, *selected;
|
||||
struct strlist_node *net, *addr;
|
||||
struct selected_dev_node *sel;
|
||||
struct resource *res;
|
||||
|
||||
path = path_get_sys_class("net", NULL);
|
||||
interfaces = strlist_new();
|
||||
|
||||
if (!misc_read_dir(path, interfaces, NULL, NULL))
|
||||
goto out;
|
||||
|
||||
/* Process all known networking interfaces. */
|
||||
util_list_iterate(interfaces, net) {
|
||||
/* Skip loopback. */
|
||||
if (strcmp(net->str, "lo") == 0)
|
||||
continue;
|
||||
|
||||
selected = NULL;
|
||||
|
||||
/* Determine IP addresses. */
|
||||
addrs = strlist_new();
|
||||
add_ip_addresses(addrs, net->str);
|
||||
if (util_list_is_empty(addrs))
|
||||
goto next;
|
||||
|
||||
/* Get z Systems specific devices. */
|
||||
selected = selected_dev_list_new();
|
||||
if (select_by_interface(NULL, selected, config_active,
|
||||
scope_mandatory, NULL, NULL,
|
||||
net->str, err_ignore) != EXIT_OK)
|
||||
goto next;
|
||||
|
||||
/* Process devices. */
|
||||
util_list_iterate(selected, sel) {
|
||||
if (sel->rc != EXIT_OK || !sel->st || !sel->id)
|
||||
continue;
|
||||
|
||||
/* Add one resource per IP address provided. */
|
||||
util_list_iterate(addrs, addr) {
|
||||
res = resource_new(sel->st, sel->id, addr->str);
|
||||
ptrlist_add(list, res);
|
||||
}
|
||||
|
||||
/* Expand list to also contain prereq-devices. */
|
||||
subtype_add_prereqs(sel->st, sel->id, selected);
|
||||
}
|
||||
|
||||
next:
|
||||
selected_dev_list_free(selected);
|
||||
strlist_free(addrs);
|
||||
}
|
||||
|
||||
out:
|
||||
strlist_free(interfaces);
|
||||
free(path);
|
||||
}
|
||||
|
||||
/* Return a ptrlist of struct resources of Linux devices which are in use
|
||||
* by the system. The following devices are considered:
|
||||
* - block devices providing a mounted file system
|
||||
* - block devices providing swap space
|
||||
* - networking interfaces providing an IP address. */
|
||||
static struct util_list *get_resources(void)
|
||||
{
|
||||
struct util_list *list;
|
||||
|
||||
list = ptrlist_new();
|
||||
|
||||
/* Add all devices providing mounted file systems. */
|
||||
add_mounts(list);
|
||||
|
||||
/* Add all devices providing swap space. */
|
||||
add_swap(list);
|
||||
|
||||
/* Add all devices providing networking interfaces in the up state. */
|
||||
add_network(list);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/* Return a strlist of names of resources that are provided by device @dev
|
||||
* or %NULL if device is not in use. */
|
||||
struct util_list *inuse_get_resources(struct device *dev)
|
||||
{
|
||||
struct subtype *st = dev->subtype;
|
||||
struct resource *res;
|
||||
struct ptrlist_node *p;
|
||||
struct util_list *list;
|
||||
|
||||
/* Filter out offline devices. */
|
||||
if (subtype_online_get(st, dev, config_active) != 1)
|
||||
return NULL;
|
||||
|
||||
/* Match device against list of devices that are in use. */
|
||||
if (!resources)
|
||||
resources = get_resources();
|
||||
|
||||
list = strlist_new();
|
||||
util_list_iterate(resources, p) {
|
||||
res = p->ptr;
|
||||
if (res->st == dev->subtype && strcmp(res->id, dev->id) == 0)
|
||||
strlist_add_unique(list, "%s", res->name);
|
||||
}
|
||||
|
||||
/* Make handling of empty lists easier for caller. */
|
||||
if (util_list_is_empty(list)) {
|
||||
strlist_free(list);
|
||||
list = NULL;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
110
zdev/src/iscsi.c
Normal file
110
zdev/src/iscsi.c
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "devnode.h"
|
||||
#include "iscsi.h"
|
||||
#include "misc.h"
|
||||
#include "path.h"
|
||||
|
||||
/* Retrieve the IP address of the iSCSI initiator networking portal associated
|
||||
* with block device @blkdev. */
|
||||
static char *ip_from_blockdev(struct devnode *blkdev)
|
||||
{
|
||||
char *link, *curr, *end, *path = NULL, *ip = NULL;
|
||||
|
||||
link = devnode_readlink(blkdev);
|
||||
if (!link)
|
||||
return NULL;
|
||||
|
||||
/* ../../devices/platform/host0/session1/target0:0:0/0:0:0:1/... */
|
||||
curr = strstr(link, "/devices/");
|
||||
if (!curr)
|
||||
goto out;
|
||||
curr++;
|
||||
|
||||
/* curr=devices/platform/host0/session1/target0:0:0/0:0:0:1/... */
|
||||
end = strstr(curr, "/session");
|
||||
if (!end)
|
||||
goto out;
|
||||
*end = 0;
|
||||
|
||||
/* curr=devices/platform/host0 */
|
||||
end = strstr(curr, "/host");
|
||||
if (!end)
|
||||
goto out;
|
||||
end++;
|
||||
|
||||
/* curr=devices/platform/host0
|
||||
* end=host0 */
|
||||
path = path_get("/sys/%s/iscsi_host/%s/ipaddress", curr, end);
|
||||
ip = misc_read_text_file(path, 0, err_ignore);
|
||||
|
||||
out:
|
||||
free(path);
|
||||
free(link);
|
||||
|
||||
return ip;
|
||||
}
|
||||
|
||||
static struct devnode *netdev_from_ip(char *ip)
|
||||
{
|
||||
char *cmd, *text, *dev, *end;
|
||||
struct devnode *devnode = NULL;
|
||||
|
||||
cmd = misc_asprintf("%s -o address show to %s 2>/dev/null", PATH_IP,
|
||||
ip);
|
||||
text = misc_read_cmd_output(cmd, 0, err_ignore);
|
||||
if (!text)
|
||||
goto out;
|
||||
|
||||
/* text=2: enccw0.0.f5f0 inet ... */
|
||||
for (dev = text; *dev && !isspace(*dev); dev++);
|
||||
|
||||
/* dev= enccw0.0.f5f0 inet ... */
|
||||
for (; isspace(*dev); dev++);
|
||||
|
||||
/* dev=enccw0.0.f5f0 inet ... */
|
||||
for (end = dev; *end && !isspace(*end); end++);
|
||||
if (end == dev)
|
||||
goto out;
|
||||
*end = 0;
|
||||
|
||||
/* dev=enccw0.0.f5f0 */
|
||||
devnode = devnode_new(NETDEV, 0, 0, dev);
|
||||
|
||||
out:
|
||||
free(cmd);
|
||||
free(text);
|
||||
|
||||
return devnode;
|
||||
}
|
||||
|
||||
/* Retrieve the networking device devnode that provides the specified
|
||||
* iSCSI block device @blkdev. */
|
||||
struct devnode *iscsi_get_net_devnode(struct devnode *blkdev)
|
||||
{
|
||||
struct devnode *devnode = NULL;
|
||||
char *ip;
|
||||
|
||||
if (blkdev->type != BLOCKDEV)
|
||||
return NULL;
|
||||
|
||||
ip = ip_from_blockdev(blkdev);
|
||||
if (!ip)
|
||||
return NULL;
|
||||
|
||||
devnode = netdev_from_ip(ip);
|
||||
|
||||
free(ip);
|
||||
|
||||
return devnode;
|
||||
}
|
||||
427
zdev/src/lcs.c
Normal file
427
zdev/src/lcs.c
Normal file
@@ -0,0 +1,427 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "attrib.h"
|
||||
#include "ccw.h"
|
||||
#include "ccwgroup.h"
|
||||
#include "device.h"
|
||||
#include "devtype.h"
|
||||
#include "lcs.h"
|
||||
#include "lcs_auto.h"
|
||||
#include "misc.h"
|
||||
#include "namespace.h"
|
||||
#include "setting.h"
|
||||
|
||||
#define DEVNAME "LCS device"
|
||||
|
||||
/*
|
||||
* LCS device ID namespace methods.
|
||||
*/
|
||||
static exit_code_t lcs_parse_devid(struct ccwgroup_devid *devid_ptr,
|
||||
const char *id, err_t err)
|
||||
{
|
||||
struct ccwgroup_devid devid;
|
||||
const char *reason = NULL;
|
||||
exit_code_t rc;
|
||||
|
||||
rc = ccwgroup_parse_devid(&devid, id, err);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (devid.num > LCS_NUM_DEVS) {
|
||||
reason = "Too many CCW device IDs specified";
|
||||
rc = EXIT_INVALID_ID;
|
||||
} else if (devid_ptr)
|
||||
*devid_ptr = devid;
|
||||
|
||||
if (reason) {
|
||||
err_t_print(err, "Error in %s ID format: %s: %s\n", DEVNAME,
|
||||
reason, id);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static exit_code_t lcs_parse_devid_range(struct ccwgroup_devid *from_ptr,
|
||||
struct ccwgroup_devid *to_ptr,
|
||||
const char *range, err_t err)
|
||||
{
|
||||
char *from_str, *to_str;
|
||||
struct ccwgroup_devid from, to;
|
||||
exit_code_t rc;
|
||||
const char *reason = NULL;
|
||||
|
||||
/* Split range. */
|
||||
from_str = misc_strdup(range);
|
||||
to_str = strchr(from_str, '-');
|
||||
if (!to_str) {
|
||||
rc = EXIT_INVALID_ID;
|
||||
reason = "Missing hyphen";
|
||||
goto out;
|
||||
}
|
||||
*to_str = 0;
|
||||
to_str++;
|
||||
|
||||
/* Parse range start end end ID. */
|
||||
rc = lcs_parse_devid(&from, from_str, err);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
rc = lcs_parse_devid(&to, to_str, err);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
/* Only allow ranges on CCWGROUP devices specified as single ID. */
|
||||
if (from.num != 1 || to.num != 1) {
|
||||
rc = EXIT_INVALID_ID;
|
||||
reason = "Ranges only supported on single CCW device IDs";
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = EXIT_OK;
|
||||
if (from_ptr)
|
||||
*from_ptr = from;
|
||||
if (to_ptr)
|
||||
*to_ptr = to;
|
||||
|
||||
out:
|
||||
free(from_str);
|
||||
|
||||
if (reason) {
|
||||
err_t_print(err, "Error in %s ID range format: %s: %s\n",
|
||||
DEVNAME, reason, range);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static bool lcs_parse_devid_range_simple(struct ccwgroup_devid *from,
|
||||
struct ccwgroup_devid *to,
|
||||
const char *range)
|
||||
{
|
||||
if (lcs_parse_devid_range(from, to, range, err_ignore) == EXIT_OK)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static exit_code_t lcs_ns_is_id_valid(const char *id, err_t err)
|
||||
{
|
||||
return lcs_parse_devid(NULL, id, err);
|
||||
}
|
||||
|
||||
static char *lcs_ns_normalize_id(const char *id)
|
||||
{
|
||||
struct ccwgroup_devid devid;
|
||||
|
||||
if (lcs_parse_devid(&devid, id, err_ignore) != EXIT_OK)
|
||||
return NULL;
|
||||
|
||||
return ccwgroup_devid_to_str(&devid);
|
||||
}
|
||||
|
||||
static void *lcs_ns_parse_id(const char *id, err_t err)
|
||||
{
|
||||
struct ccwgroup_devid *devid;
|
||||
|
||||
devid = misc_malloc(sizeof(struct ccwgroup_devid));
|
||||
if (lcs_parse_devid(devid, id, err) != EXIT_OK) {
|
||||
free(devid);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return devid;
|
||||
}
|
||||
|
||||
static exit_code_t lcs_ns_is_id_range_valid(const char *range, err_t err)
|
||||
{
|
||||
return lcs_parse_devid_range(NULL, NULL, range, err);
|
||||
}
|
||||
|
||||
static unsigned long lcs_ns_num_ids_in_range(const char *range)
|
||||
{
|
||||
struct ccwgroup_devid f, t;
|
||||
|
||||
if (!lcs_parse_devid_range_simple(&f, &t, range))
|
||||
return 0;
|
||||
|
||||
if (f.devid[0].cssid != t.devid[0].cssid ||
|
||||
f.devid[0].ssid != t.devid[0].ssid)
|
||||
return 0;
|
||||
|
||||
if (f.devid[0].devno > t.devid[0].devno)
|
||||
return 0;
|
||||
|
||||
return t.devid[0].devno - f.devid[0].devno + 1;
|
||||
}
|
||||
|
||||
static void lcs_ns_range_start(struct ns_range_iterator *it, const char *range)
|
||||
{
|
||||
struct ccwgroup_devid from, to;
|
||||
|
||||
if (!lcs_parse_devid_range_simple(&from, &to, range)) {
|
||||
memset(it, 0, sizeof(struct ns_range_iterator));
|
||||
return;
|
||||
}
|
||||
|
||||
it->devid = ccwgroup_copy_devid(&from);
|
||||
it->devid_last = ccwgroup_copy_devid(&to);
|
||||
it->id = ccwgroup_devid_to_str(it->devid);
|
||||
}
|
||||
|
||||
static bool lcs_ns_is_id_blacklisted(const char *id)
|
||||
{
|
||||
struct ccwgroup_devid devid;
|
||||
char *ccw_id;
|
||||
unsigned int i;
|
||||
bool result = false;
|
||||
|
||||
if (lcs_parse_devid(&devid, id, err_ignore) != EXIT_OK)
|
||||
return false;
|
||||
for (i = 0; i < devid.num; i++) {
|
||||
ccw_id = ccw_devid_to_str(&devid.devid[i]);
|
||||
result = ccw_is_id_blacklisted(ccw_id);
|
||||
free(ccw_id);
|
||||
if (result)
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void lcs_ns_unblacklist_id(const char *id)
|
||||
{
|
||||
struct ccwgroup_devid devid;
|
||||
char *ccw_id;
|
||||
unsigned int i;
|
||||
|
||||
if (lcs_parse_devid(&devid, id, err_ignore) != EXIT_OK)
|
||||
return;
|
||||
for (i = 0; i < devid.num; i++) {
|
||||
ccw_id = ccw_devid_to_str(&devid.devid[i]);
|
||||
if (ccw_is_id_blacklisted(ccw_id))
|
||||
ccw_unblacklist_id(ccw_id);
|
||||
free(ccw_id);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* LCS device ID namespace.
|
||||
*/
|
||||
|
||||
struct namespace lcs_namespace = {
|
||||
.devname = DEVNAME,
|
||||
.is_id_valid = lcs_ns_is_id_valid,
|
||||
.is_id_similar = ccwgroup_is_id_similar,
|
||||
.cmp_ids = ccwgroup_cmp_ids,
|
||||
.normalize_id = lcs_ns_normalize_id,
|
||||
.parse_id = lcs_ns_parse_id,
|
||||
.cmp_parsed_ids = ccwgroup_cmp_parsed_ids,
|
||||
.qsort_cmp = ccwgroup_qsort_cmp,
|
||||
.is_id_range_valid = lcs_ns_is_id_range_valid,
|
||||
.num_ids_in_range = lcs_ns_num_ids_in_range,
|
||||
.is_id_in_range = ccwgroup_is_id_in_range,
|
||||
.range_start = lcs_ns_range_start,
|
||||
.range_next = ccwgroup_range_next,
|
||||
|
||||
/* Blacklist handling. */
|
||||
.is_blacklist_active = ccw_is_blacklist_active,
|
||||
.is_id_blacklisted = lcs_ns_is_id_blacklisted,
|
||||
.is_id_range_blacklisted = ccw_is_id_range_blacklisted,
|
||||
.unblacklist_id = lcs_ns_unblacklist_id,
|
||||
.unblacklist_id_range = ccw_unblacklist_id_range,
|
||||
.blacklist_persist = ccw_blacklist_persist,
|
||||
};
|
||||
|
||||
/*
|
||||
* LCS device attributes.
|
||||
*/
|
||||
|
||||
static struct attrib lcs_attr_lancmd_timeout = {
|
||||
.name = "lancmd_timeout",
|
||||
.title = "Modify LAN command timeout",
|
||||
.desc =
|
||||
"Specify the time in seconds that the LCS driver waits for a reply\n"
|
||||
"after issuing a LAN command to the LAN adapter.\n",
|
||||
.defval = "5",
|
||||
.accept = ACCEPT_ARRAY(ACCEPT_NUM_GE(1)),
|
||||
};
|
||||
|
||||
static struct attrib lcs_attr_recover = {
|
||||
.name = "recover",
|
||||
.title = "Trigger device recovery",
|
||||
.desc =
|
||||
"Write '1' to this attribute to restart the recovery process for the\n"
|
||||
"QETH device.\n",
|
||||
.accept = ACCEPT_ARRAY(ACCEPT_NUM(1)),
|
||||
.writeonly = 1,
|
||||
.activeonly = 1,
|
||||
};
|
||||
|
||||
/*
|
||||
* LCS subtype methods.
|
||||
*/
|
||||
|
||||
static exit_code_t lcs_st_is_definable(struct subtype *st, const char *id,
|
||||
err_t err)
|
||||
{
|
||||
struct ccwgroup_subtype_data *data = st->data;
|
||||
struct ccwgroup_devid devid;
|
||||
exit_code_t rc;
|
||||
|
||||
rc = ccwgroup_parse_devid(&devid, id, err);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (subtype_device_exists_active(st, id))
|
||||
return EXIT_OK;
|
||||
|
||||
if (devid.num == data->num_devs)
|
||||
return lcs_auto_is_possible(&devid, err);
|
||||
|
||||
if (devid.num == 1)
|
||||
return lcs_auto_get_devid(NULL, &devid.devid[0], err);
|
||||
|
||||
err_t_print(err, "Invalid number of CCW device IDs\n");
|
||||
|
||||
return EXIT_INVALID_ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* device_detect_definable - Detect configuration of definable device
|
||||
* @st: Device subtype
|
||||
* @dev: Device
|
||||
*
|
||||
* Detect the full ID and default parameters for non-existing but definable
|
||||
* device @dev and update active.definable. Return %EXIT_OK on success, or an
|
||||
* error code otherwise.
|
||||
*/
|
||||
static exit_code_t lcs_st_detect_definable(struct subtype *st,
|
||||
struct device *dev)
|
||||
{
|
||||
struct ccwgroup_devid *devid;
|
||||
exit_code_t rc;
|
||||
|
||||
devid = dev->devid;
|
||||
if (devid->num == 1) {
|
||||
/* Detect possible group for this device. */
|
||||
rc = lcs_auto_get_devid(devid, &devid->devid[0],
|
||||
err_delayed_print);
|
||||
if (rc) {
|
||||
error("Auto-detection failed for %s %s\n"
|
||||
"Please be sure to specify full CCWGROUP ID!\n",
|
||||
st->devname, dev->id);
|
||||
return rc;
|
||||
}
|
||||
free(dev->id);
|
||||
dev->id = ccwgroup_devid_to_str(dev->devid);
|
||||
}
|
||||
|
||||
dev->active.definable = 1;
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
static void lcs_st_add_definable_ids(struct subtype *st, struct util_list *ids)
|
||||
{
|
||||
lcs_auto_add_ids(ids);
|
||||
}
|
||||
|
||||
/*
|
||||
* LCS subtype.
|
||||
*/
|
||||
|
||||
static struct ccwgroup_subtype_data lcs_data = {
|
||||
.ccwgroupdrv = LCS_CCWGROUPDRV_NAME,
|
||||
.ccwdrv = LCS_CCWDRV_NAME,
|
||||
.rootdrv = LCS_ROOTDRV_NAME,
|
||||
.mod = LCS_MOD_NAME,
|
||||
.num_devs = LCS_NUM_DEVS,
|
||||
};
|
||||
|
||||
static struct subtype lcs_subtype = {
|
||||
.super = &ccwgroup_subtype,
|
||||
.devtype = &lcs_devtype,
|
||||
.name = "lcs",
|
||||
.title = "LAN-Channel-Station (LCS) network devices",
|
||||
.devname = DEVNAME,
|
||||
.modules = STRING_ARRAY(LCS_MOD_NAME),
|
||||
.namespace = &lcs_namespace,
|
||||
.data = &lcs_data,
|
||||
|
||||
.dev_attribs = ATTRIB_ARRAY(
|
||||
&ccw_attr_online,
|
||||
&lcs_attr_lancmd_timeout,
|
||||
&lcs_attr_recover,
|
||||
),
|
||||
.unknown_dev_attribs = 1,
|
||||
.support_definable = 1,
|
||||
|
||||
.is_definable = &lcs_st_is_definable,
|
||||
.detect_definable = &lcs_st_detect_definable,
|
||||
.add_definable_ids = &lcs_st_add_definable_ids,
|
||||
};
|
||||
|
||||
/*
|
||||
* LCS devtype methods.
|
||||
*/
|
||||
|
||||
/* Clean up all resources used by devtype object. */
|
||||
static void lcs_devtype_exit(struct devtype *dt)
|
||||
{
|
||||
setting_list_free(dt->active_settings);
|
||||
setting_list_free(dt->persistent_settings);
|
||||
}
|
||||
|
||||
static exit_code_t lcs_devtype_read_settings(struct devtype *dt,
|
||||
config_t config)
|
||||
{
|
||||
/* No kernel or module parameters exist for the lcs device driver,
|
||||
* but at least determine module loaded state. */
|
||||
dt->active_settings = setting_list_new();
|
||||
dt->persistent_settings = setting_list_new();
|
||||
|
||||
if (SCOPE_ACTIVE(config))
|
||||
dt->active_exists = devtype_is_module_loaded(dt);
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
static exit_code_t lcs_devtype_write_settings(struct devtype *dt,
|
||||
config_t config)
|
||||
{
|
||||
/* No kernel or module parameters exist for the lcs device driver. */
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* LCS devtype.
|
||||
*/
|
||||
|
||||
struct devtype lcs_devtype = {
|
||||
.name = "lcs",
|
||||
.title = "", /* Only use subtypes. */
|
||||
.devname = "LCS",
|
||||
|
||||
.subtypes = SUBTYPE_ARRAY(
|
||||
&lcs_subtype,
|
||||
),
|
||||
|
||||
.type_attribs = ATTRIB_ARRAY(
|
||||
),
|
||||
|
||||
.exit = &lcs_devtype_exit,
|
||||
|
||||
.read_settings = &lcs_devtype_read_settings,
|
||||
.write_settings = &lcs_devtype_write_settings,
|
||||
};
|
||||
347
zdev/src/lcs_auto.c
Normal file
347
zdev/src/lcs_auto.c
Normal file
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <string.h>
|
||||
|
||||
#include "ccw.h"
|
||||
#include "ccwgroup.h"
|
||||
#include "ctc.h"
|
||||
#include "device.h"
|
||||
#include "lcs.h"
|
||||
#include "lcs_auto.h"
|
||||
#include "module.h"
|
||||
#include "path.h"
|
||||
|
||||
struct cutype {
|
||||
unsigned int cutype:16;
|
||||
unsigned int cumodel:8;
|
||||
};
|
||||
|
||||
static struct cutype lcs_cutypes[] = {
|
||||
{ .cutype = 0x3088, .cumodel = 0x08, },
|
||||
{ .cutype = 0x3088, .cumodel = 0x1f, },
|
||||
{ .cutype = 0x3088, .cumodel = 0x60, },
|
||||
};
|
||||
|
||||
/*
|
||||
* LCS autodetection
|
||||
*
|
||||
* A LCS device must be grouped before it can be used. The following
|
||||
* rules apply to grouping:
|
||||
*
|
||||
* 1. A LCS device can be grouped from 2 CCW devices
|
||||
* a) Read device
|
||||
* b) Write device
|
||||
* 2. All CCW devices must be bound to the LCS CCW device driver. Note that
|
||||
* due to an overlap in CU-Types, CCW device could also be bound to
|
||||
* the LCS device driver.
|
||||
* 3. The subchannel of all CCW devices must be defined with the same CHPID
|
||||
* 4. None of the CCW devices is part of an existing CCWGROUP device
|
||||
*/
|
||||
|
||||
/* Compare by: 1. CHPID, 2. CUTYPE, 3. DEVTYPE, 4. CCW device ID
|
||||
* -1 = a < b 1 = a > b 0 = a == b. */
|
||||
static int info_cmp(void *a, void *b, void *data)
|
||||
{
|
||||
struct ptrlist_node *pa = a, *pb = b;
|
||||
struct ccw_devinfo *ia = pa->ptr, *ib = pb->ptr;
|
||||
int r;
|
||||
|
||||
r = ccw_devinfo_chpids_cmp(ia, ib);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
r = ccw_devinfo_cutype_cmp(ia, ib);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
r = ccw_devinfo_devtype_cmp(ia, ib);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return ccw_cmp_devids(&ia->devid, &ib->devid);
|
||||
}
|
||||
|
||||
static bool is_compatible(struct ccw_devinfo *a, struct ccw_devinfo *b)
|
||||
{
|
||||
if (ccw_devinfo_chpids_cmp(a, b) == 0 &&
|
||||
ccw_devinfo_cutype_cmp(a, b) == 0 &&
|
||||
ccw_devinfo_devtype_cmp(a, b) == 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool is_lcs(struct ccw_devinfo *info)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
/* Rule out devices which can be confirmed to be CTC devices. */
|
||||
if (ctc_confirm(&info->devid))
|
||||
return false;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(lcs_cutypes); i++) {
|
||||
if (info->cutype == lcs_cutypes[i].cutype &&
|
||||
info->cumodel == lcs_cutypes[i].cumodel)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Add device info for all LCS CCW devices to ptrlist in data. */
|
||||
static exit_code_t add_cb(const char *path, const char *filename, void *data)
|
||||
{
|
||||
struct ccw_devid devid;
|
||||
struct util_list *infos = data;
|
||||
struct ccw_devinfo *devinfo;
|
||||
|
||||
if (!strchr(filename, '.'))
|
||||
return EXIT_OK;
|
||||
if (!ccw_parse_devid_simple(&devid, filename))
|
||||
return EXIT_OK;
|
||||
devinfo = ccw_devinfo_get(&devid, 0);
|
||||
if (devinfo->exists && is_lcs(devinfo))
|
||||
ptrlist_add(infos, devinfo);
|
||||
else
|
||||
free(devinfo);
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
/* Return a sorted ptrlist of struct ccw_devinfos for all CCW devices
|
||||
* bound to the lcs or ctc CCW device driver with matching CUTYPE.
|
||||
* The result must be freed using ptrlist_free(,1); */
|
||||
static struct util_list *read_sorted_lcs_devinfos(void)
|
||||
{
|
||||
struct util_list *infos;
|
||||
char *path;
|
||||
|
||||
/* Get CHPID information for all devices handled by the LCS driver. */
|
||||
infos = ptrlist_new();
|
||||
|
||||
/* Add CCW devices bound to the LCS CCW device driver. */
|
||||
module_try_load_once(LCS_MOD_NAME, NULL);
|
||||
path = path_get_sys_bus_drv(CCW_BUS_NAME, LCS_CCWDRV_NAME);
|
||||
if (dir_exists(path))
|
||||
path_for_each(path, add_cb, infos);
|
||||
free(path);
|
||||
|
||||
/* Add CCW devices bound to the CTC CCW device driver. */
|
||||
path = path_get_sys_bus_drv(CCW_BUS_NAME, CTC_CCWDRV_NAME);
|
||||
if (dir_exists(path))
|
||||
path_for_each(path, add_cb, infos);
|
||||
free(path);
|
||||
|
||||
/* For each CHPID: Find groups. Add to result list. */
|
||||
util_list_sort(infos, info_cmp, NULL);
|
||||
|
||||
return infos;
|
||||
}
|
||||
|
||||
static void add_ccwgroup_devid(struct util_list *devids, struct ccw_devid *read,
|
||||
struct ccw_devid *write)
|
||||
{
|
||||
struct ccwgroup_devid devid;
|
||||
|
||||
devid.devid[0] = *read;
|
||||
devid.devid[1] = *write;
|
||||
devid.num = LCS_NUM_DEVS;
|
||||
|
||||
ptrlist_add(devids, ccwgroup_copy_devid(&devid));
|
||||
}
|
||||
|
||||
static void add_groupable_devids(struct util_list *devids,
|
||||
struct util_list *infos)
|
||||
{
|
||||
struct ptrlist_node *curr, *next;
|
||||
struct ccw_devinfo *r, *w;
|
||||
|
||||
/* For each CHPID: Find groups. Add to result list. */
|
||||
curr = util_list_start(infos);
|
||||
while (curr) {
|
||||
next = util_list_next(infos, curr);
|
||||
if (!next)
|
||||
break;
|
||||
r = curr->ptr;
|
||||
w = next->ptr;
|
||||
if (is_compatible(r, w) && EVEN(r->devid.devno) &&
|
||||
ccw_devid_distance(&r->devid, &w->devid) == 1) {
|
||||
add_ccwgroup_devid(devids, &r->devid, &w->devid);
|
||||
curr = util_list_next(infos, next);
|
||||
} else
|
||||
curr = next;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add CCWGROUP IDs of lcs devices that can be grouped to strlist @ids. */
|
||||
void lcs_auto_add_ids(struct util_list *ids)
|
||||
{
|
||||
struct util_list *infos, *devids;
|
||||
struct ptrlist_node *p;
|
||||
char *id;
|
||||
|
||||
infos = read_sorted_lcs_devinfos();
|
||||
devids = ptrlist_new();
|
||||
add_groupable_devids(devids, infos);
|
||||
|
||||
util_list_iterate(devids, p) {
|
||||
id = ccwgroup_devid_to_str(p->ptr);
|
||||
strlist_add(ids, id);
|
||||
free(id);
|
||||
}
|
||||
|
||||
ptrlist_free(devids, 1);
|
||||
ptrlist_free(infos, 1);
|
||||
}
|
||||
|
||||
exit_code_t lcs_auto_get_devid(struct ccwgroup_devid *devid_ptr,
|
||||
struct ccw_devid *ccw_devid, err_t err)
|
||||
{
|
||||
struct util_list *devids, *infos;
|
||||
struct ptrlist_node *p, *read, *write;
|
||||
struct ccw_devinfo *r, *w;
|
||||
struct ccwgroup_devid *devid;
|
||||
exit_code_t rc;
|
||||
|
||||
infos = read_sorted_lcs_devinfos();
|
||||
|
||||
/* Try to find an ID from the canonical auto-generated list. */
|
||||
devids = ptrlist_new();
|
||||
add_groupable_devids(devids, infos);
|
||||
|
||||
util_list_iterate(devids, p) {
|
||||
devid = p->ptr;
|
||||
if (ccw_cmp_devids(ccw_devid, &devid->devid[0]) != 0)
|
||||
continue;
|
||||
rc = EXIT_OK;
|
||||
if (devid_ptr)
|
||||
*devid_ptr = *devid;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Try to create a CCWGROUP ID with the specified ID as read device. */
|
||||
|
||||
/* Get CCW device info for read device. */
|
||||
read = NULL;
|
||||
util_list_iterate(infos, read) {
|
||||
r = read->ptr;
|
||||
if (ccw_cmp_devids(&r->devid, ccw_devid) == 0)
|
||||
break;
|
||||
}
|
||||
if (!read) {
|
||||
err_t_print(err, "Read CCW device not found\n");
|
||||
rc = EXIT_GROUP_NOT_FOUND;
|
||||
goto out;
|
||||
}
|
||||
if (!EVEN(r->devid.devno)) {
|
||||
err_t_print(err, "Read CCW device number not even\n");
|
||||
rc = EXIT_GROUP_INVALID;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get CCW device ID for write device. */
|
||||
write = util_list_next(infos, read);
|
||||
if (!write) {
|
||||
err_t_print(err, "Write CCW device not found\n");
|
||||
rc = EXIT_GROUP_NOT_FOUND;
|
||||
goto out;
|
||||
}
|
||||
w = write->ptr;
|
||||
if (!is_compatible(r, w)) {
|
||||
err_t_print(err, "No compatible write CCW device found\n");
|
||||
rc = EXIT_GROUP_INVALID;
|
||||
goto out;
|
||||
}
|
||||
if (ccw_devid_distance(&r->devid, &w->devid) != 1) {
|
||||
err_t_print(err, "Write CCW device ID must be read plus one\n");
|
||||
rc = EXIT_GROUP_INVALID;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = EXIT_OK;
|
||||
if (devid_ptr) {
|
||||
devid_ptr->devid[0] = r->devid;
|
||||
devid_ptr->devid[1] = w->devid;
|
||||
devid_ptr->num = LCS_NUM_DEVS;
|
||||
}
|
||||
|
||||
out:
|
||||
ptrlist_free(devids, 1);
|
||||
ptrlist_free(infos, 1);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
exit_code_t lcs_auto_is_possible(struct ccwgroup_devid *devid, err_t err)
|
||||
{
|
||||
struct ccw_devinfo *info[LCS_NUM_DEVS];
|
||||
unsigned int i;
|
||||
char *ccwid;
|
||||
const char *msg;
|
||||
exit_code_t rc;
|
||||
|
||||
if (devid->num < LCS_NUM_DEVS) {
|
||||
err_t_print(err, "Not enough CCW device IDs in LCS device "
|
||||
"ID\n");
|
||||
return EXIT_INCOMPLETE_ID;
|
||||
}
|
||||
if (devid->num > LCS_NUM_DEVS) {
|
||||
err_t_print(err, "LCS device ID contains too many CCW device "
|
||||
"IDs\n");
|
||||
return EXIT_INVALID_ID;
|
||||
}
|
||||
if (ccw_devid_distance(&devid->devid[0], &devid->devid[1]) != 1) {
|
||||
err_t_print(err, "Write device ID must be read plus one\n");
|
||||
return EXIT_GROUP_INVALID;
|
||||
}
|
||||
if (!EVEN(devid->devid[0].devno)) {
|
||||
err_t_print(err, "Device number of read device ID must be "
|
||||
"even\n");
|
||||
return EXIT_GROUP_INVALID;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(info); i++)
|
||||
info[i] = ccw_devinfo_get(&devid->devid[i], 1);
|
||||
|
||||
rc = EXIT_OK;
|
||||
msg = NULL;
|
||||
for (i = 0; i < ARRAY_SIZE(info); i++) {
|
||||
if (!info[i]->exists) {
|
||||
msg = "CCW device %s does not exist\n";
|
||||
rc = EXIT_GROUP_NOT_FOUND;
|
||||
} else if (info[i]->grouped) {
|
||||
msg = "CCW device %s already grouped\n";
|
||||
rc = EXIT_GROUP_ALREADY;
|
||||
} else if (i > 0 &&
|
||||
ccw_devinfo_chpids_cmp(info[i - 1], info[i]) != 0) {
|
||||
msg = "CCW device %s is not on the same CHPID\n";
|
||||
rc = EXIT_GROUP_INVALID;
|
||||
} else if (i > 0 &&
|
||||
ccw_devinfo_cutype_cmp(info[i - 1], info[i]) != 0) {
|
||||
msg = "CUTYPE of CCW device %s differs\n";
|
||||
rc = EXIT_GROUP_INVALID;
|
||||
} else if (i > 0 &&
|
||||
ccw_devinfo_devtype_cmp(info[i - 1], info[i]) != 0) {
|
||||
msg = "DEVTYPE of CCW device %s differs\n";
|
||||
rc = EXIT_GROUP_INVALID;
|
||||
}
|
||||
if (!msg)
|
||||
continue;
|
||||
ccwid = ccw_devid_to_str(&devid->devid[i]);
|
||||
err_t_print(err, msg, ccwid);
|
||||
free(ccwid);
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(info); i++)
|
||||
free(info[i]);
|
||||
|
||||
return rc;
|
||||
}
|
||||
1538
zdev/src/lszdev.c
Normal file
1538
zdev/src/lszdev.c
Normal file
File diff suppressed because it is too large
Load Diff
45
zdev/src/lszdev_usage.txt
Normal file
45
zdev/src/lszdev_usage.txt
Normal file
@@ -0,0 +1,45 @@
|
||||
Usage: lszdev [TYPE] [DEVICE] [SELECTION] [ACTION] [OPTIONS]
|
||||
|
||||
Use lszdev to view the configuration of z Systems specific devices in either:
|
||||
- active configuration (running system), or
|
||||
- persistent configuration (configuration files)
|
||||
|
||||
Actions apply to both configurations unless specified otherwise.
|
||||
|
||||
TYPE
|
||||
Device type to which this command applies. Use --list-types to display
|
||||
supported types.
|
||||
|
||||
DEVICE
|
||||
ID Select single device by ID, e.g. 0.0.1234
|
||||
FROM-TO Select range of devices between FROM and TO
|
||||
DEV1,DEV2,... Select list of devices or device ranges
|
||||
|
||||
SELECTION
|
||||
--all Select all existing and configured devices (default)
|
||||
--configured Select devices with a persistent configuration
|
||||
--existing Select devices found in the active configuration
|
||||
--online/--offline Select devices that are online/offline
|
||||
--failed Select devices that are not functioning correctly
|
||||
--by-path PATH Select device providing file system path, e.g. /usr
|
||||
--by-node NODE Select device providing device node, e.g. /dev/sda
|
||||
--by-interface NAME Select device providing network interface, e.g. eth0
|
||||
--by-attrib KEY=VALUE Select devices with specified attribute value
|
||||
|
||||
ACTIONS
|
||||
-i, --info Display detailed information
|
||||
-l, --list-columns List available output columns
|
||||
-L, --list-types List supported device types
|
||||
-h, --help Print usage information, then exit
|
||||
-v, --version Print version information, then exit
|
||||
|
||||
OPTIONS
|
||||
-a, --active Only show data from the active configuration
|
||||
-p, --persistent Only show data from the persistent configuration
|
||||
-t, --type List information about device type
|
||||
-c, --columns COLUMNS Specify comma-separated list of columns to display
|
||||
-n, --no-headings Do not print column headings
|
||||
--base PATH Use PATH as base for accessing files
|
||||
--pairs Produce output in KEY="VALUE" format
|
||||
-V, --verbose Print additional run-time information
|
||||
-q, --quiet Print only minimal run-time information
|
||||
1753
zdev/src/misc.c
Normal file
1753
zdev/src/misc.c
Normal file
File diff suppressed because it is too large
Load Diff
424
zdev/src/modprobe.c
Normal file
424
zdev/src/modprobe.c
Normal file
@@ -0,0 +1,424 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "attrib.h"
|
||||
#include "misc.h"
|
||||
#include "modprobe.h"
|
||||
#include "path.h"
|
||||
#include "setting.h"
|
||||
|
||||
/**
|
||||
* struct modprobe_line - Representation of a line in a modprobe.conf file
|
||||
* @node: List node for adding to list
|
||||
* @line: Original line contents or NULL if unavailable
|
||||
* @argv: Array of command arguments
|
||||
* @argc: Count of command arguments
|
||||
*/
|
||||
struct modprobe_line {
|
||||
struct util_list_node node;
|
||||
char *line;
|
||||
char **argv;
|
||||
int argc;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct modprobe_file - Representation of a modprobe.conf file
|
||||
* @path: Full path and filename of modprobe.conf file
|
||||
* @list: List of lines in file
|
||||
*/
|
||||
struct modprobe_file {
|
||||
char *path;
|
||||
struct util_list lines;
|
||||
};
|
||||
|
||||
/* Used for debugging. */
|
||||
void modprobe_file_print(struct modprobe_file *mf)
|
||||
{
|
||||
struct modprobe_line *l;
|
||||
int i;
|
||||
|
||||
printf("modprobe_file(%p)\n", (void *) mf);
|
||||
printf(" path='%s'\n", mf->path);
|
||||
util_list_iterate(&mf->lines, l) {
|
||||
printf(" line(%p)\n", (void *) l);
|
||||
printf(" line='%s'\n", l->line);
|
||||
printf(" argc=%d\n", l->argc);
|
||||
for (i = 0; i < l->argc; i++)
|
||||
printf(" argv[%d]='%s'\n", i, l->argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove the line continuation character \\ at end of line. */
|
||||
static void remove_linecont(char *line)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; line[i]; i++) {
|
||||
if (line[i] == '\\' && line[i + 1] == '\n')
|
||||
line[i++] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate and initialize a struct modprobe_line from a line of text. */
|
||||
static void set_line_content(struct modprobe_line *m, char *line)
|
||||
{
|
||||
char c, *copy;
|
||||
|
||||
m->line = misc_strdup(line);
|
||||
if (sscanf(line, " %c", &c) != 1) {
|
||||
/* Empty line. */
|
||||
m->argv = misc_malloc(sizeof(char *));
|
||||
m->argv[0] = misc_strdup("");
|
||||
m->argc = 1;
|
||||
} else if (c == '#') {
|
||||
/* Comment line. */
|
||||
m->argv = misc_malloc(sizeof(char *));
|
||||
m->argv[0] = misc_strdup("#");
|
||||
m->argc = 1;
|
||||
} else {
|
||||
/* Line containing a command. */
|
||||
copy = misc_strdup(line);
|
||||
remove_linecont(copy);
|
||||
line_split(copy, &m->argc, &m->argv);
|
||||
free(copy);
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate and initialize a struct modprobe_line from a line of text. */
|
||||
static struct modprobe_line *modprobe_line_new(char *line)
|
||||
{
|
||||
struct modprobe_line *m;
|
||||
|
||||
m = misc_malloc(sizeof(struct modprobe_line));
|
||||
set_line_content(m, line);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
/* Release all resources associated with a modprobe_line. */
|
||||
static void modprobe_line_free(struct modprobe_line *m)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!m)
|
||||
return;
|
||||
free(m->line);
|
||||
for (i = 0; i < m->argc; i++)
|
||||
free(m->argv[i]);
|
||||
free(m->argv);
|
||||
free(m);
|
||||
}
|
||||
|
||||
/* Create and initialize a new modprobe_file. */
|
||||
static struct modprobe_file *modprobe_file_new(const char *path)
|
||||
{
|
||||
struct modprobe_file *file;
|
||||
|
||||
file = misc_malloc(sizeof(struct modprobe_file));
|
||||
file->path = misc_strdup(path);
|
||||
util_list_init(&file->lines, struct modprobe_line, node);
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
/* Release resources used by file. */
|
||||
static void modprobe_file_free(struct modprobe_file *file)
|
||||
{
|
||||
struct modprobe_line *l, *n;
|
||||
|
||||
if (!file)
|
||||
return;
|
||||
free(file->path);
|
||||
util_list_iterate_safe(&file->lines, l, n) {
|
||||
util_list_remove(&file->lines, l);
|
||||
modprobe_line_free(l);
|
||||
}
|
||||
|
||||
free(file);
|
||||
}
|
||||
|
||||
/* Add a new line to the file. */
|
||||
static void modprobe_file_add(struct modprobe_file *file,
|
||||
struct modprobe_line *line)
|
||||
{
|
||||
util_list_add_tail(&file->lines, line);
|
||||
}
|
||||
|
||||
#define MODPROBE_MAX_LINE 2048
|
||||
|
||||
/* Read a modprobe.conf file and return a modprobe_file. */
|
||||
static exit_code_t modprobe_read(const char *path, struct modprobe_file **mf)
|
||||
{
|
||||
char line[MODPROBE_MAX_LINE];
|
||||
char *l, *last;
|
||||
int len;
|
||||
FILE *fd;
|
||||
struct modprobe_file *mfile;
|
||||
|
||||
debug("Reading udev file %s\n", path);
|
||||
mfile = modprobe_file_new(path);
|
||||
fd = misc_fopen(path, "r");
|
||||
if (!fd) {
|
||||
error("Could not read file %s: %s\n", path, strerror(errno));
|
||||
modprobe_file_free(mfile);
|
||||
return EXIT_RUNTIME_ERROR;
|
||||
}
|
||||
last = NULL;
|
||||
while (fgets(line, sizeof(line), fd)) {
|
||||
if (last) {
|
||||
/* The previous line had a continuation mark. */
|
||||
l = misc_asprintf("%s%s", last, line);
|
||||
free(last);
|
||||
last = NULL;
|
||||
} else
|
||||
l = misc_strdup(line);
|
||||
len = strlen(l);
|
||||
if (len > 2 && line[len - 1] == '\n' && line[len - 2] == '\\') {
|
||||
/* This line is continued on the next one. */
|
||||
last = l;
|
||||
continue;
|
||||
}
|
||||
modprobe_file_add(mfile, modprobe_line_new(l));
|
||||
free(l);
|
||||
}
|
||||
if (last) {
|
||||
/* Handle last line with a broken continuation mark
|
||||
* gracefully. */
|
||||
modprobe_file_add(mfile, modprobe_line_new(last));
|
||||
free(last);
|
||||
}
|
||||
if (misc_fclose(fd))
|
||||
warn("Could not close file %s: %s\n", path, strerror(errno));
|
||||
*mf = mfile;
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
/* Check modprobe file for a leading chzdev comment. */
|
||||
static bool find_chzdev_comment(struct modprobe_file *mf)
|
||||
{
|
||||
struct modprobe_line *l;
|
||||
|
||||
util_list_iterate(&mf->lines, l) {
|
||||
/* Newly created line at start. */
|
||||
if (!l->line)
|
||||
return false;
|
||||
/* Skip empty line. */
|
||||
if (strlen(l->argv[0]) == 0)
|
||||
continue;
|
||||
/* Non-comment line. */
|
||||
if (strcmp(l->argv[0], "#") != 0)
|
||||
return false;
|
||||
/* Check for chzdev comment. */
|
||||
if (strstr(l->line, "chzdev"))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Write a modprobe.conf file. */
|
||||
static exit_code_t modprobe_write(struct modprobe_file *mf)
|
||||
{
|
||||
FILE *fd;
|
||||
struct modprobe_line *l;
|
||||
int i;
|
||||
|
||||
debug("Writing udev file %s\n", mf->path);
|
||||
fd = misc_fopen(mf->path, "w");
|
||||
if (!fd) {
|
||||
error("Could not write to file %s: %s\n", mf->path,
|
||||
strerror(errno));
|
||||
return EXIT_RUNTIME_ERROR;
|
||||
}
|
||||
|
||||
/* Add leading comment. */
|
||||
if (!find_chzdev_comment(mf))
|
||||
fprintf(fd, "# Generated by chzdev\n");
|
||||
util_list_iterate(&mf->lines, l) {
|
||||
if (l->line) {
|
||||
/* Use existing line. */
|
||||
fprintf(fd, "%s", l->line);
|
||||
} else {
|
||||
/* Create line by concatenating arguments. */
|
||||
for (i = 0; i < l->argc; i++) {
|
||||
fprintf(fd, "%s%s", i == 0 ? "" : " ",
|
||||
l->argv[i]);
|
||||
}
|
||||
fprintf(fd, "\n");
|
||||
}
|
||||
}
|
||||
if (misc_fclose(fd)) {
|
||||
warn("Could not close file %s: %s\n", mf->path,
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
/* Convert a single modprobe.conf option argument into a newly allocated
|
||||
* struct setting. */
|
||||
static struct setting *arg_to_setting(struct attrib **attribs, char *arg)
|
||||
{
|
||||
struct setting *s;
|
||||
struct attrib *a;
|
||||
char *key;
|
||||
char *val;
|
||||
|
||||
key = misc_strdup(arg);
|
||||
val = strchr(key, '=');
|
||||
if (val) {
|
||||
*val = 0;
|
||||
val++;
|
||||
} else {
|
||||
/* Assume boolean parameter. */
|
||||
val = "1";
|
||||
}
|
||||
|
||||
/* Get attribute pointer for known attributes. */
|
||||
a = attrib_find(attribs, key);
|
||||
s = setting_new(a, key, val);
|
||||
free(key);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Return a newly allocated struct setting_list for module options found
|
||||
* in modprobe.conf file for specified module name. */
|
||||
static struct setting_list *modprobe_get_settings(struct modprobe_file *mf,
|
||||
const char *mod,
|
||||
struct attrib **attribs)
|
||||
{
|
||||
struct setting_list *sl;
|
||||
struct modprobe_line *l;
|
||||
struct setting *s;
|
||||
int i;
|
||||
|
||||
sl = setting_list_new();
|
||||
util_list_iterate(&mf->lines, l) {
|
||||
/* argv[]="option", "<mod>", "<key>=<value>", ... */
|
||||
if (l->argc < 3)
|
||||
continue;
|
||||
if (strcmp(l->argv[0], "options") != 0)
|
||||
continue;
|
||||
if (strcmp(l->argv[1], mod) != 0)
|
||||
continue;
|
||||
for (i = 2; i < l->argc; i++) {
|
||||
s = arg_to_setting(attribs, l->argv[i]);
|
||||
setting_list_add(sl, s);
|
||||
}
|
||||
}
|
||||
|
||||
return sl;
|
||||
}
|
||||
|
||||
/* Apply settings list as module parameters to modprobe file. */
|
||||
static void modprobe_apply_settings(struct modprobe_file *mf, const char *mod,
|
||||
struct setting_list *sl)
|
||||
{
|
||||
struct modprobe_line *l, *n;
|
||||
struct setting *s;
|
||||
unsigned long num;
|
||||
|
||||
/* Remove all lines containing parameters for this module. */
|
||||
util_list_iterate_safe(&mf->lines, l, n) {
|
||||
if (l->argc < 2)
|
||||
continue;
|
||||
if (strcmp(l->argv[0], "options") != 0)
|
||||
continue;
|
||||
if (strcmp(l->argv[1], mod) != 0)
|
||||
continue;
|
||||
util_list_remove(&mf->lines, l);
|
||||
modprobe_line_free(l);
|
||||
}
|
||||
|
||||
/* Add new line with specified settings. */
|
||||
num = 0;
|
||||
util_list_iterate(&sl->list, s) {
|
||||
if (setting_is_set(s))
|
||||
num++;
|
||||
}
|
||||
if (num == 0)
|
||||
return;
|
||||
l = misc_malloc(sizeof(struct modprobe_line));
|
||||
l->argv = misc_malloc(sizeof(char *) * (num + 2));
|
||||
l->argv[0] = misc_strdup("options");
|
||||
l->argv[1] = misc_strdup(mod);
|
||||
l->argc = 2;
|
||||
util_list_iterate(&sl->list, s) {
|
||||
if (!setting_is_set(s))
|
||||
continue;
|
||||
l->argv[(l->argc)++] = misc_asprintf("%s=%s", s->name,
|
||||
s->value);
|
||||
}
|
||||
modprobe_file_add(mf, l);
|
||||
}
|
||||
|
||||
/* Read attribute settings from a modprobe.conf file into a newly
|
||||
* allocated struct setting_list. */
|
||||
exit_code_t modprobe_read_settings(const char *path, const char *mod,
|
||||
struct attrib **attribs,
|
||||
struct setting_list **settings)
|
||||
{
|
||||
struct modprobe_file *mf;
|
||||
exit_code_t rc;
|
||||
|
||||
if (!file_exists(path)) {
|
||||
*settings = NULL;
|
||||
return EXIT_OK;
|
||||
}
|
||||
rc = modprobe_read(path, &mf);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
*settings = modprobe_get_settings(mf, mod, attribs);
|
||||
modprobe_file_free(mf);
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
/* Write attribute settings to a modprobe.conf file.*/
|
||||
exit_code_t modprobe_write_settings(const char *path, const char *mod,
|
||||
struct setting_list *settings)
|
||||
{
|
||||
struct modprobe_file *mf;
|
||||
exit_code_t rc;
|
||||
unsigned long lines;
|
||||
|
||||
if (file_exists(path)) {
|
||||
rc = modprobe_read(path, &mf);
|
||||
if (rc)
|
||||
return rc;
|
||||
} else {
|
||||
rc = path_create(path);
|
||||
if (rc)
|
||||
return rc;
|
||||
mf = modprobe_file_new(path);
|
||||
}
|
||||
|
||||
modprobe_apply_settings(mf, mod, settings);
|
||||
|
||||
lines = util_list_len(&mf->lines);
|
||||
if (lines == 0 || (lines == 1 && find_chzdev_comment(mf))) {
|
||||
/* Do not write empty files. */
|
||||
if (file_exists(path))
|
||||
rc = remove_file(path);
|
||||
} else
|
||||
rc = modprobe_write(mf);
|
||||
modprobe_file_free(mf);
|
||||
|
||||
return rc;
|
||||
}
|
||||
305
zdev/src/module.c
Normal file
305
zdev/src/module.c
Normal file
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <dirent.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "attrib.h"
|
||||
#include "misc.h"
|
||||
#include "module.h"
|
||||
#include "path.h"
|
||||
#include "setting.h"
|
||||
#include "udev.h"
|
||||
|
||||
static struct util_list *tried_loading;
|
||||
static int suppress_module_load;
|
||||
|
||||
/* Allow suppression of module loading. */
|
||||
void module_load_suppress(int state)
|
||||
{
|
||||
suppress_module_load = 1;
|
||||
}
|
||||
|
||||
/* Check if a module is currently loaded. */
|
||||
bool module_loaded(const char *mod)
|
||||
{
|
||||
char *path = path_get_sys_module(mod);
|
||||
bool rc;
|
||||
|
||||
rc = dir_exists(path);
|
||||
free(path);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int module_get_refcnt(const char *mod)
|
||||
{
|
||||
char *path, *refcnt_path, *text;
|
||||
int refcnt = 0;
|
||||
|
||||
path = path_get_sys_module(mod);
|
||||
refcnt_path = misc_asprintf("%s/refcnt", path);
|
||||
text = misc_read_text_file(refcnt_path, 1, err_delayed_print);
|
||||
if (text)
|
||||
refcnt = atoi(text);
|
||||
free(refcnt_path);
|
||||
free(path);
|
||||
|
||||
return refcnt;
|
||||
}
|
||||
|
||||
/* Attempt to unload specified kernel module. */
|
||||
static exit_code_t module_unload(const char *mod, err_t err)
|
||||
{
|
||||
char *mp;
|
||||
int rc;
|
||||
|
||||
if (!dryrun && module_get_refcnt(mod) > 0) {
|
||||
err_t_print(err, "Cannot unload module %s: Module is in use\n",
|
||||
mod);
|
||||
return EXIT_MOD_BUSY;
|
||||
}
|
||||
|
||||
mp = path_get_modprobe();
|
||||
rc = misc_system(err, "%s -r %s", mp, mod);
|
||||
free(mp);
|
||||
if (rc != 0)
|
||||
return EXIT_MOD_UNLOAD_FAILED;
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
/* Attempt to load kernel module with specified parameters. PARAMS may be
|
||||
* NULL to load the module with default parameters. */
|
||||
static exit_code_t do_load(const char *mod, char *params, err_t err)
|
||||
{
|
||||
char *empty_file;
|
||||
char *mp;
|
||||
exit_code_t rc;
|
||||
|
||||
rc = EXIT_OK;
|
||||
mp = path_get_modprobe();
|
||||
if (params) {
|
||||
/* Note: We need to pass an empty configuration file to
|
||||
* modprobe or the persistent parameters in /etc/modprobe.d
|
||||
* would always overwrite the specified parameters. */
|
||||
rc = misc_mktemp(&empty_file, NULL);
|
||||
if (rc)
|
||||
goto out;
|
||||
if (misc_system(err, "%s %s %s -C %s %s", mp, mod, params,
|
||||
empty_file,
|
||||
err == err_ignore ? " 2>/dev/null" : ""))
|
||||
rc = EXIT_MOD_LOAD_FAILED;
|
||||
remove_file(empty_file);
|
||||
free(empty_file);
|
||||
} else {
|
||||
if (misc_system(err, "%s %s %s", mp, mod,
|
||||
err == err_ignore ? " 2>/dev/null" : ""))
|
||||
rc = EXIT_MOD_LOAD_FAILED;
|
||||
}
|
||||
|
||||
out:
|
||||
free(mp);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Apply kernel module parameters. */
|
||||
exit_code_t module_load(const char *mod, const char **deps,
|
||||
struct setting_list *settings, err_t err)
|
||||
{
|
||||
char *params;
|
||||
struct util_list *unloaded = NULL;
|
||||
struct strlist_node *s;
|
||||
exit_code_t rc;
|
||||
int i;
|
||||
|
||||
if (suppress_module_load)
|
||||
return EXIT_OK;
|
||||
|
||||
/* Unload modules depending on mod. */
|
||||
if (deps) {
|
||||
unloaded = strlist_new();
|
||||
for (i = 0; deps[i]; i++) {
|
||||
if (!module_loaded(deps[i]) && !dryrun)
|
||||
continue;
|
||||
rc = module_unload(deps[i], err);
|
||||
if (rc)
|
||||
goto out;
|
||||
strlist_add(unloaded, "%s", deps[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (module_loaded(mod) || dryrun) {
|
||||
rc = module_unload(mod, err);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
params = settings ? setting_list_flatten(settings) : NULL;
|
||||
rc = do_load(mod, params, err);
|
||||
free(params);
|
||||
|
||||
if (rc == EXIT_OK && unloaded) {
|
||||
/* Re-load modules depending on mod. */
|
||||
util_list_iterate(unloaded, s) {
|
||||
rc = do_load(s->str, "", err);
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
}
|
||||
out:
|
||||
strlist_free(unloaded);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct add_setting_data {
|
||||
struct setting_list *list;
|
||||
struct attrib **attribs;
|
||||
};
|
||||
|
||||
/* Add all attribute values to settings list. */
|
||||
static exit_code_t add_setting(const char *path, const char *name, void *data)
|
||||
{
|
||||
struct add_setting_data *sdata = data;
|
||||
struct setting_list *list = sdata->list;
|
||||
struct attrib **attribs = sdata->attribs;
|
||||
struct attrib *a;
|
||||
char *value;
|
||||
|
||||
value = misc_read_text_file(path, 1, err_print);
|
||||
if (!value)
|
||||
return EXIT_RUNTIME_ERROR;
|
||||
if (strcmp(value, "(null)") == 0)
|
||||
goto out;
|
||||
a = attrib_find(attribs, name);
|
||||
setting_list_apply_actual(list, a, name, value);
|
||||
out:
|
||||
free(value);
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
/* Retrieve currently active module parameters. */
|
||||
exit_code_t module_get_params(const char *mod, struct attrib **attribs,
|
||||
struct setting_list **settings)
|
||||
{
|
||||
struct add_setting_data data;
|
||||
struct setting_list *list = NULL;
|
||||
char *path;
|
||||
exit_code_t rc = EXIT_OK;
|
||||
|
||||
if (!module_loaded(mod))
|
||||
goto out;
|
||||
list = setting_list_new();
|
||||
|
||||
path = path_get_sys_module_param(mod, NULL);
|
||||
data.list = list;
|
||||
data.attribs = attribs;
|
||||
rc = path_for_each(path, add_setting, &data);
|
||||
free(path);
|
||||
|
||||
out:
|
||||
if (rc == EXIT_OK)
|
||||
*settings = list;
|
||||
else
|
||||
setting_list_free(list);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Try to load a kernel module once. If @path is not %NULL, don't try loading
|
||||
* the module if @path exists. */
|
||||
void module_try_load_once(const char *mod, const char *path)
|
||||
{
|
||||
if (suppress_module_load)
|
||||
return;
|
||||
if (tried_loading) {
|
||||
if (strlist_find(tried_loading, mod))
|
||||
return;
|
||||
} else
|
||||
tried_loading = strlist_new();
|
||||
strlist_add(tried_loading, mod);
|
||||
if (path && path_exists(path))
|
||||
return;
|
||||
if (module_loaded(mod))
|
||||
return;
|
||||
|
||||
verb("Loading required kernel module: %s\n", mod);
|
||||
if (module_load(mod, NULL, NULL, err_ignore))
|
||||
verb("Failed to load kernel module: %s\n", mod);
|
||||
else {
|
||||
/* Let udev rules apply. */
|
||||
udev_settle();
|
||||
}
|
||||
}
|
||||
|
||||
/* Release any global memory. */
|
||||
void module_exit(void)
|
||||
{
|
||||
strlist_free(tried_loading);
|
||||
}
|
||||
|
||||
/* Apply module parameters for kernel module @mod found in @settings via
|
||||
* /sys/module/../parameters. Return %true if all modified settings
|
||||
* could be set via this method. */
|
||||
bool module_set_params(const char *mod, struct setting_list *settings)
|
||||
{
|
||||
struct setting *s;
|
||||
char *path;
|
||||
const char *value;
|
||||
bool result;
|
||||
exit_code_t rc;
|
||||
|
||||
/* First check if all modified settings can be applied this way. */
|
||||
util_list_iterate(&settings->list, s) {
|
||||
if (!s->modified && !s->removed) {
|
||||
/* Nothing to do. */
|
||||
continue;
|
||||
}
|
||||
if (!s->attrib || !s->attrib->nounload) {
|
||||
/* Attribute does not support setting via Sysfs. */
|
||||
return false;
|
||||
}
|
||||
if (s->removed && !s->attrib->defval) {
|
||||
/* Cannot remove setting with no default value. */
|
||||
return false;
|
||||
}
|
||||
path = path_get_sys_module_param(mod, s->name);
|
||||
result = file_writable(path);
|
||||
free(path);
|
||||
if (!result) {
|
||||
/* Sysfs file is not writable. */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Apply settings. */
|
||||
util_list_iterate(&settings->list, s) {
|
||||
if (s->removed)
|
||||
value = s->attrib->defval;
|
||||
else if (s->modified)
|
||||
value = s->value;
|
||||
else {
|
||||
/* Nothing to do. */
|
||||
continue;
|
||||
}
|
||||
|
||||
path = path_get_sys_module_param(mod, s->name);
|
||||
rc = misc_write_text_file(path, value, err_ignore);
|
||||
free(path);
|
||||
if (rc)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
174
zdev/src/namespace.c
Normal file
174
zdev/src/namespace.c
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "ccw.h"
|
||||
#include "ctc.h"
|
||||
#include "devtype.h"
|
||||
#include "lcs.h"
|
||||
#include "namespace.h"
|
||||
#include "qeth.h"
|
||||
#include "zfcp_lun.h"
|
||||
|
||||
struct namespace *namespaces[] = {
|
||||
&ccw_namespace,
|
||||
&zfcp_lun_namespace,
|
||||
&qeth_namespace,
|
||||
&ctc_namespace,
|
||||
&lcs_namespace,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static int ns_modified[NUM_NAMESPACES];
|
||||
|
||||
/* Return the index of NS in the namespaces array. */
|
||||
int namespaces_index(struct namespace *ns)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; namespaces[i]; i++) {
|
||||
if (namespaces[i] == ns)
|
||||
return i;
|
||||
}
|
||||
|
||||
/* Should not happen. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check if the specified string is a valid ID for any known namespace. */
|
||||
bool namespaces_is_id_valid(const char *id)
|
||||
{
|
||||
struct namespace *ns;
|
||||
int i;
|
||||
|
||||
for (i = 0; (ns = namespaces[i]); i++) {
|
||||
if (ns_is_id_valid(ns, id))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check if the specified string is a valid ID range for any known namespace. */
|
||||
bool namespaces_is_id_range_valid(const char *range)
|
||||
{
|
||||
struct namespace *ns;
|
||||
int i;
|
||||
|
||||
for (i = 0; (ns = namespaces[i]); i++) {
|
||||
if (ns_is_id_range_valid(ns, range))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check known subtypes of the same namespace for the existence of a device
|
||||
* with the specified ID. Return pointer to first matching subtype found. */
|
||||
bool namespaces_device_exists(struct namespace *ns, const char *id,
|
||||
config_t config, struct subtype **st_ptr)
|
||||
{
|
||||
int i, j;
|
||||
struct devtype *dt;
|
||||
struct subtype *st;
|
||||
|
||||
for (i = 0; (dt = devtypes[i]); i++) {
|
||||
for (j = 0; (st = dt->subtypes[j]); j++) {
|
||||
if (st->namespace != ns)
|
||||
continue;
|
||||
if (subtype_device_exists(st, id, config)) {
|
||||
if (st_ptr)
|
||||
*st_ptr = st;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Mark a namespace as modified. */
|
||||
void namespace_set_modified(struct namespace *target)
|
||||
{
|
||||
struct namespace *ns;
|
||||
int i;
|
||||
|
||||
for (i = 0; (ns = namespaces[i]); i++) {
|
||||
if (ns == target) {
|
||||
ns_modified[i] = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Make all modified blacklists persistent. */
|
||||
exit_code_t namespace_exit(void)
|
||||
{
|
||||
struct namespace *ns, *ns_done;
|
||||
int i, j;
|
||||
exit_code_t rc, drc = EXIT_OK;
|
||||
|
||||
for (i = 0; (ns = namespaces[i]); i++) {
|
||||
if (!ns->blacklist_persist || !ns_modified[i])
|
||||
continue;
|
||||
|
||||
/* In case of shared blacklist persist functions, call function
|
||||
* only once. */
|
||||
for (j = 0; j < i; j++) {
|
||||
ns_done = namespaces[j];
|
||||
if (!ns_modified[j])
|
||||
continue;
|
||||
if (ns->blacklist_persist == ns_done->blacklist_persist)
|
||||
break;
|
||||
}
|
||||
if (j < i)
|
||||
continue;
|
||||
|
||||
rc = ns->blacklist_persist();
|
||||
if (rc && !drc)
|
||||
drc = rc;
|
||||
}
|
||||
|
||||
return drc;
|
||||
}
|
||||
|
||||
/* Return a newly allocated and initialized namespace iterator object. */
|
||||
struct ns_range_iterator *ns_range_iterator_new(void)
|
||||
{
|
||||
return misc_malloc(sizeof(struct ns_range_iterator));
|
||||
}
|
||||
|
||||
/* Release all resources associated the specified namespace iterator object. */
|
||||
void ns_range_iterator_free(struct ns_range_iterator *it)
|
||||
{
|
||||
free(it->devid);
|
||||
free(it->devid_last);
|
||||
free(it->id);
|
||||
free(it);
|
||||
}
|
||||
|
||||
/* Check if the specified @id is valid for the given namespace @ns. */
|
||||
bool ns_is_id_valid(struct namespace *ns, const char *id)
|
||||
{
|
||||
if (ns->is_id_valid(id, err_ignore) == EXIT_OK)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check if the specified @range is valid for the given namespace @ns. */
|
||||
bool ns_is_id_range_valid(struct namespace *ns, const char *range)
|
||||
{
|
||||
if (ns->is_id_range_valid(range, err_ignore) == EXIT_OK)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
110
zdev/src/net.c
Normal file
110
zdev/src/net.c
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <ctype.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "devnode.h"
|
||||
#include "misc.h"
|
||||
#include "net.h"
|
||||
#include "path.h"
|
||||
|
||||
struct add_linked_cb_data {
|
||||
struct util_list *list;
|
||||
const char *prefix;
|
||||
size_t prefix_len;
|
||||
bool result;
|
||||
};
|
||||
|
||||
/* Add a devnode to data->list for each sysfs link that indicates a linked
|
||||
* device. */
|
||||
static exit_code_t add_linked_cb(const char *abs_path, const char *rel_path,
|
||||
void *data)
|
||||
{
|
||||
struct add_linked_cb_data *cb_data = data;
|
||||
const char *name;
|
||||
struct devnode *d;
|
||||
|
||||
if (starts_with(rel_path, cb_data->prefix)) {
|
||||
name = rel_path + cb_data->prefix_len;
|
||||
d = devnode_new(NETDEV, 0, 0, name);
|
||||
ptrlist_add(cb_data->list, d);
|
||||
cb_data->result = true;
|
||||
}
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
/* Add devnodes for all networking devices that are linked to @devnode via
|
||||
* a link starting with @prefix to @list. */
|
||||
static bool add_devnodes_from_link(struct util_list *list,
|
||||
struct devnode *devnode, const char *prefix)
|
||||
{
|
||||
struct add_linked_cb_data cb_data;
|
||||
char *path;
|
||||
|
||||
cb_data.list = list;
|
||||
cb_data.prefix = prefix;
|
||||
cb_data.prefix_len = strlen(prefix);
|
||||
cb_data.result = false;
|
||||
path = path_get_sys_class("net", devnode->name);
|
||||
path_for_each(path, add_linked_cb, &cb_data);
|
||||
free(path);
|
||||
|
||||
return cb_data.result;
|
||||
}
|
||||
|
||||
/* Add devnodes for all devices to @list that are linked as "lower" devices
|
||||
* of network interface @devnode. */
|
||||
bool net_add_linked_devnodes(struct util_list *list, struct devnode *devnode)
|
||||
{
|
||||
return add_devnodes_from_link(list, devnode, "lower_");
|
||||
}
|
||||
|
||||
#define DEVICE_PREFIX "Device:"
|
||||
|
||||
/* If @devnode refers to a vlan device, add a devnode representing its
|
||||
* base device to @list. */
|
||||
bool net_add_vlan_base(struct util_list *list, struct devnode *devnode)
|
||||
{
|
||||
char *path, *text, *name, *end;
|
||||
bool rc = false;
|
||||
|
||||
path = path_get("/proc/net/vlan/%s", devnode->name);
|
||||
text = misc_read_text_file(path, 0, err_ignore);
|
||||
if (!text)
|
||||
goto out;
|
||||
|
||||
name = strstr(text, DEVICE_PREFIX);
|
||||
if (!name)
|
||||
goto out;
|
||||
name += sizeof(DEVICE_PREFIX) - 1;
|
||||
|
||||
for (; *name && isspace(*name); name++) ;
|
||||
for (end = name; *end && !isspace(*end); end++) ;
|
||||
if (name == end)
|
||||
goto out;
|
||||
*end = 0;
|
||||
ptrlist_add(list, devnode_new(NETDEV, 0, 0, name));
|
||||
rc = true;
|
||||
|
||||
out:
|
||||
free(text);
|
||||
free(path);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* If @devnode refers to a bonding device, add a devnode representing its
|
||||
* base device to @list. */
|
||||
bool net_add_bonding_base(struct util_list *list, struct devnode *devnode)
|
||||
{
|
||||
return add_devnodes_from_link(list, devnode, "slave_");
|
||||
}
|
||||
151
zdev/src/nic.c
Normal file
151
zdev/src/nic.c
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "misc.h"
|
||||
#include "nic.h"
|
||||
#include "path.h"
|
||||
|
||||
/* Determine NIC data for the specified NIC. */
|
||||
bool nic_data_get(const char *id, struct nic_data *data_ptr)
|
||||
{
|
||||
struct nic_data data;
|
||||
char *cmd, *vmcp;
|
||||
char **argv = NULL;
|
||||
int argc = 0;
|
||||
bool result = false;
|
||||
|
||||
cmd = misc_asprintf("%s query virtual nic %s 2>/dev/null", PATH_VMCP,
|
||||
id);
|
||||
vmcp = misc_read_cmd_output(cmd, 0, 1);
|
||||
if (!vmcp)
|
||||
goto out;
|
||||
|
||||
line_split(vmcp, &argc, &argv);
|
||||
|
||||
/* Type. */
|
||||
if (argc < 4)
|
||||
goto out;
|
||||
if (strcmp(argv[3], "QDIO") == 0)
|
||||
data.type = nic_qdio;
|
||||
else if (strcmp(argv[3], "HIPERS") == 0)
|
||||
data.type = nic_hipers;
|
||||
else if (strcmp(argv[3], "IEDN") == 0)
|
||||
data.type = nic_iedn;
|
||||
else if (strcmp(argv[3], "INMN") == 0)
|
||||
data.type = nic_inmn;
|
||||
else
|
||||
goto out;
|
||||
|
||||
/* Target. */
|
||||
if (argc < 13)
|
||||
goto out;
|
||||
if (strcmp(argv[10], "VSWITCH:") == 0)
|
||||
data.target = nic_vswitch;
|
||||
else if (strcmp(argv[10], "LAN:") == 0)
|
||||
data.target = nic_lan;
|
||||
else
|
||||
goto out;
|
||||
|
||||
strncpy(data.owner, argv[11], sizeof(data.owner));
|
||||
strncpy(data.name, argv[12], sizeof(data.name));
|
||||
|
||||
result = true;
|
||||
*data_ptr = data;
|
||||
out:
|
||||
line_free(argc, argv);
|
||||
free(vmcp);
|
||||
free(cmd);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Used for debugging. */
|
||||
void nic_data_print(struct nic_data *data, int level)
|
||||
{
|
||||
printf("%*snic_data at %p\n", level, "", (void *) data);
|
||||
level += 2;
|
||||
printf("%*stype=%d\n", level, "", data->type);
|
||||
printf("%*starget=%d\n", level, "", data->target);
|
||||
printf("%*starget owner=%s\n", level, "", data->owner);
|
||||
printf("%*starget name=%s\n", level, "", data->name);
|
||||
}
|
||||
|
||||
/* Determine layer2 setting required for the specified vswitch. */
|
||||
bool nic_vswitch_get_layer2(const char *name, int *layer2)
|
||||
{
|
||||
char *cmd, *vmcp;
|
||||
char **argv = NULL;
|
||||
int argc = 0;
|
||||
bool result = false;
|
||||
|
||||
cmd = misc_asprintf("%s query vswitch %s 2>/dev/null", PATH_VMCP, name);
|
||||
vmcp = misc_read_cmd_output(cmd, 0, 1);
|
||||
if (!vmcp)
|
||||
goto out;
|
||||
|
||||
line_split(vmcp, &argc, &argv);
|
||||
if (argc < 12)
|
||||
goto out;
|
||||
if (strcmp(argv[11], "ETHERNET") == 0)
|
||||
*layer2 = 1;
|
||||
else if (strcmp(argv[11], "NONROUTER") == 0 ||
|
||||
strcmp(argv[11], "PRIROUTER") == 0 ||
|
||||
strcmp(argv[11], "IP") == 0)
|
||||
*layer2 = 0;
|
||||
else
|
||||
goto out;
|
||||
|
||||
result = true;
|
||||
|
||||
out:
|
||||
line_free(argc, argv);
|
||||
free(vmcp);
|
||||
free(cmd);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Determine layer2 setting required for the specified guest lan. */
|
||||
bool nic_lan_get_layer2(const char *name, const char *owner, int *layer2)
|
||||
{
|
||||
char *cmd, *vmcp;
|
||||
char **argv = NULL;
|
||||
int argc = 0;
|
||||
bool result = false;
|
||||
|
||||
if (strcmp(name, "*") == 0)
|
||||
return false;
|
||||
cmd = misc_asprintf("%s query lan %s owner %s 2>/dev/null", PATH_VMCP,
|
||||
name, owner);
|
||||
vmcp = misc_read_cmd_output(cmd, 0, 1);
|
||||
if (!vmcp)
|
||||
goto out;
|
||||
|
||||
line_split(vmcp, &argc, &argv);
|
||||
if (argc < 12)
|
||||
goto out;
|
||||
if (strcmp(argv[11], "ETHERNET") == 0)
|
||||
*layer2 = 1;
|
||||
else if (strcmp(argv[11], "IP") == 0)
|
||||
*layer2 = 0;
|
||||
else
|
||||
goto out;
|
||||
|
||||
result = true;
|
||||
|
||||
out:
|
||||
line_free(argc, argv);
|
||||
free(vmcp);
|
||||
free(cmd);
|
||||
|
||||
return result;
|
||||
}
|
||||
55
zdev/src/opts.c
Normal file
55
zdev/src/opts.c
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <getopt.h>
|
||||
|
||||
#include "misc.h"
|
||||
#include "opts.h"
|
||||
|
||||
static const char *get_name(const struct option *opt_list, int op)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; opt_list[i].name; i++) {
|
||||
if (opt_list[i].val == op)
|
||||
break;
|
||||
}
|
||||
|
||||
return opt_list[i].name;
|
||||
}
|
||||
|
||||
exit_code_t opts_check_conflict(int op, int selected[OPTS_MAX + 1],
|
||||
struct opts_conflict *conf_list,
|
||||
const struct option *opt_list)
|
||||
{
|
||||
int i, j, b;
|
||||
|
||||
for (i = 0; conf_list[i].op; i++) {
|
||||
for (j = 0; conf_list[i].conflicts[j]; j++) {
|
||||
if (conf_list[i].op == op &&
|
||||
selected[conf_list[i].conflicts[j]]) {
|
||||
b = conf_list[i].conflicts[j];
|
||||
goto err;
|
||||
}
|
||||
if (conf_list[i].conflicts[j] == op &&
|
||||
selected[conf_list[i].op]) {
|
||||
b = conf_list[i].op;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return EXIT_OK;
|
||||
|
||||
err:
|
||||
error("Cannot specify '--%s' together with '--%s'\n",
|
||||
get_name(opt_list, op), get_name(opt_list, b));
|
||||
|
||||
return EXIT_USAGE_ERROR;
|
||||
}
|
||||
396
zdev/src/path.c
Normal file
396
zdev/src/path.c
Normal file
@@ -0,0 +1,396 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "devtype.h"
|
||||
#include "misc.h"
|
||||
#include "path.h"
|
||||
#include "zfcp.h"
|
||||
#include "zfcp_lun.h"
|
||||
|
||||
#define PATH_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
|
||||
|
||||
struct base_prefix {
|
||||
char *from;
|
||||
char *to;
|
||||
};
|
||||
|
||||
/* prlist of struct base_prefix sorted descending by prefix length. */
|
||||
static struct util_list *base_prefixes;
|
||||
|
||||
/* Add an entry to the prefix list. */
|
||||
static void prefix_add(const char *key, const char *value)
|
||||
{
|
||||
struct base_prefix *prefix, *curr;
|
||||
size_t len;
|
||||
struct ptrlist_node *p;
|
||||
|
||||
prefix = misc_malloc(sizeof(struct base_prefix));
|
||||
prefix->from = misc_strdup(key);
|
||||
prefix->to = misc_strdup(value);
|
||||
|
||||
/* Add to list according to length. */
|
||||
if (!base_prefixes)
|
||||
base_prefixes = ptrlist_new();
|
||||
len = strlen(key);
|
||||
p = NULL;
|
||||
util_list_iterate(base_prefixes, p) {
|
||||
curr = p->ptr;
|
||||
if (strlen(curr->from) < len)
|
||||
break;
|
||||
}
|
||||
if (p)
|
||||
ptrlist_add_before(base_prefixes, p, prefix);
|
||||
else
|
||||
ptrlist_add(base_prefixes, prefix);
|
||||
}
|
||||
|
||||
/* Initialize the prefix list from a strlist. */
|
||||
void path_set_base(struct util_list *base)
|
||||
{
|
||||
struct strlist_node *s;
|
||||
char *copy, *value;
|
||||
|
||||
if (!base)
|
||||
return;
|
||||
util_list_iterate(base, s) {
|
||||
copy = misc_strdup(s->str);
|
||||
value = strchr(copy, '=');
|
||||
if (value) {
|
||||
*value = 0;
|
||||
value++;
|
||||
prefix_add(copy, value);
|
||||
} else {
|
||||
prefix_add("", copy);
|
||||
}
|
||||
free(copy);
|
||||
}
|
||||
}
|
||||
|
||||
/* Release the prefix list. */
|
||||
void path_exit(void)
|
||||
{
|
||||
struct ptrlist_node *p, *n;
|
||||
struct base_prefix *prefix;
|
||||
|
||||
if (!base_prefixes)
|
||||
return;
|
||||
util_list_iterate_safe(base_prefixes, p, n) {
|
||||
util_list_remove(base_prefixes, p);
|
||||
prefix = p->ptr;
|
||||
free(prefix->from);
|
||||
free(prefix->to);
|
||||
free(prefix);
|
||||
free(p);
|
||||
}
|
||||
free(base_prefixes);
|
||||
base_prefixes = NULL;
|
||||
}
|
||||
|
||||
/* Modify @path according to specified prefix conversion. */
|
||||
static void apply_base(char **path)
|
||||
{
|
||||
struct ptrlist_node *p;
|
||||
struct base_prefix *prefix;
|
||||
char *new_path;
|
||||
|
||||
if (!base_prefixes)
|
||||
return;
|
||||
util_list_iterate(base_prefixes, p) {
|
||||
prefix = p->ptr;
|
||||
if (!starts_with(*path, prefix->from))
|
||||
continue;
|
||||
|
||||
new_path = misc_asprintf("%s%s", prefix->to,
|
||||
*path + strlen(prefix->from));
|
||||
free(*path);
|
||||
*path = new_path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return a path that is created by resolving the specified format string
|
||||
* @fmt and applying any base prefixes. */
|
||||
char *path_get(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
char *path;
|
||||
|
||||
/* Get original path. */
|
||||
va_start(args, fmt);
|
||||
if (vasprintf(&path, fmt, args) == -1)
|
||||
oom();
|
||||
va_end(args);
|
||||
|
||||
/* Apply base prefix if necessary. */
|
||||
apply_base(&path);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/* Create all directories leading up to path. */
|
||||
exit_code_t path_create(const char *path)
|
||||
{
|
||||
char *copy, *curr, *next;
|
||||
struct stat s;
|
||||
int rc;
|
||||
|
||||
copy = misc_strdup(path);
|
||||
curr = (*copy == '/') ? copy + 1 : copy;
|
||||
curr = strchr(curr, '/');
|
||||
if (!curr) {
|
||||
free(copy);
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
do {
|
||||
next = strchr(curr + 1, '/');
|
||||
|
||||
*curr = 0;
|
||||
/* Ensure sub-path exists and is a directory. */
|
||||
rc = stat(copy, &s);
|
||||
if (rc == -1 && errno == EACCES)
|
||||
goto err_access;
|
||||
if (rc == 0 && !S_ISDIR(s.st_mode))
|
||||
goto err_file;
|
||||
if (rc == -1) {
|
||||
/* Create directory. */
|
||||
rc = mkdir(copy, PATH_MODE);
|
||||
if (rc)
|
||||
goto err_mkdir;
|
||||
}
|
||||
|
||||
*curr = '/';
|
||||
curr = next;
|
||||
} while (curr);
|
||||
|
||||
free(copy);
|
||||
|
||||
return EXIT_OK;
|
||||
|
||||
err_access:
|
||||
error("Could not access '%s'\n", copy);
|
||||
free(copy);
|
||||
return EXIT_RUNTIME_ERROR;
|
||||
|
||||
err_file:
|
||||
error("Non-directory found in path '%s': %s\n", path, copy);
|
||||
free(copy);
|
||||
return EXIT_RUNTIME_ERROR;
|
||||
|
||||
err_mkdir:
|
||||
error("Could not create directory '%s': %s\n", copy, strerror(errno));
|
||||
free(copy);
|
||||
return EXIT_RUNTIME_ERROR;
|
||||
}
|
||||
|
||||
/* Return path to modprobe.conf file for the specified device type. */
|
||||
char *path_get_modprobe_conf(struct devtype *dt)
|
||||
{
|
||||
return path_get("%s/%s-%s.conf", PATH_MODPROBE_CONF, MODPROBE_PREFIX,
|
||||
dt->name);
|
||||
}
|
||||
|
||||
/* Return sysfs path to module directory. */
|
||||
char *path_get_sys_module(const char *mod)
|
||||
{
|
||||
return path_get("/sys/module/%s", mod);
|
||||
}
|
||||
|
||||
/* Return sysfs path to module parameter file. */
|
||||
char *path_get_sys_module_param(const char *mod, const char *name)
|
||||
{
|
||||
if (name)
|
||||
return path_get("/sys/module/%s/parameters/%s", mod, name);
|
||||
|
||||
return path_get("/sys/module/%s/parameters", mod);
|
||||
}
|
||||
|
||||
/* Return sysfs path to block device dev file. */
|
||||
char *path_get_sys_block_dev(const char *name)
|
||||
{
|
||||
return path_get("/sys/block/%s/dev", name);
|
||||
}
|
||||
|
||||
/* Return sysfs path to /sys/dev/block/major:minor directory. */
|
||||
char *path_get_sys_dev_block(unsigned int major, unsigned int minor)
|
||||
{
|
||||
return path_get("/sys/dev/block/%d:%d", major, minor);
|
||||
}
|
||||
|
||||
/* Return sysfs path to /sys/dev/char/major:minor directory. */
|
||||
char *path_get_sys_dev_char(unsigned int major, unsigned int minor)
|
||||
{
|
||||
return path_get("/sys/dev/char/%d:%d", major, minor);
|
||||
}
|
||||
|
||||
/* Return sysfs path to /sys/dev/char directory. */
|
||||
char *path_get_sys_dev_char_devices(void)
|
||||
{
|
||||
return path_get("/sys/dev/char");
|
||||
}
|
||||
|
||||
/* Return sysfs path to class directory. */
|
||||
char *path_get_sys_class(const char *class, const char *name)
|
||||
{
|
||||
if (name)
|
||||
return path_get("/sys/class/%s/%s", class, name);
|
||||
|
||||
return path_get("/sys/class/%s", class);
|
||||
}
|
||||
|
||||
/* Return path to modprobe executable. */
|
||||
char *path_get_modprobe(void)
|
||||
{
|
||||
return path_get("%s", PATH_MODPROBE);
|
||||
}
|
||||
|
||||
/* Return sysfs path to CCW device. */
|
||||
char *path_get_ccw_device(const char *drv, const char *id)
|
||||
{
|
||||
if (drv)
|
||||
return path_get("%s/drivers/%s/%s", PATH_CCW_BUS, drv, id);
|
||||
|
||||
return path_get("%s/devices/%s", PATH_CCW_BUS, id);
|
||||
}
|
||||
|
||||
/* Return sysfs path to directory containing all CCW devices. */
|
||||
char *path_get_ccw_devices(const char *drv)
|
||||
{
|
||||
if (drv)
|
||||
return path_get("%s/drivers/%s/", PATH_CCW_BUS, drv);
|
||||
|
||||
return path_get("%s/devices/", PATH_CCW_BUS);
|
||||
}
|
||||
|
||||
/* Return sysfs path to CCWGROUP device. */
|
||||
char *path_get_ccwgroup_device(const char *drv, const char *id)
|
||||
{
|
||||
if (drv)
|
||||
return path_get("%s/drivers/%s/%s", PATH_CCWGROUP_BUS, drv, id);
|
||||
|
||||
return path_get("%s/devices/%s", PATH_CCWGROUP_BUS, id);
|
||||
}
|
||||
|
||||
/* Return sysfs path to directory containing all CCWGROUP devices. */
|
||||
char *path_get_ccwgroup_devices(const char *drv)
|
||||
{
|
||||
if (drv)
|
||||
return path_get("%s/drivers/%s/", PATH_CCWGROUP_BUS, drv);
|
||||
|
||||
return path_get("%s/devices/", PATH_CCWGROUP_BUS);
|
||||
}
|
||||
|
||||
/* Return path to udev rule. */
|
||||
char *path_get_udev_rule(const char *type, const char *id)
|
||||
{
|
||||
if (id) {
|
||||
return path_get("%s/%s-%s-%s%s", PATH_UDEV_RULES,
|
||||
UDEV_PREFIX, type, id, UDEV_SUFFIX);
|
||||
}
|
||||
|
||||
return path_get("%s/%s-%s%s", PATH_UDEV_RULES,
|
||||
UDEV_PREFIX, type, UDEV_SUFFIX);
|
||||
}
|
||||
|
||||
/* Return path to directory containing all udev rules. */
|
||||
char *path_get_udev_rules(void)
|
||||
{
|
||||
return path_get("%s", PATH_UDEV_RULES);
|
||||
}
|
||||
|
||||
/* Return path to the specified file in the proc file system. */
|
||||
char *path_get_proc(const char *filename)
|
||||
{
|
||||
return path_get("%s/%s", PATH_PROC, filename);
|
||||
}
|
||||
|
||||
/* Call a function for each entry in a directory:
|
||||
* exit_code_t callback(const char *abs_path, const char *rel_path, void *data)
|
||||
* Aborts when callback returns any value other than EXIT_OK.
|
||||
*/
|
||||
exit_code_t path_for_each(const char *path,
|
||||
exit_code_t (*callback)(const char *, const char *,
|
||||
void *), void *data)
|
||||
{
|
||||
DIR *dir;
|
||||
struct dirent *de;
|
||||
char *p;
|
||||
exit_code_t rc = EXIT_OK;
|
||||
|
||||
dir = opendir(path);
|
||||
if (!dir) {
|
||||
warn("Could not open directory %s: %s\n", path,
|
||||
strerror(errno));
|
||||
return EXIT_RUNTIME_ERROR;
|
||||
}
|
||||
while (rc == EXIT_OK && (de = readdir(dir))) {
|
||||
if (strcmp(de->d_name, ".") == 0 ||
|
||||
strcmp(de->d_name, "..") == 0)
|
||||
continue;
|
||||
p = path_get("%s/%s", path, de->d_name);
|
||||
rc = callback(p, de->d_name, data);
|
||||
free(p);
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Return sysfs path to device or devices directory. */
|
||||
char *path_get_sys_bus_dev(const char *bus, const char *id)
|
||||
{
|
||||
if (!id)
|
||||
return path_get("/sys/bus/%s/devices", bus);
|
||||
|
||||
return path_get("/sys/bus/%s/devices/%s", bus, id);
|
||||
}
|
||||
|
||||
/* Return sysfs path to scsi drivers directory. */
|
||||
char *path_get_sys_bus_drv(const char *bus, const char *drv)
|
||||
{
|
||||
if (!drv)
|
||||
return path_get("/sys/bus/%s/drivers", bus);
|
||||
|
||||
return path_get("/sys/bus/%s/drivers/%s", bus, drv);
|
||||
}
|
||||
|
||||
/* Return sysfs path to zFCP LUN directory. */
|
||||
char *path_get_zfcp_lun_dev(struct zfcp_lun_devid *id)
|
||||
{
|
||||
return path_get("%s/drivers/%s/%x.%x.%04x/0x%016" PRIx64
|
||||
"/0x%016" PRIx64, PATH_CCW_BUS,
|
||||
ZFCP_CCWDRV_NAME, id->fcp_dev.cssid,
|
||||
id->fcp_dev.ssid, id->fcp_dev.devno, id->wwpn, id->lun);
|
||||
}
|
||||
|
||||
/* Return sysfs path to zFCP target port directory. */
|
||||
char *path_get_zfcp_port_dev(struct zfcp_lun_devid *id)
|
||||
{
|
||||
return path_get("%s/drivers/%s/%x.%x.%04x/0x%016" PRIx64,
|
||||
PATH_CCW_BUS, ZFCP_CCWDRV_NAME,
|
||||
id->fcp_dev.cssid, id->fcp_dev.ssid,
|
||||
id->fcp_dev.devno, id->wwpn);
|
||||
}
|
||||
|
||||
/* Return sysfs path to SCSI device directory. */
|
||||
char *path_get_scsi_hctl_dev(const char *hctl)
|
||||
{
|
||||
return path_get("/sys/bus/scsi/devices/%s", hctl);
|
||||
}
|
||||
1262
zdev/src/qeth.c
Normal file
1262
zdev/src/qeth.c
Normal file
File diff suppressed because it is too large
Load Diff
482
zdev/src/qeth_auto.c
Normal file
482
zdev/src/qeth_auto.c
Normal file
@@ -0,0 +1,482 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ccw.h"
|
||||
#include "ccwgroup.h"
|
||||
#include "device.h"
|
||||
#include "misc.h"
|
||||
#include "path.h"
|
||||
#include "qeth.h"
|
||||
#include "qeth_auto.h"
|
||||
|
||||
/*
|
||||
* QETH autodetection
|
||||
*
|
||||
* A QETH device must be grouped before it can be used. The following
|
||||
* rules apply to grouping:
|
||||
*
|
||||
* 1. A QETH device can be grouped from 3 CCW devices
|
||||
* a) Read device
|
||||
* b) Write device
|
||||
* c) Data device
|
||||
* 2. All CCW devices must be bound to the QETH CCW device driver
|
||||
* 3. The subchannel of all CCW devices must be defined with the same
|
||||
* CHPID
|
||||
* 4. The CCW devices must have the same CUTYPE
|
||||
* 5. The CCW devices must have the same DEVTYPE
|
||||
* 6. The write device ID must be the read device ID plus one
|
||||
* 7. None of the CCW devices is part of an existing CCWGROUP device
|
||||
*/
|
||||
|
||||
/* Compare by: 1. CHPID, 2. CUTYPE, 3. DEVTYPE, 4. CCW device ID
|
||||
* -1 = a < b 1 = a > b 0 = a == b. */
|
||||
static int info_cmp(void *a, void *b, void *data)
|
||||
{
|
||||
struct ptrlist_node *pa = a, *pb = b;
|
||||
struct ccw_devinfo *ia = pa->ptr, *ib = pb->ptr;
|
||||
int r;
|
||||
|
||||
r = ccw_devinfo_chpids_cmp(ia, ib);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
r = ccw_devinfo_cutype_cmp(ia, ib);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
r = ccw_devinfo_devtype_cmp(ia, ib);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return ccw_cmp_devids(&ia->devid, &ib->devid);
|
||||
}
|
||||
|
||||
static bool is_compatible(struct ccw_devinfo *a, struct ccw_devinfo *b)
|
||||
{
|
||||
if (ccw_devinfo_chpids_cmp(a, b) == 0 &&
|
||||
ccw_devinfo_cutype_cmp(a, b) == 0 &&
|
||||
ccw_devinfo_devtype_cmp(a, b) == 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return the number of consecutive ptrlist_nodes pointing to ccw_devinfos
|
||||
* which are compatible with each other. */
|
||||
static unsigned int count_compatible(struct util_list *infos,
|
||||
struct ptrlist_node *start)
|
||||
{
|
||||
struct ptrlist_node *curr;
|
||||
unsigned int num;
|
||||
|
||||
num = 0;
|
||||
for (curr = start; curr && is_compatible(start->ptr, curr->ptr);
|
||||
curr = util_list_next(infos, curr))
|
||||
num++;
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
/* Check if sequential fill is possible without holes. */
|
||||
static bool check_seq(struct util_list *infos, struct ptrlist_node *start,
|
||||
unsigned int num)
|
||||
{
|
||||
struct ptrlist_node *curr;
|
||||
struct ccw_devinfo *read = NULL, *write = NULL;
|
||||
unsigned int i;
|
||||
|
||||
curr = start;
|
||||
for (i = 0; i < num; i++) {
|
||||
if (write) {
|
||||
/* Expect data. */
|
||||
read = NULL;
|
||||
write = NULL;
|
||||
} else if (read) {
|
||||
/* Expect write. */
|
||||
write = curr->ptr;
|
||||
if (ccw_devid_distance(&read->devid,
|
||||
&write->devid) != 1)
|
||||
return false;
|
||||
} else {
|
||||
/* Expect read. */
|
||||
read = curr->ptr;
|
||||
}
|
||||
curr = util_list_next(infos, curr);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void add_ccwgroup_devid(struct util_list *devids, struct ccw_devid *read,
|
||||
struct ccw_devid *write, struct ccw_devid *data)
|
||||
{
|
||||
struct ccwgroup_devid devid;
|
||||
|
||||
devid.devid[0] = *read;
|
||||
devid.devid[1] = *write;
|
||||
devid.devid[2] = *data;
|
||||
devid.num = QETH_NUM_DEVS;
|
||||
|
||||
ptrlist_add(devids, ccwgroup_copy_devid(&devid));
|
||||
}
|
||||
|
||||
static struct ptrlist_node *add_seq(struct util_list *devids,
|
||||
struct util_list *infos,
|
||||
struct ptrlist_node *start,
|
||||
unsigned int num)
|
||||
{
|
||||
struct ptrlist_node *curr;
|
||||
struct ccw_devinfo *read = NULL, *write = NULL, *data;
|
||||
unsigned int i;
|
||||
|
||||
curr = start;
|
||||
for (i = 0; i < num; i++) {
|
||||
if (write) {
|
||||
/* Expect data. */
|
||||
data = curr->ptr;
|
||||
add_ccwgroup_devid(devids, &read->devid, &write->devid,
|
||||
&data->devid);
|
||||
read = NULL;
|
||||
write = NULL;
|
||||
data = NULL;
|
||||
} else if (read) {
|
||||
/* Expect write. */
|
||||
write = curr->ptr;
|
||||
} else {
|
||||
/* Expect read. */
|
||||
read = curr->ptr;
|
||||
}
|
||||
curr = util_list_next(infos, curr);
|
||||
}
|
||||
|
||||
return curr;
|
||||
}
|
||||
|
||||
/* Create IDs by searching for consecutive pairs of CCW device IDs first. */
|
||||
static struct ptrlist_node *add_pairs_first(struct util_list *devids,
|
||||
struct util_list *infos,
|
||||
struct ptrlist_node *start,
|
||||
unsigned int num)
|
||||
{
|
||||
struct util_list *pairs, *all;
|
||||
struct ptrlist_node *curr, *next, *read, *data, *cont;
|
||||
struct ccw_devinfo *r, *w, *d;
|
||||
unsigned int i, max_pairs, num_pairs;
|
||||
|
||||
all = ptrlist_new();
|
||||
pairs = ptrlist_new();
|
||||
|
||||
/* Copy devinfos to all list. */
|
||||
curr = start;
|
||||
for (i = 0; i < num; i++) {
|
||||
ptrlist_add(all, curr->ptr);
|
||||
curr = util_list_next(infos, curr);
|
||||
}
|
||||
cont = curr;
|
||||
|
||||
/* Move valid read-write pairs to pairs list. */
|
||||
max_pairs = num / QETH_NUM_DEVS;
|
||||
num_pairs = 0;
|
||||
read = NULL;
|
||||
util_list_iterate_safe(all, curr, next) {
|
||||
if (read) {
|
||||
r = read->ptr;
|
||||
w = curr->ptr;
|
||||
/* Check for write = read + 1. */
|
||||
if (ccw_devid_distance(&r->devid, &w->devid) == 1) {
|
||||
ptrlist_move(pairs, all, read);
|
||||
ptrlist_move(pairs, all, curr);
|
||||
num_pairs++;
|
||||
if (num_pairs >= max_pairs)
|
||||
break;
|
||||
read = NULL;
|
||||
} else
|
||||
read = curr;
|
||||
} else
|
||||
read = curr;
|
||||
}
|
||||
|
||||
/* Create full groups by combining pairs + remaining IDs. */
|
||||
read = NULL;
|
||||
util_list_iterate(pairs, curr) {
|
||||
if (read) {
|
||||
/* Got read and write - check for data. */
|
||||
data = util_list_start(all);
|
||||
if (!data)
|
||||
break;
|
||||
util_list_remove(all, data);
|
||||
r = read->ptr;
|
||||
w = curr->ptr;
|
||||
d = data->ptr;
|
||||
add_ccwgroup_devid(devids, &r->devid, &w->devid,
|
||||
&d->devid);
|
||||
read = NULL;
|
||||
} else
|
||||
read = curr;
|
||||
}
|
||||
|
||||
ptrlist_free(all, 0);
|
||||
ptrlist_free(pairs, 0);
|
||||
|
||||
return cont;
|
||||
}
|
||||
|
||||
static struct ptrlist_node *add_groups(struct util_list *devids,
|
||||
struct util_list *infos,
|
||||
struct ptrlist_node *start,
|
||||
unsigned int num)
|
||||
{
|
||||
struct ptrlist_node *next;
|
||||
|
||||
if (check_seq(infos, start, num))
|
||||
next = add_seq(devids, infos, start, num);
|
||||
else
|
||||
next = add_pairs_first(devids, infos, start, num);
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
/* Add device info for all QETH CCW devices to ptrlist in data. */
|
||||
static exit_code_t add_cb(const char *path, const char *filename, void *data)
|
||||
{
|
||||
struct ccw_devid devid;
|
||||
struct util_list *infos = data;
|
||||
struct ccw_devinfo *devinfo;
|
||||
|
||||
if (!strchr(filename, '.'))
|
||||
return EXIT_OK;
|
||||
if (ccw_parse_devid(&devid, filename, err_ignore) != EXIT_OK)
|
||||
return EXIT_OK;
|
||||
devinfo = ccw_devinfo_get(&devid, 0);
|
||||
if (devinfo->exists && !devinfo->grouped)
|
||||
ptrlist_add(infos, devinfo);
|
||||
else
|
||||
free(devinfo);
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
/* Return a sorted ptrlist of struct ccw_devinfos for all CCW devices
|
||||
* bound to the qeth CCW device driver. The result must be freed using
|
||||
* ptrlist_free(,1); */
|
||||
static struct util_list *read_sorted_qeth_devinfos(void)
|
||||
{
|
||||
struct util_list *infos;
|
||||
char *path;
|
||||
|
||||
/* Get CHPID information for all devices bound to the QETH driver. */
|
||||
infos = ptrlist_new();
|
||||
path = path_get_sys_bus_drv(CCW_BUS_NAME, QETH_CCWDRV_NAME);
|
||||
if (dir_exists(path))
|
||||
path_for_each(path, add_cb, infos);
|
||||
free(path);
|
||||
|
||||
/* For each CHPID: Find groups. Add to result list. */
|
||||
util_list_sort(infos, info_cmp, NULL);
|
||||
|
||||
return infos;
|
||||
}
|
||||
|
||||
static void add_groupable_devids(struct util_list *devids,
|
||||
struct util_list *infos)
|
||||
{
|
||||
struct ptrlist_node *curr;
|
||||
unsigned int num;
|
||||
|
||||
/* For each group of compatible devices: Find valid CCWGROUPs.
|
||||
* Add to result list. */
|
||||
curr = util_list_start(infos);
|
||||
while (curr) {
|
||||
num = count_compatible(infos, curr);
|
||||
curr = add_groups(devids, infos, curr, num);
|
||||
}
|
||||
}
|
||||
|
||||
/* Add CCWGROUP IDs of qeth devices that can be grouped to strlist IDs.*/
|
||||
void qeth_auto_add_ids(struct util_list *ids)
|
||||
{
|
||||
struct util_list *infos, *devids;
|
||||
struct ptrlist_node *p;
|
||||
char *id;
|
||||
|
||||
infos = read_sorted_qeth_devinfos();
|
||||
devids = ptrlist_new();
|
||||
add_groupable_devids(devids, infos);
|
||||
|
||||
util_list_iterate(devids, p) {
|
||||
id = ccwgroup_devid_to_str(p->ptr);
|
||||
strlist_add(ids, id);
|
||||
free(id);
|
||||
}
|
||||
|
||||
ptrlist_free(devids, 1);
|
||||
ptrlist_free(infos, 1);
|
||||
}
|
||||
|
||||
/* Determine the CCWGROUP ID of the qeth device that can be grouped with the
|
||||
* specified CCW device ID as first ID. */
|
||||
exit_code_t qeth_auto_get_devid(struct ccwgroup_devid *devid_ptr,
|
||||
struct ccw_devid *ccw_devid, err_t err)
|
||||
{
|
||||
struct util_list *devids, *infos;
|
||||
struct ptrlist_node *p, *read, *write, *data;
|
||||
struct ccw_devinfo *r, *w, *d;
|
||||
struct ccwgroup_devid *devid;
|
||||
exit_code_t rc;
|
||||
|
||||
infos = read_sorted_qeth_devinfos();
|
||||
|
||||
/* Try to find an ID from the canonical auto-generated list. */
|
||||
devids = ptrlist_new();
|
||||
add_groupable_devids(devids, infos);
|
||||
|
||||
util_list_iterate(devids, p) {
|
||||
devid = p->ptr;
|
||||
if (ccw_cmp_devids(ccw_devid, &devid->devid[0]) != 0)
|
||||
continue;
|
||||
rc = EXIT_OK;
|
||||
if (devid_ptr)
|
||||
*devid_ptr = *devid;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Try to create a CCWGROUP ID with the specified ID as read device. */
|
||||
|
||||
/* Get CCW device info for read device. */
|
||||
util_list_iterate(infos, read) {
|
||||
r = read->ptr;
|
||||
if (ccw_cmp_devids(&r->devid, ccw_devid) == 0)
|
||||
break;
|
||||
}
|
||||
if (!read) {
|
||||
err_t_print(err, "Read CCW device not found\n");
|
||||
rc = EXIT_GROUP_NOT_FOUND;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get CCW device ID for write device. */
|
||||
write = util_list_next(infos, read);
|
||||
if (!write) {
|
||||
err_t_print(err, "Write CCW device not found\n");
|
||||
rc = EXIT_GROUP_NOT_FOUND;
|
||||
goto out;
|
||||
}
|
||||
w = write->ptr;
|
||||
if (!is_compatible(r, w)) {
|
||||
err_t_print(err, "No compatible write CCW device found\n");
|
||||
rc = EXIT_GROUP_INVALID;
|
||||
goto out;
|
||||
}
|
||||
if (ccw_devid_distance(&r->devid, &w->devid) != 1) {
|
||||
err_t_print(err, "Write CCW device ID must be read plus one\n");
|
||||
rc = EXIT_GROUP_INVALID;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get CCW device ID for data device. */
|
||||
|
||||
/* 1. Prefer ID after the write ID. */
|
||||
data = util_list_next(infos, write);
|
||||
if (!data || !is_compatible(r, data->ptr)) {
|
||||
/* Try ID before read ID. */
|
||||
data = util_list_prev(infos, read);
|
||||
}
|
||||
if (!data) {
|
||||
err_t_print(err, "Data CCW device not found\n");
|
||||
rc = EXIT_GROUP_NOT_FOUND;
|
||||
goto out;
|
||||
}
|
||||
if (!is_compatible(r, data->ptr)) {
|
||||
err_t_print(err, "No compatible data CCW device found\n");
|
||||
rc = EXIT_GROUP_INVALID;
|
||||
goto out;
|
||||
}
|
||||
d = data->ptr;
|
||||
|
||||
rc = EXIT_OK;
|
||||
if (devid_ptr) {
|
||||
devid_ptr->devid[0] = r->devid;
|
||||
devid_ptr->devid[1] = w->devid;
|
||||
devid_ptr->devid[2] = d->devid;
|
||||
devid_ptr->num = QETH_NUM_DEVS;
|
||||
}
|
||||
|
||||
out:
|
||||
ptrlist_free(devids, 1);
|
||||
ptrlist_free(infos, 1);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Check if the specified QETH device can be grouped .*/
|
||||
exit_code_t qeth_auto_is_possible(struct ccwgroup_devid *devid, err_t err)
|
||||
{
|
||||
struct ccw_devinfo *info[QETH_NUM_DEVS];
|
||||
unsigned int i;
|
||||
char *ccwid;
|
||||
const char *msg;
|
||||
exit_code_t rc;
|
||||
|
||||
if (devid->num < QETH_NUM_DEVS) {
|
||||
err_t_print(err, "Not enough CCW device IDs in QETH device "
|
||||
"ID\n");
|
||||
return EXIT_INCOMPLETE_ID;
|
||||
}
|
||||
if (devid->num > QETH_NUM_DEVS) {
|
||||
err_t_print(err, "QETH device ID contains too many CCW device "
|
||||
"IDs\n");
|
||||
return EXIT_INVALID_ID;
|
||||
}
|
||||
if (ccw_devid_distance(&devid->devid[0], &devid->devid[1]) != 1) {
|
||||
err_t_print(err, "Write device ID must be read plus one\n");
|
||||
return EXIT_GROUP_INVALID;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(info); i++)
|
||||
info[i] = ccw_devinfo_get(&devid->devid[i], 1);
|
||||
|
||||
rc = EXIT_OK;
|
||||
msg = NULL;
|
||||
for (i = 0; i < ARRAY_SIZE(info); i++) {
|
||||
if (!info[i]->exists) {
|
||||
msg = "CCW device %s does not exist\n";
|
||||
rc = EXIT_GROUP_NOT_FOUND;
|
||||
} else if (info[i]->grouped) {
|
||||
msg = "CCW device %s is in another group device\n";
|
||||
rc = EXIT_GROUP_ALREADY;
|
||||
} else if (i > 0 &&
|
||||
ccw_devinfo_chpids_cmp(info[i - 1], info[i]) != 0) {
|
||||
msg = "CCW device %s is not on the same CHPID\n";
|
||||
rc = EXIT_GROUP_INVALID;
|
||||
} else if (i > 0 &&
|
||||
ccw_devinfo_cutype_cmp(info[i - 1], info[i]) != 0) {
|
||||
msg = "CUTYPE of CCW device %s differs\n";
|
||||
rc = EXIT_GROUP_INVALID;
|
||||
} else if (i > 0 &&
|
||||
ccw_devinfo_devtype_cmp(info[i - 1], info[i]) != 0) {
|
||||
msg = "DEVTYPE of CCW device %s differs\n";
|
||||
rc = EXIT_GROUP_INVALID;
|
||||
}
|
||||
if (!msg)
|
||||
continue;
|
||||
ccwid = ccw_devid_to_str(&devid->devid[i]);
|
||||
err_t_print(err, msg, ccwid);
|
||||
free(ccwid);
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(info); i++)
|
||||
free(info[i]);
|
||||
|
||||
return rc;
|
||||
}
|
||||
104
zdev/src/root.c
Normal file
104
zdev/src/root.c
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
|
||||
#include "device.h"
|
||||
#include "devtype.h"
|
||||
#include "misc.h"
|
||||
#include "path.h"
|
||||
#include "root.h"
|
||||
#include "select.h"
|
||||
#include "setting.h"
|
||||
#include "subtype.h"
|
||||
|
||||
/* Determine if the root device was modified. If it was modified, run the
|
||||
* corresponding root-install scripts. */
|
||||
exit_code_t root_check(void)
|
||||
{
|
||||
struct util_list *selected, *params, *mod = NULL;
|
||||
struct selected_dev_node *sel;
|
||||
struct device *dev;
|
||||
char *params_str;
|
||||
exit_code_t rc;
|
||||
struct strlist_node *s;
|
||||
struct devtype *dt;
|
||||
|
||||
debug("Checking for modified root device configuration\n");
|
||||
|
||||
/* Get list of devices that provide the root device. */
|
||||
selected = selected_dev_list_new();
|
||||
rc = select_by_path(NULL, selected, config_active, scope_mandatory,
|
||||
NULL, NULL, PATH_ROOT, err_print);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
/* Determine if any of the devices or device types has been modified. */
|
||||
mod = strlist_new();
|
||||
util_list_iterate(selected, sel) {
|
||||
dt = sel->st->devtype;
|
||||
|
||||
/* Check devtype. */
|
||||
if (devtype_needs_writing(dt, config_persistent)) {
|
||||
strlist_add(mod, "Device type %s",
|
||||
sel->st->devtype->name);
|
||||
}
|
||||
|
||||
/* Check devices. */
|
||||
dev = device_list_find(sel->st->devices, sel->id, NULL);
|
||||
if (dev && dev->persistent.exists &&
|
||||
device_needs_writing(dev, config_persistent)) {
|
||||
strlist_add(mod, "%s %s", dev->subtype->devname,
|
||||
dev->id);
|
||||
}
|
||||
}
|
||||
|
||||
if (util_list_is_empty(mod))
|
||||
goto out;
|
||||
info("Note: Some of the changes affect devices providing the root "
|
||||
"file system:\n");
|
||||
util_list_iterate(mod, s)
|
||||
info(" - %s\n", s->str);
|
||||
info(" Additional steps such as rebuilding the RAM-disk might be "
|
||||
"required.\n");
|
||||
|
||||
/* Check if script is available. */
|
||||
if (!file_exists(PATH_ROOT_SCRIPT))
|
||||
goto out;
|
||||
|
||||
/* Ask for confirmation. */
|
||||
if (!confirm("Update persistent root device configuration now?")) {
|
||||
rc = EXIT_ABORTED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Build the command line. */
|
||||
params = strlist_new();
|
||||
util_list_iterate(selected, sel) {
|
||||
strlist_add(params, "%s", sel->st->name);
|
||||
strlist_add(params, "%s", sel->id);
|
||||
}
|
||||
params_str = strlist_flatten(params, " ");
|
||||
strlist_free(params);
|
||||
|
||||
/* Run update command. */
|
||||
if (misc_system(err_delayed_print, "%s %s", PATH_ROOT_SCRIPT,
|
||||
params_str) != 0) {
|
||||
error("Failure while updating root device configuration\n");
|
||||
delayed_print(DELAY_INDENT);
|
||||
rc = EXIT_RUNTIME_ERROR;
|
||||
}
|
||||
free(params_str);
|
||||
|
||||
out:
|
||||
strlist_free(mod);
|
||||
selected_dev_list_free(selected);
|
||||
|
||||
return rc;
|
||||
}
|
||||
362
zdev/src/scsi.c
Normal file
362
zdev/src/scsi.c
Normal file
@@ -0,0 +1,362 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "misc.h"
|
||||
#include "path.h"
|
||||
#include "scsi.h"
|
||||
#include "zfcp_lun.h"
|
||||
|
||||
struct scsi_hctl_devid {
|
||||
unsigned int host;
|
||||
unsigned int channel;
|
||||
unsigned int target;
|
||||
uint64_t lun;
|
||||
};
|
||||
|
||||
struct scsi_zfcp {
|
||||
struct scsi_hctl_devid scsi;
|
||||
struct zfcp_lun_devid zfcp;
|
||||
};
|
||||
|
||||
static struct util_list *scsi_zfcp_list;
|
||||
|
||||
static char *scsi_hctl_devid_to_str(struct scsi_hctl_devid *devid)
|
||||
{
|
||||
return misc_asprintf("%u:%u:%u:%" PRIu64, devid->host, devid->channel,
|
||||
devid->target, devid->lun);
|
||||
}
|
||||
|
||||
/* Used for debugging. */
|
||||
void scsi_zfcp_print(struct scsi_zfcp *s, int i)
|
||||
{
|
||||
char *scsi, *zfcp;
|
||||
|
||||
indent(i, "scsi_zfcp at %p:\n", (void *) s);
|
||||
i += 2;
|
||||
scsi = scsi_hctl_devid_to_str(&s->scsi);
|
||||
zfcp = zfcp_lun_devid_to_str(&s->zfcp);
|
||||
indent(i, "scsi=%s\n", scsi);
|
||||
indent(i, "zfcp=%s\n", zfcp);
|
||||
free(zfcp);
|
||||
free(scsi);
|
||||
}
|
||||
|
||||
void scsi_reread(void)
|
||||
{
|
||||
ptrlist_free(scsi_zfcp_list, 1);
|
||||
scsi_zfcp_list = NULL;
|
||||
}
|
||||
|
||||
void scsi_exit(void)
|
||||
{
|
||||
ptrlist_free(scsi_zfcp_list, 1);
|
||||
}
|
||||
|
||||
static bool scsi_hctl_parse_devid(struct scsi_hctl_devid *id, const char *str)
|
||||
{
|
||||
unsigned int host, channel, target;
|
||||
uint64_t lun;
|
||||
char dummy;
|
||||
|
||||
if (sscanf(str, "%u:%u:%u:%" SCNu64 " %c", &host, &channel, &target,
|
||||
&lun, &dummy) != 4)
|
||||
return false;
|
||||
|
||||
if (id) {
|
||||
id->host = host;
|
||||
id->channel = channel;
|
||||
id->target = target;
|
||||
id->lun = lun;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Retrieve CCW device ID of HBA for specified SCSI device path. */
|
||||
static char *devpath_to_hba_id(const char *path)
|
||||
{
|
||||
char *copy, *start, *end, *hba_id = NULL;
|
||||
|
||||
copy = misc_strdup(path);
|
||||
|
||||
/* copy=/devices/css0/0.0.001c/0.0.1940/host0/... */
|
||||
end = strstr(copy, "/host");
|
||||
if (!end)
|
||||
goto out;
|
||||
*end = 0;
|
||||
/* copy=/devices/css0/0.0.001c/0.0.1940 */
|
||||
start = strrchr(copy, '/');
|
||||
if (!start)
|
||||
goto out;
|
||||
start++;
|
||||
hba_id = misc_strdup(start);
|
||||
|
||||
out:
|
||||
free(copy);
|
||||
|
||||
return hba_id;
|
||||
}
|
||||
|
||||
/* Retrieve WWPN from specified SCSI device path. */
|
||||
static char *devpath_to_wwpn(const char *devpath)
|
||||
{
|
||||
char *copy, *rport, *end, *path = NULL, *wwpn = NULL;
|
||||
|
||||
/* devpath=/devices/css0/0.0.001c/0.0.1940/host0/rport-0:0-16/
|
||||
* target0:0:16/0:0:16:1085030433/ */
|
||||
copy = misc_strdup(devpath);
|
||||
rport = strstr(copy, "/rport-");
|
||||
end = skip_comp(rport);
|
||||
if (!end)
|
||||
goto out;
|
||||
*end = 0;
|
||||
|
||||
/* devpath=/devices/css0/0.0.001c/0.0.1940/host0/rport-0:0-16
|
||||
* rport=rport-0:0-16 */
|
||||
path = path_get("/sys%s/fc_remote_ports%s/port_name", copy, rport);
|
||||
wwpn = misc_read_text_file(path, 1, err_ignore);
|
||||
|
||||
out:
|
||||
free(path);
|
||||
free(copy);
|
||||
|
||||
return wwpn;
|
||||
}
|
||||
|
||||
static unsigned int lun_swap[] = { 6, 7, 4, 5 };
|
||||
|
||||
uint64_t scsi_lun_to_fcp_lun(uint64_t lun)
|
||||
{
|
||||
byte_swap((uint8_t *) &lun, lun_swap, ARRAY_SIZE(lun_swap));
|
||||
|
||||
return lun;
|
||||
}
|
||||
|
||||
uint64_t scsi_lun_from_fcp_lun(uint64_t lun)
|
||||
{
|
||||
byte_swap((uint8_t *) &lun, lun_swap, ARRAY_SIZE(lun_swap));
|
||||
|
||||
return lun;
|
||||
}
|
||||
|
||||
/* Retrieve FCP LUN from specified HCTL. */
|
||||
static char *hctl_to_fcp_lun(const char *hctl)
|
||||
{
|
||||
struct scsi_hctl_devid devid;
|
||||
|
||||
if (!scsi_hctl_parse_devid(&devid, hctl))
|
||||
return NULL;
|
||||
|
||||
return misc_asprintf("0x%016" PRIx64, scsi_lun_to_fcp_lun(devid.lun));
|
||||
}
|
||||
|
||||
/* Retrieve FCP LUN from specified SCSI device path. Works with paths to
|
||||
* SCSI device and sub-directories (required for paths from /sys/dev/block and
|
||||
* /sys/dev/char). */
|
||||
static char *devpath_to_fcp_lun(const char *devpath)
|
||||
{
|
||||
char *hctl, *fcp_lun = NULL;
|
||||
|
||||
hctl = scsi_hctl_from_devpath(devpath);
|
||||
if (hctl) {
|
||||
fcp_lun = hctl_to_fcp_lun(hctl);
|
||||
free(hctl);
|
||||
}
|
||||
|
||||
return fcp_lun;
|
||||
}
|
||||
|
||||
/* Try to determine the zfcp LUN ID from the specified zfcp SCSI device path. */
|
||||
static char *devpath_to_zfcp_lun_id(const char *path)
|
||||
{
|
||||
const char *devpath;
|
||||
char *hba_id, *wwpn, *fcp_lun, *zfcp_lun_id = NULL;
|
||||
|
||||
devpath = strstr(path, "/devices/");
|
||||
if (!devpath)
|
||||
return NULL;
|
||||
|
||||
hba_id = devpath_to_hba_id(devpath);
|
||||
wwpn = devpath_to_wwpn(devpath);
|
||||
fcp_lun = devpath_to_fcp_lun(devpath);
|
||||
|
||||
if (hba_id && wwpn && fcp_lun)
|
||||
zfcp_lun_id = misc_asprintf("%s:%s:%s", hba_id, wwpn, fcp_lun);
|
||||
|
||||
free(fcp_lun);
|
||||
free(wwpn);
|
||||
free(hba_id);
|
||||
|
||||
return zfcp_lun_id;
|
||||
}
|
||||
|
||||
/* Return the zfcp LUN ID from the specified SCSI HCTL ID. */
|
||||
char *scsi_hctl_to_zfcp_lun_id(const char *hctl)
|
||||
{
|
||||
char *buspath, *link = NULL, *zfcp_lun_id = NULL;
|
||||
|
||||
buspath = path_get_sys_bus_dev("scsi", hctl);
|
||||
link = misc_readlink(buspath);
|
||||
if (!link)
|
||||
goto out;
|
||||
|
||||
zfcp_lun_id = devpath_to_zfcp_lun_id(link);
|
||||
|
||||
out:
|
||||
free(link);
|
||||
free(buspath);
|
||||
|
||||
return zfcp_lun_id;
|
||||
}
|
||||
|
||||
static exit_code_t add_ids_cb(const char *path, const char *name, void *data)
|
||||
{
|
||||
struct util_list *list = data;
|
||||
struct scsi_hctl_devid scsi_devid;
|
||||
struct zfcp_lun_devid zfcp_devid;
|
||||
char *zfcp_id;
|
||||
struct scsi_zfcp *s;
|
||||
|
||||
if (starts_with(name, "host") || starts_with(name, "target"))
|
||||
return EXIT_OK;
|
||||
if (!scsi_hctl_parse_devid(&scsi_devid, name))
|
||||
return EXIT_OK;
|
||||
zfcp_id = scsi_hctl_to_zfcp_lun_id(name);
|
||||
if (!zfcp_id)
|
||||
return EXIT_OK;
|
||||
if (zfcp_lun_parse_devid(&zfcp_devid, zfcp_id, err_ignore) == EXIT_OK) {
|
||||
s = misc_malloc(sizeof(struct scsi_zfcp));
|
||||
s->scsi = scsi_devid;
|
||||
s->zfcp = zfcp_devid;
|
||||
ptrlist_add(list, s);
|
||||
}
|
||||
free(zfcp_id);
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
static struct util_list *read_scsi_zfcp_list(void)
|
||||
{
|
||||
struct util_list *list;
|
||||
char *path;
|
||||
|
||||
list = ptrlist_new();
|
||||
path = path_get_sys_bus_dev("scsi", NULL);
|
||||
if (dir_exists(path))
|
||||
path_for_each(path, add_ids_cb, list);
|
||||
free(path);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static struct scsi_zfcp *get_scsi_zfcp(struct zfcp_lun_devid *devid)
|
||||
{
|
||||
struct ptrlist_node *p;
|
||||
struct scsi_zfcp *s;
|
||||
|
||||
if (!scsi_zfcp_list)
|
||||
scsi_zfcp_list = read_scsi_zfcp_list();
|
||||
|
||||
/* Search for zfcp LUN device ID in cached list. */
|
||||
util_list_iterate(scsi_zfcp_list, p) {
|
||||
s = p->ptr;
|
||||
if (zfcp_lun_cmp_devids(&s->zfcp, devid) == 0)
|
||||
return s;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check for SCSI device associated with zfcp lun device @devid. Return newly
|
||||
* allocated HCTL ID of SCSI device on success, %NULL on failure. */
|
||||
char *scsi_hctl_from_zfcp_lun_devid(struct zfcp_lun_devid *devid)
|
||||
{
|
||||
struct scsi_zfcp *s;
|
||||
|
||||
s = get_scsi_zfcp(devid);
|
||||
if (s)
|
||||
return scsi_hctl_devid_to_str(&s->scsi);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check for SCSI device associated with zfcp LUN device @id. Return newly
|
||||
* allocated HCTL ID of SCSI device on success, %NULL on failure. */
|
||||
char *scsi_hctl_from_zfcp_lun_id(const char *id)
|
||||
{
|
||||
struct zfcp_lun_devid devid;
|
||||
|
||||
if (zfcp_lun_parse_devid(&devid, id, err_ignore) != EXIT_OK)
|
||||
return NULL;
|
||||
|
||||
return scsi_hctl_from_zfcp_lun_devid(&devid);
|
||||
}
|
||||
|
||||
/* Check if SCSI device exists for zfcp LUN @id. */
|
||||
bool scsi_hctl_exists(const char *id)
|
||||
{
|
||||
struct zfcp_lun_devid devid;
|
||||
|
||||
if (zfcp_lun_parse_devid(&devid, id, err_ignore) != EXIT_OK)
|
||||
return false;
|
||||
|
||||
if (!get_scsi_zfcp(&devid))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Add zfcp LUN ids for all SCSI devices to strlist @list. */
|
||||
void scsi_hctl_add_zfcp_lun_ids(struct util_list *list)
|
||||
{
|
||||
struct ptrlist_node *p;
|
||||
struct scsi_zfcp *s;
|
||||
char *id;
|
||||
|
||||
if (!scsi_zfcp_list)
|
||||
scsi_zfcp_list = read_scsi_zfcp_list();
|
||||
|
||||
util_list_iterate(scsi_zfcp_list, p) {
|
||||
s = p->ptr;
|
||||
id = zfcp_lun_devid_to_str(&s->zfcp);
|
||||
strlist_add(list, id);
|
||||
free(id);
|
||||
}
|
||||
}
|
||||
|
||||
/* Return SCSI HCTL ID from SCSI device path. Works with paths to SCSI device
|
||||
* and sub-directories (required for paths from /sys/dev/block and
|
||||
* /sys/dev/char). */
|
||||
char *scsi_hctl_from_devpath(const char *path)
|
||||
{
|
||||
char *copy, *start, *end, *hctl = NULL;
|
||||
|
||||
/* ../../devices/css0/0.0.001c/0.0.1940/host0/rport-0:0-16/
|
||||
* target0:0:16/0:0:16:1085030433/ */
|
||||
copy = misc_strdup(path);
|
||||
start = strstr(copy, "/target");
|
||||
start = skip_comp(start);
|
||||
if (!start)
|
||||
goto out;
|
||||
start++;
|
||||
end = strchr(start, '/');
|
||||
if (end)
|
||||
*end = 0;
|
||||
hctl = misc_strdup(start);
|
||||
|
||||
out:
|
||||
free(copy);
|
||||
|
||||
return hctl;
|
||||
}
|
||||
1291
zdev/src/select.c
Normal file
1291
zdev/src/select.c
Normal file
File diff suppressed because it is too large
Load Diff
713
zdev/src/setting.c
Normal file
713
zdev/src/setting.c
Normal file
@@ -0,0 +1,713 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "attrib.h"
|
||||
#include "misc.h"
|
||||
#include "path.h"
|
||||
#include "setting.h"
|
||||
|
||||
/* Create and initialize a new setting. */
|
||||
struct setting *setting_new(struct attrib *attrib, const char *name,
|
||||
const char *val)
|
||||
{
|
||||
struct setting *s;
|
||||
|
||||
s = misc_malloc(sizeof(struct setting));
|
||||
s->attrib = attrib;
|
||||
if (attrib && !name)
|
||||
s->name = misc_strdup(attrib->name);
|
||||
else
|
||||
s->name = misc_strdup(name);
|
||||
s->value = misc_strdup(val);
|
||||
if (attrib && attrib->multi) {
|
||||
/* Split multi-line value into multiple values. */
|
||||
s->values = strlist_new();
|
||||
strlist_add_multi(s->values, val, "\n", 0);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Release all resources associated with the specified setting. */
|
||||
static void setting_free(struct setting *s)
|
||||
{
|
||||
if (!s)
|
||||
return;
|
||||
free(s->name);
|
||||
free(s->value);
|
||||
free(s->actual_value);
|
||||
strlist_free(s->values);
|
||||
strlist_free(s->actual_values);
|
||||
free(s);
|
||||
}
|
||||
|
||||
/* Create a newly allocated setting copy. */
|
||||
struct setting *setting_copy(const struct setting *s)
|
||||
{
|
||||
struct setting *copy;
|
||||
|
||||
copy = misc_malloc(sizeof(struct setting));
|
||||
copy->attrib = s->attrib;
|
||||
copy->name = misc_strdup(s->name);
|
||||
copy->value = misc_strdup(s->value);
|
||||
if (s->values)
|
||||
copy->values = strlist_copy(s->values);
|
||||
if (s->actual_value)
|
||||
copy->actual_value = misc_strdup(s->actual_value);
|
||||
if (s->actual_values)
|
||||
copy->actual_values = strlist_copy(s->actual_values);
|
||||
copy->modified = s->modified;
|
||||
copy->removed = s->removed;
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
/* Replace or add to the value of a setting. */
|
||||
static void setting_mod_value(struct setting *s, const char *value)
|
||||
{
|
||||
char *new_value;
|
||||
|
||||
if (s->values) {
|
||||
/* Check if this is a rewrite of a previous value. */
|
||||
if (!s->derived && s->attrib && !s->attrib->rewrite &&
|
||||
strlist_find(s->values, value))
|
||||
return;
|
||||
/* Attribute supports multiple values - add. */
|
||||
new_value = misc_asprintf("%s%s%s", s->value,
|
||||
*s->value ? "\n" : "", value);
|
||||
free(s->value);
|
||||
s->value = new_value;
|
||||
strlist_add(s->values, value);
|
||||
} else {
|
||||
/* Check if this is a rewrite of a previous value. */
|
||||
if (!s->derived && s->attrib && !s->attrib->rewrite &&
|
||||
strcmp(s->value, value) == 0)
|
||||
return;
|
||||
/* Attribute unknown or does not support multiple values -
|
||||
* replace. */
|
||||
free(s->value);
|
||||
s->value = misc_strdup(value);
|
||||
}
|
||||
|
||||
s->modified = 1;
|
||||
s->removed = 0;
|
||||
}
|
||||
|
||||
/* Determine if a setting is set (that is configured). */
|
||||
bool setting_is_set(struct setting *s)
|
||||
{
|
||||
if (s->removed)
|
||||
return false;
|
||||
if (s->modified)
|
||||
return true;
|
||||
if (s->derived)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void setting_set_actual(struct setting *s, const char *value)
|
||||
{
|
||||
free(s->actual_value);
|
||||
s->actual_value = misc_strdup(value);
|
||||
if (s->attrib && s->attrib->multi) {
|
||||
strlist_free(s->actual_values);
|
||||
s->actual_values = strlist_new();
|
||||
strlist_add_multi(s->actual_values, value, "\n", 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Write a single setting value to a sysfs attribute. */
|
||||
static exit_code_t write_setting_value(const char *path, struct setting *s,
|
||||
const char *value)
|
||||
{
|
||||
struct attrib *a = s->attrib;
|
||||
int newline, rewrite, unstable, writeonly;
|
||||
exit_code_t rc = EXIT_OK;
|
||||
char *currvalue = NULL, *newvalue = NULL;
|
||||
|
||||
newline = a ? a->newline : 0;
|
||||
rewrite = a ? a->rewrite : 0;
|
||||
unstable = a ? a->unstable : 0;
|
||||
writeonly = a ? a->writeonly : 0;
|
||||
|
||||
/* Do we need to check for a rewrite? */
|
||||
if (rewrite || unstable || force)
|
||||
goto do_write;
|
||||
|
||||
/* Get current value if necessary. */
|
||||
if (!s->actual_value && !writeonly) {
|
||||
currvalue = misc_read_text_file(path, 1, err_ignore);
|
||||
if (!currvalue)
|
||||
goto do_write;
|
||||
setting_set_actual(s, currvalue);
|
||||
free(currvalue);
|
||||
}
|
||||
|
||||
/* Check if value is already set. */
|
||||
if (s->actual_values) {
|
||||
if (strlist_find(s->actual_values, value))
|
||||
goto out;
|
||||
} else if (s->actual_value) {
|
||||
if (strcmp(s->actual_value, value) == 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
do_write:
|
||||
/* Ensure newline if required. */
|
||||
if (newline && !ends_with(value, "\n"))
|
||||
newvalue = misc_asprintf("%s\n", value);
|
||||
|
||||
rc = misc_write_text_file_retry(path, newvalue ? newvalue : value,
|
||||
err_delayed_print);
|
||||
if (rc)
|
||||
rc = EXIT_SETTING_FAILED;
|
||||
|
||||
out:
|
||||
free(newvalue);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Write a single setting to a sysfs attribute. */
|
||||
exit_code_t setting_write(const char *path, struct setting *s)
|
||||
{
|
||||
exit_code_t rc;
|
||||
struct strlist_node *str;
|
||||
|
||||
if (!s->values) {
|
||||
/* Single value attribute. */
|
||||
return write_setting_value(path, s, s->value);
|
||||
}
|
||||
util_list_iterate(s->values, str) {
|
||||
rc = write_setting_value(path, s, str->str);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
/* Used for debugging. */
|
||||
void setting_print(struct setting *s, int level)
|
||||
{
|
||||
struct strlist_node *str;
|
||||
int i;
|
||||
|
||||
printf("%*ssetting at %p\n", level, "", (void *) s);
|
||||
if (!s)
|
||||
return;
|
||||
level += 4;
|
||||
if (s->attrib)
|
||||
printf("%*sattrib=%s\n", level, "", s->attrib->name);
|
||||
else
|
||||
printf("%*sattrib=<none>\n", level, "");
|
||||
printf("%*sname='%s'\n", level, "", s->name);
|
||||
printf("%*svalue='%s'\n", level, "", s->value);
|
||||
if (s->values) {
|
||||
i = 0;
|
||||
util_list_iterate(s->values, str) {
|
||||
printf("%*svalues[%d]='%s'\n", level, "", i++,
|
||||
str->str);
|
||||
}
|
||||
}
|
||||
if (s->actual_value)
|
||||
printf("%*sactual_value='%s'\n", level, "", s->actual_value);
|
||||
else
|
||||
printf("%*sactual_value=<none>\n", level, "");
|
||||
if (s->actual_values) {
|
||||
i = 0;
|
||||
util_list_iterate(s->actual_values, str) {
|
||||
printf("%*sactual_values[%d]='%s'\n", level, "", i++,
|
||||
str->str);
|
||||
}
|
||||
}
|
||||
printf("%*smodified='%d'\n", level, "", s->modified);
|
||||
printf("%*sspecified='%d'\n", level, "", s->specified);
|
||||
printf("%*sremoved='%d'\n", level, "", s->removed);
|
||||
printf("%*sderived='%d'\n", level, "", s->derived);
|
||||
printf("%*sreadonly='%d'\n", level, "", s->readonly);
|
||||
}
|
||||
|
||||
/* Create and initialize a new setting_list. */
|
||||
struct setting_list *setting_list_new(void)
|
||||
{
|
||||
struct setting_list *list;
|
||||
|
||||
list = misc_malloc(sizeof(struct setting_list));
|
||||
util_list_init(&list->list, struct setting, node);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/* Remove all settings from list. */
|
||||
void setting_list_clear(struct setting_list *list)
|
||||
{
|
||||
struct setting *s, *n;
|
||||
|
||||
util_list_iterate_safe(&list->list, s, n) {
|
||||
util_list_remove(&list->list, s);
|
||||
setting_free(s);
|
||||
}
|
||||
}
|
||||
|
||||
/* Release resources used by list and enlisted settings. */
|
||||
void setting_list_free(struct setting_list *list)
|
||||
{
|
||||
if (!list)
|
||||
return;
|
||||
setting_list_clear(list);
|
||||
|
||||
free(list);
|
||||
}
|
||||
|
||||
/* Add a new element to a list and mark the list as modified. */
|
||||
void setting_list_add(struct setting_list *list, struct setting *setting)
|
||||
{
|
||||
util_list_add_tail(&list->list, setting);
|
||||
list->modified = 1;
|
||||
}
|
||||
|
||||
/* Find an element in the list. */
|
||||
struct setting *setting_list_find(struct setting_list *list, const char *name)
|
||||
{
|
||||
struct setting *s;
|
||||
|
||||
util_list_iterate(&list->list, s) {
|
||||
if (strcmp(s->name, name) == 0)
|
||||
return s;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct setting *add_setting(struct setting_list *list,
|
||||
struct attrib *attrib,
|
||||
const char *name, const char *value,
|
||||
int new_modified)
|
||||
{
|
||||
struct setting *s;
|
||||
|
||||
s = setting_list_find(list, name);
|
||||
if (s)
|
||||
setting_mod_value(s, value);
|
||||
else {
|
||||
s = setting_new(attrib, name, value);
|
||||
setting_list_add(list, s);
|
||||
if (new_modified)
|
||||
s->modified = 1;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Modify existing setting or add new one. */
|
||||
struct setting *setting_list_apply(struct setting_list *list,
|
||||
struct attrib *attrib, const char *name,
|
||||
const char *value)
|
||||
{
|
||||
return add_setting(list, attrib, name, value, 1);
|
||||
}
|
||||
|
||||
/* Modify existing setting or add new one based on user request. */
|
||||
struct setting *setting_list_apply_specified(struct setting_list *list,
|
||||
struct attrib *attrib,
|
||||
const char *name,
|
||||
const char *value)
|
||||
{
|
||||
struct setting *s;
|
||||
|
||||
s = add_setting(list, attrib, name, value, 1);
|
||||
s->specified = 1;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Set actual value for existing setting or add new one. */
|
||||
struct setting *setting_list_apply_actual(struct setting_list *list,
|
||||
struct attrib *attrib,
|
||||
const char *name, const char *value)
|
||||
{
|
||||
struct setting *s;
|
||||
|
||||
s = add_setting(list, attrib, name, value, 0);
|
||||
setting_set_actual(s, value);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Check if a setting was modified. */
|
||||
bool setting_list_modified(struct setting_list *list)
|
||||
{
|
||||
struct setting *s;
|
||||
|
||||
if (!list)
|
||||
return false;
|
||||
util_list_iterate(&list->list, s) {
|
||||
if (s->modified)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Determine the state of a boolean attribute with the given name in the
|
||||
* specified setting list. */
|
||||
void setting_list_get_bool_state(struct setting_list *list, const char *name,
|
||||
int *changed, int *set)
|
||||
{
|
||||
struct setting *s;
|
||||
|
||||
s = setting_list_find(list, name);
|
||||
if (!s) {
|
||||
*changed = 0;
|
||||
*set = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(s->value, "1") == 0)
|
||||
*set = 1;
|
||||
else
|
||||
*set = 0;
|
||||
|
||||
if (s->actual_value && strcmp(s->value, s->actual_value) != 0)
|
||||
*changed = 1;
|
||||
else
|
||||
*changed = 0;
|
||||
}
|
||||
|
||||
/* Return a newly allocated space-separated string containing all non-removed
|
||||
* settings in KEY=VALUE format. */
|
||||
char *setting_list_flatten(struct setting_list *list)
|
||||
{
|
||||
struct setting *s;
|
||||
int i;
|
||||
char *str;
|
||||
|
||||
/* Determine total string length. */
|
||||
i = 1;
|
||||
util_list_iterate(&list->list, s) {
|
||||
if (s->removed)
|
||||
continue;
|
||||
i += strlen(s->name) + 1 + strlen(s->value) + 1;
|
||||
}
|
||||
str = misc_malloc(i);
|
||||
|
||||
/* Combine string. */
|
||||
i = 0;
|
||||
util_list_iterate(&list->list, s) {
|
||||
if (s->removed)
|
||||
continue;
|
||||
i += sprintf(&str[i], "%s%s=%s", i == 0 ? "" : " ", s->name,
|
||||
s->value);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/* Used for debugging. */
|
||||
void setting_list_print(struct setting_list *list, int level)
|
||||
{
|
||||
struct setting *s;
|
||||
|
||||
printf("%*ssetting list at %p\n", level, "", (void *) list);
|
||||
if (!list)
|
||||
return;
|
||||
util_list_iterate(&list->list, s)
|
||||
setting_print(s, level + 4);
|
||||
}
|
||||
|
||||
/* Determine the order of applying attributes based on the attribute order
|
||||
* information. */
|
||||
static int setting_cmp(struct setting *a, struct setting *b)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (!a->attrib || !b->attrib)
|
||||
return 0;
|
||||
|
||||
/* Try order_cmp first if available. */
|
||||
if (a->attrib && a->attrib->order_cmp) {
|
||||
result = a->attrib->order_cmp(a, b);
|
||||
if (result != 0)
|
||||
return result;
|
||||
}
|
||||
if (b->attrib && b->attrib->order_cmp) {
|
||||
result = b->attrib->order_cmp(b, a);
|
||||
if (result != 0)
|
||||
return -result;
|
||||
}
|
||||
|
||||
/* Try static ordering next. */
|
||||
if (a->attrib->order < b->attrib->order)
|
||||
return -1;
|
||||
if (a->attrib->order > b->attrib->order)
|
||||
return 1;
|
||||
|
||||
/* Don't fallback to strcmp or similar here since that could
|
||||
* conflict with an actual ordering requirement reported by
|
||||
* a cmp callback. */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return a newly allocated ptrlist of settings sorted according to
|
||||
* order_cmp. */
|
||||
struct util_list *setting_list_get_sorted(struct setting_list *list)
|
||||
{
|
||||
struct util_list *result;
|
||||
struct setting *s;
|
||||
struct ptrlist_node *p;
|
||||
|
||||
result = ptrlist_new();
|
||||
p = NULL;
|
||||
util_list_iterate(&list->list, s) {
|
||||
/* Find first element that should be after new element
|
||||
* in list. */
|
||||
util_list_iterate(result, p) {
|
||||
if (setting_cmp(p->ptr, s) > 0)
|
||||
break;
|
||||
}
|
||||
if (p)
|
||||
ptrlist_add_before(result, p, s);
|
||||
else
|
||||
ptrlist_add(result, s);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Check if there is any conflict between settings in @list. Display errors
|
||||
* according to @err. Return %false if there is a conflict, %true otherwise. */
|
||||
bool setting_list_check_conflict(struct setting_list *list, config_t config,
|
||||
err_t err)
|
||||
{
|
||||
struct setting *a, *b;
|
||||
|
||||
util_list_iterate(&list->list, a) {
|
||||
if (!a->attrib || !a->attrib->check)
|
||||
continue;
|
||||
if (a->removed)
|
||||
continue;
|
||||
if (!a->modified && !a->specified)
|
||||
continue;
|
||||
util_list_iterate(&list->list, b) {
|
||||
if (a == b)
|
||||
continue;
|
||||
if (!a->attrib->check(a, b, config))
|
||||
goto conflict;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
conflict:
|
||||
err_t_print(err, "Cannot set %s='%s' while %s='%s'\n", a->name,
|
||||
a->value, b->name, b->value);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Add settings with default values to @list for all attributes in @attribs
|
||||
* for which no setting has been added. If @mand_only is specified, only
|
||||
* apply default values for mandatory settings. */
|
||||
void setting_list_apply_defaults(struct setting_list *list,
|
||||
struct attrib **attribs, bool mand_only)
|
||||
{
|
||||
int i;
|
||||
struct attrib *a;
|
||||
struct setting *s;
|
||||
|
||||
for (i = 0; (a = attribs[i]); i++) {
|
||||
if (!a->defval)
|
||||
continue;
|
||||
if (mand_only && !a->mandatory)
|
||||
continue;
|
||||
s = setting_list_find(list, a->name);
|
||||
if (s)
|
||||
continue;
|
||||
s = setting_list_apply_actual(list, a, a->name, a->defval);
|
||||
s->derived = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Merge settings from setting list @from into @to. If @specified is true,
|
||||
* also copy specified flag. If @modified is true, also copy modified flag. */
|
||||
void setting_list_merge(struct setting_list *to, struct setting_list *from,
|
||||
bool specified, bool modified)
|
||||
{
|
||||
struct setting *s, *n;
|
||||
|
||||
util_list_iterate(&from->list, s) {
|
||||
n = setting_list_apply(to, s->attrib, s->name, s->value);
|
||||
if (specified)
|
||||
n->specified = s->specified;
|
||||
if (modified)
|
||||
n->modified = n->modified;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return a copy of setting list @list. */
|
||||
struct setting_list *setting_list_copy(struct setting_list *list)
|
||||
{
|
||||
struct setting_list *copy;
|
||||
struct setting *s;
|
||||
|
||||
copy = setting_list_new();
|
||||
util_list_iterate(&list->list, s)
|
||||
util_list_add_tail(©->list, setting_copy(s));
|
||||
copy->modified = list->modified;
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
/* Apply the attribute value replacement map for all settings in list which
|
||||
* define such a map. */
|
||||
void setting_list_map_values(struct setting_list *list)
|
||||
{
|
||||
struct setting *s;
|
||||
struct attrib *a;
|
||||
const char *to;
|
||||
|
||||
util_list_iterate(&list->list, s) {
|
||||
a = s->attrib;
|
||||
if (!a || !a->map)
|
||||
continue;
|
||||
to = attrib_map_value(a, s->value);
|
||||
if (!to)
|
||||
continue;
|
||||
free(s->value);
|
||||
s->value = misc_strdup(to);
|
||||
}
|
||||
}
|
||||
|
||||
/* Mark all settings in @list as derived which have an actual_value equal
|
||||
* to the default value. */
|
||||
void setting_list_mark_default_derived(struct setting_list *list)
|
||||
{
|
||||
struct setting *s;
|
||||
|
||||
util_list_iterate(&list->list, s) {
|
||||
if (!s->attrib || !s->attrib->defval || !s->actual_value)
|
||||
continue;
|
||||
if (!attrib_match_default(s->attrib, s->actual_value))
|
||||
continue;
|
||||
s->derived = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the number of settings in @list that are set. */
|
||||
int setting_list_count_set(struct setting_list *list)
|
||||
{
|
||||
struct setting *s;
|
||||
int set;
|
||||
|
||||
set = 0;
|
||||
util_list_iterate(&list->list, s) {
|
||||
if (setting_is_set(s))
|
||||
set++;
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
/* Remove all settings in @list which are derived. */
|
||||
void setting_list_remove_derived(struct setting_list *list)
|
||||
{
|
||||
struct setting *s, *n;
|
||||
|
||||
util_list_iterate_safe(&list->list, s, n) {
|
||||
if (!s->derived)
|
||||
continue;
|
||||
util_list_remove(&list->list, s);
|
||||
setting_free(s);
|
||||
}
|
||||
}
|
||||
|
||||
static void add_changes(struct util_list *list, struct setting *s)
|
||||
{
|
||||
struct strlist_node *str;
|
||||
|
||||
if (s->values) {
|
||||
/* Multi value attribute. */
|
||||
util_list_iterate(s->values, str)
|
||||
strlist_add(list, "%s=%s", s->name, str->str);
|
||||
} else {
|
||||
/* Single value attribute. */
|
||||
strlist_add(list, "%s=%s", s->name, s->value);
|
||||
}
|
||||
}
|
||||
|
||||
/* Return a newly allocated string containing the setting changes found
|
||||
* in PERS and ACT or NULL if no change was found. */
|
||||
char *setting_get_changes(struct setting_list *act, struct setting_list *pers)
|
||||
{
|
||||
struct util_list *out;
|
||||
struct util_list *processed;
|
||||
struct setting *s;
|
||||
char *result;
|
||||
|
||||
out = strlist_new();
|
||||
processed = strlist_new();
|
||||
|
||||
/* Collect modified settings. */
|
||||
if (pers) {
|
||||
util_list_iterate(&pers->list, s) {
|
||||
if (s->removed)
|
||||
strlist_add(out, "-%s", s->name);
|
||||
else if (s->modified)
|
||||
add_changes(out, s);
|
||||
else
|
||||
continue;
|
||||
strlist_add(processed, s->name);
|
||||
}
|
||||
}
|
||||
if (act) {
|
||||
util_list_iterate(&act->list, s) {
|
||||
if (!s->modified && !s->removed)
|
||||
continue;
|
||||
if (strlist_find(processed, s->name))
|
||||
continue;
|
||||
if (s->removed)
|
||||
strlist_add(out, "-%s", s->name);
|
||||
else
|
||||
add_changes(out, s);
|
||||
strlist_add(processed, s->name);
|
||||
}
|
||||
}
|
||||
|
||||
if (!util_list_is_empty(out))
|
||||
result = strlist_flatten(out, " ");
|
||||
else
|
||||
result = NULL;
|
||||
|
||||
strlist_free(processed);
|
||||
strlist_free(out);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Check if @value matches the value of setting @s. */
|
||||
bool setting_match_value(struct setting *s, const char *value)
|
||||
{
|
||||
struct strlist_node *str;
|
||||
|
||||
if (s->values) {
|
||||
util_list_iterate(s->values, str) {
|
||||
if (strcmp(str->str, value) == 0)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (s->value) {
|
||||
if (strcmp(s->value, value) == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
816
zdev/src/subtype.c
Normal file
816
zdev/src/subtype.c
Normal file
@@ -0,0 +1,816 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
|
||||
#include "lib/util_base.h"
|
||||
|
||||
#include "attrib.h"
|
||||
#include "device.h"
|
||||
#include "devnode.h"
|
||||
#include "devtype.h"
|
||||
#include "misc.h"
|
||||
#include "namespace.h"
|
||||
#include "setting.h"
|
||||
#include "subtype.h"
|
||||
#include "udev.h"
|
||||
|
||||
/*
|
||||
* Generic subtype helper functions.
|
||||
*/
|
||||
|
||||
/* Return struct subtype associated with NAME or NULL if type could not
|
||||
* be found. */
|
||||
struct subtype *subtype_find(const char *name)
|
||||
{
|
||||
int i, j;
|
||||
struct devtype *dt;
|
||||
struct subtype *st;
|
||||
|
||||
for (i = 0; devtypes[i]; i++) {
|
||||
dt = devtypes[i];
|
||||
for (j = 0; dt->subtypes[j]; j++) {
|
||||
st = dt->subtypes[j];
|
||||
if (strcasecmp(st->name, name) == 0)
|
||||
return st;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Search for a device attribute named STR. */
|
||||
struct attrib *subtype_find_dev_attrib(struct subtype *st, const char *str)
|
||||
{
|
||||
struct attrib *a;
|
||||
int i;
|
||||
|
||||
for (i = 0; (a = st->dev_attribs[i]); i++) {
|
||||
if (strcmp(str, a->name) == 0)
|
||||
return a;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct devnode_cb_data_t {
|
||||
struct devnode *devnode;
|
||||
struct subtype **st_ptr;
|
||||
char **id_ptr;
|
||||
};
|
||||
|
||||
static exit_code_t devnode_cb(struct subtype *st, const char *id,
|
||||
config_t config, void *data)
|
||||
{
|
||||
struct devnode_cb_data_t *cb_data = data;
|
||||
struct devnode *devnode;
|
||||
struct ptrlist_node *p, *n;
|
||||
struct util_list *devnodes;
|
||||
exit_code_t rc = EXIT_OK;
|
||||
|
||||
devnodes = subtype_get_devnodes(st, id);
|
||||
if (!devnodes)
|
||||
return EXIT_OK;
|
||||
util_list_iterate_safe(devnodes, p, n) {
|
||||
util_list_remove(devnodes, p);
|
||||
devnode = p->ptr;
|
||||
if (!rc && devnode_cmp(devnode, cb_data->devnode) == 0) {
|
||||
*cb_data->st_ptr = st;
|
||||
*cb_data->id_ptr = misc_strdup(id);
|
||||
/* Not an error but aborts the loop. */
|
||||
rc = EXIT_ABORTED;
|
||||
}
|
||||
free(devnode);
|
||||
free(p);
|
||||
}
|
||||
free(devnodes);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Find a device which provides the specified devnode. Note that id_ptr will
|
||||
* point to a newly allocated string on success. */
|
||||
bool subtypes_find_by_devnode(struct devnode *devnode, struct subtype **st_ptr,
|
||||
char **id_ptr)
|
||||
{
|
||||
int i, j;
|
||||
struct devtype *dt;
|
||||
struct subtype *st;
|
||||
struct devnode_cb_data_t cb_data;
|
||||
char *id;
|
||||
|
||||
/* Try direct resolution if available. */
|
||||
for (i = 0; (dt = devtypes[i]); i++) {
|
||||
for (j = 0; (st = dt->subtypes[j]); j++) {
|
||||
id = subtype_resolve_devnode(st, devnode);
|
||||
if (id) {
|
||||
*st_ptr = st;
|
||||
*id_ptr = id;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Search list of provided devnodes. */
|
||||
cb_data.devnode = devnode;
|
||||
cb_data.st_ptr = st_ptr;
|
||||
cb_data.id_ptr = id_ptr;
|
||||
for (i = 0; (dt = devtypes[i]); i++) {
|
||||
for (j = 0; (st = dt->subtypes[j]); j++) {
|
||||
if (subtype_for_each_id(st, config_active, devnode_cb,
|
||||
&cb_data))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Subtype method acessor functions. These functions run the subtype's
|
||||
* method or, if non was defined, the method of its super-subtype.
|
||||
*/
|
||||
|
||||
/* Follow curr->super until curr->method is non-zero or curr is NULL. */
|
||||
static void *_super_get(void *curr, size_t method_off, size_t super_off,
|
||||
int mandatory)
|
||||
{
|
||||
void **addr;
|
||||
|
||||
while (curr) {
|
||||
addr = (void *) ((unsigned long) curr + method_off);
|
||||
if (*addr)
|
||||
break;
|
||||
addr = (void *) ((unsigned long) curr + super_off);
|
||||
curr = *addr;
|
||||
}
|
||||
|
||||
if (!curr && mandatory)
|
||||
internal("Missing method implementation");
|
||||
|
||||
return curr;
|
||||
}
|
||||
|
||||
#define super_get(obj, method, mand) \
|
||||
_super_get((obj), offsetof(__typeof__(*(obj)), method), \
|
||||
offsetof(__typeof__(*(obj)), super), \
|
||||
(mand))
|
||||
|
||||
void subtype_init(struct subtype *st)
|
||||
{
|
||||
struct subtype *super = super_get(st, init, 0);
|
||||
|
||||
if (super)
|
||||
super->init(st);
|
||||
}
|
||||
|
||||
void subtype_exit(struct subtype *st)
|
||||
{
|
||||
struct subtype *super = super_get(st, exit, 0);
|
||||
|
||||
if (super)
|
||||
super->exit(st);
|
||||
}
|
||||
|
||||
bool subtype_device_exists_active(struct subtype *st, const char *id)
|
||||
{
|
||||
struct subtype *super = super_get(st, exists_active, 1);
|
||||
|
||||
return super->exists_active(st, id);
|
||||
}
|
||||
|
||||
bool subtype_device_exists_persistent(struct subtype *st, const char *id)
|
||||
{
|
||||
struct subtype *super = super_get(st, exists_persistent, 1);
|
||||
|
||||
return super->exists_persistent(st, id);
|
||||
}
|
||||
|
||||
void subtype_add_active_ids(struct subtype *st, struct util_list *ids)
|
||||
{
|
||||
struct subtype *super = super_get(st, add_active_ids, 1);
|
||||
|
||||
super->add_active_ids(st, ids);
|
||||
}
|
||||
|
||||
void subtype_add_persistent_ids(struct subtype *st, struct util_list *ids)
|
||||
{
|
||||
struct subtype *super = super_get(st, add_persistent_ids, 1);
|
||||
|
||||
super->add_persistent_ids(st, ids);
|
||||
}
|
||||
|
||||
exit_code_t subtype_device_read_active(struct subtype *st, struct device *dev,
|
||||
read_scope_t scope)
|
||||
{
|
||||
struct subtype *super = super_get(st, read_active, 1);
|
||||
|
||||
return super->read_active(st, dev, scope);
|
||||
}
|
||||
|
||||
exit_code_t subtype_device_read_persistent(struct subtype *st,
|
||||
struct device *dev,
|
||||
read_scope_t scope)
|
||||
{
|
||||
struct subtype *super = super_get(st, read_persistent, 1);
|
||||
|
||||
return super->read_persistent(st, dev, scope);
|
||||
}
|
||||
|
||||
exit_code_t subtype_device_configure_active(struct subtype *st,
|
||||
struct device *dev)
|
||||
{
|
||||
struct subtype *super = super_get(st, configure_active, 1);
|
||||
|
||||
return super->configure_active(st, dev);
|
||||
}
|
||||
|
||||
exit_code_t subtype_device_configure_persistent(struct subtype *st,
|
||||
struct device *dev)
|
||||
{
|
||||
struct subtype *super = super_get(st, configure_persistent, 1);
|
||||
|
||||
return super->configure_persistent(st, dev);
|
||||
}
|
||||
|
||||
exit_code_t subtype_device_deconfigure_active(struct subtype *st,
|
||||
struct device *dev)
|
||||
{
|
||||
struct subtype *super = super_get(st, deconfigure_active, 1);
|
||||
|
||||
return super->deconfigure_active(st, dev);
|
||||
}
|
||||
|
||||
exit_code_t subtype_device_deconfigure_persistent(struct subtype *st,
|
||||
struct device *dev)
|
||||
{
|
||||
struct subtype *super = super_get(st, deconfigure_persistent, 1);
|
||||
|
||||
return super->deconfigure_persistent(st, dev);
|
||||
}
|
||||
|
||||
exit_code_t subtype_check_pre_configure(struct subtype *st, struct device *dev,
|
||||
int prereq, config_t config)
|
||||
{
|
||||
struct subtype *super = super_get(st, check_pre_configure, 0);
|
||||
exit_code_t rc;
|
||||
|
||||
/* Generic checking. */
|
||||
rc = device_check_settings(dev, config, err_delayed_forceable);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Type-specific checking. */
|
||||
if (super)
|
||||
return super->check_pre_configure(st, dev, prereq, config);
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
exit_code_t subtype_check_post_configure(struct subtype *st, struct device *dev,
|
||||
int prereq, config_t config)
|
||||
{
|
||||
struct subtype *super = super_get(st, check_post_configure, 0);
|
||||
|
||||
if (super)
|
||||
return super->check_post_configure(st, dev, prereq, config);
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
void subtype_online_set(struct subtype *st, struct device *dev, int online,
|
||||
config_t config)
|
||||
{
|
||||
struct subtype *super = super_get(st, online_set, 0);
|
||||
|
||||
if (super)
|
||||
super->online_set(st, dev, online, config);
|
||||
}
|
||||
|
||||
int subtype_online_get(struct subtype *st, struct device *dev, config_t config)
|
||||
{
|
||||
struct subtype *super = super_get(st, online_get, 0);
|
||||
int act_online = 1, pers_online = 1;
|
||||
|
||||
if (super)
|
||||
return super->online_get(st, dev, config);
|
||||
|
||||
/* Devices that do not support online setting are online when they
|
||||
* exist. */
|
||||
if (SCOPE_ACTIVE(config))
|
||||
act_online = dev->active.exists;
|
||||
if (SCOPE_PERSISTENT(config))
|
||||
pers_online = dev->persistent.exists;
|
||||
|
||||
return MIN(act_online, pers_online);
|
||||
}
|
||||
|
||||
bool subtype_online_specified(struct subtype *st, struct device *dev,
|
||||
config_t config)
|
||||
{
|
||||
struct subtype *super = super_get(st, online_specified, 0);
|
||||
|
||||
if (super)
|
||||
return super->online_specified(st, dev, config);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void subtype_add_errors(struct subtype *st, const char *id,
|
||||
struct util_list *errors)
|
||||
{
|
||||
struct subtype *super = super_get(st, add_errors, 0);
|
||||
|
||||
if (super)
|
||||
super->add_errors(st, id, errors);
|
||||
}
|
||||
|
||||
void subtype_add_modules(struct subtype *st, struct device *dev,
|
||||
struct util_list *modules)
|
||||
{
|
||||
struct subtype *super = super_get(st, add_modules, 0);
|
||||
|
||||
if (super)
|
||||
super->add_modules(st, dev, modules);
|
||||
}
|
||||
|
||||
void subtype_add_devnodes(struct subtype *st, const char *id,
|
||||
struct util_list *devnodes)
|
||||
{
|
||||
struct subtype *super = super_get(st, add_devnodes, 0);
|
||||
|
||||
if (super)
|
||||
super->add_devnodes(st, id, devnodes);
|
||||
}
|
||||
|
||||
char *subtype_resolve_devnode(struct subtype *st, struct devnode *devnode)
|
||||
{
|
||||
struct subtype *super = super_get(st, resolve_devnode, 0);
|
||||
|
||||
if (super)
|
||||
return super->resolve_devnode(st, devnode);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void subtype_add_prereqs(struct subtype *st, const char *id,
|
||||
struct util_list *selected)
|
||||
{
|
||||
struct subtype *super = super_get(st, add_prereqs, 0);
|
||||
|
||||
if (super)
|
||||
super->add_prereqs(st, id, selected);
|
||||
}
|
||||
|
||||
void subtype_rem_combined(struct subtype *st, struct device *dev,
|
||||
struct selected_dev_node *curr,
|
||||
struct util_list *selected)
|
||||
{
|
||||
struct subtype *super = super_get(st, rem_combined, 0);
|
||||
|
||||
if (super)
|
||||
super->rem_combined(st, dev, curr, selected);
|
||||
}
|
||||
|
||||
char *subtype_get_active_attrib_path(struct subtype *st, struct device *dev,
|
||||
const char *name)
|
||||
{
|
||||
struct subtype *super = super_get(st, get_active_attrib_path, 0);
|
||||
|
||||
if (super)
|
||||
return super->get_active_attrib_path(st, dev, name);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *subtype_get_active_attrib(struct subtype *st, struct device *dev,
|
||||
const char *name)
|
||||
{
|
||||
struct subtype *super = super_get(st, get_active_attrib, 0);
|
||||
|
||||
if (super)
|
||||
return super->get_active_attrib(st, dev, name);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
exit_code_t subtype_device_is_definable(struct subtype *st, const char *id,
|
||||
err_t err)
|
||||
{
|
||||
struct subtype *super = super_get(st, is_definable, 0);
|
||||
|
||||
if (super)
|
||||
return super->is_definable(st, id, err);
|
||||
|
||||
return EXIT_GROUP_NOT_FOUND;
|
||||
}
|
||||
|
||||
exit_code_t subtype_detect_definable(struct subtype *st, struct device *dev)
|
||||
{
|
||||
struct subtype *super = super_get(st, detect_definable, 0);
|
||||
|
||||
if (super)
|
||||
return super->detect_definable(st, dev);
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
exit_code_t subtype_device_define(struct subtype *st, struct device *dev)
|
||||
{
|
||||
struct subtype *super = super_get(st, device_define, 0);
|
||||
exit_code_t rc;
|
||||
int proc;
|
||||
struct setting_list *settings;
|
||||
|
||||
if (super) {
|
||||
rc = super->device_define(st, dev);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* After device was defined, udev might have already applied
|
||||
* existing persistent settings. Need to wait for udev and
|
||||
* reread active configuration. */
|
||||
udev_settle();
|
||||
|
||||
proc = dev->processed;
|
||||
settings = dev->active.settings;
|
||||
dev->active.settings = setting_list_new();
|
||||
|
||||
if (subtype_reread_device(st, dev->id, config_active,
|
||||
scope_known, &dev) == EXIT_OK)
|
||||
dev->active.modified = 1;
|
||||
|
||||
/* Re-apply settings. */
|
||||
setting_list_merge(dev->active.settings, settings, true, true);
|
||||
setting_list_free(settings);
|
||||
|
||||
/* Need to restore dev->proc since it might have been cleared
|
||||
* by subtype_reread_device() . */
|
||||
dev->processed = proc;
|
||||
}
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
exit_code_t subtype_device_undefine(struct subtype *st, struct device *dev)
|
||||
{
|
||||
struct subtype *super = super_get(st, device_undefine, 0);
|
||||
|
||||
if (super)
|
||||
return super->device_undefine(st, dev);
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
void subtype_add_definable_ids(struct subtype *st, struct util_list *ids)
|
||||
{
|
||||
struct subtype *super = super_get(st, add_definable_ids, 0);
|
||||
|
||||
if (super)
|
||||
super->add_definable_ids(st, ids);
|
||||
}
|
||||
|
||||
/*
|
||||
* Subtype helpers. These functions combine some of the subtype methods
|
||||
* to implement more complex functions.
|
||||
*/
|
||||
|
||||
bool subtype_device_exists(struct subtype *st, const char *id, config_t config)
|
||||
{
|
||||
if (SCOPE_ACTIVE(config)) {
|
||||
if (!subtype_device_exists_active(st, id) &&
|
||||
subtype_device_is_definable(st, id, err_ignore) != EXIT_OK)
|
||||
return false;
|
||||
}
|
||||
if (SCOPE_PERSISTENT(config)) {
|
||||
if (!subtype_device_exists_persistent(st, id))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct util_list *get_ids(struct subtype *st, config_t config)
|
||||
{
|
||||
struct util_list *ids;
|
||||
|
||||
ids = strlist_new();
|
||||
|
||||
/* Get IDs from each configuration. */
|
||||
if (SCOPE_ACTIVE(config)) {
|
||||
subtype_add_active_ids(st, ids);
|
||||
subtype_add_definable_ids(st, ids);
|
||||
}
|
||||
if (SCOPE_PERSISTENT(config))
|
||||
subtype_add_persistent_ids(st, ids);
|
||||
|
||||
/* Provide a sorted view. */
|
||||
strlist_sort_unique(ids, st->namespace->qsort_cmp);
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
unsigned long subtype_count_ids(struct subtype *st, config_t config)
|
||||
{
|
||||
struct util_list *ids;
|
||||
unsigned long num;
|
||||
|
||||
ids = get_ids(st, config);
|
||||
num = util_list_len(ids);
|
||||
strlist_free(ids);
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
exit_code_t subtype_for_each_id(struct subtype *st, config_t config,
|
||||
subtype_cb_t cb, void *data)
|
||||
{
|
||||
struct util_list *ids;
|
||||
struct strlist_node *s;
|
||||
exit_code_t rc;
|
||||
|
||||
ids = get_ids(st, config);
|
||||
|
||||
rc = EXIT_OK;
|
||||
util_list_iterate(ids, s) {
|
||||
rc = cb(st, s->str, config, data);
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
|
||||
strlist_free(ids);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct util_list *subtype_get_devnodes(struct subtype *st, const char *id)
|
||||
{
|
||||
struct util_list *devnodes;
|
||||
|
||||
devnodes = ptrlist_new();
|
||||
subtype_add_devnodes(st, id, devnodes);
|
||||
if (util_list_is_empty(devnodes)) {
|
||||
ptrlist_free(devnodes, 1);
|
||||
devnodes = NULL;
|
||||
}
|
||||
|
||||
return devnodes;
|
||||
}
|
||||
|
||||
/* Return a space-separated list of device names provided by device with
|
||||
* subtype @st and @id. */
|
||||
char *subtype_get_devnodes_str(struct subtype *st, const char *id, int bdev,
|
||||
int bdev_part, int cdev, int netdev)
|
||||
{
|
||||
struct util_list *devnodes, *names;
|
||||
struct ptrlist_node *p;
|
||||
struct devnode *d;
|
||||
char *str;
|
||||
int first_bdev = 1;
|
||||
|
||||
devnodes = ptrlist_new();
|
||||
subtype_add_devnodes(st, id, devnodes);
|
||||
|
||||
names = strlist_new();
|
||||
util_list_iterate(devnodes, p) {
|
||||
d = p->ptr;
|
||||
switch (d->type) {
|
||||
case BLOCKDEV:
|
||||
if (bdev_part || (bdev && first_bdev))
|
||||
strlist_add(names, "%s", d->name);
|
||||
first_bdev = 0;
|
||||
break;
|
||||
case CHARDEV:
|
||||
if (cdev)
|
||||
strlist_add(names, "%s", d->name);
|
||||
break;
|
||||
case NETDEV:
|
||||
if (netdev)
|
||||
strlist_add(names, "%s", d->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
str = strlist_flatten(names, " ");
|
||||
|
||||
strlist_free(names);
|
||||
ptrlist_free(devnodes, 1);
|
||||
|
||||
return str;
|
||||
|
||||
}
|
||||
|
||||
struct util_list *subtype_get_errors(struct subtype *st, const char *id)
|
||||
{
|
||||
struct util_list *errors;
|
||||
|
||||
errors = strlist_new();
|
||||
subtype_add_errors(st, id, errors);
|
||||
if (util_list_is_empty(errors)) {
|
||||
strlist_free(errors);
|
||||
errors = NULL;
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
static exit_code_t read_device(struct subtype *st, const char *id,
|
||||
config_t config, struct device **dev_ptr,
|
||||
int reread, read_scope_t scope)
|
||||
{
|
||||
struct device *dev;
|
||||
exit_code_t rc = EXIT_OK;
|
||||
int add, defined;
|
||||
|
||||
dev = device_list_find(st->devices, id, NULL);
|
||||
if (dev) {
|
||||
if (!reread) {
|
||||
if (SCOPE_ACTIVE(config) && dev->active.blacklisted) {
|
||||
/* Reread active device information because
|
||||
* device might have been removed from
|
||||
* blacklist. . */
|
||||
config = config_active;
|
||||
} else
|
||||
goto out;
|
||||
}
|
||||
add = 0;
|
||||
device_reset(dev, config);
|
||||
} else {
|
||||
dev = device_new(st, id);
|
||||
if (!dev)
|
||||
return EXIT_INVALID_ID;
|
||||
add = 1;
|
||||
}
|
||||
|
||||
defined = 0;
|
||||
if (SCOPE_ACTIVE(config)) {
|
||||
if (subtype_device_exists_active(st, id))
|
||||
rc = subtype_device_read_active(st, dev, scope);
|
||||
else if (subtype_device_is_definable(st, id,
|
||||
err_ignore) == EXIT_OK) {
|
||||
rc = subtype_detect_definable(st, dev);
|
||||
defined = 1;
|
||||
}
|
||||
if (rc)
|
||||
goto out;
|
||||
/* Apply attribute value mapping. */
|
||||
setting_list_map_values(dev->active.settings);
|
||||
/* Add blacklisted flag. */
|
||||
if (st->namespace->is_id_blacklisted &&
|
||||
st->namespace->is_id_blacklisted(id))
|
||||
dev->active.blacklisted = 1;
|
||||
}
|
||||
|
||||
if (SCOPE_PERSISTENT(config)) {
|
||||
if (subtype_device_exists_persistent(st, id))
|
||||
rc = subtype_device_read_persistent(st, dev, scope);
|
||||
else if (defined) {
|
||||
/* Need to copy detected settings to persistent
|
||||
* config. */
|
||||
setting_list_merge(dev->persistent.settings,
|
||||
dev->active.settings, false, false);
|
||||
}
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (add)
|
||||
device_list_add(st->devices, dev);
|
||||
|
||||
out:
|
||||
if (rc)
|
||||
device_free(dev);
|
||||
else if (dev_ptr)
|
||||
*dev_ptr = dev;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
exit_code_t subtype_read_device(struct subtype *st, const char *id,
|
||||
config_t config, read_scope_t scope,
|
||||
struct device **dev_ptr)
|
||||
{
|
||||
return read_device(st, id, config, dev_ptr, 0, scope);
|
||||
}
|
||||
|
||||
exit_code_t subtype_reread_device(struct subtype *st, const char *id,
|
||||
config_t config, read_scope_t scope,
|
||||
struct device **dev_ptr)
|
||||
{
|
||||
return read_device(st, id, config, dev_ptr, 1, scope);
|
||||
}
|
||||
|
||||
/* Apply device configuration %dev to the configuration sets %config. */
|
||||
exit_code_t subtype_write_device(struct subtype *st, struct device *dev,
|
||||
config_t config)
|
||||
{
|
||||
exit_code_t rc = EXIT_OK;
|
||||
|
||||
if (SCOPE_ACTIVE(config)) {
|
||||
if (dev->active.deconfigured) {
|
||||
/* Deconfigure device. */
|
||||
if (dev->active.exists) {
|
||||
rc = subtype_device_deconfigure_active(st, dev);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = subtype_device_undefine(st, dev);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
} else if (dev->active.exists || dev->active.definable) {
|
||||
/* Configure device. */
|
||||
if (dev->active.definable) {
|
||||
rc = subtype_device_define(st, dev);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
rc = subtype_device_configure_active(st, dev);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
if (SCOPE_PERSISTENT(config)) {
|
||||
if (dev->persistent.deconfigured) {
|
||||
/* Deconfigure device. */
|
||||
if (dev->persistent.exists) {
|
||||
rc = subtype_device_deconfigure_persistent(st,
|
||||
dev);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
} else if (dev->persistent.exists) {
|
||||
/* Configure device. */
|
||||
rc = subtype_device_configure_persistent(st, dev);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
if (!rc)
|
||||
namespace_set_modified(dev->subtype->namespace);
|
||||
}
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
/* Used for debugging. */
|
||||
void subtype_devices_print_all(void)
|
||||
{
|
||||
int i, j;
|
||||
struct devtype *dt;
|
||||
struct subtype *st;
|
||||
|
||||
for (i = 0; devtypes[i]; i++) {
|
||||
dt = devtypes[i];
|
||||
for (j = 0; dt->subtypes[j]; j++) {
|
||||
st = dt->subtypes[j];
|
||||
device_list_print(st->devices, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Add name of all modules required by subtype @st to strlist @modules. */
|
||||
void subtype_add_static_modules(struct util_list *modules, struct subtype *st)
|
||||
{
|
||||
const char **mod = st->modules;
|
||||
int i;
|
||||
|
||||
if (!mod)
|
||||
return;
|
||||
|
||||
for (i = 0; mod[i]; i++)
|
||||
strlist_add_unique(modules, "%s", mod[i]);
|
||||
}
|
||||
|
||||
void subtype_print(struct subtype *st, int indent)
|
||||
{
|
||||
printf("%*ssubtype at %p\n", indent, "", (void *) st);
|
||||
if (!st)
|
||||
return;
|
||||
indent += 2;
|
||||
printf("%*sname=%s\n", indent, "", st->name);
|
||||
printf("%*sdevices:\n", indent, "");
|
||||
device_list_print(st->devices, indent + 2);
|
||||
}
|
||||
|
||||
static void base_st_init(struct subtype *st)
|
||||
{
|
||||
st->devices = device_list_new(st);
|
||||
}
|
||||
|
||||
static void base_st_exit(struct subtype *st)
|
||||
{
|
||||
device_list_free(st->devices);
|
||||
}
|
||||
|
||||
struct subtype subtype_base = {
|
||||
.init = &base_st_init,
|
||||
.exit = &base_st_exit,
|
||||
};
|
||||
452
zdev/src/table.c
Normal file
452
zdev/src/table.c
Normal file
@@ -0,0 +1,452 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lib/util_base.h"
|
||||
|
||||
#include "misc.h"
|
||||
#include "table.h"
|
||||
|
||||
/* Search for a column in @columns which matches the specified @name. Return
|
||||
* corresponding struct column on success, NULL otherwise. */
|
||||
struct column *table_get_column(struct column *columns, const char *name)
|
||||
{
|
||||
struct column *c;
|
||||
int i;
|
||||
|
||||
for (i = 0; columns[i].name; i++) {
|
||||
c = &columns[i];
|
||||
if (strcasecmp(c->name, name) == 0)
|
||||
return c;
|
||||
if (strchr(c->name, ':') && starts_with_nocase(name, c->name))
|
||||
return c;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* cell - Representation of a single table cell
|
||||
* @id: Column ID of the cell
|
||||
* @heading: Actual column heading
|
||||
* @value: Cell contents
|
||||
* @width: Total width of cell
|
||||
* @align: Alignment of cell text
|
||||
*
|
||||
*/
|
||||
struct cell {
|
||||
char *value;
|
||||
char *heading;
|
||||
int id;
|
||||
int width;
|
||||
align_t align;
|
||||
};
|
||||
|
||||
/* Used for debugging. */
|
||||
void cells_print(struct cell *cells, int indent)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("%*scells at %p\n", indent, "", (void *) cells);
|
||||
if (!cells)
|
||||
return;
|
||||
indent += 2;
|
||||
for (i = 0; cells[i].heading; i++) {
|
||||
printf("%*scells[%d]:\n", indent, "", i);
|
||||
printf("%*svalue=%s\n", indent + 2, "", cells[i].value);
|
||||
printf("%*sheading=%s\n", indent + 2, "", cells[i].heading);
|
||||
printf("%*sid=%d\n", indent + 2, "", cells[i].id);
|
||||
printf("%*swidth=%d\n", indent + 2, "", cells[i].width);
|
||||
printf("%*salign=%d\n", indent + 2, "", cells[i].align);
|
||||
}
|
||||
}
|
||||
|
||||
static void cells_free(struct cell *cells)
|
||||
{
|
||||
int i;
|
||||
struct cell *c;
|
||||
|
||||
if (!cells)
|
||||
return;
|
||||
for (i = 0; cells[i].heading; i++) {
|
||||
c = &cells[i];
|
||||
free(c->value);
|
||||
free(c->heading);
|
||||
}
|
||||
free(cells);
|
||||
}
|
||||
|
||||
/* Return a newly allocated array of struct cells containing a struct cell for
|
||||
* each default column in the specified table. */
|
||||
static struct cell *cells_get_default(struct column *columns)
|
||||
{
|
||||
int i, num;
|
||||
struct cell *cells;
|
||||
|
||||
/* Determine number of default columns. */
|
||||
num = 0;
|
||||
for (i = 0; columns[i].name; i++) {
|
||||
if (!columns[i].def)
|
||||
continue;
|
||||
num++;
|
||||
}
|
||||
|
||||
/* Allocated cells array. */
|
||||
cells = misc_malloc((num + 1) * sizeof(struct cell));
|
||||
|
||||
/* Initialize cells for default columns. */
|
||||
num = 0;
|
||||
for (i = 0; columns[i].name; i++) {
|
||||
if (!columns[i].def)
|
||||
continue;
|
||||
cells[num].heading = misc_strdup(columns[i].name);
|
||||
cells[num].id = columns[i].id;
|
||||
cells[num].align = columns[i].align;
|
||||
num++;
|
||||
}
|
||||
|
||||
return cells;
|
||||
}
|
||||
|
||||
/* Return a newly allocated array of struct cells containing a struct cell for
|
||||
* each column definition whose name was specified in strlist @names. */
|
||||
static struct cell *cells_get(struct column *columns, struct util_list *names)
|
||||
{
|
||||
unsigned long num;
|
||||
struct cell *cells;
|
||||
int i;
|
||||
const struct column *column;
|
||||
struct strlist_node *s;
|
||||
|
||||
num = util_list_len(names);
|
||||
cells = misc_malloc((num + 1) * sizeof(struct cell));
|
||||
|
||||
i = 0;
|
||||
util_list_iterate(names, s) {
|
||||
column = table_get_column(columns, s->str);
|
||||
if (!column)
|
||||
goto err_unknown;
|
||||
cells[i].heading = misc_strdup(s->str);
|
||||
cells[i].id = column->id;
|
||||
cells[i].align = column->align;
|
||||
i++;
|
||||
}
|
||||
|
||||
return cells;
|
||||
|
||||
err_unknown:
|
||||
cells_free(cells);
|
||||
error("Unknown column name specified: %s\n", s->str);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Determine maximum width for values and headings in table. */
|
||||
static void cells_get_width(struct cell *cells, struct util_list *items,
|
||||
table_value_cb_t value_cb, void *data)
|
||||
{
|
||||
int i, len;
|
||||
struct cell *c;
|
||||
struct ptrlist_node *p;
|
||||
char *val;
|
||||
|
||||
/* Initialize widths. */
|
||||
for (i = 0; cells[i].heading; i++)
|
||||
cells[i].width = strlen(cells[i].heading);
|
||||
|
||||
/* Determine largest width from items. */
|
||||
util_list_iterate(items, p) {
|
||||
for (i = 0; cells[i].heading; i++) {
|
||||
c = &cells[i];
|
||||
val = value_cb(p->ptr, c->id, c->heading, data);
|
||||
if (!val)
|
||||
continue;
|
||||
len = strlen(val);
|
||||
free(val);
|
||||
if (c->width < len)
|
||||
c->width = len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the optimal space delimiter for the specified cells. */
|
||||
static const char *cells_get_space(struct cell *cells, struct util_list *items,
|
||||
table_value_cb_t value_cb, void *data,
|
||||
int indent)
|
||||
{
|
||||
char *val;
|
||||
int columns, i, off_one, off_two, last, wrap_one, wrap_two, len;
|
||||
struct ptrlist_node *p;
|
||||
struct cell *c;
|
||||
|
||||
/* Quick exit on special case to make processing simpler. */
|
||||
if (!cells[0].heading)
|
||||
return " ";
|
||||
|
||||
/* Determine column offset of last column. */
|
||||
off_one = indent;
|
||||
off_two = indent;
|
||||
for (i = 0; cells[i + 1].heading; i++) {
|
||||
off_one += cells[i].width + 1;
|
||||
off_two += cells[i].width + 2;
|
||||
}
|
||||
last = i;
|
||||
|
||||
/* Determine how many rows would wrap with either one or two spaces. */
|
||||
columns = get_columns();
|
||||
wrap_one = 0;
|
||||
wrap_two = 0;
|
||||
c = &cells[last];
|
||||
util_list_iterate(items, p) {
|
||||
val = value_cb(p->ptr, c->id, c->heading, data);
|
||||
if (val) {
|
||||
len = strlen(val);
|
||||
free(val);
|
||||
} else
|
||||
len = 0;
|
||||
if (off_one + len >= columns)
|
||||
wrap_one++;
|
||||
if (off_two + len >= columns)
|
||||
wrap_two++;
|
||||
}
|
||||
|
||||
/* Use two spaces if the number of lines that would wrap is the same
|
||||
* for one and two spaces. */
|
||||
if (wrap_two <= wrap_one)
|
||||
return " ";
|
||||
|
||||
return " ";
|
||||
}
|
||||
|
||||
/* Get all values for an item. */
|
||||
static void cells_get_values(struct cell *cells, void *item,
|
||||
table_value_cb_t value_cb, void *data)
|
||||
{
|
||||
int i;
|
||||
struct cell *c;
|
||||
|
||||
for (i = 0; cells[i].heading; i++) {
|
||||
c = &cells[i];
|
||||
free(c->value);
|
||||
c->value = value_cb(item, c->id, c->heading, data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Print all headings. */
|
||||
static void print_heading(struct cell *cells, const char *space, int indent)
|
||||
{
|
||||
int i, width;
|
||||
struct cell *c;
|
||||
|
||||
if (indent > 0)
|
||||
printf("%*s", indent, "");
|
||||
for (i = 0; cells[i].heading; i++) {
|
||||
c = &cells[i];
|
||||
|
||||
/* Don't print spaces in last cell. */
|
||||
if (!cells[i + 1].heading)
|
||||
width = 0;
|
||||
else if (c->align == align_left)
|
||||
width = -c->width;
|
||||
else
|
||||
width = c->width;
|
||||
|
||||
printf("%s%*s", i > 0 ? space : "", width, c->heading);
|
||||
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/* Print all cells in a row in list format. */
|
||||
static void print_row(struct cell *cells, const char *space, int indent,
|
||||
int wrap)
|
||||
{
|
||||
int i, width, columns, offset, slen;
|
||||
struct cell *c;
|
||||
|
||||
columns = get_columns();
|
||||
slen = strlen(space);
|
||||
offset = indent;
|
||||
|
||||
if (indent > 0)
|
||||
printf("%*s", indent, "");
|
||||
|
||||
for (i = 0; cells[i].heading; i++) {
|
||||
c = &cells[i];
|
||||
|
||||
/* Don't print spaces in last cell. */
|
||||
if (!cells[i + 1].heading)
|
||||
width = 0;
|
||||
else if (c->align == align_left)
|
||||
width = -c->width;
|
||||
else
|
||||
width = c->width;
|
||||
|
||||
if (!wrap)
|
||||
goto do_print;
|
||||
|
||||
offset += (i > 0 ? slen : 0);
|
||||
if (c->value)
|
||||
offset += MAX((size_t) abs(width), strlen(c->value));
|
||||
else
|
||||
offset += abs(width);
|
||||
|
||||
if (offset >= columns && i > 0) {
|
||||
/* Printing this cell would wrap the line - start on
|
||||
* the next line, indented to the second column. */
|
||||
offset = indent + cells[0].width;
|
||||
printf("\n%*s", offset, "");
|
||||
if (c->value) {
|
||||
offset += MAX((size_t) abs(width),
|
||||
strlen(c->value));
|
||||
} else
|
||||
offset += abs(width);
|
||||
}
|
||||
|
||||
do_print:
|
||||
printf("%s%*s", i > 0 ? space : "", width,
|
||||
c->value ? c->value : "");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/* Print all cells in a row in pairs format. */
|
||||
static void print_row_pairs(struct cell *cells)
|
||||
{
|
||||
int i;
|
||||
struct cell *c;
|
||||
char *val;
|
||||
|
||||
for (i = 0; cells[i].heading; i++) {
|
||||
c = &cells[i];
|
||||
val = quote_str(c->value ? c->value : "", 1);
|
||||
printf("%s%s=%s", i > 0 ? " " : "", c->heading, val);
|
||||
free(val);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/* Print a table. Either in list form or in pairs form (if @pairs is set).
|
||||
* In list form, print a heading if @heading is set. Show data from items
|
||||
* in ptrlist @items. Specify column names of columns to be printed in
|
||||
* @names. If @wrap is set, wrap overlong lines. , */
|
||||
exit_code_t table_print(struct column *columns, table_value_cb_t get_value_cb,
|
||||
void *data, struct util_list *items,
|
||||
struct util_list *names, int heading, int pairs,
|
||||
int indent, int wrap)
|
||||
{
|
||||
struct cell *cells;
|
||||
struct ptrlist_node *p;
|
||||
const char *space = " ";
|
||||
|
||||
/* Initialize cells array. */
|
||||
if (!names || util_list_is_empty(names))
|
||||
cells = cells_get_default(columns);
|
||||
else {
|
||||
cells = cells_get(columns, names);
|
||||
if (!cells)
|
||||
return EXIT_UNKNOWN_COLUMN;
|
||||
}
|
||||
|
||||
if (!pairs) {
|
||||
cells_get_width(cells, items, get_value_cb, data);
|
||||
if (wrap) {
|
||||
space = cells_get_space(cells, items, get_value_cb,
|
||||
data, indent);
|
||||
} else
|
||||
space = " ";
|
||||
if (heading)
|
||||
print_heading(cells, space, indent);
|
||||
}
|
||||
|
||||
/* Print rows. */
|
||||
util_list_iterate(items, p) {
|
||||
cells_get_values(cells, p->ptr, get_value_cb, data);
|
||||
if (pairs)
|
||||
print_row_pairs(cells);
|
||||
else
|
||||
print_row(cells, space, indent, wrap);
|
||||
}
|
||||
|
||||
cells_free(cells);
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
exit_code_t table_check_columns(struct column *columns, struct util_list *names)
|
||||
{
|
||||
struct cell *cells;
|
||||
|
||||
if (!names || util_list_is_empty(names))
|
||||
return EXIT_OK;
|
||||
|
||||
cells = cells_get(columns, names);
|
||||
if (!cells)
|
||||
return EXIT_UNKNOWN_COLUMN;
|
||||
|
||||
cells_free(cells);
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
enum {
|
||||
column_name,
|
||||
column_desc,
|
||||
};
|
||||
|
||||
static struct column *columns_table = COLUMN_ARRAY(
|
||||
COLUMN("COLUMN", align_left, column_name, 1, ""),
|
||||
COLUMN("DESCRIPTION", align_left, column_desc, 1, "")
|
||||
);
|
||||
|
||||
static char *columns_table_get_value(void *item, int id, const char *heading,
|
||||
void *data)
|
||||
{
|
||||
struct column *c = item;
|
||||
|
||||
switch (id) {
|
||||
case column_name:
|
||||
return misc_strdup(c->name);
|
||||
case column_desc:
|
||||
return misc_strdup(c->desc);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Print a table listing available columns and their description. */
|
||||
void table_print_columns(struct column *columns, struct util_list *names,
|
||||
int heading, int pairs)
|
||||
{
|
||||
int i;
|
||||
struct util_list *items;
|
||||
|
||||
items = ptrlist_new();
|
||||
for (i = 0; columns[i].name; i++)
|
||||
ptrlist_add(items, &columns[i]);
|
||||
table_print(columns_table, columns_table_get_value, NULL, items, names,
|
||||
heading, pairs, 0, 0);
|
||||
ptrlist_free(items, 0);
|
||||
}
|
||||
|
||||
/* Change the default state of column with @id in table @columns to @def. */
|
||||
void table_set_default(struct column *columns, int id, int def)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; columns[i].name; i++) {
|
||||
if (columns[i].id == id) {
|
||||
columns[i].def = def;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
279
zdev/src/table_attribs.c
Normal file
279
zdev/src/table_attribs.c
Normal file
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 "attrib.h"
|
||||
#include "ccw.h"
|
||||
#include "devtype.h"
|
||||
#include "misc.h"
|
||||
#include "subtype.h"
|
||||
#include "table.h"
|
||||
#include "table_attribs.h"
|
||||
|
||||
/* Column IDs for the types table. */
|
||||
enum table_attribs_id {
|
||||
table_attribs_name,
|
||||
table_attribs_desc,
|
||||
};
|
||||
|
||||
/* Definition of output table for --list-types. */
|
||||
static struct column *table_attribs = COLUMN_ARRAY(
|
||||
COLUMN("NAME", align_left, table_attribs_name, 1, ""),
|
||||
COLUMN("DESCRIPTION", align_left, table_attribs_desc, 1, "")
|
||||
);
|
||||
|
||||
/* Return a newly allocated struct table_attrib. */
|
||||
struct table_attrib *table_attrib_new(struct subtype *st, struct attrib *a)
|
||||
{
|
||||
struct table_attrib *t;
|
||||
|
||||
t = misc_malloc(sizeof(struct table_attrib));
|
||||
t->st = st;
|
||||
t->attrib = a;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/* Retrieve value of a cell for devtype/subtype name @item in column @id in the
|
||||
* types table. */
|
||||
static char *table_attribs_get_value(void *item, int id, const char *heading,
|
||||
void *data)
|
||||
{
|
||||
struct table_attrib *t = item;
|
||||
struct attrib *a = t->attrib;
|
||||
|
||||
switch (id) {
|
||||
case table_attribs_name:
|
||||
return misc_strdup(a->name);
|
||||
case table_attribs_desc:
|
||||
return misc_strdup(a->title);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct util_list *get_subtypes(struct util_list *attribs)
|
||||
{
|
||||
struct ptrlist_node *p, *q;
|
||||
struct table_attrib *t;
|
||||
struct util_list *list;
|
||||
|
||||
list = ptrlist_new();
|
||||
|
||||
util_list_iterate(attribs, p) {
|
||||
t = p->ptr;
|
||||
util_list_iterate(list, q) {
|
||||
if (t->st == q->ptr)
|
||||
break;
|
||||
}
|
||||
if (!q)
|
||||
ptrlist_add(list, t->st);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
struct util_list *get_subtype_attribs(struct util_list *all, struct subtype *st)
|
||||
{
|
||||
struct util_list *subattribs;
|
||||
struct ptrlist_node *p;
|
||||
struct table_attrib *t;
|
||||
|
||||
subattribs = ptrlist_new();
|
||||
util_list_iterate(all, p) {
|
||||
t = p->ptr;
|
||||
if (t->st == st)
|
||||
ptrlist_add(subattribs, t);
|
||||
}
|
||||
|
||||
return subattribs;
|
||||
}
|
||||
|
||||
void print_type(struct devtype *dt, struct subtype *st, bool multiple)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!multiple)
|
||||
return;
|
||||
|
||||
if (st)
|
||||
printf("TYPE %s\n", st->name);
|
||||
else {
|
||||
printf("TYPE ");
|
||||
for (i = 0; dt->subtypes[i]; i++) {
|
||||
printf("%s%s", i == 0 ? "" : ", ",
|
||||
dt->subtypes[i]->name);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Display table of attributes. */
|
||||
void table_attribs_show(struct util_list *attribs, int headings, int pairs,
|
||||
struct devtype *dt)
|
||||
{
|
||||
struct util_list *subtypes, *subattribs;
|
||||
struct ptrlist_node *p;
|
||||
struct subtype *st;
|
||||
bool multiple, first;
|
||||
int indent;
|
||||
|
||||
subtypes = get_subtypes(attribs);
|
||||
if (util_list_len(subtypes) > 1) {
|
||||
multiple = true;
|
||||
indent = 2;
|
||||
} else {
|
||||
multiple = false;
|
||||
indent = 0;
|
||||
}
|
||||
|
||||
first = true;
|
||||
util_list_iterate(subtypes, p) {
|
||||
st = p->ptr;
|
||||
subattribs = get_subtype_attribs(attribs, st);
|
||||
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
printf("\n");
|
||||
|
||||
print_type(dt, st, multiple);
|
||||
|
||||
table_print(table_attribs, table_attribs_get_value, st,
|
||||
subattribs, NULL, headings, pairs, indent, 0);
|
||||
|
||||
ptrlist_free(subattribs, 0);
|
||||
}
|
||||
|
||||
ptrlist_free(subtypes, 0);
|
||||
}
|
||||
|
||||
static void table_attribs_show_details_one(struct table_attrib *t,
|
||||
struct devtype *dt, bool multiple)
|
||||
{
|
||||
struct subtype *st = t->st;
|
||||
struct attrib *a = t->attrib;
|
||||
const int i = 2, j = 4;
|
||||
int k;
|
||||
|
||||
printf("ATTRIBUTE %s\n\n", a->name);
|
||||
|
||||
if (multiple) {
|
||||
indent(i, "APPLICABLE TYPES\n");
|
||||
printf("%*s", j, "");
|
||||
if (st)
|
||||
printf("%s", st->name);
|
||||
else {
|
||||
for (k = 0; dt->subtypes[k]; k++) {
|
||||
printf("%s%s", k == 0 ? "" : ", ",
|
||||
dt->subtypes[k]->name);
|
||||
}
|
||||
}
|
||||
printf("\n\n");
|
||||
}
|
||||
|
||||
indent(i, "DESCRIPTION\n");
|
||||
indent(j, "%s", a->desc);
|
||||
|
||||
if (a->defval) {
|
||||
printf("\n");
|
||||
indent(i, "DEFAULT VALUE\n");
|
||||
indent(j, "The default value is '%s'.\n", a->defval);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
indent(i, "ACCEPTED VALUES\n");
|
||||
attrib_print_acceptable(a, j);
|
||||
|
||||
if (!(a->multi || a->activeonly || a->unstable || a->writeonly ||
|
||||
a->rewrite || a->mandatory || a->newline || a->activerem ||
|
||||
a->nounload || a->check))
|
||||
return;
|
||||
|
||||
printf("\n");
|
||||
indent(i, "NOTES\n");
|
||||
if (a->multi) {
|
||||
indent(j, "- This attribute maintains a list of values "
|
||||
"written to it\n");
|
||||
}
|
||||
if (a->activeonly) {
|
||||
indent(j, "- Only specify this attribute in the active "
|
||||
"configuration\n");
|
||||
}
|
||||
if (a->unstable) {
|
||||
indent(j, "- The value read from this attribute is different "
|
||||
"from the last value\n written to it\n");
|
||||
}
|
||||
if (a->writeonly)
|
||||
indent(j, "- You cannot read this attribute\n");
|
||||
if (a->rewrite) {
|
||||
indent(j, "- Setting the same value multiple times may have "
|
||||
"additional effects\n");
|
||||
}
|
||||
if (a->mandatory)
|
||||
indent(j, "- Settings for this attribute cannot be removed\n");
|
||||
if (a->newline) {
|
||||
indent(j, "- A value written to this attribute must be "
|
||||
"followed by a newline character\n");
|
||||
}
|
||||
if (a->activerem) {
|
||||
indent(j, "- Settings for this attribute can be removed in "
|
||||
"the active configuration\n");
|
||||
}
|
||||
if (a->check == ccw_offline_only_check) {
|
||||
indent(j, "- This attribute cannot be changed while the device "
|
||||
"is online\n");
|
||||
}
|
||||
if (a->check == ccw_online_only_check) {
|
||||
indent(j, "- This attribute cannot be changed while the device "
|
||||
"is offline\n");
|
||||
}
|
||||
if (a->nounload) {
|
||||
indent(j, "- Settings for this attribute can be changed "
|
||||
"without reloading the\n associated kernel "
|
||||
"module\n");
|
||||
}
|
||||
}
|
||||
|
||||
static bool check_multiple_types(struct util_list *list)
|
||||
{
|
||||
struct ptrlist_node *p;
|
||||
struct table_attrib *t;
|
||||
struct subtype *st = NULL;
|
||||
bool first;
|
||||
|
||||
first = true;
|
||||
util_list_iterate(list, p) {
|
||||
t = p->ptr;
|
||||
if (first) {
|
||||
first = false;
|
||||
st = t->st;
|
||||
} else if (t->st != st)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Display detailed attribute information*/
|
||||
void table_attribs_show_details(struct util_list *attribs, struct devtype *dt)
|
||||
{
|
||||
struct ptrlist_node *p;
|
||||
bool first, multiple;
|
||||
|
||||
first = true;
|
||||
multiple = check_multiple_types(attribs);
|
||||
util_list_iterate(attribs, p) {
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
printf("\n");
|
||||
|
||||
table_attribs_show_details_one(p->ptr, dt, multiple);
|
||||
}
|
||||
}
|
||||
90
zdev/src/table_types.c
Normal file
90
zdev/src/table_types.c
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 "devtype.h"
|
||||
#include "misc.h"
|
||||
#include "subtype.h"
|
||||
#include "table.h"
|
||||
#include "table_types.h"
|
||||
|
||||
/* Column IDs for the types table. */
|
||||
enum table_types_id {
|
||||
table_types_name,
|
||||
table_types_title,
|
||||
};
|
||||
|
||||
/* Definition of output table for --list-types. */
|
||||
static struct column *table_types = COLUMN_ARRAY(
|
||||
COLUMN("TYPE", align_left, table_types_name, 1, ""),
|
||||
COLUMN("DESCRIPTION", align_left, table_types_title, 1, "")
|
||||
);
|
||||
|
||||
/* Retrieve value of a cell for devtype/subtype name @item in column @id in the
|
||||
* types table. */
|
||||
static char *table_types_get_value(void *item, int id, const char *heading,
|
||||
void *data)
|
||||
{
|
||||
char *name = item;
|
||||
struct devtype *dt;
|
||||
struct subtype *st;
|
||||
|
||||
switch (id) {
|
||||
case table_types_name:
|
||||
return misc_strdup(name);
|
||||
case table_types_title:
|
||||
st = subtype_find(name);
|
||||
if (st)
|
||||
return misc_strdup(st->title);
|
||||
dt = devtype_find(name);
|
||||
if (dt)
|
||||
return misc_strdup(dt->title);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Build list of items in table. Note that we're adding names here instead
|
||||
* of the actual object because of the different types (devtype/subtype). */
|
||||
static struct util_list *table_types_build(void)
|
||||
{
|
||||
struct util_list *items;
|
||||
int i, j;
|
||||
struct devtype *dt;
|
||||
struct subtype *st;
|
||||
|
||||
items = ptrlist_new();
|
||||
for (i = 0; devtypes[i]; i++) {
|
||||
dt = devtypes[i];
|
||||
if (*(dt->title))
|
||||
ptrlist_add(items, misc_strdup(dt->name));
|
||||
for (j = 0; dt->subtypes[j]; j++) {
|
||||
st = dt->subtypes[j];
|
||||
ptrlist_add(items, misc_strdup(st->name));
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
/* Perform --list-types. */
|
||||
exit_code_t table_types_show(struct util_list *columns, int headings, int pairs)
|
||||
{
|
||||
struct util_list *items;
|
||||
exit_code_t rc;
|
||||
|
||||
items = table_types_build();
|
||||
rc = table_print(table_types, table_types_get_value, NULL,
|
||||
items, columns, headings, pairs, 0, 0);
|
||||
ptrlist_free(items, 1);
|
||||
|
||||
return rc;
|
||||
}
|
||||
405
zdev/src/udev.c
Normal file
405
zdev/src/udev.c
Normal file
@@ -0,0 +1,405 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "attrib.h"
|
||||
#include "ccw.h"
|
||||
#include "device.h"
|
||||
#include "misc.h"
|
||||
#include "path.h"
|
||||
#include "setting.h"
|
||||
#include "udev.h"
|
||||
|
||||
int udev_need_settle = 0;
|
||||
|
||||
/* Create a newly allocated udev entry. */
|
||||
static struct udev_entry_node *udev_entry_node_new(const char *key,
|
||||
const char *op,
|
||||
const char *value)
|
||||
{
|
||||
struct udev_entry_node *entry;
|
||||
|
||||
entry = misc_malloc(sizeof(struct udev_entry_node));
|
||||
entry->key = misc_strdup(key);
|
||||
entry->op = misc_strdup(op);
|
||||
entry->value = misc_strdup(value);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/* Release resources associated with udev entry. */
|
||||
static void udev_entry_node_free(struct udev_entry_node *entry)
|
||||
{
|
||||
if (!entry)
|
||||
return;
|
||||
free(entry->key);
|
||||
free(entry->op);
|
||||
free(entry->value);
|
||||
free(entry);
|
||||
}
|
||||
|
||||
/* Create a newly allocated udev line. */
|
||||
static struct udev_line_node *udev_line_node_new(void)
|
||||
{
|
||||
struct udev_line_node *line;
|
||||
|
||||
line = misc_malloc(sizeof(struct udev_line_node));
|
||||
util_list_init(&line->entries, struct udev_entry_node, node);
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
/* Release resources associated with udev line. */
|
||||
static void udev_line_node_free(struct udev_line_node *line)
|
||||
{
|
||||
struct udev_entry_node *e, *n;
|
||||
|
||||
if (!line)
|
||||
return;
|
||||
util_list_iterate_safe(&line->entries, e, n) {
|
||||
util_list_remove(&line->entries, e);
|
||||
udev_entry_node_free(e);
|
||||
}
|
||||
free(line->line);
|
||||
free(line);
|
||||
}
|
||||
|
||||
/* Create a newly allocated udev file. */
|
||||
static struct udev_file *udev_file_new(void)
|
||||
{
|
||||
struct udev_file *file;
|
||||
|
||||
file = misc_malloc(sizeof(struct udev_file));
|
||||
util_list_init(&file->lines, struct udev_line_node, node);
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
/* Release resources associated with udev file. */
|
||||
void udev_free_file(struct udev_file *file)
|
||||
{
|
||||
struct udev_line_node *l, *n;
|
||||
|
||||
if (!file)
|
||||
return;
|
||||
util_list_iterate_safe(&file->lines, l, n) {
|
||||
util_list_remove(&file->lines, l);
|
||||
udev_line_node_free(l);
|
||||
}
|
||||
free(file);
|
||||
}
|
||||
|
||||
/* Used for debugging. */
|
||||
void udev_file_print(struct udev_file *file)
|
||||
{
|
||||
struct udev_line_node *l;
|
||||
struct udev_entry_node *e;
|
||||
|
||||
printf("udev_file at %p\n", (void *) file);
|
||||
if (!file)
|
||||
return;
|
||||
util_list_iterate(&file->lines, l) {
|
||||
printf(" udev_line_node at %p\n", (void *) l);
|
||||
printf(" line='%s'\n", l->line);
|
||||
util_list_iterate(&l->entries, e) {
|
||||
printf(" udev_entry at %p\n", (void *) e);
|
||||
printf(" '%s' '%s' '%s'\n", e->key, e->op,
|
||||
e->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void skip_whitespace(const char **s_ptr)
|
||||
{
|
||||
const char *s = *s_ptr;
|
||||
|
||||
while (*s && isspace(*s))
|
||||
s++;
|
||||
|
||||
*s_ptr = s;
|
||||
}
|
||||
|
||||
static char *parse_key(const char **s_ptr)
|
||||
{
|
||||
const char *s, *e;
|
||||
char *key;
|
||||
|
||||
s = *s_ptr;
|
||||
/* Parse \w+(\{[^\}]*\})? */
|
||||
e = s;
|
||||
while (*e && (isalnum(*e) || *e == '_'))
|
||||
e++;
|
||||
if (*e == '{') {
|
||||
while (*e && *e != '}')
|
||||
e++;
|
||||
if (*e == '}')
|
||||
e++;
|
||||
}
|
||||
|
||||
if (e == s)
|
||||
return NULL;
|
||||
|
||||
/* s points to key start, e to character after key end. */
|
||||
key = misc_malloc(e - s + 1);
|
||||
memcpy(key, s, e - s);
|
||||
|
||||
*s_ptr = e;
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
static char *parse_op(const char **s_ptr)
|
||||
{
|
||||
const char *ops[] = { "==", "!=", "=", "+=", ":=", NULL };
|
||||
const char *entry;
|
||||
size_t len;
|
||||
int i;
|
||||
|
||||
entry = *s_ptr;
|
||||
for (i = 0; ops[i]; i++) {
|
||||
len = strlen(ops[i]);
|
||||
if (strncmp(entry, ops[i], len) == 0) {
|
||||
*s_ptr += len;
|
||||
return misc_strdup(ops[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *parse_value(const char **s_ptr)
|
||||
{
|
||||
const char *s, *e;
|
||||
char *value;
|
||||
|
||||
/* Parse: ^\s*(.*)\s*$ */
|
||||
s = *s_ptr;
|
||||
skip_whitespace(&s);
|
||||
e = s;
|
||||
while (*e)
|
||||
e++;
|
||||
e--;
|
||||
while (e > s && isspace(*e))
|
||||
e--;
|
||||
e++;
|
||||
|
||||
*s_ptr = e;
|
||||
|
||||
/* Remove quotes. */
|
||||
if ((*s == '"' && *(e - 1) == '"') ||
|
||||
(*s == '\'' && *(e - 1) == '\'')) {
|
||||
s++;
|
||||
e--;
|
||||
}
|
||||
|
||||
/* s points to value start, e to character after value end. */
|
||||
value = misc_malloc(e - s + 1);
|
||||
memcpy(value, s, e - s);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static bool parse_udev_entry(struct udev_line_node *line, const char *entry)
|
||||
{
|
||||
char *key = NULL, *op = NULL, *value = NULL;
|
||||
struct udev_entry_node *e;
|
||||
bool rc = false;
|
||||
|
||||
/* Parse: ^\s*(\w+)\s*(==|!=|=|\+=|:=)\s*"?([^"]*)"\s*$ */
|
||||
|
||||
/* Parse key. */
|
||||
skip_whitespace(&entry);
|
||||
key = parse_key(&entry);
|
||||
if (!key)
|
||||
goto out;
|
||||
|
||||
/* Parse operator. */
|
||||
skip_whitespace(&entry);
|
||||
op = parse_op(&entry);
|
||||
if (!op)
|
||||
goto out;
|
||||
|
||||
/* Parse value. */
|
||||
skip_whitespace(&entry);
|
||||
value = parse_value(&entry);
|
||||
if (!value)
|
||||
goto out;
|
||||
skip_whitespace(&entry);
|
||||
|
||||
/* Check for unrecognized characters at end of entry. */
|
||||
if (*entry != 0)
|
||||
goto out;
|
||||
|
||||
/* Add entry to list. */
|
||||
e = udev_entry_node_new(key, op, value);
|
||||
util_list_add_tail(&line->entries, e);
|
||||
rc = true;
|
||||
|
||||
out:
|
||||
free(key);
|
||||
free(op);
|
||||
free(value);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void replace_unquoted(char *s, char from, char to)
|
||||
{
|
||||
char quoted = 0;
|
||||
|
||||
for (; *s; s++) {
|
||||
if (quoted) {
|
||||
/* Skip until quote end is found. */
|
||||
if (*s == quoted)
|
||||
quoted = 0;
|
||||
continue;
|
||||
}
|
||||
if (*s == '"' || *s == '\'') {
|
||||
quoted = *s;
|
||||
continue;
|
||||
}
|
||||
if (*s == from)
|
||||
*s = to;
|
||||
}
|
||||
}
|
||||
|
||||
static bool parse_udev_line(struct udev_file *file, const char *line)
|
||||
{
|
||||
char *copy, *curr, *next;
|
||||
struct udev_line_node *l;
|
||||
int i;
|
||||
bool result = true;
|
||||
|
||||
l = udev_line_node_new();
|
||||
l->line = misc_strdup(line);
|
||||
|
||||
/* Check for empty lines and comment lines. */
|
||||
for (i = 0; line[i] && isspace(line[i]); i++);
|
||||
if (line[i] == 0 || line[i] == '#')
|
||||
goto ok;
|
||||
|
||||
/* Parse each comma-separated entry. */
|
||||
copy = misc_strdup(line);
|
||||
|
||||
/* A hack to differentiate between quoted and unquoted commas. */
|
||||
replace_unquoted(copy, ',', 1);
|
||||
|
||||
next = copy;
|
||||
while ((curr = strsep(&next, "\1"))) {
|
||||
if (!parse_udev_entry(l, curr)) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(copy);
|
||||
|
||||
ok:
|
||||
if (result)
|
||||
util_list_add_tail(&file->lines, l);
|
||||
else
|
||||
udev_line_node_free(l);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Read the contents of a udev rule file. */
|
||||
exit_code_t udev_read_file(const char *path, struct udev_file **file_ptr)
|
||||
{
|
||||
char *text, *curr, *next;
|
||||
struct udev_file *file;
|
||||
int once = 0;
|
||||
|
||||
text = misc_read_text_file(path, 0, err_print);
|
||||
if (!text)
|
||||
return EXIT_RUNTIME_ERROR;
|
||||
file = udev_file_new();
|
||||
|
||||
/* Iterate over each line. */
|
||||
next = text;
|
||||
while ((curr = strsep(&next, "\n"))) {
|
||||
if (parse_udev_line(file, curr))
|
||||
continue;
|
||||
if (!once) {
|
||||
once = 1;
|
||||
verb("Unrecognized udev rule format in %s:\n", path);
|
||||
}
|
||||
verb("%s\n", curr);
|
||||
}
|
||||
|
||||
free(text);
|
||||
*file_ptr = file;
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
static bool get_ids_cb(const char *filename, void *data)
|
||||
{
|
||||
char *prefix = data;
|
||||
|
||||
if (strncmp(filename, prefix, strlen(prefix)) != 0)
|
||||
return false;
|
||||
if (!ends_with(filename, UDEV_SUFFIX))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Add the IDs for all devices of the specified subtype name for which a
|
||||
* udev rule exists to strlist LIST. */
|
||||
void udev_get_device_ids(const char *type, struct util_list *list)
|
||||
{
|
||||
char *path, *prefix;
|
||||
struct util_list *files;
|
||||
struct strlist_node *s;
|
||||
size_t plen, len;
|
||||
|
||||
prefix = misc_asprintf("%s-%s-", UDEV_PREFIX, type);
|
||||
plen = strlen(prefix);
|
||||
path = path_get_udev_rules();
|
||||
files = strlist_new();
|
||||
if (!misc_read_dir(path, files, get_ids_cb, prefix))
|
||||
goto out;
|
||||
|
||||
util_list_iterate(files, s) {
|
||||
/* 41-dasd-eckd-0.0.1234.rules */
|
||||
len = strlen(s->str);
|
||||
s->str[len - sizeof(UDEV_SUFFIX) + 1] = 0;
|
||||
strlist_add(list, &s->str[plen]);
|
||||
}
|
||||
|
||||
out:
|
||||
strlist_free(files);
|
||||
free(path);
|
||||
free(prefix);
|
||||
}
|
||||
|
||||
/* Remove UDEV rule for device. */
|
||||
exit_code_t udev_remove_rule(const char *type, const char *id)
|
||||
{
|
||||
char *path;
|
||||
exit_code_t rc = EXIT_OK;
|
||||
|
||||
path = path_get_udev_rule(type, id);
|
||||
if (file_exists(path))
|
||||
rc = remove_file(path);
|
||||
free(path);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Wait for all current udev events to finish. */
|
||||
void udev_settle(void)
|
||||
{
|
||||
misc_system(err_ignore, "%s settle", PATH_UDEVADM);
|
||||
}
|
||||
288
zdev/src/udev_ccw.c
Normal file
288
zdev/src/udev_ccw.c
Normal file
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "attrib.h"
|
||||
#include "ccw.h"
|
||||
#include "device.h"
|
||||
#include "misc.h"
|
||||
#include "path.h"
|
||||
#include "setting.h"
|
||||
#include "udev.h"
|
||||
#include "udev_ccw.h"
|
||||
|
||||
/* Check if a udev rule for the specified ccw device exists. */
|
||||
bool udev_ccw_exists(const char *type, const char *id)
|
||||
{
|
||||
char *path, *normid;
|
||||
bool rc;
|
||||
|
||||
normid = ccw_normalize_id(id);
|
||||
if (!normid)
|
||||
return false;
|
||||
|
||||
path = path_get_udev_rule(type, normid);
|
||||
rc = file_exists(path);
|
||||
free(path);
|
||||
free(normid);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void add_setting_from_entry(struct setting_list *list,
|
||||
struct udev_entry_node *entry,
|
||||
struct attrib **attribs)
|
||||
{
|
||||
char *copy, *name, *end;
|
||||
struct attrib *a;
|
||||
|
||||
/* ATTR{[ccw/0.0.37bf]online}=1 */
|
||||
if (strncmp(entry->key, "ATTR{[ccw/", 10) != 0 ||
|
||||
strcmp(entry->op, "=") != 0)
|
||||
return;
|
||||
copy = misc_strdup(entry->key);
|
||||
name = copy;
|
||||
|
||||
/* Find attribute name start. */
|
||||
name = strchr(entry->key, ']');
|
||||
end = strrchr(entry->key, '}');
|
||||
if (!name || !end)
|
||||
goto out;
|
||||
*end = 0;
|
||||
name++;
|
||||
|
||||
a = attrib_find(attribs, name);
|
||||
setting_list_apply_actual(list, a, name, entry->value);
|
||||
|
||||
out:
|
||||
free(copy);
|
||||
}
|
||||
|
||||
/* Extract CCW device settings from a CCW device udev rule file. */
|
||||
static void udev_file_get_settings(struct udev_file *file,
|
||||
struct attrib **attribs,
|
||||
struct setting_list *list)
|
||||
{
|
||||
struct udev_line_node *line;
|
||||
struct udev_entry_node *entry;
|
||||
|
||||
util_list_iterate(&file->lines, line) {
|
||||
entry = util_list_start(&line->entries);
|
||||
if (!entry)
|
||||
continue;
|
||||
add_setting_from_entry(list, entry, attribs);
|
||||
}
|
||||
}
|
||||
|
||||
/* Read the persistent configuration of a CCW device from a udev rule. */
|
||||
exit_code_t udev_ccw_read_device(struct device *dev)
|
||||
{
|
||||
struct subtype *st = dev->subtype;
|
||||
struct device_state *state = &dev->persistent;
|
||||
struct udev_file *file = NULL;
|
||||
exit_code_t rc;
|
||||
char *path;
|
||||
|
||||
path = path_get_udev_rule(st->name, dev->id);
|
||||
rc = udev_read_file(path, &file);
|
||||
if (rc)
|
||||
goto out;
|
||||
udev_file_get_settings(file, st->dev_attribs, state->settings);
|
||||
state->exists = 1;
|
||||
udev_free_file(file);
|
||||
|
||||
out:
|
||||
free(path);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Return an ID suitable for use as udev label. */
|
||||
static char *get_label_id(const char *prefix, const char *type,
|
||||
const char *dev_id)
|
||||
{
|
||||
char *id;
|
||||
int i;
|
||||
|
||||
id = misc_asprintf("%s_%s_%s", prefix, type, dev_id);
|
||||
for (i = 0; id[i]; i++) {
|
||||
if (isalnum(id[i]) || id[i] == '_' || id[i] == '.')
|
||||
continue;
|
||||
id[i] = '_';
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
/* Write the persistent configuration of a CCW device to a udev rule. */
|
||||
exit_code_t udev_ccw_write_device(struct device *dev)
|
||||
{
|
||||
struct subtype *st = dev->subtype;
|
||||
struct ccw_subtype_data *data = st->data;
|
||||
const char *type = st->name, *drv = data->ccwdrv, *id = dev->id;
|
||||
struct device_state *state = &dev->persistent;
|
||||
char *path, *cfg_label = NULL, *end_label = NULL;
|
||||
struct util_list *list;
|
||||
struct ptrlist_node *p;
|
||||
struct setting *s;
|
||||
exit_code_t rc = EXIT_OK;
|
||||
FILE *fd;
|
||||
|
||||
if (!state->exists)
|
||||
return udev_remove_rule(type, id);
|
||||
|
||||
cfg_label = get_label_id("cfg", type, id);
|
||||
end_label = get_label_id("end", type, id);
|
||||
|
||||
/* Apply attributes in correct order. */
|
||||
list = setting_list_get_sorted(state->settings);
|
||||
|
||||
path = path_get_udev_rule(type, id);
|
||||
debug("Writing %s udev rule file %s\n", type, path);
|
||||
if (!path_exists(path)) {
|
||||
rc = path_create(path);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
fd = misc_fopen(path, "w");
|
||||
if (!fd) {
|
||||
error("Could not write to file %s: %s\n", path,
|
||||
strerror(errno));
|
||||
rc = EXIT_RUNTIME_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Write udev rule prolog. */
|
||||
fprintf(fd, "# Generated by chzdev\n");
|
||||
if (drv) {
|
||||
fprintf(fd, "ACTION==\"add\", SUBSYSTEM==\"ccw\", "
|
||||
"KERNEL==\"%s\", DRIVER==\"%s\", GOTO=\"%s\"\n", id,
|
||||
drv, cfg_label);
|
||||
fprintf(fd, "ACTION==\"add\", SUBSYSTEM==\"drivers\", "
|
||||
"KERNEL==\"%s\", TEST==\"[ccw/%s]\", "
|
||||
"GOTO=\"%s\"\n", drv, id, cfg_label);
|
||||
} else {
|
||||
fprintf(fd, "ACTION==\"add\", SUBSYSTEM==\"ccw\", "
|
||||
"KERNEL==\"%s\", GOTO=\"%s\"\n", id, cfg_label);
|
||||
}
|
||||
fprintf(fd, "GOTO=\"%s\"\n", end_label);
|
||||
fprintf(fd, "\n");
|
||||
fprintf(fd, "LABEL=\"%s\"\n", cfg_label);
|
||||
|
||||
/* Write settings. */
|
||||
util_list_iterate(list, p) {
|
||||
s = p->ptr;
|
||||
if (s->removed)
|
||||
continue;
|
||||
fprintf(fd, "ATTR{[ccw/%s]%s}=\"%s\"\n", id, s->name, s->value);
|
||||
}
|
||||
|
||||
/* Write udev rule epilog. */
|
||||
fprintf(fd, "\n");
|
||||
fprintf(fd, "LABEL=\"%s\"\n", end_label);
|
||||
|
||||
if (misc_fclose(fd))
|
||||
warn("Could not close file %s: %s\n", path, strerror(errno));
|
||||
|
||||
out:
|
||||
ptrlist_free(list, 0);
|
||||
free(end_label);
|
||||
free(cfg_label);
|
||||
free(path);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#define MARKER "echo free "
|
||||
|
||||
static char *read_cio_ignore(const char *path)
|
||||
{
|
||||
char *text, *start, *end, *result = NULL;
|
||||
|
||||
text = misc_read_text_file(path, 0, err_ignore);
|
||||
if (!text)
|
||||
goto out;
|
||||
|
||||
start = strstr(text, MARKER);
|
||||
if (!start)
|
||||
goto out;
|
||||
start += strlen(MARKER);
|
||||
end = strchr(start, ' ');
|
||||
if (!end)
|
||||
goto out;
|
||||
*end = 0;
|
||||
result = misc_strdup(start);
|
||||
|
||||
out:
|
||||
free(text);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Write a udev rule to free devices from the cio-ignore blacklist. */
|
||||
exit_code_t udev_ccw_write_cio_ignore(const char *id_list)
|
||||
{
|
||||
char *path, *curr = NULL;
|
||||
FILE *fd;
|
||||
exit_code_t rc = EXIT_OK;
|
||||
|
||||
/* Create file. */
|
||||
path = path_get_udev_rule("cio-ignore", NULL);
|
||||
|
||||
if (!*id_list) {
|
||||
/* Empty id_list string - remove file. */
|
||||
if (!file_exists(path)) {
|
||||
/* Already removed. */
|
||||
goto out;
|
||||
}
|
||||
rc = remove_file(path);
|
||||
goto out;
|
||||
}
|
||||
|
||||
curr = read_cio_ignore(path);
|
||||
if (curr && strcmp(curr, id_list) == 0)
|
||||
goto out;
|
||||
|
||||
debug("Writing cio-ignore udev rule file %s\n", path);
|
||||
if (!path_exists(path)) {
|
||||
rc = path_create(path);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
fd = misc_fopen(path, "w");
|
||||
if (!fd) {
|
||||
error("Could not write to file %s: %s\n", path,
|
||||
strerror(errno));
|
||||
rc = EXIT_RUNTIME_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Write udev rule. */
|
||||
fprintf(fd, "# Generated by chzdev\n");
|
||||
fprintf(fd, "ACTION==\"add\", SUBSYSTEM==\"subsystem\", "
|
||||
"KERNEL==\"ccw\", RUN{program}+=\"/bin/sh -c "
|
||||
"'echo free %s > /proc/cio_ignore'\"\n", id_list);
|
||||
|
||||
/* Close file. */
|
||||
if (misc_fclose(fd))
|
||||
warn("Could not close file %s: %s\n", path, strerror(errno));
|
||||
|
||||
out:
|
||||
free(curr);
|
||||
free(path);
|
||||
|
||||
return rc;
|
||||
}
|
||||
391
zdev/src/udev_ccwgroup.c
Normal file
391
zdev/src/udev_ccwgroup.c
Normal file
@@ -0,0 +1,391 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "attrib.h"
|
||||
#include "ccwgroup.h"
|
||||
#include "device.h"
|
||||
#include "misc.h"
|
||||
#include "path.h"
|
||||
#include "setting.h"
|
||||
#include "udev.h"
|
||||
#include "udev_ccwgroup.h"
|
||||
|
||||
static char *get_rule_path_by_devid(const char *type,
|
||||
struct ccwgroup_devid *devid)
|
||||
{
|
||||
char *ccw_id, *path;
|
||||
|
||||
ccw_id = ccw_devid_to_str(&devid->devid[0]);
|
||||
path = path_get_udev_rule(type, ccw_id);
|
||||
free(ccw_id);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
static char *get_rule_path(const char *type, const char *id)
|
||||
{
|
||||
struct ccwgroup_devid devid;
|
||||
|
||||
if (ccwgroup_parse_devid(&devid, id, err_ignore) != EXIT_OK)
|
||||
return NULL;
|
||||
|
||||
return get_rule_path_by_devid(type, &devid);
|
||||
}
|
||||
|
||||
/* Check if a udev rule for the specified CCWGROUP device exists. */
|
||||
bool udev_ccwgroup_exists(const char *type, const char *id)
|
||||
{
|
||||
char *path;
|
||||
bool rc;
|
||||
|
||||
path = get_rule_path(type, id);
|
||||
if (!path)
|
||||
return false;
|
||||
rc = file_exists(path);
|
||||
free(path);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void add_setting_from_entry(struct setting_list *list,
|
||||
struct udev_entry_node *entry,
|
||||
struct attrib **attribs)
|
||||
{
|
||||
char *copy, *name, *end;
|
||||
struct attrib *a;
|
||||
|
||||
/* ATTR{[ccwgroup/0.0.f5f0]online}=1 */
|
||||
if (strncmp(entry->key, "ATTR{[ccwgroup/", 10) != 0 ||
|
||||
strcmp(entry->op, "=") != 0)
|
||||
return;
|
||||
copy = misc_strdup(entry->key);
|
||||
name = copy;
|
||||
|
||||
/* Find attribute name start. */
|
||||
name = strchr(entry->key, ']');
|
||||
end = strrchr(entry->key, '}');
|
||||
if (!name || !end)
|
||||
goto out;
|
||||
*end = 0;
|
||||
name++;
|
||||
|
||||
a = attrib_find(attribs, name);
|
||||
setting_list_apply_actual(list, a, name, entry->value);
|
||||
|
||||
out:
|
||||
free(copy);
|
||||
}
|
||||
|
||||
/* Extract CCWGROUP device settings from a CCWGROUP device udev rule file. */
|
||||
static void udev_file_get_settings(struct udev_file *file,
|
||||
struct attrib **attribs,
|
||||
struct setting_list *list)
|
||||
{
|
||||
struct udev_line_node *line;
|
||||
struct udev_entry_node *entry;
|
||||
|
||||
util_list_iterate(&file->lines, line) {
|
||||
entry = util_list_start(&line->entries);
|
||||
if (!entry)
|
||||
continue;
|
||||
add_setting_from_entry(list, entry, attribs);
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine full CCWGROUP from data in udev rule file. */
|
||||
static void expand_id(struct device *dev, struct udev_file *file)
|
||||
{
|
||||
struct ccwgroup_devid devid;
|
||||
struct ccwgroup_devid *devid_ptr = dev->devid;
|
||||
struct udev_line_node *line;
|
||||
struct udev_entry_node *entry;
|
||||
char *id;
|
||||
int i;
|
||||
|
||||
if (devid_ptr->num != 1)
|
||||
return;
|
||||
|
||||
util_list_iterate(&file->lines, line) {
|
||||
entry = util_list_start(&line->entries);
|
||||
if (!entry)
|
||||
continue;
|
||||
if (!starts_with(entry->key, "ATTR{[drivers/ccwgroup:") ||
|
||||
!ends_with(entry->key, "]group}"))
|
||||
continue;
|
||||
/* Extract CCWGROUP ID from comma-separated list of CCW
|
||||
* device IDs. */
|
||||
id = misc_strdup(entry->value);
|
||||
for (i = 0; id[i]; i++) {
|
||||
if (id[i] == ',')
|
||||
id[i] = ':';
|
||||
}
|
||||
if (ccwgroup_parse_devid(&devid, id, err_ignore) == EXIT_OK)
|
||||
*devid_ptr = devid;
|
||||
free(dev->id);
|
||||
dev->id = ccwgroup_devid_to_str(&devid);
|
||||
free(id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read the persistent configuration of a CCWGROUP device from a udev rule. */
|
||||
exit_code_t udev_ccwgroup_read_device(struct device *dev)
|
||||
{
|
||||
struct subtype *st = dev->subtype;
|
||||
struct device_state *state = &dev->persistent;
|
||||
struct udev_file *file = NULL;
|
||||
exit_code_t rc;
|
||||
char *path;
|
||||
|
||||
path = get_rule_path_by_devid(st->name, dev->devid);
|
||||
rc = udev_read_file(path, &file);
|
||||
if (rc)
|
||||
goto out;
|
||||
udev_file_get_settings(file, st->dev_attribs, state->settings);
|
||||
expand_id(dev, file);
|
||||
state->exists = 1;
|
||||
udev_free_file(file);
|
||||
|
||||
out:
|
||||
free(path);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Return an ID suitable for use as udev label. */
|
||||
static char *get_label_id(const char *prefix, const char *type,
|
||||
const char *dev_id)
|
||||
{
|
||||
char *id;
|
||||
int i;
|
||||
|
||||
id = misc_asprintf("%s_%s_%s", prefix, type, dev_id);
|
||||
for (i = 0; id[i]; i++) {
|
||||
if (isalnum(id[i]) || id[i] == '_' || id[i] == '.')
|
||||
continue;
|
||||
id[i] = '_';
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
/* Write the persistent configuration of a CCWGROUP device to a udev rule. */
|
||||
exit_code_t udev_ccwgroup_write_device(struct device *dev)
|
||||
{
|
||||
struct subtype *st = dev->subtype;
|
||||
struct ccwgroup_subtype_data *data = st->data;
|
||||
const char *type = st->name, *drv = data->ccwgroupdrv, *id = dev->id;
|
||||
struct ccwgroup_devid devid;
|
||||
char *path, *cfg_label = NULL, *group_label = NULL, *end_label = NULL,
|
||||
*ccw_id, *chan_id;
|
||||
struct util_list *list;
|
||||
struct ptrlist_node *p;
|
||||
struct setting *s;
|
||||
struct strlist_node *str;
|
||||
exit_code_t rc = EXIT_OK;
|
||||
FILE *fd;
|
||||
unsigned int i;
|
||||
|
||||
if (!dev->persistent.exists)
|
||||
return udev_ccwgroup_remove_rule(type, id);
|
||||
|
||||
if (ccwgroup_parse_devid(&devid, id, err_ignore) != EXIT_OK)
|
||||
return EXIT_INVALID_ID;
|
||||
|
||||
ccw_id = ccw_devid_to_str(&devid.devid[0]);
|
||||
path = get_rule_path(type, ccw_id);
|
||||
|
||||
group_label = get_label_id("group", type, ccw_id);
|
||||
cfg_label = get_label_id("cfg", type, ccw_id);
|
||||
end_label = get_label_id("end", type, ccw_id);
|
||||
|
||||
/* Apply attributes in correct order. */
|
||||
list = setting_list_get_sorted(dev->persistent.settings);
|
||||
|
||||
debug("Writing %s udev rule file %s\n", type, path);
|
||||
if (!path_exists(path)) {
|
||||
rc = path_create(path);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
fd = misc_fopen(path, "w");
|
||||
if (!fd) {
|
||||
error("Could not write to file %s: %s\n", path,
|
||||
strerror(errno));
|
||||
rc = EXIT_RUNTIME_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Write udev rule prolog. */
|
||||
fprintf(fd, "# Generated by chzdev\n");
|
||||
|
||||
/* Triggers. */
|
||||
fprintf(fd, "ACTION==\"add\", SUBSYSTEM==\"drivers\", "
|
||||
"KERNEL==\"%s\", GOTO=\"%s\"\n", drv, group_label);
|
||||
for (i = 0; i < devid.num; i++) {
|
||||
chan_id = ccw_devid_to_str(&devid.devid[i]);
|
||||
fprintf(fd, "ACTION==\"add\", SUBSYSTEM==\"ccw\", "
|
||||
"KERNEL==\"%s\", DRIVER==\"%s\", "
|
||||
"GOTO=\"%s\"\n", chan_id, drv, group_label);
|
||||
free(chan_id);
|
||||
}
|
||||
fprintf(fd, "ACTION==\"add\", SUBSYSTEM==\"ccwgroup\", "
|
||||
"KERNEL==\"%s\", DRIVER==\"%s\", GOTO=\"%s\"\n", ccw_id,
|
||||
drv, cfg_label);
|
||||
fprintf(fd, "GOTO=\"%s\"\n\n", end_label);
|
||||
|
||||
/* Group. */
|
||||
fprintf(fd, "LABEL=\"%s\"\n", group_label);
|
||||
fprintf(fd, "TEST==\"[ccwgroup/%s]\", GOTO=\"%s\"\n", ccw_id,
|
||||
end_label);
|
||||
for (i = 0; i < devid.num; i++) {
|
||||
chan_id = ccw_devid_to_str(&devid.devid[i]);
|
||||
fprintf(fd, "TEST!=\"[ccw/%s]\", GOTO=\"%s\"\n", chan_id,
|
||||
end_label);
|
||||
free(chan_id);
|
||||
}
|
||||
fprintf(fd, "ATTR{[drivers/ccwgroup:%s]group}=\"", drv);
|
||||
for (i = 0; i < devid.num; i++) {
|
||||
chan_id = ccw_devid_to_str(&devid.devid[i]);
|
||||
fprintf(fd, "%s%s", i > 0 ? "," : "", chan_id);
|
||||
free(chan_id);
|
||||
}
|
||||
fprintf(fd, "\"\n");
|
||||
fprintf(fd, "GOTO=\"%s\"\n\n", end_label);
|
||||
|
||||
/* Configure. */
|
||||
fprintf(fd, "LABEL=\"%s\"\n", cfg_label);
|
||||
util_list_iterate(list, p) {
|
||||
s = p->ptr;
|
||||
if (s->removed)
|
||||
continue;
|
||||
if (s->values) {
|
||||
util_list_iterate(s->values, str) {
|
||||
fprintf(fd, "ATTR{[ccwgroup/%s]%s}=\"%s\"\n",
|
||||
ccw_id, s->name, str->str);
|
||||
}
|
||||
} else {
|
||||
fprintf(fd, "ATTR{[ccwgroup/%s]%s}=\"%s\"\n", ccw_id,
|
||||
s->name, s->value);
|
||||
}
|
||||
}
|
||||
|
||||
/* Write udev rule epilog. */
|
||||
fprintf(fd, "\n");
|
||||
fprintf(fd, "LABEL=\"%s\"\n", end_label);
|
||||
|
||||
if (misc_fclose(fd))
|
||||
warn("Could not close file %s: %s\n", path, strerror(errno));
|
||||
|
||||
out:
|
||||
ptrlist_free(list, 0);
|
||||
free(end_label);
|
||||
free(cfg_label);
|
||||
free(group_label);
|
||||
free(path);
|
||||
free(ccw_id);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static char *read_full_id(const char *path)
|
||||
{
|
||||
char *text, *start, *end, *id = NULL;
|
||||
|
||||
text = misc_read_text_file(path, 0, err_ignore);
|
||||
if (!text)
|
||||
return NULL;
|
||||
start = strstr(text, "ATTR{[drivers/ccwgroup:");
|
||||
if (!start)
|
||||
goto out;
|
||||
start = strchr(start, '"');
|
||||
if (!start)
|
||||
goto out;
|
||||
start++;
|
||||
end = strchr(start, '"');
|
||||
if (!end)
|
||||
goto out;
|
||||
*end = 0;
|
||||
id = misc_strdup(start);
|
||||
|
||||
start = id;
|
||||
while ((start = strchr(start, ',')))
|
||||
*start = ':';
|
||||
|
||||
out:
|
||||
free(text);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
struct get_ids_cb_data {
|
||||
char *prefix;
|
||||
struct util_list *ids;
|
||||
};
|
||||
|
||||
static exit_code_t get_ids_cb(const char *path, const char *filename,
|
||||
void *data)
|
||||
{
|
||||
struct get_ids_cb_data *cb_data = data;
|
||||
char *id;
|
||||
|
||||
if (!starts_with(filename, cb_data->prefix))
|
||||
return EXIT_OK;
|
||||
id = read_full_id(path);
|
||||
if (!id)
|
||||
return EXIT_OK;
|
||||
strlist_add(cb_data->ids, id);
|
||||
free(id);
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
/* Add the IDs for all devices of the specified subtype name for which a
|
||||
* udev rule exists to strlist LIST. */
|
||||
void udev_ccwgroup_add_device_ids(const char *type, struct util_list *list)
|
||||
{
|
||||
struct get_ids_cb_data cb_data;
|
||||
char *path;
|
||||
|
||||
path = path_get_udev_rules();
|
||||
cb_data.prefix = misc_asprintf("%s-%s-", UDEV_PREFIX, type);
|
||||
cb_data.ids = list;
|
||||
|
||||
if (dir_exists(path))
|
||||
path_for_each(path, get_ids_cb, &cb_data);
|
||||
|
||||
free(cb_data.prefix);
|
||||
free(path);
|
||||
}
|
||||
|
||||
/* Remove UDEV rule for CCWGROUP device. */
|
||||
exit_code_t udev_ccwgroup_remove_rule(const char *type, const char *id)
|
||||
{
|
||||
char *partial_id, *path;
|
||||
exit_code_t rc = EXIT_OK;
|
||||
|
||||
partial_id = ccwgroup_get_partial_id(id);
|
||||
if (!partial_id)
|
||||
return EXIT_INVALID_ID;
|
||||
|
||||
path = path_get_udev_rule(type, partial_id);
|
||||
if (file_exists(path))
|
||||
rc = remove_file(path);
|
||||
free(path);
|
||||
free(partial_id);
|
||||
|
||||
return rc;
|
||||
}
|
||||
687
zdev/src/udev_zfcp_lun.c
Normal file
687
zdev/src/udev_zfcp_lun.c
Normal file
@@ -0,0 +1,687 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "attrib.h"
|
||||
#include "device.h"
|
||||
#include "misc.h"
|
||||
#include "path.h"
|
||||
#include "scsi.h"
|
||||
#include "setting.h"
|
||||
#include "udev.h"
|
||||
#include "udev_zfcp_lun.h"
|
||||
#include "zfcp_lun.h"
|
||||
|
||||
#define LABEL_START "start_zfcp_lun_"
|
||||
#define LABEL_END "end_zfcp_lun_"
|
||||
#define LABEL_FC "cfg_fc_"
|
||||
#define LABEL_SCSI "cfg_scsi_"
|
||||
|
||||
struct zfcp_lun_node {
|
||||
struct util_list_node node;
|
||||
struct zfcp_lun_devid id;
|
||||
struct setting_list *fc_settings;
|
||||
struct setting_list *scsi_settings;
|
||||
};
|
||||
|
||||
static void zfcp_lun_node_print(struct zfcp_lun_node *node, int indent)
|
||||
{
|
||||
char *id;
|
||||
|
||||
printf("%*szfcp_lun_node at %p\n", indent, "", (void *) node);
|
||||
if (!node)
|
||||
return;
|
||||
indent += 2;
|
||||
id = zfcp_lun_devid_to_str(&node->id);
|
||||
printf("%*sid=%s\n", indent, "", id);
|
||||
free(id);
|
||||
printf("%*sfc_settings:\n", indent, "");
|
||||
setting_list_print(node->fc_settings, indent + 2);
|
||||
printf("%*sscsi_settings:\n", indent, "");
|
||||
setting_list_print(node->scsi_settings, indent + 2);
|
||||
}
|
||||
|
||||
static struct zfcp_lun_node *zfcp_lun_node_new(struct zfcp_lun_devid *id)
|
||||
{
|
||||
struct zfcp_lun_node *node;
|
||||
|
||||
node = misc_malloc(sizeof(struct zfcp_lun_node));
|
||||
node->id = *id;
|
||||
node->fc_settings = setting_list_new();
|
||||
node->scsi_settings = setting_list_new();
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static void zfcp_lun_node_free(struct zfcp_lun_node *node)
|
||||
{
|
||||
if (!node)
|
||||
return;
|
||||
setting_list_free(node->fc_settings);
|
||||
setting_list_free(node->scsi_settings);
|
||||
free(node);
|
||||
}
|
||||
|
||||
static struct zfcp_lun_node *zfcp_lun_node_find(struct util_list *list,
|
||||
struct zfcp_lun_devid *id)
|
||||
{
|
||||
struct zfcp_lun_node *node;
|
||||
|
||||
util_list_iterate(list, node) {
|
||||
if (zfcp_lun_cmp_devids(&node->id, id) == 0)
|
||||
return node;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct util_list *zfcp_lun_node_list_new(void)
|
||||
{
|
||||
struct util_list *list;
|
||||
|
||||
list = misc_malloc(sizeof(struct util_list));
|
||||
util_list_init(list, struct zfcp_lun_node, node);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static void zfcp_lun_node_list_free(struct util_list *list)
|
||||
{
|
||||
struct zfcp_lun_node *n, *p;
|
||||
|
||||
if (!list)
|
||||
return;
|
||||
util_list_iterate_safe(list, n, p) {
|
||||
util_list_remove(list, n);
|
||||
zfcp_lun_node_free(n);
|
||||
}
|
||||
free(list);
|
||||
}
|
||||
|
||||
/* Used for debugging. */
|
||||
void zfcp_lun_node_list_print(struct util_list *list, int indent)
|
||||
{
|
||||
struct zfcp_lun_node *n;
|
||||
|
||||
printf("%*szfcp_lun_node_list at %p\n", indent, "", (void *) list);
|
||||
if (!list)
|
||||
return;
|
||||
util_list_iterate(list, n)
|
||||
zfcp_lun_node_print(n, indent + 2);
|
||||
}
|
||||
|
||||
static bool zfcp_lun_devid_from_entry(struct zfcp_lun_devid *id_ptr,
|
||||
struct udev_entry_node *entry)
|
||||
{
|
||||
struct zfcp_lun_devid id;
|
||||
char *copy = NULL, *s;
|
||||
int i;
|
||||
bool rc = false;
|
||||
|
||||
/* LABEL="cfg_scsi_0.0.1941_0x500507630510c1ae_0x402340d400000000" */
|
||||
if (strcmp(entry->key, "LABEL") == 0 &&
|
||||
starts_with(entry->value, LABEL_SCSI)) {
|
||||
copy = misc_strdup(entry->value);
|
||||
s = copy + strlen(LABEL_SCSI);
|
||||
for (i = 0; s[i]; i++) {
|
||||
if (s[i] == '_')
|
||||
s[i] = ':';
|
||||
}
|
||||
rc = zfcp_lun_parse_devid(&id, s, err_ignore) == EXIT_OK ?
|
||||
true : false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*ATTR{[ccw/0.0.1941]0x500507630510c1ae/unit_add}="0x402340d400000000"*/
|
||||
if (starts_with(entry->key, "ATTR{[ccw/") &&
|
||||
ends_with(entry->key, "/unit_add}")) {
|
||||
copy = misc_asprintf("%s%s", entry->key, entry->value);
|
||||
s = copy + strlen("ATTR{[ccw/");
|
||||
for (i = 0; s[i]; i++) {
|
||||
if (s[i] == ']')
|
||||
s[i] = ':';
|
||||
else if (s[i] == '/')
|
||||
break;
|
||||
}
|
||||
if (s[i] != '/')
|
||||
goto out;
|
||||
s[i] = ':';
|
||||
strcpy(s + i + 1, entry->value);
|
||||
rc = zfcp_lun_parse_devid(&id, s, err_ignore) == EXIT_OK ?
|
||||
true : false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*ATTR{[ccw/0.0.1941]0x500507630510c1ae/0x402340d400000000/failed}="0"*/
|
||||
if (starts_with(entry->key, "ATTR{[ccw/")) {
|
||||
copy = misc_strdup(entry->key);
|
||||
s = strrchr(copy, '/');
|
||||
if (s)
|
||||
*s = 0;
|
||||
s = copy + strlen("ATTR{[ccw/");
|
||||
for (i = 0; s[i]; i++) {
|
||||
if (s[i] == ']' || s[i] == '/')
|
||||
s[i] = ':';
|
||||
}
|
||||
rc = zfcp_lun_parse_devid(&id, s, err_ignore) == EXIT_OK ?
|
||||
true : false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
free(copy);
|
||||
if (rc)
|
||||
*id_ptr = id;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct zfcp_lun_node *zfcp_lun_node_from_entry(
|
||||
struct udev_entry_node *entry,
|
||||
struct zfcp_lun_node *old,
|
||||
struct util_list *list)
|
||||
{
|
||||
struct zfcp_lun_devid id;
|
||||
struct zfcp_lun_node *node;
|
||||
|
||||
if (!zfcp_lun_devid_from_entry(&id, entry))
|
||||
return old;
|
||||
if (old && zfcp_lun_cmp_devids(&old->id, &id) == 0)
|
||||
return old;
|
||||
node = zfcp_lun_node_find(list, &id);
|
||||
if (!node) {
|
||||
node = zfcp_lun_node_new(&id);
|
||||
util_list_add_tail(list, node);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static void add_fc_setting_from_entry(struct udev_entry_node *entry,
|
||||
struct zfcp_lun_node *node)
|
||||
{
|
||||
char *copy, *s, *e;
|
||||
|
||||
/*ATTR{[ccw/0.0.1941]0x500507630510c1ae/0x402340d400000000/failed}="0"*/
|
||||
if (!starts_with(entry->key, "ATTR{[ccw/"))
|
||||
return;
|
||||
if (strstr(entry->key, "unit_add"))
|
||||
return;
|
||||
copy = misc_strdup(entry->key);
|
||||
s = strrchr(copy, '/');
|
||||
if (!s)
|
||||
goto out;
|
||||
s++;
|
||||
e = strchr(s, '}');
|
||||
if (!e)
|
||||
goto out;
|
||||
*e = 0;
|
||||
setting_list_add(node->fc_settings, setting_new(NULL, s, entry->value));
|
||||
|
||||
out:
|
||||
free(copy);
|
||||
}
|
||||
|
||||
static void add_scsi_setting_from_entry(struct udev_entry_node *entry,
|
||||
struct zfcp_lun_node *node)
|
||||
{
|
||||
char *copy, *s, *e;
|
||||
|
||||
/* ATTR{queue_depth}="64" */
|
||||
if (!starts_with(entry->key, "ATTR{"))
|
||||
return;
|
||||
copy = misc_strdup(entry->key);
|
||||
s = strchr(copy, '{');
|
||||
if (!s)
|
||||
goto out;
|
||||
s++;
|
||||
e = strchr(s, '}');
|
||||
if (!e)
|
||||
goto out;
|
||||
*e = 0;
|
||||
setting_list_add(node->scsi_settings,
|
||||
setting_new(NULL, s, entry->value));
|
||||
|
||||
out:
|
||||
free(copy);
|
||||
}
|
||||
|
||||
static int zfcp_lun_node_cmp(void *a, void *b, void *data)
|
||||
{
|
||||
struct zfcp_lun_node *a_node = a, *b_node = b;
|
||||
|
||||
return zfcp_lun_cmp_devids(&a_node->id, &b_node->id);
|
||||
}
|
||||
|
||||
static void sort_zfcp_lun_list(struct util_list *list)
|
||||
{
|
||||
util_list_sort(list, zfcp_lun_node_cmp, NULL);
|
||||
}
|
||||
|
||||
/* Read udev rule from FILENAME and extract all LUN settings as zfcp_lun_node
|
||||
* to list. Note: List entries will be sorted by ID. */
|
||||
static exit_code_t udev_read_zfcp_lun_rule(const char *filename,
|
||||
struct util_list *list)
|
||||
{
|
||||
exit_code_t rc;
|
||||
struct udev_file *file = NULL;
|
||||
struct udev_line_node *line;
|
||||
struct udev_entry_node *entry;
|
||||
struct zfcp_lun_node *node = NULL;
|
||||
enum {
|
||||
none,
|
||||
in_fc,
|
||||
in_scsi,
|
||||
} state = none;
|
||||
|
||||
rc = udev_read_file(filename, &file);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
util_list_iterate(&file->lines, line) {
|
||||
entry = util_list_start(&line->entries);
|
||||
|
||||
/* Skip comments and empty lines. */
|
||||
if (!entry)
|
||||
continue;
|
||||
|
||||
/* GOTO resets current state. */
|
||||
if (strcmp(entry->key, "GOTO") == 0) {
|
||||
node = NULL;
|
||||
state = none;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
case none:
|
||||
if (strcmp(entry->key, "LABEL") != 0)
|
||||
continue;
|
||||
if (starts_with(entry->value, LABEL_FC))
|
||||
state = in_fc;
|
||||
else if (starts_with(entry->value, LABEL_SCSI)) {
|
||||
state = in_scsi;
|
||||
node = zfcp_lun_node_from_entry(entry, node,
|
||||
list);
|
||||
}
|
||||
break;
|
||||
case in_fc:
|
||||
node = zfcp_lun_node_from_entry(entry, node, list);
|
||||
if (node)
|
||||
add_fc_setting_from_entry(entry, node);
|
||||
break;
|
||||
case in_scsi:
|
||||
if (node)
|
||||
add_scsi_setting_from_entry(entry, node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sort_zfcp_lun_list(list);
|
||||
|
||||
out:
|
||||
udev_free_file(file);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct lun_cb_data {
|
||||
char *prefix;
|
||||
struct util_list *list;
|
||||
};
|
||||
|
||||
static exit_code_t lun_cb(const char *path, const char *name, void *data)
|
||||
{
|
||||
struct lun_cb_data *cb_data = data;
|
||||
struct util_list *luns;
|
||||
struct zfcp_lun_node *node;
|
||||
char *id;
|
||||
|
||||
if (!starts_with(name, cb_data->prefix))
|
||||
return EXIT_OK;
|
||||
|
||||
luns = zfcp_lun_node_list_new();
|
||||
udev_read_zfcp_lun_rule(path, luns);
|
||||
|
||||
util_list_iterate(luns, node) {
|
||||
id = zfcp_lun_devid_to_str(&node->id);
|
||||
strlist_add(cb_data->list, id);
|
||||
free(id);
|
||||
}
|
||||
|
||||
zfcp_lun_node_list_free(luns);
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
/* Add the IDs for all zfcp lun devices for which a configuration exists to
|
||||
* LIST. */
|
||||
void udev_zfcp_lun_add_device_ids(struct util_list *list)
|
||||
{
|
||||
struct lun_cb_data cb_data;
|
||||
char *path;
|
||||
|
||||
cb_data.prefix = misc_asprintf("%s-%s-", UDEV_PREFIX, ZFCP_LUN_NAME);
|
||||
cb_data.list = list;
|
||||
path = path_get_udev_rules();
|
||||
|
||||
if (dir_exists(path))
|
||||
path_for_each(path, lun_cb, &cb_data);
|
||||
|
||||
free(path);
|
||||
free(cb_data.prefix);
|
||||
}
|
||||
|
||||
static char *get_zfcp_lun_path(const char *id)
|
||||
{
|
||||
char *copy, *e, *path;
|
||||
|
||||
copy = misc_strdup(id);
|
||||
e = strchr(copy, ':');
|
||||
if (e)
|
||||
*e = 0;
|
||||
path = path_get_udev_rule(ZFCP_LUN_NAME, copy);
|
||||
free(copy);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/* Apply the settings found in NODE to STATE. */
|
||||
static void zfcp_lun_node_to_state(struct zfcp_lun_node *node,
|
||||
struct attrib **attribs,
|
||||
struct device_state *state)
|
||||
{
|
||||
struct setting *s;
|
||||
struct attrib *a;
|
||||
char *name;
|
||||
|
||||
state->exists = 1;
|
||||
state->modified = 0;
|
||||
state->deconfigured = 0;
|
||||
state->definable = 0;
|
||||
|
||||
util_list_iterate(&node->fc_settings->list, s) {
|
||||
a = attrib_find(attribs, s->name);
|
||||
setting_list_add(state->settings,
|
||||
setting_new(a, s->name, s->value));
|
||||
}
|
||||
util_list_iterate(&node->scsi_settings->list, s) {
|
||||
name = misc_asprintf("%s/%s", SCSI_ATTR_PREFIX, s->name);
|
||||
a = attrib_find(attribs, name);
|
||||
setting_list_add(state->settings,
|
||||
setting_new(a, name, s->value));
|
||||
free(name);
|
||||
}
|
||||
}
|
||||
|
||||
/* Read the persistent configuration of a zfcp lun from a udev rule. */
|
||||
exit_code_t udev_zfcp_lun_read_device(struct device *dev)
|
||||
{
|
||||
struct subtype *st = dev->subtype;
|
||||
struct device_state *state = &dev->persistent;
|
||||
struct util_list *luns;
|
||||
struct zfcp_lun_node *node;
|
||||
exit_code_t rc = EXIT_OK;
|
||||
char *path;
|
||||
|
||||
path = get_zfcp_lun_path(dev->id);
|
||||
|
||||
/* Get previous rule data. */
|
||||
luns = zfcp_lun_node_list_new();
|
||||
rc = udev_read_zfcp_lun_rule(path, luns);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
node = zfcp_lun_node_find(luns, dev->devid);
|
||||
if (node)
|
||||
zfcp_lun_node_to_state(node, st->dev_attribs, state);
|
||||
else
|
||||
rc = EXIT_DEVICE_NOT_FOUND;
|
||||
|
||||
out:
|
||||
zfcp_lun_node_list_free(luns);
|
||||
free(path);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct zfcp_lun_node *state_to_zfcp_lun_node(struct zfcp_lun_devid *id,
|
||||
struct device_state *state)
|
||||
{
|
||||
struct zfcp_lun_node *node;
|
||||
struct setting *s, *n;
|
||||
|
||||
node = zfcp_lun_node_new(id);
|
||||
util_list_iterate(&state->settings->list, s) {
|
||||
if (s->removed)
|
||||
continue;
|
||||
if (attrib_match_prefix(s->name, SCSI_ATTR_PREFIX)) {
|
||||
n = setting_new(NULL,
|
||||
attrib_rem_prefix(s->name,
|
||||
SCSI_ATTR_PREFIX),
|
||||
s->value);
|
||||
setting_list_add(node->scsi_settings, n);
|
||||
} else {
|
||||
n = setting_new(NULL, s->name, s->value);
|
||||
setting_list_add(node->fc_settings, n);
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/* Write udev rule as defined by LIST of struct zfcp_lun_nodes to PATH. */
|
||||
static exit_code_t write_luns_rule(const char *path, struct util_list *list)
|
||||
{
|
||||
FILE *fd;
|
||||
exit_code_t rc = EXIT_OK;
|
||||
struct zfcp_lun_node *node, *last_node;
|
||||
char *hba_id;
|
||||
struct setting *s;
|
||||
|
||||
sort_zfcp_lun_list(list);
|
||||
|
||||
node = util_list_start(list);
|
||||
if (!node)
|
||||
return EXIT_INTERNAL_ERROR;
|
||||
hba_id = ccw_devid_to_str(&node->id.fcp_dev);
|
||||
debug("Writing FCP LUN udev rule file %s\n", path);
|
||||
if (!path_exists(path)) {
|
||||
rc = path_create(path);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
fd = misc_fopen(path, "w");
|
||||
if (!fd) {
|
||||
error("Could not write to file %s: %s\n", path,
|
||||
strerror(errno));
|
||||
rc = EXIT_RUNTIME_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
fprintf(fd, "# Generated by chzdev\n");
|
||||
fprintf(fd, "ACTION==\"add\", SUBSYSTEMS==\"ccw\", KERNELS==\"%s\", "
|
||||
"GOTO=\"%s%s\"\n", hba_id, LABEL_START, hba_id);
|
||||
fprintf(fd, "GOTO=\"%s%s\"\n", LABEL_END, hba_id);
|
||||
fprintf(fd, "\nLABEL=\"%s%s\"\n", LABEL_START, hba_id);
|
||||
|
||||
/* Emit FC port triggers. */
|
||||
last_node = NULL;
|
||||
util_list_iterate(list, node) {
|
||||
if (last_node && last_node->id.wwpn == node->id.wwpn) {
|
||||
/* Only one trigger per WWPN required. */
|
||||
continue;
|
||||
}
|
||||
fprintf(fd, "SUBSYSTEM==\"fc_remote_ports\", "
|
||||
"ATTR{port_name}==\"0x%016" PRIx64 "\", "
|
||||
"GOTO=\"%s%s_0x%016" PRIx64 "\"\n",
|
||||
node->id.wwpn, LABEL_FC, hba_id, node->id.wwpn);
|
||||
last_node = node;
|
||||
}
|
||||
|
||||
/* Emit SCSI unit triggers. */
|
||||
util_list_iterate(list, node) {
|
||||
if (util_list_is_empty(&node->scsi_settings->list))
|
||||
continue;
|
||||
fprintf(fd, "SUBSYSTEM==\"scsi\", "
|
||||
"ENV{DEVTYPE}==\"scsi_device\", "
|
||||
"KERNEL==\"*:%" PRIu64 "\", "
|
||||
"KERNELS==\"rport-*\", "
|
||||
"ATTRS{fc_remote_ports/$id/port_name}==\"0x%016" PRIx64
|
||||
"\", GOTO=\"%s%s_0x%016" PRIx64 "_0x%016" PRIx64 "\"\n",
|
||||
scsi_lun_from_fcp_lun(node->id.lun), node->id.wwpn,
|
||||
LABEL_SCSI, hba_id, node->id.wwpn, node->id.lun);
|
||||
}
|
||||
|
||||
fprintf(fd, "GOTO=\"%s%s\"\n", LABEL_END, hba_id);
|
||||
|
||||
/* Emit FC port sections. */
|
||||
last_node = NULL;
|
||||
util_list_iterate(list, node) {
|
||||
if (!last_node || last_node->id.wwpn != node->id.wwpn) {
|
||||
if (last_node) {
|
||||
fprintf(fd, "GOTO=\"end_zfcp_lun_%s\"\n",
|
||||
hba_id);
|
||||
}
|
||||
fprintf(fd, "\nLABEL=\"%s%s_0x%016" PRIx64 "\"\n",
|
||||
LABEL_FC, hba_id, node->id.wwpn);
|
||||
}
|
||||
fprintf(fd, "ATTR{[ccw/%s]0x%016" PRIx64 "/unit_add}="
|
||||
"\"0x%016" PRIx64 "\"\n", hba_id, node->id.wwpn,
|
||||
node->id.lun);
|
||||
|
||||
util_list_iterate(&node->fc_settings->list, s) {
|
||||
fprintf(fd, "ATTR{[ccw/%s]0x%016" PRIx64 "/0x%016"
|
||||
PRIx64 "/%s}=\"%s\"\n", hba_id, node->id.wwpn,
|
||||
node->id.lun, s->name, s->value);
|
||||
}
|
||||
last_node = node;
|
||||
}
|
||||
if (last_node)
|
||||
fprintf(fd, "GOTO=\"%s%s\"\n", LABEL_END, hba_id);
|
||||
|
||||
/* Emit SCSI unit sections. */
|
||||
util_list_iterate(list, node) {
|
||||
if (util_list_is_empty(&node->scsi_settings->list))
|
||||
continue;
|
||||
fprintf(fd, "\nLABEL=\"%s%s_0x%016" PRIx64 "_0x%016"
|
||||
PRIx64 "\"\n", LABEL_SCSI, hba_id, node->id.wwpn,
|
||||
node->id.lun);
|
||||
util_list_iterate(&node->scsi_settings->list, s)
|
||||
fprintf(fd, "ATTR{%s}=\"%s\"\n", s->name, s->value);
|
||||
fprintf(fd, "GOTO=\"%s%s\"\n", LABEL_END, hba_id);
|
||||
}
|
||||
|
||||
fprintf(fd, "\nLABEL=\"%s%s\"\n", LABEL_END, hba_id);
|
||||
|
||||
if (misc_fclose(fd))
|
||||
warn("Could not close file %s: %s\n", path, strerror(errno));
|
||||
|
||||
out:
|
||||
free(hba_id);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Update the udev rule file that configures the zfcp lun with the specified
|
||||
* ID. If @state is %NULL, remove the rule, otherwise create a rule that
|
||||
* applies the corresponding parameters. */
|
||||
static exit_code_t update_lun_rule(const char *id, struct device_state *state)
|
||||
{
|
||||
struct zfcp_lun_devid devid;
|
||||
struct util_list *luns;
|
||||
struct zfcp_lun_node *node;
|
||||
exit_code_t rc = EXIT_OK;
|
||||
char *path;
|
||||
bool exists;
|
||||
|
||||
rc = zfcp_lun_parse_devid(&devid, id, err_delayed_print);
|
||||
if (rc)
|
||||
return rc;
|
||||
path = get_zfcp_lun_path(id);
|
||||
|
||||
/* Get previous rule data. */
|
||||
luns = zfcp_lun_node_list_new();
|
||||
exists = file_exists(path);
|
||||
if (exists)
|
||||
udev_read_zfcp_lun_rule(path, luns);
|
||||
|
||||
/* Replace previous rule data for this ID. */
|
||||
node = zfcp_lun_node_find(luns, &devid);
|
||||
if (node) {
|
||||
util_list_remove(luns, node);
|
||||
zfcp_lun_node_free(node);
|
||||
}
|
||||
if (state && state->exists) {
|
||||
node = state_to_zfcp_lun_node(&devid, state);
|
||||
util_list_add_tail(luns, node);
|
||||
}
|
||||
|
||||
if (util_list_is_empty(luns)) {
|
||||
/* Remove empty file. */
|
||||
if (exists)
|
||||
rc = remove_file(path);
|
||||
} else {
|
||||
/* Write updated rules file. */
|
||||
rc = write_luns_rule(path, luns);
|
||||
}
|
||||
|
||||
zfcp_lun_node_list_free(luns);
|
||||
free(path);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Write a udev-rule to configure the specified zfcp lun and associated
|
||||
* device state. */
|
||||
exit_code_t udev_zfcp_lun_write_device(struct device *dev)
|
||||
{
|
||||
return update_lun_rule(dev->id, &dev->persistent);
|
||||
}
|
||||
|
||||
/* Remove the UDEV rule used to configure the zfcp lun with the specified ID. */
|
||||
exit_code_t udev_zfcp_lun_remove_rule(const char *id)
|
||||
{
|
||||
return update_lun_rule(id, NULL);
|
||||
}
|
||||
|
||||
/* Determine if a udev rule exists for configuring the specified zfcp lun. */
|
||||
bool udev_zfcp_lun_exists(const char *id)
|
||||
{
|
||||
struct zfcp_lun_devid devid;
|
||||
char *path, *rule, *pattern = NULL;
|
||||
bool rc = false;
|
||||
|
||||
if (zfcp_lun_parse_devid(&devid, id, err_ignore) != EXIT_OK)
|
||||
return false;
|
||||
path = get_zfcp_lun_path(id);
|
||||
rule = misc_read_text_file(path, 1, err_ignore);
|
||||
if (!rule)
|
||||
goto out;
|
||||
|
||||
pattern = misc_asprintf("ATTR{[ccw/%x.%x.%04x]0x%016" PRIx64
|
||||
"/unit_add}=\"0x%016" PRIx64 "\"\n",
|
||||
devid.fcp_dev.cssid, devid.fcp_dev.ssid,
|
||||
devid.fcp_dev.devno, devid.wwpn, devid.lun);
|
||||
if (strstr(rule, pattern))
|
||||
rc = true;
|
||||
|
||||
out:
|
||||
free(pattern);
|
||||
free(rule);
|
||||
free(path);
|
||||
|
||||
return rc;
|
||||
}
|
||||
29
zdev/src/zdev-root-update.dracut
Normal file
29
zdev/src/zdev-root-update.dracut
Normal file
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# zdev-root-update
|
||||
# Ensure that the persistent root device configuration is put into effect.
|
||||
# On typical distributions this requires a rebuild of the initial ram disk
|
||||
# and the re-installation of the IPL record referencing the ram disk.
|
||||
#
|
||||
# Parameters:
|
||||
# zdev-root-update <devtype> <devid> [<devtype2> <devid2>...]
|
||||
#
|
||||
# Where <devtype> is the device type as used by chzdev and <devid> is the
|
||||
# ID of the root device.
|
||||
#
|
||||
|
||||
TOOLNAME=$(basename $0)
|
||||
|
||||
echo "Building initial RAM-disk"
|
||||
dracut -f || {
|
||||
echo "${TOOLNAME}: Error: Could not build initial RAM-disk" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo "Installing IPL record"
|
||||
zipl --noninteractive || {
|
||||
echo "${TOOLNAME}: Error: Could not install IPL record" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
exit 0
|
||||
207
zdev/src/zdev-root-update.zipl
Normal file
207
zdev/src/zdev-root-update.zipl
Normal file
@@ -0,0 +1,207 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# zdev-root-update
|
||||
# Ensure that the persistent root device configuration is put into effect.
|
||||
# On typical distributions this requires a rebuild of the initial ram disk
|
||||
# and the re-installation of the IPL record referencing the ram disk.
|
||||
#
|
||||
# Parameters:
|
||||
# zdev-root-update <devtype> <devid> [<devtype2> <devid2>...]
|
||||
#
|
||||
# Where <devtype> is the device type as used by chzdev and <devid> is the
|
||||
# ID of the root device.
|
||||
#
|
||||
|
||||
TOOLNAME=$(basename $0)
|
||||
ZIPLCONF=/etc/zipl.conf
|
||||
|
||||
# die MESSAGE
|
||||
# Print MESSAGE on standard error and exit with exit code 1
|
||||
die()
|
||||
{
|
||||
echo "$TOOLNAME: Error: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# add LIST NEW DELIM
|
||||
# Add NEW to DELIM-separated LIST.
|
||||
add()
|
||||
{
|
||||
local LIST="$1"
|
||||
local NEW="$2"
|
||||
local DELIM="$3"
|
||||
|
||||
if [ -z "$LIST" ] ; then
|
||||
echo "$NEW"
|
||||
else
|
||||
echo "$LIST$DELIM$NEW"
|
||||
fi
|
||||
}
|
||||
|
||||
# add LIST NEW DELIM
|
||||
# Add NEW to DELIM-separated LIST unless it is already in list.
|
||||
add_unique()
|
||||
{
|
||||
local LIST="$1"
|
||||
local NEW="$2"
|
||||
local DELIM="$3"
|
||||
local ENTRY
|
||||
local FOUND
|
||||
local IFS="$DELIM"
|
||||
|
||||
FOUND=0
|
||||
for ENTRY in $LIST ; do
|
||||
if [ "$ENTRY" == "$NEW" ] ; then
|
||||
FOUND=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ "$FOUND" -eq 0 ] ; then
|
||||
LIST=$(add "$LIST" "$NEW" "$DELIM")
|
||||
fi
|
||||
|
||||
echo "$LIST"
|
||||
}
|
||||
|
||||
# process CMDLINE
|
||||
# Apply root device settings from globals DASD ZFCP and CIOIGNORE to CMDLINE
|
||||
process_cmdline()
|
||||
{
|
||||
local CMDLINE="$1"
|
||||
local ENTRY
|
||||
local NEW
|
||||
local FOUND_DASD=0
|
||||
local FOUND_ZFCP=0
|
||||
local V
|
||||
local X
|
||||
|
||||
for ENTRY in $CMDLINE ; do
|
||||
K=${ENTRY%%=*}
|
||||
case "$K" in
|
||||
dasd)
|
||||
if [ ! -z "$DASD" ] ; then
|
||||
NEW=$(add "$NEW" "dasd=$DASD" " ")
|
||||
FOUND_DASD=1
|
||||
fi
|
||||
;;
|
||||
zfcp.device)
|
||||
if [ ! -z "$ZFCP" ] ; then
|
||||
NEW=$(add "$NEW" "zfcp.device=$ZFCP" " ")
|
||||
FOUND_ZFCP=1
|
||||
fi
|
||||
;;
|
||||
cio_ignore)
|
||||
V=${ENTRY#*=}
|
||||
for X in $CIOIGNORE ; do
|
||||
V=$(add_unique "$V" "$X" ",")
|
||||
done
|
||||
NEW=$(add "$NEW" "cio_ignore=$V" " ")
|
||||
;;
|
||||
*)
|
||||
NEW=$(add "$NEW" "$ENTRY" " ")
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$FOUND_DASD" -eq 0 -a ! -z "$DASD" ] ; then
|
||||
NEW=$(add "$NEW" "dasd=$DASD" " ")
|
||||
fi
|
||||
if [ "$FOUND_ZFCP" -eq 0 -a ! -z "$ZFCP" ] ; then
|
||||
NEW=$(add "$NEW" "zfcp.device=$ZFCP" " ")
|
||||
fi
|
||||
|
||||
echo "$NEW"
|
||||
}
|
||||
|
||||
# process_file FILENAME
|
||||
# Apply root device settings from globals DASD ZFCP and CIOIGNORE to kernel
|
||||
# command line found in file FILENAME
|
||||
process_file()
|
||||
{
|
||||
local FILENAME=$1
|
||||
local CMDLINE
|
||||
|
||||
CMDLINE=$(cat $FILENAME)
|
||||
CMDLINE=$(process_cmdline "$CMDLINE")
|
||||
if ! echo $CMDLINE > "$FILENAME" ; then
|
||||
die "Could not write to $FILENAME"
|
||||
fi
|
||||
}
|
||||
|
||||
# Build dasd= zfcp.device= and cio_ignore= parameter values
|
||||
DASD=
|
||||
ZFCP=
|
||||
CIOIGNORE=
|
||||
while [ $# -gt 0 ] ; do
|
||||
TYPE=$1
|
||||
ID=$2
|
||||
if [ -z "$TYPE" -o -z "$ID" ] ; then
|
||||
die "Incomplete parameters: $*"
|
||||
fi
|
||||
case $TYPE in
|
||||
dasd-*)
|
||||
DASD=$(add "$DASD" "$ID" ",")
|
||||
CIOIGNORE=$(add "$CIOIGNORE" "!$ID" " ")
|
||||
;;
|
||||
zfcp-lun)
|
||||
if [ ! -z "$ZFCP" ] ; then
|
||||
die "Too many zFCP SCSI devices used for root device"
|
||||
fi
|
||||
ZFCP=$(echo $ID | sed -e 's/:/,/g')
|
||||
FCPDEV=$(echo $ID | cut -d: -f1)
|
||||
CIOIGNORE=$(add "$CIOIGNORE" "!$FCPDEV" " ")
|
||||
;;
|
||||
*)
|
||||
die "Unsupported root device type: $TYPE"
|
||||
;;
|
||||
esac
|
||||
shift 2
|
||||
done
|
||||
|
||||
if [ -z "$DASD" -a -z "$ZFCP" ] ; then
|
||||
die "No root device specified"
|
||||
fi
|
||||
|
||||
if [ ! -r "$ZIPLCONF" ] ; then
|
||||
die "Could not read $ZIPLCONF"
|
||||
fi
|
||||
|
||||
# Create new zipl.conf file
|
||||
echo "Updating $ZIPLCONF"
|
||||
TMPFILE=$(mktemp zdev-zipl.conf.XXX)
|
||||
[ $? -ne 0 ] && die "Could not create temporary file"
|
||||
while read LINE ; do
|
||||
OKEY=${LINE%%=*}
|
||||
KEY=$(echo $OKEY)
|
||||
case "$KEY" in
|
||||
parameters)
|
||||
V=$(echo ${LINE#*=} | sed -e 's/^"//' -e 's/"$//')
|
||||
V=$(process_cmdline "$V")
|
||||
echo "$OKEY=\"$V\""
|
||||
;;
|
||||
parmfile)
|
||||
V=$(echo ${LINE#*=} | sed -e 's/^"//' -e 's/"$//')
|
||||
process_file "$V"
|
||||
echo "$LINE"
|
||||
;;
|
||||
*)
|
||||
echo "$LINE"
|
||||
;;
|
||||
esac
|
||||
done < "$ZIPLCONF" >"$TMPFILE"
|
||||
|
||||
|
||||
# Swap new and old zipl.conf file
|
||||
if ! mv "$ZIPLCONF" "${ZIPLCONF}.old" ; then
|
||||
die "Could not rename $ZIPLCONF to ${ZIPLCONF}.old"
|
||||
fi
|
||||
if ! mv $TMPFILE $ZIPLCONF ; then
|
||||
die "Could not rename $TMPFILE to $ZIPCONF"
|
||||
fi
|
||||
rm -f "${ZIPLCONF}.old"
|
||||
|
||||
# Install IPL loader
|
||||
echo "Installing IPL record"
|
||||
if ! zipl --noninteractive ; then
|
||||
die "Could not install IPL record"
|
||||
fi
|
||||
309
zdev/src/zfcp.c
Normal file
309
zdev/src/zfcp.c
Normal file
@@ -0,0 +1,309 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "attrib.h"
|
||||
#include "ccw.h"
|
||||
#include "device.h"
|
||||
#include "devtype.h"
|
||||
#include "misc.h"
|
||||
#include "modprobe.h"
|
||||
#include "module.h"
|
||||
#include "path.h"
|
||||
#include "setting.h"
|
||||
#include "udev.h"
|
||||
#include "zfcp.h"
|
||||
#include "zfcp_host.h"
|
||||
#include "zfcp_lun.h"
|
||||
|
||||
/*
|
||||
* zfcp type attributes.
|
||||
*/
|
||||
|
||||
static struct attrib zfcp_tattr_dbfsize = {
|
||||
.name = "dbfsize",
|
||||
.title = "Modify buffer size for debugging records",
|
||||
.desc =
|
||||
"Control the number of 4 KB pages to be used for the debug feature\n",
|
||||
.defval = "4",
|
||||
.accept = ACCEPT_ARRAY(ACCEPT_NUM_GE(1)),
|
||||
};
|
||||
|
||||
static struct attrib zfcp_tattr_dbflevel = {
|
||||
.name = "dbflevel",
|
||||
.title = "Modify the minimum log level for debugging records",
|
||||
.desc =
|
||||
"Control the initial log level of the debug feature.\n",
|
||||
.defval = "3",
|
||||
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 6)),
|
||||
};
|
||||
|
||||
static struct attrib zfcp_tattr_queue_depth = {
|
||||
.name = "queue_depth",
|
||||
.title = "Modify the initial maximum SCSI device queue depth",
|
||||
.desc =
|
||||
"Control the initial upper limit on the number of outstanding SCSI\n"
|
||||
"commands per SCSI device.\n",
|
||||
.nounload = 1,
|
||||
.defval = "32",
|
||||
.accept = ACCEPT_ARRAY(ACCEPT_NUM_GE(1)),
|
||||
};
|
||||
|
||||
static struct attrib zfcp_tattr_allow_lun_scan = {
|
||||
.name = "allow_lun_scan",
|
||||
.title = "Disable automatic LUN scanning in NPIV mode",
|
||||
.desc =
|
||||
"Control the use of the automatic LUN scanning feature for FCP\n"
|
||||
"devices that are configured in N_PORT ID Virtualization mode.\n"
|
||||
" 0: Automatic LUN scanning is disabled\n"
|
||||
" 1: Automatic LUN scanning is enabled\n\n",
|
||||
.nounload = 1,
|
||||
.defval = "1",
|
||||
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)),
|
||||
};
|
||||
|
||||
static struct attrib zfcp_tattr_dif = {
|
||||
.name = "dif",
|
||||
.title = "Enable DIF/DIX data consistency checking",
|
||||
.desc =
|
||||
"Control the use of the end-to-end data consistency checking\n"
|
||||
"mechanism (DIF/DIX):\n"
|
||||
" 0: DIF is disabled\n"
|
||||
" 1: DIF is enabled when supported by the FCP device hardware\n",
|
||||
.defval = "0",
|
||||
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)),
|
||||
};
|
||||
|
||||
static struct attrib zfcp_tattr_datarouter = {
|
||||
.name = "datarouter",
|
||||
.title = "Enable hardware data routing",
|
||||
.desc =
|
||||
"Control the use of the hardware data routing (DR) feature:\n"
|
||||
" 0: DR is disabled\n"
|
||||
" 1: DR is enabled when supported by the FCP device hardware\n",
|
||||
.defval = "1",
|
||||
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)),
|
||||
};
|
||||
|
||||
static struct attrib zfcp_tattr_no_auto_port_rescan = {
|
||||
.name = "no_auto_port_rescan",
|
||||
.title = "Inhibit automatic port rescan",
|
||||
.desc =
|
||||
"Control the automatic port rescan feature:\n"
|
||||
" 0: Automatic port rescan is enabled\n"
|
||||
" 1: Automatic port rescan is disabled\n",
|
||||
.nounload = 1,
|
||||
.defval = "0",
|
||||
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)),
|
||||
};
|
||||
|
||||
static struct attrib zfcp_tattr_port_scan_ratelimit = {
|
||||
.name = "port_scan_ratelimit",
|
||||
.title = "Minimum delay between automatic port scans",
|
||||
.desc =
|
||||
"Control the automatic port scan ratelimit:\n"
|
||||
" 0: Ratelimit is disabled\n"
|
||||
" >0: Minimum delay in milliseconds\n",
|
||||
.nounload = 1,
|
||||
.defval = "60000",
|
||||
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 4294967295)),
|
||||
};
|
||||
|
||||
static struct attrib zfcp_tattr_port_scan_backoff = {
|
||||
.name = "port_scan_backoff",
|
||||
.title = "Avoid simultaneous automatic port scans",
|
||||
.desc =
|
||||
"Control the automatic port scan backoff:\n"
|
||||
" 0: Backoff is disabled\n"
|
||||
" >0: Random delay between 0 and given value in milliseconds\n",
|
||||
.nounload = 1,
|
||||
.defval = "500",
|
||||
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 4294967295)),
|
||||
};
|
||||
|
||||
/*
|
||||
* ZFCP methods.
|
||||
*/
|
||||
|
||||
/* Clean up all resources used by devtype object. */
|
||||
static void zfcp_devtype_exit(struct devtype *dt)
|
||||
{
|
||||
setting_list_free(dt->active_settings);
|
||||
setting_list_free(dt->persistent_settings);
|
||||
}
|
||||
|
||||
static void bool_to_num(char *s)
|
||||
{
|
||||
if (!s)
|
||||
return;
|
||||
if (strcasecmp(s, "y") == 0)
|
||||
*s = '1';
|
||||
else if (strcasecmp(s, "n") == 0)
|
||||
*s = '0';
|
||||
}
|
||||
|
||||
static void all_bools_to_num(struct setting_list *list)
|
||||
{
|
||||
struct setting *s;
|
||||
|
||||
util_list_iterate(&list->list, s) {
|
||||
if (s->attrib == &zfcp_tattr_allow_lun_scan ||
|
||||
s->attrib == &zfcp_tattr_dif ||
|
||||
s->attrib == &zfcp_tattr_datarouter ||
|
||||
s->attrib == &zfcp_tattr_no_auto_port_rescan) {
|
||||
/* Convert Y to 1 and N to 0. */
|
||||
bool_to_num(s->value);
|
||||
bool_to_num(s->actual_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static exit_code_t zfcp_devtype_read_settings(struct devtype *dt,
|
||||
config_t config)
|
||||
{
|
||||
struct setting_list *list;
|
||||
char *path;
|
||||
exit_code_t rc = EXIT_OK;
|
||||
|
||||
if (SCOPE_ACTIVE(config) && !dt->active_settings) {
|
||||
dt->active_exists = 0;
|
||||
rc = module_get_params(ZFCP_MOD_NAME, dt->type_attribs, &list);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (list) {
|
||||
all_bools_to_num(list);
|
||||
dt->active_settings = list;
|
||||
setting_list_mark_default_derived(dt->active_settings);
|
||||
setting_list_apply_defaults(dt->active_settings,
|
||||
dt->type_attribs, false);
|
||||
dt->active_exists = 1;
|
||||
} else
|
||||
dt->active_settings = setting_list_new();
|
||||
}
|
||||
|
||||
if (SCOPE_PERSISTENT(config) && !dt->persistent_settings) {
|
||||
dt->persistent_exists = 0;
|
||||
path = path_get_modprobe_conf(dt);
|
||||
rc = modprobe_read_settings(path, ZFCP_MOD_NAME,
|
||||
dt->type_attribs, &list);
|
||||
free(path);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (list) {
|
||||
dt->persistent_settings = list;
|
||||
setting_list_apply_defaults(dt->persistent_settings,
|
||||
dt->type_attribs, false);
|
||||
dt->persistent_exists = 1;
|
||||
} else
|
||||
dt->persistent_settings = setting_list_new();
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static exit_code_t zfcp_devtype_write_settings(struct devtype *dt,
|
||||
config_t config)
|
||||
{
|
||||
char *path;
|
||||
exit_code_t rc = EXIT_OK;
|
||||
|
||||
if (SCOPE_ACTIVE(config) && dt->active_settings) {
|
||||
/* Try setting parameters directly via Sysfs. */
|
||||
if (module_set_params(ZFCP_MOD_NAME, dt->active_settings))
|
||||
goto persistent;
|
||||
|
||||
/* Re-load kernel module.*/
|
||||
rc = module_load(ZFCP_MOD_NAME, NULL, dt->active_settings,
|
||||
err_delayed_print);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
persistent:
|
||||
if (SCOPE_PERSISTENT(config) && dt->persistent_settings) {
|
||||
path = path_get_modprobe_conf(dt);
|
||||
if (!rc) {
|
||||
rc = modprobe_write_settings(path, ZFCP_MOD_NAME,
|
||||
dt->persistent_settings);
|
||||
}
|
||||
free(path);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Determine the value of the allow_lun_scan zfcp attribute. */
|
||||
exit_code_t zfcp_check_allow_lun_scan(int *allow, config_t config)
|
||||
{
|
||||
struct devtype *dt = &zfcp_devtype;
|
||||
exit_code_t rc;
|
||||
struct setting *s;
|
||||
|
||||
/* Check auto_lun_scan_setting. */
|
||||
rc = dt->read_settings(dt, config);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
*allow = 1;
|
||||
|
||||
if (SCOPE_ACTIVE(config)) {
|
||||
s = setting_list_find(dt->active_settings, "allow_lun_scan");
|
||||
if (s && strcmp(s->value, "0") == 0)
|
||||
*allow = 0;
|
||||
}
|
||||
|
||||
if (SCOPE_PERSISTENT(config)) {
|
||||
s = setting_list_find(dt->persistent_settings,
|
||||
"allow_lun_scan");
|
||||
if (s && strcmp(s->value, "0") == 0)
|
||||
*allow = 0;
|
||||
}
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* ZFCP device type.
|
||||
*/
|
||||
|
||||
struct devtype zfcp_devtype = {
|
||||
.name = "zfcp",
|
||||
.title = "SCSI-over-Fibre Channel (FCP) devices and SCSI "
|
||||
"devices",
|
||||
.devname = "zFCP device",
|
||||
.modules = STRING_ARRAY(ZFCP_MOD_NAME),
|
||||
|
||||
.subtypes = SUBTYPE_ARRAY(
|
||||
&zfcp_host_subtype,
|
||||
&zfcp_lun_subtype,
|
||||
),
|
||||
|
||||
.type_attribs = ATTRIB_ARRAY(
|
||||
&zfcp_tattr_dbfsize,
|
||||
&zfcp_tattr_dbflevel,
|
||||
&zfcp_tattr_queue_depth,
|
||||
&zfcp_tattr_allow_lun_scan,
|
||||
&zfcp_tattr_dif,
|
||||
&zfcp_tattr_datarouter,
|
||||
&zfcp_tattr_no_auto_port_rescan,
|
||||
&zfcp_tattr_port_scan_ratelimit,
|
||||
&zfcp_tattr_port_scan_backoff,
|
||||
),
|
||||
.unknown_type_attribs = 1,
|
||||
|
||||
.exit = &zfcp_devtype_exit,
|
||||
|
||||
.read_settings = &zfcp_devtype_read_settings,
|
||||
.write_settings = &zfcp_devtype_write_settings,
|
||||
};
|
||||
255
zdev/src/zfcp_host.c
Normal file
255
zdev/src/zfcp_host.c
Normal file
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* 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 <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "attrib.h"
|
||||
#include "ccw.h"
|
||||
#include "device.h"
|
||||
#include "devtype.h"
|
||||
#include "misc.h"
|
||||
#include "path.h"
|
||||
#include "setting.h"
|
||||
#include "zfcp.h"
|
||||
#include "zfcp_host.h"
|
||||
|
||||
/*
|
||||
* zfcp host attributes.
|
||||
*/
|
||||
|
||||
static struct attrib zfcp_host_attr_failed = {
|
||||
.name = "failed",
|
||||
.title = "Check and restart FCP device recovery",
|
||||
.desc =
|
||||
"This attribute shows if error recovery of an FCP device has failed:\n"
|
||||
" 0: Error recovery has not failed or was not started\n"
|
||||
" 1: Error recovery was started and failed to complete\n"
|
||||
" successfully even after several automatic retries\n\n"
|
||||
"Write the value 0 to this attribute to restart the error recovery\n"
|
||||
"process after resolving the root cause of the failure.\n",
|
||||
.activeonly = 1,
|
||||
.rewrite = 1,
|
||||
.unstable = 1,
|
||||
.defval = "0",
|
||||
.accept = ACCEPT_ARRAY(ACCEPT_NUM(0)),
|
||||
};
|
||||
|
||||
static struct attrib zfcp_host_attr_port_remove = {
|
||||
.name = "port_remove",
|
||||
.title = "Unregister a remote port from the FCP device",
|
||||
.desc =
|
||||
"Unregister a remote port from the FCP device by writing its WWPN\n"
|
||||
"to this attribute. The WWPN must be in a format with 16 hexadecimal\n"
|
||||
"digits and 0x prefix and no manually configured FCP LUNs may be\n"
|
||||
"registered with that remote port.\n\n"
|
||||
"Note: The next port scan will register all available ports again,\n"
|
||||
"including any previously removed ports. To prevent removed ports\n"
|
||||
"from being registered automatically, use zoning.\n",
|
||||
.writeonly = 1,
|
||||
};
|
||||
|
||||
static struct attrib zfcp_host_attr_port_rescan = {
|
||||
.name = "port_rescan",
|
||||
.title = "Trigger a port rescan for the FCP device",
|
||||
.desc =
|
||||
"Rescan FCP device for available remote ports by writing the value 1\n"
|
||||
"to this attribute\n",
|
||||
.activeonly = 1,
|
||||
.writeonly = 1,
|
||||
.accept = ACCEPT_ARRAY(ACCEPT_NUM(1)),
|
||||
};
|
||||
|
||||
/*
|
||||
* zfcp host methods.
|
||||
*/
|
||||
|
||||
static exit_code_t check_cmb_enable(struct subtype *st, struct device *dev,
|
||||
config_t config)
|
||||
{
|
||||
struct setting *c, *o;
|
||||
|
||||
c = setting_list_find(dev->active.settings, ccw_attr_cmb_enable.name);
|
||||
o = setting_list_find(dev->active.settings, ccw_attr_online.name);
|
||||
|
||||
if (!c || !o) {
|
||||
/* Could not determine attribute states. */
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
if (c->modified && o->actual_value &&
|
||||
strcmp(o->actual_value, "1") == 0 && strcmp(o->value, "0") != 0) {
|
||||
delayed_forceable("Cannot modify cmb_enable setting while "
|
||||
"device is online\n");
|
||||
return EXIT_INVALID_CONFIG;
|
||||
}
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
static exit_code_t zfcp_host_st_check_pre_configure(struct subtype *st,
|
||||
struct device *dev,
|
||||
int prereq, config_t config)
|
||||
{
|
||||
exit_code_t rc;
|
||||
|
||||
/* No need to check if device is deconfigured. */
|
||||
if (dev->active.deconfigured)
|
||||
return EXIT_OK;
|
||||
|
||||
rc = check_cmb_enable(st, dev, config);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
static char *get_port_type_path(const char *id)
|
||||
{
|
||||
char *devpath, *path = NULL;
|
||||
struct util_list *files;
|
||||
struct strlist_node *s;
|
||||
|
||||
devpath = path_get_ccw_device(ZFCP_CCWDRV_NAME, id);
|
||||
files = strlist_new();
|
||||
if (!misc_read_dir(devpath, files, NULL, NULL))
|
||||
goto out;
|
||||
util_list_iterate(files, s) {
|
||||
if (!starts_with(s->str, "host"))
|
||||
continue;
|
||||
path = misc_asprintf("%s/%s/fc_host/%s/port_type", devpath,
|
||||
s->str, s->str);
|
||||
}
|
||||
out:
|
||||
free(devpath);
|
||||
strlist_free(files);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
exit_code_t zfcp_host_check_npiv(const char *id, int *enabled)
|
||||
{
|
||||
char *path, *type = NULL;
|
||||
exit_code_t rc = EXIT_RUNTIME_ERROR;
|
||||
|
||||
path = get_port_type_path(id);
|
||||
if (!path)
|
||||
goto out;
|
||||
|
||||
type = misc_read_text_file(path, 1, err_ignore);
|
||||
if (!type)
|
||||
goto out;
|
||||
|
||||
/* Check FCP port type. */
|
||||
if (strcmp(type, "NPIV VPORT") == 0)
|
||||
*enabled = 1;
|
||||
else
|
||||
*enabled = 0;
|
||||
rc = EXIT_OK;
|
||||
|
||||
out:
|
||||
free(path);
|
||||
free(type);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static exit_code_t check_npiv(struct subtype *st, struct device *dev,
|
||||
int prereq, config_t config)
|
||||
{
|
||||
int npiv, allow_lun_scan;
|
||||
static int warn_done;
|
||||
|
||||
if (prereq) {
|
||||
/* FCP device is configured as part of 3-tuple. */
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
if (subtype_online_get(st, dev, config) != 1) {
|
||||
/* FCP device is set offline. */
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
/* Check FCP port type. */
|
||||
if (zfcp_host_check_npiv(dev->id, &npiv)) {
|
||||
/* Could not determine NPIV setting. */
|
||||
return EXIT_OK;
|
||||
}
|
||||
if (!npiv) {
|
||||
delayed_info("Note: NPIV mode disabled - LUNs must be "
|
||||
"configured manually\n");
|
||||
return EXIT_INVALID_CONFIG;
|
||||
}
|
||||
|
||||
/* Check allow_lun_scan setting. */
|
||||
if (zfcp_check_allow_lun_scan(&allow_lun_scan, config)) {
|
||||
/* Could not determine allow_lun_scan setting. */
|
||||
return EXIT_OK;
|
||||
}
|
||||
if (!allow_lun_scan && !warn_done) {
|
||||
delayed_info("Note: Automatic LUN scan disabled - LUNs must "
|
||||
"be configured manually\n");
|
||||
return EXIT_INVALID_CONFIG;
|
||||
}
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
/* Perform post-write checks specific to zfcp hosts. */
|
||||
static exit_code_t zfcp_host_st_check_post_configure(struct subtype *st,
|
||||
struct device *dev,
|
||||
int prereq,
|
||||
config_t config)
|
||||
{
|
||||
/* No need to check if device is deconfigured. */
|
||||
if (dev->active.deconfigured)
|
||||
return EXIT_OK;
|
||||
|
||||
/* Check for NPIV but don't route exit code to caller - we only
|
||||
* want to show a warning. */
|
||||
check_npiv(st, dev, prereq, config);
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* zfcp host sub-type.
|
||||
*/
|
||||
|
||||
static struct ccw_subtype_data zfcp_host_data = {
|
||||
.ccwdrv = ZFCP_CCWDRV_NAME,
|
||||
.mod = ZFCP_MOD_NAME,
|
||||
};
|
||||
|
||||
struct subtype zfcp_host_subtype = {
|
||||
.super = &ccw_subtype,
|
||||
|
||||
.devtype = &zfcp_devtype,
|
||||
.name = "zfcp-host",
|
||||
.title = "FCP devices",
|
||||
.devname = "FCP device",
|
||||
.modules = STRING_ARRAY(ZFCP_MOD_NAME),
|
||||
.namespace = &ccw_namespace,
|
||||
.data = &zfcp_host_data,
|
||||
|
||||
.dev_attribs = ATTRIB_ARRAY(
|
||||
&ccw_attr_online,
|
||||
&ccw_attr_cmb_enable,
|
||||
&zfcp_host_attr_failed,
|
||||
&zfcp_host_attr_port_remove,
|
||||
&zfcp_host_attr_port_rescan,
|
||||
),
|
||||
.unknown_dev_attribs = 1,
|
||||
|
||||
.check_pre_configure = &zfcp_host_st_check_pre_configure,
|
||||
.check_post_configure = &zfcp_host_st_check_post_configure,
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user