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>.
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
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
40 #include <sys/utsname.h>
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 */
54 maildir_access(const char *fname
)
60 i
= (int)strlen(fname
);
61 path
= ac_alloc(i
+ 2);
62 memcpy(path
, fname
, i
+ 1);
63 p
= strrchr(path
, '/');
66 if (p
== NULL
|| *path
== '\0') {
70 i
= access(path
, R_OK
|W_OK
|X_OK
);
76 * Set the gid if the path is in the normal mail spool
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
), '/'))
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.
102 create_exclusive(const char *fname
)
104 char path
[MAXPATHLEN
];
110 size_t ntries
, cookie
;
117 hostname
= ut
.nodename
;
120 cookie
= (int)pid
^ (int)t
;
123 * We generate a semi-unique filename, from hostname.(pid ^ usec)
125 if ((ptr
= strrchr(fname
, '/')) == NULL
)
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 */
141 snprintf(apid
, APID_SZ
, "%d", (int)getpid());
142 write(fd
, apid
, strlen(apid
));
146 else if (errno
== EEXIST
)
152 * We link the path to the name
154 perhaps_setgid(fname
, effectivegid
);
155 cc
= link(path
, fname
);
156 (void)setgid(realgid
); /* XXX */
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)
168 perhaps_setgid(fname
, effectivegid
);
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) {
190 fcntl_lock(int fd
, int ltype
) /* TODO check callees for EINTR etc.!!! */
196 flp
.l_whence
= SEEK_SET
;
198 return fcntl(fd
, F_SETLKW
, &flp
);
202 dot_lock(const char *fname
, int fd
, int pollinterval
, FILE *fp
, const char *msg
)
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 */
211 char path
[MAXPATHLEN
];
215 if (maildir_access(fname
) != 0)
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
);
231 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
232 if (create_exclusive(path
) != -1) {
233 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
238 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
241 fcntl_lock(fd
, F_UNLCK
);
242 if (olderrno
!= EEXIST
)
249 if (pollinterval
== -1) {
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
);
263 dot_unlock(const char *fname
)
265 char path
[MAXPATHLEN
];
267 if (maildir_access(fname
) != 0)
270 snprintf(path
, sizeof(path
), "%s.lock", fname
);
271 perhaps_setgid(path
, effectivegid
);
273 (void)setgid(realgid
); /* XXX */