summaryrefslogtreecommitdiffstats
path: root/usr.sbin/ctm/ctm_smail
diff options
context:
space:
mode:
authormckay <mckay@FreeBSD.org>1996-11-27 12:58:44 +0000
committermckay <mckay@FreeBSD.org>1996-11-27 12:58:44 +0000
commit20c91aa5ab613ab9a182e361d0e31a95b07dbf29 (patch)
treeea17d2eb86d67d6408a1d332bf90870049829aba /usr.sbin/ctm/ctm_smail
parent26c9e6ed97f85c4895662ed0de6ccb3c76c57c7a (diff)
downloadFreeBSD-src-20c91aa5ab613ab9a182e361d0e31a95b07dbf29.zip
FreeBSD-src-20c91aa5ab613ab9a182e361d0e31a95b07dbf29.tar.gz
Removed unnecessary locking. Simplified. Tidied.
Diffstat (limited to 'usr.sbin/ctm/ctm_smail')
-rw-r--r--usr.sbin/ctm/ctm_smail/ctm_smail.c486
1 files changed, 181 insertions, 305 deletions
diff --git a/usr.sbin/ctm/ctm_smail/ctm_smail.c b/usr.sbin/ctm/ctm_smail/ctm_smail.c
index 6be0a0d..56379eb 100644
--- a/usr.sbin/ctm/ctm_smail/ctm_smail.c
+++ b/usr.sbin/ctm/ctm_smail/ctm_smail.c
@@ -1,8 +1,9 @@
/*
* Send a compressed CTM delta to a recipient mailing list by encoding it
- * in safe ASCII characters, in mailer-friendly chunks, and passing it
- * to sendmail. The encoding is almost the same as MIME BASE64, and is
- * protected by a simple checksum.
+ * in safe ASCII characters, in mailer-friendly chunks, and passing them
+ * to sendmail. Optionally, the chunks can be queued to be sent later by
+ * ctm_dequeue in controlled bursts. The encoding is almost the same as
+ * MIME BASE64, and is protected by a simple checksum.
*
* Author: Stephen McKay
*
@@ -10,7 +11,7 @@
* In return you should think about all the nice people who give away software.
* Maybe you should write some free software too.
*
- * $Id: ctm_smail.c,v 1.7 1996/09/07 18:48:44 peter Exp $
+ * $Id: ctm_smail.c,v 1.8 1996/09/07 21:06:19 peter Exp $
*/
#include <stdio.h>
@@ -22,38 +23,42 @@
#include <sys/stat.h>
#include <errno.h>
#include <paths.h>
+#include <limits.h>
#include "error.h"
#include "options.h"
#define DEF_MAX_MSG 64000 /* Default maximum mail msg minus headers. */
-#define LINE_LENGTH 76 /* Chars per encode line. Divisible by 4. */
+#define LINE_LENGTH 76 /* Chars per encoded line. Divisible by 4. */
-void chop_and_send(char *delta, off_t ctm_size, long max_msg_size,
+int chop_and_send_or_queue(FILE *dfp, char *delta, off_t ctm_size,
+ long max_msg_size, char *mail_alias, char *queue_dir);
+int chop_and_send(FILE *dfp, char *delta, long msg_size, int npieces,
char *mail_alias);
-void chop_and_queue(char *delta, off_t ctm_size, long max_msg_size,
- char *queue_dir, char *mail_alias);
-unsigned encode_body(FILE *sm_fp, FILE *delta_fp, long msg_size);
+int chop_and_queue(FILE *dfp, char *delta, long msg_size, int npieces,
+ char *mail_alias, char *queue_dir);
+void clean_up_queue(char *queue_dir);
+int encode_body(FILE *sm_fp, FILE *delta_fp, long msg_size, unsigned *sum);
void write_header(FILE *sfp, char *mail_alias, char *delta, int pce,
int npieces);
void write_trailer(FILE *sfp, unsigned sum);
-void apologise(char *delta, off_t ctm_size, long max_ctm_size,
+int apologise(char *delta, off_t ctm_size, long max_ctm_size,
char *mail_alias);
FILE *open_sendmail(void);
int close_sendmail(FILE *fp);
-int lock_queuedir(char *queue_dir);
-void free_lock(int lockf, char *queue_dir);
-void add_to_queue(char *queue_dir, char *mail_alias, char *delta, int npieces, char **tempnames);
int
main(int argc, char **argv)
{
+ int status = 0;
char *delta_file;
char *mail_alias;
long max_msg_size = DEF_MAX_MSG;
long max_ctm_size = 0;
char *log_file = NULL;
char *queue_dir = NULL;
+ char *delta;
+ FILE *dfp;
struct stat sb;
err_prog_name(argv[0]);
@@ -74,43 +79,44 @@ main(int argc, char **argv)
delta_file = argv[1];
mail_alias = argv[2];
- if (stat(delta_file, &sb) < 0)
+ if ((delta = strrchr(delta_file, '/')) == NULL)
+ delta = delta_file;
+ else
+ delta++;
+
+ if ((dfp = fopen(delta_file, "r")) == NULL || fstat(fileno(dfp), &sb) < 0)
{
- err("%s: %s", delta_file, strerror(errno));
+ err("*%s", delta_file);
exit(1);
}
if (max_ctm_size != 0 && sb.st_size > max_ctm_size)
- apologise(delta_file, sb.st_size, max_ctm_size, mail_alias);
- else if (queue_dir == NULL)
- chop_and_send(delta_file, sb.st_size, max_msg_size, mail_alias);
+ status = apologise(delta, sb.st_size, max_ctm_size, mail_alias);
else
- chop_and_queue(delta_file, sb.st_size, max_msg_size, queue_dir, mail_alias);
+ status = chop_and_send_or_queue(dfp, delta, sb.st_size, max_msg_size,
+ mail_alias, queue_dir);
- return 0;
+ fclose(dfp);
+
+ return status;
}
/*
- * Carve our CTM delta into pieces, encode them, and send them.
+ * Carve our CTM delta into pieces, encode them, and send or queue them.
+ * Returns 0 on success, and 1 on failure.
*/
-void
-chop_and_send(char *delta, off_t ctm_size, long max_msg_size, char *mail_alias)
+int
+chop_and_send_or_queue(FILE *dfp, char *delta, off_t ctm_size,
+ long max_msg_size, char *mail_alias, char *queue_dir)
{
int npieces;
long msg_size;
long exp_size;
- int pce;
- FILE *sfp;
- FILE *dfp;
- unsigned sum;
- char *deltaname;
+ int status;
-#ifdef howmany
#undef howmany
-#endif
-
-#define howmany(x, y) (((x) + ((y) - 1)) / (y))
+#define howmany(x,y) (((x)+((y)-1)) / (y))
/*
* Work out how many pieces we need, bearing in mind that each piece
@@ -125,143 +131,160 @@ chop_and_send(char *delta, off_t ctm_size, long max_msg_size, char *mail_alias)
#undef howmany
- if ((dfp = fopen(delta, "r")) == NULL)
+ if (queue_dir == NULL)
+ status = chop_and_send(dfp, delta, msg_size, npieces, mail_alias);
+ else
{
- err("cannot open '%s' for reading.", delta);
- exit(1);
+ status = chop_and_queue(dfp, delta, msg_size, npieces, mail_alias,
+ queue_dir);
+ if (status)
+ clean_up_queue(queue_dir);
}
- deltaname = strrchr(delta, '/');
- if (deltaname)
- deltaname++; /* skip slash */
- else
- deltaname = delta;
+ return status;
+ }
+
+
+/*
+ * Carve our CTM delta into pieces, encode them, and send them.
+ * Returns 0 on success, and 1 on failure.
+ */
+int
+chop_and_send(FILE *dfp, char *delta, long msg_size, int npieces,
+ char *mail_alias)
+ {
+ int pce;
+ FILE *sfp;
+ unsigned sum;
+ /*
+ * Send each chunk directly to sendmail as it is generated.
+ * No temporary files necessary. If things turn ugly, we just
+ * have to live with the fact the we have sent only part of
+ * the delta.
+ */
for (pce = 1; pce <= npieces; pce++)
{
- sfp = open_sendmail();
- if (sfp == NULL)
- exit(1);
+ int read_error;
+
+ if ((sfp = open_sendmail()) == NULL)
+ return 1;
+
write_header(sfp, mail_alias, delta, pce, npieces);
- sum = encode_body(sfp, dfp, msg_size);
- write_trailer(sfp, sum);
- if (!close_sendmail(sfp))
- exit(1);
- err("%s %d/%d sent to %s", deltaname, pce, npieces, mail_alias);
+ read_error = encode_body(sfp, dfp, msg_size, &sum);
+ if (!read_error)
+ write_trailer(sfp, sum);
+
+ if (!close_sendmail(sfp) || read_error)
+ return 1;
+
+ err("%s %d/%d sent to %s", delta, pce, npieces, mail_alias);
}
- fclose(dfp);
+ return 0;
}
-/*
- * Carve our CTM delta into pieces, encode them, and drop them in the
- * queue dir.
- *
- * Basic algorythm:
- *
- * - for (each piece)
- * - gen. temp. file name (one which the de-queuer will ignore)
- * - record in array
- * - open temp. file
- * - encode delta (including headers) into the temp file
- * - close temp. file
- * - end
- * - lock queue directory
- * - foreach (temp. file)
- * - rename to the proper filename
- * - end
- * - unlock queue directory
- *
- * This is probably overkill, but it means that incomplete deltas
- * don't get mailed, and also reduces the window for lock races
- * between ctm_smail and the de-queueing process.
+
+/*
+ * Construct the tmp queue file name of a delta piece.
*/
+#define mk_tmp_name(fn,qd,p) \
+ sprintf((fn), "%s/.%08ld.%03d", (qd), (long)getpid(), (p))
-void
-chop_and_queue(char *delta, off_t ctm_size, long max_msg_size, char *queue_dir, char *mail_alias)
-{
- int npieces, pce, len;
- long msg_size, exp_size;
- FILE *sfp, *dfp;
- unsigned sum;
- char **tempnames, *tempnam, *sn;
+/*
+ * Construct the final queue file name of a delta piece.
+ */
+#define mk_queue_name(fn,qd,d,p,n) \
+ sprintf((fn), "%s/%s+%03d-%03d", (qd), (d), (p), (n))
-#define howmany(x, y) (((x) + ((y) - 1)) / (y))
+/*
+ * Carve our CTM delta into pieces, encode them, and queue them.
+ * Returns 0 on success, and 1 on failure.
+ */
+int
+chop_and_queue(FILE *dfp, char *delta, long msg_size, int npieces,
+ char *mail_alias, char *queue_dir)
+ {
+ int pce;
+ FILE *qfp;
+ unsigned sum;
+ char tname[PATH_MAX];
+ char qname[PATH_MAX];
/*
- * Work out how many pieces we need, bearing in mind that each piece
- * grows by 4/3 when encoded. We count the newlines too, but ignore
- * all mail headers and piece headers. They are a "small" (almost
- * constant) per message overhead that we make the user worry about. :-)
+ * Store each piece in the queue directory, but under temporary names,
+ * so that they can be deleted without unpleasant consequences if
+ * anything goes wrong. We could easily fill up a disk, for example.
*/
- exp_size = ctm_size * 4 / 3;
- exp_size += howmany(exp_size, LINE_LENGTH);
- npieces = howmany(exp_size, max_msg_size);
- msg_size = howmany(ctm_size, npieces);
+ for (pce = 1; pce <= npieces; pce++)
+ {
+ int write_error;
-#undef howmany
+ mk_tmp_name(tname, queue_dir, pce);
+ if ((qfp = fopen(tname, "w")) == NULL)
+ {
+ err("cannot open '%s' for writing", tname);
+ return 1;
+ }
- /*
- * allocate space for the array of filenames. Try to be portable
- * by not assuming anything to do with sizeof(char *)
- */
- tempnames = malloc(npieces * sizeof(char *));
- if (tempnames == NULL)
- {
- err("malloc for tempnames failed");
- exit(1);
- }
-
- len = strlen(queue_dir) + 16;
- tempnam = malloc(len);
- if (tempnam == NULL)
- {
- err("malloc for tempnames failed");
- exit(1);
- }
-
- if ((dfp = fopen(delta, "r")) == NULL)
- {
- err("cannot open '%s' for reading.", delta);
- exit(1);
- }
+ write_header(qfp, mail_alias, delta, pce, npieces);
+ if (encode_body(qfp, dfp, msg_size, &sum))
+ return 1;
+ write_trailer(qfp, sum);
- if ((sn = strrchr(delta, '/')) == NULL)
- sn = delta;
- else
- sn++;
+ fflush(qfp);
+ write_error = ferror(qfp);
+ fclose(qfp);
+ if (write_error)
+ {
+ err("error writing '%s'", tname);
+ return 1;
+ }
- for (pce = 1; pce <= npieces; pce++)
- {
- if (snprintf(tempnam, len, "%s/.%08d-%03d", queue_dir, getpid(), pce) >= len)
- err("Whoops! tempnam isn't long enough");
+ /*
+ * Give the warm success message now, instead of all in a rush
+ * during the rename phase.
+ */
+ err("%s %d/%d queued for %s", delta, pce, npieces, mail_alias);
+ }
- tempnames[pce - 1] = strdup(tempnam);
- if (tempnames[pce - 1] == NULL)
+ /*
+ * Rename the pieces into place. If an error occurs now, we are
+ * stuffed, but there is no neat way to back out. rename() should
+ * only fail now under extreme circumstances.
+ */
+ for (pce = 1; pce <= npieces; pce++)
{
- err("strdup failed for temp. filename");
- exit(1);
+ mk_tmp_name(tname, queue_dir, pce);
+ mk_queue_name(qname, queue_dir, delta, pce, npieces);
+ if (rename(tname, qname) < 0)
+ {
+ err("*rename: '%s' to '%s'", tname, qname);
+ unlink(tname);
+ }
}
- sfp = fopen(tempnam, "w");
- if (sfp == NULL)
- exit(1);
-
- write_header(sfp, mail_alias, delta, pce, npieces);
- sum = encode_body(sfp, dfp, msg_size);
- write_trailer(sfp, sum);
-
- if (fclose(sfp) != 0)
- exit(1);
-
- err("%s %d/%d created succesfully", sn, pce, npieces);
+ return 0;
}
- add_to_queue(queue_dir, mail_alias, delta, npieces, tempnames);
- fclose(dfp);
+/*
+ * There may be temporary files cluttering up the queue directory.
+ */
+void
+clean_up_queue(char *queue_dir)
+ {
+ int pce;
+ char tname[PATH_MAX];
-}
+ err("discarding queued delta pieces");
+ for (pce = 1; ; pce++)
+ {
+ mk_tmp_name(tname, queue_dir, pce);
+ if (unlink(tname) < 0)
+ break;
+ }
+ }
/*
@@ -288,8 +311,8 @@ static char to_b64[0x40] =
* of 4 characters encodes 3 input characters. Each output character encodes
* 6 bits. Thus 64 different characters are needed in this representation.
*/
-unsigned
-encode_body(FILE *sm_fp, FILE *delta_fp, long msg_size)
+int
+encode_body(FILE *sm_fp, FILE *delta_fp, long msg_size, unsigned *sum)
{
unsigned short cksum = 0xffff;
unsigned char *ip;
@@ -344,16 +367,12 @@ encode_body(FILE *sm_fp, FILE *delta_fp, long msg_size)
if (ferror(delta_fp))
{
err("error reading input file.");
- exit(1);
+ return 1;
}
- if (ferror(sm_fp))
- {
- err("error writing encoded file");
- exit(1);
- }
+ *sum = cksum;
- return cksum;
+ return 0;
}
@@ -363,18 +382,11 @@ encode_body(FILE *sm_fp, FILE *delta_fp, long msg_size)
void
write_header(FILE *sfp, char *mail_alias, char *delta, int pce, int npieces)
{
- char *sn;
-
- if ((sn = strrchr(delta, '/')) == NULL)
- sn = delta;
- else
- sn++;
-
fprintf(sfp, "From: owner-%s\n", mail_alias);
fprintf(sfp, "To: %s\n", mail_alias);
- fprintf(sfp, "Subject: ctm-mail %s %d/%d\n\n", sn, pce, npieces);
+ fprintf(sfp, "Subject: ctm-mail %s %d/%d\n\n", delta, pce, npieces);
- fprintf(sfp, "CTM_MAIL BEGIN %s %d %d\n", sn, pce, npieces);
+ fprintf(sfp, "CTM_MAIL BEGIN %s %d %d\n", delta, pce, npieces);
}
@@ -390,32 +402,30 @@ write_trailer(FILE *sfp, unsigned sum)
/*
* We're terribly sorry, but the delta is too big to send.
+ * Returns 0 on success, 1 on failure.
*/
-void
+int
apologise(char *delta, off_t ctm_size, long max_ctm_size, char *mail_alias)
{
FILE *sfp;
- char *sn;
sfp = open_sendmail();
if (sfp == NULL)
- exit(1);
-
- if ((sn = strrchr(delta, '/')) == NULL)
- sn = delta;
- else
- sn++;
+ return 1;
- fprintf(sfp, "From: %s-owner\n", mail_alias);
+ fprintf(sfp, "From: owner-%s\n", mail_alias);
fprintf(sfp, "To: %s\n", mail_alias);
- fprintf(sfp, "Subject: ctm-notice %s\n\n", sn);
+ fprintf(sfp, "Subject: ctm-notice %s\n\n", delta);
- fprintf(sfp, "%s is %ld bytes. The limit is %ld bytes.\n\n", sn,
+ fprintf(sfp, "%s is %ld bytes. The limit is %ld bytes.\n\n", delta,
(long)ctm_size, max_ctm_size);
- fprintf(sfp, "You can retrieve this delta via ftpmail, or your good mate at the university.\n");
+ fprintf(sfp, "You can retrieve this delta via ftpmail, "
+ "or your good mate at the university.\n");
if (!close_sendmail(sfp))
- exit(1);
+ return 1;
+
+ return 0;
}
@@ -457,137 +467,3 @@ close_sendmail(FILE *fp)
return (status == 0);
}
-
-/*
- * Lock the queuedir so we're the only guy messing about in there.
- */
-int
-lock_queuedir(char *queue_dir)
-{
- int fp, len;
- char *buffer;
- struct stat sb;
-
- len = strlen(queue_dir) + 8;
-
- buffer = malloc(len);
- if (buffer == NULL)
- {
- err("malloc failed in lock_queuedir");
- exit(1);
- }
-
- if (snprintf(buffer, len, "%s/.lock", queue_dir) >= len)
- err("Whoops. lock buffer too small in lock_queuedir");
-
- /*
- * We do our own lockfile scanning to avoid unlink races. 60
- * seconds should be enough to ensure that we won't get more races
- * happening between the stat and the open/flock.
- */
-
- while (stat(buffer, &sb) == 0)
- sleep(60);
-
- if ((fp = open(buffer, O_WRONLY | O_CREAT | O_EXLOCK, 0600)) < 0)
- {
- err("can't open `%s' in lock_queuedir", buffer);
- exit(1);
- }
-
- snprintf(buffer, len, "%8ld", getpid());
- write(fp, buffer, 8);
-
- free(buffer);
-
- return(fp);
-}
-
-/*
- * Lock the queuedir so we're the only guy messing about in there.
- */
-void
-free_lock(int lockf, char *queue_dir)
-{
- int len;
- char *path;
-
- /*
- * Most important: free the lock before we do anything else!
- */
-
- close(lockf);
-
- len = strlen(queue_dir) + 7;
-
- path = malloc(len);
- if (path == NULL)
- {
- err("malloc failed in free_lock");
- exit(1);
- }
-
- if (snprintf(path, len, "%s/.lock", queue_dir) >= len)
- err("lock path buffer too small in free_lock");
-
- if (unlink(path) != 0)
- {
- err("can't unlink lockfile `%s'", path);
- exit(1);
- }
-
- free(path);
-}
-
-/* move everything into the queue directory. */
-
-void
-add_to_queue(char *queue_dir, char *mail_alias, char *delta, int npieces, char **tempnames)
-{
- char *queuefile, *sn;
- int pce, len, lockf;
-
- if ((sn = strrchr(delta, '/')) == NULL)
- sn = delta;
- else
- sn++;
-
- /* try to malloc all we need BEFORE entering the lock loop */
-
- len = strlen(queue_dir) + strlen(sn) + 7;
- queuefile = malloc(len);
- if (queuefile == NULL)
- {
- err("can't malloc for queuefile");
- exit(1);
- }
-
- /*
- * We should be the only process mucking around in the queue
- * directory while we add the new queue files ... it could be
- * awkward if the de-queue process starts it's job while we're
- * adding files ...
- */
-
- lockf = lock_queuedir(queue_dir);
- for (pce = 0; pce < npieces; pce++)
- {
- struct stat sb;
-
- if (snprintf(queuefile, len, "%s/%s+%03d", queue_dir, sn, pce + 1) >= len)
- err("whoops, queuefile buffer is too small");
-
- if (stat(queuefile, &sb) == 0)
- {
- err("WOAH! Queue file `%s' already exists! Bailing out.", queuefile);
- free_lock(lockf, queue_dir);
- exit(1);
- }
-
- rename(tempnames[pce], queuefile);
- }
-
- free_lock(lockf, queue_dir);
-
- free(queuefile);
-}
OpenPOWER on IntegriCloud