Line data Source code
1 : // SPDX-License-Identifier: ISC
2 : /*
3 : * Copyright (c) 2017 David Lamparter, for NetDEF, Inc.
4 : */
5 :
6 : #ifndef _FRR_MONOTIME_H
7 : #define _FRR_MONOTIME_H
8 :
9 : #include <stdint.h>
10 : #include <time.h>
11 : #include <sys/time.h>
12 :
13 : #ifdef __cplusplus
14 : extern "C" {
15 : #endif
16 :
17 : struct fbuf;
18 : struct printfrr_eargs;
19 :
20 : #ifndef TIMESPEC_TO_TIMEVAL
21 : /* should be in sys/time.h on BSD & Linux libcs */
22 : #define TIMESPEC_TO_TIMEVAL(tv, ts) \
23 : do { \
24 : (tv)->tv_sec = (ts)->tv_sec; \
25 : (tv)->tv_usec = (ts)->tv_nsec / 1000; \
26 : } while (0)
27 : #endif
28 : #ifndef TIMEVAL_TO_TIMESPEC
29 : /* should be in sys/time.h on BSD & Linux libcs */
30 : #define TIMEVAL_TO_TIMESPEC(tv, ts) \
31 : do { \
32 : (ts)->tv_sec = (tv)->tv_sec; \
33 : (ts)->tv_nsec = (tv)->tv_usec * 1000; \
34 : } while (0)
35 : #endif
36 :
37 : /* Linux/glibc is sadly missing these timespec helpers */
38 : #ifndef timespecadd
39 : #define timespecadd(tsp, usp, vsp) \
40 : do { \
41 : (vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec; \
42 : (vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec; \
43 : if ((vsp)->tv_nsec >= 1000000000L) { \
44 : (vsp)->tv_sec++; \
45 : (vsp)->tv_nsec -= 1000000000L; \
46 : } \
47 : } while (0)
48 : #endif
49 :
50 : #ifndef timespecsub
51 : #define timespecsub(tsp, usp, vsp) \
52 : do { \
53 : (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \
54 : (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \
55 : if ((vsp)->tv_nsec < 0) { \
56 : (vsp)->tv_sec--; \
57 : (vsp)->tv_nsec += 1000000000L; \
58 : } \
59 : } while (0)
60 : #endif
61 :
62 15147 : static inline time_t monotime(struct timeval *tvo)
63 : {
64 15147 : struct timespec ts;
65 :
66 15147 : clock_gettime(CLOCK_MONOTONIC, &ts);
67 15134 : if (tvo) {
68 15134 : TIMESPEC_TO_TIMEVAL(tvo, &ts);
69 : }
70 15130 : return ts.tv_sec;
71 : }
72 :
73 : #define ONE_DAY_SECOND (60 * 60 * 24)
74 : #define ONE_WEEK_SECOND (ONE_DAY_SECOND * 7)
75 : #define ONE_YEAR_SECOND (ONE_DAY_SECOND * 365)
76 :
77 : /* the following two return microseconds, not time_t!
78 : *
79 : * also, they're negative forms of each other, but having both makes the
80 : * code more readable
81 : */
82 74 : static inline int64_t monotime_since(const struct timeval *ref,
83 : struct timeval *out)
84 : {
85 74 : struct timeval tv;
86 74 : monotime(&tv);
87 74 : timersub(&tv, ref, &tv);
88 74 : if (out)
89 17 : *out = tv;
90 74 : return (int64_t)tv.tv_sec * 1000000LL + tv.tv_usec;
91 : }
92 :
93 6452 : static inline int64_t monotime_until(const struct timeval *ref,
94 : struct timeval *out)
95 : {
96 6452 : struct timeval tv;
97 6452 : monotime(&tv);
98 6452 : timersub(ref, &tv, &tv);
99 6452 : if (out)
100 6446 : *out = tv;
101 6452 : return (int64_t)tv.tv_sec * 1000000LL + tv.tv_usec;
102 : }
103 :
104 0 : static inline time_t monotime_to_realtime(const struct timeval *mono,
105 : struct timeval *realout)
106 : {
107 0 : struct timeval delta, real;
108 :
109 0 : monotime_since(mono, &delta);
110 0 : gettimeofday(&real, NULL);
111 :
112 0 : timersub(&real, &delta, &real);
113 0 : if (realout)
114 0 : *realout = real;
115 0 : return real.tv_sec;
116 : }
117 :
118 : /* Char buffer size for time-to-string api */
119 : #define MONOTIME_STRLEN 32
120 :
121 0 : static inline char *time_to_string(time_t ts, char *buf)
122 : {
123 0 : struct timeval tv;
124 0 : time_t tbuf;
125 :
126 0 : monotime(&tv);
127 0 : tbuf = time(NULL) - (tv.tv_sec - ts);
128 :
129 0 : return ctime_r(&tbuf, buf);
130 : }
131 :
132 : /* Convert interval to human-friendly string, used in cli output e.g. */
133 0 : static inline const char *frrtime_to_interval(time_t t, char *buf,
134 : size_t buflen)
135 : {
136 0 : struct tm tm;
137 :
138 0 : gmtime_r(&t, &tm);
139 :
140 0 : if (t < ONE_DAY_SECOND)
141 0 : snprintf(buf, buflen, "%02d:%02d:%02d", tm.tm_hour, tm.tm_min,
142 : tm.tm_sec);
143 0 : else if (t < ONE_WEEK_SECOND)
144 0 : snprintf(buf, buflen, "%dd%02dh%02dm", tm.tm_yday, tm.tm_hour,
145 : tm.tm_min);
146 : else
147 0 : snprintf(buf, buflen, "%02dw%dd%02dh", tm.tm_yday / 7,
148 0 : tm.tm_yday - ((tm.tm_yday / 7) * 7), tm.tm_hour);
149 0 : return buf;
150 : }
151 :
152 : enum {
153 : /* n/a - input was seconds precision, don't print any fractional */
154 : TIMEFMT_SECONDS = (1 << 0),
155 : /* caller is directly invoking printfrr_time and has pre-specified
156 : * I/Iu/Is/M/Mu/Ms/R/Ru/Rs (for printing timers)
157 : */
158 : TIMEFMT_PRESELECT = (1 << 1),
159 : /* don't print any output - this is needed for invoking printfrr_time
160 : * from another printfrr extensions to skip over flag characters
161 : */
162 : TIMEFMT_SKIP = (1 << 2),
163 : /* use spaces in appropriate places */
164 : TIMEFMT_SPACE = (1 << 3),
165 :
166 : /* input interpretations: */
167 : TIMEFMT_REALTIME = (1 << 8),
168 : TIMEFMT_MONOTONIC = (1 << 9),
169 : TIMEFMT_SINCE = (1 << 10),
170 : TIMEFMT_UNTIL = (1 << 11),
171 :
172 : TIMEFMT_ABSOLUTE = TIMEFMT_REALTIME | TIMEFMT_MONOTONIC,
173 : TIMEFMT_ANCHORS = TIMEFMT_SINCE | TIMEFMT_UNTIL,
174 :
175 : /* calendaric formats: */
176 : TIMEFMT_ISO8601 = (1 << 16),
177 :
178 : /* interval formats: */
179 : /* 't' - use [t]raditional 3-block format */
180 : TIMEFMT_BASIC = (1 << 24),
181 : /* 'm' - select mm:ss */
182 : TIMEFMT_MMSS = (1 << 25),
183 : /* 'h' - select hh:mm:ss */
184 : TIMEFMT_HHMMSS = (1 << 26),
185 : /* 'd' - print as decimal number of seconds */
186 : TIMEFMT_DECIMAL = (1 << 27),
187 : /* 'mx'/'hx' - replace zero value with "--:--" or "--:--:--" */
188 : TIMEFMT_DASHES = (1 << 31),
189 :
190 : /* helpers for reference */
191 : TIMEFMT_TIMER_DEADLINE =
192 : TIMEFMT_PRESELECT | TIMEFMT_MONOTONIC | TIMEFMT_UNTIL,
193 : TIMEFMT_TIMER_INTERVAL = TIMEFMT_PRESELECT,
194 : };
195 :
196 : extern ssize_t printfrr_time(struct fbuf *buf, struct printfrr_eargs *ea,
197 : const struct timespec *ts, unsigned int flags);
198 :
199 : #ifdef __cplusplus
200 : }
201 : #endif
202 :
203 : #endif /* _FRR_MONOTIME_H */
|