Credit Wouter Verhelst with V1.35
[users/heiko/exim.git] / src / OS / os.c-cygwin
1 /* $Cambridge: exim/src/OS/os.c-cygwin,v 1.1 2004/10/06 15:07:39 ph10 Exp $ */
2
3 /*************************************************
4 *     Exim - an Internet mail transport agent    *
5 *************************************************/
6
7 /* Cygwin-specific code. December 2002
8    This is concatenated onto the generic src/os.c file.
9
10    This code was supplied by Pierre A. Humblet <Pierre.Humblet@ieee.org>
11 */
12
13 /* We need a special mkdir that
14    allows names starting with // */
15 #undef mkdir
16 int cygwin_mkdir( const char *path, mode_t mode )
17 {
18   const char * p = path;
19   if (*p == '/') while(*(p+1) == '/') p++;
20   return mkdir(p, mode);
21 }
22
23 /* We have strsignal but cannot use #define
24    because types don't match */
25 #define OS_STRSIGNAL /* src/os.c need not provide it */
26 char * os_strsignal(int sig)
27 {
28   return (char *) strsignal(sig);
29 }
30
31 #ifndef COMPILE_UTILITY /* Utilities don't need special code */
32 #ifdef INCLUDE_MINIRES
33 #include "../minires/minires.c"
34 #include "../minires/os-interface.c"
35 #endif
36
37 #ifdef INCLUDE_PAM
38 #include "../pam/pam.c"
39 #endif
40
41 unsigned int cygwin_WinVersion;
42
43 /* Conflict between Windows definitions and others */
44 #ifdef NOERROR
45 #undef NOERROR
46 #endif
47 #ifdef DELETE
48 #undef DELETE
49 #endif
50
51 #include <windows.h>
52 #include <sys/cygwin.h>
53
54 /* Special static variables */
55 static BOOL cygwin_debug = FALSE;
56 static int privileged = 1; /* when not privileged, setuid = noop */
57
58 #undef setuid
59 int cygwin_setuid(uid_t uid )
60 {
61   int res;
62   if (privileged <= 0) return 0;
63   else {
64     res = setuid(uid);
65     if (cygwin_debug)
66       fprintf(stderr, "setuid %lu %lu %d pid: %d\n",
67               uid, getuid(),res, getpid());
68   }
69   return res;
70 }
71
72 #undef setgid
73 int cygwin_setgid(gid_t gid )
74 {
75   int res;
76   if (privileged <= 0) return 0;
77   else {
78     res = setgid(gid);
79     if (cygwin_debug)
80       fprintf(stderr, "setgid %lu %lu %d pid: %d\n",
81               gid, getgid(), res, getpid());
82   }
83   return res;
84 }
85
86 /* Background processes run at lower priority */
87 static void setpriority()
88 {
89   if (!SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS))
90     SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS);
91   return;
92 }
93
94
95 /* GetVersion()
96    MSB: 1 for 95/98/ME; Next 7: build number, except for 95/98/ME
97    Next byte: 0
98    Next byte: minor version of OS
99    Low  byte: major version of OS (3 or 4 for for NT, 5 for 2000 and XP) */
100 #define VERSION_IS_58M(x) (x & 0x80000000) /* 95, 98, Me   */
101 #define VERSION_IS_NT(x)  ((x & 0XFF) < 5) /* NT 4 or 3.51 */
102
103 /*
104   Routine to find if process or thread is privileged
105 */
106
107 enum {
108   CREATE_BIT = 1,
109   RESTORE_BIT = 2
110 };
111
112 static DWORD get_privileges ()
113 {
114   char buffer[1024];
115   DWORD i, length;
116   HANDLE hToken = NULL;
117   PTOKEN_PRIVILEGES privs;
118   LUID cluid, rluid;
119   DWORD ret = 0;
120
121   privs = (PTOKEN_PRIVILEGES) buffer;
122
123   if (OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hToken)
124       && LookupPrivilegeValue (NULL, SE_CREATE_TOKEN_NAME, &cluid)
125       && LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &rluid)
126       && (GetTokenInformation( hToken, TokenPrivileges,
127                                privs, sizeof (buffer), &length)
128           || (GetLastError () == ERROR_INSUFFICIENT_BUFFER
129               && (privs = (PTOKEN_PRIVILEGES) alloca (length))
130               && GetTokenInformation(hToken, TokenPrivileges,
131                                      privs, length, &length)))) {
132     for (i = 0; i < privs->PrivilegeCount; i++) {
133       if (privs->Privileges[i].Luid.QuadPart == cluid.QuadPart)
134         ret |= CREATE_BIT;
135       else if (privs->Privileges[i].Luid.QuadPart == rluid.QuadPart)
136         ret |= RESTORE_BIT;
137       else continue;
138       if (ret == (CREATE_BIT | RESTORE_BIT))
139         break;
140     }
141   }
142   else
143     fprintf(stderr, "has_create_token_privilege %ld\n", GetLastError());
144
145   if (hToken)
146     CloseHandle(hToken);
147
148   return ret;
149 }
150
151 /* We use a special routine to initialize
152     cygwin_init is called from the OS_INIT macro in main(). */
153
154 void cygwin_init(int argc, char ** argv, void * rup,
155                  void * eup, void * egp, void * cup)
156 {
157   int i;
158   uid_t myuid, systemuid;
159   gid_t mygid, adminsgid;
160   struct passwd * pwp;
161   char *cygenv, win32_path[MAX_PATH];
162   SID(1, SystemSid, SECURITY_LOCAL_SYSTEM_RID);
163   SID(2, AdminsSid, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS);
164   DWORD priv_flags;
165
166   myuid = getuid();
167   mygid = getgid();
168   cygwin_WinVersion = GetVersion();
169   if ((cygenv = getenv("CYGWIN")) == NULL) cygenv = "";
170   /* Produce some debugging on stderr,
171      cannot yet use exim's debug functions.
172      Exim does not use -c and ignores -n.
173      Set lower priority for daemons */
174   for (i = 1; i < argc; i++) {
175     if (argv[i][0] == '-') {
176       if (argv[i][1] == 'c') {
177         argv[i][1] = 'n';  /* Replace -c by -n */
178         cygwin_debug = TRUE;
179         fprintf(stderr, "CYGWIN = \"%s\".", cygenv);
180         cygwin_conv_to_win32_path("/", win32_path);
181         fprintf(stderr, " Root / mapped to %s.\n", win32_path);
182       }
183       else if (argv[i][1] == 'b' && argv[i][2] == 'd')
184         setpriority();
185     }
186   }
187   if (VERSION_IS_58M(cygwin_WinVersion)) {
188     * (uid_t *) rup = myuid;  /* Pretend we are root */
189     * (uid_t *) eup = myuid;  /* ... and exim */
190     * (gid_t *) egp = mygid;
191     return;
192   }
193   /* Nt/2000/XP
194      We initially set the exim uid & gid to those of the "real exim",
195        or to the root uid (SYSTEM) and exim gid (ADMINS),
196      If privileged, we setuid to those.
197      We always set the configure uid to the system uid.
198      We always set the root uid to the real uid
199        to avoid useless execs following forks.
200      If not privileged and unable to chown,
201        we set the exim uid to our uid.
202      If unprivileged, we fake all subsequent setuid. */
203
204   priv_flags = get_privileges ();
205   privileged = !!(priv_flags & CREATE_BIT);
206
207   /* Get the system and admins uid from their sids,
208      or use the default values from the Makefile. */
209   if ((systemuid = cygwin_internal(CW_GET_UID_FROM_SID, & SystemSid)) == -1)
210     systemuid = * (uid_t *) eup;
211   if ((adminsgid = cygwin_internal(CW_GET_GID_FROM_SID, & AdminsSid)) == -1)
212     adminsgid = * (gid_t *) egp;
213
214   if ((pwp = getpwnam("exim")) != NULL) {
215     * (uid_t *) eup = pwp->pw_uid;  /* Set it according to passwd */
216     * (gid_t *) egp = pwp->pw_gid;
217   }
218   else {
219     * (uid_t *) eup = systemuid;
220     * (gid_t *) egp = adminsgid;
221   }
222
223   /* Set the configuration uid to the system uid.
224      Note that exim uid is also accepted as owner of exim.conf. */
225   * (uid_t *) cup = systemuid;
226
227   if (privileged) {             /* Can setuid */
228     if (cygwin_setgid(* (gid_t *) egp) /* Setuid to exim */
229         || cygwin_setuid(* (uid_t *) eup))
230       privileged = -1;          /* Problem... Perhaps not in 544 */
231   }
232
233   /* Pretend we are root to avoid useless execs.
234      We are limited by file access rights */
235   * (uid_t *) rup = getuid ();
236
237   /* If we have not setuid to exim and cannot chown,
238      set the exim uid to our uid to avoid chown failures */
239   if (privileged <= 0 && !(priv_flags & RESTORE_BIT))
240     * (uid_t *) eup = * (uid_t *) rup;
241
242   if (cygwin_debug) {
243     fprintf(stderr, "Starting uid %ld, gid %ld, ntsec %lu, privileged %d.\n",
244             myuid, mygid, cygwin_internal(CW_CHECK_NTSEC, NULL), privileged);
245     fprintf(stderr, "root_uid %ld, exim_uid %ld, exim_gid %ld, config_uid %ld.\n",
246             * (uid_t *) rup, * (uid_t *) eup, * (gid_t *) egp, * (uid_t *) cup);
247   }
248   return;
249 }
250
251 /*****************************************************************
252  *
253  Functions for average load measurements
254
255  Obtaining statistics in Windows is done at a low level by
256  calling registry functions, in particular the key
257  HKEY_PERFORMANCE_DATA on NT and successors.
258  Something equivalent exists on Win95, see Microsoft article
259  HOWTO: Access the Performance Registry Under Windows 95 (Q174631)
260  but it is not implemented here.
261
262  The list of objects to be polled is specified in the string
263  passed to RegQueryValueEx in ReadStat() below.
264  On NT, all objects are polled even if info about only one is
265  required. This is fixed in Windows 2000. See articles
266  INFO: Perflib Calling Close Procedure in Windows 2000 (Q270127)
267  INFO: Performance Data Changes Between Windows NT 4.0 and Windows
268  2000 (Q296523)
269
270  It is unclear to me how the counters are primarily identified.
271  Whether it's by name strings or by the offset of their strings
272  as mapped in X:\Winnt\system32\perfc009.dat [or equivalently as
273  reported by the registry functions in GetNameStrings( ) below].
274  Microsoft documentation seems to say that both methods should
275  work.
276
277  In the interest of speed and language independence, the main
278  code below relies on offsets. However if debug is enabled, the
279  code verifies that the names of the corresponding strings are
280  as expected.
281
282 *****************************************************************/
283 #ifndef OS_LOAD_AVERAGE /* Can be set on command line */
284 #define OS_LOAD_AVERAGE /* src/os.c need not provide it */
285
286 /* Object and counter indices and names */
287 #define PROCESSOR_OBJECT_INDEX 238
288 #define PROCESSOR_OBJECT_STRING "238"
289 #define PROCESSOR_OBJECT_NAME "Processor"
290 #define PROCESSOR_TIME_COUNTER 6
291 #define PROCESSOR_TIME_NAME "% Processor Time"
292
293 /* Structure to compute the load average efficiently */
294 static struct {
295   long long Time100ns;       /* Last measurement time */
296   long long IdleCount;       /* Latest cumulative idle time */
297   long long LastCounter;     /* Last measurement counter */
298   long long PerfFreq;        /* Perf counter frequency */
299   PPERF_DATA_BLOCK PerfData; /* Pointer to a buffer to get the data */
300   DWORD BufferSize;          /* Size of PerfData */
301   int LastLoad;              /* Last reported load, or -1 */
302   LPSTR * NamesArray;        /* Temporary (malloc) buffer for index */
303   BOOL Init;                 /* True if initialized */
304 } cygwin_load = { 0, 0, 0, 0, NULL, 0, 0, NULL, FALSE};
305
306 #define BYTEINCREMENT 800    /* Block to add to PerfData */
307
308 /*****************************************************************
309  *
310  Macros to navigate through the performance data.
311
312  *****************************************************************/
313 #define FirstObject(PerfData)\
314   ((PPERF_OBJECT_TYPE)((PBYTE)PerfData + PerfData->HeaderLength))
315 #define NextObject(PerfObj)\
316   ((PPERF_OBJECT_TYPE)((PBYTE)PerfObj + PerfObj->TotalByteLength))
317 #define ObjectCounterBlock(PerfObj)\
318   ((PPERF_COUNTER_BLOCK)(PBYTE)PerfObj + PerfObj->DefinitionLength )
319 #define FirstInstance(PerfObj )\
320   ((PPERF_INSTANCE_DEFINITION)((PBYTE)PerfObj + PerfObj->DefinitionLength))
321 #define InstanceCounterBlock(PerfInst)\
322   ((PPERF_COUNTER_BLOCK) ((PBYTE)PerfInst + PerfInst->ByteLength ))
323 #define NextInstance(PerfInst )\
324   ((PPERF_INSTANCE_DEFINITION)((PBYTE)InstanceCounterBlock(PerfInst) + \
325         InstanceCounterBlock(PerfInst)->ByteLength) )
326 #define FirstCounter(PerfObj)\
327   ((PPERF_COUNTER_DEFINITION) ((PBYTE)PerfObj + PerfObj->HeaderLength))
328 #define NextCounter(PerfCntr)\
329   ((PPERF_COUNTER_DEFINITION)((PBYTE)PerfCntr + PerfCntr->ByteLength))
330
331 /*****************************************************************
332  *
333  Load the counter and object names from the registry
334  to cygwin_load.NameStrings
335  and index them in cygwin_load.NamesArray
336
337  NameStrings seems to be taken from the file
338  X:\Winnt\system32\perfc009.dat
339
340  This is used only for name verification during initialization,
341  if DEBUG(D_load) is TRUE.
342
343 *****************************************************************/
344 static BOOL GetNameStrings( )
345 {
346   HKEY hKeyPerflib;      // handle to registry key
347   DWORD dwArraySize;     // size for array
348   DWORD dwNamesSize;     // size for strings
349   LPSTR lpCurrentString; // pointer for enumerating data strings
350   DWORD dwCounter;       // current counter index
351   LONG  res;
352
353   /* Get the number of Counter items into dwArraySize. */
354   if ((res = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
355                            "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib",
356                            0,
357                            KEY_QUERY_VALUE, /* KEY_READ, */
358                            &hKeyPerflib))
359       != ERROR_SUCCESS) {
360     DEBUG(D_load) debug_printf("RegOpenKeyEx (1): error %ld (Windows)\n", res);
361     return FALSE;
362   }
363   dwNamesSize = sizeof(dwArraySize); /* Temporary reuse */
364   if ((res = RegQueryValueEx( hKeyPerflib,
365                               "Last Counter",
366                               NULL,
367                               NULL,
368                               (LPBYTE) &dwArraySize,
369                               &dwNamesSize ))
370       != ERROR_SUCCESS) {
371     DEBUG(D_load) debug_printf("RegQueryValueEx (1): error %ld (Windows)\n", res);
372     return FALSE;
373   }
374   RegCloseKey( hKeyPerflib );
375   /* Open the key containing the counter and object names. */
376   if ((res = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
377                            "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009",
378                            0,
379                            KEY_READ,
380                            &hKeyPerflib))
381       != ERROR_SUCCESS) {
382     DEBUG(D_load) debug_printf("RegOpenKeyEx (2): error %ld (Windows)\n", res);
383     return FALSE;
384   }
385   /* Get the size of the Counter value in the key
386      and then read the value in the tail of NamesArray */
387   dwNamesSize = 0;
388   lpCurrentString = NULL;
389   while (1) {
390     res = RegQueryValueEx( hKeyPerflib,
391                            "Counter",
392                            NULL,
393                            NULL,
394                            (unsigned char *) lpCurrentString,
395                            &dwNamesSize);
396     if ((res == ERROR_SUCCESS) && /* Bug (NT 4.0): SUCCESS was returned on first call */
397         (cygwin_load.NamesArray != NULL)) break;
398     if ((res == ERROR_SUCCESS) || /* but cygwin_load.NamesArrays == NULL */
399         (res == ERROR_MORE_DATA)) {
400       /* Allocate memory BOTH for the names array and for the counter and object names */
401       if ((cygwin_load.NamesArray =
402            (LPSTR *) malloc( (dwArraySize + 1) * sizeof(LPSTR) + dwNamesSize * sizeof(CHAR)))
403           != NULL) {
404         /* Point to area for the counter and object names */
405         lpCurrentString = (LPSTR) & cygwin_load.NamesArray[dwArraySize + 1];
406         continue;
407       }
408       DEBUG(D_load) debug_printf("Malloc: errno %d (%s)\n", errno, strerror(errno));
409     }
410     else { /* Serious error */
411       DEBUG(D_load) debug_printf("RegQueryValueEx (2): error %ld (Windows)\n", res);
412     }
413     return FALSE;
414   }
415   RegCloseKey( hKeyPerflib );
416   /* Index the names into an array. */
417   while (*lpCurrentString) {
418     dwCounter = atol( lpCurrentString );
419     lpCurrentString += (lstrlen(lpCurrentString)+1);
420     cygwin_load.NamesArray[dwCounter] = lpCurrentString;
421     lpCurrentString += (strlen(lpCurrentString)+1);
422   }
423   return TRUE;
424 }
425
426 /*****************************************************************
427  *
428  Find the value of the Processor Time counter
429
430 *****************************************************************/
431 static BOOL ReadTimeCtr(PPERF_OBJECT_TYPE PerfObj,
432                         PPERF_COUNTER_DEFINITION CurCntr,
433                         PPERF_COUNTER_BLOCK PtrToCntr,
434                         unsigned long long * TimePtr){
435   int j;
436   /* Scan all counters. */
437   for( j = 0; j < PerfObj->NumCounters; j++ ) {
438     if (CurCntr->CounterNameTitleIndex == PROCESSOR_TIME_COUNTER) {
439       /* Verify it is really the proc time counter */
440       if ((CurCntr->CounterType != PERF_100NSEC_TIMER_INV) || /* Wrong type */
441           ((cygwin_load.NamesArray != NULL) &&                /* Verify name */
442            (strcmp(cygwin_load.NamesArray[CurCntr->CounterNameTitleIndex],
443                    PROCESSOR_TIME_NAME)))) {
444         log_write(0, LOG_MAIN|LOG_PANIC,
445                   "Incorrect Perf counter type or name %x %s",
446                   (unsigned) CurCntr->CounterType,
447                   cygwin_load.NamesArray[CurCntr->CounterNameTitleIndex]);
448         return FALSE;
449       }
450       *TimePtr += *(unsigned long long int *) ((PBYTE) PtrToCntr + CurCntr->CounterOffset);
451       return TRUE; /* return TRUE as soon as we found the counter */
452     }
453     /* Get the next counter. */
454     CurCntr = NextCounter( CurCntr );
455   }
456   return FALSE;
457 }
458 /*****************************************************************
459  *
460  ReadStat()
461  Measures current Time100ns and IdleCount
462  Return TRUE if success.
463
464  *****************************************************************/
465 static BOOL ReadStat(long long int *Time100nsPtr,
466                                      long long int * IdleCountPtr)
467 {
468   PPERF_OBJECT_TYPE PerfObj;
469   PPERF_INSTANCE_DEFINITION PerfInst;
470   PPERF_COUNTER_DEFINITION PerfCntr;
471   PPERF_COUNTER_BLOCK PtrToCntr;
472   DWORD i, k, res;
473
474   /* Get the performance data for the Processor object
475      There is no need to open a key.
476      We may need to blindly increase the buffer size.
477      BufferSize does not return info but may be changed */
478   while (1) {
479     DWORD BufferSize = cygwin_load.BufferSize;
480     res = RegQueryValueEx( HKEY_PERFORMANCE_DATA,
481                            PROCESSOR_OBJECT_STRING,
482                            NULL,
483                            NULL,
484                            (LPBYTE) cygwin_load.PerfData,
485                            &BufferSize );
486     if (res == ERROR_SUCCESS) break;
487     if (res == ERROR_MORE_DATA ) {
488       /* Increment if necessary to get a buffer that is big enough. */
489       cygwin_load.BufferSize += BYTEINCREMENT;
490       if ((cygwin_load.PerfData =
491            (PPERF_DATA_BLOCK) realloc( cygwin_load.PerfData, cygwin_load.BufferSize ))
492           != NULL) continue;
493       DEBUG(D_load) debug_printf("Malloc: errno %d (%s)\n", errno, strerror(errno));
494     }
495     else { /* Serious error */
496       DEBUG(D_load) debug_printf("RegQueryValueEx (3): error %ld (Windows)\n", res);
497     }
498     return FALSE;
499   }
500   /* Initialize the counters */
501   *Time100nsPtr = 0;
502   *IdleCountPtr = 0;
503   /* We should only have one object, but write general code just in case. */
504   PerfObj = FirstObject( cygwin_load.PerfData );
505   for( i = 0; i < cygwin_load.PerfData->NumObjectTypes; i++ ) {
506     /* We are only interested in the processor object */
507     if ( PerfObj->ObjectNameTitleIndex == PROCESSOR_OBJECT_INDEX) {
508       /* Possibly verify it is really the Processor object. */
509       if ((cygwin_load.NamesArray != NULL) &&
510           (strcmp(cygwin_load.NamesArray[PerfObj->ObjectNameTitleIndex],
511                   PROCESSOR_OBJECT_NAME))) {
512         log_write(0, LOG_MAIN|LOG_PANIC,
513                   "Incorrect Perf object name %s",
514                   cygwin_load.NamesArray[PerfObj->ObjectNameTitleIndex]);
515         return FALSE;
516       }
517       /* Get the first counter */
518       PerfCntr = FirstCounter( PerfObj );
519       /* See if the object has instances.
520          It should, but write general code. */
521       if( PerfObj->NumInstances != PERF_NO_INSTANCES ) {
522         PerfInst = FirstInstance( PerfObj );
523         for( k = 0; k < PerfObj->NumInstances; k++ ) {
524           /* There can be several processors.
525              Accumulate both the Time100ns and the idle counter.
526              On Win 2000 I have seen an instance named "_Total".
527              Do not use it.     We only use instances with a single
528              character in the name.
529              If we examine the object names, we also look at the instance
530              names and their lengths and issue reports */
531           if ( cygwin_load.NamesArray != NULL) {
532             CHAR ascii[30]; /* The name is in unicode */
533             wsprintf(ascii,"%.29lS",
534                      (char *)((PBYTE)PerfInst + PerfInst->NameOffset));
535             log_write(0, LOG_MAIN,
536                       "Perf: Found processor instance \"%s\", length %d",
537                       ascii, PerfInst->NameLength);
538             if ((PerfInst->NameLength != 4) &&
539                 (strcmp(ascii, "_Total") != 0)) {
540               log_write(0, LOG_MAIN|LOG_PANIC,
541                         "Perf: WARNING: Unexpected processor instance name");
542               return FALSE;
543             }
544           }
545           if (PerfInst->NameLength == 4) {
546             *Time100nsPtr += cygwin_load.PerfData->PerfTime100nSec.QuadPart;
547             PtrToCntr = InstanceCounterBlock(PerfInst);
548             if (! ReadTimeCtr(PerfObj, PerfCntr, PtrToCntr, IdleCountPtr)) {
549               return FALSE;
550             }
551           }
552           PerfInst = NextInstance( PerfInst );
553         }
554         return (*Time100nsPtr != 0); /* Something was read */
555       }
556       else { /* No instance, just the counter data */
557         *Time100nsPtr = cygwin_load.PerfData->PerfTime100nSec.QuadPart;
558         PtrToCntr = ObjectCounterBlock(PerfObj);
559         return ReadTimeCtr(PerfObj, PerfCntr, PtrToCntr, IdleCountPtr);
560       }
561     }
562     PerfObj = NextObject( PerfObj );
563   }
564   return FALSE; /* Did not find the Processor object */
565 }
566
567 /*****************************************************************
568  *
569  InitLoadAvg()
570  Initialize the cygwin_load structure.
571  and set cygwin_load.Flag to TRUE if successful.
572  This is called the first time os_getloadavg is called
573  *****************************************************************/
574 static void InitLoadAvg()
575 {
576   BOOL success = TRUE;
577   cygwin_load.Init = TRUE;        /* We have run */
578   /* Get perf frequency and counter */
579   QueryPerformanceFrequency((LARGE_INTEGER *)& cygwin_load.PerfFreq);
580   QueryPerformanceCounter((LARGE_INTEGER *)& cygwin_load.LastCounter);
581   DEBUG(D_load) {
582     /* Get the name strings through the registry
583        to verify that the object and counter numbers
584        have the names we expect */
585     success = GetNameStrings();
586   }
587   /* Get initial values for Time100ns and IdleCount
588      and possibly verify the names */
589   //  success = success &&
590   success = ReadStat( & cygwin_load.Time100ns,
591                       & cygwin_load.IdleCount);
592   /* If success, set the Load to 0, else to -1 */
593   if (success) cygwin_load.LastLoad = 0;
594   else {
595     log_write(0, LOG_MAIN, "Cannot obtain Load Average");
596     cygwin_load.LastLoad = -1;
597   }
598   /* Free the buffer created for debug name verification */
599   if (cygwin_load.NamesArray != NULL) {
600     free(cygwin_load.NamesArray);
601     cygwin_load.NamesArray = NULL;
602   }
603 }
604 /*****************************************************************
605  *
606  os_getloadavg()
607
608  Return -1 if not available;
609  Return the previous value if less than AVERAGING sec old.
610  else return the processor load on a [0 - 1000] scale.
611
612  The first time we are called we initialize the counts
613  and return 0 or -1.
614  The load cannot be measured because we use the processor 100%
615 *****************************************************************/
616 #define AVERAGING 10
617 int os_getloadavg()
618 {
619   long long Time100ns, IdleCount, CurrCounter;
620   int value;
621
622   if (! cygwin_load.Init) InitLoadAvg();
623   else if (cygwin_load.LastLoad >= 0) { /* Initialized OK */
624     /* Get the current time (PerfCounter) */
625     QueryPerformanceCounter((LARGE_INTEGER *)& CurrCounter);
626     /* Calls closer than AVERAGING sec apart use the previous value */
627     if (CurrCounter - cygwin_load.LastCounter >
628         AVERAGING * cygwin_load.PerfFreq) {
629       /* Get Time100ns and IdleCount */
630       if (ReadStat( & Time100ns, & IdleCount)) { /* Success */
631         /* Return processor load on 1000 scale */
632         value = 1000 - ((1000 * (IdleCount - cygwin_load.IdleCount)) /
633                         (Time100ns - cygwin_load.Time100ns));
634         cygwin_load.Time100ns = Time100ns;
635         cygwin_load.IdleCount = IdleCount;
636         cygwin_load.LastCounter = CurrCounter;
637         cygwin_load.LastLoad = value;
638       }
639       else { /* Something bad happened.
640                 Refuse to measure the load anymore
641                 but don't bother releasing the buffer */
642         log_write(0, LOG_MAIN, "Cannot obtain Load Average");
643         cygwin_load.LastLoad = -1;
644       }
645     }
646   }
647   DEBUG(D_load)
648     debug_printf("Perf: load average = %d\n", cygwin_load.LastLoad);
649   return cygwin_load.LastLoad;
650 }
651 #endif /* OS_LOAD_AVERAGE */
652 #endif /* COMPILE_UTILITY */