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