hlfw.ca

drawcpu

Download patch

ref: 46b0c36526f6004ba57adf9fe675313bae870836
parent: 271a7db1cd9afb4cf841101188199c3a0d0f4c62
author: halfwit <michaelmisch1985@gmail.com>
date: Mon Feb 26 18:31:45 PST 2024

rc now builds fairly unchanged from upstream

--- a/Makefile
+++ b/Makefile
@@ -26,7 +26,7 @@
 	libip/libip.a\
 
 # stupid gcc
-LIBS=$(LIBS1) $(LIBS1) $(LIBS1) libmachdep.a
+LIBS=$(LIBS1) libmachdep.a
 
 default: $(TARG)
 $(TARG): $(OFILES) $(LIBS)
--- a/TODO
+++ b/TODO
@@ -1,40 +1,44 @@
 NOTES:
-Some builtins will be required **TEST** 
+Some commands we will need to add as builtins to rc
  - 'mount'
- - 'bind'
- - 'auth/factotum'
- - 'auth/secstore'
+ - 'bind', might need a device driver or header-exposed syscall See how bind (2) is coded
+ - 'unmount'
  - 'ns'
- - Can run a command or interactive shell
- - The profile is ran (!!!)
 
 TODO:
- - [ ] have it export a var $service=unix or $service=windows
+ - [ ] Install rcmain properly on system, refer to it as needed
+ - [ ] have it export a var service=unix
  - [ ] Some people probably want aan?
- - [ ] Move rc code to use our libc.h, u.h everywhere so our defs of open + such are correct
+ - [x] Move rc code to use our libc.h, u.h everywhere so our defs of open + such are correct
  - [ ] gui-cocoa try to capture the windows we create so we can send mouse/keyboard to it, plist for launchd
  - [ ] gui-wl cannibalize the wayland shims
- - [ ] handle notes and signals
- - [x] Remove listen1 outright, it's messing everything up.
- - [x] tlssrv, but use internal factotum bits
  - [ ] create namespaces with our bind/mount and allow report via ns(1)
  - [ ] define the gui interface
- - [ ] Makefile librc --> librc.a
- - [ ] move much of io.c and exec.c into our cpu space
-      - It looks like it's mostly broken out into unix.c/plan9.c to where the defs we'd want are and builtins
-      - Could pull in exec.c into the cpu scope instead of where it is, provide pieces into the library or abstracted out? Unsure. 
+ - [x] Makefile librc --> librc.a
+ - [x] libc.h getwd plan9port/src/lib9/getwd.c 
+ - [x] libc.h dirread /sys/src/libc/9sys/dirread.c
+ - [x] libc.h notify plan9port/src/lib9/notify.c
+ - [x] libc.h postnote plan9port/src/lib9/postnote.c
+ - [x] libc.h await plan9port/src/lib9/await.c
+ - [x] libc.h rfork plan9port/src/lib9/rfork.c 
+ - [x] libc.h wait plan9port/src/lib9/wait.c
+ - [x] libc.h time plan9port/src/lib9/time.c
+ - [ ] RFREND + RFNOMEM could probably be handled
 
 Kernel space needs:
- - devdraw, devkbd, devmouse, devaudio - wrong side
- - devcmd - we'll need more insight as to what this really does
- - devcons - needed, console
- - devenv - needed, env vars
- - devfs - needed, local files
- - devip - needed, networking
- - devlfd - I am unsure
- - devmnt - needed, client shares
- - devpipe - probably needed for any pipe
- - devroot - needed, base of stack
- - devssl - needed, ssl
- - devtab - meta, needed
- - devtls - needed! probably before connection even happens, though it may be odd scoping there, we may just need to bind it up just after the accept
+ - devdraw  - #i: !needed, We use the client exported /dev/draw instead
+ - devkbd   - #b: !needed, We use the client exported /dev/kbd instead
+ - devmouse - #m: !needed, We use the client exported /dev/mouse instead
+ - devaudio - #A: !needed, We use the client exported /dev/audio instead
+ - devcmd   - #C: needed OS(1) commands *outside* namespace
+ - devcons  - #c: needed, /dev/cons
+ - devenv   - #e: needed, /dev/env
+ - devfs    - #U: needed, local files
+ - devip    - #I: !needed, networking
+ - devlfd   - #L: !needed
+ - devmnt   - #M: needed, client shares
+ - devpipe  - #|: !needed for pipe in rc
+ - devroot  - #/: needed, base of stack
+ - devssl   - #D: !needed, ssl
+ - devtls   - #a: !needed, tls
+ - devtab   - meta, needed - add/remove any devices that we add/remove, from here as well
binary files /dev/null b/drawcpu differ
--- a/include/lib.h
+++ b/include/lib.h
@@ -66,6 +66,31 @@
 };
 
 /*
+ * one-of-a-kind
+ */
+enum
+{
+	PNPROC		= 1,
+	PNGROUP		= 2,
+};
+
+enum
+{
+	RFNAMEG		= (1<<0),
+	RFENVG		= (1<<1),
+	RFFDG		= (1<<2),
+	RFNOTEG		= (1<<3),
+	RFPROC		= (1<<4),
+	RFMEM		= (1<<5),
+	RFNOWAIT	= (1<<6),
+	RFCNAMEG	= (1<<10),
+	RFCENVG		= (1<<11),
+	RFCFDG		= (1<<12),
+	/*RFREND    - (1<<13),*/
+	/*RFNOMNT     = (1<<14),*/
+};
+
+/*
  * new rune routines
  */
 extern	int	runetochar(char*, Rune*);
@@ -155,6 +180,9 @@
 	uchar	type;
 } Qid;
 
