xref: /freebsd/contrib/dma/spool.c (revision a9e8641da961bcf3d24afc85fd657f2083a872a2)
1*a9e8641dSBaptiste Daroussin /*
2*a9e8641dSBaptiste Daroussin  * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
3*a9e8641dSBaptiste Daroussin  *
4*a9e8641dSBaptiste Daroussin  * This code is derived from software contributed to The DragonFly Project
5*a9e8641dSBaptiste Daroussin  * by Simon 'corecode' Schubert <corecode@fs.ei.tum.de>.
6*a9e8641dSBaptiste Daroussin  *
7*a9e8641dSBaptiste Daroussin  * Redistribution and use in source and binary forms, with or without
8*a9e8641dSBaptiste Daroussin  * modification, are permitted provided that the following conditions
9*a9e8641dSBaptiste Daroussin  * are met:
10*a9e8641dSBaptiste Daroussin  *
11*a9e8641dSBaptiste Daroussin  * 1. Redistributions of source code must retain the above copyright
12*a9e8641dSBaptiste Daroussin  *    notice, this list of conditions and the following disclaimer.
13*a9e8641dSBaptiste Daroussin  * 2. Redistributions in binary form must reproduce the above copyright
14*a9e8641dSBaptiste Daroussin  *    notice, this list of conditions and the following disclaimer in
15*a9e8641dSBaptiste Daroussin  *    the documentation and/or other materials provided with the
16*a9e8641dSBaptiste Daroussin  *    distribution.
17*a9e8641dSBaptiste Daroussin  * 3. Neither the name of The DragonFly Project nor the names of its
18*a9e8641dSBaptiste Daroussin  *    contributors may be used to endorse or promote products derived
19*a9e8641dSBaptiste Daroussin  *    from this software without specific, prior written permission.
20*a9e8641dSBaptiste Daroussin  *
21*a9e8641dSBaptiste Daroussin  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22*a9e8641dSBaptiste Daroussin  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23*a9e8641dSBaptiste Daroussin  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24*a9e8641dSBaptiste Daroussin  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25*a9e8641dSBaptiste Daroussin  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26*a9e8641dSBaptiste Daroussin  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27*a9e8641dSBaptiste Daroussin  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28*a9e8641dSBaptiste Daroussin  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29*a9e8641dSBaptiste Daroussin  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30*a9e8641dSBaptiste Daroussin  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31*a9e8641dSBaptiste Daroussin  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32*a9e8641dSBaptiste Daroussin  * SUCH DAMAGE.
33*a9e8641dSBaptiste Daroussin  */
34*a9e8641dSBaptiste Daroussin 
35*a9e8641dSBaptiste Daroussin #include "dfcompat.h"
36*a9e8641dSBaptiste Daroussin 
37*a9e8641dSBaptiste Daroussin #include <sys/file.h>
38*a9e8641dSBaptiste Daroussin #include <sys/stat.h>
39*a9e8641dSBaptiste Daroussin 
40*a9e8641dSBaptiste Daroussin #include <ctype.h>
41*a9e8641dSBaptiste Daroussin #include <dirent.h>
42*a9e8641dSBaptiste Daroussin #include <err.h>
43*a9e8641dSBaptiste Daroussin #include <errno.h>
44*a9e8641dSBaptiste Daroussin #include <fcntl.h>
45*a9e8641dSBaptiste Daroussin #include <inttypes.h>
46*a9e8641dSBaptiste Daroussin #include <unistd.h>
47*a9e8641dSBaptiste Daroussin #include <syslog.h>
48*a9e8641dSBaptiste Daroussin 
49*a9e8641dSBaptiste Daroussin #include "dma.h"
50*a9e8641dSBaptiste Daroussin 
51*a9e8641dSBaptiste Daroussin /*
52*a9e8641dSBaptiste Daroussin  * Spool file format:
53*a9e8641dSBaptiste Daroussin  *
54*a9e8641dSBaptiste Daroussin  * 'Q'id files (queue):
55*a9e8641dSBaptiste Daroussin  *   Organized like an RFC822 header, field: value.  Ignores unknown fields.
56*a9e8641dSBaptiste Daroussin  *   ID: id
57*a9e8641dSBaptiste Daroussin  *   Sender: envelope-from
58*a9e8641dSBaptiste Daroussin  *   Recipient: envelope-to
59*a9e8641dSBaptiste Daroussin  *
60*a9e8641dSBaptiste Daroussin  * 'M'id files (data):
61*a9e8641dSBaptiste Daroussin  *   mail data
62*a9e8641dSBaptiste Daroussin  *
63*a9e8641dSBaptiste Daroussin  * Each queue file needs to have a corresponding data file.
64*a9e8641dSBaptiste Daroussin  * One data file might be shared by linking it several times.
65*a9e8641dSBaptiste Daroussin  *
66*a9e8641dSBaptiste Daroussin  * Queue ids are unique, formed from the inode of the data file
67*a9e8641dSBaptiste Daroussin  * and a unique identifier.
68*a9e8641dSBaptiste Daroussin  */
69*a9e8641dSBaptiste Daroussin 
70*a9e8641dSBaptiste Daroussin int
71*a9e8641dSBaptiste Daroussin newspoolf(struct queue *queue)
72*a9e8641dSBaptiste Daroussin {
73*a9e8641dSBaptiste Daroussin 	char fn[PATH_MAX+1];
74*a9e8641dSBaptiste Daroussin 	struct stat st;
75*a9e8641dSBaptiste Daroussin 	struct stritem *t;
76*a9e8641dSBaptiste Daroussin 	int fd;
77*a9e8641dSBaptiste Daroussin 
78*a9e8641dSBaptiste Daroussin 	if (snprintf(fn, sizeof(fn), "%s/%s", config.spooldir, "tmp_XXXXXXXXXX") <= 0)
79*a9e8641dSBaptiste Daroussin 		return (-1);
80*a9e8641dSBaptiste Daroussin 
81*a9e8641dSBaptiste Daroussin 	fd = mkstemp(fn);
82*a9e8641dSBaptiste Daroussin 	if (fd < 0)
83*a9e8641dSBaptiste Daroussin 		return (-1);
84*a9e8641dSBaptiste Daroussin 	/* XXX group rights */
85*a9e8641dSBaptiste Daroussin 	if (fchmod(fd, 0660) < 0)
86*a9e8641dSBaptiste Daroussin 		goto fail;
87*a9e8641dSBaptiste Daroussin 	if (flock(fd, LOCK_EX) == -1)
88*a9e8641dSBaptiste Daroussin 		goto fail;
89*a9e8641dSBaptiste Daroussin 	queue->tmpf = strdup(fn);
90*a9e8641dSBaptiste Daroussin 	if (queue->tmpf == NULL)
91*a9e8641dSBaptiste Daroussin 		goto fail;
92*a9e8641dSBaptiste Daroussin 
93*a9e8641dSBaptiste Daroussin 	/*
94*a9e8641dSBaptiste Daroussin 	 * Assign queue id
95*a9e8641dSBaptiste Daroussin 	 */
96*a9e8641dSBaptiste Daroussin 	if (fstat(fd, &st) != 0)
97*a9e8641dSBaptiste Daroussin 		goto fail;
98*a9e8641dSBaptiste Daroussin 	if (asprintf(&queue->id, "%"PRIxMAX, (uintmax_t)st.st_ino) < 0)
99*a9e8641dSBaptiste Daroussin 		goto fail;
100*a9e8641dSBaptiste Daroussin 
101*a9e8641dSBaptiste Daroussin 	queue->mailf = fdopen(fd, "r+");
102*a9e8641dSBaptiste Daroussin 	if (queue->mailf == NULL)
103*a9e8641dSBaptiste Daroussin 		goto fail;
104*a9e8641dSBaptiste Daroussin 
105*a9e8641dSBaptiste Daroussin 	t = malloc(sizeof(*t));
106*a9e8641dSBaptiste Daroussin 	if (t != NULL) {
107*a9e8641dSBaptiste Daroussin 		t->str = queue->tmpf;
108*a9e8641dSBaptiste Daroussin 		SLIST_INSERT_HEAD(&tmpfs, t, next);
109*a9e8641dSBaptiste Daroussin 	}
110*a9e8641dSBaptiste Daroussin 	return (0);
111*a9e8641dSBaptiste Daroussin 
112*a9e8641dSBaptiste Daroussin fail:
113*a9e8641dSBaptiste Daroussin 	if (queue->mailf != NULL)
114*a9e8641dSBaptiste Daroussin 		fclose(queue->mailf);
115*a9e8641dSBaptiste Daroussin 	close(fd);
116*a9e8641dSBaptiste Daroussin 	unlink(fn);
117*a9e8641dSBaptiste Daroussin 	return (-1);
118*a9e8641dSBaptiste Daroussin }
119*a9e8641dSBaptiste Daroussin 
120*a9e8641dSBaptiste Daroussin static int
121*a9e8641dSBaptiste Daroussin writequeuef(struct qitem *it)
122*a9e8641dSBaptiste Daroussin {
123*a9e8641dSBaptiste Daroussin 	int error;
124*a9e8641dSBaptiste Daroussin 	int queuefd;
125*a9e8641dSBaptiste Daroussin 
126*a9e8641dSBaptiste Daroussin 	queuefd = open_locked(it->queuefn, O_CREAT|O_EXCL|O_RDWR, 0660);
127*a9e8641dSBaptiste Daroussin 	if (queuefd == -1)
128*a9e8641dSBaptiste Daroussin 		return (-1);
129*a9e8641dSBaptiste Daroussin 	if (fchmod(queuefd, 0660) < 0)
130*a9e8641dSBaptiste Daroussin 		return (-1);
131*a9e8641dSBaptiste Daroussin 	it->queuef = fdopen(queuefd, "w+");
132*a9e8641dSBaptiste Daroussin 	if (it->queuef == NULL)
133*a9e8641dSBaptiste Daroussin 		return (-1);
134*a9e8641dSBaptiste Daroussin 
135*a9e8641dSBaptiste Daroussin 	error = fprintf(it->queuef,
136*a9e8641dSBaptiste Daroussin 			"ID: %s\n"
137*a9e8641dSBaptiste Daroussin 			"Sender: %s\n"
138*a9e8641dSBaptiste Daroussin 			"Recipient: %s\n",
139*a9e8641dSBaptiste Daroussin 			 it->queueid,
140*a9e8641dSBaptiste Daroussin 			 it->sender,
141*a9e8641dSBaptiste Daroussin 			 it->addr);
142*a9e8641dSBaptiste Daroussin 
143*a9e8641dSBaptiste Daroussin 	if (error <= 0)
144*a9e8641dSBaptiste Daroussin 		return (-1);
145*a9e8641dSBaptiste Daroussin 
146*a9e8641dSBaptiste Daroussin 	if (fflush(it->queuef) != 0 || fsync(fileno(it->queuef)) != 0)
147*a9e8641dSBaptiste Daroussin 		return (-1);
148*a9e8641dSBaptiste Daroussin 
149*a9e8641dSBaptiste Daroussin 	return (0);
150*a9e8641dSBaptiste Daroussin }
151*a9e8641dSBaptiste Daroussin 
152*a9e8641dSBaptiste Daroussin static struct qitem *
153*a9e8641dSBaptiste Daroussin readqueuef(struct queue *queue, char *queuefn)
154*a9e8641dSBaptiste Daroussin {
155*a9e8641dSBaptiste Daroussin 	char line[1000];
156*a9e8641dSBaptiste Daroussin 	struct queue itmqueue;
157*a9e8641dSBaptiste Daroussin 	FILE *queuef = NULL;
158*a9e8641dSBaptiste Daroussin 	char *s;
159*a9e8641dSBaptiste Daroussin 	char *queueid = NULL, *sender = NULL, *addr = NULL;
160*a9e8641dSBaptiste Daroussin 	struct qitem *it = NULL;
161*a9e8641dSBaptiste Daroussin 
162*a9e8641dSBaptiste Daroussin 	bzero(&itmqueue, sizeof(itmqueue));
163*a9e8641dSBaptiste Daroussin 	LIST_INIT(&itmqueue.queue);
164*a9e8641dSBaptiste Daroussin 
165*a9e8641dSBaptiste Daroussin 	queuef = fopen(queuefn, "r");
166*a9e8641dSBaptiste Daroussin 	if (queuef == NULL)
167*a9e8641dSBaptiste Daroussin 		goto out;
168*a9e8641dSBaptiste Daroussin 
169*a9e8641dSBaptiste Daroussin 	while (!feof(queuef)) {
170*a9e8641dSBaptiste Daroussin 		if (fgets(line, sizeof(line), queuef) == NULL || line[0] == 0)
171*a9e8641dSBaptiste Daroussin 			break;
172*a9e8641dSBaptiste Daroussin 		line[strlen(line) - 1] = 0;	/* chop newline */
173*a9e8641dSBaptiste Daroussin 
174*a9e8641dSBaptiste Daroussin 		s = strchr(line, ':');
175*a9e8641dSBaptiste Daroussin 		if (s == NULL)
176*a9e8641dSBaptiste Daroussin 			goto malformed;
177*a9e8641dSBaptiste Daroussin 		*s = 0;
178*a9e8641dSBaptiste Daroussin 
179*a9e8641dSBaptiste Daroussin 		s++;
180*a9e8641dSBaptiste Daroussin 		while (isspace(*s))
181*a9e8641dSBaptiste Daroussin 			s++;
182*a9e8641dSBaptiste Daroussin 
183*a9e8641dSBaptiste Daroussin 		s = strdup(s);
184*a9e8641dSBaptiste Daroussin 		if (s == NULL)
185*a9e8641dSBaptiste Daroussin 			goto malformed;
186*a9e8641dSBaptiste Daroussin 
187*a9e8641dSBaptiste Daroussin 		if (strcmp(line, "ID") == 0) {
188*a9e8641dSBaptiste Daroussin 			queueid = s;
189*a9e8641dSBaptiste Daroussin 		} else if (strcmp(line, "Sender") == 0) {
190*a9e8641dSBaptiste Daroussin 			sender = s;
191*a9e8641dSBaptiste Daroussin 		} else if (strcmp(line, "Recipient") == 0) {
192*a9e8641dSBaptiste Daroussin 			addr = s;
193*a9e8641dSBaptiste Daroussin 		} else {
194*a9e8641dSBaptiste Daroussin 			syslog(LOG_DEBUG, "ignoring unknown queue info `%s' in `%s'",
195*a9e8641dSBaptiste Daroussin 			       line, queuefn);
196*a9e8641dSBaptiste Daroussin 			free(s);
197*a9e8641dSBaptiste Daroussin 		}
198*a9e8641dSBaptiste Daroussin 	}
199*a9e8641dSBaptiste Daroussin 
200*a9e8641dSBaptiste Daroussin 	if (queueid == NULL || sender == NULL || addr == NULL ||
201*a9e8641dSBaptiste Daroussin 	    *queueid == 0 || *addr == 0) {
202*a9e8641dSBaptiste Daroussin malformed:
203*a9e8641dSBaptiste Daroussin 		errno = EINVAL;
204*a9e8641dSBaptiste Daroussin 		syslog(LOG_ERR, "malformed queue file `%s'", queuefn);
205*a9e8641dSBaptiste Daroussin 		goto out;
206*a9e8641dSBaptiste Daroussin 	}
207*a9e8641dSBaptiste Daroussin 
208*a9e8641dSBaptiste Daroussin 	if (add_recp(&itmqueue, addr, 0) != 0)
209*a9e8641dSBaptiste Daroussin 		goto out;
210*a9e8641dSBaptiste Daroussin 
211*a9e8641dSBaptiste Daroussin 	it = LIST_FIRST(&itmqueue.queue);
212*a9e8641dSBaptiste Daroussin 	it->sender = sender; sender = NULL;
213*a9e8641dSBaptiste Daroussin 	it->queueid = queueid; queueid = NULL;
214*a9e8641dSBaptiste Daroussin 	it->queuefn = queuefn; queuefn = NULL;
215*a9e8641dSBaptiste Daroussin 	LIST_INSERT_HEAD(&queue->queue, it, next);
216*a9e8641dSBaptiste Daroussin 
217*a9e8641dSBaptiste Daroussin out:
218*a9e8641dSBaptiste Daroussin 	if (sender != NULL)
219*a9e8641dSBaptiste Daroussin 		free(sender);
220*a9e8641dSBaptiste Daroussin 	if (queueid != NULL)
221*a9e8641dSBaptiste Daroussin 		free(queueid);
222*a9e8641dSBaptiste Daroussin 	if (addr != NULL)
223*a9e8641dSBaptiste Daroussin 		free(addr);
224*a9e8641dSBaptiste Daroussin 	if (queuef != NULL)
225*a9e8641dSBaptiste Daroussin 		fclose(queuef);
226*a9e8641dSBaptiste Daroussin 
227*a9e8641dSBaptiste Daroussin 	return (it);
228*a9e8641dSBaptiste Daroussin }
229*a9e8641dSBaptiste Daroussin 
230*a9e8641dSBaptiste Daroussin int
231*a9e8641dSBaptiste Daroussin linkspool(struct queue *queue)
232*a9e8641dSBaptiste Daroussin {
233*a9e8641dSBaptiste Daroussin 	struct stat st;
234*a9e8641dSBaptiste Daroussin 	struct qitem *it;
235*a9e8641dSBaptiste Daroussin 
236*a9e8641dSBaptiste Daroussin 	if (fflush(queue->mailf) != 0 || fsync(fileno(queue->mailf)) != 0)
237*a9e8641dSBaptiste Daroussin 		goto delfiles;
238*a9e8641dSBaptiste Daroussin 
239*a9e8641dSBaptiste Daroussin 	syslog(LOG_INFO, "new mail from user=%s uid=%d envelope_from=<%s>",
240*a9e8641dSBaptiste Daroussin 	       username, getuid(), queue->sender);
241*a9e8641dSBaptiste Daroussin 
242*a9e8641dSBaptiste Daroussin 	LIST_FOREACH(it, &queue->queue, next) {
243*a9e8641dSBaptiste Daroussin 		if (asprintf(&it->queueid, "%s.%"PRIxPTR, queue->id, (uintptr_t)it) <= 0)
244*a9e8641dSBaptiste Daroussin 			goto delfiles;
245*a9e8641dSBaptiste Daroussin 		if (asprintf(&it->queuefn, "%s/Q%s", config.spooldir, it->queueid) <= 0)
246*a9e8641dSBaptiste Daroussin 			goto delfiles;
247*a9e8641dSBaptiste Daroussin 		if (asprintf(&it->mailfn, "%s/M%s", config.spooldir, it->queueid) <= 0)
248*a9e8641dSBaptiste Daroussin 			goto delfiles;
249*a9e8641dSBaptiste Daroussin 
250*a9e8641dSBaptiste Daroussin 		/* Neither file may not exist yet */
251*a9e8641dSBaptiste Daroussin 		if (stat(it->queuefn, &st) == 0 || stat(it->mailfn, &st) == 0)
252*a9e8641dSBaptiste Daroussin 			goto delfiles;
253*a9e8641dSBaptiste Daroussin 
254*a9e8641dSBaptiste Daroussin 		if (writequeuef(it) != 0)
255*a9e8641dSBaptiste Daroussin 			goto delfiles;
256*a9e8641dSBaptiste Daroussin 
257*a9e8641dSBaptiste Daroussin 		if (link(queue->tmpf, it->mailfn) != 0)
258*a9e8641dSBaptiste Daroussin 			goto delfiles;
259*a9e8641dSBaptiste Daroussin 	}
260*a9e8641dSBaptiste Daroussin 
261*a9e8641dSBaptiste Daroussin 	LIST_FOREACH(it, &queue->queue, next) {
262*a9e8641dSBaptiste Daroussin 		syslog(LOG_INFO, "mail to=<%s> queued as %s",
263*a9e8641dSBaptiste Daroussin 		       it->addr, it->queueid);
264*a9e8641dSBaptiste Daroussin 	}
265*a9e8641dSBaptiste Daroussin 
266*a9e8641dSBaptiste Daroussin 	unlink(queue->tmpf);
267*a9e8641dSBaptiste Daroussin 	return (0);
268*a9e8641dSBaptiste Daroussin 
269*a9e8641dSBaptiste Daroussin delfiles:
270*a9e8641dSBaptiste Daroussin 	LIST_FOREACH(it, &queue->queue, next) {
271*a9e8641dSBaptiste Daroussin 		unlink(it->mailfn);
272*a9e8641dSBaptiste Daroussin 		unlink(it->queuefn);
273*a9e8641dSBaptiste Daroussin 	}
274*a9e8641dSBaptiste Daroussin 	return (-1);
275*a9e8641dSBaptiste Daroussin }
276*a9e8641dSBaptiste Daroussin 
277*a9e8641dSBaptiste Daroussin int
278*a9e8641dSBaptiste Daroussin load_queue(struct queue *queue)
279*a9e8641dSBaptiste Daroussin {
280*a9e8641dSBaptiste Daroussin 	struct stat sb;
281*a9e8641dSBaptiste Daroussin 	struct qitem *it;
282*a9e8641dSBaptiste Daroussin 	DIR *spooldir;
283*a9e8641dSBaptiste Daroussin 	struct dirent *de;
284*a9e8641dSBaptiste Daroussin 	char *queuefn;
285*a9e8641dSBaptiste Daroussin 	char *mailfn;
286*a9e8641dSBaptiste Daroussin 
287*a9e8641dSBaptiste Daroussin 	bzero(queue, sizeof(*queue));
288*a9e8641dSBaptiste Daroussin 	LIST_INIT(&queue->queue);
289*a9e8641dSBaptiste Daroussin 
290*a9e8641dSBaptiste Daroussin 	spooldir = opendir(config.spooldir);
291*a9e8641dSBaptiste Daroussin 	if (spooldir == NULL)
292*a9e8641dSBaptiste Daroussin 		err(1, "reading queue");
293*a9e8641dSBaptiste Daroussin 
294*a9e8641dSBaptiste Daroussin 	while ((de = readdir(spooldir)) != NULL) {
295*a9e8641dSBaptiste Daroussin 		queuefn = NULL;
296*a9e8641dSBaptiste Daroussin 		mailfn = NULL;
297*a9e8641dSBaptiste Daroussin 
298*a9e8641dSBaptiste Daroussin 		/* ignore non-queue files */
299*a9e8641dSBaptiste Daroussin 		if (de->d_name[0] != 'Q')
300*a9e8641dSBaptiste Daroussin 			continue;
301*a9e8641dSBaptiste Daroussin 		if (asprintf(&queuefn, "%s/Q%s", config.spooldir, de->d_name + 1) < 0)
302*a9e8641dSBaptiste Daroussin 			goto fail;
303*a9e8641dSBaptiste Daroussin 		if (asprintf(&mailfn, "%s/M%s", config.spooldir, de->d_name + 1) < 0)
304*a9e8641dSBaptiste Daroussin 			goto fail;
305*a9e8641dSBaptiste Daroussin 
306*a9e8641dSBaptiste Daroussin 		/*
307*a9e8641dSBaptiste Daroussin 		 * Some file systems don't provide a de->d_type, so we have to
308*a9e8641dSBaptiste Daroussin 		 * do an explicit stat on the queue file.
309*a9e8641dSBaptiste Daroussin 		 * Move on if it turns out to be something else than a file.
310*a9e8641dSBaptiste Daroussin 		 */
311*a9e8641dSBaptiste Daroussin 		if (stat(queuefn, &sb) != 0)
312*a9e8641dSBaptiste Daroussin 			goto skip_item;
313*a9e8641dSBaptiste Daroussin 		if (!S_ISREG(sb.st_mode)) {
314*a9e8641dSBaptiste Daroussin 			errno = EINVAL;
315*a9e8641dSBaptiste Daroussin 			goto skip_item;
316*a9e8641dSBaptiste Daroussin 		}
317*a9e8641dSBaptiste Daroussin 
318*a9e8641dSBaptiste Daroussin 		if (stat(mailfn, &sb) != 0)
319*a9e8641dSBaptiste Daroussin 			goto skip_item;
320*a9e8641dSBaptiste Daroussin 
321*a9e8641dSBaptiste Daroussin 		it = readqueuef(queue, queuefn);
322*a9e8641dSBaptiste Daroussin 		if (it == NULL)
323*a9e8641dSBaptiste Daroussin 			goto skip_item;
324*a9e8641dSBaptiste Daroussin 
325*a9e8641dSBaptiste Daroussin 		it->mailfn = mailfn;
326*a9e8641dSBaptiste Daroussin 		continue;
327*a9e8641dSBaptiste Daroussin 
328*a9e8641dSBaptiste Daroussin skip_item:
329*a9e8641dSBaptiste Daroussin 		syslog(LOG_INFO, "could not pick up queue file: `%s'/`%s': %m", queuefn, mailfn);
330*a9e8641dSBaptiste Daroussin 		if (queuefn != NULL)
331*a9e8641dSBaptiste Daroussin 			free(queuefn);
332*a9e8641dSBaptiste Daroussin 		if (mailfn != NULL)
333*a9e8641dSBaptiste Daroussin 			free(mailfn);
334*a9e8641dSBaptiste Daroussin 	}
335*a9e8641dSBaptiste Daroussin 	closedir(spooldir);
336*a9e8641dSBaptiste Daroussin 	return (0);
337*a9e8641dSBaptiste Daroussin 
338*a9e8641dSBaptiste Daroussin fail:
339*a9e8641dSBaptiste Daroussin 	return (-1);
340*a9e8641dSBaptiste Daroussin }
341*a9e8641dSBaptiste Daroussin 
342*a9e8641dSBaptiste Daroussin void
343*a9e8641dSBaptiste Daroussin delqueue(struct qitem *it)
344*a9e8641dSBaptiste Daroussin {
345*a9e8641dSBaptiste Daroussin 	unlink(it->mailfn);
346*a9e8641dSBaptiste Daroussin 	unlink(it->queuefn);
347*a9e8641dSBaptiste Daroussin 	if (it->queuef != NULL)
348*a9e8641dSBaptiste Daroussin 		fclose(it->queuef);
349*a9e8641dSBaptiste Daroussin 	if (it->mailf != NULL)
350*a9e8641dSBaptiste Daroussin 		fclose(it->mailf);
351*a9e8641dSBaptiste Daroussin 	free(it);
352*a9e8641dSBaptiste Daroussin }
353*a9e8641dSBaptiste Daroussin 
354*a9e8641dSBaptiste Daroussin int
355*a9e8641dSBaptiste Daroussin acquirespool(struct qitem *it)
356*a9e8641dSBaptiste Daroussin {
357*a9e8641dSBaptiste Daroussin 	int queuefd;
358*a9e8641dSBaptiste Daroussin 
359*a9e8641dSBaptiste Daroussin 	if (it->queuef == NULL) {
360*a9e8641dSBaptiste Daroussin 		queuefd = open_locked(it->queuefn, O_RDWR|O_NONBLOCK);
361*a9e8641dSBaptiste Daroussin 		if (queuefd < 0)
362*a9e8641dSBaptiste Daroussin 			goto fail;
363*a9e8641dSBaptiste Daroussin 		it->queuef = fdopen(queuefd, "r+");
364*a9e8641dSBaptiste Daroussin 		if (it->queuef == NULL)
365*a9e8641dSBaptiste Daroussin 			goto fail;
366*a9e8641dSBaptiste Daroussin 	}
367*a9e8641dSBaptiste Daroussin 
368*a9e8641dSBaptiste Daroussin 	if (it->mailf == NULL) {
369*a9e8641dSBaptiste Daroussin 		it->mailf = fopen(it->mailfn, "r");
370*a9e8641dSBaptiste Daroussin 		if (it->mailf == NULL)
371*a9e8641dSBaptiste Daroussin 			goto fail;
372*a9e8641dSBaptiste Daroussin 	}
373*a9e8641dSBaptiste Daroussin 
374*a9e8641dSBaptiste Daroussin 	return (0);
375*a9e8641dSBaptiste Daroussin 
376*a9e8641dSBaptiste Daroussin fail:
377*a9e8641dSBaptiste Daroussin 	if (errno == EWOULDBLOCK)
378*a9e8641dSBaptiste Daroussin 		return (1);
379*a9e8641dSBaptiste Daroussin 	syslog(LOG_INFO, "could not acquire queue file: %m");
380*a9e8641dSBaptiste Daroussin 	return (-1);
381*a9e8641dSBaptiste Daroussin }
382*a9e8641dSBaptiste Daroussin 
383*a9e8641dSBaptiste Daroussin void
384*a9e8641dSBaptiste Daroussin dropspool(struct queue *queue, struct qitem *keep)
385*a9e8641dSBaptiste Daroussin {
386*a9e8641dSBaptiste Daroussin 	struct qitem *it;
387*a9e8641dSBaptiste Daroussin 
388*a9e8641dSBaptiste Daroussin 	LIST_FOREACH(it, &queue->queue, next) {
389*a9e8641dSBaptiste Daroussin 		if (it == keep)
390*a9e8641dSBaptiste Daroussin 			continue;
391*a9e8641dSBaptiste Daroussin 
392*a9e8641dSBaptiste Daroussin 		if (it->queuef != NULL)
393*a9e8641dSBaptiste Daroussin 			fclose(it->queuef);
394*a9e8641dSBaptiste Daroussin 		if (it->mailf != NULL)
395*a9e8641dSBaptiste Daroussin 			fclose(it->mailf);
396*a9e8641dSBaptiste Daroussin 	}
397*a9e8641dSBaptiste Daroussin }
398*a9e8641dSBaptiste Daroussin 
399*a9e8641dSBaptiste Daroussin int
400*a9e8641dSBaptiste Daroussin flushqueue_since(unsigned int period)
401*a9e8641dSBaptiste Daroussin {
402*a9e8641dSBaptiste Daroussin         struct stat st;
403*a9e8641dSBaptiste Daroussin 	struct timeval now;
404*a9e8641dSBaptiste Daroussin         char *flushfn = NULL;
405*a9e8641dSBaptiste Daroussin 
406*a9e8641dSBaptiste Daroussin 	if (asprintf(&flushfn, "%s/%s", config.spooldir, SPOOL_FLUSHFILE) < 0)
407*a9e8641dSBaptiste Daroussin 		return (0);
408*a9e8641dSBaptiste Daroussin 	if (stat(flushfn, &st) < 0) {
409*a9e8641dSBaptiste Daroussin 		free(flushfn);
410*a9e8641dSBaptiste Daroussin 		return (0);
411*a9e8641dSBaptiste Daroussin 	}
412*a9e8641dSBaptiste Daroussin 	free(flushfn);
413*a9e8641dSBaptiste Daroussin 	flushfn = NULL;
414*a9e8641dSBaptiste Daroussin 	if (gettimeofday(&now, 0) != 0)
415*a9e8641dSBaptiste Daroussin 		return (0);
416*a9e8641dSBaptiste Daroussin 
417*a9e8641dSBaptiste Daroussin 	/* Did the flush file get touched within the last period seconds? */
418*a9e8641dSBaptiste Daroussin 	if (st.st_mtim.tv_sec + period >= now.tv_sec)
419*a9e8641dSBaptiste Daroussin 		return (1);
420*a9e8641dSBaptiste Daroussin 	else
421*a9e8641dSBaptiste Daroussin 		return (0);
422*a9e8641dSBaptiste Daroussin }
423*a9e8641dSBaptiste Daroussin 
424*a9e8641dSBaptiste Daroussin int
425*a9e8641dSBaptiste Daroussin flushqueue_signal(void)
426*a9e8641dSBaptiste Daroussin {
427*a9e8641dSBaptiste Daroussin         char *flushfn = NULL;
428*a9e8641dSBaptiste Daroussin 	int fd;
429*a9e8641dSBaptiste Daroussin 
430*a9e8641dSBaptiste Daroussin         if (asprintf(&flushfn, "%s/%s", config.spooldir, SPOOL_FLUSHFILE) < 0)
431*a9e8641dSBaptiste Daroussin 		return (-1);
432*a9e8641dSBaptiste Daroussin 	fd = open(flushfn, O_CREAT|O_WRONLY|O_TRUNC, 0660);
433*a9e8641dSBaptiste Daroussin 	free(flushfn);
434*a9e8641dSBaptiste Daroussin 	if (fd < 0) {
435*a9e8641dSBaptiste Daroussin 		syslog(LOG_ERR, "could not open flush file: %m");
436*a9e8641dSBaptiste Daroussin 		return (-1);
437*a9e8641dSBaptiste Daroussin 	}
438*a9e8641dSBaptiste Daroussin         close(fd);
439*a9e8641dSBaptiste Daroussin 	return (0);
440*a9e8641dSBaptiste Daroussin }
441