Browse Source

Plan 9 from Bell Labs 2009-04-30

David du Colombier 15 years ago
parent
commit
774c4323a9

+ 4 - 1
sys/man/8/plan9.ini

@@ -242,7 +242,10 @@ in such cases,
 depending on how NE2000 compatible they are.
 .TP
 .B ne2000
-Not software configurable. 16-bit card.
+Not software configurable iff ISA;
+PCI clones or supersets are software configurable;
+includes the Realtek 8029 clone used by Parallels.
+16-bit card.
 Defaults are
 .EX
 	port=0x300 irq=2 mem=0x04000 size=0x4000

+ 3 - 2
sys/man/8/venti

@@ -385,8 +385,9 @@ queue writes in memory
 (default is not to queue)
 .TP
 .BI webroot " dir
-directory tree containing files for HTTP server
-to consult for unrecognized URLs
+directory tree containing files for
+.IR venti 's
+internal HTTP server to consult for unrecognized URLs
 .PD
 .PP
 The units for the various cache sizes above can be specified by appending a

+ 5 - 0
sys/src/9/pc/ethervgbe.c

@@ -524,6 +524,11 @@ vgberxeof(Ether* edev)
 			block->wp = block->rp + length;
 
 			ctlr->stats.rx++;
+			/*
+			 * the packet actually *is* from the wire, but
+			 * we're maintaining a private array of Blocks,
+			 * so can't have etheriq free block.
+			 */
 			etheriq(edev, block, 0);
 		}
 		else

+ 13 - 14
sys/src/cmd/cec/LICENSE

@@ -1,6 +1,6 @@
 The FreeBSD License
 
-Copyright 2006 Coraid, Inc. All rights reserved.
+Copyright © 2006-8 Coraid, Inc. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -14,19 +14,18 @@ met:
       disclaimer in the documentation and/or other materials provided
       with the distribution.
 
-THIS SOFTWARE IS PROVIDED BY THE BRANTLEY COILE COMPANY ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE BRANTLEY COILE COMPANY
-OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+This software is provided by Coraid “as is” and any express or
+implied warranties, including, but not limited to, the implied
+warranties of merchantability and fitness for a particular purpose are
+disclaimed.  In no event shall Coraid or contributors be liable for
+any direct, indirect, incidental, special, exemplary, or consequential
+damages (including, but not limited to, procurement of substitute
+goods or services; loss of use, data, or profits; or business
+interruption) however caused and on any theory of liability, whether
+in contract, strict liability, or tort (including negligence or
+otherwise) arising in any way out of the use of this software, even if
+advised of the possibility of such damage.
 
 The views and conclusions contained in the software and documentation
 are those of the authors and should not be interpreted as representing
-official policies, either expressed or implied, of the Brantley Coile
-Company.
+official policies, either expressed or implied, of Coraid.

+ 2 - 2
sys/src/cmd/cec/Protocol

@@ -68,7 +68,7 @@ specific.
 3.  Initializing a connection. Tinit[abc]
 
 A connection is initialized by the following conversation: In addition
-to the fields set for the Tdiscover packet, he client sends a packet
+to the fields set for the Tdiscover packet, the client sends a packet
 of type Tinita with the conntag of its choice.  The server responds to
 Tinita with a packet of type Tinitb.  And finally the client sents a
 Tinitc packet back to the server, completing the connection.
@@ -78,7 +78,7 @@ Tinitc packet back to the server, completing the connection.
 Data is sent from the client to the console server with the Tdata packet.
 The seq field is incremented for each data packet sent.  Thus data packets
 may be transmitted if lost.  The data is whatever data the client has to
-send to the server, up to 1500 bytes.  Typically, however, each keystroke
+send to the server, up to 255 bytes.  Typically, however, each keystroke
 is sent in a seperate packet.  The len field is the length of the
 data.
 

+ 210 - 101
sys/src/cmd/cec/cec.c

