]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - debian/migrate/dma-migrate.c
Import dma 89702b7f14 (2013-02-13) into vendors
[FreeBSD/FreeBSD.git] / debian / migrate / dma-migrate.c
1 /*-
2  * Copyright (c) 2010  Peter Pentchev
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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.
13  *
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
24  * SUCH DAMAGE.
25  */
26
27 #define _GNU_SOURCE
28
29 #include <sys/types.h>
30 #include <sys/file.h>
31 #include <sys/stat.h>
32
33 #include <dirent.h>
34 #include <err.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <inttypes.h>
38 #include <regex.h>
39 #include <stdarg.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <time.h>
44 #include <unistd.h>
45
46 #ifndef __printflike
47 #ifdef __GNUC__
48 #define __printflike(fmtarg, firstvararg)       \
49         __attribute__((__format__ (__printf__, fmtarg, firstvararg)))
50 #else
51 #define __printflike(fmtarg, firstvararg)
52 #endif
53 #endif
54
55 #define DEFAULT_SPOOLDIR        "/var/spool/dma"
56
57 static int       verbose = 0;
58 static char      copybuf[BUFSIZ];
59
60 static int       dma_migrate(int, const char *);
61
62 static int       open_locked(const char *, int, ...);
63 static void      cleanup_file(int, char *);
64
65 static void      usage(int);
66 static void      version(void);
67 static void      debug(const char *, ...) __printflike(1, 2);
68
69 int
70 main(int argc, char **argv)
71 {
72         const char *spooldir;
73         int hflag, Vflag, errs, fd, res;
74         int ch;
75         DIR *d;
76         struct dirent *e;
77         struct stat sb;
78
79         srandom((unsigned long)((time(NULL) ^ getpid()) + ((uintptr_t)argv)));
80
81         hflag = Vflag = 0;
82         spooldir = DEFAULT_SPOOLDIR;
83         while (ch = getopt(argc, argv, "d:hVv"), ch != -1)
84                 switch (ch) {
85                         case 'd':
86                                 spooldir = optarg;
87                                 break;
88
89                         case 'h':
90                                 hflag = 1;
91                                 break;
92
93                         case 'V':
94                                 Vflag = 1;
95                                 break;
96
97                         case 'v':
98                                 verbose = 1;
99                                 break;
100
101                         case '?':
102                         default:
103                                 usage(1);
104                                 /* NOTREACHED */
105                 }
106         if (Vflag)
107                 version();
108         if (hflag)
109                 usage(0);
110         if (hflag || Vflag)
111                 exit(0);
112
113         argc -= optind;
114         argv += optind;
115
116         /* Let's roll! */
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);
121         errs = 0;
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))
128                         continue;
129                 if (e->d_type == DT_UNKNOWN)
130                         if (stat(e->d_name, &sb) == -1 || !S_ISREG(sb.st_mode))
131                                 continue;
132                 debug("- want to process it\n");
133
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");
137                         continue;
138                 }
139
140                 /* Okay, convert it to the M/Q schema */
141                 res = dma_migrate(fd, e->d_name);
142                 close(fd);
143                 if (res == -1)
144                         errs++;
145         }
146         if (errs)
147                 debug("Finished, %d conversion errors\n", errs);
148         else
149                 debug("Everything seems to be all right\n");
150         return (errs && 1);
151 }
152
153 static int
154 dma_migrate(int fd, const char *fname)
155 {
156         const char *id;
157         char *mname, *qname, *tempname, *sender, *recp, *line, *recpline;
158         int mfd, qfd, tempfd;
159         struct stat sb;
160         FILE *fp, *qfp, *mfp;
161         size_t sz, len;
162         static regex_t *qidreg = NULL;
163
164         mfd = tempfd = qfd = -1;
165         mname = qname = sender = recp = line = NULL;
166         fp = qfp = NULL;
167
168         if (fstat(fd, &sb) == -1) {
169                 warn("Could not fstat(%s)", fname);
170                 return (-1);
171         }
172         /*
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.
176          */
177         if (qidreg == NULL) {
178                 regex_t *nreg;
179
180                 if ((nreg = malloc(sizeof(*qidreg))) == NULL) {
181                         warn("Could not allocate memory for a regex");
182                         return (-1);
183                 }
184                 if (regcomp(nreg, "^[a-fA-F0-9]\\+\\.[a-fA-F0-9]\\+$", 0)
185                     != 0) {
186                         warnx("Could not compile a dma queue ID regex");
187                         free(nreg);
188                         return (-1);
189                 }
190                 qidreg = nreg;
191         }
192         if (regexec(qidreg, fname, 0, NULL, 0) != 0) {
193                 warnx("The name '%s' is not a valid dma queue ID", fname);
194                 return (-1);
195         }
196         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)
202                 goto fail;
203
204         /* Create the message placeholder early to avoid races */
205         mfd = open_locked(mname, O_CREAT | O_EXCL | O_RDWR, 0600);
206         if (mfd == -1) {
207                 warn("Could not create temporary file %s", mname);
208                 goto fail;
209         }
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);
213                 goto fail;
214         }
215         debug("  - mfd %d names %s, %s, %s\n", mfd, mname, tempname, qname);
216
217         fp = fdopen(fd, "r");
218         if (fp == NULL) {
219                 warn("Could not reopen the descriptor for %s", fname);
220                 goto fail;
221         }
222
223         /* Parse the header of the old-format message file */
224         /* ...sender... */
225         if (getline(&sender, &sz, fp) == -1) {
226                 warn("Could not read the initial line from %s", fname);
227                 goto fail;
228         }
229         sz = strlen(sender);
230         while (sz > 0 && (sender[sz - 1] == '\n' || sender[sz - 1] == '\r'))
231                 sender[--sz] = '\0';
232         if (sz == 0) {
233                 warnx("Empty sender line in %s", fname);
234                 goto fail;
235         }
236         debug("  - sender %s\n", sender);
237         /* ...recipient(s)... */
238         len = strlen(fname);
239         recpline = NULL;
240         while (1) {
241                 if (getline(&line, &sz, fp) == -1) {
242                         warn("Could not read a recipient line from %s", fname);
243                         goto fail;
244                 }
245                 sz = strlen(line);
246                 while (sz > 0 &&
247                     (line[sz - 1] == '\n' || line[sz - 1] == '\r'))
248                         line[--sz] = '\0';
249                 if (sz == 0) {
250                         free(line);
251                         line = NULL;
252                         break;
253                 }
254                 if (recp == NULL &&
255                     strncmp(line, fname, len) == 0 && line[len] == ' ') {
256                         recp = line + len + 1;
257                         recpline = line;
258                 } else {
259                         free(line);
260                 }
261                 line = NULL;
262         }
263         if (recp == NULL) {
264                 warnx("Could not find its own recipient line in %s", fname);
265                 goto fail;
266         }
267         /* ..phew, finished with the header. */
268
269         tempfd = open_locked(tempname, O_CREAT | O_EXCL | O_RDWR, 0600);
270         if (tempfd == -1) {
271                 warn("Could not create a queue file for %s", fname);
272                 goto fail;
273         }
274         qfp = fdopen(tempfd, "w");
275         if (qfp == NULL) {
276                 warn("Could not fdopen(%s) for %s", tempname, fname);
277                 goto fail;
278         }
279         mfp = fdopen(mfd, "w");
280         if (mfp == NULL) {
281                 warn("Could not fdopen(%s) for %s", mname, fname);
282                 goto fail;
283         }
284         fprintf(qfp, "ID: %s\nSender: %s\nRecipient: %s\n", id, sender, recp);
285         fflush(qfp);
286         fsync(tempfd);
287
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",
292                             fname, mname);
293                         goto fail;
294                 }
295         if (ferror(fp)) {
296                 warn("Could not read the full message from %s", fname);
297                 goto fail;
298         }
299         fflush(mfp);
300         fsync(mfd);
301
302         if (rename(tempname, qname) == -1) {
303                 warn("Could not rename the queue file for %s", fname);
304                 goto fail;
305         }
306         qfd = tempfd;
307         tempfd = -1;
308         if (unlink(fname) == -1) {
309                 warn("Could not remove the old converted file %s", fname);
310                 goto fail;
311         }
312
313         fclose(fp);
314         fclose(qfp);
315         free(sender);
316         free(line);
317         free(recpline);
318         free(mname);
319         free(qname);
320         free(tempname);
321         return (0);
322
323 fail:
324         if (fp != NULL)
325                 fclose(fp);
326         if (qfp != NULL)
327                 fclose(qfp);
328         if (sender != NULL)
329                 free(sender);
330         if (line != NULL)
331                 free(line);
332         if (recpline != NULL)
333                 free(recpline);
334         cleanup_file(mfd, mname);
335         cleanup_file(qfd, qname);
336         cleanup_file(tempfd, tempname);
337         return (-1);
338 }
339
340 static void
341 cleanup_file(int fd, char *fname)
342 {
343         if (fd != -1) {
344                 close(fd);
345                 unlink(fname);
346         }
347         if (fname != NULL)
348                 free(fname);
349 }
350
351 static void
352 usage(int ferr)
353 {
354         const char *s =
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";
360
361         if (ferr)
362                 errx(1, "%s", s);
363         puts(s);
364 }
365
366 static void
367 version(void)
368 {
369         printf("dma-migrate 0.01 (dma 0.0.2010.06.17)\n");
370 }
371
372 static void
373 debug(const char *fmt, ...)
374 {
375         va_list v;
376
377         if (verbose < 1)
378                 return;
379         va_start(v, fmt);
380         vfprintf(stderr, fmt, v);
381         va_end(v);
382 }
383
384 static int
385 open_locked(const char *fname, int flags, ...)
386 {
387         int mode = 0;
388 #ifndef O_EXLOCK
389         int fd, save_errno;
390 #endif
391
392         if (flags & O_CREAT) {
393                 va_list ap;
394                 va_start(ap, flags);
395                 mode = va_arg(ap, int);
396                 va_end(ap);
397         }
398
399 #ifndef O_EXLOCK
400         fd = open(fname, flags, mode);
401         if (fd < 0)
402                 return(fd);
403         if (flock(fd, LOCK_EX|((flags & O_NONBLOCK)? LOCK_NB: 0)) < 0) {
404                 save_errno = errno;
405                 close(fd);
406                 errno = save_errno;
407                 return(-1);
408         }
409         return(fd);
410 #else
411         return(open(fname, flags|O_EXLOCK, mode));
412 #endif
413 }