Files
s390-tools/zdev/src/chzdev.c
Peter Oberparleiter 156450f359 zdev: Implement support for early device configuration
Enable user to specify that a device should be configured early, that is
during the initial RAM-disk boot phase. This may be necessary, e.g. to
override auto-configuration for a device which is also applied during
that boot phase. It may also be used to manually specify devices that
are required to access the root file system, such as networking devices.

Users can mark devices as requiring early configuration by specifying
a value of 1 for the newly added internal attribute zdev:early:

 # chzdev dasd-eckd 0.0.1234 -p zdev:early=1

This can be changed back by removing the attribute setting, or by
setting the attribute value to 0:

 # chzdev dasd-eckd 0.0.1234 -p -r zdev:early

or

 # chzdev dasd-eckd 0.0.1234 -p zdev:early=0

Signed-off-by: Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2018-06-08 15:03:30 +02:00

3062 lines
71 KiB
C

/*
* zdev - Modify and display the persistent configuration of devices
*
* chzdev: Configure z Systems specific 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 <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"
/* Main program action. */
typedef enum {
ACT_CONFIGURE,
ACT_DECONFIGURE,
ACT_LIST_ATTRIBS,
ACT_LIST_TYPES,
ACT_HELP_ATTRIBS,
ACT_EXPORT,
ACT_IMPORT,
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 list_attribs:1;
unsigned int list_types:1;
unsigned int help_attribs:1;
char *export;
char *import;
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;
};
/* Makefile converts chzdev_usage.txt into C file which we include here. */
static const char *usage_text =
#include "chzdev_usage.c"
;
#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_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_LIST_ATTRIBS = 'l',
OPT_HELP_ATTRIBS = 'H',
OPT_LIST_TYPES = 'L',
OPT_EXPORT = (OPT_ANONYMOUS_BASE+__COUNTER__),
OPT_IMPORT = (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__),
};
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, 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, 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, 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, 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, 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, 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, 0),
OPTS_CONFLICT(OPT_ONLINE,
OPT_OFFLINE),
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 },
{ "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 },
{ "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 },
{ "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 },
{ NULL, no_argument, NULL, 0 },
};
/* Command line abbreviations. */
static const char opt_str[] = ":edlHLapr:RfyhvVqt";
/* 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();
}
/* Release memory used in options data structure. */
static void free_options(struct options *opts)
{
if (!opts)
return;
free(opts->export);
free(opts->import);
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->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_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_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_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_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 ':':
/* 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->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;
}
/* 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;
}
/* 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;
}
/* 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;
/* 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 ||
dev->persistent.exists || 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) && dev->persistent.exists) ||
(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) && dev->persistent.exists) {
dev->persistent.deconfigured = 1;
dev->persistent.modified = 1;
}
if (SCOPE_AUTOCONF(config) && dev->autoconf.exists) {
dev->autoconf.deconfigured = 1;
dev->autoconf.modified = 1;
}
if (!dev->active.modified && !dev->persistent.modified &&
!dev->autoconf.modified)
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;
}
/* 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))
remove_settings(dt->active_settings, names, found, notfound, 1);
if (SCOPE_PERSISTENT(config)) {
remove_settings(dt->persistent_settings, names, found,
notfound, 0);
}
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;
}
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, 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)
info("%s the persistent configuration only\n", msg);
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(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);
}
}
/* 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);
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_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 = root_check();
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;
}