1 /* lock_fcntl.c -- Lock files using fcntl()
2  *
3  * Copyright (c) 1994-2008 Carnegie Mellon University.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in
14  *    the documentation and/or other materials provided with the
15  *    distribution.
16  *
17  * 3. The name "Carnegie Mellon University" must not be used to
18  *    endorse or promote products derived from this software without
19  *    prior written permission. For permission or any legal
20  *    details, please contact
21  *      Carnegie Mellon University
22  *      Center for Technology Transfer and Enterprise Creation
23  *      4615 Forbes Avenue
24  *      Suite 302
25  *      Pittsburgh, PA  15213
26  *      (412) 268-7393, fax: (412) 268-7395
27  *      innovation@andrew.cmu.edu
28  *
29  * 4. Redistributions of any form whatsoever must retain the following
30  *    acknowledgment:
31  *    "This product includes software developed by Computing Services
32  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
33  *
34  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
35  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
36  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
37  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
38  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
39  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
40  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
41  */
42 
43 #include <config.h>
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <unistd.h>
47 #include <fcntl.h>
48 #include <errno.h>
49 
50 #include "cyr_lock.h"
51 
52 #include <syslog.h>
53 #include <time.h>
54 
55 EXPORTED const char lock_method_desc[] = "fcntl";
56 
57 EXPORTED double debug_locks_longer_than = 0.0;
58 
59 /*
60  * Block until we obtain an exclusive lock on the file descriptor 'fd',
61  * opened for reading and writing on the file named 'filename'.  If
62  * 'filename' is replaced, will re-open it as 'fd' and acquire a lock
63  * on the new file.
64  *
65  * On success, returns 0.  If a pointer to a struct stat is given as
66  * 'sbuf', it is filled in.
67  *
68  * On failure, returns -1 with an error code in errno.  If
69  * 'failaction' is provided, it is filled in with a pointer to a fixed
70  * string naming the action that failed.
71  *
72  */
lock_reopen_ex(int fd,const char * filename,struct stat * sbuf,const char ** failaction,int * changed)73 EXPORTED int lock_reopen_ex(int fd, const char *filename,
74                             struct stat *sbuf, const char **failaction,
75                             int *changed)
76 {
77     int r;
78     struct flock fl;
79     struct stat sbuffile, sbufspare;
80     int newfd;
81     struct timeval starttime;
82     if (debug_locks_longer_than)
83         gettimeofday(&starttime, 0);
84 
85 
86     if (!sbuf) sbuf = &sbufspare;
87 
88     for (;;) {
89         fl.l_type= F_WRLCK;
90         fl.l_whence = SEEK_SET;
91         fl.l_start = 0;
92         fl.l_len = 0;
93         r = fcntl(fd, F_SETLKW, &fl);
94         if (r == -1) {
95             if (errno == EINTR) continue;
96             if (failaction) *failaction = "locking";
97             return -1;
98         }
99 
100         r = fstat(fd, sbuf);
101         if (!r) r = stat(filename, &sbuffile);
102         if (r == -1) {
103             if (failaction) *failaction = "stating";
104             r = lock_unlock(fd, filename);
105             return -1;
106         }
107 
108         if (sbuf->st_ino == sbuffile.st_ino) {
109             if (debug_locks_longer_than) {
110                 struct timeval endtime;
111                 gettimeofday(&endtime, 0);
112                 double locktime = (double)(endtime.tv_sec - starttime.tv_sec) +
113                                   (double)(endtime.tv_usec - starttime.tv_usec)/1000000.0;
114                 if (locktime > debug_locks_longer_than) /* 10ms */
115                     syslog(LOG_NOTICE, "locktimer: reopen %s (%0.2fs)", filename, locktime);
116             }
117             return 0;
118         }
119 
120         if (changed) *changed = 1;
121 
122         newfd = open(filename, O_RDWR);
123         if (newfd == -1) {
124             if (failaction) *failaction = "opening";
125             r = lock_unlock(fd, filename);
126             return -1;
127         }
128         dup2(newfd, fd);
129         close(newfd);
130     }
131 }
132 
133 /*
134  * Obtain a lock on 'fd'.  The lock is exclusive if 'exclusive'
135  * is true, otherwise shared.  Normally blocks until a lock is
136  * obtained, but if 'nonblock' is true does not block and instead
137  * fails with errno=EWOUDBLOCK if the lock cannot be obtained.
138  *
139  * Returns 0 for success, -1 for failure, with errno set to an
140  * appropriate error code.
141  */
lock_setlock(int fd,int exclusive,int nonblock,const char * filename)142 EXPORTED int lock_setlock(int fd, int exclusive, int nonblock,
143                           const char *filename)
144 {
145     int r;
146     struct flock fl;
147     int type = (exclusive ? F_WRLCK : F_RDLCK);
148     int cmd = (nonblock ? F_SETLK : F_SETLKW);
149     struct timeval starttime;
150     if (debug_locks_longer_than)
151         gettimeofday(&starttime, 0);
152 
153     for (;;) {
154         fl.l_type= type;
155         fl.l_whence = SEEK_SET;
156         fl.l_start = 0;
157         fl.l_len = 0;
158         r = fcntl(fd, cmd, &fl);
159         if (r != -1) {
160             if (debug_locks_longer_than) {
161                 struct timeval endtime;
162                 gettimeofday(&endtime, 0);
163                 double locktime = (double)(endtime.tv_sec - starttime.tv_sec) +
164                                   (double)(endtime.tv_usec - starttime.tv_usec)/1000000.0;
165                 if (locktime > debug_locks_longer_than)
166                     syslog(LOG_NOTICE, "locktimer: reopen %s (%0.2fs)", filename, locktime);
167             }
168             return 0;
169         }
170         if (errno == EINTR) continue;
171         return -1;
172     }
173 }
174 
175 /*
176  * Release any lock on 'fd'.  Always returns success.
177  */
lock_unlock(int fd,const char * filename)178 EXPORTED int lock_unlock(int fd, const char *filename __attribute__((unused)))
179 {
180     struct flock fl;
181     int r;
182 
183     fl.l_type= F_UNLCK;
184     fl.l_whence = SEEK_SET;
185     fl.l_start = 0;
186     fl.l_len = 0;
187 
188     for (;;) {
189         r = fcntl(fd, F_SETLKW, &fl);
190         if (r != -1) return 0;
191         if (errno == EINTR) continue;
192         /* xxx help! */
193         return -1;
194     }
195 }
196 
197