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