Commit 27b0f54c authored by Daniel Salzman's avatar Daniel Salzman

Merge branch 'timestamp_io_refactor' into 'master'

Timestamp io refactor

See merge request !752
parents 5fc62cd4 0489393e
...@@ -53,27 +53,28 @@ Print the program help. ...@@ -53,27 +53,28 @@ Print the program help.
\fB\-V\fP, \fB\-\-version\fP \fB\-V\fP, \fB\-\-version\fP
Print the program version. Print the program version.
.TP .TP
\fB\-t\fP \fBtsig_name\fP [\fItsig_algorithm\fP] [\fItsig_bits\fP] \fB\-t\fP, \fB\-\-tsig\fP \fItsig_name\fP [\fItsig_algorithm\fP] [\fItsig_bits\fP]
Generates TSIG key. TSIG algorithm can be specified by string (default: hmac\-sha256), Generates a TSIG key. TSIG algorithm can be specified by string (default: hmac\-sha256),
bit length of the key by number (default: optimal length given by algorithm). bit length of the key by number (default: optimal length given by algorithm).
.UNINDENT .UNINDENT
.SS Config options .SS Config options
.INDENT 0.0 .INDENT 0.0
.TP .TP
\fB\-c\fP \fB\-c\fP, \fB\-\-config\fP \fIfile\fP
Use specified Knot DNS configuration file path. Use a textual configuration file (default is \fB@config_dir@/knot.conf\fP).
.TP .TP
\fB\-C\fP \fB\-C\fP, \fB\-\-confdb\fP \fIdirectory\fP
Use specified Knot DNS configuration database path. The default configuration Use a binary configuration database directory (default is \fB@storage_dir@/confdb\fP).
database, if exists, has a preference to the default configuration file. The default configuration database, if exists, has a preference to the default
configuration file.
.TP .TP
\fB\-d\fP \fB\-d\fP, \fB\-\-dir\fP \fIpath\fP
Use specified KASP database path and default configuration. Use specified KASP database path and default configuration.
.UNINDENT .UNINDENT
.SS Commands .SS Commands
.INDENT 0.0 .INDENT 0.0
.TP .TP
\fBlist\fP \fBlist\fP [\fItimestamp_format\fP]
Prints the list of key IDs and parameters of keys belonging to the zone. Prints the list of key IDs and parameters of keys belonging to the zone.
.TP .TP
\fBgenerate\fP [\fIarguments\fP\&...] \fBgenerate\fP [\fIarguments\fP\&...]
...@@ -148,15 +149,27 @@ Timestamp for key ot be deleted. ...@@ -148,15 +149,27 @@ Timestamp for key ot be deleted.
Zero timestamp means infinite future. Zero timestamp means infinite future.
.TP .TP
\fIUNIX_time\fP \fIUNIX_time\fP
Positive number of seconds since 1970. Positive number of seconds since 1970 UTC.
.TP .TP
\fIYYYYMMDDHHMMSS\fP \fIYYYYMMDDHHMMSS\fP
Date and time in this format without any punctuation. Date and time in this format without any punctuation.
.TP .TP
\fIrelative_timestamp\fP \fIrelative_timestamp\fP
The word "now" followed by sign (+, \-), a number and a shortcut for time unit A sign character (\fB+\fP, \fB\-\fP), a number, and an optional time unit
(y, mo, d, h, mi, (nothing = seconds)), e.g. now+1mi, now\-2mo, now+10, (\fBy\fP, \fBmo\fP, \fBd\fP, \fBh\fP, \fBmi\fP, \fBs\fP). The default unit is one second.
now+0, now\-1y, ... E.g. +1mi, \-2mo.
.UNINDENT
.SS Output timestamp formats
.INDENT 0.0
.TP
(none)
The timestamps are printed as UNIX timestamp.
.TP
\fBhuman\fP
The timestamps are printed relatively to now using time units (e.g. \-2y5mo, +1h13s).
.TP
\fBiso\fP
The timestamps are printed in the ISO8601 format (e.g. 2016\-12\-31T23:59:00).
.UNINDENT .UNINDENT
.SH EXAMPLES .SH EXAMPLES
.INDENT 0.0 .INDENT 0.0
...@@ -180,7 +193,7 @@ Generate new DNSSEC key: ...@@ -180,7 +193,7 @@ Generate new DNSSEC key:
.nf .nf
.ft C .ft C
$ keymgr example.com. generate algorithm=ECDSAP256SHA256 size=256 \e $ keymgr example.com. generate algorithm=ECDSAP256SHA256 size=256 \e
ksk=true created=1488034625 publish=20170223205611 retire=now+10mo remove=now+1y ksk=true created=1488034625 publish=20170223205611 retire=+10mo remove=+1y
.ft P .ft P
.fi .fi
.UNINDENT .UNINDENT
...@@ -204,7 +217,7 @@ Configure key timing: ...@@ -204,7 +217,7 @@ Configure key timing:
.sp .sp
.nf .nf
.ft C .ft C
$ keymgr example.com. set 4208 active=now+2mi retire=now+4mi remove=now+5mi $ keymgr example.com. set 4208 active=+2mi retire=+4mi remove=+5mi
.ft P .ft P
.fi .fi
.UNINDENT .UNINDENT
......
...@@ -30,27 +30,28 @@ Basic options ...@@ -30,27 +30,28 @@ Basic options
**-V**, **--version** **-V**, **--version**
Print the program version. Print the program version.
**-t** **tsig_name** [*tsig_algorithm*] [*tsig_bits*] **-t**, **--tsig** *tsig_name* [*tsig_algorithm*] [*tsig_bits*]
Generates TSIG key. TSIG algorithm can be specified by string (default: hmac-sha256), Generates a TSIG key. TSIG algorithm can be specified by string (default: hmac-sha256),
bit length of the key by number (default: optimal length given by algorithm). bit length of the key by number (default: optimal length given by algorithm).
Config options Config options
.............. ..............
**-c** **-c**, **--config** *file*
Use specified Knot DNS configuration file path. Use a textual configuration file (default is :file:`@config_dir@/knot.conf`).
**-C** **-C**, **--confdb** *directory*
Use specified Knot DNS configuration database path. The default configuration Use a binary configuration database directory (default is :file:`@storage_dir@/confdb`).
database, if exists, has a preference to the default configuration file. The default configuration database, if exists, has a preference to the default
configuration file.
**-d** **-d**, **--dir** *path*
Use specified KASP database path and default configuration. Use specified KASP database path and default configuration.
Commands Commands
........ ........
**list** **list** [*timestamp_format*]
Prints the list of key IDs and parameters of keys belonging to the zone. Prints the list of key IDs and parameters of keys belonging to the zone.
**generate** [*arguments*...] **generate** [*arguments*...]
...@@ -125,15 +126,27 @@ Timestamps ...@@ -125,15 +126,27 @@ Timestamps
Zero timestamp means infinite future. Zero timestamp means infinite future.
*UNIX_time* *UNIX_time*
Positive number of seconds since 1970. Positive number of seconds since 1970 UTC.
*YYYYMMDDHHMMSS* *YYYYMMDDHHMMSS*
Date and time in this format without any punctuation. Date and time in this format without any punctuation.
*relative_timestamp* *relative_timestamp*
The word "now" followed by sign (+, -), a number and a shortcut for time unit A sign character (**+**, **-**), a number, and an optional time unit
(y, mo, d, h, mi, (nothing = seconds)), e.g. now+1mi, now-2mo, now+10, (**y**, **mo**, **d**, **h**, **mi**, **s**). The default unit is one second.
now+0, now-1y, ... E.g. +1mi, -2mo.
Output timestamp formats
........................
(none)
The timestamps are printed as UNIX timestamp.
**human**
The timestamps are printed relatively to now using time units (e.g. -2y5mo, +1h13s).
**iso**
The timestamps are printed in the ISO8601 format (e.g. 2016-12-31T23:59:00).
Examples Examples
-------- --------
...@@ -145,7 +158,7 @@ Examples ...@@ -145,7 +158,7 @@ Examples
2. Generate new DNSSEC key:: 2. Generate new DNSSEC key::
$ keymgr example.com. generate algorithm=ECDSAP256SHA256 size=256 \ $ keymgr example.com. generate algorithm=ECDSAP256SHA256 size=256 \
ksk=true created=1488034625 publish=20170223205611 retire=now+10mo remove=now+1y ksk=true created=1488034625 publish=20170223205611 retire=+10mo remove=+1y
3. Import a DNSSEC key from BIND:: 3. Import a DNSSEC key from BIND::
...@@ -153,7 +166,7 @@ Examples ...@@ -153,7 +166,7 @@ Examples
4. Configure key timing:: 4. Configure key timing::
$ keymgr example.com. set 4208 active=now+2mi retire=now+4mi remove=now+5mi $ keymgr example.com. set 4208 active=+2mi retire=+4mi remove=+5mi
5. Share a KSK from another zone:: 5. Share a KSK from another zone::
......
...@@ -14,6 +14,14 @@ ...@@ -14,6 +14,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <assert.h>
#include <ctype.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "contrib/time.h" #include "contrib/time.h"
#ifndef HAVE_CLOCK_GETTIME #ifndef HAVE_CLOCK_GETTIME
#include <sys/time.h> #include <sys/time.h>
...@@ -56,3 +64,333 @@ double time_diff_ms(const struct timespec *begin, const struct timespec *end) ...@@ -56,3 +64,333 @@ double time_diff_ms(const struct timespec *begin, const struct timespec *end)
return (result.tv_sec * 1e3) + (result.tv_nsec / 1e6); return (result.tv_sec * 1e3) + (result.tv_nsec / 1e6);
} }
typedef struct {
const char *format;
const char *timespec;
const char *parsed;
knot_timediff_t offset;
char offset_sign;
char offset_unit;
struct tm calendar;
int error;
} time_ctx_t;
// After casting (struct tm) to (int []), we can use indexes...
static int calendar_index(char ind)
{
switch (ind) {
case 'Y': return 5;
case 'M': return 4;
case 'D': return 3;
case 'h': return 2;
case 'm': return 1;
case 's': return 0;
default: assert(0); return 6;
}
}
static size_t calendar_digits(int index)
{
return index == 5 ? 4 : 2;
}
static size_t unit_value(char unit)
{
size_t val = 1;
switch (unit) {
case 'M':
return 3600 * 24 * 30;
case 'Y':
val *= 365;
case 'D':
val *= 24;
case 'h':
val *= 60;
case 'm':
val *= 60;
case 's':
default:
return val;
}
}
static knot_time_t time_ctx_finalize(time_ctx_t *ctx)
{
if (ctx->offset_sign) {
ctx->offset *= unit_value(ctx->offset_unit);
return knot_time_add(knot_time(), (ctx->offset_sign == '-' ? -1 : 1) * ctx->offset);
} else if (ctx->offset) {
return (knot_time_t)ctx->offset;
} else if (ctx->calendar.tm_year != 0) {
ctx->calendar.tm_isdst = -1;
ctx->calendar.tm_year -= 1900;
ctx->calendar.tm_mon -= 1;
// Set UTC timezone before using mktime
putenv("TZ=UTC");
tzset();
return (knot_time_t)mktime(&ctx->calendar);
} else {
return (knot_time_t)0;
}
}
static void time_ctx_reset(time_ctx_t *ctx)
{
ctx->parsed = ctx->timespec;
ctx->offset = 0;
ctx->offset_sign = 0;
memset(&ctx->calendar, 0, sizeof(ctx->calendar));
ctx->error = 0;
}
static void parse_quote(time_ctx_t *ctx)
{
while (*ctx->format != '|' && *ctx->format != '\0') {
if (*ctx->format == '\'') {
ctx->format++;
return;
}
if (*ctx->format++ != *ctx->parsed++) {
ctx->error = -1;
return;
}
}
ctx->error = -2;
return;
}
static void parse_offset(time_ctx_t *ctx)
{
ctx->offset = 0;
ctx->error = -1;
while (isdigit(*ctx->parsed)) {
ctx->offset *= 10;
ctx->offset += *ctx->parsed++ - '0';
ctx->error = 0;
}
}
static void parse_calendar(time_ctx_t *ctx, int index)
{
int *cal_arr = (int *)&ctx->calendar;
cal_arr[index] = 0;
for (size_t i = 0; i < calendar_digits(index); i++) {
if (!isdigit(*ctx->parsed)) {
ctx->error = -1;
return;
}
cal_arr[index] *= 10;
cal_arr[index] += *ctx->parsed++ - '0';
}
}
static void parse_sign(time_ctx_t *ctx)
{
char sign1 = *(ctx->format - 1), sign2 = *ctx->format;
bool use_sign2 = (sign2 == '+' || sign2 == '-');
bool allow_plus = (sign1 == '+' || (sign1 == '-' && sign2 == '+'));
bool allow_minus = (sign1 == '-' || (sign1 == '+' && sign2 == '-'));
assert(sign1 == '+' || sign1 == '-');
if ((*ctx->parsed == '+' && allow_plus) || (*ctx->parsed == '-' && allow_minus)) {
ctx->offset_sign = *ctx->parsed++;
ctx->format += (use_sign2 ? 1 : 0);
} else {
ctx->error = -11;
}
}
static void parse_unit1(time_ctx_t *ctx)
{
char u = *ctx->parsed++;
switch (u) {
case 'Y':
case 'M':
case 'D':
case 'h':
case 'm':
case 's':
ctx->offset_unit = u;
break;
default:
ctx->error = -1;
}
}
static void parse_unit2(time_ctx_t *ctx)
{
char u = *ctx->parsed++;
switch (u) {
case 'y':
case 'd':
ctx->offset_unit = toupper(u);
break;
case 'h':
case 's':
ctx->offset_unit = u;
break;
case 'm':
switch (*ctx->parsed++) {
case 'o':
ctx->offset_unit = 'M';
break;
case 'i':
ctx->offset_unit = 'm';
break;
default:
ctx->error = -1;
}
break;
default:
ctx->error = -1;
}
}
int knot_time_parse(const char *format, const char *timespec, knot_time_t *time)
{
if (format == NULL || timespec == NULL || time == NULL) {
return -1;
}
time_ctx_t ctx = {
.format = format,
.timespec = timespec,
.parsed = timespec,
.offset = 0,
.offset_sign = 0,
// we hope that .calendar is zeroed by default
.error = 0,
};
while (ctx.error == 0 && *ctx.format != '\0') {
switch (*ctx.format++) {
case '|':
if (*ctx.parsed == '\0') {
*time = time_ctx_finalize(&ctx);
return 0;
} else {
time_ctx_reset(&ctx);
}
break;
case '\'':
parse_quote(&ctx);
break;
case '#':
parse_offset(&ctx);
break;
case 'Y':
case 'M':
case 'D':
case 'h':
case 'm':
case 's':
parse_calendar(&ctx, calendar_index(*(ctx.format - 1)));
break;
case '+':
case '-':
parse_sign(&ctx);
break;
case 'U':
parse_unit1(&ctx);
break;
case 'u':
parse_unit2(&ctx);
break;
default:
return -1;
}
if (ctx.error < 0) {
while (*ctx.format != '|' && *ctx.format != '\0') {
ctx.format++;
}
time_ctx_reset(&ctx);
ctx.error = (*ctx.format == '\0' ? -1 : 0);
}
}
if (ctx.error == 0 && *ctx.parsed == '\0') {
*time = time_ctx_finalize(&ctx);
return 0;
}
return -1;
}
static char *unit_names_mixed[] = { "Y", "M", "D", "h", "m", "s" };
static char *unit_names_lower[] = { "y", "mo", "d", "h", "mi", "s" };
static size_t unit_sizes[] = { 3600*24*365, 3600*24*30, 3600*24, 3600, 60, 1 };
static const size_t unit_count = 6;
static int print_unit(char *dst, size_t dst_len, char *unit_names[unit_count],
size_t max_units, knot_time_t time)
{
int ret;
if (time == 0) {
ret = snprintf(dst, dst_len, "0");
return (ret < 0 || ret >= dst_len ? -1 : 0);
}
knot_timediff_t diff = knot_time_diff(time, knot_time());
if (dst_len-- < 1) {
return -1;
}
*dst++ = (diff < 0 ? '-' : '+');
if (diff < 0) {
diff = -diff;
} else if (diff == 0) {
ret = snprintf(dst, dst_len, "0%s", unit_names[unit_count - 1]);
return (ret < 0 || ret >= dst_len ? -1 : 0);
}
size_t curr_unit = 0, used_units = 0;
while (curr_unit < unit_count && used_units < max_units) {
if (diff >= unit_sizes[curr_unit]) {
ret = snprintf(dst, dst_len, "%"KNOT_TIMEDIFF_PRINTF"%s",
diff / unit_sizes[curr_unit],
unit_names[curr_unit]);
if (ret < 0 || ret >= dst_len) {
return -1;
}
dst += ret;
dst_len -= ret;
used_units++;
diff %= unit_sizes[curr_unit];
}
curr_unit++;
}
return 0;
}
int knot_time_print(knot_time_print_t format, knot_time_t time, char *dst, size_t dst_len)
{
if (dst == NULL) {
return -1;
}
int ret;
switch (format) {
case TIME_PRINT_UNIX:
ret = snprintf(dst, dst_len, "%"KNOT_TIME_PRINTF, time);
return ((ret >= 0 && ret < dst_len) ? 0 : -1);
case TIME_PRINT_ISO8601:
if (time > LONG_MAX) {
return -1;
}
struct tm lt;
time_t tt = (time_t)time;
ret = (localtime_r(&tt, &lt) == NULL ? -1 :
strftime(dst, dst_len, "%Y-%m-%dT%H:%M:%S", &lt));
return (ret > 0 ? 0 : -1);
case TIME_PRINT_RELSEC:
ret = snprintf(dst, dst_len, "%+"KNOT_TIMEDIFF_PRINTF,
knot_time_diff(time, knot_time()));
return ((ret >= 0 && ret < dst_len) ? 0 : -1);
case TIME_PRINT_HUMAN_MIXED:
return print_unit(dst, dst_len, unit_names_mixed, unit_count, time);
case TIME_PRINT_HUMAN_LOWER:
return print_unit(dst, dst_len, unit_names_lower, unit_count, time);
default:
return -1;
}
}
...@@ -18,6 +18,18 @@ ...@@ -18,6 +18,18 @@
#include <stdint.h> #include <stdint.h>
#include <time.h> #include <time.h>
#include <inttypes.h>
/*!
* \brief Specify output format for knot_time_print().
*/
typedef enum {
TIME_PRINT_UNIX, // numeric UNIX time
TIME_PRINT_ISO8601, // 2016-12-31T23:59:00
TIME_PRINT_RELSEC, // relative +6523
TIME_PRINT_HUMAN_MIXED, // relative with mixed-case units
TIME_PRINT_HUMAN_LOWER, // relative with lower-case units
} knot_time_print_t;
/*! /*!
* \brief Get current time. * \brief Get current time.
...@@ -50,6 +62,9 @@ typedef int64_t knot_timediff_t; ...@@ -50,6 +62,9 @@ typedef int64_t knot_timediff_t;
#define KNOT_TIMEDIFF_MIN INT64_MIN #define KNOT_TIMEDIFF_MIN INT64_MIN
#define KNOT_TIMEDIFF_MAX INT64_MAX #define KNOT_TIMEDIFF_MAX INT64_MAX
#define KNOT_TIME_PRINTF PRIu64
#define KNOT_TIMEDIFF_PRINTF PRId64
/*! /*!
* \brief Returns current time sice epoch. * \brief Returns current time sice epoch.
*/ */
...@@ -115,3 +130,42 @@ inline static knot_time_t knot_time_from_u32(uint32_t u32time) ...@@ -115,3 +130,42 @@ inline static knot_time_t knot_time_from_u32(uint32_t u32time)
{ {
return (knot_time_t)u32time; return (knot_time_t)u32time;
} }
/*!
* \brief Parse a text-formatted timestamp to knot_time_t using format specification.
*
* \param format The timestamp text format specification.
* \param timespec Text-formatted timestamp.
* \param time The parsed timestamp.
*
* The format specification basics:
* <format 1>|<format2> - The pipe sign separates two time format specifications. Leftmost
* specification matching the timespec is used.
* '<a string>' - Matches exactly <a string> (not containing apostrophes) in timespec.
* # - Hashtag matches for a number in timespec, stands for either a UNIX timestamp,
* or, within a context of an unit, as a number of such units.
* Y, M, D, h, m, s - Matches a number, stands for a number of years, months, days, hours,
* minutes and seconds, respectively.
* +, - - The + and - signs declaring that following timespec is relative to "now".
* A single sign can be used to limit the timestamp being in future or in past,
* or both +- allow the timestamp to select any (just one) of them.
* U - Matches one of Y, M, D, h, m, s in the timespec standing for a time unit.
* u - Like U, but the unit in the timestamp is from: y, mo, d, h, mi, s.
*
* \retval -1 An error occured, out_time has no sense.
* \return 0 OK, timestamp parsed successfully.
*/
int knot_time_parse(const char *format, const char *timespec, knot_time_t *time);
/*!
* \brief Print the timestamp in specified format into a string buffer.
*
* \param format The timestamp text format specification.
* \param time The timestamp to be printed.
* \param dst The destination buffer pointer with text-formatted timestamp.
* \param dst_len The destination buffer length.
*
* \retval -1 An error occured, the buffer may be filled with nonsense.
* \return 0 OK, timestamp printed successfully.
*/
int knot_time_print(knot_time_print_t format, knot_time_t time, char *dst, size_t dst_len);
...@@ -254,11 +254,8 @@ static int zone_status(zone_t *zone, ctl_args_t *args) ...@@ -254,11 +254,8 @@ static int zone_status(zone_t *zone, ctl_args_t *args)
} else if (ev_time <= time(NULL)) { } else if (ev_time <= time(NULL)) {
ret = snprintf(buff, sizeof(buff), "pending"); ret = snprintf(buff, sizeof(buff), "pending");
} else { } else {
ev_time -= time(NULL); ret = knot_time_print(TIME_PRINT_HUMAN_MIXED,
ret = snprintf(buff, sizeof(buff), "in %lldh%lldm%llds", ev_time, buff, sizeof(buff));
(long long)(ev_time / 3600),
(long long)(ev_time % 3600) / 60,
(long long)(ev_time % 60));
} }
if (ret < 0 || ret >= sizeof(buff)) { if (ret < 0 || ret >= sizeof(buff)) {
return KNOT_ESPACE; return KNOT_ESPACE;
......
...@@ -32,57 +32,6 @@ ...@@ -32,57 +32,6 @@
#include "zscanner/scanner.h" #include "zscanner/scanner.h"
#include "contrib/base64.h" #include "contrib/base64.h"
static time_t arg_timestamp(const char *arg)
{
if (isdigit((int)arg[0]) && strlen(arg) < 12) {
return atol(arg); // unixtime
}
if (isdigit((int)arg[0]) && strlen(arg) == 14) {
struct tm tm = { 0 };
char *end = strptime(arg, "%Y%m%d%H%M%S", &tm);
if (end == NULL || *end != '\0') {
return -1;
}
return mktime(&tm); // time format
}
long amount;
if (strncasecmp(arg, "now+", 4) == 0) {
amount = atol(arg + 4);
} else if (strncasecmp(arg, "now-", 4) == 0) {
amount = 0 - atol(arg + 4);
} else if (strncasecmp(arg, "t+", 2) == 0) {
amount = atol(arg + 2);
} else if (strncasecmp(arg, "t-", 2) == 0) {
amount = 0 - atol(arg + 2);
} else if (arg[0] == '+' || arg[0] == '-') {
amount = atol(arg);
} else {
return -1;
}
char *unit = strrchr(arg, '0' + (labs(amount) % 10));
if (unit++ == NULL) {
return -1;
}
time_t now = time(NULL);
switch ((*unit == 'm') ? 'm' + *(unit + 1) : *unit) {
case 'm' + 'i':
return now + amount * 60;
case 'h':
return now + amount * 3600;
case 'd':
return now + amount * 3600 * 24;
case 'w':
return now + amount * 3600 * 24 * 7;
case 'm' + 'o':
return now + amount * 3600 * 24 * 30; // this is lame but same as keymgr
case 'y':
return now + amount * 3600 * 24 * 365;
case '\0':
return now + amount;
}
return -1;
}
static bool genkeyargs(int argc, char *argv[], bool just_timing, static bool genkeyargs(int argc, char *argv[], bool just_timing,
bool *isksk, dnssec_key_algorithm_t *algorithm, bool *isksk, dnssec_key_algorithm_t *algorithm,
uint16_t *keysize, knot_kasp_key_timing_t *timing) uint16_t *keysize, knot_kasp_key_timing_t *timing)
...@@ -134,29 +83,31 @@ static bool genkeyargs(int argc, char *argv[], bool just_timing, ...@@ -134,29 +83,31 @@ static bool genkeyargs(int argc, char *argv[], bool just_timing,
strncasecmp(argv[i], "active=", 7) == 0 || strncasecmp(argv[i], "active=", 7) == 0 ||
strncasecmp(argv[i], "retire=", 7) == 0 || strncasecmp(argv[i], "retire=", 7) == 0 ||
strncasecmp(argv[i], "remove=", 7) == 0) { strncasecmp(argv[i], "remove=", 7) == 0) {
time_t stamp = arg_timestamp(strchr(argv[i], '='