--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/daemon/main.cc Wed Jan 16 22:39:43 2008 +0100
@@ -0,0 +1,194 @@
+// 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 controls the operation of many
+// parts of this package.
+// It is currently used to handle password checking and virtual user
+// lookups, but will eventually contain the functionality to handle adding
+// and deleting users and aliases, and changing passwords.
+//
+// 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<accustamp> and F<cyclog> (from the
+// F<daemontools> package), or
+// F<splogger> (included with the F<qmail> 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;
+}