Viewing: lr_reader.c
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
* Copyright (c) 2013, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
*
* lustre/utils/lr_reader.c
*
* Author: Nathan Rutman <nathan@clusterfs.com>
*/
/* Safely read the last_rcvd file from a device */
#if HAVE_CONFIG_H
# include "config.h"
#endif /* HAVE_CONFIG_H */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <errno.h>
#include <limits.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdarg.h>
#include <mntent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <string.h>
#include <getopt.h>
#include <asm/byteorder.h>
#include <linux/lustre/lustre_idl.h>
#include <linux/lustre/lustre_disk.h>
#include <linux/lustre/lustre_ver.h>
char *progname;
static struct option const long_opts[] = {
{ .val = 'c', .name = "client", .has_arg = no_argument },
{ .val = 'h', .name = "help", .has_arg = no_argument },
{ .val = 'r', .name = "reply", .has_arg = no_argument },
{ .val = 'R', .name = "reply_data", .has_arg = required_argument },
{ .val = 'C', .name = "last_rcvd", .has_arg = required_argument },
{ .name = NULL } };
static void dump_log(int fd)
{
char buf[128];
int n;
do {
n = read(fd, buf, sizeof(buf));
if (n < 0) {
fprintf(stderr,
"%s: dump_log() Failed to read: %s\n",
progname, strerror(errno));
exit(errno);
}
n = write(2, buf, n);
} while (n == sizeof(buf));
fprintf(stderr, "\n");
}
static FILE *open_debugfs_file(char *filename, char *tmpdir, char *dev)
{
char log[] = "/tmp/run_command_logXXXXXX";
char filepnm[128];
char *cmd = NULL;
FILE *fp = NULL;
int flog = 0;
int cmdsize;
int n = 128;
int rc = 0;
flog = mkstemp(log);
if (flog < 0)
return NULL;
do {
cmdsize = n;
cmd = realloc(cmd, cmdsize);
if (!cmd) {
fprintf(stderr, "%s: Unable to allocate cmd buffer\n",
progname);
goto out;
}
n = snprintf(cmd, cmdsize,
"%s -c -R 'dump /%s %s/%s' %s > %s 2>&1",
DEBUGFS, filename, tmpdir, filename, dev, log);
n++;
} while (n > cmdsize);
rc = system(cmd);
free(cmd);
if (rc) {
fprintf(stderr, "%s: Unable to dump %s file\n",
progname, filename);
goto out;
}
snprintf(filepnm, sizeof(filepnm), "%s/%s", tmpdir, filename);
fp = fopen(filepnm, "r");
if (!fp)
rc = errno;
unlink(filepnm);
out:
if (rc)
dump_log(flog);
close(flog);
unlink(log);
errno = rc;
return fp;
}
static int print_last_rcvd(FILE *fp, int opt_client)
{
struct lr_server_data lsd = {};
int rc = 0;
int n;
/* read lr_server_data structure */
printf("%s:\n", LAST_RCVD);
n = fread(&lsd, 1, sizeof(lsd), fp);
if (n < sizeof(lsd)) {
fprintf(stderr, "%s: Short read (%d of %d)\n",
progname, n, (int)sizeof(lsd));
rc = ferror(fp) ? EIO : EINVAL;
}
/* swab structure fields of interest */
lsd.lsd_feature_compat = __le32_to_cpu(lsd.lsd_feature_compat);
lsd.lsd_feature_incompat = __le32_to_cpu(lsd.lsd_feature_incompat);
lsd.lsd_feature_rocompat = __le32_to_cpu(lsd.lsd_feature_rocompat);
lsd.lsd_last_transno = __le64_to_cpu(lsd.lsd_last_transno);
lsd.lsd_osd_index = __le32_to_cpu(lsd.lsd_osd_index);
lsd.lsd_mount_count = __le64_to_cpu(lsd.lsd_mount_count);
/* display */
printf(" uuid: %.40s\n", lsd.lsd_uuid);
printf(" feature_compat: %#x\n", lsd.lsd_feature_compat);
printf(" feature_incompat: %#x\n", lsd.lsd_feature_incompat);
printf(" feature_rocompat: %#x\n", lsd.lsd_feature_rocompat);
printf(" last_transaction: %llu\n",
(unsigned long long)lsd.lsd_last_transno);
printf(" target_index: %u\n", lsd.lsd_osd_index);
printf(" mount_count: %llu\n",
(unsigned long long)lsd.lsd_mount_count);
if (!opt_client || rc)
return rc;
/* read client information */
lsd.lsd_client_start = __le32_to_cpu(lsd.lsd_client_start);
lsd.lsd_client_size = __le16_to_cpu(lsd.lsd_client_size);
printf(" client_area_start: %u\n", lsd.lsd_client_start);
printf(" client_area_size: %hu\n", lsd.lsd_client_size);
/* seek to per-client data area */
rc = fseek(fp, lsd.lsd_client_start, SEEK_SET);
if (rc) {
fprintf(stderr, "%s: seek failed. %s\n",
progname, strerror(errno));
return errno;
}
/* walk throuh the per-client data area */
while (true) {
struct lsd_client_data lcd;
/* read a per-client data area */
n = fread(&lcd, 1, sizeof(lcd), fp);
if (n < sizeof(lcd)) {
if (feof(fp))
break;
fprintf(stderr, "%s: Short read (%d of %d)\n",
progname, n, (int)sizeof(lcd));
return ferror(fp) ? EIO : EINVAL;
}
if (lcd.lcd_uuid[0] == '\0')
continue;
/* swab structure fields */
lcd.lcd_last_transno =
__le64_to_cpu(lcd.lcd_last_transno);
lcd.lcd_last_xid = __le64_to_cpu(lcd.lcd_last_xid);
lcd.lcd_last_result = __le32_to_cpu(lcd.lcd_last_result);
lcd.lcd_last_data = __le32_to_cpu(lcd.lcd_last_data);
lcd.lcd_generation = __le32_to_cpu(lcd.lcd_generation);
/* display per-client data area */
printf("\n %.40s:\n", lcd.lcd_uuid);
printf(" generation: %u\n", lcd.lcd_generation);
printf(" last_transaction: %llu\n",
(unsigned long long)lcd.lcd_last_transno);
printf(" last_xid: %llu\n",
(unsigned long long)lcd.lcd_last_xid);
printf(" last_result: %u\n", lcd.lcd_last_result);
printf(" last_data: %u\n", lcd.lcd_last_data);
if (lcd.lcd_last_close_transno != 0 &&
lcd.lcd_last_close_xid != 0) {
lcd.lcd_last_close_transno =
__le64_to_cpu(lcd.lcd_last_close_transno);
lcd.lcd_last_close_xid =
__le64_to_cpu(lcd.lcd_last_close_xid);
lcd.lcd_last_close_result =
__le32_to_cpu(lcd.lcd_last_close_result);
lcd.lcd_last_close_data =
__le32_to_cpu(lcd.lcd_last_close_data);
printf(" last_close_transation: %llu\n",
(unsigned long long)lcd.lcd_last_close_transno);
printf(" last_close_xid: %llu\n",
(unsigned long long)lcd.lcd_last_close_xid);
printf(" last_close_result: %u\n",
lcd.lcd_last_close_result);
printf(" last_close_data: %u\n",
lcd.lcd_last_close_data);
}
}
return 0;
}
static int print_reply_data(FILE *fp)
{
struct lsd_reply_header lrh = {};
unsigned long long slot;
__u32 recsz;
int rc = 0;
int n;
/* read reply_data header */
printf("\n%s:\n", REPLY_DATA);
n = fread(&lrh, 1, sizeof(lrh), fp);
if (n < sizeof(lrh)) {
fprintf(stderr, "%s: Short read (%d of %d)\n",
progname, n, (int)sizeof(lrh));
rc = ferror(fp) ? EIO : EINVAL;
}
/* check header */
lrh.lrh_magic = __le32_to_cpu(lrh.lrh_magic);
lrh.lrh_header_size = __le32_to_cpu(lrh.lrh_header_size);
lrh.lrh_reply_size = __le32_to_cpu(lrh.lrh_reply_size);
if (lrh.lrh_header_size != sizeof(struct lsd_reply_header)) {
fprintf(stderr,
"%s: invalid %s header: lrh_header_size=0x%08x expected 0x%08x\n",
progname, REPLY_DATA, lrh.lrh_header_size,
(unsigned int)sizeof(struct lsd_reply_header));
rc = EINVAL;
}
if (lrh.lrh_magic == LRH_MAGIC_V2) {
if (lrh.lrh_reply_size != sizeof(struct lsd_reply_data)) {
fprintf(stderr,
"%s: invalid %s header: lrh_reply_size=0x%08x expected 0x%08x\n",
progname, REPLY_DATA, lrh.lrh_reply_size,
(unsigned int)sizeof(struct lsd_reply_data));
rc = EINVAL;
} else {
recsz = sizeof(struct lsd_reply_data);
}
} else if (lrh.lrh_magic == LRH_MAGIC_V1) {
if (lrh.lrh_reply_size != sizeof(struct lsd_reply_data_v1)) {
fprintf(stderr,
"%s: invalid %s header: lrh_reply_size=0x%08x expected 0x%08x\n",
progname, REPLY_DATA, lrh.lrh_reply_size,
(unsigned int)sizeof(struct lsd_reply_data));
rc = EINVAL;
} else {
recsz = sizeof(struct lsd_reply_data_v1);
}
} else {
fprintf(stderr,
"%s: invalid %s header: lrh_magic=0x%08x expected 0x%08x or 0x%08x\n",
progname, REPLY_DATA, lrh.lrh_magic, LRH_MAGIC_V1,
LRH_MAGIC_V2);
rc = EINVAL;
}
if (rc) {
/* dump header */
fprintf(stderr, "lsd_reply_header:\n");
fprintf(stderr, "\tlrh_magic: 0x%08x\n", lrh.lrh_magic);
fprintf(stderr, "\tlrh_header_size: %u\n", lrh.lrh_header_size);
fprintf(stderr, "\tlrh_reply_size: %u\n", lrh.lrh_reply_size);
return rc;
}
/* walk throuh the reply data */
for (slot = 0; ; slot++) {
struct lsd_reply_data lrd;
/* read a reply data */
n = fread(&lrd, 1, recsz, fp);
if (n < recsz) {
if (feof(fp))
break;
fprintf(stderr, "%s: Short read (%d of %d)\n",
progname, n, (int)sizeof(lrd));
return ferror(fp) ? EIO : EINVAL;
}
/* display reply data */
lrd.lrd_transno = __le64_to_cpu(lrd.lrd_transno);
lrd.lrd_xid = __le64_to_cpu(lrd.lrd_xid);
lrd.lrd_data = __le64_to_cpu(lrd.lrd_data);
lrd.lrd_result = __le32_to_cpu(lrd.lrd_result);
lrd.lrd_client_gen = __le32_to_cpu(lrd.lrd_client_gen);
if (lrh.lrh_magic > LRH_MAGIC_V1)
lrd.lrd_batch_idx = __le32_to_cpu(lrd.lrd_batch_idx);
printf(" %lld:\n", slot);
printf(" client_generation: %u\n",
lrd.lrd_client_gen);
printf(" last_transaction: %llu\n",
(unsigned long long)lrd.lrd_transno);
printf(" last_xid: %llu\n",
(unsigned long long)lrd.lrd_xid);
printf(" last_result: %u\n", lrd.lrd_result);
printf(" last_data: %llu\n\n",
(unsigned long long)lrd.lrd_data);
if (lrh.lrh_magic > LRH_MAGIC_V1)
printf(" batch_idx: %u\n", lrd.lrd_batch_idx);
}
return 0;
}
static void display_usage(void)
{
printf("Usage: %s [OPTIONS] devicename\n", progname);
printf("Usage: %s [OPTIONS] -C <last_rcvd_file> -R <reply_data_file>\n",
progname);
printf("Read and print the last_rcvd/reply_data file from a device\n");
printf("(safe for mounted devices) or from a file\n");
printf("\t-c, --client, display client information\n");
printf("\t-h, --help, display this help and exit\n");
printf("\t-r, --reply, display reply data information\n");
printf("\t-C FILE, --last_rcvd=FILE, specify FILE as input for client information\n");
printf("\t-R FILE, --reply_data=FILE, specify FILE as input for reply information\n");
}
int main(int argc, char *const argv[])
{
char tmpdir[] = "/tmp/dirXXXXXX";
char *dev;
FILE *filep = NULL;
int ret = 0;
int c;
int opt_client = 0;
int opt_reply = 0;
char *file_client = NULL;
char *file_reply = NULL;
int need_dev = 1;
progname = basename(argv[0]);
while ((c = getopt_long(argc, argv, "hcrC:R:", long_opts, NULL)) != -1) {
switch (c) {
case 'c':
opt_client = 1;
break;
case 'r':
opt_reply = 1;
break;
case 'C':
file_client = optarg;
break;
case 'R':
file_reply = optarg;
break;
case 'h':
default:
display_usage();
return -1;
}
}
if ((file_reply && file_client) ||
(!opt_reply && file_client) ||
(!opt_client && opt_reply && file_reply))
need_dev = 0;
dev = argv[optind];
if (need_dev && !dev) {
display_usage();
return -1;
}
/* Make a temporary directory to hold Lustre data files. */
if (need_dev && !mkdtemp(tmpdir)) {
fprintf(stderr, "%s: Can't create temporary directory %s: %s\n",
progname, tmpdir, strerror(errno));
return errno;
}
if (file_client || dev) {
if (file_client)
filep = fopen(file_client, "r");
else
filep = open_debugfs_file(LAST_RCVD, tmpdir, dev);
if (!filep) {
ret = errno;
fprintf(stderr, "%s: Can't open %s: %s\n",
progname, LAST_RCVD, strerror(errno));
goto out_rmdir;
}
ret = print_last_rcvd(filep, opt_client);
fclose(filep);
filep = NULL;
if (ret)
goto out_rmdir;
}
if (opt_reply) {
if (file_reply)
filep = fopen(file_reply, "r");
else
filep = open_debugfs_file(REPLY_DATA, tmpdir, dev);
if (!filep) {
ret = errno;
fprintf(stderr, "%s: Can't open %s: %s\n",
progname, LAST_RCVD, strerror(errno));
goto out_rmdir;
}
ret = print_reply_data(filep);
fclose(filep);
}
out_rmdir:
if (need_dev)
rmdir(tmpdir);
return ret;
}