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