Added support for locking to determine if configuration-specific daemon
authorMatthew Mondor <mmondor@pulsar-zone.net>
Sat, 5 Mar 2005 15:33:34 +0000 (15:33 +0000)
committerMatthew Mondor <mmondor@pulsar-zone.net>
Sat, 5 Mar 2005 15:33:34 +0000 (15:33 +0000)
is already running, along with LOCK_PATH configuration directive.

mmsoftware/mmmail/etc/mmsmtpd.conf
mmsoftware/mmmail/src/mmpop3d/mmpop3d.c
mmsoftware/mmmail/src/mmpop3d/mmpop3d.conf.5
mmsoftware/mmmail/src/mmpop3d/mmpop3d.h
mmsoftware/mmmail/src/mmrelayd/mmrelayd.c
mmsoftware/mmmail/src/mmsmtpd/mmsmtpd.c
mmsoftware/mmmail/src/mmsmtpd/mmsmtpd.conf.5
mmsoftware/mmmail/src/mmsmtpd/mmsmtpd.h

index b133ba2..c2227e2 100644 (file)
@@ -1,4 +1,4 @@
-; $Id: mmsmtpd.conf,v 1.5 2004/09/27 21:24:12 mmondor Exp $
+; $Id: mmsmtpd.conf,v 1.6 2005/03/05 15:33:33 mmondor Exp $
 ;
 ; mmsmtpd configuration file (/etc/mmsmtpd.conf)
 ; and # are considered comments, and can happen at start or end of line.
@@ -17,6 +17,9 @@ ASYNC_PROCESSES       3
 ; files will be required in the new root for instance.
 ;CHROOT_DIR ""
 ;
+; Location of lock file used to determine if daemon already runs
+LOCK_PATH      "/var/run/mmsmtpd.lock"
+;
 ; Location of the path where to store our process ID
 PID_PATH       "/var/run/mmsmtpd.pid"
 ;
index 1d7d3ba..ab1de52 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: mmpop3d.c,v 1.39 2004/11/15 23:02:05 mmondor Exp $ */
+/* $Id: mmpop3d.c,v 1.40 2005/03/05 15:33:33 mmondor Exp $ */
 
 /*
  * Copyright (C) 2001-2004, Matthew Mondor
@@ -81,7 +81,7 @@
 
 MMCOPYRIGHT("@(#) Copyright (c) 2001-2004\n\
 \tMatthew Mondor. All rights reserved.\n");
-MMRCSID("$Id: mmpop3d.c,v 1.39 2004/11/15 23:02:05 mmondor Exp $");
+MMRCSID("$Id: mmpop3d.c,v 1.40 2005/03/05 15:33:33 mmondor Exp $");
 
 
 
@@ -201,6 +201,7 @@ main(int argc, char **argv)
     cres_t cres;
     carg_t *cargp;
     carg_t cargs[] = {
+       {CAT_STR, CAF_NONE, 1, 255, "LOCK_PATH", CONF.LOCK_PATH},
        {CAT_STR, CAF_NONE, 1, 255, "CHROOT_DIR", CONF.CHROOT_DIR},
        {CAT_STR, CAF_NONE, 1, 255, "PID_PATH", CONF.PID_PATH},
        {CAT_STR, CAF_NONE, 1, 31, "USER", CONF.USER},
@@ -265,6 +266,7 @@ main(int argc, char **argv)
 
     /* Set defaults */
     *CONF.CHROOT_DIR = 0;
+    mm_strcpy(CONF.LOCK_PATH, "/var/run/mmpop3d.lock");
     mm_strcpy(CONF.PID_PATH, "/var/run/mmpop3d.pid");
     mm_strcpy(CONF.USER, "mmmail");
     mm_strcpy(CONF.GROUPS, "mmmail,mmstat");
@@ -316,6 +318,13 @@ main(int argc, char **argv)
        exit(-1);
     }
 
+    /* Ensure that only a single instance with same configuration runs */
+    if (lock_check(CONF.LOCK_PATH) == -1) {
+       printf("\nCouldn't lock file '%s' (already running?)\n\n",
+               CONF.LOCK_PATH);
+       exit(-1);
+    }
+
     /* Post parsing */
     if (!mmmapstring(cmap, CONF.LOG_FACILITY, &facility)) {
        printf("\nUnknown syslog facility %s\n\n", CONF.LOG_FACILITY);
@@ -938,6 +947,24 @@ free_clientenv(clientenv *clenv)
 }
 
 
