Commit | Line | Data |
---|---|---|
7f918cf1 CE |
1 | #define _GNU_SOURCE |
2 | ||
3 | #include "platform.h" | |
4 | ||
5 | #include "windows.c" | |
6 | #include "mremap.c" | |
7 | ||
8 | void *GC_mmapAnon (void *start, size_t length) { | |
9 | return Windows_mmapAnon (start, length); | |
10 | } | |
11 | ||
12 | void GC_release (void *base, size_t length) { | |
13 | Windows_release (base, length); | |
14 | } | |
15 | ||
16 | void *GC_extendHead (void *base, size_t length) { | |
17 | return Windows_mmapAnon (base, length); | |
18 | } | |
19 | ||
20 | void *GC_extendTail (void *base, size_t length) { | |
21 | return Windows_extend (base, length); | |
22 | } | |
23 | ||
24 | uintmax_t GC_physMem (void) { | |
25 | #ifdef _WIN64 | |
26 | MEMORYSTATUSEX memstat; | |
27 | memstat.dwLength = sizeof(memstat); | |
28 | GlobalMemoryStatusEx(&memstat); | |
29 | return (uintmax_t)memstat.ullTotalPhys; | |
30 | #else | |
31 | MEMORYSTATUS memstat; | |
32 | memstat.dwLength = sizeof(memstat); | |
33 | GlobalMemoryStatus(&memstat); | |
34 | return (uintmax_t)memstat.dwTotalPhys; | |
35 | #endif | |
36 | } | |
37 | ||
38 | size_t GC_pageSize (void) { | |
39 | SYSTEM_INFO sysinfo; | |
40 | GetSystemInfo(&sysinfo); | |
41 | return (size_t)sysinfo.dwAllocationGranularity; | |
42 | } | |
43 | ||
44 | HANDLE fileDesHandle (int fd) { | |
45 | // The temporary prevents a "cast does not match function type" warning. | |
46 | intptr_t t; | |
47 | ||
48 | t = _get_osfhandle (fd); | |
49 | return (HANDLE)t; | |
50 | } | |
51 | ||
52 | int mkstemp (char *template) { | |
53 | char file_path[255]; | |
54 | char file_name[255]; | |
55 | char templ[4]; | |
56 | ||
57 | if (0 == GetTempPath (sizeof (file_path), file_path)) | |
58 | diee ("unable to make temporary file"); | |
59 | strncpy (templ, template, sizeof (templ) - 1); | |
60 | templ[sizeof (templ) - 1] = 0x00; | |
61 | if (0 == GetTempFileName (file_path, templ, 0, file_name)) | |
62 | diee ("unable to make temporary file"); | |
63 | return _open (file_name, _O_CREAT | _O_RDWR, _S_IREAD | _S_IWRITE); | |
64 | } | |
65 | ||
66 | /* ------------------------------------------------- */ | |
67 | /* Date */ | |
68 | /* ------------------------------------------------- */ | |
69 | ||
70 | #ifndef __GNUC__ | |
71 | #define EPOCHFILETIME (116444736000000000i64) | |
72 | #else | |
73 | #define EPOCHFILETIME (116444736000000000LL) | |
74 | #endif | |
75 | ||
76 | /* The basic plan is to get an initial time using GetSystemTime | |
77 | * that is good up to ~10ms accuracy. From then on, we compute | |
78 | * using deltas with the high-resolution (> microsecond range) | |
79 | * performance timers. A 64-bit accumulator holds microseconds | |
80 | * since (*nix) epoch. This is good for over 500,000 years before | |
81 | * wrap-around becomes a concern. | |
82 | * | |
83 | * However, we might need to watch out for wrap-around with the | |
84 | * QueryPerformanceCounter, because it could be measuring at a higher | |
85 | * frequency than microseconds. | |
86 | * | |
87 | * This function only strives to allow a program to run for | |
88 | * 100 years without being restarted. | |
89 | */ | |
90 | int gettimeofday (struct timeval *tv, | |
91 | __attribute__ ((unused)) struct timezone *tz) { | |
92 | static LARGE_INTEGER frequency; /* ticks/second */ | |
93 | static LARGE_INTEGER baseCounter; /* ticks since last rebase */ | |
94 | static LARGE_INTEGER baseMicroSeconds; /* unix time at last rebase */ | |
95 | ||
96 | LARGE_INTEGER nowCounter; | |
97 | LARGE_INTEGER deltaCounter; | |
98 | LARGE_INTEGER nowMicroSeconds; | |
99 | double deltaMicroseconds; | |
100 | ||
101 | /* This code is run the first time gettimeofday is called. */ | |
102 | if (frequency.QuadPart == 0) { | |
103 | FILETIME ft; | |
104 | /* tzset prepares the localtime function. I don't | |
105 | * really understand why it's here and not there, | |
106 | * but this has been the case since before svn logs. | |
107 | * So I leave it here to preserve the status-quo. | |
108 | */ | |
109 | tzset(); | |
110 | ||
111 | QueryPerformanceCounter(&baseCounter); | |
112 | QueryPerformanceFrequency(&frequency); | |
113 | if (frequency.QuadPart == 0) | |
114 | die("no high resolution clock"); | |
115 | ||
116 | GetSystemTimeAsFileTime (&ft); | |
117 | baseMicroSeconds.LowPart = ft.dwLowDateTime; | |
118 | baseMicroSeconds.HighPart = ft.dwHighDateTime; | |
119 | baseMicroSeconds.QuadPart -= EPOCHFILETIME; | |
120 | baseMicroSeconds.QuadPart /= 10; /* 100ns -> 1ms */ | |
121 | } | |
122 | ||
123 | /* Use the high res counter ticks to calculate the delta. | |
124 | * A double has 52+1 bits of precision. This means it can fit | |
125 | * deltas of up to 9007199254 seconds, or 286 years. We could | |
126 | * rebase before an overflow, but 286 is already > 100. | |
127 | */ | |
128 | QueryPerformanceCounter(&nowCounter); | |
129 | deltaCounter.QuadPart = nowCounter.QuadPart - baseCounter.QuadPart; | |
130 | deltaMicroseconds = deltaCounter.QuadPart; | |
131 | deltaMicroseconds /= frequency.QuadPart; | |
132 | deltaMicroseconds *= 1000000.0; | |
133 | nowMicroSeconds.QuadPart = | |
134 | baseMicroSeconds.QuadPart + deltaMicroseconds; | |
135 | ||
136 | /* If the frequency too fast, we need to check for wrap around. | |
137 | * 2**32 seconds is 136 years, so if HighPart == 0 we don't need to | |
138 | * waste a system call on GetSystemTimeAsFileTime. | |
139 | */ | |
140 | if (frequency.HighPart != 0) { | |
141 | LARGE_INTEGER nowLowResMicroSeconds; | |
142 | FILETIME ft; | |
143 | ||
144 | /* Use low res timer to detect performance counter wrap-around. */ | |
145 | GetSystemTimeAsFileTime (&ft); | |
146 | nowLowResMicroSeconds.LowPart = ft.dwLowDateTime; | |
147 | nowLowResMicroSeconds.HighPart = ft.dwHighDateTime; | |
148 | nowLowResMicroSeconds.QuadPart -= EPOCHFILETIME; | |
149 | nowLowResMicroSeconds.QuadPart /= 10; | |
150 | ||
151 | /* If deltaMicroseconds deviates by more than a second from the low | |
152 | * resolution timer, assume the high performance counter has wrapped. | |
153 | * One second is a safe margin b/c QueryPerformanceFrequency must fit | |
154 | * in a 64-bit integer. Therefore any wrap must exceed one second. | |
155 | */ | |
156 | if (nowMicroSeconds.QuadPart + 1000000 < nowLowResMicroSeconds.QuadPart) { | |
157 | baseCounter = nowCounter; | |
158 | baseMicroSeconds = nowLowResMicroSeconds; | |
159 | nowMicroSeconds = nowLowResMicroSeconds; | |
160 | } | |
161 | ||
162 | /* The above wrap-around detection destroys high resolution timing. | |
163 | * However, if one needs high resolution timing, then one is querying | |
164 | * gettimeofday quite often. Therefore, rebase the clock before any | |
165 | * wrap around troubles happen. We don't do this too often as it | |
166 | * introduces clock drift. | |
167 | */ | |
168 | if ((deltaCounter.HighPart & 0xffff0000UL) != 0) { | |
169 | baseCounter = nowCounter; | |
170 | baseMicroSeconds = nowMicroSeconds; | |
171 | } | |
172 | } | |
173 | ||
174 | tv->tv_sec = (long)(nowMicroSeconds.QuadPart / 1000000); | |
175 | tv->tv_usec = (long)(nowMicroSeconds.QuadPart % 1000000); | |
176 | ||
177 | return 0; | |
178 | } | |
179 | ||
180 | /* ------------------------------------------------- */ | |
181 | /* MLton.Itimer */ | |
182 | /* ------------------------------------------------- */ | |
183 | ||
184 | /* We use the kernel's TimerQueues -- see: | |
185 | * http://msdn.microsoft.com/en-us/library/ms686796(VS.85).aspx | |
186 | */ | |
187 | ||
188 | static HANDLE MainThread = NULL; | |
189 | static HANDLE TimerQueue = NULL; | |
190 | static HANDLE RealTimer = NULL; | |
191 | static HANDLE VirtTimer = NULL; | |
192 | static HANDLE ProfTimer = NULL; | |
193 | static HANDLE PrioTimer = NULL; | |
194 | static void (*SIGALRM_handler)(int sig) = SIG_DFL; | |
195 | static void (*SIGVTAM_handler)(int sig) = SIG_DFL; | |
196 | static void (*SIGPROF_handler)(int sig) = SIG_DFL; | |
197 | ||
198 | /* The timer handler is fired in another thread. | |
199 | * The idea is to suspend the main thread and resume it once we're done. | |
200 | * This will appear more-or-less the same as if a Unix system had received | |
201 | * the signal. We will also be firing the handler in the timer thread itself | |
202 | * for performance reasons (MLton uses this mechanism to do multi-threading). | |
203 | * This means the signal handlers must be fast, which they are since they | |
204 | * just mark the signal to be processed later. | |
205 | */ | |
206 | ||
207 | static VOID CALLBACK MLton_SIGALRM(__attribute__ ((unused)) PVOID myArg, | |
208 | __attribute__ ((unused)) BOOLEAN timeout) { | |
209 | SuspendThread(MainThread); | |
210 | if (SIGALRM_handler == SIG_IGN) { | |
211 | /* noop */ | |
212 | } else if (SIGALRM_handler == SIG_DFL) { | |
213 | die("alarm"); | |
214 | } else { | |
215 | (*SIGALRM_handler)(SIGALRM); | |
216 | } | |
217 | ResumeThread(MainThread); | |
218 | } | |
219 | static VOID CALLBACK MLton_SIGVTAM(__attribute__ ((unused)) PVOID myArg, | |
220 | __attribute__ ((unused)) BOOLEAN timeout) { | |
221 | SuspendThread(MainThread); | |
222 | if (SIGVTAM_handler == SIG_IGN) { | |
223 | /* noop */ | |
224 | } else if (SIGVTAM_handler == SIG_DFL) { | |
225 | die("vtalarm"); | |
226 | } else { | |
227 | (*SIGVTAM_handler)(SIGVTALRM); | |
228 | } | |
229 | ResumeThread(MainThread); | |
230 | } | |
231 | static VOID CALLBACK MLton_SIGPROF(__attribute__ ((unused)) PVOID myArg, | |
232 | __attribute__ ((unused)) BOOLEAN timeout) { | |
233 | SuspendThread(MainThread); | |
234 | if (SIGPROF_handler == SIG_IGN) { | |
235 | /* noop */ | |
236 | } else if (SIGPROF_handler == SIG_DFL) { | |
237 | die("sigprof"); | |
238 | } else { | |
239 | (*SIGPROF_handler)(SIGPROF); | |
240 | } | |
241 | ResumeThread(MainThread); | |
242 | } | |
243 | ||
244 | static void CALLBACK fixPriority(__attribute__ ((unused)) PVOID myArg, | |
245 | __attribute__ ((unused)) BOOLEAN timeout) { | |
246 | SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL); | |
247 | DeleteTimerQueueTimer(TimerQueue, PrioTimer, NULL); | |
248 | } | |
249 | ||
250 | static int MLTimer(HANDLE *timer, | |
251 | const struct itimerval *value, | |
252 | WAITORTIMERCALLBACK callback) { | |
253 | DWORD DueTime, Period; | |
254 | ||
255 | /* Initialize the TimerQueue */ | |
256 | if (MainThread == 0) { | |
257 | /* This call improves the resolution of the scheduler from | |
258 | * 16ms to about 2ms in my testing. Sadly it requires winmm. | |
259 | */ | |
260 | timeBeginPeriod(1); | |
261 | ||
262 | TimerQueue = CreateTimerQueue(); | |
263 | if (TimerQueue == NULL) { errno = ENOMEM; return -1; } | |
264 | ||
265 | /* We need to get the TimerQueue to have higher priority. | |
266 | * From my testing, if it has the same priority as the main | |
267 | * thread and the main thread is busy, your best resolution | |
268 | * is a terribly slow 188ms. By boosting the priority of the | |
269 | * timer thread to ABOVE_NORMAL, I've gotten down to 2ms. | |
270 | */ | |
271 | CreateTimerQueueTimer(&PrioTimer, TimerQueue, fixPriority, | |
272 | 0, 1, 0, WT_EXECUTEINTIMERTHREAD); | |
273 | ||
274 | /* We need a handle to the main thread usable by the timer | |
275 | * thread. GetCurrentThread() is a self-reference so we need | |
276 | * to copy it to a new handle for it to work in other threads. | |
277 | */ | |
278 | DuplicateHandle(GetCurrentProcess(), /* source process */ | |
279 | GetCurrentThread(), /* source handle */ | |
280 | GetCurrentProcess(), /* target process */ | |
281 | &MainThread, /* target handle */ | |
282 | 0, /* access (ignored) */ | |
283 | FALSE, /* not inheritable */ | |
284 | DUPLICATE_SAME_ACCESS); | |
285 | ||
286 | if (MainThread == 0) die("Cannot get handle to initial thread"); | |
287 | } | |
288 | ||
289 | /* Windows uses ms accuracy for TimerQueues */ | |
290 | DueTime = value->it_value.tv_sec * 1000 | |
291 | + (value->it_value.tv_usec + 999) / 1000; | |
292 | Period = value->it_interval.tv_sec * 1000 | |
293 | + (value->it_interval.tv_usec + 999) / 1000; | |
294 | ||
295 | if (timer != NULL) { | |
296 | DeleteTimerQueueTimer(TimerQueue, *timer, NULL); | |
297 | *timer = NULL; | |
298 | } | |
299 | ||
300 | if (DueTime == 0) { | |
301 | return 0; | |
302 | } | |
303 | ||
304 | if (!CreateTimerQueueTimer( | |
305 | timer, /* output: created timer */ | |
306 | TimerQueue, /* The queue which holds the timers */ | |
307 | callback, /* Invoked on timer events */ | |
308 | 0, /* myArg for the callback */ | |
309 | DueTime, /* Must be non-zero => time till first event */ | |
310 | Period, /* Time till the event repeats (forever) */ | |
311 | WT_EXECUTEINTIMERTHREAD)) { /* Don't use a thread pool */ | |
312 | errno = ENOMEM; | |
313 | return -1; | |
314 | } | |
315 | ||
316 | return 0; | |
317 | } | |
318 | ||
319 | int setitimer (int which, | |
320 | const struct itimerval *value, | |
321 | struct itimerval *ovalue) { | |
322 | if (ovalue != 0) die("setitimer doesn't support retrieving old state"); | |
323 | ||
324 | switch (which) { | |
325 | case ITIMER_REAL: return MLTimer(&RealTimer, value, &MLton_SIGALRM); | |
326 | case ITIMER_VIRT: return MLTimer(&VirtTimer, value, &MLton_SIGVTAM); | |
327 | case ITIMER_PROF: return MLTimer(&ProfTimer, value, &MLton_SIGPROF); | |
328 | default: errno = EINVAL; return -1; | |
329 | } | |
330 | ||
331 | } | |
332 | ||
333 | static void catcher(__attribute__ ((unused)) int signo) { | |
334 | CONTEXT context; | |
335 | context.ContextFlags = CONTEXT_CONTROL; | |
336 | ||
337 | GetThreadContext(MainThread, &context); | |
338 | #if defined(__i386__) | |
339 | GC_handleSigProf((code_pointer) context.Eip); | |
340 | #elif defined(__x86_64__) | |
341 | GC_handleSigProf((code_pointer) context.Rip); | |
342 | #elif defined(_PPC_) | |
343 | GC_handleSigProf((code_pointer) context.Iar); | |
344 | #elif defined(_ALPHA_) | |
345 | GC_handleSigProf((code_pointer) context.Fir); | |
346 | #elif defined(MIPS) | |
347 | GC_handleSigProf((code_pointer) context.Fir); | |
348 | #elif defined(ARM) | |
349 | GC_handleSigProf((code_pointer) context.Pc); | |
350 | #else | |
351 | #error Profiling handler is missing for this architecture | |
352 | #endif | |
353 | } | |
354 | ||
355 | void GC_setSigProfHandler (struct sigaction *sa) { | |
356 | sa->sa_flags = 0; | |
357 | sa->sa_handler = (_sig_func_ptr)&catcher; | |
358 | } | |
359 | ||
360 | /* ------------------------------------------------- */ | |
361 | /* MLton.Rlimit */ | |
362 | /* ------------------------------------------------- */ | |
363 | ||
364 | static struct rlimit rlimits[RLIM_NLIMITS]; | |
365 | ||
366 | static void initRlimits (void) { | |
367 | static int done = FALSE; | |
368 | int lim; | |
369 | ||
370 | if (done) | |
371 | return; | |
372 | done = TRUE; | |
373 | for (lim = 0; lim < RLIM_NLIMITS; ++lim ) { | |
374 | rlimits[lim].rlim_cur = 0; | |
375 | rlimits[lim].rlim_max = UINT_MAX; | |
376 | } | |
377 | } | |
378 | ||
379 | int getrlimit (int resource, struct rlimit *rlp) { | |
380 | initRlimits (); | |
381 | if (resource < 0 or resource >= RLIM_NLIMITS) { | |
382 | errno = EINVAL; | |
383 | return -1; | |
384 | } | |
385 | *rlp = rlimits[resource]; | |
386 | return 0; | |
387 | } | |
388 | ||
389 | int setrlimit (int resource, const struct rlimit *rlp) { | |
390 | initRlimits (); | |
391 | if (resource < 0 or resource >= RLIM_NLIMITS) { | |
392 | errno = EINVAL; | |
393 | return -1; | |
394 | } | |
395 | if (rlp->rlim_cur < rlimits[resource].rlim_max) | |
396 | rlimits[resource].rlim_cur = rlp->rlim_cur; | |
397 | else { | |
398 | errno = EPERM; | |
399 | return -1; | |
400 | } | |
401 | rlimits[resource].rlim_max = rlp->rlim_max; | |
402 | return 0; | |
403 | } | |
404 | ||
405 | /* ------------------------------------------------- */ | |
406 | /* MLton.Rusage */ | |
407 | /* ------------------------------------------------- */ | |
408 | ||
409 | /* GetProcessTimes and GetSystemTimeAsFileTime are documented at: | |
410 | * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/getprocesstimes.asp | |
411 | */ | |
412 | int getrusage (int who, struct rusage *usage) { | |
413 | /* FILETIME has dw{High,Low}DateTime which store the number of | |
414 | * 100-nanoseconds since January 1, 1601 | |
415 | */ | |
416 | FILETIME creation_time; | |
417 | FILETIME exit_time; | |
418 | FILETIME kernel_time; | |
419 | FILETIME user_time; | |
420 | ||
421 | uint64_t user_usecs, kernel_usecs; | |
422 | ||
423 | if (who == RUSAGE_CHILDREN) { | |
424 | // !!! could use exit_time - creation_time from cwait | |
425 | memset(usage, 0, sizeof(struct rusage)); | |
426 | return 0; | |
427 | } | |
428 | ||
429 | if (who != RUSAGE_SELF) { | |
430 | errno = EINVAL; | |
431 | return -1; | |
432 | } | |
433 | ||
434 | if (GetProcessTimes(GetCurrentProcess(), | |
435 | &creation_time, &exit_time, | |
436 | &kernel_time, &user_time) == 0) { | |
437 | errno = EFAULT; | |
438 | return -1; | |
439 | } | |
440 | ||
441 | kernel_usecs = kernel_time.dwHighDateTime; | |
442 | kernel_usecs <<= sizeof(kernel_time.dwHighDateTime)*8; | |
443 | kernel_usecs |= kernel_time.dwLowDateTime; | |
444 | kernel_usecs /= 10; | |
445 | ||
446 | user_usecs = user_time.dwHighDateTime; | |
447 | user_usecs <<= sizeof(user_time.dwHighDateTime)*8; | |
448 | user_usecs |= user_time.dwLowDateTime; | |
449 | user_usecs /= 10; | |
450 | ||
451 | usage->ru_utime.tv_sec = user_usecs / 1000000; | |
452 | usage->ru_utime.tv_usec = user_usecs % 1000000; | |
453 | usage->ru_stime.tv_sec = kernel_usecs / 1000000; | |
454 | usage->ru_stime.tv_usec = kernel_usecs % 1000000; | |
455 | return 0; | |
456 | } | |
457 | ||
458 | /* ------------------------------------------------- */ | |
459 | /* OS.IO */ | |
460 | /* ------------------------------------------------- */ | |
461 | ||
462 | __attribute__ ((noreturn)) | |
463 | int poll (__attribute__ ((unused)) struct pollfd *ufds, | |
464 | __attribute__ ((unused)) unsigned int nfds, | |
465 | __attribute__ ((unused)) int timeout) { | |
466 | die ("poll not implemented"); | |
467 | } | |
468 | ||
469 | /* ------------------------------------------------- */ | |
470 | /* Posix.FileSys */ | |
471 | /* ------------------------------------------------- */ | |
472 | ||
473 | static void GetWin32FileName (int fd, char* fname) { | |
474 | HANDLE fh, fhmap; | |
475 | DWORD fileSize, fileSizeHi; | |
476 | void* pMem = NULL; | |
477 | intptr_t tmp; | |
478 | ||
479 | tmp = _get_osfhandle (fd); | |
480 | fh = (HANDLE)tmp; | |
481 | fileSize = GetFileSize (fh, &fileSizeHi); | |
482 | fhmap = CreateFileMapping (fh, NULL, PAGE_READONLY, 0, fileSize, NULL); | |
483 | if (fhmap) { | |
484 | pMem = MapViewOfFile (fhmap, FILE_MAP_READ, 0, 0, 1); | |
485 | if (pMem) { | |
486 | GetMappedFileNameA (GetCurrentProcess(), | |
487 | pMem, fname, MAX_PATH); | |
488 | UnmapViewOfFile (pMem); | |
489 | } | |
490 | CloseHandle (fhmap); | |
491 | } | |
492 | } | |
493 | ||
494 | int fchmod (int filedes, mode_t mode) { | |
495 | char fname[MAX_PATH + 1]; | |
496 | ||
497 | GetWin32FileName (filedes, fname); | |
498 | return _chmod (fname, mode); | |
499 | } | |
500 | ||
501 | int fchdir (int filedes) { | |
502 | char fname[MAX_PATH + 1]; | |
503 | ||
504 | GetWin32FileName (filedes, fname); | |
505 | return chdir (fname); | |
506 | } | |
507 | ||
508 | __attribute__ ((noreturn)) | |
509 | int chown (__attribute__ ((unused)) const char *path, | |
510 | __attribute__ ((unused)) uid_t owner, | |
511 | __attribute__ ((unused)) gid_t group) { | |
512 | die ("chown not implemented"); | |
513 | } | |
514 | ||
515 | __attribute__ ((noreturn)) | |
516 | int fchown (__attribute__ ((unused)) int fd, | |
517 | __attribute__ ((unused)) uid_t owner, | |
518 | __attribute__ ((unused)) gid_t group) { | |
519 | die ("fchown not implemented"); | |
520 | } | |
521 | ||
522 | __attribute__ ((noreturn)) | |
523 | long fpathconf (__attribute__ ((unused)) int filedes, | |
524 | __attribute__ ((unused)) int name) { | |
525 | die ("fpathconf not implemented"); | |
526 | } | |
527 | ||
528 | __attribute__ ((noreturn)) | |
529 | int link (__attribute__ ((unused)) const char *oldpath, | |
530 | __attribute__ ((unused)) const char *newpath) { | |
531 | die ("link not implemented"); | |
532 | } | |
533 | ||
534 | int lstat (const char *file_name, struct stat *buf) { | |
535 | /* Win32 doesn't really have links. */ | |
536 | return stat (file_name, buf); | |
537 | } | |
538 | ||
539 | __attribute__ ((noreturn)) | |
540 | int mkfifo (__attribute__ ((unused)) const char *pathname, | |
541 | __attribute__ ((unused)) mode_t mode) { | |
542 | die ("mkfifo not implemented"); | |
543 | } | |
544 | ||
545 | __attribute__ ((noreturn)) | |
546 | long pathconf (__attribute__ ((unused)) const char *path, | |
547 | __attribute__ ((unused)) int name) { | |
548 | die ("pathconf not implemented"); | |
549 | } | |
550 | ||
551 | __attribute__ ((noreturn)) | |
552 | int readlink (__attribute__ ((unused)) const char *path, | |
553 | __attribute__ ((unused)) char *buf, | |
554 | __attribute__ ((unused)) size_t bufsiz) { | |
555 | die ("readlink not implemented"); | |
556 | } | |
557 | ||
558 | __attribute__ ((noreturn)) | |
559 | int symlink (__attribute__ ((unused)) const char *oldpath, | |
560 | __attribute__ ((unused)) const char *newpath) { | |
561 | die ("symlink not implemented"); | |
562 | } | |
563 | ||
564 | int truncate (const char *path, off_t len) { | |
565 | int fd; | |
566 | ||
567 | if ((fd = open(path, O_RDWR)) == -1) | |
568 | return -1; | |
569 | if (ftruncate(fd, len) < 0) { | |
570 | close(fd); | |
571 | return -1; | |
572 | } | |
573 | close(fd); | |
574 | return 0; | |
575 | } | |
576 | ||
577 | ||
578 | /* ------------------------------------------------- */ | |
579 | /* Posix.IO */ | |
580 | /* ------------------------------------------------- */ | |
581 | ||
582 | __attribute__ ((noreturn)) | |
583 | int fcntl (__attribute__ ((unused)) int fd, | |
584 | __attribute__ ((unused)) int cmd, | |
585 | ...) { | |
586 | die ("fcntl not implemented"); | |
587 | } | |
588 | ||
589 | int fsync (int fd) { | |
590 | return _commit (fd); | |
591 | } | |
592 | ||
593 | int pipe (int filedes[2]) { | |
594 | HANDLE read_h; | |
595 | HANDLE write_h; | |
596 | ||
597 | /* We pass no security attributes (0), so the current policy gets | |
598 | * inherited. The pipe is set to NOT stay open in child processes. | |
599 | * This will be corrected using DuplicateHandle in create() | |
600 | * The 4k buffersize is choosen b/c that's what linux uses. | |
601 | */ | |
602 | if (!CreatePipe(&read_h, &write_h, 0, 4096)) { | |
603 | errno = ENOMEM; /* fake errno: out of resources */ | |
604 | return -1; | |
605 | } | |
606 | /* This requires Win98+ | |
607 | * Choosing text/binary mode is defered till a later setbin/text call | |
608 | */ | |
609 | filedes[0] = _open_osfhandle((intptr_t)read_h, _O_RDONLY); | |
610 | filedes[1] = _open_osfhandle((intptr_t)write_h, _O_WRONLY); | |
611 | if (filedes[0] == -1 or filedes[1] == -1) { | |
612 | if (filedes[0] == -1) | |
613 | CloseHandle(read_h); | |
614 | else close(filedes[0]); | |
615 | if (filedes[1] == -1) | |
616 | CloseHandle(write_h); | |
617 | else close(filedes[1]); | |
618 | ||
619 | errno = ENFILE; | |
620 | return -1; | |
621 | } | |
622 | return 0; | |
623 | } | |
624 | ||
625 | /* ------------------------------------------------- */ | |
626 | /* Posix.ProcEnv */ | |
627 | /* ------------------------------------------------- */ | |
628 | ||
629 | __attribute__ ((noreturn)) | |
630 | char *ctermid (__attribute__ ((unused)) char* s) { | |
631 | die ("*ctermid not implemented"); | |
632 | } | |
633 | ||
634 | __attribute__ ((noreturn)) | |
635 | gid_t getegid (void) { | |
636 | die ("getegid not implemented"); | |
637 | } | |
638 | ||
639 | __attribute__ ((noreturn)) | |
640 | uid_t geteuid (void) { | |
641 | die ("geteuid not implemented"); | |
642 | } | |
643 | ||
644 | __attribute__ ((noreturn)) | |
645 | gid_t getgid (void) { | |
646 | die ("getgid not implemented"); | |
647 | } | |
648 | ||
649 | __attribute__ ((noreturn)) | |
650 | int getgroups (__attribute__ ((unused)) int size, | |
651 | __attribute__ ((unused)) gid_t list[]) { | |
652 | die ("getgroups not implemented"); | |
653 | } | |
654 | ||
655 | __attribute__ ((noreturn)) | |
656 | char *getlogin (void) { | |
657 | die ("getlogin not implemented"); | |
658 | } | |
659 | ||
660 | __attribute__ ((noreturn)) | |
661 | pid_t getpgid(__attribute__ ((unused)) pid_t pid) { | |
662 | die ("getpgid not implemented"); | |
663 | } | |
664 | ||
665 | __attribute__ ((noreturn)) | |
666 | pid_t getpgrp(void) { | |
667 | die ("getpgrp not implemented"); | |
668 | } | |
669 | ||
670 | __attribute__ ((noreturn)) | |
671 | pid_t getppid (void) { | |
672 | die ("getppid not implemented"); | |
673 | } | |
674 | ||
675 | __attribute__ ((noreturn)) | |
676 | uid_t getuid (void) { | |
677 | die ("getuid not implemented"); | |
678 | } | |
679 | ||
680 | int setenv (const char *name, const char *value, int overwrite) { | |
681 | /* We could use _putenv, but then we'd need a temporary buffer for | |
682 | * use to concat name=value. | |
683 | */ | |
684 | if (not overwrite and getenv (name)) { | |
685 | errno = EEXIST; | |
686 | return -1; /* previous mingw setenv was buggy and returned 0 */ | |
687 | } | |
688 | ||
689 | if (SetEnvironmentVariable (name, value)) { | |
690 | errno = ENOMEM; /* this happens often in Windows.. */ | |
691 | return -1; | |
692 | } | |
693 | ||
694 | return 0; | |
695 | } | |
696 | ||
697 | __attribute__ ((noreturn)) | |
698 | int setgid (__attribute__ ((unused)) gid_t gid) { | |
699 | die ("setgid not implemented"); | |
700 | } | |
701 | ||
702 | __attribute__ ((noreturn)) | |
703 | int setgroups (__attribute__ ((unused)) size_t size, | |
704 | __attribute__ ((unused)) const gid_t *list) { | |
705 | die ("setgroups not implemented"); | |
706 | } | |
707 | ||
708 | __attribute__ ((noreturn)) | |
709 | int setpgid (__attribute__ ((unused)) pid_t pid, | |
710 | __attribute__ ((unused)) pid_t pgid) { | |
711 | die ("setpgid not implemented"); | |
712 | } | |
713 | ||
714 | __attribute__ ((noreturn)) | |
715 | pid_t setsid (void) { | |
716 | die ("setsid not implemented"); | |
717 | } | |
718 | ||
719 | __attribute__ ((noreturn)) | |
720 | int setuid (__attribute__ ((unused)) uid_t uid) { | |
721 | die ("setuid not implemented"); | |
722 | } | |
723 | ||
724 | __attribute__ ((noreturn)) | |
725 | long sysconf (__attribute__ ((unused)) int name) { | |
726 | die ("sysconf not implemented"); | |
727 | } | |
728 | ||
729 | __attribute__ ((noreturn)) | |
730 | clock_t times (__attribute__ ((unused)) struct tms *buf) { | |
731 | die ("times not implemented"); | |
732 | } | |
733 | ||
734 | __attribute__ ((noreturn)) | |
735 | char *ttyname (__attribute__ ((unused)) int desc) { | |
736 | die ("*ttyname not implemented"); | |
737 | } | |
738 | ||
739 | static void setMachine (struct utsname *buf) { | |
740 | int level; | |
741 | const char* platform = "unknown"; | |
742 | SYSTEM_INFO si; | |
743 | ||
744 | GetSystemInfo (&si); | |
745 | level = si.dwProcessorType; | |
746 | switch (si.wProcessorArchitecture) { | |
747 | case PROCESSOR_ARCHITECTURE_INTEL: | |
748 | if (level < 3) level = 3; | |
749 | if (level > 6) level = 6; | |
750 | platform = "i%d86"; | |
751 | break; | |
752 | case PROCESSOR_ARCHITECTURE_IA64: platform = "ia64"; break; | |
753 | case PROCESSOR_ARCHITECTURE_AMD64: platform = "amd64"; break; | |
754 | case PROCESSOR_ARCHITECTURE_PPC: platform = "ppc"; break; | |
755 | case PROCESSOR_ARCHITECTURE_ALPHA: platform = "alpha"; break; | |
756 | case PROCESSOR_ARCHITECTURE_MIPS: platform = "mips"; break; | |
757 | case PROCESSOR_ARCHITECTURE_ARM: platform = "arm"; break; | |
758 | case PROCESSOR_ARCHITECTURE_ALPHA64: platform = "alpha64"; break; | |
759 | /* SHX? MSIL? IA32_ON_WIN64? */ | |
760 | default: platform = "unknown"; break; | |
761 | } | |
762 | sprintf (buf->machine, platform, level); | |
763 | } | |
764 | ||
765 | static void setSysname (struct utsname *buf) { | |
766 | OSVERSIONINFOEX osv; | |
767 | const char* os = "??"; | |
768 | ||
769 | #ifndef _WIN64 | |
770 | /* Call GetNativeSystemInfo if supported or GetSystemInfo otherwise. */ | |
771 | SYSTEM_INFO si; | |
772 | void (WINAPI *pGNSI)(LPSYSTEM_INFO); | |
773 | pGNSI = (void(WINAPI *)(LPSYSTEM_INFO)) | |
774 | GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), | |
775 | "GetNativeSystemInfo"); | |
776 | if (NULL != pGNSI) | |
777 | pGNSI(&si); | |
778 | else | |
779 | GetSystemInfo(&si); | |
780 | #endif | |
781 | ||
782 | osv.dwOSVersionInfoSize = sizeof (osv); | |
783 | /* Try to get extended information in order to be able to match the O.S. more | |
784 | precisely using osv.wProductType */ | |
785 | if (! GetVersionEx ((OSVERSIONINFO *)(void*) &osv)) { | |
786 | ZeroMemory(&osv, sizeof(OSVERSIONINFOEX)); | |
787 | osv.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); | |
788 | GetVersionEx((OSVERSIONINFO *)(void*) &osv); | |
789 | } | |
790 | switch (osv.dwPlatformId) { | |
791 | case VER_PLATFORM_WIN32_NT: | |
792 | #ifdef _WIN64 | |
793 | if (osv.dwMinorVersion == 0) { | |
794 | if (osv.dwMajorVersion <= 6) { | |
795 | if (osv.wProductType == VER_NT_WORKSTATION) | |
796 | os = "Vista_64"; | |
797 | else | |
798 | os = "2008_64"; | |
799 | } else os = "NTx_64"; | |
800 | } else if (osv.dwMinorVersion <= 2) os = "XP_64"; | |
801 | else os = "NTx_64"; | |
802 | #else | |
803 | if (osv.dwMinorVersion == 0) { | |
804 | if (osv.dwMajorVersion <= 4) os = "NT"; | |
805 | else if (osv.dwMajorVersion <= 5) os = "2000"; | |
806 | else { | |
807 | if (osv.wProductType == VER_NT_WORKSTATION) { | |
808 | if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) | |
809 | os = "Vista_WOW64"; | |
810 | else | |
811 | os = "Vista"; | |
812 | } else { | |
813 | if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) | |
814 | os = "2008"; | |
815 | else | |
816 | os = "2008_WOW64"; | |
817 | } | |
818 | } | |
819 | } else if (osv.dwMinorVersion <= 1) os = "XP"; | |
820 | else if (osv.dwMinorVersion <= 2) { | |
821 | if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) | |
822 | os = "XP_WOW64"; | |
823 | else | |
824 | os = "2003"; | |
825 | } else os = "NTx"; | |
826 | #endif | |
827 | break; | |
828 | case VER_PLATFORM_WIN32_WINDOWS: | |
829 | if (osv.dwMinorVersion == 0) os = "95"; | |
830 | else if (osv.dwMinorVersion < 90) os = "98"; | |
831 | else if (osv.dwMinorVersion == 90) os = "Me"; | |
832 | else os = "9X"; | |
833 | break; | |
834 | case VER_PLATFORM_WIN32s: | |
835 | os = "31"; /* aka DOS + Windows 3.1 */ | |
836 | break; | |
837 | default: | |
838 | os = "unknown"; | |
839 | break; | |
840 | } | |
841 | #ifdef _WIN64 | |
842 | #define mingw_name "MINGW64" | |
843 | #else | |
844 | #define mingw_name "MINGW32" | |
845 | #endif | |
846 | sprintf (buf->sysname, "%s_%s-%d.%d", mingw_name, | |
847 | os, (int)osv.dwMajorVersion, (int)osv.dwMinorVersion); | |
848 | } | |
849 | ||
850 | int uname (struct utsname *buf) { | |
851 | MLton_initSockets(); /* needed for gethostname */ | |
852 | setMachine (buf); | |
853 | setSysname (buf); | |
854 | unless (0 == gethostname (buf->nodename, sizeof (buf->nodename))) { | |
855 | strcpy (buf->nodename, "unknown"); | |
856 | } | |
857 | #ifdef _WIN64 | |
858 | sprintf (buf->release, "%d", __MINGW64_VERSION_MINOR); | |
859 | sprintf (buf->version, "%d", __MINGW64_VERSION_MAJOR); | |
860 | #else | |
861 | sprintf (buf->release, "%d", __MINGW32_MINOR_VERSION); | |
862 | sprintf (buf->version, "%d", __MINGW32_MAJOR_VERSION); | |
863 | #endif | |
864 | return 0; | |
865 | } | |
866 | ||
867 | /* ------------------------------------------------- */ | |
868 | /* Posix.Process */ | |
869 | /* ------------------------------------------------- */ | |
870 | ||
871 | int alarm (int secs) { | |
872 | struct itimerval new; | |
873 | new.it_interval.tv_usec = 0; | |
874 | new.it_interval.tv_sec = 0; | |
875 | new.it_value.tv_usec = 0; | |
876 | new.it_value.tv_sec = secs; | |
877 | return setitimer(ITIMER_REAL, &new, 0); | |
878 | } | |
879 | ||
880 | __attribute__ ((noreturn)) | |
881 | int fork (void) { | |
882 | die ("fork not implemented"); | |
883 | } | |
884 | ||
885 | int kill (pid_t pid, int sig) { | |
886 | HANDLE h = (HANDLE)pid; | |
887 | unless (TerminateProcess (h, SIGNALLED_BIT | sig)) { | |
888 | errno = ECHILD; | |
889 | return -1; | |
890 | } | |
891 | return 0; | |
892 | } | |
893 | ||
894 | int nanosleep (const struct timespec *req, struct timespec *rem) { | |
895 | Sleep (req->tv_sec * 1000 + (req->tv_nsec + 999999) / 1000000); | |
896 | rem->tv_nsec = 0; | |
897 | rem->tv_sec = 0; | |
898 | return 0; | |
899 | } | |
900 | ||
901 | __attribute__ ((noreturn)) | |
902 | int pause (void) { | |
903 | die ("pause not implemented"); | |
904 | } | |
905 | ||
906 | unsigned int sleep (unsigned int seconds) { | |
907 | Sleep (seconds * 1000); | |
908 | return 0; | |
909 | } | |
910 | ||
911 | __attribute__ ((noreturn)) | |
912 | pid_t wait (__attribute__ ((unused)) int *status) { | |
913 | die ("wait not implemented"); | |
914 | } | |
915 | ||
916 | pid_t waitpid (pid_t pid, int *status, int options) { | |
917 | HANDLE h; | |
918 | DWORD delay; | |
919 | ||
920 | /* pid <= 0 is handled in stub-mingw.sml */ | |
921 | h = (HANDLE)pid; | |
922 | ||
923 | delay = ((options & WNOHANG) != 0) ? 0 : INFINITE; | |
924 | ||
925 | switch (WaitForSingleObject (h, delay)) { | |
926 | case WAIT_OBJECT_0: /* process has exited */ | |
927 | break; | |
928 | case WAIT_TIMEOUT: /* process has not exited */ | |
929 | return 0; | |
930 | default: /* some sort of error */ | |
931 | errno = ECHILD; | |
932 | return -1; | |
933 | } | |
934 | ||
935 | unless (GetExitCodeProcess (h, (DWORD*)status)) { | |
936 | errno = ECHILD; | |
937 | return -1; | |
938 | } | |
939 | ||
940 | return pid; | |
941 | } | |
942 | ||
943 | /* ------------------------------------------------- */ | |
944 | /* Signals */ | |
945 | /* ------------------------------------------------- */ | |
946 | ||
947 | int sigaction (int signum, | |
948 | const struct sigaction *newact, | |
949 | struct sigaction *oldact) { | |
950 | _sig_func_ptr old; | |
951 | ||
952 | if (signum < 0 or signum >= NSIG) { | |
953 | errno = EINVAL; | |
954 | return -1; | |
955 | } | |
956 | ||
957 | switch (signum) { | |
958 | case SIGKILL: | |
959 | case SIGSTOP: | |
960 | errno = EINVAL; | |
961 | return -1; | |
962 | case SIGALRM: | |
963 | old = SIGALRM_handler; | |
964 | if (newact) SIGALRM_handler = newact->sa_handler; | |
965 | break; | |
966 | case SIGVTALRM: | |
967 | old = SIGVTAM_handler; | |
968 | if (newact) SIGVTAM_handler = newact->sa_handler; | |
969 | break; | |
970 | case SIGPROF: | |
971 | old = SIGPROF_handler; | |
972 | if (newact) SIGPROF_handler = newact->sa_handler; | |
973 | break; | |
974 | default: | |
975 | old = signal (signum, newact?newact->sa_handler:0); | |
976 | if (!newact) signal (signum, old); | |
977 | break; | |
978 | } | |
979 | ||
980 | if (oldact) | |
981 | oldact->sa_handler = old; | |
982 | return 0; | |
983 | } | |
984 | ||
985 | int sigaddset (sigset_t *set, const int signum) { | |
986 | if (signum < 0 or signum >= NSIG) { | |
987 | errno = EINVAL; | |
988 | return -1; | |
989 | } | |
990 | *set |= SIGTOMASK (signum); | |
991 | return 0; | |
992 | } | |
993 | ||
994 | int sigdelset (sigset_t *set, const int signum) { | |
995 | if (signum < 0 or signum >= NSIG) { | |
996 | errno = EINVAL; | |
997 | return -1; | |
998 | } | |
999 | *set &= ~SIGTOMASK (signum); | |
1000 | return 0; | |
1001 | } | |
1002 | ||
1003 | int sigemptyset (sigset_t *set) { | |
1004 | *set = (sigset_t) 0; | |
1005 | return 0; | |
1006 | } | |
1007 | ||
1008 | int sigfillset (sigset_t *set) { | |
1009 | *set = ~((sigset_t) 0); | |
1010 | return 0; | |
1011 | } | |
1012 | ||
1013 | int sigismember (const sigset_t *set, const int signum) { | |
1014 | if (signum < 0 or signum >= NSIG) { | |
1015 | errno = EINVAL; | |
1016 | return -1; | |
1017 | } | |
1018 | return (*set & SIGTOMASK(signum)) ? 1 : 0; | |
1019 | } | |
1020 | ||
1021 | ||
1022 | /* With a bit of work and a redirected signal() function, we could | |
1023 | * probably emulate these methods properly. AtM blocking is a lie. | |
1024 | */ | |
1025 | static sigset_t signals_blocked = 0; | |
1026 | static sigset_t signals_pending = 0; | |
1027 | ||
1028 | int sigpending (sigset_t *set) { | |
1029 | *set = signals_pending; | |
1030 | return 0; | |
1031 | } | |
1032 | ||
1033 | int sigprocmask (int how, const sigset_t *set, sigset_t *oldset) { | |
1034 | if (oldset) { | |
1035 | *oldset = signals_blocked; | |
1036 | } | |
1037 | if (set) { | |
1038 | sigset_t newmask = signals_blocked; | |
1039 | ||
1040 | switch (how) { | |
1041 | case SIG_BLOCK: | |
1042 | /* add set to current mask */ | |
1043 | newmask |= *set; | |
1044 | break; | |
1045 | case SIG_UNBLOCK: | |
1046 | /* remove set from current mask */ | |
1047 | newmask &= ~*set; | |
1048 | break; | |
1049 | case SIG_SETMASK: | |
1050 | /* just set it */ | |
1051 | newmask = *set; | |
1052 | break; | |
1053 | default: | |
1054 | return -1; | |
1055 | } | |
1056 | ||
1057 | signals_blocked = newmask; | |
1058 | } | |
1059 | return 0; | |
1060 | } | |
1061 | ||
1062 | __attribute__ ((noreturn)) | |
1063 | int sigsuspend (__attribute__ ((unused)) const sigset_t *mask) { | |
1064 | die("sigsuspend is unimplemented, but could be hacked in if needed"); | |
1065 | } | |
1066 | ||
1067 | /* ------------------------------------------------- */ | |
1068 | /* Posix.IO */ | |
1069 | /* ------------------------------------------------- */ | |
1070 | ||
1071 | void Posix_IO_setbin (C_Fd_t fd) { | |
1072 | _setmode (fd, _O_BINARY); | |
1073 | } | |
1074 | ||
1075 | void Posix_IO_settext (C_Fd_t fd) { | |
1076 | _setmode (fd, _O_TEXT); | |
1077 | } | |
1078 | ||
1079 | /* ------------------------------------------------- */ | |
1080 | /* Posix.SysDB.Passwd */ | |
1081 | /* ------------------------------------------------- */ | |
1082 | ||
1083 | #define INFO_LEVEL 3 | |
1084 | static LPUSER_INFO_3 usrData = NULL; | |
1085 | ||
1086 | static struct passwd passwd; | |
1087 | ||
1088 | __attribute__ ((noreturn)) | |
1089 | struct group *getgrgid (__attribute__ ((unused)) gid_t gid) { | |
1090 | die ("getgrgid not implemented"); | |
1091 | } | |
1092 | ||
1093 | __attribute__ ((noreturn)) | |
1094 | struct group *getgrnam (__attribute__ ((unused)) const char *name) { | |
1095 | die ("getgrnam not implemented"); | |
1096 | } | |
1097 | ||
1098 | struct passwd *getpwnam (__attribute__ ((unused)) const char *name) { | |
1099 | return NULL; | |
1100 | // unless (NERR_Success == | |
1101 | // NetUserGetInfo (NULL, (LPCWSTR)name, INFO_LEVEL, | |
1102 | // (LPBYTE*)&usrData)) | |
1103 | // return NULL; | |
1104 | passwd.pw_dir = (char*)usrData->usri3_home_dir; | |
1105 | passwd.pw_gid = usrData->usri3_primary_group_id; | |
1106 | passwd.pw_name = (char*)usrData->usri3_name; | |
1107 | passwd.pw_shell = (char*)usrData->usri3_script_path; | |
1108 | passwd.pw_uid = usrData->usri3_user_id; | |
1109 | return &passwd; | |
1110 | } | |
1111 | ||
1112 | __attribute__ ((noreturn)) | |
1113 | struct passwd *getpwuid (__attribute__ ((unused)) uid_t uid) { | |
1114 | die ("getpwuid not implemented"); | |
1115 | } | |
1116 | ||
1117 | /* ------------------------------------------------- */ | |
1118 | /* Posix.TTY */ | |
1119 | /* ------------------------------------------------- */ | |
1120 | ||
1121 | __attribute__ ((noreturn)) | |
1122 | speed_t cfgetispeed (__attribute__ ((unused)) struct termios *termios_p) { | |
1123 | die ("cfgetispeed not implemented"); | |
1124 | } | |
1125 | ||
1126 | __attribute__ ((noreturn)) | |
1127 | speed_t cfgetospeed (__attribute__ ((unused)) struct termios *termios_p) { | |
1128 | die ("cfgetospeed not implemented"); | |
1129 | } | |
1130 | ||
1131 | __attribute__ ((noreturn)) | |
1132 | int cfsetispeed (__attribute__ ((unused)) struct termios *termios_p, | |
1133 | __attribute__ ((unused)) speed_t speed) { | |
1134 | die ("cfsetispeed not implemented"); | |
1135 | } | |
1136 | ||
1137 | __attribute__ ((noreturn)) | |
1138 | int cfsetospeed (__attribute__ ((unused)) struct termios *termios_p, | |
1139 | __attribute__ ((unused)) speed_t speed) { | |
1140 | die ("cfsetospeed not implemented"); | |
1141 | } | |
1142 | ||
1143 | __attribute__ ((noreturn)) | |
1144 | int tcdrain (__attribute__ ((unused)) int fd) { | |
1145 | die ("tcdrain not implemented"); | |
1146 | } | |
1147 | ||
1148 | __attribute__ ((noreturn)) | |
1149 | int tcflow (__attribute__ ((unused)) int fd, | |
1150 | __attribute__ ((unused)) int action) { | |
1151 | die ("tcflow not implemented"); | |
1152 | } | |
1153 | ||
1154 | __attribute__ ((noreturn)) | |
1155 | int tcflush (__attribute__ ((unused)) int fd, | |
1156 | __attribute__ ((unused)) int queue_selector) { | |
1157 | die ("tcflush not implemented"); | |
1158 | } | |
1159 | ||
1160 | __attribute__ ((noreturn)) | |
1161 | int tcgetattr (__attribute__ ((unused)) int fd, | |
1162 | __attribute__ ((unused)) struct termios *termios_p) { | |
1163 | die ("tcgetattr not implemented"); | |
1164 | } | |
1165 | ||
1166 | __attribute__ ((noreturn)) | |
1167 | pid_t tcgetpgrp (__attribute__ ((unused)) int fd) { | |
1168 | die ("tcgetpgrp not implemented"); | |
1169 | } | |
1170 | ||
1171 | __attribute__ ((noreturn)) | |
1172 | int tcsendbreak (__attribute__ ((unused)) int fd, | |
1173 | __attribute__ ((unused)) int duration) { | |
1174 | die ("tcsendbreak not implemented"); | |
1175 | } | |
1176 | ||
1177 | __attribute__ ((noreturn)) | |
1178 | int tcsetattr (__attribute__ ((unused)) int fd, | |
1179 | __attribute__ ((unused)) int optional_actions, | |
1180 | __attribute__ ((unused)) struct termios *termios_p) { | |
1181 | die ("tcsetattr not implemented"); | |
1182 | } | |
1183 | ||
1184 | __attribute__ ((noreturn)) | |
1185 | int tcsetpgrp (__attribute__ ((unused)) int fd, | |
1186 | __attribute__ ((unused)) pid_t pgrpid) { | |
1187 | die ("tcsetpgrp not implemented"); | |
1188 | } | |
1189 | ||
1190 | /* ------------------------------------------------- */ | |
1191 | /* Socket */ | |
1192 | /* ------------------------------------------------- */ | |
1193 | ||
1194 | __attribute__ ((noreturn)) | |
1195 | int ioctl (__attribute__ ((unused)) int d, | |
1196 | __attribute__ ((unused)) int request, | |
1197 | ...) { | |
1198 | die ("ioctl not implemented"); | |
1199 | } | |
1200 | ||
1201 | __attribute__ ((noreturn)) | |
1202 | int socketpair (__attribute__ ((unused)) int d, | |
1203 | __attribute__ ((unused)) int type, | |
1204 | __attribute__ ((unused)) int protocol, | |
1205 | __attribute__ ((unused)) int sv[2]) { | |
1206 | die ("socketpair not implemented"); | |
1207 | } | |
1208 | ||
1209 | void MLton_initSockets (void) { | |
1210 | static Bool isInitialized = FALSE; | |
1211 | WORD version; | |
1212 | WSADATA wsaData; | |
1213 | ||
1214 | unless (isInitialized) { | |
1215 | isInitialized = TRUE; | |
1216 | version = MAKEWORD (2,2); | |
1217 | WSAStartup (version, &wsaData); | |
1218 | } | |
1219 | } | |
1220 | ||
1221 | /* This table was constructed with help of | |
1222 | * http://msdn.microsoft.com/en-us/library/ms740668(VS.85).aspx#winsock.wsaenotsock_2 | |
1223 | * man errno(3) | |
1224 | */ | |
1225 | void MLton_fixSocketErrno (void) { | |
1226 | int status = WSAGetLastError (); | |
1227 | ||
1228 | switch (status) { | |
1229 | case 0: errno = 0; break; | |
1230 | case WSAEINTR: errno = EINTR; break; | |
1231 | case WSAEBADF: errno = EBADF; break; | |
1232 | case WSAEACCES: errno = EACCES; break; | |
1233 | case WSAEFAULT: errno = EFAULT; break; | |
1234 | case WSAEINVAL: errno = EINVAL; break; | |
1235 | case WSAEMFILE: errno = EMFILE; break; | |
1236 | case WSAEWOULDBLOCK: errno = EWOULDBLOCK; break; | |
1237 | case WSAEINPROGRESS: errno = EINPROGRESS; break; | |
1238 | case WSAEALREADY: errno = EALREADY; break; | |
1239 | case WSAENOTSOCK: errno = ENOTSOCK; break; | |
1240 | case WSAEDESTADDRREQ: errno = EDESTADDRREQ; break; | |
1241 | case WSAEMSGSIZE: errno = EMSGSIZE; break; | |
1242 | case WSAEPROTOTYPE: errno = EPROTOTYPE; break; | |
1243 | case WSAENOPROTOOPT: errno = ENOPROTOOPT; break; | |
1244 | case WSAEPROTONOSUPPORT: errno = EPROTONOSUPPORT; break; | |
1245 | case WSAESOCKTNOSUPPORT: errno = ESOCKTNOSUPPORT; break; | |
1246 | case WSAEOPNOTSUPP: errno = EOPNOTSUPP; break; | |
1247 | case WSAEPFNOSUPPORT: errno = EPFNOSUPPORT; break; | |
1248 | case WSAEAFNOSUPPORT: errno = EAFNOSUPPORT; break; | |
1249 | case WSAEADDRINUSE: errno = EADDRINUSE; break; | |
1250 | case WSAEADDRNOTAVAIL: errno = EADDRNOTAVAIL; break; | |
1251 | case WSAENETDOWN: errno = ENETDOWN; break; | |
1252 | case WSAENETUNREACH: errno = ENETUNREACH; break; | |
1253 | case WSAENETRESET: errno = ENETRESET; break; | |
1254 | case WSAECONNABORTED: errno = ECONNABORTED; break; | |
1255 | case WSAECONNRESET: errno = ECONNRESET; break; | |
1256 | case WSAENOBUFS: errno = ENOBUFS; break; | |
1257 | case WSAEISCONN: errno = EISCONN; break; | |
1258 | case WSAENOTCONN: errno = ENOTCONN; break; | |
1259 | case WSAESHUTDOWN: errno = ESHUTDOWN; break; | |
1260 | case WSAETIMEDOUT: errno = ETIMEDOUT; break; | |
1261 | case WSAECONNREFUSED: errno = ECONNREFUSED; break; | |
1262 | case WSAELOOP: errno = ELOOP; break; | |
1263 | case WSAENAMETOOLONG: errno = ENAMETOOLONG; break; | |
1264 | case WSAEHOSTDOWN: errno = EHOSTDOWN; break; | |
1265 | case WSAEHOSTUNREACH: errno = EHOSTUNREACH; break; | |
1266 | case WSAENOTEMPTY: errno = ENOTEMPTY; break; | |
1267 | case WSAEDQUOT: errno = EDQUOT; break; | |
1268 | case WSAESTALE: errno = ESTALE; break; | |
1269 | case WSAEREMOTE: errno = EREMOTE; break; | |
1270 | /* These codes appear to have a matching name, but the manual | |
1271 | * descriptions of what the error codes mean seem to differ | |
1272 | */ | |
1273 | case WSAEUSERS: errno = EUSERS; break; | |
1274 | case WSAECANCELLED: errno = ECANCELED; break; | |
1275 | case WSA_E_CANCELLED: errno = ECANCELED; break; | |
1276 | /* These codes have no matching code in the errno(3) man page. */ | |
1277 | case WSAEPROCLIM: errno = EBUSY; break; | |
1278 | case WSAETOOMANYREFS: errno = ENOMEM; break; | |
1279 | case WSAEDISCON: errno = ESHUTDOWN; break; | |
1280 | case WSA_E_NO_MORE: | |
1281 | case WSAENOMORE: | |
1282 | case WSASYSCALLFAILURE: errno = EIO; break; | |
1283 | /* These codes are returned from the OS and subject to chage */ | |
1284 | // WSA_INVALID_HANDLE | |
1285 | // WSA_NOT_ENOUGH_MEMORY | |
1286 | // WSA_INVALID_PARAMETER | |
1287 | // WSA_OPERATION_ABORTED | |
1288 | // WSA_IO_INCOMPLETE | |
1289 | // WSA_IO_PENDING | |
1290 | /* These codes mean some sort of windows specific fatal error */ | |
1291 | case WSASYSNOTREADY: | |
1292 | case WSAVERNOTSUPPORTED: | |
1293 | case WSANOTINITIALISED: | |
1294 | case WSAEINVALIDPROCTABLE: | |
1295 | case WSAEINVALIDPROVIDER: | |
1296 | case WSAEPROVIDERFAILEDINIT: | |
1297 | case WSASERVICE_NOT_FOUND: | |
1298 | case WSATYPE_NOT_FOUND: | |
1299 | die("Problem loading winsock"); | |
1300 | case WSAEREFUSED: | |
1301 | case WSAHOST_NOT_FOUND: | |
1302 | case WSATRY_AGAIN: | |
1303 | case WSANO_RECOVERY: | |
1304 | case WSANO_DATA: | |
1305 | die("Strange winsock specific status code"); | |
1306 | default: | |
1307 | die("Unknown winsock status code"); | |
1308 | } | |
1309 | } | |
1310 | ||
1311 | static const char *MLton_strerrorExtension(int code) { | |
1312 | switch (code) { | |
1313 | case EINTR: return "Interrupted function call"; | |
1314 | case EBADF: return "Bad file descriptor"; | |
1315 | case EACCES: return "Permission denied"; | |
1316 | case EFAULT: return "Bad address"; | |
1317 | case EINVAL: return "Invalid argument"; | |
1318 | case EMFILE: return "Too many open files"; | |
1319 | case EAGAIN: return "Resource temporarily unavailable"; | |
1320 | case EINPROGRESS: return "Operation in progress"; | |
1321 | case EALREADY: return "Connection already in progress"; | |
1322 | case ENOTSOCK: return "Not a socket"; | |
1323 | case EDESTADDRREQ: return "Destination address required"; | |
1324 | case EMSGSIZE: return "Message too long"; | |
1325 | case EPROTOTYPE: return "Protocol wrong type for socket"; | |
1326 | case ENOPROTOOPT: return "Protocol not available"; | |
1327 | case EPROTONOSUPPORT: return "Protocol not supported"; | |
1328 | case ESOCKTNOSUPPORT: return "Socket type not supported"; | |
1329 | case EOPNOTSUPP: return "Operation not supported on socket"; | |
1330 | case EPFNOSUPPORT: return "Protocol family not supported"; | |
1331 | case EAFNOSUPPORT: return "Address family not supported"; | |
1332 | case EADDRINUSE: return "Address already in use"; | |
1333 | case EADDRNOTAVAIL: return "Address not available"; | |
1334 | case ENETDOWN: return "Network is down"; | |
1335 | case ENETUNREACH: return "Network unreachable"; | |
1336 | case ENETRESET: return "Connection aborted by network"; | |
1337 | case ECONNABORTED: return "Connection aborted"; | |
1338 | case ECONNRESET: return "Connection reset"; | |
1339 | case ENOBUFS: return "No buffer space available"; | |
1340 | case EISCONN: return "Socket is connected"; | |
1341 | case ENOTCONN: return "The socket is not connected"; | |
1342 | case ESHUTDOWN: return "Cannot send after transport endpoint shutdown"; | |
1343 | case ETIMEDOUT: return "Connection timed out"; | |
1344 | case ECONNREFUSED: return "Connection refused"; | |
1345 | case ELOOP: return "Too many levels of symbolic links"; | |
1346 | case ENAMETOOLONG: return "Filename too long"; | |
1347 | case EHOSTDOWN: return "Host is down"; | |
1348 | case EHOSTUNREACH: return "Host is unreachable"; | |
1349 | case ENOTEMPTY: return "Directory not empty"; | |
1350 | case EDQUOT: return "Disk quota exceeded"; | |
1351 | case ESTALE: return "Stale file handle"; | |
1352 | case EREMOTE: return "Object is remote"; | |
1353 | case EUSERS: return "Too many users"; | |
1354 | case ECANCELED: return "Operation canceled"; | |
1355 | default: return "Unknown error"; | |
1356 | } | |
1357 | } | |
1358 | ||
1359 | /* MinGW strerror works for all system-defined errno values. | |
1360 | * However, platform/mingw.h adds some missing POSIX networking error codes. | |
1361 | * It defines these codes as their closest-equivalent winsock error code. | |
1362 | * To report network errors, MLton_fixSocketErrno maps winsock errors to | |
1363 | * their closest POSIX errno value. | |
1364 | * | |
1365 | * This function must handle the winsock errno values we have added. | |
1366 | * FormatMessage doesn't return the POSIX string for errors, and it uses | |
1367 | * the current locale's language. The MinGW strerror is always English. | |
1368 | * | |
1369 | * Thus, we just make a big English table to augment strerror. | |
1370 | * The descriptions are taken from man errno(3). | |
1371 | */ | |
1372 | char *MLton_strerror(int code) { | |
1373 | static char buffer[80]; | |
1374 | ||
1375 | #undef strerror | |
1376 | if (code < sys_nerr) return strerror(code); | |
1377 | #define strerror MLton_strerror | |
1378 | ||
1379 | strcpy(buffer, MLton_strerrorExtension(code)); | |
1380 | return buffer; | |
1381 | } | |
1382 | ||
1383 | int MLton_recv(int s, void *buf, int len, int flags) { | |
1384 | int ret, status = 0; | |
1385 | ||
1386 | if (flags & MSG_DONTWAIT) MinGW_setNonBlock(s); | |
1387 | ret = recv(s, buf, len, flags & ~MSG_DONTWAIT); | |
1388 | ||
1389 | /* We need to preserve the error status across non-blocking call */ | |
1390 | if (ret == -1) status = WSAGetLastError(); | |
1391 | if (flags & MSG_DONTWAIT) MinGW_clearNonBlock(s); | |
1392 | if (ret == -1) WSASetLastError(status); | |
1393 | ||
1394 | return ret; | |
1395 | } | |
1396 | ||
1397 | int MLton_recvfrom(int s, void *buf, int len, int flags, void *from, | |
1398 | socklen_t *fromlen) { | |
1399 | int ret, status = 0; | |
1400 | ||
1401 | if (flags & MSG_DONTWAIT) MinGW_setNonBlock(s); | |
1402 | ret = recvfrom(s, buf, len, flags & ~MSG_DONTWAIT, from, fromlen); | |
1403 | ||
1404 | /* We need to preserve the error status across non-blocking call */ | |
1405 | if (ret == -1) status = WSAGetLastError(); | |
1406 | if (flags & MSG_DONTWAIT) MinGW_clearNonBlock(s); | |
1407 | if (ret == -1) WSASetLastError(status); | |
1408 | ||
1409 | return ret; | |
1410 | } | |
1411 | /* ------------------------------------------------- */ | |
1412 | /* Syslog */ | |
1413 | /* ------------------------------------------------- */ | |
1414 | ||
1415 | static const char* logident = "<unknown>"; | |
1416 | static int logopt = LOG_PERROR; | |
1417 | static int logfacility = LOG_LOCAL0; | |
1418 | ||
1419 | void openlog(const char* ident, int opt, int facility) { | |
1420 | logident = ident; | |
1421 | logopt = opt; | |
1422 | logfacility = facility; | |
1423 | } | |
1424 | ||
1425 | void closelog(void) { | |
1426 | } | |
1427 | ||
1428 | void syslog(int priority, __attribute__ ((unused)) const char* fmt, const char* msg) { | |
1429 | static const char* severity[] = { | |
1430 | "debug", | |
1431 | "informational", | |
1432 | "notice", | |
1433 | "warning", | |
1434 | "error", | |
1435 | "CRITICAL", | |
1436 | "ALERT", | |
1437 | "EMERGENCY" | |
1438 | }; | |
1439 | ||
1440 | if (priority < 0) priority = LOG_DEBUG; | |
1441 | if (priority > LOG_EMERG) priority = LOG_EMERG; | |
1442 | ||
1443 | ||
1444 | /* !!! Use ReportEvent to log with windows */ | |
1445 | ||
1446 | if ((logopt & LOG_PERROR) != 0) { | |
1447 | if ((logopt & LOG_PID) != 0) | |
1448 | fprintf(stderr, "%s(%d): %s: %s\n", logident, getpid(), severity[priority], msg); | |
1449 | else | |
1450 | fprintf(stderr, "%s: %s: %s\n", logident, severity[priority], msg); | |
1451 | } | |
1452 | } | |
1453 | ||
1454 | /* ------------------------------------------------- */ | |
1455 | /* MinGW */ | |
1456 | /* ------------------------------------------------- */ | |
1457 | ||
1458 | C_Size_t MinGW_getTempPath(C_Size_t buf_size, Array(Char8_t) buf) { | |
1459 | return GetTempPath(buf_size, (char*)buf); | |
1460 | } | |
1461 | ||
1462 | void MinGW_setNonBlock(C_Fd_t fd) { | |
1463 | unsigned long yes = 1; | |
1464 | ioctlsocket(fd, FIONBIO, &yes); | |
1465 | } | |
1466 | ||
1467 | void MinGW_clearNonBlock(C_Fd_t fd) { | |
1468 | unsigned long no = 0; | |
1469 | ioctlsocket(fd, FIONBIO, &no); | |
1470 | } |