xref: /plan9/sys/src/cmd/ssh1/ssh1.c (revision 58da3067adcdccaaa043d0bfde28ba83b7ced07d)
163afb9a5SDavid du Colombier /* remote login via ssh v1 */
263afb9a5SDavid du Colombier #include "ssh.h"
363afb9a5SDavid du Colombier 
463afb9a5SDavid du Colombier int cooked = 0;		/* user wants cooked mode */
563afb9a5SDavid du Colombier int raw = 0;		/* console is in raw mode */
663afb9a5SDavid du Colombier int crstrip;
763afb9a5SDavid du Colombier int interactive = -1;
863afb9a5SDavid du Colombier int usemenu = 1;
963afb9a5SDavid du Colombier int isatty(int);
1063afb9a5SDavid du Colombier int rawhack;
1163afb9a5SDavid du Colombier int forwardagent = 0;
1263afb9a5SDavid du Colombier char *buildcmd(int, char**);
1363afb9a5SDavid du Colombier void fromnet(Conn*);
1463afb9a5SDavid du Colombier void fromstdin(Conn*);
1563afb9a5SDavid du Colombier void winchanges(Conn*);
1663afb9a5SDavid du Colombier static void	sendwritemsg(Conn *c, char *buf, int n);
1763afb9a5SDavid du Colombier 
1863afb9a5SDavid du Colombier /*
1963afb9a5SDavid du Colombier  * Lifted from telnet.c, con.c
2063afb9a5SDavid du Colombier  */
2163afb9a5SDavid du Colombier static int consctl = -1;
2263afb9a5SDavid du Colombier static int outfd = 1;			/* changed during system */
2363afb9a5SDavid du Colombier static void system(Conn*, char*);
2463afb9a5SDavid du Colombier 
2563afb9a5SDavid du Colombier Cipher *allcipher[] = {
2663afb9a5SDavid du Colombier 	&cipherrc4,
2763afb9a5SDavid du Colombier 	&cipherblowfish,
2863afb9a5SDavid du Colombier 	&cipher3des,
2963afb9a5SDavid du Colombier 	&cipherdes,
3063afb9a5SDavid du Colombier 	&ciphernone,
3163afb9a5SDavid du Colombier 	&ciphertwiddle,
3263afb9a5SDavid du Colombier };
3363afb9a5SDavid du Colombier 
3463afb9a5SDavid du Colombier Auth *allauth[] = {
3563afb9a5SDavid du Colombier 	&authpassword,
3663afb9a5SDavid du Colombier 	&authrsa,
3763afb9a5SDavid du Colombier 	&authtis,
3863afb9a5SDavid du Colombier };
3963afb9a5SDavid du Colombier 
4063afb9a5SDavid du Colombier char *cipherlist = "blowfish rc4 3des";
4163afb9a5SDavid du Colombier char *authlist = "rsa password tis";
4263afb9a5SDavid du Colombier 
4363afb9a5SDavid du Colombier Cipher*
findcipher(char * name,Cipher ** list,int nlist)4463afb9a5SDavid du Colombier findcipher(char *name, Cipher **list, int nlist)
4563afb9a5SDavid du Colombier {
4663afb9a5SDavid du Colombier 	int i;
4763afb9a5SDavid du Colombier 
4863afb9a5SDavid du Colombier 	for(i=0; i<nlist; i++)
4963afb9a5SDavid du Colombier 		if(strcmp(name, list[i]->name) == 0)
5063afb9a5SDavid du Colombier 			return list[i];
5163afb9a5SDavid du Colombier 	error("unknown cipher %s", name);
5263afb9a5SDavid du Colombier 	return nil;
5363afb9a5SDavid du Colombier }
5463afb9a5SDavid du Colombier 
5563afb9a5SDavid du Colombier Auth*
findauth(char * name,Auth ** list,int nlist)5663afb9a5SDavid du Colombier findauth(char *name, Auth **list, int nlist)
5763afb9a5SDavid du Colombier {
5863afb9a5SDavid du Colombier 	int i;
5963afb9a5SDavid du Colombier 
6063afb9a5SDavid du Colombier 	for(i=0; i<nlist; i++)
6163afb9a5SDavid du Colombier 		if(strcmp(name, list[i]->name) == 0)
6263afb9a5SDavid du Colombier 			return list[i];
6363afb9a5SDavid du Colombier 	error("unknown auth %s", name);
6463afb9a5SDavid du Colombier 	return nil;
6563afb9a5SDavid du Colombier }
6663afb9a5SDavid du Colombier 
6763afb9a5SDavid du Colombier void
usage(void)6863afb9a5SDavid du Colombier usage(void)
6963afb9a5SDavid du Colombier {
7063afb9a5SDavid du Colombier 	fprint(2, "usage: ssh [-CiImPpRr] [-A authlist] [-c cipherlist] [user@]hostname [cmd [args]]\n");
7163afb9a5SDavid du Colombier 	exits("usage");
7263afb9a5SDavid du Colombier }
7363afb9a5SDavid du Colombier 
7463afb9a5SDavid du Colombier void
main(int argc,char ** argv)7563afb9a5SDavid du Colombier main(int argc, char **argv)
7663afb9a5SDavid du Colombier {
7763afb9a5SDavid du Colombier 	int i, dowinchange, fd, usepty;
7863afb9a5SDavid du Colombier 	char *host, *cmd, *user, *p;
7963afb9a5SDavid du Colombier 	char *f[16];
8063afb9a5SDavid du Colombier 	Conn c;
8163afb9a5SDavid du Colombier 	Msg *m;
8263afb9a5SDavid du Colombier 
8363afb9a5SDavid du Colombier 	fmtinstall('B', mpfmt);
8463afb9a5SDavid du Colombier 	fmtinstall('H', encodefmt);
8563afb9a5SDavid du Colombier 	atexit(atexitkiller);
8663afb9a5SDavid du Colombier 	atexitkill(getpid());
8763afb9a5SDavid du Colombier 
8863afb9a5SDavid du Colombier 	dowinchange = 0;
8963afb9a5SDavid du Colombier 	if(getenv("LINES"))
9063afb9a5SDavid du Colombier 		dowinchange = 1;
9163afb9a5SDavid du Colombier 	usepty = -1;
9263afb9a5SDavid du Colombier 	user = nil;
9363afb9a5SDavid du Colombier 	ARGBEGIN{
9463afb9a5SDavid du Colombier 	case 'B':	/* undocumented, debugging */
9563afb9a5SDavid du Colombier 		doabort = 1;
9663afb9a5SDavid du Colombier 		break;
9763afb9a5SDavid du Colombier 	case 'D':	/* undocumented, debugging */
9863afb9a5SDavid du Colombier 		debuglevel = strtol(EARGF(usage()), nil, 0);
9963afb9a5SDavid du Colombier 		break;
10063afb9a5SDavid du Colombier 	case 'l':	/* deprecated */
10163afb9a5SDavid du Colombier 	case 'u':
10263afb9a5SDavid du Colombier 		user = EARGF(usage());
10363afb9a5SDavid du Colombier 		break;
10463afb9a5SDavid du Colombier 	case 'a':	/* used by Unix scp implementations; we must ignore them. */
10563afb9a5SDavid du Colombier 	case 'x':
10663afb9a5SDavid du Colombier 		break;
10763afb9a5SDavid du Colombier 
10863afb9a5SDavid du Colombier 	case 'A':
10963afb9a5SDavid du Colombier 		authlist = EARGF(usage());
11063afb9a5SDavid du Colombier 		break;
11163afb9a5SDavid du Colombier 	case 'C':
11263afb9a5SDavid du Colombier 		cooked = 1;
11363afb9a5SDavid du Colombier 		break;
11463afb9a5SDavid du Colombier 	case 'c':
11563afb9a5SDavid du Colombier 		cipherlist = EARGF(usage());
11663afb9a5SDavid du Colombier 		break;
11763afb9a5SDavid du Colombier 	case 'f':
11863afb9a5SDavid du Colombier 		forwardagent = 1;
11963afb9a5SDavid du Colombier 		break;
12063afb9a5SDavid du Colombier 	case 'I':
12163afb9a5SDavid du Colombier 		interactive = 0;
12263afb9a5SDavid du Colombier 		break;
12363afb9a5SDavid du Colombier 	case 'i':
12463afb9a5SDavid du Colombier 		interactive = 1;
12563afb9a5SDavid du Colombier 		break;
12663afb9a5SDavid du Colombier 	case 'm':
12763afb9a5SDavid du Colombier 		usemenu = 0;
12863afb9a5SDavid du Colombier 		break;
12963afb9a5SDavid du Colombier 	case 'P':
13063afb9a5SDavid du Colombier 		usepty = 0;
13163afb9a5SDavid du Colombier 		break;
13263afb9a5SDavid du Colombier 	case 'p':
13363afb9a5SDavid du Colombier 		usepty = 1;
13463afb9a5SDavid du Colombier 		break;
13563afb9a5SDavid du Colombier 	case 'R':
13663afb9a5SDavid du Colombier 		rawhack = 1;
13763afb9a5SDavid du Colombier 		break;
13863afb9a5SDavid du Colombier 	case 'r':
13963afb9a5SDavid du Colombier 		crstrip = 1;
14063afb9a5SDavid du Colombier 		break;
14163afb9a5SDavid du Colombier 	default:
14263afb9a5SDavid du Colombier 		usage();
14363afb9a5SDavid du Colombier 	}ARGEND
14463afb9a5SDavid du Colombier 
14563afb9a5SDavid du Colombier 	if(argc < 1)
14663afb9a5SDavid du Colombier 		usage();
14763afb9a5SDavid du Colombier 
14863afb9a5SDavid du Colombier 	host = argv[0];
14963afb9a5SDavid du Colombier 
15063afb9a5SDavid du Colombier 	cmd = nil;
15163afb9a5SDavid du Colombier 	if(argc > 1)
15263afb9a5SDavid du Colombier 		cmd = buildcmd(argc-1, argv+1);
15363afb9a5SDavid du Colombier 
15463afb9a5SDavid du Colombier 	if((p = strchr(host, '@')) != nil){
15563afb9a5SDavid du Colombier 		*p++ = '\0';
15663afb9a5SDavid du Colombier 		user = host;
15763afb9a5SDavid du Colombier 		host = p;
15863afb9a5SDavid du Colombier 	}
15963afb9a5SDavid du Colombier 	if(user == nil)
16063afb9a5SDavid du Colombier 		user = getenv("user");
16163afb9a5SDavid du Colombier 	if(user == nil)
16263afb9a5SDavid du Colombier 		sysfatal("cannot find user name");
16363afb9a5SDavid du Colombier 
16463afb9a5SDavid du Colombier 	privatefactotum();
16563afb9a5SDavid du Colombier 	if(interactive==-1)
16663afb9a5SDavid du Colombier 		interactive = isatty(0);
16763afb9a5SDavid du Colombier 
16863afb9a5SDavid du Colombier 	if((fd = dial(netmkaddr(host, "tcp", "ssh"), nil, nil, nil)) < 0)
16963afb9a5SDavid du Colombier 		sysfatal("dialing %s: %r", host);
17063afb9a5SDavid du Colombier 
17163afb9a5SDavid du Colombier 	memset(&c, 0, sizeof c);
17263afb9a5SDavid du Colombier 	c.interactive = interactive;
17363afb9a5SDavid du Colombier 	c.fd[0] = c.fd[1] = fd;
17463afb9a5SDavid du Colombier 	c.user = user;
17563afb9a5SDavid du Colombier 	c.host = host;
17663afb9a5SDavid du Colombier 	setaliases(&c, host);
17763afb9a5SDavid du Colombier 
17863afb9a5SDavid du Colombier 	c.nokcipher = getfields(cipherlist, f, nelem(f), 1, ", ");
17963afb9a5SDavid du Colombier 	c.okcipher = emalloc(sizeof(Cipher*)*c.nokcipher);
18063afb9a5SDavid du Colombier 	for(i=0; i<c.nokcipher; i++)
18163afb9a5SDavid du Colombier 		c.okcipher[i] = findcipher(f[i], allcipher, nelem(allcipher));
18263afb9a5SDavid du Colombier 
18363afb9a5SDavid du Colombier 	c.nokauth = getfields(authlist, f, nelem(f), 1, ", ");
18463afb9a5SDavid du Colombier 	c.okauth = emalloc(sizeof(Auth*)*c.nokauth);
18563afb9a5SDavid du Colombier 	for(i=0; i<c.nokauth; i++)
18663afb9a5SDavid du Colombier 		c.okauth[i] = findauth(f[i], allauth, nelem(allauth));
18763afb9a5SDavid du Colombier 
18863afb9a5SDavid du Colombier 	sshclienthandshake(&c);
18963afb9a5SDavid du Colombier 
19063afb9a5SDavid du Colombier 	if(forwardagent){
19163afb9a5SDavid du Colombier 		if(startagent(&c) < 0)
19263afb9a5SDavid du Colombier 			forwardagent = 0;
19363afb9a5SDavid du Colombier 	}
19463afb9a5SDavid du Colombier 	if(usepty == -1)
19563afb9a5SDavid du Colombier 		usepty = cmd==nil;
19663afb9a5SDavid du Colombier 	if(usepty)
19763afb9a5SDavid du Colombier 		requestpty(&c);
19863afb9a5SDavid du Colombier 	if(cmd){
19963afb9a5SDavid du Colombier 		m = allocmsg(&c, SSH_CMSG_EXEC_CMD, 4+strlen(cmd));
20063afb9a5SDavid du Colombier 		putstring(m, cmd);
20163afb9a5SDavid du Colombier 	}else
20263afb9a5SDavid du Colombier 		m = allocmsg(&c, SSH_CMSG_EXEC_SHELL, 0);
20363afb9a5SDavid du Colombier 	sendmsg(m);
20463afb9a5SDavid du Colombier 
20563afb9a5SDavid du Colombier 	fromstdin(&c);
20663afb9a5SDavid du Colombier 	rfork(RFNOTEG);	/* only fromstdin gets notes */
20763afb9a5SDavid du Colombier 	if(dowinchange)
20863afb9a5SDavid du Colombier 		winchanges(&c);
20963afb9a5SDavid du Colombier 	fromnet(&c);
21063afb9a5SDavid du Colombier 	exits(0);
21163afb9a5SDavid du Colombier }
21263afb9a5SDavid du Colombier 
21363afb9a5SDavid du Colombier int
isatty(int fd)21463afb9a5SDavid du Colombier isatty(int fd)
21563afb9a5SDavid du Colombier {
21663afb9a5SDavid du Colombier 	char buf[64];
21763afb9a5SDavid du Colombier 
21863afb9a5SDavid du Colombier 	buf[0] = '\0';
21963afb9a5SDavid du Colombier 	fd2path(fd, buf, sizeof buf);
22063afb9a5SDavid du Colombier 	if(strlen(buf)>=9 && strcmp(buf+strlen(buf)-9, "/dev/cons")==0)
22163afb9a5SDavid du Colombier 		return 1;
22263afb9a5SDavid du Colombier 	return 0;
22363afb9a5SDavid du Colombier }
22463afb9a5SDavid du Colombier 
22563afb9a5SDavid du Colombier char*
buildcmd(int argc,char ** argv)22663afb9a5SDavid du Colombier buildcmd(int argc, char **argv)
22763afb9a5SDavid du Colombier {
22863afb9a5SDavid du Colombier 	int i, len;
22963afb9a5SDavid du Colombier 	char *s, *t;
23063afb9a5SDavid du Colombier 
23163afb9a5SDavid du Colombier 	len = argc-1;
23263afb9a5SDavid du Colombier 	for(i=0; i<argc; i++)
23363afb9a5SDavid du Colombier 		len += strlen(argv[i]);
23463afb9a5SDavid du Colombier 	s = emalloc(len+1);
23563afb9a5SDavid du Colombier 	t = s;
23663afb9a5SDavid du Colombier 	for(i=0; i<argc; i++){
23763afb9a5SDavid du Colombier 		if(i)
23863afb9a5SDavid du Colombier 			*t++ = ' ';
23963afb9a5SDavid du Colombier 		strcpy(t, argv[i]);
24063afb9a5SDavid du Colombier 		t += strlen(t);
24163afb9a5SDavid du Colombier 	}
24263afb9a5SDavid du Colombier 	return s;
24363afb9a5SDavid du Colombier }
24463afb9a5SDavid du Colombier 
24563afb9a5SDavid du Colombier 
24663afb9a5SDavid du Colombier void
fromnet(Conn * c)24763afb9a5SDavid du Colombier fromnet(Conn *c)
24863afb9a5SDavid du Colombier {
24963afb9a5SDavid du Colombier 	int fd, len;
25063afb9a5SDavid du Colombier 	char *s, *es, *r, *w;
25163afb9a5SDavid du Colombier 	ulong ex;
25263afb9a5SDavid du Colombier 	char buf[64];
25363afb9a5SDavid du Colombier 	Msg *m;
25463afb9a5SDavid du Colombier 
25563afb9a5SDavid du Colombier 	for(;;){
25663afb9a5SDavid du Colombier 		m = recvmsg(c, -1);
25763afb9a5SDavid du Colombier 		if(m == nil)
25863afb9a5SDavid du Colombier 			break;
25963afb9a5SDavid du Colombier 		switch(m->type){
26063afb9a5SDavid du Colombier 		default:
26163afb9a5SDavid du Colombier 			badmsg(m, 0);
26263afb9a5SDavid du Colombier 
26363afb9a5SDavid du Colombier 		case SSH_SMSG_EXITSTATUS:
26463afb9a5SDavid du Colombier 			ex = getlong(m);
26563afb9a5SDavid du Colombier 			if(ex==0)
26663afb9a5SDavid du Colombier 				exits(0);
26763afb9a5SDavid du Colombier 			sprint(buf, "%lud", ex);
26863afb9a5SDavid du Colombier 			exits(buf);
26963afb9a5SDavid du Colombier 
27063afb9a5SDavid du Colombier 		case SSH_MSG_DISCONNECT:
27163afb9a5SDavid du Colombier 			s = getstring(m);
27263afb9a5SDavid du Colombier 			error("disconnect: %s", s);
27363afb9a5SDavid du Colombier 
27463afb9a5SDavid du Colombier 		/*
27563afb9a5SDavid du Colombier 		 * If we ever add reverse port forwarding, we'll have to
27663afb9a5SDavid du Colombier 		 * revisit this.  It assumes that the agent connections are
27763afb9a5SDavid du Colombier 		 * the only ones.
27863afb9a5SDavid du Colombier 		 */
27963afb9a5SDavid du Colombier 		case SSH_SMSG_AGENT_OPEN:
28063afb9a5SDavid du Colombier 			if(!forwardagent)
28163afb9a5SDavid du Colombier 				error("server tried to use agent forwarding");
28263afb9a5SDavid du Colombier 			handleagentopen(m);
28363afb9a5SDavid du Colombier 			break;
28463afb9a5SDavid du Colombier 		case SSH_MSG_CHANNEL_INPUT_EOF:
28563afb9a5SDavid du Colombier 			if(!forwardagent)
28663afb9a5SDavid du Colombier 				error("server tried to use agent forwarding");
28763afb9a5SDavid du Colombier 			handleagentieof(m);
28863afb9a5SDavid du Colombier 			break;
28963afb9a5SDavid du Colombier 		case SSH_MSG_CHANNEL_OUTPUT_CLOSED:
29063afb9a5SDavid du Colombier 			if(!forwardagent)
29163afb9a5SDavid du Colombier 				error("server tried to use agent forwarding");
29263afb9a5SDavid du Colombier 			handleagentoclose(m);
29363afb9a5SDavid du Colombier 			break;
29463afb9a5SDavid du Colombier 		case SSH_MSG_CHANNEL_DATA:
29563afb9a5SDavid du Colombier 			if(!forwardagent)
29663afb9a5SDavid du Colombier 				error("server tried to use agent forwarding");
29763afb9a5SDavid du Colombier 			handleagentmsg(m);
29863afb9a5SDavid du Colombier 			break;
29963afb9a5SDavid du Colombier 
30063afb9a5SDavid du Colombier 		case SSH_SMSG_STDOUT_DATA:
30163afb9a5SDavid du Colombier 			fd = outfd;
30263afb9a5SDavid du Colombier 			goto Dataout;
30363afb9a5SDavid du Colombier 		case SSH_SMSG_STDERR_DATA:
30463afb9a5SDavid du Colombier 			fd = 2;
30563afb9a5SDavid du Colombier 			goto Dataout;
30663afb9a5SDavid du Colombier 		Dataout:
30763afb9a5SDavid du Colombier 			len = getlong(m);
30863afb9a5SDavid du Colombier 			s = (char*)getbytes(m, len);
30963afb9a5SDavid du Colombier 			if(crstrip){
31063afb9a5SDavid du Colombier 				es = s+len;
31163afb9a5SDavid du Colombier 				for(r=w=s; r<es; r++)
31263afb9a5SDavid du Colombier 					if(*r != '\r')
31363afb9a5SDavid du Colombier 						*w++ = *r;
31463afb9a5SDavid du Colombier 				len = w-s;
31563afb9a5SDavid du Colombier 			}
31663afb9a5SDavid du Colombier 			write(fd, s, len);
31763afb9a5SDavid du Colombier 			break;
31863afb9a5SDavid du Colombier 		}
31963afb9a5SDavid du Colombier 		free(m);
32063afb9a5SDavid du Colombier 	}
32163afb9a5SDavid du Colombier }
32263afb9a5SDavid du Colombier 
32363afb9a5SDavid du Colombier /*
32463afb9a5SDavid du Colombier  *  turn keyboard raw mode on
32563afb9a5SDavid du Colombier  */
32663afb9a5SDavid du Colombier static void
rawon(void)32763afb9a5SDavid du Colombier rawon(void)
32863afb9a5SDavid du Colombier {
32963afb9a5SDavid du Colombier 	if(raw)
33063afb9a5SDavid du Colombier 		return;
33163afb9a5SDavid du Colombier 	if(cooked)
33263afb9a5SDavid du Colombier 		return;
33363afb9a5SDavid du Colombier 	if(consctl < 0)
33463afb9a5SDavid du Colombier 		consctl = open("/dev/consctl", OWRITE);
33563afb9a5SDavid du Colombier 	if(consctl < 0)
33663afb9a5SDavid du Colombier 		return;
33763afb9a5SDavid du Colombier 	if(write(consctl, "rawon", 5) != 5)
33863afb9a5SDavid du Colombier 		return;
33963afb9a5SDavid du Colombier 	raw = 1;
34063afb9a5SDavid du Colombier }
34163afb9a5SDavid du Colombier 
34263afb9a5SDavid du Colombier /*
34363afb9a5SDavid du Colombier  *  turn keyboard raw mode off
34463afb9a5SDavid du Colombier  */
34563afb9a5SDavid du Colombier static void
rawoff(void)34663afb9a5SDavid du Colombier rawoff(void)
34763afb9a5SDavid du Colombier {
34863afb9a5SDavid du Colombier 	if(raw == 0)
34963afb9a5SDavid du Colombier 		return;
35063afb9a5SDavid du Colombier 	if(consctl < 0)
35163afb9a5SDavid du Colombier 		return;
35263afb9a5SDavid du Colombier 	if(write(consctl, "rawoff", 6) != 6)
35363afb9a5SDavid du Colombier 		return;
35463afb9a5SDavid du Colombier 	close(consctl);
35563afb9a5SDavid du Colombier 	consctl = -1;
35663afb9a5SDavid du Colombier 	raw = 0;
35763afb9a5SDavid du Colombier }
35863afb9a5SDavid du Colombier 
35963afb9a5SDavid du Colombier /*
36063afb9a5SDavid du Colombier  *  control menu
36163afb9a5SDavid du Colombier  */
36263afb9a5SDavid du Colombier #define STDHELP	"\t(q)uit, (i)nterrupt, toggle printing (r)eturns, (.)continue, (!cmd)\n"
36363afb9a5SDavid du Colombier 
36463afb9a5SDavid du Colombier static int
menu(Conn * c)36563afb9a5SDavid du Colombier menu(Conn *c)
36663afb9a5SDavid du Colombier {
36763afb9a5SDavid du Colombier 	char buf[1024];
36863afb9a5SDavid du Colombier 	long n;
36963afb9a5SDavid du Colombier 	int done;
37063afb9a5SDavid du Colombier 	int wasraw;
37163afb9a5SDavid du Colombier 
37263afb9a5SDavid du Colombier 	wasraw = raw;
37363afb9a5SDavid du Colombier 	if(wasraw)
37463afb9a5SDavid du Colombier 		rawoff();
37563afb9a5SDavid du Colombier 
37663afb9a5SDavid du Colombier 	buf[0] = '?';
37763afb9a5SDavid du Colombier 	fprint(2, ">>> ");
37863afb9a5SDavid du Colombier 	for(done = 0; !done; ){
37963afb9a5SDavid du Colombier 		n = read(0, buf, sizeof(buf)-1);
38063afb9a5SDavid du Colombier 		if(n <= 0)
38163afb9a5SDavid du Colombier 			return -1;
38263afb9a5SDavid du Colombier 		buf[n] = 0;
38363afb9a5SDavid du Colombier 		switch(buf[0]){
38463afb9a5SDavid du Colombier 		case '!':
38563afb9a5SDavid du Colombier 			print(buf);
38663afb9a5SDavid du Colombier 			system(c, buf+1);
38763afb9a5SDavid du Colombier 			print("!\n");
38863afb9a5SDavid du Colombier 			done = 1;
38963afb9a5SDavid du Colombier 			break;
39063afb9a5SDavid du Colombier 		case 'i':
39163afb9a5SDavid du Colombier 			buf[0] = 0x1c;
39263afb9a5SDavid du Colombier 			sendwritemsg(c, buf, 1);
39363afb9a5SDavid du Colombier 			done = 1;
39463afb9a5SDavid du Colombier 			break;
39563afb9a5SDavid du Colombier 		case '.':
39663afb9a5SDavid du Colombier 		case 'q':
39763afb9a5SDavid du Colombier 			done = 1;
39863afb9a5SDavid du Colombier 			break;
39963afb9a5SDavid du Colombier 		case 'r':
40063afb9a5SDavid du Colombier 			crstrip = 1-crstrip;
40163afb9a5SDavid du Colombier 			done = 1;
40263afb9a5SDavid du Colombier 			break;
40363afb9a5SDavid du Colombier 		default:
40463afb9a5SDavid du Colombier 			fprint(2, STDHELP);
40563afb9a5SDavid du Colombier 			break;
40663afb9a5SDavid du Colombier 		}
40763afb9a5SDavid du Colombier 		if(!done)
40863afb9a5SDavid du Colombier 			fprint(2, ">>> ");
40963afb9a5SDavid du Colombier 	}
41063afb9a5SDavid du Colombier 
41163afb9a5SDavid du Colombier 	if(wasraw)
41263afb9a5SDavid du Colombier 		rawon();
41363afb9a5SDavid du Colombier 	else
41463afb9a5SDavid du Colombier 		rawoff();
41563afb9a5SDavid du Colombier 	return buf[0];
41663afb9a5SDavid du Colombier }
41763afb9a5SDavid du Colombier 
41863afb9a5SDavid du Colombier static void
sendwritemsg(Conn * c,char * buf,int n)41963afb9a5SDavid du Colombier sendwritemsg(Conn *c, char *buf, int n)
42063afb9a5SDavid du Colombier {
42163afb9a5SDavid du Colombier 	Msg *m;
42263afb9a5SDavid du Colombier 
42363afb9a5SDavid du Colombier 	if(n==0)
42463afb9a5SDavid du Colombier 		m = allocmsg(c, SSH_CMSG_EOF, 0);
42563afb9a5SDavid du Colombier 	else{
42663afb9a5SDavid du Colombier 		m = allocmsg(c, SSH_CMSG_STDIN_DATA, 4+n);
42763afb9a5SDavid du Colombier 		putlong(m, n);
42863afb9a5SDavid du Colombier 		putbytes(m, buf, n);
42963afb9a5SDavid du Colombier 	}
43063afb9a5SDavid du Colombier 	sendmsg(m);
43163afb9a5SDavid du Colombier }
43263afb9a5SDavid du Colombier 
43363afb9a5SDavid du Colombier /*
43463afb9a5SDavid du Colombier  *  run a command with the network connection as standard IO
43563afb9a5SDavid du Colombier  */
43663afb9a5SDavid du Colombier static void
system(Conn * c,char * cmd)43763afb9a5SDavid du Colombier system(Conn *c, char *cmd)
43863afb9a5SDavid du Colombier {
43963afb9a5SDavid du Colombier 	int pid;
44063afb9a5SDavid du Colombier 	int p;
44163afb9a5SDavid du Colombier 	int pfd[2];
44263afb9a5SDavid du Colombier 	int n;
44363afb9a5SDavid du Colombier 	int wasconsctl;
44463afb9a5SDavid du Colombier 	char buf[4096];
44563afb9a5SDavid du Colombier 
44663afb9a5SDavid du Colombier 	if(pipe(pfd) < 0){
44763afb9a5SDavid du Colombier 		perror("pipe");
44863afb9a5SDavid du Colombier 		return;
44963afb9a5SDavid du Colombier 	}
45063afb9a5SDavid du Colombier 	outfd = pfd[1];
45163afb9a5SDavid du Colombier 
45263afb9a5SDavid du Colombier 	wasconsctl = consctl;
45363afb9a5SDavid du Colombier 	close(consctl);
45463afb9a5SDavid du Colombier 	consctl = -1;
45563afb9a5SDavid du Colombier 	switch(pid = fork()){
45663afb9a5SDavid du Colombier 	case -1:
45763afb9a5SDavid du Colombier 		perror("con");
45863afb9a5SDavid du Colombier 		return;
45963afb9a5SDavid du Colombier 	case 0:
46063afb9a5SDavid du Colombier 		close(pfd[1]);
46163afb9a5SDavid du Colombier 		dup(pfd[0], 0);
46263afb9a5SDavid du Colombier 		dup(pfd[0], 1);
46363afb9a5SDavid du Colombier 		close(c->fd[0]);	/* same as c->fd[1] */
46463afb9a5SDavid du Colombier 		close(pfd[0]);
46563afb9a5SDavid du Colombier 		if(*cmd)
46663afb9a5SDavid du Colombier 			execl("/bin/rc", "rc", "-c", cmd, nil);
46763afb9a5SDavid du Colombier 		else
46863afb9a5SDavid du Colombier 			execl("/bin/rc", "rc", nil);
46963afb9a5SDavid du Colombier 		perror("con");
47063afb9a5SDavid du Colombier 		exits("exec");
47163afb9a5SDavid du Colombier 		break;
47263afb9a5SDavid du Colombier 	default:
47363afb9a5SDavid du Colombier 		close(pfd[0]);
47463afb9a5SDavid du Colombier 		while((n = read(pfd[1], buf, sizeof(buf))) > 0)
47563afb9a5SDavid du Colombier 			sendwritemsg(c, buf, n);
47663afb9a5SDavid du Colombier 		p = waitpid();
47763afb9a5SDavid du Colombier 		outfd = 1;
47863afb9a5SDavid du Colombier 		close(pfd[1]);
47963afb9a5SDavid du Colombier 		if(p < 0 || p != pid)
48063afb9a5SDavid du Colombier 			return;
48163afb9a5SDavid du Colombier 		break;
48263afb9a5SDavid du Colombier 	}
48363afb9a5SDavid du Colombier 	if(wasconsctl >= 0){
48463afb9a5SDavid du Colombier 		consctl = open("/dev/consctl", OWRITE);
48563afb9a5SDavid du Colombier 		if(consctl < 0)
48663afb9a5SDavid du Colombier 			error("cannot open consctl");
48763afb9a5SDavid du Colombier 	}
48863afb9a5SDavid du Colombier }
48963afb9a5SDavid du Colombier 
49063afb9a5SDavid du Colombier static void
cookedcatchint(void *,char * msg)49163afb9a5SDavid du Colombier cookedcatchint(void*, char *msg)
49263afb9a5SDavid du Colombier {
49363afb9a5SDavid du Colombier 	if(strstr(msg, "interrupt"))
49463afb9a5SDavid du Colombier 		noted(NCONT);
49563afb9a5SDavid du Colombier 	else if(strstr(msg, "kill"))
49663afb9a5SDavid du Colombier 		noted(NDFLT);
49763afb9a5SDavid du Colombier 	else
49863afb9a5SDavid du Colombier 		noted(NCONT);
49963afb9a5SDavid du Colombier }
50063afb9a5SDavid du Colombier 
50163afb9a5SDavid du Colombier static int
wasintr(void)50263afb9a5SDavid du Colombier wasintr(void)
50363afb9a5SDavid du Colombier {
50463afb9a5SDavid du Colombier 	char err[64];
50563afb9a5SDavid du Colombier 
50663afb9a5SDavid du Colombier 	rerrstr(err, sizeof err);
50763afb9a5SDavid du Colombier 	return strstr(err, "interrupt") != 0;
50863afb9a5SDavid du Colombier }
50963afb9a5SDavid du Colombier 
51063afb9a5SDavid du Colombier void
fromstdin(Conn * c)51163afb9a5SDavid du Colombier fromstdin(Conn *c)
51263afb9a5SDavid du Colombier {
51363afb9a5SDavid du Colombier 	int n;
51463afb9a5SDavid du Colombier 	char buf[1024];
51563afb9a5SDavid du Colombier 	int pid;
51663afb9a5SDavid du Colombier 	int eofs;
51763afb9a5SDavid du Colombier 
51863afb9a5SDavid du Colombier 	switch(pid = rfork(RFMEM|RFPROC|RFNOWAIT)){
51963afb9a5SDavid du Colombier 	case -1:
52063afb9a5SDavid du Colombier 		error("fork: %r");
52163afb9a5SDavid du Colombier 	case 0:
52263afb9a5SDavid du Colombier 		break;
52363afb9a5SDavid du Colombier 	default:
52463afb9a5SDavid du Colombier 		atexitkill(pid);
52563afb9a5SDavid du Colombier 		return;
52663afb9a5SDavid du Colombier 	}
52763afb9a5SDavid du Colombier 
52863afb9a5SDavid du Colombier 	atexit(atexitkiller);
52963afb9a5SDavid du Colombier 	if(interactive)
53063afb9a5SDavid du Colombier 		rawon();
53163afb9a5SDavid du Colombier 
53263afb9a5SDavid du Colombier 	notify(cookedcatchint);
53363afb9a5SDavid du Colombier 
53463afb9a5SDavid du Colombier 	eofs = 0;
53563afb9a5SDavid du Colombier 	for(;;){
53663afb9a5SDavid du Colombier 		n = read(0, buf, sizeof(buf));
53763afb9a5SDavid du Colombier 		if(n < 0){
53863afb9a5SDavid du Colombier 			if(wasintr()){
53963afb9a5SDavid du Colombier 				if(!raw){
54063afb9a5SDavid du Colombier 					buf[0] = 0x7f;
54163afb9a5SDavid du Colombier 					n = 1;
54263afb9a5SDavid du Colombier 				}else
54363afb9a5SDavid du Colombier 					continue;
54463afb9a5SDavid du Colombier 			}else
54563afb9a5SDavid du Colombier 				break;
54663afb9a5SDavid du Colombier 		}
54763afb9a5SDavid du Colombier 		if(n == 0){
54863afb9a5SDavid du Colombier 			if(!c->interactive || ++eofs > 32)
54963afb9a5SDavid du Colombier 				break;
55063afb9a5SDavid du Colombier 		}else
55163afb9a5SDavid du Colombier 			eofs = 0;
55263afb9a5SDavid du Colombier 		if(interactive && usemenu && n && memchr(buf, 0x1c, n)) {
55363afb9a5SDavid du Colombier 			if(menu(c)=='q'){
55463afb9a5SDavid du Colombier 				sendwritemsg(c, "", 0);
55563afb9a5SDavid du Colombier 				exits("quit");
55663afb9a5SDavid du Colombier 			}
55763afb9a5SDavid du Colombier 			continue;
55863afb9a5SDavid du Colombier 		}
55963afb9a5SDavid du Colombier 		if(!raw && n==0){
56063afb9a5SDavid du Colombier 			buf[0] = 0x4;
56163afb9a5SDavid du Colombier 			n = 1;
56263afb9a5SDavid du Colombier 		}
56363afb9a5SDavid du Colombier 		sendwritemsg(c, buf, n);
56463afb9a5SDavid du Colombier 	}
56563afb9a5SDavid du Colombier 	sendwritemsg(c, "", 0);
566*58da3067SDavid du Colombier 	if(n >= 0)				/* weren't hung up upon? */
56763afb9a5SDavid du Colombier 		atexitdont(atexitkiller);
56863afb9a5SDavid du Colombier 	exits(nil);
56963afb9a5SDavid du Colombier }
57063afb9a5SDavid du Colombier 
57163afb9a5SDavid du Colombier void
winchanges(Conn * c)57263afb9a5SDavid du Colombier winchanges(Conn *c)
57363afb9a5SDavid du Colombier {
57463afb9a5SDavid du Colombier 	int nrow, ncol, width, height;
57563afb9a5SDavid du Colombier 	int pid;
57663afb9a5SDavid du Colombier 
57763afb9a5SDavid du Colombier 	switch(pid = rfork(RFMEM|RFPROC|RFNOWAIT)){
57863afb9a5SDavid du Colombier 	case -1:
57963afb9a5SDavid du Colombier 		error("fork: %r");
58063afb9a5SDavid du Colombier 	case 0:
58163afb9a5SDavid du Colombier 		break;
58263afb9a5SDavid du Colombier 	default:
58363afb9a5SDavid du Colombier 		atexitkill(pid);
58463afb9a5SDavid du Colombier 		return;
58563afb9a5SDavid du Colombier 	}
58663afb9a5SDavid du Colombier 
58763afb9a5SDavid du Colombier 	for(;;){
58863afb9a5SDavid du Colombier 		if(readgeom(&nrow, &ncol, &width, &height) < 0)
58963afb9a5SDavid du Colombier 			break;
59063afb9a5SDavid du Colombier 		sendwindowsize(c, nrow, ncol, width, height);
59163afb9a5SDavid du Colombier 	}
59263afb9a5SDavid du Colombier 	exits(nil);
59363afb9a5SDavid du Colombier }
594