Commit | Line | Data |
---|---|---|
2ea97746 CE |
1 | /************************************************* |
2 | * Exim - an Internet mail transport agent * | |
3 | *************************************************/ | |
4 | ||
5 | /* Cygwin-specific code. December 2002. Updated Jan 2015. | |
6 | This is prefixed to the 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 | #ifndef COMPILE_UTILITY /* Utilities don't need special code */ | |
22 | ||
23 | #ifdef INCLUDE_PAM | |
24 | #include "../pam/pam.c" | |
25 | #endif | |
26 | #include <alloca.h> | |
27 | ||
28 | unsigned int cygwin_WinVersion; | |
29 | ||
30 | /* Conflict between Windows definitions and others */ | |
31 | #ifdef NOERROR | |
32 | #undef NOERROR | |
33 | #endif | |
34 | #ifdef DELETE | |
35 | #undef DELETE | |
36 | #endif | |
37 | ||
38 | #include <windows.h> | |
39 | #include <ntstatus.h> | |
40 | #include <lmcons.h> | |
41 | ||
42 | #define EqualLuid(Luid1, Luid2) \ | |
43 | ((Luid1.LowPart == Luid2.LowPart) && (Luid1.HighPart == Luid2.HighPart)) | |
44 | #include <sys/cygwin.h> | |
45 | ||
46 | /* Special static variables */ | |
47 | static BOOL cygwin_debug = FALSE; | |
48 | static int fakesetugid = 1; /* when not privileged, setugid = noop */ | |
49 | ||
50 | #undef setuid | |
51 | int cygwin_setuid(uid_t uid ) | |
52 | { | |
53 | int res = 0; | |
54 | if (fakesetugid == 0) { | |
55 | res = setuid(uid); | |
56 | if (cygwin_debug) | |
57 | fprintf(stderr, "setuid %u %u %d pid: %d\n", | |
58 | uid, getuid(),res, getpid()); | |
59 | } | |
60 | return res; | |
61 | } | |
62 | ||
63 | #undef setgid | |
64 | int cygwin_setgid(gid_t gid ) | |
65 | { | |
66 | int res = 0; | |
67 | if (fakesetugid == 0) { | |
68 | res = setgid(gid); | |
69 | if (cygwin_debug) | |
70 | fprintf(stderr, "setgid %u %u %d pid: %d\n", | |
71 | gid, getgid(), res, getpid()); | |
72 | } | |
73 | return res; | |
74 | } | |
75 | ||
76 | /* Background processes run at lower priority */ | |
77 | static void cygwin_setpriority() | |
78 | { | |
79 | if (!SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS)) | |
80 | SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS); | |
81 | return; | |
82 | } | |
83 | ||
84 | ||
85 | /* GetVersion() | |
86 | MSB: 1 for 95/98/ME; Next 7: build number, except for 95/98/ME | |
87 | Next byte: 0 | |
88 | Next byte: minor version of OS | |
89 | Low byte: major version of OS (3 or 4 for for NT, 5 for 2000 and XP) */ | |
90 | //#define VERSION_IS_58M(x) (x & 0x80000000) /* 95, 98, Me */ | |
91 | //#define VERSION_IS_NT(x) ((x & 0XFF) < 5) /* NT 4 or 3.51 */ | |
92 | ||
93 | /* | |
94 | Routine to find if process or thread is privileged | |
95 | */ | |
96 | ||
97 | enum { | |
98 | CREATE_BIT = 1, | |
99 | }; | |
100 | ||
101 | static DWORD get_privileges () | |
102 | { | |
103 | char buffer[1024]; | |
104 | DWORD i, length; | |
105 | HANDLE hToken = NULL; | |
106 | PTOKEN_PRIVILEGES privs; | |
107 | LUID cluid, rluid; | |
108 | DWORD ret = 0; | |
109 | ||
110 | privs = (PTOKEN_PRIVILEGES) buffer; | |
111 | ||
112 | if (OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hToken) | |
113 | && LookupPrivilegeValue (NULL, SE_CREATE_TOKEN_NAME, &cluid) | |
114 | && LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &rluid) | |
115 | && (GetTokenInformation( hToken, TokenPrivileges, | |
116 | privs, sizeof (buffer), &length) | |
117 | || (GetLastError () == ERROR_INSUFFICIENT_BUFFER | |
118 | && (privs = (PTOKEN_PRIVILEGES) alloca (length)) | |
119 | && GetTokenInformation(hToken, TokenPrivileges, | |
120 | privs, length, &length)))) { | |
121 | for (i = 0; i < privs->PrivilegeCount; i++) { | |
122 | if (EqualLuid(privs->Privileges[i].Luid, cluid)) | |
123 | ret |= CREATE_BIT; | |
124 | if (ret == (CREATE_BIT)) | |
125 | break; | |
126 | } | |
127 | } | |
128 | else | |
129 | fprintf(stderr, "has_create_token_privilege %u\n", GetLastError()); | |
130 | ||
131 | if (hToken) | |
132 | CloseHandle(hToken); | |
133 | ||
134 | return ret; | |
135 | } | |
136 | ||
137 | /* | |
138 | We use cygwin_premain to fake a few things | |
139 | and to provide some debug info | |
140 | */ | |
141 | void cygwin_premain2(int argc, char ** argv, struct per_process * ptr) | |
142 | { | |
143 | int i, res, is_daemon = 0, is_spoolwritable, is_privileged, is_eximuser; | |
144 | uid_t myuid, systemuid; | |
145 | gid_t mygid, adminsgid; | |
146 | struct passwd * pwp = NULL; | |
147 | struct stat buf; | |
148 | char *cygenv; | |
149 | SID(1, SystemSid, SECURITY_LOCAL_SYSTEM_RID); | |
150 | SID(2, AdminsSid, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS); | |
151 | DWORD priv_flags; | |
152 | ||
153 | myuid = getuid(); | |
154 | mygid = getgid(); | |
155 | cygwin_WinVersion = GetVersion(); | |
156 | if ((cygenv = getenv("CYGWIN")) == NULL) cygenv = ""; | |
157 | /* Produce some debugging on stderr, | |
158 | cannot yet use exim's debug functions. | |
159 | Exim does not use -c and ignores -n. | |
160 | Set lower priority for daemons */ | |
161 | for (i = 1; i < argc; i++) { | |
162 | if (argv[i][0] == '-') { | |
163 | if (argv[i][1] == 'c') { | |
164 | ssize_t size; | |
165 | wchar_t *win32_path; | |
166 | argv[i][1] = 'n'; /* Replace -c by -n */ | |
167 | cygwin_debug = TRUE; | |
168 | fprintf(stderr, "CYGWIN = \"%s\".\n", cygenv); | |
169 | if (((size = cygwin_conv_path(CCP_POSIX_TO_WIN_W,"/", win32_path, 0)) > 0) | |
170 | && ((win32_path = malloc(size)) != NULL) | |
171 | && (cygwin_conv_path(CCP_POSIX_TO_WIN_W,"/", win32_path, size) == 0)) { | |
172 | fprintf(stderr, " Root / mapped to %ls.\n", win32_path); | |
173 | free(win32_path); | |
174 | } | |
175 | } | |
176 | else if (argv[i][1] == 'b' && argv[i][2] == 'd') { | |
177 | is_daemon = 1; | |
178 | cygwin_setpriority(); | |
179 | } | |
180 | } | |
181 | } | |
182 | ||
183 | /* Nt/2000/XP | |
184 | We initially set the exim uid & gid to those of the "exim user", | |
185 | or to the root uid (SYSTEM) and exim gid (ADMINS), | |
186 | If privileged, we setuid to those. | |
187 | We always set the configure uid to the system uid. | |
188 | We always set the root uid to the real uid | |
189 | to allow exim imposed restrictions (bypassable by recompiling) | |
190 | and to avoid exec that cause loss of privilege | |
191 | If not privileged and unable to chown, | |
192 | we set the exim uid to our uid. | |
193 | If unprivileged and /var/spool/exim is writable and not running as listening daemon, | |
194 | we fake all subsequent setuid. */ | |
195 | ||
196 | /* Get the system and admins uid from their sids */ | |
197 | if ((systemuid = cygwin_internal(CW_GET_UID_FROM_SID, & SystemSid)) == -1) { | |
198 | fprintf(stderr, "Cannot map System sid. Aborting\n"); | |
199 | exit(1); | |
200 | } | |
201 | if ((adminsgid = cygwin_internal(CW_GET_GID_FROM_SID, & AdminsSid)) == -1) { | |
202 | fprintf(stderr, "Cannot map Admins sid. Aborting\n"); | |
203 | exit(1); | |
204 | } | |
205 | ||
206 | priv_flags = get_privileges (); | |
207 | is_privileged = !!(priv_flags & CREATE_BIT); | |
208 | ||
209 | /* Call getpwnam for account exim after getting the local exim name */ | |
210 | char exim_username[DNLEN + UNLEN + 2]; | |
211 | if (cygwin_internal(CW_CYGNAME_FROM_WINNAME, "exim", exim_username, sizeof exim_username) != 0) | |
212 | pwp = getpwnam (exim_username); | |
213 | ||
214 | /* If cannot setuid to exim or and is not the daemon (which is assumed to be | |
215 | able to chown or to be the exim user) set the exim ugid to our ugid to avoid | |
216 | chown failures after creating files and to be able to setuid to exim in | |
217 | exim.c ( "privilege not needed" ). */ | |
218 | if ((is_privileged == 0) && (!is_daemon)) { | |
219 | exim_uid = myuid; | |
220 | exim_gid = mygid; | |
221 | } | |
222 | else if (pwp != NULL) { | |
223 | exim_uid = pwp->pw_uid; /* Set it according to passwd */ | |
224 | exim_gid = pwp->pw_gid; | |
225 | is_eximuser = 1; | |
226 | } | |
227 | else { | |
228 | exim_uid = systemuid; | |
229 | exim_gid = adminsgid; | |
230 | is_eximuser = 0; | |
231 | } | |
232 | ||
233 | res = stat("/var/spool/exim", &buf); | |
234 | /* Check if writable (and can be stat) */ | |
235 | is_spoolwritable = ((res == 0) && ((buf.st_mode & S_IWOTH) != 0)); | |
236 | ||
237 | fakesetugid = (is_privileged == 0) && (is_daemon == 0) && (is_spoolwritable == 1); | |
238 | ||
239 | if (is_privileged) { /* Can setuid */ | |
240 | if (cygwin_setgid(exim_gid) /* Setuid to exim */ | |
241 | || cygwin_setuid(exim_uid)) { | |
242 | fprintf(stderr, "Unable to setuid/gid to exim. priv_flags: %x\n", priv_flags); | |
243 | exit(0); /* Problem... Perhaps not in 544 */ | |
244 | } | |
245 | } | |
246 | ||
247 | /* Set the configuration file uid and gid to the system uid and admins gid. */ | |
248 | config_uid = systemuid; | |
249 | config_gid = adminsgid; | |
250 | ||
251 | /* Pretend we are root to avoid useless exec | |
252 | and avoid exim set limitations. | |
253 | We are limited by file access rights */ | |
254 | root_uid = getuid (); | |
255 | ||
256 | if (cygwin_debug) { | |
257 | fprintf(stderr, "Starting uid %u, gid %u, priv_flags %x, is_privileged %d, is_daemon %d, is_spoolwritable %d.\n", | |
258 | myuid, mygid, priv_flags, is_privileged, is_daemon, is_spoolwritable); | |
259 | fprintf(stderr, "root_uid %u, exim_uid %u, exim_gid %u, config_uid %u, config_gid %u, is_eximuser %d.\n", | |
260 | root_uid, exim_uid, exim_gid, config_uid, config_gid, is_eximuser); | |
261 | } | |
262 | return; | |
263 | } | |
264 | ||
265 | #ifndef OS_LOAD_AVERAGE /* Can be set on command line */ | |
266 | #define OS_LOAD_AVERAGE /* src/os.c need not provide it */ | |
267 | ||
268 | /***************************************************************** | |
269 | Functions for average load measurements | |
270 | ||
271 | Uses NtQuerySystemInformation. | |
272 | This requires definitions that are not part of | |
273 | standard include files. | |
274 | ||
275 | This is discouraged starting with WinXP. | |
276 | ||
277 | *************************************************************/ | |
278 | /* Structure to compute the load average efficiently */ | |
279 | typedef struct { | |
280 | DWORD Lock; | |
281 | unsigned long long Time100ns; /* Last measurement time */ | |
282 | unsigned long long IdleCount; /* Latest cumulative idle time */ | |
283 | unsigned long long LastCounter; /* Last measurement counter */ | |
284 | unsigned long long PerfFreq; /* Perf counter frequency */ | |
285 | int LastLoad; /* Last reported load, or -1 */ | |
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 | #include <ntdef.h> | |
295 | ||
296 | typedef enum _SYSTEM_INFORMATION_CLASS | |
297 | { | |
298 | SystemBasicInformation = 0, | |
299 | SystemPerformanceInformation = 2, | |
300 | SystemTimeOfDayInformation = 3, | |
301 | SystemProcessesAndThreadsInformation = 5, | |
302 | SystemProcessorTimes = 8, | |
303 | SystemPagefileInformation = 18, | |
304 | /* There are a lot more of these... */ | |
305 | } SYSTEM_INFORMATION_CLASS; | |
306 | ||
307 | typedef struct _SYSTEM_BASIC_INFORMATION | |
308 | { | |
309 | ULONG Unknown; | |
310 | ULONG MaximumIncrement; | |
311 | ULONG PhysicalPageSize; | |
312 | ULONG NumberOfPhysicalPages; | |
313 | ULONG LowestPhysicalPage; | |
314 | ULONG HighestPhysicalPage; | |
315 | ULONG AllocationGranularity; | |
316 | ULONG LowestUserAddress; | |
317 | ULONG HighestUserAddress; | |
318 | ULONG ActiveProcessors; | |
319 | UCHAR NumberProcessors; | |
320 | } SYSTEM_BASIC_INFORMATION, *PSYSTEM_BASIC_INFORMATION; | |
321 | ||
322 | typedef struct __attribute__ ((aligned (8))) _SYSTEM_PROCESSOR_TIMES | |
323 | { | |
324 | LARGE_INTEGER IdleTime; | |
325 | LARGE_INTEGER KernelTime; | |
326 | LARGE_INTEGER UserTime; | |
327 | LARGE_INTEGER DpcTime; | |
328 | LARGE_INTEGER InterruptTime; | |
329 | ULONG InterruptCount; | |
330 | } SYSTEM_PROCESSOR_TIMES, *PSYSTEM_PROCESSOR_TIMES; | |
331 | ||
332 | typedef NTSTATUS NTAPI (*NtQuerySystemInformation_t) (SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG); | |
333 | typedef ULONG NTAPI (*RtlNtStatusToDosError_t) (NTSTATUS); | |
334 | ||
335 | static NtQuerySystemInformation_t NtQuerySystemInformation; | |
336 | static RtlNtStatusToDosError_t RtlNtStatusToDosError; | |
337 | ||
338 | /***************************************************************** | |
339 | * | |
340 | LoadNtdll() | |
341 | Load special functions from the NTDLL | |
342 | Return TRUE if success. | |
343 | ||
344 | *****************************************************************/ | |
345 | ||
346 | static BOOL LoadNtdll() | |
347 | { | |
348 | HINSTANCE hinstLib; | |
349 | ||
350 | if ((hinstLib = LoadLibrary("NTDLL.DLL")) | |
351 | && (NtQuerySystemInformation = | |
352 | (NtQuerySystemInformation_t) GetProcAddress(hinstLib, | |
353 | "NtQuerySystemInformation")) | |
354 | && (RtlNtStatusToDosError = | |
355 | (RtlNtStatusToDosError_t) GetProcAddress(hinstLib, | |
356 | "RtlNtStatusToDosError"))) | |
357 | return TRUE; | |
358 | ||
359 | DEBUG(D_load) | |
360 | debug_printf("perf: load: %u (Windows)\n", GetLastError()); | |
361 | return FALSE; | |
362 | } | |
363 | /***************************************************************** | |
364 | * | |
365 | ReadStat() | |
366 | Measures current Time100ns and IdleCount | |
367 | Return TRUE if success. | |
368 | ||
369 | *****************************************************************/ | |
370 | ||
371 | static BOOL ReadStat(unsigned long long int *Time100nsPtr, | |
372 | unsigned long long int *IdleCountPtr) | |
373 | { | |
374 | NTSTATUS ret; | |
375 | SYSTEM_BASIC_INFORMATION sbi; | |
376 | PSYSTEM_PROCESSOR_TIMES spt; | |
377 | ||
378 | *Time100nsPtr = *IdleCountPtr = 0; | |
379 | ||
380 | if ((ret = NtQuerySystemInformation(SystemBasicInformation, | |
381 | (PVOID) &sbi, sizeof sbi, NULL)) | |
382 | != STATUS_SUCCESS) { | |
383 | DEBUG(D_load) | |
384 | debug_printf("Perf: NtQuerySystemInformation: %u (Windows)\n", | |
385 | RtlNtStatusToDosError(ret)); | |
386 | } | |
387 | else if (!(spt = (PSYSTEM_PROCESSOR_TIMES) alloca(sizeof(spt[0]) * sbi.NumberProcessors))) { | |
388 | DEBUG(D_load) | |
389 | debug_printf("Perf: alloca: errno %d (%s)\n", errno, strerror(errno)); | |
390 | } | |
391 | else if ((ret = NtQuerySystemInformation(SystemProcessorTimes, (PVOID) spt, | |
392 | sizeof spt[0] * sbi.NumberProcessors, NULL)) | |
393 | != STATUS_SUCCESS) { | |
394 | DEBUG(D_load) | |
395 | debug_printf("Perf: NtQuerySystemInformation: %u (Windows)\n", | |
396 | RtlNtStatusToDosError(ret)); | |
397 | } | |
398 | else { | |
399 | int i; | |
400 | for (i = 0; i < sbi.NumberProcessors; i++) { | |
401 | *Time100nsPtr += spt[i].KernelTime.QuadPart;; | |
402 | *Time100nsPtr += spt[i].UserTime.QuadPart; | |
403 | *IdleCountPtr += spt[i].IdleTime.QuadPart; | |
404 | } | |
405 | return TRUE; | |
406 | } | |
407 | return FALSE; | |
408 | } | |
409 | ||
410 | /***************************************************************** | |
411 | * | |
412 | InitLoadAvg() | |
413 | Initialize the cygwin_load.perf structure. | |
414 | and set cygwin_load.perf->Flag to TRUE if successful. | |
415 | This is called the first time os_getloadavg is called | |
416 | *****************************************************************/ | |
417 | static void InitLoadAvg(cygwin_perf_t *this) | |
418 | { | |
419 | BOOL success = TRUE; | |
420 | ||
421 | /* Get perf frequency and counter */ | |
422 | QueryPerformanceFrequency((LARGE_INTEGER *)& this->PerfFreq); | |
423 | QueryPerformanceCounter((LARGE_INTEGER *)& this->LastCounter); | |
424 | ||
425 | /* Get initial values for Time100ns and IdleCount */ | |
426 | success = success | |
427 | && ReadStat( & this->Time100ns, | |
428 | & this->IdleCount); | |
429 | /* If success, set the Load to 0, else to -1 */ | |
430 | if (success) this->LastLoad = 0; | |
431 | else { | |
432 | log_write(0, LOG_MAIN, "Cannot obtain Load Average"); | |
433 | this->LastLoad = -1; | |
434 | } | |
435 | } | |
436 | ||
437 | ||
438 | /***************************************************************** | |
439 | * | |
440 | os_getloadavg() | |
441 | ||
442 | Return -1 if not available; | |
443 | Return the previous value if less than AVERAGING sec old. | |
444 | else return the processor load on a [0 - 1000] scale. | |
445 | ||
446 | The first time we are called we initialize the counts | |
447 | and return 0 or -1. | |
448 | The initial load cannot be measured as we use the processor 100% | |
449 | *****************************************************************/ | |
450 | static SECURITY_ATTRIBUTES sa = {sizeof (SECURITY_ATTRIBUTES), NULL, TRUE}; | |
451 | #define AVERAGING 10 | |
452 | ||
453 | int os_getloadavg() | |
454 | { | |
455 | unsigned long long Time100ns, IdleCount, CurrCounter; | |
456 | int value; | |
457 | pid_t newpid; | |
458 | ||
459 | /* New process. | |
460 | Reload the dlls and the file mapping */ | |
461 | if ((newpid = getpid()) != cygwin_load.pid) { | |
462 | BOOL new; | |
463 | cygwin_load.pid = newpid; | |
464 | ||
465 | if (!LoadNtdll()) { | |
466 | log_write(0, LOG_MAIN, "Cannot obtain Load Average"); | |
467 | cygwin_load.perf = NULL; | |
468 | return -1; | |
469 | } | |
470 | ||
471 | if ((new = !cygwin_load.handle)) { | |
472 | cygwin_load.handle = CreateFileMapping (INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, | |
473 | 0, sizeof(cygwin_perf_t), NULL); | |
474 | DEBUG(D_load) | |
475 | debug_printf("Perf: CreateFileMapping: handle %p\n", (void *) cygwin_load.handle); | |
476 | } | |
477 | cygwin_load.perf = (cygwin_perf_t *) MapViewOfFile (cygwin_load.handle, | |
478 | FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); | |
479 | DEBUG(D_load) | |
480 | debug_printf("Perf: MapViewOfFile: addr %p\n", (void *) cygwin_load.perf); | |
481 | if (new && cygwin_load.perf) | |
482 | InitLoadAvg(cygwin_load.perf); | |
483 | } | |
484 | ||
485 | /* Check if initialized OK */ | |
486 | if (!cygwin_load.perf || cygwin_load.perf->LastLoad < 0) | |
487 | return -1; | |
488 | ||
489 | /* If we cannot get the lock, we return 0. | |
490 | This is to prevent any lock-up possibility. | |
491 | Finding a lock busy is unlikely, and giving up only | |
492 | results in an immediate delivery .*/ | |
493 | ||
494 | if (InterlockedCompareExchange(&cygwin_load.perf->Lock, 1, 0)) { | |
495 | DEBUG(D_load) | |
496 | debug_printf("Perf: Lock busy\n"); | |
497 | return 0; | |
498 | } | |
499 | ||
500 | /* Get the current time (PerfCounter) */ | |
501 | QueryPerformanceCounter((LARGE_INTEGER *)& CurrCounter); | |
502 | /* Calls closer than AVERAGING sec apart use the previous value */ | |
503 | if (CurrCounter - cygwin_load.perf->LastCounter > | |
504 | AVERAGING * cygwin_load.perf->PerfFreq) { | |
505 | /* Get Time100ns and IdleCount */ | |
506 | if (ReadStat( & Time100ns, & IdleCount)) { /* Success */ | |
507 | /* Return processor load on 1000 scale */ | |
508 | value = 1000 - ((1000 * (IdleCount - cygwin_load.perf->IdleCount)) / | |
509 | (Time100ns - cygwin_load.perf->Time100ns)); | |
510 | cygwin_load.perf->Time100ns = Time100ns; | |
511 | cygwin_load.perf->IdleCount = IdleCount; | |
512 | cygwin_load.perf->LastCounter = CurrCounter; | |
513 | cygwin_load.perf->LastLoad = value; | |
514 | DEBUG(D_load) | |
515 | debug_printf("Perf: New load average %d\n", value); | |
516 | } | |
517 | else { /* Something bad happened. | |
518 | Refuse to measure the load anymore | |
519 | but don't bother releasing the buffer */ | |
520 | log_write(0, LOG_MAIN, "Cannot obtain Load Average"); | |
521 | cygwin_load.perf->LastLoad = -1; | |
522 | } | |
523 | } | |
524 | else | |
525 | DEBUG(D_load) | |
526 | debug_printf("Perf: Old load average %d\n", cygwin_load.perf->LastLoad); | |
527 | cygwin_load.perf->Lock = 0; | |
528 | return cygwin_load.perf->LastLoad; | |
529 | } | |
530 | #endif /* OS_LOAD_AVERAGE */ | |
531 | #endif /* COMPILE_UTILITY */ |