imapext-2007

diff src/osdep/unix/flocksim.c @ 0:ada5e610ab86

imap-2007e
author yuuji@gentei.org
date Mon, 14 Sep 2009 15:17:45 +0900
parents
children
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/osdep/unix/flocksim.c	Mon Sep 14 15:17:45 2009 +0900
     1.3 @@ -0,0 +1,920 @@
     1.4 +/* ========================================================================
     1.5 + * Copyright 1988-2007 University of Washington
     1.6 + *
     1.7 + * Licensed under the Apache License, Version 2.0 (the "License");
     1.8 + * you may not use this file except in compliance with the License.
     1.9 + * You may obtain a copy of the License at
    1.10 + *
    1.11 + *     http://www.apache.org/licenses/LICENSE-2.0
    1.12 + *
    1.13 + * 
    1.14 + * ========================================================================
    1.15 + */
    1.16 +
    1.17 +/*
    1.18 + * Program:	flock emulation via fcntl() locking
    1.19 + *
    1.20 + * Author:	Mark Crispin
    1.21 + *		Networks and Distributed Computing
    1.22 + *		Computing & Communications
    1.23 + *		University of Washington
    1.24 + *		Administration Building, AG-44
    1.25 + *		Seattle, WA  98195
    1.26 + *		Internet: MRC@CAC.Washington.EDU
    1.27 + *
    1.28 + * Date:	10 April 2001
    1.29 + * Last Edited:	11 October 2007
    1.30 + */
    1.31 + 
    1.32 +#undef flock			/* name is used as a struct for fcntl */
    1.33 +
    1.34 +#ifndef NOFSTATVFS		/* thank you, SUN.  NOT! */
    1.35 +# ifndef NOFSTATVFS64
    1.36 +#  ifndef _LARGEFILE64_SOURCE
    1.37 +#   define _LARGEFILE64_SOURCE
    1.38 +#  endif	/* _LARGEFILE64_SOURCE */
    1.39 +# endif		/* NOFSTATVFFS64 */
    1.40 +#include <sys/statvfs.h>
    1.41 +#endif		/* NOFSTATVFS */
    1.42 +
    1.43 +#ifndef NSIG			/* don't know if this can happen */
    1.44 +#define NSIG 32			/* a common maximum */
    1.45 +#endif
    1.46 +
    1.47 +/* Emulator for flock() call
    1.48 + * Accepts: file descriptor
    1.49 + *	    operation bitmask
    1.50 + * Returns: 0 if successful, -1 if failure under BSD conditions
    1.51 + */
    1.52 +
    1.53 +int flocksim (int fd,int op)
    1.54 +{
    1.55 +  char tmp[MAILTMPLEN];
    1.56 +  int logged = 0;
    1.57 +  struct stat sbuf;
    1.58 +  struct ustat usbuf;
    1.59 +  struct flock fl;
    1.60 +				/* lock zero bytes at byte 0 */
    1.61 +  fl.l_whence = SEEK_SET; fl.l_start = fl.l_len = 0;
    1.62 +  fl.l_pid = getpid ();		/* shouldn't be necessary */
    1.63 +  switch (op & ~LOCK_NB) {	/* translate to fcntl() operation */
    1.64 +  case LOCK_EX:			/* exclusive */
    1.65 +    fl.l_type = F_WRLCK;
    1.66 +    break;
    1.67 +  case LOCK_SH:			/* shared */
    1.68 +    fl.l_type = F_RDLCK;
    1.69 +    break;
    1.70 +  case LOCK_UN:			/* unlock */
    1.71 +    fl.l_type = F_UNLCK;
    1.72 +    break;
    1.73 +  default:			/* default */
    1.74 +    errno = EINVAL;
    1.75 +    return -1;
    1.76 +  }
    1.77 +				/* always return success if disabled */
    1.78 +  if (mail_parameters (NIL,GET_DISABLEFCNTLLOCK,NIL)) return 0;
    1.79 +
    1.80 +  /*  Make fcntl() locking of NFS files be a no-op the way it is with flock()
    1.81 +   * on BSD.  This is because the rpc.statd/rpc.lockd daemons don't work very
    1.82 +   * well and cause cluster-wide hangs if you exercise them at all.  The
    1.83 +   * result of this is that you lose the ability to detect shared mail_open()
    1.84 +   * on NFS-mounted files.  If you are wise, you'll use IMAP instead of NFS
    1.85 +   * for mail files.
    1.86 +   *
    1.87 +   *  Sun alleges that it doesn't matter, and that they have fixed all the
    1.88 +   * rpc.statd/rpc.lockd bugs.  As of October 2006, that is still false.
    1.89 +   *
    1.90 +   *  We need three tests for three major historical variants in SVR4:
    1.91 +   *  1) In NFSv2, ustat() would return -1 in f_tinode for NFS.
    1.92 +   *  2) When fstatvfs() was introduced with NFSv3, ustat() was "fixed".
    1.93 +   *  3) When 64-bit filesystems were introduced, fstatvfs() would return
    1.94 +   *	 EOVERFLOW; you have to use fstatvfs64() even though you don't care
    1.95 +   *	 about any of the affected values.
    1.96 +   *
    1.97 +   * We can't use fstatfs() because fstatfs():
    1.98 +   * . is documented as being deprecated in SVR4.
    1.99 +   * . has inconsistent calling conventions (there are two additional int
   1.100 +   *   arguments on Solaris and I don't know what they do).
   1.101 +   * . returns inconsistent statfs structs.  On Solaris, the file system type
   1.102 +   *   is a short called f_fstyp.  On AIX, it's an int called f_type that is
   1.103 +   *   documented as always being 0!
   1.104 +   *
   1.105 +   * For what it's worth, here's the scoop on fstatfs() elsewhere:
   1.106 +   *
   1.107 +   *  On Linux, the file system type is a long called f_type that has a file
   1.108 +   * system type code.  A different module (flocklnx.c) uses this because
   1.109 +   * some knothead "improved" flock() to return ENOLCK on NFS files instead
   1.110 +   * of being a successful no-op.  This "improvement" apparently has been
   1.111 +   * reverted, but not before it got to many systems in the field.
   1.112 +   *
   1.113 +   *  On BSD, it's a short called either f_otype or f_type that is documented
   1.114 +   * as always being zero.  Fortunately, BSD has flock() the way it's supposed
   1.115 +   * to be, and none of this nonsense is necessary.
   1.116 +   */
   1.117 +  if (!fstat (fd,&sbuf))	{ /* no hope of working if can't fstat()! */
   1.118 +    /* Any base type that begins with "nfs" or "afs" is considered to be a
   1.119 +     * network filesystem.
   1.120 +     */
   1.121 +#ifndef NOFSTATVFS
   1.122 +    struct statvfs vsbuf;
   1.123 +#ifndef NOFSTATVFS64
   1.124 +    struct statvfs64 vsbuf64;
   1.125 +    if (!fstatvfs64 (fd,&vsbuf64) && (vsbuf64.f_basetype[1] == 'f') &&
   1.126 +	(vsbuf64.f_basetype[2] == 's') &&
   1.127 +	((vsbuf64.f_basetype[0] == 'n') || (vsbuf64.f_basetype[0] == 'a')))
   1.128 +      return 0;
   1.129 +#endif		/* NOFSTATVFS64 */
   1.130 +    if (!fstatvfs (fd,&vsbuf) && (vsbuf.f_basetype[1] == 'f') &&
   1.131 +	(vsbuf.f_basetype[2] == 's') &&
   1.132 +	((vsbuf.f_basetype[0] == 'n') || (vsbuf.f_basetype[0] == 'a')))
   1.133 +      return 0;
   1.134 +#endif		/* NOFSTATVFS */
   1.135 +    if (!ustat (sbuf.st_dev,&usbuf) && !++usbuf.f_tinode) return 0;
   1.136 +  }
   1.137 +
   1.138 +				/* do the lock */
   1.139 +  while (fcntl (fd,(op & LOCK_NB) ? F_SETLK : F_SETLKW,&fl))
   1.140 +    if (errno != EINTR) {
   1.141 +      /* Can't use switch here because these error codes may resolve to the
   1.142 +       * same value on some systems.
   1.143 +       */
   1.144 +      if ((errno != EWOULDBLOCK) && (errno != EAGAIN) && (errno != EACCES)) {
   1.145 +	sprintf (tmp,"Unexpected file locking failure: %.100s",
   1.146 +		 strerror (errno));
   1.147 +				/* give the user a warning of what happened */
   1.148 +	MM_NOTIFY (NIL,tmp,WARN);
   1.149 +	if (!logged++) syslog (LOG_ERR,"%s",tmp);
   1.150 +	if (op & LOCK_NB) return -1;
   1.151 +	sleep (5);		/* slow things down for loops */
   1.152 +      }
   1.153 +				/* return failure for non-blocking lock */
   1.154 +      else if (op & LOCK_NB) return -1;
   1.155 +    }
   1.156 +  return 0;			/* success */
   1.157 +}
   1.158 +
   1.159 +/* Master/slave procedures for safe fcntl() locking.
   1.160 + *
   1.161 + *  The purpose of this nonsense is to work around a bad bug in fcntl()
   1.162 + * locking.  The cretins who designed it decided that a close() should
   1.163 + * release any locks made by that process on the file opened on that
   1.164 + * file descriptor.  Never mind that the lock wasn't made on that file
   1.165 + * descriptor, but rather on some other file descriptor.
   1.166 + *
   1.167 + *  This bug is on every implementation of fcntl() locking that I have
   1.168 + * tested.  Fortunately, on BSD systems, OSF/1, and Linux, we can use the
   1.169 + * flock() system call which doesn't have this bug.
   1.170 + *
   1.171 + *  Note that OSF/1, Linux, and some BSD systems have both broken fcntl()
   1.172 + * locking and the working flock() locking.
   1.173 + *
   1.174 + *  The program below can be used to demonstrate this problem.  Be sure to
   1.175 + * let it run long enough for all the sleep() calls to finish.
   1.176 + */
   1.177 +
   1.178 +#if 0
   1.179 +#include <stdio.h>
   1.180 +#include <unistd.h>
   1.181 +#include <fcntl.h>
   1.182 +#include <errno.h>
   1.183 +#include <string.h>
   1.184 +#include <sys/file.h>
   1.185 +
   1.186 +main ()
   1.187 +{
   1.188 +  struct flock fl;
   1.189 +  int fd,fd2;
   1.190 +  char *file = "a.a";
   1.191 +  if ((fd = creat (file,0666)) < 0)
   1.192 +    perror ("TEST FAILED: can't create test file"),_exit (errno);
   1.193 +  close (fd);
   1.194 +  if (fork ()) {		/* parent */
   1.195 +    if ((fd = open (file,O_RDWR,0)) < 0) abort();
   1.196 +				/* lock applies to entire file */
   1.197 +    fl.l_whence = fl.l_start = fl.l_len = 0;
   1.198 +    fl.l_pid = getpid ();	/* shouldn't be necessary */
   1.199 +    fl.l_type = F_RDLCK;
   1.200 +    if (fcntl (fd,F_SETLKW,&fl) == -1) abort ();
   1.201 +    sleep (5);
   1.202 +    if ((fd2 = open (file,O_RDWR,0)) < 0) abort ();
   1.203 +    sleep (1);
   1.204 +    puts ("parent test ready -- will hang here if locking works correctly");
   1.205 +    close (fd2);
   1.206 +    wait (0);
   1.207 +    puts ("OS BUG: child terminated");
   1.208 +    _exit (0);
   1.209 +  }
   1.210 +  else {			/* child */
   1.211 +    sleep (2);
   1.212 +    if ((fd = open (file,O_RDWR,0666)) < 0) abort ();
   1.213 +    puts ("child test ready -- child will hang if no bug");
   1.214 +				/* lock applies to entire file */
   1.215 +    fl.l_whence = fl.l_start = fl.l_len = 0;
   1.216 +    fl.l_pid = getpid ();	/* shouldn't be necessary */
   1.217 +    fl.l_type = F_WRLCK;
   1.218 +    if (fcntl (fd,F_SETLKW,&fl) == -1) abort ();
   1.219 +    puts ("OS BUG: child got lock");
   1.220 +  }
   1.221 +}
   1.222 +#endif
   1.223 +
   1.224 +/*  Beware of systems such as AIX which offer flock() as a compatibility
   1.225 + * function that is just a jacket into fcntl() locking.  The program below
   1.226 + * is a variant of the program above, only using flock().  It can be used
   1.227 + * to test to see if your system has real flock() or just a jacket into
   1.228 + * fcntl().
   1.229 + *
   1.230 + *  Be sure to let it run long enough for all the sleep() calls to finish.
   1.231 + * If the program hangs, then flock() works and you can dispense with the
   1.232 + * use of this module (you lucky person!).
   1.233 + */
   1.234 +
   1.235 +#if 0
   1.236 +#include <stdio.h>
   1.237 +#include <errno.h>
   1.238 +#include <string.h>
   1.239 +#include <sys/file.h>
   1.240 +
   1.241 +main ()
   1.242 +{
   1.243 +  int fd,fd2;
   1.244 +  char *file = "a.a";
   1.245 +  if ((fd = creat (file,0666)) < 0)
   1.246 +    perror ("TEST FAILED: can't create test file"),_exit (errno);
   1.247 +  close (fd);
   1.248 +  if (fork ()) {		/* parent */
   1.249 +    if ((fd = open (file,O_RDWR,0)) < 0) abort();
   1.250 +    if (flock (fd,LOCK_SH) == -1) abort ();
   1.251 +    sleep (5);
   1.252 +    if ((fd2 = open (file,O_RDWR,0)) < 0) abort ();
   1.253 +    sleep (1);
   1.254 +    puts ("parent test ready -- will hang here if flock() works correctly");
   1.255 +    close (fd2);
   1.256 +    wait (0);
   1.257 +    puts ("OS BUG: child terminated");
   1.258 +    _exit (0);
   1.259 +  }
   1.260 +  else {			/* child */
   1.261 +    sleep (2);
   1.262 +    if ((fd = open (file,O_RDWR,0666)) < 0) abort ();
   1.263 +    puts ("child test ready -- child will hang if no bug");
   1.264 +    if (flock (fd,LOCK_EX) == -1) abort ();
   1.265 +    puts ("OS BUG: child got lock");
   1.266 +  }
   1.267 +}
   1.268 +#endif
   1.269 +
   1.270 +/* Master/slave details
   1.271 + *
   1.272 + *  On broken systems, we invoke an inferior fork to execute any driver
   1.273 + * dispatches which are likely to tickle this bug; specifically, any
   1.274 + * dispatch which may fiddle with a mailbox that is already selected.  As
   1.275 + * of this writing, these are: delete, rename, status, scan, copy, and append.
   1.276 + *
   1.277 + *  Delete and rename are pretty marginal, yet there are certain clients
   1.278 + * (e.g. Outlook Express) that really want to delete or rename the selected
   1.279 + * mailbox.  The same is true of status, but there are people (such as the
   1.280 + * authors of Entourage) who don't understand why status of the selected
   1.281 + * mailbox is bad news.
   1.282 + *
   1.283 + *  However, in copy and append it is reasonable to do this to a selected
   1.284 + * mailbox.  Although scanning the selected mailbox isn't particularly
   1.285 + * sensible, it's hard to avoid due to wildcards.
   1.286 + *
   1.287 + *  It is still possible for an application to trigger the bug by doing
   1.288 + * mail_open() on the same mailbox twice.  Don't do it.
   1.289 + *
   1.290 + *  Once the slave is invoked, the master only has to read events from the
   1.291 + * slave's output (see below for these events) and translate these events
   1.292 + * to the appropriate c-client callback.  When end of file occurs on the pipe,
   1.293 + * the master reads the slave's exit status and uses that as the function
   1.294 + * return.  The append master is slightly more complicated because it has to
   1.295 + * send data back to the slave (see below).
   1.296 + *
   1.297 + *  The slave takes callback events from the driver which otherwise would
   1.298 + * pass to the main program.  Only those events which a slave can actually
   1.299 + * encounter are covered here; for example mm_searched() and mm_list() are
   1.300 + * not covered since a slave never does the operations that trigger these.
   1.301 + * Certain other events (mm_exists(), mm_expunged(), mm_flags()) are discarded
   1.302 + * by the slave since the master will generate these events for itself.
   1.303 + *
   1.304 + *  The other events cause the slave to write a newline-terminated string to
   1.305 + * its output.  The first character of string indicates the event: S for
   1.306 + * mm_status(), N for mm_notify(), L for mm_log(), C for mm_critical(), X for
   1.307 + * mm_nocritical(), D for mm_diskerror(), F for mm_fatal(), and "A" for append
   1.308 + * argument callback.  Most of these events also carry data, which carried as
   1.309 + * text space-delimited in the string.
   1.310 + *
   1.311 + *  Append argument callback requires the master to provide the slave with
   1.312 + * data in the slave's input.  The first thing that the master provides is
   1.313 + * either a "+" (master has data for the slave) or a "-" (master has no data).
   1.314 + * If the master has data, it will then send the flags, internal date, and
   1.315 + * message text, each as <text octet count><SPACE><text>.
   1.316 + */
   1.317 +
   1.318 +/*  It should be alright for lockslavep to be a global, since it will always
   1.319 + * be zero in the master (which is where threads would be).  The slave won't
   1.320 + * ever thread, since any driver which threads in its methods probably can't
   1.321 + * use fcntl() locking so won't have DR_LOCKING in its driver flags 
   1.322 + *
   1.323 + *  lockslavep can not be a static, since it's used by the dispatch macros.
   1.324 + */
   1.325 +
   1.326 +int lockslavep = 0;		/* non-zero means slave process for locking */
   1.327 +static int lockproxycopy = 0;	/* non-zero means redo copy as proxy */
   1.328 +FILE *slavein = NIL;		/* slave input */
   1.329 +FILE *slaveout = NIL;		/* slave output */
   1.330 +
   1.331 +
   1.332 +/* Common master
   1.333 + * Accepts: permitted stream
   1.334 + *	    append callback (append calls only, else NIL)
   1.335 + *	    data for callback (append calls only, else NIL)
   1.336 + * Returns: (master) T if slave succeeded, NIL if slave failed
   1.337 + *	    (slave) NIL always, with lockslavep non-NIL
   1.338 + */
   1.339 +
   1.340 +static long master (MAILSTREAM *stream,append_t af,void *data)
   1.341 +{
   1.342 +  MAILSTREAM *st;
   1.343 +  MAILSTATUS status;
   1.344 +  STRING *message;
   1.345 +  FILE *pi,*po;
   1.346 +  blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
   1.347 +  long ret = NIL;
   1.348 +  unsigned long i,j;
   1.349 +  int c,pid,pipei[2],pipeo[2];
   1.350 +  char *s,*t,event[MAILTMPLEN],tmp[MAILTMPLEN];
   1.351 +  lockproxycopy = NIL;		/* not doing a lock proxycopy */
   1.352 +				/* make pipe from slave */
   1.353 +  if (pipe (pipei) < 0) mm_log ("Can't create input pipe",ERROR);
   1.354 +  else if (pipe (pipeo) < 0) {
   1.355 +    mm_log ("Can't create output pipe",ERROR);
   1.356 +    close (pipei[0]); close (pipei[1]);
   1.357 +  }
   1.358 +  else if ((pid = fork ()) < 0) {/* make slave */
   1.359 +    mm_log ("Can't create execution process",ERROR);
   1.360 +    close (pipei[0]); close (pipei[1]);
   1.361 +    close (pipeo[0]); close (pipeo[1]);
   1.362 +  }
   1.363 +  else if (lockslavep = !pid) {	/* are we slave or master? */
   1.364 +    alarm (0);			/* slave doesn't have alarms or signals */
   1.365 +    for (c = 0; c < NSIG; c++) signal (c,SIG_DFL);
   1.366 +    if (!(slavein = fdopen (pipeo[0],"r")) ||
   1.367 +	!(slaveout = fdopen (pipei[1],"w")))
   1.368 +      fatal ("Can't do slave pipe buffered I/O");
   1.369 +    close (pipei[0]);		/* close parent's side of the pipes */
   1.370 +    close (pipeo[1]);
   1.371 +  }
   1.372 +
   1.373 +  else {			/* master process */
   1.374 +    void *blockdata = (*bn) (BLOCK_SENSITIVE,NIL);
   1.375 +    close (pipei[1]);		/* close slave's side of the pipes */
   1.376 +    close (pipeo[0]);
   1.377 +    if (!(pi = fdopen (pipei[0],"r")) || !(po = fdopen (pipeo[1],"w")))
   1.378 +      fatal ("Can't do master pipe buffered I/O");
   1.379 +				/* do slave events until EOF */
   1.380 +				/* read event */
   1.381 +    while (fgets (event,MAILTMPLEN-1,pi)) {
   1.382 +      if (!(s = strchr (event,'\n'))) {
   1.383 +	sprintf (tmp,"Execution process event string too long: %.500s",event);
   1.384 +	fatal (tmp);
   1.385 +      }
   1.386 +      *s = '\0';		/* tie off event at end of line */
   1.387 +      switch (event[0]) {	/* analyze event */
   1.388 +      case 'A':			/* append callback */
   1.389 +	if ((*af) (NIL,data,&s,&t,&message)) {
   1.390 +	  if (i = message ? SIZE (message) : 0) {
   1.391 +	    if (!s) s = "";	/* default values */
   1.392 +	    if (!t) t = "";
   1.393 +	  }
   1.394 +	  else s = t = "";	/* no flags or date if no message */
   1.395 +	  errno = NIL;		/* reset last error */
   1.396 +				/* build response */
   1.397 +	  if (fprintf (po,"+%lu %s%lu %s%lu ",strlen (s),s,strlen (t),t,i) < 0)
   1.398 +	    fatal ("Failed to pipe append command");
   1.399 +				/* write message text */
   1.400 +	  if (i) do if (putc (c = 0xff & SNX (message),po) == EOF) {
   1.401 +	    sprintf (tmp,"Failed to pipe %lu bytes (of %lu), last=%u: %.100s",
   1.402 +		     i,message->size,c,strerror (errno));
   1.403 +	    fatal (tmp);
   1.404 +	  } while (--i);
   1.405 +	}
   1.406 +	else putc ('-',po);	/* append error */
   1.407 +	fflush (po);
   1.408 +	break;
   1.409 +      case '&':			/* slave wants a proxycopy? */
   1.410 +	lockproxycopy = T;
   1.411 +	break;
   1.412 +
   1.413 +      case 'L':			/* mm_log() */
   1.414 +	i = strtoul (event+1,&s,10);
   1.415 +	if (!s || (*s++ != ' ')) {
   1.416 +	  sprintf (tmp,"Invalid log event arguments: %.500s",event);
   1.417 +	  fatal (tmp);
   1.418 +	}
   1.419 +	mm_log (s,i);
   1.420 +	break;
   1.421 +      case 'N':			/* mm_notify() */
   1.422 +	st = (MAILSTREAM *) strtoul (event+1,&s,16);
   1.423 +	if (s && (*s++ == ' ')) {
   1.424 +	  i = strtoul (s,&s,10);/* get severity */
   1.425 +	  if (s && (*s++ == ' ')) {
   1.426 +	    mm_notify ((st == stream) ? stream : NIL,s,i);
   1.427 +	    break;
   1.428 +	  }
   1.429 +	}
   1.430 +	sprintf (tmp,"Invalid notify event arguments: %.500s",event);
   1.431 +	fatal (tmp);
   1.432 +
   1.433 +      case 'S':			/* mm_status() */
   1.434 +	st = (MAILSTREAM *) strtoul (event+1,&s,16);
   1.435 +	if (s && (*s++ == ' ')) {
   1.436 +	  status.flags = strtoul (s,&s,10);
   1.437 +	  if (s && (*s++ == ' ')) {
   1.438 +	    status.messages = strtoul (s,&s,10);
   1.439 +	    if (s && (*s++ == ' ')) {
   1.440 +	      status.recent = strtoul (s,&s,10);
   1.441 +	      if (s && (*s++ == ' ')) {
   1.442 +		status.unseen = strtoul (s,&s,10);
   1.443 +		if (s && (*s++ == ' ')) {
   1.444 +		  status.uidnext = strtoul (s,&s,10);
   1.445 +		  if (s && (*s++ == ' ')) {
   1.446 +		    status.uidvalidity = strtoul (s,&s,10);
   1.447 +		    if (s && (*s++ == ' ')) {
   1.448 +		      mm_status ((st == stream) ? stream : NIL,s,&status);
   1.449 +		      break;
   1.450 +		    }
   1.451 +		  }
   1.452 +		}
   1.453 +	      }
   1.454 +	    }
   1.455 +	  }
   1.456 +	}
   1.457 +	sprintf (tmp,"Invalid status event arguments: %.500s",event);
   1.458 +	fatal (tmp);
   1.459 +      case 'C':			/* mm_critical() */
   1.460 +	st = (MAILSTREAM *) strtoul (event+1,&s,16);
   1.461 +	mm_critical ((st == stream) ? stream : NIL);
   1.462 +	break;
   1.463 +      case 'X':			/* mm_nocritical() */
   1.464 +	st = (MAILSTREAM *) strtoul (event+1,&s,16);
   1.465 +	mm_nocritical ((st == stream) ? stream : NIL);
   1.466 +	break;
   1.467 +
   1.468 +      case 'D':			/* mm_diskerror() */
   1.469 +	st = (MAILSTREAM *) strtoul (event+1,&s,16);
   1.470 +	if (s && (*s++ == ' ')) {
   1.471 +	  i = strtoul (s,&s,10);
   1.472 +	  if (s && (*s++ == ' ')) {
   1.473 +	    j = (long) strtoul (s,NIL,10);
   1.474 +	    if (st == stream)	/* let's hope it's on usable stream */
   1.475 +	      putc (mm_diskerror (stream,(long) i,j) ? '+' : '-',po);
   1.476 +	    else if (j) {	/* serious diskerror on slave-created stream */
   1.477 +	      mm_log ("Retrying disk write to avoid mailbox corruption!",WARN);
   1.478 +	      sleep (5);	/* give some time for it to clear up */
   1.479 +	      putc ('-',po);	/* don't abort */
   1.480 +	    }
   1.481 +	    else {		/* recoverable on slave-created stream */
   1.482 +	      mm_log ("Error on disk write",ERROR);
   1.483 +	      putc ('+',po);	/* so abort it */
   1.484 +	    }
   1.485 +	    fflush (po);	/* force it out either way */
   1.486 +	    break;
   1.487 +	  }
   1.488 +	}
   1.489 +	sprintf (tmp,"Invalid diskerror event arguments: %.500s",event);
   1.490 +	fatal (tmp);
   1.491 +      case 'F':			/* mm_fatal() */
   1.492 +	mm_fatal (event+1);
   1.493 +	break;
   1.494 +      default:			/* random lossage */
   1.495 +	sprintf (tmp,"Unknown event from execution process: %.500s",event);
   1.496 +	fatal (tmp);
   1.497 +      }
   1.498 +    }
   1.499 +    fclose (pi); fclose (po);	/* done with the pipes */
   1.500 +				/* get slave status */
   1.501 +    grim_pid_reap_status (pid,NIL,&ret);
   1.502 +    if (ret & 0177) {		/* signal or stopped */
   1.503 +      sprintf (tmp,"Execution process terminated abnormally (%lx)",ret);
   1.504 +      mm_log (tmp,ERROR);
   1.505 +      ret = NIL;
   1.506 +    }
   1.507 +    else ret >>= 8;		/* return exit code */
   1.508 +    (*bn) (BLOCK_NONSENSITIVE,blockdata);
   1.509 +  }
   1.510 +  return ret;			/* return status */
   1.511 +}
   1.512 +
   1.513 +/* Safe driver calls */
   1.514 +
   1.515 +
   1.516 +/* Safely delete mailbox
   1.517 + * Accepts: driver to call under slave
   1.518 + *	    MAIL stream
   1.519 + *	    mailbox name to delete
   1.520 + * Returns: T on success, NIL on failure
   1.521 + */
   1.522 +
   1.523 +long safe_delete (DRIVER *dtb,MAILSTREAM *stream,char *mbx)
   1.524 +{
   1.525 +  long ret = master (stream,NIL,NIL);
   1.526 +  if (lockslavep) exit ((*dtb->mbxdel) (stream,mbx));
   1.527 +  return ret;
   1.528 +}
   1.529 +
   1.530 +
   1.531 +/* Safely rename mailbox
   1.532 + * Accepts: driver to call under slave
   1.533 + *	    MAIL stream
   1.534 + *	    old mailbox name
   1.535 + *	    new mailbox name (or NIL for delete)
   1.536 + * Returns: T on success, NIL on failure
   1.537 + */
   1.538 +
   1.539 +long safe_rename (DRIVER *dtb,MAILSTREAM *stream,char *old,char *newname)
   1.540 +{
   1.541 +  long ret = master (stream,NIL,NIL);
   1.542 +  if (lockslavep) exit ((*dtb->mbxren) (stream,old,newname));
   1.543 +  return ret;
   1.544 +}
   1.545 +
   1.546 +
   1.547 +/* Safely get status of mailbox
   1.548 + * Accepts: driver to call under slave
   1.549 + *	    MAIL stream
   1.550 + *	    mailbox name
   1.551 + *	    status flags
   1.552 + * Returns: T on success, NIL on failure
   1.553 + */
   1.554 +
   1.555 +long safe_status (DRIVER *dtb,MAILSTREAM *stream,char *mbx,long flags)
   1.556 +{
   1.557 +  long ret = master (stream,NIL,NIL);
   1.558 +  if (lockslavep) exit ((*dtb->status) (stream,mbx,flags));
   1.559 +  return ret;
   1.560 +}
   1.561 +
   1.562 +
   1.563 +/* Scan file for contents
   1.564 + * Accepts: driver to call under slave
   1.565 + *	    file name
   1.566 + *	    desired contents
   1.567 + *	    length of contents
   1.568 + *	    length of file
   1.569 + * Returns: NIL if contents not found, T if found
   1.570 + */
   1.571 +
   1.572 +long safe_scan_contents (DRIVER *dtb,char *name,char *contents,
   1.573 +			 unsigned long csiz,unsigned long fsiz)
   1.574 +{
   1.575 +  long ret = master (NIL,NIL,NIL);
   1.576 +  if (lockslavep) exit (scan_contents (dtb,name,contents,csiz,fsiz));
   1.577 +  return ret;
   1.578 +}
   1.579 +
   1.580 +/* Safely copy message to mailbox
   1.581 + * Accepts: driver to call under slave
   1.582 + *	    MAIL stream
   1.583 + *	    sequence
   1.584 + *	    destination mailbox
   1.585 + *	    copy options
   1.586 + * Returns: T if success, NIL if failed
   1.587 + */
   1.588 +
   1.589 +long safe_copy (DRIVER *dtb,MAILSTREAM *stream,char *seq,char *mbx,long flags)
   1.590 +{
   1.591 +  mailproxycopy_t pc =
   1.592 +    (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
   1.593 +  long ret = master (stream,NIL,NIL);
   1.594 +  if (lockslavep) {
   1.595 +				/* don't do proxycopy in slave */
   1.596 +    if (pc) mail_parameters (stream,SET_MAILPROXYCOPY,(void *) slaveproxycopy);
   1.597 +    exit ((*dtb->copy) (stream,seq,mbx,flags));
   1.598 +  }
   1.599 +				/* do any proxycopy in master */
   1.600 +  if (lockproxycopy && pc) return (*pc) (stream,seq,mbx,flags);
   1.601 +  return ret;
   1.602 +}
   1.603 +
   1.604 +
   1.605 +/* Append package for slave */
   1.606 +
   1.607 +typedef struct append_data {
   1.608 +  int first;			/* flag indicating first message */
   1.609 +  char *flags;			/* message flags */
   1.610 +  char *date;			/* message date */
   1.611 +  char *msg;			/* message text */
   1.612 +  STRING message;		/* message stringstruct */
   1.613 +} APPENDDATA;
   1.614 +
   1.615 +
   1.616 +/* Safely append message to mailbox
   1.617 + * Accepts: driver to call under slave
   1.618 + *	    MAIL stream
   1.619 + *	    destination mailbox
   1.620 + *	    append callback
   1.621 + *	    data for callback
   1.622 + * Returns: T if append successful, else NIL
   1.623 + */
   1.624 +
   1.625 +long safe_append (DRIVER *dtb,MAILSTREAM *stream,char *mbx,append_t af,
   1.626 +		  void *data)
   1.627 +{
   1.628 +  long ret = master (stream,af,data);
   1.629 +  if (lockslavep) {
   1.630 +    APPENDDATA ad;
   1.631 +    ad.first = T;		/* initialize initial append package */
   1.632 +    ad.flags = ad.date = ad.msg = NIL;
   1.633 +    exit ((*dtb->append) (stream,mbx,slave_append,&ad));
   1.634 +  }
   1.635 +  return ret;
   1.636 +}
   1.637 +
   1.638 +/* Slave callbacks */
   1.639 +
   1.640 +
   1.641 +/* Message exists (i.e. there are that many messages in the mailbox)
   1.642 + * Accepts: MAIL stream
   1.643 + *	    message number
   1.644 + */
   1.645 +
   1.646 +void slave_exists (MAILSTREAM *stream,unsigned long number)
   1.647 +{
   1.648 +  /* this event never passed by slaves */
   1.649 +}
   1.650 +
   1.651 +
   1.652 +/* Message expunged
   1.653 + * Accepts: MAIL stream
   1.654 + *	    message number
   1.655 + */
   1.656 +
   1.657 +void slave_expunged (MAILSTREAM *stream,unsigned long number)
   1.658 +{
   1.659 +  /* this event never passed by slaves */
   1.660 +}
   1.661 +
   1.662 +
   1.663 +/* Message status changed
   1.664 + * Accepts: MAIL stream
   1.665 + *	    message number
   1.666 + */
   1.667 +
   1.668 +void slave_flags (MAILSTREAM *stream,unsigned long number)
   1.669 +{
   1.670 +  /* this event never passed by slaves */
   1.671 +}
   1.672 +
   1.673 +/* Mailbox status
   1.674 + * Accepts: MAIL stream
   1.675 + *	    mailbox name
   1.676 + *	    mailbox status
   1.677 + */
   1.678 +
   1.679 +void slave_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
   1.680 +{
   1.681 +  int i,c;
   1.682 +  fprintf (slaveout,"S%lx %lu %lu %lu %lu %lu %lu ",
   1.683 +	  (unsigned long) stream,status->flags,status->messages,status->recent,
   1.684 +	  status->unseen,status->uidnext,status->uidvalidity,mailbox);
   1.685 +				/* yow!  are we paranoid enough yet? */
   1.686 +  for (i = 0; (i < 500) && (c = *mailbox++); ++i) switch (c) {
   1.687 +  case '\r': case '\n':		/* newline in a mailbox name? */
   1.688 +    c = ' ';
   1.689 +  default:
   1.690 +    putc (c,slaveout);
   1.691 +  }
   1.692 +  putc ('\n',slaveout);
   1.693 +  fflush (slaveout);
   1.694 +}
   1.695 +
   1.696 +/* Notification event
   1.697 + * Accepts: MAIL stream
   1.698 + *	    string to log
   1.699 + *	    error flag
   1.700 + */
   1.701 +
   1.702 +void slave_notify (MAILSTREAM *stream,char *string,long errflg)
   1.703 +{
   1.704 +  int i,c;
   1.705 +  fprintf (slaveout,"N%lx %lu ",(unsigned long) stream,errflg);
   1.706 +				/* prevent more than 500 bytes */
   1.707 +  for (i = 0; (i < 500) && (c = *string++); ++i) switch (c) {
   1.708 +  case '\r': case '\n':		/* or embedded newline */
   1.709 +    c = ' ';
   1.710 +  default:
   1.711 +    putc (c,slaveout);
   1.712 +  }
   1.713 +  putc ('\n',slaveout);
   1.714 +  fflush (slaveout);
   1.715 +}
   1.716 +
   1.717 +
   1.718 +/* Log an event for the user to see
   1.719 + * Accepts: string to log
   1.720 + *	    error flag
   1.721 + */
   1.722 +
   1.723 +void slave_log (char *string,long errflg)
   1.724 +{
   1.725 +  int i,c;
   1.726 +  fprintf (slaveout,"L%lu ",errflg);
   1.727 +				/* prevent more than 500 bytes */
   1.728 +  for (i = 0; (i < 500) && (c = *string++); ++i) switch (c) {
   1.729 +  case '\r': case '\n':		/* or embedded newline */
   1.730 +    c = ' ';
   1.731 +  default:
   1.732 +    putc (c,slaveout);
   1.733 +  }
   1.734 +  putc ('\n',slaveout);
   1.735 +  fflush (slaveout);
   1.736 +}
   1.737 +
   1.738 +/* About to enter critical code
   1.739 + * Accepts: stream
   1.740 + */
   1.741 +
   1.742 +void slave_critical (MAILSTREAM *stream)
   1.743 +{
   1.744 +  fprintf (slaveout,"C%lx\n",(unsigned long) stream);
   1.745 +  fflush (slaveout);
   1.746 +}
   1.747 +
   1.748 +
   1.749 +/* About to exit critical code
   1.750 + * Accepts: stream
   1.751 + */
   1.752 +
   1.753 +void slave_nocritical (MAILSTREAM *stream)
   1.754 +{
   1.755 +  fprintf (slaveout,"X%lx\n",(unsigned long) stream);
   1.756 +  fflush (slaveout);
   1.757 +}
   1.758 +
   1.759 +/* Disk error found
   1.760 + * Accepts: stream
   1.761 + *	    system error code
   1.762 + *	    flag indicating that mailbox may be clobbered
   1.763 + * Returns: abort flag
   1.764 + */
   1.765 +
   1.766 +long slave_diskerror (MAILSTREAM *stream,long errcode,long serious)
   1.767 +{
   1.768 +  char tmp[MAILTMPLEN];
   1.769 +  int c;
   1.770 +  long ret = NIL;
   1.771 +  fprintf (slaveout,"D%lx %lu %lu\n",(unsigned long) stream,errcode,serious);
   1.772 +  fflush (slaveout);
   1.773 +  switch (c = getc (slavein)) {
   1.774 +  case EOF:			/* pipe broken */
   1.775 +    slave_fatal ("Pipe broken reading diskerror response");
   1.776 +  case '+':			/* user wants to abort */
   1.777 +    ret = LONGT;
   1.778 +  case '-':			/* no abort */
   1.779 +    break;
   1.780 +  default:
   1.781 +    sprintf (tmp,"Unknown master response for diskerror: %c",c);
   1.782 +    slave_fatal (tmp);
   1.783 +  }
   1.784 +  return ret;
   1.785 +}
   1.786 +
   1.787 +
   1.788 +/* Log a fatal error event
   1.789 + * Accepts: string to log
   1.790 + * Does not return
   1.791 + */
   1.792 +
   1.793 +void slave_fatal (char *string)
   1.794 +{
   1.795 +  int i,c;
   1.796 +  syslog (LOG_ALERT,"IMAP toolkit slave process crash: %.500s",string);
   1.797 +  putc ('F',slaveout);
   1.798 +				/* prevent more than 500 bytes */
   1.799 +  for (i = 0; (i < 500) && (c = *string++); ++i) switch (c) {
   1.800 +  case '\r': case '\n':		/* newline in a mailbox name? */
   1.801 +    c = ' ';
   1.802 +  default:
   1.803 +    putc (c,slaveout);
   1.804 +  }
   1.805 +  putc ('\n',slaveout);
   1.806 +  fflush (slaveout);
   1.807 +  abort ();			/* die */
   1.808 +}
   1.809 +
   1.810 +/* Append read buffer
   1.811 + * Accepts: number of bytes to read
   1.812 + *	    error message if fails
   1.813 + * Returns: read-in string
   1.814 + */
   1.815 +
   1.816 +static char *slave_append_read (unsigned long n,char *error)
   1.817 +{
   1.818 +#if 0
   1.819 +  unsigned long i;
   1.820 +#endif
   1.821 +  int c;
   1.822 +  char *t,tmp[MAILTMPLEN];
   1.823 +  char *s = (char *) fs_get (n + 1);
   1.824 +  s[n] = '\0';
   1.825 +#if 0
   1.826 +  /* This doesn't work on Solaris with GCC.  I think that it's a C library
   1.827 +   * bug, since the problem only shows up if the application does fread()
   1.828 +   * on some other file
   1.829 +   */
   1.830 +  for (t = s; n && ((i = fread (t,1,n,slavein)); t += i,n -= i);
   1.831 +#else
   1.832 +  for (t = s; n && ((c = getc (slavein)) != EOF); *t++ = c,--n);
   1.833 +#endif
   1.834 +  if (n) {
   1.835 +    sprintf(tmp,"Pipe broken reading %.100s with %lu bytes remaining",error,n);
   1.836 +    slave_fatal (tmp);
   1.837 +  }
   1.838 +  return s;
   1.839 +}
   1.840 +
   1.841 +/* Append message callback
   1.842 + * Accepts: MAIL stream
   1.843 + *	    append data package
   1.844 + *	    pointer to return initial flags
   1.845 + *	    pointer to return message internal date
   1.846 + *	    pointer to return stringstruct of message or NIL to stop
   1.847 + * Returns: T if success (have message or stop), NIL if error
   1.848 + */
   1.849 +
   1.850 +long slave_append (MAILSTREAM *stream,void *data,char **flags,char **date,
   1.851 +		   STRING **message)
   1.852 +{
   1.853 +  char tmp[MAILTMPLEN];
   1.854 +  unsigned long n;
   1.855 +  int c;
   1.856 +  APPENDDATA *ad = (APPENDDATA *) data;
   1.857 +				/* flush text of previous message */
   1.858 +  if (ad->flags) fs_give ((void **) &ad->flags);
   1.859 +  if (ad->date) fs_give ((void **) &ad->date);
   1.860 +  if (ad->msg) fs_give ((void **) &ad->msg);
   1.861 +  *flags = *date = NIL;		/* assume no flags or date */
   1.862 +  fputs ("A\n",slaveout);	/* tell master we're doing append callback */
   1.863 +  fflush (slaveout);
   1.864 +  switch (c = getc (slavein)) {	/* what did master say? */
   1.865 +  case '+':			/* have message, get size of flags */
   1.866 +    for (n = 0; isdigit (c = getc (slavein)); n *= 10, n += (c - '0'));
   1.867 +    if (c != ' ') {
   1.868 +      if (c == EOF) sprintf (tmp,"Pipe broken after flag size %lu",n);
   1.869 +      sprintf (tmp,"Missing delimiter after flag size %lu: %c",n,c);
   1.870 +      slave_fatal (tmp);
   1.871 +    }
   1.872 +    if (n) *flags = ad->flags = slave_append_read (n,"flags");
   1.873 +				/* get size of date */
   1.874 +    for (n = 0; isdigit (c = getc (slavein)); n *= 10, n += (c - '0'));
   1.875 +    if (c != ' ') {
   1.876 +      if (c == EOF) sprintf (tmp,"Pipe broken after date size %lu",n);
   1.877 +      else sprintf (tmp,"Missing delimiter after date size %lu: %c",n,c);
   1.878 +      slave_fatal (tmp);
   1.879 +    }
   1.880 +    if (n) *date = ad->date = slave_append_read (n,"date");
   1.881 +				/* get size of message */
   1.882 +    for (n = 0; isdigit (c = getc (slavein)); n *= 10, n += (c - '0'));
   1.883 +    if (c != ' ') {
   1.884 +      if (c == EOF) sprintf (tmp,"Pipe broken after message size %lu",n);
   1.885 +      sprintf (tmp,"Missing delimiter after message size %lu: %c",n,c);
   1.886 +      slave_fatal (tmp);
   1.887 +    }
   1.888 +    if (n) {			/* make buffer for message */
   1.889 +      ad->msg = slave_append_read (n,"message");
   1.890 +				/* initialize stringstruct */
   1.891 +      INIT (&ad->message,mail_string,(void *) ad->msg,n);
   1.892 +      ad->first = NIL;		/* no longer first message */
   1.893 +      *message = &ad->message;	/* return message */
   1.894 +    }
   1.895 +    else *message = NIL;	/* empty message */
   1.896 +    return LONGT;
   1.897 +  case '-':			/* error */
   1.898 +    *message = NIL;		/* set stop */
   1.899 +    break;
   1.900 +  case EOF:			/* end of file */
   1.901 +    slave_fatal ("Pipe broken reading append response");
   1.902 +  default:			/* unknown event */
   1.903 +    sprintf (tmp,"Unknown master response for append: %c",c);
   1.904 +    slave_fatal (tmp);
   1.905 +  }
   1.906 +  return NIL;			/* return failure */
   1.907 +}
   1.908 +
   1.909 +/* Proxy copy across mailbox formats
   1.910 + * Accepts: mail stream
   1.911 + *	    sequence to copy on this stream
   1.912 + *	    destination mailbox
   1.913 + *	    option flags
   1.914 + * Returns: T if success, else NIL
   1.915 + */
   1.916 +
   1.917 +long slaveproxycopy (MAILSTREAM *stream,char *sequence,char *mailbox,
   1.918 +		     long options)
   1.919 +{
   1.920 +  fputs ("&\n",slaveout);	/* redo copy as append */
   1.921 +  fflush (slaveout);
   1.922 +  return NIL;			/* failure for now */
   1.923 +}

UW-IMAP'd extensions by yuuji