We came up with a distributed mail program earlier this year for
Manchester. We had two options: let some machine receive all mail
in traditional fashion, and transfer it to AFS, or write an agent
to receive mail. Our networking staff recommended that we use PP
as the mail receiver, since it allows us to run an arbitrary program
when it receives mail. The program assumes that a cron job runs
from time to time to refresh an admin token, to which only root on
a restricted machine has access. This saved the overhead of authenticating
every time a mail message is received. We modified all sendmail.cf
files so that they pretend all outgoing mail comes from afs.mcc.ac.uk,
and so that no mail is ever delivered locally. We found a few programs
still try to write directly to /usr/mail, so we have just removed those.
Mail is delivered to the home directory of each user, to a subdirectory
named ~/.Mail, and can be read only with the help of a patched version of
elm. The patch was only about two lines to change the default mail directory.
By default elm assumes that the default mail directory is known at compile
time, whereas we of course need a different mail directory for each user.
By changing a variable (MAIL or MAILPATH) we can get the shells to spot
incoming mail as it arrives.
> From: [EMAIL PROTECTED] (Jorge Cisternas)
>
> The first approach I try was to link the /usr/mail (or /usr/spool/mail) to
> a directory on the AFS environment.
This approach requires the AFS directory to be world writeable, or requires
authentication in the mail delivery agent.
Actually, we first tried using NFS to mount the standard spool directory
across our systems, but the bugs in HP's NFS implementation stopped us.
After months of complaining to HP, installing one patch after another, and
seeing the mail files locked yet again five minutes later, we just gave up.
AFS has had a lot of problems for us, but NFS was far worse.
> Any pop mail reader for HPUX 9.X?
We preferred to stick with our recommended mailer, elm, though we shall
be looking at pine as a possibility to add to the list of supported mailers.
> From: "Perry E. Metzger" <[EMAIL PROTECTED]>
>
> Your users in Tokyo don't
> end up depending on a machine in New York for their mail and vice
> versa... No single point of failure
> exists for your organization's mail system.
We have only one machine receiving mail for thousands of users, with
no backup. This is certainly a problem.
> The other method requires extensive hacking and yields, IMHO mediocre
> results, especially with respect to reliability, load, and security.
Actually, the load and security problems do not seem to be there.
The real problem of reliability is that the PP software, which is supplied
by Sun, is buggy and full of undocumented features. For example, when a
file server is down, if there are several users whose mail has to be
received on those machines, PP assumes that no mail can be received by
anybody after the first several failures.
Also the load factor worried us about using POP, since the delivery
machine -- the weak link in the whole system -- would have the most
strain put on it when it receives and pop-serves during the day time.
The AFS solution means that when I read my mail, the mailserver machine
has no load on it at all.
> using AFS as a means of
> distributing around rapidly changing data of any sort -- be it news,
> mail, or anything that alters a lot in the day -- is a bad idea.
This argument does not apply to the system we are using, since we do
not have a single mail directory.
> From: [EMAIL PROTECTED]
>
> But it doesn't seem to be a huge tax to deliver mail into AFS (into
> directories under users' home dirs), as long as the delivery process
> knows about the transient failure conditions that don't arise in a local
> FS environment, and which are reflected via errors on system calls.
This is what we found. The system normally works quite well.
> From: Keith Gorlen <[EMAIL PROTECTED]>
>
> Discussions similar to this one on mail delivery have appeared on this
> group before. I don't recall any previous discussion of mail
> *notification*, however. How does the user know to read his mail?
> Around here, our non-AMDS users tend to use "xbiff"...
This is solved with our system; the 'biff' programs work if the MAIL
variable is set properly.
So here is the delivery program. I have compiled it under SunOs 5.3
on a sparc, and under HP-UX 9.03 on an HP 9000/700. The peculiar
return code 0300 tells the PP software to bounce the message back to
the sender, which we do if the recipient's home directory is over quota.
-- Owen
[EMAIL PROTECTED]
#ifdef __hpux__
#define _INCLUDE_POSIX_SOURCE
#define _INCLUDE_AES_SOURCE
#define _INCLUDE_HPUX_SOURCE
#endif
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#ifdef __sparc__
#include <sys/ioccom.h>
#endif
#include <netinet/in.h>
#include <afs/param.h>
#include <afs/stds.h>
#include <afs/vice.h>
#include <afs/venus.h>
#include <afs/auth.h>
#include <afs/afsint.h>
/* AFS-specific functions not declared in any include file */
void setpag(void);
long ktc_SetToken(struct ktc_principal *service, struct ktc_token *token,
struct ktc_principal *client, int flags);
#define PW_BUF_SIZE 1024
#define USER_NAME_LEN 17
#define MAX_RETRIES 6
#define MAIL_DIR ".Mail"
#define AFSMAIL_DIR "/usr/lib/afsmail"
#define AFSMAIL_PW "/usr/lib/afsmail/passwd"
int lockfp = 0;
char lockfile[USER_NAME_LEN + 5],
thisname[USER_NAME_LEN + 2] = "unknown";
/* rc -- exit(1) after writing an error message if a condition is not 0. */
void rc(long code, char *mesg)
{
if (!code) return;
fprintf(stderr, "Aborting (code %ld: %s: %s.)\n", code, thisname, mesg);
if (lockfp > 0) {
if (!close(lockfp))
fprintf(stderr, "Closing %s fails with error %d.\n", lockfile, errno);
if (!unlink(lockfile))
fprintf(stderr, "Deleting %s fails with error %d.\n", lockfile, errno);
}
if (code == 0300) exit(code);
else exit(0200);
}
void bounce(long code, char *mesg) /* Failure should cause the message
to be returned to the sender */
{
if (!code) return;
rc(0300, mesg);
}
int remove_symlink(char *name) /* return 1 if exists and is not dir */
{
struct stat sb;
struct ViceIoctl data;
if (lstat(name, &sb))
return(0); /* Does not exist, or is off line */
if (!S_ISLNK(sb.st_mode)) { /* Is not a link */
if (!S_ISDIR(sb.st_mode))
return(1); /* Return true if not a dir */
data.in = name;
data.in_size = strlen(name) + 1;
data.out = NULL;
data.out_size = 0;
if (pioctl(".", VIOC_AFS_STAT_MT_PT, &data, 1))
return(0);
}
rc(unlink(name), "Can't delete illegal link or mount point");
return(0);
}
void main(int count, char **vector)
{
int iterate, fd;
FILE *fp;
uid_t idno;
gid_t grno;
char buffer[PW_BUF_SIZE],
maildir[PW_BUF_SIZE];
if (count != 4) {
fprintf(stderr, "Usage: %s destination source size\n", *vector);
exit(2);
}
chdir(AFSMAIL_DIR);
/* Create an authentication group to isolate this 'family' of processes. */
setpag();
{
struct ktc_principal service, client;
struct ktc_token token;
/* Recover a saved admin token, and activate it. These tokens are
saved by another program every 8 hours, and have 25 hour duration. */
rc(!(fd = open("token", O_RDONLY)), "Can't open token file");
read(fd, &service, sizeof(struct ktc_principal));
read(fd, &token, sizeof(struct ktc_token));
read(fd, &client, sizeof(struct ktc_principal));
close(fd);
rc(ktc_SetToken(&service, &token, &client, 0), "Can't set token");
}
{
int namelen;
char *idpos, *grouppos, *homedpos, *termpos;
/* I need this section because most incoming mail is for people not
in the local passwd file. */
strncpy(thisname, vector[1], USER_NAME_LEN);
strcat(thisname, ":");
namelen = strlen(thisname);
rc(!(fp = fopen(AFSMAIL_PW, "r")), "Can't open passwd file");
do
if (!fgets(buffer, PW_BUF_SIZE, fp))
rc(1, "Can't find addressee in passwd file");
while (strncmp(thisname, buffer, namelen));
fclose(fp);
thisname[namelen - 1] = '\0';
rc(!(idpos = strchr(buffer + namelen, ':')) ||
!(grouppos = strchr(++idpos, ':')) ||
!(homedpos = strchr(++grouppos, ':')) ||
!(homedpos = strchr(homedpos + 1, ':')) ||
!(termpos = strchr(++homedpos, ':')), "Bad password entry");
*termpos = '\0';
rc(setgid(grno = atoi(grouppos)), "Unable to set gid");
rc(setuid(idno = atoi(idpos)), "Unable to set uid");
/* Change the home directories, especially for those users outside
of AFS, to a pseudo-home in AFS. */
if (!strncmp(homedpos, "/home/", 6))
sprintf(maildir, "/afs/mcc/users%s", homedpos + 5);
else if (!strncmp(homedpos, "/users3/dbase/dbusers/", 22))
sprintf(maildir, "/afs/mcc/users/mbj%s", homedpos + 21);
else if (!strncmp(homedpos, "/usr/users/", 11) ||
!strncmp(homedpos, "/usr/local/", 11))
sprintf(maildir, "/afs/mcc/users/mcc%s", homedpos + 10);
else if (!strncmp(homedpos, "/users2/", 8))
sprintf(maildir, "/afs/mcc/users/mcc%s", homedpos + 7);
else rc(1, "Unrecognizable home directory");
rc(chdir(maildir), "Unable to chdir to home directory");
}
/* Ensure that the destination volume has enough space to receive
the mail, and bounce it if it doesn't. */
{
struct ViceIoctl data;
char b[sizeof(VolumeStatus) + 1024];
VolumeStatus *status = (VolumeStatus *) b;
unsigned int size;
size = (atoi(vector[3]) + 1023) / 1024;
data.in_size = 0;
data.out_size = sizeof(b);
data.out = b;
pioctl(".", VIOCGETVOLSTAT, &data, 1);
bounce(status->BlocksInUse + size + 2 >= status->MaxQuota,
"Unable to deliver mail; volume is too full");
}
/* Create a .Mail directory if necessary. */
if (remove_symlink(MAIL_DIR))
rc(unlink(MAIL_DIR), "Can't delete illegal mail directory");
if (chdir(MAIL_DIR)) {
rc(mkdir(MAIL_DIR, 0711), "Unable to mkdir .Mail; possible quota problem");
chown(MAIL_DIR, idno, grno);
sprintf(maildir, "/usr/afsws/bin/fs sa .Mail -acl "
"system:administrators all %s all -clear", thisname);
system(maildir);
rc(chdir(MAIL_DIR), "Unable to chdir to .Mail");
}
/* Create a lock file, retrying MAX_RETRIES times, waiting 10 seconds */
sprintf(lockfile, "%s.lock", thisname);
iterate = MAX_RETRIES;
while (1) {
errno = 0;
if ((lockfp = open(lockfile, O_WRONLY | O_CREAT | O_EXCL, 0444)) != -1)
break;
rc(errno != EEXIST, "Unable to create lock file; possible quota problem");
if (!--iterate) {
time_t now;
struct stat sb;
time(&now);
rc(lstat(lockfile, &sb), "Unable to lstat lockfile");
if (now - sb.st_ctime > 3600) {
unlink(lockfile);
exit(1);
}
rc(1, "Lock file exists for more than 2 minutes");
}
sleep(10);
}
/* Format the date and time for the From line. */
{
time_t now;
time(&now);
strftime(buffer, PW_BUF_SIZE, "%a %b %d %H:%M %Z %Y", localtime(&now));
}
remove_symlink(thisname); /* Delete symbolic link */
rc(!(fp = fopen(thisname, "a")), "Unable to open mail folder");
fprintf(fp, "\01\01\01\01\nFrom %s %s\n", vector[2], buffer);
/* Copy from stdin to the mail folder. */
while(fgets(buffer, PW_BUF_SIZE, stdin))
fputs(buffer, fp);
fputs("\01\01\01\01\n", fp);
iterate = MAX_RETRIES;
while(1) {
if (!fclose(fp))
break;
rc(!--iterate, "Unable to close mail folder");
sleep(10);
}
chown(thisname, idno, grno);
chmod(thisname, 0600);
close(lockfp);
unlink(lockfile);
exit(0);
}
-----