diff lib-src/movemail.c @ 438:84b14dcb0985 r21-2-27

Import from CVS: tag r21-2-27
author cvs
date Mon, 13 Aug 2007 11:32:25 +0200
parents 3ecd8885ac67
children 8de8e3f6228a
line wrap: on
line diff
--- a/lib-src/movemail.c	Mon Aug 13 11:31:26 2007 +0200
+++ b/lib-src/movemail.c	Mon Aug 13 11:32:25 2007 +0200
@@ -1,35 +1,41 @@
 /* movemail foo bar -- move file foo to file bar,
-   locking file foo the way /bin/mail respects.
+   locking file foo.
    Copyright (C) 1986, 1992, 1993, 1994, 1996 Free Software Foundation, Inc.
 
-This file is part of GNU Emacs.
+This file is part of XEmacs.
 
-GNU Emacs is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
+XEmacs is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
 
-GNU Emacs is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
+XEmacs is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
 
 You should have received a copy of the GNU General Public License
-along with GNU Emacs; see the file COPYING.  If not, write to
+along with XEmacs; see the file COPYING.  If not, write to
 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  */
+Boston, MA 02111-1307, USA.
+
+ Please mail bugs and suggestions to the XEmacs maintainer.
+*/
 
-/* Important notice: defining MAIL_USE_FLOCK or MAIL_USE_LOCKF *will
-   cause loss of mail* if you do it on a system that does not normally
-   use flock as its way of interlocking access to inbox files.  The
-   setting of MAIL_USE_FLOCK and MAIL_USE_LOCKF *must agree* with the
-   system's own conventions.  It is not a choice that is up to you.
+/* Important notice:
+ *
+ *  You *must* coordinate the locking method used by movemail with that
+ *  used by your mail delivery agent, as well as that of the other mail
+ *  user agents on your system.  movemail allows you to do this at run
+ *  time via the -m flag.  Moreover, it uses a default determined by
+ *  the MAIL_LOCK_DOT, MAIL_LOCK_LOCKF, MAIL_LOCK_FLOCK,
+ *  MAIL_LOCK_LOCKING, and MAIL_LOCK_MMDF preprocessor settings.
+ */
 
-   So, if your system uses lock files rather than flock, then the only way
-   you can get proper operation is to enable movemail to write lockfiles there.
-   This means you must either give that directory access modes
-   that permit everyone to write lockfiles in it, or you must make movemail
-   a setuid or setgid program.  */
+/*
+ * Mike Sperber <sperber@informatik.uni-tuebingen.de> reorganized
+ * everything that has to with locking in December 1999.
+ */
 
 /*
  * Modified January, 1986 by Michael R. Gretzinger (Project Athena)
@@ -56,7 +62,7 @@
 
 #define NO_SHORTNAMES   /* Tell config not to load remap.h */
 #define DONT_ENCAPSULATE
-#include <../src/config.h>
+#include <config.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <stdio.h>
@@ -106,7 +112,7 @@
 #include <io.h>
 #endif /* WINDOWSNT */
 
-#if defined (HAVE_UNISTD_H) || defined (USG)
+#if defined (HAVE_UNISTD_H)
 #include <unistd.h>
 #endif /* unistd.h */
 #ifndef F_OK
@@ -116,23 +122,15 @@
 #define R_OK 4
 #endif /* No F_OK */
 
-#if defined (HAVE_FCNTL_H) || defined (USG)
+#if defined (HAVE_FCNTL_H)
 #include <fcntl.h>
 #endif /* fcntl.h */
 
-#if defined (XENIX) || defined (WINDOWSNT)
+#ifdef HAVE_LOCKING
 #include <sys/locking.h>
 #endif
 
-#ifdef MAIL_USE_LOCKF
-#define MAIL_USE_SYSTEM_LOCK
-#endif
-
-#ifdef MAIL_USE_FLOCK
-#define MAIL_USE_SYSTEM_LOCK
-#endif
-
-#ifdef MAIL_USE_MMDF
+#ifdef HAVE_MMDF
 extern int lk_open (), lk_close ();
 #endif
 
