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