6fbf66fa |
/*
* 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.
* |
49979459 |
* Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net> |
6fbf66fa |
*
* 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.
* |
caa54ac3 |
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
6fbf66fa |
*/
|
c110b289 |
#ifdef HAVE_CONFIG_H
#include "config.h"
#elif defined(_MSC_VER)
#include "config-msvc.h"
#endif
|
6fbf66fa |
#include "syshead.h"
#include "status.h"
#include "perf.h" |
bdf9ab75 |
#include "misc.h" |
09ee4192 |
#include "fdmisc.h" |
6fbf66fa |
#include "memdbg.h"
/*
* printf-style interface for outputting status info
*/
static const char * |
81d882d5 |
print_status_mode(unsigned int flags) |
6fbf66fa |
{ |
81d882d5 |
switch (flags) |
6fbf66fa |
{ |
81d882d5 |
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"; |
6fbf66fa |
}
}
struct status_output * |
81d882d5 |
status_open(const char *filename,
const int refresh_freq,
const int msglevel,
const struct virtual_output *vout,
const unsigned int flags) |
6fbf66fa |
{ |
81d882d5 |
struct status_output *so = NULL;
if (filename || msglevel >= 0 || vout) |
6fbf66fa |
{ |
81d882d5 |
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) |
71bbbd76 |
{ |
81d882d5 |
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);
set_cloexec(so->fd);
/* allocate read buffer */
if (so->flags & STATUS_OUTPUT_READ)
{
so->read_buf = alloc_buf(512);
}
}
else |
71bbbd76 |
{ |
81d882d5 |
msg(M_WARN, "Note: cannot open %s for %s", filename, print_status_mode(so->flags));
so->errors = true; |
71bbbd76 |
} |
81d882d5 |
}
else
{
so->flags = STATUS_OUTPUT_WRITE;
}
if ((so->flags & STATUS_OUTPUT_WRITE) && refresh_freq > 0)
{
event_timeout_init(&so->et, refresh_freq, 0);
} |
6fbf66fa |
} |
81d882d5 |
return so; |
6fbf66fa |
}
bool |
81d882d5 |
status_trigger(struct status_output *so) |
6fbf66fa |
{ |
81d882d5 |
if (so) |
6fbf66fa |
{ |
81d882d5 |
struct timeval null;
CLEAR(null);
return event_timeout_trigger(&so->et, &null, ETT_DEFAULT);
}
else
{
return false; |
6fbf66fa |
}
}
bool |
81d882d5 |
status_trigger_tv(struct status_output *so, struct timeval *tv) |
6fbf66fa |
{ |
81d882d5 |
if (so)
{
return event_timeout_trigger(&so->et, tv, ETT_DEFAULT);
}
else
{
return false;
} |
6fbf66fa |
}
void |
81d882d5 |
status_reset(struct status_output *so) |
6fbf66fa |
{ |
81d882d5 |
if (so && so->fd >= 0)
{
lseek(so->fd, (off_t)0, SEEK_SET);
} |
6fbf66fa |
}
void |
81d882d5 |
status_flush(struct status_output *so) |
6fbf66fa |
{ |
81d882d5 |
if (so && so->fd >= 0 && (so->flags & STATUS_OUTPUT_WRITE)) |
6fbf66fa |
{
#if defined(HAVE_FTRUNCATE) |
81d882d5 |
{
const off_t off = lseek(so->fd, (off_t)0, SEEK_CUR);
if (ftruncate(so->fd, off) != 0)
{ |
56b396dc |
msg(M_WARN | M_ERRNO, "Failed to truncate status file"); |
81d882d5 |
}
} |
6fbf66fa |
#elif defined(HAVE_CHSIZE) |
81d882d5 |
{
const long off = (long) lseek(so->fd, (off_t)0, SEEK_CUR);
chsize(so->fd, off);
}
#else /* if defined(HAVE_FTRUNCATE) */ |
6fbf66fa |
#warning both ftruncate and chsize functions appear to be missing from this OS
#endif
|
81d882d5 |
/* clear read buffer */
if (buf_defined(&so->read_buf))
{
ASSERT(buf_init(&so->read_buf, 0));
} |
6fbf66fa |
}
}
/* return false if error occurred */
bool |
81d882d5 |
status_close(struct status_output *so) |
6fbf66fa |
{ |
81d882d5 |
bool ret = true;
if (so) |
6fbf66fa |
{ |
81d882d5 |
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; |
6fbf66fa |
} |
81d882d5 |
return ret; |
6fbf66fa |
}
|
6cd276ba |
#define STATUS_PRINTF_MAXLEN 512 |
6fbf66fa |
void |
81d882d5 |
status_printf(struct status_output *so, const char *format, ...) |
6fbf66fa |
{ |
81d882d5 |
if (so && (so->flags & STATUS_OUTPUT_WRITE)) |
6fbf66fa |
{ |
81d882d5 |
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);
} |
6fbf66fa |
}
}
bool |
81d882d5 |
status_read(struct status_output *so, struct buffer *buf) |
6fbf66fa |
{ |
81d882d5 |
bool ret = false; |
6fbf66fa |
|
81d882d5 |
if (so && so->fd >= 0 && (so->flags & STATUS_OUTPUT_READ)) |
6fbf66fa |
{ |
81d882d5 |
ASSERT(buf_defined(&so->read_buf));
ASSERT(buf_defined(buf));
while (true)
{
const int c = buf_read_u8(&so->read_buf); |
6fbf66fa |
|
81d882d5 |
/* read more of file into buffer */
if (c == -1)
{
int len; |
6fbf66fa |
|
81d882d5 |
ASSERT(buf_init(&so->read_buf, 0));
len = read(so->fd, BPTR(&so->read_buf), BCAP(&so->read_buf));
if (len <= 0)
{
break;
} |
6fbf66fa |
|
81d882d5 |
ASSERT(buf_inc_len(&so->read_buf, len));
continue;
} |
6fbf66fa |
|
81d882d5 |
ret = true; |
6fbf66fa |
|
81d882d5 |
if (c == '\r')
{
continue;
} |
6fbf66fa |
|
81d882d5 |
if (c == '\n')
{
break;
} |
6fbf66fa |
|
81d882d5 |
buf_write_u8(buf, c);
} |
6fbf66fa |
|
81d882d5 |
buf_null_terminate(buf); |
6fbf66fa |
}
|
81d882d5 |
return ret; |
6fbf66fa |
} |