Documentation for randint. Better randomness defaults. Fixes: #722
[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 */