Deleted Added
full compact
ctm_smail.c (18120) ctm_smail.c (19983)
1/*
2 * Send a compressed CTM delta to a recipient mailing list by encoding it
1/*
2 * Send a compressed CTM delta to a recipient mailing list by encoding it
3 * in safe ASCII characters, in mailer-friendly chunks, and passing it
4 * to sendmail. The encoding is almost the same as MIME BASE64, and is
5 * protected by a simple checksum.
3 * in safe ASCII characters, in mailer-friendly chunks, and passing them
4 * to sendmail. Optionally, the chunks can be queued to be sent later by
5 * ctm_dequeue in controlled bursts. The encoding is almost the same as
6 * MIME BASE64, and is protected by a simple checksum.
6 *
7 * Author: Stephen McKay
8 *
9 * NOTICE: This is free software. I hope you get some use from this program.
10 * In return you should think about all the nice people who give away software.
11 * Maybe you should write some free software too.
12 *
7 *
8 * Author: Stephen McKay
9 *
10 * NOTICE: This is free software. I hope you get some use from this program.
11 * In return you should think about all the nice people who give away software.
12 * Maybe you should write some free software too.
13 *
13 * $Id: ctm_smail.c,v 1.7 1996/09/07 18:48:44 peter Exp $
14 * $Id: ctm_smail.c,v 1.8 1996/09/07 21:06:19 peter Exp $
14 */
15
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <unistd.h>
20#include <fcntl.h>
21#include <sys/types.h>
22#include <sys/stat.h>
23#include <errno.h>
24#include <paths.h>
15 */
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <unistd.h>
21#include <fcntl.h>
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <errno.h>
25#include <paths.h>
26#include <limits.h>
25#include "error.h"
26#include "options.h"
27
28#define DEF_MAX_MSG 64000 /* Default maximum mail msg minus headers. */
29
27#include "error.h"
28#include "options.h"
29
30#define DEF_MAX_MSG 64000 /* Default maximum mail msg minus headers. */
31
30#define LINE_LENGTH 76 /* Chars per encode line. Divisible by 4. */
32#define LINE_LENGTH 76 /* Chars per encoded line. Divisible by 4. */
31
33
32void chop_and_send(char *delta, off_t ctm_size, long max_msg_size,
34int chop_and_send_or_queue(FILE *dfp, char *delta, off_t ctm_size,
35 long max_msg_size, char *mail_alias, char *queue_dir);
36int chop_and_send(FILE *dfp, char *delta, long msg_size, int npieces,
33 char *mail_alias);
37 char *mail_alias);
34void chop_and_queue(char *delta, off_t ctm_size, long max_msg_size,
35 char *queue_dir, char *mail_alias);
36unsigned encode_body(FILE *sm_fp, FILE *delta_fp, long msg_size);
38int chop_and_queue(FILE *dfp, char *delta, long msg_size, int npieces,
39 char *mail_alias, char *queue_dir);
40void clean_up_queue(char *queue_dir);
41int encode_body(FILE *sm_fp, FILE *delta_fp, long msg_size, unsigned *sum);
37void write_header(FILE *sfp, char *mail_alias, char *delta, int pce,
38 int npieces);
39void write_trailer(FILE *sfp, unsigned sum);
42void write_header(FILE *sfp, char *mail_alias, char *delta, int pce,
43 int npieces);
44void write_trailer(FILE *sfp, unsigned sum);
40void apologise(char *delta, off_t ctm_size, long max_ctm_size,
45int apologise(char *delta, off_t ctm_size, long max_ctm_size,
41 char *mail_alias);
42FILE *open_sendmail(void);
43int close_sendmail(FILE *fp);
46 char *mail_alias);
47FILE *open_sendmail(void);
48int close_sendmail(FILE *fp);
44int lock_queuedir(char *queue_dir);
45void free_lock(int lockf, char *queue_dir);
46void add_to_queue(char *queue_dir, char *mail_alias, char *delta, int npieces, char **tempnames);
47
48int
49main(int argc, char **argv)
50 {
49
50int
51main(int argc, char **argv)
52 {
53 int status = 0;
51 char *delta_file;
52 char *mail_alias;
53 long max_msg_size = DEF_MAX_MSG;
54 long max_ctm_size = 0;
55 char *log_file = NULL;
56 char *queue_dir = NULL;
54 char *delta_file;
55 char *mail_alias;
56 long max_msg_size = DEF_MAX_MSG;
57 long max_ctm_size = 0;
58 char *log_file = NULL;
59 char *queue_dir = NULL;
60 char *delta;
61 FILE *dfp;
57 struct stat sb;
58
59 err_prog_name(argv[0]);
60
61 OPTIONS("[-l log] [-m maxmsgsize] [-c maxctmsize] [-q queuedir] ctm-delta mail-alias")
62 NUMBER('m', max_msg_size)
63 NUMBER('c', max_ctm_size)
64 STRING('l', log_file)

--- 4 unchanged lines hidden (view full) ---

69 usage();
70
71 if (log_file != NULL)
72 err_set_log(log_file);
73
74 delta_file = argv[1];
75 mail_alias = argv[2];
76
62 struct stat sb;
63
64 err_prog_name(argv[0]);
65
66 OPTIONS("[-l log] [-m maxmsgsize] [-c maxctmsize] [-q queuedir] ctm-delta mail-alias")
67 NUMBER('m', max_msg_size)
68 NUMBER('c', max_ctm_size)
69 STRING('l', log_file)

--- 4 unchanged lines hidden (view full) ---

74 usage();
75
76 if (log_file != NULL)
77 err_set_log(log_file);
78
79 delta_file = argv[1];
80 mail_alias = argv[2];
81
77 if (stat(delta_file, &sb) < 0)
82 if ((delta = strrchr(delta_file, '/')) == NULL)
83 delta = delta_file;
84 else
85 delta++;
86
87 if ((dfp = fopen(delta_file, "r")) == NULL || fstat(fileno(dfp), &sb) < 0)
78 {
88 {
79 err("%s: %s", delta_file, strerror(errno));
89 err("*%s", delta_file);
80 exit(1);
81 }
82
83 if (max_ctm_size != 0 && sb.st_size > max_ctm_size)
90 exit(1);
91 }
92
93 if (max_ctm_size != 0 && sb.st_size > max_ctm_size)
84 apologise(delta_file, sb.st_size, max_ctm_size, mail_alias);
85 else if (queue_dir == NULL)
86 chop_and_send(delta_file, sb.st_size, max_msg_size, mail_alias);
94 status = apologise(delta, sb.st_size, max_ctm_size, mail_alias);
87 else
95 else
88 chop_and_queue(delta_file, sb.st_size, max_msg_size, queue_dir, mail_alias);
96 status = chop_and_send_or_queue(dfp, delta, sb.st_size, max_msg_size,
97 mail_alias, queue_dir);
89
98
90 return 0;
99 fclose(dfp);
100
101 return status;
91 }
92
93
94/*
102 }
103
104
105/*
95 * Carve our CTM delta into pieces, encode them, and send them.
106 * Carve our CTM delta into pieces, encode them, and send or queue them.
107 * Returns 0 on success, and 1 on failure.
96 */
108 */
97void
98chop_and_send(char *delta, off_t ctm_size, long max_msg_size, char *mail_alias)
109int
110chop_and_send_or_queue(FILE *dfp, char *delta, off_t ctm_size,
111 long max_msg_size, char *mail_alias, char *queue_dir)
99 {
100 int npieces;
101 long msg_size;
102 long exp_size;
112 {
113 int npieces;
114 long msg_size;
115 long exp_size;
103 int pce;
104 FILE *sfp;
105 FILE *dfp;
106 unsigned sum;
107 char *deltaname;
116 int status;
108
117
109#ifdef howmany
110#undef howmany
118#undef howmany
111#endif
119#define howmany(x,y) (((x)+((y)-1)) / (y))
112
120
113#define howmany(x, y) (((x) + ((y) - 1)) / (y))
114
115 /*
116 * Work out how many pieces we need, bearing in mind that each piece
117 * grows by 4/3 when encoded. We count the newlines too, but ignore
118 * all mail headers and piece headers. They are a "small" (almost
119 * constant) per message overhead that we make the user worry about. :-)
120 */
121 exp_size = ctm_size * 4 / 3;
122 exp_size += howmany(exp_size, LINE_LENGTH);
123 npieces = howmany(exp_size, max_msg_size);
124 msg_size = howmany(ctm_size, npieces);
125
126#undef howmany
127
121 /*
122 * Work out how many pieces we need, bearing in mind that each piece
123 * grows by 4/3 when encoded. We count the newlines too, but ignore
124 * all mail headers and piece headers. They are a "small" (almost
125 * constant) per message overhead that we make the user worry about. :-)
126 */
127 exp_size = ctm_size * 4 / 3;
128 exp_size += howmany(exp_size, LINE_LENGTH);
129 npieces = howmany(exp_size, max_msg_size);
130 msg_size = howmany(ctm_size, npieces);
131
132#undef howmany
133
128 if ((dfp = fopen(delta, "r")) == NULL)
134 if (queue_dir == NULL)
135 status = chop_and_send(dfp, delta, msg_size, npieces, mail_alias);
136 else
129 {
137 {
130 err("cannot open '%s' for reading.", delta);
131 exit(1);
138 status = chop_and_queue(dfp, delta, msg_size, npieces, mail_alias,
139 queue_dir);
140 if (status)
141 clean_up_queue(queue_dir);
132 }
133
142 }
143
134 deltaname = strrchr(delta, '/');
135 if (deltaname)
136 deltaname++; /* skip slash */
137 else
138 deltaname = delta;
144 return status;
145 }
139
146
147
148/*
149 * Carve our CTM delta into pieces, encode them, and send them.
150 * Returns 0 on success, and 1 on failure.
151 */
152int
153chop_and_send(FILE *dfp, char *delta, long msg_size, int npieces,
154 char *mail_alias)
155 {
156 int pce;
157 FILE *sfp;
158 unsigned sum;
159
160 /*
161 * Send each chunk directly to sendmail as it is generated.
162 * No temporary files necessary. If things turn ugly, we just
163 * have to live with the fact the we have sent only part of
164 * the delta.
165 */
140 for (pce = 1; pce <= npieces; pce++)
141 {
166 for (pce = 1; pce <= npieces; pce++)
167 {
142 sfp = open_sendmail();
143 if (sfp == NULL)
144 exit(1);
168 int read_error;
169
170 if ((sfp = open_sendmail()) == NULL)
171 return 1;
172
145 write_header(sfp, mail_alias, delta, pce, npieces);
173 write_header(sfp, mail_alias, delta, pce, npieces);
146 sum = encode_body(sfp, dfp, msg_size);
147 write_trailer(sfp, sum);
148 if (!close_sendmail(sfp))
149 exit(1);
150 err("%s %d/%d sent to %s", deltaname, pce, npieces, mail_alias);
174 read_error = encode_body(sfp, dfp, msg_size, &sum);
175 if (!read_error)
176 write_trailer(sfp, sum);
177
178 if (!close_sendmail(sfp) || read_error)
179 return 1;
180
181 err("%s %d/%d sent to %s", delta, pce, npieces, mail_alias);
151 }
152
182 }
183
153 fclose(dfp);
184 return 0;
154 }
155
185 }
186
156/*
157 * Carve our CTM delta into pieces, encode them, and drop them in the
158 * queue dir.
159 *
160 * Basic algorythm:
161 *
162 * - for (each piece)
163 * - gen. temp. file name (one which the de-queuer will ignore)
164 * - record in array
165 * - open temp. file
166 * - encode delta (including headers) into the temp file
167 * - close temp. file
168 * - end
169 * - lock queue directory
170 * - foreach (temp. file)
171 * - rename to the proper filename
172 * - end
173 * - unlock queue directory
174 *
175 * This is probably overkill, but it means that incomplete deltas
176 * don't get mailed, and also reduces the window for lock races
177 * between ctm_smail and the de-queueing process.
187
188/*
189 * Construct the tmp queue file name of a delta piece.
178 */
190 */
191#define mk_tmp_name(fn,qd,p) \
192 sprintf((fn), "%s/.%08ld.%03d", (qd), (long)getpid(), (p))
179
193
180void
181chop_and_queue(char *delta, off_t ctm_size, long max_msg_size, char *queue_dir, char *mail_alias)
182{
183 int npieces, pce, len;
184 long msg_size, exp_size;
185 FILE *sfp, *dfp;
194/*
195 * Construct the final queue file name of a delta piece.
196 */
197#define mk_queue_name(fn,qd,d,p,n) \
198 sprintf((fn), "%s/%s+%03d-%03d", (qd), (d), (p), (n))
199
200/*
201 * Carve our CTM delta into pieces, encode them, and queue them.
202 * Returns 0 on success, and 1 on failure.
203 */
204int
205chop_and_queue(FILE *dfp, char *delta, long msg_size, int npieces,
206 char *mail_alias, char *queue_dir)
207 {
208 int pce;
209 FILE *qfp;
186 unsigned sum;
210 unsigned sum;
187 char **tempnames, *tempnam, *sn;
211 char tname[PATH_MAX];
212 char qname[PATH_MAX];
188
213
189#define howmany(x, y) (((x) + ((y) - 1)) / (y))
190
191 /*
214 /*
192 * Work out how many pieces we need, bearing in mind that each piece
193 * grows by 4/3 when encoded. We count the newlines too, but ignore
194 * all mail headers and piece headers. They are a "small" (almost
195 * constant) per message overhead that we make the user worry about. :-)
215 * Store each piece in the queue directory, but under temporary names,
216 * so that they can be deleted without unpleasant consequences if
217 * anything goes wrong. We could easily fill up a disk, for example.
196 */
218 */
197 exp_size = ctm_size * 4 / 3;
198 exp_size += howmany(exp_size, LINE_LENGTH);
199 npieces = howmany(exp_size, max_msg_size);
200 msg_size = howmany(ctm_size, npieces);
219 for (pce = 1; pce <= npieces; pce++)
220 {
221 int write_error;
201
222
202#undef howmany
223 mk_tmp_name(tname, queue_dir, pce);
224 if ((qfp = fopen(tname, "w")) == NULL)
225 {
226 err("cannot open '%s' for writing", tname);
227 return 1;
228 }
203
229
230 write_header(qfp, mail_alias, delta, pce, npieces);
231 if (encode_body(qfp, dfp, msg_size, &sum))
232 return 1;
233 write_trailer(qfp, sum);
234
235 fflush(qfp);
236 write_error = ferror(qfp);
237 fclose(qfp);
238 if (write_error)
239 {
240 err("error writing '%s'", tname);
241 return 1;
242 }
243
244 /*
245 * Give the warm success message now, instead of all in a rush
246 * during the rename phase.
247 */
248 err("%s %d/%d queued for %s", delta, pce, npieces, mail_alias);
249 }
250
204 /*
251 /*
205 * allocate space for the array of filenames. Try to be portable
206 * by not assuming anything to do with sizeof(char *)
252 * Rename the pieces into place. If an error occurs now, we are
253 * stuffed, but there is no neat way to back out. rename() should
254 * only fail now under extreme circumstances.
207 */
255 */
208 tempnames = malloc(npieces * sizeof(char *));
209 if (tempnames == NULL)
210 {
211 err("malloc for tempnames failed");
212 exit(1);
256 for (pce = 1; pce <= npieces; pce++)
257 {
258 mk_tmp_name(tname, queue_dir, pce);
259 mk_queue_name(qname, queue_dir, delta, pce, npieces);
260 if (rename(tname, qname) < 0)
261 {
262 err("*rename: '%s' to '%s'", tname, qname);
263 unlink(tname);
264 }
265 }
266
267 return 0;
213 }
268 }
214
215 len = strlen(queue_dir) + 16;
216 tempnam = malloc(len);
217 if (tempnam == NULL)
218 {
219 err("malloc for tempnames failed");
220 exit(1);
221 }
222
223 if ((dfp = fopen(delta, "r")) == NULL)
224 {
225 err("cannot open '%s' for reading.", delta);
226 exit(1);
227 }
228
269
229 if ((sn = strrchr(delta, '/')) == NULL)
230 sn = delta;
231 else
232 sn++;
233
270
234 for (pce = 1; pce <= npieces; pce++)
271/*
272 * There may be temporary files cluttering up the queue directory.
273 */
274void
275clean_up_queue(char *queue_dir)
235 {
276 {
236 if (snprintf(tempnam, len, "%s/.%08d-%03d", queue_dir, getpid(), pce) >= len)
237 err("Whoops! tempnam isn't long enough");
277 int pce;
278 char tname[PATH_MAX];
238
279
239 tempnames[pce - 1] = strdup(tempnam);
240 if (tempnames[pce - 1] == NULL)
280 err("discarding queued delta pieces");
281 for (pce = 1; ; pce++)
241 {
282 {
242 err("strdup failed for temp. filename");
243 exit(1);
283 mk_tmp_name(tname, queue_dir, pce);
284 if (unlink(tname) < 0)
285 break;
244 }
286 }
245
246 sfp = fopen(tempnam, "w");
247 if (sfp == NULL)
248 exit(1);
249
250 write_header(sfp, mail_alias, delta, pce, npieces);
251 sum = encode_body(sfp, dfp, msg_size);
252 write_trailer(sfp, sum);
253
254 if (fclose(sfp) != 0)
255 exit(1);
256
257 err("%s %d/%d created succesfully", sn, pce, npieces);
258 }
259
287 }
288
260 add_to_queue(queue_dir, mail_alias, delta, npieces, tempnames);
261
289
262 fclose(dfp);
263
264}
265
266
267/*
268 * MIME BASE64 encode table.
269 */
270static char to_b64[0x40] =
271 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
272
273/*
274 * This cheap plastic checksum effectively rotates our checksum-so-far

--- 8 unchanged lines hidden (view full) ---

283 *
284 * Characters are read from delta_fp and encoded characters are written
285 * to sm_fp. At most 'msg_size' characters should be read from delta_fp.
286 *
287 * The body consists of lines of up to LINE_LENGTH characters. Each group
288 * of 4 characters encodes 3 input characters. Each output character encodes
289 * 6 bits. Thus 64 different characters are needed in this representation.
290 */
290/*
291 * MIME BASE64 encode table.
292 */
293static char to_b64[0x40] =
294 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
295
296/*
297 * This cheap plastic checksum effectively rotates our checksum-so-far

--- 8 unchanged lines hidden (view full) ---

306 *
307 * Characters are read from delta_fp and encoded characters are written
308 * to sm_fp. At most 'msg_size' characters should be read from delta_fp.
309 *
310 * The body consists of lines of up to LINE_LENGTH characters. Each group
311 * of 4 characters encodes 3 input characters. Each output character encodes
312 * 6 bits. Thus 64 different characters are needed in this representation.
313 */
291unsigned
292encode_body(FILE *sm_fp, FILE *delta_fp, long msg_size)
314int
315encode_body(FILE *sm_fp, FILE *delta_fp, long msg_size, unsigned *sum)
293 {
294 unsigned short cksum = 0xffff;
295 unsigned char *ip;
296 char *op;
297 int want, n, i;
298 unsigned char inbuf[LINE_LENGTH*3/4];
299 char outbuf[LINE_LENGTH+1];
300

--- 38 unchanged lines hidden (view full) ---

339 }
340 *op++ = '\n';
341 fwrite(outbuf, sizeof(char), op - outbuf, sm_fp);
342 }
343
344 if (ferror(delta_fp))
345 {
346 err("error reading input file.");
316 {
317 unsigned short cksum = 0xffff;
318 unsigned char *ip;
319 char *op;
320 int want, n, i;
321 unsigned char inbuf[LINE_LENGTH*3/4];
322 char outbuf[LINE_LENGTH+1];
323

--- 38 unchanged lines hidden (view full) ---

362 }
363 *op++ = '\n';
364 fwrite(outbuf, sizeof(char), op - outbuf, sm_fp);
365 }
366
367 if (ferror(delta_fp))
368 {
369 err("error reading input file.");
347 exit(1);
370 return 1;
348 }
349
371 }
372
350 if (ferror(sm_fp))
351 {
352 err("error writing encoded file");
353 exit(1);
354 }
373 *sum = cksum;
355
374
356 return cksum;
375 return 0;
357 }
358
359
360/*
361 * Write the mail header and data header.
362 */
363void
364write_header(FILE *sfp, char *mail_alias, char *delta, int pce, int npieces)
365 {
376 }
377
378
379/*
380 * Write the mail header and data header.
381 */
382void
383write_header(FILE *sfp, char *mail_alias, char *delta, int pce, int npieces)
384 {
366 char *sn;
367
368 if ((sn = strrchr(delta, '/')) == NULL)
369 sn = delta;
370 else
371 sn++;
372
373 fprintf(sfp, "From: owner-%s\n", mail_alias);
374 fprintf(sfp, "To: %s\n", mail_alias);
385 fprintf(sfp, "From: owner-%s\n", mail_alias);
386 fprintf(sfp, "To: %s\n", mail_alias);
375 fprintf(sfp, "Subject: ctm-mail %s %d/%d\n\n", sn, pce, npieces);
387 fprintf(sfp, "Subject: ctm-mail %s %d/%d\n\n", delta, pce, npieces);
376
388
377 fprintf(sfp, "CTM_MAIL BEGIN %s %d %d\n", sn, pce, npieces);
389 fprintf(sfp, "CTM_MAIL BEGIN %s %d %d\n", delta, pce, npieces);
378 }
379
380
381/*
382 * Write the data trailer.
383 */
384void
385write_trailer(FILE *sfp, unsigned sum)
386 {
387 fprintf(sfp, "CTM_MAIL END %ld\n", (long)sum);
388 }
389
390
391/*
392 * We're terribly sorry, but the delta is too big to send.
390 }
391
392
393/*
394 * Write the data trailer.
395 */
396void
397write_trailer(FILE *sfp, unsigned sum)
398 {
399 fprintf(sfp, "CTM_MAIL END %ld\n", (long)sum);
400 }
401
402
403/*
404 * We're terribly sorry, but the delta is too big to send.
405 * Returns 0 on success, 1 on failure.
393 */
406 */
394void
407int
395apologise(char *delta, off_t ctm_size, long max_ctm_size, char *mail_alias)
396 {
397 FILE *sfp;
408apologise(char *delta, off_t ctm_size, long max_ctm_size, char *mail_alias)
409 {
410 FILE *sfp;
398 char *sn;
399
400 sfp = open_sendmail();
401 if (sfp == NULL)
411
412 sfp = open_sendmail();
413 if (sfp == NULL)
402 exit(1);
414 return 1;
403
415
404 if ((sn = strrchr(delta, '/')) == NULL)
405 sn = delta;
406 else
407 sn++;
408
409 fprintf(sfp, "From: %s-owner\n", mail_alias);
416 fprintf(sfp, "From: owner-%s\n", mail_alias);
410 fprintf(sfp, "To: %s\n", mail_alias);
417 fprintf(sfp, "To: %s\n", mail_alias);
411 fprintf(sfp, "Subject: ctm-notice %s\n\n", sn);
418 fprintf(sfp, "Subject: ctm-notice %s\n\n", delta);
412
419
413 fprintf(sfp, "%s is %ld bytes. The limit is %ld bytes.\n\n", sn,
420 fprintf(sfp, "%s is %ld bytes. The limit is %ld bytes.\n\n", delta,
414 (long)ctm_size, max_ctm_size);
421 (long)ctm_size, max_ctm_size);
415 fprintf(sfp, "You can retrieve this delta via ftpmail, or your good mate at the university.\n");
422 fprintf(sfp, "You can retrieve this delta via ftpmail, "
423 "or your good mate at the university.\n");
416
417 if (!close_sendmail(sfp))
424
425 if (!close_sendmail(sfp))
418 exit(1);
426 return 1;
427
428 return 0;
419 }
420
421
422/*
423 * Start a pipe to sendmail. Sendmail will decode the destination
424 * from the message contents.
425 */
426FILE *

--- 25 unchanged lines hidden (view full) ---

452 return 0;
453 }
454
455 if ((status = pclose(fp)) != 0)
456 err("sendmail failed with status %d", status);
457
458 return (status == 0);
459 }
429 }
430
431
432/*
433 * Start a pipe to sendmail. Sendmail will decode the destination
434 * from the message contents.
435 */
436FILE *