@@ -144,6 +142,7 @@
 
 static void fatal (char *, char*);
 static void error (char *, char *, char *);
+static void usage(int);
 static void pfatal_with_name (char *);
 static void pfatal_and_delete (char *);
 static char *concat (char *, char *, char *);
@@ -160,9 +159,6 @@
 			   struct re_pattern_buffer* regexp);
 #endif
 
-/* Nonzero means this is name of a lock file to delete on fatal error.  */
-char *delete_lockname;
-
 int verbose=0;
 #ifdef MAIL_USE_POP
 int reverse=0;
@@ -184,10 +180,35 @@
   { "regex",			required_argument,	   NULL,	'r' 	},
   { "match-lines",		required_argument,	   NULL,	'l' 	},
 #endif
+  { "lock-method",		required_argument,	   NULL,	'm' 	},
+  { "help",			no_argument,	   	   NULL,	'h' 	},
   { "verbose", 			no_argument,		   NULL,	'v'	},
   { 0 }
 };
 
+#define DOTLOCKING	0
+#define FLOCKING 	1
+#define LOCKFING	2
+#define MMDF   		3
+#define LOCKING         4
+
+#if defined(MAIL_LOCK_FLOCK) && defined(HAVE_FLOCK)
+#define DEFAULT_LOCKING FLOCKING
+#elif defined(MAIL_LOCK_LOCKF) && defined(HAVE_LOCKF)
+#define DEFAULT_LOCKING LOCKFING
+#elif defined(MAIL_LOCK_MMDF) && defined(HAVE_MMDF)
+#define DEFAULT_LOCKING MMDF
+#elif defined(MAIL_LOCK_LOCKING) && defined(HAVE_LOCKING)
+#define DEFAULT_LOCKING LOCKING
+#else
+#define DEFAULT_LOCKING DOTLOCKING
+#endif
+
+static void lock_dot(char *);
+static void unlock_dot(char *);
+static int parse_lock_method(char *);
+static char *unparse_lock_method(int);
+
 int
 main (int argc, char *argv[])
 {
@@ -198,23 +219,23 @@
   int status;
 #endif
 
-#ifndef MAIL_USE_SYSTEM_LOCK
-  struct stat st;
-  long now;
-  int tem;
-  char *lockname, *p;
-  char *tempname;
-  int desc;
-#endif /* not MAIL_USE_SYSTEM_LOCK */
+  int lock_method = DEFAULT_LOCKING;
+
+  char *maybe_lock_env;
 
-  delete_lockname = 0;
+  maybe_lock_env = getenv("EMACSLOCKMETHOD");
+  if (maybe_lock_env)
+    {
+      printf("maybe-lock_env: %s\n", maybe_lock_env);
+      lock_method = parse_lock_method(maybe_lock_env);
+    }
 
-  while (1)
+  for (;;)
     {
 #ifdef MAIL_USE_POP
-      char* optstring = "i:o:p:l:r:xvk";
+      char* optstring = "i:o:m:p:l:r:xvhk";
 #else
-      char* optstring = "i:o:v";
+      char* optstring = "i:o:m:vh";
 #endif
       int opt = getopt_long (argc, argv, optstring, longopts, 0);
   
@@ -255,7 +276,16 @@
 	  regexp_pattern = compile_regex (optarg);
 	  break;
 #endif
-	case 'v':		verbose = 1;	break;
+
+	case 'm':
+	  lock_method = parse_lock_method(optarg);
+	  break;
+	case 'h':
+	  usage(lock_method);
+	  exit(0);
+	case 'v':
+	  verbose = 1;
+	  break;
 	}
     }
 
@@ -272,17 +302,19 @@
     
   if (!inname || !outname)
     {
-      fprintf (stderr, "Usage: movemail [-rvxk] [-l lines ] [-i] inbox [-o] destfile [[-p] POP-password]\n");
+      usage(lock_method);
       exit(1);
     }
 