@@ -1,16 +1,16 @@
 /*
- * Copyright © Coraid, Inc. 2006, 2007.  All Rights Reserved.
- * ethernet console for Coraid storage products.
- *  simple command line version.
+ * cec — coraid ethernet console
+ * Copyright © Coraid, Inc. 2006-2008.
+ * All Rights Reserved.
  */
 #include <u.h>
 #include <libc.h>
-#include <ip.h>	/* really! */
+#include <ip.h>		/* really! */
 #include <ctype.h>
 #include "cec.h"
 
 enum {
-	Tinita = 0,
+	Tinita		= 0,
 	Tinitb,
 	Tinitc,
 	Tdata,
@@ -19,40 +19,89 @@ enum {
 	Toffer,
 	Treset,
 
-	HDRSIZ = 18,
-	Eaddrlen = 6,
+	Hdrsz		= 18,
+	Eaddrlen	= 6,
 };
 
-typedef struct Shelf Shelf;
-
-struct Shelf {
+typedef struct{
 	uchar	ea[Eaddrlen];
-	int	shelfno;
-	char	*str;
-};
+	int	major;
+	char	name[28];
+} Shelf;
 
-void 	conn(int);
-void	exits0(char *);
+int 	conn(int);
 void 	gettingkilled(int);
 int 	pickone(void);
 void 	probe(void);
 void	sethdr(Pkt *, int);
 int	shelfidx(void);
 
-extern int errno;
-extern int fd;			/* set in netopen */
-
+Shelf	*con;
 Shelf	tab[1000];
+
+char	*host;
+char	*srv;
+char	*svc;
+
+char	pflag;
+
 int	ntab;
-uchar	contag;
 int	shelf = -1;
-Shelf	*connp;
-char 	esc = '';
+
+uchar	bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+uchar	contag;
+uchar 	esc = '';
+uchar	ea[Eaddrlen];
+uchar	unsetea[Eaddrlen];
+
+extern 	int fd;		/* set in netopen */
+
+void
+post(char *srv, int fd)
+{
+	char buf[32];
+	int f;
+
+	if((f = create(srv, OWRITE, 0666)) == -1)
+		sysfatal("create %s: %r", srv);
+	snprint(buf, sizeof buf, "%d", fd);
+	if(write(f, buf, strlen(buf)) != strlen(buf))
+		sysfatal("write %s: %r", srv);
+	close(f);
+}
+
+void
+dosrv(char *s)
+{
+	int p[2];
+
+	if(pipe(p) < 0)
+		sysfatal("pipe: %r");
+	if (srv[0] != '/')
+		svc = smprint("/srv/%s", s);
+	else
+		svc = smprint("%s", s);
+	post(svc, p[0]);
+	close(p[0]);
+	dup(p[1], 0);
+	dup(p[1], 1);
+
+	switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){
+	case -1:
+		sysfatal("fork: %r");
+	case 0:
+		break;
+	default:
+		exits("");
+	}
+	close(2);
+}
 
 void
 usage(void)
 {
-	fprint(2, "usage: cec [-d] [-e esc] [-s shelf] interface\n");
+	fprint(2, "usage: cec [-dp] [-c esc] [-e ea] [-h host] [-s shelf] "
+		"[-S srv] interface\n");
 	exits0("usage");
 }
 
@@ -64,32 +113,54 @@ catch(void*, char *note)
 	noted(NDFLT);
 }
 
