mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-09 18:03:17 +03:00
9dfe5ce2bc
Another enum used as bitfield mask.
4645 lines
143 KiB
C
4645 lines
143 KiB
C
/*
|
|
* Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
|
|
* Copyright (C) 2004-2019 Red Hat, Inc. All rights reserved.
|
|
*
|
|
* This file is part of LVM2.
|
|
*
|
|
* 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 Lesser General Public License v.2.1.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "lib/misc/lib.h"
|
|
#include "lib/metadata/metadata.h"
|
|
#include "lib/report/report.h"
|
|
#include "lib/commands/toolcontext.h"
|
|
#include "lib/misc/lvm-string.h"
|
|
#include "lib/display/display.h"
|
|
#include "lib/activate/activate.h"
|
|
#include "lib/metadata/segtype.h"
|
|
#include "lib/cache/lvmcache.h"
|
|
#include "lib/device/device-types.h"
|
|
#include "lib/device/device_id.h"
|
|
#include "lib/datastruct/str_list.h"
|
|
#include "lib/locking/lvmlockd.h"
|
|
|
|
#include <stddef.h> /* offsetof() */
|
|
#include <float.h> /* DBL_MAX */
|
|
#include <time.h>
|
|
|
|
/*
|
|
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
* IMPORTANT NOTE ABOUT ADDING A NEW VALUE FOR REPORTING
|
|
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
*
|
|
* When adding a new string value to report, try to keep it
|
|
* self-descriptive so when it's printed even without the header,
|
|
* we can still deduce what it is actually reporting.
|
|
*
|
|
* If you need more than one descriptive string to mean the same value,
|
|
* please define them as reserved values in values.h.
|
|
*
|
|
* The first reserved value is the one that is printed in reports (unless
|
|
* it's a binary value and we have report/binary_values_as_numeric=1 config
|
|
* option used OR --binary command line option is used OR we're using an
|
|
* output format which must always print binary values in numeric way,
|
|
* like json_std output format.
|
|
*
|
|
* All the other (2nd and further) listed reserved names are synonyms which
|
|
* may be also used in selection (-S|--select).
|
|
*
|
|
* Also, always use proper *_disp functions to display each type of value
|
|
* properly. For example, in case of binary values, you should use
|
|
* _binary_disp so that we can always switch between numerical (0/1/-1) and
|
|
* string representation while reporting the value.
|
|
*/
|
|
|
|
struct lvm_report_object {
|
|
struct volume_group *vg;
|
|
struct lv_with_info_and_seg_status *lvdm;
|
|
struct physical_volume *pv;
|
|
struct lv_segment *seg;
|
|
struct pv_segment *pvseg;
|
|
struct label *label;
|
|
};
|
|
|
|
static uint32_t _log_seqnum = 1;
|
|
|
|
/*
|
|
* Enum for field_num index to use in per-field reserved value definition.
|
|
* Each field is represented by enum value with name "field_<id>" where <id>
|
|
* is the field_id of the field as registered in columns.h.
|
|
*/
|
|
#define FIELD(type, strct, sorttype, head, field_name, width, func, id, desc, writeable) field_ ## id,
|
|
enum {
|
|
/* coverity[unnecessary_header] */
|
|
#include "columns.h"
|
|
};
|
|
#undef FIELD
|
|
|
|
static const uint64_t _zero64 = UINT64_C(0);
|
|
static const uint64_t _one64 = UINT64_C(1);
|
|
static const uint64_t _two64 = UINT64_C(2);
|
|
static const char _str_zero[] = "0";
|
|
static const char _str_one[] = "1";
|
|
static const char _str_no[] = "no";
|
|
static const char _str_yes[] = "yes";
|
|
static const char _str_unknown[] = "unknown";
|
|
static const double _siz_max = DBL_MAX;
|
|
|
|
/*
|
|
* 32 bit signed is casted to 64 bit unsigned in dm_report_field internally!
|
|
* So when stored in the struct, the _reserved_num_undef_32 is actually
|
|
* equal to _reserved_num_undef_64.
|
|
*/
|
|
static const int32_t _reserved_num_undef_32 = INT32_C(-1);
|
|
|
|
typedef enum {
|
|
/* top-level identification */
|
|
TIME_NULL,
|
|
TIME_NUM,
|
|
TIME_STR,
|
|
|
|
/* direct numeric value */
|
|
TIME_NUM__START,
|
|
TIME_NUM_MULTIPLIER,
|
|
TIME_NUM_MULTIPLIER_NEGATIVE,
|
|
TIME_NUM_DAY,
|
|
TIME_NUM_YEAR,
|
|
TIME_NUM__END,
|
|
|
|
/* direct string value */
|
|
TIME_STR_TIMEZONE,
|
|
|
|
/* time frame strings */
|
|
TIME_FRAME__START,
|
|
TIME_FRAME_AGO,
|
|
TIME_FRAME__END,
|
|
|
|
/* labels for dates */
|
|
TIME_LABEL_DATE__START,
|
|
|
|
TIME_LABEL_DATE_TODAY,
|
|
TIME_LABEL_DATE_YESTERDAY,
|
|
|
|
/* weekday name strings */
|
|
TIME_WEEKDAY__START,
|
|
TIME_WEEKDAY_SUNDAY,
|
|
TIME_WEEKDAY_MONDAY,
|
|
TIME_WEEKDAY_TUESDAY,
|
|
TIME_WEEKDAY_WEDNESDAY,
|
|
TIME_WEEKDAY_THURSDAY,
|
|
TIME_WEEKDAY_FRIDAY,
|
|
TIME_WEEKDAY_SATURDAY,
|
|
TIME_WEEKDAY__END,
|
|
|
|
TIME_LABEL_DATE__END,
|
|
|
|
/* labels for times */
|
|
TIME_LABEL_TIME__START,
|
|
TIME_LABEL_TIME_NOON,
|
|
TIME_LABEL_TIME_MIDNIGHT,
|
|
TIME_LABEL_TIME__END,
|
|
|
|
/* time unit strings */
|
|
TIME_UNIT__START,
|
|
TIME_UNIT_SECOND,
|
|
TIME_UNIT_SECOND_REL,
|
|
TIME_UNIT_MINUTE,
|
|
TIME_UNIT_MINUTE_REL,
|
|
TIME_UNIT_HOUR,
|
|
TIME_UNIT_HOUR_REL,
|
|
TIME_UNIT_AM,
|
|
TIME_UNIT_PM,
|
|
TIME_UNIT_DAY,
|
|
TIME_UNIT_WEEK,
|
|
TIME_UNIT_MONTH,
|
|
TIME_UNIT_YEAR,
|
|
TIME_UNIT_TZ_MINUTE,
|
|
TIME_UNIT_TZ_HOUR,
|
|
TIME_UNIT__END,
|
|
|
|
/* month name strings */
|
|
TIME_MONTH__START,
|
|
TIME_MONTH_JANUARY,
|
|
TIME_MONTH_FEBRUARY,
|
|
TIME_MONTH_MARCH,
|
|
TIME_MONTH_APRIL,
|
|
TIME_MONTH_MAY,
|
|
TIME_MONTH_JUNE,
|
|
TIME_MONTH_JULY,
|
|
TIME_MONTH_AUGUST,
|
|
TIME_MONTH_SEPTEMBER,
|
|
TIME_MONTH_OCTOBER,
|
|
TIME_MONTH_NOVEMBER,
|
|
TIME_MONTH_DECEMBER,
|
|
TIME_MONTH__END,
|
|
} time_id_t;
|
|
|
|
#define TIME_PROP_DATE 0x00000001 /* date-related */
|
|
#define TIME_PROP_TIME 0x00000002 /* time-related */
|
|
#define TIME_PROP_ABS 0x00000004 /* absolute value */
|
|
#define TIME_PROP_REL 0x00000008 /* relative value */
|
|
|
|
struct time_prop {
|
|
time_id_t id;
|
|
uint32_t prop_flags;
|
|
time_id_t granularity;
|
|
};
|
|
|
|
#define ADD_TIME_PROP(id, flags, granularity) [(id)] = {(id), (flags), (granularity)},
|
|
|
|
static const struct time_prop _time_props[] = {
|
|
ADD_TIME_PROP(TIME_NULL, 0, TIME_NULL)
|
|
ADD_TIME_PROP(TIME_NUM, 0, TIME_NULL)
|
|
ADD_TIME_PROP(TIME_STR, 0, TIME_NULL)
|
|
|
|
ADD_TIME_PROP(TIME_NUM_MULTIPLIER, 0, TIME_NULL)
|
|
ADD_TIME_PROP(TIME_NUM_MULTIPLIER_NEGATIVE, 0, TIME_NULL)
|
|
ADD_TIME_PROP(TIME_NUM_DAY, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_DAY)
|
|
ADD_TIME_PROP(TIME_NUM_YEAR, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_YEAR)
|
|
|
|
ADD_TIME_PROP(TIME_STR_TIMEZONE, TIME_PROP_TIME | TIME_PROP_ABS, TIME_NULL)
|
|
|
|
ADD_TIME_PROP(TIME_FRAME_AGO, TIME_PROP_DATE | TIME_PROP_TIME | TIME_PROP_REL, TIME_NULL)
|
|
|
|
ADD_TIME_PROP(TIME_LABEL_DATE_TODAY, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_DAY)
|
|
ADD_TIME_PROP(TIME_LABEL_DATE_YESTERDAY, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_DAY)
|
|
ADD_TIME_PROP(TIME_WEEKDAY_SUNDAY, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_DAY)
|
|
ADD_TIME_PROP(TIME_WEEKDAY_MONDAY, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_DAY)
|
|
ADD_TIME_PROP(TIME_WEEKDAY_TUESDAY, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_DAY)
|
|
ADD_TIME_PROP(TIME_WEEKDAY_WEDNESDAY, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_DAY)
|
|
ADD_TIME_PROP(TIME_WEEKDAY_THURSDAY, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_DAY)
|
|
ADD_TIME_PROP(TIME_WEEKDAY_FRIDAY, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_DAY)
|
|
ADD_TIME_PROP(TIME_WEEKDAY_SATURDAY, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_DAY)
|
|
|
|
ADD_TIME_PROP(TIME_LABEL_TIME_NOON, TIME_PROP_TIME | TIME_PROP_ABS, TIME_UNIT_SECOND)
|
|
ADD_TIME_PROP(TIME_LABEL_TIME_MIDNIGHT, TIME_PROP_TIME | TIME_PROP_ABS, TIME_UNIT_SECOND)
|
|
|
|
ADD_TIME_PROP(TIME_UNIT_SECOND, TIME_PROP_TIME | TIME_PROP_ABS, TIME_UNIT_SECOND)
|
|
ADD_TIME_PROP(TIME_UNIT_SECOND_REL, TIME_PROP_TIME | TIME_PROP_REL, TIME_UNIT_SECOND)
|
|
ADD_TIME_PROP(TIME_UNIT_MINUTE, TIME_PROP_TIME | TIME_PROP_ABS, TIME_UNIT_MINUTE)
|
|
ADD_TIME_PROP(TIME_UNIT_MINUTE_REL, TIME_PROP_TIME | TIME_PROP_REL, TIME_UNIT_MINUTE)
|
|
ADD_TIME_PROP(TIME_UNIT_HOUR, TIME_PROP_TIME | TIME_PROP_ABS, TIME_UNIT_HOUR)
|
|
ADD_TIME_PROP(TIME_UNIT_HOUR_REL, TIME_PROP_TIME | TIME_PROP_REL, TIME_UNIT_HOUR)
|
|
ADD_TIME_PROP(TIME_UNIT_AM, TIME_PROP_TIME | TIME_PROP_ABS, TIME_UNIT_HOUR)
|
|
ADD_TIME_PROP(TIME_UNIT_PM, TIME_PROP_TIME | TIME_PROP_ABS, TIME_UNIT_HOUR)
|
|
ADD_TIME_PROP(TIME_UNIT_DAY, TIME_PROP_DATE | TIME_PROP_REL, TIME_UNIT_DAY)
|
|
ADD_TIME_PROP(TIME_UNIT_WEEK, TIME_PROP_DATE | TIME_PROP_REL, TIME_UNIT_WEEK)
|
|
ADD_TIME_PROP(TIME_UNIT_MONTH, TIME_PROP_DATE | TIME_PROP_REL, TIME_UNIT_MONTH)
|
|
ADD_TIME_PROP(TIME_UNIT_YEAR, TIME_PROP_DATE | TIME_PROP_REL, TIME_UNIT_YEAR)
|
|
ADD_TIME_PROP(TIME_UNIT_TZ_MINUTE, TIME_PROP_TIME | TIME_PROP_ABS, TIME_NULL)
|
|
ADD_TIME_PROP(TIME_UNIT_TZ_HOUR, TIME_PROP_TIME | TIME_PROP_ABS, TIME_NULL)
|
|
|
|
ADD_TIME_PROP(TIME_MONTH_JANUARY, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_MONTH)
|
|
ADD_TIME_PROP(TIME_MONTH_FEBRUARY, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_MONTH)
|
|
ADD_TIME_PROP(TIME_MONTH_MARCH, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_MONTH)
|
|
ADD_TIME_PROP(TIME_MONTH_APRIL, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_MONTH)
|
|
ADD_TIME_PROP(TIME_MONTH_MAY, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_MONTH)
|
|
ADD_TIME_PROP(TIME_MONTH_JUNE, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_MONTH)
|
|
ADD_TIME_PROP(TIME_MONTH_JULY, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_MONTH)
|
|
ADD_TIME_PROP(TIME_MONTH_AUGUST, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_MONTH)
|
|
ADD_TIME_PROP(TIME_MONTH_SEPTEMBER, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_MONTH)
|
|
ADD_TIME_PROP(TIME_MONTH_OCTOBER, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_MONTH)
|
|
ADD_TIME_PROP(TIME_MONTH_NOVEMBER, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_MONTH)
|
|
ADD_TIME_PROP(TIME_MONTH_DECEMBER, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_MONTH)
|
|
};
|
|
|
|
#define TIME_REG_PLURAL_S 0x00000001 /* also recognize plural form with "s" suffix */
|
|
|
|
struct time_reg {
|
|
const char name[16];
|
|
const struct time_prop *prop;
|
|
uint32_t reg_flags;
|
|
};
|
|
|
|
#define TIME_PROP(id) (_time_props + (id))
|
|
|
|
static const struct time_reg _time_reg[] = {
|
|
/*
|
|
* Group of tokens representing time frame and used
|
|
* with relative date/time to specify different flavours
|
|
* of relativity.
|
|
*/
|
|
{"ago", TIME_PROP(TIME_FRAME_AGO), 0},
|
|
|
|
/*
|
|
* Group of tokens labeling some date and used
|
|
* instead of direct absolute specification.
|
|
*/
|
|
{"today", TIME_PROP(TIME_LABEL_DATE_TODAY), 0}, /* 0:00 - 23:59:59 for current date */
|
|
{"yesterday", TIME_PROP(TIME_LABEL_DATE_YESTERDAY), 0}, /* 0:00 - 23:59:59 for current date minus 1 day*/
|
|
|
|
/*
|
|
* Group of tokens labeling some date - weekday
|
|
* names used to build up date.
|
|
*/
|
|
{"Sunday", TIME_PROP(TIME_WEEKDAY_SUNDAY), TIME_REG_PLURAL_S},
|
|
{"Sun", TIME_PROP(TIME_WEEKDAY_SUNDAY), 0},
|
|
{"Monday", TIME_PROP(TIME_WEEKDAY_MONDAY), TIME_REG_PLURAL_S},
|
|
{"Mon", TIME_PROP(TIME_WEEKDAY_MONDAY), 0},
|
|
{"Tuesday", TIME_PROP(TIME_WEEKDAY_TUESDAY), TIME_REG_PLURAL_S},
|
|
{"Tue", TIME_PROP(TIME_WEEKDAY_TUESDAY), 0},
|
|
{"Wednesday", TIME_PROP(TIME_WEEKDAY_WEDNESDAY), TIME_REG_PLURAL_S},
|
|
{"Wed", TIME_PROP(TIME_WEEKDAY_WEDNESDAY), 0},
|
|
{"Thursday", TIME_PROP(TIME_WEEKDAY_THURSDAY), TIME_REG_PLURAL_S},
|
|
{"Thu", TIME_PROP(TIME_WEEKDAY_THURSDAY), 0},
|
|
{"Friday", TIME_PROP(TIME_WEEKDAY_FRIDAY), TIME_REG_PLURAL_S},
|
|
{"Fri", TIME_PROP(TIME_WEEKDAY_FRIDAY), 0},
|
|
{"Saturday", TIME_PROP(TIME_WEEKDAY_SATURDAY), TIME_REG_PLURAL_S},
|
|
{"Sat", TIME_PROP(TIME_WEEKDAY_SATURDAY), 0},
|
|
|
|
/*
|
|
* Group of tokens labeling some time and used
|
|
* instead of direct absolute specification.
|
|
*/
|
|
{"noon", TIME_PROP(TIME_LABEL_TIME_NOON), TIME_REG_PLURAL_S}, /* 12:00:00 */
|
|
{"midnight", TIME_PROP(TIME_LABEL_TIME_MIDNIGHT), TIME_REG_PLURAL_S}, /* 00:00:00 */
|
|
|
|
/*
|
|
* Group of tokens used to build up time. Most of these
|
|
* are used either as relative or absolute time units.
|
|
* The absolute ones are always used with TIME_FRAME_*
|
|
* token, otherwise the unit is relative.
|
|
*/
|
|
{"second", TIME_PROP(TIME_UNIT_SECOND), TIME_REG_PLURAL_S},
|
|
{"sec", TIME_PROP(TIME_UNIT_SECOND), TIME_REG_PLURAL_S},
|
|
{"s", TIME_PROP(TIME_UNIT_SECOND), 0},
|
|
{"minute", TIME_PROP(TIME_UNIT_MINUTE), TIME_REG_PLURAL_S},
|
|
{"min", TIME_PROP(TIME_UNIT_MINUTE), TIME_REG_PLURAL_S},
|
|
{"m", TIME_PROP(TIME_UNIT_MINUTE), 0},
|
|
{"hour", TIME_PROP(TIME_UNIT_HOUR), TIME_REG_PLURAL_S},
|
|
{"hr", TIME_PROP(TIME_UNIT_HOUR), TIME_REG_PLURAL_S},
|
|
{"h", TIME_PROP(TIME_UNIT_HOUR), 0},
|
|
{"AM", TIME_PROP(TIME_UNIT_AM), 0},
|
|
{"PM", TIME_PROP(TIME_UNIT_PM), 0},
|
|
|
|
/*
|
|
* Group of tokens used to build up date.
|
|
* These are all relative ones.
|
|
*/
|
|
{"day", TIME_PROP(TIME_UNIT_DAY), TIME_REG_PLURAL_S},
|
|
{"week", TIME_PROP(TIME_UNIT_WEEK), TIME_REG_PLURAL_S},
|
|
{"month", TIME_PROP(TIME_UNIT_MONTH), TIME_REG_PLURAL_S},
|
|
{"year", TIME_PROP(TIME_UNIT_YEAR), TIME_REG_PLURAL_S},
|
|
{"yr", TIME_PROP(TIME_UNIT_YEAR), TIME_REG_PLURAL_S},
|
|
|
|
/*
|
|
* Group of tokes used to build up date.
|
|
* These are all absolute.
|
|
*/
|
|
{"January", TIME_PROP(TIME_MONTH_JANUARY), 0},
|
|
{"Jan", TIME_PROP(TIME_MONTH_JANUARY), 0},
|
|
{"February", TIME_PROP(TIME_MONTH_FEBRUARY), 0},
|
|
{"Feb", TIME_PROP(TIME_MONTH_FEBRUARY), 0},
|
|
{"March", TIME_PROP(TIME_MONTH_MARCH), 0},
|
|
{"Mar", TIME_PROP(TIME_MONTH_MARCH), 0},
|
|
{"April", TIME_PROP(TIME_MONTH_APRIL), 0},
|
|
{"Apr", TIME_PROP(TIME_MONTH_APRIL), 0},
|
|
{"May", TIME_PROP(TIME_MONTH_MAY), 0},
|
|
{"June", TIME_PROP(TIME_MONTH_JUNE), 0},
|
|
{"Jun", TIME_PROP(TIME_MONTH_JUNE), 0},
|
|
{"July", TIME_PROP(TIME_MONTH_JULY), 0},
|
|
{"Jul", TIME_PROP(TIME_MONTH_JULY), 0},
|
|
{"August", TIME_PROP(TIME_MONTH_AUGUST), 0},
|
|
{"Aug", TIME_PROP(TIME_MONTH_AUGUST), 0},
|
|
{"September", TIME_PROP(TIME_MONTH_SEPTEMBER), 0},
|
|
{"Sep", TIME_PROP(TIME_MONTH_SEPTEMBER), 0},
|
|
{"October", TIME_PROP(TIME_MONTH_OCTOBER), 0},
|
|
{"Oct", TIME_PROP(TIME_MONTH_OCTOBER), 0},
|
|
{"November", TIME_PROP(TIME_MONTH_NOVEMBER), 0},
|
|
{"Nov", TIME_PROP(TIME_MONTH_NOVEMBER), 0},
|
|
{"December", TIME_PROP(TIME_MONTH_DECEMBER), 0},
|
|
{"Dec", TIME_PROP(TIME_MONTH_DECEMBER), 0},
|
|
};
|
|
|
|
struct time_item {
|
|
struct dm_list list;
|
|
const struct time_prop *prop;
|
|
const char *s;
|
|
size_t len;
|
|
};
|
|
|
|
struct time_info {
|
|
struct dm_pool *mem;
|
|
struct dm_list *ti_list;
|
|
time_t *now;
|
|
time_id_t min_abs_date_granularity;
|
|
time_id_t max_abs_date_granularity;
|
|
time_id_t min_abs_time_granularity;
|
|
time_id_t min_rel_time_granularity;
|
|
};
|
|
|
|
static int _is_time_num(time_id_t id)
|
|
{
|
|
return ((id > TIME_NUM__START) && (id < TIME_NUM__END));
|
|
}
|
|
|
|
/*
|
|
static int _is_time_frame(time_id_t id)
|
|
{
|
|
return ((id > TIME_FRAME__START) && (id < TIME_FRAME__END));
|
|
}
|
|
*/
|
|
|
|
static int _is_time_label_date(time_id_t id)
|
|
{
|
|
return ((id > TIME_LABEL_DATE__START) && (id < TIME_LABEL_DATE__END));
|
|
}
|
|
|
|
static int _is_time_label_time(time_id_t id)
|
|
{
|
|
return ((id > TIME_LABEL_TIME__START) && (id < TIME_LABEL_TIME__END));
|
|
}
|
|
|
|
static int _is_time_unit(time_id_t id)
|
|
{
|
|
return ((id > TIME_UNIT__START) && (id < TIME_UNIT__END));
|
|
}
|
|
|
|
static int _is_time_weekday(time_id_t id)
|
|
{
|
|
return ((id > TIME_WEEKDAY__START) && (id < TIME_WEEKDAY__END));
|
|
}
|
|
|
|
static int _is_time_month(time_id_t id)
|
|
{
|
|
return ((id > TIME_MONTH__START) && (id < TIME_MONTH__END));
|
|
}
|
|
|
|
static const char *_skip_space(const char *s)
|
|
{
|
|
while (*s && isspace(*s))
|
|
s++;
|
|
return s;
|
|
}
|
|
|
|
/* Move till delim or space */
|
|
static const char *_move_till_item_end(const char *s)
|
|
{
|
|
char c = *s;
|
|
int is_num = isdigit(c);
|
|
|
|
/*
|
|
* Allow numbers to be attached to next token, for example
|
|
* it's correct to write "12 hours" as well as "12hours".
|
|
*/
|
|
while (c && !isspace(c) && (is_num ? (is_num = isdigit(c)) : 1))
|
|
c = *++s;
|
|
|
|
return s;
|
|
}
|
|
|
|
static struct time_item *_alloc_time_item(struct dm_pool *mem, time_id_t id,
|
|
const char *s, size_t len)
|
|
{
|
|
struct time_item *ti;
|
|
|
|
if (!(ti = dm_pool_zalloc(mem, sizeof(struct time_item)))) {
|
|
log_error("alloc_time_item: dm_pool_zalloc failed");
|
|
return NULL;
|
|
}
|
|
|
|
ti->prop = &_time_props[id];
|
|
ti->s = s;
|
|
ti->len = len;
|
|
|
|
return ti;
|
|
}
|
|
|
|
static int _add_time_part_to_list(struct dm_pool *mem, struct dm_list *list,
|
|
time_id_t id, int minus, const char *s, size_t len)
|
|
{
|
|
struct time_item *ti1, *ti2;
|
|
|
|
if (!(ti1 = _alloc_time_item(mem, minus ? TIME_NUM_MULTIPLIER_NEGATIVE
|
|
: TIME_NUM_MULTIPLIER, s, len)) ||
|
|
!(ti2 = _alloc_time_item(mem, id, s + len, 0)))
|
|
return 0;
|
|
dm_list_add(list, &ti1->list);
|
|
dm_list_add(list, &ti2->list);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _get_time(struct dm_pool *mem, const char **str,
|
|
struct dm_list *list, int tz)
|
|
{
|
|
const char *end, *s = *str;
|
|
int r = 0;
|
|
|
|
/* hour */
|
|
end = _move_till_item_end(s);
|
|
if (!_add_time_part_to_list(mem, list, tz ? TIME_UNIT_TZ_HOUR : TIME_UNIT_HOUR,
|
|
tz == -1, s, end - s))
|
|
goto out;
|
|
|
|
/* minute */
|
|
if (*end != ':')
|
|
/* minute required */
|
|
goto out;
|
|
s = end + 1;
|
|
end = _move_till_item_end(s);
|
|
if (!_add_time_part_to_list(mem, list, tz ? TIME_UNIT_TZ_MINUTE : TIME_UNIT_MINUTE,
|
|
tz == -1, s, end - s))
|
|
goto out;
|
|
|
|
/* second */
|
|
if (*end != ':') {
|
|
/* second not required */
|
|
s = end + 1;
|
|
r = 1;
|
|
goto out;
|
|
} else if (tz)
|
|
/* timezone does not have seconds */
|
|
goto out;
|
|
|
|
s = end + 1;
|
|
end = _move_till_item_end(s);
|
|
if (!_add_time_part_to_list(mem, list, TIME_UNIT_SECOND, 0, s, end - s))
|
|
goto out;
|
|
|
|
s = end + 1;
|
|
r = 1;
|
|
out:
|
|
*str = s;
|
|
return r;
|
|
}
|
|
|
|
static int _preparse_fuzzy_time(const char *s, struct time_info *info)
|
|
{
|
|
struct dm_list *list;
|
|
struct time_item *ti;
|
|
const char *end;
|
|
int fuzzy = 0;
|
|
time_id_t id;
|
|
size_t len;
|
|
int r = 0;
|
|
char c;
|
|
|
|
if (!(list = dm_pool_alloc(info->mem, sizeof(struct dm_list)))) {
|
|
log_error("_preparse_fuzzy_time: dm_pool_alloc failed");
|
|
goto out;
|
|
}
|
|
dm_list_init(list);
|
|
s = _skip_space(s);
|
|
|
|
while ((c = *s)) {
|
|
/*
|
|
* If the string consists of -:+, digits or spaces,
|
|
* it's not worth looking for fuzzy names here -
|
|
* it's standard YYYY-MM-DD HH:MM:SS +-HH:MM format
|
|
* and that is parseable by libdm directly.
|
|
*/
|
|
if (!(isdigit(c) || (c == '-') || (c == ':') || (c == '+')))
|
|
fuzzy = 1;
|
|
|
|
end = _move_till_item_end(s);
|
|
|
|
if (isalpha(c))
|
|
id = TIME_STR;
|
|
else if (isdigit(c)) {
|
|
if (*end == ':') {
|
|
/* we have time */
|
|
if (!_get_time(info->mem, &s, list, 0))
|
|
goto out;
|
|
continue;
|
|
}
|
|
/* we have some other number */
|
|
id = TIME_NUM;
|
|
} else if ((c == '-') || (c == '+')) {
|
|
s++;
|
|
/* we have timezone */
|
|
if (!_get_time(info->mem, &s, list, (c == '-') ? -1 : 1))
|
|
goto out;
|
|
continue;
|
|
} else
|
|
goto out;
|
|
|
|
len = end - s;
|
|
if (!(ti = _alloc_time_item(info->mem, id, s, len)))
|
|
goto out;
|
|
dm_list_add(list, &ti->list);
|
|
s += len;
|
|
s = _skip_space(s);
|
|
}
|
|
|
|
info->ti_list = list;
|
|
r = 1;
|
|
out:
|
|
if (!(r && fuzzy)) {
|
|
dm_pool_free(info->mem, list);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _match_time_str(struct dm_list *ti_list, struct time_item *ti)
|
|
{
|
|
struct time_item *ti_context_p = (struct time_item *) dm_list_prev(ti_list, &ti->list);
|
|
size_t reg_len;
|
|
unsigned i;
|
|
|
|
ti->prop = TIME_PROP(TIME_NULL);
|
|
|
|
for (i = 0; i < DM_ARRAY_SIZE(_time_reg); ++i) {
|
|
reg_len = strlen(_time_reg[i].name);
|
|
if ((ti->len != reg_len) &&
|
|
!((_time_reg[i].reg_flags & TIME_REG_PLURAL_S) &&
|
|
(ti->len == reg_len+1) && (ti->s[reg_len] == 's')))
|
|
continue;
|
|
|
|
if (!strncasecmp(ti->s, _time_reg[i].name, reg_len)) {
|
|
ti->prop = _time_reg[i].prop;
|
|
if ((ti->prop->id > TIME_UNIT__START) && (ti->prop->id < TIME_UNIT__END) &&
|
|
ti_context_p && (ti_context_p->prop->id == TIME_NUM))
|
|
ti_context_p->prop = TIME_PROP(TIME_NUM_MULTIPLIER);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ti->prop->id;
|
|
}
|
|
|
|
static int _match_time_num(struct dm_list *ti_list, struct time_item *ti)
|
|
{
|
|
struct time_item *ti_context_p = (struct time_item *) dm_list_prev(ti_list, &ti->list);
|
|
struct time_item *ti_context_n = (struct time_item *) dm_list_next(ti_list, &ti->list);
|
|
struct time_item *ti_context_nn = ti_context_n ? (struct time_item *) dm_list_next(ti_list, &ti_context_n->list) : NULL;
|
|
|
|
if (ti_context_n &&
|
|
(ti_context_n->prop->id > TIME_MONTH__START) &&
|
|
(ti_context_n->prop->id < TIME_MONTH__END)) {
|
|
if (ti_context_nn && ti_context_nn->prop->id == TIME_NUM) {
|
|
if (ti->len < ti_context_nn->len) {
|
|
/* 24 Feb 2015 */
|
|
ti->prop = TIME_PROP(TIME_NUM_DAY);
|
|
ti_context_nn->prop = TIME_PROP(TIME_NUM_YEAR);
|
|
} else {
|
|
/* 2015 Feb 24 */
|
|
ti->prop = TIME_PROP(TIME_NUM_YEAR);
|
|
ti_context_nn->prop = TIME_PROP(TIME_NUM_DAY);
|
|
}
|
|
} else {
|
|
if (ti->len <= 2)
|
|
/* 24 Feb */
|
|
ti->prop = TIME_PROP(TIME_NUM_DAY);
|
|
else
|
|
/* 2015 Feb */
|
|
ti->prop = TIME_PROP(TIME_NUM_YEAR);
|
|
}
|
|
} else if (ti_context_p &&
|
|
(ti_context_p->prop->id > TIME_MONTH__START) &&
|
|
(ti_context_p->prop->id < TIME_MONTH__END)) {
|
|
if (ti->len <= 2)
|
|
/* Feb 24 */
|
|
ti->prop = TIME_PROP(TIME_NUM_DAY);
|
|
else
|
|
/* Feb 2015 */
|
|
ti->prop = TIME_PROP(TIME_NUM_YEAR);
|
|
} else
|
|
ti->prop = TIME_PROP(TIME_NUM_YEAR);
|
|
|
|
return ti->prop->id;
|
|
}
|
|
|
|
static void _detect_time_granularity(struct time_info *info, struct time_item *ti)
|
|
{
|
|
time_id_t gran = ti->prop->granularity;
|
|
int is_date, is_abs, is_rel;
|
|
|
|
if (gran == TIME_NULL)
|
|
return;
|
|
|
|
is_date = ti->prop->prop_flags & TIME_PROP_DATE;
|
|
is_abs = ti->prop->prop_flags & TIME_PROP_ABS;
|
|
is_rel = ti->prop->prop_flags & TIME_PROP_REL;
|
|
|
|
if (is_date && is_abs) {
|
|
if (gran > info->max_abs_date_granularity)
|
|
info->max_abs_date_granularity = gran;
|
|
if (gran < info->min_abs_date_granularity)
|
|
info->min_abs_date_granularity = gran;
|
|
} else {
|
|
if (is_abs && (gran < info->min_abs_time_granularity))
|
|
info->min_abs_time_granularity = gran;
|
|
else if (is_rel && (gran < info->min_rel_time_granularity))
|
|
info->min_rel_time_granularity = gran;
|
|
}
|
|
}
|
|
|
|
static void _change_to_relative(struct time_info *info, struct time_item *ti)
|
|
{
|
|
struct time_item *ti2;
|
|
|
|
ti2 = ti;
|
|
while ((ti2 = (struct time_item *) dm_list_prev(info->ti_list, &ti2->list))) {
|
|
if (ti2->prop->id == TIME_FRAME_AGO)
|
|
break;
|
|
|
|
switch (ti2->prop->id) {
|
|
case TIME_UNIT_SECOND:
|
|
ti2->prop = TIME_PROP(TIME_UNIT_SECOND_REL);
|
|
break;
|
|
case TIME_UNIT_MINUTE:
|
|
ti2->prop = TIME_PROP(TIME_UNIT_MINUTE_REL);
|
|
break;
|
|
case TIME_UNIT_HOUR:
|
|
ti2->prop = TIME_PROP(TIME_UNIT_HOUR_REL);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int _recognize_time_items(struct time_info *info)
|
|
{
|
|
struct time_item *ti;
|
|
|
|
/*
|
|
* At first, try to recognize strings.
|
|
* Also, if there are any items which may be absolute or
|
|
* relative and we have "TIME_FRAME_AGO", change them to relative.
|
|
*/
|
|
dm_list_iterate_items(ti, info->ti_list) {
|
|
if ((ti->prop->id == TIME_STR) && !_match_time_str(info->ti_list, ti)) {
|
|
log_error("Unrecognized string in date/time "
|
|
"specification at \"%s\".", ti->s);
|
|
return 0;
|
|
}
|
|
if (ti->prop->id == TIME_FRAME_AGO)
|
|
_change_to_relative(info, ti);
|
|
}
|
|
|
|
/*
|
|
* Now, recognize any numbers and be sensitive to the context
|
|
* given by strings we recognized before. Also, detect time
|
|
* granularity used (both for absolute and/or relative parts).
|
|
*/
|
|
dm_list_iterate_items(ti, info->ti_list) {
|
|
if ((ti->prop->id == TIME_NUM) && !_match_time_num(info->ti_list, ti)) {
|
|
log_error("Unrecognized number in date/time "
|
|
"specification at \"%s\".", ti->s);
|
|
return 0;
|
|
}
|
|
_detect_time_granularity(info, ti);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _check_time_items(struct time_info *info)
|
|
{
|
|
struct time_item *ti;
|
|
uint32_t flags;
|
|
int rel;
|
|
int date_is_relative = -1, time_is_relative = -1;
|
|
int label_time = 0, label_date = 0;
|
|
|
|
dm_list_iterate_items(ti, info->ti_list) {
|
|
flags = ti->prop->prop_flags;
|
|
rel = flags & TIME_PROP_REL;
|
|
|
|
if (flags & TIME_PROP_DATE) {
|
|
if (date_is_relative < 0)
|
|
date_is_relative = rel;
|
|
else if ((date_is_relative ^ rel) &&
|
|
(info->max_abs_date_granularity >= info->min_rel_time_granularity)) {
|
|
log_error("Mixed absolute and relative date "
|
|
"specification found at \"%s\".", ti->s);
|
|
return 0;
|
|
}
|
|
|
|
/* Date label can be used only once and not mixed with other date spec. */
|
|
if (label_date) {
|
|
log_error("Ambiguous date specification found at \"%s\".", ti->s);
|
|
return 0;
|
|
}
|
|
|
|
if (_is_time_label_date(ti->prop->id))
|
|
label_date = 1;
|
|
}
|
|
|
|
else if (flags & TIME_PROP_TIME) {
|
|
if (time_is_relative < 0)
|
|
time_is_relative = rel;
|
|
else if ((time_is_relative ^ rel)) {
|
|
log_error("Mixed absolute and relative time "
|
|
"specification found at \"%s\".", ti->s);
|
|
return 0;
|
|
}
|
|
|
|
/* Time label can be used only once and not mixed with other time spec. */
|
|
if (label_time) {
|
|
log_error("Ambiguous time specification found at \"%s\".", ti->s);
|
|
return 0;
|
|
}
|
|
|
|
if (_is_time_label_time(ti->prop->id))
|
|
label_time = 1;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
#define CACHE_ID_TIME_NOW "time_now"
|
|
|
|
static time_t *_get_now(struct dm_report *rh, struct dm_pool *mem)
|
|
{
|
|
const void *cached_obj;
|
|
time_t *now;
|
|
|
|
if (!(cached_obj = dm_report_value_cache_get(rh, CACHE_ID_TIME_NOW))) {
|
|
if (!(now = dm_pool_zalloc(mem, sizeof(time_t)))) {
|
|
log_error("_get_now: dm_pool_zalloc failed");
|
|
return NULL;
|
|
}
|
|
time(now);
|
|
if (!dm_report_value_cache_set(rh, CACHE_ID_TIME_NOW, now)) {
|
|
log_error("_get_now: failed to cache current time");
|
|
return NULL;
|
|
}
|
|
} else
|
|
now = (time_t *) cached_obj;
|
|
|
|
return now;
|
|
}
|
|
|
|
static void _adjust_time_for_granularity(struct time_info *info, struct tm *tm, time_t *t)
|
|
{
|
|
switch (info->min_abs_date_granularity) {
|
|
case TIME_UNIT_YEAR:
|
|
tm->tm_mon = 0;
|
|
/* fall through */
|
|
case TIME_UNIT_MONTH:
|
|
tm->tm_mday = 1;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (info->min_abs_time_granularity) {
|
|
case TIME_UNIT_HOUR:
|
|
tm->tm_min = 0;
|
|
/* fall through */
|
|
case TIME_UNIT_MINUTE:
|
|
tm->tm_sec = 0;
|
|
break;
|
|
case TIME_UNIT__END:
|
|
if (info->min_rel_time_granularity == TIME_UNIT__END)
|
|
tm->tm_hour = tm->tm_min = tm->tm_sec = 0;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if ((info->min_abs_time_granularity == TIME_UNIT__END) &&
|
|
(info->min_rel_time_granularity >= TIME_UNIT_DAY) &&
|
|
(info->min_rel_time_granularity <= TIME_UNIT_YEAR))
|
|
tm->tm_hour = tm->tm_min = tm->tm_sec = 0;
|
|
}
|
|
|
|
#define SECS_PER_MINUTE 60
|
|
#define SECS_PER_HOUR 3600
|
|
#define SECS_PER_DAY ((time_t)86400)
|
|
|
|
static const int _days_in_month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
|
|
|
static int _is_leap_year(long year)
|
|
{
|
|
return (((year % 4==0) && (year % 100 != 0)) || (year % 400 == 0));
|
|
}
|
|
|
|
static int _get_days_in_month(long month, long year)
|
|
{
|
|
return (month == 2 && _is_leap_year(year)) ? _days_in_month[month-1] + 1
|
|
: _days_in_month[month-1];
|
|
}
|
|
|
|
static void _get_resulting_time_span(struct time_info *info,
|
|
struct tm *tm, time_t t,
|
|
time_t *t_result1, time_t *t_result2)
|
|
{
|
|
time_t t1 = mktime(tm) - t;
|
|
time_t t2 = t1;
|
|
struct tm tmp;
|
|
|
|
if (info->min_abs_time_granularity != TIME_UNIT__END) {
|
|
if (info->min_abs_time_granularity == TIME_UNIT_MINUTE)
|
|
t2 += (SECS_PER_MINUTE - 1);
|
|
else if (info->min_abs_time_granularity == TIME_UNIT_HOUR)
|
|
t2 += (SECS_PER_HOUR - 1);
|
|
} else if (info->min_rel_time_granularity != TIME_UNIT__END) {
|
|
if (info->min_rel_time_granularity == TIME_UNIT_MINUTE)
|
|
t1 -= (SECS_PER_MINUTE + 1);
|
|
else if (info->min_rel_time_granularity == TIME_UNIT_HOUR)
|
|
t1 -= (SECS_PER_HOUR + 1);
|
|
else if ((info->min_rel_time_granularity >= TIME_UNIT_DAY) &&
|
|
(info->min_rel_time_granularity <= TIME_UNIT_YEAR))
|
|
t2 += (SECS_PER_DAY - 1);
|
|
} else {
|
|
if (info->min_abs_date_granularity == TIME_UNIT_MONTH)
|
|
t2 += (SECS_PER_DAY * _get_days_in_month(tm->tm_mon + 1, tm->tm_year) - 1);
|
|
else if (info->min_abs_date_granularity != TIME_UNIT__END)
|
|
t2 += (SECS_PER_DAY - 1);
|
|
}
|
|
|
|
/* Adjust for DST if needed. */
|
|
localtime_r(&t1, &tmp);
|
|
if (tmp.tm_isdst)
|
|
t1 -= SECS_PER_HOUR;
|
|
localtime_r(&t2, &tmp);
|
|
if (tmp.tm_isdst)
|
|
t2 -= SECS_PER_HOUR;
|
|
|
|
*t_result1 = t1;
|
|
*t_result2 = t2;
|
|
}
|
|
|
|
static int _translate_time_items(struct dm_report *rh, struct time_info *info,
|
|
const char **data_out)
|
|
{
|
|
struct time_item *ti, *ti_p = NULL;
|
|
long multiplier = 1;
|
|
struct tm tm_now;
|
|
time_id_t id;
|
|
long num;
|
|
struct tm tm; /* absolute time */
|
|
time_t t = 0; /* offset into past before absolute time */
|
|
time_t t1, t2;
|
|
char buf[32];
|
|
|
|
localtime_r(info->now, &tm_now);
|
|
tm = tm_now;
|
|
tm.tm_isdst = 0; /* we'll adjust for dst later */
|
|
tm.tm_wday = tm.tm_yday = -1;
|
|
|
|
dm_list_iterate_items(ti, info->ti_list) {
|
|
id = ti->prop->id;
|
|
|
|
if (_is_time_num(id)) {
|
|
errno = 0;
|
|
num = strtol(ti->s, NULL, 10);
|
|
if (errno) {
|
|
log_error("_translate_time_items: invalid time.");
|
|
return 0;
|
|
}
|
|
switch (id) {
|
|
case TIME_NUM_MULTIPLIER_NEGATIVE:
|
|
multiplier = -num;
|
|
break;
|
|
case TIME_NUM_MULTIPLIER:
|
|
multiplier = num;
|
|
break;
|
|
case TIME_NUM_DAY:
|
|
tm.tm_mday = num;
|
|
break;
|
|
case TIME_NUM_YEAR:
|
|
tm.tm_year = num - 1900;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else if (_is_time_month(id)) {
|
|
tm.tm_mon = id - TIME_MONTH__START - 1;
|
|
} else if (_is_time_label_date(id)) {
|
|
if (_is_time_weekday(id)) {
|
|
num = id - TIME_WEEKDAY__START - 1;
|
|
if (tm_now.tm_wday < num)
|
|
num = 7 - num + tm_now.tm_wday;
|
|
else
|
|
num = tm_now.tm_wday - num;
|
|
t += num * SECS_PER_DAY;
|
|
} else switch (id) {
|
|
case TIME_LABEL_DATE_YESTERDAY:
|
|
t += SECS_PER_DAY;
|
|
break;
|
|
case TIME_LABEL_DATE_TODAY:
|
|
/* Nothing to do here - we started with today. */
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else if (_is_time_label_time(id)) {
|
|
switch (id) {
|
|
case TIME_LABEL_TIME_NOON:
|
|
tm.tm_hour = 12;
|
|
tm.tm_min = tm.tm_sec = 0;
|
|
break;
|
|
case TIME_LABEL_TIME_MIDNIGHT:
|
|
tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else if (_is_time_unit(id)) {
|
|
switch (id) {
|
|
case TIME_UNIT_SECOND:
|
|
tm.tm_sec = multiplier;
|
|
break;
|
|
case TIME_UNIT_SECOND_REL:
|
|
t += multiplier;
|
|
break;
|
|
case TIME_UNIT_MINUTE:
|
|
tm.tm_min = multiplier;
|
|
break;
|
|
case TIME_UNIT_MINUTE_REL:
|
|
t += (multiplier * SECS_PER_MINUTE);
|
|
break;
|
|
case TIME_UNIT_HOUR:
|
|
tm.tm_hour = multiplier;
|
|
break;
|
|
case TIME_UNIT_HOUR_REL:
|
|
t += (multiplier * SECS_PER_HOUR);
|
|
break;
|
|
case TIME_UNIT_AM:
|
|
if (ti_p && ti_p->prop->id == TIME_NUM_MULTIPLIER)
|
|
tm.tm_hour = multiplier;
|
|
break;
|
|
case TIME_UNIT_PM:
|
|
if (ti_p && _is_time_unit(ti_p->prop->id))
|
|
t -= 12 * SECS_PER_HOUR;
|
|
else if (ti_p && ti_p->prop->id == TIME_NUM_MULTIPLIER)
|
|
tm.tm_hour = multiplier + 12;
|
|
break;
|
|
case TIME_UNIT_DAY:
|
|
t += multiplier * SECS_PER_DAY;
|
|
break;
|
|
case TIME_UNIT_WEEK:
|
|
t += multiplier * 7 * SECS_PER_DAY;
|
|
break;
|
|
case TIME_UNIT_MONTH:
|
|
/* if months > 12, convert to years first */
|
|
num = multiplier / 12;
|
|
tm.tm_year -= num;
|
|
|
|
num = multiplier % 12;
|
|
if (num > (tm.tm_mon + 1)) {
|
|
tm.tm_year--;
|
|
tm.tm_mon = 12 - num + tm.tm_mon;
|
|
} else
|
|
tm.tm_mon -= num;
|
|
break;
|
|
case TIME_UNIT_YEAR:
|
|
tm.tm_year -= multiplier;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
ti_p = ti;
|
|
}
|
|
|
|
_adjust_time_for_granularity(info, &tm, &t);
|
|
_get_resulting_time_span(info, &tm, t, &t1, &t2);
|
|
|
|
dm_pool_free(info->mem, info->ti_list);
|
|
info->ti_list = NULL;
|
|
|
|
if (dm_snprintf(buf, sizeof(buf), "@" FMTd64 ":@" FMTd64, (int64_t)t1, (int64_t)t2) == -1) {
|
|
log_error("_translate_time_items: dm_snprintf failed");
|
|
return 0;
|
|
}
|
|
|
|
if (!(*data_out = dm_pool_strdup(info->mem, buf))) {
|
|
log_error("_translate_time_items: dm_pool_strdup failed");
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static const char *_lv_time_handler_parse_fuzzy_name(struct dm_report *rh,
|
|
struct dm_pool *mem,
|
|
const char *data_in)
|
|
{
|
|
const char *s = data_in;
|
|
const char *data_out = NULL;
|
|
struct time_info info = {.mem = mem,
|
|
.ti_list = NULL,
|
|
.now = _get_now(rh, mem),
|
|
.min_abs_date_granularity = TIME_UNIT__END,
|
|
.max_abs_date_granularity = TIME_UNIT__START,
|
|
.min_abs_time_granularity = TIME_UNIT__END,
|
|
.min_rel_time_granularity = TIME_UNIT__END};
|
|
|
|
if (!info.now)
|
|
goto_out;
|
|
|
|
/* recognize top-level parts - string/number/time/timezone? */
|
|
if (!_preparse_fuzzy_time(s, &info))
|
|
goto out;
|
|
|
|
/* recognize each part in more detail, also look at the context around if needed */
|
|
if (!_recognize_time_items(&info))
|
|
goto out;
|
|
|
|
/* check if the combination of items is allowed or whether it makes sense at all */
|
|
if (!_check_time_items(&info))
|
|
goto out;
|
|
|
|
/* translate items into final time range */
|
|
if (!_translate_time_items(rh, &info, &data_out))
|
|
goto out;
|
|
out:
|
|
if (info.ti_list)
|
|
dm_pool_free(info.mem, info.ti_list);
|
|
return data_out;
|
|
}
|
|
|
|
static void *_lv_time_handler_get_dynamic_value(struct dm_report *rh,
|
|
struct dm_pool *mem,
|
|
const char *data_in)
|
|
{
|
|
int64_t t1, t2;
|
|
time_t *result;
|
|
|
|
if (sscanf(data_in, "@" FMTd64 ":@" FMTd64, &t1, &t2) != 2) {
|
|
log_error("Failed to get value for parsed time specification.");
|
|
return NULL;
|
|
}
|
|
|
|
if (!(result = dm_pool_alloc(mem, 2 * sizeof(time_t)))) {
|
|
log_error("Failed to allocate space to store time range.");
|
|
return NULL;
|
|
}
|
|
|
|
result[0] = (time_t) t1; /* Validate range for 32b arch ? */
|
|
result[1] = (time_t) t2;
|
|
|
|
return result;
|
|
}
|
|
|
|
static int _lv_time_handler(struct dm_report *rh, struct dm_pool *mem,
|
|
uint32_t field_num,
|
|
dm_report_reserved_action_t action,
|
|
const void *data_in, const void **data_out)
|
|
{
|
|
*data_out = NULL;
|
|
if (!data_in)
|
|
return 1;
|
|
|
|
switch (action) {
|
|
case DM_REPORT_RESERVED_PARSE_FUZZY_NAME:
|
|
*data_out = _lv_time_handler_parse_fuzzy_name(rh, mem, data_in);
|
|
break;
|
|
case DM_REPORT_RESERVED_GET_DYNAMIC_VALUE:
|
|
if (!(*data_out = _lv_time_handler_get_dynamic_value(rh, mem, data_in)))
|
|
return 0;
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Get type reserved value - the value returned is the direct value of that type.
|
|
*/
|
|
#define GET_TYPE_RESERVED_VALUE(id) _reserved_ ## id
|
|
|
|
/*
|
|
* Get field reserved value - the value returned is always a pointer (const void *).
|
|
*/
|
|
#define GET_FIELD_RESERVED_VALUE(id) _reserved_ ## id.value
|
|
|
|
/*
|
|
* Get first name assigned to the reserved value - this is the one that
|
|
* should be reported/displayed. All the other names assigned for the reserved
|
|
* value are synonyms recognized in selection criteria.
|
|
*/
|
|
#define GET_FIRST_RESERVED_NAME(id) _reserved_ ## id ## _names[0]
|
|
|
|
/*
|
|
* Reserved values and their assigned names.
|
|
* The first name is the one that is also used for reporting.
|
|
* All names listed are synonyms recognized in selection criteria.
|
|
* For binary-based values we map all reserved names listed onto value 1, blank onto value 0.
|
|
*
|
|
* TYPE_RESERVED_VALUE(type, reserved_value_id, description, value, reserved name, ...)
|
|
* FIELD_RESERVED_VALUE(field_id, reserved_value_id, description, value, reserved name, ...)
|
|
* FIELD_RESERVED_BINARY_VALUE(field_id, reserved_value_id, description, reserved name for 1, ...)
|
|
*
|
|
* Note: FIELD_RESERVED_BINARY_VALUE creates:
|
|
* - 'reserved_value_id_y' (for 1)
|
|
* - 'reserved_value_id_n' (for 0)
|
|
*/
|
|
#define NUM uint64_t
|
|
#define NUM_HND dm_report_reserved_handler
|
|
#define HND (dm_report_reserved_handler)
|
|
#define NOFLAG 0
|
|
#define NAMED DM_REPORT_FIELD_RESERVED_VALUE_NAMED
|
|
#define RANGE DM_REPORT_FIELD_RESERVED_VALUE_RANGE
|
|
#define FUZZY DM_REPORT_FIELD_RESERVED_VALUE_FUZZY_NAMES
|
|
#define DYNAMIC DM_REPORT_FIELD_RESERVED_VALUE_DYNAMIC_VALUE
|
|
|
|
#define TYPE_RESERVED_VALUE(type, flags, id, desc, value, ...) \
|
|
static const char * const _reserved_ ## id ## _names[] = { __VA_ARGS__, NULL}; \
|
|
static const type _reserved_ ## id = value;
|
|
|
|
#define FIELD_RESERVED_VALUE(flags, field_id, id, desc, value, ...) \
|
|
static const char * const _reserved_ ## id ## _names[] = { __VA_ARGS__ , NULL}; \
|
|
static const struct dm_report_field_reserved_value _reserved_ ## id = {field_ ## field_id, value};
|
|
|
|
#define FIELD_RESERVED_BINARY_VALUE(field_id, id, desc, ...) \
|
|
FIELD_RESERVED_VALUE(NAMED, field_id, id ## _y, desc, &_one64, __VA_ARGS__, _str_yes) \
|
|
FIELD_RESERVED_VALUE(NAMED, field_id, id ## _n, desc, &_zero64, __VA_ARGS__, _str_no)
|
|
|
|
#include "values.h"
|
|
|
|
#undef NUM
|
|
#undef NUM_HND
|
|
#undef HND
|
|
#undef NOFLAG
|
|
#undef NAMED
|
|
#undef RANGE
|
|
#undef TYPE_RESERVED_VALUE
|
|
#undef FIELD_RESERVED_VALUE
|
|
#undef FIELD_RESERVED_BINARY_VALUE
|
|
#undef FUZZY
|
|
#undef DYNAMIC
|
|
|
|
/*
|
|
* Create array of reserved values to be registered with reporting code via
|
|
* dm_report_init_with_selection function that initializes report with
|
|
* selection criteria. Selection code then recognizes these reserved values
|
|
* when parsing selection criteria.
|
|
*/
|
|
|
|
#define NUM DM_REPORT_FIELD_TYPE_NUMBER
|
|
#define NUM_HND DM_REPORT_FIELD_TYPE_NUMBER
|
|
#define HND 0
|
|
#define NOFLAG 0
|
|
#define NAMED DM_REPORT_FIELD_RESERVED_VALUE_NAMED
|
|
#define RANGE DM_REPORT_FIELD_RESERVED_VALUE_RANGE
|
|
#define FUZZY DM_REPORT_FIELD_RESERVED_VALUE_FUZZY_NAMES
|
|
#define DYNAMIC DM_REPORT_FIELD_RESERVED_VALUE_DYNAMIC_VALUE
|
|
|
|
/* Declaration of dm_report_reserved_value should actually be using const char * const * names */
|
|
#define TYPE_RESERVED_VALUE(type, flags, id, desc, value, ...) {type | flags, &_reserved_ ## id, (const char**) _reserved_ ## id ## _names, desc},
|
|
|
|
#define FIELD_RESERVED_VALUE(flags, field_id, id, desc, value, ...) {DM_REPORT_FIELD_TYPE_NONE | flags, &_reserved_ ## id, (const char**) _reserved_ ## id ## _names, desc},
|
|
|
|
#define FIELD_RESERVED_BINARY_VALUE(field_id, id, desc, ...) \
|
|
FIELD_RESERVED_VALUE(NAMED, field_id, id ## _y, desc, &_one64, __VA_ARGS__) \
|
|
FIELD_RESERVED_VALUE(NAMED, field_id, id ## _n, desc, &_zero64, __VA_ARGS__)
|
|
|
|
static const struct dm_report_reserved_value _report_reserved_values[] = {
|
|
#include "values.h"
|
|
{0, NULL, NULL, NULL}
|
|
};
|
|
|
|
#undef NUM
|
|
#undef NUM_HND
|
|
#undef HND
|
|
#undef NOFLAG
|
|
#undef NAMED
|
|
#undef RANGE
|
|
#undef FUZZY
|
|
#undef DYNAMIC
|
|
#undef TYPE_RESERVED_VALUE
|
|
#undef FIELD_RESERVED_VALUE
|
|
#undef FIELD_RESERVED_BINARY_VALUE
|
|
|
|
static int _field_string(struct dm_report *rh, struct dm_report_field *field, const char *data)
|
|
{
|
|
return dm_report_field_string(rh, field, &data);
|
|
}
|
|
|
|
static int _field_set_value(struct dm_report_field *field, const void *data, const void *sort)
|
|
{
|
|
dm_report_field_set_value(field, data, sort);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _field_set_string_list(struct dm_report *rh, struct dm_report_field *field,
|
|
const struct dm_list *list, void *private, int sorted,
|
|
const char *delimiter)
|
|
{
|
|
struct cmd_context *cmd = (struct cmd_context *) private;
|
|
return sorted ? dm_report_field_string_list(rh, field, list, delimiter ? : cmd->report_list_item_separator)
|
|
: dm_report_field_string_list_unsorted(rh, field, list, delimiter ? : cmd->report_list_item_separator);
|
|
}
|
|
|
|
/*
|
|
* Data-munging functions to prepare each data type for display and sorting
|
|
*/
|
|
|
|
/*
|
|
* Display either "0"/"1" or ""/"word" based on bin_value,
|
|
* cmd->report_binary_values_as_numeric selects the mode to use.
|
|
*/
|
|
static int _binary_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
|
|
struct dm_report_field *field, int bin_value, const char *word,
|
|
void *private)
|
|
{
|
|
const struct cmd_context *cmd = (const struct cmd_context *) private;
|
|
|
|
if (cmd->report_strict_type_mode || cmd->report_binary_values_as_numeric)
|
|
/* "0"/"1" */
|
|
return _field_set_value(field, bin_value ? _str_one : _str_zero, bin_value ? &_one64 : &_zero64);
|
|
|
|
/* blank/"word" */
|
|
return _field_set_value(field, bin_value ? word : "", bin_value ? &_one64 : &_zero64);
|
|
}
|
|
|
|
static int _binary_undef_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
|
|
struct dm_report_field *field, void *private)
|
|
{
|
|
const struct cmd_context *cmd = (const struct cmd_context *) private;
|
|
|
|
if (cmd->report_strict_type_mode || cmd->report_binary_values_as_numeric)
|
|
return _field_set_value(field, GET_FIRST_RESERVED_NAME(num_undef_64), &GET_TYPE_RESERVED_VALUE(num_undef_64));
|
|
|
|
return _field_set_value(field, _str_unknown, &GET_TYPE_RESERVED_VALUE(num_undef_64));
|
|
}
|
|
|
|
static int _string_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 _chars_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute__((unused)))
|
|
{
|
|
return _field_string(rh, field, data);
|
|
}
|
|
|
|
static int _uuid_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
char *repstr;
|
|
|
|
if (!(repstr = id_format_and_copy(mem, data)))
|
|
return_0;
|
|
|
|
return _field_set_value(field, repstr, NULL);
|
|
}
|
|
|
|
static int _devminor_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
int devminor = (int) MINOR((*(const struct device * const *) data)->dev);
|
|
|
|
return dm_report_field_int(rh, field, &devminor);
|
|
}
|
|
|
|
static int _devmajor_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
int devmajor = (int) MAJOR((*(const struct device * const *) data)->dev);
|
|
|
|
return dm_report_field_int(rh, field, &devmajor);
|
|
}
|
|
|
|
static int _dev_name_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
return _field_string(rh, field, dev_name(*(const struct device * const *) data));
|
|
}
|
|
|
|
static int _devices_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct lv_segment *seg = (const struct lv_segment *) data;
|
|
struct dm_list *list;
|
|
|
|
if (!(list = lvseg_devices(mem, seg)))
|
|
return_0;
|
|
|
|
return _field_set_string_list(rh, field, list, private, 0, ",");
|
|
}
|
|
|
|
static int _metadatadevices_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct lv_segment *seg = (const struct lv_segment *) data;
|
|
struct dm_list *list;
|
|
|
|
if (!(list = lvseg_metadata_devices(mem, seg)))
|
|
return_0;
|
|
|
|
return _field_set_string_list(rh, field, list, private, 0, ",");
|
|
}
|
|
|
|
static int _peranges_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct lv_segment *seg = (const struct lv_segment *) data;
|
|
struct dm_list *list;
|
|
|
|
if (!(list = lvseg_seg_pe_ranges(mem, seg)))
|
|
return_0;
|
|
|
|
return _field_set_string_list(rh, field, list, private, 0, " ");
|
|
}
|
|
|
|
static int _leranges_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct lv_segment *seg = (const struct lv_segment *) data;
|
|
struct dm_list *list;
|
|
|
|
if (!(list = lvseg_seg_le_ranges(mem, seg)))
|
|
return_0;
|
|
|
|
return _field_set_string_list(rh, field, list, private, 0, NULL);
|
|
}
|
|
|
|
static int _metadataleranges_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct lv_segment *seg = (const struct lv_segment *) data;
|
|
struct dm_list *list;
|
|
|
|
if (!(list = lvseg_seg_metadata_le_ranges(mem, seg)))
|
|
return_0;
|
|
|
|
return _field_set_string_list(rh, field, list, private, 0, NULL);
|
|
}
|
|
|
|
static int _tags_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct dm_list *tagsl = (const struct dm_list *) data;
|
|
|
|
return _field_set_string_list(rh, field, tagsl, private, 1, NULL);
|
|
}
|
|
|
|
struct _str_list_append_baton {
|
|
struct dm_pool *mem;
|
|
struct dm_list *result;
|
|
};
|
|
|
|
static int _str_list_append(const char *line, void *baton)
|
|
{
|
|
struct _str_list_append_baton *b = baton;
|
|
const char *line2 = dm_pool_strdup(b->mem, line);
|
|
|
|
if (!line2)
|
|
return_0;
|
|
|
|
if (!str_list_add(b->mem, b->result, line2))
|
|
return_0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _cache_settings_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct lv_segment *seg = (const struct lv_segment *) data;
|
|
const struct lv_segment *setting_seg = NULL;
|
|
const struct dm_config_node *settings;
|
|
struct dm_list *result;
|
|
struct _str_list_append_baton baton;
|
|
struct dm_list dummy_list; /* dummy list to display "nothing" */
|
|
|
|
if (seg_is_writecache(seg)) {
|
|
if (!(result = str_list_create(mem)))
|
|
return_0;
|
|
|
|
if (!writecache_settings_to_str_list((struct writecache_settings *)&seg->writecache_settings, result, mem))
|
|
return_0;
|
|
|
|
return _field_set_string_list(rh, field, result, private, 0, NULL);
|
|
}
|
|
|
|
if (seg_is_cache(seg) && lv_is_cache_vol(seg->pool_lv))
|
|
setting_seg = seg;
|
|
|
|
else if (seg_is_cache_pool(seg))
|
|
setting_seg = seg;
|
|
|
|
else if (seg_is_cache(seg))
|
|
setting_seg = first_seg(seg->pool_lv);
|
|
|
|
if (!setting_seg || !setting_seg->policy_settings) {
|
|
dm_list_init(&dummy_list);
|
|
return _field_set_string_list(rh, field, &dummy_list, private, 0, NULL);
|
|
/* TODO: once we have support for STR_LIST reserved values, replace with:
|
|
* return _field_set_value(field, GET_FIRST_RESERVED_NAME(cache_settings_undef), GET_FIELD_RESERVED_VALUE(cache_settings_undef));
|
|
*/
|
|
}
|
|
|
|
settings = setting_seg->policy_settings->child;
|
|
|
|
if (!(result = str_list_create(mem)))
|
|
return_0;
|
|
|
|
baton.mem = mem;
|
|
baton.result = result;
|
|
|
|
while (settings) {
|
|
dm_config_write_one_node(settings, _str_list_append, &baton);
|
|
settings = settings->sib;
|
|
};
|
|
|
|
return _field_set_string_list(rh, field, result, private, 0, NULL);
|
|
}
|
|
|
|
static int _do_get_kernel_cache_settings_list(struct dm_pool *mem,
|
|
int cache_argc, char **cache_argv,
|
|
struct dm_list *result)
|
|
{
|
|
const char *key, *value;
|
|
char *buf;
|
|
size_t buf_len;
|
|
int i;
|
|
|
|
for (i = 0; i+1 < cache_argc; i += 2) {
|
|
key = cache_argv[i];
|
|
value = cache_argv[i+1];
|
|
/* +1 for "=" char and +1 for trailing zero */
|
|
buf_len = strlen(key) + strlen(value) + 2;
|
|
if (!(buf = dm_pool_alloc(mem, buf_len)))
|
|
return_0;
|
|
if (dm_snprintf(buf, buf_len, "%s=%s", key, value) < 0)
|
|
return_0;
|
|
if (!str_list_add_no_dup_check(mem, result, buf))
|
|
return_0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _get_kernel_cache_settings_list(struct dm_pool *mem,
|
|
struct dm_status_cache *cache_status,
|
|
struct dm_list **result)
|
|
{
|
|
if (!(*result = str_list_create(mem)))
|
|
return_0;
|
|
|
|
if (!_do_get_kernel_cache_settings_list(mem, cache_status->core_argc,
|
|
cache_status->core_argv, *result))
|
|
return_0;
|
|
|
|
if (!_do_get_kernel_cache_settings_list(mem, cache_status->policy_argc,
|
|
cache_status->policy_argv, *result))
|
|
return_0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _kernel_cache_settings_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
|
|
struct dm_list dummy_list; /* dummy list to display "nothing" */
|
|
struct dm_list *result;
|
|
int r = 0;
|
|
|
|
if (lvdm->seg_status.type != SEG_STATUS_CACHE) {
|
|
dm_list_init(&dummy_list);
|
|
return _field_set_string_list(rh, field, &dummy_list, private, 0, NULL);
|
|
}
|
|
|
|
if (!(mem = dm_pool_create("reporter_pool", 1024)))
|
|
return_0;
|
|
|
|
if (!_get_kernel_cache_settings_list(mem, lvdm->seg_status.cache, &result))
|
|
goto_out;
|
|
|
|
r = _field_set_string_list(rh, field, result, private, 0, NULL);
|
|
out:
|
|
dm_pool_destroy(mem);
|
|
return r;
|
|
}
|
|
|
|
static int _kernel_cache_policy_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
|
|
|
|
if ((lvdm->seg_status.type == SEG_STATUS_CACHE) &&
|
|
lvdm->seg_status.cache->policy_name)
|
|
return _field_string(rh, field, lvdm->seg_status.cache->policy_name);
|
|
|
|
return _field_set_value(field, GET_FIRST_RESERVED_NAME(cache_policy_undef),
|
|
GET_FIELD_RESERVED_VALUE(cache_policy_undef));
|
|
}
|
|
|
|
static int _kernelmetadataformat_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
|
|
unsigned format;
|
|
|
|
if (lvdm->seg_status.type == SEG_STATUS_CACHE) {
|
|
format = (lvdm->seg_status.cache->feature_flags & DM_CACHE_FEATURE_METADATA2);
|
|
return dm_report_field_uint64(rh, field, format ? &_two64 : &_one64);
|
|
}
|
|
|
|
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
|
|
}
|
|
|
|
static int _cache_policy_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct lv_segment *seg = (const struct lv_segment *) data;
|
|
const struct lv_segment *setting_seg = NULL;
|
|
|
|
if (seg_is_cache(seg) && lv_is_cache_vol(seg->pool_lv))
|
|
setting_seg = seg;
|
|
|
|
else if (seg_is_cache_pool(seg))
|
|
setting_seg = seg;
|
|
|
|
else if (seg_is_cache(seg))
|
|
setting_seg = first_seg(seg->pool_lv);
|
|
|
|
if (!setting_seg || !setting_seg->policy_name)
|
|
return _field_set_value(field, GET_FIRST_RESERVED_NAME(cache_policy_undef),
|
|
GET_FIELD_RESERVED_VALUE(cache_policy_undef));
|
|
|
|
return _field_string(rh, field, setting_seg->policy_name);
|
|
}
|
|
|
|
static int _modules_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
struct dm_list *modules;
|
|
|
|
if (!(modules = str_list_create(mem))) {
|
|
log_error("modules str_list allocation failed");
|
|
return 0;
|
|
}
|
|
|
|
if (!(list_lv_modules(mem, lv, modules)))
|
|
return_0;
|
|
|
|
return _field_set_string_list(rh, field, modules, private, 1, NULL);
|
|
}
|
|
|
|
static int _lvprofile_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
|
|
if (lv->profile)
|
|
return _field_string(rh, field, lv->profile->name);
|
|
|
|
return _field_set_value(field, "", NULL);
|
|
}
|
|
|
|
static int _lvlockargs_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
|
|
return _field_string(rh, field, lv->lock_args ? : "");
|
|
}
|
|
|
|
static int _vgfmt_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct volume_group *vg = (const struct volume_group *) data;
|
|
|
|
if (vg->fid && vg->fid->fmt)
|
|
return _field_string(rh, field, vg->fid->fmt->name);
|
|
|
|
return _field_set_value(field, "", NULL);
|
|
}
|
|
|
|
static int _pvfmt_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct label *l = (const struct label *) data;
|
|
|
|
if (l->labeller && l->labeller->fmt)
|
|
return _field_string(rh, field, l->labeller->fmt->name);
|
|
|
|
return _field_set_value(field, "", NULL);
|
|
}
|
|
|
|
static int _lvkmaj_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 lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
|
|
|
|
if (lvdm->info.exists && lvdm->info.major >= 0)
|
|
return dm_report_field_int(rh, field, &lvdm->info.major);
|
|
|
|
return dm_report_field_int32(rh, field, &GET_TYPE_RESERVED_VALUE(num_undef_32));
|
|
}
|
|
|
|
static int _lvkmin_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 lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
|
|
|
|
if (lvdm->info.exists && lvdm->info.minor >= 0)
|
|
return dm_report_field_int(rh, field, &lvdm->info.minor);
|
|
|
|
return dm_report_field_int32(rh, field, &GET_TYPE_RESERVED_VALUE(num_undef_32));
|
|
}
|
|
|
|
static int _lvstatus_disp(struct dm_report *rh __attribute__((unused)), struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute__((unused)))
|
|
{
|
|
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
|
|
char *repstr;
|
|
|
|
if (!(repstr = lv_attr_dup_with_info_and_seg_status(mem, lvdm)))
|
|
return_0;
|
|
|
|
return _field_set_value(field, repstr, NULL);
|
|
}
|
|
|
|
static int _pvstatus_disp(struct dm_report *rh __attribute__((unused)), struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute__((unused)))
|
|
{
|
|
const struct physical_volume *pv =
|
|
(const struct physical_volume *) data;
|
|
char *repstr;
|
|
|
|
if (!(repstr = pv_attr_dup(mem, pv)))
|
|
return_0;
|
|
|
|
return _field_set_value(field, repstr, NULL);
|
|
}
|
|
|
|
static int _vgstatus_disp(struct dm_report *rh __attribute__((unused)), struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute__((unused)))
|
|
{
|
|
const struct volume_group *vg = (const struct volume_group *) data;
|
|
char *repstr;
|
|
|
|
if (!(repstr = vg_attr_dup(mem, vg)))
|
|
return_0;
|
|
|
|
return _field_set_value(field, repstr, NULL);
|
|
}
|
|
|
|
static int _segtype_disp(struct dm_report *rh __attribute__((unused)),
|
|
struct dm_pool *mem __attribute__((unused)),
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute__((unused)))
|
|
{
|
|
const struct lv_segment *seg = (const struct lv_segment *) data;
|
|
char *name;
|
|
|
|
if (!(name = lvseg_segtype_dup(mem, seg))) {
|
|
log_error("Failed to get segtype name.");
|
|
return 0;
|
|
}
|
|
|
|
return _field_set_value(field, name, NULL);
|
|
}
|
|
|
|
static int _lvname_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
struct cmd_context *cmd = (struct cmd_context *) private;
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
int is_historical = lv_is_historical(lv);
|
|
const char *tmp_lvname;
|
|
char *repstr, *lvname;
|
|
size_t len;
|
|
|
|
if (!is_historical && (lv_is_visible(lv) || !cmd->report_mark_hidden_devices))
|
|
return _field_string(rh, field, lv->name);
|
|
|
|
if (is_historical) {
|
|
tmp_lvname = lv->this_glv->historical->name;
|
|
len = strlen(tmp_lvname) + strlen(HISTORICAL_LV_PREFIX) + 1;
|
|
} else {
|
|
tmp_lvname = lv->name;
|
|
len = strlen(tmp_lvname) + 3;
|
|
}
|
|
|
|
if (!(repstr = dm_pool_zalloc(mem, len))) {
|
|
log_error("dm_pool_alloc failed");
|
|
return 0;
|
|
}
|
|
|
|
if (dm_snprintf(repstr, len, "%s%s%s",
|
|
is_historical ? HISTORICAL_LV_PREFIX : "[",
|
|
tmp_lvname,
|
|
is_historical ? "" : "]") < 0) {
|
|
log_error("lvname snprintf failed");
|
|
return 0;
|
|
}
|
|
|
|
if (!(lvname = dm_pool_strdup(mem, tmp_lvname))) {
|
|
log_error("dm_pool_strdup failed");
|
|
return 0;
|
|
}
|
|
|
|
return _field_set_value(field, repstr, lvname);
|
|
}
|
|
|
|
static int _do_loglv_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private,
|
|
int uuid)
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
struct logical_volume *mirror_log_lv = lv_mirror_log_lv(lv);
|
|
|
|
if (!mirror_log_lv)
|
|
return _field_set_value(field, "", NULL);
|
|
|
|
if (uuid)
|
|
return _uuid_disp(rh, mem, field, &mirror_log_lv->lvid.id[1], private);
|
|
|
|
return _lvname_disp(rh, mem, field, mirror_log_lv, private);
|
|
}
|
|
|
|
static int _loglv_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute__((unused)))
|
|
{
|
|
return _do_loglv_disp(rh, mem, field, data, private, 0);
|
|
}
|
|
|
|
static int _loglvuuid_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute__((unused)))
|
|
{
|
|
return _do_loglv_disp(rh, mem, field, data, private, 1);
|
|
}
|
|
|
|
static int _lvfullname_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute__((unused)))
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
char *repstr;
|
|
|
|
if (!(repstr = lv_fullname_dup(mem, lv)))
|
|
return_0;
|
|
|
|
return _field_set_value(field, repstr, NULL);
|
|
}
|
|
|
|
static int _lvparent_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
struct logical_volume *parent_lv = lv_parent(lv);
|
|
|
|
if (!parent_lv)
|
|
return _field_set_value(field, "", NULL);
|
|
|
|
return _lvname_disp(rh, mem, field, parent_lv, private);
|
|
}
|
|
|
|
static int _do_datalv_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute__((unused)),
|
|
int uuid)
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
struct logical_volume *data_lv = lv_data_lv(lv);
|
|
|
|
if (!data_lv)
|
|
return _field_set_value(field, "", NULL);
|
|
|
|
if (uuid)
|
|
return _uuid_disp(rh, mem, field, &data_lv->lvid.id[1], private);
|
|
|
|
return _lvname_disp(rh, mem, field, data_lv, private);
|
|
}
|
|
|
|
static int _datalv_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute__((unused)))
|
|
{
|
|
return _do_datalv_disp(rh, mem, field, data, private, 0);
|
|
}
|
|
|
|
static int _datalvuuid_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute__((unused)))
|
|
{
|
|
return _do_datalv_disp(rh, mem, field, data, private, 1);
|
|
}
|
|
|
|
static int _do_metadatalv_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute__((unused)),
|
|
int uuid)
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
struct logical_volume *metadata_lv = lv_metadata_lv(lv);
|
|
|
|
if (!metadata_lv)
|
|
return _field_set_value(field, "", NULL);
|
|
|
|
if (uuid)
|
|
return _uuid_disp(rh, mem, field, &metadata_lv->lvid.id[1], private);
|
|
|
|
return _lvname_disp(rh, mem, field, metadata_lv, private);
|
|
}
|
|
|
|
static int _metadatalv_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute__((unused)))
|
|
{
|
|
return _do_metadatalv_disp(rh, mem, field, data, private, 0);
|
|
}
|
|
|
|
static int _metadatalvuuid_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute__((unused)))
|
|
{
|
|
return _do_metadatalv_disp(rh, mem, field, data, private, 1);
|
|
}
|
|
|
|
static int _do_poollv_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private,
|
|
int uuid)
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
struct logical_volume *pool_lv = lv_pool_lv(lv);
|
|
|
|
if (!pool_lv)
|
|
return _field_set_value(field, "", NULL);
|
|
|
|
if (uuid)
|
|
return _uuid_disp(rh, mem, field, &pool_lv->lvid.id[1], private);
|
|
|
|
return _lvname_disp(rh, mem, field, pool_lv, private);
|
|
}
|
|
|
|
static int _poollv_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute__((unused)))
|
|
{
|
|
return _do_poollv_disp(rh, mem, field, data, private, 0);
|
|
}
|
|
|
|
static int _poollvuuid_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute__((unused)))
|
|
{
|
|
return _do_poollv_disp(rh, mem, field, data, private, 1);
|
|
}
|
|
|
|
static int _lvpath_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute__((unused)))
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
char *repstr;
|
|
|
|
if (!(repstr = lv_path_dup(mem, lv)))
|
|
return_0;
|
|
|
|
return _field_set_value(field, repstr, NULL);
|
|
}
|
|
|
|
static int _lvdmpath_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute__((unused)))
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
char *repstr;
|
|
|
|
if (!(repstr = lv_dmpath_dup(mem, lv)))
|
|
return_0;
|
|
|
|
return _field_set_value(field, repstr, NULL);
|
|
}
|
|
|
|
static int _do_origin_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private,
|
|
int uuid)
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
struct logical_volume *origin_lv = lv_origin_lv(lv);
|
|
|
|
if (!origin_lv)
|
|
return _field_set_value(field, "", NULL);
|
|
|
|
if (uuid)
|
|
return _uuid_disp(rh, mem, field, &origin_lv->lvid.id[1], private);
|
|
|
|
return _lvname_disp(rh, mem, field, origin_lv, private);
|
|
}
|
|
|
|
static int _origin_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
return _do_origin_disp(rh, mem, field, data, private, 0);
|
|
}
|
|
|
|
static int _originuuid_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
return _do_origin_disp(rh, mem, field, data, private, 1);
|
|
}
|
|
|
|
static const char *_get_glv_str(char *buf, size_t buf_len,
|
|
struct generic_logical_volume *glv)
|
|
{
|
|
if (!glv->is_historical)
|
|
return glv->live->name;
|
|
|
|
if (dm_snprintf(buf, buf_len, "%s%s", HISTORICAL_LV_PREFIX, glv->historical->name) < 0) {
|
|
log_error("_get_glv_str: dm_snprintf failed");
|
|
return NULL;
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
static int _find_ancestors(struct _str_list_append_baton *ancestors,
|
|
struct generic_logical_volume glv,
|
|
int full, int include_historical_lvs)
|
|
{
|
|
struct lv_segment *seg;
|
|
void *orig_p = glv.live;
|
|
const char *ancestor_str;
|
|
char buf[NAME_LEN + sizeof(HISTORICAL_LV_PREFIX)];
|
|
|
|
if (glv.is_historical) {
|
|
if (full && glv.historical->indirect_origin)
|
|
glv = *glv.historical->indirect_origin;
|
|
} else if (lv_is_cow(glv.live)) {
|
|
glv.live = origin_from_cow(glv.live);
|
|
} else if (lv_is_thin_volume(glv.live)) {
|
|
seg = first_seg(glv.live);
|
|
if (seg->origin)
|
|
glv.live = seg->origin;
|
|
else if (seg->external_lv)
|
|
glv.live = seg->external_lv;
|
|
else if (full && seg->indirect_origin)
|
|
glv = *seg->indirect_origin;
|
|
}
|
|
|
|
if (orig_p != glv.live) {
|
|
if (!(ancestor_str = _get_glv_str(buf, sizeof(buf), &glv)))
|
|
return_0;
|
|
if (!glv.is_historical || include_historical_lvs) {
|
|
if (!_str_list_append(ancestor_str, ancestors))
|
|
return_0;
|
|
}
|
|
if (!_find_ancestors(ancestors, glv, full, include_historical_lvs))
|
|
return_0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _lvancestors_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
struct cmd_context *cmd = (struct cmd_context *) private;
|
|
struct logical_volume *lv = (struct logical_volume *) data;
|
|
struct _str_list_append_baton ancestors;
|
|
struct generic_logical_volume glv;
|
|
|
|
ancestors.mem = mem;
|
|
if (!(ancestors.result = str_list_create(mem)))
|
|
return_0;
|
|
|
|
if ((glv.is_historical = lv_is_historical(lv)))
|
|
glv.historical = lv->this_glv->historical;
|
|
else
|
|
glv.live = lv;
|
|
|
|
if (!_find_ancestors(&ancestors, glv, 0, cmd->include_historical_lvs)) {
|
|
dm_pool_free(ancestors.mem, ancestors.result);
|
|
return_0;
|
|
}
|
|
|
|
return _field_set_string_list(rh, field, ancestors.result, private, 0, NULL);
|
|
}
|
|
|
|
static int _lvfullancestors_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
struct cmd_context *cmd = (struct cmd_context *) private;
|
|
struct logical_volume *lv = (struct logical_volume *) data;
|
|
struct _str_list_append_baton full_ancestors;
|
|
struct generic_logical_volume glv;
|
|
|
|
full_ancestors.mem = mem;
|
|
if (!(full_ancestors.result = str_list_create(mem)))
|
|
return_0;
|
|
|
|
if ((glv.is_historical = lv_is_historical(lv)))
|
|
glv.historical = lv->this_glv->historical;
|
|
else
|
|
glv.live = lv;
|
|
|
|
if (!_find_ancestors(&full_ancestors, glv, 1, cmd->include_historical_lvs)) {
|
|
dm_pool_free(full_ancestors.mem, full_ancestors.result);
|
|
return_0;
|
|
}
|
|
|
|
return _field_set_string_list(rh, field, full_ancestors.result, private, 0, NULL);
|
|
}
|
|
|
|
static int _find_descendants(struct _str_list_append_baton *descendants,
|
|
struct generic_logical_volume glv,
|
|
int full, int include_historical_lvs)
|
|
{
|
|
struct generic_logical_volume glv_next = {0};
|
|
const struct seg_list *sl;
|
|
struct lv_segment *seg;
|
|
struct glv_list *glvl;
|
|
struct dm_list *list;
|
|
const char *descendant_str;
|
|
char buf[64];
|
|
|
|
if (glv.is_historical) {
|
|
if (full) {
|
|
list = &glv.historical->indirect_glvs;
|
|
dm_list_iterate_items(glvl, list) {
|
|
if (!glvl->glv->is_historical || include_historical_lvs) {
|
|
if (!(descendant_str = _get_glv_str(buf, sizeof(buf), glvl->glv)))
|
|
return_0;
|
|
if (!_str_list_append(descendant_str, descendants))
|
|
return_0;
|
|
}
|
|
if (!_find_descendants(descendants, *glvl->glv, full, include_historical_lvs))
|
|
return_0;
|
|
}
|
|
}
|
|
} else if (lv_is_origin(glv.live)) {
|
|
list = &glv.live->snapshot_segs;
|
|
dm_list_iterate_items_gen(seg, list, origin_list) {
|
|
if ((glv.live = seg->cow)) {
|
|
if (!(descendant_str = _get_glv_str(buf, sizeof(buf), &glv)))
|
|
return_0;
|
|
if (!_str_list_append(descendant_str, descendants))
|
|
return_0;
|
|
if (!_find_descendants(descendants, glv, full, include_historical_lvs))
|
|
return_0;
|
|
}
|
|
}
|
|
} else {
|
|
list = &glv.live->segs_using_this_lv;
|
|
dm_list_iterate_items(sl, list) {
|
|
if (lv_is_thin_volume(sl->seg->lv)) {
|
|
seg = first_seg(sl->seg->lv);
|
|
if ((seg->origin == glv.live) || (seg->external_lv == glv.live)) {
|
|
glv_next.live = sl->seg->lv;
|
|
if (!(descendant_str = _get_glv_str(buf, sizeof(buf), &glv_next)))
|
|
return_0;
|
|
if (!_str_list_append(descendant_str, descendants))
|
|
return_0;
|
|
if (!_find_descendants(descendants, glv_next, full, include_historical_lvs))
|
|
return_0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (full) {
|
|
list = &glv.live->indirect_glvs;
|
|
dm_list_iterate_items(glvl, list) {
|
|
if (!glvl->glv->is_historical || include_historical_lvs) {
|
|
if (!(descendant_str = _get_glv_str(buf, sizeof(buf), glvl->glv)))
|
|
return_0;
|
|
if (!_str_list_append(descendant_str, descendants))
|
|
return_0;
|
|
}
|
|
if (!_find_descendants(descendants, *glvl->glv, full, include_historical_lvs))
|
|
return_0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _lvdescendants_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
struct cmd_context *cmd = (struct cmd_context *) private;
|
|
struct logical_volume *lv = (struct logical_volume *) data;
|
|
struct _str_list_append_baton descendants;
|
|
struct generic_logical_volume glv;
|
|
|
|
descendants.mem = mem;
|
|
if (!(descendants.result = str_list_create(mem)))
|
|
return_0;
|
|
|
|
if ((glv.is_historical = lv_is_historical(lv)))
|
|
glv.historical = lv->this_glv->historical;
|
|
else
|
|
glv.live = lv;
|
|
|
|
if (!_find_descendants(&descendants, glv, 0, cmd->include_historical_lvs)) {
|
|
dm_pool_free(descendants.mem, descendants.result);
|
|
return_0;
|
|
}
|
|
|
|
return _field_set_string_list(rh, field, descendants.result, private, 0, NULL);
|
|
}
|
|
|
|
static int _lvfulldescendants_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
struct cmd_context *cmd = (struct cmd_context *) private;
|
|
struct logical_volume *lv = (struct logical_volume *) data;
|
|
struct _str_list_append_baton descendants;
|
|
struct generic_logical_volume glv;
|
|
|
|
descendants.mem = mem;
|
|
if (!(descendants.result = str_list_create(mem)))
|
|
return_0;
|
|
|
|
if ((glv.is_historical = lv_is_historical(lv)))
|
|
glv.historical = lv->this_glv->historical;
|
|
else
|
|
glv.live = lv;
|
|
|
|
if (!_find_descendants(&descendants, glv, 1, cmd->include_historical_lvs)) {
|
|
dm_pool_free(descendants.mem, descendants.result);
|
|
return_0;
|
|
}
|
|
|
|
return _field_set_string_list(rh, field, descendants.result, private, 0, NULL);
|
|
}
|
|
|
|
static int _do_movepv_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private,
|
|
int uuid)
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
const char *repstr;
|
|
|
|
if (uuid)
|
|
repstr = lv_move_pv_uuid_dup(mem, lv);
|
|
else
|
|
repstr = lv_move_pv_dup(mem, lv);
|
|
|
|
if (repstr)
|
|
return _field_string(rh, field, repstr);
|
|
|
|
return _field_set_value(field, "", NULL);
|
|
}
|
|
|
|
static int _movepv_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute__((unused)))
|
|
{
|
|
return _do_movepv_disp(rh, mem, field, data, private, 0);
|
|
}
|
|
|
|
static int _movepvuuid_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute__((unused)))
|
|
{
|
|
return _do_movepv_disp(rh, mem, field, data, private, 1);
|
|
}
|
|
|
|
static int _do_convertlv_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private,
|
|
int uuid)
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
const struct logical_volume *convert_lv = lv_convert_lv(lv);
|
|
|
|
if (!convert_lv)
|
|
return _field_set_value(field, "", NULL);
|
|
|
|
if (uuid)
|
|
return _uuid_disp(rh, mem, field, &convert_lv->lvid.id[1], private);
|
|
|
|
return _lvname_disp(rh, mem, field, convert_lv, private);
|
|
}
|
|
|
|
static int _convertlv_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute__((unused)))
|
|
{
|
|
return _do_convertlv_disp(rh, mem, field, data, private, 0);
|
|
}
|
|
|
|
static int _convertlvuuid_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute__((unused)))
|
|
{
|
|
return _do_convertlv_disp(rh, mem, field, data, private, 1);
|
|
}
|
|
|
|
static int _size32_disp(struct dm_report *rh __attribute__((unused)), struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const uint32_t size = *(const uint32_t *) data;
|
|
const char *disp, *repstr;
|
|
double *sortval;
|
|
|
|
if (!*(disp = display_size_units(private, (uint64_t) size)))
|
|
return_0;
|
|
|
|
if (!(repstr = dm_pool_strdup(mem, disp))) {
|
|
log_error("dm_pool_strdup failed");
|
|
return 0;
|
|
}
|
|
|
|
if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t)))) {
|
|
log_error("dm_pool_alloc failed");
|
|
return 0;
|
|
}
|
|
|
|
*sortval = (double) size;
|
|
|
|
return _field_set_value(field, repstr, sortval);
|
|
}
|
|
|
|
static int _size64_disp(struct dm_report *rh __attribute__((unused)),
|
|
struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const uint64_t size = *(const uint64_t *) data;
|
|
const char *disp, *repstr;
|
|
double *sortval;
|
|
|
|
if (!*(disp = display_size_units(private, size)))
|
|
return_0;
|
|
|
|
if (!(repstr = dm_pool_strdup(mem, disp))) {
|
|
log_error("dm_pool_strdup failed");
|
|
return 0;
|
|
}
|
|
|
|
if (!(sortval = dm_pool_alloc(mem, sizeof(double)))) {
|
|
log_error("dm_pool_alloc failed");
|
|
return 0;
|
|
}
|
|
|
|
*sortval = (double) size;
|
|
|
|
return _field_set_value(field, repstr, sortval);
|
|
}
|
|
|
|
static int _lv_size_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
const struct lv_segment *seg = first_seg(lv);
|
|
uint64_t size = lv->le_count;
|
|
|
|
if (seg && !lv_is_raid_image(lv))
|
|
size -= (uint64_t) seg->reshape_len * (seg->area_count > 2 ? (seg->area_count - seg->segtype->parity_devs) : 1);
|
|
|
|
size *= lv->vg->extent_size;
|
|
|
|
return _size64_disp(rh, mem, field, &size, private);
|
|
}
|
|
|
|
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)))
|
|
{
|
|
return dm_report_field_uint32(rh, field, data);
|
|
}
|
|
|
|
static int _int8_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 val = *(const int8_t *)data;
|
|
|
|
return dm_report_field_int32(rh, field, &val);
|
|
}
|
|
|
|
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)))
|
|
{
|
|
return dm_report_field_int32(rh, field, data);
|
|
}
|
|
|
|
static int _lvwhenfull_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute__((unused)))
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
|
|
if (lv_is_thin_pool(lv)) {
|
|
if (lv->status & LV_ERROR_WHEN_FULL)
|
|
return _field_set_value(field, GET_FIRST_RESERVED_NAME(lv_when_full_error),
|
|
GET_FIELD_RESERVED_VALUE(lv_when_full_error));
|
|
|
|
return _field_set_value(field, GET_FIRST_RESERVED_NAME(lv_when_full_queue),
|
|
GET_FIELD_RESERVED_VALUE(lv_when_full_queue));
|
|
}
|
|
|
|
return _field_set_value(field, GET_FIRST_RESERVED_NAME(lv_when_full_undef),
|
|
GET_FIELD_RESERVED_VALUE(lv_when_full_undef));
|
|
}
|
|
|
|
static int _lvreadahead_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute__((unused)))
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
|
|
if (lv->read_ahead == DM_READ_AHEAD_AUTO)
|
|
return _field_set_value(field, GET_FIRST_RESERVED_NAME(lv_read_ahead_auto),
|
|
GET_FIELD_RESERVED_VALUE(lv_read_ahead_auto));
|
|
|
|
return _size32_disp(rh, mem, field, &lv->read_ahead, private);
|
|
}
|
|
|
|
static int _lvkreadahead_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data,
|
|
void *private)
|
|
{
|
|
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
|
|
|
|
if (!lvdm->info.exists)
|
|
return dm_report_field_int32(rh, field, &GET_TYPE_RESERVED_VALUE(num_undef_32));
|
|
|
|
return _size32_disp(rh, mem, field, &lvdm->info.read_ahead, private);
|
|
}
|
|
|
|
static int _vdo_operating_mode_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
|
|
|
|
if ((lv_is_vdo_pool(lvdm->lv) || lv_is_vdo(lvdm->lv)) &&
|
|
(lvdm->seg_status.type == SEG_STATUS_VDO_POOL))
|
|
return _field_string(rh, field, get_vdo_operating_mode_name(lvdm->seg_status.vdo_pool.vdo->operating_mode));
|
|
|
|
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
|
|
}
|
|
|
|
static int _vdo_compression_state_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
|
|
|
|
if ((lv_is_vdo_pool(lvdm->lv) || lv_is_vdo(lvdm->lv)) &&
|
|
(lvdm->seg_status.type == SEG_STATUS_VDO_POOL))
|
|
return _field_string(rh, field, get_vdo_compression_state_name(lvdm->seg_status.vdo_pool.vdo->compression_state));
|
|
|
|
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
|
|
}
|
|
|
|
static int _vdo_index_state_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
|
|
|
|
if ((lv_is_vdo_pool(lvdm->lv) || lv_is_vdo(lvdm->lv)) &&
|
|
(lvdm->seg_status.type == SEG_STATUS_VDO_POOL))
|
|
return _field_string(rh, field, get_vdo_index_state_name(lvdm->seg_status.vdo_pool.vdo->index_state));
|
|
|
|
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
|
|
}
|
|
|
|
static int _vdo_used_size_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
uint64_t size;
|
|
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
|
|
|
|
if ((lv_is_vdo_pool(lvdm->lv) || lv_is_vdo(lvdm->lv)) &&
|
|
(lvdm->seg_status.type == SEG_STATUS_VDO_POOL)) {
|
|
size = lvdm->seg_status.vdo_pool.vdo->used_blocks * DM_VDO_BLOCK_SIZE;
|
|
return _size64_disp(rh, mem, field, &size, private);
|
|
}
|
|
|
|
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
|
|
}
|
|
|
|
static int _vdo_saving_percent_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
|
|
|
|
if ((lv_is_vdo_pool(lvdm->lv) || lv_is_vdo(lvdm->lv)) &&
|
|
(lvdm->seg_status.type == SEG_STATUS_VDO_POOL))
|
|
return dm_report_field_percent(rh, field, &lvdm->seg_status.vdo_pool.saving);
|
|
|
|
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
|
|
}
|
|
|
|
static int _vgsize_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct volume_group *vg = (const struct volume_group *) data;
|
|
uint64_t size = vg_size(vg);
|
|
|
|
return _size64_disp(rh, mem, field, &size, private);
|
|
}
|
|
|
|
static int _segmonitor_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct lv_segment *seg = (const struct lv_segment *)data;
|
|
char *str;
|
|
|
|
if (!(str = lvseg_monitor_dup(mem, seg)))
|
|
return_0;
|
|
|
|
if (*str)
|
|
return _field_set_value(field, str, NULL);
|
|
|
|
return _field_set_value(field, GET_FIRST_RESERVED_NAME(seg_monitor_undef),
|
|
GET_FIELD_RESERVED_VALUE(seg_monitor_undef));
|
|
}
|
|
|
|
static int _segstart_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct lv_segment *seg = (const struct lv_segment *) data;
|
|
uint64_t start = lvseg_start(seg);
|
|
|
|
return _size64_disp(rh, mem, field, &start, private);
|
|
}
|
|
|
|
static int _segstartpe_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 lv_segment *seg = (const struct lv_segment *) data;
|
|
|
|
return dm_report_field_uint32(rh, field, &seg->le);
|
|
}
|
|
|
|
/* Hepler: get used stripes = total stripes minux any to remove after reshape */
|
|
static int _get_seg_used_stripes(const struct lv_segment *seg)
|
|
{
|
|
uint32_t s;
|
|
uint32_t stripes = seg->area_count;
|
|
|
|
for (s = seg->area_count - 1; stripes && s; s--) {
|
|
if (seg_type(seg, s) == AREA_LV &&
|
|
seg_lv(seg, s)->status & LV_REMOVE_AFTER_RESHAPE)
|
|
stripes--;
|
|
else
|
|
break;
|
|
}
|
|
|
|
return stripes;
|
|
}
|
|
|
|
static int _seg_stripes_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct lv_segment *seg = ((const struct lv_segment *) data);
|
|
|
|
return dm_report_field_uint32(rh, field, &seg->area_count);
|
|
}
|
|
|
|
/* Report the number of data stripes, which is less than total stripes (e.g. 2 less for raid6) */
|
|
static int _seg_data_stripes_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct lv_segment *seg = (const struct lv_segment *) data;
|
|
uint32_t stripes = _get_seg_used_stripes(seg) - seg->segtype->parity_devs;
|
|
|
|
/* FIXME: in case of odd numbers of raid10 stripes */
|
|
if (seg_is_raid10(seg))
|
|
stripes /= seg->data_copies;
|
|
|
|
return dm_report_field_uint32(rh, field, &stripes);
|
|
}
|
|
|
|
/* Helper: return the top-level, reshapable raid LV in case @seg belongs to an raid rimage LV */
|
|
|