-#ifdef MAIL_USE_MMDF
-  mmdf_init (argv[0]);
+#ifdef HAVE_MMDF
+  if (lock_method == MMDF)
+    mmdf_init (argv[0]);
 #endif
 
   if (*outname == 0)
     fatal ("Destination file name is empty", 0);
 
+  VERBOSE(("checking access to output file\n"));
   /* Check access to output file.  */
   if (access (outname, F_OK) == 0 && access (outname, W_OK) != 0)
     pfatal_with_name (outname);
@@ -320,34 +352,238 @@
   if (access (inname, R_OK | W_OK) != 0)
     pfatal_with_name (inname);
 
-#ifndef MAIL_USE_MMDF
-#ifndef MAIL_USE_SYSTEM_LOCK
-  /* Use a lock file named after our first argument with .lock appended:
-     If it exists, the mail file is locked.  */
-  /* Note: this locking mechanism is *required* by the mailer
-     (on systems which use it) to prevent loss of mail.
+
+  if (fork () == 0)
+    {
+      setuid (getuid ());
+
+      VERBOSE(("opening input file\n"));
+
+      switch (lock_method)
+	{
+	case DOTLOCKING:
+	  indesc = open (inname, O_RDONLY);
+	  break;
+#ifdef HAVE_LOCKF
+	case LOCKFING:
+	  indesc = open (inname, O_RDWR);
+	  break;
+#endif
+#ifdef HAVE_FLOCK
+	case FLOCKING:
+	  indesc = open (inname, O_RDWR);
+	  break;
+#endif
+#ifdef HAVE_LOCKING
+	case LOCKING:
+	  indesc = open (inname, O_RDWR);
+	  break;
+#endif
+#ifdef HAVE_MMDF
+	case MMDF:
+	  indesc = lk_open (inname, O_RDONLY, 0, 0, 10);
+	  break;
+#endif
+	default: abort();
+	}
+
+      if (indesc < 0)
+	pfatal_with_name (inname);
+
+#ifdef HAVE_UMASK      
+      /* In case movemail is setuid to root, make sure the user can
+	 read the output file.  */
+      umask (umask (0) & 0333);
+#endif
+
+      outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
+      if (outdesc < 0)
+	pfatal_with_name (outname);
+
+      VERBOSE(("locking input file\n"));
 
-     On systems that use a lock file, extracting the mail without locking
-     WILL occasionally cause loss of mail due to timing errors!
+      switch (lock_method)
+	{
+#ifdef HAVE_LOCKF
+	case LOCKFING:
+	  if (lockf (indesc, F_LOCK, 0) < 0)
+	    pfatal_with_name (inname);
+	  break;
+#endif
+#ifdef HAVE_FLOCK
+	case FLOCKING:
+	  if (flock (indesc, LOCK_EX) < 0)
+	    pfatal_with_name (inname);
+	  break;
+#endif
+#ifdef HAVE_LOCKING
+	case LOCKING:
+	  if (locking (indesc, LK_RLCK, -1L) < 0)
+	    pfatal_with_name (inname);
+	  break;
+#endif
+	case DOTLOCKING:
+	  lock_dot(inname);
+	  break;
+	}
+
+      VERBOSE(("copying input file to output file\n"));
+	    
+      {
+	char buf[1024];
+
+	while (1)
+	  {
+	    nread = read (indesc, buf, sizeof buf);
+	    if (nread != write (outdesc, buf, nread))
+	      {
+		int saved_errno = errno;
+		unlink (outname);
+		errno = saved_errno;
+		pfatal_with_name (outname);
+	      }
+	    if (nread < sizeof buf)
+	      break;
+	  }
+      }
+
+#ifdef HAVE_FSYNC
+      if (fsync (outdesc) < 0)
+	pfatal_and_delete (outname);
+#endif
+
+      /* Check to make sure no errors before we zap the inbox.  */
+      if (close (outdesc) != 0)
+	pfatal_and_delete (outname);
+
+      VERBOSE(("deleting or truncating input file\n"));
 
