README: nits
[s-mailx.git] / dotlock.c
blobbeec5be335a64ead6b3787ae9e482841c49b3ff7
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Mailbox file locking.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2013 Steffen "Daode" Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
8 * Copyright (c) 1996 Christos Zoulas. All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by Christos Zoulas.
21 * 4. The name of the author may not be used to endorse or promote products
22 * derived from this software without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
33 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 #include "rcv.h"
38 #include <sys/stat.h>
39 #include <sys/utsname.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <time.h>
43 #include <unistd.h>
45 #include "extern.h"
47 #ifndef O_SYNC
48 # define O_SYNC 0
49 #endif
51 static int maildir_access(const char *fname);
52 static int perhaps_setgid(const char *name, gid_t gid);
53 static int create_exclusive(const char *fname);
55 /* Check if we can write a lock file at all */
56 static int
57 maildir_access(const char *fname)
59 char *path;
60 char *p;
61 int i;
63 i = (int)strlen(fname);
64 path = ac_alloc(i + 2);
65 memcpy(path, fname, i + 1);
66 p = strrchr(path, '/');
67 if (p != NULL)
68 *p = '\0';
69 if (p == NULL || *path == '\0') {
70 path[0] = '.';
71 path[1] = '\0';
73 i = access(path, R_OK|W_OK|X_OK);
74 ac_free(path);
75 return i;
79 * Set the gid if the path is in the normal mail spool
81 static int
82 perhaps_setgid(const char *name, gid_t gid)
84 char safepath[]= MAILSPOOL;
86 if (strncmp(name, safepath, sizeof (safepath)-1) ||
87 strchr(name + sizeof (safepath), '/'))
88 return 0;
89 return (setgid (gid));
93 #define APID_SZ 40 /* sufficient for storign 128 bits pids */
95 * Create a unique file. O_EXCL does not really work over NFS so we follow
96 * the following trick: [Inspired by S.R. van den Berg]
98 * - make a mostly unique filename and try to create it.
99 * - link the unique filename to our target
100 * - get the link count of the target
101 * - unlink the mostly unique filename
102 * - if the link count was 2, then we are ok; else we've failed.
104 static int
105 create_exclusive(const char *fname)
107 char path[MAXPATHLEN];
108 char *hostname;
109 char apid[APID_SZ];
110 const char *ptr;
111 time_t t;
112 pid_t pid;
113 size_t ntries, cookie;
114 int fd, serrno, cc;
115 struct stat st;
116 struct utsname ut;
118 time(&t);
119 uname(&ut);
120 hostname = ut.nodename;
121 pid = getpid();
123 cookie = (int)pid ^ (int)t;
126 * We generate a semi-unique filename, from hostname.(pid ^ usec)
128 if ((ptr = strrchr(fname, '/')) == NULL)
129 ptr = fname;
130 else
131 ptr++;
133 snprintf(path, sizeof path, "%.*s.%s.%x",
134 (int) (ptr - fname), fname, hostname, (unsigned int) cookie);
137 * We try to create the unique filename.
139 for (ntries = 0; ntries < 5; ntries++) {
140 perhaps_setgid(path, effectivegid);
141 fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL|O_SYNC, 0);
142 (void)setgid(realgid); /* XXX */
143 if (fd != -1) {
144 snprintf(apid, APID_SZ, "%d", (int)getpid());
145 write(fd, apid, strlen(apid));
146 close(fd);
147 break;
149 else if (errno == EEXIST)
150 continue;
151 else
152 return -1;
155 * We link the path to the name
157 perhaps_setgid(fname, effectivegid);
158 cc = link(path, fname);
159 (void)setgid(realgid); /* XXX */
161 if (cc == -1)
162 goto bad;
165 * Note that we stat our own exclusively created name, not the
166 * destination, since the destination can be affected by others.
168 if (stat(path, &st) == -1)
169 goto bad;
171 perhaps_setgid(fname, effectivegid);
172 unlink(path);
173 (void)setgid(realgid); /* XXX */
176 * If the number of links was two (one for the unique file and one
177 * for the lock), we've won the race
179 if (st.st_nlink != 2) {
180 errno = EEXIST;
181 return -1;
183 return 0;
185 bad:
186 serrno = errno;
187 unlink(path);
188 errno = serrno;
189 return -1;
192 int
193 fcntl_lock(int fd, int type) /* TODO check callees for EINTR etc.!!! */
195 struct flock flp;
197 flp.l_type = type;
198 flp.l_start = 0;
199 flp.l_whence = SEEK_SET;
200 flp.l_len = 0;
201 return fcntl(fd, F_SETLKW, &flp);
205 dot_lock(const char *fname, int fd, int pollinterval, FILE *fp, const char *msg)
206 #ifdef notdef
207 const char *fname; /* Pathname to lock */
208 int fd; /* File descriptor for fname, for fcntl lock */
209 int pollinterval; /* Interval to check for lock, -1 return */
210 FILE *fp; /* File to print message */
211 const char *msg; /* Message to print */
212 #endif
214 char path[MAXPATHLEN];
215 sigset_t nset, oset;
216 int i, olderrno;
218 if (maildir_access(fname) != 0)
219 return 0;
221 sigemptyset(&nset);
222 sigaddset(&nset, SIGHUP);
223 sigaddset(&nset, SIGINT);
224 sigaddset(&nset, SIGQUIT);
225 sigaddset(&nset, SIGTERM);
226 sigaddset(&nset, SIGTTIN);
227 sigaddset(&nset, SIGTTOU);
228 sigaddset(&nset, SIGTSTP);
229 sigaddset(&nset, SIGCHLD);
231 snprintf(path, sizeof(path), "%s.lock", fname);
233 for (i=0;i<15;i++) {
234 sigprocmask(SIG_BLOCK, &nset, &oset);
235 if (create_exclusive(path) != -1) {
236 sigprocmask(SIG_SETMASK, &oset, NULL);
237 return 0;
239 else {
240 olderrno = errno;
241 sigprocmask(SIG_SETMASK, &oset, NULL);
244 fcntl_lock(fd, F_UNLCK);
245 if (olderrno != EEXIST)
246 return -1;
248 if (fp && msg)
249 fputs(msg, fp);
251 if (pollinterval) {
252 if (pollinterval == -1) {
253 errno = EEXIST;
254 return -1;
256 sleep(pollinterval);
258 (void)fcntl_lock(fd, F_WRLCK);
260 fprintf(stderr, catgets(catd, CATSET, 71,
261 "%s seems a stale lock? Need to be removed by hand?\n"), path);
262 return -1;
265 void
266 dot_unlock(const char *fname)
268 char path[MAXPATHLEN];
270 if (maildir_access(fname) != 0)
271 return;
273 snprintf(path, sizeof(path), "%s.lock", fname);
274 perhaps_setgid(path, effectivegid);
275 unlink(path);
276 (void)setgid(realgid); /* XXX */