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:
Michael Holzheu
2017-08-07 16:13:17 +02:00
commit b627b8d8e1
647 changed files with 168974 additions and 0 deletions

13
zdev/Makefile Normal file
View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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 */

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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 */

View 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 */

View 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
View 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
View 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 */

View 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 */

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

1214
zdev/src/ccwgroup.c Normal file

File diff suppressed because it is too large Load Diff

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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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(&copy[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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

45
zdev/src/lszdev_usage.txt Normal file
View 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

File diff suppressed because it is too large Load Diff

424
zdev/src/modprobe.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

482
zdev/src/qeth_auto.c Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

713
zdev/src/setting.c Normal file
View 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(&copy->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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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;
}

View 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

View 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
View 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
View 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