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