summaryrefslogtreecommitdiffstats
path: root/OS/unsupported/os.c-cygwin
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 09:44:07 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 09:44:07 +0000
commit39ce00b8d520cbecbd6af87257e8fb11df0ec273 (patch)
tree4c21a2674c19e5c44be3b3550b476b9e63d8ae3d /OS/unsupported/os.c-cygwin
parentInitial commit. (diff)
downloadexim4-60bddf70a0555b5f386aa68ac440742aefbfa361.tar.xz
exim4-60bddf70a0555b5f386aa68ac440742aefbfa361.zip
Adding upstream version 4.94.2.upstream/4.94.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'OS/unsupported/os.c-cygwin')
-rw-r--r--OS/unsupported/os.c-cygwin531
1 files changed, 531 insertions, 0 deletions
diff --git a/OS/unsupported/os.c-cygwin b/OS/unsupported/os.c-cygwin
new file mode 100644
index 0000000..5ca05a8
--- /dev/null
+++ b/OS/unsupported/os.c-cygwin
@@ -0,0 +1,531 @@
+/*************************************************
+* Exim - an Internet mail transport agent *
+*************************************************/
+
+/* Cygwin-specific code. December 2002. Updated Jan 2015.
+ This is prefixed to the src/os.c file.
+
+ This code was supplied by Pierre A. Humblet <Pierre.Humblet@ieee.org>
+*/
+
+/* We need a special mkdir that
+ allows names starting with // */
+#undef mkdir
+int cygwin_mkdir( const char *path, mode_t mode )
+{
+ const char * p = path;
+ if (*p == '/') while(*(p+1) == '/') p++;
+ return mkdir(p, mode);
+}
+
+#ifndef COMPILE_UTILITY /* Utilities don't need special code */
+
+#ifdef INCLUDE_PAM
+#include "../pam/pam.c"
+#endif
+#include <alloca.h>
+
+unsigned int cygwin_WinVersion;
+
+/* Conflict between Windows definitions and others */
+#ifdef NOERROR
+#undef NOERROR
+#endif
+#ifdef DELETE
+#undef DELETE
+#endif
+
+#include <windows.h>
+#include <ntstatus.h>
+#include <lmcons.h>
+
+#define EqualLuid(Luid1, Luid2) \
+ ((Luid1.LowPart == Luid2.LowPart) && (Luid1.HighPart == Luid2.HighPart))
+#include <sys/cygwin.h>
+
+/* Special static variables */
+static BOOL cygwin_debug = FALSE;
+static int fakesetugid = 1; /* when not privileged, setugid = noop */
+
+#undef setuid
+int cygwin_setuid(uid_t uid )
+{
+ int res = 0;
+ if (fakesetugid == 0) {
+ res = setuid(uid);
+ if (cygwin_debug)
+ fprintf(stderr, "setuid %u %u %d pid: %d\n",
+ uid, getuid(),res, getpid());
+ }
+ return res;
+}
+
+#undef setgid
+int cygwin_setgid(gid_t gid )
+{
+ int res = 0;
+ if (fakesetugid == 0) {
+ res = setgid(gid);
+ if (cygwin_debug)
+ fprintf(stderr, "setgid %u %u %d pid: %d\n",
+ gid, getgid(), res, getpid());
+ }
+ return res;
+}
+
+/* Background processes run at lower priority */
+static void cygwin_setpriority()
+{
+ if (!SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS))
+ SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS);
+ return;
+}
+
+
+/* GetVersion()
+ MSB: 1 for 95/98/ME; Next 7: build number, except for 95/98/ME
+ Next byte: 0
+ Next byte: minor version of OS
+ Low byte: major version of OS (3 or 4 for for NT, 5 for 2000 and XP) */
+//#define VERSION_IS_58M(x) (x & 0x80000000) /* 95, 98, Me */
+//#define VERSION_IS_NT(x) ((x & 0XFF) < 5) /* NT 4 or 3.51 */
+
+/*
+ Routine to find if process or thread is privileged
+*/
+
+enum {
+ CREATE_BIT = 1,
+};
+
+static DWORD get_privileges ()
+{
+ char buffer[1024];
+ DWORD i, length;
+ HANDLE hToken = NULL;
+ PTOKEN_PRIVILEGES privs;
+ LUID cluid, rluid;
+ DWORD ret = 0;
+
+ privs = (PTOKEN_PRIVILEGES) buffer;
+
+ if (OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hToken)
+ && LookupPrivilegeValue (NULL, SE_CREATE_TOKEN_NAME, &cluid)
+ && LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &rluid)
+ && (GetTokenInformation( hToken, TokenPrivileges,
+ privs, sizeof (buffer), &length)
+ || (GetLastError () == ERROR_INSUFFICIENT_BUFFER
+ && (privs = (PTOKEN_PRIVILEGES) alloca (length))
+ && GetTokenInformation(hToken, TokenPrivileges,
+ privs, length, &length)))) {
+ for (i = 0; i < privs->PrivilegeCount; i++) {
+ if (EqualLuid(privs->Privileges[i].Luid, cluid))
+ ret |= CREATE_BIT;
+ if (ret == (CREATE_BIT))
+ break;
+ }
+ }
+ else
+ fprintf(stderr, "has_create_token_privilege %u\n", GetLastError());
+
+ if (hToken)
+ CloseHandle(hToken);
+
+ return ret;
+}
+
+/*
+ We use cygwin_premain to fake a few things
+ and to provide some debug info
+*/
+void cygwin_premain2(int argc, char ** argv, struct per_process * ptr)
+{
+ int i, res, is_daemon = 0, is_spoolwritable, is_privileged, is_eximuser;
+ uid_t myuid, systemuid;
+ gid_t mygid, adminsgid;
+ struct passwd * pwp = NULL;
+ struct stat buf;
+ char *cygenv;
+ SID(1, SystemSid, SECURITY_LOCAL_SYSTEM_RID);
+ SID(2, AdminsSid, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS);
+ DWORD priv_flags;
+
+ myuid = getuid();
+ mygid = getgid();
+ cygwin_WinVersion = GetVersion();
+ if ((cygenv = getenv("CYGWIN")) == NULL) cygenv = "";
+ /* Produce some debugging on stderr,
+ cannot yet use exim's debug functions.
+ Exim does not use -c and ignores -n.
+ Set lower priority for daemons */
+ for (i = 1; i < argc; i++) {
+ if (argv[i][0] == '-') {
+ if (argv[i][1] == 'c') {
+ ssize_t size;
+ wchar_t *win32_path;
+ argv[i][1] = 'n'; /* Replace -c by -n */
+ cygwin_debug = TRUE;
+ fprintf(stderr, "CYGWIN = \"%s\".\n", cygenv);
+ if (((size = cygwin_conv_path(CCP_POSIX_TO_WIN_W,"/", win32_path, 0)) > 0)
+ && ((win32_path = store_malloc(size)) != NULL)
+ && (cygwin_conv_path(CCP_POSIX_TO_WIN_W,"/", win32_path, size) == 0)) {
+ fprintf(stderr, " Root / mapped to %ls.\n", win32_path);
+ store_free(win32_path);
+ }
+ }
+ else if (argv[i][1] == 'b' && argv[i][2] == 'd') {
+ is_daemon = 1;
+ cygwin_setpriority();
+ }
+ }
+ }
+
+ /* Nt/2000/XP
+ We initially set the exim uid & gid to those of the "exim user",
+ or to the root uid (SYSTEM) and exim gid (ADMINS),
+ If privileged, we setuid to those.
+ We always set the configure uid to the system uid.
+ We always set the root uid to the real uid
+ to allow exim imposed restrictions (bypassable by recompiling)
+ and to avoid exec that cause loss of privilege
+ If not privileged and unable to chown,
+ we set the exim uid to our uid.
+ If unprivileged and /var/spool/exim is writable and not running as listening daemon,
+ we fake all subsequent setuid. */
+
+ /* Get the system and admins uid from their sids */
+ if ((systemuid = cygwin_internal(CW_GET_UID_FROM_SID, & SystemSid)) == -1) {
+ fprintf(stderr, "Cannot map System sid. Aborting\n");
+ exit(1);
+ }
+ if ((adminsgid = cygwin_internal(CW_GET_GID_FROM_SID, & AdminsSid)) == -1) {
+ fprintf(stderr, "Cannot map Admins sid. Aborting\n");
+ exit(1);
+ }
+
+ priv_flags = get_privileges ();
+ is_privileged = !!(priv_flags & CREATE_BIT);
+
+ /* Call getpwnam for account exim after getting the local exim name */
+ char exim_username[DNLEN + UNLEN + 2];
+ if (cygwin_internal(CW_CYGNAME_FROM_WINNAME, "exim", exim_username, sizeof exim_username) != 0)
+ pwp = getpwnam (exim_username);
+
+ /* If cannot setuid to exim or and is not the daemon (which is assumed to be
+ able to chown or to be the exim user) set the exim ugid to our ugid to avoid
+ chown failures after creating files and to be able to setuid to exim in
+ exim.c ( "privilege not needed" ). */
+ if ((is_privileged == 0) && (!is_daemon)) {
+ exim_uid = myuid;
+ exim_gid = mygid;
+ }
+ else if (pwp != NULL) {
+ exim_uid = pwp->pw_uid; /* Set it according to passwd */
+ exim_gid = pwp->pw_gid;
+ is_eximuser = 1;
+ }
+ else {
+ exim_uid = systemuid;
+ exim_gid = adminsgid;
+ is_eximuser = 0;
+ }
+
+ res = stat("/var/spool/exim", &buf);
+ /* Check if writable (and can be stat) */
+ is_spoolwritable = ((res == 0) && ((buf.st_mode & S_IWOTH) != 0));
+
+ fakesetugid = (is_privileged == 0) && (is_daemon == 0) && (is_spoolwritable == 1);
+
+ if (is_privileged) { /* Can setuid */
+ if (cygwin_setgid(exim_gid) /* Setuid to exim */
+ || cygwin_setuid(exim_uid)) {
+ fprintf(stderr, "Unable to setuid/gid to exim. priv_flags: %x\n", priv_flags);
+ exit(0); /* Problem... Perhaps not in 544 */
+ }
+ }
+
+ /* Set the configuration file uid and gid to the system uid and admins gid. */
+ config_uid = systemuid;
+ config_gid = adminsgid;
+
+ /* Pretend we are root to avoid useless exec
+ and avoid exim set limitations.
+ We are limited by file access rights */
+ root_uid = getuid ();
+
+ if (cygwin_debug) {
+ fprintf(stderr, "Starting uid %u, gid %u, priv_flags %x, is_privileged %d, is_daemon %d, is_spoolwritable %d.\n",
+ myuid, mygid, priv_flags, is_privileged, is_daemon, is_spoolwritable);
+ fprintf(stderr, "root_uid %u, exim_uid %u, exim_gid %u, config_uid %u, config_gid %u, is_eximuser %d.\n",
+ root_uid, exim_uid, exim_gid, config_uid, config_gid, is_eximuser);
+ }
+ return;
+}
+
+#ifndef OS_LOAD_AVERAGE /* Can be set on command line */
+#define OS_LOAD_AVERAGE /* src/os.c need not provide it */
+
+/*****************************************************************
+ Functions for average load measurements
+
+ Uses NtQuerySystemInformation.
+ This requires definitions that are not part of
+ standard include files.
+
+ This is discouraged starting with WinXP.
+
+*************************************************************/
+/* Structure to compute the load average efficiently */
+typedef struct {
+ DWORD Lock;
+ unsigned long long Time100ns; /* Last measurement time */
+ unsigned long long IdleCount; /* Latest cumulative idle time */
+ unsigned long long LastCounter; /* Last measurement counter */
+ unsigned long long PerfFreq; /* Perf counter frequency */
+ int LastLoad; /* Last reported load, or -1 */
+} cygwin_perf_t;
+
+static struct {
+ HANDLE handle;
+ pid_t pid;
+ cygwin_perf_t *perf;
+} cygwin_load = {NULL, 0, NULL};
+
+#include <ntdef.h>
+
+typedef enum _SYSTEM_INFORMATION_CLASS
+{
+ SystemBasicInformation = 0,
+ SystemPerformanceInformation = 2,
+ SystemTimeOfDayInformation = 3,
+ SystemProcessesAndThreadsInformation = 5,
+ SystemProcessorTimes = 8,
+ SystemPagefileInformation = 18,
+ /* There are a lot more of these... */
+} SYSTEM_INFORMATION_CLASS;
+
+typedef struct _SYSTEM_BASIC_INFORMATION
+{
+ ULONG Unknown;
+ ULONG MaximumIncrement;
+ ULONG PhysicalPageSize;
+ ULONG NumberOfPhysicalPages;
+ ULONG LowestPhysicalPage;
+ ULONG HighestPhysicalPage;
+ ULONG AllocationGranularity;
+ ULONG LowestUserAddress;
+ ULONG HighestUserAddress;
+ ULONG ActiveProcessors;
+ UCHAR NumberProcessors;
+} SYSTEM_BASIC_INFORMATION, *PSYSTEM_BASIC_INFORMATION;
+
+typedef struct __attribute__ ((aligned (8))) _SYSTEM_PROCESSOR_TIMES
+{
+ LARGE_INTEGER IdleTime;
+ LARGE_INTEGER KernelTime;
+ LARGE_INTEGER UserTime;
+ LARGE_INTEGER DpcTime;
+ LARGE_INTEGER InterruptTime;
+ ULONG InterruptCount;
+} SYSTEM_PROCESSOR_TIMES, *PSYSTEM_PROCESSOR_TIMES;
+
+typedef NTSTATUS NTAPI (*NtQuerySystemInformation_t) (SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG);
+typedef ULONG NTAPI (*RtlNtStatusToDosError_t) (NTSTATUS);
+
+static NtQuerySystemInformation_t NtQuerySystemInformation;
+static RtlNtStatusToDosError_t RtlNtStatusToDosError;
+
+/*****************************************************************
+ *
+ LoadNtdll()
+ Load special functions from the NTDLL
+ Return TRUE if success.
+
+ *****************************************************************/
+
+static BOOL LoadNtdll()
+{
+ HINSTANCE hinstLib;
+
+ if ((hinstLib = LoadLibrary("NTDLL.DLL"))
+ && (NtQuerySystemInformation =
+ (NtQuerySystemInformation_t) GetProcAddress(hinstLib,
+ "NtQuerySystemInformation"))
+ && (RtlNtStatusToDosError =
+ (RtlNtStatusToDosError_t) GetProcAddress(hinstLib,
+ "RtlNtStatusToDosError")))
+ return TRUE;
+
+ DEBUG(D_load)
+ debug_printf("perf: load: %u (Windows)\n", GetLastError());
+ return FALSE;
+}
+/*****************************************************************
+ *
+ ReadStat()
+ Measures current Time100ns and IdleCount
+ Return TRUE if success.
+
+ *****************************************************************/
+
+static BOOL ReadStat(unsigned long long int *Time100nsPtr,
+ unsigned long long int *IdleCountPtr)
+{
+ NTSTATUS ret;
+ SYSTEM_BASIC_INFORMATION sbi;
+ PSYSTEM_PROCESSOR_TIMES spt;
+
+ *Time100nsPtr = *IdleCountPtr = 0;
+
+ if ((ret = NtQuerySystemInformation(SystemBasicInformation,
+ (PVOID) &sbi, sizeof sbi, NULL))
+ != STATUS_SUCCESS) {
+ DEBUG(D_load)
+ debug_printf("Perf: NtQuerySystemInformation: %u (Windows)\n",
+ RtlNtStatusToDosError(ret));
+ }
+ else if (!(spt = (PSYSTEM_PROCESSOR_TIMES) alloca(sizeof(spt[0]) * sbi.NumberProcessors))) {
+ DEBUG(D_load)
+ debug_printf("Perf: alloca: errno %d (%s)\n", errno, strerror(errno));
+ }
+ else if ((ret = NtQuerySystemInformation(SystemProcessorTimes, (PVOID) spt,
+ sizeof spt[0] * sbi.NumberProcessors, NULL))
+ != STATUS_SUCCESS) {
+ DEBUG(D_load)
+ debug_printf("Perf: NtQuerySystemInformation: %u (Windows)\n",
+ RtlNtStatusToDosError(ret));
+ }
+ else {
+ int i;
+ for (i = 0; i < sbi.NumberProcessors; i++) {
+ *Time100nsPtr += spt[i].KernelTime.QuadPart;;
+ *Time100nsPtr += spt[i].UserTime.QuadPart;
+ *IdleCountPtr += spt[i].IdleTime.QuadPart;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*****************************************************************
+ *
+ InitLoadAvg()
+ Initialize the cygwin_load.perf structure.
+ and set cygwin_load.perf->Flag to TRUE if successful.
+ This is called the first time os_getloadavg is called
+ *****************************************************************/
+static void InitLoadAvg(cygwin_perf_t *this)
+{
+ BOOL success = TRUE;
+
+ /* Get perf frequency and counter */
+ QueryPerformanceFrequency((LARGE_INTEGER *)& this->PerfFreq);
+ QueryPerformanceCounter((LARGE_INTEGER *)& this->LastCounter);
+
+ /* Get initial values for Time100ns and IdleCount */
+ success = success
+ && ReadStat( & this->Time100ns,
+ & this->IdleCount);
+ /* If success, set the Load to 0, else to -1 */
+ if (success) this->LastLoad = 0;
+ else {
+ log_write(0, LOG_MAIN, "Cannot obtain Load Average");
+ this->LastLoad = -1;
+ }
+}
+
+
+/*****************************************************************
+ *
+ os_getloadavg()
+
+ Return -1 if not available;
+ Return the previous value if less than AVERAGING sec old.
+ else return the processor load on a [0 - 1000] scale.
+
+ The first time we are called we initialize the counts
+ and return 0 or -1.
+ The initial load cannot be measured as we use the processor 100%
+*****************************************************************/
+static SECURITY_ATTRIBUTES sa = {sizeof (SECURITY_ATTRIBUTES), NULL, TRUE};
+#define AVERAGING 10
+
+int os_getloadavg()
+{
+ unsigned long long Time100ns, IdleCount, CurrCounter;
+ int value;
+ pid_t newpid;
+
+ /* New process.
+ Reload the dlls and the file mapping */
+ if ((newpid = getpid()) != cygwin_load.pid) {
+ BOOL new;
+ cygwin_load.pid = newpid;
+
+ if (!LoadNtdll()) {
+ log_write(0, LOG_MAIN, "Cannot obtain Load Average");
+ cygwin_load.perf = NULL;
+ return -1;
+ }
+
+ if ((new = !cygwin_load.handle)) {
+ cygwin_load.handle = CreateFileMapping (INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE,
+ 0, sizeof(cygwin_perf_t), NULL);
+ DEBUG(D_load)
+ debug_printf("Perf: CreateFileMapping: handle %p\n", (void *) cygwin_load.handle);
+ }
+ cygwin_load.perf = (cygwin_perf_t *) MapViewOfFile (cygwin_load.handle,
+ FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
+ DEBUG(D_load)
+ debug_printf("Perf: MapViewOfFile: addr %p\n", (void *) cygwin_load.perf);
+ if (new && cygwin_load.perf)
+ InitLoadAvg(cygwin_load.perf);
+ }
+
+ /* Check if initialized OK */
+ if (!cygwin_load.perf || cygwin_load.perf->LastLoad < 0)
+ return -1;
+
+ /* If we cannot get the lock, we return 0.
+ This is to prevent any lock-up possibility.
+ Finding a lock busy is unlikely, and giving up only
+ results in an immediate delivery .*/
+
+ if (InterlockedCompareExchange(&cygwin_load.perf->Lock, 1, 0)) {
+ DEBUG(D_load)
+ debug_printf("Perf: Lock busy\n");
+ return 0;
+ }
+
+ /* Get the current time (PerfCounter) */
+ QueryPerformanceCounter((LARGE_INTEGER *)& CurrCounter);
+ /* Calls closer than AVERAGING sec apart use the previous value */
+ if (CurrCounter - cygwin_load.perf->LastCounter >
+ AVERAGING * cygwin_load.perf->PerfFreq) {
+ /* Get Time100ns and IdleCount */
+ if (ReadStat( & Time100ns, & IdleCount)) { /* Success */
+ /* Return processor load on 1000 scale */
+ value = 1000 - ((1000 * (IdleCount - cygwin_load.perf->IdleCount)) /
+ (Time100ns - cygwin_load.perf->Time100ns));
+ cygwin_load.perf->Time100ns = Time100ns;
+ cygwin_load.perf->IdleCount = IdleCount;
+ cygwin_load.perf->LastCounter = CurrCounter;
+ cygwin_load.perf->LastLoad = value;
+ DEBUG(D_load)
+ debug_printf("Perf: New load average %d\n", value);
+ }
+ else { /* Something bad happened.
+ Refuse to measure the load anymore
+ but don't bother releasing the buffer */
+ log_write(0, LOG_MAIN, "Cannot obtain Load Average");
+ cygwin_load.perf->LastLoad = -1;
+ }
+ }
+ else
+ DEBUG(D_load)
+ debug_printf("Perf: Old load average %d\n", cygwin_load.perf->LastLoad);
+ cygwin_load.perf->Lock = 0;
+ return cygwin_load.perf->LastLoad;
+}
+#endif /* OS_LOAD_AVERAGE */
+#endif /* COMPILE_UTILITY */