]>
Commit | Line | Data |
---|---|---|
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 | static inline time_t monotime(struct timeval *tvo) | |
63 | { | |
64 | struct timespec ts; | |
65 | ||
66 | clock_gettime(CLOCK_MONOTONIC, &ts); | |
67 | if (tvo) { | |
68 | TIMESPEC_TO_TIMEVAL(tvo, &ts); | |
69 | } | |
70 | 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 | static inline int64_t monotime_since(const struct timeval *ref, | |
83 | struct timeval *out) | |
84 | { | |
85 | struct timeval tv; | |
86 | monotime(&tv); | |
87 | timersub(&tv, ref, &tv); | |
88 | if (out) | |
89 | *out = tv; | |
90 | return (int64_t)tv.tv_sec * 1000000LL + tv.tv_usec; | |
91 | } | |
92 | ||
93 | static inline int64_t monotime_until(const struct timeval *ref, | |
94 | struct timeval *out) | |
95 | { | |
96 | struct timeval tv; | |
97 | monotime(&tv); | |
98 | timersub(ref, &tv, &tv); | |
99 | if (out) | |
100 | *out = tv; | |
101 | return (int64_t)tv.tv_sec * 1000000LL + tv.tv_usec; | |
102 | } | |
103 | ||
104 | static inline time_t monotime_to_realtime(const struct timeval *mono, | |
105 | struct timeval *realout) | |
106 | { | |
107 | struct timeval delta, real; | |
108 | ||
109 | monotime_since(mono, &delta); | |
110 | gettimeofday(&real, NULL); | |
111 | ||
112 | timersub(&real, &delta, &real); | |
113 | if (realout) | |
114 | *realout = real; | |
115 | return real.tv_sec; | |
116 | } | |
117 | ||
118 | /* Char buffer size for time-to-string api */ | |
119 | #define MONOTIME_STRLEN 32 | |
120 | ||
121 | static inline char *time_to_string(time_t ts, char *buf) | |
122 | { | |
123 | struct timeval tv; | |
124 | time_t tbuf; | |
125 | ||
126 | monotime(&tv); | |
127 | tbuf = time(NULL) - (tv.tv_sec - ts); | |
128 | ||
129 | return ctime_r(&tbuf, buf); | |
130 | } | |
131 | ||
132 | /* Convert interval to human-friendly string, used in cli output e.g. */ | |
133 | static inline const char *frrtime_to_interval(time_t t, char *buf, | |
134 | size_t buflen) | |
135 | { | |
136 | struct tm tm; | |
137 | ||
138 | gmtime_r(&t, &tm); | |
139 | ||
140 | if (t < ONE_DAY_SECOND) | |
141 | snprintf(buf, buflen, "%02d:%02d:%02d", tm.tm_hour, tm.tm_min, | |
142 | tm.tm_sec); | |
143 | else if (t < ONE_WEEK_SECOND) | |
144 | snprintf(buf, buflen, "%dd%02dh%02dm", tm.tm_yday, tm.tm_hour, | |
145 | tm.tm_min); | |
146 | else | |
147 | snprintf(buf, buflen, "%02dw%dd%02dh", tm.tm_yday / 7, | |
148 | tm.tm_yday - ((tm.tm_yday / 7) * 7), tm.tm_hour); | |
149 | 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 */ |