1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-10-28 11:55:55 +03:00
lvm2/tools/dmsetup.c

6069 lines
150 KiB
C
Raw Normal View History

/*
2004-03-30 23:08:57 +04:00
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004-2012 Red Hat, Inc. All rights reserved.
* Copyright (C) 2005-2007 NEC Corporation
*
2004-03-30 23:08:57 +04:00
* This file is part of the device-mapper userspace tools.
*
* It includes tree drawing code based on pstree: http://psmisc.sourceforge.net/
*
2004-03-30 23:08:57 +04:00
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU General Public License v.2.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "tool.h"
2005-05-17 00:46:46 +04:00
2008-10-30 20:24:04 +03:00
#include "dm-logging.h"
#include <ctype.h>
2003-11-13 16:14:28 +03:00
#include <dirent.h>
2005-05-17 00:46:46 +04:00
#include <sys/wait.h>
#include <sys/param.h>
#include <locale.h>
#include <langinfo.h>
2009-08-06 19:05:10 +04:00
#include <time.h>
#include <fcntl.h>
#include <sys/stat.h>
#ifdef UDEV_SYNC_SUPPORT
# include <sys/types.h>
# include <sys/ipc.h>
# include <sys/sem.h>
# include <libudev.h>
#endif
/* FIXME Unused so far */
#undef HAVE_SYS_STATVFS_H
#ifdef HAVE_SYS_STATVFS_H
# include <sys/statvfs.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
2015-08-17 16:11:00 +03:00
#ifdef HAVE_SYS_TIMERFD_H
# include <sys/timerfd.h>
#endif
2015-08-17 16:11:00 +03:00
#ifdef HAVE_TERMIOS_H
# include <termios.h>
#endif
#ifdef HAVE_GETOPTLONG
# include <getopt.h>
# define GETOPTLONG_FN(a, b, c, d, e) getopt_long((a), (b), (c), (d), (e))
# define OPTIND_INIT 0
#else
struct option {
};
extern int optind;
extern char *optarg;
# define GETOPTLONG_FN(a, b, c, d, e) getopt((a), (b), (c))
# define OPTIND_INIT 1
#endif
2005-05-17 00:46:46 +04:00
#ifndef TEMP_FAILURE_RETRY
# define TEMP_FAILURE_RETRY(expression) \
2015-08-05 07:03:33 +03:00
(__extension__ \
({ long int __result; \
do __result = (long int) (expression); \
while (__result == -1L && errno == EINTR); \
2005-05-17 00:46:46 +04:00
__result; }))
#endif
#ifdef __linux__
# include "kdev_t.h"
#else
# define MAJOR(x) major((x))
# define MINOR(x) minor((x))
# define MKDEV(x,y) makedev((x),(y))
#endif
2006-04-20 00:43:30 +04:00
#define LINE_SIZE 4096
2005-05-17 00:46:46 +04:00
#define ARGS_MAX 256
#define LOOP_TABLE_SIZE (PATH_MAX + 255)
#define DEFAULT_DM_DEV_DIR "/dev/"
#define DM_DEV_DIR_ENV_VAR_NAME "DM_DEV_DIR"
#define DM_UDEV_COOKIE_ENV_VAR_NAME "DM_UDEV_COOKIE"
/* FIXME Should be imported */
#ifndef DM_MAX_TYPE_NAME
# define DM_MAX_TYPE_NAME 16
#endif
/* FIXME Should be elsewhere */
#define SECTOR_SHIFT 9L
#define err(msg, x...) fprintf(stderr, msg "\n", ##x)
/* program_id used for dmstats-managed statistics regions */
#define DM_STATS_PROGRAM_ID "dmstats"
/*
* Basic commands this code implments.
*/
typedef enum {
DMSETUP_CMD = 0,
LOSETUP_CMD = 1,
DMLOSETUP_CMD = 2,
DMSTATS_CMD = 3,
DMSETUP_STATS_CMD = 4,
DEVMAP_NAME_CMD = 5
} cmd_name_t;
typedef enum {
DMSETUP_TYPE = 0,
LOSETUP_TYPE = 1,
STATS_TYPE = 2,
DEVMAP_NAME_TYPE = 3
} cmd_type_t;
#define DMSETUP_CMD_NAME "dmsetup"
#define LOSETUP_CMD_NAME "losetup"
#define DMLOSETUP_CMD_NAME "dmlosetup"
#define DMSTATS_CMD_NAME "dmstats"
#define DMSETUP_STATS_CMD_NAME "dmsetup stats"
#define DEVMAP_NAME_CMD_NAME "devmap_name"
static const struct {
cmd_name_t command;
const char name[14];
cmd_type_t type;
} _base_commands[] = {
{ DMSETUP_CMD, DMSETUP_CMD_NAME, DMSETUP_TYPE },
{ LOSETUP_CMD, LOSETUP_CMD_NAME, LOSETUP_TYPE },
{ DMLOSETUP_CMD, DMLOSETUP_CMD_NAME, LOSETUP_TYPE },
{ DMSTATS_CMD, DMSTATS_CMD_NAME, STATS_TYPE },
{ DMSETUP_STATS_CMD, DMSETUP_STATS_CMD_NAME, STATS_TYPE },
{ DEVMAP_NAME_CMD, DEVMAP_NAME_CMD_NAME, DEVMAP_NAME_TYPE },
};
static const int _num_base_commands = DM_ARRAY_SIZE(_base_commands);
/*
* We have only very simple switches ATM.
*/
enum {
READ_ONLY = 0,
ADD_NODE_ON_CREATE_ARG,
ADD_NODE_ON_RESUME_ARG,
ALL_DEVICES_ARG,
ALL_PROGRAMS_ARG,
ALL_REGIONS_ARG,
AREAS_ARG,
AREA_SIZE_ARG,
AUX_DATA_ARG,
CHECKS_ARG,
CLEAR_ARG,
COLS_ARG,
COUNT_ARG,
DEFERRED_ARG,
SELECT_ARG,
2005-05-17 00:46:46 +04:00
EXEC_ARG,
FORCE_ARG,
GID_ARG,
HELP_ARG,
INACTIVE_ARG,
INTERVAL_ARG,
LENGTH_ARG,
MANGLENAME_ARG,
2003-04-04 17:22:58 +04:00
MAJOR_ARG,
MINOR_ARG,
MODE_ARG,
2008-06-06 22:53:08 +04:00
NAMEPREFIXES_ARG,
NOFLUSH_ARG,
NOHEADINGS_ARG,
NOLOCKFS_ARG,
NOOPENCOUNT_ARG,
NOSUFFIX_ARG,
NOTABLE_ARG,
UDEVCOOKIE_ARG,
NOUDEVRULES_ARG,
NOUDEVSYNC_ARG,
2005-03-27 15:37:46 +04:00
OPTIONS_ARG,
PRECISE_ARG,
PROGRAM_ID_ARG,
RAW_ARG,
READAHEAD_ARG,
REGION_ID_ARG,
RETRY_ARG,
2008-06-25 02:53:48 +04:00
ROWS_ARG,
SEPARATOR_ARG,
SETUUID_ARG,
SHOWKEYS_ARG,
SORT_ARG,
START_ARG,
TABLE_ARG,
TARGET_ARG,
SEGMENTS_ARG,
TREE_ARG,
UID_ARG,
UNBUFFERED_ARG,
UNITS_ARG,
2008-06-25 00:16:47 +04:00
UNQUOTED_ARG,
UUID_ARG,
2003-01-22 00:25:51 +03:00
VERBOSE_ARG,
VERIFYUDEV_ARG,
VERSION_ARG,
YES_ARG,
NUM_SWITCHES
};
typedef enum {
DR_TASK = 1,
DR_INFO = 2,
DR_DEPS = 4,
DR_TREE = 8, /* Complete dependency tree required */
DR_NAME = 16,
DR_STATS = 32, /* Requires populated stats handle. */
DR_STATS_META = 64, /* Requires listed stats handle. */
} report_type_t;
typedef enum {
DN_DEVNO, /* Major and minor number pair */
DN_BLK, /* Block device name (e.g. dm-0) */
DN_MAP /* Map name (for dm devices only, equal to DN_BLK otherwise) */
} dev_name_t;
static cmd_name_t _base_command = DMSETUP_CMD; /* Default command is 'dmsetup' */
static cmd_type_t _base_command_type = DMSETUP_TYPE;
static int _switches[NUM_SWITCHES];
static int _int_args[NUM_SWITCHES];
static char *_string_args[NUM_SWITCHES];
static int _num_devices;
static char *_uuid;
static char *_table;
static char *_target;
static char *_command_to_exec; /* --exec <command> */
static const char *_command; /* dmsetup <command> */
static uint32_t _read_ahead_flags;
static uint32_t _udev_cookie;
static int _udev_only;
2005-11-09 17:10:50 +03:00
static struct dm_tree *_dtree;
static struct dm_report *_report;
static report_type_t _report_type;
static dev_name_t _dev_name_type;
static uint32_t _count = 1; /* count of repeating reports */
static struct dm_timestamp *_initial_timestamp = NULL;
static uint64_t _disp_factor = 512; /* display sizes in sectors */
static char _disp_units = 's';
const char *_program_id = DM_STATS_PROGRAM_ID; /* program_id used for reports. */
static int _stats_report_by_areas = 1; /* output per-area info for stats reports. */
/* report timekeeping */
static struct dm_timestamp *_cycle_timestamp = NULL;
static uint64_t _interval = 0; /* configured interval in nsecs */
static uint64_t _new_interval = 0; /* flag top-of-interval */
static uint64_t _last_interval = 0; /* approx. measured interval in nsecs */
static int _timer_fd = -1; /* timerfd file descriptor. */
/* Invalid fd value used to signal end-of-reporting. */
#define TIMER_STOPPED -2
#define NSEC_PER_USEC UINT64_C(1000)
#define NSEC_PER_MSEC UINT64_C(1000000)
#define NSEC_PER_SEC UINT64_C(1000000000)
/*
* Commands
*/
struct command;
#define CMD_ARGS const struct command *cmd, const char *subcommand, int argc, char **argv, struct dm_names *names, int multiple_devices
typedef int (*command_fn) (CMD_ARGS);
struct command {
const char *name;
const char *help;
int min_args;
int max_args;
int repeatable_cmd; /* Repeat to process device list? */
int has_subcommands; /* Command implements sub-commands. */
command_fn fn;
};
static int _parse_line(struct dm_task *dmt, char *buffer, const char *file,
int line)
{
char ttype[LINE_SIZE], *ptr, *comment;
unsigned long long start, size;
int n;
/* trim trailing space */
for (ptr = buffer + strlen(buffer) - 1; ptr >= buffer; ptr--)
if (!isspace((int) *ptr))
break;
ptr++;
*ptr = '\0';
/* trim leading space */
for (ptr = buffer; *ptr && isspace((int) *ptr); ptr++)
;
if (!*ptr || *ptr == '#')
return 1;
if (sscanf(ptr, "%llu %llu %s %n",
&start, &size, ttype, &n) < 3) {
err("Invalid format on line %d of table %s", line, file);
return 0;
}
ptr += n;
if ((comment = strchr(ptr, (int) '#')))
*comment = '\0';
if (!dm_task_add_target(dmt, start, size, ttype, ptr))
return 0;
return 1;
}
static int _parse_file(struct dm_task *dmt, const char *file)
{
char *buffer = NULL;
size_t buffer_size = 0;
FILE *fp;
int r = 0, line = 0;
/* one-line table on cmdline */
if (_table)
return _parse_line(dmt, _table, "", ++line);
/* OK for empty stdin */
if (file) {
if (!(fp = fopen(file, "r"))) {
err("Couldn't open '%s' for reading", file);
return 0;
}
} else
fp = stdin;
#ifndef HAVE_GETLINE
buffer_size = LINE_SIZE;
if (!(buffer = dm_malloc(buffer_size))) {
err("Failed to malloc line buffer.");
return 0;
}
while (fgets(buffer, (int) buffer_size, fp))
#else
while (getline(&buffer, &buffer_size, fp) > 0)
#endif
if (!_parse_line(dmt, buffer, file ? : "on stdin", ++line))
goto out;
r = 1;
out:
memset(buffer, 0, buffer_size);
#ifndef HAVE_GETLINE
dm_free(buffer);
#else
free(buffer);
#endif
if (file && fclose(fp))
fprintf(stderr, "%s: fclose failed: %s", file, strerror(errno));
return r;
}
struct dm_split_name {
2015-08-05 07:03:33 +03:00
char *subsystem;
char *vg_name;
char *lv_name;
char *lv_layer;
};
struct dmsetup_report_obj {
struct dm_task *task;
struct dm_info *info;
struct dm_task *deps_task;
struct dm_tree_node *tree_node;
struct dm_split_name *split_name;
struct dm_stats *stats;
};
static int _task_run(struct dm_task *dmt)
{
int r;
uint64_t delta;
if (_initial_timestamp)
dm_task_set_record_timestamp(dmt);
r = dm_task_run(dmt);
if (_initial_timestamp) {
delta = dm_timestamp_delta(dm_task_get_ioctl_timestamp(dmt), _initial_timestamp);
log_debug("Timestamp: %7" PRIu64 ".%09" PRIu64 " seconds", delta / NSEC_PER_SEC, delta % NSEC_PER_SEC);
}
return r;
}
static struct dm_task *_get_deps_task(int major, int minor)
{
struct dm_task *dmt;
struct dm_info info;
if (!(dmt = dm_task_create(DM_DEVICE_DEPS)))
return NULL;
if (!dm_task_set_major(dmt, major) ||
!dm_task_set_minor(dmt, minor))
goto err;
if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
goto err;
if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
goto err;
if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
goto err;
if (!_task_run(dmt))
goto err;
if (!dm_task_get_info(dmt, &info))
goto err;
if (!info.exists)
goto err;
return dmt;
err:
dm_task_destroy(dmt);
return NULL;
}
2009-06-04 00:44:42 +04:00
static char *_extract_uuid_prefix(const char *uuid, const int separator)
{
char *ptr = NULL;
char *uuid_prefix = NULL;
size_t len;
if (uuid)
2009-06-04 00:44:42 +04:00
ptr = strchr(uuid, separator);
len = ptr ? ptr - uuid : 0;
if (!(uuid_prefix = dm_malloc(len + 1))) {
log_error("Failed to allocate memory to extract uuid prefix.");
return NULL;
}
if (uuid)
memcpy(uuid_prefix, uuid, len);
uuid_prefix[len] = '\0';
return uuid_prefix;
}
2009-06-04 00:44:42 +04:00
static struct dm_split_name *_get_split_name(const char *uuid, const char *name,
int separator)
{
struct dm_split_name *split_name;
if (!(split_name = dm_malloc(sizeof(*split_name)))) {
log_error("Failed to allocate memory to split device name "
"into components.");
return NULL;
}
if (!(split_name->subsystem = _extract_uuid_prefix(uuid, separator))) {
dm_free(split_name);
2012-03-02 01:49:32 +04:00
return_NULL;
}
2012-03-02 01:49:32 +04:00
split_name->vg_name = split_name->lv_name =
split_name->lv_layer = (char *) "";
if (!strcmp(split_name->subsystem, "LVM") &&
(!(split_name->vg_name = dm_strdup(name)) ||
!dm_split_lvm_name(NULL, NULL, &split_name->vg_name,
2015-08-05 07:03:33 +03:00
&split_name->lv_name, &split_name->lv_layer)))
log_error("Failed to allocate memory to split LVM name "
"into components.");
return split_name;
}
static void _destroy_split_name(struct dm_split_name *split_name)
{
/*
* lv_name and lv_layer are allocated within the same block
* of memory as vg_name so don't need to be freed separately.
*/
if (!strcmp(split_name->subsystem, "LVM"))
dm_free(split_name->vg_name);
dm_free(split_name->subsystem);
dm_free(split_name);
}
/*
* Stats clock:
*
* Use either Linux timerfds or usleep to implement the reporting
* interval wait.
*
* _start_timer() - Start the timer running.
* _do_timer_wait() - Wait until the beginning of the next interval.
*
* _update_interval_times() - Update timestamps and interval estimate.
*/
/*
* Return the current interval number counting upwards from one.
*/
static uint64_t _interval_num(void)
{
return 1 + (uint64_t) _int_args[COUNT_ARG] - _count;
}
2015-08-17 16:11:00 +03:00
#ifdef HAVE_SYS_TIMERFD_H
static int _start_timerfd_timer(void)
{
struct itimerspec interval_timer;
time_t secs;
long nsecs;
log_debug("Using timerfd for interval timekeeping.");
/* timer running? */
if (_timer_fd != -1)
return 1;
memset(&interval_timer, 0, sizeof(interval_timer));
/* Use CLOCK_MONOTONIC to avoid warp on RTC adjustments. */
if ((_timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC)) < 0) {
log_error("Could not create timer: %s", strerror(errno));
return 0;
}
secs = (time_t) _interval / NSEC_PER_SEC;
nsecs = (long) _interval % NSEC_PER_SEC;
/* Must set interval and value to create an armed periodic timer. */
interval_timer.it_interval.tv_sec = secs;
interval_timer.it_interval.tv_nsec = nsecs;
interval_timer.it_value.tv_sec = secs;
interval_timer.it_value.tv_nsec = nsecs;
2015-08-18 16:28:56 +03:00
log_debug("Setting interval timer to: " FMTu64 "s %ldns", (uint64_t)secs, nsecs);
if (timerfd_settime(_timer_fd, 0, &interval_timer, NULL)) {
log_error("Could not set interval timer: %s", strerror(errno));
return 0;
}
return 1;
}
static int _do_timerfd_wait(void)
{
uint64_t expired;
ssize_t bytes;
if (_timer_fd < 0)
return 0;
/* read on timerfd returns a uint64_t in host byte order. */
bytes = read(_timer_fd, &expired, sizeof(expired));
if (bytes < 0) {
/* EBADF from invalid timerfd or EINVAL from too small buffer. */
log_error("Interval timer wait failed: %s",
strerror(errno));
return 0;
}
/* read(2) on a timerfd descriptor is guaranteed to return 8 bytes. */
if (bytes != 8)
log_error("Unexpected byte count on timerfd read: " FMTssize_t, bytes);
/* FIXME: attempt to rebase clock? */
if (expired > 1)
log_warn("WARNING: Try increasing --interval ("FMTu64
" missed timer events).", expired - 1);
/* Signal that a new interval has begun. */
_new_interval = 1;
/* Final interval? */
if (_count == 2) {
if (close(_timer_fd))
stack;
/* Tell _update_interval_times() to shut down. */
_timer_fd = TIMER_STOPPED;
}
return 1;
}
static int _start_timer(void)
{
return _start_timerfd_timer();
}
static int _do_timer_wait(void)
{
return _do_timerfd_wait();
}
#else /* !HAVE_SYS_TIMERFD_H */
static int _start_usleep_timer(void)
{
log_debug("Using usleep for interval timekeeping.");
return 1;
}
static int _do_usleep_wait(void)
{
static struct dm_timestamp *_last_sleep, *_now = NULL;
uint64_t this_interval;
int64_t delta_t;
/*
* Report clock: compensate for time spent in userspace and stats
* message ioctls by keeping track of the last wake time and
* adjusting the sleep interval accordingly.
*/
if (!_last_sleep && !_now) {
if (!(_last_sleep = dm_timestamp_alloc()))
goto_out;
if (!(_now = dm_timestamp_alloc()))
goto_out;
dm_timestamp_get(_now);
this_interval = _interval;
log_error("Using "FMTu64" as first interval.", this_interval);
} else {
dm_timestamp_get(_now);
delta_t = dm_timestamp_delta(_now, _last_sleep);
log_debug("Interval timer delta_t: "FMTi64, delta_t);
/* FIXME: usleep timer drift over large counts. */
/* adjust for time spent populating and reporting */
this_interval = 2 * _interval - delta_t;
log_debug("Using "FMTu64" as interval.", this_interval);
}
/* Signal that a new interval has begun. */
_new_interval = 1;
dm_timestamp_copy(_last_sleep, _now);
if (usleep(this_interval / NSEC_PER_USEC)) {
if (errno == EINTR)
log_error("Report interval interrupted by signal.");
if (errno == EINVAL)
log_error("Report interval too short.");
goto out;
}
if(_count == 2) {
dm_timestamp_destroy(_last_sleep);
dm_timestamp_destroy(_now);
}
return 1;
out:
return 0;
}
static int _start_timer(void)
{
return _start_usleep_timer();
}
static int _do_timer_wait(void)
{
return _do_usleep_wait();
}
#endif /* HAVE_SYS_TIMERFD_H */
static int _update_interval_times(void)
{
static struct dm_timestamp *this_timestamp = NULL;
uint64_t delta_t, interval_num = _interval_num();
int r = 0;
/*
* Clock shutdown for exit - nothing to do.
*/
if (_timer_fd == TIMER_STOPPED && !_cycle_timestamp)
return 1;
/*
* Current timestamp. If _new_interval is set this is used as
* the new cycle start timestamp.
*/
if (!this_timestamp) {
if (!(this_timestamp = dm_timestamp_alloc()))
return_0;
}
/*
* Take cycle timstamp as close as possible to ioctl return.
*
* FIXME: use per-region timestamp deltas for interval estimate.
*/
if (!dm_timestamp_get(this_timestamp))
goto_out;
/*
* Stats clock: maintain a single timestamp taken just after the
* call to dm_stats_populate() and take a delta between the current
* and last value to determine the sampling interval.
*
* A new interval is started when the _new_interval flag is set
* on return from _do_report_wait().
*
* The first interval is treated as a special case: since the
* time since the last clear of the counters is unknown (no
* previous timestamp exists) the duration is assumed to be the
* configured value.
*/
if (_cycle_timestamp)
/* Current delta_t: time from start of cycle to now. */
delta_t = dm_timestamp_delta(this_timestamp, _cycle_timestamp);
else {
_cycle_timestamp = dm_timestamp_alloc();
if (!_cycle_timestamp) {
log_error("Could not allocate timestamp object.");
goto out;
}
/* Pretend we have the configured interval. */
delta_t = _interval;
/* start the first cycle */
log_debug("Beginning first interval");
_new_interval = 1;
}
log_debug("Interval #%-4"PRIu64" time delta: %12"
PRIu64"ns", interval_num, delta_t);
if (_new_interval) {
/* Update timestamp and interval and clear _new_interval */
dm_timestamp_copy(_cycle_timestamp, this_timestamp);
_last_interval = delta_t;
_new_interval = 0;
/*
* Log interval duration and current error.
*/
log_debug("Interval #%-5"PRIu64" current err: %12"PRIi64"ns",
interval_num, ((int64_t)_last_interval - (int64_t)_interval));
log_debug("End interval #%-9"PRIu64" duration: %12"PRIu64"ns",
interval_num, _last_interval);
}
r = 1;
out:
if (!r || _timer_fd == TIMER_STOPPED) {
/* The _cycle_timestamp has not yet been allocated if we
* fail to obtain this_timestamp on the first interval.
*/
if (_cycle_timestamp)
dm_timestamp_destroy(_cycle_timestamp);
dm_timestamp_destroy(this_timestamp);
/* Clear timestamp pointers to signal shutdown. */
_cycle_timestamp = this_timestamp = NULL;
}
return r;
}
static int _display_info_cols(struct dm_task *dmt, struct dm_info *info)
2003-07-02 01:20:58 +04:00
{
struct dmsetup_report_obj obj;
int r = 0;
2003-07-02 01:20:58 +04:00
if (!info->exists) {
fprintf(stderr, "Device does not exist.\n");
return 0;
}
2003-07-02 01:20:58 +04:00
obj.task = dmt;
obj.info = info;
obj.deps_task = NULL;
obj.split_name = NULL;
obj.stats = NULL;
if (_report_type & DR_TREE)
if (!(obj.tree_node = dm_tree_find_node(_dtree, info->major, info->minor))) {
log_error("Cannot find node %d:%d.", info->major, info->minor);
goto out;
}
if (_report_type & DR_DEPS)
if (!(obj.deps_task = _get_deps_task(info->major, info->minor))) {
log_error("Cannot get deps for %d:%d.", info->major, info->minor);
goto out;
}
if (_report_type & DR_NAME)
if (!(obj.split_name = _get_split_name(dm_task_get_uuid(dmt),
dm_task_get_name(dmt), '-')))
goto_out;
/*
* Obtain statistics for the current reporting object and set
* the interval estimate used for stats rate conversion.
*/
if (_report_type & DR_STATS) {
if (!(obj.stats = dm_stats_create(DM_STATS_PROGRAM_ID)))
goto_out;
dm_stats_bind_devno(obj.stats, info->major, info->minor);
if (!dm_stats_populate(obj.stats, _program_id, DM_STATS_REGIONS_ALL))
goto out;
/* Update timestamps and handle end-of-interval accounting. */
_update_interval_times();
log_debug("Adjusted sample interval duration: %12"PRIu64"ns", _last_interval);
/* use measured approximation for calculations */
dm_stats_set_sampling_interval_ns(obj.stats, _last_interval);
}
/* Only a dm_stats_list is needed for DR_STATS_META reports. */
if (!obj.stats && (_report_type & DR_STATS_META)) {
if (!(obj.stats = dm_stats_create(DM_STATS_PROGRAM_ID)))
goto_out;
dm_stats_bind_devno(obj.stats, info->major, info->minor);
if (!dm_stats_list(obj.stats, _program_id))
goto out;
/* No regions to report */
if (!dm_stats_get_nr_regions(obj.stats))
goto out;
}
/*
* Walk any statistics regions contained in the current
* reporting object: for objects with a NULL stats handle,
* or a handle containing no registered regions, this loop
* always executes exactly once.
*/
dm_stats_walk_do(obj.stats) {
if (!dm_report_object(_report, &obj))
goto_out;
if (_stats_report_by_areas)
dm_stats_walk_next(obj.stats);
else
dm_stats_walk_next_region(obj.stats);
} dm_stats_walk_while(obj.stats);
r = 1;
out:
if (obj.deps_task)
dm_task_destroy(obj.deps_task);
if (obj.split_name)
_destroy_split_name(obj.split_name);
if (obj.stats)
dm_stats_destroy(obj.stats);
return r;
}
static void _display_info_long(struct dm_task *dmt, struct dm_info *info)
{
const char *uuid;
2007-11-30 17:59:57 +03:00
uint32_t read_ahead;
if (!info->exists) {
fprintf(stderr, "Device does not exist.\n");
2003-07-02 01:20:58 +04:00
return;
}
printf("Name: %s\n", dm_task_get_name(dmt));
printf("State: %s%s%s\n",
info->suspended ? "SUSPENDED" : "ACTIVE",
info->read_only ? " (READ-ONLY)" : "",
info->deferred_remove ? " (DEFERRED REMOVE)" : "");
2003-07-02 01:20:58 +04:00
2007-12-04 01:53:04 +03:00
/* FIXME Old value is being printed when it's being changed. */
2007-11-30 17:59:57 +03:00
if (dm_task_get_read_ahead(dmt, &read_ahead))
2007-12-04 01:48:36 +03:00
printf("Read Ahead: %" PRIu32 "\n", read_ahead);
if (!info->live_table && !info->inactive_table)
2003-07-02 01:20:58 +04:00
printf("Tables present: None\n");
else
printf("Tables present: %s%s%s\n",
info->live_table ? "LIVE" : "",
info->live_table && info->inactive_table ? " & " : "",
info->inactive_table ? "INACTIVE" : "");
2003-07-02 01:20:58 +04:00
if (info->open_count != -1)
printf("Open count: %d\n", info->open_count);
2003-07-02 01:20:58 +04:00
printf("Event number: %" PRIu32 "\n", info->event_nr);
printf("Major, minor: %d, %d\n", info->major, info->minor);
2003-07-02 01:20:58 +04:00
if (info->target_count != -1)
printf("Number of targets: %d\n", info->target_count);
2003-07-02 01:20:58 +04:00
if ((uuid = dm_task_get_uuid(dmt)) && *uuid)
printf("UUID: %s\n", uuid);
printf("\n");
2003-07-02 01:20:58 +04:00
}
2005-03-27 15:37:46 +04:00
static int _display_info(struct dm_task *dmt)
{
struct dm_info info;
if (!dm_task_get_info(dmt, &info))
2005-03-27 15:37:46 +04:00
return 0;
if (!_switches[COLS_ARG])
_display_info_long(dmt, &info);
else
/* FIXME return code */
_display_info_cols(dmt, &info);
2005-03-27 15:37:46 +04:00
return info.exists ? 1 : 0;
}
static int _set_task_device(struct dm_task *dmt, const char *name, int optional)
{
if (name) {
if (!dm_task_set_name(dmt, name))
return 0;
} else if (_switches[UUID_ARG]) {
if (!dm_task_set_uuid(dmt, _uuid))
return 0;
} else if (_switches[MAJOR_ARG] && _switches[MINOR_ARG]) {
if (!dm_task_set_major(dmt, _int_args[MAJOR_ARG]) ||
!dm_task_set_minor(dmt, _int_args[MINOR_ARG]))
return 0;
} else if (!optional) {
fprintf(stderr, "No device specified.\n");
return 0;
}
return 1;
}
static int _set_task_add_node(struct dm_task *dmt)
{
if (!dm_task_set_add_node(dmt, DEFAULT_DM_ADD_NODE))
return 0;
if (_switches[ADD_NODE_ON_RESUME_ARG] &&
!dm_task_set_add_node(dmt, DM_ADD_NODE_ON_RESUME))
return 0;
if (_switches[ADD_NODE_ON_CREATE_ARG] &&
!dm_task_set_add_node(dmt, DM_ADD_NODE_ON_CREATE))
return 0;
return 1;
}
static int _load(CMD_ARGS)
{
int r = 0;
struct dm_task *dmt;
const char *file = NULL;
const char *name = NULL;
if (_switches[NOTABLE_ARG]) {
err("--notable only available when creating new device\n");
return 0;
}
if (!_switches[UUID_ARG] && !_switches[MAJOR_ARG]) {
if (!argc) {
err("Please specify device.\n");
return 0;
}
name = argv[0];
argc--;
argv++;
} else if (argc > 1) {
err("Too many command line arguments.\n");
return 0;
}
if (argc == 1)
file = argv[0];
if (!(dmt = dm_task_create(DM_DEVICE_RELOAD)))
return 0;
2003-11-13 16:14:28 +03:00
if (!_set_task_device(dmt, name, 0))
goto out;
if (!_switches[NOTABLE_ARG] && !_parse_file(dmt, file))
2003-04-04 17:22:58 +04:00
goto out;
if (_switches[READ_ONLY] && !dm_task_set_ro(dmt))
goto out;
if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
goto out;
if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
goto out;
if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
goto out;
if (!_task_run(dmt))
goto out;
r = 1;
if (_switches[VERBOSE_ARG])
2005-03-27 15:37:46 +04:00
r = _display_info(dmt);
2003-07-02 01:20:58 +04:00
out:
dm_task_destroy(dmt);
return r;
}
static int _create(CMD_ARGS)
{
int r = 0;
struct dm_task *dmt;
const char *file = NULL;
uint32_t cookie = 0;
uint16_t udev_flags = 0;
if (argc == 2)
file = argv[1];
if (!(dmt = dm_task_create(DM_DEVICE_CREATE)))
return 0;
if (!dm_task_set_name(dmt, argv[0]))
goto out;
if (_switches[UUID_ARG] && !dm_task_set_uuid(dmt, _uuid))
goto out;
if (!_switches[NOTABLE_ARG] && !_parse_file(dmt, file))
goto out;
if (_switches[READ_ONLY] && !dm_task_set_ro(dmt))
goto out;
if (_switches[MAJOR_ARG] && !dm_task_set_major(dmt, _int_args[MAJOR_ARG]))
goto out;
if (_switches[MINOR_ARG] && !dm_task_set_minor(dmt, _int_args[MINOR_ARG]))
goto out;
if (_switches[UID_ARG] && !dm_task_set_uid(dmt, _int_args[UID_ARG]))
goto out;
if (_switches[GID_ARG] && !dm_task_set_gid(dmt, _int_args[GID_ARG]))
goto out;
if (_switches[MODE_ARG] && !dm_task_set_mode(dmt, _int_args[MODE_ARG]))
goto out;
if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
goto out;
if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
goto out;
if (_switches[READAHEAD_ARG] &&
!dm_task_set_read_ahead(dmt, _int_args[READAHEAD_ARG],
_read_ahead_flags))
goto out;
if (_switches[NOTABLE_ARG])
dm_udev_set_sync_support(0);
if (_switches[NOUDEVRULES_ARG])
udev_flags |= DM_UDEV_DISABLE_DM_RULES_FLAG |
DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG;
if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
goto out;
if (!_set_task_add_node(dmt))
2015-08-05 07:03:33 +03:00
goto out;
if (_udev_cookie)
cookie = _udev_cookie;
if (_udev_only)
udev_flags |= DM_UDEV_DISABLE_LIBRARY_FALLBACK;
if (!dm_task_set_cookie(dmt, &cookie, udev_flags) ||
!_task_run(dmt))
goto out;
r = 1;
out:
if (!_udev_cookie)
(void) dm_udev_wait(cookie);
if (r && _switches[VERBOSE_ARG])
2005-03-27 15:37:46 +04:00
r = _display_info(dmt);
dm_task_destroy(dmt);
return r;
}
static int _do_rename(const char *name, const char *new_name, const char *new_uuid) {
int r = 0;
struct dm_task *dmt;
uint32_t cookie = 0;
uint16_t udev_flags = 0;
if (!(dmt = dm_task_create(DM_DEVICE_RENAME)))
return 0;
/* FIXME Kernel doesn't support uuid or device number here yet */
if (!_set_task_device(dmt, name, 0))
goto out;
if (new_uuid) {
if (!dm_task_set_newuuid(dmt, new_uuid))
goto out;
2012-02-27 04:19:23 +04:00
} else if (!new_name || !dm_task_set_newname(dmt, new_name))
goto out;
if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
goto out;
if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
goto out;
if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
goto out;
if (_switches[NOUDEVRULES_ARG])
udev_flags |= DM_UDEV_DISABLE_DM_RULES_FLAG |
DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG;
if (_udev_cookie)
cookie = _udev_cookie;
if (_udev_only)
udev_flags |= DM_UDEV_DISABLE_LIBRARY_FALLBACK;
if (!dm_task_set_cookie(dmt, &cookie, udev_flags) ||
!_task_run(dmt))
goto out;
r = 1;
out:
if (!_udev_cookie)
(void) dm_udev_wait(cookie);
dm_task_destroy(dmt);
return r;
}
static int _rename(CMD_ARGS)
{
const char *name = (argc == 2) ? argv[0] : NULL;
return _switches[SETUUID_ARG] ? _do_rename(name, NULL, argv[argc - 1]) :
_do_rename(name, argv[argc - 1], NULL);
}
static int _message(CMD_ARGS)
2004-06-09 00:34:40 +04:00
{
int r = 0, i;
size_t sz = 1;
2004-06-09 00:34:40 +04:00
struct dm_task *dmt;
char *str;
const char *response;
uint64_t sector;
char *endptr;
2004-06-09 00:34:40 +04:00
if (!(dmt = dm_task_create(DM_DEVICE_TARGET_MSG)))
return 0;
if (_switches[UUID_ARG] || _switches[MAJOR_ARG]) {
if (!_set_task_device(dmt, NULL, 0))
goto out;
} else {
if (!_set_task_device(dmt, argv[0], 0))
goto out;
argc--;
argv++;
}
2004-06-09 00:34:40 +04:00
sector = strtoull(argv[0], &endptr, 10);
if (*endptr || endptr == argv[0]) {
err("invalid sector");
goto out;
}
if (!dm_task_set_sector(dmt, sector))
2004-06-09 00:34:40 +04:00
goto out;
argc--;
argv++;
if (argc <= 0)
err("No message supplied.\n");
2004-06-09 00:34:40 +04:00
for (i = 0; i < argc; i++)
sz += strlen(argv[i]) + 1;
if (!(str = dm_zalloc(sz))) {
err("message string allocation failed");
goto out;
}
2004-06-09 00:34:40 +04:00
for (i = 0; i < argc; i++) {
if (i)
strcat(str, " ");
strcat(str, argv[i]);
}
i = dm_task_set_message(dmt, str);
2004-06-09 00:34:40 +04:00
dm_free(str);
2004-06-09 00:34:40 +04:00
if (!i)
goto out;
if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
goto out;
if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
goto out;
if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
goto out;
if (!_task_run(dmt))
2004-06-09 00:34:40 +04:00
goto out;
if ((response = dm_task_get_message_response(dmt))) {
if (!*response || response[strlen(response) - 1] == '\n')
fputs(response, stdout);
else
puts(response);
}
2004-06-09 00:34:40 +04:00
r = 1;
out:
dm_task_destroy(dmt);
return r;
}
static int _setgeometry(CMD_ARGS)
{
int r = 0;
struct dm_task *dmt;
if (!(dmt = dm_task_create(DM_DEVICE_SET_GEOMETRY)))
return 0;
if (_switches[UUID_ARG] || _switches[MAJOR_ARG]) {
if (!_set_task_device(dmt, NULL, 0))
goto out;
} else {
if (!_set_task_device(dmt, argv[0], 0))
goto out;
argc--;
argv++;
}
if (!dm_task_set_geometry(dmt, argv[0], argv[1], argv[2], argv[3]))
goto out;
if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
goto out;
if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
goto out;
if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
goto out;
/* run the task */
if (!_task_run(dmt))
goto out;
r = 1;
out:
dm_task_destroy(dmt);
return r;
}
static int _splitname(CMD_ARGS)
2009-06-04 00:44:42 +04:00
{
struct dmsetup_report_obj obj = { NULL };
int r;
2009-06-04 00:44:42 +04:00
if (!(obj.split_name = _get_split_name((argc == 2) ? argv[1] : "LVM",
argv[0], '\0')))
2015-08-05 07:03:33 +03:00
return_0;
2009-06-04 00:44:42 +04:00
r = dm_report_object(_report, &obj);
_destroy_split_name(obj.split_name);
return r;
}
static uint32_t _get_cookie_value(const char *str_value)
{
unsigned long int value;
char *p;
errno = 0;
if (!(value = strtoul(str_value, &p, 0)) ||
*p ||
(value == ULONG_MAX && errno == ERANGE) ||
value > 0xFFFFFFFF) {
err("Incorrect cookie value");
return 0;
}
else
return (uint32_t) value;
}
static int _udevflags(CMD_ARGS)
{
uint32_t cookie;
uint16_t flags;
int i;
static const char *dm_flag_names[] = {"DISABLE_DM_RULES",
"DISABLE_SUBSYSTEM_RULES",
"DISABLE_DISK_RULES",
"DISABLE_OTHER_RULES",
"LOW_PRIORITY",
"DISABLE_LIBRARY_FALLBACK",
"PRIMARY_SOURCE",
0};
if (!(cookie = _get_cookie_value(argv[0])))
return 0;
flags = cookie >> DM_UDEV_FLAGS_SHIFT;
for (i = 0; i < DM_UDEV_FLAGS_SHIFT; i++)
if (1 << i & flags) {
if (i < DM_UDEV_FLAGS_SHIFT / 2 && dm_flag_names[i])
printf("DM_UDEV_%s_FLAG='1'\n", dm_flag_names[i]);
else if (i < DM_UDEV_FLAGS_SHIFT / 2)
/*
* This is just a fallback. Each new DM flag
* should have its symbolic name assigned.
*/
printf("DM_UDEV_FLAG%d='1'\n", i);
else
/*
* We can't assign symbolic names to subsystem
* flags. Their semantics vary based on the
* subsystem that is currently used.
*/
printf("DM_SUBSYSTEM_UDEV_FLAG%d='1'\n",
i - DM_UDEV_FLAGS_SHIFT / 2);
}
return 1;
}
static int _udevcomplete(CMD_ARGS)
{
uint32_t cookie;
if (!(cookie = _get_cookie_value(argv[0])))
return 0;
/*
* Strip flags from the cookie and use cookie magic instead.
* If the cookie has non-zero prefix and the base is zero then
* this one carries flags to control udev rules only and it is
* not meant to be for notification. Return with success in this
* situation.
*/
if (!(cookie &= ~DM_UDEV_FLAGS_MASK))
return 1;
cookie |= DM_COOKIE_MAGIC << DM_UDEV_FLAGS_SHIFT;
return dm_udev_complete(cookie);
}
#ifndef UDEV_SYNC_SUPPORT
static const char _cmd_not_supported[] = "Command not supported. Recompile with \"--enable-udev_sync\" to enable.";
static int _udevcreatecookie(CMD_ARGS)
{
log_error(_cmd_not_supported);
return 0;
}
static int _udevreleasecookie(CMD_ARGS)
{
log_error(_cmd_not_supported);
return 0;
}
static int _udevcomplete_all(CMD_ARGS)
{
log_error(_cmd_not_supported);
return 0;
}
static int _udevcookies(CMD_ARGS)
{
log_error(_cmd_not_supported);
return 0;
}
#else /* UDEV_SYNC_SUPPORT */
static int _set_up_udev_support(const char *dev_dir)
{
int dirs_diff;
const char *env;
size_t len = strlen(dev_dir), udev_dir_len = strlen(DM_UDEV_DEV_DIR);
if (_switches[NOUDEVSYNC_ARG])
dm_udev_set_sync_support(0);
if (!_udev_cookie) {
env = getenv(DM_UDEV_COOKIE_ENV_VAR_NAME);
if (env && *env && (_udev_cookie = _get_cookie_value(env)))
log_debug("Using udev transaction 0x%08" PRIX32
" defined by %s environment variable.",
_udev_cookie,
DM_UDEV_COOKIE_ENV_VAR_NAME);
}
else if (_switches[UDEVCOOKIE_ARG])
log_debug("Using udev transaction 0x%08" PRIX32
" defined by --udevcookie option.",
_udev_cookie);
/*
* Normally, there's always a fallback action by libdevmapper if udev
* has not done its job correctly, e.g. the nodes were not created.
* If using udev transactions by specifying existing cookie value,
* we need to disable node creation by libdevmapper completely,
* disabling any fallback actions, since any synchronisation happens
* at the end of the transaction only. We need to do this to prevent
* races between udev and libdevmapper but only in case udev "dev path"
* is the same as "dev path" used by libdevmapper.
*/
/*
* DM_UDEV_DEV_DIR always has '/' at its end.
* If the dev_dir does not have it, be sure
* to make the right comparison without the '/' char!
*/
if (dev_dir[len - 1] != '/')
udev_dir_len--;
dirs_diff = udev_dir_len != len ||
strncmp(DM_UDEV_DEV_DIR, dev_dir, len);
_udev_only = !dirs_diff && (_udev_cookie || !_switches[VERIFYUDEV_ARG]);
if (dirs_diff) {
log_debug("The path %s used for creating device nodes that is "
"set via DM_DEV_DIR environment variable differs from "
"the path %s that is used by udev. All warnings "
"about udev not working correctly while processing "
"particular nodes will be suppressed. These nodes "
"and symlinks will be managed in each directory "
"separately.", dev_dir, DM_UDEV_DEV_DIR);
dm_udev_set_checking(0);
}
return 1;
}
static int _udevcreatecookie(CMD_ARGS)
{
uint32_t cookie;
if (!dm_udev_create_cookie(&cookie))
return 0;
if (cookie)
printf("0x%08" PRIX32 "\n", cookie);
return 1;
}
static int _udevreleasecookie(CMD_ARGS)
{
if (argv[0] && !(_udev_cookie = _get_cookie_value(argv[0])))
return 0;
if (!_udev_cookie) {
log_error("No udev transaction cookie given.");
return 0;
}
return dm_udev_wait(_udev_cookie);
}
2011-03-30 01:56:53 +04:00
__attribute__((format(printf, 1, 2)))
static char _yes_no_prompt(const char *prompt, ...)
{
int c = 0, ret = 0;
va_list ap;
do {
if (c == '\n' || !c) {
va_start(ap, prompt);
vprintf(prompt, ap);
va_end(ap);
}
if ((c = getchar()) == EOF) {
ret = 'n';
break;
}
c = tolower(c);
if ((c == 'y') || (c == 'n'))
ret = c;
} while (!ret || c != '\n');
if (c != '\n')
printf("\n");
return ret;
}
static int _udevcomplete_all(CMD_ARGS)
{
int max_id, id, sid;
struct seminfo sinfo;
struct semid_ds sdata;
int counter = 0;
int skipped = 0;
unsigned age = 0;
time_t t;
if (argc == 1 && (sscanf(argv[0], "%u", &age) != 1)) {
log_error("Failed to read age_in_minutes parameter.");
return 0;
}
if (!_switches[YES_ARG]) {
log_warn("This operation will destroy all semaphores %s%.0d%swith keys "
"that have a prefix %" PRIu16 " (0x%" PRIx16 ").",
age ? "older than " : "", age, age ? " minutes " : "",
DM_COOKIE_MAGIC, DM_COOKIE_MAGIC);
if (_yes_no_prompt("Do you really want to continue? [y/n]: ") == 'n') {
log_print("Semaphores with keys prefixed by %" PRIu16
" (0x%" PRIx16 ") NOT destroyed.",
DM_COOKIE_MAGIC, DM_COOKIE_MAGIC);
return 1;
}
}
if ((max_id = semctl(0, 0, SEM_INFO, &sinfo)) < 0) {
log_sys_error("semctl", "SEM_INFO");
return 0;
}
for (id = 0; id <= max_id; id++) {
if ((sid = semctl(id, 0, SEM_STAT, &sdata)) < 0)
continue;
if (sdata.sem_perm.__key >> 16 == DM_COOKIE_MAGIC) {
t = time(NULL);
if (sdata.sem_ctime + age * 60 > t ||
sdata.sem_otime + age * 60 > t) {
skipped++;
continue;
}
if (semctl(sid, 0, IPC_RMID, 0) < 0) {
log_error("Could not cleanup notification semaphore "
"with semid %d and cookie value "
FMTu32 " (0x" FMTx32 ")", sid,
sdata.sem_perm.__key, sdata.sem_perm.__key);
continue;
}
counter++;
}
}
log_print("%d semaphores with keys prefixed by "
FMTu16 " (0x" FMTx16 ") destroyed. %d skipped.",
counter, DM_COOKIE_MAGIC, DM_COOKIE_MAGIC, skipped);
return 1;
}
static int _udevcookies(CMD_ARGS)
2009-08-06 19:05:10 +04:00
{
int max_id, id, sid;
struct seminfo sinfo;
struct semid_ds sdata;
int val;
char otime_str[26], ctime_str[26];
char *otimes, *ctimes;
2009-08-06 19:05:10 +04:00
if ((max_id = semctl(0, 0, SEM_INFO, &sinfo)) < 0) {
log_sys_error("sem_ctl", "SEM_INFO");
return 0;
}
printf("Cookie Semid Value Last semop time Last change time\n");
2009-08-06 19:05:10 +04:00
for (id = 0; id <= max_id; id++) {
if ((sid = semctl(id, 0, SEM_STAT, &sdata)) < 0)
continue;
if (sdata.sem_perm.__key >> 16 == DM_COOKIE_MAGIC) {
if ((val = semctl(sid, 0, GETVAL)) < 0) {
log_error("semid %d: sem_ctl failed for "
"cookie 0x%" PRIx32 ": %s",
sid, sdata.sem_perm.__key,
strerror(errno));
continue;
}
if ((otimes = ctime_r((const time_t *) &sdata.sem_otime, (char *)&otime_str)))
otime_str[strlen(otimes)-1] = '\0';
if ((ctimes = ctime_r((const time_t *) &sdata.sem_ctime, (char *)&ctime_str)))
ctime_str[strlen(ctimes)-1] = '\0';
2009-08-06 19:05:10 +04:00
printf("0x%-10x %-10d %-10d %s %s\n", sdata.sem_perm.__key,
sid, val, otimes ? : "unknown",
ctimes? : "unknown");
2009-08-06 19:05:10 +04:00
}
}
return 1;
}
#endif /* UDEV_SYNC_SUPPORT */
2009-08-06 19:05:10 +04:00
static int _version(CMD_ARGS)
{
char version[80];
if (dm_get_library_version(version, sizeof(version)))
printf("Library version: %s\n", version);
2006-01-03 23:53:57 +03:00
if (!dm_driver_version(version, sizeof(version)))
return 0;
printf("Driver version: %s\n", version);
/* don't output column headings for 'dmstats version'. */
if (_report) {
dm_report_free(_report);
_report = NULL;
}
2005-10-17 22:05:39 +04:00
return 1;
}
2004-01-23 17:09:33 +03:00
static int _simple(int task, const char *name, uint32_t event_nr, int display)
{
uint32_t cookie = 0;
uint16_t udev_flags = 0;
int udev_wait_flag = task == DM_DEVICE_RESUME ||
task == DM_DEVICE_REMOVE;
int r = 0;
struct dm_task *dmt;
if (!(dmt = dm_task_create(task)))
return 0;
if (!_set_task_device(dmt, name, 0))
goto out;
2004-01-23 17:09:33 +03:00
if (event_nr && !dm_task_set_event_nr(dmt, event_nr))
goto out;
if (_switches[NOFLUSH_ARG] && !dm_task_no_flush(dmt))
goto out;
if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
goto out;
if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
goto out;
if (_switches[NOLOCKFS_ARG] && !dm_task_skip_lockfs(dmt))
goto out;
if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
goto out;
/* FIXME: needs to coperate with udev */
if (!_set_task_add_node(dmt))
2015-08-05 07:03:33 +03:00
goto out;
if (_switches[READAHEAD_ARG] &&
!dm_task_set_read_ahead(dmt, _int_args[READAHEAD_ARG],
_read_ahead_flags))
goto out;
if (_switches[NOUDEVRULES_ARG])
udev_flags |= DM_UDEV_DISABLE_DM_RULES_FLAG |
DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG;
if (_udev_cookie)
cookie = _udev_cookie;
if (_udev_only)
udev_flags |= DM_UDEV_DISABLE_LIBRARY_FALLBACK;
if (udev_wait_flag && !dm_task_set_cookie(dmt, &cookie, udev_flags))
goto out;
if (_switches[RETRY_ARG] && task == DM_DEVICE_REMOVE)
dm_task_retry_remove(dmt);
if (_switches[DEFERRED_ARG] && (task == DM_DEVICE_REMOVE || task == DM_DEVICE_REMOVE_ALL))
dm_task_deferred_remove(dmt);
r = _task_run(dmt);
out:
if (!_udev_cookie && udev_wait_flag)
2009-08-03 22:01:45 +04:00
(void) dm_udev_wait(cookie);
if (r && display && _switches[VERBOSE_ARG])
r = _display_info(dmt);
dm_task_destroy(dmt);
return r;
}
static int _suspend(CMD_ARGS)
{
return _simple(DM_DEVICE_SUSPEND, argc ? argv[0] : NULL, 0, 1);
}
static int _resume(CMD_ARGS)
{
return _simple(DM_DEVICE_RESUME, argc ? argv[0] : NULL, 0, 1);
2003-07-02 01:20:58 +04:00
}
static int _clear(CMD_ARGS)
2003-07-02 01:20:58 +04:00
{
return _simple(DM_DEVICE_CLEAR, argc ? argv[0] : NULL, 0, 1);
}
static int _wait(CMD_ARGS)
{
const char *name = NULL;
if (!_switches[UUID_ARG] && !_switches[MAJOR_ARG]) {
if (!argc) {
err("No device specified.");
return 0;
}
name = argv[0];
argc--, argv++;
}
return _simple(DM_DEVICE_WAITEVENT, name,
(argc) ? (uint32_t) atoi(argv[argc - 1]) : 0, 1);
}
static int _process_all(const struct command *cmd, const char *subcommand, int argc, char **argv, int silent,
int (*fn) (CMD_ARGS))
{
2003-11-13 16:14:28 +03:00
int r = 1;
struct dm_names *names;
unsigned next = 0;
struct dm_task *dmt;
if (!(dmt = dm_task_create(DM_DEVICE_LIST)))
return 0;
if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
goto out;
if (!_task_run(dmt)) {
2003-11-13 16:14:28 +03:00
r = 0;
goto out;
2003-11-13 16:14:28 +03:00
}
2003-11-13 16:14:28 +03:00
if (!(names = dm_task_get_names(dmt))) {
r = 0;
goto out;
2003-11-13 16:14:28 +03:00
}
if (!names->dev) {
2006-06-18 15:51:46 +04:00
if (!silent)
printf("No devices found\n");
goto out;
}
do {
2010-12-20 16:37:26 +03:00
names = (struct dm_names *)((char *) names + next);
if (!fn(cmd, subcommand, argc, argv, names, 1))
r = 0;
next = names->next;
} while (next);
out:
dm_task_destroy(dmt);
return r;
}
static uint64_t _get_device_size(const char *name)
{
uint64_t start, length, size = UINT64_C(0);
struct dm_info info;
char *target_type, *params;
struct dm_task *dmt;
2006-06-18 15:51:46 +04:00
void *next = NULL;
if (!(dmt = dm_task_create(DM_DEVICE_TABLE)))
return 0;
2006-06-18 15:51:46 +04:00
if (!_set_task_device(dmt, name, 0))
goto out;
if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
goto out;
if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
goto out;
if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
goto out;
if (!_task_run(dmt))
goto out;
if (!dm_task_get_info(dmt, &info) || !info.exists)
goto out;
do {
next = dm_get_next_target(dmt, next, &start, &length,
&target_type, &params);
size += length;
} while (next);
out:
dm_task_destroy(dmt);
return size;
}
static int _error_device(CMD_ARGS)
{
struct dm_task *dmt;
const char *name;
uint64_t size;
2006-06-18 15:51:46 +04:00
int r = 0;
name = names ? names->name : argv[0];
size = _get_device_size(name);
if (!(dmt = dm_task_create(DM_DEVICE_RELOAD)))
return 0;
2006-06-18 15:51:46 +04:00
if (!_set_task_device(dmt, name, 0))
goto error;
2006-06-18 15:51:46 +04:00
if (!dm_task_add_target(dmt, UINT64_C(0), size, "error", ""))
goto error;
if (_switches[READ_ONLY] && !dm_task_set_ro(dmt))
goto error;
if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
goto error;
if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
goto error;
if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
goto error;
if (!_task_run(dmt))
goto error;
2006-06-18 15:51:46 +04:00
if (!_simple(DM_DEVICE_RESUME, name, 0, 0)) {
_simple(DM_DEVICE_CLEAR, name, 0, 0);
goto error;
2006-06-18 15:51:46 +04:00
}
2006-06-18 15:51:46 +04:00
r = 1;
error:
dm_task_destroy(dmt);
2006-06-18 15:51:46 +04:00
return r;
}
static int _remove(CMD_ARGS)
{
if (_switches[FORCE_ARG] && argc) {
/*
* 'remove --force' option is doing 2 operations on the same device
* this is not compatible with the use of --udevcookie/DM_UDEV_COOKIE.
* Udevd collision could be partially avoided with --retry.
*/
if (_udev_cookie)
log_warn("WARNING: Use of cookie and --force is not compatible.");
(void) _error_device(cmd, NULL, argc, argv, NULL, 0);
}
return _simple(DM_DEVICE_REMOVE, argc ? argv[0] : NULL, 0, 0);
}
static int _count_devices(CMD_ARGS)
{
_num_devices++;
return 1;
}
static int _remove_all(CMD_ARGS)
{
int r;
/* Remove all closed devices */
r = _simple(DM_DEVICE_REMOVE_ALL, "", 0, 0) | dm_mknodes(NULL);
if (!_switches[FORCE_ARG])
return r;
_num_devices = 0;
r |= _process_all(cmd, NULL, argc, argv, 1, _count_devices);
/* No devices left? */
if (!_num_devices)
return r;
r |= _process_all(cmd, NULL, argc, argv, 1, _error_device);
r |= _simple(DM_DEVICE_REMOVE_ALL, "", 0, 0) | dm_mknodes(NULL);
_num_devices = 0;
r |= _process_all(cmd, NULL, argc, argv, 1, _count_devices);
if (!_num_devices)
return r;
2006-06-18 15:51:46 +04:00
fprintf(stderr, "Unable to remove %d device(s).\n", _num_devices);
return r;
}
static void _display_dev(struct dm_task *dmt, const char *name)
2005-05-16 20:04:34 +04:00
{
struct dm_info info;
if (dm_task_get_info(dmt, &info))
printf("%s\t(%u, %u)\n", name, info.major, info.minor);
}
static int _mknodes(CMD_ARGS)
2005-05-17 00:46:46 +04:00
{
return dm_mknodes(argc ? argv[0] : NULL);
2005-05-17 00:46:46 +04:00
}
static int _exec_command(const char *name)
2005-05-17 00:46:46 +04:00
{
int n;
static char path[PATH_MAX];
static char *args[ARGS_MAX + 1];
static int argc = 0;
char *c;
pid_t pid;
if (argc < 0)
return 0;
2005-10-17 02:57:20 +04:00
if (!dm_mknodes(name))
2005-05-17 00:46:46 +04:00
return 0;
n = snprintf(path, sizeof(path), "%s/%s", dm_dir(), name);
if (n < 0 || n > (int) sizeof(path) - 1)
2005-05-17 00:46:46 +04:00
return 0;
if (!argc) {
c = _command_to_exec;
2005-05-17 00:46:46 +04:00
while (argc < ARGS_MAX) {
while (*c && isspace(*c))
c++;
if (!*c)
break;
args[argc++] = c;
while (*c && !isspace(*c))
c++;
if (*c)
*c++ = '\0';
}
if (!argc) {
argc = -1;
return 0;
}
if (argc == ARGS_MAX) {
err("Too many args to --exec\n");
argc = -1;
return 0;
}
args[argc++] = path;
args[argc] = NULL;
}
if (!(pid = fork())) {
execvp(args[0], args);
_exit(127);
2005-05-17 00:46:46 +04:00
} else if (pid < (pid_t) 0)
return 0;
TEMP_FAILURE_RETRY(waitpid(pid, NULL, 0));
return 1;
}
static int _status(CMD_ARGS)
{
int r = 0;
struct dm_task *dmt;
void *next = NULL;
uint64_t start, length;
char *target_type = NULL;
char *params, *c;
int cmdno;
const char *name = NULL;
int matched = 0;
2005-05-16 20:04:34 +04:00
int ls_only = 0;
struct dm_info info;
if (names)
name = names->name;
else {
if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG])
return _process_all(cmd, NULL, argc, argv, 0, _status);
name = argv[0];
}
if (!strcmp(cmd->name, "table"))
cmdno = DM_DEVICE_TABLE;
2003-11-13 16:14:28 +03:00
else
cmdno = DM_DEVICE_STATUS;
if (!strcmp(cmd->name, "ls"))
2005-05-16 20:04:34 +04:00
ls_only = 1;
if (!(dmt = dm_task_create(cmdno)))
return 0;
if (!_set_task_device(dmt, name, 0))
goto out;
if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
goto out;
if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
goto out;
if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
goto out;
if (_switches[NOFLUSH_ARG] && !dm_task_no_flush(dmt))
goto out;
if (!_task_run(dmt))
goto out;
if (!dm_task_get_info(dmt, &info) || !info.exists)
goto out;
if (!name)
name = dm_task_get_name(dmt);
/* Fetch targets and print 'em */
do {
next = dm_get_next_target(dmt, next, &start, &length,
&target_type, &params);
/* Skip if target type doesn't match */
if (_switches[TARGET_ARG] &&
(!target_type || strcmp(target_type, _target)))
continue;
2005-05-16 20:04:34 +04:00
if (ls_only) {
if (!_switches[EXEC_ARG] || !_command_to_exec ||
2005-05-17 00:46:46 +04:00
_switches[VERBOSE_ARG])
_display_dev(dmt, name);
2005-05-16 20:04:34 +04:00
next = NULL;
} else if (!_switches[EXEC_ARG] || !_command_to_exec ||
2005-05-17 00:46:46 +04:00
_switches[VERBOSE_ARG]) {
2005-05-16 20:04:34 +04:00
if (!matched && _switches[VERBOSE_ARG])
_display_info(dmt);
if (multiple_devices && !_switches[VERBOSE_ARG])
2005-05-16 20:04:34 +04:00
printf("%s: ", name);
if (target_type) {
/* Suppress encryption key */
if (!_switches[SHOWKEYS_ARG] &&
cmdno == DM_DEVICE_TABLE &&
!strcmp(target_type, "crypt")) {
c = params;
while (*c && *c != ' ')
c++;
if (*c)
c++;
while (*c && *c != ' ')
*c++ = '0';
}
printf(FMTu64 " " FMTu64 " %s %s",
2005-05-16 20:04:34 +04:00
start, length, target_type, params);
}
printf("\n");
}
matched = 1;
} while (next);
if (multiple_devices && _switches[VERBOSE_ARG] && matched && !ls_only)
printf("\n");
if (matched && _switches[EXEC_ARG] && _command_to_exec && !_exec_command(name))
2005-05-17 00:46:46 +04:00
goto out;
r = 1;
out:
dm_task_destroy(dmt);
return r;
}
/* Show target names and their version numbers */
static int _targets(CMD_ARGS)
{
int r = 0;
struct dm_task *dmt;
struct dm_versions *target;
struct dm_versions *last_target;
if (!(dmt = dm_task_create(DM_DEVICE_LIST_VERSIONS)))
return 0;
if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
goto out;
if (!_task_run(dmt))
goto out;
target = dm_task_get_versions(dmt);
/* Fetch targets and print 'em */
do {
last_target = target;
printf("%-16s v%d.%d.%d\n", target->name, target->version[0],
target->version[1], target->version[2]);
2010-12-20 16:37:26 +03:00
target = (struct dm_versions *)((char *) target + target->next);
} while (last_target != target);
r = 1;
out:
dm_task_destroy(dmt);
return r;
}
static int _info(CMD_ARGS)
{
int r = 0;
struct dm_task *dmt;
char *name = NULL;
if (names)
name = names->name;
else {
if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG])
return _process_all(cmd, NULL, argc, argv, 0, _info);
name = argv[0];
}
if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
return 0;
if (!_set_task_device(dmt, name, 0))
goto out;
if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
goto out;
if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
goto out;
if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
goto out;
if (!_task_run(dmt))
goto out;
2005-03-27 15:37:46 +04:00
r = _display_info(dmt);
out:
dm_task_destroy(dmt);
return r;
}
static int _deps(CMD_ARGS)
{
int r = 0;
uint32_t i;
struct dm_deps *deps;
struct dm_task *dmt;
struct dm_info info;
char *name = NULL;
char dev_name[PATH_MAX];
int major, minor;
if (names)
name = names->name;
else {
if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG])
return _process_all(cmd, NULL, argc, argv, 0, _deps);
name = argv[0];
}
if (!(dmt = dm_task_create(DM_DEVICE_DEPS)))
return 0;
if (!_set_task_device(dmt, name, 0))
goto out;
if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
goto out;
if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
goto out;
if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
goto out;
if (!_task_run(dmt))
goto out;
if (!dm_task_get_info(dmt, &info))
goto out;
if (!(deps = dm_task_get_deps(dmt)))
goto out;
if (!info.exists) {
printf("Device does not exist.\n");
r = 1;
goto out;
}
if (_switches[VERBOSE_ARG])
_display_info(dmt);
2003-07-02 01:20:58 +04:00
if (multiple_devices && !_switches[VERBOSE_ARG])
printf("%s: ", name);
printf("%d dependencies\t:", deps->count);
for (i = 0; i < deps->count; i++) {
major = (int) MAJOR(deps->device[i]);
minor = (int) MINOR(deps->device[i]);
if ((_dev_name_type == DN_BLK || _dev_name_type == DN_MAP) &&
dm_device_get_name(major, minor, _dev_name_type == DN_BLK,
dev_name, PATH_MAX))
printf(" (%s)", dev_name);
else
printf(" (%d, %d)", major, minor);
}
printf("\n");
if (multiple_devices && _switches[VERBOSE_ARG])
printf("\n");
r = 1;
out:
dm_task_destroy(dmt);
return r;
}
static int _display_name(CMD_ARGS)
2003-07-02 01:20:58 +04:00
{
char dev_name[PATH_MAX];
if (!names)
return 1;
if ((_dev_name_type == DN_BLK || _dev_name_type == DN_MAP) &&
dm_device_get_name((int) MAJOR(names->dev), (int) MINOR(names->dev),
_dev_name_type == DN_BLK, dev_name, PATH_MAX))
printf("%s\t(%s)\n", names->name, dev_name);
else
printf("%s\t(%d:%d)\n", names->name,
(int) MAJOR(names->dev),
(int) MINOR(names->dev));
2003-07-02 01:20:58 +04:00
return 1;
}
2003-07-02 01:20:58 +04:00
/*
* Tree drawing code
*/
enum {
TR_DEVICE=0, /* display device major:minor number */
TR_BLKDEVNAME, /* display device kernel name */
TR_TABLE,
TR_STATUS,
TR_ACTIVE,
TR_RW,
TR_OPENCOUNT,
TR_UUID,
TR_COMPACT,
TR_TRUNCATE,
TR_BOTTOMUP,
NUM_TREEMODE,
};
static int _tree_switches[NUM_TREEMODE];
#define TR_PRINT_ATTRIBUTE ( _tree_switches[TR_ACTIVE] || \
_tree_switches[TR_RW] || \
_tree_switches[TR_OPENCOUNT] || \
_tree_switches[TR_UUID] )
#define TR_PRINT_TARGETS ( _tree_switches[TR_TABLE] || \
_tree_switches[TR_STATUS] )
/* Compact - fewer newlines */
#define TR_PRINT_COMPACT (_tree_switches[TR_COMPACT] && \
!TR_PRINT_ATTRIBUTE && \
!TR_PRINT_TARGETS)
/* FIXME Get rid of this */
#define MAX_DEPTH 100
/* Drawing character definition from pstree */
/* [pstree comment] UTF-8 defines by Johan Myreen, updated by Ben Winslow */
#define UTF_V "\342\224\202" /* U+2502, Vertical line drawing char */
#define UTF_VR "\342\224\234" /* U+251C, Vertical and right */
#define UTF_H "\342\224\200" /* U+2500, Horizontal */
#define UTF_UR "\342\224\224" /* U+2514, Up and right */
#define UTF_HD "\342\224\254" /* U+252C, Horizontal and down */
#define VT_BEG "\033(0\017" /* use graphic chars */
#define VT_END "\033(B" /* back to normal char set */
#define VT_V "x" /* see UTF definitions above */
#define VT_VR "t"
#define VT_H "q"
#define VT_UR "m"
#define VT_HD "w"
static struct {
const char *empty_2; /* */
const char *branch_2; /* |- */
const char *vert_2; /* | */
const char *last_2; /* `- */
const char *single_3; /* --- */
const char *first_3; /* -+- */
}
_tsym_ascii = {
" ",
"|-",
"| ",
"`-",
"---",
"-+-"
},
_tsym_utf = {
" ",
UTF_VR UTF_H,
UTF_V " ",
UTF_UR UTF_H,
UTF_H UTF_H UTF_H,
UTF_H UTF_HD UTF_H
},
_tsym_vt100 = {
" ",
VT_BEG VT_VR VT_H VT_END,
VT_BEG VT_V VT_END " ",
VT_BEG VT_UR VT_H VT_END,
VT_BEG VT_H VT_H VT_H VT_END,
VT_BEG VT_H VT_HD VT_H VT_END
},
*_tsym = &_tsym_ascii;
/*
* Tree drawing functions.
*/
/* FIXME Get rid of these statics - use dynamic struct */
/* FIXME Explain what these vars are for */
static int _tree_width[MAX_DEPTH], _tree_more[MAX_DEPTH];
static int _termwidth = 80; /* Maximum output width */
static int _cur_x = 1; /* Current horizontal output position */
static char _last_char = 0;
static void _out_char(const unsigned c)
{
/* Only first UTF-8 char counts */
_cur_x += ((c & 0xc0) != 0x80);
if (!_tree_switches[TR_TRUNCATE]) {
putchar((int) c);
return;
}
/* Truncation? */
if (_cur_x <= _termwidth)
putchar((int) c);
if (_cur_x == _termwidth + 1 && ((c & 0xc0) != 0x80)) {
if (_last_char || (c & 0x80)) {
putchar('.');
putchar('.');
putchar('.');
} else {
_last_char = c;
_cur_x--;
}
}
}
static void _out_string(const char *str)
{
while (*str)
_out_char((unsigned char) *str++);
}
/* non-negative integers only */
static unsigned _out_int(unsigned num)
{
unsigned digits = 0;
unsigned divi;
if (!num) {
_out_char('0');
return 1;
}
/* non zero case */
for (divi = 1; num / divi; divi *= 10)
digits++;
for (divi /= 10; divi; divi /= 10)
_out_char('0' + (num / divi) % 10);
return digits;
}
static void _out_newline(void)
{
if (_last_char && _cur_x == _termwidth)
putchar(_last_char);
_last_char = 0;
putchar('\n');
_cur_x = 1;
}
static void _out_prefix(unsigned depth)
{
unsigned x, d;
for (d = 0; d < depth; d++) {
for (x = _tree_width[d] + 1; x > 0; x--)
_out_char(' ');
_out_string(d == depth - 1 ?
!_tree_more[depth] ? _tsym->last_2 : _tsym->branch_2
: _tree_more[d + 1] ?
_tsym->vert_2 : _tsym->empty_2);
}
}
/*
* Display tree
*/
2005-11-09 17:10:50 +03:00
static void _display_tree_attributes(struct dm_tree_node *node)
{
int attr = 0;
const char *uuid;
const struct dm_info *info;
2005-11-09 17:10:50 +03:00
uuid = dm_tree_node_get_uuid(node);
info = dm_tree_node_get_info(node);
if (!info->exists)
return;
if (_tree_switches[TR_ACTIVE]) {
_out_string(attr++ ? ", " : " [");
_out_string(info->suspended ? "SUSPENDED" : "ACTIVE");
}
if (_tree_switches[TR_RW]) {
_out_string(attr++ ? ", " : " [");
_out_string(info->read_only ? "RO" : "RW");
}
if (_tree_switches[TR_OPENCOUNT]) {
_out_string(attr++ ? ", " : " [");
(void) _out_int((unsigned) info->open_count);
}
if (_tree_switches[TR_UUID]) {
_out_string(attr++ ? ", " : " [");
_out_string(uuid && *uuid ? uuid : "");
}
if (attr)
_out_char(']');
}
2011-12-01 18:57:30 +04:00
/* FIXME Display table or status line. (Disallow both?) */
static void _display_tree_targets(struct dm_tree_node *node, unsigned depth)
{
}
2005-11-09 17:10:50 +03:00
static void _display_tree_node(struct dm_tree_node *node, unsigned depth,
unsigned first_child __attribute__((unused)),
unsigned last_child, unsigned has_children)
{
int offset;
const char *name;
const struct dm_info *info;
int first_on_line = 0;
char dev_name[PATH_MAX];
/* Sub-tree for targets has 2 more depth */
if (depth + 2 > MAX_DEPTH)
return;
2005-11-09 17:10:50 +03:00
name = dm_tree_node_get_name(node);
if ((!name || !*name) &&
(!_tree_switches[TR_DEVICE] && !_tree_switches[TR_BLKDEVNAME]))
return;
/* Indicate whether there are more nodes at this depth */
_tree_more[depth] = !last_child;
_tree_width[depth] = 0;
if (_cur_x == 1)
first_on_line = 1;
if (!TR_PRINT_COMPACT || first_on_line)
_out_prefix(depth);
/* Remember the starting point for compact */
offset = _cur_x;
if (TR_PRINT_COMPACT && !first_on_line)
_out_string(_tree_more[depth] ? _tsym->first_3 : _tsym->single_3);
/* display node */
if (name)
_out_string(name);
2005-11-09 17:10:50 +03:00
info = dm_tree_node_get_info(node);
if (_tree_switches[TR_BLKDEVNAME] &&
dm_device_get_name(info->major, info->minor, 1, dev_name, PATH_MAX)) {
_out_string(name ? " <" : "<");
_out_string(dev_name);
_out_char('>');
}
if (_tree_switches[TR_DEVICE]) {
_out_string(name ? " (" : "(");
(void) _out_int(info->major);
_out_char(':');
(void) _out_int(info->minor);
_out_char(')');
}
/* display additional info */
if (TR_PRINT_ATTRIBUTE)
_display_tree_attributes(node);
if (TR_PRINT_COMPACT)
_tree_width[depth] = _cur_x - offset;
if (!TR_PRINT_COMPACT || !has_children)
_out_newline();
if (TR_PRINT_TARGETS) {
_tree_more[depth + 1] = has_children;
2011-12-01 18:57:30 +04:00
_display_tree_targets(node, depth + 2);
}
}
/*
* Walk the dependency tree
*/
static void _display_tree_walk_children(struct dm_tree_node *node,
unsigned depth)
{
2005-11-09 17:10:50 +03:00
struct dm_tree_node *child, *next_child;
void *handle = NULL;
uint32_t inverted = _tree_switches[TR_BOTTOMUP];
unsigned first_child = 1;
unsigned has_children;
2005-11-09 17:10:50 +03:00
next_child = dm_tree_next_child(&handle, node, inverted);
while ((child = next_child)) {
2005-11-09 17:10:50 +03:00
next_child = dm_tree_next_child(&handle, node, inverted);
has_children =
2005-11-09 17:10:50 +03:00
dm_tree_node_num_children(child, inverted) ? 1 : 0;
_display_tree_node(child, depth, first_child,
next_child ? 0U : 1U, has_children);
if (has_children)
_display_tree_walk_children(child, depth + 1);
first_child = 0;
}
}
static int _add_dep(CMD_ARGS)
{
if (names &&
!dm_tree_add_dev(_dtree, (unsigned) MAJOR(names->dev), (unsigned) MINOR(names->dev)))
return 0;
return 1;
}
/*
* Create and walk dependency tree
*/
static int _build_whole_deptree(const struct command *cmd)
{
if (_dtree)
return 1;
2005-11-09 17:10:50 +03:00
if (!(_dtree = dm_tree_create()))
return 0;
if (!_process_all(cmd, NULL, 0, NULL, 0, _add_dep))
return 0;
return 1;
}
static int _display_tree(CMD_ARGS)
{
if (!_build_whole_deptree(cmd))
return 0;
_display_tree_walk_children(dm_tree_find_node(_dtree, 0, 0), 0);
return 1;
}
/*
* Report device information
*/
/* dm specific display functions */
static int _int32_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
const int32_t value = *(const int32_t *)data;
return dm_report_field_int32(rh, field, &value);
}
static int _uint32_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
const uint32_t value = *(const int32_t *)data;
return dm_report_field_uint32(rh, field, &value);
}
static int _show_units(void)
{
/* --nosuffix overrides --units */
if (_switches[NOSUFFIX_ARG])
return 0;
return (_int_args[UNITS_ARG]) ? 1 : 0;
}
static int _dm_name_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
const char *name = dm_task_get_name((const struct dm_task *) data);
return dm_report_field_string(rh, field, &name);
}
static int _dm_mangled_name_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
char *name;
int r = 0;
if ((name = dm_task_get_name_mangled((const struct dm_task *) data))) {
2012-08-17 14:15:36 +04:00
r = dm_report_field_string(rh, field, (const char * const *) &name);
dm_free(name);
}
return r;
}
static int _dm_unmangled_name_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
char *name;
int r = 0;
if ((name = dm_task_get_name_unmangled((const struct dm_task *) data))) {
2012-08-17 14:15:36 +04:00
r = dm_report_field_string(rh, field, (const char * const *) &name);
dm_free(name);
}
return r;
}
static int _dm_uuid_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
const char *uuid = dm_task_get_uuid((const struct dm_task *) data);
if (!uuid || !*uuid)
uuid = "";
return dm_report_field_string(rh, field, &uuid);
}
static int _dm_mangled_uuid_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
char *uuid;
int r = 0;
if ((uuid = dm_task_get_uuid_mangled((const struct dm_task *) data))) {
r = dm_report_field_string(rh, field, (const char * const *) &uuid);
dm_free(uuid);
}
return r;
}
static int _dm_unmangled_uuid_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
char *uuid;
int r = 0;
if ((uuid = dm_task_get_uuid_unmangled((const struct dm_task *) data))) {
r = dm_report_field_string(rh, field, (const char * const *) &uuid);
dm_free(uuid);
}
return r;
}
static int _dm_read_ahead_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
2007-11-30 17:59:57 +03:00
uint32_t value;
2007-11-30 17:59:57 +03:00
if (!dm_task_get_read_ahead((const struct dm_task *) data, &value))
value = 0;
return dm_report_field_uint32(rh, field, &value);
}
static int _dm_blk_name_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
char dev_name[PATH_MAX];
const char *s = dev_name;
const struct dm_info *info = data;
if (!dm_device_get_name(info->major, info->minor, 1, dev_name, PATH_MAX)) {
log_error("Could not resolve block device name for %d:%d.",
info->major, info->minor);
return 0;
}
return dm_report_field_string(rh, field, &s);
}
static int _dm_info_status_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
char buf[5];
const char *s = buf;
const struct dm_info *info = data;
buf[0] = info->live_table ? 'L' : '-';
buf[1] = info->inactive_table ? 'I' : '-';
buf[2] = info->suspended ? 's' : '-';
buf[3] = info->read_only ? 'r' : 'w';
buf[4] = '\0';
return dm_report_field_string(rh, field, &s);
}
static int _dm_info_table_loaded_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field,
const void *data,
void *private __attribute__((unused)))
{
const struct dm_info *info = data;
if (info->live_table) {
if (info->inactive_table)
dm_report_field_set_value(field, "Both", NULL);
else
dm_report_field_set_value(field, "Live", NULL);
return 1;
}
if (info->inactive_table)
dm_report_field_set_value(field, "Inactive", NULL);
else
dm_report_field_set_value(field, "None", NULL);
return 1;
}
static int _dm_info_suspended_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field,
const void *data,
void *private __attribute__((unused)))
{
const struct dm_info *info = data;
if (info->suspended)
dm_report_field_set_value(field, "Suspended", NULL);
else
dm_report_field_set_value(field, "Active", NULL);
return 1;
}
static int _dm_info_read_only_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field,
const void *data,
void *private __attribute__((unused)))
{
const struct dm_info *info = data;
if (info->read_only)
dm_report_field_set_value(field, "Read-only", NULL);
else
dm_report_field_set_value(field, "Writeable", NULL);
return 1;
}
static int _dm_info_devno_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field, const void *data,
void *private)
{
char buf[PATH_MAX], *repstr;
const struct dm_info *info = data;
if (!dm_pool_begin_object(mem, 8)) {
log_error("dm_pool_begin_object failed");
return 0;
}
if (private) {
if (!dm_device_get_name(info->major, info->minor,
1, buf, PATH_MAX))
goto out_abandon;
}
else {
if (dm_snprintf(buf, sizeof(buf), "%d:%d",
info->major, info->minor) < 0) {
log_error("dm_pool_alloc failed");
goto out_abandon;
}
}
if (!dm_pool_grow_object(mem, buf, strlen(buf) + 1)) {
log_error("dm_pool_grow_object failed");
goto out_abandon;
}
repstr = dm_pool_end_object(mem);
dm_report_field_set_value(field, repstr, repstr);
return 1;
out_abandon:
dm_pool_abandon_object(mem);
return 0;
}
static int _dm_tree_names(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field, const void *data,
void *private, unsigned inverted)
{
const struct dm_tree_node *node = data;
struct dm_tree_node *parent;
void *t = NULL;
const char *name;
int first_node = 1;
char *repstr;
if (!dm_pool_begin_object(mem, 16)) {
log_error("dm_pool_begin_object failed");
return 0;
}
while ((parent = dm_tree_next_child(&t, node, inverted))) {
name = dm_tree_node_get_name(parent);
if (!name || !*name)
continue;
2007-06-19 20:50:38 +04:00
if (!first_node && !dm_pool_grow_object(mem, ",", 1)) {
log_error("dm_pool_grow_object failed");
goto out_abandon;
}
if (!dm_pool_grow_object(mem, name, 0)) {
log_error("dm_pool_grow_object failed");
goto out_abandon;
}
if (first_node)
first_node = 0;
}
if (!dm_pool_grow_object(mem, "\0", 1)) {
log_error("dm_pool_grow_object failed");
goto out_abandon;
}
repstr = dm_pool_end_object(mem);
dm_report_field_set_value(field, repstr, repstr);
return 1;
out_abandon:
dm_pool_abandon_object(mem);
return 0;
}
static int _dm_deps_names_disp(struct dm_report *rh,
struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
return _dm_tree_names(rh, mem, field, data, private, 0);
}
static int _dm_tree_parents_names_disp(struct dm_report *rh,
struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
return _dm_tree_names(rh, mem, field, data, private, 1);
}
static int _dm_tree_parents_devs_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct dm_tree_node *node = data;
struct dm_tree_node *parent;
void *t = NULL;
const struct dm_info *info;
int first_node = 1;
char buf[DM_MAX_TYPE_NAME], *repstr;
if (!dm_pool_begin_object(mem, 16)) {
log_error("dm_pool_begin_object failed");
return 0;
}
while ((parent = dm_tree_next_child(&t, node, 1))) {
info = dm_tree_node_get_info(parent);
if (!info->major && !info->minor)
continue;
if (!first_node && !dm_pool_grow_object(mem, ",", 1)) {
log_error("dm_pool_grow_object failed");
goto out_abandon;
}
if (dm_snprintf(buf, sizeof(buf), "%d:%d",
info->major, info->minor) < 0) {
log_error("dm_snprintf failed");
goto out_abandon;
}
if (!dm_pool_grow_object(mem, buf, 0)) {
log_error("dm_pool_grow_object failed");
goto out_abandon;
}
if (first_node)
first_node = 0;
}
if (!dm_pool_grow_object(mem, "\0", 1)) {
log_error("dm_pool_grow_object failed");
goto out_abandon;
}
repstr = dm_pool_end_object(mem);
dm_report_field_set_value(field, repstr, repstr);
return 1;
out_abandon:
dm_pool_abandon_object(mem);
return 0;
}
static int _dm_tree_parents_count_disp(struct dm_report *rh,
struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct dm_tree_node *node = data;
int num_parent = dm_tree_node_num_children(node, 1);
return dm_report_field_int(rh, field, &num_parent);
}
static int _dm_deps_disp_common(struct dm_report *rh, struct dm_pool*mem,
struct dm_report_field *field, const void *data,
void *private, int disp_blk_dev_names)
{
const struct dm_deps *deps = data;
char buf[PATH_MAX], *repstr;
int major, minor;
unsigned i;
if (!dm_pool_begin_object(mem, 16)) {
log_error("dm_pool_begin_object failed");
return 0;
}
for (i = 0; i < deps->count; i++) {
major = (int) MAJOR(deps->device[i]);
minor = (int) MINOR(deps->device[i]);
if (disp_blk_dev_names) {
if (!dm_device_get_name(major, minor, 1, buf, PATH_MAX)) {
log_error("Could not resolve block device "
"name for %d:%d.", major, minor);
goto out_abandon;
}
}
else if (dm_snprintf(buf, sizeof(buf), "%d:%d",
major, minor) < 0) {
log_error("dm_snprintf failed");
goto out_abandon;
}
if (!dm_pool_grow_object(mem, buf, 0)) {
log_error("dm_pool_grow_object failed");
goto out_abandon;
}
if (i + 1 < deps->count && !dm_pool_grow_object(mem, ",", 1)) {
log_error("dm_pool_grow_object failed");
goto out_abandon;
}
}
if (!dm_pool_grow_object(mem, "\0", 1)) {
log_error("dm_pool_grow_object failed");
goto out_abandon;
}
repstr = dm_pool_end_object(mem);
dm_report_field_set_value(field, repstr, repstr);
return 1;
out_abandon:
dm_pool_abandon_object(mem);
return 0;
}
static int _dm_deps_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field, const void *data,
void *private)
{
return _dm_deps_disp_common(rh, mem, field, data, private, 0);
}
static int _dm_deps_blk_names_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
return _dm_deps_disp_common(rh, mem, field, data, private, 1);
}
static int _dm_subsystem_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
return dm_report_field_string(rh, field, (const char *const *) data);
}
static int _dm_vg_name_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
return dm_report_field_string(rh, field, (const char *const *) data);
}
static int _dm_lv_name_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
return dm_report_field_string(rh, field, (const char *const *) data);
}
static int _dm_lv_layer_name_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
return dm_report_field_string(rh, field, (const char *const *) data);
}
/**
* All _dm_stats_*_disp functions for basic counters are identical:
* obtain the value for the current region and area and pass it to
* dm_report_field_uint64().
*/
#define MK_STATS_COUNTER_DISP_FN(counter) \
static int _dm_stats_ ## counter ## _disp(struct dm_report *rh, \
struct dm_pool *mem __attribute__((unused)), \
struct dm_report_field *field, const void *data, \
void *private __attribute__((unused))) \
{ \
const struct dm_stats *dms = (const struct dm_stats *) data; \
uint64_t value = dm_stats_get_ ## counter(dms, DM_STATS_REGION_CURRENT, \
DM_STATS_AREA_CURRENT); \
return dm_report_field_uint64(rh, field, &value); \
}
MK_STATS_COUNTER_DISP_FN(reads)
MK_STATS_COUNTER_DISP_FN(reads_merged)
MK_STATS_COUNTER_DISP_FN(read_sectors)
MK_STATS_COUNTER_DISP_FN(read_nsecs)
MK_STATS_COUNTER_DISP_FN(writes)
MK_STATS_COUNTER_DISP_FN(writes_merged)
MK_STATS_COUNTER_DISP_FN(write_sectors)
MK_STATS_COUNTER_DISP_FN(write_nsecs)
MK_STATS_COUNTER_DISP_FN(io_in_progress)
MK_STATS_COUNTER_DISP_FN(io_nsecs)
MK_STATS_COUNTER_DISP_FN(weighted_io_nsecs)
MK_STATS_COUNTER_DISP_FN(total_read_nsecs)
MK_STATS_COUNTER_DISP_FN(total_write_nsecs)
#undef MK_STATS_COUNTER_DISP_FN
static int _dm_stats_region_id_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
const struct dm_stats *dms = (const struct dm_stats *) data;
uint64_t region_id = dm_stats_get_current_region(dms);
return dm_report_field_uint64(rh, field, &region_id);
}
static int _dm_stats_region_start_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
const struct dm_stats *dms = (const struct dm_stats *) data;
uint64_t region_start;
const char *repstr;
double *sortval;
char units = _disp_units;
uint64_t factor = _disp_factor;
if (!dm_stats_get_current_region_start(dms, &region_start))
return_0;
if (!(repstr = dm_size_to_string(mem, region_start, units, 1, factor,
_show_units(), DM_SIZE_UNIT)))
return_0;
if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
return_0;
*sortval = (double) region_start;
dm_report_field_set_value(field, repstr, sortval);
return 1;
}
static int _dm_stats_region_len_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
const struct dm_stats *dms = (const struct dm_stats *) data;
uint64_t region_length;
const char *repstr;
double *sortval;
char units = _disp_units;
uint64_t factor = _disp_factor;
if (!dm_stats_get_current_region_len(dms, &region_length))
return_0;
if (!(repstr = dm_size_to_string(mem, region_length, units, 1, factor,
_show_units(), DM_SIZE_UNIT)))
return_0;
if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
return_0;
*sortval = (double) region_length;
dm_report_field_set_value(field, repstr, sortval);
return 1;
}
static int _dm_stats_area_id_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
const struct dm_stats *dms = (const struct dm_stats *) data;
uint64_t area_id = dm_stats_get_current_area(dms);
return dm_report_field_uint64(rh, field, &area_id);
}
static int _dm_stats_area_start_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
const struct dm_stats *dms = (const struct dm_stats *) data;
uint64_t area_start;
const char *repstr;
double *sortval;
char units = _disp_units;
uint64_t factor = _disp_factor;
if (!dm_stats_get_current_area_start(dms, &area_start))
return_0;
if (!(repstr = dm_size_to_string(mem, area_start, units, 1, factor,
_show_units(), DM_SIZE_UNIT)))
return_0;
if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
return_0;
*sortval = (double) area_start;
dm_report_field_set_value(field, repstr, sortval);
return 1;
}
static int _dm_stats_area_offset_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
const struct dm_stats *dms = (const struct dm_stats *) data;
uint64_t area_offset;
const char *repstr;
double *sortval;
char units = _disp_units;
uint64_t factor = _disp_factor;
if (!dm_stats_get_current_area_offset(dms, &area_offset))
return_0;
if (!(repstr = dm_size_to_string(mem, area_offset, units, 1, factor,
_show_units(), DM_SIZE_UNIT)))
return_0;
if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
return_0;
*sortval = (double) area_offset;
dm_report_field_set_value(field, repstr, sortval);
return 1;
}
static int _dm_stats_area_len_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
const struct dm_stats *dms = (const struct dm_stats *) data;
uint64_t area_len;
const char *repstr;
double *sortval;
char units = _disp_units;
uint64_t factor = _disp_factor;
if (!dm_stats_get_current_area_len(dms, &area_len))
return_0;
if (!(repstr = dm_size_to_string(mem, area_len, units, 1, factor,
_show_units(), DM_SIZE_UNIT)))
return_0;
if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
return_0;
*sortval = (double) area_len;
dm_report_field_set_value(field, repstr, sortval);
return 1;
}
static int _dm_stats_area_count_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
const struct dm_stats *dms = (const struct dm_stats *) data;
uint64_t area_count, region;
region = dm_stats_get_current_region(dms);
if (!(area_count = dm_stats_get_region_nr_areas(dms, region)))
return_0;
return dm_report_field_uint64(rh, field, &area_count);
}
static int _dm_stats_program_id_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
const struct dm_stats *dms = (const struct dm_stats *) data;
const char *program_id;
if (!(program_id = dm_stats_get_current_region_program_id(dms)))
return_0;
return dm_report_field_string(rh, field, (const char * const *) &program_id);
}
static int _dm_stats_aux_data_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
const struct dm_stats *dms = (const struct dm_stats *) data;
const char *aux_data;
if (!(aux_data = dm_stats_get_current_region_aux_data(dms)))
return_0;
return dm_report_field_string(rh, field, (const char * const *) &aux_data);
}
2009-06-04 00:44:42 +04:00
static int _dm_stats_precise_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
const struct dm_stats *dms = (const struct dm_stats *) data;
int precise;
precise = dm_stats_get_current_region_precise_timestamps(dms);
return dm_report_field_int(rh, field, (const int *) &precise);
}
static int _dm_stats_rrqm_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
const struct dm_stats *dms = (const struct dm_stats *) data;
char buf[64];
char *repstr;
double *sortval, rrqm;
if (!dm_stats_get_rd_merges_per_sec(dms, &rrqm,
DM_STATS_REGION_CURRENT,
DM_STATS_AREA_CURRENT))
return_0;
if (!dm_snprintf(buf, sizeof(buf), "%.2f", rrqm))
return_0;
2008-06-25 02:53:48 +04:00
if (!(repstr = dm_pool_strdup(mem, buf)))
return_0;
2008-06-25 00:16:47 +04:00
if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
return_0;
*sortval = rrqm;
dm_report_field_set_value(field, repstr, sortval);
return 1;
}
static int _dm_stats_wrqm_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
const struct dm_stats *dms = (const struct dm_stats *) data;
char buf[64];
char *repstr;
double *sortval, wrqm;
if (!dm_stats_get_wr_merges_per_sec(dms, &wrqm,
DM_STATS_REGION_CURRENT,
DM_STATS_AREA_CURRENT))
return_0;
if (!dm_snprintf(buf, sizeof(buf), "%.2f", wrqm))
return_0;
if (!(repstr = dm_pool_strdup(mem, buf)))
return_0;
if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
return_0;
*sortval = wrqm;
2008-06-25 00:16:47 +04:00
dm_report_field_set_value(field, repstr, sortval);
return 1;
2008-06-25 02:53:48 +04:00
}
static int _dm_stats_rs_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
const struct dm_stats *dms = (const struct dm_stats *) data;
char buf[64];
char *repstr;
double *sortval, rs;
if (!dm_stats_get_reads_per_sec(dms, &rs,
DM_STATS_REGION_CURRENT,
DM_STATS_AREA_CURRENT))
return_0;
if (!dm_snprintf(buf, sizeof(buf), "%.2f", rs))
return_0;
if (!(repstr = dm_pool_strdup(mem, buf)))
return_0;
if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
return_0;
*sortval = rs;
dm_report_field_set_value(field, repstr, sortval);
return 1;
2003-07-02 01:20:58 +04:00
}
static int _dm_stats_ws_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
const struct dm_stats *dms = (const struct dm_stats *) data;
char buf[64];
char *repstr;
double *sortval, ws;
if (!dm_stats_get_writes_per_sec(dms, &ws,
DM_STATS_REGION_CURRENT,
DM_STATS_AREA_CURRENT))
return_0;
if (!dm_snprintf(buf, sizeof(buf), "%.2f", ws))
return_0;
if (!(repstr = dm_pool_strdup(mem, buf)))
return_0;
if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
return_0;
*sortval = ws;
dm_report_field_set_value(field, repstr, sortval);
return 1;
}
static int _dm_stats_read_secs_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
const struct dm_stats *dms = (const struct dm_stats *) data;
const char *repstr;
double *sortval, rsec;
char units = _disp_units;
uint64_t factor = _disp_factor;
if (!dm_stats_get_read_sectors_per_sec(dms, &rsec,
DM_STATS_REGION_CURRENT,
DM_STATS_AREA_CURRENT))
return_0;
if (!(repstr = dm_size_to_string(mem, (uint64_t) rsec, units, 1,
factor, _show_units(), DM_SIZE_UNIT)))
return_0;
if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
return_0;
*sortval = rsec;
dm_report_field_set_value(field, repstr, sortval);
return 1;
}
static int _dm_stats_write_secs_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
const struct dm_stats *dms = (const struct dm_stats *) data;
const char *repstr;
double *sortval, wsec;
char units = _disp_units;
uint64_t factor = _disp_factor;
if (!dm_stats_get_write_sectors_per_sec(dms, &wsec,
DM_STATS_REGION_CURRENT,
DM_STATS_AREA_CURRENT))
return_0;
if (!(repstr = dm_size_to_string(mem, (uint64_t) wsec, units, 1,
factor, _show_units(), DM_SIZE_UNIT)))
return_0;
if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
return_0;
*sortval = wsec;
dm_report_field_set_value(field, repstr, sortval);
return 1;
}
static int _dm_stats_arqsz_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
const struct dm_stats *dms = (const struct dm_stats *) data;
const char *repstr;
double *sortval, arqsz;
char units = _disp_units;
uint64_t factor = _disp_factor;
if (!dm_stats_get_average_request_size(dms, &arqsz,
DM_STATS_REGION_CURRENT,
DM_STATS_AREA_CURRENT))
return_0;
if (!(repstr = dm_size_to_string(mem, (uint64_t) arqsz, units, 1,
factor, _show_units(), DM_SIZE_UNIT)))
return_0;
if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
return_0;
*sortval = arqsz;
dm_report_field_set_value(field, repstr, sortval);
return 1;
}
static int _dm_stats_qusz_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
const struct dm_stats *dms = (const struct dm_stats *) data;
char buf[64];
char *repstr;
double *sortval, qusz;
if (!dm_stats_get_average_queue_size(dms, &qusz,
DM_STATS_REGION_CURRENT,
DM_STATS_AREA_CURRENT))
return_0;
if (!dm_snprintf(buf, sizeof(buf), "%.2f", qusz))
return_0;
if (!(repstr = dm_pool_strdup(mem, buf)))
return_0;
if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
return_0;
*sortval = qusz;
dm_report_field_set_value(field, repstr, sortval);
return 1;
}
static int _dm_stats_await_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
const struct dm_stats *dms = (const struct dm_stats *) data;
char buf[64];
char *repstr;
double *sortval, await;
if (!dm_stats_get_average_wait_time(dms, &await,
DM_STATS_REGION_CURRENT,
DM_STATS_AREA_CURRENT))
return_0;
/* FIXME: make scale configurable */
/* display in msecs */
await /= NSEC_PER_MSEC;
if (!dm_snprintf(buf, sizeof(buf), "%.2f", await))
return_0;
if (!(repstr = dm_pool_strdup(mem, buf)))
return_0;
if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
return_0;
*sortval = await;
dm_report_field_set_value(field, repstr, sortval);
return 1;
}
static int _dm_stats_r_await_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
const struct dm_stats *dms = (const struct dm_stats *) data;
char buf[64];
char *repstr;
double *sortval, r_await;
if (!dm_stats_get_average_rd_wait_time(dms, &r_await,
DM_STATS_REGION_CURRENT,
DM_STATS_AREA_CURRENT))
return_0;
/* FIXME: make scale configurable */
/* display in msecs */
r_await /= NSEC_PER_MSEC;
if (!dm_snprintf(buf, sizeof(buf), "%.2f", r_await))
return_0;
if (!(repstr = dm_pool_strdup(mem, buf)))
return_0;
if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
return_0;
*sortval = r_await;
dm_report_field_set_value(field, repstr, sortval);
return 1;
}
static int _dm_stats_w_await_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
const struct dm_stats *dms = (const struct dm_stats *) data;
char buf[64];
char *repstr;
double *sortval, w_await;
if (!dm_stats_get_average_wr_wait_time(dms, &w_await,
DM_STATS_REGION_CURRENT,
DM_STATS_AREA_CURRENT))
return_0;
/* FIXME: make scale configurable */
/* display in msecs */
w_await /= NSEC_PER_MSEC;
if (!dm_snprintf(buf, sizeof(buf), "%.2f", w_await))
return_0;
if (!(repstr = dm_pool_strdup(mem, buf)))
return_0;
if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
return_0;
*sortval = w_await;
dm_report_field_set_value(field, repstr, sortval);
return 1;
}
static int _dm_stats_tput_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
const struct dm_stats *dms = (const struct dm_stats *) data;
char buf[64];
char *repstr;
double *sortval, tput;
if (!dm_stats_get_throughput(dms, &tput,
DM_STATS_REGION_CURRENT,
DM_STATS_AREA_CURRENT))
return_0;
if (!dm_snprintf(buf, sizeof(buf), "%.2f", tput))
return_0;
if (!(repstr = dm_pool_strdup(mem, buf)))
return_0;
if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
return_0;
*sortval = tput;
dm_report_field_set_value(field, repstr, sortval);
return 1;
}
static int _dm_stats_svctm_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
const struct dm_stats *dms = (const struct dm_stats *) data;
char buf[64];
char *repstr;
double *sortval, svctm;
if (!dm_stats_get_service_time(dms, &svctm,
DM_STATS_REGION_CURRENT,
DM_STATS_AREA_CURRENT))
return_0;
/* FIXME: make scale configurable */
/* display in msecs */
svctm /= NSEC_PER_MSEC;
if (!dm_snprintf(buf, sizeof(buf), "%.2f", svctm))
return_0;
if (!(repstr = dm_pool_strdup(mem, buf)))
return_0;
if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
return_0;
*sortval = svctm;
dm_report_field_set_value(field, repstr, sortval);
return 1;
}
static int _dm_stats_util_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
const struct dm_stats *dms = (const struct dm_stats *) data;
dm_percent_t util;
if (!dm_stats_get_utilization(dms, &util,
DM_STATS_REGION_CURRENT,
DM_STATS_AREA_CURRENT))
return_0;
dm_report_field_percent(rh, field, &util);
return 1;
}
static int _dm_stats_sample_interval_ns_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
/* FIXME: use internal interval estimate when supported by libdm */
return dm_report_field_uint64(rh, field, &_last_interval);
}
static int _dm_stats_sample_interval_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, const void *data,
void *private __attribute__((unused)))
{
char buf[64];
char *repstr;
double *sortval;
if (!(sortval = dm_pool_alloc(mem, sizeof(*sortval))))
return_0;
*sortval = (double)_last_interval / (double) NSEC_PER_SEC;
if (!dm_snprintf(buf, sizeof(buf), "%2.6f", *sortval))
return_0;
if (!(repstr = dm_pool_strdup(mem, buf)))
return_0;
dm_report_field_set_value(field, repstr, sortval);
return 1;
}
static void *_task_get_obj(void *obj)
{
return ((struct dmsetup_report_obj *)obj)->task;
}
static void *_info_get_obj(void *obj)
{
return ((struct dmsetup_report_obj *)obj)->info;
}
static void *_deps_get_obj(void *obj)
{
return dm_task_get_deps(((struct dmsetup_report_obj *)obj)->deps_task);
}
static void *_tree_get_obj(void *obj)
{
return ((struct dmsetup_report_obj *)obj)->tree_node;
}
static void *_split_name_get_obj(void *obj)
{
return ((struct dmsetup_report_obj *)obj)->split_name;
}
static void *_stats_get_obj(void *obj)
{
return ((struct dmsetup_report_obj *)obj)->stats;
}
static const struct dm_report_object_type _report_types[] = {
{ DR_TASK, "Mapped Device Name", "name_", _task_get_obj },
{ DR_INFO, "Mapped Device Information", "info_", _info_get_obj },
{ DR_DEPS, "Mapped Device Relationship Information", "deps_", _deps_get_obj },
{ DR_TREE, "Mapped Device Relationship Information", "tree_", _tree_get_obj },
{ DR_NAME, "Mapped Device Name Components", "splitname_", _split_name_get_obj },
{ DR_STATS, "Mapped Device Statistics","stats_", _stats_get_obj },
{ DR_STATS_META, "Mapped Device Statistics Region Information","region_", _stats_get_obj },
{ 0, "", "", NULL }
};
/* Column definitions */
/* N.B. Field names must not contain the substring 'help' as this will disable --count. */
#define OFFSET_OF(strct, field) (((char*)&((struct strct*)0)->field) - (char*)0)
#define STR (DM_REPORT_FIELD_TYPE_STRING)
#define NUM (DM_REPORT_FIELD_TYPE_NUMBER)
#define SIZ (DM_REPORT_FIELD_TYPE_SIZE)
#define TIM (DM_REPORT_FIELD_TYPE_TIME)
#define FIELD_O(type, strct, sorttype, head, field, width, func, id, desc) {DR_ ## type, sorttype, OFFSET_OF(strct, field), width, id, head, &_ ## func ## _disp, desc},
#define FIELD_F(type, sorttype, head, width, func, id, desc) {DR_ ## type, sorttype, 0, width, id, head, &_ ## func ## _disp, desc},
static const struct dm_report_field_type _report_fields[] = {
/* *INDENT-OFF* */
FIELD_F(TASK, STR, "Name", 16, dm_name, "name", "Name of mapped device.")
FIELD_F(TASK, STR, "MangledName", 16, dm_mangled_name, "mangled_name", "Mangled name of mapped device.")
FIELD_F(TASK, STR, "UnmangledName", 16, dm_unmangled_name, "unmangled_name", "Unmangled name of mapped device.")
FIELD_F(TASK, STR, "UUID", 32, dm_uuid, "uuid", "Unique (optional) identifier for mapped device.")
FIELD_F(TASK, STR, "MangledUUID", 32, dm_mangled_uuid, "mangled_uuid", "Mangled unique (optional) identifier for mapped device.")
FIELD_F(TASK, STR, "UnmangledUUID", 32, dm_unmangled_uuid, "unmangled_uuid", "Unmangled unique (optional) identifier for mapped device.")
/* FIXME Next one should be INFO */
FIELD_F(TASK, NUM, "RAhead", 6, dm_read_ahead, "read_ahead", "Read ahead value.")
FIELD_F(INFO, STR, "BlkDevName", 16, dm_blk_name, "blkdevname", "Name of block device.")
FIELD_F(INFO, STR, "Stat", 4, dm_info_status, "attr", "(L)ive, (I)nactive, (s)uspended, (r)ead-only, read-(w)rite.")
FIELD_F(INFO, STR, "Tables", 6, dm_info_table_loaded, "tables_loaded", "Which of the live and inactive table slots are filled.")
FIELD_F(INFO, STR, "Suspended", 9, dm_info_suspended, "suspended", "Whether the device is suspended.")
FIELD_F(INFO, STR, "Read-only", 9, dm_info_read_only, "readonly", "Whether the device is read-only or writeable.")
FIELD_F(INFO, STR, "DevNo", 5, dm_info_devno, "devno", "Device major and minor numbers")
FIELD_O(INFO, dm_info, NUM, "Maj", major, 3, int32, "major", "Block device major number.")
FIELD_O(INFO, dm_info, NUM, "Min", minor, 3, int32, "minor", "Block device minor number.")
FIELD_O(INFO, dm_info, NUM, "Open", open_count, 4, int32, "open", "Number of references to open device, if requested.")
FIELD_O(INFO, dm_info, NUM, "Targ", target_count, 4, int32, "segments", "Number of segments in live table, if present.")
FIELD_O(INFO, dm_info, NUM, "Event", event_nr, 6, uint32, "events", "Number of most recent event.")
FIELD_O(DEPS, dm_deps, NUM, "#Devs", count, 5, int32, "device_count", "Number of devices used by this one.")
FIELD_F(TREE, STR, "DevNamesUsed", 16, dm_deps_names, "devs_used", "List of names of mapped devices used by this one.")
FIELD_F(DEPS, STR, "DevNosUsed", 16, dm_deps, "devnos_used", "List of device numbers of devices used by this one.")
FIELD_F(DEPS, STR, "BlkDevNamesUsed", 16, dm_deps_blk_names, "blkdevs_used", "List of names of block devices used by this one.")
FIELD_F(TREE, NUM, "#Refs", 5, dm_tree_parents_count, "device_ref_count", "Number of mapped devices referencing this one.")
FIELD_F(TREE, STR, "RefNames", 8, dm_tree_parents_names, "names_using_dev", "List of names of mapped devices using this one.")
FIELD_F(TREE, STR, "RefDevNos", 9, dm_tree_parents_devs, "devnos_using_dev", "List of device numbers of mapped devices using this one.")
FIELD_O(NAME, dm_split_name, STR, "Subsys", subsystem, 6, dm_subsystem, "subsystem", "Userspace subsystem responsible for this device.")
FIELD_O(NAME, dm_split_name, STR, "VG", vg_name, 4, dm_vg_name, "vg_name", "LVM Volume Group name.")
FIELD_O(NAME, dm_split_name, STR, "LV", lv_name, 4, dm_lv_name, "lv_name", "LVM Logical Volume name.")
FIELD_O(NAME, dm_split_name, STR, "LVLayer", lv_layer, 7, dm_lv_layer_name, "lv_layer", "LVM device layer.")
/* basic stats counters */
2015-08-16 21:35:06 +03:00
FIELD_F(STATS, NUM, "Reads", 5, dm_stats_reads, "reads", "Number of reads completed.")
FIELD_F(STATS, NUM, "RdMrges", 5, dm_stats_reads_merged, "reads_merged", "Number of reads merged.")
FIELD_F(STATS, NUM, "RdSectors", 5, dm_stats_read_sectors, "read_sectors", "Number of sectors read.")
FIELD_F(STATS, NUM, "RdNsec", 5, dm_stats_read_nsecs, "read_nsecs", "Time spent reading.")
FIELD_F(STATS, NUM, "Writes", 5, dm_stats_writes, "writes", "Number of writes completed.")
FIELD_F(STATS, NUM, "WrMerges", 5, dm_stats_writes_merged, "writes_merged", "Number of writes merged.")
FIELD_F(STATS, NUM, "WrSectors", 5, dm_stats_write_sectors, "write_sectors", "Number of sectors written.")
FIELD_F(STATS, NUM, "WrNsec", 5, dm_stats_write_nsecs, "write_nsecs", "Time spent writing.")
FIELD_F(STATS, NUM, "InProgress", 5, dm_stats_io_in_progress, "in_progress", "Number of I/Os currently in progress.")
FIELD_F(STATS, NUM, "IoNsec", 5, dm_stats_io_nsecs, "io_nsecs", "Time spent doing I/O.")
FIELD_F(STATS, NUM, "WtIoNsec", 5, dm_stats_weighted_io_nsecs, "weighted_io_nsecs", "Weighted time spent doing I/O.")
FIELD_F(STATS, NUM, "TotalRdNsec", 5, dm_stats_total_read_nsecs, "total_rd_nsecs", "Total time spent reading.")
FIELD_F(STATS, NUM, "TotalWrNsec", 5, dm_stats_total_write_nsecs, "total_wr_nsecs", "Total time spent writing.")
/* Stats derived metrics */
2015-08-16 21:35:06 +03:00
FIELD_F(STATS, NUM, "RRqM/s", 5, dm_stats_rrqm, "rrqm", "Read requests merged per second.")
FIELD_F(STATS, NUM, "WRqM/s", 5, dm_stats_wrqm, "wrqm", "Write requests merged per second.")
FIELD_F(STATS, NUM, "R/s", 5, dm_stats_rs, "rs", "Reads per second.")
FIELD_F(STATS, NUM, "W/s", 5, dm_stats_ws, "ws", "Writes per second.")
FIELD_F(STATS, NUM, "RSz/s", 5, dm_stats_read_secs, "rsize_sec", "Size of data read per second.")
FIELD_F(STATS, NUM, "WSz/s", 5, dm_stats_write_secs, "wsize_sec", "Size of data written per second.")
FIELD_F(STATS, NUM, "AvRqSz", 5, dm_stats_arqsz, "arqsz", "Average request size.")
FIELD_F(STATS, NUM, "QSize", 5, dm_stats_qusz, "qusz", "Average queue size.")
FIELD_F(STATS, NUM, "AWait", 5, dm_stats_await, "await", "Averate wait time.")
FIELD_F(STATS, NUM, "RdAWait", 5, dm_stats_r_await, "r_await", "Averate read wait time.")
FIELD_F(STATS, NUM, "WrAWait", 5, dm_stats_w_await, "w_await", "Averate write wait time.")
FIELD_F(STATS, NUM, "TPut", 5, dm_stats_tput, "tput", "Throughput.")
FIELD_F(STATS, NUM, "SvcTm", 5, dm_stats_svctm, "svctm", "Service time.")
2015-08-16 21:35:06 +03:00
FIELD_F(STATS, NUM, "Util%", 5, dm_stats_util, "util", "Utilization.")
/* Stats interval duration estimates */
FIELD_F(STATS, NUM, "IntervalNSec", 10, dm_stats_sample_interval_ns, "interval_ns", "Sampling interval in nanoseconds.")
FIELD_F(STATS, NUM, "Interval", 8, dm_stats_sample_interval, "interval", "Sampling interval.")
/* Stats report meta-fields */
2015-08-16 21:35:06 +03:00
FIELD_F(STATS_META, NUM, "RgID", 4, dm_stats_region_id, "region_id", "Region ID.")
FIELD_F(STATS_META, SIZ, "RStart", 5, dm_stats_region_start, "region_start", "Region start.")
FIELD_F(STATS_META, SIZ, "RSize", 5, dm_stats_region_len, "region_len", "Region length.")
2015-08-16 21:35:06 +03:00
FIELD_F(STATS_META, NUM, "ArID", 4, dm_stats_area_id, "area_id", "Area ID.")
FIELD_F(STATS_META, SIZ, "AStart", 5, dm_stats_area_start, "area_start", "Area offset from start of device.")
FIELD_F(STATS_META, SIZ, "ASize", 5, dm_stats_area_len, "area_len", "Area length.")
FIELD_F(STATS_META, SIZ, "AOff", 5, dm_stats_area_offset, "area_offset", "Area offset from start of region.")
2015-08-16 21:35:06 +03:00
FIELD_F(STATS_META, NUM, "#Areas", 3, dm_stats_area_count, "area_count", "Area count.")
FIELD_F(STATS_META, STR, "ProgID", 6, dm_stats_program_id, "program_id", "Program ID.")
FIELD_F(STATS_META, STR, "AuxDat", 6, dm_stats_aux_data, "aux_data", "Auxiliary data.")
FIELD_F(STATS_META, STR, "Precise", 5, dm_stats_precise, "precise", "Set if the nanosecond precision timers are enabled.")
{0, 0, 0, 0, "", "", NULL, NULL},
/* *INDENT-ON* */
};
#undef FIELD_O
#undef FIELD_F
#undef STR
#undef NUM
#undef SIZ
static const char *default_report_options = "name,major,minor,attr,open,segments,events,uuid";
static const char *splitname_report_options = "vg_name,lv_name,lv_layer";
/* Stats counters & derived metrics. */
#define RD_COUNTERS "reads,reads_merged,read_sectors,read_nsecs,total_rd_nsecs"
#define WR_COUNTERS "writes,writes_merged,write_sectors,write_nsecs,total_wr_nsecs"
#define IO_COUNTERS "in_progress,io_nsecs,weighted_io_nsecs"
#define METRICS "rrqm,wrqm,rs,ws,rsize_sec,wsize_sec,arqsz,qusz,util,await,r_await,w_await"
#define COUNTERS RD_COUNTERS "," WR_COUNTERS "," IO_COUNTERS
/* Device, region and area metadata. */
#define STATS_DEV_INFO "name,region_id"
#define STATS_AREA_INFO STATS_DEV_INFO ",region_start,region_len,area_count,area_id,area_start,area_len"
#define STATS_REGION_INFO STATS_DEV_INFO ",region_start,region_len,area_count,area_len"
/* Default stats report options. */
static const char *_stats_default_report_options = STATS_DEV_INFO ",area_id,area_start,area_len," METRICS;
static const char *_stats_raw_report_options = STATS_DEV_INFO ",area_id,area_start,area_len," COUNTERS;
static const char *_stats_list_options = STATS_REGION_INFO ",program_id";
static const char *_stats_area_list_options = STATS_AREA_INFO ",program_id";
static int _report_init(const struct command *cmd, const char *subcommand)
{
char *options = (char *) default_report_options;
char *opt_fields = NULL; /* optional fields from command line */
const char *keys = "";
const char *separator = " ";
const char *selection = NULL;
int aligned = 1, headings = 1, buffered = 1, field_prefixes = 0;
int quoted = 1, columns_as_rows = 0;
uint32_t flags = 0;
size_t len = 0;
int r = 0;
if (cmd && !strcmp(cmd->name, "splitname")) {
options = (char *) splitname_report_options;
_report_type |= DR_NAME;
}
if (cmd && !strcmp(cmd->name, "stats")) {
_report_type |= DR_STATS_META;
if (!strcmp(subcommand, "list"))
options = (char *) ((_switches[VERBOSE_ARG])
? _stats_area_list_options
: _stats_list_options);
else {
options = (char *) ((!_switches[RAW_ARG])
? _stats_default_report_options
: _stats_raw_report_options);
_report_type |= DR_STATS;
}
}
if (cmd && !strcmp(cmd->name, "list")) {
options = (char *) _stats_list_options;
_report_type |= DR_STATS_META;
}
/* emulate old dmsetup behaviour */
if (_switches[NOHEADINGS_ARG]) {
separator = ":";
aligned = 0;
headings = 0;
}
if (_switches[UNBUFFERED_ARG])
buffered = 0;
if (_switches[ROWS_ARG])
columns_as_rows = 1;
if (_switches[UNQUOTED_ARG])
quoted = 0;
if (_switches[NAMEPREFIXES_ARG]) {
aligned = 0;
field_prefixes = 1;
}
if (_switches[OPTIONS_ARG] && _string_args[OPTIONS_ARG]) {
/* Count & interval forbidden for help. */
/* FIXME Detect "help" correctly and exit */
if (strstr(_string_args[OPTIONS_ARG], "help")) {
_switches[COUNT_ARG] = 0;
_count = 1;
_switches[INTERVAL_ARG] = 0;
headings = 0;
}
if (*_string_args[OPTIONS_ARG] != '+')
options = _string_args[OPTIONS_ARG];
else {
char *tmpopts;
opt_fields = _string_args[OPTIONS_ARG] + 1;
len = strlen(options) + strlen(opt_fields) + 2;
if (!(tmpopts = dm_malloc(len))) {
err("Failed to allocate option string.");
return 0;
}
if (dm_snprintf(tmpopts, len, "%s,%s",
options, opt_fields) < 0) {
dm_free(tmpopts);
return 0;
}
options = tmpopts;
}
}
if (_switches[SORT_ARG] && _string_args[SORT_ARG]) {
keys = _string_args[SORT_ARG];
buffered = 1;
if (cmd && (!strcmp(cmd->name, "status") || !strcmp(cmd->name, "table"))) {
err("--sort is not yet supported with status and table");
goto out;
}
}
if (_switches[SEPARATOR_ARG] && _string_args[SEPARATOR_ARG]) {
separator = _string_args[SEPARATOR_ARG];
aligned = 0;
}
if (_switches[SELECT_ARG] && _string_args[SELECT_ARG])
selection = _string_args[SELECT_ARG];
if (aligned)
flags |= DM_REPORT_OUTPUT_ALIGNED;
if (buffered)
flags |= DM_REPORT_OUTPUT_BUFFERED;
if (headings)
flags |= DM_REPORT_OUTPUT_HEADINGS;
if (field_prefixes)
flags |= DM_REPORT_OUTPUT_FIELD_NAME_PREFIX;
if (!quoted)
flags |= DM_REPORT_OUTPUT_FIELD_UNQUOTED;
if (columns_as_rows)
flags |= DM_REPORT_OUTPUT_COLUMNS_AS_ROWS;
if (!(_report = dm_report_init_with_selection(&_report_type, _report_types,
_report_fields, options, separator, flags, keys,
selection, NULL, NULL)))
goto out;
if ((_report_type & DR_TREE) && !_build_whole_deptree(cmd)) {
err("Internal device dependency tree creation failed.");
goto out;
}
if (!_switches[INTERVAL_ARG])
_int_args[INTERVAL_ARG] = 1; /* 1s default. */
_interval = NSEC_PER_SEC * (uint64_t) _int_args[INTERVAL_ARG];
if (field_prefixes)
dm_report_set_output_field_name_prefix(_report, "dm_");
r = 1;
out:
if (len)
dm_free(options);
return r;
}
/*
* List devices
*/
static int _ls(CMD_ARGS)
{
if ((_switches[TARGET_ARG] && _target) ||
(_switches[EXEC_ARG] && _command_to_exec))
return _status(cmd, NULL, argc, argv, NULL, 0);
else if ((_switches[TREE_ARG]))
return _display_tree(cmd, NULL, 0, NULL, NULL, 0);
else
return _process_all(cmd, NULL, argc, argv, 0, _display_name);
}
static int _mangle(CMD_ARGS)
{
const char *name, *uuid;
char *new_name = NULL, *new_uuid = NULL;
struct dm_task *dmt;
struct dm_info info;
int r = 0;
int target_format;
if (names)
name = names->name;
else {
if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG])
return _process_all(cmd, NULL, argc, argv, 0, _mangle);
name = argv[0];
}
if (!(dmt = dm_task_create(DM_DEVICE_STATUS)))
return 0;
if (!(_set_task_device(dmt, name, 0)))
goto out;
if (!_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
goto out;
if (!_task_run(dmt))
goto out;
if (!dm_task_get_info(dmt, &info) || !info.exists)
goto out;
uuid = dm_task_get_uuid(dmt);
target_format = _switches[MANGLENAME_ARG] ? _int_args[MANGLENAME_ARG]
: DEFAULT_DM_NAME_MANGLING;
if (target_format == DM_STRING_MANGLING_AUTO) {
if (strstr(name, "\\x5cx")) {
log_error("The name \"%s\" seems to be mangled more than once. "
"Manual intervention required to rename the device.", name);
goto out;
}
if (strstr(uuid, "\\x5cx")) {
log_error("The UUID \"%s\" seems to be mangled more than once. "
"Manual intervention required to correct the device UUID.", uuid);
goto out;
}
}
if (target_format == DM_STRING_MANGLING_NONE) {
if (!(new_name = dm_task_get_name_unmangled(dmt)))
goto out;
if (!(new_uuid = dm_task_get_uuid_unmangled(dmt)))
goto out;
}
else {
if (!(new_name = dm_task_get_name_mangled(dmt)))
goto out;
if (!(new_uuid = dm_task_get_uuid_mangled(dmt)))
goto out;
}
/* We can't rename the UUID, the device must be reactivated manually. */
if (strcmp(uuid, new_uuid)) {
log_error("%s: %s: UUID in incorrect form. ", name, uuid);
log_error("Unable to change device UUID. The device must be deactivated first.");
r = 0;
goto out;
}
/* Nothing to do if the name is in correct form already. */
if (!strcmp(name, new_name)) {
log_print("%s: %s: name %salready in correct form", name,
*uuid ? uuid : "[no UUID]", *uuid ? "and UUID " : "");
r = 1;
goto out;
}
else
log_print("%s: renaming to %s", name, new_name);
/* Rename to correct form of the name. */
r = _do_rename(name, new_name, NULL);
out:
dm_free(new_name);
dm_free(new_uuid);
dm_task_destroy(dmt);
return r;
}
static int _stats(CMD_ARGS);
static int _bind_stats_device(struct dm_stats *dms, const char *name)
{
if (name && !dm_stats_bind_name(dms, name))
return 0;
else if (_switches[UUID_ARG] && !dm_stats_bind_uuid(dms, _uuid))
return 0;
else if (_switches[MAJOR_ARG] && _switches[MINOR_ARG]
&& !dm_stats_bind_devno(dms, _int_args[MAJOR_ARG],
_int_args[MINOR_ARG]))
return 0;
return 1;
}
static int _stats_clear_regions(struct dm_stats *dms, uint64_t region_id)
{
int allregions = (region_id == DM_STATS_REGIONS_ALL);
if (!dm_stats_list(dms, NULL))
goto_out;
if (!dm_stats_get_nr_regions(dms))
goto done;
dm_stats_walk_do(dms) {
if (allregions)
region_id = dm_stats_get_current_region(dms);
if (!dm_stats_region_present(dms, region_id)) {
log_error("No such region: %"PRIu64".", region_id);
goto out;
}
if (!dm_stats_clear_region(dms, region_id)) {
log_error("Clearing statistics region %"PRIu64" failed.",
region_id);
goto out;
}
log_info("Cleared statistics region %"PRIu64".", region_id);
dm_stats_walk_next_region(dms);
} dm_stats_walk_while(dms);
done:
return 1;
out:
return 0;
}
static int _stats_clear(CMD_ARGS)
{
struct dm_stats *dms;
uint64_t region_id;
char *name = NULL;
int allregions = _switches[ALL_REGIONS_ARG];
/* clear does not use a report */
if (_report) {
dm_report_free(_report);
_report = NULL;
}
if (!_switches[REGION_ID_ARG] && !_switches[ALL_REGIONS_ARG]) {
err("Please specify a --regionid or use --allregions.");
return 0;
}
if (names)
name = names->name;
else {
if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG])
return _process_all(cmd, subcommand, argc, argv, 0, _stats_clear);
name = argv[0];
}
region_id = (allregions) ? DM_STATS_REGIONS_ALL
: (uint64_t) _int_args[REGION_ID_ARG];
dms = dm_stats_create(DM_STATS_PROGRAM_ID);
if (!_bind_stats_device(dms, name))
goto_out;
if (!_stats_clear_regions(dms, region_id))
goto_out;
dm_stats_destroy(dms);
return 1;
out:
dm_stats_destroy(dms);
return 0;
}
static uint64_t _factor_from_units(char *argptr, char *unit_type)
{
return dm_units_to_factor(argptr, unit_type, 0, NULL);
}
/**
* Parse a start, length, or area size argument in bytes from a string
* using optional units as supported by _factor_from_units().
*/
static int _size_from_string(char *argptr, uint64_t *size, const char *name)
{
uint64_t factor;
char *endptr = NULL, unit_type;
if (!argptr)
return 0;
*size = strtoull(argptr, &endptr, 10);
if (endptr == argptr) {
*size = 0;
log_error("Invalid %s argument: \"%s\"",
name, (*argptr) ? argptr : "");
return 0;
}
if (*endptr == '\0') {
*size *= 512;
return 1;
}
factor = _factor_from_units(endptr, &unit_type);
if (factor)
*size *= factor;
return 1;
}
/*
* FIXME: expose this from libdm-stats
*/
static uint64_t _nr_areas_from_step(uint64_t len, int64_t step)
{
/* Default is one area. */
if (!step || !len)
return 1;
/* --areas */
if (step < 0)
return (uint64_t)(-step);
/* --areasize - cast step to unsigned as it cannot be -ve here. */
return (len / (step ? : len)) + !!(len % (uint64_t) step);
}
/*
* Create a single region starting at start and spanning len sectors,
* or, if the segments argument is no-zero create one region for each
* segment present in the mapped device. Passing zero for segments,
* start, and length will create a single segment spanning the whole
* device.
*/
static int _do_stats_create_regions(struct dm_stats *dms,
const char *name, uint64_t start,
uint64_t len, int64_t step,
int segments,
const char *program_id,
const char *aux_data)
{
uint64_t this_start = 0, this_len = len, region_id = UINT64_C(0);
char *target_type, *params; /* unused */
struct dm_task *dmt;
struct dm_info info;
void *next = NULL;
const char *devname = NULL;
int r = 0, precise = _switches[PRECISE_ARG];
if (!(dmt = dm_task_create(DM_DEVICE_TABLE))) {
dm_stats_destroy(dms);
return 0;
}
if (!_set_task_device(dmt, name, 0))
goto out;
if (!dm_task_no_open_count(dmt))
goto out;
if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
goto out;
if (!_task_run(dmt))
goto out;
if (!dm_task_get_info(dmt, &info) || !info.exists)
goto out;
if (!(devname = dm_task_get_name(dmt)))
goto out;
do {
uint64_t segment_start, segment_len;
next = dm_get_next_target(dmt, next, &segment_start, &segment_len,
&target_type, &params);
/* Accumulate whole-device size for nr_areas calculation. */
if (!segments && !len)
this_len += segment_len;
/* Segments or whole-device. */
if (segments || !next) {
/*
* this_start and this_len hold the start and length in
* sectors of the to-be-created region: this is either the
* segment start/len (for --segments), the value of the
* --start/--length arguments, or 0/0 for a default
* whole-device region).
*/
this_start = (segments) ? segment_start : start;
this_len = (segments) ? segment_len : this_len;
if (!dm_stats_create_region(dms, &region_id,
this_start, this_len, step,
libdm: add latency histogram support Add support for creating, parsing, and reporting dm-stats latency histograms on kernels that support precise_timestamps. Histograms are specified as a series of time values that give the boundaries of the bins into which I/O counts accumulate (with implicit lower and upper bounds on the first and last bins). A new type, struct dm_histogram, is introduced to represent histogram values and bin boundaries. The boundary values may be given as either a string of values (with optional unit suffixes) or as a zero terminated array of uint64_t values expressing boundary times in nanoseconds. A new bounds argument is added to dm_stats_create_region() which accepts a pointer to a struct dm_histogram initialised with bounds values. Histogram data associated with a region is parsed during a call to dm_stats_populate() and used to build a table of histogram values that are pointed to from the containing area's counter set. The histogram for a specified area may then be obtained and interogated for values and properties. This relies on kernel support to provide the boundary values in a @stats_list response: this will be present in 4.3 and 4.2-stable. A check for a minimum driver version of 4.33.0 is implemented to ensure that this is present (4.32.0 has the necessary precise_timestamps and histogram features but is unable to report these via @stats_list). Access methods are provided to retrieve histogram values and bounds as well as simple string representations of the counts and bin boundaries. Methods are also available to return the total count for a histogram and the relative value (as a dm_percent_t) of a specified bin.
2015-08-19 22:39:10 +03:00
precise, NULL,
program_id, aux_data)) {
log_error("%s: Could not create statistics region.",
devname);
goto out;
}
printf("%s: Created new region with "FMTu64" area(s) as "
"region ID "FMTu64"\n", devname,
_nr_areas_from_step(this_len, step), region_id);
}
} while (next);
r = 1;
out:
dm_task_destroy(dmt);
dm_stats_destroy(dms);
return r;
}
static int _stats_create(CMD_ARGS)
{
struct dm_stats *dms;
const char *name, *aux_data = "", *program_id = DM_STATS_PROGRAM_ID;
uint64_t start = 0, len = 0, areas = 0, area_size = 0;
int64_t step = 0;
/* create does not use a report */
if (_report) {
dm_report_free(_report);
_report = NULL;
}
if (_switches[ALL_REGIONS_ARG]) {
log_error("Cannot use --allregions with create.");
return 0;
}
if (_switches[ALL_PROGRAMS_ARG]) {
log_error("Cannot use --allprograms with create.");
return 0;
}
if (_switches[AREAS_ARG] && _switches[AREA_SIZE_ARG]) {
log_error("Please specify one of --areas and --areasize.");
return 0;
}
if (_switches[PROGRAM_ID_ARG]
&& !strlen(_string_args[PROGRAM_ID_ARG]) && !_switches[FORCE_ARG]) {
log_error("Creating a region with no program "
"id requires --force.");
return 0;
}
if (names)
name = names->name;
else {
if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG]) {
if (!_switches[ALL_DEVICES_ARG]) {
log_error("Please specify device(s) or use "
"--alldevices.");
return 0;
}
return _process_all(cmd, subcommand, argc, argv, 0, _stats_create);
}
name = argv[0];
}
if (_switches[AREAS_ARG])
areas = (uint64_t) _int_args[AREAS_ARG];
if (_switches[AREA_SIZE_ARG])
if (!_size_from_string(_string_args[AREA_SIZE_ARG],
&area_size, "areasize"))
return 0;
areas = (areas) ? areas : 1;
/* bytes to sectors or -(areas): promote to signed before conversion */
step = (area_size) ? ((int64_t) area_size / 512) : -((int64_t) areas);
if (_switches[START_ARG]) {
if (!_size_from_string(_string_args[START_ARG],
&start, "start"))
return 0;
}
/* bytes to sectors */
start /= 512;
if (_switches[LENGTH_ARG]) {
if (!_size_from_string(_string_args[LENGTH_ARG],
&len, "length"))
return 0;
}
/* bytes to sectors */
len /= 512;
if (_switches[PROGRAM_ID_ARG])
program_id = _string_args[PROGRAM_ID_ARG];
if (!strlen(program_id) && !_switches[FORCE_ARG])
program_id = DM_STATS_PROGRAM_ID;
if (_switches[AUX_DATA_ARG])
aux_data = _string_args[AUX_DATA_ARG];
dms = dm_stats_create(DM_STATS_PROGRAM_ID);
if (!_bind_stats_device(dms, name))
goto_out;
if (_switches[PRECISE_ARG]) {
if (!dm_stats_driver_supports_precise()) {
log_error("Using --precise requires driver version "
"4.32.0 or later.");
goto out;
}
}
if (!strlen(program_id))
/* force creation of a region with no id */
dm_stats_set_program_id(dms, 1, NULL);
return _do_stats_create_regions(dms, name, start, len, step,
_switches[SEGMENTS_ARG],
program_id, aux_data);
out:
dm_stats_destroy(dms);
return 0;
}
static int _stats_delete(CMD_ARGS)
{
struct dm_stats *dms;
uint64_t region_id;
char *name = NULL;
const char *program_id = DM_STATS_PROGRAM_ID;
int allregions = _switches[ALL_REGIONS_ARG];
/* delete does not use a report */
if (_report) {
dm_report_free(_report);
_report = NULL;
}
if (!_switches[REGION_ID_ARG] && !allregions) {
err("Please specify a --regionid or use --allregions.");
return 0;
}
if (names)
name = names->name;
else {
if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG]) {
if (!_switches[ALL_DEVICES_ARG]) {
log_error("Please specify device(s) or use "
"--alldevices.");
return 0;
}
return _process_all(cmd, subcommand, argc, argv, 0, _stats_delete);
}
name = argv[0];
}
if (_switches[ALL_PROGRAMS_ARG])
program_id = DM_STATS_ALL_PROGRAMS;
region_id = (uint64_t) _int_args[REGION_ID_ARG];
dms = dm_stats_create(program_id);
if (!_bind_stats_device(dms, name))
goto_out;
if (allregions && !dm_stats_list(dms, program_id))
goto_out;
if (allregions && !dm_stats_get_nr_regions(dms))
/* no regions present */
goto done;
dm_stats_walk_do(dms) {
if (_switches[ALL_REGIONS_ARG])
region_id = dm_stats_get_current_region(dms);
if (!dm_stats_delete_region(dms, region_id)) {
log_error("Could not delete statistics region.");
goto out;
}
log_info("Deleted statistics region %" PRIu64, region_id);
dm_stats_walk_next_region(dms);
} dm_stats_walk_while(dms);
done:
dm_stats_destroy(dms);
return 1;
out:
dm_stats_destroy(dms);
return 0;
}
static int _stats_print(CMD_ARGS)
{
struct dm_stats *dms;
char *name, *stbuff = NULL;
uint64_t region_id;
unsigned clear = (unsigned) _switches[CLEAR_ARG];
int allregions = _switches[ALL_REGIONS_ARG];
/* print does not use a report */
if (_report) {
dm_report_free(_report);
_report = NULL;
}
if (!_switches[REGION_ID_ARG] && !allregions) {
err("Please specify a --regionid or use --allregions.");
return 0;
}
if (names)
name = names->name;
else {
if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG])
return _process_all(cmd, subcommand, argc, argv, 0, _stats_print);
name = argv[0];
}
region_id = (uint64_t) _int_args[REGION_ID_ARG];
dms = dm_stats_create(DM_STATS_PROGRAM_ID);
if (!_bind_stats_device(dms, name))
goto_out;
if (!dm_stats_list(dms, NULL))
goto_out;
if (allregions && !dm_stats_get_nr_regions(dms))
goto done;
dm_stats_walk_do(dms) {
if (_switches[ALL_REGIONS_ARG])
region_id = dm_stats_get_current_region(dms);
if (!dm_stats_region_present(dms, region_id)) {
log_error("No such region: %"PRIu64".", region_id);
goto out;
}
/*FIXME: line control for large regions */
if (!(stbuff = dm_stats_print_region(dms, region_id, 0, 0, clear))) {
log_error("Could not print statistics region.");
goto out;
}
printf("%s", stbuff);
dm_stats_buffer_destroy(dms, stbuff);
dm_stats_walk_next_region(dms);
} dm_stats_walk_while(dms);
done:
dm_stats_destroy(dms);
return 1;
out:
dm_stats_destroy(dms);
return 0;
}
static int _stats_report(CMD_ARGS)
{
int r = 0;
struct dm_task *dmt;
char *name = NULL;
if (_switches[PROGRAM_ID_ARG])
_program_id = _string_args[PROGRAM_ID_ARG];
if (_switches[ALL_PROGRAMS_ARG])
_program_id = "";
if (!_switches[VERBOSE_ARG] && !strcmp(subcommand, "list"))
_stats_report_by_areas = 0;
if (names)
name = names->name;
else {
if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG])
return _process_all(cmd, subcommand, argc, argv, 0, _info);
name = argv[0];
}
if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
return 0;
if (!_set_task_device(dmt, name, 0))
goto out;
if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
goto out;
if (!_task_run(dmt))
goto out;
r = _display_info(dmt);
out:
dm_task_destroy(dmt);
if (!r && _report) {
dm_report_free(_report);
_report = NULL;
}
return r;
}
/*
* Command dispatch tables and usage.
*/
static int _stats_help(CMD_ARGS);
/*
* dmsetup stats <cmd> [options] [device_name]
* dmstats <cmd> [options] [device_name]
*
* clear [--regionid id] <device_name>
* create [--areas nr_areas] [--areasize size]
* [ [--start start] [--length len] | [--segments]]
* [--auxdata data] [--programid id] [<device_name>]
* delete [--regionid] <device_name>
* delete_all [--programid id]
* list [--programid id] [<device_name>]
* print [--clear] [--programid id] [--regionid id] [<device_name>]
* report [--interval seconds] [--count count] [--units units] [--regionid id]
* [--programid id] [<device>]
*/
#define AREA_OPTS "[--areas <nr_areas>] [--areasize <size>] "
#define CREATE_OPTS "[--start <start> [--length <len>]]\n\t\t" AREA_OPTS
#define ID_OPTS "[--programid <id>] [--auxdata <data> ] "
#define SELECT_OPTS "[--programid <id>] [--regionid <id>] "
#define PRINT_OPTS "[--clear] " SELECT_OPTS
#define REPORT_OPTS "[--interval <seconds>] [--count <cnt>]\n\t\t[--units <u>]" SELECT_OPTS
static struct command _stats_subcommands[] = {
{"help", "", 0, 0, 0, 0, _stats_help},
{"clear", "--regionid <id> [<device>]", 0, -1, 1, 0, _stats_clear},
{"create", CREATE_OPTS "\n\t\t" ID_OPTS "[<device>]", 0, -1, 1, 0, _stats_create},
{"delete", "--regionid <id> <device>", 1, -1, 1, 0, _stats_delete},
{"list", "[--programid <id>] [<device>]", 0, -1, 1, 0, _stats_report},
{"print", PRINT_OPTS "[<device>]", 0, -1, 1, 0, _stats_print},
{"report", REPORT_OPTS "[<device>]", 0, -1, 1, 0, _stats_report},
{"version", "", 0, -1, 1, 0, _version},
{NULL, NULL, 0, 0, 0, 0, NULL}
};
#undef AREA_OPTS
#undef CREATE_OPTS
#undef ID_OPTS
#undef PRINT_OPTS
#undef REPORT_OPTS
#undef SELECT_OPTS
static int _dmsetup_help(CMD_ARGS);
static struct command _dmsetup_commands[] = {
{"help", "[-c|-C|--columns]", 0, 0, 0, 0, _dmsetup_help},
{"create", "<dev_name>\n"
"\t [-j|--major <major> -m|--minor <minor>]\n"
"\t [-U|--uid <uid>] [-G|--gid <gid>] [-M|--mode <octal_mode>]\n"
"\t [-u|uuid <uuid>] [{--addnodeonresume|--addnodeoncreate}]\n"
"\t [--notable | --table <table> | <table_file>]", 1, 2, 0, 0, _create},
{"remove", "[-f|--force] [--deferred] <device>", 0, -1, 1, 0, _remove},
{"remove_all", "[-f|--force]", 0, 0, 0, 0, _remove_all},
{"suspend", "[--noflush] <device>", 0, -1, 1, 0, _suspend},
{"resume", "<device> [{--addnodeonresume|--addnodeoncreate}]", 0, -1, 1, 0, _resume},
{"load", "<device> [<table_file>]", 0, 2, 0, 0, _load},
{"clear", "<device>", 0, -1, 1, 0, _clear},
{"reload", "<device> [<table_file>]", 0, 2, 0, 0, _load},
{"wipe_table", "<device>", 1, -1, 1, 0, _error_device},
{"rename", "<device> [--setuuid] <new_name_or_uuid>", 1, 2, 0, 0, _rename},
{"message", "<device> <sector> <message>", 2, -1, 0, 0, _message},
{"ls", "[--target <target_type>] [--exec <command>] [-o options] [--tree]", 0, 0, 0, 0, _ls},
{"info", "[<device>]", 0, -1, 1, 0, _info},
{"deps", "[-o options] [<device>]", 0, -1, 1, 0, _deps},
{"stats", "<command> [<options>] [<devices>]", 1, -1, 1, 1, _stats},
{"status", "[<device>] [--noflush] [--target <target_type>]", 0, -1, 1, 0, _status},
{"table", "[<device>] [--target <target_type>] [--showkeys]", 0, -1, 1, 0, _status},
{"wait", "<device> [<event_nr>] [--noflush]", 0, 2, 0, 0, _wait},
{"mknodes", "[<device>]", 0, -1, 1, 0, _mknodes},
{"mangle", "[<device>]", 0, -1, 1, 0, _mangle},
{"udevcreatecookie", "", 0, 0, 0, 0, _udevcreatecookie},
{"udevreleasecookie", "[<cookie>]", 0, 1, 0, 0, _udevreleasecookie},
{"udevflags", "<cookie>", 1, 1, 0, 0, _udevflags},
{"udevcomplete", "<cookie>", 1, 1, 0, 0, _udevcomplete},
{"udevcomplete_all", "<age_in_minutes>", 0, 1, 0, 0, _udevcomplete_all},
{"udevcookies", "", 0, 0, 0, 0, _udevcookies},
{"targets", "", 0, 0, 0, 0, _targets},
{"version", "", 0, 0, 0, 0, _version},
{"setgeometry", "<device> <cyl> <head> <sect> <start>", 5, 5, 0, 0, _setgeometry},
{"splitname", "<device> [<subsystem>]", 1, 2, 0, 0, _splitname},
{NULL, NULL, 0, 0, 0, 0, NULL}
};
/*
* Usage and help text.
*/
static void _devmap_name_usage(FILE *out)
{
fprintf(out, "Usage: " DEVMAP_NAME_CMD_NAME " <major> <minor>\n\n");
}
static void _stats_usage(FILE *out)
{
int i;
fprintf(out, "Usage:\n\n");
fprintf(out, "%s\n", _base_commands[_base_command].name);
fprintf(out, " [-h|--help]\n");
fprintf(out, " [-v|--verbose [-v|--verbose ...]]\n");
fprintf(out, " [--areas <nr_areas>] [--areasize <size>]\n");
fprintf(out, " [--auxdata <data>] [--clear]\n");
fprintf(out, " [--count <count>] [--interval <seconds>]\n");
fprintf(out, " [-o <fields>] [-O|--sort <sort_fields>]\n");
fprintf(out, " [--programid <id>]\n");
fprintf(out, " [--start <start>] [--length <length>]\n");
fprintf(out, " [--segments] [--units <units>]\n\n");
for (i = 0; _stats_subcommands[i].name; i++)
fprintf(out, "\t%s %s\n", _stats_subcommands[i].name, _stats_subcommands[i].help);
fprintf(out, "<device> may be device name or -u <uuid> or "
"-j <major> -m <minor>\n");
fprintf(out, "<fields> are comma-separated. Use 'help -c' for list.\n");
fprintf(out, "\n");
}
static void _dmsetup_usage(FILE *out)
{
int i;
fprintf(out, "Usage:\n\n");
fprintf(out, "%s\n"
" [--version] [-h|--help [-c|-C|--columns]]\n"
" [-v|--verbose [-v|--verbose ...]]\n"
" [--checks] [--manglename <mangling_mode>]\n"
" [-r|--readonly] [--noopencount] [--nolockfs] [--inactive]\n"
" [--udevcookie [cookie]] [--noudevrules] [--noudevsync] [--verifyudev]\n"
" [-y|--yes] [--readahead [+]<sectors>|auto|none] [--retry]\n"
2007-01-29 21:45:08 +03:00
" [-c|-C|--columns] [-o <fields>] [-O|--sort <sort_fields>]\n"
" [-S|--select <selection>] [--nameprefixes] [--noheadings]\n"
" [--separator <separator>]\n\n",
_base_commands[_base_command].name);
for (i = 0; _dmsetup_commands[i].name; i++)
fprintf(out, "\t%s %s\n", _dmsetup_commands[i].name, _dmsetup_commands[i].help);
fprintf(out, "\n<device> may be device name or -u <uuid> or "
"-j <major> -m <minor>\n");
fprintf(out, "<mangling_mode> is one of 'none', 'auto' and 'hex'.\n");
2007-01-29 21:37:57 +03:00
fprintf(out, "<fields> are comma-separated. Use 'help -c' for list.\n");
2006-04-06 20:20:40 +04:00
fprintf(out, "Table_file contents may be supplied on stdin.\n");
fprintf(out, "Options are: devno, devname, blkdevname.\n");
fprintf(out, "Tree specific options are: ascii, utf, vt100; compact, inverted, notrunc;\n"
" blkdevname, [no]device, active, open, rw and uuid.\n");
fprintf(out, "\n");
}
static void _losetup_usage(FILE *out)
{
fprintf(out, "Usage:\n\n");
fprintf(out, "%s [-d|-a] [-e encryption] "
"[-o offset] [-f|loop_device] [file]\n\n",
_base_commands[_base_command].name);
}
static void _usage(FILE *out)
{
switch (_base_commands[_base_command].type) {
case DMSETUP_TYPE:
return _dmsetup_usage(out);
case LOSETUP_TYPE:
return _losetup_usage(out);
case STATS_TYPE:
return _stats_usage(out);
case DEVMAP_NAME_TYPE:
return _devmap_name_usage(out);
}
}
static int _stats_help(CMD_ARGS)
{
_usage(stderr);
if (_switches[COLS_ARG] || (argc && !strcmp(argv[0], "report"))) {
_switches[OPTIONS_ARG] = 1;
_string_args[OPTIONS_ARG] = (char *) "help";
_switches[SORT_ARG] = 0;
if (_report) {
dm_report_free(_report);
_report = NULL;
}
(void) _report_init(cmd, "help");
if (_report) {
dm_report_free(_report);
_report = NULL;
}
}
return 1;
}
static int _dmsetup_help(CMD_ARGS)
{
_usage(stderr);
2007-01-29 21:37:57 +03:00
if (_switches[COLS_ARG]) {
_switches[OPTIONS_ARG] = 1;
_string_args[OPTIONS_ARG] = (char *) "help";
_switches[SORT_ARG] = 0;
if (_report) {
dm_report_free(_report);
_report = NULL;
}
(void) _report_init(cmd, "");
if (_report) {
dm_report_free(_report);
_report = NULL;
}
2007-01-29 21:37:57 +03:00
}
return 1;
}
2015-07-31 18:58:14 +03:00
static const struct command *_find_command(const struct command *commands,
const char *name)
{
int i;
for (i = 0; commands[i].name; i++)
if (!strcmp(commands[i].name, name))
return commands + i;
return NULL;
}
2015-07-31 18:58:14 +03:00
static const struct command *_find_dmsetup_command(const char *name)
{
return _find_command(_dmsetup_commands, name);
}
static const struct command *_find_stats_subcommand(const char *name)
{
return _find_command(_stats_subcommands, name);
}
static int _stats(CMD_ARGS)
{
const struct command *stats_cmd;
if (!(stats_cmd = _find_stats_subcommand(subcommand))) {
log_error("Unknown stats command.");
_stats_help(stats_cmd, NULL, argc, argv, NULL, multiple_devices);
return 0;
}
if (_switches[ALL_PROGRAMS_ARG] && _switches[PROGRAM_ID_ARG]) {
log_error("Please supply one of --allprograms and --programid");
return 0;
}
if (_switches[ALL_REGIONS_ARG] && _switches[REGION_ID_ARG]) {
log_error("Please supply one of --allregions and --regionid");
return 0;
}
/*
* Pass the sub-command through to allow a single function to be
* used to implement several distinct sub-commands (e.g. 'report'
* and 'list' share a single implementation.
*/
if (!stats_cmd->fn(stats_cmd, subcommand, argc, argv, NULL,
multiple_devices))
return 0;
return 1;
}
static int _process_tree_options(const char *options)
{
const char *s, *end;
struct winsize winsz;
size_t len;
/* Symbol set default */
if (!strcmp(nl_langinfo(CODESET), "UTF-8"))
_tsym = &_tsym_utf;
else
_tsym = &_tsym_ascii;
/* Default */
_tree_switches[TR_DEVICE] = 1;
_tree_switches[TR_TRUNCATE] = 1;
/* parse */
for (s = options; s && *s; s++) {
len = 0;
for (end = s; *end && *end != ','; end++, len++)
;
if (!strncmp(s, "device", len))
_tree_switches[TR_DEVICE] = 1;
else if (!strncmp(s, "blkdevname", len))
_tree_switches[TR_BLKDEVNAME] = 1;
else if (!strncmp(s, "nodevice", len))
_tree_switches[TR_DEVICE] = 0;
else if (!strncmp(s, "status", len))
_tree_switches[TR_STATUS] = 1;
else if (!strncmp(s, "table", len))
_tree_switches[TR_TABLE] = 1;
else if (!strncmp(s, "active", len))
_tree_switches[TR_ACTIVE] = 1;
else if (!strncmp(s, "open", len))
_tree_switches[TR_OPENCOUNT] = 1;
else if (!strncmp(s, "uuid", len))
_tree_switches[TR_UUID] = 1;
else if (!strncmp(s, "rw", len))
_tree_switches[TR_RW] = 1;
else if (!strncmp(s, "utf", len))
_tsym = &_tsym_utf;
else if (!strncmp(s, "vt100", len))
_tsym = &_tsym_vt100;
else if (!strncmp(s, "ascii", len))
_tsym = &_tsym_ascii;
else if (!strncmp(s, "inverted", len))
_tree_switches[TR_BOTTOMUP] = 1;
else if (!strncmp(s, "compact", len))
_tree_switches[TR_COMPACT] = 1;
else if (!strncmp(s, "notrunc", len))
_tree_switches[TR_TRUNCATE] = 0;
else {
fprintf(stderr, "Tree options not recognised: %s\n", s);
return 0;
}
if (!*end)
break;
s = end;
}
/* Truncation doesn't work well with vt100 drawing char */
if (_tsym != &_tsym_vt100)
if (ioctl(1, (unsigned long) TIOCGWINSZ, &winsz) >= 0 && winsz.ws_col > 3)
_termwidth = winsz.ws_col - 3;
return 1;
}
/*
* Returns the full absolute path, or NULL if the path could
* not be resolved.
*/
static char *_get_abspath(const char *path)
{
char *_path;
#ifdef HAVE_CANONICALIZE_FILE_NAME
_path = canonicalize_file_name(path);
#else
/* FIXME Provide alternative */
log_error(INTERNAL_ERROR "Unimplemented _get_abspath.");
_path = NULL;
#endif
return _path;
}
static char *parse_loop_device_name(const char *dev, const char *dev_dir)
{
char *buf;
char *device = NULL;
2007-01-17 00:13:07 +03:00
if (!(buf = dm_malloc(PATH_MAX)))
return NULL;
if (dev[0] == '/') {
if (!(device = _get_abspath(dev)))
goto error;
if (strncmp(device, dev_dir, strlen(dev_dir)))
goto error;
/* If dev_dir does not end in a slash, ensure that the
following byte in the device string is "/". */
2007-12-05 20:05:04 +03:00
if (dev_dir[strlen(dev_dir) - 1] != '/' &&
device[strlen(dev_dir)] != '/')
goto error;
if (!dm_strncpy(buf, strrchr(device, '/') + 1, PATH_MAX))
goto error;
dm_free(device);
} else {
/* check for device number */
if (strncmp(dev, "loop", sizeof("loop") - 1))
goto error;
if (!dm_strncpy(buf, dev, PATH_MAX))
goto error;
}
return buf;
error:
dm_free(device);
dm_free(buf);
return NULL;
}
/*
* create a table for a mapped device using the loop target.
*/
static int _loop_table(char *table, size_t tlen, char *file,
char *dev __attribute__((unused)), off_t off)
{
struct stat fbuf;
off_t size, sectors;
int fd = -1;
#ifdef HAVE_SYS_STATVFS_H
struct statvfs fsbuf;
off_t blksize;
#endif
if (!_switches[READ_ONLY])
fd = open(file, O_RDWR);
if (fd < 0) {
_switches[READ_ONLY]++;
fd = open(file, O_RDONLY);
}
if (fd < 0)
goto error;
if (fstat(fd, &fbuf))
goto error;
size = (fbuf.st_size - off);
sectors = size >> SECTOR_SHIFT;
if (_switches[VERBOSE_ARG])
fprintf(stderr, LOSETUP_CMD_NAME ": set loop size to %llukB "
"(%llu sectors)\n", (long long unsigned) sectors >> 1,
(long long unsigned) sectors);
#ifdef HAVE_SYS_STATVFS_H
if (fstatvfs(fd, &fsbuf))
goto error;
/* FIXME Fragment size currently unused */
blksize = fsbuf.f_frsize;
#endif
if (close(fd))
2015-08-05 07:03:33 +03:00
log_sys_error("close", file);
if (dm_snprintf(table, tlen, "%llu %llu loop %s %llu\n", 0ULL,
(long long unsigned)sectors, file, (long long unsigned)off) < 0)
return 0;
if (_switches[VERBOSE_ARG] > 1)
fprintf(stderr, "Table: %s\n", table);
return 1;
error:
if (fd > -1 && close(fd))
log_sys_error("close", file);
return 0;
}
2015-08-01 00:53:38 +03:00
static int _process_losetup_switches(const char *base, int *argcp, char ***argvp,
const char *dev_dir)
{
int c;
int encrypt_loop = 0, delete = 0, find = 0, show_all = 0;
char *device_name = NULL;
char *loop_file = NULL;
off_t offset = 0;
#ifdef HAVE_GETOPTLONG
static struct option long_options[] = {
{0, 0, 0, 0}
};
#endif
optarg = 0;
optind = OPTIND_INIT;
2015-08-01 00:53:38 +03:00
while ((c = GETOPTLONG_FN(*argcp, *argvp, "ade:fo:v",
long_options, NULL)) != -1 ) {
if (c == ':' || c == '?')
return 0;
if (c == 'a')
show_all++;
if (c == 'd')
delete++;
if (c == 'e')
encrypt_loop++;
if (c == 'f')
find++;
if (c == 'o')
offset = atoi(optarg);
if (c == 'v')
_switches[VERBOSE_ARG]++;
}
2015-08-01 00:53:38 +03:00
*argvp += optind ;
*argcp -= optind ;
if (encrypt_loop){
fprintf(stderr, "%s: Sorry, cryptoloop is not yet implemented "
"in this version.\n", base);
return 0;
}
if (show_all) {
fprintf(stderr, "%s: Sorry, show all is not yet implemented "
"in this version.\n", base);
return 0;
}
if (find) {
fprintf(stderr, "%s: Sorry, find is not yet implemented "
"in this version.\n", base);
2015-08-01 00:53:38 +03:00
if (!*argcp)
return 0;
}
2015-08-01 00:53:38 +03:00
if (!*argcp) {
fprintf(stderr, "%s: Please specify loop_device.\n", base);
_usage(stderr);
return 0;
}
2015-08-01 00:53:38 +03:00
if (!(device_name = parse_loop_device_name((*argvp)[0], dev_dir))) {
fprintf(stderr, "%s: Could not parse loop_device %s\n",
2015-08-01 00:53:38 +03:00
base, (*argvp)[0]);
_usage(stderr);
return 0;
}
if (delete) {
*argcp = 1;
(*argvp)[0] = device_name;
_command = "remove";
return 1;
}
2015-08-01 00:53:38 +03:00
if (*argcp != 2) {
fprintf(stderr, "%s: Too few arguments\n", base);
_usage(stderr);
dm_free(device_name);
return 0;
}
/* FIXME move these to make them available to native dmsetup */
2015-08-01 00:53:38 +03:00
if (!(loop_file = _get_abspath((*argvp)[(find) ? 0 : 1]))) {
fprintf(stderr, "%s: Could not parse loop file name %s\n",
2015-08-01 00:53:38 +03:00
base, (*argvp)[1]);
_usage(stderr);
dm_free(device_name);
return 0;
}
_table = dm_malloc(LOOP_TABLE_SIZE);
if (!_table ||
!_loop_table(_table, (size_t) LOOP_TABLE_SIZE, loop_file, device_name, offset)) {
2015-08-01 00:53:38 +03:00
fprintf(stderr, "Could not build device-mapper table for %s\n", (*argvp)[0]);
dm_free(device_name);
return 0;
}
_switches[TABLE_ARG]++;
_command = "create";
(*argvp)[0] = device_name ;
*argcp = 1;
return 1;
}
static int _process_options(const char *options)
{
const char *s, *end;
size_t len;
/* Tree options are processed separately. */
if (_switches[TREE_ARG])
return _process_tree_options(_string_args[OPTIONS_ARG]);
/* Column options are processed separately by _report_init (called later). */
if (_switches[COLS_ARG])
return 1;
/* No options specified. */
if (!_switches[OPTIONS_ARG])
return 1;
/* Set defaults. */
_dev_name_type = DN_DEVNO;
/* Parse. */
for (s = options; s && *s; s++) {
len = 0;
for (end = s; *end && *end != ','; end++, len++)
;
if (!strncmp(s, "devno", len))
_dev_name_type = DN_DEVNO;
else if (!strncmp(s, "blkdevname", len))
_dev_name_type = DN_BLK;
else if (!strncmp(s, "devname", len))
_dev_name_type = DN_MAP;
else {
fprintf(stderr, "Option not recognised: %s\n", s);
return 0;
}
if (!*end)
break;
s = end;
}
return 1;
}
2015-08-01 00:53:38 +03:00
static int _process_switches(int *argcp, char ***argvp, const char *dev_dir)
{
const char *base;
char *namebase, *s;
static int ind;
int c, r, i;
#ifdef HAVE_GETOPTLONG
static struct option long_options[] = {
{"readonly", 0, &ind, READ_ONLY},
{"alldevices", 0, &ind, ALL_DEVICES_ARG},
{"allprograms", 0, &ind, ALL_PROGRAMS_ARG},
{"allregions", 0, &ind, ALL_REGIONS_ARG},
{"areas", 1, &ind, AREAS_ARG},
{"areasize", 1, &ind, AREA_SIZE_ARG},
{"auxdata", 1, &ind, AUX_DATA_ARG},
{"checks", 0, &ind, CHECKS_ARG},
{"clear", 0, &ind, CLEAR_ARG},
{"columns", 0, &ind, COLS_ARG},
{"count", 1, &ind, COUNT_ARG},
{"deferred", 0, &ind, DEFERRED_ARG},
{"select", 1, &ind, SELECT_ARG},
2005-05-17 00:46:46 +04:00
{"exec", 1, &ind, EXEC_ARG},
{"force", 0, &ind, FORCE_ARG},
{"gid", 1, &ind, GID_ARG},
{"help", 0, &ind, HELP_ARG},
{"inactive", 0, &ind, INACTIVE_ARG},
{"interval", 1, &ind, INTERVAL_ARG},
{"length", 1, &ind, LENGTH_ARG},
{"manglename", 1, &ind, MANGLENAME_ARG},
{"major", 1, &ind, MAJOR_ARG},
{"minor", 1, &ind, MINOR_ARG},
{"mode", 1, &ind, MODE_ARG},
2008-06-06 22:53:08 +04:00
{"nameprefixes", 0, &ind, NAMEPREFIXES_ARG},
{"noflush", 0, &ind, NOFLUSH_ARG},
{"noheadings", 0, &ind, NOHEADINGS_ARG},
{"nolockfs", 0, &ind, NOLOCKFS_ARG},
{"noopencount", 0, &ind, NOOPENCOUNT_ARG},
{"nosuffix", 0, &ind, NOSUFFIX_ARG},
{"notable", 0, &ind, NOTABLE_ARG},
{"udevcookie", 1, &ind, UDEVCOOKIE_ARG},
{"noudevrules", 0, &ind, NOUDEVRULES_ARG},
{"noudevsync", 0, &ind, NOUDEVSYNC_ARG},
{"options", 1, &ind, OPTIONS_ARG},
{"precise", 0, &ind, PRECISE_ARG},
{"programid", 1, &ind, PROGRAM_ID_ARG},
{"raw", 0, &ind, RAW_ARG},
{"readahead", 1, &ind, READAHEAD_ARG},
{"regionid", 1, &ind, REGION_ID_ARG},
{"retry", 0, &ind, RETRY_ARG},
2008-06-25 02:53:48 +04:00
{"rows", 0, &ind, ROWS_ARG},
{"segments", 0, &ind, SEGMENTS_ARG},
{"separator", 1, &ind, SEPARATOR_ARG},
{"setuuid", 0, &ind, SETUUID_ARG},
{"showkeys", 0, &ind, SHOWKEYS_ARG},
{"sort", 1, &ind, SORT_ARG},
{"start", 1, &ind, START_ARG},
{"table", 1, &ind, TABLE_ARG},
{"target", 1, &ind, TARGET_ARG},
{"tree", 0, &ind, TREE_ARG},
{"uid", 1, &ind, UID_ARG},
{"units", 1, &ind, UNITS_ARG},
{"uuid", 1, &ind, UUID_ARG},
{"unbuffered", 0, &ind, UNBUFFERED_ARG},
2008-06-25 00:16:47 +04:00
{"unquoted", 0, &ind, UNQUOTED_ARG},
{"verbose", 1, &ind, VERBOSE_ARG},
{"verifyudev", 0, &ind, VERIFYUDEV_ARG},
{"version", 0, &ind, VERSION_ARG},
{"yes", 0, &ind, YES_ARG},
{"addnodeonresume", 0, &ind, ADD_NODE_ON_RESUME_ARG},
{"addnodeoncreate", 0, &ind, ADD_NODE_ON_CREATE_ARG},
{0, 0, 0, 0}
};
#else
struct option long_options;
#endif
/*
* Zero all the index counts.
*/
memset(&_switches, 0, sizeof(_switches));
memset(&_int_args, 0, sizeof(_int_args));
_read_ahead_flags = 0;
2015-08-01 00:53:38 +03:00
if (!(namebase = strdup((*argvp)[0]))) {
fprintf(stderr, "Failed to duplicate name.\n");
return 0;
}
base = dm_basename(namebase);
2005-03-27 15:37:46 +04:00
i = 0;
do {
if (!strcmp(base, _base_commands[i].name)) {
_base_command = _base_commands[i].command;
_base_command_type = _base_commands[i].type;
break;
}
} while (++i < _num_base_commands);
free(namebase);
if (_base_command_type == DEVMAP_NAME_TYPE) {
2005-03-27 15:37:46 +04:00
_switches[COLS_ARG]++;
_switches[NOHEADINGS_ARG]++;
_switches[OPTIONS_ARG]++;
_switches[MAJOR_ARG]++;
_switches[MINOR_ARG]++;
_string_args[OPTIONS_ARG] = (char *) "name";
2005-03-27 15:37:46 +04:00
2015-08-01 00:53:38 +03:00
if (*argcp == 3) {
_int_args[MAJOR_ARG] = atoi((*argvp)[1]);
_int_args[MINOR_ARG] = atoi((*argvp)[2]);
*argcp -= 2;
*argvp += 2;
} else if ((*argcp == 2) &&
(2 == sscanf((*argvp)[1], "%i:%i",
&_int_args[MAJOR_ARG],
&_int_args[MINOR_ARG]))) {
2015-08-01 00:53:38 +03:00
*argcp -= 1;
*argvp += 1;
2005-03-27 15:37:46 +04:00
} else {
_usage(stderr);
2005-03-27 15:37:46 +04:00
return 0;
}
_command = "info";
(*argvp)++;
(*argcp)--;
2005-03-27 15:37:46 +04:00
return 1;
}
if (_base_command_type == LOSETUP_TYPE) {
r = _process_losetup_switches(_base_commands[_base_command].name, argcp, argvp, dev_dir);
return r;
}
optarg = 0;
optind = OPTIND_INIT;
2015-08-01 00:53:38 +03:00
while ((ind = -1, c = GETOPTLONG_FN(*argcp, *argvp, "cCfG:hj:m:M:no:O:rS:u:U:vy",
long_options, NULL)) != -1) {
if (ind == ALL_DEVICES_ARG)
_switches[ALL_DEVICES_ARG]++;
if (ind == ALL_PROGRAMS_ARG)
_switches[ALL_PROGRAMS_ARG]++;
if (ind == ALL_REGIONS_ARG)
_switches[ALL_REGIONS_ARG]++;
if (ind == AREAS_ARG) {
_switches[AREAS_ARG]++;
_int_args[AREAS_ARG] = atoi(optarg);
}
if (ind == AREA_SIZE_ARG) {
_switches[AREA_SIZE_ARG]++;
_string_args[AREA_SIZE_ARG] = optarg;
}
if (ind == AUX_DATA_ARG) {
_switches[AUX_DATA_ARG]++;
_string_args[AUX_DATA_ARG] = optarg;
}
2006-08-11 00:53:21 +04:00
if (c == ':' || c == '?')
return 0;
if (c == 'h' || ind == HELP_ARG)
_switches[HELP_ARG]++;
if (ind == CLEAR_ARG)
_switches[CLEAR_ARG]++;
if (c == 'c' || c == 'C' || ind == COLS_ARG)
_switches[COLS_ARG]++;
if (c == 'f' || ind == FORCE_ARG)
_switches[FORCE_ARG]++;
2003-01-22 00:25:51 +03:00
if (c == 'r' || ind == READ_ONLY)
_switches[READ_ONLY]++;
if (ind == LENGTH_ARG) {
_switches[LENGTH_ARG]++;
_string_args[LENGTH_ARG] = optarg;
}
2003-04-04 17:22:58 +04:00
if (c == 'j' || ind == MAJOR_ARG) {
_switches[MAJOR_ARG]++;
_int_args[MAJOR_ARG] = atoi(optarg);
2003-04-04 17:22:58 +04:00
}
2003-01-22 00:25:51 +03:00
if (c == 'm' || ind == MINOR_ARG) {
_switches[MINOR_ARG]++;
_int_args[MINOR_ARG] = atoi(optarg);
}
if (ind == NOSUFFIX_ARG)
_switches[NOSUFFIX_ARG]++;
if (c == 'n' || ind == NOTABLE_ARG)
_switches[NOTABLE_ARG]++;
2005-03-27 15:37:46 +04:00
if (c == 'o' || ind == OPTIONS_ARG) {
_switches[OPTIONS_ARG]++;
_string_args[OPTIONS_ARG] = optarg;
}
if (ind == PROGRAM_ID_ARG) {
_switches[PROGRAM_ID_ARG]++;
_string_args[PROGRAM_ID_ARG] = optarg;
}
if (ind == PRECISE_ARG)
_switches[PRECISE_ARG]++;
if (ind == RAW_ARG)
_switches[RAW_ARG]++;
if (ind == REGION_ID_ARG) {
_switches[REGION_ID_ARG]++;
_int_args[REGION_ID_ARG] = atoi(optarg);
}
if (ind == SEPARATOR_ARG) {
_switches[SEPARATOR_ARG]++;
_string_args[SEPARATOR_ARG] = optarg;
}
if (ind == UNITS_ARG) {
_switches[UNITS_ARG]++;
_string_args[UNITS_ARG] = optarg;
}
if (c == 'O' || ind == SORT_ARG) {
_switches[SORT_ARG]++;
_string_args[SORT_ARG] = optarg;
2005-03-27 15:37:46 +04:00
}
if (c == 'S' || ind == SELECT_ARG) {
_switches[SELECT_ARG]++;
_string_args[SELECT_ARG] = optarg;
}
if (ind == START_ARG) {
_switches[START_ARG]++;
_string_args[START_ARG] = optarg;
}
2003-01-22 00:25:51 +03:00
if (c == 'v' || ind == VERBOSE_ARG)
_switches[VERBOSE_ARG]++;
if (c == 'u' || ind == UUID_ARG) {
_switches[UUID_ARG]++;
_uuid = optarg;
}
if (c == 'y' || ind == YES_ARG)
_switches[YES_ARG]++;
if (ind == ADD_NODE_ON_RESUME_ARG)
_switches[ADD_NODE_ON_RESUME_ARG]++;
if (ind == ADD_NODE_ON_CREATE_ARG)
_switches[ADD_NODE_ON_CREATE_ARG]++;
if (ind == CHECKS_ARG)
_switches[CHECKS_ARG]++;
if (ind == COUNT_ARG) {
_switches[COUNT_ARG]++;
_int_args[COUNT_ARG] = atoi(optarg);
if (_int_args[COUNT_ARG] < 0) {
log_error("Count must be zero or greater.");
return 0;
}
}
if (ind == UDEVCOOKIE_ARG) {
_switches[UDEVCOOKIE_ARG]++;
_udev_cookie = _get_cookie_value(optarg);
}
if (ind == NOUDEVRULES_ARG)
_switches[NOUDEVRULES_ARG]++;
if (ind == NOUDEVSYNC_ARG)
_switches[NOUDEVSYNC_ARG]++;
if (ind == VERIFYUDEV_ARG)
_switches[VERIFYUDEV_ARG]++;
if (c == 'G' || ind == GID_ARG) {
_switches[GID_ARG]++;
_int_args[GID_ARG] = atoi(optarg);
}
if (c == 'U' || ind == UID_ARG) {
_switches[UID_ARG]++;
_int_args[UID_ARG] = atoi(optarg);
}
if (c == 'M' || ind == MODE_ARG) {
_switches[MODE_ARG]++;
/* FIXME Accept modes as per chmod */
_int_args[MODE_ARG] = (int) strtol(optarg, NULL, 8);
}
if (ind == DEFERRED_ARG)
_switches[DEFERRED_ARG]++;
if (ind == EXEC_ARG) {
2005-05-17 00:46:46 +04:00
_switches[EXEC_ARG]++;
_command_to_exec = optarg;
2005-05-17 00:46:46 +04:00
}
if (ind == TARGET_ARG) {
_switches[TARGET_ARG]++;
_target = optarg;
}
if (ind == SEGMENTS_ARG)
_switches[SEGMENTS_ARG]++;
if (ind == INACTIVE_ARG)
_switches[INACTIVE_ARG]++;
if (ind == INTERVAL_ARG) {
_switches[INTERVAL_ARG]++;
_int_args[INTERVAL_ARG] = atoi(optarg);
if (_int_args[INTERVAL_ARG] <= 0) {
log_error("Interval must be a positive integer.");
return 0;
}
}
2013-02-04 23:31:56 +04:00
if (ind == MANGLENAME_ARG) {
_switches[MANGLENAME_ARG]++;
if (!strcasecmp(optarg, "none"))
_int_args[MANGLENAME_ARG] = DM_STRING_MANGLING_NONE;
else if (!strcasecmp(optarg, "auto"))
_int_args[MANGLENAME_ARG] = DM_STRING_MANGLING_AUTO;
else if (!strcasecmp(optarg, "hex"))
_int_args[MANGLENAME_ARG] = DM_STRING_MANGLING_HEX;
else {
log_error("Unknown name mangling mode");
return 0;
}
dm_set_name_mangling_mode((dm_string_mangling_t) _int_args[MANGLENAME_ARG]);
}
if (ind == NAMEPREFIXES_ARG)
2008-06-06 22:53:08 +04:00
_switches[NAMEPREFIXES_ARG]++;
if (ind == NOFLUSH_ARG)
_switches[NOFLUSH_ARG]++;
if (ind == NOHEADINGS_ARG)
_switches[NOHEADINGS_ARG]++;
if (ind == NOLOCKFS_ARG)
_switches[NOLOCKFS_ARG]++;
if (ind == NOOPENCOUNT_ARG)
_switches[NOOPENCOUNT_ARG]++;
if (ind == READAHEAD_ARG) {
_switches[READAHEAD_ARG]++;
if (!strcasecmp(optarg, "auto"))
_int_args[READAHEAD_ARG] = DM_READ_AHEAD_AUTO;
else if (!strcasecmp(optarg, "none"))
_int_args[READAHEAD_ARG] = DM_READ_AHEAD_NONE;
else {
for (s = optarg; isspace(*s); s++)
;
if (*s == '+')
_read_ahead_flags = DM_READ_AHEAD_MINIMUM_FLAG;
_int_args[READAHEAD_ARG] = atoi(optarg);
if (_int_args[READAHEAD_ARG] < -1) {
log_error("Negative read ahead value "
"(%d) is not understood.",
_int_args[READAHEAD_ARG]);
return 0;
}
}
}
if (ind == RETRY_ARG)
_switches[RETRY_ARG]++;
if (ind == ROWS_ARG)
2008-06-25 02:53:48 +04:00
_switches[ROWS_ARG]++;
if (ind == SETUUID_ARG)
_switches[SETUUID_ARG]++;
if (ind == SHOWKEYS_ARG)
_switches[SHOWKEYS_ARG]++;
if (ind == TABLE_ARG) {
_switches[TABLE_ARG]++;
2012-02-15 18:27:53 +04:00
if (!(_table = dm_strdup(optarg))) {
log_error("Could not allocate memory for table string.");
return 0;
}
}
if (ind == TREE_ARG)
_switches[TREE_ARG]++;
if (ind == UNQUOTED_ARG)
2008-06-25 00:16:47 +04:00
_switches[UNQUOTED_ARG]++;
if (ind == VERSION_ARG)
_switches[VERSION_ARG]++;
}
if (_switches[VERBOSE_ARG] > 1) {
dm_log_init_verbose(_switches[VERBOSE_ARG] - 1);
if (_switches[VERBOSE_ARG] > 2) {
if (!(_initial_timestamp = dm_timestamp_alloc()))
stack;
else if (!dm_timestamp_get(_initial_timestamp))
stack;
else
log_debug("Timestamp: 0.000000000 seconds");
}
}
2003-01-22 00:25:51 +03:00
if ((_switches[MAJOR_ARG] && !_switches[MINOR_ARG]) ||
(!_switches[MAJOR_ARG] && _switches[MINOR_ARG])) {
fprintf(stderr, "Please specify both major number and "
"minor number.\n");
return 0;
}
if (_switches[TABLE_ARG] && _switches[NOTABLE_ARG]) {
fprintf(stderr, "--table and --notable are incompatible.\n");
return 0;
}
if (_switches[ADD_NODE_ON_RESUME_ARG] && _switches[ADD_NODE_ON_CREATE_ARG]) {
fprintf(stderr, "--addnodeonresume and --addnodeoncreate are incompatible.\n");
return 0;
}
2015-08-01 00:53:38 +03:00
*argvp += optind;
*argcp -= optind;
if (!*argcp)
_command = NULL;
else if (!strcmp((*argvp)[0], "stats")) {
_base_command = DMSETUP_STATS_CMD;
_base_command_type = STATS_TYPE;
_command = "stats";
(*argvp)++;
(*argcp)--;
} else if (_base_command == DMSTATS_CMD) {
_command = "stats";
} else if (*argcp) {
_command = (*argvp)[0];
(*argvp)++;
(*argcp)--;
}
return 1;
}
static int _perform_command_for_all_repeatable_args(CMD_ARGS)
{
do {
if (!cmd->fn(cmd, subcommand, argc, argv++, NULL, multiple_devices)) {
fprintf(stderr, "Command failed\n");
return 0;
}
} while (cmd->repeatable_cmd && argc-- > 1);
return 1;
}
static int _do_report_wait(void)
{
return _do_timer_wait();
}
int main(int argc, char **argv)
{
2015-08-14 02:09:40 +03:00
int ret = 1, r;
const char *dev_dir;
const struct command *cmd;
const char *subcommand = NULL;
int multiple_devices;
(void) setlocale(LC_ALL, "");
dev_dir = getenv (DM_DEV_DIR_ENV_VAR_NAME);
if (dev_dir && *dev_dir) {
if (!dm_set_dev_dir(dev_dir)) {
2007-12-05 20:05:04 +03:00
fprintf(stderr, "Invalid DM_DEV_DIR environment variable value.\n");
goto out;
}
2007-12-05 20:05:04 +03:00
} else
dev_dir = DEFAULT_DM_DEV_DIR;
if (!_process_switches(&argc, &argv, dev_dir)) {
2005-03-27 15:37:46 +04:00
fprintf(stderr, "Couldn't process command line.\n");
goto out;
}
if (_switches[HELP_ARG]) {
switch (_base_command_type) {
case STATS_TYPE:
if ((cmd = _find_stats_subcommand("help")))
goto doit;
goto unknown;
default:
if ((cmd = _find_dmsetup_command("help")))
goto doit;
goto unknown;
}
}
if (_switches[VERSION_ARG]) {
switch (_base_command_type) {
case STATS_TYPE:
if ((cmd = _find_stats_subcommand("version")))
goto doit;
goto unknown;
default:
if ((cmd = _find_dmsetup_command("version")))
goto doit;
goto unknown;
}
}
if (!_command) {
_usage(stderr);
goto out;
}
if (!(cmd = _find_dmsetup_command(_command))) {
unknown:
fprintf(stderr, "Unknown command\n");
_usage(stderr);
goto out;
}
if (argc < cmd->min_args ||
(cmd->max_args >= 0 && argc > cmd->max_args)) {
fprintf(stderr, "Incorrect number of arguments\n");
_usage(stderr);
goto out;
}
if (!_switches[COLS_ARG] && !strcmp(cmd->name, "splitname"))
2009-06-04 00:44:42 +04:00
_switches[COLS_ARG]++;
if (!strcmp(cmd->name, "stats")) {
_switches[COLS_ARG]++;
if (!_switches[UNITS_ARG]) {
_switches[UNITS_ARG]++;
_string_args[UNITS_ARG] = (char *) "h";
}
}
if (!strcmp(cmd->name, "mangle"))
dm_set_name_mangling_mode(DM_STRING_MANGLING_NONE);
if (!_process_options(_string_args[OPTIONS_ARG])) {
fprintf(stderr, "Couldn't process command line.\n");
goto out;
}
#ifdef UDEV_SYNC_SUPPORT
if (!_set_up_udev_support(dev_dir))
goto out;
#endif
/*
* Extract subcommand?
* dmsetup <command> <subcommand> [args...]
*/
if (cmd->has_subcommands) {
subcommand = argv[0];
argc--, argv++;
} else
subcommand = (char *) "";
if (_switches[COLS_ARG] && !_report_init(cmd, subcommand))
goto out;
if (_switches[COUNT_ARG])
_count = ((uint32_t)_int_args[COUNT_ARG]) ? : UINT32_MAX;
else if (_switches[INTERVAL_ARG])
_count = UINT32_MAX;
if (_switches[UNITS_ARG]) {
_disp_factor = _factor_from_units(_string_args[UNITS_ARG],
&_disp_units);
if (!_disp_factor) {
log_error("Invalid --units argument.");
goto out;
}
}
/* Start interval timer. */
if (_count > 1)
if (!_start_timer())
goto_out;
doit:
multiple_devices = (cmd->repeatable_cmd && argc != 1 &&
(argc || (!_switches[UUID_ARG] && !_switches[MAJOR_ARG])));
do {
r = _perform_command_for_all_repeatable_args(cmd, subcommand, argc, argv, NULL, multiple_devices);
if (_report) {
/* only output headings for repeating reports */
if (_int_args[COUNT_ARG] != 1 && !dm_report_is_empty(_report))
dm_report_column_headings(_report);
dm_report_output(_report);
if (_count > 1 && r) {
printf("\n");
/* wait for --interval and update timestamps */
if (!_do_report_wait())
goto_out;
}
}
if (!r)
2015-08-14 02:09:40 +03:00
goto_out;
} while (--_count);
2015-08-14 02:09:40 +03:00
/* Success */
ret = 0;
out:
if (_report)
dm_report_free(_report);
if (_dtree)
dm_tree_free(_dtree);
dm_free(_table);
if (_initial_timestamp)
dm_timestamp_destroy(_initial_timestamp);
2015-08-14 02:09:40 +03:00
return ret;
}