Start
[exim.git] / src / OS / os.c-cygwin
diff --git a/src/OS/os.c-cygwin b/src/OS/os.c-cygwin
new file mode 100644 (file)
index 0000000..7396055
--- /dev/null
@@ -0,0 +1,652 @@
+/* $Cambridge: exim/src/OS/os.c-cygwin,v 1.1 2004/10/06 15:07:39 ph10 Exp $ */
+
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+
+/* Cygwin-specific code. December 2002
+   This is concatenated onto the generic src/os.c file.
+
+   This code was supplied by Pierre A. Humblet <Pierre.Humblet@ieee.org>
+*/
+
+/* We need a special mkdir that
+   allows names starting with // */
+#undef mkdir
+int cygwin_mkdir( const char *path, mode_t mode )
+{
+  const char * p = path;
+  if (*p == '/') while(*(p+1) == '/') p++;
+  return mkdir(p, mode);
+}
+
+/* We have strsignal but cannot use #define
+   because types don't match */
+#define OS_STRSIGNAL /* src/os.c need not provide it */
+char * os_strsignal(int sig)
+{
+  return (char *) strsignal(sig);
+}
+
+#ifndef COMPILE_UTILITY /* Utilities don't need special code */
+#ifdef INCLUDE_MINIRES
+#include "../minires/minires.c"
+#include "../minires/os-interface.c"
+#endif
+
+#ifdef INCLUDE_PAM
+#include "../pam/pam.c"
+#endif
+
+unsigned int cygwin_WinVersion;
+
+/* Conflict between Windows definitions and others */
+#ifdef NOERROR
+#undef NOERROR
+#endif
+#ifdef DELETE
+#undef DELETE
+#endif
+
+#include <windows.h>
+#include <sys/cygwin.h>
+
+/* Special static variables */
+static BOOL cygwin_debug = FALSE;
+static int privileged = 1; /* when not privileged, setuid = noop */
+
+#undef setuid
+int cygwin_setuid(uid_t uid )
+{
+  int res;
+  if (privileged <= 0) return 0;
+  else {
+    res = setuid(uid);
+    if (cygwin_debug)
+      fprintf(stderr, "setuid %lu %lu %d pid: %d\n",
+              uid, getuid(),res, getpid());
+  }
+  return res;
+}
+
+#undef setgid
+int cygwin_setgid(gid_t gid )
+{
+  int res;
+  if (privileged <= 0) return 0;
+  else {
+    res = setgid(gid);
+    if (cygwin_debug)
+      fprintf(stderr, "setgid %lu %lu %d pid: %d\n",
+             gid, getgid(), res, getpid());
+  }
+  return res;
+}
+
+/* Background processes run at lower priority */
+static void setpriority()
+{
+  if (!SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS))
+    SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS);
+  return;
+}
+
+
+/* GetVersion()
+   MSB: 1 for 95/98/ME; Next 7: build number, except for 95/98/ME
+   Next byte: 0
+   Next byte: minor version of OS
+   Low  byte: major version of OS (3 or 4 for for NT, 5 for 2000 and XP) */
+#define VERSION_IS_58M(x) (x & 0x80000000) /* 95, 98, Me   */
+#define VERSION_IS_NT(x)  ((x & 0XFF) < 5) /* NT 4 or 3.51 */
+
+/*
+  Routine to find if process or thread is privileged
+*/
+
+enum {
+  CREATE_BIT = 1,
+  RESTORE_BIT = 2
+};
+
+static DWORD get_privileges ()
+{
+  char buffer[1024];
+  DWORD i, length;
+  HANDLE hToken = NULL;
+  PTOKEN_PRIVILEGES privs;
+  LUID cluid, rluid;
+  DWORD ret = 0;
+
+  privs = (PTOKEN_PRIVILEGES) buffer;
+
+  if (OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hToken)
+      && LookupPrivilegeValue (NULL, SE_CREATE_TOKEN_NAME, &cluid)
+      && LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &rluid)
+      && (GetTokenInformation( hToken, TokenPrivileges,
+                              privs, sizeof (buffer), &length)
+         || (GetLastError () == ERROR_INSUFFICIENT_BUFFER
+             && (privs = (PTOKEN_PRIVILEGES) alloca (length))
+             && GetTokenInformation(hToken, TokenPrivileges,
+                                    privs, length, &length)))) {
+    for (i = 0; i < privs->PrivilegeCount; i++) {
+      if (privs->Privileges[i].Luid.QuadPart == cluid.QuadPart)
+       ret |= CREATE_BIT;
+      else if (privs->Privileges[i].Luid.QuadPart == rluid.QuadPart)
+       ret |= RESTORE_BIT;
+      else continue;
+      if (ret == (CREATE_BIT | RESTORE_BIT))
+       break;
+    }
+  }
+  else
+    fprintf(stderr, "has_create_token_privilege %ld\n", GetLastError());
+
+  if (hToken)
+    CloseHandle(hToken);
+
+  return ret;
+}
+
+/* We use a special routine to initialize
+    cygwin_init is called from the OS_INIT macro in main(). */
+
+void cygwin_init(int argc, char ** argv, void * rup,
+                 void * eup, void * egp, void * cup)
+{
+  int i;
+  uid_t myuid, systemuid;
+  gid_t mygid, adminsgid;
+  struct passwd * pwp;
+  char *cygenv, win32_path[MAX_PATH];
+  SID(1, SystemSid, SECURITY_LOCAL_SYSTEM_RID);
+  SID(2, AdminsSid, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS);
+  DWORD priv_flags;
+
+  myuid = getuid();
+  mygid = getgid();
+  cygwin_WinVersion = GetVersion();
+  if ((cygenv = getenv("CYGWIN")) == NULL) cygenv = "";
+  /* Produce some debugging on stderr,
+     cannot yet use exim's debug functions.
+     Exim does not use -c and ignores -n.
+     Set lower priority for daemons */
+  for (i = 1; i < argc; i++) {
+    if (argv[i][0] == '-') {
+      if (argv[i][1] == 'c') {
+        argv[i][1] = 'n';  /* Replace -c by -n */
+        cygwin_debug = TRUE;
+        fprintf(stderr, "CYGWIN = \"%s\".", cygenv);
+        cygwin_conv_to_win32_path("/", win32_path);
+        fprintf(stderr, " Root / mapped to %s.\n", win32_path);
+      }
+      else if (argv[i][1] == 'b' && argv[i][2] == 'd')
+        setpriority();
+    }
+  }
+  if (VERSION_IS_58M(cygwin_WinVersion)) {
+    * (uid_t *) rup = myuid;  /* Pretend we are root */
+    * (uid_t *) eup = myuid;  /* ... and exim */
+    * (gid_t *) egp = mygid;
+    return;
+  }
+  /* Nt/2000/XP
+     We initially set the exim uid & gid to those of the "real exim",
+       or to the root uid (SYSTEM) and exim gid (ADMINS),
+     If privileged, we setuid to those.
+     We always set the configure uid to the system uid.
+     We always set the root uid to the real uid
+       to avoid useless execs following forks.
+     If not privileged and unable to chown,
+       we set the exim uid to our uid.
+     If unprivileged, we fake all subsequent setuid. */
+
+  priv_flags = get_privileges ();
+  privileged = !!(priv_flags & CREATE_BIT);
+
+  /* Get the system and admins uid from their sids,
+     or use the default values from the Makefile. */
+  if ((systemuid = cygwin_internal(CW_GET_UID_FROM_SID, & SystemSid)) == -1)
+    systemuid = * (uid_t *) eup;
+  if ((adminsgid = cygwin_internal(CW_GET_GID_FROM_SID, & AdminsSid)) == -1)
+    adminsgid = * (gid_t *) egp;
+
+  if ((pwp = getpwnam("exim")) != NULL) {
+    * (uid_t *) eup = pwp->pw_uid;  /* Set it according to passwd */
+    * (gid_t *) egp = pwp->pw_gid;
+  }
+  else {
+    * (uid_t *) eup = systemuid;
+    * (gid_t *) egp = adminsgid;
+  }
+
+  /* Set the configuration uid to the system uid.
+     Note that exim uid is also accepted as owner of exim.conf. */
+  * (uid_t *) cup = systemuid;
+
+  if (privileged) {             /* Can setuid */
+    if (cygwin_setgid(* (gid_t *) egp) /* Setuid to exim */
+        || cygwin_setuid(* (uid_t *) eup))
+      privileged = -1;         /* Problem... Perhaps not in 544 */
+  }
+
+  /* Pretend we are root to avoid useless execs.
+     We are limited by file access rights */
+  * (uid_t *) rup = getuid ();
+
+  /* If we have not setuid to exim and cannot chown,
+     set the exim uid to our uid to avoid chown failures */
+  if (privileged <= 0 && !(priv_flags & RESTORE_BIT))
+    * (uid_t *) eup = * (uid_t *) rup;
+
+  if (cygwin_debug) {
+    fprintf(stderr, "Starting uid %ld, gid %ld, ntsec %lu, privileged %d.\n",
+           myuid, mygid, cygwin_internal(CW_CHECK_NTSEC, NULL), privileged);
+    fprintf(stderr, "root_uid %ld, exim_uid %ld, exim_gid %ld, config_uid %ld.\n",
+           * (uid_t *) rup, * (uid_t *) eup, * (gid_t *) egp, * (uid_t *) cup);
+  }
+  return;
+}
+
+/*****************************************************************
+ *
+ Functions for average load measurements
+
+ Obtaining statistics in Windows is done at a low level by
+ calling registry functions, in particular the key
+ HKEY_PERFORMANCE_DATA on NT and successors.
+ Something equivalent exists on Win95, see Microsoft article
+ HOWTO: Access the Performance Registry Under Windows 95 (Q174631)
+ but it is not implemented here.
+
+ The list of objects to be polled is specified in the string
+ passed to RegQueryValueEx in ReadStat() below.
+ On NT, all objects are polled even if info about only one is
+ required. This is fixed in Windows 2000. See articles
+ INFO: Perflib Calling Close Procedure in Windows 2000 (Q270127)
+ INFO: Performance Data Changes Between Windows NT 4.0 and Windows
+ 2000 (Q296523)
+
+ It is unclear to me how the counters are primarily identified.
+ Whether it's by name strings or by the offset of their strings
+ as mapped in X:\Winnt\system32\perfc009.dat [or equivalently as
+ reported by the registry functions in GetNameStrings( ) below].
+ Microsoft documentation seems to say that both methods should
+ work.
+
+ In the interest of speed and language independence, the main
+ code below relies on offsets. However if debug is enabled, the
+ code verifies that the names of the corresponding strings are
+ as expected.
+
+*****************************************************************/
+#ifndef OS_LOAD_AVERAGE /* Can be set on command line */
+#define OS_LOAD_AVERAGE /* src/os.c need not provide it */
+
+/* Object and counter indices and names */
+#define PROCESSOR_OBJECT_INDEX 238
+#define PROCESSOR_OBJECT_STRING "238"
+#define PROCESSOR_OBJECT_NAME "Processor"
+#define PROCESSOR_TIME_COUNTER 6
+#define PROCESSOR_TIME_NAME "% Processor Time"
+
+/* Structure to compute the load average efficiently */
+static struct {
+  long long Time100ns;       /* Last measurement time */
+  long long IdleCount;       /* Latest cumulative idle time */
+  long long LastCounter;     /* Last measurement counter */
+  long long PerfFreq;        /* Perf counter frequency */
+  PPERF_DATA_BLOCK PerfData; /* Pointer to a buffer to get the data */
+  DWORD BufferSize;          /* Size of PerfData */
+  int LastLoad;              /* Last reported load, or -1 */
+  LPSTR * NamesArray;        /* Temporary (malloc) buffer for index */
+  BOOL Init;                 /* True if initialized */
+} cygwin_load = { 0, 0, 0, 0, NULL, 0, 0, NULL, FALSE};
+
+#define BYTEINCREMENT 800    /* Block to add to PerfData */
+
+/*****************************************************************
+ *
+ Macros to navigate through the performance data.
+
+ *****************************************************************/
+#define FirstObject(PerfData)\
+  ((PPERF_OBJECT_TYPE)((PBYTE)PerfData + PerfData->HeaderLength))
+#define NextObject(PerfObj)\
+  ((PPERF_OBJECT_TYPE)((PBYTE)PerfObj + PerfObj->TotalByteLength))
+#define ObjectCounterBlock(PerfObj)\
+  ((PPERF_COUNTER_BLOCK)(PBYTE)PerfObj + PerfObj->DefinitionLength )
+#define FirstInstance(PerfObj )\
+  ((PPERF_INSTANCE_DEFINITION)((PBYTE)PerfObj + PerfObj->DefinitionLength))
+#define InstanceCounterBlock(PerfInst)\
+  ((PPERF_COUNTER_BLOCK) ((PBYTE)PerfInst + PerfInst->ByteLength ))
+#define NextInstance(PerfInst )\
+  ((PPERF_INSTANCE_DEFINITION)((PBYTE)InstanceCounterBlock(PerfInst) + \
+        InstanceCounterBlock(PerfInst)->ByteLength) )
+#define FirstCounter(PerfObj)\
+  ((PPERF_COUNTER_DEFINITION) ((PBYTE)PerfObj + PerfObj->HeaderLength))
+#define NextCounter(PerfCntr)\
+  ((PPERF_COUNTER_DEFINITION)((PBYTE)PerfCntr + PerfCntr->ByteLength))
+
+/*****************************************************************
+ *
+ Load the counter and object names from the registry
+ to cygwin_load.NameStrings
+ and index them in cygwin_load.NamesArray
+
+ NameStrings seems to be taken from the file
+ X:\Winnt\system32\perfc009.dat
+
+ This is used only for name verification during initialization,
+ if DEBUG(D_load) is TRUE.
+
+*****************************************************************/
+static BOOL GetNameStrings( )
+{
+  HKEY hKeyPerflib;      // handle to registry key
+  DWORD dwArraySize;     // size for array
+  DWORD dwNamesSize;     // size for strings
+  LPSTR lpCurrentString; // pointer for enumerating data strings
+  DWORD dwCounter;       // current counter index
+  LONG  res;
+
+  /* Get the number of Counter items into dwArraySize. */
+  if ((res = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
+                          "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib",
+                          0,
+                          KEY_QUERY_VALUE, /* KEY_READ, */
+                          &hKeyPerflib))
+      != ERROR_SUCCESS) {
+    DEBUG(D_load) debug_printf("RegOpenKeyEx (1): error %ld (Windows)\n", res);
+    return FALSE;
+  }
+  dwNamesSize = sizeof(dwArraySize); /* Temporary reuse */
+  if ((res = RegQueryValueEx( hKeyPerflib,
+                             "Last Counter",
+                             NULL,
+                             NULL,
+                             (LPBYTE) &dwArraySize,
+                             &dwNamesSize ))
+      != ERROR_SUCCESS) {
+    DEBUG(D_load) debug_printf("RegQueryValueEx (1): error %ld (Windows)\n", res);
+    return FALSE;
+  }
+  RegCloseKey( hKeyPerflib );
+  /* Open the key containing the counter and object names. */
+  if ((res = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
+                          "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009",
+                          0,
+                          KEY_READ,
+                          &hKeyPerflib))
+      != ERROR_SUCCESS) {
+    DEBUG(D_load) debug_printf("RegOpenKeyEx (2): error %ld (Windows)\n", res);
+    return FALSE;
+  }
+  /* Get the size of the Counter value in the key
+     and then read the value in the tail of NamesArray */
+  dwNamesSize = 0;
+  lpCurrentString = NULL;
+  while (1) {
+    res = RegQueryValueEx( hKeyPerflib,
+                          "Counter",
+                          NULL,
+                          NULL,
+                          (unsigned char *) lpCurrentString,
+                          &dwNamesSize);
+    if ((res == ERROR_SUCCESS) && /* Bug (NT 4.0): SUCCESS was returned on first call */
+       (cygwin_load.NamesArray != NULL)) break;
+    if ((res == ERROR_SUCCESS) || /* but cygwin_load.NamesArrays == NULL */
+        (res == ERROR_MORE_DATA)) {
+      /* Allocate memory BOTH for the names array and for the counter and object names */
+      if ((cygwin_load.NamesArray =
+          (LPSTR *) malloc( (dwArraySize + 1) * sizeof(LPSTR) + dwNamesSize * sizeof(CHAR)))
+         != NULL) {
+        /* Point to area for the counter and object names */
+       lpCurrentString = (LPSTR) & cygwin_load.NamesArray[dwArraySize + 1];
+        continue;
+      }
+      DEBUG(D_load) debug_printf("Malloc: errno %d (%s)\n", errno, strerror(errno));
+    }
+    else { /* Serious error */
+      DEBUG(D_load) debug_printf("RegQueryValueEx (2): error %ld (Windows)\n", res);
+    }
+    return FALSE;
+  }
+  RegCloseKey( hKeyPerflib );
+  /* Index the names into an array. */
+  while (*lpCurrentString) {
+    dwCounter = atol( lpCurrentString );
+    lpCurrentString += (lstrlen(lpCurrentString)+1);
+    cygwin_load.NamesArray[dwCounter] = lpCurrentString;
+    lpCurrentString += (strlen(lpCurrentString)+1);
+  }
+  return TRUE;
+}
+
+/*****************************************************************
+ *
+ Find the value of the Processor Time counter
+
+*****************************************************************/
+static BOOL ReadTimeCtr(PPERF_OBJECT_TYPE PerfObj,
+                       PPERF_COUNTER_DEFINITION CurCntr,
+                       PPERF_COUNTER_BLOCK PtrToCntr,
+                       unsigned long long * TimePtr){
+  int j;
+  /* Scan all counters. */
+  for( j = 0; j < PerfObj->NumCounters; j++ ) {
+    if (CurCntr->CounterNameTitleIndex == PROCESSOR_TIME_COUNTER) {
+      /* Verify it is really the proc time counter */
+      if ((CurCntr->CounterType != PERF_100NSEC_TIMER_INV) || /* Wrong type */
+         ((cygwin_load.NamesArray != NULL) &&                /* Verify name */
+          (strcmp(cygwin_load.NamesArray[CurCntr->CounterNameTitleIndex],
+                  PROCESSOR_TIME_NAME)))) {
+       log_write(0, LOG_MAIN|LOG_PANIC,
+                  "Incorrect Perf counter type or name %x %s",
+                 (unsigned) CurCntr->CounterType,
+                 cygwin_load.NamesArray[CurCntr->CounterNameTitleIndex]);
+       return FALSE;
+      }
+      *TimePtr += *(unsigned long long int *) ((PBYTE) PtrToCntr + CurCntr->CounterOffset);
+      return TRUE; /* return TRUE as soon as we found the counter */
+    }
+    /* Get the next counter. */
+    CurCntr = NextCounter( CurCntr );
+  }
+  return FALSE;
+}
+/*****************************************************************
+ *
+ ReadStat()
+ Measures current Time100ns and IdleCount
+ Return TRUE if success.
+
+ *****************************************************************/
+static BOOL ReadStat(long long int *Time100nsPtr,
+                                    long long int * IdleCountPtr)
+{
+  PPERF_OBJECT_TYPE PerfObj;
+  PPERF_INSTANCE_DEFINITION PerfInst;
+  PPERF_COUNTER_DEFINITION PerfCntr;
+  PPERF_COUNTER_BLOCK PtrToCntr;
+  DWORD i, k, res;
+
+  /* Get the performance data for the Processor object
+     There is no need to open a key.
+     We may need to blindly increase the buffer size.
+     BufferSize does not return info but may be changed */
+  while (1) {
+    DWORD BufferSize = cygwin_load.BufferSize;
+    res = RegQueryValueEx( HKEY_PERFORMANCE_DATA,
+                          PROCESSOR_OBJECT_STRING,
+                          NULL,
+                          NULL,
+                          (LPBYTE) cygwin_load.PerfData,
+                          &BufferSize );
+    if (res == ERROR_SUCCESS) break;
+    if (res == ERROR_MORE_DATA ) {
+      /* Increment if necessary to get a buffer that is big enough. */
+      cygwin_load.BufferSize += BYTEINCREMENT;
+      if ((cygwin_load.PerfData =
+          (PPERF_DATA_BLOCK) realloc( cygwin_load.PerfData, cygwin_load.BufferSize ))
+         != NULL) continue;
+      DEBUG(D_load) debug_printf("Malloc: errno %d (%s)\n", errno, strerror(errno));
+    }
+    else { /* Serious error */
+      DEBUG(D_load) debug_printf("RegQueryValueEx (3): error %ld (Windows)\n", res);
+    }
+    return FALSE;
+  }
+  /* Initialize the counters */
+  *Time100nsPtr = 0;
+  *IdleCountPtr = 0;
+  /* We should only have one object, but write general code just in case. */
+  PerfObj = FirstObject( cygwin_load.PerfData );
+  for( i = 0; i < cygwin_load.PerfData->NumObjectTypes; i++ ) {
+    /* We are only interested in the processor object */
+    if ( PerfObj->ObjectNameTitleIndex == PROCESSOR_OBJECT_INDEX) {
+      /* Possibly verify it is really the Processor object. */
+      if ((cygwin_load.NamesArray != NULL) &&
+         (strcmp(cygwin_load.NamesArray[PerfObj->ObjectNameTitleIndex],
+                 PROCESSOR_OBJECT_NAME))) {
+       log_write(0, LOG_MAIN|LOG_PANIC,
+                 "Incorrect Perf object name %s",
+                 cygwin_load.NamesArray[PerfObj->ObjectNameTitleIndex]);
+       return FALSE;
+      }
+      /* Get the first counter */
+      PerfCntr = FirstCounter( PerfObj );
+      /* See if the object has instances.
+        It should, but write general code. */
+      if( PerfObj->NumInstances != PERF_NO_INSTANCES ) {
+       PerfInst = FirstInstance( PerfObj );
+       for( k = 0; k < PerfObj->NumInstances; k++ ) {
+         /* There can be several processors.
+             Accumulate both the Time100ns and the idle counter.
+             On Win 2000 I have seen an instance named "_Total".
+            Do not use it.     We only use instances with a single
+             character in the name.
+             If we examine the object names, we also look at the instance
+             names and their lengths and issue reports */
+         if ( cygwin_load.NamesArray != NULL) {
+            CHAR ascii[30]; /* The name is in unicode */
+           wsprintf(ascii,"%.29lS",
+                    (char *)((PBYTE)PerfInst + PerfInst->NameOffset));
+           log_write(0, LOG_MAIN,
+                      "Perf: Found processor instance \"%s\", length %d",
+                      ascii, PerfInst->NameLength);
+           if ((PerfInst->NameLength != 4) &&
+                (strcmp(ascii, "_Total") != 0)) {
+             log_write(0, LOG_MAIN|LOG_PANIC,
+                        "Perf: WARNING: Unexpected processor instance name");
+             return FALSE;
+            }
+          }
+         if (PerfInst->NameLength == 4) {
+            *Time100nsPtr += cygwin_load.PerfData->PerfTime100nSec.QuadPart;
+           PtrToCntr = InstanceCounterBlock(PerfInst);
+           if (! ReadTimeCtr(PerfObj, PerfCntr, PtrToCntr, IdleCountPtr)) {
+             return FALSE;
+           }
+          }
+         PerfInst = NextInstance( PerfInst );
+       }
+        return (*Time100nsPtr != 0); /* Something was read */
+      }
+      else { /* No instance, just the counter data */
+       *Time100nsPtr = cygwin_load.PerfData->PerfTime100nSec.QuadPart;
+       PtrToCntr = ObjectCounterBlock(PerfObj);
+       return ReadTimeCtr(PerfObj, PerfCntr, PtrToCntr, IdleCountPtr);
+      }
+    }
+    PerfObj = NextObject( PerfObj );
+  }
+  return FALSE; /* Did not find the Processor object */
+}
+
+/*****************************************************************
+ *
+ InitLoadAvg()
+ Initialize the cygwin_load structure.
+ and set cygwin_load.Flag to TRUE if successful.
+ This is called the first time os_getloadavg is called
+ *****************************************************************/
+static void InitLoadAvg()
+{
+  BOOL success = TRUE;
+  cygwin_load.Init = TRUE;        /* We have run */
+  /* Get perf frequency and counter */
+  QueryPerformanceFrequency((LARGE_INTEGER *)& cygwin_load.PerfFreq);
+  QueryPerformanceCounter((LARGE_INTEGER *)& cygwin_load.LastCounter);
+  DEBUG(D_load) {
+    /* Get the name strings through the registry
+       to verify that the object and counter numbers
+       have the names we expect */
+    success = GetNameStrings();
+  }
+  /* Get initial values for Time100ns and IdleCount
+     and possibly verify the names */
+  //  success = success &&
+  success = ReadStat( & cygwin_load.Time100ns,
+                     & cygwin_load.IdleCount);
+  /* If success, set the Load to 0, else to -1 */
+  if (success) cygwin_load.LastLoad = 0;
+  else {
+    log_write(0, LOG_MAIN, "Cannot obtain Load Average");
+    cygwin_load.LastLoad = -1;
+  }
+  /* Free the buffer created for debug name verification */
+  if (cygwin_load.NamesArray != NULL) {
+    free(cygwin_load.NamesArray);
+    cygwin_load.NamesArray = NULL;
+  }
+}
+/*****************************************************************
+ *
+ os_getloadavg()
+
+ Return -1 if not available;
+ Return the previous value if less than AVERAGING sec old.
+ else return the processor load on a [0 - 1000] scale.
+
+ The first time we are called we initialize the counts
+ and return 0 or -1.
+ The load cannot be measured because we use the processor 100%
+*****************************************************************/
+#define AVERAGING 10
+int os_getloadavg()
+{
+  long long Time100ns, IdleCount, CurrCounter;
+  int value;
+
+  if (! cygwin_load.Init) InitLoadAvg();
+  else if (cygwin_load.LastLoad >= 0) { /* Initialized OK */
+    /* Get the current time (PerfCounter) */
+    QueryPerformanceCounter((LARGE_INTEGER *)& CurrCounter);
+    /* Calls closer than AVERAGING sec apart use the previous value */
+    if (CurrCounter - cygwin_load.LastCounter >
+       AVERAGING * cygwin_load.PerfFreq) {
+      /* Get Time100ns and IdleCount */
+      if (ReadStat( & Time100ns, & IdleCount)) { /* Success */
+       /* Return processor load on 1000 scale */
+       value = 1000 - ((1000 * (IdleCount - cygwin_load.IdleCount)) /
+                       (Time100ns - cygwin_load.Time100ns));
+       cygwin_load.Time100ns = Time100ns;
+       cygwin_load.IdleCount = IdleCount;
+       cygwin_load.LastCounter = CurrCounter;
+       cygwin_load.LastLoad = value;
+      }
+      else { /* Something bad happened.
+               Refuse to measure the load anymore
+               but don't bother releasing the buffer */
+        log_write(0, LOG_MAIN, "Cannot obtain Load Average");
+       cygwin_load.LastLoad = -1;
+      }
+    }
+  }
+  DEBUG(D_load)
+    debug_printf("Perf: load average = %d\n", cygwin_load.LastLoad);
+  return cygwin_load.LastLoad;
+}
+#endif /* OS_LOAD_AVERAGE */
+#endif /* COMPILE_UTILITY */