⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 flocksim.c

📁 广泛使用的邮件服务器!同时
💻 C
📖 第 1 页 / 共 2 页
字号:
/* ======================================================================== * Copyright 1988-2007 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:	flock emulation via fcntl() locking * * 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:	10 April 2001 * Last Edited:	11 October 2007 */ #undef flock			/* name is used as a struct for fcntl */#ifndef NOFSTATVFS		/* thank you, SUN.  NOT! */# ifndef NOFSTATVFS64#  ifndef _LARGEFILE64_SOURCE#   define _LARGEFILE64_SOURCE#  endif	/* _LARGEFILE64_SOURCE */# endif		/* NOFSTATVFFS64 */#include <sys/statvfs.h>#endif		/* NOFSTATVFS */#ifndef NSIG			/* don't know if this can happen */#define NSIG 32			/* a common maximum */#endif/* Emulator for flock() call * Accepts: file descriptor *	    operation bitmask * Returns: 0 if successful, -1 if failure under BSD conditions */int flocksim (int fd,int op){  char tmp[MAILTMPLEN];  int logged = 0;  struct stat sbuf;  struct ustat usbuf;  struct flock fl;				/* lock zero bytes at byte 0 */  fl.l_whence = SEEK_SET; fl.l_start = fl.l_len = 0;  fl.l_pid = getpid ();		/* shouldn't be necessary */  switch (op & ~LOCK_NB) {	/* translate to fcntl() operation */  case LOCK_EX:			/* exclusive */    fl.l_type = F_WRLCK;    break;  case LOCK_SH:			/* shared */    fl.l_type = F_RDLCK;    break;  case LOCK_UN:			/* unlock */    fl.l_type = F_UNLCK;    break;  default:			/* default */    errno = EINVAL;    return -1;  }				/* always return success if disabled */  if (mail_parameters (NIL,GET_DISABLEFCNTLLOCK,NIL)) return 0;  /*  Make fcntl() locking of NFS files be a no-op the way it is with flock()   * on BSD.  This is because the rpc.statd/rpc.lockd daemons don't work very   * well and cause cluster-wide hangs if you exercise them at all.  The   * result of this is that you lose the ability to detect shared mail_open()   * on NFS-mounted files.  If you are wise, you'll use IMAP instead of NFS   * for mail files.   *   *  Sun alleges that it doesn't matter, and that they have fixed all the   * rpc.statd/rpc.lockd bugs.  As of October 2006, that is still false.   *   *  We need three tests for three major historical variants in SVR4:   *  1) In NFSv2, ustat() would return -1 in f_tinode for NFS.   *  2) When fstatvfs() was introduced with NFSv3, ustat() was "fixed".   *  3) When 64-bit filesystems were introduced, fstatvfs() would return   *	 EOVERFLOW; you have to use fstatvfs64() even though you don't care   *	 about any of the affected values.   *   * We can't use fstatfs() because fstatfs():   * . is documented as being deprecated in SVR4.   * . has inconsistent calling conventions (there are two additional int   *   arguments on Solaris and I don't know what they do).   * . returns inconsistent statfs structs.  On Solaris, the file system type   *   is a short called f_fstyp.  On AIX, it's an int called f_type that is   *   documented as always being 0!   *   * For what it's worth, here's the scoop on fstatfs() elsewhere:   *   *  On Linux, the file system type is a long called f_type that has a file   * system type code.  A different module (flocklnx.c) uses this because   * some knothead "improved" flock() to return ENOLCK on NFS files instead   * of being a successful no-op.  This "improvement" apparently has been   * reverted, but not before it got to many systems in the field.   *   *  On BSD, it's a short called either f_otype or f_type that is documented   * as always being zero.  Fortunately, BSD has flock() the way it's supposed   * to be, and none of this nonsense is necessary.   */  if (!fstat (fd,&sbuf))	{ /* no hope of working if can't fstat()! */    /* Any base type that begins with "nfs" or "afs" is considered to be a     * network filesystem.     */#ifndef NOFSTATVFS    struct statvfs vsbuf;#ifndef NOFSTATVFS64    struct statvfs64 vsbuf64;    if (!fstatvfs64 (fd,&vsbuf64) && (vsbuf64.f_basetype[1] == 'f') &&	(vsbuf64.f_basetype[2] == 's') &&	((vsbuf64.f_basetype[0] == 'n') || (vsbuf64.f_basetype[0] == 'a')))      return 0;#endif		/* NOFSTATVFS64 */    if (!fstatvfs (fd,&vsbuf) && (vsbuf.f_basetype[1] == 'f') &&	(vsbuf.f_basetype[2] == 's') &&	((vsbuf.f_basetype[0] == 'n') || (vsbuf.f_basetype[0] == 'a')))      return 0;#endif		/* NOFSTATVFS */    if (!ustat (sbuf.st_dev,&usbuf) && !++usbuf.f_tinode) return 0;  }				/* do the lock */  while (fcntl (fd,(op & LOCK_NB) ? F_SETLK : F_SETLKW,&fl))    if (errno != EINTR) {      /* Can't use switch here because these error codes may resolve to the       * same value on some systems.       */      if ((errno != EWOULDBLOCK) && (errno != EAGAIN) && (errno != EACCES)) {	sprintf (tmp,"Unexpected file locking failure: %.100s",		 strerror (errno));				/* give the user a warning of what happened */	MM_NOTIFY (NIL,tmp,WARN);	if (!logged++) syslog (LOG_ERR,"%s",tmp);	if (op & LOCK_NB) return -1;	sleep (5);		/* slow things down for loops */      }				/* return failure for non-blocking lock */      else if (op & LOCK_NB) return -1;    }  return 0;			/* success */}/* Master/slave procedures for safe fcntl() locking. * *  The purpose of this nonsense is to work around a bad bug in fcntl() * locking.  The cretins who designed it decided that a close() should * release any locks made by that process on the file opened on that * file descriptor.  Never mind that the lock wasn't made on that file * descriptor, but rather on some other file descriptor. * *  This bug is on every implementation of fcntl() locking that I have * tested.  Fortunately, on BSD systems, OSF/1, and Linux, we can use the * flock() system call which doesn't have this bug. * *  Note that OSF/1, Linux, and some BSD systems have both broken fcntl() * locking and the working flock() locking. * *  The program below can be used to demonstrate this problem.  Be sure to * let it run long enough for all the sleep() calls to finish. */#if 0#include <stdio.h>#include <unistd.h>#include <fcntl.h>#include <errno.h>#include <string.h>#include <sys/file.h>main (){  struct flock fl;  int fd,fd2;  char *file = "a.a";  if ((fd = creat (file,0666)) < 0)    perror ("TEST FAILED: can't create test file"),_exit (errno);  close (fd);  if (fork ()) {		/* parent */    if ((fd = open (file,O_RDWR,0)) < 0) abort();				/* lock applies to entire file */    fl.l_whence = fl.l_start = fl.l_len = 0;    fl.l_pid = getpid ();	/* shouldn't be necessary */    fl.l_type = F_RDLCK;    if (fcntl (fd,F_SETLKW,&fl) == -1) abort ();    sleep (5);    if ((fd2 = open (file,O_RDWR,0)) < 0) abort ();    sleep (1);    puts ("parent test ready -- will hang here if locking works correctly");    close (fd2);    wait (0);    puts ("OS BUG: child terminated");    _exit (0);  }  else {			/* child */    sleep (2);    if ((fd = open (file,O_RDWR,0666)) < 0) abort ();    puts ("child test ready -- child will hang if no bug");				/* lock applies to entire file */    fl.l_whence = fl.l_start = fl.l_len = 0;    fl.l_pid = getpid ();	/* shouldn't be necessary */    fl.l_type = F_WRLCK;    if (fcntl (fd,F_SETLKW,&fl) == -1) abort ();    puts ("OS BUG: child got lock");  }}#endif/*  Beware of systems such as AIX which offer flock() as a compatibility * function that is just a jacket into fcntl() locking.  The program below * is a variant of the program above, only using flock().  It can be used * to test to see if your system has real flock() or just a jacket into * fcntl(). * *  Be sure to let it run long enough for all the sleep() calls to finish. * If the program hangs, then flock() works and you can dispense with the * use of this module (you lucky person!). */#if 0#include <stdio.h>#include <errno.h>#include <string.h>#include <sys/file.h>main (){  int fd,fd2;  char *file = "a.a";  if ((fd = creat (file,0666)) < 0)    perror ("TEST FAILED: can't create test file"),_exit (errno);  close (fd);  if (fork ()) {		/* parent */    if ((fd = open (file,O_RDWR,0)) < 0) abort();    if (flock (fd,LOCK_SH) == -1) abort ();    sleep (5);    if ((fd2 = open (file,O_RDWR,0)) < 0) abort ();    sleep (1);    puts ("parent test ready -- will hang here if flock() works correctly");    close (fd2);    wait (0);    puts ("OS BUG: child terminated");    _exit (0);  }  else {			/* child */    sleep (2);    if ((fd = open (file,O_RDWR,0666)) < 0) abort ();    puts ("child test ready -- child will hang if no bug");    if (flock (fd,LOCK_EX) == -1) abort ();    puts ("OS BUG: child got lock");  }}#endif/* Master/slave details * *  On broken systems, we invoke an inferior fork to execute any driver * dispatches which are likely to tickle this bug; specifically, any * dispatch which may fiddle with a mailbox that is already selected.  As * of this writing, these are: delete, rename, status, scan, copy, and append. * *  Delete and rename are pretty marginal, yet there are certain clients * (e.g. Outlook Express) that really want to delete or rename the selected * mailbox.  The same is true of status, but there are people (such as the * authors of Entourage) who don't understand why status of the selected * mailbox is bad news. * *  However, in copy and append it is reasonable to do this to a selected * mailbox.  Although scanning the selected mailbox isn't particularly * sensible, it's hard to avoid due to wildcards. * *  It is still possible for an application to trigger the bug by doing * mail_open() on the same mailbox twice.  Don't do it. * *  Once the slave is invoked, the master only has to read events from the * slave's output (see below for these events) and translate these events * to the appropriate c-client callback.  When end of file occurs on the pipe, * the master reads the slave's exit status and uses that as the function * return.  The append master is slightly more complicated because it has to * send data back to the slave (see below). * *  The slave takes callback events from the driver which otherwise would * pass to the main program.  Only those events which a slave can actually * encounter are covered here; for example mm_searched() and mm_list() are * not covered since a slave never does the operations that trigger these. * Certain other events (mm_exists(), mm_expunged(), mm_flags()) are discarded * by the slave since the master will generate these events for itself. * *  The other events cause the slave to write a newline-terminated string to * its output.  The first character of string indicates the event: S for * mm_status(), N for mm_notify(), L for mm_log(), C for mm_critical(), X for * mm_nocritical(), D for mm_diskerror(), F for mm_fatal(), and "A" for append * argument callback.  Most of these events also carry data, which carried as * text space-delimited in the string. * *  Append argument callback requires the master to provide the slave with * data in the slave's input.  The first thing that the master provides is * either a "+" (master has data for the slave) or a "-" (master has no data). * If the master has data, it will then send the flags, internal date, and * message text, each as <text octet count><SPACE><text>. *//*  It should be alright for lockslavep to be a global, since it will always * be zero in the master (which is where threads would be).  The slave won't * ever thread, since any driver which threads in its methods probably can't * use fcntl() locking so won't have DR_LOCKING in its driver flags  * *  lockslavep can not be a static, since it's used by the dispatch macros. */int lockslavep = 0;		/* non-zero means slave process for locking */static int lockproxycopy = 0;	/* non-zero means redo copy as proxy */FILE *slavein = NIL;		/* slave input */FILE *slaveout = NIL;		/* slave output *//* Common master * Accepts: permitted stream *	    append callback (append calls only, else NIL) *	    data for callback (append calls only, else NIL) * Returns: (master) T if slave succeeded, NIL if slave failed *	    (slave) NIL always, with lockslavep non-NIL */static long master (MAILSTREAM *stream,append_t af,void *data){  MAILSTREAM *st;  MAILSTATUS status;  STRING *message;  FILE *pi,*po;  blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);  long ret = NIL;  unsigned long i,j;  int c,pid,pipei[2],pipeo[2];  char *s,*t,event[MAILTMPLEN],tmp[MAILTMPLEN];  lockproxycopy = NIL;		/* not doing a lock proxycopy */				/* make pipe from slave */  if (pipe (pipei) < 0) mm_log ("Can't create input pipe",ERROR);  else if (pipe (pipeo) < 0) {    mm_log ("Can't create output pipe",ERROR);    close (pipei[0]); close (pipei[1]);  }  else if ((pid = fork ()) < 0) {/* make slave */    mm_log ("Can't create execution process",ERROR);    close (pipei[0]); close (pipei[1]);    close (pipeo[0]); close (pipeo[1]);  }  else if (lockslavep = !pid) {	/* are we slave or master? */    alarm (0);			/* slave doesn't have alarms or signals */    for (c = 0; c < NSIG; c++) signal (c,SIG_DFL);    if (!(slavein = fdopen (pipeo[0],"r")) ||	!(slaveout = fdopen (pipei[1],"w")))      fatal ("Can't do slave pipe buffered I/O");    close (pipei[0]);		/* close parent's side of the pipes */    close (pipeo[1]);  }  else {			/* master process */    void *blockdata = (*bn) (BLOCK_SENSITIVE,NIL);    close (pipei[1]);		/* close slave's side of the pipes */    close (pipeo[0]);    if (!(pi = fdopen (pipei[0],"r")) || !(po = fdopen (pipeo[1],"w")))      fatal ("Can't do master pipe buffered I/O");				/* do slave events until EOF */				/* read event */    while (fgets (event,MAILTMPLEN-1,pi)) {      if (!(s = strchr (event,'\n'))) {	sprintf (tmp,"Execution process event string too long: %.500s",event);	fatal (tmp);      }      *s = '\0';		/* tie off event at end of line */      switch (event[0]) {	/* analyze event */      case 'A':			/* append callback */	if ((*af) (NIL,data,&s,&t,&message)) {	  if (i = message ? SIZE (message) : 0) {	    if (!s) s = "";	/* default values */	    if (!t) t = "";	  }	  else s = t = "";	/* no flags or date if no message */	  errno = NIL;		/* reset last error */				/* build response */	  if (fprintf (po,"+%lu %s%lu %s%lu ",strlen (s),s,strlen (t),t,i) < 0)	    fatal ("Failed to pipe append command");				/* write message text */	  if (i) do if (putc (c = 0xff & SNX (message),po) == EOF) {	    sprintf (tmp,"Failed to pipe %lu bytes (of %lu), last=%u: %.100s",		     i,message->size,c,strerror (errno));	    fatal (tmp);	  } while (--i);	}	else putc ('-',po);	/* append error */	fflush (po);	break;      case '&':			/* slave wants a proxycopy? */	lockproxycopy = T;	break;      case 'L':			/* mm_log() */	i = strtoul (event+1,&s,10);	if (!s || (*s++ != ' ')) {	  sprintf (tmp,"Invalid log event arguments: %.500s",event);	  fatal (tmp);	}	mm_log (s,i);	break;      case 'N':			/* mm_notify() */	st = (MAILSTREAM *) strtoul (event+1,&s,16);	if (s && (*s++ == ' ')) {	  i = strtoul (s,&s,10);/* get severity */	  if (s && (*s++ == ' ')) {	    mm_notify ((st == stream) ? stream : NIL,s,i);	    break;	  }	}	sprintf (tmp,"Invalid notify event arguments: %.500s",event);	fatal (tmp);      case 'S':			/* mm_status() */	st = (MAILSTREAM *) strtoul (event+1,&s,16);	if (s && (*s++ == ' ')) {	  status.flags = strtoul (s,&s,10);	  if (s && (*s++ == ' ')) {	    status.messages = strtoul (s,&s,10);	    if (s && (*s++ == ' ')) {	      status.recent = strtoul (s,&s,10);	      if (s && (*s++ == ' ')) {		status.unseen = strtoul (s,&s,10);		if (s && (*s++ == ' ')) {		  status.uidnext = strtoul (s,&s,10);		  if (s && (*s++ == ' ')) {		    status.uidvalidity = strtoul (s,&s,10);		    if (s && (*s++ == ' ')) {		      mm_status ((st == stream) ? stream : NIL,s,&status);		      break;		    }		  }		}	      }	    }	  }	}	sprintf (tmp,"Invalid status event arguments: %.500s",event);	fatal (tmp);      case 'C':			/* mm_critical() */	st = (MAILSTREAM *) strtoul (event+1,&s,16);	mm_critical ((st == stream) ? stream : NIL);	break;      case 'X':			/* mm_nocritical() */	st = (MAILSTREAM *) strtoul (event+1,&s,16);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -