mk-conf.sh: note MD5, also in $features
[s-mailx.git] / dotlock.h
blobaddb233b411265c90018c0a8993285aba9d50cc2
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Creation of an exclusive "dotlock" file. This is (potentially) shared
3 *@ in between dot_lock() and the privilege-separated "dotlocker"..
4 *@ (Which is why it doesn't use NYD or other utilities.)
5 *@ The code assumes it has been chdir(2)d into the target directory and
6 *@ that SIGPIPE is ignored (we react upon EPIPE).
8 * Copyright (c) 2012 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
9 */
11 * Copyright (c) 1996 Christos Zoulas. All rights reserved.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 /* Callee has to ensure strlen(di_lock_name)+1+1 fits in NAME_MAX! */
35 static enum dotlock_state _dotlock_create(struct dotlock_info *dip);
37 /* Create a unique file. O_EXCL does not really work over NFS so we follow
38 * the following trick (inspired by S.R. van den Berg):
39 * - make a mostly unique filename and try to create it
40 * - link the unique filename to our target
41 * - get the link count of the target
42 * - unlink the mostly unique filename
43 * - if the link count was 2, then we are ok; else we've failed */
44 static enum dotlock_state __dotlock_create_excl(struct dotlock_info *dip,
45 char const *lname);
47 static enum dotlock_state
48 _dotlock_create(struct dotlock_info *dip)
50 char lname[NAME_MAX];
51 sigset_t nset, oset;
52 size_t tries;
53 ssize_t w;
54 enum dotlock_state rv, xrv;
56 /* (Callee ensured this doesn't end up as plain "di_lock_name" */
57 snprintf(lname, sizeof lname, "%s.%s.%s",
58 dip->di_lock_name, dip->di_hostname, dip->di_randstr);
60 sigemptyset(&nset);
61 sigaddset(&nset, SIGHUP);
62 sigaddset(&nset, SIGINT);
63 sigaddset(&nset, SIGQUIT);
64 sigaddset(&nset, SIGTERM);
65 sigaddset(&nset, SIGTTIN);
66 sigaddset(&nset, SIGTTOU);
67 sigaddset(&nset, SIGTSTP);
69 for (tries = 0;; ++tries) {
70 sigprocmask(SIG_BLOCK, &nset, &oset);
71 rv = __dotlock_create_excl(dip, lname);
72 sigprocmask(SIG_SETMASK, &oset, NULL);
74 if (rv == DLS_NONE || (rv & DLS_ABANDON))
75 break;
76 if (dip->di_pollmsecs == 0 || tries >= DOTLOCK_TRIES) {
77 rv |= DLS_ABANDON;
78 break;
81 xrv = DLS_PING;
82 w = write(STDOUT_FILENO, &xrv, sizeof xrv);
83 if (w == -1 && errno == EPIPE) {
84 rv = DLS_DUNNO | DLS_ABANDON;
85 break;
87 sleep(1); /* TODO pollmsecs -> use finer grain */
89 return rv;
92 static enum dotlock_state
93 __dotlock_create_excl(struct dotlock_info *dip, char const *lname)
95 struct stat stb;
96 int fd;
97 size_t tries;
98 enum dotlock_state rv = DLS_NONE;
100 /* We try to create the unique filename */
101 for (tries = 0;; ++tries) {
102 fd = open(lname,
103 #ifdef O_SYNC
104 (O_WRONLY | O_CREAT | O_TRUNC | O_EXCL | O_SYNC),
105 #else
106 (O_WRONLY | O_CREAT | O_TRUNC | O_EXCL),
107 #endif
108 S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
109 if (fd != -1) {
110 #ifdef n_PRIVSEP_SOURCE
111 if (dip->di_stb != NULL &&
112 fchown(fd, dip->di_stb->st_uid, dip->di_stb->st_gid)) {
113 int x = errno;
114 close(fd);
115 errno = x;
116 goto jbados;
118 #endif
119 close(fd);
120 break;
121 } else if (errno != EEXIST) {
122 rv = (errno == EROFS) ? DLS_ROFS | DLS_ABANDON : DLS_NOPERM;
123 goto jleave;
124 } else if (tries >= DOTLOCK_TRIES) {
125 rv = DLS_EXIST;
126 goto jleave;
130 /* We link the name to the fname */
131 if (link(lname, dip->di_lock_name) == -1)
132 goto jbados;
134 /* Note that we stat our own exclusively created name, not the
135 * destination, since the destination can be affected by others */
136 if (stat(lname, &stb) == -1)
137 goto jbados;
139 unlink(lname);
141 /* If the number of links was two (one for the unique file and one for
142 * the lock), we've won the race */
143 if (stb.st_nlink != 2)
144 rv = DLS_EXIST;
145 jleave:
146 return rv;
147 jbados:
148 rv = (errno == EEXIST) ? DLS_EXIST : DLS_NOPERM | DLS_ABANDON;
149 unlink(lname);
150 goto jleave;
153 /* s-it-mode */