1 /*
2 * system.c - system interface
3 *
4 * Copyright (c) 2000-2020 Shiro Kawai <shiro@acm.org>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the authors nor the names of its contributors
18 * may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 #define LIBGAUCHE_BODY
35 #include "gauche.h"
36 #include "gauche/class.h"
37 #include "gauche/bignum.h"
38 #include "gauche/priv/builtin-syms.h"
39
40 #include <locale.h>
41 #include <errno.h>
42 #include <string.h>
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <ctype.h>
46 #include <fcntl.h>
47 #include <math.h>
48 #include <dirent.h>
49
50 #if !defined(GAUCHE_WINDOWS)
51 #include <grp.h>
52 #include <pwd.h>
53 #include <sys/times.h>
54 #include <sys/wait.h>
55
56 # if !defined(HAVE_CRT_EXTERNS_H)
57 /* POSIX defines environ, and ISO C defines __environ.
58 Modern C seems to have the latter declared in unistd.h */
59 extern char **environ;
60 # else /* HAVE_CRT_EXTERNS_H */
61 /* On newer OSX, we can't directly access global 'environ' variable.
62 We need to use _NSGetEnviron(), and this header defines it. */
63 #include <crt_externs.h>
64 # endif /* HAVE_CRT_EXTERNS_H */
65 #else /* GAUCHE_WINDOWS */
66 #include <lm.h>
67 #include <tlhelp32.h>
68 /* For windows redirection; win_prepare_handles creats and returns
69 win_redirects[3]. Each entry contains an inheritable handle for
70 the child process' stdin, stdout and stderr, respectively, and the flag
71 duped indicates whether the parent process must close the handle. */
72 typedef struct win_redirects_rec {
73 HANDLE *h;
74 int duped;
75 } win_redirects;
76 static win_redirects *win_prepare_handles(int *fds);
77 static int win_wait_for_handles(HANDLE *handles, int nhandles, int options,
78 int *status /*out*/);
79 #endif /* GAUCHE_WINDOWS */
80
81 #ifdef HAVE_GLOB_H
82 #include <glob.h>
83 #endif
84 #ifdef HAVE_SCHED_H
85 #include <sched.h>
86 #endif
87
88 /*
89 * Auxiliary system interface functions. See syslib.stub for
90 * Scheme binding.
91 */
92
93 /*===============================================================
94 * Conversion between off_t and Scheme integer.
95 * off_t can be either 32bit or 64bit.
96 */
Scm_IntegerToOffset(ScmObj i)97 off_t Scm_IntegerToOffset(ScmObj i)
98 {
99 if (SCM_INTP(i)) {
100 return (off_t)SCM_INT_VALUE(i);
101 } else if (SCM_BIGNUMP(i)) {
102 #if SIZEOF_OFF_T == SIZEOF_LONG
103 return (off_t)Scm_GetIntegerClamp(i, SCM_CLAMP_ERROR, NULL);
104 #elif SIZEOF_OFF_T == 8
105 return (off_t)Scm_GetInteger64Clamp(i, SCM_CLAMP_ERROR, NULL);
106 #else
107 /* I don't think there's such an architecture. */
108 # error "off_t size on this platform is not suported."
109 #endif
110 }
111 Scm_Error("bad value as offset: %S", i);
112 return (off_t)-1; /* dummy */
113 }
114
Scm_OffsetToInteger(off_t off)115 ScmObj Scm_OffsetToInteger(off_t off)
116 {
117 #if SIZEOF_OFF_T == SIZEOF_LONG
118 return Scm_MakeInteger(off);
119 #elif SIZEOF_OFF_T == 8
120 return Scm_MakeInteger64((int64_t)off);
121 #else
122 # error "off_t size on this platform is not suported."
123 #endif
124 }
125
126 /*===============================================================
127 * Windows specific - conversion between mbs and wcs.
128 */
129 #if defined(GAUCHE_WINDOWS) && defined(UNICODE)
130 #include "win-compat.c"
131
Scm_MBS2WCS(const char * s)132 WCHAR *Scm_MBS2WCS(const char *s)
133 {
134 return mbs2wcs(s, TRUE, Scm_Error);
135 }
136
Scm_WCS2MBS(const WCHAR * s)137 const char *Scm_WCS2MBS(const WCHAR *s)
138 {
139 return wcs2mbs(s, TRUE, Scm_Error);
140 }
141 #endif /* defined(GAUCHE_WINDOWS) && defined(UNICODE) */
142
143 /*===============================================================
144 * OBSOLETED: Wrapper to the system call to handle signals.
145 * Use SCM_SYSCALL_{I|P} macro instead.
146 */
Scm_SysCall(int r)147 int Scm_SysCall(int r)
148 {
149 Scm_Warn("Obsoleted API Scm_SysCall is called.");
150 if (r < 0 && errno == EINTR) {
151 ScmVM *vm = Scm_VM();
152 errno = 0;
153 SCM_SIGCHECK(vm);
154 }
155 return r;
156 }
157
Scm_PtrSysCall(void * r)158 void *Scm_PtrSysCall(void *r)
159 {
160 Scm_Warn("Obsoleted API Scm_PtrSysCall is called.");
161 if (r == NULL && errno == EINTR) {
162 ScmVM *vm = Scm_VM();
163 errno = 0;
164 SCM_SIGCHECK(vm);
165 }
166 return r;
167 }
168
169 /*
170 * A utility function for the procedures that accepts either port or
171 * integer file descriptor. Returns the file descriptor. If port_or_fd
172 * is a port that is not associated with the system file, and needfd is
173 * true, signals error. Otherwise it returns -1.
174 */
Scm_GetPortFd(ScmObj port_or_fd,int needfd)175 int Scm_GetPortFd(ScmObj port_or_fd, int needfd)
176 {
177 int fd = -1;
178 if (SCM_INTP(port_or_fd)) {
179 fd = SCM_INT_VALUE(port_or_fd);
180 } else if (SCM_PORTP(port_or_fd)) {
181 fd = Scm_PortFileNo(SCM_PORT(port_or_fd));
182 if (fd < 0 && needfd) {
183 Scm_Error("the port is not associated with a system file descriptor: %S",
184 port_or_fd);
185 }
186 } else {
187 Scm_Error("port or small integer required, but got %S", port_or_fd);
188 }
189 return fd;
190 }
191
192 /*===============================================================
193 * Directory primitives (dirent.h)
194 * We don't provide the iterator primitives, but a function which
195 * reads entire directory.
196 */
197
198 /* Returns a list of directory entries. If pathname is not a directory,
199 or can't be opened by some reason, an error is signalled. */
Scm_ReadDirectory(ScmString * pathname)200 ScmObj Scm_ReadDirectory(ScmString *pathname)
201 {
202 ScmObj head = SCM_NIL, tail = SCM_NIL;
203 #if !defined(GAUCHE_WINDOWS)
204 ScmVM *vm = Scm_VM();
205 struct dirent *dire;
206 DIR *dirp = opendir(Scm_GetStringConst(pathname));
207
208 if (dirp == NULL) {
209 SCM_SIGCHECK(vm);
210 Scm_SysError("couldn't open directory %S", pathname);
211 }
212 while ((dire = readdir(dirp)) != NULL) {
213 ScmObj ent = SCM_MAKE_STR_COPYING(dire->d_name);
214 SCM_APPEND1(head, tail, ent);
215 }
216 SCM_SIGCHECK(vm);
217 closedir(dirp);
218 return head;
219 #else /* GAUCHE_WINDOWS */
220 WIN32_FIND_DATA fdata;
221 DWORD winerrno;
222 ScmObj pattern;
223
224 int pathlen = SCM_STRING_LENGTH(pathname);
225 if (pathlen == 0) {
226 Scm_Error("Couldn't open directory \"\"");
227 }
228 ScmChar lastchar = Scm_StringRef(pathname, pathlen-1, FALSE);
229 if (lastchar == SCM_CHAR('/') || lastchar == SCM_CHAR('\\')) {
230 pattern = Scm_StringAppendC(pathname, "*", 1, 1);
231 } else {
232 pattern = Scm_StringAppendC(pathname, "\\*", 2, 2);
233 }
234 const char *path = Scm_GetStringConst(SCM_STRING(pattern));
235
236 HANDLE dirp = FindFirstFile(SCM_MBS2WCS(path), &fdata);
237 if (dirp == INVALID_HANDLE_VALUE) {
238 if ((winerrno = GetLastError()) != ERROR_FILE_NOT_FOUND) goto err;
239 return head;
240 }
241 const char *tpath = SCM_WCS2MBS(fdata.cFileName);
242 SCM_APPEND1(head, tail, SCM_MAKE_STR_COPYING(tpath));
243 while (FindNextFile(dirp, &fdata) != 0) {
244 tpath = SCM_WCS2MBS(fdata.cFileName);
245 SCM_APPEND1(head, tail, SCM_MAKE_STR_COPYING(tpath));
246 }
247 winerrno = GetLastError();
248 FindClose(dirp);
249 if (winerrno != ERROR_NO_MORE_FILES) goto err;
250 return head;
251 err:
252 Scm_Error("Searching directory failed by windows error %d",
253 winerrno);
254 return SCM_UNDEFINED; /* dummy */
255 #endif
256 }
257
258 /* getcwd compatibility layer.
259 Some implementations of getcwd accepts NULL as buffer to allocate
260 enough buffer memory in it, but that's not standardized and we avoid
261 relying on it.
262 */
Scm_GetCwd(void)263 ScmObj Scm_GetCwd(void)
264 {
265 #if defined(GAUCHE_WINDOWS)&&defined(UNICODE)
266 # define CHAR_T wchar_t
267 # define GETCWD _wgetcwd
268 #else /*!(defined(GAUCHE_WINDOWS)&&defined(UNICODE))*/
269 # define CHAR_T char
270 # define GETCWD getcwd
271 #endif /*!(defined(GAUCHE_WINDOWS)&&defined(UNICODE))*/
272
273 #define GETCWD_INITIAL_BUFFER_SIZE 1024
274 int bufsiz = GETCWD_INITIAL_BUFFER_SIZE;
275 CHAR_T sbuf[GETCWD_INITIAL_BUFFER_SIZE];
276 CHAR_T *buf = sbuf;
277 CHAR_T *r;
278
279 for (;;) {
280 SCM_SYSCALL3(r, GETCWD(buf, bufsiz), r == NULL);
281 if (r != NULL) break;
282 if (errno == ERANGE) {
283 bufsiz *= 2;
284 buf = SCM_NEW_ATOMIC_ARRAY(CHAR_T, bufsiz);
285 } else {
286 Scm_SysError("getcwd failed");
287 }
288 }
289 #if defined(GAUCHE_WINDOWS) && defined(UNICODE)
290 return Scm_MakeString(Scm_WCS2MBS(buf), -1, -1, 0);
291 #else /*!(defined(GAUCHE_WINDOWS) && defined(UNICODE))*/
292 return Scm_MakeString(buf, -1, -1, SCM_STRING_COPYING);
293 #endif /*!(defined(GAUCHE_WINDOWS) && defined(UNICODE))*/
294 #undef CHAR_T
295 }
296
297 /*===============================================================
298 * Pathname manipulation
299 *
300 * It gets complicated since the byte '/' and '\\' can appear in
301 * the trailing octets of a multibyte character.
302 * Assuming these operations won't be a bottleneck, we use simple and
303 * straightforward code rather than tricky and fast one.
304 */
305
306 /* Returns the system's native pathname delimiter. */
Scm_PathDelimiter(void)307 const char *Scm_PathDelimiter(void)
308 {
309 #if !defined(GAUCHE_WINDOWS)
310 return "/";
311 #else /* GAUCHE_WINDOWS */
312 return "\\";
313 #endif /* GAUCHE_WINDOWS */
314 }
315
316 /* On Windows, '/' is *allowed* to be an alternative separator. */
317 #if defined(GAUCHE_WINDOWS)
318 #define SEPARATOR '\\'
319 #define ROOTDIR "\\"
320 #define SEPARATOR_P(c) ((c) == SEPARATOR || (c) == '/')
321 #else
322 #define SEPARATOR '/'
323 #define ROOTDIR "/"
324 #define SEPARATOR_P(c) ((c) == SEPARATOR)
325 #endif
326
327 /* Returns the pointer to the first path separator character,
328 or NULL if no separator is found. */
get_first_separator(const char * path,const char * end)329 static const char *get_first_separator(const char *path, const char *end)
330 {
331 const char *p = path;
332 while (p < end) {
333 if (SEPARATOR_P(*p)) return p;
334 p += SCM_CHAR_NFOLLOWS(*p)+1;
335 }
336 return NULL;
337 }
338
339 /* Returns the pointer to the last path separator character,
340 or NULL if no separator is found. */
get_last_separator(const char * path,const char * end)341 static const char *get_last_separator(const char *path, const char *end)
342 {
343 const char *p = path, *last = NULL;
344 while (p < end) {
345 if (SEPARATOR_P(*p)) last = p;
346 p += SCM_CHAR_NFOLLOWS(*p)+1;
347 }
348 return last;
349 }
350
skip_separators(const char * p,const char * end)351 static const char *skip_separators(const char *p, const char *end)
352 {
353 while (p < end) {
354 if (!SEPARATOR_P(*p)) break;
355 p += SCM_CHAR_NFOLLOWS(*p)+1;
356 }
357 return p;
358 }
359
360 /* Returns the end pointer sans trailing separators. */
truncate_trailing_separators(const char * path,const char * end)361 static const char *truncate_trailing_separators(const char *path,
362 const char *end)
363 {
364 const char *p = get_first_separator(path, end);
365 if (p == NULL) return end;
366 for (;;) {
367 const char *q = skip_separators(p, end);
368 if (q == end) return p;
369 p = get_first_separator(q, end);
370 if (p == NULL) return end;
371 }
372 }
373
374 /* for keyword arguments */
375 static ScmObj key_absolute = SCM_FALSE;
376 static ScmObj key_expand = SCM_FALSE;
377 static ScmObj key_canonicalize = SCM_FALSE;
378
Scm_NormalizePathname(ScmString * pathname,int flags)379 ScmObj Scm_NormalizePathname(ScmString *pathname, int flags)
380 {
381 static ScmObj proc = SCM_UNDEFINED;
382 SCM_BIND_PROC(proc, "sys-normalize-pathname", Scm_GaucheModule());
383
384 ScmObj h = SCM_NIL, t = SCM_NIL;
385 SCM_APPEND1(h, t, SCM_OBJ(pathname));
386 if (flags & SCM_PATH_ABSOLUTE) {
387 SCM_APPEND1(h, t, key_absolute);
388 SCM_APPEND1(h, t, SCM_TRUE);
389 }
390 if (flags & SCM_PATH_CANONICALIZE) {
391 SCM_APPEND1(h, t, key_canonicalize);
392 SCM_APPEND1(h, t, SCM_TRUE);
393 }
394 if (flags & SCM_PATH_EXPAND) {
395 SCM_APPEND1(h, t, key_expand);
396 SCM_APPEND1(h, t, SCM_TRUE);
397 }
398 return Scm_ApplyRec(proc, h);
399 }
400
401 /* Returns system's temporary directory. */
Scm_TmpDir(void)402 ScmObj Scm_TmpDir(void)
403 {
404 #if defined(GAUCHE_WINDOWS)
405 # define TMP_PATH_MAX 1024
406 TCHAR buf[TMP_PATH_MAX+1], *tbuf = buf;
407 /* According to the windows document, this API checks environment
408 variables TMP, TEMP, and USERPROFILE. Fallback is the Windows
409 directory. */
410 DWORD r = GetTempPath(TMP_PATH_MAX, buf);
411 if (r == 0) Scm_SysError("GetTempPath failed");
412 if (r > TMP_PATH_MAX) {
413 tbuf = SCM_NEW_ATOMIC_ARRAY(TCHAR, r+1);
414 DWORD r2 = GetTempPath(r, tbuf);
415 if (r2 != r) Scm_SysError("GetTempPath failed");
416 }
417 return SCM_MAKE_STR_COPYING(SCM_WCS2MBS(tbuf));
418 #else /*!GAUCHE_WINDOWS*/
419 const char *s;
420 if ((s = Scm_GetEnv("TMPDIR")) != NULL) return SCM_MAKE_STR_COPYING(s);
421 if ((s = Scm_GetEnv("TMP")) != NULL) return SCM_MAKE_STR_COPYING(s);
422 else return SCM_MAKE_STR("/tmp"); /* fallback */
423 #endif /*!GAUCHE_WINDOWS*/
424 }
425
426 /* Basename and dirname.
427 On Win32, we need to treat drive names specially, e.g.:
428 (sys-dirname "C:/a") == (sys-dirname "C:/") == (sys-dirname "C:") == "C:\\"
429 (sys-basename "C:/") == (sys-basename "C:) == ""
430 */
431
Scm_BaseName(ScmString * filename)432 ScmObj Scm_BaseName(ScmString *filename)
433 {
434 ScmSmallInt size;
435 const char *path = Scm_GetStringContent(filename, &size, NULL, NULL);
436
437 #if defined(GAUCHE_WINDOWS)
438 /* Ignore drive letter, for it can never be a part of basename. */
439 if (size >= 2 && path[1] == ':' && isalpha(path[0])) {
440 path += 2;
441 size -= 2;
442 }
443 #endif /* GAUCHE_WINDOWS) */
444
445 if (size == 0) return SCM_MAKE_STR("");
446 const char *endp = truncate_trailing_separators(path, path+size);
447 const char *last = get_last_separator(path, endp);
448 if (last == NULL) {
449 return Scm_MakeString(path, (int)(endp-path), -1, 0);
450 } else {
451 return Scm_MakeString(last+1, (int)(endp-last-1), -1, 0);
452 }
453 }
454
Scm_DirName(ScmString * filename)455 ScmObj Scm_DirName(ScmString *filename)
456 {
457 ScmSmallInt size;
458 const char *path = Scm_GetStringContent(filename, &size, NULL, NULL);
459 #if defined(GAUCHE_WINDOWS)
460 int drive_letter = -1;
461 if (size >= 2 && path[1] == ':' && isalpha(path[0])) {
462 drive_letter = path[0];
463 path += 2;
464 size -= 2;
465 }
466 #endif /* GAUCHE_WINDOWS */
467
468 if (size == 0) { path = NULL; goto finale; }
469 const char *endp = truncate_trailing_separators(path, path+size);
470 if (endp == path) { path = ROOTDIR, size = 1; goto finale; }
471 const char *last = get_last_separator(path, endp);
472 if (last == NULL) { path = ".", size = 1; goto finale; }
473
474 /* we have "something/", and 'last' points to the last separator. */
475 last = truncate_trailing_separators(path, last);
476 if (last == path) {
477 path = ROOTDIR, size = 1;
478 } else {
479 size = (int)(last - path);
480 }
481 finale:
482 #if defined(GAUCHE_WINDOWS)
483 if (drive_letter > 0) {
484 ScmObj z;
485 char p[3] = "x:";
486 p[0] = (char)drive_letter;
487 z = Scm_MakeString(p, 2, 2, SCM_MAKSTR_COPYING);
488 if (path) {
489 return Scm_StringAppendC(SCM_STRING(z), path, size, -1);
490 } else {
491 return Scm_StringAppendC(SCM_STRING(z), ROOTDIR, 1, -1);
492 }
493 }
494 #endif /* GAUCHE_WINDOWS */
495 if (path) return Scm_MakeString(path, size, -1, 0);
496 else return Scm_MakeString(".", 1, 1, 0);
497 }
498
499 #undef ROOTDIR
500 #undef SEPARATOR
501
502
503 #if !defined(HAVE_MKSTEMP) || !defined(HAVE_MKDTEMP)
504 /*
505 * Helper function to emulate mkstemp or mkdtemp. FUNC returns 0 on
506 * success and non-zero otherwize. NAME is a name of operation
507 * performed by FUNC. ARG is caller supplied data passed to FUNC.
508 */
emulate_mkxtemp(char * name,char * templat,int (* func)(char *,void *),void * arg)509 static void emulate_mkxtemp(char *name, char *templat,
510 int (*func)(char *, void *), void *arg)
511 {
512 /* Emulate mkxtemp. */
513 int siz = (int)strlen(templat);
514 if (siz < 6) {
515 Scm_Error("%s - invalid template: %s", name, templat);
516 }
517 #define MKXTEMP_MAX_TRIALS 65535 /* avoid infinite loop */
518 {
519 u_long seed = (u_long)time(NULL);
520 int numtry, rv;
521 char suffix[7];
522 for (numtry=0; numtry<MKXTEMP_MAX_TRIALS; numtry++) {
523 snprintf(suffix, 7, "%06lx", (seed>>8)&0xffffff);
524 memcpy(templat+siz-6, suffix, 7);
525 rv = (*func)(templat, arg);
526 if (rv == 0) break;
527 seed *= 2654435761UL;
528 }
529 if (numtry == MKXTEMP_MAX_TRIALS) {
530 Scm_Error("%s failed", name);
531 }
532 }
533 }
534 #endif /* !defined(HAVE_MKSTEMP) || !defined(HAVE_MKDTEMP) */
535
536 #define MKXTEMP_PATH_MAX 1025 /* Geez, remove me */
build_template(ScmString * templat,char * name)537 static void build_template(ScmString *templat, char *name)
538 {
539 ScmSmallInt siz;
540 const char *t = Scm_GetStringContent(templat, &siz, NULL, NULL);
541 if (siz >= MKXTEMP_PATH_MAX-6) {
542 Scm_Error("pathname too long: %S", templat);
543 }
544 memcpy(name, t, siz);
545 memcpy(name + siz, "XXXXXX", 6);
546 name[siz+6] = '\0';
547 }
548
549 #if !defined(HAVE_MKSTEMP)
create_tmpfile(char * templat,void * arg)550 static int create_tmpfile(char *templat, void *arg)
551 {
552 int *fdp = (int *)arg;
553 int flags;
554
555 #if defined(GAUCHE_WINDOWS)
556 flags = O_CREAT|O_EXCL|O_WRONLY|O_BINARY;
557 #else /* !GAUCHE_WINDOWS */
558 flags = O_CREAT|O_EXCL|O_WRONLY;
559 #endif /* !GAUCHE_WINDOWS */
560 SCM_SYSCALL(*fdp, open(templat, flags, 0600));
561 return *fdp < 0;
562 }
563 #endif
564
565 /* Make mkstemp() work even if the system doesn't have one. */
Scm_Mkstemp(char * templat)566 int Scm_Mkstemp(char *templat)
567 {
568 int fd = -1;
569 #if defined(HAVE_MKSTEMP)
570 SCM_SYSCALL(fd, mkstemp(templat));
571 if (fd < 0) Scm_SysError("mkstemp failed");
572 return fd;
573 #else /*!defined(HAVE_MKSTEMP)*/
574 emulate_mkxtemp("mkstemp", templat, create_tmpfile, &fd);
575 return fd;
576 #endif /*!defined(HAVE_MKSTEMP)*/
577 }
578
579
Scm_SysMkstemp(ScmString * templat)580 ScmObj Scm_SysMkstemp(ScmString *templat)
581 {
582 char name[MKXTEMP_PATH_MAX];
583 build_template(templat, name);
584 int fd = Scm_Mkstemp(name);
585 ScmObj sname = SCM_MAKE_STR_COPYING(name);
586 SCM_RETURN(Scm_Values2(Scm_MakePortWithFd(sname, SCM_PORT_OUTPUT, fd,
587 SCM_PORT_BUFFER_FULL, TRUE),
588 sname));
589 }
590
591 #if !defined(HAVE_MKDTEMP)
create_tmpdir(char * templat,void * arg SCM_UNUSED)592 static int create_tmpdir(char *templat, void *arg SCM_UNUSED)
593 {
594 int r;
595
596 #if defined(GAUCHE_WINDOWS)
597 SCM_SYSCALL(r, mkdir(templat));
598 #else /* !GAUCHE_WINDOWS */
599 SCM_SYSCALL(r, mkdir(templat, 0700));
600 #endif /* !GAUCHE_WINDOWS */
601 return r < 0;
602 }
603 #endif
604
Scm_SysMkdtemp(ScmString * templat)605 ScmObj Scm_SysMkdtemp(ScmString *templat)
606 {
607 char name[MKXTEMP_PATH_MAX];
608 build_template(templat, name);
609
610 #if defined(HAVE_MKDTEMP)
611 {
612 char *p = NULL;
613 SCM_SYSCALL3(p, mkdtemp(name), (p == NULL));
614 if (p == NULL) Scm_SysError("mkdtemp failed");
615 }
616 #else /*!defined(HAVE_MKDTEMP)*/
617 emulate_mkxtemp("mkdtemp", name, create_tmpdir, NULL);
618 #endif /*!defined(HAVE_MKDTEMP)*/
619
620 return SCM_MAKE_STR_COPYING(name);
621 }
622
623 /*===============================================================
624 * Stat (sys/stat.h)
625 */
626
stat_allocate(ScmClass * klass,ScmObj initargs SCM_UNUSED)627 static ScmObj stat_allocate(ScmClass *klass, ScmObj initargs SCM_UNUSED)
628 {
629 return SCM_OBJ(SCM_NEW_INSTANCE(ScmSysStat, klass));
630 }
631
stat_hash(ScmObj obj,ScmSmallInt salt,u_long flags)632 static ScmSmallInt stat_hash(ScmObj obj, ScmSmallInt salt, u_long flags)
633 {
634 ScmStat *s = SCM_SYS_STAT_STAT(obj);
635 ScmSmallInt h = salt;
636 #define STAT_HASH_UI(name) \
637 h = Scm_CombineHashValue(Scm_SmallIntHash((ScmSmallInt)s->SCM_CPP_CAT(st_, name), \
638 salt, flags), h)
639 #define STAT_HASH_TIME(name) \
640 h = Scm_CombineHashValue(Scm_Int64Hash((int64_t)s->SCM_CPP_CAT(st_, name), \
641 salt, flags), h)
642 #define STAT_HASH_TIMESPEC(name) \
643 h = Scm_CombineHashValue(Scm_Int64Hash((int64_t)s->SCM_CPP_CAT(st_, name).tv_sec, \
644 salt, flags), \
645 Scm_CombineHashValue(Scm_Int64Hash((int64_t)s->SCM_CPP_CAT(st_, name).tv_nsec, \
646 salt, flags),h))
647
648
649 STAT_HASH_UI(mode);
650 STAT_HASH_UI(ino);
651 STAT_HASH_UI(dev);
652 STAT_HASH_UI(rdev);
653 STAT_HASH_UI(nlink);
654 STAT_HASH_UI(uid);
655 STAT_HASH_UI(gid);
656 #if HAVE_STRUCT_STAT_ST_ATIM
657 STAT_HASH_TIMESPEC(atim);
658 #else
659 STAT_HASH_TIME(atime);
660 #endif
661 #if HAVE_STRUCT_STAT_ST_MTIM
662 STAT_HASH_TIMESPEC(mtim);
663 #else
664 STAT_HASH_TIME(mtime);
665 #endif
666 #if HAVE_STRUCT_STAT_ST_CTIM
667 STAT_HASH_TIMESPEC(ctim);
668 #else
669 STAT_HASH_TIME(ctime);
670 #endif
671 return h;
672 }
673
674 SCM_DEFINE_BUILTIN_CLASS(Scm_SysStatClass,
675 NULL, NULL, stat_hash,
676 stat_allocate,
677 SCM_CLASS_DEFAULT_CPL);
678
Scm_MakeSysStat(void)679 ScmObj Scm_MakeSysStat(void)
680 {
681 return stat_allocate(&Scm_SysStatClass, SCM_NIL);
682 }
683
stat_type_get(ScmSysStat * stat)684 static ScmObj stat_type_get(ScmSysStat *stat)
685 {
686 if (S_ISDIR(SCM_SYS_STAT_STAT(stat)->st_mode)) return (SCM_SYM_DIRECTORY);
687 if (S_ISREG(SCM_SYS_STAT_STAT(stat)->st_mode)) return (SCM_SYM_REGULAR);
688 if (S_ISCHR(SCM_SYS_STAT_STAT(stat)->st_mode)) return (SCM_SYM_CHARACTER);
689 if (S_ISBLK(SCM_SYS_STAT_STAT(stat)->st_mode)) return (SCM_SYM_BLOCK);
690 if (S_ISFIFO(SCM_SYS_STAT_STAT(stat)->st_mode)) return (SCM_SYM_FIFO);
691 #ifdef S_ISLNK
692 if (S_ISLNK(SCM_SYS_STAT_STAT(stat)->st_mode)) return (SCM_SYM_SYMLINK);
693 #endif
694 #ifdef S_ISSOCK
695 if (S_ISSOCK(SCM_SYS_STAT_STAT(stat)->st_mode)) return (SCM_SYM_SOCKET);
696 #endif
697 return (SCM_FALSE);
698 }
699
stat_perm_get(ScmSysStat * stat)700 static ScmObj stat_perm_get(ScmSysStat *stat)
701 {
702 return Scm_MakeIntegerFromUI(SCM_SYS_STAT_STAT(stat)->st_mode & 0777);
703 }
704
stat_size_get(ScmSysStat * stat)705 static ScmObj stat_size_get(ScmSysStat *stat)
706 {
707 return Scm_OffsetToInteger(SCM_SYS_STAT_STAT(stat)->st_size);
708 }
709
710
711 #define STAT_GETTER_UI(name) \
712 static ScmObj SCM_CPP_CAT3(stat_, name, _get)(ScmSysStat *s) \
713 { return Scm_MakeIntegerFromUI((u_long)(SCM_SYS_STAT_STAT(s)->SCM_CPP_CAT(st_, name))); }
714 #define STAT_GETTER_TIME(name) \
715 static ScmObj SCM_CPP_CAT3(stat_, name, _get)(ScmSysStat *s) \
716 { return Scm_MakeSysTime(SCM_SYS_STAT_STAT(s)->SCM_CPP_CAT(st_, name)); }
717
718 STAT_GETTER_UI(mode)
STAT_GETTER_UI(ino)719 STAT_GETTER_UI(ino)
720 STAT_GETTER_UI(dev)
721 STAT_GETTER_UI(rdev)
722 STAT_GETTER_UI(nlink)
723 STAT_GETTER_UI(uid)
724 STAT_GETTER_UI(gid)
725 STAT_GETTER_TIME(atime)
726 STAT_GETTER_TIME(mtime)
727 STAT_GETTER_TIME(ctime)
728
729 static ScmObj stat_atim_get(ScmSysStat *s)
730 {
731 #if HAVE_STRUCT_STAT_ST_ATIM
732 return Scm_MakeTime64(SCM_SYM_TIME_UTC,
733 (int64_t)s->statrec.st_atim.tv_sec,
734 s->statrec.st_atim.tv_nsec);
735 #else
736 return Scm_MakeTime64(SCM_SYM_TIME_UTC,
737 (int64_t)s->statrec.st_atime,
738 0);
739 #endif
740 }
741
stat_mtim_get(ScmSysStat * s)742 static ScmObj stat_mtim_get(ScmSysStat *s)
743 {
744 #if HAVE_STRUCT_STAT_ST_MTIM
745 return Scm_MakeTime64(SCM_SYM_TIME_UTC,
746 (int64_t)s->statrec.st_mtim.tv_sec,
747 s->statrec.st_mtim.tv_nsec);
748 #else
749 return Scm_MakeTime64(SCM_SYM_TIME_UTC,
750 (int64_t)s->statrec.st_mtime,
751 0);
752 #endif
753 }
754
stat_ctim_get(ScmSysStat * s)755 static ScmObj stat_ctim_get(ScmSysStat *s)
756 {
757 #if HAVE_STRUCT_STAT_ST_CTIM
758 return Scm_MakeTime64(SCM_SYM_TIME_UTC,
759 (int64_t)s->statrec.st_ctim.tv_sec,
760 s->statrec.st_ctim.tv_nsec);
761 #else
762 return Scm_MakeTime64(SCM_SYM_TIME_UTC,
763 (int64_t)s->statrec.st_ctime,
764 0);
765 #endif
766 }
767
768 static ScmClassStaticSlotSpec stat_slots[] = {
769 SCM_CLASS_SLOT_SPEC("type", stat_type_get, NULL),
770 SCM_CLASS_SLOT_SPEC("perm", stat_perm_get, NULL),
771 SCM_CLASS_SLOT_SPEC("mode", stat_mode_get, NULL),
772 SCM_CLASS_SLOT_SPEC("ino", stat_ino_get, NULL),
773 SCM_CLASS_SLOT_SPEC("dev", stat_dev_get, NULL),
774 SCM_CLASS_SLOT_SPEC("rdev", stat_rdev_get, NULL),
775 SCM_CLASS_SLOT_SPEC("nlink", stat_nlink_get, NULL),
776 SCM_CLASS_SLOT_SPEC("uid", stat_uid_get, NULL),
777 SCM_CLASS_SLOT_SPEC("gid", stat_gid_get, NULL),
778 SCM_CLASS_SLOT_SPEC("size", stat_size_get, NULL),
779 SCM_CLASS_SLOT_SPEC("atime", stat_atime_get, NULL),
780 SCM_CLASS_SLOT_SPEC("mtime", stat_mtime_get, NULL),
781 SCM_CLASS_SLOT_SPEC("ctime", stat_ctime_get, NULL),
782 SCM_CLASS_SLOT_SPEC("atim", stat_atim_get, NULL),
783 SCM_CLASS_SLOT_SPEC("mtim", stat_mtim_get, NULL),
784 SCM_CLASS_SLOT_SPEC("ctim", stat_ctim_get, NULL),
785 SCM_CLASS_SLOT_SPEC_END()
786 };
787
788 /*===============================================================
789 * Time (sys/time.h and time.h)
790 */
791
792 /* Gauche has two notion of time. A simple number is used by the low-level
793 * system interface (sys-time, sys-gettimeofday). An object of <time> class
794 * is used for higher-level interface, including threads.
795 */
796
797 /* <time> object */
798
time_allocate(ScmClass * klass,ScmObj initargs SCM_UNUSED)799 static ScmObj time_allocate(ScmClass *klass, ScmObj initargs SCM_UNUSED)
800 {
801 ScmTime *t = SCM_NEW_INSTANCE(ScmTime, klass);
802 t->type = SCM_SYM_TIME_UTC;
803 t->sec = 0;
804 t->nsec = 0;
805 return SCM_OBJ(t);
806 }
807
time_print(ScmObj obj,ScmPort * port,ScmWriteContext * ctx SCM_UNUSED)808 static void time_print(ScmObj obj, ScmPort *port,
809 ScmWriteContext *ctx SCM_UNUSED)
810 {
811 ScmTime *t = SCM_TIME(obj);
812 ScmObj sec = Scm_MakeInteger64(t->sec);
813 long nsec = t->nsec;
814 /* t->sec can be negative for time-difference. */
815 if (Scm_Sign(sec) < 0 && t->nsec > 0) {
816 sec = Scm_Abs(Scm_Add(sec, SCM_MAKE_INT(1)));
817 nsec = 1000000000L - nsec;
818 Scm_Printf(port, "#<%S -%S.%09lu>", t->type, sec, nsec);
819 } else {
820 Scm_Printf(port, "#<%S %S.%09lu>", t->type, sec, nsec);
821 }
822 }
823
time_compare(ScmObj x,ScmObj y,int equalp)824 static int time_compare(ScmObj x, ScmObj y, int equalp)
825 {
826 ScmTime *tx = SCM_TIME(x);
827 ScmTime *ty = SCM_TIME(y);
828
829 if (equalp) {
830 if (SCM_EQ(tx->type, ty->type)
831 && tx->sec == ty->sec
832 && tx->nsec == ty->nsec) {
833 return 0;
834 } else {
835 return 1;
836 }
837 } else {
838 if (!SCM_EQ(tx->type, ty->type)) {
839 Scm_Error("cannot compare different types of time objects: %S vs %S", x, y);
840 }
841 if (tx->sec < ty->sec) return -1;
842 if (tx->sec == ty->sec) {
843 if (tx->nsec < ty->nsec) return -1;
844 if (tx->nsec == ty->nsec) return 0;
845 else return 1;
846 }
847 else return 1;
848 }
849 }
850
time_hash(ScmObj x,ScmSmallInt salt,u_long flags)851 static ScmSmallInt time_hash(ScmObj x, ScmSmallInt salt, u_long flags)
852 {
853 ScmTime *t = SCM_TIME(x);
854 ScmSmallInt h = salt;
855 h = Scm_CombineHashValue(Scm_RecursiveHash(t->type, salt, flags), h);
856 h = Scm_CombineHashValue(Scm_Int64Hash(t->sec, salt, flags), h);
857 h = Scm_CombineHashValue(Scm_SmallIntHash(t->nsec, salt, flags), h);
858 return h;
859 }
860
861 SCM_DEFINE_BUILTIN_CLASS(Scm_TimeClass,
862 time_print, time_compare, time_hash,
863 time_allocate, SCM_CLASS_DEFAULT_CPL);
864
make_time_int(ScmObj type)865 static ScmTime *make_time_int(ScmObj type)
866 {
867 ScmTime *t = SCM_TIME(time_allocate(SCM_CLASS_TIME, SCM_NIL));
868 t->type = SCM_FALSEP(type)? SCM_SYM_TIME_UTC : type;
869 return t;
870 }
871
872
Scm_MakeTime(ScmObj type,long sec,long nsec)873 ScmObj Scm_MakeTime(ScmObj type, long sec, long nsec)
874 {
875 ScmTime *t = make_time_int(type);
876 t->sec = (int64_t)sec;
877 t->nsec = nsec;
878 return SCM_OBJ(t);
879 }
880
Scm_MakeTime64(ScmObj type,int64_t sec,long nsec)881 ScmObj Scm_MakeTime64(ScmObj type, int64_t sec, long nsec)
882 {
883 ScmTime *t = make_time_int(type);
884 t->sec = sec;
885 t->nsec = nsec;
886 return SCM_OBJ(t);
887 }
888
889 /* Abstract gettimeofday() */
Scm_GetTimeOfDay(u_long * sec,u_long * usec)890 void Scm_GetTimeOfDay(u_long *sec, u_long *usec)
891 {
892 #if defined(HAVE_GETTIMEOFDAY)
893 struct timeval tv;
894 int r;
895 SCM_SYSCALL(r, gettimeofday(&tv, NULL));
896 if (r < 0) Scm_SysError("gettimeofday failed");
897 *sec = (u_long)tv.tv_sec;
898 *usec = (u_long)tv.tv_usec;
899 #elif defined(GAUCHE_WINDOWS)
900 FILETIME ft;
901 GetSystemTimeAsFileTime(&ft);
902 SCM_FILETIME_TO_UNIXTIME(ft, *sec, *usec);
903 #else /* !HAVE_GETTIMEOFDAY && !GAUCHE_WINDOWS */
904 /* Last resort */
905 *sec = (u_long)time(NULL);
906 *usec = 0;
907 #endif /* !HAVE_GETTIMEOFDAY && !GAUCHE_WINDOWS */
908 }
909
910 /* Abstract clock_gettime and clock_getres.
911 If the system doesn't have these, those API returns FALSE; the caller
912 should make up fallback means.
913
914 NB: XCode8 breaks clock_getres on OSX 10.11---it's only provided in
915 OSX 10.12, but the SDK pretends it's available on all platforms.
916 For the workaround, we call OSX specific functions.
917 Cf. http://developer.apple.com/library/mac/#qa/qa1398/_index.html
918 */
919 #if defined(__APPLE__) && defined(__MACH__)
920 #include <mach/mach.h>
921 #include <mach/mach_time.h>
922 static mach_timebase_info_data_t tinfo;
923 #endif /* __APPLE__ && __MACH__ */
924
Scm_ClockGetTimeMonotonic(u_long * sec,u_long * nsec)925 int Scm_ClockGetTimeMonotonic(u_long *sec, u_long *nsec)
926 {
927 #if defined(__APPLE__) && defined(__MACH__)
928 if (tinfo.denom == 0) {
929 (void)mach_timebase_info(&tinfo);
930 }
931 uint64_t t = mach_absolute_time();
932 uint64_t ns = t * tinfo.numer / tinfo.denom;
933 *sec = ns / 1000000000;
934 *nsec = ns % 1000000000;
935 return TRUE;
936 #elif defined(GAUCHE_WINDOWS)
937 /* On MinGW, clock_gettime is in libwinpthread-1.dll; we avoid depending
938 on it. */
939 LARGE_INTEGER qpf;
940 LARGE_INTEGER qpc;
941 if (!QueryPerformanceFrequency(&qpf)) {
942 Scm_SysError("QueryPerformanceFrequency failed");
943 }
944 if (!QueryPerformanceCounter(&qpc)) {
945 Scm_SysError("QueryPerformanceCounter failed");
946 }
947 *sec = (u_long)(qpc.QuadPart / qpf.QuadPart);
948 *nsec = (u_long)((qpc.QuadPart % qpf.QuadPart) * 1000000000 / qpf.QuadPart);
949 return TRUE;
950 #elif defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
951 ScmTimeSpec ts;
952 int r;
953 SCM_SYSCALL(r, clock_gettime(CLOCK_MONOTONIC, &ts));
954 if (r < 0) Scm_SysError("clock_gettime failed");
955 *sec = (u_long)ts.tv_sec;
956 *nsec = (u_long)ts.tv_nsec;
957 return TRUE;
958 #else /*!HAVE_CLOCK_GETTIME*/
959 *sec = *nsec = 0;
960 return FALSE;
961 #endif /*!HAVE_CLOCK_GETTIME*/
962 }
963
Scm_ClockGetResMonotonic(u_long * sec,u_long * nsec)964 int Scm_ClockGetResMonotonic(u_long *sec, u_long *nsec)
965 {
966 #if defined(__APPLE__) && defined(__MACH__)
967 if (tinfo.denom == 0) {
968 (void)mach_timebase_info(&tinfo);
969 }
970 if (tinfo.numer <= tinfo.denom) {
971 /* The precision is finer than nano seconds, but we can only
972 represent nanosecond resolution. */
973 *sec = 0;
974 *nsec = 1;
975 } else {
976 *sec = 0;
977 *nsec = tinfo.numer / tinfo.denom;
978 }
979 return TRUE;
980 #elif defined(GAUCHE_WINDOWS)
981 /* On MinGW, clock_getres is in libwinpthread-1.dll; we avoid depending
982 on it. */
983 LARGE_INTEGER qpf;
984 if (!QueryPerformanceFrequency(&qpf)) {
985 Scm_SysError("QueryPerformanceFrequency failed");
986 }
987 *sec = 0;
988 *nsec = (u_long)(1000000000 / qpf.QuadPart);
989 if (*nsec == 0) *nsec = 1;
990 return TRUE;
991 #elif defined(HAVE_CLOCK_GETRES) && defined(CLOCK_MONOTONIC)
992 ScmTimeSpec ts;
993 int r;
994 SCM_SYSCALL(r, clock_getres(CLOCK_MONOTONIC, &ts));
995 if (r < 0) Scm_SysError("clock_getres failed");
996 *sec = (u_long)ts.tv_sec;
997 *nsec = (u_long)ts.tv_nsec;
998 return TRUE;
999 #else /*!HAVE_CLOCK_GETRES*/
1000 *sec = *nsec = 0;
1001 return FALSE;
1002 #endif /*!HAVE_CLOCK_GETRES*/
1003 }
1004
1005
1006 /* Experimental. This returns the microsecond-resolution time, wrapped
1007 around the fixnum resolution. In 32-bit architecture it's a bit more
1008 than 1000seconds. Good for micro-profiling, since this guarantees
1009 no allocation. Returned value can be negative. */
Scm_CurrentMicroseconds()1010 long Scm_CurrentMicroseconds()
1011 {
1012 u_long sec, usec;
1013 Scm_GetTimeOfDay(&sec, &usec);
1014 /* we ignore overflow */
1015 usec += sec * 1000000;
1016 usec &= (1UL<<(SCM_SMALL_INT_SIZE+1)) - 1;
1017 if (usec > SCM_SMALL_INT_MAX) usec -= (1UL<<(SCM_SMALL_INT_SIZE+1));
1018 return (long)usec;
1019 }
1020
Scm_CurrentTime(void)1021 ScmObj Scm_CurrentTime(void)
1022 {
1023 u_long sec, usec;
1024 Scm_GetTimeOfDay(&sec, &usec);
1025 return Scm_MakeTime(SCM_SYM_TIME_UTC, sec, usec*1000);
1026 }
1027
Scm_IntSecondsToTime(long sec)1028 ScmObj Scm_IntSecondsToTime(long sec)
1029 {
1030 return Scm_MakeTime(SCM_SYM_TIME_UTC, sec, 0);
1031 }
1032
Scm_Int64SecondsToTime(int64_t sec)1033 ScmObj Scm_Int64SecondsToTime(int64_t sec)
1034 {
1035 return Scm_MakeTime64(SCM_SYM_TIME_UTC, sec, 0);
1036 }
1037
Scm_RealSecondsToTime(double sec)1038 ScmObj Scm_RealSecondsToTime(double sec)
1039 {
1040 double s;
1041 double frac = modf(sec, &s);
1042 int64_t secs = (int64_t)s;
1043 return Scm_MakeTime64(SCM_SYM_TIME_UTC, secs, (long)(frac * 1.0e9));
1044 }
1045
time_type_get(ScmTime * t)1046 static ScmObj time_type_get(ScmTime *t)
1047 {
1048 return t->type;
1049 }
1050
time_type_set(ScmTime * t,ScmObj val)1051 static void time_type_set(ScmTime *t, ScmObj val)
1052 {
1053 if (!SCM_SYMBOLP(val)) {
1054 Scm_Error("time type must be a symbol, but got %S", val);
1055 }
1056 t->type = val;
1057 }
1058
time_sec_get(ScmTime * t)1059 static ScmObj time_sec_get(ScmTime *t)
1060 {
1061 return Scm_MakeInteger64(t->sec);
1062 }
1063
time_sec_set(ScmTime * t,ScmObj val)1064 static void time_sec_set(ScmTime *t, ScmObj val)
1065 {
1066 if (!SCM_REALP(val)) {
1067 Scm_Error("real number required, but got %S", val);
1068 }
1069 t->sec = Scm_GetInteger64(val);
1070 }
1071
time_nsec_get(ScmTime * t)1072 static ScmObj time_nsec_get(ScmTime *t)
1073 {
1074 return Scm_MakeInteger(t->nsec);
1075 }
1076
time_nsec_set(ScmTime * t,ScmObj val)1077 static void time_nsec_set(ScmTime *t, ScmObj val)
1078 {
1079 if (!SCM_REALP(val)) {
1080 Scm_Error("real number required, but got %S", val);
1081 }
1082 long l = Scm_GetInteger(val);
1083 if (l >= 1000000000) {
1084 Scm_Error("nanoseconds out of range: %ld", l);
1085 }
1086 t->nsec = l;
1087 }
1088
1089 static ScmClassStaticSlotSpec time_slots[] = {
1090 SCM_CLASS_SLOT_SPEC("type", time_type_get, time_type_set),
1091 SCM_CLASS_SLOT_SPEC("second", time_sec_get, time_sec_set),
1092 SCM_CLASS_SLOT_SPEC("nanosecond", time_nsec_get, time_nsec_set),
1093 SCM_CLASS_SLOT_SPEC_END()
1094 };
1095
1096 /* time_t and conversion routines */
1097 /* NB: I assume time_t is typedefed to either an integral type or
1098 * a floating point type. As far as I know it is true on most
1099 * current architectures. POSIX doesn't specify so, however; it
1100 * may be some weird structure. If you find such an architecture,
1101 * tweak configure.in and modify the following two functions.
1102 */
Scm_MakeSysTime(time_t t)1103 ScmObj Scm_MakeSysTime(time_t t)
1104 {
1105 #ifdef INTEGRAL_TIME_T
1106 return Scm_MakeIntegerFromUI((unsigned long)t);
1107 #else
1108 double val = (double)t;
1109 return Scm_MakeFlonum(val);
1110 #endif
1111 }
1112
Scm_GetSysTime(ScmObj val)1113 time_t Scm_GetSysTime(ScmObj val)
1114 {
1115 if (SCM_TIMEP(val)) {
1116 #ifdef INTEGRAL_TIME_T
1117 return (time_t)SCM_TIME(val)->sec;
1118 #else
1119 return (time_t)(Scm_Int64ToDouble(SCM_TIME(val)->sec) +
1120 (double)SCM_TIME(val)->nsec/1.0e9);
1121 #endif
1122 } else if (SCM_NUMBERP(val)) {
1123 #ifdef INTEGRAL_TIME_T
1124 return (time_t)Scm_GetUInteger(val);
1125 #else
1126 return (time_t)Scm_GetDouble(val);
1127 #endif
1128 } else {
1129 Scm_Error("bad time value: either a <time> object or a real number is required, but got %S", val);
1130 return (time_t)0; /* dummy */
1131 }
1132 }
1133
Scm_TimeToSeconds(ScmTime * t)1134 ScmObj Scm_TimeToSeconds(ScmTime *t)
1135 {
1136 if (t->nsec) {
1137 return Scm_MakeFlonum((double)(t->sec) + (double)t->nsec/1.0e9);
1138 } else {
1139 return Scm_MakeInteger64(t->sec);
1140 }
1141 }
1142
1143 /* Scheme time -> timespec conversion */
Scm_GetTimeSpec(ScmObj t,ScmTimeSpec * spec)1144 ScmTimeSpec *Scm_GetTimeSpec(ScmObj t, ScmTimeSpec *spec)
1145 {
1146 if (SCM_FALSEP(t)) return NULL;
1147 if (SCM_TIMEP(t)) {
1148 spec->tv_sec = SCM_TIME(t)->sec;
1149 spec->tv_nsec = SCM_TIME(t)->nsec;
1150 } else if (!SCM_REALP(t)) {
1151 Scm_Error("bad timeout spec: <time> object or real number is required, but got %S", t);
1152 } else {
1153 ScmTime *ct = SCM_TIME(Scm_CurrentTime());
1154 spec->tv_sec = ct->sec;
1155 spec->tv_nsec = ct->nsec;
1156 if (SCM_INTP(t)) {
1157 spec->tv_sec += Scm_GetUInteger(t);
1158 } else if (!SCM_REALP(t)) {
1159 Scm_Panic("implementation error: Scm_GetTimeSpec: something wrong");
1160 } else {
1161 double s;
1162 spec->tv_nsec += (unsigned long)(modf(Scm_GetDouble(t), &s)*1.0e9);
1163 spec->tv_sec += (unsigned long)s;
1164 while (spec->tv_nsec >= 1000000000) {
1165 spec->tv_nsec -= 1000000000;
1166 spec->tv_sec += 1;
1167 }
1168 }
1169 }
1170 return spec;
1171 }
1172
1173 /* <sys-tm> object */
1174
tm_allocate(ScmClass * klass,ScmObj initargs SCM_UNUSED)1175 static ScmObj tm_allocate(ScmClass *klass, ScmObj initargs SCM_UNUSED)
1176 {
1177 return SCM_OBJ(SCM_NEW_INSTANCE(ScmSysTm, klass));
1178 }
1179
tm_print(ScmObj obj,ScmPort * port,ScmWriteContext * ctx SCM_UNUSED)1180 static void tm_print(ScmObj obj, ScmPort *port, ScmWriteContext *ctx SCM_UNUSED)
1181 {
1182 #define TM_BUFSIZ 50
1183 char buf[TM_BUFSIZ];
1184 ScmSysTm *st = SCM_SYS_TM(obj);
1185 #if !defined(GAUCHE_WINDOWS)
1186 strftime(buf, TM_BUFSIZ, "%a %b %e %T %Y", &st->tm);
1187 #else /* GAUCHE_WINDOWS */
1188 strftime(buf, TM_BUFSIZ, "%a %b %d %H:%M:%S %Y", &st->tm);
1189 #endif /* GAUCHE_WINDOWS */
1190 Scm_Printf(port, "#<sys-tm \"%s\">", buf);
1191 #undef TM_BUFSIZ
1192 }
1193
1194 SCM_DEFINE_BUILTIN_CLASS(Scm_SysTmClass,
1195 tm_print, NULL, NULL,
1196 tm_allocate, SCM_CLASS_DEFAULT_CPL);
1197
Scm_MakeSysTm(struct tm * tm)1198 ScmObj Scm_MakeSysTm(struct tm *tm)
1199 {
1200 ScmSysTm *st = SCM_NEW(ScmSysTm);
1201 SCM_SET_CLASS(st, SCM_CLASS_SYS_TM);
1202 st->tm = *tm; /* copy */
1203 return SCM_OBJ(st);
1204 }
1205
1206 #define TM_ACCESSOR(name) \
1207 static ScmObj SCM_CPP_CAT(name, _get)(ScmSysTm *tm) { \
1208 return Scm_MakeInteger(tm->tm.name); \
1209 } \
1210 static void SCM_CPP_CAT(name, _set)(ScmSysTm *tm, ScmObj val) { \
1211 if (!SCM_EXACTP(val)) \
1212 Scm_Error("exact integer required, but got %S", val); \
1213 tm->tm.name = Scm_GetInteger(val); \
1214 }
1215
1216 TM_ACCESSOR(tm_sec)
1217 TM_ACCESSOR(tm_min)
1218 TM_ACCESSOR(tm_hour)
1219 TM_ACCESSOR(tm_mday)
1220 TM_ACCESSOR(tm_mon)
1221 TM_ACCESSOR(tm_year)
1222 TM_ACCESSOR(tm_wday)
1223 TM_ACCESSOR(tm_yday)
1224 TM_ACCESSOR(tm_isdst)
1225
1226 static ScmClassStaticSlotSpec tm_slots[] = {
1227 SCM_CLASS_SLOT_SPEC("sec", tm_sec_get, tm_sec_set),
1228 SCM_CLASS_SLOT_SPEC("min", tm_min_get, tm_min_set),
1229 SCM_CLASS_SLOT_SPEC("hour", tm_hour_get, tm_hour_set),
1230 SCM_CLASS_SLOT_SPEC("mday", tm_mday_get, tm_mday_set),
1231 SCM_CLASS_SLOT_SPEC("mon", tm_mon_get, tm_mon_set),
1232 SCM_CLASS_SLOT_SPEC("year", tm_year_get, tm_year_set),
1233 SCM_CLASS_SLOT_SPEC("wday", tm_wday_get, tm_wday_set),
1234 SCM_CLASS_SLOT_SPEC("yday", tm_yday_get, tm_yday_set),
1235 SCM_CLASS_SLOT_SPEC("isdst", tm_isdst_get, tm_isdst_set),
1236 SCM_CLASS_SLOT_SPEC_END()
1237 };
1238
1239 /*
1240 * nanosleep() compatibility layer
1241 */
Scm_NanoSleep(const ScmTimeSpec * req,ScmTimeSpec * rem)1242 int Scm_NanoSleep(const ScmTimeSpec *req, ScmTimeSpec *rem)
1243 {
1244 #if defined(GAUCHE_WINDOWS)
1245 /* Recent mingw32 includes nanosleep but it seems broken, so we keep
1246 using this compatibility code for the time being. */
1247 DWORD msecs = 0;
1248 time_t sec;
1249 u_long overflow = 0, c;
1250 const DWORD MSEC_OVERFLOW = 4294967; /* 4294967*1000 = 0xfffffed8 */
1251
1252 /* It's very unlikely that we overflow msecs, but just in case... */
1253 if (req->tv_sec > 0 || (req->tv_sec == 0 && req->tv_nsec > 0)) {
1254 if ((unsigned)req->tv_sec >= MSEC_OVERFLOW) {
1255 overflow = req->tv_sec / MSEC_OVERFLOW;
1256 sec = req->tv_sec % MSEC_OVERFLOW;
1257 } else {
1258 sec = req->tv_sec;
1259 }
1260 msecs = (sec * 1000 + (req->tv_nsec + 999999)/1000000);
1261 }
1262 Sleep (msecs);
1263 for (c = 0; c < overflow; c++) {
1264 Sleep(MSEC_OVERFLOW * 1000);
1265 }
1266 if (rem) {
1267 rem->tv_sec = rem->tv_nsec = 0;
1268 }
1269 return 0;
1270 #elif defined(HAVE_NANOSLEEP)
1271 return nanosleep(req, rem);
1272 #else /* !defined(HAVE_NANOSLEEP) && !defined(GAUCHE_WINDOWS) */
1273 /* This case should be excluded at the caller site */
1274 errno = EINVAL;
1275 return -1;
1276 #endif
1277 }
1278
1279 /*===============================================================
1280 * Yielding CPU (sched.h, if available)
1281 */
1282
1283 /* If sched_yield is not available, we make the calling thread sleep
1284 small amount of time, hoping there are other threads that can run
1285 in place. */
1286 void
Scm_YieldCPU(void)1287 Scm_YieldCPU(void)
1288 {
1289 #if defined(GAUCHE_WINDOWS)
1290 /* Windows have select(), but it doesn't allow all fds are NULL. */
1291 Sleep(0);
1292 #elif defined(HAVE_SCHED_YIELD)
1293 sched_yield();
1294 #elif defined(HAVE_NANOSLEEP)
1295 /* We can use struct timespec instead of ScmTimeSpec here, for mingw
1296 won't use this path. */
1297 struct timespec spec;
1298 spec.tv_sec = 0;
1299 spec.tv_nsec = 1;
1300 nanosleep(&spec, NULL);
1301 #elif defined(HAVE_SELECT)
1302 struct timeval tv;
1303 tv.tv_sec = 0;
1304 tv.tv_usec = 1;
1305 select(0, NULL, NULL, NULL, &tv);
1306 #else /* the last resort */
1307 sleep(1);
1308 #endif
1309 }
1310
1311 /*===============================================================
1312 * Groups (grp.h)
1313 */
1314
grp_print(ScmObj obj,ScmPort * port,ScmWriteContext * ctx SCM_UNUSED)1315 static void grp_print(ScmObj obj, ScmPort *port, ScmWriteContext *ctx SCM_UNUSED)
1316 {
1317 Scm_Printf(port, "#<sys-group %S>",
1318 SCM_SYS_GROUP(obj)->name);
1319 }
1320
grp_compare(ScmObj x,ScmObj y,int equalp)1321 static int grp_compare(ScmObj x, ScmObj y, int equalp)
1322 {
1323 ScmSysGroup *gx = SCM_SYS_GROUP(x);
1324 ScmSysGroup *gy = SCM_SYS_GROUP(y);
1325
1326 if (equalp) {
1327 return (Scm_EqualP(gx->name, gy->name)
1328 && Scm_EqualP(gx->gid, gy->gid)
1329 && Scm_EqualP(gx->passwd, gy->passwd)
1330 && Scm_EqualP(gx->mem, gy->mem));
1331 } else {
1332 /* This is arbitrary, but having some order allows the object
1333 to be used as a key in treemap. */
1334 int r = Scm_Compare(gx->gid, gy->gid);
1335 if (r != 0) return r;
1336 r = Scm_Compare(gx->name, gy->name);
1337 if (r != 0) return r;
1338 r = Scm_Compare(gx->passwd, gy->passwd);
1339 if (r != 0) return r;
1340 return Scm_Compare(gx->mem, gy->mem);
1341 }
1342 }
1343
grp_hash(ScmObj obj,ScmSmallInt salt,u_long flags)1344 static ScmSmallInt grp_hash(ScmObj obj, ScmSmallInt salt, u_long flags)
1345 {
1346 ScmSysGroup *g = SCM_SYS_GROUP(obj);
1347 ScmSmallInt h = salt;
1348 h = Scm_CombineHashValue(Scm_RecursiveHash(g->name, salt, flags), h);
1349 h = Scm_CombineHashValue(Scm_RecursiveHash(g->gid, salt, flags), h);
1350 h = Scm_CombineHashValue(Scm_RecursiveHash(g->passwd, salt, flags), h);
1351 h = Scm_CombineHashValue(Scm_RecursiveHash(g->mem, salt, flags), h);
1352 return h;
1353 }
1354
1355 SCM_DEFINE_BUILTIN_CLASS(Scm_SysGroupClass,
1356 grp_print, grp_compare, grp_hash,
1357 NULL, SCM_CLASS_DEFAULT_CPL);
1358
make_group(struct group * g)1359 static ScmObj make_group(struct group *g)
1360 {
1361 ScmSysGroup *sg = SCM_NEW(ScmSysGroup);
1362 SCM_SET_CLASS(sg, SCM_CLASS_SYS_GROUP);
1363
1364 sg->name = SCM_MAKE_STR_COPYING(g->gr_name);
1365 #ifdef HAVE_STRUCT_GROUP_GR_PASSWD
1366 sg->passwd = SCM_MAKE_STR_COPYING(g->gr_passwd);
1367 #else
1368 sg->passwd = SCM_FALSE;
1369 #endif
1370 sg->gid = Scm_MakeInteger(g->gr_gid);
1371 sg->mem = Scm_CStringArrayToList((const char**)g->gr_mem, -1,
1372 SCM_MAKSTR_COPYING);
1373 return SCM_OBJ(sg);
1374 }
1375
Scm_GetGroupById(gid_t gid)1376 ScmObj Scm_GetGroupById(gid_t gid)
1377 {
1378 struct group *gdata = getgrgid(gid);
1379 if (gdata == NULL) {
1380 Scm_SigCheck(Scm_VM());
1381 return SCM_FALSE;
1382 } else {
1383 return make_group(gdata);
1384 }
1385 }
1386
Scm_GetGroupByName(ScmString * name)1387 ScmObj Scm_GetGroupByName(ScmString *name)
1388 {
1389 struct group *gdata = getgrnam(Scm_GetStringConst(name));
1390 if (gdata == NULL) {
1391 Scm_SigCheck(Scm_VM());
1392 return SCM_FALSE;
1393 } else {
1394 return make_group(gdata);
1395 }
1396 }
1397
1398 #define GRP_GETTER(name) \
1399 static ScmObj SCM_CPP_CAT3(grp_, name, _get)(ScmSysGroup *s) \
1400 { return s->name; }
1401
1402 GRP_GETTER(name)
1403 GRP_GETTER(gid)
1404 GRP_GETTER(passwd)
1405 GRP_GETTER(mem)
1406
1407 static ScmClassStaticSlotSpec grp_slots[] = {
1408 SCM_CLASS_SLOT_SPEC("name", grp_name_get, NULL),
1409 SCM_CLASS_SLOT_SPEC("gid", grp_gid_get, NULL),
1410 SCM_CLASS_SLOT_SPEC("passwd", grp_passwd_get, NULL),
1411 SCM_CLASS_SLOT_SPEC("mem", grp_mem_get, NULL),
1412 SCM_CLASS_SLOT_SPEC_END()
1413 };
1414
1415 /*===============================================================
1416 * Passwords (pwd.h)
1417 * Patch provided by Yuuki Takahashi (t.yuuki@mbc.nifty.com)
1418 */
1419
pwd_print(ScmObj obj,ScmPort * port,ScmWriteContext * ctx SCM_UNUSED)1420 static void pwd_print(ScmObj obj, ScmPort *port,
1421 ScmWriteContext *ctx SCM_UNUSED)
1422 {
1423 Scm_Printf(port, "#<sys-passwd %S>",
1424 SCM_SYS_PASSWD(obj)->name);
1425 }
1426
1427 SCM_DEFINE_BUILTIN_CLASS_SIMPLE(Scm_SysPasswdClass, pwd_print);
1428
make_passwd(struct passwd * pw)1429 static ScmObj make_passwd(struct passwd *pw)
1430 {
1431 ScmSysPasswd *sp = SCM_NEW(ScmSysPasswd);
1432 SCM_SET_CLASS(sp, SCM_CLASS_SYS_PASSWD);
1433
1434 sp->name = SCM_MAKE_STR_COPYING(pw->pw_name);
1435 sp->uid = Scm_MakeInteger(pw->pw_uid);
1436 sp->gid = Scm_MakeInteger(pw->pw_gid);
1437 #ifdef HAVE_STRUCT_PASSWD_PW_PASSWD
1438 sp->passwd = SCM_MAKE_STR_COPYING(pw->pw_passwd);
1439 #else
1440 sp->passwd = SCM_FALSE;
1441 #endif
1442 #ifdef HAVE_STRUCT_PASSWD_PW_GECOS
1443 sp->gecos = SCM_MAKE_STR_COPYING(pw->pw_gecos);
1444 #else
1445 sp->gecos = SCM_FALSE;
1446 #endif
1447 #ifdef HAVE_STRUCT_PASSWD_PW_CLASS
1448 sp->pwclass = SCM_MAKE_STR_COPYING(pw->pw_class);
1449 #else
1450 sp->pwclass = SCM_FALSE;
1451 #endif
1452 sp->dir = SCM_MAKE_STR_COPYING(pw->pw_dir);
1453 sp->shell = SCM_MAKE_STR_COPYING(pw->pw_shell);
1454 return SCM_OBJ(sp);
1455 }
1456
Scm_GetPasswdById(uid_t uid)1457 ScmObj Scm_GetPasswdById(uid_t uid)
1458 {
1459 struct passwd *pdata = getpwuid(uid);
1460 if (pdata == NULL) {
1461 Scm_SigCheck(Scm_VM());
1462 return SCM_FALSE;
1463 } else {
1464 return make_passwd(pdata);
1465 }
1466 }
1467
Scm_GetPasswdByName(ScmString * name)1468 ScmObj Scm_GetPasswdByName(ScmString *name)
1469 {
1470 struct passwd *pdata = getpwnam(Scm_GetStringConst(name));
1471 if (pdata == NULL) {
1472 Scm_SigCheck(Scm_VM());
1473 return SCM_FALSE;
1474 } else {
1475 return make_passwd(pdata);
1476 }
1477 }
1478
1479 #define PWD_GETTER(name) \
1480 static ScmObj SCM_CPP_CAT3(pwd_, name, _get)(ScmSysPasswd *p) \
1481 { return p->name; }
1482
1483 PWD_GETTER(name)
1484 PWD_GETTER(uid)
1485 PWD_GETTER(gid)
1486 PWD_GETTER(passwd)
1487 PWD_GETTER(gecos)
1488 PWD_GETTER(dir)
1489 PWD_GETTER(shell)
1490 PWD_GETTER(pwclass)
1491
1492 static ScmClassStaticSlotSpec pwd_slots[] = {
1493 SCM_CLASS_SLOT_SPEC("name", pwd_name_get, NULL),
1494 SCM_CLASS_SLOT_SPEC("uid", pwd_uid_get, NULL),
1495 SCM_CLASS_SLOT_SPEC("gid", pwd_gid_get, NULL),
1496 SCM_CLASS_SLOT_SPEC("passwd", pwd_passwd_get, NULL),
1497 SCM_CLASS_SLOT_SPEC("gecos", pwd_gecos_get, NULL),
1498 SCM_CLASS_SLOT_SPEC("dir", pwd_dir_get, NULL),
1499 SCM_CLASS_SLOT_SPEC("shell", pwd_shell_get, NULL),
1500 SCM_CLASS_SLOT_SPEC("class", pwd_pwclass_get, NULL),
1501 SCM_CLASS_SLOT_SPEC_END()
1502 };
1503
1504 /*
1505 * Check if we're suid/sgid-ed.
1506 */
1507
1508 /* We "remember" the initial state, in case issetugid() isn't available.
1509 This isn't perfect, for the process may change euid/egid before calling
1510 Scm_Init(). */
1511 static int initial_ugid_differ = FALSE;
1512
Scm_IsSugid(void)1513 int Scm_IsSugid(void)
1514 {
1515 #if HAVE_ISSETUGID
1516 return issetugid();
1517 #else
1518 return initial_ugid_differ;
1519 #endif /* GAUCHE_WINDOWS */
1520 }
1521
1522 /*===============================================================
1523 * Process management
1524 */
1525
1526 /* Child process management (windows only)
1527 * On windows, parent-child relationship is very weak. The system
1528 * records parent's pid (and we can query it in a very twisted way), but
1529 * the child's process record is discarded upon child's termination
1530 * unless the parent keeps its process handle. To emulate exec-wait
1531 * semantics, we keep the list of child process handles whose status is
1532 * unclaimed.
1533 * One issue is that we cannot wait() for child processes that
1534 * are created by Gauche extension code and not using Scm_SysExec API.
1535 */
1536 #if defined(GAUCHE_WINDOWS)
1537 static struct process_mgr_rec {
1538 ScmObj children;
1539 ScmInternalMutex mutex;
1540 } process_mgr = { SCM_NIL, SCM_INTERNAL_MUTEX_INITIALIZER };
1541
win_process_register(ScmObj process)1542 ScmObj win_process_register(ScmObj process)
1543 {
1544 SCM_ASSERT(Scm_WinProcessP(process));
1545 ScmObj pair = Scm_Cons(process, SCM_NIL);
1546 SCM_INTERNAL_MUTEX_LOCK(process_mgr.mutex);
1547 SCM_SET_CDR_UNCHECKED(pair, process_mgr.children);
1548 process_mgr.children = pair;
1549 SCM_INTERNAL_MUTEX_UNLOCK(process_mgr.mutex);
1550 return process;
1551 }
1552
win_process_unregister(ScmObj process)1553 ScmObj win_process_unregister(ScmObj process)
1554 {
1555 SCM_INTERNAL_MUTEX_LOCK(process_mgr.mutex);
1556 process_mgr.children = Scm_DeleteX(process, process_mgr.children,
1557 SCM_CMP_EQ);
1558 SCM_INTERNAL_MUTEX_UNLOCK(process_mgr.mutex);
1559 return process;
1560 }
1561
win_process_active_child_p(ScmObj process)1562 int win_process_active_child_p(ScmObj process)
1563 {
1564 SCM_INTERNAL_MUTEX_LOCK(process_mgr.mutex);
1565 ScmObj r = Scm_Member(process, process_mgr.children, SCM_CMP_EQ);
1566 SCM_INTERNAL_MUTEX_UNLOCK(process_mgr.mutex);
1567 return !SCM_FALSEP(r);
1568 }
1569
win_process_get_array(int * size)1570 ScmObj *win_process_get_array(int *size /*out*/)
1571 {
1572 SCM_INTERNAL_MUTEX_LOCK(process_mgr.mutex);
1573 ScmSize array_size;
1574 ScmObj *r = Scm_ListToArray(process_mgr.children, &array_size, NULL, TRUE);
1575 *size = (int)array_size;
1576 SCM_INTERNAL_MUTEX_UNLOCK(process_mgr.mutex);
1577 return r;
1578 }
1579
win_process_cleanup(void * data SCM_UNUSED)1580 void win_process_cleanup(void *data SCM_UNUSED)
1581 {
1582 SCM_INTERNAL_MUTEX_LOCK(process_mgr.mutex);
1583 ScmObj cp;
1584 SCM_FOR_EACH(cp, process_mgr.children) {
1585 CloseHandle(Scm_WinHandle(SCM_CAR(cp), SCM_FALSE));
1586 }
1587 process_mgr.children = SCM_NIL;
1588 SCM_INTERNAL_MUTEX_UNLOCK(process_mgr.mutex);
1589 }
1590 #endif /*GAUCHE_WINDOWS*/
1591
1592 /* Command line construction (Windows only)
1593 * In order to use CreateProcess we have to concatenate all arguments
1594 * into one command line string. Proper escaping should be considered
1595 * when the arguments include whitespaces or double-quotes.
1596 * It's pretty silly that we have to do this, since the child process
1597 * crt will re-parse the command line again. Besides, since the parsing
1598 * of the command line is up to each application, THERE IS NO WAY TO
1599 * GUARANTEE TO QUOTE THE ARGUMENTS PROPERLY. This is intolerably
1600 * broken specification.
1601 */
1602 #if defined(GAUCHE_WINDOWS)
win_create_command_line(ScmObj args)1603 char *win_create_command_line(ScmObj args)
1604 {
1605 ScmObj ostr = Scm_MakeOutputStringPort(TRUE);
1606 static ScmObj proc = SCM_UNDEFINED;
1607 SCM_BIND_PROC(proc, "%sys-escape-windows-command-line", Scm_GaucheModule());
1608 ScmObj ap;
1609 SCM_FOR_EACH(ap, args) {
1610 ScmObj escaped = Scm_ApplyRec1(proc, SCM_CAR(ap));
1611 Scm_Printf(SCM_PORT(ostr), "%A ", escaped);
1612 }
1613 ScmObj out = Scm_GetOutputStringUnsafe(SCM_PORT(ostr), 0);
1614 return Scm_GetString(SCM_STRING(out));
1615 }
1616 #endif /*GAUCHE_WINDOWS*/
1617
1618 /* Scm_SysExec
1619 * execvp(), with optionally setting stdios correctly.
1620 *
1621 * iomap argument, when provided, specifies how the open file descriptors
1622 * are treated. If it is not a pair, nothing will be changed for open
1623 * file descriptors. If it is a pair, it must be a list of
1624 * (<to> . <from>), where <tofd> is an integer file descriptor that
1625 * executed process will get, and <from> is either an integer file descriptor
1626 * or a port. If a list is passed to iomap, any file descriptors other
1627 * than specified in the list will be closed before exec().
1628 *
1629 * If forkp arg is TRUE, this function forks before swapping file
1630 * descriptors. It is more reliable way to fork&exec in multi-threaded
1631 * program. In such a case, this function returns Scheme integer to
1632 * show the children's pid. If fork arg is FALSE, this procedure
1633 * of course never returns.
1634 *
1635 * On Windows port, this returns a process handle obejct instead of
1636 * pid of the child process in fork mode. We need to keep handle, or
1637 * the process exit status will be lost when the child process terminates.
1638 */
Scm_SysExec(ScmString * file,ScmObj args,ScmObj iomap,ScmSysSigset * mask,ScmString * dir,int flags)1639 ScmObj Scm_SysExec(ScmString *file, ScmObj args, ScmObj iomap,
1640 ScmSysSigset *mask, ScmString *dir, int flags)
1641 {
1642 int argc = Scm_Length(args);
1643 pid_t pid = 0;
1644 int forkp = flags & SCM_EXEC_WITH_FORK;
1645 int detachp = flags & SCM_EXEC_DETACHED;
1646
1647 if (argc < 1) {
1648 Scm_Error("argument list must have at least one element: %S", args);
1649 }
1650
1651 /* make a C array of C strings */
1652 char **argv = Scm_ListToCStringArray(args, TRUE, NULL);
1653 const char *program = Scm_GetStringConst(file);
1654
1655 /* setting up iomap table */
1656 int *fds = Scm_SysPrepareFdMap(iomap);
1657
1658 /*
1659 * From now on, we have totally different code for Unix and Windows.
1660 */
1661 #if !defined(GAUCHE_WINDOWS)
1662 /*
1663 * Unix path
1664 */
1665 const char *cdir = NULL;
1666 if (dir != NULL) cdir = Scm_GetStringConst(dir);
1667
1668 /* When requested, call fork() here. */
1669 if (forkp) {
1670 SCM_SYSCALL(pid, fork());
1671 if (pid < 0) Scm_SysError("fork failed");
1672 }
1673
1674 if (!forkp || pid == 0) { /* possibly the child process */
1675
1676 /* If we're running the daemon, we fork again to detach the parent,
1677 and also reset the session id. */
1678 if (detachp) {
1679 SCM_SYSCALL(pid, fork());
1680 if (pid < 0) Scm_SysError("fork failed");
1681 if (pid > 0) exit(0); /* not Scm_Exit(), for we don't want to
1682 run the cleanup stuff. */
1683 setsid();
1684 }
1685
1686 if (cdir != NULL) {
1687 if (chdir(cdir) < 0) {
1688 Scm_Panic("chdir to %s failed before executing %s: %s",
1689 cdir, program, strerror(errno));
1690 }
1691 }
1692
1693 Scm_SysSwapFds(fds);
1694 if (mask) {
1695 Scm_ResetSignalHandlers(&mask->set);
1696 Scm_SysSigmask(SIG_SETMASK, mask);
1697 }
1698
1699 execvp(program, (char *const*)argv);
1700 /* here, we failed */
1701 Scm_Panic("exec failed: %s: %s", program, strerror(errno));
1702 }
1703
1704 /* We come here only when fork is requested. */
1705 return Scm_MakeInteger(pid);
1706 #else /* GAUCHE_WINDOWS */
1707 /*
1708 * Windows path
1709 */
1710 const char *cdir = NULL;
1711 (void)mask; /* suppress unused var warning */
1712 (void)pid; /* suppress unused var warning */
1713 if (dir != NULL) {
1714 /* we need full path for CreateProcess. */
1715 dir = SCM_STRING(Scm_NormalizePathname(dir, SCM_PATH_ABSOLUTE|SCM_PATH_CANONICALIZE));
1716 cdir = Scm_GetStringConst(dir);
1717
1718 /* If the program is given in relative pathname,
1719 it must be adjusted relative to the specified directory. */
1720 if (program[0] != '/' && program[0] != '\\'
1721 && !(program[0] && program[1] == ':')) {
1722 ScmDString ds;
1723 int c = cdir[strlen(cdir)-1];
1724 Scm_DStringInit(&ds);
1725 Scm_DStringPutz(&ds, cdir, -1);
1726 if (c != '/' && c != '\\') Scm_DStringPutc(&ds, SCM_CHAR('/'));
1727 Scm_DStringPutz(&ds, program, -1);
1728 program = Scm_DStringGetz(&ds);
1729 }
1730 }
1731
1732 if (forkp) {
1733 TCHAR program_path[MAX_PATH+1], *filepart;
1734 win_redirects *hs = win_prepare_handles(fds);
1735 PROCESS_INFORMATION pi;
1736 DWORD creation_flags = 0;
1737
1738 BOOL pathlen = SearchPath(NULL, SCM_MBS2WCS(program),
1739 _T(".exe"), MAX_PATH, program_path,
1740 &filepart);
1741 if (pathlen == 0) Scm_SysError("cannot find program '%s'", program);
1742 program_path[pathlen] = 0;
1743
1744 STARTUPINFO si;
1745 GetStartupInfo(&si);
1746 if (hs != NULL) {
1747 si.dwFlags |= STARTF_USESTDHANDLES;
1748 si.hStdInput = hs[0].h;
1749 si.hStdOutput = hs[1].h;
1750 si.hStdError = hs[2].h;
1751 }
1752
1753 LPCTSTR curdir = NULL;
1754 if (cdir != NULL) curdir = SCM_MBS2WCS(cdir);
1755
1756 if (detachp) {
1757 creation_flags |= CREATE_NEW_PROCESS_GROUP;
1758 }
1759
1760 BOOL r = CreateProcess(program_path,
1761 SCM_MBS2WCS(win_create_command_line(args)),
1762 NULL, /* process attr */
1763 NULL, /* thread addr */
1764 TRUE, /* inherit handles */
1765 creation_flags, /* creation flags */
1766 NULL, /* nenvironment */
1767 curdir, /* current dir */
1768 &si, /* startup info */
1769 &pi); /* process info */
1770 if (hs != NULL) {
1771 for (int i=0; i<3; i++) {
1772 /* hs[i].h may be a handle duped in win_prepare_handles().
1773 We have to close it in parent process or they would be
1774 inherited to subsequent child process. (The higher-level
1775 Scheme routine closes the open end of the pipe, but that
1776 won't affect the duped one. */
1777 if (hs[i].duped) CloseHandle(hs[i].h);
1778 }
1779 }
1780 if (r == 0) Scm_SysError("spawning %s failed", program);
1781 CloseHandle(pi.hThread); /* we don't need it. */
1782 return win_process_register(Scm_MakeWinProcess(pi.hProcess));
1783 } else {
1784 Scm_SysSwapFds(fds);
1785 if (cdir != NULL) {
1786 if (_chdir(cdir) < 0) {
1787 Scm_SysError("Couldn't chdir to %s", cdir);
1788 }
1789 }
1790 /* TODO: We should probably use Windows API to handle various
1791 options consistently with fork-and-exec case above. */
1792 #if defined(__MINGW64_VERSION_MAJOR)
1793 execvp(program, (char *const*)argv);
1794 #else /* !defined(__MINGW64_VERSION_MAJOR) */
1795 execvp(program, (const char *const*)argv);
1796 #endif /* !defined(__MINGW64_VERSION_MAJOR) */
1797 Scm_Panic("exec failed: %s: %s", program, strerror(errno));
1798 }
1799 return SCM_FALSE; /* dummy */
1800 #endif /* GAUCHE_WINDOWS */
1801 }
1802
1803 /* Two auxiliary functions to support iomap feature. They are exposed
1804 so that the library can implement iomap feature as the same way as
1805 sys-exec.
1806
1807 The first function, Scm_SysPrepareFdMap, walks iomap structure and
1808 prepare a table of file descriptors to modify. The second function,
1809 Scm_SysSwapFds, takes the table and modifies process's file descriptors.
1810
1811 We need to split this feature to two function, since it is unsafe
1812 to raise an error after fork() in multi-threaded environment.
1813 Scm_SysPrepareFdMap may throw an error if passed iomap contains
1814 invalid entries. On the other hand, Scm_SysSwapFds just aborts if
1815 things goes wrong---not only because of the MT-safety issue, but also
1816 it is generally impossible to handle errors reasonably since we don't
1817 even sure we have stdios. And the client code is supposed to call
1818 fork() between these functions.
1819
1820 The client code should treat the returned pointer of Scm_SysPrepareFdMap
1821 opaque, and pass it to Scm_SysSwapFds as is.
1822 */
Scm_SysPrepareFdMap(ScmObj iomap)1823 int *Scm_SysPrepareFdMap(ScmObj iomap)
1824 {
1825 int *fds = NULL;
1826 if (SCM_PAIRP(iomap)) {
1827 int iollen = Scm_Length(iomap);
1828
1829 /* check argument vailidity before duping file descriptors, so that
1830 we can still use Scm_Error */
1831 if (iollen < 0) {
1832 Scm_Error("proper list required for iolist, but got %S", iomap);
1833 }
1834 fds = SCM_NEW_ATOMIC2(int *, 2 * iollen * sizeof(int) + 1);
1835 fds[0] = iollen;
1836 int *tofd = fds + 1;
1837 int *fromfd = fds + 1 + iollen;
1838
1839 ScmObj iop;
1840 int i = 0;
1841 SCM_FOR_EACH(iop, iomap) {
1842 ScmObj port, elt = SCM_CAR(iop);
1843 if (!SCM_PAIRP(elt) || !SCM_INTP(SCM_CAR(elt))
1844 || (!SCM_PORTP(SCM_CDR(elt)) && !SCM_INTP(SCM_CDR(elt)))) {
1845 Scm_Error("bad iomap specification: needs (int . int-or-port): %S", elt);
1846 }
1847 tofd[i] = SCM_INT_VALUE(SCM_CAR(elt));
1848 if (SCM_INTP(SCM_CDR(elt))) {
1849 fromfd[i] = SCM_INT_VALUE(SCM_CDR(elt));
1850 } else {
1851 port = SCM_CDAR(iop);
1852 fromfd[i] = Scm_PortFileNo(SCM_PORT(port));
1853 if (fromfd[i] < 0) {
1854 Scm_Error("iolist requires a port that has associated file descriptor, but got %S",
1855 SCM_CDAR(iop));
1856 }
1857 if (tofd[i] == 0 && !SCM_IPORTP(port))
1858 Scm_Error("input port required to make it stdin: %S",
1859 port);
1860 if (tofd[i] == 1 && !SCM_OPORTP(port))
1861 Scm_Error("output port required to make it stdout: %S",
1862 port);
1863 if (tofd[i] == 2 && !SCM_OPORTP(port))
1864 Scm_Error("output port required to make it stderr: %S",
1865 port);
1866 }
1867 i++;
1868 }
1869 }
1870 return fds;
1871 }
1872
Scm_SysSwapFds(int * fds)1873 void Scm_SysSwapFds(int *fds)
1874 {
1875 if (fds == NULL) return;
1876
1877 int maxfd;
1878 int nfds = fds[0];
1879 int *tofd = fds + 1;
1880 int *fromfd = fds + 1 + nfds;
1881
1882 /* TODO: use getdtablehi if available */
1883 #if !defined(GAUCHE_WINDOWS)
1884 if ((maxfd = sysconf(_SC_OPEN_MAX)) < 0) {
1885 Scm_Panic("failed to get OPEN_MAX value from sysconf");
1886 }
1887 #else /*GAUCHE_WINDOWS*/
1888 maxfd = 256; /* guess it and cross your finger */
1889 #endif /*GAUCHE_WINDOWS*/
1890
1891 /* Dup fromfd to the corresponding tofd. We need to be careful
1892 not to override the destination fd if it will be used. */
1893 for (int i=0; i<nfds; i++) {
1894 if (tofd[i] == fromfd[i]) continue;
1895 for (int j=i+1; j<nfds; j++) {
1896 if (tofd[i] == fromfd[j]) {
1897 int tmp = dup(tofd[i]);
1898 if (tmp < 0) Scm_Panic("dup failed: %s", strerror(errno));
1899 fromfd[j] = tmp;
1900 }
1901 }
1902 if (dup2(fromfd[i], tofd[i]) < 0)
1903 Scm_Panic("dup2 failed: %s", strerror(errno));
1904 }
1905
1906 /* Close unused fds */
1907 for (int fd=0; fd<maxfd; fd++) {
1908 int j;
1909 for (j=0; j<nfds; j++) if (fd == tofd[j]) break;
1910 if (j == nfds) close(fd);
1911 }
1912 }
1913
1914 #if defined(GAUCHE_WINDOWS)
1915 /* Fds is Scm_SysPrepareFdMap returns. */
win_prepare_handles(int * fds)1916 static win_redirects *win_prepare_handles(int *fds)
1917 {
1918 if (fds == NULL) return NULL;
1919
1920 /* For the time being, we only consider stdin, stdout, and stderr. */
1921 win_redirects *hs = SCM_NEW_ATOMIC_ARRAY(win_redirects, 3);
1922 int count = fds[0];
1923
1924 for (int i=0; i<count; i++) {
1925 int to = fds[i+1], from = fds[i+1+count];
1926 if (to >= 0 && to < 3) {
1927 if (from >= 3) {
1928 /* FROM may be a pipe. in that case, it will be closed
1929 in the higher-level, so we shouldn't give
1930 DUPLICATE_CLOSE_SOURCE here. */
1931 HANDLE zh;
1932 if (!DuplicateHandle(GetCurrentProcess(),
1933 (HANDLE)_get_osfhandle(from),
1934 GetCurrentProcess(),
1935 &zh,
1936 0, TRUE,
1937 DUPLICATE_SAME_ACCESS)) {
1938 Scm_SysError("DuplicateHandle failed");
1939 }
1940 hs[to].h = zh;
1941 hs[to].duped = TRUE;
1942 } else {
1943 hs[to].h = (HANDLE)_get_osfhandle(from);
1944 hs[to].duped = FALSE;
1945 }
1946 }
1947 }
1948 for (int i=0; i<3; i++) {
1949 if (hs[i].h == NULL) {
1950 hs[i].h = (HANDLE)_get_osfhandle(i);
1951 hs[i].duped = FALSE;
1952 }
1953 }
1954 return hs;
1955 }
1956 #endif /*GAUCHE_WINDOWS*/
1957
1958 /*===============================================================
1959 * Kill
1960 *
1961 * It is simple on Unix, but on windows it is a lot more involved,
1962 * mainly due to the lack of signals as the means of IPC.
1963 */
Scm_SysKill(ScmObj process,int signal)1964 void Scm_SysKill(ScmObj process, int signal)
1965 {
1966 #if !defined(GAUCHE_WINDOWS)
1967 pid_t pid;
1968 int r;
1969 if (!SCM_INTEGERP(process)) SCM_TYPE_ERROR(process, "integer process id");
1970 pid = Scm_GetInteger(process);
1971 SCM_SYSCALL(r, kill(pid, signal));
1972 if (r < 0) Scm_SysError("kill failed");
1973 #else /*GAUCHE_WINDOWS*/
1974 /* You cannot really "send" signals to other processes on Windows.
1975 We try to emulate SIGKILL and SIGINT by Windows API.
1976 To send a signal to the current process we can use raise(). */
1977 HANDLE p;
1978 BOOL r;
1979 DWORD errcode;
1980 int pid_given = FALSE;
1981 pid_t pid = 0;
1982
1983 if (SCM_INTEGERP(process)) {
1984 pid_given = TRUE; pid = Scm_GetInteger(process);
1985 } else if (Scm_WinProcessP(process)) {
1986 pid = Scm_WinProcessPID(process);
1987 } else {
1988 SCM_TYPE_ERROR(process, "process handle or integer process id");
1989 }
1990
1991 if (signal == SIGKILL) {
1992 if (pid_given) {
1993 p = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
1994 if (p == NULL) Scm_SysError("OpenProcess failed for pid %d", pid);
1995 } else {
1996 p = Scm_WinProcess(process);
1997 }
1998 /* We send 0xff00 + KILL, so that the receiving process (if it is
1999 Gauche) can yield an exit status that indicates it is kill. */
2000 r = TerminateProcess(p, SIGKILL+0xff00);
2001 errcode = GetLastError();
2002 if (pid_given) CloseHandle(p);
2003 SetLastError(errcode);
2004 if (r == 0) Scm_SysError("TerminateProcess failed");
2005 return;
2006 }
2007 /* another idea; we may map SIGTERM to WM_CLOSE message. */
2008
2009 if (signal == 0) {
2010 /* We're supposed to do the error check without actually sending
2011 the signal. For now we just pretend nothing's wrong. */
2012 return;
2013 }
2014 if (pid == getpid()) {
2015 /* we're sending signal to the current process. */
2016 int r = raise(signal); /* r==0 is success */
2017 if (r < 0) Scm_SysError("raise failed");
2018 return;
2019 }
2020 if (signal == SIGINT || signal == SIGABRT) {
2021 /* we can emulate these signals by console event, although the
2022 semantics of process group differ from unix significantly.
2023 Process group id is the same as the pid of the process
2024 that started the group. So you cannot send SIGABRT only
2025 to the process group leader. OTOH, for SIGINT, the windows
2026 manual says it always directed to the specified process,
2027 not the process group, unless pid == 0 */
2028 if (pid < 0) pid = -pid;
2029 r = GenerateConsoleCtrlEvent(pid,
2030 (signal == SIGINT)?
2031 CTRL_C_EVENT : CTRL_BREAK_EVENT);
2032 if (r == 0) {
2033 Scm_SysError("GenerateConsoleCtrlEvent failed for process %d", pid);
2034 }
2035 return;
2036 }
2037 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2038 #endif /*GAUCHE_WINDOWS*/
2039 }
2040
2041 /*===============================================================
2042 * Wait
2043 *
2044 * A wrapper of waitpid. Returns two values---the process object or pid that
2045 * whose status has been taken, and the exit status.
2046 * Again, it is simple on Unix, but on windows it is a lot more involved.
2047 */
2048
Scm_SysWait(ScmObj process,int options)2049 ScmObj Scm_SysWait(ScmObj process, int options)
2050 {
2051 #if !defined(GAUCHE_WINDOWS)
2052 pid_t r;
2053 int status = 0;
2054 if (!SCM_INTEGERP(process)) SCM_TYPE_ERROR(process, "integer process id");
2055 SCM_SYSCALL(r, waitpid(Scm_GetInteger(process), &status, options));
2056 if (r < 0) Scm_SysError("waitpid() failed");
2057 return Scm_Values2(Scm_MakeInteger(r), Scm_MakeInteger(status));
2058 #else /* GAUCHE_WINDOWS */
2059 /* We have four cases
2060 process is integer and < -1 -> not supported.
2061 process is -1 or 0 -> wait for all children (we ignore process group)
2062 process is integer and > 0 -> wait for specific pid
2063 process is #<win:process-handle> -> wait for specified process
2064 The common op is factored out in win_wait_for_handles. */
2065 int r, status = 0;
2066
2067 if (SCM_INTEGERP(process)) {
2068 pid_t pid = Scm_GetInteger(process);
2069 if (pid < -1) {
2070 /* Windows doesn't have the concept of "process group id" */
2071 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2072 Scm_SysError("waitpid cannot wait for process group on Windows.");
2073 }
2074 if (pid > 0) {
2075 /* wait for specific pid */
2076 HANDLE handle = OpenProcess(SYNCHRONIZE|PROCESS_QUERY_INFORMATION,
2077 FALSE, pid);
2078 DWORD errcode;
2079 if (handle == NULL) {
2080 Scm_SysError("OpenProcess failed for pid %d", pid);
2081 }
2082 r = win_wait_for_handles(&handle, 1, options, &status);
2083 errcode = GetLastError();
2084 CloseHandle(handle);
2085 SetLastError(errcode);
2086 if (r == -2) goto timeout;
2087 if (r == -1) goto error;
2088 return Scm_Values2(Scm_MakeInteger(pid), Scm_MakeInteger(status));
2089 }
2090 else {
2091 /* wait for any children. */
2092 ScmObj *children;
2093 int num_children, i;
2094 HANDLE *handles;
2095 children = win_process_get_array(&num_children);
2096 if (num_children == 0) {
2097 SetLastError(ERROR_WAIT_NO_CHILDREN);
2098 Scm_SysError("waitpid failed");
2099 }
2100 handles = SCM_NEW_ATOMIC_ARRAY(HANDLE, num_children);
2101 for (i=0; i<num_children; i++) {
2102 handles[i] = Scm_WinProcess(children[i]);
2103 }
2104 r = win_wait_for_handles(handles, num_children, options, &status);
2105 if (r == -2) goto timeout;
2106 if (r == -1) goto error;
2107 win_process_unregister(children[r]);
2108 return Scm_Values2(children[r], Scm_MakeInteger(status));
2109 }
2110 } else if (Scm_WinProcessP(process)) {
2111 /* wait for the specified process */
2112 HANDLE handle;
2113 if (!win_process_active_child_p(process)) {
2114 SetLastError(ERROR_WAIT_NO_CHILDREN);
2115 Scm_SysError("waitpid failed");
2116 }
2117 handle = Scm_WinProcess(process);
2118 r = win_wait_for_handles(&handle, 1, options, &status);
2119 if (r == -2) goto timeout;
2120 if (r == -1) goto error;
2121 win_process_unregister(process);
2122 return Scm_Values2(process, Scm_MakeInteger(status));
2123 }
2124 timeout:
2125 return Scm_Values2(SCM_MAKE_INT(0), SCM_MAKE_INT(0));
2126 error:
2127 Scm_SysError("waitpid failed");
2128 return SCM_UNDEFINED; /* dummy */
2129 #endif /* GAUCHE_WINDOWS */
2130 }
2131
2132 #if defined(GAUCHE_WINDOWS)
2133 /* aux fn. */
win_wait_for_handles(HANDLE * handles,int nhandles,int options,int * status)2134 static int win_wait_for_handles(HANDLE *handles, int nhandles, int options,
2135 int *status /*out*/)
2136 {
2137 DWORD r = MsgWaitForMultipleObjects(nhandles,
2138 handles,
2139 FALSE,
2140 (options&WNOHANG)? 0 : INFINITE,
2141 0);
2142 if (r == WAIT_FAILED) return -1;
2143 if (r == WAIT_TIMEOUT) return -2;
2144 if ((int)r >= (int)WAIT_OBJECT_0 && (int)r < (int)WAIT_OBJECT_0 + nhandles) {
2145 DWORD exitcode;
2146 int index = r - WAIT_OBJECT_0;
2147 r = GetExitCodeProcess(handles[index], &exitcode);
2148 if (r == 0) return -1;
2149 *status = exitcode;
2150 return index;
2151 }
2152 return -1;
2153 }
2154 #endif /*GAUCHE_WINDOWS*/
2155
2156 /*===============================================================
2157 * select
2158 */
2159
2160 #ifdef HAVE_SELECT
fdset_allocate(ScmClass * klass,ScmObj initargs SCM_UNUSED)2161 static ScmObj fdset_allocate(ScmClass *klass, ScmObj initargs SCM_UNUSED)
2162 {
2163 ScmSysFdset *set = SCM_NEW_INSTANCE(ScmSysFdset, klass);
2164 set->maxfd = -1;
2165 FD_ZERO(&set->fdset);
2166 return SCM_OBJ(set);
2167 }
2168
fdset_copy(ScmSysFdset * fdset)2169 static ScmSysFdset *fdset_copy(ScmSysFdset *fdset)
2170 {
2171 ScmSysFdset *set = SCM_NEW(ScmSysFdset);
2172 SCM_SET_CLASS(set, SCM_CLASS_SYS_FDSET);
2173 set->maxfd = fdset->maxfd;
2174 set->fdset = fdset->fdset;
2175 return set;
2176 }
2177
2178 SCM_DEFINE_BUILTIN_CLASS(Scm_SysFdsetClass, NULL, NULL, NULL,
2179 fdset_allocate, SCM_CLASS_DEFAULT_CPL);
2180
select_checkfd(ScmObj fds)2181 static ScmSysFdset *select_checkfd(ScmObj fds)
2182 {
2183 if (SCM_FALSEP(fds)) return NULL;
2184 if (!SCM_SYS_FDSET_P(fds))
2185 Scm_Error("sys-fdset object or #f is required, but got %S", fds);
2186 return SCM_SYS_FDSET(fds);
2187 }
2188
select_timeval(ScmObj timeout,struct timeval * tm)2189 static struct timeval *select_timeval(ScmObj timeout, struct timeval *tm)
2190 {
2191 if (SCM_FALSEP(timeout)) return NULL;
2192 if (SCM_INTP(timeout)) {
2193 int val = SCM_INT_VALUE(timeout);
2194 if (val < 0) goto badtv;
2195 tm->tv_sec = val / 1000000;
2196 tm->tv_usec = val % 1000000;
2197 return tm;
2198 } else if (SCM_BIGNUMP(timeout)) {
2199 long usec;
2200 ScmObj sec;
2201 if (Scm_Sign(timeout) < 0) goto badtv;
2202 sec = Scm_BignumDivSI(SCM_BIGNUM(timeout), 1000000, &usec);
2203 tm->tv_sec = Scm_GetInteger(sec);
2204 tm->tv_usec = usec;
2205 return tm;
2206 } else if (SCM_FLONUMP(timeout)) {
2207 long val = Scm_GetInteger(timeout);
2208 if (val < 0) goto badtv;
2209 tm->tv_sec = val / 1000000;
2210 tm->tv_usec = val % 1000000;
2211 return tm;
2212 } else if (SCM_PAIRP(timeout) && SCM_PAIRP(SCM_CDR(timeout))) {
2213 ScmObj sec = SCM_CAR(timeout);
2214 ScmObj usec = SCM_CADR(timeout);
2215 long isec, iusec;
2216 if (!Scm_IntegerP(sec) || !Scm_IntegerP(usec)) goto badtv;
2217 isec = Scm_GetInteger(sec);
2218 iusec = Scm_GetInteger(usec);
2219 if (isec < 0 || iusec < 0) goto badtv;
2220 tm->tv_sec = isec;
2221 tm->tv_usec = iusec;
2222 return tm;
2223 }
2224 badtv:
2225 Scm_Error("timeval needs to be a real number (in microseconds) or a list of two integers (seconds and microseconds), but got %S", timeout);
2226 return NULL; /* dummy */
2227 }
2228
select_int(ScmSysFdset * rfds,ScmSysFdset * wfds,ScmSysFdset * efds,ScmObj timeout)2229 static ScmObj select_int(ScmSysFdset *rfds, ScmSysFdset *wfds,
2230 ScmSysFdset *efds, ScmObj timeout)
2231 {
2232 int numfds, maxfds = 0;
2233 struct timeval tm;
2234 if (rfds) maxfds = rfds->maxfd;
2235 if (wfds && wfds->maxfd > maxfds) maxfds = wfds->maxfd;
2236 if (efds && efds->maxfd > maxfds) maxfds = efds->maxfd;
2237
2238 SCM_SYSCALL(numfds,
2239 select(maxfds+1,
2240 (rfds? &rfds->fdset : NULL),
2241 (wfds? &wfds->fdset : NULL),
2242 (efds? &efds->fdset : NULL),
2243 select_timeval(timeout, &tm)));
2244 if (numfds < 0) Scm_SysError("select failed");
2245 return Scm_Values4(Scm_MakeInteger(numfds),
2246 (rfds? SCM_OBJ(rfds) : SCM_FALSE),
2247 (wfds? SCM_OBJ(wfds) : SCM_FALSE),
2248 (efds? SCM_OBJ(efds) : SCM_FALSE));
2249 }
2250
Scm_SysSelect(ScmObj rfds,ScmObj wfds,ScmObj efds,ScmObj timeout)2251 ScmObj Scm_SysSelect(ScmObj rfds, ScmObj wfds, ScmObj efds, ScmObj timeout)
2252 {
2253 ScmSysFdset *r = select_checkfd(rfds);
2254 ScmSysFdset *w = select_checkfd(wfds);
2255 ScmSysFdset *e = select_checkfd(efds);
2256 return select_int((r? fdset_copy(r) : NULL),
2257 (w? fdset_copy(w) : NULL),
2258 (e? fdset_copy(e) : NULL),
2259 timeout);
2260 }
2261
Scm_SysSelectX(ScmObj rfds,ScmObj wfds,ScmObj efds,ScmObj timeout)2262 ScmObj Scm_SysSelectX(ScmObj rfds, ScmObj wfds, ScmObj efds, ScmObj timeout)
2263 {
2264 ScmSysFdset *r = select_checkfd(rfds);
2265 ScmSysFdset *w = select_checkfd(wfds);
2266 ScmSysFdset *e = select_checkfd(efds);
2267 return select_int(r, w, e, timeout);
2268 }
2269
2270 #endif /* HAVE_SELECT */
2271
2272 /*===============================================================
2273 * Environment
2274 */
2275
2276 /* We provide a compatibility layer for getenv/setenv stuff, whose semantics
2277 slightly differ among platforms.
2278
2279 POSIX putenv() has a flaw that passed string can't be freed reliably;
2280 the system may retain the pointer, so the caller can't free it afterwards,
2281 while putenv() itself can't know if the passed pointer is malloc-ed or
2282 static. Some Unixes appears to change the semantics, guaranteeing
2283 the system copies the passed string so that the caller can free it;
2284 however, it's not easy to check which semantics the given platform uses.
2285
2286 What POSIX suggests is setenv() when you want to pass malloc-ed
2287 strings. Unfortunately it is a newer addition and not all platforms
2288 supports it. Windows doesn't, either, but it offers _[w]putenv_s
2289 as an alternative. Unfortunately again, current MinGW doesn't include
2290 _[w]putenv_s in its headers and import libraries.
2291
2292 So, for those platforms, we use putenv/_wputenv. We track allocated
2293 memory in env_string table, keyed by names of envvars, and free them
2294 whenever we put a new definition of envvars we've inserted before.
2295
2296 Another merit of this compatibility layer is to guarantee MT-safeness;
2297 Putenv/setenv aren't usually MT-safe, neither is getenv when environment
2298 is being modified.
2299 */
2300
2301 static ScmInternalMutex env_mutex;
2302 static ScmHashCore env_strings; /* name -> malloc-ed mem.
2303 used with putenv()/_wputenv() to prevent
2304 leak. */
2305
Scm_GetEnv(const char * name)2306 const char *Scm_GetEnv(const char *name)
2307 {
2308 #if defined(GAUCHE_WINDOWS) && defined(UNICODE)
2309 const wchar_t *wname = Scm_MBS2WCS(name);
2310 const char *value = NULL;
2311 (void)SCM_INTERNAL_MUTEX_LOCK(env_mutex);
2312 const wchar_t *wvalue = _wgetenv(wname);
2313 if (wvalue != NULL) {
2314 value = Scm_WCS2MBS(wvalue);
2315 }
2316 (void)SCM_INTERNAL_MUTEX_UNLOCK(env_mutex);
2317 return value;
2318 #else /*!(defined(GAUCHE_WINDOWS) && defined(UNICODE))*/
2319 (void)SCM_INTERNAL_MUTEX_LOCK(env_mutex);
2320 const char *value = SCM_STRDUP(getenv(name));
2321 (void)SCM_INTERNAL_MUTEX_UNLOCK(env_mutex);
2322 return value;
2323 #endif /*!(defined(GAUCHE_WINDOWS) && defined(UNICODE))*/
2324 }
2325
Scm_SetEnv(const char * name,const char * value,int overwrite)2326 void Scm_SetEnv(const char *name, const char *value, int overwrite)
2327 {
2328 #if defined(GAUCHE_WINDOWS) && defined(UNICODE)
2329 /* We need to use _wputenv for wide-character support. Since we pass
2330 the converted strings to OS, we have to allocate them by malloc.
2331 To prevent leak, we register the allocated memory to the global
2332 hash table, and free it when Scm_SetEnv is called with the same NAME
2333 again. */
2334 wchar_t *wname = Scm_MBS2WCS(name);
2335 wchar_t *wvalue = Scm_MBS2WCS(value);
2336 int nlen = wcslen(wname);
2337 int vlen = wcslen(wvalue);
2338 wchar_t *wnameval = (wchar_t*)malloc((nlen+vlen+2)*sizeof(wchar_t));
2339 if (wnameval == NULL) {
2340 Scm_Error("sys-setenv: out of memory");
2341 }
2342 wcscpy(wnameval, wname);
2343 wcscpy(wnameval+nlen, L"=");
2344 wcscpy(wnameval+nlen+1, wvalue);
2345
2346 ScmObj sname = Scm_MakeString(name, -1, -1, SCM_STRING_COPYING);
2347
2348 int result = 0;
2349 wchar_t *prev_mem = NULL;
2350
2351 (void)SCM_INTERNAL_MUTEX_LOCK(env_mutex);
2352 if (overwrite || _wgetenv(wname) == NULL) {
2353 result = _wputenv(wnameval);
2354 if (result >= 0) {
2355 ScmDictEntry *e = Scm_HashCoreSearch(&env_strings,
2356 (intptr_t)sname,
2357 SCM_DICT_CREATE);
2358 /* SCM_DICT_VALUE is only for ScmObj, so we directly access value
2359 field here. */
2360 prev_mem = (wchar_t*)e->value;
2361 e->value = (intptr_t)wnameval;
2362 }
2363 }
2364 (void)SCM_INTERNAL_MUTEX_UNLOCK(env_mutex);
2365
2366 if (result < 0) {
2367 free(wnameval);
2368 Scm_SysError("setenv failed on '%s=%s'", name, value);
2369 }
2370 if (prev_mem != NULL) {
2371 free(prev_mem);
2372 }
2373 #elif defined(HAVE_SETENV)
2374 (void)SCM_INTERNAL_MUTEX_LOCK(env_mutex);
2375 int r = setenv(name, value, overwrite);
2376 (void)SCM_INTERNAL_MUTEX_UNLOCK(env_mutex);
2377 if (r < 0) Scm_SysError("setenv failed on '%s=%s'", name, value);
2378 #elif defined(HAVE_PUTENV)
2379 int nlen = (int)strlen(name);
2380 int vlen = (int)strlen(value);
2381 char *nameval = (char*)malloc(nlen+vlen+2);
2382 if (nameval == NULL) {
2383 Scm_Error("sys-setenv: out of memory");
2384 }
2385 strcpy(nameval, name);
2386 strcpy(nameval+nlen, "=");
2387 strcpy(nameval+nlen+1, value);
2388
2389 ScmObj sname = Scm_MakeString(name, -1, -1, SCM_STRING_COPYING);
2390
2391 int result = 0;
2392 char *prev_mem = NULL;
2393
2394 (void)SCM_INTERNAL_MUTEX_LOCK(env_mutex);
2395 if (overwrite || getenv(name) == NULL) {
2396 result = putenv(nameval);
2397 if (result >= 0) {
2398 ScmDictEntry *e = Scm_HashCoreSearch(&env_strings,
2399 (intptr_t)sname,
2400 SCM_DICT_CREATE);
2401 /* SCM_DICT_VALUE is only for ScmObj, so we directly access value
2402 field here. */
2403 prev_mem = (char*)e->value;
2404 e->value = (intptr_t)nameval;
2405 }
2406 }
2407 (void)SCM_INTERNAL_MUTEX_UNLOCK(env_mutex);
2408 if (result < 0) {
2409 free (nameval);
2410 Scm_SysError("putenv failed on '%s=%s'", name, value);
2411 }
2412 if (prev_mem != NULL) {
2413 free(prev_mem);
2414 }
2415 #else /* !HAVE_SETENV && !HAVE_PUTENV */
2416 /* We can't do much. we may replace environ by ourselves, but
2417 it is unlikely that the system have extern environ and not putenv.
2418 */
2419 Scm_Error("neither setenv nor putenv is supported on this platform.");
2420 #endif
2421 }
2422
2423 /* Returns the system's environment table as a list of strings.
2424 Each string is in the format of "key=value". */
Scm_Environ(void)2425 ScmObj Scm_Environ(void)
2426 {
2427 #if defined(GAUCHE_WINDOWS)
2428 #define ENV_BUFSIZ 64
2429 LPVOID ss = GetEnvironmentStrings();
2430 ScmObj h = SCM_NIL, t = SCM_NIL;
2431 TCHAR *cp = (TCHAR*)ss, *pp;
2432 TCHAR sbuf[ENV_BUFSIZ], *buf=sbuf;
2433 int bsize = ENV_BUFSIZ, size;
2434
2435 do {
2436 for (pp=cp; *pp; pp++) /*proceed ptr*/;
2437 size = (int)(pp - cp) + 1;
2438 if (size >= bsize) {
2439 buf = SCM_NEW_ATOMIC_ARRAY(TCHAR, size);
2440 bsize = size;
2441 }
2442 memcpy(buf, cp, size*sizeof(TCHAR));
2443 SCM_APPEND1(h, t, SCM_MAKE_STR_COPYING(SCM_WCS2MBS(buf)));
2444 cp = pp+1;
2445 } while (pp[1] != 0);
2446 FreeEnvironmentStrings(ss);
2447 return h;
2448 #else
2449 (void)SCM_INTERNAL_MUTEX_LOCK(env_mutex);
2450 # if defined(HAVE_CRT_EXTERNS_H)
2451 char **environ = *_NSGetEnviron(); /* OSX Hack*/
2452 # endif
2453 ScmObj r = (environ == NULL
2454 ? SCM_NIL
2455 : Scm_CStringArrayToList((const char**)environ, -1,
2456 SCM_STRING_COPYING));
2457 (void)SCM_INTERNAL_MUTEX_UNLOCK(env_mutex);
2458 return r;
2459 #endif /*!GAUCHE_WINDOWS*/
2460 }
2461
Scm_UnsetEnv(const char * name)2462 void Scm_UnsetEnv(const char *name)
2463 {
2464 #if defined(HAVE_UNSETENV)
2465 /* NB: If we HAVE_SETENV, we don't have any entries in env_strings,
2466 so the lookup of snv_strings is a waste; but the result is always
2467 NULL and it won't harm the operation, and we expect sys-unsetenv
2468 is rarely used, so we just let it waste cpu cycles. */
2469 char *prev_mem = NULL;
2470 ScmObj sname = Scm_MakeString(name, -1, -1, SCM_STRING_COPYING);
2471 (void)SCM_INTERNAL_MUTEX_LOCK(env_mutex);
2472 int r = unsetenv(name);
2473 ScmDictEntry *e = Scm_HashCoreSearch(&env_strings,
2474 (intptr_t)sname,
2475 SCM_DICT_DELETE);
2476 if (e != NULL) { prev_mem = (char*)e->value; e->value = (intptr_t)NULL; }
2477 (void)SCM_INTERNAL_MUTEX_UNLOCK(env_mutex);
2478 if (r < 0) Scm_SysError("unsetenv failed on %s", name);
2479 if (prev_mem != NULL) free(prev_mem);
2480 #else /*!HAVE_UNSETENV*/
2481 (void)name; /* suppress unused var warning */
2482 Scm_Error("sys-unsetenv is not supported on this platform.");
2483 #endif /*!HAVE_UNSETENV*/
2484 }
2485
Scm_ClearEnv()2486 void Scm_ClearEnv()
2487 {
2488 #if defined(HAVE_CLEARENV)
2489 /* As in Scm_UnsetEnv, we don't need env_strings business if
2490 we HAVE_SETENV, but it does no harm either. */
2491 (void)SCM_INTERNAL_MUTEX_LOCK(env_mutex);
2492 int r = clearenv();
2493 ScmHashIter iter;
2494 Scm_HashIterInit(&iter, &env_strings);
2495 ScmDictEntry *e;
2496 while ((e = Scm_HashIterNext(&iter)) != NULL) {
2497 free((void*)e->value);
2498 e->value = (intptr_t)NULL;
2499 }
2500 Scm_HashCoreClear(&env_strings);
2501 (void)SCM_INTERNAL_MUTEX_UNLOCK(env_mutex);
2502 if (r < 0) Scm_SysError("clearenv failed");
2503 #else /*!HAVE_UNSETENV*/
2504 Scm_Error("sys-clearenv is not supported on this platform.");
2505 #endif /*!HAVE_UNSETENV*/
2506 }
2507
2508 /*===============================================================
2509 * Closer-to-metal thingy
2510 */
2511
2512 /* Try to find # of available processors. If we don't know how to
2513 find that info on the platform, we fall back to 1.
2514 If GAUCHE_AVAILABLE_PROCESSORS environment variable is defined and
2515 has the value interpreted as a positive integer, we use that value
2516 instead.
2517 */
Scm_AvailableProcessors()2518 int Scm_AvailableProcessors()
2519 {
2520 const char *env = Scm_GetEnv("GAUCHE_AVAILABLE_PROCESSORS");
2521 if (env && env[0] != '\0') {
2522 char *ep;
2523 long v = strtol(env, &ep, 10);
2524 if (v > 0 && *ep == '\0') return (int)v;
2525 }
2526 #if !defined(GAUCHE_WINDOWS)
2527 #if defined(_SC_NPROCESSORS_ONLN)
2528 return (int)sysconf(_SC_NPROCESSORS_ONLN);
2529 #else /*!defined(_SC_NPROCESSORS_ONLN)*/
2530 return 1; /* fallback */
2531 #endif /*!defined(_SC_NPROCESSORS_ONLN)*/
2532 #else /*defined(GAUCHE_WINDOWS)*/
2533 SYSTEM_INFO sysinfo;
2534 GetSystemInfo( &sysinfo );
2535 return (int)sysinfo.dwNumberOfProcessors;
2536 #endif /*defined(GAUCHE_WINDOWS)*/
2537 }
2538
2539 /*===============================================================
2540 * Emulation layer for Windows
2541 */
2542 #if defined(GAUCHE_WINDOWS)
2543
2544 /* Dynamically obtain an entry point that may not be available on
2545 all Windows versions. If throw_error is TRUE, throws an error
2546 if DLL mapping failed, or entry cannot be found. Otherwise,
2547 returns NULL on error. */
get_api_entry(const TCHAR * module,const char * proc,int throw_error)2548 static void *get_api_entry(const TCHAR *module, const char *proc,
2549 int throw_error)
2550 {
2551 void *entry;
2552 HMODULE m = LoadLibrary(module);
2553 if (m == NULL) {
2554 if (throw_error)
2555 Scm_SysError("LoadLibrary(%s) failed", SCM_WCS2MBS(module));
2556 else
2557 return NULL;
2558 }
2559 entry = (void*)GetProcAddress(m, proc);
2560 if (entry == NULL) {
2561 DWORD errcode = GetLastError();
2562 FreeLibrary(m);
2563 SetLastError(errcode);
2564 if (throw_error)
2565 Scm_SysError("GetProcAddress(%s) failed", proc);
2566 else
2567 return NULL;
2568 }
2569 return entry;
2570 }
2571
2572 /* Scan the processes to find out either the parent process, or the
2573 child processes of the current process. I cannot imagine why we
2574 need such a hassle to perform this kind of simple task, but this
2575 is the way the MS document suggests.
2576 Returns a single Scheme integer of the parent process id if childrenp
2577 is FALSE; returns a list of Scheme integers of child process ids if
2578 childrenp is TRUE. */
get_relative_processes(int childrenp)2579 static ScmObj get_relative_processes(int childrenp)
2580 {
2581 HANDLE snapshot;
2582 PROCESSENTRY32 entry;
2583 DWORD myid = GetCurrentProcessId(), parentid = 0;
2584 int found = FALSE;
2585 ScmObj h = SCM_NIL, t = SCM_NIL; /* children pids */
2586
2587 snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
2588 if (snapshot == INVALID_HANDLE_VALUE) {
2589 Scm_Error("couldn't take process snapshot in getppid()");
2590 }
2591 entry.dwSize = sizeof(PROCESSENTRY32);
2592 if (!Process32First(snapshot, &entry)) {
2593 CloseHandle(snapshot);
2594 Scm_Error("Process32First failed in getppid()");
2595 }
2596 do {
2597 if (childrenp) {
2598 if (entry.th32ParentProcessID == myid) {
2599 SCM_APPEND1(h, t, Scm_MakeInteger(entry.th32ProcessID));
2600 }
2601 } else {
2602 if (entry.th32ProcessID == myid) {
2603 parentid = entry.th32ParentProcessID;
2604 found = TRUE;
2605 break;
2606 }
2607 }
2608 } while (Process32Next(snapshot, &entry));
2609 CloseHandle(snapshot);
2610
2611 if (childrenp) {
2612 return h;
2613 } else {
2614 if (!found) {
2615 Scm_Error("couldn't find the current process entry in getppid()");
2616 }
2617 return Scm_MakeInteger(parentid);
2618 }
2619 }
2620
2621 /* Retrieve PID from windows process handle wrapper. */
2622
Scm_WinProcessPID(ScmObj handle)2623 pid_t Scm_WinProcessPID(ScmObj handle)
2624 {
2625 /* GetProcessId seems very primitive procedure, but somehow Windows
2626 only provides it in XP SP1 or after. Before that it seems you
2627 can only map pid -> handle by OpenProcess but you can't do the
2628 reverse (except you enumerate all process ids, calling OpenProcess
2629 on each and look for one whose handle matches the given handle.
2630 Sounds expensive. */
2631 static DWORD (WINAPI *pGetProcessId)(HANDLE) = NULL;
2632 static int queried = FALSE;
2633
2634 if (!Scm_WinProcessP(handle)) {
2635 SCM_TYPE_ERROR(handle, "<win:handle process>");
2636 }
2637
2638 if (pGetProcessId == NULL) {
2639 if (queried) return (pid_t)-1;
2640 pGetProcessId = get_api_entry(_T("kernel32.dll"), "GetProcessId",
2641 FALSE);
2642 if (pGetProcessId == NULL) {
2643 queried = TRUE;
2644 return (pid_t)-1;
2645 }
2646 }
2647 return pGetProcessId(Scm_WinProcess(handle));
2648 }
2649
2650 /*
2651 * Users and groups
2652 * Kinda Kluge, since we don't have "user id" associated with each
2653 * user. (If a domain server is active, Windows security manager seems
2654 * to assign an unique user id for every user; but it doesn't seem available
2655 * for stand-alone machine.)
2656 */
2657
convert_user(const USER_INFO_2 * wuser,struct passwd * res)2658 static void convert_user(const USER_INFO_2 *wuser, struct passwd *res)
2659 {
2660 res->pw_name = (const char*)SCM_WCS2MBS(wuser->usri2_name);
2661 res->pw_passwd = "*";
2662 res->pw_uid = 0;
2663 res->pw_gid = 0;
2664 res->pw_comment = (const char*)SCM_WCS2MBS(wuser->usri2_comment);
2665 res->pw_gecos = (const char*)SCM_WCS2MBS(wuser->usri2_full_name);
2666 res->pw_dir = (const char*)SCM_WCS2MBS(wuser->usri2_home_dir);
2667 res->pw_shell = "";
2668 }
2669
2670 /* Arrgh! thread unsafe! just for the time being...*/
2671 static struct passwd pwbuf = { "dummy", "", 0, 0, "", "", "", "" };
2672
getpwnam(const char * name)2673 struct passwd *getpwnam(const char *name)
2674 {
2675 USER_INFO_2 *res;
2676 if (NetUserGetInfo(NULL, (LPCWSTR)SCM_MBS2WCS(name), 2, (LPBYTE*)&res)
2677 != NERR_Success) {
2678 return NULL;
2679 }
2680 convert_user(res, &pwbuf);
2681 NetApiBufferFree(res);
2682 return &pwbuf;
2683 }
2684
getpwuid(uid_t uid SCM_UNUSED)2685 struct passwd *getpwuid(uid_t uid SCM_UNUSED)
2686 {
2687 /* for the time being, we just ignore uid and returns the current
2688 user info. */
2689 #define NAMELENGTH 256
2690 TCHAR buf[NAMELENGTH];
2691 DWORD len = NAMELENGTH;
2692 if (GetUserName(buf, &len) == 0) {
2693 return NULL;
2694 }
2695 return getpwnam(SCM_WCS2MBS(buf));
2696 }
2697
2698 static struct group dummy_group = {
2699 "dummy",
2700 "",
2701 100,
2702 NULL
2703 };
2704
getgrgid(gid_t gid SCM_UNUSED)2705 struct group *getgrgid(gid_t gid SCM_UNUSED)
2706 {
2707 return &dummy_group;
2708 }
2709
getgrnam(const char * name SCM_UNUSED)2710 struct group *getgrnam(const char *name SCM_UNUSED)
2711 {
2712 return &dummy_group;
2713 }
2714
2715 /* Kluge kluge kluge */
getuid(void)2716 uid_t getuid(void)
2717 {
2718 return 0;
2719 }
2720
geteuid(void)2721 uid_t geteuid(void)
2722 {
2723 return 0;
2724 }
2725
getgid(void)2726 gid_t getgid(void)
2727 {
2728 return 0;
2729 }
2730
getegid(void)2731 gid_t getegid(void)
2732 {
2733 return 0;
2734 }
2735
getppid(void)2736 pid_t getppid(void)
2737 {
2738 ScmObj ppid = get_relative_processes(FALSE);
2739 return Scm_GetInteger(ppid);
2740 }
2741
getlogin(void)2742 const char *getlogin(void)
2743 {
2744 static TCHAR buf[256]; /* this isn't thread-safe, but getlogin() is
2745 inherently thread-unsafe call anyway */
2746 DWORD size = sizeof(buf)/sizeof(TCHAR);
2747 BOOL r;
2748 r = GetUserName(buf, &size);
2749 if (r) {
2750 return SCM_WCS2MBS(buf);
2751 } else {
2752 return NULL;
2753 }
2754 }
2755
times(struct tms * info)2756 clock_t times(struct tms *info)
2757 {
2758 HANDLE process = GetCurrentProcess();
2759 FILETIME ctime, xtime, utime, stime;
2760 int64_t val;
2761 const int factor = 10000000/CLK_TCK;
2762 const int bias = factor/2;
2763
2764 if (!GetProcessTimes(process, &ctime, &xtime, &stime, &utime)) {
2765 Scm_SysError("GetProcessTimes failed");
2766 }
2767 val = ((int64_t)stime.dwHighDateTime << 32) + stime.dwLowDateTime;
2768 info->tms_stime = (u_int)((val+bias) / factor);
2769 val = ((int64_t)utime.dwHighDateTime << 32) + utime.dwLowDateTime;
2770 info->tms_utime = (u_int)((val+bias) / factor);
2771
2772 info->tms_cstime = 0;
2773 info->tms_cutime = 0;
2774 return 0;
2775 }
2776
2777
2778
2779 /*
2780 * Other obscure stuff
2781 */
2782
fork(void)2783 int fork(void)
2784 {
2785 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2786 return -1;
2787 }
2788
pipe(int fd[])2789 int pipe(int fd[])
2790 {
2791 #define PIPE_BUFFER_SIZE 512
2792 /* NB: We create pipe with NOINHERIT to avoid complication when spawning
2793 child process. Scm_SysExec will dups the handle with inheritable flag
2794 for the children. */
2795 int r = _pipe(fd, PIPE_BUFFER_SIZE, O_BINARY|O_NOINHERIT);
2796 return r;
2797 }
2798
2799 /* If the given handle points to a pipe, returns its name.
2800 As of Oct 2016, mingw headers does not include
2801 GetFileInformationByHandleEx API, so we provide alternative. */
2802
2803 typedef struct {
2804 DWORD FileNameLength;
2805 WCHAR FileName[1];
2806 } X_FILE_NAME_INFO;
2807
2808 typedef enum {
2809 X_FileNameInfo = 2
2810 } X_FILE_INFO_BY_HANDLE_CLASS;
2811
Scm_WinGetPipeName(HANDLE h)2812 ScmObj Scm_WinGetPipeName(HANDLE h)
2813 {
2814 if (GetFileType(h) != FILE_TYPE_PIPE) return SCM_FALSE;
2815 static BOOL (WINAPI *pGetFileInformationByHandleEx)(HANDLE,
2816 X_FILE_INFO_BY_HANDLE_CLASS,
2817 LPVOID, DWORD) = NULL;
2818
2819 if (pGetFileInformationByHandleEx == NULL) {
2820 pGetFileInformationByHandleEx =
2821 get_api_entry(_T("kernel32.dll"),
2822 "GetFileInformationByHandleEx",
2823 FALSE);
2824 }
2825 if (pGetFileInformationByHandleEx == NULL) return SCM_FALSE;
2826
2827 DWORD size = sizeof(X_FILE_NAME_INFO) + sizeof(WCHAR)*MAX_PATH;
2828 X_FILE_NAME_INFO *info = SCM_MALLOC_ATOMIC(size);
2829 BOOL r = pGetFileInformationByHandleEx(h, X_FileNameInfo, info, size);
2830 if (!r) return SCM_FALSE;
2831
2832 info->FileName[info->FileNameLength / sizeof(WCHAR)] = 0;
2833 return SCM_MAKE_STR_COPYING(SCM_WCS2MBS(info->FileName));
2834 }
2835
ttyname(int desc SCM_UNUSED)2836 char *ttyname(int desc SCM_UNUSED)
2837 {
2838 return NULL;
2839 }
2840
2841 #if !HAVE_UTIMENSAT
2842 /* Emulate utimensat() by utime(). For MinGW. */
utimensat(int dirfd SCM_UNUSED,const char * path,const ScmTimeSpec times[2],int flags SCM_UNUSED)2843 int utimensat(int dirfd SCM_UNUSED,
2844 const char *path,
2845 const ScmTimeSpec times[2],
2846 int flags SCM_UNUSED)
2847 {
2848 struct utimbuf buf;
2849 buf.actime = times[0].tv_sec;
2850 buf.modtime = times[1].tv_sec;
2851
2852 if (times[0].tv_nsec == UTIME_NOW) {
2853 buf.actime = time(NULL);
2854 }
2855 if (times[1].tv_nsec == UTIME_NOW) {
2856 buf.modtime = time(NULL);
2857 }
2858 /* TODO: UTIME_OMIT case */
2859
2860 return utime(path, &buf);
2861 }
2862 #endif /*!HAVE_UTIMENSAT*/
2863
2864 #ifndef __MINGW64_VERSION_MAJOR /* MinGW64 has truncate and ftruncate */
2865
win_truncate(HANDLE file,off_t len)2866 static int win_truncate(HANDLE file, off_t len)
2867 {
2868 typedef BOOL (WINAPI *pSetEndOfFile_t)(HANDLE);
2869 typedef BOOL (WINAPI *pSetFilePointer_t)(HANDLE, LONG, PLONG, DWORD);
2870
2871 static pSetEndOfFile_t pSetEndOfFile = NULL;
2872 static pSetFilePointer_t pSetFilePointer = NULL;
2873
2874 DWORD r1;
2875 BOOL r2;
2876
2877 if (pSetEndOfFile == NULL) {
2878 pSetEndOfFile = (pSetEndOfFile_t)get_api_entry(_T("kernel32.dll"),
2879 "SetEndOfFile",
2880 FALSE);
2881 if (pSetEndOfFile == NULL) return -1;
2882 }
2883 if (pSetFilePointer == NULL) {
2884 pSetFilePointer = (pSetFilePointer_t)get_api_entry(_T("kernel32.dll"),
2885 "SetFilePointer",
2886 FALSE);
2887 if (pSetFilePointer == NULL) return -1;
2888 }
2889
2890 /* TODO: 64bit size support! */
2891 r1 = pSetFilePointer(file, (LONG)len, NULL, FILE_BEGIN);
2892 if (r1 == INVALID_SET_FILE_POINTER) return -1;
2893 r2 = pSetEndOfFile(file);
2894 if (r2 == 0) return -1;
2895 return 0;
2896 }
2897
truncate(const char * path,off_t len)2898 int truncate(const char *path, off_t len)
2899 {
2900 HANDLE file;
2901 int r;
2902
2903 file = CreateFile(SCM_MBS2WCS(path), GENERIC_WRITE,
2904 FILE_SHARE_READ|FILE_SHARE_WRITE,
2905 NULL, OPEN_EXISTING, 0, NULL);
2906 if (file == INVALID_HANDLE_VALUE) return -1;
2907 r = win_truncate(file, len);
2908 if (r < 0) {
2909 DWORD errcode = GetLastError();
2910 CloseHandle(file);
2911 SetLastError(errcode);
2912 return -1;
2913 }
2914 CloseHandle(file);
2915 return 0;
2916 }
2917
ftruncate(int fd,off_t len)2918 int ftruncate(int fd, off_t len)
2919 {
2920 HANDLE h = (HANDLE)_get_osfhandle(fd);
2921 int r;
2922 if (h == INVALID_HANDLE_VALUE) return -1;
2923 r = win_truncate(h, len);
2924 if (r < 0) return -1;
2925 return 0;
2926 }
2927
2928 #endif /* __MINGW64_VERSION_MAJOR */
2929
alarm(unsigned int seconds SCM_UNUSED)2930 unsigned int alarm(unsigned int seconds SCM_UNUSED)
2931 {
2932 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2933 Scm_SysError("alarm");
2934 return 0;
2935 }
2936
2937 /* file links */
link(const char * existing,const char * newpath)2938 int link(const char *existing, const char *newpath)
2939 {
2940 /* CreateHardLink only exists in WinNT or later. Officially we don't
2941 support anything before, but let's try to be kind for the legacy
2942 system ...*/
2943 typedef BOOL (WINAPI *pCreateHardLink_t)(LPTSTR, LPTSTR,
2944 LPSECURITY_ATTRIBUTES);
2945 static pCreateHardLink_t pCreateHardLink = NULL;
2946 BOOL r;
2947 #if defined(UNICODE)
2948 #define CREATEHARDLINK "CreateHardLinkW"
2949 #else
2950 #define CREATEHARDLINK "CreateHardLinkA"
2951 #endif
2952
2953 if (pCreateHardLink == NULL) {
2954 pCreateHardLink = (pCreateHardLink_t)get_api_entry(_T("kernel32.dll"),
2955 CREATEHARDLINK,
2956 TRUE);
2957 }
2958 r = pCreateHardLink((LPTSTR)SCM_MBS2WCS(newpath),
2959 (LPTSTR)SCM_MBS2WCS(existing), NULL);
2960 return r? 0 : -1;
2961 }
2962
2963 /* Winsock requires some obscure initialization.
2964 We perform initialization here, since winsock module is used
2965 in both gauche.net and gauche.auxsys. */
2966 static WSADATA wsaData;
2967
init_winsock(void)2968 static void init_winsock(void)
2969 {
2970 int opt;
2971 int r = WSAStartup(MAKEWORD(2,2), &wsaData);
2972 if (r != 0) {
2973 SetLastError(r);
2974 Scm_SysError("WSAStartup failed");
2975 }
2976 /* windows voodoo to make _open_osfhandle magic work */
2977 opt = SO_SYNCHRONOUS_NONALERT;
2978 r = setsockopt(INVALID_SOCKET, SOL_SOCKET,
2979 SO_OPENTYPE, (char*)&opt, sizeof(opt));
2980 if (r == SOCKET_ERROR) {
2981 Scm_SysError("winsock initialization failed");
2982 }
2983 }
2984
fini_winsock(void * data SCM_UNUSED)2985 static void fini_winsock(void *data SCM_UNUSED)
2986 {
2987 (void)WSACleanup();
2988 }
2989
2990 /* Win32 thread support. See also gauche/wthread.h */
2991
2992 #if defined(GAUCHE_USE_WTHREADS)
2993
Scm__WinCreateMutex()2994 HANDLE Scm__WinCreateMutex()
2995 {
2996 HANDLE m = CreateMutex(NULL, FALSE, NULL);
2997 if (m == NULL) Scm_SysError("couldn't create a mutex");
2998 return m;
2999 }
3000
Scm__WinMutexLock(HANDLE mutex)3001 int Scm__WinMutexLock(HANDLE mutex)
3002 {
3003 DWORD r = WaitForSingleObject(mutex, INFINITE);
3004 if (r == WAIT_OBJECT_0) return 0;
3005 else return 1; /* TODO: proper error handling */
3006 }
3007
3008 /* Win32 conditional variable emulation.
3009 Native condition variable support is only available on Windows Vista
3010 and later. We don't want to drop XP support (yet), so we avoid using
3011 it. Instead we emulate posix condition variable semantics.
3012 We enhanced the implementation described as the SignalObjectAndWait
3013 solution shown in
3014 <http://www1.cse.wustl.edu/~schmidt/win32-cv-1.html>
3015 */
Scm__InternalCondInit(ScmInternalCond * cond)3016 void Scm__InternalCondInit(ScmInternalCond *cond)
3017 {
3018 cond->numWaiters = 0;
3019 cond->broadcast = FALSE;
3020 cond->mutex = NULL; /* set by the first CondWait */
3021 cond->sem = CreateSemaphore(NULL, /* no security */
3022 0, 0x7fffffff, /* initial and max val */
3023 NULL); /* name */
3024 if (cond->sem == NULL) {
3025 Scm_SysError("couldn't create a semaphore for a condition variable");
3026 }
3027 cond->done = CreateEvent(NULL, /* no security */
3028 FALSE, /* auto-reset */
3029 FALSE, /* initially non-signalled */
3030 NULL); /* name */
3031 if (cond->done == NULL) {
3032 DWORD err = GetLastError();
3033 CloseHandle(cond->sem);
3034 SetLastError(err);
3035 Scm_SysError("couldn't create event for a condition variable");
3036 }
3037 InitializeCriticalSection(&cond->numWaitersLock);
3038 }
3039
Scm__InternalCondWait(ScmInternalCond * cond,ScmInternalMutex * mutex,ScmTimeSpec * pts)3040 int Scm__InternalCondWait(ScmInternalCond *cond, ScmInternalMutex *mutex,
3041 ScmTimeSpec *pts)
3042 {
3043 DWORD r0, r1;
3044 DWORD timeout_msec;
3045 int badMutex = FALSE, lastWaiter;
3046
3047 if (pts) {
3048 u_long now_sec, now_usec;
3049 u_long target_sec, target_usec;
3050 Scm_GetTimeOfDay(&now_sec, &now_usec);
3051 target_sec = pts->tv_sec;
3052 target_usec = pts->tv_nsec / 1000;
3053 if (target_sec < now_sec
3054 || (target_sec == now_sec && target_usec <= now_usec)) {
3055 timeout_msec = 0;
3056 } else if (target_usec >= now_usec) {
3057 timeout_msec = ceil((target_sec - now_sec) * 1000
3058 + (target_usec - now_usec)/1000.0);
3059 } else {
3060 timeout_msec = ceil((target_sec - now_sec - 1) * 1000
3061 + (1.0e6 + target_usec - now_usec)/1000.0);
3062 }
3063 } else {
3064 timeout_msec = INFINITE;
3065 }
3066
3067 EnterCriticalSection(&cond->numWaitersLock);
3068 /* If we're the first one to wait on this cond var, set cond->mutex.
3069 We don't allow to use multiple mutexes together with single cond var.
3070 */
3071 if (cond->mutex != NULL && cond->mutex != mutex) {
3072 badMutex = TRUE;
3073 } else {
3074 cond->numWaiters++;
3075 if (cond->mutex == NULL) cond->mutex = mutex;
3076 }
3077 LeaveCriticalSection(&cond->numWaitersLock);
3078
3079 if (badMutex) {
3080 Scm_Error("Attempt to wait on condition variable %p with different"
3081 " mutex %p\n", cond, mutex);
3082 }
3083
3084 /* Signals mutex and atomically waits on the semaphore */
3085 r0 = SignalObjectAndWait(*mutex, cond->sem, timeout_msec, FALSE);
3086
3087 /* We're signaled, or timed out. There can be a case that cond is
3088 broadcasted between the timeout of SignalObjectAndWait and the
3089 following EnterCriticalSection. So we should check lastWaiter
3090 anyway. */
3091 EnterCriticalSection(&cond->numWaitersLock);
3092 cond->numWaiters--;
3093 lastWaiter = cond->broadcast && cond->numWaiters == 0;
3094 LeaveCriticalSection(&cond->numWaitersLock);
3095
3096 if (lastWaiter) {
3097 /* tell the broadcaster that all the waiters have gained
3098 control, and wait to acquire mutex. */
3099 r1 = SignalObjectAndWait(cond->done, *mutex, INFINITE, FALSE);
3100 } else {
3101 /* Acquire mutex */
3102 r1 = WaitForSingleObject(*mutex, INFINITE);
3103 }
3104 if (r0 == WAIT_TIMEOUT) return SCM_INTERNAL_COND_TIMEDOUT;
3105 if (r0 != WAIT_OBJECT_0 || r1 != WAIT_OBJECT_0) return -1;
3106 return 0;
3107 }
3108
Scm__InternalCondSignal(ScmInternalCond * cond)3109 int Scm__InternalCondSignal(ScmInternalCond *cond)
3110 {
3111 int haveWaiters;
3112 BOOL r = TRUE;
3113
3114 if (!cond->mutex) return 0; /* nobody ever waited on this cond var. */
3115
3116 SCM_INTERNAL_MUTEX_SAFE_LOCK_BEGIN(cond->mutex);
3117
3118 EnterCriticalSection(&cond->numWaitersLock);
3119 haveWaiters = (cond->numWaiters > 0);
3120 LeaveCriticalSection(&cond->numWaitersLock);
3121
3122 if (haveWaiters) {
3123 r = ReleaseSemaphore(cond->sem, 1, 0);
3124 }
3125
3126 SCM_INTERNAL_MUTEX_SAFE_LOCK_END();
3127 if (!r) return -1;
3128 return 0;
3129 }
3130
Scm__InternalCondBroadcast(ScmInternalCond * cond)3131 int Scm__InternalCondBroadcast(ScmInternalCond *cond)
3132 {
3133 int haveWaiters;
3134 DWORD err = 0;
3135 BOOL r0 = TRUE;
3136 DWORD r1 = WAIT_OBJECT_0;
3137
3138 if (!cond->mutex) return 0; /* nobody ever waited on this cond var. */
3139
3140 SCM_INTERNAL_MUTEX_SAFE_LOCK_BEGIN(cond->mutex);
3141
3142 EnterCriticalSection(&cond->numWaitersLock);
3143 cond->broadcast = haveWaiters = (cond->numWaiters > 0);
3144
3145 if (haveWaiters) {
3146 r0 = ReleaseSemaphore(cond->sem, cond->numWaiters, 0);
3147 if (!r0) err = GetLastError();
3148 LeaveCriticalSection(&cond->numWaitersLock);
3149
3150 if (r0) {
3151 /* Each waiter aquires mutex in turn, until the last waiter
3152 who will signal on 'done'. */
3153 r1 = WaitForSingleObject(cond->done, INFINITE);
3154 cond->broadcast = FALSE; /* safe; nobody will check this */
3155 }
3156 } else {
3157 /* nobody's waiting */
3158 LeaveCriticalSection(&cond->numWaitersLock);
3159 }
3160
3161 SCM_INTERNAL_MUTEX_SAFE_LOCK_END();
3162
3163 if (!r0) { SetLastError(err); return -1; }
3164 if (r1 != WAIT_OBJECT_0) return -1;
3165 return 0;
3166 }
3167
Scm__InternalCondDestroy(ScmInternalCond * cond)3168 void Scm__InternalCondDestroy(ScmInternalCond *cond)
3169 {
3170 CloseHandle(cond->sem);
3171 cond->sem = NULL;
3172 CloseHandle(cond->done);
3173 cond->done = NULL;
3174 }
3175
Scm__WinThreadExit()3176 void Scm__WinThreadExit()
3177 {
3178 ScmVM *vm = Scm_VM();
3179 ScmWinCleanup *cup = vm->winCleanup;
3180 while (cup) {
3181 cup->cleanup(cup->data);
3182 cup = cup->prev;
3183 }
3184 GC_ExitThread(0);
3185 }
3186
3187 #endif /* GAUCHE_USE_WTHREADS */
3188
3189 #endif /* GAUCHE_WINDOWS */
3190
3191 /*===============================================================
3192 * Initialization
3193 */
Scm__InitSystem(void)3194 void Scm__InitSystem(void)
3195 {
3196 ScmModule *mod = Scm_GaucheModule();
3197 Scm_InitStaticClass(&Scm_SysStatClass, "<sys-stat>", mod, stat_slots, 0);
3198 Scm_InitStaticClass(&Scm_TimeClass, "<time>", mod, time_slots, 0);
3199 Scm_InitStaticClass(&Scm_SysTmClass, "<sys-tm>", mod, tm_slots, 0);
3200 Scm_InitStaticClass(&Scm_SysGroupClass, "<sys-group>", mod, grp_slots, 0);
3201 Scm_InitStaticClass(&Scm_SysPasswdClass, "<sys-passwd>", mod, pwd_slots, 0);
3202 #ifdef HAVE_SELECT
3203 Scm_InitStaticClass(&Scm_SysFdsetClass, "<sys-fdset>", mod, NULL, 0);
3204 #endif
3205 SCM_INTERNAL_MUTEX_INIT(env_mutex);
3206 Scm_HashCoreInitSimple(&env_strings, SCM_HASH_STRING, 0, NULL);
3207
3208 key_absolute = SCM_MAKE_KEYWORD("absolute");
3209 key_expand = SCM_MAKE_KEYWORD("expand");
3210 key_canonicalize = SCM_MAKE_KEYWORD("canonicalize");
3211
3212 initial_ugid_differ = (geteuid() != getuid() || getegid() != getgid());
3213
3214 #ifdef GAUCHE_WINDOWS
3215 init_winsock();
3216 SCM_INTERNAL_MUTEX_INIT(process_mgr.mutex);
3217 Scm_AddCleanupHandler(fini_winsock, NULL);
3218 Scm_AddCleanupHandler(win_process_cleanup, NULL);
3219 #endif
3220 }
3221