1 /* $Cambridge: exim/src/OS/os.c-cygwin,v 1.5 2006/03/08 09:43:10 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 #define EqualLuid(Luid1, Luid2) \
53 ((Luid1.LowPart == Luid2.LowPart) && (Luid1.HighPart == Luid2.HighPart))
54 #include <sys/cygwin.h>
56 /* Special static variables */
57 static BOOL cygwin_debug = FALSE;
58 static int privileged = 1; /* when not privileged, setuid = noop */
61 int cygwin_setuid(uid_t uid )
64 if (privileged <= 0) return 0;
68 fprintf(stderr, "setuid %lu %lu %d pid: %d\n",
69 uid, getuid(),res, getpid());
75 int cygwin_setgid(gid_t gid )
78 if (privileged <= 0) return 0;
82 fprintf(stderr, "setgid %lu %lu %d pid: %d\n",
83 gid, getgid(), res, getpid());
88 /* Background processes run at lower priority */
89 static void cygwin_setpriority()
91 if (!SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS))
92 SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS);
98 MSB: 1 for 95/98/ME; Next 7: build number, except for 95/98/ME
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 */
106 Routine to find if process or thread is privileged
114 static DWORD get_privileges ()
118 HANDLE hToken = NULL;
119 PTOKEN_PRIVILEGES privs;
123 privs = (PTOKEN_PRIVILEGES) buffer;
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))
137 else if (EqualLuid(privs->Privileges[i].Luid, rluid))
140 if (ret == (CREATE_BIT | RESTORE_BIT))
145 fprintf(stderr, "has_create_token_privilege %ld\n", GetLastError());
153 /* We use a special routine to initialize
154 cygwin_init is called from the OS_INIT macro in main(). */
156 void cygwin_init(int argc, char ** argv, void * rup,
157 void * eup, void * egp, void * cup, void * cgp)
160 uid_t myuid, systemuid;
161 gid_t mygid, adminsgid;
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);
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 */
181 fprintf(stderr, "CYGWIN = \"%s\".", cygenv);
182 cygwin_conv_to_win32_path("/", win32_path);
183 fprintf(stderr, " Root / mapped to %s.\n", win32_path);
185 else if (argv[i][1] == 'b' && argv[i][2] == 'd')
186 cygwin_setpriority();
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;
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. */
206 priv_flags = get_privileges ();
207 privileged = !!(priv_flags & CREATE_BIT);
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;
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;
221 * (uid_t *) eup = systemuid;
222 * (gid_t *) egp = adminsgid;
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;
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 */
236 /* Pretend we are root to avoid useless execs.
237 We are limited by file access rights */
238 * (uid_t *) rup = getuid ();
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;
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);
254 #ifndef OS_LOAD_AVERAGE /* Can be set on command line */
255 #define OS_LOAD_AVERAGE /* src/os.c need not provide it */
257 /*****************************************************************
259 Functions for average load measurements
261 There are two methods, which work only on NT.
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.
267 The second one uses NtQuerySystemInformation.
268 Its use is discouraged starting with WinXP.
270 Until 4.43, the Cygwin port of exim was using the first
273 *****************************************************************/
276 /* Structure to compute the load average efficiently */
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 */
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 */
295 } cygwin_load = {NULL, 0, NULL};
298 /*************************************************************
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.
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
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
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
328 *****************************************************************/
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"
337 #define BYTEINCREMENT 800 /* Block to add to PerfData */
339 /*****************************************************************
341 Macros to navigate through the performance data.
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))
362 /*****************************************************************
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
368 NameStrings seems to be taken from the file
369 X:\Winnt\system32\perfc009.dat
371 This is used only for name verification during initialization,
372 if DEBUG(D_load) is TRUE.
374 *****************************************************************/
375 static BOOL GetNameStrings( )
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
384 /* Get the number of Counter items into dwArraySize. */
385 if ((res = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
386 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib",
388 KEY_QUERY_VALUE, /* KEY_READ, */
391 DEBUG(D_load) debug_printf("RegOpenKeyEx (1): error %ld (Windows)\n", res);
394 dwNamesSize = sizeof(dwArraySize); /* Temporary reuse */
395 if ((res = RegQueryValueEx( hKeyPerflib,
399 (LPBYTE) &dwArraySize,
402 DEBUG(D_load) debug_printf("RegQueryValueEx (1): error %ld (Windows)\n", res);
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",
413 DEBUG(D_load) debug_printf("RegOpenKeyEx (2): error %ld (Windows)\n", res);
416 /* Get the size of the Counter value in the key
417 and then read the value in the tail of NamesArray */
419 lpCurrentString = NULL;
421 res = RegQueryValueEx( hKeyPerflib,
425 (unsigned char *) lpCurrentString,
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)))
435 /* Point to area for the counter and object names */
436 lpCurrentString = (LPSTR) & cygwin_load.perf->NamesArray[dwArraySize + 1];
439 DEBUG(D_load) debug_printf("Malloc: errno %d (%s)\n", errno, strerror(errno));
441 else { /* Serious error */
442 DEBUG(D_load) debug_printf("RegQueryValueEx (2): error %ld (Windows)\n", res);
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);
457 /*****************************************************************
459 Find the value of the Processor Time counter
461 *****************************************************************/
462 static BOOL ReadTimeCtr(PPERF_OBJECT_TYPE PerfObj,
463 PPERF_COUNTER_DEFINITION CurCntr,
464 PPERF_COUNTER_BLOCK PtrToCntr,
465 unsigned long long * TimePtr){
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]);
481 *TimePtr += *(unsigned long long int *) ((PBYTE) PtrToCntr + CurCntr->CounterOffset);
482 return TRUE; /* return TRUE as soon as we found the counter */
484 /* Get the next counter. */
485 CurCntr = NextCounter( CurCntr );
490 /*****************************************************************
493 Measures current Time100ns and IdleCount
494 Return TRUE if success.
496 *****************************************************************/
497 static BOOL ReadStat(unsigned long long int *Time100nsPtr,
498 unsigned long long int * IdleCountPtr)
500 PPERF_OBJECT_TYPE PerfObj;
501 PPERF_INSTANCE_DEFINITION PerfInst;
502 PPERF_COUNTER_DEFINITION PerfCntr;
503 PPERF_COUNTER_BLOCK PtrToCntr;
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 */
511 DWORD BufferSize = cygwin_load.perf->BufferSize;
512 res = RegQueryValueEx( HKEY_PERFORMANCE_DATA,
513 PROCESSOR_OBJECT_STRING,
516 (LPBYTE) cygwin_load.perf->PerfData,
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 ))
525 DEBUG(D_load) debug_printf("Malloc: errno %d (%s)\n", errno, strerror(errno));
527 else { /* Serious error */
528 DEBUG(D_load) debug_printf("RegQueryValueEx (3): error %ld (Windows)\n", res);
532 /* Initialize the counters */
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]);
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");
577 if (PerfInst->NameLength == 4) {
578 *Time100nsPtr += cygwin_load.perf->PerfData->PerfTime100nSec.QuadPart;
579 PtrToCntr = InstanceCounterBlock(PerfInst);
580 if (! ReadTimeCtr(PerfObj, PerfCntr, PtrToCntr, IdleCountPtr)) {
584 PerfInst = NextInstance( PerfInst );
586 return (*Time100nsPtr != 0); /* Something was read */
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);
594 PerfObj = NextObject( PerfObj );
596 return FALSE; /* Did not find the Processor object */
599 #elif defined(PERF_METHOD2)
601 /*************************************************************
604 Uses NtQuerySystemInformation.
605 This requires definitions that are not part of
606 standard include files.
607 *************************************************************/
610 typedef enum _SYSTEM_INFORMATION_CLASS
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;
621 typedef struct _SYSTEM_BASIC_INFORMATION
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;
636 typedef struct __attribute__ ((aligned (8))) _SYSTEM_PROCESSOR_TIMES
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;
646 typedef NTSTATUS NTAPI (*NtQuerySystemInformation_t) (SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG);
647 typedef ULONG NTAPI (*RtlNtStatusToDosError_t) (NTSTATUS);
649 static NtQuerySystemInformation_t NtQuerySystemInformation;
650 static RtlNtStatusToDosError_t RtlNtStatusToDosError;
652 /*****************************************************************
655 Load special functions from the NTDLL
656 Return TRUE if success.
658 *****************************************************************/
660 static BOOL LoadNtdll()
664 if ((hinstLib = LoadLibrary("NTDLL.DLL"))
665 && (NtQuerySystemInformation =
666 (NtQuerySystemInformation_t) GetProcAddress(hinstLib,
667 "NtQuerySystemInformation"))
668 && (RtlNtStatusToDosError =
669 (RtlNtStatusToDosError_t) GetProcAddress(hinstLib,
670 "RtlNtStatusToDosError")))
674 debug_printf("perf: load: %ld (Windows)\n", GetLastError());
678 /*****************************************************************
681 Measures current Time100ns and IdleCount
682 Return TRUE if success.
684 *****************************************************************/
686 static BOOL ReadStat(unsigned long long int *Time100nsPtr,
687 unsigned long long int *IdleCountPtr)
690 SYSTEM_BASIC_INFORMATION sbi;
691 PSYSTEM_PROCESSOR_TIMES spt;
693 *Time100nsPtr = *IdleCountPtr = 0;
695 if ((ret = NtQuerySystemInformation(SystemBasicInformation,
696 (PVOID) &sbi, sizeof sbi, NULL))
699 debug_printf("Perf: NtQuerySystemInformation: %lu (Windows)\n",
700 RtlNtStatusToDosError(ret));
702 else if (!(spt = (PSYSTEM_PROCESSOR_TIMES) alloca(sizeof(spt[0]) * sbi.NumberProcessors))) {
704 debug_printf("Perf: alloca: errno %d (%s)\n", errno, strerror(errno));
706 else if ((ret = NtQuerySystemInformation(SystemProcessorTimes, (PVOID) spt,
707 sizeof spt[0] * sbi.NumberProcessors, NULL))
710 debug_printf("Perf: NtQuerySystemInformation: %lu (Windows)\n",
711 RtlNtStatusToDosError(ret));
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;
724 #endif /* PERF_METHODX */
726 /*****************************************************************
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)
737 /* Get perf frequency and counter */
738 QueryPerformanceFrequency((LARGE_INTEGER *)& this->PerfFreq);
739 QueryPerformanceCounter((LARGE_INTEGER *)& this->LastCounter);
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();
749 /* Get initial values for Time100ns and IdleCount */
751 && ReadStat( & this->Time100ns,
753 /* If success, set the Load to 0, else to -1 */
754 if (success) this->LastLoad = 0;
756 log_write(0, LOG_MAIN, "Cannot obtain Load Average");
760 /* Free the buffer created for debug name verification */
761 if (this->NamesArray != NULL) {
762 free(this->NamesArray);
763 this->NamesArray = NULL;
769 /*****************************************************************
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.
777 The first time we are called we initialize the counts
779 The initial load cannot be measured as we use the processor 100%
780 *****************************************************************/
781 static SECURITY_ATTRIBUTES sa = {sizeof (SECURITY_ATTRIBUTES), NULL, TRUE};
786 unsigned long long Time100ns, IdleCount, CurrCounter;
791 Reload the dlls and the file mapping */
792 if ((newpid = getpid()) != cygwin_load.pid) {
794 cygwin_load.pid = newpid;
798 log_write(0, LOG_MAIN, "Cannot obtain Load Average");
799 cygwin_load.perf = NULL;
804 if ((new = !cygwin_load.handle)) {
805 cygwin_load.handle = CreateFileMapping (INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE,
806 0, sizeof(cygwin_perf_t), NULL);
808 debug_printf("Perf: CreateFileMapping: handle %x\n", (unsigned) cygwin_load.handle);
810 cygwin_load.perf = (cygwin_perf_t *) MapViewOfFile (cygwin_load.handle,
811 FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
813 debug_printf("Perf: MapViewOfFile: addr %x\n", (unsigned) cygwin_load.perf);
814 if (new && cygwin_load.perf)
815 InitLoadAvg(cygwin_load.perf);
818 /* Check if initialized OK */
819 if (!cygwin_load.perf || cygwin_load.perf->LastLoad < 0)
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 .*/
827 if (InterlockedCompareExchange(&cygwin_load.perf->Lock, 1, 0)) {
829 debug_printf("Perf: Lock busy\n");
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;
848 debug_printf("Perf: New load average %d\n", value);
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;
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;
863 #endif /* OS_LOAD_AVERAGE */
864 #endif /* COMPILE_UTILITY */