xref: /netbsd-src/external/bsd/am-utils/dist/amd/nfs_start.c (revision 8bae5d409deb915cf7c8f0539fae22ff2cb8a313)
1 /*	$NetBSD: nfs_start.c,v 1.1.1.3 2015/01/17 16:34:15 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 1997-2014 Erez Zadok
5  * Copyright (c) 1990 Jan-Simon Pendry
6  * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
7  * Copyright (c) 1990 The Regents of the University of California.
8  * All rights reserved.
9  *
10  * This code is derived from software contributed to Berkeley by
11  * Jan-Simon Pendry at Imperial College, London.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  *
37  *
38  * File: am-utils/amd/nfs_start.c
39  *
40  */
41 
42 #ifdef HAVE_CONFIG_H
43 # include <config.h>
44 #endif /* HAVE_CONFIG_H */
45 #include <am_defs.h>
46 #include <amd.h>
47 
48 #ifndef SELECT_MAXWAIT
49 # define SELECT_MAXWAIT 16
50 #endif /* not SELECT_MAXWAIT */
51 
52 SVCXPRT *nfsxprt = NULL;
53 u_short nfs_port = 0;
54 
55 #ifndef HAVE_SIGACTION
56 # define MASKED_SIGS	(sigmask(SIGINT)|sigmask(SIGTERM)|sigmask(SIGCHLD)|sigmask(SIGHUP))
57 #endif /* not HAVE_SIGACTION */
58 
59 #ifdef DEBUG
60 /*
61  * Check that we are not burning resources
62  */
63 static void
checkup(void)64 checkup(void)
65 {
66   static int max_fd = 0;
67   static char *max_mem = NULL;
68   int next_fd = dup(0);
69   caddr_t next_mem = sbrk(0);
70 
71   close(next_fd);
72 
73   if (max_fd < next_fd) {
74     dlog("%d new fds allocated; total is %d",
75 	 next_fd - max_fd, next_fd);
76     max_fd = next_fd;
77   }
78   if (max_mem < next_mem) {
79 #ifdef HAVE_GETPAGESIZE
80     dlog("%#lx bytes of memory allocated; total is %#lx (%ld pages)",
81 	 (long) (next_mem - max_mem), (unsigned long) next_mem,
82 	 ((long) next_mem + getpagesize() - 1) / (long) getpagesize());
83 #else /* not HAVE_GETPAGESIZE */
84     dlog("%#lx bytes of memory allocated; total is %#lx",
85 	 (long) (next_mem - max_mem), (unsigned long) next_mem);
86 #endif /* not HAVE_GETPAGESIZE */
87     max_mem = next_mem;
88 
89   }
90 }
91 #else  /* not DEBUG */
92 #define checkup()
93 #endif /* not DEBUG */
94 
95 
96 static int
97 #ifdef HAVE_SIGACTION
do_select(sigset_t smask,int fds,fd_set * fdp,struct timeval * tvp)98 do_select(sigset_t smask, int fds, fd_set *fdp, struct timeval *tvp)
99 #else /* not HAVE_SIGACTION */
100 do_select(int smask, int fds, fd_set *fdp, struct timeval *tvp)
101 #endif /* not HAVE_SIGACTION */
102 {
103 
104   int sig;
105   int nsel;
106 
107   if ((sig = setjmp(select_intr))) {
108     select_intr_valid = 0;
109     /* Got a signal */
110     switch (sig) {
111     case SIGINT:
112     case SIGTERM:
113       amd_state = Finishing;
114       reschedule_timeout_mp();
115       break;
116     }
117     nsel = -1;
118     errno = EINTR;
119   } else {
120     select_intr_valid = 1;
121     /*
122      * Allow interrupts.  If a signal
123      * occurs, then it will cause a longjmp
124      * up above.
125      */
126 #ifdef HAVE_SIGACTION
127     sigprocmask(SIG_SETMASK, &smask, NULL);
128 #else /* not HAVE_SIGACTION */
129     (void) sigsetmask(smask);
130 #endif /* not HAVE_SIGACTION */
131 
132     /*
133      * Wait for input
134      */
135     nsel = select(fds, fdp, (fd_set *) NULL, (fd_set *) NULL,
136 		  tvp->tv_sec ? tvp : (struct timeval *) NULL);
137   }
138 
139 #ifdef HAVE_SIGACTION
140   sigprocmask(SIG_BLOCK, &masked_sigs, NULL);
141 #else /* not HAVE_SIGACTION */
142   (void) sigblock(MASKED_SIGS);
143 #endif /* not HAVE_SIGACTION */
144 
145   /*
146    * Perhaps reload the cache?
147    */
148   if (do_mapc_reload < clocktime(NULL)) {
149     mapc_reload();
150     do_mapc_reload = clocktime(NULL) + gopt.map_reload_interval;
151   }
152   return nsel;
153 }
154 
155 
156 /*
157  * Determine whether anything is left in
158  * the RPC input queue.
159  */
160 static int
rpc_pending_now(void)161 rpc_pending_now(void)
162 {
163   struct timeval tvv;
164   int nsel;
165   fd_set readfds;
166 
167   FD_ZERO(&readfds);
168   FD_SET(fwd_sock, &readfds);
169 
170   tvv.tv_sec = tvv.tv_usec = 0;
171   nsel = select(FD_SETSIZE, &readfds, (fd_set *) NULL, (fd_set *) NULL, &tvv);
172   if (nsel < 1)
173     return (0);
174   if (FD_ISSET(fwd_sock, &readfds))
175     return (1);
176 
177   return (0);
178 }
179 
180 
181 static serv_state
run_rpc(void)182 run_rpc(void)
183 {
184 #ifdef HAVE_SIGACTION
185   sigset_t smask;
186   sigprocmask(SIG_BLOCK, &masked_sigs, &smask);
187 #else /* not HAVE_SIGACTION */
188   int smask = sigblock(MASKED_SIGS);
189 #endif /* not HAVE_SIGACTION */
190 
191   next_softclock = clocktime(NULL);
192 
193   amd_state = Run;
194 
195   /*
196    * Keep on trucking while we are in Run mode.  This state
197    * is switched to Quit after all the file systems have
198    * been unmounted.
199    */
200   while ((int) amd_state <= (int) Finishing) {
201     struct timeval tvv;
202     int nsel;
203     time_t now;
204     fd_set readfds;
205 
206 #ifdef HAVE_SVC_GETREQSET
207     memmove(&readfds, &svc_fdset, sizeof(svc_fdset));
208 #else /* not HAVE_SVC_GETREQSET */
209     FD_ZERO(&readfds);
210 # ifdef HAVE_FD_SET_FDS_BITS
211     readfds.fds_bits[0] = svc_fds;
212 # else /* not HAVE_FD_SET_FDS_BITS */
213     readfds = svc_fds;
214 # endif  /* not HAVE_FD_SET_FDS_BITS */
215 #endif /* not HAVE_SVC_GETREQSET */
216     FD_SET(fwd_sock, &readfds);
217 
218     checkup();
219 
220     /*
221      * If the full timeout code is not called,
222      * then recompute the time delta manually.
223      */
224     now = clocktime(NULL);
225 
226     if (next_softclock <= now) {
227       if (amd_state == Finishing)
228 	umount_exported();
229       tvv.tv_sec = softclock();
230     } else {
231       tvv.tv_sec = next_softclock - now;
232     }
233     tvv.tv_usec = 0;
234 
235     if (amd_state == Finishing && get_exported_ap(0) == NULL) {
236       flush_mntfs();
237       amd_state = Quit;
238       break;
239     }
240 
241 #ifdef HAVE_FS_AUTOFS
242     autofs_add_fdset(&readfds);
243 #endif /* HAVE_FS_AUTOFS */
244 
245     if (tvv.tv_sec <= 0)
246       tvv.tv_sec = SELECT_MAXWAIT;
247     if (tvv.tv_sec) {
248       dlog("Select waits for %ds", (int) tvv.tv_sec);
249     } else {
250       dlog("Select waits for Godot");
251     }
252 
253     nsel = do_select(smask, FD_SETSIZE, &readfds, &tvv);
254 
255     switch (nsel) {
256     case -1:
257       if (errno == EINTR) {
258 	dlog("select interrupted");
259 	continue;
260       }
261       plog(XLOG_ERROR, "select: %m");
262       break;
263 
264     case 0:
265       break;
266 
267     default:
268       /*
269        * Read all pending NFS responses at once to avoid having responses
270        * queue up as a consequence of retransmissions.
271        */
272       if (FD_ISSET(fwd_sock, &readfds)) {
273 	FD_CLR(fwd_sock, &readfds);
274 	--nsel;
275 	do {
276 	  fwd_reply();
277 	} while (rpc_pending_now() > 0);
278       }
279 
280 #ifdef HAVE_FS_AUTOFS
281       if (nsel)
282 	nsel = autofs_handle_fdset(&readfds, nsel);
283 #endif /* HAVE_FS_AUTOFS */
284 
285       if (nsel) {
286 	/*
287 	 * Anything left must be a normal
288 	 * RPC request.
289 	 */
290 #ifdef HAVE_SVC_GETREQSET
291 	svc_getreqset(&readfds);
292 #else /* not HAVE_SVC_GETREQSET */
293 # ifdef HAVE_FD_SET_FDS_BITS
294 	svc_getreq(readfds.fds_bits[0]);
295 # else /* not HAVE_FD_SET_FDS_BITS */
296 	svc_getreq(readfds);
297 # endif /* not HAVE_FD_SET_FDS_BITS */
298 #endif /* not HAVE_SVC_GETREQSET */
299       }
300       break;
301     }
302   }
303 
304 #ifdef HAVE_SIGACTION
305   sigprocmask(SIG_SETMASK, &smask, NULL);
306 #else /* not HAVE_SIGACTION */
307   (void) sigsetmask(smask);
308 #endif /* not HAVE_SIGACTION */
309 
310   if (amd_state == Quit)
311     amd_state = Done;
312 
313   return amd_state;
314 }
315 
316 
317 int
mount_automounter(int ppid)318 mount_automounter(int ppid)
319 {
320   /*
321    * Old code replaced by rpc-trash patch.
322    * Erez Zadok <ezk@cs.columbia.edu>
323    int so = socket(AF_INET, SOCK_DGRAM, 0);
324    */
325   SVCXPRT *udp_amqp = NULL, *tcp_amqp = NULL;
326   int nmount, ret;
327   int soNFS;
328   int udp_soAMQ, tcp_soAMQ;
329   struct netconfig *udp_amqncp, *tcp_amqncp;
330 
331   /*
332    * This must be done first, because it attempts to bind
333    * to various UDP ports and we don't want anything else
334    * potentially taking over those ports before we get a chance
335    * to reserve them.
336    */
337   if (gopt.flags & CFM_RESTART_EXISTING_MOUNTS)
338     restart_automounter_nodes();
339 
340   /*
341    * Start RPC forwarding
342    */
343   if (fwd_init() != 0)
344     return 3;
345 
346   /*
347    * Construct the root automount node
348    */
349   make_root_node();
350 
351   /*
352    * Pick up the pieces from a previous run
353    * This is likely to (indirectly) need the rpc_fwd package
354    * so it *must* come after the call to fwd_init().
355    */
356   if (gopt.flags & CFM_RESTART_EXISTING_MOUNTS)
357     restart();
358 
359   /*
360    * Create the nfs service for amd
361    * If nfs_port is already initialized, it means we
362    * already created the service during restart_automounter_nodes().
363    */
364   if (nfs_port == 0) {
365     ret = create_nfs_service(&soNFS, &nfs_port, &nfsxprt, nfs_dispatcher,
366 	get_nfs_dispatcher_version(nfs_dispatcher));
367     if (ret != 0)
368       return ret;
369   }
370   xsnprintf(pid_fsname, sizeof(pid_fsname), "%s:(pid%ld,port%u)",
371 	    am_get_hostname(), (long) am_mypid, nfs_port);
372 
373   /* security: if user sets -D noamq, don't even create listening socket */
374   if (amuDebug(D_AMQ)) {
375     ret = create_amq_service(&udp_soAMQ,
376 			     &udp_amqp,
377 			     &udp_amqncp,
378 			     &tcp_soAMQ,
379 			     &tcp_amqp,
380 			     &tcp_amqncp,
381 			     gopt.preferred_amq_port);
382     if (ret != 0)
383       return ret;
384   }
385 
386 #ifdef HAVE_FS_AUTOFS
387   if (amd_use_autofs) {
388     /*
389      * Create the autofs service for amd.
390      */
391     ret = create_autofs_service();
392     /* if autofs service fails it is OK if using a test amd */
393     if (ret != 0) {
394       plog(XLOG_WARNING, "autofs service registration failed, turning off autofs support");
395       amd_use_autofs = 0;
396     }
397   }
398 #endif /* HAVE_FS_AUTOFS */
399 
400   /*
401    * Mount the top-level auto-mountpoints
402    */
403   nmount = mount_exported();
404 
405   /*
406    * Now safe to tell parent that we are up and running
407    */
408   if (ppid)
409     kill(ppid, SIGQUIT);
410 
411   if (nmount == 0) {
412     plog(XLOG_FATAL, "No work to do - quitting");
413     amd_state = Done;
414     return 0;
415   }
416 
417   if (amuDebug(D_AMQ)) {
418     /*
419      * Complete registration of amq (first TCP service then UDP)
420      */
421     int tcp_ok = 0, udp_ok = 0;
422 
423     unregister_amq();	 /* unregister leftover Amd, if any, just in case */
424 
425     tcp_ok = amu_svc_register(tcp_amqp, get_amd_program_number(), AMQ_VERSION,
426 			      amq_program_1, IPPROTO_TCP, tcp_amqncp);
427     if (!tcp_ok)
428       plog(XLOG_FATAL,
429 	   "unable to register (AMQ_PROGRAM=%lu, AMQ_VERSION, tcp)",
430 	   get_amd_program_number());
431 
432     udp_ok = amu_svc_register(udp_amqp, get_amd_program_number(), AMQ_VERSION,
433 			      amq_program_1, IPPROTO_UDP, udp_amqncp);
434     if (!udp_ok)
435       plog(XLOG_FATAL,
436 	   "unable to register (AMQ_PROGRAM=%lu, AMQ_VERSION, udp)",
437 	   get_amd_program_number());
438 
439     /* return error only if both failed */
440     if (!tcp_ok && !udp_ok) {
441       amd_state = Done;
442       return 3;
443     }
444   }
445 
446   /*
447    * Start timeout_mp rolling
448    */
449   reschedule_timeout_mp();
450 
451   /*
452    * Start the server
453    */
454   if (run_rpc() != Done) {
455     plog(XLOG_FATAL, "run_rpc failed");
456     amd_state = Done;
457   }
458   return 0;
459 }
460