+int
+nilea(uchar *ea)
+{
+	return memcmp(ea, unsetea, Eaddrlen) == 0;
+}
+
 void
 main(int argc, char **argv)
 {
 	int r, n;
 
 	ARGBEGIN{
+	case 'S':
+		srv = EARGF(usage());
+		break;
+	case 'c':
+		esc = tolower(*(EARGF(usage()))) - 'a' + 1;
+		if(esc == 0 || esc >= ' ')
+			usage();
+		break;
 	case 'd':
 		debug++;
 		break;
-	case 's':
-		shelf = atoi(EARGF(usage()));
-		break;
 	case 'e':
-		esc = toupper(*(EARGF(usage()))) - 'A' + 1;
-		if(esc <= 0 || esc >= ' ')
+		if(parseether(ea, EARGF(usage())) == -1)
 			usage();
+		pflag = 1;
+		break;
+	case 'h':
+		host = EARGF(usage());
+		break;
+	case 'p':
+		pflag = 1;
+		break;
+	case 's':
+		shelf = atoi(EARGF(usage()));
 		break;
 	default:
 		usage();
 	}ARGEND
-	if(debug)
-		fprint(2, "debug is on\n");
-	if(argc != 1)
+	if(argc == 0)
+		*argv = "/net/ether0";
+	else if(argc != 1)
 		usage();
 
 	fmtinstall('E', eipfmt);
+	if(srv != nil)
+		dosrv(srv);
 	r = netopen(*argv);
 	if(r == -1){
 		fprint(2, "cec: can't netopen %s\n", *argv);
@@ -99,18 +170,25 @@ main(int argc, char **argv)
 	probe();
 	for(;;){
 		n = 0;
-		if(shelf == -1)
+		if(shelf == -1 && host == 0 && nilea(ea))
 			n = pickone();
 		rawon();
 		conn(n);
 		rawoff();
-		if(shelf != -1)
-			exits0("shelf not found");
+		if(pflag == 0){
+			if(shelf != -1)
+				exits0("shelf not found");
+			if(host)
+				exits0("host not found");
+			if(!nilea(ea))
+				exits0("ea not found");
+		} else if(shelf != -1 || host || !nilea(ea))
+			exits0("");
 	}
 }
 
 void
-timewait(int ms)  /* arrange for a sig_alarm signal after `ms' milliseconds */
+timewait(int ms)
 {
 	alarm(ms);
 }
@@ -151,54 +229,73 @@ ntohs(int h)
 	return p[0] << 8 | p[1];
 }
 
+int
+tcmp(void *a, void *b)
+{
+	Shelf *s, *t;
+	int d;
+
+	s = a;
+	t = b;
+	d = s->major - t->major;
+	if(d == 0)
+		d = strcmp(s->name, t->name);
+	if(d == 0)
+		d = memcmp(s->ea, t->ea, Eaddrlen);
+	return d;
+}
+
 void
 probe(void)
 {
-	int n;
 	char *sh, *other;
-	uchar buf[1500];
+	int n;
 	Pkt q;
 	Shelf *p;
 
-	ntab = 0;
-	memset(buf, 0xff, Eaddrlen);
-	memset(q.dst, 0xff, Eaddrlen);
-	memset(q.src, 0, Eaddrlen);
-	q.etype = htons(Etype);
-	q.type = Tdiscover;
-	q.len = 0;
-	q.conn = 0;
-	q.seq = 0;
-	netsend(&q, 60);
-//	fprint(2, "Probing for shelves ... ");
-	timewait(Iowait);
-	while((n = netget(&q, sizeof q)) >= 0) {
-		if((n <= 0 && didtimeout()) || ntab == nelem(tab))
-			break;
-		if(n < 60 || q.len == 0 || q.type != Toffer)
-			continue;
-		q.data[q.len] = 0;
-		sh = strtok((char *)q.data, " \t");
-		if(sh == nil)
-			continue;
-		if(shelf != -1 && atoi(sh) != shelf)
-			continue;
-		other = strtok(nil, "\x1");
-		p = tab + ntab++;
-		memcpy(p->ea, q.src, Eaddrlen);
-		p->shelfno = atoi(sh);
-		p->str = other? strdup(other): "";
-		if(shelf != -1) {
-			fprint(2, "shelf %d found.\n", shelf);
-			break;
+	do {
+		ntab = 0;
+		memset(q.dst, 0xff, Eaddrlen);
+		memset(q.src, 0, Eaddrlen);
+		q.etype = htons(Etype);
+		q.type = Tdiscover;
+		q.len = 0;
+		q.conn = 0;
+		q.seq = 0;
+		netsend(&q, 60);
+		timewait(Iowait);
+		while((n = netget(&q, sizeof q)) >= 0){
+			if((n <= 0 && didtimeout()) || ntab == nelem(tab))
+				break;
+			if(n < 60 || q.len == 0 || q.type != Toffer)
+				continue;
+			q.data[q.len] = 0;
+			sh = strtok((char *)q.data, " \t");
+			if(sh == nil)
+				continue;
+			if(!nilea(ea) && memcmp(ea, q.src, Eaddrlen) != 0)
+				continue;
+			if(shelf != -1 && atoi(sh) != shelf)
+				continue;
+			other = strtok(nil, "\x1");
+			if(other == 0)
+				other = "";
+			if(host && strcmp(host, other) != 0)
+				continue;
+			p = tab + ntab++;
+			memcpy(p->ea, q.src, Eaddrlen);
+			p->major = atoi(sh);
+			p->name[0] = 0;
+			if(p->name)
+				snprint(p->name, sizeof p->name, "%s", other);
 		}
-	}
-	alarm(0);
-	if(ntab == 0) {
+		alarm(0);
+	} while (ntab == 0 && pflag);
+	if(ntab == 0){
 		fprint(2, "none found.\n");
 		exits0("none found");
 	}
-//	fprint(2, "done.\n");
+	qsort(tab, ntab, sizeof tab[0], tcmp);
 }
 
 void
@@ -207,8 +304,7 @@ showtable(void)
 	int i;
 
 	for(i = 0; i < ntab; i++)
-		print("%2d   %5d %E %s\n", i,
-			tab[i].shelfno, tab[i].ea, tab[i].str);
+		print("%2d   %5d %E %s\n", i, tab[i].major, tab[i].ea, tab[i].name);
 }
 
 int
@@ -231,10 +327,11 @@ pickone(void)
 				break;
 			}
 			if(buf[0] == 'q')
