Include note about exicyclog and keeping more than 99 back copies.
[exim.git] / src / OS / os.c-cygwin
1 /* $Cambridge: exim/src/OS/os.c-cygwin,v 1.1 2004/10/06 15:07:39 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 /*****************************************************************
252 *
253 Functions for average load measurements
254
255 Obtaining statistics in Windows is done at a low level by
256 calling registry functions, in particular the key
257 HKEY_PERFORMANCE_DATA on NT and successors.
258 Something equivalent exists on Win95, see Microsoft article
259 HOWTO: Access the Performance Registry Under Windows 95 (Q174631)
260 but it is not implemented here.
261
262 The list of objects to be polled is specified in the string
263 passed to RegQueryValueEx in ReadStat() below.
264 On NT, all objects are polled even if info about only one is
265 required. This is fixed in Windows 2000. See articles
266 INFO: Perflib Calling Close Procedure in Windows 2000 (Q270127)
267 INFO: Performance Data Changes Between Windows NT 4.0 and Windows
268 2000 (Q296523)
269
270 It is unclear to me how the counters are primarily identified.
271 Whether it's by name strings or by the offset of their strings
272 as mapped in X:\Winnt\system32\perfc009.dat [or equivalently as
273 reported by the registry functions in GetNameStrings( ) below].
274 Microsoft documentation seems to say that both methods should
275 work.
276
277 In the interest of speed and language independence, the main
278 code below relies on offsets. However if debug is enabled, the
279 code verifies that the names of the corresponding strings are
280 as expected.
281
282 *****************************************************************/
283 #ifndef OS_LOAD_AVERAGE /* Can be set on command line */
284 #define OS_LOAD_AVERAGE /* src/os.c need not provide it */
285
286 /* Object and counter indices and names */
287 #define PROCESSOR_OBJECT_INDEX 238
288 #define PROCESSOR_OBJECT_STRING "238"
289 #define PROCESSOR_OBJECT_NAME "Processor"
290 #define PROCESSOR_TIME_COUNTER 6
291 #define PROCESSOR_TIME_NAME "% Processor Time"
292
293 /* Structure to compute the load average efficiently */
294 static struct {
295 long long Time100ns; /* Last measurement time */
296 long long IdleCount; /* Latest cumulative idle time */
297 long long LastCounter; /* Last measurement counter */
298 long long PerfFreq; /* Perf counter frequency */
299 PPERF_DATA_BLOCK PerfData; /* Pointer to a buffer to get the data */
300 DWORD BufferSize; /* Size of PerfData */
301 int LastLoad; /* Last reported load, or -1 */
302 LPSTR * NamesArray; /* Temporary (malloc) buffer for index */
303 BOOL Init; /* True if initialized */
304 } cygwin_load = { 0, 0, 0, 0, NULL, 0, 0, NULL, FALSE};
305
306 #define BYTEINCREMENT 800 /* Block to add to PerfData */
307
308 /*****************************************************************
309 *
310 Macros to navigate through the performance data.
311
312 *****************************************************************/
313 #define FirstObject(PerfData)\
314 ((PPERF_OBJECT_TYPE)((PBYTE)PerfData + PerfData->HeaderLength))
315 #define NextObject(PerfObj)\
316 ((PPERF_OBJECT_TYPE)((PBYTE)PerfObj + PerfObj->TotalByteLength))
317 #define ObjectCounterBlock(PerfObj)\
318 ((PPERF_COUNTER_BLOCK)(PBYTE)PerfObj + PerfObj->DefinitionLength )
319 #define FirstInstance(PerfObj )\
320 ((PPERF_INSTANCE_DEFINITION)((PBYTE)PerfObj + PerfObj->DefinitionLength))
321 #define InstanceCounterBlock(PerfInst)\
322 ((PPERF_COUNTER_BLOCK) ((PBYTE)PerfInst + PerfInst->ByteLength ))
323 #define NextInstance(PerfInst )\
324 ((PPERF_INSTANCE_DEFINITION)((PBYTE)InstanceCounterBlock(PerfInst) + \
325 InstanceCounterBlock(PerfInst)->ByteLength) )
326 #define FirstCounter(PerfObj)\
327 ((PPERF_COUNTER_DEFINITION) ((PBYTE)PerfObj + PerfObj->HeaderLength))
328 #define NextCounter(PerfCntr)\
329 ((PPERF_COUNTER_DEFINITION)((PBYTE)PerfCntr + PerfCntr->ByteLength))
330
331 /*****************************************************************
332 *
333 Load the counter and object names from the registry
334 to cygwin_load.NameStrings
335 and index them in cygwin_load.NamesArray
336
337 NameStrings seems to be taken from the file
338 X:\Winnt\system32\perfc009.dat
339
340 This is used only for name verification during initialization,
341 if DEBUG(D_load) is TRUE.
342
343 *****************************************************************/
344 static BOOL GetNameStrings( )
345 {
346 HKEY hKeyPerflib; // handle to registry key
347 DWORD dwArraySize; // size for array
348 DWORD dwNamesSize; // size for strings
349 LPSTR lpCurrentString; // pointer for enumerating data strings
350 DWORD dwCounter; // current counter index
351 LONG res;
352
353 /* Get the number of Counter items into dwArraySize. */
354 if ((res = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
355 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib",
356 0,
357 KEY_QUERY_VALUE, /* KEY_READ, */
358 &hKeyPerflib))
359 != ERROR_SUCCESS) {
360 DEBUG(D_load) debug_printf("RegOpenKeyEx (1): error %ld (Windows)\n", res);
361 return FALSE;
362 }
363 dwNamesSize = sizeof(dwArraySize); /* Temporary reuse */
364 if ((res = RegQueryValueEx( hKeyPerflib,
365 "Last Counter",
366 NULL,
367 NULL,
368 (LPBYTE) &dwArraySize,
369 &dwNamesSize ))
370 != ERROR_SUCCESS) {
371 DEBUG(D_load) debug_printf("RegQueryValueEx (1): error %ld (Windows)\n", res);
372 return FALSE;
373 }
374 RegCloseKey( hKeyPerflib );
375 /* Open the key containing the counter and object names. */
376 if ((res = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
377 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009",
378 0,
379 KEY_READ,
380 &hKeyPerflib))
381 != ERROR_SUCCESS) {
382 DEBUG(D_load) debug_printf("RegOpenKeyEx (2): error %ld (Windows)\n", res);
383 return FALSE;
384 }
385 /* Get the size of the Counter value in the key
386 and then read the value in the tail of NamesArray */
387 dwNamesSize = 0;
388 lpCurrentString = NULL;
389 while (1) {
390 res = RegQueryValueEx( hKeyPerflib,
391 "Counter",
392 NULL,
393 NULL,
394 (unsigned char *) lpCurrentString,
395 &dwNamesSize);
396 if ((res == ERROR_SUCCESS) && /* Bug (NT 4.0): SUCCESS was returned on first call */
397 (cygwin_load.NamesArray != NULL)) break;
398 if ((res == ERROR_SUCCESS) || /* but cygwin_load.NamesArrays == NULL */
399 (res == ERROR_MORE_DATA)) {
400 /* Allocate memory BOTH for the names array and for the counter and object names */
401 if ((cygwin_load.NamesArray =
402 (LPSTR *) malloc( (dwArraySize + 1) * sizeof(LPSTR) + dwNamesSize * sizeof(CHAR)))
403 != NULL) {
404 /* Point to area for the counter and object names */
405 lpCurrentString = (LPSTR) & cygwin_load.NamesArray[dwArraySize + 1];
406 continue;
407 }
408 DEBUG(D_load) debug_printf("Malloc: errno %d (%s)\n", errno, strerror(errno));
409 }
410 else { /* Serious error */
411 DEBUG(D_load) debug_printf("RegQueryValueEx (2): error %ld (Windows)\n", res);
412 }
413 return FALSE;
414 }
415 RegCloseKey( hKeyPerflib );
416 /* Index the names into an array. */
417 while (*lpCurrentString) {
418 dwCounter = atol( lpCurrentString );
419 lpCurrentString += (lstrlen(lpCurrentString)+1);
420 cygwin_load.NamesArray[dwCounter] = lpCurrentString;
421 lpCurrentString += (strlen(lpCurrentString)+1);
422 }
423 return TRUE;
424 }
425
426 /*****************************************************************
427 *
428 Find the value of the Processor Time counter
429
430 *****************************************************************/
431 static BOOL ReadTimeCtr(PPERF_OBJECT_TYPE PerfObj,
432 PPERF_COUNTER_DEFINITION CurCntr,
433 PPERF_COUNTER_BLOCK PtrToCntr,
434 unsigned long long * TimePtr){
435 int j;
436 /* Scan all counters. */
437 for( j = 0; j < PerfObj->NumCounters; j++ ) {
438 if (CurCntr->CounterNameTitleIndex == PROCESSOR_TIME_COUNTER) {
439 /* Verify it is really the proc time counter */
440 if ((CurCntr->CounterType != PERF_100NSEC_TIMER_INV) || /* Wrong type */
441 ((cygwin_load.NamesArray != NULL) && /* Verify name */
442 (strcmp(cygwin_load.NamesArray[CurCntr->CounterNameTitleIndex],
443 PROCESSOR_TIME_NAME)))) {
444 log_write(0, LOG_MAIN|LOG_PANIC,
445 "Incorrect Perf counter type or name %x %s",
446 (unsigned) CurCntr->CounterType,
447 cygwin_load.NamesArray[CurCntr->CounterNameTitleIndex]);
448 return FALSE;
449 }
450 *TimePtr += *(unsigned long long int *) ((PBYTE) PtrToCntr + CurCntr->CounterOffset);
451 return TRUE; /* return TRUE as soon as we found the counter */
452 }
453 /* Get the next counter. */
454 CurCntr = NextCounter( CurCntr );
455 }
456 return FALSE;
457 }
458 /*****************************************************************
459 *
460 ReadStat()
461 Measures current Time100ns and IdleCount
462 Return TRUE if success.
463
464 *****************************************************************/
465 static BOOL ReadStat(long long int *Time100nsPtr,
466 long long int * IdleCountPtr)
467 {
468 PPERF_OBJECT_TYPE PerfObj;
469 PPERF_INSTANCE_DEFINITION PerfInst;
470 PPERF_COUNTER_DEFINITION PerfCntr;
471 PPERF_COUNTER_BLOCK PtrToCntr;
472 DWORD i, k, res;
473
474 /* Get the performance data for the Processor object
475 There is no need to open a key.
476 We may need to blindly increase the buffer size.
477 BufferSize does not return info but may be changed */
478 while (1) {
479 DWORD BufferSize = cygwin_load.BufferSize;
480 res = RegQueryValueEx( HKEY_PERFORMANCE_DATA,
481 PROCESSOR_OBJECT_STRING,
482 NULL,
483 NULL,
484 (LPBYTE) cygwin_load.PerfData,
485 &BufferSize );
486 if (res == ERROR_SUCCESS) break;
487 if (res == ERROR_MORE_DATA ) {
488 /* Increment if necessary to get a buffer that is big enough. */
489 cygwin_load.BufferSize += BYTEINCREMENT;
490 if ((cygwin_load.PerfData =
491 (PPERF_DATA_BLOCK) realloc( cygwin_load.PerfData, cygwin_load.BufferSize ))
492 != NULL) continue;
493 DEBUG(D_load) debug_printf("Malloc: errno %d (%s)\n", errno, strerror(errno));
494 }
495 else { /* Serious error */
496 DEBUG(D_load) debug_printf("RegQueryValueEx (3): error %ld (Windows)\n", res);
497 }
498 return FALSE;
499 }
500 /* Initialize the counters */
501 *Time100nsPtr = 0;
502 *IdleCountPtr = 0;
503 /* We should only have one object, but write general code just in case. */
504 PerfObj = FirstObject( cygwin_load.PerfData );
505 for( i = 0; i < cygwin_load.PerfData->NumObjectTypes; i++ ) {
506 /* We are only interested in the processor object */
507 if ( PerfObj->ObjectNameTitleIndex == PROCESSOR_OBJECT_INDEX) {
508 /* Possibly verify it is really the Processor object. */
509 if ((cygwin_load.NamesArray != NULL) &&
510 (strcmp(cygwin_load.NamesArray[PerfObj->ObjectNameTitleIndex],
511 PROCESSOR_OBJECT_NAME))) {
512 log_write(0, LOG_MAIN|LOG_PANIC,
513 "Incorrect Perf object name %s",
514 cygwin_load.NamesArray[PerfObj->ObjectNameTitleIndex]);
515 return FALSE;
516 }
517 /* Get the first counter */
518 PerfCntr = FirstCounter( PerfObj );
519 /* See if the object has instances.
520 It should, but write general code. */
521 if( PerfObj->NumInstances != PERF_NO_INSTANCES ) {
522 PerfInst = FirstInstance( PerfObj );
523 for( k = 0; k < PerfObj->NumInstances; k++ ) {
524 /* There can be several processors.
525 Accumulate both the Time100ns and the idle counter.
526 On Win 2000 I have seen an instance named "_Total".
527 Do not use it. We only use instances with a single
528 character in the name.
529 If we examine the object names, we also look at the instance
530 names and their lengths and issue reports */
531 if ( cygwin_load.NamesArray != NULL) {
532 CHAR ascii[30]; /* The name is in unicode */
533 wsprintf(ascii,"%.29lS",
534 (char *)((PBYTE)PerfInst + PerfInst->NameOffset));
535 log_write(0, LOG_MAIN,
536 "Perf: Found processor instance \"%s\", length %d",
537 ascii, PerfInst->NameLength);
538 if ((PerfInst->NameLength != 4) &&
539 (strcmp(ascii, "_Total") != 0)) {
540 log_write(0, LOG_MAIN|LOG_PANIC,
541 "Perf: WARNING: Unexpected processor instance name");
542 return FALSE;
543 }
544 }
545 if (PerfInst->NameLength == 4) {
546 *Time100nsPtr += cygwin_load.PerfData->PerfTime100nSec.QuadPart;
547 PtrToCntr = InstanceCounterBlock(PerfInst);
548 if (! ReadTimeCtr(PerfObj, PerfCntr, PtrToCntr, IdleCountPtr)) {
549 return FALSE;
550 }
551 }
552 PerfInst = NextInstance( PerfInst );
553 }
554 return (*Time100nsPtr != 0); /* Something was read */
555 }
556 else { /* No instance, just the counter data */
557 *Time100nsPtr = cygwin_load.PerfData->PerfTime100nSec.QuadPart;
558 PtrToCntr = ObjectCounterBlock(PerfObj);
559 return ReadTimeCtr(PerfObj, PerfCntr, PtrToCntr, IdleCountPtr);
560 }
561 }
562 PerfObj = NextObject( PerfObj );
563 }
564 return FALSE; /* Did not find the Processor object */
565 }
566
567 /*****************************************************************
568 *
569 InitLoadAvg()
570 Initialize the cygwin_load structure.
571 and set cygwin_load.Flag to TRUE if successful.
572 This is called the first time os_getloadavg is called
573 *****************************************************************/
574 static void InitLoadAvg()
575 {
576 BOOL success = TRUE;
577 cygwin_load.Init = TRUE; /* We have run */
578 /* Get perf frequency and counter */
579 QueryPerformanceFrequency((LARGE_INTEGER *)& cygwin_load.PerfFreq);
580 QueryPerformanceCounter((LARGE_INTEGER *)& cygwin_load.LastCounter);
581 DEBUG(D_load) {
582 /* Get the name strings through the registry
583 to verify that the object and counter numbers
584 have the names we expect */
585 success = GetNameStrings();
586 }
587 /* Get initial values for Time100ns and IdleCount
588 and possibly verify the names */
589 // success = success &&
590 success = ReadStat( & cygwin_load.Time100ns,
591 & cygwin_load.IdleCount);
592 /* If success, set the Load to 0, else to -1 */
593 if (success) cygwin_load.LastLoad = 0;
594 else {
595 log_write(0, LOG_MAIN, "Cannot obtain Load Average");
596 cygwin_load.LastLoad = -1;
597 }
598 /* Free the buffer created for debug name verification */
599 if (cygwin_load.NamesArray != NULL) {
600 free(cygwin_load.NamesArray);
601 cygwin_load.NamesArray = NULL;
602 }
603 }
604 /*****************************************************************
605 *
606 os_getloadavg()
607
608 Return -1 if not available;
609 Return the previous value if less than AVERAGING sec old.
610 else return the processor load on a [0 - 1000] scale.
611
612 The first time we are called we initialize the counts
613 and return 0 or -1.
614 The load cannot be measured because we use the processor 100%
615 *****************************************************************/
616 #define AVERAGING 10
617 int os_getloadavg()
618 {
619 long long Time100ns, IdleCount, CurrCounter;
620 int value;
621
622 if (! cygwin_load.Init) InitLoadAvg();
623 else if (cygwin_load.LastLoad >= 0) { /* Initialized OK */
624 /* Get the current time (PerfCounter) */
625 QueryPerformanceCounter((LARGE_INTEGER *)& CurrCounter);
626 /* Calls closer than AVERAGING sec apart use the previous value */
627 if (CurrCounter - cygwin_load.LastCounter >
628 AVERAGING * cygwin_load.PerfFreq) {
629 /* Get Time100ns and IdleCount */
630 if (ReadStat( & Time100ns, & IdleCount)) { /* Success */
631 /* Return processor load on 1000 scale */
632 value = 1000 - ((1000 * (IdleCount - cygwin_load.IdleCount)) /
633 (Time100ns - cygwin_load.Time100ns));
634 cygwin_load.Time100ns = Time100ns;
635 cygwin_load.IdleCount = IdleCount;
636 cygwin_load.LastCounter = CurrCounter;
637 cygwin_load.LastLoad = value;
638 }
639 else { /* Something bad happened.
640 Refuse to measure the load anymore
641 but don't bother releasing the buffer */
642 log_write(0, LOG_MAIN, "Cannot obtain Load Average");
643 cygwin_load.LastLoad = -1;
644 }
645 }
646 }
647 DEBUG(D_load)
648 debug_printf("Perf: load average = %d\n", cygwin_load.LastLoad);
649 return cygwin_load.LastLoad;
650 }
651 #endif /* OS_LOAD_AVERAGE */
652 #endif /* COMPILE_UTILITY */