/* sysconf.cc

   Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
   2007, 2008, 2009, 2010, 2011, 2012, 2013 Red Hat, Inc.

This file is part of Cygwin.

This software is a copyrighted work licensed under the terms of the
Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
details. */

#include "winsup.h"
#include <unistd.h>
#include <sys/param.h>
#include <sys/sysinfo.h>
#include "cygerrno.h"
#include "security.h"
#include "path.h"
#include "fhandler.h"
#include "dtable.h"
#include "pinfo.h"
#include "ntdll.h"

static long
get_open_max (int in)
{
  long max = getdtablesize ();
  if (max < OPEN_MAX)
    max = OPEN_MAX;
  return max;
}

static long
get_page_size (int in)
{
  return wincap.allocation_granularity ();
}

static long
get_nproc_values (int in)
{
  NTSTATUS status;
  SYSTEM_BASIC_INFORMATION sbi;

  status = NtQuerySystemInformation (SystemBasicInformation, (PVOID) &sbi,
				     sizeof sbi, NULL);
  if (!NT_SUCCESS (status))
    {
      __seterrno_from_nt_status (status);
      debug_printf ("NtQuerySystemInformation: status %y, %E", status);
      return -1;
    }
  switch (in)
    {
    case _SC_NPROCESSORS_CONF:
      return sbi.NumberProcessors;
    case _SC_NPROCESSORS_ONLN:
      {
	int i = 0;
	do
	 if (sbi.ActiveProcessors & 1)
	   i++;
	while (sbi.ActiveProcessors >>= 1);
	return i;
      }
    case _SC_PHYS_PAGES:
      return sbi.NumberOfPhysicalPages
	     / (wincap.allocation_granularity () / wincap.page_size ());
    }
  return -1;
}

static long
get_avphys (int in)
{
  NTSTATUS status;
  SYSTEM_PERFORMANCE_INFORMATION spi;

  status = NtQuerySystemInformation (SystemPerformanceInformation,
				     (PVOID) &spi, sizeof spi, NULL);
  if (!NT_SUCCESS (status))
    {
      __seterrno_from_nt_status (status);
      debug_printf ("NtQuerySystemInformation: status %y, %E", status);
      return -1;
    }
  return spi.AvailablePages
	 / (wincap.allocation_granularity () / wincap.page_size ());
}

enum sc_type { nsup, cons, func };

