2 * Copyright (c) 2010 Peter Pentchev
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 #include <sys/types.h>
48 #define __printflike(fmtarg, firstvararg) \
49 __attribute__((__format__ (__printf__, fmtarg, firstvararg)))
51 #define __printflike(fmtarg, firstvararg)
55 #define DEFAULT_SPOOLDIR "/var/spool/dma"
57 static int verbose = 0;
58 static char copybuf[BUFSIZ];
60 static int dma_migrate(int, const char *);
62 static int open_locked(const char *, int, ...);
63 static void cleanup_file(int, char *);
65 static void usage(int);
66 static void version(void);
67 static void debug(const char *, ...) __printflike(1, 2);
70 main(int argc, char **argv)
73 int hflag, Vflag, errs, fd, res;
79 srandom((unsigned long)((time(NULL) ^ getpid()) + ((uintptr_t)argv)));
82 spooldir = DEFAULT_SPOOLDIR;
83 while (ch = getopt(argc, argv, "d:hVv"), ch != -1)
117 if (chdir(spooldir) == -1)
118 err(1, "Could not change into spool directory %s", spooldir);
119 if (d = opendir("."), d == NULL)
120 err(1, "Could not read spool directory %s", spooldir);
122 while (e = readdir(d), e != NULL) {
123 /* Do we care about this entry? */
124 debug("Read a directory entry: %s\n", e->d_name);
125 if (strncmp(e->d_name, "tmp_", 4) == 0 ||
126 e->d_name[0] == 'M' || e->d_name[0] == 'Q' ||
127 (e->d_type != DT_REG && e->d_type != DT_UNKNOWN))
129 if (e->d_type == DT_UNKNOWN)
130 if (stat(e->d_name, &sb) == -1 || !S_ISREG(sb.st_mode))
132 debug("- want to process it\n");
134 /* Try to lock it - skip it if dma is delivering the message */
135 if (fd = open_locked(e->d_name, O_RDONLY|O_NDELAY), fd == -1) {
136 debug("- seems to be locked, skipping\n");
140 /* Okay, convert it to the M/Q schema */
141 res = dma_migrate(fd, e->d_name);
147 debug("Finished, %d conversion errors\n", errs);
149 debug("Everything seems to be all right\n");
154 dma_migrate(int fd, const char *fname)
157 char *mname, *qname, *tempname, *sender, *recp, *line, *recpline;
158 int mfd, qfd, tempfd;
160 FILE *fp, *qfp, *mfp;
162 static regex_t *qidreg = NULL;
164 mfd = tempfd = qfd = -1;
165 mname = qname = sender = recp = line = NULL;
168 if (fstat(fd, &sb) == -1) {
169 warn("Could not fstat(%s)", fname);
173 * Let's just blithely assume that the queue ID *is* the filename,
174 * since that's the way dma did things so far.
175 * Well, okay, let's check it.
177 if (qidreg == NULL) {
180 if ((nreg = malloc(sizeof(*qidreg))) == NULL) {
181 warn("Could not allocate memory for a regex");
184 if (regcomp(nreg, "^[a-fA-F0-9]\\+\\.[a-fA-F0-9]\\+$", 0)
186 warnx("Could not compile a dma queue ID regex");
192 if (regexec(qidreg, fname, 0, NULL, 0) != 0) {
193 warnx("The name '%s' is not a valid dma queue ID", fname);
197 debug(" - queue ID %s\n", id);
198 if (asprintf(&mname, "M%s", id) == -1 ||
199 asprintf(&tempname, "tmp_%s", id) == -1 ||
200 asprintf(&qname, "Q%s", id) == -1 ||
201 mname == NULL || tempname == NULL || qname == NULL)
204 /* Create the message placeholder early to avoid races */
205 mfd = open_locked(mname, O_CREAT | O_EXCL | O_RDWR, 0600);
207 warn("Could not create temporary file %s", mname);
210 if (stat(qname, &sb) != -1 || errno != ENOENT ||
211 stat(tempname, &sb) != -1 || errno != ENOENT) {
212 warnx("Some of the queue files for %s already exist", fname);
215 debug(" - mfd %d names %s, %s, %s\n", mfd, mname, tempname, qname);
217 fp = fdopen(fd, "r");
219 warn("Could not reopen the descriptor for %s", fname);
223 /* Parse the header of the old-format message file */
225 if (getline(&sender, &sz, fp) == -1) {
226 warn("Could not read the initial line from %s", fname);
230 while (sz > 0 && (sender[sz - 1] == '\n' || sender[sz - 1] == '\r'))
233 warnx("Empty sender line in %s", fname);
236 debug(" - sender %s\n", sender);
237 /* ...recipient(s)... */
241 if (getline(&line, &sz, fp) == -1) {
242 warn("Could not read a recipient line from %s", fname);
247 (line[sz - 1] == '\n' || line[sz - 1] == '\r'))
255 strncmp(line, fname, len) == 0 && line[len] == ' ') {
256 recp = line + len + 1;
264 warnx("Could not find its own recipient line in %s", fname);
267 /* ..phew, finished with the header. */
269 tempfd = open_locked(tempname, O_CREAT | O_EXCL | O_RDWR, 0600);
271 warn("Could not create a queue file for %s", fname);
274 qfp = fdopen(tempfd, "w");
276 warn("Could not fdopen(%s) for %s", tempname, fname);
279 mfp = fdopen(mfd, "w");
281 warn("Could not fdopen(%s) for %s", mname, fname);
284 fprintf(qfp, "ID: %s\nSender: %s\nRecipient: %s\n", id, sender, recp);
288 /* Copy the message file over to mname */
289 while ((sz = fread(copybuf, 1, sizeof(copybuf), fp)) > 0)
290 if (fwrite(copybuf, 1, sz, mfp) != sz) {
291 warn("Could not copy the message from %s to %s",
296 warn("Could not read the full message from %s", fname);
302 if (rename(tempname, qname) == -1) {
303 warn("Could not rename the queue file for %s", fname);
308 if (unlink(fname) == -1) {
309 warn("Could not remove the old converted file %s", fname);
332 if (recpline != NULL)
334 cleanup_file(mfd, mname);
335 cleanup_file(qfd, qname);
336 cleanup_file(tempfd, tempname);
341 cleanup_file(int fd, char *fname)
355 "Usage:\tdma-migrate [-hVv] [-d spooldir]\n"
356 "\t-d\tspecify the spool directory (" DEFAULT_SPOOLDIR ")\n"
357 "\t-h\tdisplay program usage information and exit\n"
358 "\t-V\tdisplay program version information and exit\n"
359 "\t-v\tverbose operation - display diagnostic messages";
369 printf("dma-migrate 0.01 (dma 0.0.2010.06.17)\n");
373 debug(const char *fmt, ...)
380 vfprintf(stderr, fmt, v);
385 open_locked(const char *fname, int flags, ...)
392 if (flags & O_CREAT) {
395 mode = va_arg(ap, int);
400 fd = open(fname, flags, mode);
403 if (flock(fd, LOCK_EX|((flags & O_NONBLOCK)? LOCK_NB: 0)) < 0) {
411 return(open(fname, flags|O_EXLOCK, mode));