+/*
+ * Used to ensure that only a single server instance with the same
+ * configuration runs at the same time.
+ */
+static int
+lock_check(const char *path)
+{
+    int        fd;
+
+    if ((fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600)) != -1) {
+       if (flock(fd, LOCK_EX | LOCK_NB) == 0)
+           return 0;
+       (void) close(fd);
+    }
+
+    return -1;
+}
+
 /* Used to initialize command hash table */
 static bool
 hash_commands(struct command *cmd, size_t min)
index 5599f0d..91829d6 100644 (file)
@@ -1,4 +1,4 @@
-.\" $Id: mmpop3d.conf.5,v 1.7 2004/08/11 14:57:23 mmondor Exp $
+.\" $Id: mmpop3d.conf.5,v 1.8 2005/03/05 15:33:33 mmondor Exp $
 .\"
 .\" Copyright (C) 2001-2004, Matthew Mondor
 .\" All rights reserved.
@@ -106,6 +106,13 @@ to re-establish it, although normally local UNIX socket connections are
 reliable. This is not a problem with remote server connections, where
 re-establishing may happen more often, and the jail won't matter.
 .Pp
+.It Nm LOCK_PATH Ar "file"
+Location of lock file which should be used to determine if the daemon is
+already running. Note that this path resides outside of the
+.Xr chroot 2
+if enabled using
+.Fa CHROOT_DIR .
+.Pp
 .It Nm PID_PATH Ar "file"
 Location where
 .Nm mmpop3d
@@ -287,6 +294,7 @@ The following defaults are used:
 .Bd -literal -offset indent -compact
 ASYNC_PROCESSES         3
 CHROOT_DIR              ""
+LOCK_PATH               "/var/run/mmpop3d.lock"
 PID_PATH                "/var/run/mmpop3d.pid"
 USER                    mmmail
 GROUPS                  mmmail,mmstat
