Install latest Cygwin config files.
[exim.git] / src / OS / os.c-cygwin
CommitLineData
b6c6011d 1/* $Cambridge: exim/src/OS/os.c-cygwin,v 1.3 2005/08/02 09:09:27 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 */
b6c6011d 87static void cygwin_setpriority()
61ec970d
PH
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,
b6c6011d 155 void * eup, void * egp, void * cup, void * cgp)
61ec970d
PH
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')
b6c6011d 184 cygwin_setpriority();
61ec970d
PH
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
b6c6011d 223 /* Set the configuration uid and gid to the system uid and admins gid.
61ec970d
PH
224 Note that exim uid is also accepted as owner of exim.conf. */
225 * (uid_t *) cup = systemuid;
b6c6011d 226 * (gid_t *) cgp = adminsgid;
61ec970d
PH
227
228 if (privileged) { /* Can setuid */
229 if (cygwin_setgid(* (gid_t *) egp) /* Setuid to exim */
230 || cygwin_setuid(* (uid_t *) eup))
8c841523 231 privileged = -1; /* Problem... Perhaps not in 544 */
61ec970d
PH
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",
8c841523 245 myuid, mygid, cygwin_internal(CW_CHECK_NTSEC, NULL), privileged);
b6c6011d
PH
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);
61ec970d
PH
248 }
249 return;
250}
251
8c841523
PH
252#ifndef OS_LOAD_AVERAGE /* Can be set on command line */
253#define OS_LOAD_AVERAGE /* src/os.c need not provide it */
254
61ec970d
PH
255/*****************************************************************
256 *
257 Functions for average load measurements
258
8c841523
PH
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 */
275typedef 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
289static 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
61ec970d
PH
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
8c841523 303 HOWTO: Access the Performance Registry Under Windows 95 (KB 174631)
61ec970d
PH
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
8c841523 310 INFO: Perflib Calling Close Procedure in Windows 2000 (KB 270127)
61ec970d 311 INFO: Performance Data Changes Between Windows NT 4.0 and Windows
8c841523 312 2000 (KB 296523)
61ec970d
PH
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*****************************************************************/
61ec970d
PH
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
61ec970d
PH
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
8c841523
PH
363 to cygwin_load.perf->NameStrings
364 and index them in cygwin_load.perf->NamesArray
61ec970d
PH
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*****************************************************************/
373static 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,
8c841523
PH
384 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib",
385 0,
386 KEY_QUERY_VALUE, /* KEY_READ, */
387 &hKeyPerflib))
61ec970d
PH
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,
8c841523
PH
394 "Last Counter",
395 NULL,
396 NULL,
397 (LPBYTE) &dwArraySize,
398 &dwNamesSize ))
61ec970d
PH
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,
8c841523
PH
406 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009",
407 0,
408 KEY_READ,
409 &hKeyPerflib))
61ec970d
PH
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,
8c841523
PH
420 "Counter",
421 NULL,
422 NULL,
423 (unsigned char *) lpCurrentString,
424 &dwNamesSize);
61ec970d 425 if ((res == ERROR_SUCCESS) && /* Bug (NT 4.0): SUCCESS was returned on first call */
8c841523
PH
426 (cygwin_load.perf->NamesArray != NULL)) break;
427 if ((res == ERROR_SUCCESS) || /* but cygwin_load.perf->NamesArrays == NULL */
61ec970d
PH
428 (res == ERROR_MORE_DATA)) {
429 /* Allocate memory BOTH for the names array and for the counter and object names */
8c841523
PH
430 if ((cygwin_load.perf->NamesArray =
431 (LPSTR *) malloc( (dwArraySize + 1) * sizeof(LPSTR) + dwNamesSize * sizeof(CHAR)))
432 != NULL) {
61ec970d 433 /* Point to area for the counter and object names */
8c841523 434 lpCurrentString = (LPSTR) & cygwin_load.perf->NamesArray[dwArraySize + 1];
61ec970d
PH
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);
8c841523 449 cygwin_load.perf->NamesArray[dwCounter] = lpCurrentString;
61ec970d
PH
450 lpCurrentString += (strlen(lpCurrentString)+1);
451 }
452 return TRUE;
453}
454
455/*****************************************************************
456 *
457 Find the value of the Processor Time counter
458
459*****************************************************************/
460static BOOL ReadTimeCtr(PPERF_OBJECT_TYPE PerfObj,
8c841523
PH
461 PPERF_COUNTER_DEFINITION CurCntr,
462 PPERF_COUNTER_BLOCK PtrToCntr,
463 unsigned long long * TimePtr){
61ec970d
PH
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 */
8c841523
PH
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,
61ec970d 474 "Incorrect Perf counter type or name %x %s",
8c841523
PH
475 (unsigned) CurCntr->CounterType,
476 cygwin_load.perf->NamesArray[CurCntr->CounterNameTitleIndex]);
477 return FALSE;
61ec970d
PH
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}
8c841523 487
61ec970d
PH
488/*****************************************************************
489 *
490 ReadStat()
491 Measures current Time100ns and IdleCount
492 Return TRUE if success.
493
494 *****************************************************************/
8c841523
PH
495static BOOL ReadStat(unsigned long long int *Time100nsPtr,
496 unsigned long long int * IdleCountPtr)
61ec970d
PH
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) {
8c841523 509 DWORD BufferSize = cygwin_load.perf->BufferSize;
61ec970d 510 res = RegQueryValueEx( HKEY_PERFORMANCE_DATA,
8c841523
PH
511 PROCESSOR_OBJECT_STRING,
512 NULL,
513 NULL,
514 (LPBYTE) cygwin_load.perf->PerfData,
515 &BufferSize );
61ec970d
PH
516 if (res == ERROR_SUCCESS) break;
517 if (res == ERROR_MORE_DATA ) {
518 /* Increment if necessary to get a buffer that is big enough. */
8c841523
PH
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;
61ec970d
PH
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. */
8c841523
PH
534 PerfObj = FirstObject( cygwin_load.perf->PerfData );
535 for( i = 0; i < cygwin_load.perf->PerfData->NumObjectTypes; i++ ) {
61ec970d
PH
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. */
8c841523
PH
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;
61ec970d
PH
546 }
547 /* Get the first counter */
548 PerfCntr = FirstCounter( PerfObj );
549 /* See if the object has instances.
8c841523 550 It should, but write general code. */
61ec970d 551 if( PerfObj->NumInstances != PERF_NO_INSTANCES ) {
8c841523
PH
552 PerfInst = FirstInstance( PerfObj );
553 for( k = 0; k < PerfObj->NumInstances; k++ ) {
554 /* There can be several processors.
61ec970d 555 Accumulate both the Time100ns and the idle counter.
8c841523
PH
556 Starting with Win2000 there is an instance named "_Total".
557 Do not use it. We only use instances with a single
61ec970d
PH
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 */
8c841523 561 if ( cygwin_load.perf->NamesArray != NULL) {
61ec970d 562 CHAR ascii[30]; /* The name is in unicode */
8c841523
PH
563 wsprintf(ascii,"%.29lS",
564 (char *)((PBYTE)PerfInst + PerfInst->NameOffset));
565 log_write(0, LOG_MAIN,
61ec970d
PH
566 "Perf: Found processor instance \"%s\", length %d",
567 ascii, PerfInst->NameLength);
8c841523 568 if ((PerfInst->NameLength != 4) &&
61ec970d 569 (strcmp(ascii, "_Total") != 0)) {
8c841523 570 log_write(0, LOG_MAIN|LOG_PANIC,
61ec970d 571 "Perf: WARNING: Unexpected processor instance name");
8c841523 572 return FALSE;
61ec970d
PH
573 }
574 }
8c841523
PH
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 }
61ec970d 581 }
8c841523
PH
582 PerfInst = NextInstance( PerfInst );
583 }
61ec970d
PH
584 return (*Time100nsPtr != 0); /* Something was read */
585 }
586 else { /* No instance, just the counter data */
8c841523
PH
587 *Time100nsPtr = cygwin_load.perf->PerfData->PerfTime100nSec.QuadPart;
588 PtrToCntr = ObjectCounterBlock(PerfObj);
589 return ReadTimeCtr(PerfObj, PerfCntr, PtrToCntr, IdleCountPtr);
61ec970d
PH
590 }
591 }
592 PerfObj = NextObject( PerfObj );
593 }
594 return FALSE; /* Did not find the Processor object */
595}
596
8c841523
PH
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
608typedef 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
619typedef 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
634typedef 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
644typedef NTSTATUS NTAPI (*NtQuerySystemInformation_t) (SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG);
645typedef ULONG NTAPI (*RtlNtStatusToDosError_t) (NTSTATUS);
646
647static NtQuerySystemInformation_t NtQuerySystemInformation;
648static RtlNtStatusToDosError_t RtlNtStatusToDosError;
649
650/*****************************************************************
651 *
652 LoadNtdll()
653 Load special functions from the NTDLL
654 Return TRUE if success.
655
656 *****************************************************************/
657
658static 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
684static 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
61ec970d
PH
724/*****************************************************************
725 *
726 InitLoadAvg()
8c841523
PH
727 Initialize the cygwin_load.perf structure.
728 and set cygwin_load.perf->Flag to TRUE if successful.
61ec970d
PH
729 This is called the first time os_getloadavg is called
730 *****************************************************************/
8c841523 731static void InitLoadAvg(cygwin_perf_t *this)
61ec970d
PH
732{
733 BOOL success = TRUE;
8c841523 734
61ec970d 735 /* Get perf frequency and counter */
8c841523
PH
736 QueryPerformanceFrequency((LARGE_INTEGER *)& this->PerfFreq);
737 QueryPerformanceCounter((LARGE_INTEGER *)& this->LastCounter);
738
739#ifdef PERF_METHOD1
61ec970d
PH
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 }
8c841523
PH
746#endif
747 /* Get initial values for Time100ns and IdleCount */
748 success = success
749 && ReadStat( & this->Time100ns,
750 & this->IdleCount);
61ec970d 751 /* If success, set the Load to 0, else to -1 */
8c841523 752 if (success) this->LastLoad = 0;
61ec970d
PH
753 else {
754 log_write(0, LOG_MAIN, "Cannot obtain Load Average");
8c841523 755 this->LastLoad = -1;
61ec970d 756 }
8c841523 757#ifdef PERF_METHOD1
61ec970d 758 /* Free the buffer created for debug name verification */
8c841523
PH
759 if (this->NamesArray != NULL) {
760 free(this->NamesArray);
761 this->NamesArray = NULL;
61ec970d 762 }
8c841523 763#endif
61ec970d 764}
8c841523
PH
765
766
61ec970d
PH
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.
8c841523 777 The initial load cannot be measured as we use the processor 100%
61ec970d 778*****************************************************************/
8c841523 779static SECURITY_ATTRIBUTES sa = {sizeof (SECURITY_ATTRIBUTES), NULL, TRUE};
61ec970d 780#define AVERAGING 10
8c841523 781
61ec970d
PH
782int os_getloadavg()
783{
8c841523 784 unsigned long long Time100ns, IdleCount, CurrCounter;
61ec970d 785 int value;
8c841523
PH
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 }
61ec970d 830
61ec970d
PH
831 /* Get the current time (PerfCounter) */
832 QueryPerformanceCounter((LARGE_INTEGER *)& CurrCounter);
833 /* Calls closer than AVERAGING sec apart use the previous value */
8c841523
PH
834 if (CurrCounter - cygwin_load.perf->LastCounter >
835 AVERAGING * cygwin_load.perf->PerfFreq) {
61ec970d
PH
836 /* Get Time100ns and IdleCount */
837 if (ReadStat( & Time100ns, & IdleCount)) { /* Success */
8c841523
PH
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);
61ec970d
PH
847 }
848 else { /* Something bad happened.
8c841523
PH
849 Refuse to measure the load anymore
850 but don't bother releasing the buffer */
61ec970d 851 log_write(0, LOG_MAIN, "Cannot obtain Load Average");
8c841523 852 cygwin_load.perf->LastLoad = -1;
61ec970d
PH
853 }
854 }
8c841523 855 else
61ec970d 856 DEBUG(D_load)
8c841523
PH
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;
61ec970d
PH
860}
861#endif /* OS_LOAD_AVERAGE */
862#endif /* COMPILE_UTILITY */