lock.c 4.33 KB
Newer Older
1 2
/**
 * \file lock.c cross-platform concurrency locking for fetchmail
Eric S. Raymond's avatar
Eric S. Raymond committed
3 4 5 6 7 8
 *
 * For license terms, see the file COPYING in this directory.
 */
#include "config.h"

#include <stdio.h>
9 10 11
#ifdef HAVE_STRING_H
#include <string.h> /* strcat() */
#endif
Eric S. Raymond's avatar
Eric S. Raymond committed
12 13 14 15 16 17
#if defined(STDC_HEADERS)
#include <stdlib.h>
#endif
#if defined(HAVE_UNISTD_H)
#include <unistd.h>
#endif
18
#include <errno.h>
Eric S. Raymond's avatar
Eric S. Raymond committed
19
#include <fcntl.h>
Eric S. Raymond's avatar
Eric S. Raymond committed
20
#include <signal.h>
Eric S. Raymond's avatar
Eric S. Raymond committed
21 22 23

#include "fetchmail.h"
#include "i18n.h"
24
#include "lock.h"
Eric S. Raymond's avatar
Eric S. Raymond committed
25

26 27
static char *lockfile;		/** name of lockfile */
static int lock_acquired;	/** flag if have we acquired a lock */
Eric S. Raymond's avatar
Eric S. Raymond committed
28

29
void fm_lock_setup(struct runctl *ctl)
Eric S. Raymond's avatar
Eric S. Raymond committed
30 31 32
/* set up the global lockfile name */
{
    /* set up to do lock protocol */
33 34 35 36 37 38 39 40 41
    const char *const FETCHMAIL_PIDFILE="fetchmail.pid";

    /* command-line option override */
    if (ctl->pidfile) {
	lockfile = xstrdup(ctl->pidfile);
	return;
    }

    /* defaults */
Eric S. Raymond's avatar
Eric S. Raymond committed
42
    if (getuid() == ROOT_UID) {
43 44
	lockfile = (char *)xmalloc(strlen(PID_DIR)
		+ strlen(FETCHMAIL_PIDFILE) + 2); /* 2: "/" and NUL */
45 46 47
	strcpy(lockfile, PID_DIR);
	strcat(lockfile, "/");
	strcat(lockfile, FETCHMAIL_PIDFILE);
Eric S. Raymond's avatar
Eric S. Raymond committed
48
    } else {
49 50
	lockfile = (char *)xmalloc(strlen(fmhome)
		+ strlen(FETCHMAIL_PIDFILE) + 3); /* 3: "/", "." and NUL */
Eric S. Raymond's avatar
Eric S. Raymond committed
51 52
	strcpy(lockfile, fmhome);
	strcat(lockfile, "/");
53 54
	if (fmhome == home)
	   strcat(lockfile, ".");
Eric S. Raymond's avatar
Eric S. Raymond committed
55 56 57 58 59 60 61 62 63 64 65
	strcat(lockfile, FETCHMAIL_PIDFILE);
    }
}

static void unlockit(void)
/* must-do actions for exit (but we can't count on being able to do malloc) */
{
    if (lockfile && lock_acquired)
	unlink(lockfile);
}

66
void fm_lock_dispose(void)
Eric S. Raymond's avatar
Eric S. Raymond committed
67 68 69 70 71 72 73
/* arrange for a lock to be removed on process exit */
{
#ifdef HAVE_ATEXIT
    atexit(unlockit);
#endif
}

