imapext-2007

view 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 source
1 /* ========================================================================
2 * Copyright 1988-2007 University of Washington
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 *
11 * ========================================================================
12 */
14 /*
15 * Program: flock emulation via fcntl() locking
16 *
17 * Author: Mark Crispin
18 * Networks and Distributed Computing
19 * Computing & Communications
20 * University of Washington
21 * Administration Building, AG-44
22 * Seattle, WA 98195
23 * Internet: MRC@CAC.Washington.EDU
24 *
25 * Date: 10 April 2001
26 * Last Edited: 11 October 2007
27 */
29 #undef flock /* name is used as a struct for fcntl */
31 #ifndef NOFSTATVFS /* thank you, SUN. NOT! */
32 # ifndef NOFSTATVFS64
33 # ifndef _LARGEFILE64_SOURCE
34 # define _LARGEFILE64_SOURCE
35 # endif /* _LARGEFILE64_SOURCE */
36 # endif /* NOFSTATVFFS64 */
37 #include <sys/statvfs.h>
38 #endif /* NOFSTATVFS */
40 #ifndef NSIG /* don't know if this can happen */
41 #define NSIG 32 /* a common maximum */
42 #endif
44 /* Emulator for flock() call
45 * Accepts: file descriptor
46 * operation bitmask
47 * Returns: 0 if successful, -1 if failure under BSD conditions
48 */
50 int flocksim (int fd,int op)
51 {
52 char tmp[MAILTMPLEN];
53 int logged = 0;
54 struct stat sbuf;
55 struct ustat usbuf;
56 struct flock fl;
57 /* lock zero bytes at byte 0 */
58 fl.l_whence = SEEK_SET; fl.l_start = fl.l_len = 0;
59 fl.l_pid = getpid (); /* shouldn't be necessary */
60 switch (op & ~LOCK_NB) { /* translate to fcntl() operation */
61 case LOCK_EX: /* exclusive */
62 fl.l_type = F_WRLCK;
63 break;
64 case LOCK_SH: /* shared */
65 fl.l_type = F_RDLCK;
66 break;
67 case LOCK_UN: /* unlock */
68 fl.l_type = F_UNLCK;
69 break;
70 default: /* default */
71 errno = EINVAL;
72 return -1;
73 }
74 /* always return success if disabled */
75 if (mail_parameters (NIL,GET_DISABLEFCNTLLOCK,NIL)) return 0;
77 /* Make fcntl() locking of NFS files be a no-op the way it is with flock()
78 * on BSD. This is because the rpc.statd/rpc.lockd daemons don't work very
79 * well and cause cluster-wide hangs if you exercise them at all. The
80 * result of this is that you lose the ability to detect shared mail_open()
81 * on NFS-mounted files. If you are wise, you'll use IMAP instead of NFS
82 * for mail files.
83 *
84 * Sun alleges that it doesn't matter, and that they have fixed all the
85 * rpc.statd/rpc.lockd bugs. As of October 2006, that is still false.
86 *
87 * We need three tests for three major historical variants in SVR4:
88 * 1) In NFSv2, ustat() would return -1 in f_tinode for NFS.
89 * 2) When fstatvfs() was introduced with NFSv3, ustat() was "fixed".
90 * 3) When 64-bit filesystems were introduced, fstatvfs() would return
91 * EOVERFLOW; you have to use fstatvfs64() even though you don't care
92 * about any of the affected values.
93 *
94 * We can't use fstatfs() because fstatfs():
95 * . is documented as being deprecated in SVR4.
96 * . has inconsistent calling conventions (there are two additional int
97 * arguments on Solaris and I don't know what they do).
98 * . returns inconsistent statfs structs. On Solaris, the file system type
99 * is a short called f_fstyp. On AIX, it's an int called f_type that is
100 * documented as always being 0!
101 *
102 * For what it's worth, here's the scoop on fstatfs() elsewhere:
103 *
104 * On Linux, the file system type is a long called f_type that has a file
105 * system type code. A different module (flocklnx.c) uses this because
106 * some knothead "improved" flock() to return ENOLCK on NFS files instead
107 * of being a successful no-op. This "improvement" apparently has been
108 * reverted, but not before it got to many systems in the field.
109 *
110 * On BSD, it's a short called either f_otype or f_type that is documented
111 * as always being zero. Fortunately, BSD has flock() the way it's supposed
112 * to be, and none of this nonsense is necessary.
113 */
114 if (!fstat (fd,&sbuf)) { /* no hope of working if can't fstat()! */
115 /* Any base type that begins with "nfs" or "afs" is considered to be a
116 * network filesystem.
117 */
118 #ifndef NOFSTATVFS
119 struct statvfs vsbuf;
120 #ifndef NOFSTATVFS64
121 struct statvfs64 vsbuf64;
122 if (!fstatvfs64 (fd,&vsbuf64) && (vsbuf64.f_basetype[1] == 'f') &&
123 (vsbuf64.f_basetype[2] == 's') &&
124 ((vsbuf64.f_basetype[0] == 'n') || (vsbuf64.f_basetype[0] == 'a')))
125 return 0;
126 #endif /* NOFSTATVFS64 */
127 if (!fstatvfs (fd,&vsbuf) && (vsbuf.f_basetype[1] == 'f') &&
128 (vsbuf.f_basetype[2] == 's') &&
129 ((vsbuf.f_basetype[0] == 'n') || (vsbuf.f_basetype[0] == 'a')))
130 return 0;
131 #endif /* NOFSTATVFS */
132 if (!ustat (sbuf.st_dev,&usbuf) && !++usbuf.f_tinode) return 0;
133 }
135 /* do the lock */
136 while (fcntl (fd,(op & LOCK_NB) ? F_SETLK : F_SETLKW,&fl))
137 if (errno != EINTR) {
138 /* Can't use switch here because these error codes may resolve to the
139 * same value on some systems.
140 */
141 if ((errno != EWOULDBLOCK) && (errno != EAGAIN) && (errno != EACCES)) {
142 sprintf (tmp,"Unexpected file locking failure: %.100s",
143 strerror (errno));
144 /* give the user a warning of what happened */
145 MM_NOTIFY (NIL,tmp,WARN);
146 if (!logged++) syslog (LOG_ERR,"%s",tmp);
147 if (op & LOCK_NB) return -1;
148 sleep (5); /* slow things down for loops */
149 }
150 /* return failure for non-blocking lock */
151 else if (op & LOCK_NB) return -1;
152 }
153 return 0; /* success */
154 }
156 /* Master/slave procedures for safe fcntl() locking.
157 *
158 * The purpose of this nonsense is to work around a bad bug in fcntl()
159 * locking. The cretins who designed it decided that a close() should
160 * release any locks made by that process on the file opened on that
161 * file descriptor. Never mind that the lock wasn't made on that file
162 * descriptor, but rather on some other file descriptor.
163 *
164 * This bug is on every implementation of fcntl() locking that I have
165 * tested. Fortunately, on BSD systems, OSF/1, and Linux, we can use the
166 * flock() system call which doesn't have this bug.
167 *
168 * Note that OSF/1, Linux, and some BSD systems have both broken fcntl()
169 * locking and the working flock() locking.
170 *
171 * The program below can be used to demonstrate this problem. Be sure to
172 * let it run long enough for all the sleep() calls to finish.
173 */
175 #if 0
176 #include <stdio.h>
177 #include <unistd.h>
178 #include <fcntl.h>
179 #include <errno.h>
180 #include <string.h>
181 #include <sys/file.h>
183 main ()
184 {
185 struct flock fl;
186 int fd,fd2;
187 char *file = "a.a";
188 if ((fd = creat (file,0666)) < 0)
189 perror ("TEST FAILED: can't create test file"),_exit (errno);
190 close (fd);
191 if (fork ()) { /* parent */
192 if ((fd = open (file,O_RDWR,0)) < 0) abort();
193 /* lock applies to entire file */
194 fl.l_whence = fl.l_start = fl.l_len = 0;
195 fl.l_pid = getpid (); /* shouldn't be necessary */
196 fl.l_type = F_RDLCK;
197 if (fcntl (fd,F_SETLKW,&fl) == -1) abort ();
198 sleep (5);
199 if ((fd2 = open (file,O_RDWR,0)) < 0) abort ();
200 sleep (1);
201 puts ("parent test ready -- will hang here if locking works correctly");
202 close (fd2);
203 wait (0);
204 puts ("OS BUG: child terminated");
205 _exit (0);
206 }
207 else { /* child */
208 sleep (2);
209 if ((fd = open (file,O_RDWR,0666)) < 0) abort ();
210 puts ("child test ready -- child will hang if no bug");
211 /* lock applies to entire file */
212 fl.l_whence = fl.l_start = fl.l_len = 0;
213 fl.l_pid = getpid (); /* shouldn't be necessary */
214 fl.l_type = F_WRLCK;
215 if (fcntl (fd,F_SETLKW,&fl) == -1) abort ();
216 puts ("OS BUG: child got lock");
217 }
218 }
219 #endif
221 /* Beware of systems such as AIX which offer flock() as a compatibility
222 * function that is just a jacket into fcntl() locking. The program below
223 * is a variant of the program above, only using flock(). It can be used
224 * to test to see if your system has real flock() or just a jacket into
225 * fcntl().
226 *
227 * Be sure to let it run long enough for all the sleep() calls to finish.
228 * If the program hangs, then flock() works and you can dispense with the
229 * use of this module (you lucky person!).
230 */
232 #if 0
233 #include <stdio.h>
234 #include <errno.h>
235 #include <string.h>
236 #include <sys/file.h>
238 main ()
239 {
240 int fd,fd2;
241 char *file = "a.a";
242 if ((fd = creat (file,0666)) < 0)
243 perror ("TEST FAILED: can't create test file"),_exit (errno);
244 close (fd);
245 if (fork ()) { /* parent */
246 if ((fd = open (file,O_RDWR,0)) < 0) abort();
247 if (flock (fd,LOCK_SH) == -1) abort ();
248 sleep (5);
249 if ((fd2 = open (file,O_RDWR,0)) < 0) abort ();
250 sleep (1);
251 puts ("parent test ready -- will hang here if flock() works correctly");
252 close (fd2);
253 wait (0);
254 puts ("OS BUG: child terminated");
255 _exit (0);
256 }
257 else { /* child */
258 sleep (2);
259 if ((fd = open (file,O_RDWR,0666)) < 0) abort ();
260 puts ("child test ready -- child will hang if no bug");
261 if (flock (fd,LOCK_EX) == -1) abort ();
262 puts ("OS BUG: child got lock");
263 }
264 }
265 #endif
267 /* Master/slave details
268 *
269 * On broken systems, we invoke an inferior fork to execute any driver
270 * dispatches which are likely to tickle this bug; specifically, any
271 * dispatch which may fiddle with a mailbox that is already selected. As
272 * of this writing, these are: delete, rename, status, scan, copy, and append.
273 *
274 * Delete and rename are pretty marginal, yet there are certain clients
275 * (e.g. Outlook Express) that really want to delete or rename the selected
276 * mailbox. The same is true of status, but there are people (such as the
277 * authors of Entourage) who don't understand why status of the selected
278 * mailbox is bad news.
279 *
280 * However, in copy and append it is reasonable to do this to a selected
281 * mailbox. Although scanning the selected mailbox isn't particularly
282 * sensible, it's hard to avoid due to wildcards.
283 *
284 * It is still possible for an application to trigger the bug by doing
285 * mail_open() on the same mailbox twice. Don't do it.
286 *
287 * Once the slave is invoked, the master only has to read events from the
288 * slave's output (see below for these events) and translate these events
289 * to the appropriate c-client callback. When end of file occurs on the pipe,
290 * the master reads the slave's exit status and uses that as the function
291 * return. The append master is slightly more complicated because it has to
292 * send data back to the slave (see below).
293 *
294 * The slave takes callback events from the driver which otherwise would
295 * pass to the main program. Only those events which a slave can actually
296 * encounter are covered here; for example mm_searched() and mm_list() are
297 * not covered since a slave never does the operations that trigger these.
298 * Certain other events (mm_exists(), mm_expunged(), mm_flags()) are discarded
299 * by the slave since the master will generate these events for itself.
300 *
301 * The other events cause the slave to write a newline-terminated string to
302 * its output. The first character of string indicates the event: S for
303 * mm_status(), N for mm_notify(), L for mm_log(), C for mm_critical(), X for
304 * mm_nocritical(), D for mm_diskerror(), F for mm_fatal(), and "A" for append
305 * argument callback. Most of these events also carry data, which carried as
306 * text space-delimited in the string.
307 *
308 * Append argument callback requires the master to provide the slave with
309 * data in the slave's input. The first thing that the master provides is
310 * either a "+" (master has data for the slave) or a "-" (master has no data).
311 * If the master has data, it will then send the flags, internal date, and
312 * message text, each as <text octet count><SPACE><text>.
313 */
315 /* It should be alright for lockslavep to be a global, since it will always
316 * be zero in the master (which is where threads would be). The slave won't
317 * ever thread, since any driver which threads in its methods probably can't
318 * use fcntl() locking so won't have DR_LOCKING in its driver flags
319 *
320 * lockslavep can not be a static, since it's used by the dispatch macros.
321 */
323 int lockslavep = 0; /* non-zero means slave process for locking */
324 static int lockproxycopy = 0; /* non-zero means redo copy as proxy */
325 FILE *slavein = NIL; /* slave input */
326 FILE *slaveout = NIL; /* slave output */
329 /* Common master
330 * Accepts: permitted stream
331 * append callback (append calls only, else NIL)
332 * data for callback (append calls only, else NIL)
333 * Returns: (master) T if slave succeeded, NIL if slave failed
334 * (slave) NIL always, with lockslavep non-NIL
335 */
337 static long master (MAILSTREAM *stream,append_t af,void *data)
338 {
339 MAILSTREAM *st;
340 MAILSTATUS status;
341 STRING *message;
342 FILE *pi,*po;
343 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
344 long ret = NIL;
345 unsigned long i,j;
346 int c,pid,pipei[2],pipeo[2];
347 char *s,*t,event[MAILTMPLEN],tmp[MAILTMPLEN];
348 lockproxycopy = NIL; /* not doing a lock proxycopy */
349 /* make pipe from slave */
350 if (pipe (pipei) < 0) mm_log ("Can't create input pipe",ERROR);
351 else if (pipe (pipeo) < 0) {
352 mm_log ("Can't create output pipe",ERROR);
353 close (pipei[0]); close (pipei[1]);
354 }
355 else if ((pid = fork ()) < 0) {/* make slave */
356 mm_log ("Can't create execution process",ERROR);
357 close (pipei[0]); close (pipei[1]);
358 close (pipeo[0]); close (pipeo[1]);
359 }
360 else if (lockslavep = !pid) { /* are we slave or master? */
361 alarm (0); /* slave doesn't have alarms or signals */
362 for (c = 0; c < NSIG; c++) signal (c,SIG_DFL);
363 if (!(slavein = fdopen (pipeo[0],"r")) ||
364 !(slaveout = fdopen (pipei[1],"w")))
365 fatal ("Can't do slave pipe buffered I/O");
366 close (pipei[0]); /* close parent's side of the pipes */
367 close (pipeo[1]);
368 }
370 else { /* master process */
371 void *blockdata = (*bn) (BLOCK_SENSITIVE,NIL);
372 close (pipei[1]); /* close slave's side of the pipes */
373 close (pipeo[0]);
374 if (!(pi = fdopen (pipei[0],"r")) || !(po = fdopen (pipeo[1],"w")))
375 fatal ("Can't do master pipe buffered I/O");
376 /* do slave events until EOF */
377 /* read event */
378 while (fgets (event,MAILTMPLEN-1,pi)) {
379 if (!(s = strchr (event,'\n'))) {
380 sprintf (tmp,"Execution process event string too long: %.500s",event);
381 fatal (tmp);
382 }
383 *s = '\0'; /* tie off event at end of line */
384 switch (event[0]) { /* analyze event */
385 case 'A': /* append callback */
386 if ((*af) (NIL,data,&s,&t,&message)) {
387 if (i = message ? SIZE (message) : 0) {
388 if (!s) s = ""; /* default values */
389 if (!t) t = "";
390 }
391 else s = t = ""; /* no flags or date if no message */
392 errno = NIL; /* reset last error */
393 /* build response */
394 if (fprintf (po,"+%lu %s%lu %s%lu ",strlen (s),s,strlen (t),t,i) < 0)
395 fatal ("Failed to pipe append command");
396 /* write message text */
397 if (i) do if (putc (c = 0xff & SNX (message),po) == EOF) {
398 sprintf (tmp,"Failed to pipe %lu bytes (of %lu), last=%u: %.100s",
399 i,message->size,c,strerror (errno));
400 fatal (tmp);
401 } while (--i);
402 }
403 else putc ('-',po); /* append error */
404 fflush (po);
405 break;
406 case '&': /* slave wants a proxycopy? */
407 lockproxycopy = T;
408 break;
410 case 'L': /* mm_log() */
411 i = strtoul (event+1,&s,10);
412 if (!s || (*s++ != ' ')) {
413 sprintf (tmp,"Invalid log event arguments: %.500s",event);
414 fatal (tmp);
415 }
416 mm_log (s,i);
417 break;
418 case 'N': /* mm_notify() */
419 st = (MAILSTREAM *) strtoul (event+1,&s,16);
420 if (s && (*s++ == ' ')) {
421 i = strtoul (s,&s,10);/* get severity */
422 if (s && (*s++ == ' ')) {
423 mm_notify ((st == stream) ? stream : NIL,s,i);
424 break;
425 }
426 }
427 sprintf (tmp,"Invalid notify event arguments: %.500s",event);
428 fatal (tmp);
430 case 'S': /* mm_status() */
431 st = (MAILSTREAM *) strtoul (event+1,&s,16);
432 if (s && (*s++ == ' ')) {
433 status.flags = strtoul (s,&s,10);
434 if (s && (*s++ == ' ')) {
435 status.messages = strtoul (s,&s,10);
436 if (s && (*s++ == ' ')) {
437 status.recent = strtoul (s,&s,10);
438 if (s && (*s++ == ' ')) {
439 status.unseen = strtoul (s,&s,10);
440 if (s && (*s++ == ' ')) {
441 status.uidnext = strtoul (s,&s,10);
442 if (s && (*s++ == ' ')) {
443 status.uidvalidity = strtoul (s,&s,10);
444 if (s && (*s++ == ' ')) {
445 mm_status ((st == stream) ? stream : NIL,s,&status);
446 break;
447 }
448 }
449 }
450 }
451 }
452 }
453 }
454 sprintf (tmp,"Invalid status event arguments: %.500s",event);
455 fatal (tmp);
456 case 'C': /* mm_critical() */
457 st = (MAILSTREAM *) strtoul (event+1,&s,16);
458 mm_critical ((st == stream) ? stream : NIL);
459 break;
460 case 'X': /* mm_nocritical() */
461 st = (MAILSTREAM *) strtoul (event+1,&s,16);
462 mm_nocritical ((st == stream) ? stream : NIL);
463 break;
465 case 'D': /* mm_diskerror() */
466 st = (MAILSTREAM *) strtoul (event+1,&s,16);
467 if (s && (*s++ == ' ')) {
468 i = strtoul (s,&s,10);
469 if (s && (*s++ == ' ')) {
470 j = (long) strtoul (s,NIL,10);
471 if (st == stream) /* let's hope it's on usable stream */
472 putc (mm_diskerror (stream,(long) i,j) ? '+' : '-',po);
473 else if (j) { /* serious diskerror on slave-created stream */
474 mm_log ("Retrying disk write to avoid mailbox corruption!",WARN);
475 sleep (5); /* give some time for it to clear up */
476 putc ('-',po); /* don't abort */
477 }
478 else { /* recoverable on slave-created stream */
479 mm_log ("Error on disk write",ERROR);
480 putc ('+',po); /* so abort it */
481 }
482 fflush (po); /* force it out either way */
483 break;
484 }
485 }
486 sprintf (tmp,"Invalid diskerror event arguments: %.500s",event);
487 fatal (tmp);
488 case 'F': /* mm_fatal() */
489 mm_fatal (event+1);
490 break;
491 default: /* random lossage */
492 sprintf (tmp,"Unknown event from execution process: %.500s",event);
493 fatal (tmp);
494 }
495 }
496 fclose (pi); fclose (po); /* done with the pipes */
497 /* get slave status */
498 grim_pid_reap_status (pid,NIL,&ret);
499 if (ret & 0177) { /* signal or stopped */
500 sprintf (tmp,"Execution process terminated abnormally (%lx)",ret);
501 mm_log (tmp,ERROR);
502 ret = NIL;
503 }
504 else ret >>= 8; /* return exit code */
505 (*bn) (BLOCK_NONSENSITIVE,blockdata);
506 }
507 return ret; /* return status */
508 }
510 /* Safe driver calls */
513 /* Safely delete mailbox
514 * Accepts: driver to call under slave
515 * MAIL stream
516 * mailbox name to delete
517 * Returns: T on success, NIL on failure
518 */
520 long safe_delete (DRIVER *dtb,MAILSTREAM *stream,char *mbx)
521 {
522 long ret = master (stream,NIL,NIL);
523 if (lockslavep) exit ((*dtb->mbxdel) (stream,mbx));
524 return ret;
525 }
528 /* Safely rename mailbox
529 * Accepts: driver to call under slave
530 * MAIL stream
531 * old mailbox name
532 * new mailbox name (or NIL for delete)
533 * Returns: T on success, NIL on failure
534 */
536 long safe_rename (DRIVER *dtb,MAILSTREAM *stream,char *old,char *newname)
537 {
538 long ret = master (stream,NIL,NIL);
539 if (lockslavep) exit ((*dtb->mbxren) (stream,old,newname));
540 return ret;
541 }
544 /* Safely get status of mailbox
545 * Accepts: driver to call under slave
546 * MAIL stream
547 * mailbox name
548 * status flags
549 * Returns: T on success, NIL on failure
550 */
552 long safe_status (DRIVER *dtb,MAILSTREAM *stream,char *mbx,long flags)
553 {
554 long ret = master (stream,NIL,NIL);
555 if (lockslavep) exit ((*dtb->status) (stream,mbx,flags));
556 return ret;
557 }
560 /* Scan file for contents
561 * Accepts: driver to call under slave
562 * file name
563 * desired contents
564 * length of contents
565 * length of file
566 * Returns: NIL if contents not found, T if found
567 */
569 long safe_scan_contents (DRIVER *dtb,char *name,char *contents,
570 unsigned long csiz,unsigned long fsiz)
571 {
572 long ret = master (NIL,NIL,NIL);
573 if (lockslavep) exit (scan_contents (dtb,name,contents,csiz,fsiz));
574 return ret;
575 }
577 /* Safely copy message to mailbox
578 * Accepts: driver to call under slave
579 * MAIL stream
580 * sequence
581 * destination mailbox
582 * copy options
583 * Returns: T if success, NIL if failed
584 */
586 long safe_copy (DRIVER *dtb,MAILSTREAM *stream,char *seq,char *mbx,long flags)
587 {
588 mailproxycopy_t pc =
589 (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
590 long ret = master (stream,NIL,NIL);
591 if (lockslavep) {
592 /* don't do proxycopy in slave */
593 if (pc) mail_parameters (stream,SET_MAILPROXYCOPY,(void *) slaveproxycopy);
594 exit ((*dtb->copy) (stream,seq,mbx,flags));
595 }
596 /* do any proxycopy in master */
597 if (lockproxycopy && pc) return (*pc) (stream,seq,mbx,flags);
598 return ret;
599 }
602 /* Append package for slave */
604 typedef struct append_data {
605 int first; /* flag indicating first message */
606 char *flags; /* message flags */
607 char *date; /* message date */
608 char *msg; /* message text */
609 STRING message; /* message stringstruct */
610 } APPENDDATA;
613 /* Safely append message to mailbox
614 * Accepts: driver to call under slave
615 * MAIL stream
616 * destination mailbox
617 * append callback
618 * data for callback
619 * Returns: T if append successful, else NIL
620 */
622 long safe_append (DRIVER *dtb,MAILSTREAM *stream,char *mbx,append_t af,
623 void *data)
624 {
625 long ret = master (stream,af,data);
626 if (lockslavep) {
627 APPENDDATA ad;
628 ad.first = T; /* initialize initial append package */
629 ad.flags = ad.date = ad.msg = NIL;
630 exit ((*dtb->append) (stream,mbx,slave_append,&ad));
631 }
632 return ret;
633 }
635 /* Slave callbacks */
638 /* Message exists (i.e. there are that many messages in the mailbox)
639 * Accepts: MAIL stream
640 * message number
641 */
643 void slave_exists (MAILSTREAM *stream,unsigned long number)
644 {
645 /* this event never passed by slaves */
646 }
649 /* Message expunged
650 * Accepts: MAIL stream
651 * message number
652 */
654 void slave_expunged (MAILSTREAM *stream,unsigned long number)
655 {
656 /* this event never passed by slaves */
657 }
660 /* Message status changed
661 * Accepts: MAIL stream
662 * message number
663 */
665 void slave_flags (MAILSTREAM *stream,unsigned long number)
666 {
667 /* this event never passed by slaves */
668 }
670 /* Mailbox status
671 * Accepts: MAIL stream
672 * mailbox name
673 * mailbox status
674 */
676 void slave_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
677 {
678 int i,c;
679 fprintf (slaveout,"S%lx %lu %lu %lu %lu %lu %lu ",
680 (unsigned long) stream,status->flags,status->messages,status->recent,
681 status->unseen,status->uidnext,status->uidvalidity,mailbox);
682 /* yow! are we paranoid enough yet? */
683 for (i = 0; (i < 500) && (c = *mailbox++); ++i) switch (c) {
684 case '\r': case '\n': /* newline in a mailbox name? */
685 c = ' ';
686 default:
687 putc (c,slaveout);
688 }
689 putc ('\n',slaveout);
690 fflush (slaveout);
691 }
693 /* Notification event
694 * Accepts: MAIL stream
695 * string to log
696 * error flag
697 */
699 void slave_notify (MAILSTREAM *stream,char *string,long errflg)
700 {
701 int i,c;
702 fprintf (slaveout,"N%lx %lu ",(unsigned long) stream,errflg);
703 /* prevent more than 500 bytes */
704 for (i = 0; (i < 500) && (c = *string++); ++i) switch (c) {
705 case '\r': case '\n': /* or embedded newline */
706 c = ' ';
707 default:
708 putc (c,slaveout);
709 }
710 putc ('\n',slaveout);
711 fflush (slaveout);
712 }
715 /* Log an event for the user to see
716 * Accepts: string to log
717 * error flag
718 */
720 void slave_log (char *string,long errflg)
721 {
722 int i,c;
723 fprintf (slaveout,"L%lu ",errflg);
724 /* prevent more than 500 bytes */
725 for (i = 0; (i < 500) && (c = *string++); ++i) switch (c) {
726 case '\r': case '\n': /* or embedded newline */
727 c = ' ';
728 default:
729 putc (c,slaveout);
730 }
731 putc ('\n',slaveout);
732 fflush (slaveout);
733 }
735 /* About to enter critical code
736 * Accepts: stream
737 */
739 void slave_critical (MAILSTREAM *stream)
740 {
741 fprintf (slaveout,"C%lx\n",(unsigned long) stream);
742 fflush (slaveout);
743 }
746 /* About to exit critical code
747 * Accepts: stream
748 */
750 void slave_nocritical (MAILSTREAM *stream)
751 {
752 fprintf (slaveout,"X%lx\n",(unsigned long) stream);
753 fflush (slaveout);
754 }
756 /* Disk error found
757 * Accepts: stream
758 * system error code
759 * flag indicating that mailbox may be clobbered
760 * Returns: abort flag
761 */
763 long slave_diskerror (MAILSTREAM *stream,long errcode,long serious)
764 {
765 char tmp[MAILTMPLEN];
766 int c;
767 long ret = NIL;
768 fprintf (slaveout,"D%lx %lu %lu\n",(unsigned long) stream,errcode,serious);
769 fflush (slaveout);
770 switch (c = getc (slavein)) {
771 case EOF: /* pipe broken */
772 slave_fatal ("Pipe broken reading diskerror response");
773 case '+': /* user wants to abort */
774 ret = LONGT;
775 case '-': /* no abort */
776 break;
777 default:
778 sprintf (tmp,"Unknown master response for diskerror: %c",c);
779 slave_fatal (tmp);
780 }
781 return ret;
782 }
785 /* Log a fatal error event
786 * Accepts: string to log
787 * Does not return
788 */
790 void slave_fatal (char *string)
791 {
792 int i,c;
793 syslog (LOG_ALERT,"IMAP toolkit slave process crash: %.500s",string);
794 putc ('F',slaveout);
795 /* prevent more than 500 bytes */
796 for (i = 0; (i < 500) && (c = *string++); ++i) switch (c) {
797 case '\r': case '\n': /* newline in a mailbox name? */
798 c = ' ';
799 default:
800 putc (c,slaveout);
801 }
802 putc ('\n',slaveout);
803 fflush (slaveout);
804 abort (); /* die */
805 }
807 /* Append read buffer
808 * Accepts: number of bytes to read
809 * error message if fails
810 * Returns: read-in string
811 */
813 static char *slave_append_read (unsigned long n,char *error)
814 {
815 #if 0
816 unsigned long i;
817 #endif
818 int c;
819 char *t,tmp[MAILTMPLEN];
820 char *s = (char *) fs_get (n + 1);
821 s[n] = '\0';
822 #if 0
823 /* This doesn't work on Solaris with GCC. I think that it's a C library
824 * bug, since the problem only shows up if the application does fread()
825 * on some other file
826 */
827 for (t = s; n && ((i = fread (t,1,n,slavein)); t += i,n -= i);
828 #else
829 for (t = s; n && ((c = getc (slavein)) != EOF); *t++ = c,--n);
830 #endif
831 if (n) {
832 sprintf(tmp,"Pipe broken reading %.100s with %lu bytes remaining",error,n);
833 slave_fatal (tmp);
834 }
835 return s;
836 }
838 /* Append message callback
839 * Accepts: MAIL stream
840 * append data package
841 * pointer to return initial flags
842 * pointer to return message internal date
843 * pointer to return stringstruct of message or NIL to stop
844 * Returns: T if success (have message or stop), NIL if error
845 */
847 long slave_append (MAILSTREAM *stream,void *data,char **flags,char **date,
848 STRING **message)
849 {
850 char tmp[MAILTMPLEN];
851 unsigned long n;
852 int c;
853 APPENDDATA *ad = (APPENDDATA *) data;
854 /* flush text of previous message */
855 if (ad->flags) fs_give ((void **) &ad->flags);
856 if (ad->date) fs_give ((void **) &ad->date);
857 if (ad->msg) fs_give ((void **) &ad->msg);
858 *flags = *date = NIL; /* assume no flags or date */
859 fputs ("A\n",slaveout); /* tell master we're doing append callback */
860 fflush (slaveout);
861 switch (c = getc (slavein)) { /* what did master say? */
862 case '+': /* have message, get size of flags */
863 for (n = 0; isdigit (c = getc (slavein)); n *= 10, n += (c - '0'));
864 if (c != ' ') {
865 if (c == EOF) sprintf (tmp,"Pipe broken after flag size %lu",n);
866 sprintf (tmp,"Missing delimiter after flag size %lu: %c",n,c);
867 slave_fatal (tmp);
868 }
869 if (n) *flags = ad->flags = slave_append_read (n,"flags");
870 /* get size of date */
871 for (n = 0; isdigit (c = getc (slavein)); n *= 10, n += (c - '0'));
872 if (c != ' ') {
873 if (c == EOF) sprintf (tmp,"Pipe broken after date size %lu",n);
874 else sprintf (tmp,"Missing delimiter after date size %lu: %c",n,c);
875 slave_fatal (tmp);
876 }
877 if (n) *date = ad->date = slave_append_read (n,"date");
878 /* get size of message */
879 for (n = 0; isdigit (c = getc (slavein)); n *= 10, n += (c - '0'));
880 if (c != ' ') {
881 if (c == EOF) sprintf (tmp,"Pipe broken after message size %lu",n);
882 sprintf (tmp,"Missing delimiter after message size %lu: %c",n,c);
883 slave_fatal (tmp);
884 }
885 if (n) { /* make buffer for message */
886 ad->msg = slave_append_read (n,"message");
887 /* initialize stringstruct */
888 INIT (&ad->message,mail_string,(void *) ad->msg,n);
889 ad->first = NIL; /* no longer first message */
890 *message = &ad->message; /* return message */
891 }
892 else *message = NIL; /* empty message */
893 return LONGT;
894 case '-': /* error */
895 *message = NIL; /* set stop */
896 break;
897 case EOF: /* end of file */
898 slave_fatal ("Pipe broken reading append response");
899 default: /* unknown event */
900 sprintf (tmp,"Unknown master response for append: %c",c);
901 slave_fatal (tmp);
902 }
903 return NIL; /* return failure */
904 }
906 /* Proxy copy across mailbox formats
907 * Accepts: mail stream
908 * sequence to copy on this stream
909 * destination mailbox
910 * option flags
911 * Returns: T if success, else NIL
912 */
914 long slaveproxycopy (MAILSTREAM *stream,char *sequence,char *mailbox,
915 long options)
916 {
917 fputs ("&\n",slaveout); /* redo copy as append */
918 fflush (slaveout);
919 return NIL; /* failure for now */
920 }

UW-IMAP'd extensions by yuuji