Import Upstream version 4.92
[hcoop/debian/exim4.git] / OS / unsupported / os.c-cygwin
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 */