*inbox*: if empty, only bypass *folder* to $MAIL or builtin default
[s-mailx.git] / dotlock.h
blob6546ce5f44742930d295dcb695c3e405282c9cef
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).
7 *@ It furtherly assumes that it can create a file name that is at least one
8 *@ byte longer than the dotlock file's name!
10 * Copyright (c) 2012 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
13 * Copyright (c) 1996 Christos Zoulas. All rights reserved.
15 * Redistribution and use in source and binary forms, with or without
16 * modification, are permitted provided that the following conditions
17 * are met:
18 * 1. Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
20 * 2. Redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in the
22 * documentation and/or other materials provided with the distribution.
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 /* Jump in */
37 static enum dotlock_state _dotlock_create(struct dotlock_info *dip);
39 /* Create a unique file. O_EXCL does not really work over NFS so we follow
40 * the following trick (inspired by S.R. van den Berg):
41 * - make a mostly unique filename and try to create it
42 * - link the unique filename to our target
43 * - get the link count of the target
44 * - unlink the mostly unique filename
45 * - if the link count was 2, then we are ok; else we've failed */
46 static enum dotlock_state __dotlock_create_excl(struct dotlock_info *dip,
47 char const *lname);
49 static enum dotlock_state
50 _dotlock_create(struct dotlock_info *dip)
52 /* Use PATH_MAX not NAME_MAX to catch those "we proclaim the minimum value"
53 * problems (SunOS), since the pathconf(3) value came too late! */
54 char lname[PATH_MAX +1];
55 sigset_t nset, oset;
56 size_t tries;
57 ssize_t w;
58 enum dotlock_state rv, xrv;
60 /* (Callee ensured this doesn't end up as plain "di_lock_name") */
61 snprintf(lname, sizeof lname, "%s.%s.%s",
62 dip->di_lock_name, dip->di_hostname, dip->di_randstr);
64 sigemptyset(&nset);
65 sigaddset(&nset, SIGHUP);
66 sigaddset(&nset, SIGINT);
67 sigaddset(&nset, SIGQUIT);
68 sigaddset(&nset, SIGTERM);
69 sigaddset(&nset, SIGTTIN);
70 sigaddset(&nset, SIGTTOU);
71 sigaddset(&nset, SIGTSTP);
73 for (tries = 0;; ++tries) {
74 sigprocmask(SIG_BLOCK, &nset, &oset);
75 rv = __dotlock_create_excl(dip, lname);
76 sigprocmask(SIG_SETMASK, &oset, NULL);
78 if (rv == DLS_NONE || (rv & DLS_ABANDON))
79 break;
80 if (dip->di_pollmsecs == 0 || tries >= DOTLOCK_TRIES) {
81 rv |= DLS_ABANDON;
82 break;
85 xrv = DLS_PING;
86 w = write(STDOUT_FILENO, &xrv, sizeof xrv);
87 if (w == -1 && errno == EPIPE) {
88 rv = DLS_DUNNO | DLS_ABANDON;
89 break;
91 sleep(1); /* TODO pollmsecs -> use finer grain */
93 return rv;
96 static enum dotlock_state
97 __dotlock_create_excl(struct dotlock_info *dip, char const *lname)
99 struct stat stb;
100 int fd;
101 size_t tries;
102 enum dotlock_state rv = DLS_NONE;
104 /* We try to create the unique filename */
105 for (tries = 0;; ++tries) {
106 fd = open(lname,
107 #ifdef O_SYNC
108 (O_WRONLY | O_CREAT | O_TRUNC | O_EXCL | O_SYNC),
109 #else
110 (O_WRONLY | O_CREAT | O_TRUNC | O_EXCL),
111 #endif
112 S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
113 if (fd != -1) {
114 #ifdef n_PRIVSEP_SOURCE
115 if (dip->di_stb != NULL &&
116 fchown(fd, dip->di_stb->st_uid, dip->di_stb->st_gid)) {
117 int x = errno;
118 close(fd);
119 errno = x;
120 goto jbados;
122 #endif
123 close(fd);
124 break;
125 } else if (errno != EEXIST) {
126 rv = (errno == EROFS) ? DLS_ROFS | DLS_ABANDON : DLS_NOPERM;
127 goto jleave;
128 } else if (tries >= DOTLOCK_TRIES) {
129 rv = DLS_EXIST;
130 goto jleave;
134 /* We link the name to the fname */
135 if (link(lname, dip->di_lock_name) == -1)
136 goto jbados;
138 /* Note that we stat our own exclusively created name, not the
139 * destination, since the destination can be affected by others */
140 if (stat(lname, &stb) == -1)
141 goto jbados;
143 unlink(lname);
145 /* If the number of links was two (one for the unique file and one for
146 * the lock), we've won the race */
147 if (stb.st_nlink != 2)
148 rv = DLS_EXIST;
149 jleave:
150 return rv;
151 jbados:
152 rv = (errno == EEXIST) ? DLS_EXIST : DLS_NOPERM | DLS_ABANDON;
153 unlink(lname);
154 goto jleave;
157 /* s-it-mode */