1 |
|
|
/* $OpenBSD: lockd_lock.c,v 1.9 2015/01/16 06:40:20 deraadt Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright (c) 2000 Manuel Bouyer. |
5 |
|
|
* |
6 |
|
|
* Redistribution and use in source and binary forms, with or without |
7 |
|
|
* modification, are permitted provided that the following conditions |
8 |
|
|
* are met: |
9 |
|
|
* 1. Redistributions of source code must retain the above copyright |
10 |
|
|
* notice, this list of conditions and the following disclaimer. |
11 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
12 |
|
|
* notice, this list of conditions and the following disclaimer in the |
13 |
|
|
* documentation and/or other materials provided with the distribution. |
14 |
|
|
* |
15 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
16 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
19 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
20 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
21 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
22 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
23 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
24 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
25 |
|
|
* SUCH DAMAGE. |
26 |
|
|
* |
27 |
|
|
*/ |
28 |
|
|
|
29 |
|
|
#include <sys/socket.h> |
30 |
|
|
#include <sys/stat.h> |
31 |
|
|
#include <sys/mount.h> |
32 |
|
|
#include <sys/wait.h> |
33 |
|
|
#include <stdio.h> |
34 |
|
|
#include <stdlib.h> |
35 |
|
|
#include <unistd.h> |
36 |
|
|
#include <fcntl.h> |
37 |
|
|
#include <inttypes.h> |
38 |
|
|
#include <syslog.h> |
39 |
|
|
#include <errno.h> |
40 |
|
|
#include <string.h> |
41 |
|
|
#include <signal.h> |
42 |
|
|
#include <rpc/rpc.h> |
43 |
|
|
#include <rpcsvc/sm_inter.h> |
44 |
|
|
#include <rpcsvc/nlm_prot.h> |
45 |
|
|
#include "lockd_lock.h" |
46 |
|
|
#include "lockd.h" |
47 |
|
|
|
48 |
|
|
/* A set of utilities for managing file locking */ |
49 |
|
|
LIST_HEAD(lcklst_head, file_lock); |
50 |
|
|
struct lcklst_head lcklst_head = LIST_HEAD_INITIALIZER(lcklst_head); |
51 |
|
|
|
52 |
|
|
#define FHANDLE_SIZE_MAX 1024 /* arbitrary big enough value */ |
53 |
|
|
typedef struct { |
54 |
|
|
size_t fhsize; |
55 |
|
|
char *fhdata; |
56 |
|
|
} nfs_fhandle_t; |
57 |
|
|
|
58 |
|
|
static int |
59 |
|
|
fhcmp(const nfs_fhandle_t *fh1, const nfs_fhandle_t *fh2) |
60 |
|
|
{ |
61 |
|
|
return memcmp(fh1->fhdata, fh2->fhdata, sizeof(fhandle_t)); |
62 |
|
|
} |
63 |
|
|
|
64 |
|
|
static int |
65 |
|
|
fhconv(nfs_fhandle_t *fh, const netobj *rfh) |
66 |
|
|
{ |
67 |
|
|
size_t sz; |
68 |
|
|
|
69 |
|
|
sz = rfh->n_len; |
70 |
|
|
if (sz > FHANDLE_SIZE_MAX) { |
71 |
|
|
syslog(LOG_DEBUG, |
72 |
|
|
"received fhandle size %zd, max supported size %d", |
73 |
|
|
sz, FHANDLE_SIZE_MAX); |
74 |
|
|
errno = EINVAL; |
75 |
|
|
return -1; |
76 |
|
|
} |
77 |
|
|
fh->fhdata = malloc(sz); |
78 |
|
|
if (fh->fhdata == NULL) { |
79 |
|
|
return -1; |
80 |
|
|
} |
81 |
|
|
fh->fhsize = sz; |
82 |
|
|
memcpy(fh->fhdata, rfh->n_bytes, sz); |
83 |
|
|
return 0; |
84 |
|
|
} |
85 |
|
|
|
86 |
|
|
static void |
87 |
|
|
fhfree(nfs_fhandle_t *fh) |
88 |
|
|
{ |
89 |
|
|
|
90 |
|
|
free(fh->fhdata); |
91 |
|
|
} |
92 |
|
|
|
93 |
|
|
/* struct describing a lock */ |
94 |
|
|
struct file_lock { |
95 |
|
|
LIST_ENTRY(file_lock) lcklst; |
96 |
|
|
nfs_fhandle_t filehandle; /* NFS filehandle */ |
97 |
|
|
struct sockaddr_in *addr; |
98 |
|
|
struct nlm4_holder client; /* lock holder */ |
99 |
|
|
netobj client_cookie; /* cookie sent by the client */ |
100 |
|
|
char client_name[128]; |
101 |
|
|
int nsm_status; /* status from the remote lock manager */ |
102 |
|
|
int status; /* lock status, see below */ |
103 |
|
|
int flags; /* lock flags, see lockd_lock.h */ |
104 |
|
|
pid_t locker; /* pid of the child process trying to get the lock */ |
105 |
|
|
int fd; /* file descriptor for this lock */ |
106 |
|
|
}; |
107 |
|
|
|
108 |
|
|
/* lock status */ |
109 |
|
|
#define LKST_LOCKED 1 /* lock is locked */ |
110 |
|
|
#define LKST_WAITING 2 /* file is already locked by another host */ |
111 |
|
|
#define LKST_PROCESSING 3 /* child is trying to acquire the lock */ |
112 |
|
|
#define LKST_DYING 4 /* must die when we get news from the child */ |
113 |
|
|
|
114 |
|
|
static struct file_lock *lalloc(void); |
115 |
|
|
void lfree(struct file_lock *); |
116 |
|
|
enum nlm_stats do_lock(struct file_lock *, int); |
117 |
|
|
enum nlm_stats do_unlock(struct file_lock *); |
118 |
|
|
void send_granted(struct file_lock *, int); |
119 |
|
|
void siglock(void); |
120 |
|
|
void sigunlock(void); |
121 |
|
|
|
122 |
|
|
/* list of hosts we monitor */ |
123 |
|
|
LIST_HEAD(hostlst_head, host); |
124 |
|
|
struct hostlst_head hostlst_head = LIST_HEAD_INITIALIZER(hostlst_head); |
125 |
|
|
|
126 |
|
|
/* struct describing a lock */ |
127 |
|
|
struct host { |
128 |
|
|
LIST_ENTRY(host) hostlst; |
129 |
|
|
char name[SM_MAXSTRLEN+1]; |
130 |
|
|
int refcnt; |
131 |
|
|
}; |
132 |
|
|
|
133 |
|
|
void do_mon(const char *); |
134 |
|
|
|
135 |
|
|
#define LL_FH 0x01 |
136 |
|
|
#define LL_NAME 0x02 |
137 |
|
|
#define LL_SVID 0x04 |
138 |
|
|
|
139 |
|
|
static struct file_lock *lock_lookup(struct file_lock *, int); |
140 |
|
|
|
141 |
|
|
/* |
142 |
|
|
* lock_lookup: lookup a matching lock. |
143 |
|
|
* called with siglock held. |
144 |
|
|
*/ |
145 |
|
|
static struct file_lock * |
146 |
|
|
lock_lookup(struct file_lock *newfl, int flags) |
147 |
|
|
{ |
148 |
|
|
struct file_lock *fl; |
149 |
|
|
|
150 |
|
|
LIST_FOREACH(fl, &lcklst_head, lcklst) { |
151 |
|
|
if ((flags & LL_SVID) != 0 && |
152 |
|
|
newfl->client.svid != fl->client.svid) |
153 |
|
|
continue; |
154 |
|
|
if ((flags & LL_NAME) != 0 && |
155 |
|
|
strcmp(newfl->client_name, fl->client_name) != 0) |
156 |
|
|
continue; |
157 |
|
|
if ((flags & LL_FH) != 0 && |
158 |
|
|
fhcmp(&newfl->filehandle, &fl->filehandle) != 0) |
159 |
|
|
continue; |
160 |
|
|
/* found */ |
161 |
|
|
break; |
162 |
|
|
} |
163 |
|
|
|
164 |
|
|
return fl; |
165 |
|
|
} |
166 |
|
|
|
167 |
|
|
/* |
168 |
|
|
* testlock(): inform the caller if the requested lock would be granted or not |
169 |
|
|
* returns NULL if lock would granted, or pointer to the current nlm4_holder |
170 |
|
|
* otherwise. |
171 |
|
|
*/ |
172 |
|
|
|
173 |
|
|
struct nlm4_holder * |
174 |
|
|
/*ARGSUSED*/ |
175 |
|
|
testlock(struct nlm4_lock *lock, int flags) |
176 |
|
|
{ |
177 |
|
|
struct file_lock *fl; |
178 |
|
|
nfs_fhandle_t filehandle; |
179 |
|
|
|
180 |
|
|
/* convert lock to a local filehandle */ |
181 |
|
|
if (fhconv(&filehandle, &lock->fh)) { |
182 |
|
|
syslog(LOG_NOTICE, "fhconv failed (%m)"); |
183 |
|
|
return NULL; /* XXX */ |
184 |
|
|
} |
185 |
|
|
|
186 |
|
|
siglock(); |
187 |
|
|
/* search through the list for lock holder */ |
188 |
|
|
LIST_FOREACH(fl, &lcklst_head, lcklst) { |
189 |
|
|
if (fl->status != LKST_LOCKED) |
190 |
|
|
continue; |
191 |
|
|
if (fhcmp(&fl->filehandle, &filehandle) != 0) |
192 |
|
|
continue; |
193 |
|
|
/* got it ! */ |
194 |
|
|
syslog(LOG_DEBUG, "test for %s: found lock held by %s", |
195 |
|
|
lock->caller_name, fl->client_name); |
196 |
|
|
sigunlock(); |
197 |
|
|
fhfree(&filehandle); |
198 |
|
|
return (&fl->client); |
199 |
|
|
} |
200 |
|
|
/* not found */ |
201 |
|
|
sigunlock(); |
202 |
|
|
fhfree(&filehandle); |
203 |
|
|
syslog(LOG_DEBUG, "test for %s: no lock found", lock->caller_name); |
204 |
|
|
return NULL; |
205 |
|
|
} |
206 |
|
|
|
207 |
|
|
/* |
208 |
|
|
* getlock: try to acquire the lock. |
209 |
|
|
* If file is already locked and we can sleep, put the lock in the list with |
210 |
|
|
* status LKST_WAITING; it'll be processed later. |
211 |
|
|
* Otherwise try to lock. If we're allowed to block, fork a child which |
212 |
|
|
* will do the blocking lock. |
213 |
|
|
*/ |
214 |
|
|
enum nlm_stats |
215 |
|
|
getlock(nlm4_lockargs * lckarg, struct svc_req *rqstp, int flags) |
216 |
|
|
{ |
217 |
|
|
struct file_lock *fl, *newfl; |
218 |
|
|
enum nlm_stats retval; |
219 |
|
|
struct sockaddr_in *addr; |
220 |
|
|
|
221 |
|
|
if (grace_expired == 0 && lckarg->reclaim == 0) |
222 |
|
|
return (flags & LOCK_V4) ? |
223 |
|
|
nlm4_denied_grace_period : nlm_denied_grace_period; |
224 |
|
|
|
225 |
|
|
/* allocate new file_lock for this request */ |
226 |
|
|
newfl = lalloc(); |
227 |
|
|
if (newfl == NULL) { |
228 |
|
|
syslog(LOG_NOTICE, "malloc failed (%m)"); |
229 |
|
|
/* failed */ |
230 |
|
|
return (flags & LOCK_V4) ? |
231 |
|
|
nlm4_denied_nolock : nlm_denied_nolocks; |
232 |
|
|
} |
233 |
|
|
if (fhconv(&newfl->filehandle, &lckarg->alock.fh)) { |
234 |
|
|
syslog(LOG_NOTICE, "fhconv failed (%m)"); |
235 |
|
|
lfree(newfl); |
236 |
|
|
/* failed */ |
237 |
|
|
return (flags & LOCK_V4) ? |
238 |
|
|
nlm4_denied_nolock : nlm_denied_nolocks; |
239 |
|
|
} |
240 |
|
|
addr = svc_getcaller(rqstp->rq_xprt); |
241 |
|
|
newfl->addr = malloc(addr->sin_len); |
242 |
|
|
if (newfl->addr == NULL) { |
243 |
|
|
syslog(LOG_NOTICE, "malloc failed (%m)"); |
244 |
|
|
lfree(newfl); |
245 |
|
|
/* failed */ |
246 |
|
|
return (flags & LOCK_V4) ? |
247 |
|
|
nlm4_denied_nolock : nlm_denied_nolocks; |
248 |
|
|
} |
249 |
|
|
memcpy(newfl->addr, addr, addr->sin_len); |
250 |
|
|
newfl->client.exclusive = lckarg->exclusive; |
251 |
|
|
newfl->client.svid = lckarg->alock.svid; |
252 |
|
|
newfl->client.oh.n_bytes = malloc(lckarg->alock.oh.n_len); |
253 |
|
|
if (newfl->client.oh.n_bytes == NULL) { |
254 |
|
|
syslog(LOG_NOTICE, "malloc failed (%m)"); |
255 |
|
|
lfree(newfl); |
256 |
|
|
return (flags & LOCK_V4) ? |
257 |
|
|
nlm4_denied_nolock : nlm_denied_nolocks; |
258 |
|
|
} |
259 |
|
|
newfl->client.oh.n_len = lckarg->alock.oh.n_len; |
260 |
|
|
memcpy(newfl->client.oh.n_bytes, lckarg->alock.oh.n_bytes, |
261 |
|
|
lckarg->alock.oh.n_len); |
262 |
|
|
newfl->client.l_offset = lckarg->alock.l_offset; |
263 |
|
|
newfl->client.l_len = lckarg->alock.l_len; |
264 |
|
|
newfl->client_cookie.n_len = lckarg->cookie.n_len; |
265 |
|
|
newfl->client_cookie.n_bytes = malloc(lckarg->cookie.n_len); |
266 |
|
|
if (newfl->client_cookie.n_bytes == NULL) { |
267 |
|
|
syslog(LOG_NOTICE, "malloc failed (%m)"); |
268 |
|
|
lfree(newfl); |
269 |
|
|
return (flags & LOCK_V4) ? |
270 |
|
|
nlm4_denied_nolock : nlm_denied_nolocks; |
271 |
|
|
} |
272 |
|
|
memcpy(newfl->client_cookie.n_bytes, lckarg->cookie.n_bytes, |
273 |
|
|
lckarg->cookie.n_len); |
274 |
|
|
strlcpy(newfl->client_name, lckarg->alock.caller_name, |
275 |
|
|
sizeof(newfl->client_name)); |
276 |
|
|
newfl->nsm_status = lckarg->state; |
277 |
|
|
newfl->status = 0; |
278 |
|
|
newfl->flags = flags; |
279 |
|
|
siglock(); |
280 |
|
|
/* look for a lock rq from this host for this fh */ |
281 |
|
|
fl = lock_lookup(newfl, LL_FH|LL_NAME|LL_SVID); |
282 |
|
|
if (fl) { |
283 |
|
|
/* already locked by this host ??? */ |
284 |
|
|
sigunlock(); |
285 |
|
|
syslog(LOG_NOTICE, "duplicate lock from %s.%" |
286 |
|
|
PRIu32, |
287 |
|
|
newfl->client_name, newfl->client.svid); |
288 |
|
|
lfree(newfl); |
289 |
|
|
switch(fl->status) { |
290 |
|
|
case LKST_LOCKED: |
291 |
|
|
return (flags & LOCK_V4) ? |
292 |
|
|
nlm4_granted : nlm_granted; |
293 |
|
|
case LKST_WAITING: |
294 |
|
|
case LKST_PROCESSING: |
295 |
|
|
return (flags & LOCK_V4) ? |
296 |
|
|
nlm4_blocked : nlm_blocked; |
297 |
|
|
case LKST_DYING: |
298 |
|
|
return (flags & LOCK_V4) ? |
299 |
|
|
nlm4_denied : nlm_denied; |
300 |
|
|
default: |
301 |
|
|
syslog(LOG_NOTICE, "bad status %d", |
302 |
|
|
fl->status); |
303 |
|
|
return (flags & LOCK_V4) ? |
304 |
|
|
nlm4_failed : nlm_denied; |
305 |
|
|
} |
306 |
|
|
/* NOTREACHED */ |
307 |
|
|
} |
308 |
|
|
fl = lock_lookup(newfl, LL_FH); |
309 |
|
|
if (fl) { |
310 |
|
|
/* |
311 |
|
|
* We already have a lock for this file. |
312 |
|
|
* Put this one in waiting state if allowed to block |
313 |
|
|
*/ |
314 |
|
|
if (lckarg->block) { |
315 |
|
|
syslog(LOG_DEBUG, "lock from %s.%" PRIu32 ": " |
316 |
|
|
"already locked, waiting", |
317 |
|
|
lckarg->alock.caller_name, |
318 |
|
|
lckarg->alock.svid); |
319 |
|
|
newfl->status = LKST_WAITING; |
320 |
|
|
LIST_INSERT_HEAD(&lcklst_head, newfl, lcklst); |
321 |
|
|
do_mon(lckarg->alock.caller_name); |
322 |
|
|
sigunlock(); |
323 |
|
|
return (flags & LOCK_V4) ? |
324 |
|
|
nlm4_blocked : nlm_blocked; |
325 |
|
|
} else { |
326 |
|
|
sigunlock(); |
327 |
|
|
syslog(LOG_DEBUG, "lock from %s.%" PRIu32 ": " |
328 |
|
|
"already locked, failed", |
329 |
|
|
lckarg->alock.caller_name, |
330 |
|
|
lckarg->alock.svid); |
331 |
|
|
lfree(newfl); |
332 |
|
|
return (flags & LOCK_V4) ? |
333 |
|
|
nlm4_denied : nlm_denied; |
334 |
|
|
} |
335 |
|
|
/* NOTREACHED */ |
336 |
|
|
} |
337 |
|
|
|
338 |
|
|
/* no entry for this file yet; add to list */ |
339 |
|
|
LIST_INSERT_HEAD(&lcklst_head, newfl, lcklst); |
340 |
|
|
/* do the lock */ |
341 |
|
|
retval = do_lock(newfl, lckarg->block); |
342 |
|
|
switch (retval) { |
343 |
|
|
case nlm4_granted: |
344 |
|
|
/* case nlm_granted: is the same as nlm4_granted */ |
345 |
|
|
case nlm4_blocked: |
346 |
|
|
/* case nlm_blocked: is the same as nlm4_blocked */ |
347 |
|
|
do_mon(lckarg->alock.caller_name); |
348 |
|
|
break; |
349 |
|
|
default: |
350 |
|
|
lfree(newfl); |
351 |
|
|
break; |
352 |
|
|
} |
353 |
|
|
sigunlock(); |
354 |
|
|
return retval; |
355 |
|
|
} |
356 |
|
|
|
357 |
|
|
/* unlock a filehandle */ |
358 |
|
|
enum nlm_stats |
359 |
|
|
unlock(nlm4_lock *lck, int flags) |
360 |
|
|
{ |
361 |
|
|
struct file_lock *fl; |
362 |
|
|
nfs_fhandle_t filehandle; |
363 |
|
|
int err = (flags & LOCK_V4) ? nlm4_granted : nlm_granted; |
364 |
|
|
|
365 |
|
|
if (fhconv(&filehandle, &lck->fh)) { |
366 |
|
|
syslog(LOG_NOTICE, "fhconv failed (%m)"); |
367 |
|
|
return (flags & LOCK_V4) ? nlm4_denied : nlm_denied; |
368 |
|
|
} |
369 |
|
|
siglock(); |
370 |
|
|
LIST_FOREACH(fl, &lcklst_head, lcklst) { |
371 |
|
|
if (strcmp(fl->client_name, lck->caller_name) || |
372 |
|
|
fhcmp(&filehandle, &fl->filehandle) != 0 || |
373 |
|
|
fl->client.oh.n_len != lck->oh.n_len || |
374 |
|
|
memcmp(fl->client.oh.n_bytes, lck->oh.n_bytes, |
375 |
|
|
fl->client.oh.n_len) != 0 || |
376 |
|
|
fl->client.svid != lck->svid) |
377 |
|
|
continue; |
378 |
|
|
/* Got it, unlock and remove from the queue */ |
379 |
|
|
syslog(LOG_DEBUG, "unlock from %s.%" PRIu32 ": found struct, " |
380 |
|
|
"status %d", lck->caller_name, lck->svid, fl->status); |
381 |
|
|
switch (fl->status) { |
382 |
|
|
case LKST_LOCKED: |
383 |
|
|
err = do_unlock(fl); |
384 |
|
|
break; |
385 |
|
|
case LKST_WAITING: |
386 |
|
|
/* remove from the list */ |
387 |
|
|
LIST_REMOVE(fl, lcklst); |
388 |
|
|
lfree(fl); |
389 |
|
|
break; |
390 |
|
|
case LKST_PROCESSING: |
391 |
|
|
/* |
392 |
|
|
* being handled by a child; will clean up |
393 |
|
|
* when the child exits |
394 |
|
|
*/ |
395 |
|
|
fl->status = LKST_DYING; |
396 |
|
|
break; |
397 |
|
|
case LKST_DYING: |
398 |
|
|
/* nothing to do */ |
399 |
|
|
break; |
400 |
|
|
default: |
401 |
|
|
syslog(LOG_NOTICE, "unknow status %d for %s", |
402 |
|
|
fl->status, fl->client_name); |
403 |
|
|
} |
404 |
|
|
sigunlock(); |
405 |
|
|
fhfree(&filehandle); |
406 |
|
|
return err; |
407 |
|
|
} |
408 |
|
|
sigunlock(); |
409 |
|
|
/* didn't find a matching entry; log anyway */ |
410 |
|
|
syslog(LOG_NOTICE, "no matching entry for %s", |
411 |
|
|
lck->caller_name); |
412 |
|
|
fhfree(&filehandle); |
413 |
|
|
return (flags & LOCK_V4) ? nlm4_granted : nlm_granted; |
414 |
|
|
} |
415 |
|
|
|
416 |
|
|
static struct file_lock * |
417 |
|
|
lalloc(void) |
418 |
|
|
{ |
419 |
|
|
return calloc(1, sizeof(struct file_lock)); |
420 |
|
|
} |
421 |
|
|
|
422 |
|
|
void |
423 |
|
|
lfree(struct file_lock *fl) |
424 |
|
|
{ |
425 |
|
|
free(fl->addr); |
426 |
|
|
free(fl->client.oh.n_bytes); |
427 |
|
|
free(fl->client_cookie.n_bytes); |
428 |
|
|
fhfree(&fl->filehandle); |
429 |
|
|
free(fl); |
430 |
|
|
} |
431 |
|
|
|
432 |
|
|
void |
433 |
|
|
/*ARGSUSED*/ |
434 |
|
|
sigchild_handler(int sig) |
435 |
|
|
{ |
436 |
|
|
int sstatus; |
437 |
|
|
pid_t pid; |
438 |
|
|
struct file_lock *fl; |
439 |
|
|
|
440 |
|
|
for (;;) { |
441 |
|
|
pid = wait4(-1, &sstatus, WNOHANG, NULL); |
442 |
|
|
if (pid == -1) { |
443 |
|
|
if (errno != ECHILD) |
444 |
|
|
syslog(LOG_NOTICE, "wait failed (%m)"); |
445 |
|
|
else |
446 |
|
|
syslog(LOG_DEBUG, "wait failed (%m)"); |
447 |
|
|
return; |
448 |
|
|
} |
449 |
|
|
if (pid == 0) { |
450 |
|
|
/* no more child to handle yet */ |
451 |
|
|
return; |
452 |
|
|
} |
453 |
|
|
/* |
454 |
|
|
* if we're here we have a child that exited |
455 |
|
|
* Find the associated file_lock. |
456 |
|
|
*/ |
457 |
|
|
LIST_FOREACH(fl, &lcklst_head, lcklst) { |
458 |
|
|
if (pid == fl->locker) |
459 |
|
|
break; |
460 |
|
|
} |
461 |
|
|
if (fl == NULL) { |
462 |
|
|
syslog(LOG_NOTICE, "unknown child %d", pid); |
463 |
|
|
} else { |
464 |
|
|
/* protect from pid reusing. */ |
465 |
|
|
fl->locker = 0; |
466 |
|
|
if (!WIFEXITED(sstatus) || WEXITSTATUS(sstatus) != 0) { |
467 |
|
|
syslog(LOG_NOTICE, "child %d failed", pid); |
468 |
|
|
/* |
469 |
|
|
* can't do much here; we can't reply |
470 |
|
|
* anything but OK for blocked locks |
471 |
|
|
* Eventually the client will time out |
472 |
|
|
* and retry. |
473 |
|
|
*/ |
474 |
|
|
do_unlock(fl); |
475 |
|
|
return; |
476 |
|
|
} |
477 |
|
|
|
478 |
|
|
/* check lock status */ |
479 |
|
|
syslog(LOG_DEBUG, "processing child %d, status %d", |
480 |
|
|
pid, fl->status); |
481 |
|
|
switch(fl->status) { |
482 |
|
|
case LKST_PROCESSING: |
483 |
|
|
fl->status = LKST_LOCKED; |
484 |
|
|
send_granted(fl, (fl->flags & LOCK_V4) ? |
485 |
|
|
nlm4_granted : nlm_granted); |
486 |
|
|
break; |
487 |
|
|
case LKST_DYING: |
488 |
|
|
do_unlock(fl); |
489 |
|
|
break; |
490 |
|
|
default: |
491 |
|
|
syslog(LOG_NOTICE, "bad lock status (%d) for" |
492 |
|
|
" child %d", fl->status, pid); |
493 |
|
|
} |
494 |
|
|
} |
495 |
|
|
} |
496 |
|
|
} |
497 |
|
|
|
498 |
|
|
/* |
499 |
|
|
* |
500 |
|
|
* try to acquire the lock described by fl. Eventually fork a child to do a |
501 |
|
|
* blocking lock if allowed and required. |
502 |
|
|
*/ |
503 |
|
|
|
504 |
|
|
enum nlm_stats |
505 |
|
|
do_lock(struct file_lock *fl, int block) |
506 |
|
|
{ |
507 |
|
|
int lflags, error; |
508 |
|
|
struct stat st; |
509 |
|
|
|
510 |
|
|
fl->fd = fhopen((fhandle_t *)fl->filehandle.fhdata, O_RDWR); |
511 |
|
|
if (fl->fd < 0) { |
512 |
|
|
switch (errno) { |
513 |
|
|
case ESTALE: |
514 |
|
|
error = nlm4_stale_fh; |
515 |
|
|
break; |
516 |
|
|
case EROFS: |
517 |
|
|
error = nlm4_rofs; |
518 |
|
|
break; |
519 |
|
|
default: |
520 |
|
|
error = nlm4_failed; |
521 |
|
|
} |
522 |
|
|
if ((fl->flags & LOCK_V4) == 0) |
523 |
|
|
error = nlm_denied; |
524 |
|
|
syslog(LOG_NOTICE, "fhopen failed (from %s) (%m)", |
525 |
|
|
fl->client_name); |
526 |
|
|
LIST_REMOVE(fl, lcklst); |
527 |
|
|
return error; |
528 |
|
|
} |
529 |
|
|
if (fstat(fl->fd, &st) < 0) { |
530 |
|
|
syslog(LOG_NOTICE, "fstat failed (from %s) (%m)", |
531 |
|
|
fl->client_name); |
532 |
|
|
} |
533 |
|
|
syslog(LOG_DEBUG, "lock from %s.%" PRIu32 " for file%s%s: " |
534 |
|
|
"dev %u ino %llu (uid %d), flags %d", |
535 |
|
|
fl->client_name, fl->client.svid, |
536 |
|
|
fl->client.exclusive ? " (exclusive)":"", block ? " (block)":"", |
537 |
|
|
st.st_dev, (unsigned long long)st.st_ino, st.st_uid, fl->flags); |
538 |
|
|
lflags = LOCK_NB; |
539 |
|
|
if (fl->client.exclusive == 0) |
540 |
|
|
lflags |= LOCK_SH; |
541 |
|
|
else |
542 |
|
|
lflags |= LOCK_EX; |
543 |
|
|
error = flock(fl->fd, lflags); |
544 |
|
|
if (error != 0 && errno == EAGAIN && block) { |
545 |
|
|
switch (fl->locker = fork()) { |
546 |
|
|
case -1: /* fork failed */ |
547 |
|
|
syslog(LOG_NOTICE, "fork failed (%m)"); |
548 |
|
|
LIST_REMOVE(fl, lcklst); |
549 |
|
|
close(fl->fd); |
550 |
|
|
return (fl->flags & LOCK_V4) ? |
551 |
|
|
nlm4_denied_nolock : nlm_denied_nolocks; |
552 |
|
|
case 0: |
553 |
|
|
/* |
554 |
|
|
* Attempt a blocking lock. Will have to call |
555 |
|
|
* NLM_GRANTED later. |
556 |
|
|
*/ |
557 |
|
|
setproctitle("%s.%" PRIu32, |
558 |
|
|
fl->client_name, fl->client.svid); |
559 |
|
|
lflags &= ~LOCK_NB; |
560 |
|
|
if(flock(fl->fd, lflags) != 0) { |
561 |
|
|
syslog(LOG_NOTICE, "flock failed (%m)"); |
562 |
|
|
_exit(1); |
563 |
|
|
} |
564 |
|
|
/* lock granted */ |
565 |
|
|
_exit(0); |
566 |
|
|
/*NOTREACHED*/ |
567 |
|
|
default: |
568 |
|
|
syslog(LOG_DEBUG, "lock request from %s.%" PRIu32 ": " |
569 |
|
|
"forked %d", |
570 |
|
|
fl->client_name, fl->client.svid, fl->locker); |
571 |
|
|
fl->status = LKST_PROCESSING; |
572 |
|
|
return (fl->flags & LOCK_V4) ? |
573 |
|
|
nlm4_blocked : nlm_blocked; |
574 |
|
|
} |
575 |
|
|
} |
576 |
|
|
/* non block case */ |
577 |
|
|
if (error != 0) { |
578 |
|
|
switch (errno) { |
579 |
|
|
case EAGAIN: |
580 |
|
|
error = nlm4_denied; |
581 |
|
|
break; |
582 |
|
|
case ESTALE: |
583 |
|
|
error = nlm4_stale_fh; |
584 |
|
|
break; |
585 |
|
|
case EROFS: |
586 |
|
|
error = nlm4_rofs; |
587 |
|
|
break; |
588 |
|
|
default: |
589 |
|
|
error = nlm4_failed; |
590 |
|
|
} |
591 |
|
|
if ((fl->flags & LOCK_V4) == 0) |
592 |
|
|
error = nlm_denied; |
593 |
|
|
if (errno != EAGAIN) |
594 |
|
|
syslog(LOG_NOTICE, "flock for %s failed (%m)", |
595 |
|
|
fl->client_name); |
596 |
|
|
else syslog(LOG_DEBUG, "flock for %s failed (%m)", |
597 |
|
|
fl->client_name); |
598 |
|
|
LIST_REMOVE(fl, lcklst); |
599 |
|
|
close(fl->fd); |
600 |
|
|
return error; |
601 |
|
|
} |
602 |
|
|
fl->status = LKST_LOCKED; |
603 |
|
|
return (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted; |
604 |
|
|
} |
605 |
|
|
|
606 |
|
|
void |
607 |
|
|
/*ARGSUSED*/ |
608 |
|
|
send_granted(struct file_lock *fl, int opcode) |
609 |
|
|
{ |
610 |
|
|
CLIENT *cli; |
611 |
|
|
static char dummy; |
612 |
|
|
struct timeval timeo; |
613 |
|
|
int success; |
614 |
|
|
static struct nlm_res retval; |
615 |
|
|
static struct nlm4_res retval4; |
616 |
|
|
|
617 |
|
|
cli = get_client(fl->addr, |
618 |
|
|
(fl->flags & LOCK_V4) ? NLM_VERS4 : NLM_VERS); |
619 |
|
|
if (cli == NULL) { |
620 |
|
|
syslog(LOG_NOTICE, "failed to get CLIENT for %s.%" PRIu32, |
621 |
|
|
fl->client_name, fl->client.svid); |
622 |
|
|
/* |
623 |
|
|
* We fail to notify remote that the lock has been granted. |
624 |
|
|
* The client will timeout and retry, the lock will be |
625 |
|
|
* granted at this time. |
626 |
|
|
*/ |
627 |
|
|
return; |
628 |
|
|
} |
629 |
|
|
timeo.tv_sec = 0; |
630 |
|
|
timeo.tv_usec = (fl->flags & LOCK_ASYNC) ? 0 : 500000; /* 0.5s */ |
631 |
|
|
|
632 |
|
|
if (fl->flags & LOCK_V4) { |
633 |
|
|
static nlm4_testargs result; |
634 |
|
|
result.cookie = fl->client_cookie; |
635 |
|
|
result.exclusive = fl->client.exclusive; |
636 |
|
|
result.alock.caller_name = fl->client_name; |
637 |
|
|
result.alock.fh.n_len = fl->filehandle.fhsize; |
638 |
|
|
result.alock.fh.n_bytes = fl->filehandle.fhdata; |
639 |
|
|
result.alock.oh = fl->client.oh; |
640 |
|
|
result.alock.svid = fl->client.svid; |
641 |
|
|
result.alock.l_offset = fl->client.l_offset; |
642 |
|
|
result.alock.l_len = fl->client.l_len; |
643 |
|
|
syslog(LOG_DEBUG, "sending v4 reply%s", |
644 |
|
|
(fl->flags & LOCK_ASYNC) ? " (async)":""); |
645 |
|
|
if (fl->flags & LOCK_ASYNC) { |
646 |
|
|
success = clnt_call(cli, NLM4_GRANTED_MSG, |
647 |
|
|
xdr_nlm4_testargs, &result, xdr_void, &dummy, timeo); |
648 |
|
|
} else { |
649 |
|
|
success = clnt_call(cli, NLM4_GRANTED, |
650 |
|
|
xdr_nlm4_testargs, &result, xdr_nlm4_res, |
651 |
|
|
&retval4, timeo); |
652 |
|
|
} |
653 |
|
|
} else { |
654 |
|
|
static nlm_testargs result; |
655 |
|
|
|
656 |
|
|
result.cookie = fl->client_cookie; |
657 |
|
|
result.exclusive = fl->client.exclusive; |
658 |
|
|
result.alock.caller_name = fl->client_name; |
659 |
|
|
result.alock.fh.n_len = fl->filehandle.fhsize; |
660 |
|
|
result.alock.fh.n_bytes = fl->filehandle.fhdata; |
661 |
|
|
result.alock.oh = fl->client.oh; |
662 |
|
|
result.alock.svid = fl->client.svid; |
663 |
|
|
result.alock.l_offset = |
664 |
|
|
(unsigned int)fl->client.l_offset; |
665 |
|
|
result.alock.l_len = |
666 |
|
|
(unsigned int)fl->client.l_len; |
667 |
|
|
syslog(LOG_DEBUG, "sending v1 reply%s", |
668 |
|
|
(fl->flags & LOCK_ASYNC) ? " (async)":""); |
669 |
|
|
if (fl->flags & LOCK_ASYNC) { |
670 |
|
|
success = clnt_call(cli, NLM_GRANTED_MSG, |
671 |
|
|
xdr_nlm_testargs, &result, xdr_void, &dummy, timeo); |
672 |
|
|
} else { |
673 |
|
|
success = clnt_call(cli, NLM_GRANTED, |
674 |
|
|
xdr_nlm_testargs, &result, xdr_nlm_res, |
675 |
|
|
&retval, timeo); |
676 |
|
|
} |
677 |
|
|
} |
678 |
|
|
if (debug_level > 2) |
679 |
|
|
syslog(LOG_DEBUG, "clnt_call returns %d(%s) for granted", |
680 |
|
|
success, clnt_sperrno(success)); |
681 |
|
|
|
682 |
|
|
} |
683 |
|
|
|
684 |
|
|
enum nlm_stats |
685 |
|
|
do_unlock(struct file_lock *rfl) |
686 |
|
|
{ |
687 |
|
|
struct file_lock *fl; |
688 |
|
|
int error; |
689 |
|
|
int lockst; |
690 |
|
|
|
691 |
|
|
/* unlock the file: closing is enough ! */ |
692 |
|
|
if (close(rfl->fd) == -1) { |
693 |
|
|
if (errno == ESTALE) |
694 |
|
|
error = nlm4_stale_fh; |
695 |
|
|
else |
696 |
|
|
error = nlm4_failed; |
697 |
|
|
if ((rfl->flags & LOCK_V4) == 0) |
698 |
|
|
error = nlm_denied; |
699 |
|
|
syslog(LOG_NOTICE, "close failed (from %s) (%m)", |
700 |
|
|
rfl->client_name); |
701 |
|
|
} else { |
702 |
|
|
error = (rfl->flags & LOCK_V4) ? |
703 |
|
|
nlm4_granted : nlm_granted; |
704 |
|
|
} |
705 |
|
|
LIST_REMOVE(rfl, lcklst); |
706 |
|
|
|
707 |
|
|
/* process the next LKST_WAITING lock request for this fh */ |
708 |
|
|
LIST_FOREACH(fl, &lcklst_head, lcklst) { |
709 |
|
|
if (fl->status != LKST_WAITING || |
710 |
|
|
fhcmp(&rfl->filehandle, &fl->filehandle) != 0) |
711 |
|
|
continue; |
712 |
|
|
|
713 |
|
|
lockst = do_lock(fl, 1); /* If it's LKST_WAITING we can block */ |
714 |
|
|
switch (lockst) { |
715 |
|
|
case nlm4_granted: |
716 |
|
|
/* case nlm_granted: same as nlm4_granted */ |
717 |
|
|
send_granted(fl, (fl->flags & LOCK_V4) ? |
718 |
|
|
nlm4_granted : nlm_granted); |
719 |
|
|
break; |
720 |
|
|
case nlm4_blocked: |
721 |
|
|
/* case nlm_blocked: same as nlm4_blocked */ |
722 |
|
|
break; |
723 |
|
|
default: |
724 |
|
|
lfree(fl); |
725 |
|
|
break; |
726 |
|
|
} |
727 |
|
|
break; |
728 |
|
|
} |
729 |
|
|
lfree(rfl); |
730 |
|
|
return error; |
731 |
|
|
} |
732 |
|
|
|
733 |
|
|
void |
734 |
|
|
siglock(void) |
735 |
|
|
{ |
736 |
|
|
sigset_t block; |
737 |
|
|
|
738 |
|
|
sigemptyset(&block); |
739 |
|
|
sigaddset(&block, SIGCHLD); |
740 |
|
|
|
741 |
|
|
if (sigprocmask(SIG_BLOCK, &block, NULL) < 0) { |
742 |
|
|
syslog(LOG_WARNING, "siglock failed (%m)"); |
743 |
|
|
} |
744 |
|
|
} |
745 |
|
|
|
746 |
|
|
void |
747 |
|
|
sigunlock(void) |
748 |
|
|
{ |
749 |
|
|
sigset_t block; |
750 |
|
|
|
751 |
|
|
sigemptyset(&block); |
752 |
|
|
sigaddset(&block, SIGCHLD); |
753 |
|
|
|
754 |
|
|
if (sigprocmask(SIG_UNBLOCK, &block, NULL) < 0) { |
755 |
|
|
syslog(LOG_WARNING, "sigunlock failed (%m)"); |
756 |
|
|
} |
757 |
|
|
} |
758 |
|
|
|
759 |
|
|
/* monitor a host through rpc.statd, and keep a ref count */ |
760 |
|
|
void |
761 |
|
|
do_mon(const char *hostname) |
762 |
|
|
{ |
763 |
|
|
static char localhost[] = "localhost"; |
764 |
|
|
struct host *hp; |
765 |
|
|
struct mon my_mon; |
766 |
|
|
struct sm_stat_res result; |
767 |
|
|
int retval; |
768 |
|
|
|
769 |
|
|
LIST_FOREACH(hp, &hostlst_head, hostlst) { |
770 |
|
|
if (strcmp(hostname, hp->name) == 0) { |
771 |
|
|
/* already monitored, just bump refcnt */ |
772 |
|
|
hp->refcnt++; |
773 |
|
|
return; |
774 |
|
|
} |
775 |
|
|
} |
776 |
|
|
/* not found, have to create an entry for it */ |
777 |
|
|
hp = malloc(sizeof(struct host)); |
778 |
|
|
if (hp == NULL) { |
779 |
|
|
syslog(LOG_WARNING, "can't monitor host %s (%m)", hostname); |
780 |
|
|
return; |
781 |
|
|
} |
782 |
|
|
strlcpy(hp->name, hostname, sizeof(hp->name)); |
783 |
|
|
hp->refcnt = 1; |
784 |
|
|
syslog(LOG_DEBUG, "monitoring host %s", hostname); |
785 |
|
|
memset(&my_mon, 0, sizeof(my_mon)); |
786 |
|
|
my_mon.mon_id.mon_name = hp->name; |
787 |
|
|
my_mon.mon_id.my_id.my_name = localhost; |
788 |
|
|
my_mon.mon_id.my_id.my_prog = NLM_PROG; |
789 |
|
|
my_mon.mon_id.my_id.my_vers = NLM_SM; |
790 |
|
|
my_mon.mon_id.my_id.my_proc = NLM_SM_NOTIFY; |
791 |
|
|
if ((retval = callrpc(localhost, SM_PROG, SM_VERS, SM_MON, xdr_mon, |
792 |
|
|
(void *)&my_mon, xdr_sm_stat_res, (void *)&result)) != 0) { |
793 |
|
|
syslog(LOG_WARNING, "rpc to statd failed (%s)", |
794 |
|
|
clnt_sperrno((enum clnt_stat)retval)); |
795 |
|
|
free(hp); |
796 |
|
|
return; |
797 |
|
|
} |
798 |
|
|
if (result.res_stat == stat_fail) { |
799 |
|
|
syslog(LOG_WARNING, "statd failed"); |
800 |
|
|
free(hp); |
801 |
|
|
return; |
802 |
|
|
} |
803 |
|
|
LIST_INSERT_HEAD(&hostlst_head, hp, hostlst); |
804 |
|
|
} |
805 |
|
|
|
806 |
|
|
void |
807 |
|
|
notify(const char *hostname, int state) |
808 |
|
|
{ |
809 |
|
|
struct file_lock *fl, *next_fl; |
810 |
|
|
int err; |
811 |
|
|
syslog(LOG_DEBUG, "notify from %s, new state %d", hostname, state); |
812 |
|
|
/* search all lock for this host; if status changed, release the lock */ |
813 |
|
|
siglock(); |
814 |
|
|
for (fl = LIST_FIRST(&lcklst_head); fl != NULL; fl = next_fl) { |
815 |
|
|
next_fl = LIST_NEXT(fl, lcklst); |
816 |
|
|
if (strcmp(hostname, fl->client_name) == 0 && |
817 |
|
|
fl->nsm_status != state) { |
818 |
|
|
syslog(LOG_DEBUG, "state %d, nsm_state %d, unlocking", |
819 |
|
|
fl->status, fl->nsm_status); |
820 |
|
|
switch(fl->status) { |
821 |
|
|
case LKST_LOCKED: |
822 |
|
|
err = do_unlock(fl); |
823 |
|
|
if (err != nlm_granted) |
824 |
|
|
syslog(LOG_DEBUG, |
825 |
|
|
"notify: unlock failed for %s (%d)", |
826 |
|
|
hostname, err); |
827 |
|
|
break; |
828 |
|
|
case LKST_WAITING: |
829 |
|
|
LIST_REMOVE(fl, lcklst); |
830 |
|
|
lfree(fl); |
831 |
|
|
break; |
832 |
|
|
case LKST_PROCESSING: |
833 |
|
|
fl->status = LKST_DYING; |
834 |
|
|
break; |
835 |
|
|
case LKST_DYING: |
836 |
|
|
break; |
837 |
|
|
default: |
838 |
|
|
syslog(LOG_NOTICE, "unknow status %d for %s", |
839 |
|
|
fl->status, fl->client_name); |
840 |
|
|
} |
841 |
|
|
} |
842 |
|
|
} |
843 |
|
|
sigunlock(); |
844 |
|
|
} |