+#define	STATMAX	65535U	/* max length of machine-independent stat structure */
+#define	DIRMAX	(sizeof(Dir)+STATMAX)	/* max length of Dir structure */
+
 typedef
 struct Dir {
 	/* system-modified data */
@@ -176,7 +204,7 @@
 struct Waitmsg
 {
 	int pid;	/* of loved one */
-	ulong time[3];	/* of loved one & descendants */
+	ulong dur[3];	/* of loved one & descendants */
 	char	*msg;
 } Waitmsg;
 
--- a/include/user.h
+++ b/include/user.h
@@ -3,8 +3,10 @@
 #define	chdir	syschdir
 #define	close	sysclose
 #define create	syscreate
-#define dup	sysdup
+#define dup	    sysdup
 #define export	sysexport
+#define getwd   sysgetwd
+#define fd2path sysfd2path
 #define fstat	sysfstat
 #define fwstat	sysfwstat
 #define mount	sysmount
@@ -11,8 +13,10 @@
 #define	open	sysopen
 #define read	sysread
 #define remove	sysremove
+#define rfork   sysrfork
 #define seek	sysseek
 #define stat	sysstat
+#define wait    syswait
 #define	write	syswrite
 #define wstat	syswstat
 #define unmount	sysunmount
@@ -34,8 +38,10 @@
 extern	int	create(char*, int, ulong);
 extern	int	dup(int, int);
 extern  int	export(int);
+extern  int fd2path(int, char*, int);
 extern	int	fstat(int, uchar*, int);
 extern	int	fwstat(int, uchar*, int);
+extern  char* getwd(char *, int);
 extern	int	mount(int, int, char*, int, char*);
 extern	int	unmount(char*, char*);
 extern	int	open(char*, int);
@@ -43,17 +49,19 @@
 extern	long	read(int, void*, long);
 extern	long	readn(int, void*, long);
 extern	int	remove(char*);
+extern  int rfork(int);
 extern	vlong	seek(int, vlong, int);
 extern	int	stat(char*, uchar*, int);
+extern  long time(long*);
 extern	long	write(int, void*, long);
 extern	int	wstat(char*, uchar*, int);
 extern	void	werrstr(char* ,...);
-
+extern  Waitmsg* wait(void);
 extern	Dir	*dirstat(char*);
 extern	Dir	*dirfstat(int);
 extern	int	dirwstat(char*, Dir*);
 extern	int	dirfwstat(int, Dir*);
-extern	long	dirread(int, Dir*, long);
+extern	long	dirread(int, Dir**);
 extern	ulong	iounit(int);
 
 extern	int	lfdfd(int);
@@ -87,6 +95,8 @@
 extern	int	errstr(char*, uint);
 extern  char *estrdup(char*);
 extern	int	rerrstr(char*, uint);
+extern  int notify(void (*)(void*, char*));
+extern  int noted(int);
 extern	int	encrypt(void*, void*, int);
 extern	int	decrypt(void*, void*, int);
 extern	void	qlock(QLock*);
@@ -100,5 +110,8 @@
 extern	void	exits(char*);
 extern	char*	getenv(char*);
 
+extern  int await(char*, int);
+extern  int awaitnohang(char*, int);
+extern  int awaitfor(int, char*, int);
 
 #define IOUNIT			32768	/* default buffer size for 9p io */
--- a/kern/Makefile
+++ b/kern/Makefile
@@ -5,6 +5,7 @@
 OFILES=\
 	alloc.$O\
 	allocb.$O\
+	await.$O\
 	chan.$O\
 	data.$O\
 	dev.$O\
@@ -26,17 +27,24 @@
 	devssl.$O\
 	devtls.$O\
 	devtab.$O\
+	dirread.$O\
 	error.$O\
+	getwd.$O\
 	parse.$O\
 	pgrp.$O\
+	postnote.$O\
 	procinit.$O\
+	notify.$O\
+	rfork.$O\
 	rwlock.$O\
 	sleep.$O\
 	stub.$O\
 	sysfile.$O\
+	time.$O\
 	qio.$O\
 	qlock.$O\
 	term.$O\
+	wait.$O\
 	waserror.$O\
 	$(OS).$O
 
--- /dev/null
+++ b/kern/await.c
@@ -1,0 +1,136 @@
+
+#include <u.h>
+#include <libc.h>
+
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#ifndef WCOREDUMP	/* not on Mac OS X Tiger */
+#define WCOREDUMP(status) 0
+#endif
+
+static struct {
+	int sig;
+	char *str;
+} tab[] = {
+	SIGHUP,		"hangup",
+	SIGINT,		"interrupt",
+	SIGQUIT,		"quit",
+	SIGILL,		"sys: illegal instruction",
+	SIGTRAP,		"sys: breakpoint",
+	SIGABRT,		"sys: abort",
+#ifdef SIGEMT
+	SIGEMT,		"sys: emulate instruction executed",
+#endif
+	SIGFPE,		"sys: fp: trap",
+	SIGKILL,		"sys: kill",
+	SIGBUS,		"sys: bus error",
+	SIGSEGV,		"sys: segmentation violation",
+	SIGALRM,		"alarm",
+	SIGTERM,		"kill",
+	SIGURG,		"sys: urgent condition on socket",
+	SIGSTOP,		"sys: stop",
+	SIGTSTP,		"sys: tstp",
+	SIGCONT,		"sys: cont",
+	SIGCHLD,		"sys: child",
+	SIGTTIN,		"sys: ttin",
+	SIGTTOU,		"sys: ttou",
+#ifdef SIGIO	/* not on Mac OS X Tiger */
+	SIGIO,		"sys: i/o possible on fd",
+#endif
+	SIGXCPU,		"sys: cpu time limit exceeded",
+	SIGXFSZ,		"sys: file size limit exceeded",
+	SIGVTALRM,	"sys: virtual time alarm",
+	SIGPROF,		"sys: profiling timer alarm",
+#ifdef SIGWINCH	/* not on Mac OS X Tiger */
+	SIGWINCH,	"sys: window size change",
+#endif
+#ifdef SIGINFO
+	SIGINFO,		"sys: status request",
+#endif
+	SIGUSR1,		"sys: usr1",
+	SIGUSR2,		"sys: usr2",
+	SIGPIPE,		"sys: write on closed pipe",
+};
+
+char*
+_p9sigstr(int sig, char *tmp)
+{
+	int i;
+
+	for(i=0; i<nelem(tab); i++)
+		if(tab[i].sig == sig)
+			return tab[i].str;
+	if(tmp == nil)
+		return nil;
+	sprint(tmp, "sys: signal %d", sig);
+	return tmp;
+}
+
+int
+_p9strsig(char *s)
+{
+	int i;
+
+	for(i=0; i<nelem(tab); i++)
+		if(strcmp(s, tab[i].str) == 0)
+			return tab[i].sig;
+	return 0;
+}
+
+static int
+_await(int pid4, char *str, int n, int opt)
+{
+	int pid, status, cd;
+	struct rusage ru;
+	char buf[128], tmp[64];
+	ulong u, s;
+
+	for(;;){
+		/* On Linux, pid==-1 means anyone; on SunOS, it's pid==0. */
+		if(pid4 == -1)
+			pid = wait3(&status, opt, &ru);
+		else
+			pid = wait4(pid4, &status, opt, &ru);
+		if(pid <= 0)
+			return -1;
+		u = ru.ru_utime.tv_sec*1000+((ru.ru_utime.tv_usec+500)/1000);
+		s = ru.ru_stime.tv_sec*1000+((ru.ru_stime.tv_usec+500)/1000);
+		if(WIFEXITED(status)){
+			status = WEXITSTATUS(status);
+			if(status)
+				snprint(buf, sizeof buf, "%d %lud %lud %lud %d", pid, u, s, u+s, status);
+			else
+				snprint(buf, sizeof buf, "%d %lud %lud %lud ''", pid, u, s, u+s, status);
+			strecpy(str, str+n, buf);
+			return strlen(str);
+		}
+		if(WIFSIGNALED(status)){
+			cd = WCOREDUMP(status);
+			snprint(buf, sizeof buf, "%d %lud %lud %lud 'signal: %s%s'", pid, u, s, u+s, _p9sigstr(WTERMSIG(status), tmp), cd ? " (core dumped)" : "");
+			strecpy(str, str+n, buf);
+			return strlen(str);
+		}
+	}
+}
+
+int
+await(char *str, int n)
+{
+	return _await(-1, str, n, 0);
+}
+
+int
+awaitnohang(char *str, int n)
+{
+	return _await(-1, str, n, WNOHANG);
+}
+
+int
+awaitfor(int pid, char *str, int n)
+{
+	return _await(pid, str, n, 0);
+}
--- /dev/null
+++ b/kern/dirread.c
@@ -1,0 +1,92 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+
+static
+long
+dirpackage(uchar *buf, long ts, Dir **d)
+{
+	char *s;
+	long i, n, nn, m;
+
+	/*
+	 * first find number of all stats, check they look like stats, & size all associated strings
+	 */
+	n = 0;
+	for(i = 0; i < ts; i += m){
+		if(i+BIT16SZ >= ts)
+			return -1;
+		m = BIT16SZ + GBIT16(&buf[i]);
+		if(i+m > ts || statcheck(&buf[i], m) < 0)
+			return -1;
+		n++;
+	}
+
+	*d = malloc(n * sizeof(Dir) + ts);
+	if(*d == nil)
+		return -1;
+
+	/*
+	 * then convert all buffers
+	 */
+	s = (char*)*d + n * sizeof(Dir);
+	nn = 0;
+	for(i = 0; i < ts; i += m){
+		m = BIT16SZ + GBIT16(&buf[i]);
+		if(i+m > ts || nn >= n || convM2D(&buf[i], m, *d + nn, s) != m){
+			free(*d);
+			*d = nil;
+			return -1;
+		}
+		nn++;
+		s += m;
+	}
+
+	return nn;
+}
+
+long
+dirread(int fd, Dir **d)
+{
+	uchar *buf;
+	long ts;
+
+	*d = nil;
+	buf = malloc(DIRMAX);
+	if(buf == nil)
+		return -1;
+	ts = read(fd, buf, DIRMAX);
+	if(ts > 0)
+		ts = dirpackage(buf, ts, d);
+	free(buf);
+	return ts;
+}
+
+long
+dirreadall(int fd, Dir **d)
+{
+	uchar *buf, *nbuf;
+	long n, ts;
+
+	*d = nil;
+	buf = nil;
+	ts = 0;
+	for(;;){
+		nbuf = realloc(buf, ts+DIRMAX);
+		if(nbuf == nil){
+			free(buf);
+			return -1;
+		}
+		buf = nbuf;
+		n = read(fd, buf+ts, DIRMAX);
+		if(n <= 0)
+			break;
+		ts += n;
+	}
+	if(ts > 0)
+		ts = dirpackage(buf, ts, d);
+	free(buf);
+	if(ts == 0 && n < 0)
+		return -1;
+	return ts;
+}
\ No newline at end of file
--- /dev/null
+++ b/kern/getwd.c
@@ -1,0 +1,10 @@
+#include <u.h>
+#include <libc.h>
+
+#undef getwd
+
+char*
+sysgetwd(char *s, int ns)
+{
+	return getcwd(s, ns);
+}
--- /dev/null
+++ b/kern/notify.c
@@ -1,0 +1,268 @@
+/*
+ * Signal handling for Plan 9 programs.
+ * We stubbornly use the strings from Plan 9 instead
+ * of the enumerated Unix constants.
+ * There are some weird translations.  In particular,
+ * a "kill" note is the same as SIGTERM in Unix.
+ * There is no equivalent note to Unix's SIGKILL, since
+ * it's not a deliverable signal anyway.
+ *
+ * We do not handle SIGABRT or SIGSEGV, mainly because
+ * the thread library queues its notes for later, and we want
+ * to dump core with the state at time of delivery.
+ *
+ * We have to add some extra entry points to provide the
+ * ability to tweak which signals are deliverable and which
+ * are acted upon.  Notifydisable and notifyenable play with
+ * the process signal mask.  Notifyignore enables the signal
+ * but will not call notifyf when it comes in.  This is occasionally
+ * useful.
+ */
+
+#include <u.h>
+#include <signal.h>
+#include <libc.h>
+
+extern char *_p9sigstr(int, char*);
+extern int _p9strsig(char*);
+
+typedef struct Sig Sig;
+struct Sig
+{
+	int sig;			/* signal number */
+	int flags;
+};
+
+enum
+{
+	Restart = 1<<0,
+	Ignore = 1<<1,
+	NoNotify = 1<<2,
+};
+
+static Sig sigs[] = {
+	SIGHUP,		0,
+	SIGINT,		0,
+	SIGQUIT,		0,
+	SIGILL,		0,
+	SIGTRAP,		0,
+/*	SIGABRT, 		0, 	*/
+#ifdef SIGEMT
+	SIGEMT,		0,
+#endif
+	SIGFPE,		0,
+	SIGBUS,		0,
+/*	SIGSEGV, 		0, 	*/
+	SIGCHLD,		Restart|Ignore,
+	SIGSYS,		0,
+	SIGPIPE,		Ignore,
+	SIGALRM,		0,
+	SIGTERM,		0,
+	SIGTSTP,		Restart|Ignore|NoNotify,
+/*	SIGTTIN,		Restart|Ignore, */
+/*	SIGTTOU,		Restart|Ignore, */
+	SIGXCPU,		0,
+	SIGXFSZ,		0,
+	SIGVTALRM,	0,
+	SIGUSR1,		0,
+	SIGUSR2,		0,
+#ifdef SIGWINCH
+	SIGWINCH,	Restart|Ignore|NoNotify,
+#endif
+#ifdef SIGINFO
+	SIGINFO,		Restart|Ignore|NoNotify,
+#endif
+};
+
+static Sig*
+findsig(int s)
+{
+	int i;
+
+	for(i=0; i<nelem(sigs); i++)
+		if(sigs[i].sig == s)
+			return &sigs[i];
+	return nil;
+}
+
+// for now
+typedef long p9jmp_buf[sizeof(sigjmp_buf)/(sizeof(long))];
+typedef struct Jmp Jmp;
+struct Jmp
+{
+	p9jmp_buf b;
+};
+
+static Jmp onejmp;
+
+static Jmp*
+getonejmp(void)
+{
+	return &onejmp;
+}
+
+Jmp *(*_notejmpbuf)(void) = getonejmp;
+static void noteinit(void);
+
+/*
+ * Actual signal handler.
+ */
+
+static void (*notifyf)(void*, char*);	/* Plan 9 handler */
+
+static void
+signotify(int sig)
+{
+	char tmp[64];
+	Jmp *j;
+	Sig *s;
+
+	j = (*_notejmpbuf)();
+	switch(sigsetjmp((void*)j->b, 1)){
+	case 0:
+		if(notifyf)
+		// probaby not p9sigstr
+			(*notifyf)(nil, _p9sigstr(sig, tmp));
+		/* fall through */
+	case 1:	/* noted(NDFLT) */
+		if(0)print("DEFAULT %d\n", sig);
+		s = findsig(sig);
+		if(s && (s->flags&Ignore))
+			return;
+		signal(sig, SIG_DFL);
+		raise(sig);
+		_exit(1);
+	case 2:	/* noted(NCONT) */
+		if(0)print("HANDLED %d\n", sig);
+		return;
+	}
+}
+
+static void
+signonotify(int sig)
+{
+	USED(sig);
+}
+
+int
+noted(int v)
+{
+	siglongjmp((*_notejmpbuf)()->b, v==NCONT ? 2 : 1);
+	abort();
+	return 0;
+}
+
+int
+notify(void (*f)(void*, char*))
+{
+	static int init;
+
+	notifyf = f;
+	if(!init){
+		init = 1;
+		noteinit();
+	}
+	return 0;
+}
+
+/*
+ * Nonsense about enabling and disabling signals.
+ */
+typedef void Sighandler(int);
+static Sighandler*
+handler(int s)
+{
+	struct sigaction sa;
+
+	sigaction(s, nil, &sa);
+	return sa.sa_handler;
+}
+
+static int
+notesetenable(int sig, int enabled)
+{
+	sigset_t mask, omask;
+
+	if(sig == 0)
+		return -1;
+
+	sigemptyset(&mask);
+	sigaddset(&mask, sig);
+	sigprocmask(enabled ? SIG_UNBLOCK : SIG_BLOCK, &mask, &omask);
+	return !sigismember(&omask, sig);
+}
+
+int
+noteenable(char *msg)
+{
+	return notesetenable(_p9strsig(msg), 1);
+}
+
+int
+notedisable(char *msg)
+{
+	return notesetenable(_p9strsig(msg), 0);
+}
+
+static int
+notifyseton(int s, int on)
+{
+	Sig *sig;
+	struct sigaction sa, osa;
+
+	sig = findsig(s);
+	if(sig == nil)
+		return -1;
+	memset(&sa, 0, sizeof sa);
+	sa.sa_handler = on ? signotify : signonotify;
+	if(sig->flags&Restart)
+		sa.sa_flags |= SA_RESTART;
+
+	/*
+	 * We can't allow signals within signals because there's
+	 * only one jump buffer.
+	 */
+	sigfillset(&sa.sa_mask);
+
+	/*
+	 * Install handler.
+	 */
+	sigaction(sig->sig, &sa, &osa);
+	return osa.sa_handler == signotify;
+}
+
+int
+notifyon(char *msg)
+{
+	return notifyseton(_p9strsig(msg), 1);
+}
+
+int
+notifyoff(char *msg)
+{
+	return notifyseton(_p9strsig(msg), 0);
+}
+
+/*
+ * Initialization follows sigs table.
+ */
+static void
+noteinit(void)
+{
+	int i;
+	Sig *sig;
+
+	for(i=0; i<nelem(sigs); i++){
+		sig = &sigs[i];
+		/*
+		 * If someone has already installed a handler,
+		 * It's probably some ld preload nonsense,
+		 * like pct (a SIGVTALRM-based profiler).
+		 * Or maybe someone has already called notifyon/notifyoff.
+		 * Leave it alone.
+		 */
+		if(handler(sig->sig) != SIG_DFL)
+			continue;
+		notifyseton(sig->sig, !(sig->flags&NoNotify));
+	}
+}
--- /dev/null
+++ b/kern/postnote.c
@@ -1,0 +1,35 @@
+#include <u.h>
+#include <libc.h>
+
+#include <signal.h>
+
+extern int _p9strsig(char*);
+
+int
+postnote(int who, int pid, char *msg)
+{
+	int sig;
+
+	sig = _p9strsig(msg);
+	if(sig == 0){
+		werrstr("unknown note");
+		return -1;
+	}
+
+	if(pid <= 0){
+		werrstr("bad pid in postnote");
+		return -1;
+	}
+
+	switch(who){
+	default:
+		werrstr("bad who in postnote");
+		return -1;
+	case PNPROC:
+		return kill(pid, sig);
+	case PNGROUP:
+		if((pid = getpgid(pid)) < 0)
+			return -1;
+		return killpg(pid, sig);
+	}
+}
--- /dev/null
+++ b/kern/rfork.c
@@ -1,0 +1,127 @@
+#include <u.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <libc.h>
+#undef rfork
+
+static void
+nop(int x)
+{
+	USED(x);
+}
+
+int
+sysrfork(int flags)
+{
+	int pid, status;
+	int p[2];
+	int n;
+	char buf[128], *q;
+	extern char **environ;
+	struct sigaction oldchld;
+
+	memset(&oldchld, 0, sizeof oldchld);
+
+	if((flags&(RFPROC|RFFDG|RFMEM)) == (RFPROC|RFFDG)){
+		/* check other flags before we commit */
+		flags &= ~(RFPROC|RFFDG|RFENVG);
+		n = (flags & ~(RFNOTEG|RFNAMEG|RFNOWAIT|RFCENVG));
+		if(n){
+			werrstr("unknown flags %08ux in rfork", n);
+			return -1;
+		}
+		if(flags&RFNOWAIT){
+			sigaction(SIGCHLD, nil, &oldchld);
+			signal(SIGCHLD, nop);
+			if(pipe(p) < 0)
+				return -1;
+		}
+		pid = fork();
+		if(pid == -1)
+			return -1;
+		if(flags&RFNOWAIT){
+			flags &= ~RFNOWAIT;
+			if(pid){
+				/*
+				 * Parent - wait for child to fork wait-free child.
+				 * Then read pid from pipe.  Assume pipe buffer can absorb the write.
+				 */
+				close(p[1]);
+				status = 0;
+				if(wait4(pid, &status, 0, 0) < 0){
+					werrstr("pipe dance - wait4 - %r");
+					close(p[0]);
+					return -1;
+				}
+				n = readn(p[0], buf, sizeof buf-1);
+				close(p[0]);
+				if(!WIFEXITED(status) || WEXITSTATUS(status)!=0 || n <= 0){
+					if(!WIFEXITED(status))
+						werrstr("pipe dance - !exited 0x%ux", status);
+					else if(WEXITSTATUS(status) != 0)
+						werrstr("pipe dance - non-zero status 0x%ux", status);
+					else if(n < 0)
+						werrstr("pipe dance - pipe read error - %r");
+					else if(n == 0)
+						werrstr("pipe dance - pipe read eof");
+					else
+						werrstr("pipe dance - unknown failure");
+					return -1;
+				}
+				buf[n] = 0;
+				if(buf[0] == 'x'){
+					werrstr("%s", buf+2);
+					return -1;
+				}
+				pid = strtol(buf, &q, 0);
+			}else{
+				/*
+				 * Child - fork a new child whose wait message can't
+				 * get back to the parent because we're going to exit!
+				 */
+				signal(SIGCHLD, SIG_IGN);
+				close(p[0]);
+				pid = fork();
+				if(pid){
+					/* Child parent - send status over pipe and exit. */
+					if(pid > 0)
+						fprint(p[1], "%d", pid);
+					else
+						fprint(p[1], "x %r");
+					close(p[1]);
+					_exit(0);
+				}else{
+					/* Child child - close pipe. */
+					close(p[1]);
+				}
+			}
+			sigaction(SIGCHLD, &oldchld, nil);
+		}
+		if(pid != 0)
+			return pid;
+		if(flags&RFCENVG)
+			if(environ)
+				*environ = nil;
+	}
+	if(flags&RFPROC){
+		werrstr("cannot use rfork for shared memory -- use libthread");
+		return -1;
+	}
+	if(flags&RFNAMEG){
+		/* XXX set $NAMESPACE to a new directory */
+		flags &= ~RFNAMEG;
+	}
+	if(flags&RFNOTEG){
+		setpgid(0, getpid());
+		flags &= ~RFNOTEG;
+	}
+	if(flags&RFNOWAIT){
+		werrstr("cannot use RFNOWAIT without RFPROC");
+		return -1;
+	}
+	if(flags){
+		werrstr("unknown flags %08ux in rfork", flags);
+		return -1;
+	}
+	return 0;
+}
--- a/kern/sysfile.c
+++ b/kern/sysfile.c
@@ -8,6 +8,7 @@
 #undef open
 #undef mount
 #undef read
+#undef fd2path
 #undef write
 #undef seek
 #undef stat
@@ -183,8 +184,8 @@
 	return o;
 }
 
