NEWS: update for s-nail-14_5_2-sort.patch
[s-mailx.git] / dotlock.c
blob094a4a82d3680af949810021e5a84ca409c01624
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 - 2014 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 #ifndef HAVE_AMALGAMATION
37 # include "nail.h"
38 #endif
40 #include <sys/utsname.h>
42 #include <fcntl.h>
44 #ifndef O_SYNC
45 # define O_SYNC 0
46 #endif
48 static int maildir_access(const char *fname);
49 static int perhaps_setgid(const char *name, gid_t gid);
50 static int create_exclusive(const char *fname);
52 /* Check if we can write a lock file at all */
53 static int
54 maildir_access(const char *fname)
56 char *path;
57 char *p;
58 int i;
60 i = (int)strlen(fname);
61 path = ac_alloc(i + 2);
62 memcpy(path, fname, i + 1);
63 p = strrchr(path, '/');
64 if (p != NULL)
65 *p = '\0';
66 if (p == NULL || *path == '\0') {
67 path[0] = '.';
68 path[1] = '\0';
70 i = access(path, R_OK|W_OK|X_OK);
71 ac_free(path);
72 return i;
76 * Set the gid if the path is in the normal mail spool
78 static int
79 perhaps_setgid(const char *name, gid_t gid)
81 char safepath[]= MAILSPOOL;
83 if (strncmp(name, safepath, sizeof (safepath)-1) ||
84 strchr(name + sizeof (safepath), '/'))
85 return 0;
86 return (setgid (gid));
90 #define APID_SZ 40 /* sufficient for storign 128 bits pids */
92 * Create a unique file. O_EXCL does not really work over NFS so we follow
93 * the following trick: [Inspired by S.R. van den Berg]
95 * - make a mostly unique filename and try to create it.
96 * - link the unique filename to our target
97 * - get the link count of the target
98 * - unlink the mostly unique filename
99 * - if the link count was 2, then we are ok; else we've failed.
101 static int
102 create_exclusive(const char *fname)
104 char path[MAXPATHLEN];
105 char *hostname;
106 char apid[APID_SZ];
107 const char *ptr;
108 time_t t;
109 pid_t pid;
110 size_t ntries, cookie;
111 int fd, serrno, cc;
112 struct stat st;
113 struct utsname ut;
115 time(&t);
116 uname(&ut);
117 hostname = ut.nodename;
118 pid = getpid();
120 cookie = (int)pid ^ (int)t;
123 * We generate a semi-unique filename, from hostname.(pid ^ usec)
125 if ((ptr = strrchr(fname, '/')) == NULL)
126 ptr = fname;
127 else
128 ptr++;
130 snprintf(path, sizeof path, "%.*s.%s.%x",
131 (int) (ptr - fname), fname, hostname, (unsigned int) cookie);
134 * We try to create the unique filename.
136 for (ntries = 0; ntries < 5; ntries++) {
137 perhaps_setgid(path, effectivegid);
138 fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL|O_SYNC, 0);
139 (void)setgid(realgid); /* XXX */
140 if (fd != -1) {
141 snprintf(apid, APID_SZ, "%d", (int)getpid());
142 write(fd, apid, strlen(apid));
143 close(fd);
144 break;
146 else if (errno == EEXIST)
147 continue;
148 else
149 return -1;
152 * We link the path to the name
154 perhaps_setgid(fname, effectivegid);
155 cc = link(path, fname);
156 (void)setgid(realgid); /* XXX */
158 if (cc == -1)
159 goto bad;
162 * Note that we stat our own exclusively created name, not the
163 * destination, since the destination can be affected by others.
165 if (stat(path, &st) == -1)
166 goto bad;
168 perhaps_setgid(fname, effectivegid);
169 unlink(path);
170 (void)setgid(realgid); /* XXX */
173 * If the number of links was two (one for the unique file and one
174 * for the lock), we've won the race
176 if (st.st_nlink != 2) {
177 errno = EEXIST;
178 return -1;
180 return 0;
182 bad:
183 serrno = errno;
184 unlink(path);
185 errno = serrno;
186 return -1;
189 FL int
190 fcntl_lock(int fd, int ltype) /* TODO check callees for EINTR etc.!!! */
192 struct flock flp;
194 flp.l_type = ltype;
195 flp.l_start = 0;
196 flp.l_whence = SEEK_SET;
197 flp.l_len = 0;
198 return fcntl(fd, F_SETLKW, &flp);
201 FL int
202 dot_lock(const char *fname, int fd, int pollinterval, FILE *fp, const char *msg)
203 #ifdef notdef
204 const char *fname; /* Pathname to lock */
205 int fd; /* File descriptor for fname, for fcntl lock */
206 int pollinterval; /* Interval to check for lock, -1 return */
207 FILE *fp; /* File to print message */
208 const char *msg; /* Message to print */
209 #endif
211 char path[MAXPATHLEN];
212 sigset_t nset, oset;
213 int i, olderrno;
215 if (maildir_access(fname) != 0)
216 return 0;
218 sigemptyset(&nset);
219 sigaddset(&nset, SIGHUP);
220 sigaddset(&nset, SIGINT);
221 sigaddset(&nset, SIGQUIT);
222 sigaddset(&nset, SIGTERM);
223 sigaddset(&nset, SIGTTIN);
224 sigaddset(&nset, SIGTTOU);
225 sigaddset(&nset, SIGTSTP);
226 sigaddset(&nset, SIGCHLD);
228 snprintf(path, sizeof(path), "%s.lock", fname);
230 for (i=0;i<15;i++) {
231 sigprocmask(SIG_BLOCK, &nset, &oset);
232 if (create_exclusive(path) != -1) {
233 sigprocmask(SIG_SETMASK, &oset, NULL);
234 return 0;
236 else {
237 olderrno = errno;
238 sigprocmask(SIG_SETMASK, &oset, NULL);
241 fcntl_lock(fd, F_UNLCK);
242 if (olderrno != EEXIST)
243 return -1;
245 if (fp && msg)
246 fputs(msg, fp);
248 if (pollinterval) {
249 if (pollinterval == -1) {
250 errno = EEXIST;
251 return -1;
253 sleep(pollinterval);
255 (void)fcntl_lock(fd, F_WRLCK);
257 fprintf(stderr, tr(71,
258 "%s seems a stale lock? Need to be removed by hand?\n"), path);
259 return -1;
262 FL void
263 dot_unlock(const char *fname)
265 char path[MAXPATHLEN];
267 if (maildir_access(fname) != 0)
268 return;
270 snprintf(path, sizeof(path), "%s.lock", fname);
271 perhaps_setgid(path, effectivegid);
272 unlink(path);
273 (void)setgid(realgid); /* XXX */