~ chicken-core (master) /posixunix.scm
Trap1;;;; posixunix.scm - Miscellaneous file- and process-handling routines
2;
3; Copyright (c) 2008-2022, The CHICKEN Team
4; Copyright (c) 2000-2007, Felix L. Winkelmann
5; All rights reserved.
6;
7; Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8; conditions are met:
9;
10; Redistributions of source code must retain the above copyright notice, this list of conditions and the following
11; disclaimer.
12; Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
13; disclaimer in the documentation and/or other materials provided with the distribution.
14; Neither the name of the author nor the names of its contributors may be used to endorse or promote
15; products derived from this software without specific prior written permission.
16;
17; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
18; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
19; AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
20; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24; OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25; POSSIBILITY OF SUCH DAMAGE.
26
27
28;; these are not available on Windows
29
30(define-foreign-variable _stat_st_blksize unsigned-int "C_statbuf.st_blksize")
31(define-foreign-variable _stat_st_blocks unsigned-int "C_statbuf.st_blocks")
32
33(include "posix-common.scm")
34
35#>
36
37static int C_wait_status;
38
39#include <sys/time.h>
40#include <sys/wait.h>
41#include <sys/ioctl.h>
42#include <fcntl.h>
43#include <dirent.h>
44#include <pwd.h>
45#include <utime.h>
46
47#if defined(__sun) && defined(__SVR4)
48# include <sys/tty.h>
49# include <termios.h>
50#endif
51
52#ifdef __linux__
53# include <sys/file.h>
54#endif
55
56#include <sys/mman.h>
57#include <poll.h>
58
59#ifndef O_FSYNC
60# define O_FSYNC O_SYNC
61#endif
62
63#ifndef PIPE_BUF
64# ifdef __CYGWIN__
65# define PIPE_BUF _POSIX_PIPE_BUF
66# else
67# define PIPE_BUF 1024
68# endif
69#endif
70
71#ifndef O_BINARY
72# define O_BINARY 0
73#endif
74#ifndef O_TEXT
75# define O_TEXT 0
76#endif
77
78#ifndef MAP_FILE
79# define MAP_FILE 0
80#endif
81
82#ifndef MAP_ANON
83# define MAP_ANON 0
84#endif
85
86#ifndef FILENAME_MAX
87# define FILENAME_MAX 1024
88#endif
89
90static DIR *temphandle;
91static struct passwd *C_user;
92
93/* Android doesn't provide pw_gecos in the passwd struct */
94#ifdef __ANDROID__
95# define C_PW_GECOS ("")
96#else
97# define C_PW_GECOS (C_user->pw_gecos)
98#endif
99
100static int C_pipefds[ 2 ];
101static time_t C_secs;
102static struct timeval C_timeval;
103static struct stat C_statbuf;
104
105#define C_fchdir(fd) C_fix(fchdir(C_unfix(fd)))
106
107#define open_binary_input_pipe(a, n, name) C_mpointer(a, popen(C_c_string(name), "r"))
108#define open_text_input_pipe(a, n, name) open_binary_input_pipe(a, n, name)
109#define open_binary_output_pipe(a, n, name) C_mpointer(a, popen(C_c_string(name), "w"))
110#define open_text_output_pipe(a, n, name) open_binary_output_pipe(a, n, name)
111#define close_pipe(p) C_fix(pclose(C_port_file(p)))
112
113#define C_fork fork
114#define C_waitpid(id, o) C_fix(waitpid(C_unfix(id), &C_wait_status, C_unfix(o)))
115#define C_getppid getppid
116#define C_kill(id, s) C_fix(kill(C_unfix(id), C_unfix(s)))
117#define C_getuid getuid
118#define C_getgid getgid
119#define C_geteuid geteuid
120#define C_getegid getegid
121#define C_chown(fn, u, g) C_fix(chown(C_c_string(fn), C_unfix(u), C_unfix(g)))
122#define C_fchown(fd, u, g) C_fix(fchown(C_unfix(fd), C_unfix(u), C_unfix(g)))
123#define C_chmod(fn, m) C_fix(chmod(C_c_string(fn), C_unfix(m)))
124#define C_fchmod(fd, m) C_fix(fchmod(C_unfix(fd), C_unfix(m)))
125#define C_setuid(id) C_fix(setuid(C_unfix(id)))
126#define C_setgid(id) C_fix(setgid(C_unfix(id)))
127#define C_seteuid(id) C_fix(seteuid(C_unfix(id)))
128#define C_setegid(id) C_fix(setegid(C_unfix(id)))
129#define C_setsid(dummy) C_fix(setsid())
130#define C_setpgid(x, y) C_fix(setpgid(C_unfix(x), C_unfix(y)))
131#define C_getpgid(x) C_fix(getpgid(C_unfix(x)))
132#define C_symlink(o, n) C_fix(symlink(C_c_string(o), C_c_string(n)))
133#define C_do_readlink(f, b) C_fix(readlink(C_c_string(f), C_c_string(b), FILENAME_MAX))
134#define C_getpwnam(n) C_mk_bool((C_user = getpwnam(C_c_string(n))) != NULL)
135#define C_getpwuid(u) C_mk_bool((C_user = getpwuid(C_unfix(u))) != NULL)
136#define C_pipe(d) C_fix(pipe(C_pipefds))
137#define C_truncate(f, n) C_fix(truncate(C_c_string(f), C_num_to_int(n)))
138#define C_ftruncate(f, n) C_fix(ftruncate(C_unfix(f), C_num_to_int(n)))
139#define C_alarm alarm
140#define C_close(fd) C_fix(close(C_unfix(fd)))
141#define C_umask(m) C_fix(umask(C_unfix(m)))
142
143#define C_u_i_lstat(fn) C_fix(lstat(C_c_string(fn), &C_statbuf))
144
145#define C_u_i_execvp(f,a) C_fix(execvp(C_c_string(f), (char *const *)C_c_pointer_vector_or_null(a)))
146#define C_u_i_execve(f,a,e) C_fix(execve(C_c_string(f), (char *const *)C_c_pointer_vector_or_null(a), (char *const *)C_c_pointer_vector_or_null(e)))
147
148static int C_uw;
149#define C_WIFEXITED(n) (C_uw = C_unfix(n), C_mk_bool(WIFEXITED(C_uw)))
150#define C_WIFSIGNALED(n) (C_uw = C_unfix(n), C_mk_bool(WIFSIGNALED(C_uw)))
151#define C_WIFSTOPPED(n) (C_uw = C_unfix(n), C_mk_bool(WIFSTOPPED(C_uw)))
152#define C_WEXITSTATUS(n) (C_uw = C_unfix(n), C_fix(WEXITSTATUS(C_uw)))
153#define C_WTERMSIG(n) (C_uw = C_unfix(n), C_fix(WTERMSIG(C_uw)))
154#define C_WSTOPSIG(n) (C_uw = C_unfix(n), C_fix(WSTOPSIG(C_uw)))
155
156#ifdef __CYGWIN__
157# define C_mkfifo(fn, m) C_fix(-1)
158#else
159# define C_mkfifo(fn, m) C_fix(mkfifo(C_c_string(fn), C_unfix(m)))
160#endif
161
162static C_word C_flock(C_word n, C_word f)
163{
164#ifdef __HAIKU__
165# define LOCK_SH 0
166# define LOCK_EX 0
167# define LOCK_NB 0
168# define LOCK_UN 0
169 return C_fix(-1);
170#else
171 return C_fix(flock(C_unfix(n), C_unfix(f)));
172#endif
173}
174
175static sigset_t C_sigset;
176#define C_sigemptyset(d) (sigemptyset(&C_sigset), C_SCHEME_UNDEFINED)
177#define C_sigaddset(s) (sigaddset(&C_sigset, C_unfix(s)), C_SCHEME_UNDEFINED)
178#define C_sigdelset(s) (sigdelset(&C_sigset, C_unfix(s)), C_SCHEME_UNDEFINED)
179#define C_sigismember(s) C_mk_bool(sigismember(&C_sigset, C_unfix(s)))
180#define C_sigprocmask_set(d) C_fix(sigprocmask(SIG_SETMASK, &C_sigset, NULL))
181#define C_sigprocmask_block(d) C_fix(sigprocmask(SIG_BLOCK, &C_sigset, NULL))
182#define C_sigprocmask_unblock(d) C_fix(sigprocmask(SIG_UNBLOCK, &C_sigset, NULL))
183#define C_sigprocmask_get(d) C_fix(sigprocmask(SIG_SETMASK, NULL, &C_sigset))
184
185#define C_open(fn, fl, m) C_fix(open(C_c_string(fn), C_unfix(fl), C_unfix(m)))
186#define C_read(fd, b, n) C_fix(read(C_unfix(fd), C_c_string(b), C_unfix(n)))
187#define C_write(fd, b, start, n) C_fix(write(C_unfix(fd), C_c_string(b) + C_unfix(start), C_unfix(n)))
188#define C_mkstemp(t) C_fix(mkstemp(C_c_string(t)))
189
190#define C_ctime(n) (C_secs = (n), ctime(&C_secs))
191
192#if defined(__SVR4) || defined(C_MACOSX) || defined(__ANDROID__) || defined(_AIX)
193/* Seen here: http://lists.samba.org/archive/samba-technical/2002-November/025571.html */
194
195static time_t C_timegm(struct tm *t)
196{
197 time_t tl, tb;
198 struct tm *tg;
199
200 tl = mktime (t);
201 if (tl == -1)
202 {
203 t->tm_hour--;
204 tl = mktime (t);
205 if (tl == -1)
206 return -1; /* can't deal with output from strptime */
207 tl += 3600;
208 }
209 tg = gmtime (&tl);
210 tg->tm_isdst = 0;
211 tb = mktime (tg);
212 if (tb == -1)
213 {
214 tg->tm_hour--;
215 tb = mktime (tg);
216 if (tb == -1)
217 return -1; /* can't deal with output from gmtime */
218 tb += 3600;
219 }
220 return (tl - (tb - tl));
221}
222#else
223#define C_timegm timegm
224#endif
225
226#define C_a_timegm(ptr, c, v, tm) C_int64_to_num(ptr, C_timegm(C_tm_set((v), C_data_pointer(tm))))
227
228#ifdef __linux__
229extern char *strptime(const char *s, const char *format, struct tm *tm);
230extern pid_t getpgid(pid_t pid);
231#endif
232
233/* tm_get could be in posix-common, but it's only used in here */
234#define cpy_tmstc08_to_tmvec(v, ptm) \
235 (C_set_block_item((v), 0, C_fix(((struct tm *)ptm)->tm_sec)), \
236 C_set_block_item((v), 1, C_fix((ptm)->tm_min)), \
237 C_set_block_item((v), 2, C_fix((ptm)->tm_hour)), \
238 C_set_block_item((v), 3, C_fix((ptm)->tm_mday)), \
239 C_set_block_item((v), 4, C_fix((ptm)->tm_mon)), \
240 C_set_block_item((v), 5, C_fix((ptm)->tm_year)), \
241 C_set_block_item((v), 6, C_fix((ptm)->tm_wday)), \
242 C_set_block_item((v), 7, C_fix((ptm)->tm_yday)), \
243 C_set_block_item((v), 8, ((ptm)->tm_isdst ? C_SCHEME_TRUE : C_SCHEME_FALSE)))
244
245#define cpy_tmstc9_to_tmvec(v, ptm) \
246 (C_set_block_item((v), 9, C_fix(-(ptm)->tm_gmtoff)))
247
248#define C_tm_get_08(v, tm) cpy_tmstc08_to_tmvec( (v), (tm) )
249#define C_tm_get_9(v, tm) cpy_tmstc9_to_tmvec( (v), (tm) )
250
251static C_word
252C_tm_get( C_word v, void *tm )
253{
254 C_tm_get_08( v, (struct tm *)tm );
255#if defined(C_GNU_ENV) && !defined(__CYGWIN__) && !defined(__uClinux__)
256 C_tm_get_9( v, (struct tm *)tm );
257#endif
258 return v;
259}
260
261#define C_strptime(s, f, v, stm) \
262 (strptime(C_c_string(s), C_c_string(f), ((struct tm *)(stm))) ? C_tm_get((v), (stm)) : C_SCHEME_FALSE)
263
264static int set_file_mtime(C_word filename, C_word atime, C_word mtime)
265{
266 struct stat sb;
267 struct utimbuf tb;
268 C_word bv = C_block_item(filename, 0);
269
270 /* Only lstat if needed */
271 if (atime == C_SCHEME_FALSE || mtime == C_SCHEME_FALSE) {
272 if (lstat(C_c_string(bv), &sb) == -1) return -1;
273 }
274
275 if (atime == C_SCHEME_FALSE) {
276 tb.actime = sb.st_atime;
277 } else {
278 tb.actime = C_num_to_int64(atime);
279 }
280 if (mtime == C_SCHEME_FALSE) {
281 tb.modtime = sb.st_mtime;
282 } else {
283 tb.modtime = C_num_to_int64(mtime);
284 }
285 return utime(C_c_string(bv), &tb);
286}
287
288<#
289
290;; Faster versions of common operations
291
292(define ##sys#file-nonblocking!
293 (foreign-lambda* bool ([int fd])
294 "int val = fcntl(fd, F_GETFL, 0);"
295 "if(val == -1) C_return(0);"
296 "C_return(fcntl(fd, F_SETFL, val | O_NONBLOCK) != -1);" ) )
297
298(define ##sys#file-select-one (foreign-lambda int "C_check_fd_ready" int) )
299
300;;; Lo-level I/O:
301
302(define-foreign-variable _f_dupfd int "F_DUPFD")
303(define-foreign-variable _f_getfd int "F_GETFD")
304(define-foreign-variable _f_setfd int "F_SETFD")
305(define-foreign-variable _f_getfl int "F_GETFL")
306(define-foreign-variable _f_setfl int "F_SETFL")
307
308(set! chicken.file.posix#fcntl/dupfd _f_dupfd)
309(set! chicken.file.posix#fcntl/getfd _f_getfd)
310(set! chicken.file.posix#fcntl/setfd _f_setfd)
311(set! chicken.file.posix#fcntl/getfl _f_getfl)
312(set! chicken.file.posix#fcntl/setfl _f_setfl)
313
314(define-foreign-variable _o_nonblock int "O_NONBLOCK")
315(define-foreign-variable _o_noctty int "O_NOCTTY")
316(define-foreign-variable _o_fsync int "O_FSYNC")
317(define-foreign-variable _o_sync int "O_SYNC")
318(set! chicken.file.posix#open/nonblock _o_nonblock)
319(set! chicken.file.posix#open/noctty _o_noctty)
320(set! chicken.file.posix#open/fsync _o_fsync)
321(set! chicken.file.posix#open/sync _o_sync)
322
323;; Windows-only definitions
324(set! chicken.file.posix#open/noinherit 0)
325
326(set! chicken.process#spawn/overlay 0)
327(set! chicken.process#spawn/wait 0)
328(set! chicken.process#spawn/nowait 0)
329(set! chicken.process#spawn/nowaito 0)
330(set! chicken.process#spawn/detach 0)
331
332(define-foreign-variable _s_isuid int "S_ISUID")
333(define-foreign-variable _s_isgid int "S_ISGID")
334(define-foreign-variable _s_isvtx int "S_ISVTX")
335(set! chicken.file.posix#perm/isvtx _s_isvtx)
336(set! chicken.file.posix#perm/isuid _s_isuid)
337(set! chicken.file.posix#perm/isgid _s_isgid)
338
339(set! chicken.file.posix#file-control
340 (let ([fcntl (foreign-lambda int fcntl int int long)])
341 (lambda (fd cmd #!optional (arg 0))
342 (##sys#check-fixnum fd 'file-control)
343 (##sys#check-fixnum cmd 'file-control)
344 (let ([res (fcntl fd cmd arg)])
345 (if (fx= res -1)
346 (posix-error #:file-error 'file-control "cannot control file" fd cmd)
347 res ) ) ) ) )
348
349(set! chicken.file.posix#file-open
350 (let ((defmode (bitwise-ior _s_irusr _s_iwusr _s_irgrp _s_iwgrp _s_iroth _s_iwoth)))
351 (lambda (filename flags . mode)
352 (let ([mode (if (pair? mode) (car mode) defmode)])
353 (##sys#check-string filename 'file-open)
354 (##sys#check-fixnum flags 'file-open)
355 (##sys#check-fixnum mode 'file-open)
356 (let ([fd (##core#inline "C_open" (##sys#make-c-string filename 'file-open) flags mode)])
357 (when (eq? -1 fd)
358 (posix-error #:file-error 'file-open "cannot open file" filename flags mode) )
359 fd) ) ) ) )
360
361(set! chicken.file.posix#file-close
362 (lambda (fd)
363 (##sys#check-fixnum fd 'file-close)
364 (let loop ()
365 (when (fx< (##core#inline "C_close" fd) 0)
366 (cond
367 ((fx= _errno _eintr) (##sys#dispatch-interrupt loop))
368 (else
369 (posix-error #:file-error 'file-close "cannot close file" fd)))))))
370
371(set! chicken.file.posix#file-read
372 (lambda (fd size . buffer)
373 (##sys#check-fixnum fd 'file-read)
374 (##sys#check-fixnum size 'file-read)
375 (let ([buf (if (pair? buffer) (car buffer) (##sys#make-bytevector size))])
376 (unless (##core#inline "C_byteblockp" buf)
377 (##sys#signal-hook #:type-error 'file-read "bad argument type - not a bytevector" buf) )
378 (let ([n (##core#inline "C_read" fd buf size)])
379 (when (eq? -1 n)
380 (posix-error #:file-error 'file-read "cannot read from file" fd size) )
381 (list buf n) ) ) ) )
382
383(set! chicken.file.posix#file-write
384 (lambda (fd buffer . size)
385 (##sys#check-fixnum fd 'file-write)
386 (unless (##core#inline "C_byteblockp" buffer)
387 (##sys#signal-hook #:type-error 'file-write "bad argument type - not a bytevector" buffer) )
388 (let ([size (if (pair? size) (car size) (##sys#size buffer))])
389 (##sys#check-fixnum size 'file-write)
390 (let ([n (##core#inline "C_write" fd buffer 0 size)])
391 (when (eq? -1 n)
392 (posix-error #:file-error 'file-write "cannot write to file" fd size) )
393 n) ) ) )
394
395(set! chicken.file.posix#file-mkstemp
396 (lambda (template)
397 (##sys#check-string template 'file-mkstemp)
398 (let* ([buf (##sys#make-c-string template 'file-mkstemp)]
399 [fd (##core#inline "C_mkstemp" buf)]
400 [path-length (string-length buf)])
401 (when (eq? -1 fd)
402 (posix-error #:file-error 'file-mkstemp "cannot create temporary file" template) )
403 (values fd (##sys#substring buf 0 (fx- path-length 1) ) ) ) ) )
404
405
406;;; I/O multiplexing:
407
408(set! chicken.file.posix#file-select
409 (lambda (fdsr fdsw . timeout)
410 (let* ((tm (if (pair? timeout) (car timeout) #f))
411 (fdsrl (cond ((not fdsr) '())
412 ((fixnum? fdsr) (list fdsr))
413 (else (##sys#check-list fdsr 'file-select)
414 fdsr)))
415 (fdswl (cond ((not fdsw) '())
416 ((fixnum? fdsw) (list fdsw))
417 (else (##sys#check-list fdsw 'file-select)
418 fdsw)))
419 (nfdsr (##sys#length fdsrl))
420 (nfdsw (##sys#length fdswl))
421 (nfds (fx+ nfdsr nfdsw))
422 (fds-blob (##sys#make-bytevector
423 (fx* nfds (foreign-value "sizeof(struct pollfd)" int)))))
424 (do ((i 0 (fx+ i 1))
425 (fdsrl fdsrl (cdr fdsrl)))
426 ((null? fdsrl))
427 ((foreign-lambda* void ((int i) (int fd) (scheme-pointer p))
428 "struct pollfd *fds = p;"
429 "fds[i].fd = fd; fds[i].events = POLLIN;") i (car fdsrl) fds-blob))
430 (do ((i nfdsr (fx+ i 1))
431 (fdswl fdswl (cdr fdswl)))
432 ((null? fdswl))
433 ((foreign-lambda* void ((int i) (int fd) (scheme-pointer p))
434 "struct pollfd *fds = p;"
435 "fds[i].fd = fd; fds[i].events = POLLOUT;") i (car fdswl) fds-blob))
436 (let ((n ((foreign-lambda int "poll" scheme-pointer int int)
437 fds-blob nfds (if tm (inexact->exact (truncate (* (max 0 tm) 1000))) -1))))
438 (cond ((fx< n 0)
439 (posix-error #:file-error 'file-select "failed" fdsr fdsw) )
440 ((fx= n 0) (values (if (pair? fdsr) '() #f) (if (pair? fdsw) '() #f)))
441 (else
442 (let ((rl (let lp ((i 0) (res '()) (fds fdsrl))
443 (cond ((null? fds) (##sys#fast-reverse res))
444 (((foreign-lambda* bool ((int i) (scheme-pointer p))
445 "struct pollfd *fds = p;"
446 "C_return(fds[i].revents & (POLLIN|POLLERR|POLLHUP|POLLNVAL));")
447 i fds-blob)
448 (lp (fx+ i 1) (cons (car fds) res) (cdr fds)))
449 (else (lp (fx+ i 1) res (cdr fds))))))
450 (wl (let lp ((i nfdsr) (res '()) (fds fdswl))
451 (cond ((null? fds) (##sys#fast-reverse res))
452 (((foreign-lambda* bool ((int i) (scheme-pointer p))
453 "struct pollfd *fds = p;"
454 "C_return(fds[i].revents & (POLLOUT|POLLERR|POLLHUP|POLLNVAL));")
455 i fds-blob)
456 (lp (fx+ i 1) (cons (car fds) res) (cdr fds)))
457 (else (lp (fx+ i 1) res (cdr fds)))))))
458 (values
459 (and fdsr (if (fixnum? fdsr) (and (memq fdsr rl) fdsr) rl))
460 (and fdsw (if (fixnum? fdsw) (and (memq fdsw wl) fdsw) wl))))))))))
461
462
463;;; Pipe primitive:
464
465(define-foreign-variable _pipefd0 int "C_pipefds[ 0 ]")
466(define-foreign-variable _pipefd1 int "C_pipefds[ 1 ]")
467
468(set! chicken.process#create-pipe
469 (lambda (#!optional mode)
470 (when (fx< (##core#inline "C_pipe" #f) 0)
471 (posix-error #:file-error 'create-pipe "cannot create pipe") )
472 (values _pipefd0 _pipefd1)) )
473
474
475;;; Signal processing:
476
477(define-foreign-variable _nsig int "NSIG")
478(define-foreign-variable _sigterm int "SIGTERM")
479(define-foreign-variable _sigkill int "SIGKILL")
480(define-foreign-variable _sigint int "SIGINT")
481(define-foreign-variable _sighup int "SIGHUP")
482(define-foreign-variable _sigfpe int "SIGFPE")
483(define-foreign-variable _sigill int "SIGILL")
484(define-foreign-variable _sigbus int "SIGBUS")
485(define-foreign-variable _sigsegv int "SIGSEGV")
486(define-foreign-variable _sigabrt int "SIGABRT")
487(define-foreign-variable _sigtrap int "SIGTRAP")
488(define-foreign-variable _sigquit int "SIGQUIT")
489(define-foreign-variable _sigalrm int "SIGALRM")
490(define-foreign-variable _sigpipe int "SIGPIPE")
491(define-foreign-variable _sigusr1 int "SIGUSR1")
492(define-foreign-variable _sigusr2 int "SIGUSR2")
493(define-foreign-variable _sigvtalrm int "SIGVTALRM")
494(define-foreign-variable _sigprof int "SIGPROF")
495(define-foreign-variable _sigio int "SIGIO")
496(define-foreign-variable _sigurg int "SIGURG")
497(define-foreign-variable _sigchld int "SIGCHLD")
498(define-foreign-variable _sigcont int "SIGCONT")
499(define-foreign-variable _sigstop int "SIGSTOP")
500(define-foreign-variable _sigtstp int "SIGTSTP")
501(define-foreign-variable _sigxcpu int "SIGXCPU")
502(define-foreign-variable _sigxfsz int "SIGXFSZ")
503(define-foreign-variable _sigwinch int "SIGWINCH")
504
505(set! chicken.process.signal#signal/term _sigterm)
506(set! chicken.process.signal#signal/kill _sigkill)
507(set! chicken.process.signal#signal/int _sigint)
508(set! chicken.process.signal#signal/hup _sighup)
509(set! chicken.process.signal#signal/fpe _sigfpe)
510(set! chicken.process.signal#signal/ill _sigill)
511(set! chicken.process.signal#signal/segv _sigsegv)
512(set! chicken.process.signal#signal/abrt _sigabrt)
513(set! chicken.process.signal#signal/trap _sigtrap)
514(set! chicken.process.signal#signal/quit _sigquit)
515(set! chicken.process.signal#signal/alrm _sigalrm)
516(set! chicken.process.signal#signal/vtalrm _sigvtalrm)
517(set! chicken.process.signal#signal/prof _sigprof)
518(set! chicken.process.signal#signal/io _sigio)
519(set! chicken.process.signal#signal/urg _sigurg)
520(set! chicken.process.signal#signal/chld _sigchld)
521(set! chicken.process.signal#signal/cont _sigcont)
522(set! chicken.process.signal#signal/stop _sigstop)
523(set! chicken.process.signal#signal/tstp _sigtstp)
524(set! chicken.process.signal#signal/pipe _sigpipe)
525(set! chicken.process.signal#signal/xcpu _sigxcpu)
526(set! chicken.process.signal#signal/xfsz _sigxfsz)
527(set! chicken.process.signal#signal/usr1 _sigusr1)
528(set! chicken.process.signal#signal/usr2 _sigusr2)
529(set! chicken.process.signal#signal/winch _sigwinch)
530(set! chicken.process.signal#signal/bus _sigbus)
531(set! chicken.process.signal#signal/break 0)
532
533(set! chicken.process.signal#signals-list
534 (list
535 chicken.process.signal#signal/term
536 chicken.process.signal#signal/kill
537 chicken.process.signal#signal/int
538 chicken.process.signal#signal/hup
539 chicken.process.signal#signal/fpe
540 chicken.process.signal#signal/ill
541 chicken.process.signal#signal/segv
542 chicken.process.signal#signal/abrt
543 chicken.process.signal#signal/trap
544 chicken.process.signal#signal/quit
545 chicken.process.signal#signal/alrm
546 chicken.process.signal#signal/vtalrm
547 chicken.process.signal#signal/prof
548 chicken.process.signal#signal/io
549 chicken.process.signal#signal/urg
550 chicken.process.signal#signal/chld
551 chicken.process.signal#signal/cont
552 chicken.process.signal#signal/stop
553 chicken.process.signal#signal/tstp
554 chicken.process.signal#signal/pipe
555 chicken.process.signal#signal/xcpu
556 chicken.process.signal#signal/xfsz
557 chicken.process.signal#signal/usr1
558 chicken.process.signal#signal/usr2
559 chicken.process.signal#signal/winch
560 chicken.process.signal#signal/bus))
561
562(set! chicken.process.signal#set-signal-mask!
563 (lambda (sigs)
564 (##sys#check-list sigs 'set-signal-mask!)
565 (##core#inline "C_sigemptyset" 0)
566 (for-each
567 (lambda (s)
568 (##sys#check-fixnum s 'set-signal-mask!)
569 (##core#inline "C_sigaddset" s) )
570 sigs)
571 (when (fx< (##core#inline "C_sigprocmask_set" 0) 0)
572 (posix-error #:process-error 'set-signal-mask! "cannot set signal mask") )))
573
574(define chicken.process.signal#signal-mask
575 (getter-with-setter
576 (lambda ()
577 (##core#inline "C_sigprocmask_get" 0)
578 (let loop ((sigs chicken.process.signal#signals-list) (mask '()))
579 (if (null? sigs)
580 mask
581 (let ([sig (car sigs)])
582 (loop (cdr sigs)
583 (if (##core#inline "C_sigismember" sig) (cons sig mask) mask)) ) ) ) )
584 chicken.process.signal#set-signal-mask!
585 "(chicken.process.signal#signal-mask)"))
586
587(set! chicken.process.signal#signal-masked?
588 (lambda (sig)
589 (##sys#check-fixnum sig 'signal-masked?)
590 (##core#inline "C_sigprocmask_get" 0)
591 (##core#inline "C_sigismember" sig)) )
592
593(set! chicken.process.signal#signal-mask!
594 (lambda (sig)
595 (##sys#check-fixnum sig 'signal-mask!)
596 (##core#inline "C_sigemptyset" 0)
597 (##core#inline "C_sigaddset" sig)
598 (when (fx< (##core#inline "C_sigprocmask_block" 0) 0)
599 (posix-error #:process-error 'signal-mask! "cannot block signal") )))
600
601(set! chicken.process.signal#signal-unmask!
602 (lambda (sig)
603 (##sys#check-fixnum sig 'signal-unmask!)
604 (##core#inline "C_sigemptyset" 0)
605 (##core#inline "C_sigaddset" sig)
606 (when (fx< (##core#inline "C_sigprocmask_unblock" 0) 0)
607 (posix-error #:process-error 'signal-unmask! "cannot unblock signal") )) )
608
609
610;;; Getting group- and user-information:
611
612(set! chicken.process-context.posix#current-user-id
613 (getter-with-setter
614 (foreign-lambda int "C_getuid")
615 (lambda (id)
616 (##sys#check-fixnum id 'current-user-id)
617 (when (fx< (##core#inline "C_setuid" id) 0)
618 (##sys#error/errno (##sys#update-errno)
619 'current-user-id!-setter "cannot set user ID" id)))
620 "(chicken.process-context.posix#current-user-id)"))
621
622(set! chicken.process-context.posix#current-effective-user-id
623 (getter-with-setter
624 (foreign-lambda int "C_geteuid")
625 (lambda (id)
626 (##sys#check-fixnum id 'current-effective-user-id)
627 (when (fx< (##core#inline "C_seteuid" id) 0)
628 (##sys#error/errno (##sys#update-errno)
629 'effective-user-id!-setter
630 "cannot set effective user ID" id)))
631 "(chicken.process-context.posix#current-effective-user-id)"))
632
633(set! chicken.process-context.posix#current-group-id
634 (getter-with-setter
635 (foreign-lambda int "C_getgid")
636 (lambda (id)
637 (##sys#check-fixnum id 'current-group-id)
638 (when (fx< (##core#inline "C_setgid" id) 0)
639 (##sys#error/errno (##sys#update-errno)
640 'current-group-id!-setter "cannot set group ID" id)))
641 "(chicken.process-context.posix#current-group-id)") )
642
643(set! chicken.process-context.posix#current-effective-group-id
644 (getter-with-setter
645 (foreign-lambda int "C_getegid")
646 (lambda (id)
647 (##sys#check-fixnum id 'current-effective-group-id)
648 (when (fx< (##core#inline "C_setegid" id) 0)
649 (##sys#error/errno (##sys#update-errno)
650 'effective-group-id!-setter
651 "cannot set effective group ID" id)))
652 "(chicken.process-context.posix#current-effective-group-id)") )
653
654(define-foreign-variable _user-name nonnull-c-string "C_user->pw_name")
655(define-foreign-variable _user-passwd nonnull-c-string "C_user->pw_passwd")
656(define-foreign-variable _user-uid int "C_user->pw_uid")
657(define-foreign-variable _user-gid int "C_user->pw_gid")
658(define-foreign-variable _user-gecos nonnull-c-string "C_PW_GECOS")
659(define-foreign-variable _user-dir c-string "C_user->pw_dir")
660(define-foreign-variable _user-shell c-string "C_user->pw_shell")
661
662(set! chicken.process-context.posix#user-information
663 (lambda (user #!optional as-vector)
664 (let ([r (if (fixnum? user)
665 (##core#inline "C_getpwuid" user)
666 (begin
667 (##sys#check-string user 'user-information)
668 (##core#inline "C_getpwnam" (##sys#make-c-string user 'user-information)) ) ) ] )
669 (and r
670 ((if as-vector vector list)
671 _user-name
672 _user-passwd
673 _user-uid
674 _user-gid
675 _user-gecos
676 _user-dir
677 _user-shell) ) )) )
678
679(set! chicken.process-context.posix#current-user-name
680 (lambda ()
681 (car (chicken.process-context.posix#user-information
682 (chicken.process-context.posix#current-user-id)))) )
683
684(set! chicken.process-context.posix#current-effective-user-name
685 (lambda ()
686 (car (chicken.process-context.posix#user-information
687 (chicken.process-context.posix#current-effective-user-id)))) )
688
689(define chown
690 (lambda (loc f uid gid)
691 (##sys#check-fixnum uid loc)
692 (##sys#check-fixnum gid loc)
693 (let ((r (cond
694 ((port? f)
695 (##core#inline "C_fchown" (chicken.file.posix#port->fileno f) uid gid))
696 ((fixnum? f)
697 (##core#inline "C_fchown" f uid gid))
698 ((string? f)
699 (##core#inline "C_chown"
700 (##sys#make-c-string f loc) uid gid))
701 (else (##sys#signal-hook
702 #:type-error loc
703 "bad argument type - not a fixnum, port or string" f)))))
704 (when (fx< r 0)
705 (posix-error #:file-error loc "cannot change file owner" f uid gid) )) ) )
706
707(set! chicken.process-context.posix#create-session
708 (lambda ()
709 (let ([a (##core#inline "C_setsid" #f)])
710 (when (fx< a 0)
711 (##sys#error/errno (##sys#update-errno)
712 'create-session "cannot create session"))
713 a)) )
714
715(set! chicken.process-context.posix#process-group-id
716 (getter-with-setter
717 (lambda (pid)
718 (##sys#check-fixnum pid 'process-group-id)
719 (let ([a (##core#inline "C_getpgid" pid)])
720 (when (fx< a 0)
721 (##sys#error/errno (##sys#update-errno)
722 'process-group-id
723 "cannot retrieve process group ID" pid))
724 a))
725 (lambda (pid pgid)
726 (##sys#check-fixnum pid 'process-group)
727 (##sys#check-fixnum pgid 'process-group)
728 (when (fx< (##core#inline "C_setpgid" pid pgid) 0)
729 (##sys#error/errno (##sys#update-errno)
730 'process-group "cannot set process group ID" pid pgid)))
731 "(chicken.process-context.posix#process-group-id pid)"))
732
733
734;;; Hard and symbolic links:
735
736(set! chicken.file.posix#create-symbolic-link
737 (lambda (old new)
738 (##sys#check-string old 'create-symbolic-link)
739 (##sys#check-string new 'create-symbolic-link)
740 (when (fx< (##core#inline
741 "C_symlink"
742 (##sys#make-c-string old 'create-symbolic-link)
743 (##sys#make-c-string new 'create-symbolic-link) )
744 0)
745 (posix-error #:file-error 'create-symbolic-link "cannot create symbolic link" old new) ) ) )
746
747(define-foreign-variable _filename_max int "FILENAME_MAX")
748
749(define ##sys#read-symbolic-link
750 (let ((buf (##sys#make-bytevector (fx+ _filename_max 1) 0)))
751 (lambda (fname location)
752 (let ((len (##core#inline
753 "C_do_readlink"
754 (##sys#make-c-string fname location)
755 buf)))
756 (if (fx< len 0)
757 (posix-error #:file-error location "cannot read symbolic link" fname)
758 (##sys#buffer->string buf 0 len))))))
759
760(set! chicken.file.posix#read-symbolic-link
761 (lambda (fname #!optional canonicalize)
762 (##sys#check-string fname 'read-symbolic-link)
763 (if canonicalize
764 (receive (base-origin base-directory directory-components) (decompose-directory fname)
765 (let loop ((components directory-components)
766 (result (string-append (or base-origin "") (or base-directory ""))))
767 (if (null? components)
768 result
769 (let ((pathname (make-pathname result (car components))))
770 (if (##sys#file-exists? pathname #f #f 'read-symbolic-link)
771 (loop (cdr components)
772 (if (chicken.file.posix#symbolic-link? pathname)
773 (let ((target (##sys#read-symbolic-link pathname 'read-symbolic-link)))
774 (if (absolute-pathname? target)
775 target
776 (make-pathname result target)))
777 pathname))
778 (##sys#signal-hook #:file-error 'read-symbolic-link "could not canonicalize path with symbolic links, component does not exist" pathname))))))
779 (##sys#read-symbolic-link fname 'read-symbolic-link))))
780
781(set! chicken.file.posix#file-link
782 (let ((link (foreign-lambda int "link" nonnull-c-string nonnull-c-string)))
783 (lambda (old new)
784 (##sys#check-string old 'file-link)
785 (##sys#check-string new 'file-link)
786 (when (fx< (link old new) 0)
787 (posix-error #:file-error 'hard-link "could not create hard link" old new) ) ) ) )
788
789(define-inline (eagain/ewouldblock? e)
790 (or (fx= e _ewouldblock)
791 (fx= e _eagain)))
792
793(define ##sys#custom-input-port
794 (lambda (loc nam fd #!optional (nonblocking? #f) (bufi 1) (on-close void) (more? #f) enc)
795 (when nonblocking? (##sys#file-nonblocking! fd) )
796 (let ((bufsiz (if (fixnum? bufi) bufi (##sys#size bufi)))
797 (buf (if (fixnum? bufi) (##sys#make-bytevector bufi) bufi))
798 (buflen 0)
799 (bufpos 0)
800 (this-port #f))
801 (let ([ready?
802 (lambda ()
803 (let ((res (##sys#file-select-one fd)))
804 (if (fx= -1 res)
805 (if (eagain/ewouldblock? _errno)
806 #f
807 (posix-error #:file-error loc "cannot select" fd nam))
808 (fx= 1 res))))]
809 [peek
810 (lambda ()
811 (if (fx>= bufpos buflen)
812 #!eof
813 (##sys#decode-buffer buf bufpos 1 (##sys#slot this-port 15)
814 (lambda (buf start n)
815 (##core#inline "C_utf_decode" buf start)))))]
816 [fetch
817 (lambda ()
818 (let loop ()
819 (let ([cnt (##core#inline "C_read" fd buf bufsiz)])
820 (cond ((fx= cnt -1)
821 (cond
822 ((eagain/ewouldblock? _errno)
823 (##sys#thread-block-for-i/o! ##sys#current-thread fd #:input)
824 (##sys#thread-yield!)
825 (loop) )
826 ((fx= _errno _eintr)
827 (##sys#dispatch-interrupt loop))
828 (else (posix-error #:file-error loc "cannot read" fd nam) )))
829 [(and more? (fx= cnt 0))
830 ;; When "more" keep trying, otherwise read once more
831 ;; to guard against race conditions
832 (if more?
833 (begin
834 (##sys#thread-yield!)
835 (loop) )
836 (let ([cnt (##core#inline "C_read" fd buf bufsiz)])
837 (when (fx= cnt -1)
838 (if (eagain/ewouldblock? _errno)
839 (set! cnt 0)
840 (posix-error #:file-error loc "cannot read" fd nam) ) )
841 (set! buflen cnt)
842 (set! bufpos 0) ) )]
843 [else
844 (set! buflen cnt)
845 (set! bufpos 0)]) ) ) )] )
846 (let ([the-port
847 (make-input-port
848 (lambda () ; read-char
849 (when (fx>= bufpos buflen)
850 (fetch))
851 (if (fx>= bufpos buflen)
852 #!eof
853 (##sys#decode-buffer buf bufpos 1 (##sys#slot this-port 15)
854 (lambda (buf start n)
855 (set! bufpos (fx+ bufpos n))
856 (##core#inline "C_utf_decode" buf start)))))
857 (lambda () ; char-ready?
858 (or (fx< bufpos buflen)
859 (ready?)) )
860 (lambda () ; close
861 (when (fx< (##core#inline "C_close" fd) 0)
862 (posix-error #:file-error loc "cannot close" fd nam))
863 (on-close))
864 peek-char:
865 (lambda () ; peek-char
866 (when (fx>= bufpos buflen)
867 (fetch))
868 (peek) )
869 read-bytevector:
870 (lambda (port n dest start) ; read-bytevector!
871 (let loop ([n (or n (fx- (##sys#size dest) start))]
872 [m 0]
873 [start start])
874 (cond [(eq? 0 n) m]
875 [(fx< bufpos buflen)
876 (let* ([rest (fx- buflen bufpos)]
877 [n2 (if (fx< n rest) n rest)])
878 (##core#inline "C_copy_memory_with_offset"
879 dest buf start bufpos n2)
880 (set! bufpos (fx+ bufpos n2))
881 (loop (fx- n n2) (fx+ m n2) (fx+ start n2)) ) ]
882 [else
883 (fetch)
884 (if (eq? 0 buflen)
885 m
886 (loop n m start) ) ] ) ) )
887 read-line:
888 (lambda (p limit) ; read-line
889 (when (fx>= bufpos buflen)
890 (fetch))
891 (if (fx>= bufpos buflen)
892 #!eof
893 (let ((limit (or limit (fx- most-positive-fixnum bufpos))))
894 (receive (next line full-line?)
895 (##sys#scan-buffer-line
896 buf
897 (fxmin buflen (fx+ bufpos limit))
898 bufpos
899 (lambda (pos)
900 (let ((nbytes (fx- pos bufpos)))
901 (cond ((fx>= nbytes limit)
902 (values #f pos #f))
903 (else
904 (set! limit (fx- limit nbytes))
905 (fetch)
906 (if (fx< bufpos buflen)
907 (values buf bufpos
908 (fxmin buflen
909 (fx+ bufpos limit)))
910 (values #f bufpos #f))))))
911 (##sys#slot this-port 15))
912 ;; Update row & column position
913 (if full-line?
914 (begin
915 (##sys#setislot p 4 (fx+ (##sys#slot p 4) 1))
916 (##sys#setislot p 5 0))
917 (##sys#setislot p 5 (fx+ (##sys#slot p 5)
918 (string-length line))))
919 (set! bufpos next)
920 line)) ) )
921 read-buffered:
922 (lambda (port) ; read-buffered
923 (if (fx>= bufpos buflen)
924 ""
925 (let* ((len (fx- buflen bufpos))
926 (str (##sys#buffer->string/encoding buf bufpos len (##sys#slot this-port 15))))
927 (set! bufpos buflen)
928 str))))])
929 (set! this-port the-port)
930 (##sys#setslot this-port 3 nam)
931 (##sys#setslot this-port 15 enc)
932 this-port ) ) ) ) )
933
934(define ##sys#custom-output-port
935 (lambda (loc nam fd #!optional (nonblocking? #f) (bufi 0) (on-close void)
936 enc)
937 (when nonblocking? (##sys#file-nonblocking! fd) )
938 (letrec ((this-port #f)
939 (poke
940 (lambda (bv start len)
941 (let loop ()
942 (let ((cnt (##core#inline "C_write" fd bv start len)))
943 (cond ((fx= -1 cnt)
944 (cond
945 ((eagain/ewouldblock? _errno)
946 (##sys#thread-yield!)
947 (poke bv start len) )
948 ((fx= _errno _eintr)
949 (##sys#dispatch-interrupt loop))
950 (else
951 (posix-error loc #:file-error "cannot write" fd nam) ) ) )
952 ((fx< cnt len)
953 (poke bv (fx+ start cnt) (fx- len cnt)) ) ) ) )))
954 (store
955 (let ([bufsiz (if (fixnum? bufi) bufi (##sys#size bufi))])
956 (if (fx= 0 bufsiz)
957 (lambda (str)
958 (when str
959 (let ((bv (##sys#slot str 0)))
960 (poke bv 0 (fx- (##sys#size bv) 1)))))
961 (let ((buf (if (fixnum? bufi) (##sys#make-bytevector bufi) bufi))
962 (bufpos 0))
963 (lambda (str)
964 (if str
965 (let ((bv (##sys#slot str 0)))
966 (let loop ((rem (fx- bufsiz bufpos))
967 (start 0)
968 (len (fx- (##sys#size bv) 1)))
969 (cond ((fx= 0 rem)
970 (poke buf 0 bufsiz)
971 (set! bufpos 0)
972 (loop bufsiz 0 len))
973 ((fx< rem len)
974 (##core#inline "C_copy_memory_with_offset" buf bv bufpos 0 len)
975 (loop 0 rem (fx- len rem)))
976 (else
977 (##core#inline "C_copy_memory_with_offset" buf bv bufpos start len)
978 (set! bufpos (fx+ bufpos len))) ) )
979 (when (fx< 0 bufpos)
980 (poke buf bufpos) ) ) ) ) ) ))))
981 (let ((the-port
982 (make-output-port
983 (lambda (str) (store str))
984 (lambda () ; close
985 (when (fx< (##core#inline "C_close" fd) 0)
986 (posix-error #:file-error loc "cannot close" fd nam))
987 (on-close))
988 force-output:
989 (lambda () ; flush
990 (store #f) ) )) )
991 (set! this-port the-port)
992 (##sys#setslot this-port 3 nam)
993 (##sys#setslot this-port 15 enc)
994 this-port ) ) ) )
995
996
997;;; Other file operations:
998
999(set! chicken.file.posix#file-truncate
1000 (lambda (fname off)
1001 (##sys#check-exact-integer off 'file-truncate)
1002 (when (fx< (cond ((string? fname) (##core#inline "C_truncate" (##sys#make-c-string fname 'file-truncate) off))
1003 ((port? fname) (##core#inline "C_ftruncate" (chicken.file.posix#port->fileno fname) off))
1004 ((fixnum? fname) (##core#inline "C_ftruncate" fname off))
1005 (else (##sys#error 'file-truncate "invalid file" fname)))
1006 0)
1007 (posix-error #:file-error 'file-truncate "cannot truncate file" fname off) ) ) )
1008
1009
1010;;; File locking:
1011
1012(define-foreign-variable _lock_sh int "LOCK_SH")
1013(define-foreign-variable _lock_ex int "LOCK_EX")
1014(define-foreign-variable _lock_un int "LOCK_UN")
1015(define-foreign-variable _lock_nb int "LOCK_NB")
1016
1017(let ()
1018 (define (err msg port loc)
1019 (posix-error #:file-error loc msg port) )
1020 (define (fileno x loc)
1021 (if (port? x)
1022 (chicken.file.posix#port->fileno x)
1023 (begin
1024 (##sys#check-exact-integer x loc)
1025 x)))
1026 (set! chicken.file.posix#file-lock
1027 (lambda (port #!optional shared)
1028 (let loop ()
1029 (let ((r (##core#inline "C_flock" (fileno port 'file-lock)
1030 (##core#inline "C_fixnum_or" _lock_nb (if shared _lock_sh _lock_ex)))))
1031 (cond ((eq? r 0) #t)
1032 ((fx= _errno _eintr) (loop))
1033 ((eagain/ewouldblock? _errno) #f)
1034 (else (err "locking file failed" port 'file-lock)))))))
1035 (set! chicken.file.posix#file-lock/blocking
1036 (lambda (port #!optional shared)
1037 (let loop ()
1038 (let ((r (##core#inline "C_flock" (fileno port 'file-lock/blocking)
1039 (if shared _lock_sh _lock_ex))))
1040 (cond ((eq? r 0) #t)
1041 ((fx= _errno _eintr) (loop))
1042 (else (err "locking file failed" port 'file-lock/blocking)))))))
1043 (set! chicken.file.posix#file-unlock
1044 (lambda (port)
1045 (let loop ()
1046 (let ((r (##core#inline "C_flock" (fileno port 'file-unlock) _lock_un)))
1047 (cond ((eq? r 0))
1048 ((fx= _errno _eintr) (loop))
1049 (else (err "unlocking file failed" port 'file-unlock))))))))
1050
1051
1052;;; FIFOs:
1053
1054(set! chicken.file.posix#create-fifo
1055 (lambda (fname . mode)
1056 (##sys#check-string fname 'create-fifo)
1057 (let ([mode (if (pair? mode) (car mode) (fxior _s_irwxu (fxior _s_irwxg _s_irwxo)))])
1058 (##sys#check-fixnum mode 'create-fifo)
1059 (when (fx< (##core#inline "C_mkfifo" (##sys#make-c-string fname 'create-fifo) mode) 0)
1060 (posix-error #:file-error 'create-fifo "cannot create FIFO" fname mode) ) ) ) )
1061
1062
1063;;; Time related things:
1064
1065(set! chicken.time.posix#string->time
1066 (let ((strptime (foreign-lambda scheme-object "C_strptime" scheme-object scheme-object scheme-object scheme-pointer))
1067 (tm-size (foreign-value "sizeof(struct tm)" int)))
1068 (lambda (tim #!optional (fmt "%a %b %e %H:%M:%S %Z %Y"))
1069 (##sys#check-string tim 'string->time)
1070 (##sys#check-string fmt 'string->time)
1071 (strptime (##sys#make-c-string tim 'string->time) (##sys#make-c-string fmt) (make-vector 10 #f) (##sys#make-string tm-size #\nul)) ) ) )
1072
1073(set! chicken.time.posix#utc-time->seconds
1074 (let ((tm-size (foreign-value "sizeof(struct tm)" int)))
1075 (lambda (tm)
1076 (check-time-vector 'utc-time->seconds tm)
1077 (let ((t (##core#inline_allocate ("C_a_timegm" 7) tm (##sys#make-string tm-size #\nul))))
1078 (if (= -1 t)
1079 (##sys#error 'utc-time->seconds "cannot convert time vector to seconds" tm)
1080 t)))))
1081
1082(set! chicken.time.posix#local-timezone-abbreviation
1083 (foreign-lambda* c-string ()
1084 "\n#if !defined(__CYGWIN__) && !defined(__SVR4) && !defined(__uClinux__) && !defined(__hpux__) && !defined(_AIX)\n"
1085 "time_t clock = time(NULL);"
1086 "struct tm *ltm = C_localtime(&clock);"
1087 "char *z = ltm ? (char *)ltm->tm_zone : 0;"
1088 "\n#else\n"
1089 "char *z = (daylight ? tzname[1] : tzname[0]);"
1090 "\n#endif\n"
1091 "C_return(z);") )
1092
1093
1094;;; Other things:
1095
1096(set! chicken.process.signal#set-alarm!
1097 (foreign-lambda int "C_alarm" int))
1098
1099
1100;;; Process handling:
1101
1102(define c-string->allocated-pointer
1103 (foreign-lambda* c-pointer ((scheme-object o))
1104 "char *ptr = C_malloc(C_header_size(o)); \n"
1105 "if (ptr != NULL) {\n"
1106 " C_memcpy(ptr, C_data_pointer(o), C_header_size(o)); \n"
1107 "}\n"
1108 "C_return(ptr);"))
1109
1110(set! chicken.process#process-fork
1111 (let ((fork (foreign-lambda int "C_fork")))
1112 (lambda (#!optional thunk killothers)
1113 ;; flush all stdio streams before fork
1114 ((foreign-lambda int "C_fflush" c-pointer) #f)
1115 (let ((pid (fork)))
1116 (cond ((fx= -1 pid) ; error
1117 (posix-error #:process-error 'process-fork "cannot create child process"))
1118 ((fx= 0 pid) ; child process
1119 (set! children '())
1120 (when killothers
1121 (call-with-current-continuation (lambda (continue) (##sys#kill-other-threads (lambda () (continue #f))))))
1122 (if thunk
1123 (##sys#call-with-cthulhu
1124 (lambda ()
1125 (thunk)
1126 ;; Make sure to run clean up tasks.
1127 ;; NOTE: ##sys#call-with-cthulhu will invoke
1128 ;; a more low-level runtime C_exit_runtime(0)
1129 (exit 0)))
1130 #f))
1131 (else ; parent process
1132 (register-pid pid)))))))
1133
1134(set! chicken.process#process-execute
1135 (lambda (filename #!optional (arglist '()) envlist _)
1136 (call-with-exec-args
1137 'process-execute filename (lambda (x) x) arglist envlist
1138 (lambda (prg argbuf envbuf)
1139 (let ((r (if envbuf
1140 (##core#inline "C_u_i_execve" prg argbuf envbuf)
1141 (##core#inline "C_u_i_execvp" prg argbuf))))
1142 (when (fx= r -1)
1143 (posix-error #:process-error 'process-execute "cannot execute process" filename)))))))
1144
1145(define-foreign-variable _wnohang int "WNOHANG")
1146(define-foreign-variable _wait-status int "C_wait_status")
1147
1148(define (process-wait-impl pid nohang)
1149 (let* ((res (##core#inline "C_waitpid" pid (if nohang _wnohang 0)))
1150 (norm (##core#inline "C_WIFEXITED" _wait-status)) )
1151 (if (and (fx= res -1) (fx= _errno _eintr))
1152 (##sys#dispatch-interrupt
1153 (lambda () (process-wait-impl pid nohang)))
1154 (values
1155 res
1156 norm
1157 (cond (norm (##core#inline "C_WEXITSTATUS" _wait-status))
1158 ((##core#inline "C_WIFSIGNALED" _wait-status)
1159 (##core#inline "C_WTERMSIG" _wait-status))
1160 (else (##core#inline "C_WSTOPSIG" _wait-status)) ) )) ) )
1161
1162(set! chicken.process-context.posix#parent-process-id (foreign-lambda int "C_getppid"))
1163
1164(set! chicken.process#process-signal
1165 (lambda (id . sig)
1166 (let ((sig (if (pair? sig) (car sig) _sigterm))
1167 (pid (if (process? id) (process-id id) id)))
1168 (##sys#check-fixnum pid 'process-signal)
1169 (##sys#check-fixnum sig 'process-signal)
1170 (let ((r (##core#inline "C_kill" pid sig)))
1171 (when (fx= r -1)
1172 (posix-error #:process-error 'process-signal
1173 "could not send signal to process" id sig) ) ) ) ) )
1174
1175(define (shell-command loc)
1176 (or (get-environment-variable "SHELL") "/bin/sh") )
1177
1178(define (shell-command-arguments cmdlin)
1179 (list "-c" cmdlin) )
1180
1181(set! chicken.process#process-run
1182 (lambda (f . args)
1183 (let ((args (if (pair? args) (car args) #f))
1184 (proc (chicken.process#process-fork)) )
1185 (cond (proc)
1186 (args (chicken.process#process-execute f args))
1187 (else
1188 (chicken.process#process-execute
1189 (shell-command 'process-run)
1190 (shell-command-arguments f)) ) ) ) ) )
1191
1192;;; Run subprocess connected with pipes:
1193
1194;; process-impl
1195; loc caller procedure symbol
1196; cmd pathname or commandline
1197; args string-list or '()
1198; env string-list or #f
1199; stdoutf #f then share, or #t then create
1200; stdinf #f then share, or #t then create
1201; stderrf #f then share, or #t then create
1202;
1203; (values stdin-input-port? stdout-output-port? pid stderr-input-port?)
1204; where stdin-input-port?, etc. is a port or #f, indicating no port created.
1205
1206(define-constant DEFAULT-INPUT-BUFFER-SIZE 256)
1207(define-constant DEFAULT-OUTPUT-BUFFER-SIZE 0)
1208
1209;FIXME process-execute, process-fork don't show parent caller
1210
1211(define process-impl
1212 (let ((replace-fd
1213 (lambda (loc fd stdfd)
1214 (unless (fx= stdfd fd)
1215 (chicken.file.posix#duplicate-fileno fd stdfd)
1216 (chicken.file.posix#file-close fd) ) )) )
1217 (let ((make-on-close
1218 (lambda (loc proc clsvec idx idxa idxb)
1219 (lambda ()
1220 (vector-set! clsvec idx #t)
1221 (when (and (vector-ref clsvec idxa) (vector-ref clsvec idxb))
1222 (chicken.process#process-wait proc #f) )
1223 (void)) ))
1224 (needed-pipe
1225 (lambda (loc port)
1226 (and port
1227 (receive (i o) (chicken.process#create-pipe)
1228 (cons i o))) ))
1229 [connect-parent
1230 (lambda (loc pipe port fd)
1231 (and port
1232 (let ([usefd (car pipe)] [clsfd (cdr pipe)])
1233 (chicken.file.posix#file-close clsfd)
1234 usefd) ) )]
1235 [connect-child
1236 (lambda (loc pipe port stdfd)
1237 (when port
1238 (let ([usefd (car pipe)] [clsfd (cdr pipe)])
1239 (chicken.file.posix#file-close clsfd)
1240 (replace-fd loc usefd stdfd)) ) )] )
1241 (let (
1242 (spawn
1243 (let ([swapped-ends
1244 (lambda (pipe)
1245 (and pipe
1246 (cons (cdr pipe) (car pipe)) ) )])
1247 (lambda (loc cmd args env stdoutf stdinf stderrf)
1248 (let ([ipipe (needed-pipe loc stdinf)]
1249 [opipe (needed-pipe loc stdoutf)]
1250 [epipe (needed-pipe loc stderrf)])
1251 (values
1252 ipipe (swapped-ends opipe) epipe
1253 (chicken.process#process-fork
1254 (lambda ()
1255 (connect-child loc opipe stdinf chicken.file.posix#fileno/stdin)
1256 (connect-child loc (swapped-ends ipipe) stdoutf chicken.file.posix#fileno/stdout)
1257 (connect-child loc (swapped-ends epipe) stderrf chicken.file.posix#fileno/stderr)
1258 (chicken.process#process-execute cmd args env)))) ) ) ))
1259 [input-port
1260 (lambda (loc cmd pipe stdf stdfd on-close enc)
1261 (and-let* ([fd (connect-parent loc pipe stdf stdfd)])
1262 (##sys#custom-input-port loc cmd fd #t DEFAULT-INPUT-BUFFER-SIZE on-close #f enc) ) )]
1263 [output-port
1264 (lambda (loc cmd pipe stdf stdfd on-close enc)
1265 (and-let* ([fd (connect-parent loc pipe stdf stdfd)])
1266 (##sys#custom-output-port loc cmd fd #t DEFAULT-OUTPUT-BUFFER-SIZE on-close enc) ) )] )
1267 (lambda (loc cmd args env stdoutf stdinf stderrf enc)
1268 (receive [inpipe outpipe errpipe proc]
1269 (spawn loc cmd args env stdoutf stdinf stderrf)
1270 ;When shared assume already "closed", since only created ports
1271 ;should be explicitly closed, and when one is closed we want
1272 ;to wait.
1273 (let ((clsvec (vector (not stdinf) (not stdoutf) (not stderrf))))
1274 (process-output-port-set! proc
1275 (input-port loc cmd inpipe stdinf
1276 chicken.file.posix#fileno/stdin
1277 (make-on-close loc proc clsvec 0 1 2)
1278 enc))
1279 (process-input-port-set! proc
1280 (output-port loc cmd outpipe stdoutf
1281 chicken.file.posix#fileno/stdout
1282 (make-on-close loc proc clsvec 1 0 2)
1283 enc))
1284 (process-error-port-set! proc
1285 (input-port loc cmd errpipe stderrf
1286 chicken.file.posix#fileno/stderr
1287 (make-on-close loc proc clsvec 2 0 1)
1288 enc) )
1289 proc) ) ) ) ) ) )
1290
1291;;; Run subprocess connected with pipes:
1292
1293;; TODO: See if this can be moved to posix-common
1294(let ((%process
1295 (lambda (loc err? cmd args env enc)
1296 (let ((chkstrlst
1297 (lambda (lst)
1298 (##sys#check-list lst loc)
1299 (for-each (cut ##sys#check-string <> loc) lst) )))
1300 (##sys#check-string cmd loc)
1301 (if args
1302 (chkstrlst args)
1303 (begin
1304 (set! args (shell-command-arguments cmd))
1305 (set! cmd (shell-command loc)) ) )
1306 (when env (check-environment-list env loc))
1307 (process-impl loc cmd args env #t #t err? enc)))))
1308 (set! chicken.process#process
1309 (lambda (cmd #!optional args env (enc 'utf-8) exactf)
1310 (%process 'process #f cmd args env enc)))
1311 (set! chicken.process#process*
1312 (lambda (cmd #!optional args env (enc 'utf-8) exactf)
1313 (%process 'process* #t cmd args env enc))))
1314
1315
1316;;; chroot:
1317
1318(set! chicken.process-context.posix#set-root-directory!
1319 (let ((chroot (foreign-lambda int "chroot" nonnull-c-string)))
1320 (lambda (dir)
1321 (##sys#check-string dir 'set-root-directory!)
1322 (when (fx< (chroot dir) 0)
1323 (posix-error #:file-error 'set-root-directory! "unable to change root directory" dir) ) ) ) )
1324
1325;;; unimplemented stuff:
1326
1327(set!-unimplemented chicken.process#process-spawn)