/* mini-inetd: Waits on a TCP socket, spawns a subprocess for each
   incoming connection to handle the connection.  Can be used to test
   servers that expect to be launched from inetd.

   Copyright (C) 1997, 2000 Jamie Lokier.

   This file is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the
   Free Software Foundation; either version 2, or (at your option) any
   later version.

   This file is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this file; see the file COPYING.  If not, write to the
   Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */

#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <errno.h>

#define PERROR(p) do { fprintf (stderr, "%s: %s: %s\n", argv [0], \
		       (p), strerror (errno)); exit (EXIT_FAILURE); } while (0)
#define ERROR(e) do { fprintf (stderr, "%s: %s\n", argv [0], (e)); \
  		      exit (EXIT_FAILURE); } while (0)

void
do_wait (void)
{
  waitpid (-1, 0, WNOHANG);
}

int
main (int argc, char ** argv)
{
  struct protoent * proto;
  int s, port;

  if (argc < 4)
    {
      fprintf (stderr, "Usage: %s tcp-port executable argv0 args...\n",
	       argv [0]);
      exit (EXIT_FAILURE);
    }

  proto = getprotobyname ("tcp");
  if (proto == 0)
    PERROR ("getprotobyname");

  s = socket (AF_INET, SOCK_STREAM, proto->p_proto);
  if (s == -1)
    PERROR ("socket");

  {
    char * eptr;
    long lport = strtol (argv [1], &eptr, 0);
    if (*eptr == 0)
      {
	if (lport < 0 || lport > 65535)
	  ERROR ("port number out of range");
	port = htons (lport);
      }
    else
      {
	struct servent * serv = getservbyname (argv [1], "tcp");
	if (serv == 0)
	  ERROR ("unknown TCP service name");
	port = serv->s_port;	/* This is already in network byte order. */
      }
  }

  {
    struct sockaddr_in addr;

    memset (&addr, 0, sizeof (addr));
    addr.sin_family = AF_INET;
    addr.sin_port = port;

    if (bind (s, (struct sockaddr *) &addr, sizeof (addr)) != 0)
      PERROR ("bind");
  }

  if (listen (s, 128) == -1)
    PERROR ("listen");

  atexit (do_wait);

  while (1)
    {
      struct sockaddr addr;
      int addrlen = sizeof (addr);
      int fd = accept (s, &addr, &addrlen);
      int pid;

      if (fd == -1)
	PERROR ("accept");

      waitpid (-1, 0, WNOHANG);

      /* We've got a connection in socket `fd'.
	 Spawn a subprocess to handle it. */

      pid = fork ();
      if (pid == -1)
	PERROR ("fork");
      if (pid == 0)
	{
	  if ((fd != 0 && dup2 (fd, 0) == -1))
	    ERROR ("dup2");
	  if ((fd != 1 && dup2 (fd, 1) == -1))
	    ERROR ("dup2");
	  if (fd != 0 && fd != 1)
	    close (fd);
	  close (2);

	  execve (argv [2], argv + 3, __environ);
	  PERROR ("execve");
	}

      close (fd);
    }

  return EXIT_SUCCESS;
}

