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 "misc.h"
31#include "fdmisc.h"
32
33#include "memdbg.h"
34
35/*
36 * printf-style interface for outputting status info
37 */
38
39static const char *
40print_status_mode(unsigned int flags)
41{
42 switch (flags)
43 {
45 return "WRITE";
46
48 return "READ";
49
51 return "READ/WRITE";
52
53 default:
54 return "UNDEF";
55 }
56}
57
58struct status_output *
59status_open(const char *filename, const int refresh_freq, const int msglevel,
60 const struct virtual_output *vout, const unsigned int flags)
61{
62 struct status_output *so = NULL;
63 if (filename || msglevel >= 0 || vout)
64 {
66 so->flags = flags;
67 so->msglevel = msglevel;
68 so->vout = vout;
69 so->fd = -1;
70 buf_reset(&so->read_buf);
72 if (filename)
73 {
74 switch (so->flags)
75 {
77 so->fd =
78 platform_open(filename, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR);
79 break;
80
82 so->fd = platform_open(filename, O_RDONLY, S_IRUSR | S_IWUSR);
83 break;
84
86 so->fd = platform_open(filename, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
87 break;
88
89 default:
90 ASSERT(0);
91 }
92 if (so->fd >= 0)
93 {
94 so->filename = string_alloc(filename, NULL);
95 set_cloexec(so->fd);
96
97 /* allocate read buffer */
98 if (so->flags & STATUS_OUTPUT_READ)
99 {
100 so->read_buf = alloc_buf(512);
101 }
102 }
103 else
104 {
105 msg(M_WARN, "Note: cannot open %s for %s", filename, print_status_mode(so->flags));
106 so->errors = true;
107 }
108 }
109 else
110 {
112 }
113
114 if ((so->flags & STATUS_OUTPUT_WRITE) && refresh_freq > 0)
115 {
116 event_timeout_init(&so->et, refresh_freq, 0);
117 }
118 }
119 return so;
120}
121
122bool
124{
125 if (so)
126 {
127 struct timeval null;
128 CLEAR(null);
129 return event_timeout_trigger(&so->et, &null, ETT_DEFAULT);
130 }
131 else
132 {
133 return false;
134 }
135}
136
137void
139{
140 if (so && so->fd >= 0)
141 {
142 lseek(so->fd, (off_t)0, SEEK_SET);
143 }
144}
145
146void
148{
149 if (so && so->fd >= 0 && (so->flags & STATUS_OUTPUT_WRITE))
150 {
151#if defined(HAVE_FTRUNCATE)
152 {
153 const off_t off = lseek(so->fd, (off_t)0, SEEK_CUR);
154 if (ftruncate(so->fd, off) != 0)
155 {
156 msg(M_WARN | M_ERRNO, "Failed to truncate status file");
157 }
158 }
159#elif defined(HAVE_CHSIZE)
160 {
161 const long off = (long)lseek(so->fd, (off_t)0, SEEK_CUR);
162 chsize(so->fd, off);
163 }
164#else /* if defined(HAVE_FTRUNCATE) */
165#warning both ftruncate and chsize functions appear to be missing from this OS
166#endif
167
168 /* clear read buffer */
169 if (buf_defined(&so->read_buf))
170 {
171 ASSERT(buf_init(&so->read_buf, 0));
172 }
173 }
174}
175
176/* return false if error occurred */
177bool
179{
180 bool ret = true;
181 if (so)
182 {
183 if (so->errors)
184 {
185 ret = false;
186 }
187 if (so->fd >= 0)
188 {
189 if (close(so->fd) < 0)
190 {
191 ret = false;
192 }
193 }
194 free(so->filename);
195
196 if (buf_defined(&so->read_buf))
197 {
198 free_buf(&so->read_buf);
199 }
200 free(so);
201 }
202 else
203 {
204 ret = false;
205 }
206 return ret;
207}
208
209#if defined(__GNUC__) || defined(__clang__)
210#pragma GCC diagnostic push
211#pragma GCC diagnostic ignored "-Wconversion"
212#endif
213
214#define STATUS_PRINTF_MAXLEN 512
215
216void
217status_printf(struct status_output *so, const char *format, ...)
218{
219 if (so && (so->flags & STATUS_OUTPUT_WRITE))
220 {
221 char buf[STATUS_PRINTF_MAXLEN + 2]; /* leave extra bytes for CR, LF */
222 va_list arglist;
223 int stat;
224
225 va_start(arglist, format);
226 stat = vsnprintf(buf, STATUS_PRINTF_MAXLEN, format, arglist);
227 va_end(arglist);
228 buf[STATUS_PRINTF_MAXLEN - 1] = 0;
229
230 if (stat < 0 || stat >= STATUS_PRINTF_MAXLEN)
231 {
232 so->errors = true;
233 }
234
235 if (so->msglevel >= 0 && !so->errors)
236 {
237 msg((msglvl_t)so->msglevel, "%s", buf);
238 }
239
240 if (so->fd >= 0 && !so->errors)
241 {
242 strcat(buf, "\n");
243 size_t len = strlen(buf);
244 if (len > 0)
245 {
246 if (write(so->fd, buf, len) != len)
247 {
248 so->errors = true;
249 }
250 }
251 }
252
253 if (so->vout && !so->errors)
254 {
255 chomp(buf);
256 (*so->vout->func)(so->vout->arg, so->vout->flags_default, buf);
257 }
258 }
259}
260
261bool
262status_read(struct status_output *so, struct buffer *buf)
263{
264 bool ret = false;
265
266 if (so && so->fd >= 0 && (so->flags & STATUS_OUTPUT_READ))
267 {
269 ASSERT(buf_defined(buf));
270 while (true)
271 {
272 const int c = buf_read_u8(&so->read_buf);
273
274 /* read more of file into buffer */
275 if (c == -1)
276 {
277 int len;
278
279 ASSERT(buf_init(&so->read_buf, 0));
280 len = read(so->fd, BPTR(&so->read_buf), BCAP(&so->read_buf));
281 if (len <= 0)
282 {
283 break;
284 }
285
286 ASSERT(buf_inc_len(&so->read_buf, len));
287 continue;
288 }
289
290 ret = true;
291
292 if (c == '\r')
293 {
294 continue;
295 }
296
297 if (c == '\n')
298 {
299 break;
300 }
301
302 buf_write_u8(buf, c);
303 }
304
306 }
307
308 return ret;
309}
310
311#if defined(__GNUC__) || defined(__clang__)
312#pragma GCC diagnostic pop
313#endif
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:786
#define BCAP(buf)
Definition buffer.h:129
#define ALLOC_OBJ_CLEAR(dptr, type)
Definition buffer.h:1052
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:47
#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:152
unsigned int msglvl_t
Definition error.h:77
#define ASSERT(x)
Definition error.h:219
#define M_WARN
Definition error.h:92
#define M_ERRNO
Definition error.h:95
int platform_open(const char *path, int flags, int mode)
Definition platform.c:513
#define STATUS_PRINTF_MAXLEN
Definition status.c:214
bool status_trigger(struct status_output *so)
Definition status.c:123
static const char * print_status_mode(unsigned int flags)
Definition status.c:40
void status_printf(struct status_output *so, const char *format,...)
Definition status.c:217
bool status_read(struct status_output *so, struct buffer *buf)
Definition status.c:262
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:59
void status_flush(struct status_output *so)
Definition status.c:147
void status_reset(struct status_output *so)
Definition status.c:138
bool status_close(struct status_output *so)
Definition status.c:178
#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:60
const struct virtual_output * vout
Definition status.h:58
bool errors
Definition status.h:64
struct event_timeout et
Definition status.h:62
int msglevel
Definition status.h:57
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