diff options
-rw-r--r-- | ChangeLog | 10 | ||||
-rw-r--r-- | NEWS | 14 | ||||
-rw-r--r-- | doc/mailfromd.texi | 21 | ||||
-rw-r--r-- | gacopyz/gacopyz.c | 10 | ||||
-rw-r--r-- | src/db.c | 1 | ||||
-rw-r--r-- | src/mailfromd.h | 9 | ||||
-rw-r--r-- | src/main.c | 68 | ||||
-rw-r--r-- | src/mu_dbm.c | 195 | ||||
-rw-r--r-- | src/mu_dbm.h | 6 | ||||
-rw-r--r-- | tests/etc/config.in | 3 |
10 files changed, 249 insertions, 88 deletions
@@ -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. @@ -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; @@ -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 */ @@ -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" |