Add TRUSTED_CONFIG_PREFIX_FILE option
[exim.git] / src / src / exim_lock.c
1 /* $Cambridge: exim/src/src/exim_lock.c,v 1.4 2010/05/29 12:11:48 pdp Exp $ */
2
3 /* A program to lock a file exactly as Exim would, for investigation of
4 interlocking problems.
5
6 Options:  -fcntl    use fcntl() lock
7           -flock    use flock() lock
8           -lockfile use lock file
9           -mbx      use mbx locking rules, with either fcntl() or flock()
10
11 Default is -fcntl -lockfile.
12
13 Argument: the name of the lock file
14 */
15
16 #include "os.h"
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <signal.h>
22 #include <errno.h>
23 #include <time.h>
24 #include <netdb.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <utime.h>
28 #include <sys/utsname.h>
29 #include <sys/stat.h>
30 #include <sys/file.h>
31 #include <pwd.h>
32
33 /* Not all systems have flock() available. Those that do must define LOCK_SH
34 in sys/file.h. */
35
36 #ifndef LOCK_SH
37 #define NO_FLOCK
38 #endif
39
40
41 typedef int BOOL;
42 #define FALSE 0
43 #define TRUE  1
44
45
46 /* Flag for timeout signal handler */
47
48 static int sigalrm_seen = FALSE;
49
50
51 /* We need to pull in strerror() and os_non_restarting_signal() from the
52 os.c source, if they are required for this OS. However, we don't need any of
53 the other stuff in os.c, so force the other macros to omit it. */
54
55 #ifndef OS_RESTARTING_SIGNAL
56   #define OS_RESTARTING_SIGNAL
57 #endif
58
59 #ifndef OS_STRSIGNAL
60   #define OS_STRSIGNAL
61 #endif
62
63 #ifndef OS_STREXIT
64   #define OS_STREXIT
65 #endif
66
67 #ifndef OS_LOAD_AVERAGE
68   #define OS_LOAD_AVERAGE
69 #endif
70
71 #ifndef FIND_RUNNING_INTERFACES
72   #define FIND_RUNNING_INTERFACES
73 #endif
74
75 #include "../src/os.c"
76
77
78
79 /*************************************************
80 *             Timeout handler                    *
81 *************************************************/
82
83 static void
84 sigalrm_handler(int sig)
85 {
86 sig = sig;      /* Keep picky compilers happy */
87 sigalrm_seen = TRUE;
88 }
89
90
91
92 /*************************************************
93 *           Give usage and die                   *
94 *************************************************/
95
96 static void
97 usage(void)
98 {
99 printf("usage: exim_lock [-v] [-q] [-lockfile] [-fcntl] [-flock] [-mbx]\n"
100        "       [-retries <n>] [-interval <n>] [-timeout <n>] [-restore-times]\n"
101        "       <file name> [command]\n");
102 exit(1);
103 }
104
105
106
107 /*************************************************
108 *         Apply a lock to a file descriptor      *
109 *************************************************/
110
111 static int
112 apply_lock(int fd, int fcntltype, BOOL dofcntl, int fcntltime, BOOL doflock,
113     int flocktime)
114 {
115 int yield = 0;
116 int save_errno;
117 struct flock lock_data;
118 lock_data.l_type = fcntltype;
119 lock_data.l_whence = lock_data.l_start = lock_data.l_len = 0;
120
121 sigalrm_seen = FALSE;
122
123 if (dofcntl)
124   {
125   if (fcntltime > 0)
126     {
127     os_non_restarting_signal(SIGALRM, sigalrm_handler);
128     alarm(fcntltime);
129     yield = fcntl(fd, F_SETLKW, &lock_data);
130     save_errno = errno;
131     alarm(0);
132     errno = save_errno;
133     }
134   else yield = fcntl(fd, F_SETLK, &lock_data);
135   if (yield < 0) printf("exim_lock: fcntl() failed: %s\n", strerror(errno));
136   }
137
138 #ifndef NO_FLOCK
139 if (doflock && (yield >= 0))
140   {
141   int flocktype = (fcntltype == F_WRLCK)? LOCK_EX : LOCK_SH;
142   if (flocktime > 0)
143     {
144     os_non_restarting_signal(SIGALRM, sigalrm_handler);
145     alarm(flocktime);
146     yield = flock(fd, flocktype);
147     save_errno = errno;
148     alarm(0);
149     errno = save_errno;
150     }
151   else yield = flock(fd, flocktype | LOCK_NB);
152   if (yield < 0) printf("exim_lock: flock() failed: %s\n", strerror(errno));
153   }
154 #endif
155
156 return yield;
157 }
158
159
160
161 /*************************************************
162 *           The exim_lock program                *
163 *************************************************/
164
165 int main(int argc, char **argv)
166 {
167 int  lock_retries = 10;
168 int  lock_interval = 3;
169 int  lock_fcntl_timeout = 0;
170 int  lock_flock_timeout = 0;
171 int  i, j, len;
172 int  fd = -1;
173 int  hd = -1;
174 int  md = -1;
175 int  yield = 0;
176 int  now = time(NULL);
177 BOOL use_lockfile = FALSE;
178 BOOL use_fcntl = FALSE;
179 BOOL use_flock = FALSE;
180 BOOL use_mbx = FALSE;
181 BOOL verbose = FALSE;
182 BOOL quiet = FALSE;
183 BOOL restore_times = FALSE;
184 char *filename;
185 char *lockname = NULL, *hitchname = NULL;
186 char *primary_hostname, *command;
187 struct utsname s;
188 char buffer[256];
189 char tempname[256];
190
191 /* Decode options */
192
193 for (i = 1; i < argc; i++)
194   {
195   char *arg = argv[i];
196   if (*arg != '-') break;
197   if (strcmp(arg, "-fcntl") == 0) use_fcntl = TRUE;
198   else if (strcmp(arg, "-flock") == 0) use_flock = TRUE;
199   else if (strcmp(arg, "-lockfile") == 0) use_lockfile = TRUE;
200   else if (strcmp(arg, "-mbx") == 0) use_mbx = TRUE;
201   else if (strcmp(arg, "-v") == 0) verbose = TRUE;
202   else if (strcmp(arg, "-q") == 0) quiet = TRUE;
203   else if (strcmp(arg, "-restore-times") == 0) restore_times = TRUE;
204   else if (++i < argc)
205     {
206     int value = atoi(argv[i]);
207     if (strcmp(arg, "-retries") == 0) lock_retries = value;
208     else if (strcmp(arg, "-interval") == 0) lock_interval = value;
209     else if (strcmp(arg, "-timeout") == 0)
210       lock_fcntl_timeout = lock_flock_timeout = value;
211     else usage();
212     }
213   else usage();
214   }
215
216 if (quiet) verbose = 0;
217
218 /* Can't use flock() if the OS doesn't provide it */
219
220 #ifdef NO_FLOCK
221 if (use_flock)
222   {
223   printf("exim_lock: can't use flock() because it was not available in the\n"
224          "           operating system when exim_lock was compiled\n");
225   exit(1);
226   }
227 #endif
228
229 /* Default is to use lockfiles and fcntl(). */
230
231 if (!use_lockfile && !use_fcntl && !use_flock && !use_mbx)
232   use_lockfile = use_fcntl = TRUE;
233
234 /* Default fcntl() for use with mbx */
235
236 if (use_mbx && !use_fcntl && !use_flock) use_fcntl = TRUE;
237
238 /* Unset unused timeouts */
239
240 if (!use_fcntl) lock_fcntl_timeout = 0;
241 if (!use_flock) lock_flock_timeout = 0;
242
243 /* A file name is required */
244
245 if (i >= argc) usage();
246
247 filename = argv[i++];
248
249 /* Expand file names starting with ~ */
250
251 if (*filename == '~')
252   {
253   struct passwd *pw;
254
255   if (*(++filename) == '/')
256     pw = getpwuid(getuid());
257   else
258     {
259     char *s = buffer;
260     while (*filename != 0 && *filename != '/')
261       *s++ = *filename++;
262     *s = 0;
263     pw = getpwnam(buffer);
264     }
265
266   if (pw == NULL)
267     {
268     printf("exim_lock: unable to expand file name %s\n", argv[i-1]);
269     exit(1);
270     }
271
272   if ((int)strlen(pw->pw_dir) + (int)strlen(filename) + 1 > sizeof(buffer))
273     {
274     printf("exim_lock: expanded file name %s%s is too long", pw->pw_dir,
275       filename);
276     exit(1);
277     }
278
279   strcpy(buffer, pw->pw_dir);
280   strcat(buffer, filename);
281   filename = buffer;
282   }
283
284 /* If using a lock file, prepare by creating the lock file name and
285 the hitching post name. */
286
287 if (use_lockfile)
288   {
289   if (uname(&s) < 0)
290     {
291     printf("exim_lock: failed to find host name using uname()\n");
292     exit(1);
293     }
294   primary_hostname = s.nodename;
295
296   len = (int)strlen(filename);
297   lockname = malloc(len + 8);
298   sprintf(lockname, "%s.lock", filename);
299   hitchname = malloc(len + 32 + (int)strlen(primary_hostname));
300   sprintf(hitchname, "%s.%s.%08x.%08x", lockname, primary_hostname,
301     now, (int)getpid());
302
303   if (verbose)
304     printf("exim_lock: lockname =  %s\n           hitchname = %s\n", lockname,
305       hitchname);
306   }
307
308 /* Locking retry loop */
309
310 for (j = 0; j < lock_retries; j++)
311   {
312   int sleep_before_retry = TRUE;
313   struct stat statbuf, ostatbuf, lstatbuf, statbuf2;
314   int mbx_tmp_oflags;
315
316   /* Try to build a lock file if so configured */
317
318   if (use_lockfile)
319     {
320     int rc;
321     if (verbose) printf("exim_lock: creating lock file\n");
322     hd = open(hitchname, O_WRONLY | O_CREAT | O_EXCL, 0440);
323     if (hd < 0)
324       {
325       printf("exim_lock: failed to create hitching post %s: %s\n", hitchname,
326         strerror(errno));
327       exit(1);
328       }
329
330     /* Apply hitching post algorithm. */
331
332     if ((rc = link(hitchname, lockname)) != 0) fstat(hd, &statbuf);
333     (void)close(hd);
334     unlink(hitchname);
335
336     if (rc != 0 && statbuf.st_nlink != 2)
337       {
338       printf("exim_lock: failed to link hitching post to lock file\n");
339       hd = -1;
340       goto RETRY;
341       }
342
343     if (!quiet) printf("exim_lock: lock file successfully created\n");
344     }
345
346   /* We are done if no other locking required. */
347
348   if (!use_fcntl && !use_flock && !use_mbx) break;
349
350   /* Open the file for writing. */
351
352   fd = open(filename, O_RDWR + O_APPEND);
353   if (fd < 0)
354     {
355     printf("exim_lock: failed to open %s for writing: %s\n", filename,
356       strerror(errno));
357     yield = 1;
358     goto CLEAN_UP;
359     }
360
361   /* If there is a timeout, implying blocked locking, we don't want to
362   sleep before any retries after this. */
363
364   if (lock_fcntl_timeout > 0 || lock_flock_timeout > 0)
365     sleep_before_retry = FALSE;
366
367   /* Lock using fcntl. There are pros and cons to using a blocking call vs
368   a non-blocking call and retries. Exim is non-blocking by default, but setting
369   a timeout changes it to blocking. */
370
371   if (!use_mbx && (use_fcntl || use_flock))
372     {
373     if (apply_lock(fd, F_WRLCK, use_fcntl, lock_fcntl_timeout, use_flock,
374         lock_flock_timeout) >= 0)
375       {
376       if (!quiet)
377         {
378         if (use_fcntl) printf("exim_lock: fcntl() lock successfully applied\n");
379         if (use_flock) printf("exim_lock: flock() lock successfully applied\n");
380         }
381       break;
382       }
383     else goto RETRY;   /* Message already output */
384     }
385
386   /* Lock using MBX rules. This is complicated and is documented with the
387   source of the c-client library that goes with Pine and IMAP. What has to
388   be done to interwork correctly is to take out a shared lock on the mailbox,
389   and an exclusive lock on a /tmp file. */
390
391   else
392     {
393     if (apply_lock(fd, F_RDLCK, use_fcntl, lock_fcntl_timeout, use_flock,
394         lock_flock_timeout) >= 0)
395       {
396       if (!quiet)
397         {
398         if (use_fcntl)
399           printf("exim_lock: fcntl() read lock successfully applied\n");
400         if (use_flock)
401           printf("exim_lock: fcntl() read lock successfully applied\n");
402         }
403       }
404     else goto RETRY;   /* Message already output */
405
406     if (fstat(fd, &statbuf) < 0)
407       {
408       printf("exim_lock: fstat() of %s failed: %s\n", filename,
409         strerror(errno));
410       yield = 1;
411       goto CLEAN_UP;
412       }
413
414     /* Set up file in /tmp and check its state if already existing. */
415
416     sprintf(tempname, "/tmp/.%lx.%lx", (long)statbuf.st_dev,
417       (long)statbuf.st_ino);
418
419     if (lstat(tempname, &statbuf) >= 0)
420       {
421       if ((statbuf.st_mode & S_IFMT) == S_IFLNK)
422         {
423         printf("exim_lock: symbolic link on lock name %s\n", tempname);
424         yield = 1;
425         goto CLEAN_UP;
426         }
427       if (statbuf.st_nlink > 1)
428         {
429         printf("exim_lock: hard link to lock name %s\n", tempname);
430         yield = 1;
431         goto CLEAN_UP;
432         }
433       }
434
435     mbx_tmp_oflags = O_RDWR | O_CREAT;
436 #ifdef O_NOFOLLOW
437     mbx_tmp_oflags |= O_NOFOLLOW;
438 #endif
439     md = open(tempname, mbx_tmp_oflags, 0600);
440     if (md < 0)
441       {
442       printf("exim_lock: failed to create mbx lock file %s: %s\n",
443         tempname, strerror(errno));
444       goto CLEAN_UP;
445       }
446
447     /* security fixes from 2010-05 */
448     if (lstat(tempname, &lstatbuf) < 0)
449       {
450       printf("exim_lock: failed to lstat(%s) after opening it: %s\n",
451           tempname, strerror(errno));
452       goto CLEAN_UP;
453       }
454     if (fstat(md, &statbuf2) < 0)
455       {
456       printf("exim_lock: failed to fstat() open fd of \"%s\": %s\n",
457           tempname, strerror(errno));
458       goto CLEAN_UP;
459       }
460     if ((statbuf2.st_nlink > 1) ||
461         (lstatbuf.st_nlink > 1) ||
462         (!S_ISREG(lstatbuf.st_mode)) ||
463         (lstatbuf.st_dev != statbuf2.st_dev) ||
464         (lstatbuf.st_ino != statbuf2.st_ino))
465       {
466       printf("exim_lock: race condition exploited against us when "
467           "locking \"%s\"\n", tempname);
468       goto CLEAN_UP;
469       }
470
471     (void)chmod(tempname, 0600);
472
473     if (apply_lock(md, F_WRLCK, use_fcntl, lock_fcntl_timeout, use_flock,
474         lock_flock_timeout) >= 0)
475       {
476       if (!quiet)
477         {
478         if (use_fcntl)
479           printf("exim_lock: fcntl() lock successfully applied to mbx "
480             "lock file %s\n", tempname);
481         if (use_flock)
482           printf("exim_lock: flock() lock successfully applied to mbx "
483             "lock file %s\n", tempname);
484         }
485
486       /* This test checks for a race condition */
487
488       if (lstat(tempname, &statbuf) != 0 ||
489           fstat(md, &ostatbuf) != 0 ||
490           statbuf.st_dev != ostatbuf.st_dev ||
491           statbuf.st_ino != ostatbuf.st_ino)
492        {
493        if (!quiet) printf("exim_lock: mbx lock file %s changed between "
494            "creation and locking\n", tempname);
495        goto RETRY;
496        }
497       else break;
498       }
499     else goto RETRY;   /* Message already output */
500     }
501
502   /* Clean up before retrying */
503
504   RETRY:
505
506   if (md >= 0)
507     {
508     if (close(md) < 0)
509       printf("exim_lock: close %s failed: %s\n", tempname, strerror(errno));
510     else
511       if (!quiet) printf("exim_lock: %s closed\n", tempname);
512     md = -1;
513     }
514
515   if (fd >= 0)
516     {
517     if (close(fd) < 0)
518       printf("exim_lock: close failed: %s\n", strerror(errno));
519     else
520       if (!quiet) printf("exim_lock: file closed\n");
521     fd = -1;
522     }
523
524   if (hd >= 0)
525     {
526     if (unlink(lockname) < 0)
527       printf("exim_lock: unlink of %s failed: %s\n", lockname, strerror(errno));
528     else
529       if (!quiet) printf("exim_lock: lock file removed\n");
530     hd = -1;
531     }
532
533   /* If a blocking call timed out, break the retry loop if the total time
534   so far is not less than than retries * interval. */
535
536   if (sigalrm_seen &&
537       (j + 1) * ((lock_fcntl_timeout > lock_flock_timeout)?
538         lock_fcntl_timeout : lock_flock_timeout) >=
539           lock_retries * lock_interval)
540     j = lock_retries;
541
542   /* Wait a bit before retrying, except when it was a blocked fcntl() that
543   caused the problem. */
544
545   if (j < lock_retries && sleep_before_retry)
546     {
547     printf(" ... waiting\n");
548     sleep(lock_interval);
549     }
550   }
551
552 if (j >= lock_retries)
553   {
554   printf("exim_lock: locking failed too many times\n");
555   yield = 1;
556   goto CLEAN_UP;
557   }
558
559 if (!quiet) printf("exim_lock: locking %s succeeded: ", filename);
560
561 /* If there are no further arguments, run the user's shell; otherwise
562 the next argument is a command to run. */
563
564 if (i >= argc)
565   {
566   command = getenv("SHELL");
567   if (command == NULL || *command == 0) command = "/bin/sh";
568   if (!quiet) printf("running %s ...\n", command);
569   }
570 else
571   {
572   command = argv[i];
573   if (!quiet) printf("running the command ...\n");
574   }
575
576 /* Run the command, saving and restoring the times if required. */
577
578 if (restore_times)
579   {
580   struct stat strestore;
581   struct utimbuf ut;
582   stat(filename, &strestore);
583   (void)system(command);
584   ut.actime = strestore.st_atime;
585   ut.modtime = strestore.st_mtime;
586   utime(filename, &ut);
587   }
588 else (void)system(command);
589
590 /* Remove the locks and exit. Unlink the /tmp file if we can get an exclusive
591 lock on the mailbox. This should be a non-blocking lock call, as there is no
592 point in waiting. */
593
594 CLEAN_UP:
595
596 if (md >= 0)
597   {
598   if (apply_lock(fd, F_WRLCK, use_fcntl, 0, use_flock, 0) >= 0)
599     {
600     if (!quiet) printf("exim_lock: %s unlinked - no sharers\n", tempname);
601     unlink(tempname);
602     }
603   else if (!quiet)
604     printf("exim_lock: %s not unlinked - unable to get exclusive mailbox lock\n",
605       tempname);
606   if (close(md) < 0)
607     printf("exim_lock: close %s failed: %s\n", tempname, strerror(errno));
608   else
609     if (!quiet) printf("exim_lock: %s closed\n", tempname);
610   }
611
612 if (fd >= 0)
613   {
614   if (close(fd) < 0)
615     printf("exim_lock: close %s failed: %s\n", filename, strerror(errno));
616   else
617     if (!quiet) printf("exim_lock: %s closed\n", filename);
618   }
619
620 if (hd >= 0)
621   {
622   if (unlink(lockname) < 0)
623     printf("exim_lock: unlink %s failed: %s\n", lockname, strerror(errno));
624   else
625     if (!quiet) printf("exim_lock: lock file removed\n");
626   }
627
628 return yield;
629 }
630
631 /* End */