Files
s390-tools/dasdinfo/dasdinfo.c
Szabina Korbai 364cb9d869 dasdinfo: Implement zsh and bash autocompletion
Add generation of shell	autocompletion scripts.

Acked-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Szabina Korbai <szkorbai@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-04-30 08:47:05 +02:00

689 lines
16 KiB
C

/*
* dasdinfo - Display unique DASD ID, either UID or volser
*
* Copyright IBM Corp. 2007, 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 <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <ftw.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <unistd.h>
#include "lib/dasd_base.h"
#include "lib/util_file.h"
#include "lib/util_libc.h"
#include "lib/util_opt.h"
#include "lib/util_prg.h"
#include "lib/util_path.h"
#include "lib/zt_common.h"
#include "dasdinfo_cli.h"
#define RD_BUFFER_SIZE 80
#define TEMP_DEV_MAX_RETRIES 1000
#ifndef FTW_CONTINUE
#define FTW_CONTINUE 0
#define FTW_STOP 1
#endif
static const struct util_prg prg = {
.desc = "Display DASD volume serial number and ID information",
.args = "-i BUSID | -b BLOCKDEV | -d DEVNODE",
.copyright_vec = {
{
.owner = "IBM Corp.",
.pub_first = 2007,
},
UTIL_PRG_COPYRIGHT_END
}
};
/* needed because ftw can not pass arbitrary arguments */
static char *searchbusid;
static char *busiddir;
struct volume_label {
char volkey[4];
char vollbl[4];
char volid[6];
} __packed;
static char ebc_to_asc[256] = {
/* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */
0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F,
/* 0x08 -GE -SPS -RPT VT FF CR SO SI */
0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
/* 0x10 DLE DC1 DC2 DC3 -RES -NL BS -POC */
0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07,
/* 0x18 CAN EM -UBS -CU1 -IFS -IGS -IRS -ITB */
0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
/* 0x20 -DS -SOS FS -WUS -BYP LF ETB ESC */
0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B,
/* 0x28 -SA -SFE -SM -CSP -MFA ENQ ACK BEL */
0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07,
/* 0x30 ---- ---- SYN -IR -PP -TRN -NBS EOT */
0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04,
/* 0x38 -SBS -IT -RFF -CU3 DC4 NAK ---- SUB */
0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A,
/* 0x40 SP RSP ? ---- */
0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86,
/* 0x48 . < ( + | */
0x87, 0xA4, 0x9B, 0x2E, 0x3C, 0x28, 0x2B, 0x7C,
/* 0x50 & ---- */
0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07,
/* 0x58 ? ! $ * ) ; */
0x8D, 0xE1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAA,
/* 0x60 - / ---- ? ---- ---- ---- */
0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F,
/* 0x68 ---- , % _ > ? */
0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F,
/* 0x70 --- ---- ---- ---- ---- ---- ---- */
0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
/* 0x78 * ` : # @ ' = " */
0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22,
/* 0x80 * a b c d e f g */
0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
/* 0x88 h i ---- ---- ---- */
0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1,
/* 0x90 ? j k l m n o p */
0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70,
/* 0x98 q r ---- ---- */
0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07,
/* 0xA0 ~ s t u v w x */
0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
/* 0xA8 y z ---- ---- ---- ---- */
0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07,
/* 0xB0 ^ ---- ? ---- */
0x5E, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC,
/* 0xB8 ---- [ ] ---- ---- ---- ---- */
0xAB, 0x07, 0x5B, 0x5D, 0x07, 0x07, 0x07, 0x07,
/* 0xC0 { A B C D E F G */
0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
/* 0xC8 H I ---- ? ---- */
0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07,
/* 0xD0 } J K L M N O P */
0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
/* 0xD8 Q R ---- ? */
0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98,
/* 0xE0 \ S T U V W X */
0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
/* 0xE8 Y Z ---- ? ---- ---- ---- */
0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07,
/* 0xF0 0 1 2 3 4 5 6 7 */
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
/* 0xF8 8 9 ---- ---- ? ---- ---- ---- */
0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07
};
static char *dinfo_ebcdic_dec(char *source, char *target, int l)
{
int i;
for (i = 0; i < l; i++)
target[i] = ebc_to_asc[(unsigned char)(source[i])];
return target;
}
static int dinfo_read_dasd_uid(char *uidfile, char *readbuf)
{
return util_file_read_line(readbuf, RD_BUFFER_SIZE, uidfile);
}
static int dinfo_read_dasd_vlabel(char *device, struct volume_label *vlabel,
char *readbuf)
{
struct volume_label tmp;
int vlsize = sizeof(struct volume_label);
dasd_information2_t dasd_info;
unsigned long vlabel_start;
unsigned int blksize;
char vollbl[5];
int f;
char *space;
if (dasd_get_blocksize(device, &blksize) != 0) {
warnx("Unable to figure out block size");
goto error;
}
if (dasd_get_info(device, &dasd_info) != 0) {
warnx("Unable to figure out DASD information");
goto error;
}
f = open(device, O_RDONLY);
if (f < 0) {
warnx("Could not open device node");
goto error;
}
vlabel_start = dasd_info.label_block * blksize;
if (lseek(f, vlabel_start, SEEK_SET) < 0)
goto error_close;
memset(vlabel, 0, vlsize);
if (read(f, vlabel, vlsize) != vlsize) {
warnx("Could not read volume label");
goto error_close;
}
if (dasd_info.FBA_layout) {
memset(&tmp, 0, vlsize);
memcpy(&tmp, vlabel, vlsize);
memcpy(vlabel->vollbl, &tmp, vlsize - 4);
}
close(f);
memset(readbuf, 0, 7);
memset(vollbl, 0, 5);
strncpy(vollbl, vlabel->vollbl, 4);
dinfo_ebcdic_dec(vollbl, vollbl, 4);
if ((strncmp(vollbl, "VOL1", 4) == 0) ||
(strncmp(vollbl, "LNX1", 4) == 0) ||
(strncmp(vollbl, "CMS1", 4) == 0)) {
strncpy(readbuf, vlabel->volid, 6);
dinfo_ebcdic_dec(readbuf, readbuf, 6);
space = strchr(readbuf, ' ');
if (space)
*space = 0;
} else {
strcpy(readbuf, "");
}
return 0;
error_close:
close(f);
error:
return -1;
}
static void *dinfo_malloc(size_t size)
{
void *result;
result = malloc(size);
if (!result)
warnx("Could not allocate %lu bytes of memory", size);
return result;
}
static char *dinfo_make_path(char *dirname, char *filename)
{
char *result;
size_t len;
len = strlen(dirname) + strlen(filename) + 2;
result = (char *)dinfo_malloc(len);
if (!result)
return NULL;
sprintf(result, "%s/%s", dirname, filename);
return result;
}
static int dinfo_create_devnode(dev_t dev, char **devno)
{
char *result;
char *pathname[] = { "/dev", getenv("TMPDIR"), "/tmp",
getenv("HOME"), ".", "/"};
char filename[] = "dasdinfo0000";
mode_t mode;
unsigned int path;
unsigned int retry;
int rc;
int fd;
mode = S_IFBLK | S_IRWXU;
/* Try several locations for the temporary device node. */
for (path = 0; path < ARRAY_SIZE(pathname); path++) {
if (!pathname[path])
continue;
for (retry = 0; retry < TEMP_DEV_MAX_RETRIES; retry++) {
snprintf(filename, sizeof(filename), "dasdinfo%04d", retry);
result = dinfo_make_path(pathname[path], filename);
if (!result)
return -1;
rc = mknod(result, mode, dev);
if (rc == 0) {
/* Need this test to cover
* 'nodev'-mounted
* filesystems.
*/
fd = open(result, O_RDONLY);
if (fd != -1) {
close(fd);
*devno = result;
return 0;
}
remove(result);
retry = TEMP_DEV_MAX_RETRIES;
} else if (errno != EEXIST) {
retry = TEMP_DEV_MAX_RETRIES;
}
free(result);
}
}
warnx("Error: Unable to create temporary device node");
return -1;
}
static void dinfo_free_devnode(char *device)
{
if (remove(device))
warnx("Warning: Could not remove temporary file %s", device);
}
static int dinfo_extract_dev(dev_t *dev, char *str)
{
char tmp[RD_BUFFER_SIZE];
char *p = NULL;
int ma, mi;
memset(tmp, 0, RD_BUFFER_SIZE);
util_strlcpy(tmp, str, RD_BUFFER_SIZE);
p = strchr(tmp, ':');
if (!p) {
warnx("Error: unable to extract major/minor");
return -1;
}
*p = '\0';
ma = atoi(tmp);
mi = atoi(p + sizeof(char));
*dev = makedev(ma, mi);
return 0;
}
static int dinfo_get_dev_from_blockdev(char *blockdev, dev_t *dev)
{
char *readbuf = NULL;
char *path;
readbuf = dinfo_malloc(RD_BUFFER_SIZE);
if (!readbuf) {
warnx("Error: Not enough memory to allocate readbuffer");
return -1;
}
path = util_path_sysfs("block/%s/dev", blockdev);
if (util_file_read_line(readbuf, RD_BUFFER_SIZE, path) < 0) {
free(path);
free(readbuf);
return -1;
}
free(path);
if (dinfo_extract_dev(dev, readbuf) != 0) {
free(readbuf);
return -1;
}
free(readbuf);
return 0;
}
static int
dinfo_is_busiddir(const char *fpath, const struct stat *UNUSED(sb),
int tflag, struct FTW *ftwbuf)
{
char *tempdir;
char *linkdir;
if (tflag != FTW_D || (strncmp((fpath + ftwbuf->base), searchbusid,
strlen(searchbusid)) != 0))
return FTW_CONTINUE;
/*
* ensure that the found entry is a busid and not a
* subchannel ID
* for large systems subchannel IDs may look like busids
*/
if (asprintf(&tempdir, "%s/driver", fpath) < 0)
return -1;
linkdir = util_readlink(tempdir);
free(tempdir);
if (!strstr(linkdir, "dasd")) {
free(linkdir);
return FTW_CONTINUE;
}
free(linkdir);
free(busiddir);
busiddir = strdup(fpath);
if (!busiddir)
return -1;
return FTW_STOP;
}
static int
dinfo_find_entry(const char *dir, const char *searchstring,
char type, char **result)
{
DIR *directory = NULL;
struct dirent *dir_entry = NULL;
directory = opendir(dir);
if (!directory)
return -1;
while ((dir_entry = readdir(directory)) != NULL) {
/* compare if the found entry has exactly the same name and type
* as searched
*/
if ((strncmp(dir_entry->d_name, searchstring,
strlen(searchstring)) == 0) &&
(dir_entry->d_type & type)) {
*result = strdup(dir_entry->d_name);
if (!*result)
goto out;
closedir(directory);
return 0; /* found */
}
}
out:
closedir(directory);
return -1; /* nothing found or error */
}
static int
dinfo_get_blockdev_from_busid(char *busid, char **blkdev)
{
int flags = FTW_PHYS; /* do not follow links */
int rc = -1;
char *tempdir = NULL;
char *result = NULL;
char *sysfsdir = util_path_sysfs("devices/");
/* dinfo_is_devnode needs to know the busid */
searchbusid = busid;
if (nftw(sysfsdir, dinfo_is_busiddir, 200, flags) != FTW_STOP)
goto out;
/*
* new sysfs: busid directory contains a directory 'block'
* which contains a directory 'dasdXXX'
*/
rc = dinfo_find_entry(busiddir, "block", DT_DIR, &result);
if (rc == 0) {
if (asprintf(&tempdir, "%s/%s/", busiddir, result) < 0) {
rc = -1;
goto out2;
}
rc = dinfo_find_entry(tempdir, "dasd", DT_DIR, blkdev);
} else {
/*
* old sysfs: entry for busiddir contain a link
* 'block:dasdXXX'
*/
rc = dinfo_find_entry(busiddir, "block:", DT_LNK, &result);
if (rc != 0)
goto out2;
*blkdev = strdup(strchr(result, ':') + 1);
if (!*blkdev)
rc = -1;
}
out:
free(tempdir);
out2:
free(busiddir);
free(result);
free(sysfsdir);
return rc;
}
static int dinfo_get_uid_from_devnode(char **uidfile, char *devnode)
{
struct stat stat_buffer;
char stat_dev[RD_BUFFER_SIZE];
char *readbuf;
DIR *directory = NULL;
struct dirent *dir_entry = NULL;
int rc = 0;
char *path;
if (stat(devnode, &stat_buffer) != 0) {
warnx("Error: could not stat %s", devnode);
return -1;
}
sprintf(stat_dev, "%d:%d", major(stat_buffer.st_rdev),
minor(stat_buffer.st_rdev));
path = util_path_sysfs("block/");
directory = opendir(path);
if (!directory) {
warnx("Error: could not open directory %s", path);
free(path);
return -1;
}
readbuf = dinfo_malloc(RD_BUFFER_SIZE);
if (!readbuf) {
warnx("Error: Not enough memory to allocate readbuffer");
free(path);
return -1;
}
while ((dir_entry = readdir(directory)) != NULL) {
if (util_file_read_line(readbuf, RD_BUFFER_SIZE, "%s%s/dev", path,
dir_entry->d_name) < 0)
continue;
if (strncmp(stat_dev, readbuf,
MAX(strlen(stat_dev), strlen(readbuf) - 1)) == 0) {
rc = snprintf(*uidfile, RD_BUFFER_SIZE, "%s%s/device/uid", path,
dir_entry->d_name);
if (rc >= RD_BUFFER_SIZE) {
fprintf(stderr,
"Error: Device name was truncated\n");
free(path);
return -1;
}
break;
}
}
closedir(directory);
free(path);
return 0;
}
int main(int argc, char *argv[])
{
char *uidfile = NULL;
char *device = NULL;
char *readbuf = NULL;
dev_t dev;
int export = 0;
int c;
int print_uid = 0;
int print_extended_uid = 0;
int print_vlabel = 0;
char *blockdev = NULL;
char *busid = NULL;
char *devnode = NULL;
struct volume_label vlabel;
char *srchuid;
int i, rc = 0;
util_prg_init(&prg);
util_opt_init(opt_vec, NULL);
while (1) {
c = util_opt_getopt_long(argc, argv);
if (c == -1)
break;
switch (c) {
case 'a':
print_uid = 1;
print_vlabel = 1;
print_extended_uid = 1;
break;
case 'u':
print_uid = 1;
break;
case 'x':
print_extended_uid = 1;
break;
case 'l':
print_vlabel = 1;
break;
case 'i':
busid = strdup(optarg);
break;
case 'b':
blockdev = strdup(optarg);
break;
case 'd':
devnode = strdup(optarg);
break;
case 'e':
export = 1;
break;
case 'h':
util_prg_print_help();
util_opt_print_help();
exit(EXIT_SUCCESS);
case 'v':
util_prg_print_version();
exit(EXIT_SUCCESS);
default:
fprintf(stderr, "Try 'dasdinfo --help' for more information.\n");
exit(1);
}
}
if (!busid && !blockdev && !devnode)
errx(EXIT_FAILURE, "Error: please specify a device using either -b, -i or -d");
if ((busid && blockdev) || (busid && devnode) || (blockdev && devnode))
errx(EXIT_FAILURE, "Error: please specify device only once, either -b, -i or -d");
if (!print_uid && !print_extended_uid && !print_vlabel)
errx(EXIT_FAILURE, "Error: no action specified (e.g. -u)");
readbuf = dinfo_malloc(RD_BUFFER_SIZE);
if (!readbuf)
exit(1);
/* try to read the uid attribute */
if (busid) {
uidfile = util_path_sysfs("bus/ccw/devices/%s/uid", busid);
} else if (blockdev) {
uidfile = util_path_sysfs("block/%s/device/uid", blockdev);
} else if (devnode) {
uidfile = dinfo_malloc(RD_BUFFER_SIZE);
if (!uidfile)
exit(1);
if (dinfo_get_uid_from_devnode(&uidfile, devnode) != 0)
goto error;
}
if (export) {
printf("ID_BUS=ccw\n");
printf("ID_TYPE=disk\n");
}
if (print_uid) {
if (dinfo_read_dasd_uid(uidfile, readbuf) == 0) {
/* look for the 4th '.' and cut there */
srchuid = readbuf - 1;
for (i = 0; i < 4; ++i) {
srchuid = index(srchuid + 1, '.');
if (!srchuid)
break;
}
if (srchuid)
srchuid[0] = '\0';
if (export)
printf("ID_UID=%s\n", readbuf);
else
printf("%s\n", readbuf);
if (!print_vlabel && !print_extended_uid)
goto out;
}
}
if (print_extended_uid) {
if (dinfo_read_dasd_uid(uidfile, readbuf) == 0) {
if (export)
printf("ID_XUID=%s\n", readbuf);
else
printf("%s\n", readbuf);
if (!print_vlabel)
goto out;
}
}
/* there is no uid, try to read the volume serial */
if (busid) {
char *blockdev_name = NULL;
if (dinfo_get_blockdev_from_busid(busid, &blockdev_name) != 0)
goto error;
if (dinfo_get_dev_from_blockdev(blockdev_name, &dev) != 0)
goto error;
if (dinfo_create_devnode(dev, &device) != 0)
goto error;
free(blockdev_name);
} else if (blockdev) {
if (dinfo_get_dev_from_blockdev(blockdev, &dev) != 0)
goto error;
if (dinfo_create_devnode(dev, &device) != 0)
goto error;
} else if (devnode) {
device = dinfo_malloc(RD_BUFFER_SIZE);
if (!device)
exit(1);
strcpy(device, devnode);
}
if (dinfo_read_dasd_vlabel(device, &vlabel, readbuf) == 0) {
if (export)
printf("ID_SERIAL=%s\n", readbuf);
else
printf("%s\n", readbuf);
goto out;
}
error:
warnx("Error: could not read unique DASD ID");
rc = 1;
out:
if (device && (busid || blockdev))
dinfo_free_devnode(device);
free(uidfile);
free(device);
free(readbuf);
exit(rc);
}