(main): If the lock call fails with EBUSY or
[bpt/emacs.git] / lib-src / movemail.c
1 /* movemail foo bar -- move file foo to file bar,
2 locking file foo the way /bin/mail respects.
3 Copyright (C) 1986, 1992, 1993, 1994, 1996 Free Software Foundation, Inc.
4
5 This file is part of GNU Emacs.
6
7 GNU Emacs is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs; see the file COPYING. If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA. */
21
22 /* Important notice: defining MAIL_USE_FLOCK or MAIL_USE_LOCKF *will
23 cause loss of mail* if you do it on a system that does not normally
24 use flock as its way of interlocking access to inbox files. The
25 setting of MAIL_USE_FLOCK and MAIL_USE_LOCKF *must agree* with the
26 system's own conventions. It is not a choice that is up to you.
27
28 So, if your system uses lock files rather than flock, then the only way
29 you can get proper operation is to enable movemail to write lockfiles there.
30 This means you must either give that directory access modes
31 that permit everyone to write lockfiles in it, or you must make movemail
32 a setuid or setgid program. */
33
34 /*
35 * Modified January, 1986 by Michael R. Gretzinger (Project Athena)
36 *
37 * Added POP (Post Office Protocol) service. When compiled -DMAIL_USE_POP
38 * movemail will accept input filename arguments of the form
39 * "po:username". This will cause movemail to open a connection to
40 * a pop server running on $MAILHOST (environment variable). Movemail
41 * must be setuid to root in order to work with POP.
42 *
43 * New module: popmail.c
44 * Modified routines:
45 * main - added code within #ifdef MAIL_USE_POP; added setuid (getuid ())
46 * after POP code.
47 * New routines in movemail.c:
48 * get_errmsg - return pointer to system error message
49 *
50 * Modified August, 1993 by Jonathan Kamens (OpenVision Technologies)
51 *
52 * Move all of the POP code into a separate file, "pop.c".
53 * Use strerror instead of get_errmsg.
54 *
55 */
56
57 #define NO_SHORTNAMES /* Tell config not to load remap.h */
58 #include <../src/config.h>
59 #include <sys/types.h>
60 #include <sys/stat.h>
61 #include <sys/file.h>
62 #include <stdio.h>
63 #include <errno.h>
64 #include <../src/syswait.h>
65 #ifdef MAIL_USE_POP
66 #include "pop.h"
67 #endif
68
69 #ifdef MSDOS
70 #undef access
71 #endif /* MSDOS */
72
73 #ifndef DIRECTORY_SEP
74 #define DIRECTORY_SEP '/'
75 #endif
76 #ifndef IS_DIRECTORY_SEP
77 #define IS_DIRECTORY_SEP(_c_) ((_c_) == DIRECTORY_SEP)
78 #endif
79
80 #ifdef WINDOWSNT
81 #undef access
82 #undef unlink
83 #define fork() 0
84 #define sys_wait(var) (*(var) = 0)
85 /* Unfortunately, Samba doesn't seem to properly lock Unix files even
86 though the locking call succeeds (and indeed blocks local access from
87 other NT programs). If you have direct file access using an NFS
88 client or something other than Samba, the locking call might work
89 properly - make sure it does before you enable this! */
90 #define DISABLE_DIRECT_ACCESS
91 #endif /* WINDOWSNT */
92
93 #ifdef USG
94 #include <fcntl.h>
95 #include <unistd.h>
96 #ifndef F_OK
97 #define F_OK 0
98 #define X_OK 1
99 #define W_OK 2
100 #define R_OK 4
101 #endif
102 #endif /* USG */
103
104 #ifdef HAVE_UNISTD_H
105 #include <unistd.h>
106 #endif
107
108 #if defined (XENIX) || defined (WINDOWSNT)
109 #include <sys/locking.h>
110 #endif
111
112 #ifdef MAIL_USE_LOCKF
113 #define MAIL_USE_SYSTEM_LOCK
114 #endif
115
116 #ifdef MAIL_USE_FLOCK
117 #define MAIL_USE_SYSTEM_LOCK
118 #endif
119
120 #ifdef MAIL_USE_MMDF
121 extern int lk_open (), lk_close ();
122 #endif
123
124 /* Cancel substitutions made by config.h for Emacs. */
125 #undef open
126 #undef read
127 #undef write
128 #undef close
129
130 #ifndef errno
131 extern int errno;
132 #endif
133 char *strerror ();
134
135 void fatal ();
136 void error ();
137 void pfatal_with_name ();
138 void pfatal_and_delete ();
139 char *concat ();
140 long *xmalloc ();
141 int popmail ();
142 int pop_retr ();
143 int mbx_write ();
144 int mbx_delimit_begin ();
145 int mbx_delimit_end ();
146
147 /* Nonzero means this is name of a lock file to delete on fatal error. */
148 char *delete_lockname;
149
150 int
151 main (argc, argv)
152 int argc;
153 char **argv;
154 {
155 char *inname, *outname;
156 int indesc, outdesc;
157 int nread;
158 WAITTYPE status;
159
160 #ifndef MAIL_USE_SYSTEM_LOCK
161 struct stat st;
162 long now;
163 int tem;
164 char *lockname, *p;
165 char *tempname;
166 int desc;
167 #endif /* not MAIL_USE_SYSTEM_LOCK */
168
169 delete_lockname = 0;
170
171 if (argc < 3)
172 {
173 fprintf (stderr, "Usage: movemail inbox destfile [POP-password]\n");
174 exit(1);
175 }
176
177 inname = argv[1];
178 outname = argv[2];
179
180 #ifdef MAIL_USE_MMDF
181 mmdf_init (argv[0]);
182 #endif
183
184 if (*outname == 0)
185 fatal ("Destination file name is empty", 0);
186
187 /* Check access to output file. */
188 if (access (outname, F_OK) == 0 && access (outname, W_OK) != 0)
189 pfatal_with_name (outname);
190
191 /* Also check that outname's directory is writable to the real uid. */
192 {
193 char *buf = (char *) xmalloc (strlen (outname) + 1);
194 char *p;
195 strcpy (buf, outname);
196 p = buf + strlen (buf);
197 while (p > buf && !IS_DIRECTORY_SEP (p[-1]))
198 *--p = 0;
199 if (p == buf)
200 *p++ = '.';
201 if (access (buf, W_OK) != 0)
202 pfatal_with_name (buf);
203 free (buf);
204 }
205
206 #ifdef MAIL_USE_POP
207 if (!strncmp (inname, "po:", 3))
208 {
209 int status;
210
211 status = popmail (inname + 3, outname, argc > 3 ? argv[3] : NULL);
212 exit (status);
213 }
214
215 setuid (getuid ());
216 #endif /* MAIL_USE_POP */
217
218 #ifndef DISABLE_DIRECT_ACCESS
219
220 /* Check access to input file. */
221 if (access (inname, R_OK | W_OK) != 0)
222 pfatal_with_name (inname);
223
224 #ifndef MAIL_USE_MMDF
225 #ifndef MAIL_USE_SYSTEM_LOCK
226 /* Use a lock file named after our first argument with .lock appended:
227 If it exists, the mail file is locked. */
228 /* Note: this locking mechanism is *required* by the mailer
229 (on systems which use it) to prevent loss of mail.
230
231 On systems that use a lock file, extracting the mail without locking
232 WILL occasionally cause loss of mail due to timing errors!
233
234 So, if creation of the lock file fails
235 due to access permission on the mail spool directory,
236 you simply MUST change the permission
237 and/or make movemail a setgid program
238 so it can create lock files properly.
239
240 You might also wish to verify that your system is one
241 which uses lock files for this purpose. Some systems use other methods.
242
243 If your system uses the `flock' system call for mail locking,
244 define MAIL_USE_SYSTEM_LOCK in config.h or the s-*.h file
245 and recompile movemail. If the s- file for your system
246 should define MAIL_USE_SYSTEM_LOCK but does not, send a bug report
247 to bug-gnu-emacs@prep.ai.mit.edu so we can fix it. */
248
249 lockname = concat (inname, ".lock", "");
250 tempname = (char *) xmalloc (strlen (inname) + strlen ("EXXXXXX") + 1);
251 strcpy (tempname, inname);
252 p = tempname + strlen (tempname);
253 while (p != tempname && !IS_DIRECTORY_SEP (p[-1]))
254 p--;
255 *p = 0;
256 strcpy (p, "EXXXXXX");
257 mktemp (tempname);
258 unlink (tempname);
259
260 while (1)
261 {
262 /* Create the lock file, but not under the lock file name. */
263 /* Give up if cannot do that. */
264 desc = open (tempname, O_WRONLY | O_CREAT | O_EXCL, 0666);
265 if (desc < 0)
266 {
267 char *message = (char *) xmalloc (strlen (tempname) + 50);
268 sprintf (message, "%s--see source file lib-src/movemail.c",
269 tempname);
270 pfatal_with_name (message);
271 }
272 close (desc);
273
274 tem = link (tempname, lockname);
275 unlink (tempname);
276 if (tem >= 0)
277 break;
278 sleep (1);
279
280 /* If lock file is five minutes old, unlock it.
281 Five minutes should be good enough to cope with crashes
282 and wedgitude, and long enough to avoid being fooled
283 by time differences between machines. */
284 if (stat (lockname, &st) >= 0)
285 {
286 now = time (0);
287 if (st.st_ctime < now - 300)
288 unlink (lockname);
289 }
290 }
291
292 delete_lockname = lockname;
293 #endif /* not MAIL_USE_SYSTEM_LOCK */
294 #endif /* not MAIL_USE_MMDF */
295
296 if (fork () == 0)
297 {
298 int lockcount = 0;
299 int status;
300
301 setuid (getuid ());
302
303 #ifndef MAIL_USE_MMDF
304 #ifdef MAIL_USE_SYSTEM_LOCK
305 indesc = open (inname, O_RDWR);
306 #else /* if not MAIL_USE_SYSTEM_LOCK */
307 indesc = open (inname, O_RDONLY);
308 #endif /* not MAIL_USE_SYSTEM_LOCK */
309 #else /* MAIL_USE_MMDF */
310 indesc = lk_open (inname, O_RDONLY, 0, 0, 10);
311 #endif /* MAIL_USE_MMDF */
312
313 if (indesc < 0)
314 pfatal_with_name (inname);
315
316 #if defined (BSD_SYSTEM) || defined (XENIX)
317 /* In case movemail is setuid to root, make sure the user can
318 read the output file. */
319 /* This is desirable for all systems
320 but I don't want to assume all have the umask system call */
321 umask (umask (0) & 0333);
322 #endif /* BSD_SYSTEM || XENIX */
323 outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
324 if (outdesc < 0)
325 pfatal_with_name (outname);
326
327 /* This label exists so we can retry locking
328 after a delay, if it got EAGAIN or EBUSY. */
329 retry_lock:
330
331 /* Try to lock it. */
332 #ifdef MAIL_USE_SYSTEM_LOCK
333 #ifdef MAIL_USE_LOCKF
334 status = lockf (indesc, F_LOCK, 0);
335 #else /* not MAIL_USE_LOCKF */
336 #ifdef XENIX
337 status = locking (indesc, LK_RLCK, 0L);
338 #else
339 #ifdef WINDOWSNT
340 status = locking (indesc, LK_RLCK, -1L);
341 #else
342 status = flock (indesc, LOCK_EX);
343 #endif
344 #endif
345 #endif /* not MAIL_USE_LOCKF */
346 #endif /* MAIL_USE_SYSTEM_LOCK */
347
348 /* If it fails, retry up to 5 times
349 for certain failure codes. */
350 if (status < 0)
351 {
352 if (++lockcount <= 5)
353 {
354 #ifdef EAGAIN
355 if (errno == EAGAIN)
356 {
357 sleep (1);
358 goto retry_lock;
359 }
360 #endif
361 #ifdef EBUSY
362 if (errno == EBUSY)
363 {
364 sleep (1);
365 goto retry_lock;
366 }
367 #endif
368 }
369
370 pfatal_with_name (inname);
371 }
372
373 {
374 char buf[1024];
375
376 while (1)
377 {
378 nread = read (indesc, buf, sizeof buf);
379 if (nread != write (outdesc, buf, nread))
380 {
381 int saved_errno = errno;
382 unlink (outname);
383 errno = saved_errno;
384 pfatal_with_name (outname);
385 }
386 if (nread < sizeof buf)
387 break;
388 }
389 }
390
391 #ifdef BSD_SYSTEM
392 if (fsync (outdesc) < 0)
393 pfatal_and_delete (outname);
394 #endif
395
396 /* Check to make sure no errors before we zap the inbox. */
397 if (close (outdesc) != 0)
398 pfatal_and_delete (outname);
399
400 #ifdef MAIL_USE_SYSTEM_LOCK
401 #if defined (STRIDE) || defined (XENIX) || defined (WINDOWSNT)
402 /* Stride, xenix have file locking, but no ftruncate. This mess will do. */
403 close (open (inname, O_CREAT | O_TRUNC | O_RDWR, 0666));
404 #else
405 ftruncate (indesc, 0L);
406 #endif /* STRIDE or XENIX */
407 #endif /* MAIL_USE_SYSTEM_LOCK */
408
409 #ifdef MAIL_USE_MMDF
410 lk_close (indesc, 0, 0, 0);
411 #else
412 close (indesc);
413 #endif
414
415 #ifndef MAIL_USE_SYSTEM_LOCK
416 /* Delete the input file; if we can't, at least get rid of its
417 contents. */
418 #ifdef MAIL_UNLINK_SPOOL
419 /* This is generally bad to do, because it destroys the permissions
420 that were set on the file. Better to just empty the file. */
421 if (unlink (inname) < 0 && errno != ENOENT)
422 #endif /* MAIL_UNLINK_SPOOL */
423 creat (inname, 0600);
424 #endif /* not MAIL_USE_SYSTEM_LOCK */
425
426 exit (0);
427 }
428
429 wait (&status);
430 if (!WIFEXITED (status))
431 exit (1);
432 else if (WRETCODE (status) != 0)
433 exit (WRETCODE (status));
434
435 #if !defined (MAIL_USE_MMDF) && !defined (MAIL_USE_SYSTEM_LOCK)
436 unlink (lockname);
437 #endif /* not MAIL_USE_MMDF and not MAIL_USE_SYSTEM_LOCK */
438
439 #endif /* ! DISABLE_DIRECT_ACCESS */
440
441 return 0;
442 }
443 \f
444 /* Print error message and exit. */
445
446 void
447 fatal (s1, s2)
448 char *s1, *s2;
449 {
450 if (delete_lockname)
451 unlink (delete_lockname);
452 error (s1, s2);
453 exit (1);
454 }
455
456 /* Print error message. `s1' is printf control string, `s2' is arg for it. */
457
458 void
459 error (s1, s2, s3)
460 char *s1, *s2, *s3;
461 {
462 fprintf (stderr, "movemail: ");
463 fprintf (stderr, s1, s2, s3);
464 fprintf (stderr, "\n");
465 }
466
467 void
468 pfatal_with_name (name)
469 char *name;
470 {
471 char *s = concat ("", strerror (errno), " for %s");
472 fatal (s, name);
473 }
474
475 void
476 pfatal_and_delete (name)
477 char *name;
478 {
479 char *s = concat ("", strerror (errno), " for %s");
480 unlink (name);
481 fatal (s, name);
482 }
483
484 /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
485
486 char *
487 concat (s1, s2, s3)
488 char *s1, *s2, *s3;
489 {
490 int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
491 char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
492
493 strcpy (result, s1);
494 strcpy (result + len1, s2);
495 strcpy (result + len1 + len2, s3);
496 *(result + len1 + len2 + len3) = 0;
497
498 return result;
499 }
500
501 /* Like malloc but get fatal error if memory is exhausted. */
502
503 long *
504 xmalloc (size)
505 unsigned size;
506 {
507 long *result = (long *) malloc (size);
508 if (!result)
509 fatal ("virtual memory exhausted", 0);
510 return result;
511 }
512 \f
513 /* This is the guts of the interface to the Post Office Protocol. */
514
515 #ifdef MAIL_USE_POP
516
517 #ifndef WINDOWSNT
518 #include <sys/socket.h>
519 #include <netinet/in.h>
520 #include <netdb.h>
521 #else
522 #undef _WINSOCKAPI_
523 #include <winsock.h>
524 #endif
525 #include <stdio.h>
526 #include <pwd.h>
527
528 #ifdef USG
529 #include <fcntl.h>
530 /* Cancel substitutions made by config.h for Emacs. */
531 #undef open
532 #undef read
533 #undef write
534 #undef close
535 #endif /* USG */
536
537 #define NOTOK (-1)
538 #define OK 0
539 #define DONE 1
540
541 char *progname;
542 FILE *sfi;
543 FILE *sfo;
544 char ibuffer[BUFSIZ];
545 char obuffer[BUFSIZ];
546 char Errmsg[80];
547
548 popmail (user, outfile, password)
549 char *user;
550 char *outfile;
551 char *password;
552 {
553 int nmsgs, nbytes;
554 register int i;
555 int mbfi;
556 FILE *mbf;
557 char *getenv ();
558 int mbx_write ();
559 popserver server;
560 extern char *strerror ();
561
562 server = pop_open (0, user, password, POP_NO_GETPASS);
563 if (! server)
564 {
565 error (pop_error);
566 return (1);
567 }
568
569 if (pop_stat (server, &nmsgs, &nbytes))
570 {
571 error (pop_error);
572 return (1);
573 }
574
575 if (!nmsgs)
576 {
577 pop_close (server);
578 return (0);
579 }
580
581 mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
582 if (mbfi < 0)
583 {
584 pop_close (server);
585 error ("Error in open: %s, %s", strerror (errno), outfile);
586 return (1);
587 }
588 fchown (mbfi, getuid (), -1);
589
590 if ((mbf = fdopen (mbfi, "wb")) == NULL)
591 {
592 pop_close (server);
593 error ("Error in fdopen: %s", strerror (errno));
594 close (mbfi);
595 unlink (outfile);
596 return (1);
597 }
598
599 for (i = 1; i <= nmsgs; i++)
600 {
601 mbx_delimit_begin (mbf);
602 if (pop_retr (server, i, mbx_write, mbf) != OK)
603 {
604 error (Errmsg);
605 close (mbfi);
606 return (1);
607 }
608 mbx_delimit_end (mbf);
609 fflush (mbf);
610 if (ferror (mbf))
611 {
612 error ("Error in fflush: %s", strerror (errno));
613 pop_close (server);
614 close (mbfi);
615 return (1);
616 }
617 }
618
619 /* On AFS, a call to write only modifies the file in the local
620 * workstation's AFS cache. The changes are not written to the server
621 * until a call to fsync or close is made. Users with AFS home
622 * directories have lost mail when over quota because these checks were
623 * not made in previous versions of movemail. */
624
625 #ifdef BSD_SYSTEM
626 if (fsync (mbfi) < 0)
627 {
628 error ("Error in fsync: %s", strerror (errno));
629 return (1);
630 }
631 #endif
632
633 if (close (mbfi) == -1)
634 {
635 error ("Error in close: %s", strerror (errno));
636 return (1);
637 }
638
639 for (i = 1; i <= nmsgs; i++)
640 {
641 if (pop_delete (server, i))
642 {
643 error (pop_error);
644 pop_close (server);
645 return (1);
646 }
647 }
648
649 if (pop_quit (server))
650 {
651 error (pop_error);
652 return (1);
653 }
654
655 return (0);
656 }
657
658 pop_retr (server, msgno, action, arg)
659 popserver server;
660 int (*action)();
661 {
662 extern char *strerror ();
663 char *line;
664 int ret;
665
666 if (pop_retrieve_first (server, msgno, &line))
667 {
668 strncpy (Errmsg, pop_error, sizeof (Errmsg));
669 Errmsg[sizeof (Errmsg)-1] = '\0';
670 return (NOTOK);
671 }
672
673 while (! (ret = pop_retrieve_next (server, &line)))
674 {
675 if (! line)
676 break;
677
678 if ((*action)(line, arg) != OK)
679 {
680 strcpy (Errmsg, strerror (errno));
681 pop_close (server);
682 return (NOTOK);
683 }
684 }
685
686 if (ret)
687 {
688 strncpy (Errmsg, pop_error, sizeof (Errmsg));
689 Errmsg[sizeof (Errmsg)-1] = '\0';
690 return (NOTOK);
691 }
692
693 return (OK);
694 }
695
696 /* Do this as a macro instead of using strcmp to save on execution time. */
697 #define IS_FROM_LINE(a) ((a[0] == 'F') \
698 && (a[1] == 'r') \
699 && (a[2] == 'o') \
700 && (a[3] == 'm') \
701 && (a[4] == ' '))
702
703 int
704 mbx_write (line, mbf)
705 char *line;
706 FILE *mbf;
707 {
708 if (IS_FROM_LINE (line))
709 {
710 if (fputc ('>', mbf) == EOF)
711 return (NOTOK);
712 }
713 if (fputs (line, mbf) == EOF)
714 return (NOTOK);
715 if (fputc (0x0a, mbf) == EOF)
716 return (NOTOK);
717 return (OK);
718 }
719
720 int
721 mbx_delimit_begin (mbf)
722 FILE *mbf;
723 {
724 if (fputs ("\f\n0, unseen,,\n", mbf) == EOF)
725 return (NOTOK);
726 return (OK);
727 }
728
729 mbx_delimit_end (mbf)
730 FILE *mbf;
731 {
732 if (putc ('\037', mbf) == EOF)
733 return (NOTOK);
734 return (OK);
735 }
736
737 #endif /* MAIL_USE_POP */
738 \f
739 #ifndef HAVE_STRERROR
740 char *
741 strerror (errnum)
742 int errnum;
743 {
744 extern char *sys_errlist[];
745 extern int sys_nerr;
746
747 if (errnum >= 0 && errnum < sys_nerr)
748 return sys_errlist[errnum];
749 return (char *) "Unknown error";
750 }
751
752 #endif /* ! HAVE_STRERROR */