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