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