Viewing: obd.c
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
* Copyright (c) 2011, 2017, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
*
* lustre/utils/obd.c
*
* Author: Peter J. Braam <braam@clusterfs.com>
* Author: Phil Schwan <phil@clusterfs.com>
* Author: Andreas Dilger <adilger@clusterfs.com>
* Author: Robert Read <rread@clusterfs.com>
*/
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <signal.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <limits.h>
#include "obdctl.h"
#include "lstddef.h"
#include "lustreapi_internal.h"
#include <libcfs/util/list.h>
#include <libcfs/util/ioctl.h>
#include <libcfs/util/param.h>
#include <libcfs/util/parser.h>
#include <libcfs/util/string.h>
#include <linux/lnet/nidstr.h>
#include <linux/lnet/lnetctl.h>
#include <linux/lustre/lustre_cfg.h>
#include <linux/lustre/lustre_ioctl.h>
#include <linux/lustre/lustre_ostid.h>
#include <linux/lustre/lustre_param.h>
#include <linux/lustre/lustre_ver.h>
#include <lustre/lustreapi.h>
#include <linux/lustre/lustre_disk.h>
#define MAX_STRING_SIZE 128
#if HAVE_LIBPTHREAD
#include <sys/ipc.h>
#include <sys/shm.h>
#include <pthread.h>
#define MAX_THREADS 4096
#define MAX_BASE_ID 0xffffffff
#define NIDSTRING_LENGTH 64
struct shared_data {
pthread_mutex_t mutex;
pthread_cond_t cond;
int stopping;
struct {
__u64 counters[MAX_THREADS];
__u64 offsets[MAX_THREADS];
int thr_running;
int start_barrier;
int stop_barrier;
struct timeval start_time;
struct timeval end_time;
} body;
};
static struct shared_data *shared_data;
static __u64 counter_snapshot[2][MAX_THREADS];
static int prev_valid;
static struct timeval prev_time;
static int thread;
static int nthreads;
#else
const int thread;
const int nthreads = 1;
#endif
static int cur_device = -1;
int lcfg_ioctl(char *func, int dev_id, struct lustre_cfg *lcfg)
{
struct obd_ioctl_data data;
char rawbuf[MAX_IOC_BUFLEN], *buf = rawbuf;
int rc;
memset(&data, 0, sizeof(data));
data.ioc_dev = cur_device;
data.ioc_type = LUSTRE_CFG_TYPE;
data.ioc_plen1 = lustre_cfg_len(lcfg->lcfg_bufcount,
lcfg->lcfg_buflens);
data.ioc_pbuf1 = (void *)lcfg;
memset(buf, 0, sizeof(rawbuf));
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "error: %s: invalid ioctl\n", jt_cmdname(func));
return rc;
}
rc = l_ioctl(dev_id, OBD_IOC_PROCESS_CFG, buf);
return rc;
}
static int do_device(char *func, char *devname);
static int get_mgs_device(void)
{
char mgs[] = "$MGS";
static int mgs_device = -1;
if (mgs_device == -1) {
int rc;
do_disconnect(NULL, 1);
rc = do_device("mgsioc", mgs);
if (rc) {
fprintf(stderr,
"This command must be run on the MGS.\n");
errno = ENODEV;
return -errno;
}
mgs_device = cur_device;
}
return mgs_device;
}
/* Returns 0 on success, -errno on failure */
int lcfg_mgs_ioctl(const char *func, int dev_id, struct lustre_cfg *lcfg)
{
struct obd_ioctl_data data;
char rawbuf[MAX_IOC_BUFLEN], *buf = rawbuf;
int rc;
memset(&data, 0, sizeof(data));
rc = data.ioc_dev = get_mgs_device();
if (rc < 0)
goto out;
data.ioc_type = LUSTRE_CFG_TYPE;
data.ioc_plen1 = lustre_cfg_len(lcfg->lcfg_bufcount,
lcfg->lcfg_buflens);
data.ioc_pbuf1 = (void *)lcfg;
memset(buf, 0, sizeof(rawbuf));
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "error: %s: invalid ioctl\n", jt_cmdname(func));
return rc;
}
rc = l_ioctl(dev_id, OBD_IOC_PARAM, buf);
out:
if (rc && errno == ENOSYS)
fprintf(stderr, "Make sure cfg_device is set first.\n");
return rc;
}
char *obdo_print(struct obdo *obd)
{
char buf[1024];
snprintf(buf, sizeof(buf), "id: %#jx\ngrp: %#jx\natime: %ju\n"
"mtime: %ju\nctime: %ju\nsize: %ju\nblocks: %ju"
"\nblksize: %u\nmode: %o\nuid: %d\ngid: %d\nflags: %x\n"
"misc: %x\nnlink: %d,\nvalid %#jx\n",
(uintmax_t)ostid_id(&obd->o_oi),
(uintmax_t)ostid_seq(&obd->o_oi),
(uintmax_t)obd->o_atime, (uintmax_t)obd->o_mtime,
(uintmax_t)obd->o_ctime, (uintmax_t)obd->o_size,
(uintmax_t)obd->o_blocks, obd->o_blksize, obd->o_mode,
obd->o_uid, obd->o_gid, obd->o_flags, obd->o_misc,
obd->o_nlink, (uintmax_t)obd->o_valid);
return strdup(buf);
}
#define BAD_VERBOSE (-999999999)
const char *jt_cmdname(const char *func)
{
static char buf[512];
if (thread) {
sprintf(buf, "%s-%d", func, thread);
return buf;
}
return func;
}
#define difftime(a, b) \
((a)->tv_sec - (b)->tv_sec + \
((a)->tv_usec - (b)->tv_usec) / 1000000.0)
static int be_verbose(int verbose, struct timeval *next_time,
__u64 num, __u64 *next_num, int num_total)
{
struct timeval now;
if (!verbose)
return 0;
if (next_time)
gettimeofday(&now, NULL);
/* A positive verbosity means to print every X iterations */
if (verbose > 0 && (num >= *next_num || num >= num_total)) {
*next_num += verbose;
if (next_time) {
next_time->tv_sec = now.tv_sec - verbose;
next_time->tv_usec = now.tv_usec;
}
return 1;
}
/* A negative verbosity means to print at most each X seconds */
if (verbose < 0 && next_time && difftime(&now, next_time) >= 0.0) {
next_time->tv_sec = now.tv_sec - verbose;
next_time->tv_usec = now.tv_usec;
*next_num = num;
return 1;
}
return 0;
}
static int get_verbose(char *func, const char *arg)
{
int verbose;
char *end;
if (!arg || arg[0] == 'v') {
verbose = 1;
} else if (arg[0] == 's' || arg[0] == 'q') {
verbose = 0;
} else {
verbose = (int)strtoul(arg, &end, 0);
if (*end) {
fprintf(stderr, "error: %s: bad verbose option '%s'\n",
jt_cmdname(func), arg);
return BAD_VERBOSE;
}
}
if (verbose < 0)
printf("Print status every %d seconds\n", -verbose);
else if (verbose == 1)
printf("Print status every operation\n");
else if (verbose > 1)
printf("Print status every %d operations\n", verbose);
return verbose;
}
int do_disconnect(char *func, int verbose)
{
lcfg_set_devname(NULL);
cur_device = -1;
return 0;
}
#ifdef MAX_THREADS
static int shmem_setup(void)
{
pthread_mutexattr_t mattr;
pthread_condattr_t cattr;
int rc;
int shmid;
if (shared_data)
return 0;
/* Create new segment */
shmid = shmget(IPC_PRIVATE, sizeof(*shared_data), 0600);
if (shmid == -1) {
fprintf(stderr, "Can't create shared data: %s\n",
strerror(errno));
return errno;
}
/* Attatch to new segment */
shared_data = (struct shared_data *)shmat(shmid, NULL, 0);
if (shared_data == (struct shared_data *)(-1)) {
fprintf(stderr, "Can't attach shared data: %s\n",
strerror(errno));
shared_data = NULL;
return errno;
}
/*
* Mark segment as destroyed, so it will disappear when we exit.
* Forks will inherit attached segments, so we should be OK.
*/
if (shmctl(shmid, IPC_RMID, NULL) == -1) {
fprintf(stderr, "Can't destroy shared data: %s\n",
strerror(errno));
return errno;
}
pthread_mutexattr_init(&mattr);
pthread_condattr_init(&cattr);
rc = pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
if (rc != 0) {
fprintf(stderr, "Can't set shared mutex attr\n");
goto out;
}
rc = pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
if (rc != 0) {
fprintf(stderr, "Can't set shared cond attr\n");
goto out;
}
pthread_mutex_init(&shared_data->mutex, &mattr);
pthread_cond_init(&shared_data->cond, &cattr);
out:
pthread_mutexattr_destroy(&mattr);
pthread_condattr_destroy(&cattr);
return rc;
}
static inline void shmem_lock(void)
{
pthread_mutex_lock(&shared_data->mutex);
}
static inline void shmem_unlock(void)
{
pthread_mutex_unlock(&shared_data->mutex);
}
static inline void shmem_wait(void)
{
pthread_cond_wait(&shared_data->cond, &shared_data->mutex);
}
static inline void shmem_wakeup_all(void)
{
pthread_cond_broadcast(&shared_data->cond);
}
static inline void shmem_reset(int total_threads)
{
if (!shared_data)
return;
memset(&shared_data->body, 0, sizeof(shared_data->body));
memset(counter_snapshot, 0, sizeof(counter_snapshot));
prev_valid = 0;
shared_data->stopping = 0;
shared_data->body.start_barrier = total_threads;
shared_data->body.stop_barrier = total_threads;
}
static inline void shmem_bump(__u32 counter)
{
static bool running_not_bumped = true;
if (!shared_data || thread <= 0 || thread > MAX_THREADS)
return;
shmem_lock();
shared_data->body.counters[thread - 1] += counter;
if (running_not_bumped) {
shared_data->body.thr_running++;
running_not_bumped = false;
}
shmem_unlock();
}
static void shmem_total(int total_threads)
{
__u64 total = 0;
double secs;
int i;
if (!shared_data || total_threads > MAX_THREADS)
return;
shmem_lock();
for (i = 0; i < total_threads; i++)
total += shared_data->body.counters[i];
secs = difftime(&shared_data->body.end_time,
&shared_data->body.start_time);
shmem_unlock();
printf("Total: total %ju threads %d sec %f %f/second\n",
(uintmax_t)total, total_threads, secs, total / secs);
}
static void shmem_snap(int total_threads, int live_threads)
{
struct timeval this_time;
int non_zero = 0;
__u64 total = 0;
double secs;
int running;
int i;
if (!shared_data || total_threads > MAX_THREADS)
return;
shmem_lock();
memcpy(counter_snapshot[0], shared_data->body.counters,
total_threads * sizeof(counter_snapshot[0][0]));
running = shared_data->body.thr_running;
shmem_unlock();
gettimeofday(&this_time, NULL);
for (i = 0; i < total_threads; i++) {
long long this_count =
counter_snapshot[0][i] - counter_snapshot[1][i];
if (this_count != 0) {
non_zero++;
total += this_count;
}
}
secs = difftime(&this_time, &prev_time);
if (prev_valid && secs > 1.0) { /* someone screwed with the time? */
printf("%d/%d Total: %f/second\n", non_zero, total_threads,
total / secs);
memcpy(counter_snapshot[1], counter_snapshot[0],
total_threads * sizeof(counter_snapshot[0][0]));
prev_time = this_time;
}
if (!prev_valid && running == total_threads) {
prev_valid = 1;
/* drop counters when all threads were started */
memcpy(counter_snapshot[1], counter_snapshot[0],
total_threads * sizeof(counter_snapshot[0][0]));
prev_time = this_time;
}
}
static void shmem_stop(void)
{
if (!shared_data)
return;
shared_data->stopping = 1;
}
static void shmem_cleanup(void)
{
if (!shared_data)
return;
shmem_stop();
pthread_mutex_destroy(&shared_data->mutex);
pthread_cond_destroy(&shared_data->cond);
}
static int shmem_running(void)
{
return (!shared_data || !shared_data->stopping);
}
static void shmem_end_time_locked(void)
{
shared_data->body.stop_barrier--;
if (shared_data->body.stop_barrier == 0)
gettimeofday(&shared_data->body.end_time, NULL);
}
static void shmem_start_time_locked(void)
{
shared_data->body.start_barrier--;
if (shared_data->body.start_barrier == 0) {
shmem_wakeup_all();
gettimeofday(&shared_data->body.start_time, NULL);
} else {
shmem_wait();
}
}
#else
static int shmem_setup(void)
{
return 0;
}
static inline void shmem_reset(int total_threads)
{
}
static inline void shmem_bump(__u32 counters)
{
}
static void shmem_lock(void)
{
}
static void shmem_unlock(void)
{
}
static void shmem_cleanup(void)
{
}
static int shmem_running(void)
{
return 1;
}
#endif
extern command_t cmdlist[];
static int do_device(char *func, char *devname)
{
int dev;
dev = parse_devname(func, devname, cur_device);
if (dev < 0)
return -1;
lcfg_set_devname(devname);
cur_device = dev;
return 0;
}
int jt_obd_get_device(void)
{
return cur_device;
}
int jt_obd_device(int argc, char **argv)
{
int rc;
if (argc > 2)
return CMD_HELP;
if (argc == 1) {
printf("current device is %d - %s\n",
cur_device, lcfg_get_devname() ? : "not set");
return 0;
}
rc = do_device("device", argv[1]);
return rc;
}
int jt_opt_device(int argc, char **argv)
{
int ret;
int rc;
if (argc < 3)
return CMD_HELP;
rc = do_device("device", argv[1]);
if (!rc)
rc = cfs_parser(argc - 1, argv + 1, cmdlist);
ret = do_disconnect(argv[0], 0);
if (!rc)
rc = ret;
return rc;
}
#ifdef MAX_THREADS
static void parent_sighandler(int sig)
{
}
int jt_opt_threads(int argc, char **argv)
{
static char cmdstr[129];
sigset_t saveset;
sigset_t sigset;
struct sigaction sigact;
struct sigaction saveact1;
struct sigaction saveact2;
unsigned long threads;
__u64 next_thread;
int verbose;
int rc = 0;
int report_count = -1;
char *end;
int i;
if (argc < 5)
return CMD_HELP;
threads = strtoul(argv[1], &end, 0);
if (*end == '.')
report_count = strtoul(end + 1, &end, 0);
if (*end || threads > MAX_THREADS) {
fprintf(stderr, "error: %s: invalid thread count '%s'\n",
jt_cmdname(argv[0]), argv[1]);
return CMD_HELP;
}
verbose = get_verbose(argv[0], argv[2]);
if (verbose == BAD_VERBOSE)
return CMD_HELP;
if (verbose != 0) {
snprintf(cmdstr, sizeof(cmdstr), "%s", argv[4]);
for (i = 5; i < argc; i++)
snprintf(cmdstr + strlen(cmdstr),
sizeof(cmdstr) - strlen(cmdstr),
" %s", argv[i]);
printf("%s: starting %ld threads on device %s running %s\n",
argv[0], threads, argv[3], cmdstr);
}
rc = shmem_setup();
if (rc)
return rc;
shmem_reset(threads);
sigemptyset(&sigset);
sigaddset(&sigset, SIGALRM);
sigaddset(&sigset, SIGCHLD);
sigprocmask(SIG_BLOCK, &sigset, &saveset);
nthreads = threads;
for (i = 1, next_thread = verbose; i <= threads; i++) {
rc = fork();
if (rc < 0) {
fprintf(stderr, "error: %s: #%d - %s\n", argv[0], i,
strerror(rc = errno));
break;
} else if (rc == 0) {
sigprocmask(SIG_SETMASK, &saveset, NULL);
thread = i;
argv[2] = "--device";
exit(jt_opt_device(argc - 2, argv + 2));
} else if (be_verbose(verbose, NULL, i, &next_thread, threads))
printf("%s: thread #%d (PID %d) started\n",
argv[0], i, rc);
rc = 0;
}
if (!thread) { /* parent process */
int live_threads = threads;
sigemptyset(&sigset);
sigemptyset(&sigact.sa_mask);
sigact.sa_handler = parent_sighandler;
sigact.sa_flags = 0;
sigaction(SIGALRM, &sigact, &saveact1);
sigaction(SIGCHLD, &sigact, &saveact2);
while (live_threads > 0) {
int status;
pid_t ret;
if (verbose < 0) /* periodic stats */
alarm(-verbose);
sigsuspend(&sigset);
alarm(0);
while (live_threads > 0) {
ret = waitpid(0, &status, WNOHANG);
if (ret == 0)
break;
if (ret < 0) {
fprintf(stderr,
"error: %s: wait - %s\n",
argv[0], strerror(errno));
if (!rc)
rc = errno;
continue;
} else {
/*
* This is a hack. We _should_ be able
* to use WIFEXITED(status) to see if
* there was an error, but it appears
* to be broken and it always returns 1
* (OK). See wait(2).
*/
int err = WEXITSTATUS(status);
if (err || WIFSIGNALED(status))
fprintf(stderr,
"%s: PID %d had rc=%d\n",
argv[0], ret, err);
if (!rc)
rc = err;
live_threads--;
}
}
/* Show stats while all threads running */
if (verbose < 0) {
shmem_snap(threads, live_threads);
if (report_count > 0 && --report_count == 0)
shmem_stop();
}
}
sigaction(SIGCHLD, &saveact2, NULL);
sigaction(SIGALRM, &saveact1, NULL);
}
shmem_total(threads);
sigprocmask(SIG_SETMASK, &saveset, NULL);
return rc;
}
#else
int jt_opt_threads(int argc, char **argv)
{
fprintf(stderr, "%s not-supported in a single-threaded runtime\n",
jt_cmdname(argv[0]));
return CMD_HELP;
}
#endif
int jt_opt_net(int argc, char **argv)
{
char *arg2[3];
int rc;
if (argc < 3)
return CMD_HELP;
arg2[0] = argv[0];
arg2[1] = argv[1];
arg2[2] = NULL;
rc = jt_ptl_network(2, arg2);
if (!rc)
rc = cfs_parser(argc - 1, argv + 1, cmdlist);
return rc;
}
#ifdef HAVE_SERVER_SUPPORT
/*
* Place this here so we can build tools that work with
* older Lustre versions
*/
#ifndef OBD_IOC_NO_TRANSNO
#define OBD_IOC_NO_TRANSNO _IOW('f', 140, OBD_IOC_DATA_TYPE)
#endif
static int get_mds_device(void)
{
char mds[] = "$MDS";
int rc;
do_disconnect(NULL, 1);
rc = do_device("mdsioc", mds);
if (rc) {
errno = ENODEV;
return -errno;
}
return cur_device;
}
static int get_qmt_device(const char *fsname)
{
static const char postfix[] = "-QMT0000";
char qmt[LUSTRE_MAXFSNAME + sizeof(postfix)];
int fsname_len, rc;
fsname_len = strnlen(fsname, LUSTRE_MAXFSNAME);
if (fsname_len > LUSTRE_MAXFSNAME || !fsname_len)
return -EINVAL;
memcpy(qmt, fsname, fsname_len);
memcpy(qmt + fsname_len, postfix, sizeof(postfix));
do_disconnect(NULL, 1);
rc = do_device("qmtioc", qmt);
if (rc) {
errno = ENODEV;
return -errno;
}
return cur_device;
}
static int get_oss_device(void)
{
char oss[] = "$OSS";
int rc;
do_disconnect(NULL, 1);
rc = do_device("ossioc", oss);
if (rc) {
errno = ENODEV;
return -errno;
}
return cur_device;
}
int jt_obd_no_transno(int argc, char **argv)
{
struct obd_ioctl_data data;
char rawbuf[MAX_IOC_BUFLEN], *buf = rawbuf;
glob_t path;
int count;
int fd;
int rc;
if (argc != 1)
return CMD_HELP;
memset(buf, 0, sizeof(rawbuf));
rc = cfs_get_param_paths(&path, "no_transno");
if (rc != 0)
goto old_ioctl;
fd = open(path.gl_pathv[0], O_WRONLY);
if (fd < 0) {
cfs_free_param_data(&path);
goto old_ioctl;
}
snprintf(rawbuf, sizeof(rawbuf), "%d", cur_device);
count = write(fd, rawbuf, strlen(rawbuf));
if (count < 0)
rc = errno;
cfs_free_param_data(&path);
close(fd);
if (rc)
goto old_ioctl;
return 0;
old_ioctl:
#if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(3, 0, 53, 0)
memset(&data, 0, sizeof(data));
data.ioc_dev = cur_device;
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return rc;
}
rc = l_ioctl(OBD_DEV_ID, OBD_IOC_NO_TRANSNO, buf);
if (rc < 0)
fprintf(stderr, "error: %s: %s\n", jt_cmdname(argv[0]),
strerror(rc = errno));
#endif
return rc;
}
int jt_obd_set_readonly(int argc, char **argv)
{
struct obd_ioctl_data data;
char rawbuf[MAX_IOC_BUFLEN], *buf = rawbuf;
int rc;
memset(&data, 0, sizeof(data));
data.ioc_dev = cur_device;
if (argc != 1)
return CMD_HELP;
memset(buf, 0, sizeof(rawbuf));
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return rc;
}
rc = l_ioctl(OBD_DEV_ID, OBD_IOC_SET_READONLY, buf);
if (rc < 0)
fprintf(stderr, "error: %s: %s\n", jt_cmdname(argv[0]),
strerror(rc = errno));
return rc;
}
static int obd_abort_recovery(char *cmd, enum obd_abort_recovery_flags flags)
{
struct obd_ioctl_data data = {
.ioc_dev = cur_device,
.ioc_type = flags,
};
char rawbuf[MAX_IOC_BUFLEN], *buf = rawbuf;
int rc;
memset(buf, 0, sizeof(rawbuf));
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(cmd));
return rc;
}
rc = l_ioctl(OBD_DEV_ID, OBD_IOC_ABORT_RECOVERY, buf);
if (rc < 0)
fprintf(stderr, "error: %s: %s\n", jt_cmdname(cmd),
strerror(rc = errno));
return rc;
}
int jt_obd_abort_recovery(int argc, char **argv)
{
if (argc != 1)
return CMD_HELP;
return obd_abort_recovery(argv[0], OBD_FLG_ABORT_RECOV_OST);
}
int jt_obd_abort_recovery_mdt(int argc, char **argv)
{
if (argc != 1)
return CMD_HELP;
return obd_abort_recovery(argv[0], OBD_FLG_ABORT_RECOV_MDT);
}
static int lcfg_get_nm_offset_limit(char *nodemap)
{
/* buffer to contain nodemap/<nodemap name>/offset */
char param[LUSTRE_NODEMAP_NAME_LENGTH + 16 + 1];
char *buf = NULL;
size_t buflen;
glob_t paths;
int rc;
snprintf(param, sizeof(param), "nodemap/%s/offset", nodemap);
rc = llapi_param_get_paths(param, &paths, 0);
if (rc)
return -errno;
rc = llapi_param_get_value(paths.gl_pathv[0], &buf, &buflen);
if (rc)
goto free_all;
rc = yaml_get_limit_uid(buf);
free_all:
free(buf);
llapi_param_paths_free(&paths);
return rc;
}
#else /* ! HAVE_SERVER_SUPPORT */
int jt_obd_no_transno(int argc, char **argv)
{
if (argc != 1)
return CMD_HELP;
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return -EOPNOTSUPP;
}
int jt_obd_set_readonly(int argc, char **argv)
{
if (argc != 1)
return CMD_HELP;
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return -EOPNOTSUPP;
}
int jt_obd_abort_recovery(int argc, char **argv)
{
if (argc != 1)
return CMD_HELP;
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return -EOPNOTSUPP;
}
int jt_obd_abort_recovery_mdt(int argc, char **argv)
{
if (argc != 1)
return CMD_HELP;
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return -EOPNOTSUPP;
}
#endif /* HAVE_SERVER_SUPPORT */
int jt_get_version(int argc, char **argv)
{
char version[128];
int rc;
if (argc != 1)
return CMD_HELP;
rc = llapi_get_version_string(version, sizeof(version));
if (rc)
printf("Lustre version: %s\n", LUSTRE_VERSION_STRING);
else
printf("Lustre version: %s\n", version);
return 0;
}
struct jt_fid_space {
__u64 jt_seq;
__u64 jt_id;
int jt_width;
};
int jt_obd_alloc_fids(struct jt_fid_space *space, struct lu_fid *fid,
__u64 *count)
{
int rc;
if (space->jt_seq == 0 || space->jt_id == space->jt_width) {
struct obd_ioctl_data data;
char rawbuf[MAX_IOC_BUFLEN];
char *buf = rawbuf;
__u64 seqnr;
int max_count;
memset(&data, 0, sizeof(data));
data.ioc_dev = cur_device;
data.ioc_pbuf1 = (char *)&seqnr;
data.ioc_plen1 = sizeof(seqnr);
data.ioc_pbuf2 = (char *)&max_count;
data.ioc_plen2 = sizeof(max_count);
memset(buf, 0, sizeof(rawbuf));
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "error: invalid ioctl rc = %d\n", rc);
return rc;
}
rc = l_ioctl(OBD_DEV_ID, OBD_IOC_ECHO_ALLOC_SEQ, buf);
if (rc) {
fprintf(stderr, "ioctl error: rc = %d\n", rc);
return rc;
}
space->jt_seq = *(__u64 *)data.ioc_pbuf1;
space->jt_width = *(int *)data.ioc_pbuf2;
space->jt_id = 1;
}
fid->f_seq = space->jt_seq;
fid->f_oid = space->jt_id;
fid->f_ver = 0;
space->jt_id = space->jt_id + *count;
if (space->jt_id > space->jt_width)
space->jt_id = space->jt_width;
*count = space->jt_id - fid->f_oid;
return 0;
}
#define MD_STEP_COUNT 1000
int jt_obd_md_common(int argc, char **argv, int cmd)
{
struct obd_ioctl_data data;
struct timeval start;
struct timeval end_time;
char rawbuf[MAX_IOC_BUFLEN];
char *buf = rawbuf;
int mode = 0000644;
int create_mode;
int rc = 0;
char *parent_basedir = NULL;
char dirname[4096];
int parent_base_id = 0;
int parent_count = 1;
__u64 child_base_id = -1;
int stripe_count = 0;
int stripe_index = -1;
int count = 0;
char *end;
__u64 seconds = 0;
double diff;
int c;
__u64 total_count = 0;
char *name = NULL;
struct jt_fid_space fid_space = {0};
int version = 0;
struct option long_opts[] = {
{ .val = 'b', .name = "child_base_id",
.has_arg = required_argument },
{ .val = 'c', .name = "stripe_count",
.has_arg = required_argument },
{ .val = 'd', .name = "parent_basedir",
.has_arg = required_argument },
{ .val = 'D', .name = "parent_dircount",
.has_arg = required_argument },
{ .val = 'i', .name = "stripe_index", .has_arg = required_argument },
{ .val = 'm', .name = "mode", .has_arg = required_argument },
{ .val = 'n', .name = "count", .has_arg = required_argument },
{ .val = 't', .name = "time", .has_arg = required_argument },
{ .val = 'v', .name = "version", .has_arg = no_argument },
{ .name = NULL } };
while ((c = getopt_long(argc, argv, "b:c:d:D:m:n:t:v",
long_opts, NULL)) >= 0) {
switch (c) {
case 'b':
child_base_id = strtoull(optarg, &end, 0);
if (*end) {
fprintf(stderr,
"error: %s: bad child_base_id '%s'\n",
jt_cmdname(argv[0]), optarg);
return CMD_HELP;
}
break;
case 'c':
stripe_count = strtoul(optarg, &end, 0);
if (*end) {
fprintf(stderr,
"error: %s: bad stripe count '%s'\n",
jt_cmdname(argv[0]), optarg);
return CMD_HELP;
}
break;
case 'd':
parent_basedir = optarg;
break;
case 'D':
parent_count = strtoul(optarg, &end, 0);
if (*end) {
fprintf(stderr,
"error: %s: bad parent count '%s'\n",
jt_cmdname(argv[0]), optarg);
return CMD_HELP;
}
break;
case 'i':
stripe_index = strtoul(optarg, &end, 0);
if (*end) {
fprintf(stderr,
"error: %s: bad stripe index '%s'\n",
jt_cmdname(argv[0]), optarg);
return CMD_HELP;
}
break;
case 'm':
mode = strtoul(optarg, &end, 0);
if (*end) {
fprintf(stderr, "error: %s: bad mode '%s'\n",
jt_cmdname(argv[0]), optarg);
return CMD_HELP;
}
break;
case 'n':
total_count = strtoul(optarg, &end, 0);
if (*end || total_count == 0) {
fprintf(stderr, "%s: bad child count '%s'\n",
jt_cmdname(argv[0]), optarg);
return CMD_HELP;
}
break;
case 't':
seconds = strtoull(optarg, &end, 0);
if (*end) {
fprintf(stderr, "error: %s: seconds '%s'\n",
jt_cmdname(argv[0]), optarg);
return CMD_HELP;
}
break;
case 'v':
version = 1;
break;
default:
fprintf(stderr,
"error: %s: option '%s' unrecognized\n",
argv[0], argv[optind - 1]);
return CMD_HELP;
}
}
memset(&data, 0, sizeof(data));
data.ioc_dev = cur_device;
if (child_base_id == -1) {
if (optind >= argc)
return CMD_HELP;
name = argv[optind];
total_count = 1;
} else {
if (optind < argc) {
fprintf(stderr,
"child_base_id and name can not specified at the same time\n");
return CMD_HELP;
}
}
if (stripe_count == 0 && stripe_index != -1) {
fprintf(stderr,
"If stripe_count is 0, stripe_index can not be specified\n");
return CMD_HELP;
}
if (total_count == 0 && seconds == 0) {
fprintf(stderr, "count or seconds needs to be indicated\n");
return CMD_HELP;
}
if (parent_count <= 0) {
fprintf(stderr, "parent count must < 0\n");
return CMD_HELP;
}
#ifdef MAX_THREADS
rc = shmem_setup();
if (rc)
return rc;
if (thread) {
shmem_lock();
/* threads interleave */
if (parent_base_id != -1)
parent_base_id += (thread - 1) % parent_count;
if (child_base_id != -1)
child_base_id += (thread - 1) *
(MAX_BASE_ID / nthreads);
shmem_start_time_locked();
shmem_unlock();
}
#endif
/*
* If parent directory is not specified, try to get the directory
* from name
*/
if (!parent_basedir) {
char *last_lash;
if (!name) {
fprintf(stderr,
"parent_basedir or name must be indicated!\n");
return CMD_HELP;
}
/*Get directory and name from name*/
last_lash = strrchr(name, '/');
if (!last_lash || name[0] != '/') {
fprintf(stderr, "Can not locate %s\n", name);
return CMD_HELP;
}
if (last_lash == name) {
sprintf(dirname, "%s", "/");
name++;
} else {
int namelen = (unsigned long)last_lash -
(unsigned long)name + 1;
snprintf(dirname, namelen, "%s", name);
name = last_lash + 1;
}
data.ioc_pbuf1 = dirname;
data.ioc_plen1 = strlen(dirname);
data.ioc_pbuf2 = name;
data.ioc_plen2 = strlen(name);
} else {
if (name) {
data.ioc_pbuf2 = name;
data.ioc_plen2 = strlen(name);
}
if (parent_base_id > 0)
sprintf(dirname, "%s%d", parent_basedir,
parent_base_id);
else
sprintf(dirname, "%s", parent_basedir);
data.ioc_pbuf1 = dirname;
data.ioc_plen1 = strlen(dirname);
}
if (cmd == ECHO_MD_MKDIR || cmd == ECHO_MD_RMDIR)
create_mode = S_IFDIR;
else
create_mode = S_IFREG;
data.ioc_obdo1.o_mode = mode | S_IFDIR;
data.ioc_obdo1.o_valid = OBD_MD_FLID | OBD_MD_FLTYPE | OBD_MD_FLMODE |
OBD_MD_FLFLAGS | OBD_MD_FLGROUP;
data.ioc_command = cmd;
gettimeofday(&start, NULL);
while (shmem_running()) {
struct lu_fid fid = { 0 };
if (child_base_id != -1)
data.ioc_obdo2.o_oi.oi.oi_id = child_base_id;
data.ioc_obdo2.o_mode = mode | create_mode;
data.ioc_obdo2.o_valid = OBD_MD_FLID | OBD_MD_FLTYPE |
OBD_MD_FLMODE | OBD_MD_FLFLAGS |
OBD_MD_FLGROUP;
data.ioc_obdo2.o_misc = stripe_count;
data.ioc_obdo2.o_stripe_idx = stripe_index;
if (total_count > 0) {
if ((total_count - count) > MD_STEP_COUNT)
data.ioc_count = MD_STEP_COUNT;
else
data.ioc_count = total_count - count;
} else {
data.ioc_count = MD_STEP_COUNT;
}
if (cmd == ECHO_MD_CREATE || cmd == ECHO_MD_MKDIR) {
/*Allocate fids for the create */
rc = jt_obd_alloc_fids(&fid_space, &fid,
&data.ioc_count);
if (rc) {
fprintf(stderr, "Allocate fids error %d.\n",
rc);
return rc;
}
data.ioc_obdo1.o_oi.oi_fid = fid;
}
child_base_id += data.ioc_count;
count += data.ioc_count;
memset(buf, 0, sizeof(rawbuf));
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "error: %s: invalid ioctl %d\n",
jt_cmdname(argv[0]), rc);
return rc;
}
rc = l_ioctl(OBD_DEV_ID, OBD_IOC_ECHO_MD, buf);
if (rc) {
fprintf(stderr, "error: %s: %s\n",
jt_cmdname(argv[0]), strerror(rc = errno));
return rc;
}
shmem_bump(data.ioc_count);
gettimeofday(&end_time, NULL);
diff = difftime(&end_time, &start);
if (seconds > 0 && (__u64)diff > seconds)
break;
if (count >= total_count && total_count > 0)
break;
}
if (count > 0 && version) {
gettimeofday(&end_time, NULL);
diff = difftime(&end_time, &start);
printf("%s: %d in %.3fs (%.3f /s): %s",
jt_cmdname(argv[0]), count, diff,
(double)count / diff, ctime(&end_time.tv_sec));
}
#ifdef MAX_THREADS
if (thread) {
shmem_lock();
shmem_end_time_locked();
shmem_unlock();
}
#endif
return rc;
}
int jt_obd_test_create(int argc, char **argv)
{
return jt_obd_md_common(argc, argv, ECHO_MD_CREATE);
}
int jt_obd_test_mkdir(int argc, char **argv)
{
return jt_obd_md_common(argc, argv, ECHO_MD_MKDIR);
}
int jt_obd_test_destroy(int argc, char **argv)
{
return jt_obd_md_common(argc, argv, ECHO_MD_DESTROY);
}
int jt_obd_test_rmdir(int argc, char **argv)
{
return jt_obd_md_common(argc, argv, ECHO_MD_RMDIR);
}
int jt_obd_test_lookup(int argc, char **argv)
{
return jt_obd_md_common(argc, argv, ECHO_MD_LOOKUP);
}
int jt_obd_test_setxattr(int argc, char **argv)
{
return jt_obd_md_common(argc, argv, ECHO_MD_SETATTR);
}
int jt_obd_test_md_getattr(int argc, char **argv)
{
return jt_obd_md_common(argc, argv, ECHO_MD_GETATTR);
}
int jt_obd_create(int argc, char **argv)
{
char rawbuf[MAX_IOC_BUFLEN], *buf = rawbuf;
struct obd_ioctl_data data;
struct timeval next_time;
__u64 count = 1, next_count, base_id = 1;
int verbose = 1, mode = 0100644, rc = 0, i;
char *end;
memset(&data, 0, sizeof(data));
data.ioc_dev = cur_device;
if (argc < 2 || argc > 4)
return CMD_HELP;
count = strtoull(argv[1], &end, 0);
if (*end) {
fprintf(stderr, "error: %s: invalid iteration count '%s'\n",
jt_cmdname(argv[0]), argv[1]);
return CMD_HELP;
}
if (argc > 2) {
mode = strtoul(argv[2], &end, 0);
if (*end) {
fprintf(stderr, "error: %s: invalid mode '%s'\n",
jt_cmdname(argv[0]), argv[2]);
return CMD_HELP;
}
if (!(mode & S_IFMT))
mode |= S_IFREG;
}
if (argc > 3) {
verbose = get_verbose(argv[0], argv[3]);
if (verbose == BAD_VERBOSE)
return CMD_HELP;
}
printf("%s: %jd objects\n", jt_cmdname(argv[0]), (uintmax_t)count);
gettimeofday(&next_time, NULL);
next_time.tv_sec -= verbose;
ostid_set_seq_echo(&data.ioc_obdo1.o_oi);
rc = shmem_setup();
if (rc)
return rc;
for (i = 1, next_count = verbose; i <= count && shmem_running(); i++) {
/*
* base_id is 1 so we don't need to worry about it being
* greater than OBIF_MAX_OID
*/
data.ioc_obdo1.o_oi.oi_fid.f_oid = base_id;
data.ioc_obdo1.o_mode = mode;
data.ioc_obdo1.o_uid = 0;
data.ioc_obdo1.o_gid = 0;
data.ioc_obdo1.o_projid = 0;
data.ioc_obdo1.o_valid = OBD_MD_FLTYPE | OBD_MD_FLMODE |
OBD_MD_FLID | OBD_MD_FLUID |
OBD_MD_FLGID | OBD_MD_FLGROUP |
OBD_MD_FLPROJID;
memset(buf, 0, sizeof(rawbuf));
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return rc;
}
rc = l_ioctl(OBD_DEV_ID, OBD_IOC_CREATE, buf);
llapi_ioctl_unpack(&data, buf, sizeof(rawbuf));
shmem_bump(1);
if (rc < 0) {
fprintf(stderr, "error: %s: #%d - %s\n",
jt_cmdname(argv[0]), i, strerror(rc = errno));
break;
}
if (!(data.ioc_obdo1.o_valid & OBD_MD_FLID)) {
fprintf(stderr, "error: %s: oid not valid #%d:%#jx\n",
jt_cmdname(argv[0]), i,
(uintmax_t)data.ioc_obdo1.o_valid);
rc = EINVAL;
break;
}
if (be_verbose(verbose, &next_time, i, &next_count, count))
printf("%s: #%d is object id %#jx\n",
jt_cmdname(argv[0]), i,
(uintmax_t)ostid_id(&data.ioc_obdo1.o_oi));
}
return rc;
}
int jt_obd_setattr(int argc, char **argv)
{
struct obd_ioctl_data data;
char rawbuf[MAX_IOC_BUFLEN], *buf = rawbuf;
__u64 objid;
char *end;
int mode;
int rc;
memset(&data, 0, sizeof(data));
data.ioc_dev = cur_device;
if (argc != 3)
return CMD_HELP;
objid = strtoull(argv[1], &end, 0);
if (*end) {
fprintf(stderr, "error: %s: objid '%s' is not a number\n",
jt_cmdname(argv[0]), argv[1]);
return CMD_HELP;
}
if (objid > OBIF_MAX_OID) {
fprintf(stderr, "error: %s: invalid objid '%s'\n",
jt_cmdname(argv[0]), argv[1]);
return CMD_HELP;
}
mode = strtoul(argv[2], &end, 0);
if (*end) {
fprintf(stderr, "error: %s: invalid mode '%s'\n",
jt_cmdname(argv[0]), argv[2]);
return CMD_HELP;
}
ostid_set_seq_echo(&data.ioc_obdo1.o_oi);
data.ioc_obdo1.o_mode = S_IFREG | mode;
data.ioc_obdo1.o_oi.oi_fid.f_oid = objid;
data.ioc_obdo1.o_valid = OBD_MD_FLID | OBD_MD_FLTYPE | OBD_MD_FLMODE;
memset(buf, 0, sizeof(rawbuf));
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return rc;
}
rc = l_ioctl(OBD_DEV_ID, OBD_IOC_SETATTR, buf);
if (rc < 0)
fprintf(stderr, "error: %s: %s\n", jt_cmdname(argv[0]),
strerror(rc = errno));
return rc;
}
int jt_obd_test_setattr(int argc, char **argv)
{
struct obd_ioctl_data data;
struct timeval start, next_time;
__u64 i, count, next_count;
char rawbuf[MAX_IOC_BUFLEN], *buf = rawbuf;
int verbose = 1;
__u64 objid = 3;
char *end;
int rc = 0;
if (argc < 2 || argc > 4)
return CMD_HELP;
memset(&data, 0, sizeof(data));
data.ioc_dev = cur_device;
count = strtoull(argv[1], &end, 0);
if (*end) {
fprintf(stderr, "error: %s: invalid iteration count '%s'\n",
jt_cmdname(argv[0]), argv[1]);
return CMD_HELP;
}
if (argc >= 3) {
verbose = get_verbose(argv[0], argv[2]);
if (verbose == BAD_VERBOSE)
return CMD_HELP;
}
if (argc >= 4) {
if (argv[3][0] == 't') {
objid = strtoull(argv[3] + 1, &end, 0);
if (thread)
objid += thread - 1;
} else {
objid = strtoull(argv[3], &end, 0);
}
if (*end) {
fprintf(stderr, "error: %s: invalid objid '%s'\n",
jt_cmdname(argv[0]), argv[3]);
return CMD_HELP;
}
}
gettimeofday(&start, NULL);
next_time.tv_sec = start.tv_sec - verbose;
next_time.tv_usec = start.tv_usec;
if (verbose != 0)
printf("%s: setting %jd attrs (objid %#jx): %s",
jt_cmdname(argv[0]), (uintmax_t)count,
(uintmax_t)objid, ctime(&start.tv_sec));
ostid_set_seq_echo(&data.ioc_obdo1.o_oi);
rc = shmem_setup();
if (rc)
return rc;
for (i = 1, next_count = verbose; i <= count && shmem_running(); i++) {
if (objid > OBIF_MAX_OID) {
fprintf(stderr, "errr: %s: invalid objid '%llu'\n",
jt_cmdname(argv[0]), (unsigned long long)objid);
return -E2BIG;
}
data.ioc_obdo1.o_oi.oi_fid.f_oid = objid;
data.ioc_obdo1.o_mode = S_IFREG;
data.ioc_obdo1.o_valid = OBD_MD_FLID | OBD_MD_FLTYPE |
OBD_MD_FLMODE;
memset(buf, 0, sizeof(rawbuf));
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return rc;
}
rc = l_ioctl(OBD_DEV_ID, OBD_IOC_SETATTR, &data);
shmem_bump(1);
if (rc < 0) {
fprintf(stderr, "error: %s: #%jd - %d:%s\n",
jt_cmdname(argv[0]), (uintmax_t)i,
errno, strerror(rc = errno));
break;
}
if (be_verbose(verbose, &next_time, i, &next_count, count))
printf("%s: set attr #%jd\n",
jt_cmdname(argv[0]), (uintmax_t)i);
}
if (!rc) {
struct timeval end;
double diff;
gettimeofday(&end, NULL);
diff = difftime(&end, &start);
--i;
if (verbose != 0)
printf("%s: %jd attrs in %.3fs (%.3f attr/s): %s",
jt_cmdname(argv[0]), (uintmax_t)i, diff,
i / diff, ctime(&end.tv_sec));
}
return rc;
}
int jt_obd_destroy(int argc, char **argv)
{
struct obd_ioctl_data data;
struct timeval next_time;
char rawbuf[MAX_IOC_BUFLEN], *buf = rawbuf;
__u64 count = 1, next_count;
int verbose = 1;
__u64 id;
char *end;
int rc = 0, i;
memset(&data, 0, sizeof(data));
data.ioc_dev = cur_device;
if (argc < 2 || argc > 4)
return CMD_HELP;
errno = 0;
id = strtoull(argv[1], &end, 0);
if (*end || id == 0 || errno != 0) {
fprintf(stderr, "error: %s: invalid objid '%s'\n",
jt_cmdname(argv[0]), argv[1]);
return CMD_HELP;
}
if (argc > 2) {
count = strtoull(argv[2], &end, 0);
if (*end) {
fprintf(stderr,
"error: %s: invalid iteration count '%s'\n",
jt_cmdname(argv[0]), argv[2]);
return CMD_HELP;
}
}
if (argc > 3) {
verbose = get_verbose(argv[0], argv[3]);
if (verbose == BAD_VERBOSE)
return CMD_HELP;
}
printf("%s: %jd objects\n", jt_cmdname(argv[0]), (uintmax_t)count);
gettimeofday(&next_time, NULL);
next_time.tv_sec -= verbose;
ostid_set_seq_echo(&data.ioc_obdo1.o_oi);
rc = shmem_setup();
if (rc)
return rc;
for (i = 1, next_count = verbose; i <= count && shmem_running();
i++, id++) {
if (id > OBIF_MAX_OID) {
fprintf(stderr, "errr: %s: invalid objid '%llu'\n",
jt_cmdname(argv[0]), (unsigned long long)id);
return -E2BIG;
}
data.ioc_obdo1.o_oi.oi_fid.f_oid = id;
data.ioc_obdo1.o_mode = S_IFREG | 0644;
data.ioc_obdo1.o_valid = OBD_MD_FLID | OBD_MD_FLMODE;
memset(buf, 0, sizeof(rawbuf));
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return rc;
}
rc = l_ioctl(OBD_DEV_ID, OBD_IOC_DESTROY, buf);
llapi_ioctl_unpack(&data, buf, sizeof(rawbuf));
shmem_bump(1);
if (rc < 0) {
fprintf(stderr, "error: %s: objid %#jx: %s\n",
jt_cmdname(argv[0]), (uintmax_t)id,
strerror(rc = errno));
break;
}
if (be_verbose(verbose, &next_time, i, &next_count, count))
printf("%s: #%d is object id %#jx\n",
jt_cmdname(argv[0]), i, (uintmax_t)id);
}
return rc;
}
static int jt_str_to_ost_id(const char *str, struct ost_id *oi)
{
__u64 oid;
char *end;
oid = strtoull(str, &end, 0);
if (*end == '\0') {
/* If str is a single number then assume old echo
* client usage. */
if (oid > OBIF_MAX_OID)
return -EINVAL;
ostid_set_seq_echo(oi);
oi->oi_fid.f_oid = oid;
return 0;
}
return llapi_fid_parse(str, &oi->oi_fid, NULL);
}
int jt_obd_getattr(int argc, char **argv)
{
struct obd_ioctl_data data;
struct obdo *oa;
char rawbuf[MAX_IOC_BUFLEN], *buf = rawbuf;
int rc;
if (argc != 2)
return CMD_HELP;
memset(&data, 0, sizeof(data));
data.ioc_dev = cur_device;
oa = &data.ioc_obdo1;
rc = jt_str_to_ost_id(argv[1], &oa->o_oi);
if (rc < 0) {
fprintf(stderr, "error: %s: invalid objid of FID '%s'\n",
jt_cmdname(argv[0]), argv[1]);
return CMD_HELP;
}
oa->o_valid = OBD_MD_FLID | OBD_MD_FLGROUP;
memset(buf, 0, sizeof(rawbuf));
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return rc;
}
rc = l_ioctl(OBD_DEV_ID, OBD_IOC_GETATTR, buf);
llapi_ioctl_unpack(&data, buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "error: %s: %s\n", jt_cmdname(argv[0]),
strerror(rc = errno));
return rc;
}
#define OP4(bits, name, format, value) \
do { \
if ((oa->o_valid & (bits)) == (bits)) \
printf("%s: "format"\n", (name), value); \
} while (0)
#define OPM(bits, member, format) \
OP4(bits, #member, format, (uintmax_t)(oa->o_ ## member))
#define OPO(bits, member) OPM(bits, member, "%#jo")
#define OPU(bits, member) OPM(bits, member, "%ju")
#define OPX(bits, member) OPM(bits, member, "%#jx")
OPX(0, valid);
OPX(OBD_MD_FLID | OBD_MD_FLGROUP, oi.oi.oi_id);
OPX(OBD_MD_FLID | OBD_MD_FLGROUP, oi.oi.oi_seq);
OP4(OBD_MD_FLID | OBD_MD_FLGROUP, "oi.oi_fid", DFID, PFID(&oa->o_oi.oi_fid));
OPU(OBD_MD_FLATIME, atime);
OPU(OBD_MD_FLMTIME, mtime);
OPU(OBD_MD_FLCTIME, ctime);
OPU(OBD_MD_FLSIZE, size);
OPU(OBD_MD_FLBLOCKS, blocks);
OPU(OBD_MD_FLBLKSZ, blksize);
OPO(OBD_MD_FLMODE | OBD_MD_FLTYPE, mode);
OPU(OBD_MD_FLUID, uid);
OPU(OBD_MD_FLGID, gid);
OPU(OBD_MD_FLFLAGS, flags);
OPU(OBD_MD_FLNLINK, nlink);
OPX(OBD_MD_FLPARENT | OBD_MD_FLFID, parent_seq);
OPX(OBD_MD_FLPARENT | OBD_MD_FLFID, parent_oid);
OPX(OBD_MD_FLPARENT | OBD_MD_FLFID, parent_ver);
OPU(OBD_MD_LAYOUT_VERSION, layout_version);
OPU(OBD_MD_FLGRANT, grant);
OPU(OBD_MD_FLPROJID, projid);
OPU(OBD_MD_FLDATAVERSION, data_version);
#undef OP4
#undef OPM
#undef OPO
#undef OPU
#undef OPX
return 0;
}
int jt_obd_test_getattr(int argc, char **argv)
{
struct obd_ioctl_data data;
struct timeval start, next_time;
char rawbuf[MAX_IOC_BUFLEN], *buf = rawbuf;
__u64 i, count, next_count;
int verbose = 1;
__u64 objid = 3;
char *end;
int rc = 0;
if (argc < 2 || argc > 4)
return CMD_HELP;
memset(&data, 0, sizeof(data));
data.ioc_dev = cur_device;
count = strtoull(argv[1], &end, 0);
if (*end) {
fprintf(stderr, "error: %s: invalid iteration count '%s'\n",
jt_cmdname(argv[0]), argv[1]);
return CMD_HELP;
}
if (argc >= 3) {
verbose = get_verbose(argv[0], argv[2]);
if (verbose == BAD_VERBOSE)
return CMD_HELP;
}
if (argc >= 4) {
if (argv[3][0] == 't') {
objid = strtoull(argv[3] + 1, &end, 0);
if (thread)
objid += thread - 1;
} else {
objid = strtoull(argv[3], &end, 0);
}
if (*end) {
fprintf(stderr, "error: %s: invalid objid '%s'\n",
jt_cmdname(argv[0]), argv[3]);
return CMD_HELP;
}
}
gettimeofday(&start, NULL);
next_time.tv_sec = start.tv_sec - verbose;
next_time.tv_usec = start.tv_usec;
if (verbose != 0)
printf("%s: getting %jd attrs (objid %#jx): %s",
jt_cmdname(argv[0]), (uintmax_t)count,
(uintmax_t)objid, ctime(&start.tv_sec));
ostid_set_seq_echo(&data.ioc_obdo1.o_oi);
rc = shmem_setup();
if (rc)
return rc;
for (i = 1, next_count = verbose; i <= count && shmem_running(); i++) {
if (objid > OBIF_MAX_OID) {
fprintf(stderr, "errr: %s: invalid objid '%llu'\n",
jt_cmdname(argv[0]), (unsigned long long)objid);
return -E2BIG;
}
data.ioc_obdo1.o_oi.oi_fid.f_oid = objid;
data.ioc_obdo1.o_mode = S_IFREG;
data.ioc_obdo1.o_valid = 0xffffffff;
memset(buf, 0, sizeof(rawbuf));
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return rc;
}
rc = l_ioctl(OBD_DEV_ID, OBD_IOC_GETATTR, &data);
shmem_bump(1);
if (rc < 0) {
fprintf(stderr, "error: %s: #%jd - %d:%s\n",
jt_cmdname(argv[0]), (uintmax_t)i,
errno, strerror(rc = errno));
break;
}
if (be_verbose(verbose, &next_time, i, &next_count, count))
printf("%s: got attr #%jd\n",
jt_cmdname(argv[0]), (uintmax_t)i);
}
if (!rc) {
struct timeval end;
double diff;
gettimeofday(&end, NULL);
diff = difftime(&end, &start);
--i;
if (verbose != 0)
printf("%s: %jd attrs in %.3fs (%.3f attr/s): %s",
jt_cmdname(argv[0]), (uintmax_t)i, diff,
i / diff, ctime(&end.tv_sec));
}
return rc;
}
/*
* test_brw <cnt> count
* <r|w[r(repeat)x(noverify)]> mode
* <q|v|#(print interval)> verbosity
* <npages[+offset]> blocksize
* <[[<interleave_threads>]t(inc obj by thread#)]obj> object
* [p|g<args>] batch
*/
int jt_obd_test_brw(int argc, char **argv)
{
struct obd_ioctl_data data;
struct timeval start, next_time;
char rawbuf[MAX_IOC_BUFLEN], *buf = rawbuf;
__u64 count, next_count, len, thr_offset = 0, objid = 3;
int write = 0, verbose = 1, cmd, i, rc = 0, pages = 1;
int offset_pages = 0;
long n;
int repeat_offset = 0;
unsigned long long ull;
int verify = 1;
char *end;
__u64 stride __attribute__ ((unused));
int nthr_per_obj __attribute__ ((unused)) = 0;
int obj_idx __attribute__ ((unused)) = 0;
if (argc < 2 || argc > 7) {
fprintf(stderr, "error: %s: bad number of arguments: %d\n",
jt_cmdname(argv[0]), argc);
return CMD_HELP;
}
count = strtoull(argv[1], &end, 0);
if (*end) {
fprintf(stderr, "error: %s: bad iteration count '%s'\n",
jt_cmdname(argv[0]), argv[1]);
return CMD_HELP;
}
if (argc >= 3) {
if (argv[2][0] == 'w' || argv[2][0] == '1')
write = 1;
/* else it's a read */
if (argv[2][0] != 0)
for (i = 1; argv[2][i] != 0; i++)
switch (argv[2][i]) {
case 'r':
repeat_offset = 1;
break;
case 'x':
verify = 0;
break;
default:
fprintf(stderr,
"Can't parse cmd '%s'\n",
argv[2]);
return CMD_HELP;
}
}
if (argc >= 4) {
verbose = get_verbose(argv[0], argv[3]);
if (verbose == BAD_VERBOSE)
return CMD_HELP;
}
if (argc >= 5) {
pages = strtoul(argv[4], &end, 0);
if (*end == '+')
offset_pages = strtoul(end + 1, &end, 0);
if (*end != 0 || offset_pages < 0 || offset_pages >= pages) {
fprintf(stderr, "error: %s: bad npages[+offset] parameter '%s'\n",
jt_cmdname(argv[0]), argv[4]);
return CMD_HELP;
}
}
if (argc >= 6) {
if (thread && (n = strtol(argv[5], &end, 0)) > 0 &&
*end == 't' && (ull = strtoull(end + 1, &end, 0)) > 0 &&
*end == 0) {
nthr_per_obj = n;
objid = ull;
} else if (thread && argv[5][0] == 't') {
nthr_per_obj = 1;
objid = strtoull(argv[5] + 1, &end, 0);
} else {
nthr_per_obj = 0;
objid = strtoull(argv[5], &end, 0);
}
if (*end) {
fprintf(stderr, "error: %s: bad objid '%s'\n",
jt_cmdname(argv[0]), argv[5]);
return CMD_HELP;
}
}
memset(&data, 0, sizeof(data));
data.ioc_dev = cur_device;
/*
* communicate the 'type' of brw test and batching to echo_client.
* don't start. we'd love to refactor this lctl->echo_client
* interface
*/
data.ioc_pbuf1 = (void *)1;
data.ioc_plen1 = 1;
if (argc >= 7) {
switch (argv[6][0]) {
case 'g': /* plug and unplug */
data.ioc_pbuf1 = (void *)2;
data.ioc_plen1 = strtoull(argv[6] + 1, &end, 0);
break;
case 'p': /* prep and commit */
data.ioc_pbuf1 = (void *)3;
data.ioc_plen1 = strtoull(argv[6] + 1, &end, 0);
break;
default:
fprintf(stderr,
"error: %s: batching '%s' needs to specify 'p' or 'g'\n",
jt_cmdname(argv[0]), argv[6]);
return CMD_HELP;
}
if (*end) {
fprintf(stderr, "error: %s: bad batching '%s'\n",
jt_cmdname(argv[0]), argv[6]);
return CMD_HELP;
}
data.ioc_plen1 *= getpagesize();
}
len = pages * getpagesize();
thr_offset = offset_pages * getpagesize();
stride = len;
#ifdef MAX_THREADS
rc = shmem_setup();
if (rc)
return rc;
if (thread) {
shmem_lock();
if (nthr_per_obj != 0) {
/* threads interleave */
obj_idx = (thread - 1) / nthr_per_obj;
objid += obj_idx;
stride *= nthr_per_obj;
if ((thread - 1) % nthr_per_obj == 0) {
shared_data->body.offsets[obj_idx] =
stride + thr_offset;
}
thr_offset += ((thread - 1) % nthr_per_obj) * len;
} else {
/* threads disjoint */
thr_offset += (thread - 1) * len;
}
shmem_start_time_locked();
shmem_unlock();
}
#endif
ostid_set_seq_echo(&data.ioc_obdo1.o_oi);
if (objid > OBIF_MAX_OID) {
fprintf(stderr, "errr: %s: invalid objid '%llu'\n",
jt_cmdname(argv[0]), (unsigned long long)objid);
return -E2BIG;
}
data.ioc_obdo1.o_oi.oi_fid.f_oid = objid;
data.ioc_obdo1.o_mode = S_IFREG;
data.ioc_obdo1.o_valid = OBD_MD_FLID | OBD_MD_FLTYPE | OBD_MD_FLMODE |
OBD_MD_FLFLAGS | OBD_MD_FLGROUP;
data.ioc_obdo1.o_flags = (verify ? OBD_FL_DEBUG_CHECK : 0);
data.ioc_count = len;
data.ioc_offset = (repeat_offset ? 0 : thr_offset);
gettimeofday(&start, NULL);
next_time.tv_sec = start.tv_sec - verbose;
next_time.tv_usec = start.tv_usec;
if (verbose != 0)
printf("%s: %s %jux%d pages (obj %#jx, off %ju): %s",
jt_cmdname(argv[0]), write ? "writing" : "reading",
(uintmax_t)count, pages, (uintmax_t)objid,
(uintmax_t)data.ioc_offset, ctime(&start.tv_sec));
cmd = write ? OBD_IOC_BRW_WRITE : OBD_IOC_BRW_READ;
for (i = 1, next_count = verbose; i <= count && shmem_running(); i++) {
data.ioc_obdo1.o_valid &= ~(OBD_MD_FLBLOCKS | OBD_MD_FLGRANT);
memset(buf, 0, sizeof(rawbuf));
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return rc;
}
rc = l_ioctl(OBD_DEV_ID, cmd, buf);
shmem_bump(1);
if (rc) {
fprintf(stderr, "error: %s: #%d - %s on %s\n",
jt_cmdname(argv[0]), i, strerror(rc = errno),
write ? "write" : "read");
break;
} else if (be_verbose(verbose, &next_time, i,
&next_count, count)) {
shmem_lock();
printf("%s: %s number %d @ %jd:%ju for %d\n",
jt_cmdname(argv[0]), write ? "write" : "read", i,
(uintmax_t)ostid_id(&data.ioc_obdo1.o_oi),
(uintmax_t)data.ioc_offset,
(int)(pages * getpagesize()));
shmem_unlock();
}
if (!repeat_offset) {
#ifdef MAX_THREADS
if (stride == len) {
data.ioc_offset += stride;
} else if (i < count) {
shmem_lock();
data.ioc_offset =
shared_data->body.offsets[obj_idx];
shared_data->body.offsets[obj_idx] += len;
shmem_unlock();
}
#else
data.ioc_offset += len;
obj_idx = 0; /* avoids an unused var warning */
#endif
}
}
if (!rc) {
struct timeval end;
double diff;
gettimeofday(&end, NULL);
diff = difftime(&end, &start);
--i;
if (verbose != 0)
printf("%s: %s %dx%d pages in %.3fs (%.3f MB/s): %s",
jt_cmdname(argv[0]), write ? "wrote" : "read",
i, pages, diff,
((double)i * pages * getpagesize()) /
(diff * 1048576.0), ctime(&end.tv_sec));
}
#ifdef MAX_THREADS
if (thread) {
shmem_lock();
shmem_end_time_locked();
shmem_unlock();
}
#endif
return rc;
}
static int do_activate(int argc, char **argv, int flag)
{
struct obd_ioctl_data data;
char rawbuf[MAX_IOC_BUFLEN], *buf = rawbuf;
int rc;
memset(&data, 0, sizeof(data));
data.ioc_dev = cur_device;
if (argc != 1)
return CMD_HELP;
/* reuse offset for 'active' */
data.ioc_offset = flag;
memset(buf, 0, sizeof(rawbuf));
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return rc;
}
rc = llapi_ioctl_dev(OBD_DEV_ID, OBD_IOC_SET_ACTIVE, buf);
if (rc)
fprintf(stderr, "error: %s: failed: %s\n",
jt_cmdname(argv[0]), strerror(rc = errno));
return rc;
}
/**
* jt_replace_nids() - Replace nids for given device.
* @argc: number of args
* @argv: variable string arguments
*
* lctl replace_nids <devicename> <nid1>[,nid2,nid3]
* Command should be started on MGS server.
*
* Only MGS server should be started (command execution
* returns error in another cases). Command mount
* -t lustre <MDT partition> -o nosvc <mount point>
* can be used for that.
*
* llogs for MDTs and clients are processed. All
* records copied as is except add_uuid and setup. This records
* are skipped and recorded with new nids and uuid.
*
* see mgs_replace_nids
* see mgs_replace_log
* see mgs_replace_nids_handler
*
* Return 0 on success or %negative on error
*/
int jt_replace_nids(int argc, char **argv)
{
int rc;
char rawbuf[MAX_IOC_BUFLEN], *buf = rawbuf;
struct obd_ioctl_data data;
memset(&data, 0, sizeof(data));
rc = data.ioc_dev = get_mgs_device();
if (rc < 0)
return rc;
if (argc != 3)
return CMD_HELP;
data.ioc_inllen1 = strlen(argv[1]) + 1;
data.ioc_inlbuf1 = argv[1];
data.ioc_inllen2 = strlen(argv[2]) + 1;
data.ioc_inlbuf2 = argv[2];
memset(buf, 0, sizeof(rawbuf));
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return rc;
}
rc = l_ioctl(OBD_DEV_ID, OBD_IOC_REPLACE_NIDS, buf);
if (rc < 0) {
fprintf(stderr, "error: %s: %s\n", jt_cmdname(argv[0]),
strerror(rc = errno));
}
return rc;
}
int jt_obd_deactivate(int argc, char **argv)
{
return do_activate(argc, argv, 0);
}
int jt_obd_activate(int argc, char **argv)
{
return do_activate(argc, argv, 1);
}
int jt_obd_recover(int argc, char **argv)
{
char rawbuf[MAX_IOC_BUFLEN], *buf = rawbuf;
struct obd_ioctl_data data;
int rc;
memset(&data, 0, sizeof(data));
data.ioc_dev = cur_device;
if (argc > 2)
return CMD_HELP;
if (argc == 2) {
data.ioc_inllen1 = strlen(argv[1]) + 1;
data.ioc_inlbuf1 = argv[1];
}
memset(buf, 0, sizeof(rawbuf));
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return rc;
}
rc = l_ioctl(OBD_DEV_ID, OBD_IOC_CLIENT_RECOVER, buf);
if (rc < 0) {
fprintf(stderr, "error: %s: %s\n", jt_cmdname(argv[0]),
strerror(rc = errno));
}
return rc;
}
int jt_obd_mdc_lookup(int argc, char **argv)
{
struct obd_ioctl_data data;
char rawbuf[MAX_IOC_BUFLEN], *buf = rawbuf;
char *parent, *child;
int rc, fd, verbose = 1;
if (argc < 3 || argc > 4)
return CMD_HELP;
parent = argv[1];
child = argv[2];
if (argc == 4)
verbose = get_verbose(argv[0], argv[3]);
memset(&data, 0, sizeof(data));
data.ioc_dev = cur_device;
data.ioc_inllen1 = strlen(child) + 1;
data.ioc_inlbuf1 = child;
memset(buf, 0, sizeof(rawbuf));
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return rc;
}
fd = open(parent, O_RDONLY);
if (fd < 0) {
fprintf(stderr, "open \"%s\" failed: %s\n", parent,
strerror(errno));
return -1;
}
rc = ioctl(fd, IOC_MDC_LOOKUP, buf);
if (rc < 0) {
fprintf(stderr, "error: %s: ioctl error: %s\n",
jt_cmdname(argv[0]), strerror(rc = errno));
}
close(fd);
if (verbose) {
rc = llapi_ioctl_unpack(&data, buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "error: %s: invalid reply\n",
jt_cmdname(argv[0]));
return rc;
}
printf("%s: mode %o uid %d gid %d\n",
child, data.ioc_obdo1.o_mode, data.ioc_obdo1.o_uid,
data.ioc_obdo1.o_gid);
}
return rc;
}
#ifdef HAVE_SERVER_SUPPORT
/**
* jt_lcfg_clear() - Clear config logs for given device or filesystem.
* @argc: number of args
* @argv: variable string arguments
*
* lctl clear_conf <devicename|fsname>
* Command has to be run on MGS node having MGS device mounted with -o
* nosvc.
*
* Configuration logs for filesystem or one particular log is
* processed. New log is created, original log is read, its records
* marked SKIP do not get copied to new log. Others are copied as-is.
* Original file is renamed to log.${time}.bak.
*
* see mgs_clear_configs
* see mgs_replace_log
* see mgs_clear_config_handler
*
* Return 0 on success or %negative on error
*/
int jt_lcfg_clear(int argc, char **argv)
{
int rc;
char rawbuf[MAX_IOC_BUFLEN], *buf = rawbuf;
struct obd_ioctl_data data;
memset(&data, 0, sizeof(data));
rc = data.ioc_dev = get_mgs_device();
if (rc < 0)
return rc;
if (argc != 2)
return CMD_HELP;
data.ioc_inllen1 = strlen(argv[1]) + 1;
data.ioc_inlbuf1 = argv[1];
memset(buf, 0, sizeof(rawbuf));
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return rc;
}
rc = l_ioctl(OBD_DEV_ID, OBD_IOC_CLEAR_CONFIGS, buf);
if (rc < 0) {
fprintf(stderr, "error: %s: %s\n", jt_cmdname(argv[0]),
strerror(rc = errno));
}
return rc;
}
int jt_lcfg_fork(int argc, char **argv)
{
struct obd_ioctl_data data;
char rawbuf[MAX_IOC_BUFLEN], *buf = rawbuf;
int rc;
if (argc != 3)
return CMD_HELP;
memset(&data, 0, sizeof(data));
rc = data.ioc_dev = get_mgs_device();
if (rc < 0)
return rc;
data.ioc_inllen1 = strlen(argv[1]) + 1;
data.ioc_inlbuf1 = argv[1];
data.ioc_inllen2 = strlen(argv[2]) + 1;
data.ioc_inlbuf2 = argv[2];
memset(buf, 0, sizeof(rawbuf));
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return rc;
}
rc = l_ioctl(OBD_DEV_ID, OBD_IOC_LCFG_FORK, buf);
if (rc < 0)
fprintf(stderr, "error: %s: OBD_IOC_LCFG_FORK failed: %s\n",
jt_cmdname(argv[0]), strerror(errno));
return rc;
}
static int jt_llog_print_iter(char *logname, long start, long end,
int (record_cb)(char *record, void *private),
void *private, bool reverse, bool raw);
struct erase_callback_data {
char *ecd_fsname;
int ecd_count;
int ecd_error;
int ecd_quiet;
};
static int erase_param_cb(char *record, void *cb_data)
{
struct erase_callback_data *ecd = cb_data;
struct obd_ioctl_data data = { 0 };
char rawbuf[MAX_IOC_BUFLEN] = "", *buf = rawbuf;
char *param_name, *end;
char *index;
int rc;
/* Parameter records look like:
* - { index: 14, event: set_param, device: general,
* parameter: jobid_var, value: nodelocal }
* - { index: 23, event: set_param, device: general,
* parameter: jobid_name, value: %H:%e:%u }
* - { index: 32, event: set_param, device: general,
* parameter: osc.myth-OST*.grant_shrink_interval, value: 120 }
*
* Skip records that aren't set_param events
*/
if (!strstr(record, "event: set_param"))
return 0;
param_name = strstr(record, "parameter: ");
if (!param_name)
return 0;
param_name += strlen("parameter: ");
/* Find the end of the parameter name */
end = strstr(param_name, ",");
if (!end)
return 0;
*end = '\0';
/* Check if fsname appears anywhere in the parameter name */
if (!strstr(param_name, ecd->ecd_fsname))
return 0;
index = strstr(record, "index: ");
if (!index)
return 0;
index += strlen("index: ");
end = strstr(index, ",");
*end = '\0';
/* Found a matching parameter, prepare to cancel it */
data.ioc_dev = get_mgs_device();
data.ioc_inlbuf1 = "params";
data.ioc_inllen1 = strlen("params") + 1; /* catalog name */
data.ioc_inlbuf3 = index;
data.ioc_inllen3 = strlen(index) + 1; /* index to cancel */
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "failed to pack param '%s': %s\n",
param_name, strerror(-rc));
if (!ecd->ecd_error)
ecd->ecd_error = rc;
return rc;
}
rc = l_ioctl(OBD_DEV_ID, OBD_IOC_LLOG_CANCEL, buf);
if (rc) {
if (!ecd->ecd_quiet)
fprintf(stderr, "failed to cancel param '%s': %s\n",
param_name, strerror(errno));
if (!ecd->ecd_error)
ecd->ecd_error = rc;
return rc;
}
if (!ecd->ecd_quiet)
printf("- cancelled param '%s'\n", param_name);
ecd->ecd_count++;
return 0;
}
/* erase the configuration log for the named filesystem and all persistent
* params that contain the fsname from the "params" log on the MGS.
*/
int jt_lcfg_erase(int argc, char **argv)
{
struct obd_ioctl_data data = { 0 };
char rawbuf[MAX_IOC_BUFLEN] = "", *buf = rawbuf;
struct erase_callback_data ecd = { 0 };
char *fsname = NULL;
char *cmd = argv[0];
bool quiet = false;
int rc, c;
static const struct option long_opts[] = {
{ .val = 'h', .name = "help", .has_arg = no_argument },
{ .val = 'q', .name = "quiet", .has_arg = no_argument },
{ .name = NULL }
};
optind = 0;
while ((c = getopt_long(argc, argv, "qh", long_opts, NULL)) != -1) {
switch (c) {
case 'q':
quiet = true;
break;
case 'h':
default:
return CMD_HELP;
}
}
if (optind >= argc) {
fprintf(stderr, "%s: missing fsname\n", argv[0]);
return CMD_HELP;
}
fsname = argv[optind];
if (!quiet)
printf("%s: erasing config log and params for fsname '%s'\n",
jt_cmdname(cmd), fsname);
/* First do the normal lcfg_erase operation */
rc = data.ioc_dev = get_mgs_device();
if (rc < 0)
return rc;
data.ioc_inllen1 = strlen(fsname) + 1;
data.ioc_inlbuf1 = fsname;
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "%s: ioctl_pack failed: %s\n",
jt_cmdname(cmd), strerror(-rc));
goto out;
}
rc = l_ioctl(OBD_DEV_ID, OBD_IOC_LCFG_ERASE, buf);
if (rc < 0) {
fprintf(stderr, "%s: OBD_IOC_LCFG_ERASE failed: %s\n",
jt_cmdname(cmd), strerror(-rc));
goto out;
}
/* Now handle the params configuration log */
ecd.ecd_fsname = fsname;
ecd.ecd_quiet = quiet;
rc = jt_llog_print_iter("params", 1, -1, erase_param_cb, &ecd,
false, false);
if (rc) {
fprintf(stderr, "%s: failed params log iteration: %s\n",
jt_cmdname(cmd), strerror(-rc));
goto out;
}
if (ecd.ecd_error)
rc = ecd.ecd_error;
if (!quiet) /* below message is checked in conf-sanity.sh test_250 */
printf("%s: erased %d parameter%s matching fsname '%s'\n",
jt_cmdname(cmd), ecd.ecd_count,
ecd.ecd_count > 1 ? "s" : "", fsname);
out:
return rc;
}
#else /* !HAVE_SERVER_SUPPORT */
int jt_lcfg_clear(int argc, char **argv)
{
if (argc != 2)
return CMD_HELP;
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return -EOPNOTSUPP;
}
int jt_lcfg_fork(int argc, char **argv)
{
if (argc != 3)
return CMD_HELP;
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return -EOPNOTSUPP;
}
int jt_lcfg_erase(int argc, char **argv)
{
if (argc != 3)
return CMD_HELP;
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return -EOPNOTSUPP;
}
#endif /* HAVE_SERVER_SUPPORT */
enum llog_default_dev_op {
LLOG_DFLT_MGS_SET = 0,
LLOG_DFLT_DEV_RESET
};
static int llog_default_device(enum llog_default_dev_op op)
{
int rc = 0;
static int dflt_dev = -1;
if (op == LLOG_DFLT_MGS_SET && (cur_device == -1)) {
char mgs[] = "$MGS";
rc = do_device("llog_default_device", mgs);
dflt_dev = cur_device;
} else if (op == LLOG_DFLT_DEV_RESET && (dflt_dev != -1)) {
do_disconnect(NULL, 1);
dflt_dev = -1;
}
return rc;
}
static int llog_catlist_next(int index, char *buf, size_t buflen)
{
struct obd_ioctl_data data;
int rc;
memset(&data, 0, sizeof(data));
data.ioc_dev = cur_device;
data.ioc_inllen1 = buflen - __ALIGN_KERNEL(sizeof(data), 8);
data.ioc_count = index;
memset(buf, 0, buflen);
rc = llapi_ioctl_pack(&data, &buf, buflen);
if (rc < 0) {
fprintf(stderr, "error: invalid llapi_ioctl_pack: %s\n",
strerror(errno));
return rc;
}
rc = l_ioctl(OBD_DEV_ID, OBD_IOC_CATLOGLIST, buf);
if (rc < 0) {
fprintf(stderr, "OBD_IOC_CATLOGLIST failed: %s\n",
strerror(errno));
return rc;
}
return ((struct obd_ioctl_data *)buf)->ioc_count;
}
int jt_llog_catlist(int argc, char **argv)
{
char rawbuf[MAX_IOC_BUFLEN], *buf = rawbuf;
char *tmp = NULL;
int start = 0;
if (argc != 1)
return CMD_HELP;
if (llog_default_device(LLOG_DFLT_MGS_SET))
return CMD_INCOMPLETE;
do {
start = llog_catlist_next(start, rawbuf, sizeof(rawbuf));
if (start < 0)
break;
tmp = ((struct obd_ioctl_data *)buf)->ioc_bulk;
if (strlen(tmp) > 0)
fprintf(stdout, "%s", tmp);
else
break;
} while (start);
llog_default_device(LLOG_DFLT_DEV_RESET);
return start;
}
int jt_llog_info(int argc, char **argv)
{
const struct option long_opts[] = {
/* Allow optional "--catalog" for compatibility with llog commands. */
{ .val = 'c', .name = "catalog", .has_arg = required_argument },
{ .val = 'h', .name = "help", .has_arg = no_argument },
{ .name = NULL } };
struct obd_ioctl_data data = { 0 };
char rawbuf[MAX_IOC_BUFLEN] = "", *buf = rawbuf;
char *cmd = argv[0];
char *catalog = NULL;
int rc, c;
while ((c = getopt_long(argc, argv, "c:h", long_opts, NULL)) != -1) {
switch (c) {
case 'c':
catalog = optarg;
break;
case 'h':
default:
return CMD_HELP;
}
}
argc -= optind;
argv += optind;
/* support "logname" positional parameter */
if (argc == 1) {
if (catalog) {
fprintf(stderr,
"%s: catalog is set, unknown argument '%s'\n",
cmd, optarg);
return CMD_HELP;
}
catalog = argv[0];
} else if (!catalog || argc > 1) {
return CMD_HELP;
}
if (llog_default_device(LLOG_DFLT_MGS_SET))
return CMD_INCOMPLETE;
data.ioc_dev = cur_device;
data.ioc_inllen1 = strlen(catalog) + 1; /* catalog name */
data.ioc_inlbuf1 = catalog;
data.ioc_inllen2 = sizeof(rawbuf) - __ALIGN_KERNEL(sizeof(data), 8) -
__ALIGN_KERNEL(data.ioc_inllen1, 8);
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "%s: ioctl_pack failed for catalog '%s': %s\n",
jt_cmdname(cmd), catalog, strerror(-rc));
goto err;
}
rc = l_ioctl(OBD_DEV_ID, OBD_IOC_LLOG_INFO, buf);
if (rc == 0)
fprintf(stdout, "%s", ((struct obd_ioctl_data *)buf)->ioc_bulk);
else
fprintf(stderr, "%s: OBD_IOC_LLOG_INFO failed: %s\n",
jt_cmdname(cmd), strerror(errno));
err:
llog_default_device(LLOG_DFLT_DEV_RESET);
return rc;
}
int jt_llog_print_cb(char *record, void *private)
{
printf("%s\n", record);
return 0;
}
static int
llog_process_records(int (record_cb)(char *record, void *private),
char *record, void *private, bool reverse)
{
char *ptr = NULL;
char *tmp = NULL;
int rc = 0;
if (!reverse) {
do {
ptr = strchr(record, '\n');
if (ptr)
*ptr = '\0';
rc = record_cb(record, private);
if (rc)
goto out;
if (ptr)
record = ptr + 1;
} while (ptr && *(ptr + 1));
} else {
tmp = record;
ptr = strrchr(record, '\n');
if (ptr)
*ptr = '\0';
else
goto out;
while ((ptr = strrchr(record, '\n'))) {
tmp = ptr + 1;
*ptr = '\0';
rc = record_cb(tmp, private);
if (rc)
goto out;
};
rc = record_cb(record, private);
if (rc)
goto out;
}
out:
return rc;
}
/**
* jt_llog_print_iter() - Iterate over llog records, typically YAML-formatted
* configuration logs
* @logname: name of llog file or FID
* @start: first record to process
* @end: last record to process (inclusive)
* @record_cb: callback for records. Return -ve error, or +ve abort.
* @private: private data passed to the @record_cb function [in, out]
* @reverse: print the llog records from the beginning or the end
* @raw: raw log
*
* Return 0 on success (others handled by the caller)
*/
static int jt_llog_print_iter(char *logname, long start, long end,
int (record_cb)(char *record, void *private),
void *private, bool reverse, bool raw)
{
struct obd_ioctl_data data = { 0 };
char rawbuf[MAX_IOC_BUFLEN], *buf = rawbuf;
char startbuf[16], endbuf[16];
static long inc = sizeof(rawbuf) / 128;
long rec;
int rc = 0;
/* default end of indexes is max indexes in a llog bitmap */
if (end == -1)
end = LLOG_MIN_CHUNK_SIZE * 8 - 1;
data.ioc_dev = cur_device;
data.ioc_inlbuf1 = logname;
data.ioc_inllen1 = strlen(logname) + 1; /* log name */
/*
* Estimate about 128 characters per configuration record. Not all
* records will be printed in any case, so they should easily fit. If
* not, the kernel will return -EOVERFLOW and ask for fewer records.
*
* We don't want to request records from the kernel one-at-a-time, as
* it restarts the config llog iteration from the beginning, so we
* fetch multiple records from the kernel per call and split locally.
*/
for (rec = start; rec < end; rec += inc) {
char *record = ((struct obd_ioctl_data *)buf)->ioc_bulk;
__u32 *is_llog_eof = &((struct obd_ioctl_data *)buf)->ioc_u32_2;
retry:
snprintf(startbuf, sizeof(startbuf), "%lu", rec);
snprintf(endbuf, sizeof(endbuf), "%lu",
end < rec + inc - 1 ? end : rec + inc - 1);
data.ioc_u32_1 = raw ? 1 : 0;
/* start and end record numbers are passed as ASCII digits */
data.ioc_inlbuf2 = startbuf;
data.ioc_inllen2 = strlen(startbuf) + 1;
data.ioc_inlbuf3 = endbuf;
data.ioc_inllen3 = strlen(endbuf) + 1;
data.ioc_inllen4 = sizeof(rawbuf) -
__ALIGN_KERNEL(sizeof(data), 8) -
__ALIGN_KERNEL(data.ioc_inllen1, 8) -
__ALIGN_KERNEL(data.ioc_inllen2, 8) -
__ALIGN_KERNEL(data.ioc_inllen3, 8);
memset(buf, 0, sizeof(rawbuf));
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "%s: invalid ioctl data\n", logname);
goto out;
}
rc = l_ioctl(OBD_DEV_ID, OBD_IOC_LLOG_PRINT, buf);
if (rc == -EOVERFLOW && inc > 2) {
inc /= 2;
goto retry;
}
if (rc) {
fprintf(stderr, "%s: OBD_IOC_LLOG_PRINT failed: %s\n",
logname, strerror(errno));
rc = -errno;
goto out;
}
/* record was not modified -> all indexes are skipped */
if (strcmp(record, logname) != 0)
rc = llog_process_records(record_cb, record, private,
reverse);
if (rc)
goto out;
/* end of llog file ? */
if (*is_llog_eof)
break;
}
out:
return rc;
}
static int llog_parse_catalog_options(int *argc, char ***argv, char **catalog,
long *start, long *end, int *raw)
{
const struct option long_opts[] = {
/* the --catalog option is not required, just for consistency */
{ .val = 'c', .name = "catalog", .has_arg = required_argument },
{ .val = 'e', .name = "end", .has_arg = required_argument },
{ .val = 'h', .name = "help", .has_arg = no_argument },
{ .val = 'r', .name = "raw", .has_arg = no_argument },
{ .val = 's', .name = "start", .has_arg = required_argument },
{ .name = NULL } };
char *cmd = (*argv)[0];
char *endp;
int c;
if (!catalog || !start || !end)
return -EINVAL;
/* now process command line arguments*/
while ((c = getopt_long(*argc, *argv, "c:e:hrs:",
long_opts, NULL)) != -1) {
switch (c) {
case 'c':
*catalog = optarg;
break;
case 'e':
*end = strtol(optarg, &endp, 0);
if (*endp != '\0') {
fprintf(stderr, "%s: bad end value '%s'\n",
cmd, optarg);
return CMD_HELP;
}
break;
case 'r':
if (!raw)
return CMD_HELP;
*raw = 1;
break;
case 's':
*start = strtol(optarg, &endp, 0);
if (*endp != '\0') {
fprintf(stderr, "%s: bad start value '%s'\n",
cmd, optarg);
return CMD_HELP;
}
break;
case 'h':
default:
return CMD_HELP;
}
}
*argc -= optind;
*argv += optind;
/*
* support old optional positional parameters only if they were
* not already specified with named arguments: logname [start [end]]
*/
if (*argc >= 1) {
if (*catalog) {
fprintf(stderr,
"%s: logname is set, unknown argument '%s'\n",
cmd, (*argv)[0]);
return CMD_HELP;
}
*catalog = (*argv)[0];
(*argc)--;
(*argv)++;
}
if (*catalog == NULL) {
fprintf(stderr, "%s: no logname specified\n", cmd);
return CMD_HELP;
}
if (*argc >= 1) {
if (*start != 1) {
fprintf(stderr,
"%s: --start is set, unknown argument '%s'\n",
cmd, (*argv)[0]);
return CMD_HELP;
}
*start = strtol((*argv)[0], &endp, 0);
if (*endp != '\0') {
fprintf(stderr, "%s: bad start value '%s'\n",
cmd, (*argv)[0]);
return CMD_HELP;
}
(*argc)--;
(*argv)++;
}
if (*argc >= 1) {
if (*end != -1) {
fprintf(stderr,
"%s: --end is set, unknown argument '%s'\n",
cmd, (*argv)[0]);
return CMD_HELP;
}
*end = strtol((*argv)[0], &endp, 0);
if (*endp != '\0') {
fprintf(stderr, "%s: bad end value '%s'\n",
cmd, (*argv)[0]);
return CMD_HELP;
}
(*argc)--;
(*argv)++;
}
if (*argc > 1) {
fprintf(stderr, "%s: unknown argument '%s'\n", cmd, (*argv)[0]);
return CMD_HELP;
}
if (*end != -1 && *end < *start) {
fprintf(stderr, "%s: end '%lu' less than than start '%lu'\n",
cmd, *end, *start);
return CMD_HELP;
}
return 0;
}
int jt_llog_print(int argc, char **argv)
{
char *catalog = NULL;
long start = 1, end = -1;
int raw = 0;
int rc;
rc = llog_parse_catalog_options(&argc, &argv, &catalog, &start, &end,
&raw);
if (rc)
return rc;
if (llog_default_device(LLOG_DFLT_MGS_SET))
return CMD_INCOMPLETE;
rc = jt_llog_print_iter(catalog, start, end, jt_llog_print_cb,
NULL, false, !!raw);
llog_default_device(LLOG_DFLT_DEV_RESET);
return rc;
}
/*
* Parse catalog, log ID, and optionally a log index with either optional
* arguments or positional arguments. Only the initial catalog argument
* may be positional with other optional arguments.
*
* The positional arguments option should eventually be phased out.
*/
static int llog_parse_catalog_log_idx(int *argc, char ***argv, const char *opts,
struct obd_ioctl_data *data)
{
const struct option long_opts[] = {
/* the --catalog option is not required, just for consistency */
{ .val = 'c', .name = "catalog", .has_arg = required_argument },
{ .val = 'h', .name = "help", .has_arg = no_argument },
{ .val = 'i', .name = "log_idx", .has_arg = required_argument },
{ .val = 'l', .name = "log_id", .has_arg = required_argument },
{ .name = NULL } };
int c;
/* sanity check */
if (!data || *argc <= 1)
return -1;
data->ioc_dev = cur_device;
/* now process command line arguments*/
while ((c = getopt_long(*argc, *argv, opts, long_opts, NULL)) != -1) {
switch (c) {
case 'c':
data->ioc_inllen1 = strlen(optarg) + 1;
data->ioc_inlbuf1 = optarg;
break;
case 'i':
data->ioc_inllen3 = strlen(optarg) + 1;
data->ioc_inlbuf3 = optarg;
break;
case 'l': /* The log_id option isn't currently needed for
* cancel as mdt_iocontrol() handles IOC_LLOG_CANCEL,
* but we may as well keep it for now.
*/
data->ioc_inllen2 = strlen(optarg) + 1;
data->ioc_inlbuf2 = optarg;
break;
case 'h':
default:
return CMD_HELP;
}
}
*argc -= optind;
*argv += optind;
/* Allow catalog to be specified as first option without --catalog */
if (!data->ioc_inlbuf1 && *argc > 0) {
data->ioc_inlbuf1 = (*argv)[0];
data->ioc_inllen1 = strlen((*argv)[0]) + 1;
(*argc)--;
(*argv)++;
}
return 0;
}
int jt_llog_cancel(int argc, char **argv)
{
struct obd_ioctl_data data = { 0 };
char rawbuf[MAX_IOC_BUFLEN] = "", *buf = rawbuf;
char *cmd = argv[0];
int rc;
/* Manage default device */
if (llog_default_device(LLOG_DFLT_MGS_SET))
return CMD_INCOMPLETE;
/* Parse catalog file (in inlbuf1) and named parameters */
rc = llog_parse_catalog_log_idx(&argc, &argv, "c:hi:l:", &data);
/*
* Handle old positional parameters if not using named parameters,
* either "<catalog> <log_idx>" or "<catalog> <log_id> <log_idx>".
* It was "inlbuf3 = log_idx", and "inlbuf2 = log_id" (ignored by
* config log cancel), and shows why I hate positional parameters.
*/
if (argc == 1) {
data.ioc_inllen3 = strlen(argv[0]) + 1;
data.ioc_inlbuf3 = argv[0]; /* log index to cancel */
} else if (argc == 2) {
data.ioc_inllen2 = strlen(argv[0]) + 1;
data.ioc_inlbuf2 = argv[0]; /* log_id (ignored) */
data.ioc_inllen3 = strlen(argv[1]) + 1;
data.ioc_inlbuf3 = argv[1]; /* log index to cancel */
}
if (!data.ioc_inlbuf1 || !data.ioc_inlbuf3) {
/* missing mandatory parameters */
rc = CMD_HELP;
goto err;
}
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "%s: ioctl_pack for catalog '%s' failed: %s\n",
jt_cmdname(cmd), data.ioc_inlbuf1, strerror(-rc));
goto err;
}
rc = l_ioctl(OBD_DEV_ID, OBD_IOC_LLOG_CANCEL, buf);
if (rc)
fprintf(stderr, "%s: cancel catalog '%s:%s' failed: %s\n",
jt_cmdname(cmd), data.ioc_inlbuf1, data.ioc_inlbuf3,
strerror(errno));
err:
llog_default_device(LLOG_DFLT_DEV_RESET);
return rc;
}
int jt_llog_check(int argc, char **argv)
{
struct obd_ioctl_data data = { 0 };
char rawbuf[MAX_IOC_BUFLEN] = "", *buf = rawbuf;
char *catalog = NULL;
char startbuf[16], endbuf[16];
long start = 1, end = -1;
char *cmd = argv[0];
int rc;
rc = llog_parse_catalog_options(&argc, &argv, &catalog, &start,
&end, NULL);
if (rc)
return rc;
if (llog_default_device(LLOG_DFLT_MGS_SET))
return CMD_INCOMPLETE;
if (end == -1)
end = 0x7fffffff;
data.ioc_dev = cur_device;
data.ioc_inllen1 = strlen(catalog) + 1;
data.ioc_inlbuf1 = catalog;
snprintf(startbuf, sizeof(startbuf), "%lu", start);
snprintf(endbuf, sizeof(endbuf), "%lu", end);
/* start and end record numbers are passed as ASCII digits */
data.ioc_inllen2 = strlen(startbuf) + 1;
data.ioc_inlbuf2 = startbuf;
data.ioc_inllen3 = strlen(endbuf) + 1;
data.ioc_inlbuf3 = endbuf;
data.ioc_inllen4 = sizeof(rawbuf) - __ALIGN_KERNEL(sizeof(data), 8) -
__ALIGN_KERNEL(data.ioc_inllen1, 8) -
__ALIGN_KERNEL(data.ioc_inllen2, 8) -
__ALIGN_KERNEL(data.ioc_inllen3, 8);
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "%s: ioctl_pack failed for catalog '%s': %s\n",
jt_cmdname(cmd), data.ioc_inlbuf1, strerror(-rc));
goto err;
}
rc = l_ioctl(OBD_DEV_ID, OBD_IOC_LLOG_CHECK, buf);
if (rc == 0)
fprintf(stdout, "%s", ((struct obd_ioctl_data *)buf)->ioc_bulk);
else
fprintf(stderr, "%s: OBD_IOC_LLOG_CHECK failed: %s\n",
jt_cmdname(cmd), strerror(errno));
err:
llog_default_device(LLOG_DFLT_DEV_RESET);
return rc;
}
int jt_llog_remove(int argc, char **argv)
{
struct obd_ioctl_data data = { 0 };
char rawbuf[MAX_IOC_BUFLEN] = "", *buf = rawbuf;
char *cmd = argv[0];
int rc;
if (llog_default_device(LLOG_DFLT_MGS_SET))
return CMD_INCOMPLETE;
rc = llog_parse_catalog_log_idx(&argc, &argv, "c:hl:", &data);
if (rc)
goto err;
if (argc == 1) {
if (data.ioc_inlbuf2) {
fprintf(stderr,
"%s: --log_id is set, unknown argument '%s'\n",
jt_cmdname(cmd), argv[0]);
rc = CMD_HELP;
goto err;
}
data.ioc_inllen2 = strlen(argv[0]) + 1;
data.ioc_inlbuf2 = argv[0]; /* llog name */
}
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "%s: ioctl_pack for catalog '%s' failed: %s\n",
jt_cmdname(cmd), data.ioc_inlbuf1, strerror(-rc));
goto err;
}
rc = l_ioctl(OBD_DEV_ID, OBD_IOC_LLOG_REMOVE, buf);
if (rc)
fprintf(stderr, "%s: cancel catalog '%s:%s' failed: %s\n",
jt_cmdname(cmd), data.ioc_inlbuf1, data.ioc_inlbuf2,
strerror(-rc));
err:
llog_default_device(LLOG_DFLT_DEV_RESET);
return rc;
}
static void signal_server(int sig)
{
if (sig == SIGINT) {
do_disconnect("sigint", 1);
exit(1);
} else {
fprintf(stderr, "%s: got signal %d\n", jt_cmdname("sigint"),
sig);
}
}
int obd_initialize(int argc, char **argv)
{
llapi_register_ioc_dev(OBD_DEV_ID, OBD_DEV_PATH);
return 0;
}
void obd_finalize(int argc, char **argv)
{
struct sigaction sigact;
/* sigact initialization */
sigact.sa_handler = signal_server;
sigfillset(&sigact.sa_mask);
sigact.sa_flags = SA_RESTART;
sigaction(SIGINT, &sigact, NULL);
shmem_cleanup();
do_disconnect(argv[0], 1);
}
/**
* llog_last_index() - Get the index of the last llog record
* @logname: pointer to config log name
*
* logid: [0x3:0xa:0x0]:0
* flags: 4 (plain)
* records_count: 57
* last_index: 57
*
* Return 0 on success or %negative on error
*/
static long llog_last_index(char *logname)
{
struct obd_ioctl_data data = { 0 };
char rawbuf[MAX_IOC_BUFLEN] = "", *buf = rawbuf;
char *last_index;
long rc;
data.ioc_dev = cur_device;
data.ioc_inllen1 = strlen(logname) + 1;
data.ioc_inlbuf1 = logname;
data.ioc_inllen2 = sizeof(rawbuf) - __ALIGN_KERNEL(sizeof(data), 8) -
__ALIGN_KERNEL(data.ioc_inllen1, 8);
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "%s: ioctl_pack failed for catalog '%s': %s\n",
__func__, logname, strerror(-rc));
return rc;
}
rc = l_ioctl(OBD_DEV_ID, OBD_IOC_LLOG_INFO, buf);
if (rc == 0) {
last_index = strstr(((struct obd_ioctl_data *)buf)->ioc_bulk,
"last_index:");
return strtol(last_index + 11, NULL, 10);
}
rc = -errno;
return rc;
}
static char *get_llog_event_name(__u32 cmd)
{
#ifdef HAVE_SERVER_SUPPORT
struct lcfg_type_data *data;
data = lcfg_cmd2data(cmd);
if (data)
return data->ltd_name;
#endif
return NULL;
}
static char *get_event_filter(__u32 cmd)
{
char *event_name;
char *filter = NULL;
int len;
event_name = get_llog_event_name(cmd);
if (event_name) {
/* 9 bytes for "event: , " */
len = 9 + strlen(event_name);
filter = malloc(len + 1);
if (!filter)
return NULL;
memset(filter, 0, len + 1);
snprintf(filter, len, "event: %s, ", event_name);
return filter;
}
return NULL;
}
struct llog_del_ost_priv {
char *logname;
char *ostname;
int found;
int dryrun;
};
/**
* llog_del_ost_cb() - Callback to search and delete ostname in llog
* @record: pointer to llog record
* @data: pointer to ostname
*
* Return:
* * %1 if ostname is found and entry deleted
* * %0 if ostname is not found
* * %negative if error
*/
static int llog_del_ost_cb(char *record, void *data)
{
char ost_filter[MAX_STRING_SIZE] = {'\0'};
char log_idxstr[MAX_STRING_SIZE] = {'\0'};
long int log_idx = 0;
struct llog_del_ost_priv *priv = data;
char rawbuf[MAX_IOC_BUFLEN], *buf = rawbuf;
struct obd_ioctl_data ioc_data = { 0 };
int rc = 0;
if (priv->ostname && priv->ostname[0])
snprintf(ost_filter, sizeof(ost_filter), " %s", priv->ostname);
if (!strstr(record, ost_filter))
return rc;
rc = sscanf(record, "- { index: %ld", &log_idx);
if (rc < 0) {
fprintf(stderr, "error: record without index:\n%s\n",
record);
return 0;
}
snprintf(log_idxstr, sizeof(log_idxstr), "%ld", log_idx);
ioc_data.ioc_dev = cur_device;
ioc_data.ioc_inllen1 = strlen(priv->logname) + 1;
ioc_data.ioc_inlbuf1 = priv->logname;
ioc_data.ioc_inllen3 = strlen(log_idxstr) + 1;
ioc_data.ioc_inlbuf3 = log_idxstr;
rc = llapi_ioctl_pack(&ioc_data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "ioctl_pack for catalog '%s' failed: %s\n",
ioc_data.ioc_inlbuf1, strerror(-rc));
return rc;
}
if (priv->dryrun) {
fprintf(stdout, "[DRY RUN] cancel catalog '%s:%s':\"%s\"\n",
ioc_data.ioc_inlbuf1, ioc_data.ioc_inlbuf3, record);
} else {
rc = l_ioctl(OBD_DEV_ID, OBD_IOC_LLOG_CANCEL, buf);
if (rc)
fprintf(stderr, "cancel catalog '%s:%s' failed: %s\n",
ioc_data.ioc_inlbuf1, ioc_data.ioc_inlbuf3,
strerror(errno));
else {
fprintf(stdout, "cancel catalog %s log_idx %ld: done\n",
priv->logname, log_idx);
priv->found++;
}
}
return rc;
}
/**
* llog_del_ost() - Search and delete ost in llog
* @logname: pointer to config log name
* @last_index: the index of the last llog record
* @ostname: pointer to ost name
* @dryrun: dry run
*
* Return:
* * %1 if ostname is found and deleted
* * %0 if ostname is not found
*/
static int llog_del_ost(char *logname, long last_index, char *ostname,
int dryrun)
{
long start, end, inc = MAX_IOC_BUFLEN / 128;
int rc = 0;
struct llog_del_ost_priv priv = { logname, ostname, false, dryrun };
for (end = last_index; end > 1; end -= inc) {
start = end - inc > 0 ? end - inc : 1;
rc = jt_llog_print_iter(logname, start, end, llog_del_ost_cb,
&priv, true, false);
if (rc)
break;
}
if (priv.found)
fprintf(stdout, "del_ost: cancelled %d catalog entries\n",
priv.found);
else
fprintf(stdout, "del_ost: no catalog entry deleted\n");
return rc;
}
static bool combined_mgs_mds(char *fsname)
{
glob_t path;
int rc;
rc = cfs_get_param_paths(&path, "mdt/%s-MDT0000", fsname);
if (!rc)
cfs_free_param_data(&path);
if (get_mgs_device() > 0 && !rc)
return true;
return false;
}
static
void pool_cmd_interpret_err(enum lcfg_command_type cmd, char *cmdname,
char *fullpool, char *fsname, char *ostname, int rc)
{
switch (rc) {
case -ENAMETOOLONG:
fprintf(stderr,
"%s: either the pool or file system name is too long (max pool name len is %d and file system name is %d)\n",
jt_cmdname(cmdname), LOV_MAXPOOLNAME, LUSTRE_MAXFSNAME);
return;
case -EINVAL:
fprintf(stderr,
"%s: can contain only alphanumeric characters, underscores, and dashes besides the required '.'\n",
jt_cmdname(cmdname));
return;
default:
break;
}
switch (cmd) {
case LCFG_POOL_NEW:
case LCFG_POOL_DEL:
if (rc == -EEXIST)
fprintf(stderr, "%s: pool %s already exists\n",
jt_cmdname(cmdname), fullpool);
else if (rc == -ENOENT)
fprintf(stderr, "%s: pool %s not found\n",
jt_cmdname(cmdname), fullpool);
else if (rc == -ENOTEMPTY)
fprintf(stderr,
"%s: pool %s not empty, please remove all members\n",
jt_cmdname(cmdname), fullpool);
else if (rc)
fprintf(stderr, "%s %s: %s\n",
jt_cmdname(cmdname), fullpool, strerror(-rc));
break;
case LCFG_POOL_ADD:
case LCFG_POOL_REM:
if (rc == -ENOMEDIUM)
fprintf(stderr, "%s: pool %s not found\n",
jt_cmdname(cmdname), fullpool);
else if (rc == -EEXIST)
fprintf(stderr, "%s: %s is already in pool %s\n",
jt_cmdname(cmdname), ostname, fullpool);
else if (rc == -ENODEV)
fprintf(stderr, "%s: %s is not part of the '%s' fs.\n",
jt_cmdname(cmdname), ostname, fsname);
else if (rc == -ENOENT)
fprintf(stderr, "%s: %s not found in pool %s\n",
jt_cmdname(cmdname), ostname, fullpool);
else if (rc)
fprintf(stderr, "%s %s %s: %s\n",
jt_cmdname(cmdname), fullpool, ostname,
strerror(-rc));
break;
default:
break;
}
}
static int get_mgc_requeue_timeout_min(void)
{
const char *path = "/sys/module/mgc/parameters/mgc_requeue_timeout_min";
FILE *fp;
char buf[PATH_MAX] = { 0 };
int val = 5;
fp = fopen(path, "r");
if (!fp)
return val;
if (!fgets(buf, sizeof(buf), fp))
goto out;
val = atoi(buf);
out:
fclose(fp);
return val;
}
/*
* This check only verifies that the changes have been "pushed out" to
* the client successfully. This involves waiting for a config update,
* and so may fail because of problems in that code or post-command
* network loss. So reporting a warning is appropriate, but not a failure.
*/
static int check_pool_cmd_result(enum lcfg_command_type cmd, char *fsname,
char *poolname, char *ostname)
{
int cpt;
int rc = 0;
/* mgs is standalone -> no client to wait */
if (!combined_mgs_mds(fsname))
return 0;
/* max time to wait a client */
cpt = 2 * get_mgc_requeue_timeout_min() + 2;
switch (cmd) {
case LCFG_POOL_NEW: {
do {
rc = llapi_search_ost(fsname, poolname, NULL);
if (rc == -ENODEV)
return rc;
if (rc < 0)
sleep(1);
cpt--;
} while ((rc < 0) && (cpt > 0));
if (rc >= 0) {
fprintf(stderr, "Pool %s.%s created\n",
fsname, poolname);
return 0;
}
fprintf(stderr, "Warning, pool %s.%s not found\n", fsname,
poolname);
return -ENOENT;
}
case LCFG_POOL_DEL: {
do {
rc = llapi_search_ost(fsname, poolname, NULL);
if (rc == -ENODEV)
return rc;
if (rc >= 0)
sleep(1);
cpt--;
} while ((rc >= 0) && (cpt > 0));
if (rc < 0) {
fprintf(stderr, "Pool %s.%s destroyed\n",
fsname, poolname);
return 0;
}
fprintf(stderr, "Warning, pool %s.%s still found\n", fsname,
poolname);
return -EEXIST;
}
case LCFG_POOL_ADD: {
do {
rc = llapi_search_ost(fsname, poolname, ostname);
if (rc == -ENODEV)
return rc;
if (rc != 1)
sleep(1);
cpt--;
} while ((rc != 1) && (cpt > 0));
if (rc == 1) {
fprintf(stderr, "OST %s added to pool %s.%s\n",
ostname, fsname, poolname);
return 0;
}
fprintf(stderr, "Warning, OST %s not found in pool %s.%s\n",
ostname, fsname, poolname);
return -ENOENT;
}
case LCFG_POOL_REM: {
do {
rc = llapi_search_ost(fsname, poolname, ostname);
if (rc == -ENODEV)
return rc;
if (rc == 1)
sleep(1);
cpt--;
} while ((rc == 1) && (cpt > 0));
if (rc != 1) {
fprintf(stderr, "OST %s removed from pool %s.%s\n",
ostname, fsname, poolname);
return 0;
}
fprintf(stderr, "Warning, OST %s still found in pool %s.%s\n",
ostname, fsname, poolname);
return -EEXIST;
}
default:
break;
}
return -EINVAL;
}
static int check_and_complete_ostname(char *fsname, char *ostname)
{
char real_ostname[UUID_MAX];
char *ptr;
int len;
char i;
if (strlen(ostname) >= sizeof(real_ostname))
return -ENAMETOOLONG;
/* if OST name does not start with fsname, we add it */
/* if not check if the fsname is the right one */
ptr = strchr(ostname, '-');
if (!ptr) {
len = snprintf(real_ostname, sizeof(real_ostname), "%s-%s",
fsname, ostname);
if (len < 0 || len >= sizeof(real_ostname))
return -ENAMETOOLONG;
} else if (strncmp(ostname, fsname, strlen(fsname)) != 0) {
fprintf(stderr, "%s does not start with fsname %s\n",
ostname, fsname);
return -EINVAL;
} else {
strncpy(real_ostname, ostname, sizeof(real_ostname));
}
/* real_ostname is fsname-????? */
ptr = real_ostname + strlen(fsname) + 1;
if (strncmp(ptr, "OST", 3) != 0) {
fprintf(stderr, "%s does not start by %s-OST nor OST\n",
ostname, fsname);
return -EINVAL;
}
/* real_ostname is fsname-OST????? */
ptr += 3;
for (i = 0; i < 4; i++) {
if (!isxdigit(*ptr)) {
fprintf(stderr,
"ost's index in %s is not an hexa number\n",
ostname);
return -EINVAL;
}
ptr++;
}
/* real_ostname is fsname-OSTXXXX????? */
/* if OST name does not end with _UUID, we add it */
if (*ptr == '\0') {
len = sizeof(real_ostname) - strlen(real_ostname) - 1;
if (sizeof("_UUID") - 1 > len)
return -ENAMETOOLONG;
strncat(real_ostname, "_UUID", len);
} else if (strcmp(ptr, "_UUID") != 0) {
fprintf(stderr,
"ostname %s does not end with _UUID\n", ostname);
return -EINVAL;
}
/* real_ostname is fsname-OSTXXXX_UUID */
strcpy(ostname, real_ostname);
return 0;
}
/* returns 0 or -errno */
static int pool_cmd(enum lcfg_command_type cmd, char *cmdname,
char *fullpoolname, char *fsname, char *ostname)
{
int rc = 0;
struct obd_ioctl_data data;
struct lustre_cfg_bufs bufs;
struct lustre_cfg *lcfg;
char rawbuf[MAX_IOC_BUFLEN], *buf = rawbuf;
lustre_cfg_bufs_reset(&bufs, NULL);
lustre_cfg_bufs_set_string(&bufs, 0, cmdname);
lustre_cfg_bufs_set_string(&bufs, 1, fullpoolname);
if (ostname)
lustre_cfg_bufs_set_string(&bufs, 2, ostname);
lcfg = malloc(lustre_cfg_len(bufs.lcfg_bufcount, bufs.lcfg_buflen));
if (!lcfg)
return -ENOMEM;
lustre_cfg_init(lcfg, cmd, &bufs);
memset(&data, 0, sizeof(data));
rc = data.ioc_dev = get_mgs_device();
if (rc < 0)
goto out;
data.ioc_type = LUSTRE_CFG_TYPE;
data.ioc_plen1 = lustre_cfg_len(lcfg->lcfg_bufcount,
lcfg->lcfg_buflens);
data.ioc_pbuf1 = (void *)lcfg;
memset(buf, 0, sizeof(rawbuf));
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(cmdname));
free(lcfg);
return rc;
}
rc = l_ioctl(OBD_DEV_ID, OBD_IOC_POOL, buf);
out:
free(lcfg);
pool_cmd_interpret_err(cmd, cmdname, fullpoolname, fsname, ostname, rc);
return rc;
}
int jt_del_ost(int argc, char **argv)
{
char *fsname = NULL, *ptr, *logname;
char mdtpattern[16], clipattern[16];
char ostname[MAX_OBD_NAME + 1];
long last_index;
__u32 index;
int rc, start = 0, dryrun = 0;
char c;
static struct option long_opts[] = {
{ .val = 'h', .name = "help", .has_arg = no_argument },
{ .val = 'n', .name = "dryrun", .has_arg = no_argument },
{ .val = 't', .name = "target", .has_arg = required_argument },
{ .name = NULL } };
while ((c = getopt_long(argc, argv, "hnt:", long_opts, NULL)) != -1) {
switch (c) {
case 't':
fsname = strdup(optarg);
break;
case 'n':
dryrun = 1;
break;
case 'h':
default:
free(fsname);
return CMD_HELP;
}
}
if (fsname == NULL)
return CMD_HELP;
if (llog_default_device(LLOG_DFLT_MGS_SET)) {
rc = CMD_INCOMPLETE;
goto out;
}
ptr = strstr(fsname, "-OST");
if (!ptr) {
rc = CMD_HELP;
goto err;
}
if (dryrun)
fprintf(stdout, "del_ost: dry run for target %s\n", fsname);
*ptr++ = '\0';
rc = sscanf(ptr, "OST%04x", &index);
if (rc != 1) {
rc = -EINVAL;
goto err;
}
if (strlen(ptr) > sizeof(ostname) - 1) {
rc = -E2BIG;
goto err;
}
snprintf(mdtpattern, sizeof(mdtpattern), "%s-MDT", fsname);
snprintf(clipattern, sizeof(clipattern), "%s-client", fsname);
snprintf(ostname, sizeof(ostname), "%s-%s", fsname, ptr);
do {
char rawbuf[MAX_IOC_BUFLEN], *buf = rawbuf;
char *begin, *end;
start = llog_catlist_next(start, rawbuf, sizeof(rawbuf));
if (start < 0)
break;
begin = ((struct obd_ioctl_data *)buf)->ioc_bulk;
if (strlen(begin) == 0)
break;
while ((end = strchr(begin, '\n'))) {
*end = '\0';
logname = strstr(begin, "config_log: ");
if (logname && (strstr(logname, mdtpattern) ||
strstr(logname, clipattern))) {
logname += 12;
fprintf(stdout, "config_log: %s\n", logname);
last_index = llog_last_index(logname);
if (last_index < 0) {
fprintf(stderr,
"error with catalog %s: %s\n",
logname, strerror(-last_index));
rc = -last_index;
goto err;
}
rc = llog_del_ost(logname, last_index, ostname,
dryrun);
if (rc < 0)
goto err;
}
begin = end + 1;
}
} while (start);
err:
llog_default_device(LLOG_DFLT_DEV_RESET);
out:
free(fsname);
return rc;
}
#ifdef HAVE_SERVER_SUPPORT
/**
* nodemap_cmd() - Format and send the ioctl to the MGS.
* @cmd: IOCTL to send
* @dynamic: If true, targets is local MDS/OSS. If false target is MGS
* @ret_data: void pointer to return anything from ioctl
* @ret_size: size of @ret_data
*
* Return 0 on success, -errno on failure
*/
static int nodemap_cmd(enum lcfg_command_type cmd, bool dynamic,
void *ret_data, unsigned int ret_size, ...)
{
va_list ap;
char *arg;
int i = 0;
struct lustre_cfg_bufs bufs;
struct obd_ioctl_data data;
struct lustre_cfg *lcfg;
char rawbuf[MAX_IOC_BUFLEN];
char *buf = rawbuf;
int rc = 0;
lustre_cfg_bufs_reset(&bufs, NULL);
va_start(ap, ret_size);
arg = va_arg(ap, char *);
while (arg) {
lustre_cfg_bufs_set_string(&bufs, i, arg);
i++;
arg = va_arg(ap, char *);
}
va_end(ap);
lcfg = malloc(lustre_cfg_len(bufs.lcfg_bufcount, bufs.lcfg_buflen));
if (!lcfg) {
errno = ENOMEM;
return -errno;
}
lustre_cfg_init(lcfg, cmd, &bufs);
memset(&data, 0, sizeof(data));
getdev:
if (dynamic) {
if (is_mds()) {
rc = data.ioc_dev = get_mds_device();
} else if (is_oss()) {
rc = data.ioc_dev = get_oss_device();
} else {
errno = EINVAL;
rc = -errno;
}
} else {
if (!is_mgs()) {
dynamic = true;
goto getdev;
}
rc = data.ioc_dev = get_mgs_device();
}
if (rc < 0)
goto out;
data.ioc_type = LUSTRE_CFG_TYPE;
data.ioc_plen1 = lustre_cfg_len(lcfg->lcfg_bufcount,
lcfg->lcfg_buflens);
data.ioc_pbuf1 = (void *)lcfg;
memset(buf, 0, sizeof(rawbuf));
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
errno = -rc;
fprintf(stderr,
"error: invalid ioctl request: %08x errno: %d: %s\n",
cmd, errno, strerror(errno));
goto out;
}
rc = l_ioctl(OBD_DEV_ID, OBD_IOC_NODEMAP, buf);
if (rc) {
if (errno == ENXIO)
fprintf(stderr,
"error: invalid ioctl: %08x errno %d: cannot proceed on non-MGS node\n",
cmd, errno);
else
fprintf(stderr,
"error: invalid ioctl: %08x errno: %d: %s\n",
cmd, errno, strerror(errno));
goto out;
}
if (ret_data) {
rc = llapi_ioctl_unpack(&data, buf, sizeof(rawbuf));
if (rc) {
errno = -rc;
goto out;
}
if (ret_size > data.ioc_plen1)
ret_size = data.ioc_plen1;
memcpy(ret_data, data.ioc_pbuf1, ret_size);
}
out:
free(lcfg);
return rc;
}
/**
* jt_nodemap_activate() - activate nodemap functions
* @argc: number of args
* @argv: variable string arguments
*
* argv[0] 1 for activate or 0 for deactivate
*
* Return 0 on success
*/
int jt_nodemap_activate(int argc, char **argv)
{
int rc = EXIT_SUCCESS;
if (argc != 2)
return CMD_HELP;
errno = -nodemap_cmd(LCFG_NODEMAP_ACTIVATE, false, NULL, 0,
argv[0], argv[1], NULL);
if (errno) {
rc = EXIT_FAILURE;
perror(argv[0]);
}
return rc;
}
/**
* jt_nodemap_add() - add a nodemap
* @argc: number of args
* @argv: variable string arguments
*
* argv[0] nodemap name
*
* Return 0 on success
*/
int jt_nodemap_new(int argc, char **argv)
{
char nm_to_send[LUSTRE_NODEMAP_NAME_LENGTH*2 + 2];
char *nodemap_name = NULL, *parent_nm = NULL;
bool dynamic = false;
int c, rc = EXIT_SUCCESS;
static struct option long_opts[] = {
{ .val = 'd', .name = "dynamic", .has_arg = no_argument },
{ .val = 'h', .name = "help", .has_arg = no_argument },
{ .val = 'n', .name = "name", .has_arg = required_argument },
{ .val = 'p', .name = "parent", .has_arg = required_argument },
{ .name = NULL } };
while ((c = getopt_long(argc, argv, "dhn:p:",
long_opts, NULL)) != -1) {
switch (c) {
case 'd':
dynamic = true;
break;
case 'n':
nodemap_name = optarg;
break;
case 'p':
parent_nm = optarg;
break;
case 'h':
default:
return CMD_HELP;
}
}
if (!nodemap_name) {
if (optind >= argc) {
fprintf(stderr, "%s: missing nodemap name\n", argv[0]);
return CMD_HELP;
}
nodemap_name = argv[optind];
}
if (dynamic && !parent_nm) {
fprintf(stderr,
"%s: missing parent for dynamic nodemap\n", argv[0]);
return CMD_HELP;
}
if (!dynamic) {
if (!is_mgs()) {
fprintf(stderr,
"%s: non-dynamic nodemap only allowed on MGS node\n",
argv[0]);
return CMD_HELP;
}
if (parent_nm) {
fprintf(stderr,
"%s: invalid parent for non-dynamic nodemap\n",
argv[0]);
return CMD_HELP;
}
}
if (!llapi_nodemap_exists(nodemap_name)) {
fprintf(stderr, "error: nodemap '%s' already exists\n",
nodemap_name);
errno = EINVAL;
goto out;
}
if (parent_nm) {
if (llapi_nodemap_exists(parent_nm)) {
fprintf(stderr, "error: parent '%s' does not exist\n",
parent_nm);
errno = EINVAL;
goto out;
}
}
if (snprintf(nm_to_send, sizeof(nm_to_send), "%s%s%s",
parent_nm ? parent_nm : "",
parent_nm ? "/" : "", nodemap_name) >=
sizeof(nm_to_send)) {
fprintf(stderr, "error: nodemap names %s%s%s too long\n",
parent_nm ? parent_nm : "", parent_nm ? "/" : "",
nodemap_name);
errno = EINVAL;
goto out;
}
errno = -nodemap_cmd(LCFG_NODEMAP_ADD, dynamic, NULL, 0, argv[0],
nm_to_send, NULL);
out:
if (errno) {
rc = EXIT_FAILURE;
perror(argv[0]);
}
return rc;
}
/**
* jt_nodemap_del() - delete a nodemap
* @argc: number of args
* @argv: variable string arguments
*
* argv[0] nodemap name
*
* Return 0 on success
*/
int jt_nodemap_del(int argc, char **argv)
{
char *nodemap_name = NULL;
int c, rc = EXIT_SUCCESS;
static struct option long_opts[] = {
{ .val = 'h', .name = "help", .has_arg = no_argument },
{ .val = 'n', .name = "name", .has_arg = required_argument },
{ .name = NULL } };
while ((c = getopt_long(argc, argv, "hn:", long_opts, NULL)) != -1) {
switch (c) {
case 'n':
nodemap_name = optarg;
break;
case 'h':
default:
return CMD_HELP;
}
}
if (!nodemap_name) {
if (argc != 2) {
fprintf(stderr, "nodemap_del: missing nodemap name\n");
return CMD_HELP;
}
nodemap_name = argv[1];
}
if (llapi_nodemap_exists(nodemap_name) != 0) {
fprintf(stderr, "error: nodemap '%s' does not exist\n",
nodemap_name);
errno = EINVAL;
goto out;
}
errno = -nodemap_cmd(LCFG_NODEMAP_DEL, false, NULL, 0, argv[0],
nodemap_name, NULL);
out:
if (errno) {
rc = EXIT_FAILURE;
perror(argv[0]);
}
return rc;
}
/**
* jt_nodemap_test_nid() - test a nid for nodemap membership
* @argc: number of args
* @argv: variable string arguments
*
* argv[0] properly formatted nid
*
* Return 0 on success
*/
int jt_nodemap_test_nid(int argc, char **argv)
{
char rawbuf[MAX_IOC_BUFLEN];
char *nid = NULL;
int c, rc = EXIT_SUCCESS;
static struct option long_opts[] = {
{ .val = 'h', .name = "help", .has_arg = no_argument },
{ .val = 'n', .name = "nid", .has_arg = required_argument },
{ .name = NULL } };
while ((c = getopt_long(argc, argv, "hn:", long_opts, NULL)) != -1) {
switch (c) {
case 'n':
nid = optarg;
break;
case 'h':
default:
return CMD_HELP;
}
}
if (!nid) {
if (argc != 2)
return CMD_HELP;
nid = argv[1];
}
errno = -nodemap_cmd(LCFG_NODEMAP_TEST_NID, false, &rawbuf,
sizeof(rawbuf), argv[0], nid, NULL);
if (errno) {
rc = EXIT_FAILURE;
perror(argv[0]);
} else {
printf("%s\n", (char *)rawbuf);
}
return rc;
}
/**
* jt_nodemap_test_id() - test a nodemap id pair for mapping
* @argc: number of args
* @argv: variable string arguments
*
* The argv array should contain the nodemap name, the id
* to checking the mapping on, and the id type (UID or GID)
*
* Return 0 on success
*/
int jt_nodemap_test_id(int argc, char **argv)
{
char rawbuf[MAX_IOC_BUFLEN];
char *nidstr = NULL;
char *idstr = NULL;
char *typestr = NULL;
int c, rc = 0;
static struct option long_opts[] = {
{ .val = 'h', .name = "help", .has_arg = no_argument },
{ .val = 'i', .name = "id", .has_arg = required_argument },
{ .val = 'n', .name = "nid", .has_arg = required_argument },
{ .val = 't', .name = "idtype",
.has_arg = required_argument },
{ .name = NULL } };
while ((c = getopt_long(argc, argv, "hi:n:t:",
long_opts, NULL)) != -1) {
switch (c) {
case 'i':
idstr = optarg;
break;
case 'n':
nidstr = optarg;
break;
case 't':
typestr = optarg;
break;
case 'h':
default:
return CMD_HELP;
}
}
if (!nidstr || !typestr || !idstr)
return CMD_HELP;
rc = nodemap_cmd(LCFG_NODEMAP_TEST_ID, false, &rawbuf, sizeof(rawbuf),
argv[0], nidstr, typestr, idstr, NULL);
if (rc == 0)
printf("%s\n", (char *)rawbuf);
return rc;
}
/**
* parse_nid_range() - parse nid range
* @nodemap_range: range string
* @nid_range: nid range string, min_nid:max_nid
* @range_len: length
*
* Return 0 on success
*/
static int parse_nid_range(char *nodemap_range, char *nid_range, int range_len)
{
char min_nid[LNET_NIDSTR_SIZE + 1];
char max_nid[LNET_NIDSTR_SIZE + 1];
struct list_head nidlist;
int rc = 0;
if (strchr(nodemap_range, '/') || strchr(nodemap_range, ':')) {
strncpy(nid_range, nodemap_range, range_len);
return 0;
}
INIT_LIST_HEAD(&nidlist);
if (!cfs_parse_nidlist(nodemap_range, strlen(nodemap_range),
&nidlist)) {
fprintf(stderr,
"error: nodemap_xxx_range: can't parse nid range: %s\n",
nodemap_range);
return -EINVAL;
}
rc = cfs_nidrange_find_min_max(&nidlist, &min_nid[0], &max_nid[0],
LNET_NIDSTR_SIZE);
if (rc < 0) {
if (rc == -EINVAL)
fprintf(stderr,
"error: nodemap_xxx_range: nid range uses currently unsupported features\n");
else if (rc == -ERANGE)
fprintf(stderr,
"error: nodemap_xxx_range: nodemap ranges must be contiguous\n");
return rc;
}
snprintf(nid_range, range_len, "%s:%s", min_nid, max_nid);
return rc;
}
/**
* jt_nodemap_add_range() - add an nid range to a nodemap
* @argc: number of args
* @argv: variable string arguments
*
* --name nodemap name
* --range properly formatted nid range
*
* Return 0 on success or -ERRNO on error
*/
int jt_nodemap_add_range(int argc, char **argv)
{
char nid_range[2 * LNET_NIDSTR_SIZE + 2];
char *nodemap_range = NULL;
char *nodemap_name = NULL;
int c, rc = EXIT_SUCCESS;
static struct option long_opts[] = {
{ .val = 'h', .name = "help", .has_arg = no_argument },
{ .val = 'n', .name = "name", .has_arg = required_argument },
{ .val = 'r', .name = "range", .has_arg = required_argument },
{ .name = NULL } };
while ((c = getopt_long(argc, argv, "hn:r:",
long_opts, NULL)) != -1) {
switch (c) {
case 'n':
nodemap_name = optarg;
break;
case 'r':
nodemap_range = optarg;
break;
case 'h':
default:
return CMD_HELP;
}
}
if (!nodemap_name) {
fprintf(stderr, "nodemap_add_range: missing nodemap name\n");
return CMD_HELP;
}
if (!nodemap_range) {
fprintf(stderr, "nodemap_add_range: missing NID range\n");
return CMD_HELP;
}
rc = parse_nid_range(nodemap_range, nid_range, sizeof(nid_range));
if (rc)
return CMD_HELP;
errno = -nodemap_cmd(LCFG_NODEMAP_ADD_RANGE, false, NULL, 0, argv[0],
nodemap_name, nid_range, NULL);
if (errno) {
rc = EXIT_FAILURE;
perror(argv[0]);
}
return rc;
}
/**
* jt_nodemap_del_range() - delete an nid range to a nodemap
* @argc: number of args
* @argv: variable string arguments
*
* --name nodemap name
* --range properly formatted nid range
*
* Return 0 on success
*/
int jt_nodemap_del_range(int argc, char **argv)
{
char nid_range[2 * LNET_NIDSTR_SIZE + 2];
char *nodemap_range = NULL;
char *nodemap_name = NULL;
int c, rc = EXIT_SUCCESS;
static struct option long_opts[] = {
{ .val = 'h', .name = "help", .has_arg = no_argument },
{ .val = 'n', .name = "name", .has_arg = required_argument },
{ .val = 'r', .name = "range", .has_arg = required_argument },
{ .name = NULL } };
while ((c = getopt_long(argc, argv, "hn:r:",
long_opts, NULL)) != -1) {
switch (c) {
case 'n':
nodemap_name = optarg;
break;
case 'r':
nodemap_range = optarg;
break;
case 'h':
default:
return CMD_HELP;
}
}
if (!nodemap_name) {
fprintf(stderr, "nodemap_del_range: missing nodemap name\n");
return CMD_HELP;
}
if (!nodemap_range) {
fprintf(stderr, "nodemap_del_range: missing NID range\n");
return CMD_HELP;
}
rc = parse_nid_range(nodemap_range, nid_range, sizeof(nid_range));
if (rc)
return CMD_HELP;
errno = -nodemap_cmd(LCFG_NODEMAP_DEL_RANGE, false, NULL, 0, argv[0],
nodemap_name, nid_range, NULL);
if (errno) {
rc = EXIT_FAILURE;
perror(argv[0]);
}
return rc;
}
/**
* jt_nodemap_banlist_add() - add a banned nid range to a nodemap
* @argc: number of args
* @argv: variable string arguments
*
* --name nodemap name
* --range properly formatted banned nid range
*
* Return 0 on success
*/
int jt_nodemap_banlist_add(int argc, char **argv)
{
char *nodemap_name = LUSTRE_NODEMAP_GUESS;
char nid_range[2 * LNET_NIDSTR_SIZE + 2];
char *nodemap_range = NULL;
int c, rc = EXIT_SUCCESS;
static struct option long_opts[] = {
{ .val = 'h', .name = "help", .has_arg = no_argument },
{ .val = 'n', .name = "name", .has_arg = required_argument },
{ .val = 'r', .name = "range", .has_arg = required_argument },
{ .name = NULL } };
while ((c = getopt_long(argc, argv, "hn:r:",
long_opts, NULL)) != -1) {
switch (c) {
case 'n':
nodemap_name = optarg;
break;
case 'r':
nodemap_range = optarg;
break;
case 'h':
case '?':
default:
return CMD_HELP;
}
}
if (!nodemap_range) {
fprintf(stderr, "nodemap_banlist_add: missing NID range\n");
return CMD_HELP;
}
errno = -parse_nid_range(nodemap_range, nid_range, sizeof(nid_range));
if (errno)
return CMD_HELP;
errno = -nodemap_cmd(LCFG_NODEMAP_BANLIST_ADD, false, NULL, 0, argv[0],
nodemap_name, nid_range, NULL);
if (errno) {
rc = EXIT_FAILURE;
perror(argv[0]);
}
return rc;
}
/**
* jt_nodemap_banlist_del() - delete a banned nid range from a nodemap
* @argc: number of args
* @argv: variable string arguments
*
* --name nodemap name
* --range properly formatted banned nid range
*
* Return 0 on success
*/
int jt_nodemap_banlist_del(int argc, char **argv)
{
char *nodemap_name = LUSTRE_NODEMAP_GUESS;
char nid_range[2 * LNET_NIDSTR_SIZE + 2];
char *nodemap_range = NULL;
int c, rc = EXIT_SUCCESS;
static struct option long_opts[] = {
{ .val = 'h', .name = "help", .has_arg = no_argument },
{ .val = 'n', .name = "name", .has_arg = required_argument },
{ .val = 'r', .name = "range", .has_arg = required_argument },
{ .name = NULL } };
while ((c = getopt_long(argc, argv, "hn:r:",
long_opts, NULL)) != -1) {
switch (c) {
case 'n':
nodemap_name = optarg;
break;
case 'r':
nodemap_range = optarg;
break;
case 'h':
case '?':
default:
return CMD_HELP;
}
}
if (!nodemap_range) {
fprintf(stderr, "nodemap_banlist_del: missing NID range\n");
return CMD_HELP;
}
errno = -parse_nid_range(nodemap_range, nid_range, sizeof(nid_range));
if (errno)
return CMD_HELP;
errno = -nodemap_cmd(LCFG_NODEMAP_BANLIST_DEL, false, NULL, 0, argv[0],
nodemap_name, nid_range, NULL);
if (errno) {
rc = EXIT_FAILURE;
perror(argv[0]);
}
return rc;
}
/**
* jt_nodemap_set_fileset() - set a fileset on a nodemap
* @argc: number of args
* @argv: variable string arguments
*
* --name nodemap name
* --fileset fileset name
*
* Return 0 on success
*/
int jt_nodemap_set_fileset(int argc, char **argv)
{
char *nodemap_name = NULL;
char *fileset_name = NULL;
int c, rc = 0;
static struct option long_opts[] = {
{ .val = 'f', .name = "fileset", .has_arg = required_argument },
{ .val = 'h', .name = "help", .has_arg = no_argument },
{ .val = 'n', .name = "name", .has_arg = required_argument },
{ .name = NULL } };
while ((c = getopt_long(argc, argv, "f:hn:",
long_opts, NULL)) != -1) {
switch (c) {
case 'f':
fileset_name = optarg;
break;
case 'n':
nodemap_name = optarg;
break;
case 'h':
case '?':
default:
return CMD_HELP;
}
}
if (!nodemap_name) {
fprintf(stderr, "nodemap_set_fileset: missing NODEMAP_NAME\n");
return CMD_HELP;
}
if (!fileset_name) {
fprintf(stderr, "nodemap_set_fileset: missing fileset SUBDIRECTORY\n");
return CMD_HELP;
}
#if LUSTRE_VERSION_CODE > OBD_OCD_VERSION(2, 17, 52, 0)
fprintf(stdout,
"This command is deprecated, please use nodemap_fileset_{add/del} instead.\n");
#endif
rc = nodemap_cmd(LCFG_NODEMAP_SET_FILESET, false, NULL, 0, argv[0],
nodemap_name, fileset_name, NULL);
if (rc != 0) {
fprintf(stderr,
"error: cannot '%s' with fileset '%s' on nodemap '%s': %s\n",
jt_cmdname(argv[0]), fileset_name, nodemap_name,
strerror(errno));
}
return rc;
}
/**
* jt_nodemap_fileset_add() - add a fileset to a nodemap
* @argc: number of args
* @argv: variable string arguments
*
* --name nodemap name
* --fileset fileset name
* --alt refer to an alternate fileset
*
* Return 0 on success
*/
int jt_nodemap_fileset_add(int argc, char **argv)
{
char *nodemap_name = NULL;
char *fileset_name = NULL;
bool alt = false;
bool ro = false;
int c;
int rc = 0;
static struct option long_opts[] = {
{ .val = 'a', .name = "alt", .has_arg = no_argument },
{ .val = 'f', .name = "fileset", .has_arg = required_argument },
{ .val = 'h', .name = "help", .has_arg = no_argument },
{ .val = 'n', .name = "name", .has_arg = required_argument },
{ .val = 'r', .name = "ro", .has_arg = no_argument },
{ .name = NULL }
};
while ((c = getopt_long(argc, argv, "af:hn:r",
long_opts, NULL)) != -1) {
switch (c) {
case 'a':
alt = true;
break;
case 'f':
fileset_name = optarg;
break;
case 'n':
nodemap_name = optarg;
break;
case 'r':
ro = true;
break;
case 'h':
case '?':
default:
return CMD_HELP;
}
}
if (!nodemap_name || !fileset_name)
return CMD_HELP;
rc = nodemap_cmd(LCFG_NODEMAP_FILESET_ADD, false, NULL, 0, argv[0],
nodemap_name, fileset_name, alt ? "1" : "0",
ro ? "1" : "0", NULL);
if (rc != 0) {
fprintf(stderr,
"error: cannot '%s' with fileset '%s' on nodemap '%s': %s\n",
jt_cmdname(argv[0]), fileset_name, nodemap_name,
strerror(errno));
}
return rc;
}
/**
* jt_nodemap_fileset_del() - delete a fileset from a nodemap
* @argc: number of args
* @argv: variable string arguments
*
* --name nodemap name
* --fileset fileset name
*
* Return 0 on success
*/
int jt_nodemap_fileset_del(int argc, char **argv)
{
char *nodemap_name = NULL;
char *fileset_name = NULL;
bool all = false;
int rc = 0;
int c;
static struct option long_opts[] = {
{ .val = 'A', .name = "all", .has_arg = no_argument },
{ .val = 'f', .name = "fileset", .has_arg = required_argument },
{ .val = 'h', .name = "help", .has_arg = no_argument },
{ .val = 'n', .name = "name", .has_arg = required_argument },
{ .name = NULL }
};
while ((c = getopt_long(argc, argv, "Af:hn:",
long_opts, NULL)) != -1) {
switch (c) {
case 'A':
all = true;
break;
case 'f':
fileset_name = optarg;
break;
case 'n':
nodemap_name = optarg;
break;
case 'h':
case '?':
default:
return CMD_HELP;
}
}
if (!nodemap_name || (!fileset_name && !all))
return CMD_HELP;
if (all && fileset_name) {
fprintf(stderr,
"error: cannot specify both --fileset and --all options\n");
return CMD_HELP;
}
rc = nodemap_cmd(LCFG_NODEMAP_FILESET_DEL, false, NULL, 0, argv[0],
nodemap_name, all ? "*" : fileset_name, NULL);
if (rc != 0) {
fprintf(stderr,
"error: cannot '%s' with fileset '%s' on nodemap '%s': %s\n",
jt_cmdname(argv[0]), fileset_name, nodemap_name,
strerror(errno));
}
return rc;
}
/**
* jt_nodemap_fileset_modify() - Modifies an existing fileset
* @argc: number of args
* @argv: variable string arguments
*
* --name nodemap name
* --fileset the existing fileset name
* --rename new fileset name
* --ro/--rw whether fileset should be rw or ro
* --alt/--primary whether the fileset should be alt or primary
*
* Return 0 on success
*/
int jt_nodemap_fileset_modify(int argc, char **argv)
{
char *nodemap_name = NULL;
char *fileset_name = NULL;
char *fileset_name_new = NULL;
char *type_new = "";
char *access_new = "";
bool type_conflict = false;
bool access_conflict = false;
char flags[16];
int c;
int rc = 0;
static struct option long_opts[] = {
{ .val = 'a', .name = "alt", .has_arg = no_argument },
{ .val = 'f', .name = "fileset", .has_arg = required_argument },
{ .val = 'h', .name = "help", .has_arg = no_argument },
{ .val = 'n', .name = "name", .has_arg = required_argument },
{ .val = 'o', .name = "ro", .has_arg = no_argument },
{ .val = 'p', .name = "primary", .has_arg = no_argument },
{ .val = 'r', .name = "rename", .has_arg = required_argument },
{ .val = 'w', .name = "rw", .has_arg = no_argument },
{ .name = NULL }
};
while ((c = getopt_long(argc, argv, "af:hn:pr",
long_opts, NULL)) != -1) {
switch (c) {
case 'a':
if (strlen(type_new) > 0)
type_conflict = true;
type_new = "alt";
break;
case 'f':
fileset_name = optarg;
break;
case 'n':
nodemap_name = optarg;
break;
case 'o':
if (strlen(access_new) > 0)
access_conflict = true;
access_new = "ro";
break;
case 'p':
if (strlen(type_new) > 0)
type_conflict = true;
type_new = "prim";
break;
case 'r':
fileset_name_new = optarg;
if (!fileset_name_new || fileset_name_new[0] == '\0') {
fprintf(stderr,
"--rename parameter cannot be empty\n");
return CMD_HELP;
}
break;
case 'w':
if (strlen(access_new) > 0)
access_conflict = true;
access_new = "rw";
break;
case 'h':
case '?':
default:
return CMD_HELP;
}
}
if (!nodemap_name || !fileset_name)
return CMD_HELP;
/* Check for conflicting options and abort */
if (type_conflict) {
fprintf(stderr, "cannot specify both --alt and --primary\n");
return CMD_HELP;
}
if (access_conflict) {
fprintf(stderr, "cannot specify both --ro and --rw\n");
return CMD_HELP;
}
if (!fileset_name_new && strlen(type_new) == 0 &&
strlen(access_new) == 0) {
fprintf(stderr,
"must specify at least one of --rename, --alt, --primary, --ro, or --rw\n");
return CMD_HELP;
}
/* Format flags as <type>:<access> */
rc = snprintf(flags, sizeof(flags), "%s:%s", type_new, access_new);
if (rc < 0 || rc >= sizeof(flags)) {
fprintf(stderr, "cannot format fileset flags\n");
return CMD_HELP;
}
rc = nodemap_cmd(LCFG_NODEMAP_FILESET_MODIFY, false, NULL, 0, argv[0],
nodemap_name, fileset_name,
fileset_name_new ? fileset_name_new : "", flags, NULL);
if (rc != 0) {
fprintf(stderr,
"error: cannot '%s' with fileset '%s' on nodemap '%s': %s\n",
jt_cmdname(argv[0]), fileset_name, nodemap_name,
strerror(errno));
}
return rc;
}
/**
* jt_nodemap_set_sepol() - set SELinux policy info on a nodemap
* @argc: number of args
* @argv: variable string arguments
*
* --name nodemap name
* --sepol SELinux policy info
*
* Return 0 on success
*/
int jt_nodemap_set_sepol(int argc, char **argv)
{
char *nodemap_name = NULL;
char *sepol = NULL;
int c, rc = EXIT_SUCCESS;
static struct option long_options[] = {
{
.name = "help",
.has_arg = no_argument,
.val = 'h',
},
{
.name = "name",
.has_arg = required_argument,
.val = 'n',
},
{
.name = "sepol",
.has_arg = required_argument,
.val = 's',
},
{
.name = NULL,
}
};
while ((c = getopt_long(argc, argv, "hn:s:",
long_options, NULL)) != -1) {
switch (c) {
case 'n':
nodemap_name = optarg;
break;
case 's':
sepol = optarg;
break;
case 'h':
default:
return CMD_HELP;
}
}
if (!nodemap_name) {
fprintf(stderr, "nodemap_set_sepol: missing nodemap name\n");
return CMD_HELP;
}
if (!sepol) {
fprintf(stderr, "nodemap_set_sepol: missing sepol\n");
return CMD_HELP;
}
errno = -nodemap_cmd(LCFG_NODEMAP_SET_SEPOL, false, NULL, 0, argv[0],
nodemap_name, sepol, NULL);
if (errno) {
rc = EXIT_FAILURE;
perror(argv[0]);
}
return rc;
}
/**
* jt_nodemap_set_cap() - Define capabilities for regular users
* on the specified nodemap
* @argc: number of args
* @argv: variable string arguments
*
* --name nodemap name
* --caps user capabilities
* --type mask or set or off
*
* Return:
* * %0 on success
*/
int jt_nodemap_set_cap(int argc, char **argv)
{
char *nodemap_name = NULL;
char *param = NULL;
char *caps = NULL;
char *type = NULL;
int c, len, rc = 0;
struct option long_options[] = {
{ .val = 'c', .name = "caps", .has_arg = required_argument },
{ .val = 'c', .name = "capabilities", .has_arg = required_argument },
{ .val = 'h', .name = "help", .has_arg = no_argument },
{ .val = 'n', .name = "name", .has_arg = required_argument },
{ .val = 't', .name = "type", .has_arg = required_argument },
{ .name = NULL },
};
while ((c = getopt_long(argc, argv, "c:hn:t:",
long_options, NULL)) != -1) {
switch (c) {
case 'c':
caps = optarg;
break;
case 'n':
nodemap_name = optarg;
break;
case 't':
type = optarg;
break;
case 'h':
default:
return CMD_HELP;
}
}
if (!nodemap_name) {
fprintf(stderr, "nodemap_set_cap: missing nodemap name\n");
return CMD_HELP;
}
if (!type) {
fprintf(stderr, "nodemap_set_cap: missing caps type\n");
return CMD_HELP;
}
if (!caps && strcmp(type, "off") != 0) {
fprintf(stderr, "nodemap_set_cap: missing capabilities\n");
return CMD_HELP;
}
len = strlen(type) + 2;
if (caps)
len += strlen(caps);
param = malloc(len);
if (!param) {
fprintf(stderr, "nodemap_set_cap: cannot allocate param\n");
return -ENOMEM;
}
snprintf(param, len, "%s:%s", type, caps);
rc = nodemap_cmd(LCFG_NODEMAP_SET_CAPS, false, NULL, 0, argv[0],
nodemap_name, param, NULL);
free(param);
if (rc != 0) {
fprintf(stderr,
"error: %s: cannot set capabilities '%s' on nodemap '%s': %s\n",
jt_cmdname(argv[0]), caps, nodemap_name,
strerror(errno));
}
return rc;
}
/**
* jt_nodemap_modify() - modify a nodemap's behavior
* @argc: number of args
* @argv: variable string arguments
*
* --name nodemap name
* --property nodemap property to change
* admin, trusted, squash_uid, squash_gid.
* Can also be in the form of property=value
* --value value to set property
*
* Returns 0 on success
*/
int jt_nodemap_modify(int argc, char **argv)
{
enum lcfg_command_type cmd = 0;
char *nodemap_name = NULL;
char *param = NULL;
char *value = NULL;
char *delimiter = NULL;
bool double_value = false;
int c, rc = 0;
static struct option long_opts[] = {
{ .val = 'h', .name = "help", .has_arg = no_argument },
{ .val = 'n', .name = "name", .has_arg = required_argument },
{ .val = 'p', .name = "property", .has_arg = required_argument },
{ .val = 'v', .name = "value", .has_arg = required_argument },
{ .name = NULL } };
while ((c = getopt_long(argc, argv, "hn:p:v:",
long_opts, NULL)) != -1) {
switch (c) {
case 'n':
nodemap_name = optarg;
break;
case 'p':
param = optarg;
/* check for property=value format */
delimiter = strchr(param, '=');
if (!value && delimiter) {
*delimiter = '\0';
value = delimiter + 1;
/* reset if empty value */
if (*value == '\0')
value = NULL;
} else if (value && delimiter) {
double_value = true;
}
break;
case 'v':
if (value && delimiter)
double_value = true;
else
value = optarg;
break;
case 'h':
case '?':
default:
return CMD_HELP;
}
}
if (double_value) {
fprintf(stderr,
"error: %s: use of both '--property=<value>' and '--value <value>' is invalid\n",
jt_cmdname(argv[0]));
return CMD_HELP;
}
if (!nodemap_name) {
fprintf(stderr, "nodemap_modify: missing nodemap name\n");
return CMD_HELP;
}
if (!param) {
fprintf(stderr, "nodemap_modify: missing property name\n");
return CMD_HELP;
}
if (!value) {
fprintf(stderr, "nodemap_modify: missing value for property\n");
return CMD_HELP;
}
if (strcmp("admin", param) == 0) {
cmd = LCFG_NODEMAP_ADMIN;
} else if (strcmp("trusted", param) == 0) {
cmd = LCFG_NODEMAP_TRUSTED;
} else if (strcmp("deny_unknown", param) == 0) {
cmd = LCFG_NODEMAP_DENY_UNKNOWN;
} else if (strcmp("squash_uid", param) == 0) {
cmd = LCFG_NODEMAP_SQUASH_UID;
} else if (strcmp("squash_gid", param) == 0) {
cmd = LCFG_NODEMAP_SQUASH_GID;
} else if (strcmp("squash_projid", param) == 0) {
cmd = LCFG_NODEMAP_SQUASH_PROJID;
} else if (strcmp("map_mode", param) == 0) {
cmd = LCFG_NODEMAP_MAP_MODE;
} else if (strcmp("audit_mode", param) == 0) {
cmd = LCFG_NODEMAP_AUDIT_MODE;
} else if (strcmp("forbid_encryption", param) == 0) {
cmd = LCFG_NODEMAP_FORBID_ENCRYPT;
} else if (strcmp("child_raise_privileges", param) == 0) {
cmd = LCFG_NODEMAP_RAISE_PRIVS;
} else if (strcmp("readonly_mount", param) == 0) {
cmd = LCFG_NODEMAP_READONLY_MOUNT;
} else if (strcmp("rbac", param) == 0) {
cmd = LCFG_NODEMAP_RBAC;
} else if (strcmp("deny_mount", param) == 0) {
cmd = LCFG_NODEMAP_DENY_MOUNT;
} else if (strcmp("gssonly_identification", param) == 0) {
cmd = LCFG_NODEMAP_GSS_IDENTIFY;
} else {
fprintf(stderr,
"error: %s: nodemap_modify invalid property: %s\n",
jt_cmdname(argv[0]), param);
return CMD_HELP;
}
if (cmd == LCFG_NODEMAP_SQUASH_UID ||
cmd == LCFG_NODEMAP_SQUASH_GID ||
cmd == LCFG_NODEMAP_SQUASH_PROJID) {
int offset_limit;
int squash;
offset_limit = lcfg_get_nm_offset_limit(nodemap_name);
squash = strtol(value, NULL, 10);
if (errno == ERANGE)
squash = -1;
if (squash == 0 &&
(cmd == LCFG_NODEMAP_SQUASH_UID ||
cmd == LCFG_NODEMAP_SQUASH_GID)) {
errno = EINVAL;
fprintf(stderr,
"error: %s: cannot squash to ID 0 on nodemap '%s': %s\n",
jt_cmdname(argv[0]), nodemap_name,
strerror(errno));
return CMD_HELP;
}
if (offset_limit && squash >= offset_limit)
fprintf(stderr,
"Warning: it is not recommended to have a squash value outside of the offset range [ 0, %d ] as it will not be mapped properly.\n",
offset_limit - 1);
}
rc = nodemap_cmd(cmd, false, NULL, 0, argv[0], nodemap_name, param,
value, NULL);
if (rc != 0) {
fprintf(stderr,
"error: %s: cannot modify nodemap '%s' to param '%s': value '%s': %s\n",
jt_cmdname(argv[0]), nodemap_name, param, value,
strerror(errno));
}
return rc;
}
/**
* jt_nodemap_info() - Output information about nodemaps.
* @argc: number of args
* @argv: variable string arguments
*
* --name nodemap to present info about
* --property nodemap property to present
* --list list nodemap state, all nodemaps, and properties
*
* deprecated positional parameters:
* [list|nodemap_name|all] @list will list all nodemaps (default).
* Specifying a @nodemap_name will
* display info about that specific nodemap.
* @all will display info for all nodemaps.
*
* Returns 0 on success
*/
int jt_nodemap_info(int argc, char **argv)
{
char *nodemap_name = NULL;
char *property = NULL;
bool list = false;
char pattern[PATH_MAX];
bool is_active = false;
char *active_str = NULL;
size_t buflen;
glob_t param;
int c, i;
int rc = 0;
struct nodemap_info_param_desc {
const char *param;
const char *desc;
};
static const struct nodemap_info_param_desc param_desc[] = {
{ "admin_nodemap", "root is not squashed on policy group" },
{ "audit_mode",
"client can record FS access events to the Changelogs" },
{ "deny_mount", "disable client mounts" },
{ "deny_unknown", "deny access for unknown (squashed) users" },
{ "exports",
"list of client connections (NIDs) for this nodemap" },
{ "fileset", "fileset restrictions for this nodemap" },
{ "forbid_encryption",
"prevent clients from using encryption" },
{ "id", "unique identifier for this nodemap" },
{ "idmap", "identity mapping rules for UID/GID/PROJID translation" },
{ "map_mode", "identity mapping mode" },
{ "offset", "idmap range offset for identity translation" },
{ "ranges", "NID ranges assigned to this nodemap" },
{ "rbac", "role-based admin control settings" },
{ "readonly_mount", "force clients to mount read-only" },
{ "sepol", "SELinux policy for this nodemap" },
{ "squash_gid", "GID for unmapped users" },
{ "squash_projid", "project ID for unmapped projects" },
{ "squash_uid", "UID for unmapped users" },
{ "trusted_nodemap",
"accept client identities without mapping" },
};
static struct option long_opts[] = {
{ .val = 'l', .name = "list", .has_arg = no_argument },
{ .val = 'p', .name = "property", .has_arg = required_argument },
{ .val = 'n', .name = "name", .has_arg = required_argument },
{ .val = 'h', .name = "help", .has_arg = no_argument },
{ .name = NULL }
};
while ((c = getopt_long(argc, argv, "hln:p:", long_opts, NULL)) != -1) {
switch (c) {
case 'n':
nodemap_name = optarg;
break;
case 'p':
property = optarg;
break;
case 'l':
list = true;
break;
case 'h':
case '?':
default:
return CMD_HELP;
}
}
if ((nodemap_name || property || list) && optind < argc) {
fprintf(stderr,
"error: using both positional and named arguments is not allowed\n");
return CMD_HELP;
}
if ((nodemap_name || property) && list) {
fprintf(stderr,
"error: using both --list and --name or --property is not allowed\n");
return CMD_HELP;
}
/* Legacy positional arguments are handled here */
if (optind < argc) {
#if LUSTRE_VERSION_CODE > OBD_OCD_VERSION(2, 17, 53, 0)
fprintf(stdout,
"Positional parameters are deprecated. Please use --name and --property instead.\n");
#endif
if ((argc - optind) > 1) {
fprintf(stderr,
"error: only one positional parameter allowed\n");
return CMD_HELP;
}
if (strcmp("list", argv[optind]) == 0) {
rc = snprintf(pattern, sizeof(pattern), "nodemap.*");
if (rc < 0 || rc >= sizeof(pattern)) {
fprintf(stderr,
"error: setting list pattern failed\n");
return -EINVAL;
}
rc = jt_lcfg_listparam(3, (char * [3]) { "list_param",
"-D",
pattern });
return rc;
}
if (strcmp("all", argv[optind]) == 0) {
rc = snprintf(pattern, sizeof(pattern), "nodemap.*.*");
} else {
rc = snprintf(pattern, sizeof(pattern), "nodemap.%s.*",
argv[optind]);
}
if (rc < 0 || rc >= sizeof(pattern)) {
fprintf(stderr,
"error: get_param pattern too long.\n");
return -EINVAL;
}
rc = jt_lcfg_getparam(3, (char * [3]) { "get_param", "-N",
pattern });
if (rc == -ENOENT) {
fprintf(stderr,
"error: nodemap_info: cannot find nodemap or property %s\n",
argv[optind]);
}
return rc;
}
/* Handle -l argument here */
if (list) {
/* Get nodemap active state */
rc = cfs_get_param_paths(¶m, "nodemap/active");
if (rc) {
fprintf(stderr,
"error: cannot get nodemap active param: %s\n",
strerror(errno));
return rc;
}
rc = llapi_param_get_value(param.gl_pathv[0], &active_str,
&buflen);
if (rc || !active_str) {
fprintf(stderr,
"error: cannot get nodemap active state\n");
cfs_free_param_data(¶m);
return rc;
}
is_active = (active_str[0] == '1');
printf("Global nodemap state:\n\t%s\n",
is_active ? "active" : "inactive");
free(active_str);
cfs_free_param_data(¶m);
/* list all nodemaps */
printf("\nDefined nodemaps:\n");
rc = cfs_get_param_paths(¶m, "nodemap/*");
if (rc) {
fprintf(stderr, "error: cannot get nodemap list: %s\n",
strerror(errno));
return rc;
}
for (i = 0; i < param.gl_pathc; i++) {
/* move to last '/' to skip nodemap prefix */
nodemap_name = strrchr(param.gl_pathv[i], '/');
/* skip '/' and check nodemap isn't empty or "active" */
if (nodemap_name && *(++nodemap_name) &&
strcmp(nodemap_name, "active") != 0)
printf("\t%s\n", nodemap_name);
}
cfs_free_param_data(¶m);
/* list all nodemap parameters */
printf("\nAvailable nodemap parameters:\n");
for (i = 0; i < ARRAY_SIZE(param_desc); i++) {
printf("\t%-20s %s\n", param_desc[i].param,
param_desc[i].desc);
}
return rc;
}
/* Handle -n and -p arguments and default case here */
if (nodemap_name && property) {
rc = snprintf(pattern, sizeof(pattern), "nodemap.%s.%s",
nodemap_name, property);
} else if (nodemap_name) {
rc = snprintf(pattern, sizeof(pattern), "nodemap.%s.*",
nodemap_name);
} else if (property) {
rc = snprintf(pattern, sizeof(pattern), "nodemap.*.%s",
property);
} else {
rc = snprintf(pattern, sizeof(pattern), "nodemap.active");
if (rc < 0 || rc >= sizeof(pattern)) {
fprintf(stderr,
"error: setting active pattern failed\n");
return -EINVAL;
}
rc = jt_lcfg_getparam(2, (char * [2]) { "get_param",
pattern });
if (rc)
return rc;
rc = snprintf(pattern, sizeof(pattern), "nodemap.*.*");
}
if (rc < 0 || rc >= sizeof(pattern)) {
fprintf(stderr, "error: nodemap name or property too long\n");
return -EINVAL;
}
rc = jt_lcfg_getparam(2, (char * [2]) { "get_param", pattern });
return rc;
}
/**
* jt_nodemap_add_offset() - Add a nodemap's UID/GID/PROJID offset
* @argc: args count
* @argv: string arguments
*
* --name nodemap name
* --offset UID/GID/PROJID offset
* --limit number of maximum entries
*
* Returns 0 on success
*/
int jt_nodemap_add_offset(int argc, char **argv)
{
char *nodemap_name = NULL;
__u32 offset = 0;
__u32 limit = 0;
char param[24];
int rc = 0;
int c;
static struct option long_opts[] = {
{ .val = 'l', .name = "limit", .has_arg = required_argument },
{ .val = 'n', .name = "name", .has_arg = required_argument },
{ .val = 'o', .name = "offset", .has_arg = required_argument },
{ .name = NULL } };
while ((c = getopt_long(argc, argv, "l:n:o:",
long_opts, NULL)) != -1) {
switch (c) {
case 'l':
limit = strtol(optarg, NULL, 10);
if (errno == ERANGE) {
fprintf(stderr,
"Invalid limit value input: %u\n",
limit);
return -1;
}
break;
case 'n':
nodemap_name = optarg;
break;
case 'o':
offset = strtol(optarg, NULL, 10);
if (errno == ERANGE) {
fprintf(stderr,
"Invalid offset value input: %u\n",
offset);
return -1;
}
break;
}
}
if (!nodemap_name || !offset || !limit ||
offset <= 0 || offset >= UINT_MAX || errno != 0) {
fprintf(stderr, "%s: invalid nodemap '%s' offset '%s'\n",
jt_cmdname(argv[0]), nodemap_name, optarg);
return CMD_HELP;
}
/* user warnings for setting offset to 0 or less than 65536 */
if (offset < 65536)
fprintf(stderr,
"Warning: it is not recommended to have an offset before 65536 as the nobody/squash ids will not be mapped properly.\n");
/* user warning for setting limit to less than 65536 */
if (limit < 65536)
fprintf(stderr,
"Warning: it is not recommended to have a limit below 65536 as the nobody/squash ids will not be mapped properly.\n");
snprintf(param, sizeof(param), "%u+%u", offset, limit);
rc = nodemap_cmd(LCFG_NODEMAP_ADD_OFFSET, false, NULL, 0,
argv[0], nodemap_name, param, NULL);
if (rc == -ERANGE) {
fprintf(stderr,
"%s: cannot set offset %s to nodemap '%s' because it overlaps with existing offset: %s\n",
*argv, param, nodemap_name, strerror(-rc));
} else if (rc != 0) {
fprintf(stderr,
"%s: cannot set offset %s to nodemap '%s': %s\n",
*argv, param, nodemap_name, strerror(-rc));
}
return rc;
}
/**
* jt_nodemap_del_offset() - Delete a nodemap's UID/GID/PROJID offset
* @argc: args count
* @argv: string arguments
*
* --name nodemap name
*
* Returns 0 on success
*/
int jt_nodemap_del_offset(int argc, char **argv)
{
char *nodemap_name = NULL;
int rc = 0;
int c;
static struct option long_opts[] = {
{ .val = 'n', .name = "name", .has_arg = required_argument },
{ .name = NULL } };
while ((c = getopt_long(argc, argv, "n:",
long_opts, NULL)) != -1) {
switch (c) {
case 'n':
nodemap_name = optarg;
break;
}
}
if (!nodemap_name || errno != 0) {
fprintf(stderr, "%s: invalid nodemap '%s' offset '%s'\n",
jt_cmdname(argv[0]), nodemap_name, optarg);
return CMD_HELP;
}
rc = nodemap_cmd(LCFG_NODEMAP_DEL_OFFSET, false, NULL, 0,
argv[0], nodemap_name, NULL);
if (rc != 0) {
fprintf(stderr,
"%s: cannot del offset from nodemap '%s': %s\n",
*argv, nodemap_name, strerror(-rc));
}
return rc;
}
int jt_nodemap_add_idmap(int argc, char **argv)
{
enum lcfg_command_type cmd = 0;
char *nodemap_name = NULL;
char *idtype = NULL;
char *idmap = NULL;
char *fsid = NULL;
int fsid_val = -1;
int c, rc = EXIT_SUCCESS;
static struct option long_opts[] = {
{ .val = 'h', .name = "help", .has_arg = no_argument },
{ .val = 'i', .name = "idtype", .has_arg = required_argument },
{ .val = 'm', .name = "idmap", .has_arg = required_argument },
{ .val = 'n', .name = "name", .has_arg = required_argument },
{ .name = NULL } };
while ((c = getopt_long(argc, argv, "hi:m:n:",
long_opts, NULL)) != -1) {
switch (c) {
case 'i':
idtype = optarg;
break;
case 'm':
idmap = optarg;
break;
case 'n':
nodemap_name = optarg;
break;
case 'h':
default:
return CMD_HELP;
}
}
if (!nodemap_name) {
fprintf(stderr, "nodemap_add_idmap: missing nodemap name\n");
return CMD_HELP;
}
if (!idtype) {
fprintf(stderr, "nodemap_add_idmap: missing ID type\n");
return CMD_HELP;
}
if (!idmap) {
fprintf(stderr, "nodemap_add_idmap: missing ID map\n");
return CMD_HELP;
}
if (strcmp("uid", idtype) == 0) {
cmd = LCFG_NODEMAP_ADD_UIDMAP;
} else if (strcmp("gid", idtype) == 0) {
cmd = LCFG_NODEMAP_ADD_GIDMAP;
} else if (strcmp("projid", idtype) == 0) {
cmd = LCFG_NODEMAP_ADD_PROJIDMAP;
} else {
fprintf(stderr,
"nodemap_add_idmap: incorrect ID type, must be one of uid, gid, projid.\n");
return CMD_HELP;
}
fsid = strchr(idmap, ':');
if (fsid) {
fsid_val = strtol(fsid + 1, NULL, 10);
if (errno == ERANGE)
fsid_val = -1;
}
if (fsid_val != -1) {
int offset_limit;
offset_limit = lcfg_get_nm_offset_limit(nodemap_name);
if (offset_limit && fsid_val >= offset_limit)
fprintf(stderr,
"Warning: it is not recommended to map an id outside of the offset range [ 0, %d ] as it will not be mapped properly.\n",
offset_limit - 1);
}
errno = -nodemap_cmd(cmd, false, NULL, 0,
argv[0], nodemap_name, idmap, NULL);
if (errno) {
rc = EXIT_FAILURE;
perror(argv[0]);
}
return rc;
}
int jt_nodemap_del_idmap(int argc, char **argv)
{
enum lcfg_command_type cmd = 0;
char *nodemap_name = NULL;
char *idtype = NULL;
char *idmap = NULL;
int c, rc = EXIT_SUCCESS;
static struct option long_opts[] = {
{ .val = 'h', .name = "help", .has_arg = no_argument },
{ .val = 'i', .name = "idtype", .has_arg = required_argument },
{ .val = 'm', .name = "idmap", .has_arg = required_argument },
{ .val = 'n', .name = "name", .has_arg = required_argument },
{ .name = NULL } };
while ((c = getopt_long(argc, argv, "hi:m:n:",
long_opts, NULL)) != -1) {
switch (c) {
case 'i':
idtype = optarg;
break;
case 'm':
idmap = optarg;
break;
case 'n':
nodemap_name = optarg;
break;
case 'h':
default:
return CMD_HELP;
}
}
if (!nodemap_name) {
fprintf(stderr, "nodemap_del_idmap: missing nodemap name\n");
return CMD_HELP;
}
if (!idtype) {
fprintf(stderr, "nodemap_del_idmap: missing ID type\n");
return CMD_HELP;
}
if (!idmap) {
fprintf(stderr, "nodemap_del_idmap: missing ID map\n");
return CMD_HELP;
}
if (strcmp("uid", idtype) == 0) {
cmd = LCFG_NODEMAP_DEL_UIDMAP;
} else if (strcmp("gid", idtype) == 0) {
cmd = LCFG_NODEMAP_DEL_GIDMAP;
} else if (strcmp("projid", idtype) == 0) {
cmd = LCFG_NODEMAP_DEL_PROJIDMAP;
} else {
fprintf(stderr,
"nodemap_del_idmap: incorrect ID type, must be one of uid, gid, projid.\n");
return CMD_HELP;
}
errno = -nodemap_cmd(cmd, false, NULL, 0,
argv[0], nodemap_name, idmap, NULL);
if (errno) {
rc = EXIT_FAILURE;
perror(argv[0]);
}
return rc;
}
#else /* !HAVE_SERVER_SUPPORT */
int jt_nodemap_activate(int argc, char **argv)
{
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return -EOPNOTSUPP;
}
int jt_nodemap_new(int argc, char **argv)
{
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return -EOPNOTSUPP;
}
int jt_nodemap_del(int argc, char **argv)
{
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return -EOPNOTSUPP;
}
int jt_nodemap_modify(int argc, char **argv)
{
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return -EOPNOTSUPP;
}
int jt_nodemap_add_offset(int argc, char **argv)
{
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return -EOPNOTSUPP;
}
int jt_nodemap_del_offset(int argc, char **argv)
{
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return -EOPNOTSUPP;
}
int jt_nodemap_add_range(int argc, char **argv)
{
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return -EOPNOTSUPP;
}
int jt_nodemap_test_nid(int argc, char **argv)
{
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return -EOPNOTSUPP;
}
int jt_nodemap_del_range(int argc, char **argv)
{
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return -EOPNOTSUPP;
}
int jt_nodemap_add_idmap(int argc, char **argv)
{
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return -EOPNOTSUPP;
}
int jt_nodemap_del_idmap(int argc, char **argv)
{
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return -EOPNOTSUPP;
}
int jt_nodemap_test_id(int argc, char **argv)
{
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return -EOPNOTSUPP;
}
int jt_nodemap_set_fileset(int argc, char **argv)
{
fprintf(stderr, "error: %s: invalid ioctl\n", jt_cmdname(argv[0]));
return -EOPNOTSUPP;
}
int jt_nodemap_fileset_add(int argc, char **argv)
{
fprintf(stderr, "error: %s: invalid ioctl\n", jt_cmdname(argv[0]));
return -EOPNOTSUPP;
}
int jt_nodemap_fileset_del(int argc, char **argv)
{
fprintf(stderr, "error: %s: invalid ioctl\n", jt_cmdname(argv[0]));
return -EOPNOTSUPP;
}
int jt_nodemap_fileset_modify(int argc, char **argv)
{
fprintf(stderr, "error: %s: invalid ioctl\n", jt_cmdname(argv[0]));
return -EOPNOTSUPP;
}
int jt_nodemap_banlist_add(int argc, char **argv)
{
fprintf(stderr, "error: %s: invalid ioctl\n", jt_cmdname(argv[0]));
return -EOPNOTSUPP;
}
int jt_nodemap_banlist_del(int argc, char **argv)
{
fprintf(stderr, "error: %s: invalid ioctl\n", jt_cmdname(argv[0]));
return -EOPNOTSUPP;
}
int jt_nodemap_set_sepol(int argc, char **argv)
{
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return -EOPNOTSUPP;
}
int jt_nodemap_set_cap(int argc, char **argv)
{
fprintf(stderr, "error: %s: invalid ioctl\n", jt_cmdname(argv[0]));
return -EOPNOTSUPP;
}
int jt_nodemap_info(int argc, char **argv)
{
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return -EOPNOTSUPP;
}
#endif /* HAVE_SERVER_SUPPORT */
/*
* this function tranforms a rule [start-end/step] into an array
* of matching numbers
* supported forms are:
* [start] : just this number
* [start-end] : all numbers from start to end
* [start-end/step] : numbers from start to end with increment of step
* on return, format contains a printf format string which can be used
* to generate all the strings
*/
static int get_array_idx(char *rule, char *format, int **array)
{
char *start, *end, *ptr;
unsigned int lo, hi, step;
int array_sz = 0;
int i, array_idx;
int rc;
start = strchr(rule, '[');
end = strchr(rule, ']');
if ((!start) || (!end)) {
*array = malloc(sizeof(int));
if (!*array)
return 0;
strcpy(format, rule);
array_sz = 1;
return array_sz;
}
*start = '\0';
*end = '\0';
end++;
start++;
/* put in format the printf format (the rule without the range) */
sprintf(format, "%s%%.4x%s", rule, end);
array_idx = 0;
array_sz = 0;
*array = NULL;
/* loop on , separator */
do {
/* extract the 3 fields */
rc = sscanf(start, "%x-%x/%u", &lo, &hi, &step);
switch (rc) {
case 0:
goto err;
case 1: {
void *tmp;
array_sz++;
tmp = realloc(*array, array_sz * sizeof(int));
if (!tmp)
goto err;
*array = tmp;
(*array)[array_idx] = lo;
array_idx++;
break;
}
case 2: {
step = 1;
/* do not break to share code with case 3: */
}
case 3: {
void *tmp;
if ((hi < lo) || (step == 0))
goto err;
array_sz += (hi - lo) / step + 1;
tmp = realloc(*array, array_sz * sizeof(int));
if (!tmp)
goto err;
*array = tmp;
for (i = lo; i <= hi; i += step, array_idx++)
(*array)[array_idx] = i;
break;
}
}
ptr = strchr(start, ',');
if (ptr)
start = ptr + 1;
} while (ptr);
return array_sz;
err:
if (*array) {
free(*array);
*array = NULL;
}
return 0;
}
struct llog_pool_name {
char lpn_name[UUID_MAX];
struct list_head lpn_list;
};
struct llog_pool_list_data {
char lpld_fsname[LUSTRE_MAXFSNAME + 1];
char lpld_poolname[LOV_MAXPOOLNAME + 1];
bool lpld_exists;
struct list_head lpld_list_head;
};
/**
* llog_poollist_cb() - Callback to list pool information in llog
* @record: to llog record
* @data: to struct llog_pool_list_data
*
* - { index: 74, event: new_pool, device: tfs-clilov, fsname: tfs, pool: tmp }
* - { index: 77, event: add_pool, device: tfs-clilov, fsname: tfs, pool: tmp,
* ost: tfs-OST0000_UUID }
* - { index: 224, event: remove_pool, device: tfs-clilov, fsname: tfs,
* pool: tmp, ost: tfs-OST0003_UUID }
* - { index: 227, event: del_pool, device: tfs-clilov, fsname: tfs, pool: tmp }
*
* Return:
* * %0 on success
* * %negative on error
*/
static int llog_poollist_cb(char *record, void *data)
{
struct llog_pool_list_data *lpld = data;
char pool_filter[MAX_STRING_SIZE] = "";
char *new_record, *del_record, *del_pool;
const char *found;
char type[10] = "";
int filter_len, rc = 0;
filter_len = snprintf(pool_filter, sizeof(pool_filter), " fsname: %s,",
lpld->lpld_fsname);
if (lpld->lpld_poolname[0] == '\0') {
new_record = get_event_filter(LCFG_POOL_NEW);
del_record = get_event_filter(LCFG_POOL_DEL);
strncpy(type, " pool: ", sizeof(type));
} else {
filter_len += snprintf(pool_filter + filter_len,
sizeof(pool_filter) - filter_len,
" pool: %s", lpld->lpld_poolname);
new_record = get_event_filter(LCFG_POOL_ADD);
del_record = get_event_filter(LCFG_POOL_REM);
strncpy(type, " ost: ", sizeof(type));
}
del_pool = get_event_filter(LCFG_POOL_DEL);
if (!new_record || !del_record || !del_pool) {
rc = -ENOMEM;
goto out;
}
found = strstr(record, pool_filter);
if (found &&
(found[filter_len] == ' ' || found[filter_len] == ',')) {
struct llog_pool_name *tmp = NULL;
struct list_head *head = &lpld->lpld_list_head;
const char *name;
int name_len, type_len = strlen(type);
lpld->lpld_exists = true;
if (strstr(record, new_record)) {
name = strstr(record, type);
/* 2 bytes for " }" */
name_len = strlen(name) - type_len - 2;
if (name_len <= 0 || name_len > sizeof(tmp->lpn_name)) {
rc = -EINVAL;
goto out;
}
tmp = malloc(sizeof(struct llog_pool_name));
if (!tmp) {
rc = -ENOMEM;
goto out;
}
memset(tmp, 0, sizeof(struct llog_pool_name));
strncpy(tmp->lpn_name, name + type_len, name_len);
list_add_tail(&tmp->lpn_list, &lpld->lpld_list_head);
} else if (strstr(record, del_record)) {
name = strstr(record, type);
name_len = strlen(name) - type_len - 2;
list_for_each_entry(tmp, head, lpn_list) {
if (strncmp(tmp->lpn_name, name + type_len,
name_len) == 0 &&
tmp->lpn_name[name_len] == '\0') {
list_del(&tmp->lpn_list);
free(tmp);
break;
}
}
}
/* verify if the specified pool still exists */
if (lpld->lpld_poolname[0] && strstr(record, del_pool))
lpld->lpld_exists = false;
}
out:
if (new_record)
free(new_record);
if (del_record)
free(del_record);
if (del_pool)
free(del_pool);
return rc;
}
/**
* llog_poollist() - List pool information by config log
* @fsname: pointer to filesystem name
* @poolname: to pool name
*
* Return:
* * %0 on success
* * %negative on error
*/
int llog_poollist(char *fsname, char *poolname)
{
char logname[MAX_OBD_NAME] = {'\0'};
struct llog_pool_list_data lpld;
struct llog_pool_name *tmp;
struct list_head *head;
int rc = 0;
if (fsname && fsname[0] == '\0')
fsname = NULL;
if (!fsname)
return -EINVAL;
memset(&lpld, 0, sizeof(lpld));
INIT_LIST_HEAD(&lpld.lpld_list_head);
lpld.lpld_exists = false;
strncpy(lpld.lpld_fsname, fsname, sizeof(lpld.lpld_fsname) - 1);
if (poolname && poolname[0])
snprintf(lpld.lpld_poolname, sizeof(lpld.lpld_poolname), "%s",
poolname);
snprintf(logname, sizeof(logname), "%s-client", fsname);
rc = jt_llog_print_iter(logname, 0, -1, llog_poollist_cb, &lpld, false,
false);
if (poolname && poolname[0])
printf("Pool: %s.%s\n", fsname, poolname);
else
printf("Pools from %s:\n", fsname);
head = &lpld.lpld_list_head;
if (poolname && poolname[0] && !lpld.lpld_exists && list_empty(head))
return -ENOENT;
list_for_each_entry(tmp, head, lpn_list) {
if (poolname && poolname[0])
printf("%s\n", tmp->lpn_name);
else
printf("%s.%s\n", fsname, tmp->lpn_name);
list_del(&tmp->lpn_list);
}
return rc;
}
static bool get_pools_path(char *fsname)
{
glob_t path;
int rc;
rc = cfs_get_param_paths(&path, "lov/%s-*/pools", fsname);
if (!rc)
cfs_free_param_data(&path);
return (rc == 0);
}
#define POOL_LIST 0
static
int parse_pool_cmd_args(int argc, char **argv, bool *wait,
enum lcfg_command_type *cmd, char **fullpool,
char *fsname, char *poolname,
char ***ostargv, int *ostargc)
{
char *cmdname, *param, *ptr;
int fsname_len;
*wait = true;
*cmd = POOL_LIST;
*fullpool = NULL;
fsname[0] = '\0';
poolname[0] = '\0';
*ostargv = NULL;
*ostargc = 0;
if (argc < 1)
return CMD_NONE;
cmdname = *argv;
if (argc < 2)
return CMD_HELP;
argc--;
argv++;
if (strcmp(*argv, "--help") == 0 || strcmp(*argv, "-h") == 0)
return CMD_HELP;
if (strcmp(*argv, "--nowait") == 0 || strcmp(*argv, "-n") == 0) {
*wait = false;
argc--;
argv++;
}
if (!argc)
return CMD_HELP;
param = *argv;
*fullpool = param;
argc--;
argv++;
if (argc) {
*ostargv = argv;
*ostargc = argc;
}
if (strcmp("pool_new", cmdname) == 0)
*cmd = LCFG_POOL_NEW;
else if (strcmp("pool_destroy", cmdname) == 0)
*cmd = LCFG_POOL_DEL;
else if (strcmp("pool_remove", cmdname) == 0)
*cmd = LCFG_POOL_REM;
else if (strcmp("pool_add", cmdname) == 0)
*cmd = LCFG_POOL_ADD;
else if (strcmp("pool_list", cmdname) == 0)
*cmd = POOL_LIST;
else
return -EINVAL;
if ((*cmd == LCFG_POOL_REM || *cmd == LCFG_POOL_ADD) && !*ostargc)
return CMD_HELP;
ptr = strchr(param, '.');
if (!ptr && *cmd == POOL_LIST)
fsname_len = strlen(param);
else if (ptr)
fsname_len = ptr - param;
else
return -EINVAL;
if (fsname_len == 0)
return -EINVAL;
if (fsname_len > LUSTRE_MAXFSNAME) {
fprintf(stderr, "%s: fsname is too long\n", cmdname);
return -ENAMETOOLONG;
}
snprintf(fsname, fsname_len + 1, "%s", param);
if (!ptr)
return 0;
++ptr;
if (ptr[0] == '\0')
return -EINVAL;
if (strlen(ptr) > LOV_MAXPOOLNAME) {
fprintf(stderr, "%s: poolname is too long\n", cmdname);
return -ENAMETOOLONG;
}
snprintf(poolname, LOV_MAXPOOLNAME + 1, "%s", ptr);
if (lov_pool_is_reserved(poolname)) {
fprintf(stderr, "%s: poolname cannot be '%s'\n",
cmdname, poolname);
return -EINVAL;
}
if (*wait && (*cmd != POOL_LIST) && !combined_mgs_mds(fsname)) {
int min_wait = get_mgc_requeue_timeout_min();
*wait = false;
fprintf(stderr,
"Warning, standalone MGS for \"%s\", unable to check pool updates on clients.\n"
"Verify if pools are updated on a client (client sync delay: %d-%ds).\n\n",
fsname, min_wait, 2 * min_wait);
}
return 0;
}
struct pool_ost_cmd {
int rc;
char ostname[UUID_MAX];
};
static
int extract_ost_list(int argc, char **argv, char *fsname,
struct pool_ost_cmd **out_osts)
{
char format[2 * UUID_MAX];
int i;
int *array = NULL, array_sz;
struct pool_ost_cmd *ostlist = NULL;
int ostcnt = 0;
int rc;
if (argc < 1)
return -EINVAL;
/* generate full list of OSTs */
for (i = 0; i < argc; i++) {
int j, start;
struct pool_ost_cmd *tmp;
if (strlen(argv[i]) >= sizeof(format)) {
rc = -EINVAL;
goto err_freelist;
}
array_sz = get_array_idx(argv[i], format, &array);
if (array_sz == 0) {
rc = -EINVAL;
goto err_freelist;
}
start = ostcnt;
ostcnt += array_sz;
tmp = realloc(ostlist, ostcnt * sizeof(*ostlist));
if (!tmp) {
rc = -ENOMEM;
goto err_freearr;
}
ostlist = tmp;
for (j = 0; j < array_sz; j++) {
char tmp[UUID_MAX];
rc = snprintf(tmp, sizeof(tmp), format, array[j]);
if (rc < 0 || rc >= sizeof(tmp)) {
rc = -ENAMETOOLONG;
goto err_freearr;
}
rc = check_and_complete_ostname(fsname, tmp);
if (rc)
goto err_freearr;
strncpy(ostlist[start + j].ostname, tmp, UUID_MAX);
}
free(array);
}
*out_osts = ostlist;
return ostcnt;
err_freearr:
free(array);
err_freelist:
free(ostlist);
*out_osts = NULL;
fprintf(stderr, "Wrong format for OST list\n");
return rc;
}
int jt_pool_cmd(int argc, char **argv)
{
enum lcfg_command_type cmd;
bool wait_client;
char *fullpool;
char fsname[LUSTRE_MAXFSNAME + 1];
char poolname[LOV_MAXPOOLNAME + 1];
int i, rc;
int ostargc = 0;
char **ostargv = NULL;
struct pool_ost_cmd *cmds = NULL;
int cmds_nr = 0;
rc = parse_pool_cmd_args(argc, argv, &wait_client, &cmd, &fullpool,
fsname, poolname, &ostargv, &ostargc);
if (rc)
goto out;
switch (cmd) {
case LCFG_POOL_NEW:
case LCFG_POOL_DEL:
rc = pool_cmd(cmd, argv[0], fullpool, fsname, NULL);
if (!rc && wait_client)
check_pool_cmd_result(cmd, fsname, poolname, NULL);
break;
case LCFG_POOL_ADD:
case LCFG_POOL_REM:
rc = extract_ost_list(ostargc, ostargv, fsname, &cmds);
if (rc < 0)
goto out;
cmds_nr = rc;
rc = 0;
for (i = 0; i < cmds_nr && rc != -EFAULT; i++) {
cmds[i].rc = pool_cmd(cmd, argv[0], fullpool, fsname,
cmds[i].ostname);
rc = cmds[i].rc ? cmds[i].rc : rc;
}
/* check results */
for (i = 0; i < cmds_nr && rc != -EFAULT; i++) {
if (!rc && wait_client)
check_pool_cmd_result(cmd, fsname, poolname,
cmds[i].ostname);
}
free(cmds);
break;
default:
if (get_pools_path(fsname))
rc = llapi_poollist(fullpool);
else if (get_mgs_device() > 0)
rc = llog_poollist(fsname, poolname);
}
return rc;
out:
if (rc < 0)
fprintf(stderr, "%s: %s\n", argv[0], strerror(-rc));
return rc;
}
#ifdef HAVE_SERVER_SUPPORT
static void
lqa_list_print(const char *fsname, const char *lqa, const char *buf, int len)
{
__u32 *p;
int i = 0;
if (!lqa) {
printf("name:%s", len ? "" : " []");
for (; i < len / LQA_NAME_MAX; i++)
printf("%s%.*s", i ? ", " : " ", LQA_NAME_MAX,
buf + i * LQA_NAME_MAX);
} else {
printf("name: %s\nranges:%s", lqa, len ? "" : " []");
for (p = (__u32 *)buf; i < len / LQA_RANGE_SIZE; i++, p += 2)
printf("%s%u-%u", i ? ", " : " ", p[0], p[1]);
}
printf("\n");
}
int lqa_ioctl(enum lqa_cmd_type cmd, char *cmdname, char *fsname, char *lqaname,
__u32 start, __u32 end)
{
struct obd_ioctl_data data;
int rc, lqalen;
char rawbuf[MAX_IOC_BUFLEN], *buf = rawbuf;
char lqa_buf[65536];
if (!fsname)
return -EFAULT;
memset(&data, 0, sizeof(data));
rc = data.ioc_dev = get_qmt_device(fsname);
if (rc < 0) {
fprintf(stderr, "error: %s: can not get qmt device\n",
jt_cmdname(cmdname));
return rc;
}
memset(buf, 0, sizeof(rawbuf));
data.ioc_command = cmd;
if (lqaname) {
lqalen = strnlen(lqaname, LQA_NAME_MAX + 1);
if (lqalen > LQA_NAME_MAX) {
fprintf(stderr, "error: lqaname `%.*s` exceeds maximum length %u\n",
LQA_NAME_MAX, lqaname, LQA_NAME_MAX);
return -ENAMETOOLONG;
}
if (lqalen) {
data.ioc_inlbuf1 = lqaname;
data.ioc_inllen1 = lqalen + 1;
if (cmd == LQA_ADD || cmd == LQA_REM) {
data.ioc_u32_1 = start;
data.ioc_u32_2 = end;
}
}
}
if (cmd == LQA_LIST) {
data.ioc_pbuf2 = lqa_buf;
data.ioc_plen2 = sizeof(lqa_buf);
}
memset(buf, 0, sizeof(rawbuf));
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "error: %s: invalid ioctl: %s\n",
jt_cmdname(cmdname), strerror(-rc));
return rc;
}
rc = l_ioctl(OBD_DEV_ID, OBD_IOC_LQACTL, buf);
if (rc) {
fprintf(stderr, "error: %s: ioctl: %s\n",
jt_cmdname(cmdname), strerror(errno));
return -errno;
}
if (cmd == LQA_LIST) {
llapi_ioctl_unpack(&data, buf, sizeof(rawbuf));
lqa_list_print(fsname, lqaname, data.ioc_pbuf2, data.ioc_plen2);
}
return 0;
}
static inline int lqa_get_range(const char *range, __u32 *start, __u32 *end)
{
long tmp_start, tmp_end;
int rc;
if (range == NULL)
return -EINVAL;
rc = sscanf(range, "%ld-%ld", &tmp_start, &tmp_end);
switch (rc) {
case 1:
tmp_end = tmp_start;
break;
case 2:
break;
default:
return -EINVAL;
}
if (tmp_end < tmp_start || tmp_start < 0 || tmp_end < 0)
return -EINVAL;
if (tmp_end > UINT_MAX || tmp_start > UINT_MAX)
return -ERANGE;
*start = (__u32)tmp_start;
*end = (__u32)tmp_end;
return 0;
}
static inline int lqa_name_insane(const char *arg, int len)
{
const char *c;
int rc = 0;
if (strnlen(arg, len + 1) > len) {
rc = -ENAMETOOLONG;
fprintf(stderr, "lqa name '%.*s' is longer than %d: rc = %d\n",
len, arg, len, rc);
return rc;
}
for (c = arg; *c != '\0'; c++) {
if (isalnum(*c) || *c == '_')
continue;
rc = -EINVAL;
fprintf(stderr, "lqa name '%.*s' has illegal character '%c'(0x%02x): rc = %d\n",
len, arg, isprint(*c) ? *c : ' ', *c, rc);
return rc;
}
return rc;
}
static inline int lqa_cmd(int argc, char **argv, enum lqa_cmd_type cmd)
{
char *lqaname = NULL;
char *fsname = NULL;
__u32 start = 0, end = 0;
struct option long_opts[] = {
{ .val = 'h', .name = "help", .has_arg = no_argument },
{ .val = 'f', .name = "fsname", .has_arg = required_argument },
{ .val = 'n', .name = "name", .has_arg = required_argument },
{ .val = 'r', .name = "range", .has_arg = required_argument },
{ .name = NULL },
};
bool range_defined = false;
int rc, c;
while ((c = getopt_long(argc, argv, "hf:l:r:",
long_opts, NULL)) != -1) {
switch (c) {
case 'f':
fsname = optarg;
if (strnlen(fsname, LUSTRE_MAXFSNAME + 1) >
LUSTRE_MAXFSNAME) {
fprintf(stderr, "fsname is too long\n");
return -ENAMETOOLONG;
}
break;
case 'n':
rc = lqa_name_insane(optarg, LQA_NAME_MAX);
if (rc)
return rc;
lqaname = optarg;
break;
case 'r':
if (!(cmd == LQA_ADD || cmd == LQA_REM))
return CMD_HELP;
if (lqa_get_range(optarg, &start, &end)) {
fprintf(stderr, "range is insane\n");
return -EINVAL;
}
range_defined = true;
break;
case 'h':
fallthrough;
default:
return CMD_HELP;
}
}
if (!fsname)
return CMD_HELP;
if (!lqaname && cmd != LQA_LIST)
return CMD_HELP;
if (!range_defined && (cmd == LQA_ADD || cmd == LQA_REM))
return CMD_HELP;
rc = lqa_ioctl(cmd, argv[0], fsname, lqaname, start, end);
if (rc < 0)
fprintf(stderr, "%s: %s\n", argv[0], strerror(-rc));
return rc;
}
int lctl_lqa_new(int argc, char **argv)
{
return lqa_cmd(argc, argv, LQA_NEW);
}
int lctl_lqa_add(int argc, char **argv)
{
return lqa_cmd(argc, argv, LQA_ADD);
}
int lctl_lqa_rem(int argc, char **argv)
{
return lqa_cmd(argc, argv, LQA_REM);
}
int lctl_lqa_del(int argc, char **argv)
{
return lqa_cmd(argc, argv, LQA_DEL);
}
int lctl_lqa_list(int argc, char **argv)
{
return lqa_cmd(argc, argv, LQA_LIST);
}
static const char *barrier_status2name(enum barrier_status status)
{
switch (status) {
case BS_INIT:
return "init";
case BS_FREEZING_P1:
return "freezing_p1";
case BS_FREEZING_P2:
return "freezing_p2";
case BS_FROZEN:
return "frozen";
case BS_THAWING:
return "thawing";
case BS_THAWED:
return "thawed";
case BS_FAILED:
return "failed";
case BS_EXPIRED:
return "expired";
case BS_RESCAN:
return "rescan";
default:
return "unknown";
}
}
int jt_barrier_freeze(int argc, char **argv)
{
struct obd_ioctl_data data;
char rawbuf[MAX_IOC_BUFLEN], *buf = rawbuf;
struct barrier_ctl bc;
int rc;
if (argc < 2 || argc > 3)
return CMD_HELP;
memset(&data, 0, sizeof(data));
rc = data.ioc_dev = get_mgs_device();
if (rc < 0)
return rc;
memset(&bc, 0, sizeof(bc));
bc.bc_version = BARRIER_VERSION_V1;
bc.bc_cmd = BC_FREEZE;
if (argc == 3)
bc.bc_timeout = atoi(argv[2]);
if (bc.bc_timeout == 0)
bc.bc_timeout = BARRIER_TIMEOUT_DEFAULT;
if (strlen(argv[1]) > 8) {
fprintf(stderr,
"%s: fsname name %s is too long. It should not exceed 8.\n",
argv[0], argv[1]);
return -EINVAL;
}
strncpy(bc.bc_name, argv[1], sizeof(bc.bc_name));
data.ioc_inlbuf1 = (char *)&bc;
data.ioc_inllen1 = sizeof(bc);
memset(buf, 0, sizeof(rawbuf));
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "Fail to pack ioctl data: rc = %d.\n", rc);
return rc;
}
rc = llapi_ioctl_dev(OBD_DEV_ID, OBD_IOC_BARRIER_V2, buf);
if (rc < 0)
fprintf(stderr, "Fail to freeze barrier for %s: %s\n",
argv[1], strerror(errno));
return rc;
}
int jt_barrier_thaw(int argc, char **argv)
{
struct obd_ioctl_data data;
char rawbuf[MAX_IOC_BUFLEN], *buf = rawbuf;
struct barrier_ctl bc;
int rc;
if (argc != 2)
return CMD_HELP;
memset(&data, 0, sizeof(data));
rc = data.ioc_dev = get_mgs_device();
if (rc < 0)
return rc;
memset(&bc, 0, sizeof(bc));
bc.bc_version = BARRIER_VERSION_V1;
bc.bc_cmd = BC_THAW;
if (strlen(argv[1]) > 8) {
fprintf(stderr,
"fsname name %s is too long. It should not exceed 8.\n",
argv[1]);
return -EINVAL;
}
strncpy(bc.bc_name, argv[1], sizeof(bc.bc_name));
data.ioc_inlbuf1 = (char *)&bc;
data.ioc_inllen1 = sizeof(bc);
memset(buf, 0, sizeof(rawbuf));
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "Fail to pack ioctl data: rc = %d.\n", rc);
return rc;
}
rc = llapi_ioctl_dev(OBD_DEV_ID, OBD_IOC_BARRIER_V2, buf);
if (rc < 0)
fprintf(stderr, "Fail to thaw barrier for %s: %s\n",
argv[1], strerror(errno));
return rc;
}
int __jt_barrier_stat(const char *fsname, struct barrier_ctl *bc)
{
struct obd_ioctl_data data;
char rawbuf[MAX_IOC_BUFLEN], *buf = rawbuf;
int rc;
memset(&data, 0, sizeof(data));
rc = data.ioc_dev = get_mgs_device();
if (rc < 0)
return rc;
memset(bc, 0, sizeof(*bc));
bc->bc_version = BARRIER_VERSION_V1;
bc->bc_cmd = BC_STAT;
strncpy(bc->bc_name, fsname, sizeof(bc->bc_name) - 1);
data.ioc_inlbuf1 = (char *)bc;
data.ioc_inllen1 = sizeof(*bc);
memset(buf, 0, sizeof(rawbuf));
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "Fail to pack ioctl data: rc = %d.\n", rc);
return rc;
}
rc = llapi_ioctl_dev(OBD_DEV_ID, OBD_IOC_BARRIER_V2, buf);
if (rc < 0)
fprintf(stderr, "Fail to query barrier for %s: %s\n",
fsname, strerror(errno));
else
llapi_ioctl_unpack(&data, buf, sizeof(rawbuf));
return rc;
}
int jt_barrier_stat(int argc, char **argv)
{
struct barrier_ctl bc;
static struct option long_opt_barrier_stat[] = {
{
.val = 's',
.name = "state",
.has_arg = no_argument,
},
{ .val = 't',
.name = "timeout",
.has_arg = no_argument,
},
{
NULL
}
};
const char *name;
int index;
int opt;
int rc;
bool state = false;
bool timeout = false;
while ((opt = getopt_long(argc, argv, "st", long_opt_barrier_stat,
&index)) != EOF) {
switch (opt) {
case 's':
state = true;
break;
case 't':
timeout = true;
break;
default:
return CMD_HELP;
}
}
if (optind >= argc)
return CMD_HELP;
name = argv[optind];
if (strlen(name) > 8) {
fprintf(stderr,
"fsname name %s is too long. It should not exceed 8.\n",
name);
return -EINVAL;
}
rc = __jt_barrier_stat(name, &bc);
if (!rc) {
if (state && !timeout)
printf("%s\n", barrier_status2name(bc.bc_status));
else if (timeout && !state)
printf("%d\n",
(bc.bc_status == BS_FREEZING_P1 ||
bc.bc_status == BS_FREEZING_P2 ||
bc.bc_status == BS_FROZEN) ?
bc.bc_timeout : 0);
else
printf("state: %s\ntimeout: %d seconds\n",
barrier_status2name(bc.bc_status),
(bc.bc_status == BS_FREEZING_P1 ||
bc.bc_status == BS_FREEZING_P2 ||
bc.bc_status == BS_FROZEN) ?
bc.bc_timeout : 0);
}
return rc;
}
int jt_barrier_rescan(int argc, char **argv)
{
struct obd_ioctl_data data;
char rawbuf[MAX_IOC_BUFLEN], *buf = rawbuf;
struct barrier_ctl bc;
int rc;
if (argc < 2 || argc > 3)
return CMD_HELP;
memset(&data, 0, sizeof(data));
rc = data.ioc_dev = get_mgs_device();
if (rc < 0)
return rc;
memset(&bc, 0, sizeof(bc));
bc.bc_version = BARRIER_VERSION_V1;
bc.bc_cmd = BC_RESCAN;
if (argc == 3)
bc.bc_timeout = atoi(argv[2]);
if (bc.bc_timeout == 0)
bc.bc_timeout = BARRIER_TIMEOUT_DEFAULT;
if (strlen(argv[1]) > 8) {
fprintf(stderr,
"fsname name %s is too long. It should not exceed 8.\n",
argv[1]);
return -EINVAL;
}
strncpy(bc.bc_name, argv[1], sizeof(bc.bc_name));
data.ioc_inlbuf1 = (char *)&bc;
data.ioc_inllen1 = sizeof(bc);
memset(buf, 0, sizeof(rawbuf));
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "Fail to pack ioctl data: rc = %d.\n", rc);
return rc;
}
rc = llapi_ioctl_dev(OBD_DEV_ID, OBD_IOC_BARRIER_V2, buf);
if (rc < 0) {
fprintf(stderr, "Fail to rescan barrier bitmap for %s: %s\n",
argv[1], strerror(errno));
} else {
llapi_ioctl_unpack(&data, buf, sizeof(rawbuf));
printf("%u of %u MDT(s) in the filesystem %s are inactive\n",
bc.bc_absence, bc.bc_total, argv[1]);
}
return rc;
}
#endif /* HAVE_SERVER_SUPPORT */
int jt_get_obj_version(int argc, char **argv)
{
struct lu_fid fid;
struct obd_ioctl_data data;
__u64 version, id = ULLONG_MAX, group = ULLONG_MAX;
char rawbuf[MAX_IOC_BUFLEN], *buf = rawbuf, *fidstr;
int rc, c;
while ((c = getopt(argc, argv, "i:g:")) != -1) {
switch (c) {
case 'i':
id = strtoull(optarg, NULL, 0);
break;
case 'g':
group = strtoull(optarg, NULL, 0);
break;
default:
return CMD_HELP;
}
}
argc -= optind;
fidstr = *(argv + optind);
if (!(id != ULLONG_MAX && group != ULLONG_MAX && argc == 0) &&
!(id == ULLONG_MAX && group == ULLONG_MAX && argc == 1))
return CMD_HELP;
memset(&data, 0, sizeof(data));
data.ioc_dev = cur_device;
if (argc == 1) {
rc = llapi_fid_parse(fidstr, &fid, NULL);
if (rc) {
fprintf(stderr, "%s: error parsing FID '%s': %s\n",
jt_cmdname(argv[0]), fidstr, strerror(-rc));
return rc;
}
data.ioc_inlbuf1 = (char *)&fid;
data.ioc_inllen1 = sizeof(fid);
} else {
data.ioc_inlbuf3 = (char *)&id;
data.ioc_inllen3 = sizeof(id);
data.ioc_inlbuf4 = (char *)&group;
data.ioc_inllen4 = sizeof(group);
}
data.ioc_inlbuf2 = (char *)&version;
data.ioc_inllen2 = sizeof(version);
memset(buf, 0, sizeof(*buf));
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc) {
fprintf(stderr, "error: %s: packing ioctl arguments: %s\n",
jt_cmdname(argv[0]), strerror(-rc));
return rc;
}
rc = l_ioctl(OBD_DEV_ID, OBD_IOC_GET_OBJ_VERSION, buf);
if (rc) {
fprintf(stderr, "error: %s: ioctl: %s\n",
jt_cmdname(argv[0]), strerror(errno));
return -errno;
}
llapi_ioctl_unpack(&data, buf, sizeof(rawbuf));
printf("%#jx\n", (uintmax_t)version);
return 0;
}
#ifdef HAVE_SERVER_SUPPORT
int jt_changelog_register(int argc, char **argv)
{
struct option long_opts[] = {
{ .val = 'h', .name = "help", .has_arg = no_argument },
{ .val = 'm', .name = "mask", .has_arg = required_argument },
{ .val = 'n', .name = "nameonly", .has_arg = no_argument },
{ .val = 'u', .name = "user", .has_arg = required_argument },
{ .name = NULL } };
struct obd_ioctl_data data = { 0 };
char rawbuf[MAX_IOC_BUFLEN] = "";
char *buf = rawbuf;
char *device = lcfg_get_devname();
char *username = NULL, *usermask = NULL;
bool print_name_only = false;
int c;
int rc;
if (cur_device < 0 || !device)
return CMD_HELP;
while ((c = getopt_long(argc, argv, "hm:nu:", long_opts, NULL)) != -1) {
switch (c) {
case 'm':
usermask = strdup(optarg);
if (!usermask) {
fprintf(stderr,
"error: %s: %s: cannot copy '%s'\n",
jt_cmdname(argv[0]), strerror(errno),
optarg);
return -errno;
}
break;
case 'n':
print_name_only = true;
break;
case 'u':
username = strdup(optarg);
if (!username) {
fprintf(stderr,
"error: %s: %s: cannot copy '%s'\n",
jt_cmdname(argv[0]), strerror(errno),
optarg);
return -errno;
}
break;
case 'h':
default:
free(username);
free(usermask);
return CMD_HELP;
}
}
data.ioc_dev = cur_device;
if (username) {
data.ioc_inlbuf1 = username;
data.ioc_inllen1 = strlen(username) + 1;
}
if (usermask) {
data.ioc_inlbuf2 = usermask;
data.ioc_inllen2 = strlen(usermask) + 1;
}
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc < 0) {
fprintf(stderr, "error: %s: cannot pack ioctl: %s\n",
jt_cmdname(argv[0]), strerror(-rc));
goto out;
}
rc = l_ioctl(OBD_DEV_ID, OBD_IOC_CHANGELOG_REG, buf);
if (rc < 0) {
rc = -errno;
fprintf(stderr, "error: %s: %s\n", jt_cmdname(argv[0]),
rc == -EEXIST ? "User exists" : strerror(-rc));
goto out;
}
llapi_ioctl_unpack(&data, buf, sizeof(rawbuf));
if (data.ioc_u32_1 == 0) {
fprintf(stderr, "received invalid userid!\n");
rc = -EPROTO;
goto out;
}
if (print_name_only)
printf("%s%u%s%s\n", CHANGELOG_USER_PREFIX, data.ioc_u32_1,
username ? "-" : "", username ? : "");
else
printf("%s: Registered changelog userid '%s%u%s%s'\n",
device, CHANGELOG_USER_PREFIX, data.ioc_u32_1,
username ? "-" : "", username ? : "");
out:
free(usermask);
free(username);
return rc;
}
int jt_changelog_deregister(int argc, char **argv)
{
struct option long_opts[] = {
{ .val = 'h', .name = "help", .has_arg = no_argument },
{ .val = 'u', .name = "user", .has_arg = required_argument },
{ .name = NULL } };
struct obd_ioctl_data data = { 0 };
char rawbuf[MAX_IOC_BUFLEN] = "";
char *buf = rawbuf;
char *device = lcfg_get_devname();
char *username = NULL;
int id = 0;
int c, rc;
if (cur_device < 0 || !device)
return CMD_HELP;
while ((c = getopt_long(argc, argv, "hu:", long_opts, NULL)) != -1) {
switch (c) {
case 'u':
username = strdup(optarg);
if (!username) {
fprintf(stderr,
"error: %s: %s: cannot copy '%s'\n",
jt_cmdname(argv[0]), strerror(errno),
optarg);
return -errno;
}
break;
case 'h':
default:
free(username);
return CMD_HELP;
}
}
if (1 == optind && argc > 1) {
/* first check if pure ID was passed */
id = atoi(argv[optind]);
/* nameless cl<ID> format or cl<ID>-... format, only ID matters */
if (id == 0)
sscanf(argv[optind], CHANGELOG_USER_PREFIX"%d", &id);
/* no valid ID was parsed */
if (id <= 0) {
rc = -EINVAL;
fprintf(stderr,
"error: %s: expect <ID> or cl<ID>[-name] got '%s'\n",
strerror(-rc), argv[optind]);
return CMD_HELP;
}
optind++;
}
if (optind < argc || argc == 1) {
free(username);
return CMD_HELP;
}
data.ioc_dev = cur_device;
data.ioc_u32_1 = id;
if (username) {
data.ioc_inlbuf1 = username;
data.ioc_inllen1 = strlen(username) + 1;
}
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc < 0) {
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return rc;
}
rc = l_ioctl(OBD_DEV_ID, OBD_IOC_CHANGELOG_DEREG, buf);
if (rc < 0) {
rc = -errno;
fprintf(stderr, "error: %s: %s\n", jt_cmdname(argv[0]),
rc == -ENOENT ? "User not found" : strerror(-rc));
return rc;
}
llapi_ioctl_unpack(&data, buf, sizeof(rawbuf));
printf("%s: Deregistered changelog user #%u\n", device, data.ioc_u32_1);
return 0;
}
#else /* !HAVE_SERVER_SUPPORT */
int jt_changelog_register(int argc, char **argv)
{
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return -EOPNOTSUPP;
}
int jt_changelog_deregister(int argc, char **argv)
{
fprintf(stderr, "error: %s: invalid ioctl\n",
jt_cmdname(argv[0]));
return -EOPNOTSUPP;
}
#endif /* HAVE_SERVER_SUPPORT */
int jt_pcc_add(int argc, char **argv)
{
struct option long_opts[] = {
{ .val = 'p', .name = "param", .has_arg = required_argument },
{ .name = NULL } };
const char *mntpath;
const char *pccpath;
char *param = NULL;
char cmd[PATH_MAX];
int rc;
optind = 1;
while ((rc = getopt_long(argc, argv, "p:", long_opts, NULL)) != -1) {
switch (rc) {
case 'p':
param = optarg;
break;
default:
return CMD_HELP;
}
}
if (!param) {
fprintf(stderr, "%s: must specify the config param for PCC\n",
jt_cmdname(argv[0]));
return CMD_HELP;
}
if (optind + 2 != argc) {
fprintf(stderr,
"%s: must specify mount path and PCC path\n",
jt_cmdname(argv[0]));
return CMD_HELP;
}
mntpath = argv[optind++];
pccpath = argv[optind];
snprintf(cmd, PATH_MAX, "add %s %s", pccpath, param);
rc = llapi_pccdev_set(mntpath, cmd);
if (rc < 0)
fprintf(stderr, "%s: failed to run '%s' on '%s': %s\n",
jt_cmdname(argv[0]), cmd, mntpath, strerror(errno));
return rc;
}
int jt_pcc_del(int argc, char **argv)
{
static struct option long_opts[] = {
{ .val = 'k', .name = "keep-data", .has_arg = no_argument },
{ .val = 'v', .name = "verbose", .has_arg = no_argument },
{ .name = NULL } };
char fsname[PATH_MAX];
const char *mntpath;
const char *pccpath;
__u32 flags = PCC_CLEANUP_FL_NONE;
int verbose = LLAPI_MSG_INFO;
int rc;
int c;
while ((c = getopt_long(argc, argv, "kv", long_opts, NULL)) != -1) {
switch (c) {
case 'k':
flags = PCC_CLEANUP_FL_KEEP_DATA;
break;
case 'v':
verbose++;
break;
case '?':
return CMD_HELP;
default:
fprintf(stderr, "%s: option '%s' unrecognized\n",
argv[0], argv[optind - 1]);
return CMD_HELP;
}
}
if (optind + 2 != argc) {
fprintf(stderr, "%s: must specify mount path and PCC path\n",
jt_cmdname(argv[0]));
return CMD_HELP;
}
mntpath = argv[optind++];
pccpath = argv[optind];
rc = llapi_search_fsname(mntpath, fsname);
if (rc < 0) {
fprintf(stderr,
"%s: cannot find a Lustre filesystem mounted at '%s'\n",
jt_cmdname(argv[0]), mntpath);
return rc;
}
/* Set llapi message level */
llapi_msg_set_level(verbose);
rc = llapi_pcc_del(mntpath, pccpath, flags);
if (rc < 0)
fprintf(stderr, "%s: failed to delete '%s' on '%s': %s\n",
jt_cmdname(argv[0]), pccpath, mntpath, strerror(errno));
return rc;
}
int jt_pcc_clear(int argc, char **argv)
{
static struct option long_opts[] = {
{ .val = 'k', .name = "keep-data", .has_arg = no_argument },
{ .val = 'v', .name = "verbose", .has_arg = no_argument },
{ .name = NULL } };
char fsname[PATH_MAX];
const char *mntpath;
__u32 flags = PCC_CLEANUP_FL_NONE;
int verbose = LLAPI_MSG_INFO;
int rc;
int c;
while ((c = getopt_long(argc, argv, "kv", long_opts, NULL)) != -1) {
switch (c) {
case 'k':
flags = PCC_CLEANUP_FL_KEEP_DATA;
break;
case 'v':
verbose++;
break;
case '?':
return CMD_HELP;
default:
fprintf(stderr, "%s: option '%s' unrecognized\n",
argv[0], argv[optind - 1]);
return CMD_HELP;
}
}
if (optind + 1 != argc) {
fprintf(stderr, "%s: must speficy mount path\n",
jt_cmdname(argv[0]));
return CMD_HELP;
}
mntpath = argv[optind];
rc = llapi_search_fsname(mntpath, fsname);
if (rc < 0) {
fprintf(stderr,
"%s: cannot find a Lustre filesystem mounted at '%s'\n",
jt_cmdname(argv[0]), mntpath);
return rc;
}
/* Set llapi message level */
llapi_msg_set_level(verbose);
rc = llapi_pcc_clear(mntpath, flags);
if (rc < 0)
fprintf(stderr,
"%s: failed to remove all PCC backends on '%s': %s\n",
jt_cmdname(argv[0]), mntpath, strerror(errno));
return rc;
}
int jt_pcc_list(int argc, char **argv)
{
const char *mntpath;
int rc;
optind = 1;
if (argc != 2) {
fprintf(stderr, "%s: require 1 arguments\n",
jt_cmdname(argv[0]));
return CMD_HELP;
}
mntpath = argv[optind];
rc = llapi_pccdev_get(mntpath);
if (rc < 0)
fprintf(stderr, "%s: failed to run 'pcc list' on '%s': %s\n",
jt_cmdname(argv[0]), mntpath, strerror(errno));
return rc;
}