diff options
author | mckay <mckay@FreeBSD.org> | 1996-11-27 12:58:44 +0000 |
---|---|---|
committer | mckay <mckay@FreeBSD.org> | 1996-11-27 12:58:44 +0000 |
commit | 20c91aa5ab613ab9a182e361d0e31a95b07dbf29 (patch) | |
tree | ea17d2eb86d67d6408a1d332bf90870049829aba /usr.sbin/ctm | |
parent | 26c9e6ed97f85c4895662ed0de6ccb3c76c57c7a (diff) | |
download | FreeBSD-src-20c91aa5ab613ab9a182e361d0e31a95b07dbf29.zip FreeBSD-src-20c91aa5ab613ab9a182e361d0e31a95b07dbf29.tar.gz |
Removed unnecessary locking. Simplified. Tidied.
Diffstat (limited to 'usr.sbin/ctm')
-rw-r--r-- | usr.sbin/ctm/ctm_smail/ctm_smail.c | 486 |
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); -} |