1 /* $Cambridge: exim/src/OS/os.c-cygwin,v 1.2 2005/03/29 11:01:32 ph10 Exp $ */
3 /*************************************************
4 * Exim - an Internet mail transport agent *
5 *************************************************/
7 /* Cygwin-specific code. December 2002
8 This is concatenated onto the generic src/os.c file.
10 This code was supplied by Pierre A. Humblet <Pierre.Humblet@ieee.org>
13 /* We need a special mkdir that
14 allows names starting with // */
16 int cygwin_mkdir( const char *path, mode_t mode )
18 const char * p = path;
19 if (*p == '/') while(*(p+1) == '/') p++;
20 return mkdir(p, mode);
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)
28 return (char *) strsignal(sig);
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"
38 #include "../pam/pam.c"
41 unsigned int cygwin_WinVersion;
43 /* Conflict between Windows definitions and others */
52 #include <sys/cygwin.h>
54 /* Special static variables */
55 static BOOL cygwin_debug = FALSE;
56 static int privileged = 1; /* when not privileged, setuid = noop */
59 int cygwin_setuid(uid_t uid )
62 if (privileged <= 0) return 0;
66 fprintf(stderr, "setuid %lu %lu %d pid: %d\n",
67 uid, getuid(),res, getpid());
73 int cygwin_setgid(gid_t gid )
76 if (privileged <= 0) return 0;
80 fprintf(stderr, "setgid %lu %lu %d pid: %d\n",
81 gid, getgid(), res, getpid());
86 /* Background processes run at lower priority */
87 static void setpriority()
89 if (!SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS))
90 SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS);
96 MSB: 1 for 95/98/ME; Next 7: build number, except for 95/98/ME
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 */
104 Routine to find if process or thread is privileged
112 static DWORD get_privileges ()
116 HANDLE hToken = NULL;
117 PTOKEN_PRIVILEGES privs;
121 privs = (PTOKEN_PRIVILEGES) buffer;
123 if (OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hToken)
124 && LookupPrivilegeValue (NULL, SE_CREATE_TOKEN_NAME, &cluid)
125 && LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &rluid)
126 && (GetTokenInformation( hToken, TokenPrivileges,
127 privs, sizeof (buffer), &length)
128 || (GetLastError () == ERROR_INSUFFICIENT_BUFFER
129 && (privs = (PTOKEN_PRIVILEGES) alloca (length))
130 && GetTokenInformation(hToken, TokenPrivileges,
131 privs, length, &length)))) {
132 for (i = 0; i < privs->PrivilegeCount; i++) {
133 if (privs->Privileges[i].Luid.QuadPart == cluid.QuadPart)
135 else if (privs->Privileges[i].Luid.QuadPart == rluid.QuadPart)
138 if (ret == (CREATE_BIT | RESTORE_BIT))
143 fprintf(stderr, "has_create_token_privilege %ld\n", GetLastError());
151 /* We use a special routine to initialize
152 cygwin_init is called from the OS_INIT macro in main(). */
154 void cygwin_init(int argc, char ** argv, void * rup,
155 void * eup, void * egp, void * cup)
158 uid_t myuid, systemuid;
159 gid_t mygid, adminsgid;
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);
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 */
179 fprintf(stderr, "CYGWIN = \"%s\".", cygenv);
180 cygwin_conv_to_win32_path("/", win32_path);
181 fprintf(stderr, " Root / mapped to %s.\n", win32_path);
183 else if (argv[i][1] == 'b' && argv[i][2] == 'd')
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;
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. */
204 priv_flags = get_privileges ();
205 privileged = !!(priv_flags & CREATE_BIT);
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;
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;
219 * (uid_t *) eup = systemuid;
220 * (gid_t *) egp = adminsgid;
223 /* Set the configuration uid to the system uid.
224 Note that exim uid is also accepted as owner of exim.conf. */
225 * (uid_t *) cup = systemuid;
227 if (privileged) { /* Can setuid */
228 if (cygwin_setgid(* (gid_t *) egp) /* Setuid to exim */
229 || cygwin_setuid(* (uid_t *) eup))
230 privileged = -1; /* Problem... Perhaps not in 544 */
233 /* Pretend we are root to avoid useless execs.
234 We are limited by file access rights */
235 * (uid_t *) rup = getuid ();
237 /* If we have not setuid to exim and cannot chown,
238 set the exim uid to our uid to avoid chown failures */
239 if (privileged <= 0 && !(priv_flags & RESTORE_BIT))
240 * (uid_t *) eup = * (uid_t *) rup;
243 fprintf(stderr, "Starting uid %ld, gid %ld, ntsec %lu, privileged %d.\n",
244 myuid, mygid, cygwin_internal(CW_CHECK_NTSEC, NULL), privileged);
245 fprintf(stderr, "root_uid %ld, exim_uid %ld, exim_gid %ld, config_uid %ld.\n",
246 * (uid_t *) rup, * (uid_t *) eup, * (gid_t *) egp, * (uid_t *) cup);
251 #ifndef OS_LOAD_AVERAGE /* Can be set on command line */
252 #define OS_LOAD_AVERAGE /* src/os.c need not provide it */
254 /*****************************************************************
256 Functions for average load measurements
258 There are two methods, which work only on NT.
260 The first one uses the HKEY_PERFORMANCE_DATA registry to
261 get performance data. It is complex but well documented
262 and works on all NT versions.
264 The second one uses NtQuerySystemInformation.
265 Its use is discouraged starting with WinXP.
267 Until 4.43, the Cygwin port of exim was using the first
270 *****************************************************************/
273 /* Structure to compute the load average efficiently */
276 unsigned long long Time100ns; /* Last measurement time */
277 unsigned long long IdleCount; /* Latest cumulative idle time */
278 unsigned long long LastCounter; /* Last measurement counter */
279 unsigned long long PerfFreq; /* Perf counter frequency */
280 int LastLoad; /* Last reported load, or -1 */
282 PPERF_DATA_BLOCK PerfData; /* Pointer to a buffer to get the data */
283 DWORD BufferSize; /* Size of PerfData */
284 LPSTR * NamesArray; /* Temporary (malloc) buffer for index */
292 } cygwin_load = {NULL, 0, NULL};
295 /*************************************************************
298 Obtaining statistics in Windows is done at a low level by
299 calling registry functions, in particular the key
300 HKEY_PERFORMANCE_DATA on NT and successors.
301 Something equivalent exists on Win95, see Microsoft article
302 HOWTO: Access the Performance Registry Under Windows 95 (KB 174631)
303 but it is not implemented here.
305 The list of objects to be polled is specified in the string
306 passed to RegQueryValueEx in ReadStat() below.
307 On NT, all objects are polled even if info about only one is
308 required. This is fixed in Windows 2000. See articles
309 INFO: Perflib Calling Close Procedure in Windows 2000 (KB 270127)
310 INFO: Performance Data Changes Between Windows NT 4.0 and Windows
313 It is unclear to me how the counters are primarily identified.
314 Whether it's by name strings or by the offset of their strings
315 as mapped in X:\Winnt\system32\perfc009.dat [or equivalently as
316 reported by the registry functions in GetNameStrings( ) below].
317 Microsoft documentation seems to say that both methods should
320 In the interest of speed and language independence, the main
321 code below relies on offsets. However if debug is enabled, the
322 code verifies that the names of the corresponding strings are
325 *****************************************************************/
327 /* Object and counter indices and names */
328 #define PROCESSOR_OBJECT_INDEX 238
329 #define PROCESSOR_OBJECT_STRING "238"
330 #define PROCESSOR_OBJECT_NAME "Processor"
331 #define PROCESSOR_TIME_COUNTER 6
332 #define PROCESSOR_TIME_NAME "% Processor Time"
334 #define BYTEINCREMENT 800 /* Block to add to PerfData */
336 /*****************************************************************
338 Macros to navigate through the performance data.
340 *****************************************************************/
341 #define FirstObject(PerfData)\
342 ((PPERF_OBJECT_TYPE)((PBYTE)PerfData + PerfData->HeaderLength))
343 #define NextObject(PerfObj)\
344 ((PPERF_OBJECT_TYPE)((PBYTE)PerfObj + PerfObj->TotalByteLength))
345 #define ObjectCounterBlock(PerfObj)\
346 ((PPERF_COUNTER_BLOCK)(PBYTE)PerfObj + PerfObj->DefinitionLength )
347 #define FirstInstance(PerfObj )\
348 ((PPERF_INSTANCE_DEFINITION)((PBYTE)PerfObj + PerfObj->DefinitionLength))
349 #define InstanceCounterBlock(PerfInst)\
350 ((PPERF_COUNTER_BLOCK) ((PBYTE)PerfInst + PerfInst->ByteLength ))
351 #define NextInstance(PerfInst )\
352 ((PPERF_INSTANCE_DEFINITION)((PBYTE)InstanceCounterBlock(PerfInst) + \
353 InstanceCounterBlock(PerfInst)->ByteLength) )
354 #define FirstCounter(PerfObj)\
355 ((PPERF_COUNTER_DEFINITION) ((PBYTE)PerfObj + PerfObj->HeaderLength))
356 #define NextCounter(PerfCntr)\
357 ((PPERF_COUNTER_DEFINITION)((PBYTE)PerfCntr + PerfCntr->ByteLength))
359 /*****************************************************************
361 Load the counter and object names from the registry
362 to cygwin_load.perf->NameStrings
363 and index them in cygwin_load.perf->NamesArray
365 NameStrings seems to be taken from the file
366 X:\Winnt\system32\perfc009.dat
368 This is used only for name verification during initialization,
369 if DEBUG(D_load) is TRUE.
371 *****************************************************************/
372 static BOOL GetNameStrings( )
374 HKEY hKeyPerflib; // handle to registry key
375 DWORD dwArraySize; // size for array
376 DWORD dwNamesSize; // size for strings
377 LPSTR lpCurrentString; // pointer for enumerating data strings
378 DWORD dwCounter; // current counter index
381 /* Get the number of Counter items into dwArraySize. */
382 if ((res = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
383 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib",
385 KEY_QUERY_VALUE, /* KEY_READ, */
388 DEBUG(D_load) debug_printf("RegOpenKeyEx (1): error %ld (Windows)\n", res);
391 dwNamesSize = sizeof(dwArraySize); /* Temporary reuse */
392 if ((res = RegQueryValueEx( hKeyPerflib,
396 (LPBYTE) &dwArraySize,
399 DEBUG(D_load) debug_printf("RegQueryValueEx (1): error %ld (Windows)\n", res);
402 RegCloseKey( hKeyPerflib );
403 /* Open the key containing the counter and object names. */
404 if ((res = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
405 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009",
410 DEBUG(D_load) debug_printf("RegOpenKeyEx (2): error %ld (Windows)\n", res);
413 /* Get the size of the Counter value in the key
414 and then read the value in the tail of NamesArray */
416 lpCurrentString = NULL;
418 res = RegQueryValueEx( hKeyPerflib,
422 (unsigned char *) lpCurrentString,
424 if ((res == ERROR_SUCCESS) && /* Bug (NT 4.0): SUCCESS was returned on first call */
425 (cygwin_load.perf->NamesArray != NULL)) break;
426 if ((res == ERROR_SUCCESS) || /* but cygwin_load.perf->NamesArrays == NULL */
427 (res == ERROR_MORE_DATA)) {
428 /* Allocate memory BOTH for the names array and for the counter and object names */
429 if ((cygwin_load.perf->NamesArray =
430 (LPSTR *) malloc( (dwArraySize + 1) * sizeof(LPSTR) + dwNamesSize * sizeof(CHAR)))
432 /* Point to area for the counter and object names */
433 lpCurrentString = (LPSTR) & cygwin_load.perf->NamesArray[dwArraySize + 1];
436 DEBUG(D_load) debug_printf("Malloc: errno %d (%s)\n", errno, strerror(errno));
438 else { /* Serious error */
439 DEBUG(D_load) debug_printf("RegQueryValueEx (2): error %ld (Windows)\n", res);
443 RegCloseKey( hKeyPerflib );
444 /* Index the names into an array. */
445 while (*lpCurrentString) {
446 dwCounter = atol( lpCurrentString );
447 lpCurrentString += (lstrlen(lpCurrentString)+1);
448 cygwin_load.perf->NamesArray[dwCounter] = lpCurrentString;
449 lpCurrentString += (strlen(lpCurrentString)+1);
454 /*****************************************************************
456 Find the value of the Processor Time counter
458 *****************************************************************/
459 static BOOL ReadTimeCtr(PPERF_OBJECT_TYPE PerfObj,
460 PPERF_COUNTER_DEFINITION CurCntr,
461 PPERF_COUNTER_BLOCK PtrToCntr,
462 unsigned long long * TimePtr){
464 /* Scan all counters. */
465 for( j = 0; j < PerfObj->NumCounters; j++ ) {
466 if (CurCntr->CounterNameTitleIndex == PROCESSOR_TIME_COUNTER) {
467 /* Verify it is really the proc time counter */
468 if ((CurCntr->CounterType != PERF_100NSEC_TIMER_INV) || /* Wrong type */
469 ((cygwin_load.perf->NamesArray != NULL) && /* Verify name */
470 (strcmp(cygwin_load.perf->NamesArray[CurCntr->CounterNameTitleIndex],
471 PROCESSOR_TIME_NAME)))) {
472 log_write(0, LOG_MAIN|LOG_PANIC,
473 "Incorrect Perf counter type or name %x %s",
474 (unsigned) CurCntr->CounterType,
475 cygwin_load.perf->NamesArray[CurCntr->CounterNameTitleIndex]);
478 *TimePtr += *(unsigned long long int *) ((PBYTE) PtrToCntr + CurCntr->CounterOffset);
479 return TRUE; /* return TRUE as soon as we found the counter */
481 /* Get the next counter. */
482 CurCntr = NextCounter( CurCntr );
487 /*****************************************************************
490 Measures current Time100ns and IdleCount
491 Return TRUE if success.
493 *****************************************************************/
494 static BOOL ReadStat(unsigned long long int *Time100nsPtr,
495 unsigned long long int * IdleCountPtr)
497 PPERF_OBJECT_TYPE PerfObj;
498 PPERF_INSTANCE_DEFINITION PerfInst;
499 PPERF_COUNTER_DEFINITION PerfCntr;
500 PPERF_COUNTER_BLOCK PtrToCntr;
503 /* Get the performance data for the Processor object
504 There is no need to open a key.
505 We may need to blindly increase the buffer size.
506 BufferSize does not return info but may be changed */
508 DWORD BufferSize = cygwin_load.perf->BufferSize;
509 res = RegQueryValueEx( HKEY_PERFORMANCE_DATA,
510 PROCESSOR_OBJECT_STRING,
513 (LPBYTE) cygwin_load.perf->PerfData,
515 if (res == ERROR_SUCCESS) break;
516 if (res == ERROR_MORE_DATA ) {
517 /* Increment if necessary to get a buffer that is big enough. */
518 cygwin_load.perf->BufferSize += BYTEINCREMENT;
519 if ((cygwin_load.perf->PerfData =
520 (PPERF_DATA_BLOCK) realloc( cygwin_load.perf->PerfData, cygwin_load.perf->BufferSize ))
522 DEBUG(D_load) debug_printf("Malloc: errno %d (%s)\n", errno, strerror(errno));
524 else { /* Serious error */
525 DEBUG(D_load) debug_printf("RegQueryValueEx (3): error %ld (Windows)\n", res);
529 /* Initialize the counters */
532 /* We should only have one object, but write general code just in case. */
533 PerfObj = FirstObject( cygwin_load.perf->PerfData );
534 for( i = 0; i < cygwin_load.perf->PerfData->NumObjectTypes; i++ ) {
535 /* We are only interested in the processor object */
536 if ( PerfObj->ObjectNameTitleIndex == PROCESSOR_OBJECT_INDEX) {
537 /* Possibly verify it is really the Processor object. */
538 if ((cygwin_load.perf->NamesArray != NULL) &&
539 (strcmp(cygwin_load.perf->NamesArray[PerfObj->ObjectNameTitleIndex],
540 PROCESSOR_OBJECT_NAME))) {
541 log_write(0, LOG_MAIN|LOG_PANIC,
542 "Incorrect Perf object name %s",
543 cygwin_load.perf->NamesArray[PerfObj->ObjectNameTitleIndex]);
546 /* Get the first counter */
547 PerfCntr = FirstCounter( PerfObj );
548 /* See if the object has instances.
549 It should, but write general code. */
550 if( PerfObj->NumInstances != PERF_NO_INSTANCES ) {
551 PerfInst = FirstInstance( PerfObj );
552 for( k = 0; k < PerfObj->NumInstances; k++ ) {
553 /* There can be several processors.
554 Accumulate both the Time100ns and the idle counter.
555 Starting with Win2000 there is an instance named "_Total".
556 Do not use it. We only use instances with a single
557 character in the name.
558 If we examine the object names, we also look at the instance
559 names and their lengths and issue reports */
560 if ( cygwin_load.perf->NamesArray != NULL) {
561 CHAR ascii[30]; /* The name is in unicode */
562 wsprintf(ascii,"%.29lS",
563 (char *)((PBYTE)PerfInst + PerfInst->NameOffset));
564 log_write(0, LOG_MAIN,
565 "Perf: Found processor instance \"%s\", length %d",
566 ascii, PerfInst->NameLength);
567 if ((PerfInst->NameLength != 4) &&
568 (strcmp(ascii, "_Total") != 0)) {
569 log_write(0, LOG_MAIN|LOG_PANIC,
570 "Perf: WARNING: Unexpected processor instance name");
574 if (PerfInst->NameLength == 4) {
575 *Time100nsPtr += cygwin_load.perf->PerfData->PerfTime100nSec.QuadPart;
576 PtrToCntr = InstanceCounterBlock(PerfInst);
577 if (! ReadTimeCtr(PerfObj, PerfCntr, PtrToCntr, IdleCountPtr)) {
581 PerfInst = NextInstance( PerfInst );
583 return (*Time100nsPtr != 0); /* Something was read */
585 else { /* No instance, just the counter data */
586 *Time100nsPtr = cygwin_load.perf->PerfData->PerfTime100nSec.QuadPart;
587 PtrToCntr = ObjectCounterBlock(PerfObj);
588 return ReadTimeCtr(PerfObj, PerfCntr, PtrToCntr, IdleCountPtr);
591 PerfObj = NextObject( PerfObj );
593 return FALSE; /* Did not find the Processor object */
596 #elif defined(PERF_METHOD2)
598 /*************************************************************
601 Uses NtQuerySystemInformation.
602 This requires definitions that are not part of
603 standard include files.
604 *************************************************************/
607 typedef enum _SYSTEM_INFORMATION_CLASS
609 SystemBasicInformation = 0,
610 SystemPerformanceInformation = 2,
611 SystemTimeOfDayInformation = 3,
612 SystemProcessesAndThreadsInformation = 5,
613 SystemProcessorTimes = 8,
614 SystemPagefileInformation = 18,
615 /* There are a lot more of these... */
616 } SYSTEM_INFORMATION_CLASS;
618 typedef struct _SYSTEM_BASIC_INFORMATION
621 ULONG MaximumIncrement;
622 ULONG PhysicalPageSize;
623 ULONG NumberOfPhysicalPages;
624 ULONG LowestPhysicalPage;
625 ULONG HighestPhysicalPage;
626 ULONG AllocationGranularity;
627 ULONG LowestUserAddress;
628 ULONG HighestUserAddress;
629 ULONG ActiveProcessors;
630 UCHAR NumberProcessors;
631 } SYSTEM_BASIC_INFORMATION, *PSYSTEM_BASIC_INFORMATION;
633 typedef struct __attribute__ ((aligned (8))) _SYSTEM_PROCESSOR_TIMES
635 LARGE_INTEGER IdleTime;
636 LARGE_INTEGER KernelTime;
637 LARGE_INTEGER UserTime;
638 LARGE_INTEGER DpcTime;
639 LARGE_INTEGER InterruptTime;
640 ULONG InterruptCount;
641 } SYSTEM_PROCESSOR_TIMES, *PSYSTEM_PROCESSOR_TIMES;
643 typedef NTSTATUS NTAPI (*NtQuerySystemInformation_t) (SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG);
644 typedef ULONG NTAPI (*RtlNtStatusToDosError_t) (NTSTATUS);
646 static NtQuerySystemInformation_t NtQuerySystemInformation;
647 static RtlNtStatusToDosError_t RtlNtStatusToDosError;
649 /*****************************************************************
652 Load special functions from the NTDLL
653 Return TRUE if success.
655 *****************************************************************/
657 static BOOL LoadNtdll()
661 if ((hinstLib = LoadLibrary("NTDLL.DLL"))
662 && (NtQuerySystemInformation =
663 (NtQuerySystemInformation_t) GetProcAddress(hinstLib,
664 "NtQuerySystemInformation"))
665 && (RtlNtStatusToDosError =
666 (RtlNtStatusToDosError_t) GetProcAddress(hinstLib,
667 "RtlNtStatusToDosError")))
671 debug_printf("perf: load: %ld (Windows)\n", GetLastError());
675 /*****************************************************************
678 Measures current Time100ns and IdleCount
679 Return TRUE if success.
681 *****************************************************************/
683 static BOOL ReadStat(unsigned long long int *Time100nsPtr,
684 unsigned long long int *IdleCountPtr)
687 SYSTEM_BASIC_INFORMATION sbi;
688 PSYSTEM_PROCESSOR_TIMES spt;
690 *Time100nsPtr = *IdleCountPtr = 0;
692 if ((ret = NtQuerySystemInformation(SystemBasicInformation,
693 (PVOID) &sbi, sizeof sbi, NULL))
696 debug_printf("Perf: NtQuerySystemInformation: %lu (Windows)\n",
697 RtlNtStatusToDosError(ret));
699 else if (!(spt = (PSYSTEM_PROCESSOR_TIMES) alloca(sizeof(spt[0]) * sbi.NumberProcessors))) {
701 debug_printf("Perf: alloca: errno %d (%s)\n", errno, strerror(errno));
703 else if ((ret = NtQuerySystemInformation(SystemProcessorTimes, (PVOID) spt,
704 sizeof spt[0] * sbi.NumberProcessors, NULL))
707 debug_printf("Perf: NtQuerySystemInformation: %lu (Windows)\n",
708 RtlNtStatusToDosError(ret));
712 for (i = 0; i < sbi.NumberProcessors; i++) {
713 *Time100nsPtr += spt[i].KernelTime.QuadPart;;
714 *Time100nsPtr += spt[i].UserTime.QuadPart;
715 *IdleCountPtr += spt[i].IdleTime.QuadPart;
721 #endif /* PERF_METHODX */
723 /*****************************************************************
726 Initialize the cygwin_load.perf structure.
727 and set cygwin_load.perf->Flag to TRUE if successful.
728 This is called the first time os_getloadavg is called
729 *****************************************************************/
730 static void InitLoadAvg(cygwin_perf_t *this)
734 /* Get perf frequency and counter */
735 QueryPerformanceFrequency((LARGE_INTEGER *)& this->PerfFreq);
736 QueryPerformanceCounter((LARGE_INTEGER *)& this->LastCounter);
740 /* Get the name strings through the registry
741 to verify that the object and counter numbers
742 have the names we expect */
743 success = GetNameStrings();
746 /* Get initial values for Time100ns and IdleCount */
748 && ReadStat( & this->Time100ns,
750 /* If success, set the Load to 0, else to -1 */
751 if (success) this->LastLoad = 0;
753 log_write(0, LOG_MAIN, "Cannot obtain Load Average");
757 /* Free the buffer created for debug name verification */
758 if (this->NamesArray != NULL) {
759 free(this->NamesArray);
760 this->NamesArray = NULL;
766 /*****************************************************************
770 Return -1 if not available;
771 Return the previous value if less than AVERAGING sec old.
772 else return the processor load on a [0 - 1000] scale.
774 The first time we are called we initialize the counts
776 The initial load cannot be measured as we use the processor 100%
777 *****************************************************************/
778 static SECURITY_ATTRIBUTES sa = {sizeof (SECURITY_ATTRIBUTES), NULL, TRUE};
783 unsigned long long Time100ns, IdleCount, CurrCounter;
788 Reload the dlls and the file mapping */
789 if ((newpid = getpid()) != cygwin_load.pid) {
791 cygwin_load.pid = newpid;
795 log_write(0, LOG_MAIN, "Cannot obtain Load Average");
796 cygwin_load.perf = NULL;
801 if ((new = !cygwin_load.handle)) {
802 cygwin_load.handle = CreateFileMapping (INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE,
803 0, sizeof(cygwin_perf_t), NULL);
805 debug_printf("Perf: CreateFileMapping: handle %x\n", (unsigned) cygwin_load.handle);
807 cygwin_load.perf = (cygwin_perf_t *) MapViewOfFile (cygwin_load.handle,
808 FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
810 debug_printf("Perf: MapViewOfFile: addr %x\n", (unsigned) cygwin_load.perf);
811 if (new && cygwin_load.perf)
812 InitLoadAvg(cygwin_load.perf);
815 /* Check if initialized OK */
816 if (!cygwin_load.perf || cygwin_load.perf->LastLoad < 0)
819 /* If we cannot get the lock, we return 0.
820 This is to prevent any lock-up possibility.
821 Finding a lock busy is unlikely, and giving up only
822 results in an immediate delivery .*/
824 if (InterlockedCompareExchange(&cygwin_load.perf->Lock, 1, 0)) {
826 debug_printf("Perf: Lock busy\n");
830 /* Get the current time (PerfCounter) */
831 QueryPerformanceCounter((LARGE_INTEGER *)& CurrCounter);
832 /* Calls closer than AVERAGING sec apart use the previous value */
833 if (CurrCounter - cygwin_load.perf->LastCounter >
834 AVERAGING * cygwin_load.perf->PerfFreq) {
835 /* Get Time100ns and IdleCount */
836 if (ReadStat( & Time100ns, & IdleCount)) { /* Success */
837 /* Return processor load on 1000 scale */
838 value = 1000 - ((1000 * (IdleCount - cygwin_load.perf->IdleCount)) /
839 (Time100ns - cygwin_load.perf->Time100ns));
840 cygwin_load.perf->Time100ns = Time100ns;
841 cygwin_load.perf->IdleCount = IdleCount;
842 cygwin_load.perf->LastCounter = CurrCounter;
843 cygwin_load.perf->LastLoad = value;
845 debug_printf("Perf: New load average %d\n", value);
847 else { /* Something bad happened.
848 Refuse to measure the load anymore
849 but don't bother releasing the buffer */
850 log_write(0, LOG_MAIN, "Cannot obtain Load Average");
851 cygwin_load.perf->LastLoad = -1;
856 debug_printf("Perf: Old load average %d\n", cygwin_load.perf->LastLoad);
857 cygwin_load.perf->Lock = 0;
858 return cygwin_load.perf->LastLoad;
860 #endif /* OS_LOAD_AVERAGE */
861 #endif /* COMPILE_UTILITY */