]>
Commit | Line | Data |
---|---|---|
ac2f15b3 A |
1 | /* $NetBSD: lockd.c,v 1.7 2000/08/12 18:08:44 thorpej Exp $ */ |
2 | /* $FreeBSD: src/usr.sbin/rpc.lockd/lockd.c,v 1.13 2002/04/11 07:19:30 alfred Exp $ */ | |
3 | ||
4 | /* | |
5 | * Copyright (c) 1995 | |
6 | * A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved. | |
7 | * | |
8 | * Redistribution and use in source and binary forms, with or without | |
9 | * modification, are permitted provided that the following conditions | |
10 | * are met: | |
11 | * 1. Redistributions of source code must retain the above copyright | |
12 | * notice, this list of conditions and the following disclaimer. | |
13 | * 2. Redistributions in binary form must reproduce the above copyright | |
14 | * notice, this list of conditions and the following disclaimer in the | |
15 | * documentation and/or other materials provided with the distribution. | |
16 | * 3. All advertising materials mentioning features or use of this software | |
17 | * must display the following acknowledgement: | |
18 | * This product includes software developed for the FreeBSD project | |
19 | * 4. Neither the name of the author nor the names of any co-contributors | |
20 | * may be used to endorse or promote products derived from this software | |
21 | * without specific prior written permission. | |
22 | * | |
23 | * THIS SOFTWARE IS PROVIDED BY ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND | |
24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
25 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
26 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
27 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
28 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
29 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
30 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
31 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
32 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
33 | * SUCH DAMAGE. | |
34 | * | |
35 | */ | |
36 | ||
37 | #include <sys/cdefs.h> | |
38 | #ifndef lint | |
39 | __RCSID("$NetBSD: lockd.c,v 1.7 2000/08/12 18:08:44 thorpej Exp $"); | |
40 | #endif | |
41 | ||
42 | /* | |
43 | * main() function for NFS lock daemon. Most of the code in this | |
44 | * file was generated by running rpcgen /usr/include/rpcsvc/nlm_prot.x. | |
45 | * | |
46 | * The actual program logic is in the file lock_proc.c | |
47 | */ | |
48 | ||
49 | #include <sys/types.h> | |
50 | #include <sys/socket.h> | |
51 | ||
52 | #include <err.h> | |
53 | #include <stdio.h> | |
54 | #include <stdlib.h> | |
55 | #include <errno.h> | |
56 | #include <syslog.h> | |
57 | #include <signal.h> | |
58 | #include <string.h> | |
59 | #include <unistd.h> | |
60 | #include <fcntl.h> | |
61 | #include <sys/stat.h> | |
ccaf7288 | 62 | #include <sys/resource.h> |
2b484d24 | 63 | #include <sys/sysctl.h> |
ac2f15b3 A |
64 | |
65 | #include <rpc/rpc.h> | |
66 | #include <rpc/pmap_clnt.h> | |
67 | #include <rpcsvc/sm_inter.h> | |
68 | ||
69 | #include "lockd.h" | |
70 | #include <rpcsvc/nlm_prot.h> | |
71 | ||
72 | int debug_level = 0; /* 0 = no debugging syslog() calls */ | |
73 | int _rpcsvcdirty = 0; | |
74 | int waitkern = 0; /* 1 = wait for kernel to say start */ | |
75 | ||
76 | const char *pid_file = NULL; | |
77 | ||
78 | int grace_expired; | |
79 | int nsm_state; | |
80 | pid_t client_pid = -1; | |
81 | struct mon mon_host; | |
82 | ||
83 | void init_nsm(void); | |
84 | void nlm_prog_0(struct svc_req *, SVCXPRT *); | |
85 | void nlm_prog_1(struct svc_req *, SVCXPRT *); | |
86 | void nlm_prog_3(struct svc_req *, SVCXPRT *); | |
87 | void nlm_prog_4(struct svc_req *, SVCXPRT *); | |
88 | void usage(void); | |
89 | ||
90 | int claim_pid_file(const char *, int); | |
91 | void cleanup_pid_file(void); | |
92 | void handle_sig_cleanup(int); | |
93 | ||
94 | void sigalarm_handler(void); | |
7902cf7e | 95 | void my_svc_run(void); |
ac2f15b3 A |
96 | |
97 | const char *transports[] = { "udp", "tcp", "udp6", "tcp6" }; | |
98 | ||
99 | int | |
100 | main(argc, argv) | |
101 | int argc; | |
102 | char **argv; | |
103 | { | |
104 | SVCXPRT *transp; | |
105 | int ch; | |
106 | struct sigaction sigalarm; | |
107 | int grace_period = 30; | |
ccaf7288 | 108 | struct rlimit rlp; |
2b484d24 A |
109 | int mib[6]; |
110 | int oldstate; | |
111 | int oldsize; | |
112 | int newstate; | |
ac2f15b3 | 113 | |
7902cf7e | 114 | while ((ch = getopt(argc, argv, "d:g:wx:")) != (-1)) { |
ac2f15b3 A |
115 | switch (ch) { |
116 | case 'd': | |
117 | debug_level = atoi(optarg); | |
118 | if (!debug_level) { | |
119 | usage(); | |
120 | /* NOTREACHED */ | |
121 | } | |
122 | break; | |
123 | case 'g': | |
124 | grace_period = atoi(optarg); | |
125 | if (!grace_period) { | |
126 | usage(); | |
127 | /* NOTREACHED */ | |
128 | } | |
129 | break; | |
130 | case 'w': | |
131 | waitkern = 1; | |
132 | break; | |
7902cf7e A |
133 | case 'x': |
134 | host_expire = atoi(optarg); | |
135 | break; | |
ac2f15b3 A |
136 | default: |
137 | case '?': | |
138 | usage(); | |
139 | /* NOTREACHED */ | |
140 | } | |
141 | } | |
142 | if (geteuid()) { /* This command allowed only to root */ | |
143 | fprintf(stderr, "Sorry. You are not superuser\n"); | |
144 | exit(1); | |
145 | } | |
146 | ||
147 | /* | |
148 | * Note that it is NOT sensible to run this program from inetd - the | |
149 | * protocol assumes that it will run immediately at boot time. | |
150 | */ | |
ccaf7288 | 151 | if (debug_level != 99 && daemon(0, debug_level > 0)) { |
ac2f15b3 A |
152 | err(1, "cannot fork"); |
153 | /* NOTREACHED */ | |
154 | } | |
155 | ||
156 | /* Install signal handler to remove any pid file */ | |
157 | signal(SIGINT, handle_sig_cleanup); | |
158 | signal(SIGTERM, handle_sig_cleanup); | |
159 | signal(SIGHUP, handle_sig_cleanup); | |
160 | signal(SIGQUIT, handle_sig_cleanup); | |
161 | ||
2b484d24 A |
162 | |
163 | ||
ccaf7288 A |
164 | openlog("rpc.lockd", debug_level == 99 ? LOG_PERROR : 0, LOG_DAEMON); |
165 | ||
2b484d24 A |
166 | mib[0] = CTL_KERN; |
167 | mib[1] = KERN_PROCDELAYTERM; | |
168 | ||
169 | oldstate = 0; | |
170 | oldsize = 4; | |
171 | newstate = 1; | |
172 | ||
173 | if (sysctl(mib, 2, &oldstate, &oldsize, &newstate, 4) < 0) { | |
174 | syslog(LOG_INFO, "cannot mark pid for delayed termination"); | |
175 | } | |
176 | ||
ccaf7288 A |
177 | if (claim_pid_file("/var/run/lockd.pid", 0) < 0) { |
178 | syslog(LOG_ERR, "cannot claim pid file"); | |
179 | exit(1); | |
180 | } | |
ac2f15b3 A |
181 | |
182 | if (waitkern) { | |
183 | struct timespec ts; | |
184 | /* wait for kernel to get first lock request */ | |
185 | client_kern_wait(); | |
186 | /* start statd now, in case it isn't already */ | |
187 | system("rpc.statd"); | |
188 | /* sleep a little to give statd/portmap a chance to start */ | |
189 | /* (better to sleep 100ms than to timeout on portmap calls) */ | |
190 | ts.tv_sec = 0; | |
191 | ts.tv_nsec = 100*1000*1000; | |
192 | nanosleep(&ts, NULL); | |
193 | } | |
194 | ||
ac2f15b3 A |
195 | if (debug_level) |
196 | syslog(LOG_INFO, "Starting, debug level %d", debug_level); | |
197 | else | |
198 | syslog(LOG_INFO, "Starting"); | |
199 | ||
200 | (void)pmap_unset(NLM_PROG, NLM_SM); | |
201 | (void)pmap_unset(NLM_PROG, NLM_VERS); | |
202 | (void)pmap_unset(NLM_PROG, NLM_VERSX); | |
203 | (void)pmap_unset(NLM_PROG, NLM_VERS4); | |
204 | ||
205 | transp = svcudp_create(RPC_ANYSOCK); | |
ccaf7288 A |
206 | if (transp == NULL) { |
207 | syslog(LOG_ERR, "cannot create udp service"); | |
208 | exit(1); | |
209 | } | |
210 | if (!svc_register(transp, NLM_PROG, NLM_SM, nlm_prog_0, IPPROTO_UDP)) { | |
211 | syslog(LOG_ERR, "unable to register (NLM_PROG, NLM_SM, udp)"); | |
212 | exit(1); | |
213 | } | |
214 | if (!svc_register(transp, NLM_PROG, NLM_VERS, nlm_prog_1, IPPROTO_UDP)) { | |
215 | syslog(LOG_ERR, "unable to register (NLM_PROG, NLM_VERS, udp)"); | |
216 | exit(1); | |
217 | } | |
218 | if (!svc_register(transp, NLM_PROG, NLM_VERSX, nlm_prog_3, IPPROTO_UDP)) { | |
219 | syslog(LOG_ERR, "unable to register (NLM_PROG, NLM_VERSX, udp)"); | |
220 | exit(1); | |
221 | } | |
222 | if (!svc_register(transp, NLM_PROG, NLM_VERS4, nlm_prog_4, IPPROTO_UDP)) { | |
223 | syslog(LOG_ERR, "unable to register (NLM_PROG, NLM_VERS4, udp)"); | |
224 | exit(1); | |
225 | } | |
ac2f15b3 A |
226 | |
227 | transp = svctcp_create(RPC_ANYSOCK, 0, 0); | |
ccaf7288 A |
228 | if (transp == NULL) { |
229 | syslog(LOG_ERR, "cannot create tcp service"); | |
230 | exit(1); | |
231 | } | |
232 | if (!svc_register(transp, NLM_PROG, NLM_SM, nlm_prog_0, IPPROTO_TCP)) { | |
233 | syslog(LOG_ERR, "unable to register (NLM_PROG, NLM_SM, tcp)"); | |
234 | exit(1); | |
235 | } | |
236 | if (!svc_register(transp, NLM_PROG, NLM_VERS, nlm_prog_1, IPPROTO_TCP)) { | |
237 | syslog(LOG_ERR, "unable to register (NLM_PROG, NLM_VERS, tcp)"); | |
238 | exit(1); | |
239 | } | |
240 | if (!svc_register(transp, NLM_PROG, NLM_VERSX, nlm_prog_3, IPPROTO_TCP)) { | |
241 | syslog(LOG_ERR, "unable to register (NLM_PROG, NLM_VERSX, tcp)"); | |
242 | exit(1); | |
243 | } | |
244 | if (!svc_register(transp, NLM_PROG, NLM_VERS4, nlm_prog_4, IPPROTO_TCP)) { | |
245 | syslog(LOG_ERR, "unable to register (NLM_PROG, NLM_VERS4, tcp)"); | |
246 | exit(1); | |
247 | } | |
ac2f15b3 A |
248 | |
249 | sigalarm.sa_handler = (sig_t) sigalarm_handler; | |
250 | sigemptyset(&sigalarm.sa_mask); | |
251 | sigalarm.sa_flags = SA_RESETHAND; /* should only happen once */ | |
252 | sigalarm.sa_flags |= SA_RESTART; | |
253 | if (sigaction(SIGALRM, &sigalarm, NULL) != 0) { | |
254 | syslog(LOG_WARNING, "sigaction(SIGALRM) failed: %s", | |
255 | strerror(errno)); | |
256 | exit(1); | |
257 | } | |
258 | grace_expired = 0; | |
259 | alarm(grace_period); | |
260 | ||
261 | init_nsm(); | |
262 | ||
263 | client_pid = client_request(); | |
264 | ||
ccaf7288 A |
265 | /* raise our resource limits as far as they can go */ |
266 | if (getrlimit(RLIMIT_NOFILE, &rlp)) { | |
267 | syslog(LOG_WARNING, "getrlimit(RLIMIT_NOFILE) failed: %s", | |
268 | strerror(errno)); | |
269 | } else { | |
270 | rlp.rlim_cur = rlp.rlim_max; | |
271 | if (setrlimit(RLIMIT_NOFILE, &rlp)) { | |
272 | syslog(LOG_WARNING, "setrlimit(RLIMIT_NOFILE) failed: %s", | |
273 | strerror(errno)); | |
274 | } | |
275 | } | |
276 | ||
7902cf7e | 277 | my_svc_run(); /* Should never return */ |
ac2f15b3 A |
278 | exit(1); |
279 | } | |
280 | ||
281 | void | |
282 | sigalarm_handler(void) | |
283 | { | |
284 | ||
285 | grace_expired = 1; | |
286 | } | |
287 | ||
288 | void | |
289 | usage() | |
290 | { | |
7902cf7e A |
291 | errx(1, "usage: rpc.lockd [-d <debuglevel>] [-g <grace period>] " |
292 | " [-x <statd cache timeout>] [-w]"); | |
ac2f15b3 A |
293 | } |
294 | ||
295 | /* | |
296 | * init_nsm -- | |
297 | * Reset the NSM state-of-the-world and acquire its state. | |
298 | */ | |
299 | void | |
300 | init_nsm(void) | |
301 | { | |
302 | enum clnt_stat ret; | |
303 | my_id id; | |
304 | sm_stat stat; | |
305 | char name[] = "NFS NLM"; | |
306 | char localhost[] = "localhost"; | |
307 | int attempt = 0; | |
308 | ||
309 | /* | |
310 | * !!! | |
311 | * The my_id structure isn't used by the SM_UNMON_ALL call, as far | |
312 | * as I know. Leave it empty for now. | |
313 | */ | |
314 | memset(&id, 0, sizeof(id)); | |
315 | id.my_name = name; | |
316 | ||
317 | /* | |
318 | * !!! | |
319 | * The statd program must already be registered when lockd runs. | |
320 | * If we have a problem contacting statd, pause and try again a | |
321 | * number of times in case statd is just slow in coming up. | |
322 | */ | |
323 | do { | |
324 | ret = callrpc("localhost", SM_PROG, SM_VERS, SM_UNMON_ALL, | |
325 | xdr_my_id, &id, xdr_sm_stat, &stat); | |
326 | if (ret) { | |
ccaf7288 | 327 | syslog(LOG_WARNING, "%lu %s", SM_PROG, clnt_sperrno(ret)); |
ac2f15b3 A |
328 | if (++attempt < 20) { |
329 | sleep(attempt); | |
330 | continue; | |
331 | } | |
332 | } | |
333 | break; | |
334 | } while (1); | |
335 | ||
336 | if (ret != 0) { | |
ccaf7288 A |
337 | syslog(LOG_ERR, "%lu %s", SM_PROG, clnt_sperrno(ret)); |
338 | exit(1); | |
ac2f15b3 A |
339 | } |
340 | ||
341 | nsm_state = stat.state; | |
342 | ||
343 | /* setup constant data for SM_MON calls */ | |
344 | mon_host.mon_id.my_id.my_name = localhost; | |
345 | mon_host.mon_id.my_id.my_prog = NLM_PROG; | |
346 | mon_host.mon_id.my_id.my_vers = NLM_SM; | |
347 | mon_host.mon_id.my_id.my_proc = NLM_SM_NOTIFY; /* bsdi addition */ | |
348 | } | |
349 | ||
350 | /* | |
351 | * claim_pid_file | |
352 | * | |
353 | * Purpose: take ownership of and store pid in given pid_file | |
354 | * Returns: 0 on success or -1 on failure | |
355 | * Notes: force parameter requests that current owner (if any) of | |
356 | * pid file be terminated. | |
357 | */ | |
358 | int | |
359 | claim_pid_file(const char *name, int force) | |
360 | { | |
361 | int pidfd, rv, retried = 0; | |
362 | FILE *pidfile; | |
363 | ||
364 | try_again: | |
365 | ||
366 | /* attempt exclusive open of pid file */ | |
367 | pidfd = open(name, O_EXCL|O_CREAT|O_WRONLY, | |
368 | S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); | |
369 | if (pidfd < 0) { | |
370 | char buf[16]; | |
371 | pid_t pid; | |
372 | if (retried) | |
373 | return -1; | |
374 | bzero(buf, 16); | |
375 | retried = 1; | |
376 | /* pid file busy, check validity */ | |
377 | pidfd = open(name, O_RDONLY); | |
378 | if (pidfd < 0) | |
379 | goto try_again; | |
380 | rv = read(pidfd, buf, 15); | |
381 | close(pidfd); | |
382 | if (rv <= 0) | |
383 | goto try_again; | |
384 | pid = atoi(buf); | |
385 | if (pid <= 0) | |
386 | goto try_again; | |
387 | rv = kill(pid, force ? SIGKILL : 0); | |
388 | /* if can't signal, assume stale pid file */ | |
389 | if ((rv < 0) || force) | |
390 | unlink(name); | |
391 | goto try_again; | |
392 | } | |
393 | pid_file = name; | |
394 | atexit(cleanup_pid_file); | |
395 | ||
396 | pidfile = fdopen(pidfd, "w"); | |
397 | if (pidfile) { | |
398 | fchmod(pidfd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); | |
399 | fprintf(pidfile, "%d\n", getpid()); | |
400 | fclose(pidfile); | |
401 | } else | |
402 | perror("fdopen"); | |
403 | close(pidfd); | |
404 | return 0; | |
405 | } | |
406 | ||
407 | /* | |
408 | * cleanup_pid_file | |
409 | * | |
410 | * Purpose: delete any pid_file that has been claimed | |
411 | * Returns: Nothing | |
412 | */ | |
413 | void | |
414 | cleanup_pid_file(void) | |
415 | { | |
416 | if (pid_file) { | |
417 | unlink(pid_file); | |
418 | pid_file = NULL; | |
419 | } | |
420 | } | |
421 | ||
422 | /* | |
423 | * handle_sig_cleanup | |
424 | * | |
425 | * Purpose: on signal, kill client child and do pid file cleanup | |
426 | * Returns: Nothing | |
427 | */ | |
428 | void | |
429 | handle_sig_cleanup(int sig __unused) | |
430 | { | |
431 | if (client_pid != -1) | |
432 | kill(client_pid, SIGTERM); | |
433 | cleanup_pid_file(); | |
434 | exit(1); | |
435 | } | |
436 | ||
7902cf7e A |
437 | void |
438 | my_svc_run(void) | |
439 | { | |
440 | fd_set readfds; | |
441 | struct timeval timeout; | |
442 | struct timeval now; | |
443 | int error; | |
444 | int hashosts = 0; | |
445 | int tsize = 0; | |
446 | struct timeval *top; | |
447 | ||
448 | ||
449 | for( ;; ) { | |
450 | timeout.tv_sec = host_expire + 1; | |
451 | timeout.tv_usec = 0; | |
452 | ||
453 | tsize = getdtablesize(); | |
454 | bcopy(&svc_fdset, &readfds, sizeof(svc_fdset)); | |
455 | /* | |
456 | * If there are any expired hosts then sleep with a | |
457 | * timeout to expire them. | |
458 | */ | |
459 | if (hashosts && (timeout.tv_sec >= 0)) | |
460 | top = &timeout; | |
461 | else | |
462 | top = NULL; | |
463 | error = select(tsize, &readfds, NULL, NULL, top); | |
464 | if (error == -1) { | |
465 | if (errno == EINTR) | |
466 | continue; | |
467 | perror("rpc.lockd: my_svc_run: select failed"); | |
468 | return; | |
469 | } | |
470 | gettimeofday(&now, NULL); | |
471 | currsec = now.tv_sec; | |
472 | if (error > 0) | |
473 | svc_getreqset(&readfds); | |
474 | if (debug_level > 3 && error == 0) | |
475 | fprintf(stderr, "my_svc_run: select timeout\n"); | |
476 | hashosts = expire_lock_hosts(); | |
477 | } | |
478 | } |