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