jim (01/04/83)
I have noticed several bugs in mail and rmail as delivered with 4.1bsd. In rmail.c, where the command line for delivermail is built, we have: sprintf(cmd, "%s -r%s %s", MAILER, from, to); I think this should be: sprintf(cmd, "%s -em -i -r%s %s", MAILER, from, to); The "-em" says to mail back errors. Without this, errors seem to get mailed back anyway, I assume because delivermail is smart enough to realize that the mail is coming from rmail. Sometimes the returned mail is missing the line that says why the mail was returned, and I think this is because the "-em" is missing, but I haven't verified this. The "-i" flag tells delivermail to ignore lines containing just a single ".". In mail.c, after uucp mail is handed off to uux, we have the curious combination: pclose(rmf); exit(0); What this does is ignore any errors from uux. Our uux returns 0 for no errors, 1 for system type failures, and 101 (where did this number come from?) if the host is not in the L.sys file. It seems to me that a better exit from mail would be something like: #include <sysexits.h> ... switch (pclose(rmf)) { case 0: /* Normal exit */ exit(0); case (101 << 8): /* Unknown host */ exit(EX_NOHOST); default: /* Unknown error from uux */ exit(EX_OSERR); Better yet, fix uux so it returns EX_* codes, and have mail.c do: exit(pclose(rmf) >> 8); Before you run out with editor in hand to do battle with the sources, I should mention that I have not tried this change. I discovered the problem when I noticed that our local version of the uucp mailer (we don't use /bin/mail directly for this) returns mail addressed to unknown hosts, but that /bin/mail just drops it. Does anyone know why the handling of exit codes in mail.c is so sloppy? Eric Allman (who wrote delivermail) seems to be much too careful to allow something like this to happen.
dmmartindale (01/04/83)
Several comments about uw-beaver!jim's suggestions on how to handle errors in /bin/mail: Do NOT change /bin/mail to do "exit(pclose(rmf) >> 8);" If uux dies from any sort of signal, this fact is indicated by the lower 8 bits of the return status and the upper 8 bits are zero, and you DON'T want to indicate normal exit in this case. The switch statement he suggested is much better. Unfortunately, it still won't return the proper exit code to delivermail, since this exit is from a forked child of /bin/mail, and is waited for by the parent who only checks the status against zero (look at the wait() not far above the exit()). Considerably more work would be required to have the parent pass back the child's exit status. Part of the problem is that /bin/mail is written to handle multiple recipients so it can't pass back a return code indicating what happened for each of them, but delivermail makes one call of the deliverer for each recipient and wants a meaningful code back. Anyway, I'm currently running code which does: exit(pclose(rmf) != 0); This at least causes delivermail to save a copy of the letter or mail it back, whichever is appropriate. While you're attacking /bin/mail, you should also wrap a "#ifndef DELIVERMAIL" around the code which saves mail in dead.letter, as: #ifndef DELIVERMAIL if (error) { setuid(getuid()); malf = fopen(dead, "w"); if (malf == NULL) { fprintf(stdout, "mail: cannot open %s\n", dead); fclose(tmpf); return; } copylet(0, malf, ZAP); fclose(malf); fprintf(stdout, "Mail saved in %s\n", dead); } #endif This allows delivermail to handle undeliverable mail, since it's cleverer about what to do with it. Once you've fixed mail to return the result of pclose() properly you will sometimes get two copies of a letter saved in dead.letter (for local mail) because both /bin/mail and delivermail save it. Also, /bin/mail has always tried to save a copy of the letter in /usr/spool/uucp, or somewhere similar, when incoming remote mail fails - this is silly. Much better to just return an exit status and let delivermail worry about what to do with the error - it knows how to mail it back. I suspect /bin/mail is sloppy because Eric didn't write it - it looks like standard v7 mail hacked up a bit. Dave Martindale
jim (01/05/83)
From: uw-beave!microsof!decvax!utzoo!watmath!watcgl!dmmartindale Several comments about uw-beaver!jim's suggestions on how to handle errors in /bin/mail: Do NOT change /bin/mail to do "exit(pclose(rmf) >> 8);" If uux dies from any sort of signal, this fact is indicated by the lower 8 bits of the return status and the upper 8 bits are zero, and you DON'T want to indicate normal exit in this case. ... Dave Martindale's assessment of /bin/mail looks right to me. As I said, I wasn't advocating that you go right out and change your sources, since I hadn't tested the modifications I proposed. I was just curious as to why the code was wrong and whether anyone had fixed it. The moral is that /bin/mail tries to do too many things and isn't very good at any of them. It is a user interface, a local mailer, and a uucp mailer. What we do is use /bin/mail for local delivery and a different program for uucp delivery. The uucp mailer checks to make sure a site exists before doing the uux, and checks the exit status on return just to make sure.