Browse Source

libposix: deep refactor; add sys/posixly command

With these changes, libposix (and newlib) can run MirBSD Korn Shell.
Giacomo Tesio 6 years ago
parent
commit
001069aa7b
56 changed files with 4937 additions and 606 deletions
  1. 2 0
      cfg/mksh/profile
  2. 5 0
      qa/check
  3. 5 5
      qa/kern/awake.c
  4. 1 1
      qa/lib/c/qlockt2.c
  5. 2 2
      qa/lib/c/rlockt1.c
  6. 1 1
      qa/lib/c/rlockt2.c
  7. 1 1
      qa/lib/c/rsleept2.c
  8. 1 1
      qa/lib/c/wlockt2.c
  9. 7 0
      qa/lib/newlib/001-hello.c
  10. 27 0
      qa/lib/newlib/002-atexit.c
  11. 44 0
      qa/lib/newlib/041-env.c
  12. 32 0
      qa/lib/newlib/041-env.runner
  13. 37 34
      qa/lib/newlib/050-setsid.c
  14. 1 1
      qa/lib/newlib/050-setsid.runner
  15. 48 0
      qa/lib/newlib/120-fcntl.c
  16. 82 0
      qa/lib/newlib/121-fcntl.c
  17. 4 3
      qa/lib/newlib/200-signals.c
  18. 4 3
      qa/lib/newlib/201-signals.c
  19. 2 4
      qa/lib/newlib/202-signals.c
  20. 3 5
      qa/lib/newlib/203-signals.c
  21. 2 4
      qa/lib/newlib/204-signals.c
  22. 51 0
      qa/lib/newlib/206-signals.c
  23. 74 0
      qa/lib/newlib/207-sigsuspend.c
  24. 69 0
      qa/lib/newlib/208-sigpending.c
  25. 54 0
      qa/lib/newlib/209-sigwaitinfo.c
  26. 58 0
      qa/lib/newlib/210-sigtimedwait.c
  27. 61 0
      qa/lib/newlib/211-sigtimedwait.c
  28. 54 0
      qa/lib/newlib/212-sigwait.c
  29. 88 0
      qa/lib/newlib/213-sigqueue.c
  30. 132 0
      qa/lib/newlib/214-sigsetjmp.c
  31. 38 0
      qa/lib/newlib/215-sigprocmask.c
  32. 34 2
      qa/lib/newlib/build.json
  33. 18 2
      qa/lib/newlib/libposix_customization.c
  34. 18 2
      qa/lib/newlib/libposix_sigchld.c
  35. 194 77
      sys/include/posix.h
  36. 5 1
      sys/src/cmd/sys/build.json
  37. 2 2
      sys/src/lib/libs.json
  38. 22 0
      sys/src/lib/posix/build.json
  39. 92 4
      sys/src/lib/posix/environment.c
  40. 35 0
      sys/src/lib/posix/errors.c
  41. 159 0
      sys/src/lib/posix/fcntl.c
  42. 71 0
      sys/src/lib/posix/files.c
  43. 149 68
      sys/src/lib/posix/ids.c
  44. 68 16
      sys/src/lib/posix/initlib.c
  45. 82 4
      sys/src/lib/posix/internal.h
  46. 50 0
      sys/src/lib/posix/kill.c
  47. 6 3
      sys/src/lib/posix/others.c
  48. 1941 0
      sys/src/lib/posix/posixly.c
  49. 76 48
      sys/src/lib/posix/processes.c
  50. 107 116
      sys/src/lib/posix/sigchlds.c
  51. 362 196
      sys/src/lib/posix/signals.c
  52. 49 0
      sys/src/lib/posix/sigqueue.c
  53. 292 0
      sys/src/lib/posix/sigsets.c
  54. 46 0
      sys/src/lib/posix/sigsuspend.c
  55. 62 0
      sys/src/lib/posix/termios.c
  56. 7 0
      sys/src/lib/posix/timers.c

+ 2 - 0
cfg/mksh/profile

@@ -0,0 +1,2 @@
+export PWD=`cat /dev/wdir`
+export HOSTNAME=$SYSNAME

+ 5 - 0
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
 	

+ 5 - 5
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");
 	}

+ 1 - 1
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");

+ 2 - 2
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

+ 1 - 1
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");

+ 1 - 1
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");

+ 1 - 1
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");

+ 7 - 0
qa/lib/newlib/001-hello.c

