Commit f047271c authored by Ondřej Zajíček's avatar Ondřej Zajíček

Timers: Parse and format functions for microsecond times

Date/time output (e.g. in logs, show commands) can use %f to specify
subsecond time. By default, millisecond precision is used in output.
parent 02552526
......@@ -102,9 +102,9 @@ config_alloc(const char *name)
c->pool = p;
c->mem = l;
c->file_name = ndup;
c->load_time = now;
c->tf_route = c->tf_proto = (struct timeformat){"%T", "%F", 20*3600};
c->tf_base = c->tf_log = (struct timeformat){"%F %T", NULL, 0};
c->load_time = current_time();
c->tf_route = c->tf_proto = TM_ISO_SHORT_MS;
c->tf_base = c->tf_log = TM_ISO_LONG_MS;
c->gr_wait = DEFAULT_GR_WAIT;
return c;
......
......@@ -57,7 +57,7 @@ struct config {
struct config *fallback; /* Link to regular config for CLI parsing */
int obstacle_count; /* Number of items blocking freeing of this config */
int shutdown; /* This is a pseudo-config for daemon shutdown */
bird_clock_t load_time; /* When we've got this configuration */
btime load_time; /* When we've got this configuration */
};
/* Please don't use these variables in protocols. Use proto_config->global instead. */
......
......@@ -14,7 +14,7 @@ CF_HDR
#include "conf/conf.h"
#include "lib/resource.h"
#include "lib/socket.h"
#include "sysdep/unix/timer.h"
#include "lib/timer.h"
#include "lib/string.h"
#include "nest/protocol.h"
#include "nest/iface.h"
......@@ -60,7 +60,7 @@ CF_DECLS
struct lsadb_show_data *ld;
struct iface *iface;
void *g;
bird_clock_t time;
btime time;
struct f_prefix px;
struct proto_spec ps;
struct channel_limit cl;
......@@ -81,7 +81,7 @@ CF_DECLS
%type <i> expr bool pxlen4
%type <i32> expr_us
%type <time> datetime
%type <time> time
%type <a> ipa
%type <net> net_ip4_ net_ip6_ net_ip6 net_ip_ net_ip net_or_ipa
%type <net_ptr> net_ net_any net_vpn4_ net_vpn6_ net_vpn_ net_roa4_ net_roa6_ net_roa_
......@@ -308,11 +308,11 @@ label_stack:
}
;
datetime:
time:
TEXT {
$$ = tm_parse_datetime($1);
$$ = tm_parse_time($1);
if (!$$)
cf_error("Invalid date and time");
cf_error("Invalid date/time");
}
;
......
......@@ -456,24 +456,26 @@ protocol rip {
used for other commands and <cf/log/ is used in a log file.
"<m/format1/" is a format string using <it/strftime(3)/ notation (see
<it/man strftime/ for details). <m/limit> and "<m/format2/" allow to
specify the second format string for times in past deeper than <m/limit/
seconds. There are few shorthands: <cf/iso long/ is a ISO 8601 date/time
format (YYYY-MM-DD hh:mm:ss) that can be also specified using <cf/"%F %T"/.
<it/man strftime/ for details). It is extended to support sub-second
time part with variable precision (up to microseconds) using "%f"
conversion code (e.g., "%T.%3f" is hh:mm:ss.sss time). <m/limit/ and
"<m/format2/" allow to specify the second format string for times in
past deeper than <m/limit/ seconds.
There are several shorthands: <cf/iso long/ is a ISO 8601 date/time
format (YYYY-MM-DD hh:mm:ss) that can be also specified using <cf/"%F
%T"/. Similarly, <cf/iso long ms/ and <cf/iso long us/ are ISO 8601
date/time formats with millisecond or microsecond precision.
<cf/iso short/ is a variant of ISO 8601 that uses just the time format
(hh:mm:ss) for near times (up to 20 hours in the past) and the date
format (YYYY-MM-DD) for far times. This is a shorthand for
<cf/"%T" 72000 "%F"/.
format (YYYY-MM-DD) for far times. This is a shorthand for <cf/"%T"
72000 "%F"/. And there are also <cf/iso short ms/ and <cf/iso short us/
high-precision variants of that.
By default, BIRD uses the <cf/iso short/ format for <cf/route/ and
<cf/protocol/ times, and the <cf/iso long/ format for <cf/base/ and
By default, BIRD uses the <cf/iso short ms/ format for <cf/route/ and
<cf/protocol/ times, and the <cf/iso long ms/ format for <cf/base/ and
<cf/log/ times.
In pre-1.4.0 versions, BIRD used an short, ad-hoc format for <cf/route/
and <cf/protocol/ times, and a <cf/iso long/ similar format (DD-MM-YYYY
hh:mm:ss) for <cf/base/ and <cf/log/. These timeformats could be set by
<cf/old short/ and <cf/old long/ compatibility shorthands.
<tag><label id="opt-table">table <m/name/ [sorted]</tag>
Create a new routing table. The default routing table is created
implicitly, other routing tables have to be added by this command.
......
......@@ -8,6 +8,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include "nest/bird.h"
......@@ -222,3 +223,137 @@ timer_init(void)
timers_init(&main_timeloop, &root_pool);
timeloop_init_current();
}
/**
* tm_parse_time - parse a date and time
* @x: time string
*
* tm_parse_time() takes a textual representation of a date and time
* (yyyy-mm-dd[ hh:mm:ss[.sss]]) and converts it to the corresponding value of
* type &btime.
*/
btime
tm_parse_time(char *x)
{
struct tm tm;
int usec, n1, n2, n3, r;
r = sscanf(x, "%d-%d-%d%n %d:%d:%d%n.%d%n",
&tm.tm_year, &tm.tm_mon, &tm.tm_mday, &n1,
&tm.tm_hour, &tm.tm_min, &tm.tm_sec, &n2,
&usec, &n3);
if ((r == 3) && !x[n1])
tm.tm_hour = tm.tm_min = tm.tm_sec = usec = 0;
else if ((r == 6) && !x[n2])
usec = 0;
else if ((r == 7) && !x[n3])
{
/* Convert subsecond digits to proper precision */
int digits = n3 - n2 - 1;
if ((usec < 0) || (usec > 999999) || (digits < 1) || (digits > 6))
return 0;
while (digits++ < 6)
usec *= 10;
}
else
return 0;
tm.tm_mon--;
tm.tm_year -= 1900;
s64 ts = mktime(&tm);
if ((ts == (s64) (time_t) -1) || (ts < 0) || (ts > ((s64) 1 << 40)))
return 0;
return ts S + usec;
}
/**
* tm_format_time - convert date and time to textual representation
* @x: destination buffer of size %TM_DATETIME_BUFFER_SIZE
* @fmt: specification of resulting textual representation of the time
* @t: time
*
* This function formats the given relative time value @t to a textual
* date/time representation (dd-mm-yyyy hh:mm:ss) in real time.
*/
void
tm_format_time(char *x, struct timeformat *fmt, btime t)
{
btime dt = current_time() - t;
btime rt = current_real_time() - dt;
int v1 = !fmt->limit || (dt < fmt->limit);
tm_format_real_time(x, v1 ? fmt->fmt1 : fmt->fmt2, rt);
}
/* Replace %f in format string with usec scaled to requested precision */
static int
strfusec(char *buf, int size, const char *fmt, uint usec)
{
char *str = buf;
int parity = 0;
while (*fmt)
{
if (!size)
return 0;
if ((fmt[0] == '%') && (!parity) &&
((fmt[1] == 'f') || (fmt[1] >= '1') && (fmt[1] <= '6') && (fmt[2] == 'f')))
{
int digits = (fmt[1] == 'f') ? 6 : (fmt[1] - '0');
uint d = digits, u = usec;
/* Convert microseconds to requested precision */
while (d++ < 6)
u /= 10;
int num = bsnprintf(str, size, "%0*u", digits, u);
if (num < 0)
return 0;
fmt += (fmt[1] == 'f') ? 2 : 3;
ADVANCE(str, size, num);
}
else
{
/* Handle '%%' expression */
parity = (*fmt == '%') ? !parity : 0;
*str++ = *fmt++;
size--;
}
}
if (!size)
return 0;
*str = 0;
return str - buf;
}
void
tm_format_real_time(char *x, const char *fmt, btime t)
{
s64 t1 = t TO_S;
s64 t2 = t - t1 S;
time_t ts = t1;
struct tm tm;
if (!localtime_r(&ts, &tm))
goto err;
byte tbuf[TM_DATETIME_BUFFER_SIZE];
if (!strfusec(tbuf, TM_DATETIME_BUFFER_SIZE, fmt, t2))
goto err;
if (!strftime(x, TM_DATETIME_BUFFER_SIZE, tbuf, &tm))
goto err;
return;
err:
strcpy(x, "<error>");
}
......@@ -105,4 +105,23 @@ void timers_fire(struct timeloop *loop);
void timer_init(void);
struct timeformat {
char *fmt1, *fmt2;
btime limit;
};
#define TM_ISO_SHORT_S (struct timeformat){"%T", "%F", (s64) (20*3600) S_}
#define TM_ISO_SHORT_MS (struct timeformat){"%T.%3f", "%F", (s64) (20*3600) S_}
#define TM_ISO_SHORT_US (struct timeformat){"%T.%6f", "%F", (s64) (20*3600) S_}
#define TM_ISO_LONG_S (struct timeformat){"%F %T", NULL, 0}
#define TM_ISO_LONG_MS (struct timeformat){"%F %T.%3f", NULL, 0}
#define TM_ISO_LONG_US (struct timeformat){"%F %T.%6f", NULL, 0}
#define TM_DATETIME_BUFFER_SIZE 32 /* Buffer size required by tm_format_time() */
btime tm_parse_time(char *x);
void tm_format_time(char *x, struct timeformat *fmt, btime t);
void tm_format_real_time(char *x, const char *fmt, btime t);
#endif
......@@ -25,12 +25,12 @@ cmd_show_status(void)
byte tim[TM_DATETIME_BUFFER_SIZE];
cli_msg(-1000, "BIRD " BIRD_VERSION);
tm_format_datetime(tim, &config->tf_base, now);
tm_format_time(tim, &config->tf_base, current_time());
cli_msg(-1011, "Router ID is %R", config->router_id);
cli_msg(-1011, "Current server time is %s", tim);
tm_format_datetime(tim, &config->tf_base, boot_time TO_S);
tm_format_time(tim, &config->tf_base, boot_time);
cli_msg(-1011, "Last reboot on %s", tim);
tm_format_datetime(tim, &config->tf_base, config->load_time);
tm_format_time(tim, &config->tf_base, config->load_time);
cli_msg(-1011, "Last reconfiguration on %s", tim);
graceful_restart_show_status();
......
......@@ -73,6 +73,7 @@ CF_KEYWORDS(ALGORITHM, KEYED, HMAC, MD5, SHA1, SHA256, SHA384, SHA512)
CF_KEYWORDS(PRIMARY, STATS, COUNT, BY, FOR, COMMANDS, PREEXPORT, NOEXPORT, GENERATE)
CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION, SORTED)
CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC, CLASS, DSCP)
CF_KEYWORDS(TIMEFORMAT, ISO, SHORT, LONG, ROUTE, PROTOCOL, BASE, LOG, S, MS, US)
CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS)
/* For r_args_channel */
......@@ -295,6 +296,7 @@ limit_spec:
| OFF { $$ = (struct channel_limit){}; }
;
CF_ADDTO(conf, debug_default)
debug_default:
......@@ -304,6 +306,31 @@ debug_default:
/* MRTDUMP PROTOCOLS is in systep/unix/config.Y */
CF_ADDTO(conf, timeformat_base)
timeformat_which:
ROUTE { $$ = &new_config->tf_route; }
| PROTOCOL { $$ = &new_config->tf_proto; }
| BASE { $$ = &new_config->tf_base; }
| LOG { $$ = &new_config->tf_log; }
;
timeformat_spec:
timeformat_which TEXT { *$1 = (struct timeformat){$2, NULL, 0}; }
| timeformat_which TEXT expr TEXT { *$1 = (struct timeformat){$2, $4, (s64) $3 S_}; }
| timeformat_which ISO SHORT { *$1 = TM_ISO_SHORT_S; }
| timeformat_which ISO SHORT MS { *$1 = TM_ISO_SHORT_MS; }
| timeformat_which ISO SHORT US { *$1 = TM_ISO_SHORT_US; }
| timeformat_which ISO LONG { *$1 = TM_ISO_LONG_S; }
| timeformat_which ISO LONG MS { *$1 = TM_ISO_LONG_MS; }
| timeformat_which ISO LONG US { *$1 = TM_ISO_LONG_US; }
;
timeformat_base:
TIMEFORMAT timeformat_spec ';'
;
/* Interface patterns */
iface_patt_node_init:
......@@ -462,12 +489,12 @@ password_item_begin:
password_item_params:
/* empty */ { }
| GENERATE FROM datetime ';' password_item_params { this_p_item->genfrom = $3; }
| GENERATE TO datetime ';' password_item_params { this_p_item->gento = $3; }
| ACCEPT FROM datetime ';' password_item_params { this_p_item->accfrom = $3; }
| ACCEPT TO datetime ';' password_item_params { this_p_item->accto = $3; }
| FROM datetime ';' password_item_params { this_p_item->genfrom = this_p_item->accfrom = $2; }
| TO datetime ';' password_item_params { this_p_item->gento = this_p_item->accto = $2; }
| GENERATE FROM time ';' password_item_params { this_p_item->genfrom = $3; }
| GENERATE TO time ';' password_item_params { this_p_item->gento = $3; }
| ACCEPT FROM time ';' password_item_params { this_p_item->accfrom = $3; }
| ACCEPT TO time ';' password_item_params { this_p_item->accto = $3; }
| FROM time ';' password_item_params { this_p_item->genfrom = this_p_item->accfrom = $2; }
| TO time ';' password_item_params { this_p_item->gento = this_p_item->accto = $2; }
| ID expr ';' password_item_params { this_p_item->id = $2; if ($2 <= 0) cf_error("Password ID has to be greated than zero."); }
| ALGORITHM password_algorithm ';' password_item_params { this_p_item->alg = $2; }
;
......
......@@ -19,12 +19,13 @@ password_find(list *l, int first_fit)
{
struct password_item *pi;
struct password_item *pf = NULL;
btime now_ = current_real_time();
if (l)
{
WALK_LIST(pi, *l)
{
if ((pi->genfrom < now_real) && (pi->gento > now_real))
if ((pi->genfrom < now_) && (pi->gento > now_))
{
if (first_fit)
return pi;
......@@ -41,12 +42,13 @@ struct password_item *
password_find_by_id(list *l, uint id)
{
struct password_item *pi;
btime now_ = current_real_time();
if (!l)
return NULL;
WALK_LIST(pi, *l)
if ((pi->id == id) && (pi->accfrom <= now_real) && (now_real < pi->accto))
if ((pi->id == id) && (pi->accfrom <= now_) && (now_ < pi->accto))
return pi;
return NULL;
......@@ -56,12 +58,13 @@ struct password_item *
password_find_by_value(list *l, char *pass, uint size)
{
struct password_item *pi;
btime now_ = current_real_time();
if (!l)
return NULL;
WALK_LIST(pi, *l)
if (password_verify(pi, pass, size) && (pi->accfrom <= now_real) && (now_real < pi->accto))
if (password_verify(pi, pass, size) && (pi->accfrom <= now_) && (now_ < pi->accto))
return pi;
return NULL;
......
......@@ -10,15 +10,13 @@
#ifndef PASSWORD_H
#define PASSWORD_H
#include "sysdep/unix/timer.h"
struct password_item {
node n;
char *password; /* Key data, null terminated */
uint length; /* Key length, without null */
uint id; /* Key ID */
uint alg; /* MAC algorithm */
bird_clock_t accfrom, accto, genfrom, gento;
btime accfrom, accto, genfrom, gento;
};
extern struct password_item *last_password_item;
......
......@@ -162,7 +162,7 @@ proto_add_channel(struct proto *p, struct channel_config *cf)
c->channel_state = CS_DOWN;
c->export_state = ES_DOWN;
c->last_state_change = now;
c->last_state_change = current_time();
c->reloadable = 1;
CALL(c->channel->init, c, cf);
......@@ -341,7 +341,7 @@ channel_set_state(struct channel *c, uint state)
return;
c->channel_state = state;
c->last_state_change = now;
c->last_state_change = current_time();
switch (state)
{
......@@ -672,7 +672,7 @@ proto_init(struct proto_config *c, node *n)
struct proto *p = pr->init(c);
p->proto_state = PS_DOWN;
p->last_state_change = now;
p->last_state_change = current_time();
insert_node(&p->n, n);
p->event = ev_new(proto_pool);
......@@ -1500,7 +1500,7 @@ proto_notify_state(struct proto *p, uint state)
return;
p->proto_state = state;
p->last_state_change = now;
p->last_state_change = current_time();
switch (state)
{
......@@ -1631,7 +1631,7 @@ proto_cmd_show(struct proto *p, uint verbose, int cnt)
buf[0] = 0;
if (p->proto->get_status)
p->proto->get_status(p, buf);
tm_format_datetime(tbuf, &config->tf_proto, p->last_state_change);
tm_format_time(tbuf, &config->tf_proto, p->last_state_change);
cli_msg(-1002, "%-8s %-8s %-8s %-5s %-10s %s",
p->name,
p->proto->name,
......
......@@ -159,7 +159,7 @@ struct proto {
byte down_sched; /* Shutdown is scheduled for later (PDS_*) */
byte down_code; /* Reason for shutdown (PDC_* codes) */
u32 hash_key; /* Random key used for hashing of neighbors */
bird_clock_t last_state_change; /* Time of last state transition */
btime last_state_change; /* Time of last state transition */
char *last_state_name_announced; /* Last state name we've announced to the user */
/*
......@@ -508,7 +508,7 @@ struct channel {
u8 gr_lock; /* Graceful restart mechanism should wait for this channel */
u8 gr_wait; /* Route export to channel is postponed until graceful restart */
bird_clock_t last_state_change; /* Time of last state transition */
btime last_state_change; /* Time of last state transition */
};
......
......@@ -159,8 +159,8 @@ typedef struct rtable {
* obstacle from this routing table.
*/
struct event *rt_event; /* Routing table event */
btime gc_time; /* Time of last GC */
int gc_counter; /* Number of operations since last GC */
bird_clock_t gc_time; /* Time of last GC */
byte prune_state; /* Table prune state, 1 -> scheduled, 2-> running */
byte hcu_scheduled; /* Hostcache update is scheduled */
byte nhu_state; /* Next Hop Update state */
......@@ -213,7 +213,7 @@ typedef struct rte {
byte flags; /* Flags (REF_...) */
byte pflags; /* Protocol-specific flags */
word pref; /* Route preference */
bird_clock_t lastmod; /* Last modified */
btime lastmod; /* Last modified */
union { /* Protocol-dependent data (metrics etc.) */
#ifdef CONFIG_RIP
struct {
......
......@@ -39,7 +39,7 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tm
void (*get_route_info)(struct rte *, byte *buf, struct ea_list *attrs);
struct nexthop *nh;
tm_format_datetime(tm, &config->tf_route, e->lastmod);
tm_format_time(tm, &config->tf_route, e->lastmod);
if (ipa_nonzero(a->from) && !ipa_equal(a->from, a->nh.gw))
bsprintf(from, " from %I", a->from);
else
......
......@@ -1173,7 +1173,7 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
}
if (new)
new->lastmod = now;
new->lastmod = current_time();
/* Log the route change */
if (p->debug & D_ROUTES)
......@@ -1201,7 +1201,7 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
if (!net->routes &&
(table->gc_counter++ >= table->config->gc_max_ops) &&
(table->gc_time + table->config->gc_min_time <= now))
(table->gc_time + table->config->gc_min_time <= current_time()))
rt_schedule_prune(table);
if (old_ok && p->rte_remove)
......@@ -1497,7 +1497,7 @@ rte_dump(rte *e)
{
net *n = e->net;
debug("%-1N ", n->n.addr);
debug("KF=%02x PF=%02x pref=%d lm=%d ", n->n.flags, e->pflags, e->pref, now-e->lastmod);
debug("KF=%02x PF=%02x pref=%d ", n->n.flags, e->pflags, e->pref);
rta_dump(e->attrs);
if (e->attrs->src->proto->proto->dump_attrs)
e->attrs->src->proto->proto->dump_attrs(e);
......@@ -1609,7 +1609,7 @@ rt_setup(pool *p, rtable *t, char *name, struct rtable_config *cf)
t->rt_event = ev_new(p);
t->rt_event->hook = rt_event;
t->rt_event->data = t;
t->gc_time = now;
t->gc_time = current_time();
}
}
......@@ -1708,7 +1708,7 @@ again:
#endif
tab->gc_counter = 0;
tab->gc_time = now;
tab->gc_time = current_time();
/* state change 2->0, 3->1 */
tab->prune_state &= 1;
......
......@@ -145,6 +145,7 @@ bfd_session_update_state(struct bfd_session *s, uint state, uint diag)
bfd_lock_sessions(p);
s->loc_state = state;
s->loc_diag = diag;
s->last_state_change = current_time();
notify = !NODE_VALID(&s->n);
if (notify)
......@@ -438,7 +439,7 @@ bfd_add_session(struct bfd_proto *p, ip_addr addr, ip_addr local, struct iface *
bfd_session_control_tx_timer(s, 1);
init_list(&s->request_list);
s->last_state_change = now;
s->last_state_change = current_time();
TRACE(D_EVENTS, "Session to %I added", s->addr);
......@@ -879,9 +880,6 @@ bfd_notify_hook(sock *sk, uint len UNUSED)
diag = s->loc_diag;
bfd_unlock_sessions(p);
/* FIXME: convert to btime and move to bfd_session_update_state() */
s->last_state_change = now;
s->notify_running = 1;
WALK_LIST_DELSAFE(n, nn, s->request_list)
bfd_request_notify(SKIP_BACK(struct bfd_request, n, n), state, diag);
......@@ -1105,7 +1103,7 @@ bfd_show_sessions(struct proto *P)
timeout = (MAX(s->req_min_rx_int, s->rem_min_tx_int) TO_MS) * s->rem_detect_mult;
state = (state < 4) ? state : 0;
tm_format_datetime(tbuf, &config->tf_proto, s->last_state_change);
tm_format_time(tbuf, &config->tf_proto, s->last_state_change);
cli_msg(-1020, "%-25I %-10s %-10s %-10s %3u.%03u %3u.%03u",
s->addr, ifname, bfd_state_names[state], tbuf,
......
......@@ -144,7 +144,7 @@ struct bfd_session
timer2 *hold_timer; /* Timer for session down detection time */
list request_list; /* List of client requests (struct bfd_request) */
bird_clock_t last_state_change; /* Time of last state change */
btime last_state_change; /* Time of last state change */
u8 notify_running; /* 1 if notify hooks are running */
u8 rx_csn_known; /* Received crypto sequence number is known */
......
......@@ -14,8 +14,7 @@ CF_HDR
CF_DECLS
CF_KEYWORDS(LOG, SYSLOG, ALL, DEBUG, TRACE, INFO, REMOTE, WARNING, ERROR, AUTH, FATAL, BUG, STDERR, SOFT)
CF_KEYWORDS(TIMEFORMAT, ISO, OLD, SHORT, LONG, BASE, NAME, CONFIRM, UNDO, CHECK, TIMEOUT)
CF_KEYWORDS(DEBUG, LATENCY, LIMIT, WATCHDOG, WARNING, TIMEOUT)
CF_KEYWORDS(NAME, CONFIRM, UNDO, CHECK, TIMEOUT, DEBUG, LATENCY, LIMIT, WATCHDOG, WARNING)
%type <i> log_mask log_mask_list log_cat cfg_timeout
%type <g> log_file
......@@ -85,29 +84,6 @@ mrtdump_base:
;
CF_ADDTO(conf, timeformat_base)
timeformat_which:
ROUTE { $$ = &new_config->tf_route; }
| PROTOCOL { $$ = &new_config->tf_proto; }
| BASE { $$ = &new_config->tf_base; }
| LOG { $$ = &new_config->tf_log; }
;
timeformat_spec:
timeformat_which TEXT { *$1 = (struct timeformat){$2, NULL, 0}; }
| timeformat_which TEXT expr TEXT { *$1 = (struct timeformat){$2, $4, $3}; }
| timeformat_which ISO SHORT { *$1 = (struct timeformat){"%T", "%F", 20*3600}; }
| timeformat_which ISO LONG { *$1 = (struct timeformat){"%F %T", NULL, 0}; }
| timeformat_which OLD SHORT { *$1 = (struct timeformat){NULL, NULL, 0}; }
| timeformat_which OLD LONG { *$1 = (struct timeformat){"%d-%m-%Y %T", NULL, 0}; }
;
timeformat_base:
TIMEFORMAT timeformat_spec ';'
;
CF_ADDTO(conf, debug_unix)
debug_unix:
......
......@@ -40,6 +40,7 @@
#include "lib/timer.h"
#include "lib/string.h"
#include "nest/iface.h"
#include "conf/conf.h"
#include "sysdep/unix/unix.h"
#include CONFIG_INCLUDE_SYSIO_H
......@@ -385,102 +386,6 @@ tm_shot(void)
}
#endif
/**
* tm_parse_datetime - parse a date and time
* @x: datetime string
*
* tm_parse_datetime() takes a textual representation of
* a date and time (dd-mm-yyyy hh:mm:ss)
* and converts it to the corresponding value of type &bird_clock_t.
*/
bird_clock_t
tm_parse_datetime(char *x)
{
struct tm tm;
int n;
time_t t;
if (sscanf(x, "%d-%d-%d %d:%d:%d%n", &tm.tm_mday, &tm.tm_mon, &tm.tm_year, &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &n) != 6 || x[n])
return tm_parse_date(x);
tm.tm_mon--;
tm.tm_year -= 1900;
t = mktime(&tm);
if (t == (time_t) -1)
return 0;
return t;
}
/**
* tm_parse_date - parse a date
* @x: date string
*
* tm_parse_date() takes a textual representation of a date (dd-mm-yyyy)
* and converts it to the corresponding value of type &bird_clock_t.
*/
bird_clock_t
tm_parse_date(char *x)
{
struct tm tm;
int n;
time_t t;
if (sscanf(x, "%d-%d-%d%n", &tm.tm_mday, &tm.tm_mon, &tm.tm_year, &n) != 3 || x[n])
return 0;
tm.tm_mon--;
tm.tm_year -= 1900;
tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
t = mktime(&tm);
if (t == (time_t) -1)
return 0;
return t;
}
static void
tm_format_reltime(char *x, struct tm *tm, bird_clock_t delta)
{
static char *month_names[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
if (delta < 20*3600)
bsprintf(x, "%02d:%02d", tm->tm_hour, tm->tm_min);
else if (delta < 360*86400)
bsprintf(x, "%s%02d", month_names[tm->tm_mon], tm->tm_mday);
else
bsprintf(x, "%d", tm->tm_year+1900);
}
#include "conf/conf.h"
/**
* tm_format_datetime - convert date and time to textual representation
* @x: destination buffer of size %TM_DATETIME_BUFFER_SIZE
* @fmt_spec: specification of resulting textual representation of the time
* @t: time
*
* This function formats the given relative time value @t to a textual
* date/time representation (dd-mm-yyyy hh:mm:ss) in real time.
*/
void
tm_format_datetime(char *x, struct timeformat *fmt_spec, bird_clock_t t)
{
const char *fmt_used;
struct tm *tm;
bird_clock_t delta = now - t;
t = now_real - delta;
tm = localtime(&t);
if (fmt_spec->fmt1 == NULL)
return tm_format_reltime(x, tm, delta);
if ((fmt_spec->limit == 0) || (delta < fmt_spec->limit))
fmt_used = fmt_spec->fmt1;
else
fmt_used = fmt_spec->fmt2;
int rv = strftime(x, TM_DATETIME_BUFFER_SIZE, fmt_used, tm);
if (((rv == 0) && fmt_used[0]) || (rv == TM_DATETIME_BUFFER_SIZE))
strcpy(x, "<too-long>");
}