/*
 *  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-2011 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
 */

/*
 * Maintain usage stats in a memory-mapped file
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#elif defined(_MSC_VER)
#include "config-msvc.h"
#endif

#include "syshead.h"

#if defined(ENABLE_MEMSTATS)

#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
#include "error.h"
#include "misc.h"
#include "mstats.h"

#include "memdbg.h"

volatile struct mmap_stats *mmap_stats = NULL; /* GLOBAL */
static char mmap_fn[128];

void
mstats_open(const char *fn)
{
  void *data;
  ssize_t stat;
  int fd;
  struct mmap_stats ms;

  if (mmap_stats) /* already called? */
    return;

  /* verify that filename is not too long */
  if (strlen(fn) >= sizeof(mmap_fn))
    msg (M_FATAL, "mstats_open: filename too long");

  /* create file that will be memory mapped */
  fd = open (fn, O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
  if (fd < 0)
    {
      msg (M_ERR, "mstats_open: cannot open: %s", fn);
      return;
    }

  /* set the file to the correct size to contain a
     struct mmap_stats, and zero it */
  CLEAR(ms);
  ms.state = MSTATS_ACTIVE;
  stat = write(fd, &ms, sizeof(ms));
  if (stat != sizeof(ms))
    {
      msg (M_ERR, "mstats_open: write error: %s", fn);
      close(fd);
      return;
    }

  /* mmap the file */
  data = mmap(NULL, sizeof(struct mmap_stats), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
  if (data == MAP_FAILED)
    {
      msg (M_ERR, "mstats_open: write error: %s", fn);
      close(fd);
      return;
    }

  /* close the fd (mmap now controls the file) */
  if (close(fd))
    {
      msg (M_ERR, "mstats_open: close error: %s", fn);
    }

  /* save filename so we can delete it later */
  strcpy(mmap_fn, fn);

  /* save a global pointer to memory-mapped region */
  mmap_stats = (struct mmap_stats *)data;

  msg (M_INFO, "memstats data will be written to %s", fn);
}

void
mstats_close(void)
{
  if (mmap_stats)
    {
      mmap_stats->state = MSTATS_EXPIRED;
      if (munmap((void *)mmap_stats, sizeof(struct mmap_stats)))
	msg (M_WARN | M_ERRNO, "mstats_close: munmap error");
      platform_unlink(mmap_fn);
      mmap_stats = NULL;
    }
}

#endif