index 46e6630..7b5d296 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: mmpop3d.h,v 1.15 2004/08/21 10:00:12 mmondor Exp $ */
+/* $Id: mmpop3d.h,v 1.16 2005/03/05 15:33:34 mmondor Exp $ */
 
 /*
  * Copyright (C) 2001-2004, Matthew Mondor
@@ -86,7 +86,7 @@
 
 /* We store config file read results in this structure */
 typedef struct config {
-    char CHROOT_DIR[256], PID_PATH[256], USER[32], GROUPS[256],
+    char LOCK_PATH[256], CHROOT_DIR[256], PID_PATH[256], USER[32], GROUPS[256],
        LOG_FACILITY[32], LISTEN_IPS[1024], SERVER_NAMES[1024], DB_HOST[64],
        DB_USER[32], DB_PASSWORD[32], DB_DATABASE[32];
     long ALLOC_BUFFERS, LOG_LEVEL, LISTEN_PORT, MAX_ERRORS, MAX_IPS,
@@ -202,6 +202,7 @@ static int main_last(clientenv *);
 static int main_top(clientenv *);
 static int main_page(clientenv *);
 
+static int lock_check(const char *);
 static bool hash_commands(struct command *, size_t);
 static u_int32_t commandnode_keyhash(const void *, size_t);
 static int commandnode_keycmp(const void *, const void *, size_t);
index bcd2105..5df4837 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: mmrelayd.c,v 1.7 2005/03/01 13:13:33 mmondor Exp $ */
+/* $Id: mmrelayd.c,v 1.8 2005/03/05 15:33:34 mmondor Exp $ */
 
 /*
  * Copyright (C) 2004-2005, Matthew Mondor
@@ -54,9 +54,7 @@
  *   all process them at once. We should have a maximum admin-specified
  *   delivery concurrency.
  *
- * - Add lock file and check with flock(2) to make sure it's not already
- *   running. In fact, add this functionality to mmsmtpd(8), mmpop3d(8) and
- *   mmftpd(8).
+ * - Take care of optional chroot(2)ing
  */
 
 
@@ -73,6 +71,7 @@
 
 #include <syslog.h>
 #include <unistd.h>
+#include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <errno.h>
@@ -86,7 +85,7 @@
 
 MMCOPYRIGHT("@(#) Copyright (c) 2004\n\
 \tMatthew Mondor. All rights reserved.\n");
-MMRCSID("$Id: mmrelayd.c,v 1.7 2005/03/01 13:13:33 mmondor Exp $");
+MMRCSID("$Id: mmrelayd.c,v 1.8 2005/03/05 15:33:34 mmondor Exp $");
 
 
 
@@ -97,19 +96,19 @@ MMRCSID("$Id: mmrelayd.c,v 1.7 2005/03/01 13:13:33 mmondor Exp $");
 #define        DAEMON_NAME     "mmrelayd"
 
 struct CONFIG {
-       char CHROOT_DIR[256], PID_PATH[256], USER[32], GROUPS[256],
-           LOG_FACILITY[32], DB_HOST[64], DB_USER[32], DB_PASSWORD[32],
-           DB_DATABASE[32], MAIL_DIR[256], RELAY_SMARTHOST[64],
-           SOCKET_PATH[256];
+       char LOCK_PATH[256], CHROOT_DIR[256], PID_PATH[256], USER[32],
+           GROUPS[256], LOG_FACILITY[32], DB_HOST[64], DB_USER[32],
+           DB_PASSWORD[32], DB_DATABASE[32], MAIL_DIR[256],
+           RELAY_SMARTHOST[64], SOCKET_PATH[256];
        long INPUT_TIMEOUT, BANDWIDTH_IN, BANDWIDTH_OUT, GBANDWIDTH_IN,
            GBANDWIDTH_OUT;
 };
 
-int                    main(int, char **);
+int            main(int, char **);
 
-static int             config_read(const char *);
-static int             notify_open(const char *, gid_t);
-static int             privileges_drop(void);
+static int     config_read(const char *);
+static int     lock_check(const char *);
+static int     notify_open(const char *, gid_t);
 
 
 
@@ -120,6 +119,7 @@ static int          privileges_drop(void);
 static struct CONFIG   CONF;
 
 static carg_t          cargs[] = {
+       {CAT_STR, CAF_NONE, 1, 255, "LOCK_PATH", CONF.LOCK_PATH},
        {CAT_STR, CAF_NONE, 1, 255, "CHROOT_DIR", CONF.CHROOT_DIR},
        {CAT_STR, CAF_NONE, 1, 255, "PID_PATH", CONF.PID_PATH},
        {CAT_STR, CAF_NONE, 1, 31, "USER", CONF.USER},
@@ -169,6 +169,23 @@ main(int argc, char **argv)
        char    *conf_file = "/usr/local/etc/mmrelayd.conf";
 
        /*
+        * Some sanity checking on privileges
+        */
+#if !defined(NODROPPRIVS)
+       if (getuid() != 0) {
+               syslog(LOG_NOTICE, "Only the superuser may launch %s",
+                   DAEMON_NAME);
+               return -1;
+       }
+#else
+       if (getuid() == 0) {
+               syslog(LOG_NOTICE, "%s compiled with NODROPPRIVS, refusing "
+                   "to run as the superuser", DAEMON_NAME);
+               return -1;
+       }
+#endif
+
+       /*
         * Parse command line arguments
         */
        {
@@ -194,8 +211,9 @@ main(int argc, char **argv)
        }
 
        /*
-        * And configuration file, bind notifications socket, drop privileges,
-        * detach and open persistent mysql connection.
+        * And configuration file, bind notifications socket, establish
+        * persistent mysql connection, chroot and drop privileges.
+        * XXX
         */
        if (config_read(conf_file) == -1)
                exit(EXIT_FAILURE);
@@ -223,6 +241,7 @@ config_read(const char *file)
         * Set defaults
         */
        *CONF.CHROOT_DIR = '\0';
+       (void) mm_strcpy(CONF.LOCK_PATH, "/var/run/mmrelayd.lock");
        (void) mm_strcpy(CONF.PID_PATH, "/var/run/mmrelayd.pid");
        (void) mm_strcpy(CONF.USER, "mmmail");
        (void) mm_strcpy(CONF.GROUPS, "mmmail,mmstat");
@@ -246,6 +265,15 @@ config_read(const char *file)
                return -1;
 
        /*
+        * Ensure that we're not already running
+        */
+       if (lock_check(CONF.LOCK_PATH) == -1) {
+               syslog(LOG_NOTICE, "Coudn't lock '%s': Already running?",
+                   CONF.LOCK_PATH);
+               return -1;
+       }
+
+       /*
         * Post-parsing sanity checking
         */
        if (!mmmapstring(cmap, CONF.LOG_FACILITY, &facility)) {
@@ -295,10 +323,33 @@ config_read(const char *file)
        if ((notify_fd = notify_open(CONF.SOCKET_PATH, gids[0])) == -1)
                return -1;
 
+       /*
+        * Finally drop privileges if necessary
+        */
+       if (!mmdropprivs(uid, gids, ngids)) {
+               syslog(LOG_NOTICE, "Could not drop privileges (%s)",
+                   strerror(errno));
+               return -1;
+       }
+
        /* Success */
        return 0;
 }
 
+static int
+lock_check(const char *path)
+{
+       int     fd;
+
+       if ((fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600)) != -1) {
+               if (flock(fd, LOCK_EX | LOCK_NB) == 0)
+                       return 0;
+               (void) close(fd);
+       }
+
+       return -1;
+}
+
 /*
  * Binds AF_LOCAL SOCK_DGRAM socket at specified <path>, with write access
  * for the specified group. Returns socket filedescriptor on success, or -1 on
@@ -333,30 +384,3 @@ notify_open(const char *path, gid_t group)
 
        return -1;
 }
-
-/*
- * If running as the superuser (not compiled with NODROPPRIVS), drop
- * privileges to that specified in USER and GROUPS.
- */
-static int
-privileges_drop(void)
-{
-
-#if !defined(NODROPPRIVS)
-       if (getuid() != 0) {
-               syslog(LOG_NOTICE, "Only the superuser may launch %s",
-                   DAEMON_NAME);
-               return -1;
-       }
-#else
-       if (getuid() == 0) {
-               syslog(LOG_NOTICE, "%s compiled with NODROPPRIVS, refusing "
-                   "to run as the superuser");
-               return -1;
-       }
-#endif
-
-       /* XXX */
-
-       return 0;
-}
index 4d85fec..5a5feb3 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: mmsmtpd.c,v 1.68 2005/03/01 11:05:29 mmondor Exp $ */
+/* $Id: mmsmtpd.c,v 1.69 2005/03/05 15:33:34 mmondor Exp $ */
 
 /*
  * Copyright (C) 2001-2004, Matthew Mondor
@@ -81,7 +81,7 @@
 
 MMCOPYRIGHT("@(#) Copyright (c) 2001-2004\n\
 \tMatthew Mondor. All rights reserved.\n");
-MMRCSID("$Id: mmsmtpd.c,v 1.68 2005/03/01 11:05:29 mmondor Exp $");
+MMRCSID("$Id: mmsmtpd.c,v 1.69 2005/03/05 15:33:34 mmondor Exp $");
 
 
 
@@ -217,6 +217,7 @@ main(int argc, char **argv)
     cres_t cres;
     carg_t *cargp;
     carg_t cargs[] = {
+       {CAT_STR, CAF_NONE, 1, 255, "LOCK_PATH", CONF.LOCK_PATH},
        {CAT_STR, CAF_NONE, 1, 255, "CHROOT_DIR", CONF.CHROOT_DIR},
        {CAT_STR, CAF_NONE, 1, 255, "PID_PATH", CONF.PID_PATH},
        {CAT_STR, CAF_NONE, 1, 31, "USER", CONF.USER},
@@ -308,6 +309,7 @@ main(int argc, char **argv)
 
     /* Set defaults */
     *CONF.CHROOT_DIR = 0;
+    mm_strcpy(CONF.LOCK_PATH, "/var/run/mmsmtpd.lock");
     mm_strcpy(CONF.PID_PATH, "/var/run/mmsmtpd.pid");
     mm_strcpy(CONF.USER, "mmmail");
     mm_strcpy(CONF.GROUPS, "mmmail,mmstat");
@@ -379,6 +381,13 @@ main(int argc, char **argv)
        exit(-1);
     }
 
