New OS/os.c-cygwin from the maintainer.
[exim.git] / src / OS / os.c-cygwin
1 /* $Cambridge: exim/src/OS/os.c-cygwin,v 1.2 2005/03/29 11:01:32 ph10 Exp $ */
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
16 int 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 */
26 char * 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
41 unsigned 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 */
55 static BOOL cygwin_debug = FALSE;
56 static int privileged = 1; /* when not privileged, setuid = noop */
57
58 #undef setuid
59 int 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
73 int 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",
81 gid, getgid(), res, getpid());
82 }
83 return res;
84 }
85
86 /* Background processes run at lower priority */
87 static 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
107 enum {
108 CREATE_BIT = 1,
109 RESTORE_BIT = 2
110 };
111
112 static 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,
127 privs, sizeof (buffer), &length)
128 || (GetLastError () == ERROR_INSUFFICIENT_BUFFER
129 && (privs = (PTOKEN_PRIVILEGES) alloca (length))
130 && GetTokenInformation(hToken, TokenPrivileges,
131 privs, length, &length)))) {
132 for (i = 0; i < privs->PrivilegeCount; i++) {
133 if (privs->Privileges[i].Luid.QuadPart == cluid.QuadPart)
134 ret |= CREATE_BIT;
135 else if (privs->Privileges[i].Luid.QuadPart == rluid.QuadPart)
136 ret |= RESTORE_BIT;
137 else continue;
138 if (ret == (CREATE_BIT | RESTORE_BIT))
139 break;
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
154 void 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))
230 privileged = -1; /* Problem... Perhaps not in 544 */
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",
244 myuid, mygid, cygwin_internal(CW_CHECK_NTSEC, NULL), privileged);
245 fprintf(stderr, "root_uid %ld, exim_uid %ld, exim_gid %ld, config_uid %ld.\n",
246 * (uid_t *) rup, * (uid_t *) eup, * (gid_t *) egp, * (uid_t *) cup);
247 }
248 return;
249 }
250
251 #ifndef OS_LOAD_AVERAGE /* Can be set on command line */
252 #define OS_LOAD_AVERAGE /* src/os.c need not provide it */
253
254 /*****************************************************************
255 *
256 Functions for average load measurements
257
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 */
274 typedef 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
288 static 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
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
302 HOWTO: Access the Performance Registry Under Windows 95 (KB 174631)
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
309 INFO: Perflib Calling Close Procedure in Windows 2000 (KB 270127)
310 INFO: Performance Data Changes Between Windows NT 4.0 and Windows
311 2000 (KB 296523)
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 *****************************************************************/
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
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
362 to cygwin_load.perf->NameStrings
363 and index them in cygwin_load.perf->NamesArray
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 *****************************************************************/
372 static 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,
383 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib",
384 0,
385 KEY_QUERY_VALUE, /* KEY_READ, */
386 &hKeyPerflib))
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,
393 "Last Counter",
394 NULL,
395 NULL,
396 (LPBYTE) &dwArraySize,
397 &dwNamesSize ))
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,
405 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009",
406 0,
407 KEY_READ,
408 &hKeyPerflib))
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,
419 "Counter",
420 NULL,
421 NULL,
422 (unsigned char *) lpCurrentString,
423 &dwNamesSize);
424 if ((res == ERROR_SUCCESS) && /* Bug (NT 4.0): SUCCESS was returned on first call */
425 (cygwin_load.perf->NamesArray != NULL)) break;
426 if ((res == ERROR_SUCCESS) || /* but cygwin_load.perf->NamesArrays == NULL */
427 (res == ERROR_MORE_DATA)) {
428 /* Allocate memory BOTH for the names array and for the counter and object names */
429 if ((cygwin_load.perf->NamesArray =
430 (LPSTR *) malloc( (dwArraySize + 1) * sizeof(LPSTR) + dwNamesSize * sizeof(CHAR)))
431 != NULL) {
432 /* Point to area for the counter and object names */
433 lpCurrentString = (LPSTR) & cygwin_load.perf->NamesArray[dwArraySize + 1];
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);
448 cygwin_load.perf->NamesArray[dwCounter] = lpCurrentString;
449 lpCurrentString += (strlen(lpCurrentString)+1);
450 }
451 return TRUE;
452 }
453
454 /*****************************************************************
455 *
456 Find the value of the Processor Time counter
457
458 *****************************************************************/
459 static BOOL ReadTimeCtr(PPERF_OBJECT_TYPE PerfObj,
460 PPERF_COUNTER_DEFINITION CurCntr,
461 PPERF_COUNTER_BLOCK PtrToCntr,
462 unsigned long long * TimePtr){
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 */
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,
473 "Incorrect Perf counter type or name %x %s",
474 (unsigned) CurCntr->CounterType,
475 cygwin_load.perf->NamesArray[CurCntr->CounterNameTitleIndex]);
476 return FALSE;
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 }
486
487 /*****************************************************************
488 *
489 ReadStat()
490 Measures current Time100ns and IdleCount
491 Return TRUE if success.
492
493 *****************************************************************/
494 static BOOL ReadStat(unsigned long long int *Time100nsPtr,
495 unsigned long long int * IdleCountPtr)
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) {
508 DWORD BufferSize = cygwin_load.perf->BufferSize;
509 res = RegQueryValueEx( HKEY_PERFORMANCE_DATA,
510 PROCESSOR_OBJECT_STRING,
511 NULL,
512 NULL,
513 (LPBYTE) cygwin_load.perf->PerfData,
514 &BufferSize );
515 if (res == ERROR_SUCCESS) break;
516 if (res == ERROR_MORE_DATA ) {
517 /* Increment if necessary to get a buffer that is big enough. */
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;
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. */
533 PerfObj = FirstObject( cygwin_load.perf->PerfData );
534 for( i = 0; i < cygwin_load.perf->PerfData->NumObjectTypes; i++ ) {
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. */
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;
545 }
546 /* Get the first counter */
547 PerfCntr = FirstCounter( PerfObj );
548 /* See if the object has instances.
549 It should, but write general code. */
550 if( PerfObj->NumInstances != PERF_NO_INSTANCES ) {
551 PerfInst = FirstInstance( PerfObj );
552 for( k = 0; k < PerfObj->NumInstances; k++ ) {
553 /* There can be several processors.
554 Accumulate both the Time100ns and the idle counter.
555 Starting with Win2000 there is an instance named "_Total".
556 Do not use it. We only use instances with a single
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 */
560 if ( cygwin_load.perf->NamesArray != NULL) {
561 CHAR ascii[30]; /* The name is in unicode */
562 wsprintf(ascii,"%.29lS",
563 (char *)((PBYTE)PerfInst + PerfInst->NameOffset));
564 log_write(0, LOG_MAIN,
565 "Perf: Found processor instance \"%s\", length %d",
566 ascii, PerfInst->NameLength);
567 if ((PerfInst->NameLength != 4) &&
568 (strcmp(ascii, "_Total") != 0)) {
569 log_write(0, LOG_MAIN|LOG_PANIC,
570 "Perf: WARNING: Unexpected processor instance name");
571 return FALSE;
572 }
573 }
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 }
580 }
581 PerfInst = NextInstance( PerfInst );
582 }
583 return (*Time100nsPtr != 0); /* Something was read */
584 }
585 else { /* No instance, just the counter data */
586 *Time100nsPtr = cygwin_load.perf->PerfData->PerfTime100nSec.QuadPart;
587 PtrToCntr = ObjectCounterBlock(PerfObj);
588 return ReadTimeCtr(PerfObj, PerfCntr, PtrToCntr, IdleCountPtr);
589 }
590 }
591 PerfObj = NextObject( PerfObj );
592 }
593 return FALSE; /* Did not find the Processor object */
594 }
595
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
607 typedef 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
618 typedef 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
633 typedef 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
643 typedef NTSTATUS NTAPI (*NtQuerySystemInformation_t) (SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG);
644 typedef ULONG NTAPI (*RtlNtStatusToDosError_t) (NTSTATUS);
645
646 static NtQuerySystemInformation_t NtQuerySystemInformation;
647 static RtlNtStatusToDosError_t RtlNtStatusToDosError;
648
649 /*****************************************************************
650 *
651 LoadNtdll()
652 Load special functions from the NTDLL
653 Return TRUE if success.
654
655 *****************************************************************/
656
657 static 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
683 static 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
723 /*****************************************************************
724 *
725 InitLoadAvg()
726 Initialize the cygwin_load.perf structure.
727 and set cygwin_load.perf->Flag to TRUE if successful.
728 This is called the first time os_getloadavg is called
729 *****************************************************************/
730 static void InitLoadAvg(cygwin_perf_t *this)
731 {
732 BOOL success = TRUE;
733
734 /* Get perf frequency and counter */
735 QueryPerformanceFrequency((LARGE_INTEGER *)& this->PerfFreq);
736 QueryPerformanceCounter((LARGE_INTEGER *)& this->LastCounter);
737
738 #ifdef PERF_METHOD1
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 }
745 #endif
746 /* Get initial values for Time100ns and IdleCount */
747 success = success
748 && ReadStat( & this->Time100ns,
749 & this->IdleCount);
750 /* If success, set the Load to 0, else to -1 */
751 if (success) this->LastLoad = 0;
752 else {
753 log_write(0, LOG_MAIN, "Cannot obtain Load Average");
754 this->LastLoad = -1;
755 }
756 #ifdef PERF_METHOD1
757 /* Free the buffer created for debug name verification */
758 if (this->NamesArray != NULL) {
759 free(this->NamesArray);
760 this->NamesArray = NULL;
761 }
762 #endif
763 }
764
765
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.
776 The initial load cannot be measured as we use the processor 100%
777 *****************************************************************/
778 static SECURITY_ATTRIBUTES sa = {sizeof (SECURITY_ATTRIBUTES), NULL, TRUE};
779 #define AVERAGING 10
780
781 int os_getloadavg()
782 {
783 unsigned long long Time100ns, IdleCount, CurrCounter;
784 int value;
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 }
829
830 /* Get the current time (PerfCounter) */
831 QueryPerformanceCounter((LARGE_INTEGER *)& CurrCounter);
832 /* Calls closer than AVERAGING sec apart use the previous value */
833 if (CurrCounter - cygwin_load.perf->LastCounter >
834 AVERAGING * cygwin_load.perf->PerfFreq) {
835 /* Get Time100ns and IdleCount */
836 if (ReadStat( & Time100ns, & IdleCount)) { /* Success */
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);
846 }
847 else { /* Something bad happened.
848 Refuse to measure the load anymore
849 but don't bother releasing the buffer */
850 log_write(0, LOG_MAIN, "Cannot obtain Load Average");
851 cygwin_load.perf->LastLoad = -1;
852 }
853 }
854 else
855 DEBUG(D_load)
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;
859 }
860 #endif /* OS_LOAD_AVERAGE */
861 #endif /* COMPILE_UTILITY */