1 /* ========================================================================
2 * Copyright 1988-2007 University of Washington
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
8 * http://www.apache.org/licenses/LICENSE-2.0
11 * ========================================================================
15 * Program: flock emulation via fcntl() locking
17 * Author: Mark Crispin
18 * Networks and Distributed Computing
19 * Computing & Communications
20 * University of Washington
21 * Administration Building, AG-44
23 * Internet: MRC@CAC.Washington.EDU
26 * Last Edited: 11 October 2007
29 #undef flock /* name is used as a struct for fcntl */
31 #ifndef NOFSTATVFS /* thank you, SUN. NOT! */
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 */
44 /* Emulator for flock() call
45 * Accepts: file descriptor
47 * Returns: 0 if successful, -1 if failure under BSD conditions
50 int flocksim (int fd
,int op
)
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 */
64 case LOCK_SH
: /* shared */
67 case LOCK_UN
: /* unlock */
70 default: /* default */
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
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.
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.
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!
102 * For what it's worth, here's the scoop on fstatfs() elsewhere:
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.
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.
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.
119 struct statvfs vsbuf
;
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')))
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')))
131 #endif /* NOFSTATVFS */
132 if (!ustat (sbuf
.st_dev
,&usbuf
) && !++usbuf
.f_tinode
) return 0;
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.
141 if ((errno
!= EWOULDBLOCK
) && (errno
!= EAGAIN
) && (errno
!= EACCES
)) {
142 sprintf (tmp
,"Unexpected file locking failure: %.100s",
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 */
150 /* return failure for non-blocking lock */
151 else if (op
& LOCK_NB
) return -1;
153 return 0; /* success */
156 /* Master/slave procedures for safe fcntl() locking.
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.
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.
168 * Note that OSF/1, Linux, and some BSD systems have both broken fcntl()
169 * locking and the working flock() locking.
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.
181 #include <sys/file.h>
188 if ((fd
= creat (file
,0666)) < 0)
189 perror ("TEST FAILED: can't create test file"),_exit (errno
);
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 */
197 if (fcntl (fd
,F_SETLKW
,&fl
) == -1) abort ();
199 if ((fd2
= open (file
,O_RDWR
,0)) < 0) abort ();
201 puts ("parent test ready -- will hang here if locking works correctly");
204 puts ("OS BUG: child terminated");
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 */
215 if (fcntl (fd
,F_SETLKW
,&fl
) == -1) abort ();
216 puts ("OS BUG: child got lock");
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
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!).
236 #include <sys/file.h>
242 if ((fd
= creat (file
,0666)) < 0)
243 perror ("TEST FAILED: can't create test file"),_exit (errno
);
245 if (fork ()) { /* parent */
246 if ((fd
= open (file
,O_RDWR
,0)) < 0) abort();
247 if (flock (fd
,LOCK_SH
) == -1) abort ();
249 if ((fd2
= open (file
,O_RDWR
,0)) < 0) abort ();
251 puts ("parent test ready -- will hang here if flock() works correctly");
254 puts ("OS BUG: child terminated");
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");
267 /* Master/slave details
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.
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.
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.
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.
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).
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.
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.
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>.
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
320 * lockslavep can not be a static, since it's used by the dispatch macros.
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 */
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
337 static long master (MAILSTREAM
*stream
,append_t af
,void *data
)
343 blocknotify_t bn
= (blocknotify_t
) mail_parameters (NIL
,GET_BLOCKNOTIFY
,NIL
);
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]);
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]);
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 */
370 else { /* master process */
371 void *blockdata
= (*bn
) (BLOCK_SENSITIVE
,NIL
);
372 close (pipei
[1]); /* close slave's side of the pipes */
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 */
378 while (fgets (event
,MAILTMPLEN
-1,pi
)) {
379 if (!(s
= strchr (event
,'\n'))) {
380 sprintf (tmp
,"Execution process event string too long: %.500s",event
);
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 */
391 else s
= t
= ""; /* no flags or date if no message */
392 errno
= NIL
; /* reset last error */
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
));
403 else putc ('-',po
); /* append error */
406 case '&': /* slave wants a proxycopy? */
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
);
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
);
427 sprintf (tmp
,"Invalid notify event arguments: %.500s",event
);
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
);
454 sprintf (tmp
,"Invalid status event arguments: %.500s",event
);
456 case 'C': /* mm_critical() */
457 st
= (MAILSTREAM
*) strtoul (event
+1,&s
,16);
458 mm_critical ((st
== stream
) ? stream
: NIL
);
460 case 'X': /* mm_nocritical() */
461 st
= (MAILSTREAM
*) strtoul (event
+1,&s
,16);
462 mm_nocritical ((st
== stream
) ? stream
: NIL
);
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 */
478 else { /* recoverable on slave-created stream */
479 mm_log ("Error on disk write",ERROR
);
480 putc ('+',po
); /* so abort it */
482 fflush (po
); /* force it out either way */
486 sprintf (tmp
,"Invalid diskerror event arguments: %.500s",event
);
488 case 'F': /* mm_fatal() */
491 default: /* random lossage */
492 sprintf (tmp
,"Unknown event from execution process: %.500s",event
);
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
);
504 else ret
>>= 8; /* return exit code */
505 (*bn
) (BLOCK_NONSENSITIVE
,blockdata
);
507 return ret
; /* return status */
510 /* Safe driver calls */
513 /* Safely delete mailbox
514 * Accepts: driver to call under slave
516 * mailbox name to delete
517 * Returns: T on success, NIL on failure
520 long safe_delete (DRIVER
*dtb
,MAILSTREAM
*stream
,char *mbx
)
522 long ret
= master (stream
,NIL
,NIL
);
523 if (lockslavep
) exit ((*dtb
->mbxdel
) (stream
,mbx
));
528 /* Safely rename mailbox
529 * Accepts: driver to call under slave
532 * new mailbox name (or NIL for delete)
533 * Returns: T on success, NIL on failure
536 long safe_rename (DRIVER
*dtb
,MAILSTREAM
*stream
,char *old
,char *newname
)
538 long ret
= master (stream
,NIL
,NIL
);
539 if (lockslavep
) exit ((*dtb
->mbxren
) (stream
,old
,newname
));
544 /* Safely get status of mailbox
545 * Accepts: driver to call under slave
549 * Returns: T on success, NIL on failure
552 long safe_status (DRIVER
*dtb
,MAILSTREAM
*stream
,char *mbx
,long flags
)
554 long ret
= master (stream
,NIL
,NIL
);
555 if (lockslavep
) exit ((*dtb
->status
) (stream
,mbx
,flags
));
560 /* Scan file for contents
561 * Accepts: driver to call under slave
566 * Returns: NIL if contents not found, T if found
569 long safe_scan_contents (DRIVER
*dtb
,char *name
,char *contents
,
570 unsigned long csiz
,unsigned long fsiz
)
572 long ret
= master (NIL
,NIL
,NIL
);
573 if (lockslavep
) exit (scan_contents (dtb
,name
,contents
,csiz
,fsiz
));
577 /* Safely copy message to mailbox
578 * Accepts: driver to call under slave
581 * destination mailbox
583 * Returns: T if success, NIL if failed
586 long safe_copy (DRIVER
*dtb
,MAILSTREAM
*stream
,char *seq
,char *mbx
,long flags
)
589 (mailproxycopy_t
) mail_parameters (stream
,GET_MAILPROXYCOPY
,NIL
);
590 long ret
= master (stream
,NIL
,NIL
);
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
));
596 /* do any proxycopy in master */
597 if (lockproxycopy
&& pc
) return (*pc
) (stream
,seq
,mbx
,flags
);
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 */
613 /* Safely append message to mailbox
614 * Accepts: driver to call under slave
616 * destination mailbox
619 * Returns: T if append successful, else NIL
622 long safe_append (DRIVER
*dtb
,MAILSTREAM
*stream
,char *mbx
,append_t af
,
625 long ret
= master (stream
,af
,data
);
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
));
635 /* Slave callbacks */
638 /* Message exists (i.e. there are that many messages in the mailbox)
639 * Accepts: MAIL stream
643 void slave_exists (MAILSTREAM
*stream
,unsigned long number
)
645 /* this event never passed by slaves */
650 * Accepts: MAIL stream
654 void slave_expunged (MAILSTREAM
*stream
,unsigned long number
)
656 /* this event never passed by slaves */
660 /* Message status changed
661 * Accepts: MAIL stream
665 void slave_flags (MAILSTREAM
*stream
,unsigned long number
)
667 /* this event never passed by slaves */
671 * Accepts: MAIL stream
676 void slave_status (MAILSTREAM
*stream
,char *mailbox
,MAILSTATUS
*status
)
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? */
689 putc ('\n',slaveout
);
693 /* Notification event
694 * Accepts: MAIL stream
699 void slave_notify (MAILSTREAM
*stream
,char *string
,long errflg
)
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 */
710 putc ('\n',slaveout
);
715 /* Log an event for the user to see
716 * Accepts: string to log
720 void slave_log (char *string
,long errflg
)
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 */
731 putc ('\n',slaveout
);
735 /* About to enter critical code
739 void slave_critical (MAILSTREAM
*stream
)
741 fprintf (slaveout
,"C%lx\n",(unsigned long) stream
);
746 /* About to exit critical code
750 void slave_nocritical (MAILSTREAM
*stream
)
752 fprintf (slaveout
,"X%lx\n",(unsigned long) stream
);
759 * flag indicating that mailbox may be clobbered
760 * Returns: abort flag
763 long slave_diskerror (MAILSTREAM
*stream
,long errcode
,long serious
)
765 char tmp
[MAILTMPLEN
];
768 fprintf (slaveout
,"D%lx %lu %lu\n",(unsigned long) stream
,errcode
,serious
);
770 switch (c
= getc (slavein
)) {
771 case EOF
: /* pipe broken */
772 slave_fatal ("Pipe broken reading diskerror response");
773 case '+': /* user wants to abort */
775 case '-': /* no abort */
778 sprintf (tmp
,"Unknown master response for diskerror: %c",c
);
785 /* Log a fatal error event
786 * Accepts: string to log
790 void slave_fatal (char *string
)
793 syslog (LOG_ALERT
,"IMAP toolkit slave process crash: %.500s",string
);
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? */
802 putc ('\n',slaveout
);
807 /* Append read buffer
808 * Accepts: number of bytes to read
809 * error message if fails
810 * Returns: read-in string
813 static char *slave_append_read (unsigned long n
,char *error
)
819 char *t
,tmp
[MAILTMPLEN
];
820 char *s
= (char *) fs_get (n
+ 1);
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()
827 for (t
= s
; n
&& ((i
= fread (t
,1,n
,slavein
)); t
+= i
,n
-= i
);
829 for (t
= s
; n
&& ((c
= getc (slavein
)) != EOF
); *t
++ = c
,--n
);
832 sprintf(tmp
,"Pipe broken reading %.100s with %lu bytes remaining",error
,n
);
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
847 long slave_append (MAILSTREAM
*stream
,void *data
,char **flags
,char **date
,
850 char tmp
[MAILTMPLEN
];
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 */
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'));
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
);
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'));
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
);
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'));
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
);
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 */
892 else *message
= NIL
; /* empty message */
894 case '-': /* error */
895 *message
= NIL
; /* set stop */
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
);
903 return NIL
; /* return failure */
906 /* Proxy copy across mailbox formats
907 * Accepts: mail stream
908 * sequence to copy on this stream
909 * destination mailbox
911 * Returns: T if success, else NIL
914 long slaveproxycopy (MAILSTREAM
*stream
,char *sequence
,char *mailbox
,
917 fputs ("&\n",slaveout
); /* redo copy as append */
919 return NIL
; /* failure for now */