summaryrefslogtreecommitdiffstats
path: root/lib/monotime.h
blob: f7ae1bbbe1a95a92cf0b07e0e1799e6925ee039c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
// SPDX-License-Identifier: ISC
/*
 * Copyright (c) 2017  David Lamparter, for NetDEF, Inc.
 */

#ifndef _FRR_MONOTIME_H
#define _FRR_MONOTIME_H

#include <stdint.h>
#include <time.h>
#include <sys/time.h>

#ifdef __cplusplus
extern "C" {
#endif

struct fbuf;
struct printfrr_eargs;

#ifndef TIMESPEC_TO_TIMEVAL
/* should be in sys/time.h on BSD & Linux libcs */
#define TIMESPEC_TO_TIMEVAL(tv, ts)                                            \
	do {                                                                   \
		(tv)->tv_sec = (ts)->tv_sec;                                   \
		(tv)->tv_usec = (ts)->tv_nsec / 1000;                          \
	} while (0)
#endif
#ifndef TIMEVAL_TO_TIMESPEC
/* should be in sys/time.h on BSD & Linux libcs */
#define TIMEVAL_TO_TIMESPEC(tv, ts)                                            \
	do {                                                                   \
		(ts)->tv_sec = (tv)->tv_sec;                                   \
		(ts)->tv_nsec = (tv)->tv_usec * 1000;                          \
	} while (0)
#endif

/* Linux/glibc is sadly missing these timespec helpers */
#ifndef timespecadd
#define timespecadd(tsp, usp, vsp)                                             \
	do {                                                                   \
		(vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec;                 \
		(vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec;              \
		if ((vsp)->tv_nsec >= 1000000000L) {                           \
			(vsp)->tv_sec++;                                       \
			(vsp)->tv_nsec -= 1000000000L;                         \
		}                                                              \
	} while (0)
#endif

#ifndef timespecsub
#define timespecsub(tsp, usp, vsp)                                             \
	do {                                                                   \
		(vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec;                 \
		(vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec;              \
		if ((vsp)->tv_nsec < 0) {                                      \
			(vsp)->tv_sec--;                                       \
			(vsp)->tv_nsec += 1000000000L;                         \
		}                                                              \
	} while (0)
#endif

static inline time_t monotime(struct timeval *tvo)
{
	struct timespec ts;

	clock_gettime(CLOCK_MONOTONIC, &ts);
	if (tvo) {
		TIMESPEC_TO_TIMEVAL(tvo, &ts);
	}
	return ts.tv_sec;
}

#define ONE_DAY_SECOND (60 * 60 * 24)
#define ONE_WEEK_SECOND (ONE_DAY_SECOND * 7)
#define ONE_YEAR_SECOND (ONE_DAY_SECOND * 365)

/* the following two return microseconds, not time_t!
 *
 * also, they're negative forms of each other, but having both makes the
 * code more readable
 */
static inline int64_t monotime_since(const struct timeval *ref,
				     struct timeval *out)
{
	struct timeval tv;
	monotime(&tv);
	timersub(&tv, ref, &tv);
	if (out)
		*out = tv;
	return (int64_t)tv.tv_sec * 1000000LL + tv.tv_usec;
}

static inline int64_t monotime_until(const struct timeval *ref,
				     struct timeval *out)
{
	struct timeval tv;
	monotime(&tv);
	timersub(ref, &tv, &tv);
	if (out)
		*out = tv;
	return (int64_t)tv.tv_sec * 1000000LL + tv.tv_usec;
}

static inline time_t monotime_to_realtime(const struct timeval *mono,
					  struct timeval *realout)
{
	struct timeval delta, real;

	monotime_since(mono, &delta);
	gettimeofday(&real, NULL);

	timersub(&real, &delta, &real);
	if (realout)
		*realout = real;
	return real.tv_sec;
}

/* Char buffer size for time-to-string api */
#define MONOTIME_STRLEN 32

static inline char *time_to_string(time_t ts, char *buf)
{
	struct timeval tv;
	time_t tbuf;

	monotime(&tv);
	tbuf = time(NULL) - (tv.tv_sec - ts);

	return ctime_r(&tbuf, buf);
}

/* Convert interval to human-friendly string, used in cli output e.g. */
static inline const char *frrtime_to_interval(time_t t, char *buf,
					      size_t buflen)
{
	struct tm tm;

	gmtime_r(&t, &tm);

	if (t < ONE_DAY_SECOND)
		snprintf(buf, buflen, "%02d:%02d:%02d", tm.tm_hour, tm.tm_min,
			 tm.tm_sec);
	else if (t < ONE_WEEK_SECOND)
		snprintf(buf, buflen, "%dd%02dh%02dm", tm.tm_yday, tm.tm_hour,
			 tm.tm_min);
	else
		snprintf(buf, buflen, "%02dw%dd%02dh", tm.tm_yday / 7,
			 tm.tm_yday - ((tm.tm_yday / 7) * 7), tm.tm_hour);
	return buf;
}

enum {
	/* n/a - input was seconds precision, don't print any fractional */
	TIMEFMT_SECONDS = (1 << 0),
	/* caller is directly invoking printfrr_time and has pre-specified
	 * I/Iu/Is/M/Mu/Ms/R/Ru/Rs (for printing timers)
	 */
	TIMEFMT_PRESELECT = (1 << 1),
	/* don't print any output - this is needed for invoking printfrr_time
	 * from another printfrr extensions to skip over flag characters
	 */
	TIMEFMT_SKIP = (1 << 2),
	/* use spaces in appropriate places */
	TIMEFMT_SPACE = (1 << 3),

	/* input interpretations: */
	TIMEFMT_REALTIME = (1 << 8),
	TIMEFMT_MONOTONIC = (1 << 9),
	TIMEFMT_SINCE = (1 << 10),
	TIMEFMT_UNTIL = (1 << 11),

	TIMEFMT_ABSOLUTE = TIMEFMT_REALTIME | TIMEFMT_MONOTONIC,
	TIMEFMT_ANCHORS = TIMEFMT_SINCE | TIMEFMT_UNTIL,

	/* calendaric formats: */
	TIMEFMT_ISO8601 = (1 << 16),

	/* interval formats: */
	/* 't' - use [t]raditional 3-block format */
	TIMEFMT_BASIC = (1 << 24),
	/* 'm' - select mm:ss */
	TIMEFMT_MMSS = (1 << 25),
	/* 'h' - select hh:mm:ss */
	TIMEFMT_HHMMSS = (1 << 26),
	/* 'd' - print as decimal number of seconds */
	TIMEFMT_DECIMAL = (1 << 27),
	/* 'mx'/'hx' - replace zero value with "--:--" or "--:--:--" */
	TIMEFMT_DASHES = (1 << 31),

	/* helpers for reference */
	TIMEFMT_TIMER_DEADLINE =
		TIMEFMT_PRESELECT | TIMEFMT_MONOTONIC | TIMEFMT_UNTIL,
	TIMEFMT_TIMER_INTERVAL = TIMEFMT_PRESELECT,
};

extern ssize_t printfrr_time(struct fbuf *buf, struct printfrr_eargs *ea,
			     const struct timespec *ts, unsigned int flags);

#ifdef __cplusplus
}
#endif

#endif /* _FRR_MONOTIME_H */