Files
s390-tools/zdev/src/udev_zfcp_lun.c
Jan Polensky 14830b1ead zdev: Remove unused header files
Remove unused header files from these sources.
This simplifies maintenance and slightly reduces compile time.

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>
2025-12-01 13:45:37 +01:00

819 lines
19 KiB
C

/*
* zdev - Modify and display the persistent configuration of devices
*
* Copyright IBM Corp. 2016, 2017
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <errno.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lib/util_path.h"
#include "lib/util_udev.h"
#include "attrib.h"
#include "device.h"
#include "internal.h"
#include "misc.h"
#include "path.h"
#include "scsi.h"
#include "setting.h"
#include "udev.h"
#include "udev_zfcp_lun.h"
#include "zfcp_lun.h"
#define LABEL_START "start_zfcp_lun_"
#define LABEL_END "end_zfcp_lun_"
#define LABEL_FC "cfg_fc_"
#define LABEL_SCSI "cfg_scsi_"
struct zfcp_lun_node {
struct util_list_node node;
struct zfcp_lun_devid id;
struct setting_list *fc_settings;
struct setting_list *scsi_settings;
};
static void zfcp_lun_node_print(struct zfcp_lun_node *node, int indent)
{
char *id;
printf("%*szfcp_lun_node at %p\n", indent, "", (void *) node);
if (!node)
return;
indent += 2;
id = zfcp_lun_devid_to_str(&node->id);
printf("%*sid=%s\n", indent, "", id);
free(id);
printf("%*sfc_settings:\n", indent, "");
setting_list_print(node->fc_settings, indent + 2);
printf("%*sscsi_settings:\n", indent, "");
setting_list_print(node->scsi_settings, indent + 2);
}
static struct zfcp_lun_node *zfcp_lun_node_new(struct zfcp_lun_devid *id)
{
struct zfcp_lun_node *node;
node = misc_malloc(sizeof(struct zfcp_lun_node));
node->id = *id;
node->fc_settings = setting_list_new();
node->scsi_settings = setting_list_new();
return node;
}
static void zfcp_lun_node_free(struct zfcp_lun_node *node)
{
if (!node)
return;
setting_list_free(node->fc_settings);
setting_list_free(node->scsi_settings);
free(node);
}
static struct zfcp_lun_node *zfcp_lun_node_find(struct util_list *list,
struct zfcp_lun_devid *id)
{
struct zfcp_lun_node *node;
util_list_iterate(list, node) {
if (zfcp_lun_cmp_devids(&node->id, id) == 0)
return node;
}
return NULL;
}
static struct util_list *zfcp_lun_node_list_new(void)
{
struct util_list *list;
list = misc_malloc(sizeof(struct util_list));
util_list_init(list, struct zfcp_lun_node, node);
return list;
}
static void zfcp_lun_node_list_free(struct util_list *list)
{
struct zfcp_lun_node *n, *p;
if (!list)
return;
util_list_iterate_safe(list, n, p) {
util_list_remove(list, n);
zfcp_lun_node_free(n);
}
free(list);
}
/* Used for debugging. */
void zfcp_lun_node_list_print(struct util_list *list, int indent)
{
struct zfcp_lun_node *n;
printf("%*szfcp_lun_node_list at %p\n", indent, "", (void *) list);
if (!list)
return;
util_list_iterate(list, n)
zfcp_lun_node_print(n, indent + 2);
}
static bool zfcp_lun_devid_from_entry(struct zfcp_lun_devid *id_ptr,
struct util_udev_entry_node *entry)
{
struct zfcp_lun_devid id;
char *copy = NULL, *s, *e, *u;
int i;
bool rc = false;
/* LABEL="cfg_scsi_0.0.1941_0x500507630510c1ae_0x402340d400000000" */
if (strcmp(entry->key, "LABEL") == 0 &&
starts_with(entry->value, LABEL_SCSI)) {
copy = misc_strdup(entry->value);
s = copy + strlen(LABEL_SCSI);
for (i = 0; s[i]; i++) {
if (s[i] == '_')
s[i] = ':';
}
rc = zfcp_lun_parse_devid(&id, s, err_ignore) == EXIT_OK ?
true : false;
goto out;
}
/*ATTR{[ccw/0.0.1941]0x500507630510c1ae/unit_add}="0x402340d400000000"*/
if (starts_with(entry->key, "ATTR{[ccw/") &&
ends_with(entry->key, "/unit_add}")) {
copy = misc_asprintf("%s%s", entry->key, entry->value);
s = copy + strlen("ATTR{[ccw/");
for (i = 0; s[i]; i++) {
if (s[i] == ']')
s[i] = ':';
else if (s[i] == '/')
break;
}
if (s[i] != '/')
goto out;
s[i] = ':';
strcpy(s + i + 1, entry->value);
rc = zfcp_lun_parse_devid(&id, s, err_ignore) == EXIT_OK ?
true : false;
goto out;
}
/*ATTR{[ccw/0.0.1941]0x500507630510c1ae/0x402340d400000000/failed}="0"*/
if (starts_with(entry->key, "ATTR{[ccw/")) {
copy = misc_strdup(entry->key);
s = strrchr(copy, '/');
if (s)
*s = 0;
s = copy + strlen("ATTR{[ccw/");
for (i = 0; s[i]; i++) {
if (s[i] == ']' || s[i] == '/')
s[i] = ':';
}
rc = zfcp_lun_parse_devid(&id, s, err_ignore) == EXIT_OK ?
true : false;
goto out;
}
/*ENV{zdev_var__0_0_1941_0x500507630510c1ae_0x402340d400000000}="1"*/
if (starts_with(entry->key, "ENV{zdev_")) {
copy = misc_strdup(entry->key);
/* Find ID start (last __) and end (last }). */
s = misc_strrstr(copy, "__");
e = strrchr(copy, '}');
if (!s || !e)
goto out;
*e = 0;
s += 2;
/* Convert variable name to ID format. */
for (i = 0, u = s; (u = strchr(u, '_')); i++, u++) {
if (i < 2)
*u = '.';
else
*u = ':';
}
rc = zfcp_lun_parse_devid(&id, s, err_ignore) == EXIT_OK ?
true : false;
}
out:
free(copy);
if (rc)
*id_ptr = id;
return rc;
}
static struct zfcp_lun_node *zfcp_lun_node_from_entry(
struct util_udev_entry_node *entry,
struct zfcp_lun_node *old,
struct util_list *list)
{
struct zfcp_lun_devid id;
struct zfcp_lun_node *node;
if (!zfcp_lun_devid_from_entry(&id, entry))
return old;
if (old && zfcp_lun_cmp_devids(&old->id, &id) == 0)
return old;
node = zfcp_lun_node_find(list, &id);
if (!node) {
node = zfcp_lun_node_new(&id);
util_list_add_tail(list, node);
}
return node;
}
static void add_internal_setting_from_entry(struct util_udev_entry_node *entry,
struct zfcp_lun_node *node)
{
char *copy, *name, *end, *u;
struct attrib *a;
/*ENV{zdev_var__0_0_1941_0x500507630510c1ae_0x402340d400000000}="1"*/
copy = misc_strdup(entry->key);
/* Find attribute name start and end. */
name = strchr(copy, '{');
end = misc_strrstr(copy, "__");
if (!name || !end)
goto out;
*end = 0;
name++;
/* zdev_ => zdev: */
u = strchr(name, '_');
if (u)
*u = ':';
a = attrib_find(zfcp_lun_subtype.dev_attribs, name);
setting_list_apply_actual(node->fc_settings, a, name, entry->value);
out:
free(copy);
}
static void add_fc_setting_from_entry(struct util_udev_entry_node *entry,
struct zfcp_lun_node *node)
{
char *copy, *s, *e;
/*ENV{zdev_var__0_0_1941_0x500507630510c1ae_0x402340d400000000}="1"*/
if (starts_with(entry->key, "ENV{zdev_") &&
strcmp(entry->op, "=") == 0) {
add_internal_setting_from_entry(entry, node);
return;
}
/*ATTR{[ccw/0.0.1941]0x500507630510c1ae/0x402340d400000000/failed}="0"*/
if (!starts_with(entry->key, "ATTR{[ccw/"))
return;
if (strstr(entry->key, "unit_add"))
return;
copy = misc_strdup(entry->key);
s = strrchr(copy, '/');
if (!s)
goto out;
s++;
e = strchr(s, '}');
if (!e)
goto out;
*e = 0;
setting_list_add(node->fc_settings, setting_new(NULL, s, entry->value));
out:
free(copy);
}
static void add_scsi_setting_from_entry(struct util_udev_entry_node *entry,
struct zfcp_lun_node *node)
{
char *copy, *s, *e;
/* ATTR{queue_depth}="64" */
if (!starts_with(entry->key, "ATTR{"))
return;
copy = misc_strdup(entry->key);
s = strchr(copy, '{');
if (!s)
goto out;
s++;
e = strchr(s, '}');
if (!e)
goto out;
*e = 0;
setting_list_add(node->scsi_settings,
setting_new(NULL, s, entry->value));
out:
free(copy);
}
static int zfcp_lun_node_cmp(void *a, void *b, void *data)
{
struct zfcp_lun_node *a_node = a, *b_node = b;
return zfcp_lun_cmp_devids(&a_node->id, &b_node->id);
}
static void sort_zfcp_lun_list(struct util_list *list)
{
util_list_sort(list, zfcp_lun_node_cmp, NULL);
}
/* Read udev rule from FILENAME and extract all LUN settings as zfcp_lun_node
* to list. Note: List entries will be sorted by ID. */
static exit_code_t udev_read_zfcp_lun_rule(const char *filename,
struct util_list *list)
{
exit_code_t rc;
struct util_udev_file *file = NULL;
struct util_udev_line_node *line;
struct util_udev_entry_node *entry;
struct zfcp_lun_node *node = NULL;
enum {
none,
in_fc,
in_scsi,
} state = none;
bool empty_rule = true;
rc = (exit_code_t) util_udev_read_file(filename, &file);
if (rc)
goto out;
util_list_iterate(&file->lines, line) {
entry = util_list_start(&line->entries);
/* Skip comments and empty lines. */
if (!entry)
continue;
/* GOTO resets current state. */
if (strcmp(entry->key, "GOTO") == 0) {
node = NULL;
state = none;
continue;
}
switch (state) {
case none:
if (strcmp(entry->key, "LABEL") != 0)
continue;
if (starts_with(entry->value, LABEL_FC))
state = in_fc;
else if (starts_with(entry->value, LABEL_SCSI)) {
state = in_scsi;
node = zfcp_lun_node_from_entry(entry, node,
list);
if (node)
empty_rule = false;
}
break;
case in_fc:
node = zfcp_lun_node_from_entry(entry, node, list);
if (node) {
add_fc_setting_from_entry(entry, node);
empty_rule = false;
}
break;
case in_scsi:
if (node) {
add_scsi_setting_from_entry(entry, node);
empty_rule = false;
}
break;
}
}
sort_zfcp_lun_list(list);
out:
util_udev_free_file(file);
if (empty_rule)
warn_once("Warning: Invalid udev rule: %s\n", filename);
return rc;
}
struct lun_cb_data {
char *prefix;
struct util_list *list;
};
static exit_code_t lun_cb(const char *path, const char *name, void *data)
{
struct lun_cb_data *cb_data = data;
struct util_list *luns;
struct zfcp_lun_node *node;
char *id;
if (!starts_with(name, cb_data->prefix))
return EXIT_OK;
luns = zfcp_lun_node_list_new();
udev_read_zfcp_lun_rule(path, luns);
util_list_iterate(luns, node) {
id = zfcp_lun_devid_to_str(&node->id);
strlist_add(cb_data->list, id);
free(id);
}
zfcp_lun_node_list_free(luns);
return EXIT_OK;
}
/* Add the IDs for all zfcp lun devices for which a configuration exists to
* LIST. */
void udev_zfcp_lun_add_device_ids(struct util_list *list, bool autoconf)
{
struct lun_cb_data cb_data;
char *path;
cb_data.prefix = misc_asprintf("%s-%s-", UDEV_PREFIX, ZFCP_LUN_NAME);
cb_data.list = list;
path = path_get_udev_rules(autoconf);
if (util_path_is_dir(path))
path_for_each(path, lun_cb, &cb_data);
free(path);
free(cb_data.prefix);
}
/* Return path to zfcp lun udev rule file containing configuration data for
* all LUNs of a zfcp device. */
static char *get_zfcp_lun_path(const char *id, bool autoconf)
{
char *copy, *e, *path;
copy = misc_strdup(id);
e = strchr(copy, ':');
if (e)
*e = 0;
path = path_get_udev_rule(ZFCP_LUN_NAME, copy, autoconf);
free(copy);
return path;
}
/* Return path to zfcp lun udev rule file containing configuration data for
* a single LUN. */
static char *get_single_zfcp_lun_path(const char *id, bool autoconf)
{
return path_get_udev_rule(ZFCP_LUN_NAME, id, autoconf);
}
/* Apply the settings found in NODE to STATE. */
static void zfcp_lun_node_to_state(struct zfcp_lun_node *node,
struct attrib **attribs,
struct device_state *state)
{
struct setting *s;
struct attrib *a;
char *name;
state->modified = 0;
state->deconfigured = 0;
state->definable = 0;
if (!node) {
state->exists = 0;
return;
}
state->exists = 1;
util_list_iterate(&node->fc_settings->list, s) {
a = attrib_find(attribs, s->name);
setting_list_add(state->settings,
setting_new(a, s->name, s->value));
}
util_list_iterate(&node->scsi_settings->list, s) {
name = misc_asprintf("%s/%s", SCSI_ATTR_PREFIX, s->name);
a = attrib_find(attribs, name);
setting_list_add(state->settings,
setting_new(a, name, s->value));
free(name);
}
}
/* Read the persistent configuration of a zfcp lun from a udev rule. */
exit_code_t udev_zfcp_lun_read_device(struct device *dev, bool autoconf)
{
struct subtype *st = dev->subtype;
struct device_state *state = autoconf ? &dev->autoconf :
&dev->persistent;
struct util_list *luns;
struct zfcp_lun_node *node;
exit_code_t rc = EXIT_OK;
char *path;
/* Check for single lun file first then try multi lun file. */
path = get_single_zfcp_lun_path(dev->id, autoconf);
if (!util_path_exists(path)) {
free(path);
path = get_zfcp_lun_path(dev->id, autoconf);
}
/* Get previous rule data. */
luns = zfcp_lun_node_list_new();
rc = udev_read_zfcp_lun_rule(path, luns);
if (rc)
goto out;
node = zfcp_lun_node_find(luns, dev->devid);
zfcp_lun_node_to_state(node, st->dev_attribs, state);
out:
zfcp_lun_node_list_free(luns);
free(path);
return rc;
}
static struct zfcp_lun_node *state_to_zfcp_lun_node(struct zfcp_lun_devid *id,
struct device_state *state)
{
struct zfcp_lun_node *node;
struct setting *s, *n;
node = zfcp_lun_node_new(id);
util_list_iterate(&state->settings->list, s) {
if (s->removed)
continue;
if (attrib_match_prefix(s->name, SCSI_ATTR_PREFIX)) {
n = setting_new(NULL,
attrib_rem_prefix(s->name,
SCSI_ATTR_PREFIX),
s->value);
setting_list_add(node->scsi_settings, n);
} else {
n = setting_new(s->attrib, s->name, s->value);
setting_list_add(node->fc_settings, n);
}
}
return node;
}
/* Write udev rule as defined by LIST of struct zfcp_lun_nodes to PATH. */
static exit_code_t write_luns_rule(const char *path, struct util_list *list)
{
FILE *fd;
exit_code_t rc = EXIT_OK;
struct zfcp_lun_node *node, *last_node;
char *hba_id;
struct setting *s;
sort_zfcp_lun_list(list);
node = util_list_start(list);
if (!node)
return EXIT_INTERNAL_ERROR;
hba_id = ccw_devid_to_str(&node->id.fcp_dev);
debug("Writing FCP LUN udev rule file %s\n", path);
if (!util_path_exists(path)) {
rc = path_create(path);
if (rc)
goto out;
}
fd = misc_fopen(path, "w");
if (!fd) {
error("Could not write to file %s: %s\n", path,
strerror(errno));
rc = EXIT_RUNTIME_ERROR;
goto out;
}
fprintf(fd, "# Generated by chzdev\n");
fprintf(fd, "ACTION==\"add\", SUBSYSTEMS==\"ccw\", KERNELS==\"%s\", "
"GOTO=\"%s%s\"\n", hba_id, LABEL_START, hba_id);
fprintf(fd, "GOTO=\"%s%s\"\n", LABEL_END, hba_id);
fprintf(fd, "\nLABEL=\"%s%s\"\n", LABEL_START, hba_id);
/* Emit FC port triggers. */
last_node = NULL;
util_list_iterate(list, node) {
if (last_node && last_node->id.wwpn == node->id.wwpn) {
/* Only one trigger per WWPN required. */
continue;
}
fprintf(fd, "SUBSYSTEM==\"fc_remote_ports\", "
"ATTR{port_name}==\"0x%016" PRIx64 "\", "
"GOTO=\"%s%s_0x%016" PRIx64 "\"\n",
node->id.wwpn, LABEL_FC, hba_id, node->id.wwpn);
last_node = node;
}
/* Emit SCSI unit triggers. */
util_list_iterate(list, node) {
if (util_list_is_empty(&node->scsi_settings->list))
continue;
fprintf(fd, "SUBSYSTEM==\"scsi\", "
"ENV{DEVTYPE}==\"scsi_device\", "
"KERNEL==\"*:%" PRIu64 "\", "
"KERNELS==\"rport-*\", "
"ATTRS{fc_remote_ports/$id/port_name}==\"0x%016" PRIx64
"\", GOTO=\"%s%s_0x%016" PRIx64 "_0x%016" PRIx64 "\"\n",
scsi_lun_from_fcp_lun(node->id.lun), node->id.wwpn,
LABEL_SCSI, hba_id, node->id.wwpn, node->id.lun);
}
fprintf(fd, "GOTO=\"%s%s\"\n", LABEL_END, hba_id);
/* Emit FC port sections. */
last_node = NULL;
util_list_iterate(list, node) {
if (!last_node || last_node->id.wwpn != node->id.wwpn) {
if (last_node) {
fprintf(fd, "GOTO=\"end_zfcp_lun_%s\"\n",
hba_id);
}
fprintf(fd, "\nLABEL=\"%s%s_0x%016" PRIx64 "\"\n",
LABEL_FC, hba_id, node->id.wwpn);
}
fprintf(fd, "ATTR{[ccw/%s]0x%016" PRIx64 "/unit_add}="
"\"0x%016" PRIx64 "\"\n", hba_id, node->id.wwpn,
node->id.lun);
util_list_iterate(&node->fc_settings->list, s) {
if ((s->attrib && s->attrib->internal) ||
internal_by_name(s->name)) {
fprintf(fd, "ENV{zdev_%s__%x_%x_%04x_0x%016"
PRIx64 "_0x%016" PRIx64 "}=\"%s\"\n",
internal_get_name(s->name),
node->id.fcp_dev.cssid,
node->id.fcp_dev.ssid,
node->id.fcp_dev.devno, node->id.wwpn,
node->id.lun, s->value);
} else {
fprintf(fd, "ATTR{[ccw/%s]0x%016" PRIx64
"/0x%016" PRIx64 "/%s}=\"%s\"\n",
hba_id, node->id.wwpn, node->id.lun,
s->name, s->value);
}
}
last_node = node;
}
if (last_node)
fprintf(fd, "GOTO=\"%s%s\"\n", LABEL_END, hba_id);
/* Emit SCSI unit sections. */
util_list_iterate(list, node) {
if (util_list_is_empty(&node->scsi_settings->list))
continue;
fprintf(fd, "\nLABEL=\"%s%s_0x%016" PRIx64 "_0x%016"
PRIx64 "\"\n", LABEL_SCSI, hba_id, node->id.wwpn,
node->id.lun);
util_list_iterate(&node->scsi_settings->list, s)
fprintf(fd, "ATTR{%s}=\"%s\"\n", s->name, s->value);
fprintf(fd, "GOTO=\"%s%s\"\n", LABEL_END, hba_id);
}
fprintf(fd, "\nLABEL=\"%s%s\"\n", LABEL_END, hba_id);
if (misc_fclose(fd))
warn("Could not close file %s: %s\n", path, strerror(errno));
out:
free(hba_id);
return rc;
}
/* Update the udev rule file that configures the zfcp lun with the specified
* ID. If @state is %NULL, remove the rule, otherwise create a rule that
* applies the corresponding parameters. If @single is set, update a single
* lun rule file, otherwise update a multi lun rule file. */
static exit_code_t update_lun_rule(const char *id, struct device_state *state,
bool single, bool autoconf)
{
struct zfcp_lun_devid devid;
struct util_list *luns;
struct zfcp_lun_node *node;
exit_code_t rc = EXIT_OK;
char *path;
bool exists;
rc = zfcp_lun_parse_devid(&devid, id, err_delayed_print);
if (rc)
return rc;
path = single ? get_single_zfcp_lun_path(id, autoconf) :
get_zfcp_lun_path(id, autoconf);
/* Get previous rule data. */
luns = zfcp_lun_node_list_new();
exists = util_path_is_reg_file(path);
if (exists)
udev_read_zfcp_lun_rule(path, luns);
/* Replace previous rule data for this ID. */
node = zfcp_lun_node_find(luns, &devid);
if (node) {
util_list_remove(luns, node);
zfcp_lun_node_free(node);
}
if (state && state->exists) {
node = state_to_zfcp_lun_node(&devid, state);
util_list_add_tail(luns, node);
}
if (util_list_is_empty(luns)) {
/* Remove empty file. */
if (exists)
rc = remove_file(path);
} else {
/* Write updated rules file. */
rc = write_luns_rule(path, luns);
}
zfcp_lun_node_list_free(luns);
free(path);
return rc;
}
/* Write a udev-rule to configure the specified zfcp lun and associated
* device state. */
exit_code_t udev_zfcp_lun_write_device(struct device *dev, bool autoconf)
{
exit_code_t rc;
struct device_state *state = autoconf ? &dev->autoconf :
&dev->persistent;
rc = update_lun_rule(dev->id, state, true, autoconf);
/* We only want single lun rule files so remove any remaining
* references in multi lun rule files. */
update_lun_rule(dev->id, NULL, false, autoconf);
return rc;
}
/* Remove the UDEV rule used to configure the zfcp lun with the specified ID. */
exit_code_t udev_zfcp_lun_remove_rule(const char *id, bool autoconf)
{
exit_code_t rc, rc2;
rc = update_lun_rule(id, NULL, true, autoconf);
rc2 = update_lun_rule(id, NULL, false, autoconf);
if (rc)
return rc;
return rc2;
}
/* Determine if a udev rule exists for configuring the specified zfcp lun. */
bool udev_zfcp_lun_exists(const char *id, bool autoconf)
{
struct zfcp_lun_devid devid;
char *path, *rule = NULL, *pattern = NULL;
bool rc = false;
if (zfcp_lun_parse_devid(&devid, id, err_ignore) != EXIT_OK)
return false;
/* Check for single lun rule file first. */
path = get_single_zfcp_lun_path(id, autoconf);
if (util_path_exists(path)) {
rc = true;
goto out;
}
free(path);
/* Check multi lun rule file next. */
path = get_zfcp_lun_path(id, autoconf);
rule = misc_read_text_file(path, 1, err_ignore);
if (!rule)
goto out;
pattern = misc_asprintf("ATTR{[ccw/%x.%x.%04x]0x%016" PRIx64
"/unit_add}=\"0x%016" PRIx64 "\"\n",
devid.fcp_dev.cssid, devid.fcp_dev.ssid,
devid.fcp_dev.devno, devid.wwpn, devid.lun);
if (strstr(rule, pattern))
rc = true;
out:
free(pattern);
free(rule);
free(path);
return rc;
}