Import Upstream version 4.89
[hcoop/debian/exim4.git] / OS / os.c-cygwin
CommitLineData
420a0d19
CE
1/*************************************************
2* Exim - an Internet mail transport agent *
3*************************************************/
4
2813c06e
CE
5/* Cygwin-specific code. December 2002. Updated Jan 2015.
6 This is prefixed to the src/os.c file.
420a0d19
CE
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
14int 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
420a0d19 21#ifndef COMPILE_UTILITY /* Utilities don't need special code */
420a0d19
CE
22
23#ifdef INCLUDE_PAM
24#include "../pam/pam.c"
25#endif
2813c06e 26#include <alloca.h>
420a0d19
CE
27
28unsigned 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>
2813c06e
CE
39#include <ntstatus.h>
40#include <lmcons.h>
41
420a0d19
CE
42#define EqualLuid(Luid1, Luid2) \
43 ((Luid1.LowPart == Luid2.LowPart) && (Luid1.HighPart == Luid2.HighPart))
44#include <sys/cygwin.h>
45
46/* Special static variables */
47static BOOL cygwin_debug = FALSE;
2813c06e 48static int fakesetugid = 1; /* when not privileged, setugid = noop */
420a0d19
CE
49
50#undef setuid
51int cygwin_setuid(uid_t uid )
52{
2813c06e
CE
53 int res = 0;
54 if (fakesetugid == 0) {
420a0d19
CE
55 res = setuid(uid);
56 if (cygwin_debug)
2813c06e 57 fprintf(stderr, "setuid %u %u %d pid: %d\n",
420a0d19
CE
58 uid, getuid(),res, getpid());
59 }
60 return res;
61}
62
63#undef setgid
64int cygwin_setgid(gid_t gid )
65{
2813c06e
CE
66 int res = 0;
67 if (fakesetugid == 0) {
420a0d19
CE
68 res = setgid(gid);
69 if (cygwin_debug)
2813c06e 70 fprintf(stderr, "setgid %u %u %d pid: %d\n",
420a0d19
CE
71 gid, getgid(), res, getpid());
72 }
73 return res;
74}
75
76/* Background processes run at lower priority */
77static 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) */
2813c06e
CE
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 */
420a0d19
CE
92
93/*
94 Routine to find if process or thread is privileged
95*/
96
97enum {
98 CREATE_BIT = 1,
420a0d19
CE
99};
100
101static 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;
2813c06e 124 if (ret == (CREATE_BIT))
420a0d19
CE
125 break;
126 }
127 }
128 else
2813c06e 129 fprintf(stderr, "has_create_token_privilege %u\n", GetLastError());
420a0d19
CE
130
131 if (hToken)
132 CloseHandle(hToken);
133
134 return ret;
135}
136
2813c06e
CE
137/*
138 We use cygwin_premain to fake a few things
139 and to provide some debug info
140*/
141void cygwin_premain2(int argc, char ** argv, struct per_process * ptr)
420a0d19 142{
2813c06e 143 int i, res, is_daemon = 0, is_spoolwritable, is_privileged, is_eximuser;
420a0d19
CE
144 uid_t myuid, systemuid;
145 gid_t mygid, adminsgid;
2813c06e
CE
146 struct passwd * pwp = NULL;
147 struct stat buf;
148 char *cygenv;
420a0d19
CE
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') {
2813c06e
CE
164 ssize_t size;
165 wchar_t *win32_path;
420a0d19
CE
166 argv[i][1] = 'n'; /* Replace -c by -n */
167 cygwin_debug = TRUE;
2813c06e
CE
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 }
420a0d19 175 }
2813c06e
CE
176 else if (argv[i][1] == 'b' && argv[i][2] == 'd') {
177 is_daemon = 1;
420a0d19
CE
178 cygwin_setpriority();
179 }
180 }
420a0d19 181 }
2813c06e 182
420a0d19 183 /* Nt/2000/XP
2813c06e 184 We initially set the exim uid & gid to those of the "exim user",
420a0d19
CE
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
2813c06e
CE
189 to allow exim imposed restrictions (bypassable by recompiling)
190 and to avoid exec that cause loss of privilege
420a0d19
CE
191 If not privileged and unable to chown,
192 we set the exim uid to our uid.
2813c06e
CE
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 }
420a0d19
CE
205
206 priv_flags = get_privileges ();
2813c06e
CE
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;
420a0d19
CE
226 }
227 else {
2813c06e
CE
228 exim_uid = systemuid;
229 exim_gid = adminsgid;
230 is_eximuser = 0;
420a0d19
CE
231 }
232
2813c06e
CE
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);
420a0d19 238
2813c06e
CE
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 }
420a0d19
CE
245 }
246
2813c06e
CE
247 /* Set the configuration file uid and gid to the system uid and admins gid. */
248 config_uid = systemuid;
249 config_gid = adminsgid;
420a0d19 250
2813c06e
CE
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 ();
420a0d19
CE
255
256 if (cygwin_debug) {
2813c06e
CE
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);
420a0d19
CE
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/*****************************************************************
420a0d19
CE
269 Functions for average load measurements
270
2813c06e
CE
271 Uses NtQuerySystemInformation.
272 This requires definitions that are not part of
273 standard include files.
420a0d19 274
2813c06e 275 This is discouraged starting with WinXP.
420a0d19 276
2813c06e 277*************************************************************/
420a0d19
CE
278/* Structure to compute the load average efficiently */
279typedef 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 */
420a0d19
CE
286} cygwin_perf_t;
287
288static struct {
289 HANDLE handle;
290 pid_t pid;
291 cygwin_perf_t *perf;
292} cygwin_load = {NULL, 0, NULL};
293
420a0d19
CE
294#include <ntdef.h>
295
296typedef 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
307typedef 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
322typedef 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
332typedef NTSTATUS NTAPI (*NtQuerySystemInformation_t) (SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG);
333typedef ULONG NTAPI (*RtlNtStatusToDosError_t) (NTSTATUS);
334
335static NtQuerySystemInformation_t NtQuerySystemInformation;
336static RtlNtStatusToDosError_t RtlNtStatusToDosError;
337
338/*****************************************************************
339 *
340 LoadNtdll()
341 Load special functions from the NTDLL
342 Return TRUE if success.
343
344 *****************************************************************/
345
346static 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)
2813c06e 360 debug_printf("perf: load: %u (Windows)\n", GetLastError());
420a0d19
CE
361 return FALSE;
362}
420a0d19
CE
363/*****************************************************************
364 *
365 ReadStat()
366 Measures current Time100ns and IdleCount
367 Return TRUE if success.
368
369 *****************************************************************/
370
371static 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)
2813c06e 384 debug_printf("Perf: NtQuerySystemInformation: %u (Windows)\n",
420a0d19
CE
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)
2813c06e 395 debug_printf("Perf: NtQuerySystemInformation: %u (Windows)\n",
420a0d19
CE
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}
420a0d19
CE
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 *****************************************************************/
417static 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
420a0d19
CE
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 }
420a0d19
CE
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*****************************************************************/
450static SECURITY_ATTRIBUTES sa = {sizeof (SECURITY_ATTRIBUTES), NULL, TRUE};
451#define AVERAGING 10
452
453int 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
420a0d19
CE
465 if (!LoadNtdll()) {
466 log_write(0, LOG_MAIN, "Cannot obtain Load Average");
467 cygwin_load.perf = NULL;
468 return -1;
469 }
420a0d19
CE
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)
2813c06e 475 debug_printf("Perf: CreateFileMapping: handle %p\n", (void *) cygwin_load.handle);
420a0d19
CE
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)
2813c06e 480 debug_printf("Perf: MapViewOfFile: addr %p\n", (void *) cygwin_load.perf);
420a0d19
CE
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 */