Note about ratelimit resets.
[exim.git] / src / OS / os.c-cygwin
CommitLineData
2c5db4fd 1/* $Cambridge: exim/src/OS/os.c-cygwin,v 1.5 2006/03/08 09:43:10 ph10 Exp $ */
61ec970d
PH
2
3/*************************************************
4* Exim - an Internet mail transport agent *
5*************************************************/
6
7/* Cygwin-specific code. December 2002
8 This is concatenated onto the generic src/os.c file.
9
10 This code was supplied by Pierre A. Humblet <Pierre.Humblet@ieee.org>
11*/
12
13/* We need a special mkdir that
14 allows names starting with // */
15#undef mkdir
16int cygwin_mkdir( const char *path, mode_t mode )
17{
18 const char * p = path;
19 if (*p == '/') while(*(p+1) == '/') p++;
20 return mkdir(p, mode);
21}
22
23/* We have strsignal but cannot use #define
24 because types don't match */
25#define OS_STRSIGNAL /* src/os.c need not provide it */
26char * os_strsignal(int sig)
27{
28 return (char *) strsignal(sig);
29}
30
31#ifndef COMPILE_UTILITY /* Utilities don't need special code */
32#ifdef INCLUDE_MINIRES
33#include "../minires/minires.c"
34#include "../minires/os-interface.c"
35#endif
36
37#ifdef INCLUDE_PAM
38#include "../pam/pam.c"
39#endif
40
41unsigned int cygwin_WinVersion;
42
43/* Conflict between Windows definitions and others */
44#ifdef NOERROR
45#undef NOERROR
46#endif
47#ifdef DELETE
48#undef DELETE
49#endif
50
51#include <windows.h>
2c5db4fd
PH
52#define EqualLuid(Luid1, Luid2) \
53 ((Luid1.LowPart == Luid2.LowPart) && (Luid1.HighPart == Luid2.HighPart))
61ec970d
PH
54#include <sys/cygwin.h>
55
56/* Special static variables */
57static BOOL cygwin_debug = FALSE;
58static int privileged = 1; /* when not privileged, setuid = noop */
59
60#undef setuid
61int cygwin_setuid(uid_t uid )
62{
63 int res;
64 if (privileged <= 0) return 0;
65 else {
66 res = setuid(uid);
67 if (cygwin_debug)
68 fprintf(stderr, "setuid %lu %lu %d pid: %d\n",
69 uid, getuid(),res, getpid());
70 }
71 return res;
72}
73
74#undef setgid
75int cygwin_setgid(gid_t gid )
76{
77 int res;
78 if (privileged <= 0) return 0;
79 else {
80 res = setgid(gid);
81 if (cygwin_debug)
82 fprintf(stderr, "setgid %lu %lu %d pid: %d\n",
8c841523 83 gid, getgid(), res, getpid());
61ec970d
PH
84 }
85 return res;
86}
87
88/* Background processes run at lower priority */
b6c6011d 89static void cygwin_setpriority()
61ec970d
PH
90{
91 if (!SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS))
92 SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS);
93 return;
94}
95
96
97/* GetVersion()
98 MSB: 1 for 95/98/ME; Next 7: build number, except for 95/98/ME
99 Next byte: 0
100 Next byte: minor version of OS
101 Low byte: major version of OS (3 or 4 for for NT, 5 for 2000 and XP) */
102#define VERSION_IS_58M(x) (x & 0x80000000) /* 95, 98, Me */
103#define VERSION_IS_NT(x) ((x & 0XFF) < 5) /* NT 4 or 3.51 */
104
105/*
106 Routine to find if process or thread is privileged
107*/
108
109enum {
110 CREATE_BIT = 1,
111 RESTORE_BIT = 2
112};
113
114static DWORD get_privileges ()
115{
116 char buffer[1024];
117 DWORD i, length;
118 HANDLE hToken = NULL;
119 PTOKEN_PRIVILEGES privs;
120 LUID cluid, rluid;
121 DWORD ret = 0;
122
123 privs = (PTOKEN_PRIVILEGES) buffer;
124
125 if (OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hToken)
126 && LookupPrivilegeValue (NULL, SE_CREATE_TOKEN_NAME, &cluid)
127 && LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &rluid)
128 && (GetTokenInformation( hToken, TokenPrivileges,
8c841523
PH
129 privs, sizeof (buffer), &length)
130 || (GetLastError () == ERROR_INSUFFICIENT_BUFFER
131 && (privs = (PTOKEN_PRIVILEGES) alloca (length))
132 && GetTokenInformation(hToken, TokenPrivileges,
133 privs, length, &length)))) {
61ec970d 134 for (i = 0; i < privs->PrivilegeCount; i++) {
2c5db4fd 135 if (EqualLuid(privs->Privileges[i].Luid, cluid))
8c841523 136 ret |= CREATE_BIT;
2c5db4fd 137 else if (EqualLuid(privs->Privileges[i].Luid, rluid))
8c841523 138 ret |= RESTORE_BIT;
61ec970d
PH
139 else continue;
140 if (ret == (CREATE_BIT | RESTORE_BIT))
8c841523 141 break;
61ec970d
PH
142 }
143 }
144 else
145 fprintf(stderr, "has_create_token_privilege %ld\n", GetLastError());
146
147 if (hToken)
148 CloseHandle(hToken);
149
150 return ret;
151}
152
153/* We use a special routine to initialize
154 cygwin_init is called from the OS_INIT macro in main(). */
155
156void cygwin_init(int argc, char ** argv, void * rup,
b6c6011d 157 void * eup, void * egp, void * cup, void * cgp)
61ec970d
PH
158{
159 int i;
160 uid_t myuid, systemuid;
161 gid_t mygid, adminsgid;
162 struct passwd * pwp;
163 char *cygenv, win32_path[MAX_PATH];
164 SID(1, SystemSid, SECURITY_LOCAL_SYSTEM_RID);
165 SID(2, AdminsSid, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS);
166 DWORD priv_flags;
167
168 myuid = getuid();
169 mygid = getgid();
170 cygwin_WinVersion = GetVersion();
171 if ((cygenv = getenv("CYGWIN")) == NULL) cygenv = "";
172 /* Produce some debugging on stderr,
173 cannot yet use exim's debug functions.
174 Exim does not use -c and ignores -n.
175 Set lower priority for daemons */
176 for (i = 1; i < argc; i++) {
177 if (argv[i][0] == '-') {
178 if (argv[i][1] == 'c') {
179 argv[i][1] = 'n'; /* Replace -c by -n */
180 cygwin_debug = TRUE;
181 fprintf(stderr, "CYGWIN = \"%s\".", cygenv);
182 cygwin_conv_to_win32_path("/", win32_path);
183 fprintf(stderr, " Root / mapped to %s.\n", win32_path);
184 }
185 else if (argv[i][1] == 'b' && argv[i][2] == 'd')
b6c6011d 186 cygwin_setpriority();
61ec970d
PH
187 }
188 }
189 if (VERSION_IS_58M(cygwin_WinVersion)) {
190 * (uid_t *) rup = myuid; /* Pretend we are root */
191 * (uid_t *) eup = myuid; /* ... and exim */
192 * (gid_t *) egp = mygid;
193 return;
194 }
195 /* Nt/2000/XP
196 We initially set the exim uid & gid to those of the "real exim",
197 or to the root uid (SYSTEM) and exim gid (ADMINS),
198 If privileged, we setuid to those.
199 We always set the configure uid to the system uid.
200 We always set the root uid to the real uid
201 to avoid useless execs following forks.
202 If not privileged and unable to chown,
203 we set the exim uid to our uid.
204 If unprivileged, we fake all subsequent setuid. */
205
206 priv_flags = get_privileges ();
207 privileged = !!(priv_flags & CREATE_BIT);
208
209 /* Get the system and admins uid from their sids,
210 or use the default values from the Makefile. */
211 if ((systemuid = cygwin_internal(CW_GET_UID_FROM_SID, & SystemSid)) == -1)
212 systemuid = * (uid_t *) eup;
213 if ((adminsgid = cygwin_internal(CW_GET_GID_FROM_SID, & AdminsSid)) == -1)
214 adminsgid = * (gid_t *) egp;
215
216 if ((pwp = getpwnam("exim")) != NULL) {
217 * (uid_t *) eup = pwp->pw_uid; /* Set it according to passwd */
218 * (gid_t *) egp = pwp->pw_gid;
219 }
220 else {
221 * (uid_t *) eup = systemuid;
222 * (gid_t *) egp = adminsgid;
223 }
224
b6c6011d 225 /* Set the configuration uid and gid to the system uid and admins gid.
61ec970d
PH
226 Note that exim uid is also accepted as owner of exim.conf. */
227 * (uid_t *) cup = systemuid;
b6c6011d 228 * (gid_t *) cgp = adminsgid;
61ec970d
PH
229
230 if (privileged) { /* Can setuid */
231 if (cygwin_setgid(* (gid_t *) egp) /* Setuid to exim */
232 || cygwin_setuid(* (uid_t *) eup))
8c841523 233 privileged = -1; /* Problem... Perhaps not in 544 */
61ec970d
PH
234 }
235
236 /* Pretend we are root to avoid useless execs.
237 We are limited by file access rights */
238 * (uid_t *) rup = getuid ();
239
240 /* If we have not setuid to exim and cannot chown,
241 set the exim uid to our uid to avoid chown failures */
242 if (privileged <= 0 && !(priv_flags & RESTORE_BIT))
243 * (uid_t *) eup = * (uid_t *) rup;
244
245 if (cygwin_debug) {
246 fprintf(stderr, "Starting uid %ld, gid %ld, ntsec %lu, privileged %d.\n",
8c841523 247 myuid, mygid, cygwin_internal(CW_CHECK_NTSEC, NULL), privileged);
b6c6011d
PH
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);
61ec970d
PH
250 }
251 return;
252}
253
8c841523
PH
254#ifndef OS_LOAD_AVERAGE /* Can be set on command line */
255#define OS_LOAD_AVERAGE /* src/os.c need not provide it */
256
61ec970d
PH
257/*****************************************************************
258 *
259 Functions for average load measurements
260
8c841523
PH
261 There are two methods, which work only on NT.
262
263 The first one uses the HKEY_PERFORMANCE_DATA registry to
264 get performance data. It is complex but well documented
265 and works on all NT versions.
266
267 The second one uses NtQuerySystemInformation.
268 Its use is discouraged starting with WinXP.
269
270 Until 4.43, the Cygwin port of exim was using the first
271 method.
272
273*****************************************************************/
274#define PERF_METHOD2
275
276/* Structure to compute the load average efficiently */
277typedef struct {
278 DWORD Lock;
279 unsigned long long Time100ns; /* Last measurement time */
280 unsigned long long IdleCount; /* Latest cumulative idle time */
281 unsigned long long LastCounter; /* Last measurement counter */
282 unsigned long long PerfFreq; /* Perf counter frequency */
283 int LastLoad; /* Last reported load, or -1 */
284#ifdef PERF_METHOD1
285 PPERF_DATA_BLOCK PerfData; /* Pointer to a buffer to get the data */
286 DWORD BufferSize; /* Size of PerfData */
287 LPSTR * NamesArray; /* Temporary (malloc) buffer for index */
288#endif
289} cygwin_perf_t;
290
291static struct {
292 HANDLE handle;
293 pid_t pid;
294 cygwin_perf_t *perf;
295} cygwin_load = {NULL, 0, NULL};
296
297#ifdef PERF_METHOD1
298/*************************************************************
299 METHOD 1
300
61ec970d
PH
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
8c841523 305 HOWTO: Access the Performance Registry Under Windows 95 (KB 174631)
61ec970d
PH
306 but it is not implemented here.
307
308 The list of objects to be polled is specified in the string
309 passed to RegQueryValueEx in ReadStat() below.
310 On NT, all objects are polled even if info about only one is
311 required. This is fixed in Windows 2000. See articles
8c841523 312 INFO: Perflib Calling Close Procedure in Windows 2000 (KB 270127)
61ec970d 313 INFO: Performance Data Changes Between Windows NT 4.0 and Windows
8c841523 314 2000 (KB 296523)
61ec970d
PH
315
316 It is unclear to me how the counters are primarily identified.
317 Whether it's by name strings or by the offset of their strings
318 as mapped in X:\Winnt\system32\perfc009.dat [or equivalently as
319 reported by the registry functions in GetNameStrings( ) below].
320 Microsoft documentation seems to say that both methods should
321 work.
322
323 In the interest of speed and language independence, the main
324 code below relies on offsets. However if debug is enabled, the
325 code verifies that the names of the corresponding strings are
326 as expected.
327
328*****************************************************************/
61ec970d
PH
329
330/* Object and counter indices and names */
331#define PROCESSOR_OBJECT_INDEX 238
332#define PROCESSOR_OBJECT_STRING "238"
333#define PROCESSOR_OBJECT_NAME "Processor"
334#define PROCESSOR_TIME_COUNTER 6
335#define PROCESSOR_TIME_NAME "% Processor Time"
336
61ec970d
PH
337#define BYTEINCREMENT 800 /* Block to add to PerfData */
338
339/*****************************************************************
340 *
341 Macros to navigate through the performance data.
342
343 *****************************************************************/
344#define FirstObject(PerfData)\
345 ((PPERF_OBJECT_TYPE)((PBYTE)PerfData + PerfData->HeaderLength))
346#define NextObject(PerfObj)\
347 ((PPERF_OBJECT_TYPE)((PBYTE)PerfObj + PerfObj->TotalByteLength))
348#define ObjectCounterBlock(PerfObj)\
349 ((PPERF_COUNTER_BLOCK)(PBYTE)PerfObj + PerfObj->DefinitionLength )
350#define FirstInstance(PerfObj )\
351 ((PPERF_INSTANCE_DEFINITION)((PBYTE)PerfObj + PerfObj->DefinitionLength))
352#define InstanceCounterBlock(PerfInst)\
353 ((PPERF_COUNTER_BLOCK) ((PBYTE)PerfInst + PerfInst->ByteLength ))
354#define NextInstance(PerfInst )\
355 ((PPERF_INSTANCE_DEFINITION)((PBYTE)InstanceCounterBlock(PerfInst) + \
356 InstanceCounterBlock(PerfInst)->ByteLength) )
357#define FirstCounter(PerfObj)\
358 ((PPERF_COUNTER_DEFINITION) ((PBYTE)PerfObj + PerfObj->HeaderLength))
359#define NextCounter(PerfCntr)\
360 ((PPERF_COUNTER_DEFINITION)((PBYTE)PerfCntr + PerfCntr->ByteLength))
361
362/*****************************************************************
363 *
364 Load the counter and object names from the registry
8c841523
PH
365 to cygwin_load.perf->NameStrings
366 and index them in cygwin_load.perf->NamesArray
61ec970d
PH
367
368 NameStrings seems to be taken from the file
369 X:\Winnt\system32\perfc009.dat
370
371 This is used only for name verification during initialization,
372 if DEBUG(D_load) is TRUE.
373
374*****************************************************************/
375static BOOL GetNameStrings( )
376{
377 HKEY hKeyPerflib; // handle to registry key
378 DWORD dwArraySize; // size for array
379 DWORD dwNamesSize; // size for strings
380 LPSTR lpCurrentString; // pointer for enumerating data strings
381 DWORD dwCounter; // current counter index
382 LONG res;
383
384 /* Get the number of Counter items into dwArraySize. */
385 if ((res = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
8c841523
PH
386 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib",
387 0,
388 KEY_QUERY_VALUE, /* KEY_READ, */
389 &hKeyPerflib))
61ec970d
PH
390 != ERROR_SUCCESS) {
391 DEBUG(D_load) debug_printf("RegOpenKeyEx (1): error %ld (Windows)\n", res);
392 return FALSE;
393 }
394 dwNamesSize = sizeof(dwArraySize); /* Temporary reuse */
395 if ((res = RegQueryValueEx( hKeyPerflib,
8c841523
PH
396 "Last Counter",
397 NULL,
398 NULL,
399 (LPBYTE) &dwArraySize,
400 &dwNamesSize ))
61ec970d
PH
401 != ERROR_SUCCESS) {
402 DEBUG(D_load) debug_printf("RegQueryValueEx (1): error %ld (Windows)\n", res);
403 return FALSE;
404 }
405 RegCloseKey( hKeyPerflib );
406 /* Open the key containing the counter and object names. */
407 if ((res = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
8c841523
PH
408 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009",
409 0,
410 KEY_READ,
411 &hKeyPerflib))
61ec970d
PH
412 != ERROR_SUCCESS) {
413 DEBUG(D_load) debug_printf("RegOpenKeyEx (2): error %ld (Windows)\n", res);
414 return FALSE;
415 }
416 /* Get the size of the Counter value in the key
417 and then read the value in the tail of NamesArray */
418 dwNamesSize = 0;
419 lpCurrentString = NULL;
420 while (1) {
421 res = RegQueryValueEx( hKeyPerflib,
8c841523
PH
422 "Counter",
423 NULL,
424 NULL,
425 (unsigned char *) lpCurrentString,
426 &dwNamesSize);
61ec970d 427 if ((res == ERROR_SUCCESS) && /* Bug (NT 4.0): SUCCESS was returned on first call */
8c841523
PH
428 (cygwin_load.perf->NamesArray != NULL)) break;
429 if ((res == ERROR_SUCCESS) || /* but cygwin_load.perf->NamesArrays == NULL */
61ec970d
PH
430 (res == ERROR_MORE_DATA)) {
431 /* Allocate memory BOTH for the names array and for the counter and object names */
8c841523
PH
432 if ((cygwin_load.perf->NamesArray =
433 (LPSTR *) malloc( (dwArraySize + 1) * sizeof(LPSTR) + dwNamesSize * sizeof(CHAR)))
434 != NULL) {
61ec970d 435 /* Point to area for the counter and object names */
8c841523 436 lpCurrentString = (LPSTR) & cygwin_load.perf->NamesArray[dwArraySize + 1];
61ec970d
PH
437 continue;
438 }
439 DEBUG(D_load) debug_printf("Malloc: errno %d (%s)\n", errno, strerror(errno));
440 }
441 else { /* Serious error */
442 DEBUG(D_load) debug_printf("RegQueryValueEx (2): error %ld (Windows)\n", res);
443 }
444 return FALSE;
445 }
446 RegCloseKey( hKeyPerflib );
447 /* Index the names into an array. */
448 while (*lpCurrentString) {
449 dwCounter = atol( lpCurrentString );
450 lpCurrentString += (lstrlen(lpCurrentString)+1);
8c841523 451 cygwin_load.perf->NamesArray[dwCounter] = lpCurrentString;
61ec970d
PH
452 lpCurrentString += (strlen(lpCurrentString)+1);
453 }
454 return TRUE;
455}
456
457/*****************************************************************
458 *
459 Find the value of the Processor Time counter
460
461*****************************************************************/
462static BOOL ReadTimeCtr(PPERF_OBJECT_TYPE PerfObj,
8c841523
PH
463 PPERF_COUNTER_DEFINITION CurCntr,
464 PPERF_COUNTER_BLOCK PtrToCntr,
465 unsigned long long * TimePtr){
61ec970d
PH
466 int j;
467 /* Scan all counters. */
468 for( j = 0; j < PerfObj->NumCounters; j++ ) {
469 if (CurCntr->CounterNameTitleIndex == PROCESSOR_TIME_COUNTER) {
470 /* Verify it is really the proc time counter */
471 if ((CurCntr->CounterType != PERF_100NSEC_TIMER_INV) || /* Wrong type */
8c841523
PH
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,
61ec970d 476 "Incorrect Perf counter type or name %x %s",
8c841523
PH
477 (unsigned) CurCntr->CounterType,
478 cygwin_load.perf->NamesArray[CurCntr->CounterNameTitleIndex]);
479 return FALSE;
61ec970d
PH
480 }
481 *TimePtr += *(unsigned long long int *) ((PBYTE) PtrToCntr + CurCntr->CounterOffset);
482 return TRUE; /* return TRUE as soon as we found the counter */
483 }
484 /* Get the next counter. */
485 CurCntr = NextCounter( CurCntr );
486 }
487 return FALSE;
488}
8c841523 489
61ec970d
PH
490/*****************************************************************
491 *
492 ReadStat()
493 Measures current Time100ns and IdleCount
494 Return TRUE if success.
495
496 *****************************************************************/
8c841523
PH
497static BOOL ReadStat(unsigned long long int *Time100nsPtr,
498 unsigned long long int * IdleCountPtr)
61ec970d
PH
499{
500 PPERF_OBJECT_TYPE PerfObj;
501 PPERF_INSTANCE_DEFINITION PerfInst;
502 PPERF_COUNTER_DEFINITION PerfCntr;
503 PPERF_COUNTER_BLOCK PtrToCntr;
504 DWORD i, k, res;
505
506 /* Get the performance data for the Processor object
507 There is no need to open a key.
508 We may need to blindly increase the buffer size.
509 BufferSize does not return info but may be changed */
510 while (1) {
8c841523 511 DWORD BufferSize = cygwin_load.perf->BufferSize;
61ec970d 512 res = RegQueryValueEx( HKEY_PERFORMANCE_DATA,
8c841523
PH
513 PROCESSOR_OBJECT_STRING,
514 NULL,
515 NULL,
516 (LPBYTE) cygwin_load.perf->PerfData,
517 &BufferSize );
61ec970d
PH
518 if (res == ERROR_SUCCESS) break;
519 if (res == ERROR_MORE_DATA ) {
520 /* Increment if necessary to get a buffer that is big enough. */
8c841523
PH
521 cygwin_load.perf->BufferSize += BYTEINCREMENT;
522 if ((cygwin_load.perf->PerfData =
523 (PPERF_DATA_BLOCK) realloc( cygwin_load.perf->PerfData, cygwin_load.perf->BufferSize ))
524 != NULL) continue;
61ec970d
PH
525 DEBUG(D_load) debug_printf("Malloc: errno %d (%s)\n", errno, strerror(errno));
526 }
527 else { /* Serious error */
528 DEBUG(D_load) debug_printf("RegQueryValueEx (3): error %ld (Windows)\n", res);
529 }
530 return FALSE;
531 }
532 /* Initialize the counters */
533 *Time100nsPtr = 0;
534 *IdleCountPtr = 0;
535 /* We should only have one object, but write general code just in case. */
8c841523
PH
536 PerfObj = FirstObject( cygwin_load.perf->PerfData );
537 for( i = 0; i < cygwin_load.perf->PerfData->NumObjectTypes; i++ ) {
61ec970d
PH
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. */
8c841523
PH
541 if ((cygwin_load.perf->NamesArray != NULL) &&
542 (strcmp(cygwin_load.perf->NamesArray[PerfObj->ObjectNameTitleIndex],
543 PROCESSOR_OBJECT_NAME))) {
544 log_write(0, LOG_MAIN|LOG_PANIC,
545 "Incorrect Perf object name %s",
546 cygwin_load.perf->NamesArray[PerfObj->ObjectNameTitleIndex]);
547 return FALSE;
61ec970d
PH
548 }
549 /* Get the first counter */
550 PerfCntr = FirstCounter( PerfObj );
551 /* See if the object has instances.
8c841523 552 It should, but write general code. */
61ec970d 553 if( PerfObj->NumInstances != PERF_NO_INSTANCES ) {
8c841523
PH
554 PerfInst = FirstInstance( PerfObj );
555 for( k = 0; k < PerfObj->NumInstances; k++ ) {
556 /* There can be several processors.
61ec970d 557 Accumulate both the Time100ns and the idle counter.
8c841523
PH
558 Starting with Win2000 there is an instance named "_Total".
559 Do not use it. We only use instances with a single
61ec970d
PH
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 */
8c841523 563 if ( cygwin_load.perf->NamesArray != NULL) {
61ec970d 564 CHAR ascii[30]; /* The name is in unicode */
8c841523
PH
565 wsprintf(ascii,"%.29lS",
566 (char *)((PBYTE)PerfInst + PerfInst->NameOffset));
567 log_write(0, LOG_MAIN,
61ec970d
PH
568 "Perf: Found processor instance \"%s\", length %d",
569 ascii, PerfInst->NameLength);
8c841523 570 if ((PerfInst->NameLength != 4) &&
61ec970d 571 (strcmp(ascii, "_Total") != 0)) {
8c841523 572 log_write(0, LOG_MAIN|LOG_PANIC,
61ec970d 573 "Perf: WARNING: Unexpected processor instance name");
8c841523 574 return FALSE;
61ec970d
PH
575 }
576 }
8c841523
PH
577 if (PerfInst->NameLength == 4) {
578 *Time100nsPtr += cygwin_load.perf->PerfData->PerfTime100nSec.QuadPart;
579 PtrToCntr = InstanceCounterBlock(PerfInst);
580 if (! ReadTimeCtr(PerfObj, PerfCntr, PtrToCntr, IdleCountPtr)) {
581 return FALSE;
582 }
61ec970d 583 }
8c841523
PH
584 PerfInst = NextInstance( PerfInst );
585 }
61ec970d
PH
586 return (*Time100nsPtr != 0); /* Something was read */
587 }
588 else { /* No instance, just the counter data */
8c841523
PH
589 *Time100nsPtr = cygwin_load.perf->PerfData->PerfTime100nSec.QuadPart;
590 PtrToCntr = ObjectCounterBlock(PerfObj);
591 return ReadTimeCtr(PerfObj, PerfCntr, PtrToCntr, IdleCountPtr);
61ec970d
PH
592 }
593 }
594 PerfObj = NextObject( PerfObj );
595 }
596 return FALSE; /* Did not find the Processor object */
597}
598
8c841523
PH
599#elif defined(PERF_METHOD2)
600
601/*************************************************************
602 METHOD 2
603
604 Uses NtQuerySystemInformation.
605 This requires definitions that are not part of
606 standard include files.
607*************************************************************/
608#include <ntdef.h>
609
610typedef enum _SYSTEM_INFORMATION_CLASS
611{
612 SystemBasicInformation = 0,
613 SystemPerformanceInformation = 2,
614 SystemTimeOfDayInformation = 3,
615 SystemProcessesAndThreadsInformation = 5,
616 SystemProcessorTimes = 8,
617 SystemPagefileInformation = 18,
618 /* There are a lot more of these... */
619} SYSTEM_INFORMATION_CLASS;
620
621typedef struct _SYSTEM_BASIC_INFORMATION
622{
623 ULONG Unknown;
624 ULONG MaximumIncrement;
625 ULONG PhysicalPageSize;
626 ULONG NumberOfPhysicalPages;
627 ULONG LowestPhysicalPage;
628 ULONG HighestPhysicalPage;
629 ULONG AllocationGranularity;
630 ULONG LowestUserAddress;
631 ULONG HighestUserAddress;
632 ULONG ActiveProcessors;
633 UCHAR NumberProcessors;
634} SYSTEM_BASIC_INFORMATION, *PSYSTEM_BASIC_INFORMATION;
635
636typedef struct __attribute__ ((aligned (8))) _SYSTEM_PROCESSOR_TIMES
637{
638 LARGE_INTEGER IdleTime;
639 LARGE_INTEGER KernelTime;
640 LARGE_INTEGER UserTime;
641 LARGE_INTEGER DpcTime;
642 LARGE_INTEGER InterruptTime;
643 ULONG InterruptCount;
644} SYSTEM_PROCESSOR_TIMES, *PSYSTEM_PROCESSOR_TIMES;
645
646typedef NTSTATUS NTAPI (*NtQuerySystemInformation_t) (SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG);
647typedef ULONG NTAPI (*RtlNtStatusToDosError_t) (NTSTATUS);
648
649static NtQuerySystemInformation_t NtQuerySystemInformation;
650static RtlNtStatusToDosError_t RtlNtStatusToDosError;
651
652/*****************************************************************
653 *
654 LoadNtdll()
655 Load special functions from the NTDLL
656 Return TRUE if success.
657
658 *****************************************************************/
659
660static BOOL LoadNtdll()
661{
662 HINSTANCE hinstLib;
663
664 if ((hinstLib = LoadLibrary("NTDLL.DLL"))
665 && (NtQuerySystemInformation =
666 (NtQuerySystemInformation_t) GetProcAddress(hinstLib,
667 "NtQuerySystemInformation"))
668 && (RtlNtStatusToDosError =
669 (RtlNtStatusToDosError_t) GetProcAddress(hinstLib,
670 "RtlNtStatusToDosError")))
671 return TRUE;
672
673 DEBUG(D_load)
674 debug_printf("perf: load: %ld (Windows)\n", GetLastError());
675 return FALSE;
676}
677
678/*****************************************************************
679 *
680 ReadStat()
681 Measures current Time100ns and IdleCount
682 Return TRUE if success.
683
684 *****************************************************************/
685
686static BOOL ReadStat(unsigned long long int *Time100nsPtr,
687 unsigned long long int *IdleCountPtr)
688{
689 NTSTATUS ret;
690 SYSTEM_BASIC_INFORMATION sbi;
691 PSYSTEM_PROCESSOR_TIMES spt;
692
693 *Time100nsPtr = *IdleCountPtr = 0;
694
695 if ((ret = NtQuerySystemInformation(SystemBasicInformation,
696 (PVOID) &sbi, sizeof sbi, NULL))
697 != STATUS_SUCCESS) {
698 DEBUG(D_load)
699 debug_printf("Perf: NtQuerySystemInformation: %lu (Windows)\n",
700 RtlNtStatusToDosError(ret));
701 }
702 else if (!(spt = (PSYSTEM_PROCESSOR_TIMES) alloca(sizeof(spt[0]) * sbi.NumberProcessors))) {
703 DEBUG(D_load)
704 debug_printf("Perf: alloca: errno %d (%s)\n", errno, strerror(errno));
705 }
706 else if ((ret = NtQuerySystemInformation(SystemProcessorTimes, (PVOID) spt,
707 sizeof spt[0] * sbi.NumberProcessors, NULL))
708 != STATUS_SUCCESS) {
709 DEBUG(D_load)
710 debug_printf("Perf: NtQuerySystemInformation: %lu (Windows)\n",
711 RtlNtStatusToDosError(ret));
712 }
713 else {
714 int i;
715 for (i = 0; i < sbi.NumberProcessors; i++) {
716 *Time100nsPtr += spt[i].KernelTime.QuadPart;;
717 *Time100nsPtr += spt[i].UserTime.QuadPart;
718 *IdleCountPtr += spt[i].IdleTime.QuadPart;
719 }
720 return TRUE;
721 }
722 return FALSE;
723}
724#endif /* PERF_METHODX */
725
61ec970d
PH
726/*****************************************************************
727 *
728 InitLoadAvg()
8c841523
PH
729 Initialize the cygwin_load.perf structure.
730 and set cygwin_load.perf->Flag to TRUE if successful.
61ec970d
PH
731 This is called the first time os_getloadavg is called
732 *****************************************************************/
8c841523 733static void InitLoadAvg(cygwin_perf_t *this)
61ec970d
PH
734{
735 BOOL success = TRUE;
8c841523 736
61ec970d 737 /* Get perf frequency and counter */
8c841523
PH
738 QueryPerformanceFrequency((LARGE_INTEGER *)& this->PerfFreq);
739 QueryPerformanceCounter((LARGE_INTEGER *)& this->LastCounter);
740
741#ifdef PERF_METHOD1
61ec970d
PH
742 DEBUG(D_load) {
743 /* Get the name strings through the registry
744 to verify that the object and counter numbers
745 have the names we expect */
746 success = GetNameStrings();
747 }
8c841523
PH
748#endif
749 /* Get initial values for Time100ns and IdleCount */
750 success = success
751 && ReadStat( & this->Time100ns,
752 & this->IdleCount);
61ec970d 753 /* If success, set the Load to 0, else to -1 */
8c841523 754 if (success) this->LastLoad = 0;
61ec970d
PH
755 else {
756 log_write(0, LOG_MAIN, "Cannot obtain Load Average");
8c841523 757 this->LastLoad = -1;
61ec970d 758 }
8c841523 759#ifdef PERF_METHOD1
61ec970d 760 /* Free the buffer created for debug name verification */
8c841523
PH
761 if (this->NamesArray != NULL) {
762 free(this->NamesArray);
763 this->NamesArray = NULL;
61ec970d 764 }
8c841523 765#endif
61ec970d 766}
8c841523
PH
767
768
61ec970d
PH
769/*****************************************************************
770 *
771 os_getloadavg()
772
773 Return -1 if not available;
774 Return the previous value if less than AVERAGING sec old.
775 else return the processor load on a [0 - 1000] scale.
776
777 The first time we are called we initialize the counts
778 and return 0 or -1.
8c841523 779 The initial load cannot be measured as we use the processor 100%
61ec970d 780*****************************************************************/
8c841523 781static SECURITY_ATTRIBUTES sa = {sizeof (SECURITY_ATTRIBUTES), NULL, TRUE};
61ec970d 782#define AVERAGING 10
8c841523 783
61ec970d
PH
784int os_getloadavg()
785{
8c841523 786 unsigned long long Time100ns, IdleCount, CurrCounter;
61ec970d 787 int value;
8c841523
PH
788 pid_t newpid;
789
790 /* New process.
791 Reload the dlls and the file mapping */
792 if ((newpid = getpid()) != cygwin_load.pid) {
793 BOOL new;
794 cygwin_load.pid = newpid;
795
796#ifdef PERF_METHOD2
797 if (!LoadNtdll()) {
798 log_write(0, LOG_MAIN, "Cannot obtain Load Average");
799 cygwin_load.perf = NULL;
800 return -1;
801 }
802#endif
803
804 if ((new = !cygwin_load.handle)) {
805 cygwin_load.handle = CreateFileMapping (INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE,
806 0, sizeof(cygwin_perf_t), NULL);
807 DEBUG(D_load)
808 debug_printf("Perf: CreateFileMapping: handle %x\n", (unsigned) cygwin_load.handle);
809 }
810 cygwin_load.perf = (cygwin_perf_t *) MapViewOfFile (cygwin_load.handle,
811 FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
812 DEBUG(D_load)
813 debug_printf("Perf: MapViewOfFile: addr %x\n", (unsigned) cygwin_load.perf);
814 if (new && cygwin_load.perf)
815 InitLoadAvg(cygwin_load.perf);
816 }
817
818 /* Check if initialized OK */
819 if (!cygwin_load.perf || cygwin_load.perf->LastLoad < 0)
820 return -1;
821
822 /* If we cannot get the lock, we return 0.
823 This is to prevent any lock-up possibility.
824 Finding a lock busy is unlikely, and giving up only
825 results in an immediate delivery .*/
826
827 if (InterlockedCompareExchange(&cygwin_load.perf->Lock, 1, 0)) {
828 DEBUG(D_load)
829 debug_printf("Perf: Lock busy\n");
830 return 0;
831 }
61ec970d 832
61ec970d
PH
833 /* Get the current time (PerfCounter) */
834 QueryPerformanceCounter((LARGE_INTEGER *)& CurrCounter);
835 /* Calls closer than AVERAGING sec apart use the previous value */
8c841523
PH
836 if (CurrCounter - cygwin_load.perf->LastCounter >
837 AVERAGING * cygwin_load.perf->PerfFreq) {
61ec970d
PH
838 /* Get Time100ns and IdleCount */
839 if (ReadStat( & Time100ns, & IdleCount)) { /* Success */
8c841523
PH
840 /* Return processor load on 1000 scale */
841 value = 1000 - ((1000 * (IdleCount - cygwin_load.perf->IdleCount)) /
842 (Time100ns - cygwin_load.perf->Time100ns));
843 cygwin_load.perf->Time100ns = Time100ns;
844 cygwin_load.perf->IdleCount = IdleCount;
845 cygwin_load.perf->LastCounter = CurrCounter;
846 cygwin_load.perf->LastLoad = value;
847 DEBUG(D_load)
848 debug_printf("Perf: New load average %d\n", value);
61ec970d
PH
849 }
850 else { /* Something bad happened.
8c841523
PH
851 Refuse to measure the load anymore
852 but don't bother releasing the buffer */
61ec970d 853 log_write(0, LOG_MAIN, "Cannot obtain Load Average");
8c841523 854 cygwin_load.perf->LastLoad = -1;
61ec970d
PH
855 }
856 }
8c841523 857 else
61ec970d 858 DEBUG(D_load)
8c841523
PH
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;
61ec970d
PH
862}
863#endif /* OS_LOAD_AVERAGE */
864#endif /* COMPILE_UTILITY */