kernel - Reduce lwp_signotify() latency
[dragonfly.git] / usr.sbin / rpc.lockd / lockd.c
1 /*
2  * Copyright (c) 1995
3  *      A.R. Gordon (andrew.gordon@net-tel.co.uk).  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  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed for the FreeBSD project
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $NetBSD: lockd.c,v 1.7 2000/08/12 18:08:44 thorpej Exp $
33  * $FreeBSD: src/usr.sbin/rpc.lockd/lockd.c,v 1.6 2001/03/19 12:50:09 alfred Exp $
34  * $DragonFly: src/usr.sbin/rpc.lockd/lockd.c,v 1.5 2005/11/25 00:32:49 swildner Exp $
35  */
36
37 /*
38  * main() function for NFS lock daemon.  Most of the code in this
39  * file was generated by running rpcgen /usr/include/rpcsvc/nlm_prot.x.
40  *
41  * The actual program logic is in the file lock_proc.c
42  */
43
44 #include <sys/types.h>
45 #include <sys/socket.h>
46
47 #include <netinet/in.h>
48 #include <arpa/inet.h>
49
50 #include <err.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <errno.h>
54 #include <syslog.h>
55 #include <signal.h>
56 #include <string.h>
57 #include <unistd.h>
58 #include <libutil.h>
59 #include <netconfig.h>
60 #include <netdb.h>
61
62 #include <rpc/rpc.h>
63 #include <rpc/rpc_com.h>
64 #include <rpcsvc/sm_inter.h>
65
66 #include "lockd.h"
67 #include <rpcsvc/nlm_prot.h>
68
69 int             debug_level = 0;        /* 0 = no debugging syslog() calls */
70 int             _rpcsvcdirty = 0;
71
72 int grace_expired;
73 char **hosts, *svcport_str = NULL;
74 int nhosts = 0;
75 int xcreated = 0;
76
77 #ifndef IPPORT_MAX
78 #define IPPORT_MAX      65535
79 #endif
80
81 void    create_service(struct netconfig *);
82
83 void    nlm_prog_0(struct svc_req *, SVCXPRT *);
84 void    nlm_prog_1(struct svc_req *, SVCXPRT *);
85 void    nlm_prog_3(struct svc_req *, SVCXPRT *);
86 void    nlm_prog_4(struct svc_req *, SVCXPRT *);
87 void    out_of_mem(void);
88
89 static void     usage(void);
90
91 void    sigalarm_handler(void);
92
93 int
94 main(int argc, char **argv)
95 {
96         int ch, i, s;
97         void *nc_handle;
98         char *endptr, **hosts_bak;
99         struct sigaction sigalarm;
100         int grace_period = 30;
101         struct netconfig *nconf;
102         int have_v6 = 1;
103         int maxrec = RPC_MAXDATASIZE;
104         in_port_t svcport = 0;
105
106         while ((ch = getopt(argc, argv, "d:g:h:p:")) != (-1)) {
107                 switch (ch) {
108                 case 'd':
109                         debug_level = atoi(optarg);
110                         if (!debug_level) {
111                                 usage();
112                                 /* NOTREACHED */
113                         }
114                         break;
115                 case 'g':
116                         grace_period = atoi(optarg);
117                         if (!grace_period) {
118                                 usage();
119                                 /* NOTREACHED */
120                         }
121                         break;
122                 case 'h':
123                         ++nhosts;
124                         hosts_bak = hosts;
125                         hosts_bak = realloc(hosts, nhosts * sizeof(char *));
126                         if (hosts_bak == NULL) {
127                                 if (hosts != NULL) {
128                                         for (i = 0; i < nhosts; i++)
129                                                 free(hosts[i]);
130                                         free(hosts);
131                                         out_of_mem();
132                                 }
133                         }
134                         hosts = hosts_bak;
135                         hosts[nhosts - 1] = strdup(optarg);
136                         if (hosts[nhosts - 1] == NULL) {
137                                 for (i = 0; i < (nhosts - 1); i++)
138                                         free(hosts[i]);
139                                 free(hosts);
140                                 out_of_mem();
141                         }
142                         break;
143                 case 'p':
144                         endptr = NULL;
145                         svcport = (in_port_t)strtoul(optarg, &endptr, 10);
146                         if (endptr == NULL || *endptr != '\0' ||
147                             svcport == 0 || svcport >= IPPORT_MAX)
148                                 usage();
149                         svcport_str = strdup(optarg);
150                         break;
151                 default:
152                 case '?':
153                         usage();
154                         /* NOTREACHED */
155                 }
156         }
157         if (geteuid()) { /* This command allowed only to root */
158                 fprintf(stderr, "Sorry. You are not superuser\n");
159                 exit(1);
160         }
161
162         rpcb_unset(NLM_PROG, NLM_SM, NULL);
163         rpcb_unset(NLM_PROG, NLM_VERS, NULL);
164         rpcb_unset(NLM_PROG, NLM_VERSX, NULL);
165         rpcb_unset(NLM_PROG, NLM_VERS4, NULL);
166
167         /*
168          * Check if IPv6 support is present.
169          */
170         s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
171         if (s < 0)
172                 have_v6 = 0;
173         else
174                 close(s);
175
176         rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
177
178         /*
179          * If no hosts were specified, add a wildcard entry to bind to
180          * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added to the
181          * list.
182          */
183         if (nhosts == 0) {
184                 hosts = malloc(sizeof(char**));
185                 if (hosts == NULL)
186                         out_of_mem();
187
188                 hosts[0] = "*";
189                 nhosts = 1;
190         } else {
191                 hosts_bak = hosts;
192                 if (have_v6) {
193                         hosts_bak = realloc(hosts, (nhosts + 2) *
194                             sizeof(char *));
195                         if (hosts_bak == NULL) {
196                                 for (i = 0; i < nhosts; i++)
197                                         free(hosts[i]);
198                                 free(hosts);
199                                 out_of_mem();
200                         } else
201                                 hosts = hosts_bak;
202
203                         nhosts += 2;
204                         hosts[nhosts - 2] = "::1";
205                 } else {
206                         hosts_bak = realloc(hosts, (nhosts + 1) * sizeof(char *));
207                         if (hosts_bak == NULL) {
208                                 for (i = 0; i < nhosts; i++)
209                                         free(hosts[i]);
210
211                                 free(hosts);
212                                 out_of_mem();
213                         } else {
214                                 nhosts += 1;
215                                 hosts = hosts_bak;
216                         }
217                 }
218                 hosts[nhosts - 1] = "127.0.0.1";
219         }
220
221         nc_handle = setnetconfig();
222         while ((nconf = getnetconfig(nc_handle))) {
223                 /* We want to listen only on udp6, tcp6, udp, tcp transports */
224                 if (nconf->nc_flag & NC_VISIBLE) {
225                         /* Skip if there's no IPv6 support */
226                         if (have_v6 == 0 && strcmp(nconf->nc_protofmly, "inet6") == 0) {
227                                 /* DO NOTHING */
228                         } else {
229                                 create_service(nconf);
230                         }
231                 }
232         }
233         endnetconfig(nc_handle);
234
235         /*
236          * Note that it is NOT sensible to run this program from inetd - the
237          * protocol assumes that it will run immediately at boot time.
238          */
239         if (daemon(0, debug_level > 0)) {
240                 err(1, "cannot fork");
241                 /* NOTREACHED */
242         }
243
244         openlog("rpc.lockd", 0, LOG_DAEMON);
245         if (debug_level)
246                 syslog(LOG_INFO, "Starting, debug level %d", debug_level);
247         else
248                 syslog(LOG_INFO, "Starting");
249
250         sigalarm.sa_handler = (sig_t) sigalarm_handler;
251         sigemptyset(&sigalarm.sa_mask);
252         sigalarm.sa_flags = SA_RESETHAND; /* should only happen once */
253         sigalarm.sa_flags |= SA_RESTART;
254         if (sigaction(SIGALRM, &sigalarm, NULL) != 0) {
255                 syslog(LOG_WARNING, "sigaction(SIGALRM) failed: %s",
256                     strerror(errno));
257                 exit(1);
258         }
259         grace_expired = 0;
260         alarm(grace_period);
261
262         svc_run();              /* Should never return */
263         exit(1);
264 }
265
266 /*
267  * This routine creates and binds sockets on the appropriate
268  * addresses. It gets called one time for each transport and
269  * registrates the service with rpcbind on that trasport.
270  */
271 void
272 create_service(struct netconfig *nconf)
273 {
274         struct addrinfo hints, *res = NULL;
275         struct sockaddr_in *sin;
276         struct sockaddr_in6 *sin6;
277         struct __rpc_sockinfo si;
278         struct netbuf servaddr;
279         SVCXPRT *transp = NULL;
280         int aicode;
281         int fd;
282         int nhostsbak;
283         int r;
284         int registered = 0;
285         u_int32_t host_addr[4];  /* IPv4 or IPv6 */
286
287         if ((nconf->nc_semantics != NC_TPI_CLTS) &&
288             (nconf->nc_semantics != NC_TPI_COTS) &&
289             (nconf->nc_semantics != NC_TPI_COTS_ORD))
290                 return; /* not my type */
291
292         /*
293          * XXX - using RPC library internal functions.
294          */
295         if (!__rpc_nconf2sockinfo(nconf, &si)) {
296                 syslog(LOG_ERR, "cannot get information for %s",
297                     nconf->nc_netid);
298                 return;
299         }
300
301         /* Get rpc.statd's address on this transport */
302         memset(&hints, 0, sizeof hints);
303         hints.ai_flags = AI_PASSIVE;
304         hints.ai_family = si.si_af;
305         hints.ai_socktype = si.si_socktype;
306         hints.ai_protocol = si.si_proto;
307
308         /*
309          * Bind to specific IPs if asked to
310          */
311         nhostsbak = nhosts;
312         while (nhostsbak > 0) {
313                 --nhostsbak;
314
315                 /*
316                  * XXX - using RPC library internal functions.
317                  */
318                 if ((fd = __rpc_nconf2fd(nconf)) < 0) {
319                         syslog(LOG_ERR, "cannot create socket for %s",
320                             nconf->nc_netid);
321                         continue;
322                 }
323
324                 switch (hints.ai_family) {
325                         case AF_INET:
326                                 if (inet_pton(AF_INET, hosts[nhostsbak],
327                                     host_addr) == 1) {
328                                         hints.ai_flags &= AI_NUMERICHOST;
329                                 } else {
330                                         /*
331                                          * Skip if we have an AF_INET6 address.
332                                          */
333                                         if (inet_pton(AF_INET6, hosts[nhostsbak],
334                                             host_addr) == 1) {
335                                                 close(fd);
336                                                 continue;
337                                         }
338                                 }
339                                 break;
340                         case AF_INET6:
341                                 if (inet_pton(AF_INET6, hosts[nhostsbak],
342                                     host_addr) == 1) {
343                                         hints.ai_flags &= AI_NUMERICHOST;
344                                 } else {
345                                         /*
346                                          * Skip if we have an AF_INET address.
347                                          */
348                                         if (inet_pton(AF_INET, hosts[nhostsbak],
349                                             host_addr) == 1) {
350                                                 close(fd);
351                                                 continue;
352                                         }
353                                 }
354                                 break;
355                         default:
356                                 break;
357                 }
358
359                 /*
360                  * If no hosts were specified, just bind to INADDR_ANY
361                  */
362                 if (strcmp("*", hosts[nhostsbak]) == 0) {
363                         if (svcport_str == NULL) {
364                                 res = malloc(sizeof(struct addrinfo));
365                                 if (res == NULL)
366                                         out_of_mem();
367                                 res->ai_flags = hints.ai_flags;
368                                 res->ai_family = hints.ai_family;
369                                 res->ai_protocol = hints.ai_protocol;
370                                 switch (res->ai_family) {
371                                         case AF_INET:
372                                                 sin = malloc(sizeof(struct sockaddr_in));
373                                                 if (sin == NULL)
374                                                         out_of_mem();
375                                                 sin->sin_family = AF_INET;
376                                                 sin->sin_port = htons(0);
377                                                 sin->sin_addr.s_addr = htonl(INADDR_ANY);
378                                                 res->ai_addr = (struct sockaddr*) sin;
379                                                 res->ai_addrlen = (socklen_t)
380                                                     sizeof(res->ai_addr);
381                                                 break;
382                                         case AF_INET6:
383                                                 sin6 = malloc(sizeof(struct sockaddr_in6));
384                                                 if (sin6 == NULL)
385                                                         out_of_mem();
386                                                 sin6->sin6_family = AF_INET6;
387                                                 sin6->sin6_port = htons(0);
388                                                 sin6->sin6_addr = in6addr_any;
389                                                 res->ai_addr = (struct sockaddr*) sin6;
390                                                 res->ai_addrlen = (socklen_t) sizeof(res->ai_addr);
391                                                 break;
392                                         default:
393                                                 break;
394                                 }
395                         } else {
396                                 if ((aicode = getaddrinfo(NULL, svcport_str,
397                                     &hints, &res)) != 0) {
398                                         syslog(LOG_ERR,
399                                             "cannot get local address for %s: %s",
400                                             nconf->nc_netid,
401                                             gai_strerror(aicode));
402                                         continue;
403                                 }
404                         }
405                 } else {
406                         if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str,
407                             &hints, &res)) != 0) {
408                                 syslog(LOG_ERR,
409                                     "cannot get local address for %s: %s",
410                                     nconf->nc_netid, gai_strerror(aicode));
411                                 continue;
412                         }
413                 }
414
415                 r = bindresvport_sa(fd, res->ai_addr);
416                 if (r != 0) {
417                         syslog(LOG_ERR, "bindresvport_sa: %m");
418                         exit(1);
419                 }
420
421                 transp = svc_tli_create(fd, nconf, NULL,
422                     RPC_MAXDATASIZE, RPC_MAXDATASIZE);
423
424                 if (transp != NULL) {
425                         if (!svc_reg(transp, NLM_PROG, NLM_SM, nlm_prog_0,
426                             NULL))
427                                 syslog(LOG_ERR,
428                                     "can't register %s NLM_PROG, NLM_SM service",
429                                     nconf->nc_netid);
430
431                         if (!svc_reg(transp, NLM_PROG, NLM_VERS, nlm_prog_1,
432                             NULL))
433                                 syslog(LOG_ERR,
434                                     "can't register %s NLM_PROG, NLM_VERS service",
435                                     nconf->nc_netid);
436
437                         if (!svc_reg(transp, NLM_PROG, NLM_VERSX, nlm_prog_3,
438                             NULL))
439                                 syslog(LOG_ERR,
440                                     "can't register %s NLM_PROG, NLM_VERSX service",
441                                     nconf->nc_netid);
442
443                         if (!svc_reg(transp, NLM_PROG, NLM_VERS4, nlm_prog_4,
444                             NULL))
445                                 syslog(LOG_ERR,
446                                     "can't register %s NLM_PROG, NLM_VERS4 service",
447                                     nconf->nc_netid);
448
449                 } else
450                         syslog(LOG_WARNING, "can't create %s services",
451                             nconf->nc_netid);
452
453                 if (registered == 0) {
454                         registered = 1;
455                         memset(&hints, 0, sizeof hints);
456                         hints.ai_flags = AI_PASSIVE;
457                         hints.ai_family = si.si_af;
458                         hints.ai_socktype = si.si_socktype;
459                         hints.ai_protocol = si.si_proto;
460
461                         if (svcport_str == NULL) {
462                                 svcport_str = malloc(NI_MAXSERV * sizeof(char));
463                                 if (svcport_str == NULL)
464                                         out_of_mem();
465
466                                 if (getnameinfo(res->ai_addr,
467                                     res->ai_addr->sa_len, NULL, NI_MAXHOST,
468                                     svcport_str, NI_MAXSERV * sizeof(char),
469                                     NI_NUMERICHOST | NI_NUMERICSERV))
470                                         errx(1, "Cannot get port number");
471                         }
472
473                         if((aicode = getaddrinfo(NULL, svcport_str, &hints,
474                             &res)) != 0) {
475                                 syslog(LOG_ERR, "cannot get local address: %s",
476                                     gai_strerror(aicode));
477                                 exit(1);
478                         }
479
480                         servaddr.buf = malloc(res->ai_addrlen);
481                         memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen);
482                         servaddr.len = res->ai_addrlen;
483
484                         rpcb_set(NLM_PROG, NLM_SM, nconf, &servaddr);
485                         rpcb_set(NLM_PROG, NLM_VERS, nconf, &servaddr);
486                         rpcb_set(NLM_PROG, NLM_VERSX, nconf, &servaddr);
487                         rpcb_set(NLM_PROG, NLM_VERS4, nconf, &servaddr);
488
489                         xcreated++;
490                         freeaddrinfo(res);
491                 }
492         } /* end while */
493 }
494
495 void
496 sigalarm_handler(void)
497 {
498
499         grace_expired = 1;
500 }
501
502 static void
503 usage(void)
504 {
505         errx(1, "usage: rpc.lockd [-d <debuglevel>]"
506             " [-g <grace period>] [-h <bindip>] [-p <port>]");
507 }
508
509 /*
510  * Out of memory, fatal
511  */
512 void
513 out_of_mem(void)
514 {
515         syslog(LOG_ERR, "out of memory");
516         exit(2);
517 }