+++ /dev/null
-/*************************************************
-* Exim - an Internet mail transport agent *
-*************************************************/
-
-/* Cygwin-specific code. December 2002
- This is concatenated onto the generic src/os.c file.
-
- This code was supplied by Pierre A. Humblet <Pierre.Humblet@ieee.org>
-*/
-
-/* We need a special mkdir that
- allows names starting with // */
-#undef mkdir
-int cygwin_mkdir( const char *path, mode_t mode )
-{
- const char * p = path;
- if (*p == '/') while(*(p+1) == '/') p++;
- return mkdir(p, mode);
-}
-
-/* We have strsignal but cannot use #define
- because types don't match */
-#define OS_STRSIGNAL /* src/os.c need not provide it */
-char * os_strsignal(int sig)
-{
- return (char *) strsignal(sig);
-}
-
-#ifndef COMPILE_UTILITY /* Utilities don't need special code */
-#ifdef INCLUDE_MINIRES
-#include "../minires/minires.c"
-#include "../minires/os-interface.c"
-#endif
-
-#ifdef INCLUDE_PAM
-#include "../pam/pam.c"
-#endif
-
-unsigned int cygwin_WinVersion;
-
-/* Conflict between Windows definitions and others */
-#ifdef NOERROR
-#undef NOERROR
-#endif
-#ifdef DELETE
-#undef DELETE
-#endif
-
-#include <windows.h>
-#define EqualLuid(Luid1, Luid2) \
- ((Luid1.LowPart == Luid2.LowPart) && (Luid1.HighPart == Luid2.HighPart))
-#include <sys/cygwin.h>
-
-/* Special static variables */
-static BOOL cygwin_debug = FALSE;
-static int privileged = 1; /* when not privileged, setuid = noop */
-
-#undef setuid
-int cygwin_setuid(uid_t uid )
-{
- int res;
- if (privileged <= 0) return 0;
- else {
- res = setuid(uid);
- if (cygwin_debug)
- fprintf(stderr, "setuid %lu %lu %d pid: %d\n",
- uid, getuid(),res, getpid());
- }
- return res;
-}
-
-#undef setgid
-int cygwin_setgid(gid_t gid )
-{
- int res;
- if (privileged <= 0) return 0;
- else {
- res = setgid(gid);
- if (cygwin_debug)
- fprintf(stderr, "setgid %lu %lu %d pid: %d\n",
- gid, getgid(), res, getpid());
- }
- return res;
-}
-
-/* Background processes run at lower priority */
-static void cygwin_setpriority()
-{
- if (!SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS))
- SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS);
- return;
-}
-
-
-/* GetVersion()
- MSB: 1 for 95/98/ME; Next 7: build number, except for 95/98/ME
- Next byte: 0
- Next byte: minor version of OS
- Low byte: major version of OS (3 or 4 for for NT, 5 for 2000 and XP) */
-#define VERSION_IS_58M(x) (x & 0x80000000) /* 95, 98, Me */
-#define VERSION_IS_NT(x) ((x & 0XFF) < 5) /* NT 4 or 3.51 */
-
-/*
- Routine to find if process or thread is privileged
-*/
-
-enum {
- CREATE_BIT = 1,
- RESTORE_BIT = 2
-};
-
-static DWORD get_privileges ()
-{
- char buffer[1024];
- DWORD i, length;
- HANDLE hToken = NULL;
- PTOKEN_PRIVILEGES privs;
- LUID cluid, rluid;
- DWORD ret = 0;
-
- privs = (PTOKEN_PRIVILEGES) buffer;
-
- if (OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hToken)
- && LookupPrivilegeValue (NULL, SE_CREATE_TOKEN_NAME, &cluid)
- && LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &rluid)
- && (GetTokenInformation( hToken, TokenPrivileges,
- privs, sizeof (buffer), &length)
- || (GetLastError () == ERROR_INSUFFICIENT_BUFFER
- && (privs = (PTOKEN_PRIVILEGES) alloca (length))
- && GetTokenInformation(hToken, TokenPrivileges,
- privs, length, &length)))) {
- for (i = 0; i < privs->PrivilegeCount; i++) {
- if (EqualLuid(privs->Privileges[i].Luid, cluid))
- ret |= CREATE_BIT;
- else if (EqualLuid(privs->Privileges[i].Luid, rluid))
- ret |= RESTORE_BIT;
- else continue;
- if (ret == (CREATE_BIT | RESTORE_BIT))
- break;
- }
- }
- else
- fprintf(stderr, "has_create_token_privilege %ld\n", GetLastError());
-
- if (hToken)
- CloseHandle(hToken);
-
- return ret;
-}
-
-/* We use a special routine to initialize
- cygwin_init is called from the OS_INIT macro in main(). */
-
-void cygwin_init(int argc, char ** argv, void * rup,
- void * eup, void * egp, void * cup, void * cgp)
-{
- int i;
- uid_t myuid, systemuid;
- gid_t mygid, adminsgid;
- struct passwd * pwp;
- char *cygenv, win32_path[MAX_PATH];
- SID(1, SystemSid, SECURITY_LOCAL_SYSTEM_RID);
- SID(2, AdminsSid, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS);
- DWORD priv_flags;
-
- myuid = getuid();
- mygid = getgid();
- cygwin_WinVersion = GetVersion();
- if ((cygenv = getenv("CYGWIN")) == NULL) cygenv = "";
- /* Produce some debugging on stderr,
- cannot yet use exim's debug functions.
- Exim does not use -c and ignores -n.
- Set lower priority for daemons */
- for (i = 1; i < argc; i++) {
- if (argv[i][0] == '-') {
- if (argv[i][1] == 'c') {
- argv[i][1] = 'n'; /* Replace -c by -n */
- cygwin_debug = TRUE;
- fprintf(stderr, "CYGWIN = \"%s\".", cygenv);
- cygwin_conv_to_win32_path("/", win32_path);
- fprintf(stderr, " Root / mapped to %s.\n", win32_path);
- }
- else if (argv[i][1] == 'b' && argv[i][2] == 'd')
- cygwin_setpriority();
- }
- }
- if (VERSION_IS_58M(cygwin_WinVersion)) {
- * (uid_t *) rup = myuid; /* Pretend we are root */
- * (uid_t *) eup = myuid; /* ... and exim */
- * (gid_t *) egp = mygid;
- return;
- }
- /* Nt/2000/XP
- We initially set the exim uid & gid to those of the "real exim",
- or to the root uid (SYSTEM) and exim gid (ADMINS),
- If privileged, we setuid to those.
- We always set the configure uid to the system uid.
- We always set the root uid to the real uid
- to avoid useless execs following forks.
- If not privileged and unable to chown,
- we set the exim uid to our uid.
- If unprivileged, we fake all subsequent setuid. */
-
- priv_flags = get_privileges ();
- privileged = !!(priv_flags & CREATE_BIT);
-
- /* Get the system and admins uid from their sids,
- or use the default values from the Makefile. */
- if ((systemuid = cygwin_internal(CW_GET_UID_FROM_SID, & SystemSid)) == -1)
- systemuid = * (uid_t *) eup;
- if ((adminsgid = cygwin_internal(CW_GET_GID_FROM_SID, & AdminsSid)) == -1)
- adminsgid = * (gid_t *) egp;
-
- if ((pwp = getpwnam("exim")) != NULL) {
- * (uid_t *) eup = pwp->pw_uid; /* Set it according to passwd */
- * (gid_t *) egp = pwp->pw_gid;
- }
- else {
- * (uid_t *) eup = systemuid;
- * (gid_t *) egp = adminsgid;
- }
-
- /* Set the configuration uid and gid to the system uid and admins gid.
- Note that exim uid is also accepted as owner of exim.conf. */
- * (uid_t *) cup = systemuid;
- * (gid_t *) cgp = adminsgid;
-
- if (privileged) { /* Can setuid */
- if (cygwin_setgid(* (gid_t *) egp) /* Setuid to exim */
- || cygwin_setuid(* (uid_t *) eup))
- privileged = -1; /* Problem... Perhaps not in 544 */
- }
-
- /* Pretend we are root to avoid useless execs.
- We are limited by file access rights */
- * (uid_t *) rup = getuid ();
-
- /* If we have not setuid to exim and cannot chown,
- set the exim uid to our uid to avoid chown failures */
- if (privileged <= 0 && !(priv_flags & RESTORE_BIT))
- * (uid_t *) eup = * (uid_t *) rup;
-
- if (cygwin_debug) {
- fprintf(stderr, "Starting uid %ld, gid %ld, ntsec %lu, privileged %d.\n",
- myuid, mygid, cygwin_internal(CW_CHECK_NTSEC, NULL), privileged);
- fprintf(stderr, "root_uid %ld, exim_uid %ld, exim_gid %ld, config_uid %ld, config_gid %ld.\n",
- * (uid_t *) rup, * (uid_t *) eup, * (gid_t *) egp, * (uid_t *) cup, * (gid_t *) cgp);
- }
- return;
-}
-
-#ifndef OS_LOAD_AVERAGE /* Can be set on command line */
-#define OS_LOAD_AVERAGE /* src/os.c need not provide it */
-
-/*****************************************************************
- *
- Functions for average load measurements
-
- There are two methods, which work only on NT.
-
- The first one uses the HKEY_PERFORMANCE_DATA registry to
- get performance data. It is complex but well documented
- and works on all NT versions.
-
- The second one uses NtQuerySystemInformation.
- Its use is discouraged starting with WinXP.
-
- Until 4.43, the Cygwin port of exim was using the first
- method.
-
-*****************************************************************/
-#define PERF_METHOD2
-
-/* Structure to compute the load average efficiently */
-typedef struct {
- DWORD Lock;
- unsigned long long Time100ns; /* Last measurement time */
- unsigned long long IdleCount; /* Latest cumulative idle time */
- unsigned long long LastCounter; /* Last measurement counter */
- unsigned long long PerfFreq; /* Perf counter frequency */
- int LastLoad; /* Last reported load, or -1 */
-#ifdef PERF_METHOD1
- PPERF_DATA_BLOCK PerfData; /* Pointer to a buffer to get the data */
- DWORD BufferSize; /* Size of PerfData */
- LPSTR * NamesArray; /* Temporary (malloc) buffer for index */
-#endif
-} cygwin_perf_t;
-
-static struct {
- HANDLE handle;
- pid_t pid;
- cygwin_perf_t *perf;
-} cygwin_load = {NULL, 0, NULL};
-
-#ifdef PERF_METHOD1
-/*************************************************************
- METHOD 1
-
- Obtaining statistics in Windows is done at a low level by
- calling registry functions, in particular the key
- HKEY_PERFORMANCE_DATA on NT and successors.
- Something equivalent exists on Win95, see Microsoft article
- HOWTO: Access the Performance Registry Under Windows 95 (KB 174631)
- but it is not implemented here.
-
- The list of objects to be polled is specified in the string
- passed to RegQueryValueEx in ReadStat() below.
- On NT, all objects are polled even if info about only one is
- required. This is fixed in Windows 2000. See articles
- INFO: Perflib Calling Close Procedure in Windows 2000 (KB 270127)
- INFO: Performance Data Changes Between Windows NT 4.0 and Windows
- 2000 (KB 296523)
-
- It is unclear to me how the counters are primarily identified.
- Whether it's by name strings or by the offset of their strings
- as mapped in X:\Winnt\system32\perfc009.dat [or equivalently as
- reported by the registry functions in GetNameStrings( ) below].
- Microsoft documentation seems to say that both methods should
- work.
-
- In the interest of speed and language independence, the main
- code below relies on offsets. However if debug is enabled, the
- code verifies that the names of the corresponding strings are
- as expected.
-
-*****************************************************************/
-
-/* Object and counter indices and names */
-#define PROCESSOR_OBJECT_INDEX 238
-#define PROCESSOR_OBJECT_STRING "238"
-#define PROCESSOR_OBJECT_NAME "Processor"
-#define PROCESSOR_TIME_COUNTER 6
-#define PROCESSOR_TIME_NAME "% Processor Time"
-
-#define BYTEINCREMENT 800 /* Block to add to PerfData */
-
-/*****************************************************************
- *
- Macros to navigate through the performance data.
-
- *****************************************************************/
-#define FirstObject(PerfData)\
- ((PPERF_OBJECT_TYPE)((PBYTE)PerfData + PerfData->HeaderLength))
-#define NextObject(PerfObj)\
- ((PPERF_OBJECT_TYPE)((PBYTE)PerfObj + PerfObj->TotalByteLength))
-#define ObjectCounterBlock(PerfObj)\
- ((PPERF_COUNTER_BLOCK)(PBYTE)PerfObj + PerfObj->DefinitionLength )
-#define FirstInstance(PerfObj )\
- ((PPERF_INSTANCE_DEFINITION)((PBYTE)PerfObj + PerfObj->DefinitionLength))
-#define InstanceCounterBlock(PerfInst)\
- ((PPERF_COUNTER_BLOCK) ((PBYTE)PerfInst + PerfInst->ByteLength ))
-#define NextInstance(PerfInst )\
- ((PPERF_INSTANCE_DEFINITION)((PBYTE)InstanceCounterBlock(PerfInst) + \
- InstanceCounterBlock(PerfInst)->ByteLength) )
-#define FirstCounter(PerfObj)\
- ((PPERF_COUNTER_DEFINITION) ((PBYTE)PerfObj + PerfObj->HeaderLength))
-#define NextCounter(PerfCntr)\
- ((PPERF_COUNTER_DEFINITION)((PBYTE)PerfCntr + PerfCntr->ByteLength))
-
-/*****************************************************************
- *
- Load the counter and object names from the registry
- to cygwin_load.perf->NameStrings
- and index them in cygwin_load.perf->NamesArray
-
- NameStrings seems to be taken from the file
- X:\Winnt\system32\perfc009.dat
-
- This is used only for name verification during initialization,
- if DEBUG(D_load) is TRUE.
-
-*****************************************************************/
-static BOOL GetNameStrings( )
-{
- HKEY hKeyPerflib; // handle to registry key
- DWORD dwArraySize; // size for array
- DWORD dwNamesSize; // size for strings
- LPSTR lpCurrentString; // pointer for enumerating data strings
- DWORD dwCounter; // current counter index
- LONG res;
-
- /* Get the number of Counter items into dwArraySize. */
- if ((res = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
- "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib",
- 0,
- KEY_QUERY_VALUE, /* KEY_READ, */
- &hKeyPerflib))
- != ERROR_SUCCESS) {
- DEBUG(D_load) debug_printf("RegOpenKeyEx (1): error %ld (Windows)\n", res);
- return FALSE;
- }
- dwNamesSize = sizeof(dwArraySize); /* Temporary reuse */
- if ((res = RegQueryValueEx( hKeyPerflib,
- "Last Counter",
- NULL,
- NULL,
- (LPBYTE) &dwArraySize,
- &dwNamesSize ))
- != ERROR_SUCCESS) {
- DEBUG(D_load) debug_printf("RegQueryValueEx (1): error %ld (Windows)\n", res);
- return FALSE;
- }
- RegCloseKey( hKeyPerflib );
- /* Open the key containing the counter and object names. */
- if ((res = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
- "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009",
- 0,
- KEY_READ,
- &hKeyPerflib))
- != ERROR_SUCCESS) {
- DEBUG(D_load) debug_printf("RegOpenKeyEx (2): error %ld (Windows)\n", res);
- return FALSE;
- }
- /* Get the size of the Counter value in the key
- and then read the value in the tail of NamesArray */
- dwNamesSize = 0;
- lpCurrentString = NULL;
- while (1) {
- res = RegQueryValueEx( hKeyPerflib,
- "Counter",
- NULL,
- NULL,
- (unsigned char *) lpCurrentString,
- &dwNamesSize);
- if ((res == ERROR_SUCCESS) && /* Bug (NT 4.0): SUCCESS was returned on first call */
- (cygwin_load.perf->NamesArray != NULL)) break;
- if ((res == ERROR_SUCCESS) || /* but cygwin_load.perf->NamesArrays == NULL */
- (res == ERROR_MORE_DATA)) {
- /* Allocate memory BOTH for the names array and for the counter and object names */
- if ((cygwin_load.perf->NamesArray =
- (LPSTR *) malloc( (dwArraySize + 1) * sizeof(LPSTR) + dwNamesSize * sizeof(CHAR)))
- != NULL) {
- /* Point to area for the counter and object names */
- lpCurrentString = (LPSTR) & cygwin_load.perf->NamesArray[dwArraySize + 1];
- continue;
- }
- DEBUG(D_load) debug_printf("Malloc: errno %d (%s)\n", errno, strerror(errno));
- }
- else { /* Serious error */
- DEBUG(D_load) debug_printf("RegQueryValueEx (2): error %ld (Windows)\n", res);
- }
- return FALSE;
- }
- RegCloseKey( hKeyPerflib );
- /* Index the names into an array. */
- while (*lpCurrentString) {
- dwCounter = atol( lpCurrentString );
- lpCurrentString += (lstrlen(lpCurrentString)+1);
- cygwin_load.perf->NamesArray[dwCounter] = lpCurrentString;
- lpCurrentString += (strlen(lpCurrentString)+1);
- }
- return TRUE;
-}
-
-/*****************************************************************
- *
- Find the value of the Processor Time counter
-
-*****************************************************************/
-static BOOL ReadTimeCtr(PPERF_OBJECT_TYPE PerfObj,
- PPERF_COUNTER_DEFINITION CurCntr,
- PPERF_COUNTER_BLOCK PtrToCntr,
- unsigned long long * TimePtr){
- int j;
- /* Scan all counters. */
- for( j = 0; j < PerfObj->NumCounters; j++ ) {
- if (CurCntr->CounterNameTitleIndex == PROCESSOR_TIME_COUNTER) {
- /* Verify it is really the proc time counter */
- if ((CurCntr->CounterType != PERF_100NSEC_TIMER_INV) || /* Wrong type */
- ((cygwin_load.perf->NamesArray != NULL) && /* Verify name */
- (strcmp(cygwin_load.perf->NamesArray[CurCntr->CounterNameTitleIndex],
- PROCESSOR_TIME_NAME)))) {
- log_write(0, LOG_MAIN|LOG_PANIC,
- "Incorrect Perf counter type or name %x %s",
- (unsigned) CurCntr->CounterType,
- cygwin_load.perf->NamesArray[CurCntr->CounterNameTitleIndex]);
- return FALSE;
- }
- *TimePtr += *(unsigned long long int *) ((PBYTE) PtrToCntr + CurCntr->CounterOffset);
- return TRUE; /* return TRUE as soon as we found the counter */
- }
- /* Get the next counter. */
- CurCntr = NextCounter( CurCntr );
- }
- return FALSE;
-}
-
-/*****************************************************************
- *
- ReadStat()
- Measures current Time100ns and IdleCount
- Return TRUE if success.
-
- *****************************************************************/
-static BOOL ReadStat(unsigned long long int *Time100nsPtr,
- unsigned long long int * IdleCountPtr)
-{
- PPERF_OBJECT_TYPE PerfObj;
- PPERF_INSTANCE_DEFINITION PerfInst;
- PPERF_COUNTER_DEFINITION PerfCntr;
- PPERF_COUNTER_BLOCK PtrToCntr;
- DWORD i, k, res;
-
- /* Get the performance data for the Processor object
- There is no need to open a key.
- We may need to blindly increase the buffer size.
- BufferSize does not return info but may be changed */
- while (1) {
- DWORD BufferSize = cygwin_load.perf->BufferSize;
- res = RegQueryValueEx( HKEY_PERFORMANCE_DATA,
- PROCESSOR_OBJECT_STRING,
- NULL,
- NULL,
- (LPBYTE) cygwin_load.perf->PerfData,
- &BufferSize );
- if (res == ERROR_SUCCESS) break;
- if (res == ERROR_MORE_DATA ) {
- /* Increment if necessary to get a buffer that is big enough. */
- cygwin_load.perf->BufferSize += BYTEINCREMENT;
- if ((cygwin_load.perf->PerfData =
- (PPERF_DATA_BLOCK) realloc( cygwin_load.perf->PerfData, cygwin_load.perf->BufferSize ))
- != NULL) continue;
- DEBUG(D_load) debug_printf("Malloc: errno %d (%s)\n", errno, strerror(errno));
- }
- else { /* Serious error */
- DEBUG(D_load) debug_printf("RegQueryValueEx (3): error %ld (Windows)\n", res);
- }
- return FALSE;
- }
- /* Initialize the counters */
- *Time100nsPtr = 0;
- *IdleCountPtr = 0;
- /* We should only have one object, but write general code just in case. */
- PerfObj = FirstObject( cygwin_load.perf->PerfData );
- for( i = 0; i < cygwin_load.perf->PerfData->NumObjectTypes; i++ ) {
- /* We are only interested in the processor object */
- if ( PerfObj->ObjectNameTitleIndex == PROCESSOR_OBJECT_INDEX) {
- /* Possibly verify it is really the Processor object. */
- if ((cygwin_load.perf->NamesArray != NULL) &&
- (strcmp(cygwin_load.perf->NamesArray[PerfObj->ObjectNameTitleIndex],
- PROCESSOR_OBJECT_NAME))) {
- log_write(0, LOG_MAIN|LOG_PANIC,
- "Incorrect Perf object name %s",
- cygwin_load.perf->NamesArray[PerfObj->ObjectNameTitleIndex]);
- return FALSE;
- }
- /* Get the first counter */
- PerfCntr = FirstCounter( PerfObj );
- /* See if the object has instances.
- It should, but write general code. */
- if( PerfObj->NumInstances != PERF_NO_INSTANCES ) {
- PerfInst = FirstInstance( PerfObj );
- for( k = 0; k < PerfObj->NumInstances; k++ ) {
- /* There can be several processors.
- Accumulate both the Time100ns and the idle counter.
- Starting with Win2000 there is an instance named "_Total".
- Do not use it. We only use instances with a single
- character in the name.
- If we examine the object names, we also look at the instance
- names and their lengths and issue reports */
- if ( cygwin_load.perf->NamesArray != NULL) {
- CHAR ascii[30]; /* The name is in unicode */
- wsprintf(ascii,"%.29lS",
- (char *)((PBYTE)PerfInst + PerfInst->NameOffset));
- log_write(0, LOG_MAIN,
- "Perf: Found processor instance \"%s\", length %d",
- ascii, PerfInst->NameLength);
- if ((PerfInst->NameLength != 4) &&
- (strcmp(ascii, "_Total") != 0)) {
- log_write(0, LOG_MAIN|LOG_PANIC,
- "Perf: WARNING: Unexpected processor instance name");
- return FALSE;
- }
- }
- if (PerfInst->NameLength == 4) {
- *Time100nsPtr += cygwin_load.perf->PerfData->PerfTime100nSec.QuadPart;
- PtrToCntr = InstanceCounterBlock(PerfInst);
- if (! ReadTimeCtr(PerfObj, PerfCntr, PtrToCntr, IdleCountPtr)) {
- return FALSE;
- }
- }
- PerfInst = NextInstance( PerfInst );
- }
- return (*Time100nsPtr != 0); /* Something was read */
- }
- else { /* No instance, just the counter data */
- *Time100nsPtr = cygwin_load.perf->PerfData->PerfTime100nSec.QuadPart;
- PtrToCntr = ObjectCounterBlock(PerfObj);
- return ReadTimeCtr(PerfObj, PerfCntr, PtrToCntr, IdleCountPtr);
- }
- }
- PerfObj = NextObject( PerfObj );
- }
- return FALSE; /* Did not find the Processor object */
-}
-
-#elif defined(PERF_METHOD2)
-
-/*************************************************************
- METHOD 2
-
- Uses NtQuerySystemInformation.
- This requires definitions that are not part of
- standard include files.
-*************************************************************/
-#include <ntdef.h>
-
-typedef enum _SYSTEM_INFORMATION_CLASS
-{
- SystemBasicInformation = 0,
- SystemPerformanceInformation = 2,
- SystemTimeOfDayInformation = 3,
- SystemProcessesAndThreadsInformation = 5,
- SystemProcessorTimes = 8,
- SystemPagefileInformation = 18,
- /* There are a lot more of these... */
-} SYSTEM_INFORMATION_CLASS;
-
-typedef struct _SYSTEM_BASIC_INFORMATION
-{
- ULONG Unknown;
- ULONG MaximumIncrement;
- ULONG PhysicalPageSize;
- ULONG NumberOfPhysicalPages;
- ULONG LowestPhysicalPage;
- ULONG HighestPhysicalPage;
- ULONG AllocationGranularity;
- ULONG LowestUserAddress;
- ULONG HighestUserAddress;
- ULONG ActiveProcessors;
- UCHAR NumberProcessors;
-} SYSTEM_BASIC_INFORMATION, *PSYSTEM_BASIC_INFORMATION;
-
-typedef struct __attribute__ ((aligned (8))) _SYSTEM_PROCESSOR_TIMES
-{
- LARGE_INTEGER IdleTime;
- LARGE_INTEGER KernelTime;
- LARGE_INTEGER UserTime;
- LARGE_INTEGER DpcTime;
- LARGE_INTEGER InterruptTime;
- ULONG InterruptCount;
-} SYSTEM_PROCESSOR_TIMES, *PSYSTEM_PROCESSOR_TIMES;
-
-typedef NTSTATUS NTAPI (*NtQuerySystemInformation_t) (SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG);
-typedef ULONG NTAPI (*RtlNtStatusToDosError_t) (NTSTATUS);
-
-static NtQuerySystemInformation_t NtQuerySystemInformation;
-static RtlNtStatusToDosError_t RtlNtStatusToDosError;
-
-/*****************************************************************
- *
- LoadNtdll()
- Load special functions from the NTDLL
- Return TRUE if success.
-
- *****************************************************************/
-
-static BOOL LoadNtdll()
-{
- HINSTANCE hinstLib;
-
- if ((hinstLib = LoadLibrary("NTDLL.DLL"))
- && (NtQuerySystemInformation =
- (NtQuerySystemInformation_t) GetProcAddress(hinstLib,
- "NtQuerySystemInformation"))
- && (RtlNtStatusToDosError =
- (RtlNtStatusToDosError_t) GetProcAddress(hinstLib,
- "RtlNtStatusToDosError")))
- return TRUE;
-
- DEBUG(D_load)
- debug_printf("perf: load: %ld (Windows)\n", GetLastError());
- return FALSE;
-}
-
-/*****************************************************************
- *
- ReadStat()
- Measures current Time100ns and IdleCount
- Return TRUE if success.
-
- *****************************************************************/
-
-static BOOL ReadStat(unsigned long long int *Time100nsPtr,
- unsigned long long int *IdleCountPtr)
-{
- NTSTATUS ret;
- SYSTEM_BASIC_INFORMATION sbi;
- PSYSTEM_PROCESSOR_TIMES spt;
-
- *Time100nsPtr = *IdleCountPtr = 0;
-
- if ((ret = NtQuerySystemInformation(SystemBasicInformation,
- (PVOID) &sbi, sizeof sbi, NULL))
- != STATUS_SUCCESS) {
- DEBUG(D_load)
- debug_printf("Perf: NtQuerySystemInformation: %lu (Windows)\n",
- RtlNtStatusToDosError(ret));
- }
- else if (!(spt = (PSYSTEM_PROCESSOR_TIMES) alloca(sizeof(spt[0]) * sbi.NumberProcessors))) {
- DEBUG(D_load)
- debug_printf("Perf: alloca: errno %d (%s)\n", errno, strerror(errno));
- }
- else if ((ret = NtQuerySystemInformation(SystemProcessorTimes, (PVOID) spt,
- sizeof spt[0] * sbi.NumberProcessors, NULL))
- != STATUS_SUCCESS) {
- DEBUG(D_load)
- debug_printf("Perf: NtQuerySystemInformation: %lu (Windows)\n",
- RtlNtStatusToDosError(ret));
- }
- else {
- int i;
- for (i = 0; i < sbi.NumberProcessors; i++) {
- *Time100nsPtr += spt[i].KernelTime.QuadPart;;
- *Time100nsPtr += spt[i].UserTime.QuadPart;
- *IdleCountPtr += spt[i].IdleTime.QuadPart;
- }
- return TRUE;
- }
- return FALSE;
-}
-#endif /* PERF_METHODX */
-
-/*****************************************************************
- *
- InitLoadAvg()
- Initialize the cygwin_load.perf structure.
- and set cygwin_load.perf->Flag to TRUE if successful.
- This is called the first time os_getloadavg is called
- *****************************************************************/
-static void InitLoadAvg(cygwin_perf_t *this)
-{
- BOOL success = TRUE;
-
- /* Get perf frequency and counter */
- QueryPerformanceFrequency((LARGE_INTEGER *)& this->PerfFreq);
- QueryPerformanceCounter((LARGE_INTEGER *)& this->LastCounter);
-
-#ifdef PERF_METHOD1
- DEBUG(D_load) {
- /* Get the name strings through the registry
- to verify that the object and counter numbers
- have the names we expect */
- success = GetNameStrings();
- }
-#endif
- /* Get initial values for Time100ns and IdleCount */
- success = success
- && ReadStat( & this->Time100ns,
- & this->IdleCount);
- /* If success, set the Load to 0, else to -1 */
- if (success) this->LastLoad = 0;
- else {
- log_write(0, LOG_MAIN, "Cannot obtain Load Average");
- this->LastLoad = -1;
- }
-#ifdef PERF_METHOD1
- /* Free the buffer created for debug name verification */
- if (this->NamesArray != NULL) {
- free(this->NamesArray);
- this->NamesArray = NULL;
- }
-#endif
-}
-
-
-/*****************************************************************
- *
- os_getloadavg()
-
- Return -1 if not available;
- Return the previous value if less than AVERAGING sec old.
- else return the processor load on a [0 - 1000] scale.
-
- The first time we are called we initialize the counts
- and return 0 or -1.
- The initial load cannot be measured as we use the processor 100%
-*****************************************************************/
-static SECURITY_ATTRIBUTES sa = {sizeof (SECURITY_ATTRIBUTES), NULL, TRUE};
-#define AVERAGING 10
-
-int os_getloadavg()
-{
- unsigned long long Time100ns, IdleCount, CurrCounter;
- int value;
- pid_t newpid;
-
- /* New process.
- Reload the dlls and the file mapping */
- if ((newpid = getpid()) != cygwin_load.pid) {
- BOOL new;
- cygwin_load.pid = newpid;
-
-#ifdef PERF_METHOD2
- if (!LoadNtdll()) {
- log_write(0, LOG_MAIN, "Cannot obtain Load Average");
- cygwin_load.perf = NULL;
- return -1;
- }
-#endif
-
- if ((new = !cygwin_load.handle)) {
- cygwin_load.handle = CreateFileMapping (INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE,
- 0, sizeof(cygwin_perf_t), NULL);
- DEBUG(D_load)
- debug_printf("Perf: CreateFileMapping: handle %x\n", (unsigned) cygwin_load.handle);
- }
- cygwin_load.perf = (cygwin_perf_t *) MapViewOfFile (cygwin_load.handle,
- FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
- DEBUG(D_load)
- debug_printf("Perf: MapViewOfFile: addr %x\n", (unsigned) cygwin_load.perf);
- if (new && cygwin_load.perf)
- InitLoadAvg(cygwin_load.perf);
- }
-
- /* Check if initialized OK */
- if (!cygwin_load.perf || cygwin_load.perf->LastLoad < 0)
- return -1;
-
- /* If we cannot get the lock, we return 0.
- This is to prevent any lock-up possibility.
- Finding a lock busy is unlikely, and giving up only
- results in an immediate delivery .*/
-
- if (InterlockedCompareExchange(&cygwin_load.perf->Lock, 1, 0)) {
- DEBUG(D_load)
- debug_printf("Perf: Lock busy\n");
- return 0;
- }
-
- /* Get the current time (PerfCounter) */
- QueryPerformanceCounter((LARGE_INTEGER *)& CurrCounter);
- /* Calls closer than AVERAGING sec apart use the previous value */
- if (CurrCounter - cygwin_load.perf->LastCounter >
- AVERAGING * cygwin_load.perf->PerfFreq) {
- /* Get Time100ns and IdleCount */
- if (ReadStat( & Time100ns, & IdleCount)) { /* Success */
- /* Return processor load on 1000 scale */
- value = 1000 - ((1000 * (IdleCount - cygwin_load.perf->IdleCount)) /
- (Time100ns - cygwin_load.perf->Time100ns));
- cygwin_load.perf->Time100ns = Time100ns;
- cygwin_load.perf->IdleCount = IdleCount;
- cygwin_load.perf->LastCounter = CurrCounter;
- cygwin_load.perf->LastLoad = value;
- DEBUG(D_load)
- debug_printf("Perf: New load average %d\n", value);
- }
- else { /* Something bad happened.
- Refuse to measure the load anymore
- but don't bother releasing the buffer */
- log_write(0, LOG_MAIN, "Cannot obtain Load Average");
- cygwin_load.perf->LastLoad = -1;
- }
- }
- else
- DEBUG(D_load)
- debug_printf("Perf: Old load average %d\n", cygwin_load.perf->LastLoad);
- cygwin_load.perf->Lock = 0;
- return cygwin_load.perf->LastLoad;
-}
-#endif /* OS_LOAD_AVERAGE */
-#endif /* COMPILE_UTILITY */