@@ -0,0 +1,7 @@
+#include <stdio.h>
+int main()
+{
+	fprintf(stdout, "hello, stdout\n");
+	fprintf(stderr, "hello, stderr\n");
+	return 0;
+}

+ 27 - 0
qa/lib/newlib/002-atexit.c

@@ -0,0 +1,27 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+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;
+}

+ 44 - 0
qa/lib/newlib/041-env.c

@@ -0,0 +1,44 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+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);
+}

+ 32 - 0
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

+ 37 - 34
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);
 	}
 }

+ 1 - 1
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

+ 48 - 0
qa/lib/newlib/120-fcntl.c

@@ -0,0 +1,48 @@
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+// 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);
+}

+ 82 - 0
qa/lib/newlib/121-fcntl.c

@@ -0,0 +1,82 @@
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+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);
+}

+ 4 - 3
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);

+ 4 - 3
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);

+ 2 - 4
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);

+ 3 - 5
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");

+ 2 - 4
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);

+ 51 - 0
qa/lib/newlib/206-signals.c

@@ -0,0 +1,51 @@
+#include <signal.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+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;
+}

+ 74 - 0
qa/lib/newlib/207-sigsuspend.c

@@ -0,0 +1,74 @@
+#define _POSIX_SOURCE
+#include <stdio.h>
+#include <signal.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+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);
+}

+ 69 - 0
qa/lib/newlib/208-sigpending.c

@@ -0,0 +1,69 @@
+#include <signal.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+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;
+}

+ 54 - 0
qa/lib/newlib/209-sigwaitinfo.c

@@ -0,0 +1,54 @@
+#include <signal.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+
+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;
+}

+ 58 - 0
qa/lib/newlib/210-sigtimedwait.c

@@ -0,0 +1,58 @@
+#include <signal.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+
+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;
+}

+ 61 - 0
qa/lib/newlib/211-sigtimedwait.c

@@ -0,0 +1,61 @@
+#include <signal.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+
+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;
+}

+ 54 - 0
qa/lib/newlib/212-sigwait.c

@@ -0,0 +1,54 @@
+#include <signal.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+
+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;
+}

+ 88 - 0
qa/lib/newlib/213-sigqueue.c

@@ -0,0 +1,88 @@
+#include <signal.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+#include <stdlib.h>
+
+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);
+}

+ 132 - 0
qa/lib/newlib/214-sigsetjmp.c

@@ -0,0 +1,132 @@
+#include <signal.h>
+#include <setjmp.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+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;
+}

+ 38 - 0
qa/lib/newlib/215-sigprocmask.c

@@ -0,0 +1,38 @@
+#include <signal.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+
+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);
+	}
+	
+}

+ 34 - 2
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": {

+ 18 - 2
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);
 }

+ 18 - 2
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();
 }

+ 194 - 77
sys/include/posix.h

@@ -30,12 +30,18 @@
  * #include <u.h>
  * #include <posix.h>
  *
- * 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 <apw/errno.h>
+
+/* 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 <apw/errno.h>
-
-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
  */

+ 5 - 1
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/"
 		],

+ 2 - 2
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/"
 		]
 	}
 }

+ 22 - 0
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"
+		]
 	}
 }

+ 92 - 4
sys/src/lib/posix/environment.c

@@ -89,12 +89,96 @@ put_in_list(EnvVar* newenv)
 	wunlock(&list_lock);
 }
 
+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){
+		*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 *errno, const char *name)
+POSIX_getenv(int *errnop, const char *name)
 {
 	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 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);

+ 35 - 0
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;

+ 159 - 0
sys/src/lib/posix/fcntl.c

