aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog10
-rw-r--r--NEWS14
-rw-r--r--doc/mailfromd.texi21
-rw-r--r--gacopyz/gacopyz.c10
-rw-r--r--src/db.c1
-rw-r--r--src/mailfromd.h9
-rw-r--r--src/main.c68
-rw-r--r--src/mu_dbm.c195
-rw-r--r--src/mu_dbm.h6
-rw-r--r--tests/etc/config.in3
10 files changed, 249 insertions, 88 deletions
diff --git a/ChangeLog b/ChangeLog
index 4a19530a..de0b4f2f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,15 @@
2007-04-16 Sergey Poznyakoff <gray@gnu.org.ua>
+ * src/mu_dbm.c, src/mu_dbm.h: Use external lock file for Berkeley
+ DB (argghh)
+ * src/mailfromd.h, src/db.c, src/main.c: Implement pragma option
+ state-directory
+ * tests/etc/config.in: Use #pragma option state-directory
+ * doc/mailfromd.texi: Document #pragma option state-directory
+
+ * gacopyz/gacopyz.c (shan_connect): Fix packet length calculation.
+ * NEWS: Update
+
* src/prog.c (instr_restex): Fix stack addressing (broken after
2007-04-01). Add trace output.
diff --git a/NEWS b/NEWS
index 5c1d168d..3d9c3420 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-Mailfromd NEWS -- history of user-visible changes. 2007-04-04
+Mailfromd NEWS -- history of user-visible changes. 2007-04-16
Copyright (C) 2005, 2006, 2007 Sergey Poznyakoff
See the end of file for copying conditions.
@@ -281,6 +281,18 @@ New built-in macro `__statedir__' expands to the name of the default
program state directory.
* Bugfixes
+** Fix incorrect packet length calculation in connect Milter handler.
+The bug manifested itself with the following log messages:
+
+- In mailfromd log:
+
+ MailfromFilter: shan_connect: wrong length
+
+- In Sendmail log:
+
+ milter_read(mailfrom): cmd read returned 0, expecting 5
+ Milter(mailfrom): to error state
+
** Fix coredumps on printing void returns with --dump-tree
** Fix coredumps on optimising conditionals like
diff --git a/doc/mailfromd.texi b/doc/mailfromd.texi
index a4c264f5..e93bfead 100644
--- a/doc/mailfromd.texi
+++ b/doc/mailfromd.texi
@@ -583,7 +583,9 @@ directory}, i.e. a directory where @command{mailfromd} will keep its
data files (communication socket, pidfile and cache database). By
default, its full name is @file{@var{localstatedir}/mailfromd}. You
can change it by setting @code{DEFAULT_STATE_DIR} configuration
-variable.
+variable. This value can be changed at run-time using @code{#pragma
+option state-directory} statement
+(@pxref{pragma state-directory}).
@item Select default communication socket.
@cindex default communication socket
@@ -2695,10 +2697,11 @@ script file.
@end smallexample
@noindent
-where @var{name} is the name of the command line option (without
-leading dashes) to set, and @var{value} is its new value. The effect
-of this statement is the same as that of the command line option
-@samp{--@var{name}=@var{value}}.
+where @var{name} is the name of the option to set, and @var{value} is
+its new value. In many cases, @var{name} corresponds to a command
+line option with the same name, but without leading dashes. In these
+cases, the effect of this statement is the same as that of the command
+line option @samp{--@var{name}=@var{value}}.
The @var{value} is specific for each particular option. The
supported value types are:
@@ -2971,6 +2974,14 @@ example:
@end smallexample
@end deffn
+@deffn {pragma option} state-directory @var{string}
+@xprindex{state-directory}
+ Changes the file name of the program state directory.
+@smallexample
+#pragma option state-directory "/var/run/mailfromd"
+@end smallexample
+@end deffn
+
@deffn {pragma option} pidfile @var{string}
@xprindex{pidfile}
Introduces the full name of a file where @command{mailfromd} will
diff --git a/gacopyz/gacopyz.c b/gacopyz/gacopyz.c
index 1c71a142..c9445134 100644
--- a/gacopyz/gacopyz.c
+++ b/gacopyz/gacopyz.c
@@ -858,17 +858,11 @@ shan_connect(SMFICTX *ctx, union state_arg *arg, unsigned char *cmd)
s = arg->strings[1];
family = *s++;
- len = strlen(s);
if (family != SMFIA_UNKNOWN) {
- if (len < sizeof port) {
- gacopyz_log(ctx->conn, SMI_LOG_ERR,
- "%s: shan_connect: wrong length",
- ctx->conn->desc.xxfi_name);
- return sret_abort;
- }
port = *(unsigned short*) s;
s += sizeof port;
- len -= sizeof port;
+
+ len = strlen(s);
if (len > 0 && s[len])
return sret_abort;
diff --git a/src/db.c b/src/db.c
index 1978dec8..9b1487e9 100644
--- a/src/db.c
+++ b/src/db.c
@@ -56,6 +56,7 @@ db_format_install(struct db_format *fmt)
p = xmalloc(sizeof *p);
p->fmt = *fmt;
+ p->fmt.dbname = strdup(p->fmt.dbname);
p->next = db_fmt_list;
db_fmt_list = p;
return &p->fmt;
diff --git a/src/mailfromd.h b/src/mailfromd.h
index 62023f7a..c09f834b 100644
--- a/src/mailfromd.h
+++ b/src/mailfromd.h
@@ -138,11 +138,11 @@ mf_status getmxip(char *ipstr, mxbuf_t mxbuf);
/* Default file names */
#define DEFAULT_PIDFILE DEFAULT_STATE_DIR "/mailfromd.pid"
-#define DEFAULT_DATABASE DEFAULT_STATE_DIR "/mailfromd.db"
-#define DEFAULT_RATE_DATABASE DEFAULT_STATE_DIR "/rates.db"
-#define DEFAULT_GREYLIST_DATABASE DEFAULT_STATE_DIR "/greylist.db"
+#define DEFAULT_DATABASE "mailfromd.db"
+#define DEFAULT_RATE_DATABASE "rates.db"
+#define DEFAULT_GREYLIST_DATABASE "greylist.db"
+#define DEFAULT_DNS_DATABASE "dns.db"
#define DEFAULT_SCRIPT_FILE SYSCONFDIR "/mailfromd.rc"
-#define DEFAULT_DNS_DATABASE DEFAULT_STATE_DIR "/dns.db"
#define DEFAULT_FROM_ADDRESS "<>"
@@ -274,6 +274,7 @@ extern int script_dump_macros;
extern int script_dump_xref;
extern size_t lock_retry_count_option;
extern time_t lock_retry_timeout_option;
+extern char *mailfromd_state_dir;
/* Configuration file parser */
diff --git a/src/main.c b/src/main.c
index c1492408..d9be5103 100644
--- a/src/main.c
+++ b/src/main.c
@@ -67,6 +67,7 @@ int single_process_option; /* Run in single process mode. */
unsigned long source_address = INADDR_ANY; /* Source address for TCP
connections */
char *syslog_tag; /* Tag to mark syslog entries with. */
+char *mailfromd_state_dir;
char *pidfile = DEFAULT_PIDFILE;
char *user = DEFAULT_USER; /* Switch to this user privileges after
startup */
@@ -742,8 +743,7 @@ option_pidfile(char *opt, void **pval, char *newval)
mu_error("Invalid pidfile name: must be absolute");
return 1;
}
- *pval = newval;
- return 0;
+ return option_string(opt, pval, newval);
}
static int
@@ -805,6 +805,35 @@ option_group(char *opt, void **pval, char *newval)
return 0;
}
+void
+set_state_directory(void *value)
+{
+ /* nothing */
+}
+
+int
+option_state_directory(char *opt, void **pval, char *newval)
+{
+ struct stat st;
+ if (stat(newval, &st)) {
+ mu_error("cannot stat file `%s': %s",
+ newval,
+ mu_strerror(errno));
+ return 1;
+ }
+ if (!S_ISDIR(st.st_mode)) {
+ mu_error("`%s' is not a directory", newval);
+ return 1;
+ }
+ if (newval[0] != '/') {
+ mu_error("state directory `%s' is not an absolute file name",
+ newval);
+ return 1;
+ }
+ mailfromd_state_dir = xstrdup(newval);
+ return 0;
+}
+
struct option_cache {
char *name;
void *value;
@@ -837,6 +866,7 @@ struct option_cache {
{ "source", NULL, option_source, set_source },
{ "lock-retry-count", NULL, option_number, set_lock_retry_count },
{ "lock-retry-timeout", NULL, option_time, set_lock_retry_timeout },
+ { "state-directory", NULL, option_state_directory, set_state_directory },
{ NULL }
};
@@ -1551,12 +1581,43 @@ get_db_name()
void
db_format_setup()
{
+ mailfromd_state_dir = xstrdup(DEFAULT_STATE_DIR);
dns_cache_format = db_format_install(dns_cache_format);
cache_format = db_format_install(cache_format);
rate_format = db_format_install(rate_format);
greylist_format = db_format_install(greylist_format);
}
+static int
+db_fixup_name_enumerator(void *sym, void *data)
+{
+ struct db_format *fmt = sym;
+ static size_t slen;
+
+ if (slen == 0)
+ slen = strlen(mailfromd_state_dir);
+
+ if (fmt->dbname[0] != '/') {
+ size_t olen = strlen(fmt->dbname);
+ size_t flen = slen + 1 + olen + 1;
+ char *p = xrealloc(fmt->dbname, flen);
+ memmove(p + slen + 1, p, olen + 1);
+ memcpy(p, mailfromd_state_dir, slen);
+ p[slen] = '/';
+ fmt->dbname = p;
+ }
+ return 0;
+}
+
+static void
+db_fixup_names()
+{
+ size_t slen = strlen(mailfromd_state_dir);
+ if (mailfromd_state_dir[slen-1] == '/')
+ mailfromd_state_dir[slen-1] = 0;
+ db_format_enumerate(db_fixup_name_enumerator, NULL);
+}
+
void
mailfromd_delete(int argc, char **argv)
{
@@ -1724,7 +1785,8 @@ main(int argc, char **argv)
exit(EX_CONFIG);
}
process_options();
-
+ db_fixup_names();
+
/* Process some special options */
if (expire_interval)
cache_format->expire_interval = negative_expire_interval =
diff --git a/src/mu_dbm.c b/src/mu_dbm.c
index 461731e7..63541483 100644
--- a/src/mu_dbm.c
+++ b/src/mu_dbm.c
@@ -216,55 +216,113 @@ mu_dbm_strerror ()
int mu_dbm_errno;
static int
-lock_file (int fd, int type)
+try_lock (int fd, int type)
{
int i;
struct flock fl;
- int rc = 0;
-
- memset(&fl, 0, sizeof fl);
+
+ memset (&fl, 0, sizeof fl);
fl.l_whence = SEEK_SET;
fl.l_type = type;
for (i = 0; i < lock_retry_count_option; i++)
{
- if (fcntl(fd, F_SETLK, &fl) == 0)
- {
- rc = 0;
- break;
- }
- rc = errno;
+ if (fcntl (fd, F_SETLK, &fl) == 0)
+ return 0;
if (!(errno == EAGAIN || errno == EACCES))
break;
sleep (lock_retry_timeout_option);
}
+ return errno;
+}
+
+static int
+lock_file (const char *name, int fd, int type)
+{
+ int rc = 0;
+ pid_t holding_pid = 0;
+
+ rc = try_lock (fd, type);
+ if (rc)
+ {
+ struct flock fl;
+
+ fl.l_pid = 0;
+ if (fcntl (fd, F_GETLK, &fl) == 0)
+ {
+ if (fl.l_pid)
+ {
+ if (kill (fl.l_pid, 0) == 0)
+ mu_error ("%s is locked by process ID %lu",
+ name, (unsigned long) fl.l_pid);
+ else if (errno == ESRCH)
+ {
+ mu_error ("%s is locked by nonexisting process ID %lu; "
+ "retrying",
+ name, (unsigned long) fl.l_pid);
+ if (unlink (name))
+ mu_error ("cannot unlink file %s: %s",
+ name, mu_strerror (errno));
+ else
+ {
+ rc = try_lock (fd, type);
+ if (rc)
+ mu_error ("%s: cannot aquire lock: %s",
+ name, mu_strerror (rc));
+ }
+ }
+ else
+ mu_error ("%s seems locked by process ID %lu, "
+ "but its validity cannot be verified: %s",
+ name, (unsigned long) holding_pid,
+ mu_strerror (errno));
+ }
+ }
+ else
+ mu_error ("cannot obtain locker info for %s: %s",
+ name, mu_strerror (errno));
+ }
return rc;
}
#define LOCK_SUFFIX ".lock"
static int
-make_lockfile (char *name, int *pfd, char **plockname)
+make_lockfile (char *name, int lktype, int *pfd, char **plockname)
{
int rc;
int fd;
- char *lockname;
- size_t size;
-
- size = strlen (name) + sizeof LOCK_SUFFIX;
- lockname = malloc (size);
+ char *p, *lockname;
+ size_t size, namesize;
+
+ size = strlen (mailfromd_state_dir);
+ if (strlen (name) > size
+ && memcmp (name, mailfromd_state_dir, size) == 0
+ && name[size] == '/')
+ p = name + size + 1;
+ else
+ p = name;
+
+ namesize = size + 1 + strlen (p) + sizeof LOCK_SUFFIX;
+ lockname = malloc (namesize);
if (!lockname)
return errno;
- strcpy (lockname, name);
+ strcpy (lockname, mailfromd_state_dir);
+ strcat (lockname, "/");
+ strcat (lockname, p);
strcat (lockname, LOCK_SUFFIX);
+ for (p = lockname + size + 1; *p; p++)
+ if (*p == '/')
+ *p = '-';
+
fd = open (lockname, O_CREAT|O_RDWR, 0600);
- if (!fd)
+ if (fd == -1)
{
free (lockname);
return errno;
}
- rc = lock_file (fd, F_WRLCK);
+ rc = lock_file (lockname, fd, lktype);
if (rc)
{
free (lockname);
@@ -291,11 +349,13 @@ mu_dbm_open (char *name, DBM_FILE *dbm, int flags, int mode, int *ro)
{
int f;
DB *db = NULL;
- int lockfd = -1;
- char *lockname;
int fd;
-
+ char *lockname = NULL;
+ int oflags;
+ int lktype;
+
mu_dbm_errno = mu_check_perm (name, mode);
+
if (mu_dbm_errno && mu_dbm_errno != ENOENT)
return MU_ERR_UNSAFE_PERMS;
@@ -306,32 +366,36 @@ mu_dbm_open (char *name, DBM_FILE *dbm, int flags, int mode, int *ro)
{
case MU_STREAM_CREAT:
f = DB_CREATE | DB_TRUNCATE;
+ lktype = F_WRLCK;
break;
case MU_STREAM_READ:
if (mu_dbm_errno == ENOENT)
return MU_ERR_FAILURE;
f = DB_RDONLY;
+ lktype = F_RDLCK;
break;
case MU_STREAM_RDWR:
f = DB_CREATE;
+ lktype = F_WRLCK;
break;
default:
- errno = EINVAL;
- return -1;
+ mu_dbm_errno = EINVAL;
+ return MU_ERR_FAILURE;
}
- if (mu_dbm_errno == ENOENT)
+#ifdef DB_FCNTL_LOCKING
+ f |= DB_FCNTL_LOCKING;
+#endif
+
+ mu_dbm_errno = make_lockfile (name, lktype, &dbm->lockfd, &dbm->lockname);
+ if (mu_dbm_errno)
{
- mu_dbm_errno = make_lockfile (name, &lockfd, &lockname);
- if (mu_dbm_errno)
- {
- mu_error ("Cannot lock file %s: %s",
- name, mu_strerror (mu_dbm_errno));
- return MU_ERR_FAILURE;
- }
+ mu_error ("Cannot lock file %s: %s",
+ name, mu_strerror (mu_dbm_errno));
+ return MU_ERR_FAILURE;
}
#if DB_VERSION_MAJOR == 2
@@ -347,35 +411,14 @@ mu_dbm_open (char *name, DBM_FILE *dbm, int flags, int mode, int *ro)
# endif
#endif
if (mu_dbm_errno)
- return MU_ERR_FAILURE;
-
- if (mu_dbm_errno = db->fd (db, &fd))
{
- mu_error("Cannot get file descriptor for the database: %s",
- mu_dbm_strerror ());
- db->close (db, 0);
- if (lockfd != -1)
- {
- unlink (lockname);
- free (lockname);
- close (lockfd);
- }
+ unlink (dbm->lockname);
+ free (dbm->lockname);
+ close (dbm->lockfd);
return MU_ERR_FAILURE;
}
- mu_dbm_errno = lock_file (fd, (flags == MU_STREAM_READ) ? F_RDLCK : F_WRLCK);
- if (lockfd != -1)
- {
- unlink (lockname);
- free (lockname);
- close (lockfd);
- }
-
- if (mu_dbm_errno)
- {
- db->close (db, 0);
- return MU_ERR_FAILURE;
- }
+ db->sync (db, 0);
dbm->name = strdup (name);
dbm->db = db;
@@ -394,9 +437,13 @@ mu_dbm_close (DBM_FILE *db)
if (mu_dbm_errno)
return MU_ERR_FAILURE;
}
+
+ db->db->sync (db->db, 0);
mu_dbm_errno = db->db->close (db->db, 0);
- if (mu_dbm_errno == DB_INCOMPLETE)
- mu_dbm_errno = 0;
+ unlink (db->lockname);
+ free (db->lockname);
+ close (db->lockfd);
+
free (db->name);
return (mu_dbm_errno == 0) ? 0 : MU_ERR_FAILURE;
}
@@ -515,8 +562,34 @@ mu_dbm_strerror ()
{
if (errno == MU_ERR_UNSAFE_PERMS)
return mu_strerror (errno);
-#if DB_VERSION_MAJOR > 2
- return db_strerror (mu_dbm_errno);
+#if DB_VERSION_MAJOR == 2
+ switch (mu_dbm_errno)
+ {
+ case DB_INCOMPLETE:
+ return "Sync didn't finish";
+
+ case DB_KEYEMPTY:
+ return "The key/data pair was deleted or was never created by the user";
+ break;
+
+ case DB_KEYEXIST:
+ return "The key/data pair already exists";
+
+ case DB_LOCK_DEADLOCK:
+ return "Locker killed to resolve deadlock";
+
+ case DB_LOCK_NOTGRANTED:
+ return "Lock unavailable, no-wait set";
+
+ case DB_LOCK_NOTHELD:
+ return "Lock not held by locker";
+
+ case DB_NOTFOUND:
+ return "Key/data pair not found (EOF)";
+
+ default:
+ return "Unknown error";
+ }
#else
return mu_strerror (mu_dbm_errno);
#endif
diff --git a/src/mu_dbm.h b/src/mu_dbm.h
index 3b8ea99b..75b9d71c 100644
--- a/src/mu_dbm.h
+++ b/src/mu_dbm.h
@@ -21,10 +21,6 @@
typedef struct mu_db_file DBM_FILE;
-#define MU_DBM_INIT 0
-#define MU_DBM_JOIN 1
-#define MU_DBM_TRY 2
-
#if defined(WITH_GDBM)
#include <gdbm.h>
@@ -45,6 +41,8 @@ typedef datum DBM_DATUM;
struct mu_db_file
{
char *name;
+ int lockfd;
+ char *lockname;
DB *db;
DBC *dbc;
};
diff --git a/tests/etc/config.in b/tests/etc/config.in
index f3686d19..00a9c3ea 100644
--- a/tests/etc/config.in
+++ b/tests/etc/config.in
@@ -1,2 +1 @@
-#pragma database greylist file "STATEDIR/gl.db"
-#pragma database dns file "STATEDIR/dns.db"
+#pragma option state-directory "STATEDIR"

Return to:

Send suggestions and report system problems to the System administrator.