214 spelling fixes
[exim.git] / src / OS / os.c-cygwin
CommitLineData
61ec970d
PH
1/*************************************************
2* Exim - an Internet mail transport agent *
3*************************************************/
4
92e6a3d9
JH
5/* Cygwin-specific code. December 2002. Updated Jan 2015.
6 This is prefixed to the src/os.c file.
61ec970d
PH
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
61ec970d 21#ifndef COMPILE_UTILITY /* Utilities don't need special code */
61ec970d
PH
22
23#ifdef INCLUDE_PAM
24#include "../pam/pam.c"
25#endif
92e6a3d9 26#include <alloca.h>
61ec970d
PH
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>
92e6a3d9
JH
39#include <ntstatus.h>
40#include <lmcons.h>
41
2c5db4fd
PH
42#define EqualLuid(Luid1, Luid2) \
43 ((Luid1.LowPart == Luid2.LowPart) && (Luid1.HighPart == Luid2.HighPart))
61ec970d
PH
44#include <sys/cygwin.h>
45
46/* Special static variables */
47static BOOL cygwin_debug = FALSE;
92e6a3d9 48static int fakesetugid = 1; /* when not privileged, setugid = noop */
61ec970d
PH
49
50#undef setuid
51int cygwin_setuid(uid_t uid )
52{
92e6a3d9
JH
53 int res = 0;
54 if (fakesetugid == 0) {
61ec970d
PH
55 res = setuid(uid);
56 if (cygwin_debug)
92e6a3d9 57 fprintf(stderr, "setuid %u %u %d pid: %d\n",
61ec970d
PH
58 uid, getuid(),res, getpid());
59 }
60 return res;
61}
62
63#undef setgid
64int cygwin_setgid(gid_t gid )
65{
92e6a3d9
JH
66 int res = 0;
67 if (fakesetugid == 0) {
61ec970d
PH
68 res = setgid(gid);
69 if (cygwin_debug)
92e6a3d9 70 fprintf(stderr, "setgid %u %u %d pid: %d\n",
8c841523 71 gid, getgid(), res, getpid());
61ec970d
PH
72 }
73 return res;
74}
75
76/* Background processes run at lower priority */
b6c6011d 77static void cygwin_setpriority()
61ec970d
PH
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) */
92e6a3d9
JH
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 */
61ec970d
PH
92
93/*
94 Routine to find if process or thread is privileged
95*/
96
97enum {
98 CREATE_BIT = 1,
61ec970d
PH
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,
8c841523
PH
116 privs, sizeof (buffer), &length)
117 || (GetLastError () == ERROR_INSUFFICIENT_BUFFER
118 && (privs = (PTOKEN_PRIVILEGES) alloca (length))
119 && GetTokenInformation(hToken, TokenPrivileges,
120 privs, length, &length)))) {
61ec970d 121 for (i = 0; i < privs->PrivilegeCount; i++) {
2c5db4fd 122 if (EqualLuid(privs->Privileges[i].Luid, cluid))
8c841523 123 ret |= CREATE_BIT;
92e6a3d9 124 if (ret == (CREATE_BIT))
8c841523 125 break;
61ec970d
PH
126 }
127 }
128 else
92e6a3d9 129 fprintf(stderr, "has_create_token_privilege %u\n", GetLastError());
61ec970d
PH
130
131 if (hToken)
132 CloseHandle(hToken);
133
134 return ret;
135}
136
92e6a3d9
JH
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)
61ec970d 142{
92e6a3d9 143 int i, res, is_daemon = 0, is_spoolwritable, is_privileged, is_eximuser;
61ec970d
PH
144 uid_t myuid, systemuid;
145 gid_t mygid, adminsgid;
92e6a3d9
JH
146 struct passwd * pwp = NULL;
147 struct stat buf;
148 char *cygenv;
61ec970d
PH
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') {
92e6a3d9
JH
164 ssize_t size;
165 wchar_t *win32_path;
61ec970d
PH
166 argv[i][1] = 'n'; /* Replace -c by -n */
167 cygwin_debug = TRUE;
92e6a3d9
JH
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 }
61ec970d 175 }
92e6a3d9
JH
176 else if (argv[i][1] == 'b' && argv[i][2] == 'd') {
177 is_daemon = 1;
b6c6011d 178 cygwin_setpriority();
61ec970d
PH
179 }
180 }
61ec970d 181 }
92e6a3d9 182
61ec970d 183 /* Nt/2000/XP
92e6a3d9 184 We initially set the exim uid & gid to those of the "exim user",
61ec970d
PH
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
92e6a3d9
JH
189 to allow exim imposed restrictions (bypassable by recompiling)
190 and to avoid exec that cause loss of privilege
61ec970d
PH
191 If not privileged and unable to chown,
192 we set the exim uid to our uid.
4c04137d 193 If unprivileged and /var/spool/exim is writable and not running as listening daemon,
92e6a3d9
JH
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 }
61ec970d
PH
205
206 priv_flags = get_privileges ();
92e6a3d9
JH
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;
61ec970d
PH
226 }
227 else {
92e6a3d9
JH
228 exim_uid = systemuid;
229 exim_gid = adminsgid;
230 is_eximuser = 0;
61ec970d
PH
231 }
232
92e6a3d9
JH
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);
61ec970d 238
92e6a3d9
JH
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 }
61ec970d
PH
245 }
246
92e6a3d9
JH
247 /* Set the configuration file uid and gid to the system uid and admins gid. */
248 config_uid = systemuid;
249 config_gid = adminsgid;
61ec970d 250
92e6a3d9
JH
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 ();
61ec970d
PH
255
256 if (cygwin_debug) {
92e6a3d9
JH
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);
61ec970d
PH
261 }
262 return;
263}
264
8c841523
PH
265#ifndef OS_LOAD_AVERAGE /* Can be set on command line */
266#define OS_LOAD_AVERAGE /* src/os.c need not provide it */
267
61ec970d 268/*****************************************************************
61ec970d
PH
269 Functions for average load measurements
270
92e6a3d9
JH
271 Uses NtQuerySystemInformation.
272 This requires definitions that are not part of
273 standard include files.
8c841523 274
92e6a3d9 275 This is discouraged starting with WinXP.
8c841523 276
92e6a3d9 277*************************************************************/
8c841523
PH
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 */
8c841523
PH
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
8c841523
PH
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)
92e6a3d9 360 debug_printf("perf: load: %u (Windows)\n", GetLastError());
8c841523
PH
361 return FALSE;
362}
8c841523
PH
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)
92e6a3d9 384 debug_printf("Perf: NtQuerySystemInformation: %u (Windows)\n",
8c841523
PH
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)
92e6a3d9 395 debug_printf("Perf: NtQuerySystemInformation: %u (Windows)\n",
8c841523
PH
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}
8c841523 409
61ec970d
PH
410/*****************************************************************
411 *
412 InitLoadAvg()
8c841523
PH
413 Initialize the cygwin_load.perf structure.
414 and set cygwin_load.perf->Flag to TRUE if successful.
61ec970d
PH
415 This is called the first time os_getloadavg is called
416 *****************************************************************/
8c841523 417static void InitLoadAvg(cygwin_perf_t *this)
61ec970d
PH
418{
419 BOOL success = TRUE;
8c841523 420
61ec970d 421 /* Get perf frequency and counter */
8c841523
PH
422 QueryPerformanceFrequency((LARGE_INTEGER *)& this->PerfFreq);
423 QueryPerformanceCounter((LARGE_INTEGER *)& this->LastCounter);
424
8c841523
PH
425 /* Get initial values for Time100ns and IdleCount */
426 success = success
427 && ReadStat( & this->Time100ns,
428 & this->IdleCount);
61ec970d 429 /* If success, set the Load to 0, else to -1 */
8c841523 430 if (success) this->LastLoad = 0;
61ec970d
PH
431 else {
432 log_write(0, LOG_MAIN, "Cannot obtain Load Average");
8c841523 433 this->LastLoad = -1;
61ec970d 434 }
61ec970d 435}
8c841523
PH
436
437
61ec970d
PH
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.
8c841523 448 The initial load cannot be measured as we use the processor 100%
61ec970d 449*****************************************************************/
8c841523 450static SECURITY_ATTRIBUTES sa = {sizeof (SECURITY_ATTRIBUTES), NULL, TRUE};
61ec970d 451#define AVERAGING 10
8c841523 452
61ec970d
PH
453int os_getloadavg()
454{
8c841523 455 unsigned long long Time100ns, IdleCount, CurrCounter;
61ec970d 456 int value;
8c841523
PH
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
8c841523
PH
465 if (!LoadNtdll()) {
466 log_write(0, LOG_MAIN, "Cannot obtain Load Average");
467 cygwin_load.perf = NULL;
468 return -1;
469 }
8c841523
PH
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)
92e6a3d9 475 debug_printf("Perf: CreateFileMapping: handle %p\n", (void *) cygwin_load.handle);
8c841523
PH
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)
92e6a3d9 480 debug_printf("Perf: MapViewOfFile: addr %p\n", (void *) cygwin_load.perf);
8c841523
PH
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 }
61ec970d 499
61ec970d
PH
500 /* Get the current time (PerfCounter) */
501 QueryPerformanceCounter((LARGE_INTEGER *)& CurrCounter);
502 /* Calls closer than AVERAGING sec apart use the previous value */
8c841523
PH
503 if (CurrCounter - cygwin_load.perf->LastCounter >
504 AVERAGING * cygwin_load.perf->PerfFreq) {
61ec970d
PH
505 /* Get Time100ns and IdleCount */
506 if (ReadStat( & Time100ns, & IdleCount)) { /* Success */
8c841523
PH
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);
61ec970d
PH
516 }
517 else { /* Something bad happened.
8c841523
PH
518 Refuse to measure the load anymore
519 but don't bother releasing the buffer */
61ec970d 520 log_write(0, LOG_MAIN, "Cannot obtain Load Average");
8c841523 521 cygwin_load.perf->LastLoad = -1;
61ec970d
PH
522 }
523 }
8c841523 524 else
61ec970d 525 DEBUG(D_load)
8c841523
PH
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;
61ec970d
PH
529}
530#endif /* OS_LOAD_AVERAGE */
531#endif /* COMPILE_UTILITY */