wsh/
wsh/binsrc/
wsh/docs/help/
wsh/docs/old/
wsh/etc/
wsh/src/util/
/*
 * Example of a concurrent server using TCP protocol.
 */

#include	"tcp.h"


static char *copyright=
"@(#) tcpserv 2.1 Shareware Copyright (c) 1993 Sam Lantinga\n";

extern struct passwd *login_auth();
extern void outa_here(), daemonize();

void  relief();
char *addr2host();

extern int errno;
int 	childpid=0, newfd;
int	netdebug=0, telnet=0;
int	login=0, doutmp=0;
int	demonize=0;
char   *pname;
char    remotehost[64];

#define MAXKIDS	256		/* The absolute maximum forked processes */
int children[MAXKIDS];		/* Forked processes handling tcp connections */
static int maxkids=0;		/* The maximum number of forked processes */

main(argc, argv)
int	argc;
char	*argv[];
{
	extern char		*optarg;
	extern int 		optind, errno;

	int			fd, streamfd, clilen, port;
	int			i, j, c, found;
	char		       *ptr, *args[256];
	struct passwd	       *pw=NULL;
	struct sockaddr_in	cli_addr, serv_addr;
	static char *Usage=
"Usage: %s [-f maxproc] [-d] [-t] [-u|-n] port [program [arguments]]\n";

	pname = argv[0];

	/* Get the run-time arguments. */

	while ( (c=getopt(argc, argv, "df:ntux")) != EOF )
	{
		switch (c)
		{
			/* Allow forking children up to a point. */
			case 'f':  if ( ! (maxkids=atoi(optarg)) ) {
					fprintf(stderr, 
				"Maximum fork limit must be an integer.\n");
					exit(1);
				   }
				   if ( maxkids > MAXKIDS ) {
					fprintf(stderr, 
			"The maximum allowed children is %d\n", MAXKIDS);
					exit(1);
				   }
				   for ( i=0; i<maxkids; ++i )
					children[i]=0;
				   break;
			case 'n':  login=1;
				   doutmp=1;
				   break;
			case 't':  telnet=1;
				   break;
			case 'u':  doutmp=1;
				   break;
			case 'd':  demonize=1;
				   break;
			case 'x':  netdebug=1;
				   break;
			case '?':  fprintf(stderr, Usage, pname);
				   exit(1);
			default:   fprintf(stderr, Usage, pname);
				   exit(1);
		}
					
	}
	argc-=optind;

	if ( ! login && (argc < 2 ) )
	{
		fprintf(stderr, "Requires a program to run at a connection.\n");
		fprintf(stderr, Usage, pname);
		exit(1);
	}
	if ( (port=atoi(argv[optind++])) < 1 ) 
	{
		fprintf(stderr, "Port must be an integer.\n");
		exit(1);
	}

	/* Copy the command line arguments to a local array */
	for ( i=0; argv[optind]; ++i )
	{
		if ((args[i]=(char *)malloc(strlen(argv[optind])+1)) == NULL)
		{
			perror("Malloc error");
			exit(2);
		}
		strcpy(args[i], argv[optind++]);
	}
	args[i]=NULL;
		
	if ( login && (argc > 1) && args[0][0] != '/' )
	{
		fprintf(stderr, "Login shell must be a full pathname.\n");
		exit(1);
	}

	/* Erase the command line arguments */
	for ( i=1; argv[i]; ++i )
	{
		for ( j=0; argv[i][j]; ++j )
			argv[i][j]=' ';
		argv[i]=NULL;
	}

	/* Set up tty modes for the pty */
	(void) tty_getmode(0);

	/*
	 * Open a TCP socket (an Internet stream socket).
	 */

	if ( (fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		perror("Can't open stream socket");
		exit(3);
	}

	/*
	 * Bind our local address so that the client can send to us.
	 */

	d_zero((char *) &serv_addr, sizeof(serv_addr));
	serv_addr.sin_family      = AF_INET;
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_addr.sin_port        = htons(port);

	if (bind(fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
		perror("Can't bind local address");
		exit(3);
	}

	if ( listen(fd, 3) < 0 ) {
		perror("listen() error");
		exit(3);
	}

	/* Run as a daemon, if desired. */
	if ( demonize && !netdebug )
		daemonize();

	signal(SIGPIPE, SIG_IGN);	/* Don't die on broken connections */
	signal(SIGCLD, relief);		/* We don't want zombie children */

	for ( ; ; ) {
		/*
		 * Wait for a connection from a client process.
		 * This is an example of a concurrent server.
		 */

		clilen = sizeof(cli_addr);
		if ((newfd=accept(fd, (struct sockaddr *)&cli_addr, &clilen))<0)
		{
			if ( errno == EINTR )
				continue;
			else {
				perror("accept() error");
				exit(3);
			}
		}
		(void) addr2host((struct sockaddr *)&cli_addr, remotehost);

		if ( netdebug ) {
			fprintf(stderr, "Connection requested from %s\n", 
								remotehost);
		}

		/* Check to see if we can fork */
		if ( maxkids ) 
		{
			for ( i=0, found=0; i<maxkids; ++i )
			{
				if ( !children[i] || kill(children[i], 0 < 0) )
				{
					found=1;
					break;
				}
			}
			if ( ! found )
			{  /* We had too many children already */
				ptr="Server busy.  Please try again later.\r\n";
				write(newfd, ptr, strlen(ptr));
				close(newfd);
				continue;
			}
		}

		if ( (childpid = fork()) < 0)
		{

			if ( errno == EAGAIN )
			{
				ptr="Can't fork.  Try again later.\r\n";
				write(newfd, ptr, strlen(ptr));
				close(newfd);
				continue;
			}
			else {
				perror("fork() error");
				exit(3);
			}
		}
		else if (childpid == 0) 
		{	/* child process */
			close(fd);  

			if ( telnet )
				init_telnet(newfd, 1);

			/* Do login authentication if desired */
			if ( login )
			{
				if ( (pw=login_auth(newfd)) == NULL )
				{
					close(newfd);
					exit(0);
				}
			}

			/* Set up the pty and shell */
			if ( (streamfd=setup(args, pw)) < 0 )
			{
				perror("setup");
				exit(0);
			}
			/* We now have no controlling tty */
				
			/* redirect stderr to newfd */
			close(2);  dup(newfd);
			close(0);  close(1);

			/* sync read/write the fd's */
			sync_rw(newfd, streamfd);   /* process the request */
#ifdef OLDDEBUG
			fprintf(stderr, "sync-rw() returned.\n");
#endif
			outa_here();
		}
		close(newfd);		/* parent process */
		children[i]=childpid;
	}
}


/* A function to prevent zombie processes from clogging the system */

void relief(sig)
int sig;
{
	wait(NULL);
	signal(sig, relief);
}


/* A function to convert a sockaddr_in structure to its corresponding
   hostname (or its IP address if the hostname can't be found)  
*/

char *addr2host(addrptr, host)
struct sockaddr *addrptr;
char *host;
{
	struct in_addr peeraddr;        /* The address of the connector */
	struct hostent *hp;
	int i;
	char *ptr, tmp[12];


	/*
         * The set of four bytes in the third buffer position are
         * the 32 bit ip address of the connector.
         */

        d_copy((&(addrptr->sa_data[2])), (char *)(&peeraddr.s_addr),
                                                sizeof(peeraddr.s_addr));
	
	/* Look up the address in the host tables */
	if ( (hp=gethostbyaddr((char *)(&peeraddr.s_addr),
                                sizeof(peeraddr.s_addr), AF_INET)) == NULL )
	{
		/* Convert it to a dot-ip address */
		*host='\0';
		for ( i=0, ptr=(char *)&peeraddr.s_addr; i<3; ++i, ++ptr )
		{
			sprintf(tmp, "%d.", 
				((int)*ptr < 0)?((int)*ptr+256):(int)*ptr);
			strcat(host, tmp);
		} 
		sprintf(tmp, "%d", ((int)*ptr < 0)?((int)*ptr+256):(int)*ptr);
		strcat(host, tmp);
	}
        else
                strcpy(host, hp->h_name);

	return(host);
}