]> git.saurik.com Git - apple/network_cmds.git/blob - rpc_lockd.tproj/lockd.c
07a1c90922cd8a24c1722312c046cb89dd82346b
[apple/network_cmds.git] / rpc_lockd.tproj / lockd.c
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>
62 #include <sys/resource.h>
63 #include <sys/sysctl.h>
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);
95 void my_svc_run(void);
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;
108 struct rlimit rlp;
109 int mib[6];
110 int oldstate;
111 int oldsize;
112 int newstate;
113
114 while ((ch = getopt(argc, argv, "d:g:wx:")) != (-1)) {
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;
133 case 'x':
134 host_expire = atoi(optarg);
135 break;
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 */
151 if (debug_level != 99 && daemon(0, debug_level > 0)) {
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
162
163
164 openlog("rpc.lockd", debug_level == 99 ? LOG_PERROR : 0, LOG_DAEMON);
165
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
177 if (claim_pid_file("/var/run/lockd.pid", 0) < 0) {
178 syslog(LOG_ERR, "cannot claim pid file");
179 exit(1);
180 }
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
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);
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 }
226
227 transp = svctcp_create(RPC_ANYSOCK, 0, 0);
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 }
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
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
277 my_svc_run(); /* Should never return */
278 exit(1);
279 }
280
281 void
282 sigalarm_handler(void)
283 {
284
285 grace_expired = 1;
286 }
287
288 void
289 usage()
290 {
291 errx(1, "usage: rpc.lockd [-d <debuglevel>] [-g <grace period>] "
292 " [-x <statd cache timeout>] [-w]");
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) {
327 syslog(LOG_WARNING, "%lu %s", SM_PROG, clnt_sperrno(ret));
328 if (++attempt < 20) {
329 sleep(attempt);
330 continue;
331 }
332 }
333 break;
334 } while (1);
335
336 if (ret != 0) {
337 syslog(LOG_ERR, "%lu %s", SM_PROG, clnt_sperrno(ret));
338 exit(1);
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
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 }