/*
 *
 * Copyright (C) 2000 Frans Kaashoek (kaashoek@mit.edu)
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 *
 */

#include "sfsusrv.h"
#include "parseopt.h"
#include "rxx.h"

filesrv *defsrv;
static str configfile;
static int usrv_standalone;
static short sfsusrv_port;

void 
client_accept (ptr<axprt_crypt> x)
{
  if (!x)
    fatal ("EOF from sfssd\n");
  vNew client (x);
}

static void
sfs_accept_standalone (sfsserv_cb cb, int sfssfd)
{
  sockaddr_in sin;
  bzero (&sin, sizeof (sin));
  socklen_t sinlen = sizeof (sin);
  int fd = accept (sfssfd, reinterpret_cast<sockaddr *> (&sin), &sinlen);
  if (fd >= 0) {
    tcp_nodelay (fd);
    ref<axprt_crypt> x = axprt_crypt::alloc (fd);
    (*cb) (x);
  } else if (errno != EAGAIN)
    warn ("accept: %m\n");
}

static void
start_server (filesrv *fsrv)
{
  setgid (sfs_gid);
  setgroups (0, NULL);

  warn ("version %s, pid %d\n", VERSION, getpid ());
  defsrv = fsrv;

  if (usrv_standalone) {
    int sfssfd = inetsocket (SOCK_STREAM, sfsusrv_port);
    if (sfssfd < 0)
      fatal ("binding TCP port %d: %m\n", sfsusrv_port);
    
    if (sfsusrv_port == 0) {
      struct sockaddr_in addr;
      socklen_t len = sizeof (addr);
      bzero (&addr, sizeof (addr));
      if (getsockname (sfssfd, (sockaddr *) &addr, &len) < 0) 
	fatal ("getsockname failed %m\n");
      sfsusrv_port = ntohs (addr.sin_port);
    }
    
    warn << "No sfssd detected, running in standalone mode.\n";
    warn << "Now exporting directory: " << fsrv->root->path << "\n";
    warn << "serving " << sfsroot << "/"  << sfsusrv_port << "@" 
	 << sfs_hostinfo2path (fsrv->servinfo.host) << "\n";
    
    close_on_exec (sfssfd);
    listen (sfssfd, 5);
    fdcb (sfssfd, selread, wrap (sfs_accept_standalone, wrap (client_accept), sfssfd));
  } else
    sfssd_slave (wrap (client_accept));
  
}


static filesrv *
parseconfig (str cf)
{
  parseargs pa (cf);
  bool errors = false;

  filesrv *fsrv = New filesrv;

  int line;
  vec<str> av;
  while (pa.getline (&av, &line)) {
    if (!strcasecmp (av[0], "export")) {
       str root (av[1]);
       if ((fsrv->root = fsrv->lookup_add (root)) == NULL) {
 	 errors = true;
	 warn << cf << ":" << line << ": non-existing root\n";
       }
    }
    else if (!strcasecmp (av[0], "hostname")) {
      if (av.size () != 2) {
	errors = true;
	warn << cf << ":" << line << ": usage: hostname name\n";
      }
      else if (fsrv->servinfo.host.hostname) {
	errors = true;
	warn << cf << ":" << line << ": hostname already specified\n";
      }
      else
	fsrv->servinfo.host.hostname = av[1];
    }
    else if (!strcasecmp (av[0], "keyfile")) {
      if (fsrv->sk) {
	  errors = true;
	  warn << cf << ":" << line << ": keyfile already specified\n";
      }
      else if (av.size () == 2) {
	str key = file2wstr (av[1]);
	if (!key) {
	  errors = true;
	  warn << av[1] << ": " << strerror (errno) << "\n";
	  warn << cf << ":" << line << ": could not read keyfile\n";
	}
	else if (!(fsrv->sk = import_rabin_priv (key, NULL))) {
	  errors = true;
	  warn << cf << ":" << line << ": could not decode keyfile\n";
	}
      }
      else {
	errors = true;
	warn << cf << ":" << line << ": usage: keyfile path\n";
      }
    }
  }

  fsrv->servinfo.release = sfs_release;
  fsrv->servinfo.host.type = SFS_HOSTINFO;
  if ((fsrv->servinfo.host.hostname.len () == 0)
      && !(fsrv->servinfo.host.hostname = myname ()))
    fatal ("could not figure out my host name\n");
  if (!fsrv->sk) 
    fatal ("no Keyfile specified\n");
  fsrv->servinfo.host.pubkey = fsrv->sk->n;
  if (!sfs_mkhostid (&fsrv->hostid, fsrv->servinfo.host))
    fatal ("could not marshal my own hostinfo\n");
  fsrv->servinfo.prog = ex_NFS_PROGRAM;
  fsrv->servinfo.vers = ex_NFS_V3;

  return fsrv;
}


static void
usage ()
{
  warnx << "usage: " << progname << " [-s] [-p port] -f configfile\n";
  exit (1);
}

int
main (int argc, char **argv)
{
  filesrv *fsrv;

  setprogname (argv[0]);
  sfsconst_init ();

  usrv_standalone = 0;
  sfsusrv_port = sfs_port;

  int ch;
  while ((ch = getopt (argc, argv, "f:sp:")) != -1)
    switch (ch) {
    case 'f':
      configfile = optarg;
      break;
    case 's':
      usrv_standalone = 1;
      break;
    case 'p':
      sfsusrv_port = atoi (optarg);
      break;
    case '?':
    default:
      usage ();
    }
  argc -= optind;
  argv += optind;

  if ( (argc > 0) || (!configfile) )
    usage ();

  fsrv = parseconfig (configfile);

  start_server (fsrv);
  amain ();
}
