OpenVPN
status.c
Go to the documentation of this file.
1/*
2 * OpenVPN -- An application to securely tunnel IP networks
3 * over a single TCP/UDP port, with support for SSL/TLS-based
4 * session authentication and key exchange,
5 * packet encryption, packet authentication, and
6 * packet compression.
7 *
8 * Copyright (C) 2002-2025 OpenVPN Inc <sales@openvpn.net>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, see <https://www.gnu.org/licenses/>.
21 */
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#include "syshead.h"
28
29#include "status.h"
30#include "perf.h"
31#include "misc.h"
32#include "fdmisc.h"
33
34#include "memdbg.h"
35
36/*
37 * printf-style interface for outputting status info
38 */
39
40static const char *
41print_status_mode(unsigned int flags)
42{
43 switch (flags)
44 {
46 return "WRITE";
47
49 return "READ";
50
52 return "READ/WRITE";
53
54 default:
55 return "UNDEF";
56 }
57}
58
59struct status_output *
60status_open(const char *filename, const int refresh_freq, const int msglevel,
61 const struct virtual_output *vout, const unsigned int flags)
62{
63 struct status_output *so = NULL;
64 if (filename || msglevel >= 0 || vout)
65 {
67 so->flags = flags;
68 so->msglevel = msglevel;
69 so->vout = vout;
70 so->fd = -1;
71 buf_reset(&so->read_buf);
73 if (filename)
74 {
75 switch (so->flags)
76 {
78 so->fd =
79 platform_open(filename, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR);
80 break;
81
83 so->fd = platform_open(filename, O_RDONLY, S_IRUSR | S_IWUSR);
84 break;
85
87 so->fd = platform_open(filename, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
88 break;
89
90 default:
91 ASSERT(0);
92 }
93 if (so->fd >= 0)
94 {
95 so->filename = string_alloc(filename, NULL);
96 set_cloexec(so->fd);
97
98 /* allocate read buffer */
99 if (so->flags & STATUS_OUTPUT_READ)
100 {
101 so->read_buf = alloc_buf(512);
102 }
103 }
104 else
105 {
106 msg(M_WARN, "Note: cannot open %s for %s", filename, print_status_mode(so->flags));
107 so->errors = true;
108 }
109 }
110 else
111 {
113 }
114
115 if ((so->flags & STATUS_OUTPUT_WRITE) && refresh_freq > 0)
116 {
117 event_timeout_init(&so->et, refresh_freq, 0);
118 }
119 }
120 return so;
121}
122
123bool
125{
126 if (so)
127 {
128 struct timeval null;
129 CLEAR(null);
130 return event_timeout_trigger(&so->et, &null, ETT_DEFAULT);
131 }
132 else
133 {
134 return false;
135 }
136}
137
138void
140{
141 if (so && so->fd >= 0)
142 {
143 lseek(so->fd, (off_t)0, SEEK_SET);
144 }
145}
146
147void
149{
150 if (so && so->fd >= 0 && (so->flags & STATUS_OUTPUT_WRITE))
151 {
152#if defined(HAVE_FTRUNCATE)
153 {
154 const off_t off = lseek(so->fd, (off_t)0, SEEK_CUR);
155 if (ftruncate(so->fd, off) != 0)
156 {
157 msg(M_WARN | M_ERRNO, "Failed to truncate status file");
158 }
159 }
160#elif defined(HAVE_CHSIZE)
161 {
162 const long off = (long)lseek(so->fd, (off_t)0, SEEK_CUR);
163 chsize(so->fd, off);
164 }
165#else /* if defined(HAVE_FTRUNCATE) */
166#warning both ftruncate and chsize functions appear to be missing from this OS
167#endif
168
169 /* clear read buffer */
170 if (buf_defined(&so->read_buf))
171 {
172 ASSERT(buf_init(&so->read_buf, 0));
173 }
174 }
175}
176
177/* return false if error occurred */
178bool
180{
181 bool ret = true;
182 if (so)
183 {
184 if (so->errors)
185 {
186 ret = false;
187 }
188 if (so->fd >= 0)
189 {
190 if (close(so->fd) < 0)
191 {
192 ret = false;
193 }
194 }
195 free(so->filename);
196
197 if (buf_defined(&so->read_buf))
198 {
199 free_buf(&so->read_buf);
200 }
201 free(so);
202 }
203 else
204 {
205 ret = false;
206 }
207 return ret;
208}
209
210#define STATUS_PRINTF_MAXLEN 512
211
212void
213status_printf(struct status_output *so, const char *format, ...)
214{
215 if (so && (so->flags & STATUS_OUTPUT_WRITE))
216 {
217 char buf[STATUS_PRINTF_MAXLEN + 2]; /* leave extra bytes for CR, LF */
218 va_list arglist;
219 int stat;
220
221 va_start(arglist, format);
222 stat = vsnprintf(buf, STATUS_PRINTF_MAXLEN, format, arglist);
223 va_end(arglist);
224 buf[STATUS_PRINTF_MAXLEN - 1] = 0;
225
226 if (stat < 0 || stat >= STATUS_PRINTF_MAXLEN)
227 {
228 so->errors = true;
229 }
230
231 if (so->msglevel >= 0 && !so->errors)
232 {
233 msg(so->msglevel, "%s", buf);
234 }
235
236 if (so->fd >= 0 && !so->errors)
237 {
238 int len;
239 strcat(buf, "\n");
240 len = strlen(buf);
241 if (len > 0)
242 {
243 if (write(so->fd, buf, len) != len)
244 {
245 so->errors = true;
246 }
247 }
248 }
249
250 if (so->vout && !so->errors)
251 {
252 chomp(buf);
253 (*so->vout->func)(so->vout->arg, so->vout->flags_default, buf);
254 }
255 }
256}
257
258bool
259status_read(struct status_output *so, struct buffer *buf)
260{
261 bool ret = false;
262
263 if (so && so->fd >= 0 && (so->flags & STATUS_OUTPUT_READ))
264 {
266 ASSERT(buf_defined(buf));
267 while (true)
268 {
269 const int c = buf_read_u8(&so->read_buf);
270
271 /* read more of file into buffer */
272 if (c == -1)
273 {
274 int len;
275
276 ASSERT(buf_init(&so->read_buf, 0));
277 len = read(so->fd, BPTR(&so->read_buf), BCAP(&so->read_buf));
278 if (len <= 0)
279 {
280 break;
281 }
282
283 ASSERT(buf_inc_len(&so->read_buf, len));
284 continue;
285 }
286
287 ret = true;
288
289 if (c == '\r')
290 {
291 continue;
292 }
293
294 if (c == '\n')
295 {
296 break;
297 }
298
299 buf_write_u8(buf, c);
300 }
301
303 }
304
305 return ret;
306}
void free_buf(struct buffer *buf)
Definition buffer.c:184
void buf_null_terminate(struct buffer *buf)
Definition buffer.c:533
void chomp(char *str)
Definition buffer.c:614
struct buffer alloc_buf(size_t size)
Definition buffer.c:63
char * string_alloc(const char *str, struct gc_arena *gc)
Definition buffer.c:649
#define BPTR(buf)
Definition buffer.h:123
static bool buf_inc_len(struct buffer *buf, int inc)
Definition buffer.h:588
static void buf_reset(struct buffer *buf)
Definition buffer.h:303
static bool buf_write_u8(struct buffer *dest, uint8_t data)
Definition buffer.h:684
static int buf_read_u8(struct buffer *buf)
Definition buffer.h:774
#define BCAP(buf)
Definition buffer.h:129
#define ALLOC_OBJ_CLEAR(dptr, type)
Definition buffer.h:1042
static bool buf_defined(const struct buffer *buf)
Definition buffer.h:228
#define buf_init(buf, offset)
Definition buffer.h:209
void set_cloexec(socket_descriptor_t fd)
Definition fdmisc.c:78
@ write
@ read
bool event_timeout_trigger(struct event_timeout *et, struct timeval *tv, const int et_const_retry)
This is the principal function for testing and triggering recurring timers.
Definition interval.c:42
#define ETT_DEFAULT
Definition interval.h:222
static void event_timeout_init(struct event_timeout *et, interval_t n, const time_t last)
Initialises a timer struct.
Definition interval.h:172
static void event_timeout_clear(struct event_timeout *et)
Clears the timeout and reset all values to 0.
Definition interval.h:153
#define CLEAR(x)
Definition basic.h:32
#define msg(flags,...)
Definition error.h:150
#define ASSERT(x)
Definition error.h:217
#define M_WARN
Definition error.h:90
#define M_ERRNO
Definition error.h:93
int platform_open(const char *path, int flags, int mode)
Definition platform.c:517
#define STATUS_PRINTF_MAXLEN
Definition status.c:210
bool status_trigger(struct status_output *so)
Definition status.c:124
static const char * print_status_mode(unsigned int flags)
Definition status.c:41
void status_printf(struct status_output *so, const char *format,...)
Definition status.c:213
bool status_read(struct status_output *so, struct buffer *buf)
Definition status.c:259
struct status_output * status_open(const char *filename, const int refresh_freq, const int msglevel, const struct virtual_output *vout, const unsigned int flags)
Definition status.c:60
void status_flush(struct status_output *so)
Definition status.c:148
void status_reset(struct status_output *so)
Definition status.c:139
bool status_close(struct status_output *so)
Definition status.c:179
#define STATUS_OUTPUT_WRITE
Definition status.h:51
#define STATUS_OUTPUT_READ
Definition status.h:50
Wrapper structure for dynamically allocated memory.
Definition buffer.h:60
struct buffer read_buf
Definition status.h:59
const struct virtual_output * vout
Definition status.h:57
bool errors
Definition status.h:63
struct event_timeout et
Definition status.h:61
int msglevel
Definition status.h:56
unsigned int flags
Definition status.h:52
char * filename
Definition status.h:54
unsigned int flags_default
Definition status.h:34
void * arg
Definition status.h:33
void(* func)(void *arg, const unsigned int flags, const char *str)
Definition status.h:35