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