/*
_____       _    _    Corso   Italia,  178
(_|__   .  (_   |_|_  56125           Pisa
(_|_) |)|(()_)()| |   tel.  +39  050 46380
  |   |               picosoft@picosoft.it

 Copyright (C) Picosoft s.r.l. 2002

 This program 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 program 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 program; if not, write to the Free Software
 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/
# include <stdio.h>
# include <errno.h>
# ifdef WIN32
# include <winsock2.h>
# include <windows.h>
# include <io.h>

void
myError(char *msg)
{
   LPVOID msgBuff;
   char pntBuffer[512];
   long nChars;
   char *err = strerror(errno);

   FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
                  NULL, GetLastError(),
                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                  (LPTSTR) &msgBuff, 0, NULL);

   wsprintf(pntBuffer, "error: %s, code = %d %s\n",
                        msg, GetLastError(), msgBuff);

   WriteConsole(GetStdHandle(STD_ERROR_HANDLE),pntBuffer,
                lstrlen(pntBuffer),&nChars,NULL);

   LocalFree(msgBuff);
   ExitProcess(GetLastError());
}	

void
WINAPI run (char *command, SOCKET sock)
{
   STARTUPINFO si;
   PROCESS_INFORMATION pi;

   memset(&si,0,sizeof(si));
   
   si.cb = sizeof(STARTUPINFO);
   si.dwFlags = STARTF_USESTDHANDLES;
   si.hStdOutput = (HANDLE) sock;
   si.hStdInput  = (HANDLE) sock;
   si.hStdError  = GetStdHandle(STD_ERROR_HANDLE);

   if (!CreateProcess(NULL, (char *) command, NULL, NULL, TRUE,
                      CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi))
      myError("CreateProcess");

   closesocket (sock);
}
# else
# include <signal.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <sys/time.h>
# include <sys/wait.h>
# include <netinet/in.h>
# include <netdb.h>
# include <unistd.h>

int sock, ns;
int mainpid;
int newgrp;
# define SOCKET int

void
ExitChild (sig)
int sig;
{
   int pid, status;

   /* while ((pid = wait3 (&status, WNOHANG, 0)) > 0)  { }*/
   while ((pid = waitpid (-1, &status, WNOHANG)) > 0)  {
   }
   signal (SIGCLD, ExitChild);
}

void
exitmain(sig)
int sig;
{
   FILE * mylogerr;
   char namemylogerr[100];
   int rc = 0;
}

void
myError(char *msg)
{
   perror (msg);
}

# endif

char *usage = "picoserv [-f] command-line port-number\n";

int
main(argc, argv)
int argc;
char *argv[];
{
   struct sockaddr_in sin;
   int namelen = sizeof(sin);
# ifdef WIN32
   WSADATA wsaData;
# else
   int pid;
# endif
   FILE *out = stdout;
   char *command = 0;
   unsigned short portnum = 0;
   int foreground = 0;
   SOCKET sock, ns;
   int mainpid;

   if (argc == 3) {
      command = argv[1];
      portnum = atoi (argv[2]);
# ifndef WIN32
   } else if (argc == 4) {
      foreground = 1;
      command = argv[2];
      portnum = atoi (argv[3]);
# endif
   }
   if (command == 0 || portnum == 0) {
      myError (usage);
      return 99;
   }
# ifdef WIN32
   if (WSAStartup(MAKEWORD (2,0), &wsaData) != 0) {
      myError ("WSAStartup");
      return 99;
   }
   mainpid = 0;;
   if ((sock = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0)) < 0) {
      myError("socket");
      exit(1);
   }
# else
   signal (SIGCLD, ExitChild);
   if (!foreground) {
      if ((pid = fork()) == 0) {
         newgrp = setpgrp();
         mainpid = getpid();
      } else {
         return 0;
      }
   } else
      mainpid = getpid();
   if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
      myError("socket");
      exit(1);
   }
# endif
 
   fprintf (stderr, "Starting service %s on port %d ... ", command, portnum);
   fflush (stderr);

/* In questa struttura metto dei dati che mi possono riservire */

   sin.sin_family = AF_INET;
   sin.sin_port = htons(portnum); /* Se 0 assegna automaticamente il numero */
   sin.sin_addr.s_addr =  0;
   memset (sin.sin_zero, 0, sizeof(sin.sin_zero));

   setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, "", 0);

   if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
      myError("bind");
      exit(2);
   }

   if (listen(sock, 128) < 0) {
      myError("listen");
      exit(4);
   }

   fprintf (stderr, "OK\n");
   printf ("%d\n", mainpid);
   for ( ; ; ) {
      if ((ns = accept (sock, (struct sockaddr *)&sin, &namelen)) < 0) {
# ifndef WIN32
        if (errno == EINTR)
           continue;
# endif
        myError("accept");
	     exit(5);
      }
# ifdef WIN32
      run (command, ns);
# else
      if ((pid = fork()) == -1) {
         myError("fork:");
         exit(6);
      }
      if (pid == 0) {
         close(sock);
         close(1);
         dup(ns);
         close(0);
         dup(ns);
         system (command);
         close (ns);
         exit (0);
      } else {
         close (ns);
      }
# endif
   }
# ifdef WIN32
   closesocket (sock);
# else
   close (sock);
# endif
   return 0;
}