-			/* fall through */
+				/* fall through */
 		case 0:
 		case -1:
 				exits0(0);
+			break;
 		}
 		if(isdigit(buf[0])){
 			buf[n] = 0;
@@ -249,7 +346,7 @@ pickone(void)
 void
 sethdr(Pkt *pp, int type)
 {
-	memmove(pp->dst, connp->ea, Eaddrlen);
+	memmove(pp->dst, con->ea, Eaddrlen);
 	memset(pp->src, 0, Eaddrlen);
 	pp->etype = htons(Etype);
 	pp->type = type;
@@ -266,7 +363,7 @@ ethclose(void)
 	timewait(Iowait);
 	netsend(&msg, 60);
 	alarm(0);
-	connp = 0;
+	con = 0;
 }
 
 int
@@ -297,13 +394,16 @@ char
 escape(void)
 {
 	char buf[64];
+	int r;
 
 	for(;;){
 		fprint(2, ">>> ");
 		buf[0] = '.';
 		rawoff();
-		read(0, buf, sizeof buf-1);
+		r = read(0, buf, sizeof buf - 1);
 		rawon();
+		if(r == -1)
+			exits0("kbd: %r");
 		switch(buf[0]){
 		case 'i':
 		case 'q':
@@ -315,9 +415,9 @@ escape(void)
 }
 
 /*
- * this is a bit too agressive.  it really needs to replace only \n\r with \n.
+ * this is a bit too aggressive.  it really needs to replace only \n\r with \n.
  */
-static uchar crbuf[1514];
+static uchar crbuf[256];
 
 void
 nocrwrite(int fd, uchar *buf, int n)
@@ -325,11 +425,9 @@ nocrwrite(int fd, uchar *buf, int n)
 	int i, j, c;
 
 	j = 0;
-	for(i = 0; i < n; i++){
-		if((c = buf[i]) == '\r')
-			continue;
-		crbuf[j++] = c;
-	}
+	for(i = 0; i < n; i++)
+		if((c = buf[i]) != '\r')
+			crbuf[j++] = c;
 	write(fd, crbuf, j);
 }
 
@@ -339,10 +437,10 @@ doloop(void)
 	int unacked, retries, set[2];
 	uchar c, tseq, rseq;
 	uchar ea[Eaddrlen];
-	Mux * m;
 	Pkt tpk, spk;
+	Mux *m;
 
-	memmove(ea, connp->ea, Eaddrlen);
+	memmove(ea, con->ea, Eaddrlen);
 	retries = 0;
 	unacked = 0;
 	tseq = 0;
@@ -362,9 +460,9 @@ top:
 				muxfree(m);
 				return 0;
 			}
-			netsend(&tpk, HDRSIZ + unacked);
+			netsend(&tpk, Hdrsz + unacked);
 			break;
-		case 0:
+		case Fkbd:
 			c = spk.data[0];
 			if (c == esc) {
 				muxfree(m);
@@ -388,13 +486,14 @@ top:
 			tpk.seq = ++tseq;
 			unacked = spk.len;
 			retries = 2;
-			netsend(&tpk, HDRSIZ + spk.len);
+			netsend(&tpk, Hdrsz + spk.len);
 			break;
-		default:
+		case Fcec:
 			if (memcmp(spk.src, ea, Eaddrlen) != 0 ||
 			    ntohs(spk.etype) != Etype)
 				continue;
-			if (spk.type == Toffer) {
+			if (spk.type == Toffer &&
+			    memcmp(spk.dst, bcast, Eaddrlen) != 0) {
 				muxfree(m);
 				return 1;
 			}
@@ -405,8 +504,6 @@ top:
 				if (spk.seq == rseq)
 					break;
 				nocrwrite(1, spk.data, spk.len);
-				if (0)
-					write(1, spk.data, spk.len);
 				memmove(spk.dst, spk.src, Eaddrlen);
 				memset(spk.src, 0, Eaddrlen);
 				spk.type = Tack;
@@ -422,28 +519,40 @@ top:
 				muxfree(m);
 				return 1;
 			}
+			break;
+		case Ffatal:
+			muxfree(m);
+			fprint(2, "kbd read error\n");
+			exits0("fatal");
 		}
 }
 