74
int fm_lock_state(void)
Eric S. Raymond's avatar
Eric S. Raymond committed
75
{
76
    int		pid, st;
Eric S. Raymond's avatar
Eric S. Raymond committed
77 78 79
    FILE	*lockfp;
    int		bkgd = FALSE;

80
    if ((lockfp = fopen(lockfile, "r")) != NULL)
Eric S. Raymond's avatar
Eric S. Raymond committed
81
    {
82 83
	int args = fscanf(lockfp, "%d %d", &pid, &st);
	bkgd = (args == 2);
Eric S. Raymond's avatar
Eric S. Raymond committed
84

85
	if (ferror(lockfp)) {
86 87
	    fprintf(stderr, GT_("fetchmail: error reading lockfile \"%s\": %s\n"),
		    lockfile, strerror(errno));
88
	    fclose(lockfp); /* not checking should be safe, file mode was "r" */
89 90
	    exit(PS_EXCLUDE);
	}
91
	fclose(lockfp); /* not checking should be safe, file mode was "r" */
92

93
	if (args == EOF || args == 0 || kill(pid, 0) == -1) {
94 95
	    /* ^ could not read PID  || process does not exist */
	    /* => lockfile is stale, unlink it */
Eric S. Raymond's avatar
Eric S. Raymond committed
96
	    pid = 0;
97
	    fprintf(stderr,GT_("fetchmail: removing stale lockfile\n"));
98 99 100
	    if (unlink(lockfile)) {
	       if (errno != ENOENT) {
		   perror(lockfile);
101 102 103 104 105 106 107 108 109 110 111 112 113 114
		   /* we complain but we don't exit; it might be
		    * writable for us, but in a directory we cannot
		    * write to. This means we can write the new PID to
		    * the file. Truncate to be safe in case the PID is
		    * recycled by another process later.
		    * \bug we should use fcntl() style locks or
		    * something else instead in a future release. */
		   if (truncate(lockfile, (off_t)0)) {
		       /* but if we cannot truncate the file either,
			* assume that we cannot write to it later,
			* complain and quit. */
		       perror(lockfile);
		       exit(PS_EXCLUDE);
		   }
115 116
	       }
	    }
Eric S. Raymond's avatar
Eric S. Raymond committed
117
	}
118 119
    } else {
	pid = 0;
120
	if (errno != ENOENT) {
121 122
	    fprintf(stderr, GT_("fetchmail: error opening lockfile \"%s\": %s\n"),
		    lockfile, strerror(errno));
123 124
	    exit(PS_EXCLUDE);
	}
Eric S. Raymond's avatar
Eric S. Raymond committed
125 126 127 128 129
    }

    return(bkgd ? -pid : pid);
}

130
void fm_lock_assert(void)
131
/* assert that we already possess a lock */
Eric S. Raymond's avatar
Eric S. Raymond committed
132 133 134 135
{
    lock_acquired = TRUE;
}

136
void fm_lock_or_die(void)
Eric S. Raymond's avatar
Eric S. Raymond committed
137 138 139
/* get a lock on a given host or exit */
{
    int fd;
140
    char	tmpbuf[50];
Eric S. Raymond's avatar
Eric S. Raymond committed
141

142 143 144 145
    if (!lock_acquired) {
	int e = 0;

	if ((fd = open(lockfile, O_WRONLY|O_CREAT|O_EXCL, 0666)) != -1) {
146 147
	    ssize_t wr;

148
	    snprintf(tmpbuf, sizeof(tmpbuf), "%ld\n", (long)getpid());
149 150 151
	    wr = write(fd, tmpbuf, strlen(tmpbuf));
	    if (wr == -1 || (size_t)wr != strlen(tmpbuf))
	        e = 1;
152 153 154
	    if (run.poll_interval)
	    {
		snprintf(tmpbuf, sizeof(tmpbuf), "%d\n", run.poll_interval);
155 156 157
		wr = write(fd, tmpbuf, strlen(tmpbuf));
		if (wr == -1 || (size_t)wr != strlen(tmpbuf))
		    e = 1;
158 159 160
	    }
	    if (fsync(fd)) e = 1;
	    if (close(fd)) e = 1;
161 162
	} else {
	    e = 1;
163 164 165 166 167 168 169 170
	}
	if (e == 0) {
	    lock_acquired = TRUE;
	} else {
	    perror(lockfile);
	    fprintf(stderr, GT_("fetchmail: lock creation failed.\n"));
	    exit(PS_EXCLUDE);
	}
Eric S. Raymond's avatar
Eric S. Raymond committed
171 172 173
    }
}

174
void fm_lock_release(void)
Eric S. Raymond's avatar
Eric S. Raymond committed
175 176 177 178 179
/* release a lock on a given host */
{
    unlink(lockfile);
}
/* lock.c ends here */