misc.c revision 5d8f37ad78fc66901af50c762029a501561f3b23
1/*
2 * Copyright (c) 1995 Danny Gasparovski.
3 *
4 * Please read the file COPYRIGHT for the
5 * terms and conditions of the copyright.
6 */
7
8#define WANT_SYS_IOCTL_H
9#include <slirp.h>
10
11u_int curtime, time_fasttimo, last_slowtimo, detach_time;
12u_int detach_wait = 600000;	/* 10 minutes */
13struct emu_t *tcpemu;
14
15int
16inet_strtoip(const char*  str, uint32_t  *ip)
17{
18    int  comp[4];
19
20    if (sscanf(str, "%d.%d.%d.%d", &comp[0], &comp[1], &comp[2], &comp[3]) != 4)
21        return -1;
22
23    if ((unsigned)comp[0] >= 256 ||
24        (unsigned)comp[1] >= 256 ||
25        (unsigned)comp[2] >= 256 ||
26        (unsigned)comp[3] >= 256)
27        return -1;
28
29    *ip = (uint32_t)((comp[0] << 24) | (comp[1] << 16) |
30                     (comp[2] << 8)  |  comp[3]);
31    return 0;
32}
33
34char*  inet_iptostr(uint32_t  ip)
35{
36    static char  buff[32];
37
38    snprintf(buff, sizeof(buff), "%d.%d.%d.%d",
39             (ip >> 24) & 255,
40             (ip >> 16) & 255,
41             (ip >> 8) & 255,
42             ip & 255);
43    return buff;
44}
45
46/*
47 * Get our IP address and put it in our_addr
48 */
49void
50getouraddr()
51{
52    char*        hostname = host_name();
53    SockAddress  hostaddr;
54
55    our_addr_ip = loopback_addr_ip;
56
57    if (sock_address_init_resolve( &hostaddr, hostname, 0, 0 ) < 0)
58        return;
59
60    our_addr_ip = sock_address_get_ip(&hostaddr);
61    if (our_addr_ip == (uint32_t)-1)
62        our_addr_ip = loopback_addr_ip;
63}
64
65struct quehead {
66	struct quehead *qh_link;
67	struct quehead *qh_rlink;
68};
69
70inline void
71insque(void *a, void *b)
72{
73	register struct quehead *element = (struct quehead *) a;
74	register struct quehead *head = (struct quehead *) b;
75	element->qh_link = head->qh_link;
76	head->qh_link = (struct quehead *)element;
77	element->qh_rlink = (struct quehead *)head;
78	((struct quehead *)(element->qh_link))->qh_rlink
79	= (struct quehead *)element;
80}
81
82inline void
83remque(void *a)
84{
85  register struct quehead *element = (struct quehead *) a;
86  ((struct quehead *)(element->qh_link))->qh_rlink = element->qh_rlink;
87  ((struct quehead *)(element->qh_rlink))->qh_link = element->qh_link;
88  element->qh_rlink = NULL;
89  /*  element->qh_link = NULL;  TCP FIN1 crashes if you do this.  Why ? */
90}
91
92/* #endif */
93
94
95int
96add_exec(struct ex_list **ex_ptr, int do_pty, char *exec, int addr, int port)
97{
98	struct ex_list *tmp_ptr;
99
100	/* First, check if the port is "bound" */
101	for (tmp_ptr = *ex_ptr; tmp_ptr; tmp_ptr = tmp_ptr->ex_next) {
102		if (port == tmp_ptr->ex_fport && addr == tmp_ptr->ex_addr)
103		   return -1;
104	}
105
106	tmp_ptr = *ex_ptr;
107	*ex_ptr = (struct ex_list *)malloc(sizeof(struct ex_list));
108	(*ex_ptr)->ex_fport = port;
109	(*ex_ptr)->ex_addr = addr;
110	(*ex_ptr)->ex_pty = do_pty;
111	(*ex_ptr)->ex_exec = (do_pty == 3) ? exec : strdup(exec);
112	(*ex_ptr)->ex_next = tmp_ptr;
113	return 0;
114}
115
116#ifndef HAVE_STRERROR
117
118/*
119 * For systems with no strerror
120 */
121
122extern int sys_nerr;
123extern char *sys_errlist[];
124
125char *
126strerror(error)
127	int error;
128{
129	if (error < sys_nerr)
130	   return sys_errlist[error];
131	else
132	   return "Unknown error.";
133}
134
135#endif
136
137
138#ifdef _WIN32
139
140int
141fork_exec(struct socket *so, const char *ex, int do_pty)
142{
143    /* not implemented */
144    return 0;
145}
146
147#else
148
149#ifndef CONFIG_QEMU
150int
151slirp_openpty(amaster, aslave)
152     int *amaster, *aslave;
153{
154	register int master, slave;
155
156#ifdef HAVE_GRANTPT
157	char *ptr;
158
159	if ((master = open("/dev/ptmx", O_RDWR)) < 0 ||
160	    grantpt(master) < 0 ||
161	    unlockpt(master) < 0 ||
162	    (ptr = ptsname(master)) == NULL)  {
163		close(master);
164		return -1;
165	}
166
167	if ((slave = open(ptr, O_RDWR)) < 0 ||
168	    ioctl(slave, I_PUSH, "ptem") < 0 ||
169	    ioctl(slave, I_PUSH, "ldterm") < 0 ||
170	    ioctl(slave, I_PUSH, "ttcompat") < 0) {
171		close(master);
172		close(slave);
173		return -1;
174	}
175
176	*amaster = master;
177	*aslave = slave;
178	return 0;
179
180#else
181
182	static char line[] = "/dev/ptyXX";
183	register const char *cp1, *cp2;
184
185	for (cp1 = "pqrsPQRS"; *cp1; cp1++) {
186		line[8] = *cp1;
187		for (cp2 = "0123456789abcdefghijklmnopqrstuv"; *cp2; cp2++) {
188			line[9] = *cp2;
189			if ((master = open(line, O_RDWR, 0)) == -1) {
190				if (errno == ENOENT)
191				   return (-1);    /* out of ptys */
192			} else {
193				line[5] = 't';
194				/* These will fail */
195				(void) chown(line, getuid(), 0);
196				(void) chmod(line, S_IRUSR|S_IWUSR|S_IWGRP);
197#ifdef HAVE_REVOKE
198				(void) revoke(line);
199#endif
200				if ((slave = open(line, O_RDWR, 0)) != -1) {
201					*amaster = master;
202					*aslave = slave;
203					return 0;
204				}
205				(void) close(master);
206				line[5] = 'p';
207			}
208		}
209	}
210	errno = ENOENT; /* out of ptys */
211	return (-1);
212#endif
213}
214#endif
215
216/*
217 * XXX This is ugly
218 * We create and bind a socket, then fork off to another
219 * process, which connects to this socket, after which we
220 * exec the wanted program.  If something (strange) happens,
221 * the accept() call could block us forever.
222 *
223 * do_pty = 0   Fork/exec inetd style
224 * do_pty = 1   Fork/exec using slirp.telnetd
225 * do_ptr = 2   Fork/exec using pty
226 */
227int
228fork_exec(struct socket *so, const char *ex, int do_pty)
229{
230	int s;
231        int master = -1;
232	const char *argv[256];
233#if 0
234	char buff[256];
235#endif
236	/* don't want to clobber the original */
237	char *bptr;
238	const char *curarg;
239	int c, i;
240
241	DEBUG_CALL("fork_exec");
242	DEBUG_ARG("so = %lx", (long)so);
243	DEBUG_ARG("ex = %lx", (long)ex);
244	DEBUG_ARG("do_pty = %lx", (long)do_pty);
245
246	if (do_pty == 2) {
247#if 0
248		if (slirp_openpty(&master, &s) == -1) {
249			lprint("Error: openpty failed: %s\n", strerror(errno));
250			return 0;
251		}
252#else
253                return 0;
254#endif
255	} else {
256		if ((s = socket_anyaddr_server(0, SOCKET_STREAM)) < 0) {
257			lprint("Error: inet socket: %s\n", errno_str);
258			return 0;
259		}
260	}
261
262	switch(fork()) {
263	 case -1:
264		lprint("Error: fork failed: %s\n", strerror(errno));
265		close(s);
266		if (do_pty == 2)
267		   close(master);
268		return 0;
269
270	 case 0:
271		/* Set the DISPLAY */
272		if (do_pty == 2) {
273			(void) close(master);
274#ifdef TIOCSCTTY /* XXXXX */
275			(void) setsid();
276			ioctl(s, TIOCSCTTY, (char *)NULL);
277#endif
278		} else {
279			SockAddress  addr;
280			socket_get_address(s, &addr);
281			socket_close(s);
282			/*
283			 * Connect to the socket
284			 * XXX If any of these fail, we're in trouble!
285	 		 */
286            s = socket_loopback_client(sock_address_get_port(&addr), SOCKET_STREAM);
287		}
288
289#if 0
290		if (x_port >= 0) {
291#ifdef HAVE_SETENV
292			sprintf(buff, "%s:%d.%d", inet_ntoa(our_addr), x_port, x_screen);
293			setenv("DISPLAY", buff, 1);
294#else
295			sprintf(buff, "DISPLAY=%s:%d.%d", inet_ntoa(our_addr), x_port, x_screen);
296			putenv(buff);
297#endif
298		}
299#endif
300		dup2(s, 0);
301		dup2(s, 1);
302		dup2(s, 2);
303		for (s = getdtablesize() - 1; s >= 3; s--)
304		   close(s);
305
306		i = 0;
307		bptr = strdup(ex); /* No need to free() this */
308		if (do_pty == 1) {
309			/* Setup "slirp.telnetd -x" */
310			argv[i++] = "slirp.telnetd";
311			argv[i++] = "-x";
312			argv[i++] = bptr;
313		} else
314		   do {
315			/* Change the string into argv[] */
316			curarg = bptr;
317			while (*bptr != ' ' && *bptr != (char)0)
318			   bptr++;
319			c = *bptr;
320			*bptr++ = (char)0;
321			argv[i++] = strdup(curarg);
322		   } while (c);
323
324                argv[i] = NULL;
325		execvp(argv[0], (char **)argv);
326
327		/* Ooops, failed, let's tell the user why */
328		  {
329			  char buff[256];
330
331			  snprintf(buff, sizeof(buff),
332                                   "Error: execvp of %s failed: %s\n",
333                                   argv[0], strerror(errno));
334			  write(2, buff, strlen(buff)+1);
335		  }
336		close(0); close(1); close(2); /* XXX */
337		exit(1);
338
339	 default:
340		if (do_pty == 2) {
341			close(s);
342			so->s = master;
343		} else {
344			/*
345			 * XXX this could block us...
346			 * XXX Should set a timer here, and if accept() doesn't
347		 	 * return after X seconds, declare it a failure
348		 	 * The only reason this will block forever is if socket()
349		 	 * of connect() fail in the child process
350		 	 */
351             so->s = socket_accept(s, NULL);
352			 socket_set_xreuseaddr(so->s);
353             socket_set_oobinline(so->s);
354		}
355		socket_set_nonblock(so->s);
356
357		/* Append the telnet options now */
358                if (so->so_m != NULL && do_pty == 1)  {
359			sbappend(so, so->so_m);
360                        so->so_m = NULL;
361		}
362
363		return 1;
364	}
365}
366#endif
367
368#ifndef HAVE_STRDUP
369char *
370strdup(const char*  str)
371{
372	char *bptr;
373	int   len = strlen(str);
374
375	bptr = (char *)malloc(len+1);
376	memcpy(bptr, str, len+1);
377
378	return bptr;
379}
380#endif
381
382#if 0
383void
384snooze_hup(num)
385	int num;
386{
387	int s, ret;
388#ifndef NO_UNIX_SOCKETS
389	struct sockaddr_un sock_un;
390#endif
391	struct sockaddr_in sock_in;
392	char buff[256];
393
394	ret = -1;
395	if (slirp_socket_passwd) {
396		s = socket(AF_INET, SOCK_STREAM, 0);
397		if (s < 0)
398		   slirp_exit(1);
399		sock_in.sin_family = AF_INET;
400		sock_in.sin_addr.s_addr = slirp_socket_addr;
401		sock_in.sin_port = htons(slirp_socket_port);
402		if (connect(s, (struct sockaddr *)&sock_in, sizeof(sock_in)) != 0)
403		   slirp_exit(1); /* just exit...*/
404		sprintf(buff, "kill %s:%d", slirp_socket_passwd, slirp_socket_unit);
405		write(s, buff, strlen(buff)+1);
406	}
407#ifndef NO_UNIX_SOCKETS
408	  else {
409		s = socket(AF_UNIX, SOCK_STREAM, 0);
410		if (s < 0)
411		   slirp_exit(1);
412		sock_un.sun_family = AF_UNIX;
413		strcpy(sock_un.sun_path, socket_path);
414		if (connect(s, (struct sockaddr *)&sock_un,
415			      sizeof(sock_un.sun_family) + sizeof(sock_un.sun_path)) != 0)
416		   slirp_exit(1);
417		sprintf(buff, "kill none:%d", slirp_socket_unit);
418		write(s, buff, strlen(buff)+1);
419	}
420#endif
421	slirp_exit(0);
422}
423
424
425void
426snooze()
427{
428	sigset_t s;
429	int i;
430
431	/* Don't need our data anymore */
432	/* XXX This makes SunOS barf */
433/*	brk(0); */
434
435	/* Close all fd's */
436	for (i = 255; i >= 0; i--)
437	   close(i);
438
439	signal(SIGQUIT, slirp_exit);
440	signal(SIGHUP, snooze_hup);
441	sigemptyset(&s);
442
443	/* Wait for any signal */
444	sigsuspend(&s);
445
446	/* Just in case ... */
447	exit(255);
448}
449
450void
451relay(s)
452	int s;
453{
454	char buf[8192];
455	int n;
456	fd_set readfds;
457	struct ttys *ttyp;
458
459	/* Don't need our data anymore */
460	/* XXX This makes SunOS barf */
461/*	brk(0); */
462
463	signal(SIGQUIT, slirp_exit);
464	signal(SIGHUP, slirp_exit);
465        signal(SIGINT, slirp_exit);
466	signal(SIGTERM, slirp_exit);
467
468	/* Fudge to get term_raw and term_restore to work */
469	if (NULL == (ttyp = tty_attach (0, slirp_tty))) {
470         lprint ("Error: tty_attach failed in misc.c:relay()\r\n");
471         slirp_exit (1);
472    }
473	ttyp->fd = 0;
474	ttyp->flags |= TTY_CTTY;
475	term_raw(ttyp);
476
477	while (1) {
478		FD_ZERO(&readfds);
479
480		FD_SET(0, &readfds);
481		FD_SET(s, &readfds);
482
483		n = select(s+1, &readfds, (fd_set *)0, (fd_set *)0, (struct timeval *)0);
484
485		if (n <= 0)
486		   slirp_exit(0);
487
488		if (FD_ISSET(0, &readfds)) {
489			n = read(0, buf, 8192);
490			if (n <= 0)
491			   slirp_exit(0);
492			n = writen(s, buf, n);
493			if (n <= 0)
494			   slirp_exit(0);
495		}
496
497		if (FD_ISSET(s, &readfds)) {
498			n = read(s, buf, 8192);
499			if (n <= 0)
500			   slirp_exit(0);
501			n = writen(0, buf, n);
502			if (n <= 0)
503			   slirp_exit(0);
504		}
505	}
506
507	/* Just in case.... */
508	exit(1);
509}
510#endif
511
512#ifdef CONFIG_QEMU
513#include "monitor.h"
514
515void lprint(const char *format, ...)
516{
517    va_list args;
518
519    va_start(args, format);
520    monitor_vprintf(cur_mon, format, args);
521    va_end(args);
522}
523#else
524int (*lprint_print) _P((void *, const char *, va_list));
525char *lprint_ptr, *lprint_ptr2, **lprint_arg;
526
527void
528#ifdef __STDC__
529lprint(const char *format, ...)
530#else
531lprint(va_alist) va_dcl
532#endif
533{
534	va_list args;
535
536#ifdef __STDC__
537        va_start(args, format);
538#else
539        char *format;
540        va_start(args);
541        format = va_arg(args, char *);
542#endif
543#if 0
544	/* If we're printing to an sbuf, make sure there's enough room */
545	/* XXX +100? */
546	if (lprint_sb) {
547		if ((lprint_ptr - lprint_sb->sb_wptr) >=
548		    (lprint_sb->sb_datalen - (strlen(format) + 100))) {
549			int deltaw = lprint_sb->sb_wptr - lprint_sb->sb_data;
550			int deltar = lprint_sb->sb_rptr - lprint_sb->sb_data;
551			int deltap = lprint_ptr -         lprint_sb->sb_data;
552
553			lprint_sb->sb_data = (char *)realloc(lprint_sb->sb_data,
554							     lprint_sb->sb_datalen + TCP_SNDSPACE);
555
556			/* Adjust all values */
557			lprint_sb->sb_wptr = lprint_sb->sb_data + deltaw;
558			lprint_sb->sb_rptr = lprint_sb->sb_data + deltar;
559			lprint_ptr =         lprint_sb->sb_data + deltap;
560
561			lprint_sb->sb_datalen += TCP_SNDSPACE;
562		}
563	}
564#endif
565	if (lprint_print)
566	   lprint_ptr += (*lprint_print)(*lprint_arg, format, args);
567
568	/* Check if they want output to be logged to file as well */
569	if (lfd) {
570		/*
571		 * Remove \r's
572		 * otherwise you'll get ^M all over the file
573		 */
574		int len = strlen(format);
575		char *bptr1, *bptr2;
576
577		bptr1 = bptr2 = strdup(format);
578
579		while (len--) {
580			if (*bptr1 == '\r')
581			   memcpy(bptr1, bptr1+1, len+1);
582			else
583			   bptr1++;
584		}
585		vfprintf(lfd, bptr2, args);
586		free(bptr2);
587	}
588	va_end(args);
589}
590
591void
592add_emu(buff)
593	char *buff;
594{
595	u_int lport, fport;
596	u_int8_t tos = 0, emu = 0;
597	char buff1[256], buff2[256], buff4[128];
598	char *buff3 = buff4;
599	struct emu_t *emup;
600	struct socket *so;
601
602	if (sscanf(buff, "%256s %256s", buff2, buff1) != 2) {
603		lprint("Error: Bad arguments\r\n");
604		return;
605	}
606
607	if (sscanf(buff1, "%d:%d", &lport, &fport) != 2) {
608		lport = 0;
609		if (sscanf(buff1, "%d", &fport) != 1) {
610			lprint("Error: Bad first argument\r\n");
611			return;
612		}
613	}
614
615	if (sscanf(buff2, "%128[^:]:%128s", buff1, buff3) != 2) {
616		buff3 = 0;
617		if (sscanf(buff2, "%256s", buff1) != 1) {
618			lprint("Error: Bad second argument\r\n");
619			return;
620		}
621	}
622
623	if (buff3) {
624		if (strcmp(buff3, "lowdelay") == 0)
625		   tos = IPTOS_LOWDELAY;
626		else if (strcmp(buff3, "throughput") == 0)
627		   tos = IPTOS_THROUGHPUT;
628		else {
629			lprint("Error: Expecting \"lowdelay\"/\"throughput\"\r\n");
630			return;
631		}
632	}
633
634	if (strcmp(buff1, "ftp") == 0)
635	   emu = EMU_FTP;
636	else if (strcmp(buff1, "irc") == 0)
637	   emu = EMU_IRC;
638	else if (strcmp(buff1, "none") == 0)
639	   emu = EMU_NONE; /* ie: no emulation */
640	else {
641		lprint("Error: Unknown service\r\n");
642		return;
643	}
644
645	/* First, check that it isn't already emulated */
646	for (emup = tcpemu; emup; emup = emup->next) {
647		if (emup->lport == lport && emup->fport == fport) {
648			lprint("Error: port already emulated\r\n");
649			return;
650		}
651	}
652
653	/* link it */
654	emup = (struct emu_t *)malloc(sizeof (struct emu_t));
655	emup->lport = (u_int16_t)lport;
656	emup->fport = (u_int16_t)fport;
657	emup->tos = tos;
658	emup->emu = emu;
659	emup->next = tcpemu;
660	tcpemu = emup;
661
662	/* And finally, mark all current sessions, if any, as being emulated */
663	for (so = tcb.so_next; so != &tcb; so = so->so_next) {
664		if ((lport && lport == so->so_laddr_port) ||
665		    (fport && fport == so->so_faddr_port)) {
666			if (emu)
667			   so->so_emu = emu;
668			if (tos)
669			   so->so_iptos = tos;
670		}
671	}
672
673	lprint("Adding emulation for %s to port %d/%d\r\n", buff1, emup->lport, emup->fport);
674}
675#endif
676
677#ifdef BAD_SPRINTF
678
679#undef vsprintf
680#undef sprintf
681
682/*
683 * Some BSD-derived systems have a sprintf which returns char *
684 */
685
686int
687vsprintf_len(string, format, args)
688	char *string;
689	const char *format;
690	va_list args;
691{
692	vsprintf(string, format, args);
693	return strlen(string);
694}
695
696int
697#ifdef __STDC__
698sprintf_len(char *string, const char *format, ...)
699#else
700sprintf_len(va_alist) va_dcl
701#endif
702{
703	va_list args;
704#ifdef __STDC__
705	va_start(args, format);
706#else
707	char *string;
708	char *format;
709	va_start(args);
710	string = va_arg(args, char *);
711	format = va_arg(args, char *);
712#endif
713	vsprintf(string, format, args);
714	return strlen(string);
715}
716
717#endif
718
719#if 0
720void
721u_sleep(int usec)
722{
723	struct timeval t;
724	fd_set fdset;
725
726	FD_ZERO(&fdset);
727
728	t.tv_sec = 0;
729	t.tv_usec = usec * 1000;
730
731	select(0, &fdset, &fdset, &fdset, &t);
732}
733#endif
734
735/*
736 * Set fd blocking and non-blocking
737 */
738
739void
740fd_nonblock(int fd)
741{
742#ifdef FIONBIO
743#ifdef _WIN32
744        unsigned long opt = 1;
745#else
746        int opt = 1;
747#endif
748
749	ioctlsocket(fd, FIONBIO, &opt);
750#else
751	int opt;
752
753	opt = fcntl(fd, F_GETFL, 0);
754	opt |= O_NONBLOCK;
755	fcntl(fd, F_SETFL, opt);
756#endif
757}
758
759void
760fd_block(int fd)
761{
762#ifdef FIONBIO
763#ifdef _WIN32
764        unsigned long opt = 0;
765#else
766	int opt = 0;
767#endif
768
769	ioctlsocket(fd, FIONBIO, &opt);
770#else
771	int opt;
772
773	opt = fcntl(fd, F_GETFL, 0);
774	opt &= ~O_NONBLOCK;
775	fcntl(fd, F_SETFL, opt);
776#endif
777}
778
779
780#if 0
781/*
782 * invoke RSH
783 */
784int
785rsh_exec(so,ns, user, host, args)
786	struct socket *so;
787	struct socket *ns;
788	char *user;
789	char *host;
790	char *args;
791{
792	int fd[2];
793	int fd0[2];
794	int s;
795	char buff[256];
796
797	DEBUG_CALL("rsh_exec");
798	DEBUG_ARG("so = %lx", (long)so);
799
800	if (pipe(fd)<0) {
801          lprint("Error: pipe failed: %s\n", strerror(errno));
802          return 0;
803	}
804/* #ifdef HAVE_SOCKETPAIR */
805#if 1
806        if (socketpair(PF_UNIX,SOCK_STREAM,0, fd0) == -1) {
807          close(fd[0]);
808          close(fd[1]);
809          lprint("Error: openpty failed: %s\n", strerror(errno));
810          return 0;
811        }
812#else
813        if (slirp_openpty(&fd0[0], &fd0[1]) == -1) {
814          close(fd[0]);
815          close(fd[1]);
816          lprint("Error: openpty failed: %s\n", strerror(errno));
817          return 0;
818        }
819#endif
820
821	switch(fork()) {
822	 case -1:
823           lprint("Error: fork failed: %s\n", strerror(errno));
824           close(fd[0]);
825           close(fd[1]);
826           close(fd0[0]);
827           close(fd0[1]);
828           return 0;
829
830	 case 0:
831           close(fd[0]);
832           close(fd0[0]);
833
834		/* Set the DISPLAY */
835           if (x_port >= 0) {
836#ifdef HAVE_SETENV
837             sprintf(buff, "%s:%d.%d", inet_ntoa(our_addr), x_port, x_screen);
838             setenv("DISPLAY", buff, 1);
839#else
840             sprintf(buff, "DISPLAY=%s:%d.%d", inet_ntoa(our_addr), x_port, x_screen);
841             putenv(buff);
842#endif
843           }
844
845           dup2(fd0[1], 0);
846           dup2(fd0[1], 1);
847           dup2(fd[1], 2);
848           for (s = 3; s <= 255; s++)
849             close(s);
850
851           execlp("rsh","rsh","-l", user, host, args, NULL);
852
853           /* Ooops, failed, let's tell the user why */
854
855           sprintf(buff, "Error: execlp of %s failed: %s\n",
856                   "rsh", strerror(errno));
857           write(2, buff, strlen(buff)+1);
858           close(0); close(1); close(2); /* XXX */
859           exit(1);
860
861        default:
862          close(fd[1]);
863          close(fd0[1]);
864          ns->s=fd[0];
865          so->s=fd0[0];
866
867          return 1;
868	}
869}
870#endif
871