1 /* $Cambridge: exim/src/OS/os.c-cygwin,v 1.4 2005/10/03 09:53:38 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 cygwin_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, void * cgp)
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')
184 cygwin_setpriority();
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 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;
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 */
234 /* Pretend we are root to avoid useless execs.
235 We are limited by file access rights */
236 * (uid_t *) rup = getuid ();
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;
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);
252 #ifndef OS_LOAD_AVERAGE /* Can be set on command line */
253 #define OS_LOAD_AVERAGE /* src/os.c need not provide it */
255 /*****************************************************************
257 Functions for average load measurements
259 There are two methods, which work only on NT.
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.
265 The second one uses NtQuerySystemInformation.
266 Its use is discouraged starting with WinXP.
268 Until 4.43, the Cygwin port of exim was using the first
271 *****************************************************************/
274 /* Structure to compute the load average efficiently */
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 */
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 */
293 } cygwin_load = {NULL, 0, NULL};
296 /*************************************************************
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.
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
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
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
326 *****************************************************************/
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"
335 #define BYTEINCREMENT 800 /* Block to add to PerfData */
337 /*****************************************************************
339 Macros to navigate through the performance data.
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))
360 /*****************************************************************
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
366 NameStrings seems to be taken from the file
367 X:\Winnt\system32\perfc009.dat
369 This is used only for name verification during initialization,
370 if DEBUG(D_load) is TRUE.
372 *****************************************************************/
373 static BOOL GetNameStrings( )
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
382 /* Get the number of Counter items into dwArraySize. */
383 if ((res = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
384 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib",
386 KEY_QUERY_VALUE, /* KEY_READ, */
389 DEBUG(D_load) debug_printf("RegOpenKeyEx (1): error %ld (Windows)\n", res);
392 dwNamesSize = sizeof(dwArraySize); /* Temporary reuse */
393 if ((res = RegQueryValueEx( hKeyPerflib,
397 (LPBYTE) &dwArraySize,
400 DEBUG(D_load) debug_printf("RegQueryValueEx (1): error %ld (Windows)\n", res);
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",
411 DEBUG(D_load) debug_printf("RegOpenKeyEx (2): error %ld (Windows)\n", res);
414 /* Get the size of the Counter value in the key
415 and then read the value in the tail of NamesArray */
417 lpCurrentString = NULL;
419 res = RegQueryValueEx( hKeyPerflib,
423 (unsigned char *) lpCurrentString,
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)))
433 /* Point to area for the counter and object names */
434 lpCurrentString = (LPSTR) & cygwin_load.perf->NamesArray[dwArraySize + 1];
437 DEBUG(D_load) debug_printf("Malloc: errno %d (%s)\n", errno, strerror(errno));
439 else { /* Serious error */
440 DEBUG(D_load) debug_printf("RegQueryValueEx (2): error %ld (Windows)\n", res);
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);
455 /*****************************************************************
457 Find the value of the Processor Time counter
459 *****************************************************************/
460 static BOOL ReadTimeCtr(PPERF_OBJECT_TYPE PerfObj,
461 PPERF_COUNTER_DEFINITION CurCntr,
462 PPERF_COUNTER_BLOCK PtrToCntr,
463 unsigned long long * TimePtr){
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]);
479 *TimePtr += *(unsigned long long int *) ((PBYTE) PtrToCntr + CurCntr->CounterOffset);
480 return TRUE; /* return TRUE as soon as we found the counter */
482 /* Get the next counter. */
483 CurCntr = NextCounter( CurCntr );
488 /*****************************************************************
491 Measures current Time100ns and IdleCount
492 Return TRUE if success.
494 *****************************************************************/
495 static BOOL ReadStat(unsigned long long int *Time100nsPtr,
496 unsigned long long int * IdleCountPtr)
498 PPERF_OBJECT_TYPE PerfObj;
499 PPERF_INSTANCE_DEFINITION PerfInst;
500 PPERF_COUNTER_DEFINITION PerfCntr;
501 PPERF_COUNTER_BLOCK PtrToCntr;
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 */
509 DWORD BufferSize = cygwin_load.perf->BufferSize;
510 res = RegQueryValueEx( HKEY_PERFORMANCE_DATA,
511 PROCESSOR_OBJECT_STRING,
514 (LPBYTE) cygwin_load.perf->PerfData,
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 ))
523 DEBUG(D_load) debug_printf("Malloc: errno %d (%s)\n", errno, strerror(errno));
525 else { /* Serious error */
526 DEBUG(D_load) debug_printf("RegQueryValueEx (3): error %ld (Windows)\n", res);
530 /* Initialize the counters */
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]);
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");
575 if (PerfInst->NameLength == 4) {
576 *Time100nsPtr += cygwin_load.perf->PerfData->PerfTime100nSec.QuadPart;
577 PtrToCntr = InstanceCounterBlock(PerfInst);
578 if (! ReadTimeCtr(PerfObj, PerfCntr, PtrToCntr, IdleCountPtr)) {
582 PerfInst = NextInstance( PerfInst );
584 return (*Time100nsPtr != 0); /* Something was read */
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);
592 PerfObj = NextObject( PerfObj );
594 return FALSE; /* Did not find the Processor object */
597 #elif defined(PERF_METHOD2)
599 /*************************************************************
602 Uses NtQuerySystemInformation.
603 This requires definitions that are not part of
604 standard include files.
605 *************************************************************/
608 typedef enum _SYSTEM_INFORMATION_CLASS
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;
619 typedef struct _SYSTEM_BASIC_INFORMATION
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;
634 typedef struct __attribute__ ((aligned (8))) _SYSTEM_PROCESSOR_TIMES
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;
644 typedef NTSTATUS NTAPI (*NtQuerySystemInformation_t) (SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG);
645 typedef ULONG NTAPI (*RtlNtStatusToDosError_t) (NTSTATUS);
647 static NtQuerySystemInformation_t NtQuerySystemInformation;
648 static RtlNtStatusToDosError_t RtlNtStatusToDosError;
650 /*****************************************************************
653 Load special functions from the NTDLL
654 Return TRUE if success.
656 *****************************************************************/
658 static BOOL LoadNtdll()
662 if ((hinstLib = LoadLibrary("NTDLL.DLL"))
663 && (NtQuerySystemInformation =
664 (NtQuerySystemInformation_t) GetProcAddress(hinstLib,
665 "NtQuerySystemInformation"))
666 && (RtlNtStatusToDosError =
667 (RtlNtStatusToDosError_t) GetProcAddress(hinstLib,
668 "RtlNtStatusToDosError")))
672 debug_printf("perf: load: %ld (Windows)\n", GetLastError());
676 /*****************************************************************
679 Measures current Time100ns and IdleCount
680 Return TRUE if success.
682 *****************************************************************/
684 static BOOL ReadStat(unsigned long long int *Time100nsPtr,
685 unsigned long long int *IdleCountPtr)
688 SYSTEM_BASIC_INFORMATION sbi;
689 PSYSTEM_PROCESSOR_TIMES spt;
691 *Time100nsPtr = *IdleCountPtr = 0;
693 if ((ret = NtQuerySystemInformation(SystemBasicInformation,
694 (PVOID) &sbi, sizeof sbi, NULL))
697 debug_printf("Perf: NtQuerySystemInformation: %lu (Windows)\n",
698 RtlNtStatusToDosError(ret));
700 else if (!(spt = (PSYSTEM_PROCESSOR_TIMES) alloca(sizeof(spt[0]) * sbi.NumberProcessors))) {
702 debug_printf("Perf: alloca: errno %d (%s)\n", errno, strerror(errno));
704 else if ((ret = NtQuerySystemInformation(SystemProcessorTimes, (PVOID) spt,
705 sizeof spt[0] * sbi.NumberProcessors, NULL))
708 debug_printf("Perf: NtQuerySystemInformation: %lu (Windows)\n",
709 RtlNtStatusToDosError(ret));
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;
722 #endif /* PERF_METHODX */
724 /*****************************************************************
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)
735 /* Get perf frequency and counter */
736 QueryPerformanceFrequency((LARGE_INTEGER *)& this->PerfFreq);
737 QueryPerformanceCounter((LARGE_INTEGER *)& this->LastCounter);
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();
747 /* Get initial values for Time100ns and IdleCount */
749 && ReadStat( & this->Time100ns,
751 /* If success, set the Load to 0, else to -1 */
752 if (success) this->LastLoad = 0;
754 log_write(0, LOG_MAIN, "Cannot obtain Load Average");
758 /* Free the buffer created for debug name verification */
759 if (this->NamesArray != NULL) {
760 free(this->NamesArray);
761 this->NamesArray = NULL;
767 /*****************************************************************
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.
775 The first time we are called we initialize the counts
777 The initial load cannot be measured as we use the processor 100%
778 *****************************************************************/
779 static SECURITY_ATTRIBUTES sa = {sizeof (SECURITY_ATTRIBUTES), NULL, TRUE};
784 unsigned long long Time100ns, IdleCount, CurrCounter;
789 Reload the dlls and the file mapping */
790 if ((newpid = getpid()) != cygwin_load.pid) {
792 cygwin_load.pid = newpid;
796 log_write(0, LOG_MAIN, "Cannot obtain Load Average");
797 cygwin_load.perf = NULL;
802 if ((new = !cygwin_load.handle)) {
803 cygwin_load.handle = CreateFileMapping (INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE,
804 0, sizeof(cygwin_perf_t), NULL);
806 debug_printf("Perf: CreateFileMapping: handle %x\n", (unsigned) cygwin_load.handle);
808 cygwin_load.perf = (cygwin_perf_t *) MapViewOfFile (cygwin_load.handle,
809 FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
811 debug_printf("Perf: MapViewOfFile: addr %x\n", (unsigned) cygwin_load.perf);
812 if (new && cygwin_load.perf)
813 InitLoadAvg(cygwin_load.perf);
816 /* Check if initialized OK */
817 if (!cygwin_load.perf || cygwin_load.perf->LastLoad < 0)
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 .*/
825 if (InterlockedCompareExchange(&cygwin_load.perf->Lock, 1, 0)) {
827 debug_printf("Perf: Lock busy\n");
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;
846 debug_printf("Perf: New load average %d\n", value);
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;
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;
861 #endif /* OS_LOAD_AVERAGE */
862 #endif /* COMPILE_UTILITY */