-void
+int
 conn(int n)
 {
-	do {
-		if(connp)
+	int r;
+
+	for(;;){
+		if(con)
 			ethclose();
-		connp = &tab[n];
+		con = tab + n;
 		if(ethopen() < 0){
-			fprint(2, "connection failed.\n");
-			return;
+			fprint(2, "connection failed\n");
+			return 0;
 		}
-	} while(doloop());
+		r = doloop();
+		if(r <= 0)
+			return r;
+	}
 }
 
 void
 exits0(char *s)
 {
-	if(connp != nil)
+	if(con != nil)
 		ethclose();
 	rawoff();
+	if(svc != nil)
+		remove(svc);
 	exits(s);
 }

+ 12 - 14
sys/src/cmd/cec/cec.h

@@ -1,5 +1,3 @@
-/* cec.h: definitions for cec */
-
 typedef struct {
 	uchar	dst[6];
 	uchar	src[6];
@@ -14,27 +12,27 @@ typedef struct {
 enum {
 	Fkbd,
 	Fcec,
-	Floc,
+	Ffatal,
 };
 
 typedef struct Mux Mux;
 #pragma incomplete Mux;
 
-enum {
+enum{
 	Iowait		= 2000,
 	Etype 		= 0xbcbc,
 };
 int debug;
 
-Mux *mux(int fd[2]);
-int muxread(Mux*, Pkt*);
-void muxfree(Mux*);
+Mux	*mux(int fd[2]);
+void	muxfree(Mux*);
+int	muxread(Mux*, Pkt*);
 
-int netopen(char *name);
-int netsend(void *, int);
-int netget(void *, int);
+int	netget(void *, int);
+int	netopen(char *name);
+int	netsend(void *, int);
 
-void rawon(void);
-void rawoff(void);
-void dump(uchar*, int);
-void exits0(char*);
+void	dump(uchar*, int);
+void	exits0(char*);
+void	rawoff(void);
+void	rawon(void);

+ 2 - 1
sys/src/cmd/cec/mux.c

@@ -1,4 +1,3 @@
-/* Copyright © Coraid, Inc. 2006.  All rights reserved. */
 #include <u.h>
 #include <libc.h>
 #include "cec.h"
@@ -48,6 +47,8 @@ muxkbd(int kfd, int cfd)
 	while((m.p.len = read(kfd, m.p.data, sizeof m.p.data)) > 0)
 		if(write(cfd, &m, m.p.len+22) != m.p.len+22)
 			break;
+	m.type = Ffatal;
+	write(cfd, &m, 4);
 	exits("");
 }
 

+ 6 - 2
sys/src/cmd/cec/utils.c

@@ -1,13 +1,15 @@
-/* Copyright Coraid, Inc.  2006.  All Rights Reserved */
 #include <u.h>
 #include <libc.h>
 #include "cec.h"
 
-static int fd = -1;
+static	int	fd	= -1;
+extern	char	*svc;
 
 void
 rawon(void)
 {
+	if(svc)
+		return;
 	if((fd = open("/dev/consctl", OWRITE)) == -1 ||
 	    write(fd, "rawon", 5) != 5)
 		fprint(2, "Can't make console raw\n");
@@ -16,6 +18,8 @@ rawon(void)
 void
 rawoff(void)
 {
+	if(svc)
+		return;
 	close(fd);
 }
 

+ 1 - 1
sys/src/cmd/unix/drawterm/kern/win32.c

@@ -448,7 +448,7 @@ oserrstr(void)
 long
 showfilewrite(char *a, int n)
 {
-	Rune *action, *arg, *cmd, *p;
+	Rune *action, *arg, *cmd;
 	static Rune Lopen[] = { 'o', 'p', 'e', 'n', 0 };
 
 	cmd = runesmprint("%.*s", n, a);

+ 3 - 2
sys/src/cmd/unix/drawterm/libc/Makefile

@@ -49,12 +49,13 @@ OFILES=\
 	runesmprint.$O\
 	runesnprint.$O\
 	runesprint.$O\
+	runestrchr.$O\
+	runestrlen.$O\
+	runestrstr.$O\
 	runetype.$O\
 	runevseprint.$O\
 	runevsmprint.$O\
 	runevsnprint.$O\
-	runestrchr.$O\
-	runestrlen.$O\
 	seprint.$O\
 	smprint.$O\
 	snprint.$O\

+ 58 - 16
sys/src/cmd/upas/smtp/smtpd.c

@@ -50,6 +50,8 @@ int	pipemsg(int*);
 int	rejectcheck(void);
 String*	startcmd(void);
 
+static void	logmsg(char *action);
+
 static int
 catchalarm(void *a, char *msg)
 {
@@ -473,9 +475,33 @@ sender(String *path)
 	/*
 	 * see if this ip address, domain name, user name or account is blocked
 	 */
+	logged = 0;
 	filterstate = blocked(path);
+	/*
+	 * permanently reject what we can before trying smtp ping, which
+	 * often leads to merely temporary rejections.
+	 */
+	switch (filterstate){
+	case DENIED:
+		syslog(0, "smtpd", "Denied %s (%s/%s)",
+			s_to_c(path), him, nci->rsys);
+		rejectcount++;
+		logged++;
+		reply("554-5.7.1 We don't accept mail from %s.\r\n",
+			s_to_c(path));
+		reply("554 5.7.1 Contact postmaster@%s for more information.\r\n",
+			dom);
+		return;
+	case REFUSED:
+		syslog(0, "smtpd", "Refused %s (%s/%s)",
+			s_to_c(path), him, nci->rsys);
+		rejectcount++;
+		logged++;
+		reply("554 5.7.1 Sender domain must exist: %s\r\n",
+			s_to_c(path));
+		return;
+	}
 
-	logged = 0;
 	listadd(&senders, path);
 	reply("250 2.0.0 sender is %s\r\n", s_to_c(path));
 }
@@ -922,23 +948,33 @@ startcmd(void)
  *  address@him
  */
 char*
-bprintnode(Biobuf *b, Node *p)
+bprintnode(Biobuf *b, Node *p, int *cntp)
 {
+	int len;
+
+	*cntp = 0;
 	if(p->s){
 		if(p->addr && strchr(s_to_c(p->s), '@') == nil){
 			if(Bprint(b, "%s@%s", s_to_c(p->s), him) < 0)
 				return nil;
+			*cntp += s_len(p->s) + 1 + strlen(him);
 		} else {
-			if(Bwrite(b, s_to_c(p->s), s_len(p->s)) < 0)
+			len = s_len(p->s);
+			if(Bwrite(b, s_to_c(p->s), len) < 0)
 				return nil;
+			*cntp += len;
 		}
 	}else{
 		if(Bputc(b, p->c) < 0)
 			return nil;
+		++*cntp;
 	}
-	if(p->white)
-		if(Bwrite(b, s_to_c(p->white), s_len(p->white)) < 0)
+	if(p->white) {
+		len = s_len(p->white);
+		if(Bwrite(b, s_to_c(p->white), len) < 0)
 			return nil;
+		*cntp += len;
+	}
 	return p->end+1;
 }
 
@@ -1003,7 +1039,7 @@ forgedheaderwarnings(void)
 int
 pipemsg(int *byteswritten)
 {
-	int n, nbytes, sawdot, status;
+	int n, nbytes, sawdot, status, nonhdr, bpr;
 	char *cp;
 	Field *f;
 	Link *l;
@@ -1070,19 +1106,20 @@ pipemsg(int *byteswritten)
 	 */
 	if(originator == 0){
 		if(senders.last == nil)
-			Bprint(pp->std[0]->fp, "From: /dev/null@%s\n", him);
+			nbytes += Bprint(pp->std[0]->fp, "From: /dev/null@%s\n",
+				him);
 		else
-			Bprint(pp->std[0]->fp, "From: %s\n",
+			nbytes += Bprint(pp->std[0]->fp, "From: %s\n",
 				s_to_c(senders.last->p));
 	}
 	if(destination == 0){
-		Bprint(pp->std[0]->fp, "To: ");
+		nbytes += Bprint(pp->std[0]->fp, "To: ");
 		for(l = rcvers.first; l; l = l->next){
 			if(l != rcvers.first)
-				Bprint(pp->std[0]->fp, ", ");
-			Bprint(pp->std[0]->fp, "%s", s_to_c(l->p));
+				nbytes += Bprint(pp->std[0]->fp, ", ");
+			nbytes += Bprint(pp->std[0]->fp, "%s", s_to_c(l->p));
 		}
-		Bprint(pp->std[0]->fp, "\n");
+		nbytes += Bprint(pp->std[0]->fp, "\n");
 	}
 
 	/*
@@ -1091,12 +1128,16 @@ pipemsg(int *byteswritten)
 	 */
 	cp = s_to_c(hdr);
 	for(f = firstfield; cp != nil && f; f = f->next){
-		for(p = f->node; cp != 0 && p; p = p->next)
-			cp = bprintnode(pp->std[0]->fp, p);
+		for(p = f->node; cp != 0 && p; p = p->next) {
+			bpr = 0;
+			cp = bprintnode(pp->std[0]->fp, p, &bpr);
+			nbytes += bpr;
+		}
 		if(status == 0 && Bprint(pp->std[0]->fp, "\n") < 0){
 			piperror = "write error";
 			status = 1;
 		}
+		nbytes++;		/* for newline */
 	}
 	if(cp == nil){
 		piperror = "sender domain";
@@ -1104,11 +1145,12 @@ pipemsg(int *byteswritten)
 	}
 
 	/* write anything we read following the header */
-	if(status == 0 &&
-	    Bwrite(pp->std[0]->fp, cp, s_to_c(hdr) + s_len(hdr) - cp) < 0){
+	nonhdr = s_to_c(hdr) + s_len(hdr) - cp;
+	if(status == 0 && Bwrite(pp->std[0]->fp, cp, nonhdr) < 0){
 		piperror = "write error 2";
 		status = 1;
 	}
+	nbytes += nonhdr;
 	s_free(hdr);
 
 	/*