static struct
{
  sc_type type;
  union
    {
      long c;
      long (*f)(int);
    };
} sca[] =
{
  {cons, {c:ARG_MAX}},			/*   0, _SC_ARG_MAX */
  {cons, {c:CHILD_MAX}},		/*   1, _SC_CHILD_MAX */
  {cons, {c:CLOCKS_PER_SEC}},		/*   2, _SC_CLK_TCK */
  {cons, {c:NGROUPS_MAX}},		/*   3, _SC_NGROUPS_MAX */
  {func, {f:get_open_max}},		/*   4, _SC_OPEN_MAX */
  {cons, {c:_POSIX_JOB_CONTROL}},	/*   5, _SC_JOB_CONTROL */
  {cons, {c:_POSIX_SAVED_IDS}},		/*   6, _SC_SAVED_IDS */
  {cons, {c:_POSIX_VERSION}},		/*   7, _SC_VERSION */
  {func, {f:get_page_size}},		/*   8, _SC_PAGESIZE */
  {func, {f:get_nproc_values}},		/*   9, _SC_NPROCESSORS_CONF */
  {func, {f:get_nproc_values}},		/*  10, _SC_NPROCESSORS_ONLN */
  {func, {f:get_nproc_values}},		/*  11, _SC_PHYS_PAGES */
  {func, {f:get_avphys}},		/*  12, _SC_AVPHYS_PAGES */
  {cons, {c:MQ_OPEN_MAX}},		/*  13, _SC_MQ_OPEN_MAX */
  {cons, {c:MQ_PRIO_MAX}},		/*  14, _SC_MQ_PRIO_MAX */
  {cons, {c:RTSIG_MAX}},		/*  15, _SC_RTSIG_MAX */
  {cons, {c:-1L}},			/*  16, _SC_SEM_NSEMS_MAX */
  {cons, {c:SEM_VALUE_MAX}},		/*  17, _SC_SEM_VALUE_MAX */
  {cons, {c:SIGQUEUE_MAX}},		/*  18, _SC_SIGQUEUE_MAX */
  {cons, {c:TIMER_MAX}},		/*  19, _SC_TIMER_MAX */
  {nsup, {c:0}},			/*  20, _SC_TZNAME_MAX */
  {cons, {c:-1L}},			/*  21, _SC_ASYNCHRONOUS_IO */
  {cons, {c:_POSIX_FSYNC}},		/*  22, _SC_FSYNC */
  {cons, {c:_POSIX_MAPPED_FILES}},	/*  23, _SC_MAPPED_FILES */
  {cons, {c:-1L}},			/*  24, _SC_MEMLOCK */
  {cons, {c:_POSIX_MEMLOCK_RANGE}},	/*  25, _SC_MEMLOCK_RANGE */
  {cons, {c:_POSIX_MEMORY_PROTECTION}},	/*  26, _SC_MEMORY_PROTECTION */
  {cons, {c:_POSIX_MESSAGE_PASSING}},	/*  27, _SC_MESSAGE_PASSING */
  {cons, {c:-1L}},			/*  28, _SC_PRIORITIZED_IO */
  {cons, {c:_POSIX_REALTIME_SIGNALS}},	/*  29, _SC_REALTIME_SIGNALS */
  {cons, {c:_POSIX_SEMAPHORES}},	/*  30, _SC_SEMAPHORES */
  {cons, {c:_POSIX_SHARED_MEMORY_OBJECTS}},	/*  31, _SC_SHARED_MEMORY_OBJECTS */
  {cons, {c:_POSIX_SYNCHRONIZED_IO}},	/*  32, _SC_SYNCHRONIZED_IO */
  {cons, {c:_POSIX_TIMERS}},		/*  33, _SC_TIMERS */
  {nsup, {c:0}},			/*  34, _SC_AIO_LISTIO_MAX */
  {nsup, {c:0}},			/*  35, _SC_AIO_MAX */
  {nsup, {c:0}},			/*  36, _SC_AIO_PRIO_DELTA_MAX */
  {nsup, {c:0}},			/*  37, _SC_DELAYTIMER_MAX */
  {cons, {c:PTHREAD_KEYS_MAX}},		/*  38, _SC_THREAD_KEYS_MAX */
  {cons, {c:PTHREAD_STACK_MIN}},	/*  39, _SC_THREAD_STACK_MIN */
  {cons, {c:-1L}},			/*  40, _SC_THREAD_THREADS_MAX */
  {cons, {c:TTY_NAME_MAX}},		/*  41, _SC_TTY_NAME_MAX */
  {cons, {c:_POSIX_THREADS}},		/*  42, _SC_THREADS */
  {cons, {c:_POSIX_THREAD_ATTR_STACKADDR}},/*  43, _SC_THREAD_ATTR_STACKADDR */
  {cons, {c:_POSIX_THREAD_ATTR_STACKSIZE}},/*  44, _SC_THREAD_ATTR_STACKSIZE */
  {cons, {c:_POSIX_THREAD_PRIORITY_SCHEDULING}},	/*  45, _SC_THREAD_PRIORITY_SCHEDULING */
  {cons, {c:-1L}},			/*  46, _SC_THREAD_PRIO_INHERIT */
  {cons, {c:-1L}},			/*  47, _SC_THREAD_PRIO_PROTECT */
  {cons, {c:_POSIX_THREAD_PROCESS_SHARED}},	/*  48, _SC_THREAD_PROCESS_SHARED */
  {cons, {c:_POSIX_THREAD_SAFE_FUNCTIONS}},	/*  49, _SC_THREAD_SAFE_FUNCTIONS */
  {cons, {c:16384L}},			/*  50, _SC_GETGR_R_SIZE_MAX */
  {cons, {c:16384L}},			/*  51, _SC_GETPW_R_SIZE_MAX */
  {cons, {c:LOGIN_NAME_MAX}},		/*  52, _SC_LOGIN_NAME_MAX */
  {cons, {c:PTHREAD_DESTRUCTOR_ITERATIONS}},	/*  53, _SC_THREAD_DESTRUCTOR_ITERATIONS */
  {cons, {c:_POSIX_ADVISORY_INFO}},	/*  54, _SC_ADVISORY_INFO */
  {cons, {c:ATEXIT_MAX}},		/*  55, _SC_ATEXIT_MAX */
  {cons, {c:-1L}},			/*  56, _SC_BARRIERS */
  {cons, {c:BC_BASE_MAX}},		/*  57, _SC_BC_BASE_MAX */
  {cons, {c:BC_DIM_MAX}},		/*  58, _SC_BC_DIM_MAX */
  {cons, {c:BC_SCALE_MAX}},		/*  59, _SC_BC_SCALE_MAX */
  {cons, {c:BC_STRING_MAX}},		/*  60, _SC_BC_STRING_MAX */
  {cons, {c:_POSIX_CLOCK_SELECTION}},	/*  61, _SC_CLOCK_SELECTION */
  {nsup, {c:0}},			/*  62, _SC_COLL_WEIGHTS_MAX */
  {cons, {c:_POSIX_CPUTIME}},		/*  63, _SC_CPUTIME */
  {cons, {c:EXPR_NEST_MAX}},		/*  64, _SC_EXPR_NEST_MAX */
  {cons, {c:HOST_NAME_MAX}},		/*  65, _SC_HOST_NAME_MAX */
  {cons, {c:IOV_MAX}},			/*  66, _SC_IOV_MAX */
  {cons, {c:_POSIX_IPV6}},		/*  67, _SC_IPV6 */
  {cons, {c:LINE_MAX}},			/*  68, _SC_LINE_MAX */
  {cons, {c:_POSIX_MONOTONIC_CLOCK}},	/*  69, _SC_MONOTONIC_CLOCK */
  {cons, {c:_POSIX_RAW_SOCKETS}},	/*  70, _SC_RAW_SOCKETS */
  {cons, {c:_POSIX_READER_WRITER_LOCKS}},	/*  71, _SC_READER_WRITER_LOCKS */
  {cons, {c:_POSIX_REGEXP}},		/*  72, _SC_REGEXP */
  {cons, {c:RE_DUP_MAX}},		/*  73, _SC_RE_DUP_MAX */
  {cons, {c:_POSIX_SHELL}},		/*  74, _SC_SHELL */
  {cons, {c:-1L}},			/*  75, _SC_SPAWN */
  {cons, {c:_POSIX_SPIN_LOCKS}},	/*  76, _SC_SPIN_LOCKS */
  {cons, {c:-1L}},			/*  77, _SC_SPORADIC_SERVER */
  {nsup, {c:0}},			/*  78, _SC_SS_REPL_MAX */
  {cons, {c:SYMLOOP_MAX}},		/*  79, _SC_SYMLOOP_MAX */
  {cons, {c:_POSIX_THREAD_CPUTIME}},	/*  80, _SC_THREAD_CPUTIME */
  {cons, {c:-1L}},			/*  81, _SC_THREAD_SPORADIC_SERVER */
  {cons, {c:-1L}},			/*  82, _SC_TIMEOUTS */
  {cons, {c:-1L}},			/*  83, _SC_TRACE */
  {cons, {c:-1L}},			/*  84, _SC_TRACE_EVENT_FILTER */
  {nsup, {c:0}},			/*  85, _SC_TRACE_EVENT_NAME_MAX */
  {cons, {c:-1L}},			/*  86, _SC_TRACE_INHERIT */
  {cons, {c:-1L}},			/*  87, _SC_TRACE_LOG */
  {nsup, {c:0}},			/*  88, _SC_TRACE_NAME_MAX */
  {nsup, {c:0}},			/*  89, _SC_TRACE_SYS_MAX */
  {nsup, {c:0}},			/*  90, _SC_TRACE_USER_EVENT_MAX */
  {cons, {c:-1L}},			/*  91, _SC_TYPED_MEMORY_OBJECTS */
  {cons, {c:_POSIX_V6_ILP32_OFF32}},	/*  92, _SC_V6_ILP32_OFF32 */
  {cons, {c:_POSIX_V6_ILP32_OFFBIG}},	/*  93, _SC_V6_ILP32_OFFBIG */
  {cons, {c:_POSIX_V6_LP64_OFF64}},	/*  94, _SC_V6_LP64_OFF64 */
  {cons, {c:_POSIX_V6_LPBIG_OFFBIG}},	/*  95, _SC_V6_LPBIG_OFFBIG */
  {cons, {c:_XOPEN_CRYPT}},		/*  96, _SC_XOPEN_CRYPT */
  {cons, {c:_XOPEN_ENH_I18N}},		/*  97, _SC_XOPEN_ENH_I18N */
  {cons, {c:-1L}},			/*  98, _SC_XOPEN_LEGACY */
  {cons, {c:-1L}},			/*  99, _SC_XOPEN_REALTIME */
  {cons, {c:STREAM_MAX}},		/* 100, _SC_STREAM_MAX */
  {cons, {c:_POSIX_PRIORITY_SCHEDULING}},	/* 101, _SC_PRIORITY_SCHEDULING */
  {cons, {c:-1L}},			/* 102, _SC_XOPEN_REALTIME_THREADS */
  {cons, {c:_XOPEN_SHM}},		/* 103, _SC_XOPEN_SHM */
  {cons, {c:-1L}},			/* 104, _SC_XOPEN_STREAMS */
  {cons, {c:-1L}},			/* 105, _SC_XOPEN_UNIX */
  {cons, {c:_XOPEN_VERSION}},		/* 106, _SC_XOPEN_VERSION */
  {cons, {c:_POSIX2_CHAR_TERM}},	/* 107, _SC_2_CHAR_TERM */
  {cons, {c:_POSIX2_C_BIND}},		/* 108, _SC_2_C_BIND */
  {cons, {c:_POSIX2_C_BIND}},		/* 109, _SC_2_C_DEV */
  {cons, {c:-1L}},			/* 110, _SC_2_FORT_DEV */
  {cons, {c:-1L}},			/* 111, _SC_2_FORT_RUN */
  {cons, {c:-1L}},			/* 112, _SC_2_LOCALEDEF */
  {cons, {c:-1L}},			/* 113, _SC_2_PBS */
  {cons, {c:-1L}},			/* 114, _SC_2_PBS_ACCOUNTING */
  {cons, {c:-1L}},			/* 115, _SC_2_PBS_CHECKPOINT */
  {cons, {c:-1L}},			/* 116, _SC_2_PBS_LOCATE */
  {cons, {c:-1L}},			/* 117, _SC_2_PBS_MESSAGE */
  {cons, {c:-1L}},			/* 118, _SC_2_PBS_TRACK */
  {cons, {c:_POSIX2_SW_DEV}},		/* 119, _SC_2_SW_DEV */
  {cons, {c:_POSIX2_UPE}},		/* 120, _SC_2_UPE */
  {cons, {c:_POSIX2_VERSION}},		/* 121, _SC_2_VERSION */
  {cons, {c:-1L}},			/* 122, _SC_THREAD_ROBUST_PRIO_INHERIT */
  {cons, {c:-1L}},			/* 123, _SC_THREAD_ROBUST_PRIO_PROTECT */
  {cons, {c:-1L}},			/* 124, _SC_XOPEN_UUCP */
};

