xref: /plan9/sys/src/cmd/con/con.c (revision 6a9fc400c33447ef5e1cda7185cb4de2c8e8010e)
1 #include <u.h>
2 #include <libc.h>
3 
4 #include "rustream.h"
5 #include "ruttyio.h"
6 #include "rusignal.h"
7 #include "rufilio.h"
8 
9 int debug;		/* true if debugging */
10 int ctl = -1;		/* control fd (for break's) */
11 int raw;		/* true if raw is on */
12 int consctl = -1;	/* control fd for cons */
13 int ttypid;		/* pid's if the 2 processes (used to kill them) */
14 int msgfd = -1;		/* mesgld file descriptor (for signals to be written to) */
15 int outfd = 1;		/* local output file descriptor */
16 int cooked;		/* non-zero forces cooked mode */
17 int returns;		/* non-zero forces carriage returns not to be filtered out */
18 int crtonl;			/* non-zero forces carriage returns to be converted to nls coming from net */
19 int	strip;		/* strip off parity bits */
20 char firsterr[2*ERRMAX];
21 char transerr[2*ERRMAX];
22 int limited;
23 char *remuser;
24 int verbose;
25 int baud;
26 int notkbd;
27 int nltocr;		/* translate kbd nl to cr  and vice versa */
28 
29 typedef struct Msg Msg;
30 #define MAXMSG (2*8192)
31 
32 int	dodial(char*, char*, char*);
33 void	fromkbd(int);
34 void	fromnet(int);
35 long	iread(int, void*, int);
36 long	iwrite(int, void*, int);
37 int	menu(int);
38 void	msgfromkbd(int);
39 void	msgfromnet(int);
40 void	msgnotifyf(void*, char*);
41 int	msgwrite(int, void*, int);
42 void	notifyf(void*, char*);
43 void	pass(int, int, int);
44 void	rawoff(void);
45 void	rawon(void);
46 int	readupto(int, char*, int);
47 int	sendctl(int, int);
48 int	sendctl1(int, int, int);
49 void	stdcon(int);
50 char*	system(int, char*);
51 void	dosystem(int, char*);
52 int	wasintr(void);
53 void	punt(char*);
54 char*	syserr(void);
55 void	seterr(char*);
56 
57 /* protocols */
58 void	device(char*, char*);
59 void	rlogin(char*, char*);
60 void	simple(char*, char*);
61 
62 void
63 usage(void)
64 {
65 	punt("usage: con [-drCvsn] [-l [user]] [-c cmd] net!host[!service]");
66 }
67 
68 void
69 main(int argc, char *argv[])
70 {
71 	char *dest;
72 	char *cmd = 0;
73 
74 	returns = 1;
75 	ARGBEGIN{
76 	case 'b':
77 		baud = atoi(ARGF());
78 		break;
79 	case 'd':
80 		debug = 1;
81 		break;
82 	case 'l':
83 		limited = 1;
84 		if(argv[1] != nil && argv[1][0] != '-')
85 			remuser = ARGF();
86 		break;
87 	case 'n':
88 		notkbd = 1;
89 		break;
90 	case 'r':
91 		returns = 0;
92 		break;
93 	case 'R':
94 		nltocr = 1;
95 		break;
96 	case 'T':
97 		crtonl = 1;
98 		break;
99 	case 'C':
100 		cooked = 1;
101 		break;
102 	case 'c':
103 		cmd = ARGF();
104 		break;
105 	case 'v':
106 		verbose = 1;
107 		break;
108 	case 's':
109 		strip = 1;
110 		break;
111 	default:
112 		usage();
113 	}ARGEND
114 
115 	if(argc != 1){
116 		if(remuser == 0)
117 			usage();
118 		dest = remuser;
119 		remuser = 0;
120 	} else
121 		dest = argv[0];
122 	if(*dest == '/' && strchr(dest, '!') == 0)
123 		device(dest, cmd);
124 	else if(limited){
125 		simple(dest, cmd);	/* doesn't return if dialout succeeds */
126 		rlogin(dest, cmd);	/* doesn't return if dialout succeeds */
127 	} else {
128 		rlogin(dest, cmd);	/* doesn't return if dialout succeeds */
129 		simple(dest, cmd);	/* doesn't return if dialout succeeds */
130 	}
131 	punt(firsterr);
132 }
133 
134 /*
135  *  just dial and use as a byte stream with remote echo
136  */
137 void
138 simple(char *dest, char *cmd)
139 {
140 	int net;
141 
142 	net = dodial(dest, 0, 0);
143 	if(net < 0)
144 		return;
145 
146 	if(cmd)
147 		dosystem(net, cmd);
148 
149 	if(!cooked)
150 		rawon();
151 	stdcon(net);
152 	exits(0);
153 }
154 
155 /*
156  *  dial, do UCB authentication, use as a byte stream with local echo
157  *
158  *  return if dial failed
159  */
160 void
161 rlogin(char *dest, char *cmd)
162 {
163 	int net;
164 	char buf[128];
165 	char *p;
166 	char *localuser;
167 
168 	/* only useful on TCP */
169 	if(strchr(dest, '!')
170 	&& (strncmp(dest, "tcp!", 4)!=0 && strncmp(dest, "net!", 4)!=0))
171 		return;
172 
173 	net = dodial(dest, "tcp", "login");
174 	if(net < 0)
175 		return;
176 
177 	/*
178 	 *  do UCB rlogin authentication
179 	 */
180 	localuser = getuser();
181 	if(remuser == 0){
182 		if(limited)
183 			remuser = ":";
184 		else
185 			remuser = localuser;
186 	}
187 	p = getenv("TERM");
188 	if(p == 0)
189 		p = "p9";
190 	if(write(net, "", 1)<0
191 	|| write(net, localuser, strlen(localuser)+1)<0
192 	|| write(net, remuser, strlen(remuser)+1)<0
193 	|| write(net, p, strlen(p)+1)<0){
194 		close(net);
195 		punt("BSD authentication failed");
196 	}
197 	if(read(net, buf, 1) != 1)
198 		punt("BSD authentication failed1");
199 	if(buf[0] != 0){
200 		fprint(2, "con: remote error: ");
201 		while(read(net, buf, 1) == 1){
202 			write(2, buf, 1);
203 			if(buf[0] == '\n')
204 				break;
205 		}
206 		exits("read");
207 	}
208 
209 	if(cmd)
210 		dosystem(net, cmd);
211 
212 	if(!cooked)
213 		rawon();
214 	nltocr = 1;
215 	stdcon(net);
216 	exits(0);
217 }
218 
219 /*
220  *  just open a device and use it as a connection
221  */
222 void
223 device(char *dest, char *cmd)
224 {
225 	int net;
226 	char cname[128];
227 
228 	net = open(dest, ORDWR);
229 	if(net < 0) {
230 		fprint(2, "con: cannot open %s: %r\n", dest);
231 		exits("open");
232 	}
233 	snprint(cname, sizeof cname, "%sctl", dest);
234 	ctl = open(cname, ORDWR);
235 	if (baud > 0) {
236 		if(ctl >= 0)
237 			fprint(ctl, "b%d", baud);
238 		else
239 			fprint(2, "con: cannot open %s: %r\n", cname);
240 	}
241 
242 	if(cmd)
243 		dosystem(net, cmd);
244 
245 	if(!cooked)
246 		rawon();
247 	stdcon(net);
248 	exits(0);
249 }
250 
251 /*
252  *  ignore interrupts
253  */
254 void
255 notifyf(void *a, char *msg)
256 {
257 	USED(a);
258 
259 	if(strstr(msg, "closed pipe")
260 	|| strcmp(msg, "interrupt") == 0
261 	|| strcmp(msg, "hangup") == 0)
262 		noted(NCONT);
263 	noted(NDFLT);
264 }
265 
266 /*
267  *  turn keyboard raw mode on
268  */
269 void
270 rawon(void)
271 {
272 	if(debug)
273 		fprint(2, "rawon\n");
274 	if(raw)
275 		return;
276 	if(consctl < 0)
277 		consctl = open("/dev/consctl", OWRITE);
278 	if(consctl < 0){
279 //		fprint(2, "can't open consctl\n");
280 		return;
281 	}
282 	write(consctl, "rawon", 5);
283 	raw = 1;
284 }
285 
286 /*
287  *  turn keyboard raw mode off
288  */
289 void
290 rawoff(void)
291 {
292 	if(debug)
293 		fprint(2, "rawoff\n");
294 	if(raw == 0)
295 		return;
296 	if(consctl < 0)
297 		consctl = open("/dev/consctl", OWRITE);
298 	if(consctl < 0){
299 //		fprint(2, "can't open consctl\n");
300 		return;
301 	}
302 	write(consctl, "rawoff", 6);
303 	raw = 0;
304 }
305 
306 /*
307  *  control menu
308  */
309 #define STDHELP	"\t(b)reak, (q)uit, (i)nterrupt, toggle printing (r)eturns, (.)continue, (!cmd)\n"
310 
311 int
312 menu(int net)
313 {
314 	char buf[MAXMSG];
315 	long n;
316 	int done;
317 	int wasraw = raw;
318 
319 	if(wasraw)
320 		rawoff();
321 
322 	fprint(2, ">>> ");
323 	for(done = 0; !done; ){
324 		n = read(0, buf, sizeof(buf)-1);
325 		if(n <= 0)
326 			return -1;
327 		buf[n] = 0;
328 		switch(buf[0]){
329 		case '!':
330 			print(buf);
331 			system(net, buf+1);
332 			print("!\n");
333 			done = 1;
334 			break;
335 		case '.':
336 			done = 1;
337 			break;
338 		case 'q':
339 			return -1;
340 			break;
341 		case 'i':
342 			buf[0] = 0x1c;
343 			if(msgfd <= 0)
344 				write(net, buf, 1);
345 			else
346 				sendctl1(msgfd, M_SIGNAL, SIGQUIT);
347 			done = 1;
348 			break;
349 		case 'b':
350 			if(msgfd >= 0)
351 				sendctl(msgfd, M_BREAK);
352 			else if(ctl >= 0)
353 				write(ctl, "k", 1);
354 			done = 1;
355 			break;
356 		case 'r':
357 			returns = 1-returns;
358 			done = 1;
359 			break;
360 		default:
361 			fprint(2, STDHELP);
362 			break;
363 		}
364 		if(!done)
365 			fprint(2, ">>> ");
366 	}
367 
368 	if(wasraw)
369 		rawon();
370 	else
371 		rawoff();
372 	return 0;
373 }
374 
375 /*
376  *  the real work.  two processes pass bytes back and forth between the
377  *  terminal and the network.
378  */
379 void
380 stdcon(int net)
381 {
382 	int netpid;
383 
384 	ttypid = getpid();
385 	switch(netpid = rfork(RFMEM|RFPROC)){
386 	case -1:
387 		perror("con");
388 		exits("fork");
389 	case 0:
390 		notify(notifyf);
391 		fromnet(net);
392 		postnote(PNPROC, ttypid, "");
393 		exits(0);
394 	default:
395 		notify(notifyf);
396 		fromkbd(net);
397 		if(notkbd)
398 			for(;;)sleep(0);
399 		postnote(PNPROC, netpid, "");
400 		exits(0);
401 	}
402 }
403 
404 /*
405  *  Read the keyboard and write it to the network.  '^\' gets us into
406  *  the menu.
407  */
408 void
409 fromkbd(int net)
410 {
411 	long n;
412 	char buf[MAXMSG];
413 	char *p, *ep;
414 	int eofs;
415 
416 	eofs = 0;
417 	for(;;){
418 		n = read(0, buf, sizeof(buf));
419 		if(n < 0){
420 			if(wasintr()){
421 				if(cooked){
422 					buf[0] = 0x7f;
423 					n = 1;
424 				} else
425 					continue;
426 			} else
427 				return;
428 		}
429 		if(n == 0){
430 			if(++eofs > 32)
431 				return;
432 		} else
433 			eofs = 0;
434 		if(n && memchr(buf, 0x1c, n)){
435 			if(menu(net) < 0)
436 				return;
437 		}else{
438 			if(cooked && n==0){
439 				buf[0] = 0x4;
440 				n = 1;
441 			}
442 			if(nltocr){
443 				ep = buf+n;
444 				for(p = buf; p < ep; p++)
445 					switch(*p){
446 					case '\r':
447 						*p = '\n';
448 						break;
449 					case '\n':
450 						*p = '\r';
451 						break;
452 					}
453 			}
454 			if(iwrite(net, buf, n) != n)
455 				return;
456 		}
457 	}
458 }
459 
460 /*
461  *  Read from the network and write to the screen.
462  *  Filter out spurious carriage returns.
463  */
464 void
465 fromnet(int net)
466 {
467 	long n;
468 	char buf[MAXMSG];
469 	char *cp, *ep;
470 
471 	for(;;){
472 		n = iread(net, buf, sizeof(buf));
473 		if(n < 0)
474 			return;
475 		if(n == 0)
476 			continue;
477 
478 		if (strip)
479 			for (cp=buf; cp<buf+n; cp++)
480 				*cp &= 0177;
481 
482 		if(crtonl) {
483 			/* convert cr's to nl's */
484 			for (cp = buf; cp < buf + n; cp++)
485 				if (*cp == '\r')
486 					*cp = '\n';
487 		}
488 		else if(!returns){
489 			/* convert cr's to null's */
490 			cp = buf;
491 			ep = buf + n;
492 			while(cp < ep && (cp = memchr(cp, '\r', ep-cp))){
493 				memmove(cp, cp+1, ep-cp-1);
494 				ep--;
495 				n--;
496 			}
497 		}
498 
499 		if(n > 0 && iwrite(outfd, buf, n) != n){
500 			if(outfd == 1)
501 				return;
502 			outfd = 1;
503 			if(iwrite(1, buf, n) != n)
504 				return;
505 		}
506 	}
507 }
508 
509 /*
510  *  dial and return a data connection
511  */
512 int
513 dodial(char *dest, char *net, char *service)
514 {
515 	char name[128];
516 	char devdir[128];
517 	int data;
518 
519 	devdir[0] = 0;
520 	strcpy(name, netmkaddr(dest, net, service));
521 	data = dial(name, 0, devdir, &ctl);
522 	if(data < 0){
523 		seterr(name);
524 		return -1;
525 	}
526 	fprint(2, "connected to %s on %s\n", name, devdir);
527 	return data;
528 }
529 
530 void
531 dosystem(int fd, char *cmd)
532 {
533 	char *p;
534 
535 	p = system(fd, cmd);
536 	if(*p){
537 		print("con: %s terminated with %s\n", cmd, p);
538 		exits(p);
539 	}
540 }
541 
542 /*
543  *  run a command with the network connection as standard IO
544  */
545 char *
546 system(int fd, char *cmd)
547 {
548 	int pid;
549 	int p;
550 	static Waitmsg msg;
551 	int pfd[2];
552 	int n;
553 	char buf[4096];
554 
555 	if(pipe(pfd) < 0){
556 		perror("pipe");
557 		return "pipe failed";
558 	}
559 	outfd = pfd[1];
560 
561 	close(consctl);
562 	consctl = -1;
563 	switch(pid = fork()){
564 	case -1:
565 		perror("con");
566 		return "fork failed";
567 	case 0:
568 		close(pfd[1]);
569 		dup(pfd[0], 0);
570 		dup(fd, 1);
571 		close(ctl);
572 		close(fd);
573 		close(pfd[0]);
574 		if(*cmd)
575 			execl("/bin/rc", "rc", "-c", cmd, 0);
576 		else
577 			execl("/bin/rc", "rc", 0);
578 		perror("con");
579 		exits("exec");
580 		break;
581 	default:
582 		close(pfd[0]);
583 		while((n = read(pfd[1], buf, sizeof(buf))) > 0){
584 			if(msgfd >= 0){
585 				if(msgwrite(fd, buf, n) != n)
586 					break;
587 			} else {
588 				if(write(fd, buf, n) != n)
589 					break;
590 			}
591 		}
592 		p = waitpid();
593 		outfd = 1;
594 		close(pfd[1]);
595 		if(p < 0 || p != pid)
596 			return "lost child";
597 		break;
598 	}
599 	return msg.msg;
600 }
601 
602 int
603 wasintr(void)
604 {
605 	return strcmp(syserr(), "interrupted") == 0;
606 }
607 
608 void
609 punt(char *msg)
610 {
611 	if(*msg == 0)
612 		msg = transerr;
613 	fprint(2, "con: %s\n", msg);
614 	exits(msg);
615 }
616 
617 char*
618 syserr(void)
619 {
620 	static char err[ERRMAX];
621 	errstr(err, sizeof err);
622 	return err;
623 }
624 
625 void
626 seterr(char *addr)
627 {
628 	char *se = syserr();
629 
630 	if(verbose)
631 		fprint(2, "'%s' calling %s\n", se, addr);
632 	if(firsterr[0] && (strstr(se, "translate") ||
633 	 strstr(se, "file does not exist") ||
634 	 strstr(se, "unknown address") ||
635 	 strstr(se, "directory entry not found")))
636 		return;
637 	strcpy(firsterr, se);
638 }
639 
640 
641 long
642 iread(int f, void *a, int n)
643 {
644 	long m;
645 
646 	for(;;){
647 		m = read(f, a, n);
648 		if(m >= 0 || !wasintr())
649 			break;
650 	}
651 	return m;
652 }
653 
654 long
655 iwrite(int f, void *a, int n)
656 {
657 	long m;
658 
659 	m = write(f, a, n);
660 	if(m < 0 && wasintr())
661 		return n;
662 	return m;
663 }
664 
665 /*
666  *  The rest is to support the V10 mesgld protocol.
667  */
668 
669 /*
670  *  network orderings
671  */
672 #define get2byte(p) ((p)[0] + ((p)[1]<<8))
673 #define get4byte(p) ((p)[0] + ((p)[1]<<8) + ((p)[2]<<16) + ((p)[3]<<24))
674 #define put2byte(p, i) ((p)[0]=(i), (p)[1]=(i)>>8)
675 #define put4byte(p, i) ((p)[0]=(i), (p)[1]=(i)>>8, (p)[2]=(i)>>16, (p)[3]=(i)>>24)
676 
677 /*
678  *  tty parameters
679  */
680 int sgflags = ECHO;
681 
682 /*
683  *  a mesgld message
684  */
685 struct Msg {
686 	struct mesg h;
687 	char b[MAXMSG];
688 };
689 
690 /*
691  *  convert certain interrupts into mesgld messages
692  */
693 void
694 msgnotifyf(void *a, char *msg)
695 {
696 	USED(a);
697 
698 	if(strstr(msg, "closed pipe"))
699 		noted(NCONT);
700 	if(strcmp(msg, "interrupt") == 0){
701 		sendctl1(msgfd, M_SIGNAL, SIGINT);
702 		noted(NCONT);
703 	}
704 	if(strcmp(msg, "hangup") == 0){
705 		sendctl(msgfd, M_HANGUP);
706 		noted(NCONT);
707 	}
708 	noted(NDFLT);
709 }
710 
711 /*
712  *  send an empty mesgld message
713  */
714 int
715 sendctl(int net, int type)
716 {
717 	Msg m;
718 
719 	m.h.type = type;
720 	m.h.magic = MSGMAGIC;
721 	put2byte(m.h.size, 0);
722 	if(iwrite(net, &m, sizeof(struct mesg)) != sizeof(struct mesg))
723 		return -1;
724 	return 0;
725 }
726 
727 /*
728  *  send a one byte mesgld message
729  */
730 int
731 sendctl1(int net, int type, int parm)
732 {
733 	Msg m;
734 
735 	m.h.type = type;
736 	m.h.magic = MSGMAGIC;
737 	m.b[0] = parm;
738 	put2byte(m.h.size, 1);
739 	if(iwrite(net, &m, sizeof(struct mesg)+1) != sizeof(struct mesg)+1)
740 		return -1;
741 	return 0;
742 }
743 
744 /*
745  *  read n bytes.  return -1 if it fails, 0 otherwise.
746  */
747 int
748 readupto(int from, char *a, int len)
749 {
750 	int n;
751 
752 	while(len > 0){
753 		n = iread(from, a, len);
754 		if(n < 0)
755 			return -1;
756 		a += n;
757 		len -= n;
758 	}
759 	return 0;
760 }
761 
762 /*
763  *  Decode a mesgld message from the network
764  */
765 void
766 msgfromnet(int net)
767 {
768 	ulong com;
769 	struct stioctl *io;
770 	struct sgttyb *sg;
771 	struct ttydevb *td;
772 	struct tchars *tc;
773 	int len;
774 	Msg m;
775 
776 	for(;;){
777 		/* get a complete mesgld message */
778 		if(readupto(net, (char*)&m.h, sizeof(struct mesg)) < 0)
779 			break;
780 		if(m.h.magic != MSGMAGIC){
781 			fprint(2, "con: bad message magic 0x%ux\n", m.h.magic);
782 			break;
783 		}
784 		len = get2byte(m.h.size);
785 		if(len > sizeof(m.b)){
786 			len = sizeof(m.b);
787 			fprint(2, "con: mesgld message too long\n");
788 		}
789 		if(len && readupto(net, m.b, len) < 0)
790 			break;
791 
792 		/* decode */
793 		switch(m.h.type){
794 		case M_HANGUP:
795 			if(debug)
796 				fprint(2, "M_HANGUP\n");
797 			return;
798 		case M_DATA:
799 			if(debug)
800 				fprint(2, "M_DATA %d bytes\n", len);
801 			if(iwrite(outfd, m.b, len) != len){
802 				if(outfd == 1)
803 					return;
804 				outfd = 1;
805 				if(iwrite(outfd, m.b, len) != len)
806 					return;
807 			}
808 			continue;
809 		case M_IOCTL:
810 			break;
811 		default:
812 			/* ignore */
813 			if(debug)
814 				fprint(2, "con: unknown message\n");
815 			continue;
816 		}
817 
818 		/*
819 		 *  answer an ioctl
820 		 */
821 		io = (struct stioctl *)m.b;
822 		com = get4byte(io->com);
823 		if(debug)
824 			fprint(2, "M_IOCTL %lud\n", com);
825 		switch(com){
826 		case FIOLOOKLD:
827 			put4byte(io->data, tty_ld);
828 			len = 0;
829 			break;
830 		case TIOCGETP:
831 			sg = (struct sgttyb *)io->data;
832 			sg->sg_ispeed = sg->sg_ospeed = B9600;
833 			sg->sg_erase = 0010;	/* back space */
834 			sg->sg_kill = 0025;	/* CNTL U */
835 			put2byte(sg->sg_flags, sgflags);
836 			len = sizeof(struct sgttyb);
837 			break;
838 		case TIOCSETN:
839 		case TIOCSETP:
840 			sg = (struct sgttyb *)io->data;
841 			sgflags = get2byte(sg->sg_flags);
842 			if((sgflags&(RAW|CBREAK)) || !(sgflags&ECHO))
843 				rawon();
844 			else
845 				rawoff();
846 			len = 0;
847 			break;
848 		case TIOCGETC:
849 			tc = (struct tchars *)io->data;
850 			tc->t_intrc = 0177;
851 			tc->t_quitc = 0034;
852 			tc->t_startc = 0;
853 			tc->t_stopc = 0;
854 			tc->t_eofc = 0004;
855 			tc->t_brkc = 0;
856 			len = sizeof(struct tchars);
857 			break;
858 		case TIOCSETC:
859 			len = 0;
860 			break;
861 		case TIOCGDEV:
862 			td = (struct ttydevb *)io->data;
863 			td->ispeed = td->ospeed = B9600;
864 			put2byte(td->flags, 0);
865 			len = sizeof(struct ttydevb);
866 			break;
867 		case TIOCSDEV:
868 			len = 0;
869 			break;
870 		default:
871 			/*
872 			 *  unimplemented
873 			 */
874 			m.b[len] = 0;
875 			if(sendctl(net, M_IOCNAK) < 0)
876 				return;
877 			continue;
878 		}
879 
880 		/*
881 		 *  acknowledge
882 		 */
883 		m.h.type = M_IOCACK;
884 		m.h.magic = MSGMAGIC;
885 		len += 4;
886 		put2byte(m.h.size, len);
887 		len += sizeof(struct mesg);
888 		if(iwrite(net, &m, len) != len)
889 			return;
890 	}
891 }
892 
893 /*
894  *  Read the keyboard, convert to mesgld messages, and write it to the network.
895  *  '^\' gets us into the menu.
896  */
897 void
898 msgfromkbd(int net)
899 {
900 	long n;
901 	char buf[MAXMSG];
902 
903 	for(;;){
904 		n = iread(0, buf, sizeof(buf));
905 		if(n < 0)
906 			return;
907 		if(n && memchr(buf, 0034, n)){
908 			if(menu(net) < 0)
909 				return;
910 		} else {
911 			if(msgwrite(net, buf, n) != n)
912 				return;
913 		}
914 	}
915 }
916 
917 int
918 msgwrite(int fd, void *buf, int len)
919 {
920 	Msg m;
921 	int n;
922 
923 	n = len;
924 	memmove(m.b, buf, n);
925 	put2byte(m.h.size, n);
926 	m.h.magic = MSGMAGIC;
927 	m.h.type = M_DATA;
928 	n += sizeof(struct mesg);
929 	if(iwrite(fd, &m, n) != n)
930 		return -1;
931 
932 	put2byte(m.h.size, 0);
933 	m.h.magic = MSGMAGIC;
934 	m.h.type = M_DELIM;
935 	n = sizeof(struct mesg);
936 	if(iwrite(fd, &m, n) != n)
937 		return -1;
938 
939 	return len;
940 }
941 
942