]> git.saurik.com Git - apple/network_cmds.git/blame - rpc_lockd.tproj/lockd.c
network_cmds-245.tar.gz
[apple/network_cmds.git] / rpc_lockd.tproj / lockd.c
CommitLineData
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
72int debug_level = 0; /* 0 = no debugging syslog() calls */
73int _rpcsvcdirty = 0;
74int waitkern = 0; /* 1 = wait for kernel to say start */
75
76const char *pid_file = NULL;
77
78int grace_expired;
79int nsm_state;
80pid_t client_pid = -1;
81struct mon mon_host;
82
83void init_nsm(void);
84void nlm_prog_0(struct svc_req *, SVCXPRT *);
85void nlm_prog_1(struct svc_req *, SVCXPRT *);
86void nlm_prog_3(struct svc_req *, SVCXPRT *);
87void nlm_prog_4(struct svc_req *, SVCXPRT *);
88void usage(void);
89
90int claim_pid_file(const char *, int);
91void cleanup_pid_file(void);
92void handle_sig_cleanup(int);
93
94void sigalarm_handler(void);
7902cf7e 95void my_svc_run(void);
ac2f15b3
A
96
97const char *transports[] = { "udp", "tcp", "udp6", "tcp6" };
98
99int
100main(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
281void
282sigalarm_handler(void)
283{
284
285 grace_expired = 1;
286}
287
288void
289usage()
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 */
299void
300init_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 */
358int
359claim_pid_file(const char *name, int force)
360{
361 int pidfd, rv, retried = 0;
362 FILE *pidfile;
363
364try_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 */
413void
414cleanup_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 */
428void
429handle_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
437void
438my_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}