]> Pileus Git - ~andy/fetchmail/blob - lock.c
Note Earl's regression fix for SSL_CTX_clear_options() on older OpenSSL.
[~andy/fetchmail] / lock.c
1 /**
2  * \file lock.c cross-platform concurrency locking for fetchmail
3  *
4  * For license terms, see the file COPYING in this directory.
5  */
6 #include "config.h"
7
8 #include <stdio.h>
9 #ifdef HAVE_STRING_H
10 #include <string.h> /* strcat() */
11 #endif
12 #if defined(STDC_HEADERS)
13 #include <stdlib.h>
14 #endif
15 #if defined(HAVE_UNISTD_H)
16 #include <unistd.h>
17 #endif
18 #include <errno.h>
19 #include <fcntl.h>
20 #include <signal.h>
21
22 #include "fetchmail.h"
23 #include "i18n.h"
24 #include "lock.h"
25
26 static char *lockfile;          /** name of lockfile */
27 static int lock_acquired;       /** flag if have we acquired a lock */
28
29 void fm_lock_setup(struct runctl *ctl)
30 /* set up the global lockfile name */
31 {
32     /* set up to do lock protocol */
33     const char *const FETCHMAIL_PIDFILE="fetchmail.pid";
34
35     /* command-line option override */
36     if (ctl->pidfile) {
37         lockfile = xstrdup(ctl->pidfile);
38         return;
39     }
40
41     /* defaults */
42     if (getuid() == ROOT_UID) {
43         lockfile = (char *)xmalloc(strlen(PID_DIR)
44                 + strlen(FETCHMAIL_PIDFILE) + 2); /* 2: "/" and NUL */
45         strcpy(lockfile, PID_DIR);
46         strcat(lockfile, "/");
47         strcat(lockfile, FETCHMAIL_PIDFILE);
48     } else {
49         lockfile = (char *)xmalloc(strlen(fmhome)
50                 + strlen(FETCHMAIL_PIDFILE) + 3); /* 3: "/", "." and NUL */
51         strcpy(lockfile, fmhome);
52         strcat(lockfile, "/");
53         if (fmhome == home)
54            strcat(lockfile, ".");
55         strcat(lockfile, FETCHMAIL_PIDFILE);
56     }
57 }
58
59 static void unlockit(void)
60 /* must-do actions for exit (but we can't count on being able to do malloc) */
61 {
62     if (lockfile && lock_acquired)
63         unlink(lockfile);
64 }
65
66 void fm_lock_dispose(void)
67 /* arrange for a lock to be removed on process exit */
68 {
69 #ifdef HAVE_ATEXIT
70     atexit(unlockit);
71 #endif
72 }
73
74 int fm_lock_state(void)
75 {
76     long        pid;
77     int         st;
78     FILE        *lockfp;
79     int         bkgd = FALSE;
80
81     if ((lockfp = fopen(lockfile, "r")) != NULL)
82     {
83         int args = fscanf(lockfp, "%ld %d", &pid, &st);
84         bkgd = (args == 2);
85
86         if (ferror(lockfp)) {
87             fprintf(stderr, GT_("fetchmail: error reading lockfile \"%s\": %s\n"),
88                     lockfile, strerror(errno));
89             fclose(lockfp); /* not checking should be safe, file mode was "r" */
90             exit(PS_EXCLUDE);
91         }
92         fclose(lockfp); /* not checking should be safe, file mode was "r" */
93
94         if (args == EOF || args == 0 || kill(pid, 0) == -1) {
95             /* ^ could not read PID  || process does not exist */
96             /* => lockfile is stale, unlink it */
97             pid = 0;
98             fprintf(stderr,GT_("fetchmail: removing stale lockfile\n"));
99             if (unlink(lockfile)) {
100                if (errno != ENOENT) {
101                    perror(lockfile);
102                    /* we complain but we don't exit; it might be
103                     * writable for us, but in a directory we cannot
104                     * write to. This means we can write the new PID to
105                     * the file. Truncate to be safe in case the PID is
106                     * recycled by another process later.
107                     * \bug we should use fcntl() style locks or
108                     * something else instead in a future release. */
109                    if (truncate(lockfile, (off_t)0)) {
110                        /* but if we cannot truncate the file either,
111                         * assume that we cannot write to it later,
112                         * complain and quit. */
113                        perror(lockfile);
114                        exit(PS_EXCLUDE);
115                    }
116                }
117             }
118         }
119     } else {
120         pid = 0;
121         if (errno != ENOENT) {
122             fprintf(stderr, GT_("fetchmail: error opening lockfile \"%s\": %s\n"),
123                     lockfile, strerror(errno));
124             exit(PS_EXCLUDE);
125         }
126     }
127
128     return(bkgd ? -pid : pid);
129 }
130
131 void fm_lock_assert(void)
132 /* assert that we already possess a lock */
133 {
134     lock_acquired = TRUE;
135 }
136
137 void fm_lock_or_die(void)
138 /* get a lock on a given host or exit */
139 {
140     int fd;
141     char        tmpbuf[50];
142
143     if (!lock_acquired) {
144         int e = 0;
145
146         if ((fd = open(lockfile, O_WRONLY|O_CREAT|O_EXCL, 0666)) != -1) {
147             ssize_t wr;
148
149             snprintf(tmpbuf, sizeof(tmpbuf), "%ld\n", (long)getpid());
150             wr = write(fd, tmpbuf, strlen(tmpbuf));
151             if (wr == -1 || (size_t)wr != strlen(tmpbuf))
152                 e = 1;
153             if (run.poll_interval)
154             {
155                 snprintf(tmpbuf, sizeof(tmpbuf), "%d\n", run.poll_interval);
156                 wr = write(fd, tmpbuf, strlen(tmpbuf));
157                 if (wr == -1 || (size_t)wr != strlen(tmpbuf))
158                     e = 1;
159             }
160             if (fsync(fd)) e = 1;
161             if (close(fd)) e = 1;
162         } else {
163             e = 1;
164         }
165         if (e == 0) {
166             lock_acquired = TRUE;
167         } else {
168             perror(lockfile);
169             fprintf(stderr, GT_("fetchmail: lock creation failed.\n"));
170             exit(PS_EXCLUDE);
171         }
172     }
173 }
174
175 void fm_lock_release(void)
176 /* release a lock on a given host */
177 {
178     unlink(lockfile);
179 }
180 /* lock.c ends here */