--- 25 unchanged lines hidden (view full) ---

462 return 0;
463 }
464
465 if ((status = pclose(fp)) != 0)
466 err("sendmail failed with status %d", status);
467
468 return (status == 0);
469 }
460
461/*
462 * Lock the queuedir so we're the only guy messing about in there.
463 */
464int
465lock_queuedir(char *queue_dir)
466{
467 int fp, len;
468 char *buffer;
469 struct stat sb;
470
471 len = strlen(queue_dir) + 8;
472
473 buffer = malloc(len);
474 if (buffer == NULL)
475 {
476 err("malloc failed in lock_queuedir");
477 exit(1);
478 }
479
480 if (snprintf(buffer, len, "%s/.lock", queue_dir) >= len)
481 err("Whoops. lock buffer too small in lock_queuedir");
482
483 /*
484 * We do our own lockfile scanning to avoid unlink races. 60
485 * seconds should be enough to ensure that we won't get more races
486 * happening between the stat and the open/flock.
487 */
488
489 while (stat(buffer, &sb) == 0)
490 sleep(60);
491
492 if ((fp = open(buffer, O_WRONLY | O_CREAT | O_EXLOCK, 0600)) < 0)
493 {
494 err("can't open `%s' in lock_queuedir", buffer);
495 exit(1);
496 }
497
498 snprintf(buffer, len, "%8ld", getpid());
499 write(fp, buffer, 8);
500
501 free(buffer);
502
503 return(fp);
504}
505
506/*
507 * Lock the queuedir so we're the only guy messing about in there.
508 */
509void
510free_lock(int lockf, char *queue_dir)
511{
512 int len;
513 char *path;
514
515 /*
516 * Most important: free the lock before we do anything else!
517 */
518
519 close(lockf);
520
521 len = strlen(queue_dir) + 7;
522
523 path = malloc(len);
524 if (path == NULL)
525 {
526 err("malloc failed in free_lock");
527 exit(1);
528 }
529
530 if (snprintf(path, len, "%s/.lock", queue_dir) >= len)
531 err("lock path buffer too small in free_lock");
532
533 if (unlink(path) != 0)
534 {
535 err("can't unlink lockfile `%s'", path);
536 exit(1);
537 }
538
539 free(path);
540}
541
542/* move everything into the queue directory. */
543
544void
545add_to_queue(char *queue_dir, char *mail_alias, char *delta, int npieces, char **tempnames)
546{
547 char *queuefile, *sn;
548 int pce, len, lockf;
549
550 if ((sn = strrchr(delta, '/')) == NULL)
551 sn = delta;
552 else
553 sn++;
554
555 /* try to malloc all we need BEFORE entering the lock loop */
556
557 len = strlen(queue_dir) + strlen(sn) + 7;
558 queuefile = malloc(len);
559 if (queuefile == NULL)
560 {
561 err("can't malloc for queuefile");
562 exit(1);
563 }
564
565 /*
566 * We should be the only process mucking around in the queue
567 * directory while we add the new queue files ... it could be
568 * awkward if the de-queue process starts it's job while we're
569 * adding files ...
570 */
571
572 lockf = lock_queuedir(queue_dir);
573 for (pce = 0; pce < npieces; pce++)
574 {
575 struct stat sb;
576
577 if (snprintf(queuefile, len, "%s/%s+%03d", queue_dir, sn, pce + 1) >= len)
578 err("whoops, queuefile buffer is too small");
579
580 if (stat(queuefile, &sb) == 0)
581 {
582 err("WOAH! Queue file `%s' already exists! Bailing out.", queuefile);
583 free_lock(lockf, queue_dir);
584 exit(1);
585 }
586
587 rename(tempnames[pce], queuefile);
588 }
589
590 free_lock(lockf, queue_dir);
591
592 free(queuefile);
593}