rsalz@uunet.uu.net (Rich Salz) (11/16/88)
Submitted-by: Chip Salzenberg <chip@ateng.uu.net> Posting-number: Volume 16, Issue 82 Archive-name: deliver/part02 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of shell archive." # Contents: context.c copymsg.c debug.c dest.c dfile.c lock.c PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'context.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'context.c'\" else echo shar: Extracting \"'context.c'\" \(2596 characters\) sed "s/^X//" >'context.c' <<'END_OF_FILE' X/* $Header: context.c,v 1.3 88/09/14 19:41:40 network Exp $ X * X * User context manager. X * This module exists for efficiency reasons; I could just call getpwnam() X * every time I need context info. X * X * $Log: context.c,v $ X * Revision 1.3 88/09/14 19:41:40 network X * Portability to System V and BSD. X * General fixup. X * X * Revision 1.2 88/08/30 16:12:28 network X * Use savestr() instead of strdup(). X * X * Revision 1.1 88/06/06 09:38:05 chip X * Initial revision X * X */ X X#include "deliver.h" X#include <pwd.h> X#include <grp.h> X Xextern struct passwd *getpwnam(); Xextern struct passwd *getpwuid(); Xextern struct group *getgrnam(); Xextern struct group *getgrgid(); X X/* X * Local functions. X */ X Xstatic CONTEXT *new_context(); X X/* X * Local data. X */ X Xstatic CONTEXT *ctlist; /* Chain of CONTEXT structures. */ X X/*---------------------------------------------------------------------- X * Look up a context by user name. X */ X XCONTEXT * Xname_context(name) Xchar *name; X{ X struct passwd *pw; X CONTEXT *ct; X X for (ct = ctlist; ct; ct = ct->next) X { X if (strcmp(ct->name, name) == 0) X return ct; X } X X if ((pw = getpwnam(name)) == NULL) X return NULL; X X return new_context(pw); X} X X/*---------------------------------------------------------------------- X * Look up a context by user ID. X */ X XCONTEXT * Xuid_context(uid) Xint uid; X{ X struct passwd *pw; X CONTEXT *ct; X X for (ct = ctlist; ct; ct = ct->next) X { X if (ct->uid == uid) X return ct; X } X X if ((pw = getpwuid(uid)) == NULL) X return NULL; X X return new_context(pw); X} X X/*---------------------------------------------------------------------- X * Local function -- create a new context structure and return X * its address. X */ X Xstatic CONTEXT * Xnew_context(pw) Xstruct passwd *pw; X{ X CONTEXT *ct; X X ct = (CONTEXT *) zalloc(sizeof(CONTEXT)); X ct->uid = pw->pw_uid; X ct->gid = pw->pw_gid; X ct->name = copystr(pw->pw_name); X ct->home = copystr(pw->pw_dir); X X ct->next = ctlist; X ctlist = ct; X X return ct; X} X X/*---------------------------------------------------------------------- X * Report whether is is possible or not to enter the given context. X */ X Xint Xok_context(ct) XCONTEXT *ct; X{ X if (! ct) X return FALSE; X X if (eff_uid == 0 X || ((real_uid == ct->uid) && (real_gid == ct->gid))) X return TRUE; X else X return FALSE; X} X X/*---------------------------------------------------------------------- X * Look up a group ID by name. X */ X X#ifdef MAILBOX_GROUP X Xint Xgroup_id(name) Xchar *name; X{ X struct group *grp; X X if ((grp = getgrnam(name)) == NULL) X return -1; X X return grp->gr_gid; X} X X#endif /* MAILBOX_GROUP */ END_OF_FILE if test 2596 -ne `wc -c <'context.c'`; then echo shar: \"'context.c'\" unpacked with wrong size! fi # end of 'context.c' fi if test -f 'copymsg.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'copymsg.c'\" else echo shar: Extracting \"'copymsg.c'\" \(5998 characters\) sed "s/^X//" >'copymsg.c' <<'END_OF_FILE' X/* $Header: copymsg.c,v 1.1 88/06/06 09:38:13 chip Exp $ X * X * Take the message from standard input and write it to two temp files, X * one for the header (including the empty line) and one for the body. X * X * $Log: copymsg.c,v $ X * Revision 1.1 88/06/06 09:38:13 chip X * Initial revision X * X */ X X#include "deliver.h" X X/* X * Macros. X */ X X/* Does a string start with "From "? */ X X#define ISFROM(p) ((p)[0] == 'F' && (p)[1] == 'r' && (p)[2] == 'o' \ X && (p)[3] == 'm' && (p)[4] == ' ') X X/* X * Local functions. X */ X Xstatic char *tempfile(); Xstatic int tcreate(); X X/*---------------------------------------------------------------------- X * Copy the message on the standard input to two temp files: X * one for the header and one for the body. X */ X Xint Xcopy_message() X{ X char buf[BUFSIZ]; X FILE *dfp[T_MAX]; X char *p, *fsender, *fremote, *osender, *oremote; X long now; X int t, b, empty_line; X int ret = 0; X X /* X * Create temporary files to hold the header and message body. X */ X X for (t = 0; t < T_MAX; ++t) X { X int fd; X X tfile[t] = tempfile(); X if ((tfd[t] = tcreate(tfile[t])) == -1) X return -1; X X if ((fd = dup(tfd[t])) == -1) X { X syserr("dup %s fd", ttype[t]); X return -1; X } X (void) lseek(fd, 0L, 0); X if ((dfp[t] = fdopen(fd, "r+")) == NULL) X { X error("can't fdopen %s fd", ttype[t]); X return -1; X } X } X X /* Debugging message for later examination of temp files. */ X X if (verbose) X { X message("header=%s, body=%s\n", X tfile[T_HEADER], tfile[T_BODY]); X } X X /* X * If there is a From_ line, find the sender name therein. X */ X X fsender = fremote = NULL; X X b = (fgets(buf, GETSIZE(buf), stdin) ? TRUE : FALSE); X X if (b && ISFROM(buf) && (p = strchr(buf, '\n')) != NULL) X { X b = FALSE; /* Don't output two From_ lines */ X *p = 0; X X /* Find sender */ X X for (fsender = buf + 5; isspace(*fsender); ++fsender) X ; /* until sender */ X for (p = fsender; *p && !isspace(*p); ++p) X ; /* until end of sender */ X if (*p) X *p++ = '\0'; X X /* Find 'remote from' phrase (if any) */ X X for (fremote = p; X (fremote = strchr(fremote, 'r')) != NULL; X ++fremote) X { X if (strncmp(fremote, "remote from", 11) == 0) X { X fremote += 11; X while (isspace(*fremote)) X ++fremote; X break; X } X } X } X X /* X * Write a From_ line to the header file. X * If the user specified a sender name, use it; X * else if we found a From_ line, use the sender found therein; X * else use the user name of our real UID. X */ X X if (sender && *sender) X { X osender = sender; X oremote = NULL; X } X else if (fsender) X { X osender = fsender; X oremote = fremote; X } X else X { X osender = real_ct->name; X oremote = NULL; X } X X (void) fputs("From ", dfp[T_HEADER]); X if (oremote) X { X (void) fputs(oremote, dfp[T_HEADER]); X (void) fputc('!', dfp[T_HEADER]); X } X (void) fputs(osender, dfp[T_HEADER]); X (void) fputc(' ', dfp[T_HEADER]); X (void) time(&now); X (void) fputs(ctime(&now), dfp[T_HEADER]); X X /* X * Copy the rest of the header (if any). X */ X X for (; !feof(stdin); b = FALSE) X { X if (!b) X { X if (fgets(buf, GETSIZE(buf), stdin)) X b = TRUE; X else X break; X } X X /* Empty line means "end of header" */ X X if (buf[0] == '\n') X { X b = FALSE; /* Don't put this line in the body. */ X break; X } X X /* X * A line too long to fit in buf[] can't be a header line. X * At least, that's my opinion... :-) X */ X X if (!strchr(buf, '\n')) X break; X X /* X * If line begins with whitespace, it's a continuation. X * Else if line begins with From_ or '>', prepend '>'. X * Else if line doesn't look like a header, this must X * be the beginning of the body. X */ X X if (isspace(buf[0])) X ; /* continuation */ X else if (ISFROM(buf) || (buf[0] == '>')) X (void) fputc('>', dfp[T_HEADER]); X else X { X /* look for the colon on a header label */ X X p = buf; X while (isalpha(*p) || *p == '-') X ++p; X if ((p == buf) || (*p != ':')) X break; /* Not a header line! */ X } X X /* Write the line to the header file. */ X X (void) fputs(buf, dfp[T_HEADER]); X } X X /* X * End the header file with a blank line. X * This enables us to simply concatenate it with the body file X * to produce a valid message. X */ X X (void) fputc('\n', dfp[T_HEADER]); X X /* X * Copy the body (if any). X */ X X empty_line = FALSE; X for (; !feof(stdin); b = FALSE) X { X if (!b) X { X if (fgets(buf, GETSIZE(buf), stdin)) X b = TRUE; X else X break; X } X X if (ISFROM(buf)) X (void) fputc('>', dfp[T_BODY]); X (void) fputs(buf, dfp[T_BODY]); X X empty_line = (buf[0] == '\n'); X X /* X * Output the rest of a very long line. X * We do this here, instead of going around the loop, X * in order to avoid misinterpreting From_ strings X * that may be found in long lines. X */ X X while (!strchr(buf, '\n') X && !feof(stdin) X && fgets(buf, GETSIZE(buf), stdin)) X (void) fputs(buf, dfp[T_BODY]); X } X X /* Ensure that the body ends with a blank line. */ X X if (! empty_line) X (void) fputc('\n', dfp[T_BODY]); X X /* X * If we encountered any trouble writing to the temp files, X * let's not keep it secret. X */ X X for (t = 0; t < T_MAX; ++t) X { X if (ferror(dfp[t])) X { X error("error writing to %s file %s\n", X ttype[t], tfile[t]); X ret = -1; X } X X (void) fclose(dfp[t]); X } X X /* Return error/success. */ X X return ret; X} X X/*---------------------------------------------------------------------- X * Return a pointer to a temporary filename, or NULL if error. X */ X Xstatic char * Xtempfile() X{ X static char template[] = "/tmp/dl.XXXXXX"; X char *f; X X f = zalloc(32); X (void) strcpy(f, template); X if (mktemp(f) == NULL) X { X error("can't create temporary file"); X return NULL; X } X return f; X} X X/*---------------------------------------------------------------------- X * Create a file, or complain if it doesn't work. X */ X Xstatic int Xtcreate(name) Xchar *name; X{ X int fd; X X if ((fd = open(name, O_RDWR|O_CREAT|O_EXCL, 0600)) == -1) X { X syserr("can't create %s", name); X return -1; X } X X return fd; X} X END_OF_FILE if test 5998 -ne `wc -c <'copymsg.c'`; then echo shar: \"'copymsg.c'\" unpacked with wrong size! fi # end of 'copymsg.c' fi if test -f 'debug.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'debug.c'\" else echo shar: Extracting \"'debug.c'\" \(1003 characters\) sed "s/^X//" >'debug.c' <<'END_OF_FILE' X/* $Header: debug.c,v 1.1 88/06/06 09:38:23 chip Exp $ X * X * Debugging output. X * X * $Log: debug.c,v $ X * Revision 1.1 88/06/06 09:38:23 chip X * Initial revision X * X */ X X#include "deliver.h" X X/*---------------------------------------------------------------------- X * Print out a complete dump of all destinations X */ X Xdumpdests(when) Xchar *when; X{ X DEST *d; X X message("Destinations %s:\n", when); X for (d = first_dest(); d; d = next_dest(d)) X { X message("\t%s", d->name); X if (d->class == CL_MBOX) X message(":%s", d->mailbox); X X message(" ("); X switch (d->class) X { X case CL_USER: X message("User"); X break; X case CL_MBOX: X message("Mailbox"); X break; X case CL_UUCP: X message("UUCP"); X break; X } X message(", "); X switch (d->state) X { X case ST_WORKING: X message("Working"); X break; X case ST_HOLD: X message("Hold"); X break; X case ST_DONE: X message("Done"); X break; X case ST_ERROR: X message("Error: %s", d->error); X break; X } X message(")\n"); X } X} END_OF_FILE if test 1003 -ne `wc -c <'debug.c'`; then echo shar: \"'debug.c'\" unpacked with wrong size! fi # end of 'debug.c' fi if test -f 'dest.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'dest.c'\" else echo shar: Extracting \"'dest.c'\" \(2079 characters\) sed "s/^X//" >'dest.c' <<'END_OF_FILE' X/* $Header: dest.c,v 1.2 88/08/30 16:12:40 network Exp $ X * X * Operations on the list of mail destinations. X * X * $Log: dest.c,v $ X * Revision 1.2 88/08/30 16:12:40 network X * Use savestr() instead of strdup(). X * X * Revision 1.1 88/06/06 09:38:29 chip X * Initial revision X * X */ X X#include "deliver.h" X X/* X * Local data. X */ X Xstatic DEST deadhead = { &deadhead, &deadhead }; X#define HEADPTR (&deadhead) X X/*---------------------------------------------------------------------- X * Add a new destination to the list (unless it already exists). X * Return pointer to DEST. X */ X XDEST * Xdest(name, mailbox) Xchar *name; Xchar *mailbox; X{ X DEST *d; X DCLASS class; X X if (strchr(name, '!')) X class = CL_UUCP; X else if (mailbox) X class = CL_MBOX; X else X class = CL_USER; X X for (d = HEADPTR->next; d != HEADPTR; d = d->next) X { X if (d->class != class) X continue; X X if (strcmp(d->name, name) != 0) X continue; X X /* X * If this destination has a named mailbox, then X * test it for equality as well. X */ X X if (class == CL_MBOX X && strcmp(d->mailbox, mailbox) != 0) X continue; X X /* X * Like, gnarly, dude! It's already in the chain! X */ X X return d; X } X X /* X * The given dest isn't in the list, so we have to add it. X */ X X d = (DEST *) zalloc(sizeof(DEST)); X d->class = class; X d->state = ST_WORKING; X d->name = copystr(name); X if (class == CL_MBOX) X d->mailbox = copystr(mailbox); X X if (class != CL_UUCP X && name_context(name) == NULL) X { X d->state = ST_ERROR; X d->error = "No such user"; X } X X d->prev = HEADPTR->prev; X d->next = HEADPTR; X d->prev->next = d; X d->next->prev = d; X X return d; X} X X/*---------------------------------------------------------------------- X * Return pointer to first DEST in the list. X */ X XDEST * Xfirst_dest() X{ X if (HEADPTR->next != HEADPTR) X return HEADPTR->next; X X return NULL; X} X X/*---------------------------------------------------------------------- X * Return pointer to next DEST in the list, or NULL. X */ X XDEST * Xnext_dest(d) XDEST *d; X{ X if (d && (d = d->next) != HEADPTR) X return d; X X return NULL; X} END_OF_FILE if test 2079 -ne `wc -c <'dest.c'`; then echo shar: \"'dest.c'\" unpacked with wrong size! fi # end of 'dest.c' fi if test -f 'dfile.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'dfile.c'\" else echo shar: Extracting \"'dfile.c'\" \(6731 characters\) sed "s/^X//" >'dfile.c' <<'END_OF_FILE' X/* $Header: dfile.c,v 1.2 88/08/25 15:23:56 network Exp $ X * X * Filter destination(s) through delivery file(s). X * X * $Log: dfile.c,v $ X * Revision 1.2 88/08/25 15:23:56 network X * Add third parameter to do_dfile(), so that if the delivery file cannot X * be executed, the given destination can be recorded as an error. X * X * Revision 1.1 88/06/06 09:38:38 chip X * Initial revision X * X */ X X#include "deliver.h" X#include <sys/types.h> X#include <sys/stat.h> X X/*---------------------------------------------------------------------- X * Filter all valid destinations through the global delivery file. X */ X Xsys_dfile(dac, dav) Xint dac; Xchar **dav; X{ X char **fav; X int fac, a, goodnames; X struct stat st; X X /* X * If there is no global delivery file, then take all the named X * addresses verbatim and return. X */ X X if (stat(sys_deliver, &st) == -1) X { X if (verbose) X message("%s: no system delivery file\n", X progname); X X for (a = 0; a < dac; ++a) X (void) dest(dav[a], (char *) NULL); X return; X } X X /* X * Collect the arguments for the delivery file. X */ X X fav = (char **) zalloc((dac + 3) * sizeof(char **)); X fav[0] = shell; X fav[1] = sys_deliver; X fac = 2; X X goodnames = 0; X for (a = 0; a < dac; ++a) X { X char *p; X X for (p = dav[a]; *p; ++p) X { X if (!isalpha(*p) X && !isdigit(*p) X && !strchr("#%-+._", *p)) X break; X } X X if (*p) X { X /* Invalid name -- note it and go on. */ X X (void) dest(dav[a], (char *) NULL); X } X else X { X /* Valid name -- let the delivery file handle it. */ X X fav[fac++] = dav[a]; X ++goodnames; X } X } X X fav[fac] = NULL; X X /* X * If there were any good names found, let loose the delivery X * file. Note the meaning of "good" is "well-formed", not "valid". X * Thus the system delivery file has control over the handling of X * all local deliveries, not just those to valid users. X */ X X if (goodnames) X (void) do_dfile(eff_ct, fav, (DEST *)NULL); X X free((char *) fav); X} X X/*---------------------------------------------------------------------- X * Filter all user destinations through their local delivery files. X */ X Xuser_dfiles() X{ X DEST *d; X int nfound; X X /* X * Continue to loop through all addresses until no destination X * that needs expanding can be found. X */ X X do { X nfound = 0; X for (d = first_dest(); d; d = next_dest(d)) X { X if (d->class == CL_USER X && d->state == ST_WORKING X && !d->dfdone) X { X one_dfile(d); X d->dfdone = TRUE; X } X } X } while (nfound > 0); X} X X/*---------------------------------------------------------------------- X * Run the delivery file (if any) for the specified destination. X */ X Xone_dfile(d) XDEST *d; X{ X CONTEXT *ct; X char *fav[4]; X char udel_path[100]; X struct stat st; X X if ((ct = name_context(d->name)) == NULL) X { X d->state = ST_ERROR; X d->error = "Missing context in user_dfile_one()"; X return; X } X X /* X * If user's home directory is missing, forget it. X * If user's home directory is writable to the world, X * executing the delivery file would allow a security breach! X * Thanks to Jon Zeeff for this hint... X */ X X if (stat(ct->home, &st) == -1 X || (st.st_mode & S_IFMT) != S_IFDIR) X { X if (verbose) X message("%s: home directory %s is missing!\n", X ct->name, ct->home); X return; X } X X if (st.st_mode & 02) X { X if (verbose) X message("%s: home directory is writable to the world!\n", X ct->name); X return; X } X X /* X * If there is no delivery file to execute, just return. X */ X X (void) sprintf(udel_path, "%s/%s", ct->home, user_deliver); X if (stat(udel_path, &st) == -1) X { X if (verbose) X message("%s has no delivery file\n", d->name); X return; X } X X /* X * Time to run the file! X * We put this dest on hold, so that it will be ignored unless X * the delivery file names it. X */ X X d->state = ST_HOLD; X X fav[0] = "sh"; X fav[1] = udel_path; X fav[2] = d->name; X fav[3] = NULL; X (void) do_dfile(ct, fav, d); X} X X/*---------------------------------------------------------------------- X * Process a delivery file. X */ X Xint Xdo_dfile(ct, av, d) XCONTEXT *ct; Xchar **av; XDEST *d; X{ X FILE *fp; X char *name, *mailbox; X X if (!ct) X return -1; X X if (! ok_context(ct)) X { X if (d) X { X d->state = ST_ERROR; X d->error = "No permissions for that context"; X } X else X message("No permissions to run as %s\n", ct); X X return -1; X } X X /* Make sure that the temp files are readable to the new process. */ X X give_temps(ct); X X /* Here we go! */ X X if (verbose) X message("Processing delivery file as %s\n", ct->name); X X if ((fp = ct_popenv(ct, "/bin/sh", av, "r")) == NULL) X { X error("can't execute delivery file as %s\n", ct->name); X leave(1); X } X X /* X * Read the standard output of the delivery file. X */ X X while (dfile_gets(fp, &name, &mailbox) >= 0) X { X DEST *nd; X X nd = dest(name, mailbox); X if (nd->state == ST_HOLD) X nd->state = ST_WORKING; X X /* X * If the delivery file specified a mailbox, verify X * that the user whose delivery file is running has X * permissions for the requested context. X */ X X if ((nd->state == ST_WORKING) && (mailbox != NULL)) X { X CONTEXT *nct; X X if ((nct = name_context(name)) == NULL) X { X nd->state = ST_ERROR; X nd->error = "Lost context in do_dfile()"; X } X else if (! ok_context(nct)) X { X nd->state = ST_ERROR; X nd->error = "No permissions for that context"; X } X } X } X X return ct_pclose(fp); X} X X/*---------------------------------------------------------------------- X * Get and parse a single delivery file output line. X */ X Xint Xdfile_gets(fp, namep, mailboxp) XFILE *fp; Xchar **namep; Xchar **mailboxp; X{ X char *p, *q; X static char buf[BUFSIZ]; X X if (fgets(buf, GETSIZE(buf), fp) == NULL) X return -1; X X if ((p = strchr(buf, '\n')) != NULL) X *p = 0; X else X { X int c; X X while ((c = fgetc(fp)) != '\n' && c != EOF) X ; /* keep reading */ X X error("invalid line from delivery file: '%s'\n", buf); X return -1; X } X X /* Strip out all whitespace and eliminate duplicated slashes */ X X p = q = buf; X while (*p) X { X if (! isspace(*p)) X { X if ((*q++ = *p++) == '/') X { X while (*p == '/') X ++p; X } X } X } X *q = 0; X X /* Debugging message: display input line */ X X if (verbose) X message("\t'%s'\n", buf); X X if ((p = strchr(buf, ':')) != NULL) X { X *p++ = 0; X if ((q = strchr(p, ':')) != NULL) X *q = 0; X } X X *namep = buf; X *mailboxp = p; X return 0; X} X X/*---------------------------------------------------------------------- X * Make the temp files readable in the given context. X * This is needed because the temps are readable by owner only. X */ X Xgive_temps(ct) XCONTEXT *ct; X{ X int t; X X if (!ct) X return; X X for (t = 0; t < T_MAX; ++t) X { X if (chown(tfile[t], ct->uid, ct->gid) == -1) X syserr("can't chown %s", tfile[t]); X } X} END_OF_FILE if test 6731 -ne `wc -c <'dfile.c'`; then echo shar: \"'dfile.c'\" unpacked with wrong size! fi # end of 'dfile.c' fi if test -f 'lock.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'lock.c'\" else echo shar: Extracting \"'lock.c'\" \(6559 characters\) sed "s/^X//" >'lock.c' <<'END_OF_FILE' X/* $Header: lock.c,v 1.2 88/08/30 16:13:14 network Exp $ X * X * Mailbox locking. X * Local hacks for mailbox access should be grafted here. X * X * $Log: lock.c,v $ X * Revision 1.2 88/08/30 16:13:14 network X * Portability fixes from Ronald Karr <tron@uts.amdahl.com>. X * X * Revision 1.1 88/06/06 09:38:48 chip X * Initial revision X * X */ X X#include "deliver.h" X X/* X * Validate the locking configuration. X */ X X#if (defined(ML_FCNTL) + defined(ML_LOCKF) + defined(ML_LOCKING)) > 1 X lose! "Only one of ML_FCNTL, ML_LOCKF and ML_LOCKING may be defined."; X#endif X X/* X * Support for the lockf() system call. X */ X X#ifdef ML_LOCKF X#include <unistd.h> X#define SIMPLE_LOCK "lockf" X#define LOCKFD(fd, size) lockf(fd, F_LOCK, size) X#define UNLOCKFD(fd, size) lockf(fd, F_ULOCK, size) X#endif /* ML_LOCKF */ X X/* X * Setup for the locking() system call. X */ X X#ifdef ML_LOCKING X#include <sys/types.h> X#include <sys/locking.h> X#define SIMPLE_LOCK "locking" X#define LOCKFD(fd, size) locking(fd, LK_LOCK, size) X#define UNLOCKFD(fd, size) locking(fd, LK_UNLOCK, size) X#endif X X/* X * Local functions. X */ X X#ifdef ML_DOTLOCK Xstatic char *dotlock_name(); X#endif X#ifdef ML_DOTMLK Xstatic char *dotmlk_name(); X#endif X X/*---------------------------------------------------------------------- X * Lock a mailbox by name. X * X * This code looks quite hairy with all the ifdefs. In fact, the only X * somewhat strange thing here is that neither, either, or both of X * ML_DOTLOCK and ML_DOTMLK may be defined, and we have to allow for it. X */ X Xint Xlock_name(name) Xchar *name; X{ X#ifdef ML_DOTLOCK X char *dotlock; X#endif X#ifdef ML_DOTMLK X char *dotmlk; X#endif X X#ifdef ML_DOTLOCK X if ((dotlock = dotlock_name(name)) == NULL X || create_lockfile(dotlock) < 0) X return -1; X#endif /* ML_DOTLOCK */ X X#ifdef ML_DOTMLK X if ((dotmlk = dotmlk_name(name)) == NULL X || create_lockfile(dotmlk) < 0) X { X#ifdef ML_DOTLOCK X (void) remove_lockfile(dotlock); /* don't leave me hanging */ X#endif X return -1; X } X#endif /* ML_DOTMLK */ X X return 0; X} X X/*---------------------------------------------------------------------- X * Unlock a mailbox by name. X */ X Xint Xunlock_name(name) Xchar *name; X{ X int ret = 0; X X#ifdef ML_DOTLOCK X char *dotlock; X#endif X#ifdef ML_DOTMLK X char *dotmlk; X#endif X X#ifdef ML_DOTLOCK X if ((dotlock = dotlock_name(name)) == NULL X || remove_lockfile(dotlock) < 0) X ret = -1; X#endif /* ML_DOTLOCK */ X X#ifdef ML_DOTMLK X if ((dotmlk = dotmlk_name(name)) == NULL X || remove_lockfile(dotmlk) < 0) X ret = -1; X#endif /* ML_DOTMLK */ X X return ret; X} X X/*---------------------------------------------------------------------- X * Lock a file descriptor. X */ X Xint Xlock_fd(fd) Xint fd; X{ X#ifdef ML_FCNTL X struct flock fl; X X fl.l_type = F_WRLCK; X fl.l_whence = 0; X fl.l_start = 0L; X fl.l_len = 0L; X X if (fcntl(fd, F_SETLKW, &fl) == -1) X { X syserr("can't lock with fcntl()"); X return -1; X } X X if (verbose) X message("locked mailbox with fcntl()\n"); X#endif /* ML_FCNTL */ X X#ifdef SIMPLE_LOCK X long pos; X X if ((pos = lseek(fd, 0L, 0)) == -1) X { X syserr("can't seek in mailbox"); X return -1; X } X if (LOCKFD(fd, 0L) == -1) X { X syserr("can't lock with %s()", SIMPLE_LOCK); X return -1; X } X if (lseek(fd, pos, 0) == -1) X { X syserr("can't seek in mailbox"); X return -1; X } X X if (verbose) X message("locked mailbox with %s()\n", SIMPLE_LOCK); X#endif /* SIMPLE_LOCK */ X X /* Default: success */ X return 0; X} X X/*---------------------------------------------------------------------- X * Unlock a file descriptor. X */ X Xint Xunlock_fd(fd) Xint fd; X{ X#ifdef ML_FCNTL X struct flock fl; X X fl.l_type = F_UNLCK; X fl.l_whence = 0; X fl.l_start = 0L; X fl.l_len = 0L; X X if (fcntl(fd, F_SETLKW, &fl) == -1) X { X syserr("can't unlock with fcntl()"); X return -1; X } X X if (verbose) X message("unlocked mailbox with fcntl()\n"); X#endif /* ML_FCNTL */ X X#ifdef SIMPLE_LOCK X long pos; X X if ((pos = lseek(fd, 0L, 0)) == -1) X { X syserr("can't seek in mailbox"); X return -1; X } X if (LOCKFD(fd, 0L) == -1) X { X syserr("can't unlock with %s()", SIMPLE_LOCK); X return -1; X } X if (lseek(fd, pos, 0) == -1) X { X syserr("can't seek in mailbox"); X return -1; X } X X if (verbose) X message("unlocked mailbox with %s()\n", SIMPLE_LOCK); X#endif /* SIMPLE_LOCK */ X X /* Default: success */ X return 0; X} X X/*---------------------------------------------------------------------- X * Return the name of the appropriate ".lock" file for a mailbox. X */ X X#ifdef ML_DOTLOCK X Xstatic char * Xdotlock_name(name) Xchar *name; X{ X static char *lname = NULL; X static int lsize = 0; X char *p; X int n, i; X X n = strlen(name); X if (lsize < n + 8) X { X if (lname) X free(lname); X lsize = n + 32; X lname = zalloc(lsize); X } X X (void) strcpy(lname, name); X X /* X * We want as much of `basename.lock' as will fit in a string X * MAX_NAMESIZE long. X */ X for (i = 0, p = basename(lname); (i < MAX_NAMESIZE - 5) && (*p); ++i) X ++p; X (void) strcpy(p, ".lock"); X X return lname; X} X X#endif /* ML_DOTLOCK */ X X/*---------------------------------------------------------------------- X * Return the name of the appropriate ".mlk" file for a mailbox. X */ X X#ifdef ML_DOTMLK X Xstatic char * Xdotmlk_name(name) Xchar *name; X{ X static char lname[MAX_NAMESIZE + 16]; X char *p, *d; X int i; X X /* X * To explain the below: If we ass_u_me that MAX_NAMESIZE is 14, X * then this code is like `printf(lname, "/tmp/%.10s.mlk", ...)'. X * In other words, we want as much of `basename.mlk' as will fit X * in a string MAX_NAMESIZE long. X */ X d = lname; X for (p = "/tmp/"; *p; ) X *d++ = *p++; X for (i = 0, p = basename(name); (i < MAX_NAMESIZE - 4) && (*p); ++i) X *d++ = *p++; X (void) strcpy(d, ".mlk"); X X return lname; X} X X#endif /* ML_DOTMLK */ X X/*---------------------------------------------------------------------- X * Create a lockfile. X */ X Xint Xcreate_lockfile(name) Xchar *name; X{ X int tries, fd; X X for (tries = 0; tries < 10; ++tries) X { X if (tries) X snooze(3); X X if ((fd = open(name, O_RDWR|O_CREAT|O_EXCL, 0)) >= 0) X { X (void) close(fd); X if (verbose) X message("created lockfile %s\n", name); X return 0; X } X X if (verbose && (tries == 0)) X { X message("Waiting to create %s (try #%d)\n", X name, tries + 1); X } X } X X syserr("can't create lockfile %s", name); X return -1; X} X X/*---------------------------------------------------------------------- X * Remove a lockfile. X */ X Xint Xremove_lockfile(name) Xchar *name; X{ X if (unlink(name) == -1) X { X syserr("can't remove lockfile %s", name); X return -1; X } X X if (verbose) X message("removed lockfile %s\n", name); X X return 0; X} END_OF_FILE if test 6559 -ne `wc -c <'lock.c'`; then echo shar: \"'lock.c'\" unpacked with wrong size! fi # end of 'lock.c' fi echo shar: End of shell archive. exit 0 -- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.