mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Initial s390-tools-2.0.0 import
This commit is based on the s390-tools-1.39.0 version. Changes on top of s390-tools-1.39.0: - Add MIT license to all source files - Add LICENSE file - Transform REAMDE to README.md (markdown) - Add AUTHORS.md file - Add CONTRIBUTING.md file - Move changelog from README to CHANGELOG.md file Reviewed-by: Stefan Haberland <sth@linux.vnet.ibm.com> Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
This commit is contained in:
391
zdev/src/udev_ccwgroup.c
Normal file
391
zdev/src/udev_ccwgroup.c
Normal file
@@ -0,0 +1,391 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* Copyright IBM Corp. 2016, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "attrib.h"
|
||||
#include "ccwgroup.h"
|
||||
#include "device.h"
|
||||
#include "misc.h"
|
||||
#include "path.h"
|
||||
#include "setting.h"
|
||||
#include "udev.h"
|
||||
#include "udev_ccwgroup.h"
|
||||
|
||||
static char *get_rule_path_by_devid(const char *type,
|
||||
struct ccwgroup_devid *devid)
|
||||
{
|
||||
char *ccw_id, *path;
|
||||
|
||||
ccw_id = ccw_devid_to_str(&devid->devid[0]);
|
||||
path = path_get_udev_rule(type, ccw_id);
|
||||
free(ccw_id);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
static char *get_rule_path(const char *type, const char *id)
|
||||
{
|
||||
struct ccwgroup_devid devid;
|
||||
|
||||
if (ccwgroup_parse_devid(&devid, id, err_ignore) != EXIT_OK)
|
||||
return NULL;
|
||||
|
||||
return get_rule_path_by_devid(type, &devid);
|
||||
}
|
||||
|
||||
/* Check if a udev rule for the specified CCWGROUP device exists. */
|
||||
bool udev_ccwgroup_exists(const char *type, const char *id)
|
||||
{
|
||||
char *path;
|
||||
bool rc;
|
||||
|
||||
path = get_rule_path(type, id);
|
||||
if (!path)
|
||||
return false;
|
||||
rc = file_exists(path);
|
||||
free(path);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void add_setting_from_entry(struct setting_list *list,
|
||||
struct udev_entry_node *entry,
|
||||
struct attrib **attribs)
|
||||
{
|
||||
char *copy, *name, *end;
|
||||
struct attrib *a;
|
||||
|
||||
/* ATTR{[ccwgroup/0.0.f5f0]online}=1 */
|
||||
if (strncmp(entry->key, "ATTR{[ccwgroup/", 10) != 0 ||
|
||||
strcmp(entry->op, "=") != 0)
|
||||
return;
|
||||
copy = misc_strdup(entry->key);
|
||||
name = copy;
|
||||
|
||||
/* Find attribute name start. */
|
||||
name = strchr(entry->key, ']');
|
||||
end = strrchr(entry->key, '}');
|
||||
if (!name || !end)
|
||||
goto out;
|
||||
*end = 0;
|
||||
name++;
|
||||
|
||||
a = attrib_find(attribs, name);
|
||||
setting_list_apply_actual(list, a, name, entry->value);
|
||||
|
||||
out:
|
||||
free(copy);
|
||||
}
|
||||
|
||||
/* Extract CCWGROUP device settings from a CCWGROUP device udev rule file. */
|
||||
static void udev_file_get_settings(struct udev_file *file,
|
||||
struct attrib **attribs,
|
||||
struct setting_list *list)
|
||||
{
|
||||
struct udev_line_node *line;
|
||||
struct udev_entry_node *entry;
|
||||
|
||||
util_list_iterate(&file->lines, line) {
|
||||
entry = util_list_start(&line->entries);
|
||||
if (!entry)
|
||||
continue;
|
||||
add_setting_from_entry(list, entry, attribs);
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine full CCWGROUP from data in udev rule file. */
|
||||
static void expand_id(struct device *dev, struct udev_file *file)
|
||||
{
|
||||
struct ccwgroup_devid devid;
|
||||
struct ccwgroup_devid *devid_ptr = dev->devid;
|
||||
struct udev_line_node *line;
|
||||
struct udev_entry_node *entry;
|
||||
char *id;
|
||||
int i;
|
||||
|
||||
if (devid_ptr->num != 1)
|
||||
return;
|
||||
|
||||
util_list_iterate(&file->lines, line) {
|
||||
entry = util_list_start(&line->entries);
|
||||
if (!entry)
|
||||
continue;
|
||||
if (!starts_with(entry->key, "ATTR{[drivers/ccwgroup:") ||
|
||||
!ends_with(entry->key, "]group}"))
|
||||
continue;
|
||||
/* Extract CCWGROUP ID from comma-separated list of CCW
|
||||
* device IDs. */
|
||||
id = misc_strdup(entry->value);
|
||||
for (i = 0; id[i]; i++) {
|
||||
if (id[i] == ',')
|
||||
id[i] = ':';
|
||||
}
|
||||
if (ccwgroup_parse_devid(&devid, id, err_ignore) == EXIT_OK)
|
||||
*devid_ptr = devid;
|
||||
free(dev->id);
|
||||
dev->id = ccwgroup_devid_to_str(&devid);
|
||||
free(id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read the persistent configuration of a CCWGROUP device from a udev rule. */
|
||||
exit_code_t udev_ccwgroup_read_device(struct device *dev)
|
||||
{
|
||||
struct subtype *st = dev->subtype;
|
||||
struct device_state *state = &dev->persistent;
|
||||
struct udev_file *file = NULL;
|
||||
exit_code_t rc;
|
||||
char *path;
|
||||
|
||||
path = get_rule_path_by_devid(st->name, dev->devid);
|
||||
rc = udev_read_file(path, &file);
|
||||
if (rc)
|
||||
goto out;
|
||||
udev_file_get_settings(file, st->dev_attribs, state->settings);
|
||||
expand_id(dev, file);
|
||||
state->exists = 1;
|
||||
udev_free_file(file);
|
||||
|
||||
out:
|
||||
free(path);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Return an ID suitable for use as udev label. */
|
||||
static char *get_label_id(const char *prefix, const char *type,
|
||||
const char *dev_id)
|
||||
{
|
||||
char *id;
|
||||
int i;
|
||||
|
||||
id = misc_asprintf("%s_%s_%s", prefix, type, dev_id);
|
||||
for (i = 0; id[i]; i++) {
|
||||
if (isalnum(id[i]) || id[i] == '_' || id[i] == '.')
|
||||
continue;
|
||||
id[i] = '_';
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
/* Write the persistent configuration of a CCWGROUP device to a udev rule. */
|
||||
exit_code_t udev_ccwgroup_write_device(struct device *dev)
|
||||
{
|
||||
struct subtype *st = dev->subtype;
|
||||
struct ccwgroup_subtype_data *data = st->data;
|
||||
const char *type = st->name, *drv = data->ccwgroupdrv, *id = dev->id;
|
||||
struct ccwgroup_devid devid;
|
||||
char *path, *cfg_label = NULL, *group_label = NULL, *end_label = NULL,
|
||||
*ccw_id, *chan_id;
|
||||
struct util_list *list;
|
||||
struct ptrlist_node *p;
|
||||
struct setting *s;
|
||||
struct strlist_node *str;
|
||||
exit_code_t rc = EXIT_OK;
|
||||
FILE *fd;
|
||||
unsigned int i;
|
||||
|
||||
if (!dev->persistent.exists)
|
||||
return udev_ccwgroup_remove_rule(type, id);
|
||||
|
||||
if (ccwgroup_parse_devid(&devid, id, err_ignore) != EXIT_OK)
|
||||
return EXIT_INVALID_ID;
|
||||
|
||||
ccw_id = ccw_devid_to_str(&devid.devid[0]);
|
||||
path = get_rule_path(type, ccw_id);
|
||||
|
||||
group_label = get_label_id("group", type, ccw_id);
|
||||
cfg_label = get_label_id("cfg", type, ccw_id);
|
||||
end_label = get_label_id("end", type, ccw_id);
|
||||
|
||||
/* Apply attributes in correct order. */
|
||||
list = setting_list_get_sorted(dev->persistent.settings);
|
||||
|
||||
debug("Writing %s udev rule file %s\n", type, path);
|
||||
if (!path_exists(path)) {
|
||||
rc = path_create(path);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
fd = misc_fopen(path, "w");
|
||||
if (!fd) {
|
||||
error("Could not write to file %s: %s\n", path,
|
||||
strerror(errno));
|
||||
rc = EXIT_RUNTIME_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Write udev rule prolog. */
|
||||
fprintf(fd, "# Generated by chzdev\n");
|
||||
|
||||
/* Triggers. */
|
||||
fprintf(fd, "ACTION==\"add\", SUBSYSTEM==\"drivers\", "
|
||||
"KERNEL==\"%s\", GOTO=\"%s\"\n", drv, group_label);
|
||||
for (i = 0; i < devid.num; i++) {
|
||||
chan_id = ccw_devid_to_str(&devid.devid[i]);
|
||||
fprintf(fd, "ACTION==\"add\", SUBSYSTEM==\"ccw\", "
|
||||
"KERNEL==\"%s\", DRIVER==\"%s\", "
|
||||
"GOTO=\"%s\"\n", chan_id, drv, group_label);
|
||||
free(chan_id);
|
||||
}
|
||||
fprintf(fd, "ACTION==\"add\", SUBSYSTEM==\"ccwgroup\", "
|
||||
"KERNEL==\"%s\", DRIVER==\"%s\", GOTO=\"%s\"\n", ccw_id,
|
||||
drv, cfg_label);
|
||||
fprintf(fd, "GOTO=\"%s\"\n\n", end_label);
|
||||
|
||||
/* Group. */
|
||||
fprintf(fd, "LABEL=\"%s\"\n", group_label);
|
||||
fprintf(fd, "TEST==\"[ccwgroup/%s]\", GOTO=\"%s\"\n", ccw_id,
|
||||
end_label);
|
||||
for (i = 0; i < devid.num; i++) {
|
||||
chan_id = ccw_devid_to_str(&devid.devid[i]);
|
||||
fprintf(fd, "TEST!=\"[ccw/%s]\", GOTO=\"%s\"\n", chan_id,
|
||||
end_label);
|
||||
free(chan_id);
|
||||
}
|
||||
fprintf(fd, "ATTR{[drivers/ccwgroup:%s]group}=\"", drv);
|
||||
for (i = 0; i < devid.num; i++) {
|
||||
chan_id = ccw_devid_to_str(&devid.devid[i]);
|
||||
fprintf(fd, "%s%s", i > 0 ? "," : "", chan_id);
|
||||
free(chan_id);
|
||||
}
|
||||
fprintf(fd, "\"\n");
|
||||
fprintf(fd, "GOTO=\"%s\"\n\n", end_label);
|
||||
|
||||
/* Configure. */
|
||||
fprintf(fd, "LABEL=\"%s\"\n", cfg_label);
|
||||
util_list_iterate(list, p) {
|
||||
s = p->ptr;
|
||||
if (s->removed)
|
||||
continue;
|
||||
if (s->values) {
|
||||
util_list_iterate(s->values, str) {
|
||||
fprintf(fd, "ATTR{[ccwgroup/%s]%s}=\"%s\"\n",
|
||||
ccw_id, s->name, str->str);
|
||||
}
|
||||
} else {
|
||||
fprintf(fd, "ATTR{[ccwgroup/%s]%s}=\"%s\"\n", ccw_id,
|
||||
s->name, s->value);
|
||||
}
|
||||
}
|
||||
|
||||
/* Write udev rule epilog. */
|
||||
fprintf(fd, "\n");
|
||||
fprintf(fd, "LABEL=\"%s\"\n", end_label);
|
||||
|
||||
if (misc_fclose(fd))
|
||||
warn("Could not close file %s: %s\n", path, strerror(errno));
|
||||
|
||||
out:
|
||||
ptrlist_free(list, 0);
|
||||
free(end_label);
|
||||
free(cfg_label);
|
||||
free(group_label);
|
||||
free(path);
|
||||
free(ccw_id);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static char *read_full_id(const char *path)
|
||||
{
|
||||
char *text, *start, *end, *id = NULL;
|
||||
|
||||
text = misc_read_text_file(path, 0, err_ignore);
|
||||
if (!text)
|
||||
return NULL;
|
||||
start = strstr(text, "ATTR{[drivers/ccwgroup:");
|
||||
if (!start)
|
||||
goto out;
|
||||
start = strchr(start, '"');
|
||||
if (!start)
|
||||
goto out;
|
||||
start++;
|
||||
end = strchr(start, '"');
|
||||
if (!end)
|
||||
goto out;
|
||||
*end = 0;
|
||||
id = misc_strdup(start);
|
||||
|
||||
start = id;
|
||||
while ((start = strchr(start, ',')))
|
||||
*start = ':';
|
||||
|
||||
out:
|
||||
free(text);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
struct get_ids_cb_data {
|
||||
char *prefix;
|
||||
struct util_list *ids;
|
||||
};
|
||||
|
||||
static exit_code_t get_ids_cb(const char *path, const char *filename,
|
||||
void *data)
|
||||
{
|
||||
struct get_ids_cb_data *cb_data = data;
|
||||
char *id;
|
||||
|
||||
if (!starts_with(filename, cb_data->prefix))
|
||||
return EXIT_OK;
|
||||
id = read_full_id(path);
|
||||
if (!id)
|
||||
return EXIT_OK;
|
||||
strlist_add(cb_data->ids, id);
|
||||
free(id);
|
||||
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
/* Add the IDs for all devices of the specified subtype name for which a
|
||||
* udev rule exists to strlist LIST. */
|
||||
void udev_ccwgroup_add_device_ids(const char *type, struct util_list *list)
|
||||
{
|
||||
struct get_ids_cb_data cb_data;
|
||||
char *path;
|
||||
|
||||
path = path_get_udev_rules();
|
||||
cb_data.prefix = misc_asprintf("%s-%s-", UDEV_PREFIX, type);
|
||||
cb_data.ids = list;
|
||||
|
||||
if (dir_exists(path))
|
||||
path_for_each(path, get_ids_cb, &cb_data);
|
||||
|
||||
free(cb_data.prefix);
|
||||
free(path);
|
||||
}
|
||||
|
||||
/* Remove UDEV rule for CCWGROUP device. */
|
||||
exit_code_t udev_ccwgroup_remove_rule(const char *type, const char *id)
|
||||
{
|
||||
char *partial_id, *path;
|
||||
exit_code_t rc = EXIT_OK;
|
||||
|
||||
partial_id = ccwgroup_get_partial_id(id);
|
||||
if (!partial_id)
|
||||
return EXIT_INVALID_ID;
|
||||
|
||||
path = path_get_udev_rule(type, partial_id);
|
||||
if (file_exists(path))
|
||||
rc = remove_file(path);
|
||||
free(path);
|
||||
free(partial_id);
|
||||
|
||||
return rc;
|
||||
}
|
||||
Reference in New Issue
Block a user