-     So, if creation of the lock file fails
-     due to access permission on the mail spool directory,
-     you simply MUST change the permission
-     and/or make movemail a setgid program
-     so it can create lock files properly.
+      switch (lock_method)
+	{
+	case LOCKFING:
+	case FLOCKING:
+	case LOCKING:
+#ifdef HAVE_FTRUNCATE
+	  ftruncate (indesc, 0L);
+#else
+	  close (open (inname, O_CREAT | O_TRUNC | O_RDWR, 0666));
+#endif
+	  close (indesc);
+	  break;
+#ifdef HAVE_MMDF
+	case MMDF:
+	  lk_close (indesc, 0, 0, 0);
+	  break;
+#endif
+	case DOTLOCKING:
+	  creat (inname, 0600);
+	  break;
+	}
+
+      exit (0);
+    }
 
-     You might also wish to verify that your system is one
-     which uses lock files for this purpose.  Some systems use other methods.
+  wait (&status);
+  if (!WIFEXITED (status))
+    exit (1);
+  else if (WEXITSTATUS (status) != 0)
+    exit (WEXITSTATUS (status));
+
+  if (lock_method == DOTLOCKING)
+    unlock_dot(inname);
+
+#endif /* not DISABLE_DIRECT_ACCESS */
+
+  return 0;
+}
+
+static void
+usage(int lock_method)
+{
+  printf ("Usage: movemail [-rvxkh] [-l lines ] [-m method ] [-i] inbox [-o] destfile [[-p] POP-password]\n");
+  printf("where method is one of: dot");
+#ifdef HAVE_LOCKF
+  printf(", lockf");
+#endif
+#ifdef HAVE_FLOCK
+  printf(", flock");
+#endif
+#ifdef HAVE_MMDF
+  printf(", mmdf");
+#endif
+#ifdef HAVE_LOCKING
+  printf(", locking");
+#endif
+  printf("\nDefault is: %s\n", unparse_lock_method(lock_method));
+  
+}
 
