summaryrefslogtreecommitdiff
path: root/src/openvpn/status.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/openvpn/status.c')
-rw-r--r--src/openvpn/status.c290
1 files changed, 290 insertions, 0 deletions
diff --git a/src/openvpn/status.c b/src/openvpn/status.c
new file mode 100644
index 0000000..5f9ab9e
--- /dev/null
+++ b/src/openvpn/status.c
@@ -0,0 +1,290 @@
+/*
+ * OpenVPN -- An application to securely tunnel IP networks
+ * over a single TCP/UDP port, with support for SSL/TLS-based
+ * session authentication and key exchange,
+ * packet encryption, packet authentication, and
+ * packet compression.
+ *
+ * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#elif defined(_MSC_VER)
+#include "config-msvc.h"
+#endif
+
+#include "syshead.h"
+
+#include "status.h"
+#include "perf.h"
+#include "misc.h"
+
+#include "memdbg.h"
+
+/*
+ * printf-style interface for outputting status info
+ */
+
+static const char *
+print_status_mode (unsigned int flags)
+{
+ switch (flags)
+ {
+ case STATUS_OUTPUT_WRITE:
+ return "WRITE";
+ case STATUS_OUTPUT_READ:
+ return "READ";
+ case STATUS_OUTPUT_READ|STATUS_OUTPUT_WRITE:
+ return "READ/WRITE";
+ default:
+ return "UNDEF";
+ }
+}
+
+struct status_output *
+status_open (const char *filename,
+ const int refresh_freq,
+ const int msglevel,
+ const struct virtual_output *vout,
+ const unsigned int flags)
+{
+ struct status_output *so = NULL;
+ if (filename || msglevel >= 0 || vout)
+ {
+ ALLOC_OBJ_CLEAR (so, struct status_output);
+ so->flags = flags;
+ so->msglevel = msglevel;
+ so->vout = vout;
+ so->fd = -1;
+ buf_reset (&so->read_buf);
+ event_timeout_clear (&so->et);
+ if (filename)
+ {
+ switch (so->flags)
+ {
+ case STATUS_OUTPUT_WRITE:
+ so->fd = platform_open (filename,
+ O_CREAT | O_TRUNC | O_WRONLY,
+ S_IRUSR | S_IWUSR);
+ break;
+ case STATUS_OUTPUT_READ:
+ so->fd = platform_open (filename,
+ O_RDONLY,
+ S_IRUSR | S_IWUSR);
+ break;
+ case STATUS_OUTPUT_READ|STATUS_OUTPUT_WRITE:
+ so->fd = platform_open (filename,
+ O_CREAT | O_RDWR,
+ S_IRUSR | S_IWUSR);
+ break;
+ default:
+ ASSERT (0);
+ }
+ if (so->fd >= 0)
+ {
+ so->filename = string_alloc (filename, NULL);
+
+ /* allocate read buffer */
+ if (so->flags & STATUS_OUTPUT_READ)
+ so->read_buf = alloc_buf (512);
+ }
+ else
+ {
+ msg (M_WARN, "Note: cannot open %s for %s", filename, print_status_mode (so->flags));
+ so->errors = true;
+ }
+ }
+ else
+ so->flags = STATUS_OUTPUT_WRITE;
+
+ if ((so->flags & STATUS_OUTPUT_WRITE) && refresh_freq > 0)
+ {
+ event_timeout_init (&so->et, refresh_freq, 0);
+ }
+ }
+ return so;
+}
+
+bool
+status_trigger (struct status_output *so)
+{
+ if (so)
+ {
+ struct timeval null;
+ CLEAR (null);
+ return event_timeout_trigger (&so->et, &null, ETT_DEFAULT);
+ }
+ else
+ return false;
+}
+
+bool
+status_trigger_tv (struct status_output *so, struct timeval *tv)
+{
+ if (so)
+ return event_timeout_trigger (&so->et, tv, ETT_DEFAULT);
+ else
+ return false;
+}
+
+void
+status_reset (struct status_output *so)
+{
+ if (so && so->fd >= 0)
+ lseek (so->fd, (off_t)0, SEEK_SET);
+}
+
+void
+status_flush (struct status_output *so)
+{
+ if (so && so->fd >= 0 && (so->flags & STATUS_OUTPUT_WRITE))
+ {
+#if defined(HAVE_FTRUNCATE)
+ {
+ const off_t off = lseek (so->fd, (off_t)0, SEEK_CUR);
+ if (ftruncate (so->fd, off) != 0) {
+ msg (M_WARN, "Failed to truncate status file: %s", strerror(errno));
+ }
+ }
+#elif defined(HAVE_CHSIZE)
+ {
+ const long off = (long) lseek (so->fd, (off_t)0, SEEK_CUR);
+ chsize (so->fd, off);
+ }
+#else
+#warning both ftruncate and chsize functions appear to be missing from this OS
+#endif
+
+ /* clear read buffer */
+ if (buf_defined (&so->read_buf))
+ {
+ ASSERT (buf_init (&so->read_buf, 0));
+ }
+ }
+}
+
+/* return false if error occurred */
+bool
+status_close (struct status_output *so)
+{
+ bool ret = true;
+ if (so)
+ {
+ if (so->errors)
+ ret = false;
+ if (so->fd >= 0)
+ {
+ if (close (so->fd) < 0)
+ ret = false;
+ }
+ if (so->filename)
+ free (so->filename);
+ if (buf_defined (&so->read_buf))
+ free_buf (&so->read_buf);
+ free (so);
+ }
+ else
+ ret = false;
+ return ret;
+}
+
+#define STATUS_PRINTF_MAXLEN 512
+
+void
+status_printf (struct status_output *so, const char *format, ...)
+{
+ if (so && (so->flags & STATUS_OUTPUT_WRITE))
+ {
+ char buf[STATUS_PRINTF_MAXLEN+2]; /* leave extra bytes for CR, LF */
+ va_list arglist;
+ int stat;
+
+ va_start (arglist, format);
+ stat = vsnprintf (buf, STATUS_PRINTF_MAXLEN, format, arglist);
+ va_end (arglist);
+ buf[STATUS_PRINTF_MAXLEN - 1] = 0;
+
+ if (stat < 0 || stat >= STATUS_PRINTF_MAXLEN)
+ so->errors = true;
+
+ if (so->msglevel >= 0 && !so->errors)
+ msg (so->msglevel, "%s", buf);
+
+ if (so->fd >= 0 && !so->errors)
+ {
+ int len;
+ strcat (buf, "\n");
+ len = strlen (buf);
+ if (len > 0)
+ {
+ if (write (so->fd, buf, len) != len)
+ so->errors = true;
+ }
+ }
+
+ if (so->vout && !so->errors)
+ {
+ chomp (buf);
+ (*so->vout->func) (so->vout->arg, so->vout->flags_default, buf);
+ }
+ }
+}
+
+bool
+status_read (struct status_output *so, struct buffer *buf)
+{
+ bool ret = false;
+
+ if (so && so->fd >= 0 && (so->flags & STATUS_OUTPUT_READ))
+ {
+ ASSERT (buf_defined (&so->read_buf));
+ ASSERT (buf_defined (buf));
+ while (true)
+ {
+ const int c = buf_read_u8 (&so->read_buf);
+
+ /* read more of file into buffer */
+ if (c == -1)
+ {
+ int len;
+
+ ASSERT (buf_init (&so->read_buf, 0));
+ len = read (so->fd, BPTR (&so->read_buf), BCAP (&so->read_buf));
+ if (len <= 0)
+ break;
+
+ ASSERT (buf_inc_len (&so->read_buf, len));
+ continue;
+ }
+
+ ret = true;
+
+ if (c == '\r')
+ continue;
+
+ if (c == '\n')
+ break;
+
+ buf_write_u8 (buf, c);
+ }
+
+ buf_null_terminate (buf);
+ }
+
+ return ret;
+}