New OS/os.c-cygwin from the maintainer.
[exim.git] / src / OS / os.c-cygwin
1 /* $Cambridge: exim/src/OS/os.c-cygwin,v 1.2 2005/03/29 11:01:32 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 #ifndef OS_LOAD_AVERAGE /* Can be set on command line */
252 #define OS_LOAD_AVERAGE /* src/os.c need not provide it */
253
254 /*****************************************************************
255  *
256  Functions for average load measurements
257
258  There are two methods, which work only on NT.
259
260  The first one uses the HKEY_PERFORMANCE_DATA registry to
261  get performance data. It is complex but well documented
262  and works on all NT versions.
263
264  The second one uses NtQuerySystemInformation.
265  Its use is discouraged starting with WinXP.
266
267  Until 4.43, the Cygwin port of exim was using the first
268  method.
269
270 *****************************************************************/
271 #define PERF_METHOD2
272
273 /* Structure to compute the load average efficiently */
274 typedef struct {
275   DWORD Lock;
276   unsigned long long Time100ns;   /* Last measurement time */
277   unsigned long long IdleCount;   /* Latest cumulative idle time */
278   unsigned long long LastCounter; /* Last measurement counter */
279   unsigned long long PerfFreq;    /* Perf counter frequency */
280   int LastLoad;                   /* Last reported load, or -1 */
281 #ifdef PERF_METHOD1
282   PPERF_DATA_BLOCK PerfData;      /* Pointer to a buffer to get the data */
283   DWORD BufferSize;               /* Size of PerfData */
284   LPSTR * NamesArray;             /* Temporary (malloc) buffer for index */
285 #endif
286 } cygwin_perf_t;
287
288 static struct {
289    HANDLE handle;
290    pid_t pid;
291    cygwin_perf_t *perf;
292 } cygwin_load = {NULL, 0, NULL};
293
294 #ifdef PERF_METHOD1
295 /*************************************************************
296  METHOD 1
297
298  Obtaining statistics in Windows is done at a low level by
299  calling registry functions, in particular the key
300  HKEY_PERFORMANCE_DATA on NT and successors.
301  Something equivalent exists on Win95, see Microsoft article
302  HOWTO: Access the Performance Registry Under Windows 95 (KB 174631)
303  but it is not implemented here.
304
305  The list of objects to be polled is specified in the string
306  passed to RegQueryValueEx in ReadStat() below.
307  On NT, all objects are polled even if info about only one is
308  required. This is fixed in Windows 2000. See articles
309  INFO: Perflib Calling Close Procedure in Windows 2000 (KB 270127)
310  INFO: Performance Data Changes Between Windows NT 4.0 and Windows
311  2000 (KB 296523)
312
313  It is unclear to me how the counters are primarily identified.
314  Whether it's by name strings or by the offset of their strings
315  as mapped in X:\Winnt\system32\perfc009.dat [or equivalently as
316  reported by the registry functions in GetNameStrings( ) below].
317  Microsoft documentation seems to say that both methods should
318  work.
319
320  In the interest of speed and language independence, the main
321  code below relies on offsets. However if debug is enabled, the
322  code verifies that the names of the corresponding strings are
323  as expected.
324
325 *****************************************************************/
326
327 /* Object and counter indices and names */
328 #define PROCESSOR_OBJECT_INDEX 238
329 #define PROCESSOR_OBJECT_STRING "238"
330 #define PROCESSOR_OBJECT_NAME "Processor"
331 #define PROCESSOR_TIME_COUNTER 6
332 #define PROCESSOR_TIME_NAME "% Processor Time"
333
334 #define BYTEINCREMENT 800    /* Block to add to PerfData */
335
336 /*****************************************************************
337  *
338  Macros to navigate through the performance data.
339
340  *****************************************************************/
341 #define FirstObject(PerfData)\
342   ((PPERF_OBJECT_TYPE)((PBYTE)PerfData + PerfData->HeaderLength))
343 #define NextObject(PerfObj)\
344   ((PPERF_OBJECT_TYPE)((PBYTE)PerfObj + PerfObj->TotalByteLength))
345 #define ObjectCounterBlock(PerfObj)\
346   ((PPERF_COUNTER_BLOCK)(PBYTE)PerfObj + PerfObj->DefinitionLength )
347 #define FirstInstance(PerfObj )\
348   ((PPERF_INSTANCE_DEFINITION)((PBYTE)PerfObj + PerfObj->DefinitionLength))
349 #define InstanceCounterBlock(PerfInst)\
350   ((PPERF_COUNTER_BLOCK) ((PBYTE)PerfInst + PerfInst->ByteLength ))
351 #define NextInstance(PerfInst )\
352   ((PPERF_INSTANCE_DEFINITION)((PBYTE)InstanceCounterBlock(PerfInst) + \
353         InstanceCounterBlock(PerfInst)->ByteLength) )
354 #define FirstCounter(PerfObj)\
355   ((PPERF_COUNTER_DEFINITION) ((PBYTE)PerfObj + PerfObj->HeaderLength))
356 #define NextCounter(PerfCntr)\
357   ((PPERF_COUNTER_DEFINITION)((PBYTE)PerfCntr + PerfCntr->ByteLength))
358
359 /*****************************************************************
360  *
361  Load the counter and object names from the registry
362  to cygwin_load.perf->NameStrings
363  and index them in cygwin_load.perf->NamesArray
364
365  NameStrings seems to be taken from the file
366  X:\Winnt\system32\perfc009.dat
367
368  This is used only for name verification during initialization,
369  if DEBUG(D_load) is TRUE.
370
371 *****************************************************************/
372 static BOOL GetNameStrings( )
373 {
374   HKEY hKeyPerflib;      // handle to registry key
375   DWORD dwArraySize;     // size for array
376   DWORD dwNamesSize;     // size for strings
377   LPSTR lpCurrentString; // pointer for enumerating data strings
378   DWORD dwCounter;       // current counter index
379   LONG  res;
380
381   /* Get the number of Counter items into dwArraySize. */
382   if ((res = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
383                            "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib",
384                            0,
385                            KEY_QUERY_VALUE, /* KEY_READ, */
386                            &hKeyPerflib))
387       != ERROR_SUCCESS) {
388     DEBUG(D_load) debug_printf("RegOpenKeyEx (1): error %ld (Windows)\n", res);
389     return FALSE;
390   }
391   dwNamesSize = sizeof(dwArraySize); /* Temporary reuse */
392   if ((res = RegQueryValueEx( hKeyPerflib,
393                               "Last Counter",
394                               NULL,
395                               NULL,
396                               (LPBYTE) &dwArraySize,
397                               &dwNamesSize ))
398       != ERROR_SUCCESS) {
399     DEBUG(D_load) debug_printf("RegQueryValueEx (1): error %ld (Windows)\n", res);
400     return FALSE;
401   }
402   RegCloseKey( hKeyPerflib );
403   /* Open the key containing the counter and object names. */
404   if ((res = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
405                            "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009",
406                            0,
407                            KEY_READ,
408                            &hKeyPerflib))
409       != ERROR_SUCCESS) {
410     DEBUG(D_load) debug_printf("RegOpenKeyEx (2): error %ld (Windows)\n", res);
411     return FALSE;
412   }
413   /* Get the size of the Counter value in the key
414      and then read the value in the tail of NamesArray */
415   dwNamesSize = 0;
416   lpCurrentString = NULL;
417   while (1) {
418     res = RegQueryValueEx( hKeyPerflib,
419                            "Counter",
420                            NULL,
421                            NULL,
422                            (unsigned char *) lpCurrentString,
423                            &dwNamesSize);
424     if ((res == ERROR_SUCCESS) && /* Bug (NT 4.0): SUCCESS was returned on first call */
425         (cygwin_load.perf->NamesArray != NULL)) break;
426     if ((res == ERROR_SUCCESS) || /* but cygwin_load.perf->NamesArrays == NULL */
427         (res == ERROR_MORE_DATA)) {
428       /* Allocate memory BOTH for the names array and for the counter and object names */
429       if ((cygwin_load.perf->NamesArray =
430            (LPSTR *) malloc( (dwArraySize + 1) * sizeof(LPSTR) + dwNamesSize * sizeof(CHAR)))
431           != NULL) {
432         /* Point to area for the counter and object names */
433         lpCurrentString = (LPSTR) & cygwin_load.perf->NamesArray[dwArraySize + 1];
434         continue;
435       }
436       DEBUG(D_load) debug_printf("Malloc: errno %d (%s)\n", errno, strerror(errno));
437     }
438     else { /* Serious error */
439       DEBUG(D_load) debug_printf("RegQueryValueEx (2): error %ld (Windows)\n", res);
440     }
441     return FALSE;
442   }
443   RegCloseKey( hKeyPerflib );
444   /* Index the names into an array. */
445   while (*lpCurrentString) {
446     dwCounter = atol( lpCurrentString );
447     lpCurrentString += (lstrlen(lpCurrentString)+1);
448     cygwin_load.perf->NamesArray[dwCounter] = lpCurrentString;
449     lpCurrentString += (strlen(lpCurrentString)+1);
450   }
451   return TRUE;
452 }
453
454 /*****************************************************************
455  *
456  Find the value of the Processor Time counter
457
458 *****************************************************************/
459 static BOOL ReadTimeCtr(PPERF_OBJECT_TYPE PerfObj,
460                         PPERF_COUNTER_DEFINITION CurCntr,
461                         PPERF_COUNTER_BLOCK PtrToCntr,
462                         unsigned long long * TimePtr){
463   int j;
464   /* Scan all counters. */
465   for( j = 0; j < PerfObj->NumCounters; j++ ) {
466     if (CurCntr->CounterNameTitleIndex == PROCESSOR_TIME_COUNTER) {
467       /* Verify it is really the proc time counter */
468       if ((CurCntr->CounterType != PERF_100NSEC_TIMER_INV) || /* Wrong type */
469           ((cygwin_load.perf->NamesArray != NULL) &&                  /* Verify name */
470            (strcmp(cygwin_load.perf->NamesArray[CurCntr->CounterNameTitleIndex],
471                    PROCESSOR_TIME_NAME)))) {
472         log_write(0, LOG_MAIN|LOG_PANIC,
473                   "Incorrect Perf counter type or name %x %s",
474                   (unsigned) CurCntr->CounterType,
475                   cygwin_load.perf->NamesArray[CurCntr->CounterNameTitleIndex]);
476         return FALSE;
477       }
478       *TimePtr += *(unsigned long long int *) ((PBYTE) PtrToCntr + CurCntr->CounterOffset);
479       return TRUE; /* return TRUE as soon as we found the counter */
480     }
481     /* Get the next counter. */
482     CurCntr = NextCounter( CurCntr );
483   }
484   return FALSE;
485 }
486
487 /*****************************************************************
488  *
489  ReadStat()
490  Measures current Time100ns and IdleCount
491  Return TRUE if success.
492
493  *****************************************************************/
494 static BOOL ReadStat(unsigned long long int *Time100nsPtr,
495                      unsigned long long int * IdleCountPtr)
496 {
497   PPERF_OBJECT_TYPE PerfObj;
498   PPERF_INSTANCE_DEFINITION PerfInst;
499   PPERF_COUNTER_DEFINITION PerfCntr;
500   PPERF_COUNTER_BLOCK PtrToCntr;
501   DWORD i, k, res;
502
503   /* Get the performance data for the Processor object
504      There is no need to open a key.
505      We may need to blindly increase the buffer size.
506      BufferSize does not return info but may be changed */
507   while (1) {
508     DWORD BufferSize = cygwin_load.perf->BufferSize;
509     res = RegQueryValueEx( HKEY_PERFORMANCE_DATA,
510                            PROCESSOR_OBJECT_STRING,
511                            NULL,
512                            NULL,
513                            (LPBYTE) cygwin_load.perf->PerfData,
514                            &BufferSize );
515     if (res == ERROR_SUCCESS) break;
516     if (res == ERROR_MORE_DATA ) {
517       /* Increment if necessary to get a buffer that is big enough. */
518       cygwin_load.perf->BufferSize += BYTEINCREMENT;
519       if ((cygwin_load.perf->PerfData =
520            (PPERF_DATA_BLOCK) realloc( cygwin_load.perf->PerfData, cygwin_load.perf->BufferSize ))
521           != NULL) continue;
522       DEBUG(D_load) debug_printf("Malloc: errno %d (%s)\n", errno, strerror(errno));
523     }
524     else { /* Serious error */
525       DEBUG(D_load) debug_printf("RegQueryValueEx (3): error %ld (Windows)\n", res);
526     }
527     return FALSE;
528   }
529   /* Initialize the counters */
530   *Time100nsPtr = 0;
531   *IdleCountPtr = 0;
532   /* We should only have one object, but write general code just in case. */
533   PerfObj = FirstObject( cygwin_load.perf->PerfData );
534   for( i = 0; i < cygwin_load.perf->PerfData->NumObjectTypes; i++ ) {
535     /* We are only interested in the processor object */
536     if ( PerfObj->ObjectNameTitleIndex == PROCESSOR_OBJECT_INDEX) {
537       /* Possibly verify it is really the Processor object. */
538       if ((cygwin_load.perf->NamesArray != NULL) &&
539           (strcmp(cygwin_load.perf->NamesArray[PerfObj->ObjectNameTitleIndex],
540                   PROCESSOR_OBJECT_NAME))) {
541         log_write(0, LOG_MAIN|LOG_PANIC,
542                   "Incorrect Perf object name %s",
543                   cygwin_load.perf->NamesArray[PerfObj->ObjectNameTitleIndex]);
544         return FALSE;
545       }
546       /* Get the first counter */
547       PerfCntr = FirstCounter( PerfObj );
548       /* See if the object has instances.
549          It should, but write general code. */
550       if( PerfObj->NumInstances != PERF_NO_INSTANCES ) {
551         PerfInst = FirstInstance( PerfObj );
552         for( k = 0; k < PerfObj->NumInstances; k++ ) {
553           /* There can be several processors.
554              Accumulate both the Time100ns and the idle counter.
555              Starting with Win2000 there is an instance named "_Total".
556              Do not use it.     We only use instances with a single
557              character in the name.
558              If we examine the object names, we also look at the instance
559              names and their lengths and issue reports */
560           if ( cygwin_load.perf->NamesArray != NULL) {
561             CHAR ascii[30]; /* The name is in unicode */
562             wsprintf(ascii,"%.29lS",
563                      (char *)((PBYTE)PerfInst + PerfInst->NameOffset));
564             log_write(0, LOG_MAIN,
565                       "Perf: Found processor instance \"%s\", length %d",
566                       ascii, PerfInst->NameLength);
567             if ((PerfInst->NameLength != 4) &&
568                 (strcmp(ascii, "_Total") != 0)) {
569               log_write(0, LOG_MAIN|LOG_PANIC,
570                         "Perf: WARNING: Unexpected processor instance name");
571               return FALSE;
572             }
573           }
574           if (PerfInst->NameLength == 4) {
575             *Time100nsPtr += cygwin_load.perf->PerfData->PerfTime100nSec.QuadPart;
576             PtrToCntr = InstanceCounterBlock(PerfInst);
577             if (! ReadTimeCtr(PerfObj, PerfCntr, PtrToCntr, IdleCountPtr)) {
578               return FALSE;
579             }
580           }
581           PerfInst = NextInstance( PerfInst );
582         }
583         return (*Time100nsPtr != 0); /* Something was read */
584       }
585       else { /* No instance, just the counter data */
586         *Time100nsPtr = cygwin_load.perf->PerfData->PerfTime100nSec.QuadPart;
587         PtrToCntr = ObjectCounterBlock(PerfObj);
588         return ReadTimeCtr(PerfObj, PerfCntr, PtrToCntr, IdleCountPtr);
589       }
590     }
591     PerfObj = NextObject( PerfObj );
592   }
593   return FALSE; /* Did not find the Processor object */
594 }
595
596 #elif defined(PERF_METHOD2)
597
598 /*************************************************************
599   METHOD 2
600
601   Uses NtQuerySystemInformation.
602   This requires definitions that are not part of
603   standard include files.
604 *************************************************************/
605 #include <ntdef.h>
606
607 typedef enum _SYSTEM_INFORMATION_CLASS
608 {
609   SystemBasicInformation = 0,
610   SystemPerformanceInformation = 2,
611   SystemTimeOfDayInformation = 3,
612   SystemProcessesAndThreadsInformation = 5,
613   SystemProcessorTimes = 8,
614   SystemPagefileInformation = 18,
615   /* There are a lot more of these... */
616 } SYSTEM_INFORMATION_CLASS;
617
618 typedef struct _SYSTEM_BASIC_INFORMATION
619 {
620   ULONG Unknown;
621   ULONG MaximumIncrement;
622   ULONG PhysicalPageSize;
623   ULONG NumberOfPhysicalPages;
624   ULONG LowestPhysicalPage;
625   ULONG HighestPhysicalPage;
626   ULONG AllocationGranularity;
627   ULONG LowestUserAddress;
628   ULONG HighestUserAddress;
629   ULONG ActiveProcessors;
630   UCHAR NumberProcessors;
631 } SYSTEM_BASIC_INFORMATION, *PSYSTEM_BASIC_INFORMATION;
632
633 typedef struct __attribute__ ((aligned (8))) _SYSTEM_PROCESSOR_TIMES
634 {
635   LARGE_INTEGER IdleTime;
636   LARGE_INTEGER KernelTime;
637   LARGE_INTEGER UserTime;
638   LARGE_INTEGER DpcTime;
639   LARGE_INTEGER InterruptTime;
640   ULONG InterruptCount;
641 } SYSTEM_PROCESSOR_TIMES, *PSYSTEM_PROCESSOR_TIMES;
642
643 typedef NTSTATUS NTAPI (*NtQuerySystemInformation_t) (SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG);
644 typedef ULONG NTAPI (*RtlNtStatusToDosError_t) (NTSTATUS);
645
646 static NtQuerySystemInformation_t NtQuerySystemInformation;
647 static RtlNtStatusToDosError_t RtlNtStatusToDosError;
648
649 /*****************************************************************
650  *
651  LoadNtdll()
652  Load special functions from the NTDLL
653  Return TRUE if success.
654
655  *****************************************************************/
656
657 static BOOL LoadNtdll()
658 {
659   HINSTANCE hinstLib;
660
661   if ((hinstLib = LoadLibrary("NTDLL.DLL"))
662       && (NtQuerySystemInformation =
663           (NtQuerySystemInformation_t) GetProcAddress(hinstLib,
664                                                         "NtQuerySystemInformation"))
665       && (RtlNtStatusToDosError =
666           (RtlNtStatusToDosError_t) GetProcAddress(hinstLib,
667                                                      "RtlNtStatusToDosError")))
668     return TRUE;
669
670   DEBUG(D_load)
671     debug_printf("perf: load: %ld (Windows)\n", GetLastError());
672   return FALSE;
673 }
674
675 /*****************************************************************
676  *
677  ReadStat()
678  Measures current Time100ns and IdleCount
679  Return TRUE if success.
680
681  *****************************************************************/
682
683 static BOOL ReadStat(unsigned long long int *Time100nsPtr,
684                      unsigned long long int *IdleCountPtr)
685 {
686   NTSTATUS ret;
687   SYSTEM_BASIC_INFORMATION sbi;
688   PSYSTEM_PROCESSOR_TIMES spt;
689
690   *Time100nsPtr = *IdleCountPtr = 0;
691
692   if ((ret = NtQuerySystemInformation(SystemBasicInformation,
693                                       (PVOID) &sbi, sizeof sbi, NULL))
694       != STATUS_SUCCESS) {
695     DEBUG(D_load)
696       debug_printf("Perf: NtQuerySystemInformation: %lu (Windows)\n",
697                    RtlNtStatusToDosError(ret));
698   }
699   else if (!(spt = (PSYSTEM_PROCESSOR_TIMES) alloca(sizeof(spt[0]) * sbi.NumberProcessors))) {
700     DEBUG(D_load)
701       debug_printf("Perf: alloca: errno %d (%s)\n", errno, strerror(errno));
702   }
703   else if ((ret = NtQuerySystemInformation(SystemProcessorTimes, (PVOID) spt,
704                                            sizeof spt[0] * sbi.NumberProcessors, NULL))
705            != STATUS_SUCCESS) {
706     DEBUG(D_load)
707       debug_printf("Perf: NtQuerySystemInformation: %lu (Windows)\n",
708                    RtlNtStatusToDosError(ret));
709   }
710   else {
711     int i;
712     for (i = 0; i < sbi.NumberProcessors; i++) {
713       *Time100nsPtr += spt[i].KernelTime.QuadPart;;
714       *Time100nsPtr += spt[i].UserTime.QuadPart;
715       *IdleCountPtr += spt[i].IdleTime.QuadPart;
716     }
717     return TRUE;
718   }
719   return FALSE;
720 }
721 #endif /* PERF_METHODX */
722
723 /*****************************************************************
724  *
725  InitLoadAvg()
726  Initialize the cygwin_load.perf structure.
727  and set cygwin_load.perf->Flag to TRUE if successful.
728  This is called the first time os_getloadavg is called
729  *****************************************************************/
730 static void InitLoadAvg(cygwin_perf_t *this)
731 {
732   BOOL success = TRUE;
733
734   /* Get perf frequency and counter */
735   QueryPerformanceFrequency((LARGE_INTEGER *)& this->PerfFreq);
736   QueryPerformanceCounter((LARGE_INTEGER *)& this->LastCounter);
737
738 #ifdef PERF_METHOD1
739   DEBUG(D_load) {
740     /* Get the name strings through the registry
741        to verify that the object and counter numbers
742        have the names we expect */
743     success = GetNameStrings();
744   }
745 #endif
746   /* Get initial values for Time100ns and IdleCount */
747   success = success
748             && ReadStat( & this->Time100ns,
749                          & this->IdleCount);
750   /* If success, set the Load to 0, else to -1 */
751   if (success) this->LastLoad = 0;
752   else {
753     log_write(0, LOG_MAIN, "Cannot obtain Load Average");
754     this->LastLoad = -1;
755   }
756 #ifdef PERF_METHOD1
757   /* Free the buffer created for debug name verification */
758   if (this->NamesArray != NULL) {
759     free(this->NamesArray);
760     this->NamesArray = NULL;
761   }
762 #endif
763 }
764
765
766 /*****************************************************************
767  *
768  os_getloadavg()
769
770  Return -1 if not available;
771  Return the previous value if less than AVERAGING sec old.
772  else return the processor load on a [0 - 1000] scale.
773
774  The first time we are called we initialize the counts
775  and return 0 or -1.
776  The initial load cannot be measured as we use the processor 100%
777 *****************************************************************/
778 static SECURITY_ATTRIBUTES sa = {sizeof (SECURITY_ATTRIBUTES), NULL, TRUE};
779 #define AVERAGING 10
780
781 int os_getloadavg()
782 {
783   unsigned long long Time100ns, IdleCount, CurrCounter;
784   int value;
785   pid_t newpid;
786
787   /* New process.
788      Reload the dlls and the file mapping */
789   if ((newpid = getpid()) != cygwin_load.pid) {
790     BOOL new;
791     cygwin_load.pid = newpid;
792
793 #ifdef PERF_METHOD2
794     if (!LoadNtdll()) {
795       log_write(0, LOG_MAIN, "Cannot obtain Load Average");
796       cygwin_load.perf = NULL;
797       return -1;
798     }
799 #endif
800
801     if ((new = !cygwin_load.handle)) {
802       cygwin_load.handle = CreateFileMapping (INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE,
803                                               0, sizeof(cygwin_perf_t), NULL);
804       DEBUG(D_load)
805         debug_printf("Perf: CreateFileMapping: handle %x\n", (unsigned) cygwin_load.handle);
806     }
807     cygwin_load.perf = (cygwin_perf_t *) MapViewOfFile (cygwin_load.handle,
808                                                         FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
809     DEBUG(D_load)
810       debug_printf("Perf: MapViewOfFile: addr %x\n", (unsigned) cygwin_load.perf);
811     if (new && cygwin_load.perf)
812       InitLoadAvg(cygwin_load.perf);
813   }
814
815   /* Check if initialized OK */
816   if (!cygwin_load.perf || cygwin_load.perf->LastLoad < 0)
817     return -1;
818
819   /* If we cannot get the lock, we return 0.
820      This is to prevent any lock-up possibility.
821      Finding a lock busy is unlikely, and giving up only
822      results in an immediate delivery .*/
823
824   if (InterlockedCompareExchange(&cygwin_load.perf->Lock, 1, 0)) {
825     DEBUG(D_load)
826       debug_printf("Perf: Lock busy\n");
827     return 0;
828   }
829
830     /* Get the current time (PerfCounter) */
831     QueryPerformanceCounter((LARGE_INTEGER *)& CurrCounter);
832     /* Calls closer than AVERAGING sec apart use the previous value */
833   if (CurrCounter - cygwin_load.perf->LastCounter >
834       AVERAGING * cygwin_load.perf->PerfFreq) {
835       /* Get Time100ns and IdleCount */
836       if (ReadStat( & Time100ns, & IdleCount)) { /* Success */
837         /* Return processor load on 1000 scale */
838       value = 1000 - ((1000 * (IdleCount - cygwin_load.perf->IdleCount)) /
839                       (Time100ns - cygwin_load.perf->Time100ns));
840       cygwin_load.perf->Time100ns = Time100ns;
841       cygwin_load.perf->IdleCount = IdleCount;
842       cygwin_load.perf->LastCounter = CurrCounter;
843       cygwin_load.perf->LastLoad = value;
844       DEBUG(D_load)
845         debug_printf("Perf: New load average %d\n", value);
846       }
847       else { /* Something bad happened.
848                 Refuse to measure the load anymore
849                 but don't bother releasing the buffer */
850         log_write(0, LOG_MAIN, "Cannot obtain Load Average");
851       cygwin_load.perf->LastLoad = -1;
852     }
853   }
854   else
855   DEBUG(D_load)
856       debug_printf("Perf: Old load average %d\n", cygwin_load.perf->LastLoad);
857   cygwin_load.perf->Lock = 0;
858   return cygwin_load.perf->LastLoad;
859 }
860 #endif /* OS_LOAD_AVERAGE */
861 #endif /* COMPILE_UTILITY */