2 * \file lock.c cross-platform concurrency locking for fetchmail
4 * For license terms, see the file COPYING in this directory.
9 #include <string.h> /* strcat() */
16 #include "fetchmail.h"
20 static char *lockfile; /** name of lockfile */
21 static int lock_acquired; /** flag if have we acquired a lock */
23 void fm_lock_setup(struct runctl *ctl)
24 /* set up the global lockfile name */
26 /* set up to do lock protocol */
27 const char *const FETCHMAIL_PIDFILE="fetchmail.pid";
29 /* command-line option override */
31 lockfile = xstrdup(ctl->pidfile);
36 if (getuid() == ROOT_UID) {
37 lockfile = (char *)xmalloc(strlen(PID_DIR)
38 + strlen(FETCHMAIL_PIDFILE) + 2); /* 2: "/" and NUL */
39 strcpy(lockfile, PID_DIR);
40 strcat(lockfile, "/");
41 strcat(lockfile, FETCHMAIL_PIDFILE);
43 lockfile = (char *)xmalloc(strlen(fmhome)
44 + strlen(FETCHMAIL_PIDFILE) + 3); /* 3: "/", "." and NUL */
45 strcpy(lockfile, fmhome);
46 strcat(lockfile, "/");
48 strcat(lockfile, ".");
49 strcat(lockfile, FETCHMAIL_PIDFILE);
53 static void unlockit(void)
54 /* must-do actions for exit (but we can't count on being able to do malloc) */
56 if (lockfile && lock_acquired)
60 void fm_lock_dispose(void)
61 /* arrange for a lock to be removed on process exit */
68 int fm_lock_state(void)
75 if ((lockfp = fopen(lockfile, "r")) != NULL)
77 int args = fscanf(lockfp, "%ld %d", &pid, &st);
81 fprintf(stderr, GT_("fetchmail: error reading lockfile \"%s\": %s\n"),
82 lockfile, strerror(errno));
83 fclose(lockfp); /* not checking should be safe, file mode was "r" */
86 fclose(lockfp); /* not checking should be safe, file mode was "r" */
88 if (args == EOF || args == 0 || kill(pid, 0) == -1) {
89 /* ^ could not read PID || process does not exist */
90 /* => lockfile is stale, unlink it */
92 fprintf(stderr,GT_("fetchmail: removing stale lockfile\n"));
93 if (unlink(lockfile)) {
94 if (errno != ENOENT) {
96 /* we complain but we don't exit; it might be
97 * writable for us, but in a directory we cannot
98 * write to. This means we can write the new PID to
99 * the file. Truncate to be safe in case the PID is
100 * recycled by another process later.
101 * \bug we should use fcntl() style locks or
102 * something else instead in a future release. */
103 if (truncate(lockfile, (off_t)0)) {
104 /* but if we cannot truncate the file either,
105 * assume that we cannot write to it later,
106 * complain and quit. */
115 if (errno != ENOENT) {
116 fprintf(stderr, GT_("fetchmail: error opening lockfile \"%s\": %s\n"),
117 lockfile, strerror(errno));
122 return(bkgd ? -pid : pid);
125 void fm_lock_assert(void)
126 /* assert that we already possess a lock */
128 lock_acquired = TRUE;
131 void fm_lock_or_die(void)
132 /* get a lock on a given host or exit */
137 if (!lock_acquired) {
140 if ((fd = open(lockfile, O_WRONLY|O_CREAT|O_EXCL, 0666)) != -1) {
143 snprintf(tmpbuf, sizeof(tmpbuf), "%ld\n", (long)getpid());
144 wr = write(fd, tmpbuf, strlen(tmpbuf));
145 if (wr == -1 || (size_t)wr != strlen(tmpbuf))
147 if (run.poll_interval)
149 snprintf(tmpbuf, sizeof(tmpbuf), "%d\n", run.poll_interval);
150 wr = write(fd, tmpbuf, strlen(tmpbuf));
151 if (wr == -1 || (size_t)wr != strlen(tmpbuf))
154 if (fsync(fd)) e = 1;
155 if (close(fd)) e = 1;
160 lock_acquired = TRUE;
163 fprintf(stderr, GT_("fetchmail: lock creation failed.\n"));
169 void fm_lock_release(void)
170 /* release a lock on a given host */
174 /* lock.c ends here */