1/*++
2/* NAME
3/*	myflock 3
4/* SUMMARY
5/*	lock open file
6/* SYNOPSIS
7/*	#include <myflock.h>
8/*
9/*	int	myflock(fd, lock_style, operation)
10/*	int	fd;
11/*	int	lock_style;
12/*	int	operation;
13/* DESCRIPTION
14/*	myflock() locks or unlocks an entire open file.
15/*
16/*	In the case of a blocking request, a call that fails due to
17/*	forseeable transient problems is retried once per second.
18/*
19/*	Arguments:
20/* .IP fd
21/*	The open file to be locked/unlocked.
22/* .IP lock_style
23/*	One of the following values:
24/* .RS
25/* .IP	MYFLOCK_STYLE_FLOCK
26/*	Use BSD-style flock() locking.
27/* .IP	MYFLOCK_STYLE_FCNTL
28/*	Use POSIX-style fcntl() locking.
29/* .RE
30/* .IP operation
31/*	One of the following values:
32/* .RS
33/* .IP	MYFLOCK_OP_NONE
34/*	Release any locks the process has on the specified open file.
35/* .IP	MYFLOCK_OP_SHARED
36/*	Attempt to acquire a shared lock on the specified open file.
37/*	This is appropriate for read-only access.
38/* .IP	MYFLOCK_OP_EXCLUSIVE
39/*	Attempt to acquire an exclusive lock on the specified open
40/*	file. This is appropriate for write access.
41/* .PP
42/*	In addition, setting the MYFLOCK_OP_NOWAIT bit causes the
43/*	call to return immediately when the requested lock cannot
44/*	be acquired.
45/* .RE
46/* DIAGNOSTICS
47/*	myflock() returns 0 in case of success, -1 in case of failure.
48/*	A problem description is returned via the global \fIerrno\fR
49/*	variable. In the case of a non-blocking lock request the value
50/*	EAGAIN means that a lock is claimed by someone else.
51/*
52/*	Panic: attempts to use an unsupported file locking method or
53/*	to implement an unsupported operation.
54/* LICENSE
55/* .ad
56/* .fi
57/*	The Secure Mailer license must be distributed with this software.
58/* AUTHOR(S)
59/*	Wietse Venema
60/*	IBM T.J. Watson Research
61/*	P.O. Box 704
62/*	Yorktown Heights, NY 10598, USA
63/*--*/
64
65/* System library. */
66
67#include "sys_defs.h"
68#include <errno.h>
69#include <unistd.h>
70
71#ifdef HAS_FCNTL_LOCK
72#include <fcntl.h>
73#include <string.h>
74#endif
75
76#ifdef HAS_FLOCK_LOCK
77#include <sys/file.h>
78#endif
79
80/* Utility library. */
81
82#include "msg.h"
83#include "myflock.h"
84
85/* myflock - lock/unlock entire open file */
86
87int     myflock(int fd, int lock_style, int operation)
88{
89    int     status;
90
91    /*
92     * Sanity check.
93     */
94    if ((operation & (MYFLOCK_OP_BITS)) != operation)
95	msg_panic("myflock: improper operation type: 0x%x", operation);
96
97    switch (lock_style) {
98
99	/*
100	 * flock() does exactly what we need. Too bad it is not standard.
101	 */
102#ifdef HAS_FLOCK_LOCK
103    case MYFLOCK_STYLE_FLOCK:
104	{
105	    static int lock_ops[] = {
106		LOCK_UN, LOCK_SH, LOCK_EX, -1,
107		-1, LOCK_SH | LOCK_NB, LOCK_EX | LOCK_NB, -1
108	    };
109
110	    while ((status = flock(fd, lock_ops[operation])) < 0
111		   && errno == EINTR)
112		sleep(1);
113	    break;
114	}
115#endif
116
117	/*
118	 * fcntl() is standard and does more than we need, but we can handle
119	 * it.
120	 */
121#ifdef HAS_FCNTL_LOCK
122    case MYFLOCK_STYLE_FCNTL:
123	{
124	    struct flock lock;
125	    int     request;
126	    static int lock_ops[] = {
127		F_UNLCK, F_RDLCK, F_WRLCK
128	    };
129
130	    memset((char *) &lock, 0, sizeof(lock));
131	    lock.l_type = lock_ops[operation & ~MYFLOCK_OP_NOWAIT];
132	    request = (operation & MYFLOCK_OP_NOWAIT) ? F_SETLK : F_SETLKW;
133	    while ((status = fcntl(fd, request, &lock)) < 0
134		   && errno == EINTR)
135		sleep(1);
136	    break;
137	}
138#endif
139    default:
140	msg_panic("myflock: unsupported lock style: 0x%x", lock_style);
141    }
142
143    /*
144     * Return a consistent result. Some systems return EACCES when a lock is
145     * taken by someone else, and that would complicate error processing.
146     */
147    if (status < 0 && (operation & MYFLOCK_OP_NOWAIT) != 0)
148	if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EACCES)
149	    errno = EAGAIN;
150
151    return (status);
152}
153