/*    main.cpp
 *
 *    Copyright (c) 1998-2000 Alexander Neundorf
 *    neundorf@kde.org
 *
 *    You may distribute under the terms of the GNU General Public
 *    License as specified in the COPYING file.
 *
 *    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.
 *
 */

#include "lisadefines.h"
#include "netmanager.h"

#ifdef LISA_DEBUG
#undef LISA_DEBUG
#endif

#ifdef dcerr
#undef dcerr
#endif

#define LISA_DEBUG 0
#define dcerr if (LISA_DEBUG==1) std::cerr<<"main "

#include <iostream>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>

// detect linux/glibc for the gnu style --args
#if defined(__linux__) || defined(__linux) || defined(linux)
#  include <features.h>
#  ifdef __GLIBC__
// only gnu libc has getopt.h... getopt(3) is defined to be in unistd.h
// by POSIX.2
#ifndef _GNU_SOURCE
#    define _GNU_SOURCE
#endif
#    include <getopt.h>
#  endif // __GLIBC__
#  define GNU_GETOPT
#endif // linux


void printVersion()
{
   const char * versionInfo=\
   "\r\nThis is the LAN Information Server LISa "MYVERSION"\r\n"\
   "It is free software according the GNU General Public License\r\n"\
   "Copyright (c) 2000-2003 by Alexander Neundorf\r\n"\
   "email: neundorf@kde.org\r\n";
   std::cout<<versionInfo<<std::endl;
}

void usage()
{
   printVersion();
   const char * usageInfo=\
      "-v, --version      prints out a short version info\n"\
      "-u, --unix         deprecated\n"\
      "-k, --kde1         deprecated\n"\
      "-K, --kde2         deprecated\n"\
      "                   lisa now looks always first for $(HOME)/.lisarc, then for /etc/lisarc\n"\
      "-c, --config=FILE  read this and no other configuration file\n"\
      "-q, --quiet        start quiet without the greeting message\n"\
      "-p, --port PORTNR  start the server on this portnumber\n"\
      "                   if you use this LISa won't be able to\n"\
      "                   cooperate with other LISa's in the network\n"\
      "-h, --help         you are currently reading it ;-)\n";
      std::cout<<usageInfo<<std::endl;
//I thought this would be the way to check wether long options are supported...
//#ifndef _GNU_SOURCE
//      std::cout<<"Please note that the long options are not supported on
//      this system"<<std::endl;
//#endif
}

NetManager *manager(0);

   
void destruct(int sigNumber)
{
   signal(sigNumber,SIG_IGN);
   dcerr<<"signal caught: "<<sigNumber<<", exiting"<<std::endl;
   //signal(sigNumber,&destruct);
   if (manager!=0)
      manager->~NetManager();
   exit(0);
}

void readConfig(int sigNumber)
{
   dcerr<<"readConfig(): signal caught: "<<sigNumber<<std::endl;
   signal(SIGHUP,SIG_IGN);
   if (manager!=0)
      manager->readConfig();
   signal(SIGHUP,&readConfig);
}

void printState(int sigNumber)
{
   dcerr<<"printState(): signal caught: "<<sigNumber<<std::endl;
   signal(SIGUSR1,SIG_IGN);
   if (manager!=0)
      manager->printState();
   signal(SIGUSR1,&printState);
}

void setSignalHandler()
{
   signal(SIGHUP,&readConfig);
	signal(SIGUSR1,&printState);

   signal(SIGINT,&destruct);
	signal(SIGQUIT,&destruct);
	signal(SIGILL,&destruct);
	signal(SIGTRAP,&destruct);
	signal(SIGABRT,&destruct);
	signal(SIGBUS,&destruct);
	signal(SIGSEGV,&destruct);
	signal(SIGUSR2,&destruct);
	signal(SIGPIPE,&destruct);
	signal(SIGALRM,&destruct);
	signal(SIGTERM,&destruct);
	signal(SIGFPE,&destruct);
#ifdef SIGPOLL
   signal(SIGPOLL, &destruct);
#endif
#ifdef SIGSYS
   signal(SIGSYS, &destruct);
#endif
#ifdef SIGVTALRM
   signal(SIGVTALRM, &destruct);
#endif
#ifdef SIGXCPU
   signal(SIGXCPU, &destruct);
#endif
#ifdef SIGXFSZ
   signal(SIGXFSZ, &destruct);
#endif
}

