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