src/deliver.c: log the error message when unlink(spoolname) fails
[users/jgh/exim.git] / src / OS / os.c-cygwin
1 /* $Cambridge: exim/src/OS/os.c-cygwin,v 1.5 2006/03/08 09:43:10 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 #define EqualLuid(Luid1, Luid2) \
53   ((Luid1.LowPart == Luid2.LowPart) && (Luid1.HighPart == Luid2.HighPart))
54 #include <sys/cygwin.h>
55
56 /* Special static variables */
57 static BOOL cygwin_debug = FALSE;
58 static int privileged = 1; /* when not privileged, setuid = noop */
59
60 #undef setuid
61 int cygwin_setuid(uid_t uid )
62 {
63   int res;
64   if (privileged <= 0) return 0;
65   else {
66     res = setuid(uid);
67     if (cygwin_debug)
68       fprintf(stderr, "setuid %lu %lu %d pid: %d\n",
69               uid, getuid(),res, getpid());
70   }
71   return res;
72 }
73
74 #undef setgid
75 int cygwin_setgid(gid_t gid )
76 {
77   int res;
78   if (privileged <= 0) return 0;
79   else {
80     res = setgid(gid);
81     if (cygwin_debug)
82       fprintf(stderr, "setgid %lu %lu %d pid: %d\n",
83               gid, getgid(), res, getpid());
84   }
85   return res;
86 }
87
88 /* Background processes run at lower priority */
89 static void cygwin_setpriority()
90 {
91   if (!SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS))
92     SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS);
93   return;
94 }
95
96
97 /* GetVersion()
98    MSB: 1 for 95/98/ME; Next 7: build number, except for 95/98/ME
99    Next byte: 0
100    Next byte: minor version of OS
101    Low  byte: major version of OS (3 or 4 for for NT, 5 for 2000 and XP) */
102 #define VERSION_IS_58M(x) (x & 0x80000000) /* 95, 98, Me   */
103 #define VERSION_IS_NT(x)  ((x & 0XFF) < 5) /* NT 4 or 3.51 */
104
105 /*
106   Routine to find if process or thread is privileged
107 */
108
109 enum {
110   CREATE_BIT = 1,
111   RESTORE_BIT = 2
112 };
113
114 static DWORD get_privileges ()
115 {
116   char buffer[1024];
117   DWORD i, length;
118   HANDLE hToken = NULL;
119   PTOKEN_PRIVILEGES privs;
120   LUID cluid, rluid;
121   DWORD ret = 0;
122
123   privs = (PTOKEN_PRIVILEGES) buffer;
124
125   if (OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hToken)
126       && LookupPrivilegeValue (NULL, SE_CREATE_TOKEN_NAME, &cluid)
127       && LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &rluid)
128       && (GetTokenInformation( hToken, TokenPrivileges,
129                                privs, sizeof (buffer), &length)
130           || (GetLastError () == ERROR_INSUFFICIENT_BUFFER
131               && (privs = (PTOKEN_PRIVILEGES) alloca (length))
132               && GetTokenInformation(hToken, TokenPrivileges,
133                                      privs, length, &length)))) {
134     for (i = 0; i < privs->PrivilegeCount; i++) {
135       if (EqualLuid(privs->Privileges[i].Luid, cluid))
136         ret |= CREATE_BIT;
137       else if (EqualLuid(privs->Privileges[i].Luid, rluid))
138         ret |= RESTORE_BIT;
139       else continue;
140       if (ret == (CREATE_BIT | RESTORE_BIT))
141         break;
142     }
143   }
144   else
145     fprintf(stderr, "has_create_token_privilege %ld\n", GetLastError());
146
147   if (hToken)
148     CloseHandle(hToken);
149
150   return ret;
151 }
152
153 /* We use a special routine to initialize
154     cygwin_init is called from the OS_INIT macro in main(). */
155
156 void cygwin_init(int argc, char ** argv, void * rup,
157                  void * eup, void * egp, void * cup, void * cgp)
158 {
159   int i;
160   uid_t myuid, systemuid;
161   gid_t mygid, adminsgid;
162   struct passwd * pwp;
163   char *cygenv, win32_path[MAX_PATH];
164   SID(1, SystemSid, SECURITY_LOCAL_SYSTEM_RID);
165   SID(2, AdminsSid, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS);
166   DWORD priv_flags;
167
168   myuid = getuid();
169   mygid = getgid();
170   cygwin_WinVersion = GetVersion();
171   if ((cygenv = getenv("CYGWIN")) == NULL) cygenv = "";
172   /* Produce some debugging on stderr,
173      cannot yet use exim's debug functions.
174      Exim does not use -c and ignores -n.
175      Set lower priority for daemons */
176   for (i = 1; i < argc; i++) {
177     if (argv[i][0] == '-') {
178       if (argv[i][1] == 'c') {
179         argv[i][1] = 'n';  /* Replace -c by -n */
180         cygwin_debug = TRUE;
181         fprintf(stderr, "CYGWIN = \"%s\".", cygenv);
182         cygwin_conv_to_win32_path("/", win32_path);
183         fprintf(stderr, " Root / mapped to %s.\n", win32_path);
184       }
185       else if (argv[i][1] == 'b' && argv[i][2] == 'd')
186         cygwin_setpriority();
187     }
188   }
189   if (VERSION_IS_58M(cygwin_WinVersion)) {
190     * (uid_t *) rup = myuid;  /* Pretend we are root */
191     * (uid_t *) eup = myuid;  /* ... and exim */
192     * (gid_t *) egp = mygid;
193     return;
194   }
195   /* Nt/2000/XP
196      We initially set the exim uid & gid to those of the "real exim",
197        or to the root uid (SYSTEM) and exim gid (ADMINS),
198      If privileged, we setuid to those.
199      We always set the configure uid to the system uid.
200      We always set the root uid to the real uid
201        to avoid useless execs following forks.
202      If not privileged and unable to chown,
203        we set the exim uid to our uid.
204      If unprivileged, we fake all subsequent setuid. */
205
206   priv_flags = get_privileges ();
207   privileged = !!(priv_flags & CREATE_BIT);
208
209   /* Get the system and admins uid from their sids,
210      or use the default values from the Makefile. */
211   if ((systemuid = cygwin_internal(CW_GET_UID_FROM_SID, & SystemSid)) == -1)
212     systemuid = * (uid_t *) eup;
213   if ((adminsgid = cygwin_internal(CW_GET_GID_FROM_SID, & AdminsSid)) == -1)
214     adminsgid = * (gid_t *) egp;
215
216   if ((pwp = getpwnam("exim")) != NULL) {
217     * (uid_t *) eup = pwp->pw_uid;  /* Set it according to passwd */
218     * (gid_t *) egp = pwp->pw_gid;
219   }
220   else {
221     * (uid_t *) eup = systemuid;
222     * (gid_t *) egp = adminsgid;
223   }
224
225   /* Set the configuration uid and gid to the system uid and admins gid.
226      Note that exim uid is also accepted as owner of exim.conf. */
227   * (uid_t *) cup = systemuid;
228   * (gid_t *) cgp = adminsgid;
229
230   if (privileged) {             /* Can setuid */
231     if (cygwin_setgid(* (gid_t *) egp) /* Setuid to exim */
232         || cygwin_setuid(* (uid_t *) eup))
233       privileged = -1;          /* Problem... Perhaps not in 544 */
234   }
235
236   /* Pretend we are root to avoid useless execs.
237      We are limited by file access rights */
238   * (uid_t *) rup = getuid ();
239
240   /* If we have not setuid to exim and cannot chown,
241      set the exim uid to our uid to avoid chown failures */
242   if (privileged <= 0 && !(priv_flags & RESTORE_BIT))
243     * (uid_t *) eup = * (uid_t *) rup;
244
245   if (cygwin_debug) {
246     fprintf(stderr, "Starting uid %ld, gid %ld, ntsec %lu, privileged %d.\n",
247             myuid, mygid, cygwin_internal(CW_CHECK_NTSEC, NULL), privileged);
248     fprintf(stderr, "root_uid %ld, exim_uid %ld, exim_gid %ld, config_uid %ld, config_gid %ld.\n",
249             * (uid_t *) rup, * (uid_t *) eup, * (gid_t *) egp, * (uid_t *) cup, * (gid_t *) cgp);
250   }
251   return;
252 }
253
254 #ifndef OS_LOAD_AVERAGE /* Can be set on command line */
255 #define OS_LOAD_AVERAGE /* src/os.c need not provide it */
256
257 /*****************************************************************
258  *
259  Functions for average load measurements
260
261  There are two methods, which work only on NT.
262
263  The first one uses the HKEY_PERFORMANCE_DATA registry to
264  get performance data. It is complex but well documented
265  and works on all NT versions.
266
267  The second one uses NtQuerySystemInformation.
268  Its use is discouraged starting with WinXP.
269
270  Until 4.43, the Cygwin port of exim was using the first
271  method.
272
273 *****************************************************************/
274 #define PERF_METHOD2
275
276 /* Structure to compute the load average efficiently */
277 typedef struct {
278   DWORD Lock;
279   unsigned long long Time100ns;   /* Last measurement time */
280   unsigned long long IdleCount;   /* Latest cumulative idle time */
281   unsigned long long LastCounter; /* Last measurement counter */
282   unsigned long long PerfFreq;    /* Perf counter frequency */
283   int LastLoad;                   /* Last reported load, or -1 */
284 #ifdef PERF_METHOD1
285   PPERF_DATA_BLOCK PerfData;      /* Pointer to a buffer to get the data */
286   DWORD BufferSize;               /* Size of PerfData */
287   LPSTR * NamesArray;             /* Temporary (malloc) buffer for index */
288 #endif
289 } cygwin_perf_t;
290
291 static struct {
292    HANDLE handle;
293    pid_t pid;
294    cygwin_perf_t *perf;
295 } cygwin_load = {NULL, 0, NULL};
296
297 #ifdef PERF_METHOD1
298 /*************************************************************
299  METHOD 1
300
301  Obtaining statistics in Windows is done at a low level by
302  calling registry functions, in particular the key
303  HKEY_PERFORMANCE_DATA on NT and successors.
304  Something equivalent exists on Win95, see Microsoft article
305  HOWTO: Access the Performance Registry Under Windows 95 (KB 174631)
306  but it is not implemented here.
307
308  The list of objects to be polled is specified in the string
309  passed to RegQueryValueEx in ReadStat() below.
310  On NT, all objects are polled even if info about only one is
311  required. This is fixed in Windows 2000. See articles
312  INFO: Perflib Calling Close Procedure in Windows 2000 (KB 270127)
313  INFO: Performance Data Changes Between Windows NT 4.0 and Windows
314  2000 (KB 296523)
315
316  It is unclear to me how the counters are primarily identified.
317  Whether it's by name strings or by the offset of their strings
318  as mapped in X:\Winnt\system32\perfc009.dat [or equivalently as
319  reported by the registry functions in GetNameStrings( ) below].
320  Microsoft documentation seems to say that both methods should
321  work.
322
323  In the interest of speed and language independence, the main
324  code below relies on offsets. However if debug is enabled, the
325  code verifies that the names of the corresponding strings are
326  as expected.
327
328 *****************************************************************/
329
330 /* Object and counter indices and names */
331 #define PROCESSOR_OBJECT_INDEX 238
332 #define PROCESSOR_OBJECT_STRING "238"
333 #define PROCESSOR_OBJECT_NAME "Processor"
334 #define PROCESSOR_TIME_COUNTER 6
335 #define PROCESSOR_TIME_NAME "% Processor Time"
336
337 #define BYTEINCREMENT 800    /* Block to add to PerfData */
338
339 /*****************************************************************
340  *
341  Macros to navigate through the performance data.
342
343  *****************************************************************/
344 #define FirstObject(PerfData)\
345   ((PPERF_OBJECT_TYPE)((PBYTE)PerfData + PerfData->HeaderLength))
346 #define NextObject(PerfObj)\
347   ((PPERF_OBJECT_TYPE)((PBYTE)PerfObj + PerfObj->TotalByteLength))
348 #define ObjectCounterBlock(PerfObj)\
349   ((PPERF_COUNTER_BLOCK)(PBYTE)PerfObj + PerfObj->DefinitionLength )
350 #define FirstInstance(PerfObj )\
351   ((PPERF_INSTANCE_DEFINITION)((PBYTE)PerfObj + PerfObj->DefinitionLength))
352 #define InstanceCounterBlock(PerfInst)\
353   ((PPERF_COUNTER_BLOCK) ((PBYTE)PerfInst + PerfInst->ByteLength ))
354 #define NextInstance(PerfInst )\
355   ((PPERF_INSTANCE_DEFINITION)((PBYTE)InstanceCounterBlock(PerfInst) + \
356         InstanceCounterBlock(PerfInst)->ByteLength) )
357 #define FirstCounter(PerfObj)\
358   ((PPERF_COUNTER_DEFINITION) ((PBYTE)PerfObj + PerfObj->HeaderLength))
359 #define NextCounter(PerfCntr)\
360   ((PPERF_COUNTER_DEFINITION)((PBYTE)PerfCntr + PerfCntr->ByteLength))
361
362 /*****************************************************************
363  *
364  Load the counter and object names from the registry
365  to cygwin_load.perf->NameStrings
366  and index them in cygwin_load.perf->NamesArray
367
368  NameStrings seems to be taken from the file
369  X:\Winnt\system32\perfc009.dat
370
371  This is used only for name verification during initialization,
372  if DEBUG(D_load) is TRUE.
373
374 *****************************************************************/
375 static BOOL GetNameStrings( )
376 {
377   HKEY hKeyPerflib;      // handle to registry key
378   DWORD dwArraySize;     // size for array
379   DWORD dwNamesSize;     // size for strings
380   LPSTR lpCurrentString; // pointer for enumerating data strings
381   DWORD dwCounter;       // current counter index
382   LONG  res;
383
384   /* Get the number of Counter items into dwArraySize. */
385   if ((res = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
386                            "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib",
387                            0,
388                            KEY_QUERY_VALUE, /* KEY_READ, */
389                            &hKeyPerflib))
390       != ERROR_SUCCESS) {
391     DEBUG(D_load) debug_printf("RegOpenKeyEx (1): error %ld (Windows)\n", res);
392     return FALSE;
393   }
394   dwNamesSize = sizeof(dwArraySize); /* Temporary reuse */
395   if ((res = RegQueryValueEx( hKeyPerflib,
396                               "Last Counter",
397                               NULL,
398                               NULL,
399                               (LPBYTE) &dwArraySize,
400                               &dwNamesSize ))
401       != ERROR_SUCCESS) {
402     DEBUG(D_load) debug_printf("RegQueryValueEx (1): error %ld (Windows)\n", res);
403     return FALSE;
404   }
405   RegCloseKey( hKeyPerflib );
406   /* Open the key containing the counter and object names. */
407   if ((res = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
408                            "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009",
409                            0,
410                            KEY_READ,
411                            &hKeyPerflib))
412       != ERROR_SUCCESS) {
413     DEBUG(D_load) debug_printf("RegOpenKeyEx (2): error %ld (Windows)\n", res);
414     return FALSE;
415   }
416   /* Get the size of the Counter value in the key
417      and then read the value in the tail of NamesArray */
418   dwNamesSize = 0;
419   lpCurrentString = NULL;
420   while (1) {
421     res = RegQueryValueEx( hKeyPerflib,
422                            "Counter",
423                            NULL,
424                            NULL,
425                            (unsigned char *) lpCurrentString,
426                            &dwNamesSize);
427     if ((res == ERROR_SUCCESS) && /* Bug (NT 4.0): SUCCESS was returned on first call */
428         (cygwin_load.perf->NamesArray != NULL)) break;
429     if ((res == ERROR_SUCCESS) || /* but cygwin_load.perf->NamesArrays == NULL */
430         (res == ERROR_MORE_DATA)) {
431       /* Allocate memory BOTH for the names array and for the counter and object names */
432       if ((cygwin_load.perf->NamesArray =
433            (LPSTR *) malloc( (dwArraySize + 1) * sizeof(LPSTR) + dwNamesSize * sizeof(CHAR)))
434           != NULL) {
435         /* Point to area for the counter and object names */
436         lpCurrentString = (LPSTR) & cygwin_load.perf->NamesArray[dwArraySize + 1];
437         continue;
438       }
439       DEBUG(D_load) debug_printf("Malloc: errno %d (%s)\n", errno, strerror(errno));
440     }
441     else { /* Serious error */
442       DEBUG(D_load) debug_printf("RegQueryValueEx (2): error %ld (Windows)\n", res);
443     }
444     return FALSE;
445   }
446   RegCloseKey( hKeyPerflib );
447   /* Index the names into an array. */
448   while (*lpCurrentString) {
449     dwCounter = atol( lpCurrentString );
450     lpCurrentString += (lstrlen(lpCurrentString)+1);
451     cygwin_load.perf->NamesArray[dwCounter] = lpCurrentString;
452     lpCurrentString += (strlen(lpCurrentString)+1);
453   }
454   return TRUE;
455 }
456
457 /*****************************************************************
458  *
459  Find the value of the Processor Time counter
460
461 *****************************************************************/
462 static BOOL ReadTimeCtr(PPERF_OBJECT_TYPE PerfObj,
463                         PPERF_COUNTER_DEFINITION CurCntr,
464                         PPERF_COUNTER_BLOCK PtrToCntr,
465                         unsigned long long * TimePtr){
466   int j;
467   /* Scan all counters. */
468   for( j = 0; j < PerfObj->NumCounters; j++ ) {
469     if (CurCntr->CounterNameTitleIndex == PROCESSOR_TIME_COUNTER) {
470       /* Verify it is really the proc time counter */
471       if ((CurCntr->CounterType != PERF_100NSEC_TIMER_INV) || /* Wrong type */
472           ((cygwin_load.perf->NamesArray != NULL) &&                  /* Verify name */
473            (strcmp(cygwin_load.perf->NamesArray[CurCntr->CounterNameTitleIndex],
474                    PROCESSOR_TIME_NAME)))) {
475         log_write(0, LOG_MAIN|LOG_PANIC,
476                   "Incorrect Perf counter type or name %x %s",
477                   (unsigned) CurCntr->CounterType,
478                   cygwin_load.perf->NamesArray[CurCntr->CounterNameTitleIndex]);
479         return FALSE;
480       }
481       *TimePtr += *(unsigned long long int *) ((PBYTE) PtrToCntr + CurCntr->CounterOffset);
482       return TRUE; /* return TRUE as soon as we found the counter */
483     }
484     /* Get the next counter. */
485     CurCntr = NextCounter( CurCntr );
486   }
487   return FALSE;
488 }
489
490 /*****************************************************************
491  *
492  ReadStat()
493  Measures current Time100ns and IdleCount
494  Return TRUE if success.
495
496  *****************************************************************/
497 static BOOL ReadStat(unsigned long long int *Time100nsPtr,
498                      unsigned long long int * IdleCountPtr)
499 {
500   PPERF_OBJECT_TYPE PerfObj;
501   PPERF_INSTANCE_DEFINITION PerfInst;
502   PPERF_COUNTER_DEFINITION PerfCntr;
503   PPERF_COUNTER_BLOCK PtrToCntr;
504   DWORD i, k, res;
505
506   /* Get the performance data for the Processor object
507      There is no need to open a key.
508      We may need to blindly increase the buffer size.
509      BufferSize does not return info but may be changed */
510   while (1) {
511     DWORD BufferSize = cygwin_load.perf->BufferSize;
512     res = RegQueryValueEx( HKEY_PERFORMANCE_DATA,
513                            PROCESSOR_OBJECT_STRING,
514                            NULL,
515                            NULL,
516                            (LPBYTE) cygwin_load.perf->PerfData,
517                            &BufferSize );
518     if (res == ERROR_SUCCESS) break;
519     if (res == ERROR_MORE_DATA ) {
520       /* Increment if necessary to get a buffer that is big enough. */
521       cygwin_load.perf->BufferSize += BYTEINCREMENT;
522       if ((cygwin_load.perf->PerfData =
523            (PPERF_DATA_BLOCK) realloc( cygwin_load.perf->PerfData, cygwin_load.perf->BufferSize ))
524           != NULL) continue;
525       DEBUG(D_load) debug_printf("Malloc: errno %d (%s)\n", errno, strerror(errno));
526     }
527     else { /* Serious error */
528       DEBUG(D_load) debug_printf("RegQueryValueEx (3): error %ld (Windows)\n", res);
529     }
530     return FALSE;
531   }
532   /* Initialize the counters */
533   *Time100nsPtr = 0;
534   *IdleCountPtr = 0;
535   /* We should only have one object, but write general code just in case. */
536   PerfObj = FirstObject( cygwin_load.perf->PerfData );
537   for( i = 0; i < cygwin_load.perf->PerfData->NumObjectTypes; i++ ) {
538     /* We are only interested in the processor object */
539     if ( PerfObj->ObjectNameTitleIndex == PROCESSOR_OBJECT_INDEX) {
540       /* Possibly verify it is really the Processor object. */
541       if ((cygwin_load.perf->NamesArray != NULL) &&
542           (strcmp(cygwin_load.perf->NamesArray[PerfObj->ObjectNameTitleIndex],
543                   PROCESSOR_OBJECT_NAME))) {
544         log_write(0, LOG_MAIN|LOG_PANIC,
545                   "Incorrect Perf object name %s",
546                   cygwin_load.perf->NamesArray[PerfObj->ObjectNameTitleIndex]);
547         return FALSE;
548       }
549       /* Get the first counter */
550       PerfCntr = FirstCounter( PerfObj );
551       /* See if the object has instances.
552          It should, but write general code. */
553       if( PerfObj->NumInstances != PERF_NO_INSTANCES ) {
554         PerfInst = FirstInstance( PerfObj );
555         for( k = 0; k < PerfObj->NumInstances; k++ ) {
556           /* There can be several processors.
557              Accumulate both the Time100ns and the idle counter.
558              Starting with Win2000 there is an instance named "_Total".
559              Do not use it.     We only use instances with a single
560              character in the name.
561              If we examine the object names, we also look at the instance
562              names and their lengths and issue reports */
563           if ( cygwin_load.perf->NamesArray != NULL) {
564             CHAR ascii[30]; /* The name is in unicode */
565             wsprintf(ascii,"%.29lS",
566                      (char *)((PBYTE)PerfInst + PerfInst->NameOffset));
567             log_write(0, LOG_MAIN,
568                       "Perf: Found processor instance \"%s\", length %d",
569                       ascii, PerfInst->NameLength);
570             if ((PerfInst->NameLength != 4) &&
571                 (strcmp(ascii, "_Total") != 0)) {
572               log_write(0, LOG_MAIN|LOG_PANIC,
573                         "Perf: WARNING: Unexpected processor instance name");
574               return FALSE;
575             }
576           }
577           if (PerfInst->NameLength == 4) {
578             *Time100nsPtr += cygwin_load.perf->PerfData->PerfTime100nSec.QuadPart;
579             PtrToCntr = InstanceCounterBlock(PerfInst);
580             if (! ReadTimeCtr(PerfObj, PerfCntr, PtrToCntr, IdleCountPtr)) {
581               return FALSE;
582             }
583           }
584           PerfInst = NextInstance( PerfInst );
585         }
586         return (*Time100nsPtr != 0); /* Something was read */
587       }
588       else { /* No instance, just the counter data */
589         *Time100nsPtr = cygwin_load.perf->PerfData->PerfTime100nSec.QuadPart;
590         PtrToCntr = ObjectCounterBlock(PerfObj);
591         return ReadTimeCtr(PerfObj, PerfCntr, PtrToCntr, IdleCountPtr);
592       }
593     }
594     PerfObj = NextObject( PerfObj );
595   }
596   return FALSE; /* Did not find the Processor object */
597 }
598
599 #elif defined(PERF_METHOD2)
600
601 /*************************************************************
602   METHOD 2
603
604   Uses NtQuerySystemInformation.
605   This requires definitions that are not part of
606   standard include files.
607 *************************************************************/
608 #include <ntdef.h>
609
610 typedef enum _SYSTEM_INFORMATION_CLASS
611 {
612   SystemBasicInformation = 0,
613   SystemPerformanceInformation = 2,
614   SystemTimeOfDayInformation = 3,
615   SystemProcessesAndThreadsInformation = 5,
616   SystemProcessorTimes = 8,
617   SystemPagefileInformation = 18,
618   /* There are a lot more of these... */
619 } SYSTEM_INFORMATION_CLASS;
620
621 typedef struct _SYSTEM_BASIC_INFORMATION
622 {
623   ULONG Unknown;
624   ULONG MaximumIncrement;
625   ULONG PhysicalPageSize;
626   ULONG NumberOfPhysicalPages;
627   ULONG LowestPhysicalPage;
628   ULONG HighestPhysicalPage;
629   ULONG AllocationGranularity;
630   ULONG LowestUserAddress;
631   ULONG HighestUserAddress;
632   ULONG ActiveProcessors;
633   UCHAR NumberProcessors;
634 } SYSTEM_BASIC_INFORMATION, *PSYSTEM_BASIC_INFORMATION;
635
636 typedef struct __attribute__ ((aligned (8))) _SYSTEM_PROCESSOR_TIMES
637 {
638   LARGE_INTEGER IdleTime;
639   LARGE_INTEGER KernelTime;
640   LARGE_INTEGER UserTime;
641   LARGE_INTEGER DpcTime;
642   LARGE_INTEGER InterruptTime;
643   ULONG InterruptCount;
644 } SYSTEM_PROCESSOR_TIMES, *PSYSTEM_PROCESSOR_TIMES;
645
646 typedef NTSTATUS NTAPI (*NtQuerySystemInformation_t) (SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG);
647 typedef ULONG NTAPI (*RtlNtStatusToDosError_t) (NTSTATUS);
648
649 static NtQuerySystemInformation_t NtQuerySystemInformation;
650 static RtlNtStatusToDosError_t RtlNtStatusToDosError;
651
652 /*****************************************************************
653  *
654  LoadNtdll()
655  Load special functions from the NTDLL
656  Return TRUE if success.
657
658  *****************************************************************/
659
660 static BOOL LoadNtdll()
661 {
662   HINSTANCE hinstLib;
663
664   if ((hinstLib = LoadLibrary("NTDLL.DLL"))
665       && (NtQuerySystemInformation =
666           (NtQuerySystemInformation_t) GetProcAddress(hinstLib,
667                                                         "NtQuerySystemInformation"))
668       && (RtlNtStatusToDosError =
669           (RtlNtStatusToDosError_t) GetProcAddress(hinstLib,
670                                                      "RtlNtStatusToDosError")))
671     return TRUE;
672
673   DEBUG(D_load)
674     debug_printf("perf: load: %ld (Windows)\n", GetLastError());
675   return FALSE;
676 }
677
678 /*****************************************************************
679  *
680  ReadStat()
681  Measures current Time100ns and IdleCount
682  Return TRUE if success.
683
684  *****************************************************************/
685
686 static BOOL ReadStat(unsigned long long int *Time100nsPtr,
687                      unsigned long long int *IdleCountPtr)
688 {
689   NTSTATUS ret;
690   SYSTEM_BASIC_INFORMATION sbi;
691   PSYSTEM_PROCESSOR_TIMES spt;
692
693   *Time100nsPtr = *IdleCountPtr = 0;
694
695   if ((ret = NtQuerySystemInformation(SystemBasicInformation,
696                                       (PVOID) &sbi, sizeof sbi, NULL))
697       != STATUS_SUCCESS) {
698     DEBUG(D_load)
699       debug_printf("Perf: NtQuerySystemInformation: %lu (Windows)\n",
700                    RtlNtStatusToDosError(ret));
701   }
702   else if (!(spt = (PSYSTEM_PROCESSOR_TIMES) alloca(sizeof(spt[0]) * sbi.NumberProcessors))) {
703     DEBUG(D_load)
704       debug_printf("Perf: alloca: errno %d (%s)\n", errno, strerror(errno));
705   }
706   else if ((ret = NtQuerySystemInformation(SystemProcessorTimes, (PVOID) spt,
707                                            sizeof spt[0] * sbi.NumberProcessors, NULL))
708            != STATUS_SUCCESS) {
709     DEBUG(D_load)
710       debug_printf("Perf: NtQuerySystemInformation: %lu (Windows)\n",
711                    RtlNtStatusToDosError(ret));
712   }
713   else {
714     int i;
715     for (i = 0; i < sbi.NumberProcessors; i++) {
716       *Time100nsPtr += spt[i].KernelTime.QuadPart;;
717       *Time100nsPtr += spt[i].UserTime.QuadPart;
718       *IdleCountPtr += spt[i].IdleTime.QuadPart;
719     }
720     return TRUE;
721   }
722   return FALSE;
723 }
724 #endif /* PERF_METHODX */
725
726 /*****************************************************************
727  *
728  InitLoadAvg()
729  Initialize the cygwin_load.perf structure.
730  and set cygwin_load.perf->Flag to TRUE if successful.
731  This is called the first time os_getloadavg is called
732  *****************************************************************/
733 static void InitLoadAvg(cygwin_perf_t *this)
734 {
735   BOOL success = TRUE;
736
737   /* Get perf frequency and counter */
738   QueryPerformanceFrequency((LARGE_INTEGER *)& this->PerfFreq);
739   QueryPerformanceCounter((LARGE_INTEGER *)& this->LastCounter);
740
741 #ifdef PERF_METHOD1
742   DEBUG(D_load) {
743     /* Get the name strings through the registry
744        to verify that the object and counter numbers
745        have the names we expect */
746     success = GetNameStrings();
747   }
748 #endif
749   /* Get initial values for Time100ns and IdleCount */
750   success = success
751             && ReadStat( & this->Time100ns,
752                          & this->IdleCount);
753   /* If success, set the Load to 0, else to -1 */
754   if (success) this->LastLoad = 0;
755   else {
756     log_write(0, LOG_MAIN, "Cannot obtain Load Average");
757     this->LastLoad = -1;
758   }
759 #ifdef PERF_METHOD1
760   /* Free the buffer created for debug name verification */
761   if (this->NamesArray != NULL) {
762     free(this->NamesArray);
763     this->NamesArray = NULL;
764   }
765 #endif
766 }
767
768
769 /*****************************************************************
770  *
771  os_getloadavg()
772
773  Return -1 if not available;
774  Return the previous value if less than AVERAGING sec old.
775  else return the processor load on a [0 - 1000] scale.
776
777  The first time we are called we initialize the counts
778  and return 0 or -1.
779  The initial load cannot be measured as we use the processor 100%
780 *****************************************************************/
781 static SECURITY_ATTRIBUTES sa = {sizeof (SECURITY_ATTRIBUTES), NULL, TRUE};
782 #define AVERAGING 10
783
784 int os_getloadavg()
785 {
786   unsigned long long Time100ns, IdleCount, CurrCounter;
787   int value;
788   pid_t newpid;
789
790   /* New process.
791      Reload the dlls and the file mapping */
792   if ((newpid = getpid()) != cygwin_load.pid) {
793     BOOL new;
794     cygwin_load.pid = newpid;
795
796 #ifdef PERF_METHOD2
797     if (!LoadNtdll()) {
798       log_write(0, LOG_MAIN, "Cannot obtain Load Average");
799       cygwin_load.perf = NULL;
800       return -1;
801     }
802 #endif
803
804     if ((new = !cygwin_load.handle)) {
805       cygwin_load.handle = CreateFileMapping (INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE,
806                                               0, sizeof(cygwin_perf_t), NULL);
807       DEBUG(D_load)
808         debug_printf("Perf: CreateFileMapping: handle %x\n", (unsigned) cygwin_load.handle);
809     }
810     cygwin_load.perf = (cygwin_perf_t *) MapViewOfFile (cygwin_load.handle,
811                                                         FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
812     DEBUG(D_load)
813       debug_printf("Perf: MapViewOfFile: addr %x\n", (unsigned) cygwin_load.perf);
814     if (new && cygwin_load.perf)
815       InitLoadAvg(cygwin_load.perf);
816   }
817
818   /* Check if initialized OK */
819   if (!cygwin_load.perf || cygwin_load.perf->LastLoad < 0)
820     return -1;
821
822   /* If we cannot get the lock, we return 0.
823      This is to prevent any lock-up possibility.
824      Finding a lock busy is unlikely, and giving up only
825      results in an immediate delivery .*/
826
827   if (InterlockedCompareExchange(&cygwin_load.perf->Lock, 1, 0)) {
828     DEBUG(D_load)
829       debug_printf("Perf: Lock busy\n");
830     return 0;
831   }
832
833     /* Get the current time (PerfCounter) */
834     QueryPerformanceCounter((LARGE_INTEGER *)& CurrCounter);
835     /* Calls closer than AVERAGING sec apart use the previous value */
836   if (CurrCounter - cygwin_load.perf->LastCounter >
837       AVERAGING * cygwin_load.perf->PerfFreq) {
838       /* Get Time100ns and IdleCount */
839       if (ReadStat( & Time100ns, & IdleCount)) { /* Success */
840         /* Return processor load on 1000 scale */
841       value = 1000 - ((1000 * (IdleCount - cygwin_load.perf->IdleCount)) /
842                       (Time100ns - cygwin_load.perf->Time100ns));
843       cygwin_load.perf->Time100ns = Time100ns;
844       cygwin_load.perf->IdleCount = IdleCount;
845       cygwin_load.perf->LastCounter = CurrCounter;
846       cygwin_load.perf->LastLoad = value;
847       DEBUG(D_load)
848         debug_printf("Perf: New load average %d\n", value);
849       }
850       else { /* Something bad happened.
851                 Refuse to measure the load anymore
852                 but don't bother releasing the buffer */
853         log_write(0, LOG_MAIN, "Cannot obtain Load Average");
854       cygwin_load.perf->LastLoad = -1;
855     }
856   }
857   else
858   DEBUG(D_load)
859       debug_printf("Perf: Old load average %d\n", cygwin_load.perf->LastLoad);
860   cygwin_load.perf->Lock = 0;
861   return cygwin_load.perf->LastLoad;
862 }
863 #endif /* OS_LOAD_AVERAGE */
864 #endif /* COMPILE_UTILITY */