-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsafelock.h
347 lines (324 loc) · 11.5 KB
/
safelock.h
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
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
#ifndef _SAFELOCK_H
#define _SAFELOCK_H 1
/** \file safelock.h */
/** \mainpage Safelock
*
* Safelock is a file-based locking primitive which can provide mutual
* exclusion between unrelated processes and threads.
*
* Safelock offers advantages over POSIX and BSD file locks such as:
* - Compatible with multi-threaded applications
* - Support for lock attempt timeouts
* - Detection of crashed lock holders
* - Detailed lock status (PID, lock age, custom data)
*
* Safelock requires POSIX.1-2008 robust mutexes. Tested under
* Linux 2.6.32 and Solaris 11.
*
* \par Note:
* A Safelock may need to be recreated after an unexpected kernel crash
* or power failure. Applications can detect and handle this situation
* when the ENOTRECOVERABLE error is returned by a Safelock function.
* This issue can be avoided by placing Safelocks on a tmpfs filesystem,
* or removing them on boot.
*
* \par Example:
* \code
* #include "safelock.h"
* ...
* safelock_t lock;
* err = safelock_open(&lock, "lock.dat", 0660);
* err = safelock_lock(lock, SAFELOCK_LOCK_WAIT, 42);
* ...
* err = safelock_unlock(lock);
* err = safelock_close(&lock);
* \endcode
*
* \author Mark Pulford <[email protected]>
* \par Source:
* https://github.com/mpx/safelock/
* \par Documentation:
* http://mpx.github.com/safelock/
* \copyright MIT license
*/
#include <stdint.h>
#include <sys/stat.h>
#include <errno.h>
/** Safelock type */
typedef struct _safelock *safelock_t;
/** Alternative lock timeout options. */
enum safelock_timeout_t {
SAFELOCK_LOCK_WAIT = 0, /**< Wait until locked. */
SAFELOCK_LOCK_TRY = 1 /**< Attempt lock once and return
* immediately. */
};
/** Updated by safelock_fetch_status() with current Safelock status. */
typedef struct {
/** Set to 1 when a Safelock is locked.
*
* When a Safelock is locked:
* - Lock metadata is always available (data_valid)
* - The locking process must still be alive (!crashed) */
int locked;
/** Set to 1 if the previous lock owner crashed.
*
* When a Safelock owner crashes, the Safelock is automatically
* unlocked (!locked). */
int crashed;
/** Set to 1 when the PID, age and data fields are available.
*
* When data_valid is zero, the PID, age and data fields are
* undefined.
*
* Safelock metadata will be unavailable when:
* - A Safelock has been created and never locked
* - A Safelock owner crashes during a Safelock update */
int data_valid;
pid_t pid; /**< PID of current or previous Safelock owner. */
uint64_t age; /**< Current lock duration (microseconds),
* or 0 (unlocked). */
int data; /**< Custom data field. */
} safelock_status_t;
/** Print an error message to STDERR and exit.
*
* The string representation of any non-zero errno value will be
* appended to the error message.
*
* This helper function is used by Safelock to handle fatal bugs.
*
* \param[in] err Errno value to decode and append
* \param[in] fmt printf(3) format string
*/
extern void safelock_die(int err, const char *fmt, ...);
/** Open a Safelock.
*
* The Safelock file will be created if it does not already exist
* and mode is non-zero.
*
* The lock file must be placed on a local filesystem that supports
* mmap(2). All processes using a Safelock must have read/write access
* to the lock file.
*
* \param[out] lock Safelock to initialise
* \param[in] filename Safelock filename to open/create
* \param[in] mode Permissions to use when creating a new
* lock file
*
* \retval 0 Success
* \retval EACCESS Permission denied, or invalid permissions (missing
* owner R/W)
* \retval EEXIST Unable to create a unique Safelock
* \retval EINTR Interrupted by signal
* \retval EIO I/O error updating inode
* \retval ENODEV Underlying filesystem does not support mmap
* \retval ENOMEM Unable to allocate memory for the Safelock
* \retval ENOSPC Disk full
*
* May also return any errno values generated by:
* - open(2)
* - link(2)
* - write(2)
* - unlink(2)
* - mmap(2)
*/
extern int safelock_open(safelock_t *lock, const char *filename,
mode_t mode);
/** Create and open a Safelock with a unique filename.
*
* Generates a unique filename by appending 6 random hexidecimal
* characters.
*
* The lock file must be placed on a local filesystem that supports
* mmap(2). All processes using a Safelock must have read/write access
* to the lock file.
*
* \param[out] lock Safelock to initialise
* \param[in] file_prefix Filename prefix
* \param[in] mode Permissions to use for the new lock file
*
* \retval 0 Success
* \retval EACCESS Permission denied, or invalid permissions (missing
* owner R/W)
* \retval EEXIST Unable to create a unique Safelock
*
* May also return any errno values generated by:
* - safelock_open()
*/
extern int safelock_open_unique(safelock_t *lock,
const char *file_prefix, mode_t mode);
/** Close a Safelock.
*
* Automatically unlocks the Safelock if it is locked.
* Frees system resources related to the Safelock.
*
* \param[in,out] lock Safelock to close
*
* \retval 0 Success
* \retval ENOTRECOVERABLE The Safelock is broken and must be
* recreated before further use.
*/
extern int safelock_close(safelock_t *lock);
/** Lock a Safelock.
*
* Atomically updates the custom data field when the lock attempt
* succeeds.
*
* \param[in] lock An open Safelock
* \param[in] timeout Timeout (microseconds), SAFELOCK_LOCK_WAIT, or
* SAFELOCK_LOCK_TRY
* \param[in] data Custom data value
*
* \retval 0 Success
* \retval EBUSY Safelock already locked (after
* SAFELOCK_LOCK_TRY)
* \retval EINVAL The Safelock has been removed and must be
* re-opened before further use
* \retval ENOTRECOVERABLE The Safelock is broken and must be closed,
* then recreated before further use
* \retval ETIMEDOUT Timeout expired
*/
extern int safelock_lock(safelock_t lock, uint64_t timeout, int data);
/** Open and lock a Safelock.
*
* Helper function combining safelock_open() and safelock_lock().
*
* The lock file must be placed on a local filesystem that supports
* mmap(2). All processes using a Safelock must have read/write access
* to the lock file.
*
* \param[out] lock Safelock to initialise
* \param[in] filename Safelock filename
* \param[in] mode Permissions to use if the Safelock is created
* \param[in] timeout Timeout (microseconds), SAFELOCK_LOCK_WAIT, or
* SAFELOCK_LOCK_TRY
* \param[in] data Custom data value
*
* \retval 0 Success
* \retval EACCESS Permission denied, or invalid permissions (missing
* owner R/W)
*
* May also return any errno values generated by:
* - safelock_open()
* - safelock_lock()
*/
extern int safelock_lock_file(safelock_t *lock, const char *filename,
mode_t mode, uint64_t timeout, int data);
/** Create and lock a Safelock with a unique filename.
*
* Helper function combining safelock_open_unique() and safelock_lock().
*
* The lock file must be placed on a local filesystem that supports
* mmap(2). All processes using a Safelock must have read/write access
* to the lock file.
*
* \param[out] lock Safelock to initialise
* \param[in] file_prefix Filename prefix
* \param[in] mode Permissions to use for the new lock file
* \param[in] data Custom data value
*
* \retval 0 Success
* \retval EACCESS Permission denied, or invalid permissions (missing
* owner R/W)
*
* May also return any errno values generated by:
* - safelock_open_unique()
* - safelock_lock()
*/
extern int safelock_lock_unique_file(safelock_t *lock,
const char *file_prefix,
mode_t mode, int data);
/** Unlock a Safelock.
*
* Only the current owner may unlock a Safelock.
*
* \param[in] lock Safelock to unlock
*
* \retval 0 Success
* \retval EINVAL The Safelock has been removed and must be
* re-opened before further use
* \retval ENOTRECOVERABLE The Safelock is broken and must be closed,
* then recreated before further use
* \retval EPERM Not the current lock owner
*/
extern int safelock_unlock(safelock_t lock);
/** Fetch current status of a Safelock.
*
* Atomically fills a safelock_status_t struct with current details for
* the Safelock.
*
* The Safelock will be automatically removed if the Safelock is
* "locked" and the owner process no longer exists. This may occur if
* the Safelock is not properly unlocked after a kernel crash or power
* failure.
*
* \param[in] lock Safelock to query
* \param[out] status Filled with the current Safelock status
*
* \retval 0 Success
* \retval EINVAL The Safelock has been removed and must be
* re-opened before further use
* \retval ENOTRECOVERABLE The Safelock is broken and must be closed,
* then recreated before further use
*/
extern int safelock_fetch_status(safelock_t lock,
safelock_status_t *status);
/** Fetch current status of a Safelock by filename.
*
* If the file is missing, an "unlocked" status will be returned
* instead.
*
* \param[in] filename Filename of Safelock to query
* \param[out] status Filled with the current Safelock status
*
* \retval 0 Success
*
* May also return any errno values generated by:
* - safelock_open() (except ENOENT)
* - safelock_fetch_status()
*/
extern int safelock_fetch_file_status(const char *filename,
safelock_status_t *status);
/** Return the filename for a Safelock.
*
* \param[in] lock Safelock
*
* \returns
*
* A pointer to the filename. The pointer remains valid until the
* Safelock is closed.
*/
extern char *safelock_filename(safelock_t lock);
/** Atomically update the Safelock custom data field.
*
* Only the current owner may update the data field.
*
* \param[in] lock Safelock to update
* \param[in] data New value for the data field
*
* \retval 0 Success
* \retval EINVAL The Safelock has been removed and must be
* re-opened before further use
* \retval ENOTRECOVERABLE The Safelock is broken and must be closed,
* then recreated before further use
* \retval EPERM Not the current lock owner
*/
extern int safelock_update_data(safelock_t lock, int data);
/** Atomically remove a Safelock from the filesystem.
*
* Tags the Safelock as "removed" and deletes the file. Any further
* operations on the Safelock (except safelock_close()) will return
* EINVAL.
*
* \param[in] lock Safelock
*
* \retval 0 Success, the safelock has been unlinked
* \retval ENOTRECOVERABLE The Safelock is broken and must be closed,
* then recreated before further use
*
* May also return any errno values generated by:
* - unlink(2)
*/
extern int safelock_remove(safelock_t lock);
/* vi:ai et sw=4 ts=4:
*/
#endif