1/*++
2/* NAME
3/*	dot_lockfile 3
4/* SUMMARY
5/*	dotlock file management
6/* SYNOPSIS
7/*	#include <dot_lockfile.h>
8/*
9/*	int	dot_lockfile(path, why)
10/*	const char *path;
11/*	VSTRING	*why;
12/*
13/*	void	dot_unlockfile(path)
14/*	const char *path;
15/* DESCRIPTION
16/*	dot_lockfile() constructs a lock file name by appending ".lock" to
17/*	\fIpath\fR and creates the named file exclusively. It tries several
18/*	times and attempts to break stale locks. A negative result value
19/*	means no lock file could be created.
20/*
21/*	dot_unlockfile() attempts to remove the lock file created by
22/*	dot_lockfile(). The operation always succeeds, and therefore
23/*	it preserves the errno value.
24/*
25/*	Arguments:
26/* .IP path
27/*	Name of the file to be locked or unlocked.
28/* .IP why
29/*	A null pointer, or storage for the reason why a lock file could
30/*	not be created.
31/* DIAGNOSTICS
32/*	dot_lockfile() returns 0 upon success. In case of failure, the
33/*	result is -1, and the errno variable is set appropriately:
34/*	EEXIST when a "fresh" lock file already exists; other values as
35/*	appropriate.
36/* CONFIGURATION PARAMETERS
37/*	deliver_lock_attempts, how many times to try to create a lock
38/*	deliver_lock_delay, how long to wait between attempts
39/*	stale_lock_time, when to break a stale lock
40/* LICENSE
41/* .ad
42/* .fi
43/*	The Secure Mailer license must be distributed with this software.
44/* AUTHOR(S)
45/*	Wietse Venema
46/*	IBM T.J. Watson Research
47/*	P.O. Box 704
48/*	Yorktown Heights, NY 10598, USA
49/*--*/
50
51/* System library. */
52
53#include "sys_defs.h"
54#include <sys/stat.h>
55#include <stdlib.h>
56#include <unistd.h>
57#include <fcntl.h>
58#include <errno.h>
59#include <time.h>
60
61/* Utility library. */
62
63#include <vstring.h>
64#include <stringops.h>
65#include <mymalloc.h>
66#include <iostuff.h>
67#include <warn_stat.h>
68
69/* Global library. */
70
71#include "mail_params.h"
72#include "dot_lockfile.h"
73
74/* Application-specific. */
75
76#define MILLION	1000000
77
78/* dot_lockfile - create user.lock file */
79
80int     dot_lockfile(const char *path, VSTRING *why)
81{
82    char   *lock_file;
83    int     count;
84    struct stat st;
85    int     fd;
86    int     status = -1;
87
88    lock_file = concatenate(path, ".lock", (char *) 0);
89
90    for (count = 1; /* void */ ; count++) {
91
92	/*
93	 * Attempt to create the lock. This code relies on O_EXCL | O_CREAT
94	 * to not follow symlinks. With NFS file systems this operation can
95	 * at the same time succeed and fail with errno of EEXIST.
96	 */
97	if ((fd = open(lock_file, O_WRONLY | O_EXCL | O_CREAT, 0)) >= 0) {
98	    close(fd);
99	    status = 0;
100	    break;
101	}
102	if (count >= var_flock_tries)
103	    break;
104
105	/*
106	 * We can deal only with "file exists" errors. Any other error means
107	 * we better give up trying.
108	 */
109	if (errno != EEXIST)
110	    break;
111
112	/*
113	 * Break the lock when it is too old. Give up when we are unable to
114	 * remove a stale lock.
115	 */
116	if (stat(lock_file, &st) == 0)
117	    if (time((time_t *) 0) > st.st_ctime + var_flock_stale)
118		if (unlink(lock_file) < 0)
119		    if (errno != ENOENT)
120			break;
121
122	rand_sleep(var_flock_delay * MILLION, var_flock_delay * MILLION / 2);
123    }
124    if (status && why)
125	vstring_sprintf(why, "unable to create lock file %s: %m", lock_file);
126
127    myfree(lock_file);
128    return (status);
129}
130
131/* dot_unlockfile - remove .lock file */
132
133void    dot_unlockfile(const char *path)
134{
135    char   *lock_file;
136    int     saved_errno = errno;
137
138    lock_file = concatenate(path, ".lock", (char *) 0);
139    (void) unlink(lock_file);
140    myfree(lock_file);
141    errno = saved_errno;
142}
143
144#ifdef TEST
145
146 /*
147  * Test program for setting a .lock file.
148  *
149  * Usage: dot_lockfile filename
150  *
151  * Creates filename.lock and removes it.
152  */
153#include <msg.h>
154#include <vstream.h>
155#include <msg_vstream.h>
156#include <mail_conf.h>
157
158int     main(int argc, char **argv)
159{
160    VSTRING *why = vstring_alloc(100);
161
162    msg_vstream_init(argv[0], VSTREAM_ERR);
163    if (argc != 2)
164	msg_fatal("usage: %s file-to-be-locked", argv[0]);
165    mail_conf_read();
166    if (dot_lockfile(argv[1], why) < 0)
167	msg_fatal("%s", vstring_str(why));
168    dot_unlockfile(argv[1]);
169    vstring_free(why);
170    return (0);
171}
172
173#endif
174