+    /* Ensure that only a single instance with same configuration runs */
+    if (lock_check(CONF.LOCK_PATH) == -1) {
+       printf("\nCouldn't lock file '%s' (already running?)\n\n",
+               CONF.LOCK_PATH);
+       exit(-1);
+    }
+
     /* Post parsing */
     if (!mmmapstring(cmap, CONF.LOG_FACILITY, &facility)) {
        printf("\nUnknown syslog facility %s\n\n", CONF.LOG_FACILITY);
@@ -1282,6 +1291,25 @@ check_nofrom(const char *addr, const char *host)
 }
 
 
+/*
+ * Used to ensure that only a single server instance with the same
+ * configuration runs at the same time.
+ */
+static int
+lock_check(const char *path)
+{
+    int        fd;
+
+    if ((fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600)) != -1) {
+       if (flock(fd, LOCK_EX | LOCK_NB) == 0)
+           return 0;
+       (void) close(fd);
+    }
+
+    return -1;
+}
+
+
 /* Returns -1 if <str> does not match <pat> '*' and '?' wildcards pattern.
  * Otherwise returns > -1, a value representing the number of literal
  * characters in <pat> which exactly matched <str>. This us useful to evaluate
index 4e9f9ef..5b9b9cd 100644 (file)
@@ -1,4 +1,4 @@
-.\" $Id: mmsmtpd.conf.5,v 1.13 2005/03/01 13:25:02 mmondor Exp $
+.\" $Id: mmsmtpd.conf.5,v 1.14 2005/03/05 15:33:34 mmondor Exp $
 .\"
 .\" Copyright (C) 2001-2004, Matthew Mondor
 .\" All rights reserved.
@@ -106,6 +106,13 @@ to re-establish it, although normally local UNIX socket connections are
 reliable. This is not a problem with remote server connections, where
 re-establishing may happen more often, and the jail won't matter.
 .Pp
+.It Nm LOCK_PATH Ar "file"
+Location of lock file which should be used to determine if the daemon is
+already running. Note that this path resides outside of the
+.Xr chroot 2
+if enabled using
+.Fa CHROOT_DIR .
+.Pp
 .It Nm PID_PATH Ar "file"
 Location where
 .Nm mmsmtpd
@@ -498,6 +505,7 @@ The following defaults are used:
 ASYNC_PROCESSES         3
 CHROOT_DIR              ""
 PID_PATH                "/var/run/mmsmtpd.pid"
+LOCK_PATH               "/var/run/mmsmtpd.lock"
 MAIL_DIR                "/var/mmmail-dir"
 USER                    mmmail
 GROUPS                  mmmail,mmstat
index df001e3..db2fdc0 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: mmsmtpd.h,v 1.29 2005/03/01 11:05:29 mmondor Exp $ */
+/* $Id: mmsmtpd.h,v 1.30 2005/03/05 15:33:34 mmondor Exp $ */
 
 /*
  * Copyright (C) 2001-2004, Matthew Mondor
@@ -122,7 +122,7 @@ enum data_reason {
 /* STRUCTURES */
 /* We store config file read results in this structure */
 typedef struct config {
-    char CHROOT_DIR[256], PID_PATH[256], USER[32], GROUPS[256],
+    char LOCK_PATH[256], CHROOT_DIR[256], PID_PATH[256], USER[32], GROUPS[256],
        LOG_FACILITY[32], SERVER_NAMES[1024], LISTEN_IPS[1024], DB_HOST[64],
        DB_USER[32], DB_PASSWORD[32], DB_DATABASE[32], MAIL_DIR[256],
        MMRELAYD_SOCKET_PATH[256];
@@ -278,6 +278,7 @@ static clientenv *free_clientenv(clientenv *);
 static void empty_rcpts(list_t *);
 static bool check_alias(char *);
 static bool check_nofrom(const char *, const char *);
+static int lock_check(const char *);
 static int best_match(const char *, const char *);
 static bool local_address(struct box_info *, const char *);
 static bool box_filter_allow(const char *, const char *, char);