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 +}