#ifdef GNU_GETOPT 
static struct option const long_opts[] =
{
  {"version", no_argument, 0, 'v'},
  {"quiet", no_argument, 0, 'q'},
  {"unix", no_argument, 0, 'u'},
  {"kde1", no_argument, 0, 'k'},
  {"kde2", no_argument, 0, 'K'},
  {"config", required_argument, 0, 'c'},
  {"port", required_argument, 0, 'p'},
  {"help", no_argument, 0, 'h'},
  {0, 0, 0, 0}
};
#endif

int main(int argc, char** argv)
{
   int quiet(0);
   int c(0);
   int configStyle(UNIXCONFIGSTYLE);
   MyString configFile;
   int portToUse(MYPORT);

//I thought this would be the way to check wether long options are supported...
#ifdef GNU_GETOPT 
   while ((c=getopt_long(argc, argv, "vqukKc:h", long_opts, 0))!=-1)
#else
   while ((c=getopt(argc, argv, "vqukKc:h"))!=-1)
#endif

   {
      char *endp(0);
      switch (c)
      {
      case 0:
         break;
      case 'v':
         printVersion();
         exit(0);
         break;
      case 'q':
         quiet=1;
         break;
      case 'u':
      case 'k':
      case 'K':
         std::cout<<"\a\nThe command line switches -k, -K, -u and \ntheir long versions "\
            "--kde1, --kde2 and --unix are not supported anymore.\n"\
            "Lisa will always first look for $(HOME)/.lisarc , then for /etc/lisarc.\n"\
            "If your lisa configuration file was created using an older version of \n"\
            "the KDE control center, copy the /root/.kde/share/config/lisarc to /etc and \n"\
            "then start lisa without any command line options.\n"<<std::endl;
         return 0;
         break;

      case 'c':
         configFile = optarg;
         configStyle = EXTRACONFIGSTYLE;
         break;

      case 'p':
         portToUse=strtol(optarg,&endp,10);
         if (endp!=0)
         {
            usage();
            exit(0);
         }
         break;

      case 'h':
      default:
         usage();
         exit(0);
         break;
      }
   }

   //fork and let the parent exit
   pid_t pid=fork();
   if (pid>0)
   {
      //this is the parent
      exit(0);
   }
   else if (pid<0)
   {
      std::cout<<"could not fork()"<<std::endl;
      exit(0);
   }
   //we will only read/write to/from this socket in the child process
   int rawSocket=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
   if (rawSocket==-1)
   {
      std::cout<<"could not create raw socket, root privileges required"<<std::endl;
      std::cout<<"take a look at the README for more information"<<std::endl;
      exit(0);
   }
   int bufferSize(60*1024);
   int on(1);
   setsockopt(rawSocket, SOL_SOCKET, SO_RCVBUF, (char*)&bufferSize,
	      sizeof(bufferSize));
   int result=setsockopt(rawSocket, SOL_SOCKET, SO_BROADCAST, (char*)&on,
			 sizeof(on));
   dcerr<<"setsockopt returns "<<result<<std::endl;
   //dropping root privileges
   //they will be regained once in the child process
   //for creating a raw socket

   //now dropping root privileges once and ever
   
   
   setuid(getuid());
   if (geteuid() != getuid())
     _exit(255);

   //according to R. Stevens the following three lines 
   //make daemons feel good :)
   setsid();
   chdir("/");
   umask(0);
   
   dcerr<<"starting, dropped root privileges"<<std::endl;
   dcerr<<"port: "<<portToUse<<" file: "<<configFile<<std::endl;
   NetManager netmanager(rawSocket,portToUse,configFile,configStyle,0);
   manager=&netmanager;
   dcerr<<"NetManager created"<<std::endl;
   setSignalHandler();

   netmanager.readConfig();
   if (netmanager.prepare())
   {
      if (!quiet)
      {
         printVersion();
         std::cout<<"\n\rrunning on port "<<portToUse<<"\n\rHave fun ! :-)"<<std::endl;
      }
      netmanager.run();
   };
   dcerr<<"server finished"<<std::endl;
}