diff --git a/zdev/include/path.h b/zdev/include/path.h index 3d91e15e..fc290902 100644 --- a/zdev/include/path.h +++ b/zdev/include/path.h @@ -23,6 +23,7 @@ #define PATH_MODPROBE "/sbin/modprobe" #define PATH_CCW_BUS "/sys/bus/ccw" #define PATH_CCWGROUP_BUS "/sys/bus/ccwgroup" +#define PATH_IPL "/sys/firmware/ipl" #define PATH_UDEV_RULES "/etc/udev/rules.d" #define PATH_UDEV_RULES_VOLATILE "/run/udev/rules.d" #define PATH_PROC "/proc" diff --git a/zdev/include/select.h b/zdev/include/select.h index bd996588..8e2b5330 100644 --- a/zdev/include/select.h +++ b/zdev/include/select.h @@ -29,6 +29,7 @@ struct select_opts { unsigned int online:1; unsigned int offline:1; unsigned int failed:1; + unsigned int ipldev:1; struct util_list devids; /* List of struct strlist_node */ struct util_list by_path; /* List of struct strlist_node */ struct util_list by_node; /* List of struct strlist_node */ diff --git a/zdev/include/subtype.h b/zdev/include/subtype.h index 2142998d..86cbefde 100644 --- a/zdev/include/subtype.h +++ b/zdev/include/subtype.h @@ -127,6 +127,7 @@ typedef exit_code_t (*subtype_cb_t)(struct subtype *st, const char *id, * @device_undefine: Optional: Undefine a device * @add_definable_ids: Optional: Add IDs of all devices that can be defined to * specified strlist + * @get_ipldev_id: Optional: If subtype provides IPL device, return its ID */ struct subtype { /* Static data. */ @@ -222,6 +223,7 @@ struct subtype { exit_code_t (*device_undefine)(struct subtype *, struct device *); void (*add_definable_ids)(struct subtype *, struct util_list *); + char *(*get_ipldev_id)(struct subtype *); }; /* Subtype method accessor functions. */ @@ -287,6 +289,7 @@ exit_code_t subtype_detect_definable(struct subtype *, struct device *); exit_code_t subtype_device_define(struct subtype *, struct device *); exit_code_t subtype_device_undefine(struct subtype *, struct device *); void subtype_add_definable_ids(struct subtype *, struct util_list *); +char *subtype_get_ipldev_id(struct subtype *); /* Subtype helper functions. */ bool subtype_device_exists(struct subtype *st, const char *id, @@ -308,6 +311,7 @@ struct subtype *subtype_find(const char *name); struct attrib *subtype_find_dev_attrib(struct subtype *st, const char *str); exit_code_t subtype_read_all_devices(struct subtype *st, config_t config, read_scope_t); +bool subtypes_find_ipldev(struct subtype **st_ptr, char **id_ptr); bool subtypes_find_by_devnode(struct devnode *devnode, struct subtype **st_ptr, char **id_ptr); void subtype_devices_print_all(void); diff --git a/zdev/man/chzdev.8 b/zdev/man/chzdev.8 index 556d7cfa..175bf32a 100644 --- a/zdev/man/chzdev.8 +++ b/zdev/man/chzdev.8 @@ -345,6 +345,12 @@ errors. .CL lszdev \-\-failed \-\-info .PP . +.OD ipldev "" "" +Select IPL device + +Select the device used for the most recent IPL operation. +.PP +. . .SH SETTINGS A diff --git a/zdev/man/lszdev.8 b/zdev/man/lszdev.8 index 59fcd4f8..7604632f 100644 --- a/zdev/man/lszdev.8 +++ b/zdev/man/lszdev.8 @@ -313,7 +313,12 @@ errors. .B Example: .CL lszdev \-\-failed \-\-info .PP +. +.OD ipldev "" "" +Select IPL device +Select the device used for the most recent IPL operation. +.PP . . .SH ACTIONS diff --git a/zdev/src/ccw.c b/zdev/src/ccw.c index d0c08892..3441c4ad 100644 --- a/zdev/src/ccw.c +++ b/zdev/src/ccw.c @@ -1827,6 +1827,27 @@ static char *ccw_st_get_active_attrib_path(struct subtype *st, return path; } +/* Return a newly allocated character string containing the IPL device ID in + * case of CCW IPL. */ +static char *ccw_st_get_ipldev_id(struct subtype *st) +{ + char *type, *id = NULL; + + type = path_read_text_file(1, err_ignore, PATH_IPL "/ipl_type"); + if (strcmp(type, "ccw") != 0) + goto out; + + id = path_read_text_file(1, err_ignore, PATH_IPL "/device"); + if (!ccw_st_exists_active(st, id)) { + free(id); + id = NULL; + } +out: + free(type); + + return id; +} + /* The methods of this subtype assume that @data points to a * struct ccw_subtype_data. */ struct subtype ccw_subtype = { @@ -1864,4 +1885,6 @@ struct subtype ccw_subtype = { .add_devnodes = &ccw_st_add_devnodes, .resolve_devnode = &ccw_st_resolve_devnode, .get_active_attrib_path = &ccw_st_get_active_attrib_path, + + .get_ipldev_id = &ccw_st_get_ipldev_id, }; diff --git a/zdev/src/chzdev.c b/zdev/src/chzdev.c index 1b29471f..29928de0 100644 --- a/zdev/src/chzdev.c +++ b/zdev/src/chzdev.c @@ -124,6 +124,7 @@ enum { 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__), @@ -173,14 +174,14 @@ static struct opts_conflict conflict_list[] = { 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_DECONFIGURE_ALL, 0), + 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_DECONFIGURE_ALL, 0), + 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, @@ -197,7 +198,7 @@ static struct opts_conflict conflict_list[] = { 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_DECONFIGURE_ALL, 0), + 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, @@ -223,6 +224,7 @@ static const struct option opt_list[] = { { "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 }, @@ -837,6 +839,11 @@ static exit_code_t parse_options(struct options *opts, int argc, char *argv[]) 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); diff --git a/zdev/src/chzdev_usage.txt b/zdev/src/chzdev_usage.txt index ab9cd854..d146057e 100644 --- a/zdev/src/chzdev_usage.txt +++ b/zdev/src/chzdev_usage.txt @@ -21,6 +21,7 @@ SELECTION --existing Select devices found in the active configuration --online/--offline Select devices that are online/offline --failed Select devices that are not functioning correctly + --ipldev Select device used for IPL/boot --by-path PATH Select device providing file system path, e.g. /usr --by-node NODE Select device providing device node, e.g. /dev/sda --by-interface NAME Select device providing network interface, e.g. eth0 diff --git a/zdev/src/dasd.c b/zdev/src/dasd.c index 43302291..4155d33f 100644 --- a/zdev/src/dasd.c +++ b/zdev/src/dasd.c @@ -477,6 +477,26 @@ static void dasd_st_add_modules(struct subtype *st, struct device *dev, strlist_add_unique(modules, DASD_DIAG_MOD_NAME); } +static char *dasd_eckd_st_get_ipldev_id(struct subtype *st) +{ + char *id, *type; + + /* Handle CCW-type IPL first. */ + id = st->super->get_ipldev_id(st); + if (id) + return id; + + /* Extra-handling for eckd LD-IPL. */ + type = path_read_text_file(1, err_ignore, PATH_IPL "/ipl_type"); + if (strcmp(type, "eckd") != 0) + goto out; + id = path_read_text_file(1, err_ignore, PATH_IPL "/ipl/device"); + +out: + free(type); + return id; +} + /* * DASD methods. */ @@ -798,6 +818,7 @@ struct subtype dasd_subtype_eckd = { .configure_persistent = &dasd_st_configure_persistent, .check_pre_configure = &dasd_st_check_pre_configure, .add_modules = &dasd_st_add_modules, + .get_ipldev_id = &dasd_eckd_st_get_ipldev_id, }; static struct ccw_subtype_data dasd_fba_data = { diff --git a/zdev/src/lszdev.c b/zdev/src/lszdev.c index 8d3754b3..02a98e95 100644 --- a/zdev/src/lszdev.c +++ b/zdev/src/lszdev.c @@ -89,6 +89,7 @@ enum { 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__), @@ -129,6 +130,7 @@ static struct opts_conflict conflict_list[] = { OPTS_CONFLICT(OPT_TYPE, OPT_CONFIGURED, OPT_EXISTING, OPT_ONLINE, OPT_OFFLINE, OPT_BY_PATH, OPT_BY_NODE, OPT_BY_INTERFACE, OPT_FAILED, + OPT_IPLDEV, 0), OPTS_CONFLICT(OPT_SITE, OPT_TYPE), @@ -148,6 +150,7 @@ static const struct option opt_list[] = { { "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 }, @@ -609,6 +612,11 @@ static exit_code_t parse_options(struct options *opts, int argc, char *argv[]) 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); diff --git a/zdev/src/lszdev_usage.txt b/zdev/src/lszdev_usage.txt index ef12b465..3b5567ed 100644 --- a/zdev/src/lszdev_usage.txt +++ b/zdev/src/lszdev_usage.txt @@ -21,6 +21,7 @@ SELECTION --existing Select devices found in the active configuration --online/--offline Select devices that are online/offline --failed Select devices that are not functioning correctly + --ipldev Select device used for IPL/boot --by-path PATH Select device providing file system path, e.g. /usr --by-node NODE Select device providing device node, e.g. /dev/sda --by-interface NAME Select device providing network interface, e.g. eth0 diff --git a/zdev/src/select.c b/zdev/src/select.c index cdb2914f..4359295f 100644 --- a/zdev/src/select.c +++ b/zdev/src/select.c @@ -69,7 +69,8 @@ bool select_opts_dev_specified(struct select_opts *select) !util_list_is_empty(&select->by_if) || !util_list_is_empty(&select->by_attr) || select->all || select->configured || select->existing || - select->online || select->offline || select->failed) + select->online || select->offline || select->failed || + select->ipldev) return true; return false; @@ -1207,6 +1208,19 @@ out: return rc; } +/* Select device that was used for IPL. */ +static void select_ipldev(struct util_list *selected) +{ + struct subtype *st; + char *id; + + if (!subtypes_find_ipldev(&st, &id)) + return; + + selected_dev_list_add(selected, st->devtype, st, id, NULL, 0); + free(id); +} + /* Determine list of IDs of selected devices based on selection options. * Results are stored as list of struct selected_dev_node in SELECTED. * If @existing is set, only return nodes for existing devices. @@ -1235,7 +1249,7 @@ exit_code_t select_devices(struct select_opts *select, !(util_list_is_empty(&select->by_path) && util_list_is_empty(&select->by_node) && util_list_is_empty(&select->by_if) && - util_list_is_empty(&select->by_attr))) + util_list_is_empty(&select->by_attr) && !select->ipldev)) goto by_function; /* Convert device ID specifications to enable tracking of specifications @@ -1291,6 +1305,9 @@ by_function: goto out; } + if (select->ipldev) + select_ipldev(selected); + out: /* Release device ID specifications tracking list. */ ptrlist_free(specs, 1); diff --git a/zdev/src/subtype.c b/zdev/src/subtype.c index 4acdf2ff..ad146570 100644 --- a/zdev/src/subtype.c +++ b/zdev/src/subtype.c @@ -96,6 +96,30 @@ static exit_code_t devnode_cb(struct subtype *st, const char *id, return rc; } +/* Find device which was used for IPL. Note that id_ptr will point to a newly + * allocated string on success. */ +bool subtypes_find_ipldev(struct subtype **st_ptr, char **id_ptr) +{ + int i, j; + struct devtype *dt; + struct subtype *st; + char *id; + + for (i = 0; (dt = devtypes[i]); i++) { + for (j = 0; (st = dt->subtypes[j]); j++) { + id = subtype_get_ipldev_id(st); + if (id) { + *st_ptr = st; + *id_ptr = id; + + return true; + } + } + } + + return false; +} + /* Find a device which provides the specified devnode. Note that id_ptr will * point to a newly allocated string on success. */ bool subtypes_find_by_devnode(struct devnode *devnode, struct subtype **st_ptr, @@ -520,6 +544,16 @@ void subtype_add_definable_ids(struct subtype *st, struct util_list *ids) super->add_definable_ids(st, ids); } +char *subtype_get_ipldev_id(struct subtype *st) +{ + struct subtype *super = super_get(st, get_ipldev_id, 0); + + if (super) + return super->get_ipldev_id(st); + + return NULL; +} + /* * Subtype helpers. These functions combine some of the subtype methods * to implement more complex functions. diff --git a/zdev/src/zfcp_lun.c b/zdev/src/zfcp_lun.c index db0fa111..266ba6a1 100644 --- a/zdev/src/zfcp_lun.c +++ b/zdev/src/zfcp_lun.c @@ -1105,6 +1105,28 @@ static exit_code_t zfcp_lun_st_detect_definable(struct subtype *st, return EXIT_OK; } +static char *zfcp_lun_st_get_ipldev_id(struct subtype *st) +{ + char *type, *fcp_dev, *wwpn, *lun, *id = NULL; + + type = path_read_text_file(1, err_ignore, PATH_IPL "/ipl_type"); + if (strcmp(type, "fcp") != 0) + goto out; + + fcp_dev = path_read_text_file(1, err_ignore, PATH_IPL "/device"); + wwpn = path_read_text_file(1, err_ignore, PATH_IPL "/wwpn"); + lun = path_read_text_file(1, err_ignore, PATH_IPL "/lun"); + if (fcp_dev && wwpn && lun) + id = misc_asprintf("%s:%s:%s", fcp_dev, wwpn, lun); + free(lun); + free(wwpn); + free(fcp_dev); + +out: + free(type); + return id; +} + /* * zfcp lun subtype. */ @@ -1168,4 +1190,6 @@ struct subtype zfcp_lun_subtype = { .detect_definable = &zfcp_lun_st_detect_definable, .device_define = &zfcp_lun_st_device_define, .device_undefine = &zfcp_lun_st_device_undefine, + + .get_ipldev_id = &zfcp_lun_st_get_ipldev_id, };