summaryrefslogtreecommitdiffstats
path: root/src/util/myflock.c
blob: bd903ee700568385334a129730cb07535f181cdd (plain)
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
/*++
/* NAME
/*	myflock 3
/* SUMMARY
/*	lock open file
/* SYNOPSIS
/*	#include <myflock.h>
/*
/*	int	myflock(fd, lock_style, operation)
/*	int	fd;
/*	int	lock_style;
/*	int	operation;
/* DESCRIPTION
/*	myflock() locks or unlocks an entire open file.
/*
/*	In the case of a blocking request, a call that fails due to
/*	foreseeable transient problems is retried once per second.
/*
/*	Arguments:
/* .IP fd
/*	The open file to be locked/unlocked.
/* .IP lock_style
/*	One of the following values:
/* .RS
/* .IP	MYFLOCK_STYLE_FLOCK
/*	Use BSD-style flock() locking.
/* .IP	MYFLOCK_STYLE_FCNTL
/*	Use POSIX-style fcntl() locking.
/* .RE
/* .IP operation
/*	One of the following values:
/* .RS
/* .IP	MYFLOCK_OP_NONE
/*	Release any locks the process has on the specified open file.
/* .IP	MYFLOCK_OP_SHARED
/*	Attempt to acquire a shared lock on the specified open file.
/*	This is appropriate for read-only access.
/* .IP	MYFLOCK_OP_EXCLUSIVE
/*	Attempt to acquire an exclusive lock on the specified open
/*	file. This is appropriate for write access.
/* .PP
/*	In addition, setting the MYFLOCK_OP_NOWAIT bit causes the
/*	call to return immediately when the requested lock cannot
/*	be acquired.
/* .RE
/* DIAGNOSTICS
/*	myflock() returns 0 in case of success, -1 in case of failure.
/*	A problem description is returned via the global \fIerrno\fR
/*	variable. In the case of a non-blocking lock request the value
/*	EAGAIN means that a lock is claimed by someone else.
/*
/*	Panic: attempts to use an unsupported file locking method or
/*	to implement an unsupported operation.
/* LICENSE
/* .ad
/* .fi
/*	The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/*	Wietse Venema
/*	IBM T.J. Watson Research
/*	P.O. Box 704
/*	Yorktown Heights, NY 10598, USA
/*--*/

/* System library. */

#include "sys_defs.h"
#include <errno.h>
#include <unistd.h>

#ifdef HAS_FCNTL_LOCK
#include <fcntl.h>
#include <string.h>
#endif

#ifdef HAS_FLOCK_LOCK
#include <sys/file.h>
#endif

/* Utility library. */

#include "msg.h"
#include "myflock.h"

/* myflock - lock/unlock entire open file */

int     myflock(int fd, int lock_style, int operation)
{
    int     status;

    /*
     * Sanity check.
     */
    if ((operation & (MYFLOCK_OP_BITS)) != operation)
	msg_panic("myflock: improper operation type: 0x%x", operation);

    switch (lock_style) {

	/*
	 * flock() does exactly what we need. Too bad it is not standard.
	 */
#ifdef HAS_FLOCK_LOCK
    case MYFLOCK_STYLE_FLOCK:
	{
	    static int lock_ops[] = {
		LOCK_UN, LOCK_SH, LOCK_EX, -1,
		-1, LOCK_SH | LOCK_NB, LOCK_EX | LOCK_NB, -1
	    };

	    while ((status = flock(fd, lock_ops[operation])) < 0
		   && errno == EINTR)
		sleep(1);
	    break;
	}
#endif

	/*
	 * fcntl() is standard and does more than we need, but we can handle
	 * it.
	 */
#ifdef HAS_FCNTL_LOCK
    case MYFLOCK_STYLE_FCNTL:
	{
	    struct flock lock;
	    int     request;
	    static int lock_ops[] = {
		F_UNLCK, F_RDLCK, F_WRLCK
	    };

	    memset((void *) &lock, 0, sizeof(lock));
	    lock.l_type = lock_ops[operation & ~MYFLOCK_OP_NOWAIT];
	    request = (operation & MYFLOCK_OP_NOWAIT) ? F_SETLK : F_SETLKW;
	    while ((status = fcntl(fd, request, &lock)) < 0
		   && errno == EINTR)
		sleep(1);
	    break;
	}
#endif
    default:
	msg_panic("myflock: unsupported lock style: 0x%x", lock_style);
    }

    /*
     * Return a consistent result. Some systems return EACCES when a lock is
     * taken by someone else, and that would complicate error processing.
     */
    if (status < 0 && (operation & MYFLOCK_OP_NOWAIT) != 0)
	if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EACCES)
	    errno = EAGAIN;

    return (status);
}