-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathlockfile_create.3
229 lines (209 loc) · 7.26 KB
/
lockfile_create.3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
.TH LOCKFILE_CREATE 3 "27 Januari 2021" "Linux Manpage" "Linux Programmer's Manual"
.SH NAME
lockfile_create, lockfile_remove, lockfile_touch, lockfile_check \- manage lockfiles
.SH SYNOPSIS
.B #include <lockfile.h>
.sp
.BI "cc [ "flag " ... ] "file " ... -llockfile [ "library " ] "
.sp
.BI "int lockfile_create( const char *" lockfile ", int " retrycnt ", int " flags " [, struct lockargs " args " ] );"
.br
.BI "int lockfile_remove( const char *" lockfile " );"
.br
.BI "int lockfile_touch( const char *" lockfile " );"
.br
.BI "int lockfile_check( const char *" lockfile ", int " flags " );"
.br
.SH DESCRIPTION
Functions to handle lockfiles in an NFS safe way.
.PP
.SS lockfile_create
The
.B lockfile_create
function creates a lockfile in an NFS safe way.
.PP
If flags is set to
.B L_PID
or
.B L_PPID
then lockfile_create will not only check for an existing lockfile, but
it will read the contents as well to see if it contains a process id
in ASCII. If so, the lockfile is only valid if that process still exists.
Otherwise, a lockfile older than 5 minutes is considered to be stale.
.PP
When creating a lockfile, if
.B L_PID
is set in flags, then the current process' PID will be written to the
lockfile. Sometimes it can be useful to use the parent's PID instead
(for example, the
.B dotlockfile
command uses that). In such cases you can use the
.B L_PPID
flag instead.
.PP
.SS lockfile_touch
If the lockfile is on a shared filesystem, it might have been created by
a process on a remote host. So the L_PID or L_PPID method of deciding
if a lockfile is still valid or stale is incorrect and must not be used.
If you are holding a lock longer than 5 minutes, a call to
.B lockfile_create
by another process will consider the lock stale and remove it.
To prevent this, call
.B lockfile_touch
to refresh the lockfile at a regular interval (every minute or so).
.PP
.SS lockfile_check
.PP
This function checks if a valid lockfile is already present without trying to
create a new lockfile.
.PP
.SS lockfile_remove
.PP
Removes the lockfile.
.SH RETURN VALUES
.B lockfile_create
returns one of the following status codes:
.nf
#define L_SUCCESS 0 /* Lockfile created */
#define L_TMPLOCK 2 /* Error creating tmp lockfile */
#define L_TMPWRITE 3 /* Can't write pid int tmp lockfile */
#define L_MAXTRYS 4 /* Failed after max. number of attempts */
#define L_ERROR 5 /* Unknown error; check errno */
#define L_ORPHANED 7 /* Called with L_PPID but parent is gone */
#define L_RMSTALE 8 /* Failed to remove stale lockfile */
.fi
.PP
.B lockfile_check
returns 0 if a valid lockfile is present. If no lockfile or no valid
lockfile is present, -1 is returned.
.PP
.B lockfile_touch
and
.B lockfile_remove
return 0 on success. On failure -1 is returned and
.I errno
is set appropriately. It is not an error to lockfile_remove()
a non-existing lockfile.
.SH ALGORITHM
The algorithm that is used to create a lockfile in an atomic way,
even over NFS, is as follows:
.IP 1
A unique file is created. In printf format, the name of the file
is .lk%05d%x%s. The first argument (%05d) is the current process id. The
second argument (%x) consists of the 4 minor bits of the value returned by
\fItime\fP(2). The last argument is the system hostname.
.IP 2
Then the lockfile is created using \fIlink\fP(2). The return value of
\fIlink\fP is ignored.
.IP 3
Now the lockfile is \fIstat\fP()ed. If the stat fails, we go to step \fI6\fP.
.IP 4
The \fIstat\fP value of the lockfile is compared with that of the temporary
file. If they are the same, we have the lock. The temporary file
is deleted and a value of 0 (success) is returned to the caller.
.IP 5
A check is made to see if the existing lockfile is a valid one. If it isn't
valid, the stale lockfile is deleted.
.IP 6
Before retrying, we sleep for \fIn\fP seconds. \fIn\fP is initially 5
seconds, but after every retry 5 extra seconds is added up to a maximum
of 60 seconds (an incremental backoff). Then we go to
step \fI2\fP up to \fIretries\fP times.
.br
.PP
.SH REMOTE FILE SYSTEMS AND THE KERNEL ATTRIBUTE CACHE
.PP
These functions do not lock a file - they \fIgenerate\fP a \fIlockfile\fP.
However in a lot of cases, such as Unix mailboxes, all concerned programs
accessing the mailboxes agree on the fact that the presence of
<filename>.lock means that <filename> is locked.
.PP
If you are using
.B lockfile_create
to create a lock on a file that resides on a remote server, and you
already have that file open, you need to flush the NFS attribute cache
after locking. This is needed to prevent the following scenario:
.PP
.PD 0
.TP 3
o
open /var/mail/USERNAME
.TP 3
o
attributes, such as size, inode, etc are now cached in the kernel!
.TP 3
o
meanwhile, another remote system appends data to /var/mail/USERNAME
.TP 3
o
grab lock using lockfile_create()
.TP 3
o
seek to end of file
.TP 3
o
write data
.PD 1
.PP
Now the end of the file really isn't the end of the file - the kernel
cached the attributes on open, and st_size is not the end of the file
anymore. So after locking the file, you need to tell the kernel to
flush the NFS file attribute cache.
.PP
The only
.I portable
way to do this is
the POSIX
.I fcntl()
file locking primitives - locking a file using
.I fcntl()
has the fortunate side-effect of invalidating the NFS file attribute
cache of the kernel.
.PP
.B lockfile_create()
cannot do this for you for two reasons. One, it just creates a lockfile-
it doesn't know which file you are actually trying to lock! Two, even
if it could deduce the file you're locking from the filename, by just
opening and closing it, it would invalidate any existing POSIX locks the
program might already have on that file (yes, POSIX locking semantics
are insane!).
.PP
So basically what you need to do is something like this:
.nf
fd = open("/var/mail/USER");
.. program code ..
lockfile_create("/var/mail/USER.lock", x, y);
/* Invalidate NFS attribute cache using POSIX locks */
if (lockf(fd, F_TLOCK, 0) == 0) lockf(fd, F_ULOCK, 0);
.fi
You have to be careful with this if you're putting this in an existing
program that might already be using fcntl(), flock() or lockf() locking-
you might invalidate existing locks.
.PP
There is also a non-portable way. A lot of NFS operations return the
updated attributes - and the Linux kernel actually uses these to
update the attribute cache. One of these operations is
.B chmod(2).
.PP
So stat()ing a file and then chmod()ing it to st.st_mode will not
actually change the file, nor will it interfere with any locks on
the file, but it will invalidate the attribute cache. The equivalent
to use from a shell script would be
.nf
chmod u=u /var/mail/USER
.fi
.SH PERMISSIONS
If you are on a system that has a mail spool directory that is only
writable by a special group (usually "mail") you cannot create a lockfile
directly in the mailspool directory without special permissions.
.PP
Lockfile_create and lockfile_remove check if the lockfile ends in
$USERNAME.lock, and if the directory the lockfile is writable
by group "mail". If so, an external set group-id mail executable
(\fIdotlockfile\fP(1) ) is spawned to do the actual locking / unlocking.
.SH FILES
/usr/lib/liblockfile.so.1
.SH AUTHOR
Miquel van Smoorenburg
.SH "SEE ALSO"
.BR dotlockfile "(1), " maillock "(3), " touchlock " (3), " mailunlock (3)