xref: /plan9/sys/src/cmd/ssh1/ssh1.c (revision 63afb9a5d3f910047231762bcce0ee49fed3d07c)
1*63afb9a5SDavid du Colombier /* remote login via ssh v1 */
2*63afb9a5SDavid du Colombier #include "ssh.h"
3*63afb9a5SDavid du Colombier 
4*63afb9a5SDavid du Colombier int cooked = 0;		/* user wants cooked mode */
5*63afb9a5SDavid du Colombier int raw = 0;		/* console is in raw mode */
6*63afb9a5SDavid du Colombier int crstrip;
7*63afb9a5SDavid du Colombier int interactive = -1;
8*63afb9a5SDavid du Colombier int usemenu = 1;
9*63afb9a5SDavid du Colombier int isatty(int);
10*63afb9a5SDavid du Colombier int rawhack;
11*63afb9a5SDavid du Colombier int forwardagent = 0;
12*63afb9a5SDavid du Colombier char *buildcmd(int, char**);
13*63afb9a5SDavid du Colombier void fromnet(Conn*);
14*63afb9a5SDavid du Colombier void fromstdin(Conn*);
15*63afb9a5SDavid du Colombier void winchanges(Conn*);
16*63afb9a5SDavid du Colombier static void	sendwritemsg(Conn *c, char *buf, int n);
17*63afb9a5SDavid du Colombier 
18*63afb9a5SDavid du Colombier /*
19*63afb9a5SDavid du Colombier  * Lifted from telnet.c, con.c
20*63afb9a5SDavid du Colombier  */
21*63afb9a5SDavid du Colombier static int consctl = -1;
22*63afb9a5SDavid du Colombier static int outfd = 1;			/* changed during system */
23*63afb9a5SDavid du Colombier static void system(Conn*, char*);
24*63afb9a5SDavid du Colombier 
25*63afb9a5SDavid du Colombier Cipher *allcipher[] = {
26*63afb9a5SDavid du Colombier 	&cipherrc4,
27*63afb9a5SDavid du Colombier 	&cipherblowfish,
28*63afb9a5SDavid du Colombier 	&cipher3des,
29*63afb9a5SDavid du Colombier 	&cipherdes,
30*63afb9a5SDavid du Colombier 	&ciphernone,
31*63afb9a5SDavid du Colombier 	&ciphertwiddle,
32*63afb9a5SDavid du Colombier };
33*63afb9a5SDavid du Colombier 
34*63afb9a5SDavid du Colombier Auth *allauth[] = {
35*63afb9a5SDavid du Colombier 	&authpassword,
36*63afb9a5SDavid du Colombier 	&authrsa,
37*63afb9a5SDavid du Colombier 	&authtis,
38*63afb9a5SDavid du Colombier };
39*63afb9a5SDavid du Colombier 
40*63afb9a5SDavid du Colombier char *cipherlist = "blowfish rc4 3des";
41*63afb9a5SDavid du Colombier char *authlist = "rsa password tis";
42*63afb9a5SDavid du Colombier 
43*63afb9a5SDavid du Colombier Cipher*
44*63afb9a5SDavid du Colombier findcipher(char *name, Cipher **list, int nlist)
45*63afb9a5SDavid du Colombier {
46*63afb9a5SDavid du Colombier 	int i;
47*63afb9a5SDavid du Colombier 
48*63afb9a5SDavid du Colombier 	for(i=0; i<nlist; i++)
49*63afb9a5SDavid du Colombier 		if(strcmp(name, list[i]->name) == 0)
50*63afb9a5SDavid du Colombier 			return list[i];
51*63afb9a5SDavid du Colombier 	error("unknown cipher %s", name);
52*63afb9a5SDavid du Colombier 	return nil;
53*63afb9a5SDavid du Colombier }
54*63afb9a5SDavid du Colombier 
55*63afb9a5SDavid du Colombier Auth*
56*63afb9a5SDavid du Colombier findauth(char *name, Auth **list, int nlist)
57*63afb9a5SDavid du Colombier {
58*63afb9a5SDavid du Colombier 	int i;
59*63afb9a5SDavid du Colombier 
60*63afb9a5SDavid du Colombier 	for(i=0; i<nlist; i++)
61*63afb9a5SDavid du Colombier 		if(strcmp(name, list[i]->name) == 0)
62*63afb9a5SDavid du Colombier 			return list[i];
63*63afb9a5SDavid du Colombier 	error("unknown auth %s", name);
64*63afb9a5SDavid du Colombier 	return nil;
65*63afb9a5SDavid du Colombier }
66*63afb9a5SDavid du Colombier 
67*63afb9a5SDavid du Colombier void
68*63afb9a5SDavid du Colombier usage(void)
69*63afb9a5SDavid du Colombier {
70*63afb9a5SDavid du Colombier 	fprint(2, "usage: ssh [-CiImPpRr] [-A authlist] [-c cipherlist] [user@]hostname [cmd [args]]\n");
71*63afb9a5SDavid du Colombier 	exits("usage");
72*63afb9a5SDavid du Colombier }
73*63afb9a5SDavid du Colombier 
74*63afb9a5SDavid du Colombier void
75*63afb9a5SDavid du Colombier main(int argc, char **argv)
76*63afb9a5SDavid du Colombier {
77*63afb9a5SDavid du Colombier 	int i, dowinchange, fd, usepty;
78*63afb9a5SDavid du Colombier 	char *host, *cmd, *user, *p;
79*63afb9a5SDavid du Colombier 	char *f[16];
80*63afb9a5SDavid du Colombier 	Conn c;
81*63afb9a5SDavid du Colombier 	Msg *m;
82*63afb9a5SDavid du Colombier 
83*63afb9a5SDavid du Colombier 	fmtinstall('B', mpfmt);
84*63afb9a5SDavid du Colombier 	fmtinstall('H', encodefmt);
85*63afb9a5SDavid du Colombier 	atexit(atexitkiller);
86*63afb9a5SDavid du Colombier 	atexitkill(getpid());
87*63afb9a5SDavid du Colombier 
88*63afb9a5SDavid du Colombier 	dowinchange = 0;
89*63afb9a5SDavid du Colombier 	if(getenv("LINES"))
90*63afb9a5SDavid du Colombier 		dowinchange = 1;
91*63afb9a5SDavid du Colombier 	usepty = -1;
92*63afb9a5SDavid du Colombier 	user = nil;
93*63afb9a5SDavid du Colombier 	ARGBEGIN{
94*63afb9a5SDavid du Colombier 	case 'B':	/* undocumented, debugging */
95*63afb9a5SDavid du Colombier 		doabort = 1;
96*63afb9a5SDavid du Colombier 		break;
97*63afb9a5SDavid du Colombier 	case 'D':	/* undocumented, debugging */
98*63afb9a5SDavid du Colombier 		debuglevel = strtol(EARGF(usage()), nil, 0);
99*63afb9a5SDavid du Colombier 		break;
100*63afb9a5SDavid du Colombier 	case 'l':	/* deprecated */
101*63afb9a5SDavid du Colombier 	case 'u':
102*63afb9a5SDavid du Colombier 		user = EARGF(usage());
103*63afb9a5SDavid du Colombier 		break;
104*63afb9a5SDavid du Colombier 	case 'a':	/* used by Unix scp implementations; we must ignore them. */
105*63afb9a5SDavid du Colombier 	case 'x':
106*63afb9a5SDavid du Colombier 		break;
107*63afb9a5SDavid du Colombier 
108*63afb9a5SDavid du Colombier 	case 'A':
109*63afb9a5SDavid du Colombier 		authlist = EARGF(usage());
110*63afb9a5SDavid du Colombier 		break;
111*63afb9a5SDavid du Colombier 	case 'C':
112*63afb9a5SDavid du Colombier 		cooked = 1;
113*63afb9a5SDavid du Colombier 		break;
114*63afb9a5SDavid du Colombier 	case 'c':
115*63afb9a5SDavid du Colombier 		cipherlist = EARGF(usage());
116*63afb9a5SDavid du Colombier 		break;
117*63afb9a5SDavid du Colombier 	case 'f':
118*63afb9a5SDavid du Colombier 		forwardagent = 1;
119*63afb9a5SDavid du Colombier 		break;
120*63afb9a5SDavid du Colombier 	case 'I':
121*63afb9a5SDavid du Colombier 		interactive = 0;
122*63afb9a5SDavid du Colombier 		break;
123*63afb9a5SDavid du Colombier 	case 'i':
124*63afb9a5SDavid du Colombier 		interactive = 1;
125*63afb9a5SDavid du Colombier 		break;
126*63afb9a5SDavid du Colombier 	case 'm':
127*63afb9a5SDavid du Colombier 		usemenu = 0;
128*63afb9a5SDavid du Colombier 		break;
129*63afb9a5SDavid du Colombier 	case 'P':
130*63afb9a5SDavid du Colombier 		usepty = 0;
131*63afb9a5SDavid du Colombier 		break;
132*63afb9a5SDavid du Colombier 	case 'p':
133*63afb9a5SDavid du Colombier 		usepty = 1;
134*63afb9a5SDavid du Colombier 		break;
135*63afb9a5SDavid du Colombier 	case 'R':
136*63afb9a5SDavid du Colombier 		rawhack = 1;
137*63afb9a5SDavid du Colombier 		break;
138*63afb9a5SDavid du Colombier 	case 'r':
139*63afb9a5SDavid du Colombier 		crstrip = 1;
140*63afb9a5SDavid du Colombier 		break;
141*63afb9a5SDavid du Colombier 	default:
142*63afb9a5SDavid du Colombier 		usage();
143*63afb9a5SDavid du Colombier 	}ARGEND
144*63afb9a5SDavid du Colombier 
145*63afb9a5SDavid du Colombier 	if(argc < 1)
146*63afb9a5SDavid du Colombier 		usage();
147*63afb9a5SDavid du Colombier 
148*63afb9a5SDavid du Colombier 	host = argv[0];
149*63afb9a5SDavid du Colombier 
150*63afb9a5SDavid du Colombier 	cmd = nil;
151*63afb9a5SDavid du Colombier 	if(argc > 1)
152*63afb9a5SDavid du Colombier 		cmd = buildcmd(argc-1, argv+1);
153*63afb9a5SDavid du Colombier 
154*63afb9a5SDavid du Colombier 	if((p = strchr(host, '@')) != nil){
155*63afb9a5SDavid du Colombier 		*p++ = '\0';
156*63afb9a5SDavid du Colombier 		user = host;
157*63afb9a5SDavid du Colombier 		host = p;
158*63afb9a5SDavid du Colombier 	}
159*63afb9a5SDavid du Colombier 	if(user == nil)
160*63afb9a5SDavid du Colombier 		user = getenv("user");
161*63afb9a5SDavid du Colombier 	if(user == nil)
162*63afb9a5SDavid du Colombier 		sysfatal("cannot find user name");
163*63afb9a5SDavid du Colombier 
164*63afb9a5SDavid du Colombier 	privatefactotum();
165*63afb9a5SDavid du Colombier 	if(interactive==-1)
166*63afb9a5SDavid du Colombier 		interactive = isatty(0);
167*63afb9a5SDavid du Colombier 
168*63afb9a5SDavid du Colombier 	if((fd = dial(netmkaddr(host, "tcp", "ssh"), nil, nil, nil)) < 0)
169*63afb9a5SDavid du Colombier 		sysfatal("dialing %s: %r", host);
170*63afb9a5SDavid du Colombier 
171*63afb9a5SDavid du Colombier 	memset(&c, 0, sizeof c);
172*63afb9a5SDavid du Colombier 	c.interactive = interactive;
173*63afb9a5SDavid du Colombier 	c.fd[0] = c.fd[1] = fd;
174*63afb9a5SDavid du Colombier 	c.user = user;
175*63afb9a5SDavid du Colombier 	c.host = host;
176*63afb9a5SDavid du Colombier 	setaliases(&c, host);
177*63afb9a5SDavid du Colombier 
178*63afb9a5SDavid du Colombier 	c.nokcipher = getfields(cipherlist, f, nelem(f), 1, ", ");
179*63afb9a5SDavid du Colombier 	c.okcipher = emalloc(sizeof(Cipher*)*c.nokcipher);
180*63afb9a5SDavid du Colombier 	for(i=0; i<c.nokcipher; i++)
181*63afb9a5SDavid du Colombier 		c.okcipher[i] = findcipher(f[i], allcipher, nelem(allcipher));
182*63afb9a5SDavid du Colombier 
183*63afb9a5SDavid du Colombier 	c.nokauth = getfields(authlist, f, nelem(f), 1, ", ");
184*63afb9a5SDavid du Colombier 	c.okauth = emalloc(sizeof(Auth*)*c.nokauth);
185*63afb9a5SDavid du Colombier 	for(i=0; i<c.nokauth; i++)
186*63afb9a5SDavid du Colombier 		c.okauth[i] = findauth(f[i], allauth, nelem(allauth));
187*63afb9a5SDavid du Colombier 
188*63afb9a5SDavid du Colombier 	sshclienthandshake(&c);
189*63afb9a5SDavid du Colombier 
190*63afb9a5SDavid du Colombier 	if(forwardagent){
191*63afb9a5SDavid du Colombier 		if(startagent(&c) < 0)
192*63afb9a5SDavid du Colombier 			forwardagent = 0;
193*63afb9a5SDavid du Colombier 	}
194*63afb9a5SDavid du Colombier 	if(usepty == -1)
195*63afb9a5SDavid du Colombier 		usepty = cmd==nil;
196*63afb9a5SDavid du Colombier 	if(usepty)
197*63afb9a5SDavid du Colombier 		requestpty(&c);
198*63afb9a5SDavid du Colombier 	if(cmd){
199*63afb9a5SDavid du Colombier 		m = allocmsg(&c, SSH_CMSG_EXEC_CMD, 4+strlen(cmd));
200*63afb9a5SDavid du Colombier 		putstring(m, cmd);
201*63afb9a5SDavid du Colombier 	}else
202*63afb9a5SDavid du Colombier 		m = allocmsg(&c, SSH_CMSG_EXEC_SHELL, 0);
203*63afb9a5SDavid du Colombier 	sendmsg(m);
204*63afb9a5SDavid du Colombier 
205*63afb9a5SDavid du Colombier 	fromstdin(&c);
206*63afb9a5SDavid du Colombier 	rfork(RFNOTEG);	/* only fromstdin gets notes */
207*63afb9a5SDavid du Colombier 	if(dowinchange)
208*63afb9a5SDavid du Colombier 		winchanges(&c);
209*63afb9a5SDavid du Colombier 	fromnet(&c);
210*63afb9a5SDavid du Colombier 	exits(0);
211*63afb9a5SDavid du Colombier }
212*63afb9a5SDavid du Colombier 
213*63afb9a5SDavid du Colombier int
214*63afb9a5SDavid du Colombier isatty(int fd)
215*63afb9a5SDavid du Colombier {
216*63afb9a5SDavid du Colombier 	char buf[64];
217*63afb9a5SDavid du Colombier 
218*63afb9a5SDavid du Colombier 	buf[0] = '\0';
219*63afb9a5SDavid du Colombier 	fd2path(fd, buf, sizeof buf);
220*63afb9a5SDavid du Colombier 	if(strlen(buf)>=9 && strcmp(buf+strlen(buf)-9, "/dev/cons")==0)
221*63afb9a5SDavid du Colombier 		return 1;
222*63afb9a5SDavid du Colombier 	return 0;
223*63afb9a5SDavid du Colombier }
224*63afb9a5SDavid du Colombier 
225*63afb9a5SDavid du Colombier char*
226*63afb9a5SDavid du Colombier buildcmd(int argc, char **argv)
227*63afb9a5SDavid du Colombier {
228*63afb9a5SDavid du Colombier 	int i, len;
229*63afb9a5SDavid du Colombier 	char *s, *t;
230*63afb9a5SDavid du Colombier 
231*63afb9a5SDavid du Colombier 	len = argc-1;
232*63afb9a5SDavid du Colombier 	for(i=0; i<argc; i++)
233*63afb9a5SDavid du Colombier 		len += strlen(argv[i]);
234*63afb9a5SDavid du Colombier 	s = emalloc(len+1);
235*63afb9a5SDavid du Colombier 	t = s;
236*63afb9a5SDavid du Colombier 	for(i=0; i<argc; i++){
237*63afb9a5SDavid du Colombier 		if(i)
238*63afb9a5SDavid du Colombier 			*t++ = ' ';
239*63afb9a5SDavid du Colombier 		strcpy(t, argv[i]);
240*63afb9a5SDavid du Colombier 		t += strlen(t);
241*63afb9a5SDavid du Colombier 	}
242*63afb9a5SDavid du Colombier 	return s;
243*63afb9a5SDavid du Colombier }
244*63afb9a5SDavid du Colombier 
245*63afb9a5SDavid du Colombier 
246*63afb9a5SDavid du Colombier void
247*63afb9a5SDavid du Colombier fromnet(Conn *c)
248*63afb9a5SDavid du Colombier {
249*63afb9a5SDavid du Colombier 	int fd, len;
250*63afb9a5SDavid du Colombier 	char *s, *es, *r, *w;
251*63afb9a5SDavid du Colombier 	ulong ex;
252*63afb9a5SDavid du Colombier 	char buf[64];
253*63afb9a5SDavid du Colombier 	Msg *m;
254*63afb9a5SDavid du Colombier 
255*63afb9a5SDavid du Colombier 	for(;;){
256*63afb9a5SDavid du Colombier 		m = recvmsg(c, -1);
257*63afb9a5SDavid du Colombier 		if(m == nil)
258*63afb9a5SDavid du Colombier 			break;
259*63afb9a5SDavid du Colombier 		switch(m->type){
260*63afb9a5SDavid du Colombier 		default:
261*63afb9a5SDavid du Colombier 			badmsg(m, 0);
262*63afb9a5SDavid du Colombier 
263*63afb9a5SDavid du Colombier 		case SSH_SMSG_EXITSTATUS:
264*63afb9a5SDavid du Colombier 			ex = getlong(m);
265*63afb9a5SDavid du Colombier 			if(ex==0)
266*63afb9a5SDavid du Colombier 				exits(0);
267*63afb9a5SDavid du Colombier 			sprint(buf, "%lud", ex);
268*63afb9a5SDavid du Colombier 			exits(buf);
269*63afb9a5SDavid du Colombier 
270*63afb9a5SDavid du Colombier 		case SSH_MSG_DISCONNECT:
271*63afb9a5SDavid du Colombier 			s = getstring(m);
272*63afb9a5SDavid du Colombier 			error("disconnect: %s", s);
273*63afb9a5SDavid du Colombier 
274*63afb9a5SDavid du Colombier 		/*
275*63afb9a5SDavid du Colombier 		 * If we ever add reverse port forwarding, we'll have to
276*63afb9a5SDavid du Colombier 		 * revisit this.  It assumes that the agent connections are
277*63afb9a5SDavid du Colombier 		 * the only ones.
278*63afb9a5SDavid du Colombier 		 */
279*63afb9a5SDavid du Colombier 		case SSH_SMSG_AGENT_OPEN:
280*63afb9a5SDavid du Colombier 			if(!forwardagent)
281*63afb9a5SDavid du Colombier 				error("server tried to use agent forwarding");
282*63afb9a5SDavid du Colombier 			handleagentopen(m);
283*63afb9a5SDavid du Colombier 			break;
284*63afb9a5SDavid du Colombier 		case SSH_MSG_CHANNEL_INPUT_EOF:
285*63afb9a5SDavid du Colombier 			if(!forwardagent)
286*63afb9a5SDavid du Colombier 				error("server tried to use agent forwarding");
287*63afb9a5SDavid du Colombier 			handleagentieof(m);
288*63afb9a5SDavid du Colombier 			break;
289*63afb9a5SDavid du Colombier 		case SSH_MSG_CHANNEL_OUTPUT_CLOSED:
290*63afb9a5SDavid du Colombier 			if(!forwardagent)
291*63afb9a5SDavid du Colombier 				error("server tried to use agent forwarding");
292*63afb9a5SDavid du Colombier 			handleagentoclose(m);
293*63afb9a5SDavid du Colombier 			break;
294*63afb9a5SDavid du Colombier 		case SSH_MSG_CHANNEL_DATA:
295*63afb9a5SDavid du Colombier 			if(!forwardagent)
296*63afb9a5SDavid du Colombier 				error("server tried to use agent forwarding");
297*63afb9a5SDavid du Colombier 			handleagentmsg(m);
298*63afb9a5SDavid du Colombier 			break;
299*63afb9a5SDavid du Colombier 
300*63afb9a5SDavid du Colombier 		case SSH_SMSG_STDOUT_DATA:
301*63afb9a5SDavid du Colombier 			fd = outfd;
302*63afb9a5SDavid du Colombier 			goto Dataout;
303*63afb9a5SDavid du Colombier 		case SSH_SMSG_STDERR_DATA:
304*63afb9a5SDavid du Colombier 			fd = 2;
305*63afb9a5SDavid du Colombier 			goto Dataout;
306*63afb9a5SDavid du Colombier 		Dataout:
307*63afb9a5SDavid du Colombier 			len = getlong(m);
308*63afb9a5SDavid du Colombier 			s = (char*)getbytes(m, len);
309*63afb9a5SDavid du Colombier 			if(crstrip){
310*63afb9a5SDavid du Colombier 				es = s+len;
311*63afb9a5SDavid du Colombier 				for(r=w=s; r<es; r++)
312*63afb9a5SDavid du Colombier 					if(*r != '\r')
313*63afb9a5SDavid du Colombier 						*w++ = *r;
314*63afb9a5SDavid du Colombier 				len = w-s;
315*63afb9a5SDavid du Colombier 			}
316*63afb9a5SDavid du Colombier 			write(fd, s, len);
317*63afb9a5SDavid du Colombier 			break;
318*63afb9a5SDavid du Colombier 		}
319*63afb9a5SDavid du Colombier 		free(m);
320*63afb9a5SDavid du Colombier 	}
321*63afb9a5SDavid du Colombier }
322*63afb9a5SDavid du Colombier 
323*63afb9a5SDavid du Colombier /*
324*63afb9a5SDavid du Colombier  *  turn keyboard raw mode on
325*63afb9a5SDavid du Colombier  */
326*63afb9a5SDavid du Colombier static void
327*63afb9a5SDavid du Colombier rawon(void)
328*63afb9a5SDavid du Colombier {
329*63afb9a5SDavid du Colombier 	if(raw)
330*63afb9a5SDavid du Colombier 		return;
331*63afb9a5SDavid du Colombier 	if(cooked)
332*63afb9a5SDavid du Colombier 		return;
333*63afb9a5SDavid du Colombier 	if(consctl < 0)
334*63afb9a5SDavid du Colombier 		consctl = open("/dev/consctl", OWRITE);
335*63afb9a5SDavid du Colombier 	if(consctl < 0)
336*63afb9a5SDavid du Colombier 		return;
337*63afb9a5SDavid du Colombier 	if(write(consctl, "rawon", 5) != 5)
338*63afb9a5SDavid du Colombier 		return;
339*63afb9a5SDavid du Colombier 	raw = 1;
340*63afb9a5SDavid du Colombier }
341*63afb9a5SDavid du Colombier 
342*63afb9a5SDavid du Colombier /*
343*63afb9a5SDavid du Colombier  *  turn keyboard raw mode off
344*63afb9a5SDavid du Colombier  */
345*63afb9a5SDavid du Colombier static void
346*63afb9a5SDavid du Colombier rawoff(void)
347*63afb9a5SDavid du Colombier {
348*63afb9a5SDavid du Colombier 	if(raw == 0)
349*63afb9a5SDavid du Colombier 		return;
350*63afb9a5SDavid du Colombier 	if(consctl < 0)
351*63afb9a5SDavid du Colombier 		return;
352*63afb9a5SDavid du Colombier 	if(write(consctl, "rawoff", 6) != 6)
353*63afb9a5SDavid du Colombier 		return;
354*63afb9a5SDavid du Colombier 	close(consctl);
355*63afb9a5SDavid du Colombier 	consctl = -1;
356*63afb9a5SDavid du Colombier 	raw = 0;
357*63afb9a5SDavid du Colombier }
358*63afb9a5SDavid du Colombier 
359*63afb9a5SDavid du Colombier /*
360*63afb9a5SDavid du Colombier  *  control menu
361*63afb9a5SDavid du Colombier  */
362*63afb9a5SDavid du Colombier #define STDHELP	"\t(q)uit, (i)nterrupt, toggle printing (r)eturns, (.)continue, (!cmd)\n"
363*63afb9a5SDavid du Colombier 
364*63afb9a5SDavid du Colombier static int
365*63afb9a5SDavid du Colombier menu(Conn *c)
366*63afb9a5SDavid du Colombier {
367*63afb9a5SDavid du Colombier 	char buf[1024];
368*63afb9a5SDavid du Colombier 	long n;
369*63afb9a5SDavid du Colombier 	int done;
370*63afb9a5SDavid du Colombier 	int wasraw;
371*63afb9a5SDavid du Colombier 
372*63afb9a5SDavid du Colombier 	wasraw = raw;
373*63afb9a5SDavid du Colombier 	if(wasraw)
374*63afb9a5SDavid du Colombier 		rawoff();
375*63afb9a5SDavid du Colombier 
376*63afb9a5SDavid du Colombier 	buf[0] = '?';
377*63afb9a5SDavid du Colombier 	fprint(2, ">>> ");
378*63afb9a5SDavid du Colombier 	for(done = 0; !done; ){
379*63afb9a5SDavid du Colombier 		n = read(0, buf, sizeof(buf)-1);
380*63afb9a5SDavid du Colombier 		if(n <= 0)
381*63afb9a5SDavid du Colombier 			return -1;
382*63afb9a5SDavid du Colombier 		buf[n] = 0;
383*63afb9a5SDavid du Colombier 		switch(buf[0]){
384*63afb9a5SDavid du Colombier 		case '!':
385*63afb9a5SDavid du Colombier 			print(buf);
386*63afb9a5SDavid du Colombier 			system(c, buf+1);
387*63afb9a5SDavid du Colombier 			print("!\n");
388*63afb9a5SDavid du Colombier 			done = 1;
389*63afb9a5SDavid du Colombier 			break;
390*63afb9a5SDavid du Colombier 		case 'i':
391*63afb9a5SDavid du Colombier 			buf[0] = 0x1c;
392*63afb9a5SDavid du Colombier 			sendwritemsg(c, buf, 1);
393*63afb9a5SDavid du Colombier 			done = 1;
394*63afb9a5SDavid du Colombier 			break;
395*63afb9a5SDavid du Colombier 		case '.':
396*63afb9a5SDavid du Colombier 		case 'q':
397*63afb9a5SDavid du Colombier 			done = 1;
398*63afb9a5SDavid du Colombier 			break;
399*63afb9a5SDavid du Colombier 		case 'r':
400*63afb9a5SDavid du Colombier 			crstrip = 1-crstrip;
401*63afb9a5SDavid du Colombier 			done = 1;
402*63afb9a5SDavid du Colombier 			break;
403*63afb9a5SDavid du Colombier 		default:
404*63afb9a5SDavid du Colombier 			fprint(2, STDHELP);
405*63afb9a5SDavid du Colombier 			break;
406*63afb9a5SDavid du Colombier 		}
407*63afb9a5SDavid du Colombier 		if(!done)
408*63afb9a5SDavid du Colombier 			fprint(2, ">>> ");
409*63afb9a5SDavid du Colombier 	}
410*63afb9a5SDavid du Colombier 
411*63afb9a5SDavid du Colombier 	if(wasraw)
412*63afb9a5SDavid du Colombier 		rawon();
413*63afb9a5SDavid du Colombier 	else
414*63afb9a5SDavid du Colombier 		rawoff();
415*63afb9a5SDavid du Colombier 	return buf[0];
416*63afb9a5SDavid du Colombier }
417*63afb9a5SDavid du Colombier 
418*63afb9a5SDavid du Colombier static void
419*63afb9a5SDavid du Colombier sendwritemsg(Conn *c, char *buf, int n)
420*63afb9a5SDavid du Colombier {
421*63afb9a5SDavid du Colombier 	Msg *m;
422*63afb9a5SDavid du Colombier 
423*63afb9a5SDavid du Colombier 	if(n==0)
424*63afb9a5SDavid du Colombier 		m = allocmsg(c, SSH_CMSG_EOF, 0);
425*63afb9a5SDavid du Colombier 	else{
426*63afb9a5SDavid du Colombier 		m = allocmsg(c, SSH_CMSG_STDIN_DATA, 4+n);
427*63afb9a5SDavid du Colombier 		putlong(m, n);
428*63afb9a5SDavid du Colombier 		putbytes(m, buf, n);
429*63afb9a5SDavid du Colombier 	}
430*63afb9a5SDavid du Colombier 	sendmsg(m);
431*63afb9a5SDavid du Colombier }
432*63afb9a5SDavid du Colombier 
433*63afb9a5SDavid du Colombier /*
434*63afb9a5SDavid du Colombier  *  run a command with the network connection as standard IO
435*63afb9a5SDavid du Colombier  */
436*63afb9a5SDavid du Colombier static void
437*63afb9a5SDavid du Colombier system(Conn *c, char *cmd)
438*63afb9a5SDavid du Colombier {
439*63afb9a5SDavid du Colombier 	int pid;
440*63afb9a5SDavid du Colombier 	int p;
441*63afb9a5SDavid du Colombier 	int pfd[2];
442*63afb9a5SDavid du Colombier 	int n;
443*63afb9a5SDavid du Colombier 	int wasconsctl;
444*63afb9a5SDavid du Colombier 	char buf[4096];
445*63afb9a5SDavid du Colombier 
446*63afb9a5SDavid du Colombier 	if(pipe(pfd) < 0){
447*63afb9a5SDavid du Colombier 		perror("pipe");
448*63afb9a5SDavid du Colombier 		return;
449*63afb9a5SDavid du Colombier 	}
450*63afb9a5SDavid du Colombier 	outfd = pfd[1];
451*63afb9a5SDavid du Colombier 
452*63afb9a5SDavid du Colombier 	wasconsctl = consctl;
453*63afb9a5SDavid du Colombier 	close(consctl);
454*63afb9a5SDavid du Colombier 	consctl = -1;
455*63afb9a5SDavid du Colombier 	switch(pid = fork()){
456*63afb9a5SDavid du Colombier 	case -1:
457*63afb9a5SDavid du Colombier 		perror("con");
458*63afb9a5SDavid du Colombier 		return;
459*63afb9a5SDavid du Colombier 	case 0:
460*63afb9a5SDavid du Colombier 		close(pfd[1]);
461*63afb9a5SDavid du Colombier 		dup(pfd[0], 0);
462*63afb9a5SDavid du Colombier 		dup(pfd[0], 1);
463*63afb9a5SDavid du Colombier 		close(c->fd[0]);	/* same as c->fd[1] */
464*63afb9a5SDavid du Colombier 		close(pfd[0]);
465*63afb9a5SDavid du Colombier 		if(*cmd)
466*63afb9a5SDavid du Colombier 			execl("/bin/rc", "rc", "-c", cmd, nil);
467*63afb9a5SDavid du Colombier 		else
468*63afb9a5SDavid du Colombier 			execl("/bin/rc", "rc", nil);
469*63afb9a5SDavid du Colombier 		perror("con");
470*63afb9a5SDavid du Colombier 		exits("exec");
471*63afb9a5SDavid du Colombier 		break;
472*63afb9a5SDavid du Colombier 	default:
473*63afb9a5SDavid du Colombier 		close(pfd[0]);
474*63afb9a5SDavid du Colombier 		while((n = read(pfd[1], buf, sizeof(buf))) > 0)
475*63afb9a5SDavid du Colombier 			sendwritemsg(c, buf, n);
476*63afb9a5SDavid du Colombier 		p = waitpid();
477*63afb9a5SDavid du Colombier 		outfd = 1;
478*63afb9a5SDavid du Colombier 		close(pfd[1]);
479*63afb9a5SDavid du Colombier 		if(p < 0 || p != pid)
480*63afb9a5SDavid du Colombier 			return;
481*63afb9a5SDavid du Colombier 		break;
482*63afb9a5SDavid du Colombier 	}
483*63afb9a5SDavid du Colombier 	if(wasconsctl >= 0){
484*63afb9a5SDavid du Colombier 		consctl = open("/dev/consctl", OWRITE);
485*63afb9a5SDavid du Colombier 		if(consctl < 0)
486*63afb9a5SDavid du Colombier 			error("cannot open consctl");
487*63afb9a5SDavid du Colombier 	}
488*63afb9a5SDavid du Colombier }
489*63afb9a5SDavid du Colombier 
490*63afb9a5SDavid du Colombier static void
491*63afb9a5SDavid du Colombier cookedcatchint(void*, char *msg)
492*63afb9a5SDavid du Colombier {
493*63afb9a5SDavid du Colombier 	if(strstr(msg, "interrupt"))
494*63afb9a5SDavid du Colombier 		noted(NCONT);
495*63afb9a5SDavid du Colombier 	else if(strstr(msg, "kill"))
496*63afb9a5SDavid du Colombier 		noted(NDFLT);
497*63afb9a5SDavid du Colombier 	else
498*63afb9a5SDavid du Colombier 		noted(NCONT);
499*63afb9a5SDavid du Colombier }
500*63afb9a5SDavid du Colombier 
501*63afb9a5SDavid du Colombier static int
502*63afb9a5SDavid du Colombier wasintr(void)
503*63afb9a5SDavid du Colombier {
504*63afb9a5SDavid du Colombier 	char err[64];
505*63afb9a5SDavid du Colombier 
506*63afb9a5SDavid du Colombier 	rerrstr(err, sizeof err);
507*63afb9a5SDavid du Colombier 	return strstr(err, "interrupt") != 0;
508*63afb9a5SDavid du Colombier }
509*63afb9a5SDavid du Colombier 
510*63afb9a5SDavid du Colombier void
511*63afb9a5SDavid du Colombier fromstdin(Conn *c)
512*63afb9a5SDavid du Colombier {
513*63afb9a5SDavid du Colombier 	int n;
514*63afb9a5SDavid du Colombier 	char buf[1024];
515*63afb9a5SDavid du Colombier 	int pid;
516*63afb9a5SDavid du Colombier 	int eofs;
517*63afb9a5SDavid du Colombier 
518*63afb9a5SDavid du Colombier 	switch(pid = rfork(RFMEM|RFPROC|RFNOWAIT)){
519*63afb9a5SDavid du Colombier 	case -1:
520*63afb9a5SDavid du Colombier 		error("fork: %r");
521*63afb9a5SDavid du Colombier 	case 0:
522*63afb9a5SDavid du Colombier 		break;
523*63afb9a5SDavid du Colombier 	default:
524*63afb9a5SDavid du Colombier 		atexitkill(pid);
525*63afb9a5SDavid du Colombier 		return;
526*63afb9a5SDavid du Colombier 	}
527*63afb9a5SDavid du Colombier 
528*63afb9a5SDavid du Colombier 	atexit(atexitkiller);
529*63afb9a5SDavid du Colombier 	if(interactive)
530*63afb9a5SDavid du Colombier 		rawon();
531*63afb9a5SDavid du Colombier 
532*63afb9a5SDavid du Colombier 	notify(cookedcatchint);
533*63afb9a5SDavid du Colombier 
534*63afb9a5SDavid du Colombier 	eofs = 0;
535*63afb9a5SDavid du Colombier 	for(;;){
536*63afb9a5SDavid du Colombier 		n = read(0, buf, sizeof(buf));
537*63afb9a5SDavid du Colombier 		if(n < 0){
538*63afb9a5SDavid du Colombier 			if(wasintr()){
539*63afb9a5SDavid du Colombier 				if(!raw){
540*63afb9a5SDavid du Colombier 					buf[0] = 0x7f;
541*63afb9a5SDavid du Colombier 					n = 1;
542*63afb9a5SDavid du Colombier 				}else
543*63afb9a5SDavid du Colombier 					continue;
544*63afb9a5SDavid du Colombier 			}else
545*63afb9a5SDavid du Colombier 				break;
546*63afb9a5SDavid du Colombier 		}
547*63afb9a5SDavid du Colombier 		if(n == 0){
548*63afb9a5SDavid du Colombier 			if(!c->interactive || ++eofs > 32)
549*63afb9a5SDavid du Colombier 				break;
550*63afb9a5SDavid du Colombier 		}else
551*63afb9a5SDavid du Colombier 			eofs = 0;
552*63afb9a5SDavid du Colombier 		if(interactive && usemenu && n && memchr(buf, 0x1c, n)) {
553*63afb9a5SDavid du Colombier 			if(menu(c)=='q'){
554*63afb9a5SDavid du Colombier 				sendwritemsg(c, "", 0);
555*63afb9a5SDavid du Colombier 				exits("quit");
556*63afb9a5SDavid du Colombier 			}
557*63afb9a5SDavid du Colombier 			continue;
558*63afb9a5SDavid du Colombier 		}
559*63afb9a5SDavid du Colombier 		if(!raw && n==0){
560*63afb9a5SDavid du Colombier 			buf[0] = 0x4;
561*63afb9a5SDavid du Colombier 			n = 1;
562*63afb9a5SDavid du Colombier 		}
563*63afb9a5SDavid du Colombier 		sendwritemsg(c, buf, n);
564*63afb9a5SDavid du Colombier 	}
565*63afb9a5SDavid du Colombier 	sendwritemsg(c, "", 0);
566*63afb9a5SDavid du Colombier 	atexitdont(atexitkiller);
567*63afb9a5SDavid du Colombier 	exits(nil);
568*63afb9a5SDavid du Colombier }
569*63afb9a5SDavid du Colombier 
570*63afb9a5SDavid du Colombier void
571*63afb9a5SDavid du Colombier winchanges(Conn *c)
572*63afb9a5SDavid du Colombier {
573*63afb9a5SDavid du Colombier 	int nrow, ncol, width, height;
574*63afb9a5SDavid du Colombier 	int pid;
575*63afb9a5SDavid du Colombier 
576*63afb9a5SDavid du Colombier 	switch(pid = rfork(RFMEM|RFPROC|RFNOWAIT)){
577*63afb9a5SDavid du Colombier 	case -1:
578*63afb9a5SDavid du Colombier 		error("fork: %r");
579*63afb9a5SDavid du Colombier 	case 0:
580*63afb9a5SDavid du Colombier 		break;
581*63afb9a5SDavid du Colombier 	default:
582*63afb9a5SDavid du Colombier 		atexitkill(pid);
583*63afb9a5SDavid du Colombier 		return;
584*63afb9a5SDavid du Colombier 	}
585*63afb9a5SDavid du Colombier 
586*63afb9a5SDavid du Colombier 	for(;;){
587*63afb9a5SDavid du Colombier 		if(readgeom(&nrow, &ncol, &width, &height) < 0)
588*63afb9a5SDavid du Colombier 			break;
589*63afb9a5SDavid du Colombier 		sendwindowsize(c, nrow, ncol, width, height);
590*63afb9a5SDavid du Colombier 	}
591*63afb9a5SDavid du Colombier 	exits(nil);
592*63afb9a5SDavid du Colombier }
593