1 /* $Cambridge: exim/src/OS/os.c-cygwin,v 1.1 2004/10/06 15:07:39 ph10 Exp $ */
3 /*************************************************
4 * Exim - an Internet mail transport agent *
5 *************************************************/
7 /* Cygwin-specific code. December 2002
8 This is concatenated onto the generic src/os.c file.
10 This code was supplied by Pierre A. Humblet <Pierre.Humblet@ieee.org>
13 /* We need a special mkdir that
14 allows names starting with // */
16 int cygwin_mkdir( const char *path, mode_t mode )
18 const char * p = path;
19 if (*p == '/') while(*(p+1) == '/') p++;
20 return mkdir(p, mode);
23 /* We have strsignal but cannot use #define
24 because types don't match */
25 #define OS_STRSIGNAL /* src/os.c need not provide it */
26 char * os_strsignal(int sig)
28 return (char *) strsignal(sig);
31 #ifndef COMPILE_UTILITY /* Utilities don't need special code */
32 #ifdef INCLUDE_MINIRES
33 #include "../minires/minires.c"
34 #include "../minires/os-interface.c"
38 #include "../pam/pam.c"
41 unsigned int cygwin_WinVersion;
43 /* Conflict between Windows definitions and others */
52 #include <sys/cygwin.h>
54 /* Special static variables */
55 static BOOL cygwin_debug = FALSE;
56 static int privileged = 1; /* when not privileged, setuid = noop */
59 int cygwin_setuid(uid_t uid )
62 if (privileged <= 0) return 0;
66 fprintf(stderr, "setuid %lu %lu %d pid: %d\n",
67 uid, getuid(),res, getpid());
73 int cygwin_setgid(gid_t gid )
76 if (privileged <= 0) return 0;
80 fprintf(stderr, "setgid %lu %lu %d pid: %d\n",
81 gid, getgid(), res, getpid());
86 /* Background processes run at lower priority */
87 static void setpriority()
89 if (!SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS))
90 SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS);
96 MSB: 1 for 95/98/ME; Next 7: build number, except for 95/98/ME
98 Next byte: minor version of OS
99 Low byte: major version of OS (3 or 4 for for NT, 5 for 2000 and XP) */
100 #define VERSION_IS_58M(x) (x & 0x80000000) /* 95, 98, Me */
101 #define VERSION_IS_NT(x) ((x & 0XFF) < 5) /* NT 4 or 3.51 */
104 Routine to find if process or thread is privileged
112 static DWORD get_privileges ()
116 HANDLE hToken = NULL;
117 PTOKEN_PRIVILEGES privs;
121 privs = (PTOKEN_PRIVILEGES) buffer;
123 if (OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hToken)
124 && LookupPrivilegeValue (NULL, SE_CREATE_TOKEN_NAME, &cluid)
125 && LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &rluid)
126 && (GetTokenInformation( hToken, TokenPrivileges,
127 privs, sizeof (buffer), &length)
128 || (GetLastError () == ERROR_INSUFFICIENT_BUFFER
129 && (privs = (PTOKEN_PRIVILEGES) alloca (length))
130 && GetTokenInformation(hToken, TokenPrivileges,
131 privs, length, &length)))) {
132 for (i = 0; i < privs->PrivilegeCount; i++) {
133 if (privs->Privileges[i].Luid.QuadPart == cluid.QuadPart)
135 else if (privs->Privileges[i].Luid.QuadPart == rluid.QuadPart)
138 if (ret == (CREATE_BIT | RESTORE_BIT))
143 fprintf(stderr, "has_create_token_privilege %ld\n", GetLastError());
151 /* We use a special routine to initialize
152 cygwin_init is called from the OS_INIT macro in main(). */
154 void cygwin_init(int argc, char ** argv, void * rup,
155 void * eup, void * egp, void * cup)
158 uid_t myuid, systemuid;
159 gid_t mygid, adminsgid;
161 char *cygenv, win32_path[MAX_PATH];
162 SID(1, SystemSid, SECURITY_LOCAL_SYSTEM_RID);
163 SID(2, AdminsSid, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS);
168 cygwin_WinVersion = GetVersion();
169 if ((cygenv = getenv("CYGWIN")) == NULL) cygenv = "";
170 /* Produce some debugging on stderr,
171 cannot yet use exim's debug functions.
172 Exim does not use -c and ignores -n.
173 Set lower priority for daemons */
174 for (i = 1; i < argc; i++) {
175 if (argv[i][0] == '-') {
176 if (argv[i][1] == 'c') {
177 argv[i][1] = 'n'; /* Replace -c by -n */
179 fprintf(stderr, "CYGWIN = \"%s\".", cygenv);
180 cygwin_conv_to_win32_path("/", win32_path);
181 fprintf(stderr, " Root / mapped to %s.\n", win32_path);
183 else if (argv[i][1] == 'b' && argv[i][2] == 'd')
187 if (VERSION_IS_58M(cygwin_WinVersion)) {
188 * (uid_t *) rup = myuid; /* Pretend we are root */
189 * (uid_t *) eup = myuid; /* ... and exim */
190 * (gid_t *) egp = mygid;
194 We initially set the exim uid & gid to those of the "real exim",
195 or to the root uid (SYSTEM) and exim gid (ADMINS),
196 If privileged, we setuid to those.
197 We always set the configure uid to the system uid.
198 We always set the root uid to the real uid
199 to avoid useless execs following forks.
200 If not privileged and unable to chown,
201 we set the exim uid to our uid.
202 If unprivileged, we fake all subsequent setuid. */
204 priv_flags = get_privileges ();
205 privileged = !!(priv_flags & CREATE_BIT);
207 /* Get the system and admins uid from their sids,
208 or use the default values from the Makefile. */
209 if ((systemuid = cygwin_internal(CW_GET_UID_FROM_SID, & SystemSid)) == -1)
210 systemuid = * (uid_t *) eup;
211 if ((adminsgid = cygwin_internal(CW_GET_GID_FROM_SID, & AdminsSid)) == -1)
212 adminsgid = * (gid_t *) egp;
214 if ((pwp = getpwnam("exim")) != NULL) {
215 * (uid_t *) eup = pwp->pw_uid; /* Set it according to passwd */
216 * (gid_t *) egp = pwp->pw_gid;
219 * (uid_t *) eup = systemuid;
220 * (gid_t *) egp = adminsgid;
223 /* Set the configuration uid to the system uid.
224 Note that exim uid is also accepted as owner of exim.conf. */
225 * (uid_t *) cup = systemuid;
227 if (privileged) { /* Can setuid */
228 if (cygwin_setgid(* (gid_t *) egp) /* Setuid to exim */
229 || cygwin_setuid(* (uid_t *) eup))
230 privileged = -1; /* Problem... Perhaps not in 544 */
233 /* Pretend we are root to avoid useless execs.
234 We are limited by file access rights */
235 * (uid_t *) rup = getuid ();
237 /* If we have not setuid to exim and cannot chown,
238 set the exim uid to our uid to avoid chown failures */
239 if (privileged <= 0 && !(priv_flags & RESTORE_BIT))
240 * (uid_t *) eup = * (uid_t *) rup;
243 fprintf(stderr, "Starting uid %ld, gid %ld, ntsec %lu, privileged %d.\n",
244 myuid, mygid, cygwin_internal(CW_CHECK_NTSEC, NULL), privileged);
245 fprintf(stderr, "root_uid %ld, exim_uid %ld, exim_gid %ld, config_uid %ld.\n",
246 * (uid_t *) rup, * (uid_t *) eup, * (gid_t *) egp, * (uid_t *) cup);
251 /*****************************************************************
253 Functions for average load measurements
255 Obtaining statistics in Windows is done at a low level by
256 calling registry functions, in particular the key
257 HKEY_PERFORMANCE_DATA on NT and successors.
258 Something equivalent exists on Win95, see Microsoft article
259 HOWTO: Access the Performance Registry Under Windows 95 (Q174631)
260 but it is not implemented here.
262 The list of objects to be polled is specified in the string
263 passed to RegQueryValueEx in ReadStat() below.
264 On NT, all objects are polled even if info about only one is
265 required. This is fixed in Windows 2000. See articles
266 INFO: Perflib Calling Close Procedure in Windows 2000 (Q270127)
267 INFO: Performance Data Changes Between Windows NT 4.0 and Windows
270 It is unclear to me how the counters are primarily identified.
271 Whether it's by name strings or by the offset of their strings
272 as mapped in X:\Winnt\system32\perfc009.dat [or equivalently as
273 reported by the registry functions in GetNameStrings( ) below].
274 Microsoft documentation seems to say that both methods should
277 In the interest of speed and language independence, the main
278 code below relies on offsets. However if debug is enabled, the
279 code verifies that the names of the corresponding strings are
282 *****************************************************************/
283 #ifndef OS_LOAD_AVERAGE /* Can be set on command line */
284 #define OS_LOAD_AVERAGE /* src/os.c need not provide it */
286 /* Object and counter indices and names */
287 #define PROCESSOR_OBJECT_INDEX 238
288 #define PROCESSOR_OBJECT_STRING "238"
289 #define PROCESSOR_OBJECT_NAME "Processor"
290 #define PROCESSOR_TIME_COUNTER 6
291 #define PROCESSOR_TIME_NAME "% Processor Time"
293 /* Structure to compute the load average efficiently */
295 long long Time100ns; /* Last measurement time */
296 long long IdleCount; /* Latest cumulative idle time */
297 long long LastCounter; /* Last measurement counter */
298 long long PerfFreq; /* Perf counter frequency */
299 PPERF_DATA_BLOCK PerfData; /* Pointer to a buffer to get the data */
300 DWORD BufferSize; /* Size of PerfData */
301 int LastLoad; /* Last reported load, or -1 */
302 LPSTR * NamesArray; /* Temporary (malloc) buffer for index */
303 BOOL Init; /* True if initialized */
304 } cygwin_load = { 0, 0, 0, 0, NULL, 0, 0, NULL, FALSE};
306 #define BYTEINCREMENT 800 /* Block to add to PerfData */
308 /*****************************************************************
310 Macros to navigate through the performance data.
312 *****************************************************************/
313 #define FirstObject(PerfData)\
314 ((PPERF_OBJECT_TYPE)((PBYTE)PerfData + PerfData->HeaderLength))
315 #define NextObject(PerfObj)\
316 ((PPERF_OBJECT_TYPE)((PBYTE)PerfObj + PerfObj->TotalByteLength))
317 #define ObjectCounterBlock(PerfObj)\
318 ((PPERF_COUNTER_BLOCK)(PBYTE)PerfObj + PerfObj->DefinitionLength )
319 #define FirstInstance(PerfObj )\
320 ((PPERF_INSTANCE_DEFINITION)((PBYTE)PerfObj + PerfObj->DefinitionLength))
321 #define InstanceCounterBlock(PerfInst)\
322 ((PPERF_COUNTER_BLOCK) ((PBYTE)PerfInst + PerfInst->ByteLength ))
323 #define NextInstance(PerfInst )\
324 ((PPERF_INSTANCE_DEFINITION)((PBYTE)InstanceCounterBlock(PerfInst) + \
325 InstanceCounterBlock(PerfInst)->ByteLength) )
326 #define FirstCounter(PerfObj)\
327 ((PPERF_COUNTER_DEFINITION) ((PBYTE)PerfObj + PerfObj->HeaderLength))
328 #define NextCounter(PerfCntr)\
329 ((PPERF_COUNTER_DEFINITION)((PBYTE)PerfCntr + PerfCntr->ByteLength))
331 /*****************************************************************
333 Load the counter and object names from the registry
334 to cygwin_load.NameStrings
335 and index them in cygwin_load.NamesArray
337 NameStrings seems to be taken from the file
338 X:\Winnt\system32\perfc009.dat
340 This is used only for name verification during initialization,
341 if DEBUG(D_load) is TRUE.
343 *****************************************************************/
344 static BOOL GetNameStrings( )
346 HKEY hKeyPerflib; // handle to registry key
347 DWORD dwArraySize; // size for array
348 DWORD dwNamesSize; // size for strings
349 LPSTR lpCurrentString; // pointer for enumerating data strings
350 DWORD dwCounter; // current counter index
353 /* Get the number of Counter items into dwArraySize. */
354 if ((res = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
355 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib",
357 KEY_QUERY_VALUE, /* KEY_READ, */
360 DEBUG(D_load) debug_printf("RegOpenKeyEx (1): error %ld (Windows)\n", res);
363 dwNamesSize = sizeof(dwArraySize); /* Temporary reuse */
364 if ((res = RegQueryValueEx( hKeyPerflib,
368 (LPBYTE) &dwArraySize,
371 DEBUG(D_load) debug_printf("RegQueryValueEx (1): error %ld (Windows)\n", res);
374 RegCloseKey( hKeyPerflib );
375 /* Open the key containing the counter and object names. */
376 if ((res = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
377 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009",
382 DEBUG(D_load) debug_printf("RegOpenKeyEx (2): error %ld (Windows)\n", res);
385 /* Get the size of the Counter value in the key
386 and then read the value in the tail of NamesArray */
388 lpCurrentString = NULL;
390 res = RegQueryValueEx( hKeyPerflib,
394 (unsigned char *) lpCurrentString,
396 if ((res == ERROR_SUCCESS) && /* Bug (NT 4.0): SUCCESS was returned on first call */
397 (cygwin_load.NamesArray != NULL)) break;
398 if ((res == ERROR_SUCCESS) || /* but cygwin_load.NamesArrays == NULL */
399 (res == ERROR_MORE_DATA)) {
400 /* Allocate memory BOTH for the names array and for the counter and object names */
401 if ((cygwin_load.NamesArray =
402 (LPSTR *) malloc( (dwArraySize + 1) * sizeof(LPSTR) + dwNamesSize * sizeof(CHAR)))
404 /* Point to area for the counter and object names */
405 lpCurrentString = (LPSTR) & cygwin_load.NamesArray[dwArraySize + 1];
408 DEBUG(D_load) debug_printf("Malloc: errno %d (%s)\n", errno, strerror(errno));
410 else { /* Serious error */
411 DEBUG(D_load) debug_printf("RegQueryValueEx (2): error %ld (Windows)\n", res);
415 RegCloseKey( hKeyPerflib );
416 /* Index the names into an array. */
417 while (*lpCurrentString) {
418 dwCounter = atol( lpCurrentString );
419 lpCurrentString += (lstrlen(lpCurrentString)+1);
420 cygwin_load.NamesArray[dwCounter] = lpCurrentString;
421 lpCurrentString += (strlen(lpCurrentString)+1);
426 /*****************************************************************
428 Find the value of the Processor Time counter
430 *****************************************************************/
431 static BOOL ReadTimeCtr(PPERF_OBJECT_TYPE PerfObj,
432 PPERF_COUNTER_DEFINITION CurCntr,
433 PPERF_COUNTER_BLOCK PtrToCntr,
434 unsigned long long * TimePtr){
436 /* Scan all counters. */
437 for( j = 0; j < PerfObj->NumCounters; j++ ) {
438 if (CurCntr->CounterNameTitleIndex == PROCESSOR_TIME_COUNTER) {
439 /* Verify it is really the proc time counter */
440 if ((CurCntr->CounterType != PERF_100NSEC_TIMER_INV) || /* Wrong type */
441 ((cygwin_load.NamesArray != NULL) && /* Verify name */
442 (strcmp(cygwin_load.NamesArray[CurCntr->CounterNameTitleIndex],
443 PROCESSOR_TIME_NAME)))) {
444 log_write(0, LOG_MAIN|LOG_PANIC,
445 "Incorrect Perf counter type or name %x %s",
446 (unsigned) CurCntr->CounterType,
447 cygwin_load.NamesArray[CurCntr->CounterNameTitleIndex]);
450 *TimePtr += *(unsigned long long int *) ((PBYTE) PtrToCntr + CurCntr->CounterOffset);
451 return TRUE; /* return TRUE as soon as we found the counter */
453 /* Get the next counter. */
454 CurCntr = NextCounter( CurCntr );
458 /*****************************************************************
461 Measures current Time100ns and IdleCount
462 Return TRUE if success.
464 *****************************************************************/
465 static BOOL ReadStat(long long int *Time100nsPtr,
466 long long int * IdleCountPtr)
468 PPERF_OBJECT_TYPE PerfObj;
469 PPERF_INSTANCE_DEFINITION PerfInst;
470 PPERF_COUNTER_DEFINITION PerfCntr;
471 PPERF_COUNTER_BLOCK PtrToCntr;
474 /* Get the performance data for the Processor object
475 There is no need to open a key.
476 We may need to blindly increase the buffer size.
477 BufferSize does not return info but may be changed */
479 DWORD BufferSize = cygwin_load.BufferSize;
480 res = RegQueryValueEx( HKEY_PERFORMANCE_DATA,
481 PROCESSOR_OBJECT_STRING,
484 (LPBYTE) cygwin_load.PerfData,
486 if (res == ERROR_SUCCESS) break;
487 if (res == ERROR_MORE_DATA ) {
488 /* Increment if necessary to get a buffer that is big enough. */
489 cygwin_load.BufferSize += BYTEINCREMENT;
490 if ((cygwin_load.PerfData =
491 (PPERF_DATA_BLOCK) realloc( cygwin_load.PerfData, cygwin_load.BufferSize ))
493 DEBUG(D_load) debug_printf("Malloc: errno %d (%s)\n", errno, strerror(errno));
495 else { /* Serious error */
496 DEBUG(D_load) debug_printf("RegQueryValueEx (3): error %ld (Windows)\n", res);
500 /* Initialize the counters */
503 /* We should only have one object, but write general code just in case. */
504 PerfObj = FirstObject( cygwin_load.PerfData );
505 for( i = 0; i < cygwin_load.PerfData->NumObjectTypes; i++ ) {
506 /* We are only interested in the processor object */
507 if ( PerfObj->ObjectNameTitleIndex == PROCESSOR_OBJECT_INDEX) {
508 /* Possibly verify it is really the Processor object. */
509 if ((cygwin_load.NamesArray != NULL) &&
510 (strcmp(cygwin_load.NamesArray[PerfObj->ObjectNameTitleIndex],
511 PROCESSOR_OBJECT_NAME))) {
512 log_write(0, LOG_MAIN|LOG_PANIC,
513 "Incorrect Perf object name %s",
514 cygwin_load.NamesArray[PerfObj->ObjectNameTitleIndex]);
517 /* Get the first counter */
518 PerfCntr = FirstCounter( PerfObj );
519 /* See if the object has instances.
520 It should, but write general code. */
521 if( PerfObj->NumInstances != PERF_NO_INSTANCES ) {
522 PerfInst = FirstInstance( PerfObj );
523 for( k = 0; k < PerfObj->NumInstances; k++ ) {
524 /* There can be several processors.
525 Accumulate both the Time100ns and the idle counter.
526 On Win 2000 I have seen an instance named "_Total".
527 Do not use it. We only use instances with a single
528 character in the name.
529 If we examine the object names, we also look at the instance
530 names and their lengths and issue reports */
531 if ( cygwin_load.NamesArray != NULL) {
532 CHAR ascii[30]; /* The name is in unicode */
533 wsprintf(ascii,"%.29lS",
534 (char *)((PBYTE)PerfInst + PerfInst->NameOffset));
535 log_write(0, LOG_MAIN,
536 "Perf: Found processor instance \"%s\", length %d",
537 ascii, PerfInst->NameLength);
538 if ((PerfInst->NameLength != 4) &&
539 (strcmp(ascii, "_Total") != 0)) {
540 log_write(0, LOG_MAIN|LOG_PANIC,
541 "Perf: WARNING: Unexpected processor instance name");
545 if (PerfInst->NameLength == 4) {
546 *Time100nsPtr += cygwin_load.PerfData->PerfTime100nSec.QuadPart;
547 PtrToCntr = InstanceCounterBlock(PerfInst);
548 if (! ReadTimeCtr(PerfObj, PerfCntr, PtrToCntr, IdleCountPtr)) {
552 PerfInst = NextInstance( PerfInst );
554 return (*Time100nsPtr != 0); /* Something was read */
556 else { /* No instance, just the counter data */
557 *Time100nsPtr = cygwin_load.PerfData->PerfTime100nSec.QuadPart;
558 PtrToCntr = ObjectCounterBlock(PerfObj);
559 return ReadTimeCtr(PerfObj, PerfCntr, PtrToCntr, IdleCountPtr);
562 PerfObj = NextObject( PerfObj );
564 return FALSE; /* Did not find the Processor object */
567 /*****************************************************************
570 Initialize the cygwin_load structure.
571 and set cygwin_load.Flag to TRUE if successful.
572 This is called the first time os_getloadavg is called
573 *****************************************************************/
574 static void InitLoadAvg()
577 cygwin_load.Init = TRUE; /* We have run */
578 /* Get perf frequency and counter */
579 QueryPerformanceFrequency((LARGE_INTEGER *)& cygwin_load.PerfFreq);
580 QueryPerformanceCounter((LARGE_INTEGER *)& cygwin_load.LastCounter);
582 /* Get the name strings through the registry
583 to verify that the object and counter numbers
584 have the names we expect */
585 success = GetNameStrings();
587 /* Get initial values for Time100ns and IdleCount
588 and possibly verify the names */
589 // success = success &&
590 success = ReadStat( & cygwin_load.Time100ns,
591 & cygwin_load.IdleCount);
592 /* If success, set the Load to 0, else to -1 */
593 if (success) cygwin_load.LastLoad = 0;
595 log_write(0, LOG_MAIN, "Cannot obtain Load Average");
596 cygwin_load.LastLoad = -1;
598 /* Free the buffer created for debug name verification */
599 if (cygwin_load.NamesArray != NULL) {
600 free(cygwin_load.NamesArray);
601 cygwin_load.NamesArray = NULL;
604 /*****************************************************************
608 Return -1 if not available;
609 Return the previous value if less than AVERAGING sec old.
610 else return the processor load on a [0 - 1000] scale.
612 The first time we are called we initialize the counts
614 The load cannot be measured because we use the processor 100%
615 *****************************************************************/
619 long long Time100ns, IdleCount, CurrCounter;
622 if (! cygwin_load.Init) InitLoadAvg();
623 else if (cygwin_load.LastLoad >= 0) { /* Initialized OK */
624 /* Get the current time (PerfCounter) */
625 QueryPerformanceCounter((LARGE_INTEGER *)& CurrCounter);
626 /* Calls closer than AVERAGING sec apart use the previous value */
627 if (CurrCounter - cygwin_load.LastCounter >
628 AVERAGING * cygwin_load.PerfFreq) {
629 /* Get Time100ns and IdleCount */
630 if (ReadStat( & Time100ns, & IdleCount)) { /* Success */
631 /* Return processor load on 1000 scale */
632 value = 1000 - ((1000 * (IdleCount - cygwin_load.IdleCount)) /
633 (Time100ns - cygwin_load.Time100ns));
634 cygwin_load.Time100ns = Time100ns;
635 cygwin_load.IdleCount = IdleCount;
636 cygwin_load.LastCounter = CurrCounter;
637 cygwin_load.LastLoad = value;
639 else { /* Something bad happened.
640 Refuse to measure the load anymore
641 but don't bother releasing the buffer */
642 log_write(0, LOG_MAIN, "Cannot obtain Load Average");
643 cygwin_load.LastLoad = -1;
648 debug_printf("Perf: load average = %d\n", cygwin_load.LastLoad);
649 return cygwin_load.LastLoad;
651 #endif /* OS_LOAD_AVERAGE */
652 #endif /* COMPILE_UTILITY */