[gnu.gdb.bug] Fixes for remote debugging

michael@garfield.mun.edu (Michael Rendell) (06/13/89)

When I tried to use gdb as a remote debugger I ran into a few problems getting
it going (this is version 1.31 compiled on a sun3 running os4).
	remote_fetch_registers() and remote_write_bytes() were not null
		terminating their requests.
	remote_open() - if a tty is already open, close it before opening
		another.
	readchar() - now reads in a buffer instead of a single char at a time
		(this is just for efficiency).
	remote_write() - added this function to check for errors when writing.
	print_spaces() - declared to be static in symmisc.c when it's defined
		as global.

The code that is #ifdef'd RTTY is for attaching to a remote tty.  I needed
to add this because all the lines to our sun were taken up.  I have included
the source (rtty_serv.c) to the daemon that must run on the machine with the
real tty (rather BSD specific).  This is also very useful in debugging the
protocol between gdb and the remote machine since it's easy to log the
conversation.

I also have code that runs on the remote machine - works for both the 68000
and the 68010 (and probably the 68020, but it isn't tested).  If anyone is
interested, let me know.

#!/bin/sh
# This is a shell archive - strip off everything before the #! line and
# run through /bin/sh.
# This file contains:
#	diffs
#	rtty_serv.c
# Created by michael, on Mon Jun 12 10:26:11 1989
if test -f 'diffs'
then
	echo 'will not over-write existing file diffs'
else
	sed 's/^X//' << 'SHAR_E_O_F' > 'diffs'
X*** orig.remote.c	Sun May  7 17:20:06 1989
X--- remote.c	Mon Jun 12 09:20:13 1989
X***************
X*** 79,84 ****
X--- 79,95 ----
X  #include <sys/ioctl.h>
X  #include <sys/file.h>
X  
X+ #ifdef RTTY
X+ #ifndef USG
X+ #include	<sys/types.h>
X+ #endif /* USG */
X+ #include	<sys/socket.h>
X+ #include	<netinet/in.h>
X+ #include	<netdb.h>
X+ 
X+ char *index();
X+ #endif /* RTTY */
X+ 
X  #ifdef HAVE_TERMIO
X  #include <termio.h>
X  #undef TIOCGETP
X***************
X*** 106,111 ****
X--- 117,125 ----
X  static void putpkt ();
X  static void getpkt ();
X  static void dcache_flush ();
X+ static int remote_write ();
X+ static int readchar_index;
X+ static int readchar_nbytes;
X  
X  
X  /* Open a connection to a remote debugger.
X***************
X*** 121,137 ****
X    remote_debugging = 0;
X    dcache_init ();
X  
X!   remote_desc = open (name, O_RDWR);
X!   if (remote_desc < 0)
X!     perror_with_name (name);
X  
X!   ioctl (remote_desc, TIOCGETP, &sg);
X  #ifdef HAVE_TERMIO
X!   sg.c_lflag &= ~ICANON;
X  #else
X!   sg.sg_flags = RAW;
X  #endif
X!   ioctl (remote_desc, TIOCSETP, &sg);
X  
X    if (from_tty)
X      printf ("Remote debugging using %s\n", name);
X--- 135,163 ----
X    remote_debugging = 0;
X    dcache_init ();
X  
X!   readchar_index = readchar_nbytes = 0;
X!   if (remote_desc >= 0)
X!     close(remote_desc);
X! #ifdef RTTY
X!   if (index (name, '@'))
X!     {
X!       remote_desc = rtty_open(name);
X!     }
X!   else
X! #endif /* RTTY */
X!     {
X!       remote_desc = open (name, O_RDWR);
X!       if (remote_desc < 0)
X!         perror_with_name (name);
X  
X!       ioctl (remote_desc, TIOCGETP, &sg);
X  #ifdef HAVE_TERMIO
X!       sg.c_lflag &= ~ICANON;
X  #else
X!       sg.sg_flags = RAW;
X  #endif
X!       ioctl (remote_desc, TIOCSETP, &sg);
X!     }
X  
X    if (from_tty)
X      printf ("Remote debugging using %s\n", name);
X***************
X*** 244,249 ****
X--- 270,276 ----
X        *p++ = tohex ((regs[i] >> 4) & 0xf);
X        *p++ = tohex (regs[i] & 0xf);
X      }
X+   *p = '\0';
X  
X    remote_send (buf);
X  }
X***************
X*** 310,315 ****
X--- 337,343 ----
X        *p++ = tohex ((myaddr[i] >> 4) & 0xf);
X        *p++ = tohex (myaddr[i] & 0xf);
X      }
X+   *p = '\0';
X  
X    remote_send (buf);
X  }
X***************
X*** 396,402 ****
X    int i;
X    char csum = 0;
X    char buf2[500];
X-   char buf3[1];
X    int cnt = strlen (buf);
X    char *p;
X  
X--- 424,429 ----
X***************
X*** 421,439 ****
X    /* Send it over and over until we get a positive ack.  */
X  
X    do {
X!     write (remote_desc, buf2, p - buf2);
X!     read (remote_desc, buf3, 1);
X!   } while (buf3[0] != '+');
X  }
X  
X  static int
X  readchar ()
X  {
X!   char buf[1];
X!   while (read (remote_desc, buf, 1) != 1) ;
X!   return buf[0] & 0x7f;
X  }
X  
X  /* Read a packet from the remote machine, with error checking,
X     and store it in BUF.  */
X  
X--- 448,484 ----
X    /* Send it over and over until we get a positive ack.  */
X  
X    do {
X!     remote_write (buf2, p - buf2);
X!   } while (readchar() != '+');
X  }
X  
X  static int
X  readchar ()
X  {
X!   static char	mybuf[64];
X! 
X!   if (readchar_index >= readchar_nbytes)
X!     {
X!       readchar_index = 0;
X!       readchar_nbytes = read (remote_desc, mybuf, sizeof(mybuf));
X!       if (readchar_nbytes <= 0)
X!          error ("read from remote failed");
X!     }
X!   return mybuf[readchar_index++] & 0x7f;
X  }
X  
X+ static int
X+ remote_write (buf, nbytes)
X+      char *buf;
X+      int nbytes;
X+ {
X+   int nwrite;
X+ 
X+   nwrite = write(remote_desc, buf, nbytes);
X+   if (nwrite < 0)
X+     error("write to remote failed");
X+   return nwrite;
X+ }
X  /* Read a packet from the remote machine, with error checking,
X     and store it in BUF.  */
X  
X***************
X*** 470,479 ****
X  	break;
X        printf ("Bad checksum, sentsum=0x%x, csum=0x%x, buf=%s\n",
X  	      (c1 << 4) + c2, csum, buf);
X!       write (remote_desc, "-", 1);
X      }
X  
X!   write (remote_desc, "+", 1);
X  
X    if (kiodebug)
X      fprintf (stderr,"Packet received :%s\n", buf);
X--- 515,524 ----
X  	break;
X        printf ("Bad checksum, sentsum=0x%x, csum=0x%x, buf=%s\n",
X  	      (c1 << 4) + c2, csum, buf);
X!       remote_write ("-", 1);
X      }
X  
X!   remote_write ("+", 1);
X  
X    if (kiodebug)
X      fprintf (stderr,"Packet received :%s\n", buf);
X***************
X*** 621,623 ****
X--- 666,726 ----
X      insque (db, &dcache_free);
X  }
X  
X+ #ifdef RTTY
X+ 
X+ #ifndef RTTY_PORT
X+ # define RTTY_PORT	3120		/* port the daemon listens to */
X+ #endif /* RTTY_PORT */
X+ 
X+ /*
X+  *	Open a connection to the rtty server on the remote machine.  The
X+  *  daemon puts the tty in raw mode.
X+  */
X+ int
X+ rtty_open(dev)
X+ 	char	*dev;
X+ {
X+ 	char			*host;
X+ 	struct hostent		*he;
X+ 	struct sockaddr_in	sin;
X+ 	short			val;
X+ 	int			sock;
X+ 	int			devlen;
X+ 
X+ 	if (!(host = index(dev, '@')))
X+ 		return -1;
X+ 	devlen = host++ - dev;
X+ 
X+ 	if (!(he = gethostbyname(host)))
X+ 		error("cannot find address for host \"%s\"", host);
X+ 	
X+ 	if (he->h_addrtype != AF_INET)
X+ 		error("no inet address for host");
X+ 
X+ 	if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
X+ 		perror_with_name("socket");
X+ 
X+ 	bzero((char *) &sin, sizeof(sin));
X+ 	sin.sin_family = AF_INET;
X+ 	sin.sin_port = htons(RTTY_PORT);
X+ 	bcopy(he->h_addr, &sin.sin_addr.s_addr, sizeof(struct in_addr));
X+ 	if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
X+ 		close(sock);
X+ 		perror_with_name("connect");
X+ 	}
X+ 
X+ 	if (write(sock, dev, devlen) != devlen) {
X+ 		close(sock);
X+ 		error("could not write to socket");
X+ 	}
X+ 	if (read(sock, (char *) &val, sizeof(val)) != sizeof(val)) {
X+ 		close(sock);
X+ 		error("could not read from socket");
X+ 	}
X+ 	if (val = ntohs(val)) {
X+ 		close(sock);
X+ 		error("got error %d from host \"%s\"", (int) val, host);
X+ 	}
X+ 	return sock;
X+ }
X+ #endif /* RTTY */
X*** orig.symmisc.c	Thu May 18 11:47:51 1989
X--- symmisc.c	Thu May 18 11:47:54 1989
X***************
X*** 377,383 ****
X  }
X  
X  static int block_depth ();
X! static void print_spaces ();
X  static void print_symbol ();
X  
X  void
X--- 377,383 ----
X  }
X  
X  static int block_depth ();
X! extern void print_spaces ();
X  static void print_symbol ();
X  
X  void
SHAR_E_O_F
fi
if test -f 'rtty_serv.c'
then
	echo 'will not over-write existing file rtty_serv.c'
