Merge branch 'hs/fix-proxy-bh' (Closes 2656)
[users/heiko/exim.git] / src / 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 = store_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                 store_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 */