/* * 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 #include #include #include #include #include #include #include #include #include "devtype.h" #include "misc.h" #include "path.h" #include "zfcp.h" #include "zfcp_lun.h" #define PATH_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) struct base_prefix { char *from; char *to; }; /* prlist of struct base_prefix sorted descending by prefix length. */ static struct util_list *base_prefixes; /* Add an entry to the prefix list. */ static void prefix_add(const char *key, const char *value) { struct base_prefix *prefix, *curr; size_t len; struct ptrlist_node *p; prefix = misc_malloc(sizeof(struct base_prefix)); prefix->from = misc_strdup(key); prefix->to = misc_strdup(value); /* Add to list according to length. */ if (!base_prefixes) base_prefixes = ptrlist_new(); len = strlen(key); p = NULL; util_list_iterate(base_prefixes, p) { curr = p->ptr; if (strlen(curr->from) < len) break; } if (p) ptrlist_add_before(base_prefixes, p, prefix); else ptrlist_add(base_prefixes, prefix); } /* Initialize the prefix list from a strlist. */ void path_set_base(struct util_list *base) { struct strlist_node *s; char *copy, *value; if (!base) return; util_list_iterate(base, s) { copy = misc_strdup(s->str); value = strchr(copy, '='); if (value) { *value = 0; value++; prefix_add(copy, value); } else { prefix_add("", copy); } free(copy); } } /* Release the prefix list. */ void path_exit(void) { struct ptrlist_node *p, *n; struct base_prefix *prefix; if (!base_prefixes) return; util_list_iterate_safe(base_prefixes, p, n) { util_list_remove(base_prefixes, p); prefix = p->ptr; free(prefix->from); free(prefix->to); free(prefix); free(p); } free(base_prefixes); base_prefixes = NULL; } /* Modify @path according to specified prefix conversion. */ static void apply_base(char **path) { struct ptrlist_node *p; struct base_prefix *prefix; char *new_path; if (!base_prefixes) return; util_list_iterate(base_prefixes, p) { prefix = p->ptr; if (!starts_with(*path, prefix->from)) continue; new_path = misc_asprintf("%s%s", prefix->to, *path + strlen(prefix->from)); free(*path); *path = new_path; break; } } /* Return a path that is created by resolving the specified format string * @fmt and applying any base prefixes. */ char *path_get(const char *fmt, ...) { va_list args; char *path; /* Get original path. */ va_start(args, fmt); if (vasprintf(&path, fmt, args) == -1) oom(); va_end(args); /* Apply base prefix if necessary. */ apply_base(&path); return path; } /* Create all directories leading up to path. */ exit_code_t path_create(const char *path) { char *copy, *curr, *next; struct stat s; int rc; copy = misc_strdup(path); curr = (*copy == '/') ? copy + 1 : copy; curr = strchr(curr, '/'); if (!curr) { free(copy); return EXIT_OK; } do { next = strchr(curr + 1, '/'); *curr = 0; /* Ensure sub-path exists and is a directory. */ rc = stat(copy, &s); if (rc == -1 && errno == EACCES) goto err_access; if (rc == 0 && !S_ISDIR(s.st_mode)) goto err_file; if (rc == -1) { /* Create directory. */ rc = mkdir(copy, PATH_MODE); if (rc) goto err_mkdir; } *curr = '/'; curr = next; } while (curr); free(copy); return EXIT_OK; err_access: error("Could not access '%s'\n", copy); free(copy); return EXIT_RUNTIME_ERROR; err_file: error("Non-directory found in path '%s': %s\n", path, copy); free(copy); return EXIT_RUNTIME_ERROR; err_mkdir: error("Could not create directory '%s': %s\n", copy, strerror(errno)); free(copy); return EXIT_RUNTIME_ERROR; } /* Return path to modprobe.conf file for the specified device type. */ char *path_get_modprobe_conf(struct devtype *dt) { return path_get("%s/%s-%s.conf", PATH_MODPROBE_CONF, MODPROBE_PREFIX, dt->name); } /* Return sysfs path to module directory. */ char *path_get_sys_module(const char *mod) { return path_get("/sys/module/%s", mod); } /* Return sysfs path to module parameter file. */ char *path_get_sys_module_param(const char *mod, const char *name) { if (name) return path_get("/sys/module/%s/parameters/%s", mod, name); return path_get("/sys/module/%s/parameters", mod); } /* Return sysfs path to block device dev file. */ char *path_get_sys_block_dev(const char *name) { return path_get("/sys/block/%s/dev", name); } /* Return sysfs path to /sys/dev/block/major:minor directory. */ char *path_get_sys_dev_block(unsigned int major, unsigned int minor) { return path_get("/sys/dev/block/%d:%d", major, minor); } /* Return sysfs path to /sys/dev/char/major:minor directory. */ char *path_get_sys_dev_char(unsigned int major, unsigned int minor) { return path_get("/sys/dev/char/%d:%d", major, minor); } /* Return sysfs path to /sys/dev/char directory. */ char *path_get_sys_dev_char_devices(void) { return path_get("/sys/dev/char"); } /* Return sysfs path to class directory. */ char *path_get_sys_class(const char *class, const char *name) { if (name) return path_get("/sys/class/%s/%s", class, name); return path_get("/sys/class/%s", class); } /* Return path to modprobe executable. */ char *path_get_modprobe(void) { return path_get("%s", PATH_MODPROBE); } /* Return sysfs path to CCW device. */ char *path_get_ccw_device(const char *drv, const char *id) { if (drv) return path_get("%s/drivers/%s/%s", PATH_CCW_BUS, drv, id); return path_get("%s/devices/%s", PATH_CCW_BUS, id); } /* Return sysfs path to directory containing all CCW devices. */ char *path_get_ccw_devices(const char *drv) { if (drv) return path_get("%s/drivers/%s/", PATH_CCW_BUS, drv); return path_get("%s/devices/", PATH_CCW_BUS); } /* Return sysfs path to CCWGROUP device. */ char *path_get_ccwgroup_device(const char *drv, const char *id) { if (drv) return path_get("%s/drivers/%s/%s", PATH_CCWGROUP_BUS, drv, id); return path_get("%s/devices/%s", PATH_CCWGROUP_BUS, id); } /* Return sysfs path to directory containing all CCWGROUP devices. */ char *path_get_ccwgroup_devices(const char *drv) { if (drv) return path_get("%s/drivers/%s/", PATH_CCWGROUP_BUS, drv); return path_get("%s/devices/", PATH_CCWGROUP_BUS); } /* Return path to udev rule. */ char *path_get_udev_rule(const char *type, const char *id, bool vol) { const char *path = vol ? PATH_UDEV_RULES_VOLATILE : PATH_UDEV_RULES; if (id) { return path_get("%s/%s-%s-%s%s", path, UDEV_PREFIX, type, id, UDEV_SUFFIX); } return path_get("%s/%s-%s%s", path, UDEV_PREFIX, type, UDEV_SUFFIX); } /* Return path to directory containing all udev rules. */ char *path_get_udev_rules(bool vol) { const char *path = vol ? PATH_UDEV_RULES_VOLATILE : PATH_UDEV_RULES; return path_get("%s", path); } /* Return path to the specified file in the proc file system. */ char *path_get_proc(const char *filename) { return path_get("%s/%s", PATH_PROC, filename); } /* Call a function for each entry in a directory: * exit_code_t callback(const char *abs_path, const char *rel_path, void *data) * Aborts when callback returns any value other than EXIT_OK. */ exit_code_t path_for_each(const char *path, exit_code_t (*callback)(const char *, const char *, void *), void *data) { DIR *dir; struct dirent *de; char *p; exit_code_t rc = EXIT_OK; dir = opendir(path); if (!dir) { warn("Could not open directory %s: %s\n", path, strerror(errno)); return EXIT_RUNTIME_ERROR; } while (rc == EXIT_OK && (de = readdir(dir))) { if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) continue; p = path_get("%s/%s", path, de->d_name); rc = callback(p, de->d_name, data); free(p); } closedir(dir); return rc; } /* Return sysfs path to device or devices directory. */ char *path_get_sys_bus_dev(const char *bus, const char *id) { if (!id) return path_get("/sys/bus/%s/devices", bus); return path_get("/sys/bus/%s/devices/%s", bus, id); } /* Return sysfs path to scsi drivers directory. */ char *path_get_sys_bus_drv(const char *bus, const char *drv) { if (!drv) return path_get("/sys/bus/%s/drivers", bus); return path_get("/sys/bus/%s/drivers/%s", bus, drv); } /* Return sysfs path to zFCP LUN directory. */ char *path_get_zfcp_lun_dev(struct zfcp_lun_devid *id) { return path_get("%s/drivers/%s/%x.%x.%04x/0x%016" PRIx64 "/0x%016" PRIx64, PATH_CCW_BUS, ZFCP_CCWDRV_NAME, id->fcp_dev.cssid, id->fcp_dev.ssid, id->fcp_dev.devno, id->wwpn, id->lun); } /* Return sysfs path to zFCP target port directory. */ char *path_get_zfcp_port_dev(struct zfcp_lun_devid *id) { return path_get("%s/drivers/%s/%x.%x.%04x/0x%016" PRIx64, PATH_CCW_BUS, ZFCP_CCWDRV_NAME, id->fcp_dev.cssid, id->fcp_dev.ssid, id->fcp_dev.devno, id->wwpn); } /* Return sysfs path to SCSI device directory. */ char *path_get_scsi_hctl_dev(const char *hctl) { return path_get("/sys/bus/scsi/devices/%s", hctl); }