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