#define SC_MIN _SC_ARG_MAX
#define SC_MAX _SC_XOPEN_UUCP

/* sysconf: POSIX 4.8.1.1 */
/* Allows a portable app to determine quantities of resources or
   presence of an option at execution time. */
long int
sysconf (int in)
{
  if (in >= SC_MIN && in <= SC_MAX)
    {
      switch (sca[in].type)
	{
	case nsup:
	  break;
	case cons:
	  return sca[in].c;
	case func:
	  return sca[in].f (in);
	}
    }
  /* Unimplemented sysconf name or invalid option value. */
  set_errno (EINVAL);
  return -1L;
}

#define ls(s)	sizeof(s),s

static struct
{
  size_t l;
  const char *s;
} csa[] =
{
  {ls ("/bin:/usr/bin")},		/* _CS_PATH */
  {0, NULL},				/* _CS_POSIX_V6_ILP32_OFF32_CFLAGS */
  {0, NULL},				/* _CS_POSIX_V6_ILP32_OFF32_LDFLAGS */
  {0, NULL},				/* _CS_POSIX_V6_ILP32_OFF32_LIBS */
  {0, NULL},				/* _CS_XBS5_ILP32_OFF32_LINTFLAGS */
#ifdef __x86_64__
  {0, NULL},				/* _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS */
  {0, NULL},				/* _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS */
  {0, NULL},				/* _CS_POSIX_V6_ILP32_OFFBIG_LIBS */
  {0, NULL},				/* _CS_XBS5_ILP32_OFFBIG_LINTFLAGS */
  {ls ("")},				/* _CS_POSIX_V6_LP64_OFF64_CFLAGS */
  {ls ("")},				/* _CS_POSIX_V6_LP64_OFF64_LDFLAGS */
  {ls ("")},				/* _CS_POSIX_V6_LP64_OFF64_LIBS */
  {ls ("")},				/* _CS_XBS5_LP64_OFF64_LINTFLAGS */
  {ls ("")},				/* _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS */
  {ls ("")},				/* _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS */
  {ls ("")},				/* _CS_POSIX_V6_LPBIG_OFFBIG_LIBS */
  {ls ("")},				/* _CS_XBS5_LPBIG_OFFBIG_LINTFLAGS */
  {ls ("POSIX_V6_LP64_OFF64")},		/* _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS */
#else
  {ls ("")},				/* _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS */
  {ls ("")},				/* _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS */
  {ls ("")},				/* _CS_POSIX_V6_ILP32_OFFBIG_LIBS */
  {ls ("")},				/* _CS_XBS5_ILP32_OFFBIG_LINTFLAGS */
  {0, NULL},				/* _CS_POSIX_V6_LP64_OFF64_CFLAGS */
  {0, NULL},				/* _CS_POSIX_V6_LP64_OFF64_LDFLAGS */
  {0, NULL},				/* _CS_POSIX_V6_LP64_OFF64_LIBS */
  {0, NULL},				/* _CS_XBS5_LP64_OFF64_LINTFLAGS */
  {0, NULL},				/* _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS */
  {0, NULL},				/* _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS */
  {0, NULL},				/* _CS_POSIX_V6_LPBIG_OFFBIG_LIBS */
  {0, NULL},				/* _CS_XBS5_LPBIG_OFFBIG_LINTFLAGS */
  {ls ("POSIX_V6_ILP32_OFFBIG")},	/* _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS */
#endif
  {ls ("")},				/* _CS_POSIX_V7_THREADS_CFLAGS */
  {ls ("")},				/* _CS_POSIX_V7_THREADS_LDFLAGS */
  {ls ("POSIXLY_CORRECT=1")},		/* _CS_V7_ENV */
};

