daemon/main.cc
author "Tomas Zeman <tzeman@volny.cz>"
Sun, 20 Jan 2008 00:22:25 +0100
changeset 3 3d1d327cfa68
parent 2 b3afb9f1e801
permissions -rw-r--r--
vmailmgr-0.97

// Copyright (C) 1999,2000 Bruce Guenter <bruceg@em.ca>
//
// 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 of the License, 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 <config.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <unistd.h>
#include "cli++/cli++.h"
#include "daemon.h"

const char* cli_program = "vmailmgrd";
const char* cli_help_prefix = "vmailmgr support daemon\n";
const char* cli_help_suffix = "";
const char* cli_args_usage = "";
const int cli_args_min = 0;
const int cli_args_max = 0;
int opt_log_all = true;
int opt_verbose = false;

// This program is the local server that is used to handle managing
// virtual domains from a web or remote interface.
//
// F<vmailmgrd> logs its activity to standard output, and as such
// requires its output to be piped through a tool to record those
// logs, such as F<multilog> (from the F<daemontools> package).

cli_option cli_options[] = {
  { 'd', 0, cli_option::flag, 0, &opt_log_all,
    "Log only requests that fail", 0 },
  { 'D', 0, cli_option::flag, 1, &opt_log_all,
    "Log all requests (default)", 0 },
  { 'v', 0, cli_option::flag, 0, &opt_verbose,
    "Log non-verbosely (default)", 0 },
  { 'V', 0, cli_option::flag, 1, &opt_verbose,
    "Log verbosely", 0 },
  // Log verbose messages regarding the system's status.
  // Note that this flag implies C<-D>.
  {0}
};

// SEE ALSO
//
// vdeliver(1), checkvpw(8)

#define TIMEOUT 1

static inline void die(const char* msg)
{
  perror(msg);
  exit(1);
}

static void finishreq()
{
  alarm(0);
  close(0);
  close(1);
}

static void abortreq(const char* m)
{
  logresponse(response(response::bad, m));
  finishreq();
}

static RETSIGTYPE handle_hup(int)
{
  signal(SIGHUP, handle_hup);
  log("Stray SIGHUP caught");
}

static RETSIGTYPE handle_alrm(int)
{
  signal(SIGALRM, handle_alrm);
  abortreq("Timed out waiting for remote");
  exit(1);
}

static RETSIGTYPE handle_pipe(int) 
{
  signal(SIGPIPE, handle_pipe);
  abortreq("Connection to client lost");
  exit(1);
}

static RETSIGTYPE handle_intr(int)
{
  signal(SIGINT, handle_intr);
  signal(SIGTERM, handle_intr);
  log("Stray interrupt caught");
}

bool decode_string(mystring& str, uchar*& buf, ssize_t& buflen)
{
  ssize_t length = (buf[0] << 8) | buf[1];
  buf += 2; buflen -= 2;
  if(length > buflen)
    return false;
  str = mystring((char*)buf, length);
  buf += length; buflen -= length;
  return true;
}
  
#define FAIL(MSG) do { abortreq(MSG ", aborting"); return 0; } while(0);

command* decode_data(uchar* ptr, ssize_t length)
{
  uchar argcount = *ptr++;
  --length;
  mystring cmdstr;
  if(!decode_string(cmdstr, ptr, length))
    FAIL("Couldn't decode the command string");
  if(cmdstr.empty())
    FAIL("Empty command string");
  command* cmd = new command(cmdstr, argcount);
  for(unsigned i = 0; i < argcount; i++) {
    mystring str;
    if(!decode_string((*cmd)[i], ptr, length)) {
      delete cmd;
      FAIL("Error decoding a command parameter");
    }
  }
  return cmd;
}

command* read_data() 
{
  alarm(TIMEOUT); // avoid denial-of-service by faulty clients

  uchar hdrbuf[3];
  switch(read(0, &hdrbuf, 3)) {
  case -1: FAIL("read system call failed or was interrupted");
  case 3: break;
  default: FAIL("Short read while reading protocol header");
  }
  if(hdrbuf[0] != 2)
    FAIL("Invalid protocol from client");
  ssize_t length = (hdrbuf[1] << 8) | hdrbuf[2];
  uchar buf[length];
  if(read(0, buf, length) != length)
    FAIL("Short read while reading message data");
  alarm(0);
  return decode_data(buf, length);
}

int cli_main(int, char**)
{
  if(opt_verbose)
    opt_log_all = true;

  signal(SIGALRM, handle_alrm);
  signal(SIGPIPE, handle_pipe);
  signal(SIGINT, handle_intr);
  signal(SIGTERM, handle_intr);
  signal(SIGHUP, handle_hup);
  signal(SIGQUIT, handle_intr);
  
  if(opt_verbose)
    log("Accepted connection");
  command* cmd = read_data();
  if(cmd) {
    response resp = dispatch_cmd(*cmd, 1);
    logresponse(resp);
    alarm(TIMEOUT);
    if(!resp.write(1))
      abortreq("Error writing response");
    finishreq();
    delete cmd;
  }

  return 0;
}