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 *
print_status_mode(unsigned int flags)44 print_status_mode(unsigned int flags)
45 {
46 switch (flags)
47 {
48 case STATUS_OUTPUT_WRITE:
49 return "WRITE";
50
51 case STATUS_OUTPUT_READ:
52 return "READ";
53
54 case STATUS_OUTPUT_READ|STATUS_OUTPUT_WRITE:
55 return "READ/WRITE";
56
57 default:
58 return "UNDEF";
59 }
60 }
61
62 struct status_output *
status_open(const char * filename,const int refresh_freq,const int msglevel,const struct virtual_output * vout,const unsigned int flags)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 {
83 case STATUS_OUTPUT_WRITE:
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
95 case STATUS_OUTPUT_READ|STATUS_OUTPUT_WRITE:
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 {
123 so->flags = STATUS_OUTPUT_WRITE;
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
status_trigger(struct status_output * so)135 status_trigger(struct status_output *so)
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 void
status_reset(struct status_output * so)150 status_reset(struct status_output *so)
151 {
152 if (so && so->fd >= 0)
153 {
154 lseek(so->fd, (off_t)0, SEEK_SET);
155 }
156 }
157
158 void
status_flush(struct status_output * so)159 status_flush(struct status_output *so)
160 {
161 if (so && so->fd >= 0 && (so->flags & STATUS_OUTPUT_WRITE))
162 {
163 #if defined(HAVE_FTRUNCATE)
164 {
165 const off_t off = lseek(so->fd, (off_t)0, SEEK_CUR);
166 if (ftruncate(so->fd, off) != 0)
167 {
168 msg(M_WARN | M_ERRNO, "Failed to truncate status file");
169 }
170 }
171 #elif defined(HAVE_CHSIZE)
172 {
173 const long off = (long) lseek(so->fd, (off_t)0, SEEK_CUR);
174 chsize(so->fd, off);
175 }
176 #else /* if defined(HAVE_FTRUNCATE) */
177 #warning both ftruncate and chsize functions appear to be missing from this OS
178 #endif
179
180 /* clear read buffer */
181 if (buf_defined(&so->read_buf))
182 {
183 ASSERT(buf_init(&so->read_buf, 0));
184 }
185 }
186 }
187
188 /* return false if error occurred */
189 bool
status_close(struct status_output * so)190 status_close(struct status_output *so)
191 {
192 bool ret = true;
193 if (so)
194 {
195 if (so->errors)
196 {
197 ret = false;
198 }
199 if (so->fd >= 0)
200 {
201 if (close(so->fd) < 0)
202 {
203 ret = false;
204 }
205 }
206 free(so->filename);
207
208 if (buf_defined(&so->read_buf))
209 {
210 free_buf(&so->read_buf);
211 }
212 free(so);
213 }
214 else
215 {
216 ret = false;
217 }
218 return ret;
219 }
220
221 #define STATUS_PRINTF_MAXLEN 512
222
223 void
status_printf(struct status_output * so,const char * format,...)224 status_printf(struct status_output *so, const char *format, ...)
225 {
226 if (so && (so->flags & STATUS_OUTPUT_WRITE))
227 {
228 char buf[STATUS_PRINTF_MAXLEN+2]; /* leave extra bytes for CR, LF */
229 va_list arglist;
230 int stat;
231
232 va_start(arglist, format);
233 stat = vsnprintf(buf, STATUS_PRINTF_MAXLEN, format, arglist);
234 va_end(arglist);
235 buf[STATUS_PRINTF_MAXLEN - 1] = 0;
236
237 if (stat < 0 || stat >= STATUS_PRINTF_MAXLEN)
238 {
239 so->errors = true;
240 }
241
242 if (so->msglevel >= 0 && !so->errors)
243 {
244 msg(so->msglevel, "%s", buf);
245 }
246
247 if (so->fd >= 0 && !so->errors)
248 {
249 int len;
250 strcat(buf, "\n");
251 len = strlen(buf);
252 if (len > 0)
253 {
254 if (write(so->fd, buf, len) != len)
255 {
256 so->errors = true;
257 }
258 }
259 }
260
261 if (so->vout && !so->errors)
262 {
263 chomp(buf);
264 (*so->vout->func)(so->vout->arg, so->vout->flags_default, buf);
265 }
266 }
267 }
268
269 bool
status_read(struct status_output * so,struct buffer * buf)270 status_read(struct status_output *so, struct buffer *buf)
271 {
272 bool ret = false;
273
274 if (so && so->fd >= 0 && (so->flags & STATUS_OUTPUT_READ))
275 {
276 ASSERT(buf_defined(&so->read_buf));
277 ASSERT(buf_defined(buf));
278 while (true)
279 {
280 const int c = buf_read_u8(&so->read_buf);
281
282 /* read more of file into buffer */
283 if (c == -1)
284 {
285 int len;
286
287 ASSERT(buf_init(&so->read_buf, 0));
288 len = read(so->fd, BPTR(&so->read_buf), BCAP(&so->read_buf));
289 if (len <= 0)
290 {
291 break;
292 }
293
294 ASSERT(buf_inc_len(&so->read_buf, len));
295 continue;
296 }
297
298 ret = true;
299
300 if (c == '\r')
301 {
302 continue;
303 }
304
305 if (c == '\n')
306 {
307 break;
308 }
309
310 buf_write_u8(buf, c);
311 }
312
313 buf_null_terminate(buf);
314 }
315
316 return ret;
317 }
318