-long
-_sysfd2path(int fd, char *buf, uint nbuf)
+int
+sysfd2path(int fd, char *buf, int nbuf)
 {
 	Chan *c;
 
--- /dev/null
+++ b/kern/time.c
@@ -1,0 +1,49 @@
+#include <u.h>
+#include <sys/time.h>
+#include <time.h>
+#include <sys/resource.h>
+#include <libc.h>
+
+#undef times
+#undef cputimes
+#undef time
+
+long
+systimes(long *t)
+{
+	struct rusage ru, cru;
+
+	if(getrusage(0, &ru) < 0 || getrusage(-1, &cru) < 0)
+		return -1;
+
+	t[0] = ru.ru_utime.tv_sec*1000 + ru.ru_utime.tv_usec/1000;
+	t[1] = ru.ru_stime.tv_sec*1000 + ru.ru_stime.tv_usec/1000;
+	t[2] = cru.ru_utime.tv_sec*1000 + cru.ru_utime.tv_usec/1000;
+	t[3] = cru.ru_stime.tv_sec*1000 + cru.ru_stime.tv_usec/1000;
+
+	/* BUG */
+	return t[0]+t[1]+t[2]+t[3];
+}
+
+double
+syscputime(void)
+{
+	long t[4];
+	double d;
+
+	if(systimes(t) < 0)
+		return -1.0;
+
+	d = (double)t[0]+(double)t[1]+(double)t[2]+(double)t[3];
+	return d/1000.0;
+}
+
+long
+systime(long *tt)
+{
+	long t;
+	t = time(0);
+	if(tt)
+		*tt = t;
+	return t;
+}
--- /dev/null
+++ b/kern/wait.c
@@ -1,0 +1,53 @@
+#include <u.h>
+#include <libc.h>
+
+static Waitmsg*
+_wait(int n, char *buf)
+{
+	int l;
+	char *fld[5];
+	Waitmsg *w;
+
+	if(n <= 0)
+		return nil;
+	buf[n] = '\0';
+	if(tokenize(buf, fld, nelem(fld)) != nelem(fld)){
+		werrstr("couldn't parse wait message");
+		return nil;
+	}
+	l = strlen(fld[4])+1;
+	w = malloc(sizeof(Waitmsg)+l);
+	if(w == nil)
+		return nil;
+	w->pid = atoi(fld[0]);
+	w->dur[0] = atoi(fld[1]);
+	w->dur[1] = atoi(fld[2]);
+	w->dur[2] = atoi(fld[3]);
+	w->msg = (char*)&w[1];
+	memmove(w->msg, fld[4], l);
+	return w;
+}
+
+Waitmsg*
+syswait(void)
+{
+	char buf[256];
+
+	return _wait(await(buf, sizeof buf-1), buf);
+}
+
+Waitmsg*
+waitnohang(void)
+{
+	char buf[256];
+
+	return _wait(awaitnohang(buf, sizeof buf-1), buf);
+}
+
+Waitmsg*
+waitfor(int pid)
+{
+	char buf[256];
+
+	return _wait(awaitfor(pid, buf, sizeof buf-1), buf);
+}
--- a/main.c
+++ b/main.c
@@ -44,8 +44,6 @@
 int
 main(int argc, char **argv)
 {
-	int fd;
-	char s[1024];
 	extern ulong kerndate;
 	kerndate = seconds();
 	eve = getuser();
@@ -65,30 +63,18 @@
 		panic("bind #c: %r");
 	if(bind("#e", "/env", MREPL|MCREATE) < 0)
 		panic("bind #e: %r");
-	if(bind("#I", "/net", MBEFORE) < 0)
-		panic("bind #I: %r");
 	if(bind("#U", "/root", MREPL) < 0)
 		panic("bind #U: %r");
-	bind("#A", "/dev", MAFTER);
-	bind("#N", "/dev", MAFTER);
-	bind("#C", "/", MAFTER);
-
     if(bind("/root", "/", MAFTER) < 0)
 		panic("bind /root: %r");
 
-	if((fd = open("/dev/cons", OREAD)) < 0)
-		panic("open stdin: %r");
-	if(read(fd, s, sizeof s) <= 0)
-		panic("read: %r");
-	close(fd);
-
+	// We get service=cpu, change to =unix
 	char *cmd[] = {
-		"/bin/rc",
-		"-c",
-		s
+		"drawcpu",
+		"-c"
+		". <{n=`{read} && ! ~ $#n 0 && read -c $n} >[2=1]"	
 	};
 
-	printf("%s %s %s\n", cmd[0], cmd[1], cmd[2]);
 	runcommand(3, cmd);
 	_exit(0);
 }
--- a/rc/drawcpu.c
+++ b/rc/drawcpu.c
@@ -3,7 +3,6 @@
  *	By convention, exported routines herein have names beginning with an
  *	upper case letter.
  */
-
 #include "rc.h"
 #include "exec.h"
 #include "io.h"
@@ -10,17 +9,12 @@
 #include "fns.h"
 #include "getflags.h"
 
-#include <signal.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <sys/wait.h>
-
+static void execrfork(void);
 static void execfinit(void);
 
 builtin Builtin[] = {
 	"cd",		execcd,
-	"whatis",	execwhatis,
+	"whatis",	execwhatis, 
 	"eval",		execeval,
 	"exec",		execexec,	/* but with popword first */
 	"exit",		execexit,
@@ -29,15 +23,31 @@
 	".",		execdot,
 	"flag",		execflag,
 	"finit",	execfinit,
+	"rfork",	execrfork,
 	0
 };
 
-// TODO: PREFIX "/lib/rcmain"
-char Rcmain[]="./rc/rcmain.unix";
+char Rcmain[]="/usr/local/lib/rcmain";
 char Fdprefix[]="/dev/fd/";
 
-char *Signame[NSIG];
+char *Signame[] = {
+	"sigexit",	"sighup",	"sigint",	"sigquit",
+	"sigalrm",	"sigkill",	"sigfpe",	"sigterm",
+	0
+};
 
+static char *syssigname[] = {
+	"exit",		/* can't happen */
+	"hangup",
+	"interrupt",
+	"quit",		/* can't happen */
+	"alarm",
+	"kill",
+	"sys: fp: ",
+	"term",
+	0
+};
+
 /*
  * finit could be removed but is kept for
  * backwards compatibility, see: rcmain.plan9
@@ -53,6 +63,61 @@
 	runq->lex->qflag = 1;
 }
 
+static void
+execrfork(void)
+{
+	int arg;
+	char *s;
+
+	switch(count(runq->argv->words)){
+	case 1:
+		arg = RFENVG|RFNAMEG|RFNOTEG;
+		break;
+	case 2:
+		arg = 0;
+		for(s = runq->argv->words->next->word;*s;s++) switch(*s){
+		default:
+			goto Usage;
+		case 'n':
+			arg|=RFNAMEG;  break;
+		case 'N':
+			arg|=RFCNAMEG;
+			break;
+		case 'm':
+			/*arg|=RFNOMNT;*/  break;
+		case 'e':
+			arg|=RFENVG;   break;
+		case 'E':
+			arg|=RFCENVG;  break;
+		case 's':
+			arg|=RFNOTEG;  break;
+		case 'f':
+			arg|=RFFDG;    break;
+		case 'F':
+			arg|=RFCFDG;   break;
+		}
+		break;
+	default:
+	Usage:
+		pfmt(err, "Usage: %s [fnesFNEm]\n", runq->argv->words->word);
+		setstatus("rfork usage");
+		poplist();
+		return;
+	}
+	if(rfork(arg)==-1){
+		pfmt(err, "%s: %s failed\n", argv0, runq->argv->words->word);
+		setstatus("rfork failed");
+	} else {
+		if(arg & RFCFDG){
+			redir *rp;
+			for(rp = runq->redir; rp; rp = rp->next)
+				rp->type = 0;
+		}
+		setstatus("");
+	}
+	poplist();
+}
+
 char*
 Env(char *name, int fn)
 {
@@ -66,69 +131,74 @@
 void
 Vinit(void)
 {
-	int fd;
-	DIR *dir;
-	struct dirent *ent;
+	int dir, fd, i, n;
+	Dir *ent;
 
-	dir = opendir("/env");
-	if(dir == nil){
+	dir = Open(Env("", 0), 0);
+	if(dir<0){
 		pfmt(err, "%s: can't open: %s\n", argv0, Errstr());
 		return;
 	}
 	for(;;){
-		ent = readdir(dir);
-		if (ent == nil)
+		ent = 0;
+		n = dirread(dir, &ent);
+		if(n <= 0)
 			break;
-		if(ent->d_namlen<=0 || strncmp(ent->d_name, "fn#", 3)==0)
-			continue;
-		if((fd = Open(Env(ent->d_name, 0), 0))>=0){
-			io *f = openiofd(fd);
-			word *w = 0, **wp = &w;
-			char *s;
-			while((s = rstr(f, "")) != 0){
-				*wp = Newword(s, (word*)0);
-				wp = &(*wp)->next;
+		for(i = 0; i<n; i++){
+			if(ent[i].length<=0 || strncmp(ent[i].name, "fn#", 3)==0)
+				continue;
+			if((fd = Open(Env(ent[i].name, 0), 0))>=0){
+				io *f = openiofd(fd);
+				word *w = 0, **wp = &w;
+				char *s;
+				while((s = rstr(f, "")) != 0){
+					*wp = Newword(s, (word*)0);
+					wp = &(*wp)->next;
+				}
+				closeio(f);
+				setvar(ent[i].name, w);
+				vlook(ent[i].name)->changed = 0;
 			}
-			closeio(f);
-			setvar(ent->d_name, w);
-			vlook(ent->d_name)->changed = 0;
 		}
 		free(ent);
 	}
-	closedir(dir);
+	Close(dir);
 }
 
 char*
 Errstr(void)
 {
-	return strerror(errno);
+	static char err[ERRMAX];
+	rerrstr(err, sizeof err);
+	return err;
 }
 
-/* Can we do this inside the kernel? */
 int
 Waitfor(int pid)
 {
 	thread *p;
-	char num[12];
-	int wpid, status;
+	Waitmsg *w;
 
 	if(pid >= 0 && !havewaitpid(pid))
 		return 0;
-	while((wpid = wait(&status))!=-1){
-		delwaitpid(wpid);
-		inttoascii(num, WIFSIGNALED(status)?WTERMSIG(status)+1000:WEXITSTATUS(status));
-		if(wpid==pid){
-			setstatus(num);
+
+	while((w = wait()) != nil){
+		delwaitpid(w->pid);
+		if(w->pid==pid){
+			setstatus(w->msg);
+			free(w);
 			return 0;
 		}
 		for(p = runq->ret;p;p = p->ret)
-			if(p->pid==wpid){
+			if(p->pid==w->pid){
 				p->pid=-1;
-				p->status = estrdup(num);
+				p->status = estrdup(w->msg);
 				break;
 			}
+		free(w);
 	}
-	if(errno==EINTR) return -1;
+
+	if(strcmp(Errstr(), "interrupted")==0) return -1;
 	return 0;
 }
 
@@ -192,8 +262,7 @@
 void
 Exec(char **argv)
 {
-	// TODO: execve after loading env to string
-	execv(argv[0], argv+1);
+	execvp(argv[0], argv+1);
 }
 
 int
@@ -200,87 +269,105 @@
 Fork(void)
 {
 	Updenv();
-	return fork();
+	// TODO: Tie into the rendezvous so we can block 'em in rfork
+	return rfork(RFPROC|RFFDG/*|RFREND*/);
 }
 
+
+typedef struct readdir readdir;
+struct readdir {
+	Dir	*dbuf;
+	int	i, n;
+	int	fd;
+};
+
 void*
 Opendir(char *name)
 {
-	return opendir(name);
+	readdir *rd;
+	int fd;
+	if((fd = Open(name, 0))<0)
+		return 0;
+	rd = new(readdir);
+	rd->dbuf = 0;
+	rd->i = 0;
+	rd->n = 0;
+	rd->fd = fd;
+	return rd;
 }
 
+static int
+trimdirs(Dir *d, int nd)
+{
+	int r, w;
+
+	for(r=w=0; r<nd; r++)
+		if(d[r].mode&DMDIR)
+			d[w++] = d[r];
+	return w;
+}
+
 char*
 Readdir(void *arg, int onlydirs)
 {
-	DIR *rd = arg;
-	struct dirent *ent = readdir(rd);
-	if(ent == NULL)
+	readdir *rd = arg;
+	int n;
+Again:
+	if(rd->i>=rd->n){	/* read */
+		free(rd->dbuf);
+		rd->dbuf = 0;
+		n = dirread(rd->fd, &rd->dbuf);
+		if(n>0){
+			if(onlydirs){
+				n = trimdirs(rd->dbuf, n);
+				if(n == 0)
+					goto Again;
+			}	
+			rd->n = n;
+		}else
+			rd->n = 0;
+		rd->i = 0;
+	}
+	if(rd->i>=rd->n)
 		return 0;
-	return ent->d_name;
+	return rd->dbuf[rd->i++].name;
 }
 
 void
 Closedir(void *arg)
 {
-	DIR *rd = arg;
-	closedir(rd);
+	readdir *rd = arg;
+	Close(rd->fd);
+	free(rd->dbuf);
+	free(rd);
 }
 
+static int interrupted = 0;
+
 static void
-sighandler(int sig)
+notifyf(void* u, char *s)
 {
-	trap[sig]++;
-	ntrap++;
+	int i;
+
+	USED(u);
+	for(i = 0;syssigname[i];i++) if(strncmp(s, syssigname[i], strlen(syssigname[i]))==0){
+		if(strncmp(s, "sys: ", 5)!=0) interrupted = 1;
+		goto Out;
+	}
+	noted(NDFLT);
+	return;
+Out:
+	if(strcmp(s, "interrupt")!=0 || trap[i]==0){
+		trap[i]++;
+		ntrap++;
+	}
+	noted(NCONT);
 }
 
-// TODO: Use kernel sighandling 
 void
 Trapinit(void)
 {
-	int i;
-
-	Signame[0] = "sigexit";
-
-#ifdef SIGINT
-	Signame[SIGINT] = "sigint";
-#endif
-#ifdef SIGTERM
-	Signame[SIGTERM] = "sigterm";
-#endif
-#ifdef SIGHUP
-	Signame[SIGHUP] = "sighup";
-#endif
-#ifdef SIGQUIT
-	Signame[SIGQUIT] = "sigquit";
-#endif
-#ifdef SIGPIPE
-	Signame[SIGPIPE] = "sigpipe";
-#endif
-#ifdef SIGUSR1
-	Signame[SIGUSR1] = "sigusr1";
-#endif
-#ifdef SIGUSR2
-	Signame[SIGUSR2] = "sigusr2";
-#endif
-#ifdef SIGBUS
-	Signame[SIGBUS] = "sigbus";
-#endif
-#ifdef SIGWINCH
-	Signame[SIGWINCH] = "sigwinch";
-#endif
-
-	for(i=1; i<NSIG; i++) if(Signame[i]){
-#ifdef SA_RESTART
-		struct sigaction a;
-
-		sigaction(i, NULL, &a);
-		a.sa_flags &= ~SA_RESTART;
-		a.sa_handler = sighandler;
-		sigaction(i, &a, NULL);
-#else
-		signal(i, sighandler);
-#endif
-	}
+	notify(notifyf);
 }
 
 long
@@ -356,7 +443,7 @@
 void
 Noerror(void)
 {
-	errno = 0;
+	interrupted = 0;
 }
 
 int
@@ -363,6 +450,12 @@
 Isatty(int fd)
 {
 	return isatty(fd);
+	/*
+	char buf[64];
+	if(fd2path(fd, buf, sizeof buf) != 0)
+		return 0;
+	return strlen(buf) >= 9 && strcmp(buf+strlen(buf)-9, "/dev/cons") == 0;
+	*/
 }
 
 void
@@ -371,9 +464,12 @@
 	abort();
 }
 
+static int newwdir;
+
 int
 Chdir(char *dir)
 {
+	newwdir = 1;
 	return chdir(dir);
 }
 
@@ -382,4 +478,15 @@
 {
 	pstr(err, s);
 	flushio(err);
+
+	if(newwdir){
+		char dir[4096];
+		int fd;
+		if((fd=Creat("/dev/wdir"))>=0){
+			getwd(dir, sizeof(dir));
+			Write(fd, dir, strlen(dir));
+			Close(fd);
+		}
+		newwdir = 0;
+	}
 }
--- a/rc/exec.c
+++ b/rc/exec.c
@@ -243,9 +243,11 @@
 	else if(flag['i']==0 && argc==1 && Isatty(0)) flag['i'] = flagset;
 	if(flag['m']) rcmain = flag['m'][0];
 
+	/* Open stdout so next desc is stderr */
 	fd = open("/dev/cons", ORDWR);
-	err = open("/dev/cons", OWRITE);
+	err = openiofd(open("/dev/cons", OWRITE));
 	close(fd);
+
 	kinit();
 	Trapinit();
 	Vinit();
@@ -275,7 +277,6 @@
 	bootstrap[18].f = Xexit;
 	bootstrap[19].f = 0;
 	start(bootstrap, 2, (var*)0, (redir*)0);
-
 	/* prime bootstrap argv */
 	pushlist();
 	for(i = argc-1;i!=0;--i) pushword(argv[i]);
@@ -287,6 +288,7 @@
 			dotrap();
 	}
 }
+
 /*
  * Opcode routines
  * Arguments on stack (...)
--- a/rc/io.c
+++ b/rc/io.c
@@ -148,10 +148,10 @@
 pptr(io *f, void *p)
 {
 	static char hex[] = "0123456789ABCDEF";
-	unsigned long v;
+	uvlong v;
 	int n;
 
-	v = (unsigned long)p;
+	v = (uvlong)p;
 	if(sizeof(v) == sizeof(p) && v>>32)
 		for(n = 60;n>=32;n-=4) pchr(f, hex[(v>>n)&0xF]);
 	for(n = 28;n>=0;n-=4) pchr(f, hex[(v>>n)&0xF]);
--- a/rc/rc.h
+++ b/rc/rc.h
@@ -1,9 +1,3 @@
-/*
- * Plan9 is defined for plan 9
- * otherwise its UNIX.
- * Please don't litter the code with ifdefs.  The three below (and one in
- * getflags) should be enough.
- */
 
 #include <u.h>
 #include <libc.h>