list.c: fix compiler warnings
[s-mailx.git] / dotlock.c
blob70702ddfb9273ab4111b286c4c51734fc5d91998
1 /*
2 * Heirloom mailx - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 */
6 /*
7 * Copyright (c) 1996 Christos Zoulas. All rights reserved.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by Christos Zoulas.
20 * 4. The name of the author may not be used to endorse or promote products
21 * derived from this software without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 #ifndef lint
36 #ifdef DOSCCS
37 static char sccsid[] = "@(#)dotlock.c 2.9 (gritter) 3/20/06";
38 #endif
39 #endif
41 #include "rcv.h"
42 #include <sys/stat.h>
43 #include <unistd.h>
44 #include <fcntl.h>
45 #include <time.h>
46 #include <sys/utsname.h>
47 #include <errno.h>
49 #include "extern.h"
51 #ifndef O_SYNC
52 #define O_SYNC 0
53 #endif
55 static int maildir_access(const char *fname);
56 static int perhaps_setgid(const char *name, gid_t gid);
57 static int create_exclusive(const char *fname);
59 /* Check if we can write a lock file at all */
60 static int
61 maildir_access(const char *fname)
63 char *path;
64 char *p;
65 int i;
67 path = ac_alloc(strlen(fname) + 2);
68 strcpy(path, fname);
69 p = strrchr(path, '/');
70 if (p != NULL)
71 *p = '\0';
72 if (p == NULL || *path == '\0')
73 strcpy(path, ".");
74 i = access(path, R_OK|W_OK|X_OK);
75 ac_free(path);
76 return i;
80 * Set the gid if the path is in the normal mail spool
82 static int
83 perhaps_setgid(const char *name, gid_t gid)
85 char safepath[]= MAILSPOOL;
87 if (strncmp(name, safepath, sizeof (safepath)-1) ||
88 strchr(name + sizeof (safepath), '/'))
89 return 0;
90 return (setgid (gid));
94 #define APID_SZ 40 /* sufficient for storign 128 bits pids */
96 * Create a unique file. O_EXCL does not really work over NFS so we follow
97 * the following trick: [Inspired by S.R. van den Berg]
99 * - make a mostly unique filename and try to create it.
100 * - link the unique filename to our target
101 * - get the link count of the target
102 * - unlink the mostly unique filename
103 * - if the link count was 2, then we are ok; else we've failed.
105 static int
106 create_exclusive(const char *fname)
108 char path[MAXPATHLEN];
109 char *hostname;
110 char apid[APID_SZ];
111 const char *ptr;
112 time_t t;
113 pid_t pid;
114 size_t ntries, cookie;
115 int fd, serrno, cc;
116 struct stat st;
117 struct utsname ut;
119 time(&t);
120 uname(&ut);
121 hostname = ut.nodename;
122 pid = getpid();
124 cookie = (int)pid ^ (int)t;
127 * We generate a semi-unique filename, from hostname.(pid ^ usec)
129 if ((ptr = strrchr(fname, '/')) == NULL)
130 ptr = fname;
131 else
132 ptr++;
134 snprintf(path, sizeof path, "%.*s.%s.%x",
135 (int) (ptr - fname), fname, hostname, (unsigned int) cookie);
138 * We try to create the unique filename.
140 for (ntries = 0; ntries < 5; ntries++) {
141 perhaps_setgid(path, effectivegid);
142 fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL|O_SYNC, 0);
143 setgid(realgid);
144 if (fd != -1) {
145 snprintf(apid, APID_SZ, "%d", (int)getpid());
146 write(fd, apid, strlen(apid));
147 close(fd);
148 break;
150 else if (errno == EEXIST)
151 continue;
152 else
153 return -1;
156 * We link the path to the name
158 perhaps_setgid(fname, effectivegid);
159 cc = link(path, fname);
160 setgid(realgid);
162 if (cc == -1)
163 goto bad;
166 * Note that we stat our own exclusively created name, not the
167 * destination, since the destination can be affected by others.
169 if (stat(path, &st) == -1)
170 goto bad;
172 perhaps_setgid(fname, effectivegid);
173 unlink(path);
174 setgid(realgid);
177 * If the number of links was two (one for the unique file and one
178 * for the lock), we've won the race
180 if (st.st_nlink != 2) {
181 errno = EEXIST;
182 return -1;
184 return 0;
186 bad:
187 serrno = errno;
188 unlink(path);
189 errno = serrno;
190 return -1;
193 int
194 fcntl_lock(int fd, int type)
196 struct flock flp;
198 flp.l_type = type;
199 flp.l_start = 0;
200 flp.l_whence = SEEK_SET;
201 flp.l_len = 0;
202 return fcntl(fd, F_SETLKW, &flp);
206 dot_lock(const char *fname, int fd, int pollinterval, FILE *fp, const char *msg)
207 #ifdef notdef
208 const char *fname; /* Pathname to lock */
209 int fd; /* File descriptor for fname, for fcntl lock */
210 int pollinterval; /* Interval to check for lock, -1 return */
211 FILE *fp; /* File to print message */
212 const char *msg; /* Message to print */
213 #endif
215 char path[MAXPATHLEN];
216 sigset_t nset, oset;
217 int i, olderrno;
219 if (maildir_access(fname) != 0)
220 return 0;
222 sigemptyset(&nset);
223 sigaddset(&nset, SIGHUP);
224 sigaddset(&nset, SIGINT);
225 sigaddset(&nset, SIGQUIT);
226 sigaddset(&nset, SIGTERM);
227 sigaddset(&nset, SIGTTIN);
228 sigaddset(&nset, SIGTTOU);
229 sigaddset(&nset, SIGTSTP);
230 sigaddset(&nset, SIGCHLD);
232 snprintf(path, sizeof(path), "%s.lock", fname);
234 for (i=0;i<15;i++) {
235 sigprocmask(SIG_BLOCK, &nset, &oset);
236 if (create_exclusive(path) != -1) {
237 sigprocmask(SIG_SETMASK, &oset, NULL);
238 return 0;
240 else {
241 olderrno = errno;
242 sigprocmask(SIG_SETMASK, &oset, NULL);
245 fcntl_lock(fd, F_UNLCK);
246 if (olderrno != EEXIST)
247 return -1;
249 if (fp && msg)
250 fputs(msg, fp);
252 if (pollinterval) {
253 if (pollinterval == -1) {
254 errno = EEXIST;
255 return -1;
257 sleep(pollinterval);
259 fcntl_lock(fd, F_WRLCK);
261 fprintf(stderr, catgets(catd, CATSET, 71,
262 "%s seems a stale lock? Need to be removed by hand?\n"), path);
263 return -1;
266 void
267 dot_unlock(const char *fname)
269 char path[MAXPATHLEN];
271 if (maildir_access(fname) != 0)
272 return;
274 snprintf(path, sizeof(path), "%s.lock", fname);
275 perhaps_setgid(path, effectivegid);
276 unlink(path);
277 setgid(realgid);