@@ -0,0 +1,159 @@
+/*
+ * This file is part of Jehanne.
+ *
+ * Copyright (C) 2017 Giacomo Tesio <giacomo@tesio.it>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <u.h>
+#include <lib9.h>
+#include <posix.h>
+#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;
+}

+ 71 - 0
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)

+ 149 - 68
sys/src/lib/posix/ids.c

@@ -21,70 +21,83 @@
 #include <posix.h>
 #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);
-		return -1;
-	}
-	n = read(f, buf, sizeof(buf) - 1);
-	if(n < 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;
-	}
-	buf[n] = '\0';
-	n = atoi(buf);
-	close(f);
+	return (int)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;
 
-	return n;
+	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;
 }

+ 68 - 16
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);
+}

+ 82 - 4
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);

+ 50 - 0
sys/src/lib/posix/kill.c

@@ -0,0 +1,50 @@
+/*
+ * This file is part of Jehanne.
+ *
+ * Copyright (C) 2017 Giacomo Tesio <giacomo@tesio.it>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <u.h>
+#include <lib9.h>
+#include <posix.h>
+#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;
+}

+ 6 - 3
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;

+ 1941 - 0
sys/src/lib/posix/posixly.c

@@ -0,0 +1,1941 @@
+/*
+ * This file is part of Jehanne.
+ *
+ * Copyright (C) 2017 Giacomo Tesio <giacomo@tesio.it>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* 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 <u.h>
+#include <lib9.h>
+#include <envvars.h>
+#include <9P2000.h>
+#include <posix.h>
+#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);
+}

+ 76 - 48
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){

+ 107 - 116
sys/src/lib/posix/sigchlds.c

@@ -21,61 +21,51 @@
 #include <posix.h>
 #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

+ 362 - 196
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 <posix.h>
 #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;
+
+typedef union {
+	PosixSignalInfo	signal;
+	char		raw[sizeof(PosixSignalInfo)];
+} SignalBuf;
+typedef union {
+	PosixSignalMask	signals;
+	char		raw[sizeof(PosixSignalMask)];
+} SigSetBuf;
 
-static int __sigrtmin;
-static int __sigrtmax;
-int __min_known_sig;
-int __max_known_sig;
-static PosixSignalTrampoline __libposix_signal_trampoline;
+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;
-
-	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;
+	char *trap[3];
+	char *tmp;
+
+	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_handler(void *ureg, char *note)
+__libposix_note_to_signal(const char *note, PosixSignalInfo *siginfo)
 {
-	int sig;
-	PosixError error;
+	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); // TODO: should we translate common notes?
-	sig = __libposix_note_to_signal(note);
-	if(sig < __min_known_sig || sig > __max_known_sig)
-		sysfatal("libposix: '%s' does not carry a signal", note);
-	*__handling_external_signal = 1;
-	error = __libposix_receive_signal(sig);
-	*__handling_external_signal = 0;
-	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 translate_jehanne_note(note, siginfo);
+	char *rest = (char*)note + __POSIX_SIGNAL_PREFIX_LEN;
+	if(*rest == 0)
 		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;
+	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_define_signal(PosixSignals signal, int code)
+__libposix_note_handler(void *ureg, char *note)
 {
-	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;
-}
+	PosixSignalInfo siginfo;
+	PosixError error;
 
-int
-libposix_set_signal_trampoline(PosixSignalTrampoline trampoline)
-{
-	if(__libposix_initialized())
-		return 0;
-	if(trampoline == nil)
-		return 0;
-	__libposix_signal_trampoline = trampoline;
-	return 1;
+	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(&siginfo);
+	*__handling_external_signal = 0;
+	werrstr("interrupted");
+	return error == 0;
 }
 
 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;
 }

+ 49 - 0
sys/src/lib/posix/sigqueue.c

@@ -0,0 +1,49 @@
+/*
+ * This file is part of Jehanne.
+ *
+ * Copyright (C) 2017 Giacomo Tesio <giacomo@tesio.it>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+#include <u.h>
+#include <lib9.h>
+#include <posix.h>
+#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;
+}

+ 292 - 0
sys/src/lib/posix/sigsets.c

@@ -0,0 +1,292 @@
+/*
+ * This file is part of Jehanne.
+ *
+ * Copyright (C) 2017 Giacomo Tesio <giacomo@tesio.it>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <u.h>
+#include <lib9.h>
+#include <posix.h>
+#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;
+}

+ 46 - 0
sys/src/lib/posix/sigsuspend.c

@@ -0,0 +1,46 @@
+/*
+ * This file is part of Jehanne.
+ *
+ * Copyright (C) 2017 Giacomo Tesio <giacomo@tesio.it>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <u.h>
+#include <lib9.h>
+#include <posix.h>
+#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;
+}

+ 62 - 0
sys/src/lib/posix/termios.c

@@ -0,0 +1,62 @@
+/*
+ * This file is part of Jehanne.
+ *
+ * Copyright (C) 2017 Giacomo Tesio <giacomo@tesio.it>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+#include <u.h>
+#include <lib9.h>
+#include <envvars.h>
+#include <posix.h>
+#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;
+}

+ 7 - 0
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)
 {