diff options
author | Eduardo Chappa <echappa@gmx.com> | 2013-02-03 00:59:38 -0700 |
---|---|---|
committer | Eduardo Chappa <echappa@gmx.com> | 2013-02-03 00:59:38 -0700 |
commit | 094ca96844842928810f14844413109fc6cdd890 (patch) | |
tree | e60efbb980f38ba9308ccb4fb2b77b87bbc115f3 /imap/src/mlock/mlock.c | |
download | alpine-094ca96844842928810f14844413109fc6cdd890.tar.xz |
Initial Alpine Version
Diffstat (limited to 'imap/src/mlock/mlock.c')
-rw-r--r-- | imap/src/mlock/mlock.c | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/imap/src/mlock/mlock.c b/imap/src/mlock/mlock.c new file mode 100644 index 00000000..1dca40ed --- /dev/null +++ b/imap/src/mlock/mlock.c @@ -0,0 +1,175 @@ +/* ======================================================================== + * Copyright 1988-2008 University of Washington + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * + * ======================================================================== + */ + +/* + * Program: Standalone Mailbox Lock program + * + * Author: Mark Crispin + * Networks and Distributed Computing + * Computing & Communications + * University of Washington + * Administration Building, AG-44 + * Seattle, WA 98195 + * Internet: MRC@CAC.Washington.EDU + * + * Date: 8 February 1999 + * Last Edited: 3 March 2008 + */ + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <sysexits.h> +#include <syslog.h> +#include <grp.h> +#include <sys/types.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <stdlib.h> +#include <netdb.h> +#include <ctype.h> +#include <string.h> + +#define LOCKTIMEOUT 5 /* lock timeout in minutes */ +#define LOCKPROTECTION 0664 + +#ifndef MAXHOSTNAMELEN /* Solaris still sucks */ +#define MAXHOSTNAMELEN 256 +#endif + +/* Fatal error + * Accepts: Message string + * exit code + * Returns: code + */ + +int die (char *msg,int code) +{ + syslog (LOG_NOTICE,"(%u) %s",code,msg); + write (1,"?",1); /* indicate "impossible" failure */ + return code; +} + + +int main (int argc,char *argv[]) +{ + int ld,i; + int tries = LOCKTIMEOUT * 60 - 1; + char *s,*dir,*file,*lock,*hitch,tmp[1024]; + size_t dlen,len; + struct stat sb,fsb; + struct group *grp = getgrnam ("mail"); + /* get syslog */ + openlog (argv[0],LOG_PID,LOG_MAIL); + if (!grp || (grp->gr_gid != getegid ())) + return die ("not setgid mail",EX_USAGE); + if (argc != 3) return die ("invalid arguments",EX_USAGE); + for (s = argv[1]; *s; s++) + if (!isdigit (*s)) return die ("invalid fd",EX_USAGE); + /* find directory */ + if ((*argv[2] != '/') || !(file = strrchr (argv[2],'/')) || !file[1]) + return die ("invalid path",EX_USAGE); + /* calculate lengths of directory and file */ + if (!(dlen = file - argv[2])) dlen = 1; + len = strlen (++file); + /* make buffers */ + dir = (char *) malloc (dlen + 1); + lock = (char *) malloc (len + 6); + hitch = (char *) malloc (len + 6 + 40 + MAXHOSTNAMELEN); + if (!dir || !lock || !hitch) return die ("malloc failure",errno); + strncpy (dir,argv[2],dlen); /* connect to desired directory */ + dir[dlen] = '\0'; + printf ("dir=%s, file=%s\n",dir,file); + chdir (dir); + /* get device/inode of file descriptor */ + if (fstat (atoi (argv[1]),&fsb)) return die ("fstat failure",errno); + /* better be a regular file */ + if ((fsb.st_mode & S_IFMT) != S_IFREG) + return die ("fd not regular file",EX_USAGE); + /* now get device/inode of file */ + if (lstat (file,&sb)) return die ("lstat failure",errno); + /* does it match? */ + if ((sb.st_mode & S_IFMT) != S_IFREG) + return die ("name not regular file",EX_USAGE); + if ((sb.st_dev != fsb.st_dev) || (sb.st_ino != fsb.st_ino)) + return die ("fd and name different",EX_USAGE); + /* build lock filename */ + sprintf (lock,"%s.lock",file); + if (!lstat (lock,&sb) && ((sb.st_mode & S_IFMT) != S_IFREG)) + return die ("existing lock not regular file",EX_NOPERM); + + do { /* until OK or out of tries */ + if (!stat (lock,&sb) && (time (0) > (sb.st_ctime + LOCKTIMEOUT * 60))) + unlink (lock); /* time out lock if enough time has passed */ + /* SUN-OS had an NFS + * As kludgy as an albatross; + * And everywhere that it was installed, + * It was a total loss. + * -- MRC 9/25/91 + */ + /* build hitching post file name */ + sprintf (hitch,"%s.%lu.%lu.",lock,(unsigned long) time (0), + (unsigned long) getpid ()); + len = strlen (hitch); /* append local host name */ + gethostname (hitch + len,MAXHOSTNAMELEN); + /* try to get hitching-post file */ + if ((ld = open (hitch,O_WRONLY|O_CREAT|O_EXCL,LOCKPROTECTION)) >= 0) { + /* make sure others can break the lock */ + chmod (hitch,LOCKPROTECTION); + /* get device/inode of hitch file */ + if (fstat (ld,&fsb)) return die ("hitch fstat failure",errno); + close (ld); /* close the hitching-post */ + /* Note: link() may return an error even if it actually succeeded. So we + * always check for success via the link count, and ignore the error if + * the link count is right. + */ + /* tie hitching-post to lock */ + i = link (hitch,lock) ? errno : 0; + /* success if link count now 2 */ + if (stat (hitch,&sb) || (sb.st_nlink != 2) || + (fsb.st_dev != sb.st_dev) || (fsb.st_ino != sb.st_ino)) { + ld = -1; /* failed to hitch */ + if (i == EPERM) { /* was it because links not allowed? */ + /* Probably a FAT filesystem on Linux. It can't be NFS, so try + * creating the lock file directly. + */ + if ((ld = open (lock,O_WRONLY|O_CREAT|O_EXCL,LOCKPROTECTION)) >= 0) { + /* get device/inode of lock file */ + if (fstat (ld,&fsb)) return die ("lock fstat failure",errno); + close (ld); /* close the file */ + } + /* give up immediately if protection failure */ + else if (errno != EEXIST) tries = 0; + } + } + unlink (hitch); /* flush hitching post */ + } + /* give up immediately if protection failure */ + else if (errno == EACCES) tries = 0; + if (ld < 0) { /* lock failed */ + if (tries--) sleep (1); /* sleep 1 second and try again */ + else { + write (1,"-",1); /* hard failure */ + return EX_CANTCREAT; + } + } + } while (ld < 0); + write (1,"+",1); /* indicate that all is well */ + read (0,tmp,1); /* read continue signal from parent */ + /* flush the lock file */ + if (!stat (lock,&sb) && (fsb.st_dev == sb.st_dev) && + (fsb.st_ino == sb.st_ino)) unlink (lock); + else syslog (LOG_NOTICE,"lock file %s/%s changed dev/inode",dir,lock); + return EX_OK; +} |