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-2026 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#define STATUS_PRINTF_MAXLEN 512
210
211void
212status_printf(struct status_output *so, const char *format, ...)
213{
214 if (so && (so->flags & STATUS_OUTPUT_WRITE))
215 {
216 char buf[STATUS_PRINTF_MAXLEN + 2]; /* leave extra bytes for CR, LF */
217 va_list arglist;
218 int stat;
219
220 va_start(arglist, format);
221 stat = vsnprintf(buf, STATUS_PRINTF_MAXLEN, format, arglist);
222 va_end(arglist);
223 buf[STATUS_PRINTF_MAXLEN - 1] = 0;
224
225 if (stat < 0 || stat >= STATUS_PRINTF_MAXLEN)
226 {
227 so->errors = true;
228 }
229
230 if (so->msglevel >= 0 && !so->errors)
231 {
232 msg((msglvl_t)so->msglevel, "%s", buf);
233 }
234
235 if (so->fd >= 0 && !so->errors)
236 {
237 strcat(buf, "\n");
238 size_t len = strlen(buf);
239 if (len > 0)
240 {
241 if (write(so->fd, buf, (unsigned int)len) != len)
242 {
243 so->errors = true;
244 }
245 }
246 }
247
248 if (so->vout && !so->errors)
249 {
250 chomp(buf);
251 (*so->vout->func)(so->vout->arg, so->vout->flags_default, buf);
252 }
253 }
254}
255
256bool
257status_read(struct status_output *so, struct buffer *buf)
258{
259 bool ret = false;
260
261 if (so && so->fd >= 0 && (so->flags & STATUS_OUTPUT_READ))
262 {
264 ASSERT(buf_defined(buf));
265 while (true)
266 {
267 const int c = buf_read_u8(&so->read_buf);
268
269 /* read more of file into buffer */
270 if (c == -1)
271 {
272 ASSERT(buf_init(&so->read_buf, 0));
273 ssize_t len = read(so->fd, BPTR(&so->read_buf), BCAP(&so->read_buf));
274 if (len <= 0)
275 {
276 break;
277 }
278
279 ASSERT(buf_inc_len(&so->read_buf, (int)len));
280 continue;
281 }
282
283 ret = true;
284
285 if (c == '\r')
286 {
287 continue;
288 }
289
290 if (c == '\n')
291 {
292 break;
293 }
294
295 buf_write_u8(buf, (uint8_t)c);
296 }
297
299 }
300
301 return ret;
302}
void free_buf(struct buffer *buf)
Definition buffer.c:184
void buf_null_terminate(struct buffer *buf)
Definition buffer.c:532
void chomp(char *str)
Definition buffer.c:613
struct buffer alloc_buf(size_t size)
Definition buffer.c:63
char * string_alloc(const char *str, struct gc_arena *gc)
Definition buffer.c:648
#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:1064
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: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:209
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:212
bool status_read(struct status_output *so, struct buffer *buf)
Definition status.c:257
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