/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/***************************************************************************
 *            miav_server.cc
 *
 *  Sat Aug 21 17:32:24 2004
 *  Copyright  2004  Bent Bisballe Nyeng
 *  deva@aasimon.org
 ****************************************************************************/

/*
 *    This file is part of MIaV.
 *
 *    MIaV 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 of the License, or
 *    (at your option) any later version.
 *
 *    MIaV 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 MIaV; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
 */
// For ETC
#include "config.h"

#include "miav_server.h"
#include "miav_config.h"

#include "info_console.h"

#include <stdio.h>
#include <string.h>

#include "server.h"
#include "socket.h"
#include "network.h"

#include <signal.h>
#include <errno.h>

// For getpwent and getgrent
#include <sys/types.h>
#include <grp.h>
#include <pwd.h>

#ifdef HAVE_GETOPT_H
#include <getopt.h>
#elif defined(HAVE_STDLIB_H)	// Solaris has getopt() here
#include <stdlib.h>
#else
#error "Don't know where getopt() is prototyped"
#endif

static const char usage_str[] =
"Usage: %s [options]\n"
"Options:\n"
"  -c file   Read configfile from 'file'\n"
"  -f        Run in foreground mode (non-background mode)\n"
"  -u user   Run as 'user' (overrides the configfile)\n"
"  -g group  Run as 'group' (overrides the configfile)\n"
"  -h        This message\n"
;


int main(int argc, char *argv[])
{
  char *config_file = ETC"/miav.conf";

  int optc;
  int lose = 0;
  bool foreground = false;

  char *user_opt = NULL;
  char *group_opt = NULL;

  // Parse the commandline
  while((optc = getopt(argc, argv, "c:fhu:g:")) != EOF) {
    switch(optc) {
    case 'c':
      config_file = strdup(optarg);
      if(!config_file) {
        fprintf(stderr, "Fatal: out of memory\n");
        return 1;
      }
      break;
    case 'u':
      user_opt = strdup(optarg);
      if(!user_opt) {
        fprintf(stderr, "Fatal: out of memory\n");
        return 1;
      }
      break;
    case 'g':
      group_opt = strdup(optarg);
      if(!group_opt) {
        fprintf(stderr, "Fatal: out of memory\n");
        return 1;
      }
      break;
    case 'f':
      foreground = true;
      break;
    case 'h':
      fprintf(stdout, usage_str, argv[0]);
      return 0;
    default:
      lose++;
      break;
    }
  }

  if(lose) {
    // Error message was printed by getopt
    return 1;
  }

  fprintf(stderr, "Using config file [%s]\n", config_file);

  MiavConfig cfg(config_file);
  MIaV::initConfig(&cfg);

  InfoConsole info;
  MIaV::initInfo(&info);

  string user;
  if(user_opt) user = string(user_opt);
  else user = *cfg.readString("server_user");

  string group;
  if(group_opt) group = string(group_opt);
  else group = *cfg.readString("server_group");

  // Fetch user id
  int uid = -1;
  struct passwd *p = getpwent();
  while(p) {
    if(strcmp(p->pw_name, user.c_str()) == 0) uid = p->pw_uid;
    p = getpwent();
  }
  if(uid == -1) {
    fprintf(stderr, "Could not find user \"%s\" in /etc/passwd file.\n", user.c_str());
  }

  // Fetch group id
  int gid = -1;
  struct group *g = getgrent();
  while(g) {
    if(strcmp(g->gr_name, group.c_str()) == 0) gid = g->gr_gid;
    g = getgrent();
  }
  if(gid == -1) {
    fprintf(stderr, "Could not find group \"%s\" in /etc/group file.\n", group.c_str());
  }

  if(!foreground) {
    fprintf(stderr, "Entering daemon mode\n");
    daemon(0,0);
    signal (SIGTERM, SIG_IGN);
    signal (SIGINT, SIG_IGN);
    signal (SIGHUP, SIG_IGN);
  } else {
    fprintf(stderr, "Running in foreground mode.\n");
  }

  int port = MIaV::config->readInt("server_port");
  pid_t childpid; // variable to store the child's pid

  signal(SIGCLD, SIG_IGN);  // Ved SIGCHILD til IGNORE maa wait/waitpid ikke kaldes 
                            //   (ellers kommer der kernel-brok)
  
  MIaV::info->info("Starting MIaV server v. %s", VERSION);
  MIaV::info->info("Listening on port %d", port);
  Socket *socket = new Socket(port);

  if(socket->hasError()) {
    MIaV::info->error("Listening socket has errors, quitting.");
    delete socket;
    return 1;
  }

  while(1) {
    Socket *csocket = new Socket(socket->slisten());

    if(socket->hasError()) {
      MIaV::info->error("Server socket has errors, quitting.");
      delete csocket;
      break;
    }

    if(csocket->hasError()) {
      MIaV::info->error("Child socket has errors, quitting.");
      delete csocket;
      break;
    }

    if(!csocket->isConnected()) {
      MIaV::info->error("Child socket is not connected, quitting.");
      delete csocket;
      break;
    }

    childpid = fork();
      
    switch(childpid) {
    case -1: // fork() returns -1 on failure
      MIaV::info->log("Fork error: %s", strerror(errno));
      exit(1);
    case 0: // fork() returns 0 to the child process
      delete socket; // Close listen socket.
      newConnection(csocket);
      delete csocket; // Close communication socket.
      exit(0);
      
    default: // fork() returns new pid to the parent process
      break;
    }
  }

  delete socket;
  return 0;
}