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