From 001069aa7ba5224dce00abcebe0fb8b597cad736 Mon Sep 17 00:00:00 2001 From: Giacomo Tesio Date: Mon, 11 Sep 2017 01:01:11 +0200 Subject: [PATCH] libposix: deep refactor; add sys/posixly command With these changes, libposix (and newlib) can run MirBSD Korn Shell. --- cfg/mksh/profile | 2 + qa/check | 5 + qa/kern/awake.c | 10 +- qa/lib/c/qlockt2.c | 2 +- qa/lib/c/rlockt1.c | 4 +- qa/lib/c/rlockt2.c | 2 +- qa/lib/c/rsleept2.c | 2 +- qa/lib/c/wlockt2.c | 2 +- qa/lib/newlib/001-hello.c | 7 + qa/lib/newlib/002-atexit.c | 27 + qa/lib/newlib/041-env.c | 44 + qa/lib/newlib/041-env.runner | 32 + qa/lib/newlib/050-setsid.c | 71 +- qa/lib/newlib/050-setsid.runner | 2 +- qa/lib/newlib/120-fcntl.c | 48 + qa/lib/newlib/121-fcntl.c | 82 + qa/lib/newlib/200-signals.c | 7 +- qa/lib/newlib/201-signals.c | 7 +- qa/lib/newlib/202-signals.c | 6 +- qa/lib/newlib/203-signals.c | 8 +- qa/lib/newlib/204-signals.c | 6 +- qa/lib/newlib/206-signals.c | 51 + qa/lib/newlib/207-sigsuspend.c | 74 + qa/lib/newlib/208-sigpending.c | 69 + qa/lib/newlib/209-sigwaitinfo.c | 54 + qa/lib/newlib/210-sigtimedwait.c | 58 + qa/lib/newlib/211-sigtimedwait.c | 61 + qa/lib/newlib/212-sigwait.c | 54 + qa/lib/newlib/213-sigqueue.c | 88 ++ qa/lib/newlib/214-sigsetjmp.c | 132 ++ qa/lib/newlib/215-sigprocmask.c | 38 + qa/lib/newlib/build.json | 36 +- qa/lib/newlib/libposix_customization.c | 20 +- qa/lib/newlib/libposix_sigchld.c | 20 +- sys/include/posix.h | 271 +++- sys/src/cmd/sys/build.json | 6 +- sys/src/lib/libs.json | 4 +- sys/src/lib/posix/build.json | 22 + sys/src/lib/posix/environment.c | 98 +- sys/src/lib/posix/errors.c | 35 + sys/src/lib/posix/fcntl.c | 159 ++ sys/src/lib/posix/files.c | 71 + sys/src/lib/posix/ids.c | 217 ++- sys/src/lib/posix/initlib.c | 84 +- sys/src/lib/posix/internal.h | 86 +- sys/src/lib/posix/kill.c | 50 + sys/src/lib/posix/others.c | 9 +- sys/src/lib/posix/posixly.c | 1941 ++++++++++++++++++++++++ sys/src/lib/posix/processes.c | 124 +- sys/src/lib/posix/sigchlds.c | 223 ++- sys/src/lib/posix/signals.c | 560 ++++--- sys/src/lib/posix/sigqueue.c | 49 + sys/src/lib/posix/sigsets.c | 292 ++++ sys/src/lib/posix/sigsuspend.c | 46 + sys/src/lib/posix/termios.c | 62 + sys/src/lib/posix/timers.c | 7 + 56 files changed, 4939 insertions(+), 608 deletions(-) create mode 100644 cfg/mksh/profile create mode 100644 qa/lib/newlib/001-hello.c create mode 100644 qa/lib/newlib/002-atexit.c create mode 100644 qa/lib/newlib/041-env.c create mode 100644 qa/lib/newlib/041-env.runner create mode 100644 qa/lib/newlib/120-fcntl.c create mode 100644 qa/lib/newlib/121-fcntl.c create mode 100644 qa/lib/newlib/206-signals.c create mode 100644 qa/lib/newlib/207-sigsuspend.c create mode 100644 qa/lib/newlib/208-sigpending.c create mode 100644 qa/lib/newlib/209-sigwaitinfo.c create mode 100644 qa/lib/newlib/210-sigtimedwait.c create mode 100644 qa/lib/newlib/211-sigtimedwait.c create mode 100644 qa/lib/newlib/212-sigwait.c create mode 100644 qa/lib/newlib/213-sigqueue.c create mode 100644 qa/lib/newlib/214-sigsetjmp.c create mode 100644 qa/lib/newlib/215-sigprocmask.c create mode 100644 sys/src/lib/posix/fcntl.c create mode 100644 sys/src/lib/posix/kill.c create mode 100644 sys/src/lib/posix/posixly.c create mode 100644 sys/src/lib/posix/sigqueue.c create mode 100644 sys/src/lib/posix/sigsets.c create mode 100644 sys/src/lib/posix/sigsuspend.c create mode 100644 sys/src/lib/posix/termios.c diff --git a/cfg/mksh/profile b/cfg/mksh/profile new file mode 100644 index 0000000..c37a2e0 --- /dev/null +++ b/cfg/mksh/profile @@ -0,0 +1,2 @@ +export PWD=`cat /dev/wdir` +export HOSTNAME=$SYSNAME diff --git a/qa/check b/qa/check index 5137fe3..25583c6 100755 --- a/qa/check +++ b/qa/check @@ -2,6 +2,11 @@ rfork +if( ! test -d /dev/posix ){ + sys/posixly -d /tmp/qa-posixly.log -p $PID & +# sys/ctrace -o /tmp/posixly.trace $APID & +} + dir=$1 if(~ $dir '') dir=/qa diff --git a/qa/kern/awake.c b/qa/kern/awake.c index 4a74c13..9d7e9d3 100644 --- a/qa/kern/awake.c +++ b/qa/kern/awake.c @@ -74,7 +74,7 @@ main(void) elapsed = (nsec() - start) / (1000 * 1000); if(verbose) fprint(2, "rendezvous interrupted, returned %#p, elapsed = %d ms\n", res, elapsed); - if(!awakened(wkup) || elapsed < 900 || elapsed > 1300){ + if(!awakened(wkup) || elapsed < 900 || elapsed > 1800){ print("FAIL: rendezvous\n"); exits("FAIL"); } @@ -105,7 +105,7 @@ main(void) elapsed = (nsec() - start) / (1000 * 1000); if(verbose) print("semacquire(&sem, 1): returned %lld, elapsed = %d ms\n", res, elapsed); - if(!awakened(wkup) || res != -1 || elapsed < 900 || elapsed > 1300){ + if(!awakened(wkup) || res != -1 || elapsed < 900 || elapsed > 1800){ print("FAIL: semacquire\n"); exits("FAIL"); } @@ -113,8 +113,8 @@ main(void) /* verify that tsemacquire are NOT interrupted */ fprint(2, "verify that tsemacquire are NOT interrupted\n", elapsed); - wkup = awake(700); start = nsec(); + wkup = awake(500); res = tsemacquire(&sem, 1500); elapsed = (nsec() - start) / (1000 * 1000); if(verbose) @@ -137,7 +137,7 @@ main(void) elapsed = (nsec() - start) / (1000 * 1000); if(verbose) fprint(2, "read(fds[0], buf, 1) returned %lld, elapsed = %d ms\n", res, elapsed); - if(!awakened(wkup) || res != -1 || elapsed < 900 || elapsed > 1300){ + if(!awakened(wkup) || res != -1 || elapsed < 900 || elapsed > 1800){ print("FAIL: read\n"); exits("FAIL"); } @@ -155,7 +155,7 @@ main(void) elapsed = (nsec() - start) / (1000 * 1000); if(verbose) fprint(2, "writeTillBlock(fds[0]) returned %lld, elapsed = %d ms\n", res, elapsed); - if(!awakened(wkup) || res >= 256 || elapsed < 900 || elapsed > 1300){ + if(!awakened(wkup) || res >= 256 || elapsed < 900 || elapsed > 1800){ print("FAIL: write\n"); exits("FAIL"); } diff --git a/qa/lib/c/qlockt2.c b/qa/lib/c/qlockt2.c index 7a0ee04..ddb4a87 100644 --- a/qa/lib/c/qlockt2.c +++ b/qa/lib/c/qlockt2.c @@ -208,7 +208,7 @@ main(int argc, char* argv[]) } average = average / NPROC / (1000 * 1000); - if(average < 300) /* we asked for 1ms... we are dumb, after all */ + if(average < 1000) /* we asked for 1ms... we are dumb, after all */ { print("PASS\n"); exits("PASS"); diff --git a/qa/lib/c/rlockt1.c b/qa/lib/c/rlockt1.c index 2756c92..3246f0f 100644 --- a/qa/lib/c/rlockt1.c +++ b/qa/lib/c/rlockt1.c @@ -112,7 +112,7 @@ waiter(int index) if(verbose) print("reader %d: got the rlock in %lld ms\n", getpid(), (end - start) / (1000*1000)); runlock(&afterAWhile); - if((end - start) / (1000*1000) > 1500) + if((end - start) / (1000*1000) > 2000) postnote(PNGROUP, getpid(), smprint("fail: reader %d got the rlock after %lld ms", getpid(), (end - start) / (1000*1000))); } else { if(verbose) @@ -127,7 +127,7 @@ waiter(int index) rwakeup(&rCompleted); qunlock(&rl); - return (end - start) / (1000*1000) < 1300 ? nil : "FAIL"; + return (end - start) / (1000*1000) < 2000 ? nil : "FAIL"; } void diff --git a/qa/lib/c/rlockt2.c b/qa/lib/c/rlockt2.c index e232574..a370358 100644 --- a/qa/lib/c/rlockt2.c +++ b/qa/lib/c/rlockt2.c @@ -214,7 +214,7 @@ main(int argc, char* argv[]) } average = average / NPROC / (1000 * 1000); - if(average < 300) /* we asked for 1ms... we are dumb, after all */ + if(average < 800) /* we asked for 1ms... we are dumb, after all */ { print("PASS\n"); exits("PASS"); diff --git a/qa/lib/c/rsleept2.c b/qa/lib/c/rsleept2.c index de351d3..93572e8 100644 --- a/qa/lib/c/rsleept2.c +++ b/qa/lib/c/rsleept2.c @@ -215,7 +215,7 @@ main(int argc, char* argv[]) } average = average / NPROC / (1000 * 1000); - if(average < 500) /* we asked for 1ms... we are dumb, after all */ + if(average < 1000) /* we asked for 1ms... we are dumb, after all */ { print("PASS\n"); exits("PASS"); diff --git a/qa/lib/c/wlockt2.c b/qa/lib/c/wlockt2.c index 7a792fd..2cdae23 100644 --- a/qa/lib/c/wlockt2.c +++ b/qa/lib/c/wlockt2.c @@ -210,7 +210,7 @@ main(int argc, char* argv[]) } average = average / NPROC / (1000 * 1000); - if(average < 300) /* we asked for 1ms... we are dumb, after all */ + if(average < 1000) /* we asked for 1ms... we are dumb, after all */ { print("PASS\n"); exits("PASS"); diff --git a/qa/lib/newlib/001-hello.c b/qa/lib/newlib/001-hello.c new file mode 100644 index 0000000..8978a35 --- /dev/null +++ b/qa/lib/newlib/001-hello.c @@ -0,0 +1,7 @@ +#include +int main() +{ + fprintf(stdout, "hello, stdout\n"); + fprintf(stderr, "hello, stderr\n"); + return 0; +} diff --git a/qa/lib/newlib/002-atexit.c b/qa/lib/newlib/002-atexit.c new file mode 100644 index 0000000..591c53c --- /dev/null +++ b/qa/lib/newlib/002-atexit.c @@ -0,0 +1,27 @@ +#include +#include +#include + +void +bye(void) +{ + printf("That was all, folks\n"); + exit(0); +} + +int +main(void) +{ +// long a; + int i; + +// a = sysconf(_SC_ATEXIT_MAX); +// printf("ATEXIT_MAX = %ld\n", a); + + i = atexit(bye); + if (i != 0) { + fprintf(stderr, "cannot set exit function\n"); + return 1; + } + return 2; +} diff --git a/qa/lib/newlib/041-env.c b/qa/lib/newlib/041-env.c new file mode 100644 index 0000000..d98dc04 --- /dev/null +++ b/qa/lib/newlib/041-env.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include + +int +main () +{ + int child, status; + printf("Parent $PATH = %s\n", getenv("PATH")); + printf("Parent $HOME = %s\n", getenv("HOME")); + printf("Parent $USER = %s\n", getenv("USER")); + printf("Parent $IFS = %s\n", getenv("IFS")); + printf("Parent $ROOT = %s\n", getenv("ROOT")); + printf("Parent $TEST = %s\n", getenv("TEST")); + putenv("TEST=value"); + printf("Parent putenv(TEST=value); now $TEST = %s\n", getenv("TEST")); + fflush(stdout); + + switch(child = fork()){ + case 0: + printf("Child $PATH = %s\n", getenv("PATH")); + printf("Child $HOME = %s\n", getenv("HOME")); + printf("Child $USER = %s\n", getenv("USER")); + printf("Child $IFS = %s\n", getenv("IFS")); + printf("Child $ROOT = %s\n", getenv("ROOT")); + printf("Child $TEST = %s\n", getenv("TEST")); + exit(0); + case -1: + printf("FAIL: fork\n"); + return 1; + default: + wait(&status); + break; + } + + unsetenv("TEST"); + printf("Parent unsetenv(TEST); now $TEST = %s\n", getenv("TEST")); + + if(status) + printf("FAIL: child returned %d\n", status); + exit(status); +} diff --git a/qa/lib/newlib/041-env.runner b/qa/lib/newlib/041-env.runner new file mode 100644 index 0000000..011baf4 --- /dev/null +++ b/qa/lib/newlib/041-env.runner @@ -0,0 +1,32 @@ +#!/cmd/rc +runner=$0 +test = `{echo $runner|sed 's/.runner//'} +test_output = /tmp/output-`{basename $test} + +if ( test -e $test_output) rm $test_output + +$test > $test_output + +expected_lines = ('Parent \$PATH = /cmd:.' \ +'Parent \$HOME = '^$HOME \ +'Parent \$USER = '^$USER \ +'Parent \$ROOT = \(null\)' \ +'Parent \$TEST = \(null\)' \ +'Parent putenv\(TEST=value\); now \$TEST = value' \ +'Child \$PATH = /cmd:.' \ +'Child \$HOME = '^$HOME \ +'Child \$USER = '^$USER \ +'Child \$ROOT = \(null\)' \ +'Child \$TEST = value' \ +'Parent unsetenv\(TEST\); now \$TEST = \(null\)' \ +PASS ) + +for (line in $expected_lines) { + if ( ! cat $test_output | grep $"line > /dev/null ) { + cat $test_output + echo FAIL: can not find line: $line + exit FAIL + } +} +echo PASS +exit PASS diff --git a/qa/lib/newlib/050-setsid.c b/qa/lib/newlib/050-setsid.c index 5b7f846..99890ba 100644 --- a/qa/lib/newlib/050-setsid.c +++ b/qa/lib/newlib/050-setsid.c @@ -11,43 +11,46 @@ main(int argc, char **argv) int p[2], ppgrp, opgrp, npgrp; char c = '?'; - if (pipe(p) != 0) + if (pipe(p) != 0){ perror("pipe() error"); - else { - ppgrp = getpgrp(); - printf("parent's pid %d; process group id %d\n", getpid(), ppgrp); - if ((pid = fork()) == 0) { - opgrp = getpgrp(); - printf("child's pid %d; process group id %d\n", getpid(), opgrp); - write(p[1], &c, 1); - setsid(); - npgrp = getpgrp(); - if(opgrp == npgrp){ - printf("FAIL: setsid did not changed child's process group id\n"); - exit(EXIT_FAILURE); - } - printf("child's process group id is now %d\n", npgrp); - sleep(5); - exit(EXIT_SUCCESS); - } else { - read(p[0], &c, 1); - sleep(3); - if(ppgrp != getpgrp()){ - printf("FAIL: parent's process group id changed from %d to %d\n", ppgrp, getpgrp()); - exit(EXIT_FAILURE); - } + exit(EXIT_FAILURE); + } + ppgrp = getpgrp(); + printf("parent's pid %d; process group id %d\n", getpid(), ppgrp); + if ((pid = fork()) == 0) { + opgrp = getpgrp(); + printf("child's pid %d; process group id %d\n", getpid(), opgrp); + if(setsid() == -1){ + write(p[1], "e", 1); + perror("FAIL: setsid"); + exit(EXIT_FAILURE); + } + write(p[1], &c, 1); + npgrp = getpgrp(); + if(opgrp == npgrp){ + printf("FAIL: setsid did not changed child's process group id\n"); + exit(EXIT_FAILURE); + } + printf("child's process group id is now %d\n", npgrp); + sleep(5); + exit(EXIT_SUCCESS); + } else { + read(p[0], &c, 1); + sleep(3); + if(c == 'e') + exit(EXIT_FAILURE); + if(ppgrp != getpgrp()){ + printf("FAIL: parent's process group id changed from %d to %d\n", ppgrp, getpgrp()); + exit(EXIT_FAILURE); + } #ifndef WITH_SIGCHLD - npgrp = getsid(pid); - if(npgrp < 0){ - printf("FAIL: parent's getsid(%d) failed with errno %d\n", pid, errno); - exit(EXIT_FAILURE); - } - if(npgrp == ppgrp){ - printf("FAIL: parent's getsid(%d) returned old process group id that should be changed\n", pid); - exit(EXIT_FAILURE); - } -#endif + npgrp = getsid(pid); + if(npgrp == ppgrp){ + printf("FAIL: parent's getsid(%d) returned old process group id that should be changed\n", pid); + exit(EXIT_FAILURE); } +#endif + exit(EXIT_SUCCESS); } } diff --git a/qa/lib/newlib/050-setsid.runner b/qa/lib/newlib/050-setsid.runner index b20c4b5..47c5d6b 100755 --- a/qa/lib/newlib/050-setsid.runner +++ b/qa/lib/newlib/050-setsid.runner @@ -5,7 +5,7 @@ test_output = /tmp/output-`{basename $test} if ( test -e $test_output) rm $test_output -$test $test_output > /dev/null +$test > $test_output if ( cat $test_output | grep 'FAIL' > /dev/null ) { cat $test_output echo FAIL diff --git a/qa/lib/newlib/120-fcntl.c b/qa/lib/newlib/120-fcntl.c new file mode 100644 index 0000000..cd5bcff --- /dev/null +++ b/qa/lib/newlib/120-fcntl.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include +#include + +// see http://www.informit.com/articles/article.aspx?p=99706&seqNum=13 +int +main(int argc, char *argv[]) +{ + + int fd, accmode, val; + + if (argc != 2) + fd = 0; + else + fd = atoi(argv[1]); + + if ( (val = fcntl(fd, F_GETFL, 0)) < 0){ + perror("fcntl error for fd"); + exit(1); + } + + printf("fcntl(%d) returns %d\n", fd, val); + + accmode = val & O_ACCMODE; + if (accmode == O_RDONLY) + printf("read only"); + else if (accmode == O_WRONLY) + printf("write only"); + else if (accmode == O_RDWR) + printf("read write"); + else { + perror("unknown access mode"); + exit(1); + } + + if (val & O_APPEND) + printf(", append"); + if (val & O_NONBLOCK) + printf(", nonblocking"); +#if !defined(_POSIX_SOURCE) && defined(O_SYNC) + if (val & O_SYNC) + printf(", synchronous writes"); +#endif + putchar('\n'); + exit(0); +} diff --git a/qa/lib/newlib/121-fcntl.c b/qa/lib/newlib/121-fcntl.c new file mode 100644 index 0000000..ce423aa --- /dev/null +++ b/qa/lib/newlib/121-fcntl.c @@ -0,0 +1,82 @@ +#include +#include +#include +#include +#include + +void +fail_if_still_open(int fd) +{ + if(fd == 0){ + printf("atoi failed\n"); + exit(2); + } + if(fcntl(fd, F_GETFD) != -1){ + printf("fd %d is still open\n", fd); + exit(3); + } +} + +int +main(int argc, char *argv[]) +{ + int sync[2]; + int outfd; + int nullfd; + int tmp; + char *eargv[7], *p; + char buf[128]; + + if(argc == 1){ + printf("cat /proc/%d/fd\n", getpid()); + + pipe(sync); + outfd = dup(1); + nullfd = open("/dev/null", O_WRONLY); + tmp = fcntl(sync[0], F_DUPFD_CLOEXEC, 1); + close(sync[0]); + sync[0] = tmp; + tmp = fcntl(sync[1], F_DUPFD_CLOEXEC, 1); + close(sync[1]); + sync[1] = tmp; + tmp = fcntl(outfd, F_DUPFD_CLOEXEC, 1); + close(outfd); + outfd = tmp; + tmp = fcntl(nullfd, F_DUPFD_CLOEXEC, 1); + close(nullfd); + nullfd = tmp; + + eargv[0] = argv[0]; + p = buf; + eargv[1] = p; + p += 1+sprintf(p, "%d", sync[0]); + eargv[2] = p; + p += 1+sprintf(p, "%d", sync[1]); + eargv[3] = p; + p += 1+sprintf(p, "%d", outfd); + eargv[4] = p; + p += 1+sprintf(p, "%d", nullfd); + eargv[5] = NULL; + + execvp(argv[0], eargv); + printf("execvp returned\n"); + exit(100); + + } else if(argc != 5){ + printf("argc = %d (should be 5)\n", argc); + exit(1); + } + + printf("argc = %d; fds: %s %s %s %s\n", argc, argv[1], argv[2], argv[3], argv[4] ); + sync[0] = atoi(argv[1]); + sync[1] = atoi(argv[2]); + outfd = atoi(argv[3]); + nullfd = atoi(argv[4]); + + fail_if_still_open(sync[0]); + fail_if_still_open(sync[1]); + fail_if_still_open(outfd); + fail_if_still_open(nullfd); + + exit(0); +} diff --git a/qa/lib/newlib/200-signals.c b/qa/lib/newlib/200-signals.c index 5b817be..a5a1746 100644 --- a/qa/lib/newlib/200-signals.c +++ b/qa/lib/newlib/200-signals.c @@ -58,6 +58,7 @@ main() { signal(SIGQUIT, sigquit); printf("\nChild going to loop...\n\n"); + write(p[1], "", 1); close(p[1]); close(p[0]); for(;;); /* loop for ever */ @@ -65,11 +66,11 @@ main() { else /* parent */ { signal(SIGCHLD,sigchld); - close(p[1]); - if(read(p[0], &dummy, 1) > 0){ - printf("sync read received data"); + if(read(p[0], &dummy, 1) != 1){ + printf("sync read"); exit(EXIT_FAILURE); } + close(p[1]); close(p[0]); printf("\nPARENT: sending SIGHUP\n\n"); kill(pid,SIGHUP); diff --git a/qa/lib/newlib/201-signals.c b/qa/lib/newlib/201-signals.c index 13ad0fb..99ee57e 100644 --- a/qa/lib/newlib/201-signals.c +++ b/qa/lib/newlib/201-signals.c @@ -26,6 +26,7 @@ main() { if (pid == 0) { printf("\nI am the new child!\n\n"); + write(p[1], "", 1); close(p[1]); close(p[0]); for(;;){ @@ -36,11 +37,11 @@ main() { } else /* parent */ { - close(p[1]); - if(read(p[0], &dummy, 1) > 0){ - printf("sync read received data"); + if(read(p[0], &dummy, 1) != 1){ + printf("sync read"); exit(EXIT_FAILURE); } + close(p[1]); close(p[0]); printf("\nPARENT: sending SIGINT\n\n"); kill(pid,SIGINT); diff --git a/qa/lib/newlib/202-signals.c b/qa/lib/newlib/202-signals.c index ccc2437..49f2560 100644 --- a/qa/lib/newlib/202-signals.c +++ b/qa/lib/newlib/202-signals.c @@ -28,6 +28,7 @@ main() { if (pid == 0) { signal(SIGCONT,sigcont); /* set function calls */ printf("Child going to loop...\n"); + write(p[1], "", 1); close(p[1]); close(p[0]); for(;;){ @@ -38,11 +39,8 @@ main() { } else /* parent */ { + read(p[0], &dummy, 1); close(p[1]); - if(read(p[0], &dummy, 1) > 0){ - printf("sync read received data"); - exit(EXIT_FAILURE); - } close(p[0]); printf("PARENT: sending SIGCONT\n\n"); kill(pid,SIGCONT); diff --git a/qa/lib/newlib/203-signals.c b/qa/lib/newlib/203-signals.c index 4e043ca..a14febb 100644 --- a/qa/lib/newlib/203-signals.c +++ b/qa/lib/newlib/203-signals.c @@ -22,7 +22,8 @@ void childloop(void) { signal(SIGCONT,sigcont); /* set function calls */ signal(SIGSTOP,sigstop); /* set function calls */ - printf("Child going to loop...\n"); + printf("Child %d going to loop...\n", getpid()); + write(p[1], "", 1); close(p[1]); close(p[0]); for(;;){ @@ -54,11 +55,8 @@ main() { } else /* parent */ { + read(p[0], &dummy, 1); close(p[1]); - if(read(p[0], &dummy, 1) > 0){ - printf("sync read received data"); - exit(EXIT_FAILURE); - } close(p[0]); sleep(2); printf("PARENT: sending SIGSTOP\n"); diff --git a/qa/lib/newlib/204-signals.c b/qa/lib/newlib/204-signals.c index c78f6d5..cf2c2b2 100644 --- a/qa/lib/newlib/204-signals.c +++ b/qa/lib/newlib/204-signals.c @@ -47,6 +47,7 @@ main() { signal(SIGQUIT, sigquit); printf("Child going to loop...\n"); + write(p[1], "", 1); close(p[1]); close(p[0]); for(;;); /* loop for ever */ @@ -54,11 +55,8 @@ main() { else /* parent */ { signal(SIGCHLD,sigchld); + read(p[0], &dummy, 1); close(p[1]); - if(read(p[0], &dummy, 1) > 0){ - printf("sync read received data"); - exit(EXIT_FAILURE); - } close(p[0]); printf("PARENT: sending SIGHUP\n"); kill(pid,SIGHUP); diff --git a/qa/lib/newlib/206-signals.c b/qa/lib/newlib/206-signals.c new file mode 100644 index 0000000..b85bfcc --- /dev/null +++ b/qa/lib/newlib/206-signals.c @@ -0,0 +1,51 @@ +#include +#include +#include +#include + +void sigusr1() { + printf("Got SIGUSR1\n"); + exit(0); +} + +int +main() +{ + sigset_t old_set, new_set; + sigemptyset(&old_set); + sigemptyset(&new_set); + + signal(SIGUSR1, sigusr1); + + if(sigaddset(&new_set, SIGSEGV) == 0) + { + printf("sigaddset successfully added for SIGSEGV\n"); + } + sigprocmask(SIG_BLOCK, &new_set, &old_set); + + printf("raise(SIGSEGV)\n"); + raise(SIGSEGV); + + if(sigaddset(&new_set, SIGUSR1) == 0) + { + printf("sigaddset successfully added for SIGUSR1\n"); + } + if(sigprocmask(SIG_BLOCK, &new_set, &old_set) == -1) + { + perror("sigprocmask"); + } + + printf("raise(SIGUSR1)\n"); + raise(SIGUSR1); + + sigemptyset(&new_set); + sigaddset(&new_set, SIGUSR1); + + printf("unblock SIGUSR1 via sigprocmask\n"); + if(sigprocmask(SIG_UNBLOCK, &new_set, &old_set) == -1) + { + perror("sigprocmask"); + } + + return 1; +} diff --git a/qa/lib/newlib/207-sigsuspend.c b/qa/lib/newlib/207-sigsuspend.c new file mode 100644 index 0000000..29f8546 --- /dev/null +++ b/qa/lib/newlib/207-sigsuspend.c @@ -0,0 +1,74 @@ +#define _POSIX_SOURCE +#include +#include +#include +#include +#include + +int SIGUSR1_caught; +int SIGUSR2_caught; + +void catcher(int signum) { + switch (signum) { + case SIGUSR1: + ++SIGUSR1_caught; + puts("catcher caught SIGUSR1"); + if(SIGUSR2_caught){ + puts("FAIL: SIGUSR2 already caught"); + exit(1); + } + break; + case SIGUSR2: + ++SIGUSR2_caught; + puts("catcher caught SIGUSR2"); + break; + default: + printf("catcher caught unexpected signal %d\n", signum); + } +} + +int +main() +{ + sigset_t sigset; + struct sigaction sact; + time_t t; + + if (fork() == 0) { + printf("child is %d\n", getpid()); + sleep(10); + puts("child is sending SIGUSR2 signal - which should be blocked"); + kill(getppid(), SIGUSR2); + sleep(5); + puts("child is sending SIGUSR1 signal - which should be caught"); + kill(getppid(), SIGUSR1); + exit(0); + } + + sigemptyset(&sact.sa_mask); + sact.sa_flags = 0; + sact.sa_handler = catcher; + + if (sigaction(SIGUSR1, &sact, NULL) != 0) + perror("1st sigaction() error"); + else if (sigaction(SIGUSR2, &sact, NULL) != 0) + perror("2nd sigaction() error"); + else { + sigfillset(&sigset); + sigdelset(&sigset, SIGUSR1); + time(&t); + printf("parent is waiting for child to send SIGUSR1 at %s", + ctime(&t)); + if (sigsuspend(&sigset) == -1) + perror("sigsuspend() returned -1 as expected"); + time(&t); + printf("sigsuspend is over at %s", ctime(&t)); + } + if(SIGUSR1_caught != 1) + printf("SIGUSR1_caught is %d\n", SIGUSR1_caught); + if(SIGUSR2_caught != 1) + printf("SIGUSR2_caught is %d\n", SIGUSR2_caught); + if(SIGUSR1_caught != 1 || SIGUSR2_caught != 1) + exit(2); + exit(0); +} diff --git a/qa/lib/newlib/208-sigpending.c b/qa/lib/newlib/208-sigpending.c new file mode 100644 index 0000000..df1d17e --- /dev/null +++ b/qa/lib/newlib/208-sigpending.c @@ -0,0 +1,69 @@ +#include +#include +#include +#include + +void catcher(int sig) { + puts("Got SIGUSR1"); +} + +int check_pending(int sig, char *signame) { + + sigset_t sigset; + + if(sigpending(&sigset) != 0){ + perror("sigpending() error\n"); + exit(1); + } + if(sigismember(&sigset, sig)){ + printf("a %s (%d) signal is pending\n", signame, sig); + return 1; + } else { + printf("no %s (%d) signals are pending\n", signame, sig); + return 0; + } +} + +int main(int argc, char *argv[]) { + + struct sigaction sigact; + sigset_t sigset; + + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + sigact.sa_handler = catcher; + + if(sigaction(SIGUSR1, &sigact, NULL) != 0){ + perror("sigaction() error\n"); + return 2; + } + + printf("Calling sigprocmask to block SIGUSR1...\n"); + sigemptyset(&sigset); + sigaddset(&sigset, SIGUSR1); + if (sigprocmask(SIG_SETMASK, &sigset, NULL) != 0){ + perror("sigprocmask() error\n"); + return 3; + } + printf("SIGUSR1 signals are now blocked\n"); + + kill(getpid(), SIGUSR1); + printf("kill(getpid(), SIGUSR1) DONE\n"); + + if(!check_pending(SIGUSR1, "SIGUSR1")){ + printf("FAIL: SIGUSR1 is not pending despite the mask\n"); + return 4; + } + + printf("Calling sigprocmask to unblock SIGUSR1...\n"); + sigemptyset(&sigset); + sigprocmask(SIG_SETMASK, &sigset, NULL); + printf("SIGUSR1 signals are no longer blocked\n"); + + if(check_pending(SIGUSR1, "SIGUSR1")){ + printf("FAIL: SIGUSR1 is still pending despite the mask\n"); + return 4; + } + + return 0; +} diff --git a/qa/lib/newlib/209-sigwaitinfo.c b/qa/lib/newlib/209-sigwaitinfo.c new file mode 100644 index 0000000..12ca74e --- /dev/null +++ b/qa/lib/newlib/209-sigwaitinfo.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include + +void catcher(int sig) { + printf("Signal catcher called for signal %d\n", sig); +} + +void timestamp(char *str) { + time_t t; + + time(&t); + printf("The time %s is %s", str, ctime(&t)); +} + +int main(int argc, char *argv[]) { + + int result = 0; + int err = 0; + + struct sigaction sigact; + sigset_t waitset; + siginfo_t info; + + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + sigact.sa_handler = catcher; + sigaction(SIGALRM, &sigact, NULL); + + sigemptyset(&waitset); + sigaddset(&waitset, SIGALRM); + + sigprocmask(SIG_BLOCK, &waitset, NULL); + + alarm(3); + + timestamp("before sigwaitinfo()"); + + result = sigwaitinfo(&waitset, &info); + err = errno; + + timestamp("after sigwaitinfo()"); + + if(result > 0){ + printf("sigwaitinfo() returned for signal %d\n", info.si_signo); + return 0; + } + + printf("sigwaitinfo() returned %d; errno = %d\n", result, err); + perror("sigwaitinfo() function failed"); + return 1; +} diff --git a/qa/lib/newlib/210-sigtimedwait.c b/qa/lib/newlib/210-sigtimedwait.c new file mode 100644 index 0000000..91d43da --- /dev/null +++ b/qa/lib/newlib/210-sigtimedwait.c @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include + +void catcher(int sig) { + printf("Signal catcher called for signal %d\n", sig); +} + +void timestamp(char *str) { + time_t t; + + time(&t); + printf("The time %s is %s\n", str, ctime(&t)); +} + +int main(int argc, char *argv[]) { + + int result = 0; + int err = 0; + + struct sigaction sigact; + sigset_t waitset; + siginfo_t info; + struct timespec timeout; + + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + sigact.sa_handler = catcher; + sigaction(SIGALRM, &sigact, NULL); + + sigemptyset(&waitset); + sigaddset(&waitset, SIGALRM); + + sigprocmask(SIG_BLOCK, &waitset, NULL); + + timeout.tv_sec = 4; /* Number of seconds to wait */ + timeout.tv_nsec = 1000; /* Number of nanoseconds to wait */ + + alarm(2); + + timestamp("before sigtimedwait()"); + + result = sigtimedwait(&waitset, &info, &timeout); + err = errno; + + timestamp("after sigtimedwait()"); + + if(result > 0){ + printf("sigtimedwait() returned for signal %d\n", info.si_signo); + return 0; + } + + printf("sigtimedwait() returned %d; errno = %d\n", result, err); + perror("sigtimedwait() function failed"); + return 1; +} diff --git a/qa/lib/newlib/211-sigtimedwait.c b/qa/lib/newlib/211-sigtimedwait.c new file mode 100644 index 0000000..085c757 --- /dev/null +++ b/qa/lib/newlib/211-sigtimedwait.c @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include + +void catcher(int sig) { + printf("Signal catcher called for signal %d\n", sig); +} + +void timestamp(char *str) { + time_t t; + + time(&t); + printf("The time %s is %s\n", str, ctime(&t)); +} + +int main(int argc, char *argv[]) { + + int result = 0; + int err = 0; + + struct sigaction sigact; + sigset_t waitset; + siginfo_t info; + struct timespec timeout; + + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + sigact.sa_handler = catcher; + sigaction(SIGALRM, &sigact, NULL); + + sigemptyset(&waitset); + sigaddset(&waitset, SIGALRM); + + sigprocmask(SIG_BLOCK, &waitset, NULL); + + timeout.tv_sec = 1; /* Number of seconds to wait */ + timeout.tv_nsec = 1000; /* Number of nanoseconds to wait */ + + alarm(4); + + timestamp("before sigtimedwait()"); + + result = sigtimedwait(&waitset, &info, &timeout); + err = errno; + + timestamp("after sigtimedwait()"); + + if(result > 0){ + printf("sigtimedwait() returned for signal %d\n", info.si_signo); + return 1; + } + + printf("sigtimedwait() returned %d; errno = %d\n", result, err); + if(err != EAGAIN){ + perror("errno is not EAGAIN"); + return 2; + } + return 0; +} diff --git a/qa/lib/newlib/212-sigwait.c b/qa/lib/newlib/212-sigwait.c new file mode 100644 index 0000000..5f3b032 --- /dev/null +++ b/qa/lib/newlib/212-sigwait.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include + +void catcher(int sig) { + printf("Signal catcher called for signal %d\n", sig); +} + +void timestamp(char *str) { + time_t t; + + time(&t); + printf("The time %s is %s", str, ctime(&t)); +} + +int main(int argc, char *argv[]) { + + int result = 0; + int err = 0; + + struct sigaction sigact; + sigset_t waitset; + int sig; + + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + sigact.sa_handler = catcher; + sigaction(SIGALRM, &sigact, NULL); + + sigemptyset(&waitset); + sigaddset(&waitset, SIGALRM); + + sigprocmask(SIG_BLOCK, &waitset, NULL); + + alarm(3); + + timestamp("before sigwait()"); + + result = sigwait(&waitset, &sig); + err = errno; + + timestamp("after sigwait()"); + + if(result == 0){ + printf("sigwait() returned for signal %d\n", sig); + return 0; + } + + printf("sigwait() returned %d; errno = %d\n", result, err); + perror("sigwait() function failed"); + return 1; +} diff --git a/qa/lib/newlib/213-sigqueue.c b/qa/lib/newlib/213-sigqueue.c new file mode 100644 index 0000000..0d9c12f --- /dev/null +++ b/qa/lib/newlib/213-sigqueue.c @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include +#include + +void catcher(int sig, siginfo_t *info, void *p) { + printf("Signal catcher called for signal %d from %d\n", sig, info->si_pid); + exit(1); +} + +void timestamp(char *str) { + time_t t; + + time(&t); + printf("The time %s is %s", str, ctime(&t)); +} + +void echoSIGUSR1(void){ + int result; + struct sigaction sigact; + sigset_t waitset; + siginfo_t info; + union sigval v; + + printf("CHILD pid %d\n", getpid()); + v.sival_int = 0; + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = SA_SIGINFO; + sigact.sa_sigaction = catcher; + sigaction(SIGUSR1, &sigact, NULL); + + sigemptyset(&waitset); + sigaddset(&waitset, SIGUSR1); + + sigprocmask(SIG_BLOCK, &waitset, NULL); + + while(v.sival_int < 5){ + result = sigwaitinfo(&waitset, &info); + v.sival_int = 1 + info.si_value.sival_int; + printf("CHILD sigqueue %d to %d\n", v.sival_int, info.si_pid); + sigqueue(info.si_pid, result, v); + } +} + +int main(int argc, char *argv[]) { + int result; + struct sigaction sigact; + sigset_t waitset; + siginfo_t info; + union sigval v; + pid_t child; + + switch(child = fork()){ + case 0: + echoSIGUSR1(); + exit(0); + break; + case -1: + exit(1); + break; + default: + break; + } + + printf("PARENT pid %d sleep(3)\n", getpid()); + sleep(3); + v.sival_int = 0; + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = SA_SIGINFO; + sigact.sa_sigaction = catcher; + sigaction(SIGUSR1, &sigact, NULL); + + sigemptyset(&waitset); + sigaddset(&waitset, SIGUSR1); + + sigprocmask(SIG_BLOCK, &waitset, NULL); + + result = SIGUSR1; + do{ + printf("PARENT sigqueue %d\n", v.sival_int); + sigqueue(child, result, v); + result = sigwaitinfo(&waitset, &info); + v.sival_int = 1 + info.si_value.sival_int; + } while(v.sival_int < 5); + exit(0); +} diff --git a/qa/lib/newlib/214-sigsetjmp.c b/qa/lib/newlib/214-sigsetjmp.c new file mode 100644 index 0000000..6e91bcc --- /dev/null +++ b/qa/lib/newlib/214-sigsetjmp.c @@ -0,0 +1,132 @@ +#include +#include +#include +#include +#include + +sigset_t sigset; +sigjmp_buf mark; +int catcherWasCalled; + +void catcher(int); +void p(void); + +int main(int argc, char *argv[]) { + + int result = 0; + int returnCode = 0; + + /* + * Block the SIGUSR2 signal. This signal set will be + * saved as part of the environment by the sigsetjmp() + * function and subsequently restored by the siglongjmp() + * function. + */ + + sigemptyset(&sigset); + sigaddset(&sigset, SIGUSR2); + sigprocmask(SIG_SETMASK, &sigset, NULL); + + /* Save the stack environment and the current signal mask */ + + returnCode = sigsetjmp(mark, 1); + + /* Handle the sigsetjmp return code */ + + switch(returnCode) { + case 0: + printf("sigsetjmp() has been called\n"); + + /* + * Call function p() which will call the siglongjmp() + * function + */ + p(); + + printf("control returning here is an error\n"); + result=-1; + break; + case -1: + printf("siglongjmp() function was called\n"); + + /* Retrieve the current signal mask */ + + sigprocmask(SIG_SETMASK, NULL, &sigset); + + /* Verify SIGUSR2 is in sigset */ + if(sigismember(&sigset, SIGUSR2)) { + printf("signal mask was restored after siglongjmp()\n"); + result=0; + } else { + printf("signal mask was not restored after siglongjmp()\n"); + result=-1; + } + break; + default: + printf("this unexpected return code is an error\n"); + result=-1; + break; + } + + printf("return from main with result %d\n", result); + + return result; +} + +void p(void) { + + struct sigaction sigact; + int error=0; + + printf("performing function p()\n"); + + /* Setup signal handler in case error condition is detected */ + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + sigact.sa_handler = catcher; + sigaction(SIGUSR2, &sigact, NULL); + + /* + * Delete SIGUSR2 from the signal set that was initialized + * by the main() function. This allows us to demonstrate + * that the original signal set saved by the sigsetjmp() function + * is restored by the siglongjmp() function. + */ + sigdelset(&sigset, SIGUSR2); + sigprocmask(SIG_SETMASK, &sigset, NULL); + + /* After some processing an error condition is detected */ + error=-1; + + /* Call catcher() function if error is detected */ + if(error != 0) { + catcherWasCalled = 0; + + /* Send SIGUSR2 to handle the error condition */ + + printf("error condition detected, send SIGUSR2 signal\n"); + kill(getpid(), SIGUSR2); + + if(catcherWasCalled == 1) { + printf("catcher() function handled the error condition\n"); + + /* + * Perform a nonlocal "goto" and specify -1 for the + * return value + */ + + siglongjmp(mark, -1); + + printf("control getting here is an error\n"); + exit(3); + } + } +} + +void catcher(int signo) { + /* + * Indicate the catcher() function is handling the + * SIGUSR2 signal. + */ + catcherWasCalled = 1; +} diff --git a/qa/lib/newlib/215-sigprocmask.c b/qa/lib/newlib/215-sigprocmask.c new file mode 100644 index 0000000..64aad21 --- /dev/null +++ b/qa/lib/newlib/215-sigprocmask.c @@ -0,0 +1,38 @@ +#include +#include +#include +#include +#include + +sigset_t parent_mask; + +int +main() +{ + int status; + sigset_t mask; + + sigprocmask(SIG_BLOCK, NULL, &parent_mask); + sigaddset(&parent_mask, SIGSEGV); + sigprocmask(SIG_BLOCK, &parent_mask, NULL); + printf("SIGSEGV blocked in parent. Forking...\n"); + + switch(fork()){ + case -1: + exit(1); + case 0: + sigprocmask(SIG_BLOCK, NULL, &mask); + printf("Is SIGSEGV (%llx) a member of %llx?\n", 1ULL<<(SIGSEGV-1), (long long unsigned int)mask); + if(sigismember(&mask, SIGSEGV) != 1){ + printf("FAIL: SIGSEGV is not present in child's mask after fork\n"); + exit(2); + } + printf("PASS\n"); + exit(0); + default: + wait(&status); + status = WEXITSTATUS(status); + exit(status); + } + +} diff --git a/qa/lib/newlib/build.json b/qa/lib/newlib/build.json index dd65d59..4ad9c0b 100644 --- a/qa/lib/newlib/build.json +++ b/qa/lib/newlib/build.json @@ -32,22 +32,37 @@ ], "SourceFilesCmd": [ "000-hello.c", + "001-hello.c", + "002-atexit.c", "010-fork.c", "020-waitpid.c", "030-pause.c", "031-setjmp.c", "040-gettimeofday.c", + "041-env.c", "050-setsid.c", "100-files.c", "101-files.c", "102-files.c", "103-files.c", + "120-fcntl.c", + "121-fcntl.c", "200-signals.c", "201-signals.c", "202-signals.c", "203-signals.c", "204-signals.c", - "205-signals.c" + "205-signals.c", + "206-signals.c", + "207-sigsuspend.c", + "208-sigpending.c", + "209-sigwaitinfo.c", + "210-sigtimedwait.c", + "211-sigtimedwait.c", + "212-sigwait.c", + "213-sigqueue.c", + "214-sigsetjmp.c", + "215-sigprocmask.c" ] }, "SIGCHLDTests": { @@ -84,20 +99,37 @@ ], "SourceFilesCmd": [ "000-hello.c", + "001-hello.c", + "002-atexit.c", "010-fork.c", "020-waitpid.c", "030-pause.c", + "031-setjmp.c", "040-gettimeofday.c", + "041-env.c", + "050-setsid.c", "100-files.c", "101-files.c", "102-files.c", "103-files.c", + "120-fcntl.c", + "121-fcntl.c", "200-signals.c", "201-signals.c", "202-signals.c", "203-signals.c", "204-signals.c", - "205-signals.c" + "205-signals.c", + "206-signals.c", + "207-sigsuspend.c", + "208-sigpending.c", + "209-sigwaitinfo.c", + "210-sigtimedwait.c", + "211-sigtimedwait.c", + "212-sigwait.c", + "213-sigqueue.c", + "214-sigsetjmp.c", + "215-sigprocmask.c" ] }, "NewlibTestsuite": { diff --git a/qa/lib/newlib/libposix_customization.c b/qa/lib/newlib/libposix_customization.c index 65767d4..8a701b6 100644 --- a/qa/lib/newlib/libposix_customization.c +++ b/qa/lib/newlib/libposix_customization.c @@ -27,18 +27,34 @@ qa_exit_translator(int status) * should return PASS/FAIL */ if(status == 0){ - jehanne_print("PASS\n"); return "PASS"; } else { - jehanne_print("FAIL: " __POSIX_EXIT_PREFIX "%d\n", status); return "FAIL"; } } return nil; } +void +qa_exit_printer(int status, void *_) +{ + extern int printf(const char *format, ...); + if(jehanne_getpid() == jehanne_getmainpid()){ + /* the QA test may fork, but only the main process + * should return PASS/FAIL + */ + if(status == 0){ + printf("PASS\n"); + } else { + printf("FAIL: " __POSIX_EXIT_PREFIX "%d\n", status); + } + } +} + void __application_newlib_init(void) { + extern int on_exit(void (*func)(int, void*), void* arg); + on_exit(qa_exit_printer, nil); libposix_translate_exit_status(qa_exit_translator); } diff --git a/qa/lib/newlib/libposix_sigchld.c b/qa/lib/newlib/libposix_sigchld.c index 2380bcd..803bd91 100644 --- a/qa/lib/newlib/libposix_sigchld.c +++ b/qa/lib/newlib/libposix_sigchld.c @@ -27,19 +27,35 @@ qa_exit_translator(int status) * should return PASS/FAIL */ if(status == 0){ - jehanne_print("PASS\n"); return "PASS"; } else { - jehanne_print("FAIL: " __POSIX_EXIT_PREFIX "%d\n", status); return "FAIL"; } } return nil; } +void +qa_exit_printer(int status, void *_) +{ + extern int printf(const char *format, ...); + if(jehanne_getpid() == jehanne_getmainpid()){ + /* the QA test may fork, but only the main process + * should return PASS/FAIL + */ + if(status == 0){ + printf("PASS\n"); + } else { + printf("FAIL: " __POSIX_EXIT_PREFIX "%d\n", status); + } + } +} + void __application_newlib_init(void) { + extern int on_exit(void (*func)(int, void*), void* arg); + on_exit(qa_exit_printer, nil); libposix_translate_exit_status(qa_exit_translator); libposix_emulate_SIGCHLD(); } diff --git a/sys/include/posix.h b/sys/include/posix.h index 19afe8d..68281de 100644 --- a/sys/include/posix.h +++ b/sys/include/posix.h @@ -30,12 +30,18 @@ * #include * #include * - * Defining _LIBPOSIX_H before the include allow you to just get dirent - * definition. + * Defining _LIBPOSIX_H before the include allow you to just get + * data structure definition. */ -#ifndef _LIBPOSIX_DIRENT -#define _LIBPOSIX_DIRENT +#ifndef _LIBPOSIX_DEF +#define _LIBPOSIX_DEF + +struct timespec +{ + long tv_sec; + long tv_nsec; +}; /* dirent alias of stat(5) message. * We (ab)use the fact that both 9P and Jehanne are little endian. @@ -75,13 +81,7 @@ struct __attribute__((__packed__)) dirent #define DT_SOCK 12 #define DT_WHT 14 -#endif /* _LIBPOSIX_DIRENT */ - -#ifndef _LIBPOSIX_H -#define _LIBPOSIX_H - -typedef unsigned long clock_t; - +/* getrusage who */ typedef enum PosixRUsages { PosixRUsageSelf = 0, @@ -90,10 +90,170 @@ typedef enum PosixRUsages PosixRUsageUnknown = -1 } PosixRUsages; +/* errno values */ +#define _ERRNO_H // skip the Posix part, we just need the enum +#include + +/* signals */ +typedef unsigned long PosixSignalMask; + + +typedef enum PosixSigProcMaskAction +{ + PosixSPMSetMask = 0, + PosixSPMBlock = 1, + PosixSPMUnblock = 2 +} PosixSigProcMaskAction; + +#define PosixNumberOfSignals (sizeof(PosixSignalMask)*7) +typedef enum PosixSignals +{ + PosixSIGABRT = 1, + PosixSIGALRM, + PosixSIGBUS, + PosixSIGCHLD, + PosixSIGCONT, + PosixSIGFPE, + PosixSIGHUP, + PosixSIGILL, + PosixSIGINT, + PosixSIGKILL, + PosixSIGPIPE, + PosixSIGQUIT, + PosixSIGSEGV, + PosixSIGSTOP, + PosixSIGTERM, + PosixSIGTSTP, + PosixSIGTTIN, + PosixSIGTTOU, + PosixSIGUSR1, + PosixSIGUSR2, + PosixSIGPOLL, + PosixSIGPROF, + PosixSIGSYS, + PosixSIGTRAP, + PosixSIGURG, + PosixSIGVTALRM, + PosixSIGXCPU, + PosixSIGXFSZ, + + /* Optional Signals */ + PosixSIGIOT, + PosixSIGEMT, + PosixSIGSTKFLT, + PosixSIGIO, + PosixSIGPWR, + PosixSIGINFO, + PosixSIGLOST, + PosixSIGWINCH, + PosixSIGUNUSED, + + PosixSIGRTMIN, + PosixSIGRTMAX = PosixNumberOfSignals +} PosixSignals; + +typedef enum PosixSigActionFlags +{ + /* supported flags */ + PosixSAFSigInfo = 1<<0, + PosixSAFRestart = 1<<1, + PosixSAFNoChildrenStop = 1<<2, + PosixSAFResetHandler = 1<<3, + PosixSAFNoChildrenWait = 1<<4, + + /* ignored flags */ + PosixSAFNoDefer = 1<<16, /* notes are not reentrant */ + PosixSAFOnStack = 1<<17, + PosixSAFDisable = 1<<18 +} PosixSigActionFlags; + +typedef enum PosixSigInfoCodes +{ + PosixSIUser = 1, + PosixSIQueue, + PosixSITimer, + PosixSIAsyncIO, + PosixSIMsgQueued, + + PosixSIFaultMapError, + PosixSIFaultAccessError, + + PosixSIChildExited, + PosixSIChildKilled, + PosixSIChildDumped, + PosixSIChildTrapped, + PosixSIChildStopped, + PosixSIChildContinued +} PosixSigInfoCodes; + +union sigval { + int sival_int; /* Integer signal value */ + void* sival_ptr; /* Pointer signal value */ + void* _si_addr; /* Address of faulting address */ + int _si_status; /* Child exit status */ + uintptr_t _sival_raw; /* Raw value */ +}; + +struct sigevent { + int sigev_notify; /* Notification type */ + int sigev_signo; /* Signal number */ + union sigval sigev_value; /* Signal value */ + void (*sigev_notify_function)( union sigval ); + /* Notification function */ + long *sigev_notify_attributes; /* Notification Attributes */ +}; + +typedef struct { + int si_signo; /* Signal number */ + int si_code; /* Cause of the signal */ + int si_errno; + int si_pid; /* Pid of sender */ + int si_uid; /* Uid of sender */ + union sigval si_value; /* Signal value */ +} PosixSignalInfo; + +#define si_addr si_value._si_addr +#define si_status si_value._si_status + +typedef void (*PosixSigHandler)(int); +typedef void (*PosixSigAction)(int, PosixSignalInfo *, void * ); + +struct sigaction { + int sa_flags; /* Special flags to affect behavior of signal */ + PosixSignalMask sa_mask; /* Additional set of signals to be blocked */ + /* during execution of signal-catching */ + /* function. */ + union { + PosixSigHandler _handler; /* SIG_DFL, SIG_IGN, or pointer to a function */ + PosixSigAction _sigaction; + } _signal_handlers; +}; + +#define sa_handler _signal_handlers._handler +#define sa_sigaction _signal_handlers._sigaction + +typedef enum PosixFDCmds +{ + PosixFDCDupFD = 1, + PosixFDCDupFDCloseOnExec, + PosixFDCGetFD, + PosixFDCSetFD, + PosixFDCGetFL, + PosixFDCSetFL +} PosixFDCmds; + +#endif /* _LIBPOSIX_DEF */ + +#ifndef _LIBPOSIX_H +#define _LIBPOSIX_H + +typedef unsigned long clock_t; + #define __POSIX_EXIT_PREFIX "posix error " #define __POSIX_EXIT_SIGNAL_PREFIX "terminated by posix signal " #define __POSIX_SIGNAL_PREFIX "posix: " +extern unsigned int POSIX_alarm(int *errnop, unsigned int seconds); extern int POSIX_access(int *errnop, const char *path, int amode); extern int POSIX_dup(int *errnop, int fildes); extern int POSIX_dup2(int *errnop, int fildes, int fildes2); @@ -134,6 +294,8 @@ extern int POSIX_waitpid(int *errnop, int pid, int *status, int options); extern long POSIX_write(int *errnop, int fd, const void *buf, size_t len); extern int POSIX_gettimeofday(int *errnop, void *timeval, void *timezone); extern char* POSIX_getenv(int *errnop, const char *name); +extern int POSIX_setenv(int *errno, const char *name, const char *value, int overwrite); +extern int POSIX_unsetenv(int *errnop, const char *name); extern void *POSIX_sbrk(int *errnop, ptrdiff_t incr); extern void * POSIX_malloc(int *errnop, size_t size); extern void *POSIX_realloc(int *errnop, void *ptr, size_t size); @@ -142,6 +304,19 @@ extern void POSIX_free(void *ptr); extern unsigned int POSIX_sleep(unsigned int seconds); extern int POSIX_pipe(int *errnop, int fildes[2]); extern int POSIX_umask(int *errnop, int mask); +extern int POSIX_fcntl(int *errnop, int fd, PosixFDCmds cmd, uintptr_t arg); + +extern int POSIX_sigaddset(int *errnop, PosixSignalMask *set, int signo); +extern int POSIX_sigdelset(int *errnop, PosixSignalMask *set, int signo); +extern int POSIX_sigismember(int *errnop, const PosixSignalMask *set, int signo); +extern int POSIX_sigfillset(int *errnop, PosixSignalMask *set); +extern int POSIX_sigemptyset(int *errnop, PosixSignalMask *set); +extern int POSIX_sigprocmask(int *errnop, PosixSigProcMaskAction how, const PosixSignalMask *set, PosixSignalMask *oset); +extern int POSIX_sigpending(int *errnop, PosixSignalMask *set); +extern int POSIX_sigsuspend(int *errnop, const PosixSignalMask *mask); +extern int POSIX_sigaction(int *errnop, int signo, const struct sigaction *act, struct sigaction *old); +extern int POSIX_sigtimedwait(int *errnop, const PosixSignalMask *set, PosixSignalInfo *info, const struct timespec *timeout); +extern int POSIX_sigqueue(int *errnop, int pid, int signo, const union sigval value); extern int POSIX_getuid(int *errnop); extern int POSIX_geteuid(int *errnop); @@ -159,72 +334,26 @@ extern int POSIX_setpgid(int *errnop, int pid, int pgid); extern int POSIX_getsid(int *errnop, int pid); extern int POSIX_setsid(int *errnop); +extern int POSIX_tcgetpgrp(int *errnop, int fd); +extern int POSIX_tcsetpgrp(int *errnop, int fd, int pgrp); + extern int libposix_getdents(int *errnop, int fd, char *buf, int buf_bytes); +extern PosixError libposix_translate_kernel_errors(const char *msg); /* Library initialization */ -#define _ERRNO_H // skip the Posix part, we just need the enum -#include - -typedef enum PosixSignals -{ - PosixSIGABRT = 1, - PosixSIGALRM, - PosixSIGBUS, - PosixSIGCHLD, - PosixSIGCONT, - PosixSIGFPE, - PosixSIGHUP, - PosixSIGILL, - PosixSIGINT, - PosixSIGKILL, - PosixSIGPIPE, - PosixSIGQUIT, - PosixSIGSEGV, - PosixSIGSTOP, - PosixSIGTERM, - PosixSIGTSTP, - PosixSIGTTIN, - PosixSIGTTOU, - PosixSIGUSR1, - PosixSIGUSR2, - PosixSIGPOLL, - PosixSIGPROF, - PosixSIGSYS, - PosixSIGTRAP, - PosixSIGURG, - PosixSIGVTALRM, - PosixSIGXCPU, - PosixSIGXFSZ, - - /* Optional Signals */ - PosixSIGIOT, - PosixSIGEMT, - PosixSIGSTKFLT, - PosixSIGIO, - PosixSIGCLD, - PosixSIGPWR, - PosixSIGINFO, - PosixSIGLOST, - PosixSIGWINCH, - PosixSIGUNUSED, - - PosixNumberOfSignals -} PosixSignals; /* Initialize libposix. Should call * * libposix_define_errno to set the value of each PosixError - * libposix_define_signal to set the value of each PosixSignal * libposix_define_at_fdcwd to set the value of AT_FDCWD (for fchmodat) * libposix_translate_error to translate error strings to PosixError - * libposix_set_signal_trampoline to dispatch signal received as notes * libposix_set_stat_reader * libposix_set_tms_reader * libposix_set_timeval_reader * libposix_set_timezone_reader */ -typedef void (*PosixInit)(void); +typedef void (*PosixInit)(int argc, char *argv[]); extern void libposix_init(int argc, char *argv[], PosixInit init) __attribute__((noreturn)); /* Translate an error string to a PosixError, in the context of the @@ -327,23 +456,11 @@ typedef char* (*PosixExitStatusTranslator)(int status); extern int libposix_translate_exit_status(PosixExitStatusTranslator translator); -/* Dispatch the signal to the registered handlers. +/* Execute process disposition (executed when main returns) */ -typedef enum PosixSignalAction -{ - SignalCatched = 1, - SignalIgnored, - SignalError, - SignalDefault -} PosixSignalAction; -typedef PosixSignalAction (*PosixSignalTrampoline)(int signal); - -extern int libposix_set_signal_trampoline(PosixSignalTrampoline trampoline); - -extern int libposix_define_signal(PosixSignals signal, int code); - -extern int libposix_define_realtime_signals(int sigrtmin, int sigrtmax); +typedef void (*PosixProcessDisposer)(int status); +extern int libposix_on_process_disposition(PosixProcessDisposer dispose); /* Enable SIGCHLD emulation */ diff --git a/sys/src/cmd/sys/build.json b/sys/src/cmd/sys/build.json index 029d3f9..2dfb038 100644 --- a/sys/src/cmd/sys/build.json +++ b/sys/src/cmd/sys/build.json @@ -1,9 +1,13 @@ { - "disk": { + "SystemTools": { "Include": [ "../cmd.json" ], "Install": "/arch/$ARCH/cmd/sys/", + "Oflags": [ + "-static", + "-lc" + ], "Projects": [ "call/" ], diff --git a/sys/src/lib/libs.json b/sys/src/lib/libs.json index 503903a..12fe5de 100644 --- a/sys/src/lib/libs.json +++ b/sys/src/lib/libs.json @@ -22,11 +22,11 @@ "/sys/src/lib/mp/", "/sys/src/lib/ndb/", "/sys/src/lib/plumb/", - "/sys/src/lib/posix/", "/sys/src/lib/regexp/", "/sys/src/lib/sec/", "/sys/src/lib/stdio/", - "/sys/src/lib/thread/" + "/sys/src/lib/thread/", + "/sys/src/lib/posix/" ] } } diff --git a/sys/src/lib/posix/build.json b/sys/src/lib/posix/build.json index e331247..e960806 100644 --- a/sys/src/lib/posix/build.json +++ b/sys/src/lib/posix/build.json @@ -1,6 +1,7 @@ { "LibPosix": { "Cflags": [ + "-DARCH=\"$ARCH\"", "-fasm", "-I." ], @@ -12,16 +13,37 @@ "SourceFiles": [ "environment.c", "errors.c", + "fcntl.c", "files.c", "ids.c", "initlib.c", + "kill.c", "links.c", "memory.c", "others.c", "processes.c", "sigchlds.c", "signals.c", + "sigqueue.c", + "sigsets.c", + "sigsuspend.c", + "termios.c", "timers.c" ] + }, + "SignalHelper": { + "Include": [ + "../../cmd/cmd.json" + ], + "Install": "/arch/$ARCH/cmd/sys/", + "Oflags": [ + "-static", + "-lposix", + "-l9p2000", + "-lc" + ], + "SourceFilesCmd": [ + "posixly.c" + ] } } diff --git a/sys/src/lib/posix/environment.c b/sys/src/lib/posix/environment.c index 3d701e9..cb0d82e 100644 --- a/sys/src/lib/posix/environment.c +++ b/sys/src/lib/posix/environment.c @@ -89,12 +89,96 @@ put_in_list(EnvVar* newenv) wunlock(&list_lock); } -char * -POSIX_getenv(int *errno, const char *name) +static int +write_env_file(const char *name, const char *value, int size) +{ + int f; + char buf[MAX_ENVNAME_LEN]; + + snprint(buf, sizeof(buf), "/env/%s", name); + f = ocreate(buf, OWRITE, 664); + if(f < 0) + return 0; + if(write(f, value, size) < 0){ + close(f); + return 0; + } + close(f); + return 1; +} + +int +POSIX_setenv(int *errnop, const char *name, const char *value, int overwrite) { EnvVar* e; - if(name == nil || name[0] == 0 || strchr(name, '=') == nil){ - *errno = PosixEINVAL; + if(name == nil || name[0] == 0 || strchr(name, '=') != nil){ + *errnop = PosixEINVAL; + return -1; + } + if(!overwrite && POSIX_getenv(errnop, name) != nil) + return 0; + if(strlen(name) > 127){ + *errnop = PosixENOMEM; + return -1; + } + e = malloc(sizeof(EnvVar)); + e->next = nil; + e->value = strdup(value); // see free_env + e->name = strdup(name); + if(!write_env_file(name, e->value, strlen(value) + 1)){ + free_env(e); + *errnop = PosixENOMEM; + return -1; + } + put_in_list(e); + return 0; +} + +int +POSIX_unsetenv(int *errnop, const char *name) +{ + EnvVar* e, **o; + char buf[MAX_ENVNAME_LEN]; + if(name == nil || name[0] == 0 || strchr(name, '=') != nil){ + *errnop = PosixEINVAL; + return -1; + } + if(POSIX_getenv(errnop, name) == nil) + return 0; + e = malloc(sizeof(EnvVar)); + + wlock(&list_lock); + + e = list_start; + o = &list_start; + while(e != nil){ + if(strcmp(e->name, name) == 0){ + *o = e->next; + free(e); + e = nil; // done + } else { + o = &e->next; + e = *o; + } + } + + wunlock(&list_lock); + + snprint(buf, sizeof(buf), "/env/%s", name); + remove(buf); + return 0; +} + +char * +POSIX_getenv(int *errnop, const char *name) +{ + EnvVar* e; + if(name == nil || name[0] == 0 || strchr(name, '=') != nil){ + *errnop = PosixEINVAL; + return nil; + } + if(strlen(name) > 127){ + *errnop = PosixEINVAL; return nil; } @@ -110,12 +194,16 @@ POSIX_getenv(int *errno, const char *name) return e->value; e = malloc(sizeof(EnvVar)); + if(e == nil){ + *errnop = PosixEINVAL; + return nil; + } e->next = nil; e->value = nil; // see free_env e->name = strdup(name); if(e->name == nil){ free_env(e); - *errno = PosixENOMEM; + *errnop = PosixEINVAL; return nil; } e->value = getenv(name); diff --git a/sys/src/lib/posix/errors.c b/sys/src/lib/posix/errors.c index b4df4c6..0b4f2d5 100644 --- a/sys/src/lib/posix/errors.c +++ b/sys/src/lib/posix/errors.c @@ -132,6 +132,39 @@ get_posix_error(PosixErrorMap *translations, char *err, uintptr_t caller) return PosixEINVAL; } +PosixError +libposix_translate_kernel_errors(const char *msg) +{ + // TODO: autogenerate from /sys/src/sysconf.json + if(nil == msg) + return 0; + if(strncmp("interrupted", msg, 9) == 0) + return PosixEINTR; + if(strncmp("no living children", msg, 18) == 0) + return PosixECHILD; + if(strstr(msg, "file not found") != nil) + return PosixENOENT; + if(strstr(msg, "does not exist") != nil) + return PosixENOENT; + if(strstr(msg, "file already exists") != nil) + return PosixEEXIST; + if(strstr(msg, "file is a directory") != nil) + return PosixEISDIR; + if(strncmp("fd out of range or not open", msg, 27) == 0) + return PosixEBADF; + if(strstr(msg, "not a directory") != nil) + return PosixENOTDIR; + if(strstr(msg, "permission denied") != nil) + return PosixEPERM; + if(strstr(msg, "name too long") != nil) + return PosixENAMETOOLONG; + if(strcmp("i/o error", msg) == 0) + return PosixEIO; + if(strcmp("i/o on hungup channel", msg) == 0) + return PosixEIO; + return 0; +} + int __libposix_translate_errstr(uintptr_t caller) { @@ -153,6 +186,8 @@ __libposix_translate_errstr(uintptr_t caller) perr = get_posix_error(handler->head, err, caller); if(perr == 0) perr = get_posix_error(generic_handlers, err, caller); + if(perr == 0) + perr = libposix_translate_kernel_errors(err); ret = __libposix_get_errno(perr); sys_errstr(err, ERRMAX); return ret; diff --git a/sys/src/lib/posix/fcntl.c b/sys/src/lib/posix/fcntl.c new file mode 100644 index 0000000..5ed4229 --- /dev/null +++ b/sys/src/lib/posix/fcntl.c @@ -0,0 +1,159 @@ +/* + * This file is part of Jehanne. + * + * Copyright (C) 2017 Giacomo Tesio + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, version 3 of the License. + * + * Jehanne is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Jehanne. If not, see . + */ + +#include +#include +#include +#include "internal.h" + +static int +fcntl_dup(int *errnop, int fd, int minfd) +{ + int newfd, i; + char buf[128]; + int opened[32]; + PosixError e; + + if(fd < 0 || minfd < 0){ + *errnop = __libposix_get_errno(PosixEBADF); + return -1; + } + + snprint(buf, sizeof(buf), "/fd/%d", minfd); + if(access(buf, AEXIST) != 0){ + if(dup(fd, minfd) < 0){ + *errnop = __libposix_get_errno(PosixEBADF); + return -1; + } + return minfd; + } + + e = 0; + i = 0; + do + { + newfd = dup(fd, -1); + if(newfd < 0){ + e = PosixEINVAL; + break; + } + opened[i++] = newfd; + } while(newfd < minfd && i < nelem(opened)); + + --i; + if(newfd >= minfd) + --i; + while(i >= 0) + close(opened[i--]); + if(newfd >= minfd) + return newfd; + + close(newfd); + if(e == 0) + e = PosixEMFILE; + *errnop = __libposix_get_errno(e); + return -1; +} + +static int +file_flags(int *errnop, int fd) +{ + int newfd, r, flags; + char buf[128], *cols[3]; + + snprint(buf, sizeof(buf), "/fd/%dctl", fd); + newfd = open(buf, OREAD); + if(newfd < 0) + goto FailWithEBADF; + r = read(newfd, buf, sizeof(buf)); + close(newfd); + if(r < 0) + goto FailWithEBADF; + + r = getfields(buf, cols, 3, 1, " "); + if(r < 3) + goto FailWithEBADF; + + flags = 0; + + if(strchr(cols[1], 'E')) + flags |= OCEXEC; + else if(__libposix_should_close_on_exec(fd)) + flags |= OCEXEC; + + if(strchr(cols[1], 'r')) + flags |= OREAD; + else if(strchr(cols[1], 'R')) + flags |= OREAD; + + if(strchr(cols[1], 'w')) + flags |= OWRITE; + else if(strchr(cols[1], 'W')) + flags |= OWRITE; + + return flags; + +FailWithEBADF: + *errnop = __libposix_get_errno(PosixEBADF); + return -1; +} + +int +POSIX_fcntl(int *errnop, int fd, PosixFDCmds cmd, uintptr_t arg) +{ + int tmp; + int flags; + switch(cmd){ + case PosixFDCDupFD: + return fcntl_dup(errnop, fd, (int)arg); + case PosixFDCDupFDCloseOnExec: + if((tmp = fcntl_dup(errnop, fd, (int)arg)) < 0) + return -1; + flags = file_flags(errnop, tmp); + if(flags < 0) + return -1; + if((flags&OCEXEC) == 0) + __libposix_set_close_on_exec(tmp, 1); + return tmp; + case PosixFDCGetFD: + flags = file_flags(errnop, fd); + if(flags < 0) + return -1; + return flags & OCEXEC; + case PosixFDCSetFD: + if(arg){ + if((file_flags(errnop, fd)&OCEXEC) == 0) + __libposix_set_close_on_exec(fd, 1); + } else { + if((file_flags(errnop, fd)&OCEXEC) != 0) + __libposix_set_close_on_exec(fd, 0); + } + return 0; + case PosixFDCGetFL: + flags = file_flags(errnop, fd); + if(flags < 0) + return -1; + return flags & (~OCEXEC); + case PosixFDCSetFL: + return 0; + break; + } + + *errnop = __libposix_get_errno(PosixEINVAL); + return -1; +} diff --git a/sys/src/lib/posix/files.c b/sys/src/lib/posix/files.c index 751ec94..d233af7 100644 --- a/sys/src/lib/posix/files.c +++ b/sys/src/lib/posix/files.c @@ -29,6 +29,10 @@ static int __libposix_R_OK; static int __libposix_W_OK; static int __libposix_X_OK; +static int *__libposix_coe_fds; +static int __libposix_coe_fds_size; +static int __libposix_coe_fds_used; + typedef enum SeekTypes { SeekSet = 0, @@ -48,6 +52,71 @@ __libposix_files_check_conf(void) sysfatal("libposix: no seek translations"); } +void +__libposix_set_close_on_exec(int fd, int close) +{ + int i; + if(__libposix_coe_fds_size == __libposix_coe_fds_used){ + __libposix_coe_fds_size += 8; + __libposix_coe_fds = realloc(__libposix_coe_fds, __libposix_coe_fds_size * sizeof(int)); + i = __libposix_coe_fds_size; + while(i > __libposix_coe_fds_used) + __libposix_coe_fds[--i] = -1; + } + /* remove fd if already present */ + i = 0; + while(i < __libposix_coe_fds_size){ + if(__libposix_coe_fds[i] == fd){ + __libposix_coe_fds[i] = -1; + --__libposix_coe_fds_used; + break; + } + ++i; + } + if(close){ + /* add fd to close on exec */ + i = 0; + while(i < __libposix_coe_fds_size){ + if(__libposix_coe_fds[i] == -1){ + __libposix_coe_fds[i] = fd; + break; + } + ++i; + } + ++__libposix_coe_fds_used; + } +} + +void +__libposix_close_on_exec(void) +{ + int i = 0; + while(i < __libposix_coe_fds_size){ + if(__libposix_coe_fds[i] != -1){ + close(__libposix_coe_fds[i]); + __libposix_coe_fds[i] = -1; + --__libposix_coe_fds_used; + } + ++i; + } + assert(__libposix_coe_fds_used == 0); + free(__libposix_coe_fds); + __libposix_coe_fds = nil; + __libposix_coe_fds_size = 0; +} + +int +__libposix_should_close_on_exec(int fd) +{ + int i = 0; + while(i < __libposix_coe_fds_size){ + if(__libposix_coe_fds[i] == fd) + return 1; + ++i; + } + return 0; +} + int libposix_translate_open(PosixOpenTranslator translation) { @@ -746,6 +815,8 @@ libposix_getdents(int *errnop, int fd, char *buf, int buf_bytes) goto FailWithENOTDIR; /* not a directory */ r = read(fd, buf, buf_bytes); + if(r == 0) + return 0; if(r < 0) goto FailWithENOENT; /* removed? */ if(r < MINSTATLEN) diff --git a/sys/src/lib/posix/ids.c b/sys/src/lib/posix/ids.c index a790d73..4acf1fd 100644 --- a/sys/src/lib/posix/ids.c +++ b/sys/src/lib/posix/ids.c @@ -21,70 +21,83 @@ #include #include "internal.h" +typedef union { + int group; + char raw[sizeof(int)]; +} IntBuf; + +extern int *__libposix_devsignal; int __libposix_session_leader = -1; static int -get_noteid(int *errnop, int pid) +get_ppid(int pid) { - int n, f; - char buf[30]; - sprint(buf, "/proc/%d/noteid", pid); - f = open(buf, OREAD); - if(f < 0){ - *errnop = __libposix_get_errno(PosixEPERM); + long n; + char buf[32]; + sprint(buf, "/proc/%d/ppid", pid); + n = remove(buf); + if(n == -1) return -1; - } - n = read(f, buf, sizeof(buf) - 1); - if(n < 0){ - *errnop = __libposix_get_errno(PosixEPERM); - return -1; - } - buf[n] = '\0'; - n = atoi(buf); - close(f); + return (int)n; +} - return n; +long +__libposix_sighelper_set_pgid(int target, int group) +{ + union { + PosixHelperRequest request; + long raw; + } offset; + IntBuf buf; + + offset.request.command = PHSetProcessGroup; + offset.request.target = target; + + buf.group = group; + return pwrite(*__libposix_devsignal, buf.raw, sizeof(buf.raw), offset.raw); } static int -set_noteid(int *errnop, int pid, int noteid) +set_group_id(int *errnop, int pid, int group) { - int n, f; - char buf[30]; + int mypid, ppid; - if(pid == 0) - pid = getpid(); - if(noteid == 0){ - noteid = get_noteid(errnop, pid); - if(noteid < 0) - return noteid; - } - sprint(buf, "/proc/%d/noteid", pid); - f = open(buf, OWRITE); - if(f < 0) { - *errnop = __libposix_get_errno(PosixESRCH); + if(pid < 0 || group < 0){ + *errnop = __libposix_get_errno(PosixEINVAL); return -1; } - n = sprint(buf, "%d", noteid); - n = write(f, buf, n); - if(n < 0){ + if(pid == __libposix_session_leader){ *errnop = __libposix_get_errno(PosixEPERM); return -1; } - close(f); - return 0; + mypid = *__libposix_pid; + if(pid == 0 && group == 0){ + /* the caller wants a new process group */ +CreateNewProcessGroup: + rfork(RFNOTEG); + return __libposix_sighelper_cmd(PHSetProcessGroup, mypid); + } + ppid = get_ppid(pid); + if(ppid == -1 || pid != mypid && mypid != ppid){ + *errnop = __libposix_get_errno(PosixESRCH); + return -1; + } + if(pid == group && pid == mypid) + goto CreateNewProcessGroup; + + return __libposix_sighelper_set_pgid(pid, group); } int POSIX_getuid(int *errnop) { - return 0; + return 1000; } int POSIX_geteuid(int *errnop) { - return 0; + return 1000; } int @@ -102,7 +115,7 @@ POSIX_seteuid(int *errnop, int euid) int POSIX_setreuid(int *errnop, int ruid, int euid) { - return 0; + return 1000; } int @@ -138,61 +151,129 @@ POSIX_setregid(int *errnop, int rgid, int egid) int POSIX_getpgrp(int *errnop) { - int pid = getpid(); - return get_noteid(errnop, pid); + long ret; + int pid = *__libposix_pid; + ret = __libposix_sighelper_cmd(PHGetProcessGroupId, pid); + if(ret < 0) + return pid; + return ret; } int POSIX_getpgid(int *errnop, int pid) { - return get_noteid(errnop, pid); + long ret; + ret = __libposix_sighelper_cmd(PHGetProcessGroupId, pid); + if(ret < 0) + *errnop = __libposix_get_errno(PosixESRCH); + return ret; } int POSIX_setpgid(int *errnop, int pid, int pgid) { - if(pid < 0 || pgid < 0){ - *errnop = __libposix_get_errno(PosixEINVAL); - return -1; - } - return set_noteid(errnop, pid, pgid); + return set_group_id(errnop, pid, pgid); } int POSIX_getsid(int *errnop, int pid) { - int reqnoteid, mynoteid; + int mypid; + long sid; + char buf[32]; if(pid < 0){ +FailWithESRCH: *errnop = __libposix_get_errno(PosixESRCH); return -1; } + snprint(buf, sizeof(buf), "/proc/%d/ns", pid); + if(access(buf, AEXIST) != 0) + goto FailWithESRCH; + + mypid = *__libposix_pid; if(pid == 0) - pid = getpid(); - else if(pid == getpid()) - return __libposix_session_leader; - reqnoteid = get_noteid(errnop, pid); - if(reqnoteid < 0) - return reqnoteid; - if(__libposix_session_leader < 0) - return reqnoteid; - mynoteid = POSIX_getpgrp(errnop); - if(mynoteid == reqnoteid){ - /* if it share our pgrp (aka noteid), it shares - * our session leader - */ - return __libposix_session_leader; + pid = mypid; + sid = __libposix_sighelper_cmd(PHGetSessionId, pid); + if(sid < 0){ + *errnop = __libposix_get_errno(PosixEPERM); + return -1; } - return reqnoteid; + if(pid == mypid) + __libposix_session_leader = (int)sid; + + return sid; } int POSIX_setsid(int *errnop) { - if(rfork(RFNAMEG|RFNOTEG) < 0){ - *errnop = __libposix_get_errno(PosixEPERM); - return -1; + extern PosixSignalMask *__libposix_signal_mask; + extern SignalConf *__libposix_signals; + extern int *__libposix_devsignal; + + char *posixly_args[4], *fname; + int mypid = *__libposix_pid; + int oldsid; + long controlpid; + SignalConf *c; + + /* detach the previous session */ + oldsid = (int)__libposix_sighelper_cmd(PHGetSessionId, 0); + fname = smprint("#s/posixly.%s.%d", getuser(), oldsid); + if(fname == nil || __libposix_sighelper_cmd(PHDetachSession, 0) < 0) + goto FailWithEPERM; + if(*__libposix_devsignal >= 0) + close(*__libposix_devsignal); + *__libposix_devsignal = -1; + rfork(RFNAMEG|RFNOTEG|RFENVG|RFFDG); + unmount(fname, "/dev"); + free(fname); + fname = nil; + assert(access("/dev/posix", AEXIST) != 0); + + /* start the new session */ + switch(controlpid = sys_rfork(RFPROC|RFNOTEG|RFENVG|RFFDG)){ + case -1: + goto FailWithEPERM; + case 0: + posixly_args[0] = "posixly"; + posixly_args[1] = "-p"; + posixly_args[2] = smprint("%d", mypid); + posixly_args[3] = nil; + jehanne_pexec("sys/posixly", posixly_args); + rfork(RFNOWAIT); + sysfatal("pexec sys/posixly"); + default: + break; } - __libposix_session_leader = POSIX_getpgrp(errnop); - return __libposix_session_leader; + + /* wait for /dev/posix/ */ + fname = smprint("/proc/%d/note", controlpid); + if(fname == nil) + goto FailWithEPERM; + while(access("/dev/posix", AEXIST) != 0 && access(fname, AWRITE) == 0) + sleep(250); + if(access(fname, AWRITE) != 0) + goto FailWithEPERM; + free(fname); + fname = nil; + + /* connect to the new session */ + __libposix_sighelper_open(); + __libposix_sighelper_set(PHBlockSignals, *__libposix_signal_mask); + c = __libposix_signals; + while(c < __libposix_signals + PosixNumberOfSignals){ + if(c->handler == (void*)1) + __libposix_sighelper_set(PHIgnoreSignal, SIGNAL_MASK(1+(c-__libposix_signals))); + ++c; + } + __libposix_session_leader = mypid; + return mypid; + +FailWithEPERM: + if(fname) + free(fname); + *errnop = __libposix_get_errno(PosixEPERM); + return -1; } diff --git a/sys/src/lib/posix/initlib.c b/sys/src/lib/posix/initlib.c index d87df55..1a0c3b9 100644 --- a/sys/src/lib/posix/initlib.c +++ b/sys/src/lib/posix/initlib.c @@ -24,11 +24,12 @@ extern int *__libposix_errors_codes; extern WaitList **__libposix_wait_list; extern ChildList **__libposix_child_list; static int __initialized; -static unsigned char signals_to_code[256]; -static unsigned char code_to_signal[256]; +static PosixProcessDisposer __libposix_process_dispose; - -int *__libposix_sigchld_target_pid; +int *__libposix_sigchld_father_pid; +int *__libposix_sigchld_child_pid; +int *__libposix_devsignal; +int *__libposix_pid; static void libposix_check_configuration(void) @@ -36,32 +37,48 @@ libposix_check_configuration(void) __libposix_errors_check_conf(); __libposix_files_check_conf(); __libposix_processes_check_conf(); - __libposix_signal_check_conf(); +} + +void +__libposix_sighelper_open(void) +{ + int mypid; + if(*__libposix_devsignal >= 0) + close(*__libposix_devsignal); + mypid = *__libposix_pid; + *__libposix_devsignal = create("/dev/posix/signals", ORDWR|OCEXEC, mypid); + if(*__libposix_devsignal < 0) + sysfatal("cannot create /dev/posix/signals: %r"); } void libposix_init(int argc, char *argv[], PosixInit init) { extern int main(int, char**); - extern unsigned char *__signals_to_code_map; - extern unsigned char *__code_to_signal_map; extern int *__handling_external_signal; extern int *__restart_syscall; - extern int *__libposix_sigchld_target_pid; + extern PosixSignalMask *__libposix_signal_mask; WaitList *wait_list; ChildList *child_list; + PosixSignalMask signal_mask; + int mypid; int status; int error_codes[ERRNO_LAST-ERRNO_FIRST]; int handling_signal; - int sigchld_target_pid; + int sigchld_father_pid; + int sigchld_child_pid; int restart_syscall; + int devsignal; assert(__initialized == 0); + mypid = getpid(); + __libposix_pid = &mypid; + /* initialize PosixErrors map */ memset(error_codes, 0, sizeof(error_codes)); - __libposix_errors_codes=error_codes; + __libposix_errors_codes = error_codes; /* initialize wait_list; see also POSIX_fork and POSIX_exit */ wait_list = nil; @@ -72,28 +89,63 @@ libposix_init(int argc, char *argv[], PosixInit init) __libposix_child_list = &child_list; /* initialize signal handling */ - restart_syscall = 0; handling_signal = 0; - sigchld_target_pid = 0; + sigchld_father_pid = 0; + sigchld_child_pid = 0; + signal_mask = 0; + devsignal = -1; + __libposix_devsignal = &devsignal; __restart_syscall = &restart_syscall; - __signals_to_code_map = signals_to_code; - __code_to_signal_map = code_to_signal; __handling_external_signal = &handling_signal; - __libposix_sigchld_target_pid = &sigchld_target_pid; + __libposix_sigchld_father_pid = &sigchld_father_pid; + __libposix_sigchld_child_pid = &sigchld_child_pid; + __libposix_signal_mask = &signal_mask; + __libposix_reset_pending_signals(); if(!atnotify(__libposix_note_handler, 1)) sysfatal("libposix: atnotify"); - init(); + __libposix_sighelper_open(); + signal_mask = (PosixSignalMask)__libposix_sighelper_cmd(PHGetProcMask, 0); + __libposix_init_signal_handlers(); + + init(argc, argv); libposix_check_configuration(); __initialized = 1; status = main(argc, argv); + + if(__libposix_process_dispose != nil) + __libposix_process_dispose(status); + POSIX_exit(status); } +int +libposix_on_process_disposition(PosixProcessDisposer dispose) +{ + if(__libposix_process_dispose != nil) + return 0; + __libposix_process_dispose = dispose; + return 1; +} + int __libposix_initialized(void) { return __initialized; } + +long +__libposix_sighelper_cmd(PosixHelperCommand command, int posix_process_pid) +{ + union { + PosixHelperRequest request; + long raw; + } offset; + + offset.request.command = command; + offset.request.target = posix_process_pid; + + return pwrite(*__libposix_devsignal, "", 0, offset.raw); +} diff --git a/sys/src/lib/posix/internal.h b/sys/src/lib/posix/internal.h index f2a7a96..8469855 100644 --- a/sys/src/lib/posix/internal.h +++ b/sys/src/lib/posix/internal.h @@ -33,10 +33,29 @@ struct ChildList ChildList *next; }; +typedef struct SignalConf SignalConf; +struct SignalConf +{ + void* handler; + PosixSignalMask mask : 56; + int sa_siginfo : 1; + int sa_resethand : 1; + int sa_restart : 1; + int sa_nochildwait : 1; +}; + +typedef struct PendingSignalList PendingSignalList; +struct PendingSignalList +{ + PosixSignalInfo signal; + PendingSignalList* next; +}; + +extern int *__libposix_pid; + extern void __libposix_files_check_conf(void); extern void __libposix_errors_check_conf(void); extern void __libposix_processes_check_conf(void); -extern void __libposix_signal_check_conf(void); extern int __libposix_initialized(void); extern int __libposix_get_errno(PosixError e); @@ -51,16 +70,75 @@ extern void __libposix_free_wait_list(void); extern void __libposix_setup_new_process(void); -extern int __libposix_note_to_signal(char *note); +extern int __libposix_signal_to_note(const PosixSignalInfo *si, char *buf, int size); + +extern int __libposix_note_to_signal(const char *note, PosixSignalInfo *siginfo); extern int __libposix_is_child(int pid); +extern void __libposix_free_child_list(void); + extern void __libposix_forget_child(int pid); extern int __libposix_send_control_msg(int pid, char *msg); -extern PosixError __libposix_notify_signal_to_process(int pid, int signal); +extern PosixError __libposix_notify_signal_to_process(int pid, PosixSignalInfo *siginfo); -extern PosixError __libposix_receive_signal(int sig); +extern PosixError __libposix_receive_signal(PosixSignalInfo *siginfo); + +extern PosixError __libposix_dispatch_signal(int pid, PosixSignalInfo* siginfo); extern int __libposix_restart_syscall(void); + +extern int __libposix_signal_blocked(PosixSignalInfo *siginfo); + +extern int __libposix_run_signal_handler(SignalConf *c, PosixSignalInfo *siginfo); + +extern void __libposix_reset_pending_signals(void); + +extern void __libposix_set_close_on_exec(int fd, int close); + +extern int __libposix_should_close_on_exec(int fd); + +extern void __libposix_close_on_exec(void); + +extern void __libposix_init_signal_handlers(void); + +#define SIGNAL_MASK(signal) (1ULL<<((signal)-1)) +#define SIGNAL_RAW_ADD(bitmask, signal) (bitmask |= SIGNAL_MASK(signal)) +#define SIGNAL_RAW_DEL(bitmask, signal) (bitmask &= ~SIGNAL_MASK(signal)) + +typedef enum { + PHProcessExited, /* WRITE, for nannies, the child might not be a libposix application */ + PHCallingExec, /* WRITE */ + + PHSignalProcess, /* WRITE */ + PHSignalGroup, /* WRITE */ + PHSignalForeground, /* WRITE, for the controller process */ + + PHIgnoreSignal, /* WRITE */ + PHEnableSignal, /* WRITE */ + PHBlockSignals, /* WRITE */ + PHWaitSignals, /* READ, may block */ + + PHGetSessionId, /* WRITE, ret contains the id */ + PHGetProcessGroupId, /* WRITE, ret contains the id */ + PHGetPendingSignals, /* WRITE, ret contains the mask */ + PHGetProcMask, /* WRITE, ret contains the mask */ + + PHSetForegroundGroup, /* WRITE */ + PHGetForegroundGroup, /* WRITE */ + PHDetachSession, /* WRITE */ + PHSetProcessGroup, /* WRITE */ +} PosixHelperCommand; +typedef struct { + int target; + PosixHelperCommand command; +} PosixHelperRequest; + +extern void __libposix_sighelper_open(void); +extern long __libposix_sighelper_cmd(PosixHelperCommand command, int target); +extern long __libposix_sighelper_set(PosixHelperCommand command, PosixSignalMask signal_set); +extern long __libposix_sighelper_signal(PosixHelperCommand command, int target, PosixSignalInfo *siginfo); +extern long __libposix_sighelper_wait(PosixSignalMask set, PosixSignalInfo *siginfo); +extern long __libposix_sighelper_set_pgid(int target, int group_id); diff --git a/sys/src/lib/posix/kill.c b/sys/src/lib/posix/kill.c new file mode 100644 index 0000000..1eca2d7 --- /dev/null +++ b/sys/src/lib/posix/kill.c @@ -0,0 +1,50 @@ +/* + * This file is part of Jehanne. + * + * Copyright (C) 2017 Giacomo Tesio + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, version 3 of the License. + * + * Jehanne is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Jehanne. If not, see . + */ + +#include +#include +#include +#include "internal.h" + + +int +POSIX_kill(int *errnop, int pid, int signo) +{ + PosixError perror; + PosixSignalInfo siginfo; + int errno; + + if(signo < 1 || signo > PosixNumberOfSignals){ + *errnop = __libposix_get_errno(PosixEINVAL); + return -1; + } + memset(&siginfo, 0, sizeof(PosixSignalInfo)); + siginfo.si_signo = signo; + siginfo.si_pid = *__libposix_pid; + siginfo.si_code = PosixSIUser; + siginfo.si_uid = POSIX_getuid(&errno); + if(pid == siginfo.si_pid) + perror = __libposix_receive_signal(&siginfo); + else + perror = __libposix_dispatch_signal(pid, &siginfo); + if(perror != 0){ + *errnop = __libposix_get_errno(perror); + return -1; + } + return 0; +} diff --git a/sys/src/lib/posix/others.c b/sys/src/lib/posix/others.c index 1088c7c..20ef99f 100644 --- a/sys/src/lib/posix/others.c +++ b/sys/src/lib/posix/others.c @@ -35,7 +35,8 @@ POSIX_isatty(int *errnop, int fd) } l = strlen(buf); - if(l >= 9 && strcmp(buf+l-9, "/dev/cons") == 0) + if((l >= 9 && strcmp(buf+l-9, "/dev/cons") == 0) + ||(l >= 8 && strcmp(buf+l-8, "/dev/tty") == 0)) return 1; *errnop = __libposix_get_errno(PosixENOTTY); return 0; @@ -138,12 +139,14 @@ POSIX_getpass(int *errnop, const char *prompt) char *p; static char buf[256]; - if(fd2path(0, buf, sizeof(buf)) == 0 && strcmp("/dev/cons", buf) == 0) + if(fd2path(0, buf, sizeof(buf)) == 0 + && (strcmp("/dev/cons", buf) == 0 || strcmp("/dev/tty", buf) == 0)) r = 0; else if((r = open("/dev/cons", OREAD)) < 0) goto ReturnENXIO; - if(fd2path(1, buf, sizeof(buf)) == 0 && strcmp("/dev/cons", buf) == 0) + if(fd2path(1, buf, sizeof(buf)) == 0 + && (strcmp("/dev/cons", buf) == 0 || strcmp("/dev/tty", buf) == 0)) w = 1; else if((w = open("/dev/cons", OWRITE)) < 0) goto CloseRAndReturnENXIO; diff --git a/sys/src/lib/posix/posixly.c b/sys/src/lib/posix/posixly.c new file mode 100644 index 0000000..64932a1 --- /dev/null +++ b/sys/src/lib/posix/posixly.c @@ -0,0 +1,1941 @@ +/* + * This file is part of Jehanne. + * + * Copyright (C) 2017 Giacomo Tesio + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, version 3 of the License. + * + * Jehanne is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Jehanne. If not, see . + */ + +/* This is an helper for programs using libposix. + * + * It simulates the kernel management of signal related stuffs, like + * - signal delivery: it translates signals to notes and send them + * - signal masks: it discards ignored signals and defer blocked ones + * - process groups + * + * It represent a single login session. + * It starts a new program in a new namespace providing /dev/posix/, + * to serve applications based on libposix. + * It translate notes received into signals for the foreground process + * group. + */ + +#include +#include +#include +#include <9P2000.h> +#include +#include "internal.h" + + +extern int *__libposix_devsignal; + +/* Type defs */ +typedef struct Process Process; +typedef enum ProcessFlags ProcessFlags; +typedef struct ProcessGroup ProcessGroup; +typedef struct Signal Signal; +typedef struct SignalList SignalList; +typedef struct Waiter Waiter; +typedef struct Fid Fid; + +/* Data structures */ +enum ProcessFlags +{ + SessionLeader = 1<<0, + GroupLeader = 1<<1, + NannyProcess = 1<<2, /* waits for a child and produce SIGCHLD */ + + CalledExec = 1<<3, /* do not free on close */ + Detached = 1<<4, +}; +struct ProcessInfos /* for actual processes */ +{ + PosixSignalMask waited; + SignalList* signals; +}; +struct NannyInfos /* for SIGCHLD generators */ +{ + int ppid; + int cpid; + Process* child; +}; +struct Process +{ + int pid; + int children; + int noteid; + ProcessFlags flags; + Process* parent; + ProcessGroup* group; + PosixSignalMask blocked; + PosixSignalMask ignored; + union{ + struct ProcessInfos; + struct NannyInfos; /* if flags & NannyProcess */ + }; + Process* next; +}; +struct Waiter +{ + uint16_t tag; + Fid* owner; + Signal* signal; + Waiter* next; +}; + +struct ProcessGroup +{ + int pgid; + int noteid; + int processes; + ProcessGroup* next; +}; + +struct Fid +{ + Qid qid; + int16_t opened; /* -1 when not open */ + int fd; + Process* process; /* if any */ + Fid *next; +}; + +/* Global variables */ +char notebuf[ERRMAX]; +int fspid; // filesystem id +int sid; // session leader id +int debugging = -1; +char *debug_prefix; +int debug_prefix_len; +char *user; +char *data; +extern void (*__assert)(char*); + +ProcessGroup* foreground; +ProcessGroup* groups; +Process* processes; +Process* session_leader; +Waiter* waiting_process; + + +/* Utilities (DEBUG, devproc and so on) */ +#define DEBUG(...) if (debugging != -1)debug(__VA_ARGS__) +static void +debug(const char *fmt, ...) +{ + va_list arg; + + if (debug_prefix == nil){ + debug_prefix = smprint("posix.%s.%d[%d]: ", user, sid, fspid); + debug_prefix_len = strlen(debug_prefix); + } + write(debugging, debug_prefix, debug_prefix_len); + va_start(arg, fmt); + vfprint(debugging, fmt, arg); + va_end(arg); +} +static void +usage(void) +{ + fprint(2, "usage: %s [-d dbgfile] [-p pid | cmd [args...]]\n", argv0); + exits("usage"); +} + +/* Signals */ +struct Signal +{ + int refs; + PosixSignalInfo info; + char note[]; +}; +struct SignalList +{ + Signal* signal; + SignalList* next; +}; +static Signal* +signal_create(PosixSignalInfo *siginfo) +{ + int i; + Signal *signal; + + __libposix_signal_to_note(siginfo, notebuf, sizeof(notebuf)); + + i = strlen(notebuf); + signal = malloc(sizeof(Signal)+i+1); + signal->refs = 1; + memmove(&signal->info, siginfo, sizeof(PosixSignalInfo)); + memmove(signal->note, notebuf, i); + signal->note[i] = '\0'; + + return signal; +} +static void +signal_free(Signal* signal) +{ + assert(signal != nil && signal->refs > 0); + if(--signal->refs == 0) + free(signal); +} + +/* Interface to devproc */ +static int +proc_noteid(int pid) +{ + long n; + char buf[32]; + sprint(buf, "/proc/%d/noteid", pid); + n = remove(buf); + DEBUG("proc_noteid(%d) = %lld\n", pid, n); + if(n == -1) + return -1; + return (int)n; +} +static int +proc_set_noteid(int pid, int noteid) +{ + int n, f; + char buf[32]; + + assert(pid != 0); + assert(noteid != 0); + + sprint(buf, "/proc/%d/noteid", pid); + f = open(buf, OWRITE); + if(f < 0) + return 0; + n = sprint(buf, "%d", noteid); + n = write(f, buf, n); + close(f); + if(n < 0) + return 0; + return 1; +} +static int +proc_ppid(int pid) +{ + long n; + char buf[32]; + sprint(buf, "/proc/%d/ppid", pid); + n = remove(buf); + DEBUG("proc_ppid(%d) = %lld\n", pid, n); + if(n == -1) + return -1; + return (int)n; +} +static int +proc_dispatch_signal(int target, Signal *signal) +{ + DEBUG("proc_dispatch_signal(%d, '%s')\n", target, signal->note); + if(postnote(PNPROC, target, signal->note) < 0) + return -1; + if(signal->info.si_signo == PosixSIGCONT) + __libposix_send_control_msg(target, "start"); + return 1; +} +static int +proc_convert_signal(int target, Signal *signal) +{ + char *note; + DEBUG("proc_convert_signal(%d, '%s')\n", target, signal->note); + switch(signal->info.si_signo){ + case PosixSIGCONT: + __libposix_send_control_msg(target, "start"); + return 1; + case PosixSIGTSTP: + case PosixSIGTTIN: + case PosixSIGTTOU: + case PosixSIGSTOP: + __libposix_send_control_msg(target, "stop"); + return 1; + case PosixSIGCHLD: + /* simply don't send notes that are not expected */ + return 1; + case PosixSIGHUP: + note = "hangup"; + break; + case PosixSIGALRM: + note = "alarm"; + break; + case PosixSIGINT: + note = "interrupt"; + break; + default: + note = signal->note; + break; + } + if(postnote(PNPROC, target, note) < 0) + return -1; + return 1; +} + + +/* ProcessGroups */ +static int process_handle_signal(Process *target, Signal *signal); +static void process_free(Process *target); + +static void +group_remove(ProcessGroup* group, Process* p) +{ + ProcessGroup **e, *g; + assert(group == p->group); + p->group = nil; + if(--group->processes == 0){ + e = &groups; + while(g = *e){ + if(group == g){ + *e = group->next; + if(foreground == group) + foreground = nil; + free(group); + return; + } + e = &g->next; + } + DEBUG("group_remove(%d, %d) not found\n", group->pgid, p->pid); + sysfatal("group_remove: group %d not found", group->pgid); + } + DEBUG("group_remove(%d, %d): done\n", group->pgid, p->pid); +} +static int +group_add(ProcessGroup* group, Process* p) +{ + assert(group != p->group); + if(!proc_set_noteid(p->pid, group->noteid)){ + DEBUG("group_add(%d, %d): cannot set noteid %d\n", group->pgid, p->pid, group->noteid); + return 0; + } + group_remove(p->group, p); + p->group = group; + p->noteid = group->noteid; + ++group->processes; + DEBUG("group_add(%d, %d): done\n", group->pgid, p->pid); + return 1; +} +static ProcessGroup* +group_create(Process *leader) +{ + ProcessGroup* g; + assert(leader != nil); + + if((leader->flags & GroupLeader) || (leader->flags & SessionLeader)) + return nil; /* operation not permitted */ + + g = mallocz(sizeof(ProcessGroup), 1); + if(g == nil) + return nil; + + DEBUG("group_create(%d)\n", leader->pid); + leader->flags |= GroupLeader; + g->pgid = leader->pid; + g->noteid = proc_noteid(leader->pid); + g->processes = 1; + if(leader->group) + group_remove(leader->group, leader); + leader->group = g; + if((leader->flags&NannyProcess) && leader->child != nil){ + group_add(g, leader->child); + } + g->next = groups; + groups = g; + DEBUG("group_create(%d): done\n", leader->pid); + return g; +} +static void +group_handle_signal(ProcessGroup *group, Signal *signal) +{ + Process *p, *tmp, **e; + assert(group != nil && signal != nil); + DEBUG("group_handle_signal(%d, %d): dispatching\n", group->pgid, signal->info.si_signo); + e = &processes; + while(p = *e){ + if(p->group == group && !(p->flags & NannyProcess)) + if(process_handle_signal(p, signal) == -1 + &&((p->flags & CalledExec)||(p->flags & Detached))){ + /* the signal dispatching (aka postnote) failed + * and the process called exec or setsid. + * Let assume that the process exited. + */ + tmp = p->next; + DEBUG("group_handle_signal(%d, %d): removing (probably) dead process %d\n", group->pgid, signal->info.si_signo, p->pid); + process_free(p); + *e = tmp; + continue; + } + e = &p->next; + } + DEBUG("group_handle_signal(%d, %d): done\n", group->pgid, signal->info.si_signo); +} +static int +group_foreground(Process *reqprocess, int pgid) +{ + static PosixSignalInfo siginfo; + ProcessGroup *g; + Signal *s; + + if(reqprocess->group == foreground + ||(reqprocess->blocked&SIGNAL_MASK(PosixSIGTTOU)) + ||(reqprocess->ignored&SIGNAL_MASK(PosixSIGTTOU))){ + g = groups; + while(g != nil && g->pgid != pgid) + g = g->next; + if(g == nil) + return 0; /* group not found */ + foreground = g; + return 1; + } + siginfo.si_signo = PosixSIGTTOU; + s = signal_create(&siginfo); + group_handle_signal(reqprocess->group, s); + signal_free(s); + return 0; +} +static ProcessGroup* +group_find(int gid) +{ + ProcessGroup* g; + + g = groups; + while(g && g->pgid != gid) + g = g->next; + + if(g && g->pgid == gid) + return g; + + return nil; +} + +/* Processes */ +static Process* +process_find(int pid) +{ + Process* p; + + p = processes; + while(p && p->pid < pid) + p = p->next; + + if(p && p->pid == pid) + return p; + + return nil; +} +static Process* +process_create(int pid) +{ + int ppid, tmp; + Process *n, *p, *t, **e; + + n = mallocz(sizeof(Process), 1); + if(n == nil) + return nil; + n->pid = pid; + ppid = proc_ppid(pid); + e = &processes; + while((p = *e) && p->pid < pid){ + if(p->pid == ppid){ + n->parent = p; + n->group = p->group; + ++n->group->processes; + } + e = &p->next; + } + if(p != nil && p->pid == pid){ + free(n); + return nil; + } + if(n->parent == nil && session_leader != nil){ + tmp = ppid; + while(tmp > sid){ + tmp = proc_ppid(tmp); + t = process_find(tmp); + if(t != nil){ + n->blocked = t->blocked; + n->group = t->group; + ++n->group->processes; + break; + } + } + } else if(session_leader != nil){ + /* libposix must ensure that the creation of a process + * here occurs before the exit of the parent process + * (so fork must be wait for the child to speak with us) + */ + ++n->parent->children; + if(n->parent->flags & NannyProcess){ + assert(n->child == nil); /* one nanny for one child */ + n->parent->cpid = pid; + n->parent->child = n; + } + n->blocked = n->parent->blocked; + n->ignored = n->parent->ignored; + ++n->group->processes; + } else { + session_leader = n; + foreground = group_create(session_leader); + session_leader->flags |= SessionLeader; + } + n->noteid = proc_noteid(pid); + n->next = p; + *e = n; + if(n->flags & SessionLeader){ + DEBUG("process_create(%d): session leader\n", pid); + } else if(n->parent == nil){ + DEBUG("process_create(%d): ppid %d (NOT POSIX), gid %d\n", pid, ppid, n->group ? n->group->pgid : -1); + } else { + DEBUG("process_create(%d): ppid %d, gid %d\n", pid, n->parent->pid, n->group->pgid); + } + return n; +} +static Process* +process_create_nanny(int pid) +{ + Process *p = process_create(pid); + if(p == nil) + return nil; + assert(p->parent != nil); + p->flags |= NannyProcess; + p->ppid = p->parent->pid; + return p; +} +static void +process_free_nannies(Process *parent, Process **list) +{ + Process *p; + char buf[256]; + int fd; + + while(parent->children > 0 && (p = *list)){ + if(p->parent == parent && (p->flags & NannyProcess)){ + *list = p->next; + DEBUG("process_free_nannies(%d): killing pid %d\n", parent->pid, p->pid); + snprint(buf, sizeof(buf), "/proc/%d/ctl", p->pid); + fd = open(buf, OWRITE); + if(fd < 0){ + DEBUG("process_free_nannies(%d): cannot open '%s': %r\n", parent, buf); + } else { + write(fd, "kill", 5); + } + close(fd); + if(p->child) + p->child->parent = nil; + free(p); + --parent->children; + } else { + list = &p->next; + } + } +} +static void +process_free(Process *target) +{ + Process *p, *c, **e; + Signal *s; + PosixSignalInfo siginfo; + + if(target == session_leader){ + if(foreground != nil){ + memset(&siginfo, 0, sizeof(PosixSignalInfo)); + siginfo.si_signo = PosixSIGHUP; + s = signal_create(&siginfo); + group_handle_signal(foreground, s); + signal_free(s); + } + session_leader = nil; + } + e = &processes; + while(p = *e){ + if(p == target){ + *e = p->next; + DEBUG("process_free(%d): gid %d\n", p->pid, p->group->pgid); + if(p->children > 0 && !(p->flags&NannyProcess)){ + /* remove all sigchild proxies */ + process_free_nannies(p, e); + } + if(p->children > 0){ + c = *e; + while(c && p->children > 0){ + if(c->parent == p){ + c->parent = nil; + --p->children; + } + c = c->next; + } + assert(p->children == 0); + } + if(p->parent){ + --p->parent->children; + if(p->parent->flags&NannyProcess) + p->parent->child = nil; + } + free(p); + return; + } + e = &p->next; + } + DEBUG("process_free(%d): not found!\n", target->pid); +} +static int +process_handle_signal(Process *target, Signal *signal) +{ + int tmp; + Waiter *wp; + SignalList *l; + + assert(target != nil && signal != nil); + + if(target->flags & NannyProcess){ + /* target is a proxy */ + if(signal->info.si_pid == target->cpid){ + /* this is a message from the child, to its parent */ + if(target->parent){ + signal->info.si_pid = target->pid; + tmp = process_handle_signal(target->parent, signal); + signal->info.si_pid = target->cpid; + return tmp; + } + return 1; + } + if(signal->info.si_pid == target->ppid){ + /* this is a message from the parent, to its child */ + if(target->child){ + signal->info.si_pid = target->pid; + tmp = process_handle_signal(target->child, signal); + signal->info.si_pid = target->ppid; + return tmp; + } + return 1; + } + /* this message is neither from the parent nor from the child + * but since only the parent can give away target pid + * as its own child pid, we assume the signal is for + * the child. + */ + if(target->child) + return process_handle_signal(target->child, signal); + } + + PosixSignalMask sigflag = SIGNAL_MASK(signal->info.si_signo); + if(target->ignored & sigflag) + return 1; + if(target->waited & sigflag){ + /* the process is waiting this signal reading */ + wp = waiting_process; + while(wp && wp->owner->process != target) + wp = wp->next; + assert(wp != nil && wp->owner->process == target); + wp->signal = signal; + ++signal->refs; + return 0; + } + if(target->blocked & sigflag){ + l = malloc(sizeof(SignalList)); + if(nil == l) + return 1; + l->signal = signal; + l->next = target->signals; + target->signals = l; + ++signal->refs; + return 0; + } + if(target->flags & CalledExec) + return proc_convert_signal(target->pid, signal); + return proc_dispatch_signal(target->pid, signal); +} +void +process_block_signals(Process *p, PosixSignalMask signals) +{ + SignalList *s, **es; + PosixSignalMask old; + + assert(p != nil); + + old = p->blocked; + if(old == signals){ + DEBUG("process_block_signals(%d, %ullb): nothing to do\n", p->pid, signals); + return; + } + p->blocked = signals; + + es = &p->signals; + while(s = *es){ + if((signals & SIGNAL_MASK(s->signal->info.si_signo)) == 0){ + *es = s->next; + DEBUG("process_block_signals(%d, %ullb): dispatching '%s'\n", p->pid, signals, s->signal->note); + process_handle_signal(p, s->signal); + signal_free(s->signal); + free(s); + continue; + } + es = &s->next; + } + DEBUG("process_block_signals(%d, %ullb): done\n", p->pid, signals); +} +void +process_enable_signal(Process *p, PosixSignalMask signalmask) +{ + PosixSignalMask old; + + assert(p != nil); + + old = p->ignored; + if(old & signalmask){ + p->ignored &= ~signalmask; + DEBUG("process_enable_signal(%d, %ullb): done\n", p->pid, signalmask); + } else { + DEBUG("process_enable_signal(%d, %ullb): nothing to do\n", p->pid, signalmask); + } +} +void +process_ignore_signal(Process *p, PosixSignalMask signalmask) +{ + SignalList *s, **es; + PosixSignalMask old; + + assert(p != nil); + + old = p->ignored; + if(old & signalmask){ + DEBUG("ignore_signals(%d, %ullb): nothing to do\n", p->pid, signalmask); + return; + } + p->ignored |= signalmask; + + es = &p->signals; + while(s = *es){ + if(signalmask & SIGNAL_MASK(s->signal->info.si_signo)){ + *es = s->next; + DEBUG("ignore_signals(%d, %ullb): discarded %d\n", p->pid, signalmask, s->signal->info.si_signo); + signal_free(s->signal); + free(s); + continue; + } + es = &s->next; + } + DEBUG("ignore_signals(%d, %ullb): done\n", p->pid, signalmask); +} +static Signal* +process_find_pending_signal(Process *p, PosixSignalMask mask) +{ + SignalList *s, **e; + Signal *found; + assert(p != nil); + + e = &p->signals; + while(s = *e){ + if(SIGNAL_MASK(s->signal->info.si_signo) & mask){ + *e = s->next; + found = s->signal; + free(s); + return found; + } + } + + return nil; +} +static PosixSignalMask +process_pending_signals(Process *p) +{ + PosixSignalMask pending = 0; + SignalList *s; + + assert(p != nil); + s = p->signals; + while(s){ + pending |= SIGNAL_MASK(s->signal->info.si_signo); + s = s->next; + } + + return pending; +} + +/* File System */ +typedef union +{ + long value; + PosixHelperRequest request; +} LongConverter; +typedef union +{ + int value; + char bytes[sizeof(int)]; +} IntConverter; +enum +{ + Maxfdata = 8192, + Miniosize = IOHDRSZ+sizeof(PosixSignalInfo), + Maxiosize = IOHDRSZ+Maxfdata, +}; + +typedef enum +{ + Initializing, + Mounted, + Unmounted, /* fsserve() loop while status < Unmounted */ +} Status; +Status status; + +enum { + Qroot, + Qposix, + Nqid, + Qcontrol, + Qsignals, + Qnanny, + Nhqid, +}; +static struct Qtab { + char *name; + int mode; + int type; + int length; +} qtab[Nhqid] = { + "/", + DMDIR|0555, + QTDIR, + 0, + + "posix", /* create files on fork */ + DMDIR|0755, + QTDIR, + 0, + + "", + 0, + 0, + 0, + + "control", + 0222, /* write to send signals */ + 0, + 0, + + "signals", /* read/write to control signal management */ + 0666, + 0, + 0, + + "nanny", /* write to send SIGCHLD */ + 0222, + 0, + 0, +}; + + + +/* linked list of known fids + * + * NOTE: we don't free() Fids, because there's no appropriate point + * in 9P2000 to do that, except the Tclunk of the attach fid, + * that in our case corresponds to shutdown + * (the kernel is our single client, we are doomed to trust it) + */ +#define ISCLOSED(f) (f != nil && f->opened == -1) + +static Fid *fids; +static Fid **ftail; +static Fid *external; /* attach fid of the mount() */ +static Fid *control_fid; + + +static Fid* +fid_create(int fd, Qid qid) +{ + Fid *fid; + + fid = (Fid*)malloc(sizeof(Fid)); + if(fid){ + fid->fd = fd; + fid->qid = qid; + fid->opened = -1; + fid->process = nil; + fid->next = nil; + *ftail = fid; + ftail = &fid->next; + } + return fid; +} +static Fid* +fid_find(int fd) +{ + Fid *fid; + + fid = fids; + while(fid != nil && fid->fd != fd) + fid = fid->next; + return fid; +} + +/* 9p message handlers */ +static int +fillstat(uint64_t path, Dir *d) +{ + struct Qtab *t; + + memset(d, 0, sizeof(Dir)); + d->uid = user; + d->gid = user; + d->muid = user; + d->qid = (Qid){path, 0, 0}; + d->atime = time(0); + t = qtab + path; + d->name = t->name; + d->qid.type = t->type; + d->mode = t->mode; + d->length = t->length; + return 1; +} +static int +rootread(Fid *fid, uint8_t *buf, long off, int cnt, int blen) +{ + int m, n; + long i, pos; + Dir d; + + n = 0; + pos = 0; + for (i = 1; i < Nqid; i++){ + fillstat(i, &d); + m = convD2M(&d, &buf[n], blen-n); + if(off <= pos){ + if(m <= BIT16SZ || m > cnt) + break; + n += m; + cnt -= m; + } + pos += m; + } + return n; +} +static int +fs_error(Fcall *rep, char *err) +{ + DEBUG("fs_error %#p: %s\n", __builtin_return_address(0), err); + rep->type = Rerror; + rep->ename = err; + return 1; +} +#define fs_eperm(req, rep) fs_error(rep, "permission denied") +#define fs_enotfound(req, rep) fs_error(rep, "file does not exist") +static int +fs_attach(Fcall *req, Fcall *rep) +{ + char *spec; + Fid *f; + + spec = req->aname; + if(spec && spec[0]){ + return fs_error(rep, "bad attach specifier"); + } + f = fid_find(req->fid); + if(f == nil) + f = fid_create(req->fid, (Qid){Qroot, 0, QTDIR}); + if(f == nil){ + return fs_error(rep, "out of memory"); + } + + external = f; + status = Mounted; + + rep->type = Rattach; + rep->qid = f->qid; + + return 1; +} +static int +fs_auth(Fcall *req, Fcall *rep) +{ + return fs_error(rep, "authentication not required"); +} +static int +fs_version(Fcall *req, Fcall *rep) +{ + if(req->msize < Miniosize){ + return fs_error(rep, "message size too small"); + } + rep->type = Rversion; + rep->msize = req->msize; + if(*req->version == 0 || strncmp(req->version, "9P2000", 6) == 0) + rep->version = "9P2000"; + else + rep->version = "unknown"; + return 1; +} +static int +fs_flush(Fcall *req, Fcall *rep) +{ + Fid *f; + Waiter **e, *s; + + e = &waiting_process; + while(s = *e){ + if(s->tag == req->oldtag){ + *e = s->next; + f = s->owner; + f->process->waited = 0; + if(s->signal != nil){ + process_handle_signal(f->process, s->signal); + signal_free(s->signal); + } + free(s); + break; + } + e = &s->next; + } + rep->type = Rflush; + return 1; + +} +static int +fs_walk(Fcall *req, Fcall *rep) +{ + Fid *f, *n; + Qid q; + + f = fid_find(req->fid); + if(f == nil) + return fs_error(rep, "bad fid"); + if(f->opened != -1) + return fs_error(rep, "fid in use"); + if(req->nwname > 0 && f->qid.type != QTDIR) + goto WalkInNonDirectory; + + if(req->fid == req->newfid){ + n = f; + } else { + n = fid_find(req->newfid); + if(n == nil) + n = fid_create(req->newfid, q); + else if(n->opened != -1) + return fs_error(rep, "newfid already in use"); + if(n == nil) + return fs_error(rep, "out of memory"); + } + if(req->nwname == 0){ + n->qid = f->qid; + rep->type = Rwalk; + rep->nwqid = 0; + return 1; + } + + // TODO handle '..' + switch(f->qid.path){ + case Qroot: + switch(req->nwname){ + case 1: + if(strcmp(qtab[Qposix].name, req->wname[0]) != 0) + goto FileNotExists; + rep->wqid[0] = (Qid){Qposix, 0, QTDIR}; + break; + case 2: + if(strcmp(qtab[Qposix].name, req->wname[0]) != 0) + goto FileNotExists; + if(control_fid != nil + || strcmp(qtab[Qcontrol].name, req->wname[1]) != 0) + goto FileNotExists; + rep->wqid[0] = (Qid){Qposix, 0, QTDIR}; + rep->wqid[1] = (Qid){Qcontrol, 0, 0}; + break; + default: + goto WalkInNonDirectory; + } + break; + case Qposix: + if(req->nwname > 1) + goto WalkInNonDirectory; + if(strcmp("..", req->wname[0]) == 0){ + rep->wqid[0] = (Qid){Qroot, 0, QTDIR}; + break; + } + if(strcmp(qtab[Qcontrol].name, req->wname[0]) != 0) + goto FileNotExists; + rep->wqid[0] = (Qid){Qcontrol, 0, 0}; + break; + default: + goto WalkInNonDirectory; + } + n->qid = rep->wqid[req->nwname-1]; + rep->type = Rwalk; + rep->nwqid = req->nwname; + return 1; + +WalkInNonDirectory: + return fs_error(rep, "walk in non directory"); +FileNotExists: + return fs_error(rep, "file does not exist"); +} +static int +fs_create(Fcall *req, Fcall *rep) +{ + static int need[4] = { + 4, /* NP_OREAD */ + 2, /* NP_OWRITE */ + 6, /* NP_ORDWR */ + 1 /* NP_OEXEC */ + }; + struct Qtab *t; + Fid *f; + int n; + + if(req->mode&NP_OZEROES) + return fs_error(rep, "invalid 9P2000 open mode"); + + f = fid_find(req->fid); + if(f == nil) + return fs_error(rep, "bad fid"); + + if(f->qid.path != Qposix) + return fs_eperm(req, rep); + + assert(f->process == nil); + if(strcmp("signals", req->name) == 0){ + t = qtab + Qsignals; + n = need[req->mode & 3]; + if((n & (t->mode>>6)) != n) + return fs_eperm(req, rep); + + n = (int)req->perm; + if(n == sid){ + /* this is the session leader */ + f->process = session_leader; + foreground = session_leader->group; + } else { + f->process = process_find(n); + if(f->process){ + if(f->process->flags & CalledExec){ + f->process->flags &= ~CalledExec; + } else { + f->process = nil; + return fs_eperm(req, rep); + } + } else { + f->process = process_create(n); + } + } + f->qid = (Qid){Qsignals, 0, 0}; + } else if(strcmp("nanny", req->name) == 0){ + t = qtab + Qnanny; + n = need[req->mode & 3]; + if((n & (t->mode>>6)) != n) + return fs_eperm(req, rep); + f->process = process_create_nanny((int)req->perm); + f->qid = (Qid){Qnanny, 0, 0}; + } + + if(f->process == nil) + return fs_eperm(req, rep); + + f->opened = req->mode; + + /* both processes and nannies share the same iounit */ + rep->type = Rcreate; + rep->qid = f->qid; + rep->iounit = sizeof(PosixSignalInfo); + + return 1; +} +static int +fs_open(Fcall *req, Fcall *rep) +{ + static int need[4] = { + 4, /* NP_OREAD */ + 2, /* NP_OWRITE */ + 6, /* NP_ORDWR */ + 1 /* NP_OEXEC */ + }; + struct Qtab *t; + Fid *f; + int n; + + if(req->mode&NP_OZEROES) + return fs_error(rep, "invalid 9P2000 open mode"); + + f = fid_find(req->fid); + if(f == nil) + return fs_error(rep, "bad fid"); + if(f->opened != -1) + return fs_error(rep, "already open"); + + t = qtab + f->qid.path; + n = need[req->mode & 3]; + if((n & (t->mode>>6)) != n) + return fs_eperm(req, rep); + + rep->iounit = 0; + switch(f->qid.path){ + case Qnanny: + case Qsignals: + return fs_enotfound(req, rep); + case Qcontrol: + if(control_fid) /* can be opened only once */ + return fs_enotfound(req, rep); + control_fid = f; + rep->iounit = sizeof(PosixSignalInfo); + break; + case Qroot: + case Qposix: + break; + default: + return fs_enotfound(req, rep); + } + f->opened = req->mode; + rep->type = Ropen; + rep->qid = f->qid; + return 1; +} +static int +fs_read(Fcall *req, Fcall *rep) +{ + Fid *f; + Signal *signal; + Waiter *waiter; + + if(req->count < 0) + return fs_error(rep, "bad read/write count"); + + f = fid_find(req->fid); + if(f == nil){ + return fs_error(rep, "bad fid"); + } + if(ISCLOSED(f) || f->opened == NP_OWRITE){ + return fs_error(rep, "i/o error"); + } + + rep->type = Rread; + switch(f->qid.path){ + case Qroot: + if(req->count == 0){ + rep->count = 0; + rep->data = nil; + return 1; + } + rep->count = rootread(f, (uint8_t*)data, req->offset, req->count, Maxfdata); + rep->data = data + req->offset; + return 1; + case Qposix: + /* posix/ is always empty */ + rep->count = 0; + rep->data = nil; + return 1; + case Qsignals: + assert(f->process != nil); + if(req->count != sizeof(PosixSignalInfo)) + return fs_error(rep, "i/o error"); + signal = process_find_pending_signal(f->process, req->offset); + if(signal != nil){ + memmove(data, &signal->info, sizeof(PosixSignalInfo)); + rep->data = data; + rep->count = sizeof(PosixSignalInfo); + signal_free(signal); + f->process->waited = 0; + return 1; + } + waiter = malloc(sizeof(Waiter)); + if(waiter == nil) + return fs_error(rep, "i/o error"); + + f->process->waited = req->offset; + waiter->tag = req->tag; + waiter->owner = f; + waiter->signal = nil; + waiter->next = waiting_process; + waiting_process = waiter; + return 0; + default: + return fs_error(rep, "i/o error"); + } +} +static int +fs_write(Fcall *req, Fcall *rep) +{ + /* write are always sincronous */ + LongConverter offset; + union + { + int group; + PosixSignalMask mask; + PosixSignalInfo signal; + char raw[sizeof(PosixSignalInfo)/sizeof(char)]; + } buffer; + Fid *f; + Process *p; + ProcessGroup *g; + Signal *s; + + if(req->count < 0) + return fs_error(rep, "bad read/write count"); + + f = fid_find(req->fid); + if(f == nil) + return fs_error(rep, "bad fid"); + if(ISCLOSED(f) || f->opened == NP_OREAD) + return fs_error(rep, "i/o error"); + + rep->count = 0; + offset.value = req->offset; + switch(f->qid.path){ + case Qcontrol: + switch(offset.request.command){ + case PHSignalForeground: + if(req->count != sizeof(PosixSignalInfo)){ + return fs_error(rep, "cannot send signal to foreground: invalid siginfo"); + } + if(foreground != nil){ + memmove(buffer.raw, req->data, sizeof(PosixSignalInfo)); + s = signal_create(&buffer.signal); + group_handle_signal(foreground, s); + signal_free(s); + } else { + DEBUG("fs_write: signal foreground: nothing to do (no foreground process).\n"); + } + break; + case PHSignalProcess: + goto SendSignalToProcess; + default: + DEBUG("fs_write: ignoring unknown Qcontrol command %d\n", offset.request.command); + return fs_error(rep, "i/o error"); + } + break; + case Qnanny: + if(f->process == nil || !(f->process->flags & NannyProcess)) + return fs_error(rep, "i/o error"); + switch(offset.request.command){ + case PHProcessExited: + /* this is for Nannies only, only for their child */ + if(f->process->cpid != offset.request.target) + return fs_error(rep, "i/o error"); + p = f->process->child; + if(p != nil) + process_free(p); + break; + case PHSignalProcess: + goto SendSignalToProcess; + default: + DEBUG("fs_write: ignoring unknown Qnanny command %d\n", offset.request.command); + return fs_error(rep, "i/o error"); + } + break; + case Qsignals: + /* here we handle commands */ + if(f->process == nil || (f->process->flags & NannyProcess)) + return fs_error(rep, "i/o error"); + switch(offset.request.command){ + case PHCallingExec: + if(f->process->flags & CalledExec) + return fs_error(rep, "i/o error"); + f->process->flags |= CalledExec; + break; + case PHSetProcessGroup: + if(offset.request.target == 0) + p = f->process; + else { + p = process_find(offset.request.target); + if(p == nil) + return fs_error(rep, "cannot set process group: unknown process"); + if(p != f->process + && p->parent != f->process + && p->parent != nil && (p->parent->flags&NannyProcess) && p->parent->parent != f->process) + return fs_eperm(req, rep); + } + if(p->flags&SessionLeader) + return fs_eperm(req, rep); + g = nil; + if(req->count == 0){ + g = group_create(p); + } else if(req->count == sizeof(int)){ + memmove(buffer.raw, req->data, sizeof(int)); + if(buffer.group < 0) + return fs_error(rep, "cannot set process group: invalid group"); + if(buffer.group == 0 || buffer.group == p->pid) + g = group_create(p); + else + g = group_find(buffer.group); + } else + return fs_error(rep, "cannot set process group: invalid group"); + if(g == nil) + return fs_eperm(req, rep); + if(p->group != g) + group_add(g, p); + break; + case PHIgnoreSignal: + if(req->count != sizeof(PosixSignalMask)){ + return fs_error(rep, "cannot set ignore mask: invalid mask"); + } + memmove(buffer.raw, req->data, sizeof(PosixSignalMask)); + process_ignore_signal(f->process, buffer.mask); + break; + case PHEnableSignal: + if(req->count != sizeof(PosixSignalMask)){ + return fs_error(rep, "cannot set ignore mask: invalid mask"); + } + memmove(buffer.raw, req->data, sizeof(PosixSignalMask)); + process_enable_signal(f->process, buffer.mask); + break; + case PHBlockSignals: + if(req->count != sizeof(PosixSignalMask)){ + return fs_error(rep, "cannot set block mask: invalid mask"); + } + memmove(buffer.raw, req->data, sizeof(PosixSignalMask)); + process_block_signals(f->process, buffer.mask); + break; + case PHSignalProcess: +SendSignalToProcess: + if(req->count != sizeof(PosixSignalInfo)){ + return fs_error(rep, "cannot send signal to proces: invalid siginfo"); + } + memmove(buffer.raw, req->data, sizeof(PosixSignalInfo)); + s = signal_create(&buffer.signal); + p = process_find(offset.request.target); + if(p != nil){ + if(process_handle_signal(p, s) == -1){ + process_free(p); + } + } else { + proc_convert_signal(offset.request.target, s); + } + signal_free(s); + break; + case PHSignalGroup: + if(req->count != sizeof(PosixSignalInfo)){ + return fs_error(rep, "cannot send signal to group: invalid siginfo"); + } + memmove(buffer.raw, req->data, sizeof(PosixSignalInfo)); + s = signal_create(&buffer.signal); + g = group_find(offset.request.target); + if(g != nil) + group_handle_signal(g, s); + signal_free(s); + break; + case PHGetProcMask: + rep->count = f->process->blocked; + break; + case PHGetSessionId: + if(offset.request.target) + p = process_find(offset.request.target); + else + p = f->process; + if(p != nil) + rep->count = sid; + else + return fs_eperm(req, rep); + break; + case PHGetProcessGroupId: + if(offset.request.target) + p = process_find(offset.request.target); + else + p = f->process; + if(p == nil){ + // TODO: lookup the group by noteid + return fs_eperm(req, rep); + } + rep->count = p->group->pgid; + break; + case PHSetForegroundGroup: + group_foreground(f->process, offset.request.target); + break; + case PHGetForegroundGroup: + if(foreground) + rep->count = foreground->pgid; + else + rep->count = 2147483647; // max_int; + break; + case PHDetachSession: + if((f->process->flags & SessionLeader) + || (f->process->flags & GroupLeader) + || (f->process->flags & NannyProcess) + || (f->process->flags & Detached)) + return fs_eperm(req, rep); + if(f->process->children > 0){ + /* we cannot free the process since some + * of its children are still in this + * session. + */ + f->process->flags |= Detached; + } else { + process_free(f->process); + f->process = nil; + } + break; + case PHGetPendingSignals: + rep->count = process_pending_signals(f->process); + break; + case PHSignalForeground: /* only in Qcontrol */ + case PHWaitSignals: /* read */ + return fs_error(rep, "i/o error"); + default: + DEBUG("fs_write: ignoring unknown Qprocess command %d\n", offset.request.command); + return fs_error(rep, "i/o error"); + } + break; + default: + return fs_error(rep, "i/o error"); + } + rep->type = Rwrite; + return 1; +} +static int +fs_clunk(Fcall *req, Fcall *rep) +{ + Fid *f; + PosixSignalInfo siginfo; + Signal *signal; + + f = fid_find(req->fid); + if(f != nil){ + if(f == external){ + DEBUG("fs_serve: external clients gone\n"); + status = Unmounted; + } else if(f->qid.path == Qsignals && f->process != nil){ + /* f->process might be nil for detached processes */ + if(f->process == session_leader && foreground != nil){ + // see https://unix.stackexchange.com/questions/407448/ + siginfo.si_signo = PosixSIGHUP; + siginfo.si_pid = sid; + signal = signal_create(&siginfo); + group_handle_signal(foreground, signal); + signal_free(signal); + } + if(!(f->process->flags & CalledExec)){ + if(f->process == session_leader) + session_leader = nil; + process_free(f->process); + } + f->process = nil; + } else if(f->qid.path == Qcontrol && session_leader != nil){ + assert(f->process == nil); + + // see https://unix.stackexchange.com/questions/407448/ + siginfo.si_signo = PosixSIGHUP; + siginfo.si_pid = sid; + signal = signal_create(&siginfo); + if(process_handle_signal(session_leader, signal) == -1 + &&((session_leader->flags & CalledExec)||(session_leader->flags & Detached))){ + /* the signal dispatching (aka postnote) failed + * and the session_leader called exec. + * Let assume that the process exited. + */ + process_free(session_leader); + session_leader = nil; + if(foreground) + group_handle_signal(foreground, signal); + } + signal_free(signal); + control_fid = nil; + } else if(f->qid.path == Qnanny) { + assert(f->process != nil); + process_free(f->process); + f->process = nil; + } + f->opened = -1; + } + rep->type = Rclunk; + return 1; +} +static int +fs_stat(Fcall *req, Fcall *rep) +{ + Dir d; + Fid *f; + static uint8_t mdata[Maxiosize]; + + f = fid_find(req->fid); + if(f == nil || f->qid.path >= Nqid){ + return fs_error(rep, "bad fid"); + } + + fillstat(f->qid.path, &d); + rep->type = Rstat; + rep->nstat = convD2M(&d, mdata, Maxiosize); + rep->stat = mdata; + return 1; +} +static int +fs_not_implemented(Fcall *req, Fcall *rep) +{ + return fs_eperm(req, rep); +} +static int (*fcalls[])(Fcall *, Fcall *) = { + [Tversion] fs_version, + [Tauth] fs_auth, + [Tattach] fs_attach, + [Tflush] fs_flush, + [Twalk] fs_walk, + [Topen] fs_open, + [Tcreate] fs_create, + [Tread] fs_read, + [Twrite] fs_write, + [Tclunk] fs_clunk, + [Tremove] fs_not_implemented, + [Tstat] fs_stat, + [Twstat] fs_not_implemented, +}; + + +static int +note_forward(void *v, char *s) +{ + PosixSignalInfo siginfo; + DEBUG("%d: noted: %s\n", *__libposix_pid, s); + + if(strncmp("sys:", s, 4) == 0) + return 0; // this is for us... + + // otherwhise it's for the foreground process + memset(&siginfo, 0, sizeof(PosixSignalInfo)); + if(!__libposix_note_to_signal(s, &siginfo)){ + DEBUG("unable to forward '%s'", s); + return 1; + } + __libposix_sighelper_signal(PHSignalForeground, 0, &siginfo); + return 1; +} +static void +traceassert(char*a) +{ + char buf[256]; + + snprint(buf, sizeof(buf), "assert failed: %s, %#p %#p\n", a, + __builtin_return_address(2), + __builtin_return_address(3) + ); + DEBUG(buf); + exits(buf); +} +void +enabledebug(const char *file) +{ + if (debugging < 0) { + if((debugging = ocreate(file, OCEXEC|OWRITE, 0666)) < 0) + sysfatal("ocreate(%s) %r", file); + __assert = traceassert; + fmtinstall('F', fcallfmt); + } +} +static int +readmessage(int fd, Fcall *req) +{ + int n; + + n = read9pmsg(fd, data, Maxiosize); + if(n > 0) + if(convM2S((uint8_t*)data, n, req) == 0){ + DEBUG("readmessage: convM2S returns 0\n"); + return -1; + } else { + DEBUG("fs_serve: <-%F\n", req); + return 1; + } + if(n < 0){ + DEBUG("readmessage: read9pmsg: %r\n"); + return -1; + } + return 0; +} +static int +sendmessage(int fd, Fcall *rep) +{ + int n; + static uint8_t repdata[Maxiosize]; + + n = convS2M(rep, repdata, Maxiosize); + if(n == 0) { + DEBUG("sendmessage: convS2M error\n"); + return 0; + } + if(write(fd, repdata, n) != n) { + DEBUG("sendmessage: write\n"); + return 0; + } + DEBUG("fs_serve: ->%F\n", rep); + return 1; +} +void +fs_serve(int connection) +{ + int r, w, syncrep; + Fcall rep; + Fcall *req; + Waiter *wp, **e; + + process_create(sid); + + req = malloc(sizeof(Fcall)+Maxfdata+((strlen(user)+1)*4)); + if(req == nil) + sysfatal("out of memory"); + data = malloc(Maxfdata); + if(data == nil) + sysfatal("out of memory"); + + ftail = &fids; + + status = Initializing; + + DEBUG("started\n"); + + do + { + DEBUG("wait for a new request\n"); + if((r = readmessage(connection, req)) <= 0){ + DEBUG("readmessage returns %d\n", r); + goto FSLoopExit; + } + + rep.tag = req->tag; + if(req->type < Tversion || req->type > Twstat) + syncrep = fs_error(&rep, "bad fcall type"); + else + syncrep = (*fcalls[req->type])(req, &rep); + + if(syncrep){ + if((w = sendmessage(connection, &rep)) <= 0){ + DEBUG("sendmessage returns %d\n", w); + goto FSLoopExit; + } + } + + /* Send available replies to processes waiting in reads */ + e = &waiting_process; + while(wp = *e){ + if(wp->signal){ + rep.type = Rread; + rep.tag = wp->tag; + rep.fid = wp->owner->fd; + rep.count = sizeof(PosixSignalInfo); + rep.data = (char*)&wp->signal->info; + if((w = sendmessage(connection, &rep)) <= 0){ + DEBUG("sendmessage for waiting process returns %d\n", w); + goto FSLoopExit; + } + *e = wp->next; + wp->owner->process->waited = 0; + signal_free(wp->signal); + free(wp); + continue; + } + e = &wp->next; + } + + /* We can exit (properly) only when the following conditions hold + * + * - the kernel decided that nobody need us anymore + * (status == Unmounted, see fs_clunk and fs_attach) + * + * Thus we exit when the kernel decides that nobody will + * need our services (aka, all the children sharing the + * mountpoint that we serve have exited). + * + * (AND obviously if an unexpected error occurred) + */ + } + while(status < Unmounted); +FSLoopExit: + + if(r < 0){ + DEBUG("error: fs_serve: readmessage"); + sysfatal("fs_serve: readmessage"); + } + if(w < 0){ + DEBUG("error: fs_serve: sendmessage"); + sysfatal("fs_serve: sendmessage"); + } + + close(connection); + DEBUG("close(%d)\n", connection); + + DEBUG("shut down\n"); +} + +/* Command line / Controller */ +static void +tty_from_cons(int fd, int mode) +{ + int tmp; + char buf[256]; + + if(sys_fd2path(fd, buf, sizeof(buf)) < 0) + sysfatal("fd2path: %d", fd); + tmp = strlen(buf); + if(tmp < 9 || strcmp(buf+tmp-9, "/dev/cons") != 0) + return; + tmp = open("/dev/tty", mode); + dup(tmp, fd); + close(tmp); +} +static void +unmount_dev(void) +{ + char name[256]; + snprint(name, sizeof(name), "#s/posixly.%s.%d", user, sid); + unmount(name, "/dev"); + remove(name); +} +static void +post_mount(int fd) +{ + /* we want the mount point to be unmount() on session detach, + * so it must have a deterministic name: "#s/posixly.glenda.123" + * is way better than "#|/data". + */ + int f; + char name[256], buf[32]; + + snprint(name, sizeof(name), "#s/posixly.%s.%d", user, sid); + f = create(name, OWRITE, 0600); + if(f < 0) + sysfatal("create(%s)", name); + sprint(buf, "%d", fd); + if(write(f, buf, strlen(buf)) != strlen(buf)) + sysfatal("write(%s)", name); + close(f); + close(fd); + + f = open(name, ORDWR); + if(f < 0) + sysfatal("open(%s)", name); + if(mount(f, -1, "/dev", MBEFORE, "", '9') == -1) + sysfatal("mount: %r"); +} +static void +open_control_fd(void) +{ + while((*__libposix_devsignal = open("/dev/posix/control", OWRITE)) < 0) + sleep(250); +} +void +main(int argc, char *argv[]) +{ + int p[2], i, sidprovided, fsrun, leaderrun, controlpid; + static PosixSignalInfo sighup; + int devsignal; + + sidprovided = 0; + + ARGBEGIN{ + case 'd': + enabledebug(EARGF(usage())); + break; + case 'p': + sidprovided = 1; + sid = atoi(EARGF(usage())); + break; + default: + usage(); + break; + }ARGEND; + + if(sid == 0 && argc < 1) + usage(); + + rfork(RFFDG); + + controlpid = getpid(); + __libposix_pid = &controlpid; + user = strdup(getuser()); + + if(sid == 0){ + rfork(RFREND|RFNAMEG); + + if(access("/dev/tty", AWRITE|AREAD) == 0){ + /* replace /dev/cons with /dev/tty */ + tty_from_cons(0, OREAD); + tty_from_cons(1, OWRITE); + tty_from_cons(2, ORDWR); + if((i = open("/dev/consctl", OWRITE)) > 0){ + write(i, "winchon", 7); + close(i); + } + } + + /* fork session leader */ + switch(sid = rfork(RFPROC|RFNOTEG|RFFDG)){ + case -1: + sysfatal("rfork"); + case 0: + while(rendezvous(main, (void*)0x1) == ((void*)~0)) + sleep(250); + close(debugging); + jehanne_pexec(strdup(argv[0]), argv); + exits("exec"); + default: + break; + } + } + + pipe(p); + + switch(fspid = rfork(RFPROC|RFMEM|RFCENVG|RFNOTEG|RFNAMEG|RFNOMNT|RFFDG|RFREND)){ + case -1: + sysfatal("rfork"); + case 0: + close(0); + close(1); + close(2); + close(p[0]); + fspid = getpid(); + fs_serve(p[1]); + exits(nil); + default: + break; + } + + close(0); + close(1); + close(2); + close(p[1]); + post_mount(p[0]); + + __libposix_devsignal = &devsignal; + open_control_fd(); + + rfork(RFCNAMEG); + + if(!atnotify(note_forward, 1)){ + fprint(2, "atnotify: %r\n"); + exits("atnotify"); + } + + if(!sidprovided){ + /* let the session leader start */ + while(rendezvous(main, (void*)0x2) == ((void*)~0)) + sleep(250); + /* if we created the session leader, we will wait for it */ + leaderrun = 1; + } + + /* We wait for fspid because it will be alive until any process + * will be in the namespace providing /dev/posix/. + * Indeed until there are process in such namespace we + * want to forward notes/signals to their foreground group. + * + * Also we wait for sid because if the session leader exits + * we have to send SIGHUP to the foreground group + */ + fsrun = 1; + while(fsrun || leaderrun){ + i = waitpid(); + if(i == fspid){ + DEBUG("file system exited\n"); + fsrun = 0; + leaderrun = 0; /* no need to wait for the leader */ + } else if(i == sid){ + DEBUG("session leader exited\n"); + sighup.si_signo = PosixSIGHUP; + __libposix_sighelper_signal(PHSignalForeground, 0, &sighup); + close(devsignal); + unmount_dev(); + leaderrun = 0; + } + } + exits(nil); +} diff --git a/sys/src/lib/posix/processes.c b/sys/src/lib/posix/processes.c index ab46eb8..a3a35a7 100644 --- a/sys/src/lib/posix/processes.c +++ b/sys/src/lib/posix/processes.c @@ -55,39 +55,29 @@ int (*__libposix_fork)(int *errnop) = fork_without_sigchld; void __libposix_free_wait_list(void) { - WaitList *wl, *c; + WaitList *tail, *w; /* free the wait list as the memory is NOT shared */ - wl = *__libposix_wait_list; - if(wl != nil){ - *__libposix_wait_list = nil; - do - { - c = wl; - wl = c->next; - free(c); - } - while (wl != nil); + tail = *__libposix_wait_list; + while(w = tail){ + tail = tail->next; + free(w); } + *__libposix_wait_list = nil; } void __libposix_free_child_list(void) { - ChildList *l, *c; + ChildList *tail, *c; /* free the wait list as the memory is shared */ - l = *__libposix_child_list; - if(l != nil){ - *__libposix_child_list = nil; - do - { - c = l; - l = c->next; - free(c); - } - while (l != nil); + tail = *__libposix_child_list; + while(c = tail){ + tail = tail->next; + free(c); } + *__libposix_child_list = nil; } int @@ -112,8 +102,7 @@ __libposix_forget_child(int pid) /* free the wait list as the memory is shared */ l = __libposix_child_list; - while(*l != nil){ - c = *l; + while(c = *l){ if(c->pid == pid){ *l = c->next; free(c); @@ -126,9 +115,18 @@ __libposix_forget_child(int pid) void __libposix_setup_new_process(void) { + extern PosixSignalMask *__libposix_signal_mask; + + *__libposix_pid = getpid(); + /* reset wait list for the child */ __libposix_free_wait_list(); __libposix_free_child_list(); + + /* clear pending signals; reload mask */ + __libposix_reset_pending_signals(); + __libposix_sighelper_open(); + *__libposix_signal_mask = (PosixSignalMask)__libposix_sighelper_cmd(PHGetProcMask, 0); } void @@ -178,8 +176,6 @@ POSIX_getrusage(int *errnop, PosixRUsages who, void *r_usagep) int POSIX_execve(int *errnop, const char *name, char * const*argv, char * const*env) { - long ret; - // see http://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html if(env == environ){ /* just get a copy of the current environment */ @@ -190,25 +186,18 @@ POSIX_execve(int *errnop, const char *name, char * const*argv, char * const*env) } __libposix_free_wait_list(); + __libposix_close_on_exec(); + __libposix_sighelper_cmd(PHCallingExec, 0); - ret = sys_exec(name, argv); - switch(ret){ - case 0: - return 0; - case ~0: - *errnop = __libposix_translate_errstr((uintptr_t)POSIX_execve); - break; - default: - *errnop = ret; - break; - } + sys_exec(name, argv); + *errnop = __libposix_translate_errstr((uintptr_t)POSIX_execve); return -1; } int POSIX_getpid(int *errnop) { - return getpid(); + return *__libposix_pid; } int @@ -223,13 +212,14 @@ POSIX_fork(int *errnop) return __libposix_fork(errnop); } -int -POSIX_wait(int *errnop, int *status) +static int +__libposix_wait(int *errnop, int *status, long ms) { Waitmsg *w; WaitList *l; - char *s; + char *s, err[ERRMAX]; int ret = 0, sig = 0, pid; + long wakeup = 0; l = *__libposix_wait_list; if(l != nil){ @@ -242,10 +232,25 @@ POSIX_wait(int *errnop, int *status) } OnIgnoredSignalInterrupt: + if(ms) + wakeup = awake(ms); w = wait(); if(w == nil){ - if(__libposix_restart_syscall()) + rerrstr(err, ERRMAX); + if(strstr(err, "no living children") != nil){ + *errnop = __libposix_get_errno(PosixECHILD); + return -1; + } + if(__libposix_restart_syscall()){ + if(wakeup) + forgivewkp(wakeup); goto OnIgnoredSignalInterrupt; + } + if(wakeup){ + if(awakened(wakeup)) + return 0; + forgivewkp(wakeup); + } *errnop = __libposix_get_errno(PosixECHILD); return -1; } @@ -283,13 +288,20 @@ POSIX_umask(int *errnop, int mask) return 0; } +int +POSIX_wait(int *errnop, int *status) +{ + return __libposix_wait(errnop, status, 0); +} + int POSIX_waitpid(int *errnop, int reqpid, int *status, int options) { Waitmsg *w; WaitList *c, **nl; - char *s; - int ret = 0, sig = 0, nohang = 0, pid; + char *s, err[ERRMAX]; + int ret = 0, sig = 0, pid; + long nohang = 0; if(options & __libposix_wnohang){ @@ -304,10 +316,9 @@ POSIX_waitpid(int *errnop, int reqpid, int *status, int options) switch(reqpid){ case -1: if(nohang){ - *errnop = __libposix_get_errno(PosixEINVAL); - return -1; + return __libposix_wait(errnop, status, 100); } - return POSIX_wait(errnop, status); + return __libposix_wait(errnop, status, 0); case 0: /* not yet implemented; requires changes to Waitmsg */ *errnop = __libposix_get_errno(PosixEINVAL); @@ -337,13 +348,30 @@ POSIX_waitpid(int *errnop, int reqpid, int *status, int options) WaitAgain: OnIgnoredSignalInterrupt: + if(nohang) + nohang = awake(100); w = wait(); if(w == nil){ - if(__libposix_restart_syscall()) + rerrstr(err, ERRMAX); + if(strstr(err, "no living children") != nil){ + *errnop = __libposix_get_errno(PosixECHILD); + return -1; + } + if(__libposix_restart_syscall()){ + if(nohang) + forgivewkp(nohang); goto OnIgnoredSignalInterrupt; + } + if(nohang){ + if(awakened(nohang)) + return 0; + forgivewkp(nohang); + } *errnop = __libposix_get_errno(PosixECHILD); return -1; } + if(nohang) + forgivewkp(nohang); pid = w->pid; __libposix_forget_child(pid); if(w->msg[0] != 0){ diff --git a/sys/src/lib/posix/sigchlds.c b/sys/src/lib/posix/sigchlds.c index c22b9d8..37cb7d3 100644 --- a/sys/src/lib/posix/sigchlds.c +++ b/sys/src/lib/posix/sigchlds.c @@ -21,61 +21,51 @@ #include #include "internal.h" -/* rendezvous points */ -extern unsigned char *__signals_to_code_map; -extern unsigned char *__code_to_signal_map; -extern ChildList **__libposix_child_list; /* pointer to the pid to forward notes to */ -extern int *__libposix_sigchld_target_pid; - -#define CHILD_READY(pid) ((long)rendezvous(&__signals_to_code_map, (void*)(~(pid)))) -#define C2P_READY(pid) ((long)rendezvous(&__code_to_signal_map, (void*)(~(pid)))) +extern ChildList **__libposix_child_list; +extern WaitList **__libposix_wait_list; +extern SignalConf *__libposix_signals; +extern int *__libposix_devsignal; static void -release_inherited_resources(void) +open_sighelper_nanny(void) { - notify(nil); - rfork(RFCNAMEG|RFCENVG|RFNOTEG|RFCFDG); - bind("#p", "/proc", MREPL); - rfork(RFNOMNT); + int mypid; + if(*__libposix_devsignal >= 0) + close(*__libposix_devsignal); + mypid = *__libposix_pid; + *__libposix_devsignal = create("/dev/posix/nanny", OWRITE|OCEXEC, mypid); + if(*__libposix_devsignal < 0) + sysfatal("cannot create /dev/posix/nanny: %r"); } static void -forwarding_note_handler(void *ureg, char *note) -{ - extern int __min_known_sig; - extern int __max_known_sig; - int sig; - PosixSignals signal; - if(strncmp(note, __POSIX_SIGNAL_PREFIX, __POSIX_SIGNAL_PREFIX_LEN) == 0){ - sig = __libposix_note_to_signal(note); - if(sig < __min_known_sig || sig > __max_known_sig){ - /* Ignore unknown signals */ - noted(NCONT); - } - signal = __code_to_signal_map[sig]; - __libposix_notify_signal_to_process(*__libposix_sigchld_target_pid, sig); - if(signal == PosixSIGCONT) - __libposix_send_control_msg(*__libposix_sigchld_target_pid, "start"); - noted(NCONT); - } else { - /* what happened? */ - noted(NDFLT); +exit_on_SA_NOCLDWAIT(char *msg){ + /* we share the father's memory, we can inspect its configuration */ + if(__libposix_signals[PosixSIGCHLD-1].sa_nochildwait){ + /* the parent does not care about us*/ + rfork(RFNOWAIT); + exits(msg); } } static void -forward_wait_msg(int sigchld_receiver, char *name) +forward_wait_msg(int father, int child) { - int n; - char buf[512], err[ERRMAX], note[512], *fld[5]; + int n, mypid; + PosixSignalInfo si; + char buf[512], err[ERRMAX], note[512], *fld[5], *tmp, *name; - snprint(buf, sizeof(buf), "/proc/%d/args", getpid()); + mypid = *__libposix_pid; + name = smprint("signal proxy %d <> %d", father, child); + snprint(buf, sizeof(buf), "/proc/%d/args", mypid); n = open(buf, OWRITE); write(n, name, strlen(name)+1); close(n); + rfork(RFCNAMEG|RFCENVG|RFNOMNT); + n = 0; WaitInterrupted: n = await(buf, sizeof buf-1); @@ -84,21 +74,45 @@ WaitInterrupted: if(strstr(err, "no living children") == nil) goto WaitInterrupted; snprint(note, sizeof(note), "%s: %r", name); - if(sigchld_receiver) - postnote(PNPROC, sigchld_receiver, note); + exit_on_SA_NOCLDWAIT(note); + postnote(PNPROC, father, note); + __libposix_sighelper_cmd(PHProcessExited, child); exits(note); } buf[n] = '\0'; if(jehanne_tokenize(buf, fld, nelem(fld)) != nelem(fld)){ - snprint(note, sizeof(note), "%s: couldn't parse wait message", name); - if(sigchld_receiver) - postnote(PNPROC, sigchld_receiver, note); + snprint(note, sizeof(note), "%s: can not parse wait msg: %s", name, buf); + exit_on_SA_NOCLDWAIT(note); + postnote(PNPROC, father, note); exits(note); } - snprint(note, sizeof(note), __POSIX_SIGNAL_PREFIX " %d", __signals_to_code_map[PosixSIGCHLD]); - if(sigchld_receiver){ - postnote(PNPROC, sigchld_receiver, note); - } + memset(&si, 0, sizeof(PosixSignalInfo)); + si.si_pid = mypid; + si.si_signo = PosixSIGCHLD; + si.si_code = PosixSIChildExited; + si.si_uid = POSIX_getuid(&n); + tmp = fld[4]; + if(tmp == nil || tmp[0] == '\0') + n = 0; + else if(strcmp(__POSIX_EXIT_PREFIX, tmp) == 0) + n = atoi(tmp + (sizeof(__POSIX_EXIT_PREFIX)/sizeof(char) - 1)); + else if(strcmp(__POSIX_EXIT_SIGNAL_PREFIX, tmp) == 0){ + n = atoi(tmp + (sizeof(__POSIX_EXIT_SIGNAL_PREFIX)/sizeof(char) - 1)); + if(n == PosixSIGTRAP) + si.si_code = PosixSIChildTrapped; + else + si.si_code = PosixSIChildKilled; + } else + n = 127; + si.si_status = n; + + exit_on_SA_NOCLDWAIT("SIGCHLD explicity ignored by parent"); + + __libposix_notify_signal_to_process(father, &si); + + __libposix_sighelper_cmd(PHProcessExited, child); + if(n == 0) + exits(nil); exits(fld[4]); } @@ -106,99 +120,76 @@ WaitInterrupted: static int fork_with_sigchld(int *errnop) { - int father = getpid(); - int p2c; - long c2p = -1, child = -1; - char proxy_name[256]; + int proxy, father, child = -1; ChildList *c; + uint64_t rend; + char *buf; /* Father here: - * - create P2C - * - wait for C2P to be ready - * - register P2C in children list - * - return P2C pid + * - create proxy + * - wait for child to be ready + * - register proxy in children list + * - return proxy pid */ - switch(p2c = rfork(RFPROC|RFMEM)){ + father = getpid(); + + switch(proxy = rfork(RFPROC|RFMEM|RFFDG)){ case -1: return -1; case 0: - /* P2C here: - * - create C2P - * - wait for the child pid - * - release all inherited resources - * - install forwarding_note_handler - * - send to father the C2P pid - * - start waiting for the child + /* proxy here: + * - create child + * - start waiting */ - switch(c2p = rfork(RFPROC|RFMEM)){ + *__libposix_pid = getpid(); + open_sighelper_nanny(); + switch(child = rfork(RFPROC|RFFDG)){ case -1: - while(C2P_READY(-2) == -1) - ; - exits("rfork (c2p)"); + rend = *__libposix_pid; + while(rendezvous((void*)rend, "e") == (void*)-1) + sleep(100); + rfork(RFNOWAIT); + exits("rfork (child)"); case 0: - /* C2P here: - * - create child - * - wait for it to get a copy of everything - * - release all inherited resources - * - install forwarding_note_handler - * - send to P2C the child pid - * - start forwarding notes to the father + /* Beloved child here */ - switch(child = fork()){ - case -1: - while(CHILD_READY(-2) == -1) - ; - exits("rfork (child)"); - case 0: - /* Beloved child here - */ - __libposix_setup_new_process(); - return 0; - default: - release_inherited_resources(); - *__libposix_sigchld_target_pid = father; - notify(forwarding_note_handler); - snprint(proxy_name, sizeof(proxy_name), "libposix signal proxy %d < %d", father, child); - while(CHILD_READY(child) == -1) - ; - forward_wait_msg(father, proxy_name); - } + __libposix_setup_new_process(); + rend = *__libposix_pid; + while(rendezvous((void*)rend, "d") == (void*)-1) + sleep(100); + rfork(RFREND); + return 0; default: - while((child = CHILD_READY(-3)) == -1) - ; - child = ~child; - if(child < 0){ - while(C2P_READY(-2) == -1) - ; - waitpid(); - exits("rfork (child)"); - } - release_inherited_resources(); - *__libposix_sigchld_target_pid = child; - notify(forwarding_note_handler); - snprint(proxy_name, sizeof(proxy_name), "libposix signal proxy %d > %d", father, child); - while(C2P_READY(c2p) == -1) - ; - forward_wait_msg(0, proxy_name); + rend = child; + while((buf = rendezvous((void*)rend, "")) == (void*)-1) + sleep(100); + rend = *__libposix_pid; + while(rendezvous((void*)rend, "d") == (void*)-1) + sleep(100); + + /* we share the memory of the parent but we do + * not need these that fortunately are on the private stack + */ + *__libposix_wait_list = nil; + *__libposix_child_list = nil; + forward_wait_msg(father, child); } default: - while((c2p = C2P_READY(-3)) == -1) - ; - c2p = ~c2p; - if(c2p < 0){ - waitpid(); + rend = proxy; + while((buf = rendezvous((void*)rend, "")) == (void*)-1) + sleep(100); + if(buf[0] == 'e') return -1; - } break; } /* no need to lock: the child list is private */ c = malloc(sizeof(ChildList)); - c->pid = p2c; + c->pid = proxy; c->next = *__libposix_child_list; *__libposix_child_list = c; - return p2c; + return proxy; } int diff --git a/sys/src/lib/posix/signals.c b/sys/src/lib/posix/signals.c index 4edfe18..ee20c02 100644 --- a/sys/src/lib/posix/signals.c +++ b/sys/src/lib/posix/signals.c @@ -19,22 +19,25 @@ /* POSIX signals have weird semantics that are hard to emulate in a * sane system without giving up on its sanity. * - * We distinguish control signals (SIGKILL, SIGSTOP, SIGCONT, - * SIGABRT, SIGIOT), SIGCHLD/SIGCLD, timers' wakeups + * We distinguish control signals (SIGKILL, SIGSTOP, SIGTSTP, SIGCONT, + * SIGABRT, SIGTTIN, SIGTTOU, SIGIOT), SIGCHLD/SIGCLD, timers' wakeups * (SIGALRM, SIGPROF, SIGVTALRM) and all the others. * * TRASMISSION * ----------- - * Signal transmission depends on the relation between the sender - * and the receiver: - * 1) if sender and receiver have no relation the signal is translated - * to a note and sent to the receiver(s); - * 2) if sender == receiver the signal trampoline is directly invoked - * for all signals except control ones and the default disposition occurs if - * the trampoline does not handle the signal - * 3) if sender is parent or child of receiver the transmision - * differs from the default if libposix_emulate_SIGCHLD() has been - * called during library initialization + * Signal transmission is done though a file server mounted /dev/posix/, + * provided by sys/posixly. On startup and at each fork, processes + * create a file named /dev/posix/signal with ORDWR mode and perm + * equals to their pid. Writing and Reading such file they can + * control signal dispatching, process groups and so on. + * + * When a signal is written to /dev/posix/signal, it is translated for + * each receiver to a note, and written to the specific note file. + * + * If the receiver is a known process, the note is in the format + * posix: si_signo si_pid si_uid si_value si_code + * otherwise, if possible, it is translated to an idiomatic note + * (eg "interrupt" or "alarm"). * * Since notes in Jehanne are not reentrant, signals translated to * notes will be enqueued in kernel. A special machinery is implemented @@ -53,14 +56,15 @@ * ---------------- * Control messages are translated by the receiver to equivalent actions: * - SIGKILL => write "kill" to the control file of the current process - * - SIGSTOP => write "stop" to the control file of the current process + * - SIGSTOP, SIGTSTP, SIGTTOU, SIGTTIN + * => write "stop" to the control file of the current process + * (when the signal is not handled nor ignored) * - SIGABRT/SIGIOT => invoke the registered signal handlers and abort * the current process * - SIGCONT => invoke the registered signal handlers via the signal * trampoline and continue; note that (since a stopped - * process cannot resume itself) the sender will write - * "start" to the control file of the receiver before - * sending the note (unless SIGCHLD emulation is enable). + * process cannot resume itself) sys/posixly will write + * "start" to the control file of the receiver. * * SIGCHLD/SIGCLD * -------------- @@ -72,30 +76,26 @@ * * Such emulation changes the way POSIX_fork and POSIX_kill works. * - * Each fork() will spawn two additional processes that are designed - * to proxy signals between the parent and the desired child: + * Each fork() will spawn an additional process that share the memory + * of the parent, and waits for the child, so that it can send SIGCHLD: * * parent - * +- proxy from parent to child (P2C) - * +- proxy from child to parent (C2P) - * +- child + * +- nanny + * +- child + * + * Such "nannies" connect to sys/posixly by creating /dev/posix/nanny + * so that the filesystem will handle them differently: + * - any signal for the nanny sent from the parent is delivered to the child + * - any signal for the nanny sent from the child is delivered to the parent + * - any signal for the nanny sent from anybody else is delivered to the child * - * Fork will return the pid of P2C to the parent, so that the - * parent process will see the first proxy as its child; the child will - * see the second as its parent. Each proxy waits for its child and - * forwards the notes it receive to its designed target. * Finally fork in child will return 0. * - * When the child exits, C2P will send a SIGCHLD note to parent and - * then exit with the same status. Then P2C will exit with - * the same status too. + * When the child exits, the proxy will send a SIGCHLD sigchld to parent + * and then exit with the same status. * - * As the parent process will see P2C as its child, it will send any - * signal to it via kill and will wait it on SIGCHLD. - * However, when this machinary is enabled, kill will treat all signals - * for forked children in the same way, sending them to P2C. - * It's P2C's responsibility to translate control messages as required, - * so that SIGCONT will work as expected. + * As the parent process will see the proxy as its child, it will send + * any signal to it via kill or sigqueue and will wait it on SIGCHLD. * * TIMERS * ------ @@ -104,6 +104,7 @@ * expire in a signal handler (interrupting a blocking syscall) but * without giving up the simplicity of notes. * + * (TO BE IMPLEMENTED: ) * We allocate these timers on libposix initialization. When normal * code is running timers will be implemented via Jehanne's alarms, * producing a note on expiration that will be mapped to the proper @@ -122,16 +123,22 @@ #include #include "internal.h" -unsigned char *__signals_to_code_map; -unsigned char *__code_to_signal_map; int *__handling_external_signal; int *__restart_syscall; +extern PosixSignalMask *__libposix_signal_mask; +extern int *__libposix_devsignal; -static int __sigrtmin; -static int __sigrtmax; -int __min_known_sig; -int __max_known_sig; -static PosixSignalTrampoline __libposix_signal_trampoline; +typedef union { + PosixSignalInfo signal; + char raw[sizeof(PosixSignalInfo)]; +} SignalBuf; +typedef union { + PosixSignalMask signals; + char raw[sizeof(PosixSignalMask)]; +} SigSetBuf; + +static SignalConf signal_configurations[PosixNumberOfSignals]; +SignalConf *__libposix_signals = signal_configurations; typedef enum PosixSignalDisposition { @@ -143,23 +150,37 @@ typedef enum PosixSignalDisposition } PosixSignalDisposition; static PosixError -note_all_writable_processes(int sig) +note_all_writable_processes(PosixSignalInfo* siginfo) { // TODO: loop over writable note files and post note. return PosixEPERM; } static void -terminated_by_signal(int sig) +terminated_by_signal(int signo) { char buf[64]; __libposix_free_wait_list(); - snprint(buf, sizeof(buf), __POSIX_EXIT_SIGNAL_PREFIX "%d", sig); + snprint(buf, sizeof(buf), __POSIX_EXIT_SIGNAL_PREFIX "%d", signo); exits(buf); } +void +__libposix_init_signal_handlers(void) +{ + __libposix_sighelper_set(PHIgnoreSignal, SIGNAL_MASK(PosixSIGCHLD)); + __libposix_sighelper_set(PHIgnoreSignal, SIGNAL_MASK(PosixSIGURG)); + __libposix_sighelper_set(PHIgnoreSignal, SIGNAL_MASK(PosixSIGWINCH)); + __libposix_signals[PosixSIGCHLD-1].handler = (void*)1; + __libposix_signals[PosixSIGCHLD-1].sa_restart = 1; + __libposix_signals[PosixSIGURG-1].handler = (void*)1; + __libposix_signals[PosixSIGURG-1].sa_restart = 1; + __libposix_signals[PosixSIGWINCH-1].handler = (void*)1; + __libposix_signals[PosixSIGWINCH-1].sa_restart = 1; +} + int __libposix_send_control_msg(int pid, char *msg) { @@ -189,7 +210,7 @@ ErrorBeforeOpen: /* Executes a PosixSignalDisposition. */ static int -execute_disposition(int sig, PosixSignalDisposition action) +execute_disposition(int signo, PosixSignalDisposition action) { int aborted_by_signal; @@ -199,32 +220,27 @@ execute_disposition(int sig, PosixSignalDisposition action) *__restart_syscall = 1; return 1; case TerminateTheProcess: - terminated_by_signal(sig); + terminated_by_signal(signo); break; case TerminateTheProcessAndCoreDump: aborted_by_signal = 0; assert(aborted_by_signal); break; case StopTheProcess: - return __libposix_send_control_msg(getpid(), "stop"); + return __libposix_send_control_msg(*__libposix_pid, "stop"); } return 0; } static PosixSignalDisposition -default_signal_disposition(int code) +default_signal_disposition(PosixSignals signal) { - PosixSignals signal; - - // see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html - if(__sigrtmin != 0 && __sigrtmax != 0 - &&(code >= __sigrtmin || code <= __sigrtmax)) + if(signal >= PosixSIGRTMIN && signal <= PosixSIGRTMAX) return TerminateTheProcess; - signal = __code_to_signal_map[code]; switch(signal){ default: - sysfatal("libposix: undefined signal %d", code); + sysfatal("libposix: undefined signal %d", signal); case PosixSIGALRM: case PosixSIGHUP: @@ -255,7 +271,6 @@ default_signal_disposition(int code) case PosixSIGXFSZ: return TerminateTheProcessAndCoreDump; case PosixSIGCHLD: - case PosixSIGCLD: case PosixSIGURG: return IgnoreWithNoEffect; case PosixSIGCONT: @@ -263,209 +278,360 @@ default_signal_disposition(int code) } } -PosixError -__libposix_receive_signal(int sig) +/* returns 1 if the signal handling has been completed, 0 otherwise */ +int +__libposix_run_signal_handler(SignalConf *c, PosixSignalInfo *siginfo) { - PosixSignalAction action = SignalDefault; - PosixSignals psig = __code_to_signal_map[sig]; - PosixSignalDisposition disposition; - - if(psig != PosixSIGKILL && psig != PosixSIGSTOP) - action = __libposix_signal_trampoline(sig); - if(psig == PosixSIGABRT) - action = SignalDefault; // lets abort despite the user will + PosixSigHandler h; + PosixSigAction a; + PosixSignalMask m; - switch(action){ - case SignalCatched: + switch((uintptr_t)c->handler){ + case 0: + /* SIG_DFL */ + if(c->sa_restart) + *__restart_syscall = 1; break; - case SignalIgnored: + case 1: + /* SIG_IGN */ + if(siginfo->si_signo == PosixSIGABRT) + break; *__restart_syscall = 1; - break; - case SignalDefault: - disposition = default_signal_disposition(sig); - if(!execute_disposition(sig, disposition)) - return PosixEPERM; - break; - case SignalError: - return PosixEINVAL; + return 1; + default: + m = *__libposix_signal_mask; + *__libposix_signal_mask |= c->mask; + __libposix_sighelper_set(PHBlockSignals, *__libposix_signal_mask); + a = c->handler; + h = c->handler; + + if(c->sa_resethand) + c->handler = 0; + + if(c->sa_siginfo){ + a(siginfo->si_signo, siginfo, nil); + } else { + h(siginfo->si_signo); + } + if(c->sa_restart) + *__restart_syscall = 1; + *__libposix_signal_mask = m; + __libposix_sighelper_set(PHBlockSignals, *__libposix_signal_mask); + if(siginfo->si_signo == PosixSIGABRT) + break; + return 1; } return 0; } PosixError -__libposix_notify_signal_to_process(int pid, int signal) +__libposix_receive_signal(PosixSignalInfo *siginfo) { - char buf[128]; - int fd, n; + SignalConf *c; + PosixSignalDisposition disposition; + PosixSignals signo = siginfo->si_signo; - snprint(buf, sizeof(buf), "/proc/%d/note", pid); - if((fd = open(buf, OWRITE)) < 0){ - if(access(buf, AEXIST) == 0) - return PosixEPERM; - else - return PosixESRCH; - } - n = snprint(buf, sizeof(buf), __POSIX_SIGNAL_PREFIX "%d", signal); - write(fd, buf, n); - close(fd); + if(signo == PosixSIGKILL || signo == PosixSIGSTOP) + goto ExecuteDefaultDisposition; + + if(__libposix_signal_blocked(siginfo)) + return 0; + + c = __libposix_signals + (signo-1); + if(__libposix_run_signal_handler(c, siginfo)) + return 0; + +ExecuteDefaultDisposition: + disposition = default_signal_disposition(signo); + if(!execute_disposition(signo, disposition)) + return PosixEPERM; return 0; } -static PosixError -notify_signal_to_group(int pid, int signal) +long +__libposix_sighelper_signal(PosixHelperCommand command, int posix_process_pid, PosixSignalInfo *siginfo) { - char buf[128]; - int fd, n; + union { + PosixHelperRequest request; + long raw; + } offset; + char buf[sizeof(PosixSignalInfo)]; - snprint(buf, sizeof(buf), "/proc/%d/notepg", pid); - if((fd = open(buf, OWRITE)) < 0){ - if(access(buf, AEXIST) == 0) - return PosixEPERM; - else - return PosixESRCH; - } - n = snprint(buf, sizeof(buf), __POSIX_SIGNAL_PREFIX "%d", signal); - write(fd, buf, n); - close(fd); - return 0; + offset.request.command = command; + offset.request.target = posix_process_pid; + + memcpy(buf, siginfo, sizeof(buf)); + + return pwrite(*__libposix_devsignal, buf, sizeof(buf), offset.raw); +} + +long +__libposix_sighelper_set(PosixHelperCommand command, PosixSignalMask signal_set) +{ + union { + PosixHelperRequest request; + long raw; + } offset; + union { + PosixSignalMask mask; + char raw[sizeof(PosixSignalMask)]; + } buffer; + + offset.request.command = command; + offset.request.target = 0; + + buffer.mask = signal_set; + + return pwrite(*__libposix_devsignal, buffer.raw, sizeof(buffer.raw), offset.raw); +} + +PosixError +__libposix_notify_signal_to_process(int pid, PosixSignalInfo *siginfo) +{ + long e = __libposix_sighelper_signal(PHSignalProcess, pid, siginfo); + return (PosixError)e; } static PosixError -dispatch_signal(int pid, int sig) +notify_signal_to_group(int pid, PosixSignalInfo* siginfo) +{ + long e = __libposix_sighelper_signal(PHSignalGroup, pid, siginfo); + return (PosixError)e; +} + +PosixError +__libposix_dispatch_signal(int pid, PosixSignalInfo* siginfo) { - PosixSignals signal; PosixError error; switch(pid){ case 0: - return notify_signal_to_group(getpid(), sig); + return notify_signal_to_group(*__libposix_pid, siginfo); case -1: - return note_all_writable_processes(sig); + return note_all_writable_processes(siginfo); default: if(pid < 0) - return notify_signal_to_group(-pid, sig); + return notify_signal_to_group(-pid, siginfo); break; } - signal = __code_to_signal_map[sig]; - error = __libposix_notify_signal_to_process(pid, sig); - if(signal == PosixSIGCONT && !__libposix_is_child(pid)) + error = __libposix_notify_signal_to_process(pid, siginfo); + if(siginfo->si_signo == PosixSIGCONT && !__libposix_is_child(pid)) __libposix_send_control_msg(pid, "start"); return error; } -int -POSIX_kill(int *errnop, int pid, int sig) +static int +translate_jehanne_kernel_note(const char *note, PosixSignalInfo *siginfo) { - PosixSignals signal; - PosixError perror; + char *trap[3]; + char *tmp; - signal = __code_to_signal_map[sig]; - if(signal == 0 - &&(__sigrtmin != 0 && __sigrtmax != 0) - &&(sig < __sigrtmin || sig > __sigrtmax)) - sysfatal("libposix: undefined signal %d", sig); - if(pid == getpid()) - perror = __libposix_receive_signal(sig); - else - perror = dispatch_signal(pid, sig); - if(perror != 0){ - *errnop = __libposix_get_errno(perror); - return -1; + assert(siginfo->si_signo == 0); + + if(strncmp("trap: fault ", note, 12) == 0){ + // trap: fault read addr=0x0 pc=0x400269 + note += 12; + siginfo->si_signo = PosixSIGTRAP; + tmp = strdup(note); + if(getfields(tmp, trap, 3, 1, " ") == 3){ + if(trap[0][0] == 'r') + siginfo->si_code = PosixSIFaultMapError; + else + siginfo->si_code = PosixSIFaultAccessError; + siginfo->si_value._sival_raw = atoll(trap[1]+5); + } + free(tmp); + } else if(strncmp("write on closed pipe", note, 20) == 0){ + // write on closed pipe pc=0x400269 + siginfo->si_signo = PosixSIGPIPE; + note += 24; + siginfo->si_value._sival_raw = atoll(note); + } else if(strncmp("bad address in syscall", note, 22) == 0){ + // bad address in syscall pc=0x41cc54 + siginfo->si_signo = PosixSIGSEGV; + note += 26; + siginfo->si_value._sival_raw = atoll(note); } - return 0; + // TODO: implement + + return siginfo->si_signo == 0 ? 0 : 1; } static int -translate_jehanne_note(char *note) +translate_jehanne_note(const char *note, PosixSignalInfo *siginfo) { - // TODO: implement + if(note == nil || note[0] == 0) + return 0; + + if(strncmp("alarm", note, 5) == 0){ + siginfo->si_signo = PosixSIGALRM; + return 1; + } + if(strncmp("sys: ", note, 5) == 0) + return translate_jehanne_kernel_note(note + 5, siginfo); + if(strncmp("interrupt", note, 9) == 0){ + siginfo->si_signo = PosixSIGINT; + return 1; + } + if(strncmp("hangup", note, 6) == 0){ + siginfo->si_signo = PosixSIGHUP; + return 1; + } + return 0; } int -__libposix_note_to_signal(char *note) +__libposix_signal_to_note(const PosixSignalInfo *si, char *buf, int size) { - return atoi(note+__POSIX_SIGNAL_PREFIX_LEN); + return + snprint(buf, size, __POSIX_SIGNAL_PREFIX "%d %d %d %#p %d", + si->si_signo, si->si_pid, si->si_uid, + si->si_value._sival_raw, si->si_code); +} + +/* The format of a note sent by libposix as a signal is + * + * "posix: %d %d %d %#p %d", signo, pid, uid, value, code + */ +int +__libposix_note_to_signal(const char *note, PosixSignalInfo *siginfo) +{ + assert(siginfo->si_signo == 0); /* siginfo must be zeroed */ + if(strncmp(note, __POSIX_SIGNAL_PREFIX, __POSIX_SIGNAL_PREFIX_LEN) != 0) + return translate_jehanne_note(note, siginfo); + char *rest = (char*)note + __POSIX_SIGNAL_PREFIX_LEN; + if(*rest == 0) + return 0; + siginfo->si_signo = strtol(rest, &rest, 0); + if(*rest == 0) + return 1; + siginfo->si_pid = strtol(rest, &rest, 0); + if(*rest == 0) + return 1; + siginfo->si_uid = strtoul(rest, &rest, 0); + if(*rest == 0) + return 1; + siginfo->si_value._sival_raw = strtoull(rest, &rest, 0); + if(*rest == 0) + return 1; + siginfo->si_code = strtoul(rest, &rest, 0); + return 1; } int __libposix_note_handler(void *ureg, char *note) { - int sig; + PosixSignalInfo siginfo; PosixError error; - if(strncmp(note, __POSIX_SIGNAL_PREFIX, __POSIX_SIGNAL_PREFIX_LEN) != 0) - return translate_jehanne_note(note); // TODO: should we translate common notes? - sig = __libposix_note_to_signal(note); - if(sig < __min_known_sig || sig > __max_known_sig) + + memset(&siginfo, 0, sizeof(PosixSignalInfo)); + + if(!__libposix_note_to_signal(note, &siginfo) + && (siginfo.si_signo < 1 || siginfo.si_signo > PosixNumberOfSignals)) sysfatal("libposix: '%s' does not carry a signal", note); *__handling_external_signal = 1; - error = __libposix_receive_signal(sig); + error = __libposix_receive_signal(&siginfo); *__handling_external_signal = 0; + werrstr("interrupted"); return error == 0; } -int -libposix_define_realtime_signals(int sigrtmin, int sigrtmax) -{ - if(sigrtmin >= 256 || sigrtmin <=0) - sysfatal("libposix: invalid SIGRTMIN %d (must be positive and less then 256)", sigrtmin); - if(sigrtmax >= 256 || sigrtmax <=0) - sysfatal("libposix: invalid SIGRTMAX %d (must be positive and less then 256)", sigrtmax); - if(sigrtmax <= sigrtmin) - sysfatal("libposix: invalid SIGRTMAX %d (must be greater than SIGRTMIN %d)", sigrtmax, sigrtmin); - if(__libposix_initialized()) - return 0; - __sigrtmin = sigrtmin; - __sigrtmax = sigrtmax; - if(sigrtmin < __min_known_sig || __min_known_sig == 0) - __min_known_sig = sigrtmin; - if(sigrtmax > __max_known_sig || __max_known_sig == 0) - __max_known_sig = sigrtmax; - return 1; -} - -int -libposix_define_signal(PosixSignals signal, int code) -{ - if(signal >= PosixNumberOfSignals) - sysfatal("libposix: unknown PosixSignal %d", signal); - if(code >= 256 || code <=0) - sysfatal("libposix: invalid signal number %d (must be positive and less then 256)", code); - if(__libposix_initialized()) - return 0; - __signals_to_code_map[signal] = (unsigned char)code; - if(signal != PosixSIGCLD || code != __signals_to_code_map[PosixSIGCHLD]){ - /* if SIGCHLD == SIGCLD then we use PosixSIGCHLD to identify the signal */ - __code_to_signal_map[code] = (unsigned char)signal; - } - if(code < __min_known_sig || __min_known_sig == 0) - __min_known_sig = code; - if(code > __max_known_sig || __max_known_sig == 0) - __max_known_sig = code; - return 1; -} - -int -libposix_set_signal_trampoline(PosixSignalTrampoline trampoline) -{ - if(__libposix_initialized()) - return 0; - if(trampoline == nil) - return 0; - __libposix_signal_trampoline = trampoline; - return 1; -} - int __libposix_restart_syscall(void) { - int r = *__restart_syscall; + int r; + + if(*__handling_external_signal) + return 0; + + r = *__restart_syscall; *__restart_syscall = 0; return r; } -void -__libposix_signal_check_conf(void) +int +POSIX_sigaction(int *errnop, int signo, const struct sigaction *act, struct sigaction *old) { - if(__libposix_signal_trampoline == nil) - sysfatal("libposix: no signal trampoline"); + SignalConf *c, oconf; + + if(signo < 1 || signo > PosixNumberOfSignals) + goto FailWithEINVAL; + + c = __libposix_signals + (signo-1); + + if(old) + memcpy(&oconf, c, sizeof(SignalConf)); + + if(act){ + memset(c, 0, sizeof(SignalConf)); + if(signo == PosixSIGKILL || signo == PosixSIGSTOP) + goto FailWithEINVAL; + if(act->sa_flags & PosixSAFSigInfo){ + c->sa_siginfo = 1; + c->handler = act->sa_sigaction; + } else { + c->handler = act->sa_handler; + } + c->mask = act->sa_mask & ~((255UL<<56) | SIGNAL_MASK(PosixSIGKILL) | SIGNAL_MASK(PosixSIGSTOP)); + if(act->sa_flags & PosixSAFResetHandler) + c->sa_resethand = 1; + else + c->sa_resethand = 0; + if(act->sa_flags & PosixSAFRestart) + c->sa_restart = 1; + else + c->sa_restart = 0; + if(signo == PosixSIGCHLD){ + if(act->sa_flags & PosixSAFNoChildrenWait) + c->sa_nochildwait = 1; + else + c->sa_nochildwait = 0; + if(c->handler == (void*)1) /* SIGCHLD && SIG_IGN => SA_NOCLDWAIT */ + c->sa_nochildwait = 1; + } + if(c->handler == (void*)1){ + /* ignore signal */ + __libposix_sighelper_set(PHIgnoreSignal, SIGNAL_MASK(signo)); + } else if(c->handler == (void*)0){ + /* default behavior */ + switch(signo){ + case PosixSIGCHLD: + case PosixSIGURG: + case PosixSIGWINCH: + __libposix_sighelper_set(PHIgnoreSignal, SIGNAL_MASK(signo)); + break; + default: + __libposix_sighelper_set(PHEnableSignal, SIGNAL_MASK(signo)); + break; + } + } else { + /* do not ignore signal */ + __libposix_sighelper_set(PHEnableSignal, SIGNAL_MASK(signo)); + } + } + + if(old){ + if(oconf.sa_siginfo){ + old->sa_sigaction = oconf.handler; + old->sa_mask = oconf.mask; + old->sa_flags = 0; + if(oconf.sa_siginfo) + old->sa_flags |= PosixSAFSigInfo; + if(oconf.sa_resethand) + old->sa_flags |= PosixSAFResetHandler; + if(oconf.sa_restart) + old->sa_flags |= PosixSAFRestart; + if(oconf.sa_nochildwait) + old->sa_flags |= PosixSAFNoChildrenWait; + } else { + old->sa_handler = oconf.handler; + old->sa_flags = 0; + old->sa_mask = 0; + } + } + return 0; + +FailWithEINVAL: + *errnop = __libposix_get_errno(PosixEINVAL); + return -1; } diff --git a/sys/src/lib/posix/sigqueue.c b/sys/src/lib/posix/sigqueue.c new file mode 100644 index 0000000..319fdac --- /dev/null +++ b/sys/src/lib/posix/sigqueue.c @@ -0,0 +1,49 @@ +/* + * This file is part of Jehanne. + * + * Copyright (C) 2017 Giacomo Tesio + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, version 3 of the License. + * + * Jehanne is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Jehanne. If not, see . + */ +#include +#include +#include +#include "internal.h" + +int +POSIX_sigqueue(int *errnop, int pid, int signo, const union sigval value) +{ + PosixError perror; + PosixSignalInfo siginfo; + int errno; + + if(signo < 1 || signo > PosixNumberOfSignals){ + *errnop = __libposix_get_errno(PosixEINVAL); + return -1; + } + memset(&siginfo, 0, sizeof(PosixSignalInfo)); + siginfo.si_signo = signo; + siginfo.si_pid = *__libposix_pid; + siginfo.si_code = PosixSIQueue; + siginfo.si_uid = POSIX_getuid(&errno); + siginfo.si_value._sival_raw = value._sival_raw; + if(pid == siginfo.si_pid) + perror = __libposix_receive_signal(&siginfo); + else + perror = __libposix_dispatch_signal(pid, &siginfo); + if(perror != 0){ + *errnop = __libposix_get_errno(perror); + return -1; + } + return 0; +} diff --git a/sys/src/lib/posix/sigsets.c b/sys/src/lib/posix/sigsets.c new file mode 100644 index 0000000..5bb0dc9 --- /dev/null +++ b/sys/src/lib/posix/sigsets.c @@ -0,0 +1,292 @@ +/* + * This file is part of Jehanne. + * + * Copyright (C) 2017 Giacomo Tesio + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, version 3 of the License. + * + * Jehanne is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Jehanne. If not, see . + */ + +#include +#include +#include +#include "internal.h" + +typedef union { + PosixSignalMask signals; + char raw[sizeof(PosixSignalMask)]; +} SigSetBuf; + +extern SignalConf *__libposix_signals; +extern int *__restart_syscall; +extern int *__libposix_devsignal; + +PosixSignalMask *__libposix_signal_mask; + +static PendingSignalList *__libposix_pending_signals; +static PendingSignalList **__libposix_pending_signals_end; +static int __libposix_blocked_signals; + +void +__libposix_reset_pending_signals(void) +{ + PendingSignalList *s; + while((s = __libposix_pending_signals) != nil){ + __libposix_pending_signals = s->next; + free(s); + } + __libposix_pending_signals_end = &__libposix_pending_signals; + *__restart_syscall = 0; + *__libposix_signal_mask = 0; +} + +int +__libposix_signal_blocked(PosixSignalInfo *siginfo) +{ + PendingSignalList *l; + + if((*__libposix_signal_mask & SIGNAL_MASK(siginfo->si_signo)) == 0) + return 0; + + /* Blocked signals are recorded at __libposix_pending_signals + * and will be delivered by sigprocmask when they will + * be unblocked + */ + l = malloc(sizeof(PendingSignalList)); + if(l == nil) + return 1; /* signal discarded */ + + memmove(&l->signal, siginfo, sizeof(PosixSignalInfo)); + l->next = nil; + *__libposix_pending_signals_end = l; + __libposix_pending_signals_end = &l->next; + *__restart_syscall = 1; + ++__libposix_blocked_signals; + return 1; +} + +int +POSIX_sigaddset(int *errnop, PosixSignalMask *set, int signo) +{ + if(signo < 1 || signo > PosixNumberOfSignals){ + *errnop = __libposix_get_errno(PosixEINVAL); + return -1; + } + SIGNAL_RAW_ADD(*set, signo); + return 0; +} + +int +POSIX_sigdelset(int *errnop, PosixSignalMask *set, int signo) +{ + if(signo < 1 || signo > PosixNumberOfSignals){ + *errnop = __libposix_get_errno(PosixEINVAL); + return -1; + } + SIGNAL_RAW_DEL(*set, signo); + return 0; +} + +int +POSIX_sigismember(int *errnop, const PosixSignalMask *set, int signo) +{ + if(signo < 1 || signo > PosixNumberOfSignals){ + *errnop = __libposix_get_errno(PosixEINVAL); + return -1; + } + if(*set & SIGNAL_MASK(signo)) + return 1; + return 0; +} + +int +POSIX_sigfillset(int *errnop, PosixSignalMask *set) +{ + *set = ~0; + return 0; +} + +int +POSIX_sigemptyset(int *errnop, PosixSignalMask *set) +{ + *set = 0; + return 0; +} + +int +POSIX_sigpending(int *errnop, PosixSignalMask *set) +{ + PendingSignalList *p; + PosixSignalMask tmp; + if(set == nil){ + *errnop = __libposix_get_errno(PosixEFAULT); + return -1; + } + + tmp = __libposix_sighelper_cmd(PHGetPendingSignals, 0); + + /* include pending signals from outside the session */ + p = __libposix_pending_signals; + while(p != nil){ + tmp |= SIGNAL_MASK(p->signal.si_signo); + p = p->next; + } + + *set = tmp; + return 0; +} + +long +__libposix_sighelper_wait(PosixSignalMask set, PosixSignalInfo *siginfo) +{ + return pread(*__libposix_devsignal, siginfo, sizeof(PosixSignalInfo), set); +} + +int +POSIX_sigtimedwait(int *errnop, const PosixSignalMask *set, + PosixSignalInfo *info, const struct timespec *timeout) +{ + long wkp = 0, r; + PendingSignalList *p, **end; + PosixSignalInfo tmp; + PosixError e = 0; + int ms = -1, bs; + + if(set == nil){ + e = PosixEFAULT; + goto FailWithError; + } + if(timeout != nil){ + if(timeout->tv_sec < 0 || timeout->tv_nsec < 0){ + e = PosixEINVAL; + goto FailWithError; + } + ms = timeout->tv_sec * 1000; + ms += timeout->tv_nsec / 1000000; + } + if(info == nil){ + memset(&tmp, 0, sizeof(PosixSignalInfo)); + info = &tmp; + } + +LookupPendingSignals: + bs = __libposix_blocked_signals; + end = &__libposix_pending_signals; + for(p = *end; p != nil; p = *end){ + if((*set & SIGNAL_MASK(p->signal.si_signo)) != 0){ + memcpy(info, &p->signal, sizeof(PosixSignalInfo)); + *end = p->next; + free(p); + if(__libposix_pending_signals == nil) + __libposix_pending_signals_end = &__libposix_pending_signals; + goto Done; + } + end = &p->next; + } + + if(bs != __libposix_blocked_signals) + goto LookupPendingSignals; + if(ms == 0){ + /* ms == 0 means that timeout has both fields to zero */ + if(__libposix_sighelper_cmd(PHGetPendingSignals, 0) == 0){ + e = PosixEAGAIN; + goto FailWithError; + } + } + + if(ms > 0) + wkp = awake(ms); + r = __libposix_sighelper_wait(*set, info); + if(r < 0){ + if(ms > 0 && awakened(wkp)){ + /* timed out */ + e = PosixEAGAIN; + goto FailWithError; + } + if(bs != __libposix_blocked_signals){ +LookupPendingSignals2: + bs = __libposix_blocked_signals; + end = &__libposix_pending_signals; + for(p = *end; p != nil; p = *end){ + if((*set & SIGNAL_MASK(p->signal.si_signo)) != 0){ + memcpy(info, &p->signal, sizeof(PosixSignalInfo)); + *end = p->next; + free(p); + if(__libposix_pending_signals == nil) + __libposix_pending_signals_end = &__libposix_pending_signals; + goto Done; + } + end = &p->next; + } + + if(bs != __libposix_blocked_signals) + goto LookupPendingSignals2; + } + e = PosixEINTR; + goto FailWithError; + } + if(ms > 0) + forgivewkp(wkp); + +Done: + return info->si_signo; + +FailWithError: + *errnop = __libposix_get_errno(e); + return -1; +} + +int +POSIX_sigprocmask(int *errnop, PosixSigProcMaskAction how, const PosixSignalMask *set, PosixSignalMask *oset) +{ + PendingSignalList *p, **e; + PosixSignalMask new; + PosixSignalMask old = *__libposix_signal_mask; + int sigindex; + if(set){ + new = *set; + new &= ~((255UL<<56) | (PosixSIGKILL-1) | (PosixSIGSTOP-1)); + switch(how){ + case PosixSPMSetMask: + *__libposix_signal_mask = new; + break; + case PosixSPMBlock: + *__libposix_signal_mask |= new; + break; + case PosixSPMUnblock: + *__libposix_signal_mask &= ~(new); + break; + default: + *errnop = __libposix_get_errno(PosixEINVAL); + return -1; + } + __libposix_sighelper_set(PHBlockSignals, *__libposix_signal_mask); + } + if(oset) + *oset = old; + new = *__libposix_signal_mask; + if(old & (~new)){ + e = &__libposix_pending_signals; + for(p = *e; p != nil; p = *e){ + sigindex = p->signal.si_signo - 1; + if((new & (1ULL << sigindex)) == 0){ + __libposix_run_signal_handler(__libposix_signals + sigindex, &p->signal); + *e = p->next; + free(p); + } else { + e = &p->next; + } + } + } + + return 0; +} diff --git a/sys/src/lib/posix/sigsuspend.c b/sys/src/lib/posix/sigsuspend.c new file mode 100644 index 0000000..f4d74e8 --- /dev/null +++ b/sys/src/lib/posix/sigsuspend.c @@ -0,0 +1,46 @@ +/* + * This file is part of Jehanne. + * + * Copyright (C) 2017 Giacomo Tesio + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, version 3 of the License. + * + * Jehanne is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Jehanne. If not, see . + */ + +#include +#include +#include +#include "internal.h" + +int +POSIX_sigsuspend(int *errnop, const PosixSignalMask *mask) +{ + PosixSignalMask old; + + if(mask == nil){ + *errnop = __libposix_get_errno(PosixEFAULT); + return -1; + } + + if(POSIX_sigprocmask(errnop, PosixSPMSetMask, mask, &old) != 0) + return -1; + + do + rendezvous((void*)~0, (void*)1); + while(__libposix_restart_syscall()); + + if(POSIX_sigprocmask(errnop, PosixSPMSetMask, &old, nil) != 0) + return -1; + + *errnop = __libposix_get_errno(PosixEINTR); + return -1; +} diff --git a/sys/src/lib/posix/termios.c b/sys/src/lib/posix/termios.c new file mode 100644 index 0000000..ab33dec --- /dev/null +++ b/sys/src/lib/posix/termios.c @@ -0,0 +1,62 @@ +/* + * This file is part of Jehanne. + * + * Copyright (C) 2017 Giacomo Tesio + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, version 3 of the License. + * + * Jehanne is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Jehanne. If not, see . + */ +#include +#include +#include +#include +#include "internal.h" + +int +POSIX_tcgetpgrp(int *errnop, int fd) +{ + long pgrp; + if(!POSIX_isatty(errnop, fd)) + return -1; + + pgrp = __libposix_sighelper_cmd(PHGetForegroundGroup, 0); + if(pgrp <= 0) + goto FailWithENOTTY; + + return pgrp; + +FailWithENOTTY: + *errnop = __libposix_get_errno(PosixENOTTY); + return -1; +} + +int +POSIX_tcsetpgrp(int *errnop, int fd, int pgrp) +{ + PosixError e; + + if(!POSIX_isatty(errnop, fd)) + return -1; + if(pgrp < 0){ + e = PosixEINVAL; + goto FailWithError; + } + if(__libposix_sighelper_cmd(PHSetForegroundGroup, pgrp) < 0){ + e = PosixEPERM; + goto FailWithError; + } + return 0; + +FailWithError: + *errnop = __libposix_get_errno(e); + return -1; +} diff --git a/sys/src/lib/posix/timers.c b/sys/src/lib/posix/timers.c index 9f796a4..79d2cc1 100644 --- a/sys/src/lib/posix/timers.c +++ b/sys/src/lib/posix/timers.c @@ -23,6 +23,13 @@ static PosixTimevalReader __libposix_timeval_reader; static PosixTimezoneReader __libposix_timezone_reader; +unsigned int +POSIX_alarm(int *errnop, unsigned int seconds) +{ + long r = alarm(seconds * 1000); + return r/1000; +} + int POSIX_gettimeofday(int *errnop, void *timeval, void *timezone) {