-     If your system uses the `flock' system call for mail locking,
-     define MAIL_USE_SYSTEM_LOCK in config.h or the s-*.h file
-     and recompile movemail.  If the s- file for your system
-     should define MAIL_USE_SYSTEM_LOCK but does not, send a bug report
-     to bug-gnu-emacs@prep.ai.mit.edu so we can fix it.  */
+static char *
+unparse_lock_method(int lock_method)
+{
+  switch (lock_method)
+    {
+    case DOTLOCKING: return "dot";
+    case FLOCKING:   return "flock";
+    case LOCKFING:   return "lockf";
+    case LOCKING:    return "locking";
+    case MMDF:       return "mmdf";
+    default: abort();return 0;
+    }
+}
 
-  lockname = concat (inname, ".lock", "");
-  tempname = (char *) xmalloc (strlen (inname) + strlen ("EXXXXXX") + 1);
-  strcpy (tempname, inname);
+static int
+parse_lock_method(char *method_name)
+{
+  if (!strcmp("dot", method_name) || !strcmp("file", method_name))
+    return DOTLOCKING;
+#ifdef HAVE_LOCKF
+  else if (!strcmp("lockf", method_name))
+    return LOCKFING;
+#endif
+#ifdef HAVE_FLOCK
+  else if (!strcmp("flock", method_name))
+    return FLOCKING;
+#endif
+#ifdef HAVE_MMDF
+  else if (!strcmp("mmdf", method_name))
+    return MMDF;
+#endif
+#ifdef HAVE_LOCKING
+  else if (!strcmp("locking", method_name))
+    return LOCKING;
+#endif
+  else
+    fatal("invalid lock method: %s", method_name);
+  return 0; /* unreached */
+}
+
+static char *
+dot_filename(char *filename)
+{
+  return concat (filename, ".lock", "");
+}
+
+static char *dotlock_filename = NULL;
+
+static void
+lock_dot(char *filename)
+{
+  struct stat st;
+  long now;
+  int tem;
+  char *lockname, *p;
+  char *tempname;
+  int desc;
+
+  dotlock_filename = (char *) xmalloc(strlen(filename) + 1);
+
+  /* Use a lock file named after our first argument with .lock appended:
+     If it exists, the mail file is locked. */
+
+  lockname = dot_filename(filename);
+  tempname = (char *) xmalloc (strlen (filename) + strlen ("EXXXXXX") + 1);
+  strcpy (tempname, filename);
   p = tempname + strlen (tempname);
   while (p != tempname && !IS_DIRECTORY_SEP (p[-1]))
     p--;
@@ -356,7 +592,7 @@
   mktemp (tempname);
   unlink (tempname);
 
-  while (1)
+  for (;;)
     {
       /* Create the lock file, but not under the lock file name.  */
       /* Give up if cannot do that.  */
@@ -387,132 +623,28 @@
 	    unlink (lockname);
 	}
     }
-
-  delete_lockname = lockname;
-#endif /* not MAIL_USE_SYSTEM_LOCK */
-#endif /* not MAIL_USE_MMDF */
-
-  if (fork () == 0)
-    {
-      setuid (getuid ());
-
-#ifndef MAIL_USE_MMDF
-#ifdef MAIL_USE_SYSTEM_LOCK
-      indesc = open (inname, O_RDWR);
-#else  /* if not MAIL_USE_SYSTEM_LOCK */
-      indesc = open (inname, O_RDONLY);
-#endif /* not MAIL_USE_SYSTEM_LOCK */
-#else  /* MAIL_USE_MMDF */
-      indesc = lk_open (inname, O_RDONLY, 0, 0, 10);
-#endif /* MAIL_USE_MMDF */
-
-      if (indesc < 0)
-	pfatal_with_name (inname);
-
-#if defined (BSD) || defined (XENIX)
-      /* In case movemail is setuid to root, make sure the user can
-	 read the output file.  */
-      /* This is desirable for all systems
-	 but I don't want to assume all have the umask system call */
-      umask (umask (0) & 0333);
-#endif /* BSD or Xenix */
-      outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
-      if (outdesc < 0)
-	pfatal_with_name (outname);
-#ifdef MAIL_USE_SYSTEM_LOCK
-#ifdef MAIL_USE_LOCKF
-      if (lockf (indesc, F_LOCK, 0) < 0) pfatal_with_name (inname);
-#else /* not MAIL_USE_LOCKF */
-#ifdef XENIX
-      if (locking (indesc, LK_RLCK, 0L) < 0) pfatal_with_name (inname);
-#else
-#ifdef WINDOWSNT
-      if (locking (indesc, LK_RLCK, -1L) < 0) pfatal_with_name (inname);
-#else
-      if (flock (indesc, LOCK_EX) < 0) pfatal_with_name (inname);
-#endif
-#endif
-#endif /* not MAIL_USE_LOCKF */
-#endif /* MAIL_USE_SYSTEM_LOCK */
-
-      {
-	char buf[1024];
+  strcpy(dotlock_filename, filename);
+}
 
-	while (1)
-	  {
-	    nread = read (indesc, buf, sizeof buf);
-	    if (nread != write (outdesc, buf, nread))
-	      {
-		int saved_errno = errno;
-		unlink (outname);
-		errno = saved_errno;
-		pfatal_with_name (outname);
-	      }
-	    if (nread < sizeof buf)
-	      break;
-	  }
-      }
-
-#ifdef BSD
-      if (fsync (outdesc) < 0)
-	pfatal_and_delete (outname);
-#endif
-
-      /* Check to make sure no errors before we zap the inbox.  */
-      if (close (outdesc) != 0)
-	pfatal_and_delete (outname);
-
-#ifdef MAIL_USE_SYSTEM_LOCK
-#if defined (STRIDE) || defined (XENIX) || defined (WINDOWSNT)
-      /* Stride, xenix have file locking, but no ftruncate.  This mess will do. */
-      close (open (inname, O_CREAT | O_TRUNC | O_RDWR, 0666));
-#else
-      ftruncate (indesc, 0L);
-#endif /* STRIDE or XENIX */
-#endif /* MAIL_USE_SYSTEM_LOCK */
+static void
+unlock_dot(char *filename)
+{
+  unlink(dot_filename(filename));
+}
 
-#ifdef MAIL_USE_MMDF
-      lk_close (indesc, 0, 0, 0);
-#else
-      close (indesc);
-#endif
-
-#ifndef MAIL_USE_SYSTEM_LOCK
-      /* Delete the input file; if we can't, at least get rid of its
-	 contents.  */
-#ifdef MAIL_UNLINK_SPOOL
-      /* This is generally bad to do, because it destroys the permissions
-	 that were set on the file.  Better to just empty the file.  */
-      if (unlink (inname) < 0 && errno != ENOENT)
-#endif /* MAIL_UNLINK_SPOOL */
-	creat (inname, 0600);
-#endif /* not MAIL_USE_SYSTEM_LOCK */
+static void
+maybe_unlock_dot(void)
+{
+  if (dotlock_filename)
+    unlock_dot(dotlock_filename);
+}
 
-      exit (0);
-    }
-
-  wait (&status);
-  if (!WIFEXITED (status))
-    exit (1);
-  else if (WEXITSTATUS (status) != 0)
-    exit (WEXITSTATUS (status));
-
-#if !defined (MAIL_USE_MMDF) && !defined (MAIL_USE_SYSTEM_LOCK)
-  unlink (lockname);
-#endif /* not MAIL_USE_MMDF and not MAIL_USE_SYSTEM_LOCK */
-
-#endif /* ! DISABLE_DIRECT_ACCESS */
-
-  return 0;
-}
-
 /* Print error message and exit.  */
 
 static void
 fatal (char *s1, char *s2)
 {
-  if (delete_lockname)
-    unlink (delete_lockname);
+  maybe_unlock_dot();
   error (s1, s2, NULL);
   exit (1);
 }
@@ -568,7 +700,7 @@
     fatal ("virtual memory exhausted", 0);
   return result;
 }
-
+
 /* This is the guts of the interface to the Post Office Protocol.  */
 
 #ifdef MAIL_USE_POP
@@ -605,7 +737,7 @@
   FILE *mbf;
   popserver server;
 
-  VERBOSE(("opening server\r"));
+  VERBOSE(("opening server\n"));
   server = pop_open (0, user, password, POP_NO_GETPASS);
   if (! server)
     {
@@ -613,7 +745,7 @@
       return (1);
     }
 
-  VERBOSE(("stat'ing messages\r"));
+  VERBOSE(("stat'ing messages\n"));
   if (pop_stat (server, &nmsgs, &nbytes))
     {
       error (pop_error, NULL, NULL);
@@ -654,13 +786,13 @@
   for (idx = 0; idx < nmsgs; idx++)
     {
       i = reverse ? nmsgs - idx : idx + 1;
-      VERBOSE(("checking message %d     \r", i));
+      VERBOSE(("checking message %d     \n", i));
       
       if (!regexp_pattern 
 	  || 
 	  pop_search_top (server, i, match_lines, regexp_pattern) == POP_RETRIEVED)
 	{
-	  VERBOSE(("retrieving message %d     \r", i));
+	  VERBOSE(("retrieving message %d     \n", i));
           mbx_delimit_begin (mbf);
 	  if (pop_retr (server, i, mbx_write, mbf) != POP_RETRIEVED)
 	    {
@@ -689,7 +821,7 @@
    *      directories have lost mail when over quota because these checks were
    *      not made in previous versions of movemail. */
 
-#ifdef BSD
+#ifdef HAVE_FSYNC
   if (fsync (mbfi) < 0)
     {
       error ("Error in fsync: %s", strerror (errno), NULL);
@@ -709,7 +841,7 @@
 	{
 	  if (retrieved_list[i] == 1)
 	    {
-	      VERBOSE(("deleting message %d     \r", i));
+	      VERBOSE(("deleting message %d     \n", i));
 	      if (pop_delete (server, i))
 		{
 		  error (pop_error, NULL, NULL);
@@ -877,7 +1009,7 @@
 
 
 #endif /* MAIL_USE_POP */
-
+
 #ifndef HAVE_STRERROR
 char *
 strerror (int errnum)