This is arepdaem.c in view mode; [Download] [Up]
static char rcsid[] = "@(#)$Id: arepdaem.c,v 5.9 1992/12/11 01:45:04 syd Exp $"; /******************************************************************************* * The Elm Mail System - $Revision: 5.9 $ $State: Exp $ * * Copyright (c) 1988-1992 USENET Community Trust * Copyright (c) 1986,1987 Dave Taylor ******************************************************************************* * Bug reports, patches, comments, suggestions should be sent to: * * Syd Weinstein, Elm Coordinator * elm@DSI.COM dsinc!elm * ******************************************************************************* * $Log: arepdaem.c,v $ * Revision 5.9 1992/12/11 01:45:04 syd * remove sys/types.h include, it is now included by defs.h * and this routine includes defs.h or indirectly includes defs.h * From: Syd * * Revision 5.8 1992/12/07 04:21:58 syd * add missing err declares * From: Syd * * Revision 5.7 1992/12/07 03:00:27 syd * Fix long -> time_t * From: Syd * * Revision 5.6 1992/11/26 00:46:50 syd * Fix how errno is used so err is inited and used instead * as errno gets overwritten by print system call * From: Syd * * Revision 5.5 1992/11/07 20:05:52 syd * change to use header_cmp to allow for linear white space around the colon * From: Syd * * Revision 5.4 1992/10/27 01:43:40 syd * Move posix_signal to lib directory * From: tom@osf.org * * Revision 5.3 1992/10/25 02:54:00 syd * add posix signal stuff as stop gap, needs to be moved to lib * From: syd * * Revision 5.2 1992/10/11 00:59:39 syd * Fix some compiler warnings that I receive compiling Elm on my SVR4 * machine. * From: Tom Moore <tmoore@fievel.DaytonOH.NCR.COM> * * Revision 5.1 1992/10/04 00:46:45 syd * Initial checkin as of 2.4 Release at PL0 * * ******************************************************************************/ /** Keep track of mail as it arrives, and respond by sending a 'recording' file to the sender as new mail is received. Note: the user program that interacts with this program is the 'autoreply' program and that should be consulted for further usage information. This program is part of the 'autoreply' system, and is designed to run every hour and check all mailboxes listed in the file "/etc/autoreply.data", where the data is in the form: username replyfile current-mailfile-size To avoid a flood of autoreplies, this program will NOT reply to mail that contains header "X-Mailer: fastmail". Further, each time the program responds to mail, the 'mailfile size' entry is updated in the file /etc/autoreply.data to allow the system to be brought down and rebooted without any loss of data or duplicate messages. This daemon also uses a lock semaphore file, /usr/spool/uucp/LCK..arep, to ensure that more than one copy of itself is never running. For this reason, it is recommended that this daemon be started up each morning from cron, since it will either start since it's needed or simply see that the file is there and disappear. Since this particular program is the main daemon answering any number of different users, it must be run with uid root. **/ #include "elmutil.h" #include "s_arepdaem.h" #ifdef BSD # include <sys/time.h> #else # include <time.h> #endif #include <sys/stat.h> #include <ctype.h> #include <errno.h> #define arep_lock_file "LCK..arep" #define autoreply_file "/etc/autoreply.data" #define logfile "/etc/autoreply.log" /* first choice */ #define logfile2 "/tmp/autoreply.log" /* second choice */ #define BEGINNING 0 /* see fseek(3S) for info */ #define SLEEP_TIME 3600 /* run once an hour */ #define MAX_PEOPLE 20 /* max number in program */ #define EXISTS 00 /* lock file exists?? */ #define remove_return(s) if (strlen(s) > 0) { \ if (s[strlen(s)-1] == '\n') \ s[strlen(s)-1] = '\0'; \ } struct replyrec { char username[NLEN]; /* login name of user */ char mailfile[SLEN]; /* name of mail file */ char replyfile[SLEN]; /* name of reply file */ long mailsize; /* mail file size */ int in_list; /* for new replies */ } reply_table[MAX_PEOPLE]; FILE *logfd; /* logfile (log action) */ time_t autoreply_time = 0L; /* modif date of autoreply file */ int active = 0; /* # of people 'enrolled' */ static char *def_subj, /* Default subject text */ *subj_fw, /* First word of subject */ *arep_subj; /* Autorepysubject with subject text */ FILE *open_logfile(); /* forward declaration */ long bytes(); /* ditto */ time_t ModTime(); /* ditto */ void read_autoreply_file(); SIGHAND_TYPE term_signal(); extern int errno; /* system error number! */ main() { long size; int person, data_changed; time_t last_mod_time; if (fork()) exit(0); if (! lock()) exit(0); /* already running! */ signal(SIGTERM, term_signal); /* Terminate signal */ /* * note the usage of the BSD style setpgrp wont hurt * system V as its calling sequence is no arguments. * The idea is to disassociate from the terminal to * prevent signals. */ person = getpid(); setpgrp(person, person); #ifdef I_LOCALE setlocale(LC_ALL, ""); #endif elm_msg_cat = catopen("elm2.4", 0); /* Get the subject texts */ def_subj = catgets(elm_msg_cat, ArepdaemSet, ArepdaemDefSubj, "Auto-reply Mail"); subj_fw = catgets(elm_msg_cat, ArepdaemSet, ArepdaemSubjFw, "Auto-reply"); arep_subj = catgets(elm_msg_cat, ArepdaemSet, ArepdaemArepSubj, "Auto-reply to:%s"); while (1) { logfd = open_logfile(); /* open the log */ /* 1. check to see if autoreply table has changed.. */ if ((last_mod_time = ModTime(autoreply_file)) != autoreply_time) { read_autoreply_file(); autoreply_time = last_mod_time; } /* 2. now for each active person... */ data_changed = 0; for (person = 0; person < active; person++) { if ((size = bytes(reply_table[person].mailfile)) != reply_table[person].mailsize) { if (size > reply_table[person].mailsize) read_newmail(person); /* else mail removed - resync */ reply_table[person].mailsize = size; data_changed++; } } /* 3. if data changed, update autoreply file */ if (data_changed) update_autoreply_file(); close_logfile(); /* close the logfile again */ /* 4. Go to sleep... */ sleep(SLEEP_TIME); } } void read_autoreply_file() { /** We're here because the autoreply file has changed!! It could either be because someone has been added or because someone has been removed...since the list will always be in order (nice, eh?) we should have a pretty easy time of it... **/ FILE *file; char username[SLEN], replyfile[SLEN]; int person; long size; log_msg(catgets(elm_msg_cat, ArepdaemSet, ArepdaemDataFileChange, "Autoreply data file has changed! Reading...")); /* * clear old entries prior to reread */ for (person = 0; person < active; person++) reply_table[person].in_list = 0; if ((file = fopen(autoreply_file,"r")) == NULL) { log_msg(catgets(elm_msg_cat, ArepdaemSet, ArepdaemNoOne, "No-one is using autoreply...")); } else { while (fscanf(file, "%s %s %dl", username, replyfile, &size) != EOF) { /* check to see if this person is already in the list */ if ((person = in_reply_list(username)) != -1) { reply_table[person].in_list = 1; reply_table[person].mailsize = size; /* sync */ } else { /* if not, add them */ if (active == MAX_PEOPLE) { unlock(); exit(log_msg(catgets(elm_msg_cat, ArepdaemSet, ArepdaemMaxPeople, "Couldn't add %s - already at max people!"), username)); } log_msg(catgets(elm_msg_cat, ArepdaemSet, ArepdaemAddingUser, "adding %s to the active list"), username); strcpy(reply_table[active].username, username); sprintf(reply_table[active].mailfile, "%s%s", mailhome, username); strcpy(reply_table[active].replyfile, replyfile); reply_table[active].mailsize = size; reply_table[active].in_list = 1; /* obviously! */ active++; } } fclose(file); } /** now check to see if anyone has been removed... **/ person = 0; while (person < active) if (reply_table[person].in_list) { person++; } else { log_msg(catgets(elm_msg_cat, ArepdaemSet, ArepdaemRemovingUser, "removing %s from the active list"), reply_table[person].username); strcpy(reply_table[person].username, reply_table[active-1].username); strcpy(reply_table[person].mailfile, reply_table[active-1].mailfile); strcpy(reply_table[person].replyfile, reply_table[active-1].replyfile); reply_table[person].mailsize = reply_table[active-1].mailsize; active--; } } update_autoreply_file() { /** update the entries in the autoreply file... **/ FILE *file; register int person; if ((file = fopen(autoreply_file,"w")) == NULL) { log_msg(catgets(elm_msg_cat, ArepdaemSet, ArepdaemErrUpdate, "Couldn't update autoreply file!")); return; } for (person = 0; person < active; person++) fprintf(file, "%s %s %ld\n", reply_table[person].username, reply_table[person].replyfile, reply_table[person].mailsize); fclose(file); autoreply_time = ModTime(autoreply_file); } int in_reply_list(name) char *name; { /** search the current active reply list for the specified username. return the index if found, or '-1' if not. **/ register int iindex; for (iindex = 0; iindex < active; iindex++) if (strcmp(name, reply_table[iindex].username) == 0) return(iindex); return(-1); } read_newmail(person) int person; { /** Read the new mail for the specified person. **/ FILE *mailfile; char from_whom[SLEN], subject[SLEN]; int sendit; log_msg(catgets(elm_msg_cat, ArepdaemSet, ArepdaemNewMail, "New mail for %s"), reply_table[person].username); if ((mailfile = fopen(reply_table[person].mailfile,"r")) == NULL) return(log_msg(catgets(elm_msg_cat, ArepdaemSet, ArepdaemErrOpen, "can't open mailfile for user %s"), reply_table[person].username)); if (fseek(mailfile, reply_table[person].mailsize, BEGINNING) == -1) { fclose(mailfile); return(log_msg(catgets(elm_msg_cat, ArepdaemSet, ArepdaemErrSeek, "couldn't seek to %ld in mail file!"), reply_table[person].mailsize)); } while (get_return(mailfile, person, from_whom, subject, &sendit) != -1) if (sendit) reply_to_mail(person, from_whom, subject); fclose(mailfile); return; } int get_return(file, person, from, subject, sendit) FILE *file; int person, *sendit; char *from, *subject; { /** Reads the new message and return the from and subject lines. sendit is set to true iff it isn't a machine generated msg If USE_EMBEDDED_ADDRESSES is set, then the return address is taken as the last From: or Reply-To: that is seen in the header lines. Crude, but it works. **/ char name1[SLEN], name2[SLEN], lastname[SLEN]; char buffer[SLEN], hold_return[SLEN]; int done = 0, in_header = 0; from[0] = '\0'; name1[0] = '\0'; name2[0] = '\0'; lastname[0] = '\0'; *sendit = 1; while (! done) { if (fgets(buffer, SLEN, file) == NULL) return(-1); #ifndef USE_EMBEDDED_ADDRESSES if (first_word(buffer, "From ")) { in_header++; sscanf(buffer, "%*s %s", hold_return); } else if (in_header) { if (first_word_nc(buffer, ">From")) { sscanf(buffer,"%*s %s %*s %*s %*s %*s %*s %*s %*s %s", name1, name2); add_site(from, name2, lastname); } #else /* We're allowing USE_EMBEDDED_ADDRESSES */ if (first_word(buffer, "From ")) in_header++; else if (in_header) { if (header_cmp(buffer, "From", NULL) || header_cmp(buffer, "Reply-To", NULL)) get_address_from(buffer, from); #endif else if (header_cmp(buffer,"Subject", NULL)) { remove_return(buffer); strcpy(subject, (char *) (buffer + 8)); } else if (header_cmp(buffer,"X-Mailer", "fastmail")) *sendit = 0; else if (strlen(buffer) == 1) done = 1; } } #ifndef USE_EMBEDDED_ADDRESSES if (from[0] == '\0') strcpy(from, hold_return); /* default address! */ else add_site(from, name1, lastname); /* get the user name too! */ #endif return(0); } reply_to_mail(person, from, subject) int person; char *from, *subject; { /** Respond to the message from the specified person with the specified subject... **/ char buffer[SLEN]; if (strlen(subject) == 0) strcpy(subject, def_subj); else if (! first_word_nc(subject, subj_fw)) { sprintf(buffer, arep_subj, subject); strcpy(subject, buffer); } log_msg(catgets(elm_msg_cat, ArepdaemSet, ArepdaemArepTo, "auto-replying to '%s'"), from); mail(from, subject, reply_table[person].replyfile, person); } long bytes(name) char *name; { /** return the number of bytes in the specified file. This is to check to see if new mail has arrived.... **/ int ok = 1, err; struct stat buffer; if (stat(name, &buffer) != 0) if (errno != 2) { err = errno; unlock(); exit(MCfprintf(stderr, catgets(elm_msg_cat, ArepdaemSet, ArepdaemErrFstat, "Error %d attempting fstat on %s"), err, name)); } else ok = 0; return(ok ? buffer.st_size : 0); } time_t ModTime(name) char *name; { /** return the modification time in the specified file. This is to check to see if autoreply has changed.... **/ int ok = 1, err; struct stat buffer; if (stat(name, &buffer) != 0) if (errno != 2) { err = errno; unlock(); exit(MCfprintf(stderr, catgets(elm_msg_cat, ArepdaemSet, ArepdaemErrFstat, "Error %d attempting fstat on %s"), err, name)); } else ok = 0; return(ok ? buffer.st_mtime : (time_t) 0); } mail(to, subject, filename, person) char *to, *subject, *filename; int person; { /** Mail 'file' to the user from person... **/ char buffer[VERY_LONG_STRING]; MCsprintf(buffer, catgets(elm_msg_cat, ArepdaemSet, ArepdaemMailCommand, "%s/fastmail -f '%s [autoreply]' -s '%s' %s %s"), BIN, reply_table[person].username, subject, filename, to); system(buffer); } log_msg(message, arg) char *message; char *arg; { /** Put log entry into log file. Use the format: date-time: <message> **/ struct tm *thetime; time_t clock; #ifndef _POSIX_SOURCE struct tm *localtime(); time_t time(); #endif char buffer[SLEN]; /** first off, get the time and date **/ clock = time((time_t *) 0); /* seconds since ??? */ thetime = localtime(&clock); /* and NOW the time... */ /** then put the message out! **/ sprintf(buffer, message, arg); fprintf(logfd,"%d/%d-%d:%02d: %s\n", thetime->tm_mon+1, thetime->tm_mday, thetime->tm_hour, thetime->tm_min, buffer); } FILE *open_logfile() { /** open the logfile. returns a valid file descriptor **/ FILE *fd; if ((fd = fopen(logfile, "a")) == NULL) if ((fd = fopen(logfile2, "a")) == NULL) { unlock(); exit(1); /* give up! */ } return( (FILE *) fd); } close_logfile() { /** Close the logfile until needed again. **/ fclose(logfd); } /*** LOCK and UNLOCK - ensure only one copy of this daemon running at any given time by using a file existance semaphore (wonderful stuff!) ***/ lock() { char lock_name[SLEN]; /* name of lock file */ char pid_buffer[SHORT]; int pid, create_fd, err; sprintf(lock_name, "%s/%s", LOCK_DIR, arep_lock_file); #ifdef PIDCHECK /** first, try to read the lock file, and if possible, check the pid. If we can validate that the pid is no longer active, then remove the lock file. **/ if((create_fd=open(lock_name,O_RDONLY)) != -1) { if (read(create_fd, pid_buffer, SHORT) > 0) { pid = atoi(pid_buffer); if (pid) { if (kill(pid, 0)) { close(create_fd); if (unlink(lock_name) != 0) { err = errno; MCprintf(catgets(elm_msg_cat, ArepdaemSet, ArepdaemErrUnlink, "Error %s\n\ttrying to unlink file %s (%s)\n"), error_description(err), lock_name, "lock"); return(0); } } else /* kill pid check succeeded */ return(0); } else /* pid was zero */ return(0); } else /* read failed */ return(0); } /* ok, either the open failed or we unlinked it, now recreate it. */ #else if (access(lock_name, EXISTS) == 0) return(0); /* file already exists */ #endif if ((create_fd=creat(lock_name, 0444)) == -1) return(0); /* can't create file!! */ sprintf(pid_buffer,"%d\n", getpid() ); /* write the current pid to the file */ write(create_fd, pid_buffer, strlen(pid_buffer)); close(create_fd); /* no need to keep it open */ return(1); } unlock() { /** remove lock file if it's there! **/ (void) unlink(arep_lock_file); } SIGHAND_TYPE term_signal() { unlock(); exit(1); /* give up! */ }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.