else
	sed 's/^X//' << 'SHAR_E_O_F' > 'rtty_serv.c'
X#include	<stdio.h>
X#include	<memory.h>
X#include	<errno.h>
X#include	<malloc.h>
X#include	<varargs.h>
X#include	<fcntl.h>
X#include	<sys/types.h>
X#include	<sys/param.h>
X#include	<sys/ioctl.h>
X#include	<sys/socket.h>
X#include	<signal.h>
X#include	<netdb.h>
X#include	<netinet/in.h>
X
X/*
X *	Remote tty server - used by gdb to attach to a tty on another machine.
X *  There really should be a generic remote device daemon (i.e. a generic
X *  /etc/rmt) so this wouldn't be nessasary.
X *
X *  Systems that do not have vfprintf() in libc should define NEED_VPRINTF
X *  when compiling.
X *
X *	Usage: rtty_serv [-Dn] [> logfile]
X *
X *  -Dn sets the debugging mask:
X *	1 for transaction log - keeps track of connections and traffic
X *	2 daemon debuggin - prints select masks, return values.
X *  All debugging goes to standard out (should be saved to a file).  When
X *  a SIGHUP is received, the log file is truncated (they get big, quickly).
X */
X
X#ifndef RTTY_PORT
X# define RTTY_PORT	3120		/* daemons master port number */
X#endif /* RTTY_PORT */
X
X#ifndef FD_SET
X/* for 4.2 systems */
X
X#define	NBBY	8		/* number of bits in a byte */
X/*
X * Select uses bit masks of file descriptors in longs.
X * These macros manipulate such bit fields (the filesystem macros use chars).
X * FD_SETSIZE may be defined by the user, but the default here
X * should be >= NOFILE (param.h).
X */
X#ifndef	FD_SETSIZE
X#define	FD_SETSIZE	64
X#endif
X
Xtypedef long	fd_mask;
X#define NFDBITS	(sizeof(fd_mask) * NBBY)	/* bits per mask */
X#ifndef howmany
X#define	howmany(x, y)	(((x)+((y)-1))/(y))
X#endif
X
X#ifndef ultrix
Xtypedef	struct fd_set {
X	fd_mask	fds_bits[howmany(FD_SETSIZE, NFDBITS)];
X} fd_set;
X#endif /* !ultrix */
X
X#define	FD_SET(n, p)	((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
X#define	FD_CLR(n, p)	((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
X#define	FD_ISSET(n, p)	((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
X#define FD_ZERO(p)	bzero((char *)(p), sizeof(*(p)))
X#endif /* FS_SET */
X
X#ifndef MAXHOSTNAMELEN
X#define MAXHOSTNAMELEN		64
X#endif /* MAXHOSTNAMELEN */
X
X#define ERRNO			0x1
X#define EXIT			0x2
X
X#define	D_IO			0x1		/* transcation debugging */
X#define D_DAEM			0x2		/* debug this daemon */
X#define D_ALL			0xff
X
X#define DEBUG(l,x)		if (debug & (l)) (void) printf x; else
X
Xtypedef struct dev_str Dev;
Xstruct dev_str {
X	char		*d_name;	/* path of device */
X	int		d_dev;		/* device file descriptor */
X	int		d_sock;		/* socket fd */
X	Dev		*d_next;
X	Dev		*d_prev;
X};
X
Xextern int	errno;
X
Xint		set_nodelay();
Xvoid		error();
Xvoid		kill_dev();
Xvoid		init_dev();
Xvoid		readwrite();
Xvoid		send_error();
Xint		sig_hup();
Xchar		*str_error();
Xchar		*str_dup();
X
Xchar		*progname;
XDev		*dev_list;
Xfd_set		rmask;
Xint		maxfd;
Xint		debug;
Xint		got_hup;
XFILE		*efp;		/* errors go here */
X
Xvoid
Xmain(argc, argv)
X	int	argc;
X	char	**argv;
X{
X	int			msock;		/* master socket */
X	fd_set			rsel;
X	Dev			*dp;
X	struct sockaddr_in	sain;
X	int			nsel;
X	int			err;
X	int			fd;
X
X	progname = argc > 0 ? argv[0] : "rtty_serv";
X	debug = 0;
X	got_hup = 0;
X	efp = stderr;
X
X	if (argc > 1 && strncmp(argv[1], "-D", 2) == 0) {
X		setlinebuf(stdout);
X		debug = atoi(&argv[1][2]);
X	}
X
X	(void) memset((char *) &sain, 0, sizeof(sain));
X	sain.sin_family = AF_INET;
X	sain.sin_port = htons(RTTY_PORT);
X	sain.sin_addr.s_addr = INADDR_ANY;
X
X	if ((msock = socket(sain.sin_family, SOCK_STREAM, 0)) < 0)
X		error(ERRNO|EXIT, "socket failed", errno);
X
X	if (bind(msock, &sain, sizeof(sain)) < 0)
X		error(ERRNO|EXIT, "bind failed", errno);
X
X	if (listen(msock, 2) < 0)
X		error(ERRNO|EXIT, "listen failed", errno);
X
X	if (set_nodelay(msock))
X		/* error printed */
X		exit(1);
X
X	switch (fork()) {
X	case -1:
X		error(ERRNO|EXIT, "fork failed", errno);
X		/*NOTREACHED*/
X
X	case 0: /* child */
X		/*
X		 *	Ack like a real daemon - goto / and get rid of the
X		 *  controlling tty.
X		 */
X		(void) chdir("/");
X		(void) close(0);
X		(void) close(2);
X		if ((fd = open("/dev/tty", O_RDONLY, 0)) >= 0) {
X			(void) ioctl(fd, TIOCNOTTY, (char *) 0);
X			(void) close(fd);
X		}
X		/* errors go to stdout from now on */
X		efp = stdout;
X		break;
X
X	default: /* parent */
X		exit(0);
X	}
X
X	(void) signal(SIGHUP, sig_hup);
X
X	FD_ZERO(&rmask);
X	FD_SET(msock, &rmask);
X	maxfd = msock;
X	dev_list = (Dev *) 0;
X	while (1) {
X		if (got_hup && !isatty(fileno(stdout))) {
X			got_hup = 0;
X			(void) fseek(stdout, 0L, 0);
X			(void) ftruncate(fileno(stdout), 0L);
X		}
X		DEBUG(D_DAEM,
X			("maxfd %d, mask %x\n", maxfd, rmask.fds_bits[0]));
X		rsel = rmask;
X		if ((nsel = select(maxfd + 1, &rsel, (fd_set *) 0,
X							(fd_set *) 0, 0)) < 0)
X		{
X			if (errno == EINTR)
X				continue;
X			/* Should we exit or just wait a while? */
X			error(ERRNO|EXIT, "select failed", errno);
X		}
X		DEBUG(D_DAEM, ("nsel %d, rsel %x\n", nsel, rsel.fds_bits[0]));
X		if (FD_ISSET(msock, &rsel)) {
X			int	newsock;
X			int	length;
X
X			nsel--;
X			length = sizeof(sain);
X			if ((newsock = accept(msock, &sain, &length)) < 0) {
X				error(ERRNO, "accept failed", errno);
X				continue;
X			}
X			if (err = set_nodelay(newsock)) {
X				send_error(newsock, err);
X				(void) close(newsock);
X				continue;
X			}
X			if (!(dp = (Dev *) malloc(sizeof(Dev)))) {
X				error(0, "no memory");
X				send_error(newsock, ENOMEM);
X				(void) close(newsock);
X				continue;
X			}
X			dp->d_sock = newsock;
X			dp->d_name = (char *) 0;
X			dp->d_dev = -1;
X			dp->d_prev = (Dev *) 0;
X			if (dp->d_next = dev_list)
X				dev_list->d_prev = dp;
X			dev_list = dp;
X			FD_SET(newsock, &rmask);
X			if (newsock > maxfd)
X				maxfd = newsock;
X			/* could save sain & length if we need to */
X			DEBUG(D_IO, ("+ %d socket\n", newsock));
X		}
X		for (dp = dev_list ; nsel > 0 && dp ; dp = dp->d_next) {
X			if (FD_ISSET(dp->d_sock, &rsel)) {
X				--nsel;
X				if (dp->d_dev < 0)
X					init_dev(dp);
X				else
X					readwrite(dp, 1);
X			}
X			if (dp->d_dev >= 0 && FD_ISSET(dp->d_dev, &rsel)) {
X				--nsel;
X				readwrite(dp, 0);
X			}
X		}
X	}
X}
X
X/*
X *	Set the no delay flag for FD.
X */
Xint
Xset_nodelay(fd)
X	int	fd;
X{
X	int	flags;
X	int	err;
X
X	if ((flags = fcntl(fd, F_GETFL, 0)) < 0) {
X		err = errno;
X		error(ERRNO, "F_GETFL failed", err);
X		return err;
X	}
X	flags |= FNDELAY;
X	if (fcntl(fd, F_SETFL, flags) < 0) {
X		err = errno;
X		error(ERRNO, "F_SETFL failed", err);
X		return err;
X	}
X	return 0;
X}
X
X/*VARARGS2*/
Xvoid
Xerror(flags, fmt, va_alist)
X	int	flags;
X	char	*fmt;
X	va_dcl
X{
X	va_list	args;
X	int	err;
X
X	va_start(args);
X	(void) fprintf(efp, "%s: ", progname);
X	if (flags & ERRNO)
X		err = va_arg(args, int);
X	(void) vfprintf(efp, fmt, args);
X	if (flags & ERRNO)
X		(void) fprintf(efp, " - %s\n", str_error(err));
X	else
X		(void) fprintf(efp, "\n");
X	va_end(args);
X	if (flags & EXIT)
X		exit(1);
X}
X
Xvoid
Xkill_dev(dp)
X	Dev	*dp;
X{
X	if (dp->d_prev)
X		dp->d_prev->d_next = dp->d_next;
X	if (dp->d_next)
X		dp->d_next->d_prev = dp->d_prev;
X	if (dev_list == dp)
X		dev_list = dp->d_next;
X	if (dp->d_dev >= 0) {
X		FD_CLR(dp->d_dev, &rmask);
X		(void) close(dp->d_dev);
X		if (dp->d_dev == maxfd)
X			maxfd--;
X	}
X	if (dp->d_sock >= 0) {
X		FD_CLR(dp->d_sock, &rmask);
X		(void) close(dp->d_sock);
X		if (dp->d_sock == maxfd)
X			maxfd--;
X	}
X	if (dp->d_name)
X		free(dp->d_name);
X	free((char *) dp);
X}
X
Xvoid
Xinit_dev(dp)
X	Dev	*dp;
X{
X	int		len;
X	int		err;
X	int		fd;
X	char		buf[1024];
X	struct sgttyb	sg;
X
X	len = read(dp->d_sock, buf, sizeof(buf) - 1);
X	if (len == 0) {
X		error(0, "read eof from uninited socket");
X		kill_dev(dp);
X		return;
X	}
X	if (len < 0) {
X		err = errno;
X		error(ERRNO, "read from socket failed", err);
X		goto bad;
X	}
X	buf[len] = '\0';
X	if (!(dp->d_name = str_dup(buf))) {
X		err = ENOMEM;
X		error(0, "no memory");
X		goto bad;
X	}
X	if ((dp->d_dev = open(dp->d_name, O_RDWR, 0)) < 0) {
X		err = errno;
X		error(ERRNO, "%s: open failed", err, dp->d_name);
X		goto bad;
X	}
X
X	if (err = set_nodelay(dp->d_dev))
X		goto bad;
X	if (ioctl(dp->d_dev, TIOCGETP, (char *) &sg) < 0) {
X		err = errno;
X		error(ERRNO, "%s: ioctl getp failed", err, dp->d_name);
X		goto bad;
X	}
X	sg.sg_flags = RAW;
X	if (ioctl(dp->d_dev, TIOCSETP, (char *) &sg) < 0) {
X		err = errno;
X		error(ERRNO, "%s: ioctl setp failed", err, dp->d_name);
X		goto bad;
X	}
X	/*
X	 *  Don't latch onto this tty (need /dev/tty - ioctl failed on
X	 *  dp->d_dev)
X	 */
X	if ((fd = open("/dev/tty", O_RDONLY, 0)) >= 0) {
X		(void) ioctl(fd, TIOCNOTTY, (char *) 0);
X		(void) close(fd);
X	}
X
X	FD_SET(dp->d_dev, &rmask);
X	if (dp->d_dev > maxfd)
X		maxfd = dp->d_dev;
X	DEBUG(D_IO, ("+ %d %s\n", dp->d_dev, dp->d_name));
X	send_error(dp->d_sock, 0);	/* all ok */
X	return;
Xbad:
X	send_error(dp->d_sock, err);
X	kill_dev(dp);
X}
X
Xvoid
Xreadwrite(dp, fromsock)
X	Dev	*dp;
X	int	fromsock;
X{
X	char	buf[1024];
X	int	in, out;
X	int	nread;
X	int	nwriten;
X
X	if (fromsock) {
X		in = dp->d_sock;
X		out = dp->d_dev;
X	} else {
X		in = dp->d_dev;
X		out = dp->d_sock;
X	}
X	nread = read(in, buf, sizeof(buf) - 1);
X	if (nread == 0) {
X		DEBUG(D_IO, ("- %d/%d\n", in, out));
X		kill_dev(dp);
X		return;
X	}
X	if (nread < 0) {
X		if (errno == EWOULDBLOCK)
X			return;
X		error(ERRNO, "read from %s failed", errno,
X			fromsock ? "socket" : dp->d_name);
X		kill_dev(dp);
X		return;
X	}
X	buf[nread] = '\0';
X	DEBUG(D_IO, ("%d>%d\t\"%s\"\n", in, out, buf));
X	nwriten = write(out, buf, nread);
X	if (nwriten < 0) {
X		error(ERRNO, "write to %s failed", errno,
X			fromsock ? dp->d_name : "socket");
X		kill_dev(dp);
X	} else if (nwriten != nread)
X		error(0, "wrote %d of %d bytes to %s", nwriten, nread,
X			fromsock ? dp->d_name : "socket");
X}
X
Xvoid
Xsend_error(fd, err)
X	int	fd;
X	int	err;
X{
X	short	val;
X	int	nwriten;
X
X	val = htons((short) err);
X	nwriten = write(fd, (char *) &val, sizeof(val));
X	if (nwriten < 0)
X		error(ERRNO, "write to socket failed", errno);
X	else if (nwriten != sizeof(val))
X		error(0, "wrote %d of %d to socket", nwriten, sizeof(val));
X}
X
Xint
Xsig_hup()
X{
X	got_hup = 1;
X	return 0;
X}
X
X/*
X *	Things that should be in libc but aren't on many systems.
X */
X
Xchar *
Xstr_error(err)
X	int	err;
X{
X	extern int	sys_nerr;
X	extern char	*sys_errlist[];
X	static char	buf[32];
X
X	char		*p;
X
X	if (err < 0 || err >= sys_nerr)
X		(void) sprintf(p = buf, "Error: %d", err);
X	else
X		p = sys_errlist[err];
X	return p;
X}
X
Xchar	*
Xstr_dup(s)
X	char	*s;
X{
X	extern char	*strcpy();
X
X	char		*p;
X
X	if ((p = malloc((unsigned) (strlen(s) + 1))) != (char *) 0)
X		(void) strcpy(p, s);
X	return p;
X}
X
X#ifdef NEED_VPRINTF
Xint
Xvfprintf(fp, fmt, args)
X	FILE	*fp;
X	char	*fmt;
X	va_list	args;
X{
X	extern	int	_doprnt();
X
X	_doprnt(fmt, args, fp);
X	return ferror(fp) ? EOF : 0;
X}
X#endif /* NEED_VPRINTF */
SHAR_E_O_F
	chmod u=rwx rtty_serv.c
fi
exit 0