Files
s390-tools/zdev/src/chzdev.c
Jan Polensky 43cf7873be zdev: generate usage text headers instead of C files
Generating and then including C source files from another C file can
lead to unexpected compilation errors in certain environments.

Switch the usage text generation from %_usage.c to %_usage.h. The
generated header provides the usage_text definition directly, and
chzdev.c and lszdev.c include the corresponding *_usage.h instead.

Update depfile prerequisites and the clean target to match the new
generated artifacts.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Jan Polensky <japo@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-04-30 08:44:01 +02:00

3272 lines
76 KiB
C

/*
* zdev - Modify and display the persistent configuration of devices
*
* chzdev: Configure z Systems specific devices
*
* Copyright IBM Corp. 2016, 2019
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "lib/util_path.h"
#include "lib/zt_common.h"
#include "attrib.h"
#include "blkinfo.h"
#include "ccw.h"
#include "ctc.h"
#include "device.h"
#include "devnode.h"
#include "devtype.h"
#include "export.h"
#include "firmware.h"
#include "inuse.h"
#include "misc.h"
#include "module.h"
#include "namespace.h"
#include "opts.h"
#include "path.h"
#include "root.h"
#include "scsi.h"
#include "select.h"
#include "setting.h"
#include "subtype.h"
#include "table_attribs.h"
#include "table_types.h"
#include "udev.h"
#include "zfcp_lun.h"
/* current site-id in action. By default, operations are always on fallback
* site; We need the current site-id as global variable to avoid excessive
* diff of simple function parameter modifications to pass this value.
*/
int global_site_id = SITE_FALLBACK;
#define MAX_HEADER_LENGTH sizeof(CHZDEV_HEADER)
/* Main program action. */
typedef enum {
ACT_CONFIGURE,
ACT_DECONFIGURE,
ACT_LIST_ATTRIBS,
ACT_LIST_TYPES,
ACT_HELP_ATTRIBS,
ACT_EXPORT,
ACT_IMPORT,
ACT_IS_OWNER,
ACT_APPLY,
ACT_HELP,
ACT_VERSION,
} action_t;
/* Representation of command line options. */
struct options {
/* Unparsed positional parameters. */
struct util_list *positional; /* List of struct strlist_node */
/* Selection. */
struct select_opts *select;
unsigned int type:1;
/* Settings */
struct util_list *settings; /* List of struct strlist_node */
/* Actions */
unsigned int enable:1;
unsigned int deconfigure:1;
unsigned int deconfigure_all:1;
unsigned int list_attribs:1;
unsigned int list_types:1;
unsigned int help_attribs:1;
char *export;
char *import;
char *is_owner;
unsigned int apply:1;
unsigned int help:1;
unsigned int version:1;
/* Options */
config_t config;
unsigned int active:1;
unsigned int persistent:1;
unsigned int auto_conf:1;
struct util_list *remove; /* List of struct strlist_node */
unsigned int remove_all:1;
unsigned int force:1;
unsigned int yes:1;
unsigned int no_root_check:1;
unsigned int dryrun:1;
struct util_list *base; /* List of struct strlist_node */
unsigned int verbose:1;
unsigned int quiet:1;
unsigned int no_settle:1;
unsigned int site_id;
unsigned int no_module_load:1;
};
/* Makefile converts chzdev_usage.txt into header file which we include here. */
#include "chzdev_usage.h"
#define OPT_ANONYMOUS_BASE 0x80
enum {
OPT_ALL = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_CONFIGURED = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_EXISTING = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_ONLINE = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_OFFLINE = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_FAILED = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_IPLDEV = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_BY_PATH = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_BY_NODE = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_BY_INTERFACE = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_BY_ATTRIB = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_TYPE = 't',
OPT_ENABLE = 'e',
OPT_DECONFIGURE = 'd',
OPT_DECONFIGURE_ALL = 'D',
OPT_LIST_ATTRIBS = 'l',
OPT_HELP_ATTRIBS = 'H',
OPT_LIST_TYPES = 'L',
OPT_EXPORT = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_IMPORT = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_IS_OWNER = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_APPLY = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_ACTIVE = 'a',
OPT_PERSISTENT = 'p',
OPT_REMOVE = 'r',
OPT_REMOVE_ALL = 'R',
OPT_FORCE = 'f',
OPT_YES = 'y',
OPT_NO_ROOT_UPDATE = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_DRY_RUN = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_BASE = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_HELP = 'h',
OPT_VERSION = 'v',
OPT_VERBOSE = 'V',
OPT_QUIET = 'q',
OPT_NO_SETTLE = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_AUTO_CONF = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_SITE = 's',
OPT_NO_MODULE_LOAD = (OPT_ANONYMOUS_BASE+__COUNTER__),
};
static struct opts_conflict conflict_list[] = {
OPTS_CONFLICT(OPT_ENABLE,
OPT_DECONFIGURE, OPT_LIST_ATTRIBS, OPT_LIST_TYPES,
OPT_EXPORT, OPT_IMPORT, OPT_APPLY, OPT_TYPE,
OPT_DECONFIGURE_ALL, 0),
OPTS_CONFLICT(OPT_DECONFIGURE,
OPT_LIST_ATTRIBS, OPT_HELP_ATTRIBS, OPT_LIST_TYPES,
OPT_EXPORT, OPT_IMPORT, OPT_APPLY, OPT_REMOVE,
OPT_REMOVE_ALL, OPT_TYPE,
OPT_DECONFIGURE_ALL, 0),
OPTS_CONFLICT(OPT_LIST_ATTRIBS,
OPT_DECONFIGURE, OPT_HELP_ATTRIBS, OPT_LIST_TYPES,
OPT_EXPORT, OPT_IMPORT, OPT_APPLY, OPT_REMOVE,
OPT_REMOVE_ALL, OPT_CONFIGURED, OPT_EXISTING, OPT_ONLINE,
OPT_OFFLINE, OPT_BY_PATH, OPT_BY_NODE, OPT_BY_INTERFACE,
OPT_BY_ATTRIB, OPT_ACTIVE, OPT_PERSISTENT, OPT_FAILED,
OPT_IPLDEV, OPT_DECONFIGURE_ALL, 0),
OPTS_CONFLICT(OPT_LIST_TYPES,
OPT_DECONFIGURE, OPT_LIST_ATTRIBS, OPT_HELP_ATTRIBS,
OPT_EXPORT, OPT_IMPORT, OPT_APPLY, OPT_REMOVE,
OPT_REMOVE_ALL, OPT_CONFIGURED, OPT_EXISTING, OPT_ONLINE,
OPT_OFFLINE, OPT_BY_PATH, OPT_BY_NODE, OPT_BY_INTERFACE,
OPT_BY_ATTRIB, OPT_ACTIVE, OPT_PERSISTENT, OPT_FAILED,
OPT_IPLDEV, OPT_DECONFIGURE_ALL, 0),
OPTS_CONFLICT(OPT_EXPORT,
OPT_DECONFIGURE, OPT_LIST_ATTRIBS, OPT_HELP_ATTRIBS,
OPT_LIST_TYPES, OPT_IMPORT, OPT_APPLY, OPT_REMOVE,
OPT_REMOVE_ALL, OPT_DECONFIGURE_ALL, 0),
OPTS_CONFLICT(OPT_IMPORT,
OPT_DECONFIGURE, OPT_LIST_ATTRIBS, OPT_HELP_ATTRIBS,
OPT_LIST_TYPES, OPT_EXPORT, OPT_APPLY, OPT_REMOVE,
OPT_REMOVE_ALL, OPT_CONFIGURED, OPT_EXISTING, OPT_BY_PATH,
OPT_BY_ATTRIB, OPT_BY_NODE, OPT_BY_INTERFACE,
OPT_DECONFIGURE_ALL, 0),
OPTS_CONFLICT(OPT_IS_OWNER,
OPT_DECONFIGURE, OPT_HELP_ATTRIBS, OPT_LIST_TYPES,
OPT_EXPORT, OPT_IMPORT, OPT_APPLY, OPT_REMOVE,
OPT_REMOVE_ALL, OPT_CONFIGURED, OPT_EXISTING, OPT_ONLINE,
OPT_OFFLINE, OPT_BY_PATH, OPT_BY_NODE, OPT_BY_INTERFACE,
OPT_BY_ATTRIB, OPT_ACTIVE, OPT_PERSISTENT, OPT_FAILED,
OPT_IPLDEV, OPT_DECONFIGURE_ALL, 0),
OPTS_CONFLICT(OPT_APPLY,
OPT_DECONFIGURE, OPT_LIST_ATTRIBS, OPT_HELP_ATTRIBS,
OPT_LIST_TYPES, OPT_EXPORT, OPT_IMPORT, OPT_REMOVE,
OPT_REMOVE_ALL, OPT_ACTIVE,
OPT_DECONFIGURE_ALL, 0),
OPTS_CONFLICT(OPT_ONLINE,
OPT_OFFLINE),
OPTS_CONFLICT(OPT_SITE,
OPT_TYPE, 0),
OPTS_CONFLICT(OPT_QUIET,
OPT_VERBOSE),
OPTS_CONFLICT(OPT_AUTO_CONF,
OPT_TYPE, OPT_LIST_ATTRIBS, OPT_LIST_TYPES),
OPTS_CONFLICT(0, 0),
};
/* Command line options. */
static const struct option opt_list[] = {
/* Selection. */
{ "all", no_argument, NULL, OPT_ALL },
{ "configured", no_argument, NULL, OPT_CONFIGURED },
{ "existing", no_argument, NULL, OPT_EXISTING },
{ "online", no_argument, NULL, OPT_ONLINE },
{ "offline", no_argument, NULL, OPT_OFFLINE },
{ "failed", no_argument, NULL, OPT_FAILED },
{ "ipldev", no_argument, NULL, OPT_IPLDEV },
{ "by-path", required_argument, NULL, OPT_BY_PATH },
{ "by-node", required_argument, NULL, OPT_BY_NODE },
{ "by-interface", required_argument, NULL, OPT_BY_INTERFACE },
{ "by-attrib", required_argument, NULL, OPT_BY_ATTRIB },
{ "type", no_argument, NULL, OPT_TYPE },
/* Actions. */
{ "enable", no_argument, NULL, OPT_ENABLE },
{ "disable", no_argument, NULL, OPT_DECONFIGURE },
{ "disable-all", no_argument, NULL, OPT_DECONFIGURE_ALL },
{ "list-attributes", no_argument, NULL, OPT_LIST_ATTRIBS },
{ "help-attribute", no_argument, NULL, OPT_HELP_ATTRIBS },
{ "list-types", no_argument, NULL, OPT_LIST_TYPES },
{ "export", required_argument, NULL, OPT_EXPORT },
{ "import", required_argument, NULL, OPT_IMPORT },
{ "is-owner", required_argument, NULL, OPT_IS_OWNER },
{ "apply", no_argument, NULL, OPT_APPLY },
{ "help", no_argument, NULL, OPT_HELP },
{ "version", no_argument, NULL, OPT_VERSION },
/* Options. */
{ "active", no_argument, NULL, OPT_ACTIVE },
{ "persistent", no_argument, NULL, OPT_PERSISTENT },
{ "auto-conf", no_argument, NULL, OPT_AUTO_CONF },
{ "remove", required_argument, NULL, OPT_REMOVE },
{ "remove-all", no_argument, NULL, OPT_REMOVE_ALL },
{ "force", no_argument, NULL, OPT_FORCE },
{ "yes", no_argument, NULL, OPT_YES },
{ "no-root-update", no_argument, NULL, OPT_NO_ROOT_UPDATE },
{ "dry-run", no_argument, NULL, OPT_DRY_RUN },
{ "base", required_argument, NULL, OPT_BASE },
{ "verbose", no_argument, NULL, OPT_VERBOSE },
{ "quiet", no_argument, NULL, OPT_QUIET },
{ "no-settle", no_argument, NULL, OPT_NO_SETTLE },
{ "site", required_argument, NULL, OPT_SITE },
{ "no-module-load", no_argument, NULL, OPT_NO_MODULE_LOAD },
{ NULL, no_argument, NULL, 0 },
};
/* Command line abbreviations. */
static const char opt_str[] = ":edlHLapr:RfyhvVqts:D";
/* Count of persistently modified devices. */
static int pers_mod_devs;
/* Count of persistently modified device types. */
static int pers_mod_devtypes;
/* Initialize options data structure. */
static void init_options(struct options *opts)
{
memset(opts, 0, sizeof(struct options));
opts->select = select_opts_new();
opts->positional = strlist_new();
opts->settings = strlist_new();
opts->remove = strlist_new();
opts->base = strlist_new();
/* Default operations are on fallback site*/
opts->site_id = SITE_FALLBACK;
}
/* Release memory used in options data structure. */
static void free_options(struct options *opts)
{
if (!opts)
return;
free(opts->export);
free(opts->import);
free(opts->is_owner);
select_opts_free(opts->select);
strlist_free(opts->positional);
strlist_free(opts->settings);
strlist_free(opts->remove);
strlist_free(opts->base);
}
/* Print usage information. */
static void print_usage(void)
{
printf("%s", usage_text);
}
/* Print version information. */
static void print_version(void)
{
printf("%s version %s\n", toolname, RELEASE_STRING);
}
/* Determine main program action from parse command line options. */
static action_t get_action(struct options *opts)
{
if (opts->help)
return ACT_HELP;
if (opts->version)
return ACT_VERSION;
if (opts->enable)
return ACT_CONFIGURE;
if (opts->deconfigure)
return ACT_DECONFIGURE;
if (opts->list_attribs)
return ACT_LIST_ATTRIBS;
if (opts->help_attribs)
return ACT_HELP_ATTRIBS;
if (opts->list_types)
return ACT_LIST_TYPES;
if (opts->export)
return ACT_EXPORT;
if (opts->import)
return ACT_IMPORT;
if (opts->is_owner)
return ACT_IS_OWNER;
if (opts->apply)
return ACT_APPLY;
return ACT_CONFIGURE;
}
/* Return option corresponding to action. */
static const char *get_action_option(action_t action)
{
switch (action) {
case ACT_DECONFIGURE:
return "--disable";
case ACT_LIST_ATTRIBS:
return "--list-attributes";
case ACT_HELP_ATTRIBS:
return "--help-attributes";
case ACT_LIST_TYPES:
return "--list-types";
case ACT_EXPORT:
return "--export";
case ACT_IMPORT:
return "--import";
case ACT_IS_OWNER:
return "--is-owner";
case ACT_APPLY:
return "--apply";
case ACT_HELP:
return "--help";
case ACT_VERSION:
return "--version";
default:
return "";
}
}
/* Check options data structure for syntax errors. */
static exit_code_t check_options(struct options *opts,
int specified[OPTS_MAX + 1], int op)
{
if (opts_check_conflict(op, specified, conflict_list, opt_list))
return EXIT_USAGE_ERROR;
return EXIT_OK;
}
/* Determine if STR is a valid device specification. */
static bool is_devspec(struct devtype *dt, struct subtype *st, char *str)
{
char *copy, *curr, *next;
bool rc;
/* ID,ID-ID,... */
copy = misc_strdup(str);
next = copy;
/* Separate by comma. */
rc = true;
if (st) {
/* Is this a valid ID or range for this subtype? */
while ((curr = strsep(&next, ","))) {
if (!ns_is_id_valid(st->namespace, curr) &&
!ns_is_id_range_valid(st->namespace, curr)) {
rc = false;
break;
}
}
} else if (dt) {
/* Is this a valid ID or range for any subtype of this
* devtype? */
while ((curr = strsep(&next, ","))) {
if (!devtype_is_id_valid(dt, curr) &&
!devtype_is_id_range_valid(dt, curr)) {
rc = false;
break;
}
}
} else {
/* Is this a valid ID or range for any subtype? */
while ((curr = strsep(&next, ","))) {
if (!namespaces_is_id_valid(curr) &&
!namespaces_is_id_range_valid(curr)) {
rc = false;
break;
}
}
}
free(copy);
return rc;
}
static exit_code_t check_devid(struct devtype *only_dt, struct subtype *only_st,
const char *id)
{
int i, j;
struct devtype *dt;
struct subtype *st;
struct namespace *ns;
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))
return EXIT_OK;
}
}
/* ID not valid - generate corresponding error message and exit code. */
ns = NULL;
if (only_st)
ns = only_st->namespace;
else if (only_dt && devtype_count_namespaces(only_dt) == 1)
ns = only_dt->subtypes[0]->namespace;
else
ns = devtype_most_similar_namespace(only_dt, NULL, id);
if (ns)
return ns->is_id_valid(id, err_print);
if (only_dt) {
error("Unrecognized %s ID format: %s\n",
only_dt->devname, id);
return EXIT_INVALID_ID;
}
syntax("Unknown device type or device ID format: %s\n", id);
return EXIT_USAGE_ERROR;
}
static exit_code_t check_range(struct devtype *only_dt, struct subtype *only_st,
const char *range)
{
int i, j;
struct devtype *dt;
struct subtype *st;
struct namespace *ns;
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_range_valid(st->namespace, range))
return EXIT_OK;
}
}
/* Range not valid - generate corresponding error message and exit
* code. */
ns = NULL;
if (only_st)
ns = only_st->namespace;
else if (only_dt && devtype_count_namespaces(only_dt) == 1)
ns = only_dt->subtypes[0]->namespace;
if (ns)
return ns->is_id_range_valid(range, err_print);
if (only_dt) {
error("Unrecognized %s ID range format: %s\n",
only_dt->devname, range);
return EXIT_INVALID_ID;
}
syntax("Unknown device type or device ID range format: %s\n", range);
return EXIT_USAGE_ERROR;
}
/* Get corresponding exit code and error message for invalid devspec. */
static exit_code_t check_devspec(struct devtype *dt, struct subtype *st,
const char *devspec)
{
char *copy, *curr, *next;
exit_code_t rc = EXIT_OK;
/* ID,ID-ID,... */
copy = misc_strdup(devspec);
next = copy;
/* Separate by comma. */
while ((curr = strsep(&next, ","))) {
if (strchr(curr, '-'))
rc = check_range(dt, st, curr);
else
rc = check_devid(dt, st, curr);
if (rc)
break;
}
free(copy);
return rc;
}
static exit_code_t check_devspecs(struct options *opts)
{
struct strlist_node *str;
exit_code_t rc = EXIT_OK;
util_list_iterate(&opts->select->devids, str) {
rc = check_devspec(opts->select->devtype, opts->select->subtype,
str->str);
if (rc)
break;
}
return rc;
}
/* Parse positional parameters. */
static exit_code_t parse_positional(struct options *opts, int argc,
char *argv[], int start)
{
action_t action = get_action(opts);
struct subtype *st;
struct devtype *dt;
int i;
bool got_devspec;
/* Collect parameters. */
for (i = start; i < argc; i++) {
dt = devtype_find(argv[i]);
st = subtype_find(argv[i]);
switch (action) {
case ACT_CONFIGURE:
if (opts->type) {
/* DEVTYPE: Required
* DEVICE: Invalid
* SETTING: Required */
if (st && !opts->select->devtype) {
opts->select->subtype = st;
opts->select->devtype = st->devtype;
} else if (dt && !opts->select->devtype) {
opts->select->devtype = dt;
} else if (strchr(argv[i], '=')) {
strlist_add(opts->settings, "%s",
argv[i]);
} else
goto err_extra;
} else {
/* DEVTYPE: Optional
* DEVICE: Optional
* SETTING: Optional */
if (st && !opts->select->devtype) {
opts->select->subtype = st;
opts->select->devtype = st->devtype;
} else if (dt && !opts->select->devtype)
opts->select->devtype = dt;
else if (strchr(argv[i], '=')) {
strlist_add(opts->settings, "%s",
argv[i]);
} else if (is_devspec(opts->select->devtype,
opts->select->subtype,
argv[i])) {
strlist_add_multi(&opts->select->devids,
argv[i], ",", 0);
} else
goto err_inv_dev;
}
break;
case ACT_DECONFIGURE:
case ACT_EXPORT:
case ACT_IMPORT:
case ACT_APPLY:
/* DEVTYPE: Optional
* DEVICE: Required/optional */
if (st && !opts->select->devtype) {
opts->select->subtype = st;
opts->select->devtype = st->devtype;
} else if (dt && !opts->select->devtype)
opts->select->devtype = dt;
else if (strchr(argv[i], '='))
goto err_inv_setting;
else if (is_devspec(opts->select->devtype,
opts->select->subtype, argv[i])) {
strlist_add_multi(&opts->select->devids,
argv[i], ",", 0);
} else
goto err_inv_dev;
break;
case ACT_LIST_ATTRIBS:
case ACT_HELP_ATTRIBS:
/* DEVTYPE: Mandatory
* OTHER: Optional */
if (st && !opts->select->devtype) {
opts->select->subtype = st;
opts->select->devtype = st->devtype;
} else if (dt && !opts->select->devtype)
opts->select->devtype = dt;
else if (!st && !opts->select->devtype)
goto err_unknown_devtype;
else
strlist_add(opts->positional, "%s", argv[i]);
break;
case ACT_LIST_TYPES:
/* DEVTYPE: Optional */
if (st || dt) {
if (opts->select->devtype)
goto err_extra;
if (dt)
opts->select->devtype = dt;
else {
opts->select->subtype = st;
opts->select->devtype = st->devtype;
}
} else {
if (!opts->select->devtype)
goto err_unknown_devtype;
else
goto err_extra;
}
break;
default:
/* No positional parameters expected. */
goto err_extra;
};
}
got_devspec = select_opts_dev_specified(opts->select);
/* Check for required parameters. */
switch (action) {
case ACT_CONFIGURE:
if (opts->type) {
/* DEVTYPE: Required
* DEVICE: Invalid
* SETTING: Required */
if (got_devspec)
goto err_inv_type_dev;
if (!opts->select->devtype)
goto err_no_devtype;
if (util_list_is_empty(opts->settings) &&
util_list_is_empty(opts->remove) &&
!opts->remove_all)
goto err_no_devtype_setting;
} else {
if (!opts->enable &&
util_list_is_empty(opts->settings) &&
util_list_is_empty(opts->remove) &&
!opts->remove_all)
goto err_no_enable;
if (!got_devspec) {
if (opts->enable)
goto err_no_dev_conf;
if (!opts->select->devtype)
goto err_no_dev_or_type;
if (util_list_is_empty(opts->settings) &&
util_list_is_empty(opts->remove) &&
!opts->remove_all)
goto err_no_devtype_or_setting;
goto err_no_dev_or_type_opt;
}
goto check_devspec;
}
break;
case ACT_DECONFIGURE:
if (!got_devspec)
goto err_no_dev_deconf;
goto check_devspec;
case ACT_LIST_ATTRIBS:
case ACT_HELP_ATTRIBS:
if (!opts->select->devtype)
goto err_no_devtype;
break;
case ACT_EXPORT:
if (!got_devspec && !opts->type)
goto err_no_dev_or_type_opt;
goto check_devspec;
case ACT_APPLY:
if (!got_devspec && !opts->type)
goto err_no_dev_or_type_opt;
goto check_devspec;
default:
/* All ok. */
break;
};
return EXIT_OK;
check_devspec:
return check_devspecs(opts);
err_unknown_devtype:
error("Unknown device type: %s\n", argv[i]);
return EXIT_UNKNOWN_DEVTYPE;
err_extra:
syntax("Extra parameter found: %s\n", argv[i]);
return EXIT_USAGE_ERROR;
err_inv_dev:
return check_devspec(opts->select->devtype, opts->select->subtype,
argv[i]);
err_inv_setting:
syntax("Cannot specify '%s' together with setting '%s'\n",
get_action_option(action), argv[i]);
return EXIT_USAGE_ERROR;
err_no_dev_or_type:
syntax("Please specify a device or device type to configure\n");
return EXIT_USAGE_ERROR;
err_no_dev_conf:
syntax("Please specify a device to configure\n");
return EXIT_USAGE_ERROR;
err_no_dev_deconf:
syntax("Please specify a device to deconfigure\n");
return EXIT_USAGE_ERROR;
err_no_devtype:
error("Please specify a device type\n"
"Use '%s --list-types' to get a list of device types\n",
toolname);
return EXIT_USAGE_ERROR;
err_no_devtype_setting:
if (opts->select->devtype->type_attribs[0]) {
error("Please specify a device type setting to configure\n"
"Use '%s %s --type --list-attributes' to get a list of "
"attributes\n", toolname, opts->select->devtype->name);
return EXIT_USAGE_ERROR;
}
error("Device type %s does not provide type attributes\n",
opts->select->devtype->name);
return EXIT_ATTRIB_NOT_FOUND;
err_no_devtype_or_setting:
syntax("Please specify a device or setting to configure\n");
return EXIT_USAGE_ERROR;
err_inv_type_dev:
error("Cannot specify '--type' and select a device when configuring\n");
return EXIT_USAGE_ERROR;
err_no_dev_or_type_opt:
syntax("Please specify a device or --type to select device type\n");
return EXIT_USAGE_ERROR;
err_no_enable:
syntax("Please specify an action\n");
return EXIT_USAGE_ERROR;
}
/* Parse command line options. Filter out invalid combinations. */
static exit_code_t parse_options(struct options *opts, int argc, char *argv[])
{
exit_code_t rc;
int opt;
int specified[OPTS_MAX + 1];
/* Suppress getopt error messages. */
memset(specified, 0, sizeof(specified));
rc = EXIT_OK;
opterr = 0;
while ((opt = getopt_long(argc, argv, opt_str, opt_list, NULL)) != -1) {
switch (opt) {
case OPT_ALL:
/* --all */
opts->select->all = 1;
break;
case OPT_CONFIGURED:
/* --configured */
opts->select->configured = 1;
break;
case OPT_EXISTING:
/* --existing */
opts->select->existing = 1;
break;
case OPT_ONLINE:
/* --online */
opts->select->online = 1;
break;
case OPT_OFFLINE:
/* --offline */
opts->select->offline = 1;
break;
case OPT_FAILED:
/* --failed */
opts->select->failed = 1;
break;
case OPT_IPLDEV:
/* --ipldev */
opts->select->ipldev = 1;
break;
case OPT_BY_PATH:
/* --by-path MOUNTPOINT */
strlist_add(&opts->select->by_path, "%s", optarg);
break;
case OPT_BY_NODE:
/* --by-node NODE */
strlist_add(&opts->select->by_node, "%s", optarg);
break;
case OPT_BY_INTERFACE:
/* --by-interface NAME */
strlist_add(&opts->select->by_if, "%s", optarg);
break;
case OPT_BY_ATTRIB:
/* --by-attrib NAME */
if (!strchr(optarg, '=')) {
syntax("--by-attrib requires argument in "
"ATTRIB=VALUE or ATTRIB!=VALUE "
"format\n");
return EXIT_USAGE_ERROR;
}
strlist_add(&opts->select->by_attr, "%s", optarg);
break;
case OPT_TYPE:
/* --type */
opts->type = 1;
break;
case OPT_ENABLE:
/* --enable */
opts->enable = 1;
break;
case OPT_DECONFIGURE:
/* --disable */
opts->deconfigure = 1;
break;
case OPT_DECONFIGURE_ALL:
/* --disable all the settings*/
opts->deconfigure = 1;
opts->deconfigure_all = 1;
break;
case OPT_LIST_ATTRIBS:
/* --list-attributes */
opts->list_attribs = 1;
break;
case OPT_LIST_TYPES:
/* --list-types */
opts->list_types = 1;
break;
case OPT_HELP_ATTRIBS:
/* --help-attributes */
opts->help_attribs = 1;
break;
case OPT_EXPORT:
/* --export */
if (opts->export) {
error("Cannot specify '--export' multiple "
"times\n");
return EXIT_USAGE_ERROR;
}
opts->export = misc_strdup(optarg);
break;
case OPT_IMPORT:
/* --import */
if (opts->import) {
error("Cannot specify '--import' multiple "
"times\n");
return EXIT_USAGE_ERROR;
}
opts->import = misc_strdup(optarg);
break;
case OPT_IS_OWNER:
/* --is-owner */
if (opts->is_owner) {
error("Cannot specify '--is-owner' multiple "
"times\n");
return EXIT_USAGE_ERROR;
}
opts->is_owner = misc_strdup(optarg);
break;
case OPT_APPLY:
/* --apply */
opts->apply = 1;
break;
case OPT_ACTIVE:
/* --active */
opts->active = 1;
break;
case OPT_PERSISTENT:
/* --persistent */
opts->persistent = 1;
break;
case OPT_AUTO_CONF:
/* --auto-conf */
opts->auto_conf = 1;
break;
case OPT_REMOVE:
/* --remove ATTRIB */
strlist_add(opts->remove, "%s", optarg);
break;
case OPT_REMOVE_ALL:
/* --remove-all */
opts->remove_all = 1;
break;
case OPT_FORCE:
/* --force */
opts->force = 1;
break;
case OPT_YES:
/* --yes */
opts->yes = 1;
break;
case OPT_NO_ROOT_UPDATE:
/* --no-root-update */
opts->no_root_check = 1;
break;
case OPT_DRY_RUN:
/* --dry-run */
opts->dryrun = 1;
break;
case OPT_BASE:
/* --base PATH */
strlist_add(opts->base, "%s", optarg);
break;
case OPT_HELP:
/* --help */
opts->help = 1;
/* --help has precedence - exit early */
return EXIT_OK;
case OPT_VERSION:
/* --version */
opts->version = 1;
/* --version has precedence - exit early */
return EXIT_OK;
case OPT_VERBOSE:
/* --verbose */
opts->verbose = 1;
break;
case OPT_QUIET:
/* --quiet */
opts->quiet = 1;
break;
case OPT_NO_SETTLE:
/* --no-settle */
opts->no_settle = 1;
break;
case OPT_NO_MODULE_LOAD:
/* --no-module-load */
opts->no_module_load = 1;
break;
case OPT_SITE:
/* --site */
/* 1. User can specify only site-ids from 0 to 9
* 2. only one --site parameter is accepted
*/
if (!is_valid_site(optarg)) {
syntax("Unsupported site ID\n");
return EXIT_USAGE_ERROR;
}
if (opts->site_id != SITE_FALLBACK) {
syntax("Cannot specify '--site' multiple "
"times\n");
return EXIT_USAGE_ERROR;
}
opts->site_id = atoi(optarg);
global_site_id = opts->site_id;
opts->persistent = 1;
break;
case ':':
/* Missing option argument. */
syntax("Option '%s' requires an argument\n",
argv[optind - 1]);
return EXIT_USAGE_ERROR;
case '?':
/* Unknown option character. */
if (optopt)
syntax("Unrecognized option '-%c'\n", optopt);
else {
syntax("Unrecognized option '%s'\n",
argv[optind - 1]);
}
return EXIT_USAGE_ERROR;
default:
break;
}
if (opt >= 0 && opt <= OPTS_MAX)
specified[opt] = 1;
/* Check after each option to report errors in order of
* specification. */
rc = check_options(opts, specified, opt);
if (rc)
break;
}
if (rc)
goto out;
/* Determine configuration set. */
if (opts->deconfigure_all) {
opts->config = config_all;
} else if (!opts->active && !opts->persistent && !opts->auto_conf) {
/* Default configuration targets are active + persistent */
opts->config = config_active | config_persistent;
} else {
opts->config = get_config(opts->active, opts->persistent,
opts->auto_conf);
}
/* Handle positional parameters. */
rc = parse_positional(opts, argc, argv, optind);
/* Set implicit settings. */
if (!select_opts_dev_specified(opts->select)) {
switch (get_action(opts)) {
case ACT_IMPORT:
/* --import without --type or device spec
* selects both type and all devices. */
if (!opts->type) {
opts->select->all = 1;
opts->type = 1;
}
break;
default:
break;
}
}
out:
return rc;
}
static bool setting_is_removable(struct setting *s, int active)
{
if (active) {
if (!s->attrib || !s->attrib->activerem)
return false;
}
if (s->derived)
return false;
if (s->attrib && s->attrib->mandatory)
return false;
return true;
}
static int count_removable(struct setting_list *list, int active)
{
struct setting *s;
int removable = 0;
util_list_iterate(&list->list, s) {
if (setting_is_removable(s, active))
removable++;
}
return removable;
}
/* Remove all settings. */
static void remove_all_settings(struct setting_list *settings, int active)
{
struct setting *s;
int modified = 0;
util_list_iterate(&settings->list, s) {
if (!setting_is_removable(s, active))
continue;
s->removed = 1;
s->modified = 1;
modified = 1;
}
if (modified)
settings->modified = 1;
}
/* Perform --remove-all operation for specified config on device. */
static exit_code_t device_remove_all(struct device *dev, config_t config)
{
int active, persistent, autoconf;
active = SCOPE_ACTIVE(config) ?
count_removable(dev->active.settings, 1) : 0;
persistent = SCOPE_PERSISTENT(config) ?
count_removable(dev->persistent.settings, 0) : 0;
autoconf = SCOPE_AUTOCONF(config) ?
count_removable(dev->autoconf.settings, 0) : 0;
if (active == 0 && persistent == 0 && autoconf == 0) {
delayed_err("No removable settings found\n");
return EXIT_SETTING_NOT_FOUND;
}
if (active)
remove_all_settings(dev->active.settings, 1);
if (persistent)
remove_all_settings(dev->persistent.settings, 0);
if (autoconf)
remove_all_settings(dev->autoconf.settings, 0);
return EXIT_OK;
}
/* Remove settings specified by NAMES from a list of SETTINGS. */
static exit_code_t remove_settings(struct setting_list *settings,
struct util_list *names,
struct util_list *found,
struct util_list *notfound,
int check_activerem)
{
struct strlist_node *s;
struct setting *set;
util_list_iterate(names, s) {
set = setting_list_find(settings, s->str);
if (set && !set->derived) {
if (set->attrib && set->attrib->mandatory)
goto err_mandatory;
if (check_activerem && set->attrib &&
!set->attrib->activerem)
goto err_activerem;
strlist_add_unique(found, "%s", s->str);
set->removed = 1;
set->modified = 1;
continue;
}
strlist_add_unique(notfound, "%s", s->str);
}
return EXIT_OK;
err_mandatory:
delayed_err("Cannot remove setting for mandatory attribute '%s'\n",
s->str);
return EXIT_USAGE_ERROR;
err_activerem:
delayed_err("Cannot remove setting '%s' from the active "
"configuration\n", s->str);
return EXIT_USAGE_ERROR;
}
/* Perform --remove operation on specified device. */
static exit_code_t device_remove_settings(struct device *dev,
struct util_list *names,
config_t config)
{
struct util_list *found, *notfound;
char *flat;
exit_code_t rc = EXIT_OK;
found = strlist_new();
notfound = strlist_new();
if (SCOPE_ACTIVE(config)) {
rc = remove_settings(dev->active.settings, names, found,
notfound, 1);
if (rc)
goto out;
}
if (SCOPE_PERSISTENT(config)) {
rc = remove_settings(dev->persistent.settings, names, found,
notfound, 0);
if (rc)
goto out;
}
if (SCOPE_AUTOCONF(config)) {
rc = remove_settings(dev->autoconf.settings, names, found,
notfound, 0);
if (rc)
goto out;
}
if (!util_list_is_empty(notfound)) {
flat = strlist_flatten(notfound, " ");
delayed_err("Setting not found: %s\n", flat);
free(flat);
rc = EXIT_SETTING_NOT_FOUND;
}
out:
strlist_free(found);
strlist_free(notfound);
return rc;
}
static void ensure_online(struct device *dev, config_t config)
{
struct subtype *st = dev->subtype;
if (subtype_online_specified(st, dev, config))
return;
if (subtype_online_get(st, dev, config) == 1)
return;
subtype_online_set(st, dev, 1, config);
}
static void print_devs(struct util_list *list, devnode_t type)
{
struct ptrlist_node *p;
struct devnode *d;
int first;
first = 1;
util_list_iterate(list, p) {
d = p->ptr;
if (d->type != type)
continue;
if (first) {
switch (type) {
case BLOCKDEV:
info(" Block devices: ");
break;
case CHARDEV:
info(" Character devices: ");
break;
case NETDEV:
info(" Network interface: ");
break;
default:
break;
}
first = 0;
} else
info(" ");
info("%s%s", type != NETDEV ? "/dev/" : "", d->name);
}
if (!first)
info("\n");
}
static void print_dev_config_info(struct device *dev, config_t config)
{
struct subtype *st = dev->subtype;
char *changes;
struct util_list *devnodes;
changes = setting_get_changes(
SCOPE_ACTIVE(config) ? dev->active.settings : NULL,
SCOPE_PERSISTENT(config) ? dev->persistent.settings : NULL,
SCOPE_AUTOCONF(config) ? dev->autoconf.settings : NULL);
if (changes)
info(" Changes: %s\n", changes);
free(changes);
/* Wait for potential renaming udev rules to finish. */
udev_settle();
devnodes = subtype_get_devnodes(st, dev->id);
if (devnodes) {
print_devs(devnodes, BLOCKDEV);
print_devs(devnodes, CHARDEV);
print_devs(devnodes, NETDEV);
ptrlist_free(devnodes, 1);
}
}
static exit_code_t cfg_read(struct subtype *st, const char *id,
config_t config, read_scope_t scope, int reread,
struct device **dev_ptr)
{
exit_code_t rc;
struct device *dev;
struct namespace *ns = st->namespace;
/* Handle blacklist. */
if (ns->is_id_blacklisted && ns->unblacklist_id &&
ns->is_id_blacklisted(id))
ns->unblacklist_id(id);
/* Read configuration. */
if (reread)
rc = subtype_reread_device(st, id, config, scope, &dev);
else
rc = subtype_read_device(st, id, config, scope, &dev);
if (rc == EXIT_OK && dev_ptr)
*dev_ptr = dev;
return rc;
}
static exit_code_t cfg_mod_existence(struct device *dev, config_t config)
{
/* Create active config if necessary. */
if (SCOPE_ACTIVE(config) && !dev->active.exists) {
if (!dev->active.definable)
return EXIT_DEVICE_NOT_FOUND;
dev->active.exists = 1;
dev->active.modified = 1;
}
/* Create persistent config if necessary. */
if (SCOPE_PERSISTENT(config) && !dev->persistent.exists) {
dev->persistent.exists = 1;
dev->persistent.modified = 1;
}
/* Create autoconf config if necessary. */
if (SCOPE_AUTOCONF(config) && !dev->autoconf.exists) {
dev->autoconf.exists = 1;
dev->autoconf.modified = 1;
}
return EXIT_OK;
}
static exit_code_t cfg_mod_settings(struct device *dev, struct options *opts,
int prereq)
{
exit_code_t rc;
if (prereq)
goto online;
/* Remove all settings. */
if (opts->remove_all) {
rc = device_remove_all(dev, opts->config);
if (rc)
return rc;
}
/* Remove a list of settings. */
if (!util_list_is_empty(opts->remove)) {
rc = device_remove_settings(dev, opts->remove, opts->config);
if (rc)
return rc;
}
/* Apply settings. */
if (!util_list_is_empty(opts->settings)) {
rc = device_apply_strlist(dev, opts->config, opts->settings);
if (rc)
return rc;
}
if (!opts->enable)
goto mand;
online:
/* Make sure there's an online attribute. */
ensure_online(dev, config_active);
ensure_online(dev, config_persistent);
ensure_online(dev, config_autoconf);
mand:
/* Ensure default values for mandatory attributes. */
setting_list_apply_defaults(dev->persistent.settings,
dev->subtype->dev_attribs, true);
setting_list_apply_defaults(dev->autoconf.settings,
dev->subtype->dev_attribs, true);
return EXIT_OK;
}
static exit_code_t handle_nonexistent(struct device *dev)
{
struct subtype *st = dev->subtype, *other_st;
struct namespace *ns = st->namespace;
if (namespaces_device_exists(ns, dev->id, config_active, &other_st) &&
dev->subtype != other_st) {
if (force) {
delayed_warn("Configuring %s %s as %s\n",
other_st->devname, dev->id, st->devname);
return EXIT_OK;
}
delayed_forceable("Trying to configure %s %s as %s\n",
other_st->devname, dev->id, st->devname);
return EXIT_INVALID_DEVTYPE;
}
delayed_warn("%s %s does not exist in active configuration\n",
st->devname, dev->id);
return EXIT_OK;
}
static exit_code_t cfg_write(struct device *dev, int prereq, config_t config,
int check_active)
{
struct subtype *st = dev->subtype;
exit_code_t rc = EXIT_OK;
if (!device_needs_writing(dev, config) && !force)
goto out;
if (check_active && !SCOPE_ACTIVE(config) &&
(SCOPE_PERSISTENT(config) || SCOPE_AUTOCONF(config)) &&
(!dev->active.exists && !dev->active.definable)) {
rc = handle_nonexistent(dev);
if (rc)
return rc;
}
/* Pre-write check. */
rc = subtype_check_pre_configure(st, dev, prereq, config);
if (rc && !force)
goto out;
/* Write configuration. */
if (SCOPE_PERSISTENT(config))
pers_mod_devs++;
rc = subtype_write_device(st, dev, config);
if (rc)
goto out;
/* Post-write check. */
rc = subtype_check_post_configure(st, dev, prereq, config);
if (rc && !force)
goto out;
out:
return rc;
}
/* Apply changes in @opts to the device defined by @st and @id. */
static exit_code_t cfg_configure(struct subtype *st, const char *id,
struct options *opts, int try, int prereq,
struct device **dev_ptr, int *proc_ptr)
{
config_t config = opts->config;
exit_code_t rc;
struct device *dev;
/* Reset processed flag. */
if (proc_ptr)
*proc_ptr = 0;
/* Read current device data. Note that we read data from all
* configurations to enable QETH autodetection and subtype
* detection (e.g. dasd -> dasd_fba).*/
dev = NULL;
rc = cfg_read(st, id, config_all, scope_known, 0, &dev);
if (dev_ptr)
*dev_ptr = dev;
if (rc)
return rc;
/* Skip if already processed. */
if (dev->processed)
return EXIT_OK;
/* Mark device as processed. */
if (proc_ptr)
*proc_ptr = 1;
dev->processed = 1;
/* Exit here if we're only trying and device cannot be configured. */
if (try && !(dev->active.exists || dev->active.definable ||
dev->persistent.exists || dev->autoconf.exists)) {
return EXIT_DEVICE_NOT_FOUND;
}
/* Create device if needed by action. */
if (opts->enable || !util_list_is_empty(opts->settings)) {
rc = cfg_mod_existence(dev, config);
if (rc)
return rc;
}
/* Apply changes to device settings. */
rc = cfg_mod_settings(dev, opts, prereq);
if (rc)
return rc;
/* Write resulting device data. */
return cfg_write(dev, prereq, config, 1);
}
/* Apply persistent configuration to active configuration. */
static exit_code_t cfg_apply(struct subtype *st, const char *id, int prereq,
struct device **dev_ptr, int *proc_ptr,
bool autoconf)
{
exit_code_t rc;
struct device *dev;
struct device_state *state;
/* Reset processed flag. */
if (proc_ptr)
*proc_ptr = 0;
/* Read current device data. */
dev = NULL;
rc = cfg_read(st, id, config_all, scope_known, 1, &dev);
if (dev_ptr)
*dev_ptr = dev;
if (rc)
return rc;
/* Skip if already processed. */
if (dev->processed)
return EXIT_OK;
/* Mark device as processed. */
if (proc_ptr)
*proc_ptr = 1;
dev->processed = 1;
state = autoconf ? &dev->autoconf : &dev->persistent;
/* Exit here if there is no persistent configuration. */
if (!state->exists)
return EXIT_NO_DATA;
/* Apply changes to device existence. */
rc = cfg_mod_existence(dev, config_active);
if (rc)
return rc;
/* Copy persistent settings to active configuration. */
rc = device_apply_settings(dev, config_active, &state->settings->list);
if (rc)
return rc;
/* Write resulting device data. */
return cfg_write(dev, prereq, config_active, 1);
}
/* Apply imported configuration. */
static exit_code_t cfg_import(struct subtype *st, const char *id,
config_t config, int prereq,
struct device **dev_ptr, int *proc_ptr)
{
exit_code_t rc;
struct setting_list *active = NULL, *persistent = NULL,
*autoconf = NULL;
struct device *dev;
/* Reset processed flag. */
if (proc_ptr)
*proc_ptr = 0;
/* Get imported data. */
dev = device_list_find(st->devices, id, NULL);
if (dev_ptr)
*dev_ptr = dev;
if (dev) {
/* Only import each device once. */
if (dev->processed)
return EXIT_OK;
active = setting_list_copy(dev->active.settings);
persistent = setting_list_copy(dev->persistent.settings);
autoconf = setting_list_copy(dev->autoconf.settings);
} else if (!prereq) {
/* Should not happen. */
return EXIT_OK;
}
/* Read current device data. */
rc = cfg_read(st, id, config, scope_known, 1, &dev);
if (rc)
goto out;
/* Mark device as processed. */
if (proc_ptr)
*proc_ptr = 1;
dev->processed = 1;
/* Apply changes to device existence. */
rc = cfg_mod_existence(dev, config);
if (rc)
goto out;
if (!prereq) {
/* Copy target settings to device configuration. */
rc = device_apply_settings(dev, config_active, &active->list);
if (rc)
goto out;
rc = device_apply_settings(dev, config_persistent,
&persistent->list);
if (rc)
goto out;
rc = device_apply_settings(dev, config_autoconf,
&autoconf->list);
if (rc)
goto out;
}
ensure_online(dev, config_active);
ensure_online(dev, config_persistent);
ensure_online(dev, config_autoconf);
/* Write resulting device data. */
rc = cfg_write(dev, prereq, config, 0);
out:
setting_list_free(autoconf);
setting_list_free(persistent);
setting_list_free(active);
return rc;
}
static exit_code_t print_generic_err(struct selected_dev_node *sel,
struct device *dev, exit_code_t rc)
{
struct subtype *other_st;
struct subtype *st;
int print = 1;
exit_code_t rc2;
if (rc != EXIT_DEVICE_NOT_FOUND || !sel)
goto out;
st = sel->st;
/* Check if there is a subtype mismatch. */
if (st && dev && !(dev->active.exists || dev->active.definable) &&
namespaces_device_exists(st->namespace, sel->id, config_active,
&other_st) && st != other_st) {
delayed_err("Trying to configure %s %s as %s\n",
other_st->devname, sel->id, st->devname);
rc = EXIT_INVALID_DEVTYPE;
print = 0;
goto out;
}
if (!sel->dt || devtype_count_namespaces(sel->dt) != 1)
goto out;
if (namespaces_device_exists(sel->dt->subtypes[0]->namespace,
sel->id, config_active, &other_st) &&
other_st->devtype != sel->dt) {
delayed_err("Trying to configure %s %s as %s\n",
other_st->devname, sel->id, sel->dt->devname);
rc = EXIT_INVALID_DEVTYPE;
print = 0;
goto out;
}
/* Try to get more information in case of a problem with definable
* devices. */
if (st && st->support_definable && delayed_errors == 0) {
rc2 = subtype_device_is_definable(st, sel->id,
err_delayed_print);
if (rc2) {
rc = rc2;
print = 0;
}
}
out:
if (print)
delayed_err("%s\n", exit_code_to_str(rc));
return rc;
}
static exit_code_t print_config_result(struct selected_dev_node *sel,
struct device *dev, struct options *opts,
config_t config, exit_code_t rc,
int prereq, int proc)
{
const char *devname, *devid, *op, *verb;
int already;
/* Exit here if a message for this device has already been printed. */
if (!proc)
return rc;
already = 0;
if (opts->deconfigure) {
op = "deconfigure";
verb = "deconfigured";
if (dev) {
already = !dev->active.modified &&
!dev->persistent.modified &&
!dev->autoconf.modified;
} else if (rc == EXIT_DEVICE_NOT_FOUND &&
(!SCOPE_ACTIVE(config) && (SCOPE_PERSISTENT(config) ||
SCOPE_AUTOCONF(config)))) {
rc = EXIT_OK;
already = 1;
}
} else {
op = "configure";
verb = "configured";
if (dev)
already = device_needs_writing(dev, config) ? 0 : 1;
}
/* Re-do actions if run with --force */
if (force)
already = 0;
if (dev) {
devname = dev->subtype->devname;
devid = dev->id;
} else if (sel && sel->st) {
devname = sel->st->devname;
devid = sel->id ? sel->id : sel->param;
} else if (sel && sel->dt) {
devname = sel->dt->devname;
devid = sel->id ? sel->id : sel->param;
} else {
devname = "Device";
if (sel)
devid = sel->id ? sel->id : sel->param;
else
devid = "";
}
if (rc) {
/* Error message. */
warn("%s %s %s failed%s\n", devname, devid, op,
prereq ? " (prerequisite)" : "");
/* Add the overall error message in case no other error message
* has been queued. */
if (delayed_errors == 0)
rc = print_generic_err(sel, dev, rc);
} else if (already) {
if (!prereq || delayed_messages_available()) {
/* Already done message. */
printf("%s %s already %s\n", devname, devid, verb);
}
} else {
/* Success message. */
printf("%s %s %s%s\n", devname, devid, verb,
prereq ? " (prerequisite)" : "");
if (verbose && dev)
print_dev_config_info(dev, config);
}
/* Show all delayed messages. */
delayed_print(DELAY_INDENT);
return rc;
}
static exit_code_t cfg_prereqs(struct subtype *st, const char *id,
struct options *opts, config_t config, int try)
{
struct util_list *prereqs;
struct selected_dev_node *sel;
exit_code_t rc = EXIT_OK;
struct device *dev;
int proc;
prereqs = selected_dev_list_new();
subtype_add_prereqs(st, id, prereqs);
util_list_iterate(prereqs, sel) {
if (opts->apply) {
rc = cfg_apply(sel->st, sel->id, 1, &dev, &proc,
opts->auto_conf);
} else if (opts->import) {
rc = cfg_import(sel->st, sel->id, config, 1, &dev,
&proc);
} else {
rc = cfg_configure(sel->st, sel->id, opts, try, 1,
&dev, &proc);
}
rc = print_config_result(sel, dev, opts, config, rc, 1, proc);
if (rc) {
if (st == &zfcp_lun_subtype &&
rc == EXIT_DEVICE_NOT_FOUND) {
/* Use special exit code for FCP. */
rc = EXIT_ZFCP_FCP_NOT_FOUND;
}
if (try)
break;
warn("%s %s configure failed\n", st->devname, id);
delayed_err("Could not configure prerequisite %s %s\n",
sel->st->devname, sel->id);
delayed_print(DELAY_INDENT);
break;
}
}
selected_dev_list_free(prereqs);
return rc;
}
static void handle_blacklist_range(struct namespace *ns, const char *range)
{
if (!ns->is_id_range_blacklisted || !ns->unblacklist_id_range)
return;
if (!ns_is_id_range_valid(ns, range))
return;
if (ns->is_id_range_blacklisted(range))
ns->unblacklist_id_range(range);
}
/* Try to perform unblacklisting in ranges while minimizing calls to
* handle_blacklist_range (that is, only call when namespace or param
* changed). */
static void unblacklist_ranges(struct selected_dev_node *sel,
struct namespace **ns_ptr,
const char **param_ptr)
{
struct namespace *ns = *ns_ptr;
const char *param = *param_ptr;
if (ns == sel->st->namespace)
return;
if (!sel->param)
return;
if (param && strcmp(param, sel->param) == 0)
return;
ns = sel->st->namespace;
param = sel->param;
handle_blacklist_range(ns, param);
*ns_ptr = ns;
*param_ptr = param;
}
/* Currently the site-specific configurations are supported only
* for some device types. Make sure that user get an error message when
* try to configure other device-types with site parameter.
*/
static exit_code_t is_site_supported(struct util_list *selected)
{
struct selected_dev_node *sel;
util_list_iterate(selected, sel) {
if (sel->dt && !sel->dt->site_support) {
error("Site specific configuration is not supported"
" for %s devices\n", sel->dt->devname);
return EXIT_USAGE_ERROR;
}
}
return EXIT_OK;
}
/* Handle device configuration. */
static exit_code_t configure_devices(struct options *opts, int specified,
int *found_ptr)
{
struct util_list *selected;
struct selected_dev_node *sel;
exit_code_t rc, drc = EXIT_OK;
int existing, proc;
struct namespace *ns;
const char *param;
struct device *dev;
config_t config = opts->config;
/* Determine list of selected devices. */
if ((!SCOPE_ACTIVE(config) &&
(SCOPE_PERSISTENT(config) || SCOPE_AUTOCONF(config))) ||
(opts->select->subtype && opts->select->subtype->support_definable))
existing = 0;
else
existing = 1;
selected = selected_dev_list_new();
rc = select_devices(opts->select, selected, existing, 1, 0,
opts->config, scope_known, err_print);
if (rc)
goto out;
if (util_list_is_empty(selected)) {
if (!specified) {
rc = EXIT_OK;
goto out;
}
error("No device was selected!\n");
rc = EXIT_EMPTY_SELECTION;
goto out;
} else if (opts->site_id != SITE_FALLBACK) {
if (is_site_supported(selected))
goto out;
}
/* Work on selected devices. */
ns = NULL;
param = NULL;
util_list_iterate(selected, sel) {
dev = NULL;
proc = 0;
rc = sel->rc;
if (rc) {
proc = 1;
/* If select_devices didn't find the device with any
* type in the active config, it does not exist. */
if (rc == EXIT_INCOMPLETE_TYPE &&
SCOPE_ACTIVE(opts->config))
rc = EXIT_DEVICE_NOT_FOUND;
goto next;
}
/* Attempt to perform efficient unblacklisting in ranges. */
unblacklist_ranges(sel, &ns, &param);
/* Configure potential prerequisite devices. */
rc = cfg_prereqs(sel->st, sel->id, opts, opts->config, 0);
if (rc)
goto next;
/* Configure actual target device. */
if (opts->apply) {
rc = cfg_apply(sel->st, sel->id, 0, &dev, &proc,
opts->auto_conf);
} else {
rc = cfg_configure(sel->st, sel->id, opts, 0,
0, &dev, &proc);
}
next:
/* Print results. */
rc = print_config_result(sel, dev, opts, opts->config, rc, 0,
proc);
/* Remember first non-zero exit code. */
if (rc && !drc)
drc = rc;
/* Skip device IDs which are combined in this one. */
if (rc == EXIT_OK && dev) {
if (found_ptr)
(*found_ptr)++;
/* Note: selected is modified but since we're not
* using util_list_iterate_safe, the next
* element will be correctly taken from the
* modified list. */
subtype_rem_combined(sel->st, dev, sel, selected);
}
}
out:
selected_dev_list_free(selected);
return drc ? drc : rc;
}
static exit_code_t check_in_use(struct device *dev)
{
struct util_list *res;
struct strlist_node *s;
if (force)
return EXIT_OK;
res = inuse_get_resources(dev);
if (!res)
return EXIT_OK;
warn("Warning: %s %s is in use!\n", dev->subtype->devname, dev->id);
warn(" The following resources may be affected:\n");
util_list_iterate(res, s)
warn(" - %s\n", s->str);
if (!confirm("Continue with operation?"))
return EXIT_ABORTED;
return EXIT_OK;
}
static bool is_site_modified(struct device *dev)
{
int i;
for (i = 0; i < NUM_USER_SITES; i++) {
if (dev->site_specific[i].modified)
return true;
}
return false;
}
/* Deconfigure specified device. */
static exit_code_t deconfigure_one_device(struct subtype *st, const char *id,
struct options *opts, int try,
struct device **dev_ptr,
int *proc_ptr)
{
exit_code_t rc;
struct device *dev;
config_t config, read_config;
int i, site_available = 0;
/* Reset processed flag. */
if (proc_ptr)
*proc_ptr = 0;
config = opts->config;
/* For trial configuration runs, we need information about the active
* configuration. */
if (try && !SCOPE_ACTIVE(config))
read_config = config_all;
else
read_config = opts->config;
/* Read configuration. */
dev = NULL;
rc = subtype_read_device(st, id, read_config, scope_mandatory, &dev);
if (dev_ptr)
*dev_ptr = dev;
if (rc)
return rc;
/* Selection can queue a device ID multiple times - filter out
* double selections here. */
if (dev->processed)
return EXIT_OK;
/* Mark device as processed. */
if (proc_ptr)
*proc_ptr = 1;
dev->processed = 1;
if (try && !(dev->active.exists || dev->active.definable ||
is_dev_pers(dev) || dev->autoconf.exists)) {
/* Attempt to deconfigure this device will fail. */
return EXIT_DEVICE_NOT_FOUND;
}
/* Check if device exists in active config - we allow deconfigure if
* device doesn't exist in active when there may be a persistent
* dev. */
if (SCOPE_ACTIVE(config) &&
!(dev->active.exists || dev->active.definable)) {
if (!((SCOPE_PERSISTENT(config) && is_dev_pers(dev)) ||
(SCOPE_AUTOCONF(config) && dev->autoconf.exists)))
return EXIT_DEVICE_NOT_FOUND;
}
/* Sanity check before deconfiguring devices that are in use. */
rc = check_in_use(dev);
if (rc)
return rc;
/* Deconfigure. */
if (SCOPE_ACTIVE(config)) {
dev->active.deconfigured = 1;
if (st->support_definable) {
/* Deconfigure for configurable devices means
* undefine. */
if (dev->active.exists)
dev->active.modified = 1;
} else if (subtype_online_get(st, dev, config_active) == 1)
dev->active.modified = 1;
}
if (SCOPE_PERSISTENT(config)) {
if (dev->persistent.exists) {
dev->persistent.deconfigured = 1;
dev->persistent.modified = 1;
} else if (is_dev_pers(dev) && global_site_id != SITE_FALLBACK) {
dev->persistent.deconfigured = 1;
dev->persistent.modified = 1;
}
}
if (SCOPE_AUTOCONF(config) && dev->autoconf.exists) {
dev->autoconf.deconfigured = 1;
dev->autoconf.modified = 1;
}
/* In case of deconfigure_all command, make sure that site specific
* settings are also removed.
*/
if (opts->deconfigure_all) {
for (i = 0; i < NUM_USER_SITES; i++) {
if (dev->site_specific[i].exists) {
site_available++;
/* To remove site-specific settings, simply
* make the deconfigured = 1
*/
dev->site_specific[i].deconfigured = 1;
dev->site_specific[i].modified = 1;
}
}
}
if (site_available) {
/* set persistent.exists = 1 to let the
* drivers know that we need to modify the
* udev-rule
*/
dev->persistent.exists = 1;
dev->persistent.modified = 1;
dev->persistent.deconfigured = 1;
}
if (!dev->active.modified && !dev->persistent.modified &&
!dev->autoconf.modified && !is_site_modified(dev))
return EXIT_OK;
/* Pre-write check. */
rc = subtype_check_pre_configure(st, dev, 0, config);
if (rc && !force)
return rc;
/* Write configuration. */
if (SCOPE_PERSISTENT(config))
pers_mod_devs++;
rc = subtype_write_device(st, dev, config);
if (rc)
return rc;
/* Post-write check. */
rc = subtype_check_post_configure(st, dev, 0, config);
if (rc && !force)
return rc;
return EXIT_OK;
}
/* Handle device deconfiguration. */
static exit_code_t deconfigure_devices(struct options *opts)
{
struct util_list *selected;
struct selected_dev_node *sel;
exit_code_t rc, drc = EXIT_OK;
int proc;
struct device *dev;
/* Determine list of selected devices. */
selected = selected_dev_list_new();
rc = select_devices(opts->select, selected, 1, 0, 0, opts->config,
scope_mandatory, err_print);
if (rc)
goto out;
if (util_list_is_empty(selected)) {
error("No device was selected!\n");
rc = EXIT_EMPTY_SELECTION;
goto out;
} else if (opts->site_id != SITE_FALLBACK) {
if (is_site_supported(selected))
goto out;
}
/* Work on selected devices. */
util_list_iterate(selected, sel) {
dev = NULL;
proc = 0;
if (sel->rc) {
rc = EXIT_DEVICE_NOT_FOUND;
proc = 1;
goto next;
}
/* Deconfigure device. */
rc = deconfigure_one_device(sel->st, sel->id, opts, 0,
&dev, &proc);
next:
/* Print results. */
rc = print_config_result(sel, dev, opts, opts->config, rc, 0,
proc);
if (rc && !drc)
drc = rc;
/* Skip devices which are combined in this one. */
if (rc == EXIT_OK && dev) {
/* Note: selected is modified but since we're not
* using util_list_iterate_safe, the next
* element will be correctly taken from the
* modified list. */
subtype_rem_combined(sel->st, dev, sel, selected);
}
}
out:
selected_dev_list_free(selected);
return drc ? drc : rc;
}
/* Perform --remove operation for specified config on devtype. */
static exit_code_t devtype_remove_settings(struct devtype *dt, config_t config,
struct util_list *names)
{
struct util_list *found, *notfound;
char *flat;
exit_code_t rc = EXIT_OK;
found = strlist_new();
notfound = strlist_new();
if (SCOPE_ACTIVE(config)) {
rc = remove_settings(dt->active_settings, names, found,
notfound, 1);
if (rc)
goto out;
}
if (SCOPE_PERSISTENT(config)) {
rc = remove_settings(dt->persistent_settings, names, found,
notfound, 0);
if (rc)
goto out;
}
if (!util_list_is_empty(notfound)) {
flat = strlist_flatten(notfound, " ");
delayed_err("Setting not found: %s\n", flat);
free(flat);
rc = EXIT_SETTING_NOT_FOUND;
}
out:
strlist_free(found);
strlist_free(notfound);
return rc;
}
/* Perform --remove-all operation for specified config on devtype. */
static exit_code_t devtype_remove_all(struct devtype *dt, config_t config)
{
int active, persistent;
active = SCOPE_ACTIVE(config) ?
count_removable(dt->active_settings, 1) : 0;
persistent = SCOPE_PERSISTENT(config) ?
count_removable(dt->persistent_settings, 0) : 0;
if (active == 0 && persistent == 0) {
delayed_err("No removable settings found\n");
return EXIT_SETTING_NOT_FOUND;
}
if (active)
remove_all_settings(dt->active_settings, 1);
if (persistent)
remove_all_settings(dt->persistent_settings, 0);
return EXIT_OK;
}
static void print_type_config_info(struct devtype *dt, const char *title,
config_t config)
{
char *changes;
changes = setting_get_changes(
SCOPE_ACTIVE(config) ? dt->active_settings : NULL,
SCOPE_PERSISTENT(config) ? dt->persistent_settings : NULL,
NULL);
if (changes)
info(" %s: %s\n", title, changes);
free(changes);
}
static void print_devtype_config_result(struct devtype *dt,
config_t config, exit_code_t rc)
{
if (rc) {
/* Error message. */
warn("%s device type configure failed\n", dt->name);
/* Add the overall error message in case no other error message
* has been queued. */
if (delayed_errors == 0)
delayed_err("%s\n", exit_code_to_str(rc));
} else if (!devtype_needs_writing(dt, config))
/* Already done message. */
printf("%s device type already configured\n", dt->name);
else {
/* Success message. */
printf("%s device type configured\n", dt->name);
if (verbose)
print_type_config_info(dt, "Changes", config);
}
delayed_print(DELAY_INDENT);
}
/* Handle device type settings. */
static exit_code_t configure_devtype(struct options *opts)
{
struct devtype *dt = opts->select->devtype;
exit_code_t rc;
/* Read settings. */
rc = dt->read_settings(dt, opts->config);
if (rc)
goto out;
/* Remove all settings. */
if (opts->remove_all) {
rc = devtype_remove_all(dt, opts->config);
if (rc)
goto out;
}
/* Remove a list of settings. */
if (!util_list_is_empty(opts->remove)) {
rc = devtype_remove_settings(dt, opts->config, opts->remove);
if (rc)
goto out;
}
/* Apply new settings. */
rc = devtype_apply_strlist(dt, opts->config, opts->settings);
if (rc)
goto out;
if (!devtype_needs_writing(dt, opts->config))
goto out;
/* Write settings. */
if (SCOPE_PERSISTENT(opts->config))
pers_mod_devtypes++;
rc = dt->write_settings(dt, opts->config);
out:
print_devtype_config_result(dt, opts->config, rc);
return rc;
}
/* Apply persistent device type settings to active configuration. */
static exit_code_t apply_devtype(struct devtype *dt, int specified,
int *found_ptr)
{
exit_code_t rc;
/* Read settings. */
rc = dt->read_settings(dt, config_all);
if (rc)
return rc;
if (!dt->persistent_settings ||
setting_list_count_set(dt->persistent_settings) == 0) {
/* No persistent configuration found. */
if (!specified)
return EXIT_OK;
rc = EXIT_NO_DATA;
goto out;
}
(*found_ptr)++;
/* Apply new settings. */
rc = devtype_apply_settings(dt, config_active,
&dt->persistent_settings->list);
if (rc)
goto out;
if (!devtype_needs_writing(dt, config_active))
goto out;
/* Write settings. */
rc = dt->write_settings(dt, config_active);
out:
print_devtype_config_result(dt, config_active, rc);
return rc;
}
/* Return a ptrlist of newly allocated struct table_attribs for all device type
* attributes of a devtype. */
static struct util_list *get_type_attribs(struct devtype *dt,
struct util_list *names)
{
int i;
struct util_list *attribs;
struct attrib *a;
struct strlist_node *s;
attribs = ptrlist_new();
if (names) {
util_list_iterate(names, s) {
a = attrib_find(dt->type_attribs, s->str);
if (!a)
goto notfound;
ptrlist_add(attribs, table_attrib_new(NULL, a));
}
} else {
for (i = 0; (a = dt->type_attribs[i]); i++)
ptrlist_add(attribs, table_attrib_new(NULL, a));
}
return attribs;
notfound:
ptrlist_free(attribs, 1);
error("%s type attribute '%s' not found!\n", dt->name, s->str);
return NULL;
}
/* Return a ptrlist of all device attributes of a devtype without duplicates. */
static struct util_list *get_dev_attribs(struct devtype *dt,
struct subtype *st_only,
struct util_list *names)
{
struct subtype *st;
int i, j;
struct util_list *attribs;
struct attrib *a;
struct strlist_node *s;
bool found;
attribs = ptrlist_new();
if (names) {
util_list_iterate(names, s) {
a = NULL;
found = false;
for (i = 0; (st = dt->subtypes[i]); i++) {
if (st_only && st != st_only)
continue;
a = attrib_find(st->dev_attribs, s->str);
if (a) {
ptrlist_add(attribs,
table_attrib_new(st, a));
found = true;
}
}
if (!found)
goto notfound;
}
} else {
for (i = 0; (st = dt->subtypes[i]); i++) {
if (st_only && st != st_only)
continue;
for (j = 0; (a = st->dev_attribs[j]); j++)
ptrlist_add(attribs, table_attrib_new(st, a));
}
}
return attribs;
notfound:
ptrlist_free(attribs, 0);
if (st_only) {
error("%s attribute '%s' not found!\n", st_only->devname,
s->str);
} else {
error("%s attribute '%s' not found!\n", dt->devname, s->str);
}
return NULL;
}
/* Remove duplicate struct type_attrib entries in ptrlist @list. A duplicate
* entry is an entry that occurs @num_subtypes times in @list. */
static void remove_duplicate_attribs(struct util_list *list, int num_subtypes)
{
struct ptrlist_node *curr, *check, *next;
struct table_attrib *curr_t, *check_t;
int num;
if (!list)
return;
util_list_iterate(list, curr) {
num = 1;
curr_t = curr->ptr;
for (check = util_list_next(list, curr); check;
check = util_list_next(list, check)) {
check_t = check->ptr;
if (curr_t->attrib == check_t->attrib)
num++;
}
if (num != num_subtypes)
continue;
/* Remove duplicates. */
curr_t->st = NULL;
check = util_list_next(list, curr);
while (check) {
next = util_list_next(list, check);
check_t = check->ptr;
if (curr_t->attrib == check_t->attrib) {
util_list_remove(list, check);
free(check->ptr);
free(check);
}
check = next;
}
}
}
static exit_code_t do_list_attribs(struct options *opts)
{
struct devtype *dt = opts->select->devtype;
struct subtype *st = opts->select->subtype;
struct util_list *names;
struct util_list *attribs;
exit_code_t rc = EXIT_OK;
names = opts->positional;
if (names && util_list_is_empty(names))
names = NULL;
if (opts->type) {
if (!dt->type_attribs[0]) {
error("Device type %s does not provide type "
"attributes\n", dt->name);
return EXIT_ATTRIB_NOT_FOUND;
}
attribs = get_type_attribs(dt, names);
} else
attribs = get_dev_attribs(dt, st, names);
remove_duplicate_attribs(attribs, devtype_count_subtypes(dt));
if (attribs) {
table_attribs_show(attribs, 1, 0, 0, dt);
ptrlist_free(attribs, 1);
} else
rc = EXIT_ATTRIB_NOT_FOUND;
return rc;
}
static exit_code_t do_help_attribs(struct options *opts)
{
struct devtype *dt = opts->select->devtype;
struct subtype *st = opts->select->subtype;
struct util_list *names;
struct util_list *attribs;
exit_code_t rc = EXIT_OK;
names = opts->positional;
if (names && util_list_is_empty(names))
names = NULL;
if (opts->type)
attribs = get_type_attribs(dt, names);
else
attribs = get_dev_attribs(dt, st, names);
remove_duplicate_attribs(attribs, devtype_count_subtypes(dt));
if (attribs) {
table_attribs_show_details(attribs, dt);
ptrlist_free(attribs, 1);
} else
rc = EXIT_ATTRIB_NOT_FOUND;
return rc;
}
/* List known device types. */
static exit_code_t do_list_types(struct options *opts)
{
return table_types_show(NULL, 1, 0);
}
static exit_code_t export_single_devtype(FILE *fd, struct devtype *dt,
struct options *opts, int *first_ptr)
{
exit_code_t rc;
/* Get settings. */
rc = dt->read_settings(dt, opts->config);
if (rc || dt->processed)
return rc;
dt->processed = 1;
return export_write_devtype(fd, dt, opts->config, first_ptr);
}
static exit_code_t export_devtypes(FILE *fd, struct options *opts,
int *first_ptr)
{
struct devtype *dt;
int i;
exit_code_t rc;
rc = EXIT_OK;
for (i = 0; (dt = devtypes[i]); i++) {
if (opts->select->devtype && dt != opts->select->devtype)
continue;
rc = export_single_devtype(fd, dt, opts, first_ptr);
if (rc)
break;
}
return rc;
}
static void print_export_error(struct selected_dev_node *sel)
{
const char *devname;
if (sel->st)
devname = sel->st->devname;
else if (sel->dt)
devname = sel->dt->devname;
else
devname = "Device";
warn("%s %s export failed\n", devname, sel->id);
warn(" Error: %s\n", exit_code_to_str(sel->rc));
}
static exit_code_t export_devices_and_devtypes(FILE *fd, struct options *opts,
int *first_ptr)
{
struct util_list *selected;
exit_code_t rc, drc = EXIT_OK;
struct selected_dev_node *sel;
struct device *dev;
config_t config = opts->config;
/* Determine list of selected devices. */
selected = selected_dev_list_new();
rc = select_devices(opts->select, selected, 1, 0, 0, opts->config,
scope_known, err_print);
if (rc)
goto out;
if (util_list_is_empty(selected))
goto out;
/* Work on selected devices. */
util_list_iterate(selected, sel) {
if (sel->rc) {
print_export_error(sel);
rc = sel->rc;
goto next;
}
rc = subtype_read_device(sel->st, sel->id, config, scope_known,
&dev);
if (rc || dev->processed)
goto next;
dev->processed = 1;
/* Write out device type configuration. */
if (opts->type) {
rc = export_single_devtype(fd, dev->subtype->devtype,
opts, first_ptr);
if (rc)
goto next;
}
/* Write out device configuration. */
rc = export_write_device(fd, dev, opts->config, first_ptr);
next:
if (rc && !drc)
drc = rc;
}
out:
selected_dev_list_free(selected);
return drc ? drc : rc;
}
static void action_note(const char *msg, config_t config)
{
if (config == config_active)
info("%s the active configuration only\n", msg);
else if (config == config_persistent) {
if (global_site_id == SITE_FALLBACK)
info("%s the persistent configuration only\n", msg);
else
info("%s the site %d configuration only\n", msg,
global_site_id);
}
else if (config == config_autoconf)
info("%s the auto-configuration only\n", msg);
}
/* Export configuration of selected devices and/or device type to file. */
static exit_code_t do_export(struct options *opts)
{
FILE *fd;
exit_code_t rc = EXIT_OK;
int devtype, devices, first;
devtype = opts->type;
devices = select_opts_dev_specified(opts->select);
/* Open output stream. */
if (strcmp(opts->export, "-") == 0) {
fd = stdout;
info("Exporting data to standard output\n");
} else {
info("Exporting data to %s\n", opts->export);
if (!util_path_exists("%s", opts->export)) {
rc = path_create(opts->export);
if (rc)
return rc;
}
fd = fopen(opts->export, "w");
}
if (!fd) {
error("Could not write to file %s: %s\n", opts->export,
strerror(errno));
return EXIT_RUNTIME_ERROR;
}
first = 1;
/* Handle export of device type configuration if no device was
* specified. */
if (devtype && !devices) {
rc = export_devtypes(fd, opts, &first);
if (rc)
goto out;
}
/* Handle export of selected devices and associated device types. */
if (devices)
rc = export_devices_and_devtypes(fd, opts, &first);
out:
if (rc == EXIT_OK) {
if (first) {
error("No settings found to export\n");
rc = EXIT_EMPTY_SELECTION;
}
}
/* Close stream. */
if (fd != stdout)
fclose(fd);
return rc;
}
static exit_code_t import_devtype(struct devtype *dt, config_t config)
{
exit_code_t rc;
struct setting_list *active = NULL, *persistent = NULL;
if (dt->processed)
return EXIT_OK;
/* Save settings. */
if (dt->active_settings) {
active = setting_list_copy(dt->active_settings);
setting_list_free(dt->active_settings);
dt->active_settings = NULL;
}
if (dt->persistent_settings) {
persistent = setting_list_copy(dt->persistent_settings);
setting_list_free(dt->persistent_settings);
dt->persistent_settings = NULL;
}
/* Read settings. */
rc = dt->read_settings(dt, config);
if (rc)
goto out;
dt->processed = 1;
/* Copy target settings to device configuration. */
if (active) {
rc = devtype_apply_settings(dt, config_active, &active->list);
if (rc)
goto out;
}
if (persistent) {
rc = devtype_apply_settings(dt, config_persistent,
&persistent->list);
if (rc)
goto out;
}
if (!devtype_needs_writing(dt, config))
goto out;
/* Write settings. */
if (SCOPE_PERSISTENT(config))
pers_mod_devtypes++;
rc = dt->write_settings(dt, config);
out:
setting_list_free(persistent);
setting_list_free(active);
print_devtype_config_result(dt, config, rc);
return rc;
}
static exit_code_t import_device(struct device *dev, struct options *opts)
{
struct subtype *st = dev->subtype;
exit_code_t rc;
config_t config;
int proc = 0, active, persistent, autoconf;
if (SCOPE_ACTIVE(opts->config))
active = (dev->active.exists || dev->active.definable);
else
active = 0;
if (SCOPE_PERSISTENT(opts->config))
persistent = dev->persistent.exists;
else
persistent = 0;
if (SCOPE_AUTOCONF(opts->config))
autoconf = dev->autoconf.exists;
else
autoconf = 0;
if (!active && !persistent && !autoconf) {
/* Nothing to import */
return EXIT_OK;
}
config = get_config(active, persistent, autoconf);
/* First check for prerequisite devices that need to be configured. */
rc = cfg_prereqs(st, dev->id, opts, config, 0);
if (rc)
goto out;
rc = cfg_import(st, dev->id, config, 0, NULL, &proc);
out:
rc = print_config_result(NULL, dev, opts, config, rc, 0, proc);
return rc;
}
static bool import_device_selected(struct device *dev, struct options *opts)
{
struct select_opts *select = opts->select;
struct subtype *st = dev->subtype;
struct devtype *dt = st->devtype;
struct namespace *ns = st->namespace;
struct strlist_node *str;
/* --type only */
if (!select_opts_dev_specified(select))
return false;
/* Target configuration */
if ((device_get_config(dev) & opts->config) == 0)
return false;
/* Devtype */
if (select->devtype && dt != select->devtype)
return false;
/* Subtype */
if (select->subtype && st != select->subtype)
return false;
/* Device ID */
if (!util_list_is_empty(&select->devids)) {
str = NULL;
util_list_iterate(&select->devids, str) {
if (ns->cmp_ids(str->str, dev->id) == 0)
break;
}
if (str)
return true;
else
return false;
}
/* State */
if (select_match_state(dev, select))
return true;
return false;
}
static bool import_devtype_selected(struct devtype *dt, struct options *opts)
{
if (!opts->type)
return false;
if (opts->config == config_active) {
if (!dt->active_settings ||
util_list_is_empty(&dt->active_settings->list))
return false;
}
if (opts->config == config_persistent) {
if (!dt->persistent_settings ||
util_list_is_empty(&dt->persistent_settings->list))
return false;
}
return true;
}
/* Remove export objects from @objects which are not matched by selection
* options. */
static void apply_selection_to_import(struct util_list *objects,
struct options *opts)
{
struct ptrlist_node *p, *n;
struct export_object *obj;
util_list_iterate_safe(objects, p, n) {
obj = p->ptr;
if (obj->type == export_device) {
if (import_device_selected(obj->ptr.dev, opts))
continue;
} else if (obj->type == export_devtype) {
if (import_devtype_selected(obj->ptr.dt, opts))
continue;
}
util_list_remove(objects, p);
free(p->ptr);
free(p);
}
}
static exit_code_t zdev_file_detect(FILE *fd)
{
char buffer[MAX_HEADER_LENGTH];
if (fgets(buffer, MAX_HEADER_LENGTH, fd)) {
if (starts_with(buffer, CHZDEV_HEADER))
return EXIT_OK;
} else {
return EXIT_RUNTIME_ERROR;
}
return EXIT_UNKNOWN_FILE;
}
/* Check if the file mentioned is generated by zdev */
static exit_code_t do_is_owner(struct options *opts)
{
FILE *fd;
exit_code_t rc = EXIT_OK;
fd = fopen(opts->is_owner, "r");
if (!fd) {
error("Could not open file %s: %s\n", opts->is_owner,
strerror(errno));
return EXIT_RUNTIME_ERROR;
}
rc = zdev_file_detect(fd);
fclose(fd);
return rc;
}
/* Import configuration data. */
static exit_code_t do_import(struct options *opts)
{
FILE *fd;
exit_code_t rc;
struct util_list *objects;
struct ptrlist_node *p;
struct export_object *obj;
exit_code_t drc = EXIT_OK;
const char *filename;
int found;
bool is_firmware;
/* Open input stream. */
if (strcmp(opts->import, "-") == 0) {
fd = stdin;
filename = "Standard input";
} else {
fd = fopen(opts->import, "r");
filename = opts->import;
}
if (!fd) {
error("Could not open file %s: %s\n", opts->import,
strerror(errno));
return EXIT_RUNTIME_ERROR;
}
is_firmware = firmware_detect(fd);
info("Importing data from %s%s\n", filename,
is_firmware ? " (firmware format)" : "");
/* Read data. */
objects = ptrlist_new();
if (is_firmware)
rc = firmware_read(fd, filename, -1, opts->config, objects);
else
rc = export_read(fd, filename, objects);
if (rc)
goto out;
found = !util_list_is_empty(objects);
apply_selection_to_import(objects, opts);
if (util_list_is_empty(objects)) {
if (found) {
error("%s: Imported configuration data did not match "
"selection\n", filename);
rc = EXIT_EMPTY_SELECTION;
} else {
info("%s: No settings found to import\n", filename);
rc = EXIT_OK;
}
goto out;
}
util_list_iterate(objects, p) {
obj = p->ptr;
if (obj->type == export_devtype)
rc = import_devtype(obj->ptr.dt, opts->config);
else
rc = import_device(obj->ptr.dev, opts);
if (rc && !drc)
drc = rc;
}
out:
ptrlist_free(objects, 1);
/* Close stream. */
if (fd != stdin)
fclose(fd);
return drc ? drc : rc;
}
static bool opts_stdout_data(struct options *opts)
{
if (opts->export && strcmp(opts->export, "-") == 0)
return true;
return false;
}
static exit_code_t do_configure(struct options *opts)
{
if (opts->type) {
action_note("Configuring device type in", opts->config);
return configure_devtype(opts);
}
action_note("Configuring devices in", opts->config);
return configure_devices(opts, 1, NULL);
}
static exit_code_t do_deconfigure(struct options *opts)
{
action_note("Deconfiguring devices in", opts->config);
return deconfigure_devices(opts);
}
static exit_code_t apply_devtypes(struct options *opts, int *found_ptr,
int *specified_ptr)
{
int i;
struct devtype *dt;
exit_code_t rc, drc = EXIT_OK;
if (opts->select->devtype) {
(*specified_ptr)++;
return apply_devtype(opts->select->devtype, 1, found_ptr);
}
for (i = 0; (dt = devtypes[i]); i++) {
rc = apply_devtype(dt, 0, found_ptr);
if (rc && drc == EXIT_OK)
drc = rc;
}
return drc;
}
static exit_code_t do_apply(struct options *opts)
{
exit_code_t rc, drc = EXIT_OK;
int found, specified;
found = 0;
specified = 0;
/* Apply devtype configuration. */
if (opts->type) {
rc = apply_devtypes(opts, &found, &specified);
if (rc && drc == EXIT_OK)
drc = rc;
}
/* Apply device configuration. */
if (select_opts_dev_specified(opts->select)) {
specified++;
rc = configure_devices(opts, 1, &found);
if (rc && drc == EXIT_OK)
drc = rc;
}
if (!found && !specified) {
error("No configuration data found\n");
if (drc == EXIT_OK)
drc = EXIT_NO_DATA;
}
return drc;
}
int main(int argc, char *argv[])
{
exit_code_t rc, drc = EXIT_OK;
struct options opts;
debug_init(argc, argv);
/* Handle command line. */
toolname = argv[0];
init_options(&opts);
devtypes_init();
rc = parse_options(&opts, argc, argv);
if (rc) {
if (!drc)
drc = rc;
goto out;
}
/* Set globals. */
if (opts_stdout_data(&opts))
set_stdout_data();
verbose = opts.verbose;
quiet = opts.quiet;
force = opts.force;
yes = opts.yes;
dryrun = opts.dryrun;
udev_no_settle = opts.no_settle;
path_set_base(opts.base);
module_load_suppress(opts.no_module_load);
if (dryrun)
info("Starting dry-run, configuration will not be changed\n");
/* Perform main action. */
switch (get_action(&opts)) {
case ACT_CONFIGURE:
rc = do_configure(&opts);
break;
case ACT_DECONFIGURE:
rc = do_deconfigure(&opts);
break;
case ACT_LIST_ATTRIBS:
rc = do_list_attribs(&opts);
break;
case ACT_HELP_ATTRIBS:
rc = do_help_attribs(&opts);
break;
case ACT_LIST_TYPES:
rc = do_list_types(&opts);
break;
case ACT_EXPORT:
rc = do_export(&opts);
break;
case ACT_IMPORT:
rc = do_import(&opts);
break;
case ACT_IS_OWNER:
rc = do_is_owner(&opts);
break;
case ACT_APPLY:
rc = do_apply(&opts);
break;
case ACT_HELP:
print_usage();
break;
case ACT_VERSION:
print_version();
break;
}
if (rc) {
if (!drc)
drc = rc;
goto out;
}
if (udev_need_settle)
udev_settle();
if ((pers_mod_devs || pers_mod_devtypes) && !opts.no_root_check &&
!dryrun) {
/* If the root device/device type or early devices have been
* modified, additional work might be necessary. */
rc = initrd_check(ZDEV_ALWAYS_UPDATE_INITRD, opts.deconfigure_all);
if (rc && !drc)
drc = rc;
}
out:
/* Write out any remaining messages. */
delayed_print(0);
/* Clean-up. */
free_options(&opts);
blkinfo_exit();
ccw_exit();
ctc_exit();
devtypes_exit();
inuse_exit();
misc_exit();
module_exit();
rc = namespace_exit();
if (rc && !drc)
drc = rc;
path_exit();
scsi_exit();
if (found_forceable && !force) {
info("Note: You can use --force to override safety checks "
"(*)\n");
}
return drc ? drc : rc;
}