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