From d873a1428fcfa0768d6bfb7dd6c2f283f3b0f63c Mon Sep 17 00:00:00 2001 From: Peter Pentchev Date: Tue, 15 Jun 2010 10:29:41 +0000 Subject: [PATCH] Initial import of a simple utility to convert dma spool files between formats. --- migrate/.depend | 29 +++ migrate/Makefile | 7 + migrate/NEWS | 6 + migrate/dma-migrate | Bin 0 -> 13165 bytes migrate/dma-migrate.8 | 98 ++++++++++ migrate/dma-migrate.c | 411 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 551 insertions(+) create mode 100644 migrate/.depend create mode 100644 migrate/Makefile create mode 100644 migrate/NEWS create mode 100755 migrate/dma-migrate create mode 100644 migrate/dma-migrate.8 create mode 100644 migrate/dma-migrate.c diff --git a/migrate/.depend b/migrate/.depend new file mode 100644 index 0000000..d78f1b9 --- /dev/null +++ b/migrate/.depend @@ -0,0 +1,29 @@ +dma-migrate.o dma-migrate.ln: dma-migrate.c /usr/include/sys/types.h \ + /usr/include/features.h /usr/include/bits/predefs.h \ + /usr/include/sys/cdefs.h /usr/include/bits/wordsize.h \ + /usr/include/gnu/stubs.h /usr/include/gnu/stubs-32.h \ + /usr/include/bits/types.h /usr/include/bits/typesizes.h \ + /usr/include/time.h /usr/lib/gcc/i486-linux-gnu/4.4.4/include/stddef.h \ + /usr/include/endian.h /usr/include/bits/endian.h \ + /usr/include/bits/byteswap.h /usr/include/sys/select.h \ + /usr/include/bits/select.h /usr/include/bits/sigset.h \ + /usr/include/bits/time.h /usr/include/sys/sysmacros.h \ + /usr/include/bits/pthreadtypes.h /usr/include/sys/file.h \ + /usr/include/fcntl.h /usr/include/bits/fcntl.h /usr/include/bits/uio.h \ + /usr/include/sys/stat.h /usr/include/bits/stat.h /usr/include/dirent.h \ + /usr/include/bits/dirent.h /usr/include/bits/posix1_lim.h \ + /usr/include/bits/local_lim.h /usr/include/linux/limits.h \ + /usr/include/err.h /usr/lib/gcc/i486-linux-gnu/4.4.4/include/stdarg.h \ + /usr/include/errno.h /usr/include/bits/errno.h \ + /usr/include/linux/errno.h /usr/include/asm/errno.h \ + /usr/include/asm-generic/errno.h /usr/include/asm-generic/errno-base.h \ + /usr/include/inttypes.h /usr/include/stdint.h /usr/include/bits/wchar.h \ + /usr/include/regex.h /usr/include/gnu/option-groups.h \ + /usr/include/stdio.h /usr/include/libio.h /usr/include/_G_config.h \ + /usr/include/wchar.h /usr/include/bits/stdio_lim.h \ + /usr/include/bits/sys_errlist.h /usr/include/stdlib.h \ + /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h \ + /usr/include/xlocale.h /usr/include/alloca.h /usr/include/string.h \ + /usr/include/unistd.h /usr/include/bits/posix_opt.h \ + /usr/include/bits/environments.h /usr/include/bits/confname.h \ + /usr/include/getopt.h diff --git a/migrate/Makefile b/migrate/Makefile new file mode 100644 index 0000000..74163fb --- /dev/null +++ b/migrate/Makefile @@ -0,0 +1,7 @@ +PROG= dma-migrate +SRCS= dma-migrate.c +MAN= dma-migrate.8 + +WARNS?= 6 + +.include diff --git a/migrate/NEWS b/migrate/NEWS new file mode 100644 index 0000000..29dca0d --- /dev/null +++ b/migrate/NEWS @@ -0,0 +1,6 @@ +Change log for dma-migrate, the DragonFly Mail Agent queue migration utility. + +0.01 not yet ;) + - first public release + +Comments: Peter Pentchev diff --git a/migrate/dma-migrate b/migrate/dma-migrate new file mode 100755 index 0000000000000000000000000000000000000000..af3f9b96e406d72eb5ae179938368027ebb613ec GIT binary patch literal 13165 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 literal 0 Hc-jL100001 diff --git a/migrate/dma-migrate.8 b/migrate/dma-migrate.8 new file mode 100644 index 0000000..e40acef --- /dev/null +++ b/migrate/dma-migrate.8 @@ -0,0 +1,98 @@ +.\" Copyright (c) 2010 Peter Pentchev +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd May 11, 2009 +.Dt dma-migrate 8 +.Os +.Sh NAME +.Nm dma-migrate +.Nd convert the DragonFly Mail Agent's queue files +.Sh SYNOPSIS +.Nm +.Op Fl v +.Op Fl d Ar spooldir +.Nm +.Op Fl h | Fl V +.Sh DESCRIPTION +The +.Nm +utility is used to convert the mail queue files in the +.Xr dma 8 +spool directory to the latest spool directory format supported by +the installed version of +.Xr dma 8 . +Currently it only handles the conversion from a single file containing +both message and delivery metadata to the M/Q format. +.Pp +The following command-line options are available: +.Bl -tag -width indent +.It Fl d +Specify the location of the +.Xr dma 8 +spool directory, default +.Pa /var/spool/dma . +.It Fl h +Display usage information and exit. +.It Fl V +Display program version information and exit. +.It Fl v +Verbose output - display diagnostic messages. +.El +.Sh ENVIRONMENT +The operation of the +.Nm +utility is currently not influenced by environment variables. +.Sh FILES +The +.Nm +utility looks for the +.Xr dma 8 +mail queue files in the +.Pa /var/spool/dma +directory, unless another location is specified by the +.Fl d +command-line option. +.Sh EXIT STATUS +The +.Nm +utility will scan the whole spool directory and attempt to convert all +suitable files there. +If there are no files to be converted, or if all the conversions are +successful, it will complete with an exit code of zero. +If any conversion errors are encountered, a message will be displayed +to the standard error stream and +.Nm +will exit with an exit code of 1 after attempting to convert the rest of +the suitable files in the spool directory. +.Sh SEE ALSO +.Xr dma 8 +.Sh STANDARDS +No standards documentation was harmed in the process of creating +.Nm . +.Sh BUGS +Please report any bugs in +.Nm +to the author. +.Sh AUTHOR +.An Peter Pentchev Aq roam@ringlet.net diff --git a/migrate/dma-migrate.c b/migrate/dma-migrate.c new file mode 100644 index 0000000..3f21b8b --- /dev/null +++ b/migrate/dma-migrate.c @@ -0,0 +1,411 @@ +/*- + * Copyright (c) 2010 Peter Pentchev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#define _GNU_SOURCE + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef __printflike +#ifdef __GNUC__ +#define __printflike(fmtarg, firstvararg) \ + __attribute__((__format__ (__printf__, fmtarg, firstvararg))) +#else +#define __printflike(fmtarg, firstvararg) +#endif +#endif + +#define DEFAULT_SPOOLDIR "/var/spool/dma" + +static int verbose = 0; +static char copybuf[BUFSIZ]; + +static int dma_migrate(int, const char *); + +static int open_locked(const char *, int, ...); +static void cleanup_file(int, char *); + +static void usage(int); +static void version(void); +static void debug(const char *, ...) __printflike(1, 2); + +int +main(int argc, char **argv) +{ + const char *spooldir; + int hflag, Vflag, errs, fd, res; + char ch; + DIR *d; + struct dirent *e; + struct stat sb; + + srandom((unsigned long)((time(NULL) ^ getpid()) + ((uintptr_t)argv))); + + hflag = Vflag = 0; + spooldir = DEFAULT_SPOOLDIR; + while (ch = getopt(argc, argv, "d:hVv"), ch != -1) + switch (ch) { + case 'd': + spooldir = optarg; + break; + + case 'h': + hflag = 1; + break; + + case 'V': + Vflag = 1; + break; + + case 'v': + verbose = 1; + break; + + case '?': + usage(1); + } + if (hflag) + usage(0); + if (Vflag) + version(); + if (hflag || Vflag) + exit(0); + + argc -= optind; + argv += optind; + + /* Let's roll! */ + if (chdir(spooldir) == -1) + err(1, "Could not change into spool directory %s", spooldir); + if (d = opendir("."), d == NULL) + err(1, "Could not read spool directory %s", spooldir); + errs = 0; + while (e = readdir(d), e != NULL) { + /* Do we care about this entry? */ + debug("Read a directory entry: %s\n", e->d_name); + if (strncmp(e->d_name, "tmp_", 4) == 0 || + e->d_name[0] == 'M' || e->d_name[0] == 'Q' || + (e->d_type != DT_REG && e->d_type != DT_UNKNOWN)) + continue; + if (e->d_type == DT_UNKNOWN) + if (stat(e->d_name, &sb) == -1 || !S_ISREG(sb.st_mode)) + continue; + debug("- want to process it\n"); + + /* Try to lock it - skip it if dma is delivering the message */ + if (fd = open_locked(e->d_name, O_RDONLY|O_NDELAY), fd == -1) { + debug("- seems to be locked, skipping\n"); + continue; + } + + /* Okay, convert it to the M/Q schema */ + res = dma_migrate(fd, e->d_name); + close(fd); + if (res == -1) + errs++; + } + if (errs) + debug("Finished, %d conversion errors\n", errs); + else + debug("Everything seems to be all right\n"); + return (errs && 1); +} + +static int +dma_migrate(int fd, const char *fname) +{ + const char *id; + char *mname, *qname, *tempname, *sender, *recp, *line, *recpline; + int mfd, qfd, tempfd; + struct stat sb; + FILE *fp, *qfp, *mfp; + size_t sz, len; + static regex_t *qidreg = NULL; + + mfd = tempfd = qfd = -1; + mname = qname = sender = recp = line = NULL; + fp = qfp = NULL; + + if (fstat(fd, &sb) == -1) { + warn("Could not fstat(%s)", fname); + return (-1); + } + /* + * Let's just blithely assume that the queue ID *is* the filename, + * since that's the way dma did things so far. + * Well, okay, let's check it. + */ + if (qidreg == NULL) { + regex_t *nreg; + + if ((nreg = malloc(sizeof(*qidreg))) == NULL) { + warn("Could not allocate memory for a regex"); + return (-1); + } + if (regcomp(nreg, "^[a-fA-F0-9]\\+\\.[a-fA-F0-9]\\+$", 0) + != 0) { + warnx("Could not compile a dma queue ID regex"); + free(nreg); + return (-1); + } + qidreg = nreg; + } + if (regexec(qidreg, fname, 0, NULL, 0) != 0) { + warnx("The name '%s' is not a valid dma queue ID", fname); + return (-1); + } + id = fname; + debug(" - queue ID %s\n", id); + if (asprintf(&mname, "M%s", id) == -1 || + asprintf(&tempname, "tmp_%s", id) == -1 || + asprintf(&qname, "Q%s", id) == -1 || + mname == NULL || tempname == NULL || qname == NULL) + goto fail; + + /* Create the message placeholder early to avoid races */ + mfd = open_locked(mname, O_CREAT | O_EXCL | O_RDWR, 0600); + if (mfd == -1) { + warn("Could not create temporary file %s", mname); + goto fail; + } + if (stat(qname, &sb) != -1 || errno != ENOENT || + stat(tempname, &sb) != -1 || errno != ENOENT) { + warnx("Some of the queue files for %s already exist", fname); + goto fail; + } + debug(" - mfd %d names %s, %s, %s\n", mfd, mname, tempname, qname); + + fp = fdopen(fd, "r"); + if (fp == NULL) { + warn("Could not reopen the descriptor for %s", fname); + goto fail; + } + + /* Parse the header of the old-format message file */ + /* ...sender... */ + if (getline(&sender, &sz, fp) == -1) { + warn("Could not read the initial line from %s", fname); + goto fail; + } + sz = strlen(sender); + while (sz > 0 && (sender[sz - 1] == '\n' || sender[sz - 1] == '\r')) + sender[--sz] = '\0'; + if (sz == 0) { + warnx("Empty sender line in %s", fname); + goto fail; + } + debug(" - sender %s\n", sender); + /* ...recipient(s)... */ + len = strlen(fname); + recpline = NULL; + while (1) { + if (getline(&line, &sz, fp) == -1) { + warn("Could not read a recipient line from %s", fname); + goto fail; + } + sz = strlen(line); + while (sz > 0 && + (line[sz - 1] == '\n' || line[sz - 1] == '\r')) + line[--sz] = '\0'; + if (sz == 0) { + free(line); + line = NULL; + break; + } + if (recp == NULL && + strncmp(line, fname, len) == 0 && line[len] == ' ') { + recp = line + len + 1; + recpline = line; + } else { + free(line); + } + line = NULL; + } + if (recp == NULL) { + warnx("Could not find its own recipient line in %s", fname); + goto fail; + } + /* ..phew, finished with the header. */ + + tempfd = open_locked(tempname, O_CREAT | O_EXCL | O_RDWR, 0600); + if (tempfd == -1) { + warn("Could not create a queue file for %s", fname); + goto fail; + } + qfp = fdopen(tempfd, "w"); + if (qfp == NULL) { + warn("Could not fdopen(%s) for %s", tempname, fname); + goto fail; + } + mfp = fdopen(mfd, "w"); + if (mfp == NULL) { + warn("Could not fdopen(%s) for %s", mname, fname); + goto fail; + } + fprintf(qfp, "ID: %s\nSender: %s\nRecipient: %s\n", id, sender, recp); + fflush(qfp); + fsync(tempfd); + + /* Copy the message file over to mname */ + while ((sz = fread(copybuf, 1, sizeof(copybuf), fp)) > 0) + if (fwrite(copybuf, 1, sz, mfp) != sz) { + warn("Could not copy the message from %s to %s", + fname, mname); + goto fail; + } + if (ferror(fp)) { + warn("Could not read the full message from %s", fname); + goto fail; + } + fflush(mfp); + fsync(mfd); + + if (rename(tempname, qname) == -1) { + warn("Could not rename the queue file for %s", fname); + goto fail; + } + qfd = tempfd; + tempfd = -1; + if (unlink(fname) == -1) { + warn("Could not remove the old converted file %s", fname); + goto fail; + } + + fclose(fp); + fclose(qfp); + free(sender); + free(line); + free(recpline); + free(mname); + free(qname); + free(tempname); + return (0); + +fail: + if (fp != NULL) + fclose(fp); + if (qfp != NULL) + fclose(qfp); + if (sender != NULL) + free(sender); + if (line != NULL) + free(line); + if (recpline != NULL) + free(recpline); + cleanup_file(mfd, mname); + cleanup_file(qfd, qname); + cleanup_file(tempfd, tempname); + return (-1); +} + +static void +cleanup_file(int fd, char *fname) +{ + if (fd != -1) { + close(fd); + unlink(fname); + } + if (fname != NULL) + free(fname); +} + +static void +usage(int ferr) +{ + const char *s = + "Usage:\tdma-migrate [-hVv] [-d spooldir]\n" + "\t-d\tspecify the spool directory (" DEFAULT_SPOOLDIR ")\n" + "\t-h\tdisplay program usage information and exit\n" + "\t-V\tdisplay program version information and exit\n" + "\t-v\tverbose operation - display diagnostic messages"; + + if (ferr) + errx(1, "%s", s); + puts(s); +} + +static void +version(void) +{ + printf("dma-migrate 0.01\n"); +} + +static void +debug(const char *fmt, ...) +{ + va_list v; + + if (verbose < 1) + return; + va_start(v, fmt); + vfprintf(stderr, fmt, v); + va_end(v); +} + +static int +open_locked(const char *fname, int flags, ...) +{ + int mode = 0; +#ifndef O_EXLOCK + int fd, save_errno; +#endif + + if (flags & O_CREAT) { + va_list ap; + va_start(ap, flags); + mode = va_arg(ap, int); + va_end(ap); + } + +#ifndef O_EXLOCK + fd = open(fname, flags, mode); + if (fd < 0) + return(fd); + if (flock(fd, LOCK_EX|((flags & O_NONBLOCK)? LOCK_NB: 0)) < 0) { + save_errno = errno; + close(fd); + errno = save_errno; + return(-1); + } + return(fd); +#else + return(open(fname, flags|O_EXLOCK, mode)); +#endif +} -- 2.47.3