aboutsummaryrefslogtreecommitdiff
path: root/debian/migrate/dma-migrate.c
diff options
context:
space:
mode:
Diffstat (limited to 'debian/migrate/dma-migrate.c')
-rw-r--r--debian/migrate/dma-migrate.c413
1 files changed, 413 insertions, 0 deletions
diff --git a/debian/migrate/dma-migrate.c b/debian/migrate/dma-migrate.c
new file mode 100644
index 000000000000..56bfa2adbf87
--- /dev/null
+++ b/debian/migrate/dma-migrate.c
@@ -0,0 +1,413 @@
+/*-
+ * 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 <sys/types.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <regex.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#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;
+ int 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 '?':
+ default:
+ usage(1);
+ /* NOTREACHED */
+ }
+ if (Vflag)
+ version();
+ if (hflag)
+ usage(0);
+ 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 (dma 0.0.2010.06.17)\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
+}