#define CS_MIN _CS_PATH
#define CS_MAX _CS_V7_ENV

extern "C" size_t
confstr (int in, char *buf, size_t len)
{
  if (in >= CS_MIN && in <= CS_MAX)
    {
      if (csa[in].l && len)
	{
	  buf[0] = 0;
	  strncat (buf, csa[in].s, MIN (len, csa[in].l) - 1);
	}
      return csa[in].l;
    }
  /* Invalid option value. */
  set_errno (EINVAL);
  return 0;
}

extern "C" int
get_nprocs_conf (void)
{
  return get_nproc_values (_SC_NPROCESSORS_CONF);
}

extern "C" int
get_nprocs (void)
{
  return get_nproc_values (_SC_NPROCESSORS_ONLN);
}

extern "C" long
get_phys_pages (void)
{
  return get_nproc_values (_SC_PHYS_PAGES);
}

extern "C" long
get_avphys_pages (void)
{
  return get_avphys (_SC_AVPHYS_PAGES);
}

extern "C" int
sysinfo (struct sysinfo *info)
{
  unsigned long long uptime = 0ULL, totalram = 0ULL, freeram = 0ULL,
		totalswap = 0ULL, freeswap = 0ULL;
  MEMORYSTATUSEX memory_status;
  PSYSTEM_PAGEFILE_INFORMATION spi = NULL;
  ULONG sizeof_spi = 512;
  PSYSTEM_TIMEOFDAY_INFORMATION stodi = NULL;
  const ULONG sizeof_stodi = sizeof (SYSTEM_TIMEOFDAY_INFORMATION);
  NTSTATUS status = STATUS_SUCCESS;
  winpids pids ((DWORD) 0);

  if (!info)
    {
      set_errno (EFAULT);
      return -1;
    }

  stodi = (PSYSTEM_TIMEOFDAY_INFORMATION) malloc (sizeof_stodi);
  status = NtQuerySystemInformation (SystemTimeOfDayInformation, (PVOID) stodi,
				     sizeof_stodi, NULL);
  if (NT_SUCCESS (status))
    uptime = (stodi->CurrentTime.QuadPart - stodi->BootTime.QuadPart)
	     / 10000000ULL;
  else
    debug_printf ("NtQuerySystemInformation(SystemTimeOfDayInformation), "
		  "status %y", status);

  if (stodi)
    free (stodi);

  memory_status.dwLength = sizeof (MEMORYSTATUSEX);
  GlobalMemoryStatusEx (&memory_status);
  totalram = memory_status.ullTotalPhys / wincap.page_size ();
  freeram = memory_status.ullAvailPhys / wincap.page_size ();

  spi = (PSYSTEM_PAGEFILE_INFORMATION) malloc (sizeof_spi);
  if (spi)
    {
      status = NtQuerySystemInformation (SystemPagefileInformation, (PVOID) spi,
					 sizeof_spi, &sizeof_spi);
      if (status == STATUS_INFO_LENGTH_MISMATCH)
	{
	  free (spi);
	  spi = (PSYSTEM_PAGEFILE_INFORMATION) malloc (sizeof_spi);
	  if (spi)
	    status = NtQuerySystemInformation (SystemPagefileInformation,
					       (PVOID) spi, sizeof_spi,
					       &sizeof_spi);
	}
    }
  if (!spi || !NT_SUCCESS (status))
    {
      debug_printf ("NtQuerySystemInformation(SystemPagefileInformation), "
		    "status %y", status);
      totalswap = (memory_status.ullTotalPageFile - memory_status.ullTotalPhys)
		  / wincap.page_size ();
      freeswap = (memory_status.ullAvailPageFile - memory_status.ullTotalPhys)
		 / wincap.page_size ();
    }
  else
    {
      PSYSTEM_PAGEFILE_INFORMATION spp = spi;
      do
	{
	  totalswap += spp->CurrentSize;
	  freeswap += spp->CurrentSize - spp->TotalUsed;
	}
      while (spp->NextEntryOffset
	     && (spp = (PSYSTEM_PAGEFILE_INFORMATION)
			   ((char *) spp + spp->NextEntryOffset)));
    }
  if (spi)
    free (spi);

  info->uptime = (long) uptime;
  info->totalram = (unsigned long) totalram;
  info->freeram = (unsigned long) freeram;
  info->totalswap = (unsigned long) totalswap;
  info->freeswap = (unsigned long) freeswap;
  info->procs = (unsigned short) pids.npids;
  info->mem_unit = (unsigned int) wincap.page_size ();

  /* FIXME: unsupported */
  info->loads[0] = 0UL;
  info->loads[1] = 0UL;
  info->loads[2] = 0UL;
  info->sharedram = 0UL;
  info->bufferram = 0UL;
  info->totalhigh = 0UL;
  info->freehigh = 0UL;

  return 0;
}
